DataWhale組隊(duì)學(xué)習(xí)之時(shí)序

參考開源組織datawhale:https://datawhalechina.github.io/joyful-pandas/build/html/%E7%9B%AE%E5%BD%95/ch10.html
第十章 時(shí)序數(shù)據(jù)

In [1]: import numpy as np

In [2]: import pandas as pd

一、時(shí)序中的基本對(duì)象
時(shí)間序列的概念在日常生活中十分常見塌忽,但對(duì)于一個(gè)具體的時(shí)序事件而言梗夸,可以從多個(gè)時(shí)間對(duì)象的角度來(lái)描述儿倒。例如2020年9月7日周一早上8點(diǎn)整需要到教室上課,這個(gè)課會(huì)在當(dāng)天早上10點(diǎn)結(jié)束,其中包含了哪些時(shí)間概念距糖?
第一,會(huì)出現(xiàn)時(shí)間戳(Date times)的概念精堕,即’2020-9-7 08:00:00’和’2020-9-7 10:00:00’這兩個(gè)時(shí)間點(diǎn)分別代表了上課和下課的時(shí)刻绰姻,在 pandas 中稱為 Timestamp 倦挂。同時(shí),一系列的時(shí)間戳可以組成 DatetimeIndex 趣斤,而將它放到 Series 中后, Series 的類型就變?yōu)榱?datetime64[ns] 黎休,如果有涉及時(shí)區(qū)則為 datetime64[ns, tz] 浓领,其中tz是timezone的簡(jiǎn)寫。

第二势腮,會(huì)出現(xiàn)時(shí)間差(Time deltas)的概念联贩,即上課需要的時(shí)間,兩個(gè) Timestamp 做差就得到了時(shí)間差捎拯,pandas中利用 Timedelta 來(lái)表示泪幌。類似的,一系列的時(shí)間差就組成了 TimedeltaIndex , 而將它放到 Series 中后座菠, Series 的類型就變?yōu)榱?timedelta64[ns] 狸眼。

第三,會(huì)出現(xiàn)時(shí)間段(Time spans)的概念浴滴,即在8點(diǎn)到10點(diǎn)這個(gè)區(qū)間都會(huì)持續(xù)地在上課拓萌,在 pandas 利用 Period 來(lái)表示。類似的升略,一系列的時(shí)間段就組成了 PeriodIndex 微王, 而將它放到 Series 中后, Series 的類型就變?yōu)榱?Period 品嚣。

第四炕倘,會(huì)出現(xiàn)日期偏置(Date offsets)的概念,假設(shè)你只知道9月的第一個(gè)周一早上8點(diǎn)要去上課翰撑,但不知道具體的日期罩旋,那么就需要一個(gè)類型來(lái)處理此類需求。再例如眶诈,想要知道2020年9月7日后的第30個(gè)工作日是哪一天涨醋,那么時(shí)間差就解決不了你的問題,從而 pandas 中的 DateOffset 就出現(xiàn)了逝撬。同時(shí)浴骂, pandas 中沒有為一列時(shí)間偏置專門設(shè)計(jì)存儲(chǔ)類型,理由也很簡(jiǎn)單宪潮,因?yàn)樾枨蟊容^奇怪溯警,一般來(lái)說(shuō)我們只需要對(duì)一批時(shí)間特征做一個(gè)統(tǒng)一的特殊日期偏置。

對(duì)于時(shí)間戳那里你也可以對(duì)它進(jìn)行一個(gè)轉(zhuǎn)換狡相,比如:

image.png

注意:這里不是數(shù)據(jù)脫敏梯轻,只是一種轉(zhuǎn)換。
通過(guò)這個(gè)簡(jiǎn)單的例子谣光,就能夠容易地總結(jié)出官方文檔中的這個(gè) 表格
我又來(lái)白漂了檩淋。
image.png

由于時(shí)間段對(duì)象 Period/PeriodIndex 的使用頻率并不高,因此將不進(jìn)行講解萄金,而只涉及時(shí)間戳序列蟀悦、時(shí)間差序列和日期偏置的相關(guān)內(nèi)容。

二氧敢、時(shí)間戳

  1. Timestamp的構(gòu)造與屬性
    單個(gè)時(shí)間戳的生成利用 pd.Timestamp 實(shí)現(xiàn)日戈,一般而言的常見日期格式都能被成功地轉(zhuǎn)換:
In [3]: ts = pd.Timestamp('2020/1/1')

In [4]: ts
Out[4]: Timestamp('2020-01-01 00:00:00')

