Skip to content

Latest commit

 

History

History
448 lines (314 loc) · 13.7 KB

74.Pandas的应用-5.md

File metadata and controls

448 lines (314 loc) · 13.7 KB

Pandas的應用-5

DataFrame的應用

視窗計算

DataFrame物件的rolling方法允許我們將資料置於視窗中,然後就可以使用函式對視窗中的資料進行運算和處理。例如,我們獲取了某隻股票近期的資料,想製作5日均線和10日均線,那麼就需要先設定視窗再進行運算。我們可以使用三方庫pandas-datareader來獲取指定的股票在某個時間段內的資料,具體的操作如下所示。

安裝pandas-datareader三方庫。

pip install pandas-datareader

透過pandas-datareader 提供的get_data_stooq從 Stooq 網站獲取百度(股票程式碼:BIDU)近期股票資料。

import pandas_datareader as pdr

baidu_df = pdr.get_data_stooq('BIDU', start='2021-11-22', end='2021-12-7')
baidu_df.sort_index(inplace=True)
baidu_df

輸出:

上面的DataFrameOpenHighLowCloseVolume五個列,分別程式碼股票的開盤價、最高價、最低價、收盤價和成交量,接下來我們對百度的股票資料進行視窗計算。

baidu_df.rolling(5).mean()

輸出:

上面的Close 列的資料就是我們需要的5日均線,當然,我們也可以用下面的方法,直接在Close列對應的Series物件上計算5日均線。

baidu_df.Close.rolling(5).mean()

輸出:

Date
2021-11-22        NaN
2021-11-23        NaN
2021-11-24        NaN
2021-11-26        NaN
2021-11-29    150.608
2021-11-30    151.014
2021-12-01    150.682
2021-12-02    150.196
2021-12-03    147.062
2021-12-06    146.534
2021-12-07    146.544
Name: Close, dtype: float64

相關性判定

在統計學中,我們通常使用協方差(covariance)來衡量兩個隨機變數的聯合變化程度。如果變數 $X$ 的較大值主要與另一個變數 $Y$ 的較大值相對應,而兩者較小值也相對應,那麼兩個變數傾向於表現出相似的行為,協方差為正。如果一個變數的較大值主要對應於另一個變數的較小值,則兩個變數傾向於表現出相反的行為,協方差為負。簡單的說,協方差的正負號顯示著兩個變數的相關性。方差是協方差的一種特殊情況,即變數與自身的協方差。

$$ cov(X,Y) = E((X - \mu)(Y - \upsilon)) = E(X \cdot Y) - \mu\upsilon $$

如果 $X$$Y$ 是統計獨立的,那麼二者的協方差為0,這是因為在 $X$$Y$ 獨立的情況下:

$$ E(X \cdot Y) = E(X) \cdot E(Y) = \mu\upsilon $$

協方差的數值大小取決於變數的大小,通常是不容易解釋的,但是正態形式的協方差大小可以顯示兩變數線性關係的強弱。在統計學中,皮爾遜積矩相關係數就是正態形式的協方差,它用於度量兩個變數 $X$$Y$ 之間的相關程度(線性相關),其值介於-11之間。

$$ \rho{X,Y} = \frac {cov(X, Y)} {\sigma_{X}\sigma_{Y}} $$

估算樣本的協方差和標準差,可以得到樣本皮爾遜係數,通常用希臘字母 $\rho$ 表示。

$$ \rho = \frac {\sum_{i=1}^{n}(X_i - \bar{X})(Y_i - \bar{Y})} {\sqrt{\sum_{i=1}^{n}(X_i - \bar{X})^2} \sqrt{\sum_{i=1}^{n}(Y_i - \bar{Y})^2}} $$

我們用 $\rho$ 值判斷指標的相關性時遵循以下兩個步驟。

  1. 判斷指標間是正相關、負相關,還是不相關。
    • 當 $ \rho \gt 0 $,認為變數之間是正相關,也就是兩者的趨勢一致。
    • 當 $ \rho \lt 0 $,認為變數之間是負相關,也就是兩者的趨勢相反。
    • 當 $ \rho = 0 $,認為變數之間是不相關的,但並不代表兩個指標是統計獨立的。
  2. 判斷指標間的相關程度。
    • 當 $ \rho $ 的絕對值在 $ [0.6,1] $ 之間,認為變數之間是強相關的。
    • 當 $ \rho $ 的絕對值在 $ [0.1,0.6) $ 之間,認為變數之間是弱相關的。
    • 當 $ \rho $ 的絕對值在 $ [0,0.1) $ 之間,認為變數之間沒有相關性。

