在线时间 1630 小时 最后登录 2024-1-29 注册时间 2017-5-16 听众数 82 收听数 1 能力 120 分 体力 569587 点 威望 12 点 阅读权限 255 积分 176099 相册 1 日志 0 记录 0 帖子 5313 主题 5273 精华 3 分享 0 好友 163
TA的每日心情 开心 2021-8-11 17:59
签到天数: 17 天
[LV.4]偶尔看看III
网络挑战赛参赛者
网络挑战赛参赛者
自我介绍 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
群组 : 2018美赛大象算法课程
群组 : 2018美赛护航培训课程
群组 : 2019年 数学中国站长建
群组 : 2019年数据分析师课程
群组 : 2018年大象老师国赛优
9 x% O$ H `6 u5 G7 L+ l
8 P6 ^' |6 E! T' D4 @# @* K
" ^4 M( B. d6 A5 V4 `( H8 k 文章目录
: i' k0 W3 e8 J% _9 R* f 第八章 文本数据
# c# U4 {0 B$ d- u- ` 8.1 str对象( [5 Y, N( _) Y# o; |0 R
8.1.1 str对象的设计意图
8 h. a& u/ E' P 8.1.3 string类型: ]; }7 H% a& a
8.2 正则表达式基础
' B5 W) I! J, p 8.2.1 . 一般字符的匹配! n7 U. {# I2 ]
8.2.2 元字符基础! s7 V( F* N* I/ I$ B% @- g" Q$ ]
8.2.3 简写字符集
. t) m) u0 M- _5 I0 J 8.3 文本处理的五类操作
6 l- ]+ c( w0 o. `1 }; N, F 8.3.1 `str.split `拆分
; J0 J* U: `8 z0 G! q4 n2 j 8.3.2 `str.join` 或 `str.cat `合并
) W5 ?( Q, m u" c+ C$ H0 \ 8.3.3 匹配& z" ^8 x6 P" n0 R+ z: A
8.3.5 提取
6 i1 I- M2 X: v7 t1 o 8.4、常用字符串函数
& y9 p) l) k1 ^; j% p2 B, B 8.4.1 字母型函数
* }0 _7 O& v, R9 h$ u 8.4.2 数值型函数
5 \) C! u5 X4 k0 R* o 8.4.3 统计型函数
. r+ X J! h8 A+ y, o" M: { 8.4.4 格式型函数
1 i% c/ ^7 S$ s3 x- S! C" i4 U 8.5 练习
% G! z: _7 `5 D( i6 {3 b2 Y Ex1:房屋信息数据集
4 _0 b9 t: i! S) t Ex2:《权力的游戏》剧本数据集. a4 a$ P5 R" w0 y3 w) B0 ^
第九章 分类数据
+ _* _! z% h& o' ?- L& S' e* ]# F" d 9.1 cat对象
$ z/ p* ?# E6 \& C5 {. N, ?8 O 9.1.1 cat对象的属性4 K+ x" M9 {" B
9.1.2 类别的增加、删除和修改
, ?0 a6 E% V6 [' Q" J; h 9.2 有序分类1 }9 y& N1 ]9 f+ s4 l2 L
9.2.1 序的建立4 M, s! i) L9 [& r/ @" X, Y
9.2.2 排序和比较) o7 u/ n& Q, M9 k; c1 ^! m. d$ m. |7 x
9.3 区间类别
6 B4 ?+ J3 b' R: w 9.3.1 利用cut和qcut进行区间构造
/ g/ L/ @1 C% y( H, z# M. z 9.3.2 一般区间的构造4 c; Y! l; a1 R, |
9.3.3 区间的属性与方法
& Y8 r- d/ `9 i& }0 C# E 9.4 练习
. [1 y/ {/ O8 [3 _ Ex1: 统计未出现的类别+ G# k1 @" `. g/ X0 H" f
Ex2: 钻石数据集
6 J7 @& J& g4 G8 e6 R0 c 第十章 时序数据
4 @. ]1 U6 J' ~; T; t. J6 q* f 10.1 时序中的基本对象# C) l) C( n& @% j9 J- o5 a
10.2 时间戳; u g. M6 s" m6 V. |2 G
10.2.1 Timestamp的构造与属性! Z+ d) N' S, u8 i
10.2.2 Datetime序列的生成5 r" q6 j1 T: _
10.2.3 dt对象& v1 y' J8 C# L' V/ S$ W( e$ i9 ^
10.2.4 时间戳的切片与索引
. i$ R# ?; m/ K% ]: D 10.3 时间差
9 J: j! I8 Z0 G; Q3 p! n$ ^- l 10.3.1 Timedelta的生成
2 I) g3 v1 H' ~. q4 V( P) X 10.2.2 Timedelta的运算
1 J) Y$ Z; F7 p0 f7 F0 @ 10.4 日期偏置. }+ Y& x- y' _1 N% c8 @+ L2 K
10.4.1 Offset对象( X1 N& F' }, F5 r/ s
10.4.2 偏置字符串& S1 o) y- `2 k4 s: F
10.5、时序中的滑窗与分组* t& \( r4 c$ R
10.5.1 滑动窗口
) t) B4 @- d5 l, d2 O% E4 p6 {* g 10.5.2 重采样( I- ?- o g; R1 ]
10.6 练习
9 x0 G. Y& A/ Y4 y1 p3 O- | Ex1:太阳辐射数据集4 }6 E2 b: E+ \, g* Q/ g
Ex2:水果销量数据集# a! {$ S4 R: t( v8 u5 a
课程资料《pandas数据处理与分析》、github地址、讲解视频、习题参考答案 、pandas官网
4 B. W# I4 F. q6 N" H5 S" } 传送门:
5 P+ k/ d, ~! Z: i
+ s1 Q+ O# W" \4 J9 [# ~ datawhale8月组队学习《pandas数据处理与分析》(上)(基础、索引、分组)3 M9 t) |: ~% M1 F" V, {2 y
datawhale8月组队学习《pandas数据处理与分析》(中)(变形、连接、缺失数据)5 c" H# f2 r, `$ d
第八章 文本数据
* t/ X- `6 D7 T8 K 8.1 str对象
, Y$ j6 p+ \* M3 J6 G2 O+ J/ D6 w 8.1.1 str对象的设计意图# h3 I, K6 l* }+ I. Q* s7 U
str 对象是定义在 Index 或 Series上的属性,专门用于处理每个元素的文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其 str 对象。在Python标准库中也有 str 模块,为了使用上的便利,在 pandas 的50个 str 对象方法中,有31个是和标准库中的 str 模块方法同名且功能一致,例如字母转为大写的操作:5 X5 U% C: q# O/ @1 A
1 ^5 b+ U, C$ G f' J4 l3 R8 l
var = 'abcd'
% q$ B; t% G/ S; M/ b str.upper(var) # Python内置str模块& \1 B4 f& U( O% C; {3 J' M; T& }
Out[4]: 'ABCD'
1 @, @2 v+ w. K" \9 z 7 d: R6 O! l7 c6 H2 R
s = pd.Series(['abcd', 'efg', 'hi'])
0 }* f* Q' u# Q- B' c 1 _1 V/ H# U3 x1 `5 I. J
s.str# h: y2 E) m; i- X: m1 x. T! o1 P3 ~
Out[6]: <pandas.core.strings.accessor.StringMethods at 0x2b796892d60>" \0 o$ Z# @* I1 ^
1 m" ?, J5 Q# F) J) i; k s.str.upper() # pandas中str对象上的upper方法
N; ?$ U ^- O$ w i1 p e5 E9 A Out[7]:
. R0 }; ^; w6 @/ O! b7 C) v 0 ABCD
9 o4 a# F( ~' {. _; f9 E 1 EFG
0 {$ F4 K4 |" _. q 2 HI6 k! W* p1 I6 \0 f) ~- f) Y
dtype: object
! V- y( A; c1 A6 s 1, \% s& ?( a5 S( ^/ ^7 M
2! a t4 K* N5 ?& ?" x# u
3
7 f3 d! ]4 Y# H, d5 m: ? 4# n/ W3 z, [, y& f. K4 G
5/ B1 |/ P$ i( Z8 H' M3 P7 Q/ v
6
1 P9 ?, I* A8 i E, }. O 7
' H' M+ g U4 a, A) G 8
3 u( c; M- F) p 9
& P! o* ?2 u Q! X1 C, g 10/ j( @) ?3 T: ~9 C+ P$ P5 `
11$ @) h( |/ O' f# Z, p* V. z
12
6 b5 o l( s! z3 n- ` 13: C, M* |& Y# b' K0 x7 b. g
14& @7 s2 i' g# Z& y+ _) S) M E1 f
157 {, k9 \' f' _% f# D3 _1 n* n4 f ]- O4 K
8.1.2 []索引器
: H- ?' h, [' p! K! | 对于 str 对象而言,可理解为其对字符串进行了序列化的操作,例如在一般的字符串中,通过 [] 可以取出某个位置的元素,同时也能通过切片得到子串。
9 e/ `. g: r6 p2 d pandas中过对 str 对象使用 [] 索引器,可以完成完全一致的功能,并且如果超出范围则返回缺失值:
% n! o& u' L9 n+ O- K
# [+ ?" g! D0 Y- w/ |7 C8 S0 ? s.str[0]
2 t" f1 J$ R- V. F( f9 l! D Out[10]: ! u5 P2 q( i L2 U4 N' o( J# b' `
0 a
; U) w1 G) s, A) ? 1 e% h! Z; e2 I7 @2 O8 [* c. K$ B
2 h- G8 T7 {6 E/ I; v# s/ ^
dtype: object* d. q8 g$ X* D0 W
- ]8 Z4 W0 I4 G s.str[-1: 0: -2]5 O8 W" ?8 w$ }" F
Out[11]: * H+ o0 c( g$ C& q7 B$ [
0 db6 l+ p8 D9 H" y7 {( s
1 g
9 o8 t& }* N/ ^. T1 e% X( R0 [ 2 i6 `, g5 B6 f5 X) x' E8 c5 V
dtype: object
% f* O5 ~; J! h; g* h% @
2 P1 g8 L# ~2 r1 O' Z9 ] s.str[2]9 c& \4 ?" @2 a$ ?# c% R: J3 o
Out[12]:
% p9 ]# A3 x& p 0 c5 e4 o5 T' w5 o2 G' r- D- ]4 M
1 g
8 {9 W& |; n/ \4 n. P1 ?0 h 2 NaN
0 ]+ s Z3 W3 p9 ]7 b dtype: object
0 j* c$ a1 k0 ] 4 T7 l8 {" B! m5 E2 p7 |1 U
1
2 h0 T$ {9 Y6 J( `* f0 R 2 U' v& j: ]* H+ H0 h+ O3 B
3
( P5 m( L+ k* C5 d/ _! K L3 j' t 4' K. Y3 S2 `/ K$ R* _' Y
5- \' B7 j2 g+ S
61 ]$ \# ~4 G+ A) Z
7
- y. u4 B- K; s% m# T 8
1 N* N" D, s% }8 s 9/ J! f7 ^: K- @% V0 u' s
10
5 E) s6 C7 ~) b3 h9 ` 11
6 r1 t. A% j3 w' t 12
9 |$ l1 {0 p+ P9 n c 13
8 B O1 T: ~& t! ] 14, m" W9 O A' \- X3 k; W. @& g: [
15. _0 p3 U+ m5 A) H
167 e/ Q( H1 c9 q1 d( V
17, {* I2 D. `/ e) G2 ^
18
2 P9 v; N5 T+ E5 A* K3 O 194 ~6 x6 u3 G z: U
206 U A. c8 O) _1 j
import numpy as np
' z8 T" n# o1 o& Y' L+ h* u import pandas as pd
2 Z4 p( C" n; x* o4 h+ ~
+ t4 m, `+ o- F" `% C( \6 H; k s = pd.Series(['abcd', 'efg', 'hi'])# r9 M: f3 p& F. b
s.str[0]1 _, |; G/ C' W
1
8 C4 [9 x8 v8 W7 D6 d3 m9 F, Y' L, U! z 2& i' ]( l' V; q4 B( b* \0 {
3
) Z" C- ^1 [, {* G: ]: |3 G+ O 46 v' l" X/ I6 g }9 P1 r
51 @0 r" ]' c/ ~* w$ M2 s: v* A
0 a0 T* L. c& i" Y5 P0 j8 r$ u
1 e" F7 O v+ s: X5 O
2 h
7 P0 C; I1 s7 @- `1 u# k dtype: object# E. h. ^' E( r( S4 R
13 R) i; g9 ?0 y# @
2
/ D8 T# w. N/ j0 x2 i8 z2 E 3
" Z* d: d' M ]5 T3 P d7 A, D. G 4/ _+ {0 S6 q5 H9 Y$ S
8.1.3 string类型1 q) {8 G9 H8 v8 |% H
在上一章提到,从 pandas 的 1.0.0 版本开始,引入了 string 类型,其引入的动机在于:原来所有的字符串类型都会以 object 类型的 Series 进行存储,但 object 类型只应当存储混合类型,例如同时存储浮点、字符串、字典、列表、自定义类型等,因此字符串有必要同数值型或 category 一样,具有自己的数据存储类型,从而引入了 string 类型。1 J5 p9 u+ E7 P! f* t; R5 w
总体上说,绝大多数对于 object 和 string 类型的序列使用 str 对象方法产生的结果是一致,但是在下面提到的两点上有较大差异:
6 K# w: Y1 r6 D% a# y
' v9 X/ j( T7 m" g 二者对于某些对象的 str 序列化方法不同。) k/ j- M& ^& c" R1 d: h6 T3 M
可迭代(Iterable)对象包括但不限于字符串、字典、列表。对于一个可迭代对象, string 类型和 object 类型对它们的序列化方式不同,序列化后str对象返回结果也可能不同。例如:
5 @& x9 b Y( h8 ?4 q5 _/ D s = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a', 'b'], 0.5, 'my_string'])
& x. n8 a0 l+ @7 m; A s
# M" Z7 J) a; @8 {& y u 1
& V" I$ B. i. Z, }; | 2
. N; W- M5 D7 t$ C2 V1 V" h2 L 0 {1: 'temp_1', 2: 'temp_2'}! ]! M, _ Y: b ^& Q) [0 w0 H5 g
1 [a, b]- Z3 w% I7 u |
2 0.5
) K. L+ c i7 e 3 my_string
a/ E0 b8 t& m6 A8 U, I, q dtype: object& @# w: F% }5 N$ B* s4 E
1
# Q1 z0 k1 Z! U( V% _7 g8 p; y 2
) L+ R! y \# d2 p9 T% [ M 3
9 a( O+ \0 q5 t( i$ v6 ` \' h 4
: Y5 S' y/ k5 G3 u# X4 c 53 Y# H2 U$ s9 k9 @. |6 L0 Y: g: a7 i
s.str[1] # 对每个元素取[1]的操作
3 Y( ~3 u" y% \3 \' d/ e 1
, J4 z5 v, _' q( a. _+ P- o4 e6 x 0 temp_1
8 P: U9 z3 V& D. y4 _& t 1 b
# K+ e$ j5 E* z) e 2 NaN
" I9 {) y& M& s% w' g6 k 3 y: q5 \$ \3 t/ D, E# o
dtype: object
% q/ N) f! |6 ~# X) s8 U 1
6 [& z; M4 G8 F- l( `. g 2/ A2 O2 |0 a4 `& V' ]
3/ }' Q0 C: w8 ?( k3 G1 T- {/ t' w/ O
4* M" W% P# w4 {+ z/ j/ Y
59 X$ w% m5 e% N+ E
s.astype('string').str[1]1 Q d9 e5 Y8 C9 p
1
) l9 X. J8 X* n5 w 0 1' r* S* V! {% J- r# C. ~& ?
1 '
' Y" }$ b; q8 O) N* s3 Y 2 .
2 q4 l7 @( Z' U 3 y0 a% X b0 P6 P" ~9 e
dtype: string4 V3 [% Q! V3 w* J/ k6 B" @5 a5 n; }
1% |5 v& w4 {6 B3 K; }5 ?- e9 \& {) d: q
29 q% |! Y8 M0 Y- v' Z
30 w4 A l Z. Q: C5 [$ e
4
+ d( s+ }6 K' } 5$ v% s( F% ?) x) p1 H) _! L
除了最后一个字符串元素,前三个元素返回的值都不同,其原因在于:
9 t( A, f/ I; ]+ r4 j# P , N' \% d- K! u2 ^; _ N
当序列类型为 object 时,是对于每一个元素进行 [] 索引,因此对于字典而言,返回temp_1字符串,对于列表则返回第二个值,而第三个为不可迭代对象,返回缺失值,第四个是对字符串进行 [] 索引。
/ A. G, p' j9 D) d. k string 类型的 str 对象先把整个元素转为字面意义的字符串,例如对于列表而言,第一个元素即 “{”,而对于最后一个字符串元素而言,恰好转化前后的表示方法一致,因此结果和 object 类型一致。; M/ o* t* ~5 s }* G0 E# ~
string 类型是 Nullable 类型,但 object 不是
2 }+ H) W L8 w" G 这意味着 string 类型的序列,如果调用的 str 方法返回值为整数 Series 和布尔 Series 时,其分别对应的 dtype 是 Int 和 boolean 的 Nullable 类型,而 object 类型则会分别返回 int/float 和 bool/object ,不过这取决于缺失值的存在与否。
% t% t- z4 z! k: O v 同时,字符串的比较操作,也具有相似的特性, string 返回 Nullable 类型,但 object 不会。
; X" |3 X: A8 u$ d0 v1 n- X9 x s = pd.Series(['a'])* Q/ E6 I+ [% ^( @
" p: r. C1 I6 S1 F7 H1 b s.str.len()$ T! G' N, l* m( |* ~4 e# a
Out[17]:
3 j( x2 M5 Y* D. N) z 0 1
. ]1 r. `/ u0 T1 x9 e' F: Y# {8 y dtype: int64, ^6 M9 e+ n9 p5 N$ l, L
! j" V# v5 T. ?
s.astype('string').str.len()
" G0 F! b o0 B( V1 ? Out[18]:
% G* |8 u9 D$ p% a1 w# q( K/ _ 0 1
! b5 X5 ]8 Q T% K: g6 Z dtype: Int64+ x* J3 M3 G$ l, Y
5 t; x- T2 Y$ _" }2 t& X
s == 'a'
+ M a2 Y3 _, w0 p7 I8 j6 B, ] Out[19]:
5 |, Q! q, S' T" T( Z 0 True) ]; \3 Z- k' S8 _0 X, N
dtype: bool# q4 ^+ ^# O8 r" X4 R' W( `
# r& V0 {2 D9 Y Z& ~; r1 u s.astype('string') == 'a'
6 v# j, R T3 P( ]0 C/ Q! f. { Out[20]: & g7 |6 ^9 L' f/ f5 i. Z. J- `4 m
0 True& R) l- S3 I8 l
dtype: boolean, F, n; Q/ e0 t0 d* S
" S% v0 C1 m1 d9 P2 w: z
s = pd.Series(['a', np.nan]) # 带有缺失值" o+ H/ o2 i$ @5 E+ P+ b# |2 V
6 _- p: e' ?4 J2 f. j s.str.len()
; s7 {# Y( S) d' s3 i$ n: |5 x Out[22]:
8 p3 i4 c3 N. c( p3 c& t" n 0 1.0- X7 F* ^1 C6 q5 B" X4 R* Y$ z
1 NaN
, d1 w- W. L) X& P6 ?' U9 ] dtype: float64
9 R; Q. j2 u3 J7 ? ; q; }5 l0 B( u' x- ^/ ~ S0 o
s.astype('string').str.len()
& ]9 `" p( n+ U/ M Out[23]:
% N1 ~% T# V( i1 h k 0 10 b+ X# G. m! y3 Z( p1 l5 @; U
1 <NA>
1 t$ W; X* O2 i6 s9 `( V6 |; \ dtype: Int64
& k. H- j) Y( A, c 5 X: Q# L# ^1 A9 W3 ?* P# U' P' ]# ]* Y; E
s == 'a'4 i/ G! t+ O( ]7 [7 m
Out[24]: * g# k3 q! R: \" k' E
0 True
; ]3 U5 Y, E6 ~7 [6 I! B/ Y* i 1 False
% L; \1 }+ V, S: t; ~8 L' @ dtype: bool5 {* h1 O i5 \" z7 O" p% q7 \" F
1 ~1 n, J0 x; j! o1 {
s.astype('string') == 'a'
' b# ^( S \4 i( ~1 ]7 d8 ^4 n- l. O Out[25]: - U: c$ e9 P5 z3 F. ?9 c
0 True+ g& U) B. q/ H+ w% x
1 <NA>
. g" [& q, h+ [- {+ Q# O, | dtype: boolean
. c; G8 ]: n& g! \" H4 C
: K, r& u4 G5 B. j. P 1
2 o& U4 q2 \" V, O2 R0 L 24 u5 |& r$ t& M: K0 s/ s8 X
3
) t( x) @( L N R5 m& e. ? 4
" I* x$ P* t, K& L, x4 L 5
i z7 h! m9 m 6; A3 I8 R* }- p5 t, J4 `( o7 {
74 B" t$ c- |. X1 F, X2 z) e. m
85 x, n7 b/ L' ?
9
3 A2 T5 U) X* R+ C* q: g: J6 {( t9 Z 10. A; W3 S0 d7 e. A
11
$ l' |2 Q: m6 Q! C( l* ?3 c. C 12
! v# ]7 c/ h2 m6 ?8 \! d& ] 13* k- S* `7 c) k6 l* L1 N- s
146 s# J- W. x8 P' _; x' N6 }: A9 d* ]
15
4 L/ z+ f# ~. k& g% Z( O 16
% B* E" y8 c& C+ t) n 17
# ?" U. a0 |" [! h 18- u$ F/ J8 S" ~9 @' F
19
2 |9 \' E) H3 [5 i- s3 o o 20
5 Y( q1 ]% l! u, t3 l; r1 P$ [. Q1 T6 I 21
5 {' [4 ^6 ?# E W 22
' ~ h: C; X: y/ F! J" j 23' H$ N- N3 p; K# x5 w
240 N" {5 Z3 }6 V8 v a$ L
25
- D( A' _; T) r4 ?5 F, B8 ^ q 26
7 p% o$ O/ y( A6 g4 r9 z) D 27, l4 y, }# j. M& _+ M
28) k( x5 U4 ?: H2 v- r% x
29: _( a0 f1 d: z! L. p
30% a# i. o6 J5 a7 k# K- `2 L$ Y
31$ Z! M4 J& L: c9 u/ e
32
) y' b9 `+ ?1 O C 338 E: @3 i% w8 `1 p4 G1 S
34& V3 |) ]4 T2 n3 m) u0 H
35$ @9 h/ ~0 k! d0 x! _
36
8 }0 D4 E) V! S1 J 37" L/ D' d9 o8 Q5 E3 c, h6 ^, Y
381 m$ X3 C ]& F6 ]
39
/ M1 H# o: y/ e8 _ H; t 40
. r4 u; v, ^1 Y; _+ k8 Y0 i 41
$ [/ U9 D5 R$ l7 P$ ~% N; R 42
- ?$ ^4 s( a0 B& _* {2 B* o 43! Y5 t; R) J: ]0 r2 Z. ~
44
2 `2 Y2 G0 k9 I% r& B- N2 { 45
" P, T& e$ X2 W8 T 46
" a; D2 [; Z1 L5 M1 c 47
0 @* D: b% a8 g4 c 对于全体元素为数值类型的序列,即使其类型为 object 或者 category 也不允许直接使用 str 属性。如果需要把数字当成 string 类型处理,可以使用 astype 强制转换为 string 类型的 Series :
v% l$ d/ k" y3 g4 m/ E& z
( Y% d. ~& B' G( q s = pd.Series([12, 345, 6789])
5 Q* T/ O2 t. g1 g3 E4 ~ 9 @" ^" q/ M4 f! n( W" h. s8 e
s.astype('string').str[1]' L& x; S( R1 m( b3 g( G
Out[27]: P' A8 L4 [3 r' c
0 2
4 w) l0 W7 i$ R2 m) m 1 4" ~. c& H9 F1 [8 W+ ^2 p( V
2 7
- x2 u! J$ E9 _ dtype: string
|" ?. d" K+ [/ P 1+ G; R; |0 G$ b8 t& h, o% a
2 T& k( h: [ w( z( d) _" T
3
0 y) o7 P/ K0 _; ?( o 42 ~3 } M, m8 n- g; U; \
51 J8 f( d3 d: h. K, T8 w
6+ V& Q4 T8 {7 ~- Y
71 e: k. }, N$ g0 S1 K, U5 d4 s
8% m' B$ P# w' A! `) r$ F
8.2 正则表达式基础" I8 g* H# K! ~- C1 F* A* J
这一节的两个表格来自于 learn-regex-zh 这个关于正则表达式项目,其使用 MIT 开源许可协议。这里只是介绍正则表达式的基本用法,需要系统学习的读者可参考《Python3 正则表达式》,或者《 正则表达式必知必会 》这本书$ r! C& r F( i: Q5 ~0 ]
2 e3 {' w- ?6 P" h2 W
8.2.1 . 一般字符的匹配6 O0 h2 t7 @, G3 j) e
正则表达式是一种按照某种正则模式,从左到右匹配字符串中内容的一种工具。对于一般的字符而言,它可以找到其所在的位置,这里为了演示便利,使用了 python 中 re 模块的 findall 函数来匹配所有出现过但不重叠的模式,第一个参数是正则表达式,第二个参数是待匹配的字符串。例如,在下面的字符串中找出 apple :
: l8 {; G* Y+ U% i/ t9 {. a n$ W ' ^) i1 D7 u) J: p! L$ B# D' j
import re
/ V. D4 |- z" _% l
7 E! O- R# S( |$ W re.findall(r'Apple', 'Apple! This Is an Apple!') # 字符串从左到右依次匹配! g$ q' ~: ?4 G: A
Out[29]: ['Apple', 'Apple']
/ Q0 o: |% e" @- d0 s8 d0 S; D5 F 1
' y, o; K9 n& [) { 2
2 x6 Q; A) V4 f9 y& Y5 B 3; V: _' x: l9 a1 d8 b1 c7 M
4
* F( ]/ j6 P5 W. E9 b' w6 v! e 8.2.2 元字符基础
) }1 }% e1 F5 S$ I s 元字符 描述
W6 g1 w$ F2 Y( z* z . 匹配除换行符以外的任意字符# q0 [5 h n/ \: s
[ ] 字符类,匹配方括号中包含的任意字符3 K( j9 ]) E! ?9 U' h
[^ ] 否定字符类,匹配方括号中不包含的任意字符 M8 V; x0 ]; B
* 匹配前面的子表达式零次或多次: `$ i& ~' t8 i7 U, x0 N3 ~2 I4 T& D
+ 匹配前面的子表达式一次或多次。比如r’d+'就是匹配数字串,r’d’就是匹配单个数字
Q! _0 }/ M# ?4 w1 Y+ f ? 匹配前面的子表达式零次或一次,非贪婪方式. k9 g1 y, Y0 U
{n,m} 花括号,匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式8 i6 z1 m; C) G5 Q: M& z5 G4 h" h
(xyz) 字符组,按照确切的顺序匹配字符xyz. @6 Q3 A; Z+ y+ D( A7 Z3 k" ]
| 分支结构,匹配符号之前的字符或后面的字符; f8 a9 w @ ]+ ]+ G
\ 转义符,它可以还原元字符原来的含义1 l7 \* N/ t% n, Q+ u% v0 ?
^ 匹配行的开始$ a. x# o: a# d4 }3 T
$ 匹配行的结束' T) f4 v. ^/ z6 Y# W4 p
import re
4 H* ^, k+ D! _* D$ F/ t9 U re.findall(r'.', 'abc')* @( Z& Q* s+ |1 ~2 C( R( n4 X# q
Out[30]: ['a', 'b', 'c'] K- }3 V& ]0 G/ [' {3 q. {
2 D& c4 v) X4 U$ i3 L
re.findall(r'[ac]', 'abc') # []中有的子串都匹配4 c% \9 ?' ?) |- L/ i
Out[31]: ['a', 'c']
5 u' h% l% J' s
3 B$ J+ [6 p- Z. ~! _* n* I3 c- | re.findall(r'[^ac]', 'abc') 1 l+ F; W# F& }8 O% E
Out[32]: ['b']
* }9 I; W6 V$ r2 r
$ m/ N1 F E5 Y, t' [' Z4 s re.findall(r'[ab]{2}', 'aaaabbbb') # {n}指匹配n次+ z6 J0 g7 D1 P/ f
Out[33]: ['aa', 'aa', 'bb', 'bb']6 j. N/ z8 M2 _9 n) _
% X. V, r2 M* ~4 f% M; ] re.findall(r'aaa|bbc|ca', 'aacabbcbbc') # 匹配前面的或者后面的字符串2 L3 A, L$ \" z/ O( E+ C- O
Out[34]: ['ca', 'bbc', 'bbc']
7 O. E& k' H7 \, k z 6 t u8 \) s/ O$ ?) ]5 e( k
# 上面的元字符都有特殊含义,要匹配其本来的意思就得用\进行转义。+ I% v: ^ @; Z3 H4 F
"""& n; Z* l. [$ ^4 [
1. ?匹配的是前一个字符,即被转义的\,所以|前面的内容就是匹配a\或者a,但是结果里面没有a\,相当于只能匹配a。
3 q6 r8 o: O/ ?- v$ s 2. |右边是a\*,转义之后匹配a*,对于竖线而言左边优先级高于右边
; S/ J/ r4 g6 y2 F' \ 3. 然后看目标字符串aa?a*a,第一个a匹配左边,第二个a匹配左边,第三个a虽然后面有*,
9 ?* M- \7 F2 ?5 x' E* T8 v& k) a0 T 但是左边优先级高, 还是匹配左边,剩下一个a还是左边,所以结果是四个a
3 E( r. h K H """
/ p0 v# _; _) r5 d: E
: r2 {7 L+ U# ] re.findall(r'a\\?|a\*', 'aa?a*a') # 第二次先匹配到a,就不会匹配a?。a*同理。
' H9 f0 N, S/ z" |2 W& T Out[35]: ['a', 'a', 'a', 'a'], L1 Z! W+ Z8 z: w* q& @; W
8 r x, Q( W! m) a" d8 k5 E! @ # 这里匹配不到是因为目标串'aa\a*a'中,\a是python的转义字符(\a\b\t\n等),所以匹配不到。
6 n3 c7 I P# V( P- S+ d' y # 如果是'aa\s*a'之内非python的转义字符,或者'aa\\s*a',或者r'aa\\s*a'就可以匹配到\字符。2 ]+ t/ g# \8 ]* G8 j
re.findall(r'\\', 'aa\a*a')
0 `9 V8 Q& n4 \. W6 \3 T& p4 c []
9 n+ C$ f9 M% C8 C
& Q4 ~: F, e$ e0 X2 Y1 k- L re.findall(r'a?.', 'abaacadaae')
+ y" f) _, |$ L( v- C9 o Out[36]: ['ab', 'aa', 'c', 'ad', 'aa', 'e']
6 ^0 @; Z$ Q1 f, o- q0 T- A 8 \5 N9 b! q; M1 }
re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10') # 多个匹配模式,返回元组列表8 L$ l: _7 }) O, y# p0 t2 c0 M
[('width', '20'), ('height', '10')]
- T6 ~0 q- R1 P9 M9 y/ w1 v
8 r( @! }5 s4 L# f 14 ~# W7 P" c( Y. e/ p
2
0 Z0 v& Y( C1 X0 L 3. n1 |2 n" q7 M" [
4& f+ S) D: c( t) w$ ^( x8 U5 c
5% Z. v ?; v% j4 ?9 f
6' N5 N# C5 ~+ N! B+ ?! [6 F
7/ j9 m( S! M' \ g# C
8
* s% i* _! a; ]7 l7 O 93 d$ q+ y: ?+ E9 c6 `6 _1 p
108 h) [/ u2 b! r0 d0 c- L# E
11* y6 z& m- r/ }3 Y! v3 k
12
# y5 ?! o) g$ f8 n( e 13
7 w# y, K( E9 ]( S& s) w% ] 14
' z, L) F" r4 q, p6 [ 15. E& v' j# _$ Z' D2 D; p0 m1 ~( t
16
6 P( ]/ P8 y( m7 n6 k 179 n4 H/ H8 T0 u3 `" S* I
18
3 [$ {9 W- q4 L" t2 n; T 19
1 A+ {9 b+ E5 K0 }, s/ F 20
6 C6 c/ m" Y% Z8 y: @* n; A9 K' v- ?9 l) k 21# \6 A7 @2 X# i; ~1 a6 I' v; K
22
- O- d0 `& U5 T2 B/ {, {" T, X 23 n2 e4 s; O+ m- M6 r. O- M
24
' k8 X$ J0 P3 H. ? q4 }$ d. ^' @& M 25
% c# P% I9 D2 m' f) t8 j 260 z$ U0 ?7 q/ j! V6 q# F* D. `1 B/ X: y
27
, V+ N) `3 A; |0 R' c7 s+ r 28& H8 v5 }) v8 t: @
29
4 F+ E7 e: J1 j5 C/ c. r 30
% L; d3 T* p2 Y$ ~- ~+ L 31
9 {% b2 ]3 z6 ^$ k. v# _ 325 W* }5 j$ |3 m/ L" C
33
5 c, D. R J: [2 s 34
$ n5 e( T9 v/ v2 r3 \7 f 35. K# A7 L1 f5 Z7 h9 J
36+ e9 X+ h1 C+ l) S, Y
37
; w. w2 y- A+ f& d. H! @3 A 8.2.3 简写字符集
/ ]5 }0 {5 ?6 T1 p( w 则表达式中还有一类简写字符集,其等价于一组字符的集合:
% s4 V: Q% i; s) z0 L k ; J' }! a% j5 W t u6 N6 E L
简写 描述
9 ]* K9 L9 V1 z \w 匹配所有字母、数字、下划线: [a-zA-Z0-9_]
8 e @5 k: B6 h7 X7 ] \W 匹配非字母和数字的字符: [^\w]0 H3 C- p) z% |6 J& m) g5 G' F }
\d 匹配数字: [0-9]
* D# t' M h3 ~+ N \D 匹配非数字: [^\d]
% D: z3 ?: o9 H4 a \s 匹配空格符: [\t\n\f\r\p{Z}]) ^. \0 Z! Y2 V! X) s7 y' e5 l1 }
\S 匹配非空格符: [^\s]1 [1 z1 A8 {7 a& G; N
\B 匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。
0 ?$ `( ]7 k3 l5 b" l re.findall(r'.s', 'Apple! This Is an Apple!')
6 X" y! s+ M/ q) L8 x Out[37]: ['is', 'Is']/ v3 i9 ?' J# y
1 ^' M5 H, R3 B: L+ x
re.findall(r'\w{2}', '09 8? 7w c_ 9q p@') # 匹配任意数字字母下划线的组合,但必须是两次
, ]5 l6 c$ S% A) c" V7 Z$ Z6 p Out[38]: ['09', '7w', 'c_', '9q']
* r1 R Y9 `. s [4 p- N1 D! m) E+ T' } , {7 Q8 N/ u- G: [. [, y
re.findall(r'\w\W\B', '09 8? 7w c_ 9q p@') # 匹配的是两个字符串,前一个是任意数字字母下划线(\W),后一个不是(\W)& x( x- G0 X& { C7 e @
Out[39]: ['8?', 'p@']+ Y" J d' i3 \
: A: F- d& C" o# }" M
re.findall(r'.\s.', 'Constant dropping wears the stone.'). A- Z d- Y' |6 \3 `. q
Out[40]: ['t d', 'g w', 's t', 'e s']0 t$ K0 |9 S( ^" R+ w' i4 Q
U8 k( W2 D9 Q; t6 y
re.findall(r'上海市(.{2,3}区)(.{2,3}路)(\d+号)',: `- z4 V7 r; z! Y+ v% a2 G
'上海市黄浦区方浜中路249号 上海市宝山区密山路5号'), Q$ _) h& Z8 \- D2 ^
: C' p" I, ]3 ?0 _
Out[41]: [('黄浦区', '方浜中路', '249号'), ('宝山区', '密山路', '5号')]3 x" w% b+ V* P% q' m7 h
* U! D. G# v( U$ d, y( t 1
/ r7 d" p/ a$ b1 W 2* U* X5 x4 `3 n) n$ _* `
3& C' f0 I, w1 K
44 X: [3 ]( f! x5 f
52 E7 N3 r5 X/ n3 F3 t3 m
6
: O! H0 h' { u1 H: H% { 73 o0 |7 H0 `* X3 ]' j& D
8
" b; c: \' Q6 ?$ _& Y- S# ~( ~ 99 a/ ], A% O! ~1 Q6 T) X
10( a! K5 `# Q/ \6 h4 @, q8 k; `
11- n- j# I6 g5 E, S0 u
12
" ]# z: q, {% _1 p* j 13# A q! \9 ~" q# a/ G" M
14 z: L% Q, L3 w0 C
15
3 r# B& o \3 d$ n8 k# G6 D 16
0 \! r1 R1 J3 f# n" n3 U% X 8.3 文本处理的五类操作
6 K6 o/ c( g: e3 m( ] 8.3.1 str.split 拆分
/ \8 i' ?3 C$ b! d- g2 u str.split 能够把字符串的列进行拆分,其中第一个参数为正则表达式,可选参数包括从左到右的最大拆分次数 n ,是否展开为多个列 expand 。
: ^$ S4 f% A, s2 j3 z 5 T; |3 V' ~' g3 m- c
s = pd.Series(['上海市黄浦区方浜中路249号',$ X( }8 n/ s" n! R9 w/ i
'上海市宝山区密山路5号'])/ V$ o$ e" [( I
& Q! W% G4 [3 H* I, ]7 c: f
' Z0 R% u; P" D, I1 a, s( E s.str.split('[市区路]') # 每条结果为一行,相当于Series
- ~- |* A- w( t- ] A/ Z Out[43]:
, R5 W" x: ?& G 0 [上海, 黄浦, 方浜中, 249号]
; J0 S/ ~# [! i3 j 1 [上海, 宝山, 密山, 5号]& s' C# _. ^' d6 n% R
dtype: object: @5 ]6 Q- ?4 h+ A! a* G0 c
7 Z& f# B( e4 J* l3 W s.str.split('[市区路]', n=2, expand=True) # 结果分成多个列展示,结果相当于DataFrame
7 k* Y- Q: n+ E( h m/ n: r( U2 M Out[44]: * L2 a7 h+ v& p, H: b
0 1 24 T- n3 B, }/ Z9 Y
0 上海 黄浦 方浜中路249号
' E2 P& a- S( v1 O4 r 1 上海 宝山 密山路5号/ Z4 w- Y9 r. O8 x- i9 u& H
1
+ ]% H7 P$ o! l% X) P5 y3 C 2
1 e4 E2 q% z% V' H) i( f3 ~ 3
; D/ |+ Q! P x. [" E L M" N3 B 4
# l6 g4 I, o/ k- B) d7 ?/ e 5' v1 D! ?9 k. \. B0 u; p& F6 @6 t5 o
6# K7 E) i: I1 J" B- \' _ a
75 \" }9 S1 E/ Y0 m. G: U
8
9 Y' S1 s W' ? 9! {2 g9 S3 ^1 e" Y# e
10
+ T) h' E8 B9 E7 e4 V7 B 11: a+ K4 t% z- P0 V8 |) L, v- O
12
; S* Z( Z* J- M: M k 134 O3 e! ^0 `9 Z: c8 P6 I/ p; q
148 r4 I+ z2 N [4 A# M8 G
15
" G# k! O$ X% Z; X 类似的函数是 str.rsplit ,其区别在于使用 n 参数的时候是从右到左限制最大拆分次数。但是当前版本下 rsplit 因为 bug 而无法使用正则表达式进行分割:
' W k8 [2 c7 _9 k) s/ L/ } ' ?# d( l1 I. A1 h2 f9 G6 L* _
s.str.rsplit('[市区路]', n=2, expand=True)8 v3 [4 W( O2 q8 M: A1 m
Out[45]:
) X; a7 {9 Z- i3 b9 z 0; l6 L% F6 V; B# U* Y; d
0 上海市黄浦区方浜中路249号
) E; V. r9 }$ ?% j 1 上海市宝山区密山路5号1 k- M0 i# A4 D, U: m, }1 ~ t4 t
13 T3 a' s. l: P
26 E3 r. p6 B6 n5 o& D9 c
3
6 y1 M9 U* S( L* {# U" x0 ? 4
' ?* H( W H& d( M 5
1 O+ P9 Z. T% n' K9 Z8 p 8.3.2 str.join 或 str.cat 合并' f3 U/ L p+ u
str.join 表示用某个连接符把 Series 中的字符串列表连接起来,如果列表中出现了非字符串元素则返回缺失值。
3 Z) ?8 u! v3 c# ] str.cat 用于合并两个序列,主要参数为:7 G. a9 N& A# }5 C1 K
sep:连接符、
' l) m+ U! W$ g R4 q2 m join:连接形式默认为以索引为键的左连接
. ~: `7 i. L9 L, Y7 ` na_rep:缺失值替代符号
+ ]9 z% f/ B/ |& A. J s = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']])9 u& _, v4 J( o
s.str.join('-')2 V8 [+ n o8 K9 A6 U+ ?% r
Out[47]: ; E5 |$ P. O* |7 z0 S
0 a-b; z( k0 j0 V) n; \3 v* y( A- l
1 NaN7 D/ f F$ f% N3 u( I# `
2 NaN7 W- q, j, U1 N7 d: L& W; |
dtype: object7 ?: r2 P- J4 `" \/ l% r
1/ V- q$ }0 n3 D/ Q
2
1 G, \& }6 H. g# X }7 m/ k4 Z 3
1 i @$ Y% E# L4 ]& m+ u- ]' F3 F' d 4
7 S( N- w+ y1 V9 ~) C$ H& X 53 Y! g8 L y: ^$ Z& v [" o
6
1 n6 A- e; h/ Z' `) o 7
! E- I. u. u7 K) D9 E- u s1 = pd.Series(['a','b']); O- n1 _! l8 {( x9 d9 b1 Z
s2 = pd.Series(['cat','dog'])2 x2 k3 g4 D6 p, \' ^8 ?
s1.str.cat(s2,sep='-')* X6 W: M: w$ K' `
Out[50]: 2 ?* }9 e. W3 H
0 a-cat
0 w- Q. d: r% P 1 b-dog
g& e6 O! Q7 L. n' _* }. `" d dtype: object, ]+ G* h* d& a3 S( c$ L" {; i- F
4 Z- M# k# V4 k9 Y# w e6 b$ r s2.index = [1, 2]- l. d. K& j- V/ ^/ g
s1.str.cat(s2, sep='-', na_rep='?', join='outer')) ?( j H$ O5 f7 }2 e: w! ]
Out[52]: ) u0 W! {3 p7 q! W+ D; [
0 a-?4 @! `7 T, I: b# D' W6 E- ?
1 b-cat
2 M" X0 P" u: F4 n9 ~ 2 ?-dog
/ }5 h X- @9 z. T1 d, z dtype: object
5 d- ]6 k, D: X d# ^! F' a9 H& e 17 C M5 F; U; a6 Q* F. i1 m( K, h* u
2
1 Z- m) k# H* e0 |4 m 3! r S4 u) V, i3 e/ v) A( K5 o$ n
4
, w& o$ T, \* X 52 e* Y7 H* v, G* e& g. _+ N
6
/ k: p! _$ I$ ^) m7 i+ ` 7
- w# \& F- T; u5 ~ 86 {5 R: U$ l! {6 g! K+ ~( U- z
9! r9 M* Q U& F
106 ~+ X4 f4 P5 z
112 c0 Z' p. ~7 H/ L0 ?6 {! q e
12* t; U. n! B3 o1 l$ T
136 D& W; [; E- y$ L! s' Q K
14) H! o, W; B2 e
15$ x& g* n# f2 @3 }* D: ^4 \
8.3.3 匹配
3 ]: I: h' a( a+ n9 I str.contains返回了每个字符串是否包含正则模式的布尔序列:
' m3 Y. E% t5 c1 b e9 y s = pd.Series(['my cat', 'he is fat', 'railway station'])
+ R8 U6 P# Z* T+ { s.str.contains('\s\wat')
3 O: z5 N7 A# Z$ a( p
% u3 G( `# G9 a/ \ v 0 True/ M, X* k- }+ m9 B- s, Z- i ~
1 True) p& O- Y5 r- x% w0 I% S+ v
2 False1 P! n4 O9 l: ?# K
dtype: bool
# b& ?! h. q6 H* T 1
) `3 a- b- z' x 27 h+ j" n R! J E) ^" W: O2 H
3. L( l5 S1 Y9 h) B: z$ s
4$ \9 D9 U& {$ _7 y" C8 `# P+ |
5
& d& {0 L4 x$ ~ 6" Z# i. X) ?+ F$ }9 z: o
7( J2 {) W2 F2 z6 O& ~
str.startswith和str.endswith返回了每个字符串以给定模式为开始和结束的布尔序列,它们都不支持正则表达式:) l2 P& v$ g% y0 M8 I
s.str.startswith('my')0 ]" n1 c4 j* m- R- x% d/ t% w
, @( K( G3 E" \6 }+ m' ?, Y 0 True! Z" m- J) U) h( L7 l" I
1 False. }5 [6 H* @' m' N$ n! m
2 False
- a! O9 [) m) p7 c5 W4 P dtype: bool
7 Q ?; C; v; O- h. F* ], @3 f 1
' V$ x9 T" `& j0 } 2! q& L( u) I; b ` c6 F3 |
3
5 q; ]' t! X9 ^9 I2 w 4
2 v; u" z) \* y' o* a7 M/ Z4 E0 ` 5" N4 b4 X9 @$ j. k0 C
61 y1 K: ?& w/ m3 G
s.str.endswith('t')& [# b6 i& N, e `, P3 Z+ H7 h; i
8 l- r1 M/ g& b; \$ s! W6 m 0 True* t! _- u( v" O5 u ~; ~
1 True. r2 F2 p9 S0 c$ W5 Y' B$ N
2 False$ f( k- C7 A+ r; [" q& }% `8 C
dtype: bool! j0 B# C' W$ P) Q! p8 Y9 N. C
1
: u" x1 j9 K) A, `! G! p o 2+ y: T: D4 d! z
3
- Q4 j! [+ v( p0 ~. a) ~- ` 4/ t# z# j: Y2 k. [) K* W
5% y- E9 U# C9 Y
6- O' D2 V* L2 v J8 H
str.match可以用正则表达式来检测开始或结束字符串的模式,其返回了每个字符串起始处是否符合给定正则模式的布尔序列。当然,这些也能通过在str.contains的正则中使用^和$来实现。(貌似没有python里的search方法)
. m% M( e* ]7 b& N: M0 f6 W s.str.match('m|h'), v2 f7 D" |0 `6 p5 ~/ f5 a
s.str.contains('^[m|h]') # 二者等价7 d# z7 V( A- k" `! |$ N
1 B2 }: U- m( R, L 0 True0 i( U/ v9 f* b' u# |) H
1 True
0 q! C2 W( Z& a9 x' P 2 False
3 _8 [; _2 `) C' |4 g! n dtype: bool' a" C& P9 Z% f( N @
1
, z/ _2 ^* y& |) Z' r4 W- \$ @ 2& F& _ E' Q" j! m# S6 m
3# I0 C6 E `+ S. }/ T) }0 O0 O2 X' b2 h
4
2 ^& E1 z. [' Z* j 59 x0 i8 D4 o3 b1 j5 }, u' i9 N; C
6
$ K: X5 f+ w$ x% o; O. [ 7# t+ \$ Z4 o& \7 T i
s.str[::-1].str.match('ta[f|g]|n') # 反转后匹配% y, \7 C- A6 C/ t9 @9 ?( j
s.str.contains('[f|g]at|n$') # 二者等价4 G+ m }/ v* M: z7 y7 e2 e! T Q
" N' ~1 E8 d) k8 ` 0 False% J4 u4 ?; X) h2 T1 K$ ?' [& g) P* Q& _
1 True
' ~# b0 n5 ~. p! N9 c& U 2 True
6 Y9 Z* n& D0 e% l+ { dtype: bool$ D" {/ d! k2 R. Q, e
1, r7 o$ C( H7 Z
2
* }4 i) z) {3 T/ B6 F1 r3 y 31 O! d9 Y4 ^/ B8 u2 C
4
6 X9 a. y' B. B9 C- ?$ Q 5. h+ s3 ~( X! H' E5 k
6
* j) z3 q( _8 V 7( n# @9 b4 B; O9 c
str.find与str.rfind返回索引的匹配函数,其分别返回从左到右和从右到左第一次匹配的位置的索引,未找到则返回-1。需要注意的是这两个函数不支持正则匹配,只能用于字符子串的匹配:+ K @! g o6 F y; F9 S. Z
s = pd.Series(['This is an apple. That is not an apple.'])( I: d( v& {8 \# o
' @* r# c- U/ B4 S" w) S s.str.find('apple')
3 P. K' b3 @9 q0 v8 d Out[62]:
3 v$ v6 ?" G9 E8 n$ K5 I, j 0 11
B2 e7 k# j1 b! t. j dtype: int64
6 o' Y! ?* Q/ C- ^. s' y
7 n$ E* {- K B. `, P' h s.str.rfind('apple'): N z6 R; x/ @! j! a1 k9 \
Out[63]: 7 ^$ U2 q" @! l; U
0 33
k. y8 j; L# A) C* H& S7 a dtype: int64
; L/ U Y2 D" f5 J' I! O 10 x. s/ h- D6 e+ t' |1 |1 z
2 @2 u1 A. q3 D o+ {+ g
3" N+ P* W9 \. B& [1 k' o
4
8 K1 s: K$ f4 z 54 M) J8 a8 Z: ^/ | j
6% D; N z; X2 F- R" I% z
70 C8 ^0 J" I* U6 b' w
8 y& L$ L! [4 @) g6 E/ i6 _
9
/ }: }$ ]3 F, T3 y. q. E" j+ s/ J 10
) N! ]3 G* j8 W+ i4 ] 11
0 ]! T( G% O% J4 A* D 替换& z' l& p1 R# V) ^/ [! u h: \
str.replace和replace并不是一个函数,在使用字符串替换时应当使用前者。
/ r% ?8 @4 @3 n. e; i4 [" F s = pd.Series(['a_1_b','c_?'])
4 T& l7 s/ H( U8 ^' t7 S" q # regex默认为True,表示是正则模式,否则第一个参数内容表示是单纯的字符串,也就是匹配字符串\d|\?
" G' q: {9 c' c s.str.replace('\d|\?', 'new', regex=True) " b7 a& d+ j* K) {$ h! P8 G) `+ H& E
9 B0 U1 Q, e; {2 D8 A
0 a_new_b
1 }# A: k2 r4 S' C 1 c_new& F3 x. J$ j9 a0 v- F A$ Q
dtype: object
4 g& ~! r) Q g# |4 S C7 u 10 y. b" x1 H2 H4 N7 @
2
+ R. ~ z, d# u+ U/ ^6 b3 Q# N0 @+ F; [/ N 3, w7 i/ g$ F, J4 g# C" s' M1 c
4& ^4 E" g7 p6 D
59 B7 a6 k! f ?9 p9 W/ K5 \ M
6
( t; A9 a" C( I3 P 74 Z7 g1 n1 N" O
当需要对不同部分进行有差别的替换时,可以利用子组的方法,并且此时可以通过传入自定义的替换函数来分别进行处理,注意group(k)代表匹配到的第k个子组(圆括号之间的内容):
' j* ^4 z( i' u. Q5 z1 ? 2 B* I1 E* O3 R% N }( b; r- C' |
s = pd.Series(['上海市黄浦区方浜中路249号',
0 k7 \. x3 m% l9 r. S, v '上海市宝山区密山路5号',
: w+ H, K! Q! ]. ^/ m '北京市昌平区北农路2号'])1 y" r% F- X. l b" T$ V8 \, Y# l' ?
pat = '(\w+市)(\w+区)(\w+路)(\d+号)'5 S3 ?& t& x- @% |
city = {'上海市': 'Shanghai', '北京市': 'Beijing'}
* L( Q' ]+ S s3 @5 N. A2 i) ` district = {'昌平区': 'CP District',
+ M Z/ I2 u, i( {# k '黄浦区': 'HP District',
! m/ W$ M' U7 m1 c; s5 W( o$ i% B '宝山区': 'BS District'}. S/ V% s) |( v. {
road = {'方浜中路': 'Mid Fangbin Road',
! Y4 I" C5 S& O* A8 E '密山路': 'Mishan Road',
0 M7 k. T! M/ x; t '北农路': 'Beinong Road'}
' I+ H4 D% M% ^6 `8 w; l def my_func(m):
i. k0 b9 H4 { Z( m7 Y$ G str_city = city[m.group(1)]
. w% F9 _& W6 S/ w" a. T+ W$ T str_district = district[m.group(2)]
3 Q: P$ j% g0 f: R str_road = road[m.group(3)]% H, i) O4 s1 J2 k2 ^+ t4 _1 z& l5 w
str_no = 'No. ' + m.group(4)[:-1]" w! b: v7 U/ }/ X J, M
return ' '.join([str_city,
! ~& k9 a7 a1 ~4 G str_district," a0 q( F# P6 y; c
str_road,1 X: Z$ d# D; \* @; O
str_no])
( e7 Y# I& e7 @ s.str.replace(pat, my_func, regex=True): X( S. U' c! d/ D) h
! q+ N7 r8 i2 N2 J
1
b, Z% w( s( s* C 2
4 A1 g2 X0 l; I 3
0 v) _0 o; I, z: @8 F, u' e$ [5 e! N4 `" y 4
g# a; Q; j0 W 51 h0 N2 |0 K/ O6 P
6# w* `: p9 w$ D/ E! ^
7
/ |. @/ j! E# P1 e$ @: a4 [+ V 8$ |1 V) w: T- B
9* F1 { |0 e5 {9 n- h; Y
10
9 O) A# `1 B8 R) v+ r3 S9 ~- I- U 11
, Q( v0 t4 \4 [ 12) x3 D. f) D, w5 Q# L" t; M. Q) T
13
( a2 \8 k; k7 E& V# G, | 14' |! E5 j! B4 S' L
158 e7 b2 u) H, k3 m' z( f+ P
16
8 o; @3 N' @' c: u# u 178 B, l* `/ N/ p
18
1 g# u2 T6 S% M. A8 O 19
. l+ L% b) s0 q* g 207 J% b6 ?1 T$ g
218 j0 y0 C$ m. |8 V& m8 |
0 Shanghai HP District Mid Fangbin Road No. 249
& R+ ^2 v3 H- q4 r" x 1 Shanghai BS District Mishan Road No. 5
) I5 u" i) N$ Q h, |/ p2 u 2 Beijing CP District Beinong Road No. 2
$ r7 N0 x/ Q' L, p7 v* @ dtype: object
+ @$ n- D4 M, u; u1 P' X 1+ A% G/ z! d# O1 v9 M8 A6 X* [; m
22 ] |! Z$ c/ R: z5 X3 {- h
3# I: Q5 m- E8 y$ ~
41 g# Y J9 [# z7 D
这里的数字标识并不直观,可以使用命名子组更加清晰地写出子组代表的含义:2 l! C6 {) n6 _+ z& G0 s$ |8 d
: H$ q- e- x9 L. J
# 将各个子组进行命名- i# _5 A, Y5 }' m
pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'- C5 _$ z: ?! ?) _& b! L
def my_func(m):: w# c# T. j* @; v# R
str_city = city[m.group('市名')]
) z# H; V# f" p* y str_district = district[m.group('区名')]+ a9 [2 x, t4 ^0 G" N: Q
str_road = road[m.group('路名')]- F$ [8 J! \* a. w0 r
str_no = 'No. ' + m.group('编号')[:-1]
$ b) Q8 ~/ W! n2 a3 ?3 y return ' '.join([str_city,% ` V2 ?4 X6 m& X
str_district,. ?$ P' C8 c( s% {& f3 F6 w
str_road,
% D5 e% P0 I- @5 G+ Z# a str_no])8 o1 V) x* X' H i1 f
s.str.replace(pat, my_func, regex=True)
/ [8 N8 e1 {( t$ C 1% g+ F7 V) @; Y2 D
2
! I5 v/ G3 r. L+ U: x! y) k- e: h) f 38 c- M$ }' U9 |- [* M A4 J& W
4
- \- e- {5 r2 u) }. Z 5
( I. Y8 Q1 K, g( ~( O% X 6& ~' A1 i# v+ h' W4 _% \
7
0 w X) x& T" ]. o 8
$ Z* U d0 k) e+ y/ ~9 R* S 9" n4 T/ X0 V, p. ~0 v
10
9 B: j8 n# H( f- X l) N$ T 11- ^9 O* _: ]$ P' D
125 Q( D- c4 [/ W8 |4 q
0 Shanghai HP District Mid Fangbin Road No. 249
* g) y* K) O! l; ]% T 1 Shanghai BS District Mishan Road No. 5/ T. [0 ^: T7 K
2 Beijing CP District Beinong Road No. 2 `, ?6 |, f9 j, P6 U/ B6 q
dtype: object
7 k2 ^$ }4 w& o5 d2 ? 1
4 V$ A1 N" y b3 w 24 I: f3 U5 _6 V, z: N: n
3 E3 L- k: G5 m1 P# \
4& D7 d3 w% \; d3 I: Y
这里虽然看起来有些繁杂,但是实际数据处理中对应的替换,一般都会通过代码来获取数据从而构造字典映射,在具体写法上会简洁的多。5 d3 D5 S- ~/ @8 L
7 @0 q& V$ I: Y S
8.3.5 提取
3 f" p( l8 ^, N% x9 ]* Z* [ str.extract进行提取:提取既可以认为是一种返回具体元素值(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的str.split例子中会把分隔符去除,这并不是用户想要的效果,这时候就可以用str.extract进行提取:
, v- x; x, L- ^! V) Z* D s.str.split('[市区路]')9 I! `: `; h1 O J. P; K
Out[43]: ' ]6 N0 A8 P- i
0 [上海, 黄浦, 方浜中, 249号]" r. Z( I9 M0 {
1 [上海, 宝山, 密山, 5号]6 ~. S/ U3 ?* J8 P
dtype: object
2 L5 s/ q! K# C% ]5 |, A, E & b: w" `: P+ ` ~
pat = '(\w+市)(\w+区)(\w+路)(\d+号)'! Y& p7 Z# r( o6 Y9 D
s.str.extract(pat)3 m) |, J; e1 O
Out[78]:
( T! w/ a. z( w2 D4 @& e# e$ H; @. W 0 1 2 30 t. g& ?+ u' ~3 ]$ H) O7 Q4 S7 s2 s
0 上海市 黄浦区 方浜中路 249号8 @1 Y- _& R, o4 r& O6 y. m
1 上海市 宝山区 密山路 5号+ N7 ~& P% W0 F5 Q
2 北京市 昌平区 北农路 2号
# b; L$ v- {" } 1
6 H: F1 ^; g9 f' p8 \! c9 L/ i 2, e2 q* r/ B8 H1 N
3
" p$ {% B) A" V; y: o( h4 ]8 W8 j 4
6 W+ w7 H2 N' s" l+ _: _9 s7 ?0 J 52 N( w3 s* y9 p; S& P* E: W
6
( A3 ^' V. V, j8 q 7" q8 h- R* t* K1 O' X& A
84 I9 g$ z- k+ H2 W; U
9
& W& d8 \. A$ k, v; l- U 10 I; F& y1 ^1 k6 x" e* y
11) g- u/ F: b, ^8 A. A
12
; O5 ~) V8 z) Y# l 13
' G, u, C5 b/ S' d 通过子组的命名,可以直接对新生成DataFrame的列命名:
J& w2 O* I; j' E/ y% v
6 q. E% V$ S% e) T! P- F pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'
4 v& J& G' j. h! x. n. v5 Q# H s.str.extract(pat)
; q) `; B3 s% b) f Out[79]: 6 o# A- O! r. Q' x( `1 I6 ]
市名 区名 路名 编号0 d: N8 @3 o2 l O- |
0 上海市 黄浦区 方浜中路 249号
- T0 M4 W4 B( K6 i7 U8 _3 [ 1 上海市 宝山区 密山路 5号
( u# P X4 I7 ^; B- ? 2 北京市 昌平区 北农路 2号
# G0 J% P3 ^! ^: C; t( O 10 Q, e: ?8 P$ r- V! |, X
2; {- d6 J2 `& P6 w* b7 [5 G" C
3
- B- v7 s. u+ f3 J# i 4& @# [/ M3 e: n0 _+ L
5) P, a7 E$ u. H* C) ~8 i0 w1 R
6
* I9 ^* R: e# g. O2 |7 W# [4 x 7
( a3 ?( U' n _: j str.extractall:不同于str.extract只匹配一次,它会把所有符合条件的模式全部匹配出来,如果存在多个结果,则以多级索引的方式存储:- T- ?" N- `1 S1 j z. z+ X
s = pd.Series(['A135T15,A26S5','B674S2,B25T6'], index = ['my_A','my_B'])
1 |; o$ I2 `% V+ t! Y4 f# k pat = '[A|B](\d+)[T|S](\d+)'
# @9 {" ?- r9 a3 I" J s.str.extractall(pat)2 f/ v0 X' ~7 D6 |' G2 K6 w5 ~
Out[83]:( O# S$ o3 m9 k. W, ?. n
0 1
4 T8 x/ P3 h+ `' _& W0 z U4 f; L match # Y3 f# b; s: Q( d
my_A 0 135 15
) n. h& Z; v& e4 l6 s4 ` 1 26 5
' v- s6 O( ? @8 m! Q3 m* j my_B 0 674 26 L+ R( {# N7 y% x( D
1 25 66 R# c8 ~- w' t, r4 v% o
1
" E4 f* Q" ^1 o6 f# W2 p( Q 2
6 }1 m' r! T, |8 h9 g% M; X" z 3
/ l& U! [0 P* S3 E8 @' A- s% m! j2 U 4$ K9 Z* Z5 D& D- M6 E
5# {6 J8 l d! b2 N6 ~. U
6
# u+ o4 {* {& z* G# ]. X- q 7' E. J d2 }4 w T R9 k
8* \7 [4 a- i( Z" n
9
2 W4 B1 O3 F* B- a: d! P# J 10
& ?" |/ F8 ]$ w' E8 y: U5 ^8 j pat_with_name = '[A|B](?P<name1>\d+)[T|S](?P<name2>\d+)'
, E* E M2 D, R! L! a s.str.extractall(pat_with_name)) |- b& { a+ v
Out[84]:
! G) N- c4 K- L* p! D! h3 x name1 name2
9 z! q4 G# \3 J$ B) e) e match / F% b5 H+ g$ ^8 x8 b
my_A 0 135 15% `% M8 X) S' e
1 26 50 x2 L5 m& f$ {; H; g- `/ P
my_B 0 674 2
7 |) D; b3 R2 I, ], ] 1 25 6
" f w) c2 _7 e" ?0 I1 y 1
1 z5 l s% P$ U. f# t 2; G i3 @$ B/ P/ B" w
3
: {! D, o- {) @ 4
! p$ @1 f2 d3 [) k( ~ 5
0 f5 p0 q/ }/ k: H' f 6' S: r: h/ K9 C+ b8 U6 N
78 ?4 L& h% V1 K& U' c) D. k9 i. P
80 K/ ?- H) X. k
9 g$ y0 e) Q( u5 @( Z5 S9 E
str.findall:功能类似于str.extractall,区别在于前者把结果存入列表中,而后者处理为多级索引,每个行只对应一组匹配,而不是把所有匹配组合构成列表。
S# \3 l( |! [ w) Q/ n" Z/ d0 n! ~ s.str.findall(pat)
: s [$ I0 p/ p3 A0 c 1
3 u/ o) I8 B( D my_A [(135, 15), (26, 5)]& P+ n- V9 G) o# i6 s0 c
my_B [(674, 2), (25, 6)]
2 V( C% p0 d" `9 N% t; h6 J: p dtype: object7 }* ^4 m' ~% D( ?
1( U+ t1 u0 ?- q
2
- t6 c! g2 r" I2 V6 e; _ 3
- B) I! x. V; L" @8 q @ 8.4、常用字符串函数
5 q- B3 c: [, b2 U 除了上述介绍的五类字符串操作有关的函数之外,str对象上还定义了一些实用的其他方法,在此进行介绍。4 i+ p9 u, U; A, f! l
# }" j/ g, \% z4 r) h" v0 V
8.4.1 字母型函数1 l/ F4 A' I. N/ O, t
upper, lower, title, capitalize, swapcase这五个函数主要用于字母的大小写转化,从下面的例子中就容易领会其功能:3 Z8 H$ E! C6 q& I
& l) \% p$ T5 ?9 e* Y- @
s = pd.Series(['lower', 'CAPITALS', 'this is a sentence', 'SwApCaSe'])
% X# M0 ^5 l( P- t9 g: G! M
" L1 i( D/ B+ \3 ^. D+ c s.str.upper()
9 x( S' k. a: x+ {+ E; _' H# n( ~' O Out[87]:
. j5 L* ^5 ~& H 0 LOWER* n. ~6 \3 n4 N& {% H
1 CAPITALS" v: Q+ I- t# ^) J; x: n" P# a
2 THIS IS A SENTENCE% [0 l+ r1 e3 q. v' M
3 SWAPCASE
. [/ x2 ]4 J& O l/ |1 m dtype: object( V1 y0 g( Y, u! Z, C" r6 R N
$ G' _- C7 {& z8 r* V7 j s.str.lower(): ` ]! R. U; y k! X
Out[88]:
8 u2 p& L( x/ z' o, \0 b- h f% w% M0 T; u 0 lower4 g2 G+ P9 U6 s/ _
1 capitals5 p5 B* }( j8 ^9 c
2 this is a sentence O$ D2 g+ h# H
3 swapcase
7 B9 R* W$ w- B) ~' \ dtype: object1 M/ F+ ]) j" [8 O; `
: [+ G# {# }" P9 a
s.str.title() # 首字母大写: \) y7 k) ~% W! `4 N
Out[89]: ) |2 R1 J" w+ v* a* c- U! W9 U+ G
0 Lower8 a8 o# H; i; F( O1 _
1 Capitals$ O1 m w0 E/ l0 c7 y- \
2 This Is A Sentence
' U& ^4 y1 r0 c8 M c# `3 v4 c& R 3 Swapcase5 G% A2 o9 n/ ~' h& `' r) ^
dtype: object' G& `& V( n; K) q
5 O0 f* n$ E0 \- |% Y: h( v3 b s.str.capitalize() # 句首大写
4 s& t* Q% B3 X: F Out[90]: 6 j0 n5 f" U( g
0 Lower) o9 V: d: \' d4 C o- ~7 e1 u
1 Capitals
5 J5 a/ h5 ?* N- K9 h 2 This is a sentence
6 P# n) V6 K& c8 h% n0 } 3 Swapcase/ ]; _$ s4 E2 z
dtype: object
6 p9 g, ^- Y; A) Z- _0 `0 N
6 D5 a6 ?: z7 V( ?& Q s.str.swapcase() # 将大写转换为小写,将小写转换为大写。* @/ O7 r2 S4 ~. X! x# K, e/ D A0 G
Out[91]: 1 L, [8 ? T& `! |
0 LOWER/ L9 `. {) K; K- O- N/ I: w7 N
1 capitals: @0 o+ W* K0 M3 b+ ~
2 THIS IS A SENTENCE" |* c& B, E; u) y- I- T" g3 P5 B3 b
3 sWaPcAsE
; b4 ], E3 h7 X% U3 g, x$ } dtype: object5 w) K1 K H$ d7 n3 Q5 T
, o4 m7 Y7 i3 t1 o8 ~" v- j s.str.casefold() # 去除字符串中所有大小写区别
& q) x Z! B8 k3 o 0 l: F. U$ T0 `" q% _
0 lower/ a' P: \* m/ s/ s7 K4 ? n
1 capitals
+ i; Z0 L- u, y 2 this is a sentence) r" y: }. g+ Z& E+ a
3 swapcase! j- H" i7 m Z$ C# S' T2 W
4 _( p) y$ h ^3 Q
1
1 P4 e% ?- n) \6 F; U' K 25 L7 I2 u" U8 I8 p
3
7 j8 J a% L$ V& [/ m. ?8 X 40 K5 s& _% L4 ^! {' l
5) [4 _: h( C4 t; S
6
* {5 i E* }' j# G r 78 v) e# P P; P$ w% C' E
8; g8 J* [3 u& a8 @
9
# Q$ |+ T/ h" M0 m) Y2 T; I 10/ ]: L$ q6 W" [: K9 t9 }$ B# ^
11
9 v4 \+ c- L- ]0 }) G, o- s) b5 x 126 F( L$ R, s2 O$ X- }- P. M
13
1 V$ H8 s% @5 s( v2 g( M* v 140 I) u7 a5 ?2 `& [0 ~) q
155 V% V- f0 h1 q* v9 r
165 H r* a5 w* }! B% k6 [% H5 b9 g+ R
175 ]& G4 Z, Y: n b! e6 k
18$ w* v6 |5 ?) V3 c5 V: R
19% O$ j- f& l9 d2 j4 `. Z
20- X8 o0 `8 G3 ?0 s/ I
21
; w$ o& l G1 t4 m 22: a6 q* K9 g( N0 f9 E' D
23
4 o H" X+ ?) f/ w ^, @: ]' b# O0 i 24/ P! x) w1 w& o5 Q& h: K: J% s
256 d8 {& {; X9 n
26& S% f% F T' r# j# y0 O; a( h
277 m% S7 _0 z7 C/ T' X H. }4 j r
28
' U* N4 _! F4 ?' I 29
+ B" Q. Y; D8 Y1 t) K 30" @( r7 A% ]2 t$ j
31
1 D( Z, @: F6 e 32
0 b+ h$ ]4 T1 ]( O 33
+ ~$ w+ C8 F; v1 E 34# A4 p6 B. ?6 K
35
; u5 x3 F1 W7 T- E2 D2 h6 t 36
9 X; o$ I6 e, M 37
5 J* O* G6 N* i) C3 [3 \ 382 W$ L3 @' y Y% l2 K
39
9 b' g6 r% v1 L0 n/ S3 L' u2 A 40
/ ^ _7 ^. h: t% G 412 c+ G5 J2 ^9 Y( [- L) W: G8 K
42
( D8 Q! X0 W" ~9 P1 l) z 43- b# U. ^/ i& ^
44* ?% U- M3 _& H: Y2 T+ t
454 _) Q/ c% S- u1 {, k
46$ V/ B% b: ~/ k3 f# C3 y/ S! v: a4 z, K
47
/ S, \0 G! {* |( X: z% b 48
9 s1 a, W' P$ K5 h$ s, f 8.4.2 数值型函数5 X) i7 ]/ s( V- U0 ?$ m
这里着重需要介绍的是pd.to_numeric方法,它虽然不是str对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。其主要参数包括:3 ^) j* v. h+ |
F2 `& q1 D4 u7 j; S8 Z
errors:非数值的处理模式。对于不能转换为数值的有三种errors选项:+ Y7 M! g3 \+ v0 D: l! E" T
raise:直接报错,默认选项7 n0 Z! d: y" e) M7 h- c
coerce:设为缺失值1 b- G8 l3 i' }4 Z9 O
ignore:保持原来的字符串。
$ ^% b1 ~3 f- C4 K* _. ~2 x downcast:转换类型,转成 ‘integer’, ‘signed’, ‘unsigned’, 或 ‘float’的最小dtype。比如可以转成float32就不会转成float64。
8 I" G7 ]' F3 V. K$ a D* h1 y1 i" _ s = pd.Series(['1', '2.2', '2e', '??', '-2.1', '0'])
8 {* A. s4 @3 j
$ v- |. [* s% @% z2 }4 P pd.to_numeric(s, errors='ignore'), P! ?0 z# E5 |7 e
Out[93]: * N: O" A0 N0 K- U
0 1
2 ]# f% p# F% Y9 {* K+ n 1 2.20 E6 P5 }( A6 X9 m( @ K, t; K; V
2 2e+ L7 G# u9 a7 P3 d) d
3 ??5 Z$ z2 E( t% Z3 C" T
4 -2.1) a/ J5 p6 {8 m4 @
5 0
- m# M- u; \* X' T/ j dtype: object
/ [8 w. U* j7 I$ P! L+ m0 N5 b
$ J3 v& B Z2 o5 S7 V" c8 r3 m7 e9 J pd.to_numeric(s, errors='coerce')
0 M1 k$ p n& t2 m' S5 m Out[94]: ) b+ M' Y" n4 B) _1 ?0 F7 p/ V" ^
0 1.00 A; l: S# t" T/ a7 S" B2 o! [
1 2.2 i5 i! u9 n: B$ J, o$ j
2 NaN
/ _0 t! e. I" G6 O3 Q 3 NaN
7 I) G- }; c5 U; ^- ?5 T 4 -2.1
5 b5 i5 e+ Y$ M z- h: N/ H& k, x8 R 5 0.0
+ n) D3 p( b6 e3 d/ C0 g dtype: float64
3 p; N# s* t$ A: ?+ b' E, u # P4 H/ c% m. d# F, f h1 w! ]
1
* ^# `/ X6 _/ M$ i) R/ A8 Y; [ 2
* y' W. S4 B" U0 y" ~ 3
+ g' u! _) |3 n/ Y 4
b. ]* f5 Q. y 5
; c5 D& m% ^9 E: w 62 A. C( h# I2 p* r8 Z( l
7
E* x( i7 r( o 8
6 [7 E8 k/ r) u, l 9
+ u$ D4 W8 M: c4 q8 {5 X' l 10
1 u$ U; |0 n) A* Y. @ 11( Z; }9 A5 W$ l s0 T
12
* \4 Y& I4 L9 i! m v& c) ^ 138 J4 D% X# B- F+ v/ c) G- d# c
14
! c. J3 l7 z. _3 b5 N. f! `7 R+ C6 O 15
& J, s5 l6 K5 L$ ] 16
3 T. u |+ `: p0 `( j+ A! }9 M; g [ 17
6 m8 G F5 a: t8 P" L 18: E4 r+ |5 _) R$ h' M
19
9 y) e1 w& ?! q; r+ }) @& E4 F 202 i8 k% W; h9 t5 ~% M+ F: w
21
0 i) g) g* v* H6 b C: q 在数据清洗时,可以利用coerce的设定,快速查看非数值型的行:
d- T! C! W8 w% D/ c 2 K# T! Z3 c0 u* d9 Q7 a6 r/ @
s[pd.to_numeric(s, errors='coerce').isna()]- _5 y% m* \/ M& q6 p; a5 x
Out[95]: # T3 N( M8 l0 g4 ]
2 2e c, ^. s* w" @0 }- Y2 W
3 ??) f$ Z o8 u' D0 ?
dtype: object3 A% {8 @+ C2 N8 S+ }
1
6 c9 R; O4 _9 C) Z U( j' `1 p7 |* ? 2* A& ~/ n" Y' Q1 T, p
36 Y; N* f( ?3 I
4
" J3 V" i, X8 D/ Q9 @ 5* Q7 F' ]; V; Y- b. B: c. N
8.4.3 统计型函数
$ c& S y6 f; o1 Z4 l count和len的作用分别是返回出现正则模式的次数和字符串的长度:
~& a: b( ?0 S4 N
( {7 ], G7 h; ^3 \- `4 S' n% W! S s = pd.Series(['cat rat fat at', 'get feed sheet heat'])1 I, `; @2 D6 f4 C
( _! E4 g! l/ O( U! L
s.str.count('[r|f]at|ee') # |左右两种子串都匹配了两次- t% c# y6 W! `0 g' ]' \$ x
Out[97]: 1 R5 h) {0 g5 K! e
0 26 x d: N' x2 G% }( P
1 2" o8 Y% }( e0 p) w& N, }1 q
dtype: int64
& s+ L% H& q8 d 5 g; L/ B2 W) v: f
s.str.len()
, V. { H" S3 M) Y: d! K Out[98]:
l E0 D# c( V; K( Z8 { 0 14) A; S1 y/ l5 A% [
1 191 c) Q* l, _4 {9 P3 [$ D
dtype: int64
8 z0 u4 _ G1 T# \" ] 1" F+ w( |7 Q3 `
2$ \& ~% E) V5 K; [
3
# P; u* D# s. A; H 4
0 _& Z) A/ I" \+ p0 q* x1 b; g 5# x1 I5 y9 G' U6 i6 {6 X2 g
6+ m8 R1 ]) [ S+ Z2 E4 `
7
. w! s) R& G0 ]- U1 y! n8 o9 J 87 y. m I' A$ q7 O/ W2 z
91 Y) o) y2 U9 E
10* m# z4 p8 a- \
11* G! Y, G' a$ b0 I: Z
12
0 v" Z6 R; j2 j. X1 Q% M/ i' @ 132 U' }$ F0 @2 e
8.4.4 格式型函数
3 |! W9 A3 B' H+ h5 ^$ t/ K6 H5 |) f5 L 格式型函数主要分为两类,第一种是除空型,第二种是填充型。其中,第一类函数一共有三种,它们分别是strip, rstrip, lstrip,分别代表去除两侧空格、右侧空格和左侧空格。这些函数在数据清洗时是有用的,特别是列名含有非法空格的时候。# H$ f2 Z( D" M( X
- s3 |" y, N) J7 [' K: F: }+ X my_index = pd.Index([' col1', 'col2 ', ' col3 '])8 C- u, z& m9 |, `, R8 C9 T2 {8 ~
& u& \) C v* t* R9 g# n
my_index.str.strip().str.len()
; i3 E$ m7 P6 x& b Out[100]: Int64Index([4, 4, 4], dtype='int64')
5 I& i# x: F: y: S6 ?+ g / N9 l* x2 X0 S( j6 K
my_index.str.rstrip().str.len()
1 w$ a+ {" G! I Out[101]: Int64Index([5, 4, 5], dtype='int64')
1 ?3 b7 u" [! F4 R; \
( @0 m5 M7 j+ M X my_index.str.lstrip().str.len()3 x" w, Q7 T/ g* |9 W
Out[102]: Int64Index([4, 5, 5], dtype='int64')+ E, V0 ~$ ?% C$ _3 c0 Y7 R
1; O4 A- Y' z: M, ?, y! D, V( O+ J
2
8 x n& N& h9 N' g. P- m6 b 3
6 F8 D7 r2 x9 {- d) F 4
) `# |/ H( l& h8 A" ]% l4 ~ 51 D4 M4 k: z S d. L
6
! H1 N) ~4 p1 K. w2 i 7
: u9 g: X' j! E9 G6 B# c 8; g9 z3 g& w! w
9" l* O8 n; P3 ? }+ r4 S. W* x0 e
101 f) S. O; H2 o
对于填充型函数而言,pad是最灵活的,它可以选定字符串长度、填充的方向和填充内容:
" X8 o( e$ r, Q4 j; l4 ]1 X - U: v+ m1 e5 J3 x) l: [+ o
s = pd.Series(['a','b','c'])
& z4 N: ?0 l; r, x( q + x: W& o8 ^* `, x5 d- M7 U8 x0 P+ O
s.str.pad(5,'left','*')
) j7 r, R& V6 G& e9 i Out[104]: 7 K; u! h k. s& x) Z
0 ****a
. C, i7 ^$ q' B: e# S 1 ****b" o" H8 `. V- D" a* I
2 ****c P) X! k" R9 h! u. E) x# \( u: B
dtype: object
0 ?1 N; \ J; R
: m! O2 [ B6 G5 ^7 m" R s.str.pad(5,'right','*')7 D+ K$ `2 {8 b7 }" Q% j
Out[105]: ' w( k0 s+ l6 d/ L4 g0 _
0 a****
1 h) e# r$ N3 I5 a# P2 T. n3 H 1 b****
5 @$ T, v. ?8 e- j* M 2 c****
. p( t) N- M, k) T/ b dtype: object
: n1 @. \* J1 {% ?* c
4 K* h2 n+ C' X3 C; K! d s.str.pad(5,'both','*')
, C1 u% _" c4 `; u Out[106]:
5 } A \6 k8 d+ q& s1 ]' }6 W 0 **a**
5 X- \# P: U0 z 1 **b**; x1 o* u0 D( y; e' a, ]+ S* `
2 **c**
4 n: C: q. ]! [ dtype: object
% i/ \- T+ g! F; y. v6 b* Q0 w
: h. j4 M9 r% t 1
. q8 ?3 t) {, ~# K% S& ? 29 M9 c1 a" p# }! Q
3
8 t' U9 z5 t* l4 _/ ~1 _ 4: Y- j. M) a$ b7 k" N( C3 b, J
5
: [2 u" s) l$ q9 x9 R 63 ?' {& J3 k" y
7: t9 |/ g: D% N1 r- l
8
6 J8 \: | @3 o @- J 9) i% x: V- I) Y- N9 T1 b2 A* C: G
107 x4 l8 x( Z W! a* R
11
' K. H3 P' U$ a7 O* z7 F+ {& N: @ 12* P1 s0 S8 Q& N5 V/ j3 ^
13
* f E7 w% _+ x* t" V 14
+ R$ Z9 e3 ^5 s% ~ 15
0 @8 ^7 y; n9 G* {& q 165 t3 c$ s& ^# f9 ?* A% i' V6 H
17. B% [( a6 k d& d( P0 U" B' g' ]
18" Q. t& G# W9 ?5 G# a/ u
19
7 t6 P; b; m6 q" P. H7 Z 20
?7 o2 O4 C6 r8 E7 v 21
U; G% V0 T3 _4 b7 ^- f 22' d- E/ A3 H' p
上述的三种情况可以分别用rjust, ljust, center来等效完成,需要注意ljust是指右侧填充而不是左侧填充:( |0 P' h/ P$ k( D$ J
* V) m0 t" h# t R
s.str.rjust(5, '*')
" m0 E' u, G( ?2 Q* Y7 x, r Out[107]: 1 L5 V7 g- W7 e& e0 A1 k
0 ****a
: t# K# c0 `7 k 1 ****b
5 J7 b8 S( U1 S3 l- T 2 ****c
6 x7 I6 B2 [% `5 Q+ ^4 p dtype: object+ b) W. t% K* K) d# X
+ S5 X1 P! b1 Q5 u( |
s.str.ljust(5, '*')0 c, e9 O2 k X" G) ^
Out[108]:
. E* ?1 m8 X8 {6 f/ P0 y# y 0 a****; f( s5 L' v' M7 S. X. k8 O7 w
1 b****( C. U5 k. ~$ d3 V
2 c****
0 g! a E& B) D4 D5 c dtype: object9 E) K6 r+ M# \- K2 }! _ o
8 d0 A8 Y; ]/ X1 ~ s.str.center(5, '*')
. O* g5 q6 d* P3 V+ g; W8 f( j Out[109]:
- n P) W8 d* e( P! Y% A: q. T" I 0 **a**6 e9 G# ~" o6 J3 f1 F8 k
1 **b**! C! K/ d1 i" }/ A3 `
2 **c**
! H; `4 \- {. V ~ dtype: object; Y1 j; G4 P3 I- J
$ Y! B2 J7 ~1 n D/ ]
1& a9 Z& _, y- H
23 E4 K$ f/ |) d1 T5 p- P5 }- L
3
, ^4 W1 M8 c, } 4, _. P3 ?; \' u! x" l# `3 `) O1 A
5
% q& H+ {$ L' T) Q 6( A( {+ h" n% x; j
7: e4 d- G! `0 O! v1 O" d2 ?: L) s5 E
8
5 k. g7 s( B/ E" U+ l" S8 T 9
( U) s2 m' j' H5 y e 10& K. [* y$ w! j, a9 i) I, i
113 ~" c M6 H/ h6 h2 g
127 A" n2 V5 A# M
13; _4 F: Y6 ~1 K
14
, j( S/ g( O+ O0 Q' P0 ~0 V 15
" H* y+ d) @, F" I; c2 W; t! { 16( F% O# t7 B% x
174 t* e/ X& o: ]" O5 f
18
. s" P- R9 T7 c: s 19
) d! S9 K7 F' o- a2 G 205 c7 X$ i) a$ O) J( p
在读取excel文件时,经常会出现数字前补0的需求,例如证券代码读入的时候会把"000007"作为数值7来处理,pandas中除了可以使用上面的左侧填充函数进行操作之外,还可用zfill来实现。
7 E: O$ L3 F1 |/ Y8 } ) X/ m- S3 ?) @" r( b
s = pd.Series([7, 155, 303000]).astype('string')
1 {9 p: r- i$ F' T+ Y ( B7 s* N( R* E( x4 ~( _" B* M
s.str.pad(6,'left','0')3 g- ~. s8 f2 Y" V
Out[111]:
" m3 Y S% l9 _+ h7 O6 v 0 000007( v' s5 f: b; s+ L4 I! A
1 0001550 X P" K3 c) W- f1 C7 b W
2 303000
- k# t8 o) B' D# U dtype: string7 Z8 }, P( C, ]4 c$ }
2 G# G; H0 D$ Z* H% o* b
s.str.rjust(6,'0')4 l3 U1 y2 W! v
Out[112]:
8 p# N' {8 ], Z5 T; d8 h 0 000007
$ t/ K3 r. i2 K/ o7 N 1 0001558 L/ e% i: g$ X$ \7 f: F( @: y- t$ E
2 303000
8 N7 n2 p M _! D$ ]; J dtype: string
' M" s' F& G# f& a( z. R # E% G' A5 f8 {- Q) E7 t* I6 B
s.str.zfill(6)( K5 k% a( p' s) g
Out[113]:
, N5 ^7 `" U" i8 |2 r4 p 0 000007 s* s% L0 [8 h& |: x* S
1 000155
3 M( T: l9 H+ [7 d9 z 2 303000
. D: ~! Q" F. b dtype: string4 X5 D, P* Q9 U4 p6 s' b
6 [# c6 T6 F$ `6 @ 1- J! H7 g+ p1 a/ X) I
21 E) s: R/ |& S* u h! H
39 V' Y) |, V" v
46 g" C, C, ]7 F
5/ A- F- b' D7 `# A6 O3 w
67 M2 E3 y! n* ~* t, T6 B6 k g) h
7& g$ Y, d- m1 r$ ~! d
8
' c# ]5 m( k5 z+ N7 A 97 c3 o# a3 o+ g6 R: q3 K1 m4 z
10" l. Q( v ]6 E9 f: S' M
11% g: V: {' {- V$ D0 W" o8 @
124 l" g j! E4 T& E% c' ]( G4 E
13- `9 a7 B$ x6 s
143 O3 |! p& n+ J |
15
& C# I" c( c# m; c: J 16
$ ?% K- ]' E! p! L" A0 j 17
9 K0 d- ]. f( R% M% I/ V 18: o& M! s' |. ~! m
19
/ N: s- F; T+ P$ [4 z) T; R9 M- a 20
# Y8 N* h4 m2 Y1 n9 ]( P 21" I6 M, a+ F$ Z3 m" ^, u6 `' r
22: }% @9 t ~1 r, P! {# p! B
8.5 练习 Z$ \8 m- i2 s( h% K, J- C O
Ex1:房屋信息数据集
. m6 W V! k: @" z7 M, ?! i 现有一份房屋信息数据集如下:
! z: m" E/ i$ u/ _& K" I0 M
1 ]' d( Q& n- `1 J% B/ c df = pd.read_excel('../data/house_info.xls', usecols=['floor','year','area','price'])
9 }; Y, e; d/ K df.head(3)
+ [( _/ }( v8 H) w0 q- B Out[115]: , k6 f: M' c. e/ Y4 T+ L0 k5 U
floor year area price) |- l, F9 t2 i8 ~. C* \, U
0 高层(共6层) 1986年建 58.23㎡ 155万
6 U6 \! Y9 N) n* H/ \ 1 中层(共20层) 2020年建 88㎡ 155万
1 y- x T! R. X0 z 2 低层(共28层) 2010年建 89.33㎡ 365万3 H& D4 F7 z- D% L1 x- ~9 j- t8 A
1
; @6 f+ h3 L# D% |( D' q- Z 2, s+ d6 _5 G0 H; L9 m* G. X
3' f S1 ^$ U- l5 ~, i: O1 G5 p" j
48 N* ]* A5 }+ w" l+ ?- O
5
" {3 T' o3 }# L9 Q 60 }1 H$ F+ V; o) ^: ?
7
% Y' p/ ?- T x: t7 P3 j 将year列改为整数年份存储。
7 e" K$ v1 P8 Y. j6 `+ I 将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。" H" M! z6 T7 a+ `
计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数
7 @; B# \/ u' K# Y9 H 将year列改为整数年份存储。
$ A# q- G! c: u: D5 r" a a0 W3 D1 ] """
6 t" p W* e. \* O! b% x- a 整个序列需要先转成Nullable类型的String类型,取出年份,再将年份转为Int64类型。
9 a; O; U- v# u& W. x 注意,转换的类型是Int64不是int,否则报错。即使astype加参数errors='ignore'跳过缺失值,
6 W; {5 g! U3 n9 j- m 转成int后,序列还有缺失值所以,还是变成了object。: ~% q& Y6 D7 U3 X6 ]: F3 x
而整个序列转为Int,就还是Int类型,缺失值变成了 pd.NA 。
0 D1 K& b( `5 e T! U """8 s B2 I" D# _/ t' V" D
df = df.convert_dtypes()/ h* C4 `3 Z. ^) P' {6 L' V
df['year']=df['year'].str.replace('\D','',regex=True).astype('Int64')! t* ^/ ]* B3 I f: ]
df.loc[df.year.notna()]['year'].head()
5 ?) z) V; k% ?$ J7 ?
" _1 m7 K5 A- N, b8 v! v 0 1986& S) @/ `7 P0 G: f) V
1 2020
; [8 {* `$ L6 Z: j/ t 2 2010) L: S/ s5 ?" K/ P; T# ^6 b
3 2014
; I0 M& v: B+ ?8 T 4 2015+ c) f1 [" _3 Z( |
Name: year, Length: 12850, dtype: Int64
5 B) z0 G7 a2 y' o7 p) m; E+ D" M B ! I1 o3 S1 k- }3 @/ f2 c
1
2 E+ L+ n) Q+ l+ k7 J 2
# \# W2 I2 I4 ~* y% F X" ^ 3
5 N$ p9 B$ D$ |3 ]2 h% Y 4 Y; P: w& A& I: n c, \
5; Z- w' A+ _; l) _7 A y( N
6" ^$ j$ w* W8 Y6 Z; j9 m
7( l# q% |$ o( U( m
8) {( V+ t/ M7 Q( z# n/ e6 K+ I
97 g3 B$ x2 N& L, D- j
100 j3 _8 P' Q! ]( s5 n, L' K
116 p# ?/ _+ L0 n# v4 g j7 M4 ?
12
$ P2 W4 w* ]& ~( G8 ^ A5 k \ 13' g+ M w4 R- V& N7 p0 F
14
" k; o- A# Z! F6 R5 ? 155 h6 q8 H+ t! b
164 w8 e; r y* h: J7 T' s
参考答案:
0 @. H2 Z6 S% s+ k' r, p l5 Z 9 _6 W* |# _7 N! O
不知道为啥pd.to_numeric(df.year.str[:-2],downcast="integer")类型为float32,不应该是整型么 F* v) v/ b" p% Z
) `$ d) S# v' s- W# G" A. n df.year = pd.to_numeric(df.year.str[:-2]).astype('Int64')
) } Q$ `1 x2 u4 c7 H" s df.loc[df.year.notna()]['year']
; ]( b2 B0 L; U& y, v# e7 J E 1
9 N* P5 I y7 u- x5 i 2
( E! x4 t7 g. W# G+ X& B0 h7 n9 {1 f7 u2 X 将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。/ i5 t9 E! C# u+ U
pat = '(?P<Level>\w+层)(?P<Highest>\(\w+层)'
: h$ Q6 h- W7 f& o( y7 Q* \ df2=df['floor'].str.extract(pat) # 拆分成两列,第二列还是(共6层得形式,所以还的替换一次% S1 C6 d1 D( [1 e: s
df=pd.concat([df,df2],axis=1).convert_dtypes() # 新增列拼接在后面,再次转为Nullable类型( v" j) e8 ^0 J& K
df['Highest']=df['Highest'].str.replace('\D+','',regex=True).astype('Int64') 7 Z# h" \0 u5 L0 }$ D6 a8 X& O. S
df=df[['Level','Highest','year','area','price']]
$ s0 r( ?- i% N4 U' k df.head()$ j+ i/ ~( M* _& \& x
. l( B. a! }$ O. h Level Highest year area price, ?. j" ~+ l; G% }) S1 K
0 高层 6 1986 58.23㎡ 155万/ b/ ~5 {6 p( J R/ |
1 中层 20 2020 88㎡ 155万
% a Z7 n" u1 s% r- i. V 2 低层 28 2010 89.33㎡ 365万
1 L! s0 C: `% T 3 低层 20 2014 82㎡ 308万/ j" x8 l0 t- g! \; O% H9 i
4 高层 1 2015 98㎡ 117万
" l$ A1 d; x: X* o' E5 l4 p8 b 1$ u; o2 T- N4 O9 h0 X( S3 m$ U
21 B* z' I- P1 x- d6 N1 b8 r
38 N+ m0 r; j& [5 a1 W; a1 q
4
; A6 M; t5 }% \4 h 5
5 T3 ^2 O: y( G( C1 ~9 ~" n' A' _+ Q 69 n, n# w2 `3 v7 Z" w& f
7( ~+ J1 e9 d# \! \
8* u2 I- }8 N9 r, q" O1 C, X
9
9 O% Z$ H& D/ `3 _: ]2 Q 10, i0 p2 M( x y0 Z' j9 N8 H5 h+ I
11: m* S* r5 f0 y
12( Y: j/ I2 I7 X+ W; Q
13
' J5 T. p; \. r' k, | F# v! j$ s3 s # 参考答案。感觉是第二个字段加了中文的()可以准备匹配出数字,但是不好直接命令子组了
) m5 |7 d0 O& n pat = '(\w层)(共(\d+)层)'' [3 D. y1 L6 c1 h# a$ i1 ?2 i$ q
new_cols = df.floor.str.extract(pat).rename(
8 J% } {8 {5 o- _3 t d columns={0:'Level', 1:'Highest'})
) a; V4 `$ }( j7 [( r/ n
/ S8 E. G- d. A1 Y0 C df = pd.concat([df.drop(columns=['floor']), new_cols], 1)) N. N8 Y0 ]) E; [
df.head(3)
2 V- a, `" x' X9 t7 E6 V3 m3 g. L 5 l+ C. M1 { O, k" D) {
Out[163]: ]+ K* x" E1 ~ z% D# Z1 P+ W
year area price Level Highest
& t/ Y" k" t. {& o4 F" V& Q 0 1986 58.23㎡ 155万 高层 6
0 M* R" p+ L- {* R+ H+ R$ s 1 2020 88㎡ 155万 中层 20 J. ^! _7 \; `# l" K6 S. r
2 2010 89.33㎡ 365万 低层 28
( E4 M& t' Y! U0 \! z) X: T 1
# a* n' t3 c! s: H9 Y( v# M( U 20 i7 n7 C3 j% @5 |
3( s! p, Z& W+ [# f" f
4
1 t$ g* D4 W. m. L# V! }2 t, | 5
! D2 }) _; A+ Z+ O0 T$ r! ^ 66 ~( a: R7 P- W( _* h, E; X' R* k
7
J7 w. D. x. B* Z( ` 8
5 g+ I, `+ C# l5 L! Z7 E 99 {) y; ^# @" u* X* T- {. v
10- {% f3 P' H; b5 C6 e3 Z
11, r( U+ C+ `4 o5 [! O: e7 z
12
1 U, A/ H! G* q( W8 ~ V) N 135 `9 `2 ?8 e7 O3 `
计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数。
( |+ Y2 o- g# o """
% @& I+ U6 ?+ u T+ y) l str.findall返回的结果都是列表,只能用apply取值去掉列表形式3 L, e9 B3 Y4 q' X
参考答案用pd.to_numeric(df.area.str[:-1])更简洁% c2 G$ O9 u* n/ n# I% O
由于area和price都没有缺失值,所以可以直接转类型) ^, b( U; l! w
"""4 r7 e4 ]# H5 E7 I
df['new_area']=df['area'].str.findall(r'\d+.\d+|\d+').apply(lambda x:float(x[0]))
! N- q% u$ `- ^8 S- L$ q) I6 @ df['new_price']=df['price'].str.replace('\D+','',regex=True).astype('int64') i0 g8 E5 C4 v" M( g" A2 W
df.eval('avg_price=10000*new_price/new_area',inplace=True)
& L* L' u; _' ?4 L4 P' i6 J3 [2 R. w # 最后均价这一列小数转整型直接用.astype('int')就行,我还准备.apply(lambda x:int(round(x,0)))$ {0 x7 j* S" Y3 Z
# 最后数字+元/平米写法更简单* |: x, G& w# ~' T* U+ u, g
df['avg_price']=df['avg_price'].astype('int').astype('string')+'元/平米'6 c* k% k6 }2 l, n/ t& }* V
del df['new_area'],df['new_price']8 l! c- n" a) z# c
df.head(). G! `* l( j6 ^& w- s _2 w
" c, u) g* h' ~6 w/ K Level Highest year area price avg_price
1 G% B* Z$ K! A1 L 0 高层 6 1986 58.23㎡ 155万 26618元/平米# v; T" Q! N- H4 x( y- x+ k
1 中层 20 2020 88㎡ 155万 17613元/平米
) p$ T$ t. G* o; K 2 低层 28 2010 89.33㎡ 365万 40859元/平米 T# J1 n& [/ b/ D6 H( `
3 低层 20 2014 82㎡ 308万 37560元/平米5 j$ P4 V9 R! l& l/ L9 Z5 L6 h
4 高层 1 2015 98㎡ 117万 11938元/平米- J" N, E, C; p$ ]0 c5 @
' G; d8 C$ o7 x; r( _& H 1
! H6 H1 d: d9 V( N# t" T0 R 2
+ E- g+ x. G7 t6 x8 g u 32 f8 l4 v3 R- m# }* y% o3 Q# z
4
" B6 y; d# N5 |- ^ 5
+ r; z) j# X6 f n/ _8 L1 r: u 6" L7 U, ^5 }& y) K
73 ^$ Q/ n+ t! E) L
8
4 d' o. T8 Z) b/ R# V0 Q/ Z1 b 9
8 P( g- d7 |$ A6 l4 N' f8 l; @5 b 103 G1 T/ p9 A9 E" r2 n# f
11
+ ^ h' \- P& z7 M' E, | 121 ?8 q) @1 a" B" P
13" p. H* G* D) Y( x* Y' e6 W
14: {& I+ l# {6 }! ~; k+ _
152 [4 X) t9 l0 Y% L8 L/ M
16
. E D! C* _/ e5 Y" _ 17
, f6 q3 E7 C# q- P0 A8 a: ]. K* i 18" f% i: R) d: r, j
19
+ \/ F4 T# q# a4 }/ X! b( R 20) ^7 s& |8 ~$ x2 l1 k4 Q8 C( g
# 参考答案
$ h" K8 M# x) O6 n s_area = pd.to_numeric(df.area.str[:-1])4 h- x) E; @7 I) t# b5 d/ |& j
s_price = pd.to_numeric(df.price.str[:-1])
, Z# Y. s3 V0 h( L; r O df['avg_price'] = ((s_price/s_area)*10000).astype(9 H, I0 N) x- J* {
'int').astype('string') + '元/平米'$ K! g7 t' G1 [& ^5 [9 t& }6 o
6 h5 ^% W/ R, i5 X e+ y df.head(3)& \# O0 W6 J6 s6 }" x, C9 s; o
Out[167]:
1 g. a# }* l0 T year area price Level Highest avg_price+ r. W7 x0 L$ O V
0 1986 58.23㎡ 155万 高层 6 26618元/平米( y4 r- R0 a7 F
1 2020 88㎡ 155万 中层 20 17613元/平米* Z! O0 W B. Q; ^8 ~1 C& ?
2 2010 89.33㎡ 365万 低层 28 40859元/平米
: g6 Q* {5 d$ {5 } 1
4 U: l5 X7 k! q) w: k _ 25 m. a5 j! v; t; p
3
% o7 @9 c6 b& |$ o8 u+ _4 V9 L 4$ \% n* W: _& J2 M) y
5( H" g2 L- [. \6 H# Z1 ?
6
* |2 u7 s+ F# X$ e1 D" n- W 7/ {, K% W; ]* t
8
9 G! g" D+ ~" `9 X# c 9' z x! ]% C0 B
10+ B# E$ h3 j( z- t4 O' ^& U
11( l1 H* J( A: W8 @) r Q( h8 j
12- A; e/ r6 ~( z5 V( c! g
Ex2:《权力的游戏》剧本数据集8 W6 `+ v4 s* h( ~0 Q; f2 K3 V* [9 ~
现有一份权力的游戏剧本数据集如下:
/ {7 [8 X3 j' n# o+ y- i * ]9 s3 e: i% A7 ]
df = pd.read_csv('../data/script.csv')
2 E9 O( s0 I0 O: s) e% b( b df.head(3)
% l# A! j' w, s( X) U
" q2 f" G+ i1 t Out[115]:
' M; Q" V/ O+ Q3 i1 j' | Out[117]:
9 F. T K2 j# w* q5 l! k8 E/ | Release Date Season Episode Episode Title Name Sentence
% m/ o+ v+ F2 j+ O( M 0 2011-04-17 Season 1 Episode 1 Winter is Coming waymar royce What do you expect? They're savages. One lot s...1 b: O- \% ]) ]8 g; X. l0 K
1 2011-04-17 Season 1 Episode 1 Winter is Coming will I've never seen wildlings do a thing like this...; d R' ]1 U( x; u! n
2 2011-04-17 Season 1 Episode 1 Winter is Coming waymar royce
3 S6 O- S+ v9 N2 I 1+ G# b' o3 S9 [) f
26 }, n5 I1 I8 y' Y) n
3 o1 K4 |: c+ l1 {. E. p
4' f% K$ }7 g/ l( c( _
5
0 D- b2 i9 l5 f/ t) n: }3 N 6! L7 u/ d% k/ \; g0 A3 q
7: O! V% C' `4 o5 q s5 U
8, `% B; v" Y, k o. A- v4 S& R
9' n% m3 M* P# X0 j9 _
计算每一个Episode的台词条数。, F0 Z; D1 J0 J4 f/ |& t
以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
6 F& W* K/ K8 ^, X* j 若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有 𝑛 个问号,则认为回答者回答了 𝑛 个问题,请求出回答最多问题的前五个人。
: ~" o3 J7 w0 h 计算每一个Episode的台词条数。
; d8 }# F2 ~9 C% P5 r" | df.columns =df.columns.str.strip() # 列名中有空格
# ]' s/ G* b. m$ l% M df.groupby(['Season','Episode'])['Sentence'].count().sort_values(ascending=False).head()
1 ?/ G( g% u/ x
8 u6 x& c3 h* r+ U+ ~( w season Episode 1 t3 |: F6 Y; }& [% v) h: d+ u
Season 7 Episode 5 505
) b& g9 e% M( v! O: V1 B8 @ Season 3 Episode 2 480% w+ Q; W/ e4 N5 S3 o& |5 S+ W3 ]- f
Season 4 Episode 1 475
% `, @4 ~# h% L8 z5 s Season 3 Episode 5 440
0 o0 {' O3 c6 N ^! [ Season 2 Episode 2 432" H/ J, a: \) P! R) S, H; N
13 h8 Y% {7 f% K! R$ Z1 h7 z0 M
2
3 Q* p9 \+ O% s& P" {. ^ 3
) B$ d4 {5 }9 _ 4
2 s L$ V: w+ K7 T1 e 5' N) `0 j$ \- F
6) a: a0 B6 c9 U
7
5 p' m* E( [0 J& y$ { 8
. t* r' a6 a+ ^$ u8 N 9% ^: d, C' A. H+ v+ M# C
以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
% | o( h9 l* J1 J* H& m # str.count是可以计算每个字符串被正则匹配了多少次,+1就是单词数$ H. Q! ?. {0 l7 E% ~
df['len_words']=df['Sentence'].str.count(r' ')+1# X2 `$ z! {% B
df.groupby(['Name'])['len_words'].mean().sort_values(ascending=False).head()
5 b& H0 ~5 R) q! l! N
7 F' r2 I$ l$ G4 K Name# f0 m5 ~6 G- z
male singer 109.000000& S8 G; Z* v5 E; p8 N" z0 N
slave owner 77.000000
/ Y( o' A5 {9 l- T' l5 n manderly 62.000000
a- Y2 ~' E- k& |4 J4 Q lollys stokeworth 62.000000* Y+ d7 J4 E6 {- [1 z5 r7 L: M
dothraki matron 56.666667
' X4 l- P7 [1 H2 [, S Name: len_words, dtype: float64* D4 T, P. g' [, W3 r6 Z
12 F8 @4 G9 I$ r! @0 Q I
2+ c; j1 T9 Z* k6 Z1 W4 i
3; h! C9 H# i& }4 Q! O9 O2 Y
4- ^# n) J9 ]0 [+ D6 Q% Q
5) Q/ z. x0 O7 j$ _8 R
60 A6 m& p5 c' B* ?( H7 P# A( Z5 B
7% b+ I- k+ M; Y2 Y
82 U: Q5 r- L' } x' F1 I
93 G4 \, I6 |$ U8 l1 |
10
3 a* ^( M d P, \: Y5 x9 ? 11, n& ^. F) w( K: n p6 s
若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有n nn个问号,则认为回答者回答了n nn个问题,请求出回答最多问题的前五个人。 z- ~ C$ e' Y: Q( l* I, V
df['Sentence'].str.count(r'\?') # 计算每人提问数
' B6 r$ u5 X" q4 _2 {/ { a ls=pd.concat([pd.Series(0),ls]).reset_index(drop=True)# 首行填02 Q+ f9 ~$ \5 A1 u+ o2 s& i
del ls[23911] # 末行删去
5 Z, h4 P" Q; X df['len_questions']=ls& q- }2 J' L/ M# t: C7 I3 i" j
df.groupby(['Name'])['len_questions'].sum().sort_values(ascending=False).head()' U9 _$ c- S( Y. n' I+ I
$ @6 I$ n! Y+ H3 `7 o
Name
( o# X+ |' C3 H) D4 P tyrion lannister 527
' X! j+ U' g7 u1 u! G: \ jon snow 374. p- Z' B: g6 p! W y a' h8 L( \
jaime lannister 2836 @9 K$ }% p) y- r
arya stark 265
8 `/ P6 t( h+ A6 P# E% r cersei lannister 246
1 p; z/ }( \, S1 r/ `8 q Name: len_questions, dtype: int64
: k7 {( f1 C8 Q" @; m$ \
! j- W: [3 C: t" D, }' Q- R' C # 参考答案& _% J2 e7 ~% ?' m# C8 C$ E7 Q3 _
s = pd.Series(df.Sentence.values, index=df.Name.shift(-1)): N9 k7 h- R0 P5 d$ k
s.str.count('\?').groupby('Name').sum().sort_values(ascending=False).head()) S! p [8 W9 P' o4 ]* W) S$ P* O' {
9 D4 T) q& [& \
13 i( h2 q* E$ I8 L6 R6 O2 c7 M
2
4 t2 H1 `: k& u6 e/ `/ T+ g2 u 3
) B* w$ M2 F. A5 b* C/ Y 4, D# c: d4 O! t% k2 N6 o
5
& N# S; }; g* { 6
6 a9 Q8 L2 S4 s, c6 o$ Q9 X 7% t5 Z% ?; J6 C* C
8
+ V+ y" |. w0 N3 @1 V 9) N* E& j' f% e0 Y2 R
10
* S9 q# @- T. d. @ 118 A: [3 F$ N7 O! G) u; x1 _
120 ~3 o& l4 ]9 |/ b( ^/ x
13
/ [2 M4 `0 b T9 K; H* R 147 v# S) ]' K8 w0 v8 Z
15
! Z A g7 v* ^1 k7 y: W 16! u: o: T5 n* k! X1 @# C- q
17
1 r" O, ]* x f, V% r' Z7 x ` 第九章 分类数据) }6 _$ |7 j4 X# C( ^
import numpy as np
6 P: b+ {9 ~" v8 c) F! m import pandas as pd
; R4 z0 y. A7 B' B M8 h6 C7 H 1
6 Q1 T7 T: J1 {: {9 J: O E 2- X+ x" D+ N8 O
9.1 cat对象
& e0 r \1 |' j7 Q. } 9.1.1 cat对象的属性
) K r: y; Q [ 在pandas中提供了category类型,使用户能够处理分类类型的变量,将一个普通序列转换成分类变量可以使用astype方法。
. k0 w& X2 p2 I/ L" U/ { . C$ X$ O% s+ z" t* i6 r
df = pd.read_csv('data/learn_pandas.csv',+ I8 ~; l1 l, U) v' p! N
usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight'])
4 b3 C" K: C3 ]' s s = df.Grade.astype('category')
5 c" N# x" C* K/ C) s) ?
# |4 r7 i7 t* n9 | s.head()
6 E9 W5 {, ]4 r1 S2 B2 G' X Out[5]: , N5 Q3 w# I! Z; Q, V U
0 Freshman+ Z8 z! x7 H1 W: c
1 Freshman
- p/ X! f) m9 {. ^1 g" h 2 Senior/ `- ^: b' z: a* L/ F" ]# |
3 Sophomore0 x6 S; o* M; W0 L
4 Sophomore
2 a+ [; s0 }8 l6 L Name: Grade, dtype: category. v4 C. c# q- J
Categories (4, object): ['Freshman', 'Junior', 'Senior', 'Sophomore'], e# H6 z! d3 W7 `, C3 u
1
7 D/ `7 A u1 }. N0 Y" d 2
2 a/ W3 D8 W& ]" _. ? 3
. {9 `( J* \4 L0 s4 s+ C 4
- P1 m1 F& `& l3 D 59 B9 p5 p8 m6 w' ^0 D
6
. U9 X. v. e6 l. B 7
2 i5 I0 I' T2 C/ \9 f. W3 I U 8) o* t7 G3 }. [8 h: [, y
9& E' V; Z$ l) f r1 T
10
% ?3 S$ C, _" {1 s 113 Q- x7 B8 K/ r
12
& f3 F3 x, B# [# ^: Z 13- u) s5 j+ }9 h! t% I
在一个分类类型的Series中定义了cat对象,它和上一章中介绍的str对象类似,定义了一些属性和方法来进行分类类别的操作。2 B: e {8 e' D- X2 p( c2 F
3 B, ?0 Q6 w/ R1 w s.cat
! d* ]6 T- C) B! c Out[6]: <pandas.core.arrays.categorical.CategoricalAccessor object at 0x000002B7974C20A0>
$ X; f- c4 r/ _) C7 F* p 1: c/ C* W% `0 h( O& L- Y, B. T$ H
2 Q9 f2 c! {# }# E) A. b$ t, {+ u
cat的属性:0 a5 x+ b n$ ^. x6 C' J8 Y" e4 w
% s' _1 h, d( p2 l cat.categories:查看类别的本身,它以Index类型存储
7 h& @" e6 l4 V3 o* K cat.ordered:类别是否有序
3 u' q% q& U1 R K cat.codes:访问类别编号。每一个序列的类别会被赋予唯一的整数编号,它们的编号取决于cat.categories中的顺序8 o+ e* N/ \& G3 j+ ?) R1 |7 f+ M
s.cat.categories
6 u: I# P3 b0 a& ~- {4 ]" l Out[7]: Index(['Freshman', 'Junior', 'Senior', 'Sophomore'], dtype='object')8 `; t! [; }- i& }: u
' V7 x- b6 i. ]' a/ A s.cat.ordered4 C2 a4 ]) C& _3 U9 T
Out[8]: False
W' W) p6 R, E' X# V1 }9 h0 Y) Q ]: z, n! Z5 b4 N$ k+ i) t
s.cat.codes.head()
/ `' l$ j7 ?2 C Out[9]:
! I+ s: k% ] u6 f* K. v 0 0
4 j' ?& h/ n6 _/ Z/ f9 s 1 0
- D& @- b) D- s+ a$ U# B 2 2
+ {% J1 r7 j f6 [; Y0 J 3 3
! F/ _2 f0 l4 y 4 3( g6 p5 ~) _* v( j
dtype: int86 c0 U3 F% c# q- U0 B( H5 l( Q; Q
14 E1 C* j5 v; m0 u+ R
2
& D# S. @' s- V6 }! n 3
& E2 M; ~5 w7 U2 F: Q! F$ W 4' X# e& x' W! M* ^
58 M& f W6 O" f2 Y* Q/ a0 K
6! A+ O- J! ]) G" ~: C
7
. J( C0 n2 y6 A7 v 8
% U. Y$ T1 ^# l" T# P( p) r) f 9
# m4 [. s" e" ]6 N+ P 10
1 \/ I' r r- w 11, l/ q" F: P; C) u
12
% q% a" j) N2 C 13. x$ c$ j+ x, d4 y: \/ I2 \
143 x8 n6 e1 X$ i9 _% [
9.1.2 类别的增加、删除和修改8 k# T8 v, O) k
通过cat对象的categories属性能够完成对类别的查询,那么应该如何进行“增改查删”的其他三个操作呢?
7 @- R( z% \1 ^ K) D
" Q+ _2 v! S6 z- h/ y' T 【NOTE】类别不得直接修改
|& K0 X \- Z4 L 在第三章中曾提到,索引 Index 类型是无法用 index_obj[0] = item 来修改的,而 categories 被存储在 Index 中,因此 pandas 在 cat 属性上定义了若干方法来达到相同的目的。7 C0 m; U B+ F6 B& ~) J
) X. W% T( m3 ~+ G; p1 N4 t7 ^ add_categories:增加类别
" H- b; U9 h# R. c( Z3 J s = s.cat.add_categories('Graduate') # 增加一个毕业生类别
5 e8 ?4 c0 x! _# S5 j s.cat.categories6 t ]9 Y: ?* l0 N" v5 Y6 u2 v
7 I9 {8 q% d7 w0 I5 A# c$ { Index(['Freshman', 'Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')+ q3 i: c* @, X) v3 [1 U
1
2 G( r) V2 `' v& y 2
/ N! {: |6 Y" |6 D 3
4 r0 J3 y3 g0 m/ m* w2 L4 D 4
" R) y& Z: c& G7 \( s* r/ x remove_categories:删除类别。同时所有原来序列中的该类会被设置为缺失。
8 Y0 Z8 t7 c" ~ s = s.cat.remove_categories('Freshman')1 b0 h, M0 S0 v/ d! X* }
/ `+ }; h: c S* x8 T5 `2 ]- }: P s.cat.categories9 ]) ^, ]: _% f0 K$ C
Out[13]: Index(['Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
' Z- J1 I6 q* l; j6 M6 x& | ; y& a" C2 w. Q
s.head()
{( x2 R1 p5 n4 F3 [9 S5 C) |! ?# d: i Out[14]: 2 q" |. F0 x! }
0 NaN/ b, v6 \* I6 f! `$ j& ^
1 NaN6 C$ j4 @4 j/ i4 E
2 Senior& r1 v) g0 Z7 w: H( z/ `
3 Sophomore
! a, M8 D0 n+ e! j m( ? 4 Sophomore: O' c2 M/ N1 F8 X
Name: Grade, dtype: category$ B9 m S; z# H. C
Categories (4, object): ['Junior', 'Senior', 'Sophomore', 'Graduate']
. C4 Y" T7 j \7 c9 H1 ~8 \" ]$ l. a, Q 1- o; x) d1 R2 F$ B6 ?# }! b# d
2
* k5 X) |) \6 O$ ]/ k 3
% f+ [! ?/ H4 h% x$ S- D: Z9 p0 Q 4
0 ]+ c& w( O Y, e) b, s 5/ T4 o t+ J. ?- K- F0 q
6" Z3 W, Z$ x6 A) K. ?) y
7
) v3 R' a- ^6 M 8# ]. m5 g0 t) p: D% Z
9
6 Z7 Y7 A& ?* p+ c8 G b" N+ I 10
% A) k; r$ A; n+ B9 t% w 11+ |" {8 F* Z. G2 M$ V) K
12
) ]$ `9 B! y i( A 13
- o9 j* l5 N; k% D3 y: {9 s 14( N5 F7 i) _3 @0 I7 x4 q: m# q( o; a
set_categories:直接设置序列的新类别,原来的类别中如果存在元素不属于新类别,那么会被设置为缺失。相当于索引重设。
- r- J+ S; X5 j$ {8 M% K0 O* C: I; O1 r s = s.cat.set_categories(['Sophomore','PhD']) # 新类别为大二学生和博士9 h- ?" Z1 P" X5 V* S! o( X2 L
s.cat.categories" |$ u+ T$ H$ _5 m2 {) A
Out[16]: Index(['Sophomore', 'PhD'], dtype='object')
1 C1 A$ R9 H" }0 j( \) t( q/ F* u% H
. P$ }: G' l7 M8 l0 \3 r: { s.head()
2 F9 N8 p. V# j0 O Out[17]: 9 v4 o& S9 K+ ?- f
0 NaN2 d5 N$ S$ P8 ~8 o( w0 U
1 NaN; y. \, M! Q3 G0 K$ z: M$ a
2 NaN2 d3 f! ?! c3 `8 R. [
3 Sophomore
4 ^* _/ h6 A) g+ [$ I4 \% K 4 Sophomore8 k3 ?9 S( W/ d2 e6 Y
Name: Grade, dtype: category- [+ s3 y$ V* M8 h$ N' T e- t
Categories (2, object): ['Sophomore', 'PhD']
, q, A; F8 d9 M! @: g$ Q/ z9 K7 O 1
7 N5 e3 P) {6 S% Z 2
: [' P/ t; H; [$ q' t 3
- r. M" i) L% s; H 4
$ |1 C/ [9 x8 D; [' i9 \0 s 58 {& d) o4 \2 ]4 W( M/ l8 s- O% y
6
) [) q/ q. n. ?% d- ] 7 Z# W0 P. x. J, X# N/ g9 [/ D
8$ w9 M. p" U; ?7 g4 w
9+ e3 E4 P p3 f+ J$ R2 W0 P
105 q6 a* h; A& c$ w% b) x3 g
11
+ @% B$ f% d" A/ V/ p 12; Y- _0 S0 t$ m/ K7 f& Q/ x5 U0 |8 a
13
" V E0 Y1 Z* o% P5 v remove_unused_categories:删除未出现在序列中的类别
8 H+ n, I, J9 I s = s.cat.remove_unused_categories() # 移除了未出现的博士生类别
- E8 G4 T: u6 J! T2 F s.cat.categories
+ w2 f) S* M1 j6 I$ a- X! { # X$ p+ V) N* R9 Y2 @: R$ p
Index(['Sophomore'], dtype='object'), c( l* { ]8 d; w$ P
1
$ N! s3 }/ V/ z: f; @1 a 2
. ?5 l4 [( c8 t; ^2 X6 @. U+ ~ 3
l! f9 ?! ?% s 49 a( D: p3 N6 ]2 \0 y/ K! Y
rename_categories:修改序列的类别。注意,这个方法会对原序列的对应值也进行相应修改。例如,现在把Sophomore改成中文的本科二年级学生:5 Q3 ^2 m7 d* \! o5 g' [0 w0 ^
s = s.cat.rename_categories({'Sophomore':'本科二年级学生'})1 \. C4 \6 Z' \; q
s.head(): F m" D, Y4 A v
2 e$ Q: O4 T" A4 e' y 0 NaN
. c0 u4 G0 A% U/ ]9 V# i7 i2 e 1 NaN; \9 `. M; w1 {
2 NaN! x* Y% \7 O6 w5 \+ {0 ^# C
3 本科二年级学生8 W- c4 x4 t9 w# {6 ~7 E
4 本科二年级学生) C8 N1 o+ Z/ P& J
Name: Grade, dtype: category8 |4 O5 o( \" u$ j, q
Categories (1, object): ['本科二年级学生']
; c5 f8 ?0 [5 W. k4 g" T 1
f2 ]. A0 ]( M6 }5 | 2
4 _" g9 j/ w7 q3 g+ a 36 t% K) I# n3 X0 z
4- y* R; b3 @7 i- R& J
5
% \ B" D6 s6 I- a- d1 o4 n; w 6
% w _8 ?7 H; I3 y4 ?' B 79 \; C; I6 W- d* y- N% X) g
8
, q, Y h. ]: H Z) h. A0 \! [4 m 95 o1 w- E, A# @7 \- q7 l1 i: |
10* \# I/ Y6 N3 Z* Q( N# ~* ]8 e+ ` t
9.2 有序分类
3 b" s+ k' `8 k/ h" U1 I/ F 9.2.1 序的建立2 x9 o0 H. K1 T x7 N3 ~- A
有序类别和无序类别可以通过as_unordered和reorder_categories互相转化。reorder_categories传入的参数必须是由当前序列的无序类别构成的列表,不能够新增或减少原先的类别,且必须指定参数ordered=True,否则方法无效。例如,对年级高低进行相对大小的类别划分,然后再恢复无序状态:
& h( G' b9 o, ~
: u& H( C, J4 O# `" J s = df.Grade.astype('category')
- @) { A# s0 o$ ] I s = s.cat.reorder_categories(['Freshman', 'Sophomore',# i9 o/ s4 z/ B& ]/ A' A8 ]' L
'Junior', 'Senior'],ordered=True)) l4 y+ t6 k& F1 o
s.head(); C% Z9 x* M* _& U' m( o, b
Out[24]:
) O4 z. ~! d7 V& ?$ ^; {! v/ A" l 0 Freshman \% N. N; ^! T W9 U
1 Freshman
) ^. i: E. |" n% W 2 Senior* m. ]* ]2 o$ I( k% o: k+ p! ^! w
3 Sophomore- v5 j3 [, c% a2 Q/ R( O) O8 G
4 Sophomore
! t" R0 k6 E4 O$ o Name: Grade, dtype: category
; g, _( Q7 E" @7 X2 T# ~6 [ Categories (4, object): ['Freshman' < 'Sophomore' < 'Junior' < 'Senior']5 E) W) e5 o; b4 X
% C( m% N5 k8 z# x! K( L1 ] s.cat.as_unordered().head()
- ~* l3 C% f v+ |8 ], | Out[25]:
/ x9 ^4 g- a1 w7 X: A; v 0 Freshman( G/ f! ^5 w( A; M
1 Freshman% h3 H, K g& c) _3 Z# O
2 Senior. Y' h. A8 B, @, e
3 Sophomore
; v; b- ]$ g. ~ 4 Sophomore: f: b% P% c! l: |
Name: Grade, dtype: category0 Q4 J* ]8 Z' q& X
Categories (4, object): ['Freshman', 'Sophomore', 'Junior', 'Senior']
- I; o( b7 [! t
8 N0 ?6 z4 \( r6 I) y$ k 1- |: c' j& U. P
2
! S- H% x8 A! M) b5 N: }% h 3
: `1 w6 l9 U- V2 l. F: {* J+ p# ] 4; M# t& t ~8 g* M% Q3 T
5
: ?8 D" Y5 E5 X$ P$ H; Z: Q 6
, T% ]5 o& t- D9 ] 7
4 m5 B* a( M1 H2 f 8
$ t) O3 a( e( t/ r& i 9% e6 u( |& e" D' Q4 Y, \" Z) i4 \
10, q) c3 R! }* P: X$ p, F8 o
11
" j5 V: u6 J8 n 12- e) O! W% U. f9 Z# d
13
& r M* _( }* |& B3 v5 h 14; J: U" _6 ]; |
15' F# a# B/ D; ~4 w4 e
16: g* R6 [& [8 d3 |/ O a U8 A
17) a, ^4 ^/ v* u' v: G
185 _; I) d: V8 j. } _( s
193 i5 z- o6 S3 y+ k% W: x
202 C, y% i( E3 n, F4 e9 D* n% L
211 T j! V/ E/ ^7 Z8 |# l
22
& [. H4 H5 l6 z# ?' \9 g9 B 如果不想指定ordered=True参数,那么可以先用s.cat.as_ordered()转化为有序类别,再利用reorder_categories进行具体的相对大小调整。
+ Z& H1 N1 A( O3 b5 Q$ S( H
1 Q" L! p8 D) x0 k$ A! ] l& l 9.2.2 排序和比较
- a) t# w. \# q! v1 c; G 在第二章中,曾提到了字符串和数值类型序列的排序。前者按照字母顺序排序,后者按照数值大小排序。& ~- T$ P. [8 T
7 y! z8 Z' X/ M/ X 分类变量排序,只需把列的类型修改为category后,再赋予相应的大小关系,就能正常地使用sort_index和sort_values。例如,对年级进行排序:
2 V2 N3 G1 R/ X v, ?6 `% o
$ J3 W# P7 z% J- @ df.Grade = df.Grade.astype('category')
, i. \( M! Z3 U7 Y @* t df.Grade = df.Grade.cat.reorder_categories(['Freshman', 'Sophomore', 'Junior', 'Senior'],ordered=True)" D+ k4 M8 w* G3 e, n& x
df.sort_values('Grade').head() # 值排序; u9 {. m+ ~; @2 K6 p7 }
Out[28]: 1 a+ w) R! _4 H3 {" k% v
Grade Name Gender Height Weight+ V0 p2 X. z7 Z3 q; I: a& B
0 Freshman Gaopeng Yang Female 158.9 46.02 n$ {# A1 y/ H3 j# L
105 Freshman Qiang Shi Female 164.5 52.0
) O( |7 A4 ~; s A! L5 p; m! U s+ { 96 Freshman Changmei Feng Female 163.8 56.0, m% e# _0 b- h
88 Freshman Xiaopeng Han Female 164.1 53.0
8 S8 u8 r& Q+ o! l1 k8 ?/ _" I* ? 81 Freshman Yanli Zhang Female 165.1 52.0" K0 c0 C: ?+ Z8 H" D2 U$ K7 U/ c" W* c
1 ]0 ~6 v5 M! _5 f4 u2 Z s
df.set_index('Grade').sort_index().head() # 索引排序
8 ]- W9 t. C! L5 n4 p Out[29]:
& t& E: m+ s! Q' S6 W* s Name Gender Height Weight
, P6 d2 L" A5 U$ x" s0 ? Grade # M5 I% V) m4 r1 a
Freshman Gaopeng Yang Female 158.9 46.08 v; x# r3 z2 C" u
Freshman Qiang Shi Female 164.5 52.0
C) f8 {: ?2 s& [0 } Freshman Changmei Feng Female 163.8 56.0& ^3 v2 p$ c5 Q
Freshman Xiaopeng Han Female 164.1 53.0& n6 \' I7 [ [: b
Freshman Yanli Zhang Female 165.1 52.0
6 P* W! A- k! e1 X F % i& L/ z/ u3 h. u
1
! `2 I4 e' M) O 27 O ~( {" s1 a, Q' N# |7 v) x
35 Q5 @9 M6 f9 U$ ~/ g
4
: m4 H. w3 X2 `8 C$ {- d: k! B$ ] 5+ @" G" v! q5 v: t. k& v9 {
67 N+ w# d$ q D3 O0 v
7$ t _2 n" r7 z& ]1 m
82 o; e0 {7 b/ C
9
3 T9 Q) L0 c% O 10
3 r1 X" U6 [7 }/ \+ J6 i4 Y 11
" j. y4 I. r: ]' Y" F; ] 12
1 v! \4 C m1 {2 X/ g) t 13
: ~/ f: C; _, v3 u& x 14 L$ J" b# F% B# I, k
15
]7 s6 c: x3 X K" `0 n 16
b: J z1 t' V0 G/ j- c) K0 R+ U1 P 17
7 M* y9 k( l: L+ W 18! G% P6 l N0 l
192 b% p( K+ y5 ~9 U; U
20" Q' b7 w! x8 |; i. ]
由于序的建立,因此就可以进行比较操作,方便后续索引操作。分类变量的比较操作分为两类:
( [8 @- Z! S9 S4 F
- z: }9 x3 `+ Z& V ==或!=关系的比较,比较的对象可以是标量或者同长度的Series(或list)。(无序时也可以比较)2 D+ U8 @6 O3 G; C% F8 X+ e
>,>=,<,<=四类大小关系的比较,比较的对象和第一种类似,但是所有参与比较的元素必须属于原序列的categories,同时要和原序列具有相同的索引。4 W1 \, I, {0 e4 ^3 R; c9 u
res1 = df.Grade == 'Sophomore'
0 G& }. k9 N+ K3 y
+ ~: s9 y. U5 A1 n res1.head()
! ]3 ]% T$ [) c+ @3 a7 T Out[31]: : C v2 g m" U0 R
0 False+ Z+ _( Z, {% E I' w# h! {
1 False# i, B6 g) E2 P/ T6 |: V4 v
2 False
( |! @2 d0 j$ c* Z5 _; U1 T 3 True
. N/ S; ^# q. n& c4 b 4 True% ]" [1 c1 L% e4 |0 m5 T
Name: Grade, dtype: bool$ j$ J3 T& j: _, _1 Q2 T
/ Y+ D, p- H7 _! j6 p. g* C! A8 i res2 = df.Grade == ['PhD']*df.shape[0]( B$ |4 b: j( ]
4 T6 k5 U. K* v( M! j res2.head()( a" V# R- p1 V8 Y. r# {
Out[33]: 4 u) S) v. B2 D. p0 M
0 False; h! c0 q+ S5 Q: b& x0 J. r$ c5 R
1 False
, y& f9 K! b2 [ k/ ` 2 False1 X- t+ L: y$ T; V1 r
3 False! e+ Y- g3 V" Q6 n; t
4 False$ Q8 J0 e& ]% }9 h+ b1 g
Name: Grade, dtype: bool. U _, i. M+ r
1 w7 G( y: ]& w" P# z! T- s0 H
res3 = df.Grade <= 'Sophomore'- X7 K, n; F& i b9 ]" s" _1 `
2 B6 `; w& ]& R2 j' c res3.head()& {( X% m0 s9 G3 K
Out[35]:
9 k6 l( `/ H! C4 z 0 True
/ g) D/ B* @8 e' W1 d& v' V" g 1 True
; X# }) J; j% D1 L 2 False
4 U! ~* y4 P/ E- t* [+ ?5 Q 3 True
/ b. `% I! N$ j3 p# c 4 True1 J! A# M! ^, }! e* `! [0 j8 \+ |5 k0 _
Name: Grade, dtype: bool, b3 M0 Q* A7 m7 V1 s$ o
: f4 u5 G8 ~6 L/ e. J- W2 b
# sample(frac=1)表示将序列随机打乱。打乱之后索引也是乱序的,直接比较会出错,必须重置索引。
/ V/ Y$ n+ ?/ K8 y& g8 A$ B) @ res4 = df.Grade <= df.Grade.sample(frac=1).reset_index(drop=True) 1 g* [; k' |! P3 S) V9 ?
+ f$ x" V2 S( P. @8 H
res4.head()
3 m; _& Z5 X% W: U% j, U+ V0 P Out[37]: " j6 T) U" P2 o3 i
0 True
' ` P; y2 \; S# ?/ a( c! _ 1 True
, \8 d2 Y u- Z6 R+ ?1 U L8 \ 2 False
3 ~) G1 v; i' y( A 3 True8 j0 c9 g$ X- A" E- Q7 A3 K+ d
4 True
8 w3 u% e0 z- v Name: Grade, dtype: bool
" R7 o! s% c/ w% s) \( R7 E ! p3 z/ x; E7 ?3 _# p9 U0 }* M, E- I
1' h4 @1 ?. s4 V! t
20 [4 i* r0 C! y/ O; ~' `! ]( `
36 @, f/ p: D4 L, I0 ]$ b
4
0 [7 ~5 o& Z! ~! R: F 5
$ Q$ _4 g$ g3 \ }# v 6
M% z5 M9 w) `+ E5 w' M9 G4 p 7' r4 c& ~; N. K
8
& @- X% k+ a4 ?* @2 h. B 99 p0 J: Y2 }5 l
10/ V0 }* v% y _6 ]# `7 C4 }- K) V
11; m; C) E4 Z- H/ H8 S
129 `0 e0 B& R0 h D& D' n- j
13
% F. h! b! H) F$ W$ Z0 F8 w6 A 14* j4 W! r ~5 R, f" h: y
15) ~9 s7 J! `. p& B; r& N1 i4 D. ?
164 G# d |# {8 e: K( Q/ @4 z- N3 k
17
% P$ z+ |) h5 \3 p7 z 18
- f9 `9 s; D0 t! L$ K; o 19 _: L5 C0 p8 Y- X- v" j
20
$ y; F" O$ h K3 D) Q% K& j- S( w 213 a. @9 G) h. ^& ^0 H0 }4 R' E) W
221 {& {2 K N* O/ r8 l* p6 m2 {
23
. [/ [, W: W2 o! M( F: J j) _ 247 W8 \/ J, M5 Q$ A# Z9 F
25/ \& }. q' c4 i3 {! O, [
26* v. |+ B) T0 a, Q# Z+ Y; K. o) Y
27
; f( H! x' r9 U! u& ]; J 288 E1 B0 j; S7 n. n# Z4 Y
29
" U% ~ y. l! q3 D' V# i 30
6 a( F; O% u0 s4 B 31' n H' n6 A4 q3 u# {0 q( N2 u
327 d: G9 I3 {4 }8 O3 [8 ^
33$ v6 @7 c1 O6 _
34
/ b8 ~: L$ {! l& @2 j5 l 35' u: ]6 m/ A( `8 ~# v' ^" }
36
. V! E& u2 e2 P. I5 `$ z& z2 d, G2 v( @ 37
$ b& \& Z, T: c8 B% A5 s. }' b 38
( D- _% ~1 |% }, D b3 V 39
* u0 Q/ k, o* o) Q! p P+ H 40
1 v( A3 b# D2 I4 G9 G. G$ R$ j) z7 g 41
* j2 C, o: {6 Z+ w5 f" ^ 423 E2 f% I! I; U, w' U' W* L8 U9 m
43
4 h8 N6 L: q, L8 r/ z( T( D7 b8 C 44" L3 a/ l$ \& ?
9.3 区间类别
- f) w; R* s( f8 o% b# Z+ ? 9.3.1 利用cut和qcut进行区间构造, {$ \( t1 ?& m& l0 P
区间是一种特殊的类别,在实际数据分析中,区间序列往往是通过cut和qcut方法进行构造的,这两个函数能够把原序列的数值特征进行装箱,即用区间位置来代替原来的具体数值。
6 a: I6 Y6 T( ` 1 d8 H, g9 g8 Z9 o$ v1 _1 J0 V; o
cut函数常用参数有:
2 J; N9 m5 K6 H2 H# i9 }2 z: _ bins:最重要的参数。
* p6 r1 Y1 a; u$ Z" w. U 如果传入整数n,则表示把整个传入数组按照最大和最小值等间距地分为n段。默认right=True,即区间是左开右闭,需要在调整时把最小值包含进去。(在pandas中的解决方案是在值最小的区间左端点再减去0.001*(max-min)。)
8 \" q6 @; O; z X% W" j 也可以传入列表,表示按指定区间分割点分割。- H: w, B+ q; f+ {9 w, a- J
如果对序列[1,2]划分为2个箱子时,第一个箱子的范围(0.999,1.5],第二个箱子的范围是(1.5,2]。
8 a3 A" f# m& f0 x) f 如果需要指定区间为左闭右开,需要把right参数设置为False,相应的区间调整方法是在值最大的区间右端点再加上0.001*(max-min)。% \, Z6 H% j0 h$ A# u
# T& A9 b/ N s* S, J( J s = pd.Series([1,2])' i; A( l' E2 i! H$ d! Q
# bin传入整数" A% [' K) S: H4 N
}8 g2 h: H! I. L7 M+ q8 \' ^4 G
pd.cut(s, bins=2)
+ H7 J2 @, }2 M# J3 m7 k0 b Out[39]: 4 A9 C6 m C" F1 p8 ^1 r' l4 Y
0 (0.999, 1.5]
$ r+ O4 X2 f2 b$ u" V; m 1 (1.5, 2.0]
" F, f! D* B, ?) d dtype: category
. r( f" L7 f& G% L0 n- j1 J8 i Categories (2, interval[float64]): [(0.999, 1.5] < (1.5, 2.0]], ]; L- E+ b5 Z& ^/ A1 q0 L$ ?
. R2 E5 o% W; w# y) P
pd.cut(s, bins=2, right=False) K' A0 N' ]4 h- P( E+ @
Out[40]: & v3 X f+ E1 a
0 [1.0, 1.5)" p- g0 u' B, a% r) m& ]) _
1 [1.5, 2.001)
2 r" K( k' Q/ M+ _* x( j# D. n a" Y dtype: category: j8 i0 Z% Q* Q# w v
Categories (2, interval[float64]): [[1.0, 1.5) < [1.5, 2.001)]
k* [3 B, j- K& \4 y 2 T& l0 s" W+ Y( v7 n; ]% v$ s
7 q3 l) D" `( g7 i$ L8 x) ?' K # bin传入分割点列表(使用`np.infty`可以表示无穷大):4 ?" S7 d+ A3 y& x0 I# v
pd.cut(s, bins=[-np.infty, 1.2, 1.8, 2.2, np.infty]): ]% a! q8 Q) n0 C' U
Out[41]:
# T9 z8 p$ S- q, { C# ] 0 (-inf, 1.2]9 p' T8 G6 X6 \7 P' a3 }
1 (1.8, 2.2]. ~" w! i5 R* _3 f2 R' e. n
dtype: category
8 t( H2 r- U& e6 G* V' ? Categories (4, interval[float64]): [(-inf, 1.2] < (1.2, 1.8] < (1.8, 2.2] < (2.2, inf]]
3 h B, r' G# o7 ]. Q# y3 j
1 O ^: f3 ^/ T: |; X0 n 1
) z3 v; j/ h* x2 J/ V4 y E p 21 D: I1 T* x6 P0 f, a- E& o
3
" g# @- Q' L; s- e7 a2 S 41 O4 ]1 g) T" V9 O' D
5
- b4 ]! ^" Z+ w 60 n& |# s% J P8 _. i1 U- i/ U
73 h- z* N# |' f. V4 [' K7 b2 D
8
: P/ D6 R# e' P* o0 W7 ?! G+ A7 ] 9
- y8 U3 \8 A2 n2 f; g# B# B8 t 10
% A2 j4 Q& v. y O' a7 Q' p 11# D# Z9 |5 L' Y0 h" M! K/ |
12) X* d1 n! P9 X# W6 P- l
13
5 P0 `) m5 D& r+ ?; h v 144 f' X% T5 V2 H+ b( Z
15
" B% U6 c2 v( q. n6 F/ v 16
! N7 T& U7 p& Z3 g6 L5 K) R4 M# C 17
) g8 x4 S) K/ ~+ o 18' ^2 Q. G2 M R; n
19+ F& T& Q! j5 W: D* ~
20
) t2 A: |5 }2 v: e: h% q 21
4 i' X9 [+ ^0 o, H( Z4 s 22
" ~8 t9 R( p) I4 s 23) K, Z( I1 n9 B* d9 P& s
24
: T1 ]; ~+ Q B! S5 S3 M# N" P 25
7 P$ y) Q) Q$ z* R i2 x) @ v3 ~ labels:区间的名字: N6 u- N1 @4 n7 W' t j5 ]- l! V
retbins:是否返回分割点(默认不返回)6 E5 t: g" A4 e: }
默认retbins=Flase时,返回每个元素所属区间的列表
" u1 o7 F, E6 S: G: }) w retbins=True时,返回的是元组,两个元素分别是元素所属区间和分割点。所属区间可再次用索引取值
Z z7 n) j& {' w& Y; n" E
& j9 E: L3 c" r s = df.Weight. o; P* h- P) b- `
res = pd.cut(s, bins=3, labels=['small', 'mid','big'],retbins=True)
& f6 \5 g R6 ~+ |" b res[0][:2]6 a- _1 K L4 l; U5 c
& M$ U. w$ u4 ~: q0 u; | Out[44]:
3 H8 A3 n2 T. ~1 ~0 P) g. b; B 0 small q) C" ]) z7 v/ E# v
1 big8 w5 x: P, ]5 T( l: Z% T" p
dtype: category
/ ~! j. A+ B9 R" J; x# C1 z8 ^3 T Categories (2, object): ['small' < 'big']
* P, F* v9 G' L+ b& m
; \) P8 L& ?2 p3 R* y8 h res[1] # 该元素为返回的分割点
, W+ o" w" Z5 f# b+ _; P5 X/ ? Out[45]: array([0.999, 1.5 , 2. ]), W4 ^$ `) P. s0 g/ P/ k$ A9 ^
1
, x" @7 n/ D9 h 2% D" X( [ h" M N
3
1 n: l; D4 _" {$ z. ]9 K% Q 4
, Y. D3 h8 c6 I. G) h- A3 ^ 5
4 w/ g6 O5 B$ F2 G( m0 H 6
& ]" U# n2 [7 r1 f2 d* I 7, \& m3 f8 }9 l
8
8 x' o% X8 v; G8 ~/ b0 D 97 o+ u/ g J8 W# @4 l
103 L; l0 A% e \
11" ~' \2 K/ \" p$ ?4 C$ p7 a5 M
12
$ I8 s% A: k* ]" a- c j! \ qcut函数。其用法cut几乎没有差别,只是把bins参数变成q参数(quantile)。
; z& i; J+ s# o1 @7 [8 z q为整数n时,指按照n等分位数把数据分箱
8 A( \& n) a; L q为浮点列表时,表示相应的分位数分割点。% _: _* ^5 y' M' B( y/ f9 Y2 Y
s = df.Weight
- K/ D1 a( v9 c8 u
& C( {) {, a- E/ X) E pd.qcut(s, q=3).head()& D0 {0 R' ?# M
Out[47]: ! e2 L. w& H" y$ W& G, S+ @3 O
0 (33.999, 48.0]
; Z" K9 w- @2 D I6 V A 1 (55.0, 89.0]
+ }' h/ j% k( I- A1 h* o( K# \ 2 (55.0, 89.0]! Z4 q ?$ q% u, I6 x* N3 P
3 (33.999, 48.0]
9 F/ _& }5 K `1 l) E# S: D 4 (55.0, 89.0]
/ |& [* k, f& v* z Name: Weight, dtype: category: `. k" w/ w( F2 `
Categories (3, interval[float64]): [(33.999, 48.0] < (48.0, 55.0] < (55.0, 89.0]]+ ~4 C* a7 ?/ `+ G/ }0 w
( l, T8 ^ E+ m# U5 P3 U pd.qcut(s, q=[0,0.2,0.8,1]).head()
* l* i& `* q0 B& v$ f: S6 j Out[48]:
H" _2 U+ D$ J" r: T6 M 0 (44.0, 69.4]8 d# [, b0 z0 _2 R- a Q% |, ]1 [
1 (69.4, 89.0]) @# A k) G7 ~' k4 i. ^
2 (69.4, 89.0]2 u1 j7 v; x2 |5 s, k0 P
3 (33.999, 44.0]
* H+ I$ B) K1 m1 }8 Y) N8 q# ? 4 (69.4, 89.0]
$ Y; R+ `3 y& j) u; r Name: Weight, dtype: category
* v- `6 O* ]) K Categories (3, interval[float64]): [(33.999, 44.0] < (44.0, 69.4] < (69.4, 89.0]]
6 ~, }: U! _ T$ D. w % j8 H* ^9 ~# C, t: W- M
1
% s) m6 j7 ]& |/ k# Q; o8 r 2
) l4 S6 n5 ]( A 3# @4 C. v! I( X5 m
4. i/ G+ E2 ]2 q
5, p9 Z3 [! ^; X4 B9 F% d' J4 @" A; {1 S
6
2 P! u5 T: I/ ^- h8 C1 A( L 72 V4 f( v+ E- ^7 G h [* L* \
8
j% h' D: j. e/ }; H* Q0 C* o 9
& ?1 F/ r! H' H+ i6 Q% h 10( M7 p6 X- ~# C( W( C* [
11, ~0 O! Q. D1 S* }) q ~) B5 ?& w6 o+ H8 c
12$ z2 y/ v- q+ _% g7 j2 i8 L7 {7 v
13' B9 \9 L7 G4 l# L; b) S% A
146 i. f/ u f, F) i3 ?2 p
15
/ \, J. u4 l+ x) A I8 j 16
: S. @& d. O) l) l# y7 ~ 17
$ W, @4 {- V8 [ 189 s! }% J0 G6 v7 r# G6 `8 }& [" ?
19
8 [9 Z; z K- f 20; ~" g8 n! G9 k9 b* V
21
; f# k% s* t( H5 p z5 u 9.3.2 一般区间的构造5 [6 |% m- W' r% t9 z6 w
pandas的单个区间用Interval表示,对于某一个具体的区间而言,其具备三个要素,即左端点、右端点和端点的开闭状态。* a% e' Z% f/ @. O4 q
; V) K) E, ]1 E
开闭状态:包含四种,即right(左开右闭), left(左闭右开), both(两边都闭), neither(两边都开)。
! s2 n0 I& a+ ]3 S; v/ _# ]$ d my_interval = pd.Interval(0, 1, 'right')
* ~$ r D7 @ n7 b! E" k7 { / Q- P! u5 H2 Z; s
my_interval; Y8 I& h* d9 T" n% ~
Out[50]: Interval(0, 1, closed='right')
0 n1 U @- P5 e b+ I* d+ K 1 _0 j: H) F! E
2
& f3 R, K7 D6 D1 D7 r4 i9 g0 t 3 j0 V- p$ l6 z6 H! g* f
4
1 s. s8 X0 s/ Z" t8 p, O 区间属性:包含left,mid,right,length,closed,,分别表示左中右端点、长度和开闭状态。) r! u# B! M2 T* K- N4 s2 e
使用in可以判断元素是否属于区间
9 q b3 ?( W( H5 ^' P3 ], x7 i 用overlaps可以判断两个区间是否有交集:
0 x: D, e1 ^8 D4 [ 0.5 in my_interval
% A* r# x# J3 L* P; S# s 7 y% n9 @. G# Y( Y) T% q
True
: _9 A+ E% Q: o7 u; j0 a/ p. c+ [! ` 1( D& p6 p- @$ c* i, m
2 E% o i% E1 r4 K- u
3- b* f) U B, {; U6 ?
my_interval_2 = pd.Interval(0.5, 1.5, 'left')
; Y9 O; p0 C8 D: f( ` my_interval.overlaps(my_interval_2)
. }9 x; a4 s* X1 K9 A5 T% P
8 G) I5 b/ _ V4 }& t3 S True! x, \) `2 }* Q) z% y
1
* R) h+ P5 O9 j7 J9 v$ u$ Y4 W 2) q a5 K2 N, q# ?- g6 K
3, t( x S# Z! [. h7 v
4 q5 S, r- s8 g+ ~
pd.IntervalIndex对象有四类方法生成,分别是from_breaks, from_arrays, from_tuples, interval_range,它们分别应用于不同的情况:
8 T; J* G& E5 U
' J% W* M9 t# A; g* _/ l# W' d from_breaks:类似于cut或qcut函数,只不过后两个是通过计算得到的分割点,而前者是直接传入自定义的分割点:$ e5 \; r5 p. c& G' g j% M
pd.IntervalIndex.from_breaks([1,3,6,10], closed='both'): I+ x1 s9 Y( b7 T9 n& M5 V( I4 B
: j/ Z1 t1 K. q+ Z8 }
IntervalIndex([[1, 3], [3, 6], [6, 10]],4 ]1 G! `/ M) O/ Y$ I
closed='both',: G/ m6 ^. F9 b+ p- |& b, B+ Z
dtype='interval[int64]')) `7 _+ f. w2 M
1% _! ?2 Z/ J' o7 n& w) N3 Z" R% s7 Q% l
2
& ?' S( u9 K# k; ] 3
1 C! c7 m) @* O2 g: } 4$ K. v u* Q! g% b8 m, n
50 I0 ~1 D4 e/ ]) i$ ?
from_arrays:分别传入左端点和右端点的列表,适用于有交集并且知道起点和终点的情况:* ^# L- u! K) O/ A o" U1 t; P
pd.IntervalIndex.from_arrays(left = [1,3,6,10], right = [5,4,9,11], closed = 'neither')) Q9 m5 x2 {" i9 y
8 U. V7 I d% a2 w0 d IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],
( y4 b W7 g+ Q+ U1 A/ d6 K- L closed='neither',% {( U3 p- l: S+ H7 K, x2 @6 e/ @
dtype='interval[int64]')8 }3 y" _ }' y0 a; z* i2 q
17 n; I6 _6 P8 _: `6 f) l! c/ O n) {0 L
2
6 B# O6 P" ?8 G. ^: l' J& w 3
5 c! k1 C1 q. U 4
% {1 M( H( h% a4 D' Z* U 5+ O$ V! E+ @ f9 M4 i* t, H# D
from_tuples:传入起点和终点元组构成的列表:
0 ]/ j! v" a! N/ A pd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)], closed='neither')+ u$ Y- j h! n# J" E
( _5 r8 ]6 t7 n$ Q' `0 s& l- R IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],
$ k: `+ M- q8 i; W! s5 [ closed='neither',! ]2 t6 o0 Y0 q( g. `* f) A: R
dtype='interval[int64]')
- j! j8 |) Y. n 1
9 V* i6 k7 F. l. O! C9 ^ 20 p( A. f, M& v" G0 o
3
+ O6 V2 D' [4 g) a 4
! |8 j' K( w2 x% A5 @$ ~ 5" ^$ O7 j5 l+ }5 C4 U' \6 F% P
interval_range:生成等差区间。其参数有四个:start, end, periods, freq。分别表示等差区间的起点、终点、区间个数和区间长度。其中三个量确定的情况下,剩下一个量就确定了,从而就能构造出相应的区间:. Z# s3 b O/ t4 x
pd.interval_range(start=1,end=5,periods=8) # 启起点终点和区间个数" e. \7 E+ s6 \; S1 n
Out[57]:
' r0 p3 G- V8 D IntervalIndex([(1.0, 1.5], (1.5, 2.0], (2.0, 2.5], (2.5, 3.0], (3.0, 3.5], (3.5, 4.0], (4.0, 4.5], (4.5, 5.0]],+ Q z& t- {& N x3 I9 s; Q
closed='right',
$ Z. A0 S/ q; @( ]4 W* s dtype='interval[float64]')
9 G: Q( r4 f4 X) ]8 e
9 R1 G% ^# y4 `, q# k pd.interval_range(end=5,periods=8,freq=0.5) # 启起点终点和区间长度& l( J) w+ l7 O( e* z! n2 p
Out[58]: - A3 P2 Q: M- C& f
IntervalIndex([(1.0, 1.5], (1.5, 2.0], (2.0, 2.5], (2.5, 3.0], (3.0, 3.5], (3.5, 4.0], (4.0, 4.5], (4.5, 5.0]],
3 T. `0 f0 c7 E, |" z) }2 Z: \ closed='right',
9 ~; ?! Q; s6 w7 X( d3 A8 C6 Y8 ? dtype='interval[float64]')
, R2 b1 a F9 L3 b! L0 L 1
: R5 H" S( G q# { 2
0 r+ l, {8 G% G4 m 31 R' q) M# m' y7 u Q' f3 \
4* I8 e# l& j" ~5 u" s+ f
5
9 `. z1 h7 S$ w 6
I5 |( z2 \# S) n5 \# ~8 n 7
( z8 S% F5 j7 _1 V$ a% H; v8 V* N 8# d% Q, R7 {* a; C
9
l* M8 O$ P0 V+ M 10# j( p. @' n1 Z- _7 Y0 K. A. a6 c
11
( J+ l: F) x5 b! f1 L' O) d 【练一练】, l# }) u9 n5 B/ r
无论是interval_range还是下一章时间序列中的date_range都是给定了等差序列中四要素中的三个,从而确定整个序列。请回顾等差数列中的首项、末项、项数和公差的联系,写出interval_range中四个参数之间的恒等关系。
" y6 u3 Q8 w/ w* } 0 F8 ^% x8 j2 X( d; q( \+ o* U
除此之外,如果直接使用pd.IntervalIndex([...], closed=...),把Interval类型的列表组成传入其中转为区间索引,那么所有的区间会被强制转为指定的closed类型,因为pd.IntervalIndex只允许存放同一种开闭区间的Interval对象。
0 c- W' _8 v2 _ & |: L5 p, g \$ V1 ~$ \
my_interval" Z4 G7 [1 @( T) K. L3 Q/ Z
Out[59]: Interval(0, 1, closed='right')1 I4 G3 H+ u7 A ?
+ z- I) i W9 U
my_interval_2
( u4 l; L" h; W Out[60]: Interval(0.5, 1.5, closed='left')0 P" G# I, f$ \0 B6 X) M
2 O/ B% D/ R: Q- y' z pd.IntervalIndex([my_interval, my_interval_2], closed='left')5 _( x) N) S S, F' }: |% k0 F
Out[61]:
9 C, @5 }" t/ I# V. P f/ ^ IntervalIndex([[0.0, 1.0), [0.5, 1.5)],, j1 m" n" r% o; U8 L4 ?
closed='left',
6 ?, }0 f a& s! x7 J dtype='interval[float64]')
. J! a" E7 s) j; G# k" V- D ]) k 1& S, H! Y9 m4 x6 O) j, S0 s# C& ?
2
( o3 |5 U/ g" ^9 l9 p 3
& M- t; k% h" N" U( F2 V 4
+ Q! C9 w Z$ f- X& [9 ]: P# g 5
- m6 o$ f- ?8 l' y% f8 ]1 e/ g) v 6( j* Z. Y; \2 b# y2 {6 ]
7
' V7 v, K% d9 N" D% X 87 \/ ?' ^: M! g# A: B) r
9
7 o- x1 y" V. p6 |6 s: { 108 R0 B! T* _4 r+ k+ }7 ~, n, |2 t
11
) f& j) w8 I& G 9.3.3 区间的属性与方法
6 V' \, n6 s/ g IntervalIndex上也定义了一些有用的属性和方法。同时,如果想要具体利用cut或者qcut的结果进行分析,那么需要先将其转为该种索引类型:: m* |6 r( g/ B& P$ V
3 I( e N2 ^% u+ k( j( L6 g5 t a" `
s=df.Weight/ \* q6 t ~6 M$ b8 d- l3 {% l
id_interval = pd.IntervalIndex(pd.cut(s, 3)) # 返回的是每个元素所属区间,用具体数值(x,y]表示3 z- n7 Q0 P7 i/ ^) b j
id_interval[:3]
- Z$ t K& T& F8 x3 w3 [7 m . v* h, \# c, N( f" |2 k
IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0]],. M B8 T @. l3 M* n
closed='right',
1 k; d' ]! N# D' u8 k name='Weight',
0 X5 e; w& ?' b' a dtype='interval[float64]')
6 i# M7 s- J. \9 S9 E 12 t* c% N W/ D; `5 Y' Q$ a
27 a/ X3 E! M. i& S! I
3 G( A" m5 i3 f R6 g4 o
4
( W- @* k( c3 u R- P. H 5
7 ^' ?; g9 N1 r2 N 6% J+ b6 }1 _# k, K, h2 v
7
4 k+ }2 \& N$ z* M# ^% X; @* y 8- S$ x# r+ d+ u @5 |2 d
与单个Interval类型相似,IntervalIndex有若干常用属性:left, right, mid, length,分别表示左右端点、两 点均值和区间长度。0 ^: M+ @" S$ Q% o" _6 G
id_demo = id_interval[:5] # 选出前5个展示$ Q6 f: n( E9 Y( m+ A
7 p1 A( t1 ?3 `3 k2 A1 a9 }% e- P id_demo. @% ^+ A; I& [ f4 v3 ^5 p
Out[64]: , X8 n: u0 `. R; l
IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0], (33.945, 52.333], (70.667, 89.0]],6 Z( \% J9 j' T( }
closed='right',
& c7 a$ i* f: \7 M name='Weight',
) j) W( H/ c7 B G1 _' [+ v dtype='interval[float64]')8 v2 A2 {0 A7 W, i' C' Y! f) D4 \
. q! f h, ^: i$ ]
id_demo.left # 获取这五个区间的左端点& { O. Q( [1 Q0 E
Out[65]: Float64Index([33.945, 52.333, 70.667, 33.945, 70.667], dtype='float64'), X4 e+ l# r2 ]% A; j, D" k
3 ^( E" L4 k/ E( t
id_demo.right # 获取这五个区间的右端点
, `+ X" l1 ]; _ ` Out[66]: Float64Index([52.333, 70.667, 89.0, 52.333, 89.0], dtype='float64')
# ?3 z+ d! p7 r- D5 }3 Y
, M! j1 X5 t; [* e- H id_demo.mid( K4 p, p2 @1 ^2 D# t
Out[67]: Float64Index([43.138999999999996, 61.5, 79.8335, 43.138999999999996, 79.8335], dtype='float64')$ I' ]2 c" `. K! J* ]1 B. o6 J7 U+ P% n
6 H/ X9 R. \1 e/ W; h
id_demo.length' L$ M* j, R# P/ F, S7 z
Out[68]: 2 R. I( E0 z) h
Float64Index([18.387999999999998, 18.334000000000003, 18.333,) p$ ?& v9 ^$ F$ I- i) K
18.387999999999998, 18.333],! N2 `8 |! D/ }% @
dtype='float64')
1 w; |5 F1 j' O! B0 A9 }, G' G 1 c7 q4 m( z k; _' W; E0 y
1
: A% J# ]' y& R7 M# j: S1 S 2
' {% c# g# o" r: y7 r1 [ 3
2 ]: k7 J, K- j, L) f; Y% J+ u 4
8 z1 d J& {6 W7 r/ S/ j 5
7 R% O; b; O B+ @ 6
. l6 b' A! t @8 V1 d 7
$ i8 j4 ^- j, e 86 d d3 K# Y8 |+ A
9
- M$ ?% s7 I! o! ? 10- q# X: K% a7 t9 U' X) |
11
q, L7 {! h, s$ R9 N! i# m, S% h 12
7 n. }3 j3 ]4 s. i! w* i 13
& H+ P. @) ^, [' ~' i$ s 14
$ a. v/ ], e8 E7 Q 155 L' c! ?; N, d$ |# r8 y- ]
164 U, ~! o" U( J! S! v
174 ]# _! B) h2 E' r
181 c4 F* R. A8 M6 `
19
+ x! K: i: s2 r6 o! q 202 e- j0 F b1 {+ w) S
21 t; w& i5 z; r2 H$ i- e0 u
22
' ~' c5 p* ^- S) t" p+ R' T 23
' F) S3 ~6 r' U S! x" A IntervalIndex还有两个常用方法:
; `+ t. N3 W5 a+ L3 @7 G contains:逐个判断每个区间是否包含某元素' Y- g% K. x6 f$ a; M
overlaps:是否和一个pd.Interval对象有交集。
" w( X3 Z# R$ f8 S4 ], n- P id_demo.contains(50)
* E8 {7 f/ w3 m$ J# X Out[69]: array([ True, False, False, True, False])
: e4 j: ?8 y' \3 s; P3 u8 ?
! L4 F* e/ n0 u( L1 O id_demo.overlaps(pd.Interval(40,60))
1 i/ i, N4 ?* n6 I- B3 s" U0 c! j& J Out[70]: array([ True, True, False, True, False])/ \( ]! g$ d( A$ V7 v6 J' l
17 L4 H5 v7 ^8 `- ^! S
2
3 h: }/ c# A9 u; e' F2 O s1 g 3
* `9 C$ C* F& K! G) d# N5 ]2 ?' [ 4; U* n$ T' c! A6 V5 `' h1 w! G6 Q' O: c
5: N" K# t# S. T( P% c
9.4 练习' B) f( L( A1 d7 H& {
Ex1: 统计未出现的类别
) L1 ?: d5 H8 f( F) C4 B, v/ z 在第五章中介绍了crosstab函数,在默认参数下它能够对两个列的组合出现的频数进行统计汇总:
" {3 [4 d& d r ' `1 J p* w* a/ G; l( N6 {
df = pd.DataFrame({'A':['a','b','c','a'], 'B':['cat','cat','dog','cat']}); _7 {/ B9 h1 E
pd.crosstab(df.A, df.B)
5 u) P8 V; v, v9 f/ [8 R+ o * L* q8 x+ B: z8 A. @
Out[72]: 6 i7 J" A5 b; `& h
B cat dog; @1 d" q8 y' Q$ h$ G4 c( z3 U% [! f
A 7 l) u% Z& y9 Q1 [- p
a 2 0& E) }. K4 H9 Y, y% m( L
b 1 0
9 ~! l( V# b' ^( w" L! T c 0 1
0 k/ m/ Y- a$ [4 B% u 12 e0 t @- U4 @; k6 G7 S
2
" k$ z3 b( W9 l6 a2 [7 ?" e$ [4 x) S 3
# i' N+ K) t% k8 F2 o9 A* a7 q* f 4
* z1 ~* x+ [( _' G8 T 5
, \/ m0 Q8 Z% I1 l0 H$ r) v 6' s& G. S0 ^/ i1 ?8 F
7
" N6 K. e: d% ? 8
# L) f8 z' p, B1 P% ~8 k% A$ G 9
1 G6 H' N n/ Y! O2 z 但事实上有些列存储的是分类变量,列中并不一定包含所有的类别,此时如果想要对这些未出现的类别在crosstab结果中也进行汇总,则可以指定dropna参数为False:
, C! L1 y9 z4 ]
/ P% a4 w' d( a df.B = df.B.astype('category').cat.add_categories('sheep')2 v$ h7 X) R( J% h
pd.crosstab(df.A, df.B, dropna=False)! k' O2 s; z9 Y" G, E1 B1 c) P
! ?/ k+ a8 g: q: z" Y. |1 u
Out[74]:
2 @) |4 r" i7 i/ O- l4 e3 F B cat dog sheep. b# k4 J6 N; x+ ]
A
* @- m& u5 f+ R+ g4 d' i a 2 0 0
; g( K) Y( p- V# B0 g b 1 0 00 W# p4 r" O( u2 [" h
c 0 1 0
/ N' H" f; X. }+ A1 ?: ~ 1' x5 l1 E9 U" K) x k ?/ I
2
: R" `# }. a% e" s 3/ X2 }: q, p4 H5 u8 t8 p3 E0 Q/ G
4
# q6 ]" z4 D; c7 P8 C: [2 ? 5
2 u5 U' G' c; t( |3 g0 @ 6# I2 R2 |$ {% B4 @; d7 C, c
7! Z; Y8 Y# ^+ M7 E1 z( D
8: u. I0 O+ B* h
9, l8 P% _8 p& d) L0 m5 H* _. h
请实现一个带有dropna参数的my_crosstab函数来完成上面的功能。
0 c8 k/ y, U; T" }- v7 d+ E/ w" }
$ G8 g4 T! d+ ~. Y6 z- ]* ` Ex2: 钻石数据集5 b& v9 c/ [) K: `3 R7 N
现有一份关于钻石的数据集,其中carat, cut, clarity, price分别表示克拉重量、切割质量、纯净度和价格,样例如下:' O2 r3 T& b& \5 W5 T8 `( p" Y3 L
; ^) a; e2 k5 M( d% S( ?3 {9 S df = pd.read_csv('../data/diamonds.csv') , ]( G) r$ N' r7 a7 H
df.head(3)
* c& B4 Z* G1 i( o9 e
" K# W* h5 S6 \9 l# _: ]1 i5 A Out[76]:
3 g& d% }; r) ] carat cut clarity price
6 s8 s: k$ u0 Z! O& R* J! z. H 0 0.23 Ideal SI2 326
/ A4 t: _4 H4 s# U9 v( J 1 0.21 Premium SI1 326
) z* t# E I5 y 2 0.23 Good VS1 327
5 |/ K0 I# x4 l7 d5 c8 n 18 E& O& h. D% X/ V8 N
29 p6 O* R( G' E$ a% t
3
' q8 M- w6 H* _. ]: Z9 | 4% L# u/ s% y+ D1 H1 a p) ^3 I
5# z0 I# `8 c# c6 B; |( u
65 D+ v' L" O8 k
7) [9 U; Z, ?$ J7 ~% j% c& ~
8
0 M4 W% P" `7 s3 x, T {4 u 分别对df.cut在object类型和category类型下使用nunique函数,并比较它们的性能。
8 J( V$ A: M4 I1 x8 Z4 T$ s" T 钻石的切割质量可以分为五个等级,由次到好分别是Fair, Good, Very Good, Premium, Ideal,纯净度有八个等级,由次到好分别是I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF,请对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。. B8 v! l" s! R) ]0 }! W
分别采用两种不同的方法,把cut, clarity这两列按照由好到次的顺序,映射到从0到n-1的整数,其中n表示类别的个数。
4 K% M; G' z9 c" ]6 l; T 对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。
# h u3 F, M: Y: w( T 第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。+ ]4 j t5 d9 g3 m6 { O' m
对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。1 G5 a' P( W( ]* G. n6 ]1 R$ ?: t
先看看数据结构:
9 N5 n/ p9 v! L' g& M( a" Q/ \
% R/ X& |* _& J* I df.info(), o$ u+ S3 v3 _
Data columns (total 4 columns):
3 F |2 X* {+ j7 ]* o2 c. K' w: \; ~ # Column Non-Null Count Dtype / \0 _$ Z& Q. O
--- ------ -------------- -----
& \% C+ K, C0 V7 B 0 carat 53940 non-null float64 v2 R6 q7 u% e9 }
1 cut 53940 non-null object
2 n! |" O. W( ~$ p3 c1 N4 ` 2 clarity 53940 non-null object 9 ]8 H) M6 |" @# g! p: E
3 price 53940 non-null int64 # ~2 ~: ^; l- [9 j# q( s0 S
dtypes: float64(1), int64(1), object(2)/ W- y; a6 Q" c |& J
1" m4 z8 t8 t! m: z' s/ e$ [0 f
2
& k) j% F& H2 P3 m 3& o3 u' p T$ |4 R$ F
4
7 D7 Q5 b- R+ Z8 |2 L 5
# I8 |7 n- U9 s( A- \5 d/ f8 q 65 _- ~+ N3 Q2 }/ e( S4 Z
74 x1 @3 x# }- O! T7 `: j! t
8) T2 b: d3 `: \ |$ f. _4 R
9/ G. g' k3 F3 X4 N8 K6 u9 G
比较两种操作的性能
. A; o& q* ?$ z H( o8 N %time df.cut.unique()
0 b, C3 e7 j$ A5 Q+ R& ~, B " V8 |6 W `9 A* ]7 y
Wall time: 5.98 ms; W+ b, u( V" Z/ Z
array(['Ideal', 'Premium', 'Good', 'Very Good', 'Fair'], dtype=object)
3 G; v" P5 ~# [' a- r: Q! s 12 l w+ C, _2 b
2# k, R+ g7 D* a: R
3) m* c p/ s! I# W; P
4
% ]9 ]1 m4 v8 G7 P; o+ Z/ w1 d6 U %time df.cut.astype('category').unique()1 n( p- h* U: H6 X1 z- m& M( N8 l H
( n4 h3 @ h: I Wall time: 8.01 ms # 转换类型加统计类别,一共8ms8 n1 F+ `* N3 s
['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']( z8 s: J' T2 c* W; t/ d; P) k2 e
Categories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
1 I6 M7 z9 P8 f4 X 14 }9 m4 x+ I j( W
29 o# @( }2 O% E
3
4 w: m1 c6 N) e a& ?9 U 4
# f0 g6 [! D9 u, \! w- r3 |- r 56 D0 U4 R' u& ^1 u0 a w
df.cut=df.cut.astype('category')
8 F: ]: K$ G) p. [ %time df.cut.unique() # 类别属性统计,2ms
1 U3 v- b7 y( ] b" V2 k 6 |+ H3 f* n; B6 I
Wall time: 2 ms8 c/ t I. ]! H! D
['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
' s$ a. n0 k V& t7 t. J) y0 K Categories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
' I( D: J! _# [" A 1- H; W7 L0 }/ e6 A; M! `3 R
2# p; L5 ~& @9 Z) L
3
& Q/ W `# _1 Z4 M3 r( t 4
$ X3 d. |6 X& D 5( b& _- S$ b% Z1 V! M, W4 v
6- h) @0 V1 b" Q5 e7 C
对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。
5 v% i. j. L# g( y- W! l ls_cut=['Fair', 'Good', 'Very Good', 'Premium', 'Ideal']
# S( r8 j6 L& r% Z# Y- t ls_clarity=['I1','SI2', 'SI1', 'VS2', 'VS1', 'VVS2', 'VVS1', 'IF']
8 r3 y5 [0 z$ \) p. f8 e df.cut=df.cut.astype('category').cat.reorder_categories(ls_cut,ordered=True) # 转换后还是得进行替换" E. C0 N0 Y& q- h' c6 g$ s% W
df.clarity=df.clarity.astype('category').cat.reorder_categories(ls_clarity,ordered=True)- I" Q# Q% X1 }0 p+ y2 x; K
3 y& _/ h i+ S! P+ w! O/ }' P' K df.sort_values(['cut','clarity'],ascending=[False,True]).head(3) M1 R0 _$ F [* r- I% \
4 y6 c! K) }, y) {! A* h carat cut clarity price' k. _! n: d* i5 ^1 l
315 0.96 Ideal I1 2801: q/ \8 {( l. W) s6 I7 Q% h, e
535 0.96 Ideal I1 28269 `1 W B6 G! b/ H( B
551 0.97 Ideal I1 2830; n3 J* G$ c# v: Z0 _ J" C
1' ~ V0 w" S4 Z9 {
2
1 j. g. O. Z$ u; Y! H, b2 Q 3' Z# a" ~! t5 R8 W
4
* k( V& r+ D6 h% L% e- i 59 v* @$ z2 j# F% ^) `
6' q; S7 x( b, }; I
7
; o; w- w8 ] V! J. i 8
. L; ` Q" `- a 9
( b! a9 y& t3 n+ a; e* v 10
: S; \6 _2 d+ f# e 11
6 n$ A# z# G) p' @' W* C! c9 s2 q 分别采用两种不同的方法,把 cut, clarity 这两列按照 由好到次 的顺序,映射到从0到n-1的整数,其中n表示类别的个数。' o7 X, c% `- p$ L1 z. \6 ~
# 第一种是将类别重命名为整数
; W5 B2 P2 J8 r+ I6 U dict1=dict(zip(ls_cut,[x for x in range (4,-1,-1)]))( C1 o0 ?' O% S! w
dict2=dict(zip(ls_clarity,[x for x in range (7,-1,-1)]))4 s2 A) j1 w+ t, r. K
' f0 l- R( y4 R0 {+ z/ [. f
df.cut=df.cut.cat.rename_categories(dict1)
7 |4 e7 @, z; H$ z% k- X df.clarity=df.clarity.cat.rename_categories(dict2)# b S" h& M2 r
df.head(3)- O/ |4 s5 g6 f0 K3 W
, H0 A- j' A; g5 T: i6 w a
carat cut clarity price
# c @ C5 j' |' s y 0 0.23 0 6 326, m' W/ a' y n+ S
1 0.21 1 5 3262 R$ F; S: b$ R1 L1 b' ^/ m4 B
2 0.23 3 3 327
. A9 N s) R) i/ w% c+ ?, c 1
# L8 V( Y" d2 o$ P5 h 2
1 {9 v8 |% x7 B4 W e& H4 o 3
' }- @! S K1 U& h; ?2 l 4- \" V! h \( Z* j1 V% @% m( x0 }
56 ` u; I& ] W7 B9 S# O: K
67 B6 l- O6 i4 i* z! e
7% a- ]* y* _7 p, {7 T7 m' Q, E
8
' V/ Z M, y6 c+ L1 v4 V) ` 9" ~5 J- S1 w2 [+ y" u) C3 c
105 r0 o' c P! m( G0 m0 |" |8 s
11' s, V9 J- L4 G; s7 `
12
: a/ L+ Q1 r' q( h1 y # 第二种应该是报错object属性,然后直接进行替换
- o. a6 r6 e. @$ c5 f7 {# z- s! k df = pd.read_csv('data/diamonds.csv')/ Z V, k3 ]9 t: l/ E5 C
for i,j in enumerate(ls_cut[::-1]):
: E0 W0 `, i7 e3 ]2 b df.loc[df.cut==j,'cut']=i
. w- }$ N! i* }3 G: [$ j" e : \2 V' e$ c& Q: Q: [1 b
for k,l in enumerate(ls_clarity[::-1]):
+ |1 M/ m+ N! N. C, D' C3 u7 f df.loc[df.clarity==l,'clarity']=k5 _$ z/ Z: B" B' ]% s" [; p
df.head(3)* m5 m) R: _5 N. `, q4 y9 E: ~
' t4 L9 ^. t' M; ~4 E8 T3 ] carat cut clarity price {$ F; H1 n# Y0 x1 l
0 0.23 0 6 326
G |3 W0 y6 e8 l" Z 1 0.21 1 5 326/ H. _% v6 E+ ?
2 0.23 3 3 3279 N, Z9 u' P4 K2 o+ N
1
/ z% P! Q; i+ f 2
9 d8 e2 j7 [- | 3
& T; d5 \, w( |# P 47 n# ]& I' d$ y0 H0 ^$ b( w/ i6 i
5. B2 |: N$ f" T o
6
2 M' S2 f2 [2 i& x' i. |) f 7- j5 \6 E5 y7 a3 T+ T* C6 J1 J
8
4 E8 k4 D6 O& l 9% h5 k6 m2 s" W
10
9 k# K& z2 a3 `- Y0 O- v 114 `6 q7 ^6 y% Q+ ]: n8 @
12
2 X+ s/ z9 S9 z+ c5 ~3 q% F 13
{ s1 t4 z T6 U& F. [# d( Y7 P0 [ 对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。2 l- b6 U" _% k+ q3 G
# retbins=True返回的是元组,第一个才是要的序列,第二个元素是分割点4 p: d; K' u/ s8 [! e
avg=df.price/df.carat% s' t g5 w! W+ d, y
8 U# O" a6 A8 c3 `' a+ L+ C df['price_quantile']=pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],8 @7 l& Z/ K* m8 d7 B
labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]$ k. b- G% }& ~5 Q
2 |, r$ G% {* ~. @% ^ f, I
df['price_list']=pd.cut(avg, bins=[-np.infty,1000, 3500, 5500, 18000,np.infty],, |* z$ ~& O* O6 h. H
labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]
1 C% }" e( G: e df.head()0 M; u$ U0 j& y; M! o
( `. \/ o4 t% \. o5 B) n0 c carat cut clarity price price_quantile price_list0 j# o% @9 f' C' ]8 x
0 0.23 0 6 326 Very Low Low
3 N4 d4 I9 H4 \$ ^) C: V 1 0.21 1 5 326 Very Low Low
3 w/ O5 x8 e1 x" c, Z 2 0.23 3 3 327 Very Low Low, d" w5 P6 V @6 s! x- g$ H
3 0.29 1 4 334 Very Low Low
4 e) v! i1 U+ I! b1 ^: G7 U 4 0.31 3 6 335 Very Low Low
3 }: i6 p/ d' _! k- ^! j0 Z & V1 F3 w7 X" @7 V: b' o/ v
1
! B! ]+ ]9 S( c. J+ s( F: P3 k 27 D, W' M- A( K5 @+ x, S
3' `7 X' `( J8 i& j. Y2 y% h* M
4+ @5 m' Q4 k4 T2 |) ^
5, Y/ J( f; q6 \0 p$ Y5 c4 D
6
4 y- I" X& f, H$ J2 F7 w 7. N, Q- {% }6 i& P
8* i( d! o$ V0 Y# ]
9+ V: }( q6 G7 t2 v3 e* Z
10
/ ]0 }% X: k t8 v* j; V 113 W. v/ J& }6 i' N
12
) P0 F a6 b5 C- T 13
3 w' H3 j5 ]9 p' Y. V1 c 14
: ?3 C( S4 x+ s* ^/ q6 q$ Y8 o2 T+ s$ s 15; ]$ A, D# k: B5 }
16) _5 [) g, G& {
分割点分别是:
! a) h' j0 }* e2 q $ Z$ E! O$ u. F+ R
array([ 1051.16 , 2295. , 3073.29, 4031.68, 5456.34, 17828.84])
+ M; e, `! @5 o, ?/ N7 Q3 I array([ -inf, 1000., 3500., 5500., 18000., inf])
* g* w6 x5 u) x 1' h5 J: u+ E& A6 v5 ?8 O1 [
28 M' V+ t( V9 a& S: n
第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。) [. Z& W8 j: Q3 @/ b8 n, P
df['price_list'].cat.categories # 原先设定的类别数
- c! X1 w% m0 Y% a Index(['Very Low', 'Low', 'Mid', 'High', 'Very High'], dtype='object'). H9 l3 {) h5 e* g7 Q0 n
4 ]* j7 y1 z- s8 P df['price_list'].cat.remove_unused_categories().cat.categories # 移除未出现的类别
* V* S$ l+ R" u/ N1 ` Index(['Low', 'Mid', 'High'], dtype='object') # 首尾两个类别未出现# K. F1 @1 I. h% F4 O$ v. S
1
% e6 V/ z5 G; s# |( ?& v! } 2
1 V( s, m6 ~ d0 q4 A3 K 3" I( x+ q7 \' \3 Z0 B
48 ?/ T8 N* n- `; K9 u7 Y
5# R" r% U9 b) O$ _8 U% z& L
avg.sort_values() # 可见首尾区间确实是没有的
+ T% |4 J5 O! K! {3 R 31962 1051.1627916 h# [" v4 P1 [5 C7 G
15 1078.125000+ r( J8 D5 P4 q
4 1080.645161
9 F6 y. I8 d# K2 [ T) H 28285 1109.090909, j* t/ U7 s8 A% L0 t3 j% U8 T0 z
13 1109.677419. N& X- ?; g! {
... & f9 z+ C! h% T2 |- a4 [7 a
26998 16764.705882. h/ X7 G) l4 i1 q% I3 S( ?6 n s
27457 16928.9719631 l. Y" a: W9 l b& ^
27226 17077.6699030 P# `7 J# c& I2 ^3 S3 V
27530 17083.177570
* B f( ]3 `. [/ j, u 27635 17828.846154
" _0 u) p& v# U9 n; x( }/ p 1
0 d: n' l2 z. q3 U; {- U. R3 q 2 P. ^: k: o9 y/ x4 @) N
3/ E, N4 D" j$ Y
4
6 C1 B# O/ t5 V+ G' \6 ?- F" G: H 5
- ]; j8 Z+ M4 X: o 6
4 y R# O& ]7 U7 G8 s 7
% K$ }$ m8 e- D4 S 8
8 E2 L1 R f5 t 9
3 i& A) V$ Z7 \ 10
2 p: |* U1 ?3 r# _( w( B' { 11. D* H( A3 o# k5 e) a. q; H- y; c* ^
12
0 m T8 Q/ v- g, p( x X 对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。2 ^9 U, n: P$ C- q# q+ b' V
# 分割时区间不能有命名,否则字符串传入错误。& F. B2 J. ]3 S7 y9 K9 l G
id_interval=pd.IntervalIndex(
" a% T' ?( q0 M pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],retbins=True)[0]
* [; Q4 Q) E! e )$ V/ u: Z, K" b6 _, P b: w C
id_interval.left
3 i- ]( S5 r; j9 P id_interval.right
; c" k5 N9 n& B* v* b5 e G id_interval.length $ b; Q3 ]+ ^7 o. M0 Q
1
* A b* m. G( t+ D& J. X# d& k 2
2 M; ]5 p$ C) P: Z Y5 p' l, f6 u. B! B 3
: \( _* c3 Q& ^; U7 E 43 Q, H+ @2 {4 m1 z$ b
5
2 _6 C C3 p- l! Q 6* W: P4 l0 j; \1 P8 h: E
7) K6 d/ k* Z/ X% p2 f$ P! w
第十章 时序数据
* y& n/ b/ B. I& H% v; { import numpy as np4 ]) R, a/ M1 _
import pandas as pd& ^" P3 ^ B O/ e& L' \
1& M3 f6 p1 c2 u0 f7 F
2
7 t1 O. Q, A) O1 e# d
2 ~ Z. a5 t) k0 n4 |/ q - G* P3 |/ F2 ~! q
10.1 时序中的基本对象
' W% I' }$ i8 v; l$ \0 |4 f 时间序列的概念在日常生活中十分常见,但对于一个具体的时序事件而言,可以从多个时间对象的角度来描述。例如2020年9月7日周一早上8点整需要到教室上课,这个课会在当天早上10点结束,其中包含了哪些时间概念?9 R' x- t- X; y( w' \0 {
( t$ E. B* x4 Y7 m
会出现时间戳(Date times)的概念,即’2020-9-7 08:00:00’和’2020-9-7 10:00:00’这两个时间点分别代表了上课和下课的时刻,在pandas中称为Timestamp。同时,一系列的时间戳可以组成DatetimeIndex,而将它放到Series中后,Series的类型就变为了datetime64[ns],如果有涉及时区则为datetime64[ns, tz],其中tz是timezone的简写。3 @' ~4 f: ~8 E
- \- q( q" M+ i: _/ ]: n
会出现时间差(Time deltas)的概念,即上课需要的时间,两个Timestamp做差就得到了时间差,pandas中利用Timedelta来表示。类似的,一系列的时间差就组成了TimedeltaIndex, 而将它放到Series中后,Series的类型就变为了timedelta64[ns]。
3 ^; C8 E- d: a4 j. ]% G% t" T 2 l# E. C/ H% ]6 n: Y; E3 K0 x; ~! k
会出现时间段(Time spans)的概念,即在8点到10点这个区间都会持续地在上课,在pandas利用Period来表示。类似的,一系列的时间段就组成了PeriodIndex, 而将它放到Series中后,Series的类型就变为了Period。
1 w# |+ V1 w$ z$ s- E z' v
" Y2 k2 K9 ^3 L8 t# ?$ p 会出现日期偏置(Date offsets)的概念,假设你只知道9月的第一个周一早上8点要去上课,但不知道具体的日期,那么就需要一个类型来处理此类需求。再例如,想要知道2020年9月7日后的第30个工作日是哪一天,那么时间差就解决不了你的问题,从而pandas中的DateOffset就出现了。同时,pandas中没有为一列时间偏置专门设计存储类型,理由也很简单,因为需求比较奇怪,一般来说我们只需要对一批时间特征做一个统一的特殊日期偏置。
* }( A Q# Z2 b5 E
' c0 a/ ^, `' I8 d9 @: N" |0 f 通过这个简单的例子,就能够容易地总结出官方文档中的这个表格:
, B6 C/ K( B3 o- Z7 T, j " X$ G8 `" o, ?$ k1 T# }
概念 单元素类型 数组类型 pandas数据类型
$ k; A# C) }7 A* r* X Date times Timestamp DatetimeIndex datetime64[ns]9 j# b o( x; s8 S9 K* \* T) U4 I: q
Time deltas Timedelta TimedeltaIndex timedelta64[ns]
* H; y& ] k# ~' M* e1 V T Time spans Period PeriodIndex period[freq]
) |' O1 d, h. d" _/ |; q Date offsets DateOffset None None6 _5 Z7 \& [& M N# J5 x
由于时间段对象Period/PeriodIndex的使用频率并不高,因此将不进行讲解,而只涉及时间戳序列、时间差序列和日期偏置的相关内容。
) }% j' d$ l7 E6 d9 V8 } V
4 `" Y, K: p( P+ h3 g' ~3 { 10.2 时间戳
2 o8 `2 Z% Y' z 10.2.1 Timestamp的构造与属性
D( L. d! A- _( r 单个时间戳的生成利用pd.Timestamp实现,一般而言的常见日期格式都能被成功地转换:% O3 l7 C$ P- _8 Q: S( B* M# r5 k' k
: l1 N" J$ x1 _8 ~
ts = pd.Timestamp('2020/1/1')
9 V0 n% m) |! X8 w7 R* i + J6 \8 n, y) I1 T+ F
ts
7 ^, K6 E4 e0 A# y/ a7 o u' o9 _' { Out[4]: Timestamp('2020-01-01 00:00:00')
" ~' @' |3 D$ _6 w! X* e
' o G" c2 \# _ R' O+ J ts = pd.Timestamp('2020-1-1 08:10:30')
' s+ |' K4 W8 `4 t
5 g3 d1 j- h- U) a; [. Q! x ts
7 g: \( Q5 k9 |+ F8 _ Out[6]: Timestamp('2020-01-01 08:10:30')
( z, Q! g: M; w. U/ t% } 1" O h9 S, u R% J l
2
7 t; \( M+ Y2 {% p4 Y" O 3
) v6 l$ p5 U3 A7 v 49 N# ` ]0 ~# {; [* ` Q
5
8 |1 R/ |7 A# i/ Q9 O 6! _- |3 X! k3 j W% @' i9 H
74 }# j4 z$ O4 c' h
8) M& _2 ^; M- h9 E. T
9" Y, ], h4 H/ ]4 V
通过year, month, day, hour, min, second可以获取具体的数值:& y6 G B$ b: W L
1 M: R: L) E0 f. l ts.year
0 t2 P1 w0 h6 L. L' f4 G$ C- k, Y6 w Out[7]: 2020
$ u3 w% W' E( r4 `8 N , c ~# U4 t1 F" P9 X
ts.month
+ Z9 B. t% C6 y: Y Out[8]: 1
/ s1 C& e5 I& f$ S/ L + }# |2 U( P( D1 \9 T3 ^
ts.day5 I3 C# Z. x+ @) M% E
Out[9]: 1
7 i0 V) s, l; H1 g, T, P 4 W2 h; G$ p3 {$ w+ g- b$ Y7 w) X
ts.hour: F5 f6 |. j5 j/ r% N0 Z# y
Out[10]: 8
& d. H o' A' }+ L 4 [+ Q9 W; P1 a* I# K% R
ts.minute. F& Y- P. G& }3 J( l0 i
Out[11]: 10
! z$ h, K' k; m+ w* H) K9 A W& v$ k$ j0 J1 @: \% ?# u
ts.second
5 }* I4 U$ b, B" {6 e+ {6 T Out[12]: 30& k& {0 p1 B1 t
% L: A* s3 L- }" S8 _6 U) i
1# y/ F/ Z( g r( y- O! U9 G
2$ e6 c3 F6 L0 v
3
- C# c- m" t# |% _ 4
, Q! m! q, S( f2 }7 j0 r 5
5 w; v- v5 j' S, A( f% {% m; C- b# q 60 g3 F) l; _9 H5 `: c& j
7
5 ]* f$ `: ^! r: h4 y 8
) b8 {% K5 t. U7 ` 99 i# t! @) T+ @5 o$ X: l
102 [8 c+ J8 @0 A0 ]/ y3 t
11" P( Y% y* A( v# Z
12# q$ W/ h: W# P4 @& W
13+ @6 _' w8 [+ ~1 d8 p9 w; a& u
14
. i9 G& Z$ G. f p A 15& T; G' ^" }" q9 ^7 }
16: X' Y/ `5 s9 _0 p ~7 K
17
% G" }8 [& i, F7 z: C) B # 获取当前时间0 g3 x+ ]7 J2 g4 R: I" ?
now=pd.Timestamp.now()
0 D3 e0 M& _; `2 |! t( E9 P$ ] 1* w& s% b8 X+ p2 N
2
( P2 c |! y0 }( ~% Z1 K 在pandas中,时间戳的最小精度为纳秒ns,由于使用了64位存储,可以表示的时间范围大约可以如下计算:
, e8 N& Z* d# R6 H5 h) k6 { T i m e R a n g e = 2 64 1 0 9 × 60 × 60 × 24 × 365 ≈ 585 ( Y e a r s ) \rm Time\,Range = \frac{2^{64}}{10^9\times 60\times 60\times 24\times 365} \approx 585 (Years)
% l( r& w/ u+ H" C TimeRange= & B( n! L) F+ w& w3 ]& s7 w( E _. C
10 + N2 P$ Z; _4 }; u- ?
9
+ o3 u. B' {* V( y" Z ×60×60×24×365: j* _. Q" D* S1 _+ u
2 c M* Q; c) C" y
64: o% x' ?3 J0 W. n& |+ S
$ @ Y P, P6 H0 {6 h6 K
% h3 o' Q! s0 x; b
≈585(Years)) R. X( Z, h1 n a
) ]/ g2 t( ^# K) C: p4 X5 N9 j 通过pd.Timestamp.max和pd.Timestamp.min可以获取时间戳表示的范围,可以看到确实表示的区间年数大小正如上述计算结果:
& [9 S/ G2 l; }# x, U
8 `$ V* n0 u$ [& R& i2 C/ ]$ g: h; s pd.Timestamp.max
- j e' F: u9 b Out[13]: Timestamp('2262-04-11 23:47:16.854775807')& {4 L- P6 y% Z3 g5 G9 [
; h5 G$ l" b, J0 [
pd.Timestamp.min
8 I2 Y& m" _9 i# X Out[14]: Timestamp('1677-09-21 00:12:43.145225')/ e: @/ T# G7 j/ \9 B$ b
9 G+ J+ }, F/ @
pd.Timestamp.max.year - pd.Timestamp.min.year0 Z& l# b" G% X4 {* a6 C" r
Out[15]: 585+ @- C, c" f5 ~0 m: Y
1) p( V3 I+ l5 v# Z& D, g& [- [
2( J- w, t2 {5 a7 g
3
& C/ L- z( }1 y% |* Q6 V9 i1 i 4* F! G2 ?& T1 d. n1 {7 t8 L6 U
51 J4 X! Y0 Z0 p) L8 C
6
5 P5 Y# [# K, c3 _! T 7+ t3 W# `6 k1 m8 [$ |0 u0 g+ w
83 l: x6 D+ o2 O: ^1 D( Q
10.2.2 Datetime序列的生成
& Q6 P* Q, B6 M. s0 k' w) T pandas.to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, utc=None, format=None,5 Q0 u ~# b z* b1 ?
exact=True, unit=None, infer_datetime_format=False, origin='unix', cache=True)
+ w( X" P1 Y9 |2 m. G 1" f5 [' s: x$ b- n5 x3 o
2
: {; {- o) [- L9 r( Q pandas.to_datetime将arg转换为日期时间。. `- a- x% ^7 M/ v# o- P5 Z
# n* X4 W1 m5 j# X, c; {
arg:可以是argint、float、str、datetime、list、tuple、一维数组、Series、DataFrame/dict-like等要转换为日期时间的对象。如果提供了 DataFrame,则该方法至少需要以下列:“年”、“月”、“日”。' o1 ?- H# M6 O- M' h
errors:$ q |6 q7 F- W5 B( |5 M) ]
- ‘raise’:默认值,无效解析将引发异常' B1 @; F/ p/ ^; m1 G: Y
- ‘raise’:无效解析将返回输入 R3 x6 b( _% A/ k ^
- ‘coerce’:无效解析将被设置为NaT, f0 [% `! A/ E4 z
dayfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析日期,例如“10/11/12”被解析为 2012-11-10。如果无法根据给定的 dayfirst 选项解析分隔日期字符串,会显示警告。
, Z6 ^6 x2 @1 q- R! m yearfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析年份,例如“10/11/12”被解析为2010-11-12。无法正确解析时会显示警告。(如果 dayfirst 和 yearfirst 都为 True,则 yearfirst 优先(与 dateutil 相同)。)
# F9 A# C7 p F! X9 s utcbool:默认None,控制时区相关的解析、本地化和转换。请参阅:pandas 有关时区转换和本地化的一般文档
7 ~% R" k9 k/ c1 _2 X0 V format:str格式,默认None。时间戳的格式不满足转换时,可以强制使用format进行匹配。
1 {4 A4 t3 r9 | i5 e unitstr:默认“ns”。它是arg (D,s,ms,us,ns) 的表示单位,可以是整数或浮点数。这将基于原点。例如,使用 unit=‘ms’ 和 origin=‘unix’ (默认值),这将计算到 unix 开始的毫秒数。
% i# y7 Q' L2 `( _0 j5 ~# c8 ^ to_datetime能够把一列时间戳格式的对象转换成为datetime64[ns]类型的时间序列:
' A7 M' x' @* {9 W5 m" p& ? pd.to_datetime(['2020-1-1', '2020-1-3', '2020-1-6'])4 y3 f/ \9 @4 W6 p6 X1 _: E) l. d
8 M: _3 x: }6 K* @( z, k
DatetimeIndex(['2020-01-01', '2020-01-03', '2020-01-06'], dtype='datetime64[ns]', freq=None)5 E. I% X3 X" A5 L8 y/ a
1/ ^3 }1 O# T0 N2 ]
2
4 S7 Y% _5 s ?0 S* g+ G 3
; ?3 Q: z9 B) K, b 在极少数情况,时间戳的格式不满足转换时,可以强制使用format进行匹配:
8 @# K. @$ H% P: j( A 7 A$ }8 t. }, M6 i/ J
temp = pd.to_datetime(['2020\\1\\1','2020\\1\\3'],format='%Y\\%m\\%d')
1 E8 d1 S1 q H# z7 ]" O1 Z temp
3 \# j1 U6 k% T8 ~+ x" v* H
, R$ x/ M0 L6 {+ r+ z DatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)
% P* ]$ l7 t( m5 G5 x6 W 1: |0 J2 t, t/ Z& U2 u, B( D( M
2: c" h- q- f" `+ F5 e0 ^
3. f4 y4 q5 G# s( \1 v+ C4 g1 t8 T
4
2 c# ^( ?$ B {5 H- d( J2 B 注意上面由于传入的是列表,而非pandas内部的Series,因此返回的是DatetimeIndex,如果想要转为datetime64[ns]的序列,需要显式用Series转化:
( I+ [) @0 b( w1 {2 ~, f
# ^, S7 U4 A& p) N) J$ h, R7 A9 \9 G pd.Series(temp).head()
/ z4 T- \5 V' Z9 V2 X% v, M2 B2 K
+ `# y+ w/ e9 q1 T- a+ n 0 2020-01-01, ]; \2 D1 u2 r% r3 x& |
1 2020-01-039 g3 Q' o1 a# s$ V% F, t# I% |+ O
dtype: datetime64[ns]' U0 i9 ?" N) _* w8 u
1# a( i6 \' n( r- {, E" b- V
2( Y' d0 N- X$ v5 C" b* h$ E5 n
3- [0 v7 i" ]0 o* A
4
3 R+ J, _% L6 u; ^# o; q 5
9 N5 q; q9 y% K" f! \" Q$ G1 P 下面的序列本身就是Series,所以不需要再转化。& S3 |. D+ {# ]4 D6 m8 { J: _
/ \7 @, T4 e0 l2 ]
df = pd.read_csv('../data/learn_pandas.csv')/ m$ n7 z! n+ v8 H# `3 S1 a0 Q6 N
s = pd.to_datetime(df.Test_Date)' B# v0 P, n% q# y
s.head()0 `: _6 i& C2 q2 f; z
% `5 @) h: K& N4 H
0 2019-10-05) y N: i5 v9 \6 a9 C, p/ n6 ^
1 2019-09-04
- w; K' b: v9 e- C 2 2019-09-129 g! K1 ]' W% S7 ~
3 2020-01-03. A# h" N9 [3 Z; P
4 2019-11-06
4 L# F r) P& Z1 S0 x: F& C) A! |: Z Name: Test_Date, dtype: datetime64[ns]
7 P+ ]* p: R, g, \: L* g7 ^9 a 1# f0 j( I+ }& f Y: [* R4 Q, l9 j" j
2# J* }4 T B) A; F# ~3 D2 @5 E
3
0 D! A/ x+ Z, @- {( a4 k! V' N 4) {) g4 n7 O& B( O
5' g& P, J3 {5 V0 O. N
6' h B, U7 P* D: z7 G& ]. o
7# b: j' z( y$ s6 b3 \9 D
8
: G8 d* N% v* a- V 9
* S1 W3 |7 P) z9 N* y4 P2 `% B! B 10
5 e0 x9 P9 O* R+ c 把表的多列时间属性拼接转为时间序列的to_datetime,此时的列名必须和以下给定的时间关键词列名一致:; c7 x k2 e; n, u4 \
df_date_cols = pd.DataFrame({'year': [2020, 2020],
5 y7 _9 p3 u. l8 k$ D: \+ V 'month': [1, 1],
. g1 k& X# P+ ` s* {. y 'day': [1, 2],* o3 J* i6 Q: \1 ?9 A/ c3 e: E! ~
'hour': [10, 20],; p* o [' Z/ B) R+ r0 x
'minute': [30, 50],
+ N. f/ g2 W7 R& C 'second': [20, 40]})5 f! i1 t0 M0 G4 x$ Y; o* D
pd.to_datetime(df_date_cols)4 y* H- w( |# f) P' _/ \" b! q: P4 Y
$ m* G C5 Q4 ~0 \& U7 \. Q 0 2020-01-01 10:30:206 l+ q9 u0 U4 F5 u
1 2020-01-02 20:50:40
, W, F# e% l2 }7 _* _ dtype: datetime64[ns]" w& Y, {1 B3 J4 \
1' ~8 _' L0 ]4 A n) R" ?
2
! L) J0 [, M2 I1 {8 ? 3* N* E2 ^! _' w3 C9 v) X4 B$ t4 x
4
6 z7 @3 x2 f7 k; l8 S0 G* H 5
0 R% [% J' B" A' | H7 } 63 u0 S1 a, ~1 H/ z7 E, Z
7
# X6 N( n: Q( L3 s4 _+ y* z 8
& Q; T* f/ G0 N* e. { 91 o. z! N# M/ f; }4 c
10" F( H5 j7 b% V
11
$ q) g# O0 u! V7 e- y date_range是一种生成连续间隔时间的一种方法,其重要的参数为start, end, freq, periods,它们分别表示开始时间,结束时间,时间间隔,时间戳个数。其中,四个中的三个参数决定了,那么剩下的一个就随之确定了。这里要注意,开始或结束日期如果作为端点则它会被包含:& a8 H* D; \$ ?
pd.date_range('2020-1-1','2020-1-21', freq='10D') # 包含' x5 m7 r/ R2 s% N2 @( W3 l$ R
Out[25]: DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21'], dtype='datetime64[ns]', freq='10D')
( A* P% @) C0 h1 r9 W* _: M! @
2 k$ P+ l* a+ r# i* Q" R pd.date_range('2020-1-1','2020-2-28', freq='10D')
* L( ^6 ^4 J4 q2 Y7 t5 j0 m w. u Out[26]:
, ]+ U" n; h# e& l) T DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21', '2020-01-31',
1 S; r* U5 p: N '2020-02-10', '2020-02-20'],
, W& r, h* C9 c0 i dtype='datetime64[ns]', freq='10D')0 H, S0 e" I J2 q' r
% q5 q5 ~/ S$ F, j4 C pd.date_range('2020-1-1',! k! ?3 K; q/ @" T$ l$ F8 [2 d* U
'2020-2-28', periods=6) # 由于结束日期无法取到,freq不为10天
' W1 p3 f0 Q9 e2 }, K! e
0 S' F( S, |( v" v, m( ?6 E0 T. S Out[27]: % O& X4 c( A4 L' P ?: ?: i& T; [
DatetimeIndex(['2020-01-01 00:00:00', '2020-01-12 14:24:00',
' n! V b. X/ U( ?* c, G* K '2020-01-24 04:48:00', '2020-02-04 19:12:00',
. s) ]( ]; x( y; o6 N, E '2020-02-16 09:36:00', '2020-02-28 00:00:00'],$ O" Z7 V6 K! Y4 M7 x
dtype='datetime64[ns]', freq=None)
* E% O3 y& g3 d) J * N7 u p' P) e1 p0 }
1+ ]2 {3 P g$ B, u/ ~/ X1 y3 U: S
2- g- L5 R) C8 l2 B& X3 F
3
3 m8 u8 u( ?4 z6 W0 q5 x 4+ r6 E% F; Z6 |
5* O# _8 @1 w6 T: U S
6
4 C G; Z! A1 P) n! Z4 H4 h 7
7 [% G R4 \1 h3 H# P 8
4 |9 `7 o% B7 ~8 H9 b+ L" d 9) Q2 \! a9 A2 P
10
) n" A6 K. \3 c7 M 11
) U& y5 H [5 A6 W" q# s- | 12
3 S" }+ l; R9 C* E9 e+ a2 {' k9 s: @ 13
( i* q8 v% R8 g$ q% F$ j 14) a2 o* S# ?* K
150 r1 _# w# r1 x) `6 e- e9 V% @
16
/ {4 @' U- t$ `! |$ g! m1 ^) \ S 17
3 x/ ]0 D- D6 g1 `1 q$ ] 这里的freq参数与DateOffset对象紧密相关,将在第四节介绍其具体的用法。
5 q; j! H2 s& Q% U3 r* h; @
4 M1 p6 W+ D) {/ H4 u) u2 ` 【练一练】
0 p2 v4 X6 h9 n Timestamp上定义了一个value属性,其返回的整数值代表了从1970年1月1日零点到给定时间戳相差的纳秒数,请利用这个属性构造一个随机生成给定日期区间内日期序列的函数。1 O4 c' x, m7 _+ w1 v2 m
! x' }6 @: n5 b0 E8 j. t ls=['2020-01-01','2020-02-20']
( T8 K9 V6 {8 ~ def dates(ls,n): N* a) b- K0 A- [* }- P; u
min=pd.Timestamp(ls[0]).value/10**9
, Y+ Y. @9 E, \. L! S max=pd.Timestamp(ls[1]).value/10**9& a- [/ R. O( r
times=np.random.randint(min,max+1,n)
8 l+ i2 @4 w( c6 F! S5 X, K: E return pd.to_datetime(times,unit='s')3 H# ~- u4 V: h4 b1 N
dates(ls,10) * l* Q- z7 i- c; [) i
# s' L0 X- N1 j4 b; z DatetimeIndex(['2020-02-16 09:25:30', '2020-01-29 07:00:04',: G0 p# U& w e5 b% z
'2020-01-21 12:26:02', '2020-02-08 20:34:08',
: E( ~* J* B1 y. c% B$ T r1 Y: ] '2020-02-15 00:18:33', '2020-02-11 02:18:07',
. D+ _. ^" I; U- `& q( x& O '2020-01-12 21:48:59', '2020-01-12 00:39:24',
, K/ b! y( X7 P# J# u7 _0 q '2020-02-14 20:55:20', '2020-01-26 15:44:13'],
& w: n) y, y& ]. ]& | dtype='datetime64[ns]', freq=None)
J G9 @ z1 U9 x( g4 w 18 B6 s* I- {2 B
2. C* {' _# ]9 ~; n! q/ Y
3: F0 u" T# S* c9 Z! u5 k
40 X7 e5 i) ]3 k* J) j. c
5
0 _: @3 }" W) d/ y$ m! s6 [, Q 6# O3 M& K( d- ?3 J+ m5 u- K
7
( k2 [# h9 m' d' R& ? 8
7 {5 R' x& V: Q2 y. ?! Q" { 9
1 J- W! ]) E3 K4 E3 _# N' X 103 `5 s& O5 S! p8 {7 ~
11# t# o5 n3 Z U' P, ?- L7 A/ _
12
: n8 j7 c* G8 I1 w! y 13
* _7 s; A) H5 X5 w$ D. l6 a6 Y$ D 14
/ W, G6 S5 q6 J: b+ f asfreq:改变序列采样频率的方法,能够根据给定的freq对序列进行类似于reindex的操作:" l8 }% \* f3 J; Q3 m9 t
s = pd.Series(np.random.rand(5),
& o8 M4 q; J) r% Z( f index=pd.to_datetime([0 o) H* A% ^+ l" j+ W+ j H
'2020-1-%d'%i for i in range(1,10,2)]))8 }* D% y6 j% J1 V0 h
4 E; u5 c/ a- L# \2 c% ? a( _0 M
2 V7 {9 j) ~0 y$ o! j+ C s.head(): K1 Y0 I" p8 R# l
Out[29]: g7 r6 u9 }/ A. d
2020-01-01 0.836578+ @2 ?5 ]( P: F/ c
2020-01-03 0.678419
& r/ X5 _7 @/ ~0 y( l, F 2020-01-05 0.711897
$ J5 c C o; E. q% i5 e" U ^ 2020-01-07 0.487429
8 U" S* |4 ^/ { 2020-01-09 0.604705
, G* t; \) ~7 J6 O& g. f/ z5 U dtype: float64
( Y" V: X% n+ Y/ j `' j+ H6 q
" }4 ^" k- Z. v s.asfreq('D').head()- |, W: l. V2 r5 z% w! t
Out[30]:
- J$ c5 A: z$ X. o2 O Y5 V6 s 2020-01-01 0.836578 n* e7 Q2 \* W
2020-01-02 NaN- ]$ D) ], u! ?- ^8 g( q
2020-01-03 0.6784190 j# V$ Q9 }4 O2 E" A
2020-01-04 NaN+ l- X# ?$ G) v4 o/ [' D
2020-01-05 0.7118973 U3 y ~9 \1 o# l
Freq: D, dtype: float64+ w( p- ^3 A) |6 ?
6 E% J( `. a' P# o" t s.asfreq('12H').head()5 s) v/ T+ k$ T" m( \' C! A+ ~
Out[31]: # |! t) B( g" R8 G
2020-01-01 00:00:00 0.836578$ T1 E+ X: ` ]7 A, t* Q
2020-01-01 12:00:00 NaN
8 X4 K* c. q! b: z& R% M: k% R 2020-01-02 00:00:00 NaN
4 M: O$ j, E$ Q! Q 2020-01-02 12:00:00 NaN4 _1 `# c! A$ L
2020-01-03 00:00:00 0.678419
9 J! @. D/ [# h& `1 P) e4 U Freq: 12H, dtype: float640 D' f/ L# S# T& @3 M1 U6 p6 J
5 `4 Q: L# U9 E
1
( w) ^1 M3 n: m% Q7 @0 q! w 2& @# D! H7 `7 i; a7 Q+ w5 k* M% R
3
4 A. G1 x# L- | 4
& F$ o! R4 X+ W% y+ i7 C 5
Z! [- M% Q } 6
8 |& l: v: P; ~* b 7
, h1 Z% d8 l" r2 T+ k 8) l3 n( O; k* M+ ^1 ?0 \7 Z
95 {1 W# b7 I, V0 q @- `: l. @$ _
10, J' c& B% s0 c! K! v
113 ^- B1 X# N: U# E( W2 ~
12# H# Z" F$ [; n( k* P" m2 b- J, U
131 U9 X, ~" n" n5 P1 ]2 B$ e
140 V: Z/ D2 l3 d& ?8 y" C
15! y& ~4 ~/ `, s8 w1 o; M
16% s4 r7 j' K& X% {2 `! ~8 ~
17
" r" p6 _, u- [. O# W6 T 18: w3 G' l3 W0 i8 C1 K w& a, ]
199 |3 j# d. F) [& ^
209 R! }# c& i2 p
217 H3 P: K4 m( D0 V4 k) w( m
220 H( x: o& N0 ?5 Z, ?: {. M
23) l4 s- ~/ L. d+ C# I) A) ?1 T. r/ h0 h
244 p1 [0 u' G1 T2 G& p1 C
251 P+ e, m* q2 p% [0 \9 g4 f
260 i$ z# d$ m/ q9 z j6 H& t
27
! n, y) w. s5 k2 V 28
* t# \) P! n7 y 29+ s" _# B9 N2 |* ~1 O4 R9 L7 b
30
9 u( [6 t( z; B4 u- U# j 31
& l( R9 ]& r7 J 【NOTE】datetime64[ns] 序列的极值与均值, q. p0 `4 z/ c
前面提到了datetime64[ns]本质上可以理解为一个整数,即从1970年1月1日零点到给定时间戳相差的纳秒数。所以对于一个datetime64[ns]序列,可以使用max, min, mean,来取得最大时间戳、最小时间戳和“平均”时间戳。
/ V( n, j& u' U: C: y! Z
5 ?: Y* O) R4 w, h% M" V 10.2.3 dt对象
9 w+ i% n# R! U- g0 y' o3 Y 如同category, string的序列上定义了cat, str来完成分类数据和文本数据的操作,在时序类型的序列上定义了dt对象来完成许多时间序列的相关操作。这里对于datetime64[ns]类型而言,可以大致分为三类操作:取出时间相关的属性、判断时间戳是否满足条件、取整操作。
- o" [' _. x z! _+ ?
* i. |* P q6 Q7 d7 V 第一类操作的常用属性包括:date, time, year, month, day, hour, minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter,其中daysinmonth, quarter分别表示该月一共有几天和季度。
( ?& K# D. |8 K- m0 L s = pd.Series(pd.date_range('2020-1-1','2020-1-3', freq='D'))
. i) w2 k( i8 x3 s* [. z
1 X, w6 o& R! W5 h0 B' p) \/ r s.dt.date
2 @ ~) {$ k+ d- B# N Out[33]:
( s' u U6 `+ O9 k8 \ 0 2020-01-01& Z7 z& x3 S& ?( F. w. g
1 2020-01-02
7 ^; t% n* e9 T8 Z+ f7 B 2 2020-01-03* B# z- I9 q1 a) t/ K# K1 q
dtype: object. p- h, O! Z$ ]. Q( P1 E7 `
3 h4 V# u) T7 H$ N s.dt.time# s; e' _9 D$ b" A( |% K+ [' l6 ^1 }
Out[34]:
# \; q+ ?) o# _: C. g% f 0 00:00:00
' p9 t$ n' b; d$ A 1 00:00:005 {& Y( p% ^- V- `7 K3 [* j
2 00:00:00+ @; C- w7 H& V! i
dtype: object! w; N- [. R0 a% n& ~; T
4 _0 V( Y" C3 t- [4 ]
s.dt.day
* V) }" a# \" M1 O! m6 u8 y3 @ Out[35]:
. e$ ? z& S" t" Z1 M 0 1
8 e: S/ G/ n5 A* D! N& }4 i, I 1 2
9 H$ n$ e7 z; P5 ~+ P 2 3
$ V) l, m+ m9 U5 Y9 C5 i+ k/ A dtype: int64
4 h# u& I9 N* K3 G, y9 n / j6 G$ q5 `5 }9 R0 R, p
s.dt.daysinmonth
$ [, c- n( e# H0 t2 c2 V' ^ Out[36]: # o. E2 |2 i! g% H1 E3 E+ |
0 31
3 A! n% z% B3 W0 K9 a 1 31
S' O/ U# [: ^5 y 2 31
: `. s/ V- ^& x+ G9 O dtype: int649 r- E4 g( P! m" O, c
" i4 E. M) p5 w
17 q0 [# N4 X1 Y7 }( Y7 g8 c
23 l0 N/ u6 M! q) z' K" S+ L" h2 y) H/ j
3
" R6 L$ q/ ?% R9 K* t5 i; H! C 4, Y- m; f) c7 z1 v+ q' T
50 D( C6 S& i4 v
6
/ c( X( i9 P, ~, h1 M 7 \! j g2 F) ]5 F4 Y" b
8
1 |) s* }2 L1 Y2 z( U2 n8 ?7 Y 9
0 Q& Z% A, r0 d; u5 u- Z/ s3 Y4 Q 10% K9 w J) e9 o, q: \: o" }
11
- g1 p6 E. w8 _$ z 12( h# M4 x8 V o8 Y3 Y9 Y5 ^) J- K
13
1 F. ^ t" Q) V* K$ @) b 149 u* G4 n' I: {0 l8 W) a/ K" h* t
15
. Y6 [4 ~+ ]: a0 L; M- O1 A 165 i. E% T5 J9 g
172 C- S% u3 S& J3 ^! F; U
180 p" @- b/ V+ i/ h) E( \$ S& f4 r
193 q6 {; B: f) G7 C( R
20 j2 N' y) f( _/ }- S
21
( }# P& ?, h# P, T6 o( F 22
" Y6 t' ~& F% l M4 T8 u+ c1 S) H$ E0 E 23
4 {" U% ]( K2 Y+ p$ L 240 c. O7 r& Y+ v$ F% ^" j& y
25
4 N. Z5 Z |7 m 260 _. G# ~ ~5 w4 }$ R
27
' P/ D8 N; u) o8 t. o# b# [8 N$ A! X 28
" {0 q0 W2 _9 J 29& I% v; g+ i! f+ l
在这些属性中,经常使用的是dayofweek,它返回了周中的星期情况,周一为0、周二为1,以此类推。此外,还可以通过month_name, day_name返回英文的月名和星期名,注意它们是方法而不是属性:4 g/ D8 Q2 m* k/ l4 Y
3 o# `9 n( D& Y* K0 i s.dt.dayofweek
U0 f4 ~5 N/ ^' J4 L1 E; U* ] Out[37]: ) l+ Q# y( C' V3 t: f8 {8 C
0 2* l. R/ n% x4 ]; _, B T0 N) m
1 34 q! B) `& z* B: y* x! o4 ^
2 47 u' i% L# {' g0 r- Y1 @
dtype: int644 q2 o( L' }/ A
4 h0 j! e2 X8 B+ C, T( z: O+ n
s.dt.month_name()8 V% D f$ s& U* O7 l5 T
Out[38]:
7 K+ ~: w) B& ]$ F3 ] 0 January8 w ~( q0 {8 B& }+ `4 W! o* M
1 January
3 l% U6 Y& D2 I) s 2 January
5 F- } Q, k( S$ Z( ~7 g4 O dtype: object
7 `( l2 i; u2 `5 U+ ` , Q e9 W3 H7 M& z* i
s.dt.day_name()
2 B4 O7 p% N: N# b4 _& i' V Out[39]:
1 i, T- n. a$ ]& U. b 0 Wednesday4 L- r4 |# ]$ I1 b
1 Thursday
% H. s P P, w+ d6 v5 u. |& U 2 Friday/ h4 Y1 n+ b; k! T/ e7 V
dtype: object% a( }& e3 L6 R# ]7 m
' }! }3 ?7 f1 X' w, e+ k 1# w% n- b) \, u8 T/ |
23 y5 ], N& F6 t) P# r
3
9 h! l7 J3 U8 E. x 4) A" p; |, U7 Q4 _5 Y2 l; H+ G- E
5
% K& z5 p- U, u) R# I) A 6, w* `- ]! B6 ~
7
& H# e1 o; l2 }: A 83 e) q0 D+ z9 Q6 v; c
9/ m! |" Z+ o) I4 w5 B, m
10 ^5 ^% [7 _# W, ?. B' ^
11
! e% ~6 I# b7 S5 e+ g 12
) W* q; h3 ^6 I9 N6 b! p+ g! B 13% s* g) Z- V5 Q
14. }7 R- s% e6 Q6 i
156 F% M& f$ w1 N2 \, \% N
16
1 @4 Q' e9 m# F D/ q' W2 |- y 17
% P3 H8 G4 D2 U/ h- r 18! U& E6 U2 r: D9 N
19
9 E# J3 a% V; t* } 20
( I1 H, }8 \+ f4 ^- [4 g/ J8 i+ l 第二类判断操作主要用于测试是否为月/季/年的第一天或者最后一天:
4 u1 a8 b5 b( m% j8 `, i s.dt.is_year_start # 还可选 is_quarter/month_start0 @. d7 k+ e1 Y
Out[40]: ; y) c. s5 J# q0 ^" E# H( t% P/ r% H
0 True" _, z) J; M9 m' U& Y) t& b2 M8 O
1 False
* ]! n! c& O- l. f8 S 2 False9 A! Q0 B" ]3 U, G5 O$ f
dtype: bool# A k; I5 h- w- z3 a$ g
4 Y9 S# p/ M, _# E6 u; U
s.dt.is_year_end # 还可选 is_quarter/month_end3 K7 o+ I- c' g
Out[41]:
. D0 @. {" p) r2 B8 ^5 R* C7 Q 0 False* g6 r* y/ u I2 a1 U( r
1 False2 @' u# R# L" G x6 `- I7 i$ I
2 False
w4 J. `4 o& c0 w. A$ o dtype: bool( z( ] f/ t- f
1 L/ i- c! h, s" k
2/ c- u/ R6 P3 Y; {
3
3 h) k. F& z& o# C 4$ b L. x H$ v) s3 s' r
56 }" U5 s. { y {" g
6
+ [/ X$ ~: p1 z 7
' A5 O6 q. S$ Y: j! ?9 ? 8
+ ^$ h% [- p! K8 x0 D 9
( P" d- M! c' H# h 10
4 y; {% ?) S( @7 [$ s 11
+ r* H* C& s1 a2 s3 @. o 12
7 i! q$ Q r- z 136 D1 l, b4 S% `: v! d. A
第三类的取整操作包含round, ceil, floor,它们的公共参数为freq,常用的包括H, min, S(小时、分钟、秒),所有可选的freq可参考此处。2 K% n( J2 |: I H( `$ b+ P# L* g3 D. b+ S
s = pd.Series(pd.date_range('2020-1-1 20:35:00',6 F) N3 M; T7 b- `7 j# e
'2020-1-1 22:35:00',
2 o1 f. _( W2 N; ?4 f0 K freq='45min'))
/ x$ \- ^( B& K9 c 1 t* ~. k7 Y" ^/ M8 i( I
; l E: R# Z3 k7 ` A* L
s
8 u+ N! C" a/ O9 s0 f Out[43]:
" I) A% {1 p3 g 0 2020-01-01 20:35:00! ?2 w3 s0 i2 ?( \+ V+ `! ?
1 2020-01-01 21:20:00+ h: e. z" m5 M' K% r- J
2 2020-01-01 22:05:007 X- W6 a, }' v; U; n5 C
dtype: datetime64[ns]2 |& w3 N) ~ o# M' V$ V; ]7 D
. x" i1 G- m. N4 y; K) ]/ v: B s.dt.round('1H')
, V: G% E' K2 }1 D Out[44]: , G2 R) |& K0 I7 M5 J
0 2020-01-01 21:00:00- u: E B" a" ~ i) r# ]
1 2020-01-01 21:00:00
0 w& v% Q0 U9 z: _7 x* M 2 2020-01-01 22:00:00
6 h/ e: {, [6 r dtype: datetime64[ns]
3 `; Z R1 q5 }4 b % R1 v* V- U- v' Z/ Q
s.dt.ceil('1H')
9 L, `, ]+ ?) T$ b6 S Out[45]:
4 c$ N6 U5 u4 ]+ Q' U/ U U$ S 0 2020-01-01 21:00:00
$ P& G' \7 C. g6 m3 q7 f: f3 B 1 2020-01-01 22:00:00
7 d0 L6 x$ F w1 Q2 ?! F9 s 2 2020-01-01 23:00:008 d3 A$ a7 {8 f" y! j8 J
dtype: datetime64[ns]8 H4 a/ a" K) I0 X; z* Z
: I4 V, Z: p P- G+ j% v s.dt.floor('1H')- @, ]7 M& H0 l& J2 A& c
Out[46]:
# L, }) s4 t$ p) A 0 2020-01-01 20:00:006 e" Z# L, {5 ?0 l4 T6 r. l5 l
1 2020-01-01 21:00:004 V& g: j9 Y. o$ e
2 2020-01-01 22:00:00! R5 P* A! X- P2 `! s% j/ c
dtype: datetime64[ns]# s5 M7 v7 j5 u5 r6 m5 m; j
+ }! _% A& w# V3 l7 k9 F: j9 l( {
1
) j U- M$ u( E% X' ]1 z 2
: Y1 k* A& c# S- |" C0 f+ v 34 J: _3 f! Z$ j- v. c5 L
45 g9 u' L% _- N7 c9 ]) b
5' Y& P M d' G+ U: T
6! Q+ y$ V ]$ V% O% R! w
71 l/ _0 D2 s$ i) C5 h
8& Y6 f3 T( C/ Q: ^1 Q
9$ v) m9 N x( n) w' k4 h {
10
, h0 B: \) `- S/ n+ q 11# {0 ~; v( v Y
12- p) j" s* b7 }1 ^1 Z' s; P H, K
13
7 M& X, V. ?% _ 144 }0 i$ h! @' ?" I& s2 t8 l+ U
15! m( _" ]% i- @3 F }1 s7 d
167 F# c7 x# r+ @) D
17
4 O- b$ a3 F( e: O; e n0 N4 y 18
! z6 `* w8 f& J* }8 ?. }+ l' u 195 W/ b5 _( p4 u5 B, Z
20; l5 ~8 M# t$ ^6 N) S# n/ J7 E
21
% ?$ s4 y+ ` m( a$ e! ~; I p 223 E* V$ ^: q6 }5 E* G, o$ y
23# k4 q/ h B$ N" [7 r' b+ Q
24
u3 \, [$ \3 e) J 25* B5 k8 u6 |" Q0 Y
26
' }" p( g& T% k8 b( Q* d 27" d; p) A" J+ y* y* s4 r
287 @! l5 ^( _" ^
29" n4 U' m) y8 i2 [$ V9 k4 @% o( z
301 T2 c) H& A5 ~+ T& ]+ c2 i+ Y
31, V$ \7 |, x6 \
32
" l: A# [2 \" O- i) ?; K( a6 J8 d 10.2.4 时间戳的切片与索引8 z$ x2 I+ |/ F/ O
一般而言,时间戳序列作为索引使用。如果想要选出某个子时间戳序列,有两种方法:
6 @0 N. A2 R# s. F1 O) h8 T
) b7 u+ m+ D6 i$ m8 H# i 利用dt对象和布尔条件联合使用
3 [; p; L7 k0 p! u/ ?1 { 利用切片,后者常用于连续时间戳。9 Y1 q" ^/ Q5 j9 y
s = pd.Series(np.random.randint(2,size=366), index=pd.date_range('2020-01-01','2020-12-31'))
) m( f( F, Z g2 W& G idx = pd.Series(s.index).dt
) @) f& d3 F- F0 I2 N, J s.head()
* _" f# V& F. H4 m9 P) |
h$ C; W5 u9 Y* o2 \ 2020-01-01 0: d2 a I: _0 j( v7 Z6 l% A1 Y
2020-01-02 1
+ I% M+ I! P8 w0 o/ R5 T 2020-01-03 12 p/ m3 I. `$ X" d" F; i
2020-01-04 0
+ R9 P" k7 j2 ` 2020-01-05 0
% p0 k2 A# Z# G' p) `" A+ Y Freq: D, dtype: int325 r' c5 h3 }, H9 _& V
1$ j" d' r- s/ B+ t5 {
2
; `& M) C; _. _. q' Q- s' w O4 y 33 P% {: X& U* ~# R: ~( W
4( Z) N3 V8 \; n
5/ W4 ~$ U+ c1 I, C, u
6
6 R k, P: ^7 f* j 71 o4 p$ S! S; s4 `- k$ J3 M- u7 D: e
8: R+ l H* V1 h: [$ K
9
( U! r, J& _# y0 w; y. H& T 101 f& P% d' @# f& Z2 w
Example1:每月的第一天或者最后一天+ v. k. y0 y( a h
8 L# g( I$ V+ u {+ I s[(idx.is_month_start|idx.is_month_end).values].head() # 必须要写.values4 K- r. `2 J& u" E* d1 Q, F
Out[50]:
% r' Q; `& Q5 j3 @$ W% }6 P 2020-01-01 1
7 O2 N5 {1 d, [0 u+ g2 A i' i4 b9 R 2020-01-31 0
6 W- T# s$ D: ~( y3 u 2020-02-01 13 }# n" o# ~& `1 m0 d3 D; y
2020-02-29 1
& g9 X# s% U, f9 T" c: e! s+ n 2020-03-01 0
+ q: H0 x9 |2 y1 Y* p9 J1 {: U dtype: int32
4 ^8 d2 V6 L! j1 |, \0 X 1* N. D+ E. [. v) n9 X) d6 \- }
2
, C0 r- Y% g- i4 C 3
( b" T' W, g$ y6 X5 \8 H 4+ G3 F1 R# y+ d7 h
55 o% L* S' n9 R: r
6
% B/ l: U6 D0 X5 o; U5 ^ 75 b H9 D. n: v5 k# q% B
8* q; e, f5 i. y' g0 A$ s' @+ e
Example2:双休日) d V' W, P) `8 O4 x& Q
. z8 {4 d3 h" [) V! D1 u s[idx.dayofweek.isin([5,6]).values].head()
2 I: E+ k" a7 s% a! | Out[51]:
c, P/ y8 C/ ^& c* M+ M7 ^ 2020-01-04 16 k% L3 Y2 m: O M$ }2 o2 \
2020-01-05 0
) Q- n: C/ S: ^% K 2020-01-11 0% t- F1 `& ?6 m8 q& w1 Y
2020-01-12 1. ?, L( L; v0 b$ n$ j, I0 ]
2020-01-18 1
2 [- ]5 q; B& i$ J( q0 D, y4 u5 x dtype: int32. ]5 R7 M9 g2 o9 ^$ W
18 l7 Z# M O& g% P/ f
28 w1 U) E5 ~; S
3
3 u6 I' x; J) b. U 4
Y' N8 V+ ^7 Q$ @9 h2 E 5
! g5 F+ V: P6 F- R( g 6
- J' @) @0 P/ |: A 7
0 a# L: @% M8 r9 c9 I; f. U) j 8, x4 ~7 }8 y. @6 d6 U0 K2 S q" K( G
Example3:取出单日值# @2 H. `/ o0 R C1 g
8 X- a w) x g- x+ B, o s( P! u s['2020-01-01']/ o, C# r4 e- _( o' ?7 [! ?
Out[52]: 1
m+ T4 t# k; L$ V 1 Z ? C- V7 C0 v) \# r( R
s['20200101'] # 自动转换标准格式& D1 [" K6 [* r z9 e
Out[53]: 1
' u: p5 a6 P/ a0 ]8 x" X 1
7 i7 T: k# H( q n, f) A, q 2
$ Q; N% n3 ~, N g$ O( F: w 3* l3 F& E0 l5 K
4( m: e' q/ `; W( c; L+ R
5' L, N+ K. @ K" ^1 x" \4 p
Example4:取出七月5 Q7 A+ A# e4 F0 D
9 ~5 \( W& I! J" B' r s['2020-07'].head()3 j: j9 j' E; {0 Y0 j$ H
Out[54]: . j$ W) |" m- x. }! F- }6 _
2020-07-01 0
/ e" E7 Z) \& B( r% `# J) f 2020-07-02 1
& j) e. E- C4 w% V& R1 Z 2020-07-03 0/ W' C) ]; A) u7 R4 ^8 s( C
2020-07-04 0
) ]3 ?3 x0 C4 ^# a# L! r9 ] 2020-07-05 0' I# F- n+ X" v% r3 P& [" p/ p' E7 \
Freq: D, dtype: int32# q9 S, T6 J9 r% }' W
1$ Z, `5 E/ o/ L5 C
2, M& a: \1 V2 f* `& }8 v) n$ P
3
, A: u5 ?- M0 S9 x- o 46 [$ j, U l3 o% R" M! L
5
& U, q& X a4 F; l 61 W- L/ K; J) p9 D6 H5 \! g
78 x7 P- c1 z) C- s
8
" n# o- M! @! J Example5:取出5月初至7月15日
5 O, `) A( d- D( ~+ L- ` * A: @) f4 C' j% f6 p3 {
s['2020-05':'2020-7-15'].head()
/ h/ o/ q& Y" c8 L. } Out[55]:
2 d: j3 @. |8 t+ Z6 g 2020-05-01 0. x4 i+ }5 k9 _& Q3 D
2020-05-02 1) i& O& N* q' d6 H
2020-05-03 0
: j( `3 L( t* }. R9 q- i/ l0 M 2020-05-04 1
) U" }, x K: x$ L8 z3 l' E9 X- K 2020-05-05 1$ M* G3 ]* e8 j0 n0 l
Freq: D, dtype: int32$ r' t$ x& e1 t7 z: g/ [
8 O( o9 K o: Q6 Y2 N% \3 ]
s['2020-05':'2020-7-15'].tail()
3 d' c; p) I& R, ^9 C) |( b" X: t2 E Out[56]: p& U( n: d3 S, Q X
2020-07-11 0 g, O0 ~; J1 F$ E
2020-07-12 08 A N7 J$ f# E& C
2020-07-13 1
' y! P1 b$ l0 m1 Q) J! b( y 2020-07-14 0
/ H7 V0 p" r5 t4 E; @4 Y 2020-07-15 1
8 a8 U. Q3 s! K( F& R. {3 S4 t# R Freq: D, dtype: int32+ q- u# i7 C' \1 J) t: [* D# U& _
8 i8 \1 Y7 u5 C3 z- y( b' B. y
1
- Y2 d4 i) ^2 e0 X) r0 ? 23 A: i2 h/ K8 ], N1 ~
38 g& r% R v$ X
4
n. E3 w3 V' ]7 i. z5 t 5. H" u/ x# x# j; i
6
8 T, I" H6 }* D+ {- A$ c2 V 79 m0 m0 ~1 P, o7 t$ W
8' q3 y! B* d, _; t$ m- c
9
6 I9 p! h" u$ p# I4 W8 W# n9 f: J 10* `" i% z- P' C- j* n
11
( N! ]" ~) d8 [* q# a4 i" ^/ g 12
4 s5 t" M7 x5 f3 q; e 135 y( K1 a. a, Q2 f* |
14
9 g& Q( o k I* s7 |$ f 151 k0 s, W% M3 n
16
: Y. H3 Q U; v6 B; a* M4 @6 Q/ a. v 17& g3 C9 b) W2 U/ Q) Q& f; {% K
10.3 时间差9 G# R) A4 Q# @- S2 L7 |
10.3.1 Timedelta的生成* N) c/ S+ o! A [! N; b
pandas.Timedelta(value=<object object>, unit=None, **kwargs)
1 R4 L+ ?- o# e4 e( w n/ @ unit:字符串格式,默认 ‘ns’。如果输入是整数,则表示输入的单位。( t) O/ ?1 ^! A+ O3 p- O" d1 i* S
可能的值有:) c' H u8 B% c9 k. c( p
8 h1 s- b% U1 `3 i5 \2 v9 O ‘W’, ‘D’, ‘T’, ‘S’, ‘L’, ‘U’, or ‘N’
x4 W$ ?& K- Y: r ‘days’ or ‘day’
- C3 u% e8 r" v. M& p5 m3 @ ‘hours’, ‘hour’, ‘hr’, or ‘h’9 v. @- T: f2 \
‘minutes’, ‘minute’, ‘min’, or ‘m’
) C: p7 A9 F) u ‘seconds’, ‘second’, or ‘sec’
: z; m$ e7 {" l- ^& S1 F( d1 c4 ] 毫秒‘milliseconds’, ‘millisecond’, ‘millis’, or ‘milli’
8 T1 M5 O- @+ V5 n 微秒‘microseconds’, ‘microsecond’, ‘micros’, or ‘micro’
5 K% B. M2 W6 C, g: q+ l: I2 v 纳秒 ‘nanoseconds’, ‘nanosecond’, ‘nanos’, ‘nano’, or ‘ns’.
0 h% H4 s4 u& ], }- e+ y: ] 时间差可以理解为两个时间戳的差,可以通过pd.Timedelta来构造:: l+ ^+ b) y6 `* I7 P/ B
pd.Timestamp('20200102 08:00:00')-pd.Timestamp('20200101 07:35:00')& L& t0 V% M s! T; s- Z
Out[57]: Timedelta('1 days 00:25:00')
5 J; v9 @) Q7 |/ {1 l( Q" ^) A. _ Z! |( m2 ~4 {( B f# h7 P- S
pd.Timedelta(days=1, minutes=25) # 需要注意加s+ T& W/ I7 U+ @( O* }
Out[58]: Timedelta('1 days 00:25:00')' p$ o9 f- i3 K! [$ [' t
( P: I8 t/ Q9 {$ ?9 Z( o7 e8 i) X
pd.Timedelta('1 days 25 minutes') # 字符串生成
0 K: _7 N8 _7 w: C& O( m Out[59]: Timedelta('1 days 00:25:00')
/ } I) T% V! @# q
. ~! @7 g/ T; @0 G" v' n pd.Timedelta(1, "d"): U! l8 |' s3 B0 f v) D, J
Out[58]: Timedelta('1 days 00:00:00')* r3 M6 w- L, a! B; p, F( Q V
1
! V) b3 _/ a3 T$ V/ P# I" J$ { 2
9 a1 ?6 \( x* V7 V4 a 3
* ^. z+ c7 R7 S6 i 4& X O" ]! x8 V( E- V/ X# @
54 s( p4 ^$ l# E) d$ |
6
2 Y0 u6 [, t0 S1 Y0 I Q 72 E5 ]/ I* A* g6 I- A
8
! W+ L; g6 _1 _% E K' T 9) ~9 a' }7 t0 Q" v* ^- m, C% d
10
6 h. n# r% u/ \' `! A, p1 m 11
) w7 q( |2 v) \( K 生成时间差序列的主要方式是 pd.to_timedelta ,其类型为 timedelta64[ns] :7 R8 {! v" c% j7 l' B1 A
s = pd.to_timedelta(df.Time_Record) o) r/ ~6 a6 V
4 }% E3 K3 @/ k s.head()% G% R* Y, T; Q6 r
Out[61]: + B. j P5 j5 N' {4 e8 k
0 0 days 00:04:34
( C4 f: C9 S2 z* A4 z 1 0 days 00:04:20
- X" e1 Y# L. ^8 w 2 0 days 00:05:22
! t5 B% R' H- y+ _; c9 V4 O& j4 h, H2 D 3 0 days 00:04:08/ B. p- U6 H2 m( y" {* i8 Z
4 0 days 00:05:22
( f! V) \' P/ A3 F5 U Name: Time_Record, dtype: timedelta64[ns]
0 l: X R7 _2 w. G( L! _- P 1
( L6 D4 D0 y) V ]- s5 ?$ m V 2) P* k2 @/ I; j1 U
3
& H9 v+ E9 @% I! i$ |' R- { 4% s( e/ s: h9 v8 Z
53 ~+ z6 f. f! V( H: V8 P1 D! R/ s
6
, o/ j6 m Y6 Z9 _$ o3 J 78 l8 R1 K' T2 ?& g$ I& I1 k& z
8- u2 }1 a- M7 t8 }2 q$ [
99 Y2 Z: m: Q$ x+ G: L- o$ V N
10. H/ }* y8 U- }- `- m' W
与date_range一样,时间差序列也可以用timedelta_range来生成,它们两者具有一致的参数:/ l2 A8 \! v1 g. v* l1 D, D! _6 I
pd.timedelta_range('0s', '1000s', freq='6min')
1 d6 a5 v( E# `8 u, i Out[62]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:06:00', '0 days 00:12:00'], dtype='timedelta64[ns]', freq='6T')8 h$ ?1 L; n9 x; X/ a$ X% h
4 m" f# x: v, `- A, e& C% l2 b pd.timedelta_range('0s', '1000s', periods=3)$ Z. e {/ x: M6 x, b* }
Out[63]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:08:20', '0 days 00:16:40'], dtype='timedelta64[ns]', freq=None)
% o" b2 Z+ t# z- N) R 1+ C& }- i; _: |) B
2. {1 c, W0 k! V7 U7 D! `# D& j
3
$ _* n- Y6 f4 e2 u7 e/ S1 z6 H8 |9 O 44 q! x1 d1 H$ n( G8 d, ?9 j
5
: B& H8 K9 z! [) n* o! m9 ]$ J; H u. a 对于Timedelta序列,同样也定义了dt对象,上面主要定义了的属性包括days, seconds, mircroseconds(毫秒), nanoseconds(纳秒),它们分别返回了对应的时间差特征。需要注意的是,这里的seconds不是指单纯的秒,而是对天数取余后剩余的秒数:
5 h* r( d) |8 a; U3 G s.dt.seconds.head()
, o i2 i; @' J; o8 O Out[64]:
$ [6 r$ g2 M* {! P, x& g: U 0 274/ Y, v: {6 \* v
1 260
" K" T7 T+ S# }$ G% n- i 2 322# T c: ?0 g* f7 @$ w9 f1 e- d
3 248+ M6 x4 @ w% ^/ n4 Z6 L4 w4 i
4 322
! X4 J F# ~( e) @$ _; i Name: Time_Record, dtype: int64- p# B% U1 y" t$ i9 ?& A
17 I- r9 n; l S4 r' X
2
! T/ e' o! N& `& v! A 3. a, P% x y5 B" x# m
4+ j. |% t, f( @
5
, q3 [, {6 e7 H% h7 c5 S 6% P Y; f" T) m4 D5 z$ K( }
7
* r* r# H: D) F; V( z 8
, x! w8 c. x5 O 如果不想对天数取余而直接对应秒数,可以使用total_seconds) j* G5 ], F9 Q; u2 S+ ?( a
$ O, p$ U( W+ p9 B; e s.dt.total_seconds().head()
) a8 a; N; s- P4 [1 @3 }8 `9 q( W Out[65]: ; J" @2 ~4 q% i' a. ^; J
0 274.0
+ ]# J/ e/ T: p# O 1 260.0
0 r8 R: ?2 @, f# F, U 2 322.0- v- k9 O! @6 }( I. K% C' T
3 248.0
1 ]8 x& S5 N3 [2 P) z+ t5 z6 N 4 322.06 Y! _+ ]. n8 _/ D
Name: Time_Record, dtype: float64
+ E6 L5 L R2 @$ L6 T 12 a( e8 i. x, d4 }
23 @3 {4 m( j) y d! i% s# f( i0 }! i
38 _0 H4 S0 ^* n% L' `2 r0 y
4) b+ E% @0 C5 t7 a8 E4 D" o
5
: U5 g' D4 Q a$ B) k( ^9 @ 6
" b8 b# G0 B8 {, P- ?3 d1 { 7* L5 _( O6 q# W7 Y# u7 a
8
r" @, o: B. F$ z0 O$ c& { 与时间戳序列类似,取整函数也是可以在dt对象上使用的:
5 @7 b! y5 G* w4 y/ Y4 W5 Q
! y7 o2 z+ C5 s( Z: j. _! G$ n pd.to_timedelta(df.Time_Record).dt.round('min').head()
1 U, I0 r" G) S: S9 a) T- g Out[66]:
A& R$ z' o( j 0 0 days 00:05:00
, z2 E1 n9 f% \) P9 q5 S8 r3 c- O 1 0 days 00:04:00, ]: X; i9 F8 a1 W& p8 ]; @ `
2 0 days 00:05:00. H" J. H5 f; E h3 D( D
3 0 days 00:04:00
9 r! H4 }% U) i4 u5 L; Q 4 0 days 00:05:001 f6 E- z9 b2 u7 ?, h
Name: Time_Record, dtype: timedelta64[ns]8 \" c. ^6 d$ n* ]; N, K8 s. O1 f
13 p# j, f x! L# T
2
: S! G* ?. h4 c; g 3
9 l# T1 n$ M* G; L. B 4
+ n( G! C$ D/ v* F* w; s6 F 5- b- K; _' W2 I. t# a" s* L
6
B: ^5 E8 _* Z" T1 J. i 7! j: D5 ^; T9 z o1 P) `
88 X1 O/ F+ [- \! o3 c
10.2.2 Timedelta的运算
& s5 n3 `7 Z: F, e( y 单个时间差的常用运算,有三类:与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算:2 y, A& F, L. b7 p
td1 = pd.Timedelta(days=1), [5 `8 h8 A5 r9 |1 [$ i2 l2 S
td2 = pd.Timedelta(days=3)
9 E7 A9 H9 P7 M, d5 T5 L ts = pd.Timestamp('20200101')( r3 }/ ~. _# \7 E2 Z m2 Z' Y
5 ]4 x/ Z" K/ K# n9 \4 y* N8 R
td1 * 2! a' t$ H/ c0 O J0 j
Out[70]: Timedelta('2 days 00:00:00')/ \. L- @' C$ Z/ w$ B& D, Z; O
5 D+ [* d1 Z8 _- [( f
td2 - td1
2 G O1 K5 V# A9 z+ z! E9 a% O2 v Out[71]: Timedelta('2 days 00:00:00'); x: F: }, c/ f' B
% Z' Q/ C. L' ]4 U ts + td1
5 Y1 X* X& e, R+ h* E v Out[72]: Timestamp('2020-01-02 00:00:00')" i1 C8 L, }" U9 w
/ _5 }) f& x& z9 K9 ^" |% y
ts - td1
/ |9 _ b6 s! R4 s* O% w/ s4 O- E2 j Out[73]: Timestamp('2019-12-31 00:00:00')! w+ @. {6 m% Q" A$ \6 _' K
11 U4 L4 W6 [% Z0 U2 Z
2
: d( k. w4 O# a6 z5 u 3
: r4 E I% v4 F5 E 4
8 O! J Q" Y! p; H 50 p6 v. h! ], M5 k
6
' `* L* m- w3 a+ J& E! x/ e" [% s* w 73 j9 C D4 W% M
8$ Y. J# _- n& \: v* B/ h
9
* \. d6 z0 K" E8 T8 _5 ] 10. }# R, a% D/ w" R$ _
11
7 g" P: b& B% x$ M 12) Q ~' F# r( `2 v8 h
13% I+ i' t2 i( u: X' u) T
14
+ l6 e8 N( W/ P 15
0 {. d2 L( a( N3 _8 N 时间差的序列的运算,和上面方法相同:
. ]4 \. W6 Z& }- v; h: {* @ td1 = pd.timedelta_range(start='1 days', periods=5)
' C9 R0 |* k4 b- Y, j0 A' Y td2 = pd.timedelta_range(start='12 hours'," v; k5 y& J. n$ c8 \! B3 _7 e
freq='2H',+ D0 M" [/ ]( N0 }
periods=5)
! l# v" I3 l3 Q8 E: U! m ts = pd.date_range('20200101', '20200105')' C6 k! f+ n# a( F
td1,td2,ts
! D N7 `$ N! V
. L% o) W, s! q2 Q8 r& x TimedeltaIndex(['1 days', '2 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq='D')
+ _! w% M7 m1 M- U- M, o" t# _9 s TimedeltaIndex(['0 days 12:00:00', '0 days 14:00:00', '0 days 16:00:00',
, h% K9 K) B* a; z7 H; ^ '0 days 18:00:00', '0 days 20:00:00'], dtype='timedelta64[ns]', freq='2H')) d# X; D, r0 B `* j
DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',- p- _6 S" U9 e
'2020-01-05'],
+ T* C1 V d2 f dtype='datetime64[ns]', freq='D')
0 l2 O& a( W" g 1
# n# D( j! s% z6 f+ i 23 i$ G9 W/ y& U+ P" N
3
6 E0 O& a# |, j. n( ~' t 4' J5 U, b* I0 e- _% w1 e
5# w( h0 R* d; [* a
6; n/ X! {; B" Y
7
8 Z8 W z; \) z+ W# O8 k 8
5 x2 W7 m' C. J, g% n/ ^( F 9
' Z. `# |) W4 ~1 @( I 108 }/ V* k, Z8 u
11. n- W0 E9 o* V T* r8 _7 B
12) l/ u; x2 Z- G& \! f+ w
13
4 _9 C; c5 ~2 R td1 * 5; ~ f. }9 a" R' |
Out[77]: TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D')
; o: g/ j9 Y+ J5 p! l1 f0 Q8 ]0 x: Q) Y # c3 G- H* d1 F( v) }! H3 ]
td1 * pd.Series(list(range(5))) # 逐个相乘" c! L3 C0 D. O8 _
Out[78]:
/ i/ G5 I7 r6 M* T! ? 0 0 days
$ ^: ^ g* j3 c2 Y7 N8 z5 ^" v: L5 p e+ q 1 2 days& [9 R, z" F: ~; G
2 6 days
7 [- E( ^* x2 M* ~) } 3 12 days
# ]) t4 h5 b' H; l, |0 M 4 20 days
! |3 W% n; l! c# @6 R dtype: timedelta64[ns]
/ n2 ^' Q) d4 U3 r) A2 T- @
( U) w- L; K6 f6 C td1 - td2; A2 R: r' s, \3 `( f7 v
Out[79]: 4 |. \+ R" D" C8 ]$ U
TimedeltaIndex(['0 days 12:00:00', '1 days 10:00:00', '2 days 08:00:00',: j( r" @1 ^/ I
'3 days 06:00:00', '4 days 04:00:00'],% ?: _, ]6 c) I6 C' [- t4 D
dtype='timedelta64[ns]', freq=None)
5 t& i F3 Y( @1 Q+ W ' H. p- x% ?5 L8 m- Y) y) \1 G6 W% ]5 E
td1 + pd.Timestamp('20200101')
3 t/ O/ K% n0 Y0 { Out[80]:
% U' k% a6 Q; h2 F DatetimeIndex(['2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05',0 I5 k4 N2 B) [
'2020-01-06'],dtype='datetime64[ns]', freq='D')
( s2 h( n n3 m- Z3 S- j6 E6 y2 Z
0 l4 O( k2 k" y4 _ td1 + ts # 逐个相加5 G' l) d; Z H5 @3 g
Out[81]: . S( }6 z( m5 ]: V7 T8 b
DatetimeIndex(['2020-01-02', '2020-01-04', '2020-01-06', '2020-01-08',) ^0 q9 l" q; P+ ~' K( w& L; q) }% ~
'2020-01-10'],
. Z0 e: A( c) [ dtype='datetime64[ns]', freq=None): t- ^& v; T* k, t: C
- Q" L8 |& P( V- T5 } 1# q: G- N- ] W' O6 v/ B
2
8 D# ~- Q3 `4 f% s0 R" ~4 j 3
0 [! J+ K/ ^5 X1 v, i$ i" `% H 4
5 z) z& {6 ~1 H; b3 U+ ~ 57 S: V, k) J! `: g) ~" d
62 @8 l: M! l' P
7- u* m0 \, g4 s& f) N3 i
8+ o8 R' H$ i L" L+ s6 T% j/ ~* h. A
9& ?% D$ C: W1 n- Z1 z2 G
10
) l: F! N. Z" k# c1 J2 y* G0 ^ 11 \/ c$ S& y/ h' v
12& d. L( u" U# e/ }" g- z5 c" O+ X
139 \; I1 K' o9 {1 Q* Q
14
' ]3 U$ N$ L8 e8 G P1 U 150 S" B, N. [* c/ w
16
; v3 Y" [" e& G M* X# Y 175 a8 l5 @' s' O& c4 a/ L
189 p& V$ _# Z& |1 g
19: a& Z) X1 O9 x+ ] L5 g: m* ~
20
& K; V. n" ?4 h( k( Y' c4 `) K 215 R- i% j% l7 r5 \. @
22
" x& n0 }- z/ m/ d 233 G; Y- W( \4 v) k1 I
24
( i4 S- P" Z! U# s. B8 o4 ? 259 a: E. V [' O: B8 T
26$ y4 l+ h7 s' _: J* O9 H
27
& r$ g1 Q( J+ P6 G 286 Q+ c1 ~: i/ t; C, ]( U
10.4 日期偏置5 Z+ v* ?0 H) S
10.4.1 Offset对象9 `. J' k% ^( c; [5 E( {
日期偏置是一种和日历相关的特殊时间差,例如回到第一节中的两个问题:如何求2020年9月第一个周一的日期,以及如何求2020年9月7日后的第30个工作日是哪一天。
8 B8 t. d% B$ ?3 r# f ' p" j' y% Z4 i
DateOffset 类有10个属性,假设s=pd.offsets.WeekOfMonth(week=0,weekday=0),则:$ d9 f# \5 Q3 u" s
4 }9 C7 R, E4 L
s.base:<WeekOfMonth: week=0, weekday=0>,返回 n=1 且所有其他属性一样的副本
. j- S* L; w$ p5 @" \$ d( | s.kwds:{‘week’: 0, ‘weekday’: 0}
1 p3 d9 }) W- E$ [8 a( C1 e$ | s.wek/s.weekday:顾名思义
" S5 O) V s: b7 g5 J# B( x* U 有14个方法,包括:
# [% y6 o/ R; G4 K6 t6 c 5 l5 X u2 T' U; H3 A9 J
DateOffset.is_month_start、DateOffset.is_month_end、DateOffset.is_quarter_start、DateOffset.is_quarter_end、DateOffset.is_year_start、DateOffset.is_year_end等等。
! L2 m) L, U+ X" W5 _$ o pandas.tseries.offsets.WeekOfMonth(week,weekday):描述每月的日期,例如“每月第二周的星期二”。
8 [# m! m, J: ~( e9 Y% C
4 A$ O3 }% W% G9 q" G+ O9 W7 f; O 有两个参数:, @/ d4 s. o4 F8 V! N5 S7 v
week:整型,表示一个月的第几周。例如 0 是一个月的第 1 周,1 是第 2 周,以此类推。- s9 Z3 ]5 A: o7 }+ r# A
weekday:整型,取值为[0,1,…6],表示周一到周日,默认取值为0(星期一)! `, K0 M2 k- q& E1 ]9 Q9 S
pandas.tseries.offsets.BusinessDay(n):相当于pd.offsets.BDay(n),DateOffset 子类,表示可能的 n 个工作日。* N) M( M4 E5 l! r
$ J1 {* _1 I# _4 @+ t9 q pd.Timestamp('20200831') + pd.offsets.WeekOfMonth(week=0,weekday=0)% Q: }! n) ]1 l* K' b
Out[82]: Timestamp('2020-09-07 00:00:00')* v1 D0 s4 ? ^+ }0 {& P! y7 f
, i, q& S: [1 ?' {0 J) K4 S/ \
pd.Timestamp('20200907') + pd.offsets.BDay(30)% D5 O+ K) R1 w- P0 P
Out[83]: Timestamp('2020-10-19 00:00:00')# ?2 f' |: v' `
1% K$ ^2 N0 [. c0 }, I. B/ p( [
2; M% w9 i8 I* i# ~% J# G/ R
3
: ?7 ^: V4 i& O1 { 4
9 F( h5 |7 I/ V1 N/ `/ D. A7 b 5$ `7 M+ t7 F1 D5 s
从上面的例子中可以看到,Offset对象在pd.offsets中被定义。当使用+时获取离其最近的下一个日期,当使用-时获取离其最近的上一个日期:
( A M+ ]7 V j
4 J4 |0 L5 [ ?0 a. F) y" Z pd.Timestamp('20200831') - pd.offsets.WeekOfMonth(week=0,weekday=0)) q! J9 @& k: [" J, m2 y
Out[84]: Timestamp('2020-08-03 00:00:00')
* |7 K, y- J: ~ 6 `) v, E# c% |# Q# Z
pd.Timestamp('20200907') - pd.offsets.BDay(30)) a5 [$ z# X" `( T4 y
Out[85]: Timestamp('2020-07-27 00:00:00')1 i# J: C# W( a# o: g' l
8 `" j! G$ E- s7 o# l
pd.Timestamp('20200907') + pd.offsets.MonthEnd()+ J+ o; r3 X. {4 r3 i+ ~
Out[86]: Timestamp('2020-09-30 00:00:00'). n. q5 }. \4 [; @) p
1
9 }" H6 x0 U, N 2
- Z) ]0 B d: C! G, R* H 3 m3 L2 Q8 E! h9 c* L! J, @7 R
4- e6 L/ s0 u* k# B- T" M
5
4 R J" g1 h1 a 6$ i, x2 h$ p0 n, o9 B
7
1 D! [% x" z6 ~. G/ [# r+ N: J5 S9 M 8
- e+ u! h8 w; F% w+ g6 s. ], ~& l 常用的日期偏置如下可以查阅这里的DateOffset 文档描述。在文档罗列的Offset中,需要介绍一个特殊的Offset对象CDay。CDay 或 CustomBusinessDay 类提供了一个参数化的 BusinessDay 类,可用于创建自定义的工作日日历,该日历说明当地假期和当地周末惯例。
* g# t# A- a& a" K6 ` 其中的holidays, weekmask参数能够分别对自定义的日期和星期进行过滤,前者传入了需要过滤的日期列表,后者传入的是三个字母的星期缩写构成的星期字符串,其作用是只保留字符串中出现的星期:+ s6 h5 p) V0 t3 A
/ Z! R5 U% h3 w+ g my_filter = pd.offsets.CDay(n=1,weekmask='Wed Fri',holidays=['20200109'])
/ p6 D1 [$ x2 _( K% r! q, b: d B" c dr = pd.date_range('20200108', '20200111')( Y$ n' C6 J* b7 i( ^
0 \3 p7 }) b! S5 V' L/ _# N dr.to_series().dt.dayofweek- x2 A4 q4 m, k! X$ A( c, _2 l
Out[89]:
% X( b& U3 h- Y4 q5 ?; Y' G 2020-01-08 25 q) q+ M% p L( O
2020-01-09 36 ]6 A) E$ I4 K% F7 K% \0 q# ]
2020-01-10 4
& S R) I' Q; E' X7 ^ 2020-01-11 53 u {4 a$ X3 s, x5 L6 W
Freq: D, dtype: int64
; m# N0 p; O7 W2 W! F4 o1 a( F
- K7 }: p* V' k+ L6 A5 a# F$ j1 ~ [i + my_filter for i in dr]
! C# N: J% F+ j0 i+ K8 G Out[90]: - A/ X6 v/ o- Y0 T; t! Z; X% n
[Timestamp('2020-01-10 00:00:00'),- i; f: i2 [( G7 ]: S3 j
Timestamp('2020-01-10 00:00:00'),
) }/ s2 k; I7 ^9 w# _ Timestamp('2020-01-15 00:00:00'),
1 p$ ~; X0 g& B `) S. t Timestamp('2020-01-15 00:00:00')]
7 V, g: v/ ~+ a: i8 z. X
; h! `* l% Q5 X4 Q# T+ X% i 10 ~1 h1 H' a3 c2 R* g0 D; b
2
$ H# o+ }8 Y! c1 }! A 3
- j5 E& T( n. V, m$ y 49 v: A o( R# t! v
5
" Z1 `! B" {- L( W4 I( F3 n# p 6
3 y5 p1 J. H6 a/ L6 T 7! |& e4 B7 }) A0 x# U
8( T3 U5 A V5 N9 l. Y
9
; S' i6 n. b' g7 r3 W. R$ X 10
( A7 `! B; X3 R4 f/ s2 Y% s/ t 110 O# U# G# R3 H% t* M4 W$ \
12" Z) n P- G- G
139 k& ^, m1 T4 b3 P
14
: O( T& ~. O# ~ i- T 15
2 `6 N4 O2 r% K/ Q4 r. Z 16: ^$ ^0 x% c" r3 q& C
17
6 ~' x: Z$ [/ y$ R 上面的例子中,n表示增加一天CDay,dr中的第一天为20200108,但由于下一天20200109被排除了,并且20200110是合法的周五,因此转为20200110,其他后面的日期处理类似。
0 H6 J$ [6 d9 v; z, d2 _ ! b% g* X* {& g3 s
【CAUTION】不要使用部分Offset8 z+ D+ }/ a" }* x
在当前版本下由于一些 bug ,不要使用 Day 级别以下的 Offset 对象,比如 Hour, Second 等,请使用对应的 Timedelta 对象来代替。# ~9 ?" ?6 w# W" D( X) T1 ]
% C* q0 m/ C- f1 P7 y) w& k
10.4.2 偏置字符串
1 q% C1 x2 x; e; ~; I, [0 l: O& d1 u 前面提到了关于date_range的freq取值可用Offset对象,同时在pandas中几乎每一个Offset对象绑定了日期偏置字符串(frequencies strings/offset aliases),可以指定Offset对应的字符串来替代使用。下面举一些常见的例子。
+ ^! v0 r) E/ |0 D% D
( ^- ~! P [" ~. `2 U Offset aliases:pd.date_range函数中的freq参数,为常见时间序列频率提供了许多字符串别名。 也称为偏移别名Offset aliases。偏移别名列表点此参看(大概27个)。
* S, x! \2 U- y! [2 Y 1 v, ?# z1 b, A( j/ ^4 ?& ^
pd.date_range('20200101','20200331', freq='MS') # 月初
T6 R* Y) c7 ?; ]- h+ A Out[91]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')2 @7 {3 M4 ]" \* q2 r% L+ H- k& k
. g: N9 N% y6 L2 p7 ~9 c0 \- y pd.date_range('20200101','20200331', freq='M') # 月末% ]9 s/ ?7 I2 p5 R0 P) D, g
Out[92]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')
. K( w$ D6 X# U0 Z! O; R * I' y, W' @' d4 r, v6 Q; S
pd.date_range('20200101','20200110', freq='B') # 工作日
6 e6 p5 {: g$ k Z. f! b Out[93]:
$ y7 P. @* V/ S/ V, Q5 r1 s/ H7 @ DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
, ^' X! y z; G8 g5 s9 f0 l '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],
0 F3 b% x+ `# m K8 U" {$ x dtype='datetime64[ns]', freq='B')
1 @& H8 N0 U2 ~# c, d 9 \+ O$ C* p0 {/ p0 ^& P3 j
pd.date_range('20200101','20200201', freq='W-MON') # 周一
1 V# L, T! P/ A' | Out[94]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='W-MON')
1 z v1 t& Q6 l- c( [# i7 R4 W* w
: b3 Y, P9 y% ^% o0 x: o: R pd.date_range('20200101','20200201',: E8 p5 L8 T6 l+ Z! |1 x. N- d
freq='WOM-1MON') # 每月第一个周一1 B' V4 N& b) Y/ d
1 T" `7 M! B! X% m3 e) G Out[95]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON'), }" I+ x% U9 s, b2 X
/ ^( L- @. N: |$ K9 N# T3 |/ z 1" s5 l# v w' x. j7 k! ~
20 ^8 J( J" p) Y+ v! b4 _% x
3
. s9 ~1 `- I7 `& t" h8 B0 [ 49 Y( `, Y+ E5 Q
5
+ J9 t& ]' P1 p- u2 Z" O% o/ R 6
& U8 l5 H; {7 T$ q/ w 7' c$ B2 G6 P- k/ v3 n0 D
81 U" A1 a1 Y# I$ @0 g" h. C
9
5 o( i- P' }: N3 | 105 c* ]0 H8 J5 Y1 X% Q% A
11
& F, `, |/ l: ?3 p& p7 e- L2 H 12: F; z5 X4 `8 p# T3 z* F
13
- o/ g8 c" o K 14
5 V2 g Q, h- H# L 15) `! ~3 u& G1 \! T2 }6 G- g* ]
16
: y) V# X+ ?9 k) N3 E$ c* n9 s 171 H/ ~- c' C' E. h7 E9 G4 ?
18" ]; C! m2 S) @6 H( u
199 E5 G' N) G1 P' l. W
上面的这些字符串,等价于使用如下的 Offset 对象:
& n- b# \4 y4 n) g
( o ]% d- P! _: [9 R pd.date_range('20200101','20200331',! R8 a( b3 x9 K9 R. w+ T
freq=pd.offsets.MonthBegin())
+ [1 [7 U( Y) \. U: Q4 i. A6 D % r: `( K2 i j. L0 F
Out[96]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')
$ ?7 {5 ] ~" e e1 y( e1 J
4 I7 s: z' M3 c* Q5 B2 l pd.date_range('20200101','20200331',# _; _5 _# D+ I) S
freq=pd.offsets.MonthEnd())5 Y. {/ i; @0 Z
7 K8 X: Z3 x' k4 T Out[97]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')+ I i8 c- v' V- W) H6 S
$ C; j4 ~5 l! E2 B1 y9 M# a6 [! A
pd.date_range('20200101','20200110', freq=pd.offsets.BDay())
! B; {* D: P& _' W7 V& e4 k+ V2 Q Out[98]:
/ t. L% T$ D2 o2 b4 Z/ k3 O DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
2 g8 V1 H3 ^6 m; V '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],
" j& C( S1 _: `4 p/ h dtype='datetime64[ns]', freq='B')
! `8 j* Z* j2 _ 3 h% q4 f; `5 n# l3 ^ R% [
pd.date_range('20200101','20200201',8 ~1 K6 u5 E; }: B0 {. d/ U
freq=pd.offsets.CDay(weekmask='Mon'))# H+ G4 T" C6 ?1 O
4 N% d6 h0 f: z) ? Out[99]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='C')
* L/ k7 {- D; }
* \* V R* a. }9 ? pd.date_range('20200101','20200201'," d8 C0 X. e( W# A# f* ?
freq=pd.offsets.WeekOfMonth(week=0,weekday=0))( n' o; F" C9 G5 c+ ?
. {* z! \5 f$ L$ I, k$ O- y' \! X
Out[100]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')( ^; R5 k, S3 S8 {
& I& O# X6 B0 G8 I 1# q0 D4 m- a5 [
2
+ ~9 d& ^% m( G+ h$ ` 3! y {7 K) T. O4 N! {$ w# |8 E
49 y( [$ v$ |# e' Y8 K, c% P/ \
58 X5 h* X! o! }" G
6
9 S0 f9 w' ]' t- h$ Z/ Z 7% f9 B: u c4 C9 w9 Z) c5 U
8( g0 X4 ^# K8 G7 e+ O- b# o |
9
6 e; K( p7 Q K# t; s) ?& J 10/ W* X2 y3 S7 i, X
11, ?9 k+ c% ^- ]5 L2 G: V' h1 x4 N# h* ^
12
# D6 K2 Z) J0 g k' a4 ~7 m/ ~ 137 O1 y8 v5 h" |: _
14. Y f! u4 Z5 s/ m% j
15
( u( ^) A4 N/ A' c 16+ e! S" ^$ K+ P+ i
17' _) L1 @( D U/ l1 i
18
; h6 V2 ^4 A* l2 c t3 D2 o+ a! s 193 t# g6 q# F0 P7 ]% i, U
20
8 o- n1 S# k7 \/ s. Q 21 j7 L% C" } j, a
221 ~8 ~) p8 {5 u% F3 i+ I+ l' e
23- Y! ?# {* k# c4 z1 S7 o
24
2 S- S! B3 `4 _& K1 e( A9 M, S 25
1 K3 f! `0 _' S& [. x% @; p 【CAUTION】关于时区问题的说明
! m$ r9 L6 {5 W8 b1 S2 E2 O& B 各类时间对象的开发,除了使用python内置的datetime模块,pandas还利用了dateutil模块,很大一部分是为了处理时区问题。总所周知,我国是没有夏令时调整时间一说的,但有些国家会有这种做法,导致了相对而言一天里可能会有23/24/25个小时,也就是relativedelta,这使得Offset对象和Timedelta对象有了对同一问题处理产生不同结果的现象,其中的规则也较为复杂,官方文档的写法存在部分描述错误,并且难以对描述做出统一修正,因为牵涉到了Offset相关的很多组件。因此,本教程完全不考虑时区处理,如果对时区处理的时间偏置有兴趣了解讨论,可以联系我或者参见这里的讨论。
8 v j( s- T; {2 G9 I6 g 5 {( D8 j5 V; F
10.5、时序中的滑窗与分组9 E0 P3 w% h5 H% M1 ^3 g7 q6 T
10.5.1 滑动窗口. ~: f- v7 l1 f2 J/ ?4 q
所谓时序的滑窗函数,即把滑动窗口windows用freq关键词代替,下面给出一个具体的应用案例:在股票市场中有一个指标为BOLL指标,它由中轨线、上轨线、下轨线这三根线构成,具体的计算方法分别是N日均值线、N日均值加两倍N日标准差线、N日均值减两倍N日标准差线。利用rolling对象计算N=30的BOLL指标可以如下写出:* G5 x6 S3 o9 L% v* S0 d
V, q3 r# X2 N7 d9 o* T import matplotlib.pyplot as plt [& [* ^$ a- O
idx = pd.date_range('20200101', '20201231', freq='B')7 d8 ]. c$ I0 L% E4 e) F$ P: X/ ~
np.random.seed(2020)
8 r+ t8 z4 Y3 w; { : [+ V4 \: N9 {" F$ y. G6 B( G% g! d
data = np.random.randint(-1,2,len(idx)).cumsum() # 随机游动构造模拟序列,cumsum表示累加8 O$ G" L; Q. e3 E' v! c3 q5 u8 B5 m
s = pd.Series(data,index=idx)
' {% ^ U2 }4 D6 u% {1 T4 L s.head()
V) A1 V _. F) j Out[106]:
+ b0 r/ I6 S' p3 c# A9 @ 2020-01-01 -1! j" f& Y+ F: k6 j
2020-01-02 -2 u" F# v" ]( z+ n/ U
2020-01-03 -1
# |% |2 C+ X) g; Q+ ` 2020-01-06 -17 ?1 q7 M7 E! k
2020-01-07 -2
& Q5 ^* [* q8 F6 Q# K n# T Freq: B, dtype: int32
1 w0 G) X8 S. ^ r = s.rolling('30D')# rolling可以指定freq或者offset对象
& s' y8 z: P9 u2 ^* o/ f$ h
6 n5 c! M$ U4 z G plt.plot(s) # 蓝色线3 `3 x0 S7 z, L. Z
Out[108]: [<matplotlib.lines.Line2D at 0x2116d887eb0>]% ^! F. Z% L" u* v" C6 [9 j9 d' [8 H
plt.title('BOLL LINES')
* C1 X5 ?) u, `+ S& a Out[109]: Text(0.5, 1.0, 'BOLL LINES')$ p8 |5 H/ }5 G5 A4 v
' w& l. r$ O; M
plt.plot(r.mean()) #橙色线; c: q: W O. H. Z" }; S
Out[110]: [<matplotlib.lines.Line2D at 0x2116d8eeb80>]
* L( Q- \' [' U5 P' u1 z - P) p) b- G2 |$ O2 s( R
plt.plot(r.mean()+r.std()*2) # 绿色线9 q6 d' I3 X1 p0 ^7 B
Out[111]: [<matplotlib.lines.Line2D at 0x2116d87efa0>]
% }5 z! p) H! |0 K9 ?- {5 u0 q2 K) x , @9 o; c- F' e8 T' [ H, w
plt.plot(r.mean()-r.std()*2) # 红色线7 n. R& @" n; t( N& h4 ~& e
Out[112]: [<matplotlib.lines.Line2D at 0x2116d90d2e0>]
( S8 p" h2 H1 T% H% Z$ `6 L4 g" R: q
* d1 X: _1 d9 g 16 e. \7 ^! J( N' X `' m
2
( z* w5 v; r+ Z4 B/ f 3
1 b2 j% x& J9 f& D% N. w/ r0 d 4
/ n1 C# c p! a- E1 {6 a- Q3 \ 5
4 [" R0 R5 v" X4 _+ j: a; } 68 W! d9 I" u' u: w1 l M! p5 `0 a
7
/ o" a7 l% G5 v7 v: ^, @+ p 81 P4 j1 q: c! i% S
9
2 d1 M: A" M( Q/ `" N/ Q1 R8 @ 10
' g& C6 c/ I- {) s+ Z 11
2 J: Z3 T' F8 ~2 @* r) u: } 12% k& w4 @5 u7 C7 A0 _
13
- V4 v: Q: P2 r% g& m 14
1 L0 m k2 r t3 ^$ B. W 15
" ]6 `9 `* l2 |$ h% @ 165 |" P9 r5 P' y( _& q
174 s$ }1 O1 t, [) W
18" C7 i1 T" ^8 d) G2 f7 i% R
19) E/ r, q- F* y
20
, K% j) C3 {9 V6 o' H* w) E/ }# I+ f 21
. f* k* `4 e% w v- E/ n- m3 } 22 h7 Q( J$ z% w$ F% n1 F
23
' E' r/ ]; B1 ~ 24
; U" N( h3 T( d( Q( e( b' ^; D 25+ W) O" ~9 {7 ^
26
* {6 a7 j! n; i% z5 p3 ^ 27
5 p, R2 x q+ v5 `, [' k9 } 28; n2 U+ Z1 Q! ?; v
29
" `/ ?! s/ j5 A( w1 Q* |2 x+ p' x 2 v( {1 P5 J4 c5 G
这里需要注意的是,pandas没有实现非固定采样频率的时间序列滑窗,及此时无法通过传入freq字段来得到滑窗结果。例如统计近7个工作日的交易总额。此时可以通过传入多个函数的组合来实现此功能。& G; L: X& M& S3 ~
首先选出所有工作日,接着用普通滑窗进行7日滑窗加和,最后用reindex()恢复索引,对于双休日使用前一个工作日的结果进行填充。 H- _! u. R1 Q* M" w# u# m
* r9 M8 i# \. y: y
select_bday=s[~s.index.to_series().dt.dayofweek.isin([5,6])]
, b( w o7 r( W# e bday_sum=select_bday.rolling(7,min_periods=1).sum() k/ L) X/ n7 M5 { }+ X) |
result=bday_sum.reindex().ffill()2 o% ^9 p+ D! M7 y4 ]( U
result
; E5 ^6 p* X {$ M
, F9 I+ }0 @1 m0 }6 _ 2020-01-01 -1.0
- T6 g3 p$ R' H+ e2 z 2020-01-02 -3.0
! w& a- X( A8 V8 t# Q5 \# f 2020-01-03 -4.0
# D0 U$ S9 } \( z7 [9 U 2020-01-06 -5.0. x# I `% M0 }+ x9 S8 m
2020-01-07 -7.0
* c) u) n" ]7 I/ N/ y5 X! H ... + n6 E: g2 c9 M) m
2020-12-25 136.0- |1 ]$ b% u. u* L
2020-12-28 133.0
8 |) O6 x2 \6 C; ^ 2020-12-29 131.0
8 x0 [& O9 T0 J2 H: r 2020-12-30 130.0* }( i9 C/ D1 U( t" {, ]% ~
2020-12-31 128.01 A# F' r7 s9 y6 k' G) P
Freq: B, Length: 262, dtype: float64
( R9 [+ n% w6 T! u) i: q
2 b! ^4 t. y: t; f( o, ] 18 R) ]9 ?% y6 u9 I
2; A" F. U5 S; t7 U/ j
3
) g# J9 P, f! ~% Q2 M 44 i9 ^& k4 I/ Y8 A* |) _9 A( ]
5
N6 i; C0 x1 @8 f c4 l 6
# A3 I, w0 O6 B. ]& W+ h 72 ?/ K. k4 z' D
8
9 f# Z" N, u- I. n% R3 C: ^ 9( a p g- T+ k8 w
10
; R' {- g2 f5 z3 f 11
5 r0 ?9 @3 V" W- p# H0 ?9 n& ] 124 {& @6 I; U0 F3 h" K5 P
136 v. B% v. L" e
14
4 e8 y" b: G- A3 v; h0 F 15' ^$ @ J0 [1 a& d9 C. ~* c
16) x7 W8 c3 ?% N K9 t
17
; o( e0 t1 }/ ]" S shift, diff, pct_change 是一组类滑窗函数,它们的公共参数为 periods=n ,默认为1,分别表示取向前第 n 个元素的值、与向前第 n 个元素做差(与 Numpy 中不同,后者表示 n 阶差分)、与向前第 n 个元素相比计算增长率。这里的 n 可以为负,表示反方向的类似操作。
. I' c( \. P; g4 \" i ; x. x* f$ L6 z4 j" ^0 f
对于shift函数而言,作用在datetime64为索引(不是value)的序列上时,可以指定freq单位进行滑动:
% `! c+ `5 B7 p1 R) n3 g5 G ; h( ~0 f: K% p6 p4 x9 q
s.shift(freq='50D').head()4 T4 ]7 l9 Q0 o9 ~. Q
Out[113]:
$ V3 Z* T k% z3 Z) h( ?, [6 ~. @ 2020-02-20 -1
6 W* ^4 {6 k- p5 P 2020-02-21 -2
, [( [+ F6 ~9 K4 S B 2020-02-22 -1
7 J+ n. m8 v! P. _ 2020-02-25 -17 m; S1 i8 K6 A" ^! _
2020-02-26 -2
) s. ]& I& r, m: x8 J' h0 ~ dtype: int32
' s7 E6 l3 Z: |9 y 13 i: n; ]" F/ x1 F1 \
2
6 q3 W$ D8 k0 G/ i- h: e* ?. ` 3* ^. H$ K O0 B0 _
4
+ l+ A/ I( `3 l8 B% `; P: E9 t; V 59 w9 E2 s( }' ~4 r. A/ P
61 C+ X# S" h- Q3 N: W Q5 c
7! } B+ l ^' T, I
8
. M, c& Y/ G! X( L 另外,datetime64[ns]的序列进行diff(前后做差)后就能够得到timedelta64[ns]的序列,这能够使用户方便地观察有序时间序列的间隔:
5 g3 p7 e- Z) k8 i
+ e% i# y- P# {+ q% i" P5 P9 W* M my_series = pd.Series(s.index)4 Y! I# g: ?5 N) n9 w7 |" @! i
my_series.head()6 m4 M0 m+ Q F' E( G( F
Out[115]:
( d: L% c* E$ \2 F- m 0 2020-01-01
$ b/ M: g7 x8 S 1 2020-01-026 Z& L/ \; E4 _4 Q3 E6 Z" |
2 2020-01-032 T ^* K' g! t x8 }' X: y
3 2020-01-061 U% E0 A- G7 z; [; F3 g- ]9 x& a
4 2020-01-07- C$ q3 }" h) p, a- O+ t: ~/ ~
dtype: datetime64[ns]5 W8 |1 c6 F9 w1 }" @. a% k
+ d! W9 W8 q0 |
my_series.diff(1).head()+ S4 y% m6 [8 I- r" k6 v
Out[116]: ' _3 N0 L7 K) [' e
0 NaT( p8 {, g. L0 |; h/ K& D% n& t# R
1 1 days
2 B6 [1 R( l9 a7 i6 {6 u 2 1 days
3 u) b* x" G; b! R 3 3 days
. O$ x& |* S7 x/ }5 v, H5 i 4 1 days7 M1 v5 _2 T4 M1 F6 V; q& I
dtype: timedelta64[ns]
+ p$ `' ?; f- h/ K 8 P5 c& T. O) ], q& t" X2 k. e
1
0 l# q/ C$ T2 v7 k! A 2
6 M8 r2 P- |4 G! ]; w 3" Z4 V1 ]; E9 u ]1 t8 E* Y; c
4
9 T: z/ G W t- s 52 E9 P4 e7 |+ G$ F
68 x4 u! Z( R# Y* m* m, T
77 `1 x- T) z% n8 h% `, A0 {
8' T) ?2 _/ N2 g" v: L
9
3 Z8 S/ \+ F* e8 ] 10
3 D' A6 R3 z. @4 T0 L 11
+ A1 U) o& }1 }" n 120 s/ e* U% u6 i0 X% |8 k7 t& r& }
13
( l5 P* M( B- ]2 G( q 14
0 \3 N3 H I5 i6 K- F 15, a* R% R+ X. L/ w, ^% J/ j
162 j! y* e) l0 x3 f# d8 e
17
! r9 f' Q$ `: V6 Z* ]( P 18
* U+ a; \* |! B2 z 10.5.2 重采样7 j% @& z4 f7 L# v
DataFrame.resample(rule, axis=0, closed=None, label=None, convention=‘start’, kind=None, loffset=None, base=None, on=None, level=None, origin=‘start_day’, offset=None)% i. w' Z& T7 [+ H; X- s
常用参数有:
7 @, \2 U# b1 T , A1 H* s3 Z* p$ [
rule:DateOffset, Timedelta or str类型。表示偏移量字符串或对象, y% ^# a) R) W5 m3 a6 F; _) W% w
axis:{0 or ‘index’, 1 or ‘columns’}, default 0。使用哪个轴进行上采样或下采样
+ C }% _. D$ S) u1 p- Z closed:{‘right’, ‘left’},默认None。表示bin 区间的哪一侧是闭合的。所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。
9 L, g7 P( [+ t$ ~ label:{‘right’, ‘left’}, 默认 None。hich bin edge label to label bucket with,所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。
- n7 f7 k/ y4 N convention{:‘start’, ‘end’, ‘s’, ‘e’}, default ‘start’。仅针对 PeriodIndex,控制是使用rule的开始还是结尾。
# G; A3 ^* z' T9 J5 ~ on:字符串类型,可选。对于 DataFrame,使用列而不是索引进行重采样。列必须类似于日期时间。
. q6 N6 N2 c. ~ level:str 或 int,可选表示多重索引MultiIndex的级别,这个级别的索引必须类似于日期时间。
. D7 R/ R3 W w a4 G( e origin参数有5种取值:. S" X: _6 t" l! {; B6 N
‘epoch’:从 1970-01-01开始算起) t1 @: q9 D7 B+ \2 r; F* X$ d
‘start’:原点是时间序列的第一个值" u0 O( N, y8 d2 W6 s; ~0 Z
‘start_day’:默认值,表示原点是时间序列第一天的午夜。, X1 d. t4 d J/ b1 y3 c
'end':原点是时间序列的最后一个值(1.3.0版本才有)# ?9 L+ F: c) n3 Y$ q9 P) Y% w
‘end_day’:原点是序列最后一天的午夜(1.3.0版本才有)
: S$ _) T7 z- {. D3 G" u4 c offset:Timedelta 或 str,默认为 None,表示对时间原点的偏移量,很有用。1 k( ]9 U$ ^2 P) {
closed和计算有关,label和显示有关,closed才有开闭。4 R/ _7 B3 j9 I4 X# N0 W" |: `* P
label指这个区间值算出来了,索引放区间的左端点还是右端点,closed是指算的时候左端点或右端点是不是包含。0 V X( T" ^7 r: b9 J$ \
: X3 x& S7 A% o h) c 重采样对象resample和第四章中分组对象groupby的用法类似,resample是针对时间序列的分组计算而设计的分组对象。例如,对上面的序列计算每10天的均值:% }" e8 w- f) v; `) O
s.resample('10D').mean().head()
$ w$ s: E2 @4 @9 e; } Out[117]: " X7 k) G2 D$ v- V) l& O0 _* p/ k
2020-01-01 -2.000000
* x/ @' ?* A5 x4 e 2020-01-11 -3.1666673 X+ Z% _0 E! [: c" M2 g3 [) P, z
2020-01-21 -3.6250007 ^$ q3 R' C3 i2 b- ?
2020-01-31 -4.0000000 E5 D- p: s& f8 o1 I0 {" D. ]
2020-02-10 -0.375000
V/ e$ Z) l3 U' ^! [3 o Freq: 10D, dtype: float64* `5 Q7 T+ {4 K) b
11 E0 D' l! B2 _5 P
2% W, N3 ]! C+ Q8 N" j7 f7 ?# Y% a
3
4 f. R. d" L) d% F 4
7 o/ u/ N6 B: d 5
( ~: @/ z0 R$ w) b2 S 6: A0 M# n. Z/ d& T, R
74 B0 \: {+ o* ~- i. q( d
80 S/ `# l1 G& p2 t
可以通过apply方法自定义处理函数:
8 |/ P. Y+ ]6 s s.resample('10D').apply(lambda x:x.max()-x.min()).head() # 极差
: s9 y8 l5 e' b! T% n; I; D' G
/ l( M8 ~- j4 Q* O% x, _5 | Out[118]: |9 a- ]6 l4 U* L* K7 B, A
2020-01-01 3
" i5 n$ e: Q* D+ @& Z 2020-01-11 4
7 d4 C l$ k E0 `9 G' |1 m 2020-01-21 44 y6 R, I( k/ B3 a9 F/ i# z$ l! A
2020-01-31 2
! Q+ M0 S5 U0 w) R9 S6 D 2020-02-10 4
/ q# g) B0 O I& h: ^ Freq: 10D, dtype: int329 J% U, d- n) q* i: r; \0 V% R
1
+ c) r( ~2 `& p. _ 2
9 o/ c+ ^! a+ } 3* V3 C: ], Y- V: {5 d
45 q5 \7 D8 v) }7 r3 V. I. b
54 |6 N. t$ o% b2 c) g# { [/ m
64 d ]3 [' D# }6 t" y
7# g- [+ L/ i6 k0 k
8
) e% Z& l) a; p" m% h& B( | 9
, B. v& E$ E5 ^1 ? n7 x& k 在resample中要特别注意组边界值的处理情况,默认情况下起始值的计算方法是从最小值时间戳对应日期的午夜00:00:00开始增加freq,直到不超过该最小时间戳的最大时间戳,由此对应的时间戳为起始值,然后每次累加freq参数作为分割结点进行分组,区间情况为左闭右开。下面构造一个不均匀的例子:1 k1 |" W+ K$ `$ @8 l0 q. W, t( U
& C, v& _: `( b& @9 u% d- H idx = pd.date_range('20200101 8:26:35', '20200101 9:31:58', freq='77s'): M7 B- X1 X/ I, ~: W1 f# ~' f& @
data = np.random.randint(-1,2,len(idx)).cumsum()
; r: i# \ L! R& o0 p s = pd.Series(data,index=idx)' c( H! V4 |- S" i8 u
s.head()# N* o& ], P& A7 Z) t! |6 g% w8 w
7 J$ W! j0 J4 r8 M7 N
Out[122]: % I# |! ]- R; W1 x" B. J
2020-01-01 08:26:35 -1) {+ M$ A( y' h3 p$ y1 S
2020-01-01 08:27:52 -1
1 o" \: a. p% T! ]7 u, m 2020-01-01 08:29:09 -2
8 o% ]3 V! u u 2020-01-01 08:30:26 -3
4 G* L7 A' L. S. { 2020-01-01 08:31:43 -4
: _! ~6 ^, y9 [: t' I Freq: 77S, dtype: int32
$ i- |' ]: P: z: L: M% K4 @ 12 H) O" [0 O% Q; C& i8 L
2. N; ?/ z7 P& J
3
$ Q' T, G5 {2 S7 N4 g# _ 4# h) F* E0 F% {/ j
5
2 _- Z/ e0 l& {+ a 6$ M; Z- G6 ~- p
7; L: V; Y( E& w( t, v# {
83 W4 U1 o! M. p7 U \
93 x$ @3 [& ?& G7 h
109 j! ~, `3 G( \
115 k$ ?- n" n9 r& i- M
12' }5 c* n# J0 e0 E' g a) [! a
下面对应的第一个组起始值为08:24:00,其是从当天0点增加72个freq=7 min得到的,如果再增加一个freq则超出了序列的最小时间戳08:26:35:4 |+ W/ q" {( o- t
" V/ o6 [% c) e" L. d5 q
s.resample('7min').mean().head()
* q: x' b; U8 `3 k8 K# r3 H Out[123]:
! _- R, v9 |" x* s& [- g: @ 2020-01-01 08:24:00 -1.750000 # 起始值,终点值包含最后一个值# m' V# |' @! k) b
2020-01-01 08:31:00 -2.600000
, D2 I: E0 e% F, K 2020-01-01 08:38:00 -2.166667; D0 W7 F, q3 B2 I
2020-01-01 08:45:00 0.200000
# e0 ^' n$ s6 _7 X& j 2020-01-01 08:52:00 2.833333
$ {+ {- J$ x6 A: M2 o f Freq: 7T, dtype: float64$ v0 f; q2 i1 V2 m) q! g7 J8 d
18 `+ x- c# u2 {6 s
2; n" h( ^* q7 Z: ]7 R
33 A( [" @; W. `0 n+ u k
4
! d( p( k9 T8 k: J* {9 } 5
- g1 T0 j! h- y4 ^; B- c 6
6 J+ z2 w4 ]7 y+ v' v; {( z0 z( j 7* t& C/ G1 |( V0 K$ T
8
[1 r% [* Z; ` 有时候,用户希望从序列的最小时间戳开始依次增加freq进行分组,此时可以指定origin参数为start:3 w' ~& Y+ M5 `4 w! @
7 m1 j1 h5 Q- N2 d5 `% s% Y$ H
s.resample('7min', origin='start').mean().head()
6 \8 ^* J! E6 s, U0 J' y Out[124]: . f0 c1 e/ k5 j" }& R9 }# P
2020-01-01 08:26:35 -2.333333
. Q/ W3 q5 x C0 J 2020-01-01 08:33:35 -2.4000000 I2 j& T7 G* P0 _/ b
2020-01-01 08:40:35 -1.333333
/ [* b0 A% o! s' n/ f 2020-01-01 08:47:35 1.200000# c2 g" `* r4 b3 |
2020-01-01 08:54:35 3.166667
. h4 T' k3 U$ {( t Freq: 7T, dtype: float64
& V) \; V1 D x, R5 [ 1
( G$ R( l! ~+ t3 w% t7 ? 2
) \4 L2 l" o" t8 ?) N/ U+ y- [1 ` 3
+ X V) C7 r3 p0 ` 4
3 b3 z) g6 Y. b 5
# }, P. |; |& U& T: H 6
; z0 a, Y0 C8 t* e% y3 O1 g 7
9 V) h6 A1 b: }$ }. A4 w( D 8" Y4 W0 n0 [1 Y( s
在返回值中,要注意索引一般是取组的第一个时间戳,但M, A, Q, BM, BA, BQ, W这七个是取对应区间的最后一个时间戳。如果想要得到正常索引,用’MS’就行。) [$ H3 r2 a) n9 t9 W1 O9 n4 F
; l k3 a* s- r' C/ t( R! [6 w3 i$ R' Y
s = pd.Series(np.random.randint(2,size=366),
6 s; I' x" t/ \4 R/ @8 K index=pd.date_range('2020-01-01',/ D9 `5 D3 i9 l( |
'2020-12-31'))
* L+ k; ^+ o% |0 d/ { 9 X* R3 }+ K6 }& {1 J
5 T. R9 w% }: H! b; j
s.resample('M').mean().head()
; V7 }% b6 Q s4 t Out[126]: 7 c$ l3 r C. S" { `& E6 Z0 m% h
2020-01-31 0.4516131 E- E2 ]/ L+ N1 A6 u; U' d3 E
2020-02-29 0.448276* {! q6 X! g$ @8 {9 v5 q8 k
2020-03-31 0.516129* P4 K. Q/ x& X3 n4 _; l
2020-04-30 0.566667! _) q# T8 K. p
2020-05-31 0.4516132 U/ T! T2 r( ~# v+ q3 f3 F
Freq: M, dtype: float64( d( U* v9 \8 @, v, ]; S4 U* n0 c+ w5 c$ v
; b7 j, R" G* ?7 ^" n4 `! K1 ~
s.resample('MS').mean().head() # 结果一样,但索引是跟正常一样
" w: @; `( X8 c3 T+ L1 y Out[127]: * {" \7 |# U/ R+ s; f1 I3 r+ X' a# k
2020-01-01 0.4516131 @' _2 z5 P( B) w
2020-02-01 0.4482766 P0 d/ E, S9 s l; n
2020-03-01 0.516129 P$ w! B4 r9 j8 a& n
2020-04-01 0.566667
0 b# C* I, B2 F 2020-05-01 0.451613* u0 P' k6 K# B; Y8 g% f' o. J
Freq: MS, dtype: float64
- w- H9 ~: C8 P5 M
4 Y; d( L! _7 R' F8 ? 1( A; [+ T8 ` x# D$ t1 M. R
2+ n+ a) u6 B% T: |
32 U% [+ y+ L! G8 P o# ?
4$ E$ G: F3 ~- C, Z, @ H2 g
5
- p2 _0 k3 V+ K: v5 x 6- a8 }: i( r& l; e- E5 M6 D9 Z
79 W" S/ m3 U1 u0 {; Y* X6 b
8, ? p4 m$ i* ^; y5 x Y2 N
9, V9 z; h# V% i' [: U7 t
108 d* ?; k4 @# S5 G D, V E. Q
11
; s2 q1 y# s2 |- P8 V* l3 G 12
/ K, A1 M6 ^# Y4 m$ d! J- k# m 13; u5 A% ~ {2 L: p! P0 t* {+ F9 t
142 n) W/ O; u4 `& K! C7 E. Y8 [0 ~' ^- P
15
; C/ l/ R( ]1 w, C% D" _ 16, M i" r: @4 |1 t! Q! m
17
; G% i6 X1 U" C# G- i 18& B6 `# A+ T( p. {- ~1 f* R. E+ l
19
3 U* p! Y0 H- j8 H5 C 20
/ h4 W% s2 ?8 ?& R 21% O3 f" C$ |5 L, |/ K0 Y- N# ` \
22
/ y9 G" x1 ~; S* T; E( Y: u m* P 对于 DataFrame 对象,关键字 on 可用于指定列而不是索引以进行重采样:5 ^% }6 S5 C' k4 @9 ^( q# |9 ?
d = {'price': [10, 11, 9, 13, 14, 18, 17, 19],+ Y: m; O& h. ~9 W! T
'volume': [50, 60, 40, 100, 50, 100, 40, 50]}
! `& m% @# T1 H. l- | df = pd.DataFrame(d)+ r5 k4 b3 R0 B. Z. N$ G0 n
df['week_starting'] = pd.date_range('01/01/2018',8 O8 P) V8 X9 |) ]5 A
periods=8,/ h8 @& |* O8 [5 }* A7 k; m* F
freq='W')! W. M$ L% K9 R# |) `1 n
df
4 b7 e: u/ o* `* j price volume week_starting
+ S' ]0 e- H9 P1 ~( h+ E 0 10 50 2018-01-072 s3 P+ ]( H9 z# D1 O: f1 r1 ]
1 11 60 2018-01-144 k6 v) e5 U/ Z5 j8 h+ v0 r7 A" A/ s
2 9 40 2018-01-21% t. u; K; B$ {9 H% M) {6 p3 a
3 13 100 2018-01-28. Y/ Y; e# D+ V; N3 i- C7 I
4 14 50 2018-02-04% {+ I% w9 A% f* T
5 18 100 2018-02-11
+ y% m- K1 Y5 ^% f s( t. z 6 17 40 2018-02-181 u8 M/ Y+ h: }9 W* [+ l, @
7 19 50 2018-02-25- `9 S, g, F& h9 P j6 J4 O
df.resample('M', on='week_starting').mean()
; y& [6 W; N2 B5 `) ?) Y price volume% z" b: h k- v8 p9 V- e/ `8 j
week_starting
, H, ?( [# c5 r# Z7 U 2018-01-31 10.75 62.5- f; \% V7 J7 ~6 e
2018-02-28 17.00 60.0/ l1 R7 f4 ]2 z- u- F" F) J
# h; j' M+ w) p! J& R2 W2 R Y
1
# z7 K2 E, f# N Z% e. {, ?9 J 2
! g" X' p" h% T2 p z 3
5 W' F, @' x/ d* @- U0 S 42 g* N1 o' t6 m; i8 X1 C* m
57 W! [: m$ {/ ]
6* I9 |- G5 |. U( F, C4 Q
7
( R( e: H( {; ~ 8
5 ^/ M9 z+ O2 W: y 9: P5 ^+ b" y3 k# k Z K% W0 J
10! ` g$ V1 Y. x. D: X4 ?. F# `6 k, g
11- _; R. E5 E% b5 q
12
! k* k. {9 W$ ]% K# u% h* W' g 132 R- h- a0 q+ s* U: P
14' M8 A$ P( t: N# K0 d% j/ h
15
- t) q% f+ u5 R# _1 x 16
# }7 _# ]" h. n0 S# _ 17: Q8 P" _ L% `4 U: h) }
18
( n: ?/ H' M2 _9 d 199 ^+ t- N2 }1 z3 N3 k
20
; x+ A4 D5 P2 i7 [ 21
; }% p: N) B- M6 c 对于具有 MultiIndex 的 DataFrame,关键字 level 可用于指定需要在哪个级别进行重采样。
5 ~ `" a; f- f m days = pd.date_range('1/1/2000', periods=4, freq='D'). S( S, @5 e8 }+ E. J
d2 = {'price': [10, 11, 9, 13, 14, 18, 17, 19],
; X, Z' K% @( g. E/ @) g* r 'volume': [50, 60, 40, 100, 50, 100, 40, 50]}
( U2 K+ U$ g, Z4 I) A% ^ df2 = pd.DataFrame(& g% |+ ^4 W" X9 R. o( o, X
d2,
7 a: i+ M, x% ]6 G2 r( U3 O* F) Z* ` index=pd.MultiIndex.from_product($ z" R! ^! s) F: ]* n
[days, ['morning', 'afternoon']]. W2 x! i& L9 h
)
% F/ O, y* e, z% v: T& | )( r8 E1 `0 s: C5 I* l# F' X
df2" M' i1 q: O l
price volume& p# m5 a2 ^. ~0 I# a
2000-01-01 morning 10 50
) k( U% G' q2 [' D1 k1 Y# i afternoon 11 60
' B$ m2 o- t% E5 s0 O }1 {% O 2000-01-02 morning 9 40
7 e7 `, _ S. K" U afternoon 13 100
* L2 k8 ^( X- p- T0 _7 ? 2000-01-03 morning 14 50/ [( d' e" Q( z1 \3 h- C% e' w/ i
afternoon 18 100$ t' [8 @; t* G- L& w. g5 W
2000-01-04 morning 17 40) }4 X* S( r m$ }* I6 r) `; b, e
afternoon 19 50% M, f/ m, {+ P: ]
df2.resample('D', level=0).sum()% Z1 }: \* K b
price volume
% @& F: J4 V L4 y 2000-01-01 21 110
4 W* P; D7 D8 m. `% X9 J 2000-01-02 22 140
7 C: C. s7 m9 { 2000-01-03 32 1502 g1 K, S- B" ]' a A
2000-01-04 36 90
, L O7 a. P; S& h7 _8 G 7 r$ F' R1 C C$ f" k# V
1
2 g1 ?) ]/ [! S4 I. S 2/ K1 y; Y! |( T4 {" _! n
3
- Q: W* X6 S% u# ~ 4! h8 J" y4 W$ z* j) Q
5& ~$ |2 X- ?3 K x- `9 H. R) t7 u; O+ _
6# q: p+ y* X; P. N
7
8 E N/ t0 D1 V 8+ w, E+ P3 j, P* j1 D0 z
9
2 ^# `& m0 [: k5 J# O* E 10
+ o8 q; N2 }+ M- v" x- C 11: u: T9 W1 I1 {5 ]
124 x" n- u8 f; B% |
13+ e' J' v; C' A# {7 _
14 {" I. v$ d* A8 l+ j
156 Y2 d+ `1 I( l# K) ^' R5 G
16
) P) C1 T' ^9 x. ?) H. n 17# z! k( L. x# m& T: N1 x
18
8 t3 e; D, `" s! O; c1 m 19
- \" w- C( ]7 h7 X! f 20
2 r- X, o v0 `8 z9 ~ 21; W/ y; ]$ Y8 `% a0 \3 X6 S% W1 R
22% n, _/ |8 ]0 L
23
, b) ?5 ^* H: a* N. ^" H6 \ 240 c% y5 O8 b" a" r7 v3 H# x4 F) I! @
25
: E) x" r( o% y5 `& r 根据固定时间戳调整 bin 的开始:
) l+ ^$ w( q9 u' ^. d0 ~ start, end = '2000-10-01 23:30:00', '2000-10-02 00:30:00'. d% B5 w$ o2 b% D
rng = pd.date_range(start, end, freq='7min')
9 Y w' L8 v6 {) Q! F ts = pd.Series(np.arange(len(rng)) * 3, index=rng)
* h8 T& m1 F @% s% A- [ Y) e+ a4 [ ts
9 Z; F8 Z& }- A! [ 2000-10-01 23:30:00 0
/ G% U! s4 e9 b0 B. F 2000-10-01 23:37:00 3% q* e, I8 |* p, p& P- q, ]
2000-10-01 23:44:00 6: k6 N: l+ s, g. Q
2000-10-01 23:51:00 9
2 I5 ~5 P: q7 D1 m+ a; D6 P 2000-10-01 23:58:00 12
. S; s% U! q+ O, S; l4 j 2000-10-02 00:05:00 15
1 D, ?- t( ~- n0 ` H2 |5 U/ V3 D 2000-10-02 00:12:00 18. u; ` u! V3 O6 `* d8 }
2000-10-02 00:19:00 21! q! \% S: P, B0 S- c
2000-10-02 00:26:00 24
* w1 n& P8 @' L# [5 z2 K Freq: 7T, dtype: int64
, x: p# z! g, z- p. T; G: _ 7 a7 L o8 B9 c: \& Z' k* p! d h. N; g
ts.resample('17min').sum()
- I4 H' F8 S' ~2 \" d4 I v- `8 N7 _4 m 2000-10-01 23:14:00 05 p( U6 n% U0 J9 q R3 ^7 }
2000-10-01 23:31:00 9
3 ? F3 t2 j6 m% |3 L+ x 2000-10-01 23:48:00 21
v3 X6 w) |1 H% d( v" `0 F 2000-10-02 00:05:00 549 v6 n7 E" ?5 |* e( W0 f4 i/ V+ C' o
2000-10-02 00:22:00 24) [ |& r8 T, g" w8 P2 o
Freq: 17T, dtype: int646 c5 u4 I" _- v$ |9 G9 A$ P
6 T1 R, }6 L& M2 |, t
ts.resample('17min', origin='epoch').sum()! D+ W9 g: i' d
2000-10-01 23:18:00 0
" j) F1 K0 K" K+ n& q) c* A 2000-10-01 23:35:00 18
9 c+ i9 K( n. v! ?6 ~$ S 2000-10-01 23:52:00 271 I w; D4 Q5 A% g M
2000-10-02 00:09:00 39
; R; Q2 g9 @. W$ n5 i C) v: ^- w4 \ 2000-10-02 00:26:00 24
! V$ T' ?/ }) h6 [9 n3 ` Freq: 17T, dtype: int64
4 D* }& M! w: n, Z6 W5 _! {
4 e8 X5 y2 b- a ts.resample('17min', origin='2000-01-01').sum()3 w7 a2 t6 g) j: ?0 z- j
2000-10-01 23:24:00 3$ |- f$ |: H/ f. W) J9 H
2000-10-01 23:41:00 15
, b( I( G7 q( c0 I8 _, A 2000-10-01 23:58:00 45
- G2 {. {; W2 g& P% _ 2000-10-02 00:15:00 45- U$ I$ y0 f) P2 \9 e, D
Freq: 17T, dtype: int64
4 s8 a, i$ k" a/ y1 P 4 f+ j$ Y! ~" I" i6 y# {. x/ h
1! F. }! E' b- e: }. q2 { @
2
% Q( T! O7 j* J 39 Y( _. B" \% }% I) m8 m7 O
4
0 U! F3 H4 H! t7 o9 m 5
# l$ d& f/ u5 f2 x6 }( S6 u9 I8 i 6- |- G8 y; H9 n: P3 G# f
7$ O) I/ C0 [6 F8 c! t" ?
8
4 P5 h. u3 u+ u8 F( x 9
% H4 p) Z( W; O `% A) i6 N 10( h7 i. L) p( {1 `: g0 h
11
/ {! _: ~% ]! u 12
" f" Y0 j- p+ P. U. P4 G 13
+ J- ]1 [3 |2 ^0 N9 z 14: W ]/ E1 q/ {" A; @2 ~( r
15
9 W; o1 `$ T; h ?3 r: A) L 16
6 F& k0 [% A# Q- u% l. d/ U 174 i' K( u* P6 m8 l, p
18$ f. q; T% M# ^! B" M N- x
19! Q6 O! W1 W: V4 n( R. V
20
8 E! ?, ]# O( ~8 Q4 y 21
6 L0 r9 \* N9 `7 x7 q, r( y/ O 22
0 d/ H/ e7 t8 W9 k) u 23
4 V) Y+ H" h7 _1 e" ]& s1 c0 T 24
5 F2 p0 w0 r. @/ {! m% Q* T/ _+ k# k 25
2 Z* d! B+ c, ~. r' n9 V+ t 26
1 X( v; G% R. ?% P t5 [7 m' } V9 m. A 27
e, j9 Z! E% }$ v 28
% S' D- Z' q+ W# d% K* @# F6 t 29
4 L. c; M4 J9 D9 Q 30
: }* D) ]' O" H7 u: @ 31
0 A/ p/ F* L6 W1 D 32
B1 V% j& l3 j! h! v 33& t/ F4 k) T3 }" l m$ s. l
34
/ k" z) D) E2 u" T7 r 35
" q$ u; Q! O& X2 f 364 d3 b! L' D/ Q, L6 l8 k
372 O$ ^: Q: h: p% ^
如果要使用偏移 Timedelta 调整 bin 的开始,则以下两行是等效的:
) ] \8 [5 W: J+ j6 ^% c ts.resample('17min', origin='start').sum()0 x" y5 {$ i1 h* y' c
ts.resample('17min', offset='23h30min').sum()1 d" A% K; t- |. z0 T3 M* e4 [+ c
2000-10-01 23:30:00 9& R. \3 { s, T* H
2000-10-01 23:47:00 21
; W! n$ v' l: P* g- i# M0 [ 2000-10-02 00:04:00 54
; ?' m; d& H9 y' j 2000-10-02 00:21:00 24( v# h+ z5 s m/ V, v O- u' A& X0 A
Freq: 17T, dtype: int64( o. W+ n$ a7 E( T" [5 i
1& n5 X* k5 l. L8 u8 [
2
( t# o1 y9 b4 M: i 36 c& f& |/ B1 j
4" v. g6 P0 ~% ?
5
0 O' n$ @8 g( S- R 6, B: y, M9 W" C0 Q
7& |% O H0 N8 e+ N4 {5 G+ U
10.6 练习' Y. O$ _' N$ r
Ex1:太阳辐射数据集
+ J7 u/ |; u6 [& t$ r4 g! {5 J, p9 A 现有一份关于太阳辐射的数据集:6 V. N% r9 l; `4 \& n% k) y
* ]/ X& R5 t9 x, I1 q
df = pd.read_csv('../data/solar.csv', usecols=['Data','Time','Radiation','Temperature'])
1 j" Q/ f1 |1 B7 r6 ] df.head(3)4 @5 t1 s/ N) Y& k
8 V& m! ~- `3 w9 B1 i3 @) q
Out[129]:
( z( c. N9 Y! o+ l8 L Data Time Radiation Temperature
3 @% M7 R- o0 [( c 0 9/29/2016 12:00:00 AM 23:55:26 1.21 48
' k1 K7 d) i+ ^8 A4 \ 1 9/29/2016 12:00:00 AM 23:50:23 1.21 48% p' w I3 c. J F7 h4 p
2 9/29/2016 12:00:00 AM 23:45:26 1.23 48" h6 c ^& ?/ s, v
12 E3 J/ q- W1 W0 n" A
2
: M$ O/ _7 B; N: X' X7 f2 m* B 3* ?6 H0 s* J) e+ y# j
46 z2 n. \: p0 `2 @0 K+ w" L7 s
5- O$ [& r5 p0 a6 |
6" c& R! x6 q+ c: g0 H, h1 h( |
7
) a7 b: }( b" _9 h$ U 8
! {, B b2 \$ x+ j- H) o' E5 | 将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。
6 q$ w" w- A" i 每条记录时间的间隔显然并不一致,请解决如下问题:9 I2 f9 J, I6 ?! `. N' M
找出间隔时间的前三个最大值所对应的三组时间戳。0 |5 J v! I& L% N* |
是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。4 f. m, V; R8 \4 o( u
求如下指标对应的Series:5 O. X# m2 ^* {7 S
温度与辐射量的6小时滑动相关系数
# n F1 r$ v" @4 j 以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列
7 C: K. S5 H5 f! | 每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)
/ C0 u# p" L9 |) G8 ` import numpy as np( }, j' M1 r/ H: n
import pandas as pd
9 U% Z; H. m; X# N; A$ d 1
8 h1 E3 e7 v1 R( ^, s$ z2 { 2
8 j% f. O {2 Y6 C8 q( J& r. n1 I 将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。* X0 P0 J; |) p7 K% Z
data=pd.to_datetime(df.Data) # 本身是object对象,要先转为时间序列0 r2 Q- l6 e9 T3 ]+ J% B0 u& V. l
times=pd.to_timedelta(df.Time)& z$ W2 |% e+ D7 d1 l
df.Data=data+times6 a0 Z j" ^; O) h: S5 i
del df['Time']
( M+ ?9 y4 g( O. X df=df.set_index('Data').sort_index() # 如果写的是set_index(df.Data),那么Data作为索引之外,这个列还另外保留
: L+ X, y5 I/ |/ i! V9 l1 {4 Y df
7 N; U2 n* C8 ? Radiation Temperature* ~* w/ v( C. ^5 c, j% A( D6 e
Data
. x- ~2 z4 _8 v; U) S 2016-09-01 00:00:08 2.58 51
4 W }& U/ W5 H; ~ i: ] 2016-09-01 00:05:10 2.83 51+ _; z5 ]; g5 x( R/ k
2016-09-01 00:20:06 2.16 512 m" ?' S$ x$ F
2016-09-01 00:25:05 2.21 51
3 l' B! M( d- F4 T. g! X8 H 2016-09-01 00:30:09 2.25 51) l0 h+ G( t1 Z8 H0 ^! J6 [' ]
... ... ...
& s5 m* g! A% [ 2016-12-31 23:35:02 1.22 41
7 s+ \/ m2 P3 ^1 x- C. U z- B8 B 2016-12-31 23:40:01 1.21 41! l2 k1 R" y$ B+ f
2016-12-31 23:45:04 1.21 42
0 I" Z) W* X5 R, Q8 D A 2016-12-31 23:50:03 1.19 419 J5 H2 Q7 w+ E; S w
2016-12-31 23:55:01 1.21 41
5 A4 v' f% o7 ~6 M0 a0 `
6 N3 G7 @ u2 @# H" J+ G# ? 1
: {9 o) g: t( K, E# v 2& d' p# J/ o+ o
3
S1 g5 B3 g. U& C$ u6 H 4- l, |* d4 g7 n1 _3 m
5. f7 Z0 z, `1 X+ |
6
7 y% ^* c* [! ]% b9 O+ t, B 76 h* t% p5 d) c4 ?- T7 M* G: y
8
; o* Q8 P, _' s& l, n5 j' W* k9 b* T 9& M* S+ W" [8 y: Z, J
10
: u% r$ d2 D! h 11' t& V2 [% E5 v( Z' f" W; O. P
12! f" C/ C2 p% ?: b& F( g9 S/ H4 {
13
. j4 V: _7 t! P+ U7 q, P 14
: Z$ y2 d$ p: Q$ ]. ~' ? 15. ^7 _8 E9 b" \" m9 l, Y
16( l0 N. u9 i, Z' |( ^. c# [' g( q8 S" L
170 p! Y+ J* i5 h* E0 @
18
3 z- t/ \$ N1 z! y2 k# y- i 19
! r+ l: a2 _' j; q% Z' z$ \ 每条记录时间的间隔显然并不一致,请解决如下问题:
/ H+ H) W. y3 k 找出间隔时间的前三个最大值所对应的三组时间戳。
* y/ @$ y; B5 U& t # 第一次做错了,不是找三组时间戳; z: ?/ M8 Y% X' `% ?6 H' G% n
idxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]
9 p$ X5 Q& i w; F( y1 V df.reset_index().Data[idxmax3,idxmax3-1]
0 L6 @3 c4 \7 V: f; E; M# V% n) z O # i& z: `% H% |* p. I ~ S% V
25923 2016-12-08 11:10:42! w1 Y+ N* F0 {
24522 2016-12-01 00:00:02
8 Q! ]7 ~3 n$ S9 o 7417 2016-10-01 00:00:19
O' `- v$ i! U$ k3 W) K w; p8 v Name: Data, dtype: datetime64[ns]
. t5 r0 ?# j7 J" P! F 11 l# A( J. V4 O; Q! s3 ~+ ]2 T+ z
2
6 F# T5 b! A. W* {, s6 _; m( H7 e 3
; G( B8 Z! J, g' P$ e3 W+ ? 4
, T+ k& {1 F7 i( D 57 Q# C9 m+ e- X' [0 s' [, t
6
n" x+ K% V' L 7/ t6 s' C ^; b6 A8 u
8) C" P4 N* ]2 ?9 e
idxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]
8 |# B: `( {( g& g: U list(zip(df.reset_index().Data[idxmax3],df.reset_index().Data[idxmax3-1]))
: l& l9 p6 s( d/ M. X' ?+ k $ u8 s; N$ f* A! p% u
[(Timestamp('2016-12-08 11:10:42'), Timestamp('2016-12-05 20:45:53')),$ V8 i) Y$ S y1 ~
(Timestamp('2016-12-01 00:00:02'), Timestamp('2016-11-29 19:05:02')),6 s7 W! r/ `+ H1 J- D- x6 j% h9 z1 k
(Timestamp('2016-10-01 00:00:19'), Timestamp('2016-09-29 23:55:26'))]
- }- h8 M7 C+ { 1
: L5 N3 Q+ q) E/ L6 ~, q 2
8 l& ^" ?9 p6 L' J( ? 3
6 V T2 @# u# }6 C& r 4# l/ N' v- }+ V7 O/ M% s1 \
5
$ O. z+ q" ]& K; J% \ 66 S' U; x$ X% t: m0 G6 j
参考答案:7 _. x3 m* g+ G
8 ~$ g) ~" q, P6 k. N s = df.index.to_series().reset_index(drop=True).diff().dt.total_seconds()
8 u: N& m! r0 j& r. o4 f max_3 = s.nlargest(3).index
0 H# n+ [. A* m' E df.index[max_3.union(max_3-1)]
- V! z6 y; S {+ n/ t 1 J2 R( f6 I- {& l
Out[215]:
$ L. y% @6 M' E5 `: G DatetimeIndex(['2016-09-29 23:55:26', '2016-10-01 00:00:19',
8 T9 D+ a5 F$ _$ C r& E( ?% P. d '2016-11-29 19:05:02', '2016-12-01 00:00:02',
# b+ ~. O; I: N! z, H '2016-12-05 20:45:53', '2016-12-08 11:10:42'],
7 d5 \. k# O5 Z dtype='datetime64[ns]', name='Datetime', freq=None): a- D Q+ |! {( R' A# E5 m7 G
1
+ m) |) |- x/ u7 F* l* `/ O 2) `- U8 v$ a4 H5 X6 w
3
$ B9 `& [( I; m0 d 4; X: w) {& I' b: s0 o, m
5
7 ?2 d/ [; r9 d! I! t 6
+ @/ {+ R1 S$ B) I& u 7
* n( {& h6 H8 v- X 8- K0 R# I" E4 O
98 ^) i" m+ q/ R3 n+ r" d1 \: H, d
是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。; A$ T0 O7 J7 C: f! N
# 将df的indexydiff做差,转为秒数后排序。再求几个分位数确定取值区间4 y0 ]3 p9 K: c
s=pd.Series(df.index).diff(1).dt.total_seconds().sort_values(ascending=False)/ X" o: {' m7 _* m( H0 `
s.quantile(0.9),s.quantile(0.95),s.quantile(0.99),s.quantile(0.01),s.quantile(0.03),s.quantile(0.05) e, h' |5 Y* C: L, @2 x1 t- j# d
# `6 B; M; p" m. d- T
(304.0, 309.0, 337.15999999999985, 285.0, 290.0, 292.0)" F* j8 H5 L7 C9 b( u$ e0 D
1
; @& }2 \5 o+ g 2. k5 y: ~ T1 @" C' O
3 L/ m# j0 J8 i( R1 p
4
' ~3 S, p7 I3 x- e/ T$ @ 5
- E$ R8 C; ^; C/ W2 L %pylab inline
9 f, _0 o7 a) b4 O2 A5 Z# M& T _ = plt.hist(ss[(s.values<337)&(s.values>285)],bins=50)1 D" X6 A: m) d3 w( \1 g' p
plt.xlabel(' Timedelta')2 O) v2 F& n8 I( ?% M
plt.title(" Timedelta of solar")+ M$ k3 o! H% }
1
1 R3 |' i# K8 B# M! o9 S) X 2 @$ L' o1 A5 V* f" M" v
3
( l7 E9 n' ?$ j 40 i' l& c& x- D- ]) I6 o
# T* H" @& Y9 B5 w) h
8 J* K8 D- t* I/ G! s) u
求如下指标对应的Series:8 b* n! _3 g/ Z- U& p6 `" {
温度与辐射量的6小时滑动相关系数1 B; d: X2 j# x1 h+ O
以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列; w: T2 q4 [3 t1 o6 ]/ c8 M
每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)
, S$ Z) ^. F6 p& z df.Radiation.rolling('6H').corr(df.Temperature).tail()8 X& |3 u3 a* u* j
, ]9 p" H; M! q; u, Q
Data! T# d1 F! q# I$ U6 f
2016-12-31 23:35:02 0.416187
7 v J& p' u: b4 E2 Q2 c 2016-12-31 23:40:01 0.416565 P1 E8 h4 s2 T8 i+ }' |' k, n! J
2016-12-31 23:45:04 0.328574
7 r7 m, h: K) ]3 l. a% W( f" ? 2016-12-31 23:50:03 0.261883; K% D% s3 d7 _/ l
2016-12-31 23:55:01 0.262406! c3 @. Z, U+ t) T H* I
dtype: float64- x1 i: Z c' P4 S- C
1
9 k n) a5 W& k0 O4 \ 2
+ {% g- p' q: G& L 3. `! g9 v# Y: X" _# w$ t$ e
4
l4 d; _* P7 y0 F% P 5
) k1 T# C1 f, q 6% B! P6 ?. h. W1 J
70 u0 f; Y) s1 t6 p
8
1 B9 E* ?6 d9 \9 F# U 9$ C% b# {7 @6 r4 s, B3 K5 ]
df['Temperature'].resample('6H',offset='3H').mean().head()
! P# p+ c0 L; G8 |: x
7 ]: K- C3 g" G+ b s2 H2 `, z Data
, _! q/ a8 _3 Y 2016-08-31 21:00:00 51.218750+ |: \1 Z: L x0 B& B" n
2016-09-01 03:00:00 50.033333; L6 p& `; P( u2 J0 M
2016-09-01 09:00:00 59.379310
# Y& B5 n) s& j9 y$ N 2016-09-01 15:00:00 57.9843757 t( P9 W" k& S7 V d* _
2016-09-01 21:00:00 51.393939
$ S2 f4 ^2 c5 k4 ~, k8 R Freq: 6H, Name: Temperature, dtype: float64
, t) k" U5 {+ h Q9 F 14 O" ?2 N5 u! E
2: n, R) O3 l$ l. X1 Z8 B# S
3, q% z7 s% f( C/ I! f/ D
45 }# {7 D5 ]* }$ v3 q. P: M
5
6 K! I. b/ ^ ^# E 6; `, i: M. S y# F L! U; C! e, n
7, l! j. M$ m% c* \9 A. M; ]' e% T
8$ b5 m1 o" u7 ]" J" b9 j& g& N
9. S3 X a) m7 ~; R- Z( e
最后一题参考答案:
2 s" x& B$ }( T; u. w* u4 ~/ r
- ]5 O* [4 W/ x, Q# Q; j7 U # 非常慢
6 a8 V% s3 f8 Z$ U: o my_dt = df.index.shift(freq='-6H')
+ K* X d) E( q- w) P int_loc = [df.index.get_indexer([i], method='nearest') for i in my_dt]5 X6 }! o* p+ z. g: n! j
int_loc = np.array(int_loc).reshape(-1)
2 h h$ m9 @7 A& e; u+ Z/ G res = df.Radiation.iloc[int_loc]. {* y- x! z L9 E% h# p0 V- m
res.index = df.index
Y7 n; |# l( X res.tail(3)- k) i. ^+ ~' i# W6 E* `8 b$ h7 \
1
) B, S S" D$ f' P 2
' o- @" X) r% p( C1 \9 I 3$ K& a9 g; x2 s
4
2 m! _7 b4 H7 ~1 f. s' v5 m 5
5 ? p# D; G( r/ f! P7 K 66 D: r& J7 J4 G+ R- l& a
7 D% z7 a- g* `, \
# 纸质版上介绍了merge_asof,性能差距可以达到3-4个数量级# \7 b$ Z, e) ?7 i5 Q; L7 C
target = pd.DataFrame($ W5 i1 u. t# q/ }
{2 A8 f( a A) V L+ V9 O2 S3 V
"Time": df.index.shift(freq='-6H'),
7 G2 Y9 [2 ?( M1 L* \3 y2 m& i "Datetime": df.index,
: ?: Q4 B T1 d+ p# Z- s c8 ~ }- Z ?% M0 ~4 h# g; |. J- ~8 ]6 J
)* r. S3 z4 Y2 j0 D+ V* ?2 }
( m n9 i; h4 \0 g& A res = pd.merge_asof(& d8 G2 K. t' l6 W
target,
* k$ E* z0 J3 _0 [# E df.reset_index().rename(columns={"Datetime": "Time"}),0 p7 q) k/ [& h; a! ?3 o9 [
left_on="Time",! L0 K5 a6 k P( q( x& r7 H
right_on="Time",
5 m, v4 g9 j( o! A direction="nearest"% S' Q" r4 ^; B4 a0 R, ?
).set_index("Datetime").Radiation: R# w: @* T' E9 u8 x0 m1 h
& p, ~) ]3 U+ ~* L- `7 o) ^& i, F: y res.tail(3)
) R+ [# V9 [6 X( G; J" [4 Q Out[224]: , j A5 g# m2 G8 h! @+ t! n
Datetime
: v4 V& I5 e& i0 h1 ~ 2016-12-31 23:45:04 9.333 i. p+ Q. W5 N& C% D: W
2016-12-31 23:50:03 8.49
: | B+ i" e1 `! o! e) v 2016-12-31 23:55:01 5.844 p; t5 I r0 P e! b" }
Name: Radiation, dtype: float64; b9 V/ R3 l% h" C# d! n' @
. S8 l6 R5 Q0 E n4 u 12 g, T: x. D5 E3 D2 d4 u
2
. D5 Y$ k, H3 m 33 P+ t5 s6 G8 o! I
4
* |$ ~6 H7 D4 R/ H+ D, z+ J 5& c& s4 t5 a4 d G- W
6
0 @- s: v+ C$ t3 {& ] 7! F9 I- W! d* Z2 ^8 L( O
8# m' S9 i% N2 n5 o% f% [
94 v! n2 ^0 g( e( U, v3 Y5 a
10
% S/ g* [( M0 G( C [ 11/ H$ a7 G, N. _3 g7 L. G3 x
122 e' \ B- I: c" Y
137 W- q- X7 G1 i7 @7 H% i2 y
14
; j9 _/ b S: h: ] I* G 15. ?: B+ ~. p8 q" J+ F
16
5 H! ^0 J1 O6 Q5 g/ U 17
, \5 o) s( H0 ]- U9 u 18& s# @- Z" b8 k1 j9 m; b8 y2 S
19
7 P l j* m9 p( U) G+ X( K 20. b* u6 [, x' C6 J% i7 |
21
; `6 T4 U. r, E 22: P3 O9 \; s7 ]; Z1 G* S- W
23% K4 H6 C4 a* Z- ^2 h" \ A" V# W
Ex2:水果销量数据集 A$ k d' _* [2 v8 i) g( H$ L
现有一份2019年每日水果销量记录表:0 g' ^- D" w; \( k# I
* ^8 u" m' G; q& f
df = pd.read_csv('../data/fruit.csv')( s0 ^; }" G" t5 b( h# w) m
df.head(3)2 U2 r M5 u* h7 ^2 s' c2 Z- D2 ^
, F7 J7 K" a) r5 [3 V; n; M
Out[131]:
) j9 i. x7 f/ E) X Date Fruit Sale
! n7 c, o; }% G) S& s' ] 0 2019-04-18 Peach 15& i2 r( J. }, G+ y. V
1 2019-12-29 Peach 15; j. H$ ]( ^2 ~2 }; X& v
2 2019-06-05 Peach 19
! s6 r1 ?4 I# Y! i( l 1
" Y: Z7 L. X6 D7 j( P! c0 K 2; ~) t5 e/ R5 o$ b* Y8 C
3" X$ O8 q" j4 _' I
4
' k1 M( a7 l2 {( W- a( @! A% T: l 53 a% n8 g6 _6 P6 N9 F
6* b3 c' U& ?# Q2 W5 c# e3 `
7; u* c# m; K7 x5 M6 Q
83 N- j% t& L! N. }+ |
统计如下指标:
; j4 Z |9 }* L: J# b4 l 每月上半月(15号及之前)与下半月葡萄销量的比值% x7 x! X1 N5 T2 P. I% e- r* K
每月最后一天的生梨销量总和; q$ B/ v) B6 |; d9 K5 `# y
每月最后一天工作日的生梨销量总和; }$ @ z4 t' A% i3 p! A
每月最后五天的苹果销量均值
' I) S6 x2 ?- G, q2 W& t9 v 按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。& N# ~- N$ R9 h) t% Y+ N. n5 ~
按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。
& ?- l S% y) D6 U" K import numpy as np0 C! _% ^6 v; t _6 e/ B/ `
import pandas as pd
k" W" G$ V2 Y3 L4 F 1
6 N( o- r4 s$ M 2! y' X5 |6 D; k
统计如下指标:9 l4 I$ x$ _ Q6 p) c( h8 k
每月上半月(15号及之前)与下半月葡萄销量的比值
$ o: x# y" s& ~" L 每月最后一天的生梨销量总和
; d' r7 h: `- i, `; M" g 每月最后一天工作日的生梨销量总和
* n$ N4 L& c! Y+ j; Z0 c 每月最后五天的苹果销量均值
; T! L+ x& \ o. }7 j* w) F # 每月上半月(15号及之前)与下半月葡萄销量的比值7 x: N3 p1 C4 I" A& c7 m) V
df.Date=pd.to_datetime(df.Date)
/ G2 E M4 J, Q: Z, L4 y5 W6 T sale=df.query('Fruit == "Grape"').groupby([df.Date.dt.month,df.Date.dt.day<=15])['Sale'].sum()
& X$ U# H9 k6 f! S& o2 S sale.columns=['Month','15Dayes','Sale'] # 为啥这么改没用啊
, p2 }" A1 ]+ D sale=pd.DataFrame(sale)5 m% Y, k* _8 L0 h" d
sale=sale.unstack(1).rename_axis(index={'Date':'Month'},
5 ?: o2 [: a3 X8 d! _8 t+ P columns={'Date':'15Days'}).stack(1).reset_index() # unstack主要是两个索引都是Date无法直接重命名
4 `0 r; C6 |' O( m9 ] j sale.head() # 每个月上下半月的销量
7 d3 W- c+ h8 h) ]' D3 }
6 }$ j" U' R8 F$ x Month 15Days Sale
/ w2 j4 o5 f! v3 F3 c& ? 0 1 False 10503
+ _6 H: G; z2 S2 e* t' _6 @( T' K 1 1 True 12341
: g6 x3 k& P2 x" b4 W( } 2 2 False 10001
1 d% D7 b9 F! J; t( D) y 3 2 True 10106, J) }% B' b0 J2 f- d
4 3 False 12814& h6 z/ @( A5 `9 Z3 h. x0 x5 j9 A
d L9 Z2 y. c- ~; l # 使用自定义聚合函数,分组后每组就上半月和下半月两个值,根据索引位置判断求比值时的分子分母顺序
* `' E* w% F3 C8 L; {) ^7 d% Q sale.groupby(sale['Month'])['Sale'].agg(
- y! R [7 |3 U# o2 I- J( ~: r7 u0 ~ Q lambda x: x.max()/x.min() if x.idxmax()>x.idxmin() else x.min()/x.max())3 t/ t2 S, P: G- x& K# x
& V1 A/ M- y5 w$ I! s9 R0 G Month
5 j; F! }' M G2 R" @ f 1 1.1749988 h+ l+ U4 R# a ^4 n8 h+ o, m
2 1.0104997 o% ^2 g+ O5 T3 W% }% l, V
3 0.7763380 t, ^6 E$ C, y) t
4 1.026345$ i2 i" J5 n9 [
5 0.900534; f: |0 t1 M1 T# p
6 0.980136
+ _2 Y* m0 t# \2 O7 t 7 1.350960! D9 j" c* L: h, c. O, J9 Z" I9 y
8 1.091584
7 X# ]) ?$ d# Y: W 9 1.1165087 a" w! G" Z" Y; l+ {8 p+ u( \
10 1.0207844 E' e( C$ P5 K
11 1.275911* ~( m6 n/ C+ ]. ~+ a& @
12 0.989662
' |* s* g5 g* u2 t3 k$ u4 }) s Name: Sale, dtype: float64 ^# v: X% s6 N
& }% I3 d1 U6 e/ J2 C 1; e1 \) A1 q% v
26 r9 A. }! m4 M
3- k2 L b7 a) _! j- y$ |
4
# [( l- n3 _8 F* G' D/ c) u 5
. H4 x3 ^8 D) e6 U 68 a# `$ e) ^& a* v
7# s; t1 r5 g# i( o4 Y3 @' y
8
( W: N- \4 D5 `) W 9
' H- _ W+ v u4 a- X 10
& P. W3 [4 T, ^! I 11. y, k4 |$ ^$ u
12
2 O$ l" m9 l, `! K# R0 ^ 13
# D/ R2 B7 p& ^ 14
+ k1 | t5 j5 ? O8 b* [. j* q 158 [1 _6 D( \6 K) a5 C% i! B
16
% s" ], \0 O& X I) u# c1 S 17
d* D# V( E/ A J5 n7 Q 18+ t2 Z! c" i9 ^ h6 h% E, k7 t
19$ o0 D0 X3 c2 d
20
; }4 |" L$ E ?* l) ^) h 21# c2 D; n% m) x4 e( C+ T* Q
22- Y; Z3 i6 y3 R3 b, ]% g
23 W/ o9 p5 [% f$ U6 O; g$ n
24
* v; D: o' }5 u! y! R- i3 a% ? 25; }! g! M- o0 q) w5 H8 L
26
! e4 L/ |% ]9 a+ Q/ q7 l; U 27! k; U+ ?' H4 C- F9 J9 Q9 e; f! D
28
2 u0 [1 ?$ O3 q* q( d* I" `( \ 29& u( I P, S9 Z! A* l
30
6 k0 ?. D6 X$ J( e+ g9 z" q 31
u7 v4 f Z& G0 r5 e 320 u: K- A. |" G2 Z& H! L
33
4 ~2 n* W) `3 m1 q/ x6 I 342 ~* b) k* c$ X l) _& a% d
# 每月最后一天的生梨销量总和8 a! a' x/ h& k
df[df.Date.dt.is_month_end].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()0 \& b; s3 N7 l# Q5 A
" O2 Y) B( ?! m0 R6 j$ j Date
( P8 s' A/ @ W4 m4 M' F 2019-01-31 847
- Y9 Z& D4 D9 ~; i+ @ 2019-02-28 774
; H2 z( N$ H3 Q. N! ]' P2 r+ M4 [) u 2019-03-31 761
- |4 a+ I/ N% E& J) l 2019-04-30 648
. O0 g4 }/ H7 Q/ d- `2 Q/ v 2019-05-31 616* P5 R" r z! _3 d
1$ @3 m W3 b( Q# L% F- F) M. O
2
5 e8 V+ `. E2 e6 A 3
( b3 E, w3 Q/ d 47 R* C5 b! d4 c3 v' ?
5
( G9 b4 V' q, m) T, ]8 T. R! Z/ f 65 a. ]7 I0 G" y
75 x) u' p: m7 S
8- A8 Y; E1 K$ |5 x& S) J r, F
9
9 `( Y# [' B2 f! E* n, m: \ # 每月最后一天工作日的生梨销量总和0 W1 C+ Q6 s& g
ls=df.Date+pd.offsets.BMonthEnd()* Y$ u v% f2 Y; Y9 [4 {$ Q
my_filter=pd.to_datetime(ls.unique())) ]+ H" e9 `- S' }0 e% B: M* g
df[df.Date.isin(my_filter)].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum() L2 `' N3 g3 ]: j, K: q+ l
; f c# Q" o% b& d$ l Date7 P2 H2 a6 y3 ?1 H5 z! g
2019-01-31 847
% E8 y# k3 A2 X9 p5 q8 U 2019-02-28 7747 D) V* e, H( ^
2019-03-29 510
! W5 I: k' f7 a2 O 2019-04-30 648( v F, w4 H5 h7 Y; [! F
2019-05-31 616- A8 ]& ~. D) s, U/ |( v. ~ k
1- |+ x% s, q& F5 H: r- G0 C1 ?
2
2 a( F/ U) f( q6 ~) \1 C; ? 3
3 C, M: V1 a$ s$ `: p4 E; {. F/ J 41 b6 u& D# S6 J" r7 `
5
* n" e2 }. |2 P" A1 o5 V 6
3 ^* a$ I. i% G2 m. F& n |6 W 7
. i" R$ b; H/ @3 e) h* V 8
8 Z+ s5 X, h7 a. o5 G$ X+ M 93 o, Q: |/ D3 x- x& t, ]
104 e/ R; c/ T" P; @" f- i
11+ ?. `: Z6 @' n$ F" k) _7 H7 L
# 每月最后五天的苹果销量均值
% R" q! w! o Q* y start, end = '2019-01-01', '2019-12-31' y: A& |) n: M9 r) _- |
end = pd.date_range(start, end, freq='M')# Y9 r7 D$ n8 D& w- e) D0 q
end=end.repeat(5) # 每月最后一天的日期列表,重复5次方便做差 E: a- a* I# {' p" [7 {0 |
; Q+ ?* I" C$ h3 z, ^ td= pd.Series(pd.timedelta_range(start='0 days', periods=5),)
5 G# ?4 w' Z# w, B# Y! b td=pd.concat([td]*12) # 日期偏置,最后一天减去0-4天
5 J5 G! g H7 h8 @1 W& |: a) ^ end5=(end-td).reset_index(drop=True) # 每个月最后5天的列表
! q& H/ v7 J$ b ' B+ e" Y0 \, ]
apple5=df[df.Date.isin(end5)].query("Fruit == 'Apple'") # 每月最后五天苹果销量
1 X5 \! _9 s& m" T7 l apple5.groupby(apple5.Date.dt.month)['Sale'].mean().head()! f" k! O% O5 J. ~# h9 M* p
0 P+ h3 m! o7 M8 F" b Date
* _; p; _; s- d8 e8 K 1 65.313725' V# o7 S( S0 n& }
2 54.061538
% m' A, u# m. D# T. m( J) ]7 L 3 59.325581# M% J/ V6 r" h8 B4 c
4 65.795455# i6 o( M" f. w! q, \" u: d" I
5 57.465116
/ k" J- b* W3 D! C: r$ m/ s2 d
! O6 {1 ]% F) x/ W" ^$ D0 X" f 1
4 O- V4 T+ o, X9 g( z5 m: T 2
/ ^' l2 ~2 U1 B7 u 3
k# r; R- h3 v) L 4- i4 |2 s1 Y7 [7 T( ?
5. y' P- v" y% |+ n& e
69 { H8 j/ D2 t6 _% j- p# j M9 M
7
1 l; G; E: z$ c3 C: Z* S 8
% ]. f$ W$ d$ e: H4 p 9& `7 T$ h5 c s( f+ S
10
: U$ I: u; R! o0 l _ 11
/ I) Y* }7 h6 `) \/ B% `" \ 12- ]0 D1 E2 w+ C; \1 I3 V$ E c q
13
, p6 v6 M/ e) f8 |$ O 140 [" K: h( e6 C" N- a
15& U# e. x) [* K: Z+ ], Z, h4 H
16
! B. r6 E, g y9 e4 \: y 17
& T+ I: z8 g( V+ B" \) G1 { 18
1 [; `1 ~+ G! g5 K I% ~ # 参考答案:- T( l; k% |' \' [" V
target_dt = df.drop_duplicates().groupby(df.Date.drop_duplicates(
l y, K% Y: i7 X- f4 a! |6 s- f" a ).dt.month)['Date'].nlargest(5).reset_index(drop=True)' C& c q0 W6 ~ d
6 a5 h+ X) Q- q. ~5 ?& W8 ~
res = df.set_index('Date').loc[target_dt].reset_index(' m) _; F! V1 {! _ t; ^8 C& M
).query("Fruit == 'Apple'")
) h( P4 j6 f- J & J8 A6 a0 h# o+ u0 i
res = res.groupby(res.Date.dt.month)['Sale'].mean(
3 m$ c: h' [$ D, j* g ).rename_axis('Month')! T& d" p7 Q3 q \+ b1 S/ ^3 d7 a
% ?/ X: ]/ s) S0 `) j* h2 F
: x2 _: q1 o6 U* z6 e
res.head()) {$ X I* f: l0 Z) g' t- }8 R
Out[236]:
1 p( P/ ~+ r; l& [ Month
( r E7 G1 r1 A2 x: J% ` 1 65.313725
$ m/ P+ a7 [. }0 U7 N; p2 y9 Q 2 54.061538' [5 R/ j% K7 o6 D$ J9 Y4 N3 R6 s
3 59.325581
/ Q$ k1 x: g6 H" F) Y. ^6 r 4 65.7954552 J5 w; D T. C( X% }* X
5 57.4651169 y$ P" ^6 g- I1 x# I
Name: Sale, dtype: float64# s( r% n# S/ L4 e( v
* a7 C2 K0 G$ [ 1
4 R/ q* t" h7 s+ V- D 2
3 M7 L- ?+ U; R3 e8 T) g. l 3
8 d4 w9 f/ t8 S1 v; q" B, L 4
: V4 v5 t3 U, R+ B 5( W5 O0 ^$ l9 t m4 T2 [4 ?
6
) b3 B4 q0 f& R" l 7& a! F3 ?+ D5 c$ y) H# W
8
) v( J! z9 J5 a9 e 9
% Q1 W; `3 D4 ^. J/ c9 r0 t% X 10
1 v" h; A, |/ b6 Y/ @6 a- ~ v 11/ u) _: e3 c$ p8 H2 _/ K1 X
121 r' {* ?" ~6 i+ |; L) {5 Z
135 g* M% X; W& x5 _; X
14! P& A+ p. v2 {& J% J
15
3 C+ ~0 Y$ r. X% s$ O2 p4 M e6 [+ w 16
6 u& J$ _1 Z) v- x) q- x7 D% z+ F! I 17( Q/ X6 F, G5 s: w( `$ B: b! b
18
- L1 g7 w! l5 r- \2 t 19
0 Y# R+ J# ~7 C( W" m! r 20; N4 n0 p5 H( Y6 g) }# Y
按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。 V" {! p4 O2 h6 W
result=pd.DataFrame(df.groupby([df.Date.dt.month,df.Date.
; o3 E4 w d& h" P dt.dayofweek,df.Fruit])['Sale'].count()) # 分组统计
; P% i, U7 }; J 7 ?+ i8 R# Q; z% d4 W
result=result.unstack(1).rename_axis(index={'Date':'Month'},; D: N6 `& s! h9 }
columns={'Date':'Week'}) # 两个index名字都是Date,只能转一个到列,分开来改名字.( N4 d# K" C1 f5 V! A1 Y, a
result=result.swaplevel(0,1,axis=0).droplevel(0,axis=1), c' D, [: n4 ^( e7 ]
result.head() # 索引名有空再改吧
9 j3 H; _, _6 w! {! u! }0 z5 o
* Z" q; W3 c# x* p- B6 `" X7 e Week 0 1 2 3 4 5 6
" X0 q- c% a1 o) A$ c( i9 J Fruit Month 7 L2 n+ \; Q7 y" b# k
Apple 1 46 50 50 45 32 42 235 j1 V8 V5 @, j4 B2 K% C; g
Banana 1 27 29 24 42 36 24 356 f) o+ C/ x+ a" |6 O* G1 s+ I% J
Grape 1 42 75 53 63 36 57 46( P3 o% j- N9 q9 B" I, |! Q
Peach 1 67 78 73 88 59 49 72( l7 t6 U2 _4 j8 c, o& Q
Pear 1 39 69 51 54 48 36 40
# B5 @* r4 B. T* ~% H$ f7 K 1
1 `' n# V" a. B2 U w 2
6 f0 o( N6 W3 ]# x: b 33 P3 F9 U& `4 h, y
4
" D# D& m' z" P; y; i! b: u 5' K9 S9 A7 R' z- K1 L( u5 f
6! X8 Q- ^, e7 V( V: G: W5 s- \
78 {! z! i6 p- f8 N
8
: g) d- Y4 C! M 92 n# g3 J7 r3 T9 z5 ` o1 A
10& A# m+ |2 ]) O1 w/ t' J2 f
111 R) U9 k9 W) `( p3 G
12
2 E i5 S( B* G( N: A9 T; j# R 13
$ e0 _2 D* t; ^# n, j, R7 d 14
, ]4 _- J& F) t7 q3 F, l4 t+ z2 \ 151 `! h! L, J& I6 z. ^, s
按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。; s5 c+ I) q# j) S$ [$ ?) w8 \: V
# 工作日苹果销量按日期排序
/ W- [3 g* }; J% `4 E0 W% H0 q select_bday=df[~df.Date.dt.dayofweek.isin([5,6])].query('Fruit=="Apple"').set_index('Date').sort_index()' Z2 I' v; ]/ r0 c
select_bday=select_bday.groupby(select_bday.index)['Sale'].sum() # 每天的销量汇总
. \8 ~7 A+ J G5 z/ [# W select_bday.head()
! Q3 W! I0 G- ]6 Z# g* m' g( ? ' e+ H( j! m5 n# C
Date
8 `2 q5 c# Y2 O$ c" d; [ 2019-01-01 189
1 [& [5 g$ W9 D1 E3 v* J! p 2019-01-02 482 e% \+ z0 A, ]: H E) E) w* x: ]
2019-01-03 890/ z5 m8 z$ O: V/ v/ y, Z
2019-01-04 550
6 d: _/ T& q; E a$ `9 h 2019-01-07 494% ~% `% y$ L6 X/ p" u v k
' ]& s% J% h3 l; _ # 此时已经是工作日,正常滑窗。结果重设索引,对周末进行向后填充。2 C$ [# x; M y7 |1 G0 h; U, Q
select_bday.rolling('10D').mean().reindex(df.Date.unique()).sort_index().ffill().head()
5 Z$ W. d7 ]2 ~- s& ~9 h
/ Q' r' y: Y$ h5 w, o6 M2 Y9 j3 X* T Date
6 i% F7 X" z9 t5 ]) o3 p7 x% B 2019-01-01 189.000000& g4 f* b3 }8 ]7 \
2019-01-02 335.500000( [4 y4 B0 `2 J1 f* h. w9 R
2019-01-03 520.333333 e* s; m/ T- g4 I
2019-01-04 527.7500001 \, Q4 }" Q# l; |7 u9 x1 z0 |
2019-01-05 527.750000# D: J( a4 T( r+ c( y S: [& j7 x: ~$ B
; m4 R; ^8 s0 d& Z
————————————————
: x/ j) A2 m7 ^ 版权声明:本文为CSDN博主「神洛华」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
' C" R7 F! ~( z i 原文链接:https://blog.csdn.net/qq_56591814/article/details/126633913
7 D$ L0 s4 U# L ! V3 V" c& D2 S, |( f/ ~
f2 X( G- S$ i c) g4 S
zan