In [5]: ts = pd.Timestamp('2020-1-1 08:10:30')
#注意兩者的不同
In [6]: ts
Out[6]: Timestamp('2020-01-01 08:10:30')

通過(guò) year, month, day, hour, min, second 可以獲取具體的數(shù)值:

In [7]: ts.year
Out[7]: 2020

In [8]: ts.month
Out[8]: 1

In [9]: ts.day
Out[9]: 1

In [10]: ts.hour
Out[10]: 8

In [11]: ts.minute
Out[11]: 10

In [12]: ts.second
Out[12]: 30

在 pandas 中,時(shí)間戳的最小精度為納秒 ns 孙乖,由于使用了64位存儲(chǔ)浙炼,可以表示的時(shí)間范圍大約可以如下計(jì)算:


image.png

通過(guò) pd.Timestamp.max 和 pd.Timestamp.min 可以獲取時(shí)間戳表示的范圍份氧,可以看到確實(shí)表示的區(qū)間年數(shù)大小正如上述計(jì)算結(jié)果:

In [13]: pd.Timestamp.max
Out[13]: Timestamp('2262-04-11 23:47:16.854775807')

In [14]: pd.Timestamp.min
Out[14]: Timestamp('1677-09-21 00:12:43.145225')

In [15]: pd.Timestamp.max.year - pd.Timestamp.min.year
Out[15]: 585

  1. Datetime序列的生成
    一組時(shí)間戳可以組成時(shí)間序列,可以用 to_datetime 和 date_range 來(lái)生成弯屈。其中蜗帜, to_datetime 能夠把一列時(shí)間戳格式的對(duì)象轉(zhuǎn)換成為 datetime64[ns] 類型的時(shí)間序列:
In [16]: pd.to_datetime(['2020-1-1', '2020-1-3', '2020-1-6'])
Out[16]: DatetimeIndex(['2020-01-01', '2020-01-03', '2020-01-06'], dtype='datetime64[ns]', freq=None)

In [17]: df = pd.read_csv('data/learn_pandas.csv')

In [18]: s = pd.to_datetime(df.Test_Date)

In [19]: s.head()
Out[19]: 
0   2019-10-05
1   2019-09-04
2   2019-09-12
3   2020-01-03
4   2019-11-06
Name: Test_Date, dtype: datetime64[ns]

在極少數(shù)情況,時(shí)間戳的格式不滿足轉(zhuǎn)換時(shí)资厉,可以強(qiáng)制使用 format 進(jìn)行匹配:

In [20]: temp = pd.to_datetime(['2020\\1\\1','2020\\1\\3'],format='%Y\\%m\\%d')

In [21]: temp
Out[21]: DatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)

注意上面由于傳入的是列表厅缺,而非 pandas 內(nèi)部的 Series ,因此返回的是 DatetimeIndex 宴偿,如果想要轉(zhuǎn)為 datetime64[ns] 的序列湘捎,需要顯式用 Series 轉(zhuǎn)化:

In [22]: pd.Series(temp).head()
Out[22]: 
0   2020-01-01
1   2020-01-03
dtype: datetime64[ns]

另外,還存在一種把表的多列時(shí)間屬性拼接轉(zhuǎn)為時(shí)間序列的 to_datetime 操作窄刘,此時(shí)的列名必須和以下給定的時(shí)間關(guān)鍵詞列名一致:

In [23]: df_date_cols = pd.DataFrame({'year': [2020, 2020],
   ....:                              'month': [1, 1],
   ....:                              'day': [1, 2],
   ....:                              'hour': [10, 20],
   ....:                              'minute': [30, 50],
   ....:                              'second': [20, 40]})
   ....: 

In [24]: pd.to_datetime(df_date_cols)
Out[24]: 
0   2020-01-01 10:30:20
1   2020-01-02 20:50:40
dtype: datetime64[ns]

date_range 是一種生成連續(xù)間隔時(shí)間的一種方法窥妇,其重要的參數(shù)為 start, end, freq, periods ,它們分別表示開始時(shí)間娩践,結(jié)束時(shí)間活翩,時(shí)間間隔,時(shí)間戳個(gè)數(shù)欺矫。其中纱新,四個(gè)中的三個(gè)參數(shù)決定了,那么剩下的一個(gè)就隨之確定了穆趴。這里要注意,開始或結(jié)束日期如果作為端點(diǎn)則它會(huì)被包含:

In [25]: pd.date_range('2020-1-1','2020-1-21', freq='10D') # 包含
Out[25]: DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21'], dtype='datetime64[ns]', freq='10D')

In [26]: pd.date_range('2020-1-1','2020-2-28', freq='10D')
Out[26]: 
DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21', '2020-01-31',
               '2020-02-10', '2020-02-20'],
              dtype='datetime64[ns]', freq='10D')

In [27]: pd.date_range('2020-1-1',
   ....:               '2020-2-28', periods=6) # 由于結(jié)束日期無(wú)法取到遇汞,freq不為10天
   ....: 
Out[27]: 
DatetimeIndex(['2020-01-01 00:00:00', '2020-01-12 14:24:00',
               '2020-01-24 04:48:00', '2020-02-04 19:12:00',
               '2020-02-16 09:36:00', '2020-02-28 00:00:00'],
              dtype='datetime64[ns]', freq=None)

這里的 freq 參數(shù)與 DateOffset 對(duì)象緊密相關(guān)未妹,將在第四節(jié)介紹其具體的用法。
最后空入,要介紹一種改變序列采樣頻率的方法 asfreq 络它,它能夠根據(jù)給定的 freq 對(duì)序列進(jìn)行類似于 reindex 的操作:

In [28]: s = pd.Series(np.random.rand(5),
   ....:             index=pd.to_datetime([
   ....:                 '2020-1-%d'%i for i in range(1,10,2)]))
   ....: 

In [29]: s.head()
Out[29]: 
2020-01-01    0.836578
2020-01-03    0.678419
2020-01-05    0.711897
2020-01-07    0.487429
2020-01-09    0.604705
dtype: float64

In [30]: s.asfreq('D').head()
Out[30]: 
2020-01-01    0.836578
2020-01-02         NaN
2020-01-03    0.678419
2020-01-04         NaN
2020-01-05    0.711897
Freq: D, dtype: float64

In [31]: s.asfreq('12H').head()
Out[31]: 
2020-01-01 00:00:00    0.836578
2020-01-01 12:00:00         NaN
2020-01-02 00:00:00         NaN
2020-01-02 12:00:00         NaN
2020-01-03 00:00:00    0.678419
Freq: 12H, dtype: float64
image.png
  1. dt對(duì)象
    如同 category, string 的序列上定義了 cat, str 來(lái)完成分類數(shù)據(jù)和文本數(shù)據(jù)的操作,在時(shí)序類型的序列上定義了 dt 對(duì)象來(lái)完成許多時(shí)間序列的相關(guān)操作歪赢。這里對(duì)于 datetime64[ns] 類型而言化戳,可以大致分為三類操作:取出時(shí)間相關(guān)的屬性、判斷時(shí)間戳是否滿足條件埋凯、取整操作点楼。
    第一類操作的常用屬性包括: date, time, year, month, day, hour, minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter ,其中 daysinmonth, quarter 分別表示月中的第幾天和季度白对。
In [32]: s = pd.Series(pd.date_range('2020-1-1','2020-1-3', freq='D'))

In [33]: s.dt.date
Out[33]: 
0    2020-01-01
1    2020-01-02
2    2020-01-03
dtype: object

In [34]: s.dt.time
Out[34]: 
0    00:00:00
1    00:00:00
2    00:00:00
dtype: object

In [35]: s.dt.day
Out[35]: 
0    1
1    2
2    3
dtype: int64

In [36]: s.dt.daysinmonth
Out[36]: 
0    31
1    31
2    31
dtype: int64

在這些屬性中掠廓,經(jīng)常使用的是 dayofweek ,它返回了周中的星期情況甩恼,周一為0蟀瞧、周二為1沉颂,以此類推:

In [37]: s.dt.dayofweek
Out[37]: 
0    2
1    3
2    4
dtype: int64

此外,可以通過(guò) month_name, day_name 返回英文的月名和星期名悦污,注意它們是方法而不是屬性:

In [38]: s.dt.month_name()
Out[38]: 
0    January
1    January
2    January
dtype: object

In [39]: s.dt.day_name()
Out[39]: 
0    Wednesday
1     Thursday
2       Friday
dtype: object

第二類判斷操作主要用于測(cè)試是否為月/季/年的第一天或者最后一天:

In [40]: s.dt.is_year_start # 還可選 is_quarter/month_start
Out[40]: 
0     True
1    False
2    False
dtype: bool

In [41]: s.dt.is_year_end # 還可選 is_quarter/month_end
Out[41]: 
0    False
1    False
2    False
dtype: bool