皮爾遜相關係數適用於:

  1. 兩個變數之間是線性關係,都是連續資料。
  2. 兩個變數的總體是正態分佈,或接近正態的單峰分佈。
  3. 兩個變數的觀測值是成對的,每對觀測值之間相互獨立。

DataFrame物件的cov方法和corr方法分別用於計算協方差和相關係數,corr方法的第一個引數method的預設值是pearson,表示計算皮爾遜相關係數;除此之外,還可以指定kendallspearman來獲得肯德爾係數或斯皮爾曼等級相關係數。

接下來,我們從名為boston_house_price.csv的檔案中獲取著名的波士頓房價資料集來建立一個DataFrame,我們透過corr方法計算可能影響房價的13個因素中,哪些跟房價是正相關或負相關的,程式碼如下所示。

boston_df = pd.read_csv('data/csv/boston_house_price.csv')
boston_df.corr()

說明:如果需要上面例子中的 CSV 檔案,可以透過下面的百度雲盤地址進行獲取,資料在《從零開始學資料分析》目錄中。連結:https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g,提取碼:e7b4。

輸出:

斯皮爾曼相關係數對資料條件的要求沒有皮爾遜相關係數嚴格,只要兩個變數的觀測值是成對的等級評定資料,或者是由連續變數觀測資料轉化得到的等級資料,不論兩個變數的總體分佈形態、樣本容量的大小如何,都可以用斯皮爾曼等級相關係數來進行研究。我們透過下面的方式來計算斯皮爾曼相關係數。

boston_df.corr('spearman')

輸出:

在 Notebook 或 JupyterLab 中,我們可以為PRICE列新增漸變色,用顏色直觀的展示出跟房價負相關、正相關、不相關的列,DataFrame物件style屬性的background_gradient方法可以完成這個操作,程式碼如下所示。

boston_df.corr('spearman').style.background_gradient('RdYlBu', subset=['PRICE'])

上面程式碼中的RdYlBu代表的顏色如下所示,相關係數的資料值越接近1,顏色越接近紅色;資料值越接近1,顏色越接近藍色;資料值在0附件則是黃色。

plt.get_cmap('RdYlBu')

Index的應用

我們再來看看Index型別,它為SeriesDataFrame物件提供了索引服務,常用的Index有以下幾種。

範圍索引(RangeIndex)

程式碼:

sales_data = np.random.randint(400, 1000, 12)
month_index = pd.RangeIndex(1, 13, name='月份')
ser = pd.Series(data=sales_data, index=month_index)
ser

輸出:

月份
1     703
2     705
3     557
4     943
5     961
6     615
7     788
8     985
9     921
10    951
11    874
12    609
dtype: int64

分類索引(CategoricalIndex)

程式碼:

cate_index = pd.CategoricalIndex(
    ['蘋果', '香蕉', '蘋果', '蘋果', '桃子', '香蕉'],
    ordered=True,
    categories=['蘋果', '香蕉', '桃子']
)
ser = pd.Series(data=amount, index=cate_index)
ser

輸出:

蘋果    6
香蕉    6
蘋果    7
蘋果    6
桃子    8
香蕉    6
dtype: int64

程式碼:

ser.groupby(level=0).sum()

輸出:

蘋果    19
香蕉    12
桃子     8
dtype: int64

多級索引(MultiIndex)

程式碼:

ids = np.arange(1001, 1006)
sms = ['期中', '期末']
index = pd.MultiIndex.from_product((ids, sms), names=['學號', '學期'])
courses = ['語文', '數學', '英語']
scores = np.random.randint(60, 101, (10, 3))
df = pd.DataFrame(data=scores, columns=courses, index=index)
df

說明:上面的程式碼使用了MultiIndex的類方法from_product,該方法透過idssms兩組資料的笛卡爾積構造了多級索引。

輸出:

             語文 數學 英語
學號	學期			
1001  期中	93	77	60
      期末	93	98	84
1002  期中	64	78	71
      期末	70	71	97
1003  期中	72	88	97
      期末	99	100	63
1004  期中	80	71	61
      期末	91	62	72
1005  期中	82	95	67
      期末	84	78	86

程式碼:

# 計算每個學生的成績,期中佔25%,期末佔75%
df.groupby(level=0).agg(lambda x: x.values[0] * 0.25 + x.values[1] * 0.75)

