- ?- { l6 t' a) A q Vpd.date_range('20200101','20200331', freq='M') # 月末 & |) ]' A: V! z6 J& i& pOut[92]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M') 6 \/ e% h* Q6 k$ s( [# f/ b 0 {6 t% v: U6 i# s. I" d8 Wpd.date_range('20200101','20200110', freq='B') # 工作日. v4 ~( X/ A+ g. X5 `2 Z" n' ?- z
Out[93]: * J3 |0 ?% [# u0 i& _
DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06', 4 U( f8 H9 d$ L% s5 |4 I) Z( a '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],9 {' l# t3 h3 P1 D4 f2 g( g
dtype='datetime64[ns]', freq='B') v9 T0 Q$ b' S2 s3 [8 p* I8 k1 `4 i2 O$ u
pd.date_range('20200101','20200201', freq='W-MON') # 周一 v6 h3 Q; z5 _! ~! d
Out[94]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='W-MON')4 @1 y, L5 t. d3 l) k. U) t7 ]% ^0 n
8 g( N; a p+ A+ `pd.date_range('20200101','20200201',' u0 d4 H h( I5 _" k1 k% R
freq='WOM-1MON') # 每月第一个周一 8 e. s8 I4 ?# V5 F x! s 5 r: i, h6 @1 D1 P7 A _) jOut[95]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')5 ~; B/ @' ~: L" U. n
: m& c9 m% r2 x# _7 E1& s+ y6 X; k. l$ [+ |
24 K! _' l. M2 `5 L2 _5 C7 V
3 7 M& O1 `* s1 R9 f# D4, K4 a/ _5 D3 A" h3 t! {7 T/ K- b h
5/ H' b' I: ~2 C6 I6 B/ ^
6, Z& _! f* ?( H5 Y
75 _: e# T; F' \) t9 u2 e" }+ H( {. S
8 4 P. T* A8 l/ U2 w9 ' U! Q. K# d. B T; ^+ E0 R( J100 p$ }" D6 R; U& x
11 , S% D8 c' W9 w- d12 ; [5 o- L" w! w8 |( i/ e |0 h139 h8 T" Q! i. O- c4 }
14. n$ A. _- r1 ?2 C, t9 w
15 6 ?( J6 ]8 m) ?/ h163 E1 y: V( L: h* b( Q
17- I; T0 n, d( o# M5 H
18 ! x* \' o; p% G0 {) ?# l, L- l198 W4 J. N2 [" \1 o/ o
上面的这些字符串,等价于使用如下的 Offset 对象:% g9 }5 t' e$ z0 D& e% W. a
1 A7 W5 w! ~, h, n( P
pd.date_range('20200101','20200331',0 c. `3 S+ z6 @3 q8 o
freq=pd.offsets.MonthBegin())5 `5 V3 z X$ l
. g: q4 V2 I/ d' F( m+ N
Out[96]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')) {+ k( q! ?! o" v& @# u- K
( x. b% A }" h) e& P
pd.date_range('20200101','20200331', 1 k, {, e# b' k8 t freq=pd.offsets.MonthEnd()). \7 B" [( a: B/ B1 Z
" L0 R* t& X% ?, {. N# b% P
Out[97]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M') 7 ~7 a/ R0 P9 b. r- j& z7 X; H2 T4 p) n
pd.date_range('20200101','20200110', freq=pd.offsets.BDay())2 t# w% s. m9 X
Out[98]: - Z+ |& A* b* \2 ?" d# [DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',: ]# Q) `/ d+ ]) J, Z* O! W
'2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'], 4 \# r* T! x7 |$ A dtype='datetime64[ns]', freq='B')% R) V& a* M6 ?' r, T5 z) ?
$ O- h' B8 u+ n: B; B
pd.date_range('20200101','20200201', j* g @* W5 p0 r1 o, i$ y' J
freq=pd.offsets.CDay(weekmask='Mon'))% ~ k1 X; K# H/ j; d4 I# }: Q
5 F3 i# p3 e- d8 Q7 \9 x1 C4 k+ N
Out[99]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='C')- x# K+ m1 Y+ g: I# P! `& r
2 H N! A% I7 x/ d. ~% B$ Jpd.date_range('20200101','20200201'," A9 m) m9 O/ n/ t, W, G
freq=pd.offsets.WeekOfMonth(week=0,weekday=0)) 6 }+ J! ?* P0 k. v' [+ K5 e) i+ w: q6 m! n7 v
Out[100]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')9 p% u8 `# p+ C6 w
9 Y+ N( w8 w& h1% u& N/ M2 t7 a
2* z9 ?; C" {; p
3 3 O3 R3 L3 [. j0 k% a4 % S& K# i6 B1 V V @8 y: `5 ; ?- _/ g# p( V. p4 B& p6 ?6% ^( p7 Q- d" e! D+ Y# |2 M) T4 p" a( k
7 8 f$ |6 d9 F2 t8 # @7 ~; t. i8 m% D9! n ~9 J$ T# Q: _4 o
10 & V; c4 v) G# t' W11 1 j0 `, E% v- E( ]12 8 o0 r4 Z2 K/ u. a7 @134 @0 d; R- Z$ Q& `5 `- R
14) X4 V3 y0 f7 q
15 & ~# g7 F+ H/ Y9 }, P; W0 v9 z4 v160 P4 E# y& P4 `2 w6 ?
17 7 D2 X, w& v, _. w18 8 e! b4 c, w. b19 + c! c0 `/ i7 I% s6 Q w3 t' c0 s20 , n; I, `$ {5 t* j/ ^7 g; ~21( R% ^' E! m! P& M! N) N
22 / n: s1 n" M; h23' }, N2 o' H( z
245 ~- [6 y$ _6 k% Y t; T
25, r+ d; H$ L& m7 h9 f. l; T `
【CAUTION】关于时区问题的说明 0 |. C% j* p: ? Y8 L" b 各类时间对象的开发,除了使用python内置的datetime模块,pandas还利用了dateutil模块,很大一部分是为了处理时区问题。总所周知,我国是没有夏令时调整时间一说的,但有些国家会有这种做法,导致了相对而言一天里可能会有23/24/25个小时,也就是relativedelta,这使得Offset对象和Timedelta对象有了对同一问题处理产生不同结果的现象,其中的规则也较为复杂,官方文档的写法存在部分描述错误,并且难以对描述做出统一修正,因为牵涉到了Offset相关的很多组件。因此,本教程完全不考虑时区处理,如果对时区处理的时间偏置有兴趣了解讨论,可以联系我或者参见这里的讨论。 6 E( N W/ n7 C5 v, h) q4 D! w' }* H3 s, \$ O
10.5、时序中的滑窗与分组% |- W. d! J' Z% O
10.5.1 滑动窗口7 s* N2 o: _: \7 x# }
所谓时序的滑窗函数,即把滑动窗口windows用freq关键词代替,下面给出一个具体的应用案例:在股票市场中有一个指标为BOLL指标,它由中轨线、上轨线、下轨线这三根线构成,具体的计算方法分别是N日均值线、N日均值加两倍N日标准差线、N日均值减两倍N日标准差线。利用rolling对象计算N=30的BOLL指标可以如下写出:6 c& e+ g# k% H4 u
- a n3 c9 T% Yimport matplotlib.pyplot as plt * H/ U7 j' D: \: @2 Didx = pd.date_range('20200101', '20201231', freq='B') & f8 ^4 X4 z( W! fnp.random.seed(2020) ( U8 @- n T D4 n 7 b& \( n0 n) B1 B Kdata = np.random.randint(-1,2,len(idx)).cumsum() # 随机游动构造模拟序列,cumsum表示累加 . B1 N) \( B5 L6 X9 U( R+ b# ls = pd.Series(data,index=idx) ! W3 O- O( X U' Z$ }' }, L3 Cs.head()' _; ]1 r. x! b1 }/ z5 Q
Out[106]: 0 k; D' u- I$ e" o0 W$ V" T
2020-01-01 -16 T1 W, V3 W; a5 |2 W$ i
2020-01-02 -2! }# @% F9 {: S6 }, E Q
2020-01-03 -1 * \0 Y! D- j+ w# k. A0 C2020-01-06 -1 $ }: {9 k0 M" D6 l2020-01-07 -27 h1 A( _# R+ Z R6 W
Freq: B, dtype: int32 " X$ d- d1 B8 P9 cr = s.rolling('30D')# rolling可以指定freq或者offset对象 ) a" f$ a: Y) r: G" f) I j- }+ J( Q) G9 T3 ^" Oplt.plot(s) # 蓝色线 . ~' ], y. W# L- n- I3 o/ A! dOut[108]: [<matplotlib.lines.Line2D at 0x2116d887eb0>] ! p$ E. B. o. Kplt.title('BOLL LINES')1 Z9 C2 n! U" ] _- v9 ?6 z
Out[109]: Text(0.5, 1.0, 'BOLL LINES'): q5 v. N! _/ D6 B7 ]) r6 c, O- l
* O8 [# y9 ]. g4 qplt.plot(r.mean()) #橙色线: W. b& j% _' A' x
Out[110]: [<matplotlib.lines.Line2D at 0x2116d8eeb80>]3 b. ^# ?* w# a( J0 i
3 Q, e5 u7 |4 ?plt.plot(r.mean()+r.std()*2) # 绿色线5 c. [8 r9 V2 Y0 U
Out[111]: [<matplotlib.lines.Line2D at 0x2116d87efa0>]: Z$ Y3 C0 W/ b' b3 X+ b
1 N$ Y: J5 T% \3 Q) h2 {8 K
plt.plot(r.mean()-r.std()*2) # 红色线 8 Q- N" |6 P6 R5 HOut[112]: [<matplotlib.lines.Line2D at 0x2116d90d2e0>] 4 G3 Z6 b A; Y" v. x4 K5 K9 S- d1 V
1 - U5 J( Z. L P K2 6 y. z8 U% H9 ?$ i3+ c0 X7 e/ f$ C
4 , n' I1 ~1 |2 n- c5" N- ]1 t6 P( }4 l' K# S& r4 k% S6 W
6+ M, _: ~# @1 J8 W
76 X1 i- ~$ d; @+ ?
8 4 ~( G, S0 t3 j/ Z9 , x q; _# c; J5 L7 u) h5 I' D! W10 Q: ~- T [4 P) Z' C; `- U, L11% n0 Z- m4 k" g' \3 [
12! b6 B9 h" v4 S
13+ P: P; z, p8 o( B. R
14% `5 V- I6 V6 b
156 @4 C- }. i8 j' H1 {! G1 z6 f4 d) s
16 k3 b' m2 e# U' j" | m; }7 q
17 ) f0 g: G3 L5 B18+ B' ~, `& s# n1 r% ^% @. }
191 J2 j$ [0 {: W1 z/ z* r
20 ' e5 P) \: q7 z" z3 u3 v h" S21 . o' O2 C. `6 p; g1 B* ~+ N22 + m: F% _9 ~ a8 k) l: L" p. p23; ^3 q6 v1 _7 t- ~% f
24 ; ~( |! K5 B7 J( u: V+ K25. z$ j6 S2 P4 `
26 - t8 ^9 ~. o7 M# c( B+ T! b27% p- X2 Y% P* _. ~; {4 `1 h
28 7 L* }1 p, o# x* S29 2 E: ], L$ w6 c9 S: Z$ [) w7 ]! J3 @ R0 I, |' v% i
这里需要注意的是,pandas没有实现非固定采样频率的时间序列滑窗,及此时无法通过传入freq字段来得到滑窗结果。例如统计近7个工作日的交易总额。此时可以通过传入多个函数的组合来实现此功能。" i( B( u- Z5 |: n9 V U
首先选出所有工作日,接着用普通滑窗进行7日滑窗加和,最后用reindex()恢复索引,对于双休日使用前一个工作日的结果进行填充。 \; L* E5 ]3 Y: q7 W
+ a" F7 m; W t& g7 g
select_bday=s[~s.index.to_series().dt.dayofweek.isin([5,6])] 4 J. J) O$ I _& P/ n. gbday_sum=select_bday.rolling(7,min_periods=1).sum() / ?/ g. W+ _9 g0 ?# Y2 A5 q# lresult=bday_sum.reindex().ffill()* q/ @* ]( ]5 J
result . Q/ V/ o' R* K. Z! p0 F0 I% p- m) q5 X4 G& [: {$ I/ L1 J; ]/ M7 p( h
2020-01-01 -1.0 ; }8 w+ H7 J2 R: L. p2020-01-02 -3.0 ) b1 \+ M- F' p2 j. Y3 ^2020-01-03 -4.0/ ?2 D3 Z1 @; u4 @
2020-01-06 -5.0 ; D( }3 S, q/ ?2020-01-07 -7.0: O+ p3 A# e+ F5 B+ k
... * w' e5 \9 |5 J
2020-12-25 136.00 v6 w% O* Z( t' `1 m+ r* t1 N
2020-12-28 133.0 1 T7 h4 k1 Q% B* ~6 `* S- b, s1 H2020-12-29 131.0 : A0 G* m1 _: H2020-12-30 130.0% r2 w; Z5 ]; l& S- B/ u
2020-12-31 128.05 X$ r. ]3 W# A/ Y+ K
Freq: B, Length: 262, dtype: float64 m, H( }4 ` e3 Z; { v6 t3 Q
; N# a% U7 V1 y$ a1 & Q8 H. H" W( c7 G: ^28 X j, @9 a s: E
3: ]$ t. @2 s4 Z( Z2 P" L9 ?
4 `& G0 v, ^3 G9 y! S' b9 O5# [4 F) P7 Y B9 c" U- O
6; x" b' w5 a" S# b" r' U
7 W* L5 f2 U ~7 {
8$ ?: I) K5 ^2 l% k! o
9 M' |/ Z2 q/ Y) ~: \
10 / O$ a8 d4 p6 A0 G% p11* `6 e9 y4 S1 t
12, M, B. ?2 k8 f$ C
13* ^( h- ^: h9 ?+ Z
148 c% n- [5 l7 h* J( j& |# W' m& B G
15# P- v% u0 x; [6 k
16 + U% _- S8 Z, _0 B177 v V( p, d$ Z
shift, diff, pct_change 是一组类滑窗函数,它们的公共参数为 periods=n ,默认为1,分别表示取向前第 n 个元素的值、与向前第 n 个元素做差(与 Numpy 中不同,后者表示 n 阶差分)、与向前第 n 个元素相比计算增长率。这里的 n 可以为负,表示反方向的类似操作。 - ~7 y# g) g7 y( ~. l7 S4 y) s+ Y5 t" s6 z5 B
对于shift函数而言,作用在datetime64为索引(不是value)的序列上时,可以指定freq单位进行滑动: 3 ?% X2 [, ?& e% Q" _ * `6 B7 n+ Q, S8 z3 Es.shift(freq='50D').head() ' e5 j! k2 l+ s. aOut[113]: ) V0 c+ `" v$ I: G& v( O
2020-02-20 -1. Z2 O7 p/ I0 n9 O* b
2020-02-21 -26 h' S( W4 T7 W7 B0 x
2020-02-22 -1# Y$ n( m$ ], V f+ u. y5 ~
2020-02-25 -1 4 S. ~8 f- ?3 ^% S/ A* w" Q6 K' U2020-02-26 -2 + w# u4 J$ U# X9 [, H# @' rdtype: int32 + R; w) M& c& @1 j' Y1 : x! G& u0 V8 |0 k7 t ]/ w' h2 0 ?) L3 F( d u6 b3 " k) ]! _1 l F! V% a0 Y" F. R4 h4 n) M- O: {) R
5 ) M9 F5 d0 z6 a2 @6 ! g* t. n3 L8 q6 l- z4 G8 ~7! J4 U, @$ W2 i: r* ?
8 5 J4 {4 |' g+ E- O4 ]" G0 k 另外,datetime64[ns]的序列进行diff(前后做差)后就能够得到timedelta64[ns]的序列,这能够使用户方便地观察有序时间序列的间隔: # A& Q) `6 u& u+ A' K! m Y0 U% ~) D: A" i( p3 [* [0 [2 k& _
my_series = pd.Series(s.index)* ?6 G, Q) ], Q. z( r- Q
my_series.head() # ^. p A4 _# \5 C8 g4 a7 {# FOut[115]: # ~, |9 r( ~/ a
0 2020-01-01+ w* q0 ?! ^: b3 h. [
1 2020-01-027 J( w$ k. |( P! Z, B* t
2 2020-01-03 6 W: |& P& I$ H, p. P, I6 Z3 2020-01-06 1 _; y* d, B9 h4 2020-01-07' E# v4 S0 _4 Z/ N/ Z |
dtype: datetime64[ns] U& m! e0 W7 Z: a6 G- l! ~ 1 X) b1 R2 v4 C8 E7 _my_series.diff(1).head() 8 x; u: Q. S6 W* O' iOut[116]: 6 k S9 P2 w6 X( S
0 NaT0 e' r% _+ T- T v* v
1 1 days . K& i% d. M9 [- D9 u6 q$ v' q( \2 1 days4 p7 V9 m8 ~- }# f* Z& H
3 3 days . {' v3 P; a. Z# L# u4 1 days0 R# u4 C( M% O" |0 w5 ^' [. X5 L
dtype: timedelta64[ns] ^" a |8 D! F9 Z1 Z' [, n