第三類的取整操作包含 round, ceil, floor 铸屉,它們的公共參數(shù)為 freq ,常用的包括 H, min, S (小時(shí)切端、分鐘抬探、秒),所有可選的 freq 可參考 此處 帆赢。

In [42]: s = pd.Series(pd.date_range('2020-1-1 20:35:00',
   ....:                             '2020-1-1 22:35:00',
   ....:                             freq='45min'))
   ....: 

In [43]: s
Out[43]: 
0   2020-01-01 20:35:00
1   2020-01-01 21:20:00
2   2020-01-01 22:05:00
dtype: datetime64[ns]

In [44]: s.dt.round('1H')
Out[44]: 
0   2020-01-01 21:00:00
1   2020-01-01 21:00:00
2   2020-01-01 22:00:00
dtype: datetime64[ns]

In [45]: s.dt.ceil('1H')
Out[45]: 
0   2020-01-01 21:00:00
1   2020-01-01 22:00:00
2   2020-01-01 23:00:00
dtype: datetime64[ns]

In [46]: s.dt.floor('1H')
Out[46]: 
0   2020-01-01 20:00:00
1   2020-01-01 21:00:00
2   2020-01-01 22:00:00
dtype: datetime64[ns]

  1. 時(shí)間戳的切片與索引
    一般而言小压,時(shí)間戳序列作為索引使用。如果想要選出某個(gè)子時(shí)間戳序列椰于,第一類方法是利用 dt 對(duì)象和布爾條件聯(lián)合使用怠益,另一種方式是利用切片,后者常用于連續(xù)時(shí)間戳瘾婿。下面蜻牢,舉一些例子說(shuō)明:
In [47]: s = pd.Series(np.random.randint(2,size=366),
   ....:               index=pd.date_range(
   ....:                       '2020-01-01','2020-12-31'))
   ....: 

In [48]: idx = pd.Series(s.index).dt

In [49]: s.head()
Out[49]: 
2020-01-01    1
2020-01-02    1
2020-01-03    0
2020-01-04    1
2020-01-05    0
Freq: D, dtype: int32

Example1:每月的第一天或者最后一天

In [50]: s[(idx.is_month_start|idx.is_month_end).values].head()
Out[50]: 
2020-01-01    1
2020-01-31    0
2020-02-01    1
2020-02-29    1
2020-03-01    0
dtype: int32

Example2:雙休日

In [51]: s[idx.dayofweek.isin([5,6]).values].head()
Out[51]: 
2020-01-04    1
2020-01-05    0
2020-01-11    0
2020-01-12    1
2020-01-18    1
dtype: int32

Example3:取出單日值

In [52]: s['2020-01-01']
Out[52]: 1

In [53]: s['20200101'] # 自動(dòng)轉(zhuǎn)換標(biāo)準(zhǔn)格式
Out[53]: 1

Example4:取出七月

In [54]: s['2020-07'].head()
Out[54]: 
2020-07-01    0
2020-07-02    1
2020-07-03    0
2020-07-04    0
2020-07-05    0
Freq: D, dtype: int32

Example5:取出5月初至7月15日

In [55]: s['2020-05':'2020-7-15'].head()
Out[55]: 
2020-05-01    0
2020-05-02    1
2020-05-03    0
2020-05-04    1
2020-05-05    1
Freq: D, dtype: int32

In [56]: s['2020-05':'2020-7-15'].tail()
Out[56]: 
2020-07-11    0
2020-07-12    0
2020-07-13    1
2020-07-14    0
2020-07-15    1
Freq: D, dtype: int32

三、時(shí)間差

  1. Timedelta的生成
    正如在第一節(jié)中所說(shuō)偏陪,時(shí)間差可以理解為兩個(gè)時(shí)間戳的差抢呆,這里也可以通過(guò) pd.Timedelta 來(lái)構(gòu)造:
In [57]: pd.Timestamp('20200102 08:00:00')-pd.Timestamp('20200101 07:35:00')
Out[57]: Timedelta('1 days 00:25:00')

In [58]: pd.Timedelta(days=1, minutes=25) # 需要注意加s
Out[58]: Timedelta('1 days 00:25:00')

In [59]: pd.Timedelta('1 days 25 minutes') # 字符串生成
Out[59]: Timedelta('1 days 00:25:00')

生成時(shí)間差序列的主要方式是 pd.to_timedelta ,其類型為 timedelta64[ns] :

In [60]: s = pd.to_timedelta(df.Time_Record)

In [61]: s.head()
Out[61]: 
0   0 days 00:04:34
1   0 days 00:04:20
2   0 days 00:05:22
3   0 days 00:04:08
4   0 days 00:05:22
Name: Time_Record, dtype: timedelta64[ns]