輸出:

        語文    數學    英語
學號			
1001	93.00	92.75	78.00
1002	68.50	72.75	90.50
1003	92.25	97.00	71.50
1004	88.25	64.25	69.25
1005	83.50	82.25	81.25

日期時間索引(DatetimeIndex)

  1. 透過date_range()函式,我們可以建立日期時間索引,程式碼如下所示。

    程式碼:

    pd.date_range('2021-1-1', '2021-6-1', periods=10)

    輸出:

    DatetimeIndex(['2021-01-01 00:00:00', '2021-01-17 18:40:00',
                   '2021-02-03 13:20:00', '2021-02-20 08:00:00',
                   '2021-03-09 02:40:00', '2021-03-25 21:20:00',
                   '2021-04-11 16:00:00', '2021-04-28 10:40:00',
                   '2021-05-15 05:20:00', '2021-06-01 00:00:00'],
                  dtype='datetime64[ns]', freq=None)
    

    程式碼:

    pd.date_range('2021-1-1', '2021-6-1', freq='W')

    輸出:

    DatetimeIndex(['2021-01-03', '2021-01-10', '2021-01-17', '2021-01-24',
                   '2021-01-31', '2021-02-07', '2021-02-14', '2021-02-21',
                   '2021-02-28', '2021-03-07', '2021-03-14', '2021-03-21',
                   '2021-03-28', '2021-04-04', '2021-04-11', '2021-04-18',
                   '2021-04-25', '2021-05-02', '2021-05-09', '2021-05-16',
                   '2021-05-23', '2021-05-30'],
                  dtype='datetime64[ns]', freq='W-SUN')
    
  2. 透過DateOffset型別,我們可以設定時間差並和DatetimeIndex進行運算,具體的操作如下所示。

    程式碼:

    index = pd.date_range('2021-1-1', '2021-6-1', freq='W')
    index - pd.DateOffset(days=2)

    輸出:

    DatetimeIndex(['2021-01-01', '2021-01-08', '2021-01-15', '2021-01-22',
                   '2021-01-29', '2021-02-05', '2021-02-12', '2021-02-19',
                   '2021-02-26', '2021-03-05', '2021-03-12', '2021-03-19',
                   '2021-03-26', '2021-04-02', '2021-04-09', '2021-04-16',
                   '2021-04-23', '2021-04-30', '2021-05-07', '2021-05-14',
                   '2021-05-21', '2021-05-28'],
                  dtype='datetime64[ns]', freq=None)
    

    程式碼:

    index + pd.DateOffset(days=2)

    輸出:

    DatetimeIndex(['2021-01-05', '2021-01-12', '2021-01-19', '2021-01-26',
                   '2021-02-02', '2021-02-09', '2021-02-16', '2021-02-23',
                   '2021-03-02', '2021-03-09', '2021-03-16', '2021-03-23',
                   '2021-03-30', '2021-04-06', '2021-04-13', '2021-04-20',
                   '2021-04-27', '2021-05-04', '2021-05-11', '2021-05-18',
                   '2021-05-25', '2021-06-01'],
                  dtype='datetime64[ns]', freq=None)
    
  3. 可以使用DatatimeIndex型別的相關方法來處理資料,具體包括:

    • shift()方法:透過時間前移或後移資料,我們仍然以上面百度股票資料為例,程式碼如下所示。

      程式碼:

      baidu_df.shift(3, fill_value=0)

      輸出:

      程式碼:

      baidu_df.shift(-1, fill_value=0)

      輸出:

    • asfreq()方法:指定一個時間頻率抽取對應的資料,程式碼如下所示。

      程式碼:

      baidu_df.asfreq('5D')

      輸出:

      程式碼:

      baidu_df.asfreq('5D', method='ffill')

      輸出:

    • resample()方法:基於時間對資料進行重取樣,相當於根據時間週期對資料進行了分組操作,程式碼如下所示。

      程式碼:

      baidu_df.resample('1M').mean()

      輸出:

    說明:上面的程式碼中,W表示一週,5D表示5天,1M表示1個月。

  4. 時區轉換

    • 獲取時區資訊。

      import pytz
      
      pytz.common_timezones
    • tz_localize()方法:將日期時間本地化。

      程式碼:

      baidu_df = baidu_df.tz_localize('Asia/Chongqing')
      baidu_df

      輸出:

    • tz_convert()方法:轉換時區。

      程式碼:

      baidu_df.tz_convert('America/New_York')

      輸出: