QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2797|回复: 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
    5 n( O7 S+ B% O. U
    - i% ?1 ~  N1 M2 T: V1 H2 w6 T% A. c* `

    . }1 u6 w" V+ X2 ]文章目录
    * a% r& o. @7 i$ Q& \5 ?/ U第八章 文本数据; D1 d: g/ T% m. @' H
    8.1 str对象
    , x) v' b# ~: q; M6 ], r8.1.1 str对象的设计意图
    " U* Z8 _% {  d  g  e5 |; f8.1.3 string类型
    % p! N$ x) z/ c/ B* T$ V0 O# E8.2 正则表达式基础: }# M1 L( t1 Q# o0 \! W5 ~' i
    8.2.1 . 一般字符的匹配
    5 \" r( Q. B$ ^2 n8.2.2 元字符基础$ T  Z9 x5 g0 l4 F% F/ D
    8.2.3 简写字符集
    0 h6 f3 v5 b9 e0 t8.3 文本处理的五类操作* L! y; t) _% E. \! f) q' A: V
    8.3.1 `str.split `拆分
    / |( u; p8 W6 T7 T8.3.2 `str.join` 或 `str.cat `合并9 a5 R2 F& Y* G& y7 F+ w% ^
    8.3.3 匹配
    0 r' d, _( P" l$ u! {8.3.5 提取
    3 }* Q' [5 x. C1 Q3 i8.4、常用字符串函数: r+ W3 |( Q( _1 X; O/ e5 a+ Z: [
    8.4.1 字母型函数
    1 U* A: X/ Q+ q& r/ I8.4.2 数值型函数8 L+ v4 ^5 I) h" I% h$ K1 w
    8.4.3 统计型函数
    ' _) x# Y, v* E, e; z2 t5 V1 x8.4.4 格式型函数; @( W) F6 A( ^/ D& I# |7 z, T
    8.5 练习9 |3 A' {6 r6 T; C& G
    Ex1:房屋信息数据集' U  B' D, ?/ R! j0 s+ t5 n
    Ex2:《权力的游戏》剧本数据集* n# z& N. G: l; j& ?- H! e
    第九章 分类数据$ H  v* Z+ Y& K5 q& y" N3 R
    9.1 cat对象. Y! L7 ~4 n" X* X( {& H2 B3 K
    9.1.1 cat对象的属性
    2 [- i: l  J! p  n1 C9 E3 y4 D9.1.2 类别的增加、删除和修改
    ' W, ~6 c% `# M* M9 U7 ~1 a/ h9.2 有序分类- z  g- t: o$ l# a
    9.2.1 序的建立! ^5 O/ |+ H1 Q$ f
    9.2.2 排序和比较
    + Y. ~9 X+ ]( m, D' r1 B3 m* n9.3 区间类别" j8 D: [! N3 ^
    9.3.1 利用cut和qcut进行区间构造, C) J* h. l8 r5 r) K/ f' |6 L
    9.3.2 一般区间的构造; m7 N, e+ B* H# z: P7 V
    9.3.3 区间的属性与方法/ Y( D( [' y8 ?# h( a  I
    9.4 练习7 X" _7 C9 r- }
    Ex1: 统计未出现的类别+ u3 F/ {; I% `" Y  }, ^
    Ex2: 钻石数据集/ e+ I# O7 i6 Q* N
    第十章 时序数据
    " x* U; I( s7 t, M10.1 时序中的基本对象+ m/ q+ w0 g' I1 ^6 e
    10.2 时间戳# z. e( L$ M2 v: N1 y& M3 F2 C
    10.2.1 Timestamp的构造与属性, n- ^2 W6 g! t3 F9 B) _
    10.2.2 Datetime序列的生成
    . N. ]% o! `/ e4 x% _" [' ^5 Q10.2.3 dt对象% j6 ^& r3 U) q& w" V
    10.2.4 时间戳的切片与索引1 |4 d, U4 s/ C' S; F
    10.3 时间差
    / a: `$ e+ M$ H1 {; b10.3.1 Timedelta的生成
    $ V, i% N" o1 O" ~10.2.2 Timedelta的运算5 {, u# @: {# P6 ~: u0 t# C8 i
    10.4 日期偏置: p) W3 t' Y0 D: H# i( d& t, r. P8 x
    10.4.1 Offset对象
    ; P5 t4 R! f0 P2 i6 M# I' c0 y' D10.4.2 偏置字符串
    0 j7 U" M; H. q$ `4 i8 x10.5、时序中的滑窗与分组. p5 W4 ]. @' a9 E: P3 V
    10.5.1 滑动窗口
    & Z3 V9 G# C/ e9 e+ f0 }10.5.2 重采样2 m" Q- I' X' W7 e$ p  Q: S
    10.6 练习' W, F( C1 @8 M
    Ex1:太阳辐射数据集: c  `# {1 \2 O/ {$ p4 M% C0 B
    Ex2:水果销量数据集& U  m' \( V4 D( V( j5 ]9 _8 n& K
      课程资料《pandas数据处理与分析》、github地址、讲解视频、习题参考答案 、pandas官网4 o) ~8 x' }) C
    传送门:
    : l! ?# t  L, v: _" W- s7 c  k! B1 j) R' }% e
    datawhale8月组队学习《pandas数据处理与分析》(上)(基础、索引、分组)
    8 q9 y1 r, f+ Adatawhale8月组队学习《pandas数据处理与分析》(中)(变形、连接、缺失数据)& P$ o! t! c) u- Q
    第八章 文本数据) q1 \8 q. [/ Y" Q8 x6 @
    8.1 str对象
    ! P; l  z: u& V8.1.1 str对象的设计意图9 I' n/ w' w# Y& u7 t9 m# m  @
      str 对象是定义在 Index 或 Series上的属性,专门用于处理每个元素的文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其 str 对象。在Python标准库中也有 str 模块,为了使用上的便利,在 pandas 的50个 str 对象方法中,有31个是和标准库中的 str 模块方法同名且功能一致,例如字母转为大写的操作:/ b1 ~( I. X7 b( O1 u

    : r, e1 s4 a) M; [; a0 Svar = 'abcd'
    ' f& m( Q$ W) E7 u7 s3 {6 m# D5 Ostr.upper(var) # Python内置str模块
    6 q& |! ~8 z# x) }Out[4]: 'ABCD'
    1 R6 K3 L$ v9 Z+ ]. S: V6 A
    ' E. b7 h- H6 `! X7 F. x5 Zs = pd.Series(['abcd', 'efg', 'hi']). n2 H# w9 D3 {8 E& r

    ; d- M* i$ j* q4 o+ e) Cs.str3 S  a1 _1 L0 Z- V$ n1 L) \
    Out[6]: <pandas.core.strings.accessor.StringMethods at 0x2b796892d60>7 \( @  W% ]+ k0 R, o$ L6 c

    ' [0 P1 c/ V& V! Y& h# E, ~s.str.upper() # pandas中str对象上的upper方法
    ( o( L/ H9 D( [  q" I6 SOut[7]: 1 n" X( k/ Z# E( B" F) P" J6 s
    0    ABCD
    ! T9 c. ^1 @! c* S* X8 z1     EFG) v+ u3 n4 A1 n; j1 O8 |
    2      HI
    1 ?4 e" T2 J1 t+ x2 ]8 ?dtype: object* F$ Z$ [6 b* l' J& H; ?' z, x
    18 k9 t" m- ?2 ?  S! }( @
    2& l: A1 D, |6 }  m4 D9 v1 K
    3
    . }1 w: J/ V, e8 W2 S# q5 f: l4 e43 W9 v$ W  k$ ~$ o$ v
    53 h5 u& O  c" z# q
    66 e2 u+ L  J- a6 v2 z$ f( o
    7, y- @& [% u2 g  b: J/ k7 j
    8; t* F  W% d" @8 k5 |% k) V& P
    9
    / b2 Q3 U/ e* d" D+ J$ b3 I. v" y9 u10
    , y, E6 `+ `* i- @' |4 U11# r5 {- E& _" t  _5 }
    126 N8 g' E4 y1 g% |
    13
    % i  d" a2 [; b14; n* S0 G& H* X3 A5 f  W
    15
    2 u4 _3 i! u6 `3 C$ d9 C8.1.2 []索引器4 B1 D, C; l6 R2 U9 Q) C
      对于 str 对象而言,可理解为其对字符串进行了序列化的操作,例如在一般的字符串中,通过 [] 可以取出某个位置的元素,同时也能通过切片得到子串。! k& K5 y2 u6 v6 p! m: _4 R
      pandas中过对 str 对象使用 [] 索引器,可以完成完全一致的功能,并且如果超出范围则返回缺失值:
    1 l1 l: g! V9 O7 d5 K5 a& }! y& j0 V$ p
    s.str[0]
    # g  h% H) w4 nOut[10]: - T; W/ ^" M, _
    0    a' q+ i8 _5 O  \! t' c; a/ \4 `
    1    e5 `- f( j4 z( Y& A' y0 a
    2    h
    1 v' a$ S6 |1 O8 h8 odtype: object
    ( j0 J5 F+ W5 I+ V  {" ^7 c% d' I/ M- P
    s.str[-1: 0: -2]2 w$ D! R8 u; L. F% w* t
    Out[11]: : z9 r' }1 N) o* ]
    0    db1 p* V2 j: Z4 O8 r5 k3 U
    1     g
    3 H/ s- q! B" \- z; P) M2     i/ c% l2 W% J) P. m8 f8 K
    dtype: object
    $ A* W; B% D, a* k  F. H- g1 w& ~$ H$ I5 `% a4 Z
    s.str[2]
    0 _$ E  Y) p& I: K8 hOut[12]: ! ^5 k, {; ?) |; [% s( c7 b. ~
    0      c
    ( n: f+ l+ Q) `6 M# L1      g
    0 d, z: `: }9 {8 B2 Y$ E- A5 C2    NaN
    % [( f  S6 g( m! O; A) ^dtype: object- |5 H+ K$ k. o$ }* I" u2 X% Z4 V

    8 Z' m. H+ ?5 o* Q1% V3 L( ^7 k2 E6 _
    25 o0 V7 c5 f8 ]
    31 d* |/ _5 b0 x/ |
    4( W; \$ V" y, ]; K8 n) n; f2 ^
    5, F. @/ @2 z6 C1 K! r" ^9 r6 O
    6! o/ F0 z$ x9 t0 u) n$ n
    7; `1 Y5 l5 h  U7 N' y$ M
    8. c" G) S' h) _+ D9 h* p
    9
    8 c. Z+ s! v& X3 s9 m& A4 e4 Q5 n1 B10
      {; @- d7 Q! r" m116 `1 B0 ^" c0 y# ]
    12: J# T! f$ ?' K( B
    134 D1 |- U' K  b5 C7 A! Q
    14
    ! d! `( e6 y( b2 |' {157 e) l7 {8 m6 M. L5 [. m8 Z
    16
    5 m/ p8 U( H$ ^2 q, T4 J1 C17
    % B' L& q/ {* W- ^  b" o4 i% G181 i$ y7 p/ ^7 {# y/ p
    197 Y# k" n# M. V9 Z, j+ O& U
    20
    $ D4 o5 U1 u5 G- E, d% `) C8 _import numpy as np( }5 }! O- d+ ^2 D* J
    import pandas as pd
    % M; R& R" p# a6 f/ N7 K: L6 V2 b; V5 m9 O' k7 g2 G, p4 U. i
    s = pd.Series(['abcd', 'efg', 'hi'])
    & M4 @" @5 Y& W3 {+ c, as.str[0]" o- s0 i% O9 _
    1
    , X/ k1 z8 D! ~$ @3 j- z2% }) S% U9 A0 I+ J5 w6 A& U1 c' W" K
    3
    , n; N# `3 J7 e  R0 E! [4
    ( y( G, t. w( h5 f" F' g5
    / _9 B2 ^* e. i* {0    a
    0 ]/ @. e# ?7 [( I4 |1    e
    5 A! m+ r0 ^  L% U7 i2    h7 f4 U+ s' Q4 ^. W) z& k0 W
    dtype: object& x& H$ J5 I* W, c! S( r
    1& P. e/ G8 l, o% I
    2
    # e- L$ w! [; H0 \6 q$ c3; W+ t8 A& a3 Z1 E9 \7 k; Q$ R
    4+ {! p0 N! v6 ^% V$ k( G
    8.1.3 string类型
    ' ?) y$ {8 m8 R& C  在上一章提到,从 pandas 的 1.0.0 版本开始,引入了 string 类型,其引入的动机在于:原来所有的字符串类型都会以 object 类型的 Series 进行存储,但 object 类型只应当存储混合类型,例如同时存储浮点、字符串、字典、列表、自定义类型等,因此字符串有必要同数值型或 category 一样,具有自己的数据存储类型,从而引入了 string 类型。
    3 T5 x! B  a: v( g) B  总体上说,绝大多数对于 object 和 string 类型的序列使用 str 对象方法产生的结果是一致,但是在下面提到的两点上有较大差异:9 N3 w  @4 [2 v: W3 `4 V7 t+ w! F

    8 k9 t9 S  ~" a& x( ?+ b8 I* q& K二者对于某些对象的 str 序列化方法不同。8 \0 }& A1 \0 ?& f  Z- ]% j
    可迭代(Iterable)对象包括但不限于字符串、字典、列表。对于一个可迭代对象, string 类型和 object 类型对它们的序列化方式不同,序列化后str对象返回结果也可能不同。例如:
    " @0 J# r% q  C4 [& z- Rs = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a', 'b'], 0.5, 'my_string']). W" W2 G$ U: p: X& @- v: }
    s
    $ m+ X" D/ T( K0 a- u% C1
    - c2 O9 b* ^9 Y8 C2' c' Y1 q7 {, H; Q
    0    {1: 'temp_1', 2: 'temp_2'}8 ~. o' J% _$ I
    1                        [a, b]6 K6 b: F7 o3 w
    2                           0.54 w, l1 ]  v9 Q- c* ]
    3                     my_string
    ( A; o9 R( W% T+ e$ tdtype: object
    * B1 k% v2 J2 V# O$ [7 W/ z$ B1( n, P1 e0 I$ j& u: w
    2
    - [- `; L! Y+ d3
    9 I8 R/ r# l2 n9 f4
    ; I) A! N6 J: _1 K58 |$ Y* m3 |( t( r& f
    s.str[1] # 对每个元素取[1]的操作, ^* v5 N" D) p  |" @' u9 Y  b
    15 _" f( e/ m, a7 O
    0    temp_1
    $ V9 n* ^$ ~! n1         b
    7 e8 J0 g6 b$ O& }+ ^2       NaN4 u- \( e. Q4 S! G0 n
    3         y
      D# j! G; a! W: d1 f. U% Ydtype: object
    + R# F3 j- F& |: i8 ^1. W3 \% D. N( |! {: d% h7 ^% j
    2& }5 w6 _% g% W: a: |# ?
    3
    ' g3 l6 t5 _  i% ^4) B# a7 S% v- t7 Q
    5# c, J/ \# ]0 i" @! m; P5 s0 {
    s.astype('string').str[1]
    , W" P# N: N* c0 g10 Q! @" E7 O$ q- S- M7 c4 F
    0    1
      f: S" ?( [) N1    '
    # g2 E: \( o8 O2    .
    1 L+ S- t+ T. ]3    y
    5 S4 u& x: Z1 [+ f# fdtype: string8 r9 w# U) \; ]; w, U# k
    1
    . T' |# q# G- R! [6 G5 ^" w25 V6 v4 A/ t* t- C+ l! ?9 o- z  L# o0 l% [
    36 H3 k$ X- v) C& I+ ]) l) v) ~9 d  b7 t  x7 ~
    4
    2 S3 j' Y& q8 s) k5 |' [8 n5' K+ b! `# A7 H' b! I& M
    除了最后一个字符串元素,前三个元素返回的值都不同,其原因在于:3 Z7 v" F: [- J
    ) _  ^1 `; B4 W
    当序列类型为 object 时,是对于每一个元素进行 [] 索引,因此对于字典而言,返回temp_1字符串,对于列表则返回第二个值,而第三个为不可迭代对象,返回缺失值,第四个是对字符串进行 [] 索引。
    9 b! @* s. I! t4 J% x& h1 jstring 类型的 str 对象先把整个元素转为字面意义的字符串,例如对于列表而言,第一个元素即 “{”,而对于最后一个字符串元素而言,恰好转化前后的表示方法一致,因此结果和 object 类型一致。) d2 o, r3 b' @
    string 类型是 Nullable 类型,但 object 不是
    ( M: N; G- E. Y/ R, j  这意味着 string 类型的序列,如果调用的 str 方法返回值为整数 Series 和布尔 Series 时,其分别对应的 dtype 是 Int 和 boolean 的 Nullable 类型,而 object 类型则会分别返回 int/float 和 bool/object ,不过这取决于缺失值的存在与否。  {( j+ U( R3 g. E  F& m8 o' j
      同时,字符串的比较操作,也具有相似的特性, string 返回 Nullable 类型,但 object 不会。
    / `8 K; o0 u; a4 r* ^s = pd.Series(['a'])5 F( v8 i; j( T* r& F1 A  @

    ) b8 N* y- ?* C9 g- Hs.str.len()( s- N+ F3 k! t# N' y
    Out[17]: * k1 Y: b, ]4 F9 e2 Y$ S0 _
    0    1: x) Z- {/ \' l" k" ^* @: |
    dtype: int64- E) o; y* A- s/ w/ s
    " A1 b$ D6 s, X, k9 m
    s.astype('string').str.len()0 q: q0 I: H& J* V1 B1 M
    Out[18]: 4 x5 ?2 f. l/ k' u, j. T
    0    10 o( B; K: W' ]6 L* Q
    dtype: Int64
    7 g2 Z" P1 ^0 k6 m+ R- n2 w- B, D8 @1 d3 D* X3 ~9 M
    s == 'a'
    2 H8 d  q* ^7 bOut[19]:
    , B  K0 w/ U7 X4 E& O4 {3 m* l0    True" p; q' |$ U9 N: L+ A" {: [
    dtype: bool
    6 T, P8 w! D7 H1 |! m' u6 q1 b% J
    # Z0 C% R$ i! F" i( ]0 rs.astype('string') == 'a'9 e/ j2 D# h: h
    Out[20]:
      r3 g5 h" s/ [4 L0 X* C0    True
    / Q$ k3 b" l) |+ m0 \5 l5 ?dtype: boolean" g  U: Q3 E- `
    # T( @0 q2 [. k8 B
    s = pd.Series(['a', np.nan]) # 带有缺失值
    ( s$ i7 k- H. d& v$ k9 n% m2 R# ]4 w# _- M5 r  D: Z8 E+ s
    s.str.len()6 o8 _9 [# N- z
    Out[22]:
    / a- c( c3 x! ?2 Y, o" ?" {7 K0    1.0
    $ ~2 E7 v8 ~# M3 @" q8 [4 A1    NaN) e  i' \5 F. H0 ~- Q# R
    dtype: float64
    ( b' \/ N8 d; |3 q, g  K: z4 d! e- e
    s.astype('string').str.len()5 t8 X4 ~3 I: b& Q
    Out[23]: ' M: K  W2 I) Q5 y( |- M# L$ ^
    0       1
    $ a) W5 S$ I" D- P- ~1    <NA>9 W2 A0 J" @4 v0 F) K9 t
    dtype: Int64
    ! R8 b# J5 p' g0 ]7 h& T5 ]) E6 l$ Y& N, P1 y( ^
    s == 'a'
    * b9 t% |2 ?( b. h/ JOut[24]: # s2 r) R. F$ ]" [) _/ J
    0     True9 {2 [2 Z  ^% v5 ~0 {# h" @8 n3 `
    1    False
    0 J8 C6 r9 y4 \' X2 U6 P( Ldtype: bool
    ! ~9 A4 h* |0 L
      d. l( L: {7 v1 P0 ~+ o" Ys.astype('string') == 'a'
    : H. J0 k$ H  n- Q7 y) P- BOut[25]: 7 W6 t# W1 A$ P& ^2 w. X
    0    True7 ^3 H) A- r' [5 h: V6 v
    1    <NA>2 `* {6 x* T. @' j+ d* }; Q
    dtype: boolean6 G" s. x" P1 T+ E7 `8 {) O- g
    4 R+ @5 k5 b1 c1 n1 ^$ r
    1. q6 J6 w" z1 b9 K
    2
    + ^4 v4 [- ~2 [3
    1 A8 Q4 f6 o" p+ O: Q1 ?8 w44 H1 k2 G+ W3 A' P, n; {( n% I  n1 ?
    5
    6 ]; H5 [- @1 }, N5 _60 _" f3 S7 A; ~' V0 q6 [
    7
    ( B! @3 d/ I1 N. a: _' V87 X: s9 ^4 U, m' p: c' ~- }
    9
    0 c+ q. ?, `# _+ U7 d109 F8 S3 J) a9 @# ^' E
    11/ y9 p9 b8 [) K; `, y( V9 r* g
    12' b+ v. Y) o) r, b4 i% C" \1 z
    13
    ( }5 @- a8 p6 Q- {' l14
    ; l; d4 K; K1 j8 L& B' C8 j15" B* o# g* k. Y. j" W3 C
    160 w) x" [& y! G( a
    177 `9 z5 n7 B9 M5 P
    18
    % O1 R, F4 N1 B( K( j" S4 v191 d: @( r! _1 U& D4 Y
    20
    1 {" w& A3 w) q/ r21
    & o1 P+ m' O/ X" `$ G22% K1 V% T1 a7 T2 D( `1 O7 F* n
    23( \! o/ J$ b  h% B$ h) n& `
    24; Z$ {/ y, W3 L% K5 \+ x
    25# [: r, }& F5 r& L# D
    26$ X0 a3 J# r0 h' w
    27
    ; q$ \9 w) I1 g8 a3 ~7 I  e# @6 L6 D28
    - I- z7 |+ K, f: u, c) ]  M9 g293 G3 a" ?2 r5 A8 J& L5 f: \0 [4 |
    30
    $ c( l) g. k; V1 t; G8 s31' V: V! G0 @, v8 R, d
    326 u' r' I$ @7 B  q3 e# c
    33
    9 r1 T3 d; t' J! Z34
    * T) ?. g6 q: z  J6 d35
    8 {0 |% T$ D' N* `" M& Z/ k# S36
    4 M5 L& j. T9 O' ~37
    $ I6 ]8 B0 Q: s  Y% w" }" n; D3 P383 W! t3 I# G( G. p
    39
    / W9 X% P( {( d0 ^, M; E% M$ `7 d40" v5 ]+ M6 |# L; H0 b
    417 ~6 P: \8 n1 u% X& o. W0 q
    426 X, I, W  X) L& P' T
    432 f* ]; `9 z7 Z* q
    44, ]5 k* b* Z) v+ _6 p- F
    45
    % {& \! N; S/ C46
    ; F) X, |6 M  O8 A) F) R47* j  ^' `! N9 R9 _* h  a
      对于全体元素为数值类型的序列,即使其类型为 object 或者 category 也不允许直接使用 str 属性。如果需要把数字当成 string 类型处理,可以使用 astype 强制转换为 string 类型的 Series :2 s# ?1 t0 F4 u; o
    9 X+ k7 {. i; z& _  ^6 R* J" ?+ L
    s = pd.Series([12, 345, 6789])
    0 x7 ~8 f7 p* Q) H8 p) t. `0 v" d+ |- }3 e0 X
    s.astype('string').str[1]1 v6 ]" G& _# ]' X) {: ^( M
    Out[27]:
    3 a5 v1 ~: j! F7 B" ~  J7 g0    2
      L$ V' o: A: n7 O( s  I1    4
    3 B" R- g9 M0 g/ g5 ~2    7
    ' C1 @- v; {  b0 f& \1 |dtype: string
    1 q- }5 s# Q+ J0 `, C' R: q. }/ ]1) M% p- v5 c0 `- ?, @9 q
    2* h* X0 o# P  G4 @( n) Q; K
    3
    2 @  N( |- ]6 R2 @5 R6 p4
    7 M4 ^1 A; O6 ^/ F9 @5
    ! w, T7 w0 I) C& ]! ^: U# C$ k6
    . M8 M- O" C+ I7 ^/ H; c) P6 x' q3 E7+ \  X' S2 F% E5 j5 |
    8. m+ O! [6 z4 Y
    8.2 正则表达式基础; p) O0 z/ m* u4 C: V$ Y" i) r
    这一节的两个表格来自于 learn-regex-zh 这个关于正则表达式项目,其使用 MIT 开源许可协议。这里只是介绍正则表达式的基本用法,需要系统学习的读者可参考《Python3 正则表达式》,或者《 正则表达式必知必会 》这本书
    9 V+ }. y. }6 x0 I) D' p. K' e  I
    8.2.1 . 一般字符的匹配# b4 m6 G& o2 u1 C! f; I
    正则表达式是一种按照某种正则模式,从左到右匹配字符串中内容的一种工具。对于一般的字符而言,它可以找到其所在的位置,这里为了演示便利,使用了 python 中 re 模块的 findall 函数来匹配所有出现过但不重叠的模式,第一个参数是正则表达式,第二个参数是待匹配的字符串。例如,在下面的字符串中找出 apple :
    , E  D6 P( v% l3 P
    & G$ \8 {/ W# [# a, ?import re
    1 M( I: z, D) E
    + A7 i, i6 k6 @5 i  c% j; @5 V; ~re.findall(r'Apple', 'Apple! This Is an Apple!') # 字符串从左到右依次匹配
    , Q# ]* m, N+ R/ [Out[29]: ['Apple', 'Apple'], Z9 H7 I" e" ~2 e$ E% {
    1
    ; E, F3 B+ Q- Y4 }  w' s0 ]6 S! y25 m$ h' r& d! C- i
    3
    1 ^' q+ `5 A/ Q9 V8 d8 o4
    0 A0 W: z7 p6 c3 o! i2 w/ S0 h8.2.2 元字符基础: A2 D& e! c$ y* v0 V( |5 z7 L' i
    元字符        描述
    : o3 ~  @2 f" N- t; }, d.        匹配除换行符以外的任意字符3 w+ O) h+ T! t, l, ^7 e. Y  F& D
    [ ]        字符类,匹配方括号中包含的任意字符
    ; k- ~5 U/ l5 b- k0 j$ }( ~[^ ]        否定字符类,匹配方括号中不包含的任意字符
    / c7 s. Y5 m- |" f* i8 F7 B*        匹配前面的子表达式零次或多次
    . m7 {3 z: M3 T0 g1 g3 X8 `+        匹配前面的子表达式一次或多次。比如r’d+'就是匹配数字串,r’d’就是匹配单个数字
    $ H# M( s, j% J5 q7 |/ H1 e?        匹配前面的子表达式零次或一次,非贪婪方式& D: i; z( n1 y4 m. i& J# y
    {n,m}        花括号,匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
    1 G+ V1 A- E# [' k% v(xyz)        字符组,按照确切的顺序匹配字符xyz0 Z7 f* V+ I4 d- H9 U
    |        分支结构,匹配符号之前的字符或后面的字符0 w. T  I8 }0 K1 e& {; C' q! a$ ]
    \        转义符,它可以还原元字符原来的含义
    + R+ H# r. h6 x^        匹配行的开始
    9 O9 g1 \8 ^$ X4 y- z3 k$        匹配行的结束
    ; \# F* Y$ ?5 d3 x1 L9 ~" ^& X/ [, jimport re1 @1 W; m& x& H' y0 i+ b
    re.findall(r'.', 'abc')7 g- S# \3 z/ S& I  y9 i! u* t
    Out[30]: ['a', 'b', 'c']
      }" a6 f# K( _3 H+ Z% X
    . U7 w& t9 x7 M2 z$ ^re.findall(r'[ac]', 'abc') # []中有的子串都匹配5 B# A- j7 z" c& H6 b
    Out[31]: ['a', 'c']/ U$ N* F6 V0 |; P7 }" f: q
    " M% P/ J8 V( w3 \
    re.findall(r'[^ac]', 'abc')
    % r6 s4 W: _) }  n  Y8 yOut[32]: ['b']
    : a. V% t% w  k% \) e* a( ^  X  X) S
    4 _0 ?( z5 [" {; }4 }re.findall(r'[ab]{2}', 'aaaabbbb') # {n}指匹配n次
    " l; D& z3 A& W7 e/ \/ j1 E6 e, YOut[33]: ['aa', 'aa', 'bb', 'bb']
    ( ~# Y2 u6 i3 ]6 T$ P0 A4 [7 G# A& {$ J7 C; F
    re.findall(r'aaa|bbc|ca', 'aacabbcbbc') # 匹配前面的或者后面的字符串+ p% R% J( y. f/ k) d
    Out[34]: ['ca', 'bbc', 'bbc']
    ' ~2 h! Z3 ]% k# S% D/ _9 J6 Q$ A  w) Q2 t, u
    # 上面的元字符都有特殊含义,要匹配其本来的意思就得用\进行转义。7 n9 f0 T8 L1 g" ~1 [
    """
    ; _% j8 ~1 E4 {; T" c0 C) l1. ?匹配的是前一个字符,即被转义的\,所以|前面的内容就是匹配a\或者a,但是结果里面没有a\,相当于只能匹配a。% f* `1 L% e# k8 `$ ]1 W: v3 c
    2. |右边是a\*,转义之后匹配a*,对于竖线而言左边优先级高于右边
    5 c! q) t1 z+ n1 T* u; k0 T3. 然后看目标字符串aa?a*a,第一个a匹配左边,第二个a匹配左边,第三个a虽然后面有*,9 Z7 M- p( x0 y" }9 u
    但是左边优先级高, 还是匹配左边,剩下一个a还是左边,所以结果是四个a7 h: Q" j' o. K$ w
    """' D- @0 ?' r! s2 y, |" a3 O' m

    - @' Z7 I3 V+ F, H+ Nre.findall(r'a\\?|a\*', 'aa?a*a')   # 第二次先匹配到a,就不会匹配a?。a*同理。
    2 h9 L1 G. g- E5 c' Y- b- nOut[35]: ['a', 'a', 'a', 'a']' e) \2 `9 c# d1 c1 U

    0 y% T  _+ {" E9 n0 S4 d5 f5 J# 这里匹配不到是因为目标串'aa\a*a'中,\a是python的转义字符(\a\b\t\n等),所以匹配不到。+ i7 r, S; g' H: i
    # 如果是'aa\s*a'之内非python的转义字符,或者'aa\\s*a',或者r'aa\\s*a'就可以匹配到\字符。0 F( C8 Z: ]; ~7 C8 I
    re.findall(r'\\', 'aa\a*a')
    ! f' E: b$ z9 n. v[]
    ) M3 W% v( y! }4 T; @
      ]/ y5 n8 b+ T3 \+ O* M$ F- D, @+ l1 xre.findall(r'a?.', 'abaacadaae')
    - j8 h2 a! v6 J: D) \Out[36]: ['ab', 'aa', 'c', 'ad', 'aa', 'e']; ^4 Q6 m1 p$ i) J
    # O1 u& t2 ^7 j" d1 d+ c' T" O
    re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10') # 多个匹配模式,返回元组列表# l9 R8 b3 l$ [! g
    [('width', '20'), ('height', '10')]
    ( i9 P; T% p7 t" Y/ a# r5 i
    3 x# q' t# g8 U& l1
    " k! ~9 g  f9 {% X4 ?6 H$ X: x2* D$ G3 s5 W- I( U6 M; A
    3
    ! V1 u) R1 `9 ]5 Z2 _  |, C4
    , f0 a) X6 ^- F4 q+ c5
    0 b' S0 Z0 b- I+ |6" [9 R5 Y% V; ]7 K" {$ j
    7
    1 A2 W; c& ?. F88 \7 t4 I' _5 n0 D# U0 W
    9+ T, U' r5 e. X$ W! c* t8 L4 W
    10
    8 S1 [6 ]  N2 [/ o2 Q1 N11
    5 w( {% H4 C/ o+ O$ y, z9 Y12
    9 J( h1 g! L. J, u; v13
    " `5 @3 G: R3 ]: P, n7 q4 _# R8 a143 {. Z- U* h6 n& O% M3 I0 X
    15
    ) e1 Y& \$ O$ K- o16
    & M" b: a6 B; _0 {9 R$ V& p+ d+ H17
    3 t% y8 n; Z' `  F* c9 i& x18
    ; I& @5 I" J+ l6 z  }3 I19) h" n; Q4 T: y& ^5 o& L- s% E
    201 S2 L& c' `2 J  l
    21# I7 \  S: A% ~$ x4 Y1 p
    22
    ( {" w; h( w) V; b* [23
    & n9 e% K6 n' T" K24& Q: o. C! Y9 v5 ^
    25
    5 |1 |( F$ L* H8 C3 Z3 f0 J26
    ! n2 f9 Q3 S! j$ T27% @: q4 b9 ]" t' J. _; L
    28
    " f, f% n5 F% ], ^, H# L, ]  M29& L: _5 v% a- B; X# a# c
    30
    0 |# i8 j2 n9 `( g5 E+ ^: F0 u7 Q31  V3 N/ U7 S% I
    329 S3 W5 k! C9 F3 T9 Q8 t9 @
    33
    7 C; N6 ~- q. N% k. c+ t- p34* E- V" o: z4 E; @' }  C
    35  {8 S6 |* Q) t! L7 t9 v6 S" I# @
    36
      e2 Y/ A" I4 w1 g( \37$ q1 F6 W$ f# k# h& M
    8.2.3 简写字符集- n. w: H# K5 L  B
    则表达式中还有一类简写字符集,其等价于一组字符的集合:% f( w$ r. s7 z( \9 Z. u8 g

    5 \& b$ x) e% [; Y简写        描述
    4 J8 Q1 B( A' g" l) Q\w        匹配所有字母、数字、下划线: [a-zA-Z0-9_]
    " n" [, `0 r: ]$ w5 t5 `, y\W        匹配非字母和数字的字符: [^\w]* r, _; G( |, J$ f0 G
    \d        匹配数字: [0-9]
    ) s" m8 b# s6 `5 M\D        匹配非数字: [^\d]% L' ?" `- I% V
    \s        匹配空格符: [\t\n\f\r\p{Z}]
    / b# o, V8 U: B- R) {7 L\S        匹配非空格符: [^\s]( _" n7 O8 n% b0 v, O; Y" `7 V
    \B        匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。$ Y. O0 W! `! n, y
    re.findall(r'.s', 'Apple! This Is an Apple!'): ~* e; \# P' t" _0 u
    Out[37]: ['is', 'Is']
    5 d8 x) a1 ]' ~  O; ^5 p$ W
    : w; m3 w& @! ]8 }% U1 Lre.findall(r'\w{2}', '09 8? 7w c_ 9q p@') # 匹配任意数字字母下划线的组合,但必须是两次
    3 n1 m1 `- @; `0 J! cOut[38]: ['09', '7w', 'c_', '9q']* h) ?6 p1 J( K3 X3 A4 J' l
    : ~5 u/ z# R/ i1 T  T
    re.findall(r'\w\W\B', '09 8? 7w c_ 9q p@') # 匹配的是两个字符串,前一个是任意数字字母下划线(\W),后一个不是(\W)4 p: a6 i, i( i7 K) B+ E, j, w
    Out[39]: ['8?', 'p@']# c$ p8 l. Z' S7 L" y' h4 n
    % x7 Y+ |" C. d0 E% x* D
    re.findall(r'.\s.', 'Constant dropping wears the stone.')
    1 c: d$ M6 W% x' VOut[40]: ['t d', 'g w', 's t', 'e s']3 _; m  s4 |) O

    8 e9 {0 j% y7 D# ~! `re.findall(r'上海市(.{2,3}区)(.{2,3}路)(\d+号)',
    ' y" A. S7 c5 M& `+ `0 [           '上海市黄浦区方浜中路249号 上海市宝山区密山路5号')
    6 z# g- L9 H1 U6 c) ]: n+ {% r7 J. {
    Out[41]: [('黄浦区', '方浜中路', '249号'), ('宝山区', '密山路', '5号')]+ p! U6 c. P: S/ Y, R: F, B0 W

    ! g/ H0 y+ B7 I, r1
    # Z" |7 j- ]; }5 L2* e: ]8 H8 I( h7 `% G
    3
    9 q* A: R% E& ~- Y) ~: x$ o4
    . V, S' k6 R3 J) {. |5! C+ s9 f* @/ F+ M* g0 e+ R( k5 Y
    6& A* k" n8 c; `# ~( B0 Q
    7
      |1 U1 ]1 r& ]& x84 F3 n& K, b) D
    9
    + E( u& {- ^/ k' i6 }100 W! w0 `" o5 L" g3 p2 d& o' r# h
    119 u% S, L* v9 V' c) J* h
    12
    4 n+ X$ `2 n9 P0 T' \13
    : ?) ]) P: \3 t! L) x: j- O14
    2 @* T2 b# V$ f: S6 ?+ F% r3 S" a150 f; i7 U6 d, K
    16) p! p% b' u2 u; b
    8.3 文本处理的五类操作
    * e9 t  x/ }2 U) B2 a7 c8.3.1 str.split 拆分; R  `# o8 i) |+ C9 x* \
      str.split 能够把字符串的列进行拆分,其中第一个参数为正则表达式,可选参数包括从左到右的最大拆分次数 n ,是否展开为多个列 expand 。
    6 [. i  V6 _3 B$ i& o3 p* U8 e3 ^! N. F; Y& H, r9 r
    s = pd.Series(['上海市黄浦区方浜中路249号',' |- c  q* h$ N: g$ N& J! ~4 L
                '上海市宝山区密山路5号'])6 N/ X7 l) s% N. E3 Z& t$ d

    ) _* e$ z7 @2 K4 h* v9 f9 X- S
    ( D* H- I. s% g) ss.str.split('[市区路]') # 每条结果为一行,相当于Series* r7 J! h4 O  l
    Out[43]: + Q( d9 T# J9 C( G! V3 ?; K2 d! Z
    0    [上海, 黄浦, 方浜中, 249号]
    1 ^0 n9 {/ W2 q& w3 ^2 @) F1       [上海, 宝山, 密山, 5号]" Q6 m- Q' I8 X* _! o$ p
    dtype: object( ?+ ^1 g# I( V; V( A
    ! C: m0 F6 e; U' s9 q
    s.str.split('[市区路]', n=2, expand=True) # 结果分成多个列展示,结果相当于DataFrame, e' F0 c5 @8 M% n
    Out[44]:
    3 [! K$ d3 c8 n* j+ P! c4 d4 \    0   1         2
    3 f; U# |+ j4 h2 d0  上海  黄浦  方浜中路249号
    : `7 @5 t2 V% i; e3 P1  上海  宝山     密山路5号
    9 D" w; F3 B' O0 k. n5 a2 f1
    6 \+ m* N! _' i( J' S& F$ y7 |2* J: Y; ^6 N: p$ `! |
    3
    : s! n3 a3 Z$ i- Q! T41 T. T5 L% n, _9 \" `
    5! f. E1 U! G. z1 T
    6
    9 X) L5 c9 |, u# A; C; z! n7$ ?5 a. b5 R$ o& [
    8
    5 Y9 Z, |9 D6 }- O! Z* P# |9, V( B" D4 i0 C$ y! D$ X, W
    10, `+ N8 n% `, S- z4 E
    11
    ; _2 n0 p! F: B- z3 ^12
    ! Z4 v" m3 H8 E# b7 I9 h13& w* y# P. i! z& L! o: M. Y
    14
    ! M/ i/ v7 N* V) a7 U15  w) ~( d7 Y) q; l! [, m
      类似的函数是 str.rsplit ,其区别在于使用 n 参数的时候是从右到左限制最大拆分次数。但是当前版本下 rsplit 因为 bug 而无法使用正则表达式进行分割:
    ! d% ~' G4 A7 T" h
      O2 m' s& _: I( e. n$ P- Ws.str.rsplit('[市区路]', n=2, expand=True)1 W" `. }( s" N7 v4 e) c
    Out[45]: $ x6 I: T3 X9 J9 N0 e3 h! i
                    0
    ) w# j: {0 L7 Z$ S0  上海市黄浦区方浜中路249号
    0 k& V: m1 W; _" @* R/ o1     上海市宝山区密山路5号2 \' a$ V+ c# b& U" @: S
    1
    . u( O/ @9 S& U, V% g0 K2
    : `) j; }* R) I. E- p( y3% O6 `3 A! U  o- V' P3 r  h
    46 d; c1 p& v% O( f8 m
    5
    4 S. Q$ ~$ Y  R* i; @- e$ A8 j8.3.2 str.join 或 str.cat 合并
    - Z! g2 C; x; R; X- p' X% p& X% |str.join 表示用某个连接符把 Series 中的字符串列表连接起来,如果列表中出现了非字符串元素则返回缺失值。
    ) c" G; m% |0 lstr.cat 用于合并两个序列,主要参数为:/ @* |+ P/ ]/ ?7 b1 U1 f. ~
    sep:连接符、
    , k& b* K4 S2 M7 r4 M6 B/ V: ijoin:连接形式默认为以索引为键的左连接
    + M% y9 B2 @  z0 @8 s$ tna_rep:缺失值替代符号
    $ D- l0 Y) ]9 M1 js = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']]). t' G5 E6 a7 S% c
    s.str.join('-')
    8 }5 b+ `  `  z! c* P. ~' q* COut[47]: 8 r( h' B7 ^! t5 l3 D) A  B4 k
    0    a-b% e: V6 T! C5 {% }, u
    1    NaN: C* ?- G- x- g& e
    2    NaN
    5 [# Q# D1 R$ _& @' G2 }dtype: object4 j% B: G# |! D: ~0 N# m6 n
    1' Q+ U5 h& p3 o5 l$ q  b
    2
    2 L4 g" l1 G  ~9 ^& X! p3
    . u& C3 T& {5 ~$ L- r( @0 v4& H7 P4 f1 W" R8 c0 w
    5
    ; H0 [- R6 j9 n& n; m0 b  }6* H2 w- ^) x6 Q7 K
    7% |: i/ m) P1 n4 ~; Z- U" n
    s1 = pd.Series(['a','b'])
    8 }7 T& w% H0 A! A1 C5 @s2 = pd.Series(['cat','dog'])
    4 q9 S0 A8 ~% E3 Vs1.str.cat(s2,sep='-')0 W6 m6 Y  B( |  e& D  Y
    Out[50]: : L! F5 [6 q9 L$ y
    0    a-cat  o. a3 h, @( L2 b. ?: G) Y
    1    b-dog6 g3 A4 p; o; E$ i; Z
    dtype: object9 q) T" F+ a, u# v  S& z5 c8 Q8 R/ b
    - u1 V0 _6 v4 a* |+ y( O
    s2.index = [1, 2]
    # S+ @! L5 c% m, p/ C- @) bs1.str.cat(s2, sep='-', na_rep='?', join='outer')- r/ @) m6 ^2 S+ E5 ?2 b
    Out[52]: 9 R2 E( }- L5 A% c# H
    0      a-?
    " O) F7 D9 {0 E+ {6 w# r( y: C1 I1    b-cat0 ?% d3 a7 E2 P- G. x: x
    2    ?-dog
    * ]4 F9 F4 X! i, n8 Ndtype: object
    " O1 Q& {% e" V8 W1
      V5 g9 f0 J% ?2 f( A$ C) f2/ Z) }' v$ N0 w& q9 w
    3
    ) F4 ?3 n% B( j% r" W# p4
    3 G$ x0 p, L% K! W# N6 T' ~3 _5
    9 L  y9 x4 B# ]6
    # [+ m' e' }* B! ]; B0 C' ^& i7
    / A2 Y- w' z. w- K( L2 H! l* l. {8
    & N) F0 g( U$ D9  P# q- I! Y) u) A" r! j
    10% E# D9 [1 i/ c7 r! r' N
    11! J& e# p% L$ X% L* F4 o
    12
    1 {6 E* A, O5 }1 |- W- n13
    / h) N6 ^6 Y: ~* g( C$ ^) m* M+ z14
    # S  X6 l1 L1 w+ X15
    # E9 H# C. q' o6 j8.3.3 匹配
    4 D3 P' A0 R% Tstr.contains返回了每个字符串是否包含正则模式的布尔序列:
    # W+ ~, {# T( w) as = pd.Series(['my cat', 'he is fat', 'railway station'])* F: v% H  V( c% O6 M: j
    s.str.contains('\s\wat')8 l& ?* u, n8 G! E% e7 `

    & ]1 Y$ \2 b9 T0     True
    $ p. |8 [0 i+ W3 W1     True# a6 T/ w1 H  C) t
    2    False' k4 \. r7 {% T
    dtype: bool) z# M6 \, P& r9 {( A: p- d) g
    1/ l7 {3 C& M* R' y6 ?: \& I( V
    2
    & U# p! f& ~0 d+ @2 i' o8 z& w3, x3 V. B) v, \8 X: E
    4
    : x" C2 x' @6 q4 Z  d3 N( v3 X4 O5
    ( U$ n# l  w: j+ m3 n! _6* R; [3 G8 K( D  _) c
    7
    ! }; O; P5 K0 M9 w3 fstr.startswith和str.endswith返回了每个字符串以给定模式为开始和结束的布尔序列,它们都不支持正则表达式:
    9 t, ]- ?( X8 }0 I8 e7 ts.str.startswith('my')/ k1 k5 f; y$ O+ l9 l/ i

    - v- C3 [' i) \8 G- Z7 U( @0     True, [- a1 G- j2 y! ^, Z8 r; r; N
    1    False
    9 ?/ d4 W6 Q2 F* ~. V& K( f2    False
    $ J) y( k0 l% K# Cdtype: bool" F% F6 l7 \/ Y2 f2 a5 Q
    1
    ! h% w2 Y! d' m6 a2 S) {2
      x6 M& ^8 k  h. M) a) z1 O# S: [3" K5 K6 [/ }! C: I# z, U
    4  O5 k: Q- b6 ]" \" c6 d
    5$ ^. v6 B- b  u0 b6 F  ~% B
    6
    9 P! X$ H0 E; v8 _# \; N- Ws.str.endswith('t')! x/ D1 T  b1 h6 O6 a( M4 B: d6 G% H

    5 k' h& f1 n9 c/ ?3 Y! {, ]$ B0     True
    ) d& y& w% [) |; K$ ~1     True
    8 n9 L) A6 d# L1 F2 D2    False
    - f0 O9 t. u* Q* _/ R2 Q% adtype: bool9 a$ G6 j( K# X1 F6 q: m" ^
    1
    ! Y2 _6 N0 C7 ^" x26 e( ], d' O+ Y% ]4 u/ M
    3. m, P  s8 q& [1 |( t1 R
    4
    ; y4 m5 H, a+ b' m5; J! l9 Q( ?: |  L6 _+ Y
    6
    1 r( D2 B) M2 a3 Wstr.match可以用正则表达式来检测开始或结束字符串的模式,其返回了每个字符串起始处是否符合给定正则模式的布尔序列。当然,这些也能通过在str.contains的正则中使用^和$来实现。(貌似没有python里的search方法)
    , W3 d# q1 |0 Z% f% ~s.str.match('m|h')
    ; l6 F' \0 ^. G7 F3 \s.str.contains('^[m|h]') # 二者等价5 O+ o* q# s/ S7 ^7 t
    2 ^; U" w/ [7 c0 S6 M
    0     True
    0 g; q1 s& h" H; L5 S9 S% h  {1     True
    ' i1 W! n/ b# E  Z1 P2    False
    1 e. H0 c8 I$ f! d1 zdtype: bool: p7 `: R2 F5 `: s( P2 D' t0 E
    18 Q% E! v; i8 ?4 q
    2
    4 {- W0 J( {; v: i33 l# h- Z9 e  R
    4
    9 S, ~5 F& [, P5; z" q3 ]+ A5 o6 A3 Z
    69 w+ [( N4 b% m+ U6 [- C9 q
    7
    * Z7 X/ t1 H9 ^5 `s.str[::-1].str.match('ta[f|g]|n') # 反转后匹配% c, Q' R9 K  h
    s.str.contains('[f|g]at|n$')       # 二者等价; Y' x/ O* E  N

    6 x' ~8 C; M& d6 Z" i9 e0    False: C4 q6 r0 P+ R" n! n
    1     True
    9 s  M5 Y0 I; P4 z* ^$ u2     True
    . q- C; ]# R. ?dtype: bool
    8 ?6 @! x% q2 n+ @6 n) _# k11 z4 c5 R( f+ g
    25 m8 T1 W7 _$ ]0 G, ~4 Y+ }" W, e
    3( E4 T8 w- y2 V9 }5 g% F
    4- M& h; y+ b; x' c
    5
    $ y0 E$ R% W4 \1 }( m  R* H6
    3 D. O2 O0 F3 ?78 j1 O1 j. h, @8 J+ b* U
    str.find与str.rfind返回索引的匹配函数,其分别返回从左到右和从右到左第一次匹配的位置的索引,未找到则返回-1。需要注意的是这两个函数不支持正则匹配,只能用于字符子串的匹配:
    & K7 Q) m( @  m# n5 e& hs = pd.Series(['This is an apple. That is not an apple.'])  [. \7 v( d% i+ J! v, d
    ) |3 \5 G' q7 h- }! p' o' t
    s.str.find('apple')
    . B: O7 G9 Y6 ~5 ^9 IOut[62]: 3 G  l, p2 a4 R: j  E" H
    0    11( w& I+ b  z9 `1 q+ I% U
    dtype: int64
    " c3 k2 W) Y$ \- G  t0 X3 k
    & {" _9 A: b6 z! x7 b( K4 P  Ts.str.rfind('apple')
    5 {  B( h, w7 E( f  FOut[63]:
    - N' E; k; J6 [8 S8 t0    33
    / C' c) l& @( E* f3 sdtype: int64: q5 y; G; ^  a7 G0 s0 a
    1
    ' R# C' U' k0 ^* t% V4 x) a2
    6 \+ `6 g/ \: S+ o* R$ ^% n7 Y3! n* @( G. Z* X, F- ^* f
    4
    ) _9 L& [% {. M* z& R( {5
    0 n7 S8 c/ ]6 h1 J+ U. S& R6
    % w& E0 D. x* Z+ B8 F  N76 X  G5 O, i3 T: p/ i/ y
    8
    : X% D! u3 r" U; U# [; i1 c* l92 m& u/ ?1 Q, e+ |' P- I3 G$ u
    101 t/ A: {/ L' A  B; s
    119 O' G! r3 c# I# a9 V
    替换8 p% S3 f3 \1 H4 G2 x7 m
    str.replace和replace并不是一个函数,在使用字符串替换时应当使用前者。
    8 a6 U) a1 d; @7 s7 r3 ks = pd.Series(['a_1_b','c_?'])
      `# q' G$ Q3 {' a# regex默认为True,表示是正则模式,否则第一个参数内容表示是单纯的字符串,也就是匹配字符串\d|\?9 r7 D9 k, L$ O
    s.str.replace('\d|\?', 'new', regex=True) * S. K' W( H& h! e" M  l
    , b- s) p# I2 D, V
    0    a_new_b
    * t- E) y7 C" {6 r1      c_new
    : F0 O; T3 o2 @- v" }dtype: object
    , I+ K6 R# T. ?, \1 O1 n2 j1
    8 B3 t; Z: F, t6 l; Z2: x0 j8 ?9 m' [) ?0 ~
    3
    & K# L( e- I5 z3 {0 k5 N# m40 h8 }/ b1 h4 K) F( [* [$ E
    5
    0 w; c- F/ N; |/ y6 _, ~6 R( M2 q% V6
    ! ^+ c8 L, @( L- m# Y2 O7
    7 m/ l: ?* l1 y  当需要对不同部分进行有差别的替换时,可以利用子组的方法,并且此时可以通过传入自定义的替换函数来分别进行处理,注意group(k)代表匹配到的第k个子组(圆括号之间的内容):7 L; j3 M+ q# d* G! |4 |9 v7 w

    , |6 N, o. \- c! Vs = pd.Series(['上海市黄浦区方浜中路249号',0 n0 Q4 A  i: m! o. k% i, b
                    '上海市宝山区密山路5号',$ }; C/ i0 W# b/ _$ h* i
                    '北京市昌平区北农路2号'])! E1 M4 @' y8 |, b5 g: Y' B, ]
    pat = '(\w+市)(\w+区)(\w+路)(\d+号)') z4 Q6 b' c8 O* I. j" @
    city = {'上海市': 'Shanghai', '北京市': 'Beijing'}# `1 m: }* F0 @" {" U* n
    district = {'昌平区': 'CP District',, I% ^- @6 o- {* Q) o0 r
                '黄浦区': 'HP District',
    9 h" K: R- L. N: ?3 C9 i7 E- T$ q! T            '宝山区': 'BS District'}
    9 {1 m1 M8 f0 f, G7 x4 J. croad = {'方浜中路': 'Mid Fangbin Road',; D; C. j! s9 c  Y9 V2 I3 C
            '密山路': 'Mishan Road',
    ' N2 G; m$ m0 u0 s2 W        '北农路': 'Beinong Road'}& P: E+ w! b: V
    def my_func(m):
    : ~6 p' O' P1 n  w/ |. X    str_city = city[m.group(1)]
    + z; E# Q% ^4 }# M& g: ]    str_district = district[m.group(2)]# C* S+ Y6 R+ [. A6 a
        str_road = road[m.group(3)]( [% P! H3 f& q
        str_no = 'No. ' + m.group(4)[:-1]& A7 d. g# A) h; H  X& ^
        return ' '.join([str_city,# d+ [( ^1 u2 Z& s& N% |- K2 q# J" \
                         str_district,
    0 ^2 R- e/ C  O3 x6 [3 p                     str_road,
    ( V- o9 H5 K' @2 ]                     str_no])
    3 e% g9 J# b4 L6 D% ]: V* n, bs.str.replace(pat, my_func, regex=True)" F( C0 @$ q* M" z3 l9 g

    1 K% w0 `4 @0 P1 ~! Q& @1
    % _  M6 J% B1 [6 i5 I8 p/ y, b( B1 {26 N" Q7 Y+ D" l# N9 @
    3  F7 V; |' _; ~% L) S
    4: O8 f# q5 ~, \' f' B
    5
    % d# y2 @1 ~& z5 |# d/ j# I6, P9 ]+ ~5 w$ J, O# T
    7
    + M' s7 C8 N3 ~$ W- z8$ \& r2 c7 ~# N" G- C' t6 t
    9: k1 H! g1 ~3 w' _; x  Y+ x& r: L# c
    10
    ; |6 U4 c: W# c. ~9 A. q11. u/ T/ i. T# Z' h2 \
    12
    % ?$ o7 S3 c  @* p13
    ; n5 Q% B$ ?1 q2 ?: O/ b: {9 P* ~14
    ) ~/ ]4 a$ a7 n. y" c7 x3 V5 T15
    , p- r8 c! ]& G: b6 [' B; E16
    " y  X/ g1 V5 W4 L% B+ G17
    5 s  d9 Q1 b) G/ l& {; p3 w* b18
    ) a8 J+ N3 t0 r- e! j, ]; p% B" O& _19
    $ V7 v- I, n( q20
    ' ?2 t$ [$ y+ o7 T8 A$ I21) R7 N0 H  N4 K
    0    Shanghai HP District Mid Fangbin Road No. 249
    - Z/ h5 _) x- o% {1           Shanghai BS District Mishan Road No. 5
    + v% X$ x  F" t" N" ]' h2           Beijing CP District Beinong Road No. 2
    $ @% _; E7 S: n) \; H* idtype: object
    5 q. s/ Q0 E+ ~9 s) u3 {+ V1
    - [( H. e( f6 f, C# S9 B2
    / i6 G% }* o/ u  d9 ?3
    : h0 P- {9 |8 p9 E: f  ]4- B1 o8 ^3 e1 c" r2 {( }
    这里的数字标识并不直观,可以使用命名子组更加清晰地写出子组代表的含义:
    . j* V6 F" C/ W6 \2 L8 x. b
    * q$ L+ c6 ^* V; [( z# 将各个子组进行命名
    7 M' T7 d$ s3 }pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'  s! l( F+ u& Y# W  T3 j$ b. E
    def my_func(m):! U: _8 J" p2 q, C5 R. B! U7 U- J- Q
        str_city = city[m.group('市名')]$ _/ ]0 b; A: T. K+ v
        str_district = district[m.group('区名')]
    % }, p& d4 F' x    str_road = road[m.group('路名')]
    0 Z6 {5 Q- C1 j- h    str_no = 'No. ' + m.group('编号')[:-1]8 U0 E5 ]" c- a$ p$ K* u3 P0 K; t3 u+ v
        return ' '.join([str_city,9 a4 \2 S3 {+ ^5 ^
                         str_district,
    ( K. Q5 f/ R' D7 K                     str_road,
    + _# X; `8 l" o8 ?" p2 C* X. c9 U                     str_no])9 d& `3 V- K8 u* ^. V; x
    s.str.replace(pat, my_func, regex=True)
    : v5 C& j6 F$ [  u# R. X- I3 I1
    0 J! V' M! C& p* y26 F) g6 Z9 u% ~
    3
    ; W( D7 n' u. X7 r  n4
      a9 `/ B/ U4 A9 n. V8 h5
    . c+ ~. y, |7 j3 [' H8 V6 Z0 [67 |) Y3 ?" t% X
    7
    % l: \7 O3 z7 v  v6 p9 E81 a2 k4 C( |+ f
    9* o3 R% E: d- k3 q! s- K
    10+ |( a& n) G& W1 m
    11
    $ N+ T3 u% g) C' s( x! \12; b- S6 ^+ c! {! O" B
    0    Shanghai HP District Mid Fangbin Road No. 2491 U# B/ j3 \1 v* D$ x
    1           Shanghai BS District Mishan Road No. 5
    , x1 |7 b5 L: ?7 S" W6 \2           Beijing CP District Beinong Road No. 2
    9 {0 z) [6 ^$ B% c8 v/ r0 }6 hdtype: object
    . `. o' m# k% {$ v# P+ h) R/ O1' D8 d5 }" J& ~
    25 o3 g. w1 \, C: H/ q5 {
    3
    ' A* Y& q% I: x" W4
    6 v  x! S. [2 P8 o" S3 p" k  这里虽然看起来有些繁杂,但是实际数据处理中对应的替换,一般都会通过代码来获取数据从而构造字典映射,在具体写法上会简洁的多。
    0 a5 M$ m: P+ [
    & \+ Q3 p- P# e" a4 c8.3.5 提取
    9 G/ A; A% {. q$ ], y. Dstr.extract进行提取:提取既可以认为是一种返回具体元素值(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的str.split例子中会把分隔符去除,这并不是用户想要的效果,这时候就可以用str.extract进行提取:5 T' P2 f/ e, w8 w* Z  X
    s.str.split('[市区路]')$ A( G7 t* @4 ~" |+ s6 s
    Out[43]:
    $ D* K! y+ }5 ^% Z0    [上海, 黄浦, 方浜中, 249号]' q. V% Q" @9 U2 J& a. F* C) x) g+ T
    1       [上海, 宝山, 密山, 5号]7 N0 R9 w$ n! \6 T  O" N4 Y
    dtype: object* D5 M$ B7 T3 c4 w: C

      ]& _& l- o# u6 M! R4 U$ Z) w8 jpat = '(\w+市)(\w+区)(\w+路)(\d+号)'! i* F! p+ C& D# ^
    s.str.extract(pat)
    4 @9 v1 D8 G: z* z. L' v5 g# {( y! DOut[78]:9 I( ~( o: o- E: W3 h
        0    1     2     3
    " Y6 v9 Y( d% E( ^4 ]7 W0  上海市  黄浦区  方浜中路  249号
    + H- V& H6 L/ X. d1  上海市  宝山区   密山路    5号
    ; \3 r- i( m2 I* ~- n2  北京市  昌平区   北农路    2号7 m6 T1 @+ V, H  b. o& C
    1
      w: I" d& M3 M$ J+ b8 x7 d$ O0 X2+ h& k  A7 h, S& d) ?
    3$ G$ t* \* \0 a3 W& n. {( }3 d( j2 |
    4
    " T  j. n# T& W8 W5 ]: R5
    ) j& |( n" _( E$ y" f0 R. |! u! a6
    ; p9 p: s1 |! J) S/ H7: P% n6 x: U. P+ h6 L) q" v
    8
      `) ?  l8 A! B! j( I1 s9  R7 T! h* D5 H/ ?* r$ z: e
    10& P, A" q' x6 ~, T; D
    11& I8 g" O. q, r  \, Z# ?. j: Q  o7 F
    12
    0 L) Y; I: d- S& F13
    ! \* R( u$ @& m9 m& x8 a  K通过子组的命名,可以直接对新生成DataFrame的列命名:
    6 H$ E9 h+ e2 u. g  [9 s6 }2 e& |( D8 A6 x/ v
    pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)', W# H0 s9 Z- ]+ a  L
    s.str.extract(pat)
    8 F# g: n" y  ~# GOut[79]:
    : C- {2 E1 K' w2 P' \    市名   区名    路名    编号' x; y3 b, l# b. W; m: B8 P
    0  上海市  黄浦区  方浜中路  249号4 {/ U, P  l4 \$ p1 s; a  R
    1  上海市  宝山区   密山路    5号$ c, V9 j' E& w; s
    2  北京市  昌平区   北农路    2号2 I  J$ D& x( c
    12 H: G3 ~4 r2 r
    2
    ' w, y# S+ x0 R$ z3* E6 K! J' X* |0 C) ^8 B
    4% P0 j$ J  r0 `& }6 e! K
    5
    & w4 l8 j6 u  s( P1 r6  L& p" M. k: _- g* b  ?
    7
    ! u& Z% i8 k7 `$ n9 ~% Qstr.extractall:不同于str.extract只匹配一次,它会把所有符合条件的模式全部匹配出来,如果存在多个结果,则以多级索引的方式存储:% O( P7 m( |) T. V. Y7 r% N
    s = pd.Series(['A135T15,A26S5','B674S2,B25T6'], index = ['my_A','my_B'])
    + E+ g0 }5 j/ {; _pat = '[A|B](\d+)[T|S](\d+)'
    * w6 ~9 v. u; j& a' Y! js.str.extractall(pat)
    - j0 ~- Y+ e/ h- r1 mOut[83]:
    $ `# v, x1 y% j       0   1
    ) N- o: Y2 O* Q1 v9 K- l; {     match         
    " `8 }# _! z% `" Dmy_A 0      135  15! Y; ^. [1 f$ Y; x* P, l, s( I2 M
         1       26   50 x7 R+ l+ C  q  _8 U5 \( f
    my_B 0      674   2
    & Z: \4 I! f9 p+ C" x; N5 j     1       25   6
      Q: L3 m/ \  ]( |1
    1 I. r" m+ o& o) K0 u" U2
    0 @: V' }3 ^7 @8 J( o35 Z. \  ^* B4 g3 ]; I
    4
    ! f& y4 B, v" {. _9 K" b5
    ) Q, L/ n/ ?# C* Y6( L7 @( L; ]4 \; M( z0 h4 n- Q
    7
    ) i7 I1 B" k8 p87 m7 K3 E1 z; M- q  V' Y
    94 N/ f# D# o- d* x: A! S
    10
    6 f' z" J7 F- d4 ?pat_with_name = '[A|B](?P<name1>\d+)[T|S](?P<name2>\d+)'& x; h. S! p  i) q% p) {- u
    s.str.extractall(pat_with_name)6 A6 z" Q0 l1 @4 x& ^% \* b# d
    Out[84]: / |+ m& B  q" E" V
               name1 name2
    5 J/ D/ Y$ l0 o& M$ {+ [2 M     match            
    . B! C% j# y/ e9 F6 zmy_A 0       135    15
    4 q6 k+ y) G* z$ A/ |9 W     1        26     58 r/ v2 \1 U+ C9 V8 ]- \
    my_B 0       674     24 S" b- P- W8 [/ {# z% x% e
         1        25     6
    ( t1 ~$ i+ X  J# v( Q" F* D8 Z/ A1
    6 o) k: _- x& w; u/ S2( ?& r  d4 F8 I  r" P
    3. w2 U9 r' M; h' Q
    4; i4 i7 J9 p/ C  v
    5
    ( y# N5 K6 p. U1 j* s) H: I6: F* P; |, G" @: ]! C
    7: @5 u- [: q& x$ f! T# \8 u$ d$ Y6 h1 c
    81 v) f7 g, }2 \8 S  _
    9* h" i7 n2 l- F9 P1 W+ A
    str.findall:功能类似于str.extractall,区别在于前者把结果存入列表中,而后者处理为多级索引,每个行只对应一组匹配,而不是把所有匹配组合构成列表。# _6 {2 E" [! U' j" B
    s.str.findall(pat)
    - J* K* H4 l7 @6 f10 ]7 O/ ^" q  z
    my_A    [(135, 15), (26, 5)]6 X$ T- K( p. J; e
    my_B     [(674, 2), (25, 6)]; ^7 H, J' Z& R6 N- F( x
    dtype: object" F7 q7 c& r; g% R
    1, R/ N5 d+ o6 p- N- ?4 {4 ?3 F
    2% R; L5 ]/ d' ~+ e
    3
    * K/ R, _" b2 J  v  l4 F. s8.4、常用字符串函数
    3 A! }- S. D8 ], Q  除了上述介绍的五类字符串操作有关的函数之外,str对象上还定义了一些实用的其他方法,在此进行介绍。
    % U7 a. n, D$ s' e/ s8 I& G( @* C2 p* Y
    8.4.1 字母型函数' L  S: `! {" J/ B
      upper, lower, title, capitalize, swapcase这五个函数主要用于字母的大小写转化,从下面的例子中就容易领会其功能:" C0 @# }4 g* D7 A
    ( S5 c, ^) G& o5 C2 b3 K
    s = pd.Series(['lower', 'CAPITALS', 'this is a sentence', 'SwApCaSe'])8 a* L! }+ f8 G  f
    * O5 a6 O3 a: ?: z: v; B
    s.str.upper()
    , R$ n' K  _( eOut[87]:
    ' D3 j+ Q4 J9 t  [9 L0                 LOWER
    3 ]0 S3 i, f- ?. b* N5 ]1              CAPITALS" i1 \/ ^; |6 d8 k: j+ v
    2    THIS IS A SENTENCE! x% R7 k0 a7 ^' V9 N5 A& w% k
    3              SWAPCASE
    6 n( @( c$ i9 i4 w  Ndtype: object
    " L! R9 x+ e5 x; V& L; o  }9 F- m8 j
    # J2 f7 E  }" H& J' J9 W- Bs.str.lower()- |; v8 R: i, q9 r, R; k
    Out[88]:
    / N; O4 m, t0 B7 n  Q- E" L$ b0                 lower& S# o4 f7 ?& ?6 `/ U; B; B
    1              capitals4 h" h# F) X: v3 ^4 t& O
    2    this is a sentence: m$ V2 I! \# V; {
    3              swapcase
    6 s( C0 n0 Q! ^  g9 B0 C# d2 |2 ydtype: object* |3 y0 T0 Z; H

    4 n3 l$ y2 z) m+ A  D  {# {s.str.title()  # 首字母大写
    + }7 F- A! V7 Q# i4 O- ^* ~Out[89]:
    ! ?  g" _1 z4 `2 g0                 Lower
    ; O6 Y8 J# {0 `0 w7 o, r6 C1              Capitals+ ]3 S( h3 F9 ~) v2 [/ l9 ~8 p9 w
    2    This Is A Sentence: @/ B8 Z# w7 R5 ?* n8 i/ k
    3              Swapcase
    + Z! |7 c' W% p& ^. ?dtype: object
    8 w) {# d4 d) D7 t9 a  L4 j+ `) N; T6 B& j4 U
    s.str.capitalize()  # 句首大写
    ! D5 ?$ v9 L- G; I* g. C; aOut[90]:
    - M" c$ Y9 @0 A8 @8 N# g0                 Lower
    ' I7 j- T4 d% [, v/ ~, C1 X" ]1 Q1              Capitals( K, g: N" y" e: M
    2    This is a sentence* k0 U- [& ~& o) i3 z6 @) ?, _- m
    3              Swapcase, `- w' h: Y, C$ g# v
    dtype: object- H6 F2 z+ ^$ W" P" W( F
    : ~9 P* a! j) I7 y8 b0 ]! H6 b
    s.str.swapcase() # 将大写转换为小写,将小写转换为大写。
    5 o- z% ]' R% s) x1 t/ O9 XOut[91]: : Q0 W& f* K7 {1 L
    0                 LOWER
    ) G! u( \7 T+ ]  G* G2 ^* @1              capitals
    ; f2 K" G! o! Y! G2 j2    THIS IS A SENTENCE
    0 X$ G: L5 B' ]# }! S6 \3              sWaPcAsE
    ! b. ]5 g9 j6 Y. {4 ydtype: object
    ( J4 U* T2 J6 n5 h& `& z' p$ S; T& p: a$ U" S
    s.str.casefold()  # 去除字符串中所有大小写区别9 N: F; _1 K$ b' `" V- v; w
    6 Q. L, o& A/ C1 x, m% d
    0                 lower& r- A& l" x( a1 {
    1              capitals4 g8 d' w5 m3 n" y+ F; f
    2    this is a sentence
    ! ^$ g- ~, ^3 i( p3 I& f) v3              swapcase
    ; E, j$ r/ A4 K( `* i% N; {8 o9 I2 z% V$ D
    12 N6 h' e; y2 L) P, t3 x" g
    21 H1 K: S8 i) n$ S" a; X& K5 x
    3
    % a) E6 h( A) {* H& N) }44 o2 k, j# k6 z* g9 Z, y
    5# p0 Z# m  V. a4 X1 K
    6
    ' i& h" z! |3 n% z7
    ! ~, B: s% ?8 [  v% l  Z( k: A2 \( h8
    9 O3 Z( b, f, N: j9
    1 ?- U9 j: d! {/ i1 \10
    * v% Y, T! `9 r# v+ g. x11) ~  D/ Q* Y+ n- k& M  V8 Y  O
    12
    / z7 o. y; G1 o13$ o/ u% E1 C8 E" L' M  a
    14
    ' S& |) j1 H6 `1 m& e' ]  Y) G15
    + E# V5 }( f( Z/ O1 X* i3 K; c16" `* m5 U4 m! P
    17
    & _8 X5 h7 d! P184 n+ Z6 f; K4 n' o- ~
    19* l8 U6 r! t% K/ `! R  H! z
    20
    # [9 L' e0 ^. A$ z/ w0 q21
    " f" ]9 w$ V6 C. m9 d& ]228 \; M9 m8 @, o
    23
    6 B  P6 h4 B$ |2 N% Q$ b+ V24& P! M8 W' q- O% s. V' ?
    25/ o2 `* L( \( k' f  N, D1 _. `  Q3 T$ @
    26/ n) l4 }) M5 [" ?+ w( @" b
    27
    # I' F" ^& @$ H; k4 a28
    0 V) y  r, j+ f* ~8 Z* P- x2 q29; J5 ^& X! K3 b( c
    30
    4 N' ]* F  [% e31
    ' y; ~6 t, R+ X) l' `- e9 a32
    8 q( y* Z; D; u  t33
      J) B3 ~4 K' M3 y" W34
    8 u$ R7 J& [3 Y, h( V# w$ }4 k35
    ) P0 Y' U- Y, _4 K4 O2 F6 F36
    " |; W! [$ {/ @% m+ s1 R! z! E37* F2 m# B7 I' |9 h- f. R
    38
    . T, u, v7 |9 O: X- o1 W4 m39! Q" |8 G" v0 ]) R/ s, t
    40
    . v6 y5 f- q/ j: }4 Z( Q5 J5 H41
    * G/ E6 U' c0 J& N8 g* m42  T3 Y2 m) x1 j# h, E2 Z
    436 n5 I  y' ~1 ^! x1 E
    44
    ; B- f! `) A. e. j  m0 j45
    3 L0 Z+ @0 N9 ~# a46
    7 u3 N, m; \; U; H% E8 p, M+ w: W47& i; \$ u- J  ]1 ~
    48
    ' @9 b* B2 B$ s( V" F3 X0 X8.4.2 数值型函数
    ; U9 X8 Z# n& Q4 _* {) Q  这里着重需要介绍的是pd.to_numeric方法,它虽然不是str对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。其主要参数包括:
    , Y  I9 i! o0 Z- p% @( d* Y* x) q* C7 r7 V6 i/ K4 c3 C. Y: k
    errors:非数值的处理模式。对于不能转换为数值的有三种errors选项:9 R6 ^2 S2 l! u2 z8 E
    raise:直接报错,默认选项
    & K+ N8 D2 s; t5 zcoerce:设为缺失值% R. D" F* |: t
    ignore:保持原来的字符串。
    5 Z2 J' g& z# V  A5 b' H. Ydowncast:转换类型,转成 ‘integer’, ‘signed’, ‘unsigned’, 或 ‘float’的最小dtype。比如可以转成float32就不会转成float64。
    & U4 q4 Q; s* r$ E& Y, Ls = pd.Series(['1', '2.2', '2e', '??', '-2.1', '0']), O! v. Y* b- t( K0 Z  U, B4 B
    3 I% n% o$ U: B( C7 a! l$ V$ X
    pd.to_numeric(s, errors='ignore')2 R$ Q' r% Q4 d: H: E
    Out[93]:
    : ~# M0 Q& p7 \+ Z0       1
    ) y' @/ w' X* Q6 z" N$ V1     2.2
    $ k& L8 M$ l2 D( ]% ~% P4 s2      2e
    9 C8 F) E6 Y2 V0 v. Y& t3      ??
    / A9 x0 e$ T( m4    -2.1
    " M) b( Q7 U9 L6 r) N6 z/ z) l8 j5       0
    1 E9 c8 D5 U2 {0 o0 B8 c, Bdtype: object
    # H! ]- _6 u' J9 W3 C8 `  r# }8 y: R0 V, `
    pd.to_numeric(s, errors='coerce')
    4 h& b! R! u' W) p* E# q) i4 {Out[94]: ( b; G" o6 d3 p/ X
    0    1.0$ T8 M+ r: ~  F- u) R0 Z) M
    1    2.2! U4 D1 L) V6 h1 C! I- R
    2    NaN
    9 F8 P' V- O1 U* i" ?) t3    NaN
      p# h3 o7 {+ Y( x  V) L: s9 K4   -2.15 O3 W; T2 m: L1 W! Z. p
    5    0.0
    5 S! }, q/ E8 U! h9 L1 Gdtype: float64
    ( D2 S2 r  ~  N( H9 @1 S+ p' _7 i4 K# |/ t- T# c
    12 i$ Q  F, z1 B2 ]( {
    2" D; z3 R9 w# q( N
    3* ]" x) K- S; l1 \
    4
    / _) y* R' E& r' s  H& u1 @5
    2 i4 c$ W  C7 z# r/ G5 G4 ]2 j6
    8 g' S  }! ^* u6 u' D7
    * F% v. k# C% l( x$ D1 V8
    ! z* y, m* b' O, Y5 b8 w91 m4 e; h& r4 N3 B
    10' z0 n5 K9 }6 N1 ~+ h5 `$ e
    11& M$ Z- c% s+ T7 j: p
    12
    : q2 c' i1 |" T: ?. Q. C# l2 ^13  x' @7 D3 _( U7 l
    149 |( w4 J+ B  J9 t$ B- D
    15+ c+ G0 c0 y' ~( {; }
    16& z0 l' {8 l. V: @  Y& `2 L
    17+ |& N- f2 l$ {5 b1 f2 w/ G5 t
    18
    ! i* z" b; n0 U19
    5 l! X. d! r% i4 l8 |* C/ m: y$ `20
    1 k- w' A1 k2 |0 f. x21
    . t* p' D. I# W6 |7 K: R3 k' f  在数据清洗时,可以利用coerce的设定,快速查看非数值型的行:
    0 q- w; h1 e2 S2 C, ^
    / Z7 ~4 w, I0 J+ \; Ss[pd.to_numeric(s, errors='coerce').isna()]5 h+ Y' q5 d% Y7 a6 h
    Out[95]: . C5 ]# d1 M9 l9 {3 ~$ E  b
    2    2e
    % B$ Q2 W% \' e& N0 j' j3    ??+ |, b1 F6 q5 n$ J* y/ ]4 r( q% A2 }
    dtype: object
    $ j' V! Z/ Y  l1 c3 U1 `; e1# R# G( Y+ }: j8 T- f
    2
    : S" T0 K! g5 c3
    # ?7 ?: B5 m# c' }$ f- S8 Q( `4
    . r# a* w! Z) ^. Y- D, ]/ A; v5
    + O; I8 ?' R$ O8.4.3 统计型函数
    8 e+ h, I/ N7 F7 R1 f3 _  count和len的作用分别是返回出现正则模式的次数和字符串的长度:  J3 d+ X! o" J( r* U$ b! D6 _7 z. E/ u3 p
    ! f4 z1 u2 v* ~/ \- @1 ]
    s = pd.Series(['cat rat fat at', 'get feed sheet heat']): I+ k7 I" [/ N2 F4 b

    " y' F$ u# E+ w% D" U9 J; Es.str.count('[r|f]at|ee') # |左右两种子串都匹配了两次8 G" K6 b' b! C! F1 T
    Out[97]: + @4 _' ~; z6 f3 m0 h, T. B7 g
    0    2% x3 E8 P+ C1 [% Y7 _
    1    29 h# e4 H* \0 V# z
    dtype: int64* {  @6 r: m& Q/ j

      g6 v/ Q- M% A$ qs.str.len()  o+ J) ^6 g& {& e$ w2 P
    Out[98]: 1 V1 E2 a, o* D( ^
    0    140 `0 A8 k% `' @' s" V. w
    1    19+ G3 {3 t0 F8 _3 N7 E2 P( I# @" B& ?
    dtype: int64& p+ I) ?6 |; I8 Z: K
    1! |7 ?7 T# |: N
    2
    2 N+ \9 ]; X( }* W0 ?5 b0 @3
    5 V4 Z/ z" [1 T1 N( W' N8 W; b1 j44 }# i$ k. L; W
    5
    5 W0 r; C2 x# c' i8 U. L6
    + U. c; u+ x5 b  ]) J76 r) f. ~9 v! {
    8' P" t- w$ s. r1 @
    9
    " D2 w( h, v3 N. W" X6 M10: X4 ~, M! G7 ?* A
    11# `6 N  Y' S. |$ ~* L6 ~( f: S
    12
    ( T5 ?# L7 ~1 ~" [" C) B13
    1 ?$ Y% p% p8 r! \. P8.4.4 格式型函数0 A! S: g- Z9 U
      格式型函数主要分为两类,第一种是除空型,第二种是填充型。其中,第一类函数一共有三种,它们分别是strip, rstrip, lstrip,分别代表去除两侧空格、右侧空格和左侧空格。这些函数在数据清洗时是有用的,特别是列名含有非法空格的时候。
    / ^! P. X. n+ o9 S" p5 ?# ^3 `, ?; C9 t' J. |
    my_index = pd.Index([' col1', 'col2 ', ' col3 ']): w# h) [8 R/ p: B4 H5 X! q2 o+ v

    - c" j' ?% a$ Y( x; u9 a: R, ^my_index.str.strip().str.len()
    , i- D7 c# P: AOut[100]: Int64Index([4, 4, 4], dtype='int64')
    - M$ s# _, U3 G, ~$ J) B- H0 @2 W8 A
    my_index.str.rstrip().str.len()/ f, w; ?# O9 L
    Out[101]: Int64Index([5, 4, 5], dtype='int64'), i7 K( v# C+ _3 U  p2 }

    6 p  D* a: K7 t0 P( g+ @my_index.str.lstrip().str.len()
    ! }6 H1 }+ y6 B# ~Out[102]: Int64Index([4, 5, 5], dtype='int64')4 O: c2 B  e+ t& q9 c
    1
      J: s! b* E3 M' U+ i0 `20 _  p5 ]% l( p  {' |4 j" A) V7 N- D) m
    3
    # G7 W, n9 O+ c2 q4
    ; q% u) R/ @2 X- e2 H" @% l5
    + `: K* A' [5 Y. T3 Q; J6
    * u8 W2 I, r; H" ]; L7* _6 c5 r3 C8 c' k! K4 D7 [
    8
    ! Y' _( H) B% ?; a: O9 h7 E- q9) n( w4 ~" m9 c" c& ]+ E/ @2 M
    108 J7 R6 |/ r) p' v. [+ T
      对于填充型函数而言,pad是最灵活的,它可以选定字符串长度、填充的方向和填充内容:2 X/ Q: L. C' m4 m: j0 T; @
    : |& j; F5 Z. g% I
    s = pd.Series(['a','b','c'])
      v+ E$ t2 }3 Y: B' ?! g3 h% k7 @& P& o% ^
    s.str.pad(5,'left','*')' j1 k' G& e% N9 `0 d' L
    Out[104]:
    1 q8 [3 v2 I6 B# D3 n" L+ t+ ]0    ****a
    7 ~, O" n8 P+ R/ u- F: k+ T1    ****b
    - }7 @4 o0 ~6 z9 n4 ^+ j  _1 i( w- ^2    ****c: U- q8 ~! c# w( r& q' A* u0 N
    dtype: object
    6 G5 ^8 p" s) r* B
    ' p3 M) z: s  Qs.str.pad(5,'right','*')
    $ d- O0 y  i: T# T/ m& {Out[105]: # p0 v/ k3 d0 P" a( `5 Y
    0    a****
    5 j' A; p6 K$ X6 Z1 X1    b****
    , r7 [* c+ y# W& G/ I2    c****0 w% v. h' C" }+ P9 _
    dtype: object
    5 r1 E0 b/ R' B5 j% W
    & r+ T/ w! M! n# |" C1 @3 I8 ws.str.pad(5,'both','*')2 T$ M8 z: y' s4 m
    Out[106]:
    : v9 O. z+ H$ J% N; r( P0    **a**
    , O. S# Q9 ~5 |0 p4 m9 E8 c1    **b**. C: r/ r1 x- Y4 e- r8 u
    2    **c*** q. t/ `! T8 x/ s% b
    dtype: object
    % W! y. H/ d. [1 g5 ?+ ^) R+ E- l, u% r4 @
    1
    ' ~4 V7 U$ F0 p+ O2$ p, \7 s- E( E; n' g* o% n
    3
    $ ^; l" p2 h3 N4. f9 I5 t  v' z, F8 w. h! N
    5, `  ~9 M9 M; b! o
    6
    # {  G; x7 e! L7; I7 k- r: L% ?
    8
    ( s8 U9 j4 U% z, ?8 }9
    " P6 y( @  b3 b# C. c# {3 X  f( x' ]10( A+ N8 @: O: J, a+ i
    11
    6 C/ l  _0 o8 e$ O12
    ! L1 b" m& v2 p& N0 s13
    6 ?8 Q, m  Q- Y2 F* ]14
    ! ?8 R; w; a, `0 i& C) P' A% i153 d( m# [* y' Q
    16
    ' k3 M: k$ Q* i7 t! W9 H; B& @17
    " X% t8 @3 ?% U  ], v18
    # Y& y! v% }/ B; z19) |: ?$ g; l3 e8 U/ k. c$ h
    20. ?+ C# M! L6 w6 O+ |! v
    21
    . ^( S5 E6 g0 {22
    2 B  B! M, y( I  上述的三种情况可以分别用rjust, ljust, center来等效完成,需要注意ljust是指右侧填充而不是左侧填充:8 }2 s9 z3 C. ?0 Q4 t  o

    / a% T4 g. Z9 Z8 Ws.str.rjust(5, '*')
    " t; o$ l* O* O2 [, VOut[107]: / Q6 j+ p. W% S5 l7 I% v
    0    ****a+ \$ v6 \% l4 x- d/ l  {9 h
    1    ****b
    - y7 U# p3 r3 w2    ****c7 }! }  W: \0 i. U" }: l0 `
    dtype: object( \2 U4 Y% T' t, N+ a6 f  |

    3 J8 T4 H1 O2 T7 p8 p) {# os.str.ljust(5, '*')8 Y% \% l. W' G) i- c
    Out[108]:
    % q3 D5 P- n2 O% G* y; F0    a****
    9 c3 y, d' Z* m/ \. E& Q4 ?) e4 i; A1    b****
    5 ~/ i# `: x7 ~' n- Y' W, C# Z2    c****3 p6 d* T4 j4 w, h- r8 N
    dtype: object
    ! @5 x, Z! b1 N$ @% N. _: n5 y2 q8 N# p9 Y! ^# {
    s.str.center(5, '*')
    : y+ ~: F6 _" `Out[109]:
    ' m8 m! y5 G+ N4 b8 [/ x0    **a**
    ' T/ r& J) j0 g* f: x1    **b**; G- \, o; K. k! z
    2    **c**  v" L5 w" x3 z; `# Y. W
    dtype: object
    1 M5 k: j4 U  ~) @# x, K6 X6 W7 ?, k% F. D% K; }2 }
    1
    4 i9 g9 m- e- Q% c/ ?( n( c2) R8 t: j$ R0 D
    3
    ) _3 G; n; d$ R4
    : J2 I- X2 s" I  @5; `' i2 t2 X8 H* V' ?
    6
    $ a7 G: m& A7 z+ N7
    7 Q7 C% B* |+ @, B1 p. B8/ j4 G+ m: ^) v
    9
    ( @3 [4 {1 y- P7 U- u0 Y10
    / ~3 x' M7 k4 i6 h3 f- P11. G9 r6 G: A" P$ V( y) `$ x- i
    12& h% M- `! Y; H" \
    13( Q9 i! v+ Z& w6 j6 ~+ {5 A
    14$ c3 {1 P, s( S7 |9 l
    15' q" ~! w9 D8 Y3 \
    16
    " z' t: C) }3 {17. u; B. e, ^8 N  O, r
    18
    ( _: t2 A/ G  i) o+ Q19
    2 N  v) T8 [4 g" }# t20
    * Z9 ~4 w& f% R+ V* l0 H  在读取excel文件时,经常会出现数字前补0的需求,例如证券代码读入的时候会把"000007"作为数值7来处理,pandas中除了可以使用上面的左侧填充函数进行操作之外,还可用zfill来实现。
    # F! X6 G4 t( c/ B, O( L# w, B8 c5 K
    s = pd.Series([7, 155, 303000]).astype('string')6 l! y4 y8 o$ i6 Y+ o# Z
      [& F3 N. v0 Y* Z
    s.str.pad(6,'left','0')* z7 v- A* n0 e! i- X0 M6 ]) c
    Out[111]:   p8 e2 a3 t/ A
    0    000007
    " c/ E9 M/ D, O5 w& [( s& M9 g1    0001554 a2 a- p! N1 r* ?6 @
    2    303000
    5 O9 q: n0 h: _6 b( ydtype: string
    + z2 P0 _0 n/ ~  l0 Q4 W3 d" L# y5 E+ a
    s.str.rjust(6,'0')
    9 b' N, {% k  S5 b# x8 Z, {Out[112]: ! W1 U" f! M. {/ H$ N. ^' _4 X/ K
    0    000007$ q2 H; G: [; [$ }% }" y, T8 v
    1    000155
    ( n- K/ P5 n& p, c$ [$ X2    303000
    0 S/ Z4 D- G% W" X9 e, hdtype: string
    * U# M, `7 }7 `! U6 N( p9 ?$ U& |0 p8 W* R' G4 k
    s.str.zfill(6)
    7 f& L$ `- \9 E6 l+ O; V9 NOut[113]:
    . e# r5 ]! d, C! A2 }; m0    000007
    ; m) C' y" }2 e- h; X% }1 z1    000155! B3 L. p8 o- h) M( n- \* n- Y
    2    303000( D% s0 i' O2 m
    dtype: string
    + I% r0 B: @4 H) p9 g9 U9 n- {+ T% g- g* \) u% e2 {
    1
    6 n! _; c& v; w) {0 c5 g  k2
    / M" U9 ?9 p$ E9 E, V+ b, I- y# z3
    1 b5 S# i, x: O) \# _48 p/ w* S: i: N* c
    5
    ! F% Y) g( Z9 f6
    , l& h% D" l$ [7 b5 j0 [6 k7
    7 G& ~3 x' z* @* W" z" P8 p+ _81 @# Y8 Q9 D$ X4 i
    9( f7 k2 V5 F# N7 B4 H
    10/ C% r  X5 t' ^: {( f# `
    114 f; J/ U$ d" k$ l) X* S
    12
    1 I9 t- e5 |/ v8 Q13, w3 Z; B( v+ L- i
    14
    9 Z& S! D6 D* [4 l) t+ H15
    1 ~( m& X  {% C- ]168 i- v; T6 M; o
    17
    # a* H& n3 {( N, p. v2 ^! D18
    ) ]4 B: [# B) Q. t19
    # W2 t  \% N- N2 i20' u' W" D( |9 p
    21' b( A% x3 s( D0 _; ~" u
    22
    4 \3 I7 x: T' J7 r8.5 练习% o. m3 x; U7 [% q" l
    Ex1:房屋信息数据集
    9 m! g& e) L% q. B5 J2 C现有一份房屋信息数据集如下:
    8 n4 n7 [+ H8 _9 s$ N3 [4 A" A- o! r' `
    df = pd.read_excel('../data/house_info.xls', usecols=['floor','year','area','price'])4 d1 r9 F, r" W$ C) r- B: i
    df.head(3)
    4 w' M8 W9 C$ ?Out[115]:
    - U" L: o* Q7 ]      floor    year    area price: A. T3 O. D% K) V# z2 X" G
    0   高层(共6层)  1986年建  58.23㎡  155万
    3 w7 |; m4 O. X1  中层(共20层)  2020年建     88㎡  155万
    ) O$ {7 P% g2 n+ K, i+ e- f2  低层(共28层)  2010年建  89.33㎡  365万
    * {+ x$ K7 I5 s' s0 h1, x- n% T5 F1 D* z6 C0 g: p/ |& C
    2. R: s1 A4 J# g4 O! E5 X
    3) g: y# f( o& x4 D0 v6 U+ T
    46 b1 t- ?+ f1 n# w) K
    5# s* ]3 `( v6 o$ ?) R" `
    6& C' {& ?3 ?+ R
    7( J" v) T# W! z- B" |7 z
    将year列改为整数年份存储。+ d+ @: n4 ?. U& |
    将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。
    % \" p9 m+ m. ]& K9 f, A+ b$ u$ Q, G' A计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数
    # G5 n/ f; k! t$ c( B0 {, ?将year列改为整数年份存储。
    , w4 q1 m; {3 i0 X"""
    1 `' l' Q3 a/ o2 _7 {整个序列需要先转成Nullable类型的String类型,取出年份,再将年份转为Int64类型。* V/ ~# ]( Y- z- @7 Q4 u$ z; Q
    注意,转换的类型是Int64不是int,否则报错。即使astype加参数errors='ignore'跳过缺失值,
    ; J! e1 V5 U  y: x- k" a* X' y转成int后,序列还有缺失值所以,还是变成了object。+ U- e9 S; Z) c. w4 K$ z( X7 d. Z
    而整个序列转为Int,就还是Int类型,缺失值变成了 pd.NA 。( J) Q$ Y$ m! X$ y. }7 ?5 e
    """: s7 q( ~1 d$ e. s! V8 X8 _
    df = df.convert_dtypes()3 v. Z; f% Y! Y/ D# B, y
    df['year']=df['year'].str.replace('\D','',regex=True).astype('Int64')
    1 D# f+ l6 M8 c" A3 a4 ^8 Jdf.loc[df.year.notna()]['year'].head()7 u' a+ q7 O) c* B) T# V9 k7 A

    * l  B3 b) F0 f' y# ^0        1986
    % l0 _7 f7 b7 L& R& x1        2020
    ( Z; g, p/ m2 b4 x& p2        2010
    / J( l8 X; Z* |3 [; Z' W3        2014# z8 ~6 B3 x3 U/ c" p: O
    4        2015
    + P8 g- a" k; A; e4 YName: year, Length: 12850, dtype: Int64' y5 p# D) ]+ |! M, k% M/ E

    / ]& @0 N" W/ x16 @2 C" |2 Q7 J9 e/ q) o1 s, s
    2# m7 |" Y% Q. u% \% q) k& K
    3& ?: |+ H- T) W' E; Y
    4# P' ^3 X( p: F( H- l
    5) ^$ G) J  m- c( x! e# k. j/ ^
    6
      [; X! d* c) L1 J7
    6 O8 r/ R/ V; f8 Y& A7 ]0 ~88 K# i4 Q1 g4 @/ f- }9 ^9 X" P) U9 ^5 q
    9
    9 F% k8 s# y4 t, h! N10
    , m% O1 g" I) R) y# p  c: R) m11. O2 B. E4 A" v6 E% g* ]5 V
    12. G, w' M7 J- q! q2 Q5 @7 ^
    13
    & N/ y) ~8 H0 n# N4 g' ^' Y14
    # n- k1 N% x; u! e4 E" X15
    2 d% n- T* ^8 n16) @" r- i" J, q9 K
    参考答案:7 u9 o  S& v+ ^) c$ @
    5 C( Q+ {5 }* |, J/ c% m0 b) d
    不知道为啥pd.to_numeric(df.year.str[:-2],downcast="integer")类型为float32,不应该是整型么
    7 C+ a6 Q( \7 W  ~9 X7 B' i
    ) h6 q+ _" y2 P) F8 n& zdf.year = pd.to_numeric(df.year.str[:-2]).astype('Int64')
    2 |) w  N  t0 O" r* }* }df.loc[df.year.notna()]['year']: P' i/ B  z1 d% `5 w
    1; N4 m2 V& g1 A( T* I$ d& }0 O
    2
    ( C! x1 L1 A- D6 Y# K将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。* g9 E1 X4 f) H$ ]/ C$ k* q! _
    pat = '(?P<Level>\w+层)(?P<Highest>\(\w+层)'
    ! j0 E( |2 j2 h* S: Q, x* @: }df2=df['floor'].str.extract(pat)  # 拆分成两列,第二列还是(共6层得形式,所以还的替换一次1 k. }; l" }1 @& k4 I
    df=pd.concat([df,df2],axis=1).convert_dtypes()  # 新增列拼接在后面,再次转为Nullable类型
    8 g, Q5 u# p' y" z8 [$ e/ b: udf['Highest']=df['Highest'].str.replace('\D+','',regex=True).astype('Int64')              
    6 y+ m$ @5 ^( H: }) ^- `2 Jdf=df[['Level','Highest','year','area','price']]
    , z# K& {  v. o0 a: T: f6 Q& [df.head()
    9 g& ]' v- |( h2 n/ [8 N1 B! c4 a# s1 t+ u, d! B0 {- \" v+ Y4 d
       Level  Highest        year        area        price( y$ x4 x$ \4 Z( Q% [+ \
    0        高层                6                1986        58.23㎡        155万
    " `1 k. y4 l! K* D+ J, u7 S1        中层                20                2020        88㎡        155万
    5 D; J# Q' j" a1 c2 k2        低层                28                2010        89.33㎡        365万# I" j4 J& J5 {  U# o& L
    3        低层                20                2014        82㎡        308万
    + C6 n. u4 l6 r% b, @: p5 I( a7 w4        高层                1                2015        98㎡        117万: o+ \) v+ B+ M% I+ N: c4 Q' K
    1  M  W0 K: d3 O
    2& C" m- @: G: l7 e/ S( X
    3
    8 m' r0 s2 I6 e, g' q8 D4
    + c/ a! q' F# a5 n50 d2 z; X2 m$ e* Q. U
    6
    9 z  z* k2 I) S2 `5 H# }78 b" n; z: a! J. C! q
    8
    9 ^3 W# ^: U( I. D5 n) x* k9
    + i$ ]6 C* u( w& D6 _9 p* y8 Y100 }: a+ B0 F% D7 l4 ]9 O- E4 L" \
    114 e3 p3 f" \2 |; A8 I# }: W- V
    12
    ) B0 u% }" }5 n" m' m13* E- {( p) K. F
    # 参考答案。感觉是第二个字段加了中文的()可以准备匹配出数字,但是不好直接命令子组了- K4 p4 O9 |. A8 S- Q( Y
    pat = '(\w层)(共(\d+)层)'
    : H. G6 s5 Q% M7 T$ bnew_cols = df.floor.str.extract(pat).rename(; L& T& w7 p# m9 l1 C  s- \( e
                        columns={0:'Level', 1:'Highest'})
    $ ]/ L0 q, L/ Q. W! d' O2 o$ @0 {9 G# X8 y( J* u
    df = pd.concat([df.drop(columns=['floor']), new_cols], 1), w6 G3 N- V2 o2 D3 b: W
    df.head(3)5 t- C& h4 _; |+ k, H
    8 l& L5 m: Z! Y1 }  e% E- y
    Out[163]: " v2 w  C2 ]5 Y5 Y( t
       year    area price    Level Highest+ C! I! d! C1 h
    0  1986  58.23㎡  155万    高层       6
    0 d! o6 {* a/ p* U6 B* u, m1  2020     88㎡  155万    中层      204 \  H7 e5 ?" N4 T2 B1 X+ m
    2  2010  89.33㎡  365万    低层      28
    # G5 G$ E  W! v! ~' G0 n2 i4 P8 W  t1: }" h: V' \- m7 ]' n3 T
    26 F8 B, l) U9 B7 U
    3
    5 u6 z' }" b2 k& I$ Y7 |4
    3 H1 P* i7 T* ~/ @5
    5 }" x, Q" e  J; P: S6
    ( r1 E$ ?0 @: z% D7
    / E# V3 s" C5 \6 y2 v9 g7 G8
    / ^1 G/ W0 l3 a9
    * M& r+ J- I" _+ W, w9 @10
    1 }7 P: S5 H2 A3 ^4 c11
    5 B3 p1 B  g! {12
    . f# S$ W& p2 A# G$ S5 r  L# a13
      j* `7 A8 r1 v- E2 }5 N6 m: w) G计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数。
    # O) P: Y; d& E# P, Y: q. @/ U& l"""
    8 i  Z$ [2 N  cstr.findall返回的结果都是列表,只能用apply取值去掉列表形式7 E; p! x4 h! U& R" E
    参考答案用pd.to_numeric(df.area.str[:-1])更简洁
    , z  s8 A2 ?% y9 U由于area和price都没有缺失值,所以可以直接转类型
    9 m2 b9 @: p; z' C1 N8 H"""! K  n4 _% ^5 H4 ?$ [8 ~: ]% H
    df['new_area']=df['area'].str.findall(r'\d+.\d+|\d+').apply(lambda x:float(x[0])), l1 v6 V- M' T8 E- w/ W& ?
    df['new_price']=df['price'].str.replace('\D+','',regex=True).astype('int64')+ ~' S0 f! K' f  o; t
    df.eval('avg_price=10000*new_price/new_area',inplace=True)/ }' ?! ?0 L+ A7 _8 u" \$ e
    # 最后均价这一列小数转整型直接用.astype('int')就行,我还准备.apply(lambda x:int(round(x,0)))
    : d7 e- m0 Y! ]4 `1 O0 g2 c# 最后数字+元/平米写法更简单; q6 D8 L2 P- F3 G+ K+ V6 K
    df['avg_price']=df['avg_price'].astype('int').astype('string')+'元/平米'
    / T& b! S7 [: R' u4 Q0 Odel df['new_area'],df['new_price']9 Y' n# e% W2 w
    df.head()
    - \  G' f1 b  f' z7 N# {: j8 T4 S. q- C/ h
       Level        Highest        year        area        price        avg_price9 c4 p7 Q+ j" Q# T/ x
    0        高层                        6        1986        58.23㎡        155万        26618元/平米
    3 i* K: C: J+ x! w6 D/ {1        中层                        20        2020        88㎡        155万        17613元/平米
    $ j, ~9 i( O# {8 w$ O9 J$ w) V2        低层                        28        2010        89.33㎡        365万        40859元/平米
    - y* }& b, u; O$ p3        低层                        20        2014        82㎡        308万        37560元/平米- v8 J  S1 ?1 O2 k& ]) m6 M" e
    4        高层                        1        2015        98㎡        117万        11938元/平米
    $ [1 g: c8 E- D- T. d# ~0 E
    . ]7 C8 s! O, }9 ?3 j" Z0 G1+ x) a: ?& j' p  z  _1 w( H, S( ~
    2
    + P0 D1 p% D. @3. e1 B0 ?  l7 h
    4, w) T6 M: T) b/ E1 j* \7 w
    51 u$ d/ @; h) ?3 ]' q
    6  ]4 Y3 h2 `( G( o
    7
    6 a( e: O. ?8 }; f, _8 Z# r8
    0 F% S& o: F  t! X1 _9( B# I) l2 Z0 d  I( A5 ?( `& V+ l
    107 c; p. E6 ^7 [/ ^4 i: P" ]
    11& A! B! S" Y9 V; H
    12
    . ^) B9 |9 A: v" @" g3 b, s13$ L* B" D: @5 \1 s  J. J
    14
    8 e( n8 \) n+ e15
    4 [2 N: z, E! V162 D. q/ @3 M, f2 t# E
    17
    * V7 j) _( ~1 \18( A7 }3 K4 s0 @3 X: V
    19. G- _  s! I  [( _% ^
    207 a8 P2 w  [' q4 r4 m# U
    # 参考答案
    * F3 w3 v4 E0 {+ y/ ]( Bs_area = pd.to_numeric(df.area.str[:-1])
    * ?* n8 ?9 W+ n+ S) t/ o7 U* `0 bs_price = pd.to_numeric(df.price.str[:-1])
    + {( Z2 g) B6 j" edf['avg_price'] = ((s_price/s_area)*10000).astype(
    . m6 `3 Q4 m( i, n* V* S/ y                    'int').astype('string') + '元/平米'
    * Q1 E) Z7 t$ N/ ?/ M/ i
    . i1 C4 [# x; D8 ?df.head(3)
    5 N+ p9 E, p9 }$ G3 hOut[167]: 5 X' i  a$ x6 Q$ _# @
       year    area   price   Level Highest  avg_price: j8 M8 g/ Q. i' h
    0  1986  58.23㎡  155万    高层     6          26618元/平米) P; i+ E7 K. [+ ?, B
    1  2020     88㎡  155万    中层     20          17613元/平米
    ' ]0 y7 g: M$ Z3 ]+ G$ ^" E2  2010  89.33㎡  365万    低层     28          40859元/平米
      z7 h  ?' p2 x$ @1
    3 |4 C4 ^( W( v) k$ z8 _6 `4 b2: H6 S8 d9 ~: t  r
    3, R' i. S- }' z8 A
    4. Y- `  @- ?8 D2 @( M
    5
    8 h( W9 Y# b* C7 X; G. h$ h, C) ?6( P! j& t0 |$ C- P
    7
    # ~; M& D. ?( y" y9 ?0 A/ c8
      [' x3 }$ X( k& @, W/ Z6 u99 W, t  ]9 X" m. S' r
    106 V; i3 n8 c4 \/ t8 D; o
    115 Q& G# s8 E) p/ ?
    12
    1 _3 L# ]; c- V. }2 mEx2:《权力的游戏》剧本数据集: S4 {3 w% i9 K# h
    现有一份权力的游戏剧本数据集如下:
    1 X5 L5 A1 f. K# I4 H! I0 I
    & b$ {6 b$ j# w# G$ K0 qdf = pd.read_csv('../data/script.csv')
    ( P& E* N9 h+ `2 Mdf.head(3)
    1 j- {7 p% T% T7 s. S) q, Z- ?
    ! r% n7 V, M8 F- L* {$ M7 eOut[115]: $ k0 y" f$ K. N( K. @
    Out[117]: % `( W4 D1 R5 m* a, ]+ |* [
      Release Date    Season   Episode      Episode Title          Name                                           Sentence8 w8 G' `  Z# }# b
    0   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce  What do you expect? They're savages. One lot s...
    3 @0 g# n! s1 r6 ~6 \1   2011-04-17  Season 1  Episode 1  Winter is Coming          will  I've never seen wildlings do a thing like this...
    " {4 i( j( [4 X7 n+ }! a2   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce
    + D9 \* L4 c/ B2 X1
    ) D& Z1 a9 K5 \; `2
    7 ]8 {. T* x6 O  |3
    ; h: b( S0 v: q! y8 u8 W4) t5 N( s6 h1 d- z
    5
    , N( f1 v5 Q! o! f" N8 M2 |" {( C65 z1 t8 z! s! f. Z
    75 T( Y% T5 T9 f) J* ?+ f
    8
    + ~8 ^1 y5 T6 K9
    " c+ @, U( g$ z1 t4 g- \计算每一个Episode的台词条数。- d$ N! ^; n! v$ U, o/ e
    以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
    8 ]% t3 y- m; B4 x( J$ B5 e若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有 &#119899; 个问号,则认为回答者回答了 &#119899; 个问题,请求出回答最多问题的前五个人。
    3 n+ I/ s: M' ~, O计算每一个Episode的台词条数。/ T1 v  s) d2 S$ L7 r. [
    df.columns =df.columns.str.strip() #  列名中有空格! ]2 _5 B8 ^  I
    df.groupby(['Season','Episode'])['Sentence'].count().sort_values(ascending=False).head()
    3 Z0 ^. b4 h+ ^1 B4 h
    / |0 Y% |5 f: ~  |( t; H% @season    Episode  
    8 A+ A& N4 p; S- j5 _+ A9 ]Season 7  Episode 5    505
    % X+ W( b3 n7 Q, n9 d8 FSeason 3  Episode 2    4800 v$ I( @* q" p
    Season 4  Episode 1    475. h, k$ X: j4 X0 b5 N6 u
    Season 3  Episode 5    440- @1 ?+ p$ B1 t4 W, n; {
    Season 2  Episode 2    432
    % Z5 N0 N' o* ^% Z; f1: t" |" [7 W: V! j% C. L7 [
    22 ^7 Q* d8 B& U0 ], {
    3
    # y1 O  S# e6 h, E) r5 g# J9 h4
    : c! K0 q, h. m2 |/ q5 U5
    , i8 K  N$ t! t1 x7 a$ {! I0 u6$ h) M' F  t% r% H) E8 f/ d1 F7 u
    73 P' v9 O, m! {
    8
    & Y  z+ ~6 v, X" t9
    6 d3 u( |, W& q5 F! \: }$ b3 s: @9 w以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
    $ I& ^5 Y2 j1 I- m& R0 v" [, `# str.count是可以计算每个字符串被正则匹配了多少次,+1就是单词数
    % r0 n5 K' L" v, d( l9 \) N1 a% x. e$ hdf['len_words']=df['Sentence'].str.count(r' ')+1. f: H+ I8 y: G. `3 }" z, D
    df.groupby(['Name'])['len_words'].mean().sort_values(ascending=False).head()
    ! T" G+ J% h; N
    + Q2 w+ @5 f7 t0 VName
    # [. p% m2 ^) {' d& f# l  L! l% cmale singer          109.000000, T2 C7 E% ]- {9 K4 }
    slave owner           77.000000
    * K- \- |" j+ R: tmanderly              62.000000- s" b% s, f7 r  U: w/ y! t! n  R
    lollys stokeworth     62.000000
    , a5 }2 M0 T  e2 [- b# Edothraki matron       56.6666670 U$ e8 I7 |# H
    Name: len_words, dtype: float64
    / K: T, }& i& ?# m! V) ~8 f1
    / Q+ J. ^- K# B1 w9 J2
    8 ?- `+ X& l" _) X3
    ( o7 M/ G0 ?$ k6 Y6 K% n4& p' j; t/ _0 `! Y6 P3 F
    5
    ! y' G; N* s1 |# p8 [2 e6) V4 S. q( [" r
    7
    4 t* G: j# x& Y! d* z/ O2 W8
    ! S* T) v& I+ T, T5 I/ w9: F/ _! ~& C/ F& h
    10) c, f" g! y. b0 e+ ^
    112 k. v) w' I5 q  E2 }
    若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有n nn个问号,则认为回答者回答了n nn个问题,请求出回答最多问题的前五个人。& u( ?8 [& M4 |) ^$ V
    df['Sentence'].str.count(r'\?') #  计算每人提问数
    & [. s% L* \4 Q* F6 g" {ls=pd.concat([pd.Series(0),ls]).reset_index(drop=True)# 首行填0
    ' M) t7 {3 [" t* Adel ls[23911] # 末行删去
    % }$ q0 B# O3 y! X4 R4 xdf['len_questions']=ls
    ( z: l3 p! G1 Y# rdf.groupby(['Name'])['len_questions'].sum().sort_values(ascending=False).head()
    ' Y8 }& g$ v: ^
    ) J, g; w4 P$ }Name" C" U; \4 g, P" q$ ?7 a
    tyrion lannister    527
    : F0 ~' u+ D6 S! A1 L- x) V$ bjon snow            374  c  A& D) Q' B" F8 @' ?
    jaime lannister     2838 {$ M( l" A+ i5 L2 V1 ^
    arya stark          265
    4 C" J/ C, O. d7 rcersei lannister    246
    + r! g, g. J$ I8 S# ~. VName: len_questions, dtype: int64
    1 W' W& I  I! T. l+ g6 S; L3 n! ?+ ?( ^
    # 参考答案
    ! m/ l/ \4 k5 A/ d! U8 O2 j# S5 F/ Cs = pd.Series(df.Sentence.values, index=df.Name.shift(-1))
      _0 Z& r  ^( @; K) k6 js.str.count('\?').groupby('Name').sum().sort_values(ascending=False).head()
    * ^8 u; ]  R/ x" d) c
    ; W: ~8 Q8 F7 N" P1" k7 L: b! X$ _0 V, Z
    2' m  Q5 U" u/ Z
    3
    % p( M) }5 Y* B9 {# O: ?4
    / H& Q- Z  M" @4 @* h5
    . E  g9 b2 ?  e0 Q" \( A, p6
    0 v; B5 v' Q# R- R! [* H/ y75 y; W6 N5 K& l
    8. S; R/ A% ~( U( Y8 [0 R* c& @
    9
    ) y7 A  t! C4 D4 C10
    & z( H8 T; `! Z  ~9 }11
    7 ?) l; C" `! B% `" g9 n12
    . e* R; ?  `8 h13
    5 D1 }/ V5 n1 O: n% j$ p1 c' R14
    % z3 D0 s0 }+ s1 u8 r( b159 r6 C3 b. m7 y( n
    16" M  O$ h" n0 H! L+ Z- |% u. Y" v
    17
    0 u: Q- v& X. o; J$ W第九章 分类数据$ W1 [9 D# x9 T. j6 g
    import numpy as np6 t! g  D% G% ~& _7 ~; {8 R% I
    import pandas as pd7 V7 o8 L# Z8 _, J
    15 ^9 F+ F) X  w2 m
    2
    1 m4 [" D$ t1 v, f9.1 cat对象
    % a3 c6 }( R3 a& \1 G6 j8 i9.1.1 cat对象的属性2 z5 {' {7 t% G% `0 o- |
      在pandas中提供了category类型,使用户能够处理分类类型的变量,将一个普通序列转换成分类变量可以使用astype方法。7 i& @( ^) _0 D& u( f8 S2 M& V
    # j( c( M# L* R% T2 d
    df = pd.read_csv('data/learn_pandas.csv',
    6 p. H! b! [. N$ ?+ L" A     usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight'])
    ( z2 z8 f2 b$ H. c' R' Vs = df.Grade.astype('category')5 E8 \: O+ j8 d2 ]0 z+ \
      w6 d2 B. [7 }+ I/ Y
    s.head()
    7 E: Z$ D$ s  hOut[5]: ! ?+ |* B  t# x7 i
    0     Freshman
    5 U* D4 b; f  V7 n) t1     Freshman( `4 t6 X4 d1 g3 o
    2       Senior( F: s; z. H9 _8 t  c2 w
    3    Sophomore% Y3 b. `+ t: |& l, R2 F' A0 k
    4    Sophomore; c6 m' k. g9 F+ n; D
    Name: Grade, dtype: category
    * r& Z  @! |- T% o6 dCategories (4, object): ['Freshman', 'Junior', 'Senior', 'Sophomore']
    , D+ N' Z7 h/ Y2 O! B# U8 ~  q17 p5 K8 c# W) J5 f# L( \, ?+ J
    2  h; l0 Y# R+ r' f
    3) {- z( ]) ~& I7 U) ~, g$ e" r
    4
    $ z$ D' q' R1 P1 a0 T5/ A' Q8 D- t% n- e
    6
    $ o8 {2 Z9 u$ w3 O/ N6 X+ ^7
    / T/ [# k: @0 C8 Q7 y- E8
    8 r( L7 b: O3 ^+ U' `, r/ G9( y7 Z. e! k' \
    10+ h- V8 c4 p8 W% ?
    11
    0 A+ I! h5 G( ^6 C% x5 k12
    : L/ G& W1 W& h1 R! j13
      S) F# q& X1 N! Y! I- [  在一个分类类型的Series中定义了cat对象,它和上一章中介绍的str对象类似,定义了一些属性和方法来进行分类类别的操作。3 M+ b2 q4 ^+ n+ h* k

    , J; H, Z7 y! K5 _s.cat
    9 u) \2 Y% `7 a. J2 YOut[6]: <pandas.core.arrays.categorical.CategoricalAccessor object at 0x000002B7974C20A0>; F% n& `" L( u; ^; R
    1
    " G( g! L& I5 p- Y* D2, r) ?5 g2 I( v5 _$ k3 U- |
    cat的属性:9 R4 n$ _% n: W

    9 Y: ^# w" c+ B( Z+ Icat.categories:查看类别的本身,它以Index类型存储5 W  S9 q( \  p, d
    cat.ordered:类别是否有序
    : B$ l( ?; G% O. ]( Tcat.codes:访问类别编号。每一个序列的类别会被赋予唯一的整数编号,它们的编号取决于cat.categories中的顺序
    ) h4 z( S$ Y5 ~( n) ^1 M2 X9 w; Zs.cat.categories
    . x; C! D: O; ]+ qOut[7]: Index(['Freshman', 'Junior', 'Senior', 'Sophomore'], dtype='object')
    - p5 D: ?. j! g( b- y3 a
    4 i4 [$ y( |  }0 s+ Bs.cat.ordered9 D% t8 t; H( l, j, h
    Out[8]: False
    3 `9 U7 o7 \6 I
    ' F, Z$ {1 N0 r6 x( @4 Zs.cat.codes.head()
      A& f0 @* ?( I  G. u. I1 k: wOut[9]: ; t; M2 |4 E( A# K; W
    0    0
    - V: m1 F8 Y  B( T0 T7 I# Y: Z* Y1    0* G/ G7 w* V7 o1 P' ]/ H
    2    2
    / s9 E. z  T  @  Z1 {3    3; x5 K2 S* y% q3 K  u; }8 s! I3 A
    4    3$ F9 l5 t% v  W3 N$ ?
    dtype: int8
    , I- O* D* U! @- |  t4 ?/ }2 q1 l" \1
    8 n" a2 N. ]0 b9 z2 i" s24 i/ d8 v- W% Q5 P& ?
    3
    & T+ b% A* x6 S* l3 _# q4! v. x; j* g0 N" R* H3 A
    5- L3 @0 H( b' x5 k/ {
    68 C& q* e* Y+ Y% W  N1 c% Z0 ?
    7" B9 g3 ~2 Y( S8 E5 i# C6 |. J$ |
    8
    5 l0 ~2 m8 j' `% L+ L& f1 G; f9
    ; O' ]* X  V# p# E# d10
    $ W. {4 P. G- ^' g6 v$ ~6 _- M11
    * t+ I6 I2 S6 F+ k5 [12
    . @+ ?* d: ^- D6 D4 \13
    8 |; o! l9 ?5 b" s14
    1 q! N% D2 B9 x9.1.2 类别的增加、删除和修改+ o' E5 {* g6 k4 q$ y9 d
      通过cat对象的categories属性能够完成对类别的查询,那么应该如何进行“增改查删”的其他三个操作呢?
    * ?2 C* d2 Q, t. d$ S3 s+ _) W' n( `9 ]3 V3 i; g( K3 w
    【NOTE】类别不得直接修改3 j0 Y' f% j# j* M( v1 ]
    在第三章中曾提到,索引 Index 类型是无法用 index_obj[0] = item 来修改的,而 categories 被存储在 Index 中,因此 pandas 在 cat 属性上定义了若干方法来达到相同的目的。
    4 g0 Y* Z% I8 @/ j  r9 U9 s( E# z& Q
    add_categories:增加类别
    & G, i8 h1 g: ]1 ~4 R$ qs = s.cat.add_categories('Graduate') # 增加一个毕业生类别
    % a  V# Q; J! D4 B  c0 t& es.cat.categories
    ( i* e% B( e6 s
    9 N- t1 o( o- e* u9 G* N- t" yIndex(['Freshman', 'Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
    4 S8 ]5 c, P# q" M, l8 L( Q( |+ Q$ K1
    # @. F6 D4 M. F2
      h# M& ^0 _8 A3$ E2 B0 c. ~9 A+ i# K& j0 f
    43 o5 h' z# F5 u+ J, q( {
    remove_categories:删除类别。同时所有原来序列中的该类会被设置为缺失。
    * n2 u7 z' b# S6 P2 I, `s = s.cat.remove_categories('Freshman')
    ) x" z+ ~0 X- p1 q( e
    8 E  K! `" Z* h# B) w4 @s.cat.categories
    . Y6 |6 u# h& D" o8 [# yOut[13]: Index(['Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')! s& V" k5 i2 V% r* t! `) ]- s3 d; `

    & \6 D: I3 y0 S. O' r2 rs.head()3 K7 x# }! M8 t0 f" z
    Out[14]: 1 V9 c+ J6 B% g- s2 G
    0          NaN
    - F( s3 e4 {+ P$ H6 c6 c. G8 t1          NaN
    " Q' W( @8 O, P# l- A9 R2       Senior
    ( D6 x  m; Z. T! ]1 G; M7 Z* ~3    Sophomore9 F: E8 j2 Q( y0 W& N2 T) ?
    4    Sophomore
    + q- t7 d) d3 Q, @  f; aName: Grade, dtype: category: ]; M! T; X! K  b
    Categories (4, object): ['Junior', 'Senior', 'Sophomore', 'Graduate']
    & w5 X9 h4 P7 Y" G12 o& q3 r; W+ \( L" G# s/ j
    2
    : F4 K! ?' M2 d$ \3 }- D/ A# H. Q! B: c- M3* `' k7 O! h, v2 U- y2 y# y% A9 S
    4
    . h' ]. X8 k# g+ v5! n, y1 A& B$ t7 ]  Y
    6" q0 |) k0 Z" D% q% m
    7' Z8 j# @7 L3 }
    8
    ! S! g% I- }: H7 q) Q) b0 x9
    ) [# x2 p( e$ x7 Y2 k0 |10
    ' E, P- ?* E) N# W/ k. I11
    ' y( O' w5 M3 k; Y2 }12
    ! a5 k; e4 s9 I+ ?" r& p& @135 I+ X5 N0 {' ^" g
    14
    ; w/ b5 t6 V. U4 ~set_categories:直接设置序列的新类别,原来的类别中如果存在元素不属于新类别,那么会被设置为缺失。相当于索引重设。
    + T! j) C% C! |. ns = s.cat.set_categories(['Sophomore','PhD']) # 新类别为大二学生和博士
    8 S2 p& Y6 d3 O3 }9 ns.cat.categories: e2 e2 z& Y9 `! R
    Out[16]: Index(['Sophomore', 'PhD'], dtype='object')
    5 C- _& M( G2 d6 s2 r3 t
    7 ]9 K9 Y' C* \: X- k5 d8 fs.head()
    ( }' f* q1 |4 J" g8 r" uOut[17]: # w. G4 e) S! K& i5 g! j0 P
    0          NaN" K4 R$ g  S3 b5 O
    1          NaN; v$ o0 R/ K8 h
    2          NaN* A* t  Z8 D( B7 D1 x7 U
    3    Sophomore
    0 p; y" z# W% T8 ~4    Sophomore
    # p% W& n6 J2 u- t7 ^1 rName: Grade, dtype: category
    & H$ f0 U/ \" [( G# @: J4 XCategories (2, object): ['Sophomore', 'PhD']
    4 B# P7 N% I& s. E$ o1
    ! x3 s) f/ K0 m% H2
    2 O1 h' |3 f$ g) A3* e7 Z7 v2 B& s1 p0 {' s
    4
    9 W0 O  O3 ^4 {$ j6 R5& a# }2 i' p: p4 L% O
    6; _* L( D$ [# ^/ L. }
    7
    # f7 q- j: X9 g; d- `& k/ \8: B% o& ^* [9 c0 z8 @5 |6 d/ [* k
    9
    5 @; ]6 L1 d$ N3 P109 P% ]( P9 o# G6 u
    11
    , r+ V9 H" S9 A) I4 x. b% s# ]122 R7 r- j6 b8 g. P! ]
    13- W/ b. O* T; a$ Q
    remove_unused_categories:删除未出现在序列中的类别
    ( U1 U9 Z2 J: v7 r2 e& D5 js = s.cat.remove_unused_categories() # 移除了未出现的博士生类别% X0 s/ c1 m8 p! n
    s.cat.categories' ~% x4 O  @0 G/ Y

    % @/ S* D9 q/ u, xIndex(['Sophomore'], dtype='object')' ~: f" {  z) F' y( G. x
    1
    $ ~/ G/ C/ s& T; b2' S% q" `  x, ^1 I, q, m- g
    3
    4 X4 {0 U4 ~8 k. X: \. f46 G7 k+ ]  H6 s& W# ~) Y
    rename_categories:修改序列的类别。注意,这个方法会对原序列的对应值也进行相应修改。例如,现在把Sophomore改成中文的本科二年级学生:' a+ A0 f: x! j( c
    s = s.cat.rename_categories({'Sophomore':'本科二年级学生'})' w7 ?& m9 [- X* Y6 [7 R
    s.head()3 H7 {4 F# l+ c( q

    % z7 z9 _' O6 Q5 ~& ]( j. E0        NaN
    : y& }8 n3 M9 _$ ~4 I( V1        NaN
    * H" v  d: l! c2        NaN1 ]* T, ]) s; _" w
    3    本科二年级学生+ H) I" W5 d/ t  ~% J
    4    本科二年级学生
    * u+ x8 j1 Q1 |6 E: R8 KName: Grade, dtype: category% w. ?; z9 i4 `8 M. E/ ]
    Categories (1, object): ['本科二年级学生']
    3 P$ s# K' y, |0 g) S8 B1
    3 {8 u7 D/ v2 ]# r( e$ D25 p% t; H4 L/ g) W) x% J& y( r
    3% b( ]& ?: E9 J* z% o0 S
    4
    $ |/ \/ {( V8 i: W- p% D53 L+ W: K3 ~! ?
    6
    + O1 q" G5 o! O7; N. C" E( c& i* T
    8
    % d5 c% |' f, i4 q  J) o: c90 G5 z* F7 l" V. h' {
    109 m0 f+ }7 s6 e- |3 D
    9.2 有序分类
    & z! p" b. C, h9.2.1 序的建立3 _7 [" z, V9 ]: i
      有序类别和无序类别可以通过as_unordered和reorder_categories互相转化。reorder_categories传入的参数必须是由当前序列的无序类别构成的列表,不能够新增或减少原先的类别,且必须指定参数ordered=True,否则方法无效。例如,对年级高低进行相对大小的类别划分,然后再恢复无序状态:
    3 T4 d# X" N5 L6 u% i  t" J- y( p/ q- ?* S/ z& |- M
    s = df.Grade.astype('category')* ?" x; Y$ R0 S  k5 v( U/ M9 q
    s = s.cat.reorder_categories(['Freshman', 'Sophomore',9 |8 J6 e1 {3 P$ X. J2 Q( I
                                  'Junior', 'Senior'],ordered=True)' v6 M& Q6 ]- _8 ?% c7 S
    s.head()6 ]8 k5 p, i* x9 S* W. e, A% G5 G; F
    Out[24]:   G% W+ d* Y! X. k/ Y
    0     Freshman7 N5 c- m. u* D" f( d, u
    1     Freshman9 w- d- j8 S9 E( C# x: G* x" n
    2       Senior
    ( M8 \2 V0 i' h! ~3    Sophomore
    , T1 @' _+ v% \! Q& \4    Sophomore( u% L) q' e6 ~0 ^  P# p$ ?5 `
    Name: Grade, dtype: category1 w& o* F% y9 {# O; Z% [! A
    Categories (4, object): ['Freshman' < 'Sophomore' < 'Junior' < 'Senior']  g: ^9 [6 W0 ~- y' w4 V) G# Z
    4 T! I2 |/ w) e' `
    s.cat.as_unordered().head()
    + i8 J1 ^* H; u, [Out[25]:
    * h* I2 R! a  \9 @/ w* f. Z6 G0     Freshman! ?1 l5 v- r4 k
    1     Freshman
    5 ~1 T/ v# t" w6 I9 K2       Senior8 E% S3 Z6 Z# q( K3 W
    3    Sophomore
    . o! n7 ]: ?# h8 B5 r+ V+ q4    Sophomore8 u6 A/ Z  V& S5 x! l" F. ^8 O
    Name: Grade, dtype: category
    7 ]1 T+ B$ J& ^: p: qCategories (4, object): ['Freshman', 'Sophomore', 'Junior', 'Senior']4 D' W6 A8 c) o7 p) }. z

    ( x( s5 ~% f, p( T1$ O) X9 w2 n0 S/ }
    2
    9 N' J/ o0 L2 K+ x, B. c3- T: p2 U4 X) w3 T! y0 l3 D
    4
    6 u. V: X9 l7 c" l' n5
    1 n! u4 E7 w' n$ K6) N. P. b: H/ T! {
    7
    ! i, j1 J2 P  N4 b/ H2 g8/ l% U8 N  m( c. h& N" u
    9
    3 b" M. K, m( e/ t10' U7 ~( V* J% E9 G- H) V0 S# G
    11
    : ~; D5 g3 B0 l$ {12& X6 o2 M0 z: K2 u0 y. r
    13
    7 d! W. u% R6 T5 O5 f  M# E3 W14
    $ F' U# |4 Y. i- P1 g15: `$ p! Z3 ^% c7 P# O
    16
    , f6 b# [7 H4 V7 u# n( z17
    + [* x0 `# y; i, c1 F* ?& Q: F: g18
    7 c+ c0 f3 V5 i0 W# n; H( A196 q( [. x6 @/ ?
    20
    " ?% q, e4 o. R1 y/ p# a; d( K219 O' ~% N' S( h5 `/ M, x" m/ g
    22
    & _- ?/ U3 N! {+ H4 W  O  如果不想指定ordered=True参数,那么可以先用s.cat.as_ordered()转化为有序类别,再利用reorder_categories进行具体的相对大小调整。: m7 O2 t2 K* m* i$ z; O- G8 f

    5 k+ |% l- f7 J5 ]- \- w, b9.2.2 排序和比较
    0 c! F3 ?' ^/ c; Z在第二章中,曾提到了字符串和数值类型序列的排序。前者按照字母顺序排序,后者按照数值大小排序。
    ; P' _, M& c7 e: V, L
    4 r; ?1 o7 d; Y* Q8 P( G  分类变量排序,只需把列的类型修改为category后,再赋予相应的大小关系,就能正常地使用sort_index和sort_values。例如,对年级进行排序:
    6 I9 i7 i2 f/ p1 x. F% J% w( h; q  ?' J- h3 ?, t
    df.Grade = df.Grade.astype('category')
    ) ~& k" p! C9 u" udf.Grade = df.Grade.cat.reorder_categories(['Freshman', 'Sophomore', 'Junior', 'Senior'],ordered=True)
    / v( G! ^4 i: b& U+ kdf.sort_values('Grade').head() # 值排序
    ) F6 X  h1 Q+ iOut[28]:
    ' C5 I3 u5 X/ J! V; y2 T        Grade           Name  Gender  Height  Weight
    5 V: ^' s( K# n* ~4 O4 B0    Freshman   Gaopeng Yang  Female   158.9    46.0
    ) ]& c' z( {) L105  Freshman      Qiang Shi  Female   164.5    52.08 f7 h8 n7 T+ \) F$ I2 z- j
    96   Freshman  Changmei Feng  Female   163.8    56.0
    7 r% m2 i" X. L: i88   Freshman   Xiaopeng Han  Female   164.1    53.0
    7 e* B' N0 o! W0 j1 u3 g81   Freshman    Yanli Zhang  Female   165.1    52.0
    9 N$ e4 }! k, t  g3 y- V$ i8 ]2 p; A0 b4 r
    df.set_index('Grade').sort_index().head() # 索引排序9 a# ^6 s: I' _0 m9 u
    Out[29]:
    % E1 X1 `4 \6 W1 n. g                   Name  Gender  Height  Weight2 w+ c& L- O( c6 X+ E8 I; b
    Grade                                          
    , _$ X! F0 q1 eFreshman   Gaopeng Yang  Female   158.9    46.0
    , l, {7 q" V( F/ qFreshman      Qiang Shi  Female   164.5    52.0
    & c* W  g/ P' ~" _& J+ e6 AFreshman  Changmei Feng  Female   163.8    56.07 t5 L: h8 d8 I9 z
    Freshman   Xiaopeng Han  Female   164.1    53.01 A9 F1 c) a+ C5 Z7 h* }
    Freshman    Yanli Zhang  Female   165.1    52.0
    , P7 ?* S; Z! S. u( I2 i& ?) J! ]" w8 l3 @" ^5 J
    1
    $ S1 T+ L1 T4 M' \3 q9 {2+ @: d, O; x2 V) F% T' l
    3  i% @7 W& U3 \
    4
    0 y5 N" X" |) S4 B. V: J+ l# d5
    9 d& G2 W  N$ J# M- a& U! y' ^$ w6
    ; p6 J+ G8 n# d2 o  }& x7- S' |# Y$ C: D1 t, Z6 r7 R
    8
    8 L& w( a1 S3 F  w- ~4 l9
    9 I: F) Q; h- p2 |6 U8 S10
    ) ]1 S& J! u) v11
    3 W3 ^0 K0 w' b  F12
    , b7 m: i( a: A5 G& F4 n# n& U7 V, K133 q' R# {0 a7 B& m' F. O& S( B
    14
    . U* z; s' j5 ]% d- Z; A156 o+ l$ d1 _) ]
    16
    ' X# Y" W3 U0 L( @177 h9 e2 v: @5 p& }5 l4 W
    18& R2 V. S% h4 S; p, E. O! F! Z9 P
    19
      ]5 m/ y8 [) _6 G: _20: ]0 ~4 Y' X. G/ T2 _! z
      由于序的建立,因此就可以进行比较操作,方便后续索引操作。分类变量的比较操作分为两类:
    9 O7 p5 z# T+ I4 D' i' X4 |9 Z; h# U: N* W+ H" e2 `# C1 G$ _7 I
    ==或!=关系的比较,比较的对象可以是标量或者同长度的Series(或list)。(无序时也可以比较)% {7 R* x, w3 K* J* K9 @5 }" u: f
    >,>=,<,<=四类大小关系的比较,比较的对象和第一种类似,但是所有参与比较的元素必须属于原序列的categories,同时要和原序列具有相同的索引。7 I7 w4 ^, v0 S/ @$ g  O
    res1 = df.Grade == 'Sophomore'  n! y5 m+ Y0 o
    * [1 ?6 o% j/ x; r# x8 N) f- i
    res1.head()" |* c3 s, ]0 j
    Out[31]:
    . F7 e3 b8 b4 r# j, }0    False0 S9 o9 d+ u9 @) [4 V
    1    False# E4 K4 |$ j3 k9 @2 X! B$ [
    2    False0 R" }; B; o1 Y- P' M3 X9 ^
    3     True
    9 F& u7 t  j# P# [: }4     True: q5 F" i- U1 H
    Name: Grade, dtype: bool  A* R3 z2 a  c4 T( z. R
    7 D$ O( Z1 @* ^* Q8 Y; B" y5 e) U  Z
    res2 = df.Grade == ['PhD']*df.shape[0], H/ G! o7 O4 }

    : w; B( ~. U( A, eres2.head()
    : F; }- q) P7 B' BOut[33]:
    + L! ^. X! H" a3 u; `6 P0    False( N# L2 }$ `/ x/ y2 ]
    1    False
    ' R( O8 ~: U! Y1 J) n2    False; \( e4 [" T& \" D
    3    False6 P" s- x: e0 o, T2 \; K
    4    False
    8 A: D7 `9 |5 {2 L  n: D5 |Name: Grade, dtype: bool: {7 {; u1 N) I( r" s' \. x5 O

    : s! v# }7 j. d( z3 [# cres3 = df.Grade <= 'Sophomore'0 ]3 i& }+ X3 E+ y2 k: t
    , w7 b7 z& _7 i& B
    res3.head()
    1 M' u4 Y3 r' U0 r# R* n! POut[35]: - v. ~' Z( t/ d8 ~' I
    0     True
    * z2 v3 N' Z$ ]% `! P0 Y2 J3 b1     True: |' r  x8 A5 p
    2    False# j2 O9 f5 r2 S0 m* }
    3     True
    3 z' K1 F' `  f7 D* L% S4     True2 o( s  ]) j  Y% C" _" t- e' a
    Name: Grade, dtype: bool+ p' l' l- w+ S! l

    8 U# R- u) J0 o) ]+ J# sample(frac=1)表示将序列随机打乱。打乱之后索引也是乱序的,直接比较会出错,必须重置索引。  d' @6 ?0 b- G2 X
    res4 = df.Grade <= df.Grade.sample(frac=1).reset_index(drop=True) . C, m* c- L& E7 L; b

    ( D; b9 [, O* D% w! \res4.head()! I5 x; J3 w7 f: L+ T6 x
    Out[37]:
    * z. U! }) O2 ^& b5 c! b+ j0     True0 l; \% n/ {* S9 \2 a4 O( Z
    1     True" j& Z$ {. ~3 g
    2    False
    # k: V9 }, \. b" j/ A+ r3     True
    . r6 A+ C4 ^7 `/ g0 Z( }( ?0 T4     True# j. w0 L. V4 t% N
    Name: Grade, dtype: bool* x7 f+ `. [  m: c
    " o* y7 a! o, U7 g; y% B
    18 C) ~& N0 Z# @6 ~2 E, N
    2- Y& j) m5 {/ i
    3
    - d* ^7 @+ ]4 l  T5 X8 ~; M47 I" w0 ^7 r' k) M( U
    5
    ; q$ t5 O- y3 r0 ^1 O) W6
    4 g/ Q7 \) I7 |& ?# Z- N; f7
    ( u  Z5 e6 R$ ?8
    6 u) q" f3 r/ a5 C, ^9
    ' |7 d2 V& f  I4 G- A5 O( e5 V1 J7 H10
      d) l; h0 N" O* d' z) ~114 |( H: h9 D3 X' _! d
    122 Q; _" k! H1 F3 Y
    13
    " R8 X6 ?4 `7 k5 \: k# z14
    / ?. p: g- m! K1 q; X9 t  h15
    - }! [5 I! U  p- r167 M0 x. m# g8 r8 m! O& n, @- J6 A  L  W
    174 {2 \! a# G! G: V! f" `& t' b6 T
    18: j: i  @8 \( m" y
    19
    3 z( Q, G. V! w5 A7 d4 E  j20/ W" e! {7 s7 k+ J  z
    21/ `# v$ S( D+ `  s! m+ K
    22
    - s1 m2 w* U# p  \8 b6 j3 H. y23
    . [& i" _* [# r) D& z24& s% i8 S/ J$ t/ {% F; r/ k
    25* T2 S% I2 b" l/ S+ d" K( i
    26
    ; c9 W; |: S- U! M* f27& l+ W7 p7 q+ s6 V
    28
    . W* O6 _( N4 Z- X# K) ]: j) M1 N29
    ( C# p- G& z& h( f30( E; H& x3 N& Q+ n8 n# ^
    31& t- d6 e* N* W
    32
    4 k& C* s, n9 c& G+ W5 {33$ r# R# W! \* t) \2 ?$ p
    34: E' o  J' }& n. x2 I; U- n
    35
    / L2 n% B3 x8 q  @. L1 o: f36" T2 H- t$ k" R* F- @0 V
    37: ]1 V: u3 {8 X
    38; m7 I. M# ~) {4 H2 ]5 e7 _
    39: E  Q$ I5 {2 E1 `# u
    40
    # h2 r" Z8 Z/ d) L) l41
    ) O8 `2 a' D: _4 ]* |* i42
    ( h' N; X( a0 f3 Z- ]; h  V43' P2 q* h" |; _
    44
    ' G' b8 @$ n9 e% g' Y# j/ ?9.3 区间类别' ?2 p5 w; @' p' M$ p) d
    9.3.1 利用cut和qcut进行区间构造
    7 T1 L- [5 U. B2 v9 l- |  区间是一种特殊的类别,在实际数据分析中,区间序列往往是通过cut和qcut方法进行构造的,这两个函数能够把原序列的数值特征进行装箱,即用区间位置来代替原来的具体数值。- R/ {8 @4 l1 b: ]$ ~/ i& E0 S

    ) C0 ^8 q; q; P9 h8 R2 Zcut函数常用参数有:
    + S4 d! c. l7 J  Y4 Z6 gbins:最重要的参数。
    7 g5 E/ H- v6 y7 a/ s' N0 ?& L如果传入整数n,则表示把整个传入数组按照最大和最小值等间距地分为n段。默认right=True,即区间是左开右闭,需要在调整时把最小值包含进去。(在pandas中的解决方案是在值最小的区间左端点再减去0.001*(max-min)。)
    2 ^. G: h( B9 P+ R也可以传入列表,表示按指定区间分割点分割。
    : b" v4 x4 S# }. R  如果对序列[1,2]划分为2个箱子时,第一个箱子的范围(0.999,1.5],第二个箱子的范围是(1.5,2]。
    - U. G4 q# C2 A' d8 v7 w' [; W9 R1 B  如果需要指定区间为左闭右开,需要把right参数设置为False,相应的区间调整方法是在值最大的区间右端点再加上0.001*(max-min)。2 o7 W# {) y6 s1 I0 |

    1 D: j5 N3 `- v6 `' _: |: rs = pd.Series([1,2])
    " a1 ]% U' w* M; H# bin传入整数& ~% k) t7 G' I, w, r6 Z4 Y
    : D" s' v7 b' g/ r, s! V
    pd.cut(s, bins=2)
    " g7 V* n; F# x/ D6 v0 pOut[39]:
    ( B0 |. m- E- k! s- q( a; j0    (0.999, 1.5]
    % w' z3 o3 m( n/ ~& D5 B6 q# Y1      (1.5, 2.0]( V& |* Q# P+ \% k; F& N
    dtype: category
    4 F; M6 B+ e* D, YCategories (2, interval[float64]): [(0.999, 1.5] < (1.5, 2.0]]6 b- M- J1 {* I% x2 m. d* S2 p0 `
    $ w& p: z5 ^) q% v- [  H
    pd.cut(s, bins=2, right=False)) o9 T; j; ^% z" T. }
    Out[40]: 9 c1 X  a( G9 z) m0 J  X
    0      [1.0, 1.5)6 Z/ v3 r. x) z* n" p, K1 j' q
    1    [1.5, 2.001); t8 ~* l2 T. E9 ^' m& W
    dtype: category) j& P9 V6 y+ p* ~
    Categories (2, interval[float64]): [[1.0, 1.5) < [1.5, 2.001)]% `+ u1 W2 |& U( e1 x" j! D* v
    ) ~: Y9 l9 X* X9 I5 N
    . D' x5 g2 z+ i( g$ x
    # bin传入分割点列表(使用`np.infty`可以表示无穷大):
    / M! G7 @3 H/ K& K/ lpd.cut(s, bins=[-np.infty, 1.2, 1.8, 2.2, np.infty])$ n# J, h) @! l2 P
    Out[41]:   g6 E( f$ q1 ^$ L# a( z. l0 L% m
    0    (-inf, 1.2]
    3 y6 {) v5 H6 @5 X+ n5 T1     (1.8, 2.2]
    : l# ^: j! c* k& y9 U5 Sdtype: category% V; h6 h2 Z0 ~/ Z2 t5 x
    Categories (4, interval[float64]): [(-inf, 1.2] < (1.2, 1.8] < (1.8, 2.2] < (2.2, inf]]
    ) A" p6 U( j9 i
    7 S! R, o# [* I, R) g2 ^' l1 A1
    9 y% ~; C& E* V$ u6 q- \& z2
    4 G( A, T/ m) J7 N) F3
    ! C% \( T6 `* w6 f4
    + `. Y, U, e- j5$ R$ }+ v. F  I. J. j! H* m
    6
    . c5 G" W. t% R3 j- s1 r7
    5 E+ U: I$ [( Q& n80 M* b3 ^; U3 J( u6 a
    9
    9 M" q, x' j( p, t) l10
    ; u; E+ @2 J5 A+ L+ `11, n6 T7 h8 h: b; {
    12# u" S& H" ~$ e% a2 a! n
    13# G( |# G7 C3 O/ U, I7 K2 z: G
    14/ S0 b- a+ E5 v6 K% p. E
    15
    ; \; q0 L' l' e1 ?* d; s# y" L" {6 f16
    2 z. @4 u* N2 F17
    + w; e( J+ C+ ~. t" u18
    4 d3 a9 O6 i; N- s) e19$ Y$ b6 D. q2 J5 x5 I& N
    20$ W( S- P4 k9 X" t+ }0 O- e
    21
    ! S. B  u$ V8 c22
    " {" U; j$ o/ Q. Z, N# j6 W23' l+ ?! s1 _4 Y. l; j, G( `3 f6 H
    249 g1 ~! O8 B8 Z8 i/ g0 c5 z) |! i
    25( |/ {2 T* A+ B5 B
    labels:区间的名字
    3 G$ E$ K8 \. nretbins:是否返回分割点(默认不返回)
    5 [/ ?( i6 I. P' T1 ^1 v' R" D默认retbins=Flase时,返回每个元素所属区间的列表
    1 K. `/ y& T7 e# k( Uretbins=True时,返回的是元组,两个元素分别是元素所属区间和分割点。所属区间可再次用索引取值
    1 z. R% ]: ^8 V6 ?7 T& k& b  q0 P8 v% x" v' l
    s = df.Weight) i0 q1 Q. t8 _9 G" v
    res = pd.cut(s, bins=3, labels=['small', 'mid','big'],retbins=True)
    # y* ]! P3 l, s; f) H# ares[0][:2]6 S7 p: q9 q+ V+ m- L

    ! @; T& W; r( e3 H8 l) BOut[44]:
    $ I5 w: j4 U+ Z4 E  G# _7 W0    small. w+ v6 J$ [2 Y  s/ D3 \
    1      big1 u) S/ ]5 ]* G" e" p5 t& m9 y
    dtype: category
    4 I# }: i2 p7 m3 K/ G& kCategories (2, object): ['small' < 'big']- c, k# |5 C- E/ W+ B" v
    - p- U9 f# \" O& Y
    res[1] # 该元素为返回的分割点
    - y0 V2 d3 Z, U6 i6 b: o; yOut[45]: array([0.999, 1.5  , 2.   ])
    , b: `; j$ D% f1
    , b( P- E1 r0 p$ u2
    ( \9 j+ t& R# d, y3: S9 {2 Y- R3 j" K; l% G4 q
    4
    & l7 m$ r# H$ k+ f5
    . W0 R3 g3 N- U; `/ z& J6
    : N& K  q* N* c+ a+ @' L+ E0 p# S74 v, p* v- @1 Z; J$ N, c0 z+ g
    8
    2 {  E3 _- K: W" l( P9 }5 d98 ?9 s8 w: a7 `0 r0 J
    10
    0 p* e8 ]* |$ |" \/ \9 p9 T! a" ?! x11
    % H- X; ^: T% ?% [  C! _2 T12
    ( x' |5 V% C+ [% \8 w; Y5 _, {( u$ fqcut函数。其用法cut几乎没有差别,只是把bins参数变成q参数(quantile)。
    5 s/ z3 z9 n! ?/ ^& Vq为整数n时,指按照n等分位数把数据分箱4 I# j% A, L5 P& [. z3 d
    q为浮点列表时,表示相应的分位数分割点。
    : D( p8 ~" M6 D- |# @s = df.Weight- c+ }$ s% L+ i" ]2 p- j; R

    0 D$ S% d4 y* v4 Zpd.qcut(s, q=3).head()) B; ?* t# D. \# W3 V
    Out[47]:
    & l1 \- z5 J6 ]0    (33.999, 48.0]
    7 z7 l6 Y6 V) P& K- f1      (55.0, 89.0]
    5 h1 c" m) n2 p2      (55.0, 89.0]
    5 g2 s. N+ R+ f1 x1 e& @3 E6 U3    (33.999, 48.0]
    ) Q+ h, h; z  L9 m4 g, W, r$ A# k4      (55.0, 89.0]3 m5 ^7 B- |: f: M
    Name: Weight, dtype: category7 ^2 O  F' ]+ x+ f1 X
    Categories (3, interval[float64]): [(33.999, 48.0] < (48.0, 55.0] < (55.0, 89.0]]. b) X: T3 B2 d1 u0 K3 Z$ ]( s

    / X( B7 @2 ~6 f! \" q2 B! ]% o9 p4 |0 Dpd.qcut(s, q=[0,0.2,0.8,1]).head(); F& ^) D- Q, o" k
    Out[48]: 9 y- }5 p: G$ p! w' V
    0      (44.0, 69.4]( u; }5 R1 ~% u
    1      (69.4, 89.0]8 F/ {8 _7 A5 S: U+ J
    2      (69.4, 89.0]
    ' H. t$ Y2 P' V: T" O# m) V4 |8 G3    (33.999, 44.0]* Q1 Y& t  q3 z6 J  Q
    4      (69.4, 89.0]
    + L8 p' |6 p' HName: Weight, dtype: category+ ?3 J, n! a. w  \
    Categories (3, interval[float64]): [(33.999, 44.0] < (44.0, 69.4] < (69.4, 89.0]]! o# [) |" w* L8 t: W/ ^
    & E# _$ W4 [$ c
    1
    # S! z/ G/ j  r  k$ d2
    ( P) Y. s$ u. R0 u' \5 Z4 \: P: |3
    * K* X5 I7 a" x47 m& i+ Y, j! P6 L) g: `3 M
    5
    1 B, z6 T; |* A/ _0 e0 W* s+ R6
    + x/ y' \; ^- J# e8 G) _) @7  U0 x5 V0 I2 S1 y$ q) `: @# u4 ?2 }; S- S
    8; K' T; u$ i0 j& q5 b1 f
    9
    7 |8 k3 G# q% k. @: |, W$ \7 c10
    ) k& e: ~+ W6 I. a- o11& ?; e% W1 C1 o  x8 _; {0 f* P
    12( }, k2 ]$ g( r* Y
    13$ p0 X4 M2 l% ?1 P% _
    148 t$ p4 B2 ?0 r$ a5 P9 V* O
    157 T% m6 `7 d( \
    16
    . Y4 `# \& M: z9 b17
    + @3 t2 {" F1 ~8 F" Z& W18& R1 M( M$ y: J
    19' g  s% d! U; V9 ]
    20( S* N% W6 @; h& X) c. w
    21. B# j: B( ?* Y4 s' o7 Y: W
    9.3.2 一般区间的构造
    . u0 Z. A# s) x! s3 ~: h  pandas的单个区间用Interval表示,对于某一个具体的区间而言,其具备三个要素,即左端点、右端点和端点的开闭状态。* e5 z" v4 L% \3 b/ K, J5 f# V
    8 U+ R& B  y7 E; G8 a8 ~
    开闭状态:包含四种,即right(左开右闭), left(左闭右开), both(两边都闭), neither(两边都开)。
    7 `+ Y& ^8 n! y6 Smy_interval = pd.Interval(0, 1, 'right')5 g& X  W& J+ X" Y. R

    , J5 Z9 W1 w* z# e. umy_interval, @5 |+ x8 C7 @" m) M  \2 r9 i. {
    Out[50]: Interval(0, 1, closed='right')1 U8 V- ]2 p: h" p' r0 X- F
    14 K/ t8 F2 h# E
    2
    ! F4 }) v5 w9 X! N8 I9 V33 E( c% C/ a9 w
    4
    ' O8 N" `4 M' }; v- l; d* Q区间属性:包含left,mid,right,length,closed,,分别表示左中右端点、长度和开闭状态。0 l& ]. r1 I" W( i) O: n
    使用in可以判断元素是否属于区间7 U$ R* j1 i6 t; U. {
    用overlaps可以判断两个区间是否有交集:5 k7 `2 e* M; C' Y- X8 S4 c, {7 `
    0.5 in my_interval/ V% u- [, e% Z9 n" `& C5 {
    9 Y* R# G' G+ R' V' c' R
    True
    0 t! j# X6 W* R3 L1 o1
    % o( H4 u1 A8 k, h' Z& J2
    $ Q) i+ m9 t* {9 u0 h3/ r% t: o; f4 E7 a& p3 _: W
    my_interval_2 = pd.Interval(0.5, 1.5, 'left')8 W  I9 L" o  e" l# h0 |
    my_interval.overlaps(my_interval_2)
    ) Y. i' R3 N4 ^8 ]. {8 D6 [$ S8 F8 t4 e/ R' h( r
    True
    % G3 K" Q0 v& v- l1
    ' I) _3 `4 n" x# d9 d- x27 e3 V/ @4 R5 c. F  H' O6 _
    3
    & O! q* h0 T. F5 p& o4
    3 w" u* I, p4 E% M, F* u  pd.IntervalIndex对象有四类方法生成,分别是from_breaks, from_arrays, from_tuples, interval_range,它们分别应用于不同的情况:8 `- X  Y/ l6 F4 O

    , w4 p, A+ M  C4 v; A% M2 d0 {5 l% ffrom_breaks:类似于cut或qcut函数,只不过后两个是通过计算得到的分割点,而前者是直接传入自定义的分割点:' [  E; M5 c" ^! P1 g1 T
    pd.IntervalIndex.from_breaks([1,3,6,10], closed='both')
    1 S! K, A0 V6 N* }3 H# L6 m! i. n5 ]- C3 t0 }; f/ x% Z
    IntervalIndex([[1, 3], [3, 6], [6, 10]],9 a5 w1 Z2 {" h' z" P4 t
                   closed='both',
    5 i/ ^5 N0 \3 V; D               dtype='interval[int64]')
    / S4 G" b6 b7 \' j1 F! }1" {0 H9 [8 V1 |3 G: T
    2
    + l5 Z5 {0 ~5 V" {, c7 a3
    ' B& \  Y$ s& a* L9 q+ r7 D4
    : k( r* j& d8 T' B8 G2 J5
    / ?" N  q2 m- Z; b  c& b" rfrom_arrays:分别传入左端点和右端点的列表,适用于有交集并且知道起点和终点的情况:% y; e8 u# W" {" W, m* W; e' ~  w
    pd.IntervalIndex.from_arrays(left = [1,3,6,10], right = [5,4,9,11], closed = 'neither')
    ) P4 L9 u% V% F+ u; {' V
    9 V5 H0 d2 }& P" h, ?- l2 DIntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],& S1 f* t+ z5 H  K# Y
                      closed='neither',; ?. V- e5 V: k& h; i$ D8 d
                      dtype='interval[int64]')) ]' Z% {: g% t- j9 l
    1
    2 X& D7 M9 i2 N' m( {2 d! B2
    ! e  K. ?3 M! B3 K34 V( x3 f2 `0 ?, y9 J
    4
    ; `1 b' r, d" V) a+ n) }5
    2 c" [7 y6 D$ w+ N" ~# ^from_tuples:传入起点和终点元组构成的列表:: @. X/ i* d. }9 ~8 _, S" `2 A% v
    pd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)], closed='neither')  B; D- ^6 T0 M9 ^

    - p% e: _  s5 b* P4 L) {IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],
    3 K  m; w4 s" p              closed='neither',# h. {  N# v- V( T8 q8 b$ R
                  dtype='interval[int64]')
    $ @2 T$ ^$ p' `3 P1
    0 G# q0 W1 E4 k2/ i3 o, M/ S4 @0 D5 Y; {* j
    3
    4 e6 A. `, d/ l5 ?  e# O4
    % p0 C+ p6 G, w# Q2 z5
    7 s+ {& `+ \8 n2 _4 x% `interval_range:生成等差区间。其参数有四个:start, end, periods, freq。分别表示等差区间的起点、终点、区间个数和区间长度。其中三个量确定的情况下,剩下一个量就确定了,从而就能构造出相应的区间:
    % D2 M) v% W, n) R' l6 h2 [pd.interval_range(start=1,end=5,periods=8) # 启起点终点和区间个数
    ( E! ~9 }+ Q& @8 T1 b# qOut[57]: 6 i7 O$ S8 l3 [/ L/ r. y
    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]],9 b5 W2 Y" A6 d9 w# v. ]4 J
                  closed='right',
    . r# H, z3 V* {3 m  q              dtype='interval[float64]')) y7 h: N; B3 |; d9 H' g* J9 H* @

    % e# s/ c( l& a1 j  Y# upd.interval_range(end=5,periods=8,freq=0.5) # 启起点终点和区间长度
    $ I/ {, i9 n/ [# p) E/ r$ A. yOut[58]: 2 X5 L1 S2 Q/ E6 Y1 q$ t
    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]],
    # H  P1 N3 \! [0 [: L: ^; q1 g* `              closed='right',' y2 u+ d  B8 _& w) j
                  dtype='interval[float64]')+ `& l1 C+ r' w% n
    1+ Y9 f1 }0 b# t, f- O
    2# J, l7 J3 B+ M
    3
    8 |" P; M( J, f/ h4
    5 Z4 ~3 j2 s1 z/ v# d5 o5
    ; P' d' b4 N( L0 z: c6
    4 b& s! d$ U$ V' A8 K) Z! n7
    7 m: I/ X( F& U8& {" p  v2 L! L
    9$ Y! ?% w  r! \. p0 H8 ]
    10
    - t5 ~9 B% p* q: h( F11
    8 d1 q7 s* ]) m, ]/ [3 t6 R【练一练】7 J5 m) @9 M( |
      无论是interval_range还是下一章时间序列中的date_range都是给定了等差序列中四要素中的三个,从而确定整个序列。请回顾等差数列中的首项、末项、项数和公差的联系,写出interval_range中四个参数之间的恒等关系。
    ) s1 P2 n- P% _  j5 m
    9 I9 D! A+ i+ c. F  n" ?  除此之外,如果直接使用pd.IntervalIndex([...], closed=...),把Interval类型的列表组成传入其中转为区间索引,那么所有的区间会被强制转为指定的closed类型,因为pd.IntervalIndex只允许存放同一种开闭区间的Interval对象。7 g) i" Z/ f2 Y, t" x# T
    ' m) `$ G& M- y8 }7 f4 C0 T1 `
    my_interval3 ]  y# R9 @* B( N6 [
    Out[59]: Interval(0, 1, closed='right')9 y3 a5 v# d/ |" S3 @  m
    : c- v: ^9 q" W. s$ L: @' q
    my_interval_2
    2 h9 F% A! w5 E  n5 x/ R& c' \* w& xOut[60]: Interval(0.5, 1.5, closed='left')
    ) O- j( t% d6 f, q! r
    9 J  b  u  j/ s- Fpd.IntervalIndex([my_interval, my_interval_2], closed='left')# H8 Z) y; A! Q3 U* C- w, H
    Out[61]:
    8 i( Y4 |# |- m1 ~IntervalIndex([[0.0, 1.0), [0.5, 1.5)],  A- ~" Z) M" l% B
                  closed='left',- M* Y5 R3 o# O; h' w; X. B  I- W
                  dtype='interval[float64]')$ |& }5 j2 G$ q! O) ?* F" N9 D
    1
      Q5 P- b0 W9 H0 o# S+ `22 T+ M* {" p5 r
    37 I- |# r2 n1 X: k4 Y* Y
    42 ^% r0 U# [2 D7 R& a! @8 J
    5" c" U+ S/ ^. q) h2 R( Z
    6
    0 B8 _9 m- X# W8 H7  G4 l1 `- K$ j8 E% v
    8
    ( P3 D8 h7 L2 Z94 G% N. _( H( M) w' D
    10# H1 T9 r' ?9 e6 }4 e+ P
    11& I4 m  v, \9 h$ p' }
    9.3.3 区间的属性与方法8 d9 H' k! G. |" R# x
      IntervalIndex上也定义了一些有用的属性和方法。同时,如果想要具体利用cut或者qcut的结果进行分析,那么需要先将其转为该种索引类型:
    3 n; r; }# u* V; U7 n2 f2 y+ e
    6 [6 n0 q" w9 Rs=df.Weight
    . e: H0 Z. I# M1 S. o. K0 Vid_interval = pd.IntervalIndex(pd.cut(s, 3)) # 返回的是每个元素所属区间,用具体数值(x,y]表示: D* X- n7 d3 ]( e: l5 k3 j
    id_interval[:3]
    - x8 d+ u% c3 K! c. F, _2 c8 H6 p0 l  {
    IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0]],1 h& q* J/ E4 q' V/ _/ X
                     closed='right',+ P1 F: Q9 X8 M1 ~- j8 s/ u* |
                     name='Weight',: q" w. i- r" l
                     dtype='interval[float64]')
    5 q0 A! {, H8 i19 g2 i6 ?: \$ {+ y. x9 a; o1 M. m' d- x
    2) j2 W) {( N3 k
    3# I5 L$ p7 ?. A7 n  w8 `- n
    4+ c# l; n) u- @9 X
    5
    * }) X/ G# }4 p; C" F" [8 O69 u/ [# b* |2 y" g  C
    7
      r, R1 @7 H4 i9 o$ P8
    * e& y1 A( K. u6 |0 @& Z与单个Interval类型相似,IntervalIndex有若干常用属性:left, right, mid, length,分别表示左右端点、两 点均值和区间长度。. U: K& |" I1 u" w
    id_demo = id_interval[:5] # 选出前5个展示
    ; ^3 E! A( ^5 C7 n, Z2 J0 i
    " C, H9 m1 W- K/ \id_demo$ l# p$ c# S; j% A
    Out[64]: 4 g5 C8 |# P% E' o/ z
    IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0], (33.945, 52.333], (70.667, 89.0]],! C5 \4 J/ r' q! {" j; `
                  closed='right',
    / ^- V( X0 d; `              name='Weight',
    : z3 N4 a( h; Z              dtype='interval[float64]')/ e) ~! C  O( u

    # p- R5 ?: z7 ]9 D  f: ?+ kid_demo.left # 获取这五个区间的左端点
    " q7 c5 Y4 N' H7 P9 |: i5 ^Out[65]: Float64Index([33.945, 52.333, 70.667, 33.945, 70.667], dtype='float64')
    # P; y# s# h- b- {# s1 t
    4 V; k- }; ^) ~1 L, Eid_demo.right # 获取这五个区间的右端点: N, H) x- m' R4 h. Y. @: z
    Out[66]: Float64Index([52.333, 70.667, 89.0, 52.333, 89.0], dtype='float64')
    # r% v4 J! Q$ }
      G2 K5 K! ]8 x5 p/ U' |- s/ Z" u3 uid_demo.mid8 K2 R6 o, Q# p
    Out[67]: Float64Index([43.138999999999996, 61.5, 79.8335, 43.138999999999996, 79.8335], dtype='float64')5 e, `: U/ [8 s  n
    5 ?( m/ q$ X) C/ |  L# L( j
    id_demo.length2 n  t* r9 T( G  ]" D
    Out[68]: 3 S0 H/ D& g2 L1 g* J' [8 a% P2 E
    Float64Index([18.387999999999998, 18.334000000000003, 18.333,& z* F8 ^! \7 R- t  N* ]
                  18.387999999999998, 18.333],
    1 W0 K5 l6 H8 I/ r/ w3 y             dtype='float64')
    , i$ s; \) b9 B& n( b0 n. A1 T8 K/ F; I1 n% t- i
    1
    + x9 x+ d" t) ~) E8 @2
    ' Y% `  v3 k8 [( a# r34 A3 l0 T$ ~5 m+ I
    44 W3 Q/ Z1 V& t, i0 C
    5' _8 U" K* `; _1 c
    6
    / J) G' b7 w6 q- J! S9 m! v# \7
    9 Y8 E) \9 U0 o  ]. m9 V8  U7 C" D# |' y1 U
    90 k3 c2 L/ d6 D5 Q
    10
    0 X% P  r; k4 ]2 s# ^11
    . L- ~" ~+ l, f# X8 q% y+ E$ J8 h12
    5 M% d* b  N; d3 E8 n13
    3 ]; o  j  ?  D$ M4 S# G14" k  Z9 t1 g5 `' Z
    15
    " O# L& i0 z  g' q" }+ S+ L# u9 i; Z16# b1 t& q' T- Q# l" L2 `
    17
    & s4 G3 _* x+ o. Y9 q" ^18
    / i# T9 x% ]0 S/ ?19: v/ q0 ]! T+ O, U2 Z  U
    20. z1 M- u- P, _
    21
    8 T/ w+ Z( k- l! W22
    & H& I& j! [1 Z0 g1 x) _) T: k% P0 k23
    5 ^9 ~# Q3 S. d: `3 rIntervalIndex还有两个常用方法:+ E; x; W( P1 Z; [+ P7 ~+ @- ^
    contains:逐个判断每个区间是否包含某元素8 @) V! \  Y$ |8 ~/ J
    overlaps:是否和一个pd.Interval对象有交集。2 ^4 x% k3 f, M$ z7 w
    id_demo.contains(50)
    , f. X  Q6 Q0 j3 L0 d* ]" fOut[69]: array([ True, False, False,  True, False])
    # @0 F  \+ o- l- c7 ~8 \0 U. c5 b- m0 }+ u. P6 \$ p1 g
    id_demo.overlaps(pd.Interval(40,60))$ X- z) m/ u! i8 l
    Out[70]: array([ True,  True, False,  True, False])
    " e# Z! g7 a* Z% p7 I. o/ J1
    : d+ R2 \+ z. M, q/ p/ K, o2/ h6 w6 b- h3 {+ V4 e( b
    34 ]5 a' @* ~: Q  Y% ?2 `: a0 z
    4" V. i* ^4 e3 v( L
    5
    8 ?. O% m" O. B  R' H2 O9.4 练习
    4 P5 N' ]/ P/ h$ o8 ]5 fEx1: 统计未出现的类别; y4 {- `+ J* s% |
      在第五章中介绍了crosstab函数,在默认参数下它能够对两个列的组合出现的频数进行统计汇总:
    9 n- Z8 i5 ~" ]1 Q* o# ?
    9 R' B4 V. z- q6 C3 S/ v, ]df = pd.DataFrame({'A':['a','b','c','a'], 'B':['cat','cat','dog','cat']})
    , }5 u% ~; d; k5 \  e; J0 Zpd.crosstab(df.A, df.B)( t5 I' {8 r! p( D, B& l
    % Z5 B. t$ {8 q" J  ~0 `
    Out[72]: $ {& I3 h/ }, ]; X
    B  cat  dog
    7 [+ }4 ~5 V7 X# w5 jA         
    / }6 Z% h; q$ a- V. Ea    2    0: z/ |; Z. H, n# o' f$ n
    b    1    01 F/ q# v% U6 q2 n: S  U
    c    0    1
    3 A. j& R3 P, k$ D$ M, K4 E1
    + k7 _/ ^1 E/ [& g/ Y7 z6 e2/ W2 O$ Q4 w5 R. F
    32 a* r" q0 i( l! U
    4
    / A: t2 b5 d. A" ]" F" A/ o5
    : R1 B: U9 n' |1 Y# b: U* }6( K% n# q  a9 e( `' a) Z; K
    7, J! |) p' j, r, q) O5 n! p
    8( k7 L8 ?( W- N: R  N8 Y$ s
    9, j$ c% f- ~* v( j2 Q0 H
      但事实上有些列存储的是分类变量,列中并不一定包含所有的类别,此时如果想要对这些未出现的类别在crosstab结果中也进行汇总,则可以指定dropna参数为False:
    0 ~2 `' C7 [; E1 B4 E
    ; C- f5 A% F( e' Pdf.B = df.B.astype('category').cat.add_categories('sheep')
    0 N) e% ?/ b0 `5 N' hpd.crosstab(df.A, df.B, dropna=False)# o3 G9 _% e3 u, p( L3 @0 Z
    5 r# A4 c0 {4 k& o' v& U+ U9 R8 o8 `
    Out[74]: 7 x! b6 J" N, y& f$ N0 ^
    B  cat  dog  sheep6 `  q) @6 m# I6 J0 A! l# B8 c
    A                 ' n$ A! j) {, b3 M4 r6 |$ E7 f$ ~
    a    2    0      0
    + C9 Z$ y5 `5 F) o6 `/ |* S' \. c6 O, Eb    1    0      0
    ' F9 R5 q: N( O/ G2 C8 Gc    0    1      00 {( Z4 O) v( @2 Z
    1
    - Q; e9 H3 B) b4 ]25 y. H+ P7 {$ z- g1 d
    3' H5 i6 i$ T6 Q  a+ j
    4
    : ^* x- x( j7 W8 l4 D5
    * u" B4 A9 V8 H# b1 B6
    . N  f! ^! ?' i/ b7 k% X7% N- f& l5 H) l; D. r( Y. u2 _$ y
    8
    ( J0 Y1 t/ R' r9 h( J  {97 K3 ?3 _5 A8 x& [& \- ]
    请实现一个带有dropna参数的my_crosstab函数来完成上面的功能。
    9 D3 \* Z! F- d* C1 |' ~* W1 `2 W/ S% r. _# S* @1 S! V- t
    Ex2: 钻石数据集
    ; Y- D! x7 j1 S5 X* ~  现有一份关于钻石的数据集,其中carat, cut, clarity, price分别表示克拉重量、切割质量、纯净度和价格,样例如下:5 x9 R. @* @$ L# G3 }  D; X( E

    0 J  r7 D6 H( D, M" Q* f% ^df = pd.read_csv('../data/diamonds.csv')
    ' T7 {4 r! @' G$ v, Cdf.head(3)
    + v" y3 Y; g% P
      c4 L6 H8 J+ H# e5 LOut[76]: ) M( Q% F* s, x  @# s. M# [% {
       carat      cut    clarity  price
    7 a4 p9 M5 M. h& H7 e0   0.23     Ideal     SI2     326
    . m7 T' Y/ _# Q& U+ J+ V1   0.21    Premium    SI1     326
    & l# X) b  f, @2   0.23     Good      VS1     327+ d7 l- ]8 }( _* r& k
    1
    1 O4 a5 G2 ^7 h+ a) w. F% ~2
    ' k" e7 z% {1 S! j3( A: C3 n4 p( ^6 Z+ L( Y
    42 b9 j! t0 o' i& L
    5
    ( I. \: R! A, z' T3 i6
    7 q! q; N: C+ U  |4 b: i* m7
    1 a5 `* {; d8 `5 o& a8 W7 j8
    5 V% Z- S: [% q) W, \( C9 l分别对df.cut在object类型和category类型下使用nunique函数,并比较它们的性能。( ]3 G5 S: B/ E. B+ L
    钻石的切割质量可以分为五个等级,由次到好分别是Fair, Good, Very Good, Premium, Ideal,纯净度有八个等级,由次到好分别是I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF,请对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。
    % g& B) v, M& b6 ]' V& |分别采用两种不同的方法,把cut, clarity这两列按照由好到次的顺序,映射到从0到n-1的整数,其中n表示类别的个数。1 Z. C  c: n* u6 d: @' B. W2 b
    对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。
    1 V! K. I5 n% [& R# z第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。# V  S" Y( b7 _
    对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。% u$ w% Y9 \% |4 J* Z
    先看看数据结构:
    : N4 U# L" ^5 n2 K- u- L
    6 T  m' m6 s( ddf.info()( e' c0 r& d5 X& b+ G* P
    Data columns (total 4 columns):1 }. X& G! j8 w4 M" [
    #   Column   Non-Null Count  Dtype  
    ) N0 `1 N4 `! e; N---  ------   --------------  -----  
    ' D+ L- y1 _) l1 ^  R8 _9 s 0   carat    53940 non-null  float640 ?  y1 d% _& ^! l5 X* F; z/ z
    1   cut      53940 non-null  object
    " h7 m" c6 C! D! O 2   clarity  53940 non-null  object 2 X6 j8 m5 f; v, Q
    3   price    53940 non-null  int64  9 r6 F1 c, @5 I8 r/ R
    dtypes: float64(1), int64(1), object(2)8 @; m, @: {9 _
    1. V0 k# j! j9 R$ y% x8 d
    2; F& x! J2 Y7 ?4 z
    34 h5 x0 E/ \/ ^$ t
    4
    ! p5 S& O. ~" l  i& }# A5: e# \  k; ?9 v) K2 x" Z% \% P
    6* m+ N- O6 t. A1 b+ g5 D! ?
    7
    9 w9 ?4 v$ r5 Z' v' J) n$ H8
    ; v" s. g0 P8 I9 V  S# D92 {: k5 t- a6 M
    比较两种操作的性能+ |) d- f; \2 {- w/ A/ l
    %time df.cut.unique()
    7 p( m# T6 v& X! a6 `" ~
    " l8 G  `. H! f7 A0 H% X0 a4 @* l' a4 gWall time: 5.98 ms: M9 s: ]# U) O
    array(['Ideal', 'Premium', 'Good', 'Very Good', 'Fair'], dtype=object)) W  |8 F9 h& t8 K
    1
    1 s! l0 X, M# J% ?2 [2. [7 O  C0 k( B  m4 V+ I% z9 K% @9 N
    38 X+ s; O2 H- z0 C' B1 n9 b, h
    4
    5 `* s2 i+ S7 V# M4 a8 D%time df.cut.astype('category').unique()
    ! l8 V9 r9 S3 h' ]6 l4 C1 K# |5 d, K" m; n3 U2 @
    Wall time: 8.01 ms  # 转换类型加统计类别,一共8ms' M% f4 L6 L2 |  e; L4 b
    ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']9 K7 X/ C% O6 }8 ^  p
    Categories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    3 e; {$ R+ N+ ]% b, ~5 U  G7 ]1; |: ~0 T" G. f3 X
    2
    2 G; Z- {* E& I3
    , Y6 K/ J4 ~$ y# s& @3 h6 E4
      U8 J) M0 \2 ~5 |7 j- K5
    0 G  G; d6 P! h0 [df.cut=df.cut.astype('category')! Y* U. w7 w7 I' y- _2 o4 y: Z
    %time df.cut.unique() # 类别属性统计,2ms
    : {7 `5 P# d! V8 l! V! t$ u6 E* X
    5 n2 {0 G) H! `- n7 k5 SWall time: 2 ms
    & u* L: E/ R- @+ [9 Q['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    ) O" q, t( H1 f: VCategories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']) s  K( p. v; T8 K% e9 x
    1
    ( J& {# N+ \3 T3 s8 t5 N24 R$ x& O2 C. J% a5 ~
    3- w7 A% q2 s7 w8 j
    4
    0 @( k% x( w4 f# Q5# v4 E& L* X/ t5 M" D2 b
    6+ l' V7 ~) b- X6 u, y
    对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。: \% V, B9 ^9 ?5 |
    ls_cut=['Fair', 'Good', 'Very Good', 'Premium', 'Ideal']! Y8 Z5 ?4 }3 K: @! g  t$ {0 o8 Y4 n
    ls_clarity=['I1','SI2', 'SI1', 'VS2', 'VS1', 'VVS2', 'VVS1', 'IF']; @& z  R0 a# l7 _( _: j
    df.cut=df.cut.astype('category').cat.reorder_categories(ls_cut,ordered=True)  # 转换后还是得进行替换# x/ Z% i% K$ v
    df.clarity=df.clarity.astype('category').cat.reorder_categories(ls_clarity,ordered=True)) t+ V8 v& `" P
    " p) H2 h9 k2 A: u: j( H3 {
    df.sort_values(['cut','clarity'],ascending=[False,True]).head(3)
    6 b# z. A' W% S' {+ K' D- ?9 y4 l+ }- y! v
            carat         cut        clarity        price! H( s  W# w! ?$ }1 F
    315        0.96        Ideal          I1        28014 T6 B, y5 @) k; U. ]: N
    535        0.96        Ideal          I1        28265 P; i8 E! b8 k& _4 {
    551        0.97        Ideal          I1        2830' g% C$ i0 p5 Q8 f8 d
    1
    ) r, G# o  o1 |- R( ]# S& o( a7 q2
    ) ^: b5 H, s$ z3; J0 v0 v4 u( T) }6 w, n# Z1 z* M5 B
    4, {' C: B( R% u! C
    5. C: J" a6 Q; b- R# t" o
    6
    : V" [, c1 I8 ~* @7 K; ]$ I7
    7 Z6 M! r7 }& U/ D* M/ Z8
    + J% V; L3 _! N% t9
    ' O0 u1 ]; T& i7 ], @10) t' @: d* j6 O8 N6 e
    11
    : D9 t- M1 L5 M- E0 W% @分别采用两种不同的方法,把 cut, clarity 这两列按照 由好到次 的顺序,映射到从0到n-1的整数,其中n表示类别的个数。
    9 {  ^  L6 l4 h/ N& E2 @# 第一种是将类别重命名为整数4 j/ _+ Y: m( H8 Q0 x
    dict1=dict(zip(ls_cut,[x for x in range (4,-1,-1)])): P$ q1 d& `* i/ ^. }+ L" f
    dict2=dict(zip(ls_clarity,[x for x in range (7,-1,-1)]))* ]( u& B0 f& `& C+ n

    ; v, d4 r3 z! G% e' K) J" Tdf.cut=df.cut.cat.rename_categories(dict1)
    % T9 i/ ^" H9 ldf.clarity=df.clarity.cat.rename_categories(dict2)' q* H: J3 V% e0 y$ p* m  h) K
    df.head(3)
    % f. Y* o3 l2 V# k) B2 L  s% f& E" w/ _: `; q; }+ k4 L, W
            carat        cut        clarity        price
    4 |9 O2 _. |, J9 X2 T$ i- O* }( w0        0.23        0          6                326+ R* v+ g  _: I( j. i. R+ e
    1        0.21        1          5                3266 K' J! e8 h* ~
    2        0.23        3          3                3274 ~/ S6 U4 {  r! I; o) D; X
    1, Q( ?* B# ~$ E' `4 {( y, o
    2  w2 E. S) R7 z2 O  b- E
    3
    0 r6 A- D( X! v$ |6 S- k4* Y+ {. U. F: [6 ^3 P4 `
    5% M* X$ X0 F* W0 h# _& X- C
    6
    8 k: P' |9 m/ V2 H3 U" S7, l, C9 |+ o( W! H- A& r: `
    82 X5 r* F- L( e* p( U' Q! p* H# S' G& M) K
    9
    4 M& b6 N1 N3 a( w10- M; Q. a3 h% B1 [
    11
    3 Y, I. w/ x$ s# {12
    . b. }: b% w. Q5 s# 第二种应该是报错object属性,然后直接进行替换" {0 n, f$ Q& j1 W7 n  O
    df = pd.read_csv('data/diamonds.csv')# g' S" I1 _8 M; ]) a% v
    for i,j in enumerate(ls_cut[::-1]):
    5 D/ z0 l, B0 \6 g; [+ M5 `0 v9 q! h    df.loc[df.cut==j,'cut']=i
    % L' ?2 I* `9 `: a. H
    5 K" I, Y' ^, C  L. I4 ]+ x  }. d/ X. \for k,l in enumerate(ls_clarity[::-1]):
    9 T+ c: J) A  G  n% y8 h    df.loc[df.clarity==l,'clarity']=k; b) q/ I  w# H" n, S
    df.head(3). B' K  J; i) F9 k- N4 u$ B

    0 O, A+ s( i: G! j$ X  z. S        carat        cut        clarity        price' l' C; i' ?1 k! [
    0        0.23        0          6                326# d) F6 O: I# @8 k
    1        0.21        1          5                326. i- U3 \+ k  i! Y2 }
    2        0.23        3          3                327
    . |) j& E0 J  ]  T" N/ Q6 o8 r1
    2 q. a' B9 a9 A0 S# v2
    2 g0 w6 Z0 e  a! }3' c$ V' t# Z7 ^* v
    4: x* j. }( [5 h* v$ d% }
    5. h9 R+ T9 t" E' J
    6
    6 J7 W, j3 {6 N8 N+ \& f' ?/ H7
    " M" t7 y- |# O  U9 p8
    - O. @/ \, T' A) J6 a98 S9 U* i. g" e; O6 B
    10
    - j. X+ b9 Y& a* z4 K5 g' c11
    # ^0 v3 t: V( w1 g& r9 v! l5 T12' l+ l! O- H+ U
    13
    3 X( M5 v/ Y& W- E, m, p. ^对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。  L3 O1 m4 C& T3 G7 Q4 z
    # retbins=True返回的是元组,第一个才是要的序列,第二个元素是分割点
    & Z- a1 X! S0 Z* A) ]& Wavg=df.price/df.carat% j% w3 H. _8 _3 V1 z& E9 a! V

    6 t3 e3 p' F% z& f3 ]3 zdf['price_quantile']=pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],4 x! _  x3 v1 V0 K0 j
                                  labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]
    & v' Z7 R! B; |9 W1 T0 W: a' `. f# L0 X% k* y5 P
    df['price_list']=pd.cut(avg, bins=[-np.infty,1000, 3500, 5500, 18000,np.infty],
    0 M4 J5 c$ N# C1 D                              labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]
    5 t+ y" ]& C/ N; H1 ?$ Y4 r8 p4 Xdf.head()  @! t2 m% h/ D! |+ _1 F8 Z9 F

    % j! I. W$ a/ G        carat        cut         clarity        price        price_quantile        price_list/ l3 `* \$ i7 q* V- }7 d' [; B+ o
    0        0.23        0                6                326                        Very Low                Low
    ' h/ ], a8 U9 r. [1        0.21        1                5                326                        Very Low                Low$ Z+ w2 V+ [2 W
    2        0.23        3                3                327                        Very Low                Low7 N+ r" P% q3 r4 Z! E, u% g
    3        0.29        1                4                334                        Very Low                Low' g; W0 ~9 }2 Y' D$ Z# `- S' [3 g
    4        0.31        3                6                335                        Very Low                Low                                       2 ~$ Z# f6 |. i2 F: ]! O

    ! O% w4 t& G; Q: _1 Y1 U1
    $ ]; @1 D- a$ Y/ C5 h2
    2 v, L. Z0 }( N" _3
    / N3 ^. a9 ~, r' D0 m+ N6 U* v4
    1 W0 j; |5 P. u' E! \59 r  o5 r1 y& I' [  S* g4 o  g
    69 s( Q) V' s! v6 [1 ~5 H5 y
    72 S; B  Y; t: ~' F( _4 z1 S
    8: K, ], O2 }# t( @
    9
    % X1 E" `8 W) E: W- L104 E; L) v2 ^1 _2 E, B
    11
      h$ ]+ H( B4 F" f. B5 m3 E12" _& {$ _- K: Z$ b! r4 B3 D) c
    13
    ( a( [% z# Z0 m% c4 V" s14
    ) {& l% z2 Z/ z/ }3 w3 H, j; f15
    1 L+ P" V: }3 r16( M2 M) e6 ~& z8 W' x
    分割点分别是:! K8 b! v4 u7 M& ^9 \0 x; e
    8 D; n+ D8 J: A) e9 g8 k
    array([ 1051.16 , 2295. ,  3073.29,  4031.68, 5456.34, 17828.84])7 m1 \# {9 M, b( n$ o
    array([  -inf,   1000.,    3500.,    5500.,   18000.,    inf])+ [2 j' A! I( D5 K
    1
    8 c) p+ K4 C$ s* ^2
    ( C9 H, x! H1 V( X3 Z第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
    & h: D( R# q8 B. |% }df['price_list'].cat.categories # 原先设定的类别数
    ! R+ I1 W3 i$ L) YIndex(['Very Low', 'Low', 'Mid', 'High', 'Very High'], dtype='object')/ U/ y. ?  W1 J; Q/ f& ^5 y

    # [; c; h6 {4 k* S, ydf['price_list'].cat.remove_unused_categories().cat.categories  # 移除未出现的类别
    6 [  v1 l$ z5 u$ aIndex(['Low', 'Mid', 'High'], dtype='object')  # 首尾两个类别未出现! K* l) Z' P1 p5 f- @4 F
    1  i; o: j# i! f' @! u0 [/ L6 v
    2* v. t: X7 K" Z2 n6 r
    3
    " w" e* Q; C" b4 {/ Y( l4
    2 c0 m& w8 C, E2 v50 u& F: H+ B6 G
    avg.sort_values() # 可见首尾区间确实是没有的
    $ J& J- b" ^$ M1 t, r% f31962     1051.162791% D# a3 L, i0 q$ m6 Y; e
    15        1078.125000
    - F+ b; X) v) I4         1080.645161
    & M$ J7 V+ f% U% t7 S28285     1109.090909
    ) `2 |, t/ M6 V13        1109.6774197 j% b. y6 U& \! ?8 ]$ E. \$ Z# i
                 ...     
    ) L: E! n2 C, m% h# h. s& M1 C& H26998    16764.705882* P3 b9 l2 N0 Y, e2 l4 _0 ~
    27457    16928.971963
    9 D5 e6 H4 {. c  A" r2 X27226    17077.669903, E8 `9 l" }2 T/ G3 F
    27530    17083.177570% `8 R$ t* @' _' L
    27635    17828.846154* G+ Q* s1 k7 B4 }. ?2 Q# f" ?6 D6 R
    1* b9 H. c: x: Y8 ^3 Y$ I
    28 D" G2 f0 w! M4 @, Y# S0 ?4 T
    3; L2 M) [1 E* V9 M4 k  r# t- S
    4$ g1 l3 m4 ]1 i) ^$ A, x8 \* h9 p$ X
    5
    5 n# r) Y, n- m6
    ; w$ @: y( ^- k, p% D- f6 E3 g) B7 Q7
    2 F. q, o; P0 p/ k! _/ F6 g8
    - K8 D# _# r* S9 m/ d1 s9) h, C/ a" Y% j9 l" Q0 P& C$ o3 P
    10
    * I! z, p1 Q, v11
    3 ^* @& l3 V4 T1 \124 B1 N# W& M: V
    对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。
      ^6 `+ Y8 r( C1 c# 分割时区间不能有命名,否则字符串传入错误。6 K1 p, a; I4 |+ s5 m) C( C  V, X8 X5 V- S
    id_interval=pd.IntervalIndex(- K3 h1 V% Y" T% B! M4 B  v
        pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],retbins=True)[0]
    . E/ v- W% |0 a) X, }                            )
    ! k) R9 @2 ^; V# y7 R8 B& P  p* [id_interval.left
    4 ^+ g7 Q  m* J$ N( aid_interval.right9 G/ l- _8 _  k- p9 V/ W& _
    id_interval.length                           
    ' m5 b; a5 d3 ~6 x* d1
    0 Y$ [  v! l  \* K. c$ q22 A: M- G7 F' A( j: O
    3
    ) N( J5 D$ V, ~: }8 ^, v2 W& }4
    1 E0 P$ e" e7 a5
    * j+ r* b& N0 c, u7 e- @6% d# i2 e; l8 a" `9 k, W1 g, o
    7
    6 o2 L5 y# e& m' B; W第十章 时序数据) o! r* R: q$ \; {6 C, M, L! M
    import numpy as np
    : x- t, [# j- N* {2 cimport pandas as pd
    $ I/ S& P) ^7 s1# W4 \8 P4 S% ]0 g0 ]/ s# Z
    29 W; k4 ]  @* F/ l2 d4 |4 K

    % _1 O4 _" k8 A( E0 ~6 ]8 @& X  b
    10.1 时序中的基本对象
    % P# H  ^2 i3 E: L, ?9 Q  M9 j2 o  时间序列的概念在日常生活中十分常见,但对于一个具体的时序事件而言,可以从多个时间对象的角度来描述。例如2020年9月7日周一早上8点整需要到教室上课,这个课会在当天早上10点结束,其中包含了哪些时间概念?
    , K. z" B: e2 v& e) X1 \& k  q4 H- K  K$ k) y
    会出现时间戳(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的简写。4 ~8 ]3 ^  M' w! K+ p
    6 ?% B$ x) A3 `0 m8 Z! O
    会出现时间差(Time deltas)的概念,即上课需要的时间,两个Timestamp做差就得到了时间差,pandas中利用Timedelta来表示。类似的,一系列的时间差就组成了TimedeltaIndex, 而将它放到Series中后,Series的类型就变为了timedelta64[ns]。
    8 h5 I% o8 a/ g0 r2 [% I* N$ e# G
    会出现时间段(Time spans)的概念,即在8点到10点这个区间都会持续地在上课,在pandas利用Period来表示。类似的,一系列的时间段就组成了PeriodIndex, 而将它放到Series中后,Series的类型就变为了Period。1 ]- e$ P9 M. e, f/ z; l% }. Q# M

    " t7 d) ^1 x0 r会出现日期偏置(Date offsets)的概念,假设你只知道9月的第一个周一早上8点要去上课,但不知道具体的日期,那么就需要一个类型来处理此类需求。再例如,想要知道2020年9月7日后的第30个工作日是哪一天,那么时间差就解决不了你的问题,从而pandas中的DateOffset就出现了。同时,pandas中没有为一列时间偏置专门设计存储类型,理由也很简单,因为需求比较奇怪,一般来说我们只需要对一批时间特征做一个统一的特殊日期偏置。
    * k6 F( C$ a: J' Q& d" c& `* f: F  k  j2 D$ q
      通过这个简单的例子,就能够容易地总结出官方文档中的这个表格:
    3 K+ C& E6 w, Z, H+ v4 [# `6 R; ~) W, D* r  l
    概念        单元素类型        数组类型        pandas数据类型
    1 o  T# X6 e, w5 t8 `4 T0 r8 ADate times        Timestamp        DatetimeIndex        datetime64[ns]5 ^0 o8 j1 M2 A3 q3 X& F  @
    Time deltas        Timedelta        TimedeltaIndex        timedelta64[ns]
    4 l! f+ r: G0 I# aTime spans        Period        PeriodIndex        period[freq]
    - @6 y7 n& \" ?6 m' wDate offsets        DateOffset        None        None
      m% W+ Q0 b+ X) R( f  由于时间段对象Period/PeriodIndex的使用频率并不高,因此将不进行讲解,而只涉及时间戳序列、时间差序列和日期偏置的相关内容。: U* ^! ]7 o" f: E
    ) S# G: l. E" {7 N5 L6 o
    10.2 时间戳
      S5 o; h- k; R5 J+ C( p4 D! t10.2.1 Timestamp的构造与属性
    , J2 q8 F& a9 `- p) d单个时间戳的生成利用pd.Timestamp实现,一般而言的常见日期格式都能被成功地转换:: f3 C$ E9 w- X8 D8 C0 Y& c1 P. F

    - d5 f+ D! i+ N  G, T4 pts = pd.Timestamp('2020/1/1')& \& b& X% Y9 F3 a
    + L! v" g1 x) l% `3 H: L1 B4 z1 B) M
    ts' V9 g+ ^' V- A6 F2 g' e
    Out[4]: Timestamp('2020-01-01 00:00:00')/ h9 K8 _1 Z; s6 b1 g

    # c$ s6 n4 ?- ]( W# tts = pd.Timestamp('2020-1-1 08:10:30'). n* c5 ?" d1 M- K2 E+ `* j
    ) V, @5 T8 B0 p& Q! b+ _, Y4 U3 y
    ts
    6 {" z; B& P1 U( H8 u% ?Out[6]: Timestamp('2020-01-01 08:10:30')
    ( N4 u+ W6 `. K: u  Y1
    5 ~. E0 v6 Q/ l9 M( @21 b5 @5 f3 _2 F( z+ u+ S
    3
    , L, k8 F  _* a) N* S4
    4 a# p. G. G! M# t# G) A- [5( K  L  h' f8 C/ L' Y+ b5 P  U" c" Q
    6( u. o- ~3 ], b6 }4 x
    7
    7 j3 i+ i/ _. J$ Q/ x, ~0 R4 W8
    : g4 u. p/ y& y! P9
    $ w: v1 Z, {. ?& c) \通过year, month, day, hour, min, second可以获取具体的数值:$ P9 I- J+ y1 {' N" A, F

    ) F, K0 M! d$ q6 V) q1 ats.year4 L0 ^, Y( A# J3 l+ ~  y
    Out[7]: 2020% e, r$ S" e  x% I" T, {' e; }

    1 [7 D2 U8 \; }* e) z" a( v* ^ts.month
    : a9 J+ |" M# [8 z5 JOut[8]: 1
    - k; u9 I. t) i2 A* C) v
    , B, p* i4 H& jts.day
    ( V; _9 v* g2 X& U0 ?+ xOut[9]: 1. ?9 m& a- D+ @* m' N3 g: Q$ n

    ( S) y! x9 {# G/ Hts.hour; @$ f5 y$ e9 W' z- G
    Out[10]: 8
    & M( {1 R; Y5 C6 w
    8 T' b8 h$ L- Fts.minute6 d3 l4 z9 v' o7 u$ p
    Out[11]: 10
    : J5 @% f1 D, i; Z* I5 b, i5 |, n# R, [( A7 w- o
    ts.second
    ) u1 D7 B; ?3 V2 {6 K( HOut[12]: 30+ X) e8 T9 ~9 [) B+ c/ v; Q! I& j
    - P5 g7 Z: ~+ C, E
    1
    * d* W9 @4 c# ~' P; H6 {8 ~" }2
    . W( L( o+ X' C& p2 h& Z' D5 @32 Z# f/ f7 u. ~- M# ]" g- a, U
    4
    5 D, b& ^5 u- I5- C# w; J8 t6 p8 r# j
    69 `7 g0 ?  @3 R/ z
    7/ |9 y- x+ t. a
    8
    5 K2 C# `, C: ?9$ w$ u& S! u. |7 O) W
    104 r. [  E2 ~$ t: {7 |$ `; p
    11
    " {- x, Z. X9 c6 S  d, l4 g. u4 j12" S) g5 u  l6 q2 p
    13/ f+ k6 D8 ~" e( S6 ]
    14
    " Y& L/ H( p- ~4 l, o; k15: I7 R% F( y) |$ @; G* I
    164 W% @: c8 m/ U, A0 i2 a8 O
    17
    1 i' n9 U% }# [# 获取当前时间3 P, ]" S. N. e- \6 @$ j+ @
    now=pd.Timestamp.now()
    8 i0 z) s% }& ^6 g  [1
    0 S0 Q7 {& {. I( i5 e# _; Y25 H! B8 b5 D: z9 ]9 Y" J( Q1 p5 o) M4 R
    在pandas中,时间戳的最小精度为纳秒ns,由于使用了64位存储,可以表示的时间范围大约可以如下计算:4 B2 T- F+ `0 j' }6 @# F  h" D
    T i m e   R a n g e = 2 64 1 0 9 × 60 × 60 × 24 × 365 ≈ 585 ( Y e a r s ) \rm Time\,Range = \frac{2^{64}}{10^9\times 60\times 60\times 24\times 365} \approx 585 (Years)
      W, z5 u$ d; BTimeRange=
    $ W: Z: Y) X/ I/ |  q10
    . }' m3 c  N% K: @. c3 @9
    ' h- c0 J  u/ G7 P% T0 W5 F( ~ ×60×60×24×365* l5 C: O5 m% C  ^- B8 L" X. d
    2 , I+ _, d6 }$ f+ F# n% U% D# R
    64# l8 T0 S# U* s+ A9 I
    & d/ c; |. V: O7 t4 X3 |' r- z

    + q* v9 _, i" A; \ ≈585(Years)
    " f2 r; ]! q* |0 E$ L1 r: d6 t4 [, P0 ~5 @6 I
    通过pd.Timestamp.max和pd.Timestamp.min可以获取时间戳表示的范围,可以看到确实表示的区间年数大小正如上述计算结果:
    8 i, e, g7 T: q2 z: T2 R
    6 r& e4 g; v. I/ T! J: L( s! Apd.Timestamp.max! J. K1 V& Y9 G
    Out[13]: Timestamp('2262-04-11 23:47:16.854775807')6 b, \' a" {  j" K/ n* @. [1 Z

    4 ?! Q2 s# E; J2 T5 T- _pd.Timestamp.min  V+ F, p8 S( M2 u4 y$ A
    Out[14]: Timestamp('1677-09-21 00:12:43.145225')
    6 ?0 a* s0 ^; Y, w; v  b- a# q5 {
    pd.Timestamp.max.year - pd.Timestamp.min.year0 R0 n$ p9 P9 U% u
    Out[15]: 585
    0 `  {  p% L9 I" Z) ?; b, G1/ g$ z7 |+ d- F. V# o
    26 y7 r7 I3 r, ^: C+ {; O% Z
    3- K, Y# w4 ]2 `5 n
    4$ @+ e0 X8 |. T2 D
    5& k. h1 E' n$ m; X; a5 V3 B
    6
    ) N9 h5 O4 q7 x5 }" w" g7
    7 t$ r2 C; T5 E8 W2 @9 o8& Q# X+ t8 i- W% X7 ?
    10.2.2 Datetime序列的生成1 g) r6 O! v0 f( A4 D4 z
    pandas.to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, utc=None, format=None,( t& y7 [* a% ?: o
                                      exact=True, unit=None, infer_datetime_format=False, origin='unix', cache=True)/ d2 V$ i; q0 F) K- H6 P
    1
    ! e+ @8 [4 E: `& p4 z: q0 L% E. K2
    8 V9 P" X  E, J, ]) U( V  ?8 Zpandas.to_datetime将arg转换为日期时间。
    2 f- O; Z; p6 P1 {3 i# `: G# y4 y: @" e
    arg:可以是argint、float、str、datetime、list、tuple、一维数组、Series、DataFrame/dict-like等要转换为日期时间的对象。如果提供了 DataFrame,则该方法至少需要以下列:“年”、“月”、“日”。
    4 M5 b7 m7 u5 q5 C4 Z, Q. L2 nerrors:% [2 n" d1 D. S( |
    - ‘raise’:默认值,无效解析将引发异常# S0 M& ~2 C* v: T- w$ I
    - ‘raise’:无效解析将返回输入) e: T3 J% E, o: i' @( x
    - ‘coerce’:无效解析将被设置为NaT$ I0 w+ m/ h; ?7 V* d
    dayfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析日期,例如“10/11/12”被解析为 2012-11-10。如果无法根据给定的 dayfirst 选项解析分隔日期字符串,会显示警告。
    " `9 ~' r  ]0 s6 u" }6 M" I: yyearfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析年份,例如“10/11/12”被解析为2010-11-12。无法正确解析时会显示警告。(如果 dayfirst 和 yearfirst 都为 True,则 yearfirst 优先(与 dateutil 相同)。)
    9 y! s  u- B6 c( I( k( A; `% ^utcbool:默认None,控制时区相关的解析、本地化和转换。请参阅:pandas 有关时区转换和本地化的一般文档
    * T5 S% x! N# a$ ]1 h5 v7 C$ Cformat:str格式,默认None。时间戳的格式不满足转换时,可以强制使用format进行匹配。
    4 x5 ]$ T2 Z% Yunitstr:默认“ns”。它是arg (D,s,ms,us,ns) 的表示单位,可以是整数或浮点数。这将基于原点。例如,使用 unit=‘ms’ 和 origin=‘unix’ (默认值),这将计算到 unix 开始的毫秒数。6 G# _. {) s6 c# k3 U9 H* J0 }3 {
    to_datetime能够把一列时间戳格式的对象转换成为datetime64[ns]类型的时间序列:
    1 M9 U& s( e! S; r/ @1 q$ F3 Y+ {pd.to_datetime(['2020-1-1', '2020-1-3', '2020-1-6'])8 Z8 v' M% u6 T- s# J$ i
    " r' L5 r; X& {3 e0 M1 j/ s
    DatetimeIndex(['2020-01-01', '2020-01-03', '2020-01-06'], dtype='datetime64[ns]', freq=None)
    * [0 b* m% `6 {# n* F7 V1 Z, H3 O1
    ; a% T& K+ @4 j: o1 J0 ^' F& [' g2
    0 Q- W# ^0 \+ M# G+ \* a6 v3
    . I5 Q6 `! A2 u* @. G$ M" t7 ^- g在极少数情况,时间戳的格式不满足转换时,可以强制使用format进行匹配:+ Y4 n* g9 m* t
    ' R1 p% V- _; K
    temp = pd.to_datetime(['2020\\1\\1','2020\\1\\3'],format='%Y\\%m\\%d')) D3 \9 ?5 M4 d3 @, m, y
    temp: N1 z* r7 E% D* f
    2 ?! c: e2 [# {
    DatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)& ^( ?7 X) c2 w; C( T# _
    13 d1 Y* U% T# I4 S1 M: D+ u
    2
    9 w+ S" G7 u9 ?" U0 o. R+ p( f% r3; W6 i( r2 d+ ^. j" {+ I1 I  j
    43 b: O7 w- k) `  g1 l( S
      注意上面由于传入的是列表,而非pandas内部的Series,因此返回的是DatetimeIndex,如果想要转为datetime64[ns]的序列,需要显式用Series转化:
    ) b5 T3 V  X7 J& K" E; ]5 P! B, o, V
    pd.Series(temp).head()0 ]) u1 @) N3 l% `9 b. ]
    ! j, W6 [9 ~$ p' @) a4 n# j* g  n8 Q
    0   2020-01-01. b/ q" C" q2 {+ ]* _
    1   2020-01-03: M# W3 v. S% \! l% n  F7 i$ g& D9 W
    dtype: datetime64[ns]
    / g7 j- ^0 f0 p. o( o5 f1
      o: D3 x  |# [0 R6 q$ Z' C2
    0 x. C9 D. c. j! Q( Y" o$ S3
      _4 d/ x# S8 I# e6 }3 p49 L& X- w+ @! G& Q1 P
    5
    * L3 P# S8 _/ g/ X- B下面的序列本身就是Series,所以不需要再转化。
    ) E: J  G% b$ J$ ]: c3 a4 e7 J
    * _# H( f" j0 rdf = pd.read_csv('../data/learn_pandas.csv'); O8 ^  m; t, x1 X
    s = pd.to_datetime(df.Test_Date)# d8 y: _( T4 r6 O- o" [0 G7 m
    s.head()
    + W' w! `. Z6 l; m# ]: p0 I5 s+ e: z2 w/ x& X9 ]
    0   2019-10-05( j* Q. Q" z: V0 H$ E7 I
    1   2019-09-04
      k# D4 q' u7 x# q1 K9 O* Y% T  e2   2019-09-12
    ( Q: g/ Y  C2 y2 K, l3   2020-01-033 E8 r, B- l$ P7 I
    4   2019-11-064 R* \6 ^7 h, }6 Z* X+ D+ Y4 V% Q0 K
    Name: Test_Date, dtype: datetime64[ns]
    5 G9 q  w5 w% q* F: h0 {' r1
    ( F0 O, D8 [$ r1 a( N2
    $ l/ {3 {3 ?2 D$ F4 D3- Z4 {) d1 C, S1 ~0 n; s) C* z
    48 |+ t- h$ y! }
    5
    , K; j; s) L, B1 D9 m+ i$ R1 s$ ?6 K6
    - S, O; V3 [6 Y9 {5 b7
    ) o+ K% Q, R1 P) r) D3 H8
    & h4 z, o: s+ u9
    8 L7 w3 X( m. Z6 N10  `3 J5 F9 o! I4 a
    把表的多列时间属性拼接转为时间序列的to_datetime,此时的列名必须和以下给定的时间关键词列名一致:
    $ C5 j% V/ m5 `8 y) b6 P" xdf_date_cols = pd.DataFrame({'year': [2020, 2020],: K% d  `7 \% z- }; w
                                 'month': [1, 1],% {1 o1 a3 b3 l2 s
                                 'day': [1, 2],) `. Z$ X1 A! C% ~1 S$ ?
                                 'hour': [10, 20],2 b2 r; U' i1 s9 U( o8 G
                                 'minute': [30, 50],
    " X6 k) ?& ^4 d: E. d; Q                             'second': [20, 40]}): j! D$ b& W& ~" T# b7 F4 G. l
    pd.to_datetime(df_date_cols)  B, |" j8 f3 \

    / p+ H& o; q0 m  G2 e8 `0   2020-01-01 10:30:20; T3 U& z' ~7 ]" u$ k
    1   2020-01-02 20:50:40
    / H5 [! q; F+ O- f# x" qdtype: datetime64[ns]% ?1 g5 g, v; u- l7 C. A
    1) C1 D" w0 m) }4 Q
    2* v/ e" n. B3 S
    3" e# \! a& W, q) i# ?( J
    4- S% r9 p9 ?  d6 Y& u8 Z7 p3 m7 m) {% i
    5
    , J  ]+ n' J9 }6 R6$ E8 ^, j. H, b! [- u$ t6 b
    7: ^2 H+ H) V+ e; O& z
    8
    + O% d. P/ K( A$ U& r! W- z' q0 H9
    " @& [4 ^) s6 r. l6 Y10
    " R; L( k9 z! I11
    1 n. D* D, f# I, x+ a" ~date_range是一种生成连续间隔时间的一种方法,其重要的参数为start, end, freq, periods,它们分别表示开始时间,结束时间,时间间隔,时间戳个数。其中,四个中的三个参数决定了,那么剩下的一个就随之确定了。这里要注意,开始或结束日期如果作为端点则它会被包含:
    % |) `( F/ U! L* G' J9 q: ppd.date_range('2020-1-1','2020-1-21', freq='10D') # 包含
    # \; N2 ]7 f7 U" R- NOut[25]: DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21'], dtype='datetime64[ns]', freq='10D')
    ( {+ a& C. ?& ?1 S
    + l5 R8 n2 P5 Spd.date_range('2020-1-1','2020-2-28', freq='10D')
    ' k) W1 z3 R+ ?% m# {# E" E" OOut[26]: ( L" _% X0 I2 a% c& `/ A. }% A+ \
    DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21', '2020-01-31',8 D7 d9 K: z$ }% j9 s
                   '2020-02-10', '2020-02-20'],( G/ Q" t2 i! y: [' e* e0 W; D2 q
                  dtype='datetime64[ns]', freq='10D')2 Y0 U" k, M  K3 h, |
    0 p! A. l; [6 }3 H) L$ w& t
    pd.date_range('2020-1-1',
    / k( j& m  q- C( A. o% Q9 A; n& m. d              '2020-2-28', periods=6) # 由于结束日期无法取到,freq不为10天
    5 {0 \, X: Y' e! O. g8 _0 G! F6 E! U- E8 f% h
    Out[27]: % T! o& g# i9 G. F( A! b
    DatetimeIndex(['2020-01-01 00:00:00', '2020-01-12 14:24:00',! e5 \/ J* a6 F; A* B% @) ?$ N
                   '2020-01-24 04:48:00', '2020-02-04 19:12:00',) }3 l* B# N5 W/ \& V7 A2 B
                   '2020-02-16 09:36:00', '2020-02-28 00:00:00'],
    " B( T- m5 o$ U              dtype='datetime64[ns]', freq=None)4 F  X, L& o1 M& {

    , P7 x" J% i$ E3 J# B- M4 ^10 D" S* z' \; n% B2 y5 t  F7 o
    22 f: b$ [& i. y3 c8 d1 H  D' A8 a2 `
    3
    : K* \4 o# i. {5 p/ J3 V; m4# E$ Q( D9 S! ~4 R. q/ \, E
    5
    $ q3 m  j& t: w  Y( c) k7 K6
    2 @; L& U2 K, M" }; ^5 u( |7* C& |4 c$ t6 p# y
    8) |. M& F9 R/ z
    9
    # t3 ?" G6 I5 g107 x1 P4 K# \9 C$ o' p, {' j6 Z
    11& \) O* M% B1 M  m' x' J6 r+ l. t) M
    12' D. C) z# L# J4 D' a, x6 u: N
    13  N5 [4 a! z% e# T( T
    14
    + y2 g* S6 e8 A156 g& Y) Z1 w# v; ]
    16
    % H7 d# ]% s, c# R17* }% ]6 n( ]3 x5 @0 ], I
    这里的freq参数与DateOffset对象紧密相关,将在第四节介绍其具体的用法。
    . C4 \2 L1 P9 P2 _" f
    . @4 I4 n* R' n6 A+ U! r4 \【练一练】& l7 s# k% o+ u0 O6 V: c" [/ E
    Timestamp上定义了一个value属性,其返回的整数值代表了从1970年1月1日零点到给定时间戳相差的纳秒数,请利用这个属性构造一个随机生成给定日期区间内日期序列的函数。
    ! n5 q+ g" g6 \0 X2 [& E+ I+ K  B  {' O1 M0 O6 m
    ls=['2020-01-01','2020-02-20']$ w. ~; f6 j2 \" M% X: o
    def dates(ls,n):, c0 B9 ]# s& \& p8 i$ J3 l
        min=pd.Timestamp(ls[0]).value/10**9
    7 _9 n: }1 M; L    max=pd.Timestamp(ls[1]).value/10**9
    & B7 v. x( q' `6 m  i8 y" j    times=np.random.randint(min,max+1,n)( S; v' e+ ^% z3 S% ?% P+ v0 l+ |
        return  pd.to_datetime(times,unit='s')* c) Q0 o1 e% H" C2 s6 _
    dates(ls,10)
    , i: P' d4 m; C. m8 |* n- d1 \
    # P# L+ B3 P$ T& v/ O: KDatetimeIndex(['2020-02-16 09:25:30', '2020-01-29 07:00:04',
    ) P1 F& `, e) a  X6 l               '2020-01-21 12:26:02', '2020-02-08 20:34:08',( o, m3 n2 _. _2 G2 s
                   '2020-02-15 00:18:33', '2020-02-11 02:18:07',9 q3 H. b2 T' e1 n* P+ y3 o
                   '2020-01-12 21:48:59', '2020-01-12 00:39:24',
    - L" F$ V6 [" w* c               '2020-02-14 20:55:20', '2020-01-26 15:44:13'],; j2 e$ H7 {; c  q. A' |- L
                  dtype='datetime64[ns]', freq=None)- A1 w9 c6 ~$ @
    1
    6 Z: ~9 E" M3 Z+ X; H8 S7 D2
    6 X1 X; Z# |8 g6 G# b36 Q! o2 J. T% H: J+ m' T
    44 ~2 G& ^9 w  }, \9 g4 q5 o
    57 z/ r9 v! E2 X1 g. e! B3 m& D8 v
    6
    0 y  w, f; ?( l+ \8 ^) h. l  r7
    + L6 N' R9 `) H3 Y8 T0 }! A85 o+ R6 ~# R, T' b
    9- `& p; c! y/ b! s$ o$ t+ i( {
    105 F8 I. S2 I' T
    11
    5 n& I1 k* w  o, N1 C12( f: c5 _- [5 P- L1 A
    13
    # s# V7 T- `. l  x$ H, ]$ Q8 k  p14# a* F) v9 j7 c+ O5 C. J! W( _% A
    asfreq:改变序列采样频率的方法,能够根据给定的freq对序列进行类似于reindex的操作:  o0 {5 C: g1 ]3 x& ^; [
    s = pd.Series(np.random.rand(5),# x* y, X# G8 U& z9 x* [1 z
                index=pd.to_datetime([4 M" C+ Q$ w# ~! g. B5 S/ U3 r7 K0 D% a
                    '2020-1-%d'%i for i in range(1,10,2)]))
    0 x" H9 h( d) x; ^; ?  O
    ; D: J* |4 q1 ?( J# [6 t7 d8 ]7 \- m; Y1 Q
    s.head(), o- l- u% I  U
    Out[29]:
    ' Q( m: a! A$ H' ~- ^8 W2020-01-01    0.836578- L7 S9 |6 P& [# u& a
    2020-01-03    0.6784192 b, a) Z- ~# `, \% p( o
    2020-01-05    0.7118972 x, i4 x- ?. ~& v& B
    2020-01-07    0.487429& V/ I6 _# y$ k/ }1 D
    2020-01-09    0.604705
    0 U+ R  F3 x  i- Sdtype: float64+ @& ~( D7 f) M$ W/ q; P
      G! X4 i( E+ ~1 l* w* g3 [
    s.asfreq('D').head(): {& M  ~/ S( D4 x7 h1 t) E% ?8 z
    Out[30]:
    8 l5 D) H0 X0 H( \  D2020-01-01    0.836578  l, _- ]. ^) f7 L1 ]$ p
    2020-01-02         NaN" k' t: \% O& o& ^8 v& j8 J% g
    2020-01-03    0.678419* i1 N( B9 j9 V1 R2 _' r
    2020-01-04         NaN
    # B) G0 N2 U* q4 i& V* x4 M2020-01-05    0.7118977 O- {; t! c0 k- @
    Freq: D, dtype: float64
    + w% K& x# I; q$ D- M1 N) Q$ S$ h2 x# a; Z% `* e* c1 y
    s.asfreq('12H').head()& J' o6 O! i+ ~( b) l/ p/ v6 k
    Out[31]:
    ; [0 K% D: w5 F5 a8 f$ `& L2020-01-01 00:00:00    0.836578
    ( v0 t) a4 x" }9 i2020-01-01 12:00:00         NaN
    # d2 o3 U8 U# l6 J! V4 ]" c9 f2020-01-02 00:00:00         NaN; h: s* j4 l6 X9 S+ P2 Z! a
    2020-01-02 12:00:00         NaN
    ; }$ i+ J$ r9 l2020-01-03 00:00:00    0.678419
    7 f" [% q! b* IFreq: 12H, dtype: float64$ }7 r6 m7 G. e" i

    6 F8 H" o6 h) r8 a+ r+ E1
    0 V, `6 g4 @; h# U' o: E/ A* r! \2
    ; w! I3 W* I4 D4 L) p9 ^3
    , q# I' z" X% x3 |' U1 ~. u! F4
    ( P( N, u8 b% H5 d, ?5/ X: {: j2 e: b; W# q
    6, {! `/ p, T4 q4 K4 d+ C
    7+ P1 x% m5 ^0 D5 q) E+ l! R
    8# t1 b& @3 [. M+ {
    9( t1 @% i( M& S4 M+ g3 ]
    10# R* Z1 ^8 V& [8 L  Y) p" v# d
    11
    ( q8 b' k8 a& b$ Q- [7 i: \2 J# \# R12" e# ~$ l: W, S( p1 l7 F+ ~2 c
    13
    - ]: L/ I# b2 v8 \* P& y' L, c# N14# r$ s% Y9 H7 A6 a
    15
    8 W+ p5 l7 P$ L7 {  J$ ^* }16
    - N% ~# M2 ]4 B8 ^17( O0 T: g$ \& \! R3 v3 J7 T4 F
    18* M* ~7 I% u1 S" Y: |& K& H
    19( r# h+ ~" j( U
    20$ V5 o5 E- r# U/ ]* p9 o
    213 C1 L1 ]  c' \( Q+ r3 l
    22/ {3 v2 _* _) U( H" _) o
    23
    % V+ R2 J9 y4 x( V+ v" E, W24
    5 T: j9 Z* d# S) b; S2 L, }% G7 c250 Q8 g- n* I# @0 @- L
    26
    * q0 h# u( L* H273 Q: N2 k" a8 \# t9 l. O& S
    28
    ( v, z3 q: w1 k5 Z# x4 L9 s29
    1 e' s7 q/ ?" i/ D30
    " j5 `$ Y) ?8 {0 D314 Q1 @# y1 I$ n. ]% ]8 W5 J
    【NOTE】datetime64[ns] 序列的极值与均值% X2 b& ~  {9 ]+ a2 T
      前面提到了datetime64[ns]本质上可以理解为一个整数,即从1970年1月1日零点到给定时间戳相差的纳秒数。所以对于一个datetime64[ns]序列,可以使用max, min, mean,来取得最大时间戳、最小时间戳和“平均”时间戳。9 ?8 t  ]  [  z1 A

    - z. i; O( T7 a) s9 J8 `- u10.2.3 dt对象
    2 p* S2 I. y( g1 F& m  如同category, string的序列上定义了cat, str来完成分类数据和文本数据的操作,在时序类型的序列上定义了dt对象来完成许多时间序列的相关操作。这里对于datetime64[ns]类型而言,可以大致分为三类操作:取出时间相关的属性、判断时间戳是否满足条件、取整操作。
    7 y  N0 P5 a/ d& F* T; \; R" u# H  Y' a3 ]
    第一类操作的常用属性包括:date, time, year, month, day, hour, minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter,其中daysinmonth, quarter分别表示该月一共有几天和季度。
    & r2 M7 h/ \$ \" q/ d4 |: x+ Ms = pd.Series(pd.date_range('2020-1-1','2020-1-3', freq='D'))$ }2 k! L' u4 _
    7 e/ p5 y) {7 A% ^+ k) }7 f0 T1 k
    s.dt.date4 x8 C6 g! }% H  l4 a
    Out[33]:
    1 d$ G+ k6 t& P# V) A0    2020-01-017 G5 b! X6 f3 }! y4 j" k
    1    2020-01-02
      |' J4 v5 L8 p" T) [1 M0 _- q2    2020-01-03# ~+ ]/ b& H- O/ Q! W1 L0 T  K/ ^
    dtype: object
    ) |4 w8 K6 I8 v" e
    , a+ k' a4 T7 Fs.dt.time$ E3 Q: J9 g) v
    Out[34]: $ q% d: h4 A* v$ Z, b
    0    00:00:00
    5 n! O6 ]3 j3 O! F  o1    00:00:00/ o% X* u+ A% V" K% h  N
    2    00:00:00
    * n' v# B1 u* sdtype: object
    1 {3 Q) C2 L1 w, e% P4 v* C9 \9 v7 ?# w' t
    s.dt.day; W; s; Q, U" q3 r7 R
    Out[35]:
    # {4 ^: B3 T: H4 {/ f" Z+ n0    1
    & p: P: B& b) o$ `2 F- n3 d' Q2 X1    2
    3 H+ }! N8 M! f! _" T2    3
    5 w$ M# t- R0 ]4 d1 P* p' k+ Bdtype: int64
    ! Z$ w6 ]7 m* d& _( ^
    ) |9 ~' D% }1 C8 N) bs.dt.daysinmonth- `! c' o0 y, ]+ w2 _& w4 i3 S
    Out[36]: & f' E1 B6 n4 Q2 a1 l, }0 L
    0    31
    3 U; C. f2 O' s6 D8 I1    31, G  B% r, p' c+ Z
    2    31
    % l$ y: i( Y2 a0 c' d5 t( \& h/ Gdtype: int64: N! J0 D! R+ x/ d2 A6 }; ?

    3 m1 p% k0 h- k2 i0 I7 _15 X0 o8 q/ g  \- s
    2
    ! o, j* S4 a" Q, z& a8 ?3- z8 c! F. v) C2 Q- ^$ u5 T
    4% N8 ]2 l; j5 \, T! H- b$ q
    5: ]3 a! C2 l( a
    6  {, l8 ^: p; F. \+ M
    7: l. V2 z5 r$ ]6 o( X. T
    8
    / U' v8 S. ]; i, b! C) e" I9- P: |- {4 ^* `3 b* j+ l
    10
    8 ?! p( ]% E: l$ n& y114 |9 ^7 X! I1 p- J' D
    12
    & I/ V1 Q6 n5 X, X9 {135 A; i( W3 {7 _! ]+ r. H
    146 F. b4 }. |$ @; M0 s, l9 e  _9 W( Y1 ]
    15
    ' W# t2 r& Q& M16
    ! k, M; v5 O4 a$ r. j) ~173 |. _$ ?% e' l  R
    18  X8 M# n$ I" @. b9 J' K- t
    199 B9 |5 T7 }8 s3 j) p  ~
    202 d) ]1 D# E# A  x4 k& ]& ~
    21
    % W& K8 {1 q: b22! R9 s* o. h0 O+ d' v' `) ~6 d/ e
    23
    , l; n7 m. E. E2 p7 S& b; w+ D9 |24
    4 V; }/ J# Z1 B/ p) }25
    7 J$ T4 a* m+ g; Z1 w. |* y26
    ) v- J8 H9 ?* p: b) x4 {( r/ P27
    " u1 ~/ e1 o$ R7 c7 T& y28. L+ Y' G& M* k1 G) q
    29
    9 F5 G7 t0 ~7 h, j5 r1 |; \8 z  在这些属性中,经常使用的是dayofweek,它返回了周中的星期情况,周一为0、周二为1,以此类推。此外,还可以通过month_name, day_name返回英文的月名和星期名,注意它们是方法而不是属性:
    ) x; d$ a+ F) B/ p5 i7 s* B3 R$ e: C7 B7 f
    s.dt.dayofweek
    7 f6 @/ v5 I  i( v5 J& A: F4 {Out[37]: 0 s4 _6 h/ l/ R* Y" a8 r
    0    2
    & `! N" ~$ H: b) r" A+ y1    3; _+ Y- w( |7 a
    2    44 T8 t. h; s; S
    dtype: int64) d3 q# x! p  |* W- j& y

    2 N% d! i" c+ P8 J/ Z% G& _s.dt.month_name()6 ]. e" b! ^% A4 ]4 H% C
    Out[38]: 9 z' }% w; K/ A, ~
    0    January) m6 G: A) H/ |# |
    1    January
    9 e6 Y% o# ^3 h2    January' {* I6 u' b# @( A# [
    dtype: object
    : d$ e6 U& c- q
    . E) p3 f- g) f" X2 H% Gs.dt.day_name()" a! O  A- _( v6 W$ f: C& T
    Out[39]:
    4 C9 n& ^+ ^6 f6 p7 C; ^& _8 \5 V0    Wednesday
    0 h4 @9 u, e& O% o& j$ Z. O1     Thursday
    $ |9 B' s1 F. ?7 D6 c- P0 Y2       Friday* k* z. e# g- L5 b' E2 L
    dtype: object, ?8 S$ Z/ a5 Y9 Q! K( c
    ; g. B' m, S" W6 S9 l0 K4 `
    1* |0 G  x0 j" i' }
    2/ c( C- z( u' s/ a2 s! ?
    3
    , e- r- b/ x1 w3 R, D47 F  L1 s$ |( K. T5 E; b" P
    5" Z, d, `0 U- c7 s- Y8 w
    6' m2 j, g1 P: r5 e1 _
    7  i8 G- K( H! F. i( s) m0 U2 X; b
    8, |$ W* T6 W( g6 m; w! y( d* }
    9
    $ y( d' Y  t1 _  X0 l10  n2 d* }2 E6 r
    11
    8 R2 O' n. f+ u  N7 ]$ B12
    . H; t4 @" [1 a2 q8 A13
    " n  b- X) q9 m) F9 b6 ]14
    & R, }1 E7 J; P' \3 `  d9 T159 ~! F8 ^8 O* t" K9 X
    162 j6 {, U" _! ^* _9 u( @
    17& y" ^9 \3 [+ Y8 ^+ y0 |, [- D
    184 P, v0 Z* O5 z$ n5 y
    19- f! A( u, S! C; D) O, M. y
    20
    . R( N, B$ W# g4 [$ _+ a第二类判断操作主要用于测试是否为月/季/年的第一天或者最后一天:
    3 O& T9 z- _* r" Q8 x) \s.dt.is_year_start # 还可选 is_quarter/month_start8 ?2 W8 {; U" }: X/ W; d7 _
    Out[40]:
    " J% H* N' ~$ a' p0     True5 t% S; Z/ v6 o* \  I, y
    1    False9 D& q3 X- y' j; |
    2    False9 K/ F% T: [7 l/ o/ }
    dtype: bool0 P& o" T3 B: [" h! i% z+ Q- H
    # L+ R0 r6 z! \3 p! y9 @: V5 y
    s.dt.is_year_end # 还可选 is_quarter/month_end
    ( V3 e. h' r- e  F- z  S7 JOut[41]:
    8 c$ r+ ^3 I" c! ?0 a0    False
    * Y* P' c) V* g; H1 @$ M1    False
    4 ?; K4 h+ ^- Y0 I2    False: E6 B! t- X! k0 d) h2 x
    dtype: bool' n& Z6 D0 ]. N2 P
    1
    5 S# s! w4 J1 U) ~2" T/ \. d' W/ S4 d1 u  o) C) X. s
    3
    : ?; e7 z( X: I5 Q  w45 \; g) k. `5 C$ {: Z) c# J8 F; v
    5
    4 z5 H+ f( @1 A68 J" t* H  ~% v( G" M# P
    7
    # q7 L2 w' R- X: j+ J5 V85 `/ _& f& ?  J* ]* e
    9
    $ E. J, A# h$ d& M: |10
    ) _8 {3 i; g, }$ D11
    1 v( p. D0 J' x9 s0 ^122 @, Y  C8 P5 j3 M2 V3 s7 w
    13
    , G8 W2 V6 b! X# l& P5 g第三类的取整操作包含round, ceil, floor,它们的公共参数为freq,常用的包括H, min, S(小时、分钟、秒),所有可选的freq可参考此处。+ [' p8 g: ^' x+ S* g
    s = pd.Series(pd.date_range('2020-1-1 20:35:00',$ e, R3 o* |( R8 C4 n
                                '2020-1-1 22:35:00',
    1 `( J8 T. k: d4 K/ L/ @1 o                            freq='45min'))
    : V! L0 t  u3 l$ r0 i1 ]& p, s' I  q6 h0 ^: r; ^
    , Y' v4 w% p; u& W% y- c
    s( {/ I" L1 K! k. z  [: K& m
    Out[43]: ( O4 u$ ?5 i: h3 n. j/ {2 l
    0   2020-01-01 20:35:00: J& z5 Z) D$ o* i2 M* G2 ^
    1   2020-01-01 21:20:00
    6 L3 d* l; a2 g3 I  S' C2   2020-01-01 22:05:008 N! U8 v# D4 P* p
    dtype: datetime64[ns]; |* t. l  f' c

    # W* ~7 m4 K. k0 i# G3 M  Fs.dt.round('1H')
    - q0 Y. l: d! d2 Q; A: P; N4 Z" lOut[44]:
    0 ?: ?  y) Z: f6 ]& x0   2020-01-01 21:00:00
    # E# [% }" T  D1   2020-01-01 21:00:00! D8 I8 b5 U0 @: r
    2   2020-01-01 22:00:00( k. O/ {! O+ o- O+ ?, X: s
    dtype: datetime64[ns]# Y; ?- d+ q  m) a  O
    ; ?* s4 @, k- H8 r& |
    s.dt.ceil('1H')) K$ P- ]/ t  r. B' v/ ^3 t0 \4 ^
    Out[45]:
    ( a) |. X% M( G0   2020-01-01 21:00:00
    " Z- U$ z& f# a" G1   2020-01-01 22:00:00
    9 c) I; b* R5 k# U$ Z2   2020-01-01 23:00:00/ l9 a" [: K1 R6 j, X" b& M
    dtype: datetime64[ns]
    ! D1 _& m+ \& D1 p3 M' R- K, ~: o/ N
    - P, R7 y' w* E. [% L- O) k* ~; Bs.dt.floor('1H')& f3 K) W( Y2 O2 `# {
    Out[46]:
    + ^$ E# l5 P( v/ l; O0   2020-01-01 20:00:00
    ( d  S/ R% O( B' m, E% L1   2020-01-01 21:00:00- }  [5 `* S$ D; u! @+ Q& e+ `
    2   2020-01-01 22:00:00% c4 C; y" m: n- X) @
    dtype: datetime64[ns]
    9 h' Y" v5 }* M' |! X
    ( R: k6 j. c6 Q8 I2 w7 [1
      K0 l. _8 e: x20 ]  v  T$ ~) C
    3
      P& Y: J. |4 y0 Y$ u9 y4
    * H" V5 n* q$ u8 \) V. j5' _4 S  _; P2 u$ a$ p; d. i' n' ?
    6% c( I9 {3 Z; n9 A6 f7 E" g
    7
    . r- P( u/ ~9 W0 \6 v/ E; D8
    ' L0 ]9 v' V+ [& p$ A0 a2 X" N. S9' W9 W  c/ Z  c8 K4 A+ Z* K
    102 Q6 ]9 A( U7 _8 ]) U6 t( x
    11
    ' H! `7 N' F& [12
    . z& p! ~8 H# ?. |13( I" S9 f* ^6 f
    14
    * x: a  r$ f  J( p/ `/ ~7 ~15! @2 p: u/ c. x
    160 h9 Z6 {& k: f7 b1 R, _
    17( [: ], \& y5 Y7 r  Y. [* P
    18$ C# y; P9 [! c7 C' Y4 r; y9 [7 x
    194 f7 l$ k# S8 e. c2 Z
    20
    1 u, ^, N4 C* C; K* U( F21% K4 ?% y7 h# ?
    226 d* _8 P. N; q. a2 ^1 M
    23
    2 J6 N5 z  Q8 g# |; L+ O24- @+ S3 X2 R$ J" i- w
    25/ C# T% k7 Y8 i3 S! u
    26
    7 M4 {" p! D5 l( f- k7 a27! W; v9 R0 K7 _" c
    284 z( F  T' I2 q$ V- h
    29
    + p1 Y: k6 \3 }, P. H30
    - c4 {5 \% }$ ~. l: h* Z) X31$ g: u1 H' M- i5 l/ Z
    32  }$ P% i# P, U# W
    10.2.4 时间戳的切片与索引5 J! r5 S+ L( t% I$ I0 j. S+ N
      一般而言,时间戳序列作为索引使用。如果想要选出某个子时间戳序列,有两种方法:' K3 r7 b( p7 h1 T6 P8 p

    7 `  |) X2 b8 y% ?, o$ V利用dt对象和布尔条件联合使用7 _5 V  w' A3 b% l' N. S3 ?
    利用切片,后者常用于连续时间戳。5 m2 Q. }1 X8 g, A4 P5 U3 u
    s = pd.Series(np.random.randint(2,size=366), index=pd.date_range('2020-01-01','2020-12-31'))( t! V4 c- F3 a; r% _* X8 x2 @
    idx = pd.Series(s.index).dt; C& ?3 [3 y7 R2 G; L2 I
    s.head()& G; [0 A" N3 D$ \

      v0 p+ }9 E5 [0 I) {2020-01-01    0% q' S$ K& u' J. M" U
    2020-01-02    1
    ; ^! o6 R4 u: a1 H( m+ w9 }5 ^2020-01-03    14 o+ K% Y2 n  A4 \4 i
    2020-01-04    0" c* F6 ~! a" x, h: b- e
    2020-01-05    0- ?; V( U/ O; K) a
    Freq: D, dtype: int32
    9 j. U9 j2 p7 J' C1$ a. H! R1 K+ r2 z3 k9 J$ c6 @6 Q
    2
    ; m5 K+ ^; e/ s2 Z% d, x. w* O3: c7 }" G2 o6 `. a; i. ^; _
    4
    * m3 C1 U2 l2 r: W5$ A! [# S# m! Q/ [
    6% q# g& k# y) C, U. z6 M; p* D
    7
    - O: {1 \4 R" {+ j. f. @8
    % ^! A- Y7 n9 t# ?1 V- ]0 P9- U2 p5 S' y  e8 l% I
    10
    4 l0 H2 `/ S- F9 |8 n  Y8 nExample1:每月的第一天或者最后一天
    9 Q2 c+ U1 e; b  E. ]5 ~9 \, C7 k8 }- P
    s[(idx.is_month_start|idx.is_month_end).values].head() # 必须要写.values- d4 l% o& b7 d3 I
    Out[50]: 1 b# B# U) B6 S7 m1 z8 P
    2020-01-01    1; y  o9 K. R. v2 |  v
    2020-01-31    0
    $ O+ T' B7 v. g, Y9 ]/ R/ ^2020-02-01    1
    3 m8 q; H; m# m( @) ^0 ^  k2020-02-29    1$ m$ C9 H! D; ~) h2 s2 E7 L4 \
    2020-03-01    09 X, h' i" b2 @3 \
    dtype: int320 _! I7 n; {( M) M7 J( P" q
    1# D+ {# y/ G" @: [, R. W( W/ E. a
    2
    $ H9 i- \8 `6 }1 x3
    5 Y& C/ e: D) ]5 T  E7 ]" ?4
    ' d1 x3 B4 L5 Y8 p8 T5 |6 I5
    3 X2 x9 B) J8 [0 e/ L6
    ; E3 T$ j" Y5 u: m1 W% {% q7
    : p0 u, S& o3 }+ {  e5 L  j' i8/ }! w, G. |! Q* c0 @
    Example2:双休日
    & N9 f( E" H5 b0 z2 W. u' @  ]/ z0 B3 i( _  I) @7 }' q: l+ H
    s[idx.dayofweek.isin([5,6]).values].head()( s) V6 |% g1 X  c8 [) S
    Out[51]:
    6 @+ ]  E8 K% z2020-01-04    1$ O! v6 C0 x+ R7 _9 @8 c' c; z
    2020-01-05    0! g9 E  v/ Q/ m+ l& G
    2020-01-11    0
    8 d3 p$ ?- j) a  Y/ Q/ |: l. k) Z0 B2020-01-12    1
    * `! D3 z- ]. ?+ n2020-01-18    1/ r$ _- c; ^. I; t8 m# R5 z; o
    dtype: int32
    / @- ~- c3 i0 A4 j1 ~2 p1
    5 X! Z/ g# u; K( ~8 }8 H4 r) D3 {; P* U29 g1 f5 F6 @/ ?$ ]' x% m4 i
    3: r+ L# r) u% o; X
    46 K: b: K1 V9 A( z' H
    5
    $ @9 X+ }6 p6 c$ d# |6
    # k$ ?8 ]9 G. G. l/ V; A8 y7
    ' ]' b: i2 I" e$ S3 V8 y; o; ^8
      e' D0 [  ^. B( S! lExample3:取出单日值
    , N0 k$ m3 V1 L/ X! q9 C
    ( ^8 Y3 W( Z* C- Fs['2020-01-01']
    6 D. @/ B- F; d5 b  ?Out[52]: 1& n" X% a/ }& @8 n. `
    " q% X" z4 x! d4 a: a/ m& ], Y
    s['20200101'] # 自动转换标准格式
    : ]0 h$ C) z) Y$ n# b$ T. n  wOut[53]: 12 `6 j% {& p# H4 M$ J
    1; M4 J1 Y& g' \/ ^
    23 V( Q; Q( i) x" f
    39 ]2 A) t+ |9 W: i
    41 ^" X, t# A1 k! V9 {
    58 {2 D: `3 Q  C$ U( U" z! D
    Example4:取出七月1 i* }+ v" }3 A: y, g
    " I8 |6 h% y( A- X0 D( }
    s['2020-07'].head()
      S' N/ A, b4 {6 f# B7 O2 P$ i7 _Out[54]:
    5 s' i% x# w: d- `: l9 ]0 C2020-07-01    03 u" C. C  i) W4 ]5 J0 l
    2020-07-02    1
    & I$ E, d0 ?; c6 P# T2020-07-03    0; j+ r' k8 X3 k3 C! D7 h
    2020-07-04    0
    5 `/ j% y0 I3 G! ?) v2020-07-05    0
    ; E& @/ j& o1 F/ {- uFreq: D, dtype: int32
    9 Z: V* [& Z* ]; Q1
    3 b" H, y" h0 ~; x# r7 e4 \. I# F6 d2
    4 q- G3 c6 [* {. n7 R5 n3
    0 \. H& e( w( O1 N; Z3 S4
    2 a2 R8 e7 S7 ]; E5
    : [# e) e* V( P& ^+ K: a5 D3 D60 K; z/ E0 q% D( c: V, G
    7
    " F+ T! P6 `: t) T' x! L( l+ `( C9 }8
    . Z( c* O( y2 b0 eExample5:取出5月初至7月15日9 L* W" H1 F% y; c

    1 X6 X, P( U: m) V, Js['2020-05':'2020-7-15'].head()
    ' O6 g" F; `; R; E' i8 Q  `Out[55]:
    ( j- b6 E8 M- \/ e2020-05-01    0; W8 h) U: r3 H: J1 M" K5 v0 P
    2020-05-02    1
    * u9 V; O& h+ s# C$ p2020-05-03    0
    " ?# J( I; Q- p2020-05-04    10 r9 h, m# L: c1 s
    2020-05-05    1
    8 E! }! T7 U9 y( IFreq: D, dtype: int32/ \! E- A2 \% n1 I
    2 W: \9 O: {* g) ~3 q& [/ I* H
    s['2020-05':'2020-7-15'].tail()( J2 y! }% `3 p4 u+ j7 n1 e
    Out[56]:
    ( g8 y0 i, o- d2020-07-11    0
    8 P7 q2 y1 y5 q: |2 t9 _2020-07-12    0
    8 G, _4 j$ ^0 t2020-07-13    13 \0 A, j. R$ Z9 b3 C! P: V1 T( s
    2020-07-14    0
    ( V+ v( A4 X& G2020-07-15    1) K) k6 r8 a: d5 A! A& T
    Freq: D, dtype: int32* D# c" q- r" A& }2 K6 H0 T7 Z
    ; C5 }2 E. m/ h& D
    1
    6 P! [1 x5 J+ K2' c/ f+ t2 s" X: _: \3 |' f( V
    3" Q: ]; z- T& b# M* G7 B' A
    47 g, G2 `) N3 m5 f  d, p
    5
    * Y" R, W; F: F3 F$ I% r% ^6
    ; g; c3 D7 X( q4 L1 d7# i3 o, [# @  B7 @
    8
    2 \- D9 ]5 \9 t# k4 U. |9
    ; W  D( e8 T9 C; o5 u( K10/ W. P0 d# J# k& G1 N- J) X
    11
    , B4 a6 X! c2 Y  o# i$ i- U8 o8 i12& P/ s& t) c& @) ?& @
    13
      r7 `! a) Q  a% J3 f% c- F- u5 Z14
    1 s! f: {- u4 R; i# U+ C$ d15
    5 `( C$ I9 }8 q0 l, J16
    6 X" ^, l& P. f+ o5 b6 x1 [17
    - s5 h$ L- p9 p9 r: B; j2 F10.3 时间差
    & |# r* S9 M: a+ ^10.3.1 Timedelta的生成  v7 T7 I' ^" t2 c
    pandas.Timedelta(value=<object object>, unit=None, **kwargs)
    + V  m' H3 u1 Q1 L7 ]6 T7 v; [  unit:字符串格式,默认 ‘ns’。如果输入是整数,则表示输入的单位。
    0 D3 k1 P( L7 h; v5 Q3 ~: w7 h  `, G) G  可能的值有:
      [+ w5 A9 ?0 Q! E, b4 J0 U7 ~
    - x( |  j: f- O- f$ I" {7 v‘W’, ‘D’, ‘T’, ‘S’, ‘L’, ‘U’, or ‘N’' t1 e; i+ ^5 d* r; Z" e! ?. `
    ‘days’ or ‘day’
    # @, \# _; c* \$ ~9 I+ E‘hours’, ‘hour’, ‘hr’, or ‘h’+ B! v% L% I: w) I8 I2 B
    ‘minutes’, ‘minute’, ‘min’, or ‘m’
    - `) C; e0 W$ M% Q/ K- D‘seconds’, ‘second’, or ‘sec’
    5 o  t  ]" m% z- o* O3 s毫秒‘milliseconds’, ‘millisecond’, ‘millis’, or ‘milli’
    % n# r4 o; i; R4 Q: u微秒‘microseconds’, ‘microsecond’, ‘micros’, or ‘micro’* g6 T; K( m) v2 E
    纳秒 ‘nanoseconds’, ‘nanosecond’, ‘nanos’, ‘nano’, or ‘ns’.
    : q: X$ o1 k1 _4 |时间差可以理解为两个时间戳的差,可以通过pd.Timedelta来构造:
    8 a6 I( N" M# B! l6 z; o. cpd.Timestamp('20200102 08:00:00')-pd.Timestamp('20200101 07:35:00')
    5 v" \" z. B+ B# C# K0 ~Out[57]: Timedelta('1 days 00:25:00')
    - b- _% a6 c6 r2 v% w5 x( h. e2 ]; H: I7 Z3 _  O+ u- l! A
    pd.Timedelta(days=1, minutes=25) # 需要注意加s
    & @$ u* T, C% @, uOut[58]: Timedelta('1 days 00:25:00')
    . ?/ O. K- k/ w* [( P
    " u4 o" a/ ?. n, Y# bpd.Timedelta('1 days 25 minutes') # 字符串生成
    % n5 ~7 B6 c  a! O1 _; c) B) eOut[59]: Timedelta('1 days 00:25:00'): O/ e1 @9 k% L" A' u
    - q( L+ f; H" e! r
    pd.Timedelta(1, "d")' l9 ]; a, }) x% ^
    Out[58]: Timedelta('1 days 00:00:00')
    1 j, Q! L) Y* v7 K7 k) i1* K$ _! O( I; O) s$ D& i$ O
    2' \( Y) p! \3 W- A" R* S$ S& S! U
    32 s" R, [: }3 h' t' S
    4
      |0 C& J5 L; H( y0 S3 h+ R5* s3 S) H% _1 n
    60 f; A3 i; p% g9 X0 U' Z/ a
    7
    ! }+ x% i8 q* U" D+ m8 n89 I/ ?* q0 p5 d2 N. ]1 q! E
    9
    - E# x  ^5 Y7 t10  i+ Z3 f. x6 u1 o
    11! A- d- W% R2 \1 ]; ^
    生成时间差序列的主要方式是 pd.to_timedelta ,其类型为 timedelta64[ns] :* t4 z. \3 h  n* s( L
    s = pd.to_timedelta(df.Time_Record)+ _# ^' y1 T( E6 J

    4 S( W  z3 p9 z! K, P6 Ds.head()2 _* Y# O# U* X; y
    Out[61]: 0 V2 \1 A( |6 J+ a1 s3 u  N) r1 Q
    0   0 days 00:04:34- q9 C8 H, w! a5 f
    1   0 days 00:04:20, v& J: v. T0 ]) m" M
    2   0 days 00:05:22
    2 S  p, z) X2 j* b) s# ?9 k3   0 days 00:04:08
    ( ^! f" R' m# _, r8 {/ T: W4   0 days 00:05:22
    * O- W5 C# E4 D2 a% e& S4 LName: Time_Record, dtype: timedelta64[ns]& ^9 t' X2 ~, t: L
    1
    # h! S0 @& R# T+ H2
    ' q6 l0 S* H4 k, D8 ^3- k5 Y2 ?, D* e
    45 L6 x* _) V9 V
    5' A! H  {) c8 k9 f) H& j6 s
    6
    4 N6 {( z5 y, T7
    8 T" Z/ L: x. ~+ _8# m: u4 O9 _& [
    9
    - s/ }7 p; B# D  D( }; O. J105 p& [4 q4 e$ K% \- S+ }" e8 u
    与date_range一样,时间差序列也可以用timedelta_range来生成,它们两者具有一致的参数:
    ) o9 {* |! a' k4 ?6 ^pd.timedelta_range('0s', '1000s', freq='6min')0 o! ~) X% d' ~0 I
    Out[62]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:06:00', '0 days 00:12:00'], dtype='timedelta64[ns]', freq='6T')
      S, i0 y9 G" h5 P+ K6 R
    ) a% |* v6 t1 Gpd.timedelta_range('0s', '1000s', periods=3)7 a& O) u7 j! H: J  b9 S# c( v' e' o
    Out[63]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:08:20', '0 days 00:16:40'], dtype='timedelta64[ns]', freq=None)0 x# A1 O6 B! G% s9 [0 z  ]
    1
    7 s& j5 H4 ?6 l0 _; o- u25 V8 q, L2 U; D& C0 u) B; s
    3
    " _2 [- @7 B+ h0 ~: S4
    3 y3 i9 o8 u5 G! B2 p; b51 B% r- @  Q/ O9 b8 i
    对于Timedelta序列,同样也定义了dt对象,上面主要定义了的属性包括days, seconds, mircroseconds(毫秒), nanoseconds(纳秒),它们分别返回了对应的时间差特征。需要注意的是,这里的seconds不是指单纯的秒,而是对天数取余后剩余的秒数:
    " t& Y2 |; @- c' d8 p4 M. l9 ws.dt.seconds.head()
    : K, {( z) Q( ^( u5 qOut[64]: $ ~2 v  g4 s4 d7 ?
    0    274
    * ^( U8 r* ~% K5 j: W+ v8 E$ i3 y1    2605 ?) Z1 @. W% ~3 I# V8 i1 r1 u
    2    322# m7 {" u6 b6 m3 c% J
    3    248$ e+ D. N& q/ B' F9 H
    4    3227 A0 K6 F9 e) U1 S: C( h
    Name: Time_Record, dtype: int64
      e: G. V& @: _( b# R1/ B$ h3 Q# S: g4 L  P- a- s
    27 ~$ r% r: G7 O) Y% @( m
    3) F) o* K2 c3 H- [9 u5 M+ D0 T
    4$ N" S8 T& a/ q* A
    57 r% F' r6 F# {! `7 d6 A! P
    6
    ; y& j% g% ^! s" O74 _+ w, t/ B+ Y6 l" s1 ?# A! F  T
    8
    ' E( y: M' _: F6 Z- E1 {如果不想对天数取余而直接对应秒数,可以使用total_seconds
    " N; n, D' G( _$ O  l! n* ^
    ) X$ ]8 Q' n  r( Ss.dt.total_seconds().head()3 u6 l% }. n# G8 q- v
    Out[65]: 1 R% x3 J' a% i/ y; U4 |  ?
    0    274.07 J6 t5 O$ U% j/ T: J; i/ l8 P2 U& P
    1    260.0
    ' k( O4 i, ]8 [0 \; h6 l2    322.0
    % O4 X5 C5 F3 s$ Q7 P# U/ u3 h3    248.0" |1 T& }% X2 D: L% q+ w* P) S
    4    322.0. l! R6 s2 A' G* u
    Name: Time_Record, dtype: float64, n8 }0 w, k% U
    1
    6 Z2 n, M$ ]% L3 R2+ z; j. U- Z* \. Z5 E! }( r& T
    3
    0 P4 N" g/ Q% v+ E0 o! @. r3 [) I- @4
    & {) T. b8 m8 A8 |! I! F; U5
    1 f9 X1 R8 H/ C7 r+ d* F! }+ n- Z6
    , V3 m2 H( y& o% {8 V9 j7
    + D( P5 R, N# s, x& ]( M& N7 V86 _; V) }" _- S1 S4 M& D: q
    与时间戳序列类似,取整函数也是可以在dt对象上使用的:
    2 i, T8 i( B3 \9 w1 G
    ' s& ]! `9 c: d8 n' kpd.to_timedelta(df.Time_Record).dt.round('min').head()
    $ ]# e0 Q; E% _0 ?; z) T' _4 UOut[66]: " O3 ^; W, r9 \9 S  P
    0   0 days 00:05:007 Y* ?/ {' r2 Y0 A/ [$ E
    1   0 days 00:04:000 i! g3 O+ t$ F6 p
    2   0 days 00:05:00
    2 F: S# Q; q9 x. y. p! X3   0 days 00:04:00
    0 j$ a+ n5 \6 v  y1 U3 k4   0 days 00:05:00
    7 B3 H. P( o: u5 IName: Time_Record, dtype: timedelta64[ns]2 Z4 F6 o* ]+ P9 u
    12 y& W6 v; u0 M7 f: r
    2
    % h! {: d) o: _, |& \3
    ; h4 ?& ^+ m" a) g5 O: Z  p4: A/ V* n2 H$ l; Y
    5
    $ a% l; J& W' p, p/ P+ O6
    4 d5 G! f& ]+ r( K7 F0 e7* a" ^0 Q) }3 b
    83 f7 e, K: v) V* T# s, j# L" h
    10.2.2 Timedelta的运算
    / @' t; j; n0 F0 e1 i  w单个时间差的常用运算,有三类:与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算:
    8 w0 b$ z" V  y  I9 v3 K4 }- htd1 = pd.Timedelta(days=1)+ o" {- j0 X( n( d: Q& F: g
    td2 = pd.Timedelta(days=3)
    8 e; R% I- X! C, m+ ^) rts = pd.Timestamp('20200101')
    * E$ A, ], L# t2 o: L
    ( w- X5 P" w) n4 B; |: v" E/ w6 Ptd1 * 2
    1 `2 A. A6 W. `0 j2 M2 {6 @; n8 ~, a7 AOut[70]: Timedelta('2 days 00:00:00')
    9 K6 a8 P$ G) s2 f3 A" t
    3 J2 M% e3 W* u% l  D; B1 X! a: Jtd2 - td17 b7 ]3 R7 N6 B
    Out[71]: Timedelta('2 days 00:00:00')
    2 b/ B* |$ V9 C5 G2 n7 j% Y/ y* _
    - F( F3 a$ Z% j0 q" ots + td16 r  Q' `3 r. N- l3 q) b6 `, F) c" m
    Out[72]: Timestamp('2020-01-02 00:00:00')+ ?  `, I) x9 C3 j' p

    : T7 l0 x0 g( R0 r3 o% tts - td1
    ) O$ r/ R: }, L* W% w$ O2 iOut[73]: Timestamp('2019-12-31 00:00:00')
    - r' Z; z+ [# p% t# t8 M1
    - D% r, W0 M; R) U; t& G1 Q23 j2 Z, G" U+ Q
    3/ O1 e: O/ N) D2 R9 U* X9 N3 V6 x
    4/ p$ D$ Y' Y( T4 I" U
    5+ S" o5 E# z: B: H7 c
    6  Q5 b7 A. @* b$ i" \* Y. ?: C
    7+ Z+ g4 q. N  d( ?7 F# z
    87 M0 `) {3 i* b) j  \' ~" Y3 T7 h
    9  D4 h: w! \& |. @% ]1 `0 o
    10. }" K4 q& X9 T' s& t. h; K( e
    11% |4 a4 T* ~2 j8 k
    12& {) L( X# J% Q
    137 J' p' `7 D. u8 p/ l. u4 y% |
    14( E! q6 U/ r' F/ R6 u' F4 [( |- v3 Z
    15( H5 O% N9 @! ^% f- [( }5 r
    时间差的序列的运算,和上面方法相同:) b. l" G' J8 b0 }' ~
    td1 = pd.timedelta_range(start='1 days', periods=5)( w# z) u, ?$ Q( k: d* S9 F
    td2 = pd.timedelta_range(start='12 hours',0 t- ]# i- p" [) h. R5 O9 @# E
                             freq='2H',
    4 Q* j- f3 X. e9 i8 U! y; q                         periods=5)1 _' s9 D$ C: M4 _4 f( W
    ts = pd.date_range('20200101', '20200105')$ K$ V# d4 ?" V1 I2 j3 \
    td1,td2,ts
    ' X/ m! [( K& f+ @( b0 b+ k
    ( U; h) N- ~) @TimedeltaIndex(['1 days', '2 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq='D')
    * i& v% G  A  \. y  F1 i. v* @( J) {  F7 J8 |TimedeltaIndex(['0 days 12:00:00', '0 days 14:00:00', '0 days 16:00:00',# `& E8 f. a0 T/ d* P
                    '0 days 18:00:00', '0 days 20:00:00'], dtype='timedelta64[ns]', freq='2H')
      b+ R- v1 T4 ^: ?8 u, Q2 tDatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',% l# h- w- |2 ]( ^# Z7 v% d
                   '2020-01-05'],
    / R* _& Z4 U; [5 @# [% ^              dtype='datetime64[ns]', freq='D')
    * }4 t7 g7 H& G: f. H1
    4 M* k4 w9 w4 B8 Y  h2
    2 v1 d' L0 d; _39 ~. G5 V/ Z6 p, `4 j
    4# G$ `' F% T$ J) y( q: ?& F4 ~
    53 o9 ~. t' K" X# I* ]; t6 ?. c
    6% U+ j2 x0 g2 q4 i9 \3 G! g- H
    70 W' `; c# z. s" P
    8) ?8 F2 o8 M+ Y' D! V1 Z% }- Q
    9" N# C9 O$ _/ v* o# W! a
    10
    : a1 u- m) l9 ]( M11- ^* _) b5 B' F4 d- \' i3 i
    12: c4 i; `. Q7 h& _# i0 q% x
    13" R( D) @$ i; c7 l$ p1 y
    td1 * 56 K* Z# h* b0 g, l2 C
    Out[77]: TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D')
    ) i6 ^& C/ L) k5 b" Y  M0 p
    2 x8 @. \/ O1 Y7 I, ctd1 * pd.Series(list(range(5))) # 逐个相乘
    , n9 P/ E$ d! \- A" m+ j0 pOut[78]: + k/ U8 P2 ~  ?) W* `
    0    0 days1 {+ U' X& w: g
    1    2 days. H5 T/ ]7 Z4 c. \+ x" m# s! R6 j6 h
    2    6 days) N; E  G  U8 Q
    3   12 days
    4 j. w* l/ H7 p% c7 n' b& }4   20 days5 B  H/ v. F! R
    dtype: timedelta64[ns]
    7 h2 ~8 d% M9 F8 @. W
    " D* s9 m' I. L0 _td1 - td2
    4 G5 ]8 a& \1 s% iOut[79]:
    ) ~( A6 [+ T) j) F% P% RTimedeltaIndex(['0 days 12:00:00', '1 days 10:00:00', '2 days 08:00:00',) y: ?. t7 i4 L" N
                    '3 days 06:00:00', '4 days 04:00:00'],5 r$ ]8 b+ L* [2 F  ~, y1 a
                   dtype='timedelta64[ns]', freq=None)9 S7 x; g7 `3 e1 o3 S

    , S" V  m: y7 A" v, ptd1 + pd.Timestamp('20200101')" I6 Z; Y9 n6 R: X. Z1 e" X
    Out[80]:
    ' H1 q$ m# r, z2 b) P* U1 N) FDatetimeIndex(['2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05',
    7 @+ c* f' ]+ ^1 E5 l9 `! ~               '2020-01-06'],dtype='datetime64[ns]', freq='D')
    4 u, D3 f* S3 T+ k  {6 ?; ^, r3 _# c0 L$ P  z1 |! U
    td1 + ts # 逐个相加
    % b. j1 L. y0 U+ K2 B. ]. ~! wOut[81]:
    , [9 T5 }. L0 w! @" u) u+ NDatetimeIndex(['2020-01-02', '2020-01-04', '2020-01-06', '2020-01-08',  Y5 W1 ]9 d- N. m: x1 c
                   '2020-01-10']," |6 n4 i5 u0 R2 Z$ f% X4 }  L
                  dtype='datetime64[ns]', freq=None)
    * S: [  o  f* X0 ^) n+ i# P0 M* d0 D5 s2 D0 s
    1
    6 t" X8 y% U- f3 x28 _6 \: h  z9 s& Q
    3" z% ]) e2 M+ p, w! g1 }7 i
    49 @) ~, K8 n$ V7 |3 k
    5% ?' {* t  D  D3 B7 G
    6
    2 G- x$ ?" K1 h5 r7
    5 n7 @: v. _5 X, j/ V5 X81 d% I( u' N$ |/ Y! v5 \9 f0 {, y" c
    9
    0 M$ i( |5 _! H! f- F10/ K3 u+ J# w8 B% o1 I+ P  n
    111 L9 @* @% D& d! J2 H
    12
    1 H2 L6 \) f! B13/ \. k, T  c3 s( J5 P
    14
      V& q' a6 x+ R- H* f, G1 D% X( V15
    8 K- F0 k/ B$ w; H+ B3 g) ~16& ]6 o8 l3 n1 e+ |2 J6 s
    17
    ; D* V4 G' e5 d. U18$ Q" {1 ], \8 n. G6 J$ k" d3 M5 E7 @
    191 F; x# i4 \8 o; q' M1 }, j
    20
    $ k, }# W7 g6 R3 B+ M$ U0 J, E21  W/ v4 y+ {1 e% f1 u
    22
    5 b% o. p# o0 I# S$ `; U6 ^23: f, {& V3 Z' l: }& k* {/ s4 Q; H" g
    24
    6 ^8 J- A/ U! ~( a* C9 y  o% f25
    # Q( J+ P) R% z& G  k; d# r26
    ) E/ H' J% w$ ]/ R0 \/ h274 l6 R. n! M. Z5 S3 p- S& y8 Z( N/ }
    281 u( K6 Y% F. {: @+ |& y: d/ {. V
    10.4 日期偏置
    . [4 z" \' h7 z) L' N10.4.1 Offset对象
    * S! {1 T2 r. K7 k9 ]4 T  日期偏置是一种和日历相关的特殊时间差,例如回到第一节中的两个问题:如何求2020年9月第一个周一的日期,以及如何求2020年9月7日后的第30个工作日是哪一天。! {  I8 t8 @5 X9 x+ K: O
    3 i% G0 Y* |# r) j( Q5 ]
    DateOffset 类有10个属性,假设s=pd.offsets.WeekOfMonth(week=0,weekday=0),则:
    4 Z7 t" r: x; P' s+ e
    3 O. I2 ?1 y0 C. E5 D  ?s.base:<WeekOfMonth: week=0, weekday=0>,返回 n=1 且所有其他属性一样的副本) K7 w: F. k4 ~3 p9 }( C! f) q
    s.kwds:{‘week’: 0, ‘weekday’: 0}
    % b. s2 l( {2 [/ ^7 _$ F3 W1 ms.wek/s.weekday:顾名思义$ o8 Y6 h3 j* n8 ~, T9 H' X
    有14个方法,包括:
    & @: k9 L9 S& L; D% x' l6 I8 i1 B& h% {: i' H
    DateOffset.is_month_start、DateOffset.is_month_end、DateOffset.is_quarter_start、DateOffset.is_quarter_end、DateOffset.is_year_start、DateOffset.is_year_end等等。: ^4 q( p3 Q# O0 J
    pandas.tseries.offsets.WeekOfMonth(week,weekday):描述每月的日期,例如“每月第二周的星期二”。) ?9 @# I" o% a7 L6 m9 B
    ' X% E- Y  z+ ^% w3 _1 d0 ~( ]
    有两个参数:
    8 E% y  @; A. ?6 X* F: f  a; K9 Qweek:整型,表示一个月的第几周。例如 0 是一个月的第 1 周,1 是第 2 周,以此类推。
    , f2 F2 E) k4 l( a0 b" c. Cweekday:整型,取值为[0,1,…6],表示周一到周日,默认取值为0(星期一)/ p9 t8 D: F+ @( a8 w) z* Y
    pandas.tseries.offsets.BusinessDay(n):相当于pd.offsets.BDay(n),DateOffset 子类,表示可能的 n 个工作日。
    , \% X& G9 u; L0 s  f" F- ~) f$ W8 @) I) U
    pd.Timestamp('20200831') + pd.offsets.WeekOfMonth(week=0,weekday=0). M: ^. m- b; D7 u
    Out[82]: Timestamp('2020-09-07 00:00:00')
    , F4 h; c" O  L( C* a* I9 r0 l' ?: U8 C* u0 V' N4 ~, b" E9 ?
    pd.Timestamp('20200907') + pd.offsets.BDay(30)3 r) ?; b* b0 a& ?# o: B
    Out[83]: Timestamp('2020-10-19 00:00:00')
    - \- X5 N& E" Q; G, n1
    # L. o8 i% Z2 x' p2 A3 H4 x/ Y2' q/ t* l4 k7 N+ V" N
    3
    : L5 i! A; V/ M) G* }1 I: ~" H( ]/ X4
    ( e% _0 J4 {+ @4 D) _5' `; ^1 p) m! O/ c: y. Y6 [. S, M( r$ N
      从上面的例子中可以看到,Offset对象在pd.offsets中被定义。当使用+时获取离其最近的下一个日期,当使用-时获取离其最近的上一个日期:
    1 z% n/ W. v1 Y- v. z& |( B: {) h+ f0 _7 N% F% O1 E: j
    pd.Timestamp('20200831') - pd.offsets.WeekOfMonth(week=0,weekday=0), r, ]  b" M" V. y$ m
    Out[84]: Timestamp('2020-08-03 00:00:00')% v* a. I9 E0 g: H

    & [' V0 n9 m% V" a& i, y& D  M6 ~pd.Timestamp('20200907') - pd.offsets.BDay(30)9 y2 o! w8 D4 ^; e  G6 l! i, q
    Out[85]: Timestamp('2020-07-27 00:00:00')
    : z5 C2 O; [3 O  s
    ' g6 I5 M3 u; `  Ypd.Timestamp('20200907') + pd.offsets.MonthEnd()
    . @& d# t- i% ~" C) Q. H$ gOut[86]: Timestamp('2020-09-30 00:00:00')
    3 w5 N: n* W: m1 @% y! V2 {9 z$ P1/ C9 h; ]5 D2 |/ L
    2
    & j, J' k+ ~3 M8 f7 O" n& x3
    7 K  v+ w! _3 r4  b' J8 c' h* I* h* C& f/ `' \
    56 {8 ]; K+ a/ V( o
    6
    , b& a6 j$ t: u7 L7 i4 K; _! P# Q74 y6 z* [; J. n) _+ _9 h
    80 V# P0 k# Z# T* b- b. ]
      常用的日期偏置如下可以查阅这里的DateOffset 文档描述。在文档罗列的Offset中,需要介绍一个特殊的Offset对象CDay。CDay 或 CustomBusinessDay 类提供了一个参数化的 BusinessDay 类,可用于创建自定义的工作日日历,该日历说明当地假期和当地周末惯例。; Y9 g% C1 b4 S9 [% m- r
      其中的holidays, weekmask参数能够分别对自定义的日期和星期进行过滤,前者传入了需要过滤的日期列表,后者传入的是三个字母的星期缩写构成的星期字符串,其作用是只保留字符串中出现的星期:$ |) f( |; `% B$ Z4 M% b
    1 Y7 W* L! h. E. T! |
    my_filter = pd.offsets.CDay(n=1,weekmask='Wed Fri',holidays=['20200109'])5 L' ~/ U8 B. W( a3 b& D2 A4 V
    dr = pd.date_range('20200108', '20200111')
    3 ?8 G. V( n( }2 `: v2 R3 p  v/ h- @" a
    dr.to_series().dt.dayofweek$ C- w% @7 E. N) m4 M+ O- L, _
    Out[89]:
    $ H$ }0 R. T: K' ^6 h* ]) C9 l2 k7 J: H2020-01-08    2
    0 T, G! S9 a1 ]2020-01-09    3
    / c: b; l: S4 K+ s) r  j- x2020-01-10    4
    - i$ B# d( v6 S( `3 ]2020-01-11    56 t2 k5 F" c; Z4 T* j
    Freq: D, dtype: int64) ]/ B$ l! m. s( h% Y+ h1 G

    - ~! Q# ]. w  q: w[i + my_filter for i in dr]
    . B! U$ L, V3 h1 q! D4 L7 uOut[90]: , q4 z/ ^# G7 [& Y( F7 i1 m
    [Timestamp('2020-01-10 00:00:00'),+ g! ~8 Y2 S; q6 E2 O
    Timestamp('2020-01-10 00:00:00'),3 j; Y; C8 V4 c6 }' u
    Timestamp('2020-01-15 00:00:00'),
    . ?# o0 A9 e9 Y' b Timestamp('2020-01-15 00:00:00')]1 b! O2 o" e9 E' _0 b+ j# j9 P
    2 `' b  I) k; _/ g/ c" F" Z
    1+ D! J! ~2 a0 J: B6 }
    23 P1 }0 k8 D2 L! C' q4 k
    3
    ) b" S# L! [: ~9 l' S4  d1 Q% B/ G; y4 r! ^
    5- r9 {; E- Z7 M
    6# d- j' q5 H. r7 S% P9 H
    7' J/ f+ ~& I7 y0 L, F2 {
    8
    / y4 s1 r0 i( P7 q& w2 G9: Z- {! i. T) p& x# U7 r4 Q* f. W
    102 ]) Z  T; _2 E  r' c6 M+ ?) P7 ^
    113 W$ Z1 S  p. S) y% O$ g( K
    12
    + U" z1 r) _' g8 o9 x1 c. n13( k$ ?- _% @$ U2 r' o* Y3 m
    14
    $ ?3 e& O8 v6 T5 q% T+ o: N0 D15  y& o6 f! Q; h! G! r( W
    16
    5 Q5 d$ L; {' Z  m" g& K17
    # c9 W1 J1 Z+ x& a  上面的例子中,n表示增加一天CDay,dr中的第一天为20200108,但由于下一天20200109被排除了,并且20200110是合法的周五,因此转为20200110,其他后面的日期处理类似。
    . j# A/ I* u- a3 d' v: l0 u- f" u+ A1 Y6 A; |+ M
    【CAUTION】不要使用部分Offset0 W8 `9 q* x5 i6 o+ M
    在当前版本下由于一些 bug ,不要使用 Day 级别以下的 Offset 对象,比如 Hour, Second 等,请使用对应的 Timedelta 对象来代替。4 ]% @% S2 X) S: F$ H/ e2 h
    3 Y- a6 g, l# x# @- O  T
    10.4.2 偏置字符串6 f" j# a( c, \+ @9 z" L
      前面提到了关于date_range的freq取值可用Offset对象,同时在pandas中几乎每一个Offset对象绑定了日期偏置字符串(frequencies strings/offset aliases),可以指定Offset对应的字符串来替代使用。下面举一些常见的例子。$ T- h# U0 k$ T
    : _7 m! ?2 X: y6 w+ C
      Offset aliases:pd.date_range函数中的freq参数,为常见时间序列频率提供了许多字符串别名。 也称为偏移别名Offset aliases。偏移别名列表点此参看(大概27个)。
    9 {- W! {5 l5 m) E6 a
      E) Z- s0 i6 t: t. S0 e7 K+ {) epd.date_range('20200101','20200331', freq='MS') # 月初
    + s* _+ |" t% X) e3 IOut[91]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')
    ' c2 \( d0 |; q9 j! F( a* l  {0 u+ l( A7 ^3 g
    pd.date_range('20200101','20200331', freq='M') # 月末
    ( \* K  x! r$ b. GOut[92]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')
    1 b4 F6 }1 w; e1 E4 u6 y) J- p
    2 m: t7 G  R. r* epd.date_range('20200101','20200110', freq='B') # 工作日
    - K, r6 [0 _8 R! U# g8 p* vOut[93]: 2 [6 d% h* q/ |8 {+ d$ f6 w
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
    4 S& i% k/ B  w; k* b8 e               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],3 @4 D5 W" s, ~- S% I' z8 i- Q
                  dtype='datetime64[ns]', freq='B')6 t% v/ S4 M! S  u3 B7 G  }

    $ b: M0 X5 C5 m' O- a1 gpd.date_range('20200101','20200201', freq='W-MON') # 周一( Z& i5 A$ k" x( N9 i9 S
    Out[94]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='W-MON')
    0 A4 F, f* `5 ~) L7 B# ^* N
    - h  Q- p) T/ {3 O' j- ~# e/ Apd.date_range('20200101','20200201',: z, a" q9 Z9 N5 V# H/ t
                  freq='WOM-1MON') # 每月第一个周一  u: }- p0 [" e7 d4 K5 I6 ~1 F

    $ X# @8 v" Z& M3 H2 rOut[95]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')* y- k, w& ?6 }; m0 p( c7 ~8 j

    % C, o( ^2 `" h' W- @1
    0 J+ n+ i. ^: `" r# k- S( I2
    - p* ]) s; Z% s$ _2 b3% Y% w6 `8 h3 H. q7 _/ }
    4, D- K: X7 l5 Q8 g! O
    5' n6 W; W+ p1 K5 |- F8 H' Y
    6
    , m# ?% m# R7 T7* q7 ^( r# R% E& x0 k( @
    8* J7 T) r, L3 s  W3 h; E( u
    9% D/ }! W1 H# V! |
    10
    ( t, ^# P3 {' t% L5 d- u( i11: z2 \& ]  a4 [8 }! {
    12
    7 W/ F# |7 [$ _* Y7 E+ V13
    0 P2 Q4 X; Y  a7 }5 R0 ^" S  E14& \! ~* w# ]( V  x+ N& q
    15
    " A+ G3 V7 z! i/ d: V162 ?2 }* `/ D- M0 [
    17
    8 V0 h. M- Q0 [. j+ b5 I18
    4 h& z1 Q4 @/ M19
    7 O7 }$ _7 ~) W# D0 c3 c上面的这些字符串,等价于使用如下的 Offset 对象:
    5 Z8 _4 Y( g' F
    0 `* ~  S$ l4 u( I; t$ jpd.date_range('20200101','20200331',5 T4 y* F5 `  B& h
                  freq=pd.offsets.MonthBegin())% P3 O4 z- m2 Y2 D% L: P9 @
    % Z2 h6 Y- U& S$ B+ y
    Out[96]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')
    8 l5 d- [5 p1 L2 T5 q/ s: ~1 J' T: N, c$ |
    pd.date_range('20200101','20200331',7 s, f+ e/ I3 w6 m+ o
                  freq=pd.offsets.MonthEnd())
    # q4 W0 X: k' ]
    ' Y+ s6 E  W8 J2 IOut[97]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')
    0 `# y3 V1 i  n% P! Y1 `9 D, z
    " g4 v' h) S  t9 d4 _pd.date_range('20200101','20200110', freq=pd.offsets.BDay())
    # _) Z" T8 u' m, A. O- C1 LOut[98]:
    % Q' F7 A4 U' Q6 ADatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
    / D5 a; G' }5 Z$ c. x9 P               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],7 x& H# |: k& W4 K
                  dtype='datetime64[ns]', freq='B')7 |+ i6 \/ y5 _. W& P. m" O8 e
    & r" M, w9 ~# ^! h
    pd.date_range('20200101','20200201',
    $ \9 C. A: W7 ^              freq=pd.offsets.CDay(weekmask='Mon'))
      g! M( I, J* p" g: X: m$ ^8 n. c* o" Y* W/ c& {% ?
    Out[99]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='C'). J+ \4 T0 G5 ]0 m* p) r5 B! @
    2 Z8 p/ C; Z2 I- `- A2 O
    pd.date_range('20200101','20200201',0 u2 L0 i. [- l+ Y  J) Q% u
                  freq=pd.offsets.WeekOfMonth(week=0,weekday=0))
    2 k6 n- A; g$ R$ T" i* R6 L" \7 `: S5 m+ P' `  [* [9 e2 p
    Out[100]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')
    3 P1 @1 _( ^- d1 y5 n8 q' s! C' ~
    1% U& d* O" m9 s+ ]" s  o8 W: x
    25 d  m" ^8 W% `) h* M& Z
    3/ Y  X; d$ y  i4 F& h# I
    4$ K* w/ M0 C; y7 b, |: }
    5, @3 E, ?" w$ {) o+ k3 D
    64 _" a+ L2 {( ^; d( {' X5 c) k3 N
    7
    % T2 v# O! Y! v; Q: n8  T. U; d2 h- S. W
    9
    % Q1 d( D6 Y6 Q3 Z! X# u3 ?10) z' I! M0 `  C/ `( i. @0 J
    11
    : V4 y' h9 M- |" h! [2 Q# j6 b12' C$ \- n. V5 H- U- \
    13- P2 K; b2 l! B) t. n8 Y6 ]
    14/ |' l$ `1 B- G) [8 F0 r/ S
    15' o) B3 t$ I( V7 N+ N
    166 y2 d/ E6 p3 k
    17
    " X& q4 Z6 P4 L# w! U4 v18
    ' e/ e$ w+ u( O+ T$ W4 O19
    1 F$ |4 N+ v! g! S20
    % Y" ^) a# ^  S- l7 x21
    ; `  L* D) D$ r9 J9 ^22
    1 [  c  ?: n! e23; \  A; c% l/ ~7 t+ R. k3 V; H
    24' R$ D$ u) v! s
    25
      A8 S+ u; L3 Z* G【CAUTION】关于时区问题的说明
    7 x' n7 O7 D2 M* W  各类时间对象的开发,除了使用python内置的datetime模块,pandas还利用了dateutil模块,很大一部分是为了处理时区问题。总所周知,我国是没有夏令时调整时间一说的,但有些国家会有这种做法,导致了相对而言一天里可能会有23/24/25个小时,也就是relativedelta,这使得Offset对象和Timedelta对象有了对同一问题处理产生不同结果的现象,其中的规则也较为复杂,官方文档的写法存在部分描述错误,并且难以对描述做出统一修正,因为牵涉到了Offset相关的很多组件。因此,本教程完全不考虑时区处理,如果对时区处理的时间偏置有兴趣了解讨论,可以联系我或者参见这里的讨论。
    7 \0 w5 P2 Y) L  [/ Q4 {, L/ }4 m* U7 J! ?
    10.5、时序中的滑窗与分组
    . s- W4 \2 ?9 _3 w10.5.1 滑动窗口" f4 M8 |+ C. E& c! `* D
      所谓时序的滑窗函数,即把滑动窗口windows用freq关键词代替,下面给出一个具体的应用案例:在股票市场中有一个指标为BOLL指标,它由中轨线、上轨线、下轨线这三根线构成,具体的计算方法分别是N日均值线、N日均值加两倍N日标准差线、N日均值减两倍N日标准差线。利用rolling对象计算N=30的BOLL指标可以如下写出:% V& `) V8 g5 f1 h

    5 l5 w$ j. O, H# @import matplotlib.pyplot as plt
    ! l, p# @9 W: m( i9 Z& W# Zidx = pd.date_range('20200101', '20201231', freq='B')+ u' G; b9 T7 a! f
    np.random.seed(2020)
    5 Z3 p; e$ \( q' w; g* o. C! d0 e
    ) e  @( ]+ U8 g6 gdata = np.random.randint(-1,2,len(idx)).cumsum() # 随机游动构造模拟序列,cumsum表示累加0 H# Y5 H6 f) M) l3 v8 W
    s = pd.Series(data,index=idx)
    " e2 ^3 f  _+ K9 C) \* W8 b( @s.head()4 H2 e2 J' l* C# Q$ Z" H; A" n6 u
    Out[106]: 7 i1 O( ]3 Z0 F0 p. n, I! t! O
    2020-01-01   -1( e" Y6 Z6 K6 B) W2 Y8 a. g; C( P
    2020-01-02   -2. k6 |  S# A2 T! U- Q) o
    2020-01-03   -1
    4 _$ u) _. n4 e. M2020-01-06   -1% n3 W3 j; W+ @0 R7 K
    2020-01-07   -26 F3 C" R! c9 H# F" g0 i* X: f
    Freq: B, dtype: int32, d4 r  @' \& ~# z
    r = s.rolling('30D')# rolling可以指定freq或者offset对象
    # V  G6 d- Y1 \, m3 U9 z8 N* z. D# t; p
    plt.plot(s) # 蓝色线7 Y! _6 P2 f# Z$ ]" d( m
    Out[108]: [<matplotlib.lines.Line2D at 0x2116d887eb0>]% l. t$ c3 Y" n% J, ~2 ?
    plt.title('BOLL LINES')8 V( G6 n* @* i7 k: [
    Out[109]: Text(0.5, 1.0, 'BOLL LINES')  P/ v  D% q5 D' ~% E" `/ E( _9 v

    ! y5 }: C: R7 Q# cplt.plot(r.mean()) #橙色线8 ^& A! w6 u, }# {( r
    Out[110]: [<matplotlib.lines.Line2D at 0x2116d8eeb80>]
    7 O, Q+ E/ ?5 ~; x' J1 m; V! \/ s. p  r; f" {
    plt.plot(r.mean()+r.std()*2) # 绿色线/ \6 q" i5 I( D' |- a' f
    Out[111]: [<matplotlib.lines.Line2D at 0x2116d87efa0>]
    % {( e% I1 A5 l! k  K: I( Z) J! y9 c3 _: b
    plt.plot(r.mean()-r.std()*2) # 红色线
    & n7 E7 Z/ c' F% s% E5 j* rOut[112]: [<matplotlib.lines.Line2D at 0x2116d90d2e0>]) K5 c. y/ \; D4 a

    5 r* _  n  r; {+ l( O1
    1 Q# l& C: [# x+ Y  {$ \6 l29 V! c: j- m" q) j
    3) q3 Z0 u5 a3 l% m2 L% G$ r+ J2 _
    4
    . q$ @2 {* a8 t5 d2 w0 A; \5) \3 F: ~1 G2 y, k6 R! k
    68 u5 R) R% \, u- B: O
    7
    ! }* \% z$ O9 }8# S8 d' u6 `1 R
    9/ Y5 ^1 N& z& d
    105 b& q( I. j+ a# d) [: k
    11
    9 |/ X  r( c' i6 r  _* U12' n" t% x! ?/ u, x
    13
    / L% C# s7 \+ r7 |' X9 T; O/ m14
    % A/ n! o/ G4 f15, e5 i  p9 J8 Y4 I
    16. ~9 T, K' x4 T0 k  F' }+ p! H
    17
    5 ?6 n( e3 n' ^* C4 o9 w- n9 U18
    + g- F; J; C6 V. V: i; T  R19
    & _7 a8 r# ^  g206 L9 t' a* P& j3 N
    21/ C. m( _. h4 g5 i
    222 x& _$ U" u+ j+ d; S) i
    23) s# d. E4 I5 x
    24
    1 ^7 S4 V" r. L1 O5 [2 z25
    . h$ `. @8 z3 ]/ l6 D269 o, L+ n6 w$ X- q4 x
    273 O4 c- G, ^, Q
    28, b% x5 _3 ?% g8 N( m
    29
    ' P  G" X9 \$ J5 E1 Z* {! @1 \. g+ d# h8 R6 M- f# A% K
       这里需要注意的是,pandas没有实现非固定采样频率的时间序列滑窗,及此时无法通过传入freq字段来得到滑窗结果。例如统计近7个工作日的交易总额。此时可以通过传入多个函数的组合来实现此功能。( J$ `4 K2 l, H, G6 g/ q4 D
       首先选出所有工作日,接着用普通滑窗进行7日滑窗加和,最后用reindex()恢复索引,对于双休日使用前一个工作日的结果进行填充。
    4 a& I) f. C4 k
    0 G# h4 E, F3 X, ^, {0 P, T  P8 `select_bday=s[~s.index.to_series().dt.dayofweek.isin([5,6])]
    $ B& |, |  S* w1 p  ubday_sum=select_bday.rolling(7,min_periods=1).sum()
    / ?. o% Q. U: K& `  F, presult=bday_sum.reindex().ffill()
    # J  R, t; F  V- [result
    8 q: R) r$ R( j3 \5 }" t2 f) I! F
    2020-01-01     -1.00 X8 _& [2 A( z1 W' n+ t
    2020-01-02     -3.0+ Z$ v9 b4 L7 T0 T' J! A2 Z
    2020-01-03     -4.0% u, t' E; ^5 I* m2 Q- N- l% i
    2020-01-06     -5.08 O' F0 w2 a9 P: ~$ Q
    2020-01-07     -7.0
    6 r2 [& X1 R% U0 U              ...  5 E# V/ O  R; D) h+ C, H. ]
    2020-12-25    136.0* n) O8 s) y+ }5 `2 ^' v7 R% p
    2020-12-28    133.0
    ; e% _* @# c2 T, M' g9 \9 K2020-12-29    131.0* G: @1 `2 X+ b* g4 ]
    2020-12-30    130.0, K! t, R! D6 V4 u& W3 C; H  M
    2020-12-31    128.0( n& B1 M$ k2 o
    Freq: B, Length: 262, dtype: float64+ u& N4 U  M3 B. W

    / @# i+ h* I  H1 q1% ]3 X+ |6 P2 ?/ E" E* d
    21 u+ n  U2 j3 o2 Z% U7 I
    3" |# t5 @& }- Y) Q+ s  V
    4
    0 d- e4 v- e8 ~! i; d3 t$ p/ E3 I5
    / w* C8 ^8 ?6 M6
    2 F- G$ A# s  X- G7 i: w7
    * v" j# ]5 K% O  q9 x4 f. S) ~) C88 J( W$ A( I# J
    9
    1 V# x' t! \2 e3 u6 O5 @* P( M6 `10/ b- U! E, q% {, k2 ~1 g
    11
    # r9 }, ^7 t5 \" u" [12
    8 H! I! M% b+ W1 @/ k2 R( ]13# V5 o: f% _( [7 C
    14
    9 K" k4 i* j! ^/ h: L- ~$ Q7 j15
    ) n) J1 P; F8 j# y! a  Y3 y1 V16# M  W4 d/ S1 I4 j5 |
    17
    6 o- [4 M. ]/ b2 p' j" W( }  shift, diff, pct_change 是一组类滑窗函数,它们的公共参数为 periods=n ,默认为1,分别表示取向前第 n 个元素的值、与向前第 n 个元素做差(与 Numpy 中不同,后者表示 n 阶差分)、与向前第 n 个元素相比计算增长率。这里的 n 可以为负,表示反方向的类似操作。
    1 W/ ^( e' S, ?4 R- w8 {9 s  B0 [8 K; m
      对于shift函数而言,作用在datetime64为索引(不是value)的序列上时,可以指定freq单位进行滑动:/ p% A, y- i4 E4 w  T$ w
    : {7 {* [' ?& p6 M1 p
    s.shift(freq='50D').head()
    / ^# M5 V4 R4 nOut[113]:
    ) d3 z% Z0 {/ H) j! t4 m9 m, [2020-02-20   -1* t  x0 R( S7 n
    2020-02-21   -2
    0 x7 Q6 c( q3 z( g8 n1 z6 ?2020-02-22   -1
    7 v. U7 G! F/ c3 V9 R5 d' b2020-02-25   -1
      W; @  ~6 h6 |0 f/ I; n, _1 T, x7 A2 z2020-02-26   -2
    4 g! i0 Y& Z# A' Adtype: int32
    5 b2 f; {# D/ M/ I% `: X* b) }1
    0 x7 b0 K* A+ H8 o2
    ) [( p  `/ F& t" b1 Y* ^3
    1 v& ^, W) C  Q0 g4
    " D' T; H' F1 N2 @* i8 Q" u. f5" Q/ Z: S" k# c! E' V
    6* b4 _/ ?% r3 A+ ^
    7
    # r* \" M* ]& r9 j1 m- N4 U' P8- L. ^/ a+ X  Y8 Q1 X- H. A) M7 P
      另外,datetime64[ns]的序列进行diff(前后做差)后就能够得到timedelta64[ns]的序列,这能够使用户方便地观察有序时间序列的间隔:4 \* j5 H) W6 H- r' m: F4 ~
    ( o( F' w! z' P1 x7 X) S- I) O
    my_series = pd.Series(s.index)5 ^# P8 n. r# a0 R) v0 A# \8 W
    my_series.head()7 t9 X7 T$ A. T1 a
    Out[115]: * [7 k% h5 Y0 ]% ?  F3 o
    0   2020-01-01
    ! P9 j- j% O3 L: j; s8 W8 v, s! S1   2020-01-02
    # G! V4 r/ W1 C. O) q2 L2   2020-01-03
    5 g- e; @7 @: _1 s' I3   2020-01-06, ~  V- i  w4 x. F* ~8 y1 p) w
    4   2020-01-07% W0 [6 Z0 ]+ {6 |5 o2 d
    dtype: datetime64[ns]
    9 x, s- y" P! Y! T! W
    ( W, s1 ^7 S; O( u- {: x  imy_series.diff(1).head(), C) P( `6 s; _/ e6 R
    Out[116]:
    / c3 R, x0 I* N; Y6 ]0      NaT$ f. |: q. G7 j2 `0 r, ?% J
    1   1 days5 X- i8 F" y& [
    2   1 days5 s$ \  l1 x& {! ?* ~1 J1 l8 `
    3   3 days9 n: R9 p1 Q& n. V3 R; D
    4   1 days
    + R- [& W) \( P) Hdtype: timedelta64[ns]
    * @3 P7 x3 j7 D$ I% K: q
    # g% E  J! Y4 G$ B2 r1; t# i  W  x+ L3 C3 }9 F& H
    2: X- t6 ], n4 l. D" H9 P: j" z
    3. s2 |8 P( ?# H2 c$ b$ F; A
    4! o, S+ l* W: {0 \  s
    50 t; c# w3 u2 f( b) S7 b2 `
    64 Y$ c) W! _8 w/ _6 h, n8 O
    76 Y, k- \- |: u8 y
    8. _: |8 A" t, q
    9
    2 W& T- `" J0 \  G  x10
    9 W# ?1 ?3 s) t4 Q11
    3 Z9 Q" x2 p+ C9 Z; ?0 b, c' _12
    - K% r9 h2 Y+ m! z+ a8 t! C13
    * V* f% e" J8 Z7 n141 n) w2 \; c% u4 ^! ~
    15& O8 G9 V! j, D5 E2 ~5 F" D! w( e
    16
    + m: E' P/ }. Z3 f: ]; [17
    8 ^! U( b' q" O3 A* A18
    , ^  a' t* D7 f% E8 c) y10.5.2 重采样9 y* ?% E/ U) N& b5 c; W1 y4 Q
      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)
    ( B% `) D1 \3 `: Q常用参数有:
    % V7 a6 K+ v# w9 i% T# A  N7 E3 {7 }
    : n2 F; _, y& K$ W/ x/ R7 d( prule:DateOffset, Timedelta or str类型。表示偏移量字符串或对象
    0 e/ p$ I* j5 Z6 oaxis:{0 or ‘index’, 1 or ‘columns’}, default 0。使用哪个轴进行上采样或下采样& K; T% w2 z" O. V
    closed:{‘right’, ‘left’},默认None。表示bin 区间的哪一侧是闭合的。所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。
    5 r( B5 y0 b; s4 m2 Z' clabel:{‘right’, ‘left’}, 默认 None。hich bin edge label to label bucket with,所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。- X  c7 M. {8 _* d, F% H# Z
    convention{:‘start’, ‘end’, ‘s’, ‘e’}, default ‘start’。仅针对 PeriodIndex,控制是使用rule的开始还是结尾。4 B9 Z; o+ U& W5 P- P
    on:字符串类型,可选。对于 DataFrame,使用列而不是索引进行重采样。列必须类似于日期时间。
    ) P' I9 ^# H; ^# u' V; [7 mlevel:str 或 int,可选表示多重索引MultiIndex的级别,这个级别的索引必须类似于日期时间。
    : k1 g, i0 H' e$ b# o. P+ ~1 ~origin参数有5种取值:! X7 @% ~* D" p9 W& A
    ‘epoch’:从 1970-01-01开始算起
    * N; F( a; T- d‘start’:原点是时间序列的第一个值: S$ L  f4 m- P7 [
    ‘start_day’:默认值,表示原点是时间序列第一天的午夜。( E' d! [& q+ x* @8 J
    'end':原点是时间序列的最后一个值(1.3.0版本才有)
      z( v6 z. ?4 g4 T; F# r3 }( U: _‘end_day’:原点是序列最后一天的午夜(1.3.0版本才有)- f7 g" N% p, u& H9 c7 x
    offset:Timedelta 或 str,默认为 None,表示对时间原点的偏移量,很有用。1 ]- h, U& Y; l) ~9 l$ _! k
      closed和计算有关,label和显示有关,closed才有开闭。. Q* a5 G3 k1 G' g4 m
      label指这个区间值算出来了,索引放区间的左端点还是右端点,closed是指算的时候左端点或右端点是不是包含。9 d6 s6 s! g& \- @* ?' c( P

    ) ]  Y$ [" T0 O4 Y. O' Q) K" b重采样对象resample和第四章中分组对象groupby的用法类似,resample是针对时间序列的分组计算而设计的分组对象。例如,对上面的序列计算每10天的均值:: k( n% c9 L0 f  X* g% p
    s.resample('10D').mean().head()
    : S" i' K* @3 ?! w+ i; R1 zOut[117]: 5 c3 j$ d( T7 w. ^
    2020-01-01   -2.0000006 _6 E: @1 J! \& r; ]
    2020-01-11   -3.166667
    ; G9 H( z) T) I% Q) b' R, `2020-01-21   -3.6250003 |( U5 l: m% z
    2020-01-31   -4.000000
    1 g! g( Y  ?; X4 w2020-02-10   -0.375000
    * }  Q) t) Z; `$ g7 @Freq: 10D, dtype: float640 s* f+ P9 C5 m5 H$ j; D
    1
    3 S6 |8 Q6 h" k- A0 Z9 e% B2
    0 S! O0 |" D/ [+ S  U# J' G8 U7 n  i3. A2 i7 e0 E" U7 c9 v
    4
    7 Q, a, y6 d: Y7 h- f2 v8 u& Y4 {5. ?/ }+ s9 e/ i8 X( i5 E
    6
    ' @0 y  g1 K; A& f- P% d5 g8 F( f7
    ! W4 C$ i6 q3 @8
    5 ]" T$ C, B) w9 \0 M9 I, [1 U可以通过apply方法自定义处理函数:! Y" k& Y$ V4 I- D5 G
    s.resample('10D').apply(lambda x:x.max()-x.min()).head() # 极差! ~1 k6 F& m4 `4 n
    9 j- w3 ~. s# _$ ~2 Q  G5 r
    Out[118]:
    ! v. z% }8 ?/ n" _2020-01-01    3% z) W/ f7 }. d
    2020-01-11    4( A+ V: {( x; {* N9 }; y1 \
    2020-01-21    4
    / u$ @9 e% U% S$ q: U2020-01-31    2
    % n0 J: k) g; p$ G2020-02-10    4- M0 X! A6 y! }/ i
    Freq: 10D, dtype: int32- ~% _2 {% S- d3 L/ s
    12 i! b) u/ o: w! O4 ^1 o* t
    2
    2 v4 j8 E# O  x* ~3
    0 w, E; ]* d0 t2 y& K, j$ m' |49 @" M/ P- ]$ T' {( J' G0 Z( }
    51 i; u8 A3 O8 s- q
    6
    / Y) s. a  u, |' l78 X- h4 p% \- D2 ^
    8
    9 p% K6 w  q6 C. w( P, k9
    ( s! L* \( S5 q) p# N  在resample中要特别注意组边界值的处理情况,默认情况下起始值的计算方法是从最小值时间戳对应日期的午夜00:00:00开始增加freq,直到不超过该最小时间戳的最大时间戳,由此对应的时间戳为起始值,然后每次累加freq参数作为分割结点进行分组,区间情况为左闭右开。下面构造一个不均匀的例子:; U0 `/ M" l# p# h- C- q. Z

    5 r# J8 M6 O0 i7 m8 Vidx = pd.date_range('20200101 8:26:35', '20200101 9:31:58', freq='77s')
    - d6 S5 d/ b) y% Jdata = np.random.randint(-1,2,len(idx)).cumsum()
    8 d- Z2 h7 ^# L2 {" e. js = pd.Series(data,index=idx)1 i! y$ S2 J& }, v5 {; Y
    s.head()
    3 W/ p; _& t7 r, A/ u) m2 _5 `- j  c9 [& s
    Out[122]:
    3 e3 p4 S5 x! P% v2020-01-01 08:26:35   -1( C0 V7 Y5 D7 h& e, }
    2020-01-01 08:27:52   -1+ E: S7 B7 K( T( U3 H3 s9 _
    2020-01-01 08:29:09   -24 R1 o" I8 r: j8 o' W
    2020-01-01 08:30:26   -3
    ) ?* W" f8 Z  t1 I* x2020-01-01 08:31:43   -4+ l1 w. X  k' r# M9 r' c
    Freq: 77S, dtype: int32
    2 T- Y) u; B5 ]* \! i10 y" B& n& g/ S+ q- g
    2
    4 Z; r# p1 t1 G3; ]" r/ v2 \; N* k; {6 Z
    4
    8 o+ e2 {! H% i* V# y5$ k5 y0 ~0 m: x9 U1 T; K& U
    6
    4 u4 B1 u$ E4 O6 _  k. b78 Z2 R6 r& z) c( w
    8; l) p/ }+ m1 o4 v% x7 D0 |2 w
    9) p# |8 n$ B: e  C5 R
    10
    " R% e0 z) C$ V5 I) }8 s! D11
    / l- G& x" K( s' D12
    - J, R4 N4 E9 O0 n; F* C4 J# z  下面对应的第一个组起始值为08:24:00,其是从当天0点增加72个freq=7 min得到的,如果再增加一个freq则超出了序列的最小时间戳08:26:35:. a- x6 v# q5 u& P+ x
    6 r2 y0 k# n  N! J4 l0 P
    s.resample('7min').mean().head()# I7 k1 E% y7 E4 C' }
    Out[123]:
    3 {2 N) Y! {& r) s/ x2020-01-01 08:24:00   -1.750000  # 起始值,终点值包含最后一个值
      w3 j+ G, E( w2020-01-01 08:31:00   -2.600000
    2 \# ?1 x  y7 H$ A# w3 Y- ^1 a2020-01-01 08:38:00   -2.166667
    5 T# J# }5 V8 S1 ^2020-01-01 08:45:00    0.200000
    4 H4 F4 w0 O. b) j( e2020-01-01 08:52:00    2.8333337 f1 @0 B  p2 q8 \+ E- @
    Freq: 7T, dtype: float640 g" l6 r* ?- y
    11 v- ^, H, |3 ~
    2
    4 x/ q/ c/ w3 E7 b- q; [) C  k3
      R: E; i4 W  z2 o, M( V45 w: u7 _  w2 h& M
    5
    ' r6 s/ g: [! V  [3 K- i4 ?61 u7 w( g- @' j' S* e( ^  ^# c
    7
    ( B4 P" I% g& G, w: q8
    $ L' O. ^! d5 Z1 b2 ~2 J0 C5 B  有时候,用户希望从序列的最小时间戳开始依次增加freq进行分组,此时可以指定origin参数为start:
    % S, l6 `- z% I5 C, M& {0 y& R6 l' L
    1 d/ u- j2 i1 k5 [: B+ O: l& B3 Ks.resample('7min', origin='start').mean().head()& P& w- o3 P% O/ D1 N2 n2 v* R
    Out[124]:
    ) P" c7 ~; r( i/ A4 r* u' B& B2020-01-01 08:26:35   -2.333333
    2 T0 a* y7 F5 d- c2 h- B7 K2020-01-01 08:33:35   -2.4000000 l& R/ n3 @/ @: h
    2020-01-01 08:40:35   -1.333333- r+ M) n3 @6 Z1 ]% [! ~. u4 k
    2020-01-01 08:47:35    1.200000
    8 w8 X9 ^+ r/ n1 d2020-01-01 08:54:35    3.166667
    1 U1 u. Q$ W) A% s6 f- ZFreq: 7T, dtype: float64
    2 n/ I, A. Q& e# }$ Z2 d1
    5 p8 a+ ~% j6 Q8 @6 K4 b2
    1 E+ d3 i7 V, V2 r30 I1 m$ g! N; Z' t$ f
    47 ~1 p$ p3 H! ?
    5
    0 U  x3 {. \/ {- j4 w- R63 L3 z7 ]6 `, |3 {+ @0 c4 R3 i
    7  g+ l  y  |: N$ g3 x" y& S, D  i$ |
    8
    6 w( G' y" p9 G" Q  在返回值中,要注意索引一般是取组的第一个时间戳,但M, A, Q, BM, BA, BQ, W这七个是取对应区间的最后一个时间戳。如果想要得到正常索引,用’MS’就行。6 o6 l  Z9 @1 T- Q$ o/ V8 u
    9 G( O/ r& D7 K  k0 \
    s = pd.Series(np.random.randint(2,size=366),
    / m! ?5 L1 X  `3 x8 e' c2 t              index=pd.date_range('2020-01-01',6 e0 R, x6 d1 x" M- X# U0 V
                                      '2020-12-31'))
    1 }9 q- ]3 q8 R2 ?5 c  ?: C* H
    3 h5 q& I- d3 x  F* y0 p8 D0 A/ x( D, \5 E, S* _1 \& V4 N/ D1 z
    s.resample('M').mean().head()2 j" Q7 E1 ~# h5 N; _4 R
    Out[126]:
    . i) ?2 I% w" K4 J6 J& |2020-01-31    0.451613
    * h7 @4 t/ I% Z, n6 q* ~: S2020-02-29    0.448276' |* A7 W' B2 b. o$ u% K
    2020-03-31    0.516129+ `6 V  a. P4 A. c# I+ E
    2020-04-30    0.5666677 p' Z4 ]) E7 ]) R
    2020-05-31    0.451613% ?8 `4 W7 m, B$ _8 I1 O3 v
    Freq: M, dtype: float64- ]* l* g, [) c! L1 C0 T- U# ~

    3 o( }. l4 I$ ps.resample('MS').mean().head() # 结果一样,但索引是跟正常一样( y& r3 H$ v! N9 _* ?5 n
    Out[127]:
    ) I4 B$ Q$ W1 o8 ^6 W8 \! n8 V2020-01-01    0.4516137 q# v! _9 t/ h9 p; M  `- I* ?
    2020-02-01    0.4482763 U+ j. x! _1 O7 E8 H
    2020-03-01    0.5161293 J" q& D, j0 y9 d0 e% V
    2020-04-01    0.566667( b: N) O0 n4 `; l) C! W" h
    2020-05-01    0.451613
    6 v4 f0 {) h1 ^# sFreq: MS, dtype: float64) {  ]" P1 n( i# B
    " B3 p* I1 v! ~0 r% {" e
    1
    : P2 C  ~2 D& }5 N! N+ P9 x2: k' o4 g/ C4 _) T
    3
    ( T# A: S' d8 l: p4 U4
    3 Q8 U3 @" D6 h6 u9 N( M4 i4 `8 W5& v( w  m# a( \7 Q
    63 Q. u& \6 ~) }9 Y% E
    71 L: q1 A/ C  g% W0 A, B
    8
    6 F- `( u  t! O1 k. i5 I0 Q( @9
    2 e4 G# r0 p* a& c1 y& q" t% c0 N104 v* q0 M9 U* a, m
    11
    ! S7 q) k; W) z, T" n12
    2 u. P: t; E. [) l# z7 |: D1 K6 k139 ]3 j8 X1 T% O1 U
    14* n+ I, P7 L$ J* e, W5 c4 N; m& |
    15
    / w4 q1 i  f$ j- Z3 D- D  d16
    " d: V; v1 q) F. i% H& F; Q; a5 ~: S17
    . G& Y' _. P4 z1 a& |18, J, u% t8 x( M) m" |+ P
    19
    ( u* H& S4 a' [# z6 r# R# D206 ^" I+ t9 y% N9 z* {5 Y) F
    21
    * B4 F8 ~3 f+ \+ S- s! O8 K1 w22
    $ C* ?! u$ O- V; R( _对于 DataFrame 对象,关键字 on 可用于指定列而不是索引以进行重采样:: c# d) J# ]. O
    d = {'price': [10, 11, 9, 13, 14, 18, 17, 19],
    : A4 X1 e8 _, D- p     'volume': [50, 60, 40, 100, 50, 100, 40, 50]}
    , Z2 h3 k2 y( C' ]+ ]% odf = pd.DataFrame(d)
    ) R" I8 o) D6 V8 k. v# K# V( Fdf['week_starting'] = pd.date_range('01/01/2018',
    - d1 X) x+ ^  x! F+ P                                    periods=8,
    2 N1 ?/ N& ^9 [: I; j& ?                                    freq='W')
    4 ~+ P; M( o# e2 l7 n4 H- ddf; R. O  ?9 ^- G; H' D6 s4 J
       price  volume week_starting2 Y  G* @+ H7 h6 Y
    0     10      50    2018-01-07
    6 O- t3 |; n# c6 S& p1     11      60    2018-01-141 O! A8 @! H# `+ A% u8 x8 t" o
    2      9      40    2018-01-216 t% v1 b, \& b8 Q
    3     13     100    2018-01-28
    ( a' b, |' z/ f9 M7 X1 T4     14      50    2018-02-04+ B& |% M! f& V+ ]
    5     18     100    2018-02-11& W  D! N2 a, M* x
    6     17      40    2018-02-18
    ' A1 A+ a; N/ ?) r0 o, g! D7     19      50    2018-02-25
    0 @: ~1 U0 @  F% edf.resample('M', on='week_starting').mean()
    7 ~* W2 i$ N" {8 k* l. e) V0 S               price  volume
    & N( R8 G: k. Q4 j/ aweek_starting
    - u9 w1 v) g; c) R8 A2018-01-31     10.75    62.5
    ( A: {9 M  N6 l2018-02-28     17.00    60.0
    $ Q2 Y6 V; ]0 B3 [/ w; e& f, f7 ~' B8 M3 |% ~  x
    1% T: N: }: N, Y7 E4 t3 b7 {( R
    2
    2 M/ k1 ^8 G7 k) p# e38 R  B  e" c* q" ?. \
    4
    8 a0 f- L2 `* K5 s53 b  h# M* B/ x- o/ ]' c
    6: h: B1 T' |  |2 P# E/ Z4 Y- @
    7) _7 Z# M  h* |/ s
    8: H8 }  D9 `7 \' K
    92 W  J, u+ K& ^- e5 r1 b
    10+ A' l* T# i7 \3 A& s. q7 e7 w
    11
    : B4 H* x" L6 L! _/ c$ v5 A2 v2 G% B+ U12
    7 Y" ?& J8 e0 I/ r( z' d/ p  j13
    0 C% ~% h6 p8 o( |% u7 k! q146 `- ~6 C4 t% N1 D& X, d0 N
    15- w, h) @! W" v2 j2 l$ R# Y+ U% |  v
    16/ j. h" ?, N" |4 @$ X0 d
    17
    ; |- o9 g# L/ r! ^# b. d5 S" k, i18
    4 h& h; \* M4 X: }19  R9 v& D2 `1 d
    20: ]" D9 o1 k$ @1 P
    21
    1 ^8 G8 `6 S8 p, U对于具有 MultiIndex 的 DataFrame,关键字 level 可用于指定需要在哪个级别进行重采样。
    / r5 s1 l9 \0 p% T; [1 Zdays = pd.date_range('1/1/2000', periods=4, freq='D')
    ! Z  m: S3 T% e! d: jd2 = {'price': [10, 11, 9, 13, 14, 18, 17, 19],/ G  R+ I$ ]# h- \) a
          'volume': [50, 60, 40, 100, 50, 100, 40, 50]}
    ( w8 X9 c# K7 _8 zdf2 = pd.DataFrame(
    1 S6 x: g6 D+ U) T& B    d2,$ f% u3 j' U# f
        index=pd.MultiIndex.from_product(1 e1 k2 `, K$ j  n) }& Q* _9 L
            [days, ['morning', 'afternoon']]
    2 |# `& p: Z- f$ \8 X    )
    & W& I& @6 c$ V) b, g): V: u7 @  B" L+ m7 w
    df2
    . k9 ~' K, k5 q                      price  volume* e2 b# m! A9 L4 d- q$ b- _+ k
    2000-01-01 morning       10      50
    - [+ V* @, p6 M; z. X: v           afternoon     11      60
    + J! y6 [. ^, z) v/ k2000-01-02 morning        9      405 C7 \2 a" M! K- l% h* C) @" u- u
               afternoon     13     100
    & j% C1 H; A; c3 M' |/ O9 N2000-01-03 morning       14      50
    2 F7 A3 p6 K" f5 ]7 u7 y           afternoon     18     100" Z% H  S9 F  n  y7 u
    2000-01-04 morning       17      40, K6 h6 j' R& U4 _1 d' T
               afternoon     19      50
    7 ]  Y, H8 c. L) Q0 Ldf2.resample('D', level=0).sum()" U5 O) A8 y: s* K9 c9 R- O
                price  volume
    3 i! d) W- ?- [6 O6 D9 d; J3 O2000-01-01     21     110) K- |: j+ u1 B
    2000-01-02     22     1400 j* E' U2 I7 @& L
    2000-01-03     32     1503 |# |5 Z' D# N7 S" E+ a$ a) n5 v  z) s
    2000-01-04     36      90' }4 h# b& l/ K$ g1 I
    5 }) z# E: \0 ^' E! A
    1
    9 O/ K8 h: [$ w8 y2# n: ~6 e  w$ u& d; B. {5 M- y( W
    3/ e- a: c; ^+ v! k8 x0 s% S
    43 l2 o6 K+ I1 S' A
    5
    8 b3 f3 U8 Z4 v6% D' p: h! L$ _* ^  r: k& [
    71 a. p; ]/ n+ r1 q+ N0 k% T. {
    84 @& [) h  H; O1 x2 e" e
    9
    8 a# h- D/ X1 a2 P7 V+ o10
    ( O2 b6 M8 d/ v11
    ! D: f4 T! U. G  ^12- p3 n; ~2 \1 h- W( P: i- A! I1 {0 F
    134 @, k. }1 c+ P$ f9 N3 y
    14/ E# L- i  Z5 Z# X
    15) A+ g% _2 u* q% k; ~, r1 s4 R
    167 _! C; l: K! S+ ?
    17: d( Z2 I. M0 A
    18
    " c+ H: J3 \; w+ I) b; F19
    3 m# ]" t# {" D( G% K6 B  _20: Q8 k/ E5 p' d7 Z8 k
    21
    7 ]5 \& y* l' U, W  E* s/ L: {22
    & e4 {! b+ p1 e+ Q234 f' A/ x! E, l
    24
    ' A* U" o& o2 b* [7 E- z3 c25( `: b& j1 O1 n3 K
    根据固定时间戳调整 bin 的开始:. H$ b$ `% N+ |4 @3 V# D2 z
    start, end = '2000-10-01 23:30:00', '2000-10-02 00:30:00', a, f! X+ u% s. @! P  I  y
    rng = pd.date_range(start, end, freq='7min')1 @, ]2 n2 `$ x+ \' ~
    ts = pd.Series(np.arange(len(rng)) * 3, index=rng)0 s2 Y; d8 F+ H/ w- \6 W- J' h
    ts- y: t0 u7 G- u  S% ~
    2000-10-01 23:30:00     0
    ! t: m$ v4 H+ X' b2 h% {4 L. W2000-10-01 23:37:00     3
    + ]# S$ F) M) t- `5 E. E& D2000-10-01 23:44:00     6! J% t! [% K8 g$ d* M4 |* i3 {
    2000-10-01 23:51:00     9' y, ~; I& e; I$ B& e! I5 p
    2000-10-01 23:58:00    12# x1 T# o7 k& ]5 _* c7 t' Q( ^
    2000-10-02 00:05:00    15
    # q" Q" F$ u; \& W8 d  e: X: @2000-10-02 00:12:00    18
    5 q. t6 @5 d, g# {% T2000-10-02 00:19:00    217 f) l' [9 a2 E) w' @
    2000-10-02 00:26:00    24
    5 h5 R- O# c9 G9 @! K- ~Freq: 7T, dtype: int64
    " }  c0 b" z1 [5 `* p4 ?# x2 J  ~) j% ?! i1 ^4 |5 N
    ts.resample('17min').sum()
    * C" U7 q! P" l) |$ {5 T2000-10-01 23:14:00     0
    ; S$ y  t( v( p/ j% o  B2000-10-01 23:31:00     9
    9 w6 O- Y/ ~  I! c0 L2000-10-01 23:48:00    21# _+ y( `5 G  e+ x9 X/ l. M
    2000-10-02 00:05:00    54
      W1 P2 x# f0 j" {3 i; L& G5 c3 B2000-10-02 00:22:00    24
      e" p  H3 J: \% k3 F  PFreq: 17T, dtype: int64
    2 A7 L! ?( O: ]7 C0 x$ N8 Q
    ! R( b$ w& w1 Y3 n4 @ts.resample('17min', origin='epoch').sum()  S# H. t! |& u" |( t
    2000-10-01 23:18:00     0
    2 k. Y6 j  k% d2000-10-01 23:35:00    18
    ; ~( j% v. a0 C. b2000-10-01 23:52:00    27
    % N4 [- X* d' |) K# r1 _5 S2000-10-02 00:09:00    39
    5 i0 @( c6 H4 I, ?% Z2000-10-02 00:26:00    24! |+ Q3 v# ?9 X0 U1 i; X) [# L
    Freq: 17T, dtype: int64
    - F" i" u& i0 W/ M! M
    ' B/ F- q. O+ o1 }: d( x0 Zts.resample('17min', origin='2000-01-01').sum()2 j" J- l# ^8 n! e  C- A, q$ x
    2000-10-01 23:24:00     3) J& N8 O  A3 g8 u: C
    2000-10-01 23:41:00    15
    3 L. \- O+ N! B0 A6 V! k2000-10-01 23:58:00    45
    ! D) I7 T% V- T3 a2000-10-02 00:15:00    45
    ; q7 H1 Y. B; X0 U* y# s1 r9 U' q2 U* R: KFreq: 17T, dtype: int64
    . V3 x. F8 F  I  p( x- S
    % [0 e  _- W1 ^8 ~+ ^7 ~" R1: T8 t$ l9 B& y
    2+ v* L2 e, r5 @9 T. H2 y7 z
    3& e3 h/ m) W; h
    4
    1 `$ W3 W1 {, V3 |5, x% n4 A3 M1 I
    6  _. i% C' d% Y
    7
    ; U; Z7 ~6 K; m& z+ O% l5 v' U: l8
    $ T. ~! Q  M8 W0 [' l5 Y97 _6 y# b' }9 k. O( i- K
    10
    : i4 J0 F5 N5 O; C4 ^3 [# e, w3 r11
    # P: P. p5 g+ J12  y: I) G7 I9 J# `! r# E0 w1 B; T
    13
      Z; w( z; x; |% n; ?14
    - l7 |8 c' n/ H' ]15
    1 y' |% D0 E  @/ q1 g0 u: `0 I16
    3 X% I# m9 [8 ]# L8 u+ p) G2 m17
      [- A+ S- o- i& O  r' V+ j8 g, A18# D# {8 z% Q6 R
    190 m. e* n' Z+ r" m
    20. y+ b( L( C5 ^8 p: F4 u
    210 J! B5 I$ z" g: k) F
    22- D6 o" q0 w- P5 q+ v7 _3 V6 z
    23
    # i8 L/ v) P% a) W5 d3 b( g242 z: X1 m; E' |# v2 T+ y
    25. r* R1 W' ?- f' }- X- L
    26* x" L9 I  E3 h, f4 }
    27
    6 B3 V3 X1 j: a28
    : @  c$ T& @) ~) e% U# D29; Z) ^9 r$ b* Z
    306 R( E9 p* m+ `) M2 p( g4 P
    31
    . o# _8 P- Y9 O; [( [5 B" q32& Z' w3 I3 @# x
    33) D. ^: a% r8 ^* R& K. F0 C& T# h
    34
    7 H1 g$ W6 _2 V4 g3 a, h" G3 ]359 d1 j! w, T2 W* q. x
    36
    $ c9 Q' U4 f& K# S& ^- b3 y/ O+ C: g37
    9 c7 [4 B/ n5 s$ ~9 a  o如果要使用偏移 Timedelta 调整 bin 的开始,则以下两行是等效的:0 i. s* D6 H, j! g: J0 f' s
    ts.resample('17min', origin='start').sum()
    " Q: W& d* X6 M5 B" W- J- y( pts.resample('17min', offset='23h30min').sum()
    . {6 O& ]/ h9 Q# i2000-10-01 23:30:00     96 t' {% _2 m* W# e
    2000-10-01 23:47:00    21' i1 O/ S+ e; {/ z8 ^9 n' X3 Y
    2000-10-02 00:04:00    54  x# k6 @$ u5 s3 |
    2000-10-02 00:21:00    24( p" z7 U! r0 ?- R
    Freq: 17T, dtype: int64  _4 }2 g8 {  E0 _8 e/ G
    1
    0 J& t' ~) ^( `8 o- a% u) b2! s  r  O: h6 k4 b/ w* b! T' m
    3
    - A4 ^. R- y! k# s' v; m4" t3 s- J9 C) j' U: P
    54 T$ z/ G+ Y5 D% O% T4 \: s
    6
    # c7 `5 t% A' j% G; Z' A7
    4 h& J& X, p/ D0 p4 ~10.6 练习
    / [- O/ t9 D9 SEx1:太阳辐射数据集$ ]5 M5 X* C' ^4 J
    现有一份关于太阳辐射的数据集:
    6 ?' _: e8 m, a( y4 h$ V) i0 K4 t* o
    df = pd.read_csv('../data/solar.csv', usecols=['Data','Time','Radiation','Temperature'])
    2 Y( _- ~+ \6 N: n( Ddf.head(3)
    2 ?/ z9 F( ^2 v0 P) P+ G
    # o+ u% g9 X5 HOut[129]:
    $ u3 y1 I4 f4 ~( n* A  S                    Data      Time  Radiation  Temperature
    & l" s6 L4 F: n- i& C  x4 H0  9/29/2016 12:00:00 AM  23:55:26       1.21           48
    # V( o; E8 y. r3 [1  9/29/2016 12:00:00 AM  23:50:23       1.21           48
    9 K* R$ A) f/ J& d3 H2  9/29/2016 12:00:00 AM  23:45:26       1.23           48% L' n; \' [0 G, Y5 P6 |0 B7 E
    1$ k, ~( h7 ]. j/ W
    2  J/ W: z" [# D5 d' Y" v0 R# x
    34 `, `0 d  @. f6 j' h2 {
    4
    ' h3 C* P' p  P# n% e* t2 ~57 s& l& N, J0 I$ N6 X3 d. [
    6
    8 Z# ?  d0 D( [% [  I, _71 k# A( M1 q  t( d; a
    89 c5 ^' t" [0 _, I3 T  W7 L
    将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。
      N$ [( K6 t2 W8 I3 w每条记录时间的间隔显然并不一致,请解决如下问题:% V( X! Q8 A1 i( c
    找出间隔时间的前三个最大值所对应的三组时间戳。, P0 @: t" D& I, L+ U- b
    是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。" h0 x, `/ D/ D7 T
    求如下指标对应的Series:
    5 J& i: v7 F( }& h$ Y( M" L8 t温度与辐射量的6小时滑动相关系数% j' }  p3 h* ^5 S
    以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列7 D% T# D. m4 O' J2 {' g' C
    每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)
    * ]4 V6 r: _' C6 H* ?3 b# D! }import numpy as np
    & L) `1 n$ F& dimport pandas as pd
    ( Y4 o) b& Y* f# O/ l1
      k1 J7 b) z* c7 P5 j5 \2- x& J: p6 H1 {4 i3 y
    将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。
    . D& P) Z' a2 a  \6 y/ T# edata=pd.to_datetime(df.Data) # 本身是object对象,要先转为时间序列% Y0 o% a" ]3 L1 k! D) U& A+ i: Z- U3 C
    times=pd.to_timedelta(df.Time)
      `, C9 M& i: f! d. C; h3 M$ odf.Data=data+times
    6 _& j' b5 F/ s7 U- Q0 }del df['Time']* O6 M; z5 g; C0 T  Q: _  g
    df=df.set_index('Data').sort_index() # 如果写的是set_index(df.Data),那么Data作为索引之外,这个列还另外保留1 D- F* _- F( u- }/ j  X2 g
    df" w8 Y) i3 C  D+ u; X1 k0 J0 Q
                                            Radiation        Temperature  I6 I( ]/ c" j8 M/ b  H. r2 L2 U6 q
    Data                , W4 \% N% o0 W' l) ]5 r2 u
    2016-09-01 00:00:08                2.58                51; J6 d" ?3 N: A
    2016-09-01 00:05:10                2.83                51
    ' p5 c$ R/ W5 Q8 x2016-09-01 00:20:06                2.16                51
    3 k7 z2 |: M3 G9 ~# m; R2016-09-01 00:25:05                2.21                51" x% o9 {5 Y1 F5 H, E) F& W. P
    2016-09-01 00:30:09                2.25                51
    " P7 G! C/ T0 \- ^...        ...        ...
    ) C4 K2 Y/ A% t9 |% f2016-12-31 23:35:02                1.22                41
    * u/ U/ j+ S' y. `2016-12-31 23:40:01                1.21                41
    7 u9 \% ?" F( c# q2016-12-31 23:45:04                1.21                42
      |! y" O. A. U  |9 y+ q$ G+ j2016-12-31 23:50:03                1.19                41
    : D: I' ^" y  D: r2016-12-31 23:55:01                1.21                41
      I9 L( j9 `+ ]' ~1 o
    $ F# ]6 A$ v; c8 `9 v8 s( D1
    0 Z3 L/ r' E, o1 `( Q0 H23 F4 G6 ~+ U4 H; i, m& D9 p2 W
    3
    6 J# [' t% s# \( `& O) T4
    * i! I7 C$ P9 G7 t4 l0 p, L54 M" I5 S( \7 l8 A2 }
    6
    , O, U6 ?3 b# ^! ~: s% x0 J7( G) z0 b6 H9 p+ W! [5 f
    8
    $ ?4 p- u; X, C1 [* Z) }9; v; p* I1 h% i* |* R; K! i4 K
    104 n1 `0 u' l5 d  j1 Z# G' o
    11
    . j7 [" S# b+ b& M1 c1 \, V128 e( c) N) k: Y
    138 m* d. B  K; q# i
    14
    , L+ J" R" G3 y# _$ C158 L3 f* _- |( m+ l- J; N, F, o
    16
    - U% w7 ^9 C3 s  Q7 K/ V1 r7 \17
    8 K5 r. X# i5 p- [: Y18
    3 j. p2 ?& v7 q' H19
    & _% O! [) o. y3 h每条记录时间的间隔显然并不一致,请解决如下问题:
    % ]% ~* J4 P! c/ c& [找出间隔时间的前三个最大值所对应的三组时间戳。
    & o% @* X0 s; {4 V9 i5 t# 第一次做错了,不是找三组时间戳
    / x1 Q. n9 \0 _7 @: }& E0 eidxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]
    5 [+ Q4 q! m' R* C7 f# u( jdf.reset_index().Data[idxmax3,idxmax3-1]3 g4 N$ h( T( I% P1 s7 k. l
    $ F& L  T0 Y1 O6 Z# p% ]# O
    25923   2016-12-08 11:10:42) L5 y* ]4 j( o- U* v
    24522   2016-12-01 00:00:02; E* R' \3 b- I* k( }
    7417    2016-10-01 00:00:19
    / ^& P. w: i  {8 }9 X- LName: Data, dtype: datetime64[ns]$ {' b0 J9 ~( I7 Q
    1  ^" T" X; z2 @9 T
    2
    : V; R' ?6 k8 _# P35 T+ u. [+ t7 ~' m. X& l# A$ @
    4
    " Z( k* l9 b" F' j5' W' T; U4 l6 `4 q9 V8 y
    6+ i: H! G+ D9 e
    7
    & m$ U9 @0 ~; t8) j% j  G; r9 V& `8 m
    idxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]$ R7 n8 L! F( U3 G# R$ Z
    list(zip(df.reset_index().Data[idxmax3],df.reset_index().Data[idxmax3-1]))" ~7 m7 _) m' T# @( O
      m$ S! m+ S, T! B( C- k" \
    [(Timestamp('2016-12-08 11:10:42'), Timestamp('2016-12-05 20:45:53')),
    6 Y9 `; H1 p1 E1 T- f+ f (Timestamp('2016-12-01 00:00:02'), Timestamp('2016-11-29 19:05:02')),
    3 u4 ]9 P, J6 v. j9 Y$ N9 r (Timestamp('2016-10-01 00:00:19'), Timestamp('2016-09-29 23:55:26'))]% h+ e4 v  ]/ V' i( ~
    14 i4 l( y+ H( C* M, j" N% i
    2
    , _. T$ d& K" d& K! t+ W  j! o! z3
    7 f1 b, f1 W8 m. {4
    % T/ s' J2 H" B5
    - k3 g  P% ^0 e8 d6
    # o7 J* ^1 e8 |8 f# d5 P1 f参考答案:
    ; R: M8 j5 F: D5 q( e" L8 Z. N' W# t( ?6 b$ o
    s = df.index.to_series().reset_index(drop=True).diff().dt.total_seconds()
    4 N! j7 [7 `! [& b: Z9 Q' u7 b7 e8 Umax_3 = s.nlargest(3).index! x2 o, Q3 y& ~" p0 i3 k: g
    df.index[max_3.union(max_3-1)]% m9 G# l# ~4 i( v$ ]1 v
    1 y$ P, N: W" s( b! J0 y/ ^: I
    Out[215]: ) z" e0 d  b. f! e% z) y+ K! f
    DatetimeIndex(['2016-09-29 23:55:26', '2016-10-01 00:00:19',
    % @1 F" [3 W& ^- Q7 t" _               '2016-11-29 19:05:02', '2016-12-01 00:00:02',' h) U+ e* d# W% K6 b
                   '2016-12-05 20:45:53', '2016-12-08 11:10:42'],
    7 i' J( ^6 p# A  Q              dtype='datetime64[ns]', name='Datetime', freq=None)7 Q0 j0 S2 Q) L
    1
    5 h3 a8 ^0 i8 y3 v2
    ; d8 l# p5 p9 X0 w5 i9 W/ R+ n3
    " E. g- H* C# H. M! E4& d. h6 _2 [1 q% o; }( D0 o5 y  U
    5
    8 d& j6 N$ E  M: p6 Q69 l+ ^/ p/ J, Y1 g% I
    7" _" W% ?/ W% U0 \' Q
    8
    + N" ?& t, p1 j9
    # N9 q3 L# p: D2 E8 _; X! A( K是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。
    # r+ C, v9 \  K- Y2 X2 I! ]: ?# 将df的indexydiff做差,转为秒数后排序。再求几个分位数确定取值区间
    ! ?2 V' A# D. ?* P# A4 Ns=pd.Series(df.index).diff(1).dt.total_seconds().sort_values(ascending=False)( x& Y2 v6 K" ?
    s.quantile(0.9),s.quantile(0.95),s.quantile(0.99),s.quantile(0.01),s.quantile(0.03),s.quantile(0.05)( V( L- P9 m$ @+ i( t# C7 a% @

      ~4 i" g% U* G1 V) M8 D(304.0, 309.0, 337.15999999999985, 285.0, 290.0, 292.0). m5 m4 @: z, M: ~
    1
    7 g" Y9 t8 l9 V) i2
    1 T: y* W3 u( t+ ]6 n) ]1 Z3
    / l' `' Y/ P8 G; x  r4% L3 C0 U  U6 r
    53 a* E$ x+ t9 j, L9 R) g" }+ o
    %pylab inline- G6 P( u& O/ C7 ]+ L5 L" Z
    _ = plt.hist(ss[(s.values<337)&(s.values>285)],bins=50)
    5 k: t6 n8 g* ?0 s. m3 q, kplt.xlabel(' Timedelta')7 y! V! D" ?8 _  L- w4 a3 |6 |
    plt.title(" Timedelta of solar")& }! B; n- i' Z7 I8 `) R4 Y
    1
    4 r+ e; }% r# E0 F2
    1 F. m$ h. i  }$ S/ B3
    4 e0 D% V# n: H2 ?5 W4$ c# C* M5 y7 N; X

    ! p, l  S# T0 a" J% g  f! j! g7 L) o2 E  z4 N/ |
    求如下指标对应的Series:$ K6 [( a' b+ i! R- V" D
    温度与辐射量的6小时滑动相关系数
    5 r0 R6 d  v/ ?* Y以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列
    3 w  e, }! ^8 H每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)2 u% x' u* H5 J" a+ Z
    df.Radiation.rolling('6H').corr(df.Temperature).tail()# X' u8 {. H* L# s) S  B" E3 K

    # T: H( Q0 O( u5 U3 f7 YData
    " L. n! I+ A7 |3 D) [2016-12-31 23:35:02    0.416187
    / I; A. [' `+ S9 O2016-12-31 23:40:01    0.416565. x7 l+ |- a# e& g5 K1 z
    2016-12-31 23:45:04    0.3285741 ~% H4 Y, F  }( W! [% |* @0 X8 b
    2016-12-31 23:50:03    0.261883
    : O3 J" u6 W5 J" l* I% i2016-12-31 23:55:01    0.262406
    $ Y+ @  l" }' V% Tdtype: float64, h8 M, k$ K; @* O
    11 W' J3 M/ M3 v
    2. l) X( T/ p6 d$ u, s0 }4 P4 E
    3
    ; Q1 _' L+ L4 i0 z4
    ! F2 _& F% e2 c  D: `7 G7 V6 Q5; S9 D- C9 ?. ^0 e
    6
    ; l0 f$ `! W2 \/ r" M) S' p2 q! H  Z) X7
    ! J8 K" h! a- _4 }* D, T8
    0 n. T7 u3 v/ H, ?# F$ l9
    / R4 k+ _8 e* \* o4 p9 adf['Temperature'].resample('6H',offset='3H').mean().head()
    $ ~# E: g( n0 u: N' e9 _% ]" z# [/ C8 t$ g* e5 F7 Y
    Data7 z: N, z2 A  s  R
    2016-08-31 21:00:00    51.218750# S# u; P6 l. @' R+ R# ^5 J8 ^
    2016-09-01 03:00:00    50.033333
    ( |& P" C% v' ~2016-09-01 09:00:00    59.3793106 S* L, Y' m8 J( b
    2016-09-01 15:00:00    57.984375& _: [" f3 B" H
    2016-09-01 21:00:00    51.393939; X8 w( X/ y& T4 B/ B1 N+ s7 s
    Freq: 6H, Name: Temperature, dtype: float64
    . R/ i; N/ u4 ?1
    , a4 `1 F9 G2 e; ]$ p8 O* B9 P: r) m9 }2
    1 Y% T- L  B5 Q! h0 c' n3
    2 h+ c* x* A( K% J47 k; S' g3 S% R7 i
    5
    - n' V$ ]; H6 D+ [% O" P6
    - B* I" N0 |# i- A6 L' }) D) h7
    ; }8 I/ b% g$ S) D3 p! ?8+ S" d6 L9 F/ x0 Y. ]! ~8 @
    9
    9 a9 @$ n4 _$ U最后一题参考答案:$ Q, e8 u( p" z, w7 [7 c! U
    4 D. ]( M) M! D) ~2 f5 y
    # 非常慢
    . Q4 r; {7 Q: ^. dmy_dt = df.index.shift(freq='-6H')& F5 h- g# |3 F* b6 m1 h2 V
    int_loc = [df.index.get_indexer([i], method='nearest') for i in my_dt]% g% F/ u5 s  b" I4 x
    int_loc = np.array(int_loc).reshape(-1)
    ; o" A# C7 @3 P0 r6 r8 }: zres = df.Radiation.iloc[int_loc]
    0 t+ w9 G: y+ w5 y, T& a; Q4 N& qres.index = df.index
    * d- n; i. E3 C/ f7 m. Ares.tail(3)
    ) q$ C5 o0 i7 c' p: g1# Q4 \; N7 j- ?" x1 [: r& u4 \
    26 g% D' g$ m/ u) z0 C% p
    3. C1 ]2 n) U+ Y! [1 O- I
    43 b! J: z( `2 c
    5  b9 I2 ]% N/ Q. A! r) W) O- \' ~
    6
    $ o6 }0 S' m* }- R78 ?! Q. l  g' [2 \* S1 r4 w
    # 纸质版上介绍了merge_asof,性能差距可以达到3-4个数量级
    % l, n' ^: F" Ntarget = pd.DataFrame(' `% S/ ~% E6 U; L: A
        {) g, T2 W& L- G2 W) M
            "Time": df.index.shift(freq='-6H')," R  G  X4 g9 Q- a- J6 Z
            "Datetime": df.index,
    1 U/ ~" e* T, J$ {8 m    }
    0 W2 w3 e$ d: v  B6 s)1 J& @( ^; J7 ^6 h4 w: i

    * ]. X) K, K" [3 |# u0 F% y9 O7 Ures = pd.merge_asof(4 u) z$ Q9 t/ F, U, N
        target,
    7 H* L: d7 i; b$ y2 A    df.reset_index().rename(columns={"Datetime": "Time"}),
    8 M0 S1 Z9 U9 U  G+ L    left_on="Time",' s2 ^# S6 `) F- r' ?8 e5 B$ h. I
        right_on="Time",3 U. y: Z" M! \2 H% P/ Q5 ~$ ~
        direction="nearest"$ r& w& c) H5 R+ Z
    ).set_index("Datetime").Radiation
    / D, {- M6 ?5 _( ?4 G  R* q  {6 M; z3 C: o
    res.tail(3)8 m( n! e9 D8 B5 o& W  M
    Out[224]:
    % D7 b% w  e( G& F5 @7 U2 J1 LDatetime
    , N4 G0 n( F& \" Q- J" o4 e/ u# |2016-12-31 23:45:04    9.33
    7 }* M2 ]& O; R  ]! `( O  y) n2016-12-31 23:50:03    8.49
    + L* l4 q! u  D2 |+ d2016-12-31 23:55:01    5.84! i# Z; s* c0 C
    Name: Radiation, dtype: float64* k! v: G  N3 c( B
    ( D5 b" z2 M6 b* G1 j+ G0 p4 m/ m
    1+ ^( n; g9 O- j7 R8 {+ _
    2+ g  W6 j$ g. F4 H+ n
    3
    : i" R0 @( a1 c6 W# n, H4& Z& t' H# T4 m. r& z+ @8 |
    5
    ' R( N, |5 h, w( ^2 ?8 f6; k) [: D" ?, ^  h
    7' \& W0 e" ^2 h9 R# z/ P5 g' A8 y, M
    8) p+ q7 _- E/ k3 m
    9) }) L" A8 K/ Z( t) Z: O  p
    101 W2 f5 d$ Z* y) `. `& X- l" g: R
    112 q% j. W0 B% j
    12
    4 f8 |$ }# {1 Q7 S: \" p13: f5 M: v& s: |7 f1 F
    14
      P$ R3 f1 e9 F3 F, p% P15
    1 K! {3 T! y1 c" x5 ^. v) |5 l! e+ F16
      x# p9 \( X% h: \1 y9 ]) t/ b17
    ; \) i1 T3 T2 ?; n9 @9 \  L3 |; \18/ {4 P; ]+ ^& R; u: k9 K8 u
    19! f( ?! M9 a2 e1 V) q6 I( B/ V
    20* a/ W  _7 v! e' k/ r
    21; }, L9 [, i8 A% j
    22
    9 j0 B  V$ `. w8 a, E1 Y23
    ' w8 j% Y! J  c9 @Ex2:水果销量数据集
    ! I3 Y$ ]) G# K3 E# z/ E/ c现有一份2019年每日水果销量记录表:9 H8 H) a3 G' w, E" Y% m
    $ Z$ r- ?1 A/ C- R
    df = pd.read_csv('../data/fruit.csv')
    0 v; G& t0 D/ n6 d' \* |8 Qdf.head(3)
      i5 C- g% H7 O: n" {. f8 u: J) v* H8 F* j$ q
    Out[131]: " m5 d# j1 R, p: ]# g
             Date  Fruit  Sale
    2 [4 x- _2 W6 U3 n" Y+ t0  2019-04-18  Peach    15
      I3 D2 C$ _( j4 f( {1  2019-12-29  Peach    15
    ! {; s6 J1 ?- Q2 w" `& E/ @( @" F" B2  2019-06-05  Peach    19' k2 h* X' k8 V
    1; |- x( X7 ?- P
    2
    . m( i/ A2 c. b% [) j; }. V3
    $ S; O; I6 q, s) G* h6 a, X' I+ N4; T' Q: b3 v- U, j! L- M; r, d
    5
    % ]- y5 N( H: \: r7 Y( m* Q* X6
    " l7 e5 x4 h9 u$ s$ Q* y8 Z$ }7
    4 V9 j& U. v: O' U0 i8
    ! Q) N8 n3 C* L# _5 P$ E! H统计如下指标:
    / x9 d1 x6 `) {$ \: X每月上半月(15号及之前)与下半月葡萄销量的比值- S  w3 k- V$ o% o
    每月最后一天的生梨销量总和' @+ F/ }8 K" H0 l6 H. x( |$ y. G
    每月最后一天工作日的生梨销量总和
    , Y. d( @& X0 q2 i; F. D每月最后五天的苹果销量均值2 [% B% q; v& X6 b. W
    按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。
    ! b2 P& b7 O1 {4 A, j% z0 J. H  m按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。1 D, \( I& C# g  W9 U) W2 p
    import numpy as np  F! ^; n5 E) o5 l2 \- e) m3 B$ y
    import pandas as pd% u0 u; N5 [% {$ Y5 ?+ M" {( W9 }
    1
    2 v  \! y2 b3 V6 N. Y+ r6 e7 c! G% o2
    3 R' u6 Q/ U" h6 W! ?统计如下指标:; w0 I7 N; [/ o: s7 T( d' @
    每月上半月(15号及之前)与下半月葡萄销量的比值
      l" ]' m: t2 f. v2 W. S) z! T每月最后一天的生梨销量总和
      V6 _- D. _, I4 Y5 Z" l每月最后一天工作日的生梨销量总和9 @& Q, t8 ~/ P! y# J# _
    每月最后五天的苹果销量均值, F8 }/ d% S% a
    # 每月上半月(15号及之前)与下半月葡萄销量的比值. Z% a  e$ k% @% f
    df.Date=pd.to_datetime(df.Date)
    ) N. _' j! O2 \7 ^8 gsale=df.query('Fruit == "Grape"').groupby([df.Date.dt.month,df.Date.dt.day<=15])['Sale'].sum()
    1 H5 w* L3 Z& A3 n/ z% R; Esale.columns=['Month','15Dayes','Sale'] # 为啥这么改没用啊" M; o9 _5 F& }
    sale=pd.DataFrame(sale)
    . H: Q1 Q3 _4 p+ k: Hsale=sale.unstack(1).rename_axis(index={'Date':'Month'},
    ! u/ u  @; P+ D; v7 }                 columns={'Date':'15Days'}).stack(1).reset_index() # unstack主要是两个索引都是Date无法直接重命名% `# b! g% G% J# ^' L
    sale.head() # 每个月上下半月的销量
    6 ^+ i$ |7 o8 C7 t8 K5 \. ]0 C/ i% D# F& Z0 T( z) g7 ~2 i
      Month        15Days        Sale
    . M( m5 t. N: \3 v" M0        1        False        10503
    ' E* h4 m) ]2 {) t* k& ]* C7 W1        1        True        12341+ a, _# m4 p9 r# y2 n+ _
    2        2        False        100018 _! ]1 k- y# @0 e" G% ^+ Q5 t; g
    3        2        True        10106. r8 [5 b* F# F! p" L1 m
    4        3        False        12814
    ! S& B2 r/ o5 p, |6 L+ S3 e* [6 D2 B2 G  \& {: c
    # 使用自定义聚合函数,分组后每组就上半月和下半月两个值,根据索引位置判断求比值时的分子分母顺序- E- R+ L& }9 j$ k% J
    sale.groupby(sale['Month'])['Sale'].agg(
    # P+ ~8 b1 M& T8 s                lambda x: x.max()/x.min() if x.idxmax()>x.idxmin()  else x.min()/x.max())
    + ^. l" T/ l; k, w/ @
    $ `2 F1 t0 H" Q# p- {" cMonth
    ' g& m1 C3 i& l, y7 `8 l& P0 }1     1.174998
    ) `; ]: |# C2 D7 {/ N2     1.010499
    , V+ G+ P) k) @* I9 V2 [+ L3     0.776338) F' e2 m% a' j7 B9 d
    4     1.0263456 o3 q# B) U1 U7 ~& S1 ~4 E* \1 f
    5     0.900534
    / K! T# k4 N2 G6     0.980136
    5 v0 _2 L# @( @1 s. k" W7     1.350960
    + g# l0 w7 W* Z; o  N9 W8     1.091584
    + q6 B6 Q; X4 S0 Q" d4 o, n( x3 W9     1.116508
    ' X0 X) l7 |1 D7 d% V1 x10    1.020784
    9 ~0 G+ X) B: |* e$ k! K+ x+ \11    1.275911% S5 T  w' K# ]! I& t- S. j
    12    0.989662
    - C8 k4 B# X& J9 K  tName: Sale, dtype: float64+ J0 Y9 S9 o2 K9 [: j! f$ @
    9 J. \# J$ C, x" K/ |
    1
    9 @1 Q& a0 ]" }0 j; Y# t8 F0 v2
    $ @2 b% z) T' m! ^3
    " h% h' |7 v( }45 r" k# u$ @3 d/ _6 M' k, I* U# D
    5
    , X  y) T2 E1 {$ T' o6
    " N! f0 T; h8 R3 k" X( G7
    ; S0 U1 t$ ^- r* p5 Z9 B, i8
    , ^9 T; {& ?8 ]  U99 R1 z: J8 G$ T, V5 ]
    10
    2 P% q9 H- J3 z% Z* k11
    4 B8 x$ L7 i5 h7 ]4 B  [12
    3 O! g; A: |1 O' |% S! A. E. a13! ?" Z1 m% i# k0 P. z
    14
    ' z+ e- l+ Y" y. d8 @3 y3 x15  Q, V/ G4 N1 f
    16
      t. Y, q8 e' V2 A  _, @! S, x17
    ! l) z+ h) I; d18% A( m# L$ {$ A5 B4 o
    19
    7 W4 h" t: ~. H( u& x! I20
    5 u+ A7 Q8 R7 @/ i21
    - `% f, ]0 h% Y9 v# ?. i& U0 {3 E6 f1 s$ E228 x  I& [  |- _1 V% W
    23
    3 [% S+ k3 X5 @7 ~) U! `3 L24
    # Y* w& }, }' T* v; [. O$ C25$ y! m, I% z/ v. U1 C+ ^, {
    26+ @/ Z( j/ p) `. D
    27" M; [' W" P0 [) i6 B! _
    28
    5 W7 M0 V8 N  K) X- n297 y1 a8 x# ^, ]- z+ I+ _2 p
    309 {5 W  K2 \" X9 }, r: i
    314 o& T9 l& t' h' b
    32+ J5 m) M5 Z0 |- {0 C3 G
    33
    4 p. i' c) Z2 |1 i1 d, |: H0 M34% r3 _* [8 a" J+ ]
    # 每月最后一天的生梨销量总和( O4 D" B& e( r- G
    df[df.Date.dt.is_month_end].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()
    ! [; A" [6 v( n" v0 H, G
    7 f" ?. ?) h1 [0 T8 o5 s* qDate
    . W5 W9 A- D. y+ c2019-01-31    847$ {/ w$ B( j* z
    2019-02-28    774
      F$ G) ?+ b+ O. I- R( C2019-03-31    761: n, }3 {( E; ]7 g' j3 U4 H0 s
    2019-04-30    6482 b3 [: G# t; K2 [
    2019-05-31    6165 b$ C8 a% c2 F" A8 a2 Y- U: W! Z8 f
    1$ ^& g" T( P. a* k
    2$ Z# B: O4 |) z. E# z* n
    3# P, y% x7 z+ ]& h8 e" o3 `7 o
    4& J/ d* K6 T& E& `) V, {
    5
    , {1 z- F8 {+ F: y" y# J  Y: ~5 Z0 E66 ~' L: E; f: \# G* S7 U
    73 `* Y) y. a/ U* T
    8$ F, i# b  B7 M9 Q% L& \, U+ L; O
    9
    ' y+ P2 p6 z. B7 F2 z  b# 每月最后一天工作日的生梨销量总和
    6 j, J) |3 |0 W, h1 Tls=df.Date+pd.offsets.BMonthEnd()3 _. q8 }0 @8 F
    my_filter=pd.to_datetime(ls.unique())
    ) o3 |1 K, _" b8 s2 Bdf[df.Date.isin(my_filter)].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()
    / Q. B( s$ F8 p. f+ \( n6 }9 D, V% Z+ F7 |2 A% P* B$ x
    Date
    ' E/ f& u: T8 Z9 [2019-01-31     847
    & D% O4 k5 C  d" a. N/ G) x1 j2019-02-28     774
    - H2 b9 l8 q0 w. |4 J2019-03-29     510
    5 a' C1 i- D0 `# b6 e2019-04-30     648# h8 R  S& H1 x  P/ ?: U
    2019-05-31     616
    4 d8 i4 g0 Q* X1
    $ \* O% u8 H% A2
    ; h2 W/ ~$ g+ _1 p, }3 t3
    7 s- k  s$ t3 t1 Z: B4* ~* P0 o' ]5 _& k  ^
    5, a  P% X; w7 T. f% f
    6
    " o* c1 W* _" a3 h* p% ?, Y6 Q78 [* {+ l6 Z8 o/ g7 N( C
    8
    9 {) W! r) [3 M. G& E% ], v9
    ; @. p( t( f$ a. O1 i10# L+ U5 z( p( Q" p/ F8 x
    11
    : u$ Z0 A) I5 R# }- D# 每月最后五天的苹果销量均值0 X4 P% J) A- M* B6 }( O4 }
    start, end = '2019-01-01', '2019-12-31'
    + Y  v. ]  A1 c/ B+ t: bend = pd.date_range(start, end, freq='M')! H# u# [2 A3 @- |
    end=end.repeat(5) # 每月最后一天的日期列表,重复5次方便做差
    # E' C, E' t$ i' F: M/ b4 j' l8 g$ E) @) X: D+ ?
    td= pd.Series(pd.timedelta_range(start='0 days', periods=5),)
    ) y4 s4 [) A* ^* ], k/ h5 Atd=pd.concat([td]*12) # 日期偏置,最后一天减去0-4天
    8 I" z4 r5 s. Bend5=(end-td).reset_index(drop=True) # 每个月最后5天的列表7 D# x# K9 _5 d1 X+ w. X) H

    ( O$ \2 r$ Z: I9 [0 h5 sapple5=df[df.Date.isin(end5)].query("Fruit == 'Apple'") # 每月最后五天苹果销量: X$ K" U/ l. R" V: R, }
    apple5.groupby(apple5.Date.dt.month)['Sale'].mean().head()
    & W# m( o5 t, y# e5 f) x8 k4 f$ b( X+ U
    Date& P+ @# D8 K! P( L
    1     65.313725
    9 v+ N2 m8 V$ X! N2 f6 `8 A2     54.061538
    2 B5 m  ?% K- Y& ~3     59.325581+ ~+ R2 E- \& M
    4     65.7954552 D2 v% h4 ^" |
    5     57.465116' \4 _4 @* g- _, b8 M
    . g5 ]7 T: K6 @8 H) ]
    1
    : ~( A: T4 P, e8 E; D% I0 V2
    + C! u$ s# P$ ^; U* }3" z* c7 e) e7 A, b1 U
    4
    8 y3 Z& g0 f; C5' k7 i) G! e2 j8 h4 j4 Z
    64 m$ z8 R( a0 ~+ S
    7
    , ?% v4 ]9 [6 \" K( d. z( }8
    8 X. i+ j+ X1 h8 ?' h- j9
    % D( [+ i( j3 g" z4 c; U10
    * ~8 b: `5 F, {5 O; H11
    3 l; {) P$ D- w: }- X' Y, ]& s12$ V8 [% |- n" D" q8 B0 Z! |/ Y
    13
    ! \- m' j9 i% }5 A* x4 m14
    $ {9 I8 n; |' ^. j2 W1 y$ e15$ _2 ?3 U9 j$ I% A+ U
    16
      p) M6 R9 @8 y17
    ! q; {8 {  q7 ^  w18
    ! W, J1 W/ A( ~8 z& R# 参考答案:" l1 ?" B3 K. S' @5 b" z
    target_dt = df.drop_duplicates().groupby(df.Date.drop_duplicates(
    + E8 N$ E7 F) C, \: s# ?            ).dt.month)['Date'].nlargest(5).reset_index(drop=True)6 f5 K* S4 a; }

    $ e' D* F9 `7 Zres = df.set_index('Date').loc[target_dt].reset_index(
    " I. R# r& h9 Z' s0 ?            ).query("Fruit == 'Apple'")! C  a- t1 l6 c* A
      I2 J  B6 `4 Q! n3 d" ~
    res = res.groupby(res.Date.dt.month)['Sale'].mean(8 a8 Q; y( z, K9 w# t
                ).rename_axis('Month')* i3 F2 R+ n* A" u7 r

    * |2 h, E# e& K1 W# F) r9 H7 I1 ~8 q3 H) i
    res.head()
    0 W1 e0 c! G& C) ~& ]Out[236]:
    , K, }% B5 L  m9 ^0 T  r5 V# zMonth
    ! u' B/ f% b3 Y/ {( F* J+ m5 X1    65.313725
    % G/ T, b; |( y3 N& t( z) i' o2    54.061538
    # C  X6 F  A; @! c3    59.3255810 r9 B3 @4 \  s, C& J/ E& `! U
    4    65.795455: K! x! B, Q$ n  E6 D
    5    57.465116* ~6 l" @& _9 ~9 q5 ]2 B
    Name: Sale, dtype: float64
    7 T1 r) B4 K- `9 X* K, R
    $ P+ \4 ^: W  M' v1
    7 L2 j/ j2 n1 |( C" ?/ e2
    0 H# k5 O- S# \1 @0 w3( _$ _# |0 G) B+ R
    4' H" ?1 Y, d) A, g3 e
    5
    . V7 K8 \5 ?8 W- C: R- d/ ?0 O6
    7 V- S5 V. Y! ^2 w7
    # O6 w' M! }5 {4 c8 c1 h81 O+ U& \3 v, |
    9
    5 ~- I* [$ F0 j) D3 Q) ^1 u4 s10
    ( a1 e& {6 v8 V11, |  {, k. M, g7 [
    12! D: s' E% Z8 a/ [! m
    13
    : \+ L# s, n; g/ N, w: Q14- Y' m0 d8 Q$ x. b# p# G
    15* g6 i! A2 G  V7 H+ r
    16
    6 v" y' @! w8 ?17
    * W" B" `& N/ p! J183 L& O  J) E* c! J3 N
    19: Q" a$ H' X" s3 b
    20" Q8 J2 Q  I& m
    按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。
    1 D! \, J4 J) i0 `3 Z' Rresult=pd.DataFrame(df.groupby([df.Date.dt.month,df.Date.3 r6 S, X3 M. C7 i4 E
                                            dt.dayofweek,df.Fruit])['Sale'].count()) # 分组统计 4 N: y  z& B  T: d* {# m% e. D: b9 E, ~
                                            2 `2 q7 {7 C: U- W  v3 X( P
    result=result.unstack(1).rename_axis(index={'Date':'Month'},
    & d" Q: j7 O6 c! A/ s: V+ G% A3 V                 columns={'Date':'Week'})  # 两个index名字都是Date,只能转一个到列,分开来改名字.
    / g) Q( p% v2 R) S/ o3 Vresult=result.swaplevel(0,1,axis=0).droplevel(0,axis=1)3 D7 ~6 X, r" X; g2 z/ b  c/ i
    result.head() # 索引名有空再改吧
    ! F( C  A' h+ u; y/ B# W8 [
    & r* ?: }, O4 {5 o6 `# ?, k          Week        0        1        2        3        4        5        6- B* s8 l9 x' V  m- n9 C
    Fruit Month                                                       
    0 b$ q# a6 g! o1 ^! tApple        1        46        50        50        45        32        42        23
    3 i5 u$ W! u2 h" r% m0 Y/ BBanana        1        27        29        24        42        36        24        357 ]% X0 B" G7 q# a; j
    Grape        1        42        75        53        63        36        57        46+ j* n$ E& m) B
    Peach        1        67        78        73        88        59        49        72
    4 `1 l) \- V& @( H1 OPear        1        39        69        51        54        48        36        40
    / q$ g" \- }0 L0 A! n( B' D1
    8 E1 |  I8 W0 |1 u8 u1 @0 ^& E2; w! X( V, h4 h- j
    3
    1 F' z. `/ c, L# `. e. Y43 N0 s4 K! t6 W7 l6 d4 J; R
    5
    6 U* P/ L$ E- N% e. B) ^$ N$ w61 U: u" O& ^$ m9 U! v7 h5 n/ }
    7
    8 O3 q5 T& ?  O' ?8
    , D5 w  @3 q0 _9' R5 |. |% e/ q& D( H
    10
    ; @1 e% [3 \4 ?- U11
    8 d' ~: B7 ]" E, e12* F1 O. R( R- |. U
    13( x# c- j& p8 Q- R* S1 }7 y
    14# P/ i1 f; F$ }5 \6 z9 ^" o
    15' d/ K* Z) M2 j0 t
    按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。
    9 Q! X& ?6 S( v3 p4 d& x# 工作日苹果销量按日期排序
    $ B& e8 z0 `& X. F/ ~9 o% ^0 s( s  Y% yselect_bday=df[~df.Date.dt.dayofweek.isin([5,6])].query('Fruit=="Apple"').set_index('Date').sort_index()/ j- q0 ~: B2 C
    select_bday=select_bday.groupby(select_bday.index)['Sale'].sum() # 每天的销量汇总' ?$ u3 n+ ^! A7 ]" W
    select_bday.head()) t/ y: Y! |1 h& r

    # A! s- ^2 C  E* UDate% Y/ e! k9 T& S1 O/ g5 Z6 g3 r, V
    2019-01-01    1892 K0 c0 R: _8 n- }) Y$ p2 H
    2019-01-02    482
    # v6 s& W/ x& K5 Q2019-01-03    8903 ]/ |# L3 z* ?& b0 E) D
    2019-01-04    550
    2 Y: ]9 j" C: o) ]6 h: g2019-01-07    494* N3 C/ c" j5 Q0 ^8 t
    ! U8 n9 p* h* x! k! |+ ]
    # 此时已经是工作日,正常滑窗。结果重设索引,对周末进行向后填充。
    ) k1 f+ ^0 B) g: aselect_bday.rolling('10D').mean().reindex(df.Date.unique()).sort_index().ffill().head()
    3 Z) K4 M/ o: \
      A6 f# P& k# G) O( KDate; x) ^$ m6 J/ g3 W/ o6 g
    2019-01-01    189.000000( d! B: H5 P; v6 m# `
    2019-01-02    335.500000
    $ |+ J7 Q. x0 j9 t1 [. v2019-01-03    520.333333
    / r2 s+ L4 m2 z" h5 C$ @2019-01-04    527.750000
    7 i* ?( ?; N, Q& d% L  }2019-01-05    527.750000
    # F. {. l" h/ n; n5 y
    6 E$ B1 t6 F  M————————————————. ~3 p! A( Y) V
    版权声明:本文为CSDN博主「神洛华」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    ) K) k! a+ U. w' j# p原文链接:https://blog.csdn.net/qq_56591814/article/details/126633913
    ) K1 B& _/ ?' e( \% s  A  q! D
    8 g& v* w1 U9 Q* G( H2 [! V1 @
    ( s. [& x" I" @8 L) a) 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-6-13 03:56 , Processed in 0.615922 second(s), 50 queries .

    回顶部