pandas時間序列數(shù)據(jù)處理

一、時間格式轉(zhuǎn)換

有時候,我們獲得的原始數(shù)據(jù)并不是按照時間類型索引進(jìn)行排列的,需要先進(jìn)行時間格式的轉(zhuǎn)換,為后續(xù)的操作和分析做準(zhǔn)備。
這里介紹兩種方法。第一種方法是用pandas.read_csv導(dǎo)入文件的時候,通過設(shè)置參數(shù)parse_dates和index_col,直接對日期列進(jìn)行轉(zhuǎn)換,并將其設(shè)置為索引。關(guān)于參數(shù)的詳細(xì)解釋。
如下示例中,在沒有設(shè)置參數(shù)之前,可以觀察到數(shù)據(jù)集中的索引是數(shù)字0-208,'date'列的數(shù)據(jù)類型也不是日期。

In [8]: data = pd.read_csv('unemployment.csv')
In [9]: data.info()
      <class 'pandas.core.frame.DataFrame'>
      RangeIndex: 209 entries, 0 to 208
      Data columns (total 2 columns):
      date      209 non-null object
      UNRATE    209 non-null float64
      dtypes: float64(1), object(1)
      memory usage: 3.3+ KB

設(shè)置參數(shù)parse_dates = ['date'] ,將數(shù)據(jù)類型轉(zhuǎn)換成日期,再設(shè)置 index_col = 'date',將這一列用作索引,結(jié)果如下。
**

In [11]: data = pd.read_csv('unemployment.csv', parse_dates=['date'], index_col='date')

In [12]: data.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 209 entries, 2000-01-01 to 2017-05-01
Data columns (total 1 columns):
UNRATE    209 non-null float64
dtypes: float64(1)
memory usage: 13.3 KB

這時,索引變成了日期'20000101'-'2017-05-01',數(shù)據(jù)類型是datetime。

第二種方法是在已經(jīng)導(dǎo)入數(shù)據(jù)的情況下,用pd.to_datetime()【2】將列轉(zhuǎn)換成日期類型,再用 df.set_index()【3】將其設(shè)置為索引,完成轉(zhuǎn)換。

以tushare.pro上面的日線行情數(shù)據(jù)為例,我們把'trade_date'列轉(zhuǎn)換成日期類型,并設(shè)置成索引。

import tushare as ts
import pandas as pd

pd.set_option('expand_frame_repr', False)  # 列太多時不換行
pro = ts.pro_api()

df = pro.daily(ts_code='000001.SZ', start_date='20180701', end_date='20180718')

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 13 entries, 0 to 12
Data columns (total 11 columns):
ts_code       13 non-null object
trade_date    13 non-null object
open          13 non-null float64
high          13 non-null float64
low           13 non-null float64
close         13 non-null float64
pre_close     13 non-null float64
change        13 non-null float64
pct_chg       13 non-null float64
vol           13 non-null float64
amount        13 non-null float64
dtypes: float64(9), object(2)
memory usage: 1.2+ KB
None
df['trade_date'] = pd.to_datetime(df['trade_date'])
df.set_index('trade_date', inplace=True)
df.sort_values('trade_date', ascending=True, inplace=True) # 升序排列

df.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 13 entries, 2018-07-02 to 2018-07-18
Data columns (total 10 columns):
ts_code      13 non-null object
open         13 non-null float64
high         13 non-null float64
low          13 non-null float64
close        13 non-null float64
pre_close    13 non-null float64
change       13 non-null float64
pct_chg      13 non-null float64
vol          13 non-null float64
amount       13 non-null float64
dtypes: float64(9), object(1)
memory usage: 1.1+ KB

打印出前5行,效果如下。

df.head()
Out[15]: 
              ts_code  open  high   low  close  pre_close  change  pct_chg         vol       amount
trade_date                                                                                         
2018-07-02  000001.SZ  9.05  9.05  8.55   8.61       9.09   -0.48    -5.28  1315520.13  1158545.868
2018-07-03  000001.SZ  8.69  8.70  8.45   8.67       8.61    0.06     0.70  1274838.57  1096657.033
2018-07-04  000001.SZ  8.63  8.75  8.61   8.61       8.67   -0.06    -0.69   711153.37   617278.559
2018-07-05  000001.SZ  8.62  8.73  8.55   8.60       8.61   -0.01    -0.12   835768.77   722169.579
2018-07-06  000001.SZ  8.61  8.78  8.45   8.66       8.60    0.06     0.70   988282.69   852071.526
02 

時間周期轉(zhuǎn)換

在完成時間格式轉(zhuǎn)換之后,我們就可以進(jìn)行后續(xù)的日期操作了。下面介紹一下如何對時間序列數(shù)據(jù)進(jìn)行重采樣resampling。

重采樣指的是將時間序列從?個頻率轉(zhuǎn)換到另?個頻率的處理過程。將?頻率數(shù)據(jù)聚合到低頻率稱為降采樣downsampling,如將股票的日線數(shù)據(jù)轉(zhuǎn)換成周線數(shù)據(jù),?將低頻率數(shù)據(jù)轉(zhuǎn)換到?頻率則稱為升采樣upsampling,如將股票的周線數(shù)據(jù)轉(zhuǎn)換成日線數(shù)據(jù)。

降采樣:以日線數(shù)據(jù)轉(zhuǎn)換周線數(shù)據(jù)為例。繼續(xù)使用上面的tushare.pro日線行情數(shù)據(jù),選出特定的幾列。

df = df[['ts_code', 'open', 'high', 'low', 'close', 'vol']]  # 單位:成交量 (手)
ts_code  open  high   low  close         vol
trade_date                                                
2018-07-02  000001.SZ  9.05  9.05  8.55   8.61  1315520.13
2018-07-03  000001.SZ  8.69  8.70  8.45   8.67  1274838.57
2018-07-04  000001.SZ  8.63  8.75  8.61   8.61   711153.37
2018-07-05  000001.SZ  8.62  8.73  8.55   8.60   835768.77
2018-07-06  000001.SZ  8.61  8.78  8.45   8.66   988282.69
2018-07-09  000001.SZ  8.69  9.03  8.68   9.03  1409954.60
2018-07-10  000001.SZ  9.02  9.02  8.89   8.98   896862.02
2018-07-11  000001.SZ  8.76  8.83  8.68   8.78   851296.70
2018-07-12  000001.SZ  8.60  8.97  8.58   8.88  1140492.31
2018-07-13  000001.SZ  8.92  8.94  8.82   8.88   603378.21
2018-07-16  000001.SZ  8.85  8.90  8.69   8.73   689845.58
2018-07-17  000001.SZ  8.74  8.75  8.66   8.72   375356.33
2018-07-18  000001.SZ  8.75  8.85  8.69   8.70   525152.77

為了方便大家觀察,把這段時間的日歷附在下面,'2018-07-02'正好是星期一。


1.jpg

轉(zhuǎn)換的思路是這樣的,以日歷中的周進(jìn)行聚合,如'20180702'-'20180708',取該周期內(nèi),日線開盤價的第一個值作為周開盤價,日線最高價的最大值作為周最高價,日線最低價的最小值作為周最低價,日線收盤價的最后一個值作為周最收盤價,日線最高價的最大值作為周最高價,日線成交量的求和作為周成交量(手),如下圖黃色方框所示。

2.jpg

我們可以通過.resample()【4】方法實現(xiàn)上述操作,對DataFrame和Series都適用。其中,參數(shù)rule設(shè)置需要轉(zhuǎn)換成的頻率,'1W'是一周。

具體轉(zhuǎn)換的代碼如下,日期默認(rèn)為本周的星期日,如果周期內(nèi)數(shù)據(jù)不全,如'20180722'這周只有3行數(shù)據(jù),也會按照上述方法進(jìn)行轉(zhuǎn)換。

freq = '1W'
df_weekly = df[['open']].resample(rule=freq).first()
df_weekly['high'] = df['high'].resample(rule=freq).max()
df_weekly['low'] = df['low'].resample(rule=freq).min()
df_weekly['close'] = df['close'].resample(rule=freq).last()
df_weekly['vol'] = df['vol'].resample(rule=freq).sum()

df_weekly
Out[33]: 
            open  high   low  close         vol
trade_date                                     
2018-07-08  9.05  9.05  8.45   8.66  5125563.53
2018-07-15  8.69  9.03  8.58   8.88  4901983.84
2018-07-22  8.85  8.90  8.66   8.70  1590354.68

升采樣:以周線數(shù)據(jù)轉(zhuǎn)換日線數(shù)據(jù)為例。繼續(xù)使用上面剛剛轉(zhuǎn)換好的周線數(shù)據(jù),我們再試著把它轉(zhuǎn)換成日線數(shù)據(jù)。先通過.resample('D').asfreq()【5】方法,將周線數(shù)據(jù)的頻率轉(zhuǎn)換成日線,效果如下。

df_daily = df_weekly.resample('D').asfreq()
print(df_daily)
Out[52]: 
                  open     high     low     close     vol
