QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2239|回复: 0
打印 上一主题 下一主题

[代码资源] datawhale8月组队学习《pandas数据处理与分析》(下)(文本、分类、时序数据)

[复制链接]
字体大小: 正常 放大
杨利霞        

5273

主题

82

听众

17万

积分

  • TA的每日心情
    开心
    2021-8-11 17:59
  • 签到天数: 17 天

    [LV.4]偶尔看看III

    网络挑战赛参赛者

    网络挑战赛参赛者

    自我介绍
    本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。

    群组2018美赛大象算法课程

    群组2018美赛护航培训课程

    群组2019年 数学中国站长建

    群组2019年数据分析师课程

    群组2018年大象老师国赛优

    跳转到指定楼层
    1#
    发表于 2022-9-5 16:11 |只看该作者 |倒序浏览
    |招呼Ta 关注Ta
    6 E; n. ?5 V; G2 v3 I
    & z& J; u4 l4 ^0 C
    4 q4 Z' h1 e4 \8 u
    文章目录
    ) D3 ~$ z0 r( u( S' u1 E第八章 文本数据
    + M; u6 v' ~! ]- i8.1 str对象: E, I: H" ~# w; g) }
    8.1.1 str对象的设计意图
    ! s& }2 R& \  L3 X% e8.1.3 string类型
    - G2 `8 R) ~1 y8.2 正则表达式基础# C6 |" V9 o! l- S2 p5 Z( j0 o
    8.2.1 . 一般字符的匹配
    : m" K/ L/ w$ h9 h& b) y8.2.2 元字符基础
    8 h  y& E2 n5 c4 m) {8.2.3 简写字符集0 l: s# `" l& Z" z4 A6 F1 D% K
    8.3 文本处理的五类操作
    ! T% \& Q3 R# L" J& u2 F8.3.1 `str.split `拆分# ]7 W. _# q1 k7 U: }3 q
    8.3.2 `str.join` 或 `str.cat `合并! ]0 w. E; a6 t$ N
    8.3.3 匹配
    6 W7 a/ y6 K5 H3 |; `- s$ m8.3.5 提取" `" M; D' M4 Q6 g  g
    8.4、常用字符串函数# S) Y8 A$ e5 {. J& \9 k
    8.4.1 字母型函数4 J0 K& L7 }' e& Y/ u- j6 V
    8.4.2 数值型函数* c' n2 K* `' {8 ?
    8.4.3 统计型函数8 g( l' |9 j! G8 `4 h3 A
    8.4.4 格式型函数# j; ~/ {% K' C7 X3 M" t
    8.5 练习/ w6 \2 `1 c. E( b- e. e
    Ex1:房屋信息数据集
    ; O+ h3 d1 v6 x# _, V2 aEx2:《权力的游戏》剧本数据集; m4 j1 @2 o8 ^
    第九章 分类数据
    0 X" p1 Y& o3 Z( d; C. {3 ~# \9.1 cat对象
    ( {7 K! V% c9 F5 |9.1.1 cat对象的属性: H& N8 C6 v7 Z' i- t0 n; u" }
    9.1.2 类别的增加、删除和修改3 F6 r$ G; q* U, N
    9.2 有序分类
    # {6 z6 U- ~; m% M) j' l9.2.1 序的建立
    9 M1 e2 f6 F, W. N( j5 F9.2.2 排序和比较8 Z$ m) H" E4 d! {
    9.3 区间类别% h/ e0 x0 t# N8 u
    9.3.1 利用cut和qcut进行区间构造
    ' i" y! T; K8 T9.3.2 一般区间的构造
    0 [) ^" {- [; E; I9.3.3 区间的属性与方法. k/ X, _- g( Z
    9.4 练习+ T; l5 e4 y3 J, \* Z
    Ex1: 统计未出现的类别
      p5 Y, D+ n" @! O, hEx2: 钻石数据集( H$ C# k. `! K9 Z
    第十章 时序数据
    ) l  t/ i0 K' z# |10.1 时序中的基本对象, @5 d3 ~* s4 F  s2 {* P
    10.2 时间戳
    2 C' z* a" t7 n3 Q& e, n10.2.1 Timestamp的构造与属性
    ) j2 L$ d4 [5 O( X: x; B) ~1 O% Q10.2.2 Datetime序列的生成8 r% ]4 m( M7 E+ }, H! w) w, a
    10.2.3 dt对象
    4 E) d8 s" M9 n+ c7 Z/ F* [10.2.4 时间戳的切片与索引
    ! J6 J7 e# e1 T8 \10.3 时间差
    2 K5 z  ?4 R0 U, _0 ~; I7 _10.3.1 Timedelta的生成
    ) \& o( Q( A% F10.2.2 Timedelta的运算
    3 [) v) p7 Z8 O# s; M10.4 日期偏置
    ' E; z3 _! E8 J; h1 ]10.4.1 Offset对象3 a7 t7 W# C! o2 J' ], N  Z: O
    10.4.2 偏置字符串
    ! [1 e9 L! [) e  E10.5、时序中的滑窗与分组0 W7 H* a: A5 K' b0 z
    10.5.1 滑动窗口
    + ?1 Y. W- ~5 t- `10.5.2 重采样
    8 _* Y" N8 U! U0 u& u$ K3 R+ u; Q10.6 练习2 _: A2 B/ r' H+ W
    Ex1:太阳辐射数据集
    & _9 W  E, j# O) b* h4 m3 WEx2:水果销量数据集" t- p) O" @3 R, i: H/ `
      课程资料《pandas数据处理与分析》、github地址、讲解视频、习题参考答案 、pandas官网
    1 l$ q! O' a0 @传送门:& Q, @9 q. {& A; p8 x" }* d% J

    # \- {0 k$ t# b2 t" Udatawhale8月组队学习《pandas数据处理与分析》(上)(基础、索引、分组)3 S1 Q& p7 j" T& H
    datawhale8月组队学习《pandas数据处理与分析》(中)(变形、连接、缺失数据)
    ! u) t; I  l: v6 l: m7 R' t第八章 文本数据+ ^( E8 L* q. q6 i
    8.1 str对象0 C" }5 t0 R4 Z1 m  Q( W" H
    8.1.1 str对象的设计意图  v7 |1 Z# u/ z4 h" v) {2 G
      str 对象是定义在 Index 或 Series上的属性,专门用于处理每个元素的文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其 str 对象。在Python标准库中也有 str 模块,为了使用上的便利,在 pandas 的50个 str 对象方法中,有31个是和标准库中的 str 模块方法同名且功能一致,例如字母转为大写的操作:
    6 ~( s2 `: Z3 ?5 }- |/ G. x7 G: M
    var = 'abcd'
    5 H7 h% S* e5 x  v3 v# l1 m- gstr.upper(var) # Python内置str模块
    3 P5 D7 f8 s: C: I6 ~5 FOut[4]: 'ABCD'
    0 Z% R  P2 ?" D. V4 Z6 J  F; t' P4 n) ^# G& K; Q' h# \
    s = pd.Series(['abcd', 'efg', 'hi'])
    8 [. j8 A* e$ [- P3 D5 K3 F" L9 c& j* e' f
    s.str- ?2 e! Y& b3 I0 H8 G* Q! w
    Out[6]: <pandas.core.strings.accessor.StringMethods at 0x2b796892d60>& \; o' U( |' d
    3 [. B5 H7 W1 ~8 S  k1 l
    s.str.upper() # pandas中str对象上的upper方法
    , s2 L6 D# H  t# y' ~* eOut[7]: 8 n8 p7 l3 H  [- j- u; _' c
    0    ABCD
    ) h" R8 J6 m: P/ s% V2 {1     EFG
    7 O, T' h1 x; y4 g5 i0 m2      HI
    ) _! O  p/ L# R0 s* f+ W7 mdtype: object
    1 p5 T- ?7 Y% P7 M+ L: q1
    : A& q' ]' _, l9 `" I# C21 s; i8 ]- s; q& F
    3) ^: |/ U, U4 A6 @+ c7 b
    4
    ' v+ B" P& c: O& u- N5( w) t1 @5 f2 \2 v( _' t
    6$ X3 f0 X4 s: [: M7 M' \. U% P
    7
    2 }2 K; }) n2 B4 |! e" I0 c8, X, s6 s( N3 M9 w* e( V# D& x$ m
    9
    % b! M7 p% j' \8 t10' ^2 N7 Z6 a3 t7 @
    117 _6 `) ^( B' a3 y# ]; d9 N
    125 d# \, o  M' X5 m0 @7 z+ m
    13
    9 U, c3 u- \0 Q9 _# r14" b( d' @% s4 _- a: h$ n8 ^
    15
    0 {6 W! U5 @, D3 O8.1.2 []索引器
    3 N/ v, f; `# q; h8 H  对于 str 对象而言,可理解为其对字符串进行了序列化的操作,例如在一般的字符串中,通过 [] 可以取出某个位置的元素,同时也能通过切片得到子串。4 U% b) l6 K% R  m9 X  U
      pandas中过对 str 对象使用 [] 索引器,可以完成完全一致的功能,并且如果超出范围则返回缺失值:
    : a- l2 q* C- V1 l. U
    / w! s- V* L  xs.str[0]
    $ G: J4 P) t) O) e0 TOut[10]:
    4 [8 P5 I! @- X0    a. ~8 r/ v. I; _/ f
    1    e
    : d1 g) S+ O- o! `* s7 t2    h
    8 q6 F$ D6 D8 x2 E  xdtype: object
    1 C( \- O, g) u% d  |+ O2 R8 b; |7 n  G2 e9 F
    s.str[-1: 0: -2]/ i( b6 [- S* z2 W
    Out[11]:
    5 t3 ?/ E1 J: V& ]: _1 j7 g0 y1 E0    db$ e! _4 Y( ^: z4 J8 Q3 p/ x
    1     g
    5 E2 [" V( n) c2     i# k6 A% y" \4 u& \+ m
    dtype: object- K7 m' `/ B8 @

    ( h9 u# |" C6 H5 P5 G' m6 Os.str[2]
    / }! A  h' M8 Z7 eOut[12]:
    6 p+ [/ v3 W/ a+ L4 ~0      c7 h  n& d  Q! Y: w9 [
    1      g
      s- o) r& G2 b  C) b) Q2    NaN" n1 g$ p" J1 G. R
    dtype: object
    ) r, T: }6 p8 H. O
    6 ], x. F0 v$ j0 U5 n1 A( T" P1
    ( n' T% y4 {( j% e! D+ k2
    ( v8 N4 l1 d# x% r  `. `3
    ' O9 O- r" }: k* F& W9 v6 {2 F4( Q$ Q7 s& ]! s, j  P! B
    59 R6 g8 L: m# S. w4 b3 ]
    6$ d. L/ g" |* |( c* M
    7. J# c- g$ B& ~' a; L
    8
    ) i/ m  Y. U4 C" ^$ C9
    8 w/ X. N6 X2 f6 `$ `4 J+ y/ g: d: ^10
    : p9 g9 l$ Y# e# V112 [6 _* e$ `+ [6 {& \' b
    12) ?# |- ^+ W3 N2 X+ c
    13
    ' B- R; E: w; z% ]8 R8 ?141 K2 A# f* j) A' D
    15
    5 F  t/ K! ~. R3 L" a( \16
    2 Z8 N5 @7 S3 ?  [5 B& U' t17+ o; ]# n5 l1 n
    18
    8 O7 ^* h- \# F2 S6 Y$ m8 S: C7 ~19
    1 ^& l! |5 E5 b, M6 X) ?0 b20
    & L# ]3 z2 E; P5 _import numpy as np7 l! F' h! s% D  P1 \
    import pandas as pd- h$ s" z' u; g
    + ^$ c: i& J4 e2 h/ E# ~4 t5 l
    s = pd.Series(['abcd', 'efg', 'hi'])
    : ~0 _( n8 l, K+ as.str[0]) E. O3 F: J5 T5 E/ I( C& Z
    1
    4 ?' _# m# R: r  f5 M6 }1 @24 C6 q( {* C9 p5 y1 y- T2 H
    3. d- J8 K; a5 b* A7 K
    4
    1 A$ \6 ]8 t" v1 ?/ o/ n5) k( C" r7 @( M5 N& d/ Q; C
    0    a/ c5 Q8 h$ J" V
    1    e/ G8 ^2 J+ {, U- N  ?: r/ f7 x
    2    h
    # i% m6 ~9 l$ z9 adtype: object
    ' B! p  U2 S) v/ k! N% ~1" X* W4 ?' X% X- d4 K" g7 n9 B
    2
    + Z6 W* {$ K3 V' g4 y3' t7 f! S4 w: c# {. R* E5 M
    47 W4 J% [/ m2 V" I0 l. Q0 i* w
    8.1.3 string类型# o: j7 }' C& V, Y
      在上一章提到,从 pandas 的 1.0.0 版本开始,引入了 string 类型,其引入的动机在于:原来所有的字符串类型都会以 object 类型的 Series 进行存储,但 object 类型只应当存储混合类型,例如同时存储浮点、字符串、字典、列表、自定义类型等,因此字符串有必要同数值型或 category 一样,具有自己的数据存储类型,从而引入了 string 类型。
    4 H. N9 _1 N! r& G  总体上说,绝大多数对于 object 和 string 类型的序列使用 str 对象方法产生的结果是一致,但是在下面提到的两点上有较大差异:
    ( |4 `; @8 \# m" B. Y3 ~% Q# D8 ?( u
    二者对于某些对象的 str 序列化方法不同。3 r& o- K8 A" Q% W/ r" w
    可迭代(Iterable)对象包括但不限于字符串、字典、列表。对于一个可迭代对象, string 类型和 object 类型对它们的序列化方式不同,序列化后str对象返回结果也可能不同。例如:$ Q* P. F! V! j( H
    s = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a', 'b'], 0.5, 'my_string'])
    ( o5 s6 k- o0 }- ^* os
    - s9 T2 {5 X9 v. H+ u! G, d  s5 i6 V1
    ) q! c; Y( J6 @( o2
    & X& h! F% |0 Z1 m8 r( `! \0    {1: 'temp_1', 2: 'temp_2'}
    % O# ?' J2 q" f& V# g/ Z, m1                        [a, b]
    * r1 R& Q" |# Z  U& M2                           0.5; O  O8 Z# H8 _* @
    3                     my_string, t( G# [$ x7 Q: @1 |  ~: L1 q
    dtype: object" f) e6 B3 G! H2 j6 h; J
    13 g4 I% w* G: J$ x
    2
    : v6 D& G( x2 }; F- @# A  d' ?4 }3
    7 @  @. y1 b& U9 J# u0 x& B4 W1 w# z4 c4
      m" [, B9 }9 B- _/ ?  y5
    - l8 r( u& N7 v( }s.str[1] # 对每个元素取[1]的操作
    ) M: H' m5 M8 u" l# g1. `- u4 g. w$ v$ E- E$ l) b! K9 j6 \% o7 ^
    0    temp_1
    : R3 ]# T4 B. ], h2 Z1         b
    1 I, s- i+ y% I+ {2       NaN
    ) n/ _! u% W  C! ]* f/ O  n, h: r9 Z' n: Q3         y
    ; V8 d! i' N  h6 K( s* Q. Pdtype: object
    8 S) X/ t0 `4 M) u, i! E$ U; ^1
    . a& N8 V6 a6 I  E3 X21 V$ M. C7 u3 J2 [4 g0 T5 L
    39 F' `! }9 n& _  N6 N6 ]) Y
    4
    # X8 Q& k) P- T: X( Q& l56 w, t7 m5 u8 z' Q. ]
    s.astype('string').str[1]+ x6 a0 p, [" {/ ]: i* M1 w
    14 Y( ^6 K" @! [: S# B+ U
    0    1
    : v3 G7 I, s& o" j9 q& O1    ', Z& r; k/ N7 {8 t
    2    .
    - Q2 s6 e4 h8 V) M- V' s3    y
    1 M8 G- c' y" A; R/ f- ^dtype: string/ w1 a: k9 `  U# _% O3 R$ E2 P6 U" n
    1( }( p9 r; S: C# `" ~* j/ D
    2  k7 a4 O7 B" H' e/ n* }3 H
    3
    ( |$ P( [+ q# I. L5 Y4
    / z8 a: \/ c& E+ z5! s3 ]1 n' o) a2 C3 Q0 A
    除了最后一个字符串元素,前三个元素返回的值都不同,其原因在于:
    " P/ x: ?  @4 h! s8 E* W) Z$ D$ v2 Q8 T; u9 H
    当序列类型为 object 时,是对于每一个元素进行 [] 索引,因此对于字典而言,返回temp_1字符串,对于列表则返回第二个值,而第三个为不可迭代对象,返回缺失值,第四个是对字符串进行 [] 索引。! ?3 S. J0 R' P  t/ ^: r$ Q% |
    string 类型的 str 对象先把整个元素转为字面意义的字符串,例如对于列表而言,第一个元素即 “{”,而对于最后一个字符串元素而言,恰好转化前后的表示方法一致,因此结果和 object 类型一致。
      A3 W6 F7 p, m/ m( Q+ zstring 类型是 Nullable 类型,但 object 不是
    / ]0 @& f  {3 |  这意味着 string 类型的序列,如果调用的 str 方法返回值为整数 Series 和布尔 Series 时,其分别对应的 dtype 是 Int 和 boolean 的 Nullable 类型,而 object 类型则会分别返回 int/float 和 bool/object ,不过这取决于缺失值的存在与否。
    + t7 S1 ]8 j' q- p7 N* q" A  同时,字符串的比较操作,也具有相似的特性, string 返回 Nullable 类型,但 object 不会。
    $ m! f" H8 E% T. `. ^- ?s = pd.Series(['a'])
    ! b1 Q3 {. R! s1 k5 X; I2 ]4 N/ p/ @% m; [1 Q
    s.str.len()
    5 R$ l' c# x/ b4 {% sOut[17]:
    $ F; j1 u' j4 F/ Z7 C0    1
    3 B9 C, b2 V- S  M" W' Rdtype: int64/ c- Q: j0 j  N( a. E

    2 V6 p  U, }8 ]' ~% ks.astype('string').str.len()
    ; z$ |/ L) X( d0 lOut[18]: 6 h; {; M# \+ H: c# g" O, o
    0    1
      l7 g& ^" ?- s! W/ \* l0 Adtype: Int64% s1 l1 x! J% |; ~( W
    * x' h$ X. y2 f7 u6 n( E; a
    s == 'a'7 r+ c; X5 w8 f9 p! G1 a; d
    Out[19]:
    ; w  b7 J0 C" x# [! T% `" i0    True, ?% O; O2 A( O
    dtype: bool
    2 m, n# ^9 ~3 m( }1 r) A9 g$ ~+ F" q+ L% Z" L* [- ^* M
    s.astype('string') == 'a'* X+ F3 U, `) N: B+ w; {- R5 l
    Out[20]:
    ( @0 v1 E) z, D/ P8 W6 Q1 d" a! {0    True+ z. y$ x/ T4 k* a+ n
    dtype: boolean. p3 H: n1 Q" U/ z' I% C

    " T) ~% e0 A$ z$ q. S2 ms = pd.Series(['a', np.nan]) # 带有缺失值
    6 b/ G% _/ g# h0 C* b) r1 u# B- m
    " ^, h( l2 o: y- ]s.str.len()
    ( f( s* ~5 a7 C  k0 A, m1 ZOut[22]: , M1 |( m: ^5 [3 p7 g, R
    0    1.0: G- T: v; V  r* K
    1    NaN
    + U# f! U  X& {. Q( Udtype: float64! M4 N* k  X& [, R
    - ]+ w! [* j* J2 s
    s.astype('string').str.len()+ x* w! U# D, Z7 t
    Out[23]:
    $ U# Q3 Z2 _% ~, m0 j- w1 @/ l0       1
    9 A9 R  W+ \4 O; _" u/ l# S  j1    <NA>
    , U) g  W9 W4 Kdtype: Int64: e% f: B- E% v/ ?9 T7 a

    % {$ g$ ]! b+ a7 Hs == 'a'# B+ y2 ~& V' s+ t
    Out[24]:
    " M  Q+ _) G' [; a0     True2 S7 L6 n; O! T0 g2 i- n
    1    False
    4 G# @) b* N$ ?# t" Ddtype: bool
    . {+ I, F! i: u
    . R# Y8 |. n! g7 T0 M& ds.astype('string') == 'a'% z: N. o2 U( ?8 }+ i& D
    Out[25]:
    & |7 H1 H/ e* P( c3 t0    True7 U+ k4 D5 H! A' j- d( r. ~9 o
    1    <NA>& O4 g/ k" a2 r! F- d* m: S
    dtype: boolean( i8 J3 o" U6 @  M% X
    6 w* W8 p* \& b6 R
    16 ?( k  b2 `# i( K# p) j
    2, X& F8 q$ A  W& h5 S( {6 i
    33 O7 i% I( ?3 m; o5 g5 m$ e" q; j
    48 k) d* |( N6 T8 r' Y3 v& r7 t
    5: J0 p8 h% r1 f9 ?; b
    6
    - a& _! H, G% g6 u( ^2 {+ d# M7
    6 I( B$ k1 |& `1 S& y' h8
    4 y+ p/ W: Q9 ~' u' x% h95 x0 a% w5 b/ B+ X  j' P2 g+ e
    10
    - g6 z' H; \. V& }117 i- l* ^8 o" l3 Y8 S# V; K  g1 ~
    121 L+ `* u" o: d# S& B* @) |
    13
    0 `' W" L& N0 c$ `14
    . C" L4 x& [( ^15% f" f. u' ?6 O8 x) }7 o' ^7 s
    16
    & ^  ^$ |% n+ r$ C" n17
    $ d, A5 F5 y2 X: ]" _18
    . |3 Z  v" c% m% r19" n+ P0 }/ W; n. C3 O  I0 g, C
    20. q( e8 S2 M% i4 o+ R* _4 t, H
    21+ z4 X; G1 H% w4 d3 m8 a! J
    22
    ; t6 x& h5 I; E/ V3 V1 I  W+ m  `232 ]# }1 F' x1 @
    24
    ' M- a- f  g" ]2 G( d9 i1 M2 z25
    - n% Z' S3 }) y7 }. W' g  V# Z26# ?& m) A6 {4 f1 j
    27
    ' F  ~7 b1 w) F% X/ K7 Q) R  k28
      P# f3 `4 p% W/ F9 q- s29
    % }1 t# m. d) @! ~: Y) i  |30
    # {2 f% F6 a( F( H2 w31
    + i2 r- B' m3 B+ a3 q6 s4 l3 I: z" V; x32
    0 \0 _% L  i% w33( y2 _# G0 Z. c# M$ N- @
    34, x9 {' _8 S' _/ ?& J7 R
    354 b* h6 p' C, |' D5 J2 T
    36; G" {7 g+ w6 M0 C# d+ O; B
    37
    9 o' [& O0 e0 G4 Y38% P! e* o- t/ d% l
    39
    5 m- y5 F8 A4 L9 k40+ D; a' a0 X# \, _, v
    41
    & J& {5 J2 ^. t1 V42
      Q. D. a5 m2 X! ]; o9 A43, \. T9 y( v3 S5 B: W. A, f4 p
    44: M2 R2 z+ {1 c: @4 }) A
    451 W2 }" ]; E0 s- X
    46
    0 i7 v% s9 Q2 c. k* K8 q2 o479 z. E- R+ R( ^$ B8 u5 g
      对于全体元素为数值类型的序列,即使其类型为 object 或者 category 也不允许直接使用 str 属性。如果需要把数字当成 string 类型处理,可以使用 astype 强制转换为 string 类型的 Series :
    4 G9 u) u8 ]( p* T7 p% g9 q# `% R3 d
    s = pd.Series([12, 345, 6789])& ^4 ^- z  P9 \

    ' H/ Q5 l8 N3 p! [# Qs.astype('string').str[1]( h" ]0 U$ B$ A# w8 S
    Out[27]:
    2 j' [3 b( @4 O8 z; b! M) J0    21 Y% Q" v. ~# i# [
    1    4
    & W+ h2 G+ s! v- w4 X2    70 p# r' R: B5 Q: ^1 L8 P- X
    dtype: string
    - t% F+ z9 w& r' [1 W7 K4 I; {1; E" r6 }  |7 o4 R& b2 o
    2
    * m) C& w/ o* w0 M' a33 ]$ m5 D1 p7 y" b: u% Y; r+ J
    4
    ( Y( z( s. D; \. [8 o9 ]4 w0 w54 S+ t( q, ^  U5 \
    6
    % r2 C1 |. ?4 u( C' R7
    5 ]0 F1 F: h2 t0 x8
    7 g, k4 `0 O. ?: Y8.2 正则表达式基础) \4 l. H2 O( G1 S6 b* y
    这一节的两个表格来自于 learn-regex-zh 这个关于正则表达式项目,其使用 MIT 开源许可协议。这里只是介绍正则表达式的基本用法,需要系统学习的读者可参考《Python3 正则表达式》,或者《 正则表达式必知必会 》这本书
    0 @3 J5 T! Z* q9 f) {; O( A
    ' H$ q8 Q2 C" X: n8.2.1 . 一般字符的匹配$ h$ Q0 w4 L% u7 L( _& B
    正则表达式是一种按照某种正则模式,从左到右匹配字符串中内容的一种工具。对于一般的字符而言,它可以找到其所在的位置,这里为了演示便利,使用了 python 中 re 模块的 findall 函数来匹配所有出现过但不重叠的模式,第一个参数是正则表达式,第二个参数是待匹配的字符串。例如,在下面的字符串中找出 apple :
      ?2 r+ K  \- @- L1 G$ I/ J: K) \* X
    import re$ M9 g" }9 ^' H" O
    9 M8 p6 F+ \9 b; B
    re.findall(r'Apple', 'Apple! This Is an Apple!') # 字符串从左到右依次匹配& Q7 S" T4 L" [$ b6 S8 z; G3 a
    Out[29]: ['Apple', 'Apple']
    $ y$ K! d% W, _6 N1
    ) `, i/ P4 y( H) e, T8 Z3 n5 `2
    1 F3 Z) u/ z: F& Z' T8 \0 [" g6 f3
    , c1 f. Q! I+ j( z& f* U: M4
    1 ^2 G, r! b; ~. N8.2.2 元字符基础# g+ G# o- \5 E' ^
    元字符        描述
    # m( C4 s$ _. ^$ j8 i.        匹配除换行符以外的任意字符
    2 V2 O: }. R7 b* V[ ]        字符类,匹配方括号中包含的任意字符/ e& h0 q9 C4 d- }% B: t
    [^ ]        否定字符类,匹配方括号中不包含的任意字符
    ( }8 k1 r% z/ ~' I*        匹配前面的子表达式零次或多次
    # V& s& [$ K' |+        匹配前面的子表达式一次或多次。比如r’d+'就是匹配数字串,r’d’就是匹配单个数字) N# q5 {/ u0 d4 m: B' p. O
    ?        匹配前面的子表达式零次或一次,非贪婪方式% R9 f! c. A9 X
    {n,m}        花括号,匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
    , w" x5 |& P" o8 s$ L(xyz)        字符组,按照确切的顺序匹配字符xyz
    ( ?5 a* f6 n7 e" K2 [: K|        分支结构,匹配符号之前的字符或后面的字符: m% m) I: n2 Q) Z8 H$ W. a
    \        转义符,它可以还原元字符原来的含义
    8 o& o& a: F% ~: R- @^        匹配行的开始
    ) l* R3 s( [' F" L9 X$        匹配行的结束4 N$ r* X- Q6 q
    import re: L8 \% H" ^2 G. v; r
    re.findall(r'.', 'abc')
    ; {: q5 x4 |9 A1 e, r! e3 W8 ?Out[30]: ['a', 'b', 'c']6 w1 ]3 ]8 ~& z, J

    & |( W: V1 l0 h- c7 ?re.findall(r'[ac]', 'abc') # []中有的子串都匹配
    9 D; y: c; H" {4 B4 z& `Out[31]: ['a', 'c']/ ~2 ^- s1 f  Z! D3 f

    ! D+ Y: Y+ l" ^9 ^2 kre.findall(r'[^ac]', 'abc')
    4 Q- [8 V5 y4 M: T! z1 K3 ROut[32]: ['b']' L$ U  x1 Q+ b/ j; y- p& J
    , Z7 [! |) a7 W; v5 b
    re.findall(r'[ab]{2}', 'aaaabbbb') # {n}指匹配n次
    4 V' y  u+ E$ \9 t. j+ P! C9 `Out[33]: ['aa', 'aa', 'bb', 'bb']
    $ W4 w9 O; M8 ]  f0 y5 U- Z# K. \  G
    re.findall(r'aaa|bbc|ca', 'aacabbcbbc') # 匹配前面的或者后面的字符串
    $ b1 |. w7 n9 z" OOut[34]: ['ca', 'bbc', 'bbc']
    5 m9 b0 T/ p8 @+ [! B* G9 c
    & X/ I2 M% x. v1 x( L) B9 g! d# 上面的元字符都有特殊含义,要匹配其本来的意思就得用\进行转义。* Y& I( b) h0 S1 S
    """0 O% w. K$ [) o8 P2 S
    1. ?匹配的是前一个字符,即被转义的\,所以|前面的内容就是匹配a\或者a,但是结果里面没有a\,相当于只能匹配a。
    ' X. I  Z* c8 L7 I% s2. |右边是a\*,转义之后匹配a*,对于竖线而言左边优先级高于右边
    0 V; p* v7 Z6 R3. 然后看目标字符串aa?a*a,第一个a匹配左边,第二个a匹配左边,第三个a虽然后面有*,  s6 r& @3 |' P, W% f& g. T- w( e8 S
    但是左边优先级高, 还是匹配左边,剩下一个a还是左边,所以结果是四个a4 ~1 s6 {' k2 q. V& h" U* B
    """
    ) b/ N0 D2 U% h- X+ L$ w% {
    $ w; I$ y6 y$ w% T! e5 Ore.findall(r'a\\?|a\*', 'aa?a*a')   # 第二次先匹配到a,就不会匹配a?。a*同理。, M9 J$ l6 Z8 Y  I( N
    Out[35]: ['a', 'a', 'a', 'a']
    * X0 Q/ c/ v6 d$ g& u- l3 U& S+ [9 f& J0 z" _
    # 这里匹配不到是因为目标串'aa\a*a'中,\a是python的转义字符(\a\b\t\n等),所以匹配不到。) Q, C) P5 o$ U/ T& V& `' F
    # 如果是'aa\s*a'之内非python的转义字符,或者'aa\\s*a',或者r'aa\\s*a'就可以匹配到\字符。
    ( j- F, {" r/ ?* `re.findall(r'\\', 'aa\a*a')
      P. D5 M# y; I* }- I[]
    2 \: U: p( z! C. s+ u) K( [
    2 b3 `3 r- P6 ?5 I$ M9 P0 Bre.findall(r'a?.', 'abaacadaae')
    , @! d% ^) q% Z4 f  X  x* nOut[36]: ['ab', 'aa', 'c', 'ad', 'aa', 'e']
    0 |$ n8 D" Z# v# K' {8 v: d# K. E$ c  x  M
    re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10') # 多个匹配模式,返回元组列表+ z3 O7 s  i* I* D! o# j+ ~
    [('width', '20'), ('height', '10')]
    " M& r8 C4 U% m
    * B5 Q. a, |9 ~9 t* ^1
    % o( _2 P- `, u, g29 [% Z1 ^# c# u! d- r
    3
    8 g+ F, O6 `7 A4  q! T4 g6 ^1 W. q  P) j
    5
    8 V, M, H9 V+ o- V0 z- `$ T) u6
    6 k9 k0 J7 I( i9 [7 U& Y1 ]7
    * |8 n* x; Z3 p" x4 z6 m; O80 Y3 Q3 M1 d* u' U
    94 r  p9 |0 x  M# E8 l8 v
    10' v2 j; V7 r# S/ ?
    117 L- G! R7 U- U+ f- i( k5 ^
    12
    " F% {9 W9 w9 J; |% N  V130 t" N0 d  F: Y, R" S& k: t, y
    14
    6 G! y' o8 L+ Y0 w! I6 h$ Z15- e9 O7 r% i' Y! g
    16
    # B7 A- e% z! b" P* w  x" J7 o4 `( o17
    3 y) l' h" B/ L$ b1 y! L18, {" I$ N" C3 F; Z# I& N
    19# ], x) s8 \3 T$ M( m! D
    20" S/ u* E. m4 a5 K) c! ]
    21; c! l  ?2 L/ e( [0 F, e3 @
    227 ]$ H9 E0 e1 O+ ]- y
    236 s! K3 Q5 o  q2 j0 D0 p
    24( O. @3 w9 M1 p6 \: [$ J
    25
    9 F) O5 j* t+ x# U2 A# h266 u, s+ j* J. b4 ~  G
    27
    + l- e) [; m6 k1 B4 S28$ L! _1 R) T9 `5 x
    29
      i7 D3 r4 Q$ H. q30( ~- ?9 e$ b) |5 j. @7 g
    31
    ; J0 X7 d) e+ x7 H9 X& G. F7 l& [32
    / m9 v1 D! E. g9 N4 E/ t33* U: s; }6 w; i* i6 c
    34: z9 Q4 @4 @# W3 R1 J
    35
    6 P7 a( W7 b0 x8 W8 A* G3 I36" e* c9 D! l, ^$ H
    37
    5 K, f9 H3 j$ O% |9 i# T  O8.2.3 简写字符集
    1 g& q; O" y. c! E: i则表达式中还有一类简写字符集,其等价于一组字符的集合:; n" \, n0 l( B# E9 K: j

    * W  Q% I/ d/ |! H$ o8 g! M" n简写        描述
    3 I. b5 T& Q5 k( V5 y" x  R\w        匹配所有字母、数字、下划线: [a-zA-Z0-9_]
    * v( H8 P; Y. y. Q/ z\W        匹配非字母和数字的字符: [^\w]) ]/ p7 C; l) r4 K* p
    \d        匹配数字: [0-9]8 q% u& ]) u! @; {+ f7 K
    \D        匹配非数字: [^\d]7 [& v6 |) i; D4 C4 _* f9 l
    \s        匹配空格符: [\t\n\f\r\p{Z}]
    2 Z: R* R, u- l0 V\S        匹配非空格符: [^\s]
    ' g7 Z7 e4 h3 N1 B4 v$ B3 N\B        匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。
    . g2 L5 s' g0 ]. o- ure.findall(r'.s', 'Apple! This Is an Apple!')
    $ h" a6 g& l9 _% D" q' i3 FOut[37]: ['is', 'Is']- ~. u& w* P! p3 f
    9 [+ _1 ?6 @- M" O
    re.findall(r'\w{2}', '09 8? 7w c_ 9q p@') # 匹配任意数字字母下划线的组合,但必须是两次
    5 f0 B2 R$ z. d/ Y0 |) D7 lOut[38]: ['09', '7w', 'c_', '9q']9 ]7 s% u8 Y/ l
    8 O( t! ~. q9 h/ x) p) [) H
    re.findall(r'\w\W\B', '09 8? 7w c_ 9q p@') # 匹配的是两个字符串,前一个是任意数字字母下划线(\W),后一个不是(\W)
    ; W+ _. [* I8 O" G2 zOut[39]: ['8?', 'p@']
    7 Y/ U% z* d: ^) a( F1 u( g- b/ F3 H/ K) Y
    re.findall(r'.\s.', 'Constant dropping wears the stone.')
    5 M# o. n9 l, Y4 s) ~2 F% i9 t) ZOut[40]: ['t d', 'g w', 's t', 'e s']
    . n, r. }7 B5 F4 Y2 g7 U- q9 R; [/ @) F1 q% G/ G
    re.findall(r'上海市(.{2,3}区)(.{2,3}路)(\d+号)',
    7 m, ?5 z: K  {3 q0 t+ M6 b           '上海市黄浦区方浜中路249号 上海市宝山区密山路5号')/ s5 y8 V5 ]. s( f
    ) y! s* }* W( m8 _& \
    Out[41]: [('黄浦区', '方浜中路', '249号'), ('宝山区', '密山路', '5号')]6 }% X/ t4 h8 F5 U5 I2 S/ @
    " f  e% k( n+ K" \8 e! G
    1" ]% K% r6 t5 |( s; }
    2& h! |1 @9 g6 S# P. a
    3
    , Y4 H6 Q/ }( {/ A& v* Z4
    & x3 Q) i6 Q$ R! x, D52 u. D: U( o. E% t
    67 ^7 q& h" H6 q: g2 G
    7" `' {0 h" o  t5 Z4 R6 N. F8 c
    86 @/ j% b$ B% @7 _
    9& p5 E; W8 i; c  `( }; \2 R; \
    10
    & Q4 I7 W) s7 s) w  U3 ?1 x11, ~, N& F$ q1 r
    12+ _( W- B0 u$ ]3 t- X3 j
    13
    7 r; |- _  h9 D' m144 Y7 T0 ]: M0 F* {7 }8 }
    15
    # Y" c; h: e2 ]! m7 d5 G16. _* T0 K7 _9 S  O& N5 _
    8.3 文本处理的五类操作
    * h" N8 F' D6 `5 ~8.3.1 str.split 拆分
    3 s( z( t, N, P/ G# T  str.split 能够把字符串的列进行拆分,其中第一个参数为正则表达式,可选参数包括从左到右的最大拆分次数 n ,是否展开为多个列 expand 。
    6 M! i1 x* C3 H
    - }, s6 R3 D+ V- ls = pd.Series(['上海市黄浦区方浜中路249号',; C$ C# R) H( Q
                '上海市宝山区密山路5号'])1 g* k5 j7 U5 v  U9 \5 P
    6 n: q+ d8 O2 p2 G' n: D& L

    0 B4 d  ]: l* m5 V5 As.str.split('[市区路]') # 每条结果为一行,相当于Series
    0 N9 v, n4 ~- \- b  i! i& eOut[43]: 7 N& S. Z/ x% e+ X. @
    0    [上海, 黄浦, 方浜中, 249号]
    % F5 K8 y$ t6 [- r9 i1       [上海, 宝山, 密山, 5号]
    9 f7 R$ i* }0 C1 ~; `dtype: object
    3 Z9 _3 t" F7 z! `7 q) i0 Y7 h0 x
    s.str.split('[市区路]', n=2, expand=True) # 结果分成多个列展示,结果相当于DataFrame
    : L% c9 w3 `  j0 YOut[44]: 3 j7 l9 i* a3 C) ~" S
        0   1         2
    : R. p+ p1 R1 W+ o! K' ~" `0  上海  黄浦  方浜中路249号
    2 Q) R2 n/ F, o" @- }& d2 Q1  上海  宝山     密山路5号$ ?( M" Y7 h% J4 W6 {1 W
    1
    0 j/ U8 a- h' o: N: j( F0 U; t27 G- }& M; ]# U! Z/ L: K
    38 i; Q6 k7 e9 o" \
    4$ m/ `: h" g4 A+ N
    57 s0 x& b! a" _3 P8 b4 j& L
    6
    & T4 u# P0 ?/ X7 X3 A# A! }1 N75 \0 J) A! D8 o# r# d- u
    8+ d- Z6 M; Z2 U! u- n% k5 J
    9
    ' w' V. i- y# Z10% ], G% x& H3 \( f7 W/ j$ x
    11
    7 T, E# D0 b: p6 z; y) X: A$ H$ V12% G- m2 k8 |& g3 X" G
    132 |, C/ ^- q3 c8 w) _
    14; ^4 ~& }' L5 o8 J5 E2 }' S: U' N
    15
    ' Y6 j& b. @7 j8 n2 }0 v- l# X2 q) T  类似的函数是 str.rsplit ,其区别在于使用 n 参数的时候是从右到左限制最大拆分次数。但是当前版本下 rsplit 因为 bug 而无法使用正则表达式进行分割:9 p8 @5 ~% R$ w! d! N) U

    % m) P5 E  J- A! Q/ P6 ?  o: w: Is.str.rsplit('[市区路]', n=2, expand=True)3 j* Z3 {4 q  d- V* `
    Out[45]: 2 C; R& @  z4 U# m; p6 u! p
                    08 s. r+ }( B1 v3 I" q1 }/ m8 r
    0  上海市黄浦区方浜中路249号
    : k- Z1 @7 ~$ U- N* j. g5 f1     上海市宝山区密山路5号  \: R. l3 G: g, V/ r  a/ G6 @
    1
    + R. M' O4 N' G. n) E6 ~29 H3 e& x4 V- K. ]/ u4 }
    3
    . ^0 E5 T; A3 [, e. k, m! x4/ f2 b4 s% V. C4 U6 `- D* O
    5
    ' P4 N" Z1 D2 R2 k8.3.2 str.join 或 str.cat 合并
    $ P- w5 D* z, H$ p7 c5 u: P$ xstr.join 表示用某个连接符把 Series 中的字符串列表连接起来,如果列表中出现了非字符串元素则返回缺失值。
    2 L. s7 p2 R' h, _# d- Hstr.cat 用于合并两个序列,主要参数为:1 L& F0 b' \# F: _2 {& a
    sep:连接符、
    ; {, O) g% J9 r) y7 X' V* c% E8 _8 Ujoin:连接形式默认为以索引为键的左连接
    / ?" |$ i8 |# d5 R; q0 @na_rep:缺失值替代符号
    7 j* D+ p# x7 X+ ]. W& Is = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']])/ B8 K, \8 I( ?1 M
    s.str.join('-')$ E7 L7 h# @8 h- H% V5 o6 \
    Out[47]: & p' R- f$ i% @7 l8 L3 q
    0    a-b
    ' z; k7 H( w3 V/ S: @: X+ A3 v( Z1    NaN
    5 S' q9 Z$ Q* o( i2 e5 v9 K- b2    NaN; `: @! b( p6 V" D
    dtype: object
    0 r4 y3 g# _& t; A. I% P7 d  R1
    / l& W) H  R) T) y2
    $ D: G) ?# l+ M4 Z6 k5 M3
    ; }6 ?: y1 y& X# f# f5 I41 z2 b! I' F2 F2 A: Q4 @
    5
    8 g# z& [4 l& @( p( I1 M0 o" w1 f6( ?9 x' T$ [8 E
    7
    $ U4 R& C2 D! t, N, f$ ~s1 = pd.Series(['a','b'])
    3 v9 W* E2 X9 X( e$ Fs2 = pd.Series(['cat','dog'])
    , S0 d! m8 t$ Q. L: ds1.str.cat(s2,sep='-')
    ! F+ c" O6 \. Q! p  z+ K: [+ hOut[50]: & D, S4 Z, k# h: @9 S7 I5 _7 s
    0    a-cat
    5 d1 ^: l2 W5 K$ a3 c4 a" q1    b-dog
    - G- S$ J1 m; u2 i$ u- Xdtype: object
    # }* O! b* h& j1 k% a; h3 H2 V- S) s4 x" k8 _. E; P" Q
    s2.index = [1, 2]
    # _0 m' i+ R6 z0 `2 T  Cs1.str.cat(s2, sep='-', na_rep='?', join='outer')( }' Z$ w, o- v, `: y
    Out[52]: 7 ?$ B$ v0 ~( C) ~$ S) S
    0      a-?! A8 J& l8 ^$ F
    1    b-cat; c- F; N; l8 F. G7 I. S  c3 Y
    2    ?-dog
    0 f/ F/ W+ S, p( b1 U5 Ldtype: object# O2 e8 a. b1 h; X
    1+ i# [7 ]9 [3 \* W7 ?  a' l2 G7 l
    27 Q+ W0 {* ^, K' v5 F; T
    3) e3 f1 x' c' c+ x( }8 [. N% D1 S0 n
    4
    + r# `1 e2 p& K$ [, y57 f" }* H* D" H' D- q8 B& T0 Z
    6# s6 ]" c9 h( X
    7
    9 ^9 N" K0 `; R: d6 r5 p8
    # L8 Z8 R9 i2 Q9+ O' B) h# ~! e5 ?+ \. [- b) Z
    10
    9 \" U+ Z5 K$ a; V( }11' I+ l4 @' P! V
    12
    7 Z# R" x" n, p2 F7 _" _8 L, j: L  W13+ w' R; Z6 M( T: T. q) C+ I
    14, t; f& o+ K7 k
    15
    # q' [2 b1 q+ v) a! ]+ ~% l- _) u- k8.3.3 匹配/ C. `" S  ^2 g: V" o1 l8 U( a
    str.contains返回了每个字符串是否包含正则模式的布尔序列:2 e7 J1 b  v4 h  c' f
    s = pd.Series(['my cat', 'he is fat', 'railway station']), W4 V! y% L/ ^5 W7 v$ w9 @. j) x. I. z
    s.str.contains('\s\wat')
    , X6 a" P. B; Q; S6 i, p8 W
    0 C" G+ e# f* l% @9 E0     True  \1 @$ h$ W( e# X2 W6 s  P6 u
    1     True, K1 t# L9 ]! P5 n# z& F& w
    2    False
    " G3 E) r& x2 ]. edtype: bool$ U$ r! M* m4 Z! O! t; ~
    1
    ; x1 Z7 Z+ _% M8 l2; ?! ]* X+ V7 e4 y0 [& n3 v
    3
    $ S$ l; W! h* @% w+ K- F( p4
    ) M! g& S- G. G8 K% I8 C5' N9 V: A5 k. W! a& ~
    6
    ' F! `4 x1 Y$ Q6 o! W7 C* R' O, W7  t/ i  V2 O, f! _" O  \2 B5 Y4 y
    str.startswith和str.endswith返回了每个字符串以给定模式为开始和结束的布尔序列,它们都不支持正则表达式:
    # P- b* D  D0 S7 F, A; Cs.str.startswith('my')
    : J  N4 h) ]  b" Y' f- _1 M2 {$ x" h+ \0 n0 l5 G$ s- o' V
    0     True# w3 O4 a6 j& E, z
    1    False+ h: }/ d3 v1 N" h$ e- f+ k3 F
    2    False9 G/ X2 z, h+ i$ ^6 L% E
    dtype: bool
    . w  x7 n9 m$ ~4 X: z3 u8 ^1 Q1
    1 X7 k, k9 Y0 f" x* n1 k2
    8 q; O, l: J# u0 b3
    ! P) D4 s+ R1 I2 I" z* H" X4: \/ Q# m: p. v* v; V: s
    5
    4 r, Z$ w! d4 H) g. p8 M, z62 ], `! v; N8 a/ ~* t$ G8 ^9 c
    s.str.endswith('t')1 K% w4 h9 x* g* ?

    : o% g+ d  _* ~7 o/ ~/ s0     True
    / [3 l6 U+ q  Q9 g4 V1     True! ^$ Z5 \; y' N9 C# x
    2    False) y8 l; P9 f  T# q5 W$ ?1 b% m- U
    dtype: bool& z7 u1 r$ t( f; s# I: b
    13 s4 ~& A" Q) U& n
    24 m) D; K, f% Y
    3# C' D1 N% \/ `1 N
    48 ]; i0 _/ t" \7 f5 {7 B& K
    5. N* @; Y4 Z, r1 b: Y$ ^: \
    6. n/ Z" M$ b2 l/ g0 ]/ `2 _9 |2 H& ]
    str.match可以用正则表达式来检测开始或结束字符串的模式,其返回了每个字符串起始处是否符合给定正则模式的布尔序列。当然,这些也能通过在str.contains的正则中使用^和$来实现。(貌似没有python里的search方法)* Y8 U; ?3 z2 {3 A
    s.str.match('m|h')2 \7 i' f7 q: T% e/ k/ o* \& M
    s.str.contains('^[m|h]') # 二者等价
    $ i: L* q/ j6 W8 r' S- y! g. ^; c4 c9 J$ U2 O
    0     True
    ; t$ }  ]* A4 l- b" p3 C% {( [1     True
    . p+ K. J0 R1 M% @9 j! w2    False
    * {/ I% }' Z6 Tdtype: bool$ t3 h0 U4 N% i0 Q
    1
    4 c( z7 y  q8 v' V2
    : a- K' O( |" Z# O. U3
    , T% T! n! _$ h/ i: j  _  c4
    7 P( U9 [+ C. M2 j5
    # |: s9 O+ H% N& K, E, x5 E/ O6
    & c6 T0 S' C% Q7
    5 J$ K. `0 h  Z$ `9 D; R2 Ws.str[::-1].str.match('ta[f|g]|n') # 反转后匹配( O; _1 f# I0 N7 R- C. k; j7 t! m2 ^2 f
    s.str.contains('[f|g]at|n$')       # 二者等价; q' Y' G. f5 C, J

    - ]' V/ P! b8 f7 T0    False
      y$ }2 H7 L0 @" ~* V. {1     True
    * d$ o$ ~, M: W( |$ p6 l2     True: o6 L3 W9 s5 A
    dtype: bool
    : n# i- @& d9 X# v2 s( |! W1  Y. S9 D: w0 F$ ~/ @) G/ P2 f
    2
    8 s: f2 J( Q1 B0 ], H( M3
    7 k! [/ O( R2 ]9 E! ^- z4
    - X9 U( z, K: o% u8 w5
    2 n( W# r: B& o% w5 @6+ {7 {0 l2 S# R5 m  ?( q5 h
    7
    $ o/ T9 ]9 X" {8 \' o* u& m  Wstr.find与str.rfind返回索引的匹配函数,其分别返回从左到右和从右到左第一次匹配的位置的索引,未找到则返回-1。需要注意的是这两个函数不支持正则匹配,只能用于字符子串的匹配:/ R# G. I8 @. u7 w* }
    s = pd.Series(['This is an apple. That is not an apple.'])
    ! h- Q% O4 @- X
    / T. m% _. s$ D: E0 ^) Rs.str.find('apple')
    / D; J9 K. @, eOut[62]: 3 p9 z" ~4 D' ^3 l
    0    11! D. A3 z6 R: @
    dtype: int64
    : I* n9 ]9 k7 u7 z6 J  x  z; y
    0 U+ J1 ]; [2 S" K1 S$ v3 ms.str.rfind('apple'); W0 p7 c9 K1 ^- B( H
    Out[63]: ' {; I# ?, C. ^' p. ^
    0    33, t0 X+ F- R" ^% C
    dtype: int64# w, }/ G5 }4 o& d; `3 u
    1
    $ e* L" t& }( O3 t) z: ^2
    5 B& C: Y, k8 ^+ l  b0 t: P0 n3
    " B9 K6 I/ z6 y# |2 g4
    2 n0 ^# ~4 o5 {2 G& N. T) L) r5
    ; y7 V3 h$ d+ M6' X- o( K% d, P
    7
    " U# S3 v7 h  A  W: D$ `- |* l8
    8 k8 X) u6 s# l9
    8 h2 x" j, f5 z; ]( d1 A105 M) A6 |1 C1 Q6 x
    11
    2 |# j- b$ ?6 z6 d0 A: A: Q- ]% g替换
    - p; m# ], I6 c: Astr.replace和replace并不是一个函数,在使用字符串替换时应当使用前者。* c* n. |' g9 C" t1 j
    s = pd.Series(['a_1_b','c_?'])
    1 m8 e/ A; ^) k) |1 ?# regex默认为True,表示是正则模式,否则第一个参数内容表示是单纯的字符串,也就是匹配字符串\d|\?
      i  x  @  z& O8 Os.str.replace('\d|\?', 'new', regex=True) 0 a6 h' y/ ^# H- @: L7 B

    3 R: O4 v; N; X) w/ d  s0    a_new_b; P. s/ z. O4 K$ u# h
    1      c_new4 e3 m& b. i6 o& M
    dtype: object
    ) N: e% C' H' q1
    : a; d$ v/ a4 u, Z" M2
    ) h( j: u; c" U+ J1 o, Z39 [: {/ q* x9 H/ X  P
    47 i, {$ ]' G- [  K8 G7 O/ r% u& {
    5
    3 Q# y! v+ Z! r; j! P: c7 v5 @6' {& J' I7 l$ Q' T( n6 G
    7( A8 z! w& [  K# S  O) ^: r- F
      当需要对不同部分进行有差别的替换时,可以利用子组的方法,并且此时可以通过传入自定义的替换函数来分别进行处理,注意group(k)代表匹配到的第k个子组(圆括号之间的内容):
    # [9 x4 A! U. A) u1 g/ s7 f& h8 S- K: b7 t! v, \5 F
    s = pd.Series(['上海市黄浦区方浜中路249号',
    # x* d: @0 U  [! v# t3 p( i# \                '上海市宝山区密山路5号',5 z/ Y4 e, }8 s) F  A
                    '北京市昌平区北农路2号'])
    * b. u6 s- H8 s# l1 spat = '(\w+市)(\w+区)(\w+路)(\d+号)'9 [8 M6 Y" ?' i6 j1 {
    city = {'上海市': 'Shanghai', '北京市': 'Beijing'}
    ; b2 h& i' C: F7 wdistrict = {'昌平区': 'CP District',
    7 w1 w7 n( ~5 t5 ?+ K& a- ~1 w2 C, Z            '黄浦区': 'HP District',
    / [- @- |7 F- e( Q- |$ U            '宝山区': 'BS District'}
    ; |  v' q  [$ o) u6 o% L; |! yroad = {'方浜中路': 'Mid Fangbin Road',
    5 I: f6 P$ v% m0 A" m9 z        '密山路': 'Mishan Road',
    " e; ^! v% F) Q; L8 X, v4 c        '北农路': 'Beinong Road'}
    . t8 S5 R, n( i6 edef my_func(m):( P9 _5 T9 M: i+ z- B6 o
        str_city = city[m.group(1)]# [* t3 V, b* v' Y' z6 i
        str_district = district[m.group(2)]. t4 x- L; B7 w: M- f9 M
        str_road = road[m.group(3)]
    4 u6 I6 ]2 B2 F. s/ K7 k    str_no = 'No. ' + m.group(4)[:-1]0 K: e. t0 I, e) M: [5 j
        return ' '.join([str_city,) t9 P1 Q* i1 _  }0 ?, a5 z7 x! \/ {
                         str_district,
    9 A9 r# }$ @  T- s/ B4 Q* i4 }                     str_road,& l. T, C9 y% I% Y; s! E4 a
                         str_no])
    ) h% t. u! J7 n% W- Zs.str.replace(pat, my_func, regex=True)( e) ^2 R3 K/ S! ?$ i$ P- c
    & K6 v+ G4 c( d% \5 Y
    1- E' U3 X/ c1 S* q
    2$ M# o6 D3 d1 {) ~
    32 n" `; p  A! Z* D6 J* T
    4
    $ {8 e, w4 x/ t; W- @! J5. M6 X& O/ S4 \' l! O) \3 D
    6$ I) ^4 H) B5 z: h" |1 M. B
    7+ W8 _% i1 @2 X5 v! M! }
    8
    5 y: k& }! X, e- K2 \9
    5 U3 q3 ^2 p: n4 c8 c10
    % I- H7 a6 a$ `11
    , J/ O6 ~: Q% x5 J9 z12
    8 E' N& A4 ]7 ?7 f7 i  y13! K" ]2 N- r8 U% J. v+ X4 c, M; ^
    149 S9 Q2 _/ S, N6 A( s- l; J4 K
    159 N7 g; i1 ~& P4 f: a8 ]5 O
    160 a% q1 ?4 a  t" h- t$ y: U* n
    17& n" ?# m" Q- o9 W, D
    18
    2 J6 q. y3 d/ k) }6 T19; e+ n8 e+ h& e" q- S- I# i
    20: Z# [% e& r9 e! R5 R( @8 s
    21
    / o( K1 G6 e+ `0    Shanghai HP District Mid Fangbin Road No. 249
    9 Y. O% l; ?$ e- ~8 @1           Shanghai BS District Mishan Road No. 5
    % u$ N$ A3 H' g9 F6 p' U2           Beijing CP District Beinong Road No. 2
    ' g3 R1 |! L  ~4 S. zdtype: object
    - E3 q9 |' ~' Y  `; d11 o+ _- V  M* J- u; Z  `9 O
    2$ R- L! \% a! Q4 g' A  R; P" \
    3
    6 x! Y5 X- M" ~' I1 I4" Q- q' e1 a7 v* n; t& |; K4 Q- Y
    这里的数字标识并不直观,可以使用命名子组更加清晰地写出子组代表的含义:
    ( v- n0 g1 E: C
    " S& ^- Y; ?! F. X; g# 将各个子组进行命名9 S7 L9 H, U9 R, P' L/ w
    pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'
    3 r; ~: W4 y4 _# q, ]def my_func(m):
      U6 ~6 g& G  G" B0 J5 m1 l    str_city = city[m.group('市名')]
    " X  q8 k0 x2 {8 o; N, ?  W& [    str_district = district[m.group('区名')]
    . c0 f8 x/ s& Q. P7 A* \$ |    str_road = road[m.group('路名')]
    ' s( h# X4 r3 l) n    str_no = 'No. ' + m.group('编号')[:-1]0 j& M  t1 r% u  E, q$ ~4 g# C
        return ' '.join([str_city,
    / b/ [  D  x" ~- V5 ~$ C                     str_district,
    % P1 ^% S, J8 r0 o2 E" I                     str_road,
    . C& G" S/ _% l# J                     str_no])
    / b, p9 D, w0 ^% L) X) D% ~2 cs.str.replace(pat, my_func, regex=True)4 X" l$ x  N( y+ z: K
    1
    3 i3 w0 b1 o) J8 R! F& z29 [+ {: h' M" C0 c- n: B% n6 _
    3* d6 `* m( w. Q9 n# {" i/ a
    4) j! K: y8 k& v' r  D# j
    5
    ( A! o$ v: m0 j; _6
    % n: I7 q! G: U5 G7
    : e8 Y8 g% B: g. f8
    8 W% a- W# [( F/ M( E6 N4 B( _9$ }; y! O! j' R% `8 j1 _% H. U
    105 Z2 ]2 }* _) A( ~& c7 i) I8 Y
    11) d* {. i, V$ s
    12
    ' @+ Y# r3 Y) m) k$ m- }1 W9 Y0    Shanghai HP District Mid Fangbin Road No. 249/ H- Q( X, v& y; h
    1           Shanghai BS District Mishan Road No. 51 A( j& I" H4 a) H  r& i2 S3 I
    2           Beijing CP District Beinong Road No. 2
    # ^( p6 w! W( {8 [; ~dtype: object
    % I- l% E9 U. j. Z& m+ ]7 @& x1
    & z/ O% S4 f' |8 j& {& o9 I" ~2
    $ d, S; Y, u# L( u2 e; q+ G30 O1 P) l5 r5 F8 _" h6 i
    4- ~) f. l8 |6 e" R& r$ L+ s  ^
      这里虽然看起来有些繁杂,但是实际数据处理中对应的替换,一般都会通过代码来获取数据从而构造字典映射,在具体写法上会简洁的多。
    ! x! Z  Y, H& l1 @  [) B
    1 I8 Z5 e2 E9 T8 F6 f3 a4 [  R8.3.5 提取) m' z% N" o: Y, F4 B2 T' d
    str.extract进行提取:提取既可以认为是一种返回具体元素值(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的str.split例子中会把分隔符去除,这并不是用户想要的效果,这时候就可以用str.extract进行提取:! _9 z3 p  p% J% F$ A) D7 t) _
    s.str.split('[市区路]'), f0 B# }: j' W
    Out[43]: 9 j' y1 r9 }, F9 ?+ O
    0    [上海, 黄浦, 方浜中, 249号]
    8 H  C; Z& h! f) ?6 w; N- w$ c1       [上海, 宝山, 密山, 5号]# b6 b# o6 k. p8 `% r0 \
    dtype: object' {! r/ r* ~8 c- ~! T1 z+ Q

    + I& `2 K  B! \1 L3 |9 dpat = '(\w+市)(\w+区)(\w+路)(\d+号)'
    8 ^& q4 t% x0 r  `s.str.extract(pat)
    ! n4 ?, o% n9 v7 s/ l9 [Out[78]:
    ; K0 C4 k* k2 K    0    1     2     3+ w; {* i' F/ O
    0  上海市  黄浦区  方浜中路  249号
    , M4 V6 B) \# H/ M% C; o) C* V1  上海市  宝山区   密山路    5号
    , X: o: o) y5 G2  北京市  昌平区   北农路    2号# X" v0 C, p" \1 `& H
    1
    % O! a% e/ g1 m2
    : s5 p, _9 R+ q7 v1 c1 B- S3
    : w5 _6 \$ Q  B7 x0 J  H) B43 W3 m2 ]$ {$ }! _1 d8 _% Y
    5
    1 x9 W1 \4 `2 K3 @6. X- S) z* T  B* q+ D0 B
    7, I3 d. V7 [# z8 _# i$ G/ N; a: y
    8
    3 r7 {5 R4 i  S/ w% w9
    3 k2 Z2 n# m6 _  s105 k  S& C" z3 `; i$ s, c
    114 u7 A# X- _- n( n* j: _2 E
    12: B1 r- a6 n9 n9 }! e* |
    13
    ; j$ ]! _* ]1 E' g# K通过子组的命名,可以直接对新生成DataFrame的列命名:
    # S0 v8 a1 Q* t" |2 p8 E' z; O8 Z# I
    pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'
    5 i$ V# y6 H" w+ D% J* W: i- As.str.extract(pat)
    " w+ v5 k( O; C7 l1 ?; C, nOut[79]: ) U1 M  w0 W$ M$ v! Q* R  W+ `) C
        市名   区名    路名    编号
    * T9 B2 w8 S; r" o  N! ?2 T% M0  上海市  黄浦区  方浜中路  249号
    ; m- B& G) }2 r- k" |7 D! U1  上海市  宝山区   密山路    5号
    ; S) M% e" g1 w6 V8 `2  北京市  昌平区   北农路    2号( l6 |! o  l% E5 k+ Z9 ~
    1! H6 M6 p3 }) a9 {
    2
    1 S. m; F2 g: n6 G) T, q3
    / f# e* u! B, P, ~' k5 H# x/ b- }4
    ( G9 L: V8 P* B( T$ J5+ R( H7 v0 O, b1 h0 H2 J" y* M) ]$ Y9 U
    6
    " Z5 d4 w: j7 R$ @4 w& j, x( y3 |" G7/ N: W* z0 _8 ?
    str.extractall:不同于str.extract只匹配一次,它会把所有符合条件的模式全部匹配出来,如果存在多个结果,则以多级索引的方式存储:; n# U' _4 F' z; l9 {2 c: m
    s = pd.Series(['A135T15,A26S5','B674S2,B25T6'], index = ['my_A','my_B'])& E" A4 K" j$ X# f7 v1 m
    pat = '[A|B](\d+)[T|S](\d+)'4 _$ U1 I% C: O$ {- P  S* m
    s.str.extractall(pat)
    , N& c2 T9 D) M% I5 e. m1 C& aOut[83]:
    ' Z) U5 X4 Y" q3 a# ~# o       0   1: K4 b7 V% v9 L8 z  o# K
         match         % W  k5 p2 z) `3 Z
    my_A 0      135  15
    8 x1 E  K  Z- d$ }& s     1       26   5  b6 y& B; l( W$ _, Y4 c2 s
    my_B 0      674   2
    : v) e6 W! D6 g+ }8 s     1       25   6
    $ w3 X& g" P9 d/ q1. p1 _" A+ ^+ v* ~7 S7 X
    2  ~! _7 g/ U# M5 ]
    3
    - e5 d% G3 l6 g4
    ) W$ {+ U* R0 l5
    # P$ z& p3 _7 O0 O: Y  N$ w6& Q  r" F4 B3 h
    7
    . O& m8 Q& J! f' q" ?3 {2 J8
    # P! l  V  m  \5 l$ x" E" d9" p2 y: i2 o: L3 B
    10' R& c' {8 `8 }# A
    pat_with_name = '[A|B](?P<name1>\d+)[T|S](?P<name2>\d+)'
    + S, Q' ]# K: \s.str.extractall(pat_with_name)2 ?% A) t" k! b  p* o
    Out[84]:
    " _: O/ L6 F+ s+ }/ j4 c* W! \5 p* J           name1 name2
      `  O* h9 M: i2 ?4 w     match              K* d! J' j% p6 |
    my_A 0       135    15
    ! a& Y8 x; f, r4 h4 H     1        26     5
    2 B4 O7 F8 ^. m' ~7 _6 {8 Hmy_B 0       674     2- c" }, x9 Y, s. I
         1        25     6
    2 b  m- c* z! f+ h9 @4 k% O' I7 u12 m: F; F& w& [
    2  F! [: Z3 `% |- i
    35 s* o4 A( j) j: |* _$ w  s! _3 ?
    4
    1 t6 E. P  W0 l# Q  _6 S8 v- `5& n$ Z& w; R0 _$ U. L
    67 X! t1 Z, G7 @5 @
    7
    # v: z1 _' ]$ _2 s8
    ' P( H0 k% J9 e3 L$ H9 P. A9 ]7 u: m93 J9 M2 ^  j+ w
    str.findall:功能类似于str.extractall,区别在于前者把结果存入列表中,而后者处理为多级索引,每个行只对应一组匹配,而不是把所有匹配组合构成列表。
    6 D- L" L8 R! Q. B3 X/ b% g% W" ^s.str.findall(pat)
    ; U! h  s: j- p" B12 D1 E3 |1 @) S8 M  a
    my_A    [(135, 15), (26, 5)]
    6 ?  ]; z. \4 G' ]$ ~0 Lmy_B     [(674, 2), (25, 6)]
      q; z) w. V+ v: q$ Gdtype: object
    / N- R9 F9 }/ \5 o. F. D1) s: t, f& s8 Q# H, K9 z' y1 x
    23 s% [" }, e7 q) M
    3
    ; \) {: A1 G: Z0 a$ J8.4、常用字符串函数
    + H- r  Q; ]5 ^1 B% j  除了上述介绍的五类字符串操作有关的函数之外,str对象上还定义了一些实用的其他方法,在此进行介绍。+ ^% t6 z. d, Y$ g. g) @
    ( P: @0 A* e. h7 b% H6 C
    8.4.1 字母型函数6 ~5 h+ q* W! H7 S: K' q$ x
      upper, lower, title, capitalize, swapcase这五个函数主要用于字母的大小写转化,从下面的例子中就容易领会其功能:
    9 u, C' y3 k, p! m! }
    & y, H3 a9 |# W5 J& P8 w; V0 Fs = pd.Series(['lower', 'CAPITALS', 'this is a sentence', 'SwApCaSe'])3 z* _  {+ g% ?

    ; D! n) |- z, U# j$ ]s.str.upper()
    " e/ j. y$ s; tOut[87]:
    5 p" p/ n3 ]0 C2 @8 r) C, r0                 LOWER% U8 H1 r( h5 R3 l# h: ?+ U
    1              CAPITALS/ ^2 A* E5 {3 A  R1 V
    2    THIS IS A SENTENCE
    / }4 _2 S% ]# i/ C3              SWAPCASE& b6 S- F: e2 P4 f& z+ }# J/ _4 [
    dtype: object
    % ?. y9 L  Y, w; f% m: \. O0 \! O5 {/ A# ~
    s.str.lower(): K8 i4 i0 t8 |# E/ \% x8 S
    Out[88]: , f, m7 {: `2 I6 c2 r
    0                 lower" X& j6 L8 |: l. o1 Q; U; t6 s
    1              capitals# \9 H/ E5 H, E! g4 ~1 ~" q: @
    2    this is a sentence, z3 h' _' `$ L0 {* Z
    3              swapcase
      |# N. u# I& f' s* I" ^dtype: object2 y4 C6 a* E1 x/ z' k2 d
    ' Z! u- a" j& @/ X/ t
    s.str.title()  # 首字母大写1 I: N+ T0 ^$ Y4 a, X
    Out[89]:
    : \: ^# q/ i5 R0                 Lower+ ^" I0 P& r* P9 C' C$ D4 r4 l
    1              Capitals
    . {" P9 o- a' O) _* b/ y, z8 c2    This Is A Sentence
    $ y2 Q  Z8 n* T3              Swapcase: k; U& z. v  S' @5 y, b1 V
    dtype: object
    , D7 |8 Y1 j6 }- t
    ; ~! e" b( _, a$ W8 us.str.capitalize()  # 句首大写5 q4 K# I+ |2 D1 g' l9 \: Z$ E
    Out[90]:
    0 {8 }+ O5 A9 [+ h; ^' ^0                 Lower
    : |: q. b% h0 V- `) m  U1              Capitals/ s9 Y5 k. q% k5 Q' s0 m
    2    This is a sentence
    , R  a; K' O3 n  b3              Swapcase
    - Z% P* _6 A- [' rdtype: object
    # ^& z) H% j7 L) g+ R8 z$ P
    ; K6 ]7 t! {& Z8 Ws.str.swapcase() # 将大写转换为小写,将小写转换为大写。
    " @- k6 a3 [; L9 vOut[91]:
    8 \$ W  |0 ]- a0                 LOWER/ {$ x& B+ V1 {! S
    1              capitals
    / V9 e0 r6 j# S; b2    THIS IS A SENTENCE7 a" e: Z. P$ E6 g
    3              sWaPcAsE! I" b# {* ~8 v- B4 Y: s
    dtype: object/ ^; F! p4 d! a* X& P& Y  H3 h% h
    - B3 u8 m1 T& J" w- s
    s.str.casefold()  # 去除字符串中所有大小写区别
    & t1 l4 V+ }/ d8 S8 V0 }% a, F/ e% \( X" i( ~! _2 a% R
    0                 lower, z" \" s$ l7 m0 s/ {
    1              capitals
    $ _( h5 _3 X- C+ p2    this is a sentence! w+ o# Y% C9 j1 q
    3              swapcase+ O, ?* j5 s; l: z/ T5 P2 w

      ^9 Z, G% R# V9 G! U! [+ l! W) w, n$ T1' i. t8 g$ r6 X* z) ~' ~. G) u3 E
    2
    1 R0 \  r, K. @9 f/ {* ?& z/ l& @* D3
    / P' s" P# A/ f# P; _+ o( \3 F43 W0 E6 A( W% l* K" V3 N
    5# X( w7 M4 K7 ^. o' C- V# v" Q
    64 v1 j' ]2 H1 J: M* I4 b
    7; s/ x* Y4 _7 d
    8
    & n/ \  y6 R5 U# W! u9 s5 V9
    3 W' X5 ~0 Q/ k10! }; c& m2 `4 o7 N1 H1 V
    11* v+ y/ `3 t+ w# U2 g) G3 P4 O
    12+ d, S& y5 B! |
    136 w. t( ]% H. C! f
    14
      D4 N. _1 G: O2 u5 X15
    ' [8 E# N5 U0 f; Y169 X+ B+ T  r4 I3 {
    174 l5 r: M5 L* Z1 M; o6 y
    18
    ( \: a+ A$ Y+ }' {7 B6 x( o- q19
    5 R% ]: x+ R. O* w+ C1 P' O20+ M7 ?# V) {6 s& e, p7 V2 q$ q
    21
    ) ^3 x& `! y& {5 E22. i% w7 o4 }8 f3 \! w7 h
    23- d/ X+ p% F. i% w5 F3 N
    245 C0 D) q( j; y
    256 B  ~. b; |* Z: t) n2 m
    26# G" Q$ d$ K2 m* \$ c: d
    27
    4 M2 k$ R; X- x. x$ s( g3 M28
    $ ?( T+ }7 {* B29. c8 l  {" M) T  [: [  t6 ^7 j
    307 a4 D! k3 I  q% f* [  f; K
    31
    & i1 s) B/ I' v32" I2 k8 i$ {9 T& d2 w/ n  B+ w5 Q
    33
    0 P; v7 T6 m$ M: b34+ S+ S* G1 v) \) r& d/ Y
    35; b/ I/ w  g8 U2 o' W
    36" z9 v9 Z& V6 b; l
    37
      U  N! s* X' u9 q& S, K% ~38
    ' `! m# V4 Y9 Y- K7 p- _39+ z( [" T- L  l4 O* q
    402 V. K0 d! S/ N# t, A; ?
    41- H! r2 o5 U/ C
    429 O9 l- B, M! G6 j
    43: H& e- ~7 A4 t* s. O5 H& s
    44* l( `. U2 r/ Z. d, h) m
    451 f5 _# f) m' Q, q/ v. I9 _
    46
    + ~8 ?: r+ d7 u5 T5 d: R47. w# k5 @/ L/ m$ U1 f3 M. t
    48# y4 Z+ H. D, v  g9 i. e! g
    8.4.2 数值型函数1 q! y! B  R* h$ T* h7 L$ I
      这里着重需要介绍的是pd.to_numeric方法,它虽然不是str对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。其主要参数包括:; N' i  g% Q8 J: A
    % Z% `9 }% b* x$ T8 Z2 m8 ~# e
    errors:非数值的处理模式。对于不能转换为数值的有三种errors选项:1 z& J3 |" u3 G
    raise:直接报错,默认选项# U2 Q9 v; g+ V+ _. f4 n9 E" M
    coerce:设为缺失值( ?8 h! g# A8 _5 X
    ignore:保持原来的字符串。9 X$ E; b% F2 J% Z' q3 j" n1 f3 d
    downcast:转换类型,转成 ‘integer’, ‘signed’, ‘unsigned’, 或 ‘float’的最小dtype。比如可以转成float32就不会转成float64。
    , L3 \9 e6 ]$ os = pd.Series(['1', '2.2', '2e', '??', '-2.1', '0']). k5 ~0 t/ h* \. H1 E
    5 x' h. |2 I6 \! t$ H7 F3 H
    pd.to_numeric(s, errors='ignore')$ q% u* f* m- t5 G
    Out[93]:
    * u1 n8 l. U0 a1 X4 G8 y: [0       1
    8 I* ?) y. M0 N  I' W1     2.2
    2 Q. r% p& V/ i% B0 \* b1 I. g2      2e
    % U) r* z8 t) U+ y' b3 i3      ??5 ^3 S- C9 G/ N- c$ G
    4    -2.1
    6 m; I1 T; U- ?$ P  P) Q2 T5       0
    ; I- n6 A9 j+ rdtype: object
    ! ?, t% u% f% `6 ]3 D
    # j7 H* P5 p- u. [5 \pd.to_numeric(s, errors='coerce')
    : D# r) q; d  I6 M7 V1 bOut[94]: ' C8 [8 r6 C2 k( B" S. i) D( D
    0    1.01 b8 N, F/ P  @4 \, c* O
    1    2.2
    # A% _: \- M8 R, r: F2    NaN. i- }- j! K- p& @, O  k# B5 x
    3    NaN
    % u. t9 i1 K- W; e, M4   -2.1
    * y1 u  [! ~# S1 @6 v3 I5    0.0
    6 {7 h  c* c7 M: p0 E3 cdtype: float64
    $ U" p8 r/ ]$ N3 v: \5 |. @0 z8 r: i  _6 M
    1
    - }( n: r7 U1 W2
    ! Q) F8 z( S+ p: Z  e( m6 o( n3
    $ ?: p6 G( ?- ]3 b2 V/ S4
    9 }$ `" L$ b7 A5
    $ v0 c2 \- e1 f6
    : @/ I+ _, |- ~, f- t) {+ W3 [7
    5 ~0 X8 h; X7 K' d' I8) j* ?+ Z+ e* U  @% p
    9' ?$ A# B! R5 f, y8 W" B3 v
    10) j2 \' z# D+ X% M( D+ I' Y
    11
    # }4 B! B! X% I* j7 q12
    1 y* y2 X$ f6 M; j  l) n13
    ! c6 m! ]0 o( \2 w+ L' m9 g7 I+ m148 {* F. J8 ]/ d$ T& L
    15
    $ g7 X, @! I) B0 G$ H16
    : f  p6 T5 b2 V1 x1 s& ^17
    ' \( g2 L9 A/ a4 Z( i18
    / q6 |& t- p. n1 b3 Z: t19# c6 y4 x1 f! x4 t3 E+ h6 ~
    209 O6 Q% F# q! L3 H/ t
    21
    7 N+ D, X! \8 E2 N. A  在数据清洗时,可以利用coerce的设定,快速查看非数值型的行:8 f- J- E- V- F" }% Y4 J' d: _3 F

    6 T# ]' l# w/ l. M+ js[pd.to_numeric(s, errors='coerce').isna()]' h$ P+ X! P2 n! q' A  \8 r# b* r
    Out[95]: & k+ D- }2 t6 C; k# L
    2    2e
    " w' [0 f2 `$ z( l" U: F3 J& Y3    ??
    * Q/ L5 c2 L- }0 R. u6 Q# Jdtype: object
    + t7 A* V! s0 g/ G; `1
    % \* p/ i( e- u! ]/ ~7 J9 k2( ^3 H" O  z+ A% \* E$ v3 @
    3$ _4 F) o; `, t. _. }
    4. w, W$ ?+ C& y- x
    5( a4 O4 e0 W, X& ^% [
    8.4.3 统计型函数
    " d1 l! c# p, V  count和len的作用分别是返回出现正则模式的次数和字符串的长度:
      W/ N/ d& ?. w
    & f. W/ j) o% Gs = pd.Series(['cat rat fat at', 'get feed sheet heat'])8 ]. r0 Y/ i1 W# Q$ h
    3 M, a% |7 s8 s( w5 c1 I. u* t
    s.str.count('[r|f]at|ee') # |左右两种子串都匹配了两次! ]4 u( |% t6 R; ^( ]: k5 T' [
    Out[97]:
    4 ~6 x0 F/ l/ q. I8 o4 d: m0    2
    + j) P6 V2 ~% c1    2
    1 B' l& E9 ?3 Y& o% xdtype: int64' B  E7 k1 F. Z( D$ B! s' v# |3 n  i
    " `1 G- l3 C4 u2 g
    s.str.len()$ i0 y# S0 v% [
    Out[98]:
    ! v" J' ?: l- O3 v& Q- P( z. q0    141 N* h: [4 Z0 R/ ]/ h
    1    19
    & V! l1 @' O' r9 e7 odtype: int646 d/ U" B& [) @  W$ [
    1$ i$ ]0 C+ _4 a
    2
    . h" m( }! h* ^3
    : d' L/ t# }8 a4! o8 _* ^8 X( o) Q, k
    5! R# R4 x7 H6 d, Z
    6
    + ^1 P7 w3 M% F7  F9 [" H! U5 e
    8. n: U, l- q2 ^/ ~
    9
    . e9 r' e3 Z! l/ A1 H- l107 H2 w6 ?. }; D4 L
    11
    " o4 T' |% `! ?1 g12! c  \% ]7 f4 P5 h% e6 y4 v
    13
    ( v2 O4 \* Y7 s( f8 w8.4.4 格式型函数
    5 S$ l5 |5 g0 Y3 Y4 a9 H  格式型函数主要分为两类,第一种是除空型,第二种是填充型。其中,第一类函数一共有三种,它们分别是strip, rstrip, lstrip,分别代表去除两侧空格、右侧空格和左侧空格。这些函数在数据清洗时是有用的,特别是列名含有非法空格的时候。, Q6 D7 u( L: f( @- A
    4 Z6 r' r, C- _4 v
    my_index = pd.Index([' col1', 'col2 ', ' col3 '])8 i1 |! k9 o! P# u+ u& c  h9 `" ~
    ! G4 K9 Z" h$ q( k
    my_index.str.strip().str.len(). {! o; Q. h6 p8 i( i, m
    Out[100]: Int64Index([4, 4, 4], dtype='int64')' ?2 [- o2 [' @
    * f/ v3 M  y5 v! q" f
    my_index.str.rstrip().str.len()
    9 @3 Q% o: T9 b$ ?Out[101]: Int64Index([5, 4, 5], dtype='int64')$ b% q2 A" O( @3 z* B& U
    & j# k8 H( ~- q% B- Q2 i
    my_index.str.lstrip().str.len()
    # @$ ]" P5 R' A7 C8 iOut[102]: Int64Index([4, 5, 5], dtype='int64')
    / z: ?- G% \' V6 h$ T6 ?1
    1 i/ s. ~  U2 b3 Z. X2 o2 _2 P" g2
    ) m$ R1 E3 U8 A8 `! q, Y3
    ' w5 x% A: s# V4 y1 R# ?  k8 k$ f4$ Q1 X1 I; `" l$ f
    5* D  I& e, m3 v# S0 o1 ^  `
    61 \. Q, [2 ]- U: y( p% \
    7( X; {8 T) i% G# j
    8
      w: |* c) R  M; b. Y99 l7 ]% w  w1 O8 L% Z" S4 X/ k
    10
    ) t& C3 W7 g: p, d  对于填充型函数而言,pad是最灵活的,它可以选定字符串长度、填充的方向和填充内容:
    ! Q/ f4 K, _" s, _  I% W$ X1 N, ]9 }' j6 O
    s = pd.Series(['a','b','c']): \% p3 I( @; k$ p$ J& T0 V+ H
      V8 v* V2 q- Q: l4 c9 I" b
    s.str.pad(5,'left','*')
    3 \+ y/ T+ q. \( R9 c: zOut[104]:
    9 Q' H! }1 X. S' Y" H: f: s0    ****a$ M( J2 z( x* r2 X& \/ e$ e* z
    1    ****b9 W: N7 W9 v* V# }8 |' n
    2    ****c, D2 h# B( v: e: B0 e! o
    dtype: object
    1 P; s. f& t7 _5 W* B1 k# c, B  g3 a1 Y) g' K
    s.str.pad(5,'right','*'); o4 W5 h8 G, Y5 D
    Out[105]: 5 s) \: f0 C& a) E" h
    0    a****+ ?% E. v. o/ _
    1    b****
    * p" G- O- a! K' X7 l2    c****
    # c* C! x& V  X+ Z6 A6 C2 edtype: object. Z( {' a4 a. L' A+ u1 R* \2 Z5 ]
    6 h. G( s0 k$ b2 i
    s.str.pad(5,'both','*'); w8 D# t; \% f- {
    Out[106]: 0 m+ h: B' A9 G) `) I
    0    **a**; J- }2 @6 F( X
    1    **b**
    8 M0 r+ }; @& s% w5 e2    **c**
    4 T! k1 M( t. m# a- Cdtype: object
      e3 ]) O" l& }' v7 G8 D. s9 T  C# {2 E- j: Z8 p$ ^
    17 B& c/ U: a( e/ _2 H' ]1 W; K
    2- I4 V3 V/ O7 g, T
    3" |+ W% |9 h, C1 m
    4
      c4 w4 b* Y& T( q2 X4 o2 a9 t2 L56 d3 ]4 s6 n2 Q1 d3 h: J( V+ v8 A
    6; m/ n$ H- p. n
    78 r# R! I3 s; f
    8
    $ j6 t: r' t, v# w4 y. g( O5 q- v, {9; X+ }. K2 t' [2 x& O, L
    10
    ' ?& P/ K: _: f1 x1 C2 t" C119 n, D, }" j, H( v7 @
    12
    " J2 D+ s) Q, g* i13# Z+ ]1 C. @6 ]/ y; {
    149 E" `, G; [% R7 y4 J6 W
    15
    / [+ V7 A# \3 ]% g9 R" p16
    ( i$ r7 h/ p% K5 V17. b% z; O- Y8 B4 q7 B
    185 r$ K0 q/ u+ E" |
    19
    1 F; Q- {; b! c' `8 a209 t% N+ i9 X3 k
    21
    3 `# D9 `: U* w4 b- R1 o; I5 L229 K+ {( @: G  H7 R( T4 o( j# j
      上述的三种情况可以分别用rjust, ljust, center来等效完成,需要注意ljust是指右侧填充而不是左侧填充:
    5 T1 H$ g8 r* n; ?5 B: j+ T+ v  ^
    s.str.rjust(5, '*')! V8 S3 l( K- t, E  X. X5 i
    Out[107]: 7 P' l2 \9 j9 ^1 F9 K) L
    0    ****a
    ) Y! i% {) N3 H) y$ g1    ****b
    " U& O4 E9 l$ U7 ?2    ****c
    ) D) z3 F% j% L2 m3 ?: u; ]) G2 udtype: object
    4 w4 s# W& A; t; B
    ; _# C- k& E- ^: X3 \/ L* T! ]0 ?s.str.ljust(5, '*')) Y; {/ }  u/ W$ Y' L
    Out[108]: 3 m9 }. k  \  P% o6 L/ j9 ^0 K
    0    a****
    4 \1 d0 {3 B" c+ J  T1    b****$ J- P8 \4 r! H4 P3 _! @7 F2 E9 P
    2    c****( {- A1 \9 n4 I0 u
    dtype: object) X1 N* v. M! L4 q3 ~
    # w4 y* w, W. V$ x
    s.str.center(5, '*')
    6 `& k7 P. m1 u" B& IOut[109]:
    9 j  t( ~* C- y6 c0    **a**7 k5 R8 p  V) q7 @. R
    1    **b**
    9 L/ {( B+ s$ p2 E, x6 C# S" r: t2    **c**% V- v1 d/ M9 O4 I5 F
    dtype: object
    % I% o6 J, p6 a# b, ^
    $ c  M# H% B3 C; b1; _$ x) s* ^5 t2 `1 i: p7 Z
    25 }. o+ G9 m. @6 W8 `# x* N7 u! Q4 g
    3
    9 S$ l+ b7 x0 Y4 Z9 E4& T8 X* d* M2 C" d" V
    5
    % ?9 X1 k, Q. g/ |; t" \6% N& Y7 U8 A5 e$ Y/ h$ M+ Q- j
    7$ l& Z; Z% ?8 r, J5 P. b1 u
    86 E" K* Y/ y7 {; k, w
    9$ ?/ n  A. k, T2 e8 J: O5 {1 S
    10
    - B  q) d! u7 |# \* l6 J11
    ' k& z1 H: S- C& m/ x12/ p6 y5 R7 G& H/ a" q
    13
    1 }$ ^% D2 _$ }* Y% o14
    : G) S" Y3 D2 u* I; w8 c0 p. _" L15
    * e" Z* r# N; L167 Q9 p# a/ \0 O# q+ T
    17& H* O0 P' M3 C) n* G4 V3 _, d
    18$ j$ l5 X3 H8 @
    19
      B1 b/ r) s3 i* c& A' Y20
    ' p  Z5 V. `" v" D9 z  在读取excel文件时,经常会出现数字前补0的需求,例如证券代码读入的时候会把"000007"作为数值7来处理,pandas中除了可以使用上面的左侧填充函数进行操作之外,还可用zfill来实现。
    % z" R3 @" J) l- ]% @( C
    ; v4 i& H3 z. w# s- ps = pd.Series([7, 155, 303000]).astype('string')7 q5 B6 I4 r3 ^

    % g/ J" `9 Y0 o9 D% Ts.str.pad(6,'left','0')* X: e/ a" }9 C+ a2 w7 B* ?( \
    Out[111]: 6 z) U5 D' D1 x+ l2 n3 I: d6 |
    0    0000079 v4 W2 W% q, Z9 n: v: ^& F
    1    000155
    0 b& \4 R1 H+ D2    303000
    3 t, }% O3 T% x: T7 v4 hdtype: string: h" N5 X/ V$ D$ R

      V8 W& a6 D* t) P& Ls.str.rjust(6,'0')5 q7 z/ _8 w4 B( z( m- M  f
    Out[112]:
    - y2 P# G" p+ `, M2 g3 d0    0000079 T' F3 Z7 r3 X1 q
    1    000155. m5 a; y* w( _+ S# _
    2    303000
    . S5 N* U% D& h: E: udtype: string8 u+ H2 T. j; b- R4 D- E
    ) \! N7 v+ h8 @5 y9 J0 x; N2 f
    s.str.zfill(6)
    5 T9 T' G) O& y$ o! s% h3 P1 [( xOut[113]:
    ) a$ F* p/ G) `2 L/ N0    0000078 U' r) u" l( {
    1    000155
    ' Y# A) w7 x; [6 K7 m( u" [2    303000
    2 e& |  t6 {9 a" r7 }* Cdtype: string, M# L: G6 |9 c9 ^6 O) ~4 q
    , r9 s+ E* W$ M$ j" k( z
    1
    ; q: C% \4 |* A- y; h. Z5 x/ b; D2, b2 N9 W. f, Q% F* a  K
    3
    ! @& a0 \$ S) U1 z4: Y! J' s$ t" _0 `7 i0 ]
    50 i- d+ b; A, p' I* c! @3 T
    6# J4 r6 g8 T' l
    7
    : a2 c, r: R( D9 O6 N! {86 I( p* V2 I; w/ J
    9
    ) O) q+ t8 m. O( v( A$ z10
    # p4 j- J# D' s11  P- S4 e" Q1 b( {( S% r* g
    12
    : s% \$ _. v% t  V# S* d13+ n8 ^) U3 S" u
    14
    3 i" B6 @7 I5 b" w' Y% _# g15
    5 f% l+ F, y6 c7 w9 q4 U- G# Y& H+ D$ s16
    4 t" c' l  u/ l: y/ V17* t5 d. Z# q0 v8 e' b# ]) X2 h) Z
    189 M. Z$ T0 X# W" U( ~+ o
    19
    % {0 r* H7 Y' P# @  j20
    " p' `6 ?5 y+ |, F21
    : K. f' d: ]& F( c: A22" A% ~, C5 t6 |* ]( I/ T+ x
    8.5 练习1 N1 m* B+ Z$ r
    Ex1:房屋信息数据集
    9 x1 p2 A" W  Q7 M现有一份房屋信息数据集如下:8 S5 R6 @7 L6 s  u$ k) Y# I8 {. G

    ) h: D+ e+ @6 \" h( Jdf = pd.read_excel('../data/house_info.xls', usecols=['floor','year','area','price'])
    ! f. Y) |6 t( U4 L- ndf.head(3)( x( {3 M  |3 G6 l) P
    Out[115]:
    $ O; `. D7 l# O$ M5 V; \) t      floor    year    area price1 X- R0 y4 P: y" T+ G! B+ u9 R- U
    0   高层(共6层)  1986年建  58.23㎡  155万
    / i8 R" N2 p$ Q" N1  中层(共20层)  2020年建     88㎡  155万2 M/ X7 o( C0 U& e% ?0 E7 J
    2  低层(共28层)  2010年建  89.33㎡  365万! ~! F2 ]* e# U* B# P
    1
    0 y+ p: Z" N* p2
    3 y4 D3 B+ R% I3/ q8 J2 h5 y; ?* A
    4/ c  t2 F# N. }4 S6 b- e
    5* i, r6 @  C# c& _' ~; c9 O: P  p
    67 @9 e3 L' L1 D
    7
    1 m( Q; ]" T; T7 j8 ?. o将year列改为整数年份存储。
    : W0 O% n4 C$ `3 k+ K) t0 b$ ]* v' F将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。
    0 h8 ~" }2 I+ f) v9 \6 c计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数
    : q) n( G) D% L6 T" P0 B3 w6 i将year列改为整数年份存储。( Y/ l# E* n1 [& q, K3 G
    """1 _+ z. J, ^) Q9 [
    整个序列需要先转成Nullable类型的String类型,取出年份,再将年份转为Int64类型。
    6 C6 [- ~! x8 p3 }4 F注意,转换的类型是Int64不是int,否则报错。即使astype加参数errors='ignore'跳过缺失值,
    0 ]& i& I) U4 @转成int后,序列还有缺失值所以,还是变成了object。. `$ i; \- ?7 q* c" Z. N/ x5 b% ~" M
    而整个序列转为Int,就还是Int类型,缺失值变成了 pd.NA 。% D: i, ~# S  j8 `9 ]
    """2 m+ T1 D4 n9 d+ a
    df = df.convert_dtypes()
    & b& k4 n. |; Q% I; v8 |. Sdf['year']=df['year'].str.replace('\D','',regex=True).astype('Int64')
    + h. J. L( o3 V) Z# D) _" ldf.loc[df.year.notna()]['year'].head(), U% {, R: ]. ^9 p; d
    , s6 w0 B) x# w' R
    0        1986
    : [# ^% L0 `7 x% G% Y1        2020
    7 \% a7 M9 ^  j& c9 f# V3 U$ `2        20107 `) I" T: e9 }; t
    3        2014
    3 ?& J% U) Y- J4        2015
    & x' ~. }2 V: R1 z5 ^Name: year, Length: 12850, dtype: Int64
    + i# ^5 Q* g2 w* s
    9 q/ d+ u0 e" K1+ @" y% N& z6 v. @
    2
    3 |# V, S$ S) |5 a+ Z3* r( H4 u( v% \, U' a
    4% v# P3 L, ~& s5 M' d# g$ K% ], H
    5
    * K" a6 c" A# v) z4 O* q; G6
    % a$ P, ?( ?7 L6 Y" f6 G7' `7 P+ c3 ]$ R! l1 `) L" j, m* ?2 X
    8. [  J+ n8 q: A! _4 s
    9
    ( O; Y: w" B. X5 k2 h0 i+ j3 s' o100 {! d7 N+ _. C( x0 h
    111 \; \; @& A- |3 A
    12
    $ @* ^6 ]1 ]3 ?13
    ! a) ]8 ^/ j0 E+ {14
    3 N: Z; m& C" v% I8 V3 }+ t. ^( a15
    6 A" p) [( r% ]16
    ' h- L  i9 b& m参考答案:
    1 S' m6 l. ~" O
    , [4 o8 h$ J7 V1 C3 [% i不知道为啥pd.to_numeric(df.year.str[:-2],downcast="integer")类型为float32,不应该是整型么4 W, }6 b( `6 s2 }
    * i( S: C2 C+ U1 t
    df.year = pd.to_numeric(df.year.str[:-2]).astype('Int64')
    2 t' y# I6 }1 F( W+ g5 Idf.loc[df.year.notna()]['year']" e& O1 G2 \# M8 b; M3 _/ Z
    1
    $ m! {) e9 m" l. I24 t8 F, s9 k- Z2 K6 R* q
    将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。& l6 S; i% K1 ?7 R4 t9 Z6 U
    pat = '(?P<Level>\w+层)(?P<Highest>\(\w+层)'! i  f/ S! y; W3 U, X
    df2=df['floor'].str.extract(pat)  # 拆分成两列,第二列还是(共6层得形式,所以还的替换一次
    $ |8 z  J/ W. l) @1 e6 Qdf=pd.concat([df,df2],axis=1).convert_dtypes()  # 新增列拼接在后面,再次转为Nullable类型
    + j3 z% J/ z8 O' Z8 q  {: `2 z5 Gdf['Highest']=df['Highest'].str.replace('\D+','',regex=True).astype('Int64')              
    & p$ K: w7 Y. V$ }3 kdf=df[['Level','Highest','year','area','price']]
    * _' \8 ]4 ]) Y0 j4 j: k% @df.head()6 ~+ j9 p/ E4 x$ I( f  W( O
    % T) d2 W1 L9 J6 r
       Level  Highest        year        area        price
    ' A5 }# D' D/ B6 \* t4 |" D0        高层                6                1986        58.23㎡        155万1 `9 ?  D) W) s8 o
    1        中层                20                2020        88㎡        155万; Q, |3 M0 x6 T  l4 e
    2        低层                28                2010        89.33㎡        365万
    ; g% R4 I0 H+ A8 ]0 q3 D5 G: ^4 n7 r3        低层                20                2014        82㎡        308万8 O/ f. s" b! I) X6 ^$ i2 T& J
    4        高层                1                2015        98㎡        117万
    ( |$ }: s. D. U1
    & j4 l3 a3 e3 i- S2 I2, M: \4 X" s; k% G
    3
    # f. X% l2 T6 s! a2 U4
    3 ]0 u8 i" ~" S) y5) f" w- E, F/ h
    6
    6 X8 r; G5 H! ~9 o4 E2 @* s; ~7, d7 }& z5 F. \# t% J" C
    8" y* V# Z9 X  x
    9
    " a& [; s5 y6 l& l3 A2 E10
    $ H$ Y* C0 p/ ^112 J: P4 K6 V! E' F  W/ a# T2 f/ W& r
    12& ]: _4 w+ J: D/ d% X
    13
    9 i4 [, o! s/ k9 b# 参考答案。感觉是第二个字段加了中文的()可以准备匹配出数字,但是不好直接命令子组了
    : ^1 a8 p4 ^# ]* J* fpat = '(\w层)(共(\d+)层)'
    % `9 w9 c6 s6 d) J4 Dnew_cols = df.floor.str.extract(pat).rename(
    4 R0 B4 b7 X, l; M% u" ^) v  k; o                    columns={0:'Level', 1:'Highest'})) M- h8 P" o; @4 p. ^9 \- u

    0 ]. F: a2 L/ _9 w. X9 Bdf = pd.concat([df.drop(columns=['floor']), new_cols], 1); u% W4 m' H: S! V" Y* Z3 d
    df.head(3); @' @/ W' n! z9 Z; @" ^- M; C

    ! Y+ W) i2 r- F0 x( j* }& @Out[163]:   t! t# R" D( h' a) y1 @( h* n) ^0 z3 i
       year    area price    Level Highest
    3 y) C( l$ J( t* x0  1986  58.23㎡  155万    高层       65 J1 n2 U$ a7 W3 F, ^3 N
    1  2020     88㎡  155万    中层      20
    8 F! s8 w4 p! W2  2010  89.33㎡  365万    低层      284 r+ E5 C* l) X+ g, l# v
    1
    3 \# j- T0 q2 ~- y% j, x0 c, o2
    1 K; f% z6 R: R, \& L3
    " l* V- B; F9 }$ t) E44 t% a0 y' w* b; i' k* P
    5
    ( b5 c9 E2 R; Y# J* C. i( p3 p6- l# A: ?* J( z( Q! l0 y
    7
    0 F: K; n  v6 B. G8 ]9 p8; J8 H) ?- l. r/ d
    9
    4 t" M. g% \1 g9 _" F3 d, V10
    ( ~+ ~- W6 U9 z0 k( u1 p- C11
    6 u  ^' y; k" q  N12
    , I) M  E, k/ m5 q13( M# j9 @: }6 b: q9 }1 d
    计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数。' J: d5 R; C, e* N- B9 N
    """' @5 Z! ?3 `& Y1 m7 W
    str.findall返回的结果都是列表,只能用apply取值去掉列表形式% y+ M4 a3 [, F2 @
    参考答案用pd.to_numeric(df.area.str[:-1])更简洁) ?7 G+ l: l' b- V
    由于area和price都没有缺失值,所以可以直接转类型$ Y3 D% T; Z6 x4 E4 h' O
    """, K8 k0 a" J' [) _; Y) k$ z- G# K6 Z" L
    df['new_area']=df['area'].str.findall(r'\d+.\d+|\d+').apply(lambda x:float(x[0]))2 |) E0 N" y9 u- d) P5 J
    df['new_price']=df['price'].str.replace('\D+','',regex=True).astype('int64')* U- r3 N3 _! o8 q  p
    df.eval('avg_price=10000*new_price/new_area',inplace=True)
    4 B# R  Z! }# {" F+ c" o# 最后均价这一列小数转整型直接用.astype('int')就行,我还准备.apply(lambda x:int(round(x,0)))
    7 ?# K! t2 Y8 ~: C2 r0 q# 最后数字+元/平米写法更简单3 {2 {4 x, _8 j4 i! k
    df['avg_price']=df['avg_price'].astype('int').astype('string')+'元/平米'
    ( J8 f# u" b7 J2 ~del df['new_area'],df['new_price']0 S( D% g0 [& h( u% o
    df.head()) D8 l0 |: R( ~8 f
    0 J* Z9 C% k8 s5 I4 e+ n
       Level        Highest        year        area        price        avg_price
    - z, D) p% M* U! E6 ]0        高层                        6        1986        58.23㎡        155万        26618元/平米! Q5 T! u; D4 V+ R0 g/ v
    1        中层                        20        2020        88㎡        155万        17613元/平米
    $ _7 I5 Q' n" m0 u) e2        低层                        28        2010        89.33㎡        365万        40859元/平米
    3 S% K1 L" g- L9 t* c- T3        低层                        20        2014        82㎡        308万        37560元/平米3 z) P% T! D8 a7 z$ |
    4        高层                        1        2015        98㎡        117万        11938元/平米' q; F' z9 t' a/ E2 v

      _2 L$ C; t" z# }& ?7 x1( J7 c' {) O9 l8 x- s
    2
      |$ i3 n! s! l3
    7 M. Q; g+ a* x4( ~  e7 [" p$ X% B& i/ o9 i
    5
    0 {( e" O, u; ^6
    . M+ i8 V9 C2 z7 }% F1 s3 V8 T7. m" }1 {" F: b4 o
    8. W4 f1 H1 ~" S' F  v
    9
    ! m; j8 w2 d- K10. h; J. o1 c$ R$ t/ a
    112 f9 T4 J, j3 q0 s: W
    126 P- @' t9 w8 j0 |- y% m$ y
    139 _: Q. u+ m: f, X0 w
    14/ g- ]$ t8 X" }1 d# P
    15/ ^4 ~2 @5 R0 L+ c, L
    16
    4 a* \0 k6 q/ b8 q7 W& x17
    6 S; d6 V& I- s# V: U. O8 M8 \187 D) V; D, x- F  ^) p  w6 f, Q
    19
    ' C1 |) d% _) ?6 k  J2 h* R2 n20& H4 P% {" b4 Z* }4 |# b0 d
    # 参考答案
    9 V7 N. o' [8 x' q+ U7 ?  C$ gs_area = pd.to_numeric(df.area.str[:-1])
    8 D2 }# P" L8 Ls_price = pd.to_numeric(df.price.str[:-1])9 m: t6 m% V. D* A" ^6 L$ [% l3 `& S
    df['avg_price'] = ((s_price/s_area)*10000).astype(: a% z9 l* Z. J6 x% o: w6 M* D8 f% e
                        'int').astype('string') + '元/平米'
    , Y& v) n$ z% a. s! g, T1 r3 e  ~3 j8 P, u8 p6 H0 c
    df.head(3): n! C( p# V7 q8 r
    Out[167]: 2 j+ `. t* B3 |& P+ r1 Y- ~
       year    area   price   Level Highest  avg_price) I/ V# W# k, I* o
    0  1986  58.23㎡  155万    高层     6          26618元/平米- ]0 y7 X& _( p( l/ r
    1  2020     88㎡  155万    中层     20          17613元/平米9 O+ F6 Q- P$ c0 t/ \
    2  2010  89.33㎡  365万    低层     28          40859元/平米3 j, e+ i6 v- F
    1
    - x7 o& z& f% V1 {- _+ O/ f22 d  v6 M1 v5 E! V0 d2 m. }
    3/ ^6 i+ }/ r% j, n; k4 R
    4
    9 m4 }$ f: J4 j% S, V5
    8 A( S0 U" \. c, j0 V, T# M8 D68 s6 ^& Z8 t) n7 t# J
    7
    ' `8 a% b' N) ?: u& V$ U4 \( l5 t* n8
    " Y& s& y) ~# O* a# B6 a, C9
    & O- t+ }1 Q5 W% I$ C5 B10
    7 k' }1 M7 |8 ^) _. H2 a11
    6 z3 U: N. i3 |& I4 }" I12* W3 j8 z, ]/ F' ?& H/ p( r9 Y
    Ex2:《权力的游戏》剧本数据集, g$ q! Z3 x0 V# y
    现有一份权力的游戏剧本数据集如下:
    % q* Q( ]5 ?+ n3 m9 V5 w" v: k: @9 ]6 ~1 G
    df = pd.read_csv('../data/script.csv')1 X3 Z' r0 f$ V; v' r
    df.head(3)
    4 n% L8 a! |' _$ r9 B0 H4 J
    ) P7 H6 k" P& \2 u" y. [Out[115]:
    & \# P" c9 B6 [! b$ qOut[117]:
    ; E% _2 T0 Y0 u+ b# o+ B  Release Date    Season   Episode      Episode Title          Name                                           Sentence
    * T5 s- K7 S& h- e0   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce  What do you expect? They're savages. One lot s...
    4 T5 h$ m6 ]1 P0 q* a1   2011-04-17  Season 1  Episode 1  Winter is Coming          will  I've never seen wildlings do a thing like this...
    # j) ?$ O! r- H- _) @2   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce
    4 M! k/ p' P2 g3 ^" C0 U1; K2 `" x- `- h4 i; n# I5 B2 F% s
    2% M" Y) y7 U. x6 b  L
    3
    " {$ F. q/ u% p8 n0 Z' c/ X1 \4+ m" O# t- O0 X) f6 H% @
    5# K7 u( W; W: S  ]# v
    6
    . C6 E1 ?2 v3 H& C4 T1 T  X7) M" m2 U( P) r* u
    81 b: i; n( {. X; z  s  z1 s& z
    9$ `* M$ E& x# G" [! B
    计算每一个Episode的台词条数。  l. @, t, X4 E1 Y2 v& a0 ~( Z8 {
    以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
    + w. j. I  f- r( j% \; k" a8 |若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有 &#119899; 个问号,则认为回答者回答了 &#119899; 个问题,请求出回答最多问题的前五个人。& Q' V# }/ Q3 L0 a+ k* l
    计算每一个Episode的台词条数。
    ) P: `8 D* t' b4 qdf.columns =df.columns.str.strip() #  列名中有空格4 ~, {+ M2 m: J. Y
    df.groupby(['Season','Episode'])['Sentence'].count().sort_values(ascending=False).head(): {; L  X; Z, Y  k9 P! |

    # a$ x% t9 j0 zseason    Episode  8 Q0 g7 I5 ?/ r1 |1 I; @# ^8 r
    Season 7  Episode 5    505/ A/ g+ E- C* {; I: G; C
    Season 3  Episode 2    480
    $ M9 ~' W: C. G/ T! @5 tSeason 4  Episode 1    475
    1 l* a7 z8 V4 a6 P  w' c1 CSeason 3  Episode 5    440% ~* t, m( V3 H/ L. @5 `
    Season 2  Episode 2    432
    , h$ @/ Y( ^9 _1 `5 k+ k1
    2 n4 O- m9 o. `2
    4 Q& f! o& E7 z3
    ) N& g' m% w0 [3 n$ S* U4) x: Q3 i% Q+ m
    5) D: U9 E/ O4 M% m3 G# p8 x
    6
    9 B, V/ }( W; j! L' t7+ x5 |  v0 J, S3 j1 C
    8
    2 R5 m" J# g9 S! Z9 L9
    . t- d9 Y7 k$ a' m; O' q( g以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
    ' C. u# w/ l' p4 {& S6 Z# str.count是可以计算每个字符串被正则匹配了多少次,+1就是单词数  t* L7 K) n: D0 N$ X. y
    df['len_words']=df['Sentence'].str.count(r' ')+1) t* `3 l6 a' N) L# \& U. w( N. B
    df.groupby(['Name'])['len_words'].mean().sort_values(ascending=False).head()
    : Z4 h' l2 Y+ Y" i0 C3 G) L5 z4 B% b( U: D( m% ]
    Name
    1 z% F1 G- b+ w% F# Cmale singer          109.000000( h/ r2 M; d% R4 Y5 x
    slave owner           77.000000- T0 M0 w0 s* I
    manderly              62.000000
    1 C# f, e+ u* ?3 v6 zlollys stokeworth     62.000000
    " }9 E% A3 Z9 qdothraki matron       56.666667
    % G5 ~- `: |4 n/ B/ J& b1 QName: len_words, dtype: float640 E- K* D. E1 L% J, L' o5 f6 j7 l
    1
    9 X1 z, }) \: k! G' j26 @& x2 t' G# ^/ w
    3- U& D" L( s5 b, R# L, s; F
    4
    8 n3 F# m5 Y/ L( }5
    9 e/ L0 n3 o8 v, `) V; c69 ~9 l3 d" O! _/ i9 f9 [5 }
    7: i+ h  A* Q$ }; R% F3 J: r+ @% {6 h
    8" j( G4 M: I3 U( p2 X3 p" P
    9
    6 S0 H# h) ~3 b# m% L1 a5 _10
    3 [4 n" t5 h7 d11
      K( K. u! r: [" P" I0 H( M: T若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有n nn个问号,则认为回答者回答了n nn个问题,请求出回答最多问题的前五个人。
    $ g, _( \* Y. N3 V4 bdf['Sentence'].str.count(r'\?') #  计算每人提问数! b4 f( P  s6 ~) H! m
    ls=pd.concat([pd.Series(0),ls]).reset_index(drop=True)# 首行填0
    / m+ P* j+ p! x2 \8 b- zdel ls[23911] # 末行删去
    1 [# q. W" L; W# Sdf['len_questions']=ls' W) k  \% Q9 |7 f7 W
    df.groupby(['Name'])['len_questions'].sum().sort_values(ascending=False).head()1 M7 ]. S  P- q0 n2 U

    1 y1 K9 E, D: tName
    " G! v- A% ?: G4 j1 ^tyrion lannister    527
    : N3 b5 R9 [$ N* D# wjon snow            374
    2 t0 R, v% ], s+ djaime lannister     283
    9 P; s/ J$ {! O2 [arya stark          265
    8 N6 O. e. u1 ]+ J8 pcersei lannister    246. V5 Q- e: T# J  @  Y. t
    Name: len_questions, dtype: int64
    7 ^6 S2 ]' K, {0 O& J1 s8 N
    5 P. Q# L. Y9 z2 g, F# 参考答案1 k: M) v$ _" `! r3 K: |- E
    s = pd.Series(df.Sentence.values, index=df.Name.shift(-1))$ I" P; z7 a  ]3 ]0 g5 ?# i* c9 j
    s.str.count('\?').groupby('Name').sum().sort_values(ascending=False).head()5 @, D6 u: v% ]

    $ i. c& E2 a& c1 c! Z# m' w1 d+ m, |18 M0 a) N% `" c4 i9 h7 Y) E
    2
    3 ]: w( c1 e! c6 {' K- N/ A3$ D2 y- z! L$ t+ R
    4
      Q) F. J" H- M' V6 M56 k' h; ~* s+ v2 a; L
    6$ W; V/ ^" g' ]+ {
    7) d' N! B' {9 U
    8
    0 l, C. W% x& \9* l5 y. c" Y" l, M* Q5 r
    10
    2 J  E; g  Q+ X8 A1 a11
    7 I! @$ y& F- N" ^& o: T12* R! u/ p0 O  j) U
    130 s' m. M- P' o3 w- `
    14
    : P" y  s4 a' |: e0 P1 g15
    " }2 }7 |  u  o4 H16( t, U: u* A% c4 j+ e( @/ C- b; x# G
    17
    ; ~' M8 w2 Z9 r9 Y  y第九章 分类数据: c, u% T9 I; o2 T% H- x: e6 \
    import numpy as np- a# c4 ~* _3 n* e/ G, g6 n
    import pandas as pd
    0 ]9 \7 n7 U* B1 N1# m3 e0 q" k7 I4 ]3 t* j
    2
    5 `9 K# n6 M6 p  p) c9.1 cat对象
    # d. |: h2 \, ~- i2 q3 j- o0 D+ f9.1.1 cat对象的属性( |9 N: x) L, e/ a3 G' w
      在pandas中提供了category类型,使用户能够处理分类类型的变量,将一个普通序列转换成分类变量可以使用astype方法。
    " `; w$ a* d; Z# o& H
    ; s* l) n1 @* h) Edf = pd.read_csv('data/learn_pandas.csv',
    6 }/ K5 Z% _6 d7 K3 e9 D0 n* ?" N     usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight'])0 s1 y+ n& z. i8 {$ K6 d
    s = df.Grade.astype('category')6 u/ f: D. D6 j  x

    0 q; V+ ^' [. Ns.head()
    6 |0 j6 O+ I& hOut[5]:
    ( S* E5 z0 c& H3 Z% _0     Freshman  T6 B, O. M* C5 |# j
    1     Freshman  t3 S( Y+ ^, n4 y) u! B
    2       Senior9 ]3 Y7 N+ [) w8 G3 ^
    3    Sophomore
    & c8 U- t) S2 ~( [" D4    Sophomore
    5 x+ D' n$ z! pName: Grade, dtype: category
    ) q$ e. v3 _+ F! o+ zCategories (4, object): ['Freshman', 'Junior', 'Senior', 'Sophomore']. C4 B( z: Q8 i4 _% d
    1( }! A% g/ F* ^) ~6 r0 e" T$ I
    2, u. o+ Z1 X$ h1 q* ?2 {
    3# V2 r4 f2 x" b0 V8 _. _8 n1 g
    4
    ! _! i7 a# h& G' z5
    / ?7 S( @. Y; U; Q7 A5 _* Z! z6, {1 c$ c* T  U- z# Z9 m9 C/ A+ y
    7
    ; ?  P7 y9 T6 W2 p8! P1 r- [7 u, z( K5 T- M3 \4 O
    9
    ; C0 G8 C9 A! z. P10
    : ]! P9 x% V, c' M, ^11$ C1 x; g, \$ I) \9 b0 \6 _
    127 b& t1 u, v* A$ ^8 G0 h8 |# w- g
    13; n/ C6 T4 {* R, p
      在一个分类类型的Series中定义了cat对象,它和上一章中介绍的str对象类似,定义了一些属性和方法来进行分类类别的操作。( R) e1 L; n0 k2 M/ E- q  ?  L3 Z$ ^

    " |' M& `+ [8 R; @. `s.cat1 [9 h1 ]; M7 `  j4 S
    Out[6]: <pandas.core.arrays.categorical.CategoricalAccessor object at 0x000002B7974C20A0>
    4 B5 q) O' O1 U1 k/ J% m1* C% O* k3 W2 |' v
    2
    . t: c: _, a. L; N8 X! a/ {cat的属性:
    4 [$ a2 ^: m' T0 |2 K
    * e$ M  t/ r$ j! a4 ^9 @* }4 Zcat.categories:查看类别的本身,它以Index类型存储. H3 `) ?9 p7 l" Q5 X
    cat.ordered:类别是否有序4 l& y+ @" K" m" {. O
    cat.codes:访问类别编号。每一个序列的类别会被赋予唯一的整数编号,它们的编号取决于cat.categories中的顺序
    ' Z9 l/ M! N; v6 I" ps.cat.categories
    ' U& d, r; d# W! q+ }3 `2 FOut[7]: Index(['Freshman', 'Junior', 'Senior', 'Sophomore'], dtype='object')/ h0 E( J# m. j' o3 N$ f
    1 _; K- l; ?/ ~0 \( M0 h- a
    s.cat.ordered
    ' `% k- c" I! {0 G: r- bOut[8]: False
    6 U2 c/ K# w: b3 v! f  z1 v4 _% p4 M, ^* Y; |, z5 U
    s.cat.codes.head()0 |0 K- a* f: z" G9 R7 \( d: H
    Out[9]: , q& P. Z4 a, L) j% @
    0    0
    . U1 _% y. p: _; F6 V1    0
    6 m! ]5 y5 [0 s2    2
    # x1 k# a6 C6 s9 w3    3
    2 S$ |8 Q) g! \- ]- c4    3
    1 {2 d* I" w, F+ vdtype: int8
    , E- g& Q! Y# |, d1 H: A1
    $ Z. ?& ~9 T+ `) Q2
    * }" F4 |' a0 n- h9 G7 Q$ N7 y3
    5 \) g- _: i. x4% m2 d0 @9 Q: x+ Q
    5. ^3 @/ n+ |. x+ a# H- i
    65 y! W% a( ^7 D% e# w/ P7 c) h
    7% i- D2 Y' d* O2 u$ R, }
    85 T9 O9 V" c! v0 Q( ?5 _
    9; O; A$ Y4 z, V# O
    10( U2 z) i# f# c
    11
    0 T4 U4 O2 A0 N. ^  n/ p" U12
    0 Q# X8 [1 x/ w3 C0 v3 _5 l" g4 K$ {# B/ H13) \% ?8 K  o" ?6 k- j
    14& v* c6 A; A& f
    9.1.2 类别的增加、删除和修改
    : U, l2 d* B5 Q7 e7 V" z8 m  通过cat对象的categories属性能够完成对类别的查询,那么应该如何进行“增改查删”的其他三个操作呢?
    9 q, R# m3 w( W, ]" R" I( Z  m; B# Q0 }3 R$ J! y1 t) k( d
    【NOTE】类别不得直接修改
    : F# E. W* n2 E# E0 x4 V- ]: s在第三章中曾提到,索引 Index 类型是无法用 index_obj[0] = item 来修改的,而 categories 被存储在 Index 中,因此 pandas 在 cat 属性上定义了若干方法来达到相同的目的。
    7 g2 H$ d6 C0 m& R8 D% J+ c2 c3 j6 W$ W% E' v5 b4 Q" {
    add_categories:增加类别
    1 M  R' W; B/ ^) {  {  Os = s.cat.add_categories('Graduate') # 增加一个毕业生类别5 f/ Y8 C  t; I9 j, C) E) c
    s.cat.categories" d! j* r7 \' j- N$ X) ^4 M1 v+ q$ s
    & K7 A  }, I* r6 {- Y3 f
    Index(['Freshman', 'Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')- p. ^8 @) W. p* i' y) k8 C8 e1 Y
    1
    7 ~6 m! ^( H. f1 v( _( ?5 |7 k# l0 ?2; o" b. _- c6 h8 t. T" A0 _# ?
    3( N9 n# d( G% g% ~  a" a$ K
    4& O% }* P+ n  }5 n  n  Q
    remove_categories:删除类别。同时所有原来序列中的该类会被设置为缺失。& [. @0 E1 }% i. n$ h& I9 w
    s = s.cat.remove_categories('Freshman')1 M$ A0 a4 J# Z  S" N
    / P0 p7 f2 J+ i
    s.cat.categories  a3 p# Z' b% X' ?9 C7 k9 M
    Out[13]: Index(['Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')2 u' f+ D( T/ o' e. n' O; z

    3 H$ X! G2 |0 p, k5 I2 L( us.head()) \' }- C% {( L1 E
    Out[14]: 4 w: F6 _1 U( O) D4 a
    0          NaN
    % S  C6 `' w& ^; ^; I+ X% k* x1          NaN) p  ^% w) |! I9 @% X5 y8 F
    2       Senior
    ' ^- p/ b$ _3 ]3    Sophomore
    . v$ J) x# e  [4    Sophomore
    9 K5 s( x7 O) A% V1 x7 ~. G4 X0 }Name: Grade, dtype: category
    * I( q5 Y3 k4 |( P0 KCategories (4, object): ['Junior', 'Senior', 'Sophomore', 'Graduate']0 I" @9 i9 J$ H+ Z' A) z2 _
    1, G. \1 R5 X( O/ X% [/ ^1 p
    24 C* K; w) R0 W" X" i" V
    30 v3 o& C' Q0 t1 h  Z( X7 d
    46 }. X* d: B  R
    5
    4 L' x6 y2 @/ A$ g61 W! m- k* g7 C' l% G9 G( X
    7) f# z+ k7 C3 W5 N* n  v( _1 f0 q% R7 h. {
    8) r6 |: z& K, l7 o& V# k' K2 z
    9
    3 S- j3 r9 P1 n10% k0 s) E5 T, G1 ?7 {& Q
    11
    ' z" I& c+ j3 `# n  Z3 w% q12' k* L- n# O7 U& @! l* C+ G
    13
    ; z1 e. k% V" s" Y  ^14
    ( [$ h* l! Y$ rset_categories:直接设置序列的新类别,原来的类别中如果存在元素不属于新类别,那么会被设置为缺失。相当于索引重设。
    9 ~. X5 C, [1 f0 m( zs = s.cat.set_categories(['Sophomore','PhD']) # 新类别为大二学生和博士
    + H1 d' I( Z! E8 Gs.cat.categories
    : J+ p8 [! K! v& h! K% n( [* EOut[16]: Index(['Sophomore', 'PhD'], dtype='object')
    : Q) U: n1 d- A; r, ]/ Q. ~" B7 Z/ z* E
    s.head()
    ) z9 e' P+ Q; R( s0 gOut[17]: . V: x, G% F- Z+ s0 [2 z+ Q
    0          NaN
    3 L0 B9 Z* R+ _4 P% [4 r1          NaN
    * E3 q8 F) E$ |* P: G+ L7 C2          NaN$ U2 m8 u4 w$ d6 d7 d; z# x
    3    Sophomore; F( R2 P* `% a+ ^, r
    4    Sophomore
    ; U, V9 p8 C: wName: Grade, dtype: category8 z' t( d, t3 I
    Categories (2, object): ['Sophomore', 'PhD']
    9 X5 u% {* ]7 J# E/ S5 J1 S2 T1
    , |/ H% x0 t& B/ f2
    - E, V( h. k( D* ?0 k3* u% J1 _5 {" j" ^  p5 i
    48 D  }& h% p( \
    5
    4 f4 m2 |& C% B: ~6! m$ j2 L' E1 I
    78 f3 _% r- J" B. Z5 Y+ T3 B8 j" \
    8
    * n5 Z1 L; m$ e: C' @9
    / F# D' o+ {; m2 h1 H10
    6 x( m3 \! j. r. h* ~6 a5 t+ d11
    8 y3 K! Z# L/ {: a/ Y% o12
    " ~0 @; D5 O, c5 m& B0 z13
    ) P. x: L1 r( j* q4 C, e) [remove_unused_categories:删除未出现在序列中的类别; N. B5 P7 i1 G: a: N- y
    s = s.cat.remove_unused_categories() # 移除了未出现的博士生类别/ J2 B: `$ I7 Z. B4 a! e- }9 f+ N
    s.cat.categories- Z9 l  l2 u/ }' r( y* y& W
    + d( K( I0 ]% X3 q, `1 L, {
    Index(['Sophomore'], dtype='object')3 H" f* B0 o% }$ k/ n
    1
    1 b+ z+ q( @! R& Q5 A$ g2
    ! T: }5 T" s3 A4 l37 L7 \' _% Z8 k  t0 {; ^) k
    4$ f1 r; b0 p$ J* a' G7 L: S. t& I  Q
    rename_categories:修改序列的类别。注意,这个方法会对原序列的对应值也进行相应修改。例如,现在把Sophomore改成中文的本科二年级学生:
      b8 \" v1 k1 _! Z# F7 R  e1 Ms = s.cat.rename_categories({'Sophomore':'本科二年级学生'})
    ! G0 d7 m) m( G0 V* `' }, Ds.head()3 x4 {% G5 v4 W$ T9 h4 G1 u

    - K; _$ |# e( M# ]0        NaN+ T1 I4 O$ x& k/ b5 j, d8 V7 S
    1        NaN
    3 t. s* h* p9 d1 h2        NaN
    ; E$ L5 _6 z$ Y$ r9 p3    本科二年级学生
    ' A5 Q  N; ?- b$ d1 ?4    本科二年级学生
    2 Z1 F9 |$ R' W( h  FName: Grade, dtype: category! r6 H! N# k" G6 k! {8 K4 k- {( I
    Categories (1, object): ['本科二年级学生']7 Q2 `8 B3 ]. n, E6 Y
    1
    + x2 y4 P  @/ s2 N7 o) P( g2' D2 Q9 S! I2 L+ ?5 v. P9 N
    3: A: n/ D$ |* p9 A
    4* T( e$ ]' e, T" X$ \% s
    5
    0 @7 ~( _+ ~: I6 d6 Q* E& ~- }' q6
    " e# i5 n# b0 \; [  \1 Z1 E7
    8 a9 d0 C8 B' t$ M/ d: D% \" G- m82 G" E+ W3 p5 e$ _2 M  w# [2 m
    9
    / y2 o' [$ v) W! c, C# L3 ]& |10
    7 P$ P9 u/ p, T2 F+ i9 M; K9.2 有序分类) _. o! q4 s2 {# A# J, `2 w
    9.2.1 序的建立, P) D9 n- v' `
      有序类别和无序类别可以通过as_unordered和reorder_categories互相转化。reorder_categories传入的参数必须是由当前序列的无序类别构成的列表,不能够新增或减少原先的类别,且必须指定参数ordered=True,否则方法无效。例如,对年级高低进行相对大小的类别划分,然后再恢复无序状态:3 m7 T3 z. @. w2 O% M% p
    % Q2 U# l, U" I& {. L7 U, S9 T
    s = df.Grade.astype('category')
    # ]0 v' R/ a! c: a+ D' {! k  r; f4 K7 es = s.cat.reorder_categories(['Freshman', 'Sophomore',
    ! J# x2 v- k, M& B8 }9 W                              'Junior', 'Senior'],ordered=True)
    / c  N+ ~- E9 x+ y5 t6 w) S& Z0 F$ js.head()
    8 I2 z5 j6 {0 j& F% X9 c- Q8 ?Out[24]:   j1 S, f1 k$ K# p# r6 Z! i
    0     Freshman
    - u) m. \4 {5 I* b! |1     Freshman
    . `; @0 F4 n2 X  A' Z( f2       Senior- |! H7 M4 v9 r
    3    Sophomore. v' t# }, j. ~& ~( w/ |$ X
    4    Sophomore  r1 E& t# k' h0 Q0 x
    Name: Grade, dtype: category. U+ U4 Y5 I9 m2 C7 n
    Categories (4, object): ['Freshman' < 'Sophomore' < 'Junior' < 'Senior']
    , [& n+ ~2 S) g* g2 u& e2 X. H* v5 X* ^4 h2 ~  m8 I9 n. r$ }! c
    s.cat.as_unordered().head()
    8 i* J' Y. o3 Z" n; dOut[25]: 9 l# ]3 C& Y% f6 Q6 c
    0     Freshman
    # T( u2 G& m+ \& g4 c1     Freshman
    2 q0 @$ H8 s1 G7 `/ s2       Senior
    / f0 g3 J$ m- M9 N3    Sophomore. v( k7 X4 M/ u" ~! O$ j
    4    Sophomore4 ?" v/ S  m# O, K4 {! M0 J/ h
    Name: Grade, dtype: category
    9 q4 ]: P. S' iCategories (4, object): ['Freshman', 'Sophomore', 'Junior', 'Senior']
    & T* e" f" O( o  C# _/ S" r3 U: @' y% b. V
    14 I4 [: i) y  j/ M4 D
    2
    / v2 d& x- a) y* J3
    ; L- ?, \9 ?* g& g: T* K4/ `( N+ |/ M  d
    59 T* O% F) U' A% O# w
    6
    + F6 H0 p( T5 G' R$ C7
    / X, H$ k% O$ r. I3 _/ I: x( N8( W) C. Y4 E$ o1 u% d$ _6 S. m
    93 U- }$ T9 p- q' j
    108 Q9 p2 O) e/ {* K. _+ w. o
    11' o0 u$ o7 T" G( W3 Z0 c' _
    12
    ; D& e* |% Y; O$ D* {13& O5 b* g6 m, J# Y  Z1 ~/ S2 p! N
    14( F# |$ \* f/ j- c! L+ V+ v; o
    156 u6 j  U2 y3 _. F$ |1 o
    16
    ! i. @/ x6 ~3 M6 |17, l  I$ O0 [! e! j; X, Q  j
    18. q1 Y+ @3 m. H! k: F; m
    19  d5 h' P0 W. b
    20
    : _3 z2 o3 H8 T0 R5 h217 P$ t$ u% U- T5 w2 B& N
    22! R; Y/ y/ y/ a$ _' W8 @
      如果不想指定ordered=True参数,那么可以先用s.cat.as_ordered()转化为有序类别,再利用reorder_categories进行具体的相对大小调整。5 Z! i3 s8 h, a

    % v( z2 r& k* G( B" Q( ], ]/ y9.2.2 排序和比较6 C6 A5 R8 k* j, ]; i+ k
    在第二章中,曾提到了字符串和数值类型序列的排序。前者按照字母顺序排序,后者按照数值大小排序。+ ^+ j4 x: n4 s+ ?1 i! v+ p; C
    " c$ W% M$ P$ H1 B3 R. W
      分类变量排序,只需把列的类型修改为category后,再赋予相应的大小关系,就能正常地使用sort_index和sort_values。例如,对年级进行排序:
    ; C- J3 B8 j9 g% x3 e( p. d! W" ^" Z( B
    df.Grade = df.Grade.astype('category')
    # E5 t- n0 w9 l# ~. zdf.Grade = df.Grade.cat.reorder_categories(['Freshman', 'Sophomore', 'Junior', 'Senior'],ordered=True)- }- u0 u9 ^9 O9 |' A
    df.sort_values('Grade').head() # 值排序
    ( g3 L" H. D) j( a, [+ @4 ^Out[28]:
      u8 c8 I, B& Y' J. A3 g        Grade           Name  Gender  Height  Weight' f9 W) }2 [4 J2 M0 U5 s$ N( K
    0    Freshman   Gaopeng Yang  Female   158.9    46.0
    0 `; j& m2 b5 }% h" o+ s, E105  Freshman      Qiang Shi  Female   164.5    52.0
    2 M3 u$ D6 a, T9 T' _& ~96   Freshman  Changmei Feng  Female   163.8    56.0
    . ]0 W9 ]1 H. Q5 K; J, D% T* X88   Freshman   Xiaopeng Han  Female   164.1    53.0
    % ?3 }; S% s8 X8 _& r81   Freshman    Yanli Zhang  Female   165.1    52.0  [) N7 D8 ~7 }
    ( {: o' l9 W/ V" n  W% x5 q
    df.set_index('Grade').sort_index().head() # 索引排序2 u- i+ L# w& e
    Out[29]:
    ( v. X# D8 i, D. ]2 J0 V                   Name  Gender  Height  Weight$ ~6 D& z6 C9 c2 R; y2 {' v$ v
    Grade                                          
    6 X# |2 @1 C4 C' @2 S. g* N, i* G9 eFreshman   Gaopeng Yang  Female   158.9    46.0
    ; I+ j! u7 [8 o& \$ nFreshman      Qiang Shi  Female   164.5    52.09 L: F3 j; s: K1 E: t
    Freshman  Changmei Feng  Female   163.8    56.0
    ' t7 [7 D* P- q# BFreshman   Xiaopeng Han  Female   164.1    53.0  d3 _3 R1 A8 i, f6 k! u
    Freshman    Yanli Zhang  Female   165.1    52.0
    & r+ K  j- @% D: M6 c6 x' n& j( |$ B) d) d5 t3 T( _
    1
    9 i% X% }* b2 A( A- e- r/ I2
    0 P$ @% ~7 \& r: B2 H5 r3
      n$ g# g3 i. v' \' }4# s1 a8 a5 L. `1 A
    5
    ( _' T# a3 v. h1 Q0 m* T  @7 h6/ }( J/ e0 G0 c  [4 X2 V1 I4 q# G' ]
    7, ?3 l% F+ s, v
    8
    & w& w; A5 I( U1 z$ x, ?1 J  l: A# B9" n2 v7 z! H3 ^& V9 U
    105 A+ g' ^7 Z$ I9 b( M: \. P
    11
    3 e/ N/ D! X' L5 L! Q' u8 Z12$ i+ t" f, ?, W: s
    13
    ; N& M% O: L, G* {0 z145 Q% W9 q3 T, ?: I
    15' y5 f: [/ B! l) k' |
    162 J$ t8 O! H: P9 a5 Q
    17; X' \3 b6 X4 j* I% l) \
    18! M% |% ]* ~  b, d
    19# a1 e; \8 ]8 I( J9 Z
    20
    * ~. N5 \& {+ d6 s$ ^% i  由于序的建立,因此就可以进行比较操作,方便后续索引操作。分类变量的比较操作分为两类:+ D* X7 I) f( ]: D

    3 \/ t+ X! m! ]. i==或!=关系的比较,比较的对象可以是标量或者同长度的Series(或list)。(无序时也可以比较)5 k! I2 R! A8 A/ L$ n; t$ Z
    >,>=,<,<=四类大小关系的比较,比较的对象和第一种类似,但是所有参与比较的元素必须属于原序列的categories,同时要和原序列具有相同的索引。/ N9 `- t( {2 M6 u, E9 _' e8 n
    res1 = df.Grade == 'Sophomore'5 Z/ S7 x+ D# d) K

    # B- _; F5 o/ t  l+ l! Ores1.head()
    ) b; u3 l: R0 O, h$ ?5 eOut[31]:
    3 B6 ~4 |4 M- M6 L* ^, P0    False
    $ ?1 }/ s' W  g0 b: a  i# T; U1    False
    - ~: u3 s" T, R+ H2    False1 t6 a) M' y4 R6 X; b
    3     True* N$ F6 I, b+ h5 ]4 Y6 i& g
    4     True' h% ?1 T( g& b" f
    Name: Grade, dtype: bool8 ?8 Q$ ?% F; k7 x5 H/ o

    7 u+ I; a' p& l8 }res2 = df.Grade == ['PhD']*df.shape[0]
    ( Q, [  k" a1 g  D3 E+ Q8 U6 [4 |# G; H
    res2.head()
    , X! U! X. D5 l" V. W. [Out[33]: * e- \$ r  K9 S' i
    0    False4 g" s9 x& w! y+ x6 ]7 d5 P1 ]
    1    False
    ; Y& u0 Z/ D4 @2    False
    : t1 C1 M& j; N2 I+ |* C5 A3    False8 h- ]5 Z7 b9 M* T- ^
    4    False" J; o; i; p& {: @; j) Y
    Name: Grade, dtype: bool6 {: u7 l8 P6 {( S2 c

    % _! a% l  }3 l& [( }res3 = df.Grade <= 'Sophomore'7 F! L5 M4 a2 r* u

    # j9 R% E# [3 U; qres3.head()9 I$ t' P# E# v$ `2 U
    Out[35]: , ]& F2 A0 P8 ^
    0     True! E2 v% T- V* F& [( ~1 h
    1     True
    8 Q2 A$ l/ j5 R2    False" Q' w  o4 }/ b# X' c, C5 ]6 v6 @& Q
    3     True' Q; [4 \9 \! A/ e. d
    4     True* {! P- @5 D( T$ [; z/ F- ?
    Name: Grade, dtype: bool
    / a+ M  O9 r4 X9 O7 N. m
    5 V) `: F, {$ V" N3 z- g. O# sample(frac=1)表示将序列随机打乱。打乱之后索引也是乱序的,直接比较会出错,必须重置索引。
    9 N7 l" ^# u& U: ures4 = df.Grade <= df.Grade.sample(frac=1).reset_index(drop=True) $ M8 D9 G8 `0 t/ R  Z8 c: T
    ) F* x9 c' R( Q: w5 X' P+ S! h
    res4.head()* o  ^, h+ i3 f; M
    Out[37]: * t; E( m  O) A& ~5 J1 w/ C
    0     True
    & ~" `- V/ v& x* E5 E7 y1     True
      |5 D9 n+ ]2 I8 v& V2 E2    False
    5 V: k) O$ \5 N' C3     True% ~5 G! P9 A. V7 F! q" q; a
    4     True- o/ j; i# @1 v+ L/ L
    Name: Grade, dtype: bool  n% U9 S! H6 T% D  I( h
    5 m" t# M. a$ Y9 T* k2 D$ N- a8 s- a
    1( ]% C1 q4 |' n. B& M
    2/ `+ U8 i6 j: a
    3' m4 w9 p0 {* f3 R! L. Q2 ?
    48 \+ }* K" X0 p' k* v# @
    5& u! P9 c" z. o/ ^9 S& ^# {
    6! _* n8 {4 ?! K+ q# ^- Z* X
    7  S, T& g, v& U# Z
    8! k  h: `: v! J! i* n/ k) w5 v
    9
    9 {9 M/ K  U) {: u105 J( _3 w4 `4 _! m* j3 F
    11! c) i' R. \) U6 K2 x3 t8 p
    12
    9 I- [- I) C7 J' J# s7 s135 J  `" e4 ^' i( q4 s* s) I% B
    14& h6 t" R! _$ g0 I
    15
    # a4 R/ `- a. V$ p16
    8 b  k$ {3 B" A9 V170 X6 g! o4 w, O
    181 _4 i3 K6 P# {9 B- O( m$ o
    19: o% e( Z9 B$ x8 `3 }
    20
    ! \8 J9 ?9 H) y+ n21. }$ Q7 i2 M" y' D5 k3 X% N, {
    22- d4 O$ E' Y7 Y
    23
    ) }0 F3 E% ~# N6 T24. }5 A( G5 M8 H2 _( S3 u
    257 h, d0 r, B' J: O. x/ |
    26# e/ x/ Y+ W! r# s1 v
    273 M" }7 X  {" a! A& u+ Z+ y
    28; W% J( a; P$ ~
    29
    7 C. e6 B5 n5 g30% G$ w+ F0 l$ X9 N  I( Y
    31! b) W+ M5 A4 A  E' C8 L. t& v8 x
    32
    $ j# @& w& N& K3 U9 l33
    ) S# ]' c; K* z34
    ( u6 l( a2 P/ ]: ^, f. y35
    / V6 n5 C; }1 ]7 u& N/ w% G36
    8 s" ?5 D: f( N6 a37' b- n* e+ U% W( `$ m4 R) |
    38
    ' g7 y' |) X0 W& b/ J39
    + Q! ]$ B4 s' `; Y( C( w401 @! |1 W5 G! L$ W
    41) ]  M- m, q% ]2 ?; b: P% B
    429 a, L# n, X% t- Y6 I, i' H
    43# j. e1 i) I+ w% d! B
    44
    7 T3 P! d3 \; n, w! L9.3 区间类别
    8 u3 T0 V$ Z' m' J5 x& s% Y, d* `: H9.3.1 利用cut和qcut进行区间构造6 _. u2 C" |3 Y6 U( |2 g5 i" A  x
      区间是一种特殊的类别,在实际数据分析中,区间序列往往是通过cut和qcut方法进行构造的,这两个函数能够把原序列的数值特征进行装箱,即用区间位置来代替原来的具体数值。
    0 \/ o. E8 V/ P2 Y" {5 ?) Y
    ! ?8 I  D3 [5 N1 Ucut函数常用参数有:4 y; @" S  f! o* Z1 p4 ^
    bins:最重要的参数。6 a* A2 d+ m! m6 e8 @
    如果传入整数n,则表示把整个传入数组按照最大和最小值等间距地分为n段。默认right=True,即区间是左开右闭,需要在调整时把最小值包含进去。(在pandas中的解决方案是在值最小的区间左端点再减去0.001*(max-min)。)
    4 L$ Q; _( Y8 N2 V也可以传入列表,表示按指定区间分割点分割。
    4 J; R2 s9 U; i% Y  如果对序列[1,2]划分为2个箱子时,第一个箱子的范围(0.999,1.5],第二个箱子的范围是(1.5,2]。! }) u; ~' S, }) y/ t% x+ M8 g
      如果需要指定区间为左闭右开,需要把right参数设置为False,相应的区间调整方法是在值最大的区间右端点再加上0.001*(max-min)。2 f% I5 O& a* t1 n" x# r
    4 q# h! h( D- U, O; i$ h* R8 D! L! m+ E
    s = pd.Series([1,2])
    & x; g; u+ c- {- S" b0 ^# bin传入整数6 A/ W3 A' i/ ~" x  J/ M

    ' n2 X: A1 ?5 Q; mpd.cut(s, bins=2)5 q; g7 l& Q- S* [
    Out[39]:
    5 e. a: J# N- d& {+ @) z! f0    (0.999, 1.5]
    8 r# k4 i+ e/ k7 h5 r) V. u1      (1.5, 2.0]
    7 t' H; q- U( r. z! @dtype: category  [! Z4 y! g: v5 f% @. @9 v+ B
    Categories (2, interval[float64]): [(0.999, 1.5] < (1.5, 2.0]]' B" T- K& T( M2 f5 n8 E9 u$ Z( B& F
    ' B6 M/ L8 i0 i% `
    pd.cut(s, bins=2, right=False)3 u' i  e; u0 w  T6 {% |& G
    Out[40]: + W( @2 N; u# x, m& I; ?
    0      [1.0, 1.5)7 h& T8 i) f0 f! s" R4 Q; P% k
    1    [1.5, 2.001)
    % y+ e" ^3 e( \3 ]2 ?  ?dtype: category
    7 g6 H4 R0 F$ UCategories (2, interval[float64]): [[1.0, 1.5) < [1.5, 2.001)]/ j+ E2 k9 D, [0 A& d
    # y. O9 H1 s( `  G

    0 W: B5 J" [8 v2 W# bin传入分割点列表(使用`np.infty`可以表示无穷大):
    5 L  T9 D" y  O2 ]pd.cut(s, bins=[-np.infty, 1.2, 1.8, 2.2, np.infty])
    , v7 W( T$ G: c) Q1 BOut[41]:
    / A( S0 J- D' N0 `) P0    (-inf, 1.2]
    # u6 J, Y1 p) I+ y6 S8 V& c1     (1.8, 2.2]/ k0 X" M' s: g0 L
    dtype: category. S( }- Y2 @9 G& Q$ [
    Categories (4, interval[float64]): [(-inf, 1.2] < (1.2, 1.8] < (1.8, 2.2] < (2.2, inf]]: l. k6 _1 _, d4 g% z2 z& W
    2 E. }/ b2 h: k; ?6 h9 L% y% P
    1) ?1 e  B" A" h' L; s1 U3 y5 ]
    2/ Y% W9 Y" ?, J
    3
    ! P* T: o4 f/ S, `9 r4
    ) O' B2 i2 d/ ^( o' O50 w+ p8 c$ v9 B1 X7 z. Y, y( F6 Z: d
    65 c. `2 `! ^7 d1 M$ D8 e
    7
    ! T1 Z. h$ _3 a# C8
    7 I- w  _( `0 g) m9) y$ F: p  V" M% i6 g
    10" s; k, m. a+ O9 G2 z: n
    11
    ! ~$ p* l9 v6 C7 V/ S2 n  o  j123 C- y; f4 i/ V
    13
    / o/ Y' ^! {, Q% K14$ O" ^6 p5 Z$ s0 q1 N
    15( j" Z  m6 B9 n0 s0 K0 V: T
    16
    " R! W. M0 |: ^6 P17* s. n2 o, ]6 @( i" R
    18
    - k* G% Y# j$ b: E# T3 g% v& q* ?0 h19
    6 j9 r. ], b. O6 L20" `: J( c% Q0 b0 y0 t5 a. O/ T: O
    21
    3 u1 U( H$ G% j3 ]' e22
    , l7 r, U7 P( _& M7 `3 q* A6 J236 J+ n# ~1 b  a* c4 @
    247 y9 s! j  w9 S" ~
    256 G% {5 i; K9 g- U) h
    labels:区间的名字
    - B+ b, s- o* a; s4 W4 X, i& Jretbins:是否返回分割点(默认不返回)$ r- \2 }  F& q& A/ D; A
    默认retbins=Flase时,返回每个元素所属区间的列表4 @% H4 f. j* e- m" ^/ B- N% t
    retbins=True时,返回的是元组,两个元素分别是元素所属区间和分割点。所属区间可再次用索引取值$ N7 Y4 Y+ C  j/ S9 F! x. K

    1 k% _& f+ W* k* ^6 ^- F- u0 |s = df.Weight3 p: A. b/ u9 f) O
    res = pd.cut(s, bins=3, labels=['small', 'mid','big'],retbins=True)
    9 q- u* T# u: o$ o0 w6 ?res[0][:2]. ~; F- B: e4 L
    ; [% p! |0 F1 s4 b- _9 p
    Out[44]: 0 {& k* @# p/ e
    0    small0 ]. ]1 I" ~2 C! w( ~
    1      big% e0 `9 f( ?+ S" T- F+ i: u
    dtype: category4 T  g- h/ E$ Y3 V- l9 p& H! O
    Categories (2, object): ['small' < 'big']
    9 l  d3 j( {5 ?0 e5 i) A: b7 f# M' J( i$ q: E  J
    res[1] # 该元素为返回的分割点
    5 F$ W1 f$ |" Y8 c6 iOut[45]: array([0.999, 1.5  , 2.   ])* w( U2 A% s4 j) k1 |4 y7 `( v
    1* ^( t" p, h: V% O; ~
    2  t) z. p  e7 q
    3
    9 b9 ?0 f* v) ?$ e, l8 O4
    4 o2 E' a( A7 p5' L9 x0 g- P' f' `
    6# k2 n; Y1 F$ r7 ~
    7
    ( i' p! H& I7 [# Z3 D87 @& n# d, r( l' M
    9* ]$ _' _! x! O* W$ [
    108 Y$ i7 d; `+ Q
    11
    ( ^# a) D* |6 `! z( x  i" l3 O8 n12+ G& y% c6 x9 ]* `
    qcut函数。其用法cut几乎没有差别,只是把bins参数变成q参数(quantile)。
    * w' x1 }" q! B* i9 Q4 e3 Q; j8 u& U% uq为整数n时,指按照n等分位数把数据分箱2 o/ n* O4 U! H' H
    q为浮点列表时,表示相应的分位数分割点。: H" ]  ~$ s$ a2 P, h& q" q
    s = df.Weight
    ) D1 `/ ~5 U9 N- {. H! w
    # R( y8 Y+ _, i: U9 z4 b6 c4 H, xpd.qcut(s, q=3).head()8 Z3 ~- c1 l0 s% U
    Out[47]: , j; j* S& y" S6 ]5 Z- M
    0    (33.999, 48.0]
    ; I7 t2 T8 Q3 y+ G- L9 Y. J1      (55.0, 89.0]! u3 G6 m% w; z2 A2 y
    2      (55.0, 89.0]; s/ _2 i* Y) @; B7 Q) L
    3    (33.999, 48.0]
    5 Q9 O& F0 l4 G0 x' n4      (55.0, 89.0]
    # |8 `. l% @# u% h  }0 wName: Weight, dtype: category
    7 w& G3 J8 E6 a# V) ]Categories (3, interval[float64]): [(33.999, 48.0] < (48.0, 55.0] < (55.0, 89.0]]" @* h& n4 H3 m; N1 \5 V6 v3 x1 w

    ! l! J% C% C) x: ~. I* H+ Cpd.qcut(s, q=[0,0.2,0.8,1]).head()
    6 P( X+ a. z( N1 g; w( x% FOut[48]: ( c$ }5 ]. b$ \
    0      (44.0, 69.4]
    2 m( b# f* c  H1      (69.4, 89.0]
    % i5 J* A& w! T0 L3 w3 ~* _8 u' H2      (69.4, 89.0]
    2 [4 I- }7 i' t3 E% ^' U3    (33.999, 44.0]
    : q0 C% e& p& j  {# C8 L0 h4      (69.4, 89.0]
    % d8 j3 h+ [; T$ JName: Weight, dtype: category4 e. w% j5 ]$ m9 M( |. U0 {
    Categories (3, interval[float64]): [(33.999, 44.0] < (44.0, 69.4] < (69.4, 89.0]]
    & F4 N3 X0 Y: j0 S& c" h0 v1 E8 G+ Y- A  h* W
    13 s# M4 _2 X! Z; D
    2# {8 M+ E% Q2 G7 k- q
    3, O+ X0 H/ J. V- v! G: A
    4
    ) E& B2 ^5 m) \9 p" z( H5# \0 Z* c6 ?4 R
    6
    9 G4 x2 s8 D1 m4 j# M$ F% {7: ?0 \/ |+ k8 s0 y4 K# X
    8
    6 j, s5 m, q6 G/ |9! X  f# Y, k- g- m2 }9 F1 n$ y; v
    10
    ( S; b7 t3 A" U0 p  ]% P11  _- }. ?# b! O$ T: ^  K( _( e
    12
    4 Z; Z5 ~" C0 x" O13  T7 x! u0 \" j0 P$ ~  S
    14
    1 e; Q- _* R1 d# x# P15* |) _8 {! m& F" t
    16
    7 T; u( M- |2 K8 Q  A' a17
    & a! |$ k5 g0 x6 }, N18
    4 N; A1 ^" L2 N9 w4 K4 N0 G' ~5 U19
    . v- h- q" `1 t6 L4 a8 i8 |5 k20
    ' j4 O' j" K6 j21' G0 o+ Z+ D, v3 Y9 i
    9.3.2 一般区间的构造
    8 [$ Y% \3 Y% o. o  pandas的单个区间用Interval表示,对于某一个具体的区间而言,其具备三个要素,即左端点、右端点和端点的开闭状态。
    1 a, K1 A& P+ E4 ?
    $ s8 U8 t4 {1 f0 R% o: r开闭状态:包含四种,即right(左开右闭), left(左闭右开), both(两边都闭), neither(两边都开)。  U5 s+ z* ]( \# A
    my_interval = pd.Interval(0, 1, 'right')
    , \  d( z) p1 P2 T5 J- ~  o5 O( e! E6 r5 k
    my_interval0 U( R$ E  n. E# j: I6 t& A
    Out[50]: Interval(0, 1, closed='right')* G- w. r4 V1 B, D
    1) U: q1 i. L+ L* C
    2
    7 X& E* D$ F1 x% u4 U3
    + H! |) D& R" N) ~# B8 K8 F3 b+ l* o4
    , h. s8 \1 N; K区间属性:包含left,mid,right,length,closed,,分别表示左中右端点、长度和开闭状态。
      }! d2 P& t0 l7 z使用in可以判断元素是否属于区间
    + H. C! p; @: A7 h$ C' X8 ^用overlaps可以判断两个区间是否有交集:, e( J. R! N& x& D$ \8 ?
    0.5 in my_interval6 ]- Q, L( w# H8 V. c5 B
    : r# q& `3 y2 V" K3 Y) l2 r
    True
    + T4 J- T0 F( L, I0 e# ~! ~1
    # A) i- e1 \+ g0 ]9 _/ c6 r2
    2 r8 D3 K/ X! a8 v& i  K  m3
    2 \7 p& G& D6 V  l0 |) n9 \/ l5 Omy_interval_2 = pd.Interval(0.5, 1.5, 'left')1 ^3 U3 J7 H% N+ ^
    my_interval.overlaps(my_interval_2)8 N, }7 D6 s  {
    + }5 a. j! n9 d' e/ \
    True
    6 {+ M+ h6 b# O; X9 j10 O6 I* S" J/ q- ?+ a" z
    2
    2 g" F3 m, H- T- F8 ~; F# I3% X/ w2 x$ n- `, g1 i& p- N
    4
    0 X0 R0 _# }* m& Q8 h  pd.IntervalIndex对象有四类方法生成,分别是from_breaks, from_arrays, from_tuples, interval_range,它们分别应用于不同的情况:
    5 o& W/ r' [4 }4 S( f, Q
    2 @9 @6 _- _+ A9 q% l6 {from_breaks:类似于cut或qcut函数,只不过后两个是通过计算得到的分割点,而前者是直接传入自定义的分割点:
    * D+ N% x" D% g- ?- q( lpd.IntervalIndex.from_breaks([1,3,6,10], closed='both')
    5 j8 j2 s; _) g/ ^! w9 \
    ( I; ~0 u6 F( T# B( s' bIntervalIndex([[1, 3], [3, 6], [6, 10]],- S6 K* g# e7 N5 t/ _# a; p; h
                   closed='both',
    0 I5 m  A/ w# u* B0 [8 w               dtype='interval[int64]'). ]) D$ s) @( l" e7 i; A% j
    1( x" w6 {. M" Z2 z, H
    2
    . L& J9 u4 N$ A* P4 ]& H3
    : I% f5 H! s. J+ R4
      F6 F! r. P7 n" x8 |2 z51 `; }, w& e  Z1 k4 D
    from_arrays:分别传入左端点和右端点的列表,适用于有交集并且知道起点和终点的情况:4 u4 X- y( I5 R+ R" d1 ?
    pd.IntervalIndex.from_arrays(left = [1,3,6,10], right = [5,4,9,11], closed = 'neither')" ?2 z3 Z3 _: I! x

    ; E1 _, Y; A( P6 q8 c: iIntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],
    , C  k. X: t( Z) J' e1 M5 g                  closed='neither',
    - |1 ^" d  F5 C' m0 X                  dtype='interval[int64]')
    / K0 _0 c# u0 a1- i6 ~' R) f9 X  T( d. Q' }* W
    2
    ; b8 k! Z3 c* [3
    / f: _9 j7 c- t& A; m) T41 N$ ]8 T& e0 A, f
    5
    # L  x4 h: Y/ |$ d# Xfrom_tuples:传入起点和终点元组构成的列表:
    , M9 I" b3 m7 c: H0 v. Ipd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)], closed='neither')
    7 p  D4 i- O( y4 E6 \0 P) m; N
    5 |$ y$ ~0 R2 C! m: l9 UIntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],
    - Q8 p# `! z" \8 ?$ l              closed='neither',
    % b+ b" C$ m% C              dtype='interval[int64]')
    7 B, L; m+ i/ c  t9 ~1
    ( ]# I# Z8 C, x27 n4 g3 x, v0 W2 z
    3- w" l) N9 o) T* l; f7 F
    4
    3 }: P8 J9 Q7 z( d8 [2 j1 p( w5
    ( B. a! Y8 u9 `3 b+ yinterval_range:生成等差区间。其参数有四个:start, end, periods, freq。分别表示等差区间的起点、终点、区间个数和区间长度。其中三个量确定的情况下,剩下一个量就确定了,从而就能构造出相应的区间:
    ) s' n- g, ?( _! M7 {. j  z7 w' dpd.interval_range(start=1,end=5,periods=8) # 启起点终点和区间个数
    3 U3 c' L& L& X1 p$ U* AOut[57]: # M) ]* w( R! y. X" J$ a. W" c
    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]],
    . F  b+ n- L/ b6 f) z              closed='right',
    : I0 u' `9 U4 q" b  s' e              dtype='interval[float64]')9 R5 e  i( @+ t$ x2 p( {+ T! l
    ' V8 R* L  r1 b2 m( h4 h
    pd.interval_range(end=5,periods=8,freq=0.5) # 启起点终点和区间长度# E" J+ V% ?: w( m) `
    Out[58]:
    3 b% H( i; Y6 o. q& Y* FIntervalIndex([(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]],
    1 [; I9 F! o4 I' X* i; \              closed='right',
    * Y. v+ m$ b3 k/ p9 i( _$ p              dtype='interval[float64]')& ^, B6 T, X! Y! t# N! s$ m% f
    1( ^" ^5 m8 B8 |9 A1 p: i2 T3 I* J
    2
    2 I1 R: B7 e, Q( J, {- o3  M4 {6 k0 c! ]0 M
    4
    " G1 z$ d! o3 t# U, ~& s6 l57 X4 [/ v8 j4 U+ S; [7 [& W& g5 P5 \
    6
    7 r4 s: S# [5 j* o" p3 a7
    9 P1 k; t% B7 a  J4 f) r8
    4 U4 Q9 }. U/ s! H$ p4 N: U* U9  S  |: [+ x- ]. V& i
    10
    9 i& ]# X( Z% V  P; m11
    8 s& a7 f" S# c$ `8 U4 g0 \5 ?【练一练】- d/ v! M0 N% k4 d7 |3 [% x  d' R: h
      无论是interval_range还是下一章时间序列中的date_range都是给定了等差序列中四要素中的三个,从而确定整个序列。请回顾等差数列中的首项、末项、项数和公差的联系,写出interval_range中四个参数之间的恒等关系。5 B' m' ~* J! _. t2 G/ c" y
    # X' D" Y% }, G9 M, Y% T
      除此之外,如果直接使用pd.IntervalIndex([...], closed=...),把Interval类型的列表组成传入其中转为区间索引,那么所有的区间会被强制转为指定的closed类型,因为pd.IntervalIndex只允许存放同一种开闭区间的Interval对象。$ q: ^5 o5 x0 \3 k. I) ~9 A

    / m' H3 N: ~; c4 }; zmy_interval2 A7 V, D2 `$ }  |
    Out[59]: Interval(0, 1, closed='right')
    2 |* E& j3 Y7 b8 t: w: J
    6 J4 x& }4 b' G' \5 |' ?' ], cmy_interval_2: L0 A- a( r) o/ T6 V
    Out[60]: Interval(0.5, 1.5, closed='left')
    3 h9 ], U7 n2 z# \2 o
    . _6 O' [% ~! ?$ hpd.IntervalIndex([my_interval, my_interval_2], closed='left')
    / l! i, P( C# ^$ J  T( y/ dOut[61]: ; H/ N" R4 O. w8 A9 x
    IntervalIndex([[0.0, 1.0), [0.5, 1.5)],
    " A3 Z  h0 m* {; Q: _, ~/ W- z              closed='left',
    . S- H! H% k, j              dtype='interval[float64]')" N& G2 ]+ p6 D5 i
    1
    . H4 h" A) |$ |: I. D  m2
    8 W1 J4 _, S! g* j0 Q+ p9 V34 w; O0 z7 d+ Q
    4
    + N" O1 x7 P* h5% R; e2 P& E8 L. H. X
    6
    . u% P2 D: @8 f- L% b) r7& g3 a$ b% @+ @1 N0 i' B
    8
    2 f/ _9 P, [6 \/ i9
    / C, |2 |1 q* ]" o10
    $ a6 B2 U+ E( _( \" J" p11
    # ~! |. W# I8 F9.3.3 区间的属性与方法8 f/ l  L, V! _; ^3 d6 a
      IntervalIndex上也定义了一些有用的属性和方法。同时,如果想要具体利用cut或者qcut的结果进行分析,那么需要先将其转为该种索引类型:, Y: C% e. g% K* F1 m
    * O  X% ~& v/ F
    s=df.Weight
    / y. f2 E% C% ?# }- _, }id_interval = pd.IntervalIndex(pd.cut(s, 3)) # 返回的是每个元素所属区间,用具体数值(x,y]表示' L. D5 a3 D/ _
    id_interval[:3]0 J5 h5 L+ Y4 b; z7 D" y; @! t

    ' ~, L* A! Q! W5 n, {0 TIntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0]],
    - n, n  e+ }& q  B+ H                 closed='right',- {. S$ E' U5 i; _6 ~0 h+ i: o
                     name='Weight',
    " J7 |7 H+ D$ M* Y0 s' [                 dtype='interval[float64]')) r( M4 P7 X* E
    1
    ' c1 K& w! i/ z" r4 z2- f2 u0 d4 z4 q: k) Y5 g
    3
    7 k3 F$ `) M' w! L: p4
    ( ]- ?, M3 r( E1 l# ]. k53 c! G/ [8 {) c  z  M
    6) e7 C1 F* q1 W3 n: p
    7
    5 k. D  K6 L6 d- x" y: @8( @  E# ^5 d1 n. B, u. v& }1 B' P- }
    与单个Interval类型相似,IntervalIndex有若干常用属性:left, right, mid, length,分别表示左右端点、两 点均值和区间长度。
    9 v! e) A# B% t" ?6 D3 `! Qid_demo = id_interval[:5] # 选出前5个展示+ m; x1 v: C& X7 d

    8 Y+ {$ S5 s, j( uid_demo8 W3 ~2 H6 E, V2 R, R# Q: Q' V
    Out[64]: $ E& N- o. M! a/ [  }1 R3 {
    IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0], (33.945, 52.333], (70.667, 89.0]],  F$ P/ v8 n% A& {
                  closed='right',6 {3 N* H: c+ D9 B# i+ ]' ?$ d0 M
                  name='Weight',
    " G) V. t' _) V              dtype='interval[float64]')
    : B5 _/ g( r5 `! r
    6 ~# Y) ?5 M3 f1 i2 d2 cid_demo.left # 获取这五个区间的左端点
    6 C2 r8 j* F- x. c0 p" W% qOut[65]: Float64Index([33.945, 52.333, 70.667, 33.945, 70.667], dtype='float64')
    $ h1 O/ k; b" G9 e
      Y3 e4 E& c) H' J) [0 S) Did_demo.right # 获取这五个区间的右端点( w# O. n: C' ^
    Out[66]: Float64Index([52.333, 70.667, 89.0, 52.333, 89.0], dtype='float64')) b% \5 r4 m8 m- U( F
    . T* _) p4 G$ h  p
    id_demo.mid
    ' C4 X; U& A* E3 }, e8 n( n3 S. ^! e* l6 \( uOut[67]: Float64Index([43.138999999999996, 61.5, 79.8335, 43.138999999999996, 79.8335], dtype='float64')& m! o& I5 x4 M, T9 C% O0 E

    & _, r' m8 l7 @% e5 Bid_demo.length0 g+ ~/ b7 u0 w( h" g, H
    Out[68]:
    5 |2 B& V+ s& c# u; Q' v, qFloat64Index([18.387999999999998, 18.334000000000003, 18.333,
    4 l& `" l- n# ?7 n; y8 V# K: ?              18.387999999999998, 18.333],
    " e/ z& _7 |' u6 w+ s             dtype='float64')" l- o2 s# p" m# {

    " w% J/ W& Y& r3 }; V1. A4 Y" ^$ x- x3 B( U% g# z
    2
    4 F& ~. ?1 D8 u% G1 c37 B0 Y2 v+ k5 Y* H7 I
    4
    : V* }: W0 N# ?0 q$ G+ e$ H5
      q; S6 ^1 z- k9 L, T5 ]% [; \6
    8 Z1 V7 `6 v) B6 I9 n7 e- o7  R9 e5 y" O- o7 G" p
    8. L0 G+ g2 L9 u9 Q9 _
    9
    # v+ ]/ u- |$ F7 c! f' g7 l10
    : T) d& p) T; J* {' ?6 S2 e2 D11
    ( H) R- R9 N! U% g5 A12
    2 b% a: C, G' A7 z13
    ( U' z) n3 L0 y8 A( x14
    ) A! L6 P9 j6 y; q9 \15
    2 x4 L& {/ u9 G# h! i+ e. N16
    9 d( {! l$ b7 ^+ M+ Z17: q9 H! g" S& H: e3 B% l( }
    18
    ) U$ f& ]% x, k  W* W8 s19
    $ B' U" V- M: B, v3 Y7 U20
    % |& z7 A3 K2 j21
    ( d# P% ^7 q  @5 Y22
    3 T& O: r. Q8 H2 Q- W# u23
    * i+ F' P6 }& U1 }1 y! vIntervalIndex还有两个常用方法:
    ) D+ j: d; i3 o" |5 rcontains:逐个判断每个区间是否包含某元素4 c4 ?1 Y- r8 B
    overlaps:是否和一个pd.Interval对象有交集。
    , ?$ N- g" b1 [* o1 w+ @1 Qid_demo.contains(50)
    * E. o; Q6 C; v" mOut[69]: array([ True, False, False,  True, False])
    - x5 f/ q! Z5 M6 K# T" y7 v/ m% h
    id_demo.overlaps(pd.Interval(40,60))
    $ r/ m6 Z; X* z/ v/ H4 S1 ?3 \Out[70]: array([ True,  True, False,  True, False])7 ?" w; D. \6 h0 F
    10 c6 d  b) u  O. ?
    2
    $ K2 {) v1 \" Q' g3 N: X/ M3# P# O+ Z" c- d+ c, S! C$ C2 C
    4
      I8 ^! m) m/ J5$ S7 b: l1 ~! V! }2 r
    9.4 练习3 U# `4 u& p6 I* A2 ^( \6 v
    Ex1: 统计未出现的类别, _: }7 S) E, w; M5 B  p, K9 Z
      在第五章中介绍了crosstab函数,在默认参数下它能够对两个列的组合出现的频数进行统计汇总:, |9 ?' P5 |: K- W2 @  Z1 X

    8 H6 k+ U, J! ?" E9 G7 i" mdf = pd.DataFrame({'A':['a','b','c','a'], 'B':['cat','cat','dog','cat']})' q! s. [$ {( i0 b# ~* a
    pd.crosstab(df.A, df.B)
    1 U4 T+ U1 M6 }
    1 h6 o+ @1 c- }0 @! @3 WOut[72]: ' ?" c3 L1 G6 H: ?" {0 B
    B  cat  dog
    : z; p% N( v) `0 _" ]A         
    $ G% x- D2 F3 W2 ?. D3 ~a    2    0: d4 N- l6 }: b4 j
    b    1    0
    & s7 o; m. A- ]/ K: P/ Y& |8 ^& bc    0    1
    * H7 q" O5 q2 t' P# T- ]1
    . @. G8 p$ w$ ^4 q2
    + C5 P. u3 G! @2 j2 u) C+ `6 z3
    ; U0 T, z9 i  e: s4- \3 b5 L4 U: a- T
    59 C2 \- Z& k1 X& E; S
    6
    ) x  f5 W) r9 \7
    ; V/ h; [; g. R/ [5 Q4 Q7 c8
    : [5 f* Y4 r4 `, H$ c: j3 _2 c" O9& g' d# o6 M% l$ a" J
      但事实上有些列存储的是分类变量,列中并不一定包含所有的类别,此时如果想要对这些未出现的类别在crosstab结果中也进行汇总,则可以指定dropna参数为False:
    1 K. b# R# L) q$ m4 b6 E0 }  o: t  {. p) ^
    df.B = df.B.astype('category').cat.add_categories('sheep')# R6 E0 z) I( l+ |/ ~
    pd.crosstab(df.A, df.B, dropna=False)
    ) ~0 I6 h1 Q- j5 [/ o+ U$ L4 B. b. U- r& w
    Out[74]:
    0 q+ j( r) ^' d* y) ^B  cat  dog  sheep! Y8 y: f5 O4 i) _
    A                 
    & \- }! `4 n3 j6 t* F1 M+ v3 A# \a    2    0      0
    : @( w3 ]' r; d0 i! ?b    1    0      0
    % O* ^6 h! s* K. J, j- q0 |c    0    1      0
    1 Q4 C' {; g1 F) P" v; H1
    - x- f2 c% v  X  q' t( l, y7 a, m2 L# Y2  m. b, Q5 r: Y
    3
    / x7 w% S: W, w, G' l4
    + n0 p/ n9 t* k/ [' ]5
    * v' ^) s. e1 k& |6; q) g4 b$ p( D
    7
    4 U* L8 Z# |1 l! y. n. o8
    - P& o( ~  i) @1 v' n0 W9
    7 N+ l8 H1 i3 ]" ?* C5 }; q请实现一个带有dropna参数的my_crosstab函数来完成上面的功能。
    / s) l$ \! Z5 x/ V! `+ B! l% g/ u
    Ex2: 钻石数据集# X! I- `5 v2 Z: Z4 S: I" I( I0 |/ ^
      现有一份关于钻石的数据集,其中carat, cut, clarity, price分别表示克拉重量、切割质量、纯净度和价格,样例如下:
    ( x9 e0 ]" O- C% A) h! x' c# w( r6 \
    df = pd.read_csv('../data/diamonds.csv') $ _6 Y% \% ^3 K( a: t2 v8 K) c  q
    df.head(3)
    , H5 Q, F2 U; j# G5 n2 R1 L1 T/ ?% o8 J6 s
    Out[76]:
    6 t9 f+ s$ y: t3 N" t/ U4 D( k   carat      cut    clarity  price
    3 r0 x$ X4 B* t# A5 N" `0   0.23     Ideal     SI2     326
    $ y" d3 y3 |3 q, D/ a1   0.21    Premium    SI1     326
    2 j* {* v# G; Q9 ?& w2   0.23     Good      VS1     327" t# s, E. E: s8 Z
    1
    2 k' a* j/ E  N( Z8 _' E25 q5 e7 z. w0 a! I1 T& I+ Y5 G
    3
    , N( R, y3 k( T1 p$ `: o  j: N48 h  I  V9 i& \, n& }- {
    5( Z2 j0 U7 `8 a# W9 D3 C3 r
    6
    # @2 Z" ^0 I. i1 _: q; q/ L0 y  C8 |7
    % Y  a2 L! ~# N85 \* e& p) w9 p0 \' C: P
    分别对df.cut在object类型和category类型下使用nunique函数,并比较它们的性能。/ I( ~$ w" G' O7 J, u% V
    钻石的切割质量可以分为五个等级,由次到好分别是Fair, Good, Very Good, Premium, Ideal,纯净度有八个等级,由次到好分别是I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF,请对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。
    1 O- a& K0 X9 t" h6 z6 p分别采用两种不同的方法,把cut, clarity这两列按照由好到次的顺序,映射到从0到n-1的整数,其中n表示类别的个数。
      ^7 r% u# d8 c- f5 m对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。
    & J: D! Q9 }+ w. S: g2 `* H第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。2 V* i  G' g% K/ z6 M/ f
    对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。* c" x& u8 V+ w) Z0 Y
    先看看数据结构:
    0 M# N! Y7 `; f3 J/ m$ F8 U7 T' G0 P1 ^$ }
    df.info()
      v0 Z: s6 E0 s/ c! b; s% XData columns (total 4 columns):
    ) Y3 x7 r) x2 {6 [- u #   Column   Non-Null Count  Dtype  
    , I( d8 J2 h7 B4 [" K$ T+ C8 H---  ------   --------------  -----  7 s- }+ K0 M( n7 s
    0   carat    53940 non-null  float64
    6 V3 J6 d8 f& H. `& ^; w( m, I( ^ 1   cut      53940 non-null  object + p$ ^- M" s9 {( w! f. W
    2   clarity  53940 non-null  object & n% @+ Q4 w( e
    3   price    53940 non-null  int64  $ @3 `! o* _+ l5 N. I
    dtypes: float64(1), int64(1), object(2)+ N& j3 d* D! U3 [% J/ [5 B. p. w, I
    1
    3 a6 S( X5 x) A$ F2
    5 l5 R- y, ?6 d! }7 b3
    6 d. R7 A0 E4 l4 y, e4
    & r+ m/ F* ?0 B0 S" |) l5
    ; r$ c9 z8 T( F6 G6 C6
    ) W# S. e9 s4 W6 {) q77 G7 Z9 ]' {$ N* q( S9 h
    8
    % T1 o) k% `8 u5 A: D( z4 z" s9( ^  k9 P3 {4 X2 F! p6 o$ N
    比较两种操作的性能
    8 E! J8 B' t: N6 C9 H, u2 Z! `%time df.cut.unique()
    : |/ k$ ?8 J. H# g4 x
    # r9 I. C" H" _% nWall time: 5.98 ms$ N8 M9 c& c2 S7 p
    array(['Ideal', 'Premium', 'Good', 'Very Good', 'Fair'], dtype=object)
    , w1 v) o5 `) W) o1
    8 z. `5 |* ~. v9 M" U29 x( F: ?& f) k
    3
    - `) M+ s5 [) h, N( z4
    + k5 b# J) U' j%time df.cut.astype('category').unique()
    / Y) @7 T8 C  w/ I  T- F% S0 ]
    * |  {4 z) Y; SWall time: 8.01 ms  # 转换类型加统计类别,一共8ms" h7 R2 R6 |$ _" \4 b
    ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']' r) g* ~# N- q# o3 X
    Categories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']" P' Q# R, G, ?1 y, h
    17 K- D0 h1 d0 p) G9 W- t7 R
    2/ s! a" h8 o0 ?  {
    32 R3 @" d. u& V5 o7 \
    42 r- C4 ?8 G& X5 k
    5
    4 j) y% O2 q- odf.cut=df.cut.astype('category'): Q6 t* y7 A$ ?  B
    %time df.cut.unique() # 类别属性统计,2ms. a2 e9 V( ~& U4 m

    1 h* ~/ m$ m' t4 P4 C# gWall time: 2 ms
    8 n0 V) i1 ?( I  g! F. T['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']8 s; L: ]9 q3 A$ j0 d
    Categories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    3 Q% d1 i  u  ^8 d1
    ; Y( `" S9 l+ q0 r8 y2
    " |7 y$ h; d$ O6 B5 e' w1 F% L  O3
    2 [! A8 H7 E; ^/ _7 n) _1 {( F4
      |( T6 ]( g8 ^" r( Y58 t3 `  D5 V7 O. ~) j- V
    6
      `3 K4 v( i" `% ?对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。$ f+ j) u8 F' A3 z3 B
    ls_cut=['Fair', 'Good', 'Very Good', 'Premium', 'Ideal']
    ( |: N" i. d/ `4 j# Y2 Lls_clarity=['I1','SI2', 'SI1', 'VS2', 'VS1', 'VVS2', 'VVS1', 'IF']
    / g2 J3 ~+ p1 L2 C* u0 udf.cut=df.cut.astype('category').cat.reorder_categories(ls_cut,ordered=True)  # 转换后还是得进行替换0 T. N  P: |0 i% q; x! U
    df.clarity=df.clarity.astype('category').cat.reorder_categories(ls_clarity,ordered=True)+ s) Q( u$ _0 s
    % a% c  Y* Z2 A+ Q
    df.sort_values(['cut','clarity'],ascending=[False,True]).head(3)
    ; H; [( {5 J+ X" f' \) }3 d2 N' T: n/ e* Z5 ^  l3 U
            carat         cut        clarity        price4 a* W% f4 u* Y/ |. C4 W; s
    315        0.96        Ideal          I1        2801/ t. U- I6 b1 p" X4 O+ V- r
    535        0.96        Ideal          I1        2826
    # E# `5 A/ P2 m7 n, w, \551        0.97        Ideal          I1        2830$ P1 M) V/ i! g
    1
    * ?! q: o  j7 D+ r2
    6 i1 k- t0 V2 ~  F  k3
    " \: `: V1 H- K6 B4; Z- x5 D9 Z; \  [) A/ M: J
    5" k: M! c; j# `7 l* w8 i
    6
    - F4 Z2 w( D9 a7 V1 v8 P70 \- Z% g, d7 s0 V
    8
    / M! {( `8 O; O. r, K9
    * a7 a7 `5 I7 w5 Q2 c$ X10
    & b# N- l8 h9 s( U$ r- G115 A2 \1 d0 M6 M9 w$ ]2 w! N% b
    分别采用两种不同的方法,把 cut, clarity 这两列按照 由好到次 的顺序,映射到从0到n-1的整数,其中n表示类别的个数。/ j. S8 F3 F; S) h9 F' N7 }2 i
    # 第一种是将类别重命名为整数
    0 P2 o; D, R6 t  Y9 Ldict1=dict(zip(ls_cut,[x for x in range (4,-1,-1)]))5 H8 f: K- f  {
    dict2=dict(zip(ls_clarity,[x for x in range (7,-1,-1)]))
    9 ]7 ?0 N6 n* N  e! M+ J
    % O" p% o* U9 M# b) {0 tdf.cut=df.cut.cat.rename_categories(dict1)
    3 {. k8 f' W6 g( i6 p; Y( q2 o. J* _df.clarity=df.clarity.cat.rename_categories(dict2)) ^% p- @7 ^* r) p& m/ C
    df.head(3)+ G/ E, W* S, z0 R( K+ {! [% V9 f& X
    , @; R( u9 b$ q
            carat        cut        clarity        price
      Q) ]  E+ S5 Y) B: S6 t0 a7 m/ h0        0.23        0          6                326; k+ w  f/ _$ p" ~2 ~2 f2 I
    1        0.21        1          5                326* U/ z4 A6 l0 P/ U2 U
    2        0.23        3          3                327
    & y' ?% L8 f1 y! H: B- k% u1
    / G( {' {6 p9 f! s3 a0 q! ~/ C0 c( `2
    0 j6 H9 M" h2 S5 q3
    " B1 C' ~4 D1 h5 k) c4
    9 W: ^1 t" y8 J- I9 F# i! m6 l5
    . t6 X, e! k1 e/ u% }- Z2 w67 S7 s9 S9 a/ @3 t2 _- K& Z
    7
    8 G  [1 G5 Z- `% O) s# y7 x88 v" I2 w' H! m2 O# X! o
    9
    0 `3 E" s) r/ m: \4 m3 v. k10( b8 S( G5 y% T5 u% T/ Y0 w; I
    119 p" w  a1 X9 w. P- z
    12
    ) Q( }1 g' X7 \, T# 第二种应该是报错object属性,然后直接进行替换# M+ ]3 ?  ?- r4 T
    df = pd.read_csv('data/diamonds.csv')
    3 q# U+ f) z4 p8 j, S: e1 sfor i,j in enumerate(ls_cut[::-1]):& @7 D* k0 g/ K+ x9 Z* k4 X- B
        df.loc[df.cut==j,'cut']=i
    ! y0 u/ [% u6 L1 u; h8 r! Q0 W* ^; a& M2 A0 @6 |6 k# V6 l2 k
    for k,l in enumerate(ls_clarity[::-1]):
    4 s  ]6 g( a% `    df.loc[df.clarity==l,'clarity']=k
    : ~$ e8 ?$ `# ]; x# `( H" bdf.head(3). h# b; Q! p2 D4 a8 B# `+ J

    4 [9 b; D1 K9 y; t  A6 g        carat        cut        clarity        price& e! V" O& e6 T0 k* c  ^$ M# a3 k
    0        0.23        0          6                326& l. k! E6 V) e
    1        0.21        1          5                326
    & s6 Y4 P( g+ ?. r2        0.23        3          3                3278 c) T: c; {' z& [3 |6 \
    1
    " v- g2 w* t* I2
    8 |, `) @6 E; `% t4 j3/ X: n' \& e5 V) F: H
    42 ^2 |9 }. f8 X8 r5 i% [
    5. l1 y# ~5 e) `, g) \+ Y9 u# t
    66 I% j, `; w, ?& @
    7" ]5 X& G- T4 ]" G$ c
    8
    " q) x4 i6 r! H( I0 c/ n9
    ( N3 n6 x1 ^. i; J5 C5 K10+ U' Z9 g2 t" \1 L. T" o- B* {
    118 {5 `4 j' A; }+ e2 c7 D& ~6 F4 k
    12' p) X2 R( F" m& p# a+ a7 x
    13
    - R; |; w( Q0 E& \& n对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。3 I! `; {; e7 {4 R# |" Q4 G1 C
    # retbins=True返回的是元组,第一个才是要的序列,第二个元素是分割点/ B$ `; s5 n" O* i& s' b2 ~" N) E
    avg=df.price/df.carat/ K) W* B. v% K) }4 J
    & E. @" ]) }' }
    df['price_quantile']=pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],
    . W0 y1 F. E* Q1 X% k                              labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]$ ^9 e$ L$ n" j- }
    ! {% q8 w1 z& d
    df['price_list']=pd.cut(avg, bins=[-np.infty,1000, 3500, 5500, 18000,np.infty],: Z; m7 t2 S% f6 K. T* ~
                                  labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]) o7 u- |, ^) h2 O/ N
    df.head()
    - I! e# y3 f" d. W: V! R5 T* N6 [2 ~6 y* S+ r( z
            carat        cut         clarity        price        price_quantile        price_list0 G! `, o- a: o& }: m' m7 N: R+ E+ N
    0        0.23        0                6                326                        Very Low                Low
    + s$ T$ F4 o* `; j# g  d( B1        0.21        1                5                326                        Very Low                Low
    , k/ y  ?/ B, `* M9 C" p  q2        0.23        3                3                327                        Very Low                Low" _  o5 ~+ S' k
    3        0.29        1                4                334                        Very Low                Low: q6 A, z: r* L
    4        0.31        3                6                335                        Very Low                Low                                       
    1 f) x- A2 P7 E4 r, L0 \+ t  K/ L, T
    1+ O: [$ D, f$ l4 V4 z: y
    2- ]) z) b$ c% ^  H' \2 b
    3
    # K$ _. l$ A% F) [* @# _( E2 D4
    . M! [* x( S; o7 S* w: u5& ~( n1 \5 \. c& E/ W, R
    6$ B9 J4 N) W0 `% K; _, M" _
    7# J; E' G1 r" r
    8
    + _% E: S; l' J$ S& Q4 o+ A5 J9- w* g5 Y9 S; Y& M: V" n, u- a
    10
    5 w# J0 g" T, b11
    2 g! x* C: N/ @12  V: N; Q: W% i# M1 O5 l
    13+ H# v0 \9 q! z8 u5 n" u$ O
    14, K/ v* U# N/ e6 a  G
    15
    0 q+ v3 r0 _2 E* @- r16. R) A' C- N7 H  T, y" M8 y
    分割点分别是:
    & F% J4 N9 s; F0 w2 r
    2 d3 o: s& m- O( ~" O3 s3 T) @, \" Aarray([ 1051.16 , 2295. ,  3073.29,  4031.68, 5456.34, 17828.84])5 X* x8 S' R1 d1 ~
    array([  -inf,   1000.,    3500.,    5500.,   18000.,    inf])
    ' |& U  c0 S. q  U5 J4 F% e1) u: \' }" q9 S1 G4 I  f' Q+ i5 K
    2# N/ c1 O1 K2 U8 \, J
    第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
    ( X/ J9 p) _" z  H  {. A6 d/ Jdf['price_list'].cat.categories # 原先设定的类别数
    ! h4 ?( {+ \7 W; o0 M9 _3 B# u, h. RIndex(['Very Low', 'Low', 'Mid', 'High', 'Very High'], dtype='object')
    5 A2 u& Y4 H' f
    ! K* y9 U& _/ z( F/ ldf['price_list'].cat.remove_unused_categories().cat.categories  # 移除未出现的类别8 C! E% `' n+ {0 ?2 ^: q; l3 K
    Index(['Low', 'Mid', 'High'], dtype='object')  # 首尾两个类别未出现" M% _: L9 |5 ?6 i6 \5 s$ j0 ~1 g
    1- Y# M! x" D+ Z& ^: d! c8 ?  q: B
    2
    5 v& e  L* ]5 m( h9 v1 O/ m3
    6 w3 _3 {; i4 X# x9 G. Z( l4* n, m8 X: F9 }. D2 g5 W, h
    5
    % E: J  r. Z" }avg.sort_values() # 可见首尾区间确实是没有的
      Y( T0 p0 S5 b1 b( C1 M8 x31962     1051.1627919 a) b9 |, c6 E  E( a
    15        1078.125000- f, Q! w& G9 D* h
    4         1080.645161
    / q( @4 f. c3 Y8 n28285     1109.090909  x. U+ |; o! [! `5 b
    13        1109.677419, p1 P4 B7 @* W+ `% z3 r
                 ...     1 U& e$ U1 ~8 d* `- a
    26998    16764.7058820 ~2 b7 F8 z# R6 {& E# ]; |
    27457    16928.971963" j) B0 B  O* @% r6 ?$ ^( N. v" K8 X! x3 Q
    27226    17077.669903
      T" B7 N1 N! X( k! V3 R. C27530    17083.177570& B6 Y- H- M! |& m
    27635    17828.846154
    / K+ u( N, I( r1
    8 c  n2 J, I2 A# g+ J% S2 M6 k2! J5 C1 w$ q8 X! g: i6 _- _8 T* S
    38 p0 n! _/ @. }0 `1 P( ~8 v
    4
    / f6 o) T5 i- w) _  W5 O) ^5! n; i5 L( a6 r5 J1 P
    6
    : i' K3 G+ x/ M6 r/ T7& J& O  }3 r( B7 u: M( \! D2 N
    8
    0 w3 \; M- x0 M% _0 d3 S& K9
    ( G/ q: f! ?, C3 U/ t8 G) q10
    5 ^2 D) q: b9 W( A11" F5 v1 M  z+ |2 F4 ^
    12! z5 g  r+ N3 L' x, p( F9 s- n3 x
    对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。
    3 \' F( e" A* t4 r$ }# 分割时区间不能有命名,否则字符串传入错误。, n7 ^" V! `5 j" ?
    id_interval=pd.IntervalIndex(
    6 P/ X& a0 u6 j6 \/ w# `    pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],retbins=True)[0]/ F1 {/ [, N+ w2 F& h: F+ r" A
                                )
    7 w' d! h- |. q. y' t+ C% N$ Bid_interval.left+ u* M3 z2 j3 C9 K1 g
    id_interval.right
    0 w; x* y0 `$ R# Did_interval.length                           
    9 U; D. y% a: Q" G* u/ a1
    2 E; Q* c( V3 a/ C28 ~5 N" e* t- b# Y7 K
    3
    ' C) Y9 ?4 i" N$ m! `& G, _4: g9 w: m) V! C: x
    51 {: @' ]* w( f2 Q. `
    6
    % b7 s. @* `# \/ k3 r' p+ q7
      S/ p+ _. f+ G( [& \第十章 时序数据0 D  N7 B" v) B+ A( Y
    import numpy as np
    6 E. c1 y/ ~" ^$ T# aimport pandas as pd
    ; j1 o. j1 L7 Y6 d. W1
      [# s( J* L+ B. |/ ?28 i8 T8 U+ c  W0 ~7 p2 j7 D

    8 F% e( v; _. r, O0 `3 j+ ]5 ]- t4 f) H
    10.1 时序中的基本对象! r3 x7 C. [0 j0 d) T1 ]
      时间序列的概念在日常生活中十分常见,但对于一个具体的时序事件而言,可以从多个时间对象的角度来描述。例如2020年9月7日周一早上8点整需要到教室上课,这个课会在当天早上10点结束,其中包含了哪些时间概念?
    5 u; p/ i1 I9 C2 I. A) [4 g$ j% G
    会出现时间戳(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的简写。5 J# ^$ O' O( E; @# Z- H8 Z

    ( e! T+ O* D( J会出现时间差(Time deltas)的概念,即上课需要的时间,两个Timestamp做差就得到了时间差,pandas中利用Timedelta来表示。类似的,一系列的时间差就组成了TimedeltaIndex, 而将它放到Series中后,Series的类型就变为了timedelta64[ns]。1 q' H" G- k2 E$ G. }& G7 e
    1 H; Z3 R  S& p3 X! I0 H
    会出现时间段(Time spans)的概念,即在8点到10点这个区间都会持续地在上课,在pandas利用Period来表示。类似的,一系列的时间段就组成了PeriodIndex, 而将它放到Series中后,Series的类型就变为了Period。
    5 y5 Q6 \: g, t2 U+ D0 r# f. j2 p1 `7 X: K, `" H4 L9 b; H( }: i
    会出现日期偏置(Date offsets)的概念,假设你只知道9月的第一个周一早上8点要去上课,但不知道具体的日期,那么就需要一个类型来处理此类需求。再例如,想要知道2020年9月7日后的第30个工作日是哪一天,那么时间差就解决不了你的问题,从而pandas中的DateOffset就出现了。同时,pandas中没有为一列时间偏置专门设计存储类型,理由也很简单,因为需求比较奇怪,一般来说我们只需要对一批时间特征做一个统一的特殊日期偏置。  i# S) {( M0 c# n2 B

    - N  G% H( n0 j% K3 Z  通过这个简单的例子,就能够容易地总结出官方文档中的这个表格:+ h! C$ s" J4 g

    8 Y# b, c1 H$ u/ w概念        单元素类型        数组类型        pandas数据类型% T" C0 Y9 K; \, a- L7 f, |
    Date times        Timestamp        DatetimeIndex        datetime64[ns]
    9 R% d2 H. _5 CTime deltas        Timedelta        TimedeltaIndex        timedelta64[ns]- @1 o/ p6 F$ c5 s3 r
    Time spans        Period        PeriodIndex        period[freq]
    6 I6 N7 j% E3 K6 z) m# I6 X9 H3 L. `Date offsets        DateOffset        None        None
    * Y, b) H5 i: H4 m1 \, Y  由于时间段对象Period/PeriodIndex的使用频率并不高,因此将不进行讲解,而只涉及时间戳序列、时间差序列和日期偏置的相关内容。
    $ o0 C" c7 u5 w  F7 J( }# m
    3 ^! `5 F: M8 {( w: B10.2 时间戳" D  y, m+ M. m! k
    10.2.1 Timestamp的构造与属性5 E* B5 y9 Q, _  x9 G9 ?' E
    单个时间戳的生成利用pd.Timestamp实现,一般而言的常见日期格式都能被成功地转换:
    8 J$ m+ e  {! X. m) l% f& M& C
    ts = pd.Timestamp('2020/1/1')& G# n( F3 W. B5 f7 j0 X3 f
    ) E. ^' J6 ?* O6 W, y4 X8 ?& T, @7 R
    ts# r0 g0 `; r8 P/ W- |  E6 S7 j2 d
    Out[4]: Timestamp('2020-01-01 00:00:00')* \8 s) r3 |4 o$ V% d! P

    5 O, C+ ~5 ~. ots = pd.Timestamp('2020-1-1 08:10:30')
    # O2 a& Q6 h" Q4 ]
    + N, F6 w$ Y, u- r2 p7 d7 rts5 O& Z- O3 c% H& M- o' x- z0 t
    Out[6]: Timestamp('2020-01-01 08:10:30')3 y- C! H5 g. m4 x
    1
    3 \6 O& x; y- v4 r; q2! b" q: a5 N, s! t% V% R0 f+ x# p
    3
    ' M8 H# I( C/ D/ x46 o; T) d; w. V2 x1 Q3 N
    5
    * \6 w" B& Q' q; T6
    # w$ X! b5 h. J+ v7/ f* I) F. s2 G" g: v% S
    8
    ! ]/ B9 F, C; c$ X0 W9" o% ^, R% _2 i% I+ {: {$ D; \
    通过year, month, day, hour, min, second可以获取具体的数值:5 t2 J% ^9 U0 N

    2 Q( z' ]5 W- H% C9 B' [$ E: m- {ts.year
    # S$ p9 L6 q) M! b( TOut[7]: 2020
    7 C4 N6 }( m$ o! h; f3 r6 }
      ?( O' E7 }8 Z* q+ v( |! P5 d, bts.month- Q9 q& Q( N6 p% w- ~
    Out[8]: 1* z& S) \  C% m6 l
    5 G" `' ]# R$ E1 R( u4 i0 p: z
    ts.day
    ' w/ U+ L; A4 C9 o' {! i7 ~Out[9]: 18 F! [+ m& @& Y% S( o* e
    0 t" U  Z$ }; n8 c) a+ [4 C( t
    ts.hour
    5 m1 D" q0 L2 ]4 q4 K- r% o, u% o# @Out[10]: 8
    3 I1 j6 K) e% l, f- c# H) N1 J
    * N( M" ]4 L1 e1 a7 w, Hts.minute
    & l4 N" Y7 W2 @Out[11]: 104 Q+ f6 \; K' Y  j7 x$ @
    $ ]& u! Y7 o, u: t; o5 }, Q
    ts.second+ s# @9 s+ K! N* Z* z7 M
    Out[12]: 30
    1 \# x2 H: H" J8 j7 u( D# l/ Y( [0 A/ j
    1
    2 e& m$ G0 g; s$ P4 b& ?2
    3 }6 a' n) P6 F- b& n5 I9 F$ Z3
    % D/ N# g9 N2 n% W& d4
    4 {7 ~1 t: m# t$ x  C( N5 U4 u55 A8 O! ?1 t3 V6 ^( T6 x. B$ Y7 u
    6, n) Q" I3 v! [
    7
    2 O7 _! k5 P9 s! _! Y5 i8% O7 d# l- U: `( t
    9; }5 b, j2 X% J1 V7 X* T
    10
    5 k* [# e6 A# R# E, Z9 Q* U; Z11
    , I1 J. R1 G: ^% ~' ]1 W0 B12
      n& n) O8 p$ Z$ ^$ W7 K: y* g13
    ! V$ e  Z& d8 z. j* j1 _5 [14
    : Y# f- U# Y. a: s! k( h/ c6 s5 r2 j15# r6 M& R; c2 O+ k. k. p
    16
    ! N2 m* x. j; R) c$ B! P# z17
    + k8 B0 Q; E) ~) H1 v% k7 _6 Y# D# 获取当前时间; n: w; ]( D1 |& k- m9 h; D7 r
    now=pd.Timestamp.now()9 Y  j1 w: ~, v7 s! `# u
    1
    - e; p; O/ n7 R, |& l! S6 z! ~2
    , Q" p: g, ~- {在pandas中,时间戳的最小精度为纳秒ns,由于使用了64位存储,可以表示的时间范围大约可以如下计算:( R2 G: Z. x' @4 R- _$ A
    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)( W0 W  C- i4 `( `9 n
    TimeRange=
    8 i4 m/ s; R2 `) b$ B5 S3 P2 }10
    ! c# {! \: x- F; P) r1 c93 g) L+ }# \+ _$ E- O1 d3 N
    ×60×60×24×3655 r$ A5 h' E' w7 Z' E
    2
    - s- K- ^7 |$ g64
    4 v, |% h. X! o' j9 v! z# [0 D0 n6 o1 o0 |& D7 r  t% |! Q. o! m

    7 v& ]+ g( S+ ?5 h) W, i/ f ≈585(Years): W& P4 _4 [% j0 b  g9 o/ B3 q
    ; C% B: M3 p4 q: D9 p
    通过pd.Timestamp.max和pd.Timestamp.min可以获取时间戳表示的范围,可以看到确实表示的区间年数大小正如上述计算结果:
    ( r6 Y9 Q1 |) b7 h+ J3 x' M
    # E. T2 K4 W. s1 |8 Z( x+ Ypd.Timestamp.max) A4 z& j, b( \# g: n
    Out[13]: Timestamp('2262-04-11 23:47:16.854775807')- _! c2 p, g  M: X
    / \$ x3 j6 O3 @  T% X6 s
    pd.Timestamp.min
    / h7 V* ?" K8 F9 z+ X7 EOut[14]: Timestamp('1677-09-21 00:12:43.145225')
    4 v# {6 _3 F3 C1 ?7 o. E
    : s  {5 z: x9 X' B8 Q2 Ipd.Timestamp.max.year - pd.Timestamp.min.year: G5 R  u4 J8 s7 R8 N5 x8 U
    Out[15]: 5853 L+ W4 p5 n/ L
    16 f0 S4 R2 S+ `! t) m5 f( w  F
    2
    8 V6 y$ @/ i& m! U7 M1 `" @6 E3
    ' b4 p& i- {2 ]5 N% b7 S4
    4 o7 c) X7 j7 U0 Q9 Q; ^; u5$ ?$ B1 y& R: N! i2 F% _- y( ], F
    6  O+ b$ j6 N  [5 a
    7
    / Q5 T6 i# V  `. m8) X: ^9 e+ A% ?) p3 S/ E0 }
    10.2.2 Datetime序列的生成3 ]$ b2 Y# m- c$ w# F
    pandas.to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, utc=None, format=None,
    + \/ |* X# `8 g$ l+ C% ~5 D                                  exact=True, unit=None, infer_datetime_format=False, origin='unix', cache=True)( M/ {, J1 {  i$ N: z. E
    12 p* \% F1 c" ~4 S/ N7 Q! ^3 _
    21 w; Z2 h6 r( G& ]+ Y
    pandas.to_datetime将arg转换为日期时间。
    9 H1 [- g3 M3 {: K% [: O* D  A2 h# o5 {9 @$ I! c
    arg:可以是argint、float、str、datetime、list、tuple、一维数组、Series、DataFrame/dict-like等要转换为日期时间的对象。如果提供了 DataFrame,则该方法至少需要以下列:“年”、“月”、“日”。  y, }2 y1 |  Z# T. X# \2 K3 {
    errors:
    # U2 [4 G- \( b! D0 y- ‘raise’:默认值,无效解析将引发异常$ ?% x; x8 [& `* z; q
    - ‘raise’:无效解析将返回输入% B3 q# \* W* \- ~
    - ‘coerce’:无效解析将被设置为NaT3 o9 q+ x0 `9 G4 R
    dayfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析日期,例如“10/11/12”被解析为 2012-11-10。如果无法根据给定的 dayfirst 选项解析分隔日期字符串,会显示警告。3 }7 U; |  j8 I+ U. H# e
    yearfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析年份,例如“10/11/12”被解析为2010-11-12。无法正确解析时会显示警告。(如果 dayfirst 和 yearfirst 都为 True,则 yearfirst 优先(与 dateutil 相同)。)$ G1 g0 K+ h$ }7 i7 o3 }0 O' N) H
    utcbool:默认None,控制时区相关的解析、本地化和转换。请参阅:pandas 有关时区转换和本地化的一般文档! \! z& ], M( H8 I
    format:str格式,默认None。时间戳的格式不满足转换时,可以强制使用format进行匹配。
    0 |7 b: S6 I- ~. \- j, g  E) f; }( v6 funitstr:默认“ns”。它是arg (D,s,ms,us,ns) 的表示单位,可以是整数或浮点数。这将基于原点。例如,使用 unit=‘ms’ 和 origin=‘unix’ (默认值),这将计算到 unix 开始的毫秒数。
    6 p# T! L2 y7 m# Wto_datetime能够把一列时间戳格式的对象转换成为datetime64[ns]类型的时间序列:6 p2 G/ E6 s- a( ]0 q4 P& K# }
    pd.to_datetime(['2020-1-1', '2020-1-3', '2020-1-6'])5 B; b; U( d3 S- a' O
    / ~1 V* w3 h+ `; Y" m2 M
    DatetimeIndex(['2020-01-01', '2020-01-03', '2020-01-06'], dtype='datetime64[ns]', freq=None)
    * G1 F; `3 E: p1' Y2 \0 b% B% w
    2: h/ B' {7 x  y* @+ H' _
    3% n' p2 x! J5 M0 ~
    在极少数情况,时间戳的格式不满足转换时,可以强制使用format进行匹配:* T$ F6 V3 o$ H0 C) C" e
    . M7 p/ u/ ^: z& C
    temp = pd.to_datetime(['2020\\1\\1','2020\\1\\3'],format='%Y\\%m\\%d')
    8 @; t) z2 C7 r. a! f: ^0 @5 K1 T' gtemp  _# q5 e8 L: e; w9 I  R+ h7 Q

    + o; a5 H0 s# T, P6 U1 C$ yDatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)1 t# U* W1 |) Y1 D5 [9 ^
    1
    + ?6 g' K* H  c2. R0 a- d$ \0 k* b) P8 O- s  B- {
    3& B) m& ]. G" r) X8 ]
    4
    + Z: ^8 C3 G5 v$ ~) k! F  注意上面由于传入的是列表,而非pandas内部的Series,因此返回的是DatetimeIndex,如果想要转为datetime64[ns]的序列,需要显式用Series转化:  e9 D; g1 p6 a7 H& p8 f. ?

    , P, ~) P% T$ `/ E- l9 Z$ B: Apd.Series(temp).head(): y9 [. T3 d; O. L
    : i/ f9 t+ S2 V+ E9 L3 U
    0   2020-01-01, V, U2 E* F2 W/ g& g
    1   2020-01-03! f, @! p# J+ C* I
    dtype: datetime64[ns]
    2 C+ N4 k% ]+ s0 y, A3 ]( M1; k! `+ z9 g* R, X: _6 ^% ^
    2$ l9 g- ]. v0 Q) o; }7 o
    3, ~" A6 v. n) w8 p9 K8 ^+ `! m
    4
    # \" R1 b' B4 z6 P( v54 F: `: o/ W$ a; I$ U5 t9 f9 Z  u
    下面的序列本身就是Series,所以不需要再转化。
    2 E/ ]4 P( L5 L
    4 U0 \; S% e7 h6 P5 A+ wdf = pd.read_csv('../data/learn_pandas.csv')
    : r* w% d3 c) G' B  Js = pd.to_datetime(df.Test_Date)
    2 L! C( V, s: B8 l  M. `: xs.head()
    * a. H' Z' q4 ^5 q! H
    6 ]" X0 a( C9 ^) J/ n0   2019-10-05
    4 W* \+ M- ~* E/ i$ q9 U3 I9 A1   2019-09-04
    6 M2 s5 g% k' _6 h8 ]) r2   2019-09-12  {. y5 N0 e# K8 f7 H
    3   2020-01-030 A8 A6 }9 i2 ?$ s
    4   2019-11-06
    4 m; t5 o) }" r. y% \8 q6 F) I# d2 DName: Test_Date, dtype: datetime64[ns]" x' F6 x7 b1 z6 a- K6 ]0 Q3 ]* O
    12 k9 w; w: a) o
    2
    7 L. n$ u$ e9 z/ j/ D3: S- [5 d( B0 B# L( E
    4
    : \& k+ @3 b. c0 E5$ L' O' }+ r0 c2 y2 x) W
    64 y/ H/ A- y" l* s7 c
    7. U* i+ Y$ m: m+ u4 M" f2 [& [
    8
    + ?, C8 I0 K7 u3 G; b9
    " l. n: o7 u( @% j9 }( R10
    3 Y9 M3 ?1 K) ~2 _/ m- P把表的多列时间属性拼接转为时间序列的to_datetime,此时的列名必须和以下给定的时间关键词列名一致:
    6 h, @/ {* p' ^( v6 ^: Ldf_date_cols = pd.DataFrame({'year': [2020, 2020],
    : b6 }3 F" `3 G: h                             'month': [1, 1],
    8 b) `1 w$ P* O# V0 q                             'day': [1, 2],
    " q) N+ G* w4 g$ _0 R                             'hour': [10, 20],8 [$ R  N& e+ Z* m( k
                                 'minute': [30, 50],& k: A" Q( n( ?1 q% n
                                 'second': [20, 40]}); ?) |5 l7 |9 ^2 g
    pd.to_datetime(df_date_cols)
    2 V4 {, O$ {% }6 H, ^, x- d2 B3 e: B
    0   2020-01-01 10:30:20
    ! \8 X# Y$ S% K5 x1   2020-01-02 20:50:40
    7 E% ^2 D9 V! t* W( @8 gdtype: datetime64[ns]2 A: M! Z/ b7 G& O  ]. v
    1! b7 k+ G0 |1 O5 z; [' Y/ S
    2+ K- z) z' L9 b; ^1 H, s3 y
    3# i4 k+ z! N: i. }: s
    48 J) R! B: q$ q; C
    5* o+ Y/ q: `- F# p
    6
    , d0 y( O6 I0 W: k7 S: N/ J7. \0 e4 @9 i4 q- v4 W- v) j! q/ v
    8
    * }6 h1 Y/ g! n+ b/ u9; t4 _! a+ D9 C/ ]0 R2 A
    10
    : B3 o9 @& C/ u9 k# c2 ~- }( N11
    6 ~5 a  ?- B. I& q' [  d, S9 \date_range是一种生成连续间隔时间的一种方法,其重要的参数为start, end, freq, periods,它们分别表示开始时间,结束时间,时间间隔,时间戳个数。其中,四个中的三个参数决定了,那么剩下的一个就随之确定了。这里要注意,开始或结束日期如果作为端点则它会被包含:" I/ L/ P  A8 J4 r- o
    pd.date_range('2020-1-1','2020-1-21', freq='10D') # 包含
    9 c- n' }" u1 Z# |0 U: J( h* B5 zOut[25]: DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21'], dtype='datetime64[ns]', freq='10D')" K0 P7 f% Q5 }  D' k

    2 e! ]: z0 D6 k7 `* \- Epd.date_range('2020-1-1','2020-2-28', freq='10D')) h7 M% ^8 C% f, ~& `6 _6 |# r
    Out[26]: & `7 e+ W2 E: Q3 x
    DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21', '2020-01-31'," \0 @+ r/ J& k8 R$ R
                   '2020-02-10', '2020-02-20'],' |) p$ ]; n3 O( P* P6 w9 F
                  dtype='datetime64[ns]', freq='10D'): Q% [4 W* @1 m. O1 V" v

    - D8 X" p& b4 E: }# h" @pd.date_range('2020-1-1',
    ) z4 M. h& \4 ?              '2020-2-28', periods=6) # 由于结束日期无法取到,freq不为10天
    , D! W& c" [6 Q* u! P( @* v, w
      E" V( J4 S2 c) @5 Z, q5 J! QOut[27]:
    " ~7 S% H1 {5 v0 VDatetimeIndex(['2020-01-01 00:00:00', '2020-01-12 14:24:00',* [% h* O+ ]* h# l$ T9 C/ j, ]1 d
                   '2020-01-24 04:48:00', '2020-02-04 19:12:00',, m7 C: W, |1 C: Q) f9 t
                   '2020-02-16 09:36:00', '2020-02-28 00:00:00'],
    : u7 s/ a4 f; M! r. D' d              dtype='datetime64[ns]', freq=None)+ _/ a/ b8 o1 X3 Z& [
    3 I1 l, d$ ~) F" I
    1
    ; {7 p4 j3 v! _1 |+ s4 N2( e% i+ ^7 O7 g1 A* l2 `
    3
    ' O( v9 s& }' V) W! a4
    5 f% B$ K7 h9 u9 r: b5
    ) T( j( Q" f& V1 ~; ~; [6
    & a0 g7 l4 |" {  ]2 m7" A9 s% o" D5 p8 u- q2 b* u
    86 U. a/ S. B& K' H' l! R6 j3 u
    9% s% O" ?+ S+ p' G% ^
    10
    8 h0 }+ ?9 Q9 L0 o3 J115 X4 V4 ~- Q. n  f1 G
    12
    $ ~8 Z" O+ d2 A13" L1 Y6 B, F* \1 V* i
    14* j8 U6 w2 k. S1 |: \- g" V
    15! Q- d* o6 M+ H9 k9 M- ?
    16
    + K4 X5 Y. U: e176 U+ r" J5 U# h# `4 p/ X1 H( b4 v
    这里的freq参数与DateOffset对象紧密相关,将在第四节介绍其具体的用法。
    3 |( S% P2 g  i6 P& L; F( Z, \3 I
    ) ^7 H) v  v1 {2 K+ f2 s【练一练】. w6 X  |: P, h% `
    Timestamp上定义了一个value属性,其返回的整数值代表了从1970年1月1日零点到给定时间戳相差的纳秒数,请利用这个属性构造一个随机生成给定日期区间内日期序列的函数。
    , c- u- ^0 B+ R0 y% Q/ T
    . v3 C2 W9 v2 L/ J' ^1 als=['2020-01-01','2020-02-20']
    ! M6 v7 \( H1 F& a" {def dates(ls,n):" Q) ]$ C( |8 ~
        min=pd.Timestamp(ls[0]).value/10**9- d% z' \5 N# R8 r' i7 m# r
        max=pd.Timestamp(ls[1]).value/10**9
    , g2 d8 |* ?" n8 _; Y9 @. ?    times=np.random.randint(min,max+1,n)& b+ C7 b- l1 \( y
        return  pd.to_datetime(times,unit='s')
    ! `/ `$ c* g9 {4 T2 L$ kdates(ls,10)   ~) ?  E7 w1 W5 K
    % a5 ?7 ]( c5 i8 {! A# i5 L
    DatetimeIndex(['2020-02-16 09:25:30', '2020-01-29 07:00:04',; S) X; ?% e; l* E2 b! O' X# t
                   '2020-01-21 12:26:02', '2020-02-08 20:34:08',
    3 Y% U1 J9 P- R8 [# Z( M' `               '2020-02-15 00:18:33', '2020-02-11 02:18:07',% [7 F+ ]& _3 ]' e
                   '2020-01-12 21:48:59', '2020-01-12 00:39:24',3 o  L0 L7 T$ I; u
                   '2020-02-14 20:55:20', '2020-01-26 15:44:13'],
    ( X$ p: a$ a; A; i- s              dtype='datetime64[ns]', freq=None)2 L2 a3 q* @: w
    1
    " n2 w3 ?$ p0 j# U8 Y2: _3 [; u6 J6 v1 u
    3
    3 Q! v1 L7 m" ?# n6 ?  M4" b+ }1 F7 ^8 o4 k! j8 H
    5
    7 `6 F: }& B; C2 _63 a6 ~, T+ m3 p* n) C* t" ?0 K5 Q
    7$ N5 O; \4 _) I) p. {
    8
    8 @3 z8 ]8 ~8 ]3 |. F; ]95 A  A. `8 e4 |) X2 `
    10
    7 m3 k2 i6 i% }2 I  P2 u2 T11# K- W) ?# A9 a
    12
    $ F0 I4 q2 @5 l: v( O" Z& [$ ], k' l13
    ; p2 `. ]( q; z# }, M14
    6 ?5 \! ~- ~' r- G% B) A2 \1 G2 Casfreq:改变序列采样频率的方法,能够根据给定的freq对序列进行类似于reindex的操作:4 d6 K+ c5 m  ~
    s = pd.Series(np.random.rand(5),
    ( ^9 {# @& C) ]9 e( ?% r            index=pd.to_datetime([; [( O9 B8 S8 s3 s+ d" s5 f4 Y: @
                    '2020-1-%d'%i for i in range(1,10,2)]))
    / N$ m4 R" F7 v
    3 s6 j/ R/ j0 [6 x% h* b. Z8 d4 ?( J, o3 }( g
    s.head()
    - u( a4 C# s4 h" ]% P4 o' S# s( ~& KOut[29]:
    ' |; E. n" F- S% R2020-01-01    0.836578
    7 Q) H3 D. j! \2020-01-03    0.678419
    ( i/ n: J% a* b5 }2020-01-05    0.711897
    ! @3 Y: o+ B, r) E6 u7 w2020-01-07    0.487429. C9 T7 A6 }7 e( d$ t! A* `
    2020-01-09    0.604705
    + ~% L0 m! |! x% hdtype: float643 z# T+ h. C4 i, ~

    * ]) L; t2 l1 [. N5 Cs.asfreq('D').head()1 A8 z* E4 I- j  g! j" d; V. Q
    Out[30]: $ \! E# H& n* W) A( _
    2020-01-01    0.836578
    7 s% G. j. Z/ |) [2020-01-02         NaN. V. o0 J' T. _/ V
    2020-01-03    0.678419: d5 b9 `; D9 b+ T0 [0 q# z
    2020-01-04         NaN) A( l) t" D" [5 U3 Z# Y; h- V4 e
    2020-01-05    0.711897$ m1 \7 U, r( t
    Freq: D, dtype: float64
    * s( H) x9 M7 x' p6 N, s* C& x1 _
    ! p4 M+ t$ \  i" Q* a% W5 ?; _; ys.asfreq('12H').head()( ]- L7 @$ B/ N- N
    Out[31]: 5 v" w( \: [; ~# H% l
    2020-01-01 00:00:00    0.836578
    6 n9 L' `7 e; c* L4 Q2020-01-01 12:00:00         NaN
    ( t1 u4 J* P: t6 _' b+ k) S! }2020-01-02 00:00:00         NaN
    5 X$ p) ?3 b8 A9 I' b2020-01-02 12:00:00         NaN
    : m* |7 s; T$ N4 H: I+ ?. l2020-01-03 00:00:00    0.678419
    8 _" D7 B) b# d* o- z! mFreq: 12H, dtype: float64
    # [' z6 ?- r4 c7 ]
    , q: F  L9 m  I2 l1) x# @; O& _& b" I& `/ z
    2+ {, g2 Y! H1 M% t. i
    3
    5 W. P2 o9 `; L- v) n8 B4
    2 W: n% a9 [0 y4 T5 M2 G! u5! J9 ~, a" B: ^
    6; k9 e1 B- U  c2 H( b6 t- q* \. n2 |
    74 n8 ?& u6 [, n0 y/ y) V  H
    8
      b& y  q' g7 Q7 w6 ]0 m7 z- |98 _3 }! f. F! q4 E- [$ r
    10
    # [( p3 U- _/ ]4 ]* L; Y( |7 o11
    + Z, m5 S+ }: u12+ ]" @9 }: C0 W) q9 c
    13' x4 ^! o$ G3 k( O7 [; _2 Z0 j5 M
    144 o' I7 J9 G) |. _
    155 t/ Z# ]' h6 \, B1 q
    16
    ( N. X: P; `& E2 H6 l4 P8 c$ ]- f3 `17
    4 J& `; y8 ?% ~7 w2 p180 A, Q* N8 w; a
    19
    . U0 A9 F; i* }# L) e20
    $ `: Z2 v# d4 s) M! p, }218 B* e1 z, B+ n7 z: }
    22
    7 W1 Y; p, t$ j% w23
    " S3 U% f8 J# u24! j/ i; G/ @0 c+ q9 }1 t
    25
    ' n& u( u" c+ M8 X26
    2 t6 U; j( x3 b2 f27
    ' p% Z8 D; A' C' Y! |8 a28  _  P$ K- G3 @  C
    294 M; E. i" q6 s. C5 c7 V
    30% C  Y6 V7 Z6 R! c5 `, r; t8 {
    31- C" W' }4 I! g9 `
    【NOTE】datetime64[ns] 序列的极值与均值  q, e& T. r) H7 I" e& \1 e, P) D
      前面提到了datetime64[ns]本质上可以理解为一个整数,即从1970年1月1日零点到给定时间戳相差的纳秒数。所以对于一个datetime64[ns]序列,可以使用max, min, mean,来取得最大时间戳、最小时间戳和“平均”时间戳。
    " z8 @; N7 N4 \
    $ M) W+ \, s9 ?3 p% q! n10.2.3 dt对象
    7 I1 d6 ]- B7 ?( z5 j3 l  如同category, string的序列上定义了cat, str来完成分类数据和文本数据的操作,在时序类型的序列上定义了dt对象来完成许多时间序列的相关操作。这里对于datetime64[ns]类型而言,可以大致分为三类操作:取出时间相关的属性、判断时间戳是否满足条件、取整操作。
    5 c1 I+ }: A5 `) ^2 `
    - p* T3 D' J5 s% _第一类操作的常用属性包括:date, time, year, month, day, hour, minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter,其中daysinmonth, quarter分别表示该月一共有几天和季度。9 ^3 r8 f$ ?- r4 v  S9 k
    s = pd.Series(pd.date_range('2020-1-1','2020-1-3', freq='D'))+ X! P% ~: C( ]" V# i
    $ A3 K- y  E! k5 ?. o' k
    s.dt.date
    ! A- h. v* A2 C" V" ^# c: ~5 EOut[33]: , h  ]$ l) ?2 t1 J  b$ h
    0    2020-01-01
    + ]9 L; t, e0 n4 T* g$ N1    2020-01-02
    " `# X! u4 E: D1 t, U* y. T2    2020-01-03; z/ S6 e! A" f/ y6 Z$ d5 s
    dtype: object* m( w. s8 U  H2 @. N

    ( s& f5 y. F- u$ K; w7 f3 As.dt.time
    - Q4 Y' u, G3 }) `: EOut[34]:
    - u7 w" Q/ F2 I' w  U0    00:00:00
    6 ^% ~2 P$ Q" T) d1    00:00:00% w% c6 ~% ^( X5 B5 O
    2    00:00:00/ G9 |: o% e/ P# [. G0 g
    dtype: object- w7 `& q0 I0 n- U5 |

    # Z2 t4 \0 z1 L% Ks.dt.day# l9 }2 w" L1 e( i. i0 X
    Out[35]:
    ' D& P7 w  l0 X0    15 p1 l- ~  e+ ?5 j9 c' ~+ R8 x
    1    2: o/ J$ f+ _) y8 ]8 j8 f
    2    3
    ! H$ {! B+ x& k* tdtype: int64
      Z2 r% e) r2 j# }- X! {' d$ g8 s% j5 B% d: f. a8 V% {  \8 {8 J4 B
    s.dt.daysinmonth+ s/ Q# m( R, e" g" E. c' H
    Out[36]: 6 ^- f! f$ `9 ?4 U+ J& H
    0    31
    . \8 J9 C3 a) f; j! z1    31
    : I# a+ T. {' Z: A* H  K% Y( \2    31
    0 M- U+ g1 }/ ]' Q! _6 Z5 Ydtype: int64) {  e" I& H0 i3 L* h* m
      ?' O' x% i- K) {: s* x/ O
    1
    + c. G+ H) m0 }& Q2 L4 d7 d/ u) V2; s$ {$ I5 y  Z! R3 w) V
    3
    ; G  [4 y5 z3 w: Z0 a2 T/ M4
    * p1 U% J# _" b57 T1 |1 G5 x. M6 n4 r* Q
    60 T! ^! t2 d7 b) @0 x
    7
    4 y5 z* Q& Z  z" X, s; y8
    5 Z* _% b' s0 O$ d- @  C9
    2 e0 b2 V5 Q( J6 Q! @% j105 O0 w) s/ L- ]1 E( L
    11$ z8 d6 e2 A4 x; z
    12
    ( M* c7 J4 k" e! u9 t% U13+ G- T) O' e, H4 Z/ F3 D4 h
    14
    . L- T0 Q& D; j151 `* |4 P1 F9 v) Y
    16% w. b2 O4 u; p0 o( t; K: p
    17
    ( T6 W% M6 l4 e* O5 y. n182 S/ W2 Z8 c) a; E, A% H) k9 h: F/ G
    19! ^/ j" ^1 R( o8 ?5 O6 G
    20
    ' F4 S( w3 n3 s1 `# b6 O218 Q) n5 A" F& |* K' \6 w
    22
    1 U% N0 L+ Q+ ~, B23
    3 _8 k) A+ T! |9 k/ L! ^24
    0 i9 u' i& m8 y. G- Q: z25! X# p; b) {+ _" q  z
    26
    / g1 y) [' \# T5 W+ {27" {- Q1 H' z# C: a6 g$ T8 W! ]0 n9 Q
    28/ n0 i# i) {( F& E2 _7 q3 N
    29
    + j; [6 z: d0 n, ], d* R5 n  在这些属性中,经常使用的是dayofweek,它返回了周中的星期情况,周一为0、周二为1,以此类推。此外,还可以通过month_name, day_name返回英文的月名和星期名,注意它们是方法而不是属性:8 j( ~% r8 p* E* i$ P' f7 D% E
    ; }1 _, F% m$ |, a
    s.dt.dayofweek( x0 E3 ^1 `1 R0 f: {2 b7 u
    Out[37]: 5 B. a0 P) Y* d! L9 k
    0    2
    8 h+ |+ F  E) P2 t1    3' N$ X: U+ i) z+ o! l
    2    4
    6 z- h) s+ Z- L( h' `8 u" {( o- Ydtype: int643 W& z- I. N& i! r( c0 B4 Z4 Y9 [
    . D' _% w: S, S8 [4 i% Y
    s.dt.month_name()' i$ _& U* b" V! I
    Out[38]: 6 l( M" V* P- ^; r# {- S$ O5 G
    0    January) I, a  @4 R% c9 b
    1    January
    / y" O( K" B) N7 n" s, ?. `2    January
    , g, H$ K  I9 z& edtype: object. H8 g2 U; }& v; b6 j

    2 `3 F$ p3 H8 I6 Ms.dt.day_name()' E5 k& C1 z1 N. L. I1 z8 ?; l
    Out[39]:
    1 M# F. \% G. m& M7 j0    Wednesday
    # o$ y- o- Y" i: F1     Thursday8 _2 B1 N: N5 a, K. ^* V  m
    2       Friday
    / ^, t- g* q# G  v1 P' Wdtype: object' m) A" M: D: U( _; }: ?
    * _1 f2 l: [! n
    1
    * b# }: W. k4 e* K7 A2& ?* x. X. H- h4 H
    3
    / T2 P7 a2 H1 m" T2 S, S! R42 a1 g8 Y2 z" v; K1 t. c
    5
    8 Y: g% R6 \( B& q4 w6
    9 ~' ^; Y# `# C5 n/ ?7
    # Z2 c7 k" P+ o2 N( d8
    + g) Q5 \% _/ W4 D& _& o9
    0 I, m9 Z2 j- S( B10
    * v& o0 i7 o- l118 }* V# ]2 j/ I
    126 p2 w# E9 I7 x3 w" P
    139 a8 Z# g- n, E( w
    14
    ' X( j$ m  i! V" g, \/ q  O15
    ( j1 x$ G+ v3 y, Q+ g0 }16
    & Y0 W- d  \8 ]; ?1 p8 g9 F17( B1 s: D  N, w7 d# Z8 j2 V$ ~5 L
    18) P- X: T1 w" Y
    197 X" {5 ~  s0 u, E5 E7 g
    20& N# \+ U3 B/ |/ ^1 p+ j
    第二类判断操作主要用于测试是否为月/季/年的第一天或者最后一天:' J! Q9 D! p. d  ], v" I
    s.dt.is_year_start # 还可选 is_quarter/month_start
    " H9 N) v3 k- j, X. QOut[40]: ( F3 V; c* h9 a: k5 [2 S& x
    0     True& X9 G& Z3 S5 o$ F) ^: n
    1    False
    $ U) ~" Q9 W' |* b3 N2    False& t3 X  o; }4 P/ {5 A) I
    dtype: bool: c; \* r9 [  j% H; |

    / A* r" G& M. C* E) Ks.dt.is_year_end # 还可选 is_quarter/month_end
      F# ^4 B8 s9 `4 N2 V5 ROut[41]: ( @1 d7 A0 ~7 }, ?
    0    False& \' W% g' q: U
    1    False2 [9 b$ H8 w1 S
    2    False# m8 @$ o; E& ]5 S2 e7 _
    dtype: bool
    , x& c4 ]" h# M% J$ o" l; \1, B4 S; X& }4 F* C
    2
    ! W) ~& S1 f  _  p* E3
      r+ H5 l; u( h1 i- ^3 \40 t7 T0 a  Y5 m. E
    50 r, J/ O# A2 v
    6" e0 }' |# K% a9 p
    7
    7 E, u8 @0 r. H' |/ P* J% }8
    6 j  y: A5 F& `$ ]& v/ F) n9
    8 r2 F$ @6 }; y! z10* _+ e$ n1 a5 ?0 p
    11' H8 V6 u( X5 H) A; i
    12+ d! [3 n8 ?( H, y) ?
    13
    ( |) j# Q# l2 P# G第三类的取整操作包含round, ceil, floor,它们的公共参数为freq,常用的包括H, min, S(小时、分钟、秒),所有可选的freq可参考此处。( V- ?6 r  D4 B- d
    s = pd.Series(pd.date_range('2020-1-1 20:35:00',0 r/ M4 [) w3 ^( p: H
                                '2020-1-1 22:35:00',
    ; y6 C( ^& K( _" I6 M# P; m                            freq='45min')): L. \( k/ A) Q% z, D  P

    % f8 p3 b& a; r1 Z% Q& R2 K+ L& v$ d, A( h7 s  ?
    s! L& @/ Y2 S; K
    Out[43]: , d$ v- j( m8 X6 a' B2 X
    0   2020-01-01 20:35:008 j( u) q; Y5 `- O$ z, X
    1   2020-01-01 21:20:006 @6 E% O$ z0 `6 K. }" i( y# H! m) g* e
    2   2020-01-01 22:05:003 r2 C$ k: a1 j/ |0 U7 B
    dtype: datetime64[ns]
    * r( {4 q% }! L; \/ i5 y: t
    5 I4 ?0 W3 V: q. H5 ps.dt.round('1H'); W+ Y8 i- r) c& _8 l
    Out[44]: 5 K# J4 [4 n" W3 R2 @& O
    0   2020-01-01 21:00:00
    - t' |1 w1 ^' H3 ~+ Q1   2020-01-01 21:00:00
    ) S6 K; M: Q. @. K& u9 X2 N; u9 m2   2020-01-01 22:00:00
    1 Q4 _* R$ Z2 idtype: datetime64[ns]8 E+ k+ `0 g3 v2 ~
    1 l' y0 s% v! I6 t8 b1 Q
    s.dt.ceil('1H')+ [: z2 D$ f! ?2 c
    Out[45]: 6 o& S4 B3 e; n( w' e- `5 i/ N
    0   2020-01-01 21:00:00
    4 |3 S" \' E  G; O5 i1   2020-01-01 22:00:00/ w! \3 P! G# s9 [
    2   2020-01-01 23:00:00
    0 s5 H$ ^: J1 u7 Zdtype: datetime64[ns]
    0 Y8 J5 c: U( `: y' a
    + h6 t6 W/ I$ O8 xs.dt.floor('1H')! I' c2 m/ }8 N3 ]
    Out[46]: 6 l2 C! k% }7 e
    0   2020-01-01 20:00:00
    : k  s  S! [$ S# d  s5 N1   2020-01-01 21:00:00) l" A1 m. A$ u+ E
    2   2020-01-01 22:00:00  R) O0 R, `9 f8 s
    dtype: datetime64[ns]
    2 F% u; d0 M6 O; E8 c
    - ], H  G5 J( e1
    9 p8 P4 H1 Y- @* l! A9 L# t" |/ ]2' G: `1 u* M1 ]1 Z8 Q: R# i  D% h
    3# g/ g6 Q4 C( a) `% C
    4+ W/ d; |3 J: N: h& Z
    5& @* _! H6 Z2 Z" C6 |* y
    69 t) q: o6 \( U% N0 i' q
    7$ A( D, R3 n5 z: G
    8( c0 J# V2 s! _' t$ `" z
    9
    ( E2 ]& B3 c' S9 E8 a10
    0 R3 x# V1 K# P/ T' V11
    " x  C# l3 {: n- }( a/ j124 [% X: X" g3 s2 X, G! \! O
    13( D; g  P: O. h3 z
    14
    : I6 _2 \5 k! \9 {/ ~8 D. S15
    $ _3 E2 J/ f, K% e3 N& V" k1 |16
      `1 ^; K3 |9 `% @$ x; E# H' |17% @/ Z+ ]0 w4 o9 {
    18
    6 v+ _7 g6 U3 w( A/ e5 a19
      A# _) m# \/ K: T0 h20
    3 C) M, A! K/ ?' Y211 h: |+ T2 d) r# c8 G6 w) Y; w
    22  @3 l) w) d( I4 I. g+ x, n5 A
    23
    , ?0 x8 I, s6 a3 e24
    & `; y1 o+ f7 `( Z25
    / a% R$ d! C" ?/ L' c& R26) r. Z2 Z. p+ q5 L8 ?
    27/ y7 Q6 p, C8 q
    28
    ; d0 ~: e! _" E6 m$ Z  y4 P$ A$ O290 C& g, l( i2 ?& R1 u  E
    30
    - j8 [* S! c/ |- i+ d$ e) M$ g2 ]6 j; T31
    4 x( ~+ I! B# n) P/ c0 ?32
    % e8 M0 [5 C. q, o. D* w9 q10.2.4 时间戳的切片与索引0 N/ U5 u$ F% H$ G6 ]+ h# O5 y
      一般而言,时间戳序列作为索引使用。如果想要选出某个子时间戳序列,有两种方法:
    ) q/ t' j  Z0 L+ P) W1 O. q9 C) ]" T4 g* K
    利用dt对象和布尔条件联合使用" G  T/ T1 _7 E/ L
    利用切片,后者常用于连续时间戳。; q& Z5 w# `, A. N" h
    s = pd.Series(np.random.randint(2,size=366), index=pd.date_range('2020-01-01','2020-12-31'))$ f8 {9 r$ R9 k" M4 _% i* Q
    idx = pd.Series(s.index).dt
    + j; Q: N* R% l- i, ?s.head()
    ( @* ?, g  S2 z- R
    7 Q* h) e6 U; h0 ^& w2020-01-01    08 M4 Z9 m( Q9 _
    2020-01-02    1
      B+ [( e7 c7 v- M! ~2 E3 o2020-01-03    1' P* C1 W& F/ o5 b; o( r( W! k
    2020-01-04    0" I- F$ R( J- @+ ?: a% |
    2020-01-05    0
    " v. d' L- @$ }" x4 i$ O. g  ]$ j. yFreq: D, dtype: int32# M7 H( G6 _% t
    1$ s% S2 O* e' O' e# [* c0 o7 S
    2. l  w4 ^+ A# G! a8 B
    3
    : h; Z! p. ~( y+ H' I$ V& B4
    4 y2 o' D3 ~. M% w  i/ S5( E& C2 T6 `; Q0 X# {. ~
    6
    ) L, s+ e' T4 f' x+ d! ~+ s7' F* }2 S3 b) |) p' T
    8
    , Z! I$ z% v( @" w* @/ t9
      r6 y; K- j5 A101 b- H) j3 j' V. K5 D3 v
    Example1:每月的第一天或者最后一天
    - O8 a3 ?. k) W! O( _# k: e# X8 l8 q* T5 ~( R
    s[(idx.is_month_start|idx.is_month_end).values].head() # 必须要写.values
    ( y; k$ f1 r, U0 [( U6 f/ A9 Z* iOut[50]:
    / B( |6 p9 l& {! F9 l, l2020-01-01    1( C/ g1 }5 m8 e
    2020-01-31    0
    ( n( G6 U) s& k: c, y! ]2020-02-01    1
    1 h4 y7 Y  A& D! ^- J, e" f2020-02-29    19 A/ j) @7 H+ }3 R8 L
    2020-03-01    0
    , F' K5 H( v  V3 Q6 Fdtype: int322 O6 f4 k% G2 G% c7 j! U
    15 w% t: N8 V+ z) ^. X
    2
    $ D5 B! Y6 ?% m% `3. y" j; W' k# X5 x
    4
    ! k9 w% A3 e- ]" A2 E5
    ! [. z, x$ N9 n! I9 H* \. {. [6' j' L0 C2 v" h" _; L. j
    74 N% {# e% M$ `% V' g) {5 |1 q
    8+ H" A2 S7 a* B8 p$ P
    Example2:双休日9 d) [* @/ ^: {' }- g
    ; t1 r6 b' z. {  m/ z
    s[idx.dayofweek.isin([5,6]).values].head(); L0 {6 |0 B1 n' I5 h5 h9 N- d
    Out[51]: 0 Q5 Y+ U! N  N: H% ~
    2020-01-04    1, _1 y. B* H7 R& V$ Y' R3 ~  i& y- s
    2020-01-05    0
    ' y9 r( f% }0 F2 F1 a4 z; o9 B6 x2020-01-11    07 Z1 h' C: s. j, b+ w+ I, M- l
    2020-01-12    1: o5 I1 U4 K+ l" C
    2020-01-18    18 j# |- D) V' z% A1 v
    dtype: int32
    ; ^- z9 ^$ z) n  n+ `5 ]$ D1 h/ j/ G12 A+ m$ F2 r+ Y7 Q6 r# x7 V
    21 U# W% g5 u  p1 I; k! w, ?
    31 m/ \- @9 @+ _( _; j, t' b
    4+ D: v; ]$ [/ [3 q, e
    5# i) X0 ~. g/ \8 Z* w
    6
    4 F9 K) c$ ?& X/ g0 G' |7
    ' X5 @% T& Z6 t8 M6 g$ |. C9 @8& z8 |" n# w& K! H; i4 _1 L1 `
    Example3:取出单日值: W  m3 o' p8 T( z4 m) z0 C

    ) Q: w9 S: y+ x3 ]5 Ss['2020-01-01']) A$ ^9 y3 m' Q' ~9 O5 c* j
    Out[52]: 19 s, n$ |. G* w: Q2 w2 p9 F8 `
    ) K4 n( e$ ?' Q8 P! G
    s['20200101'] # 自动转换标准格式
    9 r% h0 C% R# b4 }Out[53]: 1
    6 Y7 R; Q: P' R+ Q; g; x" a7 P+ ~1% X2 U& Q$ ~4 M1 ?. e& l) g" b
    2
    / t5 G7 F! H* J! l& M$ u+ Y  t37 C3 T* L, h% P: A
    49 v) ]6 u- D1 H, _0 ~* [  d/ v
    5
    5 P$ Z1 ?) f, m2 cExample4:取出七月# z/ w; u- s" I) ], P2 }
    5 l& Z/ o# n3 A/ z; ]; s" t( n
    s['2020-07'].head()$ ], \# g$ c% C$ Z  r
    Out[54]:
    4 K% e* y/ u8 O; D8 _& g8 k2020-07-01    0% _& q. }% I4 K" g  z4 \" v/ [7 ]2 D
    2020-07-02    1% d9 L+ M7 x6 C1 q
    2020-07-03    0+ I( R6 t  {' I& A3 ]( E
    2020-07-04    0
    / E% q" u" O6 t, ~% A2020-07-05    00 e3 O  n1 [5 V! [
    Freq: D, dtype: int32
    0 r) }; j: T& m& h% k# @: g4 \5 M+ l1
    : L/ k4 o% Y) ^3 M7 ]2
    1 ~) L8 P- E: c! r% I4 k3
    $ R- d% d# A& U4$ O* w1 X0 |8 I% P* V
    5
    1 F# O6 F# J6 @. @. |67 F1 G- k9 e& M8 ?" t! Y. f
    7
    , i/ v7 W4 _) A1 y( J  l8: e) K) w3 [! w7 ]$ p/ Q
    Example5:取出5月初至7月15日
    ! L& b( s* l& d6 P. _- W4 A: k3 f4 T
    s['2020-05':'2020-7-15'].head()
    0 \  l9 W7 k6 V9 z2 l' VOut[55]:
    3 G1 `) w% j- I9 c2020-05-01    0& p. m6 A& S7 s0 P
    2020-05-02    18 G$ K" K' H0 f: L
    2020-05-03    0
    . o' K* a! S* @: l2020-05-04    1( N7 q/ {$ W4 E7 [) f. P
    2020-05-05    1
    0 }; o+ j/ [# l$ F# u, SFreq: D, dtype: int329 g' `( o2 \; q0 E
    9 Z) p1 k; P0 C$ R  A8 g3 H
    s['2020-05':'2020-7-15'].tail()2 i0 H* P& v* ~+ e
    Out[56]:
    & X7 B8 X- m; E0 ]# h2020-07-11    0# A* k4 }* d; S, P3 J
    2020-07-12    0' e& b, Y  q1 p
    2020-07-13    1
    9 J! }; H( {7 O, c/ h, p2020-07-14    09 s5 a, }4 A% x2 w; c( q
    2020-07-15    1
    0 m) W- D7 g! V3 b! t. |( S- ?Freq: D, dtype: int32
    ( K3 M3 _% N: N9 X7 j: X; z* V( r5 D- n" l: k$ {4 i
    1: {* E% f% a/ N
    23 z1 Z3 ]! [& `3 C& t1 z; ^
    39 I+ l! _- x4 a( n# }6 Z3 W
    4" p8 E7 R! E' e9 O: ?2 t
    5
    # N( X7 l- i0 R6% S# i9 T0 j& q, a4 S. S2 @
    7
    0 Q! X& B& h# q: S0 U83 K( \' ^4 ~8 m2 g8 ]
    9" N! U, R! M1 i4 k# {
    10
    3 u$ }$ [7 R( N9 |; C7 [6 Q: {11
    1 }3 U2 Y3 _0 w3 W( `- z122 G3 b4 f0 d$ j1 k/ w8 c
    13# I3 O# ?. T) v% F* V4 e$ C6 [
    14
    , F) ]. q# Q! m0 V% k: O/ y8 h- x8 m15/ X7 M* x& \, W1 t
    165 |* ~  a6 t! y" h' ^# K) s0 S
    17
    0 v# y) J& @# @$ @' I10.3 时间差
    & w9 u  @# E0 }( k. O10.3.1 Timedelta的生成, w9 i+ \8 \2 ^: R$ v2 D* r
    pandas.Timedelta(value=<object object>, unit=None, **kwargs)
    ; ^- Q6 e, D, B' X  unit:字符串格式,默认 ‘ns’。如果输入是整数,则表示输入的单位。" c3 S. v8 {9 ^
      可能的值有:, _+ R+ R* r2 E8 m5 w. X* \, I
    5 |+ ?6 j7 \2 R- Y$ w
    ‘W’, ‘D’, ‘T’, ‘S’, ‘L’, ‘U’, or ‘N’4 F8 V& S6 ?3 z4 J8 G! x8 N8 p" l* F# i
    ‘days’ or ‘day’7 w) ]1 {+ y8 o6 g! P
    ‘hours’, ‘hour’, ‘hr’, or ‘h’8 t+ i* G2 V2 _: D/ N
    ‘minutes’, ‘minute’, ‘min’, or ‘m’
    9 K! ?" w  F% e7 I: I6 y‘seconds’, ‘second’, or ‘sec’
    5 b2 @" f: G  K毫秒‘milliseconds’, ‘millisecond’, ‘millis’, or ‘milli’
    9 A# l; a0 L; a& Z0 N; D) m9 U微秒‘microseconds’, ‘microsecond’, ‘micros’, or ‘micro’
    ' E2 p, P$ O" s0 V7 c6 [纳秒 ‘nanoseconds’, ‘nanosecond’, ‘nanos’, ‘nano’, or ‘ns’.
    # G5 S/ S! m4 j' N5 E5 o时间差可以理解为两个时间戳的差,可以通过pd.Timedelta来构造:8 ~1 k, n7 s! {. E8 d  f
    pd.Timestamp('20200102 08:00:00')-pd.Timestamp('20200101 07:35:00')0 N  c; u4 O6 ?9 O
    Out[57]: Timedelta('1 days 00:25:00')0 t% v$ f1 E6 \6 H$ f

    , J5 u8 y9 X* u$ _3 s0 opd.Timedelta(days=1, minutes=25) # 需要注意加s
    , h5 l/ c7 S9 S6 y2 DOut[58]: Timedelta('1 days 00:25:00')1 z9 S- o$ W( a5 _* _/ `

    6 _2 c4 p6 M9 j3 spd.Timedelta('1 days 25 minutes') # 字符串生成
    2 u- _$ L  G. G* u9 @  tOut[59]: Timedelta('1 days 00:25:00')
    ) f: Q9 t0 f( t0 I; ~; A+ j1 E/ I! h  m$ d. N
    pd.Timedelta(1, "d"), k( O' H+ r/ ]: ]' W8 W
    Out[58]: Timedelta('1 days 00:00:00'). I: |% f2 ^% v( H. q" s$ M2 f5 Y
    1
    4 j; G4 p& T$ p( O2
    , b4 O8 `2 U% J! n; x3
    * ]# L8 f$ p. c+ \6 Q49 i  U* C/ j  y- R' ^4 b5 I
    5! e2 y4 Y. y( J$ U) j$ H; z7 J" z
    6
    . x# g1 a$ O" r2 X7
    + I+ H0 k1 x9 q! m. x9 ~( _. j8
    ; f, [7 N0 e. s9% f  w+ `9 `/ b3 o
    107 L1 c4 D( U" H9 q7 R4 ~
    11# i) o- l4 A6 p( ]& s
    生成时间差序列的主要方式是 pd.to_timedelta ,其类型为 timedelta64[ns] :; P$ I/ {+ s/ e9 K' D" @7 L- F, @
    s = pd.to_timedelta(df.Time_Record)
    ! Z" b$ P0 m/ F" S. E6 p( A2 Y6 V2 h( G6 U0 ~! e0 q
    s.head()2 Z, R& R- y/ s) J
    Out[61]: ; }* Q" G% |, D: T& n
    0   0 days 00:04:34
      j3 w$ a9 s$ t% c) ?1   0 days 00:04:20
    ; p; O% w& X: q2 r7 _4 |2   0 days 00:05:22: w  @) D) y2 l% w, g# u9 }
    3   0 days 00:04:08
    ) R$ P8 r7 G, E0 m3 N0 o4   0 days 00:05:225 _! E7 F! S- q9 w" L
    Name: Time_Record, dtype: timedelta64[ns]/ A- s3 }: H" i
    1; a! l; H( b0 P$ t! W
    2
      L( T6 t( a0 o; l, u9 s+ C% ^) g32 A6 m) Q+ g3 ^$ \" Z
    4
      O% V7 T! T$ V5
    8 K1 D8 ?) n* N0 A! ]0 z& J1 D60 |, E, G* }7 u: t; Z- @! F$ s
    7) }/ x7 B3 j4 I% ~; y2 @
    8
    # q. {- h& V& t99 ^9 S  L3 z: L$ j6 D2 x
    10
    . e/ ^8 a1 F1 h8 h$ J与date_range一样,时间差序列也可以用timedelta_range来生成,它们两者具有一致的参数:
    6 z+ s( J' T. I8 v+ npd.timedelta_range('0s', '1000s', freq='6min')
    5 M% e) Z5 y+ f% h" M- KOut[62]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:06:00', '0 days 00:12:00'], dtype='timedelta64[ns]', freq='6T')
    0 \% L- r3 R' ~, H# l0 w2 s. e/ J# U: f4 Q  X2 o% o
    pd.timedelta_range('0s', '1000s', periods=3)7 y( b, o' p# V9 A, |4 T  ]% t
    Out[63]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:08:20', '0 days 00:16:40'], dtype='timedelta64[ns]', freq=None): M4 w0 c% ^( Q5 f- M: w
    1( P" O& Y/ }  f: W' n
    27 A0 D& [" m2 m4 Y
    3% }/ P& `5 s+ Y3 v, {0 ^: L6 _
    4
    ' c2 @3 o( R! v$ z) U( E% b5: [7 U8 `6 m5 C4 N1 ]
    对于Timedelta序列,同样也定义了dt对象,上面主要定义了的属性包括days, seconds, mircroseconds(毫秒), nanoseconds(纳秒),它们分别返回了对应的时间差特征。需要注意的是,这里的seconds不是指单纯的秒,而是对天数取余后剩余的秒数:
    ! ^$ X# t( K8 `: G3 o/ j* fs.dt.seconds.head()  Q- k& V: u7 u9 w0 {- z
    Out[64]: + t! |4 k  g1 ]" K6 D3 [0 h
    0    274
      u) v0 ]! w  _6 S. f+ i) H& S7 U1    260
    1 H" {& J, s1 d: s; A2    322$ z$ ]+ m% Y- c3 i7 x
    3    248
    7 ~$ N( E- k0 E6 @5 D7 G) A" V4    3224 d2 e. b; `! J9 ~, O8 v( s8 ~
    Name: Time_Record, dtype: int64
    + g. g  N( R3 ]- G& k( F. C' e1
    + f6 @7 O7 }( U# ^& m2
    ! F  M* ]+ x; |" \" O3
    9 \8 J1 \4 ]5 A& c6 h4 J* O/ {8 E; t4! w7 {( B- d  x: @3 A3 \- O
    5
    " c; k; z& J4 r& n69 Q* d9 m3 {: q8 o+ b3 r
    7
      c/ ^, o4 ]) \  W& l3 o8
    & R# ^4 t& Z* L0 f1 Q* _7 F如果不想对天数取余而直接对应秒数,可以使用total_seconds: A7 d( [6 _' x. l
    9 {  N& T* g$ v. R" G
    s.dt.total_seconds().head()) k3 q* c/ F5 e6 ]! U+ _
    Out[65]: " y$ c/ N1 h9 @9 n1 G+ R
    0    274.04 P$ _$ h. u! w/ t% l& f
    1    260.0
    $ u- m' m" s& ?9 p2    322.0
    * F: [5 [3 Z3 j8 p0 S3    248.05 N/ w# z7 A  s- P9 w9 ^
    4    322.0
    # W3 k+ `3 t0 `3 j$ Q1 |. y. YName: Time_Record, dtype: float64
    1 e7 b/ e5 c& T) G1' [: d: k) p7 a% S% R3 m. i
    2
    ( F; W( |1 i) \6 k8 F2 x7 V3
    ; N- M! v; s: M# k" I+ K4
    . {) }# B  [' S( p5' Z* v% _+ r( C& P9 T0 @! w
    6/ e. D6 s$ d9 p, V! \
    7/ y; }6 h8 M* F0 n7 K( o5 F0 ^
    89 s0 `4 C# ]( F
    与时间戳序列类似,取整函数也是可以在dt对象上使用的:
    ; Y& e/ i' q: R* Q( m+ G! c: f. O$ h7 }2 J- p% [9 \) G# Z( p
    pd.to_timedelta(df.Time_Record).dt.round('min').head()
    % ~: R2 t1 J8 T/ x' ^1 A9 W6 ZOut[66]: ' e8 a: ?/ I. y$ q! ?  _
    0   0 days 00:05:00/ B7 x0 O. k" p
    1   0 days 00:04:00) d- r/ z1 G: V2 W* I  P
    2   0 days 00:05:00$ l* e$ `6 g4 E2 _7 \
    3   0 days 00:04:00
    ; U" N  C" A* s( A0 C4   0 days 00:05:00) |( x, P- z! Z
    Name: Time_Record, dtype: timedelta64[ns]! ?  t% g( l- y5 {
    10 J* X4 K. |; j* Z
    2
    0 I  o: G& V3 m2 g2 G3
    ' N7 ^1 S5 n3 ^$ T" F/ r! e4% R9 Z% B* r" k6 O8 Y/ a. E
    5* B$ Y2 t6 G/ g2 w. m9 K& j! e+ W( @
    6
    . e% X' y% y. J+ ~* e( Y0 P7% g6 C2 f0 f& u) |( X
    8
    + Z+ O8 z4 |6 i. M, [+ X10.2.2 Timedelta的运算1 k# L2 v3 B3 M: y1 q5 y3 v3 b8 I
    单个时间差的常用运算,有三类:与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算:
    - A: J8 \  O" |  Z; C7 g3 V+ ytd1 = pd.Timedelta(days=1)' ?; v2 W. {6 A. B: d5 y  b+ n
    td2 = pd.Timedelta(days=3)2 D" P$ q) u  u" w8 Q5 d7 N; o
    ts = pd.Timestamp('20200101')' `% _9 D+ r4 ^1 p: j" |

    - M: `# D4 l9 Z# ~$ m  t* `% btd1 * 2
    & Y& E. {, ^1 B! j' Q$ q1 K/ f, kOut[70]: Timedelta('2 days 00:00:00')9 F3 t  c! X2 j0 [

    2 M& n% ]! g  J1 Utd2 - td14 P- D6 ]+ V, I! w8 B) E' l
    Out[71]: Timedelta('2 days 00:00:00')
    7 P# E; N7 L4 b" _7 T: {! M7 {: `* b7 o: Y  F$ c' Y0 c* U/ f
    ts + td1
    8 a& V. y1 z: h5 j# M& \9 F. jOut[72]: Timestamp('2020-01-02 00:00:00')1 ~, a! }# ]( Y. {5 z0 X

    - |& l( }! B7 Y* _ts - td12 l8 `& }; j. Z
    Out[73]: Timestamp('2019-12-31 00:00:00')) I" u' B5 a# O/ q6 j8 k1 Y
    1
    9 J# B; K6 r; ?2 {22 R/ G3 Q; R9 f, _+ }6 K
    3
    ( `$ ?( S/ g0 @- @2 x46 a' g' l' ?2 e
    5
    : _, g) l8 @4 P  g  r6% K; o2 y! q  ~, L
    73 z  u# Z6 n& h4 }3 A
    8
    $ a: _* z7 t. A- Z/ Z: I9
    ( g9 ?5 @2 k# H, d) V10
    3 f, |- }  Z; k11
    . M# k! e! p4 [% ]12
    ; N# H- k: N& a; ^' R5 m" y13
    + I5 Q7 r2 B! Q. Z% I/ k* a14
    : t3 R. R3 R8 n& H15
    # N" ^' G# e( ]5 e时间差的序列的运算,和上面方法相同:
    . @* z5 q, k. C- L& j1 Q0 f& Dtd1 = pd.timedelta_range(start='1 days', periods=5)
    % q4 s' d4 O# Ptd2 = pd.timedelta_range(start='12 hours',
    0 B: l4 b7 a+ k% {$ V                         freq='2H',
    & B, y3 j" I) h+ d. ^9 I                         periods=5)6 n+ E. A6 G: Q+ e5 p5 q: o; C
    ts = pd.date_range('20200101', '20200105')5 }+ U) k$ x, ^) L) C
    td1,td2,ts
    1 ?* p2 b2 `6 ~- T5 }; s$ l( _! ~$ u) x' ?; K
    TimedeltaIndex(['1 days', '2 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq='D')
    ( y. u4 J. O% ]: I5 m+ @  HTimedeltaIndex(['0 days 12:00:00', '0 days 14:00:00', '0 days 16:00:00',
    & r& [; O, n; C, L" ~( A6 X                '0 days 18:00:00', '0 days 20:00:00'], dtype='timedelta64[ns]', freq='2H')# z4 ^4 l4 S" X# p( {) e8 d
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',3 ^! [. b) a3 }( w2 z2 Z
                   '2020-01-05'],0 n; s0 H: d" I# f( }% _
                  dtype='datetime64[ns]', freq='D')
    & B3 F8 F% s2 N, x/ m% c1
    % K5 s- n5 [2 O, G: }! N9 ~$ b2- ^7 v7 U3 U& d8 I
    33 q7 n+ n+ l$ X1 r; I0 k3 T5 X8 o8 |
    4. s! F3 Q' M5 ?  o1 `! L6 U
    5
    . `7 G* q) `# O$ k3 O* L6
    2 n% L. ~! B& K1 G, @7
    ! a2 I+ P3 P2 m1 X7 }* l85 S1 w8 N( {& _- h, T% l5 ^
    9
    # V4 a& G5 p6 P8 E10$ \5 g/ R3 o4 N+ A) `( e  p8 ]8 ~
    11
    # u+ ^3 [0 ~  Q, O8 o& B( N& w* I12" S- s. _3 i, c2 F
    13
    5 ?/ Y2 e4 X6 E5 s1 o9 g7 itd1 * 5
    6 c5 K2 o8 Z. {) j4 J% n) }, tOut[77]: TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D')
    0 L. N& f& f6 l; c  l, n: H! X* j  ]2 s) g4 i' n
    td1 * pd.Series(list(range(5))) # 逐个相乘
    . T" k' [, x3 _. Z! g! O* h; vOut[78]:
    / m- x* ?$ T" ^/ h0    0 days
    ; G9 k9 D  O0 ?5 r4 c' o+ D1    2 days
    ' p& o7 A8 h0 X5 g2    6 days$ x4 {! V4 V) o- m) N8 R
    3   12 days9 R2 N9 w! b! O* N9 \* b
    4   20 days
    ) c; k6 ]# q& y7 d: udtype: timedelta64[ns]. `! @  r6 Y2 d. \  y8 G5 W, }

    $ [  i5 p2 d, S" _td1 - td2
    * `4 R$ Q' k. a. Q" fOut[79]: & y4 l6 |/ w# ]9 S
    TimedeltaIndex(['0 days 12:00:00', '1 days 10:00:00', '2 days 08:00:00',- a0 J( p1 K1 i7 V
                    '3 days 06:00:00', '4 days 04:00:00'],
    # \) {5 @% i9 f" C* {; t2 A               dtype='timedelta64[ns]', freq=None): a, f5 i: h' B5 _# q
    ; c! y# V& O5 S& A
    td1 + pd.Timestamp('20200101')3 {+ M8 @6 S& S9 W8 L
    Out[80]:
    + t' n* f  x1 G8 {6 ]5 X) Y9 MDatetimeIndex(['2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05',& i% `- U$ ~8 i" |
                   '2020-01-06'],dtype='datetime64[ns]', freq='D')
    6 z+ d5 K* t2 }, v
    + |! h# m  M) Y" v4 ~1 Atd1 + ts # 逐个相加
    # W8 A, i# c0 ^" m0 `" `Out[81]: " P. f0 `# a- L& L# ?
    DatetimeIndex(['2020-01-02', '2020-01-04', '2020-01-06', '2020-01-08',
    4 y( N& T) `$ F/ G: F               '2020-01-10'],
    & P$ k: J% v  Z, @: \) u              dtype='datetime64[ns]', freq=None)( O2 G# I. C& _2 t2 {3 d

    0 Q) c* Q2 q5 X  Q1
    + M* t3 c# ]9 h; @6 k2
    * t$ V5 g/ ~( a# @" R& h35 e& T$ \; }! N! @% |
    4& I% R( B/ N0 ~0 j
    5
    ' m% E- B1 \3 q& H4 G3 V67 D$ R$ T+ n- u+ Q* t- K3 l! m. W5 v/ z
    7: u5 n' M+ O' J+ ^- e
    8
    9 {* n( b: L% M5 d9
    , T2 X' s& F8 N( k5 t10
    ; l$ e8 X. r; g8 R' w5 b  y, @9 ~11- M8 w. u: C2 B, h& N5 g0 Q, C
    12' O4 o- b! x& m  f
    13: ]2 d  v- u( U7 i, ~
    14
      d; ~# }1 Z9 E3 k15# A) S: C9 I& H7 ^6 h, M( u6 I- _' w
    16
    ( o$ l0 W$ Q3 K9 m! Y17
    ; Y+ t4 Z( {' }7 N18) T, Y  E0 {0 F3 }. `
    19
    0 A. l& j' a( ]20
    5 s# l' h2 X, N# ~1 r) h; k/ N% \21
    ( y. Q" Y4 o6 P4 n) f22, B) a& A, i4 f1 q% D! H
    23
    - s( f8 _" r6 W0 i- ?24
    & j# `" x5 d/ L5 n" ^/ z& n0 h25
    1 `" J0 {5 W; S6 j& i8 i& J267 x7 P4 O  U  o6 O! x; H0 M
    27
      ^- K5 M; L* }/ ^28
    & [- X0 D# U! l/ D) G7 }8 z1 O% `10.4 日期偏置
    / i! g& b/ v, f10.4.1 Offset对象
    8 g9 Q7 G4 z! T+ M+ B3 h  日期偏置是一种和日历相关的特殊时间差,例如回到第一节中的两个问题:如何求2020年9月第一个周一的日期,以及如何求2020年9月7日后的第30个工作日是哪一天。9 Q2 C- {: f+ e& G  w; h, L8 S
    / |( S7 \. l8 b0 a6 @6 T
    DateOffset 类有10个属性,假设s=pd.offsets.WeekOfMonth(week=0,weekday=0),则:2 [" P+ S" f- y( M  Y5 c

    . P# i& V6 T, gs.base:<WeekOfMonth: week=0, weekday=0>,返回 n=1 且所有其他属性一样的副本2 S5 b; a+ V' _: H7 u) @5 N% e" n
    s.kwds:{‘week’: 0, ‘weekday’: 0}
    + n8 N" i$ E& Y7 Z, `6 Xs.wek/s.weekday:顾名思义: ?; d* ?" A" Q3 {1 }
    有14个方法,包括:
    0 G& N: L, w) @! M  a6 m  [  q( L" s$ P1 G, @
    DateOffset.is_month_start、DateOffset.is_month_end、DateOffset.is_quarter_start、DateOffset.is_quarter_end、DateOffset.is_year_start、DateOffset.is_year_end等等。1 e5 u- p5 `* {
    pandas.tseries.offsets.WeekOfMonth(week,weekday):描述每月的日期,例如“每月第二周的星期二”。
    / o4 L2 D4 G: j" e3 w' S* ^
    3 P- h$ }# b# E6 P1 G有两个参数:& B0 N/ |7 Q: r& a
    week:整型,表示一个月的第几周。例如 0 是一个月的第 1 周,1 是第 2 周,以此类推。; W0 `3 [5 x6 x) L" g+ K% v
    weekday:整型,取值为[0,1,…6],表示周一到周日,默认取值为0(星期一)
    ! O1 o* F( f  ?+ J& Q) apandas.tseries.offsets.BusinessDay(n):相当于pd.offsets.BDay(n),DateOffset 子类,表示可能的 n 个工作日。
    4 t: J' m3 w$ \2 F, [/ o
    * {6 G8 R# U+ K0 i9 H& J$ @; upd.Timestamp('20200831') + pd.offsets.WeekOfMonth(week=0,weekday=0)
    9 C# ?" m# O1 F4 j: K* XOut[82]: Timestamp('2020-09-07 00:00:00')
    . p0 t! \0 f4 A  a: S( i, L% Z7 @1 }2 V3 S5 g9 c
    pd.Timestamp('20200907') + pd.offsets.BDay(30)' Z1 V- i) a4 o- n5 @  }
    Out[83]: Timestamp('2020-10-19 00:00:00')
    0 R8 t! u; p$ u1 l  a+ b: s1
    8 D# C  H7 ?' q* r2 c+ x2& S8 W* `) ~# Y# H7 l7 K
    31 f) d( r1 X! {$ o# ?  c
    4
    1 Z( r0 ?- X" z/ `5
    . U2 d+ H$ _/ _  从上面的例子中可以看到,Offset对象在pd.offsets中被定义。当使用+时获取离其最近的下一个日期,当使用-时获取离其最近的上一个日期:+ w5 S9 q% l7 X% e5 E
    7 A2 Y8 {4 ?! E' e% e* k7 g2 u9 V
    pd.Timestamp('20200831') - pd.offsets.WeekOfMonth(week=0,weekday=0)
    * V! ^% e' o3 y$ y9 |9 j5 F- @( `Out[84]: Timestamp('2020-08-03 00:00:00')# l( i0 V# m: A& m! [+ @

    3 e: B6 Z/ p; dpd.Timestamp('20200907') - pd.offsets.BDay(30)/ i* s& E/ Q5 S  m, V5 U4 t1 s
    Out[85]: Timestamp('2020-07-27 00:00:00')
    & L% T0 h: Z/ ~# E6 L1 }% P
    2 }- i. H* m  T5 S6 E+ `* T) bpd.Timestamp('20200907') + pd.offsets.MonthEnd()4 p, H& a/ O0 I% L' Y7 |" [
    Out[86]: Timestamp('2020-09-30 00:00:00')# o# I9 k( L% k1 t
    1
    $ U+ o0 S3 L4 A! g1 \& C! H24 L2 m9 x' E4 h( s7 t8 Y1 f  L
    3$ O4 r$ f: b% ]  B  q% N: b1 _4 {- q- M
    4& j. h2 K1 V% C4 Z/ G4 g1 ?: w
    5
    3 t2 ^+ j1 @: y. z6
    * W( Z1 q* U; T) B$ V7
    ; ^" ]% I0 x0 S/ E; o8 R' l6 I8
    - e. D  x6 J  p, N4 x  c$ p  K  常用的日期偏置如下可以查阅这里的DateOffset 文档描述。在文档罗列的Offset中,需要介绍一个特殊的Offset对象CDay。CDay 或 CustomBusinessDay 类提供了一个参数化的 BusinessDay 类,可用于创建自定义的工作日日历,该日历说明当地假期和当地周末惯例。
    6 n- X' E# n$ ]' C1 b! o  其中的holidays, weekmask参数能够分别对自定义的日期和星期进行过滤,前者传入了需要过滤的日期列表,后者传入的是三个字母的星期缩写构成的星期字符串,其作用是只保留字符串中出现的星期:
    3 g" P0 H/ d# r% |: f! A) k4 y  Z. r, o1 `2 q- {2 S3 [& E( y
    my_filter = pd.offsets.CDay(n=1,weekmask='Wed Fri',holidays=['20200109'])
    ! S, `! S7 h/ F) ?4 m: m2 edr = pd.date_range('20200108', '20200111'). K* F4 ]" t  E1 J5 l8 J, |
    : l9 J" J3 Y$ R; v! O
    dr.to_series().dt.dayofweek
    8 s  h3 t; \8 _7 x6 u0 JOut[89]:
    2 y7 e/ {4 P9 V' o$ l2020-01-08    25 d, h' s: x) Z2 M
    2020-01-09    3) Q- Z* ^7 z) O, y
    2020-01-10    4+ s- i, A6 ]/ B1 F& z" f8 n; |6 Y
    2020-01-11    5
      H% P! N, \; A) H& q$ h: ?4 |Freq: D, dtype: int64, l3 O& J5 u  R: i1 v
    % }4 _1 C  V6 d' \$ X
    [i + my_filter for i in dr]1 K! r- c$ v$ E, [
    Out[90]: ; d7 \( \$ Q& M
    [Timestamp('2020-01-10 00:00:00'),) w# ?* m! F. x) U6 E, a' a
    Timestamp('2020-01-10 00:00:00'),& W) U8 b# t/ [" ^9 u9 K
    Timestamp('2020-01-15 00:00:00'),& \# h1 T; m- Q' Z8 [3 P1 H
    Timestamp('2020-01-15 00:00:00')]( v3 }3 H' a) P, Q. F* U
    3 @) m' m4 O- \+ Z/ i
    1
    1 j* y' G$ P# T! Y2/ B2 Q. o3 F8 @% {
    3
    4 |. B* F% \4 D- K4
    8 X% }) c8 `  M6 J; y( x5
    4 e1 J' z, m8 V1 E" @+ X; ^4 L& R# ^6
    + q4 W, i+ }! J; N( I" C7
    . o# _1 r9 x3 h" o8
    7 D4 D. M0 l6 S" c2 S6 o: n" {9$ a8 t2 O  |5 h( g! M) g8 E, i
    10
    " F+ W$ n% y/ t3 j/ k3 @11
    : o- w6 M7 `+ {9 _' `" e) z  V" S" b12+ \  I! L' x7 M# B- {9 r4 n* p
    13
    . x: C; ?7 t0 f: u8 L9 S7 U5 T% F14
    % c/ x. `4 [. v  k4 o. I2 Z- T' q15
    & n3 l4 T! ]1 l0 m- b  _0 k16  z; K% A% {9 {5 @
    17
    ( v1 `- l, y7 ?) T3 J2 e+ d  上面的例子中,n表示增加一天CDay,dr中的第一天为20200108,但由于下一天20200109被排除了,并且20200110是合法的周五,因此转为20200110,其他后面的日期处理类似。
    7 J+ g' |! ]5 K4 a% u% o( y! P2 f) w
    【CAUTION】不要使用部分Offset. E* I6 k7 d( m: y2 X
    在当前版本下由于一些 bug ,不要使用 Day 级别以下的 Offset 对象,比如 Hour, Second 等,请使用对应的 Timedelta 对象来代替。
    / A. @) p6 o7 R) q9 K8 F
    : H: w+ ]6 g7 M  |" N- B10.4.2 偏置字符串
    ) t5 N2 ~( B( G# Y/ Z5 K9 k  N  前面提到了关于date_range的freq取值可用Offset对象,同时在pandas中几乎每一个Offset对象绑定了日期偏置字符串(frequencies strings/offset aliases),可以指定Offset对应的字符串来替代使用。下面举一些常见的例子。
    ; j, P3 E9 e1 C4 Y+ @
    9 h$ v$ r3 Q, @& B5 O  Offset aliases:pd.date_range函数中的freq参数,为常见时间序列频率提供了许多字符串别名。 也称为偏移别名Offset aliases。偏移别名列表点此参看(大概27个)。
    5 G& F5 d+ Y7 f3 H# P
    + N( U# y. A/ l0 M0 B$ a1 G. r/ e8 X& Hpd.date_range('20200101','20200331', freq='MS') # 月初2 M& _  j+ t6 U+ O2 ^9 f
    Out[91]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')
    % k0 A9 R# I2 l8 ]$ V+ m$ x2 M6 r9 t* w: {7 }) C* ?0 p
    pd.date_range('20200101','20200331', freq='M') # 月末
    + h& V3 r+ P/ o6 W' t' x# ~) r& v( _Out[92]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')
    ' A; B5 A. G8 U1 K) |$ c8 X! O( b! Z4 a, |; f9 r) W! d3 x$ i0 Z* S
    pd.date_range('20200101','20200110', freq='B') # 工作日- a. ]0 I& o3 B& g) p8 C+ u
    Out[93]:
    / r5 P7 ?2 B3 h, @) V1 WDatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
    # o2 |7 _; P' d8 o! ~# Z               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],
    ; V% O3 @( J# s3 K6 |              dtype='datetime64[ns]', freq='B')4 B5 J: {6 W) m5 S! F* L/ e' f/ v

    4 Z7 c9 S" w1 E5 i* v- ~! v. ~7 kpd.date_range('20200101','20200201', freq='W-MON') # 周一: _" `" b% G0 c: X
    Out[94]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='W-MON')9 G' D3 h2 V, a2 f  V

    . c1 o5 M( @8 N0 L; I( O6 dpd.date_range('20200101','20200201',( j5 e7 t# k! f4 E
                  freq='WOM-1MON') # 每月第一个周一7 s/ ~3 W% z4 o0 D+ r: j

    8 b. S: h" H" W6 t* I  |: c7 EOut[95]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')
    * z8 [5 ]3 ?1 |+ u; w# A7 c" w
    * {$ s% v  B! C5 L0 J7 C1+ z) W7 k0 u5 `3 B0 |
    23 E5 K# i- X7 F8 a) [. o: p) f! n
    3
    ' s0 j5 b. J- \. O$ \4
    3 ^- W4 F( X; T0 E7 W  S( d% j5) R- Z5 ?% [$ c  i
    6
    5 D  K) @) }8 T$ q9 `7
    % P9 r; M7 y  \" u88 E4 s2 [5 x, l
    9
    $ H/ r9 C3 E7 g$ s( Z10
    , d7 ~5 q% t/ `4 B4 [0 h- Y11: Q* r; M& f1 F/ ^, ?9 J
    12) z; L3 T! U( ^8 y
    13
    $ T* X+ J+ Y4 f5 @3 W$ f( ]+ W14
      b. N3 o5 J* f9 |; v$ c  ^8 v8 Z154 Y* [$ {1 q3 C; h6 b
    16
    2 k; K7 P2 J( U17' O2 ^9 j) b/ _3 B; i6 s+ G4 Z
    18
    # O! @" D, r! Q9 G- y19: n8 J0 ~7 u9 X( \' Q( O1 n3 a
    上面的这些字符串,等价于使用如下的 Offset 对象:
    ; ~% ]+ O0 v$ x6 @# H' [! v
    2 B7 c- @3 V8 }pd.date_range('20200101','20200331',
    8 S' T) k6 {+ z! X/ s+ p, B$ |6 r3 ]5 D. m              freq=pd.offsets.MonthBegin())
    - \3 P7 Y0 z2 g
    " H' q! M* a5 Z9 i9 FOut[96]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')  H. F& Q2 k' m7 e- e

    & Q2 u8 ]8 \  G2 tpd.date_range('20200101','20200331',+ r8 X) Y% x/ P* v
                  freq=pd.offsets.MonthEnd())5 n1 r0 M4 q; i
    4 g+ v. D5 t( z
    Out[97]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')
    1 l* D0 C4 `" [* ]+ j) |7 P- u  [' W) c3 b
    pd.date_range('20200101','20200110', freq=pd.offsets.BDay())4 C- L. x$ O# C2 E; ?; M6 I$ s& A
    Out[98]:
    & n0 u. [$ E6 N% q9 \/ pDatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',5 G; U. @2 H! r( a/ {9 w0 L
                   '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],# M0 W: [* x, N+ n4 y; P
                  dtype='datetime64[ns]', freq='B')4 E2 y; v& l/ L  }- W3 [9 \/ E9 {
    ( {% x8 w% {1 B& }% Z
    pd.date_range('20200101','20200201',7 d3 r. B4 R- z2 O6 Z
                  freq=pd.offsets.CDay(weekmask='Mon'))+ e* T1 z1 G+ n4 c& G
    % c1 W8 Z0 t/ V
    Out[99]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='C')
    ( w! j+ V; a4 [- Z% a% y5 m* }. J, T- e3 V, f" `# F
    pd.date_range('20200101','20200201',  a& E9 T" s3 Y+ X
                  freq=pd.offsets.WeekOfMonth(week=0,weekday=0))% V, O3 j: f* C6 {3 f, C  q% f

    7 p8 `- E4 W& h. `Out[100]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')
    ; j" @( R. J; n
    4 k' _1 Y) x- s! V* F$ x1
    * I* l+ W8 J% k! n2
    0 c- M. F1 V" v: m9 [3
    1 P; {5 G9 P2 j4 o0 G2 F/ d4
    / n, i! w& W: _( q5
    / N: Y+ ?' Q' r; y" [6
    / p6 v2 O# u" @7
    7 k; _, w* B" F8
    ) j+ n) A" \0 b" h0 ^9# e& P$ A/ n3 `+ s4 b# w: x, x
    100 l. S7 G! \$ \
    11$ q- ]. A- ^. }/ N; _. L. ^0 y: y
    12
    % p# w- R- C7 f. H' `- I# J13
    ) \% ~4 N# t! z0 W: Q14
    0 n4 Y& h/ @' `+ h" F$ ~155 c0 t: y0 Q, [
    16; U( D% f, E+ @7 c% N
    17
    " w; P4 u/ h1 b; U) v5 ~6 W8 N18' f$ X2 f  T5 R8 o6 b) x4 _
    19; D3 z. ]7 P, c# C" B/ }
    20
    4 u& q* g' `0 O  m+ g21! f9 z) t7 r0 I' [3 A' G  r$ `
    22  h- T' P3 u& `) D$ P8 X
    23' p. q( }/ ~9 K0 v0 r
    24
    4 U: D+ Z, R2 k7 B/ d259 X4 K9 v$ [9 b5 p" w# U
    【CAUTION】关于时区问题的说明
    + _. J! f0 h5 i* L2 Q6 l1 L  各类时间对象的开发,除了使用python内置的datetime模块,pandas还利用了dateutil模块,很大一部分是为了处理时区问题。总所周知,我国是没有夏令时调整时间一说的,但有些国家会有这种做法,导致了相对而言一天里可能会有23/24/25个小时,也就是relativedelta,这使得Offset对象和Timedelta对象有了对同一问题处理产生不同结果的现象,其中的规则也较为复杂,官方文档的写法存在部分描述错误,并且难以对描述做出统一修正,因为牵涉到了Offset相关的很多组件。因此,本教程完全不考虑时区处理,如果对时区处理的时间偏置有兴趣了解讨论,可以联系我或者参见这里的讨论。; u0 @0 p- B1 G$ f4 U6 J
    2 G+ K5 ?6 e9 E
    10.5、时序中的滑窗与分组
    , h- L; T( x$ |10.5.1 滑动窗口& S! y% n7 h8 X; U5 d+ b
      所谓时序的滑窗函数,即把滑动窗口windows用freq关键词代替,下面给出一个具体的应用案例:在股票市场中有一个指标为BOLL指标,它由中轨线、上轨线、下轨线这三根线构成,具体的计算方法分别是N日均值线、N日均值加两倍N日标准差线、N日均值减两倍N日标准差线。利用rolling对象计算N=30的BOLL指标可以如下写出:, E! K! n  t) `" b; p
    5 y+ x6 u6 ?! _
    import matplotlib.pyplot as plt# |6 Z4 z8 S5 `/ N7 _2 P
    idx = pd.date_range('20200101', '20201231', freq='B')
      p8 F6 S- i+ Z- @np.random.seed(2020)$ ]* E8 _; G: h  n6 W

    ( \- V" a: G; a* y5 `data = np.random.randint(-1,2,len(idx)).cumsum() # 随机游动构造模拟序列,cumsum表示累加& }; D  `0 Q  p+ n' a
    s = pd.Series(data,index=idx)# S4 G6 |5 L$ _. k( ?
    s.head()
    : ?: V& L  ]) {0 x+ D( u7 COut[106]:
    9 R4 B, y9 k' l1 c: E2020-01-01   -1
    + \: q1 F4 L  Z- v* M: `2020-01-02   -2" s( t& @1 o7 o, E/ w$ m
    2020-01-03   -1
    ; O' X; ^" K$ ~9 F1 `2020-01-06   -1+ w/ o5 @7 @3 e
    2020-01-07   -2
    3 j* L& U; y9 C6 U* o# d* h4 I% AFreq: B, dtype: int32) j8 S# e" V( T
    r = s.rolling('30D')# rolling可以指定freq或者offset对象  M# N2 p. E8 K8 P; t5 t0 g9 j

    3 N( p# E( a; Cplt.plot(s) # 蓝色线
    & s! n+ |* d( Y" f# VOut[108]: [<matplotlib.lines.Line2D at 0x2116d887eb0>]0 L$ `" e& E- i$ D4 I0 _% }$ p3 u
    plt.title('BOLL LINES')
    3 `2 `# Y- [3 s( z; h2 l' r% dOut[109]: Text(0.5, 1.0, 'BOLL LINES')
    " y6 O2 V" x& H# p) p8 i
    8 M# D7 S# ]) pplt.plot(r.mean()) #橙色线
    5 Z( o5 q4 I8 I3 LOut[110]: [<matplotlib.lines.Line2D at 0x2116d8eeb80>]
    ( J/ t  [& g8 U& v( y
    / y% I( M' G5 @9 q/ s4 }plt.plot(r.mean()+r.std()*2) # 绿色线6 }, m  {& T" a! }  x5 u, _1 t
    Out[111]: [<matplotlib.lines.Line2D at 0x2116d87efa0>]
    - S: |+ n* M+ S& b1 m* I0 e& `, r, N7 [( M5 I
    plt.plot(r.mean()-r.std()*2) # 红色线9 U( k( R1 v" ~# j1 J- {
    Out[112]: [<matplotlib.lines.Line2D at 0x2116d90d2e0>]4 \; c8 z. u/ N
    / U- q# B5 }! W* x$ D6 D2 m. d9 B9 D0 u  `
    1
    6 f& J1 K$ b7 Z$ J$ F  P& a2+ O7 ]& O- o) @% j
    3
    3 u- ~+ e( k7 i0 \. U4
    * Z  h, U- V0 A6 q* v: g* l5
    % v4 }8 N* @3 f% ^2 o6 I6
    ( j0 [% e  g* o( `. ?# j70 \, w' l- ]# k- \  b% }
    83 ?! b8 [* d8 s. F8 o) u
    9
    6 I- t2 l5 k( F107 d$ F- s3 ~5 e- Y
    11
    ; j2 z  V" J6 d, G. d/ s125 f4 s& Y  n$ Q
    13, e% k9 e& p5 s- ?+ k  |* i& ^, G
    14
    & n9 S3 U1 y7 Q7 P15( m7 i1 r2 p2 {3 P, J
    16* e# r% V% X+ q3 t
    17( S9 t8 C. M9 y) \# M- P; [8 T
    18
    ( o5 K% A/ b1 ~$ l/ Z8 }1 F; y/ k. C19
    ' d4 g$ z5 ~/ j' w9 j20
    + ~( i0 F. @6 W& r! k3 M) U21
    + y( o7 h1 V0 U8 P224 Z0 ~: }" [; }; j( U8 r" s
    23
    , ^8 L. Q9 C$ K; L$ d24: W$ m% k7 r: \. T, s2 w0 g
    25
    - @4 @& C, B+ l+ q7 |0 p6 l26
    7 i9 M* c% g3 u: W2 z/ C" i3 v27
    6 Q; L% v! o- f0 m6 Z28' f; j. p% ~8 Y
    29
    7 P' t% R/ n( h+ s! P
    0 M/ P6 W8 T5 V! c( k   这里需要注意的是,pandas没有实现非固定采样频率的时间序列滑窗,及此时无法通过传入freq字段来得到滑窗结果。例如统计近7个工作日的交易总额。此时可以通过传入多个函数的组合来实现此功能。
    4 Z& U" L7 Q. H   首先选出所有工作日,接着用普通滑窗进行7日滑窗加和,最后用reindex()恢复索引,对于双休日使用前一个工作日的结果进行填充。7 A2 O# `/ b) R# i$ t4 B! @
    6 ^7 {* z8 `! ?# B$ i2 M
    select_bday=s[~s.index.to_series().dt.dayofweek.isin([5,6])]
    7 A9 ]# g: D) A- dbday_sum=select_bday.rolling(7,min_periods=1).sum()1 Z% _' s2 d; C3 |
    result=bday_sum.reindex().ffill()6 c* j" b" ^8 l" D
    result; Q4 L! }8 Q& N' l

    6 {9 p# f& a9 S! u1 A  @2020-01-01     -1.0
    ( j3 ]5 r% b0 `8 i+ M. q( C" K2020-01-02     -3.0
    ' ~7 D& X" {" @' J5 c8 f2020-01-03     -4.0" B5 k- ?. h0 S: C
    2020-01-06     -5.0
    9 X, n% }; z) @8 V8 |% i! L. A) Z2020-01-07     -7.0
    4 E) j/ [+ c. L              ...  
    / G" k* h* h, \$ {  T' N+ Q2020-12-25    136.0
    . O5 @* u; w( F+ Y" ?, T& P& Y+ M2020-12-28    133.0
    3 b  X1 B8 l: u+ }: Y2020-12-29    131.07 M/ `3 A: @& I& ]* M
    2020-12-30    130.0
    * w- a% ^  [$ b! y2020-12-31    128.0* V. J6 k8 Q9 Z+ r2 Q# J4 w
    Freq: B, Length: 262, dtype: float64
    # }5 U5 _/ k0 H% u
    3 P0 B- `# G: J) K# J' ]; p1
    0 v2 D, s- ]. }3 D! E2+ E. \1 _9 w! |5 s% @
    3
    8 n* Y; D: ?2 I# K3 X0 b4, p4 a) ]" X: L) K
    5- x. I- C3 p: [1 g  [4 g4 Q! |# `
    6
    . k6 ~8 `2 y9 R4 W, w/ F7
    0 X4 W! F5 I5 s' l# s( T8
    $ Z' u3 {# j0 K# B/ U9 p9
    " n3 A* \- S: X: C5 e" \10
    4 x4 t& E0 z5 h2 r" N. I+ U  ^11/ K! J% F; d# R7 N0 f  Z
    12
    * [8 x0 v$ q+ y8 h: O7 W4 q( V# F& q: f4 E13/ M* T6 r/ Q% v0 `0 K
    14# q- t  a2 }* D( m- H' A
    15
    & G! j  W* R( c" H7 J4 F16) |' ?: j% L0 s/ y, L, ]1 y1 j) e
    178 p; M1 ~2 S$ g3 P8 q' ]
      shift, diff, pct_change 是一组类滑窗函数,它们的公共参数为 periods=n ,默认为1,分别表示取向前第 n 个元素的值、与向前第 n 个元素做差(与 Numpy 中不同,后者表示 n 阶差分)、与向前第 n 个元素相比计算增长率。这里的 n 可以为负,表示反方向的类似操作。
      k: w$ U  k8 _. w8 n2 x. l, v- i. R- U* Q1 Y9 a( U; K
      对于shift函数而言,作用在datetime64为索引(不是value)的序列上时,可以指定freq单位进行滑动:2 h, p6 C2 I& @. G6 h# R( p, S# W

    + y+ O& Q" a( s8 Z, ~; e% l" ms.shift(freq='50D').head()
    , U" g, P3 c8 A' e# eOut[113]:
    * P5 j4 c3 R, z; ^/ y" `. e2020-02-20   -1: z. c2 i" S; N" K  c
    2020-02-21   -2
    6 ]/ V+ g% z* ?0 b& [8 W5 t2020-02-22   -1
    ) q+ E5 a/ J; |# H3 H2020-02-25   -1+ g- t+ P7 l: ~
    2020-02-26   -2
    6 x3 \" k5 W! n6 \# U$ k/ idtype: int326 X7 t) g* k( M5 \+ K7 I# P
    1
    . l/ Z/ \" z) J( n" f& Y6 _2% ~  }- s' B* N! `2 [  N" A9 A
    34 C7 B; {7 u. U1 |+ f4 N3 b6 x' M
    4
    # s5 e% ]' a+ B5 I* X! {5$ G8 n( I: k/ t# @" a
    6
    . K" E% X8 f& z% S" X7
    % a0 S% E7 |* _/ p1 T81 M' @7 a: S( u% [' E. @6 T
      另外,datetime64[ns]的序列进行diff(前后做差)后就能够得到timedelta64[ns]的序列,这能够使用户方便地观察有序时间序列的间隔:- `' P. S9 J7 M. A4 w

    3 G+ h% N" ?: Z) j; W9 h' emy_series = pd.Series(s.index)
    ' g$ K! n- |; \4 Q* Xmy_series.head()* [: |: ?4 g6 E
    Out[115]: 3 S: g2 e( V& q
    0   2020-01-019 H6 r; r& i6 s5 o  w$ w* p* \& [
    1   2020-01-02( m" G! T  X- z8 Q( G' x
    2   2020-01-03
    + P9 F! }& z3 R7 q) G3   2020-01-06) I6 H/ f3 v  E9 a% D$ l, |. \
    4   2020-01-07# C1 D1 A+ Y9 ~8 l5 P1 h
    dtype: datetime64[ns]
    5 _% R% m8 N; h* ^! j' l/ l& t
    ' q3 S3 `% J5 t+ Q: r; C" wmy_series.diff(1).head()- x: ?5 D0 L" M0 ]7 @
    Out[116]: + ]$ l4 b, T1 c$ G
    0      NaT1 s% |9 I" D; \8 r; u
    1   1 days
    6 v3 f! I0 }9 y2 Y5 W- b4 L# j0 N2 @2   1 days
    # D- Z) I! B- m, s5 E- f6 W3   3 days# u0 D* f! S! c7 o" z# |4 j
    4   1 days
    . ], `' J" m* y4 L) S9 W! Qdtype: timedelta64[ns]
    & ^1 C% ~  a6 w7 [* ~
    + s: J& i; l+ q1
    0 J9 C: U  g5 W) X- f+ n2- ^% S) U9 T+ s! g! [
    3
    + |6 d6 g1 C% ^$ `4; H! r* b$ I7 W
    58 y5 _4 M# B4 t, }) I
    6
      K) J0 }* t' O# E. v7
    ( n5 s% ^6 I+ k/ P: \4 a/ b8
    9 {8 T7 e/ `4 V; j& v9 Q9) Y% N3 Z& D& p9 `( e9 w
    108 n8 [$ e% _( ?  s8 L+ I
    11$ T- t  F. K9 v: m! J+ t
    12
    ( i* d! `/ p: c13* ~- m7 R9 D" r# a: y) @. k
    14+ c7 F- E3 P$ U9 q( W8 ?3 M. f
    15
    1 M6 `# u' A9 x0 @8 k& ^2 V  R168 g. Y4 E- |! s- s& A2 ~8 N
    17
    9 m7 Y/ Z7 V0 I0 K& Z, K18
    2 C- f2 \" E4 \. c# ~$ p5 ], p# e3 G10.5.2 重采样. s+ G6 ^' k0 |  b8 I- j
      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)
    " k, l1 t3 ]9 h. b0 K( J0 C$ }常用参数有:
    + z6 k" j8 W4 O3 h" e! l# ~: M$ E( w) v
    rule:DateOffset, Timedelta or str类型。表示偏移量字符串或对象
    0 a8 k2 N9 R- d& Baxis:{0 or ‘index’, 1 or ‘columns’}, default 0。使用哪个轴进行上采样或下采样
    ! m. u; G$ U8 }& |4 Xclosed:{‘right’, ‘left’},默认None。表示bin 区间的哪一侧是闭合的。所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。- K( M5 ?* q" ^& k
    label:{‘right’, ‘left’}, 默认 None。hich bin edge label to label bucket with,所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。  [4 O2 d  l  j0 P' i: @
    convention{:‘start’, ‘end’, ‘s’, ‘e’}, default ‘start’。仅针对 PeriodIndex,控制是使用rule的开始还是结尾。
    4 I' X. o9 q% P& Von:字符串类型,可选。对于 DataFrame,使用列而不是索引进行重采样。列必须类似于日期时间。+ P) n2 ]& w; a2 @
    level:str 或 int,可选表示多重索引MultiIndex的级别,这个级别的索引必须类似于日期时间。
    # l7 _, b( Q1 ?. c! m+ q; L, Corigin参数有5种取值:
      u2 q* P( |4 x/ ?1 _‘epoch’:从 1970-01-01开始算起
    . h$ X, c, S8 F+ {& k6 `‘start’:原点是时间序列的第一个值
    * j' g8 T3 L# Y9 J‘start_day’:默认值,表示原点是时间序列第一天的午夜。
    4 {$ N5 o7 ]. x* s! M6 a$ A$ U'end':原点是时间序列的最后一个值(1.3.0版本才有)
    . d! O: c4 ?7 M! G‘end_day’:原点是序列最后一天的午夜(1.3.0版本才有)
    $ `7 K; j( W* o( {offset:Timedelta 或 str,默认为 None,表示对时间原点的偏移量,很有用。4 x7 x  q" _: [8 R7 j
      closed和计算有关,label和显示有关,closed才有开闭。
    6 Y3 U! C$ R) f* r+ w& F$ r  label指这个区间值算出来了,索引放区间的左端点还是右端点,closed是指算的时候左端点或右端点是不是包含。
    ! W( B7 |6 Z7 C  r: p8 h
      _5 Q0 `0 e. k( f重采样对象resample和第四章中分组对象groupby的用法类似,resample是针对时间序列的分组计算而设计的分组对象。例如,对上面的序列计算每10天的均值:
    ' B0 X; T2 F1 Ps.resample('10D').mean().head()
      O" Y* G) E* o5 ROut[117]:
    : U% ]% Z/ u+ p" L7 w. F- q( \* M1 s2020-01-01   -2.000000
      l& a. w- n, T2020-01-11   -3.1666671 M/ I* r2 F, b# n2 O+ d. u
    2020-01-21   -3.625000
    * G& A7 P, m1 G2020-01-31   -4.0000009 N8 x2 i9 I# S9 `# }/ T) R# M
    2020-02-10   -0.375000* R! h% @, {' `% g" t( o& R1 `; X/ n
    Freq: 10D, dtype: float646 p/ q# L4 K/ ]7 {7 Q6 i
    1
    * Q5 J! @, s. j# H( m: ~' B2
    ; d) Q2 i" i: b3
    8 _! J  S' E. z2 M48 c# t0 e: U" O: B0 j8 M) H
    5# N9 H. p/ g+ _, i* S" ~1 ^
    6* {& d: w) t9 L* q
    7
    : f3 d8 C/ t5 a8 Y/ q1 y. ?8
    ; Z& |6 t5 J9 k, x: D可以通过apply方法自定义处理函数:
    4 T. p( [  M3 E& X8 j' i" q- os.resample('10D').apply(lambda x:x.max()-x.min()).head() # 极差: k( U: a2 ]8 K2 A% X

    : x3 v) o( _' h5 EOut[118]: ; y1 j' k# z8 m; u8 g( e) a
    2020-01-01    3! ?$ B0 q: T8 G2 ~" i
    2020-01-11    4
    , X0 _! ^2 Z% o5 ~( C  D# |3 k2020-01-21    4
    # b* Q* _4 `5 r. N0 g) E5 d) A2020-01-31    26 b$ f! |9 L% d0 Y  o
    2020-02-10    4' p6 T: F- N* S* E: N* {4 @8 [
    Freq: 10D, dtype: int32% q8 I4 e0 K( U; A, t% Z7 a$ P
    1: R& q: W! o- P7 p1 `3 @
    2$ o2 y+ L5 u$ V$ }
    3( W( D7 T# ]: l# D6 N
    4
    2 s* ?# N2 q6 ~) {7 `5; p+ e' ^( F% J" ~; K
    6
    1 o% }. |1 \9 C$ J; o% a70 K9 H- v1 J" B. a/ Y! q, S5 K4 A9 ?
    8% I" l- V# D! O0 ^. t% J; _- b5 v" E
    9
    / T8 Y0 _6 Q- N- G* e3 T  在resample中要特别注意组边界值的处理情况,默认情况下起始值的计算方法是从最小值时间戳对应日期的午夜00:00:00开始增加freq,直到不超过该最小时间戳的最大时间戳,由此对应的时间戳为起始值,然后每次累加freq参数作为分割结点进行分组,区间情况为左闭右开。下面构造一个不均匀的例子:7 y4 O; C& f" Q. V+ D! u. t. O1 P
    ( U1 y9 a. l' W, I0 U8 n5 i
    idx = pd.date_range('20200101 8:26:35', '20200101 9:31:58', freq='77s')
    , `# j+ z2 m. n0 ddata = np.random.randint(-1,2,len(idx)).cumsum()
    2 j* ]6 B. e/ p& ns = pd.Series(data,index=idx)
    1 U% s: C( X; }/ w$ S1 ws.head()
    2 t: I+ ?& ?* B- y7 i/ {
    % ?5 n- H1 L9 SOut[122]: ! Q9 G/ k# |. R) \7 C
    2020-01-01 08:26:35   -1
    7 ~3 j  ]3 a: _9 X( l! M: D2020-01-01 08:27:52   -1% k  u- {! \4 D$ Z! \4 ?; ]
    2020-01-01 08:29:09   -2
    % K, E3 `9 ~" M8 s* j2020-01-01 08:30:26   -3
    8 ?6 ^6 c5 V# i4 P& g7 @2020-01-01 08:31:43   -4
    ' l8 K8 S+ l6 E: e6 I$ n* CFreq: 77S, dtype: int325 n, a/ [% @6 Q) K8 V% r
    1
    0 t+ {( Z2 ^/ \; Q# D, R8 F, y5 j2! g" z4 X6 K/ Z; z5 m
    3
    0 D% f* f) _: ]46 P% l9 \* P0 q3 a' K7 Q/ M
    5
    % X+ t9 ]+ Z4 l& v& g/ M$ P6
    9 b+ o/ L' ]) S' l) \7$ I4 J! X/ {: p# K
    8
    & Z# T1 G1 C' D8 \/ Q/ E9. p& Y6 B3 `2 n+ Z4 W1 ]1 U' i
    10: q# K: j/ e. M; h! |
    110 y& ]  J8 l& r0 u  _; }' q# w: |
    12
    % V4 K, G' Z: E, j  b  下面对应的第一个组起始值为08:24:00,其是从当天0点增加72个freq=7 min得到的,如果再增加一个freq则超出了序列的最小时间戳08:26:35:
    9 f/ Z7 \+ @5 d$ J7 \( \7 U, s( h5 k2 ?
    s.resample('7min').mean().head()' s$ J! ?- o7 ^3 m2 H
    Out[123]:
    ; r( T9 i7 P8 t" j2020-01-01 08:24:00   -1.750000  # 起始值,终点值包含最后一个值; T. f( X! |" X2 t- c  |
    2020-01-01 08:31:00   -2.600000
    " `; v3 H( U5 w2020-01-01 08:38:00   -2.166667$ K. G( ~: r, g9 t, ]7 d
    2020-01-01 08:45:00    0.200000
    : M% D" T# s+ t  y- R2020-01-01 08:52:00    2.833333
    ' e; V8 I) u) V" hFreq: 7T, dtype: float64
    & {5 U) h6 `! J: ^4 R1
    ' g5 _. v: ~! {. D. O( W! L26 l+ F  g! B9 k! e
    32 m7 x) w& w. l0 o* `
    42 s1 [$ p% x3 E6 \5 i( p
    5
    , u$ G5 @; n6 Z- E+ t" {6
    9 e8 B2 h9 ^- g' j8 @$ \74 X4 ]& e/ Z3 E& H
    8
    # T$ @7 H% H5 r! w  有时候,用户希望从序列的最小时间戳开始依次增加freq进行分组,此时可以指定origin参数为start:0 v, r! U9 G3 [0 O4 Y3 q2 _
    / g( s# W( g( X* f2 F; y
    s.resample('7min', origin='start').mean().head()% W# }) L5 I+ V. e8 h. S
    Out[124]:
    # |' P8 x. z) [% @; J. b2020-01-01 08:26:35   -2.333333) i2 G3 b1 y; h
    2020-01-01 08:33:35   -2.400000. s6 t/ d; W6 X2 c
    2020-01-01 08:40:35   -1.333333; M) g+ P0 N0 V, I1 [4 [
    2020-01-01 08:47:35    1.200000
    & Y: m1 d  q- _. `2020-01-01 08:54:35    3.166667( F/ k, ~9 r9 i$ ^" `2 \
    Freq: 7T, dtype: float64- O, C( M7 T; a( f! t) J
    1' X5 w, p0 t9 m. b' S6 Q& n
    2( F# o  ]9 r6 O8 n1 O+ B, x
    3
    # G5 M4 U- u6 @+ R4
    7 O) x0 Q9 F( _5
    ; ?& s' r8 U4 @63 f1 _; a* O* g9 [
    7
    ; f7 Z# d2 Y5 y% t' P& Q80 N, T% b, Y* \8 H5 R
      在返回值中,要注意索引一般是取组的第一个时间戳,但M, A, Q, BM, BA, BQ, W这七个是取对应区间的最后一个时间戳。如果想要得到正常索引,用’MS’就行。
    1 o0 ^% Y6 O' J9 \' P& E) O
    % k; h. @: E4 i6 f2 c" }0 B+ a2 ?s = pd.Series(np.random.randint(2,size=366),
    ! w. \* C1 e1 y9 K5 n              index=pd.date_range('2020-01-01',: |- W& _- R, z# o& W$ c( o4 |
                                      '2020-12-31'))
    / P: ]5 z( D1 R* q- f# M5 }! U% _" ~+ m3 o+ E

    5 g% o5 ~  K) `$ ns.resample('M').mean().head()
    3 W+ D, B: W/ }% h, }% gOut[126]:
    ' {1 N8 n/ J7 f5 m2020-01-31    0.451613% z; j2 Y" `( S2 u- }/ E1 I
    2020-02-29    0.448276
    6 q3 X2 y& b% K5 u, O2020-03-31    0.516129' C* L  _1 Z# y! l$ i4 J- }! P
    2020-04-30    0.566667
    ! m2 \8 ?+ W8 i* J2020-05-31    0.4516131 w3 ^6 ]' h. I3 y6 U) x, U! A
    Freq: M, dtype: float64
    " _6 u2 {2 g% |8 r' x7 v1 w. v$ p9 R2 }& k+ c6 i9 S# L- v
    s.resample('MS').mean().head() # 结果一样,但索引是跟正常一样, Q4 A* t2 B- _2 A9 y7 m& u
    Out[127]: 7 |8 S0 K: g3 v% s3 L
    2020-01-01    0.4516138 w) m0 o: D2 s1 H" o) ]
    2020-02-01    0.448276" s$ M  h) R/ ~2 w0 Z  b, W" L; D
    2020-03-01    0.516129
    * U4 e, Y7 ], J1 R2020-04-01    0.5666677 N& ^7 n% H+ W  q( D& L2 x) p
    2020-05-01    0.451613
    4 @- r/ x" L$ j/ W6 `0 n- d. jFreq: MS, dtype: float64
    ) Y1 [; V7 k5 U7 y* E" V1 J& l, s8 G' a2 S" f
    1
    5 R8 L. |( @2 t. m8 K, X* B2, R+ D- [, [& |3 K8 o) C
    31 A' e0 b& o1 v, @" c
    4
    ' S: h2 I# N( K* p! V5
    / U$ c: n3 W$ b. _# D/ P! {' o5 l3 t6; A# K+ s' W' k
    73 v4 Y9 D* f% V+ W; h; w
    8& z0 b* M- h, d! F' z+ H! g
    9
    ; R# q! N8 ^8 a10) q$ V$ j' k& A8 k- y- O2 |
    11
    ( ?, f1 k- D. X! k2 P0 g# ?127 ^+ G; g& g7 p4 C
    13
    - `- j0 X3 Y0 x- Q146 J; x/ U. ?8 C
    153 g4 P  M1 g" }. b
    16# C5 X& j+ q1 v! b1 B5 l5 A
    17+ Q' K* v. Q; K5 e" r/ \
    18
    * v9 Y& O3 g2 E4 \* m197 H' J+ `; t" d* j
    20
    % v- b6 \/ {  j21
    ! J  |3 ~- Q& x  s1 @/ v22
    ! ^; p; i% ~" g" W+ s对于 DataFrame 对象,关键字 on 可用于指定列而不是索引以进行重采样:
    7 q' }! y- f, G9 _- I1 \' _d = {'price': [10, 11, 9, 13, 14, 18, 17, 19],
    , ?% J5 R, e3 [$ l+ N2 o; `  |8 M     'volume': [50, 60, 40, 100, 50, 100, 40, 50]}/ e% l1 F8 s' v. I$ [# q
    df = pd.DataFrame(d)" H! Q, P9 s( O+ t7 g
    df['week_starting'] = pd.date_range('01/01/2018',* C' z  {* |$ u4 N' l( l
                                        periods=8,
    7 q3 ^! W. B+ j; b                                    freq='W')
    5 L9 Y" ]4 A2 fdf5 J0 i9 Z$ `6 C. G
       price  volume week_starting
    , D5 v3 g# d7 l3 i4 r0 g0     10      50    2018-01-076 f# Z3 N  O6 d) g
    1     11      60    2018-01-14+ ~. J9 l. F1 P) J, _
    2      9      40    2018-01-21
    . @+ i" H9 A* v. V8 u& C3     13     100    2018-01-28
    3 x3 R' s% S  |# `) O7 g" Q7 H4     14      50    2018-02-04# o( b9 \" _; M' e  a. T. Q
    5     18     100    2018-02-11
    " M" k; @. p0 r2 B6     17      40    2018-02-18
    9 p5 ]: s- L1 T2 f; s" j0 _3 s7     19      50    2018-02-255 y: G& d5 d4 D" N
    df.resample('M', on='week_starting').mean()6 c9 X* v! J8 l2 F& i6 d4 M4 v! T3 u
                   price  volume9 H  t0 Y4 o% S7 D, m- p2 k7 k* Q
    week_starting" i% |6 Z" K2 p4 f4 F
    2018-01-31     10.75    62.5
    $ J7 R3 V: A( T" J2 J3 y5 H0 k2018-02-28     17.00    60.0( H9 K. z* @! ]3 A6 Q* Y1 W
    5 R( @3 A* l0 R
    18 l6 ]) T/ d/ P; i
    2% v  M; E' m# W
    3
    ) k) F, _8 d, H& J4
    $ K' }/ l# O0 ]/ q9 h) V5* K: J4 K0 @4 E( J, X0 Y
    6
    2 z5 h" j! J# x" m$ L- B* F6 k5 q7
    , u( }) q# o. A* _, l/ |, K82 z. ^8 h! y, d; }9 ]+ V7 }
    9. H( Z' z! V% e% f
    10
    8 I; Y2 p: G; _11. ^& r! ]( w9 `* G- S# o
    128 N0 D7 @# W% {0 I- |
    13* Y( v1 x( G  D0 m+ V+ U
    14" c+ E, R. `* `# B$ o9 H8 [, V$ M1 g
    15* R: c, t/ [/ }& U  c4 L
    16
    8 `- Q, f4 e9 j% o' R- }17$ C8 M' N% _+ J' B- |
    18& h  d6 o2 L6 K" c" x7 k
    198 H' o$ E; e3 X4 k6 F" Y3 j9 z
    20
    9 ], M( T& `# R" A211 ~7 R- F  z: K
    对于具有 MultiIndex 的 DataFrame,关键字 level 可用于指定需要在哪个级别进行重采样。5 H# O+ F# }  q7 e5 A
    days = pd.date_range('1/1/2000', periods=4, freq='D')
    + s, {% R* K( X. T! N3 pd2 = {'price': [10, 11, 9, 13, 14, 18, 17, 19],) p& c3 q, B- Y' m
          'volume': [50, 60, 40, 100, 50, 100, 40, 50]}7 x4 C9 z3 k" m6 T  S% k# h/ G
    df2 = pd.DataFrame(7 U& \( d; m9 p) z; d
        d2,
    3 _0 ]- S* k- b" O7 v2 B    index=pd.MultiIndex.from_product(
    ( R9 {. e0 ?" H# P  p5 }        [days, ['morning', 'afternoon']]
    . Q! m) r# H3 z. l: b    )
    % K) U; Y! o. }+ h)* \8 B8 U( O9 H( L5 D8 ~; v; d
    df2$ w+ x9 M# X! @- I% m$ |! U6 Z% U& v
                          price  volume6 S  U, p& @/ q1 H
    2000-01-01 morning       10      50) v& g7 Z5 k/ k) Q& r
               afternoon     11      60$ ]/ r! E$ Q# \8 G& A
    2000-01-02 morning        9      40
    6 U2 W- y2 j, O4 o3 e% {1 U: ~5 K8 A           afternoon     13     100! `7 L0 z3 H, E, W
    2000-01-03 morning       14      500 \) z/ G8 A+ C! G& h6 E* F" g
               afternoon     18     100
    ) k& N3 Z. Y* K% p2000-01-04 morning       17      40) i/ n9 v1 d2 c1 u) _( X
               afternoon     19      500 ~+ D) L+ t! H+ ?
    df2.resample('D', level=0).sum()
    & y. A' n" Q2 l/ M% f, ?! _9 k" _            price  volume. g5 \. I- s- ?: s
    2000-01-01     21     110' s# B! p2 }* I. Z3 d9 @/ G$ j
    2000-01-02     22     140
    8 [% T" O3 q8 p: ?1 J" }! G: o2000-01-03     32     150
    * M/ E3 t. i6 l  j' d) b2000-01-04     36      90
    # t  r6 P( z* C  ^7 Q/ E% C% x
    : x; l! G" `! t4 |3 a, y1, f# \1 }# X, ~5 }( k
    27 ?5 \( ~( I  e0 d2 o
    3
    ; e" `7 C5 ^- x. O4
    7 A& h0 w6 f/ d5
    7 [9 e4 P+ I6 C+ U) j6
    4 u8 p, k4 c3 B- ?3 U1 i7
    8 @! y2 A. y! u* p% O) ?8
    9 k3 X: p! X1 a$ u- J4 J( R9
    ' P& j! M8 Q1 F+ l1 g6 @, L10
    - g' i& j( R% O11/ U$ Q% D4 F4 j
    12
    6 G: d1 M' g6 G% M0 T  @$ v13
    ' }8 X, J( A8 [3 s* r% I14+ j: @5 {8 M+ P# S- K+ h
    15( |2 l) ]" s: [/ n8 D4 }/ W% s
    16' H: k: ?+ I! V
    17: s) f5 s9 T9 C+ X: a* W0 d
    18
    $ \' O7 C) `- ^' e5 ?19" R8 o- N* C8 m
    20
    0 \5 O5 z3 n' R21
    - \$ n" [9 F, s- Y5 \229 W8 q* A) @% e* J# I& B
    23
    ! s* C# S1 ]5 }$ j* W1 n' o2 x244 r4 w. P' b5 q
    25
    + z% e9 l6 K1 P! _2 b根据固定时间戳调整 bin 的开始:
    * U* U  k; J( k+ J+ l2 h7 Y/ F  l' sstart, end = '2000-10-01 23:30:00', '2000-10-02 00:30:00'
    ) o1 U9 X3 B1 m, y" h* jrng = pd.date_range(start, end, freq='7min')
    9 R8 q$ i/ t) N/ ets = pd.Series(np.arange(len(rng)) * 3, index=rng)
    . m1 K& G" R6 }+ ?1 _( ^ts8 T, d; j2 Z  X2 x' `3 x/ ?
    2000-10-01 23:30:00     0$ N) d8 P) |# C7 v
    2000-10-01 23:37:00     3
    " _) Z5 ]& a4 v* q9 `" X2000-10-01 23:44:00     6
    9 p- f, r; u( J+ V2000-10-01 23:51:00     9
    & ?$ `( a5 p4 v# e7 ?2000-10-01 23:58:00    12
    " K. Z# t5 m* R. p9 V+ U2000-10-02 00:05:00    15
    8 g+ G: N$ w- p$ |9 D; }2000-10-02 00:12:00    18* q& _" G2 ]& R& a% D* i: A
    2000-10-02 00:19:00    21
    % {0 O% S4 q( U8 F$ N8 M2000-10-02 00:26:00    24
    8 b7 V4 e0 v- D* f! K4 Q( xFreq: 7T, dtype: int64$ s& X8 K! B( ?3 t3 f4 _
    5 ]7 Y4 j, d4 T' W# F$ K
    ts.resample('17min').sum()# ]. z9 l+ @0 J2 }
    2000-10-01 23:14:00     07 n8 ?9 {" C, \1 j' `) r7 t
    2000-10-01 23:31:00     90 D; K- _. m( }4 k- [- N! f
    2000-10-01 23:48:00    219 n2 M: b! b$ r- M- @8 V3 ^$ y
    2000-10-02 00:05:00    54$ u! `$ A/ |5 j* S/ \; Q( B& @
    2000-10-02 00:22:00    244 b5 W+ R8 n. p( a8 ], Z
    Freq: 17T, dtype: int64
    7 Z5 `' k$ t* c# M7 `5 v/ z2 Y5 T& @; g2 V5 b5 J' {
    ts.resample('17min', origin='epoch').sum()
    - |: {  N8 v* U1 a  S. ]2000-10-01 23:18:00     0& c& m5 L$ B9 d0 l$ F) G8 f
    2000-10-01 23:35:00    184 L/ ~+ n8 L! p& w8 U
    2000-10-01 23:52:00    272 U2 K4 o: W/ w1 V$ F  t" O
    2000-10-02 00:09:00    39
    / R4 z0 y3 o9 ]  o7 l- }1 w  f' I2000-10-02 00:26:00    24: P7 R' M0 J8 E% U% }
    Freq: 17T, dtype: int64
    7 a" w0 R9 b6 I" F, D7 ^. U# q. p
    4 |) @# L: t' |: \  \: Rts.resample('17min', origin='2000-01-01').sum()8 {7 p' n9 ~! g" f3 h3 s! Y
    2000-10-01 23:24:00     3
    9 t/ B% G8 c; v1 M0 G" K# N/ E2000-10-01 23:41:00    15
    1 T% R3 B: l6 v3 L0 p5 H, \/ b2000-10-01 23:58:00    45
    ) @3 p8 _. r/ v8 Z/ f. o2000-10-02 00:15:00    45
    - G, p6 g7 @" ~- @Freq: 17T, dtype: int64/ b0 {$ p, @$ m2 U  P6 V4 V

    ! I& U  J' h; m: q4 h1
    3 ^8 q. ?' c# r2
    / s4 y0 C' C# |2 G/ b3
    . c% y, s# N3 o2 O. t46 j! p! g7 m3 N. B0 d- W5 O
    54 z. e' i+ t$ ~  Z1 n+ {1 @
    6# H* p9 M6 ]2 n3 Q' i! B3 I1 M
    7
    8 T7 Q% V$ m8 S2 k: P8
      D4 a4 w( B0 _9
    4 m% ?, t" @% d0 I% O' t108 U; t/ k% z( p2 s' ?; i: r3 y0 d
    11" T- Z! i* M6 I2 W
    12/ c1 w. U3 n) F0 \2 {; w0 h- u
    13
    , K& D% i' o' |0 g14' b' S2 X, M' E- V  _
    15
    6 e' q( H, \# g+ h( H8 a* ]16
    1 L" C* {5 B4 F# @! \+ m  W" [- G17, O$ y0 a( l) `: r9 d- r3 [) e( A! F; Y
    18
    / F" z! R; @' {2 j19
    4 E1 ~* X/ n, c* o# V8 T6 U20: g( c7 E! o1 E  i, _2 g- q
    21! f5 W; w& L- P, m) C" A  _$ |
    22
    3 [7 \  i& j# R; F" X1 c, D23
    & j$ H, T% L. y1 [! n2 r( Z3 U24& d5 a0 A9 B' [' |" J* B
    25# P/ n0 C1 S4 N" y1 e# R
    26- S& O- ~" }# u" v/ Y7 H1 }
    27$ z: h  s. G  c1 R
    28% l. F* ~7 n- d3 O
    29* m7 H* |6 _: Y3 G5 R
    30% `: Z! i* S' W; H1 X3 R4 ~
    31
    + f, [, u  T% w5 v! W5 O* K32$ I/ ?3 ?2 J2 i* s# V
    33
    ! N8 {3 s: C! U/ H! [) z7 y34
    9 g- X6 n1 w5 w) n+ ?+ ?# V5 {351 T0 N: `6 X; k
    36
      Z6 |# s; h; ~) e376 l5 y* d3 `# O3 p
    如果要使用偏移 Timedelta 调整 bin 的开始,则以下两行是等效的:
    5 Y3 {8 `& |0 V7 l. H# Lts.resample('17min', origin='start').sum()4 M( H; ^! c6 \8 p/ g& z0 X; ?; \  U
    ts.resample('17min', offset='23h30min').sum()& Q0 ^% n: f. T& A. ^
    2000-10-01 23:30:00     90 E- R/ r: ], z
    2000-10-01 23:47:00    21" X5 j8 \2 r+ K
    2000-10-02 00:04:00    545 a5 b% F# q5 U, w0 T- B
    2000-10-02 00:21:00    24+ E! W9 H3 e" d6 x; X* {6 z- j& m
    Freq: 17T, dtype: int64
    / q7 L! j5 Y2 I" R" s10 \1 ^, S- f' J0 r! ~5 _, B7 h# u# J+ y
    27 O1 L$ ^7 z9 T8 J, \1 P( j
    3
    6 a% P2 F( b" Q' ]6 e2 \, z/ Z/ p4
    9 T6 v  k7 e; r: G2 L; o5. G$ b! O9 _6 E/ M0 B6 o7 w
    6
    * n- \; p7 q) Z( M0 e7
    * H2 s7 x* `7 _  I5 i10.6 练习
    . N3 M9 a) O$ Z; OEx1:太阳辐射数据集
    + N3 @" h- [; m# _. b: w现有一份关于太阳辐射的数据集:
      I# r- O0 K5 l' G( L6 H9 R( A
    / B4 d7 Y& P# I2 {$ Ddf = pd.read_csv('../data/solar.csv', usecols=['Data','Time','Radiation','Temperature'])
    ' f3 k7 `$ c: |df.head(3)! v: G$ G1 N2 i
    9 t  X2 v) E! C9 a* o7 O
    Out[129]:
    0 M" d. V& i# W, y                    Data      Time  Radiation  Temperature3 f4 T' V8 x% I' ?# N5 x
    0  9/29/2016 12:00:00 AM  23:55:26       1.21           48
    * g( C( f4 B4 r: v) ^- h. `1  9/29/2016 12:00:00 AM  23:50:23       1.21           48! G. s4 H/ b1 r) Y+ b
    2  9/29/2016 12:00:00 AM  23:45:26       1.23           48: c7 t! H6 C2 N
    1; |. g2 ]% J% W- |3 y  z
    2
    - O$ L4 @7 Y. J( H* h3  i6 s' a' P! L1 V5 V- Q" X0 |
    4
    : m1 E. ~- t0 \6 I8 A3 B! E- h+ _5
    & N+ j( I( I! u! a6 I6. z1 g8 u. y. p/ `0 N) }8 j$ {! v
    7
    1 u, Q* j5 z# t8$ w, ^1 G6 {9 [8 [
    将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。3 X$ R* ]3 K+ r6 i: p
    每条记录时间的间隔显然并不一致,请解决如下问题:
    3 d4 h  t2 v* H$ K) _( D% x找出间隔时间的前三个最大值所对应的三组时间戳。
    / [1 y0 E) ]( ]/ `" \6 C5 \- I是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。
    5 j/ P. Q, \/ p( T$ z1 A求如下指标对应的Series:
    0 _4 ?- R! `0 a温度与辐射量的6小时滑动相关系数
    6 a4 Y* Z( x- ?% J' V. J以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列; B2 V) D& f' d# o' \
    每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)* U$ G/ a6 L6 ^; A* F
    import numpy as np2 u4 M& i0 C3 E
    import pandas as pd7 t0 j" W3 I9 u0 f) u! m) I  |
    1) e8 a# v- j4 ]
    2' h( r( L1 H* D1 v! H
    将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。1 p3 ?( N. g, l* J+ w
    data=pd.to_datetime(df.Data) # 本身是object对象,要先转为时间序列
    - s$ ^! u" ^/ m5 C" ^times=pd.to_timedelta(df.Time)
    2 y# Y& Y/ `" Ldf.Data=data+times
    - J5 K8 e7 I; V4 S* X! O' }del df['Time']. O# n1 v1 u) o6 p
    df=df.set_index('Data').sort_index() # 如果写的是set_index(df.Data),那么Data作为索引之外,这个列还另外保留% O. ?' M$ i1 ]1 y0 q9 q3 H5 W
    df' s: u8 L& E/ N2 |  R& {+ p
                                            Radiation        Temperature+ N  Y' W* Z2 t1 J
    Data                % a3 T; n- G$ L  X
    2016-09-01 00:00:08                2.58                51  M! V7 A# @& H/ X/ R; c9 |0 x5 x
    2016-09-01 00:05:10                2.83                51
    3 k9 h8 D/ n/ T* U) B" f2016-09-01 00:20:06                2.16                51
    % X& [/ q! Z) w; y/ ^: }2016-09-01 00:25:05                2.21                51
    , q3 o  O2 r9 y/ \% A* R2 E2016-09-01 00:30:09                2.25                51
    + Y) f3 l1 i* E6 x" h5 `...        ...        ...: ]; q% i  d  ^+ ]6 s  ^2 d
    2016-12-31 23:35:02                1.22                414 r% ]; y# }7 q3 z' ?$ _' V7 l
    2016-12-31 23:40:01                1.21                413 K) o8 N: S6 x! J
    2016-12-31 23:45:04                1.21                42( X  W( J+ R8 k# b4 M1 N, f2 v, w# p
    2016-12-31 23:50:03                1.19                41  ]/ F: F: _+ t6 H6 |4 d3 {
    2016-12-31 23:55:01                1.21                41
    ) e3 N: e! o: j9 y- p& C4 o3 D9 m6 V% B& C* o6 j, o& m+ a) P
    17 C' z, `3 N' O; W+ Z) X1 V
    2
    6 K, N3 `7 C# w0 G) o3, D& _3 S& f( `  ]/ B
    41 y0 G* A  {+ B( N
    5, _3 T7 y' Q) u7 s  d! d4 q1 @7 a
    64 e3 |/ }; L' v0 h5 U/ z
    70 c! s4 _. m  C3 Z! F
    8
    * Y0 B) f9 o1 I7 e" }- v99 r& S, z' K6 x) v. G! L6 }; I
    10& T4 I" R, U! S
    11
    ! M) t+ z1 Y. e+ e( W12" F3 |# a7 V8 u, f
    13& K: w, T7 P9 J. r3 a: R$ w
    14
    8 w3 q. I" ~! H( F. [4 V3 u15
    # R  ?2 d- Z5 [+ e6 A& L165 f$ b: y8 h0 V6 C5 J  s8 j
    177 u2 P  W$ W. V) l  ~/ r$ Q
    18
    + A( [! W3 e( Y0 k! y8 S9 |19
    . M) ]7 \1 A) f' z0 ?每条记录时间的间隔显然并不一致,请解决如下问题:
    6 D0 {; t7 }1 p+ m) m7 ]8 ~找出间隔时间的前三个最大值所对应的三组时间戳。+ L8 L. y5 m6 E8 i& G/ a. W
    # 第一次做错了,不是找三组时间戳
    8 _  G8 b( O* Q9 J/ V/ \; bidxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]
    ; {' U; V! s; \" @; R* bdf.reset_index().Data[idxmax3,idxmax3-1]: s3 E1 u$ L$ u& c$ Y6 m2 G1 b1 p
    9 g* C$ J9 @  \2 {# b3 b- J
    25923   2016-12-08 11:10:42* N2 h' B0 [# [! ^) [( L
    24522   2016-12-01 00:00:02% e3 p; T4 x8 \7 R
    7417    2016-10-01 00:00:19
    " z- h0 b2 n: P: C- M! }Name: Data, dtype: datetime64[ns]
    3 c5 y7 ?* ~$ m# A1( v$ u- p: Y1 |8 C. C  h" j
    2
    $ V, ]) t& y- O37 F: g7 I( [! U5 M. L
    42 u0 z- _9 q6 O" ]
    5
    , ^( z) X! z- e: t( z1 w( N6
    4 R" ]5 X) a" C# ?0 v$ E% Q& U7
    & H1 b8 p; {  @8
    " ~" U8 R: J. g7 Y$ Q$ o2 x1 s8 jidxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]
    4 ]6 z( ^9 O: W. A9 plist(zip(df.reset_index().Data[idxmax3],df.reset_index().Data[idxmax3-1])); L) i, ?. g: Q
    , L" K9 I2 a' y& B3 R
    [(Timestamp('2016-12-08 11:10:42'), Timestamp('2016-12-05 20:45:53')),4 a9 O1 F4 x0 q
    (Timestamp('2016-12-01 00:00:02'), Timestamp('2016-11-29 19:05:02')),* Z- U0 S. X/ z( r% q
    (Timestamp('2016-10-01 00:00:19'), Timestamp('2016-09-29 23:55:26'))]
    ! ?; D: B$ ]0 H/ T; a1
    % C" o# t' N# ^% }: F1 @1 G) p9 V2
    $ M  ~- S! c3 J: a( t. [2 A7 U3( ?% A# x% g+ \9 C& y- K
    46 j  N, x5 e' U# E3 i
    5
    * T2 E7 q/ V4 b3 i6# _/ l* P# t3 K- v9 M$ [
    参考答案:
    ) M/ d' T- R, S* y2 b: n& ?! }$ P8 `3 r) q6 G+ n0 W
    s = df.index.to_series().reset_index(drop=True).diff().dt.total_seconds()$ q7 f2 d6 B  G
    max_3 = s.nlargest(3).index
    + J8 i# V, [3 m( ]# N& gdf.index[max_3.union(max_3-1)]. L! f3 q! p0 V+ P

    - |6 [. K# T8 t" W- H9 A# L- |; v6 uOut[215]: 9 [% r/ }( F/ J# k, Y
    DatetimeIndex(['2016-09-29 23:55:26', '2016-10-01 00:00:19',+ N, G# F! A  i, l- z
                   '2016-11-29 19:05:02', '2016-12-01 00:00:02',% _+ o9 J/ X: |, Q: N+ r( ?2 c3 `
                   '2016-12-05 20:45:53', '2016-12-08 11:10:42'],
    6 Z& Y- [$ ]& X0 w! T6 E/ `              dtype='datetime64[ns]', name='Datetime', freq=None)( [/ F) G; }& _, i% D1 D* ]  y
    1
    5 E, x, \$ {7 J) v. T2( Z- i. |! E1 G* w
    3
    2 I9 S4 w5 c4 q: W( A  P, s/ A1 @0 A4, b8 ^2 L% [) G7 w. E2 E* n- s5 V
    5
    ( M6 H( P/ M- ~6
    7 M7 L  f# J1 o) s8 m6 Q7
    4 S7 L4 [. t# n5 k: n- a8
    $ t0 `% e& G3 x* y90 s1 s" E9 s8 w% B0 V9 q" \. m
    是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。. g7 T- E6 N! a) O5 T
    # 将df的indexydiff做差,转为秒数后排序。再求几个分位数确定取值区间; M7 ?9 }7 e- C% ^+ R; X7 ~
    s=pd.Series(df.index).diff(1).dt.total_seconds().sort_values(ascending=False)
    / I7 f1 b" s5 i. d! {) |' A$ us.quantile(0.9),s.quantile(0.95),s.quantile(0.99),s.quantile(0.01),s.quantile(0.03),s.quantile(0.05)
    2 n$ a' D/ r/ N1 Z- `) O. J2 y7 M! V" k  @+ {! c
    (304.0, 309.0, 337.15999999999985, 285.0, 290.0, 292.0)
    ( c' n9 y$ U$ G  N: K15 o4 C7 C) f) F4 M8 ?
    2
    $ F2 @; P2 R. O3
    - ]1 O  I$ \; D0 X  {) w4
    % d7 W9 Z5 f$ [- m9 V" }9 X) z5 r9 D5
    + x& B% @  y6 M%pylab inline8 d) j0 S- {- A# m& a8 X- R
    _ = plt.hist(ss[(s.values<337)&(s.values>285)],bins=50)
    3 N/ K, r, U/ _6 qplt.xlabel(' Timedelta')9 u; I  q* a8 }3 x+ n1 s
    plt.title(" Timedelta of solar")
    . I" F: Z* J+ Q( p2 S14 k; C2 \/ K( {( N: E9 e
    2
    6 v2 v: [( X3 H$ ?+ @* r: O39 n2 t" F7 z- r# X
    4
      W8 T& `, h! ?# c' p
    ! Q: y5 b8 v1 v( u5 _1 n8 N- d& o" W! A+ ~# r8 k
    求如下指标对应的Series:; y2 J, O  u2 [
    温度与辐射量的6小时滑动相关系数
    $ F. C$ A' ]+ I5 N; x以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列
    & K( {) n8 G+ C: p每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)
    / x7 V5 m; f7 ]8 h+ N( R: mdf.Radiation.rolling('6H').corr(df.Temperature).tail()
    8 E; J2 p2 L* d* E2 _. X' C- V( E% l" c2 h
    Data6 i' |) {# u9 {; K) J9 P+ V4 q6 d6 J
    2016-12-31 23:35:02    0.416187
    & o  I2 L  v; }2 \2 @1 ]2016-12-31 23:40:01    0.416565: F3 J, S0 u. j" l( G  t8 `
    2016-12-31 23:45:04    0.328574
    ; j4 B+ {8 i! t7 r6 E; P8 Y( Y2016-12-31 23:50:03    0.2618831 y4 s, _" h3 ?& d, i
    2016-12-31 23:55:01    0.262406  a& F* ]( |6 c
    dtype: float644 d  w6 [0 Q% c$ E5 Z  a  q
    1
    ' M; R  R7 z: c) v) z% P. T: `2
    + v3 Y* z6 w& v8 n; T9 [" ^3
    * Q* O' Y: [# w8 l' e! f( Q4! y) U$ E' \2 U/ i: p  c
    5: y! }4 R. w# D+ J" H( v- c6 ^" P
    6
    ' q4 \) p$ u% C7 n9 B2 U7 m7
    2 n! i' p& Z+ p6 Q3 L8
    , m7 e  P/ m, V9 J& }" H5 m9. K0 Y! v. i, p, \; \4 i
    df['Temperature'].resample('6H',offset='3H').mean().head()$ b# h: L6 ]3 L  z6 ]. ]
    * O$ B" s9 _2 L7 X. b1 w) S" m# _
    Data. D, ?. J& A9 X2 p* b6 n' O
    2016-08-31 21:00:00    51.2187506 h/ y/ J" ~8 n- P1 E0 @; _2 M
    2016-09-01 03:00:00    50.033333
    - M5 F1 s0 I. S5 j  V' [. E2016-09-01 09:00:00    59.379310
    2 R' J; j) z6 F3 a/ m2 p) W4 j; D2016-09-01 15:00:00    57.9843750 a: j0 X  t2 k1 d
    2016-09-01 21:00:00    51.393939
    1 n) ~; I9 e$ y% S! V1 p) b! h# [/ SFreq: 6H, Name: Temperature, dtype: float64! s3 Q1 \8 p+ }2 T
    1' f& t- ?3 M- a
    2
    . k6 j, m" ]/ ~" d5 x$ V6 i3
    / v& w6 F! E' o1 r# t6 U41 A( m6 }9 ?2 x  @; Z6 I0 m
    5( a. x* r( d: `( X" |
    6
    3 c6 G0 v7 B, j9 v7
    ; v& V# [) k) r8
    9 Q8 f; l! T9 X5 u97 j# k: p/ D* l$ v2 Y+ q
    最后一题参考答案:
    3 T+ x/ f; y/ g/ x6 d3 u$ o, j$ ?$ ]5 ]
    # 非常慢, o0 j. M( K# X/ x8 I$ n; G
    my_dt = df.index.shift(freq='-6H')
    " o/ ^% `/ Z( z( ]int_loc = [df.index.get_indexer([i], method='nearest') for i in my_dt]- A2 ?3 e# H' j- y: V$ ^
    int_loc = np.array(int_loc).reshape(-1)
    . E4 S4 w4 d4 }  H8 q3 V& Dres = df.Radiation.iloc[int_loc]
    : H8 a5 s  o! A' @: jres.index = df.index
    9 J8 P# i& o, H9 @3 {* U' cres.tail(3)8 h# b  G: r1 M1 b/ t
    1" Y+ T! |& x7 k. _
    2
    & Q2 K- E/ q) I2 _1 _4 x39 Q' E& B6 q- @+ j
    4
    8 B' N3 i" }  k; J7 }5
    1 K6 g* ]/ w5 D5 y: X6
    , S& s  l8 @& |, U' N7
    : p. n% Z2 {- i0 v# 纸质版上介绍了merge_asof,性能差距可以达到3-4个数量级$ p/ `' ^# F; D
    target = pd.DataFrame(+ r, Z  N" ?6 b2 \5 ~: o$ j
        {
    $ V  T8 H: W4 ]' \        "Time": df.index.shift(freq='-6H')," c# m1 t) i6 k' a# _, V
            "Datetime": df.index,
    ! {& N/ d  c( ~6 l# S    }; H' T0 V( c" d1 g, ?
    )
    ! F# T/ z) S1 _+ x7 D" O; A; ]
    % X" a# E. T! i. b2 H/ Rres = pd.merge_asof(
    9 b+ D) V9 _( T9 t. {" h4 n2 q    target,: ]) r3 d* I, o8 a0 Q" @6 J; _8 g
        df.reset_index().rename(columns={"Datetime": "Time"}),$ ?& g0 Z4 H7 B8 z  C8 V; g
        left_on="Time",
    + Y: \& h9 s0 }8 X' d% a& Y% J0 a    right_on="Time",) ]: Z+ M. O, k
        direction="nearest"
    & s8 n/ v# B1 ~0 v0 L1 n) ^).set_index("Datetime").Radiation
    ! F; `3 D  J( K& p8 b3 ?: D
    % N0 K$ B  Z  F! E" X& r8 [res.tail(3)2 e3 U- `5 l3 p. ]' H
    Out[224]:
    " v7 K$ B. [+ J5 ?; a! p5 {- xDatetime, I0 l2 i- h) [$ Z( p3 ]9 u5 j
    2016-12-31 23:45:04    9.331 U- i) e' Q, w2 i( t0 ~
    2016-12-31 23:50:03    8.49; }8 e. }# v5 u6 G: ?$ j* z
    2016-12-31 23:55:01    5.841 ^, E3 T4 x# K
    Name: Radiation, dtype: float64+ W# ~' I- e. b0 h( ^

    ' S; Q7 \. ~3 X1 q: A, o1
    $ y' W; V( \7 W7 j2" v% J, A. c% ^" W, x9 K
    3
    8 l; y  q: V$ r" _5 y- r( ~1 j4
    ) \7 T+ Y2 H0 s- g. Z" p' P' \5* x* ~8 ^1 ]% y
    64 f0 N$ c* s4 v  l; @  r/ a0 A
    7
      h0 U& V! \# e2 a/ t0 H8. X/ E! x& s9 q$ N& J6 ~+ _3 d
    9
    6 l& N0 d2 h  g; d( q10
    3 U* z/ g4 Q- a( v" G3 J- f11
    ' v* M, A, f- [/ F( d: m) T12
    % r3 Y: d+ Q2 Y0 J+ S& o7 X. m13
    3 a3 ?3 {5 k; S7 e' {3 o' k14  h7 e$ W2 [% b
    15
    3 I$ c, a1 M5 m. {16" _$ T) q& K: n4 m* |% ?% F
    17( K# Q& c9 S; e
    18( y5 B, s5 j+ r" }1 [) J1 @
    19
    9 @  z; _- x, Z+ s0 x7 ^20/ R/ o4 ]4 B" I2 P- B% B
    21# D6 ~  O0 r  _; ~) W
    221 K/ p+ r4 g" K; y1 w0 V
    231 e5 @1 \% ~! e1 r) H" R
    Ex2:水果销量数据集9 ^: x* P0 T% ^+ v
    现有一份2019年每日水果销量记录表:, N: p! y6 T) Y* U

    7 X- r% Y6 h* P5 edf = pd.read_csv('../data/fruit.csv')( N; t1 A  P2 h8 F& z
    df.head(3)' ^. r4 Q% g8 }3 d1 ^, y

    ) V! |8 J7 Z& D3 a' IOut[131]:
    9 j- w  D8 m  L, |1 ^         Date  Fruit  Sale
    7 m/ ~% W% R& L: b0  2019-04-18  Peach    15) i! ^- _/ N# @2 R6 o4 Q
    1  2019-12-29  Peach    153 [2 ^( C/ `% j2 l7 o7 F. c9 e
    2  2019-06-05  Peach    19/ B2 D7 T& [' m
    1( s* t& o; r( u3 C7 D6 W4 B$ Y
    2) E! n/ f6 ^. d& O/ e
    3
    ; r# \" b+ h& |9 X2 l0 I8 a4* ]7 X$ K# B$ b, y" t' t
    51 F, c6 e& [4 ]
    6! Q( v* A8 Y4 X6 V! C0 M
    7
    , f8 L# t4 v! G$ G4 D+ M80 W. x0 J* d; J) P- Q4 C/ j) k$ s
    统计如下指标:0 s# b. V) D0 y; I+ h- T# k/ t
    每月上半月(15号及之前)与下半月葡萄销量的比值. l, J& M: O  U# ?1 h$ A: t
    每月最后一天的生梨销量总和
    ' s7 @' K! ~9 J: ^$ e每月最后一天工作日的生梨销量总和
    ' c+ q1 E, b8 O6 H每月最后五天的苹果销量均值1 {  R2 ^) [8 D/ [/ R
    按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。) F! e! a% Y9 ]; S
    按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。
    - `9 Z8 L6 d9 d  v( Rimport numpy as np
    : k1 Q2 v( g  o6 v0 |# qimport pandas as pd; e) ?( g7 Y* j+ n8 t0 k" M, `
    16 Q. J4 p; Q+ d3 i* f6 ]& X6 s' E' {
    2
    7 f( L/ D3 [9 ~" A统计如下指标:
    - L" p" Z6 Q$ o每月上半月(15号及之前)与下半月葡萄销量的比值) O& D  H% q* \2 z: o
    每月最后一天的生梨销量总和
    : I6 |# I  \/ u6 c每月最后一天工作日的生梨销量总和, v( n; J) x4 I3 e# I: M
    每月最后五天的苹果销量均值
    , L1 W9 Q0 z& a! `% j1 Y# 每月上半月(15号及之前)与下半月葡萄销量的比值  E1 v3 G/ G8 J* w6 q; P0 r
    df.Date=pd.to_datetime(df.Date)# v7 j8 ^9 b7 f7 |8 j
    sale=df.query('Fruit == "Grape"').groupby([df.Date.dt.month,df.Date.dt.day<=15])['Sale'].sum()
    1 _' O* x% R! a, ]- Psale.columns=['Month','15Dayes','Sale'] # 为啥这么改没用啊
    7 J' U* u1 P8 N9 D3 S2 z4 c+ ]sale=pd.DataFrame(sale)
    ) M" a( r$ k: O0 wsale=sale.unstack(1).rename_axis(index={'Date':'Month'},
      X  b3 m+ Y4 f0 P8 |) b                 columns={'Date':'15Days'}).stack(1).reset_index() # unstack主要是两个索引都是Date无法直接重命名
      y/ D5 I9 {3 V( bsale.head() # 每个月上下半月的销量7 i9 T! M2 R0 V4 f: k$ k5 p

    5 x) \" I$ p7 C# M( t  Month        15Days        Sale
    4 r# X$ j, `& j9 T+ k% y. G( r- s0        1        False        10503
    : D3 e8 l5 _( D/ D* l1        1        True        12341
    / _! T4 }1 _/ X, B9 o+ v2 p2        2        False        10001
    * l, N9 z9 o7 r# N  a" {- X3 H3        2        True        10106
    - u! q6 {" \- J3 N9 O4        3        False        12814
      Z0 `4 H5 d' U$ s3 O! k4 X
      H4 t; D+ T3 N' z# 使用自定义聚合函数,分组后每组就上半月和下半月两个值,根据索引位置判断求比值时的分子分母顺序
    " x7 L. g5 R+ ^* @, X" Rsale.groupby(sale['Month'])['Sale'].agg(
    5 W. J( P  Q  B4 x  l                lambda x: x.max()/x.min() if x.idxmax()>x.idxmin()  else x.min()/x.max())% b" P8 m) X0 N1 }, x! J

    ' r0 U) \6 ]9 b6 @% @( O! R) T7 nMonth
    , j) j7 ]5 `; L# m  s1     1.174998
    8 {5 A" |( |+ G! _# x1 |2     1.010499
    % F# W  H" O$ D0 L2 Z! K# B3     0.776338
    0 c( ]6 q; o6 T& D% h6 v" o4     1.026345
    # r0 j& f' R+ z5     0.900534& Q/ M  S( S+ w/ i9 [! t! H
    6     0.980136, m1 \5 Q) C% ^; m$ Z
    7     1.3509603 y' R8 E- _) w  T  A
    8     1.091584% B* l0 n2 N! x7 \4 z' b. x9 f0 l
    9     1.1165089 {: ]$ s7 h8 {% H4 e2 X
    10    1.020784
    5 {6 `3 l1 S- r% h1 q: Y11    1.275911
    ) y) O  U  J$ ?( Z. _; X" }3 ]12    0.989662* l3 V9 G' f# |3 S5 n1 e" U2 v
    Name: Sale, dtype: float64  b: p! O2 P$ N5 R0 @7 y3 m3 |
    ( Q0 [9 G9 p+ \- E
    1
    ! @. _. l: N; O! L0 p" g/ T2( H' @9 a( Y8 b: J& W- I7 h
    3
    ! P8 X9 ^! F  T5 T" k9 w! Y4
    5 ?: A: s$ F/ n( }0 x$ |+ B5/ z" k! w. W) N+ C" z! r: G
    6$ S. G% _. x8 j; s0 C7 s
    7; q! ]% S( I% L1 n) ]9 s) X7 w
    8
    " T- |2 x2 X  C2 K& H7 S3 D4 w97 u3 u4 V% x% d# y
    102 W4 k3 y# t, A2 @: m! D2 z
    11
    ) m% d* y1 }( J" s. \/ \1 O9 e12
    " d( A2 B' k5 h; }13, B3 Q. o  T) `
    14
    1 K6 h+ T( Y$ m& h15  t' w& C3 N/ r; |: o+ t7 w' C
    16
    ) G# K' ^' B4 u! c8 x+ j- Z: g4 `17# e4 L0 J& v1 A. v1 c
    18
    & j& y& l! i2 x* X3 T19
    ; f' h* Z; U/ _2 _20
    ; j) i) B! \1 U7 A" `$ Y214 u, ~* S$ D! z# M" _# E
    22' i5 o# q" M; C, i$ f
    23
    ( \& ~( z3 y) `! P" g- C24! _, \6 w, i) a$ f- D$ [/ T, A
    25/ ?$ P3 E  C4 c
    26
    2 d7 e5 ]( A/ [; R9 R) }5 [27; C4 r; N5 O/ J4 ^7 z, I
    28. y9 ^. \/ M" ~5 X2 v/ N- u2 T
    29
    2 n" T6 R# |4 {9 P/ e; A4 I30
    7 m, o2 @! ~8 m31
    - D% o, m2 l" S) P5 u32* Z% l5 Y% W8 X4 J8 t$ @8 g
    33
    7 D# h  K1 r/ q$ H7 Q- n34
    8 s8 e( ~: y& L/ t" {+ R! r/ l# 每月最后一天的生梨销量总和
    4 W, X! `, x( E5 j: i. ^3 \' P& kdf[df.Date.dt.is_month_end].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()1 D: G! S, @. w! d$ d# Y
    9 A- h& H* Z' L9 Z5 s/ U. B- T
    Date
    " z" ~- k# u* s# }+ u1 n3 P2019-01-31    847- z$ @3 X& x, A
    2019-02-28    7749 ^! X# M6 G! o: }! g& l- L
    2019-03-31    7614 u$ s% v1 K  D: S1 m
    2019-04-30    6481 d6 \/ f5 S( f% b8 v+ Y
    2019-05-31    616
    5 g3 S/ ^! @( a- U/ l, U% ?/ [1
    : q+ S7 B% }% \" H2 c: v2, s, A4 T% G4 @5 v! m
    3  r& i; L5 P# J* Z; [" }
    4( M3 G& K! Z. a
    58 N  y. Z3 @2 i% d: ^! m
    6
    + P! p. P4 P9 `7
    ; `# y6 t/ m" T6 H& I8
    7 X+ y5 w+ \0 ?  t5 n9
    . Y/ Q& k! o9 q0 V& G# 每月最后一天工作日的生梨销量总和
    2 c2 E8 |7 q  _  P- `( `, rls=df.Date+pd.offsets.BMonthEnd()  {9 `7 _& W( C$ z# B3 p
    my_filter=pd.to_datetime(ls.unique())
    - J6 `) K5 j. }2 t7 |; D# ~) mdf[df.Date.isin(my_filter)].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()
    2 G: b, y7 [7 w5 H; X. l8 T) U$ h8 u3 z5 w* A
    Date
    9 _* b+ B' x" b/ j0 ?  P( u2019-01-31     847( T4 m6 U( n- O& `5 K  g( F
    2019-02-28     7740 i. s4 f, n4 w+ b; m7 A
    2019-03-29     510
    ! J% ]- o# W2 A* n# k2019-04-30     648
    + t! W1 H" y: O* p9 B8 r9 ^/ R2019-05-31     616
    * ?9 l8 ^! q) W% b: ~& \8 `1' G; L+ L6 P; v
    2
    4 e" c8 ^# {& p' O: A3: r0 F$ V' ]! D5 C4 k
    4
    + Y( ]* R7 {& f7 Q/ U5
    ! A: Q/ g8 s6 i: w$ o6
    0 }4 v/ _, B2 ?6 A" \+ f0 g7 b$ Y7
    1 H6 w( g6 @& g' j1 |: m1 r% o1 {87 }, |5 V5 p! t- k
    9/ n* S/ `9 G, T% u. g  z7 S
    10
    & T' m+ p+ E, r4 {5 A9 R11. [/ G8 E7 E& a: e
    # 每月最后五天的苹果销量均值3 u8 b6 S1 y( X, @
    start, end = '2019-01-01', '2019-12-31'
    / ?3 c) y) V, a# Pend = pd.date_range(start, end, freq='M')# `1 {/ ^5 X. I8 b
    end=end.repeat(5) # 每月最后一天的日期列表,重复5次方便做差! g* W! y7 I& Q0 ]7 {' A# v
    " G4 N( e! Y3 f/ w7 ^
    td= pd.Series(pd.timedelta_range(start='0 days', periods=5),)
    # C. G2 [3 a  o; n* Itd=pd.concat([td]*12) # 日期偏置,最后一天减去0-4天
    2 s! e3 }, ^, w: D( J- yend5=(end-td).reset_index(drop=True) # 每个月最后5天的列表8 t- u' n, b+ O! M& p% b8 u

    6 W0 T- b& z! g3 oapple5=df[df.Date.isin(end5)].query("Fruit == 'Apple'") # 每月最后五天苹果销量
    ! X6 q6 E2 ?  U& c3 ?8 Mapple5.groupby(apple5.Date.dt.month)['Sale'].mean().head()
    1 _' {/ N' b2 S( Q/ P
    " D9 C0 u6 d5 YDate
    4 O, A# C6 s' J' ^) H1     65.313725
    ' }6 f9 R; W7 A# M# S& A2     54.061538" n4 c6 _. V' ]! x2 P# [
    3     59.3255815 ?1 n- g1 ~* \3 n  I
    4     65.795455- C  N0 y$ X, V* M  h1 E4 @* }
    5     57.465116
    8 W0 Y* B, X! O* ?2 L/ N8 S. W  y" y5 _! }. m7 A( Q0 v
    1
    ) o* `) u9 |4 m" y0 x) A2
    ) E! T5 m+ q# ]7 N3
    + `! Q  ]& T3 z# W1 F/ G47 {5 L& P" D: r# }
    56 W  t: u) Q( v7 C: N, c7 ?
    6+ N# S- Z& b' O  E! `( z* R
    7
    & ]8 I' w7 f* g- u88 |- g: ~$ p+ j* b" @2 _
    9& M+ x8 I1 `0 G' |* u
    10/ `6 L- t* w+ T5 u
    11$ O$ L9 H" {. Q; B0 F& D
    121 l! G+ Y( t) L7 [0 Q
    135 R5 j) x9 K9 R* ]7 y, @; \( @
    14
    1 G$ i9 g8 c5 s8 ?* G15
      G- f8 K6 H6 }4 ]1 j16; p. J: ], q& s) h7 G3 L1 \2 G
    17: C* b# D5 F4 D
    18
    + ^( ?: ]7 @! W; t# 参考答案:, Y# S# W3 f/ w+ k( R
    target_dt = df.drop_duplicates().groupby(df.Date.drop_duplicates(# g/ }  B) i0 o! {3 z, `( p
                ).dt.month)['Date'].nlargest(5).reset_index(drop=True)
    " c+ B. [4 ?! @: O- J8 V, V4 Q5 W
    res = df.set_index('Date').loc[target_dt].reset_index(& }) C# J1 \# A2 t& S0 }% T
                ).query("Fruit == 'Apple'")
    + v' V' {: U5 r0 n
    8 x, z( I, ?; M. `4 Kres = res.groupby(res.Date.dt.month)['Sale'].mean(" b/ v$ M& ?% ?  M/ x8 X  F/ x
                ).rename_axis('Month')
    & p- p% [$ a: ]7 r
    6 F4 S9 g/ p+ @/ l* j0 R- B8 b/ m5 b2 P) T, t
    res.head()
    + F$ {7 r, K' c% h* D: zOut[236]: 3 a  U- Q7 v: z' t( P6 ?
    Month% O$ J# b; G( ?% ~
    1    65.313725
    # U* l# T* f3 N3 O- c, j2 A" j2    54.061538
    5 L. Y& h0 ~8 U! f+ A  _3    59.325581
    # _+ z: A8 e" ^3 m6 Q) k3 C4    65.795455
      y- x" Q5 M' f5    57.4651169 v- p7 p( {. q- U+ \) B
    Name: Sale, dtype: float64
    $ a# N; u( J1 w/ Y
    , P# b6 u3 i: \3 D18 Q2 F4 p$ s5 U$ p
    20 v5 E- f. I3 j% M9 `* f; W. d7 d
    3
    - c- f- \* [; P8 s+ ~  W4 x6 K4
    # u8 Q6 D$ ?& f1 ~7 ~2 G5' |2 o5 ~- {/ \( k" S
    64 D( [& A5 ]" ]7 V4 @9 Q, v
    7
    # B3 g, Y4 j2 L0 f: M8
    ! U- Q! P$ L/ C7 S2 ^8 a0 n; t& k' U- N9
    ! l3 _( S, F2 J, Z100 ]3 k$ N/ ?6 E, n8 l7 ]
    11
    # x0 ]3 [" ^7 G- }3 g12# ~; F2 S& V/ E0 k6 W! `
    13& i! G6 @( r$ o0 |2 t7 P
    14; U: L3 P6 r  L3 Y: r+ |
    15* H8 T/ y* x9 h: d
    16; s2 y: u8 ], q: J
    17
    ; X+ s; g+ c# Z/ k/ A- X3 {$ H8 U9 z185 {) |' `* ^% b& ]% v/ D
    199 r6 J+ f# K6 y; p: Y
    20
    + j& T* f! v8 s, Z# K0 R, L按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。& p+ i0 T$ y( U! h- G4 i5 n
    result=pd.DataFrame(df.groupby([df.Date.dt.month,df.Date.3 t/ [+ ~0 ]5 a' r
                                            dt.dayofweek,df.Fruit])['Sale'].count()) # 分组统计
    9 C# O8 |1 S( ~6 W; l( [  @                                       
    ! h2 h& \$ ~$ ~( Z+ I2 t( K+ Fresult=result.unstack(1).rename_axis(index={'Date':'Month'},
    % E0 `$ W2 h3 v* D                 columns={'Date':'Week'})  # 两个index名字都是Date,只能转一个到列,分开来改名字.
    * ]3 s8 B! N" e, e& ?result=result.swaplevel(0,1,axis=0).droplevel(0,axis=1), ?2 F6 U" h6 W* r# z
    result.head() # 索引名有空再改吧% N6 [6 s6 i8 a" N0 C  L+ \

    ; p) A5 M* j9 w  E1 W6 Y. M          Week        0        1        2        3        4        5        6* u, q& l- @' Q' p& F8 s8 c3 f0 S
    Fruit Month                                                       
    3 m: d8 X& o# L  }Apple        1        46        50        50        45        32        42        234 ~3 @: V/ f+ V0 q1 C
    Banana        1        27        29        24        42        36        24        35
    " _4 Z6 m: w# ^# zGrape        1        42        75        53        63        36        57        46
      J4 H3 ?* P( }( q) h8 ~/ qPeach        1        67        78        73        88        59        49        72- w: V, j& A# S* l, }: j9 |5 r7 u
    Pear        1        39        69        51        54        48        36        40) D7 P, A  [1 E! ~
    18 P  s  n" E& ~. ~5 @* {
    2
    2 b# K( o. I" s) D. T) e) H3
    ; y% W: [8 J0 Y0 A5 O4 k# v42 F6 F0 B3 E* s9 i2 Z* F( a
    5, r2 y6 L  g. s9 z$ _7 @5 n* h, c2 j
    63 b+ a' V( i# @$ A8 s
    7
    5 z: J% L/ w- Z; f9 X8
    + Q, ^3 J: Y- ?& O95 s( f; V3 t' w3 @" H
    100 {& E% I! W! M( t( o9 X# k
    11) Q; H0 q/ h2 a4 X4 K
    12: f/ S0 Z0 {- r5 M3 `  Y0 i
    13
    / `+ W, t, i# t$ W' w; R14
    3 v* M* p0 j( W. v9 F: t15% s  ~( Q7 W8 _
    按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。
    % U; N8 D' h2 P: v' f# G( ^8 P5 M# 工作日苹果销量按日期排序. F- F6 x! d* D" i- b5 s
    select_bday=df[~df.Date.dt.dayofweek.isin([5,6])].query('Fruit=="Apple"').set_index('Date').sort_index()
    & H. H; _8 [" C2 q* k! O+ k6 Sselect_bday=select_bday.groupby(select_bday.index)['Sale'].sum() # 每天的销量汇总5 L2 \) p+ b0 _! y+ O( V1 v. f" n
    select_bday.head()
    " ~3 C6 `$ H( L3 t3 C7 `5 Z* w+ |, E% `; W" p
    Date! T" x+ l3 b% C: ]5 D( V# N+ p
    2019-01-01    189
    $ C+ F$ f( m% w5 L; y2019-01-02    482
    8 Q( g& `5 e- z! L% v! ~2019-01-03    890
    5 X1 J* `4 C! M  \0 G, Z2019-01-04    550
    , {' X/ g) Z2 w; Z1 U4 H9 H8 v& H2019-01-07    494& R  T6 b7 v! x! C9 p) H

    . v. P4 M7 I6 v$ G+ k2 r; l# c# 此时已经是工作日,正常滑窗。结果重设索引,对周末进行向后填充。! N4 D5 V! a9 O2 i. r
    select_bday.rolling('10D').mean().reindex(df.Date.unique()).sort_index().ffill().head(): n+ s( w+ l+ W  \3 }* x7 L" a

    ! y* N! _( l  S, ?8 kDate' R, W% t1 |# N. @- f
    2019-01-01    189.000000& {/ T, k1 H) O  R
    2019-01-02    335.500000) @8 W% V; O( P
    2019-01-03    520.333333/ g+ u! o. [! B. k
    2019-01-04    527.750000: K9 L) E- @7 m, X4 f, J$ D
    2019-01-05    527.750000& X1 b/ l$ S( h4 W# o4 ]) q
    4 ?7 A' ^  s! i8 L& w) Y
    ————————————————# c) J' k2 ?2 ]$ [6 a; H
    版权声明:本文为CSDN博主「神洛华」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
      C& m+ G) }5 p原文链接:https://blog.csdn.net/qq_56591814/article/details/126633913
    - d$ T0 k$ O" j8 w. e- ?
    1 v4 A, I5 l* G5 o7 j
    6 }( V' r4 w- D! k$ |# B1 C
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
    您需要登录后才可以回帖 登录 | 注册地址

    qq
    收缩
    • 电话咨询

    • 04714969085
    fastpost

    关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

    手机版|Archiver| |繁體中文 手机客户端  

    蒙公网安备 15010502000194号

    Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

    GMT+8, 2025-8-1 09:20 , Processed in 0.360606 second(s), 51 queries .

    回顶部