與 date_range 一樣笛谦,時(shí)間差序列也可以用 timedelta_range 來(lái)生成抱虐,它們兩者具有一致的參數(shù):

In [62]: pd.timedelta_range('0s', '1000s', freq='6min')
Out[62]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:06:00', '0 days 00:12:00'], dtype='timedelta64[ns]', freq='6T')

In [63]: pd.timedelta_range('0s', '1000s', periods=3)
Out[63]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:08:20', '0 days 00:16:40'], dtype='timedelta64[ns]', freq=None)

對(duì)于 Timedelta 序列,同樣也定義了 dt 對(duì)象饥脑,上面主要定義了的屬性包括 days, seconds, mircroseconds, nanoseconds 恳邀,它們分別返回了對(duì)應(yīng)的時(shí)間差特征。需要注意的是灶轰,這里的 seconds 不是指單純的秒谣沸,而是對(duì)天數(shù)取余后剩余的秒數(shù):

In [64]: s.dt.seconds.head()
Out[64]: 
0    274
1    260
2    322
3    248
4    322
Name: Time_Record, dtype: int64

如果不想對(duì)天數(shù)取余而直接對(duì)應(yīng)秒數(shù),可以使用 total_seconds

In [65]: s.dt.total_seconds().head()
Out[65]: 
0    274.0
1    260.0
2    322.0
3    248.0
4    322.0
Name: Time_Record, dtype: float64

與時(shí)間戳序列類似笋颤,取整函數(shù)也是可以在 dt 對(duì)象上使用的:

In [66]: pd.to_timedelta(df.Time_Record).dt.round('min').head()
Out[66]: 
0   0 days 00:05:00
1   0 days 00:04:00
2   0 days 00:05:00
3   0 days 00:04:00
4   0 days 00:05:00
Name: Time_Record, dtype: timedelta64[ns]

  1. Timedelta的運(yùn)算
    時(shí)間差支持的常用運(yùn)算有三類:與標(biāo)量的乘法運(yùn)算乳附、與時(shí)間戳的加減法運(yùn)算、與時(shí)間差的加減法與除法運(yùn)算:
In [67]: td1 = pd.Timedelta(days=1)

In [68]: td2 = pd.Timedelta(days=3)

In [69]: ts = pd.Timestamp('20200101')

In [70]: td1 * 2
Out[70]: Timedelta('2 days 00:00:00')

In [71]: td2 - td1
Out[71]: Timedelta('2 days 00:00:00')

In [72]: ts + td1
Out[72]: Timestamp('2020-01-02 00:00:00')

In [73]: ts - td1
Out[73]: Timestamp('2019-12-31 00:00:00')

這些運(yùn)算都可以移植到時(shí)間差的序列上:

In [74]: td1 = pd.timedelta_range(start='1 days', periods=5)

In [75]: td2 = pd.timedelta_range(start='12 hours',
   ....:                          freq='2H',
   ....:                          periods=5)
   ....: 

In [76]: ts = pd.date_range('20200101', '20200105')

In [77]: td1 * 5
Out[77]: TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D')

In [78]: td1 * pd.Series(list(range(5))) # 逐個(gè)相乘
Out[78]: 
0    0 days
1    2 days
2    6 days
3   12 days
4   20 days
dtype: timedelta64[ns]

In [79]: td1 - td2
Out[79]: 
TimedeltaIndex(['0 days 12:00:00', '1 days 10:00:00', '2 days 08:00:00',
                '3 days 06:00:00', '4 days 04:00:00'],
               dtype='timedelta64[ns]', freq=None)

In [80]: td1 + pd.Timestamp('20200101')
Out[80]: 
DatetimeIndex(['2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05',
               '2020-01-06'],
              dtype='datetime64[ns]', freq='D')

In [81]: td1 + ts # 逐個(gè)相加
Out[81]: 
DatetimeIndex(['2020-01-02', '2020-01-04', '2020-01-06', '2020-01-08',
               '2020-01-10'],
              dtype='datetime64[ns]', freq=None)

四伴澄、日期偏置

  1. Offset對(duì)象
    日期偏置是一種和日歷相關(guān)的特殊時(shí)間差赋除,例如回到第一節(jié)中的兩個(gè)問題:如何求2020年9月第一個(gè)周一的日期,以及如何求2020年9月7日后的第30個(gè)工作日是哪一天秉版。