trade_date                                     
2018-07-08  9.05  9.05  8.45   8.66  5125563.53
2018-07-09   NaN   NaN   NaN    NaN         NaN
2018-07-10   NaN   NaN   NaN    NaN         NaN
2018-07-11   NaN   NaN   NaN    NaN         NaN
2018-07-12   NaN   NaN   NaN    NaN         NaN
2018-07-13   NaN   NaN   NaN    NaN         NaN
2018-07-14   NaN   NaN   NaN    NaN         NaN
2018-07-15  8.69  9.03  8.58   8.88  4901983.84
2018-07-16   NaN   NaN   NaN    NaN         NaN
2018-07-17   NaN   NaN   NaN    NaN         NaN
2018-07-18   NaN   NaN   NaN    NaN         NaN
2018-07-19   NaN   NaN   NaN    NaN         NaN
2018-07-20   NaN   NaN   NaN    NaN         NaN
2018-07-21   NaN   NaN   NaN    NaN         NaN
2018-07-22  8.85  8.90  8.66   8.70  1590354.68

結(jié)果中出現(xiàn)了很多空值,需要我們按照一定的方法進(jìn)行填充,可以通過添加.ffill()或者.bfill()實現(xiàn)。

其中,.ffill()代表用前值進(jìn)行填充,也就是用前面的非空值對后面的NaN值進(jìn)行填充,如'20180709'-20180714' 的NaN值都等于'20180708'這一行的非空值,效果如下。

df_daily = df_weekly.resample('D').ffill()
df_daily
Out[54]: 
            open  high   low  close         vol
trade_date                                     
2018-07-08  9.05  9.05  8.45   8.66  5125563.53
2018-07-09  9.05  9.05  8.45   8.66  5125563.53
2018-07-10  9.05  9.05  8.45   8.66  5125563.53
2018-07-11  9.05  9.05  8.45   8.66  5125563.53
2018-07-12  9.05  9.05  8.45   8.66  5125563.53
2018-07-13  9.05  9.05  8.45   8.66  5125563.53
2018-07-14  9.05  9.05  8.45   8.66  5125563.53
2018-07-15  8.69  9.03  8.58   8.88  4901983.84
2018-07-16  8.69  9.03  8.58   8.88  4901983.84
2018-07-17  8.69  9.03  8.58   8.88  4901983.84
2018-07-18  8.69  9.03  8.58   8.88  4901983.84
2018-07-19  8.69  9.03  8.58   8.88  4901983.84
2018-07-20  8.69  9.03  8.58   8.88  4901983.84
2018-07-21  8.69  9.03  8.58   8.88  4901983.84
2018-07-22  8.85  8.90  8.66   8.70  1590354.68

同理,.bfill()代表用后值對空值進(jìn)行填充,效果如下。

df_daily = df_weekly.resample('D').bfill()
df_daily
Out[55]: 
            open  high   low  close         vol
trade_date                                     
2018-07-08  9.05  9.05  8.45   8.66  5125563.53
2018-07-09  8.69  9.03  8.58   8.88  4901983.84
2018-07-10  8.69  9.03  8.58   8.88  4901983.84
2018-07-11  8.69  9.03  8.58   8.88  4901983.84
2018-07-12  8.69  9.03  8.58   8.88  4901983.84
2018-07-13  8.69  9.03  8.58   8.88  4901983.84
2018-07-14  8.69  9.03  8.58   8.88  4901983.84
2018-07-15  8.69  9.03  8.58   8.88  4901983.84
2018-07-16  8.85  8.90  8.66   8.70  1590354.68
2018-07-17  8.85  8.90  8.66   8.70  1590354.68
2018-07-18  8.85  8.90  8.66   8.70  1590354.68
2018-07-19  8.85  8.90  8.66   8.70  1590354.68
2018-07-20  8.85  8.90  8.66   8.70  1590354.68
2018-07-21  8.85  8.90  8.66   8.70  1590354.68
2018-07-22  8.85  8.90  8.66   8.70  1590354.68
03

時間窗口函數(shù)

當(dāng)我們想要比較數(shù)據(jù)在相同時間窗口的不同特征和變化時,可以借助窗口函數(shù)rolling【6】進(jìn)行計算。

看一個實例:計算股票收盤價的移動平均值。

df = df[['ts_code', 'close']]
df
Out[58]: 
              ts_code  close
