QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2825|回复: 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
    & w8 g% m% K0 Y
    2 {- O8 K7 |1 Y
    1 Z7 m2 ^  |) l; @
    文章目录# }; Q( G# U3 Z/ }$ E  M! _
    第八章 文本数据- ?7 a0 K7 i) |3 W( O' t
    8.1 str对象
    ! x% D  D! N1 \# `" m8.1.1 str对象的设计意图% v' f' |+ V; F( m
    8.1.3 string类型- b6 A( _$ C  P  ?4 _8 N
    8.2 正则表达式基础
    + a/ W  R& |8 S1 `4 c8.2.1 . 一般字符的匹配& B: `6 m& ^& X& a4 ~
    8.2.2 元字符基础
    + D" w7 h6 s3 K, @8.2.3 简写字符集, |5 Z! j: G( v
    8.3 文本处理的五类操作: x" l/ X* [: ^6 G, E5 u
    8.3.1 `str.split `拆分
    " z9 R/ K+ O) A8 {9 s8.3.2 `str.join` 或 `str.cat `合并2 W  s% N% F0 c
    8.3.3 匹配
    5 ^7 [# n( y8 d- I8.3.5 提取
    . z# c5 f7 H% j6 Y8.4、常用字符串函数9 r4 Y# M+ o( M# h
    8.4.1 字母型函数: O# `& G, \$ W
    8.4.2 数值型函数& U* ^( \3 k2 ]0 }0 n
    8.4.3 统计型函数
    ) a4 N$ g0 e0 p9 ^; W8.4.4 格式型函数! I3 B' E) O8 ~8 [: Y& b+ P
    8.5 练习
    4 D* |) h) y! n$ ^7 TEx1:房屋信息数据集3 r( C: w: V6 w& D) H( r
    Ex2:《权力的游戏》剧本数据集
    ( o8 z. I4 n1 A6 }' `第九章 分类数据
    6 f. }$ g. e, |( e$ ?9.1 cat对象2 A) I4 v  m0 N* g* e& y
    9.1.1 cat对象的属性# V& O# P  G- i: w& O
    9.1.2 类别的增加、删除和修改! U4 f2 _$ Q+ r8 u: t7 d, Q
    9.2 有序分类
      k+ y/ v0 u2 |. i9.2.1 序的建立
    : d  i) d- `7 p" P- {4 Q# Y9.2.2 排序和比较4 f! k3 U) _/ U- J
    9.3 区间类别/ j1 @# }' H1 a7 s
    9.3.1 利用cut和qcut进行区间构造- Y5 o- O0 W) l, h7 b3 R
    9.3.2 一般区间的构造
    : q' T3 j4 p7 A0 f4 d" ]( V9.3.3 区间的属性与方法
    ! N! o1 ~* O6 k/ Q6 b9.4 练习4 o4 D7 u; i: k# |9 L( o! q; k
    Ex1: 统计未出现的类别
    . L; c& s, R: l' rEx2: 钻石数据集
    " J0 R8 J5 z# O, |+ e1 d( \1 s2 _1 c第十章 时序数据
    2 \2 `& x8 C0 O6 W. z. I. w10.1 时序中的基本对象7 f: O' Y) w- N& v0 y
    10.2 时间戳
    & a% Q4 ?% Z! ?; b: X* E1 p10.2.1 Timestamp的构造与属性
    ) f. X5 U/ y; ]! ]10.2.2 Datetime序列的生成1 q7 O! v% _0 M, r$ m- y
    10.2.3 dt对象8 n; O0 H$ g7 V% @
    10.2.4 时间戳的切片与索引+ x4 H+ |& \% p' ^8 d/ i3 F, ]
    10.3 时间差
    ( N8 z, X. z/ {$ `) Q) F10.3.1 Timedelta的生成0 B9 y2 w/ }5 c- \2 h* j
    10.2.2 Timedelta的运算4 w: E/ {7 Z, N
    10.4 日期偏置7 v1 g+ j& ^+ ]' R
    10.4.1 Offset对象* R1 d$ s. ?  S; _- c8 Y. d
    10.4.2 偏置字符串: ?2 C- Q- P5 B4 c9 ?" w" F
    10.5、时序中的滑窗与分组
    ) f! m* p( v, Z% u% u6 d, z10.5.1 滑动窗口
    3 g  y1 {8 D2 \$ ]1 J' f* }10.5.2 重采样
    7 O4 K! Y2 k& _% j! j8 P8 {10.6 练习2 r2 y4 f7 R* {. t; w  A& z
    Ex1:太阳辐射数据集
    % f  x9 F/ k: Z: J  EEx2:水果销量数据集
    * F! f* h& O# J' [1 H# H' c& _  课程资料《pandas数据处理与分析》、github地址、讲解视频、习题参考答案 、pandas官网5 J* y1 [% i! S/ x" \  N
    传送门:& f* m8 H+ |7 p

    4 P! n( X! q1 E0 x; C: w3 {; vdatawhale8月组队学习《pandas数据处理与分析》(上)(基础、索引、分组)4 s2 Q; C, z5 T) Y
    datawhale8月组队学习《pandas数据处理与分析》(中)(变形、连接、缺失数据)' @% ?/ u3 D0 y: H+ F
    第八章 文本数据  q9 Q! V! o- J5 c
    8.1 str对象) w# |* v/ `1 V" h! {
    8.1.1 str对象的设计意图8 q# r8 p. k: ~& }1 V- p, Q
      str 对象是定义在 Index 或 Series上的属性,专门用于处理每个元素的文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其 str 对象。在Python标准库中也有 str 模块,为了使用上的便利,在 pandas 的50个 str 对象方法中,有31个是和标准库中的 str 模块方法同名且功能一致,例如字母转为大写的操作:$ r1 l' A; ?/ D; g$ d, ~

    8 U( Y- T5 @, D1 o7 g& ?var = 'abcd'
    ) ]! k# X# H  v1 [: ?8 ?str.upper(var) # Python内置str模块/ G9 T, S2 G) m! t3 c
    Out[4]: 'ABCD'
    ) m4 {2 |- E$ k- j6 k+ h$ i' u8 Q+ p# [6 |5 I7 w
    s = pd.Series(['abcd', 'efg', 'hi'])3 v+ P3 U5 @4 S
    . r5 f* \/ s9 g
    s.str
    : }# J# O- q; j, dOut[6]: <pandas.core.strings.accessor.StringMethods at 0x2b796892d60>: k- A  c4 @% i' ?) @3 m$ o. c
    % x" ?8 i- S8 j- h4 `) {! _! f, r
    s.str.upper() # pandas中str对象上的upper方法/ q- g! U7 B9 m( |; y1 V1 {+ _7 l
    Out[7]: / {% L' T- h9 e! v; @# y$ I- ~
    0    ABCD
    / ~4 ]9 U& P% ?: G2 R. I1     EFG) H: V$ b* S, [9 Z
    2      HI% R5 ~* x" _0 h+ |8 Z
    dtype: object
    " N  X4 C& G/ b+ }2 E12 B! k, ^2 T0 [% ^2 g+ J
    2
    # b' a: R# R8 k4 X3 S$ X36 `$ S; Y) f1 f; T% |
    4, P- T9 S3 U% D3 @2 ], T' b
    5
    ( y2 V5 ~( }1 F9 s, D3 C  Z5 b# X6
    3 F, \- }! }! j' S$ A+ A* A74 V- }. K& `" q5 j/ K) t& v
    8
    % M8 h5 M9 l8 [# ?/ t91 c* s" D1 R& `. K- P0 q. j& c
    10
    4 ?  e: y- X/ [* z' W: @11
      t$ `$ h& r* H, Q; B2 z& y124 e% `1 @6 d4 t2 L( @6 Y
    13
    3 g2 d" g4 l" l5 J0 l140 M  @2 O/ w$ k' n% F1 Z- A" P2 i# v
    15
    6 ]* c) V- |* m7 O3 E7 M% H8.1.2 []索引器
    . i% J+ {5 n' h& E  对于 str 对象而言,可理解为其对字符串进行了序列化的操作,例如在一般的字符串中,通过 [] 可以取出某个位置的元素,同时也能通过切片得到子串。
    6 s7 n' [* M# p  P+ A) K  pandas中过对 str 对象使用 [] 索引器,可以完成完全一致的功能,并且如果超出范围则返回缺失值:
    4 O5 {$ ]/ J) d7 [0 z( R
    , [! D/ U# ^2 A$ A# e3 j) v8 Ds.str[0]
    - _+ _/ \. R4 V3 ?Out[10]: % [* o. m* l+ H3 o" D9 u
    0    a
    6 g" L# t0 k$ `" y5 g1    e
    & o0 S" ^- J1 n& Y1 j, W$ c2    h, O. r% Z& P$ b) \, k( f$ V" W! ?9 A! \
    dtype: object
    ! r4 n, w7 Y9 Q( v0 n* p; S3 j* ~
    s.str[-1: 0: -2]0 \4 K6 _4 c' I$ t6 j4 T8 t% B4 G' l! H
    Out[11]: 1 I; J- ?4 p( [
    0    db7 m5 \* V' w5 O/ S7 p$ |1 X
    1     g$ B4 Y7 m3 F, Y7 y0 }3 c
    2     i/ k/ n6 H; S) N, f% q
    dtype: object: P+ _; L( L- d7 |1 W& r
    $ c6 L1 a  G+ b( r2 N2 R4 a
    s.str[2]
    0 }5 ]0 X+ u! B) `, NOut[12]: ! I2 V/ m3 U- T* W% p7 z! L4 f5 o- d
    0      c% X2 _6 }- z5 p3 p4 C6 ~2 K
    1      g$ p: k$ S; z) d/ ~2 T
    2    NaN; h9 h1 X& N: b9 ?* M5 g) H" M
    dtype: object
    8 }& P: O5 V7 z- y/ p
    ; m+ L0 u5 E; {& ]1/ A- Q& L" J5 P! X/ s8 S. j& {
    2
    4 o% G  T/ C4 g) c' }3% ?9 j, H( |! A* U. p7 H
    4  a+ S1 e' X7 p9 J* i
    5
    ' H% B+ q" @6 j9 _7 L4 o% _/ s+ V1 N9 h60 U6 s0 f6 h4 W9 K, C
    7
    + l* Y0 e* o3 L% `9 h8
    ) p- i; W, k, s2 m- c8 u9
    # L& \: q8 M! N+ h; P1 F7 L10! ]3 E) d4 r, J  ?5 [9 b+ J$ ]
    11
    : K2 ?5 u7 n. W12
    0 A5 @- b7 M4 k8 q/ ]2 d13
    * {$ t) r' C% w$ R* k14
    8 ]' U  @/ ?3 F) k* W15
    + S5 j+ O6 {* ^$ I. ]) x* B2 O16
    2 s! ^1 {2 r! C3 R1 b: m& C170 a: N# K$ d( I1 h0 C7 i8 C
    18
    ! z% [( z, R" y: d19
    / s1 j% Y4 l# z7 a  O9 b3 c) y203 h% b6 B, j" P  j. |7 w
    import numpy as np! n  u; e0 e+ P2 ?
    import pandas as pd. H5 l0 S9 a! a% U9 [' D$ R

    9 b- T5 q9 f; v7 Es = pd.Series(['abcd', 'efg', 'hi'])0 F+ x9 ?, }3 w
    s.str[0]
    , J+ s' n- _# s; R" H1
    0 n) |" X. H( Z; p' u$ ^$ I2, }$ W; k/ n% _( o0 o
    3
    ( x) c1 L; K8 N8 e4
    6 q/ I  I/ M* F50 h/ C6 Q2 ]# L
    0    a
    + h) i& N; Z( a1    e
    5 P! ?- y0 L5 J5 c, r2 ^2    h- C4 Z- Y# Y* I
    dtype: object; P5 p8 J2 G+ I) T; {; O, M
    1
    0 w8 i1 M, `) i5 Q2
    / H% K/ U! M2 B32 U1 u; k% \8 N8 ?9 W8 h
    4
    & p) t8 Q- T" B; G8.1.3 string类型( ^9 d$ H1 t  E( N" x0 ^
      在上一章提到,从 pandas 的 1.0.0 版本开始,引入了 string 类型,其引入的动机在于:原来所有的字符串类型都会以 object 类型的 Series 进行存储,但 object 类型只应当存储混合类型,例如同时存储浮点、字符串、字典、列表、自定义类型等,因此字符串有必要同数值型或 category 一样,具有自己的数据存储类型,从而引入了 string 类型。
    / A5 D, E; n( i- o  总体上说,绝大多数对于 object 和 string 类型的序列使用 str 对象方法产生的结果是一致,但是在下面提到的两点上有较大差异:' G. G7 @2 Z8 u. f8 H0 h! `
    5 \. A/ P" E' C- T* m# s
    二者对于某些对象的 str 序列化方法不同。
    6 }5 `  P" q" j; {可迭代(Iterable)对象包括但不限于字符串、字典、列表。对于一个可迭代对象, string 类型和 object 类型对它们的序列化方式不同,序列化后str对象返回结果也可能不同。例如:( G5 z1 o* G+ J2 ?
    s = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a', 'b'], 0.5, 'my_string'])) |) {$ b2 Y2 H0 x( ^& Y0 ?$ D
    s; e$ q' Q- |4 c% y9 X7 @8 S: @: ~" D4 z
    1
    ( n- d1 o2 |( I0 A* {! Y2& H0 l6 f- l8 O% p/ w+ e
    0    {1: 'temp_1', 2: 'temp_2'}7 X- X5 I0 N1 X* S9 F! J' e* v
    1                        [a, b]  u) h$ ~3 T  S' Y
    2                           0.5: A5 a. p+ y3 Z$ S2 ?6 Z4 D0 P8 J
    3                     my_string. f9 k6 U2 j# `# n/ [
    dtype: object% G% ^& A' W7 Q. z
    1
    4 C/ Q8 V+ K( t" Z1 j27 D9 x- V. O. Z# x- W
    3
    4 k2 `/ L) S6 J8 t) }. i- g45 c: H( G& n8 Q2 O
    5
    - I' L# {3 R) D+ T7 x' E4 ?s.str[1] # 对每个元素取[1]的操作; @$ J% o- u- x# c
    1
    & m* Y- {# a8 \6 k0    temp_15 J8 {. b/ [8 t6 F$ Y* y
    1         b
    3 P& }/ ], S- e1 F6 {: M5 Q2       NaN8 _# P) R8 K4 h1 @+ B
    3         y  x! E1 W% G' W/ _/ o
    dtype: object
    7 w# m% `& p, l$ r1$ Z! a! i. A, m- y5 R5 i5 j
    28 _1 A7 \; w& M0 O2 B9 m- q  l
    3
    " i1 L9 p( ~. z& L. W4+ o; R1 H& G$ t" [) }/ y  }2 E) l. G
    5
    % K# F- ~4 _7 r4 r/ x* us.astype('string').str[1]$ w3 ?0 x' R6 G# b2 z) Z& r) r" r
    1
    ) k# O1 [: S: J0    1# ?# L8 o4 \  E9 N7 s+ @2 _8 `
    1    '
    6 R4 p4 ?; ?& L% R. \* E6 y( v2    .
    6 F  X# J( A- U- \# A9 P* s3    y$ s4 A: v/ d8 r7 J) w
    dtype: string
    - r2 I8 m( E! Z1 u! ?1 b1
    8 z1 A; t7 g9 v& B+ X2
    " I% Y. Y4 q5 v2 w* V3 D3
    ! r1 P: H& U: \. i2 x4( Y, i1 N6 e3 ?
    5
    3 L* C  W6 y, n) x8 Z0 X, m, j除了最后一个字符串元素,前三个元素返回的值都不同,其原因在于:; ]4 V* g+ D1 H+ Q6 r  P3 z& ^
    & c$ L: K2 H, c" I* k" o& @) v
    当序列类型为 object 时,是对于每一个元素进行 [] 索引,因此对于字典而言,返回temp_1字符串,对于列表则返回第二个值,而第三个为不可迭代对象,返回缺失值,第四个是对字符串进行 [] 索引。
    - j+ [7 r) {2 Q7 Mstring 类型的 str 对象先把整个元素转为字面意义的字符串,例如对于列表而言,第一个元素即 “{”,而对于最后一个字符串元素而言,恰好转化前后的表示方法一致,因此结果和 object 类型一致。8 H+ G+ B5 ^4 z$ U3 C  |6 z
    string 类型是 Nullable 类型,但 object 不是$ h$ f, A9 v8 J" E1 J7 j, k" p
      这意味着 string 类型的序列,如果调用的 str 方法返回值为整数 Series 和布尔 Series 时,其分别对应的 dtype 是 Int 和 boolean 的 Nullable 类型,而 object 类型则会分别返回 int/float 和 bool/object ,不过这取决于缺失值的存在与否。
    & K: g; j0 g% K  同时,字符串的比较操作,也具有相似的特性, string 返回 Nullable 类型,但 object 不会。
    / t. f. Y$ V4 ^: o$ L, ds = pd.Series(['a'])$ Q; ]; \1 \; V9 s( V! W
    % l/ w9 y, t* j: `0 \1 G& J$ A
    s.str.len()
    & Y6 }  I7 E( C% SOut[17]: 0 o4 S) ~" Z3 n4 y& R
    0    1
    % T! f- \; F( l' q- L+ tdtype: int645 `. y9 ^5 w& W2 C& ~( Z, y

    6 q$ {/ Q, A8 C; I- c& Gs.astype('string').str.len()% m- c' O- R, _/ @4 x
    Out[18]:   z" w2 w% `8 H  Z& e5 L+ k/ L
    0    16 ~1 n7 ?/ A: r) H$ b
    dtype: Int648 F2 g9 J2 m1 _7 r, t7 a) x0 r
    5 S, w, C; I1 d7 i/ n' Y6 `
    s == 'a'
    4 B1 q7 d* H% @3 ^% x# f& kOut[19]: 9 \! M9 C0 M" S4 @: b9 J7 k
    0    True
    ! x+ x1 Q1 ~3 q; M) Z2 d) Qdtype: bool5 L, y2 s; S2 J( U( X
    " F& L1 k! j/ t" y
    s.astype('string') == 'a'  b7 ?1 R+ E2 ~# o9 ~
    Out[20]:
    ' U6 x8 q) {3 W& t0    True
    6 S' M, X; V* S2 W; a2 Q9 [/ @dtype: boolean1 \. V; r9 {+ R* Q" |& t
    + \  q* T3 u* O2 n+ w: K6 g
    s = pd.Series(['a', np.nan]) # 带有缺失值3 J& _6 P) R# c) E  v# z' A! Z
    # {* Z. T+ H* N9 F& l
    s.str.len()- m5 J" D% `/ D
    Out[22]:
    " a0 i# A9 C8 I  R2 K0    1.0" F$ R( M" l# i( f2 |( k
    1    NaN
    0 k1 x2 J8 F$ sdtype: float64
    ! ^- U5 G" z  v0 N. g4 q6 `! W
    7 J4 V0 v( o& r% vs.astype('string').str.len()4 m+ a) o* j/ o
    Out[23]: ( F/ f  s9 v; \% H0 }- [
    0       1  E" x; m! T8 T) f3 z1 ^
    1    <NA>
      u- B- `2 n$ \dtype: Int64
    " A. @& |; r- \# e0 S5 u
    ) O9 ~9 m6 j) Y# \2 t  es == 'a'
    ( k1 G+ x/ M; W. DOut[24]: # @2 e2 x# q3 k4 \
    0     True: x, w4 u) h& u, t- {/ k) g% A
    1    False+ F$ y* n, D7 Q# E/ t: `4 M
    dtype: bool/ i, m* y: ^' a. u9 S; d! u
    9 _! y+ D+ L$ n( l' c/ t
    s.astype('string') == 'a'
    ! \5 f- t" k+ L* I( E2 E' iOut[25]:
    0 p5 {1 w/ w1 k% F: s7 W0    True
    4 F1 ^9 R, o" U6 j4 m' I, [2 U1    <NA>
    & j; ]  e9 k. j7 q$ v, b2 Udtype: boolean
    2 S2 {2 `+ t1 `6 F) U1 w2 Q+ X, B; s+ m
    1
    1 h0 k1 G) {# q, ]4 C29 b! X4 I: x9 J" C4 ?# Y  T! ^
    3
    7 {& }: W5 C- s49 D' E6 [0 t; B4 |
    5
    * E7 ?, V( n% I2 Z6
    ! S# q5 |5 O9 F8 n7
    2 F+ U# ^; L; ~! l1 b2 g& p8
    9 @# R9 n# i# {7 i: v' X9
    ' b. {2 y0 B/ s8 T. q10
    ' E8 L# w5 U( Q  h8 i/ t( k' `112 ^$ _0 b. g" ^( E# j" |) H# R. i
    125 F2 f1 m3 D6 U% U$ h' [& q4 |
    13
    * S4 r. L) O& J14( [& J' N/ ^& R) n7 }# E
    15
    3 ^: {, V9 ]6 L1 v167 Q; D) @% m" H0 O5 a4 r9 W
    17
    + y6 |. A, M# W/ ^18
    ) C: `) J+ E' q& g19; X! R. v3 B, A" w( G
    20
      B9 w2 D# _2 f5 |. [21
    5 n7 T8 M8 W" r: s22* h2 H& g3 L' F3 e! J. O
    23
    4 G0 ]' ~. P% a( ^- f24
    " m) ~- J. _! N# N# o& e25
    - u8 x1 x5 g8 S3 M0 S1 M$ t5 K265 ~6 Y: i/ o! ^0 ^. c: W
    27; l( J- A! t3 N! O4 u
    28  d6 ~; m# G2 [* f
    29
    7 x- s% |' r  m! j* J30
    7 ?( f4 |1 t7 O, Q2 M. i6 A31
    . u, ~% ~. P0 R" D32
    $ ^  n0 Q% p1 l9 c$ L33% c- K) J2 G, q& |, c& c+ ?+ f9 m
    34' P! o# l! I* e/ j
    35
    - l& c6 l/ _& [& U( f& Y364 Z. b# e7 Z; p/ r' I
    37. S, R( k2 o9 z2 i
    38& |0 ~5 C, q; E- O) q/ k3 S
    398 j. P9 H% F* W2 M& h' T
    40
    ! j* }' W* h3 j% t1 M  L415 a( p- @4 p! [7 k4 c) Q/ `% _% W
    425 n6 X" _5 B3 ]* r0 y
    436 p- y. \' U% Y3 l
    44
    + h0 T8 n3 P! w' s8 v% |8 V5 \45- J! m, [) b5 I, \
    46
    ! T# O( |* ~2 Q0 k47
    4 J- w4 x6 l/ S  对于全体元素为数值类型的序列,即使其类型为 object 或者 category 也不允许直接使用 str 属性。如果需要把数字当成 string 类型处理,可以使用 astype 强制转换为 string 类型的 Series :7 Z" d2 m% e5 w2 t

    $ h+ [9 B; r) R# q9 us = pd.Series([12, 345, 6789])
    2 @3 W( c' p3 }
    9 w) T' ^8 i, a9 Ks.astype('string').str[1]. y: |, _4 S1 s& h! K/ w1 k+ ]& M7 u6 q
    Out[27]:
    ' L: e* d( l% J. a/ g  }7 _. ]2 j1 }0    2  E$ A9 O9 X; C
    1    4" Q4 ]/ a4 X/ v
    2    71 j6 _. c, [, b" e; }6 Q
    dtype: string
    8 L9 j/ U" K: m) F- K+ R14 u2 w1 b% P* a. A  X. h& z( o
    2
    6 X9 J! ~  u! o; T7 j3" L( q  E/ `$ _8 z5 t
    4
    ) I1 G* R3 a& n0 [, ^5
    # `9 p! ?: s- C/ k6
    . G6 W0 v3 Q; \4 Z7
    + O0 W0 i9 F, N0 V8
    7 Z: j1 _8 l5 ?' {. {: |4 ^$ m8.2 正则表达式基础, ?. A* w0 @0 Z( {4 ~; R
    这一节的两个表格来自于 learn-regex-zh 这个关于正则表达式项目,其使用 MIT 开源许可协议。这里只是介绍正则表达式的基本用法,需要系统学习的读者可参考《Python3 正则表达式》,或者《 正则表达式必知必会 》这本书( T- A  b4 b& E7 `4 q7 |% }

    8 A9 r" ?+ ^( C: I# X* R8.2.1 . 一般字符的匹配
    ) D+ r* s5 k. m" B/ R6 v) @! F0 [正则表达式是一种按照某种正则模式,从左到右匹配字符串中内容的一种工具。对于一般的字符而言,它可以找到其所在的位置,这里为了演示便利,使用了 python 中 re 模块的 findall 函数来匹配所有出现过但不重叠的模式,第一个参数是正则表达式,第二个参数是待匹配的字符串。例如,在下面的字符串中找出 apple :
    7 a, i  Z5 e) f) b$ ^0 n1 F9 k! A. d6 v, i+ t5 f
    import re
    8 x6 v) r! \" C0 x/ s0 P# O7 x4 S( a; _7 o3 g/ Y# m
    re.findall(r'Apple', 'Apple! This Is an Apple!') # 字符串从左到右依次匹配
    9 V$ U$ q7 R" g/ C8 x4 h# j9 @Out[29]: ['Apple', 'Apple']
    # O0 o: _3 L1 L6 O' k" b1
    3 ~. R6 W, C/ T; l, M3 `3 s: M! K2; M, ^7 P4 w$ z. a& {; I' B3 l7 C
    31 X; d- e7 w% G& r1 j
    4) ~4 k$ W9 l4 R3 M
    8.2.2 元字符基础
    " l3 b/ H* l& S5 A2 @$ U: ]+ w6 j元字符        描述+ e% ^8 G/ d% @: a9 p, |
    .        匹配除换行符以外的任意字符
    ) e8 U6 g: j* z2 T. B3 k[ ]        字符类,匹配方括号中包含的任意字符/ Y% {. K0 [" l- A" i' s
    [^ ]        否定字符类,匹配方括号中不包含的任意字符
    # E, B, K5 H# ?' x3 K9 [; @  ~*        匹配前面的子表达式零次或多次
    * i* _0 c1 B% r5 G+        匹配前面的子表达式一次或多次。比如r’d+'就是匹配数字串,r’d’就是匹配单个数字
    7 ~4 K# z# ~4 h' u$ T! v" A?        匹配前面的子表达式零次或一次,非贪婪方式: s" Q$ z' _# H& P1 x& S) F
    {n,m}        花括号,匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式% X4 n* Z  x- k9 P9 `
    (xyz)        字符组,按照确切的顺序匹配字符xyz
    / p; e' x: x2 P|        分支结构,匹配符号之前的字符或后面的字符, m' M' A. l$ r7 M8 J# d# \9 |' A1 W
    \        转义符,它可以还原元字符原来的含义
    % b% G3 r5 p" B) s. s+ G( x# F/ Q^        匹配行的开始
    ) Z, t) r" @0 J$        匹配行的结束5 @% U5 L2 d: f+ G
    import re
    & Z9 w9 Q) V; H, F1 N! `! x) O: ^re.findall(r'.', 'abc')
    : q3 ?( y$ c5 C/ IOut[30]: ['a', 'b', 'c']6 O, m/ e/ E0 P: _9 Y
    1 p& |& R' u  ?' K& V8 b% l
    re.findall(r'[ac]', 'abc') # []中有的子串都匹配5 p1 B2 F. y* m( |' v7 i& r! h
    Out[31]: ['a', 'c']* X7 L0 s0 ?. }

    ( I2 v$ _9 h2 }( n6 ore.findall(r'[^ac]', 'abc')
    . Z) M% H7 w" o0 k7 {8 e* a" x- W6 BOut[32]: ['b']2 j6 V& {7 f2 v- J4 q" I

    - {% n& W, Q3 @re.findall(r'[ab]{2}', 'aaaabbbb') # {n}指匹配n次
    . C3 y; I0 X0 P! C; A1 tOut[33]: ['aa', 'aa', 'bb', 'bb']) f, i3 Z  l5 @/ |, D
    ' L2 J# `( s2 V+ Y- h" R; s% d
    re.findall(r'aaa|bbc|ca', 'aacabbcbbc') # 匹配前面的或者后面的字符串
    ( ~" N3 n0 i  e  K5 W/ I9 ^0 @* HOut[34]: ['ca', 'bbc', 'bbc']6 o9 J5 z9 H/ X
    9 v: ~! x0 h' s
    # 上面的元字符都有特殊含义,要匹配其本来的意思就得用\进行转义。
    7 d! [/ U6 h- @. p"""
    4 Y: }- q9 U2 b8 Z6 o5 o1. ?匹配的是前一个字符,即被转义的\,所以|前面的内容就是匹配a\或者a,但是结果里面没有a\,相当于只能匹配a。# e" ?$ c3 `# P( N& q) q, _
    2. |右边是a\*,转义之后匹配a*,对于竖线而言左边优先级高于右边& t) j% W' d* U9 G" j: B) @
    3. 然后看目标字符串aa?a*a,第一个a匹配左边,第二个a匹配左边,第三个a虽然后面有*,1 N# j7 N* B1 O9 D
    但是左边优先级高, 还是匹配左边,剩下一个a还是左边,所以结果是四个a
    8 E/ M& S+ u! D+ ]"""
    7 P  z/ j  B' j; T4 g0 {, r8 Y6 t$ c, [5 C
    re.findall(r'a\\?|a\*', 'aa?a*a')   # 第二次先匹配到a,就不会匹配a?。a*同理。& Q0 p8 m) t2 ~
    Out[35]: ['a', 'a', 'a', 'a']
    " l7 C5 F7 B) Y$ z2 c
    & }- W6 j" A+ u; b8 k# \6 i# 这里匹配不到是因为目标串'aa\a*a'中,\a是python的转义字符(\a\b\t\n等),所以匹配不到。5 C& L2 U9 X* U& C
    # 如果是'aa\s*a'之内非python的转义字符,或者'aa\\s*a',或者r'aa\\s*a'就可以匹配到\字符。/ p- W! N0 x5 w+ J( G/ z. q
    re.findall(r'\\', 'aa\a*a') 3 |4 ?6 g. z; B9 k" J0 Q
    []/ q3 ]3 U# K  V* N

    + q$ a% Z# E9 L+ T7 D2 R1 T# Sre.findall(r'a?.', 'abaacadaae')
    . j; s1 o' C7 `" M4 t0 \$ kOut[36]: ['ab', 'aa', 'c', 'ad', 'aa', 'e']+ V) H: E1 L8 E+ `) |: h' J
    % v) u4 o3 r+ d2 S  A* I4 W$ p  A
    re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10') # 多个匹配模式,返回元组列表
    2 X: A$ [) s, X& ]' `[('width', '20'), ('height', '10')]7 T1 Y% Q( ^" M" u2 @- b0 F
    6 S% f1 h' \1 }0 t; n
    1
    , n6 \* Y! T1 f4 ^. O1 g: X; W2
    . J7 m5 \% C; o1 z9 G37 S$ B8 a3 r9 V2 e
    40 i  T* Q+ x7 ^9 s
    5. ], `" A5 I$ s  H3 f7 G
    60 I+ |0 p! Y# b' ^- p; O
    7
    1 U, X- C  c1 `! ~2 c& @* `8
    ; p$ b: m) n. z' Z, A9
    / r& ]/ C" B1 Z" V& D10& ?( o% y" n" _5 E. ^* l4 Z
    11+ `5 N. a8 e3 R3 M" N; S
    12
    ! d3 g8 v3 V1 n; L* n13$ O1 Y  q: i8 I
    14
    4 F2 ?, x! \( p% d8 }/ w, \15
    + ^: `7 {0 s* p8 u0 z$ {16
    5 [1 Q" F: \* G; ?17& t1 x( ^7 w7 E9 r. Y
    18. L' e/ y- Z6 L9 S) v; Y% Y6 l* f" w
    19
    * l- O, L! e- U0 `" D6 v* d, {- f20+ ]4 l0 N0 a) j. q% |
    21
    8 U) H: L  s, P+ R22
    7 ~0 a1 a1 {2 i6 ^2 |7 n23  d6 l) b' o6 v2 C  b6 w3 p/ x: V' n; h
    24. L1 }' M8 h3 F& C1 c! C; a
    25: {" H4 o# L8 k8 b, W  Q
    26
    5 Y" t3 w$ ^6 p1 U* H8 c7 u270 q" `. E, f3 r: O' b: r
    28
    ) e" P# g: l1 P" I# c" a( u29* D2 c: [- n' M0 ]$ n8 Z. ]8 }
    302 j6 d' R; g& k/ p1 u* f2 F* y) n
    313 F; B% L3 w7 x2 Y. |! {
    32
    / y/ b6 T0 d6 T4 `33
    5 Q; j. Y$ @% F3 h2 w34
    8 T9 ~2 l+ O- Y" \35
    # O2 t3 f- j1 q36
    ' }: {/ k$ K$ Y% r' N4 M9 c378 ^! A$ M# J& b- X
    8.2.3 简写字符集
    0 U( v& A! j& N4 X/ m* u" \# G则表达式中还有一类简写字符集,其等价于一组字符的集合:
    # I2 Q  c) B) x4 _! T7 d9 C" S6 N- v5 s! W: n! O3 Q2 T/ y
    简写        描述( [! G; C: O: f- i. L+ ]- [/ w
    \w        匹配所有字母、数字、下划线: [a-zA-Z0-9_]
    , w: _  P: {( H/ w3 _8 f\W        匹配非字母和数字的字符: [^\w]
    7 b8 O; W7 h) h% p\d        匹配数字: [0-9]
    3 M' r1 w+ M; A( x5 `\D        匹配非数字: [^\d]  Z3 H2 @3 k0 Q9 p: [6 Y8 j1 k8 a
    \s        匹配空格符: [\t\n\f\r\p{Z}]* L8 S' J! J! r! C% X
    \S        匹配非空格符: [^\s]
    / @& E1 b3 c& }\B        匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。- Y# x  K# t, b" i3 f0 K
    re.findall(r'.s', 'Apple! This Is an Apple!'). P  i9 g9 y' m  ?9 e5 D+ y2 f
    Out[37]: ['is', 'Is']
    7 G  w" B" j  h# j
    ' X( T* G" ]$ y* U9 q8 Jre.findall(r'\w{2}', '09 8? 7w c_ 9q p@') # 匹配任意数字字母下划线的组合,但必须是两次
    8 _7 k5 h$ j1 U# U  u2 R# }Out[38]: ['09', '7w', 'c_', '9q']+ @* n( I$ W  ~3 \! I- P2 K, Z& a
    ; c4 ]* y* u. U
    re.findall(r'\w\W\B', '09 8? 7w c_ 9q p@') # 匹配的是两个字符串,前一个是任意数字字母下划线(\W),后一个不是(\W)
    5 o+ i% I. j! M/ t8 F! }Out[39]: ['8?', 'p@']/ b$ l' X* P6 D  v0 C' T8 o
    ! j9 \1 T' `; n
    re.findall(r'.\s.', 'Constant dropping wears the stone.')
    # y  y+ G: u# S# ?/ }4 aOut[40]: ['t d', 'g w', 's t', 'e s']
    - y6 B$ R; `9 n5 G9 v" ?, F* S
    0 ?# ?+ Q$ Y0 |re.findall(r'上海市(.{2,3}区)(.{2,3}路)(\d+号)',3 u! T# z8 V- W* t' G  X
               '上海市黄浦区方浜中路249号 上海市宝山区密山路5号')
    3 d/ b7 E8 P% H! \# ~6 z) W  b5 _  J9 U( q4 {$ k) p$ n8 g
    Out[41]: [('黄浦区', '方浜中路', '249号'), ('宝山区', '密山路', '5号')]
      D& R+ o7 B( N
    % U& V- `5 s% p9 t3 r1- c' Y& Q! _2 F( h5 D, _
    2& j7 B: o" N4 W- ^" S
    3
    ( T4 n  ~' r7 P: ~  Z# l6 K% {4
    1 P; x3 P0 _' v4 D$ v# @5
    8 T! G1 }* _: C4 b" b1 R6 U6% N8 e; g) I9 l% @* ]
    7
    2 l/ h  y# }1 e% t8
    ) z! L4 F5 d+ z! [3 s/ a% ?9
    6 f. ~* J% F* r2 f' H10! f! ^7 m1 [" S
    11
      u& q5 N: N( `6 i) t2 d2 v12
    3 N' R: [& n) s) U) N9 e* l13
    2 U0 `& P6 U% W, j: `14
    3 O  x# Z! ?* j' S7 B5 q. h15/ D& Q7 L/ ]1 [2 y3 [4 p
    16
    7 b9 O0 u) c6 b5 h/ \( N" F8.3 文本处理的五类操作
    7 ~( q5 q/ a, W) ]% h- T8.3.1 str.split 拆分, b  n$ u4 B. d* ?1 \* Q: C
      str.split 能够把字符串的列进行拆分,其中第一个参数为正则表达式,可选参数包括从左到右的最大拆分次数 n ,是否展开为多个列 expand 。
    / W" J9 _" y# r$ F9 k2 U: E3 j, w$ j" w; q# z/ {
    s = pd.Series(['上海市黄浦区方浜中路249号',
    4 N6 ~1 |0 z* Y8 K            '上海市宝山区密山路5号']), o( ~& L; B9 ?" U3 |& J: m
    - j7 J8 l2 Q/ I4 y& Y( h4 w
    % `5 r- ~8 b7 ^- J
    s.str.split('[市区路]') # 每条结果为一行,相当于Series, h/ m% B. \( U. E9 [
    Out[43]:
    + T! E  n1 ~  a' X* C0    [上海, 黄浦, 方浜中, 249号]( e( J; y$ ], A! ?& l9 A+ ~5 }
    1       [上海, 宝山, 密山, 5号]
    % C+ {# U2 A% X+ rdtype: object; u  y0 ~9 L5 n

    * W5 h+ z+ b+ e$ h+ n& Ks.str.split('[市区路]', n=2, expand=True) # 结果分成多个列展示,结果相当于DataFrame
    9 B. A$ S  h! ?5 L4 P" ?" _Out[44]: 4 }+ _! F# n1 u4 C4 _, \5 q, v
        0   1         22 I- j5 d* P+ l
    0  上海  黄浦  方浜中路249号
    : _- F' g8 |+ t+ D, L3 _0 T$ B1  上海  宝山     密山路5号3 l8 x* [& l% ?5 m
    1
    + O8 W7 u; l2 c  ~3 H# P0 V27 a+ a' t* F4 S  Q1 b% v
    3
    3 b; F9 k3 \8 @. O' b2 u4. J, V1 m3 ^; u9 g8 R
    5& |) f# _, g: v7 y9 N* w
    6
    . A" T. ^! p2 I8 x7
    " v/ o, w$ {! ^! i9 R8' m' G2 b" C) M- H
    9
    ! {4 D- C( \8 z5 v/ y  z5 q1 _9 X104 Y, Q" L6 K9 e6 _1 `, @
    119 A$ t0 d& Z9 s, U& P7 O
    12
    9 b! E3 e: d; |( `) N13
    9 H3 F( d. {: k0 p14' [3 p2 A' W8 _1 w/ U: D
    15# E0 Z5 ~/ |  u, {. D3 |) \
      类似的函数是 str.rsplit ,其区别在于使用 n 参数的时候是从右到左限制最大拆分次数。但是当前版本下 rsplit 因为 bug 而无法使用正则表达式进行分割:
      ^4 l; P: }+ h# w# @$ {" ?' |2 S; P' c4 f0 [
    s.str.rsplit('[市区路]', n=2, expand=True)8 M  _* P0 w& ?: ~
    Out[45]: . h) c! s% D( G# T9 Q( g
                    0
    ; c' ]; |) M5 M$ I# X8 ~0 E0  上海市黄浦区方浜中路249号4 j1 Z. [: `/ L$ L9 S7 [, p
    1     上海市宝山区密山路5号! J$ K) i# v  w, R" ?9 i9 O
    1
    + u! n2 ]) @7 I7 J/ X/ ]0 d. p2
    / l2 r0 M: k+ k7 q3 }" B' `30 Z* G7 z' {! [
    4! B+ ?) L/ _6 |4 Y
    5
    & M1 c% h" `' t8 Y; k8.3.2 str.join 或 str.cat 合并0 ?9 o# Y) T" \& Z
    str.join 表示用某个连接符把 Series 中的字符串列表连接起来,如果列表中出现了非字符串元素则返回缺失值。
    & R, v1 q: H/ P' n, Xstr.cat 用于合并两个序列,主要参数为:5 d) U% F3 m0 j# G) u) h* ~& I
    sep:连接符、6 F. d) ~3 ]$ @$ A- N7 g* k
    join:连接形式默认为以索引为键的左连接: A' `6 \- D' i9 j/ |  @0 ^
    na_rep:缺失值替代符号$ ~0 N( G  @3 x. u
    s = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']])
      L& |! V5 i) J' G) \s.str.join('-')
    ! K: _- ^7 ]3 H; T) I& i$ t7 m/ qOut[47]:
    9 Z, S% B# r3 _# S" G, L. P0    a-b
    6 K; W! Z" h7 q' B4 ?( v1    NaN( a( N, M1 C1 |& b! a; N8 ^
    2    NaN
    2 K# X5 Z9 U0 y4 {dtype: object8 k; N4 E; x# Q# P0 ~
    1
    ! ]3 b, |) D7 c2' J3 m9 T% M, y/ G5 j
    3
    & s; o, z6 w1 U7 e$ H6 g4; ~3 G9 X6 c1 z' K  Z* |# s: t
    5
    , U4 g, y5 @4 s7 @& \: a63 E0 V( D" `5 @  n5 e
    7
    8 D# x+ Y5 A" k8 S9 F& ]; X, C& @s1 = pd.Series(['a','b'])& h& I) s0 C$ E# Z, O
    s2 = pd.Series(['cat','dog'])
    0 q7 G% V: n6 u) t2 @3 J; Ns1.str.cat(s2,sep='-')
    * g7 [7 j$ v7 h1 C! ]" eOut[50]: 3 U7 i% ?; W$ [. S* O) \" Z0 U9 z0 N
    0    a-cat
    - ]9 j. ]# I: e1    b-dog* X2 ]: O7 d1 Z1 a
    dtype: object, @+ ]6 `6 n7 h8 d7 ^/ H, M

    * V. Q, h* Q  N  ls2.index = [1, 2]& g5 Z: i+ I8 H3 H' h, `6 i
    s1.str.cat(s2, sep='-', na_rep='?', join='outer')
    - C- v; k( p) p7 z0 X; ^1 u. L  fOut[52]: 3 k, k. ^( C9 c% C& z% P
    0      a-?
    # D: k4 J1 a& w6 A3 K" ]1    b-cat
    " L9 c/ h9 }. e" w. l) D3 x9 z2    ?-dog
    : I/ w# Q. _4 v6 x. V( fdtype: object
    & Z0 _- L8 X- h1" z5 e+ t; h/ O9 k
    28 j% H# ]9 ?& x/ @# c8 ]
    3
    . B% U, v2 e  @6 L4
    5 G0 h# H6 n7 h  ~3 X4 _5& x4 W' o  P3 t1 U: `# d
    6
    4 k9 J/ G) U. L; f) O' \' z7+ u' A# b7 S( R' x5 i
    8
    + C+ p2 H0 F+ X7 F: T9
    5 |: c, ]/ f! a+ ^* T10# x1 l. k( d3 E' I3 z
    11* n' V; G9 A* {$ e% d/ H
    12. l' u8 n& N% [; t3 P
    135 U4 ^9 ^: [) u$ R2 _/ Y  z. ]
    14
    # y% N! Q0 v% o1 J% x9 J5 N$ G158 @3 v, Y/ \3 a! R
    8.3.3 匹配
    % K) P9 p" P9 m6 vstr.contains返回了每个字符串是否包含正则模式的布尔序列:
    : Q! V* p& P. M  s9 O0 L9 Gs = pd.Series(['my cat', 'he is fat', 'railway station'])# x. @! g$ ~: P# ~7 I. N, _" n
    s.str.contains('\s\wat')
    + M1 S/ X0 a- o# q1 e
    * D8 k1 B1 |* a( l4 q' `0     True  x2 g& o( H. i
    1     True+ H% r! X1 e$ c
    2    False8 r( H5 l2 B4 O3 t2 E
    dtype: bool
    7 ~5 d9 I# C: I3 M% W1+ q' u$ d% l5 `( J+ @
    2' y& W9 g& A& n! V; V
    3: a8 q# v+ ~' K! z% e" T* f* k
    4
    $ A) s7 Z4 P; X; C. G  |6 Z5
    / o: q( B! k* p6
    ( N+ b3 W% J# L, |& x% y74 M+ Z7 a( I5 }$ }
    str.startswith和str.endswith返回了每个字符串以给定模式为开始和结束的布尔序列,它们都不支持正则表达式:
      l0 F- m. P! c# \/ Hs.str.startswith('my')" U/ T* i/ [. D4 b' H, a
    $ E: c: t8 s: K7 F: N% ~( q
    0     True3 K& A6 X: O# N( W- x
    1    False0 j( ~& L3 Y1 W; L
    2    False
    0 u4 _8 z( h( F2 [* x2 ydtype: bool
    & Q" X* @5 n/ j, Q+ h  l% c5 I1
    5 k0 F- ?5 N) `; v! m8 c2
    9 m" L$ v  a# E' S3$ v. Q, T8 M7 \$ S
    4
    + \& W1 U# v8 X0 ^5. V8 e% q7 p* q$ k3 T. E6 l
    62 _: E3 _) b1 @# n
    s.str.endswith('t')
    $ r& D8 P! |- l/ a) W0 m* a- V6 }; L) f  J6 X# t" O. ?
    0     True* o  o4 Y0 ?* K* G0 \
    1     True
    " j0 s# B' y$ U% P1 N. W2    False
    ( @* o" V4 S6 G# N) [dtype: bool! A' h8 f8 S( |4 r+ a" b
    1
    2 d5 X2 }6 E4 @4 I- |' x' ?! K7 A25 M8 c8 N( g2 Q' b8 K$ @' ?
    3# ^3 R) b2 T' O9 F1 a& I
    4# a, c- ]' U+ B: r) X) H8 R
    5
    6 A. ]7 B$ ^9 H5 D( W5 F! n/ Z6! z" z, w9 y0 i' A) I+ Z
    str.match可以用正则表达式来检测开始或结束字符串的模式,其返回了每个字符串起始处是否符合给定正则模式的布尔序列。当然,这些也能通过在str.contains的正则中使用^和$来实现。(貌似没有python里的search方法)% e' k5 |' l6 v. `
    s.str.match('m|h'); }, r( [! S* q% u4 X" |
    s.str.contains('^[m|h]') # 二者等价
    / {: ]! M! m4 T; s
    ) _3 F" W/ Z; S0 G# ~0     True
      l$ j# f/ B3 @4 O- ?! [1     True
    7 P* [0 M4 Y8 R% t2    False" c1 {5 \5 e6 A3 l& `
    dtype: bool/ g; L2 _# F+ ]1 u6 |) X6 f
    1$ l) Q! J& C' U/ ~* W' l+ D
    2* z/ z& L/ I6 v( W3 F$ A4 ?
    3
    . h9 S" f0 u3 x4 y8 R4
    * r- {0 `" i% S' Z( y" `/ A58 W4 x" r% w& W. m0 T$ n
    6
    3 ^' \7 z  I7 s7
    9 T0 m! u1 v% D: v& |, u# J' C5 C8 Qs.str[::-1].str.match('ta[f|g]|n') # 反转后匹配
    ! z1 c7 H# s; m- d) t" Ws.str.contains('[f|g]at|n$')       # 二者等价( |6 z, K/ b  j+ y9 l& Z/ D

    4 F1 E( t3 e: g  c; W" w$ u0    False
    4 g. J1 J- f6 T# ]! u1 s  }1     True0 O$ o% G  H6 @1 B2 G) i# C5 s
    2     True
    5 W* C) p( S: Gdtype: bool( Y* E5 K0 J3 F/ V. `
    18 K- i# r# f7 ^9 Z
    27 f6 g+ C3 z6 d
    3/ I  e; |" O7 G( R0 r( N8 d
    4: m, D2 E7 s/ g9 E6 i" c% {1 ]
    5
    ; t3 }8 R( f/ F9 R6* K4 w' ?* M' |2 b) \
    7& f% w  B* q% }& Y
    str.find与str.rfind返回索引的匹配函数,其分别返回从左到右和从右到左第一次匹配的位置的索引,未找到则返回-1。需要注意的是这两个函数不支持正则匹配,只能用于字符子串的匹配:
    $ D% j( J% ~9 c1 a6 V0 P/ Ls = pd.Series(['This is an apple. That is not an apple.'])
    2 C. [0 G" t9 r% g6 B$ m; j; p: T  n+ F; T* Y0 y' T7 T* E
    s.str.find('apple')5 c- ?2 p; d% F' ?; M6 ?* o
    Out[62]:
    ( Q* l9 N" f: i, g9 |: J. e0    11
    + a: @% W4 N' p1 F: [- a! {dtype: int644 F: [. y& T7 ^. x2 \

    3 r# ^. e+ ]9 Hs.str.rfind('apple'). i/ }+ X! B1 z
    Out[63]: . Y& S# n$ Z7 P) s% I5 O+ P
    0    33
    : w( P5 \8 X, t0 J6 e; L4 Odtype: int64! \, F" x3 T! z0 u$ {" ]
    1  F; q& @* {8 i3 c" h
    2
    # K$ I. Z$ y, g' `% S- V" f3
    4 |5 [9 [1 q1 S49 I0 T. B- f" E% F: r# W$ E! B
    5
    + O5 s. C( a$ X# p5 V6 o; V6
    ( Z9 n/ f1 \7 S* a4 O7
    $ n1 G7 w! k  ~4 ]' N8
    4 Z( y& B0 X5 \* q8 C: F. y9
    - ?" C6 V  G1 b6 F, L8 N8 {, m; Q: y10: }+ b, R( y* A0 s+ ~7 j
    110 v% _1 k; b' o
    替换5 N* Q2 [  P& C5 O( {. }( T
    str.replace和replace并不是一个函数,在使用字符串替换时应当使用前者。) w7 O) [. m0 J! w& o  X8 a3 r
    s = pd.Series(['a_1_b','c_?'])6 h: c' o! D$ A0 c5 a
    # regex默认为True,表示是正则模式,否则第一个参数内容表示是单纯的字符串,也就是匹配字符串\d|\?
    ; A5 m+ S( R: t3 e# j5 Vs.str.replace('\d|\?', 'new', regex=True)
    & [0 g  G+ t' V4 i3 B  o5 z! v
    " U0 A' V( p- G. t* a0    a_new_b
    $ B, Y% @7 I7 x6 {1      c_new+ v, ~2 k5 J  S$ k* @' f
    dtype: object: U; q6 h  D! T4 O# j! M! F& L
    1# {- ?1 F# R  @$ q# m; b/ W4 s
    2
    $ Z  P; D. E& |+ j7 H, R) s3/ f. V7 _9 Q9 `1 O+ G
    4
    3 H5 H+ U4 t2 q  w( \) I8 C! q5
    , J- t' m' C: J6 w/ q) }% P6
    / l0 O* B# y+ r1 |& m72 r  o9 ?6 I/ _" D2 c( Z5 k
      当需要对不同部分进行有差别的替换时,可以利用子组的方法,并且此时可以通过传入自定义的替换函数来分别进行处理,注意group(k)代表匹配到的第k个子组(圆括号之间的内容):
    ' [/ T, H9 Q  i0 c/ _& }) J0 G0 s) h: y- N* a6 ?" g
    s = pd.Series(['上海市黄浦区方浜中路249号',# D2 O% {# _: c/ M' P+ [
                    '上海市宝山区密山路5号',* [! L2 d: i# V( \5 Q7 `
                    '北京市昌平区北农路2号'])
    3 @2 `: N. t& }6 V* w% tpat = '(\w+市)(\w+区)(\w+路)(\d+号)'4 Q' `( u9 ~; s' p# Y- R: w
    city = {'上海市': 'Shanghai', '北京市': 'Beijing'}0 n7 i3 t9 |; V# c4 A7 u5 ~. j
    district = {'昌平区': 'CP District',
    " W3 |: G$ N+ @, L& D            '黄浦区': 'HP District',
    # f- H" j3 d1 L            '宝山区': 'BS District'}
    / l5 Y! L1 N- Eroad = {'方浜中路': 'Mid Fangbin Road',
    ' K7 g" j1 {/ `& i9 n# y# d4 A& K        '密山路': 'Mishan Road',
    % c' O7 H, E- y- p0 W        '北农路': 'Beinong Road'}. E) ~! ^) L* s+ d% z
    def my_func(m):
    - A6 d9 q- K* P$ k# f    str_city = city[m.group(1)]
    / q$ @/ D) R6 ^" q* w    str_district = district[m.group(2)]+ I  z! J6 a( b5 J8 t! |8 _3 E
        str_road = road[m.group(3)]! |9 H  h' n9 l+ y+ Q" Y4 a, T& G9 |
        str_no = 'No. ' + m.group(4)[:-1]
    ; O2 i2 d6 `& c+ ?    return ' '.join([str_city,
    " H* l, W2 l& R3 o, H                     str_district,
    / M) k3 f7 P6 I  A                     str_road,# y6 f2 K* L  u. |. i" n1 a- G  t
                         str_no])0 i; s% r% M* k$ [& f
    s.str.replace(pat, my_func, regex=True)4 }' K5 F9 w1 b1 W$ @! s

    5 _3 q7 j2 v( d% {1
    0 N3 i/ V& E/ D" j* |2
    & f7 @3 X, ~2 _9 s' \1 g# S2 ]4 r3  o' U$ v: U* w7 t: d3 O
    4
    6 V$ t8 U& _" v3 Z9 x: |5
    * O) {2 }* M0 j. u7 ^% z$ o6
    ( O; z* D2 d$ b, g* {7- Q. _' l& C% u/ I5 N  S3 {4 V- B2 G
    8
    4 T. G6 s& Q/ i6 n9
    1 n- J3 C* B) z103 y$ ~' d' g2 H: g
    11/ t4 F: g* l5 F/ q2 \  B
    12
    9 ]( G/ |8 u7 q) m& O1 m% E' h* i13
    8 @9 P5 m& R6 L, Y14
    . ^! E; Z+ ^1 B# @8 i, ^' N! {15, h+ M* C9 x7 E
    16+ {! k+ n. U" h, P/ r
    17
    ' z6 \3 P/ f4 H1 i18, B' i/ X4 c0 M
    19
    # N0 k1 M9 B1 V9 Y. n/ h20
    . H; N8 V. A. J0 h219 t; k! G4 G& |
    0    Shanghai HP District Mid Fangbin Road No. 249
    ' Q3 P# q# S4 q* ^0 w$ Z) [1           Shanghai BS District Mishan Road No. 5% g: U1 p% S% F. }4 B
    2           Beijing CP District Beinong Road No. 2
    ) j( W# x( y9 f. C' ]0 [8 Tdtype: object
    0 N/ F( S$ T* l4 ?, v- F19 p) e& N# p( Q# @' i  ]
    2
    . c( l4 _- H2 x/ U8 n) ~" r35 f! G7 B$ P* s, [
    4: P; S. B2 M/ ^) L) ]! w0 w
    这里的数字标识并不直观,可以使用命名子组更加清晰地写出子组代表的含义:5 u/ u& d5 j1 q) {# b- v5 ]
    * W3 Y, ^4 b" E  G" g# @, U
    # 将各个子组进行命名: S- ^+ f) ~1 ^  A8 V5 m
    pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'
    ! A8 K( E& x- u; N1 Vdef my_func(m):
    7 g; Y$ o) }' X/ i    str_city = city[m.group('市名')]/ O  S# x, P* C/ Q7 O4 w6 Q
        str_district = district[m.group('区名')]
    , b4 U! z+ J& q) R1 ^7 C& u    str_road = road[m.group('路名')]
    / [) J0 K. [1 P  r9 ?* P) N' v    str_no = 'No. ' + m.group('编号')[:-1]
    5 _7 Q$ D/ N& _, P  m- V$ U; L    return ' '.join([str_city,& ~* x- e; z9 D, B) B6 [5 Q. L
                         str_district,/ c+ G5 A6 P3 w
                         str_road,
    / ^6 a0 a5 f, s9 [2 J1 g0 y! d5 L1 @                     str_no])
    ; S$ O% _! q# Ks.str.replace(pat, my_func, regex=True)
    ' J- d6 k5 Y) X1
    ! d0 B! d* k& T. ?6 c; v2
    9 F, f9 z' `/ _9 j31 V+ T& q+ f, c* a8 G* L. g
    4
    + C! ^' j2 ]3 |1 F$ T% l; [5
    6 Y) @- F, L- F  Q; T; a6  V$ j+ p2 t; R
    7& X, C3 k$ l0 |; x2 Y2 S
    8% [* d# N0 p: h
    9
    % g' T: [0 D3 U$ f% P  c10. `1 k! i! G2 v4 \: n% Z+ D% m2 ]: D
    111 B! \* r  b2 S; {
    12
    - K1 Y* \; _' d' N& l0    Shanghai HP District Mid Fangbin Road No. 249( O6 U# u$ z+ c5 U! X4 y, }2 i# o- L
    1           Shanghai BS District Mishan Road No. 5& B; S  ~' h3 X2 [) h
    2           Beijing CP District Beinong Road No. 2; x4 @# b; a8 u: O
    dtype: object
    + U$ D& K7 I6 k1
    ' o! N$ |. E  I" S+ Q0 h: u. Q21 m' \3 N& @6 C
    3
    ; \/ B+ b& q4 ?- _6 O5 E" P49 ?3 T9 k/ f4 {* V# X& f
      这里虽然看起来有些繁杂,但是实际数据处理中对应的替换,一般都会通过代码来获取数据从而构造字典映射,在具体写法上会简洁的多。2 ]: M! g5 T2 l; i4 H' j$ w& J3 z2 \

      G  a( _  N# F8.3.5 提取8 l' J% I& `/ n  E
    str.extract进行提取:提取既可以认为是一种返回具体元素值(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的str.split例子中会把分隔符去除,这并不是用户想要的效果,这时候就可以用str.extract进行提取:) f/ w' v! ^8 k9 k0 ~
    s.str.split('[市区路]'), l9 A' P* @$ B; K  d, ~
    Out[43]:
    + s! c9 g6 c7 M* k7 d0    [上海, 黄浦, 方浜中, 249号]
    9 k$ J& c! O# D/ o/ g8 j1       [上海, 宝山, 密山, 5号]
    " R5 A8 ], Z3 Jdtype: object' ^, w6 O, ?3 W; G8 p. |
    : s& L' k9 X. F0 ~5 N
    pat = '(\w+市)(\w+区)(\w+路)(\d+号)'
    " r" R7 L% w/ l. K2 Cs.str.extract(pat)
    $ y# P! \4 \  [6 Q; `2 YOut[78]:. _8 o$ Z9 Y: b
        0    1     2     3) u0 F$ G$ q7 V3 H+ S8 U
    0  上海市  黄浦区  方浜中路  249号: p) v# m5 q( V. _- a, i# L0 J
    1  上海市  宝山区   密山路    5号
    . X8 _- T7 r6 C2  北京市  昌平区   北农路    2号
    4 H- }5 H! `3 X  ~5 d1
      x0 `' Z9 w3 ?  M22 s8 R5 O$ @, b, j' [
    3
    $ K; f0 t3 t7 ^, m4 ~7 Q1 C  t9 N  F4
    / M8 s: M/ u+ h; t3 P0 O( ]: P5
    % T, r* {. @6 R, F/ n" F' g68 c1 v; w' r- x0 @$ M) a% j5 d, ?
    7
    0 _6 o0 c/ q4 O: c/ V8
    & O# E; ?2 Z% A9
    $ H$ G3 a5 P$ X3 J/ w10
    " X- Q) U' Z+ Y2 G3 \" a) ?: s! B11
    4 b6 T- N+ C. {" w. C12
    8 O! ~! O4 w" J7 X  @$ g- _2 M13$ X  }/ p& y8 L* w& x; e; M8 j
    通过子组的命名,可以直接对新生成DataFrame的列命名:
    9 ?0 M( N# y( F3 o9 r
    - e% N. e9 G3 f  Epat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'7 L$ V8 E3 T0 ?9 L! |8 k7 }1 s
    s.str.extract(pat)* k, j2 Y, P' Q' H3 K" g
    Out[79]:
    & ^# x. h4 b; d& ~' k) U) X# f: W    市名   区名    路名    编号
    ; O. z, ]' p# M' k1 t) z* ^- _0  上海市  黄浦区  方浜中路  249号
    + j9 E: ?0 D9 Y5 }2 ?* l, `1  上海市  宝山区   密山路    5号: q/ }- s$ U6 p
    2  北京市  昌平区   北农路    2号7 E- g  W) Z! M( B! }% j' {9 u0 D
    1/ u# l7 d& o/ b! L) h: x9 c% F
    2* M% q; A5 `% c* ~6 c: Q7 Y/ q
    3
    9 g# Y/ q- q  g7 w4
    & ~1 k1 M1 ~, }5
    & C, I& a  s) z2 w( p- S6$ I2 y$ L6 O" e6 ?6 b$ [+ o
    7/ p8 \; d% _! i5 p# Y& m0 M) X
    str.extractall:不同于str.extract只匹配一次,它会把所有符合条件的模式全部匹配出来,如果存在多个结果,则以多级索引的方式存储:
    1 p8 K/ y4 w6 `. m: N" X8 ys = pd.Series(['A135T15,A26S5','B674S2,B25T6'], index = ['my_A','my_B'])
    6 G3 k7 r) o% z4 Hpat = '[A|B](\d+)[T|S](\d+)'
    & K0 M3 |2 z6 }+ B  }s.str.extractall(pat)
    6 M- L: E6 Y2 |. fOut[83]:
    $ n# P/ T0 S6 M& \3 b3 C) f       0   1
    ( K0 c6 A( W9 D( D# G+ ]( g' S* O# a     match         
    " S% K/ E+ x2 p) y1 F3 A' Emy_A 0      135  154 M5 a" L! b; l+ |5 v" c
         1       26   5* P- K; t; b& {( {  |5 v
    my_B 0      674   2- x8 X- F" \* o( k& l
         1       25   6$ k$ k: I3 X$ R- R
    1& z8 z. i, U* @' f* e, J  c8 n
    2" P( T" b  ?/ @8 j; N1 R8 S
    3$ g8 U. R- d/ p, [0 p! V5 a& l
    4
    9 E+ ]5 t, V, _+ s5, K& A6 }! J7 l2 i
    6
    ; e  w0 f7 v8 i- n: u+ C* D7
    " X  e6 L3 q/ t8 c  p! ?84 ~' z3 I4 e* o0 k0 F! `" r: d( r
    9
    5 n" t6 \: d7 T+ d" |10. |1 S" Y8 v" j% h
    pat_with_name = '[A|B](?P<name1>\d+)[T|S](?P<name2>\d+)'
    3 k5 _; m$ g/ d( w/ Ks.str.extractall(pat_with_name)
    7 A3 B: D9 o! UOut[84]:
    2 k2 U' P" ]1 R; t           name1 name27 x; _# K( l. }+ e
         match            
    6 `) r* T6 c! g7 e$ b% X8 Y) gmy_A 0       135    151 S0 h9 W& [$ h8 {4 e+ }& ^: P; _
         1        26     5. r5 d  L% s1 D7 i8 F: F9 j2 N
    my_B 0       674     2
    7 B7 j. H. n( C     1        25     6
    0 h- V/ E6 P! H# C' k$ T1
    8 [2 v; r( @$ K# p2
    . Y$ H6 |: D* i8 K6 S39 S9 {$ |7 N! }
    4& Y: A+ ?3 n% g# p/ A0 k
    5
    2 }7 U/ [9 R. S, i8 N6
    8 r% p0 K  y( m! N7
    . G; o6 B9 _- f9 r7 z: Z8
    & P; ~& I' E+ _' [, M# \9
    % A1 z5 P* \* Estr.findall:功能类似于str.extractall,区别在于前者把结果存入列表中,而后者处理为多级索引,每个行只对应一组匹配,而不是把所有匹配组合构成列表。4 `+ V. u- q, M
    s.str.findall(pat)
    # D; Y: s& U! N1$ i+ }, [  s; D) \' b
    my_A    [(135, 15), (26, 5)]
    " v& e, f- @% X* H/ H( kmy_B     [(674, 2), (25, 6)]5 M4 i+ K% R* Y
    dtype: object
    8 L3 e. T+ X8 Y, P  Y1
    - ]0 [6 `, E! P2 B; O7 c3 Z0 W2
    % q, R: e0 _! _$ z! y0 ?+ L' p3$ h  k3 j5 ~/ j# W; [2 }
    8.4、常用字符串函数
    * B+ `1 P3 n4 u  除了上述介绍的五类字符串操作有关的函数之外,str对象上还定义了一些实用的其他方法,在此进行介绍。
    $ z/ O$ G) Y( [& W  l  p
    : h5 v) u' e  G8.4.1 字母型函数
      P! c. z' [0 @3 h  upper, lower, title, capitalize, swapcase这五个函数主要用于字母的大小写转化,从下面的例子中就容易领会其功能:, c5 ^- ^& S1 X5 F6 T4 w

    , s0 B8 S4 c$ k: c0 p1 D9 l! V% ss = pd.Series(['lower', 'CAPITALS', 'this is a sentence', 'SwApCaSe'])5 q6 T, I5 n$ \! Q1 z# H/ \5 e: N% E
    4 y+ a! P+ y* U3 K( f3 [
    s.str.upper()
    . w. N  G; a. [9 hOut[87]: ( V& z7 z( I: d7 F( C2 c
    0                 LOWER+ `  j# q: X  ?$ k/ O3 g/ ^# A
    1              CAPITALS0 F  p) v; v& X. ]3 a) s& m  e* `
    2    THIS IS A SENTENCE
    ' g8 C' r, n! @: c3              SWAPCASE3 F$ o$ H! t! m. Y  P& g
    dtype: object; f9 Z3 x$ w2 |2 z5 H
    + x) x! j: a- o$ g* @: V! `
    s.str.lower(), t1 }% a! e0 u! H
    Out[88]:
    6 u! W8 ]( D$ u" h1 W1 H0                 lower, }( t2 o$ O# n' g3 @: c2 k8 M
    1              capitals9 u* e1 @' @) k" W( t  f) ?. b  M
    2    this is a sentence
    " P4 B0 Y) H  q2 J3 F0 q* ^% J3              swapcase# H+ n; H1 B& ~- T, k; C; E
    dtype: object0 V! {" i4 f6 p2 u) ^

    # C4 S0 d- u4 H: {- O! L8 H- T! Es.str.title()  # 首字母大写# V% N- C; }9 Y
    Out[89]: . f( T9 W+ n; d" d9 v8 T
    0                 Lower  q: H% o' d2 E+ |2 m8 |
    1              Capitals
    / n# J2 f  Y, n9 @& M2    This Is A Sentence
    5 H$ ~) G) i4 o- J. f4 i2 v3              Swapcase2 H2 C+ w9 x" q" A7 }
    dtype: object- M( W, ~# y7 w0 V0 J
    . y  {* D2 e& }
    s.str.capitalize()  # 句首大写- @# q: H# ]/ n
    Out[90]:
    % A  x( ^0 |9 j0 D0                 Lower
    3 W- C5 _' B/ Q4 c4 b1              Capitals1 @& F. v% _4 D
    2    This is a sentence
    : s: U+ G: ~& L/ J/ ^" y! W3              Swapcase* X4 R+ [. S6 j2 S0 V0 d
    dtype: object, @* ], H5 M  p% p$ U5 c( F

    8 B  a' X9 B% Y/ I; n3 xs.str.swapcase() # 将大写转换为小写,将小写转换为大写。
    7 j1 Y0 c0 w/ dOut[91]: + H, g* `- K# n2 g/ }" P
    0                 LOWER
    $ L! |: I9 Y& R/ a3 X0 p1              capitals
    ' L* i9 ]$ i' n/ t3 \2    THIS IS A SENTENCE
    9 @+ c# Q$ V1 w* Y% M! d/ Q; {4 ~3              sWaPcAsE
    ; \( q3 B+ t( d7 ddtype: object6 N6 i  u& w2 S7 R* Y

    $ S, M2 [5 s6 K! Ks.str.casefold()  # 去除字符串中所有大小写区别0 l- A+ x9 O; d1 h5 k
    / b+ s6 g( h0 y) l! e9 E/ ?
    0                 lower
    ! B2 I( D" n& J% H1              capitals
    . y9 L5 V" x2 a, J2    this is a sentence! {6 _( `7 Y& ~
    3              swapcase- |( k4 E+ V5 v9 |# h
    ( a- J9 T5 g( y3 R( N8 O$ K7 R  _
    1) g6 g  l7 I. O/ R% D% z4 c
    2
    - \3 {! d# s& y  \( P6 E3; O* [6 I  w5 n, M4 U
    42 k! p+ T& v! K2 x4 ?# D: K
    5
    & o1 T. H: `; D6; s. F, H' G* z/ j: s( @& F9 x: x
    7
    ! b) @  n* m. m4 z  ?, D8  N. t% y6 |+ C" ]% `$ f
    90 m2 P5 l* \# a- u7 f) v
    10& D7 t; U; P" y' ~& \0 j
    11
    $ b$ x+ r3 e+ w* O% |12
    / o6 i' Q/ t5 z! \: j) |$ ^/ }* R$ d5 f13, L% `7 `  R8 C: V4 M6 o& e4 E
    14
    3 b* D0 l+ c; C4 r15
    + Q: C; n9 A' D16
    9 J2 ?/ e: m: P. k: [17: v% n8 \7 b* X* @# A, g! e- |5 E
    188 w6 l& M% @* @. s9 s" t
    193 c! P( p, |$ x
    20
    ' E8 Q  C: |# |' M21
    6 Q( B; `/ u! i/ W3 N22
    ) ?0 U6 q& o" }1 f* T+ D2 _23
    + a: F! Z+ ?' o4 s9 D! N24. `$ b% ?; Q. @! P3 A3 n- Y
    25
    6 W2 Z: v1 v& u5 u- \7 H26  b. q3 ?( Y# b6 a! D' w
    27
    4 X  c- W( p4 p, q& n6 {) b287 V* T6 _2 _5 ?' n' v) F$ J
    29, r$ u# b8 L% u. G. w
    30/ o. y8 v9 i1 }
    310 x' U( z1 m; G8 V; X" f( I
    32
    + M* k$ D7 }% T/ u33
    5 K( B' c' a. K) c34" D; W4 O6 J* B( a. E- \7 i- X
    35
    - U" O- U  Q8 s, k! ~36
    5 p) G, v) B& q* v37
    & k' _3 d8 P1 }8 K6 G6 u38
    + Z- p* M- O4 W; S, Q% r9 T4 u39) \, r1 U8 Z/ @# \/ q- u8 V
    40
    - A: r0 ?+ m8 J6 z41
    $ |- O( `9 c3 _- |420 o7 a, {3 B! x/ O2 b" f
    43
    * ]  ^& S/ M2 R2 S3 O. \44
      ~! N' z4 I* X1 E: u0 @4 C% |45
    ; ]* j6 B* a, Y& M  q# O6 S46
    / U' i4 u& Q. p* F1 m47
    # [% M$ l9 j% |* |1 ?1 M! g48
    # F9 W& ^2 O4 B% j7 p7 `# `8.4.2 数值型函数; A) r+ D4 i1 [, U( ~
      这里着重需要介绍的是pd.to_numeric方法,它虽然不是str对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。其主要参数包括:" o( U6 w, `: u8 e9 a* e1 P1 L
    9 U" \0 S' j. H+ v# N- M  B
    errors:非数值的处理模式。对于不能转换为数值的有三种errors选项:
    7 Y2 M+ K- G8 g; z5 lraise:直接报错,默认选项: R, K" E7 e2 _. r/ C6 t" O7 I
    coerce:设为缺失值
    0 Z) {8 h+ f* {! Q4 q, m( w, nignore:保持原来的字符串。
    3 F" J# F* f- t3 l3 vdowncast:转换类型,转成 ‘integer’, ‘signed’, ‘unsigned’, 或 ‘float’的最小dtype。比如可以转成float32就不会转成float64。
    : S# r& Q% D" p: |3 M; Ws = pd.Series(['1', '2.2', '2e', '??', '-2.1', '0'])' K+ w/ k6 k  [8 D; T9 f+ o. l% L
    + h, i% `4 ~" I" [
    pd.to_numeric(s, errors='ignore')
    . v: O' `) ^1 H0 POut[93]: - j) K" b( ^; G8 n
    0       16 k1 G! z3 C$ s+ c$ \7 Z+ i
    1     2.2
    9 O& V: `% L: b) K; V1 P  }: K2      2e
    / L: x' o3 u9 i3      ??6 t+ z, f6 S: D- ^, W
    4    -2.1
    ! W  m: _. h0 P% ]5       0
    - p* A6 c7 }$ j! y" xdtype: object
    ) c: g, C7 w, F( L9 l" ?5 t2 {. `$ V. b4 K: Z- E
    pd.to_numeric(s, errors='coerce')
    4 K6 K. O: [# t1 P' ~Out[94]:
    # O7 R0 @' p, D0    1.0: u# N2 V* {! d$ L
    1    2.2
    % F- r  p  T; e% ]6 s3 p6 d) J2    NaN3 }0 G3 c  l! P8 J& U& C
    3    NaN0 y: s2 Z7 A7 z- D( G1 e4 C3 q0 r
    4   -2.1
    6 h* a, I9 Y0 i1 \' v9 r! w5    0.0
    $ V0 t+ S- \9 e8 h$ ]dtype: float647 M( j9 W! T- K; L, r0 D/ U

    4 H" b* S" g- n& p. f3 B1
    - u" J$ M$ o0 ]) J9 g7 @2
    3 u! e5 k8 y! Q. y% U3/ y/ J8 z- p9 n, v2 R4 e
    4" S: p! N" d$ X" l. E) a
    5
    - o' m+ S6 D5 z9 r/ b6 i6* s9 ]4 ?, U: o
    76 v5 c( C: _- T( e5 O8 e
    8
    8 b% z/ h5 r0 W3 ?/ ?( W+ Y5 l6 Z2 u9  h8 F* L7 n* f1 h7 c0 Z+ ]
    10
    ; b8 Y% q* c- i, c# ^+ Y' {0 w11
    % G& O) J; R$ E9 ^2 s9 r7 {7 _12
    * L8 C9 I+ J& K13
    " c# d: f7 |" [0 [, [* k14+ ?8 k4 m- p( \
    15( `9 {3 w/ K7 p% ~" M
    16
    % O9 f" U2 C" l# e0 t17
      u6 C1 u6 G. \* ?" c" A  Y18/ L. R' t* P" t7 L3 q0 m
    19
    2 `. j" v  n3 D4 F7 N, ~0 F20
    1 i: t' \4 v# R" N( s$ i0 \& j8 r21
    , l1 P" p5 n. ~3 z# @; c! T  在数据清洗时,可以利用coerce的设定,快速查看非数值型的行:: t2 u9 K- N8 i% T5 l
    & {7 R( }2 ]+ E  C$ l7 q
    s[pd.to_numeric(s, errors='coerce').isna()]  x% s( c' |: n& P- [" ^
    Out[95]: + g% S# Q3 K  Q: U4 v
    2    2e, A! o& D9 b/ M6 r; j" r
    3    ??
    # s. K% P7 {2 O  }2 ^7 idtype: object
    ; |9 J3 R% C3 O" r2 B# o1; {; B0 l& u" O/ t/ T
    2. \( ^3 h' e/ A6 q. e
    3% e) R+ Q2 Y) U6 W# I2 K" ~& `
    4. k' h) l/ V& Q- C7 B
    56 j. O8 ?4 R; W1 Q* p
    8.4.3 统计型函数
    7 w+ U6 B$ S' V& T; z  count和len的作用分别是返回出现正则模式的次数和字符串的长度:  W+ T% N2 y' ~
    / S( R5 a3 v& E/ o3 C/ q8 ^
    s = pd.Series(['cat rat fat at', 'get feed sheet heat']), [9 l0 Y6 s  }( `" A9 ~

    3 _/ A; z+ B, y6 a* ws.str.count('[r|f]at|ee') # |左右两种子串都匹配了两次
    ) W5 Y$ H- N" F8 F" ^Out[97]: * @5 o, ?& G3 R/ ]
    0    2
    % b/ i% e+ a# O, [9 L2 C8 O& _" h1    2# C. w( X3 s$ a4 Y) Q8 Z' A
    dtype: int64. }. G- K) \: k/ |5 t5 \6 x! N

    5 `5 @- Y5 d4 V# p+ e3 b# ?9 hs.str.len()
    8 E+ ?+ n$ J8 R2 M0 I. j+ _5 vOut[98]: 3 W) s) ]" t# W7 R$ F, l3 S
    0    146 X" \# u: `( u$ T. r
    1    19
    / \  i/ |  V0 y* I& J$ Bdtype: int64, m2 I  y1 F8 f! s  ^- E, t
    11 X- w) p, v& r# H6 u
    2
    5 ^( {/ u$ y" K. p5 ~5 S# s! U5 [31 F% u/ g6 L) X* q* z+ j% y8 f
    4
    - X+ L- H. P* m" b$ d, U5
    - I% q* `' V+ v% k: d) H2 L6
    * H$ s: a  I/ U- c' t3 B7 T7
    : |9 j* K( @, q2 L6 w; l) s. ]8# \5 \! ]  L* j
    9
    ' l7 x. l3 F/ q2 O4 C+ |4 k10/ P9 J* V7 D% [) E; d- U
    119 \( X, f! y$ W
    12# s0 u+ r; c6 O8 r2 `- u
    13* G) j: f( d' G& Y% b" e& }0 }& {$ f
    8.4.4 格式型函数: L* e3 L8 T3 l7 \* U2 w
      格式型函数主要分为两类,第一种是除空型,第二种是填充型。其中,第一类函数一共有三种,它们分别是strip, rstrip, lstrip,分别代表去除两侧空格、右侧空格和左侧空格。这些函数在数据清洗时是有用的,特别是列名含有非法空格的时候。" g9 W, |6 g/ h& P) {

    ( k2 w3 I  h  }. O$ ^my_index = pd.Index([' col1', 'col2 ', ' col3 '])6 Q- s1 }0 k& R3 b: h  v9 m8 J
    2 V6 C. ^' T: X+ R
    my_index.str.strip().str.len()! _4 H3 W2 U, L# r/ c7 |
    Out[100]: Int64Index([4, 4, 4], dtype='int64')' k3 e: g1 ]  F* f" Y  |; p0 R

    3 a( P; W- j% n$ omy_index.str.rstrip().str.len()3 w/ h$ M' p, r
    Out[101]: Int64Index([5, 4, 5], dtype='int64')
    % w. z: N. `. l& x. I0 f+ M) U
    ( Y  x8 i! r" P# [9 I; Q2 smy_index.str.lstrip().str.len()( B$ x( D& m9 Y5 P- k8 P3 g
    Out[102]: Int64Index([4, 5, 5], dtype='int64')8 i) g* Z8 q! h- G
    1$ n( K$ `4 Z7 j( m, B& m
    21 C, M! B# s# s
    3
    ) B6 d- ?; ]7 @" ]4
    8 W2 ~$ u' n) \- y  y58 K) i1 e' P1 h( U) J! q1 `
    6& x7 l# z9 u) W: X% [* [6 q& w% }
    7
    " r" ^6 c" y  Q4 z4 {2 j8
    8 R9 |, y- a1 u; U* z/ `2 l9
    # B+ o8 ]% U7 Q& Y. F0 ~* x10
    ' M" G: z, [. ?3 p, l! E  对于填充型函数而言,pad是最灵活的,它可以选定字符串长度、填充的方向和填充内容:
    % A( b- j; }7 g
    + d5 E, f7 [+ O1 as = pd.Series(['a','b','c'])
    7 L* N/ M. l+ W6 w# }
    + O  U" @0 J. W" V4 Z4 is.str.pad(5,'left','*')! O- V. R( J5 G$ J+ e
    Out[104]: * t% Q4 B- J' M# h" a* e* e! I
    0    ****a/ D. A/ O$ j2 ~, O
    1    ****b
    8 y* Q5 s( l$ E/ C: a6 ?8 s% L2    ****c7 T. a% t1 v$ h) N* [" W- x! _0 Y
    dtype: object
    - U) `, `$ j% Z% ?; j% q9 R" K! P3 l$ U
    s.str.pad(5,'right','*')
    3 U( Z# Z, w5 j8 t6 pOut[105]:
    $ O% E8 ?8 X6 ^  I0    a****
    # {7 Q8 F' g, Y- F/ b# G1    b****
    5 k& S9 y, a8 F) F0 H2    c****$ n& l$ O5 `' m  }
    dtype: object
    7 L! W6 _" |) H9 a' l% @( _" L6 ]6 o2 ?: q/ P
    s.str.pad(5,'both','*')
    & G3 u  w# L3 a+ |) `( |; |  y( A4 ~Out[106]: & [# X5 \( L% S
    0    **a**4 v- E8 \+ z' i! I/ t  e$ u
    1    **b**  J  l: p0 Z! ^
    2    **c**& c5 W; `) q9 }! S6 n
    dtype: object2 ~+ N& ~. ~( x; ]7 v1 W' w& A! |
    8 m1 u) y9 u" [( w$ s
    1( f! J6 F2 _7 l  J9 i1 `
    2
    8 ^9 ^) g' c! S7 X3" E: l: @) j% ?3 E/ X6 [' _0 N
    4. J3 Q* \/ b5 Y4 r, ~+ y
    55 ]5 C5 K+ x# d
    6" q2 K% Q. F+ X! @' m8 {
    77 J( L3 z! J" U
    8
    ) H" ]7 A( Y) k3 T" k! y/ K9+ w8 i4 l2 i! j0 {4 n5 i0 K
    105 M2 G$ }4 ?% V- d" i* V; I
    11
    2 e  b6 p& U1 v& o12
    * j4 ^* C* Q9 ~- g13$ t9 Z; {8 `' o" E6 @' C$ N. B# R
    14
    7 r- G7 ^( s5 q1 @+ l$ k15' m* o( K  [, c+ Q/ ?( \) T* J
    16
    5 f0 K, C/ E2 o# w17
    1 D$ j! ~$ F5 X  I/ N/ K18
    ( g- k  \9 G/ g! ]( H* A: A19: K1 `) C5 J" v% |) `0 J' U
    20
    * j) w7 c; b$ _/ z8 u6 r% ^217 {/ m9 p" C& Z& {7 N
    22
    6 ~2 [* a3 t' R, k6 z3 U  上述的三种情况可以分别用rjust, ljust, center来等效完成,需要注意ljust是指右侧填充而不是左侧填充:
    ! x6 K9 }( M: \& a+ t1 H5 u
    8 [  a! W  J7 _0 @9 {& Bs.str.rjust(5, '*')
    ; A& P5 e8 D8 m- EOut[107]:
    5 m) J, |% Y9 `. a- a0    ****a9 Q. U; Q" O, j
    1    ****b9 a' l1 P* m& O& u8 j! r
    2    ****c; k( k7 `0 z. x5 _0 R
    dtype: object
    $ C% n1 O  f( h/ [& y" S$ x+ B2 k0 V  j" ]5 K4 x
    s.str.ljust(5, '*')
    * h. N% L" ]! hOut[108]:
    ' X; }2 z, @3 V% o/ `& O0    a****
    : E$ u2 W. [3 V* W- A5 n1    b****% P$ o1 h( v1 V% l
    2    c****2 _7 s* }4 e3 g9 Z+ B2 s5 t/ }
    dtype: object
    ( _# ]2 d# P, ^* P3 \9 ?
    + _! p( r0 m( Js.str.center(5, '*')
    6 Z3 @" D8 k$ E, o5 I5 C" D  [Out[109]:
    2 O1 o, _* b( M3 w* x# }0    **a**
    / B: _  Y4 l* i0 d1    **b**
    / `" H" v: G8 `/ u0 t6 x2    **c**7 t* i3 m9 a; {7 b5 w
    dtype: object3 Y. H. s1 Z4 i$ P: \) X
    . F& S' Y" A" U" ?/ T  |) G4 ?
    1
    3 {; E2 t& t: v9 v3 @; D2
    1 r1 I- a" Q" L% G( a  |( z3
    7 z0 I5 f$ `  z+ R- ~2 Z4: Q; Q* t7 }2 d9 J' Z
    5( f3 y% \' B$ ^! `5 R
    66 g9 O4 f' c7 k0 H+ y
    7
    9 N3 [% J$ M: H  a81 Q  X& l5 ]* v+ n; F
    9/ d6 L& M2 o3 b* w$ j
    10# {5 T1 v  o3 G; |) n: c! y9 S, g
    11" \7 V( P, D  L5 J
    12/ m0 X+ M3 |/ D5 t
    13
    + W* e; f0 S. o% K14
    * ]8 G% ]8 V) Z6 B# h  B& l/ ~2 ~15
    9 X  \! R8 y) B165 A' Q+ g2 ~* J$ y' F9 h
    17
    ( }  x+ x; C2 f8 c' {3 e189 H$ A5 a6 \: u
    194 M  i3 r. x4 [
    20
    6 n9 v+ R- S% G2 F7 y) q  在读取excel文件时,经常会出现数字前补0的需求,例如证券代码读入的时候会把"000007"作为数值7来处理,pandas中除了可以使用上面的左侧填充函数进行操作之外,还可用zfill来实现。: F0 f1 c8 b- b7 c* k

    2 ]& M, E1 ^9 F/ v+ s, ps = pd.Series([7, 155, 303000]).astype('string')
    7 {3 u  x! K+ R% H, q: ]
    , x0 g9 b" n, ~0 x2 Ss.str.pad(6,'left','0')
    * e% @( k6 O0 D. u6 r* rOut[111]:
    - S, s( T8 u6 R0    000007& Q& C1 L6 F& p' e
    1    000155
    % h$ {) ~, _% j! j8 h2    303000
    * V) D& M7 o7 d3 k5 r  hdtype: string* x% h* B' Y! d1 Z) a' a6 y
    - b( K7 E: R6 q* S) o  K
    s.str.rjust(6,'0')
    4 g: @3 r' Z" EOut[112]:
      Z$ C. j0 D& Y  q- x0    000007
    # C* \* M" c+ r8 ]2 b. e1    000155
    * ?9 v; K# Z7 m, t  m$ S2    303000
    # X3 j9 A' E4 ]" c! Gdtype: string
    $ _. j- u6 ~: U' c1 p# P. U; U' x( q. L& x
    s.str.zfill(6)
    1 j  S. @9 X) D4 c8 p' r/ dOut[113]:
    ) w, X, E! [) ?! E7 n% I) j; u0    000007
    5 j; t6 ]$ M, p( k1    000155
    2 a9 Y" }/ W1 R2    3030002 ?5 j5 S' W$ I2 d1 H
    dtype: string
    % B! D% o0 c3 _: j& C5 [, d! |4 _0 `! P, M* k. G1 S
    1
    ( W" H3 g3 @  K' @' {2
    ( E1 |0 u2 r! J3
    . _& C- R" h- X& Y43 ~( v, A3 P" f, r( ^
    5
    % [* n1 S$ }5 Z/ b# o6
    3 }" u2 o6 K, J7 l6 F7
    ' K; f' R4 b  u' B7 f84 y- w" R% |9 S8 J4 M- V( H
    9) ~+ s* x" P7 A1 j+ G
    10" t) I3 n: m8 d9 v. u7 V
    11
    % ?% L; {1 S& c5 E12
    / k4 S8 U5 @& e" g# L* ^13
    & [) u9 m2 T! q& ~14
    * d" o8 ~% H: _6 V9 }0 ?150 D. E* i- b. x' r7 o+ p
    16
    & I# [; ]' C0 S# x17
    $ @. N* l9 j$ ]18
    6 H/ d- m( Z$ T* A19, c# }+ }& d' x" p- H' ^2 [2 H7 v2 D
    20
    ! s# j4 x5 O1 b' i- T0 w/ U& {21
    0 w" w5 [( t' l/ V# h+ |1 S22
    . G2 _+ z. T* k# v/ h, d. e8.5 练习$ S: t% D; M% _( z$ U' Z
    Ex1:房屋信息数据集- m' o$ i6 g3 a2 Q
    现有一份房屋信息数据集如下:9 V; L" b7 o8 L

    : d% d" p! E7 q7 S! sdf = pd.read_excel('../data/house_info.xls', usecols=['floor','year','area','price'])9 K1 e8 ^+ J. i; H
    df.head(3)
    ! u/ O* ^* `4 W6 JOut[115]: ; E3 B( T/ B8 ^, l& r7 V4 ~: G6 E/ C  V
          floor    year    area price
    / `& ~' D1 a9 u+ p) T0   高层(共6层)  1986年建  58.23㎡  155万4 L+ N3 f% Q( ?2 m$ @! g- U
    1  中层(共20层)  2020年建     88㎡  155万& y1 U$ D% K' r- ~
    2  低层(共28层)  2010年建  89.33㎡  365万- N! Z% s$ R- _: f
    1
    " t1 {! r  s, n( n# W3 g+ I- Y8 O24 \$ m" F6 J, w. x, i9 I  y
    3( S# j0 S# _6 f$ N# b. M: s
    49 r% c7 b' }/ t# f
    5
    2 `  v% h5 c, h" U6
    3 K' ]4 B; c0 u7 V2 e" n( b- c70 i0 f4 o/ J2 Z' `3 B# y
    将year列改为整数年份存储。
    8 s, _3 E# M, G将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。1 P8 `0 f* U: V3 [) @
    计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数: ~4 Z/ R' |, g. N  T
    将year列改为整数年份存储。
    4 K4 o+ ], ^2 ^; L) q3 r( S2 A* ^9 f"""- D0 h% e! R& K8 l3 C6 ~
    整个序列需要先转成Nullable类型的String类型,取出年份,再将年份转为Int64类型。
    - n/ q: {5 u- P3 o/ `% [% M注意,转换的类型是Int64不是int,否则报错。即使astype加参数errors='ignore'跳过缺失值,4 ^8 ~" u- I7 L( e/ B" _# y
    转成int后,序列还有缺失值所以,还是变成了object。+ l, y$ x# h' g6 k2 Z+ C
    而整个序列转为Int,就还是Int类型,缺失值变成了 pd.NA 。7 z1 X9 Z& ]; s* N
    """
    ) T. h* n& B) a; @9 @4 r) Edf = df.convert_dtypes()& Z+ o3 ~- Z* J
    df['year']=df['year'].str.replace('\D','',regex=True).astype('Int64'). x; b5 |0 \' w+ S8 z
    df.loc[df.year.notna()]['year'].head()
    * O2 m" K# a, i5 f9 n; {; o
    9 K  I% ]) d  o% F. p: k0        1986
    * {! \+ W$ O4 y( q: A7 K  b* m1        2020
    & u- d  a7 u" @8 f2        2010" M+ P) ~( M/ v4 P
    3        2014
    $ l/ N6 A7 e1 B$ ]' K) h4        2015
    $ M: `* U/ ]4 |7 z$ sName: year, Length: 12850, dtype: Int642 X" G3 Y0 b' T3 c  [
    % s2 X% K+ y$ \  o9 X7 f; y( }
    1
    - ]" _2 t  r  I% K/ k6 b6 X2
    2 @4 T1 G' P, J6 D# ]2 k39 p; W5 e, h' |, Q; W
    4
    9 u% K+ f4 Q) H5 ?3 Z55 N. s2 m, i9 l
    6" q* h+ l* \* y  Y" h( h4 _: H
    7
    # E* n" Q% Y! p% v# S8+ M+ v3 Q* c9 w% h  g
    9
    ( W+ ~, _+ z" f' s- v103 W' H# l1 b8 u5 D# U
    11
    . }( s& q6 S+ i! E* s12  q) @7 C8 l8 o+ H9 k
    13- Z* I$ `$ T1 R/ _9 h' m- w+ P
    14
      V+ u6 l) ?0 f0 u% }, G154 R2 V0 I, F4 I& C/ a! v" P
    16
    " i. f! U: K4 r2 }9 O! I1 q$ m参考答案:3 h( b3 J) W/ H; q1 V

    ) A9 n4 j6 h9 V/ W6 p' ^5 U; P2 Z6 f不知道为啥pd.to_numeric(df.year.str[:-2],downcast="integer")类型为float32,不应该是整型么) ]& B3 N3 y) M7 F; y: y
    7 o7 g5 Z1 I/ P) F# N0 J
    df.year = pd.to_numeric(df.year.str[:-2]).astype('Int64')
    ( @& ]) p! i+ N1 U" O) D% _df.loc[df.year.notna()]['year']
    , C8 A' [: Q( g: ^$ }: d# }6 P13 `( b1 |1 b7 T( o4 Y2 q4 G/ G
    2* N' `" |7 C7 x% O
    将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。
    : i+ h: G# j! @& \, z$ h4 D6 |pat = '(?P<Level>\w+层)(?P<Highest>\(\w+层)'9 ^8 x# Y5 ?# s& b
    df2=df['floor'].str.extract(pat)  # 拆分成两列,第二列还是(共6层得形式,所以还的替换一次
    9 h  H7 ~2 k2 r9 R& _df=pd.concat([df,df2],axis=1).convert_dtypes()  # 新增列拼接在后面,再次转为Nullable类型% Q2 X/ |( X% z# J7 x+ n
    df['Highest']=df['Highest'].str.replace('\D+','',regex=True).astype('Int64')              . H1 x- t5 ~  S' T% U& v# Z2 \
    df=df[['Level','Highest','year','area','price']]" ~3 @0 u! K8 E1 m" j, S
    df.head()
    3 H7 r+ s0 W# e; a" v/ {6 y* k8 I# c! o+ \/ m% S0 i: z4 r* |
       Level  Highest        year        area        price
    ; A: w: y9 z  q, q4 u& H; [- k0        高层                6                1986        58.23㎡        155万+ w! m" C4 Z1 i/ a) M3 k
    1        中层                20                2020        88㎡        155万
    ( u+ x% }8 t- o- ]( `7 h2        低层                28                2010        89.33㎡        365万* ^1 I3 Q0 Y; @! J7 \; z+ Y
    3        低层                20                2014        82㎡        308万4 H% t) l3 p5 R: B% A+ G- x
    4        高层                1                2015        98㎡        117万; T5 s$ ^- S- k
    1
    , h, s, Q% a* u3 w" }: U  n0 D/ {9 I2
    " ]0 `5 L+ I; m) B( J2 ?0 _7 |" h3* ]2 A8 Y. ]9 V, O
    4
    . l3 V: b. e& p$ X57 v; _  f- u% t1 k8 {" L
    6- V1 h. j0 T, @! z- E7 I
    7
    : x3 |# B' D7 A6 j: x! d( f. c8
    . _$ r  Y: d" y& S, [' o9# p* m2 d* H9 }7 ~- {! ]
    10
    6 q6 T* K$ L9 @" i; B11( ?9 e( \) l2 d. w/ N
    12! y3 u% s, z9 e) D; F; N
    134 ^. B0 i, l9 c* |: p6 S1 T
    # 参考答案。感觉是第二个字段加了中文的()可以准备匹配出数字,但是不好直接命令子组了$ J  l( B: w$ m+ i5 U- Q. K
    pat = '(\w层)(共(\d+)层)'& \- t" A( ~" a: N9 q4 M4 n
    new_cols = df.floor.str.extract(pat).rename(
    0 H: x. [) W3 B+ a' O                    columns={0:'Level', 1:'Highest'})
    1 N4 Z, o  w) g, S% q
    ) g+ T% a) E- M6 w8 B5 p! f% @4 a0 `df = pd.concat([df.drop(columns=['floor']), new_cols], 1)
    8 C2 M" V( i" m# O; L6 \4 C4 M! cdf.head(3); K. l! l6 c' {. h7 t

    5 y% y+ u% _) k9 p9 `2 UOut[163]: # U( Q0 j. Y8 ^. P
       year    area price    Level Highest
    # k# Z6 T/ Y' g4 Y: h; V( m) w0  1986  58.23㎡  155万    高层       6
    $ ~( j, s$ x' H; W* C! N1  2020     88㎡  155万    中层      20
    $ `* D2 R/ F1 w8 `, H8 B. l2  2010  89.33㎡  365万    低层      28
    7 i% T$ C) s( L6 s: U1
    8 r0 K$ y; T% }2 x2% o" n3 ?2 w7 J( l! x
    34 h( s* U' L' p, t
    4- P9 E: K# o3 L* C% o, q8 b4 U
    52 E2 u. P. }; |
    6  a! a& x6 p) O' S9 W  B
    72 Z% @0 _6 H  {
    8# g. x5 q4 j, n8 Y( J8 ~
    9
    8 m) J8 j7 h2 y7 M; _2 h! _10
    2 C2 q; ~6 |1 _. O7 v+ a* I2 ^11: c& A2 X% _% p
    12
    7 F" l0 M2 p0 `, [; U; o13" Q/ q* R( ]: \; |$ `: O
    计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数。
    5 P+ s+ z; Q3 j7 U: G"""
    ; {' N: X0 L( D3 L8 ustr.findall返回的结果都是列表,只能用apply取值去掉列表形式9 y1 L8 S# p1 v7 k
    参考答案用pd.to_numeric(df.area.str[:-1])更简洁" _+ C1 p2 z6 K8 ?- C( M! b
    由于area和price都没有缺失值,所以可以直接转类型5 `/ ]! t0 F6 L7 i% v" _0 Z) H
    """; v6 O  p1 _  }- X
    df['new_area']=df['area'].str.findall(r'\d+.\d+|\d+').apply(lambda x:float(x[0]))
    $ t7 E: j1 F7 Ydf['new_price']=df['price'].str.replace('\D+','',regex=True).astype('int64'), @) v' g* `1 U7 f8 m' e
    df.eval('avg_price=10000*new_price/new_area',inplace=True)- ^. C( v" t, ^3 N
    # 最后均价这一列小数转整型直接用.astype('int')就行,我还准备.apply(lambda x:int(round(x,0)))5 I. z( I) F2 B, K; w
    # 最后数字+元/平米写法更简单" t' s9 L. d! r" o1 t4 }' ^
    df['avg_price']=df['avg_price'].astype('int').astype('string')+'元/平米'
    9 Z0 ^* |; ^8 `! E% d: {, J2 j# Qdel df['new_area'],df['new_price']
    6 h; g0 b; ^! n$ I( w+ Mdf.head()
    : `- p& Q: H$ I- G/ n; h) z
    1 A0 g' c( [' o2 t- B3 T   Level        Highest        year        area        price        avg_price
    2 I* w" w% h4 {0        高层                        6        1986        58.23㎡        155万        26618元/平米( h; b: r4 W( ~: }% O
    1        中层                        20        2020        88㎡        155万        17613元/平米5 v" \; C4 e+ ]  T  y
    2        低层                        28        2010        89.33㎡        365万        40859元/平米3 m$ b. l: V5 d
    3        低层                        20        2014        82㎡        308万        37560元/平米
    . [  l( a6 B' U4        高层                        1        2015        98㎡        117万        11938元/平米; }9 B3 H0 g0 R: l  [
    : c8 f  F, Y2 r5 ?& x
    1
    ! a4 k4 `1 Z# C* O% p; z" A2" V& j5 `7 ?4 D$ z4 E8 p2 _
    31 Q& V, q6 d1 _5 U& H; K$ d, @8 `
    4
    7 C2 B. ?* N' {4 _2 n& P5* K- M1 F+ B8 v$ F/ p$ C
    6- i; Q, m/ [. x
    73 u6 K+ m% {: ]9 Q3 f
    8
    8 ^* u* s6 N0 V; i# m  m; _9
    & D. J! b/ D) E0 f5 L1 o' x, f4 {" w10
    # R+ @8 U( Q2 I" W4 j6 r110 a7 G7 t; [) L5 n% T& e2 R* h
    12
    : [, g7 I+ f; P  k! f+ M8 S13; |1 M3 S. d& |: K: A+ x
    140 D1 R9 {0 v/ G! p" h
    15
    * i" Y8 B! Q$ W+ P- ]$ G  ^3 f16
    ( q7 J2 r! L- ^& |0 H7 M" d17. `) `! y) j7 R
    18
    5 z! v, ^, ?- s19
    : r% e: h. q* H/ Q! y* d& f* o20  V/ @( p9 r7 w; Y" X5 ~
    # 参考答案
    / @" @) w/ [6 N8 A3 ~s_area = pd.to_numeric(df.area.str[:-1])
    - c$ M! M: a6 h4 p) G5 N/ o; zs_price = pd.to_numeric(df.price.str[:-1])
    / o. C4 N: Z7 M  p* H" |+ n2 wdf['avg_price'] = ((s_price/s_area)*10000).astype(
    : N# ~: ?8 ~& e4 S1 i                    'int').astype('string') + '元/平米'
    1 T! S% l  M8 ^% M: j5 n6 Z4 V
    . P! r0 W4 U% Q5 Y" Jdf.head(3)% \2 G; r6 p" t0 b6 ?
    Out[167]:
    # o  h8 i8 E( j: U# P- Z1 H( f4 b   year    area   price   Level Highest  avg_price& u8 u( j2 w% P( Y0 t
    0  1986  58.23㎡  155万    高层     6          26618元/平米
    - L0 \  w7 c* @0 ?9 q- t7 n6 P- A1  2020     88㎡  155万    中层     20          17613元/平米, q- K- ~' P6 N* v
    2  2010  89.33㎡  365万    低层     28          40859元/平米# J0 I5 H$ p# j) o7 ]
    1
    . ~. t. a+ ?( k% v3 m1 G) ?2
    - w1 _4 O2 {# R& m& n) F32 M6 A3 W9 |+ h
    4
      m  c/ |- X. ]% J7 ?5 h5' B# r8 F4 M# S2 b+ A- P
    6
    # {8 E6 ^' C# l& U: Z( G. ]* d7
    ; e0 J' P1 [+ W! z2 Q1 Q8$ L. C, n; Z( B+ Z" T& \# G4 `
    93 Q! n+ y# u1 L4 Z  g: S
    10
    ! O3 `3 y' W0 _, ~" o$ Q11
    / d" G2 s) H+ Y  v12+ C9 H' k7 T3 O4 Y9 ?2 l8 o! f; G% j
    Ex2:《权力的游戏》剧本数据集
    7 h: y7 M! m: ]8 @/ {现有一份权力的游戏剧本数据集如下:
    ) e! e, C: E$ i& W! d" h
    9 I0 l: F, f, G2 kdf = pd.read_csv('../data/script.csv')3 j( x. f7 ?! i/ N+ q% s
    df.head(3)
    / R# ~! A4 ^5 ~) S/ f) c6 z: G0 r+ f/ c( E
    Out[115]:
    / t; n# Z( N  t, p6 mOut[117]: ( S6 f$ a" U3 C6 E# j# x2 k
      Release Date    Season   Episode      Episode Title          Name                                           Sentence
    * j+ H( G) `$ b, A: z# N# G0   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce  What do you expect? They're savages. One lot s...
    ' x+ P+ N5 l; K# v1   2011-04-17  Season 1  Episode 1  Winter is Coming          will  I've never seen wildlings do a thing like this...2 z+ o3 h! D* N9 l1 j; u  |+ K
    2   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce 6 ?7 k$ B/ V  y6 [/ j) z: ^; G1 ^
    13 r! b3 ?7 }' {$ `
    2
    # f& C4 A: g1 o8 R0 n1 u38 R" T% J* T; [5 F: [
    4( @1 {7 F+ E& b
    5, Y! E# M; V/ Y  B! `. Q
    6
    ) V9 b% z7 Y# j4 O! W6 h7 s, n/ [7
    & H. J& z% I: r3 T2 q$ A82 f  }9 k. c! ~  X% D+ B
    9
    9 q& D0 d: v% q: y2 T6 P计算每一个Episode的台词条数。
    7 u* }6 C- p% P  ]以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。+ E- Z8 O0 A6 ]  n
    若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有 &#119899; 个问号,则认为回答者回答了 &#119899; 个问题,请求出回答最多问题的前五个人。
    # t) y! V& ]% T- e# w计算每一个Episode的台词条数。6 A; j+ J( p) `8 n. i4 i, x
    df.columns =df.columns.str.strip() #  列名中有空格2 B, @3 Y4 G- k+ o9 O
    df.groupby(['Season','Episode'])['Sentence'].count().sort_values(ascending=False).head()- _% K) I8 N: X+ E

    ! Y- h$ ]0 ^1 Y9 B1 r& Fseason    Episode  
    4 {% [6 }- j: y/ Q. jSeason 7  Episode 5    505, h& @7 Y# T( R( v: i9 B- D
    Season 3  Episode 2    480
    3 j, j. v/ h2 ]# NSeason 4  Episode 1    4751 y9 X/ L$ f) m+ Q' D
    Season 3  Episode 5    440, k9 w+ B5 M: J& {
    Season 2  Episode 2    432
    ( d4 @. J% i# X, u- M( j5 _: N1
    ' u; @4 d* q- n* {  v' [9 j2 W29 `9 y; I  u. d: W
    3
    0 |7 T& t9 W: x/ j$ ^8 \  s7 c* Z0 s43 m1 Y% Q# {) o2 k/ }+ _% ?. F
    5
    0 C0 K) s+ H" `! I- b9 ^6
    $ _' u" Z! O# R. L7
    $ w9 Q$ r8 F, L) b! K# T5 i8
    & Z- W9 z2 U7 }% k4 ^7 B& l. i0 O95 ~9 D5 `, q4 n+ a
    以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
    * p2 D  l9 S' {. s: [+ k# str.count是可以计算每个字符串被正则匹配了多少次,+1就是单词数
    & d- q2 @8 a8 n' D& n2 F+ m3 ~df['len_words']=df['Sentence'].str.count(r' ')+14 |+ r. N- K) O3 L) ]% X# J9 v
    df.groupby(['Name'])['len_words'].mean().sort_values(ascending=False).head()
    + a  t7 F& U+ a
    ( k/ X" h# f* h" t: W7 n; T7 hName4 {6 y8 L2 {+ n4 b3 g
    male singer          109.000000
    # [9 B6 r+ M# N6 eslave owner           77.0000006 q$ J) s1 U" h$ P
    manderly              62.000000; o  O+ H6 m8 N, `$ s$ w1 v
    lollys stokeworth     62.0000001 F6 `7 B" [+ W  R6 y
    dothraki matron       56.666667
    8 s4 K. [0 O4 [2 O4 G/ z9 GName: len_words, dtype: float64
    7 a# A1 C) o: w5 K( {, R$ u" u1
    1 a. c5 k: h; x; J24 Y" x7 h, v' v$ j
    3
    ( T9 v( ^! Z2 N8 d' Q4
    1 l7 S, p0 h, t9 Q5 v5% ~0 p& g# d  A& [. k* U
    6
    5 ]- V( h- [# ~5 ?7/ Q+ A# ]9 j4 s& m& x, f9 Y% N
    8
    + l. R& r4 n2 ]2 }: T# F: B+ y9+ _0 n3 h% f8 T3 t+ F
    10
    " o! C, n( B% K# c" f, B11$ F' P: M' I8 s% K6 u
    若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有n nn个问号,则认为回答者回答了n nn个问题,请求出回答最多问题的前五个人。
    . u8 v/ ?9 W0 f/ q3 l8 k: ^9 ]  pdf['Sentence'].str.count(r'\?') #  计算每人提问数3 H% K2 |. P5 L  ]: ]8 |
    ls=pd.concat([pd.Series(0),ls]).reset_index(drop=True)# 首行填0
    & y/ T6 C3 F, xdel ls[23911] # 末行删去
    ( t0 j3 }$ l5 C+ U, `df['len_questions']=ls
    ' B* F2 `5 y" u' p5 m. Sdf.groupby(['Name'])['len_questions'].sum().sort_values(ascending=False).head()2 _2 ]  c, t. k! ~, G( W( l
    2 Z( U/ Y" l8 ]# @; X
    Name9 c6 Z% t( Y# q
    tyrion lannister    5279 N1 _# f" O3 B3 Y1 U+ s/ N) c
    jon snow            374
    & V7 U( E) B6 O3 T0 @3 \jaime lannister     283
    + L8 O+ n# V$ parya stark          265* Q& G& z, X. y( K2 i, p
    cersei lannister    246$ I% K# }/ `0 P; V) m$ I7 ]# w
    Name: len_questions, dtype: int642 C) V* H. E% E# V. ^0 A
    & ~( q' d5 j: j' h1 G
    # 参考答案
    2 ], W) O1 R3 {0 a$ h( \; vs = pd.Series(df.Sentence.values, index=df.Name.shift(-1))
    9 ~" F2 p* Z% X1 r: k- |- w* \s.str.count('\?').groupby('Name').sum().sort_values(ascending=False).head()# v2 p1 w# d- Q+ m' a9 v  @

    * ~5 N# K6 |) p; g1. ^6 @& J; ^( ]2 o4 V
    2
    + \3 @% U% \% J$ ]. g  L& |3# E" J# E2 l* T5 n
    45 @/ M! L: l$ V" V! S/ d" K
    5
    8 a- E& `" V2 c. D. b& x6$ B1 a- J" ~' [" Z) O# q+ D1 t
    7
    6 V% `4 ?. R0 G/ T" _- n  s9 @6 K# @' A8
    $ w) m, `" _3 Z/ O! P9; D  t3 w& b! [* a, k
    107 i+ B% t2 F% @
    11
    ) r* u) r% p5 z* |8 |120 w/ ]7 c! b9 v3 y) P7 r
    135 X6 q; q( R& S  _
    14
      P' v+ X0 n" D' q% q! K* u150 j& R5 R7 b' p' T) G* l3 n
    16
    $ P4 j& ?; H+ u' C8 f; f17
    $ {1 R; w5 n8 M! a) p" `( _第九章 分类数据
    - z( E+ D+ q! b3 [, cimport numpy as np$ {- Q' A. x  D  ?4 A1 b
    import pandas as pd
    * `  y' Y, X+ R; E5 a; u# ^( F1
    0 q  x) d' a% J. v+ T/ X: n# Q" u2
    . `* o7 C! l7 {9.1 cat对象( I2 u3 z) U3 @0 z$ U
    9.1.1 cat对象的属性
    6 J6 U2 d7 @! c) {5 B  在pandas中提供了category类型,使用户能够处理分类类型的变量,将一个普通序列转换成分类变量可以使用astype方法。
    0 u9 C1 ^( l, x, V7 g9 \9 \; O* m. L; Z$ r0 O% z6 h3 O  I" m
    df = pd.read_csv('data/learn_pandas.csv'," Q( C/ p' b/ K/ K
         usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight']). C% O; `) J$ ]3 O% N5 i! }
    s = df.Grade.astype('category')
    + {. u! y8 n7 G' d! G+ O) S# }4 ?# l# j4 H
    s.head()- l0 r7 |: }! M( R+ q
    Out[5]:
    0 M( ?; q. [# }1 P2 f7 S2 F0     Freshman4 U. f, R0 y& J  p
    1     Freshman
    & W, }& V, q: n# ^2       Senior
    6 G, V8 y8 N+ v2 r3    Sophomore
    - i; @* X/ Z: U$ [! T/ J4    Sophomore
    8 i$ d  |9 p4 o+ FName: Grade, dtype: category
    1 M) J4 N7 }) v) B4 _! a0 XCategories (4, object): ['Freshman', 'Junior', 'Senior', 'Sophomore']0 z/ x. I# y* i9 p+ {  m
    1
    % q) t. X1 W2 l; x" e2  r  }# S- f+ y; _$ c+ g3 ?0 z0 S6 q( y
    37 l* q4 ]$ h& \) [( J: \$ P: K
    4
    ) c; \2 v% r# r0 b5 ]5& q! {9 N& O# u+ w
    6
    / v/ V. F3 k1 O% D7! ~  @' d- D# [4 N6 r! F
    8
    3 r; Z" Q3 a1 `& K9 O4 m* |9
    8 g7 o& N2 x0 Q+ F+ q10" D6 F) c5 F1 j) U2 k3 F
    11$ ?7 Y7 v  D$ o% m# C4 T7 |7 V
    12& J: @) ~2 Q4 c
    139 p% [: k8 |( D! D5 l+ Q0 O
      在一个分类类型的Series中定义了cat对象,它和上一章中介绍的str对象类似,定义了一些属性和方法来进行分类类别的操作。
    + M" Y* j% g6 W
    ; e2 i5 r4 ~* J9 u9 U- `s.cat& A) P7 x* N! `
    Out[6]: <pandas.core.arrays.categorical.CategoricalAccessor object at 0x000002B7974C20A0>
    + q1 @. ~/ `4 T, n% U0 G% E1
    + n, k7 C! m3 ^( Q$ o8 l2 ]# ^, ?& l1 c2
    7 d* v2 ?% \: X" scat的属性:4 o! {; I2 E8 T7 j
    8 K. k$ [& V( V3 Z- p
    cat.categories:查看类别的本身,它以Index类型存储: Z' d) U: I$ G( v
    cat.ordered:类别是否有序
    & r0 m# h2 V( m- J8 _cat.codes:访问类别编号。每一个序列的类别会被赋予唯一的整数编号,它们的编号取决于cat.categories中的顺序. Z; }7 n2 ^( a/ k: ?
    s.cat.categories
    . [- f) Z; [1 F+ d( U4 t' r7 P2 gOut[7]: Index(['Freshman', 'Junior', 'Senior', 'Sophomore'], dtype='object')
    ; ~' r; p" U5 g- Z: P) L. Z6 O/ z2 k, }) y  c; U7 `- T' Q
    s.cat.ordered
    4 |( K4 I" j) G' @( t" u+ AOut[8]: False
    % Y8 Q4 S  t9 u
    & j  S3 C, H  |, ?. f. ts.cat.codes.head()
    * P4 C  J  A& ]; }' Y/ C) \. M& AOut[9]: 8 N! R& f% F" H- B$ E4 h5 W
    0    0
    3 u9 T* f% k: L) C1 H( v' {1    0* x- n1 `. r. m0 o) X
    2    2
    # x! _6 q# J$ c) }6 E1 v1 M( ]3    3
    ! G1 j% {; m: e4    3
    4 J% p8 Y. u( c8 Cdtype: int8
    4 `- d- E! o+ [5 N0 x3 e1
    1 Z/ G( V0 b% K3 ^4 u3 y( p- Y( r2- \7 \- l9 G, z& ^
    3
      v: e! v4 S" A4 }; L6 g4- N3 q% C: r: \0 b6 G
    5
    " |! m* f3 S" f* u# w6+ b7 r: x7 j" `- y
    7
    , x) }) q7 [) X/ I% @8
    7 u+ i5 }% Q, `9
    7 t* g; O6 O& k" `" V8 a10% O+ S8 H$ ~' u$ v4 b& Q
    11
    8 M  y- I8 N/ y8 e) R0 S122 k! c* S1 \* V2 A
    13: r6 r5 I3 ~/ u* K
    14
    7 g5 u: M, C7 v& c9.1.2 类别的增加、删除和修改* y; }& ~+ o( ]5 T4 R& P, s( G
      通过cat对象的categories属性能够完成对类别的查询,那么应该如何进行“增改查删”的其他三个操作呢?( X% B; E: |9 v0 x- H" L! `* l* T

    - N4 Y, v- G& V0 z% r4 L【NOTE】类别不得直接修改1 C% }  ~0 M& l% Q5 Y, N# x' F
    在第三章中曾提到,索引 Index 类型是无法用 index_obj[0] = item 来修改的,而 categories 被存储在 Index 中,因此 pandas 在 cat 属性上定义了若干方法来达到相同的目的。+ P/ k; q9 h" [2 i# U9 ^( M

    % X% G1 y# ?# R! y3 Xadd_categories:增加类别
    : d( G2 ?9 g2 d$ f: I( Bs = s.cat.add_categories('Graduate') # 增加一个毕业生类别
    * J1 G/ O' e, B* q+ q# P- q# Es.cat.categories
    3 M: R# B1 B7 h3 @0 b% E* a
    8 h9 f# X  t- N7 V' c2 o$ oIndex(['Freshman', 'Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
    + R# o# L; X% y1
    # Y4 n4 [9 U  |- w/ C2
    , K% S$ Y9 B9 {2 h- k9 U; r3' J, h' Z) }; H/ z( u- Y& X% A
    4
    ) W/ ?3 T7 q8 a1 o. W. _remove_categories:删除类别。同时所有原来序列中的该类会被设置为缺失。. Q0 a3 P9 {9 V5 n* H$ r8 ?
    s = s.cat.remove_categories('Freshman')
    . \/ p. I" _6 p% p! ?) A. T' o9 t& W" x1 I
    s.cat.categories
    4 k, Q8 z% ^2 c4 k  [- b5 b" F$ n+ UOut[13]: Index(['Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
    1 Z8 ~+ H: d; I; A
    1 H6 r3 e" m# {8 H# \$ gs.head()4 Y2 i5 g! d0 _; N7 m5 D5 [
    Out[14]: : z4 k$ k/ @  N- E* r
    0          NaN  Z" V& J6 f- {4 d
    1          NaN& C* H5 |1 Q; H6 U5 @% w
    2       Senior# W4 A9 d- ~1 M1 Z' f; ]
    3    Sophomore* N1 A. q+ X  u  R: g
    4    Sophomore
    , m+ G- N# d; M1 O/ h+ gName: Grade, dtype: category
    0 E) f' X. u' z8 L2 KCategories (4, object): ['Junior', 'Senior', 'Sophomore', 'Graduate']2 [8 d+ x" Y6 y9 k1 I5 S: c
    17 z' [$ B  v. W: m, x3 Q7 p
    21 X1 {& D$ K5 B* v: m
    3
    1 o  R, m0 K4 {: h+ N7 w3 j3 d4; X: j! y, |0 }1 A
    5
    " m6 Q8 W) ^1 s  n1 _8 j3 N66 ]$ \7 V& ?1 o
    7
    / I; B  P' J3 T3 V1 J, @. g4 ~84 ?/ x# p1 E& B  b
    9- \6 m5 {4 h5 a& w  A$ w' D
    10
    ' j0 O) y% W# n$ W/ T11
    , l( ]7 q  v! V6 Y( G) B2 E127 o1 h, V" x; I* L3 E9 z0 Q! C& k" @
    13
    . |  A1 _1 h+ H% p4 u/ \14
    ' n5 H' H2 V) ~' Xset_categories:直接设置序列的新类别,原来的类别中如果存在元素不属于新类别,那么会被设置为缺失。相当于索引重设。9 c5 Q. M" d. v
    s = s.cat.set_categories(['Sophomore','PhD']) # 新类别为大二学生和博士
    % Q7 d; [4 F) l, p4 L% js.cat.categories3 j0 @. |1 X' ~6 c: l
    Out[16]: Index(['Sophomore', 'PhD'], dtype='object')8 \" s. m3 Q* c1 ]4 C- S2 ~
    + F9 [7 d2 W" P* ?
    s.head()* D7 f3 T2 D0 O* f+ ^( D
    Out[17]: " |  d& L8 I1 @3 \3 n
    0          NaN
      I3 T7 _. \: i( S/ v7 b1          NaN- A+ ~; d$ B! ~
    2          NaN
    7 U* G/ S9 C" O% e) k/ B3    Sophomore, e) v# Q6 [; T, P* H5 ~
    4    Sophomore
      z+ B! r+ M0 L% VName: Grade, dtype: category3 L; W7 `1 @7 Z- z
    Categories (2, object): ['Sophomore', 'PhD']3 \! u  H5 b7 J5 v2 K# F
    1
      g- o; _! i0 k0 @3 s' o# q* |2  H5 d2 i; S1 g$ X) ]
    3
      A5 A* M1 p" H, y42 P. s: T! L" I/ X( B
    55 J# g) w& S+ u9 a0 |& k
    6
    2 V( l' E* q) s8 u+ S7% [1 c6 |9 u2 q
    8* j: A1 Q4 |1 t4 B/ c( r, V8 [) }
    9
    " U& \8 @0 C" ]' h& L10
    7 k# L, X! P* O) V7 E& _7 p112 _) i, ?' v% k: Q2 q$ M. q! P
    12
    5 K/ e7 ?9 R: k0 R# v13
    6 m  y* }9 [" G. v' vremove_unused_categories:删除未出现在序列中的类别6 @# O9 r. O  L3 u5 [
    s = s.cat.remove_unused_categories() # 移除了未出现的博士生类别1 X: }; ?1 y  _2 z' k# }! b0 H
    s.cat.categories
    : O. K3 G3 a8 z! c2 q2 E7 @7 K
    3 C: Q$ l5 ~; o9 Y7 G9 hIndex(['Sophomore'], dtype='object')2 m8 T  {) A6 @. d+ T* y- U3 y' ]5 p
    1
    6 x/ _7 b0 v( L8 m7 M2
    % i  Y- c" e# ~+ ]8 [! h3
    . Y/ j2 y& w! O  Y( C4
    6 \3 S6 t+ j2 F) Krename_categories:修改序列的类别。注意,这个方法会对原序列的对应值也进行相应修改。例如,现在把Sophomore改成中文的本科二年级学生:$ ^; u0 a. W6 D" d& w; D
    s = s.cat.rename_categories({'Sophomore':'本科二年级学生'})+ e  t4 R& M; M1 R' t1 C+ x+ L( g4 Z
    s.head()  l" q0 Q" A6 T. _* X
    # N# E2 Z" ?2 `& H* H5 Z
    0        NaN
    " C4 g! E% s9 W( |9 D( {# a1        NaN
    ' ?$ O+ g+ p" C7 a2        NaN" `) Y/ D$ F8 B* T& K7 H
    3    本科二年级学生
    0 y5 x3 H+ x, B4    本科二年级学生! L6 y& H# @! U4 D
    Name: Grade, dtype: category
    8 _  \! e- Z' Q- n  c6 B7 zCategories (1, object): ['本科二年级学生']( e, ]+ {9 b/ H8 ]
    1
    + {) Y  T* }& R27 {/ f$ e& }+ z+ E( m
    3
    : v  z9 X& ?( \6 t) t4
    : C8 S. |+ D& c9 n9 k7 P0 t3 f5
    0 ]8 `8 D; _4 s& Q! j. ]. C( q6- o  F- x6 f0 k( V1 Y! t9 {
    70 Z* v5 d1 J9 T
    8! I8 }& p6 Z7 \# }
    9% _! r$ t" ~/ s2 B7 B
    10
    ) |6 |- t3 w' D$ Q/ h! }* D* k9.2 有序分类* i. J/ A" `" ^  A" L' V
    9.2.1 序的建立
    6 V/ o$ P1 f; N4 C- u) p) d9 u  有序类别和无序类别可以通过as_unordered和reorder_categories互相转化。reorder_categories传入的参数必须是由当前序列的无序类别构成的列表,不能够新增或减少原先的类别,且必须指定参数ordered=True,否则方法无效。例如,对年级高低进行相对大小的类别划分,然后再恢复无序状态:
    ; Q9 ~# P& ^- s  }+ |
    ! u; W7 W7 N3 ~& F$ Ls = df.Grade.astype('category')
    0 v; z+ D2 ^: @& _& ~7 Ps = s.cat.reorder_categories(['Freshman', 'Sophomore',
    1 W  k# C8 S/ ~& O) W7 R' k0 A$ z                              'Junior', 'Senior'],ordered=True)* Q. E# p# `, [$ w" ^5 g. Z* z
    s.head()
    / b# ?5 q, H3 w$ `/ ^6 \0 HOut[24]:
    + {% G  j) p9 q" P8 g; o0     Freshman
    ' K+ _# r% {8 c8 N' j6 o% @1     Freshman
    / o& Z8 q6 f& F$ O$ I$ d- h6 ?2       Senior: v, j. _9 s9 n" t. n
    3    Sophomore
    ! `! [+ t/ y- y4 t. Z: A4    Sophomore! L4 V+ n" h) o7 m# ~' x
    Name: Grade, dtype: category
    ) z8 g: G* ^% ^: d5 V" K: ]Categories (4, object): ['Freshman' < 'Sophomore' < 'Junior' < 'Senior']( _: P1 y7 W! m3 {
    & D; `# a; ^/ U4 e
    s.cat.as_unordered().head()4 p: O3 W- j! e- ^  w
    Out[25]: 7 v% w: S% Y! S  ]& N
    0     Freshman! ~( ?$ @8 ?8 X
    1     Freshman
    / u8 ~7 f+ ^& p+ n. k2       Senior- B! p6 X, m# s! K4 W4 D
    3    Sophomore5 N8 @( c, J9 ^1 Q, G& P9 C
    4    Sophomore
    % f/ Q% ?, A+ k/ J' RName: Grade, dtype: category* H) ^7 e  ?4 `/ V  j4 i: J
    Categories (4, object): ['Freshman', 'Sophomore', 'Junior', 'Senior']
    # v% Z5 ]# M" y! N
    # K! |% \2 M! c! D1
    + L; M+ p, f; ^; s6 G2
    + _' O+ [+ {* P9 l4 N% W/ f( d3$ }7 ?7 W; E$ |/ b) I- C. S
    4
    5 S1 _8 y/ t- C  N0 |* [53 c3 e( B, O  Q: }
    69 Q6 d8 v" e$ Z- O9 R
    74 p) a7 @+ [5 h' v- `" s
    8
    9 y. ?) m! b" J+ \. f9
    . Z" d7 p9 Z: h8 M# @10
    ' q; z- v' ?3 D( ]118 g. A5 q1 h# j- o/ R( B
    12
    5 H# H. f/ \: g0 w! y/ ?" \" `13
    # |$ M3 D  j) f; \2 Q, C' N14
    % a6 g3 i6 t/ H+ x; V) ?  O15
    & h- N) b+ V- u, `9 ]16+ P9 i2 t* z* l- j" Y) r5 L
    17; B, e$ R4 _6 W
    18$ I' d9 w+ V) r3 @
    19
    $ s, J* W+ X$ l& o0 i, G  Y20& f8 t. E0 [9 @0 B
    211 P5 F8 d, e; S! l! @' n
    22
    . `/ B0 \7 Q9 f! [3 `9 ~% W  如果不想指定ordered=True参数,那么可以先用s.cat.as_ordered()转化为有序类别,再利用reorder_categories进行具体的相对大小调整。9 _! `1 P; S' p/ n4 L

    * O9 ?* @6 u) r" {- _- g9.2.2 排序和比较6 e& {' Y( i# l: q
    在第二章中,曾提到了字符串和数值类型序列的排序。前者按照字母顺序排序,后者按照数值大小排序。& z. m6 T$ @7 {# a: ~% i8 @

    - ]4 q8 V7 l- L& q& ?2 ?2 J/ S; L  分类变量排序,只需把列的类型修改为category后,再赋予相应的大小关系,就能正常地使用sort_index和sort_values。例如,对年级进行排序:
    ! d8 t+ B& K. T$ |" R4 ~
    ) K0 J0 ~+ I' G6 q; [: {df.Grade = df.Grade.astype('category')
    1 n4 e+ Z. F9 i* C5 Z2 w8 sdf.Grade = df.Grade.cat.reorder_categories(['Freshman', 'Sophomore', 'Junior', 'Senior'],ordered=True)
    ' {  \5 u+ [7 V8 y& mdf.sort_values('Grade').head() # 值排序
    # a& V, N3 N- s- G$ t! E  ]" dOut[28]: % b4 y9 U6 {2 j
            Grade           Name  Gender  Height  Weight+ K. N1 p  W3 ^
    0    Freshman   Gaopeng Yang  Female   158.9    46.0  e" l1 q: \2 f0 k
    105  Freshman      Qiang Shi  Female   164.5    52.0
    + Y3 V+ Z9 A2 Z4 x/ @96   Freshman  Changmei Feng  Female   163.8    56.0
    # ~, R, o7 q) D  e9 O5 i88   Freshman   Xiaopeng Han  Female   164.1    53.0
    8 z2 u& K$ m- }" e: Y& D6 L0 n81   Freshman    Yanli Zhang  Female   165.1    52.00 B) `* o8 E% i& W
    , y; k, q, @1 |5 L2 h: W
    df.set_index('Grade').sort_index().head() # 索引排序( L! ~- {' V3 c* s
    Out[29]: . B3 k& A8 L0 h1 F
                       Name  Gender  Height  Weight9 }, {* `/ A' e- g! w
    Grade                                          + s+ Q' A, e/ Y! t- V# V3 _
    Freshman   Gaopeng Yang  Female   158.9    46.0
    " h: x- W! X) M* Q/ z+ @' q% m4 VFreshman      Qiang Shi  Female   164.5    52.08 T  H6 p* i( R. M5 L
    Freshman  Changmei Feng  Female   163.8    56.08 y/ B' O5 k( x- y
    Freshman   Xiaopeng Han  Female   164.1    53.0
      [. h/ J2 x1 e+ A/ l/ FFreshman    Yanli Zhang  Female   165.1    52.06 v5 G1 r) r* P: X7 K
    ; l# T" U( ]9 c9 y
    1$ [5 [' ^; L; u2 K. h
    2/ K, [5 h$ B: q/ _! ^6 B' d
    39 Z2 E6 z$ N* I8 R. q" k2 P
    4' Q8 I/ B, l  l$ p% y
    5/ O& ^6 r0 s" w: {# r
    67 O2 o/ ~2 k) J3 [, S
    7- `9 T% i$ b. V& E) W
    8
    / P9 `1 ~7 j  G7 ]9# C5 u/ S7 `* r  w3 w
    10
    & l. |0 Q- N# f11
    % }' V1 a2 W! ^- V4 k, M" u12. R( `' c# B; j) u3 @
    13  I8 v" ?) m2 q* [7 i- Q
    14  Z) p* O# b  _8 P6 G
    15: `8 R( n% D+ {, _, c
    16" b3 U3 T9 q' J
    17
    . }3 L  w* o) F6 p0 [18
    2 `! n. j" t' N1 t, @1 [! a/ k19
    : g( I8 A% k& k$ ]' L, A# h20% {& b" F6 j. ~6 p+ m/ w
      由于序的建立,因此就可以进行比较操作,方便后续索引操作。分类变量的比较操作分为两类:' L! i$ o4 p+ o* w# d% y
    * ~/ g% _) D" F8 M6 R
    ==或!=关系的比较,比较的对象可以是标量或者同长度的Series(或list)。(无序时也可以比较)0 C2 j$ P% T) N( u" A4 @
    >,>=,<,<=四类大小关系的比较,比较的对象和第一种类似,但是所有参与比较的元素必须属于原序列的categories,同时要和原序列具有相同的索引。
    5 o4 j0 g! g7 ~1 e1 xres1 = df.Grade == 'Sophomore'! l, T) I3 I% g5 y* x( c9 ?+ M, n
    1 L  f% s8 Z$ w/ v( x, t  U
    res1.head()
    % u7 N! i& ]/ m/ I1 K  y9 KOut[31]:
    * ^4 R% z$ o* S3 V0    False% [  L* V0 s& h3 N) D0 b. I6 L% S
    1    False8 ^! O" ~5 ~6 P6 f
    2    False8 O6 G5 d$ L! b7 m5 ^# f' O
    3     True5 f0 g% M' l' d0 L( N$ U. b
    4     True8 @0 L  ]# Z# h8 n5 J; [; E% n
    Name: Grade, dtype: bool
    5 p4 w" t/ m3 K/ k  q# X! F
    2 o( B" t* h7 mres2 = df.Grade == ['PhD']*df.shape[0]
    . B6 I9 p- h* F: y$ s6 X. R
    6 e! {  S) o( _- q7 U% o% Lres2.head()4 }& [5 Y7 S& ]
    Out[33]: & _1 |' Z/ C, b1 Q' C
    0    False: @* q2 X& u7 x
    1    False# R  {$ J) j- P0 p4 f
    2    False- z/ i8 u4 W8 `1 r: I3 F  E- @
    3    False
    ! F" X; y- f/ r- I  y( }( j7 D4    False
    # N, Z  F- O( t  Z) D! LName: Grade, dtype: bool! w% q) q+ |: l' v( S# `2 ^

    ! |0 j7 v: |- o3 g0 ~/ t8 ~: Kres3 = df.Grade <= 'Sophomore'% c, q* W7 `- l1 Q3 G- G) i

    ; l5 e, X5 P! _: {res3.head()
    2 P9 E. n: Z9 M# k6 g! W5 zOut[35]: 8 u. M+ A* s1 W$ q( J, B
    0     True
    : H1 a3 f, W& {+ ^% U1     True
    8 r" j* X9 A1 }) }( i" R* w: n2    False
    " h3 e- ^/ }) I2 u6 o3     True
    # O& L7 K1 {8 M' z( G. E4     True6 b6 A5 E) t9 ~6 ^$ \
    Name: Grade, dtype: bool
    8 d$ z5 |# q7 b8 b9 ~( t; I- u6 Q' j$ N. V, X1 V6 N; P
    # sample(frac=1)表示将序列随机打乱。打乱之后索引也是乱序的,直接比较会出错,必须重置索引。) m) Q$ ~  N: p% O
    res4 = df.Grade <= df.Grade.sample(frac=1).reset_index(drop=True)
    $ o1 i6 a, ?9 Z# K- I, C2 Z) U
    res4.head()$ O$ I: x" s; V3 _
    Out[37]: # F" r! t3 p. i
    0     True! a4 D+ Z. X! t. y8 C
    1     True- y1 @' Y# f* I% R7 B
    2    False, P+ n: a4 e6 [0 g. [6 H
    3     True
    , ^9 ?7 k# }  s& b6 g4     True& \3 q2 D. f, G+ u0 Y& B
    Name: Grade, dtype: bool
    5 a  b: O5 V" u7 E  N5 [4 k4 J2 M# W3 L. ?  w
    1
    / w6 C+ N) b. I( @2
    . F) s4 [3 {" D30 k) L% _- _- H4 Z3 ?; b
    47 C# H- U, ^7 T& H
    50 K: S# G# N! n- v( K
    60 Q- u+ z  Q1 b2 W  C' J. N
    7  ^; @4 ^" N" j6 C! s4 T
    8; Y2 d8 \; o2 I
    9
    ( q' [4 Y+ D2 ^7 M% O0 R$ Q103 o6 P- a2 W' y; j- B4 l( D
    11. K6 [# N' H  t; ^3 t4 M
    12, Q4 P# }* I$ g+ b7 P8 o
    139 P# c# l7 y) C5 f
    14& T% a7 X9 b5 i$ r+ B
    15
    3 @6 l4 r0 L" a. W! J0 Z16
    ; ^* L+ W  N7 Q17/ n0 a5 e4 {0 g) O; g4 S
    18) P0 _8 {* r: y6 y' `
    19
    3 e, Z7 d7 S1 x0 f7 H$ i- ^20
    6 R/ g5 C  E5 K+ p% \8 P$ g8 D/ P21
    1 J6 L+ Y: l7 W2 E: _22
    ( G$ W) `( G1 A6 n6 H: u  c/ P0 l234 Y& g7 Z9 t1 {" r( l- e7 p" U
    24
    8 k( f+ C3 P# [8 {- s25: n# Y/ t5 _/ u' L+ C
    263 d* U: B5 r2 z( D
    27
    0 u, l7 {/ ^. D8 F" Q6 W28! m9 Q1 ?. C9 g3 t9 x' {8 {
    29
    ) Y5 z( ^3 ^. o, w2 \- I30
    8 q' i4 [: j# y( E31
    # n' e& H/ ^! ^( g32
    7 z* i/ D) [6 _$ {33
    3 s) I. y+ ]  W, p. i34" X: [. N- ^2 u' b1 G# o% S
    35$ M4 R# s0 ~6 u- D5 ?7 M
    36) a# {/ }& v9 A! s: J
    37! M! {+ n7 [8 i
    38$ g% ^3 r* `: R
    39
    9 X8 P( h5 T3 {4 }* l' L6 U  P40" w1 D5 ]+ g+ Y; P  A# e$ X/ |
    41
      N( @1 O6 ^& H+ k& v* n( D42. q" f3 D$ f% a$ g
    43* K$ m( [! X/ V! l
    44* i8 q( S, S9 H- H- `4 }( T
    9.3 区间类别0 B9 {! P- U6 O. y' e1 f: i; U$ ?
    9.3.1 利用cut和qcut进行区间构造
    4 b' r/ G/ H0 W/ z1 S  区间是一种特殊的类别,在实际数据分析中,区间序列往往是通过cut和qcut方法进行构造的,这两个函数能够把原序列的数值特征进行装箱,即用区间位置来代替原来的具体数值。# Y2 C+ @2 n2 T, S" ~
    4 H4 L7 t) A$ m
    cut函数常用参数有:7 r$ o7 U* E2 \. j
    bins:最重要的参数。. H8 t- j  [  p
    如果传入整数n,则表示把整个传入数组按照最大和最小值等间距地分为n段。默认right=True,即区间是左开右闭,需要在调整时把最小值包含进去。(在pandas中的解决方案是在值最小的区间左端点再减去0.001*(max-min)。)0 |' z9 O3 ]( y) ^
    也可以传入列表,表示按指定区间分割点分割。+ X: A" `. `7 |' |
      如果对序列[1,2]划分为2个箱子时,第一个箱子的范围(0.999,1.5],第二个箱子的范围是(1.5,2]。
    % k( q. x; F# |: b4 H  如果需要指定区间为左闭右开,需要把right参数设置为False,相应的区间调整方法是在值最大的区间右端点再加上0.001*(max-min)。7 I, u5 h2 s) m& u
    7 ]" h& m3 ~: @# [
    s = pd.Series([1,2])
    ( \0 ~) N6 t; x  P# bin传入整数& W  ]% o! z* |  s
    ! x( U7 t  T" \5 k& `9 `: L9 b6 M
    pd.cut(s, bins=2)
    7 a" F% c& u  D! W0 n6 J6 x5 x; yOut[39]:
    9 ^4 M# z# w) w3 c( L0    (0.999, 1.5]( K4 I! N( w0 N* v7 D  B8 g$ d3 [
    1      (1.5, 2.0]* V1 ]; N' O- W) L& S
    dtype: category. }. T6 S- V6 j
    Categories (2, interval[float64]): [(0.999, 1.5] < (1.5, 2.0]]- v3 ^+ E* A: {% n2 l/ S; \/ Q

    9 d0 |- n) r- b$ J& ipd.cut(s, bins=2, right=False)
    : f, Z. n: |( x# o8 `Out[40]: ' H6 v' M, t" p( q8 q1 L
    0      [1.0, 1.5)
    ! X9 m' E. @- q* J1    [1.5, 2.001)
    ! c3 w3 Q1 p! T  n; U0 V# Xdtype: category$ B9 f0 x- D. m' C* w" z
    Categories (2, interval[float64]): [[1.0, 1.5) < [1.5, 2.001)]2 C. z7 Y7 Z( m$ q
    ) f+ A$ y1 \2 S- o: t
    1 |: m# u) i9 T' K
    # bin传入分割点列表(使用`np.infty`可以表示无穷大):
    6 j: H- ]5 a0 \4 J$ Npd.cut(s, bins=[-np.infty, 1.2, 1.8, 2.2, np.infty])
    * K1 R2 D2 T( ~$ zOut[41]:
    1 _3 p" S, J6 g7 T, h0    (-inf, 1.2]
    7 {- `( r% B2 H7 R( t( G3 }1     (1.8, 2.2]
    : x' n+ P4 i3 M2 T7 P: _dtype: category
    , [  a0 }# n# ?2 D, s/ A- ]Categories (4, interval[float64]): [(-inf, 1.2] < (1.2, 1.8] < (1.8, 2.2] < (2.2, inf]]
    4 `8 E( k+ V3 m( X* s- O3 z2 v0 \! t$ F# [5 X
    1
    * n4 k) ~6 x, E0 f4 i21 L2 g+ T9 j9 u+ C1 ^
    3* }0 H2 G+ q5 f8 F/ n1 F
    4
      Z. N- n, K: S7 }5
    : E- q  L# c/ B, V8 ]* `6. k" P6 w, w1 h" B7 m: _" K
    7
    ( h* D/ P' h/ r* [; w! R' q; N8/ A  N1 D, x& r! B
    9
    . z; l# q7 V9 n3 |5 t& h10
    $ I' w' z& i% b# P110 v3 _9 m" j* M7 I% _6 |# x
    12. j2 h5 g3 D2 C% R& T% q
    13$ M7 m4 M2 E4 c; ~/ y
    14
    % \, O" y% \: K# N/ c* l15
    8 |/ k- A* K* R) N8 Q8 k9 M16, m  d* X$ @: f8 s
    17
    $ `& P2 v& ?; U6 ^* o+ X18
    2 B: Z5 K! V, g5 J, q; ]19( z) B' q. {; J! M# Z4 D$ e
    20$ Z' [" f4 f/ S) s/ P
    21
    # Z, o) J- N5 j- y22
    * t3 o; }( p* I23
    + G+ ^4 q& i' n# a' f24' q! h2 |* b# c; \9 w: \
    25
    1 V" j' y# ]! q3 blabels:区间的名字
    # i' ~8 s, r: A- t5 w  k0 X5 [' v7 K; Bretbins:是否返回分割点(默认不返回)& d, l# n) o/ U8 [3 K# l, e# s
    默认retbins=Flase时,返回每个元素所属区间的列表: J2 c5 m5 v( `
    retbins=True时,返回的是元组,两个元素分别是元素所属区间和分割点。所属区间可再次用索引取值
    + M0 k* j. A0 F* G
    - [; [8 J2 d) n; Y3 g$ x6 Q6 r; Hs = df.Weight6 y% U- v) k% ]) m
    res = pd.cut(s, bins=3, labels=['small', 'mid','big'],retbins=True)& n6 q5 B/ }, U9 l/ W) r( P
    res[0][:2]0 K* p! {: |8 `1 Y2 e( I3 I/ B. W5 [
    + J4 U( w3 {! c- X. h; P  u
    Out[44]: * K& j2 \, x: U" z8 R2 S
    0    small) v: x& v" l7 r" S
    1      big
    ) V5 s. N; u; |" p9 d! W' Adtype: category
    ! B1 t5 u7 w$ o; |& l& UCategories (2, object): ['small' < 'big']
    1 l% A1 {3 E+ U9 H/ p7 l; o6 g2 {8 ?
    9 i; S5 R3 E, x+ I. b, G9 y0 G$ ]res[1] # 该元素为返回的分割点+ r, {/ G# u+ m( \  Z2 z" l
    Out[45]: array([0.999, 1.5  , 2.   ])
    ! k8 Q. p* C: p" F! }) w; i# |! k1
    ! U$ z: q" e" `8 _2
    4 g8 o% b5 s/ K3
    6 N  l% t: e( {4
    3 E3 H- B+ v6 X/ u/ }- [5
    ' x- e4 Q7 A% p! y6/ z# ~( M* v' a
    7
    : _* n1 ]$ }7 ^( U& E4 \; R8( I  ?1 y. p4 s5 t3 q6 K
    93 \7 _& D& R' C
    10
    ) N) J/ z# B" j6 t! Q11( G! Q( l# x0 |% @$ C
    12
    9 \  ]+ M" U2 x  [qcut函数。其用法cut几乎没有差别,只是把bins参数变成q参数(quantile)。, a- b4 v+ L; S* S+ i% w+ [! S
    q为整数n时,指按照n等分位数把数据分箱
    3 W; ^! y+ m# Q( S$ x/ Bq为浮点列表时,表示相应的分位数分割点。
    0 p; q9 Q$ p9 g3 ?, Ts = df.Weight
    & y% G/ W& Y" h; `9 d2 Q2 k
    1 B2 Y- B1 Q, r) R, X# Ypd.qcut(s, q=3).head()
    1 u  _; U, q" D; _# YOut[47]:
    % c+ ?, l) }; N- U- `( z( P0    (33.999, 48.0]
    " [! l6 f; a' E  e: C# s1      (55.0, 89.0]* D" Z; O9 g. ]/ J% V& S
    2      (55.0, 89.0]  b, G( u6 z5 H" x% |' Z
    3    (33.999, 48.0]
      k4 w9 Z0 t$ F4      (55.0, 89.0]
    # _4 a; _& v% }0 |9 A9 C3 f0 KName: Weight, dtype: category2 L# |+ o# [6 B
    Categories (3, interval[float64]): [(33.999, 48.0] < (48.0, 55.0] < (55.0, 89.0]]
    8 C/ I: K: w2 e% n. `: O
    / B  M3 f3 n' Kpd.qcut(s, q=[0,0.2,0.8,1]).head()
    # J! F! e3 U" Y3 m0 WOut[48]: - H6 M! t* c! c/ \8 U+ G
    0      (44.0, 69.4]
    4 I4 B1 ~4 Z  }" \1      (69.4, 89.0]: O0 S* `) ^/ B5 a; t! v2 }' M
    2      (69.4, 89.0]
    5 d3 C8 G# H9 Q  t) ^5 f# V. I3    (33.999, 44.0]
    8 ~$ X* f  E# o0 j  s) o- D4      (69.4, 89.0]9 ^$ u* b# M. E& r# V" w, p0 o/ H
    Name: Weight, dtype: category
    , V  u" ~. f) e4 b9 RCategories (3, interval[float64]): [(33.999, 44.0] < (44.0, 69.4] < (69.4, 89.0]]: n! q* ?0 M9 D5 r

    8 l+ P1 e% B$ ~- O/ X1( m9 \. R$ L, s6 j8 O1 a  R
    2
    5 o  H+ B8 Q$ n1 V39 X) a$ \: X6 Q8 g
    4' d: q: H- @5 b5 d/ P" [
    5, h# F# T+ ], ]& ^3 d* V
    6% u, B) L# N- A/ x6 d, }9 v
    70 M2 l! j2 @) O) y# K  ^
    8
    3 K- ?: ?5 X7 {* ]9
    ; ?1 @! r1 |1 `, N  F+ d10
    ) U1 ]: G5 q  r3 G11
    : `5 j, K; h% K* p' o1 s8 [120 s6 H5 h9 y1 e8 d! I1 I1 H& O
    13
    * w( i' M9 C+ r# P( F3 f1 o14
    * }* V! u( s- |( q: c15
    , p  y% q4 @6 S0 M7 }7 S16
    # k+ U- {. V, P* G173 ^9 N/ O- l, L/ I8 E5 t
    185 P* C# f! j) Y
    194 F5 F+ v* B8 S# C) j7 W1 T
    20
    / |* R* d( B3 v, w5 S21
    % d3 {7 i! e, x5 ^3 h- d9.3.2 一般区间的构造
    4 B; ^7 ^! M6 O  y! Z  pandas的单个区间用Interval表示,对于某一个具体的区间而言,其具备三个要素,即左端点、右端点和端点的开闭状态。3 L8 v% J# ^$ g  f9 T
    1 U- a; o) \1 |
    开闭状态:包含四种,即right(左开右闭), left(左闭右开), both(两边都闭), neither(两边都开)。2 E& f5 h, W) r3 s% ?, c6 A" U9 C' e
    my_interval = pd.Interval(0, 1, 'right')1 v2 H- L6 l+ c" Q' M! R# z
    8 n1 U' U5 T/ i6 z0 z2 I% K
    my_interval9 c" p  G+ F$ g* X: x
    Out[50]: Interval(0, 1, closed='right')! F( L. \) V! p: M% i
    17 E: |, V* @+ d! I) Q  f( q6 G
    2$ |2 a+ J$ l/ z9 H9 s0 P' p1 W
    39 ~8 h4 N; o" P
    4
    . N5 d  R( @5 d7 C区间属性:包含left,mid,right,length,closed,,分别表示左中右端点、长度和开闭状态。! H* W7 [" W- V, w9 G% l( X& X
    使用in可以判断元素是否属于区间; j) s% U7 A- ?: l, N
    用overlaps可以判断两个区间是否有交集:: L; R3 U; e2 c' m4 Z. k
    0.5 in my_interval
    7 i5 A# _/ @( X& f% l5 T1 f4 ]7 q3 e  H. C* x4 Y9 i
    True; q' S1 K# w8 b2 d
    1
    # ~" ^& X& z& G1 J# V2 E2! R* I5 I9 Z1 D8 U5 {4 ?% v4 [
    30 V( i, @4 ~9 t  y* Q; M
    my_interval_2 = pd.Interval(0.5, 1.5, 'left')
    8 D: a2 s7 \0 g2 {7 P: u( K0 r6 S9 Umy_interval.overlaps(my_interval_2): A5 J1 A! F5 N

    & b- j% U  F+ o/ a0 I+ zTrue5 @/ ~  A( F- u5 l* _
    1$ b! K" p$ A  X' N4 W: [+ T
    28 s& B; ~1 f4 N1 w8 Z
    3. ~( g# R7 J* A9 J9 l
    4
    & v3 @% y% g  b1 ~8 e" k  pd.IntervalIndex对象有四类方法生成,分别是from_breaks, from_arrays, from_tuples, interval_range,它们分别应用于不同的情况:
    ' [- s3 O/ Y; K  @( t
    9 Z/ J# q% e1 H* P, R9 e! Dfrom_breaks:类似于cut或qcut函数,只不过后两个是通过计算得到的分割点,而前者是直接传入自定义的分割点:! m0 R% [; F8 n1 @
    pd.IntervalIndex.from_breaks([1,3,6,10], closed='both')
    . P  P% Z) S7 ^2 ~/ M1 V; D; P  o: ^
    IntervalIndex([[1, 3], [3, 6], [6, 10]],
    1 }) _3 x% v7 Y7 W7 _               closed='both',
    5 J& Y9 t: P! `+ w- k2 @9 W- U               dtype='interval[int64]')
    / n; r0 ~: r  G: q1
      b2 H* `# }; c6 \2 u9 T9 @28 J4 G- {2 P% }: `0 I
    3
    ! s9 R" s0 K- t4 M5 V40 u# m9 Q' R+ j2 K3 e2 T: @' H
    5" t. A/ P6 r6 }2 z/ l6 v
    from_arrays:分别传入左端点和右端点的列表,适用于有交集并且知道起点和终点的情况:
    8 ^0 G7 g/ o; }4 r5 wpd.IntervalIndex.from_arrays(left = [1,3,6,10], right = [5,4,9,11], closed = 'neither')
    0 g$ t, ^. `, Y0 a4 }+ c& {, @. i- p
    9 _$ [' B: l1 p. ?7 d. s+ {IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],' V5 z0 M( [# l: I& t' s2 a
                      closed='neither',% l$ D3 n, T+ g
                      dtype='interval[int64]')
    . y' _! P4 K7 A8 {% m1
    9 o1 a; n: h: {- n" f* y& G1 Q) d# J2; P9 l* N' r2 ~; x
    35 l, b! r8 h+ x/ z% L* X7 s0 d: L2 U
    4
    4 ~4 k$ L$ _0 x5: R* v2 ?4 b' H' `2 H2 q
    from_tuples:传入起点和终点元组构成的列表:, \& m+ _9 \+ f8 R5 N, s' [9 [+ a
    pd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)], closed='neither')) w/ d+ U& D/ y$ T7 d

    ( G6 O$ R9 T4 _IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],
    / m1 M7 H8 Z0 G% l5 g3 x0 @: h              closed='neither',) @& S8 |* n# f8 T; m, M$ V
                  dtype='interval[int64]')5 x% R7 u$ J  p2 h% A
    14 a( O0 n  s  M* G5 e, e+ D2 m
    2
    / W* h  u3 u9 ~+ D" a9 ~38 }) u( T1 m2 w, f
    43 d# I: M7 G3 g+ ^
    5
    6 x6 ?( c) q% y7 S0 w0 G- D/ S& k1 finterval_range:生成等差区间。其参数有四个:start, end, periods, freq。分别表示等差区间的起点、终点、区间个数和区间长度。其中三个量确定的情况下,剩下一个量就确定了,从而就能构造出相应的区间:: E) s# B/ t) B; j% S
    pd.interval_range(start=1,end=5,periods=8) # 启起点终点和区间个数
    0 H1 _, V+ r3 J2 QOut[57]:
    / j4 l+ V5 _4 }% G7 xIntervalIndex([(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]],* Y5 a: ?1 H% _3 P3 x0 c) b- P
                  closed='right',
      L- G$ t' |7 Z2 y. W              dtype='interval[float64]')
    " F! X% F8 \* j- ]4 e
    . X5 b" ~/ p4 Y$ s0 W: Dpd.interval_range(end=5,periods=8,freq=0.5) # 启起点终点和区间长度( Q5 O! Z* r8 G9 m- }' v( k3 s7 a
    Out[58]: , f+ \! Z0 d% o" Q8 r  U! P
    IntervalIndex([(1.0, 1.5], (1.5, 2.0], (2.0, 2.5], (2.5, 3.0], (3.0, 3.5], (3.5, 4.0], (4.0, 4.5], (4.5, 5.0]],3 M! F5 g9 G) q
                  closed='right',1 i: L: _3 v7 t- }! j
                  dtype='interval[float64]')' F: Z# a) W8 a  o& Z
    1
    : |4 }! a) @$ A0 J! I  a/ S+ Y2; d* @' p- e0 B7 [* n5 ]
    3
    , d4 Y6 _0 q4 b# \% E4. R/ {9 [: v3 X+ X" c% @6 {
    54 }; w) @( o( |4 r( K
    6
      ]1 c0 o2 ]$ a9 d5 S7 y$ z  d7& V9 A/ g/ I" Z' B- [5 r" R& j' H
    89 W3 g: g. H. c% u$ x4 [' o
    9) D6 H; ^* r) @! i, W  ~
    10
    # Y( t, W& x" E0 S; n: s11$ @& G2 j2 F' H' k. D# `( h
    【练一练】' X+ F4 T' s" W8 j
      无论是interval_range还是下一章时间序列中的date_range都是给定了等差序列中四要素中的三个,从而确定整个序列。请回顾等差数列中的首项、末项、项数和公差的联系,写出interval_range中四个参数之间的恒等关系。
    4 K! V2 X8 X* o; A; k: \& c- D- M. L$ `# K2 A2 Y
      除此之外,如果直接使用pd.IntervalIndex([...], closed=...),把Interval类型的列表组成传入其中转为区间索引,那么所有的区间会被强制转为指定的closed类型,因为pd.IntervalIndex只允许存放同一种开闭区间的Interval对象。
    / x4 a" b5 K* Y9 }9 d9 b* m  `
      @- T7 V, _5 o  {( A7 }5 Omy_interval& Z' A+ G4 V3 S6 \" ]) |
    Out[59]: Interval(0, 1, closed='right')- N2 u; s  ]5 {3 @5 h/ s6 l
    $ ^/ R  |1 S5 a  `
    my_interval_2
    4 z$ o( d' ^9 a8 V, J5 o! M: |+ kOut[60]: Interval(0.5, 1.5, closed='left')2 ]1 l. C' R- j# x" l; K6 x* w

    , a8 w7 n( r2 R! jpd.IntervalIndex([my_interval, my_interval_2], closed='left')
    # q* C. l1 G6 f4 X5 iOut[61]: - T0 ]  d2 v6 X( S6 O2 E
    IntervalIndex([[0.0, 1.0), [0.5, 1.5)],
    5 n! x: Z" p$ ~, \  f              closed='left',3 A" u, D0 H$ \
                  dtype='interval[float64]')
    / e) U+ W5 g( U4 U: @1
    2 @3 ~8 n$ r0 p- [+ B2
    ) G8 C5 `2 q' w3
    " A& G" j" D$ G4; N& @9 o) K- ]5 f
    5# I  z7 v3 r6 s5 R; R$ L& D
    6
    5 \3 Z( t1 A; I5 m5 ?. b7
      f$ O% m1 E* C, y- @) S8* e0 Y9 l0 e7 C9 M
    9; F; p6 l3 I" k; ~$ i
    10$ a7 a$ ^. q9 ^4 u8 S$ k5 l5 N
    11& O' h, h& n/ e. m6 |
    9.3.3 区间的属性与方法  \) h- p6 k1 m/ j, Y0 q2 J/ r
      IntervalIndex上也定义了一些有用的属性和方法。同时,如果想要具体利用cut或者qcut的结果进行分析,那么需要先将其转为该种索引类型:
    . g/ N4 V6 N( Y0 O, S) @' N: N+ L( _: y: o/ ?4 E4 b( l" ?) F/ w6 L
    s=df.Weight
    : K1 N2 F0 l. Z# @* fid_interval = pd.IntervalIndex(pd.cut(s, 3)) # 返回的是每个元素所属区间,用具体数值(x,y]表示" }8 `$ }* d1 N" \5 \
    id_interval[:3]
    $ Q+ A4 b% g, Z7 ?* g3 P, U* h& h
    ) l2 i5 M: U* QIntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0]],1 K6 a8 V$ m6 |& n$ k
                     closed='right',
    + d9 N0 r4 l$ z9 U* k: c                 name='Weight',
    $ W2 N. y' C/ D; Y                 dtype='interval[float64]')) f& a  s1 U) [, s, n' J) E
    1
    9 R* c  d7 c3 `6 y2, y0 {$ h( q, I9 E  @$ g
    3
    8 @1 m' m  m% d* l8 q4 g2 v4# a- k3 t! S0 ]* D
    5
    - T; z- m1 v, R& U6. C7 @% c3 _: ]6 q! b1 K6 A
    7
    ; I* _, i- A/ c' l% i, }: ~8
    2 J, S; @3 T/ E4 E) \  H4 i, x与单个Interval类型相似,IntervalIndex有若干常用属性:left, right, mid, length,分别表示左右端点、两 点均值和区间长度。/ H) \0 w" z3 ~) Q# b1 V4 b2 W* d
    id_demo = id_interval[:5] # 选出前5个展示( ?: l( l6 L7 H6 p

    ; S6 y/ I3 L7 j2 H, H2 P$ J- \id_demo# m, k9 x& J! N- P* l, U
    Out[64]:
    % \  ?* L2 }+ _- J) z$ _IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0], (33.945, 52.333], (70.667, 89.0]],
    5 \, h1 Z0 Y  j, @5 @              closed='right',! N3 G  ~9 e3 h4 t7 J
                  name='Weight',2 N+ s" [7 B9 g: ~' s& B+ b, [
                  dtype='interval[float64]')
    ( J/ e1 e" ?) ^1 V( a9 U+ T( }* G  x, V& i" I
    id_demo.left # 获取这五个区间的左端点7 d  l6 ~8 U( \' p1 a
    Out[65]: Float64Index([33.945, 52.333, 70.667, 33.945, 70.667], dtype='float64')3 g' W( _- F# x+ D) J: V

    / J: N; Z+ w6 m: n  ~id_demo.right # 获取这五个区间的右端点3 w- u# ?0 `0 T0 f
    Out[66]: Float64Index([52.333, 70.667, 89.0, 52.333, 89.0], dtype='float64')- v, U: I5 ?( R  B9 v& B. F8 l& H
    2 M) P9 h. i2 L; f5 B$ |' Q- |) ^
    id_demo.mid
    ' J3 E7 M0 Z  t' ROut[67]: Float64Index([43.138999999999996, 61.5, 79.8335, 43.138999999999996, 79.8335], dtype='float64')+ ~8 `# |! n% [3 \* v/ \+ w1 u

    + V2 \- ^* u0 K. H+ gid_demo.length
    ) \# D4 R0 `! J, ROut[68]: ) G9 k0 a9 N$ R4 s! ^: z9 i
    Float64Index([18.387999999999998, 18.334000000000003, 18.333,& s: d) A2 \% p; U6 M2 k
                  18.387999999999998, 18.333],0 O0 |- W1 S# }/ p/ M
                 dtype='float64')
    ; a# ^0 @4 j2 _! Y0 W- }5 J2 r. L! k! G9 h& g
    1% Q; V# f6 G* ^
    2
    ( o% X0 u* a0 J) I- ~: y( S4 E3
    " M5 \6 w5 p& e0 p4* N, u& [7 [) y$ r5 l
    5
    & Z' ^: Q0 ]5 d$ S0 J6
    ! E9 m, R: y- u' c2 b70 R" i3 j( q" H+ V) ~" ~( Z- [
    8
    $ X! @" O% j7 a, H9
    5 E! m/ l" H( g4 X' \; o4 t* }10
    4 Q9 G* @4 P. B3 N11
    & S/ _$ B+ t2 ~- _  P% ]' t  I: T12
    9 t% {, F7 U4 H) W' S136 P- U3 W. S# G( }1 a
    14
    . f# f( p: R/ i$ c; ]2 c158 p* Z7 F& v2 X3 U4 b; B, Y0 q
    16
    5 f$ N4 m3 y! A' s3 w. u5 F4 v7 o17
    ; Q+ V: g$ ]; J- C18
    ' m. g, ^: x0 G' L4 A* I# a19
    % x" h( p+ K5 D7 |2 h20; a" b. q0 E3 z8 ?6 a3 o
    21
    ) v" T9 k7 G: a! `22# R1 z7 Z5 O% n0 [
    23' I) M1 z6 W# S2 C+ h- Q* P
    IntervalIndex还有两个常用方法:
    : ?+ O7 {& }, m1 T0 vcontains:逐个判断每个区间是否包含某元素! Q, q; d* L# l# o! v
    overlaps:是否和一个pd.Interval对象有交集。
    / d  \9 y5 `7 |" hid_demo.contains(50)
    2 ]9 s" K2 w5 W1 U; ^( Q6 rOut[69]: array([ True, False, False,  True, False])
    ) j5 V3 y( Z: H4 h3 n0 v. E8 i7 @( w* p0 U
    id_demo.overlaps(pd.Interval(40,60))
    + T6 L. T/ C: cOut[70]: array([ True,  True, False,  True, False])
    2 d1 _$ o( y, ?) L5 X2 R. d1
    % g5 k' e: r  c- k8 y/ o2  c% {# V5 M: K% w; W) \
    3
    " p* Y+ N, u* i2 a. t5 y  c% y4. J( f5 M0 b0 w, A6 o. j5 ?( v& F
    53 j4 [0 J1 G) I1 w7 e- w! U: Y
    9.4 练习- Y( R$ F. U# x' O& o
    Ex1: 统计未出现的类别) q) J3 k2 Y: s) S7 B
      在第五章中介绍了crosstab函数,在默认参数下它能够对两个列的组合出现的频数进行统计汇总:
    ! [  j, W0 `3 |! v9 h) k# I  d0 j. G) ^- ^, _  k
    df = pd.DataFrame({'A':['a','b','c','a'], 'B':['cat','cat','dog','cat']})  q# a" ]# [" @8 X2 o! m' ~
    pd.crosstab(df.A, df.B)* H9 X2 d. [3 J$ k& r4 b" S4 I% S

    * j9 H5 u" i7 Z9 Z7 d" hOut[72]:
    + z* \5 p0 C- Y4 HB  cat  dog6 \9 b- p7 h: ~* _% }  P- m+ u9 p- u
    A         
    ' g# E$ H" {5 l8 na    2    09 m. j" t7 V) J& u5 n) Y* p
    b    1    0
    2 ?. q) J% j+ i9 _% i1 y8 Y- Bc    0    1
    + t( _" [% C: b( f10 K8 z" ]( A$ }  e. X5 @% [
    2- R" ?4 ~& F! [# Q4 M
    32 c* j$ m" Z( T- J! \/ ]
    4
    * I' s( E2 O" D7 _7 O6 _5
    & ~! g5 `* \( z' D" ?! y6. g$ G; W( D! }# Z
    7, u9 A0 J; U0 E7 A. M. f' b7 X
    8& {7 F2 S$ Q: [5 x: M
    9
    5 y, e, t. M" `" E& H  但事实上有些列存储的是分类变量,列中并不一定包含所有的类别,此时如果想要对这些未出现的类别在crosstab结果中也进行汇总,则可以指定dropna参数为False:
    9 f+ }  n, a  Z2 r7 i8 c% o1 R$ M$ _" _8 k
    df.B = df.B.astype('category').cat.add_categories('sheep')
    " b" G7 Y. M1 n/ D. M  W2 f% tpd.crosstab(df.A, df.B, dropna=False)
      u# @# t8 R, I+ ~: \! `$ a7 ]& ]0 A/ i
    Out[74]: , Z5 f- B2 D0 m& y( s( I% |& E
    B  cat  dog  sheep
    + H: \8 {$ W% h4 D* X* a& ?* MA                 6 Z8 M0 x7 W2 F+ ^$ `' ^
    a    2    0      0- K) j) X3 N& y4 c$ c) |
    b    1    0      05 a/ `, G9 j, Q
    c    0    1      0
    6 q0 D6 |9 P3 `2 I% Q& T# S1- p$ e$ c" E& {
    2
    6 }/ E' ^3 s7 P* L2 b  R$ G39 N& d9 Q, o! w7 P
    4
    & ]* [- Q% Z: x6 S1 N5
    # o) R. @. c5 j8 S: I6 k6+ j1 `5 T* g5 w3 [/ e: \
    7
    * V' k6 D$ P, N- ~5 @# i1 c' a; B( h8
    5 F4 k$ B& p: e* W8 ]6 f9
    6 @  P+ b* @- ?, q请实现一个带有dropna参数的my_crosstab函数来完成上面的功能。/ y5 l, S. N# p' ^# ~. Q
    6 }! V1 C+ \: y* |. Q' A0 ?/ d- m
    Ex2: 钻石数据集
    6 T* s( z: Y: I  现有一份关于钻石的数据集,其中carat, cut, clarity, price分别表示克拉重量、切割质量、纯净度和价格,样例如下:
    # \. E8 b+ w1 B7 d, s" ?3 c0 M1 Y& K+ w6 s2 i
    df = pd.read_csv('../data/diamonds.csv')
    / A. x7 Y$ M3 ?+ \8 U7 y* J( d- x/ \df.head(3)4 Y( Q! f; j/ r- k4 a
    / C7 @( d% V/ K1 L8 I: H* a
    Out[76]:
    + u  Y% u7 g, Q3 W  T   carat      cut    clarity  price) w/ H1 ]5 m* s, y
    0   0.23     Ideal     SI2     3266 D: e" D% m' `/ v) v0 D- h
    1   0.21    Premium    SI1     326
    - Y" G+ v& i* f/ Z5 {* @/ W5 F2   0.23     Good      VS1     327
    * Z% p. ^5 ]2 x* k$ B" Z" g# ]; C8 ?& w% a16 e8 f4 n& x- R
    2
      ~4 M" x+ O: {. l3 w' U33 h& G; a* M: p- y
    46 c! K% e2 J, w$ L3 Y3 X6 O3 u3 L
    5
      i3 S% |6 X- x, E: [6
    + U. w. l/ B! r% m7
    , L8 @; c5 m4 {; X* a0 ?% a8
    9 M/ W1 a/ }. {分别对df.cut在object类型和category类型下使用nunique函数,并比较它们的性能。3 y0 y, S8 j8 a/ O
    钻石的切割质量可以分为五个等级,由次到好分别是Fair, Good, Very Good, Premium, Ideal,纯净度有八个等级,由次到好分别是I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF,请对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。7 C) }7 V0 Y: l
    分别采用两种不同的方法,把cut, clarity这两列按照由好到次的顺序,映射到从0到n-1的整数,其中n表示类别的个数。
    ) v- g) S; @. y4 [对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。1 S* T/ ?' g8 l: E# F
    第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
    * B' o; N3 y8 @5 E对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。
    + t9 H$ Z5 M& C4 g7 ^9 L8 p$ ~先看看数据结构:
    7 d+ ?( w  r  o$ ?" n7 k/ \6 a5 S: O+ Y, w  a' F* ?  H+ L) y
    df.info()
    & ~# B0 E* ^  T( [Data columns (total 4 columns):
    ! b! ^$ Y/ V; V9 s6 l# _1 l #   Column   Non-Null Count  Dtype  * a( y. p, E2 n9 f' T: e2 a; `) H
    ---  ------   --------------  -----  , ^; D5 R# L3 A4 E: g9 C% @0 S
    0   carat    53940 non-null  float64$ N, T4 s! r: I* E
    1   cut      53940 non-null  object 3 x& z8 O( Y3 r5 r  k: m/ X
    2   clarity  53940 non-null  object 3 y! P: ]( i0 B! i+ U, s5 s$ J2 j
    3   price    53940 non-null  int64  5 }1 S( V' V/ }, E& P6 p" z( N& m% g2 Q- X
    dtypes: float64(1), int64(1), object(2)
    5 q8 G& a$ |$ d; |  d1
    0 v& K, W& S0 H5 ]+ w2
    + K! x  _% m" S6 K. t( p3+ [1 j  d' Y- N7 z; b
    49 x$ d; ?3 ^4 I; V1 |6 U
    53 m5 u; Y0 w  e
    6% t& {! S! P8 w# I3 U4 b
    7
    5 s- e0 C9 }. R8
    8 W4 V: ]0 G# Z( D9
    % M% o. x7 [  R: D& Y* ]- Y6 Z比较两种操作的性能- n& Z! n) y1 r$ C& Y1 h: W: `; P
    %time df.cut.unique()7 p2 W0 S) d% a1 y3 D4 p3 W

    ! ^! o- D7 M; U9 S7 y9 GWall time: 5.98 ms
    8 T+ q0 f) z& a2 g3 |3 q& |array(['Ideal', 'Premium', 'Good', 'Very Good', 'Fair'], dtype=object)
    5 q. X8 \6 s7 T6 N' ?* V/ L1
    4 P7 y& K  S  C/ l9 k2
    # t- \1 Q- W. A& i3& _- H  v5 W$ n( M
    4
    0 i, q- e8 V' _$ H%time df.cut.astype('category').unique()
    + v% \( u6 U. l) U+ j
    ) U" J+ l6 g. v- yWall time: 8.01 ms  # 转换类型加统计类别,一共8ms
    7 I- M! n1 u% w7 z0 \# M1 z7 V7 S['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    + o* w- N% B* f! e* c7 T- DCategories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    % M8 p0 C6 |" k. o( i1
    % N* J3 L" t9 {! {+ G' m& }25 g: g; x4 C+ e: ]+ y
    3
    & M7 U2 t: h3 i4
    . ^! p' F* `$ r# P, I4 E( O  M+ v5
    $ }+ L. H, j* B' E' h2 @df.cut=df.cut.astype('category')
    + t3 I9 g# d6 h3 a%time df.cut.unique() # 类别属性统计,2ms  q0 ~, d, ]9 o  V4 j" A

    1 n8 L' H# A8 Y1 F* ?, sWall time: 2 ms- a" r+ g: ~7 ~+ _- p6 z
    ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    - S3 s* G$ H* L, m1 [9 a* jCategories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']! p9 O2 J$ T5 z6 r# O2 D; K
    1
    ( w& i' }; n7 V) b2
    ; m6 }, H2 G9 x, [& I+ B: P33 x! E. Z( ^7 a7 g, s
    4) T$ n, D* {1 H* [- n+ [6 v) P
    51 Z( K/ D9 j; c8 g$ R& d" P
    6
    5 e. @2 o5 O- M- e4 Y/ O对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。
    5 B$ J  `, e  i/ h5 d. q/ fls_cut=['Fair', 'Good', 'Very Good', 'Premium', 'Ideal']3 y% _. I! C0 F( C  W4 Y8 F; A
    ls_clarity=['I1','SI2', 'SI1', 'VS2', 'VS1', 'VVS2', 'VVS1', 'IF']
    ( x# ^- H+ A8 f) i7 j5 C9 kdf.cut=df.cut.astype('category').cat.reorder_categories(ls_cut,ordered=True)  # 转换后还是得进行替换
    ! e0 [' Z& ~! ?% V" L) Adf.clarity=df.clarity.astype('category').cat.reorder_categories(ls_clarity,ordered=True)
    & ^$ }/ @/ J' U
    & M' h$ f1 d3 X: Y7 Ldf.sort_values(['cut','clarity'],ascending=[False,True]).head(3)6 }5 ?2 h1 L, x' v: |

    & j; h# U% L; S' Q- Q6 ]+ \        carat         cut        clarity        price" c, A3 o6 i- w5 V
    315        0.96        Ideal          I1        2801
      P' u7 v6 O% O4 Z: T& Z535        0.96        Ideal          I1        2826
    . W- G& V6 w6 A: N" t8 N551        0.97        Ideal          I1        2830
    & |! w1 w0 I: p2 V! r& T- H1# T* N+ h1 m! B) m
    26 i+ v* S% h3 O# o. C- W8 k3 R% \2 a
    3
    ! V  Y; _  o5 N; m; V, f4
    ( H9 J6 }9 I. I/ I5 J5. I& w3 F. `5 V! ^
    6# H. b* W5 j! ?; Z* i, u
    7
    + p3 @$ V$ u1 Y( p83 _( ~/ X& a5 C% p
    94 {& n7 t1 v/ [# p) x. i, O7 }
    102 G+ J8 j& [9 J. I3 {2 X
    11- a' K" ^: I, R4 g# |, S! u1 H# }
    分别采用两种不同的方法,把 cut, clarity 这两列按照 由好到次 的顺序,映射到从0到n-1的整数,其中n表示类别的个数。) Z+ o8 P5 E0 D& y& f
    # 第一种是将类别重命名为整数1 S- B5 s, `, ]1 n6 G1 h
    dict1=dict(zip(ls_cut,[x for x in range (4,-1,-1)]))+ s, n  G7 [' b3 b0 t
    dict2=dict(zip(ls_clarity,[x for x in range (7,-1,-1)]))
    : _* n/ \2 `& h' \* Z5 U3 ^
    7 S3 D/ S% r& p' q# y- Q6 kdf.cut=df.cut.cat.rename_categories(dict1)
    0 F( X# r# \. v0 g  ]  v! L& Wdf.clarity=df.clarity.cat.rename_categories(dict2)
    ' i* k$ q" N9 q8 ?, P5 M1 r$ J- edf.head(3)
    5 z9 K& v, q5 X
    1 k9 E, P/ h( \7 ^6 j        carat        cut        clarity        price
    - r- q4 X. r3 j* `* g# a/ R$ i0        0.23        0          6                326
    2 e9 C* V/ a, V" [- m4 x* {# y( \1        0.21        1          5                326& b9 e) y' }2 o% u, i- w
    2        0.23        3          3                327- S/ y* Q- {9 t9 T4 t- W0 E7 i" p
    1
    0 B% v0 ]9 P2 [3 m+ m2
    1 h! T0 U, n/ X34 Y" ~" l5 O' h3 X
    4, ?3 k+ Q8 K8 e" H" _1 U
    5, s, O) Y6 b5 H+ y
    63 O( ~$ y- l% s0 Q, y6 x
    7
    % e* u+ D4 Y3 m4 I# d8( x, Z9 Q& S( O1 _' U- I) U
    9! @2 d, W! Z6 K8 F
    10  k' D5 N6 \  `5 L7 Z( v
    11
    0 F: U# O  [2 g$ B- ]12
    " o7 ~7 X$ e6 w7 E$ {: _7 J9 [" S% M# 第二种应该是报错object属性,然后直接进行替换* C2 _$ s0 Q4 w; N7 h8 a& J, R
    df = pd.read_csv('data/diamonds.csv')$ U1 s# j2 k+ [, G6 i
    for i,j in enumerate(ls_cut[::-1]):
    8 D% `0 \8 z+ @- v    df.loc[df.cut==j,'cut']=i
    ! o5 k2 G- }# T) K" o6 K6 J" s, q5 F! ~- _& v$ o& J" ~/ m) Y
    for k,l in enumerate(ls_clarity[::-1]):
    : x" q% A4 S! K! R! ]$ W    df.loc[df.clarity==l,'clarity']=k
    3 ^+ `% a  B9 C# R( T5 a2 U3 vdf.head(3)
    ( a' H" b4 n, O% c( ?1 ]8 [; g  d! Y. l8 |% [$ |; M  g' R2 E2 Q' ~
            carat        cut        clarity        price! O+ r6 P9 w( f/ A
    0        0.23        0          6                326
    * f& W& C# A* I  W0 ~: h1        0.21        1          5                3267 p- Z0 J# U+ p# ~* U1 z5 G
    2        0.23        3          3                327
    : U7 ^9 U& Z: K$ j1
    . u  u5 A' }- A2
    8 r2 j- I/ Y" |7 }- b, _3
    2 W/ G( Y8 m& Z' B1 u43 F% [3 f( n3 d) @+ ?& b2 ~
    5$ i$ N( Y8 B, w; n
    6
    ; j$ v0 J( P; k% F+ J+ q: s7
    1 ?3 A5 P- u0 K' g7 `6 k8
      q5 c+ h2 k0 G8 G9' P. P4 B; {( `% C' k2 W% I/ k& K
    100 J' w$ a/ G# \- |/ o' ]
    11
    2 J4 m; {7 x' i% t2 f& r' h12
    * {( m" f. T+ L5 O13, G9 d0 R& p! b
    对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。' s1 J( a' e/ J
    # retbins=True返回的是元组,第一个才是要的序列,第二个元素是分割点% x8 x* t9 D+ M9 Z, k9 V: ?5 Q) Y
    avg=df.price/df.carat
    8 {8 K/ C. r" n6 O) m9 v/ y5 \; w9 ^; |
    2 F. y4 p" ]" M8 qdf['price_quantile']=pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],
    * {1 h9 T- o! z- y                              labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]1 X/ {2 L% Z/ v8 [' ~; a
    $ A$ I9 f/ k2 P6 m/ U4 e- v
    df['price_list']=pd.cut(avg, bins=[-np.infty,1000, 3500, 5500, 18000,np.infty],' G$ k  l' R% c" p, D2 k
                                  labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]
    ( f# H3 z2 p( ?0 P  ^! X/ kdf.head()
    ' h$ v! K2 N4 N2 R1 B
    # {9 F' [5 k% x7 d; T* u3 p        carat        cut         clarity        price        price_quantile        price_list
    6 q7 Z6 @- Q, Q2 a0 U3 `$ g5 f, f0        0.23        0                6                326                        Very Low                Low1 X: c! H' J  F- H  L
    1        0.21        1                5                326                        Very Low                Low
    6 {3 E5 m3 n: }4 m: [0 S2        0.23        3                3                327                        Very Low                Low# s$ t' s- |( ]  ]
    3        0.29        1                4                334                        Very Low                Low- G& f1 `* X. N& ]2 H1 Z& P
    4        0.31        3                6                335                        Very Low                Low                                       
    " E# D. i+ n2 [1 l; {6 ]- |% B
    8 t6 I9 v, C1 A* b! N13 d* Q1 I; f0 K0 L1 e
    24 N# T' K+ i6 a& Q$ i, A
    3# f& e& Z9 t5 d/ N) U, I4 ]
    42 v, ^) a) x$ \6 C- ]# o/ M& P
    5( y; M# U& @. Q& |1 a) k
    6
      D; }: J2 I' A! T; `* g: x7
    : g# R3 p8 w) U; E+ a3 M! w. A8- n5 l8 }8 G0 r) z7 ^
    9
    # `. U0 G2 c& ]10
    , E, i& `% H+ M! A! t% R11
    8 j; y) a. n% h" }$ M0 ~% ~12) O& X3 Y: A3 e2 V5 l
    13/ X! N' S8 B) S5 t* ~, D
    14
    " p* H, l# @3 y1 }15
    + ]7 g8 a7 n! l) _6 i% C8 M16
    $ ^  e$ k: ~) H  T分割点分别是:
      u9 ?- K2 f3 H" B/ X6 v1 B9 ~" p% ]- g8 W1 F. h
    array([ 1051.16 , 2295. ,  3073.29,  4031.68, 5456.34, 17828.84])
    4 _% B8 C8 Z4 ^. ?array([  -inf,   1000.,    3500.,    5500.,   18000.,    inf])
    8 C9 L4 m. E0 ?+ W5 q9 Z8 o1
    / U  [3 ~( Y# I9 |/ I1 N8 I2: Z  Z3 x' t2 j( e- j
    第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
    2 [( K" ~( p/ R( U0 r0 \6 s3 ]8 mdf['price_list'].cat.categories # 原先设定的类别数
    1 K- k8 Y" X7 n) R2 rIndex(['Very Low', 'Low', 'Mid', 'High', 'Very High'], dtype='object')
    ' s1 w; W9 S, g" G
    9 T- ^+ Z! h/ h! ~8 Q7 g; Hdf['price_list'].cat.remove_unused_categories().cat.categories  # 移除未出现的类别
    - Z& @7 m5 h9 q/ ZIndex(['Low', 'Mid', 'High'], dtype='object')  # 首尾两个类别未出现; a0 e" B. ^: {' h
    1
    # o$ k+ \) T) Z; `" l* ?( Q6 o2
    - T3 |7 M5 }8 \+ T$ l3: B* h  W% f/ O" O2 g5 j$ h
    4; s  L6 z  Q+ Y6 B0 o- R
    5/ ]8 y. u6 h3 |9 n8 e
    avg.sort_values() # 可见首尾区间确实是没有的6 M( A+ h9 ?" |. }5 `; \, U; F
    31962     1051.1627912 u1 N% F! t. Q0 I  g
    15        1078.125000' F8 s, Q1 X) ^
    4         1080.6451610 d) B: i, v! O8 u$ R' W
    28285     1109.090909
      j# ?7 m/ ]% E4 ]" D8 ?13        1109.677419
    6 n7 \" P4 U1 o- ]  T. `             ...     
    3 K  N: a+ G$ h1 Q1 e/ \; M+ Y& W26998    16764.705882
    ; N6 U$ x6 {( `# [9 y. Z27457    16928.9719632 @7 f2 {: z9 k3 Q& a+ X# x- Y
    27226    17077.6699032 I" P& Q4 y4 P- M  L) a5 M
    27530    17083.177570
    1 i- R, R/ W$ G/ l. r9 o) M27635    17828.846154
    ' ?0 U: o: K& Z( L1- S  I8 h2 q- K' G8 n
    2) d/ M8 Q2 [: E( d
    3
    0 V, n- P9 B% P2 E41 @4 h2 R" u$ n- z2 r
    5
    0 O# q9 A& M7 t) K$ }6
    & s6 i& `3 J2 \( {7
    " \# b8 F% z  q8
    0 x5 p' n6 V2 A! c9, P$ S1 |! R* h& p: T5 h
    10
    3 k, Y" c9 a# S4 |5 C11
    - E# T% d+ `- Q% z7 O% T) O( U- x12" m: `9 I: z, i' u: R/ Q; X
    对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。& T9 w( A6 B$ O
    # 分割时区间不能有命名,否则字符串传入错误。  a8 s0 W" [6 n7 N5 D0 V; d
    id_interval=pd.IntervalIndex(
    8 Z. u4 \+ B1 \+ |    pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],retbins=True)[0]
    ( V( y# _- N- J0 r' f3 [; {                            )
    1 i& ~; p+ `; _5 _3 _+ U' [5 ~; ~id_interval.left
    : Y5 |& ~" e  a  ]& N, Q; |id_interval.right
    / d4 y# ^# p6 Bid_interval.length                           
    : y; I$ s+ `4 Q5 n( {1
    8 ~$ ^" a! I7 y20 N6 f! t5 g: W4 w
    3
    % ~' N) G- H4 v+ }4 q' N4
    / m+ u) w( I5 ^  p1 G/ `5
    9 ~7 ~: {2 X5 a& Q* }/ F6
    5 s0 n; [* B, L% K3 ^% W78 h  F5 o7 p! \' m
    第十章 时序数据# ?6 W) d; V( L2 V2 i. ?
    import numpy as np
    2 }6 w! Y5 z* }9 Z6 u! jimport pandas as pd2 g" O5 L9 `4 D. T, ^+ z
    1+ ?$ @2 Z& Y% j) v
    2
    + y1 {5 K1 S* n, }7 {% I! G( ~* V2 a: \/ i+ R

    9 _& D. m" x9 V* h. N10.1 时序中的基本对象% k& S: ^( M/ L9 c8 W& w  n0 s
      时间序列的概念在日常生活中十分常见,但对于一个具体的时序事件而言,可以从多个时间对象的角度来描述。例如2020年9月7日周一早上8点整需要到教室上课,这个课会在当天早上10点结束,其中包含了哪些时间概念?+ Y2 \* W7 w# H
    0 Y2 B7 f: T, P3 U6 {' l! I% O
    会出现时间戳(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的简写。+ s- g/ x1 V1 y: @% a7 I: U5 C
    8 G& f9 _& T9 D3 H- t/ d
    会出现时间差(Time deltas)的概念,即上课需要的时间,两个Timestamp做差就得到了时间差,pandas中利用Timedelta来表示。类似的,一系列的时间差就组成了TimedeltaIndex, 而将它放到Series中后,Series的类型就变为了timedelta64[ns]。, c4 y& W0 M, }6 _: {) y0 A4 V/ \
    % D* ~  x5 S' u# s/ e  y0 G$ q* M
    会出现时间段(Time spans)的概念,即在8点到10点这个区间都会持续地在上课,在pandas利用Period来表示。类似的,一系列的时间段就组成了PeriodIndex, 而将它放到Series中后,Series的类型就变为了Period。! ]* ~" C# ~/ w/ o. U9 v* i
    . @% o" Y; r/ W: o  i8 k& Q
    会出现日期偏置(Date offsets)的概念,假设你只知道9月的第一个周一早上8点要去上课,但不知道具体的日期,那么就需要一个类型来处理此类需求。再例如,想要知道2020年9月7日后的第30个工作日是哪一天,那么时间差就解决不了你的问题,从而pandas中的DateOffset就出现了。同时,pandas中没有为一列时间偏置专门设计存储类型,理由也很简单,因为需求比较奇怪,一般来说我们只需要对一批时间特征做一个统一的特殊日期偏置。
    ( {2 P4 [6 ]) d' `: r# I; g! `  ~( d( H9 D1 _' Y/ g! E0 Y
      通过这个简单的例子,就能够容易地总结出官方文档中的这个表格:( s9 X5 Y& G: T% u

    , i/ S, O( S1 M1 c概念        单元素类型        数组类型        pandas数据类型3 b& h9 M0 f* C' o
    Date times        Timestamp        DatetimeIndex        datetime64[ns]% k  F, R; Q, G/ W6 P# @; _
    Time deltas        Timedelta        TimedeltaIndex        timedelta64[ns]; r" ?5 @  J' |7 Q5 d
    Time spans        Period        PeriodIndex        period[freq]5 c1 u) |( p( `: u8 z! F$ C
    Date offsets        DateOffset        None        None" u) p+ }$ }9 u2 f$ }6 |4 ~
      由于时间段对象Period/PeriodIndex的使用频率并不高,因此将不进行讲解,而只涉及时间戳序列、时间差序列和日期偏置的相关内容。# P( Q1 u, x4 ^7 ]8 v

    0 [  w* F" e' c- J: Z! q10.2 时间戳: N2 i  m- E* M) t! @
    10.2.1 Timestamp的构造与属性  _8 W3 s! v8 M) N% a
    单个时间戳的生成利用pd.Timestamp实现,一般而言的常见日期格式都能被成功地转换:6 x7 ~% {. ~* F

    8 K6 L. _8 l. s3 l/ tts = pd.Timestamp('2020/1/1')
    + F7 I9 i1 E0 Q& b8 a+ q0 Z* }, F  K0 j3 W) y% ]/ t
    ts
    0 P$ b; Q0 C* Q* {Out[4]: Timestamp('2020-01-01 00:00:00')2 q! a5 i1 r" b
    / }9 p. a, L# U! r' j; F3 v
    ts = pd.Timestamp('2020-1-1 08:10:30')# D0 v; o  t. v

    ; z8 A( }- y: c# [4 ~3 ]2 {ts
    1 j9 X* Q( Q' ]6 N: dOut[6]: Timestamp('2020-01-01 08:10:30')
    4 _' u2 A: L6 e) X6 t1 ]$ a1$ f( v: ]( R; Z8 ^
    2
    . G2 f% m* p. C" j( G& Z3
    , B  c2 O& J. d4 ^" Q2 {1 n- n* l$ j4  _+ ?. d) m$ f  B* n/ t& z
    5
    8 \  S5 I" S, e( s6
      Y; i) N: i& k! S7
    ! W6 P  T% U" I/ F9 U8/ M, R# ]' x6 [7 ^
    94 w" T- o' ~1 U
    通过year, month, day, hour, min, second可以获取具体的数值:
    % S! H4 F5 B- S# s* R0 U3 }4 k( X
    ts.year
    % @( W8 n; T0 Z9 a, B; eOut[7]: 2020" y; a& u' ?) \: I$ P1 L  P# ]
    : v7 Q( N6 P6 E* h/ E
    ts.month* I; g1 C: W* m/ L! \
    Out[8]: 1' U, p. u4 j" i. C: O9 @( L% V8 Q
    3 ^7 [, j* ^  |# }7 e: W, Y
    ts.day$ n( n/ D( Q, G2 v
    Out[9]: 18 s% S  N8 F( n! y/ Z

    1 Y6 k7 i- x) Z6 ]% zts.hour
    1 o* V& j0 W. c$ LOut[10]: 8$ R+ O$ l; P% `8 }: K
    , [( T; q0 h9 v  a" C( X6 n( h
    ts.minute' Z' X; `6 c+ q4 L5 U3 B/ ^" J) T# B
    Out[11]: 102 U6 `: w' s! b% K+ F

    9 ~# ^/ e+ C6 qts.second
    " p7 z2 ?- F% u: zOut[12]: 30
    : r0 J& x# [- h" `) {* A, O6 H+ [
      d1 j- J! o0 v1+ ?  n$ R+ d: i* j+ d! D
    2
    ; ]; n- k7 R0 ]) F+ K3
    4 P5 R7 j' Y0 {4* J: @% L$ W+ K+ m5 n/ ]& e
    5
    , X' _! W; }" |& {68 v6 g* ^6 _2 i8 r% M
    7
    . o- c# J6 j# B- p: i. \85 t6 m, T& p2 n; b
    9
    ( g9 _# @( b& O' s: s10
    6 B+ h- u6 ]" Z) h8 I; _( M; F3 L11$ f' F2 R9 r9 A! e4 R
    12
    # ]5 \' w/ T3 T5 k! S8 l1 d$ I. U13
    ( @# w  R9 r' _2 c' a- t  t14* ]6 t6 ?' u% f
    15
    6 }/ v, }% A! o  j3 R; W% S' M7 O16
    . p2 P- `4 h( L1 f7 Y: G6 V9 n2 g+ @17
    ( B+ j. \+ g# x2 O& C- ?" j2 t# 获取当前时间
    ! g* r2 Z/ N* A. q2 Bnow=pd.Timestamp.now()
    # `* ?6 z' R1 N$ F17 ~0 |$ ~% N) g6 L
    21 g1 f9 p* o. o0 H3 n3 }' f4 F
    在pandas中,时间戳的最小精度为纳秒ns,由于使用了64位存储,可以表示的时间范围大约可以如下计算:
    : Q' f- \$ P& p0 q  WT 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)1 _( k' l/ _3 @, L
    TimeRange= 6 p% P, j1 C! b5 v
    10 - u  t- X. b% R2 k- t& [
    94 \; e" d( z7 L7 z
    ×60×60×24×365
    - F' `% G* j" }2 g3 a1 O( {* s2 `! j5 e2
    $ d+ K, p! U0 q& M' ?3 H64  J( v* b8 |; m. F* D. z# i1 N

    . D# n+ K' Y& [; w! C6 B' m8 d+ B! e5 w8 ?
    ≈585(Years)
    - l1 y4 n0 {- x) _% o
    2 \6 {  h& p, k3 X5 b通过pd.Timestamp.max和pd.Timestamp.min可以获取时间戳表示的范围,可以看到确实表示的区间年数大小正如上述计算结果:  ?9 `5 O$ \% b6 C; E! y0 \9 j' U! p
      M$ H% L. ]1 b" j' w
    pd.Timestamp.max
    8 ~: m" L! H5 P9 z1 ~/ w- l7 ROut[13]: Timestamp('2262-04-11 23:47:16.854775807')# K) L. \% J* w9 z% ]
    " Y$ b' [" k& d2 y9 ^
    pd.Timestamp.min. c' {: \" b# z6 @' u
    Out[14]: Timestamp('1677-09-21 00:12:43.145225')
    0 X2 G3 Q4 x, G( _: ?: [- U; N( g5 m6 e2 S+ @8 c# i
    pd.Timestamp.max.year - pd.Timestamp.min.year
    % p$ }* o2 U' K5 W7 ~3 M9 y, oOut[15]: 5856 p7 m8 v9 f" M+ o3 a
    1
    ' O$ w5 e/ P" Y, i% I3 Z2
    * I4 T* i7 R/ i& K+ Q- e3
    * ^% [7 z# z8 o. C4 t$ i4
    5 ~8 K! F/ c1 [- n1 V  G5
    9 S: Q; q* v" [7 W# A( ~# R6/ F) x/ L2 |$ f) a3 x
    76 `2 l' u8 s- f% }
    87 w5 U5 c, A) P/ i, z' ]
    10.2.2 Datetime序列的生成5 k6 a- U( i1 S" i
    pandas.to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, utc=None, format=None,
    " o% K( \* p0 ]( J# r                                  exact=True, unit=None, infer_datetime_format=False, origin='unix', cache=True)
    % t- a7 |! l, o' H17 B/ f/ S# @( t2 Z
    2
    3 I, O& Q- e4 b, dpandas.to_datetime将arg转换为日期时间。
    1 l% M5 n7 W1 k* |
    / f8 s1 a9 f3 j1 H7 A) Y! rarg:可以是argint、float、str、datetime、list、tuple、一维数组、Series、DataFrame/dict-like等要转换为日期时间的对象。如果提供了 DataFrame,则该方法至少需要以下列:“年”、“月”、“日”。  I6 g9 ~4 g- ~7 L! ^
    errors:
    3 Y8 i1 b8 f# Y( c- ‘raise’:默认值,无效解析将引发异常
    + X) F, z  i1 r7 x- ?- ‘raise’:无效解析将返回输入
    3 Z; r5 u5 ~% A9 v6 A- U- ‘coerce’:无效解析将被设置为NaT
    ! ]; j6 `5 }4 p  S6 kdayfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析日期,例如“10/11/12”被解析为 2012-11-10。如果无法根据给定的 dayfirst 选项解析分隔日期字符串,会显示警告。2 f) ^6 U- o7 E! z9 s% X1 f
    yearfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析年份,例如“10/11/12”被解析为2010-11-12。无法正确解析时会显示警告。(如果 dayfirst 和 yearfirst 都为 True,则 yearfirst 优先(与 dateutil 相同)。)
    3 L9 O- L% d( a& sutcbool:默认None,控制时区相关的解析、本地化和转换。请参阅:pandas 有关时区转换和本地化的一般文档
    : v- Q- s# A+ J; L, S; J& n# eformat:str格式,默认None。时间戳的格式不满足转换时,可以强制使用format进行匹配。+ ^0 g5 p! z% e: [
    unitstr:默认“ns”。它是arg (D,s,ms,us,ns) 的表示单位,可以是整数或浮点数。这将基于原点。例如,使用 unit=‘ms’ 和 origin=‘unix’ (默认值),这将计算到 unix 开始的毫秒数。
    : n2 F' p1 P# b9 Pto_datetime能够把一列时间戳格式的对象转换成为datetime64[ns]类型的时间序列:
    : A% y3 W( e, s% \pd.to_datetime(['2020-1-1', '2020-1-3', '2020-1-6'])# e3 O$ ?& d& [  E0 d: b1 l0 g

    + D: A# m, q8 \7 ~! J" pDatetimeIndex(['2020-01-01', '2020-01-03', '2020-01-06'], dtype='datetime64[ns]', freq=None)
      o. g. o: }' w9 Q1
    8 U0 R+ v7 b; j24 j' `, c+ `2 l8 l
    37 T' V9 P, M: X2 H% k( [
    在极少数情况,时间戳的格式不满足转换时,可以强制使用format进行匹配:
    ! b) o) T: B& B8 l5 W, b9 a8 C% L2 E0 _& L, U5 i; |
    temp = pd.to_datetime(['2020\\1\\1','2020\\1\\3'],format='%Y\\%m\\%d')* [: O; n; J+ N- c8 Y
    temp
    ) o  ]" a- V  t5 G( Y: {4 J6 x) o9 P1 F  B! j: ~
    DatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)
    8 @8 F/ b- _: v$ W6 {# E1
    ' z, P% M  p0 S; d3 m2
    5 H2 N# H4 I1 q36 U& {* M( C& X& x, [
    4
    0 U/ y8 v1 y/ Z# m8 r  注意上面由于传入的是列表,而非pandas内部的Series,因此返回的是DatetimeIndex,如果想要转为datetime64[ns]的序列,需要显式用Series转化:+ j* i. x; L; [- V7 l

    7 Q1 Y5 @0 ?2 I; |0 K$ m. J8 r* Jpd.Series(temp).head()7 x7 u) e% \% {2 i# W" `) H( ]9 I

    5 i8 p6 I; h/ a0   2020-01-01
    / J& Z" @5 y. N2 h1   2020-01-03
    # T8 n7 ?, f5 P* ?2 Wdtype: datetime64[ns]* G/ N7 t. z- M* t- M
    14 d; _$ V' a" ^4 ~- k
    21 }, c- m( q; q  W1 I
    34 f. u# Z, I  R" q( {; I
    4
    + [! e: T) z+ Q6 k; s" M6 Y7 m53 p( ]: s1 v$ ?9 o8 M( x
    下面的序列本身就是Series,所以不需要再转化。" T$ L8 F- s7 ]. i
    5 `# Q& T8 Q. Y, \
    df = pd.read_csv('../data/learn_pandas.csv')$ }  [% I% K9 `: D0 X" |" B6 h/ q
    s = pd.to_datetime(df.Test_Date)0 W) G( r& Y. y1 Z! {
    s.head()/ A3 i3 V6 G( [( s- g6 H( G+ K
    ( L- \2 r, Z: k9 L8 @9 y2 ~7 S+ r
    0   2019-10-05
    / \1 Y$ ]; a1 B1   2019-09-04' b5 Q, T& X/ e, H* r
    2   2019-09-12. r+ }3 Z. d/ a! A
    3   2020-01-03
    2 d0 [3 o! ~( F0 p9 ]4   2019-11-06% h! s- w: @- A$ h4 [2 F
    Name: Test_Date, dtype: datetime64[ns]/ m/ C* O8 H7 ^; L4 ?
    1- k4 c$ T" J( U* }6 l
    2" x$ c+ u4 Z. D) ^; X
    3# y- A6 A; }% Z8 ?% f
    4
    6 Y2 T) j* p* |0 Y2 U$ L5
    * L  Z; I- m; C3 o6$ v; j# L9 H4 u4 |- t
    7
    ; [$ A# {' z$ R- f8* |' f) j: T6 `# b1 U) r; @
    9
    ( {* s1 |  }+ ^& o7 W% K9 J; X10
    ; n* f# ]. I9 Z, F把表的多列时间属性拼接转为时间序列的to_datetime,此时的列名必须和以下给定的时间关键词列名一致:0 n0 E. g3 X* V9 Q  x9 |
    df_date_cols = pd.DataFrame({'year': [2020, 2020],. H% J4 ^: ~4 V5 V! q! t
                                 'month': [1, 1],, q# T* l6 G6 P6 W! [* z. I0 F7 v
                                 'day': [1, 2],' u/ S# j6 F2 p- U# E
                                 'hour': [10, 20],
    0 F' _$ h( x- ^# j. A                             'minute': [30, 50],
    ) d' d/ Y1 \8 R+ F                             'second': [20, 40]})0 J9 v4 I0 W3 T% O' U
    pd.to_datetime(df_date_cols)
    9 A/ c( _- x7 z$ B, x2 ^/ D+ T# Z; I& |7 c6 k' n
    0   2020-01-01 10:30:20
    0 [, Y1 i* B( j2 ]% z1   2020-01-02 20:50:40  \6 j7 E$ J! n" j. _2 u" `3 _5 S, `
    dtype: datetime64[ns]6 A) y! ], X6 v3 T3 t
    1  O% D. s) D- N
    2
    ' t5 @5 s9 ?( Q4 x& J3( _# s' ]3 ^& v- |0 G2 @
    4
    0 h. U; v2 f: e, ^  O  m, K53 J( \6 p9 a) @  \( R( o6 `1 S& U( C
    6
    # X: Y1 ?" k, ]$ F& _8 V$ |7: ?0 s1 @% X3 ^4 {& _3 R% {
    8
    ' A; d- q$ ^7 U- @9
    7 s( L! `! `" {8 ]1 t3 |( o10! V9 X% X! y* @+ {, A6 l$ c: h" y0 c
    11
    & E/ {* |$ V) w. M* pdate_range是一种生成连续间隔时间的一种方法,其重要的参数为start, end, freq, periods,它们分别表示开始时间,结束时间,时间间隔,时间戳个数。其中,四个中的三个参数决定了,那么剩下的一个就随之确定了。这里要注意,开始或结束日期如果作为端点则它会被包含:% [5 T9 e) h2 x2 E; |6 Y- R
    pd.date_range('2020-1-1','2020-1-21', freq='10D') # 包含
      U; q' T4 L- p" C) h: AOut[25]: DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21'], dtype='datetime64[ns]', freq='10D')5 t. [: b3 H# h$ r; A
    5 ?% v% n: Y9 F( h( K  g0 r5 C* v
    pd.date_range('2020-1-1','2020-2-28', freq='10D')# s7 K" j; x6 I' O
    Out[26]: / F1 [) b. P! g# I( `+ n
    DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21', '2020-01-31',
    # z+ y" T3 L2 |' @               '2020-02-10', '2020-02-20'],$ Y- J0 G% ^1 A
                  dtype='datetime64[ns]', freq='10D')
    2 u: S3 `* Y! A3 k( l7 P$ Q6 [2 U- d) n$ f  I
    pd.date_range('2020-1-1',$ \8 ?6 T( \. h0 T- V
                  '2020-2-28', periods=6) # 由于结束日期无法取到,freq不为10天
    . w& h! s) _5 }5 Z3 u2 b7 M
    % t2 {4 e% s9 X/ i( }' i( C' qOut[27]: & p5 w% V' }2 z# H  V5 w
    DatetimeIndex(['2020-01-01 00:00:00', '2020-01-12 14:24:00',
    9 h- B. X3 m8 m# |               '2020-01-24 04:48:00', '2020-02-04 19:12:00',, \/ k, g: o2 r3 \8 k
                   '2020-02-16 09:36:00', '2020-02-28 00:00:00'],
    , n. E% O! Y7 `2 O              dtype='datetime64[ns]', freq=None)& w2 O2 c  a0 Y8 r
    2 D6 B$ Y- I$ P( ?# c' R# Y
    1$ M4 k1 {4 e' F) o" k1 ]( _+ a
    2
    & X# V7 D5 z& ?8 Q3
    , q0 [3 \: D. V, K# k4; B" y. I( U! X% R
    5- a& g: y, C) ]( o, h, C8 j
    6# C7 \% Z  |# z
    7( e; h" g3 b- D  ^8 \8 k
    8
    4 w+ Z$ t$ Q4 @* p% w9
    1 u. f" v9 T1 H$ s3 M9 }$ ?10
    ; [" H2 S* ?  e2 n& t5 X0 ]11$ w& d2 x) h- t! R0 _% S, s0 y
    122 Z2 |2 ?0 O( h) N" j" d6 J
    13% \4 w+ A+ @' T; E+ P
    145 j: Y8 H7 l$ ]( M; l# A& P# l
    15
    3 h3 F1 W. p( r( k16; F* U% s6 }: Q/ B6 s2 D( P
    17( `! B. \0 P: K" ]
    这里的freq参数与DateOffset对象紧密相关,将在第四节介绍其具体的用法。
    7 o( m1 @5 B! r9 Q. d) _
    * l: F& f* ~, b3 \【练一练】+ G+ W! k. h; X% N& e1 [5 j
    Timestamp上定义了一个value属性,其返回的整数值代表了从1970年1月1日零点到给定时间戳相差的纳秒数,请利用这个属性构造一个随机生成给定日期区间内日期序列的函数。
    2 {& u9 W0 N" K3 x: V, z4 P, s% _6 O2 e: {- u) ^
    ls=['2020-01-01','2020-02-20']$ Y# {  k  y/ ?! j# G" o
    def dates(ls,n):
    : m6 I5 G+ z+ c+ C    min=pd.Timestamp(ls[0]).value/10**9
    ' v  ~0 i9 o% [! b    max=pd.Timestamp(ls[1]).value/10**9
    / {2 V% m# W3 H2 T0 p    times=np.random.randint(min,max+1,n)$ V1 U: b& K" \4 o4 q
        return  pd.to_datetime(times,unit='s')
      T: ~7 N% l2 _; P. Udates(ls,10)
    6 `! r' v, }4 o& H, G5 u5 m4 q1 H* w# t! Z, ^% G0 p$ l
    DatetimeIndex(['2020-02-16 09:25:30', '2020-01-29 07:00:04',. F0 v9 y& p$ O( `5 _
                   '2020-01-21 12:26:02', '2020-02-08 20:34:08',
    4 e+ i# g  V! v9 e: e               '2020-02-15 00:18:33', '2020-02-11 02:18:07',
    " c' H, B  X6 T9 W: r2 Z, Y* v               '2020-01-12 21:48:59', '2020-01-12 00:39:24',
      i. R. I1 \5 g               '2020-02-14 20:55:20', '2020-01-26 15:44:13'],
    / |; [/ z% f( Y+ ^0 [. |              dtype='datetime64[ns]', freq=None), @  |9 I# S+ }" V" R7 p- t
    1& w+ V  m) N- y+ I/ L1 q6 V
    2
    0 J( B* T4 w" I$ \+ t, M3
    2 g, J8 u6 K9 n* y4
      q9 d, N0 D( o% @! M0 [0 g/ b5# K6 ?- r! a" ?+ N/ K
    6% q; a. M# A# R# @
    7
    $ ]; c+ f& t4 ]8 s: w8
    , x* ]( [+ [& s$ N& T95 R0 N9 d; C) Z& v. U+ W
    104 s* z1 P9 K# }3 d& T6 j# I- P" \
    11+ S7 g8 k: d; o' d0 G6 g4 a' F
    124 J. ~) q7 V* w$ l7 \& I
    13( v- A! H' {8 s
    14! [1 C! h& c% G2 E* J
    asfreq:改变序列采样频率的方法,能够根据给定的freq对序列进行类似于reindex的操作:
    9 t) |) i) d3 _s = pd.Series(np.random.rand(5),( l3 e$ l. c  I/ J0 p
                index=pd.to_datetime([  z' Q! D0 Q- v1 ^
                    '2020-1-%d'%i for i in range(1,10,2)]))
    5 p9 G  Z% X. k
    2 A8 B' m9 P% b7 o. z2 d9 q7 H$ e. i0 D7 z3 U$ ~; }) V
    s.head()
    - @1 o2 L/ v$ H2 I; b: {Out[29]:
    2 d+ e, G7 c7 z2020-01-01    0.836578
    " z9 p6 Z7 k( s4 O3 e! s& q2020-01-03    0.678419
    6 ]$ ^! [" k' z9 E8 Q3 J  R2020-01-05    0.7118977 J7 _0 ~; u+ P( {  y0 |
    2020-01-07    0.487429# l7 ~2 A) T6 V$ d2 v
    2020-01-09    0.604705( `. t. @1 B5 @, Q- A& v
    dtype: float64
    , r' O5 @! t6 ~" ~* L' L
    % W: A( F' j) bs.asfreq('D').head()
    2 @- O- w/ C5 P' \& XOut[30]: & i+ [+ u- H2 U7 m& D6 [" {2 Z1 L  |
    2020-01-01    0.836578
    4 ?2 X( _$ ^, \# ]  j+ a2020-01-02         NaN
    ( a' R% g- d& U/ S2020-01-03    0.6784192 A: L, i  h1 h9 [
    2020-01-04         NaN" v. S" f4 w' p' Y  J! W6 f
    2020-01-05    0.711897: M( F- `1 u1 C) ]9 i
    Freq: D, dtype: float64/ @1 I) r; a2 r& x7 t

    / [5 A+ S( f6 O& t& is.asfreq('12H').head()
    ( g! \$ j6 p. QOut[31]: * u. L9 L( O. f# y" j, C/ F4 a% w
    2020-01-01 00:00:00    0.8365787 M; E+ e8 u+ u! x! a
    2020-01-01 12:00:00         NaN
    3 u; R! G6 t* {  M% V) y2020-01-02 00:00:00         NaN
    9 [# M3 {. o' D9 I. g" a9 B2020-01-02 12:00:00         NaN
    1 [5 A, M9 x4 A. u' e2020-01-03 00:00:00    0.678419$ Q: z4 W& k$ T' _- f; O
    Freq: 12H, dtype: float64* `% P& q  R  A7 G& j

    : k- D* k9 A5 Q9 b# r2 ]4 c; `2 A; X" A1
      e1 w2 y& z8 x$ ]0 o2
    4 L; |) c' m8 |7 r; C34 v) m( J7 Z: T$ b
    4
    / V5 N6 k  t5 s" h( A3 e5
    5 ~4 U' ]! _9 H. p7 s6
    + l' s5 I& r/ v+ m* X77 R, o, y( s1 N: s1 G0 z. w" r" `8 C
    8
    & H- f0 D3 q6 m9 O9& s: t. c1 V) @/ m( Z9 e
    10# L4 _# {0 W1 ~4 e1 c0 G
    11
    6 h+ |% C& i2 c! f+ r12' o" f! E2 y6 o6 H+ Q$ o' o* m/ |4 O
    130 r5 G, D/ G! m
    14
    3 }5 Q& R5 ^  e; l15- B% i9 E% ]! ?
    16
      y0 J) x8 T7 i179 Q8 d, b& J+ B& Y+ \
    18
      X; k5 ?0 T, A* g! L193 a, X: p- f5 ~
    20+ A) {7 }& ^( `  i2 o$ x# R, c% k
    21
    : A# v0 v$ S3 j$ b/ A3 R22) a6 U9 ^: p+ a' a. v- C* K& p' G* ]
    23( m. _, m; Y" c" k" F& B. n
    24+ z3 _' O$ K! a0 M
    25
    8 [$ b; R) f" s( {$ ~! K, j26
    1 |. L$ b2 p4 N& l$ Z" V27
      Z$ D- B9 B, I! s$ x28
    , H7 x) S- `: `4 @, L+ s29
    4 u3 k+ \$ d; {2 d30
    3 b/ O1 o) W( t8 a3 W6 N' E& ]31
    & }$ G& |7 F( s8 O' v4 e% H【NOTE】datetime64[ns] 序列的极值与均值6 e/ K) L7 N9 |" [* J& o
      前面提到了datetime64[ns]本质上可以理解为一个整数,即从1970年1月1日零点到给定时间戳相差的纳秒数。所以对于一个datetime64[ns]序列,可以使用max, min, mean,来取得最大时间戳、最小时间戳和“平均”时间戳。
    * P9 B- t5 X& V  h4 n. \$ r' l
    10.2.3 dt对象/ I; Y+ Z$ X3 ^1 T
      如同category, string的序列上定义了cat, str来完成分类数据和文本数据的操作,在时序类型的序列上定义了dt对象来完成许多时间序列的相关操作。这里对于datetime64[ns]类型而言,可以大致分为三类操作:取出时间相关的属性、判断时间戳是否满足条件、取整操作。3 `; w& ^" S1 T/ ~# J1 G8 A4 L0 B" Y- k
    5 f" B4 g/ n1 z
    第一类操作的常用属性包括:date, time, year, month, day, hour, minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter,其中daysinmonth, quarter分别表示该月一共有几天和季度。, l6 F+ Y+ A  ~- ?
    s = pd.Series(pd.date_range('2020-1-1','2020-1-3', freq='D'))" z# f5 A' {/ o# J
    2 p: W# t& u, C* w/ k6 o+ R( j
    s.dt.date
    1 O) y9 J3 L" ^2 M. i, ROut[33]: ! O* U1 ^0 o8 j
    0    2020-01-015 A9 I" G" u  N# L9 p8 ?$ u- T8 ]
    1    2020-01-022 n  I4 j2 E- @$ r9 l
    2    2020-01-038 \1 ]$ z' P; w+ |; x6 r
    dtype: object
    8 b: K, ~- m7 _" q
    ) o, a7 z, b' A5 q# G. z8 j1 P$ Os.dt.time
    + E  r# u7 ?9 c3 D& ^/ _Out[34]:
    , C0 u8 q' M) ]& m) j; A0    00:00:00
    / z: w) L! ]( @$ Z& t1    00:00:00
    + I; e& ^2 ~; V1 n3 g2    00:00:00+ @6 k% u6 l8 Y7 P7 I
    dtype: object
    # Z0 R+ t8 U8 g+ N$ H9 i
    - j" d: N! T0 x. I3 o# o! zs.dt.day
    0 X4 ~% q1 o& WOut[35]: 6 ?2 g; H& z4 P& a
    0    1
    ) h1 r" k  A9 B1    2
    0 E7 r9 P$ G( O( l2 ^/ D2    3; H) M* i' x, b  ~: ?4 V
    dtype: int64
    2 _+ W0 u% J$ P; d' j7 e" [4 k# L3 ]% N0 {: i: Q$ ^2 J6 v
    s.dt.daysinmonth3 o/ t: ]: I+ W# |9 {5 y: J
    Out[36]: + ^  j$ i" y$ X8 O- E
    0    315 u3 I- ]- X8 t3 p# e
    1    31
    8 t3 N; y& o; a  t) z$ d+ |: N2    31% w3 Y% B  K9 U) _4 E5 J
    dtype: int64( O; Q) N% R. ^( j

    3 M) c8 ~$ M7 j$ L# f5 N8 R& W- g1
    2 {/ e. {/ c* r7 q7 U! ^; z2
    * O5 S  E' G0 i  _5 q6 M0 `/ j8 t9 b3
    # i. L3 |; b8 ~3 L/ ^4
    - w, [/ i& _4 [2 u' {* F" w5
    9 ^3 m3 V" x2 g1 @' ?. ?$ x6
    & ~7 i3 a$ N% N7 u  a7% e3 S% H# c; d4 V
    8
    ; @9 Y3 r' q5 b! M6 c/ j/ n9; Q% W$ `$ ^7 a6 \, E3 \
    10( Z- _, C# ~' K. c" b9 E6 O6 N6 E
    11
      y$ y, U7 U8 h% o; q+ ^4 L$ L12( X; W5 g" e2 i0 u" @
    13+ O! Z  }% U4 M* Z* v+ [+ |8 I
    14
    * G# |7 W8 U, {( O8 l157 e+ G2 m* l1 F" P) r
    16
    - E8 F6 k" _0 k5 m17
      V6 F$ _0 A+ q9 [( K5 Q18: v2 N! e" X. ?
    19- q! ]* o6 S4 D! W$ d
    20' ?, w8 ?8 V! O7 T; y. l+ N
    21$ x( {, W9 p% r. p$ N5 ~
    22) v5 [8 o9 @: O$ @# @* `0 h
    23
    ) R+ V% j$ G% J, o( @( b* V6 J24* K& g! ~+ `  a, ^: N% N6 @6 `
    25$ P5 c$ b; O% ]
    26
    . x' q* i8 C( C! b1 t27  f' |6 [' e! D% N. n' o
    28$ X8 Y3 F) o& ~4 p. |2 Q
    29: U0 N) a2 S! T% S5 Y. @
      在这些属性中,经常使用的是dayofweek,它返回了周中的星期情况,周一为0、周二为1,以此类推。此外,还可以通过month_name, day_name返回英文的月名和星期名,注意它们是方法而不是属性:3 L1 O% l( ^8 l) I0 W4 g; P7 q9 @

    # D1 J6 H& i9 w7 N. x1 _) vs.dt.dayofweek
    ( U" b8 ?7 E2 I4 S( r! _/ uOut[37]: 8 |  l9 E# [- K7 Q2 i; b
    0    2
    4 y7 r9 c( p1 r1    3
    & @. V, c* \1 n( c3 R2    4/ @+ Y! S7 M1 v- F6 h
    dtype: int64
    ( ~2 q  C3 T7 Q) n9 N: |, t4 v4 d3 H& {; N
    s.dt.month_name()2 l# g& Q8 |9 e" m0 c
    Out[38]:
    3 F! G1 L- u- x0    January
    " d3 g' N1 |; q, I1    January2 R, B5 i- s# Z0 q  E
    2    January( |! Q3 V0 b8 G* e
    dtype: object
    + [0 J9 v0 r5 A* c9 H: [+ b
    ( J! c% y& H$ Q' A9 ns.dt.day_name()
    + F$ V) I( d1 f2 B" s: d* b3 @  \Out[39]: 4 m/ ^0 E2 [0 Z
    0    Wednesday
    ! o0 C9 ?& k, A3 m: f: \, S1     Thursday1 ^3 V$ v( E0 z  Q
    2       Friday
    ( e6 P& g- I' i+ Q* G# S# c9 H& L6 ~% f/ udtype: object
    ) ?+ r: \1 l1 k: w2 _" S* n0 G! i: V- e* p) _6 P. M1 C8 n- F
    1
    9 ~6 l& A& j( M1 {7 {) a2( `6 p' [6 ~; Z
    3. }( \& o% c9 I$ ?: j0 q( h
    4; k6 U  W! }0 j" S6 T5 T; N
    50 i+ S9 W$ i; w% @+ `
    6
    % X5 q% w3 X1 r  s2 N9 d7
    * t( m3 E" A1 ?& j8. ~! U. g: g' C
    9  u$ _5 I4 a( z9 a/ E
    10
    # A3 `% y" _' ^. P) v7 U" Y+ B11
    3 `. c* n) F3 w  U( F' _125 E4 X; J( _3 ]6 P& Q- O
    13
    % F# M" F' {8 z" q7 o0 }0 {  u14
    5 |( W; C# \) s9 X9 p8 O158 }$ B0 j3 f2 b! k4 t" d
    16
    - o1 h5 H: `' i% Z0 J! l" s178 \) d) b1 }8 f% u  Q' j
    18" y$ q; ^0 y1 x) k% F8 ^! K" b$ h
    19) b2 {$ D2 m+ T+ a9 O' i( C$ B
    20
    * W. k% |" P& a. Y. \第二类判断操作主要用于测试是否为月/季/年的第一天或者最后一天:
    5 k( p% l* Y5 X, i7 R7 Q1 os.dt.is_year_start # 还可选 is_quarter/month_start
    * N( Q" ]& d0 ]' V- ~( d7 S! [Out[40]:
    & a) L" L& a* {9 J! j, R. R* i0     True
    ) D% V/ f$ ~( e3 u6 A# H$ |1    False; }' F1 v6 c8 i4 _
    2    False8 W( X3 d+ V! y1 w6 E& i
    dtype: bool
    " V! f, r6 \( i8 C9 }4 d: M5 \* z
    6 C" q" a; O! V" Hs.dt.is_year_end # 还可选 is_quarter/month_end
    # D: T& Q6 @/ D( y9 J9 P& zOut[41]: - ~" C+ G0 w( D- W* `' j
    0    False
      k5 p( ~  J7 L* A+ i) A, t1    False
    9 z7 o+ c( M& n2 y9 M2    False
    $ N2 _7 j. V9 T9 W2 qdtype: bool
    , T* [6 U- u: A2 D$ B. _1
    2 U4 i: ], J  s3 G2 N* ?! H2
    . E& f8 A- a4 [+ F) G3" {3 n  P7 H$ f- W- p4 {
    4  U& G) I9 U2 C# X# u; E
    5
    & d/ p+ o" A+ E0 I) M6
    + |( `7 W) E% i: h& ?7
    3 L# B- U( O0 e* u5 N1 H' M8
    8 k8 k7 ?' X' x0 l0 _9 |. J9
    8 m/ _$ N0 [( k( L9 z7 Y10
    & N% i0 _+ D5 G" T11
    " x8 L& y% F" j12
    , C' A  X1 X& o0 X  X6 }! Z13  H4 y6 Y& J/ W# w) N
    第三类的取整操作包含round, ceil, floor,它们的公共参数为freq,常用的包括H, min, S(小时、分钟、秒),所有可选的freq可参考此处。
    , Q0 o* V. r9 V& z% D: ~s = pd.Series(pd.date_range('2020-1-1 20:35:00',
    0 N4 W. C. `& B: }4 K) W  ^- U                            '2020-1-1 22:35:00',5 H8 [# u) V5 c0 B( B3 g) U: n* {
                                freq='45min'))
    # F# @# Z: z1 b; F( c: }5 b  W3 q; \" C4 l
    ' A7 |. H5 \+ @
    s/ b1 T) r! v  N' ~8 _1 N2 d& L6 C# _
    Out[43]: - V9 h, t. e0 z+ t2 y- R
    0   2020-01-01 20:35:00; i2 ^2 K. k# E7 i. u  m
    1   2020-01-01 21:20:00; n* o% G0 {0 v" v: J
    2   2020-01-01 22:05:00+ j( J$ N$ v# S$ A6 ]
    dtype: datetime64[ns]
    ' Y  L" k7 z- I& C& U! [0 T' ^
    s.dt.round('1H')
    & w" I- {& z3 wOut[44]:
    0 o; T! b' a5 B2 Z7 W0   2020-01-01 21:00:00/ C3 U) X' Z5 s
    1   2020-01-01 21:00:00' _+ U  r: q; D# E
    2   2020-01-01 22:00:00
    2 `# N( y/ L' P& D& u! I; ldtype: datetime64[ns]
    5 f5 i# h1 d. ~% a  s2 Q' {
    2 k% u! D: i5 d  q3 c* {s.dt.ceil('1H')
    6 F/ s3 g# G9 Y/ Z# U; c8 ^Out[45]:
    - \3 r9 o4 ]* b4 B& A# N0   2020-01-01 21:00:00' V: o" b4 Q: C: ]' X4 R1 [) `/ Y
    1   2020-01-01 22:00:004 U; v6 O$ {* h# H4 }
    2   2020-01-01 23:00:00; J: w) M! \2 x  ^8 B+ F
    dtype: datetime64[ns]
    " O0 G/ b: U! T! c  W6 u$ {
    + d( A6 R  Q" w' y( P  _s.dt.floor('1H')
    ) x) r! S: E/ GOut[46]:
    . J8 t4 C; Q* z0 i* T! n* x: u, }0 P7 u0 L0   2020-01-01 20:00:00" _/ o  {/ h* `0 B4 U
    1   2020-01-01 21:00:00
    # A. ?' z0 s+ O! Z$ e) Y& [2   2020-01-01 22:00:00  h$ Z6 \2 ]& h& a& {( m
    dtype: datetime64[ns]/ w. n5 _5 M- v5 V
    / P: V( U% D! n+ z) s. f  p# n
    1
    / f( L$ j5 E+ O6 Z+ C# I* J2
    : R: K8 l- H, Z8 `2 O+ N$ K3
    / [  D7 K" B, l6 }4$ p$ |3 T: ?8 }  ^! ?
    5
    % w7 f* O% t( F7 t4 v6
    8 j, h! B. f5 ^. h+ u- l# F4 N7( D" `( x2 e0 h1 I
    8
    ! Q/ u8 n+ Z! R' k9
    / L) u4 w8 A, y5 A% z/ `108 l+ W9 O  [* Y% y" V' F' a! U  z# c; f
    11
    , w# N" s# c) \: C12
    - n6 D* L6 M1 m  J5 W) g134 c' R  J4 E2 i" b2 m2 G8 M
    14' e- t6 x+ e7 C; r, N7 j7 ^& K
    15" W! I+ X# @) T
    16
    , s% \: |# x# o% ~/ R1 H17$ U# N  }# x3 j
    182 ]7 V4 Z; K/ p
    19( A+ B) Q& B0 D4 t
    20
    : F5 ^7 Y$ ~4 p/ T21
    * F( p0 ~* f, Y5 l* D+ C( q2 k22
    ( d2 S5 D" g/ N23
    * g/ o8 D/ n3 a+ T1 b5 |24
    + a3 X% Z( u8 [" I3 N25
    : b" P* s7 [; Z# V26
    ; R' f0 p+ B: j* t# Q9 L9 Y27( m  B' d* W% h6 U3 G$ y9 w3 r/ f
    28( H4 k% r# t5 y4 y* O
    29$ y( i0 P" C& S9 P/ y& U6 h, u
    301 f1 O) d- N) x% w( m/ K
    31/ c2 l5 ^& [0 g9 x# Q+ L  X
    32
    4 i9 n/ {( t3 |6 Q10.2.4 时间戳的切片与索引, j/ B' ]. k* O/ i
      一般而言,时间戳序列作为索引使用。如果想要选出某个子时间戳序列,有两种方法:: r% ]# T" L0 [0 O/ }0 ?, r* b
    & F2 [5 u/ F; h3 _, t* j
    利用dt对象和布尔条件联合使用
    * m' Z" }. ~7 Z利用切片,后者常用于连续时间戳。6 C& n$ m6 t3 M: `% B, U
    s = pd.Series(np.random.randint(2,size=366), index=pd.date_range('2020-01-01','2020-12-31'))
    ' s/ b. t0 j( ~$ tidx = pd.Series(s.index).dt
      H7 Y. ]8 I. M' C7 C; I4 h4 x. Fs.head()( A, {/ k& R* n1 @* P$ \
    % q3 d9 K' q( \- n5 ]" k1 Z
    2020-01-01    0& w. e+ n7 }( t
    2020-01-02    10 s0 L  s* y: P8 I, U9 b
    2020-01-03    1
    7 V- q2 P6 X& n# Y/ H" v$ O- \2 f2020-01-04    0
    0 s7 d  i/ S; u; @7 Y' ^2020-01-05    0/ Y% c5 j/ E/ q& F: T3 n# x/ Z
    Freq: D, dtype: int32  {) r, p7 K8 ]* M2 f
    13 j1 l2 j: q1 R2 P- I; Q( j
    2
    3 d9 F2 ^' @9 B% K3: Y2 R0 }* _$ ~" {: z
    4* [' o" R2 o9 l" i% m1 k% z
    5
    ) ~3 V# y8 i! p) C- U6
    9 n5 v. P4 x/ r. H7
    ( J; t( v1 S# P3 @87 o, f5 p6 J3 i
    9, [# n) c% U1 N( T! g6 n
    10
    ! k* d0 q2 B. \Example1:每月的第一天或者最后一天' r1 N+ e  f3 L: T% `8 Q

    & I$ @& f" b% Y3 {s[(idx.is_month_start|idx.is_month_end).values].head() # 必须要写.values
    $ i& L9 f% u& @7 EOut[50]:   e; }, s- x! X* j  F1 v
    2020-01-01    1
    : t( {7 ]) |6 U% s# w4 x2020-01-31    0
    9 I3 \( f/ F& g, [! [# s2020-02-01    1
    ! C8 F1 L) i( I& Z2020-02-29    1
    % E' C8 x8 x, R, S6 Z2020-03-01    09 a5 n8 F) T/ Y# _
    dtype: int32  Z! f2 S9 c) Q8 o: v% s
    1
    / y1 a' a, P8 n, d% _2
    & J% S0 F3 w: ~! t3% ?) c. r$ ~- A% Q# Q" C
    43 Z3 a9 z7 M0 J% P+ T# T8 P
    5; f: W* n# c- P( ]& a; r+ ?% l# E) p! h
    6( T# I2 j4 T) k% U. k
    77 [# A# M# z6 {) Q, Z& R
    8
    2 z3 ^4 c" \- {Example2:双休日6 _2 [* c/ C1 P
    ) k0 n7 z3 }9 W; a9 [. Y
    s[idx.dayofweek.isin([5,6]).values].head()% I3 u5 X) ]1 @4 h
    Out[51]:
    $ s, [7 Z' O7 o) S0 E- {2020-01-04    1
    9 L! j9 ^! ?+ o2020-01-05    0
    . v+ Q: E3 @( u  o2020-01-11    01 w0 ~; D! P! E9 G
    2020-01-12    1: ]2 a6 k& ?) K8 W) _5 v
    2020-01-18    1+ L& y# T* _" J, S
    dtype: int32
    ' n4 f3 b3 n8 m6 |2 B% {1
    ; U, m; Z+ i. a9 M! l! j! [& c, Z, Z2) a, S  R' b; f  R
    3
    9 U+ O+ a( o# @# g1 w& L4
    4 e1 H$ F, T' |" N2 M, {# _: r5
    & \- N. J& [7 C/ I: b6- {8 }) `; ]1 W1 }, Z% J" a; U
    7' j4 L- _+ X2 {6 m2 X
    8' W4 e& A7 u0 F) v
    Example3:取出单日值
    ' p# @% b; E4 x5 ?4 @1 x5 a; o* r
    s['2020-01-01']$ p7 N) g7 l/ Y" q8 E
    Out[52]: 1' [; ?* {; V0 e1 F
    4 d3 c  Y# P! V7 D
    s['20200101'] # 自动转换标准格式
    7 {- N: B  N9 y9 o! lOut[53]: 1
    + a8 W: j8 m0 B1
    - G$ \' ?7 w% E' m/ Q4 d: k2
    4 E( o, a7 ^7 @3 H3
    8 b& j  q. f1 _' X! e47 @: Y, Z3 C) t6 W* A1 \3 M- r( Z
    5
    9 M0 ?9 {7 i/ t+ vExample4:取出七月  s4 f( A) c# U# {, ^

    8 D- y5 N4 {& d+ @' ?* g' W: Js['2020-07'].head()
    1 @2 Z" i0 w9 G" j3 n5 c- a' ZOut[54]: # Q; D; t8 M( x* |$ Q
    2020-07-01    0- U1 d3 f# v# Q% N, N# D% [) l; C
    2020-07-02    12 b9 D) \  f* a1 I/ H" O/ ], g9 h6 i) F
    2020-07-03    0
    / B8 z9 Y. a- l6 c2020-07-04    00 d8 B0 S; M* D, J6 W; l
    2020-07-05    0
    9 ]+ ~5 y' v) u% v7 c: A0 w* K2 CFreq: D, dtype: int32
    ! ]  b" x5 D$ |1
    ! u+ C5 i( e+ k1 \" Y0 D6 E; t+ d26 _! ^/ d" e3 @8 j; h
    36 B1 z9 n7 X4 ]( M
    4( |0 w3 w) j0 W1 d/ m2 ?$ `
    5) X% \/ n6 ?- ]4 i! \
    6
    1 h4 D6 s4 N0 `. l2 D. e1 j79 V8 \; _) C" q# f
    8
    ; u! d' k% C, H) q3 S6 j3 SExample5:取出5月初至7月15日
    & w4 p& L* s/ s8 T/ V) c6 i" r
    ) D* Q( N6 a; H' o$ S, V) v$ Cs['2020-05':'2020-7-15'].head()$ r+ Z  W6 L6 J0 J
    Out[55]: 3 Y% c$ S5 a  I' E
    2020-05-01    0
    2 m7 q+ f7 G+ h5 d4 r/ R7 V2020-05-02    11 q3 w2 e  ?3 ^+ E" X( M
    2020-05-03    0* P) w6 H3 N6 Y7 ?* D* s( o6 U
    2020-05-04    1/ T9 a  N  s. D5 F. T7 L
    2020-05-05    1; n$ o/ C- ~; ]* |' k/ J5 K" n) {  `
    Freq: D, dtype: int329 f4 k. X* h6 o" w

    + X: Y2 m- @7 D' S- X$ o2 \s['2020-05':'2020-7-15'].tail()  w; D4 |" ]* Q& Z& s' E
    Out[56]: / y. K0 T2 s, z  D' ^
    2020-07-11    0
    2 G1 ~- k) [' g  \0 @/ s2 N2020-07-12    0
    4 P& q0 a: P7 G$ o6 A; O. @( D9 X' ]2020-07-13    1
    8 p: J! G' ]$ q6 i" `5 V# M2020-07-14    0
    % _3 J! z3 I8 n# v( c2020-07-15    1
    & v% Y, p, X9 P$ aFreq: D, dtype: int32
    # ^' d2 l1 w2 s# q. s- r; i! L
    ( T! R# c: U' d* ~. I8 d' Y% f4 Z18 d1 i; X8 K6 _2 x; K9 h( x$ n
    2
    8 H! _) Z: T1 P) x' w: w% ~3  L; |7 g4 O8 ^- k! q2 [$ J0 ?
    48 b; |0 z  x+ V8 P/ w  _
    5% j% p( e( }) L9 d/ R
    6, B3 [, z! m7 s8 _
    7
    ' j' {2 n2 u5 Y80 ?  A# H; z  Y- d* _: A+ L
    9
    3 E" T. o( K* L) Z10
    + M  ~4 a7 N, c! @11* U* \% w' W3 {, O7 C1 M2 C9 D9 C1 o
    12
    " t7 [  M2 b! {% t& ^13
    9 m! y9 J6 v3 x( [* N2 V) i/ X/ y14
    5 r  k4 X+ W2 y15
    7 t) \6 Q) q+ @& S( ]. l16
    0 l3 e& I! P4 l17
    8 _  A3 {  g  J4 @' M6 z  ]10.3 时间差, t$ m/ ?5 o8 I
    10.3.1 Timedelta的生成, P+ ^& b6 C' H
    pandas.Timedelta(value=<object object>, unit=None, **kwargs)
    ; p$ U4 W+ k/ v% Z3 f( @  unit:字符串格式,默认 ‘ns’。如果输入是整数,则表示输入的单位。
    8 T! ]+ k0 V, i1 m5 B, r  可能的值有:* o# A$ F, `9 n% g. @9 L4 A

    * J$ L  Q# q) x  x- H% [7 l- H8 @1 B‘W’, ‘D’, ‘T’, ‘S’, ‘L’, ‘U’, or ‘N’
    8 S4 I7 ]. E. I; W1 ?‘days’ or ‘day’" a! h( v0 D; y: n! x2 v: Q' B
    ‘hours’, ‘hour’, ‘hr’, or ‘h’9 x7 ~* d! r$ N! K! ^5 b; _' K
    ‘minutes’, ‘minute’, ‘min’, or ‘m’% X+ e% e  _4 e8 e; R6 m: E
    ‘seconds’, ‘second’, or ‘sec’+ x$ Q1 @) e! K4 m' s- F
    毫秒‘milliseconds’, ‘millisecond’, ‘millis’, or ‘milli’5 p) b2 G/ K5 E
    微秒‘microseconds’, ‘microsecond’, ‘micros’, or ‘micro’. ~4 d0 Z* M1 r" W! K/ H6 v. J
    纳秒 ‘nanoseconds’, ‘nanosecond’, ‘nanos’, ‘nano’, or ‘ns’.
    6 p% d0 W6 S8 `, H9 }" f时间差可以理解为两个时间戳的差,可以通过pd.Timedelta来构造:
    # ~' L+ C/ i! t3 c: T  ~pd.Timestamp('20200102 08:00:00')-pd.Timestamp('20200101 07:35:00')4 q1 u( k5 w- x8 x
    Out[57]: Timedelta('1 days 00:25:00')- L  O' @. Z; g) k* P" ~

    % o( a; V  R, E- m% Y! qpd.Timedelta(days=1, minutes=25) # 需要注意加s: B5 u4 Z4 R3 `4 C* _! S
    Out[58]: Timedelta('1 days 00:25:00')
    . y8 e3 @+ X1 Z( K- [1 L
    / m9 w2 ?1 w2 Xpd.Timedelta('1 days 25 minutes') # 字符串生成, j: z+ ~* K1 H5 X# m* A+ X
    Out[59]: Timedelta('1 days 00:25:00')+ s( W9 }9 w! V! M0 b7 K" r
    8 o: ?9 @/ [3 R& p  S+ e7 Y) ^. F
    pd.Timedelta(1, "d")
    # f# x; l# W% ~$ H  I" lOut[58]: Timedelta('1 days 00:00:00')& X& G& _* W* `6 J! I
    1
    6 `# X3 _  h6 S7 _2
    * D* m0 e8 t1 d! i1 w9 V( p1 U) g3
    9 B/ |% Y3 h, K4. e8 p8 d; q3 `# @; A/ z: j
    5
    - R: ^& M6 ^) c6
    0 S2 `7 \  m( U% ^% s& U7
      \' ^2 B- x$ \% p* ^0 \8
    $ y, z' X9 F& ^. K, d9
    2 A* _/ p# w/ a7 s+ c/ G3 T10
    & e- K' {8 P  z' a2 ^117 f  J; x: x! i! A4 b
    生成时间差序列的主要方式是 pd.to_timedelta ,其类型为 timedelta64[ns] :! Z2 j, U1 A0 y6 _. k8 {5 [: M
    s = pd.to_timedelta(df.Time_Record)! y' z. M! ~- e6 m3 g6 Y

    7 d! W5 B. q" L9 G9 Y" K. L( m/ a* i: Os.head()2 m& ]1 q4 u5 F, ]
    Out[61]:
    $ L6 [* @  t% J' n0   0 days 00:04:34
    * `! l0 H# Q$ J* c( l! w6 m; p1   0 days 00:04:20- Y% i6 v1 I7 ~, ~$ e8 s% k0 O
    2   0 days 00:05:22- t* m; Y- k! m! h" E
    3   0 days 00:04:083 A* @: n# X! d1 O
    4   0 days 00:05:22
    4 ~% d; w' o! f  b# O* x: RName: Time_Record, dtype: timedelta64[ns]9 U! y* Y, A2 o$ M3 s
    1
    : r$ S" N2 s5 j! G3 P" u/ P20 o4 i6 m2 v) c) q5 f! y9 S& Z
    3& U5 |$ Z1 B1 S3 N; M  k& O
    4$ m5 p! }! |: H" B. a% l: Q
    5
      @  Y6 ?% ~" N( d( E% v& @% @6" x, I; f: E8 c) x3 A, B0 e9 q
    76 Z. d! G( |$ e
    87 w% `& u1 S% t
    9
    ( {+ M( h  p$ X" r( K1 y10
    3 Y+ t) D* ?, J  ^0 \7 J与date_range一样,时间差序列也可以用timedelta_range来生成,它们两者具有一致的参数:2 ~" _$ Q$ d) o$ r. d
    pd.timedelta_range('0s', '1000s', freq='6min')
    3 \1 N/ U! ]1 @2 V1 rOut[62]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:06:00', '0 days 00:12:00'], dtype='timedelta64[ns]', freq='6T')
    # y' M1 L3 r3 R4 O' o
    & `4 h4 ~" j, z% r+ Rpd.timedelta_range('0s', '1000s', periods=3)
    ; s, i, \) f3 _/ U( aOut[63]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:08:20', '0 days 00:16:40'], dtype='timedelta64[ns]', freq=None)
    # m0 E2 {1 ~. U# O+ K8 c2 ~13 O" [/ P* W- k8 K4 i
    2) e5 @# q$ q" @) k# \0 h
    3: r: _7 B1 r- j! [% R% U
    41 c9 j. w' a2 z1 n
    5
    ' V! _4 Q  T; R对于Timedelta序列,同样也定义了dt对象,上面主要定义了的属性包括days, seconds, mircroseconds(毫秒), nanoseconds(纳秒),它们分别返回了对应的时间差特征。需要注意的是,这里的seconds不是指单纯的秒,而是对天数取余后剩余的秒数:; K  ~) f5 e, `" Z0 B! x
    s.dt.seconds.head()
    6 u* S; v' Y: Z; UOut[64]:
    & J& o9 n  D/ T" P0    274. _3 H. S% }3 D( R; z
    1    260. W5 ]! E8 i% t# Y0 F  I! @- A
    2    322+ F: u: ~- e! n
    3    248  }" `. e/ m: z, v
    4    322; r: {4 d8 y5 o1 S5 c: M2 o
    Name: Time_Record, dtype: int641 y7 p! c% e* B- j. D$ f" ?
    1
    ! M4 H( T; `" q; h26 S/ T9 z9 D: B# ^
    3% r' `4 g; n+ e) G, _6 P
    4. x2 H) p3 r5 U* L9 b6 Q
    56 I8 G) G5 K1 r# x& s; m# C
    6: I3 {: H, c. G. E% x
    7
    4 w" C8 w5 u' |+ z% b7 s: N9 y84 t! X+ I: E+ ~
    如果不想对天数取余而直接对应秒数,可以使用total_seconds% _0 W3 s. F1 M1 f

    7 Z8 _4 F9 ?  _" _' hs.dt.total_seconds().head()
    * \# t- ^, j7 [3 D) wOut[65]: & l& P$ ~9 c! m9 D3 `9 m) s9 M2 a2 t
    0    274.08 E, j9 a, v4 h* d% R
    1    260.0& r7 t6 b# R7 _* s
    2    322.0
    1 K& |6 T; m- [4 ~' r3    248.0" }% }9 x$ Z) V! X* g& Z, L
    4    322.0' T$ a" r  t; y- x. x) [
    Name: Time_Record, dtype: float64
    7 E; c* @9 B7 m. k$ b6 X) B1& p' Y2 p6 c: X7 J" W: s
    2- G& V2 [& g: b) r5 q, U2 l
    33 a$ |7 }; L$ ~
    4
    1 ?" K, D/ J& v4 Q  `& |9 S5( g8 x) C) B* j$ d# q) m' Q
    6
    % |: x* b( O& |3 c6 J$ r7
    6 X& L- M8 }3 b! m: t4 l3 B( Z8
    % b# @4 {" C5 P2 C与时间戳序列类似,取整函数也是可以在dt对象上使用的:
    % w9 _/ i8 X2 ^7 z: ?
    7 |/ K, _, g9 ^# `pd.to_timedelta(df.Time_Record).dt.round('min').head()
    1 @6 u  F' Z6 UOut[66]:
    % z' b* }2 M8 k- O  s7 j. N, H0   0 days 00:05:00
    % l& K/ ~  b9 K# |1   0 days 00:04:004 V  d1 X! X! L" Q( ^
    2   0 days 00:05:00
    0 T8 M: j9 ~% p) ]5 U9 [0 {3   0 days 00:04:00( G: C4 S7 ?* T- j6 r
    4   0 days 00:05:005 @* l' p5 \$ I9 r  J
    Name: Time_Record, dtype: timedelta64[ns]" K) O' X$ g2 l, n9 d+ s
    1/ u9 }4 D+ n2 s: S) |
    29 N* Q* q9 U) K; c
    3- B8 p  x, g( p. B0 O$ t2 Q
    42 {4 @# ]3 p) D6 o5 B& R* u, {" O0 ]
    5: O! r1 v& v7 u5 V& u
    6
    6 L) h8 }& }! r4 p  X& c. j3 }7+ Z, K8 c1 o% Z! m9 r' Q+ `
    8# R* @/ _& `" O) Z0 m
    10.2.2 Timedelta的运算
    6 L( }) h% a$ G3 f单个时间差的常用运算,有三类:与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算:  v0 u, |; ?" t
    td1 = pd.Timedelta(days=1)
    5 i% E( B3 b0 I6 X3 M1 t0 E+ ~" K) itd2 = pd.Timedelta(days=3); U4 E$ x- ?5 k9 E0 u
    ts = pd.Timestamp('20200101')+ _* D6 g, ?7 r0 d; U: ]# c- D

    $ |4 w. V: j+ h; c7 itd1 * 2( E0 Z+ J! L2 A" D- _
    Out[70]: Timedelta('2 days 00:00:00')
    2 X( |8 Y3 p' P! n6 z
    5 X9 @( g+ y% }0 F; xtd2 - td1
    / U0 M5 C2 g) `1 n4 L$ ?Out[71]: Timedelta('2 days 00:00:00')7 F% {8 v& p% Z8 x8 u, x
    # j" m3 ~  U. u% D
    ts + td1
    ! B& t* u' B+ KOut[72]: Timestamp('2020-01-02 00:00:00')" q' |& U9 ~5 Z: ?9 R2 _
    % S) Z" ^. [1 U+ D& ]8 ?7 D- ]8 F
    ts - td1, t' o$ f9 i6 Z% d) i# A
    Out[73]: Timestamp('2019-12-31 00:00:00')6 @, o6 L. s3 J. K- X8 T" c6 @
    14 K# @6 [5 f/ ~
    2
    . N' g! ^( G8 G, T+ Q4 t5 L- j3: t8 N& x7 ?% d0 \: ^1 h
    4
    0 v/ ^* o4 x) v+ y2 d4 Y5
    6 V+ j; c6 z, Y- Q5 V. o6
    % C% P4 Y7 ^6 b% p# F& z+ v7
    $ l  v, M$ D8 B1 e; R* |& w8
    5 a8 T4 B1 p9 C92 u( J4 ?# m( A  d$ E0 a
    106 ~1 v' h# j7 ~4 u
    11
      ^! p* f* C2 G# F1 |3 `4 {4 ~1 O' B- m12
      H, }6 k8 A* E, E( Y' O13
    ' i  Q4 c/ c& Z% B1 K14
    + B! b# N( ?' k, [15
    + y! i/ V2 z- _' z时间差的序列的运算,和上面方法相同:
      D& I: a7 H$ x# y3 b; R( c& f3 }, {0 Ltd1 = pd.timedelta_range(start='1 days', periods=5)
    * I8 _+ H7 G( K- C/ [% q/ R& {4 Ntd2 = pd.timedelta_range(start='12 hours',1 w. ?8 X7 X, Y& g
                             freq='2H',, M% E4 h' H3 b4 s
                             periods=5)
    5 U. x7 D) ^4 m5 D. s9 qts = pd.date_range('20200101', '20200105')1 ?' }4 [8 z/ |: d
    td1,td2,ts
    + z3 [) x. M5 L% Q8 c$ e3 S
    * `+ k6 k8 u) |+ d* A7 wTimedeltaIndex(['1 days', '2 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq='D')8 @1 D: H- A, C  g5 O9 s$ R+ o
    TimedeltaIndex(['0 days 12:00:00', '0 days 14:00:00', '0 days 16:00:00',
    ) m& W3 Y" b$ W5 g                '0 days 18:00:00', '0 days 20:00:00'], dtype='timedelta64[ns]', freq='2H')
    4 s% a; v, l/ I( b$ k8 HDatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',
    ' u; A- z  U3 j0 |  F% x; |# m               '2020-01-05'],
    ( ?5 k/ R; H) Q( N( V              dtype='datetime64[ns]', freq='D')' i' R7 W3 q' E1 I
    1
    9 x2 e, Y' p; {0 b8 {/ c: ^2
    ) O9 c" ^' ~, E1 ^3, f* t; g, ^) G; i4 y$ O
    4
    3 I' ^9 `$ b* i. ^  \5
    , e) j2 h" R* T6, ?: M8 W' n- Y3 ^6 C+ M( d
    77 P/ b, t/ W) P( \
    8
    4 k/ L3 o% k, d+ x6 ?# s7 U3 u9# a' Y0 n( c& N  q% v
    101 R; n9 ~' m6 D' C* l% j
    11; }: j% q5 M9 n( }
    12
    ' w8 w/ t1 R7 \2 S% s; ^13" K2 M& J. ~% i5 N
    td1 * 5# ?3 N. V( I: b
    Out[77]: TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D')
    ; p& d# q; e0 H! ?7 C# a- s" v& e8 f% U6 H! ]+ `& p6 T
    td1 * pd.Series(list(range(5))) # 逐个相乘
    0 s& S2 ~8 w; F2 y$ e! z; [Out[78]:
    6 {8 J' O( ~7 J% n0 @( p% w0    0 days
    % A7 }; Y. K$ Q% Z+ k+ `1 ]7 D1    2 days' \( g' ~7 N5 c) G. S+ B' Q9 k9 k
    2    6 days
    ; y; a: i8 s* ^2 j; |3   12 days
    2 x# Q& ?0 v. Q( o) I4   20 days6 l8 O( |& E: [
    dtype: timedelta64[ns], s" w& N/ k; O5 T: D5 w  U! `
    ; E3 G- H- G4 u) ?9 C2 X
    td1 - td2
    1 f- T/ f7 Q; c8 eOut[79]: ! ^7 T' H( E) t- ~* r5 s$ k
    TimedeltaIndex(['0 days 12:00:00', '1 days 10:00:00', '2 days 08:00:00',
    * h2 N1 M/ j, G) U" W                '3 days 06:00:00', '4 days 04:00:00'],6 p* i/ n& ~" ^: t% z
                   dtype='timedelta64[ns]', freq=None)
    5 D4 O! j' v3 f' H
      w) v5 T3 T, s& ^  xtd1 + pd.Timestamp('20200101')
    ) x! k. B4 X" Y7 \/ i1 ~Out[80]: 9 m3 e8 B8 I+ w( d8 N7 o, C) U
    DatetimeIndex(['2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05',
    * D$ [$ l( Q' u0 x               '2020-01-06'],dtype='datetime64[ns]', freq='D')! E5 J2 T7 R# X8 B

    ; ~3 d  I/ @2 a9 A7 U' jtd1 + ts # 逐个相加
    2 S. y4 t) O) n+ z& MOut[81]:
    & w4 B% q6 c+ lDatetimeIndex(['2020-01-02', '2020-01-04', '2020-01-06', '2020-01-08',
    3 v3 i4 `" L9 z7 z, f0 R- |) Q               '2020-01-10'],
    * ]7 e4 \. k! }+ m* _              dtype='datetime64[ns]', freq=None)+ |) ^0 {; \  U6 @. S: G( G/ ?1 b

    # k+ ?5 V9 i$ c# i) e1- U" C* F3 h6 m( t- |" ?* ^* r
    2
    , ~2 K2 Y. p  E+ l0 j" c8 M3
    % k" p6 x& t$ j. P42 `7 }: r+ S" e3 Z% i
    5
    # ^; ^" R3 F; }! |6% y# u4 d- y' T4 M6 u
    7
    . \5 R, A! t% ^: C3 g- A- `8. ]/ m) {- v: P/ C& t" B
    9: s% o) ?; U% E) H) Z+ \; ^
    10
    * P' ]7 e) u$ I# {7 S( D11
    ! J2 s1 U: ~# k" |12; A. c, V2 v' _
    13
    ' v1 E! h* T' z+ V14  k& _8 F: Z* [" u& n6 |6 Z* @* e4 E
    15
    - k( E' [3 e( @& {5 c2 |16
    . ?/ N7 f/ x! `6 z5 `- e/ }17- J+ S; H) t! u, E5 C: |
    18
    % e" ~, y1 ~6 d9 K19- O$ k7 ]. s; \# ^, Y7 `8 ~2 e
    209 w/ @0 X% \* i3 i% V  J
    21% q2 Y7 U( F( @9 @1 e
    22
    $ K. M4 ~* ~' w. g$ h23# g' n& ?5 O, a$ Q, N
    245 p" z* k& g* J" i' j" }
    258 U# D+ ^' a/ S9 x
    26
    $ P5 r" T7 u" T, o27
    ! Y& P/ F0 g0 ?4 i! i" Z28
    5 b9 |, C5 ^5 S# N+ K' t$ e( N" _10.4 日期偏置
    / \( M9 H8 [* F+ d0 ?9 Z+ e2 N10.4.1 Offset对象. l0 G4 Q5 t- g% x4 g% d
      日期偏置是一种和日历相关的特殊时间差,例如回到第一节中的两个问题:如何求2020年9月第一个周一的日期,以及如何求2020年9月7日后的第30个工作日是哪一天。
    , W/ Y! @' P+ u, l( k
    % \  E+ Y9 t: T" m1 l4 ^DateOffset 类有10个属性,假设s=pd.offsets.WeekOfMonth(week=0,weekday=0),则:
    6 J, p. m( Y& f: T: Z; S! b  x  |* @3 ~: V
    s.base:<WeekOfMonth: week=0, weekday=0>,返回 n=1 且所有其他属性一样的副本: q& r, w. e1 P# S
    s.kwds:{‘week’: 0, ‘weekday’: 0}& }  H1 n3 v* g* L  y
    s.wek/s.weekday:顾名思义
    2 {9 V0 K8 N2 K' B7 L有14个方法,包括:2 w( Q/ d9 X  J, w7 h! b
    - r8 @  s4 G, u5 |- N- B* G6 D
    DateOffset.is_month_start、DateOffset.is_month_end、DateOffset.is_quarter_start、DateOffset.is_quarter_end、DateOffset.is_year_start、DateOffset.is_year_end等等。$ o3 |( F. ?" a/ u
    pandas.tseries.offsets.WeekOfMonth(week,weekday):描述每月的日期,例如“每月第二周的星期二”。
    7 k! ?4 `! ^( Y" k* c  ~$ [6 r
    % v( H( Y3 {# g4 }# |有两个参数:
    , S* _0 l, n0 t; M/ yweek:整型,表示一个月的第几周。例如 0 是一个月的第 1 周,1 是第 2 周,以此类推。: H" Y* n* @# O# T8 n/ j" V
    weekday:整型,取值为[0,1,…6],表示周一到周日,默认取值为0(星期一)% l  o* Z1 {, n  D, h" k9 i7 J
    pandas.tseries.offsets.BusinessDay(n):相当于pd.offsets.BDay(n),DateOffset 子类,表示可能的 n 个工作日。
    ) K7 ^2 C' L6 @; R( G
    * j& k% i9 s4 B8 Apd.Timestamp('20200831') + pd.offsets.WeekOfMonth(week=0,weekday=0)9 l: ~( ^3 T, ]
    Out[82]: Timestamp('2020-09-07 00:00:00')& f2 G6 @  m' H9 Z7 k- S
    , ?' t: y* K, E( g0 ^) L
    pd.Timestamp('20200907') + pd.offsets.BDay(30)
    * f* f' G) |7 M  L/ }/ i9 }Out[83]: Timestamp('2020-10-19 00:00:00')
    - e6 U# T+ W2 {+ P# P% g1 Z5 Q1
    6 _. ?( ?4 T+ g( ]! Q  t2
    : n" G* U9 A0 L9 T+ f3
    : G2 n7 Q6 k+ C4
    * {1 A4 J, |/ l% E4 w. t8 [5
    8 }: O# p1 n  ^" {" }  从上面的例子中可以看到,Offset对象在pd.offsets中被定义。当使用+时获取离其最近的下一个日期,当使用-时获取离其最近的上一个日期:
    : N* d- R: o/ @1 D
    " H& m1 q- s  tpd.Timestamp('20200831') - pd.offsets.WeekOfMonth(week=0,weekday=0)) M) _0 p! N; [2 r2 I
    Out[84]: Timestamp('2020-08-03 00:00:00')$ h% y3 Z: f% A

    6 \; I& z) K3 Rpd.Timestamp('20200907') - pd.offsets.BDay(30)
    ( {5 m  n0 R0 [/ f9 E, |Out[85]: Timestamp('2020-07-27 00:00:00')" p; X$ p, S4 l& P6 U* e

    6 |, S) w2 \) a- i0 z6 dpd.Timestamp('20200907') + pd.offsets.MonthEnd()& z2 ^6 J' `( `
    Out[86]: Timestamp('2020-09-30 00:00:00')% x  y4 l* t, v( g( p# E
    1
    ' {2 K6 x, B- G  |/ F2
    1 j) h0 {) S* B! Q! r' H8 J3
    0 Z4 k" q9 |- _. l4: l+ p" O! {& b2 s" F" ^2 E! N
    5: I# o, E) E5 U3 X8 T
    6' K7 O$ ~8 h) h3 A5 ~7 n$ `
    7
    $ f$ V1 I9 I. S! V* H8: p; i/ r* m2 u
      常用的日期偏置如下可以查阅这里的DateOffset 文档描述。在文档罗列的Offset中,需要介绍一个特殊的Offset对象CDay。CDay 或 CustomBusinessDay 类提供了一个参数化的 BusinessDay 类,可用于创建自定义的工作日日历,该日历说明当地假期和当地周末惯例。
    4 r( F" }% z" k  其中的holidays, weekmask参数能够分别对自定义的日期和星期进行过滤,前者传入了需要过滤的日期列表,后者传入的是三个字母的星期缩写构成的星期字符串,其作用是只保留字符串中出现的星期:) m4 r* k7 a  F6 f, r0 A) J5 c& b

    1 S) ~5 e& K" r% r! D/ _' d9 lmy_filter = pd.offsets.CDay(n=1,weekmask='Wed Fri',holidays=['20200109'])
    $ X" b# ?  ^6 Q, u8 Y: [1 ~% Kdr = pd.date_range('20200108', '20200111')! r6 c( F7 a' f+ |$ V

    # }% Y, S, M$ I. ^  ydr.to_series().dt.dayofweek
    : Q4 l3 X2 f  z5 v8 w& uOut[89]:
    8 T0 T& c+ p8 c/ |/ Z2020-01-08    2/ {9 P: [3 j$ B3 r, h7 I$ E
    2020-01-09    3
    9 W6 S& C$ \) V6 D9 j2020-01-10    44 G5 }" ?$ u( V1 X* f0 c/ r) @( T% b
    2020-01-11    57 c& m# Z7 o! |: Y4 _' M6 ]
    Freq: D, dtype: int64
    ( N6 X* G2 a* \+ }* p0 f+ Z& j4 B) o/ ]. Q9 Y
    [i + my_filter for i in dr]
    # E$ ~: U% Z6 X, gOut[90]: 9 }0 p. i0 u$ t" z8 k
    [Timestamp('2020-01-10 00:00:00'),
    9 K, Q7 A9 v( V0 J+ @ Timestamp('2020-01-10 00:00:00'),
      \: S0 [5 |2 i4 v4 A$ w( m Timestamp('2020-01-15 00:00:00'),6 A- O; L5 j2 z4 q# u
    Timestamp('2020-01-15 00:00:00')]# M0 x$ R* _2 K4 g; X
    4 M" z+ @7 R4 |0 ?/ k% [: q
    1
    3 v! I, X2 v. R9 |+ O- ^' I2; V2 r3 O7 o) v* Z: ]
    3
    $ q7 o/ J( z0 N4
    , v0 `& S. A/ ~, o1 h( A3 `6 C51 h7 c2 l. L( O* F
    6
    : N. h. K/ J& j/ L4 x7
    ' Y$ _5 _8 N9 f4 J- V8
    - A; ~! k. p$ m3 N9
    " V; c# C0 ?% y2 @$ c* W10- O" n$ k7 i( x* D( {, c0 |! D6 B
    11
    # J' k! Y; v9 ~- \+ `12
    ' S' d. N5 G( Y+ C; \, {7 o. |13
    , c, o! }. W& p; e/ Q/ A6 X14* F( R6 b$ C7 }" h
    15; C# K9 f, Q# W
    16
    - ~2 Y( l. A8 G9 [178 j+ @! q4 g+ O4 U
      上面的例子中,n表示增加一天CDay,dr中的第一天为20200108,但由于下一天20200109被排除了,并且20200110是合法的周五,因此转为20200110,其他后面的日期处理类似。# z3 z4 y; t, }8 ?3 r" y

    # {: W" f. ?  d# @0 I: V【CAUTION】不要使用部分Offset
    ( n( f* E& S( j- e( Y) q' w在当前版本下由于一些 bug ,不要使用 Day 级别以下的 Offset 对象,比如 Hour, Second 等,请使用对应的 Timedelta 对象来代替。7 _- C! H4 T4 U0 l2 R
    8 c2 h* o: O3 w
    10.4.2 偏置字符串
    6 Y0 s4 r; ?+ {: l  前面提到了关于date_range的freq取值可用Offset对象,同时在pandas中几乎每一个Offset对象绑定了日期偏置字符串(frequencies strings/offset aliases),可以指定Offset对应的字符串来替代使用。下面举一些常见的例子。: ?2 d; O* r3 c- A

    . _6 f9 X. x0 V' Y& \8 j. ~  Offset aliases:pd.date_range函数中的freq参数,为常见时间序列频率提供了许多字符串别名。 也称为偏移别名Offset aliases。偏移别名列表点此参看(大概27个)。, }; E! }! {  S' U) ~6 _, J
    ) x7 w& W1 T- _# h
    pd.date_range('20200101','20200331', freq='MS') # 月初4 h$ `! u; i: _# h+ o8 X
    Out[91]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')
    2 @! O" n# w5 t4 x0 S4 r' o, k* I" r) F
    pd.date_range('20200101','20200331', freq='M') # 月末0 {( H. _9 v+ w* o( P4 Y6 `
    Out[92]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')5 {. _, ~: x$ R" L
    0 K+ v8 r3 ^) W% R$ G
    pd.date_range('20200101','20200110', freq='B') # 工作日
    , ~. B! J1 `7 d+ V1 d' I5 LOut[93]: 4 P  n. Z: A% L
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
    ! ]! G3 a/ [0 y, i3 ?$ g               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],
    / T% ^0 D( e+ B4 }% J              dtype='datetime64[ns]', freq='B')
    8 F% N: }6 Q  x" W" O8 {: d
      O' [/ m; U0 ]3 D. C( O4 d, fpd.date_range('20200101','20200201', freq='W-MON') # 周一
    - u) f8 ]! ]) T% jOut[94]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='W-MON')
    1 C+ Y0 S% N! Z; x' j) t
    ) {5 D- I% Q" J$ E" L( l5 ipd.date_range('20200101','20200201',
    8 A( L, x$ `  I              freq='WOM-1MON') # 每月第一个周一
    & e; d& D) O3 z+ G3 M* z5 X7 Q; H$ P
    Out[95]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')
    / F7 C* d2 W3 R& j  d2 e( m: a9 ]  K9 C: z
    15 t' R3 s, X/ y0 e8 u* h
    2$ c* L; ?# u0 m9 j! y9 c: q1 E
    3
    / `; q6 U' t( V3 _5 `4' b- B3 n  y- Y7 u& I" `
    5
    # |7 ~# k1 a0 H1 A6
    " Z4 F$ }& Y; f- }4 v' q72 P3 u. O# X8 F' y
    8& m5 q8 ?) z- D& g7 N) J! w
    9
    " x. z+ F+ _% L% G% d% ]4 _10& \( ~# p6 r: V( F5 W
    112 s* I' r/ N2 ]  q! S2 Z& [$ ^
    12
    6 S# }8 y! Z0 s( F13
    " f$ V5 L% Y, |7 X& t148 y7 _1 j! |  Q5 o7 Z3 ^2 D0 ~: J# S
    158 e0 L. L- Z3 k/ b8 r
    16
    . H% n8 {3 O! J' w170 b& d8 g6 P- j5 j# Y. x8 v
    187 T! l) S6 A+ d
    19
    $ S# L. s$ J3 a5 J7 Z( W上面的这些字符串,等价于使用如下的 Offset 对象:
    * O0 s$ w& r5 m5 b" ?2 k3 H1 A8 ~) w1 t
    4 f  [' n  U9 c7 rpd.date_range('20200101','20200331',! {7 C' ]5 [  K5 @
                  freq=pd.offsets.MonthBegin())
    # Z1 x& w" m: ]9 d0 w" Q& {: T1 d2 p% E$ U+ q' u1 g' B1 t. J. ?
    Out[96]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')
    & Q! V4 E: P4 O) g- ]' Y/ Z/ M$ g" d: @2 x" t
    pd.date_range('20200101','20200331',; {9 m8 \/ R! e. h$ T
                  freq=pd.offsets.MonthEnd())
    ) c# t$ _/ t; k4 Y" E6 r. m4 Q0 o; a( T  t9 X( s' t4 Y
    Out[97]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')9 E4 I1 `# Q% H4 R7 Z  p

    3 l8 Y9 S8 T3 @2 P6 Opd.date_range('20200101','20200110', freq=pd.offsets.BDay())
    * L; q% ]2 L9 pOut[98]: ; h7 M& R. R% y2 m
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
    - G- g7 j2 y' Y0 I  J               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],
    + m+ V, \8 |4 T- m; g              dtype='datetime64[ns]', freq='B')2 s' x, y; T, p7 j

    " H5 B% G  O7 x4 N% e$ o9 m; \8 Cpd.date_range('20200101','20200201',/ u) Q5 e- q' P3 v4 f
                  freq=pd.offsets.CDay(weekmask='Mon'))5 o$ O! p$ _( X& ^0 z
    / d( [* E# y$ g
    Out[99]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='C')
    $ C9 \1 j0 {" @; c0 _+ D
    & x& ]7 W" G. X/ Spd.date_range('20200101','20200201',5 [9 h3 {0 G5 s5 l+ q
                  freq=pd.offsets.WeekOfMonth(week=0,weekday=0))
    5 Z7 M$ [% X) M- u/ q2 x- @" |6 b& ]* W5 d3 v  v
    Out[100]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON'). q) m" W& U. |3 _1 R+ R

    & W- v: T" o! N& j2 {0 G10 t3 m! z! S; ^' c" }2 l$ `
    20 X* r! O! h1 X" B9 l1 D- I$ f
    3/ o$ @3 c1 @* z( m- L# C$ s9 ^
    4
    : Q& N1 x2 @$ G% ]" z5
    / C# v( u' o2 ~6
    2 F$ K% p8 \) p: A; r; Y7
    ! O2 K. j9 }* p6 w1 S% E8+ j# e" a0 i5 g; p/ _
    97 q- g# l& j. p: \6 n5 N6 z
    10
    * b. |# v" P; J7 F4 A; Z' y( O11
    0 v9 D" Q* J0 P12* V$ G4 F5 t6 {; R6 S
    13: @9 V: {+ w8 O
    14
    ) i0 v9 ]6 ]( u, G15+ h( [/ j0 j) s) V4 K4 o
    16
    9 |1 s' W8 o2 y: m6 ^+ v8 T172 ?# s* X8 A& g, l# o
    18, ]& N  V- e: `2 k! B; }
    19
    : o" @9 C+ y2 ?$ J- y205 w$ P" Y" a8 W5 Y
    219 ]3 R" w; F: n4 e% K# p3 ^( I
    22
    5 ?' f* e. \% q7 y0 b2 e# K230 c4 s  L6 T& ?' [/ s. t
    24
    7 C" ^3 J) H4 H' \/ Y1 q. `( v25
    0 s- E  p" F' C: i3 J1 b0 s5 Q$ ]【CAUTION】关于时区问题的说明
    $ O8 x% W$ P3 Y: L9 l  各类时间对象的开发,除了使用python内置的datetime模块,pandas还利用了dateutil模块,很大一部分是为了处理时区问题。总所周知,我国是没有夏令时调整时间一说的,但有些国家会有这种做法,导致了相对而言一天里可能会有23/24/25个小时,也就是relativedelta,这使得Offset对象和Timedelta对象有了对同一问题处理产生不同结果的现象,其中的规则也较为复杂,官方文档的写法存在部分描述错误,并且难以对描述做出统一修正,因为牵涉到了Offset相关的很多组件。因此,本教程完全不考虑时区处理,如果对时区处理的时间偏置有兴趣了解讨论,可以联系我或者参见这里的讨论。# Y5 t9 ]3 G3 h; O  C6 V8 q

    6 ~9 q: a8 L0 i/ o% r/ ^10.5、时序中的滑窗与分组2 L* Q& F8 V) S1 Y$ C* ^
    10.5.1 滑动窗口& H0 N; U: d7 L6 j. K
      所谓时序的滑窗函数,即把滑动窗口windows用freq关键词代替,下面给出一个具体的应用案例:在股票市场中有一个指标为BOLL指标,它由中轨线、上轨线、下轨线这三根线构成,具体的计算方法分别是N日均值线、N日均值加两倍N日标准差线、N日均值减两倍N日标准差线。利用rolling对象计算N=30的BOLL指标可以如下写出:
      m" j% ?* E. v  H, Q
    * U- `& g" [3 M* J+ \import matplotlib.pyplot as plt$ p- f4 p, M$ S
    idx = pd.date_range('20200101', '20201231', freq='B')
    ; r$ N0 b! J& M1 J. Fnp.random.seed(2020)9 K. d5 P* m8 o/ y1 H) y
    & r4 G- o6 d7 _
    data = np.random.randint(-1,2,len(idx)).cumsum() # 随机游动构造模拟序列,cumsum表示累加' f$ E) K9 M  a7 N8 {
    s = pd.Series(data,index=idx)
    1 {- P+ ^6 \4 M7 S7 q# |2 fs.head()
    1 L( q/ u- i) N5 ?9 I" H$ q! ?Out[106]:
    ( l& R  D  I( i4 u2020-01-01   -1
    5 w8 ]6 }, k' @2020-01-02   -2
    ' L; W2 r" n- \! i- I2020-01-03   -1
    6 W3 P% `" V' S/ S0 M' r2020-01-06   -1
    : r# `6 |0 h- I0 l" w2020-01-07   -2& f5 d9 I8 x; @& j4 o
    Freq: B, dtype: int32
    8 c. M. ]7 p' H& E6 zr = s.rolling('30D')# rolling可以指定freq或者offset对象! i1 [1 f2 }; d3 O! [
    6 x! N7 a6 e: @" d1 g! q; b% p
    plt.plot(s) # 蓝色线
    % C* O: R; I- ?( Y: ]4 sOut[108]: [<matplotlib.lines.Line2D at 0x2116d887eb0>]1 i0 R- J) V$ G3 J  ~& y4 h6 x8 L
    plt.title('BOLL LINES'), q+ Q* P6 k7 K. ]
    Out[109]: Text(0.5, 1.0, 'BOLL LINES')
    % b+ V4 t# M! u$ Y. R/ y; B/ R/ Z+ d" S3 W* N7 W4 P
    plt.plot(r.mean()) #橙色线6 C4 u3 j' Q# O" J- {2 i# O* a
    Out[110]: [<matplotlib.lines.Line2D at 0x2116d8eeb80>]3 R- l) Y/ m" _; K$ m
    7 l) G6 b& _  C, M: x: ]5 X
    plt.plot(r.mean()+r.std()*2) # 绿色线
    / H8 j  c# C' a* X  [- \, gOut[111]: [<matplotlib.lines.Line2D at 0x2116d87efa0>]
    ( w" [) _+ F9 }9 l: Y% I; x# ]' J: K6 a2 s; T
    plt.plot(r.mean()-r.std()*2) # 红色线
    3 V! X! V& @# s9 Q9 jOut[112]: [<matplotlib.lines.Line2D at 0x2116d90d2e0>]
    # z3 V$ L& U2 D: R- E& U- w% c* ?
    7 b8 e0 \$ [- K& g1
    ! Q) X* Y/ j* P  s5 _0 ]/ u2- _4 u# k( h* g# C( _
    3
    2 B9 b5 |( f8 V6 ^1 ]$ Q44 t+ C! N' j% m  [. E+ @+ I3 ?+ @9 d
    5, s! f. w: A3 O5 Y+ ]6 w" s  l
    62 P) b4 _, m4 n' h0 j' Y
    73 i( `8 v* h; z0 A8 @
    8
    # w: p- a" G! q: T6 U9
    6 S: b' g% q1 l, L* `10
    8 R' @3 d1 m$ A* y115 s/ H: C3 B* l9 H) B
    12
    3 P& l; `+ t8 S7 Z13
    ; o5 W5 g: P  c% Q* N% l14
    ; S* C8 k) ^) K2 F7 `15
    0 V7 |" v( K/ a- Y& C- s3 i16, _. G* _  n9 i! s4 K& p0 Q
    17
    5 M4 P/ p  E, s  p6 k& S18! S1 R0 L" X( y" A- w1 L
    19
    ! Z( T# Q; F  r1 _1 h& r20) ?. \' ~- I, E" u9 W0 ]4 G
    21: z( q3 l6 ]2 H% [9 e; G9 a
    22" d3 t( I1 q2 R' g6 N( J+ P% D7 H9 y
    23
    6 k8 S0 |4 E3 j' X$ a24
    , l# r! o0 Z- F, c) G, b7 t25
      o) t3 j9 I+ C( O8 r* }9 t26
    ; c2 Y$ V, o# A  ~5 C27
    1 y  ?3 z( A7 }7 h6 |1 Y: p28
    & o' e! O* c! x* @9 D29; C- e6 x( f! l0 S
    ' k& E/ J1 Y' L1 c. J- @2 o2 p0 X
       这里需要注意的是,pandas没有实现非固定采样频率的时间序列滑窗,及此时无法通过传入freq字段来得到滑窗结果。例如统计近7个工作日的交易总额。此时可以通过传入多个函数的组合来实现此功能。
    5 }2 F$ q  l$ J   首先选出所有工作日,接着用普通滑窗进行7日滑窗加和,最后用reindex()恢复索引,对于双休日使用前一个工作日的结果进行填充。
    $ I; N: |+ s! P, m/ R
    / I' o! @! R( Z" iselect_bday=s[~s.index.to_series().dt.dayofweek.isin([5,6])]
    ' I1 _" R# E2 `8 t/ s& zbday_sum=select_bday.rolling(7,min_periods=1).sum()
    # B% ~6 X  O* v# A+ d8 V$ }result=bday_sum.reindex().ffill()
    % q  O5 x1 w' t7 L- tresult
      @" O' K$ _; H) E4 P1 g/ G; _4 N6 \$ a
    2020-01-01     -1.0
    1 r) ~* Z$ f) g4 R( T! o2 S! V" O2020-01-02     -3.0: U' m! o) \0 O' `/ c
    2020-01-03     -4.0
      g- k) _* ?# _$ f4 }3 I2020-01-06     -5.0- u# t. j/ R# r* k3 v+ q0 p
    2020-01-07     -7.0: I( H* C: A5 q3 s
                  ...  ( F! o! r% B. c  ?& S0 P0 C8 w9 c
    2020-12-25    136.0! k7 o6 S5 x8 p6 w
    2020-12-28    133.0
    8 V0 \+ k, T3 x+ t8 E% P! K1 X) f2020-12-29    131.0' L  Q2 @4 Y' d1 C7 S9 x
    2020-12-30    130.0/ u9 Q- f' ^4 y' X  {. ^
    2020-12-31    128.0" f( K. p& ~9 ^9 j, }7 `
    Freq: B, Length: 262, dtype: float647 l- V$ n1 U; ]: C) Z" a  u
    + {- x0 C3 R' d, I- V
    1
    $ D& ?- ~7 r. @! K2: S; y3 Q8 b8 e" `. ^, n; W
    3
    0 [, K, R6 X+ j: m45 c9 L9 b1 \1 l* G" i
    5
    % Y1 {- N1 J- \' x6
    8 _7 M" o5 S7 P) n( h7
    4 u8 V$ e2 g( q6 i6 R8! b' |  M6 B& l9 w
    9
    * F2 u! W, C1 ?10
    0 k/ V+ n5 j# `6 Q11
    $ F& N6 g8 d' R7 {12& _) n' y2 R! B$ ]! N
    138 r) {; r! w- c# [
    145 K5 ]% U: S1 J5 J
    15
    - S/ U" \0 r# W16
    6 D% j# ~6 R- Q& L! s/ }+ w  M+ z17
    . t# i; o, j9 q# j% \- W  shift, diff, pct_change 是一组类滑窗函数,它们的公共参数为 periods=n ,默认为1,分别表示取向前第 n 个元素的值、与向前第 n 个元素做差(与 Numpy 中不同,后者表示 n 阶差分)、与向前第 n 个元素相比计算增长率。这里的 n 可以为负,表示反方向的类似操作。
    7 Y- I# |: W+ d2 X( W4 z: f" p- h. ]& m- q' A
      对于shift函数而言,作用在datetime64为索引(不是value)的序列上时,可以指定freq单位进行滑动:
    & i0 N+ U2 r% U
    3 U* K3 ?1 F& x/ j& Z8 is.shift(freq='50D').head()3 e; |) T# a  E" G
    Out[113]: ! H' c& `  R: U& t+ t1 |
    2020-02-20   -13 N; h' R# e! P" E% L$ d
    2020-02-21   -25 f& R1 ~$ ?; E4 N% ^* \
    2020-02-22   -15 F& l0 d5 |4 o* P& }; S, P
    2020-02-25   -17 p* Q4 n, ?) T3 m; x8 ]
    2020-02-26   -26 i" g% e6 `+ J9 o) q* L+ @
    dtype: int32/ w3 G3 n% x# x6 W: k* M: k( z
    1* w6 u" q8 O( G7 o+ R
    2: _2 [# F/ z/ \9 q
    37 A0 R8 v/ R- o
    4$ @3 I4 E: h' g
    5& b% n: V) E( P. Z1 y! W( D
    6
    6 F; S* d$ W: |& Z  Y4 Q% `7
    * T2 K5 W- F& [+ u8
    / D/ S; B( e2 f; q9 i, B  另外,datetime64[ns]的序列进行diff(前后做差)后就能够得到timedelta64[ns]的序列,这能够使用户方便地观察有序时间序列的间隔:/ @9 s% _5 i5 s& R+ R
    + n8 o# U; W/ t; o+ h0 V* J
    my_series = pd.Series(s.index)
    5 J7 i7 f) \4 x% Rmy_series.head(): h. Y0 v: a, F7 w3 r& n" D
    Out[115]:
    $ L2 w* s% s8 C8 ~0   2020-01-01. S; y3 \8 b4 g8 c5 A5 `$ a
    1   2020-01-02: g9 I, h; p* H; ?
    2   2020-01-03( H  C- g, \2 g
    3   2020-01-06$ [, n0 j6 E1 g5 L
    4   2020-01-07
    ! `# b  B$ E9 k  [; idtype: datetime64[ns]
      k) `% T  [5 ^. t
    1 W" t& T  P* Q& Smy_series.diff(1).head()4 `+ }) m2 I3 x
    Out[116]:
    * L. D1 I5 C8 |" |: B0      NaT! @/ C7 a3 l8 N  T) i- \
    1   1 days7 r1 r6 H- Y8 \8 H: m3 Y& C* C
    2   1 days/ n5 m3 {$ J2 t* B
    3   3 days
    $ C0 v& K5 i) H4   1 days
    / y& }! x! W$ |/ n9 Adtype: timedelta64[ns]
    + ~, o; X. E+ B8 F( h
    . g& D3 x& I) g$ o. z/ D+ I! W11 @" \. s* f! \+ N' l
    21 b. k# k8 f4 ?2 [4 ]; i. Y
    31 }7 u* W) \9 ?& Z0 a. b, f
    4
    - S1 t4 l8 ^% L5. P9 q5 _1 P! ^, g' j
    6
    2 ~& }2 U# ?& k8 a5 C7 U7
    ! `, F  U  H* v$ ]: |& ^9 t82 K0 [2 s- z/ I# E) t
    90 u' f" ^; L" O& H# @9 I2 p" q9 H
    10
    ! K% s: r; \6 a0 L3 @$ E2 I9 q6 p11
    3 |: _( w) o2 I2 @6 L! T12
    & b8 a# f( ~7 j1 H$ i. b& g# v3 |; p8 ~138 E' t3 W, A+ u6 D' D5 S
    144 |3 a. t/ M8 |+ s
    153 W- Z/ r' F  j5 C2 R
    16
    / R# B& @( N5 R, Y2 h3 m17
    ) M! x# s8 ~. h& ]" t" d$ a18
    ; U+ t9 H9 Y- e' Y+ z10.5.2 重采样2 y! M% W5 b! `8 B( n& S# s
      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)
    # U/ s6 Y# s& d& x/ p& E  P( |" _常用参数有:
    ' b( M6 k8 j, h4 p
    # w% e# w0 D, H* ?' R7 \5 Krule:DateOffset, Timedelta or str类型。表示偏移量字符串或对象
    7 T* D+ a! V8 n( c( }( Saxis:{0 or ‘index’, 1 or ‘columns’}, default 0。使用哪个轴进行上采样或下采样! r, W& W* t. o7 B0 J" Z
    closed:{‘right’, ‘left’},默认None。表示bin 区间的哪一侧是闭合的。所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。
    , H5 H. n+ i/ l  W7 A  N7 c; {! E5 Vlabel:{‘right’, ‘left’}, 默认 None。hich bin edge label to label bucket with,所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。
    . e0 r" n8 H& gconvention{:‘start’, ‘end’, ‘s’, ‘e’}, default ‘start’。仅针对 PeriodIndex,控制是使用rule的开始还是结尾。
    7 e+ d: t$ n9 a/ z- X# i: X" r1 Eon:字符串类型,可选。对于 DataFrame,使用列而不是索引进行重采样。列必须类似于日期时间。
    2 K8 p& [  }: m# f7 h' \+ H2 ]level:str 或 int,可选表示多重索引MultiIndex的级别,这个级别的索引必须类似于日期时间。5 [, y( a5 D0 s* q
    origin参数有5种取值:
    ( h. ?# B& i7 V& ?- _2 P) @) Q‘epoch’:从 1970-01-01开始算起
    ; [0 f* b; r5 A& p; Q! v, f9 B‘start’:原点是时间序列的第一个值
    $ D) j# T& {9 l2 q, j! y3 H6 J‘start_day’:默认值,表示原点是时间序列第一天的午夜。
    . M9 [: J. l. {# g. u'end':原点是时间序列的最后一个值(1.3.0版本才有)7 N  ]0 _. F. p, {8 t4 p- j
    ‘end_day’:原点是序列最后一天的午夜(1.3.0版本才有)
    # [2 G( s' k: V4 Woffset:Timedelta 或 str,默认为 None,表示对时间原点的偏移量,很有用。
    $ z* b. e+ @8 J% D) \8 t5 C  closed和计算有关,label和显示有关,closed才有开闭。
    : N: i, M7 i" }+ Y2 y  label指这个区间值算出来了,索引放区间的左端点还是右端点,closed是指算的时候左端点或右端点是不是包含。
    3 |0 c# C' ]/ Q6 q( Z6 s/ I% ^2 J+ q+ B4 g4 _6 O0 p" Y
    重采样对象resample和第四章中分组对象groupby的用法类似,resample是针对时间序列的分组计算而设计的分组对象。例如,对上面的序列计算每10天的均值:9 C( H5 v& }% D1 ^: Y
    s.resample('10D').mean().head()8 N# r. U2 U% J' U
    Out[117]:
    ' f5 U0 J' H8 t# K% @* L  D2020-01-01   -2.0000009 f$ _& `7 \& j3 |2 |% E" \
    2020-01-11   -3.166667
    ( A) P, q/ P/ Q% i2020-01-21   -3.625000
    5 ^  ]2 g$ i' e1 [( l* r# g2020-01-31   -4.000000
    9 \, q. a/ ]  C2020-02-10   -0.375000
    ) q, T( f+ C8 G5 mFreq: 10D, dtype: float64
    " w* @" k8 i+ q7 X* U$ J1' b* s: d# n5 Q" r0 M4 y' j
    2& \1 @- [# X6 L: i. Q; Z* G0 M
    3% G4 v" N4 r2 m# |! t8 u
    4
    1 S! f; y5 ?4 N) _6 e1 D! b( y5
    ) U- S9 V; m% ]' c0 J% Q0 |. X. G& ^6" \7 G# u+ g1 e3 k& V4 M  g1 h
    7
    ) H+ W. F& y$ w  M5 Q8
    3 L$ W3 L8 U- d- ]" m1 [! s  n可以通过apply方法自定义处理函数:- k& P: S  U: y9 y" c
    s.resample('10D').apply(lambda x:x.max()-x.min()).head() # 极差; p# c  }! W* _/ `

    2 \6 o! \  I' [2 c$ SOut[118]: / s! K& _3 ~0 e8 _- q
    2020-01-01    3* W3 W* K% X- b3 f
    2020-01-11    4
    : J* }+ h) A) |- ^' i3 b2020-01-21    4; z1 i1 S" H$ D9 T( `
    2020-01-31    2; q5 I6 W! H8 X' ]% D
    2020-02-10    4
    6 I0 `, T+ C2 B, MFreq: 10D, dtype: int32
    : U; q- c1 W4 E$ V  d, N0 B" {1) s+ D' u. a4 U6 q3 c9 i
    2
    1 C7 m6 Y3 f* q9 B6 z: L3. I# C/ L( p  ?  n7 f
    4/ p7 p9 R- G) F0 C! Z
    5
    7 K$ W. K0 d) W" j61 f! j$ R+ G# u' @/ k
    7
    3 H6 U+ {* J. q& v7 `8/ J& ?  C  u& ^, \
    9
    0 B4 t+ E0 z: Q" V3 I) ?& v. H8 t1 `7 ~  在resample中要特别注意组边界值的处理情况,默认情况下起始值的计算方法是从最小值时间戳对应日期的午夜00:00:00开始增加freq,直到不超过该最小时间戳的最大时间戳,由此对应的时间戳为起始值,然后每次累加freq参数作为分割结点进行分组,区间情况为左闭右开。下面构造一个不均匀的例子:
    . x3 x. [  l+ \2 D9 ^: O, i
    3 j- o2 O+ ~7 z8 h) I8 F) |! gidx = pd.date_range('20200101 8:26:35', '20200101 9:31:58', freq='77s')
    7 ~$ O( u2 ?' T- K. ^data = np.random.randint(-1,2,len(idx)).cumsum()) A7 s# j5 L  E, D& v
    s = pd.Series(data,index=idx)
    ! O; y3 o( M( h) C$ Ys.head()$ j1 A- v* ]+ I# U3 k6 y5 W

    ( D4 R/ K3 Y" H# r& G" k8 [% nOut[122]:
    % i3 s+ b- H. D+ `( P1 l: A2020-01-01 08:26:35   -11 y. J) g. k. t: d6 T; g6 H
    2020-01-01 08:27:52   -1$ f. b% d1 i4 s, G% \  A. f- j
    2020-01-01 08:29:09   -23 a% [: H( a4 M* B
    2020-01-01 08:30:26   -3
    , x* ~5 [- i& j0 Z, h& _2020-01-01 08:31:43   -40 |  T+ e  N; i6 j
    Freq: 77S, dtype: int32
    6 @2 S1 h8 m  q8 T! l1
    5 j4 t' x; R9 I0 `7 f27 n; c0 [; V( Q
    3
    ; v! U4 x; G1 H) Z" f' s4( X0 m, G, r' S# f$ Y
    59 R' ]; _, Q$ n2 K
    66 J0 \9 \  X0 ?( w, }- V) k) f
    7: E9 H8 z+ a& \. x' l5 c, j
    8
    & q6 D7 a+ d# u) M/ G9 v9
    5 K% z9 a1 m+ H& O& y4 f  j10- B) r, x: z2 v/ E4 J
    11
    * C7 Y. L' g( l0 }  G12
    ' Y5 Z" @- o) B- u  下面对应的第一个组起始值为08:24:00,其是从当天0点增加72个freq=7 min得到的,如果再增加一个freq则超出了序列的最小时间戳08:26:35:
    2 @  ^) f) V% w4 `
    & U4 @, h: Z% Ds.resample('7min').mean().head()5 Z% B; \$ ?0 \' O) ]2 U2 R
    Out[123]:
    * E' h( x( z  e* ^9 H% ^) H2020-01-01 08:24:00   -1.750000  # 起始值,终点值包含最后一个值. w6 T# `. B1 [: ^2 d/ @4 z
    2020-01-01 08:31:00   -2.600000
    # u, R# O% P; h8 }+ E2020-01-01 08:38:00   -2.166667
    4 a& n9 p4 C! F$ V4 D  M2020-01-01 08:45:00    0.200000; O8 [# e! n# X4 O3 V
    2020-01-01 08:52:00    2.8333337 v3 s3 K+ R9 W6 f
    Freq: 7T, dtype: float64& j9 w3 H+ r# P1 F0 z+ j' i
    1, g8 m2 V9 s( y: W: F
    2, S. |. U' _, T, p* ?
    3
    ) _7 U8 P6 J' N, H& i42 S- o' c) F+ r# W
    5
    6 L% Z  ?3 k! H' e5 j' }6
    ; ^+ M5 P' P1 K1 A  T7# g0 I  ]  ?4 g# ^) S
    8! e- {  c4 `2 A0 E% T2 {
      有时候,用户希望从序列的最小时间戳开始依次增加freq进行分组,此时可以指定origin参数为start:
    ' h" V, c/ ^( i8 b$ ]7 }! I
    1 ]% r2 O, x3 {s.resample('7min', origin='start').mean().head()4 N/ v. }& b) s: @) X
    Out[124]: ) a0 X3 V  `: p
    2020-01-01 08:26:35   -2.3333335 M. N2 i! X6 I/ w4 a
    2020-01-01 08:33:35   -2.4000000 t9 H" ?0 m! Z  ^$ e: u
    2020-01-01 08:40:35   -1.333333/ D* K  `: F* E1 e# w2 @
    2020-01-01 08:47:35    1.2000003 S) Y, R0 f8 \/ ~1 o( R
    2020-01-01 08:54:35    3.166667
    % g+ s. s" X; S3 u" H" W0 U* UFreq: 7T, dtype: float64
    ' H( ]1 S0 V1 @* q( x; _  _1$ \6 O: x* S6 h  Z
    2# c! e" E* z2 G$ ]: E
    3
    4 Z* h/ o2 \. W8 I' Q9 g44 g4 |2 L& `7 v  r& _/ U
    5; B; d6 x  E; E! D
    6
    0 j% E& Z2 K6 I2 h- h& T# ?; k7
    1 r! X" U# z' P. }9 f& C% O8
    9 R! U. ~4 z( }9 L$ q; x  在返回值中,要注意索引一般是取组的第一个时间戳,但M, A, Q, BM, BA, BQ, W这七个是取对应区间的最后一个时间戳。如果想要得到正常索引,用’MS’就行。" O, D! r% `+ }' {6 [# h

      V' W/ _. a3 c: P  @s = pd.Series(np.random.randint(2,size=366),( w/ m& H# O" s4 M
                  index=pd.date_range('2020-01-01',. i/ G3 l% `0 ~* n: o( d
                                      '2020-12-31'))6 {  Y8 J- @4 B

    + ?$ }; r" a- ~3 I  w' Z& e7 S8 c' |
    s.resample('M').mean().head()2 {& h4 P& A- y  S3 k
    Out[126]:
    # l3 }* j4 F& T, C0 x2 g  i' q2020-01-31    0.451613
    9 P- h; C  S0 e2020-02-29    0.448276& g" r% A- o+ t8 b. S0 k
    2020-03-31    0.516129' b/ U. Z' z8 X
    2020-04-30    0.566667
    ( H, J( a5 N8 C0 ~7 ^2020-05-31    0.4516134 E: B7 m3 e9 U: A( L  u
    Freq: M, dtype: float64: ^3 Z: h# }$ @( D2 u7 E

    2 m* S/ t% u: M) Bs.resample('MS').mean().head() # 结果一样,但索引是跟正常一样" y$ H( i3 h0 R# y3 n
    Out[127]:
    1 g5 H+ L) a" l! T/ B2020-01-01    0.451613: D- X! |+ {& L0 b9 k  r; H
    2020-02-01    0.448276
    2 F4 ^, h% H- W4 f3 `1 z/ _; l2020-03-01    0.516129& N! R) \1 m$ L9 d* [; x6 g
    2020-04-01    0.566667
    % y" ]4 l0 q, B) P0 l/ |/ b7 f2020-05-01    0.451613
    # b% u9 t* a2 C: Y" t  UFreq: MS, dtype: float64
    ; B1 i4 _' H5 x# ^: Z- p% R! z  d' D! g% U3 i; @' q
    1$ Y0 @" L! ?+ U- j5 [( O
    2" V4 J( Z$ ]: Q2 T6 @) z
    3
    # u2 A* ~0 i9 e3 }" @- |+ T0 N4
      e2 z* ]& M& }9 l! ?" a5
    4 b3 }- L+ U/ {7 t$ P62 {9 M  \2 M+ h
    7( V- _$ x# p9 V' K! _
    8
    4 F$ b( x" s& i% ^, _# ]2 I% ?9
    ) ]3 l& c8 O& p0 b- O6 V9 n/ ^+ g10( Z+ H; s: B( \! g* z+ o
    11
    - m, R9 x; Z# @. f, y/ |12
    ( e6 n! a9 e$ _6 b13
    , O2 [9 l$ A5 V  ^14
    8 S6 s# j. U' i) L15
    : i4 a  c9 L. w+ Y. _16
    5 u) N0 S- G- F  q& o2 P! m" k17
    7 ~" k) B5 ]4 [+ }. A186 c/ @% G+ E' f  w
    19* g0 u" x( j1 O: ~4 S+ g* \* e& d3 s
    205 H% R. W- x5 p. r8 V8 p! c
    21* U+ q8 \3 W6 e) ~
    22
    & q' Q, n* ]5 }对于 DataFrame 对象,关键字 on 可用于指定列而不是索引以进行重采样:& J! {$ y, e/ G: v. L- E2 ~1 ]
    d = {'price': [10, 11, 9, 13, 14, 18, 17, 19],. H& v0 E4 [5 N& C# w+ W2 r$ m7 s
         'volume': [50, 60, 40, 100, 50, 100, 40, 50]}
    : b! r. X) o* v3 E/ xdf = pd.DataFrame(d)
    1 u: n, J# {7 Z+ U5 hdf['week_starting'] = pd.date_range('01/01/2018',: q! @7 |$ X, _$ ~! ~9 U
                                        periods=8,7 O% Q* F% |" ?9 @0 j
                                        freq='W')
    ( Q0 H/ V2 i. D: f$ c2 Vdf4 K) j; y1 e' }5 V2 }, U
       price  volume week_starting
    " y3 S. d) e: G$ H" X0     10      50    2018-01-07
    $ Y7 v' o& a9 ]2 E* a1     11      60    2018-01-14
    % [$ C" c. o% O! U# q+ E% y# d7 I2      9      40    2018-01-21* {. Q! j3 J+ _+ x' I9 R7 E
    3     13     100    2018-01-28
    ) I6 ?2 ?1 _' }8 V% h4     14      50    2018-02-04& @9 B" c) O) L
    5     18     100    2018-02-11' d/ y5 S% H7 @
    6     17      40    2018-02-18
    : w& e  L! X6 \+ I5 W/ t* Y* b7     19      50    2018-02-25, O3 s; Y9 r" j" |( Y
    df.resample('M', on='week_starting').mean()/ ]* t$ g" }* r- W- Z) @  U4 {
                   price  volume- k3 o/ A$ p& G2 v$ X( f' p& g8 b
    week_starting  K# w0 q0 u1 H5 n
    2018-01-31     10.75    62.5. R* e) d' A. y
    2018-02-28     17.00    60.0" s# c1 P, p9 k* Z6 p) w8 r
    ' o  z( p) c7 ^# m
    1
    0 Z6 I8 k" p# T( l% m" }) s8 ]2
    2 y6 y" R+ ?5 A( q3
    + B2 o$ p! r& f/ U" U$ r4
    - ^6 A9 ^7 ~5 ^: B5
    & i& ]/ v" f# J6
    0 }. T5 |8 p: A, E6 b2 U" s5 D7
    8 {1 O, L7 U1 e: r; z8
    - T( X  E3 y4 T& [; R9) l/ m( P2 \  W
    10
    6 B! B, m8 Q; a1 g0 ]; e11- {3 _$ q/ C4 i
    12
    + \& `  Q* s! ?) m, n, Z7 U/ u" b$ l13
    2 B+ i9 T  |5 m4 @1 z14% C7 B. Z0 s" B0 I
    15% O6 p3 _9 d! C8 W3 ]2 O
    16
    & y  ]- a; D2 s, O17
    9 J( C- b. B$ M0 D1 s! i18& L) \! `; W2 p* n8 L- @1 ^
    19& k1 E) o9 f, d9 N& _3 y' X: n
    20
    ! I. s0 S! G; @; n21# n8 |7 k( R. W+ T2 r* e$ f
    对于具有 MultiIndex 的 DataFrame,关键字 level 可用于指定需要在哪个级别进行重采样。
    7 K# ]# N) S1 x+ T/ E* B6 j, }( edays = pd.date_range('1/1/2000', periods=4, freq='D')
    * a4 G$ [  a+ r5 Od2 = {'price': [10, 11, 9, 13, 14, 18, 17, 19],
    2 k$ F- g* T: Y5 P; q      'volume': [50, 60, 40, 100, 50, 100, 40, 50]}0 q& J0 w2 X, A
    df2 = pd.DataFrame(8 K8 T1 B$ E9 f1 d
        d2,5 N/ ?4 q6 l7 |  ^) X( f. v1 Z
        index=pd.MultiIndex.from_product(+ s# e, n# i: U3 ^  q9 w. m
            [days, ['morning', 'afternoon']]
    , Y+ U0 \+ [  w9 V4 L1 b+ m0 o    )
    ! o) j( X! P& D% a) j* @)* \. X0 T4 o* s4 U2 m
    df2# \% L  }9 c/ p& f" o! u4 E
                          price  volume
    ( z% h& q* H. u( r. v' Q2000-01-01 morning       10      50
    4 v0 }! C7 Q+ J( }( s# Y           afternoon     11      60
    ( Q4 a5 I8 p# E& n% S8 `2000-01-02 morning        9      40
    + `% x3 x8 o) k           afternoon     13     100
    5 A9 ]+ B  _8 w6 {% M; k) A) X2000-01-03 morning       14      50" h) w% W( a# Y) E
               afternoon     18     1007 [8 O/ ]8 k5 L" I  b! W% @2 L6 B
    2000-01-04 morning       17      40" }: @5 l% J, ~
               afternoon     19      50
    9 _) ^, I$ Z' q& P$ R% j) Idf2.resample('D', level=0).sum()- e8 I8 R& r% |
                price  volume
    9 s  z4 h( d5 c2000-01-01     21     110$ Q4 R" o+ e4 O) ]
    2000-01-02     22     1400 i8 n8 }  Y! c% K6 B8 [$ a
    2000-01-03     32     1502 N+ v, \: K, s" H* i* r+ K/ G2 S
    2000-01-04     36      90! T: L! w5 _; h

    . H7 i) z$ T7 B" s  ~2 w& I7 ]/ e1
    , A/ p3 D+ J2 V& x' j: b( w$ s25 ^" F& S1 {  M# m. l$ F0 \: k
    3
    * C0 _2 m' R3 u9 D+ ?44 q: x  Z3 P7 d
    5
    7 S& E; t' x7 D8 j  ^" {) x8 Z7 d9 o$ P1 ]6
    # y" G- k8 K% S7% T+ P5 N3 H& M. N: E, u0 H7 P! E
    8
    ' }! y$ X" Q' T4 j0 q5 ?9$ f( W: v* u6 c7 U% w0 r% f
    10* ~8 V* ~% l/ v8 n
    11
    * V: a: x: h: H9 F12
    9 c0 i7 y8 b* Q/ d13
    9 A. \) |: e8 N0 R140 B. q9 [. g+ a- o9 ^  n: W  b
    15/ ?* {: f! a' |0 L- D( b9 m
    169 K" Y% o5 s7 u0 |5 u8 R
    17
      ~3 [3 \) ]5 ^4 A, g$ R18  }# F7 `7 c/ G
    19
    ) z. Y8 Z/ q+ M& I& C203 Q6 R5 X4 a1 Y" b2 Y
    21
    ) V( E: e5 \2 B8 G, g222 @. I7 J' h+ O* D' P
    23. G  I3 U! n; M* K
    24
    & e" `2 C: R  N  J25. L6 o9 B/ y3 i9 y
    根据固定时间戳调整 bin 的开始:% e" ?; ^' Q0 c: L
    start, end = '2000-10-01 23:30:00', '2000-10-02 00:30:00'
      V  H$ G: G3 t- s5 M* arng = pd.date_range(start, end, freq='7min')  z/ ?, R3 U0 z! [8 ^
    ts = pd.Series(np.arange(len(rng)) * 3, index=rng)
      G, ]" v: Y+ U8 L) D2 a# ats6 S7 r  g" a  _- M5 c
    2000-10-01 23:30:00     09 z) ]1 b8 d1 J& V/ W  N: b
    2000-10-01 23:37:00     3- R. G$ R9 q: |0 B2 e
    2000-10-01 23:44:00     68 u, ^$ u- \. m, B8 L
    2000-10-01 23:51:00     9! M+ r) t2 ]& I
    2000-10-01 23:58:00    12# t3 O0 k: W8 D
    2000-10-02 00:05:00    156 z7 Q$ ^$ e  B  D# F9 i
    2000-10-02 00:12:00    18) y! T' o# J. e6 G& i) \, A0 B5 A1 `
    2000-10-02 00:19:00    21
    / d# g" t7 r, j/ v' \4 X2000-10-02 00:26:00    24* G" k' D9 Z( t
    Freq: 7T, dtype: int644 q- U/ }6 T( b8 T& u

    . z# T3 \5 a. i) L4 zts.resample('17min').sum()
    : A# U( ]  m, s+ g6 E- m0 w7 F2000-10-01 23:14:00     0
    : b6 r, }  Z( X/ t2000-10-01 23:31:00     93 @  p) J3 P3 \$ ^9 z5 a
    2000-10-01 23:48:00    21
    ! G! K' D- {# @( r2000-10-02 00:05:00    54
    5 k& O7 n) e& I# i2000-10-02 00:22:00    24
    + U$ }; Z. b  Q$ e+ JFreq: 17T, dtype: int64
    1 ~3 D/ }" [' }& I4 K4 {; G, D  w# N" {
    ts.resample('17min', origin='epoch').sum()5 Y% R' j" M4 a- X
    2000-10-01 23:18:00     0
    4 a4 ~" g1 U6 o5 h/ I2000-10-01 23:35:00    18
    7 B7 i% y0 X5 `# K: z* {: A, Q2000-10-01 23:52:00    27
    0 V0 y) Y: m) U3 B4 k" ?2000-10-02 00:09:00    39
    9 q9 a, B% f# ~" T$ Z  z/ y: K  U2000-10-02 00:26:00    243 Y' f$ S. D. _& A$ I  J
    Freq: 17T, dtype: int64
    $ _8 `% X) q/ o! Q5 z% Q% F2 M* ?) J) ^& w4 S& G! a0 @( c
    ts.resample('17min', origin='2000-01-01').sum()
    3 g' \. p2 O- i* F2 F2000-10-01 23:24:00     3
    # {1 h! H8 j% T; |  v. e) j2000-10-01 23:41:00    15
    $ ~" r! l4 d2 N9 A& r2000-10-01 23:58:00    454 u2 k& z! d* U; f
    2000-10-02 00:15:00    45
    - X9 z6 _* \1 JFreq: 17T, dtype: int64+ ^$ Q; N+ A* k% n5 O/ G& L
    6 z" l* h, d9 m  Z: k; f
    1, o* G2 z3 f3 S, D; T7 u
    2
    * g) _' g7 E0 E2 L31 w8 T! I! b2 V( N, `
    4  m# n( ~( k- x+ Q5 r8 }  p2 i
    5
    ) g$ E- G- n7 P& m6
    8 s+ k$ B2 l  U' c" Y9 Q! T7
    0 P" p4 t: R0 f. \5 V1 A, G/ R84 R/ v+ I, b+ u, M$ O: H
    9
    0 o' N3 ?# K# G( l3 S10
    # B3 E4 D; ~# U3 s) K8 j6 E# [7 I- w11
    6 E- ^- @; n$ ~" A  X5 U# d0 S12
    : X; T. y7 q/ y; n+ q13
      Q2 i. r$ \0 q; D/ v1 |14
    ( [$ n" y) l( g( z1 A' V157 i- h4 d4 z+ d# ~1 `/ L
    16
    ; ?6 w  d; I9 c4 i* J- A170 O% X6 v& T8 R; m" O( r! x6 i
    18  F. L! t/ d0 J: d) P% E7 ?; |& @5 A
    19$ J3 L) ~1 a! ~4 R
    207 p7 b, r6 N2 a5 E
    21
    ) Z0 L" Z0 Q, |, T1 b# }0 w/ N22
    " ?1 z2 B& d3 l: ?% J, M5 p23
    % C8 J  A7 K% U! o( M* i; W244 {) r) D# x/ ]% r) S' }3 u
    25
    / Z! T* F" _4 [264 Q4 x( u; A. E/ l
    27
    ! V- D. m6 }6 h. s28
    , [! [- |) V* ]) W  ^. T( O296 j+ u' L" M, U7 d
    30' d/ i( b( N( i+ A7 a. V
    31  W) T6 T: F3 f2 M, h
    32
    % E3 N! Z9 P7 M33
    ; m  n' I& X' P: ?# d34, X5 S5 G" L- J! S* f# G; U
    35
    % _# [2 Q% u5 W& w36  R, I' T5 j/ z1 ?$ S
    37
    6 R* r! R% G" J( q/ ~如果要使用偏移 Timedelta 调整 bin 的开始,则以下两行是等效的:) c% D% e1 ~( L$ N" ^$ A/ Q
    ts.resample('17min', origin='start').sum()
    ! n2 w  F3 [6 y6 z" ?2 Sts.resample('17min', offset='23h30min').sum()1 e. x4 S% S( y0 w- U
    2000-10-01 23:30:00     9
    & w; p2 W5 a5 o# L4 J; w! x2000-10-01 23:47:00    21
    2 `5 g# ]$ A) q; U2000-10-02 00:04:00    549 {$ p. ?5 X+ v! S' q& b6 {+ C
    2000-10-02 00:21:00    24
    & B9 W# K* f6 ?6 W  a8 rFreq: 17T, dtype: int64
    ) r4 y5 a% ?& v( [- b( o# j1" i( a7 }, Z% d& F: s
    27 C( J5 E$ F9 b$ d2 g6 B
    3
    8 R" e2 N9 q( U. s# E6 M4
    9 D+ b1 U, a  ^# d/ e9 k5, v: q6 ?3 n% @/ B, [
    6
    4 R$ C4 N; n/ s1 Y+ A+ h6 r7; s/ r% X; [- Q- h7 L2 H
    10.6 练习
    $ h- y  R+ _  B9 z# F0 nEx1:太阳辐射数据集; r$ `) A7 ~1 D2 }/ V8 Z7 e% E
    现有一份关于太阳辐射的数据集:# w* {) `; Y. h3 a" Z) @8 z" r

    ( g0 X/ C) Z5 h5 Vdf = pd.read_csv('../data/solar.csv', usecols=['Data','Time','Radiation','Temperature'])% T$ s! o- d: s+ V
    df.head(3)( P9 h4 V& t. t; S

    1 M( u6 l7 N# ]* kOut[129]: " `  L! A2 U8 A8 e- L% F- W& }
                        Data      Time  Radiation  Temperature+ t  Y9 l: ^# A5 \4 a4 C: g. |  c; V: l2 Q
    0  9/29/2016 12:00:00 AM  23:55:26       1.21           484 R4 M& ]: [* a4 }
    1  9/29/2016 12:00:00 AM  23:50:23       1.21           48
    7 L/ N4 x5 |6 S7 x4 d! r2  9/29/2016 12:00:00 AM  23:45:26       1.23           48: W4 q2 m2 z  w! C. b9 h/ S* j- S. o! D
    10 C- R6 R  q/ @& J' ~
    2% ?3 x( h$ _6 n4 ~4 W
    35 L+ A6 w$ o, d0 ~3 I
    4# l7 s( U' J+ s
    5) z! F# S1 D% O9 r
    63 K8 M5 R( ~1 T3 Q5 _
    7, Z. Y  r2 l2 R$ \6 l
    8! I0 c& X8 O* t5 M" p
    将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。; K+ b5 N) ~& h1 U
    每条记录时间的间隔显然并不一致,请解决如下问题:
    2 O+ l) L; W. H5 Z( B4 G0 o: F找出间隔时间的前三个最大值所对应的三组时间戳。8 E$ j+ s6 k  d( Z# p( t# W2 N8 i& m
    是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。3 k; V, ?6 ?" L
    求如下指标对应的Series:
    ) ?/ W- U! Q9 {  N8 i温度与辐射量的6小时滑动相关系数
    2 l8 K6 W* E/ F* b" v以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列
    " v+ z: b8 m0 J1 p1 J每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)3 T' M6 T- r) @  I
    import numpy as np
    # N. n% k) U: Y$ X$ T* Gimport pandas as pd( _( H# X* s6 Z4 q( s
    1
    + ^( c' h) Y; M4 Z. e9 `1 c2. @8 ]8 e) K" U( I8 O
    将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。" o% o& |6 J% x* a( p8 M) }6 M! ~$ O
    data=pd.to_datetime(df.Data) # 本身是object对象,要先转为时间序列
    ' g% ^4 L& M9 ytimes=pd.to_timedelta(df.Time)( p2 a/ S) o( F3 I8 a3 n
    df.Data=data+times& L! @7 ~9 {. w6 x% h
    del df['Time']
    . G- z0 O0 L# q; Xdf=df.set_index('Data').sort_index() # 如果写的是set_index(df.Data),那么Data作为索引之外,这个列还另外保留4 A9 D/ b7 p# m* ]
    df- K4 @% c' y1 u$ t9 b7 _! n, X* ^
                                            Radiation        Temperature
    $ G( n2 i' I  S5 WData               
    3 y+ ^$ Z2 x/ {! R9 O, y9 x0 b4 y2016-09-01 00:00:08                2.58                51
    ! y3 S+ U$ v* t: p+ J8 p( J" @2016-09-01 00:05:10                2.83                51
    7 Q0 g  p" T, w2016-09-01 00:20:06                2.16                51
    8 t+ G9 O% R- Y: K+ ]* M* m2016-09-01 00:25:05                2.21                51
    1 |2 S- T: g! K- D! E2016-09-01 00:30:09                2.25                51! `$ y" C3 N8 F1 _" }1 n1 s" `- Q
    ...        ...        ...3 r" w& E2 f. V2 r
    2016-12-31 23:35:02                1.22                41
    / I: M; K" O4 c2 O1 I2016-12-31 23:40:01                1.21                41( Q$ i) @$ o" Y8 z1 c
    2016-12-31 23:45:04                1.21                42
    . w# a4 |  L" v+ d' H2016-12-31 23:50:03                1.19                41/ @5 K( Y; x# q: E
    2016-12-31 23:55:01                1.21                41/ R4 Y( V3 v) _- o, i

    ' h+ N2 j5 v  D5 b# @1- h% X  }6 |+ `8 C$ [  v$ H
    2+ E( T& ^$ N% z3 R; l: Q$ _  C
    3. l! A& S/ N8 U# e7 h+ ~0 Y" u
    4
    ; U$ H; r7 R3 x7 H3 |5
    ) N' x2 e5 b1 |( r# C6$ ]; V: F1 L8 u/ m1 _0 P3 \
    7
    5 l( I# N( w6 |8 f5 u8) ~2 Z* w  i1 e# |3 A. o$ S
    9
    * r: l0 [* Q% v( N8 e% g: E10
    & G* b+ R, c7 t$ L2 n, i119 a7 }/ T( V+ V  G) B+ }
    12
    6 G6 U* h& i, Y0 ^+ W+ j13, [5 X* G$ p4 w. W9 ^
    143 D+ f1 P5 `/ K, v/ R
    15
    # Q* x' R2 }: X- I. R167 r9 n; `8 Z3 l2 S1 i9 t8 v( f
    17; K# D& K: r9 z2 e5 Y$ J
    189 a: N0 a+ g8 @& G$ F. u1 I
    19
    + x. a9 `6 T  a# L: O每条记录时间的间隔显然并不一致,请解决如下问题:
      ?. l' `+ h) r* n找出间隔时间的前三个最大值所对应的三组时间戳。( L5 H2 ]+ ~5 r9 t4 b% d
    # 第一次做错了,不是找三组时间戳
    / W- H9 R/ E& E7 g1 @: w+ @. h, pidxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]0 v4 v, b  H* [( r0 e
    df.reset_index().Data[idxmax3,idxmax3-1]7 J6 W0 g5 c: z4 L6 l: i8 K# o3 u/ h
    ; v& f# B  m7 T3 J1 x+ @: K, ]
    25923   2016-12-08 11:10:42. W2 s% N! ~0 k6 r
    24522   2016-12-01 00:00:02
    % h% Z- C  G# c- a$ ~7 v8 v7417    2016-10-01 00:00:192 y+ c- }  r" m' m( \
    Name: Data, dtype: datetime64[ns]
    1 ?6 B' ~3 S" S( h9 d: w+ u: z1
    : v% G, V/ L1 v! v) U2
    0 m% D: z! V6 h( J' B" ~( R$ y3
    $ R; e. E' o( H4; G' b0 @2 |$ h# h' r- ?6 |
    5, h) P4 r' g( N4 i
    6' E# j- h$ ]% x& k
    7
    3 N6 F( I+ j* N0 M( C+ M8) P" q! K6 W% u9 x, w
    idxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]+ t! ]9 \7 H- X9 `* r
    list(zip(df.reset_index().Data[idxmax3],df.reset_index().Data[idxmax3-1]))
    4 _; v/ V7 I2 h# U$ `% Z' l- t* W* A) B  o2 }
    [(Timestamp('2016-12-08 11:10:42'), Timestamp('2016-12-05 20:45:53')),
    1 a6 a* y; B# @& l. T- D! X% J (Timestamp('2016-12-01 00:00:02'), Timestamp('2016-11-29 19:05:02')),
    / o  G2 e# R4 T) |" Q" s (Timestamp('2016-10-01 00:00:19'), Timestamp('2016-09-29 23:55:26'))]6 Z: l% I! Y$ T  O; D( }
    1; u  j7 D2 @% M; S% j
    2
    1 q: q; S( h9 h# b$ {3, @; ^: h7 b8 m; m* u2 I
    4
    / R1 r+ G( F9 L+ a4 Q$ R9 B7 U. N5. f. C% M* P# x' B
    6
    4 U1 O( p) C- H# H* g参考答案:
    7 U' |/ ^. \5 z* i4 X/ s
    ' s% a$ k, q, w# f8 j& zs = df.index.to_series().reset_index(drop=True).diff().dt.total_seconds()- ?0 z( w" d% U  _5 c
    max_3 = s.nlargest(3).index
    $ Y- [4 m- _" G/ |; vdf.index[max_3.union(max_3-1)]
    , Y) h) k7 y; J! A2 h
    8 x5 J$ d" A4 Q: iOut[215]:
    8 k5 y0 W: v6 h: rDatetimeIndex(['2016-09-29 23:55:26', '2016-10-01 00:00:19',
    . a: p) t" J5 T. \5 U+ E7 Z# o               '2016-11-29 19:05:02', '2016-12-01 00:00:02',* S6 q" c* u: N/ n4 z' f" H
                   '2016-12-05 20:45:53', '2016-12-08 11:10:42'],
    ! B- G9 f3 b; ^+ w              dtype='datetime64[ns]', name='Datetime', freq=None)6 Q, J7 u5 J6 n  o6 J
    12 ^7 F9 _$ t: m, L1 W7 V0 e1 ^
    2
    ) L" W, H0 p# k8 p3
    & ^. n& @8 Q  I: n. h; @2 o' N% Z4: D' E' ?4 g/ S& q& D4 q
    5
    1 [: X  a2 s, L2 Y3 K6
    % {* V) i4 [8 G7 `" m+ X7- z# N+ a9 m' Q6 P
    84 J8 r1 s: k7 H7 _) ~
    9: R* x; X: O# b
    是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。: M, N; R2 ?( b
    # 将df的indexydiff做差,转为秒数后排序。再求几个分位数确定取值区间
    " g, J" R7 d6 L, P  z- F" Hs=pd.Series(df.index).diff(1).dt.total_seconds().sort_values(ascending=False)0 `& E; ?4 ]4 m, K
    s.quantile(0.9),s.quantile(0.95),s.quantile(0.99),s.quantile(0.01),s.quantile(0.03),s.quantile(0.05)
    1 |$ f. n# O8 B# ~5 w) g. W; J/ n' n( S( U8 ~
    (304.0, 309.0, 337.15999999999985, 285.0, 290.0, 292.0)9 x4 J0 _4 F- }! M0 N" Y
    1! F0 T2 y! z" |& g! S
    2
    5 E7 K* l' y$ b7 \! |4 Q36 a% Z7 b- s. v
    4
    , X( n: v/ v) b0 {& g6 r. f5
    " ~/ d9 f; w$ i$ ^%pylab inline( ]! i0 a# z2 H
    _ = plt.hist(ss[(s.values<337)&(s.values>285)],bins=50)
    8 k% L- M! U9 Z% W' L, kplt.xlabel(' Timedelta')+ Z4 v, H9 e( W: S
    plt.title(" Timedelta of solar")
    7 F2 X9 s, q( o5 n0 d1 A% m- b, b1
    - ]8 n4 F* {$ K- K# }1 A6 O2
      B3 O  W0 I. q- O0 T& U3* J3 \- p+ O- h1 ?8 j, b* Q
    4
    ! u5 y3 [4 s. B2 P6 Y) l% A% z$ m; {! C" h3 x7 ]& ~

    ; N& B# s2 C- B% S求如下指标对应的Series:7 ~7 B; p9 Y+ W
    温度与辐射量的6小时滑动相关系数
    , ?3 C0 |# k4 s以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列  C6 f: }; j1 G# H6 y5 A. R
    每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)9 q' Z5 a8 K1 P% x6 I. L8 u
    df.Radiation.rolling('6H').corr(df.Temperature).tail()4 }# f8 k- G( p" [9 o8 O' r% j
      s! u2 O" e- ~9 k" q
    Data
    % P9 j' Z) ?6 S2016-12-31 23:35:02    0.416187
    0 E6 L$ _. [5 L3 S% G8 D2016-12-31 23:40:01    0.416565
    4 c0 |- e& S3 x; N4 R/ E3 Y) n2016-12-31 23:45:04    0.328574
    6 x; U' @' x8 {, \, o# w2016-12-31 23:50:03    0.261883
    ) d2 d/ Y9 M6 _# P- z+ v8 a2 L2016-12-31 23:55:01    0.2624060 Q8 a: \+ S- V: [
    dtype: float64! a. ^- i, j4 [: H
    16 Y; b& e' T6 w) R, r) I
    2  T- j' _+ @& K: A
    3
    + h8 s- ^& t/ F2 S" ?( V+ W+ t4
    4 o& w  k4 H- l# d: \1 m3 x1 p5
    ) x; y' I+ S7 G8 f8 ?) h65 ~2 w" y: F4 y$ ?. u
    7% @, ~% G8 K# }- a  @3 u1 ~
    8# j2 g2 f7 n4 }! |* \
    97 }/ z3 \' ]$ W3 X
    df['Temperature'].resample('6H',offset='3H').mean().head()
    + Y1 A/ _3 S% W7 N# M& s
    2 R* G  J2 i0 mData
    0 r3 u" y. U. K$ p# m2016-08-31 21:00:00    51.218750
    ' b, Z% O) D2 s2016-09-01 03:00:00    50.033333! R) {6 H" p* c& K7 x
    2016-09-01 09:00:00    59.379310
    ! y! Q8 J8 m% T& l1 r2016-09-01 15:00:00    57.9843759 K# E1 e1 P& i, q1 Z4 C% c
    2016-09-01 21:00:00    51.393939
    : K# z8 d; p' T# ?( AFreq: 6H, Name: Temperature, dtype: float64( Z9 @- Y  N/ G! E
    1
    6 c' b& ^5 g& z) X  v5 |" L1 _# x9 i2) J' z; W) x. [" V& E
    3
    : q$ L9 u: K2 {4
    5 [& x* Y" `; N' A- {) Q7 |6 I5
    6 X' ~8 M: d6 X' n6 N: R6- u- b6 K  ?8 i/ P7 t
    7! ]5 k0 `% j$ b0 H7 P; l: ^3 I
    87 d5 l1 u0 }* e  {
    99 D; }, b$ o9 a, B8 C, F& y7 h
    最后一题参考答案:* K+ S, @0 r) t# _: X

    0 M& v. L- W) @$ W8 y( A# 非常慢5 r% X! D2 A5 J) n
    my_dt = df.index.shift(freq='-6H')/ X1 H. D" W! Y( ?+ g0 U
    int_loc = [df.index.get_indexer([i], method='nearest') for i in my_dt]
    : f0 ~+ C* z* mint_loc = np.array(int_loc).reshape(-1)
    : v: x& M& [! U& y. V& Gres = df.Radiation.iloc[int_loc]1 e0 {( }1 P) m0 k5 t" C' }
    res.index = df.index7 q0 [2 {0 p, Q% T! G: r
    res.tail(3)
    5 A8 o1 x" u$ T6 v# e1 b  P1
    ) _6 C' s  Y; q, z9 v5 j* Q2
    ! k( K( B$ R* G) c# L7 a3
    3 W- v$ k$ U8 L8 S4
    / m) k0 g) F1 K: Q5
    ( _: n# k4 a7 Y9 `: U6
    3 c% b, i- `1 G* }; W8 {72 L' ^' h4 ~+ w2 [) c
    # 纸质版上介绍了merge_asof,性能差距可以达到3-4个数量级! Y; g3 v% F. ?/ Q" u
    target = pd.DataFrame(3 d- s) \! k. L% b
        {0 M) k  X* _" |% N
            "Time": df.index.shift(freq='-6H'),
    + I3 a; E+ E% U1 D1 ?, r* o# t        "Datetime": df.index,! G/ O! Z/ ^+ r- v; e5 w+ {
        }
    ) y1 s+ N1 s$ I% E5 T5 @9 z)' P/ h1 W4 n" [* ~3 o  e2 I) z$ e

    / y  K, H, }( n- S& b3 t  S, dres = pd.merge_asof(& p" ^( Z) f3 `
        target,7 G. D( ~" I! x' X8 P- t
        df.reset_index().rename(columns={"Datetime": "Time"}),. y3 |- ^" [5 @, r9 o, H
        left_on="Time",
    7 F  H+ d2 p' S" U+ O) {' Q    right_on="Time",4 d" u8 t2 H2 c; I# g0 A& _
        direction="nearest"
    + w1 Z+ y8 n: c, C7 s7 M).set_index("Datetime").Radiation& i7 B8 @4 K1 X# Y

    0 ?  N3 ^& w7 c& @res.tail(3)4 ~/ h9 y0 S% |3 [
    Out[224]:
    ; f- q1 R" |  }8 H5 }  j$ vDatetime
    & @1 R6 Q+ N9 t: \2016-12-31 23:45:04    9.33
    , ]% Y  @7 M  G: f5 e* B2016-12-31 23:50:03    8.492 P$ B" ~/ e! a& f1 o% X! a% L  S
    2016-12-31 23:55:01    5.84: D$ O) W7 z* |3 I1 u
    Name: Radiation, dtype: float64! Q2 L8 J' I3 K) v3 r, ?
    & C; k/ L9 F2 M: G  R
    1
    4 R* o1 w' N. Z7 \5 P" _3 G2$ g# X; k4 X& I" W/ w
    3: B  H1 a  z) O6 g+ m* M; i8 f
    4- @6 \* k7 |. J9 |* `
    5. O+ b, r- B8 }6 R% a% V! V
    6' Z4 O9 T7 K) [$ ^1 B+ E
    7/ n: `! u! l; t8 q2 G
    8$ c0 R( N( S+ c% _: n+ e% \9 W
    9. B9 u6 R9 @# x9 o% E7 k
    10
    1 G# t% X4 [  I* }" O8 a11
    0 x6 V; T( Q' e8 r12
    ) v+ {" i! T' ~& t  u1 }5 y13
      @) t' ]% b& I& Z7 k" X6 M5 v14
    1 P8 o% ]; W6 D6 E: a) \% z15
    / ~- L( D: b( I1 K16: W2 q1 [0 N( H; F, \
    176 t  b% ?2 Y, U! k3 \
    18
    + i% e- D9 |. H" [19( u8 }2 K3 u' a1 P/ s% X( q9 q# {
    20
    2 U3 D" m9 k+ h, C* |, {21& C0 c5 ^! @( ~' p+ V
    22/ b- u' M9 A2 J8 z7 R
    23
    # S0 Q* w" Y' \+ d, O. PEx2:水果销量数据集
    0 F/ z; @1 G9 L6 d2 T现有一份2019年每日水果销量记录表:- |) @! v; ~/ o, Q& L
    ; p; E7 t# E2 K7 E" E
    df = pd.read_csv('../data/fruit.csv')
      w/ P3 q( N0 i7 Jdf.head(3)% y- Z: Y( R( s3 c/ `. H5 z1 ]# d

    3 ^2 w6 ^* W7 ^0 S1 OOut[131]: ( @% C7 N, h# E& L7 E- m
             Date  Fruit  Sale
    ) D9 O: X2 A: a$ @7 H0  2019-04-18  Peach    153 W0 l) W2 y, C: T. k
    1  2019-12-29  Peach    15
    ) f$ Y' ^; f+ l* e- l% w& T. R2  2019-06-05  Peach    19) x4 @5 u# y3 }/ v  o
    1. @& |, P. L2 C$ E( k! m! c+ T
    2" D' p* C0 k& I" U
    3, P: M" s; d" K; U
    4; c# E# O( |: X+ n; D
    5( a2 ^2 l% A' v0 z* u, W9 H
    6
    9 L, h/ s/ P/ n( F75 R, Q8 s2 w( ?# i4 a+ E% W3 Y
    8
    % k# ?# [* m4 Q2 ~3 X0 p: w统计如下指标:$ H% j, A, e3 W/ k$ x+ j9 v
    每月上半月(15号及之前)与下半月葡萄销量的比值
    . r. T( i3 t4 K( h每月最后一天的生梨销量总和! G$ [5 U( }' Y/ F0 D& a
    每月最后一天工作日的生梨销量总和" Z7 q( K4 F( y
    每月最后五天的苹果销量均值
    - b$ m) {3 L. ~1 \/ c8 Q按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。
    6 m8 N+ D! |' r4 E- L; _按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。; g* _1 l3 |% M
    import numpy as np" E5 B2 I9 i( A: }
    import pandas as pd4 v( z- e( M/ V8 ^# ?* i/ Y1 W
    17 A+ \7 r, Y  ?9 V  W' q1 g4 P' n( h1 L
    24 X; {' B' r2 `! e
    统计如下指标:/ N2 F. Y, K7 M8 `7 ]
    每月上半月(15号及之前)与下半月葡萄销量的比值
    ) _1 |$ A9 F, P/ _' B每月最后一天的生梨销量总和
    ( S5 o9 U5 q, a! [每月最后一天工作日的生梨销量总和* u: O0 B3 q4 o
    每月最后五天的苹果销量均值
    , {( Y. n# x- x# N  W# 每月上半月(15号及之前)与下半月葡萄销量的比值
    4 B8 b, Y2 ~" Z* J" l6 O- Rdf.Date=pd.to_datetime(df.Date)$ o2 h+ i7 Q6 s' Q' h3 D, r
    sale=df.query('Fruit == "Grape"').groupby([df.Date.dt.month,df.Date.dt.day<=15])['Sale'].sum()+ W) s6 b. Q) U
    sale.columns=['Month','15Dayes','Sale'] # 为啥这么改没用啊
    ( ^. @) w: p- [, y  dsale=pd.DataFrame(sale)
    9 r) m3 q% M6 H, [sale=sale.unstack(1).rename_axis(index={'Date':'Month'},5 d+ V' a& F; v" m
                     columns={'Date':'15Days'}).stack(1).reset_index() # unstack主要是两个索引都是Date无法直接重命名
    " s, u3 I8 h4 n  |; c- gsale.head() # 每个月上下半月的销量9 j- Y$ C8 p/ \8 `( e) {

    , I" d) Y1 `, ~0 V6 Y  Month        15Days        Sale8 N" ?- d  m2 V/ G" K5 R
    0        1        False        105030 J# @3 Z- m" x
    1        1        True        123419 m) P! E8 ]! e* A5 x$ X5 ^
    2        2        False        10001* n* w& N* t. O( p+ R# A& q0 D
    3        2        True        10106
    ; i+ B# U7 O' @0 v. S6 C; d. }4        3        False        12814
    3 Z" v0 C, ?0 O: [  d9 U% R& N( m# X1 t1 t# U1 S* ]7 y0 [5 `: w
    # 使用自定义聚合函数,分组后每组就上半月和下半月两个值,根据索引位置判断求比值时的分子分母顺序
    : w/ c. u  O' n1 t# msale.groupby(sale['Month'])['Sale'].agg(
    ! m# e; ?2 S( x) x: J                lambda x: x.max()/x.min() if x.idxmax()>x.idxmin()  else x.min()/x.max())
    5 }" T  s) T8 n2 E5 d& D; h9 ]' k% y9 H0 `' Q
    Month0 K2 b' `$ a0 C/ o0 ?
    1     1.174998
    8 f6 u8 m& |2 P! j$ k; }+ ~2     1.010499- W4 c* p( Z) }8 ]
    3     0.776338
    & k) J& A! J& u* D: x4     1.0263457 ~" H0 O9 @) b2 Q
    5     0.900534
    8 g- G7 {2 U& X) {# W6     0.980136
    6 N. f; Y$ G- t6 ]% j7     1.3509601 A/ P( q" Q6 ^! }# l
    8     1.091584$ g- H5 S0 L4 x5 A) ], \$ _1 p
    9     1.116508
    5 S# R" D4 N3 n0 [10    1.020784" c4 |  j* o9 c1 N4 i5 Y0 r) B7 P
    11    1.275911
    & x( g0 p; E1 i: J12    0.9896624 T  \, V9 v$ L% J0 c/ w4 P
    Name: Sale, dtype: float64! {. }2 D3 v9 U; F: \
    6 }( l& s, I/ {$ k4 \5 F
    1
    0 A8 b8 c  W% g0 K2
    - x6 L0 G" b; |9 O7 i3
    * |9 Z3 {; u0 J2 }( T3 W+ ]0 z4
    4 f6 A- B8 e, M: L  \& V5
    : ~7 z# O2 ~- Y" T7 p3 m0 H% h. @6
    1 }5 T4 ?( l$ M! z4 Q! g7' u/ L6 n# c; l' q; s; S. w
    86 I0 R& R7 l2 X+ y
    9
    & a! y7 R6 ?7 A10$ y( B0 ^* m5 f
    11
    3 w( e8 Y( X; [3 l' y4 e12
    & k" c5 ?4 p3 a3 `139 a, g; T) x6 f- O1 C
    14
    ! R* z' A5 D1 Z% i157 h- j+ R6 C* A! }, Y' G
    16+ w: T0 Z$ U% L1 [$ X6 O) Z: P
    17
    " h# e' R3 o: Q; d/ n  i18# S9 D9 D* T0 {3 y5 N
    195 b7 V) n( y' c4 |( e9 A9 r& H- X
    20( O- \0 `. a' ~2 e7 |$ @0 t- _
    21/ R/ Z( K0 n1 o
    227 J9 \5 F  J  `) {7 B
    23  T  h" d* I+ A3 b8 o
    240 ~3 S3 m+ ~% L  W/ P2 C
    25+ @& _+ \! I* v8 L- S! a' m
    26
    7 b0 I1 ?" C# ?& b: [- s27. s% o* d; `( J6 \4 l+ [
    28( \6 Q. D  l% }1 k1 u
    29
    6 H4 f2 W4 S. ?30/ X5 m  Y% d$ J3 ~, s
    31. n- u$ a" J$ A% r- R, i' I; h$ q
    32
    4 ?* F/ W: L# C. |6 m1 m339 C& m/ u3 K; ^" Z6 o, E
    34/ F2 f8 m* e& Y! O' x
    # 每月最后一天的生梨销量总和
    " ?! h2 e& c2 U$ v: e1 \df[df.Date.dt.is_month_end].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()
    7 ]; C& W# p3 f9 ~4 d! w9 J6 d
    / _3 A3 H: O" nDate
    " m7 O- U) ?# b8 w2019-01-31    847
    - b: B( E4 V* |0 X: _. j' q2019-02-28    7748 M7 c+ `& r6 v7 [! f7 U$ O& }
    2019-03-31    761* J9 p/ z7 g- ]7 V" B/ L
    2019-04-30    648' E" l1 k) |2 E0 h# b
    2019-05-31    616. v, d4 s# u" j+ y4 d2 W
    1
    % m$ @' y" @* O9 X0 J! V* X' Y2' [% |0 Q+ w7 N. t( a# ~0 e
    3
    7 J$ H7 Y# k( B43 O* u3 u$ @# B2 \/ d
    5
    " Y& J1 N+ ]! U3 ~. @' E6  y* i" ~) C6 D+ l4 o% p
    7
    9 V" g' t8 o, \' r% o8
    0 k( j. r& {) G9
    : F1 v1 s! d8 ]1 @# 每月最后一天工作日的生梨销量总和
    ) J% b# L7 j( j2 s# c" xls=df.Date+pd.offsets.BMonthEnd()4 Q! [& q! T; C
    my_filter=pd.to_datetime(ls.unique())& Q! K8 d3 G/ h, t& k
    df[df.Date.isin(my_filter)].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()
    4 I4 j$ O6 F; z$ ?5 s; s+ j1 F2 [8 i6 C# x$ S( ~0 H
    Date" r" H$ @; |8 x8 ~# l3 T
    2019-01-31     847
    , p/ ]( R& T/ \4 P! ^2019-02-28     774
    / v: q/ V  }3 r! O  R, \2019-03-29     5107 W6 j* h" j) b7 I! I; g* K* Q8 N
    2019-04-30     648
    0 e' G" ]. k' a  Z( a  w' x2019-05-31     616
    : l! S& C* ?) U1 P' w! w' O1/ f( q8 h( [, R
    2
    - j9 [. N0 a  Y2 f3
    : }, y1 {2 C1 d  ^# Z; T  {4) t. e& f1 y( t
    5
    0 n+ W) u0 f  s0 V/ }1 S6& m5 j' ?+ H! D; x
    7) P* F1 y' ]2 B1 ]* @
    8$ b2 ?4 g5 b1 P! ^* @$ ?
    9
    9 h% s6 v- E; @* b; _! }6 b2 L10
    4 B! N$ a  m) @11
    0 R3 R( g2 i3 j/ y: R  A; [# 每月最后五天的苹果销量均值: g& g3 B. C& E' w# k6 s& L
    start, end = '2019-01-01', '2019-12-31'
    * Y( m1 V( V7 @& V9 Q1 ~1 N! H$ c/ h" Uend = pd.date_range(start, end, freq='M')8 \! }5 p+ Z6 G8 p+ b: |
    end=end.repeat(5) # 每月最后一天的日期列表,重复5次方便做差
      q" ?& z+ y( z- f; g8 K$ V+ Y6 m7 I7 T
    # P8 K2 s  ]" v3 k+ ]& W( T+ |td= pd.Series(pd.timedelta_range(start='0 days', periods=5),)
    3 E9 A1 l. N, z$ R; ttd=pd.concat([td]*12) # 日期偏置,最后一天减去0-4天
    ' ]/ E$ y  \' ~) [# g0 {end5=(end-td).reset_index(drop=True) # 每个月最后5天的列表
    . m; V/ V( c$ J6 h( ~; D5 s( C: X- A* W* g/ ~: f/ d
    apple5=df[df.Date.isin(end5)].query("Fruit == 'Apple'") # 每月最后五天苹果销量# _* j% j% F& _0 M0 C
    apple5.groupby(apple5.Date.dt.month)['Sale'].mean().head()3 w- s, K! E" ~" I6 ~+ G
    , d0 A, p" W! x  G9 J$ d
    Date
    7 A+ Y: u6 z0 u1     65.313725+ h/ E$ y: V4 e" H% Y: ~
    2     54.061538
    . p  S( D4 c; `" r- ~3     59.3255819 Q1 n& @+ T8 r
    4     65.7954557 Y9 g$ e/ c0 g7 S" S/ O' i% H
    5     57.465116( k! G- o6 W% v9 @$ i3 b& {. ~

    + @! ?, p# J  C, @! q' I$ O/ q, f1
    . U5 }- B# M; R7 m" W$ k20 p' J, M( j: }% ~& R& N* q0 t+ J9 H
    3
    - ?6 C, Y# n( B" _* \+ A" r" u4 l4! P$ T: J0 T9 \
    5
    ) _; n! V' L# F: l6 e' A* o( R6. {! d" _2 o$ X5 ]
    7
    5 [6 U9 E" T  ~; x* h0 I8
    5 c. f, n& H: @$ A6 {7 q95 J) C0 D, Q  q: F
    10* g6 t) r" e$ v1 u! {
    117 J7 y! [' u+ A) e
    12
    3 b/ }# m1 ^1 ?, T1 x; C13( K; d  c+ M( t1 O# W/ i
    14
    ' W' B! w5 Y, o7 |+ ]0 b15
    ' j* W8 C* T: S, B/ A16
    6 c% M! {8 J% t9 P! {$ I. O179 ?0 `; z; B* z+ p( H* n& g
    18; l4 @- J8 m4 p2 m2 k4 v) n8 Z. x
    # 参考答案:" t! X6 e6 b# x; U
    target_dt = df.drop_duplicates().groupby(df.Date.drop_duplicates(
    ' O- r9 ]6 G3 _0 B, x            ).dt.month)['Date'].nlargest(5).reset_index(drop=True)* b/ \# L. w4 u+ I0 H2 w% i% ~

    : ~" X' ]" l6 s! ires = df.set_index('Date').loc[target_dt].reset_index(  q$ H, M7 t) ^' v% R  S  M% x) g
                ).query("Fruit == 'Apple'")- Y' ]4 J2 `8 k, C
    $ A% M. {3 A6 i0 }& ?* Z
    res = res.groupby(res.Date.dt.month)['Sale'].mean(
    4 E! _  s% J) h4 j/ ]' g( p            ).rename_axis('Month')
    5 G/ X# a& d( \1 y' n+ \
    - R$ t1 L" c: Y
    0 ^5 Y! P5 k  A. Wres.head()
    6 i, o8 W0 x/ x% l) D! kOut[236]:
    / v2 P) \% t; vMonth
    ! g( H3 R) n4 e8 y1    65.313725& b7 d: h( W# d2 S
    2    54.061538
    2 e& N# Z0 X( p6 N* I3    59.325581
    0 E5 q& ?6 m$ p4 p1 ]2 T4 q4    65.795455
      L/ g$ Z0 t" R% g! ~, N2 X$ r& \5    57.4651164 `( B, i1 e, K* x3 l) N
    Name: Sale, dtype: float64/ e3 S1 A* a/ @* E% R
    0 K/ Q0 A, A% ^$ S8 t7 n7 {
    1
    + L% d  I& y% l) t& B% A( D2
    9 M" ^+ B9 A( N6 O' ^: Y* j8 F0 S$ Q5 i34 }+ a9 w0 A" |1 T' Q$ M
    4
    3 I8 e# z6 ^' x% J( i53 }0 c5 i0 M1 O) |# G
    6
    " E8 i6 R! M/ Y( F0 j  [* Y7
    ' @, I3 D. t; ]" i, J% z8) X$ h1 w4 J! w
    9& o9 u/ p( F; x! r
    10
    % [8 r- n" T# F- m8 ^8 `& B11
    - m  d6 F4 Y( b/ b  g: J2 U126 B* F+ u$ R* m) C6 ~/ O$ J, d
    13
    5 C' U: N1 _6 e14
    7 ^& n8 I  H2 r4 Y" A- I5 i; L/ y15
    6 P+ f3 }7 Y) D+ c8 x, ]/ e16% k+ {! b7 z" Q# g
    171 z4 f* K" `1 Z- D5 I
    182 T8 s/ x2 W7 D
    191 i6 ~8 S# G* M5 G5 F' b1 _
    20, h- y8 w% r1 a1 `9 H+ }
    按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。# z8 r5 O6 B0 d- ]4 `, V
    result=pd.DataFrame(df.groupby([df.Date.dt.month,df.Date.
    ! s! Y8 g1 X8 E; l  J7 S                                        dt.dayofweek,df.Fruit])['Sale'].count()) # 分组统计
    7 ]. f8 C$ ^3 u3 u- O) o                                        . w- m- [$ X6 i8 f
    result=result.unstack(1).rename_axis(index={'Date':'Month'},
    & n, Q. a+ `1 U% a5 Z                 columns={'Date':'Week'})  # 两个index名字都是Date,只能转一个到列,分开来改名字.5 D7 Y4 H/ `; O
    result=result.swaplevel(0,1,axis=0).droplevel(0,axis=1)
    $ E! @8 J/ A' D, `result.head() # 索引名有空再改吧& d7 H% Z* A' ?# ]/ n% v3 {

    7 D+ o( H8 e0 l          Week        0        1        2        3        4        5        61 Z8 u, h/ y0 e* X  V$ u% S
    Fruit Month                                                       
    - m+ V  y) t) r* B, O  WApple        1        46        50        50        45        32        42        23
    2 r& J% P  N# |5 X' e# `Banana        1        27        29        24        42        36        24        355 l0 ~" H* i: _9 ]* E9 p/ \
    Grape        1        42        75        53        63        36        57        46" [6 I: D( Y7 e% _7 D' t+ ^) P1 R! |7 F
    Peach        1        67        78        73        88        59        49        72
    7 G; o: w' y% J$ qPear        1        39        69        51        54        48        36        40
    $ N- \! g# f1 W7 n& h) I5 D; O1
    1 N5 [6 g4 m- W0 f$ U% C. h2/ n5 O5 m& Q! u6 ^. _$ V
    3& r$ T% A) i5 y) n) B
    4
    ! Y- @/ a/ R; y5* [# l) Y5 i+ b* _3 ]
    6
    * @; G- ], C! M" c0 u1 s7& b. a* B$ _4 i5 R! e3 X
    8
    " c. i5 M3 D0 D9
    9 e$ v. y" ?/ h0 i104 y; O* [5 I* {: g, P. P5 }
    11
    6 q. H# w: c( X: x! B& A3 e12
    0 L/ P& a7 ]1 F: Z; x5 [13
    7 D) X3 f; y* A( M14
    ) `! W5 B' O) W$ ~& d3 ^15
    % F  x- l# V, U5 Y/ }: N4 j( k按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。
    . y* D& `; L# c3 r# 工作日苹果销量按日期排序  c4 j' t0 Y+ {6 V* E; [
    select_bday=df[~df.Date.dt.dayofweek.isin([5,6])].query('Fruit=="Apple"').set_index('Date').sort_index()
    ! m: K+ G/ {& J$ u; bselect_bday=select_bday.groupby(select_bday.index)['Sale'].sum() # 每天的销量汇总2 t5 a2 N3 s  K0 e* C
    select_bday.head()
    . g0 [# g/ M5 E" g% O4 \8 f% c  `8 K1 P0 q4 T: m4 c' ]
    Date
    5 U4 X' Q* F2 k4 e9 W) A) g8 x2019-01-01    189" |* b- E2 H% q* D
    2019-01-02    482
    4 _7 A; T8 d+ ]& I" S8 x& q2019-01-03    8908 o- j, N, F- B! |" f# D8 e& R
    2019-01-04    5500 M1 ~8 H9 _9 {9 M. Q8 N- D
    2019-01-07    494
    $ o# J7 u6 @4 k+ r. N1 t, L& K* F6 t& L1 T' h
    # 此时已经是工作日,正常滑窗。结果重设索引,对周末进行向后填充。6 V& p4 ?: @2 ]5 j' L
    select_bday.rolling('10D').mean().reindex(df.Date.unique()).sort_index().ffill().head()9 E9 G& E7 W& g& n! X  d

    ; V; v6 c  c0 P: Q5 ~Date
    + J0 }( g6 k& u( p2019-01-01    189.000000
    3 u' ]6 R! D) q7 \3 X- C" Q2019-01-02    335.500000
    4 F3 H, T) @: ?8 t2019-01-03    520.333333
    , S9 j. O4 T9 x- u' E2019-01-04    527.750000- ]5 v4 m! j% r5 b- t9 v4 l" |
    2019-01-05    527.750000
    ; p3 F/ k  z" I" ?. c
    . p* S5 ?6 C$ w! }————————————————9 C  B) \" N8 @. [1 }5 c2 ?  F( k; Q; X
    版权声明:本文为CSDN博主「神洛华」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。! {$ Z! q4 D( f
    原文链接:https://blog.csdn.net/qq_56591814/article/details/1266339138 g; j6 e" C" n

    : P7 u( u' P, f4 x* I5 w# @" `  U% n0 ]5 r. J, F% y
    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, 2026-7-28 13:50 , Processed in 0.710011 second(s), 51 queries .

    回顶部