In [82]: pd.Timestamp('20200831') + pd.offsets.WeekOfMonth(week=0,weekday=0)
Out[82]: Timestamp('2020-09-07 00:00:00')

In [83]: pd.Timestamp('20200907') + pd.offsets.BDay(30)
Out[83]: Timestamp('2020-10-19 00:00:00')

從上面的例子中可以看到贤重, Offset 對(duì)象在 pd.offsets 中被定義。當(dāng)使用 + 時(shí)獲取離其最近的下一個(gè)日期清焕,當(dāng)使用 - 時(shí)獲取離其最近的上一個(gè)日期:

In [84]: pd.Timestamp('20200831') - pd.offsets.WeekOfMonth(week=0,weekday=0)
Out[84]: Timestamp('2020-08-03 00:00:00')

In [85]: pd.Timestamp('20200907') - pd.offsets.BDay(30)
Out[85]: Timestamp('2020-07-27 00:00:00')

In [86]: pd.Timestamp('20200907') + pd.offsets.MonthEnd()
Out[86]: Timestamp('2020-09-30 00:00:00')

常用的日期偏置如下可以查閱這里的 文檔 描述并蝗。在文檔羅列的 Offset 中祭犯,需要介紹一個(gè)特殊的 Offset 對(duì)象 CDay ,其中的 holidays, weekmask 參數(shù)能夠分別對(duì)自定義的日期和星期進(jìn)行過(guò)濾滚停,前者傳入了需要過(guò)濾的日期列表沃粗,后者傳入的是三個(gè)字母的星期縮寫構(gòu)成的星期字符串,其作用是只保留字符串中出現(xiàn)的星期:

In [87]: my_filter = pd.offsets.CDay(n=1,weekmask='Wed Fri',holidays=['20200109'])

In [88]: dr = pd.date_range('20200108', '20200111')

In [89]: dr.to_series().dt.dayofweek
Out[89]: 
2020-01-08    2
2020-01-09    3
2020-01-10    4
2020-01-11    5
Freq: D, dtype: int64

In [90]: [i + my_filter for i in dr]
Out[90]: 
[Timestamp('2020-01-10 00:00:00'),
 Timestamp('2020-01-10 00:00:00'),
 Timestamp('2020-01-15 00:00:00'),
 Timestamp('2020-01-15 00:00:00')]

上面的例子中键畴, n 表示增加一天 CDay 最盅, dr 中的第一天為 20200108 ,但由于下一天 20200109 被排除了起惕,并且 20200110 是合法的周五涡贱,因此轉(zhuǎn)為 20200110 ,其他后面的日期處理類似惹想。


image.png
  1. 偏置字符串
    前面提到了關(guān)于 date_range 的 freq 取值可用 Offset 對(duì)象问词,同時(shí)在 pandas 中幾乎每一個(gè) Offset 對(duì)象綁定了日期偏置字符串( frequencies strings/offset aliases ),可以指定 Offset 對(duì)應(yīng)的字符串來(lái)替代使用嘀粱。下面舉一些常見的例子激挪。
In [91]: pd.date_range('20200101','20200331', freq='MS') # 月初
Out[91]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')

In [92]: pd.date_range('20200101','20200331', freq='M') # 月末
Out[92]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')

In [93]: pd.date_range('20200101','20200110', freq='B') # 工作日
Out[93]: 
DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],
              dtype='datetime64[ns]', freq='B')

In [94]: pd.date_range('20200101','20200201', freq='W-MON') # 周一
Out[94]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='W-MON')

In [95]: pd.date_range('20200101','20200201',
   ....:               freq='WOM-1MON') # 每月第一個(gè)周一
   ....: 
Out[95]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')

上面的這些字符串,等價(jià)于使用如下的 Offset 對(duì)象:

In [96]: pd.date_range('20200101','20200331',
   ....:               freq=pd.offsets.MonthBegin())
   ....: 
Out[96]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')

In [97]: pd.date_range('20200101','20200331',
   ....:               freq=pd.offsets.MonthEnd())
   ....: 
Out[97]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')

In [98]: pd.date_range('20200101','20200110', freq=pd.offsets.BDay())
Out[98]: 
DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],
              dtype='datetime64[ns]', freq='B')

In [99]: pd.date_range('20200101','20200201',
   ....:               freq=pd.offsets.CDay(weekmask='Mon'))
   ....: 
Out[99]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='C')

In [100]: pd.date_range('20200101','20200201',
   .....:               freq=pd.offsets.WeekOfMonth(week=0,weekday=0))
   .....: 
