QQ登录

只需要一步,快速开始

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

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

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

5273

主题

82

听众

17万

积分

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

    [LV.4]偶尔看看III

    网络挑战赛参赛者

    网络挑战赛参赛者

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

    群组2018美赛大象算法课程

    群组2018美赛护航培训课程

    群组2019年 数学中国站长建

    群组2019年数据分析师课程

    群组2018年大象老师国赛优

    跳转到指定楼层
    1#
    发表于 2022-9-5 16:11 |只看该作者 |倒序浏览
    |招呼Ta 关注Ta
    - |6 r7 w# p2 y. s+ J7 o
    ! D6 s1 L  i2 R/ A

    ; N7 ]% e# Z6 k* E7 ~) {文章目录  P1 X3 h% L4 F+ O
    第八章 文本数据
    3 A' J1 T' H4 f) s9 z0 X8.1 str对象
    7 a" y4 Y: U  G8 X8 ?, J8.1.1 str对象的设计意图0 F- X' p1 }: p2 K6 Q6 {7 L
    8.1.3 string类型
    6 f( i8 Q( D4 @) j8.2 正则表达式基础
    / [; M& `6 E( S: L4 g2 ~8.2.1 . 一般字符的匹配# E, g0 y4 E( O- o" E
    8.2.2 元字符基础/ R* ~8 f5 z+ b. K! s% K
    8.2.3 简写字符集
    4 e2 [$ y5 `% t+ d: ^8.3 文本处理的五类操作
    7 q& R# o+ R" I' h8.3.1 `str.split `拆分
    & R1 G7 K5 s! @" k- a$ B9 R' ?8.3.2 `str.join` 或 `str.cat `合并$ I+ B; a) `# U* o; ~' q
    8.3.3 匹配
    ' B; ]8 n% a, Z; N( ?2 R8.3.5 提取
    * Z  Y. Y+ F; ]1 o/ g. ]7 ^8.4、常用字符串函数
    5 y0 G) e4 t8 }$ H& G6 U# H2 R& ], k8.4.1 字母型函数2 L: x' ^) e$ p2 a/ G0 P2 a
    8.4.2 数值型函数
    ( ^* a% u4 E6 }& {% F) W8 e8.4.3 统计型函数3 B: L! G  @6 v! d$ M# k% E
    8.4.4 格式型函数
      k# i4 ?7 a4 R; `8.5 练习
    1 ?  n" F# i2 F8 D( P" oEx1:房屋信息数据集
    2 M% b  ~8 X* T+ yEx2:《权力的游戏》剧本数据集
    : ^5 t6 M1 [/ D* k9 h3 H8 N3 ^第九章 分类数据
    2 P* A. S* q& v6 `8 @) i9.1 cat对象7 [0 M6 r/ L3 [7 Z0 u
    9.1.1 cat对象的属性
    ' w6 G/ v6 P2 j9.1.2 类别的增加、删除和修改
    $ Q8 l  x# O* ]6 \9.2 有序分类6 m* p6 _* X- n, _& h& H
    9.2.1 序的建立3 B% e- a9 Q* Z+ N' Y- u8 L  w; |
    9.2.2 排序和比较
    2 g5 [- B5 P+ [( s9 R0 q* A* w& U9.3 区间类别
    ; {; V$ Z4 y' @8 ?% t4 |9.3.1 利用cut和qcut进行区间构造5 L) O9 i/ I' G% _3 N' @
    9.3.2 一般区间的构造, |3 L& @( Z  U) x- F  H, f( j6 g
    9.3.3 区间的属性与方法: f" ?: d* O8 \! }9 J9 @
    9.4 练习
    % t; V6 A5 ^/ @8 [Ex1: 统计未出现的类别$ x: `, I/ D5 y" V
    Ex2: 钻石数据集" t, l3 ]8 }2 s) W! q
    第十章 时序数据
    7 Q! l0 s9 l7 ?7 \& d9 E( |& B# M% Q10.1 时序中的基本对象
    ) I: {1 J' y% a4 w10.2 时间戳# x8 T8 T$ y+ }7 I4 s
    10.2.1 Timestamp的构造与属性' P- Y3 H/ D7 W0 U5 l& r
    10.2.2 Datetime序列的生成& D5 h7 V9 w, M- I2 L8 U
    10.2.3 dt对象0 j1 _/ z9 M' c
    10.2.4 时间戳的切片与索引
    . q* }# h1 ^7 O4 q10.3 时间差- S# X$ t* s: b
    10.3.1 Timedelta的生成
    * s, d9 P. H' Q10.2.2 Timedelta的运算' w$ w( V7 e8 ?- k8 T5 d
    10.4 日期偏置
    2 Y3 l/ R' \7 \4 V3 d8 l& ~10.4.1 Offset对象
    % F( z/ h0 `4 B3 N1 H10.4.2 偏置字符串
    0 ]0 `9 X, _# I  p5 z0 `; [1 D$ }10.5、时序中的滑窗与分组0 p/ f$ X. e4 B3 \2 h$ H$ j+ D
    10.5.1 滑动窗口/ ~+ G9 A2 n' M* c; F$ k4 N  z2 x3 g
    10.5.2 重采样
    ( M2 U' _6 O( @, B10.6 练习, B, V! G( h/ \4 Q. R0 F
    Ex1:太阳辐射数据集
    7 Z, [0 r  ~. I1 [* J% hEx2:水果销量数据集+ J6 V! y+ \: f& D6 y
      课程资料《pandas数据处理与分析》、github地址、讲解视频、习题参考答案 、pandas官网
    % b4 f4 f# ?3 |' \传送门:( U) ~6 }% A- o2 c& y* J) Q- r. W% m

    ( h3 ]7 c4 M' n* y9 u, cdatawhale8月组队学习《pandas数据处理与分析》(上)(基础、索引、分组)
    ) b6 X# t! G1 T6 A5 e; G' Cdatawhale8月组队学习《pandas数据处理与分析》(中)(变形、连接、缺失数据)
    7 s; u: ?; X% _4 W8 }第八章 文本数据  A. t! F8 |# R. [  A) g
    8.1 str对象
    4 u* Z& u4 s2 l4 N8.1.1 str对象的设计意图
    0 [. j& J2 I1 Z1 ^7 F# G  str 对象是定义在 Index 或 Series上的属性,专门用于处理每个元素的文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其 str 对象。在Python标准库中也有 str 模块,为了使用上的便利,在 pandas 的50个 str 对象方法中,有31个是和标准库中的 str 模块方法同名且功能一致,例如字母转为大写的操作:1 \3 W3 z4 _/ @

    8 y1 P$ L2 x7 cvar = 'abcd'
    ! }7 X* O+ D4 ~str.upper(var) # Python内置str模块- [8 S6 D" r; ^8 ^
    Out[4]: 'ABCD'
    4 Q. K% S7 `- k4 K/ h) _# W) l, P
    2 W! ?  c% T7 F2 m/ ~s = pd.Series(['abcd', 'efg', 'hi'])( f6 P6 ~- F; O

    . `; z6 ?1 c! W! c* Q9 m; Is.str
    1 f, \7 T5 N" sOut[6]: <pandas.core.strings.accessor.StringMethods at 0x2b796892d60>$ c; o3 ?, P  N# t6 d
    2 P# T9 z+ a/ K- g* ?  M- z
    s.str.upper() # pandas中str对象上的upper方法
    ; m: `" j( c8 U3 \Out[7]:
    4 j2 J3 {3 i4 _) ?5 f4 X, @, L' y$ G0    ABCD
    6 w, t1 r+ D' N( A+ C9 e1     EFG
    2 @8 f/ F# z  q8 J# E2      HI0 ]9 `6 [  m. H$ G; v
    dtype: object
    ) V" W  s2 s+ y9 L0 v/ a1
    & }& n3 W" S! G# [- A5 I% Z4 i. l2
    " j2 u9 ^; q' E9 y: [3
    2 \' B, H5 q. M5 O4+ W2 q) s& G- |% \
    5/ @8 L# I$ j* ~3 [4 `0 R1 s' L
    6
    7 ]! F; ]' h: X4 B# }/ E1 D: i7
    : H" G! U, d6 v, `3 Y0 E. }8) z' L1 i: n. i7 M3 a, X' l2 M9 R+ U
    9
    % B8 U, ]) c; A( W10: r- S$ {$ s4 X* c$ h; z
    11$ c" R# _$ I4 D3 C  }  c
    12
    ) t* r1 c  g( `! H2 b8 X$ `138 o* S2 n5 B6 V  w
    147 J+ i3 \% p# W% }
    15
    5 u/ c# S) ~8 [/ R+ c; j- s8.1.2 []索引器
    ' E- J4 U/ I  L4 F% Y* f  对于 str 对象而言,可理解为其对字符串进行了序列化的操作,例如在一般的字符串中,通过 [] 可以取出某个位置的元素,同时也能通过切片得到子串。
    ' N- p% y4 T2 t( ?4 u2 A9 F  pandas中过对 str 对象使用 [] 索引器,可以完成完全一致的功能,并且如果超出范围则返回缺失值:
    9 ?" K' \/ U! @7 u' A) Q* z+ b% V' n/ B! \+ F6 l, j( V$ {( t  Q3 [
    s.str[0]) @6 Z- T( x+ X
    Out[10]:
    2 \" R6 d: y3 m0 ^0    a: n3 v. j8 `& n8 j7 L, a$ J
    1    e( ^0 P0 v- C4 E
    2    h5 J: y, R+ B+ E, V
    dtype: object6 N. w7 P2 h3 p5 t

    9 _0 P1 d9 z- {0 O2 }s.str[-1: 0: -2]2 F2 o# t7 s# p
    Out[11]: , L% {) l+ e" W9 r7 V1 R
    0    db  a; D0 K" o4 F' q' @( u* F
    1     g7 J) A1 Q* Z, e& I' s
    2     i
    $ ]5 z, g/ J6 a7 Y) w0 `dtype: object
    9 t. o) ^# X+ K  }: D/ h$ R6 S* Q7 x* e" m" G7 j: H
    s.str[2]. i- d2 e6 G$ r  ]2 @
    Out[12]:
      Y" u$ x* ?) B6 {" G: Q$ g' Z0      c$ F/ Y9 o" d! y( o( v  A2 ^
    1      g. r5 K4 m5 \0 M
    2    NaN
    ' _" r4 M3 D; J& O$ F1 tdtype: object" V9 Q- h- E1 S3 [. j  W4 t

    ' i7 z1 [4 O4 Z, T- F$ x1
    8 U( m6 _0 O, h8 l( [0 k. [2
    9 G0 \# M& b$ A$ m1 D3/ J  n# n$ W% L- G7 q0 m$ k% z
    40 X' {: Z8 w: v+ R7 D( [
    5: B7 G* X5 m5 S7 p2 v2 |6 A
    6
    7 m+ L2 j  a' s7
    ( T9 U" y% {: ^! z3 ?7 K8: [( ?5 D. }0 \0 T6 P3 Q7 u
    9  {) d/ Y* s9 L: F' H. V
    10: V2 }) k5 w* S) t7 {  L: _
    11+ e2 y; ?: \7 C  u2 z
    122 d" k: t. G; y- ]2 H$ ?' h" J9 S
    13
    ( ~# P& J3 V8 G( {; k6 W: E7 T& p14- C4 U6 P5 t' J9 ]5 C) v
    15
    ; v% f0 A, U/ J/ y( i) ?+ D! E16
    8 K0 m) `' |* s1 x. B6 f17
    7 C( a+ y" {! E! ^% C# p8 ^181 R& ?7 G# M: r6 T
    19
    & ^/ V! H$ f& f20
    : x5 \! Q. z) W) Vimport numpy as np
    # U) K/ T1 N" _. x" j3 u8 himport pandas as pd
    ' P! h; g; G7 E/ ?+ B. ]. m: f
    s = pd.Series(['abcd', 'efg', 'hi'])
    ( [* b9 g. P* Q% _: }3 i0 [s.str[0]+ Q  a+ {' b+ E, j
    1
    ) e. X. ?+ W3 ]1 P" {2
    * {: s' m" ]1 L* D0 L" b31 ~9 A( v% o1 ^: r9 X' {
    4
    8 g# ~, l  R7 x! Y3 Y2 j5
    1 y8 u6 p% T* V" X0 i: c0    a3 L: l% {% ~7 W: P
    1    e. s7 s# B6 b* b  l* g* j
    2    h, X+ Z3 B/ }, {9 U
    dtype: object, k% h/ L6 R: p% D* i( o
    1
    $ p2 R$ t6 A- U# |8 G( }- j2
    : \2 F- v3 H3 Z  E, }3
    # n# h* N! w, _) ~$ m+ ~4
    7 A6 O4 [) b. m# K8.1.3 string类型
    ; m/ s; b1 ?+ ?& q0 E  在上一章提到,从 pandas 的 1.0.0 版本开始,引入了 string 类型,其引入的动机在于:原来所有的字符串类型都会以 object 类型的 Series 进行存储,但 object 类型只应当存储混合类型,例如同时存储浮点、字符串、字典、列表、自定义类型等,因此字符串有必要同数值型或 category 一样,具有自己的数据存储类型,从而引入了 string 类型。$ W+ {( k" C7 E% ~/ r, j
      总体上说,绝大多数对于 object 和 string 类型的序列使用 str 对象方法产生的结果是一致,但是在下面提到的两点上有较大差异:
    5 l1 Y2 ^2 s& o7 X! j
    , T& P0 X. H( y% G二者对于某些对象的 str 序列化方法不同。3 B+ T( s; ~$ U$ S4 b- X* I
    可迭代(Iterable)对象包括但不限于字符串、字典、列表。对于一个可迭代对象, string 类型和 object 类型对它们的序列化方式不同,序列化后str对象返回结果也可能不同。例如:* ]& I: D) {" I+ q  }$ L* Q
    s = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a', 'b'], 0.5, 'my_string'])& B  `) C) S" |% z# t- Y3 f: Q0 v9 E7 f
    s
    5 X2 Q# O$ d" ~15 a- ^% @# }  N6 F: C3 H
    2
    . O- ]3 A5 V$ h$ L5 ~7 X$ }0    {1: 'temp_1', 2: 'temp_2'}0 i3 a. s' ~9 H
    1                        [a, b]
    % f/ {) Y; A+ B2                           0.5# v  w9 m$ |2 L4 }1 J2 M# H! ~
    3                     my_string
    8 N. @& A7 X  K9 D$ g" {# ydtype: object
    2 S3 o. ?# Y* F& ~  W% w1
    ; E* _$ T5 }, N- k: J2
    ( N) M& X( {1 s8 R3
    6 w8 s% i* x  w' [2 ~: X7 Z1 S7 `7 \42 ]$ ]5 i" z/ N4 C+ w( G
    5
    / l/ ?. p9 _, Z, v$ Rs.str[1] # 对每个元素取[1]的操作2 v& a9 ~) I$ Q
    1, e8 a, ]1 v+ ~5 S0 \
    0    temp_1# _8 P& V7 c; C; A( B8 i& u
    1         b
    9 U9 P( q0 }9 P$ f4 c2       NaN
    - d7 j3 I) P2 X" Y8 l. h) P3         y% v7 @- @/ t" ~+ H  `& s
    dtype: object4 ]( t  v! t) M& Y- k
    17 S. d" _9 W2 c# U* ?
    2
      Y4 W% e" x7 R4 d3
    / c: `6 }& s+ t' ?' }# t4
    3 x8 C/ D4 ^6 G) C6 r& ]1 i' N5
    $ t) Y( W  `& G7 K6 K! I2 Us.astype('string').str[1]" c% h" C0 a# G5 t1 j9 I
    1
    * v: @: Q( m8 j3 [% H0 T$ |. m% I0    1- i7 @* e# Y' I
    1    '6 X' T; f$ Z+ ]; ?- k% |
    2    ./ A  k9 n; D2 V- l, t  k
    3    y0 p% Y% G0 D+ \+ Y; |8 t
    dtype: string) d6 ^' r- Z; j! n% L7 W
    1
    + ^- m2 d% ^8 L# @' }" Y- R: `2
      x- b- |- V- P0 z( ?39 I( O. J) J) `8 D2 D* C+ t
    48 s. v3 O: P/ F7 g: }2 f! ^
    5& K3 e3 F! r: b- d* h
    除了最后一个字符串元素,前三个元素返回的值都不同,其原因在于:( ?0 e, V% y8 k' w  d3 j
    + V; |0 g0 d5 z  N" t
    当序列类型为 object 时,是对于每一个元素进行 [] 索引,因此对于字典而言,返回temp_1字符串,对于列表则返回第二个值,而第三个为不可迭代对象,返回缺失值,第四个是对字符串进行 [] 索引。
    2 T9 ~" J9 M2 [2 [- U* \string 类型的 str 对象先把整个元素转为字面意义的字符串,例如对于列表而言,第一个元素即 “{”,而对于最后一个字符串元素而言,恰好转化前后的表示方法一致,因此结果和 object 类型一致。
    ! s) e: [% k, y0 X0 h( T3 ^5 M; ^string 类型是 Nullable 类型,但 object 不是7 o# A) w( @- [$ r
      这意味着 string 类型的序列,如果调用的 str 方法返回值为整数 Series 和布尔 Series 时,其分别对应的 dtype 是 Int 和 boolean 的 Nullable 类型,而 object 类型则会分别返回 int/float 和 bool/object ,不过这取决于缺失值的存在与否。  N6 m* Q( v) |* S' N1 G( ]# x
      同时,字符串的比较操作,也具有相似的特性, string 返回 Nullable 类型,但 object 不会。
    ( B$ S* J/ _* \  n4 [8 fs = pd.Series(['a'])
    4 |! O$ r( W+ `( m3 _# T' z- E9 S  w# f) m+ A# w: c6 K- `. I2 n
    s.str.len(), S: H7 N% V* B' Z
    Out[17]:
    $ W$ A; e# Z2 f0    1
    ( `1 _' I' m& n* ~+ {dtype: int642 ]. `, h& _/ c9 T& k

    # W; J* v. C  J! ]/ |4 q. Ks.astype('string').str.len(). A. ~. `- M5 j
    Out[18]:
    6 S" [% M, V; f1 A2 f3 R2 q2 R( F0    1
    9 @  W5 Q& p2 p. h; Y2 fdtype: Int64
    $ W  o% P/ y% S. E$ o3 R1 i& Y  t( X: p" V  n
    s == 'a'
    ; J) E0 N% u8 T) c: [; q9 S" ]1 m+ jOut[19]:
    9 T+ ]; }6 v3 ^1 F- g/ @0    True* s" Z9 l! v0 c
    dtype: bool! s9 r: H/ y. f  M' Q

    - q- K2 I$ ?) K* M( ps.astype('string') == 'a'
    & G+ r( M- k7 h" Z% @) I/ lOut[20]: 9 o1 d' Q3 b. n3 W6 A/ Q1 v
    0    True* g' P0 T, G3 g- d, {% N: z: c
    dtype: boolean9 ~9 l2 }4 E4 M# o' d

    ( ~9 e& F( u, \, fs = pd.Series(['a', np.nan]) # 带有缺失值" u  C+ o" f& U) B3 S+ w2 V  z
    ; V: k: [- S8 s7 V
    s.str.len(), u$ X% H; g/ O
    Out[22]:
    / V1 z1 r: D- a4 N$ m0    1.0; a4 T7 v" {9 f1 I0 V! |$ d
    1    NaN% b/ _& z- _8 R
    dtype: float64
    9 o% ?! m0 ^3 r, D
    ' ~* l* x! U7 `! q3 xs.astype('string').str.len()
    4 F; B# A8 E( X/ y; m, h4 qOut[23]: : U6 F- s( r  d7 _7 C
    0       1
    + k7 |- h5 B. s7 m7 D$ A1    <NA>- s7 a: u3 G. |$ k9 u
    dtype: Int64/ k' b; V3 U5 q$ u

      A! O, `6 @- Y5 U, d- _' c& As == 'a': V2 U1 i% ?1 h; c. N$ ~9 z7 n
    Out[24]:
    6 D% t8 p7 `1 F: a9 n5 B2 P0     True
    8 _0 j  I; ?/ s; R2 I8 W0 U1    False# S6 k  W* M4 Q' n; `, B! T
    dtype: bool
      Z: a$ ^! d# {- C3 L
    , |0 \; Q+ z% [  r, i. Z6 is.astype('string') == 'a'
    ! A# _6 Y# q: _) AOut[25]:
      ]/ L1 C0 y" t4 V4 r" \4 J( R0    True
    / |7 u+ b2 ~. @' p  y& d& j1    <NA>- J3 Y. d5 r) L6 z1 `
    dtype: boolean
    & B$ e  S1 ]8 Q
    " u" A9 _4 v- {$ r( Z) h1$ u. E# K0 V9 ^; W6 T3 {9 [
    2, j; ~: A, d" x0 X7 S* ]. D8 O
    3' M6 l2 l/ @/ v2 Q. `- W9 q
    46 @9 W! |$ h/ N& b6 ^
    5
    * \# B" M1 V1 E) _, Z; ?  A3 u6, S" `& R" o+ V
    7/ p, ?! \& O+ C* X
    8# R" c0 E, i: u& O+ c
    9
    0 b9 a; R  K4 s# B8 n7 O. n10
    ' M2 ^" E. ~# @7 g5 H* x11
    3 i) K8 k8 S2 o% U/ V, p& W8 j9 u12
    * E% w7 M: x* C+ l8 K2 I13
    ; A# f: p8 Y5 ^# x14
    # e$ J7 {7 W& ^, [15% g/ |& F: U( w) q
    16' l. S! T; ?' p. w: a7 {
    17
    3 w: R! Q8 {, E18
    3 a9 g) y. \5 u! G6 P' y193 k& Q- x) r# F6 v! C% o
    20
    ! l# U6 V- s- P' ]21
    6 l- v0 ]& I8 p: u5 W) T* F22
    ! X' Q/ a7 N1 Z0 l# j23
    + p8 z, v  g% j0 d1 D5 r$ X/ @24
    8 K  Z6 ~2 n+ Y  _( o+ Z8 y! o25
    7 h' l% I8 c& O8 I$ m# ^2 h26; X# m* Q" h9 o7 `/ j
    272 ?0 y  a+ f# M. t! f
    28
    , ]+ b9 r) [& J2 W29
    2 a; `( y) V! X$ r& S0 N) f308 S& r" t: s( p3 a6 _( o
    31. A3 J5 x# F; C, H# V
    32
    4 y  a0 P  ?. V" g33  q3 k# h5 J1 T# b; w
    34
    ) S, ^; ]4 t4 i* ^' Q- ?% j35
    " e) H/ {; X( k5 |1 g+ y. O2 l% I36
    / |# o* U6 Q2 D7 @# N37) S7 W9 Z" A9 S% I9 W' z
    38! ~  ^0 |$ B* I9 L3 z6 a1 `$ F
    39& }: O9 k) _% V5 |+ @! a( ^
    40
    ; u3 }& b: o* ?41
    , X1 j# b0 l* }421 g# \. i$ h8 I' q4 K9 H
    43
    : W! L+ k* J# X6 \$ g44
    ) I0 {+ |, j' t7 ~$ U6 M  p! n4 i45- c; k1 w7 _) v3 l  s$ R
    46
    ) i7 M9 ]! I$ a8 c; }% P5 u475 E( H; }* Z+ [7 h8 G; H& b. H
      对于全体元素为数值类型的序列,即使其类型为 object 或者 category 也不允许直接使用 str 属性。如果需要把数字当成 string 类型处理,可以使用 astype 强制转换为 string 类型的 Series :
    ) \/ b& |/ q  E" K9 D. U) l% A) f. w0 Q  F% i) }
    s = pd.Series([12, 345, 6789])1 N$ `$ x3 D7 w" r; H

    + b* m' P" R: Y+ t' L! ?5 R) q* ts.astype('string').str[1]
    ) y: H# b9 t, m7 S9 rOut[27]: 6 @0 i9 e/ S/ ?7 ^% V
    0    2# g" A) ?' f' u; |) C; ?! x4 c% J
    1    4
    $ C3 U8 g3 E* K. J. \* H) j2    7
    ( O+ f; b' G- \$ S. g8 Pdtype: string
    1 V7 D4 y7 a6 O7 Y/ M/ g  w1
    & e, y4 Q" J' }. L4 `2! h' E# \+ i/ j' d0 T
    3- k7 D. E" I" ]+ r% Y% i- v
    4$ T# x+ N# ]! d
    5
    ) V) q& V7 P" O& o+ f6
    4 Z8 K& B: |4 y; T7
    + ~% A" J- z. {( q+ V* }8
    # W+ S7 w" }) N" A3 a- j* X+ ?% O8.2 正则表达式基础
    " M+ d5 N, A0 ?. K9 T# f这一节的两个表格来自于 learn-regex-zh 这个关于正则表达式项目,其使用 MIT 开源许可协议。这里只是介绍正则表达式的基本用法,需要系统学习的读者可参考《Python3 正则表达式》,或者《 正则表达式必知必会 》这本书
    2 |+ c1 H+ Y. H2 v
    - |* i) }, Q, J7 ]5 U# o8.2.1 . 一般字符的匹配
    & X7 C: V/ j0 j# ?  Q0 S( M! O* ]$ q正则表达式是一种按照某种正则模式,从左到右匹配字符串中内容的一种工具。对于一般的字符而言,它可以找到其所在的位置,这里为了演示便利,使用了 python 中 re 模块的 findall 函数来匹配所有出现过但不重叠的模式,第一个参数是正则表达式,第二个参数是待匹配的字符串。例如,在下面的字符串中找出 apple :
    : e% J1 [! a$ ^7 p- d0 h, s+ }9 t% v9 {
    import re. b, X6 v5 a0 ~0 ^4 G

    5 f! X9 |7 @! C" _0 u% k# Q, L! ~# {re.findall(r'Apple', 'Apple! This Is an Apple!') # 字符串从左到右依次匹配
    4 l: H  E4 i: u2 MOut[29]: ['Apple', 'Apple']
    , }% M: B7 j/ C, x8 ]+ u& X% L7 \& \1
    ' f0 f* V4 E: h9 e$ l/ r2
    ! h, t; Z5 f; [0 _8 }# G' I, k3: A$ c7 k% x2 R; Y& ?5 f
    4! e' D/ ?$ D9 b- Q3 W( W
    8.2.2 元字符基础( J/ O+ O) S9 v% i
    元字符        描述
    ' a- U* k$ F/ x" T5 S( H! \, X% v+ H.        匹配除换行符以外的任意字符, ~% ?* j: t9 J7 \0 U. Q
    [ ]        字符类,匹配方括号中包含的任意字符, O" A$ e2 c5 y3 }
    [^ ]        否定字符类,匹配方括号中不包含的任意字符
    4 X* j% p8 S' v, _9 p; |*        匹配前面的子表达式零次或多次
      a5 C, \' V% \+        匹配前面的子表达式一次或多次。比如r’d+'就是匹配数字串,r’d’就是匹配单个数字
    ' ^2 I* R% @- e, w* ~?        匹配前面的子表达式零次或一次,非贪婪方式# J0 P5 e/ G6 ?
    {n,m}        花括号,匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
    3 H9 f6 U! T" m0 l( U(xyz)        字符组,按照确切的顺序匹配字符xyz# }- h  ^: n  U
    |        分支结构,匹配符号之前的字符或后面的字符4 v, }7 l1 _8 x5 z3 M. c( Q% e
    \        转义符,它可以还原元字符原来的含义/ y, j2 H5 y1 w6 P* ~# V% @
    ^        匹配行的开始- f' c3 h( w' R7 g" W
    $        匹配行的结束
    3 e- o( A: N1 C0 ~& h! Nimport re
    9 h& K4 C3 z7 ]" G) |re.findall(r'.', 'abc')
    , ]7 Y0 d; M( Y) `3 o4 e! |: x2 x1 mOut[30]: ['a', 'b', 'c']. P- W8 ~  ]2 [! A

      O8 k5 X) [; Gre.findall(r'[ac]', 'abc') # []中有的子串都匹配2 f  i1 z) M' `- M( B' B/ r; j
    Out[31]: ['a', 'c']3 q) x7 }  t0 h" m8 i* H  `
    7 d  @, y/ E7 d6 r4 }
    re.findall(r'[^ac]', 'abc') . b) Y- {! B  Q# K
    Out[32]: ['b']
    1 d5 B8 X6 V, z- H
    1 Q5 n+ ~# e* O. }7 Lre.findall(r'[ab]{2}', 'aaaabbbb') # {n}指匹配n次% ?4 t/ ^6 i1 r6 b' [0 e) ~0 c. d
    Out[33]: ['aa', 'aa', 'bb', 'bb']* c( b1 ^$ T; Y$ u. z9 @7 l
    9 l) q! n) ^4 R
    re.findall(r'aaa|bbc|ca', 'aacabbcbbc') # 匹配前面的或者后面的字符串& h# L8 J- E0 c3 h
    Out[34]: ['ca', 'bbc', 'bbc']/ y) G( m; O" O& s' i
    " r4 Q- X5 p% B2 B( W1 `( n" `
    # 上面的元字符都有特殊含义,要匹配其本来的意思就得用\进行转义。
    . o) \" C; w% k" z4 S! B* s- Y% W& a* P4 y"""& q9 ~" W! y3 V3 L
    1. ?匹配的是前一个字符,即被转义的\,所以|前面的内容就是匹配a\或者a,但是结果里面没有a\,相当于只能匹配a。
    $ t5 M; i1 a$ d* M2. |右边是a\*,转义之后匹配a*,对于竖线而言左边优先级高于右边1 [; [' |( e3 E) Z) `
    3. 然后看目标字符串aa?a*a,第一个a匹配左边,第二个a匹配左边,第三个a虽然后面有*,
    3 V. f3 ?! O9 O" _7 e但是左边优先级高, 还是匹配左边,剩下一个a还是左边,所以结果是四个a
    & B* c. z; g* C/ w8 M# `1 L"""! m% a8 B5 ^, h. x7 s
    . P6 }3 D( l0 h+ {5 n
    re.findall(r'a\\?|a\*', 'aa?a*a')   # 第二次先匹配到a,就不会匹配a?。a*同理。( q4 O1 o, o) j! [& w* o
    Out[35]: ['a', 'a', 'a', 'a']1 o, `) v2 s5 x4 q& l8 p. H

      m9 }/ P1 R, U1 T# 这里匹配不到是因为目标串'aa\a*a'中,\a是python的转义字符(\a\b\t\n等),所以匹配不到。2 h! U1 l0 m7 b$ I" f
    # 如果是'aa\s*a'之内非python的转义字符,或者'aa\\s*a',或者r'aa\\s*a'就可以匹配到\字符。: u/ R) E' `+ G; `5 S
    re.findall(r'\\', 'aa\a*a')
    + F3 F; Y% o  ~3 U1 Q% A+ D[]) w( b/ {; u0 e' P; k4 Z: a/ z
    ' p; i' d) y7 u' I* A" }6 H9 x
    re.findall(r'a?.', 'abaacadaae'); ^, z$ H6 @8 S  n! {
    Out[36]: ['ab', 'aa', 'c', 'ad', 'aa', 'e']  L5 d( E" L1 ?8 _
    1 u; t5 M9 H3 S  b& Y
    re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10') # 多个匹配模式,返回元组列表
    8 S- e; U/ `0 S( e: W[('width', '20'), ('height', '10')]0 G- s# O" l; V2 H9 _( d

    3 |4 R8 E: |3 H4 V8 L6 q7 d1
    : Z5 H, \6 C2 O/ M9 x' Y% @2
    % e" A( P4 D5 [# k) R' g33 p1 j3 y5 I# L/ T  r1 Y) ~$ D
    44 T3 r8 _9 D4 M
    5
    : W! O) O) j$ a6 z& P% B8 b6
    . W/ N8 k+ C& g$ j7
    : H0 B8 V7 T7 [4 U. V8
    ; z) h9 T! N+ C6 N& E9& V, ?9 {% q/ H2 ?5 @/ L
    10& E* W9 R! ]9 V+ @5 ?! @& r# _
    11! `2 P7 ]! P3 E5 Z
    12
    0 y% a$ p8 b  G13: g6 t! L, H* t6 e
    144 {" E& Q! V& L4 Y! M  @  I) b! f
    15
    1 [- ^) V/ U/ j$ h+ A16
    4 f1 q( F6 O) }: p) u( ^+ K17* y% {  l1 y( M# n* {8 R, Y
    18
    % J8 Q' R; O& Y, i! V2 [) x: S6 D7 u19! `& h  k( T1 s3 Z' G! m$ c5 J
    20
    3 t; A: x: s, }" ]! I  Z219 M4 k0 s/ o1 |5 Q
    22
    : r% }! r5 H* |/ a235 ~/ k0 {' S2 n. ]$ ^+ W, _
    24
    1 I4 w4 W( f4 m1 P25
    8 Q3 w0 F8 h7 W8 }9 _26& ~8 O3 C  j1 ~5 l- `# ^  T
    27
    # R" [  Z. U# ?$ S" p# K28% s8 W9 S+ f# X9 e$ a% k
    29
    3 j9 z9 d( ]5 b, R) b30
    4 k& h( O# o9 n" y& V8 d* z31
    1 D4 U; [+ r2 ]32
    2 B+ l% V) I; W! L5 b" O33
    ) c" e3 x5 Q6 h3 w34
    . ~/ f5 Q2 S1 z+ v+ G: Q# _2 R35
    6 H/ X3 t( ]: u6 ^* ~1 ?% A" G" G1 a368 x2 \: l8 q7 Z4 d' b2 t
    37
    6 Q' D$ u4 e8 T/ k/ S8.2.3 简写字符集4 z: Q5 X" e' H& o# U! Q
    则表达式中还有一类简写字符集,其等价于一组字符的集合:4 W8 n. y5 ^7 G& v" Q% l( n  @2 Z# E
    * S$ {# |; \! Z, S+ Z" N
    简写        描述
    5 U* y. h, J6 n/ \! f\w        匹配所有字母、数字、下划线: [a-zA-Z0-9_]7 J; F. z8 V6 n  G/ d
    \W        匹配非字母和数字的字符: [^\w]
    5 [" T5 l6 t- V! ^9 o' J\d        匹配数字: [0-9]
    7 j+ @# t' z+ ~* C% ~\D        匹配非数字: [^\d]
    * `# |* T3 }3 L5 `, o5 k\s        匹配空格符: [\t\n\f\r\p{Z}]
    ! [, w0 ~2 i3 t- p1 j$ v\S        匹配非空格符: [^\s]4 z+ ?$ k% E/ f* {6 A0 r4 d
    \B        匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。. b  G3 H% g: w- D0 g( U5 w+ b
    re.findall(r'.s', 'Apple! This Is an Apple!')
    9 a3 a' x  }( Z6 j/ _2 zOut[37]: ['is', 'Is']2 q9 f. V, O& i3 e) [$ K

    * Y% u, [+ }( Y8 e1 e  R8 ure.findall(r'\w{2}', '09 8? 7w c_ 9q p@') # 匹配任意数字字母下划线的组合,但必须是两次
    7 K3 k% A4 a' s, e% LOut[38]: ['09', '7w', 'c_', '9q']
    5 q. O1 s$ b( b! a8 n, @$ h/ Z: G9 d: `3 a3 \5 R4 p7 H
    re.findall(r'\w\W\B', '09 8? 7w c_ 9q p@') # 匹配的是两个字符串,前一个是任意数字字母下划线(\W),后一个不是(\W)
      _# n# `* [; u, |Out[39]: ['8?', 'p@']; v/ ~& `  O4 P9 q) K- R
      {3 o, ^% z" ~. A+ C
    re.findall(r'.\s.', 'Constant dropping wears the stone.')6 y+ I) |; M- }
    Out[40]: ['t d', 'g w', 's t', 'e s']( a8 c1 j6 b! d# o9 s- h& g' p

    3 ]2 u, \% d- k  e/ p. V/ [re.findall(r'上海市(.{2,3}区)(.{2,3}路)(\d+号)',
    8 a1 T+ S2 R6 f% E; u8 ?           '上海市黄浦区方浜中路249号 上海市宝山区密山路5号')
    : H$ O5 P  r- a' _8 s- b2 F1 k) ?  m. P  D0 s( ^; P
    Out[41]: [('黄浦区', '方浜中路', '249号'), ('宝山区', '密山路', '5号')]; P1 |; d9 h$ E6 n

    % w( ]) U5 g1 B& R! y1
    8 O" G, P; f% u7 O; D" J( Q25 f' j, w2 k. h( d1 d, G
    30 Q" G  @5 Q7 Z# ~* a
    4
    ) }) ?+ b* \0 S, p% h53 s/ E8 ]: c# F& r2 z
    6' G4 o, p9 t3 ]" l5 K
    7
      B+ Z' h/ g! J+ V: o0 H8
    - R9 ~: f! k0 _! b! O( C2 e9
    5 J  @" ?" I( i$ ~/ _& T8 ^3 U10) b& [" l! G# v3 G
    11
      N9 p0 z6 G$ L" _8 v' g! N- Z; ~12
    : R) m3 z$ ?" T4 u13" n1 X% u/ L1 l: O7 i
    14
    4 T6 n) e) q& l  o& L; m/ N15. m- k5 e/ ]( |+ f8 }  _
    16
    % t* H1 @* \! m6 k8 i4 f* V* J8.3 文本处理的五类操作
    ' v% Z* M( D! E# _6 O! |8.3.1 str.split 拆分
    8 b8 o$ X5 ~9 d9 R  str.split 能够把字符串的列进行拆分,其中第一个参数为正则表达式,可选参数包括从左到右的最大拆分次数 n ,是否展开为多个列 expand 。
    0 f; z3 T8 R6 k& s4 V
    # }9 [$ m+ U4 G2 B# j+ p& ss = pd.Series(['上海市黄浦区方浜中路249号',) Q- k2 _0 [3 Q& V: O  g  ^
                '上海市宝山区密山路5号'])! P7 h4 |5 f2 w  F- [
    / j# C! {4 r6 y  ~# C2 b5 Q- O' C) K

    ( }- U" c# ?3 v3 w1 @/ s. E7 Ms.str.split('[市区路]') # 每条结果为一行,相当于Series, L4 B: s; t1 ?. p7 f) F  L0 k% F
    Out[43]: : b' M' Y. U. C$ f4 z5 o8 G4 h
    0    [上海, 黄浦, 方浜中, 249号]2 `+ M1 P3 p7 p
    1       [上海, 宝山, 密山, 5号]
    6 j4 F; L; k0 P* o( L( i( gdtype: object
    # W/ E4 t: V7 a) ?" x- g" n* ]4 S0 A1 O" {# m
    s.str.split('[市区路]', n=2, expand=True) # 结果分成多个列展示,结果相当于DataFrame# c4 [4 w' u$ u0 N% O
    Out[44]:   w  C" O/ Y6 B
        0   1         2
    . s) g' ?+ L  A/ C! N9 K* |1 m0  上海  黄浦  方浜中路249号
    9 y& b1 @( B6 S* g" {4 Z- D1  上海  宝山     密山路5号7 f- s6 i( q- I
    15 l+ u$ K# A& z, o# R; W
    2
    4 `0 O, Z# D* _5 N6 R& ^% @! k3
    * |7 e3 L  A. f2 x0 P) M4! A& y" O7 F3 e8 z7 ^3 k2 Q$ {
    5% F' _" J1 F! v- t
    6! e! p0 n$ y8 d0 |# M
    74 d- w* C" J8 ?2 \9 s  i3 q) O
    85 V) a0 |6 A- P! o
    9
    0 l: c4 B7 g& c6 L3 E10# M5 m, ^" R3 r2 I0 L) p
    11
    ' o3 d. H1 t' V* W12$ _) r3 h. V# N8 z+ p+ o$ x
    13
    7 a1 V1 Q$ @; ]" ?& B& a14) y1 _6 |' U& l+ ^
    15( F7 s- j, B) }0 K
      类似的函数是 str.rsplit ,其区别在于使用 n 参数的时候是从右到左限制最大拆分次数。但是当前版本下 rsplit 因为 bug 而无法使用正则表达式进行分割:
    ! f* C! M7 S: M
      O1 J+ }! g5 J% i6 i$ os.str.rsplit('[市区路]', n=2, expand=True)& K: J/ i- i% R" e" s' o1 r
    Out[45]:
    & r1 ~2 z- Q* G0 B. n                0
      i% f5 |6 a5 x7 ?2 ~* C0 U7 T# _0  上海市黄浦区方浜中路249号* d" w+ {  R# }2 p  y
    1     上海市宝山区密山路5号+ ^& C( P9 t4 v
    1
    & y* S$ u1 U/ z& z) A8 o) ]2
    4 T& Z' x# E! d* x' }3
    ' }/ p. L) p: @' q8 p: j; {48 A# p9 q# k, O. T- r! u
    5+ N4 @. y2 _' }! v' Y) N
    8.3.2 str.join 或 str.cat 合并& G+ o4 ]3 y: Y
    str.join 表示用某个连接符把 Series 中的字符串列表连接起来,如果列表中出现了非字符串元素则返回缺失值。
    8 R1 B3 u! j; s/ A" cstr.cat 用于合并两个序列,主要参数为:
    8 q! [3 ?3 X5 ~' P( E) Esep:连接符、$ I+ s1 ?6 ]% e' Q, K, _
    join:连接形式默认为以索引为键的左连接
    ! J4 `9 P. O6 C+ zna_rep:缺失值替代符号- |; V* I3 [' l6 I! r: [
    s = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']])
    2 n4 x) B  J" {  V9 C3 N) Q1 ^s.str.join('-')
    % {' t0 }! b' N3 ^( EOut[47]: " g5 I' o; C* a  T, L, @
    0    a-b5 K5 p* g' R1 ~  O1 P2 ?0 D/ Z3 p
    1    NaN
    ' q) Z1 y8 z5 Q/ b" {2    NaN& [  v# J. X4 _6 v7 i8 v( ]
    dtype: object
      e+ i# B' k  G- _8 C' q1
    * H& L1 I/ y8 X% u8 J2' j$ K* v; ~8 w- L/ V7 m3 z
    3
    6 I4 |+ s9 K- P48 `. H. O8 I; ^* e7 P% C
    55 }% A" s: w2 `1 P
    6
    # U, Q1 u1 Z0 [3 E7
    , B6 O3 f% p! i2 o' e! Ls1 = pd.Series(['a','b'])
    3 ~# q8 Q$ E8 G9 Q/ cs2 = pd.Series(['cat','dog'])
    8 I, E# q  s& Ms1.str.cat(s2,sep='-')/ D9 c5 A: B3 u2 ^
    Out[50]: ( \3 U. J: B9 L, P2 ?- ^$ N
    0    a-cat
    2 O9 M1 h+ P4 N& |$ m3 K1    b-dog
    ! g4 m$ ]0 \& G# |, s! [% f" ~4 U1 Adtype: object
    / `; D1 N% b- T- n
    ' S6 M% `, N; K' As2.index = [1, 2], s' p: E4 O- Q
    s1.str.cat(s2, sep='-', na_rep='?', join='outer')- ]' L2 \+ j1 o& @3 _. ^4 ?2 |9 Y. A
    Out[52]:
    8 j1 |. C- Y  i% K0      a-?
    8 e5 p& a9 w- V/ G4 P! ~1    b-cat) g9 f5 ]" F$ g
    2    ?-dog2 s$ H3 e5 J; K8 Q3 m
    dtype: object
    1 F' p# p9 @& W9 P7 V16 Y2 ]/ `( y3 i9 A8 ~
    2
    0 ^5 H  ]  _6 S: Z3
    / |. y- }$ c0 ~5 Y3 s$ G, {8 o4
    ; K8 o0 ]$ Q" [) J5  T: Y+ g3 w7 ]2 D: z4 k9 _( [
    6
    4 i: E0 G% j% N% }5 n/ N8 l- H7
    $ C) W8 h# m% n' x" ^  j" g) W8
    ; I$ e5 P% \+ v* w( B( k7 u9
    + z1 w6 G  v! m  d9 Z9 o/ t103 E+ `. ^( M. ~7 m( W) D! W+ n
    117 x% q' C5 L3 v" ~; S
    12  `& v  Y% b7 T7 q5 N
    13
      E( ]/ r% j9 C1 {, O; C& v14
    . G, {" F: S5 Y8 R15- n% `# b4 ~& E" R2 ]
    8.3.3 匹配. P% r& I  K* ]! |8 ?1 E$ Z5 ~% f
    str.contains返回了每个字符串是否包含正则模式的布尔序列:& P4 B* T3 T! p; t! d& P
    s = pd.Series(['my cat', 'he is fat', 'railway station'])
    " x1 ^  g4 r  q$ |% Y7 U& ~0 Ks.str.contains('\s\wat')
    & [5 U% V' K5 o1 L  Q+ c
    6 H# d5 G* R0 @$ o$ ]0     True' |+ s; Q; _3 }" p
    1     True
    ) B1 \  d, z* X9 d  D- W2    False5 j  f, N: E1 _, g2 @! \0 t
    dtype: bool
    # I  _& }' F/ t: O# T1
    " ^& {8 e2 Y3 t5 x! R2, w# j" F6 Q$ T5 S* ]; @
    3( d; ~1 U: ^% t: _+ M$ Q
    4
    ' e% E; k) E- e4 q5$ F+ T, }5 k9 q+ y/ m* i& Z
    6# s% f% ]  t# c4 b% X' p
    7
    ' L4 w% P, d6 T) q- b: S+ Ostr.startswith和str.endswith返回了每个字符串以给定模式为开始和结束的布尔序列,它们都不支持正则表达式:6 R; C+ ?* G# k3 s2 d
    s.str.startswith('my')
    0 B8 ?% \. P, D$ j& M- u; ?" ?
    ' n# f& d& i4 F8 w9 o2 A# X0     True0 Y! n, P4 q$ P! N: [* j6 z$ j
    1    False
    9 U7 o$ [- A* o8 E5 Q- E- l" {2    False
    0 s+ N: ^7 I+ X3 m7 m+ ?dtype: bool
    : C" o" D8 j9 Q/ h7 K1) B- I. h% M1 ~4 _1 h9 q
    24 s% Q2 ]7 S) q2 u( ~
    3
    6 j: c6 a0 Z9 x+ i% V4
    ) V8 H; t0 ]( y6 v1 G5# k- c6 T9 j2 U% C; V
    6; U# t0 i/ x& S" F* @( |, ~, _3 y2 z
    s.str.endswith('t')
    " U! p, U! V  v+ y0 N5 ?9 ]2 K8 q: B0 ?
    0     True
    9 D( j9 J4 q3 }" \3 u! O1     True
    . E3 X- p; I2 R1 a6 a; W/ I2 u; d2    False
    % m9 B3 c- r+ N9 e; ]2 kdtype: bool
    ' M( [7 T- p- |/ B1! \2 ~/ [/ p/ y) _6 u$ _
    2
    0 {6 G8 Y& V  P35 c  k* X; R( m7 N
    4" I+ e& B1 m# o2 Q- c6 J5 w6 {
    5: o* A# i! e, D# }! ~. [2 ^
    6
    6 C; R$ c4 J+ Astr.match可以用正则表达式来检测开始或结束字符串的模式,其返回了每个字符串起始处是否符合给定正则模式的布尔序列。当然,这些也能通过在str.contains的正则中使用^和$来实现。(貌似没有python里的search方法)
    1 b9 H* w/ H# t1 vs.str.match('m|h')
    ; J9 j) Q1 I3 c9 |6 r$ D# Ts.str.contains('^[m|h]') # 二者等价. d% `- Z$ x. ]" {
    ( X# R/ o) w- Q
    0     True
    9 L' S' L; u, `2 E: I/ x0 s$ f5 o3 j1     True
      {. q' q/ b2 m# T  s' t: D2    False& ]% T# P4 H" j) ^
    dtype: bool
    4 h, |  T! R6 R; @1% d& N: d/ j( L6 |; o& W6 l
    2
    & C* _4 M0 E4 y/ f3
    2 T0 f$ E& _) w0 O42 \/ N' w2 U' w) X
    5- e  D, J  Z& G5 w# a
    6
    1 D- T: P( h3 z4 ]5 Y7
    + p' S. l( |6 R* ~. F4 R( ms.str[::-1].str.match('ta[f|g]|n') # 反转后匹配) s: e  F9 _) G+ y, j/ j; q
    s.str.contains('[f|g]at|n$')       # 二者等价
    # z8 A7 _1 m- m
    $ w$ h' |. W+ q' d  M$ a0 w7 C0    False' G% w1 |6 W6 X9 r+ a9 \) Z
    1     True
    ! e& N0 o% ]3 N4 e8 D2     True4 M7 ^  |/ P7 M# N% ~: Q+ H
    dtype: bool
    & k* s; P+ o+ D- {9 o) u2 h1
    6 }: l, P' L2 R0 M5 m28 v' w# P! l, U& m' z/ V; Y
    3( K+ x; ^! ~( x+ t- _/ P' j6 P+ u
    4/ z3 e7 Q- n% v1 ^
    5, o8 m$ C/ w2 {! f
    6
    7 m( ~% y4 P( G- d) f5 x  \7
    - _9 f! B+ Z- {str.find与str.rfind返回索引的匹配函数,其分别返回从左到右和从右到左第一次匹配的位置的索引,未找到则返回-1。需要注意的是这两个函数不支持正则匹配,只能用于字符子串的匹配:$ o  J; ]2 K: ~/ L+ U5 Q4 y
    s = pd.Series(['This is an apple. That is not an apple.'])
    1 z1 ]8 U6 ?+ O5 @3 T# d: k# _; l* `  f: o6 h! f
    s.str.find('apple')
    5 Z8 s* `- m2 ]0 DOut[62]:
    9 b& l7 b8 t& x* S0    11
    2 G+ G; ~$ Z) k! L7 B8 h" B" C6 a% |dtype: int64
    $ [* d( C) V" o7 Z% R1 u; ^  P& _: B+ ~. i, j7 s
    s.str.rfind('apple')
    + c' B# v: ~' a: Z8 V$ }Out[63]: / e& }& Y. j6 y) N( A; k' h: `4 n
    0    33( N$ q2 N% g. i* P5 [# R! e  T
    dtype: int64
    6 y& [% w3 Z+ x! [6 [3 ]1
    : o- a' p$ O% d  X: T2# c) {/ ]$ Y, ~" W& ]7 B. J+ _2 K! B
    3
      r% a3 f2 F4 e2 b1 h4  g+ l; a7 C, K( w) Q) p
    5( M3 q. O' ?( i, ~7 r3 Y, `
    6
    " Q# D  H5 j1 d- C( F/ u; W7 p, L7; k+ L- f3 V8 O2 m# c% y
    89 {$ y# _/ A; m
    9
    , d& m+ f+ }6 w3 O10
    $ a9 R+ _3 f: U# d; A. g( j5 }# @11$ m4 Z4 S$ w2 [& h, [7 h1 s4 K
    替换! {; G3 v, X* t, Q( |/ x3 o
    str.replace和replace并不是一个函数,在使用字符串替换时应当使用前者。
    ' @) F0 S7 z& e3 h3 A0 F# P  ps = pd.Series(['a_1_b','c_?'])
    2 n, J7 o" e" r: j7 d7 Y  C# regex默认为True,表示是正则模式,否则第一个参数内容表示是单纯的字符串,也就是匹配字符串\d|\?- X$ x2 |: Y& y1 T  @/ a" Y
    s.str.replace('\d|\?', 'new', regex=True) % M; w; R, s3 W$ v6 |

    / X: ^1 n5 {! w* ]" M0    a_new_b
    . R( D+ b8 T# \4 ?- t. {1      c_new$ J2 s7 T8 D% t) A% {
    dtype: object
    5 r4 n/ b8 z2 G, {, c1. _: X* H4 ]5 T- n4 c3 O
    2
    - z3 x, p# y) z+ R* \( ^3
    ( A/ |$ E) g5 L) R# ^5 ^' T4
    $ J. p" B+ y% T/ X4 ]59 q6 K0 g8 I2 {
    6
    . N( G. Q; i* d& H2 |! x& i7 T7
    6 a; ]( j  s, ~6 `& h: @& s- P  当需要对不同部分进行有差别的替换时,可以利用子组的方法,并且此时可以通过传入自定义的替换函数来分别进行处理,注意group(k)代表匹配到的第k个子组(圆括号之间的内容):
    8 u9 U: D. P7 D+ ~$ G- k$ F7 G4 a
    s = pd.Series(['上海市黄浦区方浜中路249号',8 {, ]) J: p* Z$ o; y0 m. m" w
                    '上海市宝山区密山路5号',' Q  ]0 O8 W2 @. R$ [9 w
                    '北京市昌平区北农路2号'])7 u; L9 p( ]! j& n4 i
    pat = '(\w+市)(\w+区)(\w+路)(\d+号)': v$ m' }( W" k( r( G
    city = {'上海市': 'Shanghai', '北京市': 'Beijing'}
    + q6 f& C$ b/ \district = {'昌平区': 'CP District',  w4 j9 S. ?' B; J0 x
                '黄浦区': 'HP District',2 A* u1 q  T1 U
                '宝山区': 'BS District'}* A/ N8 _0 o1 Z9 x
    road = {'方浜中路': 'Mid Fangbin Road',# x( }& z! l& h' }' k
            '密山路': 'Mishan Road',
    * J: B  b: b) o; ~        '北农路': 'Beinong Road'}
    , |0 v  |9 c* G! U4 fdef my_func(m):1 P0 Y" R' h/ r3 m" t# }) q0 r$ J
        str_city = city[m.group(1)]
    * Q$ A: c* k# w    str_district = district[m.group(2)]2 H; L) C: K! b/ J
        str_road = road[m.group(3)]
    * t/ M1 Z' T5 o! L" z! o0 J3 y    str_no = 'No. ' + m.group(4)[:-1]- V7 ?( m( R) S: {- G5 \) ]/ z
        return ' '.join([str_city,
    : ]. w! Q5 I# ~, e                     str_district,$ _- i1 {7 d, p& Q
                         str_road,
    ' m) L1 q* o; f( D                     str_no])
    2 D, @3 P2 @" y  ^) xs.str.replace(pat, my_func, regex=True)& i: E6 K* ?; d$ D8 A9 y) w
    1 w; O# p3 a# z4 l
    1
    ' n/ Y! b2 r/ d% o5 R2 f2( Z! x  |( v1 `
    3. O9 B$ }' ?2 p/ Z
    4
    5 @0 G$ [, t- X& |/ ~& B9 Z5
    " g6 h4 O1 c3 s) }  L6" V7 x# N) u: Q/ R* z# P9 S8 y) s4 n
    7
    : A0 A, O. U0 M( O3 y8
    2 s: `0 x* r6 {. F/ C( `9
    ( ?2 n8 Y3 E/ s+ Q$ i2 Y9 B$ n. a103 b/ L$ o/ M6 q! I7 ^
    11, f6 \  ]1 [. T6 S9 f) x9 N
    12  S+ @# |6 a0 L5 X# }6 [% ]
    13% r4 B0 `" N: V6 I" @
    14
    % f: u% U- o& Q& |3 K% }, ^15
    & g2 F7 {0 H# e. y" U0 m166 Z; D! w1 N0 G( a  w
    17- D1 R1 f3 j  M4 Y) k
    18% w5 z5 P" m9 G( r
    19
    8 n6 O/ M6 b, e! Q" s20
    + W) M4 X" H. F' I6 O21
    $ Q9 Z; f% p+ L# ]0    Shanghai HP District Mid Fangbin Road No. 249( D, o4 Y/ A! L
    1           Shanghai BS District Mishan Road No. 5
    6 X$ E8 y7 r. `1 A0 p2           Beijing CP District Beinong Road No. 25 n. o+ J5 W) g  s
    dtype: object6 k& Y/ `& W# z& }
    1' T* `# ~7 [1 s, @/ H
    2
    ( m  O$ h( g; ?( k  j- W. ?3* T  y7 ?: ?: }" I$ f. Y
    4
    ( y8 Y; U/ k* c这里的数字标识并不直观,可以使用命名子组更加清晰地写出子组代表的含义:2 o  O7 ]0 e" M/ r6 e# _
    , ?& e" k7 X& v5 @7 O& V; d
    # 将各个子组进行命名/ D+ l/ G. I$ v0 H/ V
    pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'" S7 e! m# W; |0 A2 B& C& g7 ]8 S
    def my_func(m):1 g, p! c7 p. X& u) ]! ~0 |3 n
        str_city = city[m.group('市名')]" P: \. S( e5 P/ c
        str_district = district[m.group('区名')]% h2 T8 C' T* u* Y% g
        str_road = road[m.group('路名')]
    % P2 a1 e" Q6 Y- \* A% h2 S    str_no = 'No. ' + m.group('编号')[:-1]
    5 U/ ~/ }9 V; I- w! m    return ' '.join([str_city,
    . ~+ h$ d, w+ o1 `) a& R                     str_district,! A2 @9 V6 b! _# M5 n2 r( k
                         str_road,
    ; J3 B' M4 l9 E: o9 @# h2 m+ g5 F                     str_no])
    ) ?; m6 p, N4 ts.str.replace(pat, my_func, regex=True)
    8 s3 A# j( H8 D4 h# I4 x1& y* {' K6 }/ }. N. ]
    29 L$ C- z/ [# e$ h0 B3 t
    3
    3 M$ v/ K6 Q! b2 n4
    : {0 }+ i! g( l) v; g* O: r; i54 c2 W8 M# g! G) y/ L
    6
    . J8 t+ c8 k, \- {4 f7 J7% m  U- }( a8 n' k, q
    8
    % |% Z% c; i0 g9 ]/ o9
    8 }: x* T; o" Z- Q# z108 n$ a4 N  G% G
    11
    $ W! J: q, W8 J% a12' A8 L" A& t5 p% R
    0    Shanghai HP District Mid Fangbin Road No. 249
    + G8 e( g  \' }" p# G5 ?1           Shanghai BS District Mishan Road No. 5" s; g. E% ^; ]& W" t% y% o" C
    2           Beijing CP District Beinong Road No. 2' {: C: Z$ A& d+ U+ [2 Q
    dtype: object
    4 ^( S: u5 d2 _3 }* j; ?% w* K) Z1
    " j1 o' f# W8 t9 o& G" n( ?20 Q( F& @4 O% W4 ^+ v6 z  d
    3# C: z; L/ y2 i) X* m* m1 m# O
    41 ^1 {2 |: E% s1 q8 \8 I
      这里虽然看起来有些繁杂,但是实际数据处理中对应的替换,一般都会通过代码来获取数据从而构造字典映射,在具体写法上会简洁的多。
    3 x# p3 h+ v8 W: |
    7 M- E$ \- W" m& ], K5 ~" V8.3.5 提取
      F- c' @& O) a! U* x% a3 ?str.extract进行提取:提取既可以认为是一种返回具体元素值(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的str.split例子中会把分隔符去除,这并不是用户想要的效果,这时候就可以用str.extract进行提取:1 K  Z) m' A( T: @- G1 d+ X+ w9 S
    s.str.split('[市区路]')' i. t: D, i* ^( }9 w. z
    Out[43]: 3 D# J2 c- Y( X- H8 G
    0    [上海, 黄浦, 方浜中, 249号]4 f4 j* q1 e' g8 P& c( l+ n# [
    1       [上海, 宝山, 密山, 5号]' F) A" D- M! L# S8 \0 H; w
    dtype: object
    5 c% M8 C! x: M, U$ A; c
    - K, _8 y) @- K7 Rpat = '(\w+市)(\w+区)(\w+路)(\d+号)'
      h( {# b6 R3 k+ Q% ?# R' rs.str.extract(pat)
    ' Z+ W. u) Z/ N! m  f1 vOut[78]:
    . M/ ]! r+ ^( X8 N9 ]' f4 D& y7 m( i    0    1     2     3
    & o9 g8 M. z# A. _9 x" J0  上海市  黄浦区  方浜中路  249号( C. y( |! B# _- ?
    1  上海市  宝山区   密山路    5号+ o8 v6 K2 L7 w* I0 C, a
    2  北京市  昌平区   北农路    2号7 x$ l3 N8 C/ L' b
    1! ]. E4 I4 \' C2 e. J& n; D
    29 a: n. q2 J3 I7 o- ~/ M, Y
    3
    % x5 [3 s, A( A) a6 ]: p& B' S2 g4) {: }2 W8 n2 L' d8 [: o
    5
    $ @& y/ L6 O2 ?0 P" {6
    + V* A; F8 P9 [6 X5 m( D) i$ O7
    , ?* v. ]  ?' K6 ]9 _% m8- y6 x; A: \. p' q
    91 k. R1 u  r8 `5 q3 F' x- R. `
    10% C' p& E6 _# d. _' Z
    11# l: v  P/ c. i! E2 F
    12( }. {% |/ _) H7 H  j1 v2 K
    13
      O! K, X; J9 U8 e$ S/ s: A7 {$ M通过子组的命名,可以直接对新生成DataFrame的列命名:! h  E$ _' E* d% e; N

    7 I) Z: H* m) A# ]  S$ Jpat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'
    8 r1 o) j2 t7 @s.str.extract(pat)
    9 J2 i% ]" x! S9 v( B! S; L# tOut[79]:
    ( b8 B: }+ g- |* K3 t    市名   区名    路名    编号
    2 M% w  @7 H( E3 u# o9 l4 I0  上海市  黄浦区  方浜中路  249号
    ( A8 t  B4 E: Y6 g* \1  上海市  宝山区   密山路    5号
    2 l$ H! i& W" `" Z2 R2  北京市  昌平区   北农路    2号
    # K* F3 j( k& O2 c9 h6 v8 K. @1  h' L3 b' m4 _8 M4 t
    2
    " K, O) @  @( v5 {4 n/ w# q" a( h! b: u3* Q1 p( Z; r  }3 W) X) X& L
    46 |, ?; B! b& _. ~. G. E  `9 C
    5
    3 _" T) D0 {  o. x6
    ) i7 }: N. w. X- {# k7
    ; P+ N; \6 f8 \# \$ S2 \7 zstr.extractall:不同于str.extract只匹配一次,它会把所有符合条件的模式全部匹配出来,如果存在多个结果,则以多级索引的方式存储:& h4 m7 y3 L) K4 n9 X+ D- `
    s = pd.Series(['A135T15,A26S5','B674S2,B25T6'], index = ['my_A','my_B'])+ H) ]7 m3 i! e/ e+ {
    pat = '[A|B](\d+)[T|S](\d+)'
    4 O6 R* `# R: ?, V3 vs.str.extractall(pat)$ V5 v% X% N; g
    Out[83]:* Q  _' H+ F+ z$ ~6 f( H
           0   1
      s+ J' c! C  e$ q: F* T6 y     match         
    0 Q+ G  \! b$ K1 ?my_A 0      135  152 w4 g/ N4 m! V( _
         1       26   5
    4 M$ H) v) B! Umy_B 0      674   2. y. u) G/ X# @$ {
         1       25   6
    1 e" r* \) o, y! D- `6 J9 M1 R" w1
    ) x9 p! b' b- H. i+ ?* J29 P: P; [  R3 A# M! d
    3
    * o% @0 y/ }0 V+ {7 ?: N9 c- O- @44 T* T+ W1 u4 @
    5
    8 G0 ]( D$ x! w7 c% U; h/ \  }6. @" V. y1 n# }$ N9 m+ k
    7
      U3 U$ `' ?' S1 h80 h4 ?: y1 S: E) ~2 ]: c
    9& D$ f% X1 N* A. s1 C7 c' q, t/ j& |
    10: |: m) B8 Z/ q4 p1 x( B# Y" C2 n
    pat_with_name = '[A|B](?P<name1>\d+)[T|S](?P<name2>\d+)'4 a  i" s% h3 J0 l- M/ L* w+ w
    s.str.extractall(pat_with_name)/ d' W( W' e4 g) B' ^/ D; s' I; A0 U
    Out[84]: 2 k: X# I3 d  X6 S% [  y0 d
               name1 name2
    6 J' R' y2 A" `3 a     match            
    / h7 V+ y8 T- A: ~. W1 ymy_A 0       135    15
    " T0 L, L% _2 _     1        26     5- x( W; G7 R6 \# `
    my_B 0       674     2
    ) Y6 _9 j: j6 u+ t     1        25     6
    , n/ Z4 C. n6 Y% l+ n1
    ) ]5 }7 N5 ^5 R/ n9 `* \7 c6 N+ y* q27 _/ W1 ?, Q) Y& V5 m% I* V: u
    3
    1 \- T9 m$ c  X/ f2 R4
    / T& f5 G! j: ?+ [" g- l% O0 c5
    0 K0 M7 s5 F  M* E* ^" {" }; ^6
    5 y' u" n$ U/ {/ s( n; |' w) m7$ ]( q$ j3 b2 ]" \9 Q# w: v5 N
    8
    7 f3 Y$ u( D2 \, j7 e9 N9
    / O; V0 L: c, d3 dstr.findall:功能类似于str.extractall,区别在于前者把结果存入列表中,而后者处理为多级索引,每个行只对应一组匹配,而不是把所有匹配组合构成列表。  q3 ^4 I0 \3 v2 R6 p& ]
    s.str.findall(pat)
    3 E; ^) M: ~( B/ b" R10 X% S* O) u1 t5 W
    my_A    [(135, 15), (26, 5)]* a3 d) M5 b; @' X; [* o
    my_B     [(674, 2), (25, 6)]3 ?' \* s+ w' v: R/ Q, u$ V
    dtype: object, A$ N$ Z0 G5 c7 }$ ?/ M
    1
    # J9 o7 q2 u6 u2* e3 [- h, b$ C1 Q( G6 s
    3
    9 ?7 W( N) ?' `3 X0 W8.4、常用字符串函数
    ! |" {& f+ @+ [7 Q# t2 o  除了上述介绍的五类字符串操作有关的函数之外,str对象上还定义了一些实用的其他方法,在此进行介绍。
    ! Y8 S' p$ s: x& A! M0 G
    / p; g! t: X1 [9 g! \' u/ M' n8 v8.4.1 字母型函数9 s) [3 G9 ~2 @9 @0 W/ j
      upper, lower, title, capitalize, swapcase这五个函数主要用于字母的大小写转化,从下面的例子中就容易领会其功能:) \/ ^0 h6 ]# H6 s" x
    ! }2 u" _% \) V4 t3 [' Q
    s = pd.Series(['lower', 'CAPITALS', 'this is a sentence', 'SwApCaSe']); z8 |2 V: x( |( k

      }* n3 |/ Y' v" ?# E4 V$ M- X2 ]( |4 Bs.str.upper()4 N" p9 s8 Z/ Y1 i1 K
    Out[87]: . z( ?' E0 [" S) k
    0                 LOWER
    6 i8 l0 W; V7 r1              CAPITALS
    9 A2 i6 e6 Z3 T$ i2    THIS IS A SENTENCE* q2 T/ l0 g/ X. _; n, Y# K' @
    3              SWAPCASE! F3 d3 \: H  w1 R( J
    dtype: object
    " O8 z% x; f; h8 x" Y
    + \2 d3 s! ]$ C  v; Us.str.lower()
    ( N1 p1 `+ v# e# t9 ?2 {Out[88]:
    : Q4 u3 R$ b1 j. l8 E2 Y/ Q% U! s  ?0                 lower* [9 C6 {( M5 d
    1              capitals
    / I' I8 a% H, p. I* b, y7 v, i2    this is a sentence
    4 E( ?3 W- e% o( o/ J: @3              swapcase
    0 e+ |; a/ b6 j+ m! U* u+ Q4 Ddtype: object; f# t% |. g. R' b) F
    3 o1 P+ F! `! G
    s.str.title()  # 首字母大写
    * o6 g) t: q- n1 S9 n+ @. Z! _% sOut[89]:
    , H  e' i: c) i$ Z6 P0                 Lower' B7 J! G- q; \( E
    1              Capitals, M$ ~3 d2 f: e: |" S
    2    This Is A Sentence% ~$ z( v$ R" ^' b
    3              Swapcase
    # E$ I$ J  O( a$ Q5 l% Cdtype: object
    7 w- ~1 j5 C# G+ Z; |4 g. ~
    # l/ ?9 Z4 Z$ G# j/ @: Fs.str.capitalize()  # 句首大写& q6 H4 W. U; o+ F  R
    Out[90]:
    , c8 y0 H& I) R0                 Lower
    % s$ F* s, z2 }( x% b1              Capitals
    - m9 G1 \* V: I# w0 }2 j, D2    This is a sentence# f8 \5 c2 n0 A
    3              Swapcase9 G' ^% v5 A7 F
    dtype: object
    ! I, R8 @: `, N2 n1 g, \3 N! }  M- [, Z" V9 r
    s.str.swapcase() # 将大写转换为小写,将小写转换为大写。& k6 `0 R, v9 P" Z0 _! r
    Out[91]: 5 S9 R1 c) \* M, m/ t0 O
    0                 LOWER
    / Y' X+ e! j9 S. N) @( q1              capitals
    % _' \8 v+ R1 H6 o3 e4 k2    THIS IS A SENTENCE
    ( F9 z7 m$ G: S4 z& l+ H( @+ @. x3              sWaPcAsE5 l6 C& s3 p& S. n3 t& S
    dtype: object
    1 }$ P, c6 }0 r* |
    $ }) o0 G9 V* U" l$ Ws.str.casefold()  # 去除字符串中所有大小写区别4 f) v( m4 ]0 P3 O
    1 e5 ]  W% z7 |, v5 r5 T
    0                 lower
    ! v; ~5 f" z6 I+ a1              capitals
    # ]  W# K' Q2 b) t( o8 H2    this is a sentence
    ! ~; g0 i7 O% \2 }( J& m" _3              swapcase
    + ]4 R$ ?/ i# a8 V# A0 j% n. B2 Y0 s9 ?8 X+ H
    1
    ( ], w0 u- ]! x  y; N/ {0 G3 j2
    & {/ S' A$ y' a- ~3 U/ r3 `36 K$ S: P/ Z9 F# Z% R
    49 u, F' e9 J; Z% S1 |
    5
    7 l9 u/ g% t5 F$ |65 h& k& k  C+ r, G
    7  P# D& w: l( p+ b, n; V- Y/ J) [
    8
    1 p6 y4 _( Q! T' V  i8 ]" x9
    - \0 N- E) a0 C, }7 Z, i4 j5 k  \10* a0 J! Z4 A( T6 Z
    11  w$ L# D1 D% o! r# X
    12
    + M1 n  `" X3 u" s# Z# c9 u  l13
    $ r& F; O5 E9 h( @% D' i142 N5 y% g3 d$ i; K* o
    15
    ) I7 _/ ?! A6 O3 v2 l16
    0 b" F, Q/ t/ o: t' ^5 G! Z17
    & D/ U* {. |& u186 W$ K6 m! L  ~! K: x" L- q# i
    19
    " `6 F3 q  H9 ^205 o0 b$ U  C3 u% Q, O
    21
    ( C+ L( e9 n* P! r$ B223 v( B( t& C& c2 P, `
    23
    - P1 x+ f9 m$ |( g% F24/ E2 p; ~. F) j. B
    25" L6 o9 S: r9 Y1 c
    26) X$ C6 Q9 c" W
    27
    % D" Y, o/ O% e( g" t$ U28
    % J& r0 |# x5 k+ Z* y29
    4 ~) D0 I' M/ @$ _4 M5 u  |3 \30
    4 K: `- p0 I* L31$ q0 |' E* K, w7 K3 L
    32
    ( X  p! S5 s) z6 B338 w- h8 i2 n% m. {8 q+ E
    34
    1 T! F! a5 |6 ]35
    7 |! C: p& u& B/ t- p. a. ?+ A1 N36
    + [4 W' p7 Z4 m& [2 N. Y37) E; O& X# D# Q/ b
    38
    : e7 K( V6 C" _7 V1 _39( |1 H/ t4 F% Y* _- v7 D
    405 _7 f. a$ s" V, r5 p
    41
    ! a8 o5 a  w# d6 E% l; S42: v+ [- `% ~! v( M: v
    430 m/ h  X9 |; D/ ]# m
    449 r& ~( c; b" g6 O5 p9 q
    45
    " w, ~! A3 s# Y- }8 A  F469 P- v& X& g$ p& K
    47
    0 P  ]# m( f( Z1 H9 t! m48
    . u5 [9 B1 I- @6 N+ S3 d* j8.4.2 数值型函数
    8 G- D! b5 e6 y2 E. r4 P' K  这里着重需要介绍的是pd.to_numeric方法,它虽然不是str对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。其主要参数包括:
    - K+ O0 B  f6 G/ }7 m# ?
    2 ^# T" }0 f4 r2 ]/ V2 u& d6 ]. K. i9 }errors:非数值的处理模式。对于不能转换为数值的有三种errors选项:- U$ ?: e8 {# ^/ ~- t, T
    raise:直接报错,默认选项
    , v8 n$ U( G2 ]/ S# Ycoerce:设为缺失值$ \2 }8 J3 Y/ @4 V) X
    ignore:保持原来的字符串。
    6 q8 `2 ]/ H& Odowncast:转换类型,转成 ‘integer’, ‘signed’, ‘unsigned’, 或 ‘float’的最小dtype。比如可以转成float32就不会转成float64。9 r$ z' f) K1 C
    s = pd.Series(['1', '2.2', '2e', '??', '-2.1', '0'])" N* h" O% S& F3 Q

    . _- o6 ^) g- _6 |pd.to_numeric(s, errors='ignore')
    ; t6 p5 a2 h+ Z' T# G; |Out[93]:
    5 z" N  {. `' ]; ~7 y0 C) t0       1
    % ?0 \% U0 N& d, W. L1     2.2
    / C; \8 n+ m: `' m2      2e) O  w; {* t" |. q2 K
    3      ??
    7 e0 `& }- h7 L, B4    -2.17 h& v; e5 G2 R( _$ N) u0 F  ]
    5       0
    8 r5 ~/ d0 t, M. C" G; b* F; Edtype: object
    # \: B+ D. k( {6 f% g; S& h  L  d1 [; ]
    pd.to_numeric(s, errors='coerce')
    5 d2 f" X4 X% N! P$ }  F' v7 EOut[94]:
    - Q* n* ^- F2 x/ Y! l2 i$ f0    1.0- K" m7 D7 F9 B& g$ w$ e
    1    2.2: }/ @! z, ~+ q7 x/ u
    2    NaN
    # u- A# w1 E+ D0 A% d' t) b3    NaN- j( b0 e4 U5 {% g- Z6 T2 I
    4   -2.1
    . s! a; t2 A4 `$ b4 S4 Y5    0.08 n6 g1 ?; N2 r$ x) z( P% l
    dtype: float64$ j6 z0 h( I  P8 e9 M
    ( b8 x& \$ d8 y4 P5 e% h" F% q0 {$ i
    1
    # Q; _2 K: [6 t22 L' c% |% `: p0 J$ f0 m# w# s# o
    3
    1 E( p/ l4 L; |/ g4
    / I$ r- a1 }) T  N5" ], n' Z8 t  B: M% {0 }& I
    67 _" v4 V! `( q7 U& {
    7
    ' m) u* f9 j% R* T87 E+ ?% q: K+ {5 k2 H5 Q6 F, d
    9
    % Z3 e' t2 Y$ R% G$ Q10
    & ?( T% G, w) [+ G: ^1 I11
    4 B4 @! V, p  _4 s12% {( l+ j" {" Y( H0 I
    13
    4 g' Y% ~" d6 l  C. F14! [' F) @2 c0 j9 d5 v+ c$ G
    15
      B7 w# f9 Z$ ~- ~6 K& x/ P16
    + J0 c* k) y: U  z" m5 Z6 s173 B+ E8 Q2 r+ Y3 j$ e; v$ ~% y: g
    184 H9 B4 W/ K- Y$ h  y
    19
    ) D4 v* q& E% b! b" E, w20# u! i  t$ x5 N
    21* l3 Y0 {5 Q! C. w3 y( \
      在数据清洗时,可以利用coerce的设定,快速查看非数值型的行:' `4 ]& \" J; o0 Z! }' w2 I# w
    4 W* w. D  T6 {5 O6 }! t' S) |9 k- d
    s[pd.to_numeric(s, errors='coerce').isna()]
    # t' G5 d. Z# L, ~Out[95]: / D. ^; R9 ]/ O2 n4 ]8 ?
    2    2e
    9 R2 _/ U5 S* l" u( l0 i0 q$ ~3    ??
    * M8 B/ N4 c4 i1 x- N: hdtype: object1 V/ W. T. m. K
    1
    4 I' T+ u/ {5 s; S2
    3 M9 o% O6 H  N! T5 A7 p4 ~) F3" u1 D  P' R! D6 J( v0 g
    4
    0 v( o' u1 j3 G& v' E# |55 e1 X7 y/ d  \
    8.4.3 统计型函数
    $ X" `" y3 Z: N$ o3 }# j. Y  count和len的作用分别是返回出现正则模式的次数和字符串的长度:
    2 z' W# O5 i* X: v0 P: q% V
    8 f# B- A% d4 cs = pd.Series(['cat rat fat at', 'get feed sheet heat'])) n$ P6 j1 }5 s3 v2 k9 X

    0 f  h( y4 W" C" w$ h/ Bs.str.count('[r|f]at|ee') # |左右两种子串都匹配了两次* K: y6 ?' N/ t
    Out[97]: : E2 O; V0 q4 [6 C
    0    2
    , _6 o% q% T; C+ }4 B; `! q1    20 s; d6 x! `" N7 ]4 y7 i
    dtype: int64
    * {+ Q: P6 }4 L* @# o& ^+ ^6 {
    4 y* o2 @' P) _$ Vs.str.len()
    5 i( Z: E" p; {9 x2 U5 KOut[98]: ' r  @: h* v% h  R1 u
    0    14: W+ f' a0 Y3 d$ S0 c5 `$ w
    1    19' X0 [% j1 p9 x6 u
    dtype: int64
    , V/ d( t: ~4 y1
    2 _3 u: a" w# \  S2% K- R( X  P+ S- |
    3
    : A) Y" M: C8 e" ~4 C3 ^8 s- q4  m) N- ?! Q4 W' ~8 Y
    5% n! c# ?4 R+ l  l
    6
    3 q! n$ G, F) R0 X% o# T7! Q! \  |; D6 V+ ^
    86 j7 Z  C1 n, [. j" J
    9
    " c% E. U% e5 R5 ]( |' l0 d10
    5 K+ V4 @. z* i8 G7 d5 X11& z# [5 w2 _' n) k( G" j2 j6 v5 r
    12
    9 g6 E- d7 C6 j7 j: Y13% V) T4 \! I& I( f' H, N
    8.4.4 格式型函数8 G# E/ U# K. q# |& b5 C
      格式型函数主要分为两类,第一种是除空型,第二种是填充型。其中,第一类函数一共有三种,它们分别是strip, rstrip, lstrip,分别代表去除两侧空格、右侧空格和左侧空格。这些函数在数据清洗时是有用的,特别是列名含有非法空格的时候。
    2 [1 u! [4 J8 v1 V
    5 P( m2 s) l+ C- j, I  [+ K# Mmy_index = pd.Index([' col1', 'col2 ', ' col3 '])
    8 K; S2 z3 X" C3 l1 C
    2 d2 D# N+ p* J% q5 e( V8 Omy_index.str.strip().str.len()) Y  o" }/ [6 ~) E: i
    Out[100]: Int64Index([4, 4, 4], dtype='int64')) I# ~' T( U$ r# [/ `  M3 f, N
    # q1 A  [8 L0 g
    my_index.str.rstrip().str.len()) t6 Q& L/ G  p/ y2 s8 q1 e
    Out[101]: Int64Index([5, 4, 5], dtype='int64')
    6 }* n  ?5 j) c! u
    / J% S; F0 h; _8 u9 D0 Hmy_index.str.lstrip().str.len()% p+ c* `1 y/ O  E8 |& u
    Out[102]: Int64Index([4, 5, 5], dtype='int64')( S9 O' p$ C4 ?& P
    1
    $ {8 y1 j) y' R) P: P7 l# k, U2
    7 a( a: \( {* n2 k, d7 P1 I3
      W  g  |4 H! w40 f: Z. y5 x0 B6 ~) ]
    5
    , o) v' Z$ F  y. n; ?6# V! a9 v) t8 U( W4 Z; o# j3 ?
    7
    % }4 u% d! N, d& v$ r9 X5 `& O4 _0 F) _8
    / y1 F8 h  K! p9
    " G& |1 X% ?2 k102 V' }0 B0 K+ }) X$ A" g4 A
      对于填充型函数而言,pad是最灵活的,它可以选定字符串长度、填充的方向和填充内容:& `. s8 m6 g& d- m
    + m; d7 p0 k. ?$ W
    s = pd.Series(['a','b','c'])) Y" P9 \$ p+ L( ^

    ) L5 w% L" w4 C( T  B/ }/ l& Ys.str.pad(5,'left','*')
    7 x: k: f6 A$ F. G) G% q2 lOut[104]:
    ) ?- D& w2 v/ L5 ^' C0    ****a
      ^( @" z# d: |1    ****b
    % a7 t5 |: @3 q/ @6 F( U4 B: _. g9 M2    ****c
      H" E- O7 ?$ [& j. F5 R! ~# Jdtype: object; b# V7 T$ U7 d/ `3 x# i* y
    ; a) |- Z* o. [* [
    s.str.pad(5,'right','*')
    7 g* P/ K/ l# W8 t0 ~Out[105]: + s: Q, i9 y# j; y. z
    0    a****
    & |6 T  _- u# t3 C% M0 p! f: l1    b****
    - Y+ {( v  P( ]) b* }$ [2    c****6 i) `/ _$ g* I% I4 {
    dtype: object
    ; h, d$ j" V3 h# y: g/ w0 G9 K9 m1 t. v, o8 @! g5 L) ^
    s.str.pad(5,'both','*')
    4 ~2 Z: W  z% g8 GOut[106]:
      C) A' B# h+ E" }0    **a**% S$ w, H' n# T1 _% o0 K' q
    1    **b**
    ; O* X' m; ^; c) P2    **c**
    + k* |3 z  {2 ~( ndtype: object5 w* P8 t  \9 q) J7 ^! `2 D

    : ?! W. Z  H2 G; f& q. H; ?1
    1 k  M8 k5 {% y& J26 O+ T% F, @' V3 H: ^
    31 g* h2 L) T9 F( R7 t+ \
    4+ v. B. G* l0 J' R/ G) ?$ Q
    5
    5 [! j/ ~4 O. m69 P5 L2 b: i* ]7 g/ z6 {
    70 D' P1 x9 @( k; _
    8
    7 s+ E0 U" g0 s& Q$ r+ }! a; u* A; m) [9% L( |  Z2 ^) F
    10. S9 `0 o2 u  W+ L  N
    11
    0 w1 Q4 m2 Q* U9 H- R12
    0 I) j3 U) x6 S5 p13
    + }7 [5 u4 l( z14
    3 W. g6 r0 V! w% o15
    # U% m' |, m& B) q, s3 `& b$ B, m16
    % l8 j; |! V9 W% f17! |- U1 z, f7 \6 O  d, X$ u3 E& E
    18" q$ Z9 W/ B2 J$ G3 S8 U+ @0 q1 q
    19
    3 l' F! e# {  F. g% N- @20
    4 }/ j& d. y$ R7 f1 `" r21
    6 |( y# l( ~) e# X/ x5 e; c  w$ {221 v# u, Q1 n& p9 |8 A
      上述的三种情况可以分别用rjust, ljust, center来等效完成,需要注意ljust是指右侧填充而不是左侧填充:& @8 V3 O" }: l' g! a) d# i
    0 F5 ?3 f- X) D  q* Q* ?
    s.str.rjust(5, '*')
    6 E/ L4 g, i, B( t+ v1 H8 o+ [Out[107]: # D: Y1 b- x1 E6 g7 m* V) \
    0    ****a
    3 a/ N/ Y& K, ?+ q. F4 f1    ****b
    9 U! k  g( @. y" y& b2    ****c! S" \; X4 J8 N
    dtype: object
    8 f9 O% q% e2 l) [0 i4 f4 h  h& h' {8 z+ x& |' w; U
    s.str.ljust(5, '*')
    - ?9 M2 J, s% }) A$ `Out[108]:
    # g8 o$ ]& M6 U% }# n4 Y0 O0    a****
    3 X6 ^6 u% F; e& O. m1 j( K; g1    b****
    * x; W& _4 N! _- ]1 H4 S2    c****4 u( m7 {' X% V& p
    dtype: object
    - w$ V# X2 [! Y" ?2 V# N( Z# e9 {7 U( ]5 H4 p/ y3 H; O
    s.str.center(5, '*')8 z/ u. H2 P& B7 Q7 Z1 O
    Out[109]: # h4 J) H* v$ ~9 W1 f
    0    **a**2 x" F! p- ~9 b3 a
    1    **b**
    " \2 E6 Z2 a9 t2 C  A/ W3 \2 ?! }  k2    **c**
    4 k3 K3 v8 W) cdtype: object
    4 z& ?, L% A! n; V3 }0 h2 _' N$ u4 [
    . T% H9 i+ r& @3 l. L1; B) `3 ]1 w4 a$ m9 n6 b+ q: l
    2
    ! {2 t9 S0 ]9 @) ~) R9 o% D3
    7 ~& ]* j7 c! w* h' |$ Q* X" ]41 U$ [  ]1 ~, ~& E& I* P) ^) ^
    5
    1 h+ Q8 u4 Y- {+ u6( \8 T6 s* o9 `* e3 _
    7; q" V0 f8 p1 L( s& \) ^6 `
    8
    ( T0 X* h0 I# H. x! F6 s3 H9
    2 o3 b2 p3 N( t9 Y2 s& i- u" ^; d10/ D& r; Y; [8 ~  }" a0 w
    11
    " n& n, ~7 K. n% L# l12  y: q  s( j1 V
    13
    : Z+ J9 [' i- j; O3 `14
    ' x" s3 s- u2 G6 u15
    % p: X5 }& h3 F! |, ^" w! y16
    ; l3 c6 r: M8 O( ~+ [% l17# B. Y7 O% L- x5 `# t
    186 t1 a  X# h8 U; q8 E
    19/ A. E4 I+ V  q' d% g3 e1 a
    20  P5 A9 m6 ^0 ?1 w3 H
      在读取excel文件时,经常会出现数字前补0的需求,例如证券代码读入的时候会把"000007"作为数值7来处理,pandas中除了可以使用上面的左侧填充函数进行操作之外,还可用zfill来实现。3 n: Q  j2 J. ?6 N' U
    % M! ]6 D# G. `
    s = pd.Series([7, 155, 303000]).astype('string')
    5 _4 K" S5 ^5 c$ O( w9 q" Y& Y8 e* b" }
    s.str.pad(6,'left','0')1 o& ?0 f+ R# U- w! Z
    Out[111]:
    1 R+ C% ^5 r1 x1 ]0    0000071 L) ^% K; C8 a( Q3 ]$ @# Y: v1 M
    1    000155
    + Y: p$ A) j, x2    303000
    1 t; g& w  r$ K& ]) i0 ?dtype: string
    / D/ G7 ^% j1 h0 z% c8 o+ \2 D7 K" Y0 A9 X4 m- @
    s.str.rjust(6,'0')
    * U& w: }+ Q+ K- S+ Z, lOut[112]:
    % Z' h  G5 W0 a$ p& P" O0    000007
    * H) S0 X# h9 O# c1 g: O1    000155+ y) `6 C5 U4 K) D  V: Y
    2    3030000 E5 f/ E& w. p4 }1 ~
    dtype: string
    7 K' M* u# H) M; z: b5 Y" r% [& H3 C+ q8 j4 g, i. y
    s.str.zfill(6)5 V7 n8 A6 n- n) Y0 p
    Out[113]:
    ' |+ P) P) ~! ~& T; P5 n* N0    0000077 f( P4 W7 a/ Q% }: K+ Q1 r
    1    0001558 ~+ w8 U# H0 U: y5 k. w
    2    303000; d1 {: T, p7 o
    dtype: string
    1 I5 M- R! K# G. H! s3 [0 F5 h/ b3 m' S3 A0 d0 q# |& \
    1$ h; y. c4 r7 W: `# q$ P
    2+ O3 I! D* M0 S% j/ @+ `5 w3 y% `
    3+ o" M- q. Q$ U" [5 N8 `# \6 A+ \
    42 K( l; H1 O3 O0 _
    5
    : l9 t0 Z3 T& o69 j6 m3 ?8 B  S; g6 g( w0 R
    7
    $ ~. B0 x" B$ }2 v- V7 O8' d5 _8 r: c, l5 c. n" [' ]5 Y
    99 \3 i# M0 M& F
    10
    $ o1 u0 n! a( J9 g11) W/ m: m' {, K& q7 p, J( w8 \
    12% k) ^. o2 Z% @# A; `
    13
    ! b3 X2 p8 O8 \; e2 [* n14/ \2 c" I+ [: g6 {- E1 N, z
    15
    ! |) ?2 P: N' V16
    1 `! Q0 _  u' b- @, K& W; B2 ^  S17$ A# s5 x7 y3 @" l" a3 l. v! Y* a
    18
    4 @7 o& o+ o( K# n9 G" E  L* \& d19  v, A4 G3 H& D2 i" |
    20
    * l1 |- ~4 P8 l- ~1 x3 _$ l21# a  R" Q3 W1 L, Y: j2 ]. E
    22
    0 q0 @: Y6 ~3 m9 T( c8.5 练习
    ( W. {% y7 p& A, i0 f/ QEx1:房屋信息数据集
    1 s1 w9 j4 q% h3 g' R$ ]; j" ^3 N现有一份房屋信息数据集如下:
    $ i  N' K% [: U: K! }% {! x" R7 m' X& H
    df = pd.read_excel('../data/house_info.xls', usecols=['floor','year','area','price'])
    1 P% [6 P% i/ I" B: Qdf.head(3)5 ^- m2 C3 {' x3 ?* R
    Out[115]:
    $ s0 j8 p: w9 {; I" @' \- u      floor    year    area price
    " Y/ y& D9 ?& M) @0   高层(共6层)  1986年建  58.23㎡  155万  a6 K: J& Q. R, f1 Z
    1  中层(共20层)  2020年建     88㎡  155万
    8 a0 J: o4 R+ y6 V0 G% a3 `, G2  低层(共28层)  2010年建  89.33㎡  365万/ V, z6 [# A* \  Y  I. T" Q" A4 A2 u
    1# Y( f6 S8 w5 O4 e7 K8 C
    2
    : j+ {5 @1 i! d! G' i0 p( O6 [- a3% f  n2 A' L% @4 Z1 p7 m
    4+ p% x5 @+ S+ u: Z. j' w
    51 ~9 X& }! E" B% _1 m
    63 y0 Z# H1 j. X5 O# f: d# z  }( S0 q- p
    7! d9 m* f! D; A2 q4 w1 \# @
    将year列改为整数年份存储。
    / O$ `3 l* P1 y# Y将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。
    # R: [9 S' ~% M  i( k% q/ d+ ^计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数
    8 Q  U+ d' a, U- j3 [! |+ p将year列改为整数年份存储。
      J: ~( R  N8 A" `"""/ H, r: d; r1 W* n. D
    整个序列需要先转成Nullable类型的String类型,取出年份,再将年份转为Int64类型。
    8 M4 b3 F) H6 |2 d注意,转换的类型是Int64不是int,否则报错。即使astype加参数errors='ignore'跳过缺失值,8 f& X6 W/ z# `+ Y* F+ }) p
    转成int后,序列还有缺失值所以,还是变成了object。' M! u' M4 Q4 X+ H, q9 }
    而整个序列转为Int,就还是Int类型,缺失值变成了 pd.NA 。
    5 ~( S7 l/ \0 p/ A! q- {. e7 {" R0 q"""
    ! F" j2 _! d5 G4 D% b& }df = df.convert_dtypes()
    " z1 y1 i. p! [) P8 kdf['year']=df['year'].str.replace('\D','',regex=True).astype('Int64')
    . X  I" W8 B* }, b( b7 |df.loc[df.year.notna()]['year'].head()& y' c1 t7 u0 x2 T+ @# ^1 m; r

    ! _, @, B4 _* y& F. {0        19862 K. a  c- g/ }" M' @( M' ]6 ~( d
    1        2020
    " |* a/ a2 V* c! ~! z2        2010" S" l# M- U5 y% U" @- `7 `, f
    3        2014- X/ k/ g7 ~' [1 Y( t1 \+ Z( {
    4        20158 p& k6 D5 \5 M8 a( g2 Q
    Name: year, Length: 12850, dtype: Int64: g/ r" t8 R/ p+ ]/ ?6 A% i

    / K9 a$ y+ f2 C' a4 t& l8 Y1
      F( Q1 ~, |  N. J5 e2( ~5 M' [: L7 p5 S, R
    3& K- N# @$ J& a
    4
    * o/ t- {: Q2 `7 `3 G4 ?$ J58 L! t" F: z6 ~: R' k# o% y( k" r
    6; ~) S6 r( K3 Y) ]& y* b
    7" ]3 A2 v. t2 j9 V0 V+ g) T
    81 v  Y. L4 b2 P3 S+ I' U1 Q9 {
    9
    : o* A( d+ q: Z+ S# n& @10
    + v1 Y1 B) f/ G- {4 h" b11' A% x* X! m3 [7 ^
    125 \" h/ _2 l7 ]: j7 k3 w
    13
    ( i5 {2 R* s5 a  a! I' K% G14% R' S8 V' l! X8 A9 J4 }
    15" Q# Q8 T& w) j, o8 m
    167 Q- K  ?) D8 `; q
    参考答案:
    % a! h) q' v) B8 T1 J* S
    3 X& L; ~9 R/ H5 I不知道为啥pd.to_numeric(df.year.str[:-2],downcast="integer")类型为float32,不应该是整型么  ~7 O* P4 Y4 M& |1 A/ Q
    + I- O/ K0 ^: r+ p6 W- r
    df.year = pd.to_numeric(df.year.str[:-2]).astype('Int64') + e, J' ?# T- ^9 D
    df.loc[df.year.notna()]['year']
    & R2 M0 c! H/ v& f1. V  C3 G6 a3 o. `; X4 n6 G" f- K
    2
    : p. K$ c' h+ [将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。
    0 `7 Y, K& N( L# U' |  s" H" ~9 hpat = '(?P<Level>\w+层)(?P<Highest>\(\w+层)'
    . Q3 z* W! G) F3 k! V5 adf2=df['floor'].str.extract(pat)  # 拆分成两列,第二列还是(共6层得形式,所以还的替换一次
    + i$ A1 X, h; n7 m+ t; odf=pd.concat([df,df2],axis=1).convert_dtypes()  # 新增列拼接在后面,再次转为Nullable类型6 L: @  V$ D% q( U5 K, g
    df['Highest']=df['Highest'].str.replace('\D+','',regex=True).astype('Int64')              ) x$ J& A! {0 z# e
    df=df[['Level','Highest','year','area','price']], B4 O4 f. Q4 v. M/ O
    df.head()
    + a! l" ?- i0 H  f" A9 e* U# l7 F! |$ d7 K' R# y
       Level  Highest        year        area        price
    : [' D" \3 s' u9 F' E! |3 _, J$ J0        高层                6                1986        58.23㎡        155万; G' [4 h/ T+ k' p( z
    1        中层                20                2020        88㎡        155万
    / w$ K( t3 Q3 K. J9 }2        低层                28                2010        89.33㎡        365万
    2 I( N$ L0 T  R0 a6 j7 q" M5 `3        低层                20                2014        82㎡        308万" U9 v3 ]) L& D7 }+ ]
    4        高层                1                2015        98㎡        117万. a8 R) k0 |  T6 `4 B
    1
    - R& I' ]& o- {' I  h$ ~3 v3 }9 f- m3 r2
    3 Q. n. f+ ^( s; W4 O, T0 h3
    ! [  j) [# u' A5 ?0 K4) x/ d4 t7 K# ]# \: |4 g) `; g
    5
      K. u/ g/ l8 S; b' y6
    & U! Z3 ]$ c% {7; X6 W) r0 n+ K: P. H/ U/ i
    8
    4 ^$ `! t  G! x! |  O* H/ B  x9: B+ m$ m* R" B0 c3 u! _
    10: A8 z: w' q. G0 f# a
    11% h2 J; |7 s  c' W. }; e- b% i
    12
    8 @/ @1 _4 ?( o6 Y% x  M" K138 c4 U) u& ^) r& Y# X) {
    # 参考答案。感觉是第二个字段加了中文的()可以准备匹配出数字,但是不好直接命令子组了
    4 E6 c% w1 J3 `, x0 Rpat = '(\w层)(共(\d+)层)'3 I# y6 Q5 d0 V! |( q
    new_cols = df.floor.str.extract(pat).rename(
    7 Y1 L: C# Z% T2 F! K) V3 U                    columns={0:'Level', 1:'Highest'})$ [0 f7 M2 P$ B8 @
    , U( `4 }* A. a) q" i
    df = pd.concat([df.drop(columns=['floor']), new_cols], 1)& }" C0 L5 M3 d
    df.head(3)
    1 r  a  T3 O- y2 k" |! y
    0 o( u( [4 p4 hOut[163]:
    3 H3 K, ~+ Q, U( T   year    area price    Level Highest
    * g, x$ @; I0 v4 ?+ n4 o/ U0  1986  58.23㎡  155万    高层       6: ~, Y" J6 \) x& V
    1  2020     88㎡  155万    中层      20) x) N( q. h( G* B' c
    2  2010  89.33㎡  365万    低层      28/ O: v( y! A* x) j
    1
    4 t. n6 t9 W8 q9 b8 F8 W& q- _! y2
    $ w& ^3 h6 }: X5 K30 _# e& A- D6 T8 g
    4) w' ]0 B8 @7 x
    5
    3 K& X$ u- T0 u& J$ _68 K, y2 c! F1 I! [( c* L
    78 k( P) b5 P& \7 p3 R$ |  k8 O" o
    8
    * E/ Y5 T8 M4 w' V+ d  t9  g/ c; S1 ?& U/ E
    10
    % x, \1 T% T* p& P5 o11$ S/ D2 M, s! i  u4 {
    121 }! s* S/ I% X9 W$ W
    13
    $ B: p" e1 d' a* L8 \计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数。; {; I- z9 a/ |) Z) e
    """
    9 t$ H; Z* u% j7 K- W# ?$ ]str.findall返回的结果都是列表,只能用apply取值去掉列表形式
    $ i7 }* \+ f$ C7 ~' C参考答案用pd.to_numeric(df.area.str[:-1])更简洁4 l9 o( T: y% Z; S. r& X4 F1 K
    由于area和price都没有缺失值,所以可以直接转类型
    . _" S! T# D8 I# O; p- {. D$ H0 K"""! z8 O5 U0 l  C2 j" q$ ^- u* x  k
    df['new_area']=df['area'].str.findall(r'\d+.\d+|\d+').apply(lambda x:float(x[0]))
    : S( z4 q6 l! Gdf['new_price']=df['price'].str.replace('\D+','',regex=True).astype('int64')7 d, q$ I, l; i' W6 ?1 l
    df.eval('avg_price=10000*new_price/new_area',inplace=True)
    ( k% x9 D6 }, L; H4 Q5 N# 最后均价这一列小数转整型直接用.astype('int')就行,我还准备.apply(lambda x:int(round(x,0)))
    - Q8 m7 F& c3 b, O+ E1 c, {# 最后数字+元/平米写法更简单3 j' k% X! c/ D2 O9 F
    df['avg_price']=df['avg_price'].astype('int').astype('string')+'元/平米'* z  [8 u% c) E; D
    del df['new_area'],df['new_price']
    . x, |9 b. e" ?& J' D/ gdf.head(): o* j$ \4 C+ p( d; W

    9 }8 f' _8 Z& f' K  B   Level        Highest        year        area        price        avg_price" j* E3 U1 v, q# n& E% o, m0 s
    0        高层                        6        1986        58.23㎡        155万        26618元/平米
    % m7 Q3 t2 `- [$ p3 j/ A# K  M" W1        中层                        20        2020        88㎡        155万        17613元/平米7 F' e% q$ H, L) I- _: g
    2        低层                        28        2010        89.33㎡        365万        40859元/平米
    . }; m6 S1 f, r5 F' v6 ^: ~3        低层                        20        2014        82㎡        308万        37560元/平米' k6 [9 l) Q% h: U& g# }. X
    4        高层                        1        2015        98㎡        117万        11938元/平米7 v; l) H( a/ J3 Q$ \0 K

    * I6 J9 E- ?9 @# Y9 U: I0 e1# h/ K- C( Q; d$ k
    2
    7 @8 G& I8 h. K0 s9 X$ O3
    4 S$ K4 @8 r, G1 i& L4$ s7 I- l1 D) o" P
    5
    : D6 `2 \0 I! x. W6 s65 b( D' Q; B; }
    7) P' S7 L5 L- ~+ v
    8' s$ _$ D; D( b4 S+ U- O
    9
    " F0 T# u7 |% B9 u6 N10
    " _$ j1 {6 @' b0 P  K8 H( F11# i5 B. q  o. Z% `7 m$ e, t: z6 i4 x
    12; K( |7 y0 r  a
    13
    & N; m7 T: v" C142 a0 c( B' K* d; f0 y: f
    15
    3 V: h) _; Z2 M! `& o- j6 K16
    ! l8 i0 W- D9 j9 g17
    # o5 z+ ]& K% {/ Z& R( ]18  ]; X+ G" T5 S0 h7 d8 m" p9 w
    19
    ' f) @# _) _7 p% O20
    2 z  P7 L9 ^# Y: n, w# 参考答案
    - i; G( V" Y% @7 x  j, N5 ^- F( as_area = pd.to_numeric(df.area.str[:-1]). ?4 s2 X$ T5 t, p, r
    s_price = pd.to_numeric(df.price.str[:-1])4 ?, B% I) m1 B4 `4 |, N$ Z7 ?+ [% n/ k
    df['avg_price'] = ((s_price/s_area)*10000).astype(
    - y: X" y8 y! ?/ G; A; @6 a! e0 h                    'int').astype('string') + '元/平米'
    0 u5 P9 ]" z8 M
    " D# s2 E2 w* L$ x- g' o9 Zdf.head(3)8 W* E9 v, ]: p: X, l; d
    Out[167]: 5 _5 U6 f. ?& G# c- W, j" x5 M/ i
       year    area   price   Level Highest  avg_price
    8 p' k+ e" r+ {$ L9 M0  1986  58.23㎡  155万    高层     6          26618元/平米
    : J- x5 v8 W. r1 \7 E( z  v1  2020     88㎡  155万    中层     20          17613元/平米' _* O: {$ ?  n8 w
    2  2010  89.33㎡  365万    低层     28          40859元/平米4 h2 f9 {) y6 G  s  V3 i, e* X1 X2 L
    1" g! |( G" j- a4 I( ]3 a: x
    2+ T1 E/ I' d# R' A
    3
    6 }6 \; h( g5 D. p) z& B/ b* |4
    ) l8 q1 H  q" f( X  ~6 i9 }5
    , t* M) ]% w& [7 n' T61 J( g& e' _: w( g
    7
    4 u  v( l5 e% ]) u8 z81 x4 n% ]; ?! m( g) m/ c
    9
    ( ^  [1 Y( C9 f6 U! @% Y10
    + t% c/ j" ]* r  D7 E  b11; a+ w) K( j2 I2 g1 Y# I
    12" Z% T9 Y5 G3 A. p1 l* r, u3 z
    Ex2:《权力的游戏》剧本数据集; G+ H9 L0 T) Y
    现有一份权力的游戏剧本数据集如下:: T; {3 C  ?) u0 U) ]

    0 Z( q: l9 Z9 Y& X; ^df = pd.read_csv('../data/script.csv')3 e" e" k7 h6 _7 y; e! i
    df.head(3): s+ O; A6 F7 a  @4 v- e
    4 Q, v  V1 L( B1 ^( c! b, G4 v
    Out[115]:
    $ h! |3 `9 n( x0 ?Out[117]:
    & ?% u, J$ x/ B$ C3 n9 @  Release Date    Season   Episode      Episode Title          Name                                           Sentence: |4 Z% a4 l& v6 N, X  [
    0   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce  What do you expect? They're savages. One lot s...) U( k' k& p- s* K' b) A
    1   2011-04-17  Season 1  Episode 1  Winter is Coming          will  I've never seen wildlings do a thing like this...! b- N  D" W7 k3 [: j* j4 B7 D
    2   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce 1 o+ y  }9 h9 ]+ c6 Q5 e8 X
    1
    ' ]# p6 o' h2 k6 m2
    5 Y$ r. a  o- G, R! ?( D1 ^3
    * n' T( G0 L' ~9 O4
    2 c/ p- F% C- }5
    % M, R- Y8 z' [60 c" o9 O. m3 M4 |. r% }! K/ V7 ^- `
    7
    5 G; \9 \: u8 X; Y8
    6 \" @* Y. K. N+ E5 U9. ]; ?3 d/ u% F
    计算每一个Episode的台词条数。& v% t  P/ G: B& Y' i
    以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。9 U: f8 {; T. |7 {
    若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有 &#119899; 个问号,则认为回答者回答了 &#119899; 个问题,请求出回答最多问题的前五个人。) M4 o) I1 d! D& r( w
    计算每一个Episode的台词条数。7 h+ L7 i% Q0 C4 o
    df.columns =df.columns.str.strip() #  列名中有空格- t, k( w1 Q+ I6 a
    df.groupby(['Season','Episode'])['Sentence'].count().sort_values(ascending=False).head()
    + {- A9 j" w% D; P7 C- I# K
    $ p/ s( R( l% d" |! F0 eseason    Episode  
    ( Q8 _: ~. S- ~Season 7  Episode 5    505
    0 T/ Z3 {* o1 q5 r- N5 oSeason 3  Episode 2    480
    % |1 L& x. k, F! xSeason 4  Episode 1    475. o, T" v4 n. h; o5 n
    Season 3  Episode 5    440
    ( }+ b. p% u* i" R/ c8 E. ISeason 2  Episode 2    432
    5 I: N8 Q; c4 Z. _" h1: }. C7 D0 t* _" g- b
    2! H! P3 \' R( s9 ^' p
    3" w! H$ h; ~5 n& b4 g3 d; ~
    4
    $ f) h* F+ U, |; F0 A! r51 i0 z4 e' N/ U2 ~1 u, {( O& ?' r
    6
    4 w. R( }) p/ A, C7
    * q# Q' q" B1 {8
    - O& s, J5 n! o$ L9" \) y2 |1 n  d4 R
    以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
    % S4 _* N) w: g+ T! Q! g# R# str.count是可以计算每个字符串被正则匹配了多少次,+1就是单词数& A% {" t7 g: I, M; c
    df['len_words']=df['Sentence'].str.count(r' ')+1% \" n( s, r; c  I2 a
    df.groupby(['Name'])['len_words'].mean().sort_values(ascending=False).head()
    3 u) \+ n$ j2 t: _! z; o8 ]  l" P4 L! d3 q2 k: B
    Name% F0 b: K; ?, D' ]: `1 n7 j) V
    male singer          109.000000. o- d9 Y; Y: H* X5 J. e4 U0 W) ~4 p
    slave owner           77.000000
    3 T* a9 C, N6 I6 V3 nmanderly              62.000000
    - E3 H. ]! N3 elollys stokeworth     62.000000
    ' i9 {. T5 h& b' O" U8 H& xdothraki matron       56.666667
    ' W, r; [) E. U, A0 E- WName: len_words, dtype: float645 l3 [: W" j: p4 ]2 d
    1
    % j( O& P0 |2 `% A- t# d. [0 D29 y9 G$ p+ j% t) n3 F
    3, H4 H9 g: J. u( |" X
    4
    % {2 _) @6 N) G% q3 @# H7 \5
    7 e3 h' _- B+ e  w6
    0 T4 o2 {; u* ~, d! ^0 @75 L$ T$ K* j+ }" O  \, H$ A  P( E
    8
    ( I2 S' G8 K" h6 ?2 u' c& Q9
    $ a1 X  B7 {/ V6 H/ d  y+ S- e10
    . O! H1 G$ S2 }0 _3 o/ x9 {  U9 F113 A) Y4 Y  K* R6 {; d
    若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有n nn个问号,则认为回答者回答了n nn个问题,请求出回答最多问题的前五个人。
    . L* |0 j/ |( [; q# t9 O8 Sdf['Sentence'].str.count(r'\?') #  计算每人提问数
    . @( S- o3 _0 E; lls=pd.concat([pd.Series(0),ls]).reset_index(drop=True)# 首行填0+ z$ f, [/ d# P# z# f0 V$ q. d( {
    del ls[23911] # 末行删去
    1 s/ E& |2 m& l+ P. Qdf['len_questions']=ls
    " t4 h' c4 f; h1 T! U5 edf.groupby(['Name'])['len_questions'].sum().sort_values(ascending=False).head()8 Z. ?2 \2 Z7 n/ M' z. J& X3 f
    1 K9 _! D) N' N4 Q3 K5 A
    Name
    $ J- V3 u/ o  S" ]6 i0 ctyrion lannister    527$ F3 m8 n2 R' S  N  _9 {" `
    jon snow            374
    & [+ L/ i. G3 w" Y0 ~0 |/ x' f2 Wjaime lannister     283
    " |# ~/ V- A( ^4 \8 barya stark          2652 P+ K6 F  g$ ~7 ~+ w7 s* K
    cersei lannister    246( A5 l5 {3 U0 l' ]# |' B
    Name: len_questions, dtype: int64
    - R7 p+ {$ e1 |" f) L$ N( K% k9 y
    4 U  o3 I3 u" o7 q7 }# 参考答案
    ' |! Q. f: Z9 Q  @  ms = pd.Series(df.Sentence.values, index=df.Name.shift(-1))0 H6 D/ d7 A; _$ }$ K$ t( E9 V
    s.str.count('\?').groupby('Name').sum().sort_values(ascending=False).head()
    7 M4 t8 L; i) M$ z. {$ B1 Z) r1 p  g$ D0 w
    1
    ) I  ?8 p& l+ y) X21 a9 z4 E7 Z  |, l4 t
    3' M- {# p3 {9 a+ m
    4
    4 B9 n4 I4 [# S! c5
    % e8 ~- h$ s9 M! C- h) D68 I8 f$ S2 T1 j8 e. S9 T
    7
    . W4 F' t4 y4 f7 f9 v86 z' J  a1 ]  _, B: x" R
    91 H3 Y  Y2 H5 a) ~1 u- j
    10( a, S0 b9 G. C0 i
    112 M+ m7 w; z0 n( |+ i2 J8 p1 c: {
    12) m: D1 q: X, R& Y5 Y
    13: G9 m. G9 S1 @" \
    14
    $ k! v! n/ R0 f! T, ~15
    ' n# i0 i# T# Q% V( T( m5 i168 h+ X/ K+ X, V+ H9 F' H8 ^
    177 o' B4 B6 i$ _8 X4 V: s
    第九章 分类数据, C+ f/ L- y+ f- c4 P: N
    import numpy as np
    ) Z" n& X$ f0 X/ D8 m3 Nimport pandas as pd0 x" P: M4 |8 H$ Q8 U+ m4 T& a
    1; P/ W9 S9 V6 y: F/ @2 @5 b/ ^
    2
    ( c1 T1 U' T. l" X& Z- z9.1 cat对象  @4 s! L3 R8 d
    9.1.1 cat对象的属性$ v, G$ S8 V6 o! f% }
      在pandas中提供了category类型,使用户能够处理分类类型的变量,将一个普通序列转换成分类变量可以使用astype方法。
    . P' i. S- j  Q( w( C* Y" |' I1 L0 P. F/ a
    df = pd.read_csv('data/learn_pandas.csv',0 X  p/ C" u/ @3 U! c: b" R8 H- i
         usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight'])
    * @% Z9 H1 G% D, r5 g7 X1 ps = df.Grade.astype('category')6 `7 K: w) P2 B: S: p
    + b- f) W% s% x7 f; _6 ^* |, P4 h
    s.head()0 v" q5 i/ b4 }. U: w7 S
    Out[5]: 5 Q: b; F8 S6 L5 S9 }
    0     Freshman
    ; Q4 t6 y8 z% y9 e4 T1     Freshman
    / x4 ?* N2 x$ v$ V7 F9 r2       Senior$ a+ {6 I9 c/ m& |9 C
    3    Sophomore/ J7 t1 I. r: ~. _, f" d
    4    Sophomore
    , Z6 s$ j& U* s: F+ IName: Grade, dtype: category2 v) x8 U' [2 P; ~
    Categories (4, object): ['Freshman', 'Junior', 'Senior', 'Sophomore']
    ; G/ @; e4 h- W& W5 T1& I/ \" F2 O5 ^( G2 y
    21 W/ g' R% D) Q% K' G
    3
    9 N( L' o5 ^; o4 [  t$ e2 f4
    / \! d2 _$ N  V0 l: _: e5( }# m2 q: s& U! E3 F
    6
    : S" Z$ C- e$ D0 K3 ~! n73 D5 T5 s& f, e
    87 r7 k- p1 K( z' s) P" j# }4 @  ~
    9
    7 B7 m$ I1 z0 s1 m; R' O- d108 H0 X7 z* d" u
    11
    6 ?. t+ e: I) m6 s: Y- a" P12
    . C. `3 g& r; f) B2 q- M13  m" G5 C. i. Y2 q% ?
      在一个分类类型的Series中定义了cat对象,它和上一章中介绍的str对象类似,定义了一些属性和方法来进行分类类别的操作。, G. C. R: G0 l) w$ M
    ( F/ R$ H0 E) k4 c- P
    s.cat
    3 T$ `6 D* ~' a2 i& {Out[6]: <pandas.core.arrays.categorical.CategoricalAccessor object at 0x000002B7974C20A0>' c0 X& ^5 U2 Q0 o
    1
    7 Y+ N( U% F( C, }2  E6 ]6 _; y8 T7 s4 `2 W: `! G& X
    cat的属性:5 |) K5 o9 R( H9 P  O: F$ B
    + D: l/ j) x# Y  L( ^% T
    cat.categories:查看类别的本身,它以Index类型存储
    8 F8 Y# i  D8 ~8 P& _$ v. Xcat.ordered:类别是否有序4 k! k! @+ c$ J& c4 ^
    cat.codes:访问类别编号。每一个序列的类别会被赋予唯一的整数编号,它们的编号取决于cat.categories中的顺序( S5 S9 g2 [/ A% Y
    s.cat.categories
    2 t& A2 M* `# nOut[7]: Index(['Freshman', 'Junior', 'Senior', 'Sophomore'], dtype='object'). u3 w$ v& `( k7 _/ @6 }

    + i4 O6 m+ D$ |7 ms.cat.ordered
    , M8 Y. s1 E# l  C& M& oOut[8]: False
    0 m+ b- Q* R! y- y$ U
    + ~; i6 I$ u- B) {s.cat.codes.head()
    # V( w3 e( G% J5 ?+ q, k- fOut[9]:
    ! p9 i4 f! J6 @0    0
    - g: J) I! i, ^6 f1    0! m; O- ?* a, \( n
    2    2
    ) n  ^* S! i9 v' j6 H, E3    3
    / [+ d0 c8 Y/ P8 \! E& W9 K5 c* o4 g5 p4    3
    1 r# T/ H( N" }0 D6 |& Udtype: int87 c7 O! x+ Z  F5 v0 l  k/ G
    1  l+ h9 T) t1 t4 j* a! ^
    2+ t# Z+ F4 Y, m3 ^2 f2 T8 E
    3# M, ]" ]( u: w$ n9 I3 ^
    42 o& v( z( M0 h4 J$ u! h
    58 J4 b$ k; ?& b& @8 l
    6
    % u% \0 C$ N* I. w& A6 i# M2 }72 l$ ?, r  I- \9 d; d  \, u& b
    8
    " u9 D. {- X: d4 y9
    0 u6 A+ p( }: D10+ ]1 j; A4 U+ f$ [  w/ T, R  }
    11
    % y3 K2 r# ~+ z+ h. [! ^128 o( N, ?( B2 Y+ O( N* Y* n9 L2 J
    13
    5 ~" T' |& F' B. c14
    ) h! ^/ p& _+ s8 Q9.1.2 类别的增加、删除和修改$ A' s) x/ d; J' Q' G2 h4 k. V
      通过cat对象的categories属性能够完成对类别的查询,那么应该如何进行“增改查删”的其他三个操作呢?5 T" ^, V) [& l# Y

    ( ~# {" f, S- Y) e$ @6 F3 ^8 }8 H【NOTE】类别不得直接修改
    + e8 i6 m8 j$ P. C6 m在第三章中曾提到,索引 Index 类型是无法用 index_obj[0] = item 来修改的,而 categories 被存储在 Index 中,因此 pandas 在 cat 属性上定义了若干方法来达到相同的目的。
    ) G+ j" ^) b- w9 P4 x
    ) M' O! j4 n: r" o! |! n0 kadd_categories:增加类别3 `1 H- C/ p3 v
    s = s.cat.add_categories('Graduate') # 增加一个毕业生类别6 E: l# S7 [3 |2 G) ]1 E& y
    s.cat.categories! ?9 \. `6 w. E1 o! z, w

    - @( `' |9 N8 s& {1 JIndex(['Freshman', 'Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')+ [$ Y8 B3 b3 G, j6 v  k
    1
    - _5 @/ [  k7 D! F2 X4 ]2
    0 z& j, r' p( J8 }4 V( H2 q/ K3
    + X* r; \! f6 j4% [, @, v* I4 t% d/ U! r% p
    remove_categories:删除类别。同时所有原来序列中的该类会被设置为缺失。  o! \/ m1 q& R
    s = s.cat.remove_categories('Freshman')
    % b# h- T: J& D" C5 v. F9 p  _+ ^+ m
    9 t5 Y0 }; W+ bs.cat.categories
    + D* ]" f7 p# s0 F, t* w6 WOut[13]: Index(['Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
    . w, ]8 L3 M2 R' }' e1 r" a0 P3 A
    * ?% @1 D6 [! U( _! l+ Xs.head()+ j! Z. u/ t4 @! _
    Out[14]: ) r1 u6 ^3 j! T& G& {3 x
    0          NaN/ I% }, t' h  n) w- W& Y0 F$ f% K
    1          NaN& H/ f" y" h9 S! u6 f2 \( m2 {- O
    2       Senior
    / g+ L9 s+ I+ n* u+ F3    Sophomore
    - Y& Y) W. Q" P4    Sophomore& p1 F  S% i% A, r1 _
    Name: Grade, dtype: category( g4 a2 P1 h4 |0 }! M* `
    Categories (4, object): ['Junior', 'Senior', 'Sophomore', 'Graduate']
    - ~. N3 M8 Y; _9 D1
    5 H" E" a( C5 l3 G4 Q2
    2 o0 \5 s0 s2 t- Y% B: p# X, l3
    ) J7 ^+ Z8 l. e5 f4 l44 `9 T' L8 K6 b8 h* }6 p: {
    5
    : s( r! P* |6 q. T6
    , }' E, V: N0 P5 A( }& D7* `) c$ a$ S0 D4 v; b( u, A  J
    8$ u  m: ~! Z" [" H4 ^+ v
    9# Z' J4 {# m- l$ f
    10
    8 }' ]" L# D0 b: d7 ^& o110 R% p' h% Z  K8 ^8 ?; D
    12
    3 E) _! I2 k: g. G! I% d5 G- K139 \, P+ e+ \8 v: h) H' N
    14/ c+ b7 j: r& t2 K5 Y
    set_categories:直接设置序列的新类别,原来的类别中如果存在元素不属于新类别,那么会被设置为缺失。相当于索引重设。
    , U) v0 F: Q9 M! T/ K% Is = s.cat.set_categories(['Sophomore','PhD']) # 新类别为大二学生和博士) [/ L3 f2 ?0 l! ^  N' q/ `
    s.cat.categories: E$ h4 x  R( B0 C
    Out[16]: Index(['Sophomore', 'PhD'], dtype='object')( d% r" ?2 T" W/ i: s

    ! d! M; v! j5 Y( w* n: P2 Ns.head()
    " e/ |+ W% o9 h+ [8 `; g' u* WOut[17]:
    * l9 C4 O5 g$ l* b) O. h' G4 X0          NaN
    ( k  x# q5 Y, v, u- Q& W7 G) T! c1          NaN
    9 E5 X5 C% f4 K% e2          NaN
    8 ]* L2 z% V) ^  R3    Sophomore8 ^" Y. `# \6 q7 d. u# N
    4    Sophomore  f3 E4 r7 M; e& z; K: ~) o* j
    Name: Grade, dtype: category: z; |: e, |7 S5 l
    Categories (2, object): ['Sophomore', 'PhD']. r6 x* O4 y0 r) f
    17 H+ {( _0 E6 `/ j2 K1 w" c# p5 X
    2' E2 O4 y/ j+ J2 Y) e5 B
    3
    2 C* H* p& \6 z3 `4 G42 t# z; y/ A* {5 f. \
    5% V3 [, j' H* Y& _: h
    6
    ! X. i, e" J$ S" C8 y" C7& f9 j9 C; N* U- C- _3 k2 F
    8
    8 S$ B7 m* n! q* T, h  w1 ^3 @* q% R9
    # r3 g" F* |. C: c6 R0 l10
    2 y8 g7 h3 C; Y+ O+ y0 p1 Z6 h119 n! S) O3 G) h. o8 ^
    12$ B0 @8 u( ?$ C$ _3 e
    13% M; d2 I  x+ o% e
    remove_unused_categories:删除未出现在序列中的类别9 v2 S9 Q, ^2 A1 u
    s = s.cat.remove_unused_categories() # 移除了未出现的博士生类别
    1 c/ M' k/ s, V; C/ \; ~( W) ]s.cat.categories- `. b% ?1 T4 E, E2 I5 t( ]

    8 y* b! X: W) F, N" NIndex(['Sophomore'], dtype='object')
    ' W# O- h  k3 w  r10 f3 O' |' l$ @8 ]' Q
    2
    - J# g: Z" U% O( s3# t* ]7 X0 o( R0 W
    4
    ' h3 b5 m* A( v$ u( \- h" o. qrename_categories:修改序列的类别。注意,这个方法会对原序列的对应值也进行相应修改。例如,现在把Sophomore改成中文的本科二年级学生:9 C) S1 X. p) P) ?% t. W8 J
    s = s.cat.rename_categories({'Sophomore':'本科二年级学生'}). b# j: T1 P- r1 f$ q# \
    s.head()
    . ?% p, j) ]. U9 f3 q- D+ r2 ^- v7 p0 Q2 P& @6 k
    0        NaN
    , G) \+ i9 `% n% |; |! N1        NaN; {0 B- Y9 ]0 ]  h3 \
    2        NaN
    ! ~1 y9 b! S6 L6 c3    本科二年级学生& X8 x3 f2 s- r9 |
    4    本科二年级学生
    1 o. e8 j: a( j. O0 Y- q, k# GName: Grade, dtype: category% g9 G% w6 z' o, T- C, _
    Categories (1, object): ['本科二年级学生']
    2 y! ~' a4 C, C% |' Q  q, U- ~+ r1& @. Q  \5 e* `4 x4 F2 `) l
    2
    4 C& O. y: s* O8 ]) s' i3
    3 a" z9 ^% o! v4 k# I; f5 B% D43 q: [9 d0 L* Q5 i
    5
    $ E0 {" h/ b& N% K( b9 y64 z1 }: p+ m  e, n
    7
    / K& }8 M: L/ U  q88 u( w5 I6 w& J6 ~& J2 T1 W) t
    93 c" _0 f/ `# Z4 L0 S- t: N, `
    10
    . x4 z) H+ A( l1 @) c  f7 T9.2 有序分类
    ) O& E# j5 w6 d9.2.1 序的建立
    8 S. l) W, }6 a+ W$ x- c% `# b  有序类别和无序类别可以通过as_unordered和reorder_categories互相转化。reorder_categories传入的参数必须是由当前序列的无序类别构成的列表,不能够新增或减少原先的类别,且必须指定参数ordered=True,否则方法无效。例如,对年级高低进行相对大小的类别划分,然后再恢复无序状态:% Q9 u  l' Z3 \  v" f

    4 I; U) y: C$ es = df.Grade.astype('category'); ~' r) N! Z1 k. ~- ]3 e
    s = s.cat.reorder_categories(['Freshman', 'Sophomore'," `2 {# f2 A& b8 @; s, X
                                  'Junior', 'Senior'],ordered=True)
    0 M/ t7 }6 `% @. Bs.head()
    0 ], u0 V2 g, G6 J. P1 O. zOut[24]:
    / t: _3 v7 f% f, L" d$ P* ?7 V0     Freshman5 D4 f; o4 K4 h9 H: i  y
    1     Freshman
    & R( h4 G. n! q* A! d2       Senior/ z) }; j" r* T
    3    Sophomore
    1 G3 _' R2 p( ]1 O3 G4    Sophomore4 e5 M; U6 l! j9 a1 X5 @# P6 W
    Name: Grade, dtype: category
    ; R0 ?8 ~9 q& f/ A/ O5 t, @Categories (4, object): ['Freshman' < 'Sophomore' < 'Junior' < 'Senior']. ^! A) b( ^$ D  ?# Y/ L0 d

    + j0 S3 `' v4 [4 I5 j+ H" Es.cat.as_unordered().head()
    + E9 i. q2 \7 S6 ?Out[25]: " g9 B8 H0 L1 F0 J! C4 c' y; ]
    0     Freshman6 r( n! F0 Q, B, V  v
    1     Freshman) x4 A0 R( u$ ~& q
    2       Senior
    ; X& S0 U% m& s% H3    Sophomore. R9 f+ g/ c0 s$ ^- \  C- ?, P
    4    Sophomore7 I1 ]4 q$ y# M
    Name: Grade, dtype: category2 v" B* Q! H3 M! _# G  ?$ ^
    Categories (4, object): ['Freshman', 'Sophomore', 'Junior', 'Senior']3 `* {0 |$ {& ]4 _! f/ c. T0 P

    / V* v" a6 P  q1+ z% N: {' q2 X
    2
    6 p1 v1 `+ K# u( g- `* `' F8 U3: I% J% ^7 Z( {( {) k) q. r
    4- p* o& R( o; h3 r6 ^; X
    5
    . `0 t9 \( L% Y5 f65 l9 H  H+ p! n2 W  F9 l
    7$ t6 B. s  d: r  L
    8/ x% j% e3 G7 E; _9 }  @/ }  s( V
    9
    3 |+ }+ W% F2 a8 \& o; Q10
    ' K) `3 h& p( [4 B8 o$ G" k11  j1 f% R+ b. X. b" ~0 O+ b7 ~7 t
    128 ]# @$ L. ]+ c; F# Q. P4 B
    13
    * D; ]  ^& V$ Y9 }1 r; v- g! ~14
    + \) V7 U1 n) M% l% _. d3 I15  h+ J5 _1 ]3 n8 |+ E! C7 {' Y
    16
    0 `. Z' I  Q0 j; |+ K1 o17
    + C/ C" c( c7 M' g) ~- A18
    2 M; B4 v0 i1 [: y$ d; t5 e# m19% `0 I5 L+ o" Q+ Z% x; V( E3 Q' ^: j
    20
    ' r& l4 \* N7 ?5 s" j21, f: v" N  k: G( l% \! }8 u" ^
    224 {0 f2 F% @0 W0 g/ P
      如果不想指定ordered=True参数,那么可以先用s.cat.as_ordered()转化为有序类别,再利用reorder_categories进行具体的相对大小调整。
    ; j. X  q5 a0 q' X' b/ N" d
    0 e2 M8 W$ h5 I$ i1 v, [" L9.2.2 排序和比较) F5 j2 w: j; O2 S- O0 ]
    在第二章中,曾提到了字符串和数值类型序列的排序。前者按照字母顺序排序,后者按照数值大小排序。
    & v$ R8 P( ]/ Y' D9 `: P
    9 l- l& Q  i$ f& W  分类变量排序,只需把列的类型修改为category后,再赋予相应的大小关系,就能正常地使用sort_index和sort_values。例如,对年级进行排序:
    - f& ^- f; F  B6 `+ y( J/ {6 P
    ( Z* ?& E# u5 j  R* Rdf.Grade = df.Grade.astype('category')
    $ \8 o& T  ~! a; n8 S7 xdf.Grade = df.Grade.cat.reorder_categories(['Freshman', 'Sophomore', 'Junior', 'Senior'],ordered=True)
    1 e% U8 z1 S. n" d7 Jdf.sort_values('Grade').head() # 值排序% X2 }0 u5 L8 }  s4 n( j
    Out[28]:
    3 T9 P8 e5 j& V. V- K+ U( {        Grade           Name  Gender  Height  Weight
    * l' \* k. D% P. q! g, d0    Freshman   Gaopeng Yang  Female   158.9    46.0
    0 m! y6 t  r. u/ r" B0 i105  Freshman      Qiang Shi  Female   164.5    52.0
    6 G% {5 m! c0 r% w1 v96   Freshman  Changmei Feng  Female   163.8    56.0
    0 t8 Q# H3 @& T88   Freshman   Xiaopeng Han  Female   164.1    53.0
    4 h* J6 D5 E( ]81   Freshman    Yanli Zhang  Female   165.1    52.0
    - O. f( n8 X! D4 ]0 k2 X, `: z) d# m  P- [# l
    df.set_index('Grade').sort_index().head() # 索引排序/ `" a6 Y8 B( w; C
    Out[29]: # v5 R% }4 t5 L, h0 r9 x
                       Name  Gender  Height  Weight% ~1 D; c+ i" f, o+ A8 p
    Grade                                          
    ) L) x# c) n. ~! bFreshman   Gaopeng Yang  Female   158.9    46.0
    # j9 p: S! |8 h8 rFreshman      Qiang Shi  Female   164.5    52.0
    ; z1 c3 ]3 _  ~4 Z7 X) qFreshman  Changmei Feng  Female   163.8    56.0; `. f2 Y; P  u2 U! {0 V
    Freshman   Xiaopeng Han  Female   164.1    53.01 Q* X) }3 x4 S: F) B3 C; x
    Freshman    Yanli Zhang  Female   165.1    52.0+ Z; z% R/ `! }" \( Z1 e

    * Y6 ~; ^% K9 p( \1 {0 s1- Y- A, l' W4 N& L$ J) a1 r2 ?
    2' H  H1 T' B: z) V2 K3 v
    3
    6 l3 d4 {9 X4 h% x7 o7 \4
    , o- w; ~2 r" z5 G0 l5
    * m( E: z+ ]7 o( ^3 A6- c2 |; T1 I# c
    70 f1 y  c& x; J8 b$ x4 y/ i
    8
      ^5 v( v( x; ~! \7 ]9
    / H9 V2 \8 b2 }, k$ H/ O" y/ n8 ^- W10& h9 |3 Q. f8 \$ S
    113 M' b- w: W& B* L# g+ _" p
    12
    ( N7 k! P/ p* F2 |& b, J/ w13
    $ E9 y! ?2 \# a- ?( U$ k14, @8 |; g, l4 o/ S
    15, x, U2 G! @, n8 M' b$ \
    16. C# @: ?5 j% Q+ L" ~2 C$ n0 D. a# |8 R$ P
    17% W: t5 ?3 X, w! i- G! b1 n
    185 `% n- e% C$ X' A3 a
    19
    ( h. R4 n6 A" p20
    5 }- R9 a4 r9 M8 I* W; a* B  由于序的建立,因此就可以进行比较操作,方便后续索引操作。分类变量的比较操作分为两类:, N2 X9 G2 r: `7 F, s

    . P  H/ C( W* W; k8 {  L; z==或!=关系的比较,比较的对象可以是标量或者同长度的Series(或list)。(无序时也可以比较)
    + k: S' \4 w0 B. }# _2 t>,>=,<,<=四类大小关系的比较,比较的对象和第一种类似,但是所有参与比较的元素必须属于原序列的categories,同时要和原序列具有相同的索引。
    $ h" f1 L# B4 O% Zres1 = df.Grade == 'Sophomore'8 T/ z  V2 b$ ~6 [

    : E/ Q0 S  L! F, r, Mres1.head(). i9 B, x: Y" S; O) a9 U0 N9 G; ~
    Out[31]:
    1 ^; f/ y7 p0 L$ h3 z/ t0    False
    1 n# z5 }& z: R9 R1    False
    * ~( N1 k' d6 S! G2 B2    False( @; g, N) i/ m9 i
    3     True1 P& C$ X$ r6 ?  D# ]
    4     True/ |/ J/ N4 @/ l
    Name: Grade, dtype: bool4 c2 }- \5 V9 |( ]
    " q/ X' K/ M8 M$ K" K
    res2 = df.Grade == ['PhD']*df.shape[0]
    / m0 i9 A7 }/ i. f) @
    " |1 G$ X* m3 `0 G) zres2.head()
    4 I) Z& i% j3 OOut[33]:
    0 w9 W* H+ f" X# c0    False5 _- H1 l. W( {9 m, H
    1    False
    9 p- F1 B  {& l" t) w2    False
      j% m' U9 V4 I. ~+ }3    False# p% C8 j! d1 R' y$ V& o
    4    False/ u0 ?. O% ^& [6 r6 H! |
    Name: Grade, dtype: bool3 D  X8 y# g4 [: l) x7 _8 G. i: N

    5 d  {; q: o& A  D8 l$ O6 ~res3 = df.Grade <= 'Sophomore'
    7 f, {, D$ C# W1 c) ]+ T" p4 e, n2 S/ Q5 C" s$ w
    res3.head()  M& R) K- r# P) u1 i
    Out[35]: 5 p7 h6 S- E% T$ R# D4 G, [
    0     True
    $ F# [0 l2 R7 l' ~/ {- \1 C7 i/ I1     True8 h- n9 v' o( J3 W! T' R! Z
    2    False
      q1 b6 U+ ^. b& I* q7 _3     True4 o' t; Q4 P6 N. o/ ^: u1 |
    4     True
    * g8 R0 g' R  A9 {1 z6 [) Z. BName: Grade, dtype: bool
    , W( U8 I! P9 E& h& m( u% E  P1 T& K9 D9 O  I& u
    # sample(frac=1)表示将序列随机打乱。打乱之后索引也是乱序的,直接比较会出错,必须重置索引。& |( k1 Q( C: s: T2 h6 W
    res4 = df.Grade <= df.Grade.sample(frac=1).reset_index(drop=True) ) w& M2 t" r, r" |% X0 u

    ! O* _! H/ A+ q) ]" o8 R# Tres4.head()
    , n4 t9 @) m! P) J% d% {1 x; iOut[37]:
    ) U+ {, E1 W  y! \0     True
    1 }/ ~" Z- r3 |2 k1     True
    ) |& \5 i( l# n* j" A2    False
    # X; h; T7 T  W1 ^8 C3     True3 |# j2 T- g* S* D4 I( \4 a" \
    4     True
    % U0 |5 ?* O* l8 `Name: Grade, dtype: bool
    : Z& q3 R! c+ D4 k# r0 e8 _6 ^1 m1 ?$ g8 s0 u
    1
    3 }0 o6 H/ U7 S. l6 t2! r# C) G1 k& D, _2 E# f2 p( T1 }
    36 x, h5 T! }: q2 z9 p* b
    4, L9 q( Z& T; o3 X. V' I% j
    5
    6 S% Y6 ?& l# U0 o6
    & u+ n3 c; f' w4 Y7
    , }9 `8 _/ f/ F' o! ?7 a85 ]4 e0 w! |" k/ J& ~) J- v0 r
    93 d* F0 ]# c' s
    10$ K* x" ]7 k7 u9 `' }
    11
    0 x5 o5 }8 J* m& `: ^: h" ]( G; K12
    - F, h* O: d6 u2 N: Y2 I# B' d/ m13
    ! t1 b) U1 O2 J' L14
    1 y7 v% h9 E5 K" n8 s157 ~& G! i3 m8 L" x5 z0 [
    16
    6 ^) U* _  J' d% M  p( C177 c" }$ d2 a5 l, b4 @+ u
    189 j$ V, `0 M% A5 f
    19. c& w+ ], U) C; a
    200 X) }" B9 f/ x
    21
    4 ?, G( F; x& O' ~6 ?, \. B" T22" Z" @( E+ H) c+ P6 G* l4 e& L
    23
    ) C6 j: y: ~$ h, @" D- s) R7 j242 R- y' t' |6 n; Y
    25
    & Z5 j: x' H6 l" {7 x  j, w5 x% h261 M9 t, A9 H% S. U- T8 F
    27
    : l8 q4 I4 ?: r: H6 a+ z0 [28$ @4 ?) V% S- Q9 \4 y9 D# ~) k
    29
    8 G9 b$ B* W& o# G4 q- ^300 X( H6 E& m1 y" E4 r
    31* I, p. S3 x$ Z% {6 T# K
    32! V6 ~+ t7 J, |0 C, w* C( t* W
    33
    - K  b. r, d  X& y0 f3 d34
    : `3 L% W1 u; z. O* i3 d4 |9 L/ e35# M: O+ V: t  X8 D+ U; Q- I* B
    36( w( d0 M* }1 P1 y
    379 M+ v8 X; E3 j. t9 X  U; {
    38
    & v  K9 J& i5 [0 c( @0 r. \39: _" s/ E1 W3 A6 X- g5 Y8 i
    40- j# Z2 A% [8 X9 [
    41
    4 Y% o5 K& R0 n* ^/ r" u42- i0 B+ R7 Q# h7 Z2 c/ o
    43
    - t, {3 Q. w' A* |' \% w/ o44
    ' s. C' T% F2 S3 Y0 D9.3 区间类别
    : b6 g  u+ p1 w/ F' b; @7 |, t: ]9.3.1 利用cut和qcut进行区间构造
    * ]5 ~& |, P" j# L6 ~  d$ k  区间是一种特殊的类别,在实际数据分析中,区间序列往往是通过cut和qcut方法进行构造的,这两个函数能够把原序列的数值特征进行装箱,即用区间位置来代替原来的具体数值。: o3 ]5 Q; ?* m. r* {7 x7 A7 Q9 _
    & h- _# S; b: M' W
    cut函数常用参数有:
    1 A) s3 }# N( Ubins:最重要的参数。
    % o* h0 o$ N' `5 X/ M如果传入整数n,则表示把整个传入数组按照最大和最小值等间距地分为n段。默认right=True,即区间是左开右闭,需要在调整时把最小值包含进去。(在pandas中的解决方案是在值最小的区间左端点再减去0.001*(max-min)。)
    1 n* `9 ~) Q1 r1 t& h) Y: n3 R# s# f+ V也可以传入列表,表示按指定区间分割点分割。
    ! u* Q- h; E3 C, a# V  如果对序列[1,2]划分为2个箱子时,第一个箱子的范围(0.999,1.5],第二个箱子的范围是(1.5,2]。
    ( y9 ?1 s: m0 u: `0 L. a  e, s& p' @+ r  如果需要指定区间为左闭右开,需要把right参数设置为False,相应的区间调整方法是在值最大的区间右端点再加上0.001*(max-min)。5 d3 B* o4 v. @. w- u) L' ~

    ! L) n. {, H3 t6 Es = pd.Series([1,2])
    * Z; [9 x2 S$ I8 W7 w6 g" E8 b4 p# bin传入整数
    4 d! E8 j/ ^7 ?; C
    2 Q/ t- @" c- m5 d, q: f1 _) Kpd.cut(s, bins=2)
    9 a% R! v/ W6 s. N. L6 ^Out[39]: ) A: A& G  ^9 r5 {  \
    0    (0.999, 1.5]
    - l9 }/ f! g3 F4 C3 z, ~1      (1.5, 2.0]
    & B3 ^: f( d4 o" m5 X: Adtype: category
    - f/ E* C' v) Z, NCategories (2, interval[float64]): [(0.999, 1.5] < (1.5, 2.0]]$ m* x3 E3 E# d- G) w  `
    4 f% f- }+ W. i1 _. L, K, Q' X. o( X
    pd.cut(s, bins=2, right=False)! A6 ?2 n$ R- l- U- H4 h
    Out[40]: / v1 f2 R( K) ]) U( C/ b5 ^
    0      [1.0, 1.5)
    + S7 U* {7 M% q1    [1.5, 2.001)
    & q/ A# A3 p6 w' `) Edtype: category
    - C4 R- ?4 h$ RCategories (2, interval[float64]): [[1.0, 1.5) < [1.5, 2.001)]1 P9 w+ }( x4 w6 S2 c  ^/ G+ Y( G
    ) M  ?) O3 w* U, |" R% F

    $ a! k4 J3 P  P# bin传入分割点列表(使用`np.infty`可以表示无穷大):$ u" n/ t; r; a) i7 @8 S+ b2 C
    pd.cut(s, bins=[-np.infty, 1.2, 1.8, 2.2, np.infty])
    $ v: \! `$ Z6 I! w4 pOut[41]:
    8 S! p; ]6 c# G0    (-inf, 1.2]# ]- m9 w1 O: I) W
    1     (1.8, 2.2]
    . \7 o6 p5 c+ n( g$ Vdtype: category
    # Q5 i& ^7 z( XCategories (4, interval[float64]): [(-inf, 1.2] < (1.2, 1.8] < (1.8, 2.2] < (2.2, inf]]7 N: [3 g  M* }9 s: \( [

    + [3 V" S, d! M& y+ w16 K# r2 M- F. y- x9 ]
    29 c& L5 ?, k5 b! f* w( o# {- P
    3
    ( z$ \( A( ~2 ~3 h. A. @2 k1 w" K4
    1 B6 g% V6 D* \, z0 u5
    ; ?5 M2 \- _( h0 y+ J+ j+ ]6
    - |5 N  f4 w. R0 _/ }3 z8 I7
    - M+ L$ u$ U' `% k% [8
    4 a( y7 n4 O) u  q8 ?8 w; L9
    8 s$ _& b" u7 |10& U+ J% f" s& J3 q6 X/ o0 m7 k
    11: B, R6 w% K5 m2 W# r4 L
    12
    ' z! ~4 Y+ j4 q/ p3 a* q( _13
    . w. L8 H' W: F$ B9 \/ @  a14
    % C( X6 U. z% h+ L5 i+ G" e7 d15
    - j" P+ t6 X2 @' G* I16$ k9 T3 V" F: t' e5 h& J+ f# p
    17
    * V: {) ?( E8 X. P189 ^$ V2 z3 F. y5 w! k/ }- {
    19
    0 n- g- p0 e* ^2 n. ]( ]' a  |20$ R; q+ b9 E* _4 Q/ _
    214 U9 B% X6 t3 R- I/ i4 |& U& X# p1 R1 I
    22( k: ~% \. _* g8 {1 }
    238 W+ q2 {$ B7 c
    24, w! c; t( {2 F2 Z$ c, e* x% |
    258 A2 ]. o: |+ w! |- E
    labels:区间的名字
    6 N' E$ j$ e$ J& p& z3 d  Oretbins:是否返回分割点(默认不返回)
    # p8 l- P2 ]- e/ S8 O7 d0 C默认retbins=Flase时,返回每个元素所属区间的列表: m; s! Z- A2 Y
    retbins=True时,返回的是元组,两个元素分别是元素所属区间和分割点。所属区间可再次用索引取值. ?2 Q7 H% `/ `7 w: @- t9 X

    8 I6 h1 C9 a* r* t5 us = df.Weight
    + `# B1 m+ U6 E' C( m, Vres = pd.cut(s, bins=3, labels=['small', 'mid','big'],retbins=True)
    / x. X: P" _4 f1 `& ]: Mres[0][:2]- G5 L- p  ~& v: g% o" a& ^8 P6 i' R

    + F9 v8 L) l) kOut[44]:
    3 S  W* r6 [2 b- z( y7 z0    small
    6 U' S6 d2 `' o, w! W1      big
    : t. X9 ^- @8 c" X5 W. |dtype: category
    / g4 p/ a7 w5 L" TCategories (2, object): ['small' < 'big']3 Y  F' \( l) K! c( a8 d" s$ n

    ; Y$ \, O  ^" Bres[1] # 该元素为返回的分割点
    9 q7 F( ?% }# T$ t/ }1 d. POut[45]: array([0.999, 1.5  , 2.   ])
    / Y" w, l0 t- U4 C1
    8 G8 F3 P+ _( M4 D2) h7 [7 Q7 Q! n# W
    3
    1 [9 C' d7 ]$ `7 \4# U0 I$ b0 r7 |  x
    58 a' V( p1 Q: Y( L6 u
    62 Q% Z* m8 o5 C6 e
    75 A" E8 _( g2 I/ |* P& l+ r7 d
    8  H  }, e' m+ j! x
    96 J% Y$ {- `' `% A+ G
    10: e+ r/ y0 ?0 Y* i2 `( M4 z
    11
    1 V6 e9 H( M: r125 U' ^' y( L/ \+ Y' U2 s
    qcut函数。其用法cut几乎没有差别,只是把bins参数变成q参数(quantile)。
    8 o! n& U8 c, v1 Bq为整数n时,指按照n等分位数把数据分箱& V) S; U& h4 `; M
    q为浮点列表时,表示相应的分位数分割点。
    8 w( D  q# e2 P) Ts = df.Weight
    / h9 C6 s7 J% K" i& E: h; R. H3 ?
    pd.qcut(s, q=3).head()
    + y9 S& r, j9 \Out[47]: ; B! A3 {, N+ P5 u# ]8 q6 ~
    0    (33.999, 48.0]
    : y$ R. \. T, H/ [2 g6 E1      (55.0, 89.0]
    * {1 @, s0 T) e, W+ f* a( u3 k2      (55.0, 89.0]
    * `0 a1 n! U+ v5 f# D7 q& J4 C3    (33.999, 48.0]
    ! e+ r, q/ q* G- C  u3 t6 p4      (55.0, 89.0]
    ( h$ D4 f& U" {; m9 Y: UName: Weight, dtype: category- k; R) q1 G) g6 D2 g1 v
    Categories (3, interval[float64]): [(33.999, 48.0] < (48.0, 55.0] < (55.0, 89.0]]
    & H1 K( _  C# q( q
    5 R: P  g! g! Y1 g# p5 cpd.qcut(s, q=[0,0.2,0.8,1]).head(): a. v, K/ Y& e; X% Z. M6 s  ?
    Out[48]: ; ^, x+ L  G4 I5 N3 o
    0      (44.0, 69.4]  v" T* X" @: u( H5 c8 i
    1      (69.4, 89.0]
    ; q2 U& c  k, j2 x  k2      (69.4, 89.0]
    2 [( T: t( R0 Y3    (33.999, 44.0]
    ) I, c1 a6 o0 c2 r( _7 |( ?4      (69.4, 89.0]
    : [2 s& h* t/ R% z. PName: Weight, dtype: category
    * L: F! {6 ?* Z) Y: ?( `Categories (3, interval[float64]): [(33.999, 44.0] < (44.0, 69.4] < (69.4, 89.0]]$ ?( ~7 [3 p& [; [2 ~4 ?
    9 n2 Y% f! K' a1 p* ~0 `
    1
    # B0 T, k+ X2 O+ y26 `( J( x8 Q( r- F6 M! D
    3
    : M$ _2 G, f9 U7 j/ d% p; H+ K2 U4
      }+ ]/ r* L0 x9 Y: F( c7 y  n5
    , |* M4 P) x" q& J* Y) B$ r) Q6% R+ q5 C1 F2 g5 I( |
    7
    - r" h6 e% j9 W  T8
    5 O, E/ ^. n' }8 W0 }1 X97 g  [5 \2 p0 ^+ d( j7 V6 N0 @
    10( M, Q, _' A7 @! |
    119 b8 b# s: X* v6 j
    12
    5 r8 O+ _& B8 `% b% K" p* I13
    1 g3 B# E' t+ R0 d9 B9 U7 L14; c. Q( {! O; o; [- n% c* D
    15
    : I- N' e5 H9 F, l! B6 t9 Y3 k16' F/ z' v# {9 x" s7 g' E: H
    17
    $ X7 S/ Q6 `  w( Q% |5 x% m# g- }2 q18) q+ ]7 Y0 {, H# w% f# o
    19/ Q" p" j# ~4 T4 m0 S' F8 R
    206 Z0 D. M: |: Q% x
    21
    / O( i) i- O: L9.3.2 一般区间的构造( }* X( a  |8 t. {( i3 |4 Y
      pandas的单个区间用Interval表示,对于某一个具体的区间而言,其具备三个要素,即左端点、右端点和端点的开闭状态。
    ) |# X2 G1 l! y1 K
      n2 E! G' y! T开闭状态:包含四种,即right(左开右闭), left(左闭右开), both(两边都闭), neither(两边都开)。
    $ M4 q9 |4 m' z+ s' Bmy_interval = pd.Interval(0, 1, 'right')7 L4 n4 {8 Q; S4 H1 X1 @
    0 m6 ~' J" f0 p8 A0 ^4 u  L5 s
    my_interval7 {, ]& p( N7 c* b# o  u4 ^. o
    Out[50]: Interval(0, 1, closed='right')/ g+ i2 G. R4 |
    1
      [5 s* O. x0 R5 @, ]2
    , |0 z, R+ T, Q  v$ Q# m3
    1 i; F2 g% c, d- ]4; }; D' |! L1 Q# q3 A! o
    区间属性:包含left,mid,right,length,closed,,分别表示左中右端点、长度和开闭状态。
    0 n, m! p! {5 I) i使用in可以判断元素是否属于区间3 ?9 y" n, s7 g) j6 \( ^7 P
    用overlaps可以判断两个区间是否有交集:
    0 v* [9 j" H6 J6 z, r3 G0.5 in my_interval
    , ~; \+ K& [  \( X: c; b6 L, q  Y+ V1 ?* d6 O
    True
    7 `; N. m+ h! k; X4 W1/ |% j+ u9 t4 R7 ?' D% `
    2; J" f  F& ~; Z" P- c; w% K8 U
    3  h: v4 f( b4 G/ [0 J. A' ^" m
    my_interval_2 = pd.Interval(0.5, 1.5, 'left')6 E: D/ b. u' z) m2 d
    my_interval.overlaps(my_interval_2)
    / \5 r; e. h- A3 _+ f$ Z- h6 i) A# S/ N1 ^8 i, d( m! O, m* @) e
    True
    3 L3 ~# P- i: a1 M9 `5 G' Q' h8 _1
    " F, y2 S# H1 T2
    ; V) h8 {' j: [* e" y) K. T+ g3# o/ b9 D# \+ i' g) s- b4 X
    4+ z9 D% w% g, c3 [
      pd.IntervalIndex对象有四类方法生成,分别是from_breaks, from_arrays, from_tuples, interval_range,它们分别应用于不同的情况:- U$ f6 j( b( D; i) m- t
    , t4 q9 d' [0 Q7 V# n6 d
    from_breaks:类似于cut或qcut函数,只不过后两个是通过计算得到的分割点,而前者是直接传入自定义的分割点:
      b! k) _( v; g: }$ y7 m' jpd.IntervalIndex.from_breaks([1,3,6,10], closed='both')
    ( y2 q8 H% @4 h; ~* y* ?
    5 u( o( S  }: D; fIntervalIndex([[1, 3], [3, 6], [6, 10]],
    ' e8 @4 E  W" e) k' h) ?; ^               closed='both',  V2 ?" N% N& P" T
                   dtype='interval[int64]')
    9 ]' D5 L  C2 {: y9 W1- z: w$ l# S$ U! l. V+ |3 p4 @
    2
    ; E7 c! Y8 a) s1 B- E% P, c3) V- j" D7 h, @, [+ G
    4
    # w, p; s8 k5 [& C3 Q2 E4 [' M) j5! P. S9 L, X5 s, R0 D0 J0 ~% R
    from_arrays:分别传入左端点和右端点的列表,适用于有交集并且知道起点和终点的情况:' P8 s- \( L$ e( M- `- H
    pd.IntervalIndex.from_arrays(left = [1,3,6,10], right = [5,4,9,11], closed = 'neither')) [+ f4 ]+ |# a- u; j) \- T

    7 w* |7 J1 }  I& h, y0 HIntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],
    8 q; t% v8 {2 J$ O                  closed='neither',
    " U& x3 b5 c6 h9 @! i                  dtype='interval[int64]')
    ! g4 }. [$ E' ]9 I0 b0 m1
    : r/ @  H, h9 b" Z  ?2$ ^5 t9 _2 {, g4 J, b
    3
    - x2 l5 h1 E2 b6 N4 ]4# l1 ], W  L  E
    5/ [! L& v1 f0 `$ O) |- {& d
    from_tuples:传入起点和终点元组构成的列表:
    : C- C2 ~4 C2 X4 \. X) M4 E  W3 Lpd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)], closed='neither')6 R6 @" D0 {8 j# Y# H" g
    $ p0 u# |  ?, b/ ?
    IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],2 e- |3 x- O" P, y' y& N
                  closed='neither',+ P7 R. H% K. l9 [/ q" H1 a. F7 N, O
                  dtype='interval[int64]')+ J. z1 K- I: k
    18 c- E% }. r  G: l8 h  Q9 {
    24 I- @- _1 W% e( E4 ^, n
    3
    ! R" b1 F0 `- z4  i. w9 @+ f8 j% ?- F4 J* D1 c& z
    5
    * |8 ^' f# w5 n/ n- ^interval_range:生成等差区间。其参数有四个:start, end, periods, freq。分别表示等差区间的起点、终点、区间个数和区间长度。其中三个量确定的情况下,剩下一个量就确定了,从而就能构造出相应的区间:! r3 j# ?8 C0 |1 n& N: h
    pd.interval_range(start=1,end=5,periods=8) # 启起点终点和区间个数
      r( |$ \% L/ ]6 O) }Out[57]: , e! s# R( [9 [1 J7 N; s
    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 l* X! i  L5 Q
                  closed='right',
    0 }1 _$ H$ a6 C  r5 i              dtype='interval[float64]')
    7 w. d/ h1 s" O, N( v9 M9 J, f; y4 C: |, e4 r2 y, T* g+ V
    pd.interval_range(end=5,periods=8,freq=0.5) # 启起点终点和区间长度
    9 h* w# q; e( @. e' f9 o3 t! KOut[58]: $ x( m5 }' U% s1 H
    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]],0 w( o% _4 V8 s1 d
                  closed='right',
      {+ l5 w7 V: |9 K              dtype='interval[float64]')
    9 g6 P9 q0 B, x1
    : K0 q' `8 Q5 r2 l- i. e; B2$ C' Z7 p$ s3 S6 K/ R
    3
    3 B; v2 \8 ~* F4
    ) y% q. R8 a7 p$ S' S: z5
    , N' R! c5 A, J7 k8 w8 B$ O6
    / t+ b% t( I. a# g- K/ O& Z7
    : v' m/ N  a' q" v2 ?- G0 ]8- w" @; A3 M# I( u0 W8 h. D
    9
    ' G& G) U6 i7 ^* U10: T1 m* u6 f  D6 D
    116 H, U7 \" Z9 c* o% S
    【练一练】! _; l0 N3 y" M5 x2 P6 n2 D
      无论是interval_range还是下一章时间序列中的date_range都是给定了等差序列中四要素中的三个,从而确定整个序列。请回顾等差数列中的首项、末项、项数和公差的联系,写出interval_range中四个参数之间的恒等关系。
    3 _0 ~. L) D6 K5 X: G7 g) l# m' R* u" w( i4 t8 r
      除此之外,如果直接使用pd.IntervalIndex([...], closed=...),把Interval类型的列表组成传入其中转为区间索引,那么所有的区间会被强制转为指定的closed类型,因为pd.IntervalIndex只允许存放同一种开闭区间的Interval对象。- g! J) D) E5 R
    ! d" L. R# c( j* D, `( |, m
    my_interval0 b# p( @$ m+ Q7 @+ i; S
    Out[59]: Interval(0, 1, closed='right')! P3 h4 f. o( G
    7 ^  O4 ~! [' C1 W" _; f6 ], ?
    my_interval_2- @4 N9 i+ V" `$ R" x8 \
    Out[60]: Interval(0.5, 1.5, closed='left')
    5 D; w) ~  Z1 v3 _4 ]/ e& r
    3 E5 ]3 }2 U; hpd.IntervalIndex([my_interval, my_interval_2], closed='left')) F5 Q: q% _% \# ~6 Q  A
    Out[61]:
    0 ?. S4 B1 T9 b3 m+ N+ ^* aIntervalIndex([[0.0, 1.0), [0.5, 1.5)],5 V; r% A- q% R- }5 }: a
                  closed='left',
    + E7 n* N7 a3 M              dtype='interval[float64]')
    ! M& v2 H$ t, h! V9 y1
    7 i: s3 B; r0 {( ]! R2
    8 j9 N/ }$ |7 T$ B8 n35 q2 H$ j1 M9 g5 U+ [
    4
    ! r: V) s6 D2 W- ?3 r5
    5 Y) P' ?# g0 c3 H8 d- v% [" o6/ ^% M( S! ]# e
    7
    5 n) Q) x5 n3 S" u8 _- T8& _) p3 D& ]- Q, W1 e
    90 c3 r6 M5 Z0 U! h/ ?
    10) R# o9 e- z( Q* d! J% |( a, e
    113 z3 B3 z% g- ?
    9.3.3 区间的属性与方法, `1 Z1 v. {$ i: Y, p5 B
      IntervalIndex上也定义了一些有用的属性和方法。同时,如果想要具体利用cut或者qcut的结果进行分析,那么需要先将其转为该种索引类型:
    1 ~! z2 ^2 l% C0 C
    9 X1 t& {0 n# @; y# j) ^s=df.Weight  f: h/ k$ K( B" z
    id_interval = pd.IntervalIndex(pd.cut(s, 3)) # 返回的是每个元素所属区间,用具体数值(x,y]表示
    1 K* c+ q  v8 a( A% `- n# M9 K& Pid_interval[:3]
    . W0 h2 @+ Y4 [. f0 `9 t! Q: M0 a
    1 h% c8 s3 ]2 nIntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0]],/ N) o8 l+ W2 D8 V) b  R* ]
                     closed='right',8 [5 o! U9 j2 ]% J
                     name='Weight',( I3 p$ p' X; c: m4 ~
                     dtype='interval[float64]')4 r- _1 s1 H; o
    1
    2 g3 V& I/ X& g4 A5 B2$ O% w7 |8 |8 o! c+ x
    3
    % [$ a  e" z" x0 K; C4
    ( p) r  \) `% V" l5
    " N$ j; t* h* p7 |$ _6
    * d" I" V1 m+ g: g0 Y7
    - s" j4 @+ W2 _; J8
    1 i' v* i: O+ X1 N* i" Q# [与单个Interval类型相似,IntervalIndex有若干常用属性:left, right, mid, length,分别表示左右端点、两 点均值和区间长度。
    9 |2 L' ^' {6 A# |id_demo = id_interval[:5] # 选出前5个展示
    6 J& F! f, W$ H% s
    + |. x0 M1 V- u7 W  bid_demo
    ; g" C& ~7 _+ v  b2 cOut[64]:
    ; O) o1 }( B" |# ZIntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0], (33.945, 52.333], (70.667, 89.0]],: w+ t' K* S  D0 |, S
                  closed='right',
    ! d3 ~8 D4 X( \! K1 y              name='Weight',
    2 H9 _- S) Q3 N              dtype='interval[float64]')/ f: a+ I0 Q% [) T

    : U- k0 C7 H8 [8 H2 o! C! |+ S8 Sid_demo.left # 获取这五个区间的左端点, p; i% j$ C. k0 Y* _% Q
    Out[65]: Float64Index([33.945, 52.333, 70.667, 33.945, 70.667], dtype='float64')" v) F/ @$ ^' e6 P: P

    $ y4 N" r, z! e" S0 I* D) l3 [id_demo.right # 获取这五个区间的右端点- C0 v( y* A" s9 {$ \
    Out[66]: Float64Index([52.333, 70.667, 89.0, 52.333, 89.0], dtype='float64')
    ( h5 R  m: _, Y4 _8 s9 b9 ?& m7 j0 O, w4 g  X/ K$ z, T$ A
    id_demo.mid
    & i1 `+ l/ H' L1 V' l' gOut[67]: Float64Index([43.138999999999996, 61.5, 79.8335, 43.138999999999996, 79.8335], dtype='float64')9 R& n6 O" w! H8 v8 ?

    , k0 n- G2 _# Gid_demo.length
    + Y; K! W7 q# N& I, j7 N* iOut[68]:
    ( N6 I9 p  c( @3 A& dFloat64Index([18.387999999999998, 18.334000000000003, 18.333,
    + W8 R9 v# ?9 |. Q              18.387999999999998, 18.333],# m( u. O. ?' y( c7 ~$ o( M5 q
                 dtype='float64'); Y0 D3 Z3 Z1 ]: G2 L9 j# X/ C

    * D7 U$ h2 G: X" m' V% O1  K4 K, Q; P1 a) w, s
    2- t* l6 |8 F1 b) R" O5 a$ t
    3- u  I- N9 K3 M& e
    48 r/ j3 b* _2 g3 [5 o
    5# Q* h" D9 j4 U) |
    66 O  A/ ?! X% c% Z& ~& Z) D* @
    7
    % ~# v( p, }) }4 N8 d2 z, J8
    $ A, T3 T! o/ D, b3 k9
    4 d: n& v6 X! `5 C& F/ B109 m& F% Z5 X& G7 ^( \
    11
    7 r# I+ B: b: W6 b1 c127 v1 ?+ _0 V2 o/ F) I: [
    13- F) e* Y1 _0 n+ O6 Q& a
    141 x5 F2 I$ ?2 Q, l! K- Z, C/ m" L3 u
    151 i: `: _3 ~; v- ?: \8 v
    166 T/ l  b5 u0 d- }) }- Z- P7 Z5 M
    171 x6 e& v2 Y7 V" a2 }
    18
    2 H, s/ V$ N) c' j$ \7 @19
    $ j& ]8 Y; s6 T+ O20# z% X) M; N) r9 ~
    211 Q! Q4 m  T7 L( c
    22+ l3 B8 z- D; M
    23
    0 A+ W/ O- o: D" C1 t/ Q$ `IntervalIndex还有两个常用方法:/ x$ d- v4 ]. [; s! F% B$ Q4 S
    contains:逐个判断每个区间是否包含某元素
    , R  O0 ?! q0 ]! m) f0 s* Qoverlaps:是否和一个pd.Interval对象有交集。
    . I8 \- ~5 r6 w$ m% sid_demo.contains(50)
    - @: z6 w( R& t, gOut[69]: array([ True, False, False,  True, False])4 O+ E( K2 J- X2 o$ m
    ) f& H2 y9 q+ R: O
    id_demo.overlaps(pd.Interval(40,60))% T1 T" M% a& K1 G0 J  y! V% P
    Out[70]: array([ True,  True, False,  True, False])
    8 z/ w$ I  T" `( {' p" U  o1
    . `# t* _/ r6 p0 H2
    6 y5 H1 F8 q* x7 d( ^  k38 N/ g6 G# Y/ a, M; Q# r7 d, x
    48 c7 X+ B6 e( A/ a$ K. E5 e+ u
    5& @1 A8 ?" \8 p% s
    9.4 练习
    % t4 Z' \& z! _8 m2 HEx1: 统计未出现的类别
    ! O: _& p+ c, u% |! G  在第五章中介绍了crosstab函数,在默认参数下它能够对两个列的组合出现的频数进行统计汇总:
    % X/ e: N+ Y: o7 F( p: f, \0 Q$ y# a
    df = pd.DataFrame({'A':['a','b','c','a'], 'B':['cat','cat','dog','cat']})
    7 V1 Q- \. I$ r. @% Gpd.crosstab(df.A, df.B)
      |. h+ V- L. @4 z, E% g" {: a0 U, F( z( M& `) `8 U5 ^- S
    Out[72]: 0 ~2 n9 I% @9 v! H
    B  cat  dog& x% x2 C9 L& c( _4 A
    A          5 \5 P+ `; \+ w* W! M! S$ }
    a    2    0. K6 o4 t+ H6 ~$ J
    b    1    0& C/ b' w; b) Z, _/ u" r, B  U' Y
    c    0    1- j/ ^; E" v) C1 j0 R- }5 D& [5 q
    14 K8 ^+ p0 {5 [9 r2 [# E+ w$ V: l% t
    27 [! U. Y' ~: U; E
    3$ u# X* _' I$ W( {4 ^
    43 F/ V4 Q3 z4 U5 _1 ~- n
    5
    1 s4 n' b+ K5 E6
    8 w7 R- _1 g: K8 ~& k. R7
    ; [* b# E$ l; W8' i* T/ `* U/ w- C8 `8 i7 g
    93 q- W# V; }3 T' K' a# y$ l+ M/ K: n
      但事实上有些列存储的是分类变量,列中并不一定包含所有的类别,此时如果想要对这些未出现的类别在crosstab结果中也进行汇总,则可以指定dropna参数为False:
    , {1 I/ f, u" j
    2 {/ ]$ U, ^3 C! idf.B = df.B.astype('category').cat.add_categories('sheep')2 [- b% f+ _/ ]( h
    pd.crosstab(df.A, df.B, dropna=False)
    * k* d3 i6 G1 `, w, V! m
    % Q2 A2 H" V3 P& j0 h" |Out[74]:
    7 B2 v0 q/ P2 G4 c5 M" T, kB  cat  dog  sheep
    , e# s& y/ g- v8 M8 ]" c. PA                 
    ; ^: L, n4 R% J, i9 F+ L5 a4 a8 Ua    2    0      0
    * @, u  s0 ~' J; Z  i( q; }b    1    0      0
    " Y/ s4 I7 W+ y, ?7 q. Xc    0    1      0
    + }5 R4 x4 z0 T# E& P& I/ ~3 S6 i1) p; v, S- @  s
    2
    : P" V) p1 o5 y& ^* O3
    6 R* t8 @7 ~& g+ u4
    . D  Q9 |# ^  v( ?! y0 ]4 g5$ g/ H. W, N2 N& n
    6" E( P9 g2 i. C, @
    7, c* e: ]4 z, N" A6 o6 K
    8
    " z3 _4 Q) I' @; Y! d, X9
    0 X7 D  g( r& r3 ]0 Q请实现一个带有dropna参数的my_crosstab函数来完成上面的功能。
    3 d8 L& K. k* D( n+ e: |* H* Y4 F9 P6 w# S. C% W: _4 e6 G
    Ex2: 钻石数据集
    6 _8 m8 Z4 l3 U$ a; ]4 {  现有一份关于钻石的数据集,其中carat, cut, clarity, price分别表示克拉重量、切割质量、纯净度和价格,样例如下:  \3 g/ o+ t1 ?' T
      k. {, Q) a! s+ {4 C6 u' k1 y# f4 f
    df = pd.read_csv('../data/diamonds.csv')
    # w3 [2 e+ S1 j. f0 r, d1 Cdf.head(3)
    / C+ k- o1 o& h2 g  ^! W% K! O- V9 {7 K7 q) y$ Y
    Out[76]:
    + Y. M6 s$ m" W3 I! Z   carat      cut    clarity  price- ~- [. P( b! B% e# H
    0   0.23     Ideal     SI2     326
    " o8 S5 S% X# z5 v  b/ e+ y1   0.21    Premium    SI1     326
    % H7 l% |1 v- N  n4 N: z2   0.23     Good      VS1     327
    $ L9 l) n3 \: y* h. v: F- J16 d" l* }& l0 \2 e. q- f4 }
    2& |+ {$ j: R* `( e- r! W
    3
    . _6 g# W; m0 l+ V* R4
    7 _: r( c* w/ E0 f2 h7 h5
    8 f3 l6 q. A4 h1 ?) N' n6& f6 [. I  ~& R4 D- c. D0 R
    7
    ; v) {# C3 l+ F0 q$ `. ~/ q; a8
    & Y! R* Z' w2 k5 g) k4 f! P分别对df.cut在object类型和category类型下使用nunique函数,并比较它们的性能。3 [4 c5 w3 J; N9 s7 J# T0 T) ]
    钻石的切割质量可以分为五个等级,由次到好分别是Fair, Good, Very Good, Premium, Ideal,纯净度有八个等级,由次到好分别是I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF,请对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。4 Y: x8 z* a4 {( G9 h
    分别采用两种不同的方法,把cut, clarity这两列按照由好到次的顺序,映射到从0到n-1的整数,其中n表示类别的个数。: r* G, ]* p( X+ u6 u' f4 K
    对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。" U) C2 G# w" ?' j6 \1 O
    第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
    5 ^; y% t" N6 ]/ u+ G) w对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。
    / j0 F! [/ N! x: \, a先看看数据结构:; m7 |7 u3 h3 z4 Z5 ]3 i: ]! ^

      J( [+ F* Z+ [9 q3 Ddf.info()! y; p) \( N5 x+ F; _; x! {6 e3 f; I. j" ~- C
    Data columns (total 4 columns):
    : q& H) @0 z, G, Z: J #   Column   Non-Null Count  Dtype  
    7 v) `4 r+ [$ |: B2 i---  ------   --------------  -----  # x$ l* a, M3 D) Q! W% C! D$ s
    0   carat    53940 non-null  float64
    ; r; y( p- d4 l 1   cut      53940 non-null  object " T, p/ u1 K! k; Q% u: u8 \: j
    2   clarity  53940 non-null  object
    2 w& B" n" J/ P 3   price    53940 non-null  int64  
    " O2 o$ g4 W& @- I6 S5 Z2 @" C- Pdtypes: float64(1), int64(1), object(2). D" H$ X& U. V* S) }0 J4 K7 z
    1
    7 u1 k8 k/ [4 h2
    6 Q  e; ^4 R; ~) |2 @' X36 _6 e* L" `( l
    4
      w) o1 ?4 c  V% Q" Y1 k& w! U55 n1 O  p" N+ ^$ D9 W  g# h- e2 M
    62 d4 B. p/ p2 V
    7
    0 V7 C7 _# ]* p- }* [0 Q8
    6 ~0 d( ]: H7 o9! E% m& [' N7 i5 X9 s# X
    比较两种操作的性能
    : I4 _) N# U# u%time df.cut.unique()9 v6 f. A% v4 i% s& I2 A

    ' k) |9 B; g* {- m. P0 }/ p  z7 q: O; vWall time: 5.98 ms2 P7 ]) `0 }" v3 y& u8 i3 N7 J
    array(['Ideal', 'Premium', 'Good', 'Very Good', 'Fair'], dtype=object)0 [% r# _( C4 s  P( P5 [
    1
    . H8 H9 P; S! T1 V6 s2 c2
    ' A$ I3 N/ |- O( m3 M' l3' _: y1 e) N* I- p5 h/ {) m
    4# e3 q) r; y4 j$ y
    %time df.cut.astype('category').unique()
    ) y6 A7 @6 F& Q0 h& j
    * Q6 X* w0 h- Z7 x5 h: ?0 dWall time: 8.01 ms  # 转换类型加统计类别,一共8ms
    ! i5 y. T* G% w' ?( u['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    7 }; t" ]$ q+ ^5 @8 ?Categories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    9 S( Z- R5 N4 K! Q7 k" S1
    ' r0 H4 u- j# w' {" i2, H3 I* q3 Z; d5 P
    37 D3 {5 u8 B+ s0 ]0 S+ Q* n* }
    4" o- c+ H2 w' }/ S
    5: z9 N0 J# d3 G% c
    df.cut=df.cut.astype('category')0 Q' G7 L% [1 K3 R1 z
    %time df.cut.unique() # 类别属性统计,2ms
    & ?+ b9 g' I# f' \. j  N/ |9 i2 E
    ' t$ I: s4 D3 wWall time: 2 ms
      z7 H: I0 {5 J$ Q; G3 _['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']) L/ e5 P+ [1 O# O' M$ k
    Categories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']* k8 m5 M$ v* N! u" d
    1
    # Z+ q* c0 x) Z; F( k% B' a% P2' a7 S' k, g: Z6 Q6 V. A
    3/ B" r7 Y7 o2 }9 L
    4( k. e1 W5 @5 X
    5
    . B& p0 G" W' Z6& \3 |: V: V2 F: y. L6 J8 l
    对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。  O: z8 m" ]7 G
    ls_cut=['Fair', 'Good', 'Very Good', 'Premium', 'Ideal']2 `% E$ q* b5 J  B& W
    ls_clarity=['I1','SI2', 'SI1', 'VS2', 'VS1', 'VVS2', 'VVS1', 'IF'], \/ d$ s& Q2 K
    df.cut=df.cut.astype('category').cat.reorder_categories(ls_cut,ordered=True)  # 转换后还是得进行替换3 `* M- o& }, D7 h9 ~
    df.clarity=df.clarity.astype('category').cat.reorder_categories(ls_clarity,ordered=True)
    ! A1 L; Q6 G) x8 p* d& q& y1 `' `. p5 I6 v3 s! N' U" u
    df.sort_values(['cut','clarity'],ascending=[False,True]).head(3)
    ' e' C& h2 @% d6 K6 f8 C: J3 x6 }. x$ {: N
            carat         cut        clarity        price
    7 L. I; v7 h4 ]315        0.96        Ideal          I1        28017 l3 }* ]# m, Z4 M  v
    535        0.96        Ideal          I1        2826" i: B- c5 C8 v$ v3 G- [+ s2 ?# q0 W
    551        0.97        Ideal          I1        2830' k% ^9 x. P* s! d& c- v
    14 g/ P8 k5 X. [
    21 @* i- e8 g* K+ c. }+ i5 R* X
    3
    8 |# P! F" r9 |! {0 V  Y+ Z2 c4
    2 e  c, m6 q7 B! F+ [& A5$ r  V6 v* l7 V: b
    6
    % L7 d; y) b) a; B- @7
    8 I+ s) s5 a/ o7 c! i87 Y# R# Q8 \/ T2 C. p
    9) Y5 t' n2 d1 u% ?
    10, I+ h4 F! W; z  e; u2 [
    11& }+ z5 W& J" q7 b9 _' ]8 J
    分别采用两种不同的方法,把 cut, clarity 这两列按照 由好到次 的顺序,映射到从0到n-1的整数,其中n表示类别的个数。* H9 y1 I: X: E% ~+ A. ^5 Y
    # 第一种是将类别重命名为整数
    8 [% T5 m8 s$ r  S3 H) @  }dict1=dict(zip(ls_cut,[x for x in range (4,-1,-1)]))# ]; ^9 T3 e! Z+ W% h6 ]" ^
    dict2=dict(zip(ls_clarity,[x for x in range (7,-1,-1)]))3 y; a! K' q/ @& B1 P

    , B! ]8 d/ O" wdf.cut=df.cut.cat.rename_categories(dict1)
    5 m& B: m: y2 d2 G$ V" y  m0 q6 tdf.clarity=df.clarity.cat.rename_categories(dict2)$ I; D% W7 [) Z
    df.head(3)
    * d. [' g# _- y( m# F" l
    9 c! [* T/ t  q: Y. H# @        carat        cut        clarity        price3 m" a. w3 o: P' S1 D
    0        0.23        0          6                326
    4 z4 N! `( h  t7 ^, b1        0.21        1          5                326% z) m1 U3 a) j/ l& e  [( P
    2        0.23        3          3                327+ R) N" p7 ~+ b+ w# I$ l$ W" ^4 U
    1( k5 @) P/ `! p! Z, |
    2: x" i! G# {/ ]% d$ o
    35 W+ T) u  V' S3 _
    4$ K0 ?$ n. T' ^( v
    5
    ! T" m7 X0 x( Y6 [, `' r6
    7 u; u1 }9 d1 W! M1 x7
    " k  ?: r- v; H3 W- M- r8
      e( O1 I3 @, [" [( k9& w/ d; q5 `! q( b; c9 w
    10
    * y: y4 h/ u3 g) t) Z% m3 J4 i11
    ' \8 R. l4 P6 J# _+ i0 l$ @) g12
    3 R5 ^8 I% f/ O1 c1 e' e# 第二种应该是报错object属性,然后直接进行替换
    9 a) ?/ z2 M# W2 X) j/ u  ^- k+ f- xdf = pd.read_csv('data/diamonds.csv')
      b0 u, h  H: W  z# q. wfor i,j in enumerate(ls_cut[::-1]):( E, b" v; @( l$ P
        df.loc[df.cut==j,'cut']=i
    * H( U% E! F; i
    . X: Q1 H8 S+ D9 Z3 v0 bfor k,l in enumerate(ls_clarity[::-1]):0 ?' m- q! @, O$ S' P
        df.loc[df.clarity==l,'clarity']=k. l3 ^! R. D) X* _& q2 p; M
    df.head(3)
    , ~7 [* D2 j8 z# c: b% D. D. O# k2 m
            carat        cut        clarity        price
    ) m3 D4 n: _! m$ i1 m' }0        0.23        0          6                326
    & o' ~1 A8 X; s) c* o4 C+ |! X1        0.21        1          5                326. b2 \3 B3 H( @; X9 f7 x" z
    2        0.23        3          3                327+ Z0 o! G, u- }9 P$ W$ Q
    1
    * T6 m" S3 d6 o0 W. c2# I+ A" G3 l0 I+ Z- c6 c# c5 c& S
    3
    4 i! f1 c( o( g5 e8 m( N4
    5 m9 D% K1 R& j+ y7 Z4 D8 s59 a1 t7 v! r+ ?6 a# b
    6
    3 V) y4 Q- \, Z' q3 t+ p# q7 S/ |$ q7
    7 j$ ?$ G/ Y* B# g8 t% g* g# W1 s4 x8
    - y' x; ^* K: ~! t/ s; q+ t4 T3 z9
    5 h3 x+ v: u! S! C4 o) s; q6 n10
    8 v7 i/ s" Z* k9 b/ a! {7 K& F11
    ! G1 K; }8 [5 [9 j. X12
    7 p  J% e  O4 U; G/ _0 ^13
    ; s# B4 V) Q, Y+ b% g. ^+ M7 ^对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。3 M' R) j$ u) U) A2 p
    # retbins=True返回的是元组,第一个才是要的序列,第二个元素是分割点
    " H9 @1 j, S4 b& r" o* t% {avg=df.price/df.carat
    ) \0 q, f& [, z, y
    " g5 R) G3 }, k# }9 G7 X) S+ wdf['price_quantile']=pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],4 C5 D- L+ m) E: P0 j
                                  labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]
    4 J& ^2 N* a# _3 L: ?* ]$ m; ^# X2 {9 O8 b+ U0 M, t, i
    df['price_list']=pd.cut(avg, bins=[-np.infty,1000, 3500, 5500, 18000,np.infty],
    8 ^5 P5 Y, ^. v7 Y8 d                              labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]
    ) }# L- M. l( j7 b' Odf.head()
    3 n7 C7 O! B: S8 d5 Y4 c& o) E2 `8 X
            carat        cut         clarity        price        price_quantile        price_list
      L+ {2 X- {5 B7 h0        0.23        0                6                326                        Very Low                Low5 X/ a8 g8 n. o3 }9 I
    1        0.21        1                5                326                        Very Low                Low9 D+ C7 n  b7 d  }6 {; Y5 E2 J
    2        0.23        3                3                327                        Very Low                Low
    - k! B  j% `3 K% E3        0.29        1                4                334                        Very Low                Low- t' N0 k+ \, x. ^8 C% C4 `
    4        0.31        3                6                335                        Very Low                Low                                       
      D* F; Y7 ?5 v* [$ E
    1 ~6 |, B. `7 P+ M# h  u1
    ! k) G# l4 P5 C; A22 f# d7 c/ m8 [5 M
    3
    # i" L% x. o) C0 o! \7 L4
    ) G1 Q" T8 M- ^  t5
    2 D# B3 K" K6 w# w62 j: q, o* m7 n: ^; _
    7& e" n: @# f3 i! n1 z, [8 H
    8% z5 s8 d* @; @* N
    9
    4 q5 S$ j7 H/ X* f5 b% j9 ]# p10
    / R% e7 ]% L% }# s% L/ k7 I11
    % h( R' ~3 K0 Y$ _12& W  B' F% n- \8 ?! ], b  ]) z
    13
    - o* x& ~! p: P! i: f5 H& |, @14* |% O: O8 t) |5 p
    156 W6 @- l! t* F5 h8 j1 s  J
    161 a0 i% l" Q) y6 h) Q' E
    分割点分别是:
    6 l, _$ D! F- |9 f. [4 D& V) F; A8 f
    array([ 1051.16 , 2295. ,  3073.29,  4031.68, 5456.34, 17828.84])$ i, y2 Z) j: p2 v2 W
    array([  -inf,   1000.,    3500.,    5500.,   18000.,    inf])
    / {& P8 m( F& [" j8 d( j1
    5 P  y% g0 T6 r4 I' }1 W: F% ~, h2
    7 m: ^  J& T. k( j3 c/ z$ G第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
    2 t6 E! e, Q4 i  ~- Odf['price_list'].cat.categories # 原先设定的类别数8 H! o+ b3 |. [  c+ ^( f
    Index(['Very Low', 'Low', 'Mid', 'High', 'Very High'], dtype='object')
    9 O9 {" H8 Z+ K9 e, `, T$ A8 z) `; M! R! Y7 `" U! K
    df['price_list'].cat.remove_unused_categories().cat.categories  # 移除未出现的类别
    0 K& B. D9 ^8 n' c0 ]0 KIndex(['Low', 'Mid', 'High'], dtype='object')  # 首尾两个类别未出现
    - p/ ?2 }7 y8 }- Q5 r1
    ) f; O) L/ ?2 `  f2) |# ~: d0 z/ z! s; d; f( y
    34 a4 X! }. ~3 R! c. B
    4
    : X0 n: d7 r  q7 G. D, O* q5
    ( G& x/ P- Z# ?( Eavg.sort_values() # 可见首尾区间确实是没有的- p4 E, E2 j! M: H
    31962     1051.162791# w$ }% e6 b; F" k0 p" x' c
    15        1078.125000" E7 H. `/ L7 @8 t
    4         1080.645161
    * c" D' |; y% t1 ~6 q28285     1109.090909+ Q% M) B( l: L* J) g" Q& f
    13        1109.677419
    ' h7 w. t/ q+ p' U4 T0 ]/ @             ...     
    , V/ B$ \- `( f4 [6 Q0 [/ Z' \  R3 y* q26998    16764.705882
    6 R. w$ V9 P% p5 w# f/ h27457    16928.971963
    ( c, \, v0 U2 G% @+ a* J7 F) V27226    17077.6699032 B6 F: [: F5 J* n9 Q
    27530    17083.1775704 E, H* A  v, @" h
    27635    17828.846154: }. a7 d; }' g! H# K$ \6 \
    1
    / L% ^, `: K) Y3 d+ E29 j( Y; r' f' r7 T: x
    37 h/ u: [; ?4 `+ R
    4. K  ?/ [5 ~" K; c' n4 u' E
    5# n; I: ~1 z& M% U6 a* v0 R
    6
    8 B) Q; _' Y0 L( ]5 C+ t. n6 K7  U6 Z$ K$ |, Z7 ^8 E' N( }! d
    8
    8 U* B0 _& _. w7 l! p  n4 _9+ E8 `  G+ X! W0 B. r: P% `3 d, V
    10
    7 p6 J, D" D5 e4 `# d3 @  f11: Z6 D! E! C6 L# P; T8 _) v
    12
    - j/ }" p; i! ~7 J% Y对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。% n: x! J) U% O! x& |/ ?6 `+ H+ t4 W
    # 分割时区间不能有命名,否则字符串传入错误。
    2 j; E6 e- K+ o- r2 Y) c5 [id_interval=pd.IntervalIndex(
    ) x4 E. n- e- u3 J# N: e( F    pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],retbins=True)[0]# w* u, c' ?9 K5 n+ O: I: G) O0 ?* h
                                )! r  y. ^$ h! |3 `% G& v% s9 L
    id_interval.left: |7 J2 a; W1 Z/ ?" H) E3 F. X" }
    id_interval.right
    5 u% ^& i% z/ ^id_interval.length                            ' L, k9 P. `  y1 ]( w; k
    1
    # I& T/ z% L  _1 }" \2- F* `. W' z, m, {5 A  j0 K# u1 z% K
    3
    * K4 |+ s$ n5 Y. T9 ?+ b8 U4( x9 Q7 v4 g4 ?0 k4 \7 _
    50 r# S  R: o' S. D. R$ E9 X6 m" J
    61 i: g! u( X) k  h
    7
    ; n. ~4 w  g9 E5 Z: ?第十章 时序数据
    / Z3 x9 G# R' t: r8 [import numpy as np
    ) b; Y/ E1 s! ~# a6 m) ]" U9 B7 Vimport pandas as pd% Y- b* P4 z( W0 X* j6 J8 O1 M
    1, |9 G/ W% `7 y4 c
    2
    & |4 o) M$ R8 ]9 B1 N& ?
    - B+ E' A4 \( n8 Z
    + U! e% I- c3 D$ A10.1 时序中的基本对象9 Q! d; b9 z& v( j6 H
      时间序列的概念在日常生活中十分常见,但对于一个具体的时序事件而言,可以从多个时间对象的角度来描述。例如2020年9月7日周一早上8点整需要到教室上课,这个课会在当天早上10点结束,其中包含了哪些时间概念?* Y4 W2 m  N0 y( _" z) {# t$ x

    2 {2 {; j. T* m5 g会出现时间戳(Date times)的概念,即’2020-9-7 08:00:00’和’2020-9-7 10:00:00’这两个时间点分别代表了上课和下课的时刻,在pandas中称为Timestamp。同时,一系列的时间戳可以组成DatetimeIndex,而将它放到Series中后,Series的类型就变为了datetime64[ns],如果有涉及时区则为datetime64[ns, tz],其中tz是timezone的简写。* h5 n7 p2 S) r" e9 ?( J: g( f2 i

    ; |/ y; N& N" ?" y0 U% g3 U  r会出现时间差(Time deltas)的概念,即上课需要的时间,两个Timestamp做差就得到了时间差,pandas中利用Timedelta来表示。类似的,一系列的时间差就组成了TimedeltaIndex, 而将它放到Series中后,Series的类型就变为了timedelta64[ns]。! m  i8 ?, w# H% o6 K
    ) e$ y- j+ F. I& ^9 t9 }% k
    会出现时间段(Time spans)的概念,即在8点到10点这个区间都会持续地在上课,在pandas利用Period来表示。类似的,一系列的时间段就组成了PeriodIndex, 而将它放到Series中后,Series的类型就变为了Period。2 p" e& y" ~- z  w1 N4 `  k
    : i( |% U% D6 [( d2 e1 R: s3 Y# G
    会出现日期偏置(Date offsets)的概念,假设你只知道9月的第一个周一早上8点要去上课,但不知道具体的日期,那么就需要一个类型来处理此类需求。再例如,想要知道2020年9月7日后的第30个工作日是哪一天,那么时间差就解决不了你的问题,从而pandas中的DateOffset就出现了。同时,pandas中没有为一列时间偏置专门设计存储类型,理由也很简单,因为需求比较奇怪,一般来说我们只需要对一批时间特征做一个统一的特殊日期偏置。
    7 c! L( v* q0 n% D( e# O2 b. I9 ?6 G1 Q) }; ^) G
      通过这个简单的例子,就能够容易地总结出官方文档中的这个表格:
    ) j6 y# t: Q2 M- t  b
    * @7 J6 l4 a+ i0 E0 U; r概念        单元素类型        数组类型        pandas数据类型
    5 r9 p2 }$ Z3 y  Z7 W) W! _# PDate times        Timestamp        DatetimeIndex        datetime64[ns]. q+ }1 \* Q0 `4 }( h; j% C
    Time deltas        Timedelta        TimedeltaIndex        timedelta64[ns]
    0 f7 V" ~' v0 N5 c$ y( dTime spans        Period        PeriodIndex        period[freq]3 X. A! Y) k$ l9 x# O
    Date offsets        DateOffset        None        None4 H; E" _) D- c( t, V8 e
      由于时间段对象Period/PeriodIndex的使用频率并不高,因此将不进行讲解,而只涉及时间戳序列、时间差序列和日期偏置的相关内容。  y# O% R4 Q# o; w9 E" ]" e' x( D. J

    % s, Q* v2 T0 p8 P- X5 O" M10.2 时间戳3 L% N) D+ Q4 P4 @/ q/ B
    10.2.1 Timestamp的构造与属性/ Y# L7 O0 j# W2 z5 u. s
    单个时间戳的生成利用pd.Timestamp实现,一般而言的常见日期格式都能被成功地转换:6 M( D  y$ b1 r" P0 g
    ; s* `! I3 _5 f7 K3 H. E3 m9 U( r. Z
    ts = pd.Timestamp('2020/1/1')
    ; L$ X3 G4 |+ A& ^4 n
    4 j8 F4 m! h6 e7 S* q  W# K3 Qts% o. H# Z* c; Y, a2 s- F0 g
    Out[4]: Timestamp('2020-01-01 00:00:00')7 v7 z: {5 H7 T

    6 [; q& v/ A/ h9 T# E' e" {" cts = pd.Timestamp('2020-1-1 08:10:30')
    9 y+ M7 Q, a# @# y  N8 ~' u" `% P9 u/ q; s1 u, w& B
    ts
    1 R3 N  N/ n2 f/ t! ^1 W- I9 _Out[6]: Timestamp('2020-01-01 08:10:30'): S9 ^( X9 l/ V$ C- w6 w0 \
    1
    7 k- O& {1 k/ D, G( g2; e- B) f$ M1 \. d" B& t" H# Y
    3
    $ E' W2 Z' I2 b  `7 I" Y/ u4% t. v: o7 w# M! c0 t$ h1 ^( R
    56 v. D. q. `/ ~3 d
    6& c4 C( l! A% y* c$ }' e; t6 F. @1 N
    7
    8 A8 o9 z1 N# j+ R2 j+ L5 q- z$ P84 E/ r5 L# @: I* `. q% A) |6 K8 s
    9
    ) p$ t9 L3 v/ Q( e* i: e% e通过year, month, day, hour, min, second可以获取具体的数值:
    + W7 X0 E( W8 m5 }) j) \, V' Q( [* L$ G. e4 H, }  g& n( D
    ts.year
    9 M8 l% T7 }  Z  _2 J6 Q) YOut[7]: 2020
    8 U# F+ m# p! \* a$ p) f/ |* K
    4 k- J( g$ {& ?$ Wts.month
    ( A6 Z# d. ?6 ?3 @& v5 L7 j" l- WOut[8]: 1
    - V( L' J- Q3 D& A+ s% H! ^- }! s  x3 \8 T, L1 A1 [
    ts.day# \: e( R0 j+ N
    Out[9]: 1, d& R" u2 r5 K' M& x) O
    / p: z6 t3 @/ y7 @3 e  z0 i
    ts.hour4 g* u$ A* A# V% }
    Out[10]: 8
    * \. K5 q9 v! _# [2 e/ M) s) k+ V
    ts.minute+ `4 a; A2 z& d/ Z7 c. U, [' C
    Out[11]: 107 v" \+ j1 z6 O3 f; M5 e

    ! W1 N( U4 e6 W( m: [$ o- R9 tts.second9 R. T) x3 w) g( @
    Out[12]: 307 O* `. k- k# k! h0 B- G' S. K

    + q& u  u: j) O$ L1
    / S4 a) J+ S0 f( K: H( n6 W# k2
    - ]- \: D+ z( R6 M3
    ! d9 S; h6 v. r: }! Z4 o% ^3 C6 E43 p) x+ N+ E2 Z; B* v
    5; W, f5 ~+ U; U& i( ?+ v- k
    6
    6 T8 Q: L" L4 \7 m7 Q1 L1 e9 H7
    8 k0 V+ T* n1 v# ]7 z1 Y  d8
    8 f5 p4 q- D! `9 Y! u0 l9" k6 k4 y- f; \. a- Q+ W
    10
    ( H0 r+ i" S5 z2 j. u; P11
    ' b% _6 c, }; P7 D1 f12% D, P, g# @0 f0 H! F8 W
    13! Z/ n6 L: Q) V6 X& @0 t# J, d
    14
    . h; \' X" E. f3 e! y/ T" R8 e15& g5 N9 A, y, C- I
    16
    / C" G1 i; Q0 t17$ u, M! o% R, v& C2 `$ y
    # 获取当前时间" M3 d0 N  ~' y8 u$ X- W
    now=pd.Timestamp.now()
    : _& `( @* _; p. q- s: P$ P1/ g0 Z* z! D9 d5 h: s" c( R
    2% m: j6 ~$ `. x, ~
    在pandas中,时间戳的最小精度为纳秒ns,由于使用了64位存储,可以表示的时间范围大约可以如下计算:
    4 }: F8 e5 O( HT 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)( r* t" i6 h& Z
    TimeRange=
    9 S5 Q3 Z1 o. q- o" R10 4 j* Q+ w( w- T1 ~: {. R- c
    96 ]7 ]1 N5 j, d5 S
    ×60×60×24×365
    $ c9 D  q7 t* T' m7 `4 Q: P$ ]' b0 |2   u# M# N7 Q- ]$ J. b
    648 Z* \+ A' n( g
    / @- W+ j8 P# H

    5 f7 {1 Y1 s; w6 ]$ F$ L8 _ ≈585(Years)1 G% _$ U2 ^+ O. Q  M$ ?7 F4 V
    * k) _* r+ |2 D& p
    通过pd.Timestamp.max和pd.Timestamp.min可以获取时间戳表示的范围,可以看到确实表示的区间年数大小正如上述计算结果:8 N/ {$ v9 F: D" d( o

    4 z& W/ S+ a( T* x/ c$ i- Upd.Timestamp.max# L9 \, `2 |, Z8 k% ~4 U' @
    Out[13]: Timestamp('2262-04-11 23:47:16.854775807')0 g& H; O& h. s/ |0 R
    7 O, ?% C& q0 W4 ]- w
    pd.Timestamp.min
    # A) K/ R- g! b% g! COut[14]: Timestamp('1677-09-21 00:12:43.145225')
    ! k0 Y) w- k% y( o4 J- ^  Y9 i. L/ |5 t/ s; b' T
    pd.Timestamp.max.year - pd.Timestamp.min.year
    5 C0 Z( A4 M. [0 v7 S5 o2 z% NOut[15]: 585) q2 k1 i) H" ]  N* L: v5 f
    19 `0 ^: }( E* Y
    2/ g$ \, }' m8 {, m
    3
    : g' V" E, t& G% W; s( m4
    9 R5 {* @2 ^4 g/ U; ~) l3 S; V1 K* U5( k, V! P' b3 I" c3 b2 h
    6
    2 _. n# i3 P& d$ N) ~( A; \7 j7
    ; u2 A- i7 D, ^/ \5 l/ z; O80 u* B% M. d- Z9 g! h4 p
    10.2.2 Datetime序列的生成
    5 U1 T# ~7 P0 A, opandas.to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, utc=None, format=None,
    ' `5 V5 N9 g0 t* I" j- n' U3 A                                  exact=True, unit=None, infer_datetime_format=False, origin='unix', cache=True)
    0 d# l& l2 W9 B( p  K+ ~9 M1) _3 `4 ?$ \- f. J3 J  M" Q$ P
    2
    2 T7 }2 L7 x# t* c# b! W5 ^pandas.to_datetime将arg转换为日期时间。- \- P( `' M) L" |
    ( h; b7 H& @1 E/ t+ Q& f# x- B: x
    arg:可以是argint、float、str、datetime、list、tuple、一维数组、Series、DataFrame/dict-like等要转换为日期时间的对象。如果提供了 DataFrame,则该方法至少需要以下列:“年”、“月”、“日”。* |3 M! z+ h% T9 {$ |% Y
    errors:
    " n& d& h# K7 ~; ^* u- ‘raise’:默认值,无效解析将引发异常5 ~" \& q5 Q" {. D. p
    - ‘raise’:无效解析将返回输入
    : o% k0 d0 ~. b2 u- ‘coerce’:无效解析将被设置为NaT: U) z: [/ c6 f9 c- h
    dayfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析日期,例如“10/11/12”被解析为 2012-11-10。如果无法根据给定的 dayfirst 选项解析分隔日期字符串,会显示警告。+ H, g* U/ P7 [3 B) W! k
    yearfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析年份,例如“10/11/12”被解析为2010-11-12。无法正确解析时会显示警告。(如果 dayfirst 和 yearfirst 都为 True,则 yearfirst 优先(与 dateutil 相同)。)! x$ A! t0 Y' I9 F7 X
    utcbool:默认None,控制时区相关的解析、本地化和转换。请参阅:pandas 有关时区转换和本地化的一般文档
    1 _1 D$ s/ n5 L- a. P3 ^  Y3 |format:str格式,默认None。时间戳的格式不满足转换时,可以强制使用format进行匹配。* ~: |: |, b2 H; h( w+ h5 M
    unitstr:默认“ns”。它是arg (D,s,ms,us,ns) 的表示单位,可以是整数或浮点数。这将基于原点。例如,使用 unit=‘ms’ 和 origin=‘unix’ (默认值),这将计算到 unix 开始的毫秒数。
    8 Y4 ]; M4 f7 j4 r$ Vto_datetime能够把一列时间戳格式的对象转换成为datetime64[ns]类型的时间序列:( n( C: s6 o) w3 G1 D" n. n4 }
    pd.to_datetime(['2020-1-1', '2020-1-3', '2020-1-6'])( U) \, @: r7 r/ J# Z
    3 @4 \# H  s  k
    DatetimeIndex(['2020-01-01', '2020-01-03', '2020-01-06'], dtype='datetime64[ns]', freq=None)
    1 V& R" W% M" t, V9 k1
    $ K" D9 Z0 a: r# m) V: a+ |2% B( X& f) R9 n- ^$ G  s( u/ \8 D0 X6 r
    3* P/ U  k, r9 d( l0 P
    在极少数情况,时间戳的格式不满足转换时,可以强制使用format进行匹配:
    . P$ i5 R3 C  H% |- b) |* ^1 M; e: |7 z+ q
    temp = pd.to_datetime(['2020\\1\\1','2020\\1\\3'],format='%Y\\%m\\%d')
    * u) V$ q6 B% {+ ?8 }! Gtemp+ w) i( {- o  ~4 x

    % |8 J2 `+ o, R4 I6 Z: c% I& I; JDatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)
    7 J& A, l  {$ S1
    2 ~4 v6 Z  J) Q/ R2/ y' h9 x* m8 C! b) P
    32 N) ~4 G/ V, t+ Y; X
    4
    & v  {4 x( A- C  注意上面由于传入的是列表,而非pandas内部的Series,因此返回的是DatetimeIndex,如果想要转为datetime64[ns]的序列,需要显式用Series转化:: E/ f. s) w! X3 g" F
    ' E/ }% f9 L* A$ Y' L: N
    pd.Series(temp).head()  ]$ v$ a8 D: C! n9 Z3 ^

    2 z: d  D+ m; f) C: C5 o0   2020-01-01
    1 o+ f% K5 j% @: f4 `9 d4 o' L1   2020-01-03
    : t- t' w# o8 q6 ndtype: datetime64[ns]
    3 n. R* W# }1 d! f  g9 d) I% i! I1
    8 P4 d& o" u8 t4 j4 _" L2+ _2 b, e6 O9 ^4 ?$ R
    35 k: Z6 E7 O5 _1 Y
    46 |$ t3 K1 ]( y1 a: y
    5+ A% h, H; N. g% P0 I
    下面的序列本身就是Series,所以不需要再转化。. W: A* d, V! p7 W& |( C" [1 E
    ( R5 A  Z( N2 U. s
    df = pd.read_csv('../data/learn_pandas.csv'). N$ D2 H" J* P
    s = pd.to_datetime(df.Test_Date)6 h) d) o3 X$ h% v* K
    s.head()
    + O* s5 T  k) O
    5 C, O$ U1 _  w/ h; ^, ~6 `$ f0   2019-10-05
    $ ], a3 H8 `3 t; S. R; X1   2019-09-04
    ! e% T! x/ Y) s% i7 v( a" k+ Z6 @9 }2   2019-09-12& W/ v  }) P# i
    3   2020-01-03; r$ a, z5 t7 s" W* D) s
    4   2019-11-06) N( H1 ~# ]7 R" B! {1 L8 O% U
    Name: Test_Date, dtype: datetime64[ns]
    # K  U# K1 h% E+ m. E3 q1
    ! |2 a0 F0 I4 t/ i. s7 g$ n% U2
    ) k& F+ v8 J) ^, o$ P2 i3
    & k+ N2 ]; J) s' d* J% ~4) M0 U9 R# a0 J; G, ?* u3 M
    5
    ' s& S$ r) [& |* ~) ?6 |' L% }6: x7 n& i# R' w
    7& {$ k2 |7 }5 p: \: h
    87 Q# z6 @  C( E4 B. E
    9
    6 \5 c! Q- @. T7 O2 C10
    5 o* T  |5 ?, ?5 x+ ?把表的多列时间属性拼接转为时间序列的to_datetime,此时的列名必须和以下给定的时间关键词列名一致:
    : m' E2 x& W6 G" G% C9 Q$ z8 hdf_date_cols = pd.DataFrame({'year': [2020, 2020],
    / |  B" O$ x0 O% z: e% @9 R1 r; ]0 c                             'month': [1, 1],
    ! b6 K& Y7 m% d0 N0 |+ F# x+ N                             'day': [1, 2],
    ( J4 L4 T) E) m7 {& s+ m2 s' w6 c                             'hour': [10, 20],
    4 W  o: T1 A# S, f, v8 }. E% z$ U                             'minute': [30, 50],
    $ t- e: [" `* X  k                             'second': [20, 40]})$ j/ n1 x5 S# j. V, M
    pd.to_datetime(df_date_cols)- M$ \, s  S! E! e# d# _
    9 Y, B2 Y0 l4 H: z8 T* o
    0   2020-01-01 10:30:20* K5 t6 u# W* B6 F, P- [3 e" P
    1   2020-01-02 20:50:40
    # \$ p& K% R4 P6 N8 w$ rdtype: datetime64[ns]
    ; l/ j' t5 V3 O9 M1& p& x3 c  ~. V
    2
    % ]5 n0 B/ j+ h* F% C37 z& B# _8 n/ `5 M# J" J
    4
    8 L' X2 ^( X7 p/ g5
    $ y9 w1 ]5 o/ c/ ~; n6 q6
    ; K7 I" @* @. t- I) V7
    6 `3 ]$ E/ R+ T! w( m5 y. {81 U' F7 I! P" Q' t$ y% J
    9
    1 ~0 q  {# R8 J* T10$ w- Q  h2 [. P% p/ j* m" ^) V
    11" }0 e6 b/ W! o0 R$ z0 Y# r: k
    date_range是一种生成连续间隔时间的一种方法,其重要的参数为start, end, freq, periods,它们分别表示开始时间,结束时间,时间间隔,时间戳个数。其中,四个中的三个参数决定了,那么剩下的一个就随之确定了。这里要注意,开始或结束日期如果作为端点则它会被包含:, y  m7 A: S+ j( t1 b2 L
    pd.date_range('2020-1-1','2020-1-21', freq='10D') # 包含9 T' q. j, Y% }' P: H
    Out[25]: DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21'], dtype='datetime64[ns]', freq='10D')! Y, s' H! Q7 R

    ; M% H5 [- }. D$ H$ Zpd.date_range('2020-1-1','2020-2-28', freq='10D')1 E# h2 t- E" p* g% A+ T, S; Z
    Out[26]: ; R9 {& x2 u  x$ A$ g
    DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21', '2020-01-31',
    ( d# B' _$ {  s. Z! d               '2020-02-10', '2020-02-20'],% j/ f3 A: z) R4 Z+ \
                  dtype='datetime64[ns]', freq='10D')
    4 ]( T- W9 Q. w: C$ U$ ~4 i( l+ e# F
    ' y) s! K9 f  f6 Dpd.date_range('2020-1-1',
    # n0 Q% ?+ T: U/ z; ^              '2020-2-28', periods=6) # 由于结束日期无法取到,freq不为10天0 p" g  t4 `/ z. E% o
    6 A1 K7 A6 m5 F+ ^" p* u1 z
    Out[27]:
    / U4 P2 q! c& pDatetimeIndex(['2020-01-01 00:00:00', '2020-01-12 14:24:00',% ?; L  L/ _6 U1 D+ L3 {9 T' {) Z0 Z
                   '2020-01-24 04:48:00', '2020-02-04 19:12:00',( ~- o0 g5 F% U( N
                   '2020-02-16 09:36:00', '2020-02-28 00:00:00'],; J2 m7 v" s- Q8 F! `+ N
                  dtype='datetime64[ns]', freq=None)
    ; ^( a4 M6 j  [9 k
    # }" A; ]9 n- K- J, b/ Q' l1- u7 e) m/ @, i& H# y. a
    22 P2 Y4 U/ T1 f+ P
    3
    ( z9 l, e  G2 A4: T! H# [( l2 J/ v# B* \2 L
    5: s5 C! W" f5 ^$ P( {6 n
    6
    ; m0 H; y  t8 Y" C+ ]; S7! {3 M" L/ A) a3 I- l. \$ ?
    8; n: A* u: _. I/ p1 R. J
    9+ U7 \% d4 R) e6 T5 ^, F
    10
    1 z1 X0 N! f  s" z( O) s; y, `114 R8 I4 O9 `+ B- v4 z1 G
    12
    , ]) Y: U0 [2 z0 r7 f+ b3 ]13
    # _4 s: _* R, |  ^# e0 k% |8 h14" q0 ~+ s! _7 A  n
    15
    0 g( w9 L( ^: Y# ^+ n7 a8 D16# @: T+ m6 }! `* Q+ p) H' o& J2 S
    17
    3 c( T  }& R8 n- L; `' W" ~2 ?# H这里的freq参数与DateOffset对象紧密相关,将在第四节介绍其具体的用法。
    2 X* [" E) K0 M! p! L2 c8 c& A) x9 O- t: O3 A
    【练一练】
    , `5 P0 l# w1 b2 \Timestamp上定义了一个value属性,其返回的整数值代表了从1970年1月1日零点到给定时间戳相差的纳秒数,请利用这个属性构造一个随机生成给定日期区间内日期序列的函数。$ c& r! r# Z; G3 T; o+ Z: B
      `) X+ d. N  h
    ls=['2020-01-01','2020-02-20']
    7 H. I/ M; h9 Q, ^def dates(ls,n):' G% e* l5 r/ F" Z7 x+ z
        min=pd.Timestamp(ls[0]).value/10**9, }: F; o8 V: j6 N% L" ^7 I! q) y/ l4 A
        max=pd.Timestamp(ls[1]).value/10**90 Y: Y( @6 v& l- w$ N. U: [) ^$ A4 Z
        times=np.random.randint(min,max+1,n)
    2 f8 r: ^. J$ O4 p    return  pd.to_datetime(times,unit='s')7 F1 n; H( K4 C: t
    dates(ls,10)
    1 w9 p$ M( t0 \' r' k7 o0 B5 x7 Y1 C. X- I* G) j0 [
    DatetimeIndex(['2020-02-16 09:25:30', '2020-01-29 07:00:04',2 j" O0 q1 k3 L8 H; N! z
                   '2020-01-21 12:26:02', '2020-02-08 20:34:08',5 P) k+ ]. |4 z3 K
                   '2020-02-15 00:18:33', '2020-02-11 02:18:07',4 k  Q; i3 ^+ l1 v& {7 p# `/ O
                   '2020-01-12 21:48:59', '2020-01-12 00:39:24',
    " F. t2 n1 O7 ^3 D2 v  `& P               '2020-02-14 20:55:20', '2020-01-26 15:44:13'],
    ) _7 b, p0 a% L1 G8 U" H# k              dtype='datetime64[ns]', freq=None)
    # K& {0 L4 ~- G" P1
    - f5 ?9 J7 B  C: _# d# g" d4 Q( g2
    . ]) e, e8 n) }# V1 f7 p2 d2 C3
    : E7 N" {$ n5 n5 _0 U4 k46 F# e8 z1 u" D* X5 b. u9 ]
    53 c5 U* f7 @# m# L
    6& C6 Y9 B* p$ M5 q) i9 w0 z
    7
    0 D# K7 Y7 x/ E% S; G% @# ^8
    7 z$ k$ I( p. V4 n4 f9
    % U( E, z" o, V6 ]2 k$ @$ B1 V10
    2 R; w! t$ F4 `- D4 f11& n1 ]( k$ F) {& T* Q; S2 G/ H! k
    12# O! _2 k' E& L2 C# T
    13* y( }9 a/ ?$ v1 p
    14
    9 ~! ~1 _0 _2 Z8 ~; D2 yasfreq:改变序列采样频率的方法,能够根据给定的freq对序列进行类似于reindex的操作:2 P8 B/ f1 ?- G- _/ z
    s = pd.Series(np.random.rand(5),
    4 f$ m, o) c- w' V            index=pd.to_datetime([
      M. Z6 ^8 n$ o                '2020-1-%d'%i for i in range(1,10,2)]))
    $ X8 s  s7 k  L( N* ]8 K
    . q* s! G0 @' i- J, _% n- ?/ \
    s.head()8 q4 c7 Y& T3 s/ E/ _* S. p
    Out[29]:
    ) n6 S0 X# b/ a) i. |0 [; W0 M2020-01-01    0.8365784 j5 |! `& s. H- z5 l" R8 G7 L
    2020-01-03    0.678419/ Q( \6 p4 U/ M: `) a
    2020-01-05    0.711897: {7 _: a- i. s0 r3 Z
    2020-01-07    0.4874291 j: ^* l  f: J
    2020-01-09    0.604705
    & _, q4 L2 N& }  S6 Q2 M% a  H( edtype: float64. j3 _9 z$ r- y! ]* Z( ~
    ! A% X, X+ g( Y; R- a, b* T
    s.asfreq('D').head()
    ; o; g# ]) o$ NOut[30]: + o. R3 ]$ x6 W. M. o+ A( A
    2020-01-01    0.836578
    ' _( [& {* Q" F) Y2020-01-02         NaN
    ( @0 s: ]$ V! C2020-01-03    0.678419
    # ]4 ]/ c1 z, g* k0 d; b# G5 ]: X2020-01-04         NaN; ^6 g, V, a# I4 N  m' z
    2020-01-05    0.711897& L8 P4 P7 F+ m( Z4 [6 k: b' Z
    Freq: D, dtype: float64" n  [% Y% n! s% c+ G: m

    ! |) t$ S) W3 Ks.asfreq('12H').head(). t  R& h  Y5 e! |! w6 Z; k
    Out[31]:
    ) Z, |" X' L! P; l" W2020-01-01 00:00:00    0.836578+ @% B2 }8 I+ c
    2020-01-01 12:00:00         NaN) W5 K1 `/ K6 R$ T
    2020-01-02 00:00:00         NaN
    ) N( v% J3 [) K, v+ v( a  K2020-01-02 12:00:00         NaN
    " x" [: ?& S' M2020-01-03 00:00:00    0.678419
    . _; `: I5 n2 K# l( fFreq: 12H, dtype: float64
    / ~* b6 d/ c4 H! ^
    ! z7 j" W6 M/ s- L  [4 B: x5 `1
    5 y7 z! B, @, V2  W$ d  ?2 v2 r* u- F2 r
    37 P$ q, d% T( c2 a8 n' y
    4" J2 U6 Z/ ^: u. M
    5
    & J  a. }4 t4 t- y* J64 v, J( N( F7 w- L' L: s
    7
    9 D1 [# B; h; ]# i. M8$ B. e  P( B0 [) B; `
    98 {4 ]. W+ M! Q, ~
    10) V) j; Y$ ^" [8 p& V" Q
    11' V+ w" i5 I, c
    12
    - |- p5 w. M  B9 \9 a13% _- k; v+ H2 n- U9 m: X
    14
    4 X2 U8 m0 J7 T0 }6 K7 E2 k15
    4 G6 v: u7 R* w2 Y. X7 g- i/ {( N16
    # g: C& c+ [. ^' B17
    5 J1 ^( G/ \! q7 E! z  S$ }' \. K185 Q( b; R! g- ~3 `
    19
    8 r0 |. Q6 c/ I! s1 V20
    + N6 L# i: Z, F6 v21) ~$ K! F( k( `8 M& H7 e
    220 H( S- L1 d5 F) V
    23
    . o& @' @6 |% r% g; P24
    5 ^; H% o. H+ A) I: K0 j25
    & k" _0 ?, K4 o9 Q& ~2 M2 _26
    1 t0 v# ]" ?: e277 U. r7 [0 A1 c
    28
    # k* s: p: c0 T: m- E- n5 {29
    ! E. m; C3 s3 b) C7 ~; |% h30* K3 A, I' t  ]/ X; Y
    31
    " R! d' `+ g* s6 g) f【NOTE】datetime64[ns] 序列的极值与均值
    1 `3 f5 Y2 i; V  前面提到了datetime64[ns]本质上可以理解为一个整数,即从1970年1月1日零点到给定时间戳相差的纳秒数。所以对于一个datetime64[ns]序列,可以使用max, min, mean,来取得最大时间戳、最小时间戳和“平均”时间戳。
    / y. {' G. l! ^5 X% C2 p, h. `4 b7 Y
    10.2.3 dt对象
    * L/ y$ u. ?; L- _6 V7 g  如同category, string的序列上定义了cat, str来完成分类数据和文本数据的操作,在时序类型的序列上定义了dt对象来完成许多时间序列的相关操作。这里对于datetime64[ns]类型而言,可以大致分为三类操作:取出时间相关的属性、判断时间戳是否满足条件、取整操作。
    $ A- O+ C# \+ \& K. r4 h2 H: q* d& |: }& L4 j: N
    第一类操作的常用属性包括:date, time, year, month, day, hour, minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter,其中daysinmonth, quarter分别表示该月一共有几天和季度。
    5 k* e( |) l5 Fs = pd.Series(pd.date_range('2020-1-1','2020-1-3', freq='D'))
    9 e) d& U) [$ x
    ) K# R+ J! U; es.dt.date7 F! f. L" y: K
    Out[33]: . X( ?3 p8 b3 d. m
    0    2020-01-01. v# E8 X. ^1 A; u
    1    2020-01-02
    6 f( Y; }& q0 [0 s2 u2    2020-01-03
    - a" T" ?; |% P: C& Cdtype: object+ D3 ]( s2 A, z: p
    ) L4 \6 I0 ^4 L2 }  e
    s.dt.time+ O! L; V/ E+ T1 Q5 u0 ?
    Out[34]:
    + c3 F: Z1 Q& X  E0    00:00:00
      b! |& `! f6 I0 K3 g1    00:00:00; ]9 u1 u, l2 l; @# w9 W9 o4 w  J+ {
    2    00:00:00
    ' K. [  V* R/ A1 R) l% Ndtype: object$ u( m- _* b+ I

    9 w( X! t1 y* z: p9 S' ps.dt.day7 X  ]% E9 E8 A( T
    Out[35]:
    $ A" T7 ]/ K+ Y) b0    1
    8 l. m! f6 p5 u# R. d" v# f  @% n( q1    2) Y1 Z4 p, w9 ^
    2    3) ^; D! y) A' F8 u" B# Y# a
    dtype: int64- p: Z  M( {$ E
    2 z; o4 _* W: t. T& i4 L
    s.dt.daysinmonth9 g: U9 @6 ^- ?$ c' D
    Out[36]: ( P) K; z& Z% W7 o7 `6 [% V
    0    31
    $ `9 J) @3 k: T$ w4 @. N* A1    31! h9 _- U2 i. ?& g% z
    2    318 {: r% T, W( i" f. |
    dtype: int649 u6 H* P- }! M$ h2 i9 n

    7 a" R$ m7 X5 m- }8 l8 t! M$ g+ B17 \- T6 Y: B+ Y$ H$ `* v
    2- y: k; a/ L. B5 W
    3+ s# ~- R+ @0 |* r4 K7 b
    4
    8 `& F9 O- R$ E6 y5% q' j% r, ?2 f- B
    6
    6 z. |' Q/ ?$ _" w6 v7
    . k1 l* Q4 S# e' _82 I0 E/ a& Z9 K  ]% K
    9& A3 v; n* j5 F
    100 E5 }" d5 J$ R9 r% C/ M; Y
    11; g! A( p4 T  r- w- q" ~0 I$ n
    12
    : v( @+ X, P+ |4 X5 n) r5 ^13
    . E3 `" N% Y* z4 V14- e) q6 _7 L( U) S; r
    155 y/ M9 @/ c, J% C3 `
    164 y. ]5 E7 _* v# x" ?) J7 Z5 H5 j5 m
    17
    ; n" w. n' P6 t18$ [2 |' `0 X) f, H
    19
    . R- t2 v' r- m" K* T/ w20
    5 j0 V3 ]4 m- O5 W0 Y; V2 E6 V$ w; B5 B21+ {& G. x- c. W% h2 x6 I
    22
    ) |3 \7 z) G' {% K7 \23
    2 s( g8 L- P. K4 {. G! a24% B1 ?1 n* ]$ P1 u1 {) m
    25' Q! T2 _% x) Z7 ~( N( `  B
    26. ]' b" q2 M7 F1 }3 v& i5 l- @( ]+ d
    27
    7 h% y, f3 y+ n" p( B286 S( y1 c- K( Z) |8 T! C
    294 [  P) \! w4 K8 O' T, M
      在这些属性中,经常使用的是dayofweek,它返回了周中的星期情况,周一为0、周二为1,以此类推。此外,还可以通过month_name, day_name返回英文的月名和星期名,注意它们是方法而不是属性:
    , c# I$ P1 P' {6 v! Z2 c/ y8 W  H9 \( \! B. u$ B
    s.dt.dayofweek
    0 \5 Z0 T6 P$ ~) W) kOut[37]: " ?9 ~% g( W% Q2 e1 K
    0    2
    7 Z& Y. |6 [! A, L1    32 d2 G) d( M8 C1 v+ G8 [
    2    4& i! f- X4 d  M- [
    dtype: int64
    9 \+ R$ p0 d2 f0 P& Q4 l( ]: }! k# b+ Q; o6 @3 |$ w& O3 w; d
    s.dt.month_name()( Q0 M  L2 L7 \$ Y
    Out[38]:
    $ D2 Z; X5 z) T) S4 x0    January  {  S; r. l8 W" B
    1    January
    4 ?  k5 f& `% u2    January
    3 |, X3 S+ f5 L9 y" @& i& n! Ldtype: object+ g0 s" Y/ R4 z# [2 N$ Z+ ^
    1 B( U  o) p5 G/ g9 Q
    s.dt.day_name()
    % W  ]4 Y, A8 O$ E7 h$ ZOut[39]:
    4 K2 `+ }3 r4 R# M0    Wednesday
    - G- Q9 c3 F4 u) p' c1     Thursday
    / u9 \9 h0 p" u1 @6 F2       Friday0 T! o- J: ]4 P/ P4 F# e
    dtype: object2 c  [( A: L" t) a( u, R

    $ ]7 s; t; b4 ~- E. C( W; H; D1  ~7 e6 C9 h% M4 t/ u/ q' V# W
    2  m9 M6 x$ e* u7 K1 ^* A
    3
    . G. H% |$ h0 Z; y; Q4
    8 f; J- c# Z+ U0 ^# y5% y  K: C5 Z+ `) r7 K: }. y+ [
    6: I4 a% A+ w0 f
    7
    7 q2 m  m4 l2 y" ?8
    - q5 j9 C7 [: C" H' t  t) M, D9
    6 `8 ~7 U1 ?$ Y& v# n2 ?. k10+ I8 Q* D9 A( C! H% w6 k
    11
    5 T* M( b. A! _4 A12
    3 x# o0 T8 j; P8 r6 V* G) G13
    5 q% [% y! d% p! E: z8 b& U' V14# B8 n# O4 U- v( ]1 ^" I3 e1 p0 p
    15
    : h$ k# s7 S$ N6 c' m1 r- ]16
    % q& v; s; i! H; K7 o( ?1 F2 K171 v6 I  l6 s1 o0 U: @5 x
    18
    - V7 a! _+ v1 h% ?: i19- C+ S' `0 k3 \; V
    20" [9 _# e4 S: W  S( P( m+ G
    第二类判断操作主要用于测试是否为月/季/年的第一天或者最后一天:
    9 P$ T& G7 \! K) o. v: d$ gs.dt.is_year_start # 还可选 is_quarter/month_start4 k7 `& G6 `1 P+ K- L
    Out[40]: $ a. E( q; j2 \6 E  \; F
    0     True5 K! ]7 N5 g& e4 f
    1    False
    * o& V# q* L# j" F2    False% c3 I4 \1 _& y8 s+ b5 m0 f
    dtype: bool
    " [, X' l& T6 A+ R. y: U5 H+ ?; H1 @3 }7 \! g2 s
    s.dt.is_year_end # 还可选 is_quarter/month_end# K, \9 S# e8 R8 {+ @# u+ r
    Out[41]:
    " O& f4 y2 h3 G0    False
    3 U9 V: o6 p% b; V/ ^: Q8 B1    False
    5 N# {' G( b3 p7 E. m# s2    False
    % U6 I# ?) u6 `+ D, T1 W5 e/ j# Ddtype: bool1 @2 N& w* e( p+ Z6 z% m
    12 q! l* ]9 b6 R; C" Y
    2
    , |# H: I" I- J9 S  T3/ L* b, ?) t5 v, p7 U
    4$ k2 G" d+ O% a  Q0 d
    5
    ; N! e  g( i: O/ o' w  T2 w63 U% c. D2 f1 r' ]: s
    7
    % d( g! r+ B* N: O; f. Q4 S  N8
    # k$ `- Z6 `- F2 S$ N2 t0 m% F0 e9
    ' _6 e* v2 k$ v* Y; I; [& Y; i10/ z2 C6 f/ j$ I$ Q3 L: ]. p
    11
    0 [! D9 F+ D0 s/ E2 X! s12
    9 n/ l  m: H7 i* r. V13
    6 x1 |0 I* {; X第三类的取整操作包含round, ceil, floor,它们的公共参数为freq,常用的包括H, min, S(小时、分钟、秒),所有可选的freq可参考此处。
    + _! Z) V5 ]4 X/ ds = pd.Series(pd.date_range('2020-1-1 20:35:00',% Y: W* e1 F" _# \; V8 U3 g6 u4 k
                                '2020-1-1 22:35:00',
    0 U. R8 U& Q+ t                            freq='45min'))! Y% Z" k" V; b- `! F% S9 X
    8 u3 H+ [  T' k  v: R9 ^6 y3 a

    * x, D1 x/ Z1 w6 y( bs6 J/ R6 }% U% S
    Out[43]: ; |( E0 M( @7 G
    0   2020-01-01 20:35:007 q& e7 ?' g' b8 T8 g! \* R
    1   2020-01-01 21:20:002 Y) Z2 P+ U3 l0 D
    2   2020-01-01 22:05:002 V) Z" T9 _4 m# Z+ }% [* v  `( g( B
    dtype: datetime64[ns]0 E9 k! D% G  ?8 q6 ?8 [( m8 `

    . C% p4 `( y- u3 c6 @- Ts.dt.round('1H'), b" n) A' |% e/ }/ ]2 a" ~
    Out[44]:
    + o6 p8 A  w" F2 l0   2020-01-01 21:00:00
    ' j0 o+ P9 I+ X+ N6 @3 M: p+ g1   2020-01-01 21:00:00! Z' S) H1 y( b! z! I9 A
    2   2020-01-01 22:00:00( W! d( G3 l+ U7 B: t; d
    dtype: datetime64[ns]
    + Y. q/ T* I3 n6 `. R2 d% w* M- K6 p' i& ]$ j
    s.dt.ceil('1H')
      G2 b( f3 J, |- h- e6 Q, r" J' C5 e; qOut[45]: . ]" u* A! A7 Z3 O
    0   2020-01-01 21:00:00
      E9 E  ?, O1 t1   2020-01-01 22:00:00
    " `/ @& c& x: o) a( ]2 V% ^; t2   2020-01-01 23:00:00  n5 M6 K9 V( Y" n$ C
    dtype: datetime64[ns]
    2 h* L4 J' b7 ^2 M3 l6 Y) w1 N3 e7 J+ w' |3 B+ N
    s.dt.floor('1H')
    3 J) D0 ^+ B  Q# _  Z& [Out[46]: + H7 Q6 ?( y+ G
    0   2020-01-01 20:00:003 n$ \! \& U  H" v+ [$ R  I: s
    1   2020-01-01 21:00:00
      [* n+ }9 j4 |7 ]* O2   2020-01-01 22:00:00! f& f, f4 j; U  h) e" l
    dtype: datetime64[ns]6 Q! @1 K) }- D4 Q, `# w* R
    ! l% c4 P# H: S$ S7 b) H% ^
    1
    % C+ t# |4 S  o3 W) q2
    + I' T7 Z" B  J- o4 @3
    * z- P; T. v5 n4' @* b, t% ~+ L: s3 b
    5
    8 b1 c0 I5 t& [1 C6
    + z/ U! M$ l' ]5 T5 Y+ Y: g5 X7
    & n; U& y# y2 G# Y# ^8
    ; T8 W4 v+ x0 @* s- J& [' n% [) K, g$ ~9$ I4 L! v& l* p( M1 J1 r
    10/ o2 i4 K, x' u7 u2 d# T5 ?
    11
    5 x7 x/ C. Z( |( V3 y% q127 Y' l0 @% Y7 |% R# D1 t
    13' j6 p9 k% X0 K+ d) ?" W
    14, B7 K2 h8 S1 n$ H, c
    15- [: b6 w& Y& s. b- |- b
    161 u1 S) V* k9 E2 ^; `
    17& b& u, {( Y2 |# t0 G( ]
    18
    8 z, Q3 V. h" {! \0 V. r19; G  g: x; u6 u
    207 y3 E9 \: ^5 w8 Q, s* W( j7 n* w/ s
    21# ~% |: f* m& W3 A+ ]
    22
    & v) m6 P; I" x: |3 I, x( K0 g) X23$ m8 B2 G& ]$ p" R: ?3 Y/ y; Y5 C8 C
    24
    4 o9 o. f9 Q& ?% a- @7 B! j$ O! \1 M259 o% p' T% l8 `
    26
    % ]+ g/ }0 c! n# M27
    4 j1 J5 D" |" N- g; P& `$ I28
    $ f" J: N' Q7 Q4 C4 D6 y296 Z* r4 e, }/ f9 \  j" ]
    30* k( b3 m& r8 x, s" e8 w! ]& f" b9 t
    31' T7 m% f% |8 q3 Z, `
    32- W4 Z7 y2 ^7 F6 I- a/ Y( V% w
    10.2.4 时间戳的切片与索引
    * y/ _% @3 Z% k) f3 Q5 J  一般而言,时间戳序列作为索引使用。如果想要选出某个子时间戳序列,有两种方法:
      D3 L- i& i, Q" ?! Z, }0 p; a$ \- ~' E/ V* L& Z( i) }7 I
    利用dt对象和布尔条件联合使用* }( d9 x% x2 s- D
    利用切片,后者常用于连续时间戳。2 n# L( V1 g. }5 p0 G+ F$ I% \/ z. j
    s = pd.Series(np.random.randint(2,size=366), index=pd.date_range('2020-01-01','2020-12-31'))
    + B6 g6 j, \/ Q  x6 H7 Lidx = pd.Series(s.index).dt+ {, p' E4 L9 I% J* [
    s.head(), o: ]1 T, w+ M! Z# g
    5 Q  U3 S% y% k* O7 a
    2020-01-01    01 A7 B7 j; R  J  A# D# D* {' i
    2020-01-02    16 z& {' o$ W/ `* I
    2020-01-03    1$ R0 e3 R0 f1 @1 e8 P5 F, X
    2020-01-04    03 b1 G$ I& X! [" w1 L1 B: J! X
    2020-01-05    0
    9 z" k3 \5 k" F- l- v2 {  GFreq: D, dtype: int32
    ' o* n/ e* p  l8 b4 N# _7 r# ^( N0 s8 V1# B1 m& u, k" l- _' e' S
    2
    . p$ t- ^! w! m0 B$ B3 j3+ ]2 n6 U4 w  }6 U6 F) R6 A, A. V: s
    4
    ' W% C# l) O/ c# z% U$ f5
    : t" Q, Q, u' o& I8 |& \6
    % N- j# }' s0 O+ m! p4 `) u( B$ q7
    1 w7 C& L# I" N4 ]: ~% h2 @. \8
    0 X" C2 I' b2 s0 p9 N0 r# x9& R3 c" g. M, c$ r) A* x
    10
    - G# L) N9 a6 o* `# @Example1:每月的第一天或者最后一天& n4 _% f! Y! L+ M  f2 T/ }
    5 K1 z7 W, q' p/ L
    s[(idx.is_month_start|idx.is_month_end).values].head() # 必须要写.values  {- i  v: T5 Q  E1 ^
    Out[50]: # ^7 k2 T6 t& f& \9 _
    2020-01-01    1
      k0 R& ?+ k0 p' T2020-01-31    0
    ; @7 ^& Q6 {6 ~! {/ t$ h: r2020-02-01    1
    - V/ s) R, A- z2 Q4 o2020-02-29    1
    7 b* a5 O  Z! b; L  m: c$ F, \2020-03-01    0
    8 a9 M! {3 j5 f5 ^4 Idtype: int32
    # t/ I0 O( b3 B2 R16 o9 x9 Z3 r* Z
    2
      n/ _! t9 M  n1 l. V. L; l* o3/ c+ }6 y$ |6 H8 e$ ?
    4
    0 z) X9 c8 R$ |& C6 _5
    / r2 H. D0 X/ V+ ~8 K6
      q; o/ C5 B1 |, k7
    ! m# ]0 E' k/ ^: T) M85 `! _/ i  T$ _6 l
    Example2:双休日
    / Y& I; h, S5 ~6 O
    " O9 b0 g3 E- q0 Fs[idx.dayofweek.isin([5,6]).values].head()
    + X& y. g1 ?: ?4 B# S. cOut[51]:
    ' I! Z  s& t) R; }$ h2 j- F" ~2020-01-04    1
    7 M5 Z5 H" v$ r" _- u8 `- c2020-01-05    0% X6 C+ m( P, o" Q  i  _: `% B
    2020-01-11    0
    ( n  H$ [) Q5 s) E2020-01-12    1
    + L4 }. g+ r3 d3 Q2020-01-18    1
    - R/ q, _: I: l3 @+ R  x) Ldtype: int32; B+ _7 D3 U! H# M' w- m* Z
    1& L* p! C8 ~8 E4 [  V5 W" o5 g5 W
    2
    ; ]3 d4 p: v$ Q$ k7 \; T4 u5 _3; B* B6 ?6 D# R
    4# K0 f+ q* T6 z
    5
    3 ?0 l' N9 z9 J  ?. ~6
    : O" O3 u# d! ?2 |9 K7) F) A8 B& N9 h( d
    8" C4 {# {0 e: g& B: {5 _6 }
    Example3:取出单日值% Q5 I2 j) M) V1 V9 G

    * ]+ H& g2 K3 E, R' L/ a. rs['2020-01-01']1 }* Y5 |3 O# M  W. d. a6 `$ d
    Out[52]: 1
    , A2 @5 F+ @' F
    9 o) O) a. p  T2 U) N+ As['20200101'] # 自动转换标准格式7 |$ K( p; \' X9 v; c* F
    Out[53]: 1
    9 p% S0 i5 H5 m8 H- r0 Z6 B15 F+ u* t  r: ^+ m8 A
    2
    + O3 o) z* v5 j8 Q2 C. u& Y5 i$ d3
    & q1 D0 K9 h$ }: ^4
    1 K9 t5 f; Z2 {( [4 o5
    % W% D! @+ X. RExample4:取出七月
    1 y  j. [% \% G/ C0 P; A! S- `0 d# f0 C5 q" o
    s['2020-07'].head()
    2 S4 `0 a: Q5 n; T5 W7 M. QOut[54]: 9 i% B: t- ?8 V5 `6 _& j
    2020-07-01    0
    8 j+ k: y* u" B4 [2020-07-02    1! h. Q9 H  p  M  ^, x
    2020-07-03    0% O' ~. h$ G! R# Q) H: [& q
    2020-07-04    0
      c( }7 M5 [7 Y% d0 B; a# ^2020-07-05    0
    1 k/ i7 P5 N0 u# p- O1 w0 TFreq: D, dtype: int32
    9 W: f, t* J+ n+ q" @8 a1
    ; m2 G3 b, r2 w7 a% h3 Q0 f2: l5 ~9 ~' x9 \7 R! N& H
    3
    8 T  a# q6 N/ }4
    ) x& A9 H6 b. S: b- d5* M3 f3 J0 _- r6 q6 i
    67 V2 U4 |9 E+ b; O% r* `
    7% d2 C+ I3 G8 W4 B
    8
    * R% D' e* T0 `0 NExample5:取出5月初至7月15日) g7 @/ v7 ?0 R2 [, P

    2 u' o; l* g" {# Is['2020-05':'2020-7-15'].head()
    . l9 C+ d) k5 @7 H! _Out[55]:
    , ~" R2 t! h, X# o- B6 K" P2020-05-01    0
    , ?( U4 A4 [# B! c( `2020-05-02    15 i6 P! m) a4 R8 [
    2020-05-03    07 h; W6 P: q/ Y6 @
    2020-05-04    15 P$ x8 w- e% W
    2020-05-05    1- ^0 }" L! @9 n9 |- D
    Freq: D, dtype: int32
    * r7 Q* t5 c) M- I5 X. t- N* [
    & n- O* M1 a- D3 h) [s['2020-05':'2020-7-15'].tail()
    * q( m1 Q) k' T3 H5 @Out[56]: ) a( t1 B% ~0 m2 k3 G5 h6 H
    2020-07-11    02 |4 ^$ P, \0 b  l+ m! c* j
    2020-07-12    0! }: g  y% Y) i+ W% x" M/ p' C
    2020-07-13    1# M7 K- W* z- E; g' g
    2020-07-14    02 M- s% Z/ h" h& w0 L' @
    2020-07-15    1* Y2 [- Q+ e( Y8 y$ E. `2 _
    Freq: D, dtype: int32
    9 O) D2 e, }" |3 n( u8 {1 h7 b/ ~5 U0 ?. o% I) n  e( p+ F! q5 M
    1' u" [* O0 n; t' `/ H7 y/ }4 ?% X
    2
    5 R' f4 v0 U+ q* n- N6 U' O* f/ ~& p0 ^3+ z) a* N) H! o) c/ Q: E4 A2 p
    4, B7 L" c2 d4 s% N" V( A, X0 u7 }
    5
    ( K0 u: N. O* M/ i# a- L0 }6
    3 k% N; q# x3 r& b, E! ^7
    + l9 K0 b( s, q- K* p; W! A& S8
    1 D5 N$ @! k* U9& U" I, u, \# U7 Y: O* ]$ U
    103 p5 E$ v6 d3 t  m
    11
    3 Z0 f$ A8 @9 \/ G$ K. i" e12( w7 \5 {; ]; w, M) r1 J
    130 ]. [% ]) O: t: {' {; u8 r
    14. E/ [3 E5 }5 N0 W, V: Y" R
    15
    1 c+ s7 P$ O. T166 N( e: I# r1 |8 g
    17
    , y" U* _7 K. J6 g10.3 时间差( w) D9 l7 t* D6 k2 V
    10.3.1 Timedelta的生成
    6 a& R0 a0 z$ D$ A, {pandas.Timedelta(value=<object object>, unit=None, **kwargs)
    ; D1 i8 }" m9 @1 I* ^; B$ w$ \  unit:字符串格式,默认 ‘ns’。如果输入是整数,则表示输入的单位。5 ^+ ]: D5 T' r2 V  h
      可能的值有:
    ( @% ~9 F( V% B( I: r7 d( a6 x6 c5 ?3 A0 Q
    ‘W’, ‘D’, ‘T’, ‘S’, ‘L’, ‘U’, or ‘N’- R2 K$ U5 G5 p3 D! }: l
    ‘days’ or ‘day’" c; u4 l! C& \8 a% ^
    ‘hours’, ‘hour’, ‘hr’, or ‘h’2 T% T. h( I; Q# b- V" z
    ‘minutes’, ‘minute’, ‘min’, or ‘m’
    ) u, Q# I: t* }" a4 ~‘seconds’, ‘second’, or ‘sec’
    , J" p$ b* ]9 o) C+ l! N毫秒‘milliseconds’, ‘millisecond’, ‘millis’, or ‘milli’( G1 Q3 [1 S  N' e0 M9 O
    微秒‘microseconds’, ‘microsecond’, ‘micros’, or ‘micro’
    - k# |1 p# N) P+ d% \纳秒 ‘nanoseconds’, ‘nanosecond’, ‘nanos’, ‘nano’, or ‘ns’.3 P, t5 f& v/ w. a$ B# z+ y* x, N
    时间差可以理解为两个时间戳的差,可以通过pd.Timedelta来构造:: g% j1 B5 P# p  D; g0 J0 b
    pd.Timestamp('20200102 08:00:00')-pd.Timestamp('20200101 07:35:00'). v9 @2 L; B' ]. E0 h! Q- K1 v" B3 H. J
    Out[57]: Timedelta('1 days 00:25:00')7 _3 R& }7 ^6 a; J( P! J  T
    : W+ B# F$ ~  F( ?
    pd.Timedelta(days=1, minutes=25) # 需要注意加s
    6 K5 e( `3 G6 v. {- iOut[58]: Timedelta('1 days 00:25:00')  t) ~/ w7 b: N
    - L- A) t1 f0 K
    pd.Timedelta('1 days 25 minutes') # 字符串生成
    . H+ e2 O4 |" ?Out[59]: Timedelta('1 days 00:25:00')+ u2 @6 a, ?; ]" W
    + N3 w6 ^5 l* U9 N: ]' o
    pd.Timedelta(1, "d")% Y) s) Q' n+ Q" ~. ^% g4 O! z" i$ y
    Out[58]: Timedelta('1 days 00:00:00')
    ' l( ^! ?$ f/ J& r- d0 _1
    $ R4 e% U& T% E# Z. y( K' S  Z2
    $ Y4 `4 G2 ~, \( v  W0 d3
    $ M% h# M0 `3 M: m) v1 x/ S4
    9 Z. D. x/ q, r' f4 h5
    6 b5 @8 Q. A& V! Z6 z6
    8 @0 z* `- y% C, R0 d7
      H) [; J, Y& K0 l3 b6 k! p0 \8
    9 w( X1 {/ H! f9 i8 G9
    % E' ?' U4 Q9 E- y/ m8 Q2 L+ g103 o; n6 W, K; V- C, Q
    11
    8 `; }+ ?3 g& q4 {$ k5 t生成时间差序列的主要方式是 pd.to_timedelta ,其类型为 timedelta64[ns] :# D( R2 @- ]7 T  ]2 f
    s = pd.to_timedelta(df.Time_Record)
    ' ]4 U9 w1 D' X7 a' a
    ) d8 y) {  V0 X. r+ q& Ys.head()- M0 i, d, l8 a6 k
    Out[61]:
    " E* P* W/ ?1 T& Z5 Q1 D+ A, W0   0 days 00:04:34! C/ m7 @% E% |8 c9 Z7 f
    1   0 days 00:04:20
    . V: T% x% ^1 p  P" I- i7 v2   0 days 00:05:22
    5 u5 J5 |7 b+ o5 o  y! e3   0 days 00:04:08! L* H0 b% Q& o2 |3 L1 h8 m+ y
    4   0 days 00:05:229 q2 I  L* [8 Z9 k' ]
    Name: Time_Record, dtype: timedelta64[ns]
    " t! S7 J$ j: V- u2 w1, Y3 b! x0 j8 t4 _
    2
    ) N6 K( B2 a& `) A3" C" a3 S7 V9 b, C
    4
    0 z2 l- r$ ]& P  C) X9 v5. s' L" |6 Q5 {! G% M2 s0 W
    6  P- ~2 y' G  {- m, R
    7
    # h' O% k# v$ G! R6 f  \9 o8
    1 ]/ H" U6 D2 |1 V/ @( Y9+ I2 a5 z0 H: f$ m( m- D1 f7 \4 y; c
    10
    8 K# }' W2 g8 t* i( S7 f# ]与date_range一样,时间差序列也可以用timedelta_range来生成,它们两者具有一致的参数:
    / H7 ?: E# ]% U( j6 C. b" bpd.timedelta_range('0s', '1000s', freq='6min')8 C; u2 O$ |0 w6 ?$ K5 A3 s# @( S
    Out[62]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:06:00', '0 days 00:12:00'], dtype='timedelta64[ns]', freq='6T')
    ! ~7 P  M. O) `* j; L% C3 O% j" E6 ]  R% D* l7 f- b
    pd.timedelta_range('0s', '1000s', periods=3)) j* a6 s9 w% R, F( m( i  q, g3 W
    Out[63]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:08:20', '0 days 00:16:40'], dtype='timedelta64[ns]', freq=None)& p3 ?) g1 M3 }3 ], U
    1
    , B4 n2 s/ q3 W$ ~+ r, l2
    ) J- T5 K$ O- r' @' [+ M3
    & @3 }5 E; Y: E: p44 R0 T% R1 _4 X% f
    5
    / B; p. P8 ~& L. Q' a/ L1 N对于Timedelta序列,同样也定义了dt对象,上面主要定义了的属性包括days, seconds, mircroseconds(毫秒), nanoseconds(纳秒),它们分别返回了对应的时间差特征。需要注意的是,这里的seconds不是指单纯的秒,而是对天数取余后剩余的秒数:
    3 h+ L  _1 v$ v2 K0 |# b. ?9 {s.dt.seconds.head()
    . X% ]6 O7 q" _0 r8 LOut[64]: 8 p8 w( I) l1 g! L5 z
    0    274$ m/ Q. O+ g* \* d" m9 x
    1    260
    # O5 I+ V( J$ X$ A% q3 k5 n2    322$ Z  T+ e8 L/ ^8 p) B# Y9 z
    3    248
    $ m2 I) ^" G; k3 l! k9 k& c4    322
    0 o' W; y6 d- p/ S1 ?Name: Time_Record, dtype: int644 r+ r/ f1 H! M- r/ {2 L
    14 W5 N/ c* d3 n7 b$ u2 o" [
    2, ^) f, p* e$ T
    3" B/ {/ }2 z3 P0 l. T9 s
    4
    , ~0 ~% I" s. b5
    ' n/ X  l& |" J1 n8 H. I! e6% [4 f2 ~: t5 R
    7& D9 w4 L6 W; W+ @- H9 N
    8
    ( g" S/ e. y% w( t" u. k, v: \, k如果不想对天数取余而直接对应秒数,可以使用total_seconds
    ( s8 v+ N. b& P9 |& s: }+ ?+ |; f: l( z! _
    s.dt.total_seconds().head(). S% n. \4 O  l4 E; r7 ~& S
    Out[65]:
    " F3 ~* B4 G& k% I0    274.09 |' k- G+ O7 ^; E" c3 N8 ~7 Q( X
    1    260.0
    2 F; Q% M* @; Y' e8 x5 n2    322.0  M/ k6 ]) l. b
    3    248.0
    ' q; I, N+ ~+ K: t4    322.0
    6 F" i1 A6 J! ?! T1 T) X4 v, D" }Name: Time_Record, dtype: float64& \) I4 X$ T$ R) ]4 _* N1 ~
    1
    - Y, J) ]7 h0 F2 j2 _# ~" q$ t2
      u4 j6 c2 \8 r0 L) U; r) X- ]3
    9 _$ p" S% e+ S6 A* @$ a# u4
      [& U& x! s  e" j2 L# L2 n8 b- x5, l  d$ o2 I% t$ h0 p6 Y- T
    6
    ! A8 ~! c9 u4 p! b8 {0 Z75 p7 F9 E/ H& q5 ]
    81 R7 C% A6 n" l, Y1 E! }
    与时间戳序列类似,取整函数也是可以在dt对象上使用的:
    ( z9 T9 y  j, |% ~( c1 O* ^
    % \& P9 U$ u& ?' n/ P2 Ppd.to_timedelta(df.Time_Record).dt.round('min').head(). m) l+ y6 c; X  L$ t  w0 V
    Out[66]:
    0 b3 Y# P: X7 \6 y# b) u# u2 S0   0 days 00:05:00; V7 A- K! C( m/ c# d3 g% x
    1   0 days 00:04:00
    & a" n2 U3 L6 u, r+ F& x+ k' y2   0 days 00:05:00
    * a1 m+ h+ g3 q3   0 days 00:04:00$ r3 D  ]0 d( t; |5 ?* y" y
    4   0 days 00:05:00
    : H" S& V1 N. K' x9 k6 SName: Time_Record, dtype: timedelta64[ns]0 Q6 b+ i2 c( k4 c9 b8 |( v
    1: n( {7 i' b2 m# t7 J
    2$ N/ E2 O( z/ ^4 P+ o4 \
    35 L' E/ P# P" U4 {$ ]
    4
    $ X8 c4 s1 c3 D1 T8 k52 b2 \9 `$ J1 M
    6
    3 `6 G1 C* \4 d' i$ U# Z7
    1 d: |/ i  ^1 f. p$ R( H$ i8
    1 @! ~( r8 O! c2 U+ v5 l3 u10.2.2 Timedelta的运算5 _' P" O2 [8 K* l& E
    单个时间差的常用运算,有三类:与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算:
      a1 Z7 Y8 {2 b  ]+ ^: O3 P' Ztd1 = pd.Timedelta(days=1)
    8 a" G- L, y4 ^8 {& M# x, Dtd2 = pd.Timedelta(days=3)
    1 m  ~! J* M: p& Z( A  w7 F6 P$ Kts = pd.Timestamp('20200101')
    4 m  }0 h# N1 ?& _1 k* L! Z* ?: P9 Q5 l- t2 ]3 |1 H. |4 I4 e
    td1 * 2
    , \+ J5 c" l, o2 s/ w+ e) H( TOut[70]: Timedelta('2 days 00:00:00')8 f; M$ }, z0 j& g
    ' m, q$ l) Z1 Q* ]
    td2 - td1
    % b4 M( E7 H8 @( d1 x0 OOut[71]: Timedelta('2 days 00:00:00')' t: O/ }1 _2 ^$ j
    # [, `4 H3 J* K; G4 }. d% J, W% i
    ts + td1
    " K( t) U8 @: w# Q$ ~' g; fOut[72]: Timestamp('2020-01-02 00:00:00')
    1 Z9 V$ C- Y+ s9 N
    6 L7 z: w" \/ k, E! n$ Ets - td1" c$ D* M( C, u* @3 s; Q
    Out[73]: Timestamp('2019-12-31 00:00:00')
    % Z  P" @! \" H- O  |1 H1# g1 _1 t4 U8 X
    26 i- w: x7 T/ ^9 h$ L, {  R
    3
    $ [% N! O! T3 c% ~' J; @  |# W4. P: u/ z% P# Y2 j
    5
    * T$ }0 X' f! m' F. M. a6
    . d/ R2 [* q# y1 l! V7+ t+ ]& B# k, P
    8' Q! \% a) c/ C  _7 O
    9
    ! a/ K- a. w& O/ \10
    ! G5 K+ ~4 Z* H: Q11( E3 b$ y# Q# C$ Q
    12
    # z5 X! g( ]; m$ S( y7 o13
    ) Q; o- m& g/ H: a14# i; u0 ?) ?% i% d; _+ [
    15
    * x% Z; D3 d) x% y% k时间差的序列的运算,和上面方法相同:
    - |; {( _$ N% ~- T) ttd1 = pd.timedelta_range(start='1 days', periods=5)
    0 g7 [9 r! b; f! Ytd2 = pd.timedelta_range(start='12 hours',
    & x' j. C* L2 o                         freq='2H',
    3 q5 D  G9 b5 _# C5 q& J3 i7 Q                         periods=5)" S- i4 Z" \  F* g
    ts = pd.date_range('20200101', '20200105')8 v, k/ ^5 p6 S/ O! ]
    td1,td2,ts9 O9 p6 C7 C8 v3 ^: b: d. [
    6 B( e4 U: Z7 N
    TimedeltaIndex(['1 days', '2 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq='D'); c  x. k9 q* N; Z4 y( ~
    TimedeltaIndex(['0 days 12:00:00', '0 days 14:00:00', '0 days 16:00:00',
    # b+ k# D6 ?' F4 w7 C5 S2 ~1 x                '0 days 18:00:00', '0 days 20:00:00'], dtype='timedelta64[ns]', freq='2H')) |8 T4 D. e: m
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',9 v: W; ~7 X- u; F8 t
                   '2020-01-05'],
    / v! ^0 l# r: n+ `0 s! ~6 E( C6 q              dtype='datetime64[ns]', freq='D')
    , f1 |& g. S$ V. t3 Y" t! l15 E0 \6 [' s7 s; L* n
    2
    : T7 |; h6 @4 H8 t% t) B0 q3% n& T2 q. M& u- [
    4
      f3 _& t# o7 g! _. j. H5) m! Y! y  A' ~; s' R" k8 X
    64 S9 ]$ p% N, ~- g+ V1 f# v5 r
    7: |4 S* T+ }% n/ b- S
    8
    9 y8 `* a1 r# J+ _$ ]" L! v3 d9: g8 y  I+ q. ~# B+ Z: Q. M
    108 f( m/ I6 k" i: i- B% X2 Q
    11
    1 ~/ S& ~; {9 E; [* o# r12
    ' A2 q* |$ d9 \9 j130 u6 F. z6 a$ |9 @
    td1 * 5
    7 }* G4 X6 j( m/ g& h* aOut[77]: TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D')
    ! ~  b1 Z. r  l; m! x7 S7 |6 S, \" Q4 w
    td1 * pd.Series(list(range(5))) # 逐个相乘
      ]* c% D# O" Q* ~( _Out[78]:
    5 V/ C: R* ^) I$ m  O1 d! T0    0 days! T% P% Y: {; `$ n+ p6 A
    1    2 days* C- Y  @, k4 ]4 p& @6 t$ h
    2    6 days
    ! |" g! A/ v% J! b+ d% j3   12 days
    $ a8 c3 c+ D" |" ^3 O8 J4   20 days
    " M) M( C: R' e" Z) Wdtype: timedelta64[ns]( G& _  }7 j3 X; |3 j% I9 _

    ( F' M$ N5 b( ttd1 - td2
    8 A$ e" l- Z- }: E8 j: h; AOut[79]: % \1 J& b. l! t, s
    TimedeltaIndex(['0 days 12:00:00', '1 days 10:00:00', '2 days 08:00:00',+ q6 X% i0 G& Z$ m$ q2 E2 [5 c
                    '3 days 06:00:00', '4 days 04:00:00'],3 p3 Y" s' o" H0 N+ l( s
                   dtype='timedelta64[ns]', freq=None)/ N4 r! J$ Y  [' ~

    & c. |% }, _5 ?td1 + pd.Timestamp('20200101'); \. B( @9 I+ ], D7 d
    Out[80]:
    , ~+ f9 a6 H  S7 X$ kDatetimeIndex(['2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05',
    / ?0 j) L! i. E( v6 j( ?               '2020-01-06'],dtype='datetime64[ns]', freq='D')5 ^) i- w8 j. z( {2 O. o$ Q2 R4 y- ~

    ; _+ I9 m3 U" l- B: T4 U" c: b) ~td1 + ts # 逐个相加& s0 n- S$ i3 T  w
    Out[81]:
    ' L" D+ U) t; ZDatetimeIndex(['2020-01-02', '2020-01-04', '2020-01-06', '2020-01-08',/ F  N4 w% y' G5 D- G
                   '2020-01-10'],3 e! I$ t- y+ x6 T5 A
                  dtype='datetime64[ns]', freq=None)
    # N5 C8 u( M2 K) G! C1 L
    ' R- @2 u: m" Y* _: `4 p! ^1! Z* I; u. a, [) Z
    2' ?' n* s) p" m2 V- D
    3% x3 Q# J  W! l
    4
      i# P/ l- ]; r5
    " n3 c7 U3 B2 r. l, l6
    + `+ ?& V' I) G- y- X6 a% n7* m( t0 r7 o# O. I
    8
    5 @, S- A- l- |  P6 x' q; [9( a# d# u- W+ c: f7 Y$ q
    10
    8 w* C4 v) H; \  {9 p* z$ O11
    # I* i2 e9 g6 q; h+ X' @* f12
    8 Z- [. Y$ ]; C5 i, [+ M13
      l3 u  @! F& z8 D3 p14# [! F7 o! S/ {; z5 V; y
    15
    & J; K2 ?  p! S! n( [" i16
    % r+ ~% u" l; R9 v0 L17
    . k+ ~# S+ X+ F, o/ {% F8 N18
    1 \/ `. B, d9 [$ c19
    7 |: j& M1 \( m. _! R* S20. U: H- J/ I, a
    21
    ( L' K' \% I$ w. N$ o' n6 i22
    / t2 a+ Q6 N! e# V1 l23
    . s7 }& _, Y. j! F! L- u/ L24
    0 e2 o6 T4 b- H# H3 d0 D3 Z25+ H+ f" _% j: z* o+ p
    263 n# H  @  H. k" Z8 X# B
    27
    8 _' f# j4 Y' W4 m" j& K. t28
    * d" {% H  j- f3 i. L# A10.4 日期偏置2 J- w% [2 A: W: i7 C* B0 z3 U
    10.4.1 Offset对象* f% F" t& i( M$ _
      日期偏置是一种和日历相关的特殊时间差,例如回到第一节中的两个问题:如何求2020年9月第一个周一的日期,以及如何求2020年9月7日后的第30个工作日是哪一天。3 ^3 `3 ^, K0 J0 [
    . O& Q( i. q& D; P
    DateOffset 类有10个属性,假设s=pd.offsets.WeekOfMonth(week=0,weekday=0),则:. Z0 ^- ?( }; n" z

    ) _# b9 g* r/ ~, Xs.base:<WeekOfMonth: week=0, weekday=0>,返回 n=1 且所有其他属性一样的副本
    1 G5 b" W2 x. }  n) P, fs.kwds:{‘week’: 0, ‘weekday’: 0}6 z, n5 }  u1 b
    s.wek/s.weekday:顾名思义
    6 z7 L  X$ {$ {' o有14个方法,包括:- w9 U( |+ E$ \1 m% P
    / k3 e' @; V/ h! C6 C+ C% [9 x
    DateOffset.is_month_start、DateOffset.is_month_end、DateOffset.is_quarter_start、DateOffset.is_quarter_end、DateOffset.is_year_start、DateOffset.is_year_end等等。
    8 a3 y2 S) j0 v! K$ [pandas.tseries.offsets.WeekOfMonth(week,weekday):描述每月的日期,例如“每月第二周的星期二”。
    4 A3 W: U- [  r5 e2 h# a2 R# `+ ?3 K, O- c8 [! N. S: @; f
    有两个参数:
    1 @' J. b! d. Q% cweek:整型,表示一个月的第几周。例如 0 是一个月的第 1 周,1 是第 2 周,以此类推。2 R: }& B$ l0 I) d1 Y  Y
    weekday:整型,取值为[0,1,…6],表示周一到周日,默认取值为0(星期一)! k4 |; {5 X0 T7 o: J
    pandas.tseries.offsets.BusinessDay(n):相当于pd.offsets.BDay(n),DateOffset 子类,表示可能的 n 个工作日。
    . a4 ~, t2 V$ S- B8 I" ~
    # [& W7 v  S' a& ^& k3 J; wpd.Timestamp('20200831') + pd.offsets.WeekOfMonth(week=0,weekday=0)9 f% p3 G$ Y' M8 `( v
    Out[82]: Timestamp('2020-09-07 00:00:00'). X6 R. j' s* b8 O4 S5 W
    : [- D% |! q7 H. |
    pd.Timestamp('20200907') + pd.offsets.BDay(30)) ?' {" L: E1 B2 H, \, _' d3 R
    Out[83]: Timestamp('2020-10-19 00:00:00')
    & o# q! A' a8 g7 ^+ C4 ~, j+ I1
    $ G( P+ q0 e5 u' Z) u" J# l4 W, S3 ]2
    . M1 W5 t& T. v# g2 P  \3* P' c' D$ l" x* W3 m5 `' E7 u$ X
    4
    + S" Q2 X- d, P0 D% V: ]% K% }7 s& a5/ {. T1 `* a4 Y3 L
      从上面的例子中可以看到,Offset对象在pd.offsets中被定义。当使用+时获取离其最近的下一个日期,当使用-时获取离其最近的上一个日期:8 M8 r- }6 _; B0 U8 d
    / J- I/ j6 d( ]: v
    pd.Timestamp('20200831') - pd.offsets.WeekOfMonth(week=0,weekday=0)! I( r  I  M3 @1 d$ v
    Out[84]: Timestamp('2020-08-03 00:00:00')
    : n) d' D' c& ~) Y+ H
    ; N/ U$ @# t  `) p5 o: o9 E3 t8 Npd.Timestamp('20200907') - pd.offsets.BDay(30)
    # H9 a" X& H1 Y6 @' UOut[85]: Timestamp('2020-07-27 00:00:00')
    ' X- W% n+ C4 @9 t) g
    9 q6 a# Z( E2 ~pd.Timestamp('20200907') + pd.offsets.MonthEnd()
    % U! t; J. k" W) d9 j' `5 e* j7 p- BOut[86]: Timestamp('2020-09-30 00:00:00')/ _% j7 f2 k# U0 M1 d
    1
    / f( Y' o6 t' t# u: P2% o  [( j1 c! O3 S4 m# j
    3
    + z4 l7 \7 H1 F4- {4 _: L3 `: q1 @/ |9 H9 X% Z
    5
    2 d4 U; n. b! J: p* u6/ N5 o/ Q( h! {2 f& [
    7/ L! ^5 [* q/ u
    8. q& ^. [& C( F% j, Q4 |1 [
      常用的日期偏置如下可以查阅这里的DateOffset 文档描述。在文档罗列的Offset中,需要介绍一个特殊的Offset对象CDay。CDay 或 CustomBusinessDay 类提供了一个参数化的 BusinessDay 类,可用于创建自定义的工作日日历,该日历说明当地假期和当地周末惯例。5 v1 }2 _7 Z. h! |
      其中的holidays, weekmask参数能够分别对自定义的日期和星期进行过滤,前者传入了需要过滤的日期列表,后者传入的是三个字母的星期缩写构成的星期字符串,其作用是只保留字符串中出现的星期:
    ; m9 F; b: H3 k$ ~1 f, b2 u  [+ j8 m2 L; S; u' U$ B
    my_filter = pd.offsets.CDay(n=1,weekmask='Wed Fri',holidays=['20200109'])5 o0 r( _4 T/ w" v  h6 n+ Q: N5 {
    dr = pd.date_range('20200108', '20200111')
    8 J7 H' L( O& }" K  q' Q
    5 o3 n+ k- ^" M, q, Jdr.to_series().dt.dayofweek3 ?1 F2 W6 E" w" Z" V* t1 J
    Out[89]:
    ; \" }! r' F( o. g& ~" z+ Y8 k) z: H2020-01-08    2
    0 i' A$ L4 F; |% t" ]3 T( R$ V2020-01-09    3
    # L& O7 g+ T, t3 m. U$ a2020-01-10    4
    4 W) z& D, ?7 P% T/ {: X! S2020-01-11    5: ?: `. S7 L' b: j) ]2 y
    Freq: D, dtype: int64
    " G6 e0 _* |8 c/ u: f. W* z6 P9 M$ Y# Z7 U: f* e7 {7 a
    [i + my_filter for i in dr]
    4 ~6 m. i6 Z4 N5 g6 Z7 h' u; `Out[90]: ! }6 O- ^4 u; |# G; u
    [Timestamp('2020-01-10 00:00:00')," T: m; R6 c" I+ w$ G1 d; d
    Timestamp('2020-01-10 00:00:00'),8 u  |! f0 d( |
    Timestamp('2020-01-15 00:00:00'),
    0 q; G8 h. O, }! a- [5 P- y Timestamp('2020-01-15 00:00:00')]( O$ j- `: C. _2 `* A4 O

    + c# _* G  A) _5 a1 l7 ]1
    & F9 j2 \5 k- N* W6 u9 \( x% [7 b2# b/ [* Z% z. s9 w+ X9 r
    3& V5 x' P. t" f8 e; G1 U5 \
    4
    5 t) U/ r5 k0 \* m4 h1 X+ c5
    4 U. s+ C  K: ~! z6* x2 A3 x2 A" N  K1 U8 U' M
    7# _. G& U9 T5 O. B1 [
    81 ?+ T' U9 D1 `$ y/ `& L) N2 c
    9
    . i( _1 R3 }/ k3 S3 Q- W10
    - E' Y  N+ _% M. V11
    3 I4 u( E; i/ X% C12# v) ^( X4 h4 S, }
    13" Q7 {5 u3 z7 ^9 d# g
    142 Y  B" T% J1 X2 O
    15* T9 _5 K6 @' l8 j2 `
    161 z' t: x/ D0 ?0 L
    17
    ; Y" D% ?: B/ J  上面的例子中,n表示增加一天CDay,dr中的第一天为20200108,但由于下一天20200109被排除了,并且20200110是合法的周五,因此转为20200110,其他后面的日期处理类似。( a+ u# A" C, }6 i
    4 i5 L: A& i% J4 T7 K
    【CAUTION】不要使用部分Offset+ n: G; _( k4 }2 L2 E/ ~; C
    在当前版本下由于一些 bug ,不要使用 Day 级别以下的 Offset 对象,比如 Hour, Second 等,请使用对应的 Timedelta 对象来代替。
    7 c; ~5 t9 V; G% g) d: T# X1 G( ^% y" ]! x  Z( b: w! h3 ]
    10.4.2 偏置字符串/ ~* t' }; x/ G) G2 b6 V
      前面提到了关于date_range的freq取值可用Offset对象,同时在pandas中几乎每一个Offset对象绑定了日期偏置字符串(frequencies strings/offset aliases),可以指定Offset对应的字符串来替代使用。下面举一些常见的例子。) M( B, u3 J' x1 z; _" `) s
    & f5 l5 F9 f9 ]( ^
      Offset aliases:pd.date_range函数中的freq参数,为常见时间序列频率提供了许多字符串别名。 也称为偏移别名Offset aliases。偏移别名列表点此参看(大概27个)。# m  a3 p9 b" U) z
    5 T7 m4 `7 O6 r  C1 x" }" q2 T' [- Z
    pd.date_range('20200101','20200331', freq='MS') # 月初
    & F! R3 w, M+ r! AOut[91]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')
    0 T. W1 \# j4 o, _" ^3 G' m. x: W- D/ ?% H  m0 s; r
    pd.date_range('20200101','20200331', freq='M') # 月末3 f+ r" U/ N' b6 ~5 y/ [# P4 R8 K
    Out[92]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')' f0 k; {* [# ^. ^7 V0 V
    ' p/ |3 _1 U, z9 n
    pd.date_range('20200101','20200110', freq='B') # 工作日
      q. j' \$ u6 U1 R- Y2 XOut[93]:
    # @* s$ {( o+ q* x+ [DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',& b1 A2 T8 X5 w. f
                   '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],
    " I) z7 ]. t. [3 _, H              dtype='datetime64[ns]', freq='B')
    - t) v0 P) u5 ~( v% }; W, i2 c. A# q
    3 F8 W! E* A: m  Z5 a9 p! o: kpd.date_range('20200101','20200201', freq='W-MON') # 周一( \& l/ r  R4 z' E( ?% Y, A" G
    Out[94]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='W-MON')
    , b6 @: V1 ]' A& A
    0 T* G9 e3 K' T9 `pd.date_range('20200101','20200201',
    ( o! d# K% O0 I, k  ^: g( u              freq='WOM-1MON') # 每月第一个周一
    ' U& ]) s% r0 P3 ~2 k: J7 I/ z9 s
    3 ]/ V4 e8 q2 E, y5 W+ S/ N0 g5 GOut[95]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')
    4 g4 J9 N; t% E  G
    6 I9 E' E% {. A# }1 n; S1# d- ^& ?$ y8 g  u4 A6 b
    2% e( d# M5 O$ f8 d
    33 q9 p- B- K  ^2 j& `
    42 W$ g9 o" s+ l3 g/ P
    5
    * a: [6 j# `) P0 u, c6
    $ i" J+ f/ |1 t/ `2 ~8 B7
    5 T4 j8 Y( G/ o: E& w* }81 w$ `' A. N( M8 @! R- t
    9% P, o( [0 X1 {  O( p
    107 r6 Z7 W' a! ^5 s
    11
    . G0 N# l; H/ ?2 ~% A0 [" c8 G0 R+ @12
    8 G! ]1 X$ ?5 z! q" {+ h13# z& y# L* ]7 ~" J
    14
    " Z5 x! e8 M3 v$ E6 J8 k15
    % ]; O, ?( e" D' J16) s, C* I- U9 v* ^4 U  m
    17
    " a- r/ K8 P  Y, Y18
    ; a1 x, `* m2 l+ Z; g: U! o19
    ; c7 o1 }. w7 A* {, G7 J* B  B- l6 p上面的这些字符串,等价于使用如下的 Offset 对象:+ }9 J+ @7 O% w' X1 O0 r
    0 ]& r9 k; X$ v7 U0 n& g$ a1 i. [
    pd.date_range('20200101','20200331',
    7 |; f' c, B2 n  k, F              freq=pd.offsets.MonthBegin())
    * M2 C1 C% n: c8 i# C% v2 L. @2 }' d' w
    Out[96]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')
    9 M( A; g; w$ C* {* {: n: [/ w5 \1 q: u- ?7 u' _  Y% Y) T* P
    pd.date_range('20200101','20200331',
    8 R% }2 T* _4 M2 J              freq=pd.offsets.MonthEnd())
    ' W7 U, z4 t( n3 o# [; B, e$ L. X/ E# c% \+ j
    Out[97]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')
    $ Z* g( _1 j9 r0 n
    : o' Y" T: o0 l% rpd.date_range('20200101','20200110', freq=pd.offsets.BDay())- \) {* a' R4 t9 L( s
    Out[98]: ! n! t# g' N6 k- ~
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
    . T4 h) Y! N, D9 o! W               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],
    : @" l* G- k$ p2 Z              dtype='datetime64[ns]', freq='B')9 \1 _$ e7 M3 r

    ! m% t& L2 s% }& ?pd.date_range('20200101','20200201',
    , T6 o$ a9 ]: f  o3 F" _              freq=pd.offsets.CDay(weekmask='Mon'))
    6 ?" L% ^' |5 q, D% N
    1 \! x) t0 b6 ]Out[99]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='C')
    % T6 D# f* K% ~  }
    " s. W& ?! P* p: Y7 n! Tpd.date_range('20200101','20200201',2 Y. z+ f& y* O/ q4 ^$ l
                  freq=pd.offsets.WeekOfMonth(week=0,weekday=0))
    6 O2 G' A0 M& R9 M' s# ]
    : Z3 w/ x! s2 p7 f5 _2 m* v% B/ |Out[100]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')
    $ e# e% r  g* ~$ m! }! G
    ; s, [- I0 U/ X. \0 {1
    ' C. Y- Q; e/ u1 D: {2
    . I$ U' r, ~' @  W! e; m3
    0 [3 U% o1 o: Q9 b* ~4# u% G; w( `8 j6 \7 M2 w' w) _! M
    50 x" y8 ^$ i6 ]5 N& g$ ?* {
    6! V  g% O9 A8 {* h6 ^2 S
    70 [9 ]- l, O9 u8 f8 g. d
    8
    . C. u) O* n8 p9
    ( t; w$ R9 t# `! B10& i  ~: s" G/ ]8 v5 Y
    11
    # i# f, l: n; r4 }122 F- r( C: D% h- d
    139 K, M# Y$ y; F5 R1 z
    14/ I% `5 D0 {" U' `
    15
    1 d7 U5 t) {, ~4 H16: z# u5 s- j! k9 ]8 a, w: n
    17
    + P* M! M1 W, J. g4 C. r/ ~18
    & ]" n- ~+ {  r4 C% R7 y19
    1 v: D- e1 W% j3 n: q% \5 x202 o$ L" C4 V1 m% A, g. I0 A; B5 C
    210 r) S! S  Z1 t9 Q- c# a
    22$ b- r: L* U  @. y! Z( n
    23* R( D' ^  U  s( ?3 \0 O
    24
    4 D0 p) P: O' I- b/ @& u! B25& O* R# }0 \& C. _9 a
    【CAUTION】关于时区问题的说明
      V' Y3 i/ x4 z. l/ R* I  各类时间对象的开发,除了使用python内置的datetime模块,pandas还利用了dateutil模块,很大一部分是为了处理时区问题。总所周知,我国是没有夏令时调整时间一说的,但有些国家会有这种做法,导致了相对而言一天里可能会有23/24/25个小时,也就是relativedelta,这使得Offset对象和Timedelta对象有了对同一问题处理产生不同结果的现象,其中的规则也较为复杂,官方文档的写法存在部分描述错误,并且难以对描述做出统一修正,因为牵涉到了Offset相关的很多组件。因此,本教程完全不考虑时区处理,如果对时区处理的时间偏置有兴趣了解讨论,可以联系我或者参见这里的讨论。0 I4 k; u' e7 I
    2 B4 w: {$ a% G( i) ~! G# e9 ~( @
    10.5、时序中的滑窗与分组
    $ J* ~5 A- `4 Y  K4 A10.5.1 滑动窗口
    , H9 Y. D5 i" @: N" M  所谓时序的滑窗函数,即把滑动窗口windows用freq关键词代替,下面给出一个具体的应用案例:在股票市场中有一个指标为BOLL指标,它由中轨线、上轨线、下轨线这三根线构成,具体的计算方法分别是N日均值线、N日均值加两倍N日标准差线、N日均值减两倍N日标准差线。利用rolling对象计算N=30的BOLL指标可以如下写出:) R% U: y: K$ Q" X
    6 Q8 ]1 @& ^6 p$ t9 U8 w
    import matplotlib.pyplot as plt3 _- D. R2 R: U9 e( k/ ~
    idx = pd.date_range('20200101', '20201231', freq='B')& m' F: O6 s$ D0 m- B5 ^( n
    np.random.seed(2020)0 p) _# N$ J6 ]1 k8 e1 M1 W
    0 ?* }0 y9 z0 b* i4 ?
    data = np.random.randint(-1,2,len(idx)).cumsum() # 随机游动构造模拟序列,cumsum表示累加
    . \  Y& l  L( Q0 ]' l& Z9 c9 b+ hs = pd.Series(data,index=idx)4 P. x3 |0 [; [9 N
    s.head()3 k* F: s- W9 ]
    Out[106]:
    $ F1 ~2 Z2 [$ ?! v! e2020-01-01   -1; S; n" b) S& O) X) m" I
    2020-01-02   -2
    3 [, l0 j3 Q( V1 N& N2020-01-03   -18 O% X# v9 |; n% ]/ d
    2020-01-06   -1
    6 t$ [) d( ?0 ~. V% \* E1 U5 x2020-01-07   -21 {* g: n5 A9 k( P  d: |; V# O
    Freq: B, dtype: int326 G1 b& T/ U2 ]* M* A# v
    r = s.rolling('30D')# rolling可以指定freq或者offset对象
    ( {: w, E6 X2 [' V; _+ a3 k' o9 M; B: |5 }3 d$ X. W( F7 f, V
    plt.plot(s) # 蓝色线
      c0 I9 J% ?2 s, D# g# n; oOut[108]: [<matplotlib.lines.Line2D at 0x2116d887eb0>]
    ; C. `, W0 A8 @$ Iplt.title('BOLL LINES')* N/ n. _: C2 g" |
    Out[109]: Text(0.5, 1.0, 'BOLL LINES')1 |5 f' d: ~0 k3 t! P* _
    ( o. E0 l! O) [' f
    plt.plot(r.mean()) #橙色线
    $ K7 k4 k' P6 u0 l  [' y4 P* Y! @Out[110]: [<matplotlib.lines.Line2D at 0x2116d8eeb80>]
    5 V4 D0 @  L( U0 v; t# T
    0 n. Q, R# j3 C0 P/ R/ E% Hplt.plot(r.mean()+r.std()*2) # 绿色线- Y0 ~  n+ n7 H' E4 V
    Out[111]: [<matplotlib.lines.Line2D at 0x2116d87efa0>]+ g+ r2 b. ]0 s' ]9 W+ u9 Y+ ?

    6 ?2 d0 m, l" Y2 fplt.plot(r.mean()-r.std()*2) # 红色线
    % }, k. y+ h4 ~$ i! Q5 _) _5 kOut[112]: [<matplotlib.lines.Line2D at 0x2116d90d2e0>]. `: T6 i/ P+ G, T1 y! A
    ; y3 U. Y6 ?( v: z( Y
    1$ q2 i3 S8 K( v. F! |
    20 D% I" b3 A) W, b0 R
    3
    5 E$ ]8 s) B# K: x* d& d4. c" D, ~0 P% V
    5$ H* c% Y1 _( Q1 O2 p' |" |
    6
    . @5 h5 I/ A1 H. m& q4 t" D! ]73 `% i/ o3 I, k6 y0 e8 E1 s
    8
    : S7 F0 c7 f1 @) ^91 U4 p1 P9 S+ s* N
    10
    & {" D2 |" P. G9 P5 w11. f6 {; h0 {- Y# R& m
    12
    $ H8 D5 p  z2 W+ j) v' Q) I130 i: Q) I4 t% I; J
    14* k2 U. Y# X; C; A* k
    15# N" T5 V7 C5 [+ `0 E
    16
    0 O6 {6 G" h6 p+ B17
    # M+ m& e  d, b' Y, q+ O18
    % n; m! |8 M8 A9 j/ K* G19& Z! E! `( M+ r
    20: p, E7 }+ A/ x1 x  a
    21
    8 u# v0 Z4 s3 |5 W) F22, y; \# M& f" F6 L. F' I
    23
    4 i4 L- _7 s) L( X+ b24
    . O# X, R% x9 k: w+ i0 B25% W3 O0 q2 z1 |0 k/ @' p
    26
    & G9 O* z2 y: b9 ^% J% ?27
    ! y- ]- M7 g7 H% [28
    $ v" L$ O( O" J( z- ^29
    2 O: t+ \  b, B% f- e, R
      P/ p* z6 R) t; L# ]   这里需要注意的是,pandas没有实现非固定采样频率的时间序列滑窗,及此时无法通过传入freq字段来得到滑窗结果。例如统计近7个工作日的交易总额。此时可以通过传入多个函数的组合来实现此功能。
    % K" s8 F* P& ~6 ^7 p# _7 W   首先选出所有工作日,接着用普通滑窗进行7日滑窗加和,最后用reindex()恢复索引,对于双休日使用前一个工作日的结果进行填充。
    " b' f9 j1 ?4 @$ P4 m
    3 b: e6 u8 p: \( ^select_bday=s[~s.index.to_series().dt.dayofweek.isin([5,6])]) h' w" H( Z- a0 a
    bday_sum=select_bday.rolling(7,min_periods=1).sum()* i# ]9 V7 f, [* W, A* t
    result=bday_sum.reindex().ffill()
    ! O7 |" \& `7 m2 s; W' r: R  cresult
    " G+ Z' _3 _6 R: i# @6 T
    0 D$ @' }2 r$ j3 |! L3 O2020-01-01     -1.0
    6 [" T% k" x! Y  P1 r7 h2020-01-02     -3.0* e' {7 Q8 L& G6 h/ N: O
    2020-01-03     -4.0
    : V" V6 @0 u. j) y( E) V2020-01-06     -5.0
    4 |; @) n; |$ L8 |2020-01-07     -7.0
    ! O/ n* \4 D- X$ D; _, n" t              ...  1 m/ J9 M  A& D) n
    2020-12-25    136.0
    2 _% ]5 y9 F4 N2020-12-28    133.0
    ; y( \9 k3 n. P" c6 r2020-12-29    131.0
    " F3 O! ]3 j- S. V2020-12-30    130.0
    3 G" @3 h8 n9 M4 j1 A( w" N+ Q2020-12-31    128.0" n7 k2 G2 a) D  n! R3 d. b! T! E& U
    Freq: B, Length: 262, dtype: float64
    ) A5 ^: |( E9 b2 |
    7 w/ u1 V- F1 B, b" S* K' D11 }* y# ?& j* v. c% C+ V, Z% r/ r
    2
    8 l) d* e  E) u6 h3
    0 h4 ]. |! v4 x6 M/ P4
    2 `$ o, u. F3 G! H" q3 y4 U* d5 K5
    7 A/ b/ Y0 ]1 {4 y+ _$ w6! d/ D4 F9 V% f: Z: M, C
    7
    7 [' ^2 t5 y  T1 d  u2 x8
    - r, g6 x; R0 ^# e9
    - O% J3 u6 X* h! g# E10
    : s" c1 c4 A9 c: y11+ b  \* b. L0 A+ y8 A; e: [
    12$ i- Q& J' d1 `
    13
    5 e- u, M% w2 K  B8 ]14' }0 i7 B; u) v' Y& t1 m4 E6 ^
    15
    & k8 q; ?3 p: s+ T- e& Z$ `16' L; R8 C, j5 A8 P; ~
    17
    , E0 i/ R8 l! R( I0 ~& D  shift, diff, pct_change 是一组类滑窗函数,它们的公共参数为 periods=n ,默认为1,分别表示取向前第 n 个元素的值、与向前第 n 个元素做差(与 Numpy 中不同,后者表示 n 阶差分)、与向前第 n 个元素相比计算增长率。这里的 n 可以为负,表示反方向的类似操作。
    5 G( u1 |6 k5 g" X& h
    5 X+ b3 m: v4 u# W7 z  对于shift函数而言,作用在datetime64为索引(不是value)的序列上时,可以指定freq单位进行滑动:! i6 _+ }0 e6 m1 \; H8 B
    " e, l5 h6 j1 \4 U' k4 @! {+ ~$ M
    s.shift(freq='50D').head()
    , b! J( I/ H1 ~% h( j/ kOut[113]:
    , Z& i$ ~7 |& @2020-02-20   -1
    6 t, b. D) K4 m4 l+ T( f2020-02-21   -24 |# Z9 R! W# {. D: s9 I
    2020-02-22   -1
    ! C- ^1 ]/ K2 Z& U: {$ T, h2020-02-25   -1
    , |0 c  A* X% O6 a; I8 A2020-02-26   -29 S. G) h' g% u/ E# N8 B* v) ?
    dtype: int32# \, k# R2 u8 z; k+ {
    1
    * ?  p) w7 M; a: k5 f- q21 Y8 L1 S0 ^& O! M7 d, j
    3
      b. [7 O# s/ G. @/ A$ n4* d) k7 b8 i) `& ?7 }6 n
    5) z1 ?3 Z: v% h: t) o
    6
    ) @% _' ]8 `1 C- G+ ?" @7
    & q0 G5 G6 c0 S+ B& r/ u87 c, j) @0 ^# q/ K: S8 Y
      另外,datetime64[ns]的序列进行diff(前后做差)后就能够得到timedelta64[ns]的序列,这能够使用户方便地观察有序时间序列的间隔:
    % d* ?$ o- L! q+ |, y' k( Y4 f% p+ o- j- z
    my_series = pd.Series(s.index)
    + [% A0 u5 S, ^# Gmy_series.head()$ w( F9 v8 `7 d+ q9 {. K
    Out[115]:   ?# l5 G% m0 f: w4 i& L
    0   2020-01-01! q" ~) j0 w' A2 N' G
    1   2020-01-02
      m# s7 x* K! k+ c8 d2 F1 D2   2020-01-031 x1 F2 Y  j- I- ]! H7 a- U' u/ I
    3   2020-01-06! @5 e) z1 S; U3 Y4 J
    4   2020-01-07# q% C; E% a) I- _( {" F
    dtype: datetime64[ns]3 m' Y! }- w; J% ~( [) s, P9 E) x
    + D& f  Z. H0 E: ^# J
    my_series.diff(1).head()/ {+ n7 U0 r( g* T5 |* ]+ |9 m
    Out[116]: ( G: L5 ~4 D( a* y( [* v
    0      NaT7 ]* v% w+ j/ T* ]
    1   1 days* M! |' W4 z% S3 M
    2   1 days# y' K6 H9 C# u
    3   3 days/ K! T, u% I" {1 v9 B8 f
    4   1 days* Q6 x/ ?+ Q. G0 P* y
    dtype: timedelta64[ns]
    - w* T6 l+ i- d0 W+ o6 B$ }% }, z: R7 C5 t- w( b
    1! n/ {" S! K1 \; a$ A) f" R; F$ w4 r
    2- D- N! C1 J; _" L
    3/ D" h7 \$ p4 b$ a+ f# [6 B+ ~5 s
    46 W  |! B/ u3 h" w. c
    5
    / R$ S3 f6 }3 q$ E6
    ' ?1 O4 f- q8 r3 w- {' k7% B- ?( v- @5 `/ N+ `4 q
    8
    , i, Y9 S8 d1 L, l! ^! i9
    " d. L# ~$ D5 |0 Y; n. z; W10
    & q6 b/ u6 f, t- G9 h11% [7 k9 {/ b- V% x  P! ?& r8 ^1 d
    12
    6 Y+ g. z, u. r0 z% z13( f5 j$ N$ G; f- }' ~
    14
    7 W- u9 w; Z: a+ u% h: m15
    9 j+ U5 U$ C$ ?4 I0 ^- G16; e3 @  b# ?! s
    17
    * j& v; i3 s1 G% n+ p% X, a# ~18
    & D) f- f7 v4 _" G8 q9 [" k" g10.5.2 重采样# |& a: z3 v: Z
      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)
      p9 N7 R) O# w, ^常用参数有:
    - y# r+ @( w+ I9 ?( w6 H3 x9 V! h  Z( o2 I! D6 H$ j1 K
    rule:DateOffset, Timedelta or str类型。表示偏移量字符串或对象2 \& O) c2 ?- n  b  G% k
    axis:{0 or ‘index’, 1 or ‘columns’}, default 0。使用哪个轴进行上采样或下采样
    / o( y' R* g+ B, jclosed:{‘right’, ‘left’},默认None。表示bin 区间的哪一侧是闭合的。所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。; U" B- c$ q/ v8 M) H5 _
    label:{‘right’, ‘left’}, 默认 None。hich bin edge label to label bucket with,所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。
      @# z4 P. c- {9 _convention{:‘start’, ‘end’, ‘s’, ‘e’}, default ‘start’。仅针对 PeriodIndex,控制是使用rule的开始还是结尾。
    ' x" j  I3 {# [4 @4 ion:字符串类型,可选。对于 DataFrame,使用列而不是索引进行重采样。列必须类似于日期时间。
    , C1 G; o1 k8 nlevel:str 或 int,可选表示多重索引MultiIndex的级别,这个级别的索引必须类似于日期时间。
    $ p' n+ G# j. t, \. y2 jorigin参数有5种取值:
    " o& ~. F: k% |4 t$ _3 q‘epoch’:从 1970-01-01开始算起& V% ~5 \% Q# p" u9 I. u
    ‘start’:原点是时间序列的第一个值
    ( B3 H: ^1 t4 t: {5 e/ |‘start_day’:默认值,表示原点是时间序列第一天的午夜。
    * w# D: _" J; p- u3 ~; g: Q'end':原点是时间序列的最后一个值(1.3.0版本才有)# x0 c2 @2 Y) J1 t/ Y6 h  S
    ‘end_day’:原点是序列最后一天的午夜(1.3.0版本才有)
    , J0 }" Z! u! ~/ r8 G1 O) boffset:Timedelta 或 str,默认为 None,表示对时间原点的偏移量,很有用。
    % `: B- e4 [- I% ?) B  closed和计算有关,label和显示有关,closed才有开闭。
    5 y% m& w8 |6 R; `( L) g  label指这个区间值算出来了,索引放区间的左端点还是右端点,closed是指算的时候左端点或右端点是不是包含。
    7 X0 I/ P: Q! S& e# G' K( D* [3 P3 }1 {
    重采样对象resample和第四章中分组对象groupby的用法类似,resample是针对时间序列的分组计算而设计的分组对象。例如,对上面的序列计算每10天的均值:6 S) K: N- C1 U6 F) |
    s.resample('10D').mean().head(). y. ]' L* j# I7 w9 s5 r. b
    Out[117]: & N) `, ^; T: \, W
    2020-01-01   -2.000000
    6 M- r8 l+ l( Y0 R5 c0 _1 a- ?: Y2020-01-11   -3.1666673 d: \0 z8 G7 b: [0 Z& f; A
    2020-01-21   -3.625000
    * U( ?$ u& K: c5 e" R& B/ Z2020-01-31   -4.000000
    7 R8 S. h. q: S8 y: l/ J& n% _4 \2020-02-10   -0.375000
    ; a8 D5 [3 Z4 r/ Y3 s( R9 BFreq: 10D, dtype: float64
    / F" [2 v* N6 O( y1
    3 Z) C% g) e( Y) w; z2 W6 z2
    $ h* e8 t8 B5 D- q; p. C3
    ' F$ t8 ^7 P! }6 v1 ]8 c4
    * G2 s% [: I3 B7 S  m9 P# |5
    8 o' t: E+ A  r2 R# Z# @9 P66 g+ [9 t. j& v+ L2 N
    7
    . R! H7 b, T" {( ?2 g8' N) s; w% ^; ^$ W
    可以通过apply方法自定义处理函数:" U" d) ~$ D& H& e! F9 z
    s.resample('10D').apply(lambda x:x.max()-x.min()).head() # 极差" s% s  t4 B; D0 ~

    4 k: O8 R; U/ A9 F, Q' ^9 h" H) ?Out[118]: " S; Y  B: a3 l
    2020-01-01    3
      ^' V9 W' n* F2 I" P2020-01-11    4
    3 G8 ~, K5 G# F& M. V0 b9 _4 u  H2020-01-21    4/ u- Q4 }, N% a3 U
    2020-01-31    2  n$ @8 F5 s2 S' b7 N) ?
    2020-02-10    4
    0 |: f. |& @" N, s4 pFreq: 10D, dtype: int32% J+ `1 C# h, F( c- n
    1
    ' f9 _% A, `  ?2
    1 X0 R6 a  w; J) ~1 A3
    $ m" U+ m+ V' [- q! u; L0 T, C! m$ x4+ L$ Z/ k3 S$ o* U" i/ w* m# c
    5
    7 x: ]6 ^4 [0 t# H2 {! y& K: i6
    7 G5 x# G: R5 z' W7
    9 s  }" g( \  B6 [' ^( L89 r  e: U* u! Y, J
    96 |4 L" c. `/ n7 I
      在resample中要特别注意组边界值的处理情况,默认情况下起始值的计算方法是从最小值时间戳对应日期的午夜00:00:00开始增加freq,直到不超过该最小时间戳的最大时间戳,由此对应的时间戳为起始值,然后每次累加freq参数作为分割结点进行分组,区间情况为左闭右开。下面构造一个不均匀的例子:
    & s4 ]* _( Q7 P- c1 G( b& L4 h& n; O% }
    idx = pd.date_range('20200101 8:26:35', '20200101 9:31:58', freq='77s')& O+ p5 M) E: a# t9 N) B4 P0 N
    data = np.random.randint(-1,2,len(idx)).cumsum()6 Y/ S' b, |- z2 E0 {7 A
    s = pd.Series(data,index=idx)
    5 a' c$ v0 |; X+ Q+ _- t5 }; |9 hs.head()
    . c! {# @& S- m2 o) j0 f) }, F+ d
    ( L4 q( g( d/ T7 E7 t3 mOut[122]:   Z/ J) w6 e! W) |; ~
    2020-01-01 08:26:35   -1
    % K3 y4 ]4 t9 u$ i) P2020-01-01 08:27:52   -1
    - v+ @0 n- q* x6 P4 z& a3 ]2020-01-01 08:29:09   -2
    ' t; ^4 x/ N: a. V3 @6 q& i9 Z2020-01-01 08:30:26   -3
    & ^% W2 Y- ~0 j; z: L5 p* l2020-01-01 08:31:43   -4: m0 D7 L% f* Q5 [* l% X1 r
    Freq: 77S, dtype: int32
    / C4 P* n7 ]2 E# @* _8 N6 ]1* s$ Y  Q/ u0 P& e
    2
    " Z5 A* d+ J# H1 c3( I9 S+ _- k9 o8 e# Z! d/ P6 g
    4
    7 [4 [1 J: G8 D8 m3 i53 B1 e; C; i6 a. k2 D/ c0 A
    6
    ( z$ g* z$ \9 y3 q$ B+ U73 ?% R% V, E, w. }  W
    85 d: _$ b. c/ ]
    9
    0 t& x9 i: N$ L. j# H5 ~- ?0 e7 U10* S3 p! Y* p+ w6 `3 K' f8 {
    11
    ; M0 s: O" ]# P. y, p12
    , v/ p' p7 ~( t  [  下面对应的第一个组起始值为08:24:00,其是从当天0点增加72个freq=7 min得到的,如果再增加一个freq则超出了序列的最小时间戳08:26:35:
    ! L$ ?) V: q# w& v9 P! h3 t
    # B. W, z0 L! Q6 a0 P* G2 o+ Ls.resample('7min').mean().head()
    & s: d5 A# V% P6 K+ P8 `. H9 s( G7 VOut[123]:
    : E0 n6 V8 t6 Y" I9 P2020-01-01 08:24:00   -1.750000  # 起始值,终点值包含最后一个值
    0 `# g$ }/ ^1 c9 ]8 \2020-01-01 08:31:00   -2.600000
    ( _+ \% |# J0 W0 A9 A2020-01-01 08:38:00   -2.166667
    . Z& n" a: g, t1 s* g+ x2020-01-01 08:45:00    0.200000: K0 m$ P# \1 W+ h& z3 B6 x' T* S
    2020-01-01 08:52:00    2.833333, U# G2 I9 O* c9 Z( @
    Freq: 7T, dtype: float64* n, I, E& n/ n* ]
    12 b- V) H/ s6 N3 q/ q* P
    2
    9 M3 S5 E! |' q; F3
    - X% D8 c  ~9 V3 V. `4
    7 D9 V, @6 H! d! t# r+ S2 ^1 P5
    6 |: ?0 p& J) Z: ?6. Y7 ^  Z$ G* p
    7
    0 E+ C% ~( V' g3 k6 e; n8
    - t5 F  b+ j. f& C7 B  有时候,用户希望从序列的最小时间戳开始依次增加freq进行分组,此时可以指定origin参数为start:5 t- `9 F1 |9 @8 B) H# _& D; L* u  U

    : W% U4 r( a1 y5 S5 N$ e! xs.resample('7min', origin='start').mean().head()
    8 J, g( E  U3 S: z% v, nOut[124]: / c) g9 {  {6 w) x4 \8 A4 A3 o
    2020-01-01 08:26:35   -2.333333. w9 R* a' H7 e9 Y9 g9 _9 q
    2020-01-01 08:33:35   -2.400000/ ?; j/ x- P# I. X8 a4 @
    2020-01-01 08:40:35   -1.333333
    6 x' h. a' X5 I8 J8 T5 v2020-01-01 08:47:35    1.200000, z' ]' L! ^; z! l1 m
    2020-01-01 08:54:35    3.166667. j( ~2 S  P9 Y' x5 v! x
    Freq: 7T, dtype: float64
    + v$ y# N' c% P+ y& m' q8 Q1
    : Q$ ?/ s! A7 \3 f- V0 B- {24 V/ W) R2 R' G) j8 `9 _2 T; @% M( Q
    3( f6 i, P& c) M6 {
    4
    $ ]2 a; V, n% c, v2 p! u5 U5
    + N& O  [5 D8 x) x9 D3 d; U$ D6
    0 m- S* ]3 ^& @& f7  Q7 A* g( ]; m, V
    8
    ' r# B. r8 d: W, b4 e* a  q3 e% c0 Y  在返回值中,要注意索引一般是取组的第一个时间戳,但M, A, Q, BM, BA, BQ, W这七个是取对应区间的最后一个时间戳。如果想要得到正常索引,用’MS’就行。7 t8 f5 U: I4 b

    % d3 c) N& g' G9 q0 @8 ws = pd.Series(np.random.randint(2,size=366),
    4 s8 c" M# s5 e+ X% T              index=pd.date_range('2020-01-01',! U6 p$ U' J) ]7 o8 K
                                      '2020-12-31'))
    5 L0 l3 I# X: q6 w
    ! C4 I1 j% [5 D" E9 A, r/ C( E
    6 r! c- I/ Q# k. Ns.resample('M').mean().head()
    8 ]1 k) Y/ T2 w" h* u$ R+ y' wOut[126]: , H: d9 j5 a+ r+ a9 V3 n/ ~
    2020-01-31    0.451613
    ! Q9 U4 H( ~# n9 v6 m* Q2020-02-29    0.448276
    & I, S' w7 b2 c+ T2020-03-31    0.5161290 e9 a' K+ N' p7 J; c
    2020-04-30    0.566667) ?+ f' J6 {. K1 K) K6 F. `
    2020-05-31    0.451613
    # y) V/ a, s" d/ Q, ^. ~Freq: M, dtype: float64
    6 p  T0 p& Y6 D4 P# @$ s; t" S1 @2 J& R1 D3 c
    s.resample('MS').mean().head() # 结果一样,但索引是跟正常一样" w  h  K! T; g0 \0 A
    Out[127]: * r3 n( i0 ^) b  H. b# `0 x8 O) C
    2020-01-01    0.451613
    # d& p9 W# n$ L3 T2020-02-01    0.448276% e1 L' M1 s: v/ r+ V
    2020-03-01    0.5161290 `9 {; v2 F+ G3 ~' X- g
    2020-04-01    0.566667# e2 c' B! o6 Q- L4 }0 Q7 o
    2020-05-01    0.4516139 s" e& j4 h/ I% z; b# ^7 Q
    Freq: MS, dtype: float64, I6 Y1 P  |4 o. I+ e
    1 d' `) S# {9 M: `2 M
    15 D3 f( ^' S8 c! r# j+ o4 s9 \
    2
    3 f$ i7 W/ E3 `! `# @3
    $ ]5 P* i! {" V3 t% \# }4
    : y* p5 v+ h% z3 E- r56 }- q3 c6 g+ w1 b
    6
    / P+ y# M+ W% s& b75 |1 ]/ {9 ]/ B7 q; @% d, f
    8
    7 L8 Y0 ^, _& L. f# i& b6 u& W9' R* s- Y9 Q$ j( K: A9 o$ x
    10! K# z8 Z7 _) C. U2 O
    11
    " `$ D  g+ A& @; ^/ p12
    + u  f& {) Z6 i8 ?+ A; U13# `# j& o7 r1 x( {
    143 L8 J! d" z7 G% M. I8 d( s
    15
    , G4 w( n$ g8 @: _16
    7 U- X5 n  S) f0 u3 _. ^17
    " [3 r' X: t% K; q$ W+ Z* E18' d) Y! F- B, m# O; Z) o
    19
    ( B! _! t4 W% c' f8 F( J# G204 _) e  [3 \! i/ e
    21
    / \1 w0 s( o0 v+ k22. [0 F$ S, y3 n8 r5 ^* a
    对于 DataFrame 对象,关键字 on 可用于指定列而不是索引以进行重采样:
    ' ^, w9 v7 N3 h2 O4 N3 F# qd = {'price': [10, 11, 9, 13, 14, 18, 17, 19],
    " }0 l1 [' G/ G' E7 G     'volume': [50, 60, 40, 100, 50, 100, 40, 50]}
    ( |4 S5 }/ m+ \4 rdf = pd.DataFrame(d)! W5 @# O, u8 I3 J
    df['week_starting'] = pd.date_range('01/01/2018',
    4 p  A$ B5 Q/ P6 J& t$ O                                    periods=8,$ b, L, t( q7 N8 J& ?% |
                                        freq='W')
    " K' T& R  A" d& k' Bdf
    & b8 t' R# `* H2 x   price  volume week_starting
    2 z4 e$ o2 s  m' H2 B0     10      50    2018-01-07' f* j6 b; G# G
    1     11      60    2018-01-142 x; _+ @- y$ }2 Z: t9 M
    2      9      40    2018-01-21
    # J0 `) ?) C5 U1 C9 I3     13     100    2018-01-281 K4 [, W9 C' G' h" V/ _6 {" ^
    4     14      50    2018-02-04
    6 L. z* f) q5 g" k' @% n- ^" j; W5     18     100    2018-02-11
    ) O) P! _  y# ~! z4 y6     17      40    2018-02-18  X7 Y( a2 b  ^
    7     19      50    2018-02-25
    5 B, `1 b; c' W- X/ P$ s+ e+ ?% d8 pdf.resample('M', on='week_starting').mean()
    8 j. H6 X1 X' ~$ M3 I0 ^               price  volume+ W4 _; C2 H- w3 J
    week_starting
    7 }1 g8 u: t- }( o9 }" ]# Y7 Z: J2018-01-31     10.75    62.5
    $ u4 c' `2 f4 [7 D+ j2018-02-28     17.00    60.0
      T: Q9 s/ Y! f& z2 j
    / n* p$ A' k5 V8 c" m% w! g+ K1
    ; M6 b3 g( B* A2
    % L0 B5 S; Q) N1 r  R. F- g3. _0 S" e7 Q% L* s: Z' h2 i
    4
    2 M: M. g* m% [5
    : [8 {3 n6 H- q6 J6 G5 ~; k6
    ! d" }/ C9 B- {3 W- K2 ]7
    ! g5 C& F: B5 H9 K1 V; q84 M6 N0 T& o, G2 G& M, K
    9
    & D; }8 h( |& n5 e# N; u, d: v" U10. j3 |. S# b9 F: Y" k
    11
    , X( i7 f+ k- a& F/ C* x12
    7 f( g2 c, }3 r  W" f135 ?( o- q0 t' b8 P- H
    14: g( K% s, u# J: }
    15' I7 i: {: y, l& U" p
    16
    ; |0 M! B, y, J* j6 @17
    6 ]5 o+ a* n- t/ @3 P" b' E18
    ; b8 \8 g  a% T0 o$ D- n$ e19+ \4 l6 K. p; @5 s+ ]: A( o
    20
    3 C: [" A; L6 S; w( [( W0 l21( c8 ^8 U/ \; H, c( T' D
    对于具有 MultiIndex 的 DataFrame,关键字 level 可用于指定需要在哪个级别进行重采样。
    / y) y" n2 t& w( mdays = pd.date_range('1/1/2000', periods=4, freq='D')% n+ _' ^, x( _# K* I0 W8 {
    d2 = {'price': [10, 11, 9, 13, 14, 18, 17, 19],
    / F/ o! ]  t, t/ Z& Z4 B      'volume': [50, 60, 40, 100, 50, 100, 40, 50]}4 N4 C& i) n9 w5 _2 e* u# ]3 o* |
    df2 = pd.DataFrame(* ~( `3 r+ p. }- r' {
        d2,
    , X' `* @9 i1 C- X) \1 V+ Y    index=pd.MultiIndex.from_product() }* D4 _3 a7 A5 o
            [days, ['morning', 'afternoon']]* `, b, ]4 D/ n' N6 K
        )2 N+ r; L4 `3 x( b5 j
    )9 d3 Z# i9 D8 j- X4 ]
    df2
    0 @3 A2 |6 Q# Z' ^0 l7 T                      price  volume
    & ~! M1 ]  y& |3 U8 j2 _: C2000-01-01 morning       10      50& s- u1 D& B0 e; P0 m
               afternoon     11      60* r' f8 b% N6 A3 O4 n! l7 Q
    2000-01-02 morning        9      408 S+ ]7 m8 X& A* f0 C; ?
               afternoon     13     100
    - z* N" @  H' r0 a1 O, Y( I* Y2000-01-03 morning       14      50: X0 s3 P3 z) u( }/ h/ D! Q9 l
               afternoon     18     100
    8 W% h7 E8 i% O4 ^' p- [; y2000-01-04 morning       17      40. F! f( ^! w' Q3 f/ H8 I9 a
               afternoon     19      50
    ; ]  [7 F+ b$ r$ f6 N( qdf2.resample('D', level=0).sum()
    ! Z( P: i0 a4 J+ u            price  volume3 ^* Z# F& o1 d: U  l; n
    2000-01-01     21     110+ f3 ^. B# W( f) `+ [9 c
    2000-01-02     22     1400 k: r+ b- R  g' P% j
    2000-01-03     32     150" c" c; o8 l6 G0 U+ L" z
    2000-01-04     36      90
    # `3 k5 g' x; Q/ K3 ?: p2 U
    2 H+ ~9 A" S$ V. P1
      E. W; w. e1 k  E6 ]/ Z, e. c) x/ `22 H: ~; t3 z% {" W; ^
    37 K. o' j; v2 M
    40 A& x) j8 d& w/ }; |2 `
    51 ^" l- y; ?: ]* _0 Z( W
    6
    & N; R2 o' c" r# I7
    3 l9 y$ E( a' v9 {( ?& S, h/ v+ [. ]0 f; T8
    1 k' H9 Y3 F7 G# n9
    ! {! N* K+ j, _2 v6 m10
    & C2 U, L9 `/ N# h11; V. w0 \% |# D- l2 K1 F' w
    124 X$ @/ z1 C* \, d8 F' O& u
    130 A& t9 O) L8 F$ g! @
    141 q5 Q7 Z. w" G2 V8 J( e
    150 r% k3 c" M( h0 q5 `4 b2 z( ~' l: e
    16  a. g+ G2 P* l3 z# y8 F" f9 V
    17
    6 I9 ~( h6 X+ ?. v5 h5 ]180 b7 E+ r4 F- Z! w% {
    19
    / J) y( N) j: d3 O$ `203 T0 U% _% q: |& J) }, l
    217 V' \) A+ A8 l( l' \
    22
    ! Q( N) b1 M3 i% F23
    0 ?! O4 ]- d, K* F, U8 Y# i24+ e, M) I4 K2 [
    258 ?7 \4 g# D! c1 j5 x0 J
    根据固定时间戳调整 bin 的开始:
    3 K, r" r  t' ~2 V! r5 Y  Tstart, end = '2000-10-01 23:30:00', '2000-10-02 00:30:00'
    $ A9 K+ c# w6 }/ a* e0 crng = pd.date_range(start, end, freq='7min')! \" e' ^! C3 F3 l) K5 F
    ts = pd.Series(np.arange(len(rng)) * 3, index=rng)
    $ X* |' U6 s# u2 ots& z$ |% D$ ?; ^
    2000-10-01 23:30:00     0
    " n& j7 _, I4 b( a# ?2 S% g2000-10-01 23:37:00     3
    - M% e* W. K1 |) U. i# _2000-10-01 23:44:00     67 A" k6 H+ m( C4 ~8 q, d+ Z
    2000-10-01 23:51:00     9$ m: p# v) `$ N5 o+ c- l
    2000-10-01 23:58:00    125 j9 j8 W- F* b" ]+ |! |; }  d
    2000-10-02 00:05:00    15. X) [0 s% Q: n/ J# T
    2000-10-02 00:12:00    18
    5 P$ e. A- C, |5 K, h$ S2000-10-02 00:19:00    21
    6 D  v$ J, @# _+ r2000-10-02 00:26:00    24
    ' l$ d$ W9 x: D( h+ IFreq: 7T, dtype: int64
    7 `- R, U% s; h8 s' ~: ?* E) c# Y$ w
    ts.resample('17min').sum()( ]! O% u' Y4 J
    2000-10-01 23:14:00     0
    0 j/ z+ Z. I6 O. J  B# t7 O& n2000-10-01 23:31:00     9/ P; y" Z& E, B9 x1 v
    2000-10-01 23:48:00    213 [( l7 E+ K0 Y; V' V+ j0 T
    2000-10-02 00:05:00    54
      @# Q: H/ g3 R1 }( U6 a2 W2000-10-02 00:22:00    242 R% E/ ?2 r+ B0 P1 b- c$ ~+ K  N
    Freq: 17T, dtype: int64, f+ V) T* w: U/ G, q

    4 |& N& U& _/ r4 z$ J/ I- nts.resample('17min', origin='epoch').sum()3 @6 g& B  J) W0 @. e4 S- z3 R
    2000-10-01 23:18:00     0
    $ C% a# W% K1 D* W4 y2000-10-01 23:35:00    18; S4 j: d; H' E: q9 l. `% c
    2000-10-01 23:52:00    27  n; t$ n% k, f
    2000-10-02 00:09:00    39, f8 `: L  g9 F1 I, ^+ s" U' k
    2000-10-02 00:26:00    24
    ( [5 u# K! j9 O8 W( iFreq: 17T, dtype: int64
    * G  L# @( H  O. R7 [) [% o9 q0 x0 M7 l  t* W' }0 V0 n
    ts.resample('17min', origin='2000-01-01').sum()
    " a# ?+ z4 L0 X/ _7 o2 |2000-10-01 23:24:00     39 r( s' |3 V; y2 x
    2000-10-01 23:41:00    15. [; g9 e7 }: L) [' ~
    2000-10-01 23:58:00    45! n# v* Q# E0 W7 D( Z4 n& `& e/ U
    2000-10-02 00:15:00    45
    7 S* r9 `" f5 sFreq: 17T, dtype: int64
    / S' P: Z; ]- P# Z& [
    - k) k- J0 Z2 C. M1( N7 M) F* R3 w! c% x, J' j7 b4 w" r
    2$ U; Q$ A7 t0 q0 Y+ @
    3
    8 v+ I: J9 k- M. y4, f' D" ]% d5 d- `0 q4 @: K; f
    5/ F1 e- j, b% v! g6 m
    6
    ; }2 U4 f! U! g- K  D! Q7
      G; C( l. C4 S, g0 v0 g8
    2 m. P; \0 T1 q7 d6 Z" M6 q9
    ; [/ h: ]3 Q9 i; `( }9 [10) ~" N' L( l3 T
    11( V4 H. [5 d; j, b
    12/ x; V; h' k2 }+ G4 E6 J" E
    13& e" U# E& m( x- [* C
    14& w; F9 D1 R! v; ^: S
    15
    5 l9 W2 Z3 b& a. W168 x! |7 E% Z4 h" Y( r
    17( l' Y$ G( E2 e. P6 A
    18
    $ _1 q( w7 ~& t. ^# z' ~19
    9 u8 j2 ]1 z3 H  U20
    $ D" F* A  O# h0 a" a3 c211 c7 y8 W. ?- k+ c! I! y1 m
    22
    * t0 N  N9 q) J! V! X- ?23
    ; ~: s! L* j: b; Q5 N+ w24
    # u9 l7 V0 w* o6 @25% i$ v/ V; O$ n5 r. V
    26
    0 E' S2 N3 b' Z, z279 X& |" S7 _7 p, B
    284 Q8 `0 L5 r# N, y' c) u
    296 K+ J- C; R, [; }! [
    301 o7 L, C3 \' ?9 K
    31$ o3 P3 w! h. z' M) z+ P0 a1 y
    32
    2 R, i8 y* Z$ |, o# R33
    & K" l; D% H# L6 c/ I4 k7 D349 T# R; o, ~/ O6 s
    35
    9 f$ Y# l) `! a  Y& g36( d5 k5 v6 w2 f! v6 B. w3 S
    37
    : f- J& P. \  z* @2 D如果要使用偏移 Timedelta 调整 bin 的开始,则以下两行是等效的:
    ( I5 H6 {3 R+ k5 g+ `6 F8 Hts.resample('17min', origin='start').sum(), g# ~9 s" x: j1 F
    ts.resample('17min', offset='23h30min').sum()5 e' ~2 Y( R7 C: G2 T
    2000-10-01 23:30:00     92 ]- v) o5 W- S1 Z6 ]
    2000-10-01 23:47:00    219 C& L5 _; y2 v( A! r* _/ b4 G
    2000-10-02 00:04:00    54* G& p; d& [- q0 W3 ]
    2000-10-02 00:21:00    24
    ) ?: b- m2 d* c9 J+ HFreq: 17T, dtype: int64
    ; x7 b% ^- [* c1
    # b% v( G* ?1 w1 ?  V$ d2
    . W" P! O' {1 y# s, ~( M- o3- A% t% ^8 Q7 y4 o
    4
    ( E9 X: Z8 x+ q3 z5% o; O' w% h4 V
    6
    1 U6 q1 c5 |& V7
    8 P* j& W) k. f, c7 d, k' w10.6 练习
    ( q( K4 G' }3 @2 t; o3 d  t$ D% ZEx1:太阳辐射数据集
    2 t( q. s! f$ v现有一份关于太阳辐射的数据集:
    2 \% N" e7 M" A! D4 A$ D& d0 {6 E  A2 ~: o4 G& y
    df = pd.read_csv('../data/solar.csv', usecols=['Data','Time','Radiation','Temperature'])7 T# x% S1 J0 a3 ?
    df.head(3), _7 |" F( S5 A, d: A

    % J, p. Z+ |9 dOut[129]: % G1 l- o) v6 W5 x6 b! h1 o; Z
                        Data      Time  Radiation  Temperature
    5 D; E. U% B: _* t+ }, v0  9/29/2016 12:00:00 AM  23:55:26       1.21           48& H6 A2 ]# z, L7 k2 }" f% }
    1  9/29/2016 12:00:00 AM  23:50:23       1.21           48
    . p/ p- o  L. Q# f4 y4 C7 l7 p2  9/29/2016 12:00:00 AM  23:45:26       1.23           48
    , T- F! A" w; E, |! `! }1
    % I9 q  L% D: U+ J2
    " Y8 ~# T+ C$ w- J, I9 Q% J6 {$ |/ D3. j7 {' o  d7 ?! i
    45 B8 h& a# Y' x, G
    5
    ! e& m3 `6 a+ r) m2 n+ K- g6
    ; t' h& E. Y8 v" z- Z7
    # c4 V3 \, l  Q8 j& ^, c0 Z, |8
    8 q" |6 |+ |9 a: f8 a! i3 x将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。3 N8 ~8 S9 x- {9 E
    每条记录时间的间隔显然并不一致,请解决如下问题:
    & d& ?4 V* Q. j0 j8 a找出间隔时间的前三个最大值所对应的三组时间戳。4 q# O) c$ \; N7 S3 B' q8 T
    是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。
    . {2 _2 T/ ~3 M# Y0 c求如下指标对应的Series:
    2 G; |9 k$ O) Y7 v温度与辐射量的6小时滑动相关系数1 P1 W$ a' u% q8 I! v) v- D
    以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列
    ' S3 X, m5 j" q每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)4 e  g1 ]( |5 n
    import numpy as np
    ; l: G$ j9 l. D$ Himport pandas as pd
    + c* Z2 W" Q+ x& k  h( Y1 P1
    " z3 g5 J4 R& D* i/ s! d2
    ) @% c4 [" U$ Z将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。/ ]9 x5 a1 V: D" o
    data=pd.to_datetime(df.Data) # 本身是object对象,要先转为时间序列
    . @) I: K1 c0 f) atimes=pd.to_timedelta(df.Time)
    5 H5 M9 R0 x+ D+ M% B( Ldf.Data=data+times/ c& l7 J& W8 ^% ~( @
    del df['Time']& }' v+ y- u( ~8 D& O1 {
    df=df.set_index('Data').sort_index() # 如果写的是set_index(df.Data),那么Data作为索引之外,这个列还另外保留3 M2 O, s7 ]7 B4 t$ m
    df
    ( B  L! |. ?0 y" O0 v- X5 l! m, T                                        Radiation        Temperature
    3 ?; w4 F0 X$ s! V0 OData                ) t6 Z$ e1 D7 H% m+ f
    2016-09-01 00:00:08                2.58                51
    8 G# J. E. [, d/ V) ~/ J/ Q& C2016-09-01 00:05:10                2.83                51- }' M$ b: N4 |  y* Q
    2016-09-01 00:20:06                2.16                51
    7 x0 r6 k+ m3 T2016-09-01 00:25:05                2.21                51
    ' _: t0 u& M2 `- M* j2016-09-01 00:30:09                2.25                51* Y6 k( _. s+ X# F8 w, t% E" U
    ...        ...        ...; c9 l$ x$ o+ W" @+ t5 G$ D3 [( e" N
    2016-12-31 23:35:02                1.22                410 ~& v$ N' O) Z" L1 t, t4 [
    2016-12-31 23:40:01                1.21                41
    5 S4 G* m4 s' [( z! g5 }- e  z2016-12-31 23:45:04                1.21                42
    $ O+ @& \5 J/ o; I9 {2016-12-31 23:50:03                1.19                41
    % K4 E8 B: g) p+ ]3 t3 j9 l; o2016-12-31 23:55:01                1.21                41; Z  y" L) @( S$ ~

    0 w' l+ O& Q( t" P1
    ' A  l$ H4 V6 ~5 K2! y( s" p/ x, e6 I3 I: p( H3 H% O: h
    3
    3 p& \+ B- j* L# d4
    + }) j* D3 J  b/ {4 e$ I5 ~: z; u5
    # x# @" c+ b0 C  v, M6
    ' p4 a& y5 I2 ^) v+ v; e79 J6 [4 p  m+ ]; y6 n" t( O
    81 e8 B$ s8 z' d( _# g
    9* S+ |" k& A; @! j! h$ u
    10
    $ S6 b2 ^% I) f, l/ s1 k8 v11# F! k& u. F+ \9 w2 O7 i
    12
    7 L) e' Y' M$ H) y! n# h13
      A( W8 L; M3 f9 j; W' f14
    ( d+ R- \- d# \* S. C. K/ V6 l$ p15* T9 D3 y( e, o, F/ }
    16
    . \7 Q( |2 ]5 [+ \17
    6 j/ `! H7 ~) B' w9 N/ d18
    : ?6 n& ]) y# f* c196 @4 O' s2 r0 r) O1 H4 b4 V
    每条记录时间的间隔显然并不一致,请解决如下问题:
    ) p3 ?6 H. V& n2 x找出间隔时间的前三个最大值所对应的三组时间戳。$ h* F5 g8 R4 H
    # 第一次做错了,不是找三组时间戳+ u$ U5 d" H9 k# j. w
    idxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]
    8 o' R+ Y1 t6 [2 }' v1 I2 S) V2 Tdf.reset_index().Data[idxmax3,idxmax3-1]
    9 {. A+ e1 b2 \( U, l+ a8 u5 q
    % V& k' j4 r/ V& P5 L) S# S25923   2016-12-08 11:10:427 \$ i+ R% s/ z! Y% T
    24522   2016-12-01 00:00:02' ^7 Q1 @- b- O7 y3 F
    7417    2016-10-01 00:00:199 o3 N6 [0 H3 f# |" w4 t0 Z9 X
    Name: Data, dtype: datetime64[ns]# l8 o& m7 l8 j! P. c( q
    1
    , w+ |# I7 L3 a0 _20 m* G! i9 f) P; }6 f  g. g6 r
    3& Z4 H7 }3 i+ V
    4% R& @% I7 U# C7 T' U
    5
    , L  @) }( b; `' c% I, I. x$ Q5 e6
    ) q( R; `/ B& g. {* K7& o4 n8 A# D' j
    8
    6 B) a% B5 ]6 Hidxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]
    1 ~" |/ @* u8 x# L/ klist(zip(df.reset_index().Data[idxmax3],df.reset_index().Data[idxmax3-1]))
    / o/ G3 P, {0 l" r  l) q
    $ [+ Q3 [# a3 G% p8 o[(Timestamp('2016-12-08 11:10:42'), Timestamp('2016-12-05 20:45:53')),
    7 f' G4 m% g1 o) `7 b! D$ w (Timestamp('2016-12-01 00:00:02'), Timestamp('2016-11-29 19:05:02')),, G% [8 _& g; V# H
    (Timestamp('2016-10-01 00:00:19'), Timestamp('2016-09-29 23:55:26'))]1 Z/ v- S1 h8 t5 T/ r4 y6 f
    1! @; g4 o1 ]% x3 T0 O
    2
    , s  c2 E: F5 J) g* B/ v3
    # `5 _( c' }5 F5 Z7 d4
    2 l% ?* ^% ]- V" }% M- W5/ f9 M' ?' |9 K( \2 N  e
    6
    1 W* j4 ~( u. {% Y1 J, R" Q参考答案:( ]% b' r0 |* r9 k( f/ @' j) g
    # f. W3 ]. }/ b7 Y% f4 {; v9 R
    s = df.index.to_series().reset_index(drop=True).diff().dt.total_seconds()
    ( u% U$ `" L3 \7 N3 |  Rmax_3 = s.nlargest(3).index( @3 ^7 ^' x" h
    df.index[max_3.union(max_3-1)]
    & Y; Q& W. X; Y+ S% Q2 s6 _$ s: q
    / ]; d. n3 i' WOut[215]: % w- l8 [  U1 N0 p# i4 @
    DatetimeIndex(['2016-09-29 23:55:26', '2016-10-01 00:00:19',% h/ K4 x, e# M3 @: h8 D+ a; s6 B( X) _
                   '2016-11-29 19:05:02', '2016-12-01 00:00:02',
    ' a0 Z3 W/ b' v6 i               '2016-12-05 20:45:53', '2016-12-08 11:10:42'],
    ' i( B. t6 `' k$ A5 E' Q. G7 B3 Y              dtype='datetime64[ns]', name='Datetime', freq=None)% V) z) C5 M/ \# J) n, t( ?) ?
    1
    " M- R" {; Y$ p9 u( X, p3 ~. d28 I2 h7 T# R, V3 j
    3
    8 n. R7 Z3 P! z( R- s4; ?. I! ?0 I/ C+ C, N6 v9 V% o
    5
    3 c' B# j- y# O2 B7 ^6
    1 c( {  O# u: s1 p76 z/ v1 L4 d1 x1 _
    8
    3 K: R9 K0 ?: w9$ N1 b/ b: L& @* x2 C5 I
    是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。
    - w7 w& u: d  V$ W" @# 将df的indexydiff做差,转为秒数后排序。再求几个分位数确定取值区间
    + V3 h% b1 _! R  w1 A* d1 ?s=pd.Series(df.index).diff(1).dt.total_seconds().sort_values(ascending=False)
    1 I* l" ^! ~, E% {s.quantile(0.9),s.quantile(0.95),s.quantile(0.99),s.quantile(0.01),s.quantile(0.03),s.quantile(0.05)
    2 I; S1 q/ x$ ]& z
    , @$ n- O5 s( S5 }& H(304.0, 309.0, 337.15999999999985, 285.0, 290.0, 292.0)9 f- `6 R( P- Q* F
    1
    ) r4 d4 j1 m/ O' _: |2
    ! B% @( c) N: y3 }$ I8 X; f1 f! h3
    * p3 o- h5 u; X; d, J- v4  }& X- \& p- L; |: x& x' r
    5! y& l# K$ V/ S' z; ], W% t0 {
    %pylab inline! j0 W. W5 V$ N9 ?% K
    _ = plt.hist(ss[(s.values<337)&(s.values>285)],bins=50)
    - J+ n" J" p8 f0 @/ j6 {& {plt.xlabel(' Timedelta')
    6 Y- s8 @  e3 |8 `9 v& v1 ]plt.title(" Timedelta of solar"); v' }2 l$ r; _! G& j7 E+ z$ |4 B
    1
    * V2 G& I: j  p: @6 J  @" H2) p, H+ L' y' r( E) P! f
    3
    ' B* f2 v: [) G' c6 c/ v4% L: U( ?% N. L- k+ q, F
    * Q0 H4 o* F' m, H4 ?
    ) \5 F- U9 Q+ f0 c, G
    求如下指标对应的Series:
    + ]+ p4 `( x# _温度与辐射量的6小时滑动相关系数
    9 a- a8 b% Z4 q# [5 T" u以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列6 v' }9 ]0 S; b
    每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)
    + F# V2 \! _; P. W) r% udf.Radiation.rolling('6H').corr(df.Temperature).tail()' {5 u; V; J* S4 f' Z# N9 w) d$ c7 q
    " N8 v' S; R' z" ^% ]  r8 _* `# L- |
    Data
    ; [2 ?3 K: H7 F' D' {& \; n8 I8 ]2016-12-31 23:35:02    0.416187( z# I) N1 a# p* _8 Q- c. u
    2016-12-31 23:40:01    0.416565% r# n% O2 K, S1 J9 s0 k5 k
    2016-12-31 23:45:04    0.328574$ I9 f% ?+ w# \
    2016-12-31 23:50:03    0.261883
    0 z) k, M% L. ]& N2016-12-31 23:55:01    0.262406/ h% C9 k1 Q, c3 {5 @8 ]$ d4 r- n
    dtype: float640 c* ?' J4 A$ U" g$ _5 ]
    1' n2 U) c! b5 u) |* q5 I" q
    2
    + d  z6 ~1 K. b9 l. `6 y3
    ; X. J! K6 t% R4 y2 T4
      o( s3 ^: E- ?0 T5
    * @" N9 U' |( j6
    " R5 R; ~! ^' C/ W! P) G7. v( E. j- J! s$ R4 J, A1 V/ _
    8, j7 C5 y' q+ a( W
    9! p* V* R5 f( [4 E- \
    df['Temperature'].resample('6H',offset='3H').mean().head()# \, E" m- P( y- m' j; D0 `

    1 m4 f+ I  f/ n1 L7 ~- _" b6 YData
    7 e4 P+ Q' p! g" X  P: V8 h2016-08-31 21:00:00    51.218750( e9 V0 T& A& U  s
    2016-09-01 03:00:00    50.0333336 g3 m: W9 _  P" z5 d# j4 d
    2016-09-01 09:00:00    59.379310
    , N7 N& R3 s3 r2 m+ f  R% i2016-09-01 15:00:00    57.984375
    5 u3 t8 n2 F6 n* T: h* q2016-09-01 21:00:00    51.393939
    7 K5 n. v8 c5 e0 D2 \Freq: 6H, Name: Temperature, dtype: float64
    $ F# t. n4 I( d% l2 C9 W4 |+ z1
    7 H7 j" n$ v0 d2 w( d3 ?2! y  M6 q. L0 }6 ?  e
    39 j# c: c* `* z( y2 `) x6 f' I8 I
    4
    $ v6 N. E. j. x2 k6 t& J59 m7 j' ]5 F$ w6 {' g
    6$ {9 G9 U5 M& O3 x( |8 `+ C$ u5 }
    7
    % H- T* Q% [% {( }, I# e' [8 S8
    + \1 o7 [7 N* l9 n- e8 l9
    0 S) j2 R6 R! h% g最后一题参考答案:) Q' C, M0 `& m0 q& j

    . |: W1 A2 H5 y' o2 M7 }, }# 非常慢
    - o0 n3 n! o+ x- z1 s0 v1 Imy_dt = df.index.shift(freq='-6H')
    6 U$ C& m; j* w& Oint_loc = [df.index.get_indexer([i], method='nearest') for i in my_dt]" S6 H) K- c: r) ], m
    int_loc = np.array(int_loc).reshape(-1)
    3 z8 [& R" O) D$ L& j* k) Eres = df.Radiation.iloc[int_loc]
    + z6 E  y  r& ~$ b% Y0 w  Eres.index = df.index& F8 _7 h" L7 P7 d4 x3 v0 x
    res.tail(3)
    6 V% k) M( d* W1
    - _. L  \  t! D. `7 k2 E2
    $ A4 W$ ]# C( c* Q( U7 l3
    5 S, v4 v* Z2 H# W8 w  _4! {6 I0 K( F) _' J+ I' a4 O
    5
    2 U, D/ y" c' Y9 w4 o6
    ' r) p8 y9 |& z% i/ a( N7. ^8 K# m$ ?# U
    # 纸质版上介绍了merge_asof,性能差距可以达到3-4个数量级3 a8 c1 M% V# n9 e& M. w
    target = pd.DataFrame(
    $ |7 ~. G/ u+ Y- D, c: M6 c. p    {3 i0 U7 Q: X, P( f. d  |3 y( I
            "Time": df.index.shift(freq='-6H'),. E; m9 W5 }" `% ^4 |0 r% t
            "Datetime": df.index,
    8 F# k, a$ ]8 Z% J7 O: c/ I    }' P  Z; y+ [: I  x
    )
    $ V* c+ v# c* _' \2 {' {9 y$ r3 J1 o
    res = pd.merge_asof(* y8 O9 @+ ?6 N' T  ?
        target,0 C4 C7 B" U$ t' |7 S
        df.reset_index().rename(columns={"Datetime": "Time"}),) z& E( @7 |& b: o6 B0 h
        left_on="Time",; O4 l" g# `0 w% n% R4 ]: @
        right_on="Time",. B6 U4 o% Y2 |% R% ]$ D* F
        direction="nearest"
    * o" C- s5 P' V).set_index("Datetime").Radiation
    4 m2 M. [4 t; c/ {7 K' I; v( w1 ^  i: X0 f  S7 D# i7 W# q
    res.tail(3)
    ' {& G! L+ b+ v1 L# {1 P& u6 SOut[224]:
    6 X. x- _: T" C, w* rDatetime% ~' n) n% p! s% @- w* y$ `, H
    2016-12-31 23:45:04    9.33
    - e. Y1 [, V' t" `' e/ ?2016-12-31 23:50:03    8.49
    + K5 m( R& I. l2016-12-31 23:55:01    5.84
    & u* \6 \' O7 s6 hName: Radiation, dtype: float64  ^! s" P" @8 T* S5 @, h2 m

    + z! X2 m5 J% F" @. o+ A16 |- @! ^! l. x4 \5 J2 g
    2
    $ G3 n; V9 L1 L8 o% n5 m  T3  T& W$ a: B" y1 N- ~
    4
    4 q$ u) r! X/ T  d) E8 V% X5
    0 H8 ~8 {3 C0 ]1 y; n6
    $ ]2 G8 I+ R4 B2 m3 `7
    % ]4 n# m1 b2 _/ |, r. }8/ R; b9 T6 x( |- a0 ]. j
    9' N7 u$ @* F* @6 C" H. D5 j( ]
    10% H7 O/ A+ B2 G" O
    11
    ' c+ H. {" N$ y4 w: N3 P12& B9 H' l' f; y( P
    13  q  v$ {& x( ]
    147 n  Y' T4 Q% |6 P0 G
    15
    3 _+ j6 ]/ g* w16) d( |# \9 ~: r
    17
    + F, B  X! \) c- s8 \" H18" e- Z# [3 p+ L
    19
    5 ~- N0 R" Y, ^, Z% K* N' Y20; z, H7 S& t1 ?: I
    21# i( t/ r6 ?/ k. O2 O9 H* p# M
    22
    . N, [! p5 v# u23
    " |1 i- B1 y) T, g' xEx2:水果销量数据集5 W* r/ E, h7 z2 v9 ^" K" V8 }' Y
    现有一份2019年每日水果销量记录表:* d# J/ Q* v* C8 n1 a$ t! j
    # h9 W+ ~& r. C" q
    df = pd.read_csv('../data/fruit.csv')% @; q. ~  y( W
    df.head(3)
    3 L% u' m0 K5 O1 l! k$ z
    % P- l5 H: f4 X$ f: n" ?Out[131]:
    2 q( |" s- _  R' g' A3 X, m, O" m         Date  Fruit  Sale9 P0 {" j% f9 b% \* c2 h7 e
    0  2019-04-18  Peach    15. }+ b+ y! V( N
    1  2019-12-29  Peach    157 r& w+ i$ |: C' }1 {
    2  2019-06-05  Peach    19$ F5 x; j$ E+ J$ h
    14 S( I+ ?( _0 p' v9 Z& O# H
    26 a% Y% r' V; V
    33 N2 A" N- s& ^- G
    4
    # @; A4 _4 P; @7 H6 }2 v5
      X! ~5 u/ I, Z3 O$ a, f66 U# W) P: C/ D. `9 Y) {2 |
    7) Y) p7 p/ r2 r4 u8 o
    8( x  B5 D- x+ Q) f. o2 f
    统计如下指标:
    - e- O! t# n3 z每月上半月(15号及之前)与下半月葡萄销量的比值
    - f' A4 z+ j9 L! E# ^8 U4 r7 o每月最后一天的生梨销量总和
    ; L7 `* L  z% g1 n# a" }! L4 j每月最后一天工作日的生梨销量总和, l: n" \5 k5 Z; S9 o0 L5 V
    每月最后五天的苹果销量均值
    : Z! `0 x' `% }/ i按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。
    " q* e  o& r; w  u0 ]% f; {! D* S按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。5 \3 n, T, `( T: t& S/ G
    import numpy as np- o2 }" K; b3 X8 p; _: f
    import pandas as pd, _1 d$ u3 v( z8 i! s$ s6 g# X
    17 l; J( y; Y/ L( V2 E/ ?0 C; M
    2
      z8 \% N0 P5 v9 i. D/ ~统计如下指标:
    2 f# Z" n+ t& x4 i( Q1 Z每月上半月(15号及之前)与下半月葡萄销量的比值
    , ^. O; z& G. l, k8 S每月最后一天的生梨销量总和
    % t: Q' H$ D9 q每月最后一天工作日的生梨销量总和' v* P4 W  U1 X0 S: c" ]% g7 |
    每月最后五天的苹果销量均值/ H. C' q% O! p3 d
    # 每月上半月(15号及之前)与下半月葡萄销量的比值! U9 m& S# r7 n* |% n) E7 F
    df.Date=pd.to_datetime(df.Date)
    3 Z; w) `% D1 Csale=df.query('Fruit == "Grape"').groupby([df.Date.dt.month,df.Date.dt.day<=15])['Sale'].sum()9 b6 F  k, w, L
    sale.columns=['Month','15Dayes','Sale'] # 为啥这么改没用啊! u( V9 e& s% O9 B0 Q9 ?
    sale=pd.DataFrame(sale)6 e( B$ a7 _0 i( Q2 A$ R; @
    sale=sale.unstack(1).rename_axis(index={'Date':'Month'},0 j5 s- {2 k; g2 q7 c
                     columns={'Date':'15Days'}).stack(1).reset_index() # unstack主要是两个索引都是Date无法直接重命名
    5 i0 W" w2 ^! Esale.head() # 每个月上下半月的销量9 X& R! R2 ^& N

    7 w" i( L8 J2 `% r  Month        15Days        Sale4 G1 B9 y8 w+ M6 I
    0        1        False        105037 D9 ~6 W) P' j
    1        1        True        12341
    , i6 u' P" J: P) R2        2        False        10001
    . A" j- y  g) E3        2        True        10106& B; l' a! c9 P5 `# c# ?
    4        3        False        12814
    " a4 }7 y) N5 e$ W- l
    9 `5 f& K5 N9 X# 使用自定义聚合函数,分组后每组就上半月和下半月两个值,根据索引位置判断求比值时的分子分母顺序
    % [+ f2 R/ ^& `# b) J  [7 M. D( usale.groupby(sale['Month'])['Sale'].agg(- k5 w" g5 M# K! a$ _  ]
                    lambda x: x.max()/x.min() if x.idxmax()>x.idxmin()  else x.min()/x.max()): {. d3 g; \' _- c' L) y& _5 ^- v: P4 v

    % V6 _/ K1 ?6 S/ k2 A9 `: TMonth
    8 {# e! J9 v* \! M3 k/ u1     1.1749987 r+ {7 w- z$ Z6 k( L. |* ^
    2     1.010499/ R1 b- `1 {5 R5 M8 S" ?7 E
    3     0.776338
    8 K5 m/ Y2 ?9 H+ l  Z4     1.026345
    6 M$ K" n9 C' r  F, G: A5     0.900534
    / i- C5 }- P" L4 E  a6     0.980136
    1 s3 F% e, v& r8 [* ]( N! |+ _7 L6 @7     1.350960& R3 g+ C$ j5 X. P1 s. ]! z) E$ y
    8     1.091584
    2 W9 u: b; w6 `9     1.1165089 r; Y. j* z: R, w" ^+ ~8 L
    10    1.0207842 `( T# ~3 D- G! V6 f
    11    1.275911
    9 w! J; |8 U7 f- O6 O12    0.989662& v2 J+ t( l$ C9 N- v; V3 v
    Name: Sale, dtype: float64' x9 e# S+ b/ B* ]2 }( _' E) T# }

    8 t7 [$ E! X3 O5 r, V15 {" t4 e& m; g4 i9 L
    2
    , {7 G) A" L+ _" D6 V$ b" V2 m3% N! i/ }" N* b" m* ]
    4) E+ y  [, c9 V
    5
    ! Z/ I2 U2 D: ^" o3 u) Q6  ]) r% a2 \* m) y
    7$ |* k! M: j. Q! v
    8/ {0 z, m1 ]! e
    9
    - ]8 S7 W* W9 Y$ W10' c; w1 _& B; V" T
    11, V, x$ q; ]5 @3 x3 `
    12' f( d0 L/ _" i, |
    131 y0 R+ N0 b" J1 E/ w& |
    14: D) K+ ^( Y( r) Y8 e
    15
    " W6 _- `4 l0 c( G7 K0 m/ N16
    4 Z% a1 l( K+ [17( Q8 f+ P# {9 G5 m" v
    18
    ! v$ ]' n9 A' N# o/ w19
    4 k. a+ ~8 x3 |6 e. c20
    $ ^" F2 M+ b$ ]6 b+ L; Z21& Z/ J: N; b& ~0 P: y
    22
    + X" C4 L& p  o/ b& v$ s: `23
    , K: W: }/ a) n2 C/ r6 p2 v8 a24
    6 M/ }) u7 }- Y$ N) k, C5 @* D25$ E$ n, }! r2 t+ v* p* n8 `; j
    26! P+ {* f! T  M' r; f
    27
    6 \- ]9 i( Q2 c3 Y28$ f# x3 Z4 `) ]7 C4 o6 Y
    29
    ' Z3 L7 `. i0 m2 t" l. G$ A: j* x30
    5 N8 ], i9 b. }4 {31
    5 n5 d# @, ^0 W5 q; n32
    ( i# K/ x4 Y3 H) L- e4 c& u% i336 q% \, A! k6 \, c( a
    34
    ' \# x2 _, _! K4 |" P  u# 每月最后一天的生梨销量总和
    0 M8 [+ B  j* d! }9 h; C% v! Pdf[df.Date.dt.is_month_end].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()
    7 d3 T5 g( y1 U: O1 T: g7 K
    7 {- B4 E- P& MDate
    : X2 L7 U: a7 q5 \, R2019-01-31    8470 g- C: g8 F9 x: y  d% I; z
    2019-02-28    774
    $ c/ l" s3 f: f. t2019-03-31    761
    * Y- E  x7 [, ^% ^2 Z# y2019-04-30    6481 P1 x% e# K. i1 `) l/ _
    2019-05-31    616
    . `( X5 O" s- M4 u6 r14 N  F7 Z& F& _5 i/ Y; X" h& l
    2
    7 h! h8 U% y- n0 p* o7 D3  V# w' N2 O" N) `" ?4 r- _) h2 _
    4' p( l& N1 `- G3 t, U) \$ ~0 ]
    5
    * w) x! A5 s- x! F; e' W6
    5 V0 {# g6 |$ ~  ?: R4 I7
    0 P7 l, c8 k; P; G4 i8
    9 Y+ m& i3 E. I0 |/ t9
    # @% _3 U3 G9 H  b/ p, j# 每月最后一天工作日的生梨销量总和9 z' ^2 _4 V( a: f/ c5 ~
    ls=df.Date+pd.offsets.BMonthEnd()
    5 g8 ~! W$ t) Y. g, F- j0 u' ?my_filter=pd.to_datetime(ls.unique())4 w8 e% z9 C( z+ V
    df[df.Date.isin(my_filter)].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()
    2 w) ?4 Z- T) J" f" ~/ s
    7 s# C# G0 U4 c* lDate
    - i* S7 J( r! S2019-01-31     847- C1 T6 U$ q( Q2 h4 @: a
    2019-02-28     774
      O& r' m& i0 o: C3 y2019-03-29     510
    / B- [" W4 {/ s! }2019-04-30     648
    , e3 R2 S( z' {" U& r) ]* }2019-05-31     616+ _, `/ Y% |3 o! h0 o, Z
    1
    2 O2 E1 |! U& ~% d2
    / U6 z+ |3 N% K: M. M/ J2 X3
    ' t3 l7 B" D# Y- [4
    . V: ?4 V$ y* Y6 r5- x. S* \$ m6 {. @8 T! x
    6
    $ U" s: A& ?8 s) y7
    9 s; B3 C  u- z2 u! t: T8, j, D3 i. r1 q; F3 z  Z6 v( h
    9! D) w" V0 B% y# R/ b2 f; E  P
    10
    4 s7 x! h# f7 d. X11
    ! a- @0 ^7 m4 E0 ~# 每月最后五天的苹果销量均值, L' Y8 W7 S5 V5 H
    start, end = '2019-01-01', '2019-12-31'
    / f1 h4 \; W2 A' u- }end = pd.date_range(start, end, freq='M')
    ' ~! u5 E+ }# b6 Y/ vend=end.repeat(5) # 每月最后一天的日期列表,重复5次方便做差, ]& F  }  m  V6 V

    : d3 P* p4 N" Y, \td= pd.Series(pd.timedelta_range(start='0 days', periods=5),)- R8 r$ }& }+ w1 e0 V: H7 ~3 C6 C
    td=pd.concat([td]*12) # 日期偏置,最后一天减去0-4天
    * ?5 o5 h1 W/ Nend5=(end-td).reset_index(drop=True) # 每个月最后5天的列表
    & I" Y/ u3 r1 p9 c0 L, |8 j8 b8 Y7 e% U; p$ O6 E. ]
    apple5=df[df.Date.isin(end5)].query("Fruit == 'Apple'") # 每月最后五天苹果销量7 W9 e; o1 ~) @* [. z
    apple5.groupby(apple5.Date.dt.month)['Sale'].mean().head()* p( r4 L  P' q' \: G' t
    8 f$ D5 y( X* l/ ^, e8 _& h7 q
    Date( P, J+ i' E: p! d
    1     65.313725
    ! m# x* D6 J( |4 j2     54.0615388 U- \5 W6 {! q6 i+ |
    3     59.325581% a7 d; t+ n* B( r; h
    4     65.795455
    * z; E" ~7 u2 Q5 z5     57.4651160 H9 b7 k; h% Z" t  a0 E% D8 E& K

    ) v1 L' }+ K/ V* ?1
    ) Q3 H3 N( Z! E2 k* n2; Z! D& T1 S$ p) ~! O* X. A* t
    3
      J; J% k3 Z; o; z, \" {  ^& a4
    ) c& n6 z1 C/ h! m) h# H5$ O& M) \( c* t
    6
    . ~8 @/ d$ O% [0 l/ b7 A* {6 Z0 V7
    ! @9 v& p& i" G& m8
    - _  a* K( |- I9
    & \! i% I. t" t10: y6 H& c0 @* d
    118 S: o% h# a/ C
    12$ L4 ^% P& ~" r1 J8 _; H1 T0 O
    13: `1 M0 C8 e; m3 j) |$ q
    14& L, K- S! C5 H% d% C
    15* `+ D9 }; c) ^) Y+ ~- D
    16
    . v2 {$ Z8 k  Q  K% V5 t4 f17+ L; U! u2 u5 v
    18: i4 O3 F1 `  t8 j2 ]! _! g
    # 参考答案:* `; v* }7 y, v2 Q  t' Y
    target_dt = df.drop_duplicates().groupby(df.Date.drop_duplicates(
    9 x2 t/ X+ B6 S' U% T& q            ).dt.month)['Date'].nlargest(5).reset_index(drop=True)
    1 z- |( y2 ?) B. S/ Y! k' w9 i  O7 P2 ^0 N1 ~$ I' g, v0 \6 n8 Y
    res = df.set_index('Date').loc[target_dt].reset_index(9 z7 J, W7 n/ G4 T" ]
                ).query("Fruit == 'Apple'")7 b8 U, d: N3 j0 q

    8 M+ ^8 d) d: E8 k" q1 a' fres = res.groupby(res.Date.dt.month)['Sale'].mean(
    ) b  b$ W! C) @6 a3 r% j# I            ).rename_axis('Month')6 g: q( r% i: Z: e. p; L

    ! h8 G8 \- Y- ?# M. J5 i& t, J
    4 F! d9 m6 C* rres.head()* Y; e/ X/ D. `; X# S: k
    Out[236]: 0 X9 E! l9 {% x" t5 L* m  x
    Month
    ) p. z& t! M9 M7 Q3 j1 _2 k1    65.313725; I$ Q  |4 x5 A2 c( E1 u
    2    54.061538/ O. k. }2 g& M% m( U& ~6 w
    3    59.325581) \4 z2 v+ _4 V. E$ L4 O
    4    65.795455
    ; ~# D3 h) A8 f9 `5    57.465116
    / ]& y& b( l+ f. C' D1 p" c# N' x4 {Name: Sale, dtype: float645 `9 [0 u% ]  ^% C0 J

    ( E3 }/ d" Q6 s7 z- p& S- N1
    # Y! f9 c% G1 N5 H: u# o2
    5 G0 Z& B; w: n( D+ r+ Y31 f* y6 P( h+ f$ K, o7 s+ E
    4
    8 Y1 t1 x6 {' x# W% U; L5
    : H1 _3 Z! ]% C& `' P) }6' i1 ^1 L! Z6 j) w
    7: g9 {) G! d5 K, u
    8
    ; M' P; f3 O7 N% `' m) l98 b: j. s# C: ]3 L4 C
    10' d8 }4 }1 q5 r. D  L6 ]
    110 [) s4 k/ _1 j! D# m+ ^) z: F
    122 q1 z, Y; g2 m" q; o
    13
    5 B- }5 u* u# f14
    7 j% m0 S8 G! a) S15
    # C0 k. _. u7 O) P16
    8 M6 c4 ^: U  {3 J* T7 T# I) h$ H6 g17, R) t4 {# U# [) r. }2 C8 D
    18
    # f8 {. t' q* ?2 W19
    % D! P$ c4 z$ s20
    8 ?/ a. Z1 j, Z9 s3 a按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。
    * n8 Z4 ]- Y9 c$ s) ~5 [  qresult=pd.DataFrame(df.groupby([df.Date.dt.month,df.Date.6 [, p/ c) ]# y6 \" e6 A
                                            dt.dayofweek,df.Fruit])['Sale'].count()) # 分组统计
    6 d% ~  R6 K$ ?( e% B3 v                                       
    3 M* C: y$ Z4 h/ tresult=result.unstack(1).rename_axis(index={'Date':'Month'},
    + e0 ^& C. R+ n: p* M                 columns={'Date':'Week'})  # 两个index名字都是Date,只能转一个到列,分开来改名字.& d% ]5 l, o, y+ L. T4 [. e
    result=result.swaplevel(0,1,axis=0).droplevel(0,axis=1)
    3 B* N9 y- H, _' t3 j( v8 vresult.head() # 索引名有空再改吧
    % {, A* l/ e. Z; }$ }, X. W- q' {! F$ D; s; I; R
              Week        0        1        2        3        4        5        6
    , N$ p8 v1 j3 |# @# xFruit Month                                                        " n5 i% Z5 ^8 b. R9 c5 |
    Apple        1        46        50        50        45        32        42        230 q2 N6 b$ N% ?- j- w
    Banana        1        27        29        24        42        36        24        35
    9 g* P' ]5 Q& n1 [# h4 L2 c) h6 JGrape        1        42        75        53        63        36        57        46
    : G4 @% V: J' B+ d4 APeach        1        67        78        73        88        59        49        72
    ' l" @+ ~$ ^$ C! E9 t0 f& \: m) s) C5 \Pear        1        39        69        51        54        48        36        40  ?( l2 ?( E8 m, A) |( e; u  i
    1' S7 }3 D+ Y" P/ i% M, }7 b3 F' p
    2
    * U/ W% d; B9 j5 H' O3# w4 Z) s) i. B# k" `0 s4 s- ]
    40 b; _( n4 R8 X$ u% V
    5
    # ~. ?3 t$ }6 h9 n, G. o61 z$ y6 W0 v/ f* k0 j5 U* g
    7" N+ m5 i& l3 c0 }, S
    8
    * [) o9 Y) }8 j/ \* h. u  g9
    , P# u+ |) h$ S1 o+ z( [* w/ x10) w+ d* @. g/ B' g* S& ?
    112 [2 _2 u; V1 b7 ]- q- Z0 L
    12- F0 I0 B1 L9 \1 W& O
    13
    ( J+ q6 n; v, p( ^  b1 q) v1 x3 ~14
    3 u1 P& l$ X* O15# F# S, g" \. O6 k0 ^2 I" ]: r
    按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。% i( y3 V' M$ }& j8 S4 R, C
    # 工作日苹果销量按日期排序
    * u8 q+ Z" h8 m* C3 v! Bselect_bday=df[~df.Date.dt.dayofweek.isin([5,6])].query('Fruit=="Apple"').set_index('Date').sort_index()
    & \/ [; M8 w2 i# G% d( X" j5 n8 K! Lselect_bday=select_bday.groupby(select_bday.index)['Sale'].sum() # 每天的销量汇总1 |$ l6 g+ O+ H1 M0 f, T  Q9 x
    select_bday.head()" @4 Z6 G$ }: f; y4 w3 ^* y
    % y3 e9 z$ J9 _% A+ L. ?8 g( b
    Date0 P+ B! w7 b! R$ F  u) a# |
    2019-01-01    189' r2 I! }  L0 c* |5 X
    2019-01-02    4820 i+ N1 u1 ^' K8 D  c$ C$ ?: h1 a
    2019-01-03    890
    ' s& l1 c- k- w( e0 I9 M1 R2019-01-04    550
    ' L# |) ^7 Y4 d0 I0 o2019-01-07    494
    : X7 _# g8 e  N/ E) j
    5 n( u. t% {/ k" Q# 此时已经是工作日,正常滑窗。结果重设索引,对周末进行向后填充。- n# H; Z* \! c
    select_bday.rolling('10D').mean().reindex(df.Date.unique()).sort_index().ffill().head()% ~6 y4 t, Y0 R2 |5 v
    " L' G# q& j* J
    Date
    2 _0 K6 o* F) B/ d8 Y2 o% t: V7 U2019-01-01    189.000000
    ) f, Y. F+ c! e* S/ C2019-01-02    335.500000/ A1 B4 k& _- N; c/ G
    2019-01-03    520.333333
      E" ^* |9 l7 b7 ?- ^2019-01-04    527.750000
    . D! N( \2 H; B$ c, d2019-01-05    527.750000  }, r, ]. f( c5 _- g$ e' e

    + }) [1 |* D, f2 h5 f5 ~: Q————————————————
    / F+ ^/ b& k; M4 ?2 r版权声明:本文为CSDN博主「神洛华」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。- k- \# j  I' h1 Q
    原文链接:https://blog.csdn.net/qq_56591814/article/details/126633913) U8 W  L0 N5 e" B
    * N$ N8 d* K" C
    ' k5 B8 ?. z  V1 b/ N
    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-4-12 12:57 , Processed in 0.765477 second(s), 51 queries .

    回顶部