trade_date                  
2018-07-02  000001.SZ   8.61
2018-07-03  000001.SZ   8.67
2018-07-04  000001.SZ   8.61
2018-07-05  000001.SZ   8.60
2018-07-06  000001.SZ   8.66
2018-07-09  000001.SZ   9.03
2018-07-10  000001.SZ   8.98
2018-07-11  000001.SZ   8.78
2018-07-12  000001.SZ   8.88
2018-07-13  000001.SZ   8.88
2018-07-16  000001.SZ   8.73
2018-07-17  000001.SZ   8.72
2018-07-18  000001.SZ   8.70

調(diào)用rolling函數(shù),通過設(shè)置參數(shù)window的值規(guī)定窗口大小,這里設(shè)置為3,并且調(diào)用.mean()方法計算窗口期為3天的均值,結(jié)果如下。

其中,'20180704'當(dāng)天的平均值等于'20180702'-'20180704'三天的收盤價取平均的結(jié)果,'20180705'當(dāng)天的平均值等于'20180703'-'20180705'三天的收盤價取平均的結(jié)果,以此類推。

df['MA3'] = df['close'].rolling(3).mean()
df
Out[76]: 
              ts_code  close       MA3
trade_date                            
2018-07-02  000001.SZ   8.61       NaN
2018-07-03  000001.SZ   8.67       NaN
2018-07-04  000001.SZ   8.61  8.630000
2018-07-05  000001.SZ   8.60  8.626667
2018-07-06  000001.SZ   8.66  8.623333
2018-07-09  000001.SZ   9.03  8.763333
2018-07-10  000001.SZ   8.98  8.890000
2018-07-11  000001.SZ   8.78  8.930000
2018-07-12  000001.SZ   8.88  8.880000
2018-07-13  000001.SZ   8.88  8.846667
2018-07-16  000001.SZ   8.73  8.830000
2018-07-17  000001.SZ   8.72  8.776667
2018-07-18  000001.SZ   8.70  8.716667

還有一個常用的窗口函數(shù)是expanding,每增加一行數(shù)據(jù),窗口會相應(yīng)的增大。比如,我們想計算某只股票每天的累計漲跌幅,就可以調(diào)用此函數(shù)。

df = df[['ts_code', 'pct_chg']]  # 列pct_chg單位是(%)
Out[71]: 
              ts_code  pct_chg
trade_date                    
2018-07-02  000001.SZ    -5.28
2018-07-03  000001.SZ     0.70
2018-07-04  000001.SZ    -0.69
2018-07-05  000001.SZ    -0.12
2018-07-06  000001.SZ     0.70
2018-07-09  000001.SZ     4.27
2018-07-10  000001.SZ    -0.55
2018-07-11  000001.SZ    -2.23
2018-07-12  000001.SZ     2.78
2018-07-13  000001.SZ     0.00
2018-07-16  000001.SZ    -1.69
2018-07-17  000001.SZ    -0.11
2018-07-18  000001.SZ    -0.23

對列'pct_chg'調(diào)用窗口函數(shù)expanding,再調(diào)用.sum()方法求累計值。

df['cum_pct_chg'] = df['pct_chg'].expanding().sum()
df
Out[78]: 
              ts_code  pct_chg  cum_pct_chg
trade_date                                 
2018-07-02  000001.SZ    -5.28        -5.28
2018-07-03  000001.SZ     0.70        -4.58
2018-07-04  000001.SZ    -0.69        -5.27
2018-07-05  000001.SZ    -0.12        -5.39
2018-07-06  000001.SZ     0.70        -4.69
2018-07-09  000001.SZ     4.27        -0.42
2018-07-10  000001.SZ    -0.55        -0.97
2018-07-11  000001.SZ    -2.23        -3.20
2018-07-12  000001.SZ     2.78        -0.42
2018-07-13  000001.SZ     0.00        -0.42
2018-07-16  000001.SZ    -1.69        -2.11
2018-07-17  000001.SZ    -0.11        -2.22
2018-07-18  000001.SZ    -0.23        -2.45

總結(jié)

本文介紹了Pandas庫中處理時間序列數(shù)據(jù)的幾種常用方法。

在時間格式轉(zhuǎn)換部分,介紹了兩種將時間轉(zhuǎn)化成日期類型的方法,分別是通過設(shè)置參數(shù)parse_dates和調(diào)用方法pd.to_datetime()。

接著,介紹了時間周期的轉(zhuǎn)換,通過調(diào)用.resample()方法實現(xiàn),包括降采樣和升采樣。

最后,介紹兩個常用的窗口函數(shù)rolling和expanding。

希望大家能靈活掌握本文中提到的方法,并應(yīng)用到實際工作和學(xué)習(xí)中去!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容