Out[100]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')
image.png

五锋叨、時(shí)序中的滑窗與分組

  1. 滑動(dòng)窗口
    所謂時(shí)序的滑窗函數(shù)垄分,即把滑動(dòng)窗口用 freq 關(guān)鍵詞代替,下面給出一個(gè)具體的應(yīng)用案例:在股票市場(chǎng)中有一個(gè)指標(biāo)為 BOLL 指標(biāo)娃磺,它由中軌線薄湿、上軌線、下軌線這三根線構(gòu)成豌鸡,具體的計(jì)算方法分別是 N 日均值線嘿般、 N 日均值加兩倍 N 日標(biāo)準(zhǔn)差線、 N 日均值減兩倍 N 日標(biāo)準(zhǔn)差線涯冠。利用 rolling 對(duì)象計(jì)算 N=30 的 BOLL 指標(biāo)可以如下寫出:
In [101]: import matplotlib.pyplot as plt

In [102]: idx = pd.date_range('20200101', '20201231', freq='B')

In [103]: np.random.seed(2020)

In [104]: data = np.random.randint(-1,2,len(idx)).cumsum() # 隨機(jī)游動(dòng)構(gòu)造模擬序列

In [105]: s = pd.Series(data,index=idx)

In [106]: s.head()
Out[106]: 
2020-01-01   -1
2020-01-02   -2
2020-01-03   -1
2020-01-06   -1
2020-01-07   -2
Freq: B, dtype: int32

In [107]: r = s.rolling('30D')

In [108]: plt.plot(s)
Out[108]: [<matplotlib.lines.Line2D at 0x1520d5bb548>]

In [109]: plt.title('BOLL LINES')
Out[109]: Text(0.5, 1.0, 'BOLL LINES')

In [110]: plt.plot(r.mean())
Out[110]: [<matplotlib.lines.Line2D at 0x1520d78fec8>]

In [111]: plt.plot(r.mean()+r.std()*2)
Out[111]: [<matplotlib.lines.Line2D at 0x1520d78f788>]

In [112]: plt.plot(r.mean()-r.std()*2)
Out[112]: [<matplotlib.lines.Line2D at 0x1520d6f3788>]
image.png

對(duì)于 shift 函數(shù)而言,作用在 datetime64 為索引的序列上時(shí)逼庞,可以指定 freq 單位進(jìn)行滑動(dòng):

In [113]: s.shift(freq='50D').head()
Out[113]: 
2020-02-20   -1
2020-02-21   -2
2020-02-22   -1
2020-02-25   -1
2020-02-26   -2
dtype: int32

另外蛇更, datetime64[ns] 的序列進(jìn)行 diff 后就能夠得到 timedelta64[ns] 的序列,這能夠使用戶方便地觀察有序時(shí)間序列的間隔:

In [114]: my_series = pd.Series(s.index)

In [115]: my_series.head()
Out[115]: 
0   2020-01-01
1   2020-01-02
2   2020-01-03
3   2020-01-06
4   2020-01-07
dtype: datetime64[ns]

In [116]: my_series.diff(1).head()
Out[116]: 
0      NaT
1   1 days
2   1 days
3   3 days
4   1 days
dtype: timedelta64[ns]

  1. 重采樣
    重采樣對(duì)象 resample 和第四章中分組對(duì)象 groupby 的用法類似赛糟,前者是針對(duì)時(shí)間序列的分組計(jì)算而設(shè)計(jì)的分組對(duì)象派任。

例如,對(duì)上面的序列計(jì)算每10天的均值:

In [117]: s.resample('10D').mean().head()
Out[117]: 
2020-01-01   -2.000000
2020-01-11   -3.166667
2020-01-21   -3.625000
2020-01-31   -4.000000
2020-02-10   -0.375000
Freq: 10D, dtype: float64

同時(shí)璧南,如果沒有內(nèi)置定義的處理函數(shù)掌逛,可以通過(guò) apply 方法自定義:

In [118]: s.resample('10D').apply(lambda x:x.max()-x.min()).head() # 極差
Out[118]: 
2020-01-01    3
2020-01-11    4
2020-01-21    4
2020-01-31    2
2020-02-10    4
Freq: 10D, dtype: int32

在 resample 中要特別注意組邊界值的處理情況,默認(rèn)情況下起始值的計(jì)算方法是從最小值時(shí)間戳對(duì)應(yīng)日期的午夜 00:00:00 開始增加 freq 司倚,直到不超過(guò)該最小時(shí)間戳的最大時(shí)間戳豆混,由此對(duì)應(yīng)的時(shí)間戳為起始值篓像,然后每次累加 freq 參數(shù)作為分割結(jié)點(diǎn)進(jìn)行分組,區(qū)間情況為左閉右開皿伺。下面構(gòu)造一個(gè)不均勻的例子:

In [119]: idx = pd.date_range('20200101 8:26:35', '20200101 9:31:58', freq='77s')

In [120]: data = np.random.randint(-1,2,len(idx)).cumsum()

In [121]: s = pd.Series(data,index=idx)

In [122]: s.head()
Out[122]: 
2020-01-01 08:26:35   -1
2020-01-01 08:27:52   -1
2020-01-01 08:29:09   -2
2020-01-01 08:30:26   -3
2020-01-01 08:31:43   -4
Freq: 77S, dtype: int32

下面對(duì)應(yīng)的第一個(gè)組起始值為 08:24:00 员辩,其是從當(dāng)天0點(diǎn)增加72個(gè) freq=7 min 得到的,如果再增加一個(gè) freq 則超出了序列的最小時(shí)間戳 08:26:35 :

In [123]: s.resample('7min').mean().head()
Out[123]: 
2020-01-01 08:24:00   -1.750000
2020-01-01 08:31:00   -2.600000
2020-01-01 08:38:00   -2.166667
2020-01-01 08:45:00    0.200000
2020-01-01 08:52:00    2.833333
Freq: 7T, dtype: float64

有時(shí)候鸵鸥,用戶希望從序列的最小時(shí)間戳開始依次增加 freq 進(jìn)行分組奠滑,此時(shí)可以指定 origin 參數(shù)為 start :

In [124]: s.resample('7min', origin='start').mean().head()
Out[124]: 
2020-01-01 08:26:35   -2.333333
2020-01-01 08:33:35   -2.400000
2020-01-01 08:40:35   -1.333333
2020-01-01 08:47:35    1.200000
2020-01-01 08:54:35    3.166667
Freq: 7T, dtype: float64

在返回值中,要注意索引一般是取組的第一個(gè)時(shí)間戳妒穴,但 M, A, Q, BM, BA, BQ, W 這七個(gè)是取對(duì)應(yīng)區(qū)間的最后一個(gè)時(shí)間戳:

In [125]: s = pd.Series(np.random.randint(2,size=366),
   .....:               index=pd.date_range('2020-01-01',
   .....:                                   '2020-12-31'))
   .....: 

In [126]: s.resample('M').mean().head()
Out[126]: 
2020-01-31    0.451613
2020-02-29    0.448276
2020-03-31    0.516129
2020-04-30    0.566667
2020-05-31    0.451613
Freq: M, dtype: float64

In [127]: s.resample('MS').mean().head() # 結(jié)果一樣宋税,但索引不同
Out[127]: 
2020-01-01    0.451613
2020-02-01    0.448276
2020-03-01    0.516129
2020-04-01    0.566667
2020-05-01    0.451613
Freq: MS, dtype: float64

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市讼油,隨后出現(xiàn)的幾起案子杰赛,更是在濱河造成了極大的恐慌,老刑警劉巖汁讼,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淆攻,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡嘿架,警方通過(guò)查閱死者的電腦和手機(jī)瓶珊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)耸彪,“玉大人伞芹,你說(shuō)我怎么就攤上這事〔跄龋” “怎么了唱较?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)召川。 經(jīng)常有香客問我南缓,道長(zhǎng),這世上最難降的妖魔是什么荧呐? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任汉形,我火速辦了婚禮,結(jié)果婚禮上倍阐,老公的妹妹穿的比我還像新娘概疆。我一直安慰自己,他們只是感情好峰搪,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布岔冀。 她就那樣靜靜地躺著,像睡著了一般概耻。 火紅的嫁衣襯著肌膚如雪使套。 梳的紋絲不亂的頭發(fā)上罐呼,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音童漩,去河邊找鬼弄贿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛矫膨,可吹牛的內(nèi)容都是我干的差凹。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼侧馅,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼危尿!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起馁痴,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤谊娇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后罗晕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體济欢,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年小渊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了法褥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡酬屉,死狀恐怖半等,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情呐萨,我是刑警寧澤杀饵,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站谬擦,受9級(jí)特大地震影響切距,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜惨远,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一蔚舀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锨络,春花似錦、人聲如沸狼牺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)是钥。三九已至掠归,卻和暖如春缅叠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背虏冻。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工肤粱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人厨相。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓领曼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蛮穿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子庶骄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容