QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2826|回复: 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

    0 Y2 R3 D2 S- r: J7 v0 z
    * U2 L" I' a" G+ P6 e! H( f1 W! z! e8 Z. C2 {! L
    文章目录
    7 R  d6 g9 X+ E& H3 v第八章 文本数据( v% T4 w, D2 S  J7 v2 p
    8.1 str对象1 e" t, j7 A7 T& C- M: t
    8.1.1 str对象的设计意图
    5 m! p- k% K3 ]: ~, L7 U8.1.3 string类型! W) Q9 H* `) t9 L) |
    8.2 正则表达式基础# p' t* y1 Z% o$ J$ T% F( d
    8.2.1 . 一般字符的匹配
    " n2 q  e& F2 \9 {, x' S4 w" B8.2.2 元字符基础0 @* S4 [' n0 U) t
    8.2.3 简写字符集
    5 v9 B% O8 s" U; D8.3 文本处理的五类操作  B: x$ j% c" q# ?+ j. K
    8.3.1 `str.split `拆分
    7 t0 E0 m. }" ^% p  Z8.3.2 `str.join` 或 `str.cat `合并
    9 ]( n" B# x4 o# M  Y: e2 K8.3.3 匹配4 }8 ~  M3 V' e. Z, O
    8.3.5 提取
    8 z0 s9 p7 r! U8 H8 y& I3 h8.4、常用字符串函数# H  m8 B- t9 f8 z7 J
    8.4.1 字母型函数
    6 J; W  S4 ~* a2 {, r4 ~% D% t8.4.2 数值型函数# g% R" p- b( P, K1 m3 R4 H
    8.4.3 统计型函数
    . j" i& X$ @& D/ F' X$ C8.4.4 格式型函数
    0 V- B" n$ [2 ]+ V( X  t: D  `1 S8.5 练习
    : K* U" J/ E1 p8 r1 sEx1:房屋信息数据集- ?  {% @7 n+ M0 w$ B) X1 j
    Ex2:《权力的游戏》剧本数据集
    % s% T, j+ l# W+ W第九章 分类数据
    + s/ F- N5 M, U9.1 cat对象
    4 L! H1 G. O& b7 P9.1.1 cat对象的属性
    & O) [: N# J) q# r4 F: a8 O& d9 M' }9.1.2 类别的增加、删除和修改
    + C$ {( r2 p4 \" n- \/ Y, Y9.2 有序分类; X) o3 h9 y4 u9 N' \# F2 r& W
    9.2.1 序的建立
    ' D0 N# a5 i8 r5 y9.2.2 排序和比较
    7 u) P6 |) O+ x5 h, o9 |0 p0 k9.3 区间类别* o4 x+ A9 {: Q  F
    9.3.1 利用cut和qcut进行区间构造7 v1 N# _0 u/ o9 W# L" P0 _) z
    9.3.2 一般区间的构造
    ! r. k* T1 F7 C9.3.3 区间的属性与方法
    2 W& v4 z/ w9 Q9.4 练习, f9 m6 k5 p- W
    Ex1: 统计未出现的类别5 J& y; @, k# w! P) _; I- l
    Ex2: 钻石数据集
    ; r1 Q+ M8 h  {& n第十章 时序数据0 s: z% B( X" d3 J' u; {  E6 I4 m
    10.1 时序中的基本对象% T1 u2 \& e8 T6 Y* T
    10.2 时间戳
    6 c7 U" C0 ^) f6 D: \9 T/ \10.2.1 Timestamp的构造与属性
    ) T# m3 ^, Y! H" I10.2.2 Datetime序列的生成
    ) j, \: v# s. Z- a1 w% u6 ^10.2.3 dt对象
    0 \3 c! @% {; f/ n" I10.2.4 时间戳的切片与索引
    / F9 Q) q; ~  j8 H" ]; _10.3 时间差
    & x, G& i& g7 q; g10.3.1 Timedelta的生成8 P9 e3 g/ I) z& V" r0 W
    10.2.2 Timedelta的运算7 \" V2 U3 G4 a' k( v* P& j
    10.4 日期偏置
    2 Q& x5 E4 z8 F* _. h10.4.1 Offset对象
    6 n- R7 O( W- j9 W0 m$ Z) ?/ b10.4.2 偏置字符串
    3 R8 R$ P4 n, P8 d9 }+ w$ L# z10.5、时序中的滑窗与分组
      c6 H. Z: i: E  R0 Q, n2 W7 ?10.5.1 滑动窗口# W8 B5 S0 O2 b  p/ F
    10.5.2 重采样: {% r/ {2 J7 r. s
    10.6 练习
    2 P& s/ w+ q3 C9 k( REx1:太阳辐射数据集2 e. i+ Q3 A9 V3 \8 X4 b
    Ex2:水果销量数据集
    8 f& E+ v: z6 ^* J+ S2 O  课程资料《pandas数据处理与分析》、github地址、讲解视频、习题参考答案 、pandas官网# ~  b: B  T1 }* N7 z1 a1 {
    传送门:( P- _; N& \; j* Q
    ' q0 L0 F- u6 H) o) [3 y& v3 |
    datawhale8月组队学习《pandas数据处理与分析》(上)(基础、索引、分组)3 O6 j8 B% h! u6 I; T9 B
    datawhale8月组队学习《pandas数据处理与分析》(中)(变形、连接、缺失数据)
    & T( o, L- q  z1 l- G7 E- P第八章 文本数据8 A0 ]2 ?/ R0 h
    8.1 str对象$ F: \0 j: p* i0 f" ~
    8.1.1 str对象的设计意图3 K' I& |4 T3 C" k
      str 对象是定义在 Index 或 Series上的属性,专门用于处理每个元素的文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其 str 对象。在Python标准库中也有 str 模块,为了使用上的便利,在 pandas 的50个 str 对象方法中,有31个是和标准库中的 str 模块方法同名且功能一致,例如字母转为大写的操作:+ H( d! `5 F: L+ J
    % u' X7 T# \4 S5 d' Y9 K1 n
    var = 'abcd'1 i. M; O3 b4 v# f* A+ J8 o# K
    str.upper(var) # Python内置str模块
    8 p9 p! V) D7 ?- t- v! p* pOut[4]: 'ABCD'
    % R. n* C9 h+ ]3 \4 ]7 Z: M6 v6 W3 T) R6 h
    s = pd.Series(['abcd', 'efg', 'hi'])
    3 B3 W5 t+ }" d) q; O% Y. R
    8 u  V$ M* c; ~1 ], ]s.str5 Y% K: Q( ]% C# h
    Out[6]: <pandas.core.strings.accessor.StringMethods at 0x2b796892d60>
    - f% J9 q  [! W2 i; d% w: ~) d- [' W& Z6 X. M
    s.str.upper() # pandas中str对象上的upper方法
    & c1 j2 F3 i, v5 R8 {. ZOut[7]: / {9 }. b8 a' ]! j7 ?$ t
    0    ABCD
    ' c8 k5 o4 X! {8 R& w1     EFG3 b' }0 H) V- `/ G2 k
    2      HI
    ( Y; l: ]$ X" \. x! o- |  N$ Rdtype: object
    # S- h1 L+ Z. {0 Z2 {1; O8 X! E$ b1 Z5 `
    2
    9 @) a( `! L% f- N3( G4 K- W4 O+ M. o; E% S
    4
    . y2 Y! a, y5 b6 P5
    1 d  ?' P/ x% J% }8 }4 |1 b0 f0 A+ n  U6
    " C8 n1 u0 P$ o) C' p6 v+ ^1 P77 k4 a, N  M8 o8 T& e. ?
    8
    ! K3 Z% F4 E; b: w96 z6 Z; V4 B  |  [5 b2 N- g: C2 j
    10
    % J+ R' x1 s! Q% G) p119 M# Y: Q, h1 j. l4 v. ]
    12
    9 Z! D5 o( z* P7 p* z137 t; @2 }: q2 |2 X- M; Y
    14
      M' e/ G1 X! @5 p( K  W154 N5 O  {8 [$ A. W8 E, f
    8.1.2 []索引器
    # O; d9 d7 P8 }% d! S0 v  对于 str 对象而言,可理解为其对字符串进行了序列化的操作,例如在一般的字符串中,通过 [] 可以取出某个位置的元素,同时也能通过切片得到子串。
    3 Y9 |. t  o* R) w; V+ ]% Z  pandas中过对 str 对象使用 [] 索引器,可以完成完全一致的功能,并且如果超出范围则返回缺失值:
    + K( X  f; t/ e' T
    6 _. y) g, Q+ ~- Z: p& \s.str[0], X! T- N$ Q/ N2 O" v2 V
    Out[10]: 3 K" g- a. a! r& m" ~
    0    a
    # X" P$ u3 {  D/ p2 M  w; P) T1    e4 C) R& N" I  ~) x
    2    h& [# t6 r) l+ L
    dtype: object
    ' I( m9 H5 y: j% H$ I5 D, Z1 P$ m0 E) L# D, X- K! ]8 \
    s.str[-1: 0: -2]
    * C$ ]. z7 ^1 \) ^: m0 I0 V2 ~  sOut[11]: ! K6 V9 ?! ]  g; K
    0    db* U, B* }/ M& [% M
    1     g
    3 R5 N- i2 w* @2     i
    4 I  D; C, L) Rdtype: object. S* R8 X# }# I# t6 `3 s" t# G
    7 e9 d- W* v/ x' k
    s.str[2]
    , D. J  K, F& aOut[12]:
    " R  L* k& t2 c7 q% c& t% r0      c
    - n* [2 }  ~( V; ]3 i0 R/ B7 R1      g
      H7 F& s9 J, H! h: o' K1 s2    NaN, P* f; R; Z) A! \  B5 X
    dtype: object
    : p! |& B, N5 X* U' p9 U5 m# E+ }& a+ X: `% ]( m/ `
    1& `& R$ g, e$ m- |" |+ _
    2
    1 P- Z4 K5 Q; u6 {8 R; y- {3
    / e; r. b+ P- a3 Q4
    6 [: C* S" Q+ x" \9 c56 J" d; P4 V3 ?
    65 ^; }- U4 I7 k  o
    7
    . \) m) A3 ~0 m5 `# C7 V86 H* P8 f8 g/ ^9 A
    9
    ; |: E6 j' b. e3 C10
    / q& p, i" E& m( m/ _% L* k11, f6 s0 ?/ m; ~1 b! n
    12
    " w+ |& T" i3 N13
    " d- a& r4 m0 ]( ?3 k: y$ k2 |14# J: O& n$ g3 {/ M
    15
    % D" a+ L) I6 y( @& h( O" x16
    0 u! i  z8 v1 K8 R, \17
    " o% O! I+ R) D& ^18
    0 y. z" {) X5 b, E: g194 `, h" f* `' |- A9 ^
    20
    2 W' z! H: D$ e! Z& T$ kimport numpy as np/ \( x, n2 h8 [6 C7 m% U
    import pandas as pd
    7 {8 g" D3 u/ B/ B- _
    7 s$ Z3 W  B( \$ ks = pd.Series(['abcd', 'efg', 'hi'])7 p, m. b5 x1 |6 c3 C( |
    s.str[0]
    ! v# f' M) w( f! z7 T  @1
    0 H/ @* c) h7 n2
    9 ]5 w$ v3 I" ?  A/ \9 T30 k0 @: s8 ?# h  u
    48 B( j8 y" u9 i& _7 x. p
    50 ]; D' }$ \5 o" ?
    0    a0 m- `4 C- o3 d
    1    e! T. o. Y/ R# h, f
    2    h
    6 m* E' r' U* _, q3 S+ O- Xdtype: object( z, Z5 V9 z& g- l8 A* }: w2 w( }  _
    1
    # D* H3 T, J8 W2
    . }0 ~! e8 a( _* Y& I# Q$ ]- W" i3$ v- x1 D+ w" ]/ v4 D5 {8 U7 ]
    40 [8 j9 e9 y$ [* Q: @$ T) ~6 W& x
    8.1.3 string类型
    8 Q, K! Z4 d) g4 ~4 d9 B  在上一章提到,从 pandas 的 1.0.0 版本开始,引入了 string 类型,其引入的动机在于:原来所有的字符串类型都会以 object 类型的 Series 进行存储,但 object 类型只应当存储混合类型,例如同时存储浮点、字符串、字典、列表、自定义类型等,因此字符串有必要同数值型或 category 一样,具有自己的数据存储类型,从而引入了 string 类型。6 v$ M/ r. z8 e6 z& J
      总体上说,绝大多数对于 object 和 string 类型的序列使用 str 对象方法产生的结果是一致,但是在下面提到的两点上有较大差异:
    7 ~2 f! N9 Y: I6 n. g( h4 k. @$ n) Q  t: y& C) g* b* h. U- S! o
    二者对于某些对象的 str 序列化方法不同。
    3 R2 x) g1 m' W# T- }1 s+ d可迭代(Iterable)对象包括但不限于字符串、字典、列表。对于一个可迭代对象, string 类型和 object 类型对它们的序列化方式不同,序列化后str对象返回结果也可能不同。例如:6 {" W" n: S' R  P
    s = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a', 'b'], 0.5, 'my_string'])
    0 K% S+ ]8 X  O/ p3 Z  W- q% gs
      A3 T' k1 f, a: C" }* L1
    7 p9 g9 a/ p, D/ K. b9 i7 ^' x  T" a2
    6 b- N" F3 `5 q7 y: l( C0    {1: 'temp_1', 2: 'temp_2'}
    ; N; h0 W, G' n4 l# G/ s2 ?( f1                        [a, b]+ f$ F# F8 h* ?0 m
    2                           0.5
    # i- `, ?4 T! T* ?3                     my_string
    / p, i; E/ S) z1 h6 [) e/ ~dtype: object, f1 v+ w; s! |6 C; N8 g
    1
    - A4 h' c0 F& y8 M2
    - \0 b1 U0 X3 ~- ?31 t" d" S% O5 x9 X, A3 T
    4
    + ?) k) X8 O: m! W( M5/ M; j  [7 p2 F/ p
    s.str[1] # 对每个元素取[1]的操作
    8 O- B8 {6 x. y/ Z& y- M" W; R; W1
    / l: m/ v/ x6 z2 D! i  u5 }0    temp_1
    7 |' V, M; m8 W9 h: O1         b
    " M1 @; ?& e3 Z7 @1 _8 S2       NaN
      e# ]+ e; s3 q: c3         y
    ( t7 Q  ^4 s# f2 r+ ]8 o& Jdtype: object+ v- k5 H0 ~0 p
    1
    ; Q: M8 ]4 d) z2( E6 R( }5 g6 |
    3  t4 ^% }  Z4 x/ j) \
    4
    " u. p& k9 N" n. W8 }5
    % U9 R$ u( @' V+ R" h' d5 @s.astype('string').str[1]
    / y' @2 o0 s7 m/ f3 A( @1% I& p$ a3 {4 }: V" _9 w: e- H# R( G
    0    1; b, M( v3 B+ a3 U( o5 h( P- Q
    1    '
    ) v" S. h" L$ K7 F: y8 u2    .
    3 d7 Z8 r: X1 O8 n, n* b- I$ X  p/ ^* L3    y
    : l9 K9 S+ \  {3 K. h% qdtype: string
    2 A1 E- H9 m7 A# |1) r! Y+ h/ p( p) \! G9 c( H- o
    2
    1 U6 T+ h" [: J3 s, _3, J5 }8 u, K8 W( R+ w1 A$ Q
    4- P/ |/ l7 K9 t1 }
    5
      t' F1 o" @7 J" s除了最后一个字符串元素,前三个元素返回的值都不同,其原因在于:3 k  X- J2 f# S; v
    % k2 Y7 J0 u( i4 x, x; W$ O# J
    当序列类型为 object 时,是对于每一个元素进行 [] 索引,因此对于字典而言,返回temp_1字符串,对于列表则返回第二个值,而第三个为不可迭代对象,返回缺失值,第四个是对字符串进行 [] 索引。
    : m7 r0 {; @% J2 Fstring 类型的 str 对象先把整个元素转为字面意义的字符串,例如对于列表而言,第一个元素即 “{”,而对于最后一个字符串元素而言,恰好转化前后的表示方法一致,因此结果和 object 类型一致。5 I2 r( P: e5 W- _1 }4 L! N
    string 类型是 Nullable 类型,但 object 不是
    + ~! m  \1 U9 }) L2 m8 e  这意味着 string 类型的序列,如果调用的 str 方法返回值为整数 Series 和布尔 Series 时,其分别对应的 dtype 是 Int 和 boolean 的 Nullable 类型,而 object 类型则会分别返回 int/float 和 bool/object ,不过这取决于缺失值的存在与否。
    . L( @7 G- F0 t5 V- K8 N' W: J  同时,字符串的比较操作,也具有相似的特性, string 返回 Nullable 类型,但 object 不会。
    4 F# q8 E" X8 L! Hs = pd.Series(['a'])9 ~0 c  X& H5 o7 O* G$ a
    ! T" K) h% t3 Y8 v: E) `, `3 a) h3 q  c
    s.str.len()
    ; n+ W" j' v- `' O5 yOut[17]:
    / d; j3 ^& u; c* C0    1" w1 |- [# I. X/ ^1 a
    dtype: int64
    3 i0 v3 M) o0 e5 E) B8 C0 ?: k) b  {& B9 Y$ m  P  _
    s.astype('string').str.len()
    6 {# u' ]2 g7 V' j! BOut[18]:
    % ~( i# l# N) L0 ]0    12 H2 |# g% K- |# G' E1 p
    dtype: Int64
    , g( R* `) v. H; R* o( {" X2 K9 M
    * c6 r0 @& D- w  P. M. ls == 'a'' O! [+ h% N+ Q9 W! v. ^4 l
    Out[19]:
    " L/ H# V' S+ l' b1 P6 c0    True
    4 H' Z+ I0 U4 i$ Z1 \) o" Hdtype: bool2 h9 f; J, T" O+ t6 D% J+ F- V  M( B
    ( t* n# R" m2 D. e! l
    s.astype('string') == 'a'& x+ h' \2 W5 ^. }
    Out[20]:   r3 [  `$ i7 u$ H
    0    True
    * `/ g6 `% B0 K" `# hdtype: boolean
    " {% i* ^: Q8 F6 g+ ~7 q  M# D5 e0 G- |# @6 ^3 ~5 J
    s = pd.Series(['a', np.nan]) # 带有缺失值
    . T) v- i! a: S0 X3 L  d6 j
    ; v9 g# H0 g( @3 Bs.str.len()
    7 h7 h( P. d/ EOut[22]:
    . O$ ]# V9 r# j; z+ Z6 y0    1.0$ A: R* H! S4 ?" B
    1    NaN# I- s- [  ~; s' s
    dtype: float64
    * ^5 J  h9 w* X8 }; x
    ; M/ l* Q! M. Ns.astype('string').str.len()* s3 c6 G+ b* v: V) B
    Out[23]: 5 }0 U0 T) Z( M9 w7 s* b
    0       1/ Z0 w- T4 K7 H
    1    <NA>
    4 |! d  R$ k2 A/ c+ `  q0 E" z& Ndtype: Int64
    - H# m1 B! l, V% S  f4 c6 Q/ D
    4 [& E2 H) P1 Y9 Gs == 'a'' `; t* a3 y% R2 E
    Out[24]:
    7 s7 i( ^8 V* [0     True: ?' C  x) |& f' }0 }% n# {
    1    False% ^% V6 m- H. y
    dtype: bool, }, E; k" U4 q9 o3 W( f
    & N8 Y" x7 Q- y
    s.astype('string') == 'a'
    : D, B- X0 P: [Out[25]:
    ! F9 ]# s& h3 v0 |% P6 R0    True
    9 S( C0 E# M5 l2 M2 N$ V2 M2 Q( z1    <NA>) X% N6 `8 u' O# Q/ U# i# V7 ?
    dtype: boolean
    ' T3 w, R  S# x& L7 ~6 z; G
      }2 ^0 c6 p- `6 H" u% W1
    + N, x" m: w0 e4 R# j8 h" _, H2
    9 J! T5 d; C, N0 x+ h2 w9 s3
    ( G6 y1 j1 m- U. z& J4' |! g) a. @5 ?  g/ \$ p: C0 M# x
    54 r4 E7 n. q( G
    6
    / J" |" f2 O0 ?+ m* X- z7, ~. E( p) c6 \1 D
    8
    , ]& p7 K$ }1 F9# z* F, _  ~. f3 B5 D, G1 `
    10
    ) ^6 }) g( m" W; O) X" S11
    6 [4 M! F% j& x125 \3 k  A9 l% O7 h, g. Z9 i
    13
    , e- Z6 _, |2 ^2 V, u; V14% ~# ]5 E- U7 ?, {+ A6 L6 k
    15
    , Q6 J  a3 g1 i+ p9 e* C6 r16
    % H$ ^- v& G) a& T2 [' g8 P5 N( y17/ @- K0 B9 b& q
    18
    " X5 M: O6 ?# n" j19* d! V$ W7 j6 h/ h, z- W3 H
    20: P& X  W. z4 W9 d; e7 w
    213 J' q4 Z& i( c6 c" [+ R: y
    228 Q% I& N, z0 E4 B1 Z! g  i
    23
    7 H- M' l- u. c! B- `24
    3 @6 d# [: q8 N9 n25, H8 e2 O2 L4 H+ K- d( W
    266 w# I2 }; F. G9 y8 l0 V: Y2 ~4 v
    272 C' C% {6 E. J  Z/ L
    28
    4 {# J$ ]/ K& c! e29; t* P' g. }0 v" b, v
    30
    : j* `/ j( H, `  v% Q6 c5 E31
    7 O6 c; N) B3 k32" a- j( e3 X, g7 R7 ~  ^
    33& t/ [# h6 Y3 {+ B# v1 y6 R
    34
    / l$ S+ q/ p6 Y5 ?5 r$ j35& J! x6 ~) U; Z# l/ c3 C+ e( w
    365 P4 G8 j4 q1 x! Q# U, y
    371 f; d8 l" P& q/ M2 @( G: z
    38
    4 e6 T. u2 ~0 `+ ~+ v9 v39
    - M3 v( a: k4 c. e* O) B4 _40
    - j7 m3 M1 P1 O  ?# a- `415 B/ N: ^$ T; ?
    42
    $ I4 L+ {" I# ]5 I, \; ]) S1 U43- ~# {+ v) E2 v4 K
    44
    , I: _' e/ A$ x# U0 n45. j  S$ Z, ~7 b7 [8 T% N4 d
    46
    6 t; X. I' P! `47
    8 r+ H: @+ ~  k) }$ ^2 N& ^& ~  对于全体元素为数值类型的序列,即使其类型为 object 或者 category 也不允许直接使用 str 属性。如果需要把数字当成 string 类型处理,可以使用 astype 强制转换为 string 类型的 Series :
    # h+ g  c) h: @# D7 W
    - A' _3 h2 W: B* K. I0 `/ K* [s = pd.Series([12, 345, 6789])
    ( p- ^! q# A+ u( G6 V) r- x4 C/ G& {- N! f
    s.astype('string').str[1]
    ! i2 R' E  v" a; E0 v0 I. F+ GOut[27]: 1 y+ h0 F1 I& L6 j) Y. L! m
    0    2
    % }& i! O, p: U6 m( K1    4
    5 I- I6 @( y( A2    7
    ' o. E3 X6 K  f6 x# L  @( t" B2 _dtype: string/ N* O3 O' j4 ^# o
    1
    " r! ]8 e3 W( k: ]; \6 m( y27 T! D# |1 [& E0 s7 t
    30 |, Q/ L$ I2 z# ?" [/ Q
    4
    1 B5 l! S5 I' R. M, C" w' p5
    9 Z' I+ ^$ V0 h' ^' `7 _6. p; Q: L9 h; u2 O
    7- q& l) f) F) G& N! i& |
    8
    + o- E9 ^0 G/ `$ [8.2 正则表达式基础1 l* j. }/ B/ {8 @
    这一节的两个表格来自于 learn-regex-zh 这个关于正则表达式项目,其使用 MIT 开源许可协议。这里只是介绍正则表达式的基本用法,需要系统学习的读者可参考《Python3 正则表达式》,或者《 正则表达式必知必会 》这本书
    # z! S/ m* y' s% }% j0 {
    # r: q6 L( N6 y. [% Z# s, h8.2.1 . 一般字符的匹配
    8 s1 F2 P6 o3 ?! [* s; r正则表达式是一种按照某种正则模式,从左到右匹配字符串中内容的一种工具。对于一般的字符而言,它可以找到其所在的位置,这里为了演示便利,使用了 python 中 re 模块的 findall 函数来匹配所有出现过但不重叠的模式,第一个参数是正则表达式,第二个参数是待匹配的字符串。例如,在下面的字符串中找出 apple :
    5 S2 o, p  e2 }
    1 |1 p9 h) U' \5 s' Z1 {7 Ximport re
    0 v' y4 w* Q  R0 n( g( E
    4 W+ k3 z6 B% X3 z2 qre.findall(r'Apple', 'Apple! This Is an Apple!') # 字符串从左到右依次匹配/ O+ d1 i# `  Z5 I" v- l% |, _: p# ]
    Out[29]: ['Apple', 'Apple']
    , [3 h7 A* }# n9 V; X, P& e( U, g1  _. Y# v- w( E: H# g4 D
    2
    . w# y7 G8 k2 U6 D- k0 L3& c# Z  n/ Z, {, U4 ~
    4! n: ]  Y, M/ L" R' k
    8.2.2 元字符基础1 q! g5 N+ w2 i3 v3 U
    元字符        描述& B4 b0 \+ _+ D0 A1 m& k& X
    .        匹配除换行符以外的任意字符
    3 Q$ H2 W: F# E; o3 V4 |. a[ ]        字符类,匹配方括号中包含的任意字符
    . g7 t4 h; }  m+ {5 l[^ ]        否定字符类,匹配方括号中不包含的任意字符
    4 J" a. ?/ ?( M* X6 [*        匹配前面的子表达式零次或多次/ l3 P/ |1 K+ V3 ^; {( g- O  [
    +        匹配前面的子表达式一次或多次。比如r’d+'就是匹配数字串,r’d’就是匹配单个数字
    , {' y; B/ M) {3 v8 _/ E" n, g! B% k?        匹配前面的子表达式零次或一次,非贪婪方式/ L& L8 t- Q4 S  b- l% f
    {n,m}        花括号,匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式1 [! `0 G7 X9 d) U' }% D4 ^
    (xyz)        字符组,按照确切的顺序匹配字符xyz
    3 ~" T/ ^% X  p) D& e|        分支结构,匹配符号之前的字符或后面的字符1 G8 d: {' c% O+ o
    \        转义符,它可以还原元字符原来的含义( a& W7 W4 M! I* k. p
    ^        匹配行的开始# ?/ ^  P) Y$ K) ~/ X7 _
    $        匹配行的结束
    : t) P, P1 |5 r. Y. i: r1 qimport re
    " g; b0 q9 B, H. D  \$ n' are.findall(r'.', 'abc')
    $ z+ _" H, t. u# e; WOut[30]: ['a', 'b', 'c']
    " _' |; O: q+ l* D4 U( e- t# E6 z, H, z! S, ]( c. I9 d/ D0 }$ b
    re.findall(r'[ac]', 'abc') # []中有的子串都匹配8 x& i% t  I. g  c! M
    Out[31]: ['a', 'c']
    0 M8 m. z- w. J7 y4 D$ q0 D
    . K; l& l5 z1 @; nre.findall(r'[^ac]', 'abc') 0 `" E$ a  K. S  p7 l( ?0 ?
    Out[32]: ['b']
    , _  F: D  W1 {; {0 W+ r8 {7 f/ }! c' k
    re.findall(r'[ab]{2}', 'aaaabbbb') # {n}指匹配n次4 Q5 L; }  C8 H7 k' P/ y
    Out[33]: ['aa', 'aa', 'bb', 'bb']
    ' y# Y6 ]2 Z  [/ Q
    ) f! r: A( }! h2 L& \* X! A8 wre.findall(r'aaa|bbc|ca', 'aacabbcbbc') # 匹配前面的或者后面的字符串
      G+ w9 J1 w4 G5 x' X0 mOut[34]: ['ca', 'bbc', 'bbc']
    % g7 e- Q$ P. y/ o  \" i
    , M9 s5 m; b" h) W" H& T3 a/ R/ w# 上面的元字符都有特殊含义,要匹配其本来的意思就得用\进行转义。) R# `" L  Y; [0 k& t  u7 B- H
    """4 x1 i! S% q& {4 Q
    1. ?匹配的是前一个字符,即被转义的\,所以|前面的内容就是匹配a\或者a,但是结果里面没有a\,相当于只能匹配a。6 i( C) Z- u3 g
    2. |右边是a\*,转义之后匹配a*,对于竖线而言左边优先级高于右边
    7 X# B' \: r$ J1 X8 \+ N! V8 Z3. 然后看目标字符串aa?a*a,第一个a匹配左边,第二个a匹配左边,第三个a虽然后面有*,
    & H2 o% R, i# ~' ?* }3 ^但是左边优先级高, 还是匹配左边,剩下一个a还是左边,所以结果是四个a) u4 F" z& r9 _2 W; w4 n" v; ^/ o% T
    """
    # M  ]0 n! T4 y  w' s
    # z( F* N. d# E. Ire.findall(r'a\\?|a\*', 'aa?a*a')   # 第二次先匹配到a,就不会匹配a?。a*同理。
    ! `1 M9 i( a$ ^% mOut[35]: ['a', 'a', 'a', 'a']
    + ~7 A# q0 [& \* V  O$ v" b" w
    / H( g" M: J1 w! P. z# 这里匹配不到是因为目标串'aa\a*a'中,\a是python的转义字符(\a\b\t\n等),所以匹配不到。4 k& N1 j$ ]; O: I4 q! V
    # 如果是'aa\s*a'之内非python的转义字符,或者'aa\\s*a',或者r'aa\\s*a'就可以匹配到\字符。) |6 [) O4 O( r8 I+ L
    re.findall(r'\\', 'aa\a*a') + d0 f9 B  \2 M8 n, G: `1 [
    []- j  Y! |2 y' l0 q- k; v* w" w
    ' r3 z" I  W& `- Q8 K
    re.findall(r'a?.', 'abaacadaae')( v  G+ N$ ~: p& h; j7 r. Y1 R3 f
    Out[36]: ['ab', 'aa', 'c', 'ad', 'aa', 'e']/ w  z7 l' ^5 U% I
    ; i2 t; O8 Q" r% |* v, t
    re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10') # 多个匹配模式,返回元组列表
    9 o- Z3 F/ K) w' ~; g. P  T[('width', '20'), ('height', '10')]- a+ d$ L( p3 K' k" k

    9 c$ `! g7 Q/ ]1+ M9 N& s% V, a& f2 k9 ]' H
    2
    # d" f: X4 _* f3
    $ P% t. C9 O5 S4- m# m2 _" }) q- S! ~
    5
    & V6 F( j: M, N+ {* G/ l6- ?1 w/ N+ k2 M/ U& S! U" g: a
    72 K! x: ?/ P) O: H& ~( O, A7 q
    8
    + c  O$ R* t0 e/ g9  A" d, z2 _1 _6 c: A8 \$ g1 S
    10" L" \; Q, `& f! ~( L& l
    11+ [+ T/ S) s9 H( _4 ?
    12  H# V+ Y  e1 _8 J+ @
    13; C8 g  ^+ u5 [6 K1 x; C  V
    148 W0 j+ y' I9 H4 _
    15
    " p3 m3 q0 ]" |" ~8 r8 ]! c' _; O16" Y8 g$ I0 Q3 t0 [- M
    17
    / A% _. A0 u( g18! ?* H) z/ e( a7 Y# g+ y
    192 R! I) O0 V; t& H$ @+ H* a. A
    20
    7 ]1 h+ M3 p& j, k21
    4 t0 {* G- ^! T) Q22
      j- p( q, c0 E$ A23+ o3 F  \2 a) w7 h, O, c* G. v+ f0 D
    24/ H, x- b+ t6 [- d: T' H
    256 M" i/ P4 E% d5 _5 Q
    26
    1 E0 _! t) N1 G' U! S- U- p27/ L" P/ S% h7 c' O9 C
    286 G* o( }' d, T1 o5 Z
    29, i  w# R3 P0 |8 C0 l; L% A5 ^
    304 R7 \6 |" p; l- X% K
    31
    ) w7 n: _4 }! l5 {# m; F5 s2 {32
    4 m9 S  [9 O8 a- r' y2 k33* H; [/ c% T  V" {" O
    34
    " M% W; v7 N" z) g35' N( T; J/ }: B) ?3 u
    36
    6 N1 M; d# r, ^9 H# S37  s$ K: A9 O8 V/ G" L9 t& q. f+ K
    8.2.3 简写字符集( M& n8 I3 m7 t& [% B) @
    则表达式中还有一类简写字符集,其等价于一组字符的集合:
    ' I6 L3 H7 u) w! S* d
    * ^, t. [$ K3 h9 i1 L4 J简写        描述
    5 i; A# }  B- B, p. w% g0 I\w        匹配所有字母、数字、下划线: [a-zA-Z0-9_]- r- h6 r  N% h5 u$ f! L
    \W        匹配非字母和数字的字符: [^\w]! ]; _; |/ i" \. ?  r0 w2 l
    \d        匹配数字: [0-9]+ |9 |- R" g. a( U# _; a" i4 N
    \D        匹配非数字: [^\d]3 h5 ?3 e7 @: g1 W" x
    \s        匹配空格符: [\t\n\f\r\p{Z}]5 P) w  T: T) `7 l: r
    \S        匹配非空格符: [^\s]  o" |# H8 o* k, z, F0 ]) e; P
    \B        匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。
    ( k. E6 q5 _: f. S$ x/ H, o1 {re.findall(r'.s', 'Apple! This Is an Apple!')
    9 c: q. i' C/ o& Z: a1 w7 J( G. b4 QOut[37]: ['is', 'Is']7 w0 |1 O6 e  r( H

    : n+ R- d6 R+ p4 O. x0 l9 a6 C, Pre.findall(r'\w{2}', '09 8? 7w c_ 9q p@') # 匹配任意数字字母下划线的组合,但必须是两次1 n. \* D5 V7 @" K6 J7 i8 g
    Out[38]: ['09', '7w', 'c_', '9q'], V% t) L) x% C7 q3 W
    # U) a' P( {7 r/ G3 @6 R% i# I1 N
    re.findall(r'\w\W\B', '09 8? 7w c_ 9q p@') # 匹配的是两个字符串,前一个是任意数字字母下划线(\W),后一个不是(\W)
    . v6 A3 F. N. }3 D! wOut[39]: ['8?', 'p@']
    ; X5 t- `2 }! N2 r8 P9 G! C8 U! q) f1 y2 H# E2 ?
    re.findall(r'.\s.', 'Constant dropping wears the stone.')
    ) [) @9 ]7 X% z; ~6 pOut[40]: ['t d', 'g w', 's t', 'e s']
    & ]$ E# t/ g, O! ]  ?$ a
    ' J& N7 U7 l4 k( Ere.findall(r'上海市(.{2,3}区)(.{2,3}路)(\d+号)',0 _( u8 M( @' d) \" P
               '上海市黄浦区方浜中路249号 上海市宝山区密山路5号')
    ' u* }; H$ r  a: Y; N6 `$ K5 j$ ~5 P/ b! k% k
    Out[41]: [('黄浦区', '方浜中路', '249号'), ('宝山区', '密山路', '5号')]
    0 y# K/ b7 z" b* r: z! a
    ( D& d2 g" o* f$ c1% }/ l7 S3 s8 J- y5 C
    2
    6 a1 u& p; L0 z" L3
    * I+ Z+ e- C$ y; P; b4
    / }$ D9 g% E$ A4 K3 [6 C0 s+ S5
    ( K1 C; k; f) a: {$ s6) N5 n0 ?. ~, ^3 p  h
    7
    # g; i! n4 E  J& x* W. l+ R4 R8. ]( h) v+ R' i" c
    9
    ) `: O' S# [3 k: T% ]9 T6 N10( x' h% R# N" V; c4 D
    11: q9 E. e: K5 @2 F
    12$ H5 y4 z9 J5 b& D$ A
    13" U8 h  g9 X% ^. D& K
    14& u  P7 B+ e' I
    15# ^$ X" J% y% ?2 }9 L- z, u
    16
    8 y- ?( B0 J$ g' ~) f: k+ q8.3 文本处理的五类操作
    2 K2 K" W# Z, z! q% G/ B7 p" \8.3.1 str.split 拆分; J, G/ |0 T/ J: v8 Y8 a2 J/ X
      str.split 能够把字符串的列进行拆分,其中第一个参数为正则表达式,可选参数包括从左到右的最大拆分次数 n ,是否展开为多个列 expand 。
    3 r  e: p. ]% O6 z" H! P* ?8 B9 O& X2 V; {/ G6 H5 u
    s = pd.Series(['上海市黄浦区方浜中路249号',+ p, L/ H) L2 G7 U+ B1 T' M
                '上海市宝山区密山路5号'])
    3 s4 P0 J/ W4 y, Q9 K/ o( [6 L
    6 x8 h: Y3 \$ K, H4 n: V% s) L, O& h# d
    s.str.split('[市区路]') # 每条结果为一行,相当于Series
    ; {/ h% l; x4 l3 t9 yOut[43]:
    : K% z7 A% s; v6 W0    [上海, 黄浦, 方浜中, 249号]
    8 ?7 ^" s* c4 v! s% ]1       [上海, 宝山, 密山, 5号]
    ' p# p0 h6 B, H, |& Ddtype: object
    6 W  ^9 k5 c+ X1 @* N) M
    ( Z2 _8 b; J8 E1 ds.str.split('[市区路]', n=2, expand=True) # 结果分成多个列展示,结果相当于DataFrame
    ! D9 O2 |/ k9 W9 m, `, oOut[44]:
    - Y) p% O5 [" u! h    0   1         2
    + v: r8 V* v: I0 R9 C' w0  上海  黄浦  方浜中路249号
    " |2 ]! N3 i7 N1  上海  宝山     密山路5号
    ( m; A- t  B4 s6 o1
    # i0 v. B/ b$ }/ C- b  h2
    8 o5 A/ |/ h6 k( C3
    , Q* _3 Y# V2 E. B$ Y, x49 \1 V1 p( E  E$ @* A/ Z! Q7 s
    5- \/ t, ~( L! M7 z% t
    6! g8 b: b1 A1 p5 W6 w( |
    7: S: D; Q# K* B  b
    82 y' u2 B3 x4 \
    9
    / r! [' V; d1 ~. \0 p) p& ?10
    6 u0 T: q! w# o3 q. Z; n# K5 E/ S11% A2 w( {5 ~5 G$ \0 c+ T& |+ `& H5 y
    12  k2 ^# F6 J1 t6 g
    13
    0 {" d5 K  g9 w- T14* R, y, {( h1 M( d4 |( V* S
    15
    3 H) z! r9 V2 [( S4 g3 _0 c  类似的函数是 str.rsplit ,其区别在于使用 n 参数的时候是从右到左限制最大拆分次数。但是当前版本下 rsplit 因为 bug 而无法使用正则表达式进行分割:: I+ T' j: g/ A/ K5 ^$ s7 h
    3 W. E" v6 d* |6 u, b( |
    s.str.rsplit('[市区路]', n=2, expand=True)0 f; l3 X) b+ p/ C
    Out[45]: ; O) @' k# U" Y7 L
                    0
    & M7 R+ H0 ^4 Z5 p4 {0 R0  上海市黄浦区方浜中路249号
    , |: [4 Q/ J* Y# P% y' H1     上海市宝山区密山路5号
    # S! p  T$ a. P9 @; T6 W2 f: S10 u: ~8 M& Y- O: p0 d
    2" ]& W: K' ~* M2 Q
    3
    1 j) V* g3 A' M- O1 v: t  V4# ~  s! P8 L4 h3 s
    5" S4 n6 |3 C2 ?" g; \& j
    8.3.2 str.join 或 str.cat 合并! U5 D3 K3 e% V0 }
    str.join 表示用某个连接符把 Series 中的字符串列表连接起来,如果列表中出现了非字符串元素则返回缺失值。. B5 B9 m3 F( r5 v: H
    str.cat 用于合并两个序列,主要参数为:2 H' I! ?+ `' i* b# `
    sep:连接符、
    # H; z: n" W; v4 W8 Y9 sjoin:连接形式默认为以索引为键的左连接8 e: Z& v$ y3 j; h& |' j2 S  F! R9 O8 k
    na_rep:缺失值替代符号
    0 q( a# d) p8 v" h( Z8 {s = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']])' y( a% o) @  o- S* D
    s.str.join('-')7 r- ?6 N5 R9 m9 b4 B: F
    Out[47]: ( {* @& t$ S# P( x) A
    0    a-b) l6 \: f# K0 i
    1    NaN
    3 _- S! m# Z# F( |2    NaN
    4 i8 y- M) j* T9 h+ Mdtype: object
    - Z1 ?/ G2 |" Y3 H1
    5 v+ ^% K- Z3 s* U2; P2 h1 z0 C* a: |4 C( w
    3
    ; j# l7 V" c( p# _7 N" M4* L6 J! V. ~; C# J7 {
    5
    ( X* R5 r& d  M7 ~6
    6 C7 ?0 ^- l# Y6 S7
    ' M  ?0 _/ C% ]+ ks1 = pd.Series(['a','b']); v' g) w+ b# T+ z* _( O% _
    s2 = pd.Series(['cat','dog'])
    ; O( P9 |% K& O. B/ q7 b* o$ r% rs1.str.cat(s2,sep='-')( Z* b( C* w: A) S! K
    Out[50]:
    ( U  V( x9 ]6 i$ F0    a-cat
    7 j$ ?' ^# X. s4 A  j- |! [1    b-dog& o0 q# z/ ?' i: `$ P: L
    dtype: object
    5 t: \2 t& O9 P/ l5 l5 J7 J3 I6 J" f4 B; U' p. n
    s2.index = [1, 2]
    1 n# P2 \! {# G7 Ms1.str.cat(s2, sep='-', na_rep='?', join='outer')$ r. l2 b- c8 u' D' h
    Out[52]: - a5 s; O, o6 T: N$ x% n6 E
    0      a-?
    - }) k" V6 L) B3 i+ w1    b-cat9 ^0 V5 p+ I7 o9 F* o9 z$ U) l" `$ l
    2    ?-dog- k2 R. S, v( t
    dtype: object
    : u: g8 @) B1 G9 P) l1$ e: F2 |# J; l) v; m2 X
    2! Q; C( r: k- G
    30 N! y; D+ O, }
    47 Q6 @* G6 [0 ]9 u. q& _/ {
    5
    7 k* ]5 H+ L# s; n4 C6( r& N0 k' s! S, F1 x
    78 ~! e. T0 d: W6 X0 E
    8
    , N* _) y/ L+ N# R' Y9+ g; F1 s9 P( a4 Y( i3 z/ N  Y
    10' A0 L: }6 P' P! B6 r1 d6 V  x
    11* P3 R5 F' B& G- N& c, H5 }
    129 p2 U5 q' b  k. ]
    13+ h) m0 j: e8 ]  h4 X% Z+ d
    14
    ' ]+ |3 k" C$ a$ [15! b( c9 e: S! T4 L1 Q3 A
    8.3.3 匹配
    0 X6 a: Z  E$ z4 ^str.contains返回了每个字符串是否包含正则模式的布尔序列:
    6 S7 v" A; W% V. V7 \; o. ~s = pd.Series(['my cat', 'he is fat', 'railway station'])0 |7 G& O( V% |
    s.str.contains('\s\wat')
    ( U. |' `* a, n$ X: C& A1 i1 n
    $ H# S' _# r2 F1 s0     True7 o0 @+ W/ S$ x; C1 I. _
    1     True
    + i# h1 {  k' ^: s- U1 m2    False
      u0 p* s& Z" E) q- p) r- J" ^dtype: bool
    , e( L. y% B' c" @  P11 _) {; m; N* [4 O
    2
    & f/ c' S4 ]- B+ J5 a  K3
    9 X0 g9 C! h1 {8 {) }  h42 c, M& v; H; O: k
    5
    / ^+ r' q+ f. C& f; a, K6& t+ z  v; u) [, B1 z
    7
    9 a5 I8 \; o9 G  d0 Vstr.startswith和str.endswith返回了每个字符串以给定模式为开始和结束的布尔序列,它们都不支持正则表达式:/ o' F: W4 l  i, }7 {4 N* Y: T
    s.str.startswith('my')
    * _9 c  ^; M  W& S' L: c& T  E
    / J5 e" u& f, K& `0     True1 w* R, s9 t! T0 A8 F8 z
    1    False
    . V/ w; w2 p& ^% a; B/ S2    False" V5 x: m# K6 ?  ]2 g
    dtype: bool
    2 ]* F/ u4 R) h5 L1 x5 M1
    ) `- _/ p3 Z( h! K  d" ?- P2
    ; T* b/ e/ f2 R9 w- t! @" w/ k3
    7 l, C* e5 V% T6 j! I4
    1 b. Q9 c9 W- T; H& U' T5) D$ R& M4 e( x& A* A8 f( y2 R8 f  B
    6" U# F2 p( l9 I- T. A0 ?% `& J/ [
    s.str.endswith('t')
    7 y/ S) ~0 U( ~& I: ?5 \" o9 ]8 v, h$ ]9 @6 V% v( {. {9 @; y4 Y5 C
    0     True2 z8 F" o; f) O  X3 V
    1     True
    / ?4 l! J! p1 D( W- `4 ^2    False
    8 p3 V2 y) A) f+ W7 ddtype: bool% i! J6 x* o( d6 }( A0 }' e; N
    1
    # l- m; S% P* \/ H/ `2  c: z# Q/ S. j& M( B" m
    3+ N4 ?9 h, U+ g2 [( A6 h
    42 Y. e) A4 J3 I" X: N+ t% ]; \
    53 ?' o7 m; g; [) d- M8 J
    68 ~1 G. A% l# _
    str.match可以用正则表达式来检测开始或结束字符串的模式,其返回了每个字符串起始处是否符合给定正则模式的布尔序列。当然,这些也能通过在str.contains的正则中使用^和$来实现。(貌似没有python里的search方法)
    7 C0 k6 B/ {' D/ P' V. ?s.str.match('m|h')5 S) L# f; c" ^& G% B
    s.str.contains('^[m|h]') # 二者等价
    5 R; e& A$ c0 c' A' X$ U
    ) H/ [" }" Z7 ?4 F0     True" _" v5 U7 Q" m3 x
    1     True% C. \# A" }" e6 C7 _  K, @* J0 w
    2    False
    2 R! u/ \8 S  s( r2 w9 mdtype: bool% g- M' }8 G$ R6 P
    1
    ' O! s9 ~" P1 F2 D( Z" \' z2 T2
    & {8 N0 C  m- j9 p! @: _: w3
    8 A) F+ o: ^5 h5 ]" e4" K3 d4 ]4 r: j! S5 x
    5( d/ F5 y  h: E* B% O
    6& {/ |) g- p1 ~2 S0 k  }" N4 Z! z
    7
      K) C0 J* S  _. q0 ?s.str[::-1].str.match('ta[f|g]|n') # 反转后匹配
    . L$ z" a' ~: N6 ~  \' Ks.str.contains('[f|g]at|n$')       # 二者等价& C# P( s+ p$ d; }+ k' T. [' v
    % I" z0 l3 W7 c2 x) n' n
    0    False# o- B% J' r& D" }  Y" ]% u# r5 K  N( a
    1     True0 n) O3 x7 n  `
    2     True
    / B) w8 Z. f; A  i% ]8 ~& {dtype: bool
    4 R/ [2 ]/ m8 I1
    ' r7 C  p: j/ z8 Q0 v2
    . a4 t1 e2 Y7 A0 M0 E& I+ B32 z5 c1 V  x7 }
    4, S8 X3 U% e9 ]4 v! h$ L+ P4 |
    5; a; F1 Q1 Y4 v% J
    6- C7 U" e4 D6 B- `+ [
    7
    6 _8 _, ?6 T- m! d+ a0 Ustr.find与str.rfind返回索引的匹配函数,其分别返回从左到右和从右到左第一次匹配的位置的索引,未找到则返回-1。需要注意的是这两个函数不支持正则匹配,只能用于字符子串的匹配:
    ( d8 g3 i# s6 t( Q# ys = pd.Series(['This is an apple. That is not an apple.'])
    - V6 @, d; B' Q$ c: h' M  m. X3 K4 H# n! B1 k
    s.str.find('apple')2 O; R, ]/ d' P4 A& j/ ]
    Out[62]:
    ( p, s: M8 q& {) \: Q- `7 F2 o0    11
    % c$ B! {0 t1 \8 X  K/ p4 y' Xdtype: int64
    1 v, e4 e6 Q6 O3 Z- H# H2 X
    * q9 m5 e7 g8 w" t0 _$ }3 s/ [, q  ms.str.rfind('apple')
    0 y1 P- y) u; h+ c) MOut[63]: ( Y% i$ e0 j$ v9 L: |8 J, G+ o
    0    33
    4 R0 l2 }" N8 Wdtype: int64
    % y5 b7 p$ j, B& g8 x( ?1) p# ^! }/ q, ]8 X: V  ~) a; u# V
    2
    * }3 V( r5 F9 I+ N& B1 t3
    / W& v4 L  p9 ~; v- w# z4, u: |8 G, u3 Y5 {  X3 w9 X# d/ D: F
    57 H9 ~: {; J( d* e( p+ l
    6" ]* ?2 ~- Q; p+ R- E) o' {4 ?3 h( V
    74 v2 [" [1 o* R/ G( u* h
    8
      F/ a2 H- K/ y9# _+ D4 W, O; {7 D9 [7 W
    10
    4 ?# z( [0 @: M0 K) K110 e8 V5 I  l5 k7 n& x4 A' g, z& Q; o. m
    替换( O1 F: D2 b# ?, F8 X! ?
    str.replace和replace并不是一个函数,在使用字符串替换时应当使用前者。7 m# h2 S9 @& ?$ O- F/ G" `
    s = pd.Series(['a_1_b','c_?'])3 w7 \2 K, J9 m" G7 t% C
    # regex默认为True,表示是正则模式,否则第一个参数内容表示是单纯的字符串,也就是匹配字符串\d|\?* W' Z! A% K. X! X- {0 i- I  X- ]+ e
    s.str.replace('\d|\?', 'new', regex=True) 9 E, a* F( _+ P2 @  i6 }( R2 ]7 M0 O
    ; Q+ d1 K  ?* K$ j0 w
    0    a_new_b9 R- m+ y+ t; T# S4 U) @% E
    1      c_new6 h  X; J$ }/ b. Y; s7 v9 C
    dtype: object
    & c5 T6 s: m; R9 {1
    + r9 E& i( {* p7 ^1 s5 g2
    $ e+ b' z1 L1 V  O+ f3
    - `8 F/ U# @- j2 j. ]3 R6 E4
    ( t8 J9 Q. e& D: ~# R9 X+ n0 Z: ^8 ~5; g3 U5 [( G( s4 O/ h
    6; F+ ~% [0 Y1 W, L* E( E" l' f
    7- [, F, D$ F, V: C
      当需要对不同部分进行有差别的替换时,可以利用子组的方法,并且此时可以通过传入自定义的替换函数来分别进行处理,注意group(k)代表匹配到的第k个子组(圆括号之间的内容):' d& e" K& G3 c" `" Y
    ' b3 @7 K5 V5 p# t% G7 H
    s = pd.Series(['上海市黄浦区方浜中路249号',4 k" _8 B, x+ l8 d+ x" P
                    '上海市宝山区密山路5号',
      C7 M. P* E/ o/ q0 i, s                '北京市昌平区北农路2号'])% L1 b; o" o7 T$ C
    pat = '(\w+市)(\w+区)(\w+路)(\d+号)'% i5 B7 {5 ~/ s2 s9 b) t
    city = {'上海市': 'Shanghai', '北京市': 'Beijing'}9 C! _. Z' E. o- n5 ]3 D1 J7 x
    district = {'昌平区': 'CP District',
    $ }2 R( ?% X5 \            '黄浦区': 'HP District',$ d! z) d. v9 N7 \4 c( V3 S3 V6 L
                '宝山区': 'BS District'}
    " e4 _# J: l; q7 K0 L9 X: Jroad = {'方浜中路': 'Mid Fangbin Road',
    # l7 E# u4 Q, L) x% b& E7 D* C        '密山路': 'Mishan Road',
    + `% X! y+ I# D        '北农路': 'Beinong Road'}7 U( Z4 ]* I8 G2 ]! |/ \
    def my_func(m):6 u1 Y' s  k: d
        str_city = city[m.group(1)]  F- {# f8 o2 p/ o/ s* H8 D# h% }
        str_district = district[m.group(2)]
    % {7 y% }" U8 C" h    str_road = road[m.group(3)]4 s! u- w9 [/ u' k
        str_no = 'No. ' + m.group(4)[:-1]
    ' u8 C. c! b' K! a- K- x    return ' '.join([str_city,
    0 m2 h; K& m* p& H! G5 W                     str_district,
    * d" j: K' ?- x& M$ _8 n* C                     str_road,
    + `7 I( ^! R2 O3 @* n& h  e                     str_no])
    $ r9 A' Z+ n5 ^' N% d- A" Vs.str.replace(pat, my_func, regex=True)
      E2 e% {  A( \: o1 z) T2 P2 I9 g0 [. E
    1- g* @; Q- {5 a& e6 t. [
    2
    ; e' g) P5 m# h! c3 K3% f% S. B  L" B: h) q2 j$ t; @1 L4 @
    4
    * V; Q( I5 T5 s; H; X+ h! ^# Y5$ ~, u. ~+ `/ W7 ?5 q8 F
    6% o4 \  h) h' g, T+ \2 ^/ Y
    7
    / n; d7 m" X, r8
    0 N4 K2 F/ o; [% O% N* P8 c- C3 g. ^9$ F/ |+ F' e. e8 O# o6 X: E/ R
    107 ~: s, g" ?& Z5 K, c$ a4 A: _
    118 k. y; G3 _' X4 d: a- T$ C
    12
    + o6 G8 l' q5 Y13
    # ~5 M0 e+ x3 v. q: ?' t: f14% T4 h* t- L2 c* q/ j' _
    15
    0 S( ?. u8 |1 M7 Y* u7 h16
    8 C# u6 x! ?' h# |. V17
    $ ~  {% n: I* K; T0 x9 n8 r. H18( X. G& E, p3 i2 t( Q# @" H2 K
    19& Z2 H$ U  N1 S9 b3 H1 b  `
    20
    . Q% P% q3 N; q. D' R! v% K# a7 n21% C) x+ U) O2 r  v* M' q0 x( u0 F, W
    0    Shanghai HP District Mid Fangbin Road No. 249: ~- m) r* S" Z+ J
    1           Shanghai BS District Mishan Road No. 5* J6 I' A: C) w3 H
    2           Beijing CP District Beinong Road No. 2
    , J- M# x6 t  N6 u0 R- ddtype: object. \. u, f' r. S+ y
    1% U; G' v) @( K+ T0 K* Q0 e
    2
    2 Z5 B' t! ~7 a+ k$ |3
    & R6 J/ u/ B5 F8 a* @* v4" p5 z; l- L) S  k7 y* p" |* R
    这里的数字标识并不直观,可以使用命名子组更加清晰地写出子组代表的含义:4 m0 a  f& _" c% t: @
    6 {3 j) p# P$ [0 G% e% g, g8 n
    # 将各个子组进行命名8 {" j+ w& N2 W
    pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'
    5 B7 u0 s: p/ xdef my_func(m):
    7 x: k4 ~( \% `+ z9 d    str_city = city[m.group('市名')]. c0 o) Z! P6 N
        str_district = district[m.group('区名')]( B: M' x  X1 a* s0 J/ U' w
        str_road = road[m.group('路名')]
    ) V" w* M' m6 K! h    str_no = 'No. ' + m.group('编号')[:-1]
    . y( r7 [( J2 K    return ' '.join([str_city,9 W. \1 q) O) w1 z
                         str_district,% b" S" {. h! E/ C( Y& L
                         str_road,
    : Z! Y( h; H9 t+ B. i2 u8 i) B                     str_no])
    / `: J0 e. n7 v7 ts.str.replace(pat, my_func, regex=True)& [: u2 D6 w& t; [3 l( O
    1
    * N/ ~1 l# t! x6 p2 }5 U7 I24 l( ^6 f" w. z$ P0 M8 M7 b6 L
    3  C* d& [5 f; f9 x  `6 W. w* R3 u
    4
    : ~( O% h& a9 Y" i# r# k6 v' ?5
    * X% _4 Z, T% x1 f$ j1 i- f( ^6
    2 r, ~, h, E4 ?. e5 G7! j( o6 s* k& h! ?$ |
    8" i! E$ T3 F' Z; P7 @3 @" C
    9
    ! L, n8 l# o7 g; r6 c10
    ) j# Z) T% O: e. ^2 G2 v118 s$ }  K6 k. F: W: v+ H
    125 X( Y* y. g0 Z8 B% p$ E, T. Z. l- r+ e
    0    Shanghai HP District Mid Fangbin Road No. 249
    , a9 J; p/ X5 h' ^. W; Q3 d1           Shanghai BS District Mishan Road No. 5* c# v7 e8 s1 h' ^% i- t7 F
    2           Beijing CP District Beinong Road No. 2
    9 F- u4 S& L6 z4 B0 udtype: object
    ) n  X! I' T; h1 t# {. U1: U6 x! q( P4 o: @  F1 B
    2- N+ ^3 e2 ?8 h  w
    38 t% K! C5 x5 i$ c2 L
    44 E; T: j- N  q9 x
      这里虽然看起来有些繁杂,但是实际数据处理中对应的替换,一般都会通过代码来获取数据从而构造字典映射,在具体写法上会简洁的多。; v" v* D5 B' \7 Y& i6 h
    1 w" A# `! |( V6 `' A
    8.3.5 提取3 }- U; P0 J2 q+ L* d* n0 z) ?8 Q
    str.extract进行提取:提取既可以认为是一种返回具体元素值(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的str.split例子中会把分隔符去除,这并不是用户想要的效果,这时候就可以用str.extract进行提取:: o8 R8 w2 j- r% a
    s.str.split('[市区路]')
    6 n9 R! i2 v: m4 s3 q" XOut[43]: * X2 e+ h2 \9 ~$ T! e$ \
    0    [上海, 黄浦, 方浜中, 249号]
    6 j' o. {- j* B$ {" o1       [上海, 宝山, 密山, 5号]
    0 V3 m* U* u% d1 U0 g2 T7 V1 Idtype: object
    . r$ q5 m: P; b2 k- ]% n8 Q/ m
    pat = '(\w+市)(\w+区)(\w+路)(\d+号)'
    $ @1 ]" A) `, A* i  n3 H( v: u8 M$ qs.str.extract(pat): z, E# Y! G& j1 S* c" f( Q' l
    Out[78]:7 p1 n/ s. g8 v8 `7 @
        0    1     2     3, J, C' l) ]) b* P2 V- @
    0  上海市  黄浦区  方浜中路  249号# Q6 z" h+ J, u5 R$ b
    1  上海市  宝山区   密山路    5号
    ; t" q( m) T# G' }6 I7 z0 Y. x# P2  北京市  昌平区   北农路    2号
    : a" ?& {2 f6 L1
    5 Q0 M2 t# i: E7 u  |21 t' p; S; p" ~5 m
    32 L3 A6 ~- b) i- P  n& M
    4
    ' A1 Q( b1 m# n6 J7 p! h' z5
    ) t# c, A3 f. A8 b- K3 G, e7 s6
    / {# T' G, E8 z* X/ o$ W9 w7
    " ?( U% L4 ]' Q: N0 {0 H) r8/ O6 ~* {2 k  ~3 v7 G/ b
    95 I# ~2 L1 W2 S5 g# U  l: _
    103 D8 V$ I( G; b7 d# J2 {. |
    11; b& {" Z1 i) P, n# U% q' b: W5 Y
    12
    2 T# J* w- g7 @% ^13
    ) F; T2 j3 T# L通过子组的命名,可以直接对新生成DataFrame的列命名:
    4 y4 `9 h# _  _7 Q$ x' w' }& w6 k. X* b, k4 i
    pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'
    - M' e; n( S: o4 N' Us.str.extract(pat)
    ; S& p1 n! j( U# k* BOut[79]: 3 c* N. |" V6 |9 A4 }6 e
        市名   区名    路名    编号
    3 T; C8 w. i/ {0 W* i0  上海市  黄浦区  方浜中路  249号* a9 V3 w$ O7 K7 X/ j$ V% _
    1  上海市  宝山区   密山路    5号8 g6 |0 S; i: P
    2  北京市  昌平区   北农路    2号" h8 F& @: B$ b( D/ {. g; E
    15 C3 l3 @% T: w) B, E
    25 K0 N$ a8 D& v8 Q7 S+ I: s) ]
    3
    # B0 P+ R0 M' u) h8 U* k4
    1 ?7 u* P  W  W  I* Y. b  b5
    9 Z* d8 y' N# p% R; }6' M+ l$ ^. E: j* @* K9 y; z# K/ I
    7  j. |6 X8 B2 x9 T7 K
    str.extractall:不同于str.extract只匹配一次,它会把所有符合条件的模式全部匹配出来,如果存在多个结果,则以多级索引的方式存储:
    7 M4 I" M* @/ `( l+ J8 U2 R( Ts = pd.Series(['A135T15,A26S5','B674S2,B25T6'], index = ['my_A','my_B'])
    5 N$ d) ^3 w8 u% T. I' Xpat = '[A|B](\d+)[T|S](\d+)'# R  @3 K, l: b+ b1 |" c$ c( l4 S
    s.str.extractall(pat)4 e6 V2 f: ]. d8 U5 a8 A% Q
    Out[83]:: {- R0 B3 l7 A8 N/ _1 q. C
           0   1+ P0 p, I1 o% K
         match         5 C6 _8 }$ d1 _; o+ [
    my_A 0      135  15: ?: e& ]+ b5 d# U; s, V9 J2 R
         1       26   5
      |$ ~2 C: y) J5 S. ]my_B 0      674   2
    : v6 k$ V4 s3 @  U+ S& L6 E$ |0 M     1       25   6
      ^4 i5 L! E) A) J6 p1. B" S* x6 e2 W
    2" ^( o7 s( i! r- p
    3
    3 N+ R' e$ x4 v( j49 b( y: Q6 d5 z1 Q6 n. {. u, M9 B
    5/ D" J8 R  {% i
    6
    6 l1 E2 A3 |5 @75 L8 [2 w, r+ g  z, r5 w: w3 q4 }$ k
    8& \% n' V7 h' j( p! N: g
    9: K' V' {) O6 {% Y/ R
    10, w9 O5 v% A* H; J
    pat_with_name = '[A|B](?P<name1>\d+)[T|S](?P<name2>\d+)'3 r& A6 A( w% ~- X8 {
    s.str.extractall(pat_with_name)
    $ C. N4 Q2 c( n6 R. oOut[84]:
    2 ~% {9 s) a# _. T4 M7 O! L% Y           name1 name2
      e5 W5 ~! S/ G% ]     match            
    , _5 Q; ^% m# Vmy_A 0       135    158 |# u9 z) e: P$ Y
         1        26     5+ |8 l% k# v+ _- ?0 c& P5 x
    my_B 0       674     2
    . r5 x4 T: d7 _+ ?' @     1        25     6
    / S/ C' M; ^4 o8 t1
    % ]" P- H8 }0 b& g25 M$ \; p& u( I& W) N/ l4 n, ~! p
    3
    0 W4 |3 V- v8 X43 v. x/ ^9 R2 e; I8 S- ]" u9 i
    5+ h3 t2 l, S* c9 A3 l1 C
    61 Y4 j* G2 d! ^* g% ~$ H
    70 O' `7 W* O" t( q! p
    86 U; P# R$ w3 w
    9+ ]7 \- V7 \; o% ~
    str.findall:功能类似于str.extractall,区别在于前者把结果存入列表中,而后者处理为多级索引,每个行只对应一组匹配,而不是把所有匹配组合构成列表。
    1 z$ p8 x# s* r$ Qs.str.findall(pat)
    ; H! `, [& @# ?& f1  W* g$ [: V6 Y: w) @8 L
    my_A    [(135, 15), (26, 5)]+ V" n2 `7 R. [% K
    my_B     [(674, 2), (25, 6)]- ]$ E+ a( l4 k2 ^% w6 B) b/ S- t& ^" @
    dtype: object
    # B* f0 F& y* E. A0 x- }1
    7 ~" A' r  O7 ~2
    5 P# g2 |2 B) b7 w- |3
    / a1 ^3 L& g, F2 z8.4、常用字符串函数! B. ]9 [9 Z6 h1 {. Y. r
      除了上述介绍的五类字符串操作有关的函数之外,str对象上还定义了一些实用的其他方法,在此进行介绍。# z: }6 R2 x8 o  E' c

    1 O' @' z/ T3 U2 [7 ^8.4.1 字母型函数
    2 n  v5 {2 i$ T4 h  upper, lower, title, capitalize, swapcase这五个函数主要用于字母的大小写转化,从下面的例子中就容易领会其功能:
    1 @. j! }9 C# ]5 g; M' O% }7 |/ _8 W% T+ x& I+ |: a4 Z8 ]. _
    s = pd.Series(['lower', 'CAPITALS', 'this is a sentence', 'SwApCaSe'])2 t* n7 l( d7 f0 o( D  E! I' R, }

    " H8 z9 b2 B( vs.str.upper()$ Y- ~) Z! A- x
    Out[87]:
    , E# y! W/ V/ e- s# x4 ]9 _/ x  I0                 LOWER8 E* h# U- J; M) M7 w  ~2 S0 z" j
    1              CAPITALS
    % y6 ]. _8 x5 V4 u! z" d2    THIS IS A SENTENCE
    4 o" m" d7 {1 }% K3              SWAPCASE
    + T- ?+ \. l/ o- s1 hdtype: object
    5 ~  m5 e6 Y) W0 ?4 l* t( l5 z0 `2 U6 k
    s.str.lower()7 H! x% m' ^& ^% U9 b
    Out[88]: : p4 [5 C. G; z& t
    0                 lower
    + x* E  @' G6 f( `/ g& {& t/ d3 y1              capitals  r9 T, q  u  y; P
    2    this is a sentence
      B) R9 }! f6 a4 W4 s  d7 J, G. J3              swapcase$ q; J$ h* {( i/ I4 e' s
    dtype: object
    . {* i3 ?; _  ]8 h3 t5 j% U" l7 r: \) k4 H% d6 {% Y. v  k
    s.str.title()  # 首字母大写; q# R" x0 ?9 I: U
    Out[89]:
    7 H5 a& q& G- ^, H# K5 r0                 Lower
    , ~' q. a7 J% B' U7 @/ \1              Capitals
    ) T% `" x+ ]  O2 Y1 ~7 k* n2    This Is A Sentence- Y' Y5 z, t9 @2 d7 i3 _% O3 c
    3              Swapcase
    : f0 U& \" c# `dtype: object
    6 ~5 h  A$ ?; @- Q
    - F' X! F7 y2 Ls.str.capitalize()  # 句首大写
    , s& g, ~1 \& X: s! r0 ^& b0 tOut[90]: 6 [+ w& c9 }8 K6 `7 e/ g; W7 v0 h
    0                 Lower
    2 E9 h7 x6 {; ?3 A* e" ?0 {1              Capitals- t) Z0 I5 }  q- e5 h/ z( d3 K
    2    This is a sentence& q% ~/ T* Y7 M5 g  @7 a
    3              Swapcase( O7 {2 C6 W/ B
    dtype: object6 s3 U( K. }3 F9 H+ ~  U1 L

    8 z, T0 I' j0 y- J0 A4 Qs.str.swapcase() # 将大写转换为小写,将小写转换为大写。
    $ u( D4 A, t' ]( i! zOut[91]:
    ' C* O* b1 C6 c3 |2 Q& P$ f+ h2 b0                 LOWER
    8 ^+ [* B2 }" V" N& J1              capitals
    " E4 w% J# \: J" x2    THIS IS A SENTENCE# i$ _: @6 @* ~
    3              sWaPcAsE$ M+ X: f" f9 z1 S3 e; F4 {9 w
    dtype: object/ S  {- p" P- ~  f$ b( T* s

    - C! `% `: g' S3 J2 ~7 d. W: ys.str.casefold()  # 去除字符串中所有大小写区别
    . ?- [/ `6 E8 ]: t3 `$ z# Q! m. d, m' k4 H7 H; }4 |5 h' M$ T
    0                 lower4 I6 o$ H/ R( f% i; D2 A
    1              capitals
    8 D" x3 m1 o8 L9 W4 H2    this is a sentence1 Y  Z% W( R3 s+ E8 r
    3              swapcase
    9 Z  e. @" s3 s; E5 Y% @( S6 \2 m) _, H: G0 [4 e& y. R% P4 |; B' t
    1
    4 I; C+ o( Y: L) B5 t2 [. Y2
    * R! M) L6 G' b- j1 i6 {6 d3- `' {; j8 a( |3 T. W
    4
    9 l- R" |3 C. e55 ~* _: B% w0 q
    6
    5 \1 R7 ]' |+ k$ E$ N9 L. \7
    . |( Q3 a2 d4 M( [* ~8
    8 x) i1 X- i& `2 V+ S9+ l' ~& z) l% x' d3 B/ m
    10, R# R2 @2 P+ e+ ^/ Z
    11
    " w/ {9 T4 F$ _0 n+ O! e12
    7 ]( e! I0 P6 T' |/ i13$ S8 }/ x9 ~) T' Q* h
    14
    $ M! D8 H4 d' C5 ^4 k15, M) ^' D6 d  h
    16) i0 [# Y: S% t3 r6 ?
    17
    . F& D, ^' v, N$ u% J$ Q188 D$ l6 g! c6 ?9 T
    19
    0 P- n7 Q  w3 ~$ Z20: ^; x$ `4 F% r0 r, F
    21  h. z2 L) g7 d. G' O
    22* l! a; E  L! }
    23
    7 d# j- I! v, m: U/ \% I0 k24
    5 R! Y0 t5 k+ X& d/ n3 b250 N4 o! M: T: e, A% T
    267 y6 I- Q2 F7 C$ x& H
    27
    ; _/ _$ }( N: Q3 }! P4 f28
    8 k$ W! s# p9 h7 y$ G% \% M0 @% v' Q29) S4 ?5 L, O8 F8 ?3 G
    30
    : [2 p' L; i9 {8 ~- Q: A$ L31, L4 Z0 a% M4 D& I. k4 q
    32
    3 F/ y) k8 G# m- N33
    # N6 O) W8 U5 t: e341 ~- |  ~. \9 b
    35
    7 w8 o  h- L3 g( c* n361 _1 T5 h8 H7 I$ _
    371 i$ ?$ W  i, O- d
    38
    / v, _/ L7 g4 S) _+ _, q39* b1 G. c% A$ g" E  X2 _* y
    40( V' N2 J" {/ k
    41# V6 S  F( w  m: D
    42
    " D% R+ G" ~; |9 p' K43* s6 x. c5 i+ G+ q0 U
    44
    . G0 S8 H  N7 s: o& v4 Z2 b45
    * U8 b% c1 D, I& [) Y- Y465 M: D3 g3 j  ^9 E3 ^7 Y
    474 `4 B, k; @) f' R" \
    48
    9 \( V4 x. c) Z- d, l# i8.4.2 数值型函数2 y, }2 {$ U0 b' `$ U6 l0 [
      这里着重需要介绍的是pd.to_numeric方法,它虽然不是str对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。其主要参数包括:
    " n3 v. V' j* |' s2 N8 Q% T. ^
    - W  ^/ d; b, U! }. aerrors:非数值的处理模式。对于不能转换为数值的有三种errors选项:' F' k* z. O; w5 n! c9 g; a
    raise:直接报错,默认选项
    2 }) ~2 |% Y7 S+ qcoerce:设为缺失值
    ! Y/ L- G/ k7 U5 r5 n" c# nignore:保持原来的字符串。) H/ l  d. t3 g+ a* k. Y
    downcast:转换类型,转成 ‘integer’, ‘signed’, ‘unsigned’, 或 ‘float’的最小dtype。比如可以转成float32就不会转成float64。
    $ y5 |& V' A  @* c$ js = pd.Series(['1', '2.2', '2e', '??', '-2.1', '0'])1 l$ _! b- Y. s5 o$ ~  T

    , v4 U* r, r, w8 L6 Y7 Kpd.to_numeric(s, errors='ignore')# [3 a- M( K5 X5 p* I" M
    Out[93]:
    5 s, `7 l7 s! @6 o  {8 A" u0       1
    0 Z4 {' G/ `+ d) _! c1     2.28 m" z" N# N$ W0 w6 C0 \" x8 S
    2      2e  G) @) N% F- B4 j
    3      ??
    4 P  g! ]' p2 E) U: E: x4    -2.1* i1 W. T: W: q& ^; z
    5       0
    5 w& ^& r9 v9 b0 udtype: object4 |% L5 Q! n& s

    # B% [0 ^! H- I9 x+ K( @pd.to_numeric(s, errors='coerce')
    ) V% m% E3 g8 h! N: c( j' SOut[94]:
    5 V1 [6 l6 n& x; q5 n) z: ?0    1.0
    . Q- m, C& g0 k1    2.2
    , R7 L: k( {; d* F5 E7 N  x4 M2    NaN
    $ W' j% C/ H% ~3    NaN- A0 ^2 m( G0 c$ ]
    4   -2.15 g% A5 L( x! C+ m% ~( [
    5    0.0
    ( ]* t2 t: B) {* Cdtype: float64, [2 \# U% N7 u4 R3 k
    ' _1 i2 p. `: ?. H1 ?
    1
    $ `" x( ]+ v; X: z) G20 n+ d# f' Y% P7 i1 i* t
    3
    ) l, f- S' o/ \. v4" G+ T8 p, n4 ?9 h
    5. V7 ~* n2 g& J+ ~
    6
    . ~  T- \6 X- T4 Z* d& p7
    # k  r9 N# s  b( `5 R8$ \1 U1 Q8 n5 C
    9
    1 F+ {' R( s2 X, \8 |' H6 ~$ I10
    ) q9 i+ o. J! W1 Z( f111 k7 J# P7 K; L' K
    12+ `% P$ M& H7 ]6 w( I# Z
    13
    ( `& G( h/ v3 d6 H, b+ ^+ G6 N141 L5 A2 }  }! p6 r
    15
    ' a7 k8 u* \  y9 d% \, N- D' h16
    / \) M2 p" T9 W17
    8 R$ c0 C( V1 {9 n. o8 N  ^  g18
    % E+ P6 `% G4 H: Z9 R3 v( M8 t19
    8 Z3 c0 |+ p" p! G  }20
    1 O$ X; T: C5 C3 y' [% g21
    1 n0 A0 y# ]7 H% c! W  在数据清洗时,可以利用coerce的设定,快速查看非数值型的行:
    " }( Z# v2 X" i' W* O; I4 F) o& c- [7 u# i
    s[pd.to_numeric(s, errors='coerce').isna()]
    4 D4 t5 Z, R. X) YOut[95]:
    + ]- L/ _3 _2 v, E! ?2    2e
    , J+ t. C/ {. y' R3    ??; `8 f! S$ p7 c. \7 r
    dtype: object' U% `3 h/ l- l' ?) m; |, d
    1
    / V* h8 @6 j3 q' U% v4 f2; o, v! t1 x% N" p; a
    3
    , w) N: s1 M7 A/ J/ C. k1 `4' U  I; U; K+ U7 m  R6 F
    5) w- [( `( [% @4 H! h
    8.4.3 统计型函数
    8 W3 t" y9 P% E3 j2 u0 G/ n  count和len的作用分别是返回出现正则模式的次数和字符串的长度:
    - _; A8 W: b1 C& L* z" m. H* C, T! B$ u' ?, o- z
    s = pd.Series(['cat rat fat at', 'get feed sheet heat'])& _, o2 W- w9 L7 M: a: N. `2 r
    4 [5 _0 r" `6 b% o' {/ y, d0 _, ]
    s.str.count('[r|f]at|ee') # |左右两种子串都匹配了两次( g) m6 \4 A& g& U8 R; z
    Out[97]: 8 g+ X! [; B4 T: ]/ }
    0    2
    ( _3 J4 w* o( P! ~4 O7 q1    26 u& H. `7 I0 l0 Z- n( g9 |
    dtype: int64- L" b. i0 \+ z, L+ t6 e* Z

    $ @& M6 _& X" @4 I5 t1 Ms.str.len()
    # _- W5 u1 }+ f1 e8 |% K" K$ OOut[98]: 4 h, M7 D( l0 }  i* X
    0    14( B3 y/ ^; G& ?
    1    19
    1 i8 e1 J5 V  u; gdtype: int64* X3 p; a: h+ U; t* W/ G! _, e
    10 c: z$ A4 Y; b) r7 c3 `; q
    2
    1 @/ H2 ~9 I! Z+ @32 n2 i! E/ j- |" X; @
    4
    / I( Q: N* |5 v7 t( v/ X1 H5! L/ \0 ]$ `/ m" J; S; q' m
    68 l5 {5 W  G2 e' U5 z; R$ n4 T" o9 ~. ]0 v
    7
    - y5 N( M+ u% W$ _; \8
      z1 I4 w0 ~: x0 U9
    $ A6 d1 ]7 ^) P, a" R, m3 C10
    ! i$ C% D3 L& E: t0 N11
    + q! i; H  l5 Y& O. b12* _% ]+ K  ]1 q! q& C' A* `; K
    13
    ; [& {6 _2 ?- i$ G4 k+ G8 i+ W8.4.4 格式型函数
    6 R' p5 m9 C, l0 w/ x% @- C  格式型函数主要分为两类,第一种是除空型,第二种是填充型。其中,第一类函数一共有三种,它们分别是strip, rstrip, lstrip,分别代表去除两侧空格、右侧空格和左侧空格。这些函数在数据清洗时是有用的,特别是列名含有非法空格的时候。
    / k$ v6 E4 i3 O8 Y3 `* g9 s2 S1 ?: @1 k
    my_index = pd.Index([' col1', 'col2 ', ' col3 '])
    * G0 I% _+ \7 \! a& u- {+ x
    ( R# X' h! K* \0 |' X' w, J$ Bmy_index.str.strip().str.len()! ?$ j' z0 h: g4 P" p+ P8 U& i
    Out[100]: Int64Index([4, 4, 4], dtype='int64')
    3 Y) x  t; o" ], u/ a7 F4 B- t1 q$ [6 a7 z% E
    my_index.str.rstrip().str.len()9 q5 g% @, M# I6 w7 n& a1 t
    Out[101]: Int64Index([5, 4, 5], dtype='int64')1 U& j9 ]5 b. ~% ]

    8 J! j) o9 s. u: h& Z9 smy_index.str.lstrip().str.len()& a4 u. v/ B5 p5 l
    Out[102]: Int64Index([4, 5, 5], dtype='int64')6 B7 y% l6 v1 M1 I: p) x8 D+ l
    1. N- y+ Q1 l7 S6 F! G, Y& g2 ~
    2
    ) L3 D, _/ N  P/ v" `& N  f9 q4 N0 Z3
    . t: B8 e+ f& _* x42 s; V2 ^7 k" [9 l4 _
    5$ d4 I! X0 A& a" m
    6" ?1 Z' @" P+ C4 ^' {
    7
    8 U( ~! z9 ?; t7 j+ w& V/ q84 w( h% q9 `: ~) k
    9. a1 A& l7 L0 u
    10
    # g( P# N2 j' h2 t3 r  对于填充型函数而言,pad是最灵活的,它可以选定字符串长度、填充的方向和填充内容:
    1 a8 p+ F- x$ p
    - c. ~3 e, p6 k. E( r0 K- {s = pd.Series(['a','b','c'])
    ) a0 W6 v/ e* @1 C- h1 M$ e
    7 d6 c, ]1 w& M; [# Bs.str.pad(5,'left','*')
    . V) [0 q7 H' h) m) KOut[104]:
    6 `- o! R$ J7 }* d8 A- s" Q1 `6 `0 ^" p0    ****a! J( z- x/ n. x- h
    1    ****b5 [" E' ^- q/ N( q/ z5 c- N
    2    ****c0 h* T. U1 P! J$ D6 F6 y
    dtype: object% u) L! d2 \0 [9 {

    $ k7 D; I: z1 w1 j5 z. as.str.pad(5,'right','*')9 I- q2 Q8 l) @+ j2 f
    Out[105]:
    7 r$ ]6 p% I9 h0    a****, |% \$ O, R4 _: ?
    1    b****: `; B8 g1 Q% d) e
    2    c****
    ) l& Y" O# a: p8 S0 M3 sdtype: object. G7 v: V2 ?2 l7 x, Y2 s: ]6 [

    % o+ E  v$ Q8 N' f3 Cs.str.pad(5,'both','*')
      s3 `* C1 C) IOut[106]:
    1 H$ k0 ~8 w9 B$ ^7 u0    **a**% y# R- ^9 k/ B0 q
    1    **b**
    , Q, s  R! o8 g/ Q4 K2    **c**
    $ e- {) {. K* ^/ I7 L) [  R. s. t5 Adtype: object
    9 x+ F0 G; R0 Z( x# `
    7 Y: e' J# [0 ?0 w2 A  U1* E% B5 w  g7 |: U' R1 n' N
    24 J$ E4 V7 A- `/ G8 h" }
    3
    $ H* V1 l2 d* X8 i- [* R+ x, y* ~6 @4$ [. R3 K5 q% r  W* [: F/ _
    58 {0 ~- S% {; I# v& t
    6
    1 U4 B% R9 S2 G' J, N- P/ E# w/ O0 r7, D+ X5 V" Z" H! d3 {* V
    8' P& b0 Y% ^& M( P! F" Y# y3 @1 ~! D
    9
    & k6 p# J5 B/ ]5 C5 J104 H1 C7 n& i* }3 Z8 e$ O
    11" y% b; K- z( `+ R3 q4 e/ C0 P! r
    12& T; s$ x' L3 `7 j, H& }! f4 {
    13
    ; d+ _% e- E1 S* _4 }14+ b. ~7 j" k  _1 d
    15
    2 W" w; N7 E/ }; Q! f16' e; z$ c% Y1 ~* h4 R1 b: H
    17: w9 `4 G* q0 K' Q$ Y( i
    18
    # C3 J9 J1 z( q* T19
    4 p; w/ g3 o" {0 k20% _; t9 S: `; x; _5 s
    21
    5 t3 R9 F  T6 k3 S4 x0 }22
    4 E' i. [6 B2 N2 U  上述的三种情况可以分别用rjust, ljust, center来等效完成,需要注意ljust是指右侧填充而不是左侧填充:
    8 c: X) l3 L! r" U/ A, f* c: b7 j6 e; S& [: U& f+ E1 k! s3 g
    s.str.rjust(5, '*')
    ; }9 t5 p+ R. {Out[107]:
    ! n% Z$ i, ^" @! x8 s+ J: t0    ****a
    ' [; t9 D: ~) b+ J# G1    ****b1 O/ ~/ i2 X5 a4 ^
    2    ****c
    % s5 W$ n0 p% m9 L  x0 P) T& P. Hdtype: object+ x$ ]* Y% C: H# H6 g& G1 n
    0 D' b" ~8 f: T4 u$ L! L
    s.str.ljust(5, '*')' b$ C+ v" U$ _3 V! x+ V2 N: m
    Out[108]:
      a7 Y9 D, v  L% B; b0    a****
    & }% {) a" \; J* F$ R1    b****
    ; S$ K( Q- C& q3 w5 g3 @0 v2    c****
    8 x  \3 `5 ]) m- a( H0 ddtype: object
    ( E$ `/ C" b6 V. P
    ( H+ I: i* g/ J$ r$ ]5 Xs.str.center(5, '*')% Z9 H  q# Y+ ^4 P, v6 Z6 {" a  {
    Out[109]: # x9 S( l5 u5 _  j5 ]0 K# a
    0    **a**
    ) F; y, F, O" o' }3 V9 D, Y1    **b**, }: t: w3 i  p+ m  N9 M$ P
    2    **c**1 F1 r' F! M: }& t# n7 L0 M  H
    dtype: object
    / J! X- [5 w7 v9 n( [. D* @/ }% t  W! d/ G+ {5 b4 V1 U1 Z0 i- B
    1$ U) v+ b) x. q) Z6 O3 B
    2
    % }: J4 [4 P2 o, t2 ]( E# E3
    6 D6 @2 [* k) p7 j2 Q4# }5 w: I. e' Z! P6 g, B
    5
    ' m1 Z5 Z% T# ~2 S% W6 H" v: N69 O; l. e9 P2 R. M9 h8 N
    7
    ; J, S! d4 d, D- Q$ V8
    : ^7 ~8 j: L/ T3 |9) g: |5 \( s0 |/ X( ]& _; W
    10, n; B0 m% ]/ B+ A. t( c$ n+ g8 E+ }( h
    11
    : Y* }% _# M$ X. q& y6 T4 h12- u5 Y& y! X0 F5 k: ~" A( V3 w( J. r' c
    13' M' B4 }; L' j0 _/ p9 o( `
    14
    ( _2 e. K4 S+ H8 N' q7 L15
    : O7 W; o7 a8 Z/ _5 q% H16/ |3 J+ c5 i# f0 [  H; L) `) M
    17
    0 P& r5 q" Z  m. z$ J  C18
    $ T, O* h+ U5 h19
    ! P% f3 U) E5 Z" n; L! i, A+ A# s" _( p20
    ) A7 o8 g* n/ ]( V  在读取excel文件时,经常会出现数字前补0的需求,例如证券代码读入的时候会把"000007"作为数值7来处理,pandas中除了可以使用上面的左侧填充函数进行操作之外,还可用zfill来实现。
    ( c4 x6 I% J/ U& e# F: b
    ' m2 p( Y; C5 W9 C! N7 F0 Ss = pd.Series([7, 155, 303000]).astype('string')! I! F0 w0 l! T2 Y4 t. G7 O6 Z7 L
    ! W- x; C# ^7 g+ ^* v6 e7 \
    s.str.pad(6,'left','0')
    . ~& d! V6 i  M3 g: p' Y, MOut[111]:
    ; n5 O; u9 _; n8 e5 g% f0    000007" B6 t, J! \* Z( k. x. b
    1    000155
    ; K  c* E. O  u6 w+ e+ o0 k2    303000
    ' D. ~  F1 \$ O/ v. O. vdtype: string, y9 w8 I2 D* _$ s" b" a8 a. R
    8 g4 k. j/ L7 o3 C) X4 w
    s.str.rjust(6,'0')5 z2 \. E; C7 W2 S* _& Z$ [: K
    Out[112]: ' u  n: B7 p7 c( t. j0 V* H
    0    0000079 @$ ?* @) ~, D/ y
    1    0001550 R" G$ e% i! q+ U9 |" n
    2    303000
    " J: L2 X  g- U' t( {6 fdtype: string: W' j4 E+ X: a3 @
    $ [/ X# g6 l2 k- i3 S- k
    s.str.zfill(6)
    # `6 Q" A3 A, ~* ?' HOut[113]: , T( K; i4 G+ y* C0 w8 h. X: Y& H
    0    000007
    ) k  m1 k: N$ a) @; F1    000155) ]; T7 r7 {& h2 ^( B2 B
    2    303000
    + l6 `- ?5 N1 S' D7 n: C- M. Ydtype: string3 K1 d. G, u$ R* e

    , @+ ?/ n7 A* f$ H) z2 H* H1
    ( Y, t: Q6 G" E( x; d, D2
    4 X- E1 _, N, @; X5 h( r3
    $ R* ~6 l7 \/ {4/ E7 u5 C) v! {1 D: `
    57 D4 y, [, s. ^' h
    6  V  `4 ~1 B: p6 s, i+ Y! ?& B
    7) U8 |8 v7 k6 p+ [: K
    8# b7 W+ C+ Y/ k1 w
    9$ w7 y, U# G. M/ s. P$ k1 u
    10
    ; O8 d8 B0 T# T5 C% j+ y, @2 x11
    8 C+ T! g, N6 h3 b! N12
    6 }6 ]7 e% W. J: Q132 D  P" m1 X5 L0 Q9 w: a( o7 q
    144 I1 @% E$ w; W8 o& d6 P6 S
    159 X6 ~  g) i9 r# J; O+ F
    16; A0 ?$ U0 M! T0 n* s" M* W
    17
    " O) C! D% A2 l9 D8 W$ i189 @$ f* x7 ?8 s- s+ R7 a
    19$ _$ N* `* n8 z7 ?4 t! ~
    20
    6 O; S% `1 G3 \& n21
    6 k5 L: t3 b# y: w) _1 O22& C# P$ ^7 y) ?: a/ j+ n, C
    8.5 练习# j9 a" Z7 x& H7 S" e: @6 Z+ I. B
    Ex1:房屋信息数据集
    * }6 w  B8 ~" D& A现有一份房屋信息数据集如下:
    9 w1 X/ k' i" @4 l9 _! D& ?  N3 V' n( T1 o. y
    df = pd.read_excel('../data/house_info.xls', usecols=['floor','year','area','price'])  @7 m! y4 V% F" a3 N
    df.head(3). Q. i9 B3 B( O* K8 W
    Out[115]: . V0 f7 z. ?) H' T; A# v
          floor    year    area price
    ( q2 H) a0 q- S, U$ c0   高层(共6层)  1986年建  58.23㎡  155万( V0 T. E5 i. x2 ]
    1  中层(共20层)  2020年建     88㎡  155万3 `; c: g: {) H& N
    2  低层(共28层)  2010年建  89.33㎡  365万' \& k) H$ A9 m8 P5 t( P
    1
    8 r) V, a! y5 U$ p$ X2
    ' A9 h5 y$ z* b, @3
    . b" }) _, w/ w6 q4
    7 _& L  I" P* @3 P, T4 C2 U5
      H0 b. D( W( g* f9 J6: E; t8 D7 r9 d1 m
    76 L; [  z2 ?2 ?  H( [# v
    将year列改为整数年份存储。9 C' c5 N. r2 r
    将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。5 t% S2 ^# @/ p* l- I0 h
    计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数
    2 Q$ X. S, u- O将year列改为整数年份存储。
    9 P3 L- a0 m7 a0 h% y. T, E& J8 F"""" i% O: w) ^! [' C6 n; k
    整个序列需要先转成Nullable类型的String类型,取出年份,再将年份转为Int64类型。
    # O0 y8 v+ b, G) V( o注意,转换的类型是Int64不是int,否则报错。即使astype加参数errors='ignore'跳过缺失值,
    4 e" c) u/ q, g2 j转成int后,序列还有缺失值所以,还是变成了object。; R' [; ^4 @9 I3 z, E/ ~6 a9 B
    而整个序列转为Int,就还是Int类型,缺失值变成了 pd.NA 。2 |' r8 s. X% M4 ~8 O
    """
    . s" [9 a8 W% x2 l% _1 [df = df.convert_dtypes()  s+ p; O8 W1 m, Q
    df['year']=df['year'].str.replace('\D','',regex=True).astype('Int64')
    7 Q$ ]) h7 U$ ]df.loc[df.year.notna()]['year'].head()1 @" I  T  V( W  {! Z) t

    4 c# l; k4 H: q/ M+ ]: Z/ M* @0        1986
    4 S4 A* }) l- @2 C% a0 g: C# n' r9 m1        2020
    2 ?% A* A; |) B6 @% w% J2        2010( I/ d2 a4 W' M9 m* H
    3        2014
    # Z4 J( ?9 x0 K, f9 x  a4 v" ]' A4        20153 o" D. Y& ?$ F$ R" i
    Name: year, Length: 12850, dtype: Int64% u& A4 e* B; a8 @' \; w2 G
    . Z) @, g- u3 X8 {
    18 S& e" q: A0 v9 r8 m+ X  K
    2
    ; e7 C2 o0 f( K3& L. D- |& x0 B3 x* z; y
    4
    9 B; i4 z) W' U- Z: m5
    2 c' s9 N; g( S) N; t6
    * d, Z2 z% n: p5 p8 X7
    5 o7 M' S( A* p& b/ x7 Z; E) {/ |8
    , A( i3 H* c) l% O9
    9 ^$ W  s! {& S$ ?: z104 @! u9 ?, j  u9 x2 a& L( ]; t
    11
    2 T) i! `& Z9 Y" M! y122 B( ^+ ?: F) X* i" |. }
    13
    * O3 V4 ~8 ^% \. `/ j# Y14
      n+ J: l9 ^) A, m' B15
    ( J" z" c! U4 N) W16! n! `& v- {9 K$ m- t
    参考答案:
    5 B" L$ I* u9 l1 |$ R& d) T# b) i
    ! z0 _. S) @' U- \4 _8 a1 E2 H不知道为啥pd.to_numeric(df.year.str[:-2],downcast="integer")类型为float32,不应该是整型么1 r+ T2 T. A1 L  \! L' s

    3 i1 W/ {7 X8 ]df.year = pd.to_numeric(df.year.str[:-2]).astype('Int64') , b1 b7 F: c: z$ q4 i
    df.loc[df.year.notna()]['year']- a+ d5 k# J3 ?3 C4 r) K
    1
    + j6 h1 {# `/ V5 C2 n2
    ) L! Z( @5 y+ h& H# Z$ b( w$ j将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。
    $ ^! o: Q" e3 }" ^% j3 Q& epat = '(?P<Level>\w+层)(?P<Highest>\(\w+层)'
    , V  K  m( Z# Y( x) Sdf2=df['floor'].str.extract(pat)  # 拆分成两列,第二列还是(共6层得形式,所以还的替换一次8 R# J/ L- u. r, |+ V) p
    df=pd.concat([df,df2],axis=1).convert_dtypes()  # 新增列拼接在后面,再次转为Nullable类型
    / Q5 J3 ?3 B4 `' z% y  q1 e7 r7 [9 udf['Highest']=df['Highest'].str.replace('\D+','',regex=True).astype('Int64')              1 `0 r* y% U) Q! M0 K& {$ H" d
    df=df[['Level','Highest','year','area','price']]3 {( i  {1 f8 T4 y% x( s
    df.head()
    / g( T! H7 d2 {, C/ ^$ V6 \, R/ u8 S4 v+ ~
       Level  Highest        year        area        price
    / i2 ]" z2 X  ~8 p8 H0        高层                6                1986        58.23㎡        155万
    / O+ M7 m9 V2 p/ H1 l1        中层                20                2020        88㎡        155万
    ! T6 ^8 w) A2 ^& F. u2        低层                28                2010        89.33㎡        365万
    ; @. ^0 p: _: A1 R+ f) Y/ q& G3        低层                20                2014        82㎡        308万
    6 T3 r7 d: m( i4        高层                1                2015        98㎡        117万
    * C1 u3 r; G6 u- ^" b- L1
    ( H8 R: W# i' R( V, P, Z2
    4 X- y" E: Q8 g" X3" L; ~% f6 \9 [% @- S( X. o7 Y
    4
    . _% n' ]# h8 F5 }6 I# G. p8 I& V3 N5
    ' z% S5 u# I) k0 Z/ d# |; K6
    - v; ?% O4 m  l& O7 w' X7
    + Z! V+ Q0 M1 m5 d8* a( ^, \5 g$ Q# A
    9
    " l9 C; u' q) U10, W% |# j) H; \! g
    114 Q/ m# q5 \: ~% ?6 U  j6 ~1 ^
    127 f/ V4 H0 ?2 V3 O) \( A% O3 \
    13% T- G; o: X" n5 `, M# Q4 y8 }6 H
    # 参考答案。感觉是第二个字段加了中文的()可以准备匹配出数字,但是不好直接命令子组了  V. D) |. X& `2 n* s  u% ~: q
    pat = '(\w层)(共(\d+)层)'. r1 E1 c2 b/ B1 ~  ?+ c
    new_cols = df.floor.str.extract(pat).rename(# I# L; y& w7 Q( U5 N; y
                        columns={0:'Level', 1:'Highest'})
    - r0 t  A2 c0 K& @
    0 {. B4 C+ l  p& n8 {  a  idf = pd.concat([df.drop(columns=['floor']), new_cols], 1). P1 |& i5 O& S9 k8 L
    df.head(3)
      Q* ?+ I, y# A6 w5 P
    * c6 L& I5 t& g' _7 }0 GOut[163]:
    $ @7 R' V" |/ U* z% j   year    area price    Level Highest3 i8 R& o. z9 \6 E7 D0 p& Z7 F1 w
    0  1986  58.23㎡  155万    高层       6) d8 ]8 Q- s, K  W9 B& Q
    1  2020     88㎡  155万    中层      20
    3 }" G4 D3 ]  `8 H8 S2  2010  89.33㎡  365万    低层      28
    3 J9 y* \0 B; x# p7 C$ J1$ r0 N7 Z8 }  t" C* j' x
    2: `/ `9 u, J, L: }3 Y% k8 l2 J
    3
    $ b" t4 B# g$ E7 i4
    1 Z: t7 [+ M& g; q2 r7 e5
    - c8 ]7 a, j* k9 ~1 A5 m63 t! C* L* @- f% u/ R3 h/ ^9 N
    7
    : v+ C# |( a6 t( `8
    ; C" w4 T4 V$ ^9; `9 `7 t' q- o. h
    10
    ' f) o2 A8 }, u% ?6 M. \/ x; B+ ?11
    % T. o0 i; B6 v. S1 |" E12! P9 A% t: X9 q* c" d
    13( ^) T& |9 x5 I
    计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数。  x7 _% R0 ^6 i* J7 _$ Q4 `
    """
    * x6 h: v" B% C% ]/ Rstr.findall返回的结果都是列表,只能用apply取值去掉列表形式
    1 b, S" B8 @6 `参考答案用pd.to_numeric(df.area.str[:-1])更简洁$ R/ n9 |/ T' B# {0 e1 [( u0 i
    由于area和price都没有缺失值,所以可以直接转类型6 v+ S( x/ v  C/ u4 m
    """/ P0 i' K0 r, {2 h
    df['new_area']=df['area'].str.findall(r'\d+.\d+|\d+').apply(lambda x:float(x[0]))
    9 G& b% l- L' ?0 Y; R# v) x0 {+ Qdf['new_price']=df['price'].str.replace('\D+','',regex=True).astype('int64'). l# V$ B% @& ^. e# e
    df.eval('avg_price=10000*new_price/new_area',inplace=True)
    9 S$ }% l. c7 x# 最后均价这一列小数转整型直接用.astype('int')就行,我还准备.apply(lambda x:int(round(x,0)))7 a  @7 m: m+ l& [. O. h1 R
    # 最后数字+元/平米写法更简单5 g0 F2 y% ~4 w4 A& r+ S: d
    df['avg_price']=df['avg_price'].astype('int').astype('string')+'元/平米'% j& c6 m3 O! Y2 z, b' }' R2 @
    del df['new_area'],df['new_price']! C  n1 D2 k) D. n$ |) [$ K, q8 |
    df.head()* {4 Z( n: c. ?$ y
    7 M" s6 x: n1 f! \9 j6 M
       Level        Highest        year        area        price        avg_price
    - N8 i; `$ I! a% [. c% [0        高层                        6        1986        58.23㎡        155万        26618元/平米
    $ j8 F: a9 o/ D2 ~1        中层                        20        2020        88㎡        155万        17613元/平米  g4 u2 q; D! a# F% N$ ]& h
    2        低层                        28        2010        89.33㎡        365万        40859元/平米
    # ~. Z5 c* h5 }3        低层                        20        2014        82㎡        308万        37560元/平米
    1 }( a/ P: m1 p1 f4        高层                        1        2015        98㎡        117万        11938元/平米, T: C2 F9 m/ L2 Q4 m- h2 _1 x
    2 m! }$ {; j1 `+ d- s* N, L% J
    1
    8 ~4 Y5 S/ p( b1 Y' X+ N, A* O2, N( F6 A" {4 r1 j( g: c3 D1 m: I
    3
    " Y: ?0 ~/ g' X; q7 d* `" N4
    % w  s6 m' q3 a  T  G5
    ! R: Q- C$ f. ?! x69 y: g. K6 I* ]! i5 N
    75 a3 `8 b$ W" M0 T( t$ L
    8
    / o0 _- z) F( V/ a, u( C6 n9
    1 F4 A2 w/ ]6 D3 b; E& l/ q10
    : a/ }+ [, ~# _: K- A11
    . p1 t8 p2 y! c6 t9 G! m12$ O- x" Y  _4 z) M5 P
    13. L" x8 D, j% I4 u" }
    14
    " |% p6 o7 d% }% ~! ?15! F" h, ^, {. B0 E: R
    16, j! G8 [- j: O# f2 c
    17
    $ n- I$ M1 L% |, x( N8 X# ~18
    6 N4 N% N. T1 F" ]7 y19, `& w7 y/ D! p) K9 V
    20$ h; Z7 J4 w& R1 q
    # 参考答案) u. q- x+ U2 P: V/ A, u
    s_area = pd.to_numeric(df.area.str[:-1])
    " @5 g/ @" Z3 b+ }s_price = pd.to_numeric(df.price.str[:-1])
    & T' h& V& O  @2 U  Z. P% Gdf['avg_price'] = ((s_price/s_area)*10000).astype($ \2 ^, A5 M# f3 q
                        'int').astype('string') + '元/平米'  g3 P6 M' F4 g- B# e

    4 P: O. l# M" udf.head(3)9 x; c! K, S% p" f( y$ E3 L
    Out[167]: ! G+ _" }, C' E$ `
       year    area   price   Level Highest  avg_price
    ! E/ a/ H: Y+ E, v0 V0  1986  58.23㎡  155万    高层     6          26618元/平米
    ' B6 K2 O0 N# K) ?8 T1  2020     88㎡  155万    中层     20          17613元/平米- n7 v* I$ g, Z; _$ t# l
    2  2010  89.33㎡  365万    低层     28          40859元/平米
    2 ^% G6 y0 _0 S6 K3 L. `8 S# Z1
    & _  d: f9 ^4 i$ Q2
    ) s: a. {& u& i+ f8 Y$ C* t3
    " Y! D& \; e, U4
    " w, l/ c" I- T9 I: ]" c6 j5
    & p! p; |# w# y9 [; K& }& Q61 G% M* v# k+ F, C
    7
    $ |7 l* D1 S% z. P$ g4 Y8, n' e2 }) E( J9 K6 ?7 e
    9
    & m- g! b. Q5 w( N3 X# o10
    , _6 o6 H* [2 Y, ^7 ]4 ^5 s11+ F! d3 c8 C) z8 l/ A
    12% i5 i; T& X/ R6 t+ B1 q
    Ex2:《权力的游戏》剧本数据集
    ) V0 b- G5 M9 p0 P# o+ G" B现有一份权力的游戏剧本数据集如下:2 e6 D% u' Q' }; V0 }+ {
    & @7 d6 O, J1 P( g! a% I
    df = pd.read_csv('../data/script.csv')* R" ^! W0 V; h- W- S
    df.head(3)$ g. y/ P& b4 f
    2 H* e+ _% L1 n8 \) `6 I7 N
    Out[115]:
    7 m1 `% ~: n/ D% jOut[117]:
    4 |2 N5 n" l8 ?  Release Date    Season   Episode      Episode Title          Name                                           Sentence
    ! _: O0 z% [; Y' x) G0   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce  What do you expect? They're savages. One lot s...
    , b2 Q+ ?: e- j1   2011-04-17  Season 1  Episode 1  Winter is Coming          will  I've never seen wildlings do a thing like this...
    9 ]2 R4 j' J" |' A. n1 d3 K2   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce
    ; u, j* O5 b+ n' P' C1 Y# Z$ m1& e, b% }6 X4 _7 o/ o
    24 C/ {% t' _3 V9 E& P9 p2 E9 U
    3; j9 R" P  T7 L$ }+ v
    4& Q) m: X$ q' z/ I4 r0 K
    5
    7 Q& [) e5 g. `) {. s6
    + o" _5 y. L5 L( z) f7
    , E& ?9 N1 }! L# k) w% }8# o6 w* I/ a" G4 a8 u+ ?& @2 i% `
    9
    $ j% d0 W/ ?. f. g. g5 l3 @计算每一个Episode的台词条数。
    + [7 [( N0 B+ W6 _% ?4 h以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
    7 q" Y  g+ A7 j3 X1 R若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有 &#119899; 个问号,则认为回答者回答了 &#119899; 个问题,请求出回答最多问题的前五个人。& H) b) N* f0 ^4 v
    计算每一个Episode的台词条数。9 [) O5 A- l( S2 R
    df.columns =df.columns.str.strip() #  列名中有空格' |0 C2 Q! P! {2 ?) N, R% H" U! {
    df.groupby(['Season','Episode'])['Sentence'].count().sort_values(ascending=False).head()
    - Q( n; O6 R9 m, I! m6 Y5 B  h+ @5 x' L0 Z3 o& i5 A
    season    Episode  
    : o' l4 p5 j+ x9 G' i" A/ KSeason 7  Episode 5    5056 A% F% {/ y4 N
    Season 3  Episode 2    4806 q) {5 U0 {7 ^1 C% Z
    Season 4  Episode 1    4755 g( \$ o6 f0 J% L0 b
    Season 3  Episode 5    4406 e0 C+ A8 G* @
    Season 2  Episode 2    432% l' ?- m* w$ I! H
    1
    : M2 w5 M8 w+ V* B7 s9 \2
    9 v  e+ {+ N- E: S7 f4 B+ N) d3
    4 r5 u2 R; S: l8 V3 y( Q/ m4* P( ^2 N( W/ T9 Z1 g/ r
    5% y8 d% P% j: ?6 }# N; A
    67 y; q1 M, K: o' ~
    7
    " L1 m8 @- m8 d1 L* Z8
    . g0 y  U$ @0 d+ v. X9
    8 L- s% T# M8 v' c; |8 N+ p以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
    6 o) s0 g5 I, W6 X0 p# str.count是可以计算每个字符串被正则匹配了多少次,+1就是单词数
    , X; D& P) F$ d5 wdf['len_words']=df['Sentence'].str.count(r' ')+1
    : w/ V' }) P; vdf.groupby(['Name'])['len_words'].mean().sort_values(ascending=False).head()$ n5 k0 R2 g/ E! ?) q+ T+ z+ i

    % V4 n0 H0 Q+ U9 JName
    : U) ]. L% a/ t& g  _* ~6 g9 nmale singer          109.000000
    + w6 W* Z/ {$ u+ r# `slave owner           77.000000( b7 X* w4 q3 D5 i& i
    manderly              62.000000
      O# N/ R* V, `! ~# O0 Ilollys stokeworth     62.000000; b; P* J4 u( W
    dothraki matron       56.666667
    * r! {( a5 \, U! AName: len_words, dtype: float648 q3 k: B( a: u- [
    17 n; H0 y+ [% Z7 R/ K  H  P
    2, k8 x5 X. U, T. X- f6 V
    36 k1 F+ W/ z: M. |& M
    4
    * f3 z; c5 N+ U5 h59 Z; ~( @9 s% A
    6
    2 O* H: \; o" z+ z/ s; v$ ]% J77 n4 H% L( ?. A* e
    8) [' z  f1 S0 @
    9
    - I; r" B9 Y4 S3 W* ]10# i; o" l# n$ c/ M* N7 i3 A/ j6 h
    11
    * F! g! c5 N2 ^' r, n$ v& W若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有n nn个问号,则认为回答者回答了n nn个问题,请求出回答最多问题的前五个人。& w! [3 i' g+ M: P8 T; k7 \4 F2 A
    df['Sentence'].str.count(r'\?') #  计算每人提问数; |2 [% M" p6 d6 N9 @
    ls=pd.concat([pd.Series(0),ls]).reset_index(drop=True)# 首行填0
    0 Q5 Y& J* M: sdel ls[23911] # 末行删去
    / e3 ~" L* a  C- m; V/ P; Edf['len_questions']=ls
    " O$ ^: m! U1 T  `8 U  pdf.groupby(['Name'])['len_questions'].sum().sort_values(ascending=False).head()% p+ F1 ^. ]/ v; b) u% `3 ?/ l
    ; \0 }) {% b- O  |. A
    Name
      y6 b! Z: t2 f9 K3 i# m  q# atyrion lannister    527  a/ e9 s' _4 u8 h
    jon snow            374, u2 l; }' d0 J- k( k0 `- u4 Q
    jaime lannister     283
    . ~6 V+ w' G, Qarya stark          265
    8 c/ O! ~2 X( ~; F7 B- t+ e/ m& lcersei lannister    246
    7 ~& ]! l" |. ^6 wName: len_questions, dtype: int64
    , o- z" _3 K0 Z& }: d, j
    $ L/ ~- L6 |& ~& o# 参考答案
    * M4 t  Y9 H) s, ^2 M+ H' n" p0 [s = pd.Series(df.Sentence.values, index=df.Name.shift(-1))& ^* k# c; }2 u# I
    s.str.count('\?').groupby('Name').sum().sort_values(ascending=False).head()5 m$ B: ?" e7 w& O* D

    - {0 s+ c* _) G7 R, u% A1
      D* O) V7 Y* ^2 a) p8 F, n26 g8 W/ v* T& F/ F  \! v: M9 ^% w
    32 j$ u* c( V8 ~1 O
    4
    * l( T8 h: v( c9 x% W& {" r5
    & T% s/ k# i* V! Q2 s7 M( j# ?6
    1 }4 g) c! t6 \# O3 ]( e5 d0 g78 I3 [$ X# P5 l' Z4 X/ s8 e$ f! J
    8
    9 B$ N- o* ~! `9: e0 g/ ~  h& x+ |
    10
    . {7 B1 a! F# d11' @- U+ R& V# c0 G8 S
    12) A  e  L/ c5 m, ~+ Z
    13
    1 p- T- v! a. r: h" K5 ?14
    2 C( l( j; L- I8 {& t15
    5 z' A8 d0 |, Q16
    - X( u. O0 _* s0 I; K' ]5 B) L17- f& s* c/ X4 p4 K& P
    第九章 分类数据3 h" X' z: y; m2 W) k
    import numpy as np
    $ m, P! i5 l2 a0 \  ]* ^8 ]6 Wimport pandas as pd
      f/ Z% y% }# z+ q13 O% |9 [. D% }% p; n
    24 Y0 w- O2 c  P) w' E
    9.1 cat对象
    ' Y, N1 K+ v" h' [  G9.1.1 cat对象的属性
    ; k9 c0 [& B0 o. f- x  在pandas中提供了category类型,使用户能够处理分类类型的变量,将一个普通序列转换成分类变量可以使用astype方法。
    ; d3 F5 S7 ^% a( e" v2 r9 P: {! ~8 }* R5 C
    df = pd.read_csv('data/learn_pandas.csv',2 ]& V( G( z* x' I( I
         usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight'])6 e5 |5 p+ @9 g# J. a
    s = df.Grade.astype('category')# J) r+ `7 p2 W0 C

    2 k0 ]* @* O7 }s.head()
    8 Z" {  K- m1 g8 t. b8 a, x  d% hOut[5]: / w6 W. B1 ^: |$ k
    0     Freshman, O8 w$ k7 S' o6 E( r
    1     Freshman( k2 P) U! P) B7 T% N: c3 `
    2       Senior
      c! H1 t- X* I2 Y3    Sophomore
    : q' x7 i  q+ S# H, A4    Sophomore; Q1 e3 `, m$ L. w4 |6 S* l4 b2 R
    Name: Grade, dtype: category
    2 v6 |( Y8 R& i$ BCategories (4, object): ['Freshman', 'Junior', 'Senior', 'Sophomore']7 _5 j/ x3 y- J+ O& \
    1
    5 i! X/ O4 }: O' q" N. W2
    7 @' B* n! d5 N/ f# o! d! @3
    " W8 k9 j2 _& H% }1 \: I: d45 D& D" U4 j/ B: T# B' H  m& O
    5
    # d6 D5 M7 B7 N/ @6  p0 j" a, g6 ~% z# s8 g6 o
    7: j  [- t, b' k0 m5 C& z, U
    8
    2 `# |3 |' ]. Q91 d' O% Q6 u. Z1 K# K9 A# ~
    10
    ) G" E& u7 H8 z11- f  i. u, ^  W0 ~' k
    12
    1 a/ s! r# N2 E13% b1 J) F5 h5 G- _4 y  X. w4 F
      在一个分类类型的Series中定义了cat对象,它和上一章中介绍的str对象类似,定义了一些属性和方法来进行分类类别的操作。
    . W& a# [+ G. [% S7 n1 q( d
    $ `( C2 {& f/ k' S# s' Us.cat, a% J# H; h! m* i0 c; {4 a9 M
    Out[6]: <pandas.core.arrays.categorical.CategoricalAccessor object at 0x000002B7974C20A0>0 Z0 L" d9 S9 P3 g
    1+ A( K) n+ r: a7 g
    2$ U, t9 I. {4 a
    cat的属性:; X1 e$ E5 j5 L; T6 T) z$ s

    / u: {8 T2 D& {# I* dcat.categories:查看类别的本身,它以Index类型存储6 I  N' K( g. x8 X" c- a
    cat.ordered:类别是否有序. C* C. g3 d$ k# ^5 v, ]) x
    cat.codes:访问类别编号。每一个序列的类别会被赋予唯一的整数编号,它们的编号取决于cat.categories中的顺序
    , M' f# b1 T% B3 t9 Gs.cat.categories! Q0 s9 U/ \9 W- b4 G
    Out[7]: Index(['Freshman', 'Junior', 'Senior', 'Sophomore'], dtype='object')( f2 _$ h8 P; _/ t* h# v* \7 b' t
    # ]6 Y# W/ Y& F$ x, {
    s.cat.ordered% _- q" U3 a4 K4 H
    Out[8]: False( O) F# k: L8 A( \4 m3 h: F/ d" F

    : p: @9 h: r& H5 qs.cat.codes.head()
    , G2 o! i7 x4 mOut[9]: - p  L4 l) \/ a! C# Q/ x' Y
    0    0; b! m, M: ]$ _$ v
    1    0
    / Q1 f, a" f2 l0 A0 k- V$ P3 a2    2! `/ |0 f( z% a% a6 T% J* M
    3    3
    8 t8 e0 L  b' T( P- |4    3; O0 ?3 ?+ @$ W
    dtype: int84 \' `* `; S' b2 S+ [3 D3 M
    1
    % W3 U5 }9 U) _9 G" S2' L; T. S( Z( z) x) `2 N' q
    3
    $ X. X% @5 K2 W$ b' k4; u% Y2 Z! g5 D5 j0 S- s5 r! w
    5
    2 r0 g8 u+ T+ m) y6 Q" Z63 d' d( H, `" Q1 k
    7
    : b* B8 C$ z+ Y2 d# W& L80 H8 U# t% x% i
    9
    ; o5 m1 k8 }( j- E, {/ N10
    0 R  K5 }. s4 ], b$ W* Z9 Y11
      U1 x1 t3 a; u3 S% l+ x123 Z+ i. @4 g3 A, ?6 d4 ~8 b, x
    13
      F- B8 a6 d$ {, J: L" ~14% Y; s% Q! R* ~
    9.1.2 类别的增加、删除和修改
    + X$ t$ }2 r; a8 Q* T7 v1 L  通过cat对象的categories属性能够完成对类别的查询,那么应该如何进行“增改查删”的其他三个操作呢?1 \$ G* G. ^7 t5 ~, T. o
    , i) e  }# r8 K9 x3 K  M
    【NOTE】类别不得直接修改9 q& B' _. X! o
    在第三章中曾提到,索引 Index 类型是无法用 index_obj[0] = item 来修改的,而 categories 被存储在 Index 中,因此 pandas 在 cat 属性上定义了若干方法来达到相同的目的。5 I: p8 @* M* a# Z9 [
    1 P0 ]4 P" G6 u& K5 T/ |. w
    add_categories:增加类别. R- i1 y8 i1 R* M" t; \$ N# `( P
    s = s.cat.add_categories('Graduate') # 增加一个毕业生类别
    2 u) n# C7 j1 W. os.cat.categories
    ! v4 _$ \9 R+ G. t8 V! r; f
    3 w9 T! {2 o8 G+ E+ E0 q' Y  OIndex(['Freshman', 'Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
    2 x/ q- u, Z# B7 W8 H# t1% c; W8 n! [7 @
    2
    3 K; }! q# z, \1 }. ~6 ?0 K3
    1 a* t) q  G0 E5 o8 B6 G40 |2 I8 O1 k' V
    remove_categories:删除类别。同时所有原来序列中的该类会被设置为缺失。8 {1 f" L& E" ^% h& I. x! V1 u% p
    s = s.cat.remove_categories('Freshman')
    8 z1 y% [  n$ s) x% g
    & c. P- e* D1 o, f4 l9 _" T, n% As.cat.categories
    * d) J" }! P! J4 {7 l3 jOut[13]: Index(['Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
    ( j! f+ {- O* B/ J( w0 x* G; y
    2 T2 `) J/ x$ m& K# Es.head()
    * N  P, D$ O: j3 }Out[14]: . H$ t5 C/ q  a$ q
    0          NaN% k2 @& H0 r- {: k
    1          NaN
    2 w2 i2 R! N9 e7 H' s' [& `2       Senior
    / ]! C0 Q" j) f2 {; b- n3    Sophomore* ?; G" B7 I& ?9 \5 k
    4    Sophomore# S1 p% u4 h) x) I- x
    Name: Grade, dtype: category
    1 t7 T: F5 S5 j' s# HCategories (4, object): ['Junior', 'Senior', 'Sophomore', 'Graduate']* Q! G# b" G# h
    1
    2 t3 F# B4 a3 Y/ P) ?  Z& a- p; L2
    0 j1 j) M* y& C2 N* }6 ~  Z3$ x) g% T( f- G7 L) L
    4
    8 P! e5 t! R  s/ I* M6 b* i5
    ! z( X3 k3 x5 S) z6
    # J. i5 X* W5 @9 G& V: g7
    " E1 i! ?$ I7 X0 i1 b8* C( H" y, ?  f
    9
    * K% A* K, i' q, ]: F9 X10
    ' ~. R8 y) N* F0 o11" z+ n2 A+ b! [% S% a7 c2 W
    129 s, S; k4 d  |/ S+ Y
    13
    3 _6 a$ }( P! w' M, z. \9 N" B14
    ( S' L/ L+ r) s+ r  P$ bset_categories:直接设置序列的新类别,原来的类别中如果存在元素不属于新类别,那么会被设置为缺失。相当于索引重设。
    % i  S  @# d$ V; f, E0 a. B8 Ys = s.cat.set_categories(['Sophomore','PhD']) # 新类别为大二学生和博士7 ?- f$ ]3 A$ Y$ k! B
    s.cat.categories& [1 ]" N. q; ?& V) p5 z( Z4 ]& [3 @
    Out[16]: Index(['Sophomore', 'PhD'], dtype='object')
    & m/ r% ?* m: u& @, d) i4 I( g0 L- n5 c  ?( x* [2 K5 q6 E$ p: ]. L* @
    s.head()
      {# F+ n0 u1 A3 T, u) ]3 `2 zOut[17]:
    # }/ Y" l5 ?* @3 y' ]5 [' @1 h/ ^, o. X0          NaN: U# P. m& F4 R$ r: k) }/ f
    1          NaN
    & N$ B7 V9 s8 D* {* C+ w2          NaN
    * l" Q6 \2 W* r; ?3    Sophomore, B9 [, J' R: p9 T, ^
    4    Sophomore
    . a, I2 A9 n3 u3 d$ y$ GName: Grade, dtype: category& a7 }: C* a3 ~( v/ z; A$ g
    Categories (2, object): ['Sophomore', 'PhD']
    - ]) `" r. y/ a8 }+ }4 c* i1/ }6 l- R& t0 D
    28 u- F- q$ U( M8 {' ~) r) C
    3
    8 d, ?0 A* |* h* k: G# @$ y2 w4
    7 E' w, m4 ?8 V( j) ]5
    5 m) j- D0 ?, v7 W, m& v' L- ]6
    & U  j, M8 f. O74 n& h7 T5 Z* g4 l& S0 j
    8  I$ O! {* u# n. Y5 h. k
    9
    / ^6 Y# P2 B# D9 D10
    : C: g' ^4 r8 z9 \5 O+ I11$ m3 P0 r' n0 x# l7 M
    12
    # a' C$ H3 J# G- x0 {9 T  z  J138 v& J) T3 j' }4 N1 N+ v/ Y5 a
    remove_unused_categories:删除未出现在序列中的类别
    4 X9 J( b0 B- _2 a4 \s = s.cat.remove_unused_categories() # 移除了未出现的博士生类别
    0 G4 ?' p! x9 z5 Hs.cat.categories" ^3 d9 G% z9 P- y" R' ^
    , Q" D. f, ]! f# `& V/ F. Q
    Index(['Sophomore'], dtype='object')
    3 [4 ^/ F8 R: g- C- c: c# m( w# f1
    4 h7 C- p0 b5 Z: K4 W2 {2
    9 l1 Y* M- j8 x: p( t; N% f  c3% D) B; N. b* F' N$ A+ c
    42 R% m$ d( T2 _& {
    rename_categories:修改序列的类别。注意,这个方法会对原序列的对应值也进行相应修改。例如,现在把Sophomore改成中文的本科二年级学生:& E1 X& r; f7 C2 Z
    s = s.cat.rename_categories({'Sophomore':'本科二年级学生'})9 D; e. n- t# f- K: q' j
    s.head()3 o3 ]3 u$ D, |* |/ M1 L8 Z
    5 V* T% P( g) p4 Y2 \7 V
    0        NaN7 O6 @$ k1 X2 n4 Q2 u9 L$ K# S/ K: u
    1        NaN* z' }# l) h0 Q4 G! k, d4 \
    2        NaN+ f, T9 w  g$ e0 f. Z. i6 Q! `
    3    本科二年级学生
    , [' u/ c% b2 g6 N4    本科二年级学生
    + l  Y3 l! @8 R. }" YName: Grade, dtype: category9 }8 G( n' x' h8 u1 U5 N' g
    Categories (1, object): ['本科二年级学生']
    - w( e: C4 E/ G0 d) j# @( J1
    " r& |1 H& X  N( t: X1 f$ n; w2
    # f' E- E+ Q: C5 e) Z7 }3
    ) k7 R6 i. Q$ f& W1 a; J4
    : d0 q4 O! K1 i1 }5 L  k5. I2 x. p( f, o! |: E1 S
    6
    + O! p# J& V* ^- N$ v- w7
    4 c3 x% ^: p" E0 [  D. h+ [: p86 P9 I5 o/ \! ?# [+ c4 E6 u: c
    98 w, b5 _0 V7 C5 R# r9 W9 U
    10! G' y! w* _3 n$ T1 ^7 j, I) U
    9.2 有序分类8 z8 @0 C! H# P6 F9 G2 D+ x  m
    9.2.1 序的建立6 ]/ ]9 G  b7 {! H/ t5 J9 ^
      有序类别和无序类别可以通过as_unordered和reorder_categories互相转化。reorder_categories传入的参数必须是由当前序列的无序类别构成的列表,不能够新增或减少原先的类别,且必须指定参数ordered=True,否则方法无效。例如,对年级高低进行相对大小的类别划分,然后再恢复无序状态:$ w) d+ n- }+ Y3 {  M; {

    2 f$ v$ m, E& r0 |5 b1 y4 Os = df.Grade.astype('category')  c, y: N1 ]6 N3 X4 e
    s = s.cat.reorder_categories(['Freshman', 'Sophomore',4 D0 P$ i& V9 p) c; M( B
                                  'Junior', 'Senior'],ordered=True)
    * l- H6 Q4 Z5 M. Ds.head()
    ) J8 F  o, J* i3 dOut[24]:
    2 \, S6 R9 ]$ n- M; B2 ?: m9 ]$ g0     Freshman9 F3 {' f7 z8 y1 U$ S4 I
    1     Freshman6 _- n  O  \% S+ w, k
    2       Senior
    3 {6 x# e5 y/ |+ i0 k- w# x7 Q0 E, ]3    Sophomore
    / F. U6 W: S  P$ N; |3 X4    Sophomore
    * _2 |! `6 Z3 a4 D& N2 AName: Grade, dtype: category; J9 t- _, o, _
    Categories (4, object): ['Freshman' < 'Sophomore' < 'Junior' < 'Senior']
    ' p  T( T/ H' k9 b
    - m3 t; i! Q! F" Y4 c" Is.cat.as_unordered().head()0 g9 j2 T9 y" s0 P$ I: Y  X8 M
    Out[25]: : y, p: w1 g' I) s1 f+ T
    0     Freshman# _6 b, O. L: ~
    1     Freshman
    8 S- N! A3 y) n1 d: n2       Senior
    - J8 @/ Q" {, O. c; w5 \3    Sophomore
    / U. X  j/ x3 ^2 {  z: V4    Sophomore
    ; O' ]( j, d% X; K) zName: Grade, dtype: category
    / R) }7 j/ h4 \8 z  yCategories (4, object): ['Freshman', 'Sophomore', 'Junior', 'Senior']
    $ d# z2 J; O9 _5 i6 L# {& c1 O, L7 h' p$ S6 P4 U
    1" S  j" y. @/ G7 H
    28 e' L: s/ M! A
    3. k2 q+ @  J/ D& M. h
    4: W& e! b4 f  V- V" v1 y
    58 b4 B& }4 J0 n' e
    6- ?" \  ^) x* X( Q
    7
    ( L+ a0 _/ q+ Y* a, M0 v) M/ @8
    % N! f: U2 p: z* M- m% v% j' R3 L9) o3 w9 Y$ X3 r! {0 F2 F/ `
    10
      @' K) y* b; {. r. K11
    7 Y- d. E/ Z+ G4 i0 k/ H7 A) \125 D& u6 r! N. B* M# n9 u2 ^% X
    13
    + `5 g7 k- l6 ]% w+ ~# x" Z14
    3 F/ }: \6 S0 t: F# |15' }4 t6 R: [& T: n1 @
    16$ A8 W$ M6 T% \3 I- d( i/ }
    17
    " C& u  x9 u; k3 n5 c9 @% M18
    ! o  w  q- M9 L; p' I/ b, S19
    ! ?7 Z$ ~6 r) F" Y6 b" d3 e2 D; a4 |20/ J* A5 n0 B3 u. ~& A8 C: D" O7 q
    21
    6 D1 n- O' E- J8 k2 V/ Q22; d0 Y$ E; y. v) T+ G9 C( m
      如果不想指定ordered=True参数,那么可以先用s.cat.as_ordered()转化为有序类别,再利用reorder_categories进行具体的相对大小调整。
    2 J( l) H  Q* P6 r5 v
    " A6 D: v3 s9 d; d/ B4 a$ v9.2.2 排序和比较# ~6 Z9 A7 y6 C
    在第二章中,曾提到了字符串和数值类型序列的排序。前者按照字母顺序排序,后者按照数值大小排序。
    ' h) o* q. @2 E/ b4 ~1 s: j+ F" u  M, J; E/ J
      分类变量排序,只需把列的类型修改为category后,再赋予相应的大小关系,就能正常地使用sort_index和sort_values。例如,对年级进行排序:6 R" }. ^0 I5 x, P
    & j. O" {4 J& U
    df.Grade = df.Grade.astype('category')* @6 |" M5 N' ]. ]. s: L/ N/ ^) ~) y, S
    df.Grade = df.Grade.cat.reorder_categories(['Freshman', 'Sophomore', 'Junior', 'Senior'],ordered=True)
    2 E; c) y9 q1 P! E* o- Kdf.sort_values('Grade').head() # 值排序' F0 L8 ?: X# f; N, J- V# {& V
    Out[28]:
    6 D+ H" i0 U: y$ X. d) ]: B        Grade           Name  Gender  Height  Weight5 a" y4 k( ^0 H) M: W2 T& Q
    0    Freshman   Gaopeng Yang  Female   158.9    46.0' t/ E: d5 W. l/ y( @9 Z2 A& Y' Y
    105  Freshman      Qiang Shi  Female   164.5    52.0# _6 j! }) S7 Y( S( n# F9 p
    96   Freshman  Changmei Feng  Female   163.8    56.04 {8 ]; w" h, Z/ w6 Z% u" e& X
    88   Freshman   Xiaopeng Han  Female   164.1    53.08 {0 _( _" ~! |" ]& J: N+ n9 s
    81   Freshman    Yanli Zhang  Female   165.1    52.0
    ! n- o- o& m. k7 R# d( d" J* C* y2 W4 y8 D, b2 D
    df.set_index('Grade').sort_index().head() # 索引排序
    2 Y" x  Q0 e4 c4 ?9 c: mOut[29]: & ]& V) I* z9 \
                       Name  Gender  Height  Weight
    : D) m4 C& a; fGrade                                          * q/ Q" d# d5 \2 @1 w& S3 n
    Freshman   Gaopeng Yang  Female   158.9    46.0
    # x! x; T3 X* zFreshman      Qiang Shi  Female   164.5    52.0
    . G' k; E) n; f. pFreshman  Changmei Feng  Female   163.8    56.0
    ; ^% }& G. Z1 `6 V/ H# JFreshman   Xiaopeng Han  Female   164.1    53.0
    * N$ P# C: p4 L! r0 {Freshman    Yanli Zhang  Female   165.1    52.0
      J( A5 H6 ~$ k0 e1 O( g5 J. B
    2 }# |+ l, E+ ?1 \. l/ p1" L9 Z9 S6 C: Y% }5 x' H3 l
    2
    , i0 B# }; \" I3! y' M9 c2 U( k/ l) n
    4
    $ m' z6 x+ V$ T7 l54 P7 x! a5 T9 n1 i+ k3 S0 d
    6+ P3 ^  x% ?. j; [4 }5 t6 E
    7  ~; z5 {9 o- M3 `8 o0 i
    8. X/ t) `+ g( H2 u  H( P: j
    9
    * \) ^* J" t7 f5 c: H10
    . H/ W  ^  }+ m8 [$ \; j6 s* Y  v! _11
    * b/ E  K& q* F2 B( m/ t12" ^, J4 m* n0 ]8 _& [4 v
    13) U/ l( v* O/ k6 N
    14& |$ \: r  b& _' u% C, p% V; n
    154 D& `, c7 V1 y! y
    16
    0 T! j1 N6 n3 _& \17
    / ^$ `/ \& I' e0 T, C+ [7 S184 D1 B8 W$ ]* n# e" h. i
    197 z4 k9 S! X' {$ e
    208 M$ S* C# q* ~
      由于序的建立,因此就可以进行比较操作,方便后续索引操作。分类变量的比较操作分为两类:$ e1 I# _, w5 i: C7 U
    1 I& w$ R4 l5 v5 ]) d) A4 d0 Z
    ==或!=关系的比较,比较的对象可以是标量或者同长度的Series(或list)。(无序时也可以比较)
    * L3 B# a* M" n: d- X5 Q% U>,>=,<,<=四类大小关系的比较,比较的对象和第一种类似,但是所有参与比较的元素必须属于原序列的categories,同时要和原序列具有相同的索引。
    3 h) C+ X! H/ Xres1 = df.Grade == 'Sophomore'; E9 ~+ W* f4 z; M& G# E4 W

    / Y: H$ z- p# }! Ores1.head()
    $ @, v& D: G3 A' m) I- n! l, }Out[31]: ) R2 i- n* C" ^2 ^* P& e
    0    False
    + X: Y& w4 c7 D# }( i+ @1    False
    . O% Y! K$ s* h* _' S2    False! H: h6 w6 |2 \1 U; \8 k" x
    3     True
      Y. m; ^6 d4 j* X% {  h4     True
    " I& w3 s# ^/ C( GName: Grade, dtype: bool
    0 R7 s5 n2 F! e" w9 j' H' O) e3 E! H
    ! u4 G+ k& H* ^( ?" [res2 = df.Grade == ['PhD']*df.shape[0]+ g' X0 B* B4 z

      y% a1 W0 S6 \; d1 P' i4 f6 @res2.head()  J" a0 t, T# B& l
    Out[33]: + s# r5 E+ }% l
    0    False+ h6 C  ~4 N* F3 Z/ @- h
    1    False
    ; N8 I5 ]8 F6 A7 O# y: @  o9 H' F) r: ~2    False8 Q) X  a: J; E+ @0 c! [
    3    False
    0 O+ w  c) \  Z" R2 V; E$ Q4    False
    ; }8 a& e  T) `Name: Grade, dtype: bool
    $ I& R' I+ w- z1 P, ]' I& p3 a; m0 _! b3 {0 [2 \
    res3 = df.Grade <= 'Sophomore'
    9 G3 ?4 l0 ~; N  c& k3 Q( v5 `) N& d$ m4 |5 A, ?$ I3 }! G
    res3.head()+ _4 E# ^' R0 ^8 a0 E% w! y' S
    Out[35]:
    0 z2 W- P' [6 m, i/ m, `0     True3 G; J7 R! P; p2 v
    1     True
    8 y( i$ b( {3 j) ~2    False2 z" j/ ?* U- t7 l" D
    3     True
    5 O! b; h" e# s* j" S, y# T( j4     True
    " W+ E9 b$ I/ a" x$ S& x3 aName: Grade, dtype: bool8 p& i9 J& n7 e& p( j- y/ S6 Z

    / v7 e+ r, u+ U2 A: Z# r9 N# sample(frac=1)表示将序列随机打乱。打乱之后索引也是乱序的,直接比较会出错,必须重置索引。5 ^! r2 {6 @+ s0 Q# M  S* a
    res4 = df.Grade <= df.Grade.sample(frac=1).reset_index(drop=True) 6 ^" M7 q! u1 V2 R* w' T4 R

    ; I' |6 S+ u$ o# m5 ores4.head(), f3 ^' _3 L2 F: }8 k
    Out[37]: ( I# K2 [! e, c9 |# x# p
    0     True
    0 ^& \6 r- A& S/ R6 e& X8 t( T1     True
    / i: t. ?1 w* ~+ V( a2    False( V# d( w' }% K$ s
    3     True
    " m( J# V) o! }5 J; X4     True6 c- p; g5 T5 v# F- \0 v
    Name: Grade, dtype: bool. Z8 {( d& Q; `& G" a% D' |3 u
    * y; P3 o+ k. d. P
    1: M0 T/ |/ X) ]3 X% l: x, C
    2
    * ~% ^( S- t# C& }3
    ; {4 C* _, ^  N( v$ D5 U. E4
    . V, g+ \% X7 K7 V/ g; ^1 C5
    5 p. R3 X2 b5 p+ ~6
    / u" H1 ~- B' S1 h0 @7, O$ |/ j$ D# V& u. v' ~0 R
    8. O& r- U7 y: L# v0 O+ e
    9
    : |' X- g7 ]. c1 f, c$ w3 w/ a* A10
    , s' {: W. W1 L: ~  T- e11- w: |7 h: y) O2 X
    12
    ! `+ j$ ?1 ~' D7 H* z13
      [+ z, ?& }& F6 }146 S- E0 z# `% \! ?* _: n0 y2 k
    15' X9 s+ x. [/ q) J9 E7 l
    164 U4 c* E: N6 \$ @3 f) s6 Y
    17$ p: m/ k5 `! n' E! ?
    18/ G6 ~, r. f8 z
    19
    1 y$ d- M% k, ]7 m201 f5 v6 {3 P7 ~5 c& e  p) o* r% v
    21
    4 k0 J8 M# c* o9 l22
    ( j% x1 p/ O8 ~" M& G# a5 s231 c/ Y0 o6 s- O$ K1 I
    24' u  R5 m4 L  u- V  w% F7 `6 X
    251 E* L9 B6 ~2 P
    26
    6 J: s, C- b- _/ J: u27' [+ {) P. `1 o0 y
    28
    " B1 y# E/ a- E296 w" B4 ]2 z& W8 @" z+ x- G
    30
    , K% r: Z, T0 H. n316 E- u; c. p% D' S# U( {
    32
    , X/ b0 W% a( p$ Z# \33
      O7 k5 D, {+ B; W7 Z. K& J9 T34+ k$ ?2 }) B3 L: b- q/ x
    353 m% c# a1 V4 }* w$ ~/ C5 q) N
    36% l9 w2 a' S/ S
    37' h6 K+ m( W; U2 G
    385 q9 P- }, T8 l3 E8 O- A; e
    39
    2 p7 Z: {, k0 P; U40/ r2 I* k3 j. B5 |. n/ Z6 C7 n
    41, M, }1 Q# `2 d2 m/ f) J9 Z
    42
    + s- E$ ]) m# k, H43
    8 n6 x8 R6 w* n/ p. r/ @8 i44
    5 T& \6 g! {. Z# z/ S9.3 区间类别5 y+ N5 H3 K; W$ @; n: V8 j; E
    9.3.1 利用cut和qcut进行区间构造
    5 K* w1 u9 \4 ~! ~" B9 I  区间是一种特殊的类别,在实际数据分析中,区间序列往往是通过cut和qcut方法进行构造的,这两个函数能够把原序列的数值特征进行装箱,即用区间位置来代替原来的具体数值。' F* X3 w- h* S4 K, m

    * U/ t1 z/ F" N% Mcut函数常用参数有:
    ( Z- d$ a; e) s- \, O, ?- P5 U& qbins:最重要的参数。
    3 P: c, [, D- _# i+ A如果传入整数n,则表示把整个传入数组按照最大和最小值等间距地分为n段。默认right=True,即区间是左开右闭,需要在调整时把最小值包含进去。(在pandas中的解决方案是在值最小的区间左端点再减去0.001*(max-min)。)9 `1 D7 U# b/ r
    也可以传入列表,表示按指定区间分割点分割。* K. D- U: A) A. y! S5 O0 C
      如果对序列[1,2]划分为2个箱子时,第一个箱子的范围(0.999,1.5],第二个箱子的范围是(1.5,2]。
    + ]0 r1 }2 }6 p$ f0 w3 G$ j3 M, o  如果需要指定区间为左闭右开,需要把right参数设置为False,相应的区间调整方法是在值最大的区间右端点再加上0.001*(max-min)。
    , a  O4 a) U/ P7 ]5 I( r1 a  u( O' @3 D+ i/ B( y
    s = pd.Series([1,2])# h7 O9 ^, f1 e9 p8 t3 m
    # bin传入整数
    - P4 l+ V! G% ^/ o9 A8 F6 q
    / g& n% L3 V9 Bpd.cut(s, bins=2)* W( q4 C5 Y) h* N
    Out[39]: - d! x+ z  S. j! I
    0    (0.999, 1.5]1 J( J2 r% S( I- z) c
    1      (1.5, 2.0]
    " g$ ?" L0 n" V' Sdtype: category
    1 K$ C4 y0 o7 G, L8 b$ `& `3 hCategories (2, interval[float64]): [(0.999, 1.5] < (1.5, 2.0]]; u  ?7 i! ^* q- _
    ( N5 X# _3 m) C" O) n1 J+ i' {
    pd.cut(s, bins=2, right=False)' a' K8 A; ~0 i! o7 o0 Q& x( ^
    Out[40]:
    7 a" o) b* z! D+ U! `0      [1.0, 1.5)1 S6 l7 B( f' d
    1    [1.5, 2.001)+ }2 r& o1 P4 m
    dtype: category1 f1 G" D, q9 s/ \
    Categories (2, interval[float64]): [[1.0, 1.5) < [1.5, 2.001)]
    2 Z1 X2 J1 W% ?" b/ {. E. R) K  r
    2 t5 n: D/ m. e5 [1 R  x+ `8 Q/ R0 N9 o4 F  y& D7 v/ }, w# [
    # bin传入分割点列表(使用`np.infty`可以表示无穷大):% H7 Y4 |# Z! l6 q( j% q
    pd.cut(s, bins=[-np.infty, 1.2, 1.8, 2.2, np.infty])
    / H; U5 c- [) E6 o( ?6 i+ FOut[41]: 6 N/ f, K, O" O  u& W# Z
    0    (-inf, 1.2]
    ' O. d9 ?0 W; r2 K4 U1     (1.8, 2.2]
    & d5 J8 [6 H1 ]) k- ?dtype: category
    2 y$ }1 R2 `& ?2 ^  Y# W( v8 ?  tCategories (4, interval[float64]): [(-inf, 1.2] < (1.2, 1.8] < (1.8, 2.2] < (2.2, inf]]
    ) w4 L: ]( x  f& u! t5 b: E% F0 y& X# Y9 V
    1/ X% z# f- t. g3 m1 A
    2
    ( X+ M5 ]8 Y( {- o% n- g3
    ! e7 d2 H- V- i# `) ]" K4
    $ f6 W- _" A0 T5  ~3 y$ @$ e1 U6 H( g# \4 R1 p0 Y
    62 a7 W) V! h' J; l' ^$ x
    7
    1 S" L( U  {: O+ `8
      m8 v! j" i% [; J" j: x9
    ) c3 Q5 _: S5 {4 w' h& U/ R' ]& E2 p- F, Z10" y2 r7 \; V5 L6 O% N
    118 G5 V' P! f0 q' i; m" q* [
    122 Q$ E4 L* p# ^  x
    13- p, V. R  y' U, y" q- Y
    14$ l) Y3 s7 U% N8 i2 }
    15# T: \6 N( u" ]' g1 K( ?+ s/ ~3 b$ H3 K# d" G
    16
      Y) t2 E+ N- n17
    : \5 D8 Z. f% s  c# i' P18
    - H" Z* I" \  t5 j! _8 t1 Y191 @6 J1 R" f0 @" ?$ f
    20
    6 b; }3 M) A, p& H; ?- {( B212 a3 R0 M# {* K+ Y7 n  ~- N4 H  n
    22
    $ T: N$ Z& G/ G  J1 B+ h23# Y7 w. k. ]& J5 D. P6 b
    24
    - @3 F1 }; X% o9 W  f25
    ' m" Y) a+ @+ F3 O9 }7 u2 qlabels:区间的名字
    ! |$ k; a; R3 U( H' _/ f3 aretbins:是否返回分割点(默认不返回)
    # F: k+ k' b' S. t, p' I9 |默认retbins=Flase时,返回每个元素所属区间的列表: U: J6 w  j  ]& F; _
    retbins=True时,返回的是元组,两个元素分别是元素所属区间和分割点。所属区间可再次用索引取值) L; O6 B& s, `' A, _

    + z; \( M- U2 u, ~4 u% V# qs = df.Weight% l* j/ R4 I8 I& E7 L9 x2 j
    res = pd.cut(s, bins=3, labels=['small', 'mid','big'],retbins=True)
    2 ~; o$ o. Z+ x) q7 tres[0][:2]
    3 n) h$ H  X  f' ]  D3 {# g) Q. ?- ~6 f$ _- d: ~
    Out[44]: 3 b3 j" n$ z/ R% o
    0    small' T- }$ S) L, }# k2 X2 [
    1      big
    ' K- _0 b/ g# @  x3 Hdtype: category
      B0 H; Y7 Z) v. q# HCategories (2, object): ['small' < 'big']
      H4 n  v* T  X; m: q" ]" C3 k/ y. E  Q9 |, U
    res[1] # 该元素为返回的分割点% R' j. S4 h- e3 H9 L' d, K; k
    Out[45]: array([0.999, 1.5  , 2.   ])  ^( v' \5 r/ p9 `
    1
    # E! q/ \+ i' v& X. s+ t" p8 |9 ~$ g2
    1 X/ d+ n! ~2 S0 R$ `3
    " [4 k& o& i6 t( @+ t45 g9 \7 s5 f7 F
    5
    4 v* T; K9 v2 c6
    % x" y* Q" Q6 U& u; y+ `% S( a, P" ?$ p72 C( z' }, n" s; b, a
    8
    . j* }: K( p0 q7 h9 c92 a0 ~3 W) ^3 ^+ o
    10% X) n2 o! l. i: H. N
    116 V; m& o0 S9 K7 x# m
    12
    - p0 O# h4 z0 d) Oqcut函数。其用法cut几乎没有差别,只是把bins参数变成q参数(quantile)。
    $ D/ n8 j% L( tq为整数n时,指按照n等分位数把数据分箱( T% g: a. V5 o( i
    q为浮点列表时,表示相应的分位数分割点。
    . X: C3 V: m- E4 Qs = df.Weight0 P* J: C4 J4 h
    / x6 p  j* _9 z2 Y( }7 D
    pd.qcut(s, q=3).head()9 B9 x/ @' O  P5 K7 ?) K5 L  N& c
    Out[47]:
    8 q- B- Y: W6 i4 U% [; M) I0    (33.999, 48.0]" y4 I+ R0 L# R9 E; p
    1      (55.0, 89.0]& L; c2 ?8 V" W, K1 y
    2      (55.0, 89.0]4 M, }; Y5 p! a) }, I) \, ?
    3    (33.999, 48.0]# T0 \) o! P9 K( w
    4      (55.0, 89.0]% E% {( X- ]' K; ]! Z8 |' u$ ?
    Name: Weight, dtype: category' i* F4 r. d' N  h
    Categories (3, interval[float64]): [(33.999, 48.0] < (48.0, 55.0] < (55.0, 89.0]]' n1 ~$ H- C6 L. M6 L: Z

    9 B4 Y* F6 G' P! r9 t4 z: N& zpd.qcut(s, q=[0,0.2,0.8,1]).head()
    * u, b8 {) C9 x5 ?Out[48]:
    / u; G' z" N, _# U' L4 ~, e1 d0      (44.0, 69.4]
    5 H/ H& b& b4 r& j. T( B& `1      (69.4, 89.0]
    # m+ X: P) Z' a) m; Q2      (69.4, 89.0]- z" C) D/ S/ W7 H+ r( J
    3    (33.999, 44.0]
    ( T$ ?. V$ @! J* n: l4      (69.4, 89.0]
    % ?8 K5 ?6 D1 v& S0 d- x% l( ZName: Weight, dtype: category
    7 z! R6 Y* P( J" NCategories (3, interval[float64]): [(33.999, 44.0] < (44.0, 69.4] < (69.4, 89.0]]" q; i" |# v3 q) j7 R* E- \) d
    0 Q0 U6 u, w$ M3 ~$ h( Q. Q$ s/ m) S7 p
    1+ e& W. W+ k, }7 I$ I
    2
    2 f" O% I8 t, R" k* F3
    3 K7 `0 e+ b8 P2 e: i4) `) o: X1 Q5 G* L2 h7 }4 X
    5
      D8 R" b  h) E' l6) I" a. Z( F0 Q; M2 S/ g, T  ^$ G6 {
    7
    % O& a" o  @' v& W! j7 p8/ t. Z. k' p  p4 @! ^; a
    95 S7 w2 L, U8 c3 b: E- o; T+ ]9 H
    10
    1 A2 M  p& O6 c! ]11
    . q4 n7 k: s, n8 k; a12# K' @, J9 B& d* _$ G
    131 [  Q( _$ G. k+ L
    14
    7 h1 i4 i9 J& O15+ v+ w2 i5 a8 W  l6 d. f
    161 i' M9 r" U5 X! E& N  V8 @) {$ T2 A
    17
    1 d- ^% d, e, A& Q18
    ( X( J& W; R, a! n19# A( \8 o/ G- i3 ^
    20
    ) |) m- b+ f/ f. ~/ L( B( l21
    : q- S. Z4 V3 p2 \% B9.3.2 一般区间的构造" b) W; A* F* ]4 ]) F/ i
      pandas的单个区间用Interval表示,对于某一个具体的区间而言,其具备三个要素,即左端点、右端点和端点的开闭状态。
    8 S' `5 d& x! P- M4 X, H
    . B2 M/ N! h8 l7 w7 L% y' t: ?开闭状态:包含四种,即right(左开右闭), left(左闭右开), both(两边都闭), neither(两边都开)。  M* s; ]8 d+ X8 A
    my_interval = pd.Interval(0, 1, 'right'): S& v; v' T# ?/ W  E- P
    0 Z* E/ C0 _& m, L0 ~& K$ t( |
    my_interval
    8 B+ }6 J( [' i% [Out[50]: Interval(0, 1, closed='right')% M2 r/ V# D; S2 U- J& w+ \% G
    1, {! \+ y" |' N: ^  M; U" b
    2
    , E8 R' e( R1 H+ k35 k* t/ |* o3 |+ b( |9 e
    4! x8 y% t  ^" Z3 ^! B) S
    区间属性:包含left,mid,right,length,closed,,分别表示左中右端点、长度和开闭状态。& V& i7 u0 q( M1 ~' [, e8 i- ]
    使用in可以判断元素是否属于区间/ Y* O3 u, C# K! Z0 K4 ~+ c
    用overlaps可以判断两个区间是否有交集:
    + W5 F( P( q3 d6 W# @* s$ n- i% q0.5 in my_interval) s' G2 ]# u& z- r

    . W, y7 f3 Q/ N* k5 ~: F# F4 Y$ JTrue$ C) @( M* q3 R% _3 k) c) N
    1# j6 {2 b! _  X* @% b
    2, Y- |' h* Q8 z% R
    38 q* O+ J% u) c: P3 k# O
    my_interval_2 = pd.Interval(0.5, 1.5, 'left'); {% |# ~: [( N# ^
    my_interval.overlaps(my_interval_2)% ]$ \7 [& A3 T" A

    ( L5 }3 O) H( m+ S' PTrue) D+ J2 I+ N2 A- H
    1: H- w" ]6 k) c! U+ A
    2& O& J+ L% ~! v: B) L1 p3 k: O
    3" n* t; K7 A# v, V2 Q5 K9 ?- w
    4
    $ o) k! h9 m& p5 |' ^  pd.IntervalIndex对象有四类方法生成,分别是from_breaks, from_arrays, from_tuples, interval_range,它们分别应用于不同的情况:' r0 u$ O8 f1 B5 h, E

    ) E  F% W, a) K7 c( h* V! Ifrom_breaks:类似于cut或qcut函数,只不过后两个是通过计算得到的分割点,而前者是直接传入自定义的分割点:; P+ N9 U. L# v$ }
    pd.IntervalIndex.from_breaks([1,3,6,10], closed='both')
    5 Q  h' }7 D1 l& ^) ^4 o0 f. X; s+ g) L: Z/ u
    IntervalIndex([[1, 3], [3, 6], [6, 10]],6 t5 I' N2 O0 z7 c9 l
                   closed='both',
    / D3 V; e) ^, X/ n) @# n8 ~               dtype='interval[int64]')
    4 r" N# V( O, |1 D; [5 _" Y1
    & O; z8 Z3 K4 d2: y* e6 {/ S* m; v1 ?% S! s
    3
    ! e' K' q! U/ T4 s9 T47 }  _" X" M8 t; Y$ P, f
    5
    7 y. k. }/ ~" O5 Lfrom_arrays:分别传入左端点和右端点的列表,适用于有交集并且知道起点和终点的情况:
    - ^5 D. h4 T7 }8 ^! M3 gpd.IntervalIndex.from_arrays(left = [1,3,6,10], right = [5,4,9,11], closed = 'neither')
    # I, K! @9 T) q3 \/ W+ r4 C* h9 j" H% o. c4 Q4 o  P
    IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],3 ]# K) f3 G* Y2 M, Q1 z( x
                      closed='neither',
      g- \3 V( w. m4 ~                  dtype='interval[int64]')6 w2 ?6 e2 ?, T( u! j
    1
    / g7 [( Z' v) d3 @4 \2
    ) f; y! L3 Z% w, F8 l+ I) _3: D2 G' G- R( Y/ @1 ~
    4
    ' v3 b2 ?# r3 R' J5 g. k5
    6 i4 s, e8 g" i+ |from_tuples:传入起点和终点元组构成的列表:
    3 ~# U- H9 ]% {" m8 J) x5 Ypd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)], closed='neither')# B0 c: g0 k8 t) P' W% `. H6 J
      }1 k8 w: {. ~+ B9 H
    IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],6 }3 e( g" z- G. i* A
                  closed='neither',# y) E- P5 A6 E  u$ B$ y- m
                  dtype='interval[int64]')
    " z# A7 `2 u8 @- i4 G1
    $ |/ [) S5 ^( Z2
    5 E" c5 J  P* @  H# y# P3
    2 T+ h4 m" D1 g$ ]4 v4 U& S4
    $ ?8 {: C" a( R5 ^4 O$ ]5
    3 M. h2 [7 l2 e" Sinterval_range:生成等差区间。其参数有四个:start, end, periods, freq。分别表示等差区间的起点、终点、区间个数和区间长度。其中三个量确定的情况下,剩下一个量就确定了,从而就能构造出相应的区间:
    2 d: T" V- j) O) m) }, Rpd.interval_range(start=1,end=5,periods=8) # 启起点终点和区间个数1 P4 N5 o, d" m* a
    Out[57]:
    3 D0 V  {- s; @IntervalIndex([(1.0, 1.5], (1.5, 2.0], (2.0, 2.5], (2.5, 3.0], (3.0, 3.5], (3.5, 4.0], (4.0, 4.5], (4.5, 5.0]],* H! E+ w9 P7 g' m; P! B5 |6 z
                  closed='right',
    3 @, c/ a8 z- \              dtype='interval[float64]')
    3 q, J% u6 D) B- U4 f( \
    5 F* ~* n" Y  E1 A- W( N8 N4 Epd.interval_range(end=5,periods=8,freq=0.5) # 启起点终点和区间长度
    ) Z) m* L0 c. o6 ~. @Out[58]:
    ' _) M, D' g0 @! SIntervalIndex([(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]],
    / z' ^; r- }6 _1 ?- s# a6 w              closed='right',) h- |1 A4 ^8 t* S) I3 n
                  dtype='interval[float64]')
    ' q" o0 C8 U# P  t  b1
    / j, y( v% `, v% \27 c- Y! ?& H8 d% {9 ^- L
    3+ h& n. X7 g+ ^% @+ v2 O3 T) y- e, c
    4
    7 _# X! t! S$ N5 U; J' I5
    % E8 {' h. P3 A- M6
    4 ]6 F- `# W( z( V% r- w( v73 Z  ^# N8 {2 j4 x: W9 r% [3 a
    8
    . I: J) A, b7 x% K9
    3 ]/ ~5 L5 I/ L* M100 h- j+ F) z+ C6 w( a) J, \0 s. F
    11
    9 C5 N0 h/ V1 n7 g6 c, q【练一练】6 e6 b: f# M& l% l0 w7 z
      无论是interval_range还是下一章时间序列中的date_range都是给定了等差序列中四要素中的三个,从而确定整个序列。请回顾等差数列中的首项、末项、项数和公差的联系,写出interval_range中四个参数之间的恒等关系。8 i3 m& F, [/ u; @) T3 M7 B8 B' R

    $ v. N$ [+ J! y  除此之外,如果直接使用pd.IntervalIndex([...], closed=...),把Interval类型的列表组成传入其中转为区间索引,那么所有的区间会被强制转为指定的closed类型,因为pd.IntervalIndex只允许存放同一种开闭区间的Interval对象。: B. B* R; z/ y! Q3 Z( g4 n

    5 w. z' K+ J, {! Z7 Kmy_interval
    - X% p! o& D- f5 f% dOut[59]: Interval(0, 1, closed='right')
      t! A% e, r. u/ E* t! H+ V+ D8 T0 N: d' g
    my_interval_2' P7 M& Z; j/ C4 @) T
    Out[60]: Interval(0.5, 1.5, closed='left')
    # q( U& N9 j$ P  ]4 ^7 M2 H! ]0 N% S  P6 g( y! ]. ]8 D- K
    pd.IntervalIndex([my_interval, my_interval_2], closed='left')
    ; S% i: ]& o" G* t% B7 gOut[61]:
    / R; a# H  L) n8 F+ rIntervalIndex([[0.0, 1.0), [0.5, 1.5)],2 n7 c1 T9 n  ^! p
                  closed='left',
    - B1 e% S; b  U: n- k              dtype='interval[float64]')
    * v3 V6 N, l# ~1
    0 u4 W; ~5 s, X7 G/ H2) ?$ j/ |/ k* S$ N: a6 v/ e4 h
    3
    3 z$ W3 y  V! K& J4' ~3 g) R" r3 l9 {& ~
    5" i4 U, T4 o' W# W
    6
    2 Q) d( b( @3 I  U+ V0 s/ F7
    0 h  z# t. q# L; K8, _6 V7 q, J/ D5 v- F) f# W: e
    9
    7 z- ]/ z' ?+ f9 P6 D3 d; A10
    9 G) C; Q& [# o11* o7 o3 L1 h) @( i' Y8 k) f
    9.3.3 区间的属性与方法
    % }# i/ }4 @7 c: `7 H  IntervalIndex上也定义了一些有用的属性和方法。同时,如果想要具体利用cut或者qcut的结果进行分析,那么需要先将其转为该种索引类型:% P: K. M5 W+ n# `* E
    2 V' H+ v# q6 p$ ^, }9 g. y$ W
    s=df.Weight+ P; l+ Z# V, q# l* D
    id_interval = pd.IntervalIndex(pd.cut(s, 3)) # 返回的是每个元素所属区间,用具体数值(x,y]表示, S1 l- V& ?7 K8 H* Q6 Y9 W% |8 z+ `5 K7 r
    id_interval[:3]8 j8 O1 b1 y8 [1 I6 E7 f' q1 l2 ?

    1 t4 w' ^8 n7 jIntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0]],
    & m% _+ \6 a# A* u1 c! m                 closed='right',
    ' Q0 u. [9 g& [: Z. f% ^7 E- O                 name='Weight',
    1 X3 l& M( P- c4 |0 g) D                 dtype='interval[float64]')
    ! v9 N  |5 p7 T( U  [. l+ `; G1
    ) a9 K* m( U! N2
    - k) R; _* K; Y/ ^  L, R3
    9 C& t- y+ R% E7 G7 \; J7 U4 e6 a4
    # L+ l5 ]  o, n5+ r' a/ q' Y% v" v' Y
    60 y7 o9 Q. |! L" M2 u8 c
    7
      R9 y8 e; W1 s89 F, B+ V: o8 `8 c  p4 j
    与单个Interval类型相似,IntervalIndex有若干常用属性:left, right, mid, length,分别表示左右端点、两 点均值和区间长度。: P8 K' I) U8 r) {3 Z! N  e4 j
    id_demo = id_interval[:5] # 选出前5个展示! a# H6 I& y  i, [

    5 J( h9 n1 b. D2 S$ O* @id_demo
    ! ~9 E8 Y" r$ V) I4 l& [Out[64]: ) O; t9 m) t+ b8 k6 E, I
    IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0], (33.945, 52.333], (70.667, 89.0]],
    1 \8 }3 ~% t  S5 ?0 u              closed='right',3 v9 ]5 t0 z% E1 }* g8 }
                  name='Weight',6 Z+ D9 S! ~5 J5 H9 F
                  dtype='interval[float64]')
    3 I0 x0 m; N. f: z: \0 ?5 K4 O0 ~1 S+ B( W5 M
    id_demo.left # 获取这五个区间的左端点
      C# l2 ]7 L& g( x9 ?, n, AOut[65]: Float64Index([33.945, 52.333, 70.667, 33.945, 70.667], dtype='float64')% ]! A0 T, }$ d9 c1 M7 n: l

    " x0 p( I$ b) ?$ |2 x5 vid_demo.right # 获取这五个区间的右端点- b/ M; G, H1 X" d  J1 W
    Out[66]: Float64Index([52.333, 70.667, 89.0, 52.333, 89.0], dtype='float64')7 ]1 _/ Z. w+ b( c$ l

    2 L& w; f% R6 O* cid_demo.mid
    / m  W  o0 w/ {# W8 H) X' FOut[67]: Float64Index([43.138999999999996, 61.5, 79.8335, 43.138999999999996, 79.8335], dtype='float64')
    ( C* G; l/ H0 g- G; h
    ; u# J8 s5 I& O) x3 x) Yid_demo.length' ~; B% W8 i) r% J
    Out[68]: " L* _: A2 v; _5 @
    Float64Index([18.387999999999998, 18.334000000000003, 18.333,
    5 K3 L0 L; _6 z2 `, @% q              18.387999999999998, 18.333],  ~" F, M7 n( q1 B
                 dtype='float64')
    ) j) o; k" p) P1 v: s' M2 x% j/ a: s3 A3 ~0 V0 S
    1
    ( r2 p- t8 c" Q" Z2
    . L  q: M3 a+ ]2 P4 [9 \3( w( a3 }5 K7 h& [' T! Z6 T# g! N
    4- [( ~# \, M# G' P
    5
    * n8 i" a# s$ d8 }# I; T$ Q' J6+ z$ D- N% x- C! L5 K
    7, a- p5 ?/ M  o+ d- m
    8+ }4 G* ^) H. u9 N2 ]$ n
    98 K! u- \2 i5 X0 q! l& t
    10* p' z2 S4 m4 J# n
    11
    % o$ r9 W" B3 s6 e12
    2 a" N( r* y5 P. b# {7 x13
    ) _' Y" U" p4 S) S3 S' b% C14
    ) w* [1 `. |2 `* @, p# _  L15
    ( J8 D3 q% [# g" i' ~16
    1 o4 Z# w% S8 w. q! N17
    % Z. U- ?" E2 Y2 e) o0 n184 H0 x2 w) B& A
    19  o+ K, f/ m3 r6 o+ B# z8 i- y: B
    20* t! t4 s1 z7 c$ ^$ n
    21
    * d' p- m6 O1 m7 m# C2 h3 h, o+ b5 y$ h227 y# u& K/ P1 ^* z4 c; D( Q& c
    230 X' s  h0 P2 ^& l- S; t8 R3 a4 T
    IntervalIndex还有两个常用方法:5 K# S. ]  [$ a5 g$ Q- l* s- e3 t% t' u, m
    contains:逐个判断每个区间是否包含某元素
    / S0 p$ ^. G' qoverlaps:是否和一个pd.Interval对象有交集。
    ! {) O$ o2 \: m3 L3 zid_demo.contains(50)& \: y3 O! b2 O- {! D6 M6 c) {( C
    Out[69]: array([ True, False, False,  True, False])
    & k9 x: C: @! |" T/ ~  V, ?0 i8 I0 Y( S( i& m
    id_demo.overlaps(pd.Interval(40,60))* o+ ^7 x3 ?* ~
    Out[70]: array([ True,  True, False,  True, False])
    0 Z& Q9 V4 I" i  D( S1
    7 g" `) e  {6 |4 _2% p$ F/ d5 i9 h9 M0 o
    32 ], n  p1 x- e2 A
    4# E+ ?# I5 a  M* k9 C: Q- n$ v2 k( O
    5
    * _2 o) _( z2 a3 d9 H  X9.4 练习
      N0 V- n4 g: y* Q2 V# S1 |0 t" @Ex1: 统计未出现的类别
    # |- i6 k9 d) r; {/ u- ^  在第五章中介绍了crosstab函数,在默认参数下它能够对两个列的组合出现的频数进行统计汇总:2 ]! R0 W# ^4 V& o. h- d
    % g1 v& |7 ], {' M8 K
    df = pd.DataFrame({'A':['a','b','c','a'], 'B':['cat','cat','dog','cat']})- H4 Z6 R! `# P- Y" a
    pd.crosstab(df.A, df.B)6 k# Z8 N1 }8 f' a! c1 d

    $ S6 H8 P- w# E% q3 t+ \5 {Out[72]:
    . U! F9 f1 J" F5 [B  cat  dog
    # t4 ~3 Q, u* n+ ^! \. LA         
    ( R$ {: k7 _7 ]! D0 a! c3 I' Pa    2    0
    : M9 l7 u$ j% J  t# N( k, ~5 ^b    1    0
    $ Z1 J& q% f" X  W$ C3 Sc    0    1
    ; e1 A/ Q' |+ |9 U0 F1
    1 q; n* O3 z) c5 o) p2( f* W, ~# Z* E& J- W6 i
    3, J7 `5 F0 r4 R+ q
    4
    4 v' C, U2 e. y$ m) \4 c5
    6 {, S# E4 s" t8 X( D8 _6
    ; L4 I2 r" D' m; P3 t( V2 N7) {5 ~6 U( ?2 K
    8+ s/ T* G" X! ?# {! i
    9
    5 e" r! V. o$ b; F% ]2 U  但事实上有些列存储的是分类变量,列中并不一定包含所有的类别,此时如果想要对这些未出现的类别在crosstab结果中也进行汇总,则可以指定dropna参数为False:
    - m6 M1 t) T" P: D: a! H
    7 y1 z& a( ?# k4 Udf.B = df.B.astype('category').cat.add_categories('sheep')2 J& N0 ~( Q8 s9 [
    pd.crosstab(df.A, df.B, dropna=False)
    $ r) B6 Y+ W& w" @  J
    7 F- `4 s/ |" i( p- a) TOut[74]: ' k+ K: y2 B* R( H) p
    B  cat  dog  sheep
    $ ^1 W$ q/ e3 zA                 1 ^& {' B) u$ u/ ?8 s
    a    2    0      0
    ' m9 |0 b( U9 a1 E; U% Q) bb    1    0      0
    9 F: {* M+ {& H* t, gc    0    1      0
    ; N) C/ m3 j, P1
    7 A3 c$ A' ~+ X+ g! O2 G& y3 b# ~2
    4 g: o2 f: a) v8 R" c' K/ }7 J  |$ O3; L% {" R/ V( q. u1 U; W
    4
    : Z: t9 e6 k& R# ?% @& H2 o) A3 K6 h( W51 A: C7 {- D3 M% k
    6
    1 w8 |0 b0 |0 b7 }- I4 J6 F  e9 L7- N- R2 d: C. [5 `
    8% Y+ w) U7 {+ i: _4 P4 `0 j6 U
    9" U( [4 n8 A6 c3 y: \5 m
    请实现一个带有dropna参数的my_crosstab函数来完成上面的功能。
    / `, g, Y! t# U7 n3 _% ^! V" l( x/ Y$ z" n9 f3 f" ^; S
    Ex2: 钻石数据集
    : P/ T" S9 ]* R) j8 z3 C  现有一份关于钻石的数据集,其中carat, cut, clarity, price分别表示克拉重量、切割质量、纯净度和价格,样例如下:* `  d( _4 @( h& Y0 d2 L
    / s( U5 d3 V4 S: B
    df = pd.read_csv('../data/diamonds.csv')
    " w, b+ A/ s( h' k& Zdf.head(3)* i3 b6 j1 [- y1 ?  V
    , `8 L8 ~: q& E# R  s! l
    Out[76]: 0 @8 G3 ~- ~# v+ P/ U! ~# d/ U
       carat      cut    clarity  price
    % a7 s6 q6 y% ?' c5 z* E0   0.23     Ideal     SI2     326- n+ |8 }$ G5 e( \3 V# x+ R
    1   0.21    Premium    SI1     326# P7 n" O0 U! o" U5 z+ d$ D
    2   0.23     Good      VS1     327
    3 M7 n4 Z2 x8 _1
    ; j' \  }# F5 A& m2 v' q2
    1 I- X* u" N( ?% k; F( G1 M- v8 ?3
    & c- m$ K7 l: A+ r2 P- v+ g, D4+ K: M4 q# V% P0 d0 K7 p
    50 Q5 \/ v' m0 r( I6 |2 g  t6 W, W
    6
    # E. d1 S( l8 D. m2 p8 b5 U5 m7( r% S  D: z7 y$ d/ M5 I
    8
    3 t# Q  l, ?3 Z7 {) ?( @分别对df.cut在object类型和category类型下使用nunique函数,并比较它们的性能。% Z$ }( \; h- V  w5 `
    钻石的切割质量可以分为五个等级,由次到好分别是Fair, Good, Very Good, Premium, Ideal,纯净度有八个等级,由次到好分别是I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF,请对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。
    $ j+ d0 z6 m6 \& r4 F% i6 F, I分别采用两种不同的方法,把cut, clarity这两列按照由好到次的顺序,映射到从0到n-1的整数,其中n表示类别的个数。
    $ B6 R% {& ~1 t, _/ {. u对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。5 o0 Y& a. }' }6 T9 D4 L
    第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
    : U: C$ y, \  N* B! s对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。
    5 t# |# y# W6 K; Z3 L先看看数据结构:
    $ W4 L$ P; M9 e6 ~4 i
    / {/ w  @; z3 s0 e! x: Q* t% Ldf.info()2 l* O$ ~2 n, f8 W
    Data columns (total 4 columns):8 t, ?! ?" q) \
    #   Column   Non-Null Count  Dtype  
    : t- ?1 u3 F" D---  ------   --------------  -----  4 A) q, g  A6 l6 O0 W
    0   carat    53940 non-null  float64
    6 ^$ H* r, i% Q& ^ 1   cut      53940 non-null  object ! `) E  U2 s; ^9 B& ]  x* L
    2   clarity  53940 non-null  object   c/ m+ Z: S  ~/ B
    3   price    53940 non-null  int64  * B9 n1 s# W0 l7 w; e4 I. y
    dtypes: float64(1), int64(1), object(2)" T0 J+ o; A! c' D# p0 j
    1/ Z5 S4 \) e3 ~& p3 _% K
    28 v, R% R8 j7 }, k% A- C( ?
    3
    + E1 o6 Q1 c5 T% q' P  p# w4. f& K$ z, Z3 j0 C, K0 `2 q
    5/ [; n$ S: n! G( N
    6
    * {5 o" ^! G" h9 n4 t  l( W3 P74 |! h" |$ k" V3 h# M
    8
    ! I. ?/ s# ~& a; C% y9
      h  P8 d. g1 l# `: R) }/ Y比较两种操作的性能/ I0 o; n, _; v7 O4 r8 n
    %time df.cut.unique()2 Q" t& z" H( R

    ! m; `  z) @1 c* F5 F6 n8 CWall time: 5.98 ms! [+ m5 O! s1 Q# Q' e* ?* A( O
    array(['Ideal', 'Premium', 'Good', 'Very Good', 'Fair'], dtype=object)
    1 l8 V1 q/ J/ ]- s1
    , J2 b2 f* x" D- G, H  v2* \' E$ J# y6 g* S! A/ Q
    3
    ! |* n# W  l1 J" G! u* @' E4
    # r- I% I' a3 P. t. P" M# B& o) J* J%time df.cut.astype('category').unique()& v$ ~6 }- D! q' R0 b
    # k, [* u* |! Y6 ^/ o
    Wall time: 8.01 ms  # 转换类型加统计类别,一共8ms8 N9 Z& O8 ~+ u' a* |2 Q
    ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    , J, ?/ q- B, d' UCategories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    1 t. s) f/ P- b3 U1
    & R+ Q' s' t/ ]7 \5 k25 A2 ?0 G  I- Z9 q9 ~# t
    3
    # g# }; C8 t1 X# U+ k4
      C6 l; _1 I1 N( n+ Z) ]1 D5
    - P9 e! J" y4 ]7 Z0 xdf.cut=df.cut.astype('category'). J) s( m4 Q  [6 c
    %time df.cut.unique() # 类别属性统计,2ms$ X  O+ w' u: S2 e
    8 d0 Z: h: c- |* `9 r
    Wall time: 2 ms. Z3 }* U2 A& Q3 k# m& ^
    ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']" R2 y) j+ p) H: H
    Categories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    + U. H- x) i0 T3 \) H* W: F7 a8 L) V- w. Q1
    0 k' ?( E. H0 [1 b9 h20 o$ F/ F  p1 \! q9 e
    3
    4 P# P. U2 T' a4" M; G% }8 M: b
    5
    2 `8 z4 [% ]' I3 `( [5 y6
    # l1 I* n: w1 _; V7 w2 n$ I对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。
    4 K6 b( V7 V, w$ l) Ils_cut=['Fair', 'Good', 'Very Good', 'Premium', 'Ideal']; k& C% X0 X: Y: }9 ]* {2 O
    ls_clarity=['I1','SI2', 'SI1', 'VS2', 'VS1', 'VVS2', 'VVS1', 'IF']; o! \3 l9 y/ i' B
    df.cut=df.cut.astype('category').cat.reorder_categories(ls_cut,ordered=True)  # 转换后还是得进行替换0 s9 r* D! f2 s4 n! C
    df.clarity=df.clarity.astype('category').cat.reorder_categories(ls_clarity,ordered=True)
    ( U  T3 n" Y7 K, i0 M9 o. }5 b2 t; W7 O4 H$ {/ T
    df.sort_values(['cut','clarity'],ascending=[False,True]).head(3)
    6 Y1 r4 i( W! s, R9 k* i- E* y6 ?" s( \8 z2 [) v( M$ E" b# W2 E1 o. s
            carat         cut        clarity        price7 b1 s) k; `! ]
    315        0.96        Ideal          I1        2801
      k$ v, u9 c% M9 R+ N" _3 |& R535        0.96        Ideal          I1        2826
    # ?- l5 \  y- \- r551        0.97        Ideal          I1        2830
    6 l9 f) H- ]- e7 w/ M. A1! H! _9 ?; w. I1 A
    2
    % ~" R+ [  w8 H7 q36 i# H+ E) u. A( |, x7 R" S7 t
    4
    5 R( H+ L1 \0 j  u- s6 C. W54 U% r9 v* B( K* s6 o2 \1 T
    69 e% D7 n4 |; k1 v) g& T! b
    74 w4 e+ N1 I' Q$ u
    8
    + R- x7 Z3 v  h8 m2 S# ]9
    ' Z7 s. g. Z* M% ?10/ R, o8 e' A' |: W- W
    11
    ' m. x/ A: u; M" y! X分别采用两种不同的方法,把 cut, clarity 这两列按照 由好到次 的顺序,映射到从0到n-1的整数,其中n表示类别的个数。2 v, h( p; N' \4 J
    # 第一种是将类别重命名为整数1 q# c# r, ^- }9 z3 T' V3 E
    dict1=dict(zip(ls_cut,[x for x in range (4,-1,-1)]))
    6 _0 ~- D0 x9 V: y& @dict2=dict(zip(ls_clarity,[x for x in range (7,-1,-1)]))& C9 h* s* F, O# F) ]
    2 _7 A7 r+ {. T6 ^
    df.cut=df.cut.cat.rename_categories(dict1)
    * a5 q9 K7 d! i% }4 m. edf.clarity=df.clarity.cat.rename_categories(dict2)$ C' w. C5 {4 c- t7 v+ X2 t  M8 b
    df.head(3)
    ) S$ S  `0 S7 H  [/ v( w4 A$ c- d
    " o) c0 \6 u) U% d' J: X        carat        cut        clarity        price6 T- t! f% v! W; w' h4 M6 J+ A: i. c0 J
    0        0.23        0          6                326& L* Z+ v" t% [! p' B; C" l3 e
    1        0.21        1          5                326& b% R( k( v7 \) O1 J( I- f
    2        0.23        3          3                327
    ) O7 b9 L( Y2 {8 N% W1# f% F5 t2 n6 |) h( J9 D/ f
    2
    / z# f* a+ a; d" U! U- j& h3
    2 J1 Y' Z! h: p( Y41 }0 Z2 @$ h# Z" I
    5
    . ?9 F% j! S6 J* o& Q- I  }0 x67 G, a! V+ g0 d+ t
    7
    ; A( a. r- n$ d' A3 [! m2 z/ R% {6 t8
    ( J2 q0 T. j& R+ Z& O) F91 N: |+ A) Q+ D7 B
    10! m, l) U2 O1 H! N2 T# l, [0 k/ q+ j
    11
    2 x) I" G* |  s* }5 Y. Q12
    3 t8 W1 i3 J! F* N$ F3 o) a# 第二种应该是报错object属性,然后直接进行替换
    & t7 N1 }1 `2 T$ A% U2 R+ G: Udf = pd.read_csv('data/diamonds.csv')
    * E% K0 e  X/ Q% n! c* ]for i,j in enumerate(ls_cut[::-1]):$ o( d) [& P% x$ l8 S8 j% f
        df.loc[df.cut==j,'cut']=i
    0 ~* `6 k; u. }5 ]6 p0 z5 A0 N1 G
    . V2 S4 S% t% ?3 |for k,l in enumerate(ls_clarity[::-1]):  X' @* b0 |8 I" h  V
        df.loc[df.clarity==l,'clarity']=k
    * ?4 ]! r! `1 S& W5 Z: s3 d! I  Ldf.head(3)6 q" p7 B  N. M# D. g# C
    $ [2 ?1 y& D: L
            carat        cut        clarity        price/ E# z8 i2 J6 o4 U8 s8 H3 O
    0        0.23        0          6                326
    ( v. ^2 p, h# V% E3 s" x1        0.21        1          5                326
    8 c# D# t$ ?/ d: U* o2        0.23        3          3                327
    - `# a. ~/ \- F" y4 }1  X$ A. N1 @1 s& t1 E
    2
    . _/ M6 Q: {& V3" [! d: c1 y: ^3 ?5 O/ ^2 t
    4
    # l# l( |: K5 ]6 b4 w  j: W. u& `5' s3 x, H1 }# K
    66 s6 d( x7 I$ M: @+ g  ~
    7
    : x) ~  k0 G1 Y  F+ z0 _. V6 o* o. k8
    3 P" w9 n  \7 ^. I( v9 x5 q$ S8 w9
    , ~% O( m' u3 S* i3 g10
    $ `5 V3 H  f! L! G11
    % ^' X. s4 F% d1 b; f12
    6 q2 g! K1 J$ p7 s/ X$ P# ]5 o13
    ) |/ ?) Y3 L. d5 T* q8 G对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。
    * h2 o/ i9 f/ Y; C! _# retbins=True返回的是元组,第一个才是要的序列,第二个元素是分割点
    9 L/ @; c3 M) ?& A- e5 x5 [avg=df.price/df.carat
    9 r) c3 e2 f- @' N% [7 {; P5 @% p5 U
    df['price_quantile']=pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],
    0 ^9 y$ N4 G0 k0 `                              labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]
    $ Z: @% f+ G- d+ t6 ?+ w/ c1 a$ {( ?' e8 r7 S! @
    df['price_list']=pd.cut(avg, bins=[-np.infty,1000, 3500, 5500, 18000,np.infty],
    * A* m1 _) v. {: W$ J6 F9 l                              labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]( O. ^6 Q7 n9 c. G' L4 l
    df.head()# ?7 t  G0 o1 D& l6 J
    ; f. }8 h8 q8 m3 _# _
            carat        cut         clarity        price        price_quantile        price_list" w, J# j& k! V( f+ y/ {# s
    0        0.23        0                6                326                        Very Low                Low
    " Z! [3 Q8 B2 ?: c+ L1        0.21        1                5                326                        Very Low                Low- U4 d9 ^1 e$ O3 Z  X. v) d; h
    2        0.23        3                3                327                        Very Low                Low
    1 Y2 C. d5 ?, m$ Y' n. [: ^( k3        0.29        1                4                334                        Very Low                Low6 S5 s( j6 ~/ n5 E
    4        0.31        3                6                335                        Very Low                Low                                       
    ) U6 z; w$ G5 I" d
    1 i' Y+ X* w: Q3 {2 F1& t- q' L2 l, f7 }" L# ?  f
    2& H8 m2 ^, P! {! I& m
    3' v9 j$ X! N4 ]6 j( J/ W
    4
    8 ~, N* M+ j: {: s! O/ ~% x5* ^6 x, f. D0 E4 ^. W2 o9 p6 I$ v
    6
    : Y7 y# ~# b* ], K" a5 C" R* m. M7
      k" k; m( a* y1 \- n% ]; X" c+ a8( n& ~% e8 U5 O& F/ m& J8 ?# D
    9
    7 Q/ w3 d, n3 C; H6 y+ ~# Z* b, |" y# p10
    % o: [% |, B: C7 t. ~  a11
    # ^! }6 N: h# w" }6 F' p12
    8 R2 M8 O+ L- n& X1 x13- R; I8 F2 A0 C6 @6 k8 a
    14% l- C9 V1 _7 n  o0 S& Y1 v3 J
    15
    " T- K% N; F* D, R2 s163 n* v7 b6 x0 ^9 X4 s
    分割点分别是:, X. _6 V. J$ B+ ~' T+ t+ I
    + R" V- t0 z1 E; I- g) q7 R. R
    array([ 1051.16 , 2295. ,  3073.29,  4031.68, 5456.34, 17828.84])
    $ Z- l& M" J" z) b- Y9 \6 Harray([  -inf,   1000.,    3500.,    5500.,   18000.,    inf])
    4 J. m& ^! y; g. y3 V1# f- u1 W/ [3 e0 _; y7 W! v: E
    24 ]* y. r$ I& y* S7 [8 m: U
    第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
    3 Y. Y1 w5 _! ^8 H6 E, V/ kdf['price_list'].cat.categories # 原先设定的类别数% I6 h: S0 T/ H
    Index(['Very Low', 'Low', 'Mid', 'High', 'Very High'], dtype='object')1 y  _2 Y: h1 H4 e: h: l1 p9 J
    4 j- b$ ~) ]8 }$ D0 Y* y& I& i
    df['price_list'].cat.remove_unused_categories().cat.categories  # 移除未出现的类别* E0 T- R- P: E: m
    Index(['Low', 'Mid', 'High'], dtype='object')  # 首尾两个类别未出现  I: t. ^" i7 Q4 D
    1
    / A- P4 w9 @6 \# C22 o! H( l) F: p3 |$ X
    37 H4 K& i: {" ]" B  W  N; p
    4: l) \( ?2 B- q& t' w7 Y5 W
    57 b" ~# N8 c& P- V1 M
    avg.sort_values() # 可见首尾区间确实是没有的
    8 S! A3 H' `! V* |% Z31962     1051.162791
    1 P7 b7 h. n7 A" x/ Y. Y1 ]15        1078.125000
    9 v# j. p8 p) a, H7 {7 K4         1080.6451612 U1 l; Q" i! v/ I
    28285     1109.0909091 l: ^* o1 J: S& k+ [+ i
    13        1109.677419* c- `8 T* T* X) g+ C: g
                 ...     ; F5 E/ e# Z/ p/ d; {. i: T" A
    26998    16764.705882
    ( K$ z5 R/ x$ m7 |- w, o. Y! ~27457    16928.9719636 u$ U5 _! u3 p+ P( U
    27226    17077.669903$ W, d/ F8 j* T; C0 F) v
    27530    17083.177570
    4 M1 _+ o+ ]) v5 E1 b27635    17828.846154
    2 b. \# d' a8 C6 K. U- q5 D6 U2 d1# O9 V  R9 ]6 c/ _# D% [
    21 D6 ?* F% S9 i9 I0 l) ~; \
    38 H: \$ ]' N* o- O; Y
    4
    - ?6 a" s- U# u) }3 a8 I2 \5+ y  L" F6 k2 _5 P2 u0 j; B
    6' K5 A* `6 l2 C# x: Y6 V' R+ g+ E0 w4 f
    7- j1 b1 c+ i% c% j
    8: m% v# [$ g2 A9 D
    9
    9 Y; `, L3 V+ e# B10
    3 _) v: M' @0 ]8 ~& V4 ~+ ]113 x" c! H% V/ t/ T
    12
    & b  V# Y2 v' A$ x: a5 |# d对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。
    8 ~( G8 C+ ^: e# 分割时区间不能有命名,否则字符串传入错误。7 S$ C7 R) H5 t, w# x! J+ [
    id_interval=pd.IntervalIndex(
    . h% k0 _& N9 o' @' g    pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],retbins=True)[0]
    3 ~/ Y8 Y8 T$ d" m                            )
    ( K0 T% \' g; ~id_interval.left1 a2 G2 `  N1 E, U" N: M
    id_interval.right
      v! }( L& ~, _' Pid_interval.length                            6 I9 f2 J5 m3 h6 ^4 T
    1
    9 w, k7 N, e2 s# {2( m4 w0 \' ^+ `# e
    3
    1 M  S" z: R# G& {0 P5 q1 W6 m4& H& B4 t! I6 e
    5
    $ ~! _/ o4 E* M7 d  Y/ I. m/ Q6
    1 [( c5 e( d4 R78 f' R+ @' b+ u* _7 _& O* [
    第十章 时序数据7 L& |2 U! T, ^% B2 v; q
    import numpy as np
    , I. W2 T, X. `5 H8 z- f- N# eimport pandas as pd
    9 a1 ?1 \6 i, r. c0 s1
    $ V1 d& u# D( J2 R! U# R  N9 k' |% `2# o& a- H1 |8 W. C" d0 m

    5 p$ T2 d  N7 @, D$ a2 Z- S+ J
    ) Y  T% G* I5 s+ i10.1 时序中的基本对象! w$ I% U% p- |) X  o
      时间序列的概念在日常生活中十分常见,但对于一个具体的时序事件而言,可以从多个时间对象的角度来描述。例如2020年9月7日周一早上8点整需要到教室上课,这个课会在当天早上10点结束,其中包含了哪些时间概念?
    / g3 E$ u8 ]8 o9 m+ C; a# B- l* [0 l
    会出现时间戳(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的简写。
    % w; w; U/ m4 S5 w$ g; T" h9 w* t
    ' ]* d' L% X( S$ |, [) k1 [) r! R会出现时间差(Time deltas)的概念,即上课需要的时间,两个Timestamp做差就得到了时间差,pandas中利用Timedelta来表示。类似的,一系列的时间差就组成了TimedeltaIndex, 而将它放到Series中后,Series的类型就变为了timedelta64[ns]。
    ' v3 m5 j+ t. w: h& _% K" N: ], k8 U: B* N: Y
    会出现时间段(Time spans)的概念,即在8点到10点这个区间都会持续地在上课,在pandas利用Period来表示。类似的,一系列的时间段就组成了PeriodIndex, 而将它放到Series中后,Series的类型就变为了Period。
    3 p9 F! H7 @6 y8 B/ m1 O" Z5 S; P" C3 @- D5 d( M
    会出现日期偏置(Date offsets)的概念,假设你只知道9月的第一个周一早上8点要去上课,但不知道具体的日期,那么就需要一个类型来处理此类需求。再例如,想要知道2020年9月7日后的第30个工作日是哪一天,那么时间差就解决不了你的问题,从而pandas中的DateOffset就出现了。同时,pandas中没有为一列时间偏置专门设计存储类型,理由也很简单,因为需求比较奇怪,一般来说我们只需要对一批时间特征做一个统一的特殊日期偏置。
    6 Q, y5 S4 y0 ~. P" J5 ?5 L) n0 a
    : {8 r9 y4 m* W7 {  通过这个简单的例子,就能够容易地总结出官方文档中的这个表格:# j0 \, z5 G, }. Q  u: U
    2 z: a, S4 a: b9 d; k  M) ~
    概念        单元素类型        数组类型        pandas数据类型
    % w$ c, K' e0 g$ |) C4 [" a" ]% W$ D) eDate times        Timestamp        DatetimeIndex        datetime64[ns]
    7 K- S/ B# v% x9 yTime deltas        Timedelta        TimedeltaIndex        timedelta64[ns]
    / `' o' \( o3 m- U8 \2 H3 YTime spans        Period        PeriodIndex        period[freq]5 L8 |0 V/ ], o0 D
    Date offsets        DateOffset        None        None8 ~- l: g: h' c& s# p1 H# {% V
      由于时间段对象Period/PeriodIndex的使用频率并不高,因此将不进行讲解,而只涉及时间戳序列、时间差序列和日期偏置的相关内容。
    $ v% ^: G$ _% @: v; r2 Q: f% i& B& J# d' s; B! U
    10.2 时间戳
    ( B- J1 o4 U( E& B; g, m10.2.1 Timestamp的构造与属性
    . }: i1 \+ t7 E! H% d单个时间戳的生成利用pd.Timestamp实现,一般而言的常见日期格式都能被成功地转换:
    & S. ^$ Q7 x3 m! h' n& V0 x; Y) [
    ts = pd.Timestamp('2020/1/1'); z2 u* O; K3 k9 p  V
    # m0 ]: `6 B- G  H. d
    ts+ ]# r8 L* R: A( \$ U% q/ n
    Out[4]: Timestamp('2020-01-01 00:00:00')' Y+ V' e$ q' |  L2 Z* B0 o1 t

    0 Y9 G8 e, q% z1 V6 v1 J% Jts = pd.Timestamp('2020-1-1 08:10:30')
    # e8 U9 C6 ^3 u1 G/ t8 |+ |# L; f
    ts# m1 f# j/ V. P$ _  N: n) C
    Out[6]: Timestamp('2020-01-01 08:10:30')
    2 a1 S  e: G3 d$ l1; a% m* v  w+ k- B# U
    2- N) C# A5 P" |% J  n/ `
    3
    8 f! r3 L! J" y1 W& o4. o" `* [4 E4 H0 }
    52 U& ~7 o+ n7 }( \: d0 j
    61 {, V# \; C4 }5 n8 v6 z& a: ]
    73 x4 v* L, f: s/ R; [
    8
    9 `2 _$ c- o; p2 W/ p" ]93 K( F, @- @8 L1 C! J- U
    通过year, month, day, hour, min, second可以获取具体的数值:
    * L! y) z- Y% P$ @" q
    ) o2 W2 m$ O1 M9 N6 gts.year) ]) o# N: h7 v2 w" x4 D1 x5 I9 [
    Out[7]: 2020- o; [0 [+ I4 g7 m+ a' o/ ]
    & S3 M; {, w* W% s
    ts.month- P- `" k9 J% f$ |( P* H0 n8 r
    Out[8]: 19 f: u8 ^( c+ F$ F6 z8 @) T; v3 h  W

    ) h7 I1 }$ C1 a9 Z2 ]4 \ts.day+ V7 ?- C1 a: [! s
    Out[9]: 18 B0 A  g$ |4 p" ]) l$ H# s
    / C) `; i9 t0 _: N2 N) ^
    ts.hour) X7 o& O+ L% a+ v. T1 W
    Out[10]: 8+ k$ s1 ]+ \# B% \/ Z+ y+ h! N( J

    0 @  r) X% U$ I/ \4 p+ |ts.minute5 T& s0 F. t, H5 j. c
    Out[11]: 10
    & ^4 h( ^0 M, d, i+ n2 Q
    + M5 G) \" R& \0 s  its.second
    ! P0 X8 |* B9 i2 z: ]6 xOut[12]: 30
    ! L6 Q  I, |, S
    9 @5 u! x' R( ^# D- ~% Z$ w1/ [- s( V' m' k% O, P
    2
    ; j8 V! z( ]! Z, y, T4 R9 {: c5 y3/ S  V/ u* G4 u& |( r4 {$ K
    4
    3 ^+ O, Y1 f- u5
    - S# f/ [  f. t$ j6 L63 S9 [) L1 U) d. \  M0 o" R
    7' y' b8 X% i; c9 \
    88 Y7 A2 q+ R/ _5 {
    9
    $ e4 e2 [- `& y# ?8 m10  d, H) @+ [" l7 u0 E
    11. O/ x; B* E( u/ B$ T  c+ a
    12
    2 r% y0 [# L. ?* A  q3 R4 A13( v' K% S+ z$ D3 b6 B( _* a
    14
    1 r- m3 `9 j$ T$ V15
    ; ?1 F# `+ f3 B8 n- w) C16( h* d. ~  F7 Q$ |8 W9 K" ~8 ^: N$ t
    17# b" T" |- c- i
    # 获取当前时间
    6 c3 Q, m4 K8 d' U% qnow=pd.Timestamp.now()
    4 r" ?$ @0 |* _( C$ H4 B1  T! V/ Y) c" k6 W6 K+ @2 y/ j5 ^
    23 O' ^" A) \- ~3 ?( m; q
    在pandas中,时间戳的最小精度为纳秒ns,由于使用了64位存储,可以表示的时间范围大约可以如下计算:
    + A$ v( {3 N& s9 N* o5 ?8 r, K' wT i m e   R a n g e = 2 64 1 0 9 × 60 × 60 × 24 × 365 ≈ 585 ( Y e a r s ) \rm Time\,Range = \frac{2^{64}}{10^9\times 60\times 60\times 24\times 365} \approx 585 (Years)% b* o) k' j/ v, F
    TimeRange= ) F3 l$ b9 E  G  i: A( o
    10 ' ]* Z) F4 S7 b# \
    9
    + c) l8 r. e8 e; C/ @* i! |, ? ×60×60×24×365
    8 q1 F* U( T# z5 ?- g9 |: r2 2 e) r" D+ U0 x3 r1 z
    64
    : P4 k4 I/ O1 R0 K
      x: f) _6 U, Q+ n; R
    ' T5 [/ l  V* u7 q, a ≈585(Years)
    4 v, _2 z- d" h, F( ?2 {! h# t9 _, [" n+ F9 F1 w5 R( H
    通过pd.Timestamp.max和pd.Timestamp.min可以获取时间戳表示的范围,可以看到确实表示的区间年数大小正如上述计算结果:
    ' l1 P! C# ?! ]8 s5 a/ K
      v+ h5 o' d- T9 Epd.Timestamp.max
    2 _4 N! |; v4 a: c  @/ dOut[13]: Timestamp('2262-04-11 23:47:16.854775807')
    $ J) j" O$ N8 v! Q9 Z& N6 p
    - b/ Y2 l6 J. v! X' z/ B: Y7 d' {pd.Timestamp.min
    . `" ^) V* a: f% e% ROut[14]: Timestamp('1677-09-21 00:12:43.145225')
    4 n" c/ m& o) n- L6 g$ T8 B
    & f1 h3 l. \6 N2 S, h/ Epd.Timestamp.max.year - pd.Timestamp.min.year3 p" H% S0 N5 J
    Out[15]: 585
    ' K; @# f6 v; \: h6 T# C1
    , J$ M  H/ C) [% ~2
    6 H# ?. \0 F8 Y+ ~3 M# C1 l3
    " T0 Q3 \1 s# K! A  E% H4% F8 q  X6 Z' u- d; P  @
    5
    ! K; y6 o: R5 `* c; n* f6) B  B: S0 s1 b% a) P$ `5 E; X
    7
    / y& S; W( v+ E% |2 g4 H8
    + h  Q3 ]( j. g10.2.2 Datetime序列的生成
    $ l; {& p" _) w/ a7 {pandas.to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, utc=None, format=None,
    , k  u: V! U! g3 T                                  exact=True, unit=None, infer_datetime_format=False, origin='unix', cache=True)
    " d" D9 \' h$ B' c4 B) _1
    4 k, R' }! j$ w9 [& V8 D1 V; n2 M* H2
    - t* o) [# R$ u" r' Q' Tpandas.to_datetime将arg转换为日期时间。$ F/ C( ~5 `/ `3 i

    1 W( H% f' z; A8 U4 j+ Z: E# uarg:可以是argint、float、str、datetime、list、tuple、一维数组、Series、DataFrame/dict-like等要转换为日期时间的对象。如果提供了 DataFrame,则该方法至少需要以下列:“年”、“月”、“日”。
    , g+ N( U9 f: P6 ?2 a- X/ derrors:
    1 I- Q' x7 Q8 R  g7 }" Z% ?, j9 @- ‘raise’:默认值,无效解析将引发异常+ h$ ^$ p. j) u# |& P
    - ‘raise’:无效解析将返回输入1 [* a, b" s2 P. J9 ^% @
    - ‘coerce’:无效解析将被设置为NaT6 Z1 Y$ z' y# X; B8 o7 U. [
    dayfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析日期,例如“10/11/12”被解析为 2012-11-10。如果无法根据给定的 dayfirst 选项解析分隔日期字符串,会显示警告。
    : s2 d) A5 Q" Q: R5 P& ~0 jyearfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析年份,例如“10/11/12”被解析为2010-11-12。无法正确解析时会显示警告。(如果 dayfirst 和 yearfirst 都为 True,则 yearfirst 优先(与 dateutil 相同)。)! L- c" \. e" H. B1 y. i
    utcbool:默认None,控制时区相关的解析、本地化和转换。请参阅:pandas 有关时区转换和本地化的一般文档
    ' t# Y( e" N  w$ `/ t) e+ gformat:str格式,默认None。时间戳的格式不满足转换时,可以强制使用format进行匹配。
    + w/ P1 \' h1 ~' _7 u1 b& q& hunitstr:默认“ns”。它是arg (D,s,ms,us,ns) 的表示单位,可以是整数或浮点数。这将基于原点。例如,使用 unit=‘ms’ 和 origin=‘unix’ (默认值),这将计算到 unix 开始的毫秒数。
    5 I# v6 `' m/ m9 `: I: g, O6 Bto_datetime能够把一列时间戳格式的对象转换成为datetime64[ns]类型的时间序列:
    9 r' x! p  W6 ~* d& l5 xpd.to_datetime(['2020-1-1', '2020-1-3', '2020-1-6'])
    9 j& S4 Y% D, h
    ) @( v: W, f( L  C! NDatetimeIndex(['2020-01-01', '2020-01-03', '2020-01-06'], dtype='datetime64[ns]', freq=None): W/ ]" n8 n) f4 E5 P
    1
    . f# s/ o' b7 ^! n6 k' Y3 H2# ]7 W& Z/ r6 K: J$ S6 r2 M
    3' r4 I1 n6 j8 F! @8 `( z
    在极少数情况,时间戳的格式不满足转换时,可以强制使用format进行匹配:- r) ], ^7 O. }" K

    5 d7 L  `4 {( g9 I/ Y) V2 `3 ?temp = pd.to_datetime(['2020\\1\\1','2020\\1\\3'],format='%Y\\%m\\%d')
    % J/ y+ {+ S0 P( C7 F, C% t% Ztemp
    ' A9 ^* i" B1 s$ a2 [  Y$ B
    3 `3 _8 q1 H4 C: FDatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)
    8 u4 u: `2 x1 s# ~- Y) N( P1
    ; ]1 q9 V3 E7 z, W$ r4 ~3 [2
    $ d5 V9 `- J, y4 r. d* `: m( y3
    $ j! [3 b7 u0 A4
    . u6 m' J4 _: U* t3 \" ~$ z. b  注意上面由于传入的是列表,而非pandas内部的Series,因此返回的是DatetimeIndex,如果想要转为datetime64[ns]的序列,需要显式用Series转化:& R( q7 E2 {5 ^0 x* Y3 |/ ^/ e  ]; x

    4 q" e0 ~! e: [pd.Series(temp).head()& Q  Z0 s$ |2 s. b! |

    9 e  C3 a# \3 b; D2 x& Q0   2020-01-014 r4 t  ]% m9 f6 q! }1 ^
    1   2020-01-03) `6 h% p1 G' ~  O8 D/ o1 z
    dtype: datetime64[ns]2 ?% A9 p( f" h2 z  C! [/ k; j, u
    1
    5 j9 n) `7 X: i" U- }22 {7 l8 l8 t/ l/ B
    3
    - v$ F8 }9 J3 @% }1 p4 R4 `45 w: }- Y7 {& Z; p% C
    59 F+ s) d9 |2 k4 R$ d8 k
    下面的序列本身就是Series,所以不需要再转化。
    2 h: [/ R7 J8 e( b* ]; K1 W, q! U2 g& `
    df = pd.read_csv('../data/learn_pandas.csv')
    5 O4 ^: [" a$ E" b1 Rs = pd.to_datetime(df.Test_Date)& Y% u% s$ K- f* ~; _' S& ]5 v8 {
    s.head()! u) q' d/ s/ k4 c$ G+ W
    + `, F/ D1 P5 k' E; D$ t1 L( ~  H
    0   2019-10-05
    2 ?# R3 f+ x  G$ e% j1   2019-09-04: H: i) V! w7 p/ O0 S
    2   2019-09-12, m3 c% ]/ W& B
    3   2020-01-03$ f9 p  U* `6 p
    4   2019-11-067 q6 v+ t# d# N4 ^4 |
    Name: Test_Date, dtype: datetime64[ns]
      C# H! e! b2 f1( p& L+ q  V% e6 n- J) o% D
    2
    8 V' b  _7 U9 H' r3+ ?, l1 Y- F: R- ~+ a; h4 |% b
    4. k' X3 U! [/ D, v2 \( b# |
    5+ u2 l- o  [- U: P. K; N
    6- Q- L; o  _4 |# S% T% b
    7' g  c, X1 |$ h3 ^
    8
    0 Y1 j% c5 N: `9' l& P* P+ Q( o! `0 Y  p
    10& P$ x" y( I) h' n. `' [
    把表的多列时间属性拼接转为时间序列的to_datetime,此时的列名必须和以下给定的时间关键词列名一致:- E% I- \, }6 R
    df_date_cols = pd.DataFrame({'year': [2020, 2020],
    # e+ ^1 h: I$ n( F5 t1 E2 o* `                             'month': [1, 1],: L: o7 O2 O8 N* |1 t$ _. h# ~
                                 'day': [1, 2],
    . j$ [1 T5 Q4 u                             'hour': [10, 20],2 |( l* F: e6 e
                                 'minute': [30, 50],# [+ E7 c! @, _& t; r! \: B# `
                                 'second': [20, 40]}): w8 o9 k! h3 W. }
    pd.to_datetime(df_date_cols)& r+ i3 @4 r0 w: p! L
    : `! E( n; u$ P9 S" t
    0   2020-01-01 10:30:20
      f% T2 N" r* C$ x0 ~4 Q3 k7 ~1   2020-01-02 20:50:40
    ; u; ^+ M, A2 n1 u' _dtype: datetime64[ns]
    ' k3 y2 u9 n7 V( A$ U9 U1
    ! B3 s8 r9 u% N2 e) H2
    8 Y* T* U$ j. {4 [3
      o0 G9 R  w5 g0 Q% t4
    ' i3 R5 g" B: p5
    5 q! x5 D9 \3 q$ B2 Y" @3 y1 j6) {* [% h" ?7 k' m
    70 ~# L8 S6 w9 K$ Q# F' B
    82 r1 w- f5 T! b. q/ \
    99 X0 G/ q- ]8 l& A3 d
    10: K6 t: D) z. D% [5 @& S1 _3 B
    11- H: h1 [7 e: y4 J1 F' b
    date_range是一种生成连续间隔时间的一种方法,其重要的参数为start, end, freq, periods,它们分别表示开始时间,结束时间,时间间隔,时间戳个数。其中,四个中的三个参数决定了,那么剩下的一个就随之确定了。这里要注意,开始或结束日期如果作为端点则它会被包含:! z6 G3 D1 y4 t$ }' }6 M6 f: F
    pd.date_range('2020-1-1','2020-1-21', freq='10D') # 包含  O: z& u6 `& s* \
    Out[25]: DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21'], dtype='datetime64[ns]', freq='10D')
    9 O( m! Z- J# l, B+ }/ X4 K. [6 d8 R
    : d1 C' ]6 f( Q4 tpd.date_range('2020-1-1','2020-2-28', freq='10D'). e3 j3 E; g5 `
    Out[26]:
    ; M6 I! X. _/ U! a1 m+ c$ m5 qDatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21', '2020-01-31',/ ~8 U4 ?& ]6 U! z3 k
                   '2020-02-10', '2020-02-20'],( v# `* P6 ^1 ~
                  dtype='datetime64[ns]', freq='10D')
    * t7 f7 q7 E3 |+ n- L* z9 ]! s* a2 o$ I# G" w7 P) [& R
    pd.date_range('2020-1-1',
    : I9 b) o$ ]2 k8 Y6 d% Q              '2020-2-28', periods=6) # 由于结束日期无法取到,freq不为10天: ~2 [' T( R! {9 a; x" n& l5 H
    6 X; I( m, _6 f- J4 o6 a, E% O" t
    Out[27]: - B5 i( I1 S! c5 f. d0 W5 t
    DatetimeIndex(['2020-01-01 00:00:00', '2020-01-12 14:24:00',
    * v9 k; K5 e- z3 W& `) L9 E3 s               '2020-01-24 04:48:00', '2020-02-04 19:12:00',8 H8 L% x  y( {+ L
                   '2020-02-16 09:36:00', '2020-02-28 00:00:00'],8 z% [8 j+ z* q8 J! D: p! `" w
                  dtype='datetime64[ns]', freq=None)+ G, I, I3 ^( f
    ! p' e- Q" h/ i, B* K6 t
    1
    # X% d" X$ c7 r4 G2
    / h& M9 U( B" y1 ~% ?$ \5 l: q$ F( E3# Z/ R6 K( m+ l: h9 b% e+ ^
    4  K" H7 o7 y8 y- n
    55 G. A1 H9 D( }1 ~
    6
    . k, ?1 h8 W( O# v& p" Y7: `5 F( M% l0 ~. B# g
    8& q' q8 o' K, v( ^& W2 g
    9
    - J5 D; L6 j/ a103 i" J2 d4 l6 J
    114 M" a* L0 m' |
    12
    3 i' ]* M% C6 Y/ G+ P4 [: l13
    , T0 T2 q$ U7 {, c14
    1 p% p8 s: ^7 ^* s; s! B3 L! n15
    * ~; g' [) m4 |, x# n( o5 G7 `16
      b3 U- y( \! f, J* h% Z. C17
    " ~; ^+ [: y8 C8 i4 O: Z这里的freq参数与DateOffset对象紧密相关,将在第四节介绍其具体的用法。: G* A! l& d& v- c9 ]& O

    1 `' A  j# |2 f- q# F【练一练】; o5 o  V; l, r# B/ K. A& R, n: ]
    Timestamp上定义了一个value属性,其返回的整数值代表了从1970年1月1日零点到给定时间戳相差的纳秒数,请利用这个属性构造一个随机生成给定日期区间内日期序列的函数。
    . x  q# ^. w% C) o# ?0 f% c# i7 J# W. A
    $ k4 z- S- T2 Q2 \, X! Q& {ls=['2020-01-01','2020-02-20']# u, ^0 a( B0 ]" S2 @6 C
    def dates(ls,n):
    ; I* M0 x) x# ^6 ~% r% G    min=pd.Timestamp(ls[0]).value/10**9# _  h7 N0 F5 ]
        max=pd.Timestamp(ls[1]).value/10**9
    2 m' ^9 N/ I' ~& u0 ~    times=np.random.randint(min,max+1,n)
    ( w: j, ~+ l/ r. ^    return  pd.to_datetime(times,unit='s')
    9 E  ~1 S: s0 o, J/ G5 L& t! ndates(ls,10) 5 Z0 f, e/ J% e  ?! @3 z# r9 u

    5 S7 s9 W3 G' p0 Q4 p& R3 EDatetimeIndex(['2020-02-16 09:25:30', '2020-01-29 07:00:04',. Q" K* j  U/ \, ?
                   '2020-01-21 12:26:02', '2020-02-08 20:34:08',- m2 O$ C3 ]8 u8 m! O; A3 |$ o
                   '2020-02-15 00:18:33', '2020-02-11 02:18:07',
    6 v# C+ u. h9 @4 z7 p: G               '2020-01-12 21:48:59', '2020-01-12 00:39:24',3 I1 S& S) O3 Q  A- `' i
                   '2020-02-14 20:55:20', '2020-01-26 15:44:13'],
    ( h  ^) b" E. T0 G7 A              dtype='datetime64[ns]', freq=None)
    % H7 N6 l3 f5 a7 ]1# L. X  {) n' a6 p; y% I
    2
    . Q% a( U6 d) {3
    + z0 p) K( Y5 u4 O4
    7 X, b# H) o1 n/ _! i5
    & F; e. r, @5 D& ~1 f9 G6
      j' m) P! m/ x& f' J9 B76 ~+ Q5 t/ w6 F. d" S9 m8 R0 S" k4 L
    8, Q6 [# O/ ]( G  k' g* x: @
    9
    # M9 c% m- }, x10( B* v; W* T; ~
    11/ P& o- c9 ?/ Z6 ~* x
    12
    ( m4 @# r/ t6 V8 k0 _/ v13
    ) l1 Q; P& `3 ^; t4 x14
    % f1 \# Z, R! \% g4 s7 hasfreq:改变序列采样频率的方法,能够根据给定的freq对序列进行类似于reindex的操作:
    ) f9 c' R) ^4 F* L6 X. rs = pd.Series(np.random.rand(5),* c  y; x& J) p% Z: e
                index=pd.to_datetime([- r* c1 ?6 x9 _8 P
                    '2020-1-%d'%i for i in range(1,10,2)]))
    * Q, G' h: Z2 f4 L6 h5 t9 ?1 g, D/ `6 t
    , N1 d/ R% ]: w4 O1 b  B0 j% C% n
    s.head()7 z6 y! I9 W2 P- z! M  }% Q- Q
    Out[29]: 3 @- Y7 F* A1 C$ H1 O! w% u* E* Q
    2020-01-01    0.836578! o) w3 s' ]9 r6 D, H
    2020-01-03    0.678419+ U, A3 ]* X6 l5 b1 z
    2020-01-05    0.711897
    2 S: Q+ a4 y* E1 Q" k2020-01-07    0.487429/ z$ k8 ]) K& ~
    2020-01-09    0.604705+ j8 [- z# E8 c/ a% P
    dtype: float64
    8 J# g( X8 c" l; _8 Z! g( g1 i9 A6 G$ i' Z* I2 i, y5 O
    s.asfreq('D').head()
    ) X5 Y! e! r6 |; I! DOut[30]: ; Z! A0 I8 }) {$ v8 @( S
    2020-01-01    0.8365782 N' m8 |6 @# i( t1 T, c
    2020-01-02         NaN
    : e+ U: f( R- D1 a2 @# H2020-01-03    0.678419
    0 H& y' n& ?  J6 [6 z2020-01-04         NaN
      D* U' r: c# S4 i6 L; @1 }2020-01-05    0.711897
    3 C6 L5 Y4 H: a% {Freq: D, dtype: float64+ n9 {' Q) s5 v: }2 B
    " q1 S' `. A4 a0 W
    s.asfreq('12H').head()
    3 {4 [4 {7 b0 ~; R4 a9 iOut[31]: % c5 B: f0 V$ P
    2020-01-01 00:00:00    0.836578
    0 b  z7 Q$ ?/ M2020-01-01 12:00:00         NaN
    " ?. d* r* O& V# V  o2020-01-02 00:00:00         NaN
    * O& ]: Q3 ~4 e, @- u- y. x' G2020-01-02 12:00:00         NaN
    ( N6 \8 u. G  D3 v/ e2020-01-03 00:00:00    0.678419- s+ V/ h2 D4 p5 P4 G' k6 k
    Freq: 12H, dtype: float64
    3 E, l5 a" D  H& j2 G% n9 U* W) W! ]: [7 Q: t  |: s" o
    1
    ( h) M' c! r! R9 y, o2
    ) x  H# ~! Z9 v  ^' t3
    - v; w# Y8 c# p4 @9 [4
    - I; K) [. T: ~+ v5
    + h, ]6 C' y# [: g' o68 b! R6 N$ w1 K2 N1 @# Q
    7* Z" z0 C2 K3 m. a2 ~
    8
    & }  s# u7 R0 R. n9
    ! j  ^& v+ ]2 N" ^8 n103 k1 D2 Z. M) `. o  k
    11
    + ?+ s* o5 L  l; _8 T+ i125 a. \8 _9 Y& \  o. ], L: n
    13
    + V* q+ u, t- R- u5 a* U5 @14
    + f0 b( _" g  C) |" t3 ^9 a4 Q1 I15
    $ ?  G5 L( A+ F& m6 j16
    6 X  [$ _. X" ^. [$ Z4 g- t; c  F17+ `& ^2 r, C+ W; M
    18
    - k5 ^% q+ L7 R! _192 r7 _% U: k: ^6 y5 p
    20  ]8 @: F* {: t# s6 a( m
    21% M  ]/ V! |$ m
    22
    2 F' J; H( D, p# Y( q23
    ! X3 s0 j* z; T$ P24
    ! X/ T- [% h/ W& X! I251 c$ \) k1 i, x8 L2 a
    26
    $ ^3 ]. w2 D- {8 K1 i" i6 u27
    ( M& J  `8 b! Y9 ?1 W0 p28
    - d; [3 d7 I* P+ B7 h2 k29
    2 h: _! K6 G6 b1 f6 h" h304 @3 |! w9 u* g  L
    31: u9 P+ R$ H# c+ V& G$ g) m. q
    【NOTE】datetime64[ns] 序列的极值与均值/ R6 l! f0 f' K  a5 ~
      前面提到了datetime64[ns]本质上可以理解为一个整数,即从1970年1月1日零点到给定时间戳相差的纳秒数。所以对于一个datetime64[ns]序列,可以使用max, min, mean,来取得最大时间戳、最小时间戳和“平均”时间戳。* p8 k/ D! Q% O

    9 _; O& d, z1 M) q6 I5 U6 |# D10.2.3 dt对象# \% @9 T0 a' I9 m% U2 }) J
      如同category, string的序列上定义了cat, str来完成分类数据和文本数据的操作,在时序类型的序列上定义了dt对象来完成许多时间序列的相关操作。这里对于datetime64[ns]类型而言,可以大致分为三类操作:取出时间相关的属性、判断时间戳是否满足条件、取整操作。* ^, Q5 T1 Y, u6 ^9 v( E- q& y0 W
    3 Q! d1 O' T" e. U* q4 F7 l' a, H1 h
    第一类操作的常用属性包括:date, time, year, month, day, hour, minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter,其中daysinmonth, quarter分别表示该月一共有几天和季度。% p" t; ?& P7 L% K
    s = pd.Series(pd.date_range('2020-1-1','2020-1-3', freq='D'))
    6 \$ U6 E; [# H9 I+ i. b
    % h! b2 d0 K) z' T7 ]# B0 }" f9 Ns.dt.date
    0 G7 t$ c3 W& WOut[33]: / w( L1 V7 N/ `4 W6 ]! o! p
    0    2020-01-01
    / y4 j9 P; h" S- k1    2020-01-02  t; e9 F# P& v3 J; D& ^) J$ {3 X" r
    2    2020-01-03; [5 ?3 x' _! }6 ?
    dtype: object
      f; h1 M$ x6 S" p, V4 A# H' o1 V" U, V5 ^+ N3 `# k) n4 ]+ Q
    s.dt.time
    ) O0 f! S7 B5 o  b5 WOut[34]: 5 e6 \' `- R1 _" u
    0    00:00:001 B6 U& r, N6 b, U3 c
    1    00:00:003 |( S; F7 {% {+ t4 q9 o
    2    00:00:00" t2 U+ s( m7 G! f& P
    dtype: object; u( u+ y" C* f. ~3 ?
    7 F1 {: M, M. K0 R$ V, C8 L4 q7 M
    s.dt.day' @( q* {) h1 v4 _; Z/ E1 x
    Out[35]: - Z4 `. k2 P) b) G8 U# r+ `
    0    1
    - r1 }2 I; p* T* i8 x2 P1    2
    ) P& x6 V! y+ S7 r4 |2    3
    5 L3 ]' v6 `1 bdtype: int64
    3 N9 C8 t  s. w1 {* l- n# u: A$ i- [1 Z( C  Q, O% m
    s.dt.daysinmonth* Z; p/ n$ c5 s
    Out[36]:
    : P; K6 T: C' J0    317 J# g; Q* C* o6 r, b3 H$ r
    1    31- X+ f  ?4 C/ D% H/ T
    2    31" P* ~- L! W' B' P. Y
    dtype: int64
    ' s& y* |0 C) x0 H
    : ^" m, D: W0 X1: ]& M0 O# w0 S0 o
    29 l) ^8 Q+ K: ?- c
    38 F' h6 U, y4 p: _; i
    42 m6 T8 |+ v+ L1 G% u: f
    5
    + P% ~( S2 X1 j9 v* R/ L/ ?, ?6& y& b- o( j$ e( }
    71 f4 t. q& x7 J' W& J0 G' k
    8
    8 ?0 [0 ~/ T* h: N" l9
    0 L0 T0 [# T7 X7 M% t10% m$ e: v! v3 J# ]
    11; `, G7 H: u6 P
    126 I6 V6 R: R2 H  E( P
    13
    0 S$ K3 ]' j3 }) c2 B14
    * q% e! x- q, P+ j15' ?! w- `# X& z9 q  F* d9 f
    16
    + ]3 E1 c( Z+ M: V4 j! s17
    0 R/ t! L+ K3 z' @' }( r/ \2 z18: f/ F1 z2 B/ y
    19! Y- d/ B" e. {1 S  ~
    20' a/ [1 [$ V% \7 d
    217 S% G' s* ]3 g& V0 `
    221 X! u5 O) n4 c8 t! Y1 p
    23
    ( ]3 G4 {7 Z! F, C$ i' k- h( Y24
    % O. l6 k4 g$ o25- ^! P7 r) q' s6 h4 C
    26/ z, K+ b0 \. Z0 j+ Y. e
    270 |( A$ d! W, D3 K' A
    28' E% {5 P8 g" Z$ k( e
    29
    % Q& R7 a# O# W( U& S% I  在这些属性中,经常使用的是dayofweek,它返回了周中的星期情况,周一为0、周二为1,以此类推。此外,还可以通过month_name, day_name返回英文的月名和星期名,注意它们是方法而不是属性:0 Q' D. c6 s# t( j

    ; H; c, @  A6 x+ r! |4 v( Qs.dt.dayofweek1 a7 H' N, {$ y9 Z) p: `  v! H
    Out[37]:
    " `" \* B% r4 R5 I2 J. I0    2
    * O9 N/ D0 H) n* h1 \1    37 [: m$ p$ K: ~" _
    2    4
    ( e- u- R  n0 h8 `: X+ W: z; Edtype: int640 E$ |0 S$ B. e

    7 O  ^. b6 ]3 S3 y. Ss.dt.month_name()% l$ o0 w1 v9 E. ?- _7 S) D
    Out[38]:
    7 r6 m# Y+ q& c9 C0    January
    ' W' u; l: i$ n: G; x: d. W5 y1    January. D/ J$ {. V% O1 ?" D( a  L
    2    January9 F4 S6 \1 y* t  h& J. ]" H6 Y" g
    dtype: object
    ! V2 g8 g& k7 k& ^
    6 C" R0 L; M" `$ W2 ls.dt.day_name()+ \5 m, P; D6 P6 |7 g- j
    Out[39]: " m; m, C. P. I: h$ q& v8 |
    0    Wednesday
    + u' {* x! O0 U8 r9 V6 {1     Thursday
    % Z% c+ H# t! {( I/ q0 P2       Friday
    * T* ^; E/ a/ _1 j( c) vdtype: object$ x/ [5 K6 I( k. W( u' e; `+ x

    0 B. n8 [8 f8 `4 {! A6 E% y1$ o" A. ?& I' O& x  V9 Z
    2& v3 r1 b! v6 h6 E, u
    3
    , j* _9 G0 t. k8 n1 y. J, f* l0 V45 J# c+ e! y6 s$ X: R" s, q
    5
    3 z7 N+ w, j8 B8 m9 X4 ]2 z$ q6
      W% }0 @: a7 n* E, g7/ t, j8 [. l5 j5 u5 G. D
    86 W+ [- @4 ?% r4 D5 u  h
    94 [7 p3 m# ?4 Z; d* r4 W2 X& _
    10' R, t- N# [5 r1 Y6 y
    11
    + x8 o0 O& p. M+ Z" r1 }12
    6 z; `' A; ?1 X  ^13+ v( |7 }. i$ `* g/ `4 G  V! V
    14  D( r0 J2 p/ p, q2 t: j4 F) H
    157 E! m( g5 Q8 ^; ~
    16
    5 F( O1 A) F9 q" E' u* n2 c17# t2 @$ W+ n/ _4 C& I
    18
      i9 C& v( g1 E5 Y+ y6 G' O19( s) Q# s: P$ x& q+ T( Y5 @) j0 O
    20
    , v4 z- t: m( H' n9 m! Q9 p第二类判断操作主要用于测试是否为月/季/年的第一天或者最后一天:
    ! d8 S: U. J8 D3 t: Xs.dt.is_year_start # 还可选 is_quarter/month_start
    # M: z( o: v1 ~) SOut[40]:
    0 M. Z4 G: {4 Y& z- |3 E) ]0     True6 \8 ?0 P- I9 r" @9 Y+ N
    1    False% j; v% {7 [; H$ U  e
    2    False5 V! O7 W" c  K$ X- p2 l
    dtype: bool
    3 c+ n2 q! `0 [" }5 @5 \8 Y( a6 j- k' n( r. R4 \* K
    s.dt.is_year_end # 还可选 is_quarter/month_end, p/ }$ a' L  E/ ^$ b% x$ g3 J3 m* M
    Out[41]: 1 a2 R0 {- P3 j& O6 ~# w& c
    0    False
    . D8 z) u: h- S# G1    False
    % Q0 L- z0 ]  K9 V' Z# ~9 i, R. \8 v% }1 }2    False
    - M! ?5 s2 v5 {! fdtype: bool
    ' T! l( N5 _. }' r$ [% [1
    ; O# x/ B5 m& R3 V# |0 z21 V! c; O7 K- D/ w
    3  [% m7 m8 l; ~4 O5 H" E
    4
    8 V" K# a3 t6 t5
    ' _  h/ V0 H" K, t' E6
    7 ]1 J' \1 V+ C# c8 m' q: i7
    # _9 Y9 p* p' a! w/ V8
    1 ~$ U& t; V. {& A8 B* u8 c( `% o/ W. w95 v* K+ k5 e, z- ^
    101 l0 f: Y" {- X; H/ V
    11
    ( m7 }( W  }  p, B12/ Q1 H8 d, ]% ]8 e2 Q
    13- ~5 V% Q0 Y; ?- q( {' L) m* x; M
    第三类的取整操作包含round, ceil, floor,它们的公共参数为freq,常用的包括H, min, S(小时、分钟、秒),所有可选的freq可参考此处。
    ! o& w( b- g; {( p* us = pd.Series(pd.date_range('2020-1-1 20:35:00'," l0 u8 M. Y$ v/ \% F9 @5 h4 r+ ^! g
                                '2020-1-1 22:35:00',
    6 A( ~$ [, u- D                            freq='45min'))
      U, M) s  h: j7 a5 H& W5 R" l6 f) E) p- Q

    . O$ Y" v$ Q4 S' Ps2 _; {2 ^( d' O9 G9 {
    Out[43]: 8 I8 O: L. D+ D6 c, D2 h
    0   2020-01-01 20:35:00
    0 \4 j6 {7 a( x. M, e, ^1   2020-01-01 21:20:00
    7 L3 ]5 V; K/ R2 [. H1 L2   2020-01-01 22:05:00+ U; w! D6 a) l/ Z: \, u
    dtype: datetime64[ns]
    6 j$ w+ Z- W' h/ F% R/ [, Z5 R8 x/ Z
    s.dt.round('1H')
    ; Y# m! z/ ~4 xOut[44]: " p) N( E# H5 Q: x% D0 ~1 w# m
    0   2020-01-01 21:00:00; J' C  U& W' v5 @
    1   2020-01-01 21:00:00
    " v0 a  L* Y- v2 K+ J2 |! q+ l7 h2   2020-01-01 22:00:00
    - ^: J9 y7 @- n: {# odtype: datetime64[ns]% a3 P: L5 m; j& J. _/ c
    - e6 ~& z; Y& f, s6 M( G/ T
    s.dt.ceil('1H')5 v  [5 t& C5 F& ]
    Out[45]:
    & }( P# }7 d# A6 e7 m/ c% Y0   2020-01-01 21:00:00
    4 g" \/ C! i  Y( H2 r1 I1   2020-01-01 22:00:00
    2 I% ^( y' d( L2 i: }6 S# ^  _2   2020-01-01 23:00:00
    / o! d  [1 x+ q6 l6 |9 W0 Cdtype: datetime64[ns]) @: H4 G2 p6 T
    + ~7 _8 b% }# ?
    s.dt.floor('1H')/ V$ J$ Z* H- C1 V! S
    Out[46]:
    2 Q/ W  H6 d: G0   2020-01-01 20:00:00
    * j" l6 p4 Z; Q. R1   2020-01-01 21:00:00; m- Z; v: o, J
    2   2020-01-01 22:00:007 z2 @1 Q6 l! P
    dtype: datetime64[ns]
    + I( O0 y% _0 n3 i$ F' v, d, Y8 Y* @8 C- }
    1! I4 ?5 v- ~' x8 v# c. H* o) E
    2  a6 g' k3 j$ V! O( p5 w
    3  t( j0 E% E+ z6 m% B9 H
    4+ ~/ Y) B# w, j6 B7 W+ j
    5
    ; D# Z1 X- @1 a8 E6
    . a. z, [  O7 K1 @7 u7$ ?; |  |$ O' Q3 K* S
    83 g* x$ `9 C; L; J) `
    9
    ( U; L2 K  C; l9 E; }6 G( g10
    8 E* u  l" [- a0 t11
    1 m* Z0 o8 G$ o, m( x. c  ?12
    + \% e/ r9 j: b7 h9 Y7 b( X. o- Y! s13
    . R$ v: L; e8 ?" Q) ]) Z142 |+ r2 W1 @$ j+ z* Y  @
    15
    9 ]/ O* B1 e0 @8 |6 A. |/ R16
    2 ?- J1 v. g5 X4 M6 S  Z17
    / \) `, O$ e# O7 X. a18
    & u6 Y8 T' |7 A; u" E% D: |/ ]19
    ' L: Z: j' H, C, i- ]' G- T20
    , ^; d8 C- k$ J6 Z6 j* o  ~5 k21
    ! |0 r  f$ K6 m: |5 E( |  C1 H# C# Q22
    " `1 g2 B5 h& p) f23
    7 x3 _2 m2 E1 ]4 \3 a' g8 D247 b/ [: w& H3 P7 H8 Z, ?" w
    25- ?9 }5 M+ Z2 [5 K
    26
      Z% |" r5 e9 D& H  B0 `( L, u0 @27  F9 J3 N- F% I; A
    28. O% D9 H8 a7 S! u: }) y( i
    29
    , j! k0 h$ h  U30
    ) u* l$ u- A" g& z31$ G8 `2 K9 f1 B
    32; P$ `; R- S4 f' i
    10.2.4 时间戳的切片与索引: m: a3 W/ ~  n$ n, D9 {6 R
      一般而言,时间戳序列作为索引使用。如果想要选出某个子时间戳序列,有两种方法:# {) W( d- I1 m! y4 w3 k

    ) \% r2 I  V, [利用dt对象和布尔条件联合使用
    7 x2 [* u. E4 A3 P利用切片,后者常用于连续时间戳。
    + q7 g0 C; w( r% ]. r: _s = pd.Series(np.random.randint(2,size=366), index=pd.date_range('2020-01-01','2020-12-31'))3 r0 A5 ~' [# b; s9 P! ]% D
    idx = pd.Series(s.index).dt
    ; w) g/ }2 ]* O! t4 Ns.head(): `2 X" ^; c) `' F
    ! i8 V7 b! X0 L4 f* ~8 Z4 e
    2020-01-01    0; Y& ~: k$ ^5 B" J5 c" @4 U# D8 \
    2020-01-02    1
    - j- T/ ~# B  ^6 ?( ?8 R2020-01-03    12 @) Y- v) _  S2 n
    2020-01-04    03 Z. f8 S  a3 h! s
    2020-01-05    0, Z3 }: n& c/ U, U# k  V+ H
    Freq: D, dtype: int32; L7 }/ L4 n# Y* `# j5 {1 {* {
    1& x: [# x3 h+ |" s
    27 [" k: f; ^7 P7 L4 z9 X
    3; f3 ]+ w4 B: c9 T! H& L
    4
    $ B3 G; c3 n4 Y( I0 Q3 P( Y8 o5
    / c4 p6 F7 Q% X- T( T6
    & ~# c: t' @3 ^4 P3 }+ h7
    1 u3 m- z; V8 X: b: m8
    0 C" P: ^3 g+ u( I' n$ E. o  j6 Y0 V9
    0 R& C* i$ Q7 M" |& V. |* p) C" L) u10
    # r) F0 U8 a7 K% o. G+ D# WExample1:每月的第一天或者最后一天- ~" k% N2 ]' I, V% ~

    - f% c. k6 H. X- l, ~  Ms[(idx.is_month_start|idx.is_month_end).values].head() # 必须要写.values
    $ Z: Z- E" U6 F9 k4 H# COut[50]:
    4 F9 @% G# D! C2020-01-01    1
    6 t# I; `# q: Q' ^2020-01-31    0# p8 m2 q. g- V: M$ R" q
    2020-02-01    1
    6 r7 R; Z8 @( O' T! k, x2020-02-29    13 v3 [! X4 [% s* c
    2020-03-01    0
    ( Q/ j! f5 c! x( W# k- tdtype: int322 @  p2 }( g5 |# Y* H) P. {
    12 l5 L5 q5 \/ J" ?: w# j  K' Z
    2
    ( I3 z8 T% ~- o; j3% C9 G/ @4 d$ c; K! J: Y- Y
    4
    , d% M* J5 K. |/ \9 k. ]* G6 p( X5, n( _+ q; f* t
    6
    ' a" x8 c: e" C7 O7 [$ [. I) D7
    + o5 D+ v6 W1 q, |8
    3 Z, r3 Q, f4 N8 WExample2:双休日7 y' K* e' L  z

    ' c! A% P; ^- b6 I$ m2 ks[idx.dayofweek.isin([5,6]).values].head(); W7 K- v1 p: O: D! c
    Out[51]: 9 Y7 N, L1 P% r3 V+ w1 g
    2020-01-04    1
    # m9 W2 _0 i6 R) b) J6 g3 E: N2020-01-05    0: f5 C; k! H3 i. a: N
    2020-01-11    0
    $ Y- K# ?3 v1 A; w6 U7 j2020-01-12    16 U" m. _. C# i  V& L7 T7 Y& B
    2020-01-18    1
    # l$ b) {# I5 ndtype: int32
    , y4 |$ |% U3 v, z' D( u# P1
    ) ^" I+ m" a$ n8 f1 T2 a: d! D22 W, W4 q0 V7 l8 k4 k$ [
    3
    0 F1 {" ^7 E% L: D+ i48 Q" _. r5 c2 S# n
    5
    ) s# ?: ~5 ]$ m- ?2 f6
    % k5 _/ n8 I, Z0 P+ y7
    # K6 N2 ^4 B6 A. B: d- P. R. b8( W' {8 u9 j7 c, {" J
    Example3:取出单日值& |' A8 X9 Q% l. j3 {8 r$ g

    ! ]* c( ^7 n8 c$ C3 e$ H- Qs['2020-01-01']
    3 P" t/ [  n" t/ o3 n, dOut[52]: 1
    , N# V5 _2 z) V5 ^) u5 M. O6 _4 \! u; ~" R5 f' A! ^% Q4 A7 i
    s['20200101'] # 自动转换标准格式2 p( C) T* N& N4 }
    Out[53]: 10 Q) F9 o  E# o9 E" Y+ o5 I
    10 @0 e) \- ^. G+ N
    2
    6 R/ q7 y8 t' l! F  s1 K9 |. R! h  |3
    ' o/ |( ?, l- Q6 U: V1 x+ {1 E4( S1 t# C9 j2 y3 L( L; w+ C; G
    5; m7 C" O. a4 d
    Example4:取出七月
    & b' p1 `$ {8 ^0 p4 S) L7 m8 k4 E( A4 x  U2 l
    s['2020-07'].head()
    * r; Z' E* ]/ s$ x/ ^% XOut[54]:
    2 q, m6 e) {* s6 x* j# C- U# t2020-07-01    0/ n# t! p8 M' l; v3 q. z
    2020-07-02    1
    ) ]- R# Z+ R# c$ L2020-07-03    0
    " R" n( P, g5 P8 }* q7 W4 w2020-07-04    0
    ; v7 e" H, C" J2020-07-05    0
    7 n" n, a8 ~! x3 j/ U8 f3 TFreq: D, dtype: int32
    8 O% [! e8 P6 _( W1) O' }! L) r0 |- z6 i# V+ l4 m1 @
    28 s* ^+ o1 z2 r4 V: `
    3, |; u: c* P5 Y
    4  H5 \4 u# w- }& P: o
    5
    ' f% `1 ~7 i$ B; @9 B3 q60 {# m7 L1 {& {! d! H0 d* b
    75 \7 \" c. i0 z7 S, Y
    8
    $ F1 n8 Q, d6 j3 W# R8 P( nExample5:取出5月初至7月15日5 a) v# }, k5 s5 p& Q- B  m

    . I) f" v$ h! c- Hs['2020-05':'2020-7-15'].head()
    0 Q0 @1 T. I  F  P- dOut[55]:
    8 S+ _5 A3 u0 t. M0 }9 o) L2020-05-01    0( o  S2 T8 X, Q! ~1 M: n
    2020-05-02    1! j3 e1 O  b! N1 u. c/ q& s
    2020-05-03    0# f/ w( f" b( W8 v+ _1 b
    2020-05-04    1& W& O2 g6 Y8 _; z$ V( \: j
    2020-05-05    1: V: f$ n6 D8 ?  N% o  ?( E' ?
    Freq: D, dtype: int321 f( {% a$ r' ~$ _" l; a' R) F
    * J& X# W: L4 R  k  r
    s['2020-05':'2020-7-15'].tail()
    ' R$ \! I8 Z; s0 n) QOut[56]: 5 d7 J/ x0 M  I8 X
    2020-07-11    0
    0 \" ^) M* a5 z9 u4 C2020-07-12    0
    / ~/ ~. E6 q0 ^. n7 l2020-07-13    14 z1 d8 B3 R. ^6 {
    2020-07-14    03 B7 Q; X! A; v  B
    2020-07-15    1
    ! D( _( A( P, ^  y- L: t. J- hFreq: D, dtype: int322 o# d6 e( B1 S; Q) _2 `7 |

    5 y$ p# r- ^2 p, J4 W' |1
    $ ]; W& _/ c8 t% h, g2' I; |1 ?6 g2 ?0 {6 s
    3/ T! o$ [/ l& e
    49 q( f( F4 S  k7 C) s4 I8 L1 l
    5
    ) F2 B/ R1 B1 o) m63 v; ~8 a( K) R; q/ U- A
    7: K' l) z6 [* K8 Y0 g
    8
    6 B( [# F' r( Z; \4 [: Y0 d9
    & x9 b7 R( a" y$ ]$ E" }2 P! A10
    2 g3 K" [5 U/ w4 f- H& P119 p. J/ w! ^& v# j4 n& @/ G
    12
    & x1 [" H, s: A" f/ _13% l; c+ v8 G8 p$ I5 P7 a& j1 ?. n
    14
    + x2 I7 G* Y4 J, [- R2 v15; V. Q% G/ ]( g6 o! c/ t
    16
    5 R2 z# W8 Q- Q7 k1 q+ s/ K0 O17
    # l- v  }4 H1 T; \3 a% B  i10.3 时间差7 v+ c8 J2 w, l
    10.3.1 Timedelta的生成
    - B1 Y! L3 B( Y6 u. O' M" }/ B: Xpandas.Timedelta(value=<object object>, unit=None, **kwargs)+ ?4 h( m9 C: l2 ^" C
      unit:字符串格式,默认 ‘ns’。如果输入是整数,则表示输入的单位。0 D2 I7 z6 n0 b
      可能的值有:
    ' R% T8 q/ n# F
    $ S* O* a) u! N2 }‘W’, ‘D’, ‘T’, ‘S’, ‘L’, ‘U’, or ‘N’& T7 K' ^" @+ I6 U4 C* y+ R
    ‘days’ or ‘day’  B0 v, [# }' S( L$ b1 {% M
    ‘hours’, ‘hour’, ‘hr’, or ‘h’; G  q0 N& @( t. l5 l0 T% L
    ‘minutes’, ‘minute’, ‘min’, or ‘m’
    2 s* f# A; g8 P/ B‘seconds’, ‘second’, or ‘sec’
    ) T) `% ~. |1 O3 ?& @: n! v6 w6 c毫秒‘milliseconds’, ‘millisecond’, ‘millis’, or ‘milli’
    4 ]9 V7 I8 Z8 k+ W, S2 [微秒‘microseconds’, ‘microsecond’, ‘micros’, or ‘micro’) J6 i4 H5 p4 a- m; I
    纳秒 ‘nanoseconds’, ‘nanosecond’, ‘nanos’, ‘nano’, or ‘ns’.
    2 \( d3 S+ E( p, m2 G时间差可以理解为两个时间戳的差,可以通过pd.Timedelta来构造:
    " n& r. U& ~" Vpd.Timestamp('20200102 08:00:00')-pd.Timestamp('20200101 07:35:00')& z$ k* f3 v6 e2 R" }! x
    Out[57]: Timedelta('1 days 00:25:00')6 e7 u$ [5 U2 \

    : k% j/ [* Y5 x* s, M/ [8 D: Hpd.Timedelta(days=1, minutes=25) # 需要注意加s
    / i" A# Q0 {+ L- W% kOut[58]: Timedelta('1 days 00:25:00')9 `; v0 J1 z. L- z9 }
    4 b2 `9 B8 Y& F- m  S
    pd.Timedelta('1 days 25 minutes') # 字符串生成
    2 W% @2 A1 X- e% \/ P! AOut[59]: Timedelta('1 days 00:25:00')+ S2 M( Q% k" [
    3 L: U, V; @2 H. g8 j
    pd.Timedelta(1, "d")( z% T3 c* j" ]* k
    Out[58]: Timedelta('1 days 00:00:00')
    ) A9 L" l' m9 G2 P7 \1, |5 p7 X- i' G" u
    22 o0 z3 @& f5 x3 F% n
    30 V' I4 X  ~2 @" {) T
    4! D9 |5 q: P- u; s
    59 P: {% Z; K9 h4 }
    6
    5 u' a$ ^: g% u% `7
    6 X3 L- o5 Q$ x" A8
    2 M$ ?2 r* {9 d  B# p9" U; |. H$ l. g
    10" s7 {. P! ?! E0 k0 W6 ]
    11
    3 L- V; G3 _- k  l/ F& _生成时间差序列的主要方式是 pd.to_timedelta ,其类型为 timedelta64[ns] :( f; B$ E5 {- m" o
    s = pd.to_timedelta(df.Time_Record)% Y3 _# C; `3 C8 R/ _! s
    4 M* L4 W/ W# t. K4 y- [
    s.head()
    # c4 u" c+ z2 `8 F3 zOut[61]:
    % b) Y1 d  m6 I; W: b; g0   0 days 00:04:34
    2 \1 ~; Y3 Q1 i7 c' q1   0 days 00:04:20
    # |; ?- ]& i9 V4 _' Q2   0 days 00:05:22  s$ _7 _; S( Z
    3   0 days 00:04:087 ^8 x; [  ?. f, U
    4   0 days 00:05:22
    9 j9 Q) G0 p; \7 E9 |Name: Time_Record, dtype: timedelta64[ns]
    + g/ d: Y' h+ u$ ~4 q1
    7 M0 C( V. I/ m4 w, `3 |2+ N& K  |$ z) k- x. D, ~5 }4 k( f9 r
    3
    6 K. R+ \- o- @/ {4
    : c$ m8 |3 L% B- i" J7 j+ {6 v5$ T6 w+ |8 ^6 h' f
    6
    , t  j7 z$ f3 B75 f2 j0 C# |2 D9 Z/ M: x6 D
    8& g- q: P1 W# Z4 }4 o
    9
    ; u9 f4 C, a$ g% b8 `1 l  v10* l: ?1 j( o2 G- X$ _
    与date_range一样,时间差序列也可以用timedelta_range来生成,它们两者具有一致的参数:
    $ G, \' F( ]  w2 ^9 t4 ?) W7 lpd.timedelta_range('0s', '1000s', freq='6min')) p# @# r& M2 C. _9 B& b
    Out[62]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:06:00', '0 days 00:12:00'], dtype='timedelta64[ns]', freq='6T'). ~' E9 M& ?2 D! R4 K
    # E7 P  ]  B; M* A2 r  G
    pd.timedelta_range('0s', '1000s', periods=3), ^3 C3 ]; y' T: X$ r
    Out[63]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:08:20', '0 days 00:16:40'], dtype='timedelta64[ns]', freq=None)" }. l1 q( R/ k2 G% {5 z
    1
    % o6 q' X7 T& O5 k8 F2
    % _  k4 u9 ]8 R1 |% B- a( W3% e4 b; [; K% C8 C7 f$ @) \
    4
    # i  B1 u6 o5 w5
    ' X4 B/ @" |3 z6 ?: ~& A对于Timedelta序列,同样也定义了dt对象,上面主要定义了的属性包括days, seconds, mircroseconds(毫秒), nanoseconds(纳秒),它们分别返回了对应的时间差特征。需要注意的是,这里的seconds不是指单纯的秒,而是对天数取余后剩余的秒数:( U) ?; e& T, A& U* M  \$ S" c
    s.dt.seconds.head()+ h. E4 S' q7 ]
    Out[64]: 9 ^" p7 M, B9 B6 B& g' {2 C
    0    274; O) m6 a4 |3 p0 B* b
    1    260' a) I5 }1 z6 e' Q
    2    322
    ) ]8 B! F7 C+ G+ r9 Z3    248
    0 w/ r9 D8 L. ?$ A, `4    322
    ; h( y5 L( W! }Name: Time_Record, dtype: int645 v9 l4 p0 x  ]+ D9 |
    1: `% o. c. |+ ^5 r, ~+ Z
    2
    $ }( {2 j3 c# e& @2 e- l5 b5 y; }7 V) h3% i! t, T- H: D
    42 v9 L: J6 S) v" N
    5: l  p. G5 q8 {
    6& x0 `  S6 q. j
    7
    5 X) t7 ^; E' Y) L8! J( |. N& |. Q3 {+ o6 g  v( w
    如果不想对天数取余而直接对应秒数,可以使用total_seconds8 ?" Y# }6 i% b+ p) O- y

    7 Y6 p" |6 E4 l+ ws.dt.total_seconds().head()
    3 z; d5 Y3 `: X, @! X5 qOut[65]: ' @6 a& v8 X% O9 P9 h: R0 f
    0    274.0& e, k% |3 {- t
    1    260.08 K' P4 D- `1 E1 E9 X* s
    2    322.0
    1 h- t" V- y1 q" {8 @% @  }$ t0 t3    248.0
    4 b) ~. K1 L0 Y1 P$ J7 y4    322.04 x: o6 C9 X1 l$ i
    Name: Time_Record, dtype: float64
    ' H( ]# ?+ U7 ]0 E0 J/ v' @; O14 {3 [9 L5 B; V) Y, G
    2( G1 `2 O, Z0 `0 Y
    3
      [( x! `: I1 X5 R: C- b, I1 C- z4
      y+ ^) G2 C5 s) f! T5
    3 r! b. c- d! v6
    ! V% N1 }* r, h4 C6 z& J7
    : r3 Y  I' N2 r# l$ k1 j' v* d7 ~8
    - O9 X. D: U- I+ T与时间戳序列类似,取整函数也是可以在dt对象上使用的:1 ?* }8 n1 u4 o7 L
    + m9 H0 X8 J! p0 E
    pd.to_timedelta(df.Time_Record).dt.round('min').head()
    ' f7 a, W4 L- p3 W( l: o! B' ]Out[66]: ; U: z" Z& y8 E- J
    0   0 days 00:05:008 o/ \; k, q& I3 w
    1   0 days 00:04:00
    " j' x1 r$ I% i$ w! w, O2   0 days 00:05:00
    , a4 N, l5 }" P9 I2 V! V, A3   0 days 00:04:00
      }0 ^, T+ f* a6 C" ]# i4   0 days 00:05:00& h0 o* ~* G% i5 {. P2 I) d
    Name: Time_Record, dtype: timedelta64[ns]9 u) g. `$ H* T0 i# f6 y# s  _1 H
    1
    ; T6 X* V+ l6 j) O2 p9 L7 u2
    6 @8 Y  O/ ~- P3
    / V* q" l: o# S" t5 C4, y# t+ ^; G. J' f
    5
      \. Y' V2 O% H0 q- R1 j6
    5 a8 F" T1 |- f7 n& `4 F7
    ; ^6 t1 w) q; }  S( Z$ C% @8
    / U$ j) ]9 H$ p$ j7 ^2 a+ e10.2.2 Timedelta的运算( k0 x% \  X+ m8 t& v" C* u
    单个时间差的常用运算,有三类:与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算:
    ( z7 u% _* C  rtd1 = pd.Timedelta(days=1)
    / W8 z6 l0 G5 ~$ i) ?% g, otd2 = pd.Timedelta(days=3)" O9 G1 ?# m: z
    ts = pd.Timestamp('20200101'); E2 r0 r# R( e9 e- P( b2 W0 P4 t

    & Y" l! O2 p# X+ L* ctd1 * 2
      s  k" b/ j: bOut[70]: Timedelta('2 days 00:00:00')9 E3 I! {- R' b) @: @/ D& S

    0 J  H" _3 L. s3 v9 D2 L5 H+ Q1 T3 ntd2 - td19 {, n# C9 J2 R  [; L, @; |
    Out[71]: Timedelta('2 days 00:00:00')
    9 @5 M3 d- W( M1 ]% c5 u1 n" o# Q  t" K  T
    ts + td1
    1 Z. j5 G" n! }' Q$ o# S9 EOut[72]: Timestamp('2020-01-02 00:00:00')
    / c( `8 ?6 {4 j. Q; j" \" h, b/ e* x) W- i$ r' r" ?5 H+ ]6 L- e
    ts - td1
    ( ?- H% ?) i( YOut[73]: Timestamp('2019-12-31 00:00:00')
    $ y0 o% @, \2 i$ s6 {2 Y1
    8 P5 z7 N+ D7 |% @6 z+ B2/ r+ ^, Z4 I: s, j
    3
    ; u' l  m% x) }) Z0 \4: P& _7 z2 c  I& `
    5
    4 ?7 r4 ~& z8 ]6. O- {- z  E! k. _
    7% C! i& `/ _. [# X0 P( A5 c+ X
    8" N& m; P0 k5 \  W
    9
    * V" Z+ M! h! Q10+ _9 Z0 H# d0 m6 R7 i
    112 ^7 I) s4 k9 |4 Q0 w
    12
    5 R9 f/ u9 v) N2 c13! O7 y/ C9 d) y
    14! A) n! V" T) f7 Y/ W& w" Q
    15% s! n, x# q/ z) n. A& s: B
    时间差的序列的运算,和上面方法相同:
    2 U$ u, B. M# N; v/ Ztd1 = pd.timedelta_range(start='1 days', periods=5)" x- \0 X+ |( u" V7 N1 [
    td2 = pd.timedelta_range(start='12 hours',
    & q/ ~% N/ W7 t0 j, b                         freq='2H',
    9 Q2 @& r: P( m9 Q! B                         periods=5)7 {# G+ ?& X! J
    ts = pd.date_range('20200101', '20200105')
    7 o$ S0 s6 S' v$ Ytd1,td2,ts9 z- q5 i: m/ T" ~

    1 ?  {. q1 A0 u5 ?; x2 B' qTimedeltaIndex(['1 days', '2 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq='D')
    " Q5 }: ^! G# mTimedeltaIndex(['0 days 12:00:00', '0 days 14:00:00', '0 days 16:00:00',
    4 I7 k% Q) d2 {! j' W                '0 days 18:00:00', '0 days 20:00:00'], dtype='timedelta64[ns]', freq='2H'), k6 ~1 B* g0 |" u4 s5 Q, ?  T' ?
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',. Z: @& n6 @% i& j" J7 v# K; `
                   '2020-01-05'],
    ' @4 b7 n3 S2 `0 E- ]  F              dtype='datetime64[ns]', freq='D')
    ' Q7 d- Z) `+ y6 K  y4 Y" {1/ ~6 M/ L5 N4 _. f- k0 x) s
    2
    & o. K% a6 r' U4 g% F: p! `30 l+ _- r- R. V7 J. _! e4 \
    43 |) {6 @3 y6 K  ~
    5: e7 u7 U0 j8 l0 j1 [
    6# B& M+ C/ ]0 n
    7
    - Y: o/ p. J, }& ]% o5 ]8
    1 y2 P. F! T* Z" C+ A1 ?& P; ?9
    5 v' }  A/ G4 G5 x8 ~7 J10& `' r0 z* k. F9 U2 B7 N1 Q
    11, i' u: [2 v$ {
    12  @# t& l: T8 W* |. V
    13- y& w* y8 P0 ]/ F7 t/ x+ b
    td1 * 5& T7 r; s# {5 @5 X4 k+ j1 @6 W
    Out[77]: TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D')
    3 [0 j; w' U7 p' D$ X
    , j( d) d$ z; r3 c8 Ktd1 * pd.Series(list(range(5))) # 逐个相乘% ~6 [/ B1 Z' B( s
    Out[78]: & @7 l- S) G( q  ]7 ?& @
    0    0 days
    5 W5 C5 w7 a0 k1 d1    2 days5 N, g- M! G, b
    2    6 days8 }, b) i& U4 }) c6 D* k! r
    3   12 days
    1 O2 W+ ?% U+ ]: R% a* i9 l7 `: F4   20 days
    * k. O: \3 w& V/ o2 udtype: timedelta64[ns]" X0 r6 Y3 k5 I$ \

    7 y: F4 A3 ]& W6 u5 u. ?: r" ptd1 - td24 s4 a' E5 c% j
    Out[79]:
    ) c& J0 d6 {# j. o$ s; STimedeltaIndex(['0 days 12:00:00', '1 days 10:00:00', '2 days 08:00:00',
    7 x5 X. C8 \( k- C) k* P( e; B$ m                '3 days 06:00:00', '4 days 04:00:00'],
    9 E$ t& P9 U4 q' [1 a               dtype='timedelta64[ns]', freq=None)
    6 \. k1 Z; F) p& I: a
      s! t% q4 r1 jtd1 + pd.Timestamp('20200101')9 h, R- ?' f- R% S( z% A# P
    Out[80]: # @2 _5 H6 P8 P4 i, I/ |9 h
    DatetimeIndex(['2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05',0 @. ]# i  P9 b7 ]6 j/ e
                   '2020-01-06'],dtype='datetime64[ns]', freq='D')4 l) c% ^, P- `$ _
    + G# c9 N, @  m! h
    td1 + ts # 逐个相加$ K9 L1 t# G, x8 Q# e3 R
    Out[81]:
    + _; Y+ o! `1 t" tDatetimeIndex(['2020-01-02', '2020-01-04', '2020-01-06', '2020-01-08',6 L# F, T' g0 X1 u, D
                   '2020-01-10'],. a) y; u; J! a7 y& H3 s# O
                  dtype='datetime64[ns]', freq=None)
    $ N4 M4 P/ e/ B, K0 G
    ) H& T  M  F0 ~) Q* k' R' m1
    , {; x7 L( x4 s1 G6 K2
    4 {0 S+ C9 D, s3 ]& H3
    8 Z" @' s5 l4 {, _41 w: c4 n6 L, a: L6 |
    5
    6 D0 y/ e, Y; q& W6
    0 O6 w# S' r# T# G$ s7
    # g+ F/ H8 Q  j! x3 v! F8
    % p9 y; e5 a2 Q: o9
    9 Z, C. ^' ^& Z  b1 x! }) z10
    4 J6 y% w/ `, A  d11: R0 ^" G! F+ |+ x. H
    12% _  N1 ?( D2 g! I
    13
    6 N) v- Q8 X! p, r. K' l: x/ I# g14
    * }5 j) J* `# i/ J9 g8 ^15$ }* R0 n* J. F6 }5 n! J+ Y
    16
    % A! T+ @5 c& M; N' l173 e) A% c. |/ w& f* ~
    18
    7 U5 x6 H; q* g8 k9 Q1 f, s19& @0 @9 y% \& [$ e9 u: |
    206 g( u- y% V4 z7 i! [; N$ ~1 r! I
    21/ Z& N. R  S9 x( o+ @
    22. o' e  j! z5 }9 J. w
    23
    , a' U; u6 [2 W( E8 a  [24
    % e+ \/ W7 S0 R5 H, c) k+ G+ D$ y$ O25
    . K( @% [* A7 Y5 W6 X26+ O0 K: T/ V' [* {$ ?9 Z% I7 ?
    274 H0 K1 E$ a" \( d) ^( _
    28/ _% c. I1 I8 d# p% X
    10.4 日期偏置
    , k* d* s: ]; D7 c. \' I10.4.1 Offset对象6 C% _9 }) ?5 |% ^
      日期偏置是一种和日历相关的特殊时间差,例如回到第一节中的两个问题:如何求2020年9月第一个周一的日期,以及如何求2020年9月7日后的第30个工作日是哪一天。
    8 J, y9 n2 Q% @: p
    : C: \6 t$ Q; t5 ^0 E# nDateOffset 类有10个属性,假设s=pd.offsets.WeekOfMonth(week=0,weekday=0),则:
    ! s3 ?+ X+ i) Y* ]) K8 @+ C4 e& _( j
    s.base:<WeekOfMonth: week=0, weekday=0>,返回 n=1 且所有其他属性一样的副本
    2 l8 P4 a; l# h" s; j! R' ?s.kwds:{‘week’: 0, ‘weekday’: 0}
    ' t: r7 X3 y; }* u8 ls.wek/s.weekday:顾名思义* ^$ U9 F1 d( N4 \
    有14个方法,包括:5 h; }! r! [+ J6 }: r! N

    % L9 a" U& c0 Y" W9 O' rDateOffset.is_month_start、DateOffset.is_month_end、DateOffset.is_quarter_start、DateOffset.is_quarter_end、DateOffset.is_year_start、DateOffset.is_year_end等等。' Q0 M; O% o& v# p/ u
    pandas.tseries.offsets.WeekOfMonth(week,weekday):描述每月的日期,例如“每月第二周的星期二”。  b8 _- Q3 |% e; q

    ' p. e2 Z' X( i& b% x( ?; {有两个参数:* Y/ J* g( k/ ~& Z
    week:整型,表示一个月的第几周。例如 0 是一个月的第 1 周,1 是第 2 周,以此类推。
    8 j7 t8 V2 P0 Iweekday:整型,取值为[0,1,…6],表示周一到周日,默认取值为0(星期一)- Z0 n# A$ Q9 `3 x5 }
    pandas.tseries.offsets.BusinessDay(n):相当于pd.offsets.BDay(n),DateOffset 子类,表示可能的 n 个工作日。2 T, Z5 M" W# r2 j1 z. e  e

    6 u6 H' ?' j: m" R1 p( E% Spd.Timestamp('20200831') + pd.offsets.WeekOfMonth(week=0,weekday=0)5 t3 v( @& O: k8 C
    Out[82]: Timestamp('2020-09-07 00:00:00')
    3 f7 D9 ?9 o2 @5 Z( p' Y- T
    6 O, c% Y0 H" h4 b5 D3 ~' [9 ipd.Timestamp('20200907') + pd.offsets.BDay(30)3 e- g$ B! l4 Q; Y% p4 ^
    Out[83]: Timestamp('2020-10-19 00:00:00')8 ~6 C# \( l% ?' D, R
    1. w* o9 Z: u+ m1 E& C8 X# ^
    2
    $ x2 _9 B; x; W% B6 O5 d7 H1 G  I% F5 e3* J! @; ?7 P: }; T+ w. i/ Y
    4  n- u4 @# K, @9 |8 `, d
    5
    & c8 M! ~/ B" Z0 Y( W  从上面的例子中可以看到,Offset对象在pd.offsets中被定义。当使用+时获取离其最近的下一个日期,当使用-时获取离其最近的上一个日期:( u5 t" y) g; r; u0 I

    ! L+ I  h/ [- W& s! A( c5 W, F. w8 W  x( Rpd.Timestamp('20200831') - pd.offsets.WeekOfMonth(week=0,weekday=0)
    ( C# \1 o" M  [. uOut[84]: Timestamp('2020-08-03 00:00:00')
    ) V- m1 C) V$ X0 D$ K
    & s9 @) J5 z8 E, P+ mpd.Timestamp('20200907') - pd.offsets.BDay(30), d" J8 \8 x9 K6 j- R" t
    Out[85]: Timestamp('2020-07-27 00:00:00')
    9 Z& Z( b7 [1 Z, ~( v% q0 R/ l* u
    . r. r0 y8 j5 C6 R& K" e) |' @pd.Timestamp('20200907') + pd.offsets.MonthEnd()
    + N- }2 j3 ?9 o. R* U# Z, oOut[86]: Timestamp('2020-09-30 00:00:00')
    5 p( ~+ _5 A" ]! r  o0 j1
    : `9 g! A& N7 |. n9 M2# i& h7 I/ f2 @4 ^; \$ K
    3
    $ P4 k+ I: x. u) m5 W6 `4
    8 j2 t1 ]4 l' x2 t6 v59 d4 y  B' z! u! L' B; N8 M
    62 n* M; w; `+ \1 Z. p
    7
    " P9 R: N) q  \# b8
    9 ?+ Y4 q/ t" x# `- [3 w  常用的日期偏置如下可以查阅这里的DateOffset 文档描述。在文档罗列的Offset中,需要介绍一个特殊的Offset对象CDay。CDay 或 CustomBusinessDay 类提供了一个参数化的 BusinessDay 类,可用于创建自定义的工作日日历,该日历说明当地假期和当地周末惯例。
    $ T' l( G3 F6 Z- n  其中的holidays, weekmask参数能够分别对自定义的日期和星期进行过滤,前者传入了需要过滤的日期列表,后者传入的是三个字母的星期缩写构成的星期字符串,其作用是只保留字符串中出现的星期:
    : J& M% D: k5 x4 o* x3 W7 Y; p% o$ Y' n, ^2 I+ Y3 e, m/ @
    my_filter = pd.offsets.CDay(n=1,weekmask='Wed Fri',holidays=['20200109'])
    3 l2 N% f9 c$ R. }2 r1 {dr = pd.date_range('20200108', '20200111')
    1 {6 E4 o/ r7 x. K4 W  F
    5 M' `6 p, h' R* W& |dr.to_series().dt.dayofweek
    9 B6 \4 ]) Y0 h0 t+ d' h( _3 `1 yOut[89]: ' d$ @5 d, {) r8 J
    2020-01-08    2* J% e& b; n6 a: p" d
    2020-01-09    3
    1 z* [% E+ T6 V; G$ |! [  N* H2020-01-10    48 {$ E' ~1 J" R7 e" _* {
    2020-01-11    5
    : S9 d  n' O, aFreq: D, dtype: int64
    * _' q+ g7 p0 i5 O0 F; X/ T/ R: j+ |0 k
    [i + my_filter for i in dr]
    9 W5 a- C: y7 F4 R- K2 VOut[90]:
    ' n& D5 @+ o% L+ J, `[Timestamp('2020-01-10 00:00:00'),
    4 S3 U4 h" ~9 A- o1 J. x Timestamp('2020-01-10 00:00:00'),
    5 R: ~& b8 E% D Timestamp('2020-01-15 00:00:00'),  J3 z) J. }* t+ K
    Timestamp('2020-01-15 00:00:00')]
    2 W) z: C4 h- k5 K# T# t4 m8 I4 @3 Z& F( V
    1+ _3 S* c' f* L
    2. ~8 g4 E2 w" E3 G" O0 L8 O
    3
    3 B  p( R" E$ w; m9 \4
    4 b: z  K9 g6 `& o9 m/ |5
    * \8 I) \- A. p6 L63 i% r0 X- R, v
    7
    4 W7 a7 n, f* K% U4 @3 p$ Z" m8
    1 S; W: T8 ^: r  P# a97 L$ {7 D) _3 f
    10
    - i% G0 s) ~3 _! s11
    " {4 Z1 L- N9 E% Q* f5 @12
    7 _4 J& [. j8 u  C+ u* {( n13
    4 a4 p+ g& R0 ~$ i5 }6 e$ X# Y" G( `14# X4 J) k  s* X2 C/ F2 g
    152 S9 W$ i" Z5 }
    16
    . P0 ~. d! Z  Y8 w$ f6 p5 ]* _- ^  S; {176 G: ]6 t9 v  W& @5 A( b: `/ f
      上面的例子中,n表示增加一天CDay,dr中的第一天为20200108,但由于下一天20200109被排除了,并且20200110是合法的周五,因此转为20200110,其他后面的日期处理类似。1 d8 n$ `# F0 @; E

    ; D3 q9 ?) K- s' m/ I$ c# I【CAUTION】不要使用部分Offset. N; A0 Z9 v8 c% N2 \1 e
    在当前版本下由于一些 bug ,不要使用 Day 级别以下的 Offset 对象,比如 Hour, Second 等,请使用对应的 Timedelta 对象来代替。
    & H& A6 ]/ b7 j% z% a1 ]
      Z/ R* H' }+ P% Q, a+ V10.4.2 偏置字符串
    : E+ P  }" J1 F3 D; L( o  前面提到了关于date_range的freq取值可用Offset对象,同时在pandas中几乎每一个Offset对象绑定了日期偏置字符串(frequencies strings/offset aliases),可以指定Offset对应的字符串来替代使用。下面举一些常见的例子。+ f' |. m5 y' X; f1 W3 ~& v
    ' B% h+ J0 D- }: w$ P1 c
      Offset aliases:pd.date_range函数中的freq参数,为常见时间序列频率提供了许多字符串别名。 也称为偏移别名Offset aliases。偏移别名列表点此参看(大概27个)。
    / I4 G1 W# h8 l4 c* R7 `  S1 p% Z. E! E* r' J
    pd.date_range('20200101','20200331', freq='MS') # 月初
    3 ?# e( N$ ?5 k  X+ {Out[91]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')
    6 p% k/ z! ]5 }3 w
    $ p7 N0 }/ R; _# G( |& \pd.date_range('20200101','20200331', freq='M') # 月末
    . t, o( K) V! W( o. JOut[92]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')
    , ]  B. s+ F* c" B4 C( W- J6 a
    ; p9 {8 L, }% r& Npd.date_range('20200101','20200110', freq='B') # 工作日% E1 D, F$ B: O# [- D
    Out[93]: & K0 g, q3 F" {% m! A* g% [, O
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
    + }2 p" \0 i1 E& d; U               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],# \  g, A" @, H
                  dtype='datetime64[ns]', freq='B')# D% C# O6 ~$ P1 P
    ! l0 x6 T5 F8 n9 k2 `' H# {( O7 O
    pd.date_range('20200101','20200201', freq='W-MON') # 周一
    0 N- I  {: h* t- EOut[94]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='W-MON'); S) \1 f2 I4 k7 z) l

    ( D" v1 S. |8 L$ S7 y: f' Hpd.date_range('20200101','20200201',
    ' k: l% l! t4 s0 |              freq='WOM-1MON') # 每月第一个周一0 d/ G8 }1 K  D

      _( g4 C5 x! a" ~& bOut[95]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')) B" M  d% u; K6 ^6 w: G/ E
    # ]8 N' B! T# _" G! x$ P- ^9 E
    1
    9 J+ ?  P* w+ N, I9 i# L2
    7 H8 A- r& S8 D5 g3
    3 C% Q- j! w  c! w4
    . L! C" {  r. _' {4 ]9 q3 b$ b5
    ! |% R) p4 b) P6 \66 t, k+ n* o. k- x6 I
    7
    ' u  b. N' r6 {8
    % A9 g! W5 C' U8 q: a9
    6 p6 W, f- _) J1 l- G102 s; ?& s3 k: U$ n' u! N9 H$ p5 x
    111 B, X2 M* Q9 O6 t7 N- X! ^1 Q6 e
    12
    5 j- |+ S, W& `0 w9 h# |0 \! n2 `13* T( b9 j4 L7 o
    144 c) n, V+ g- I5 k: P6 q: V
    15: m, E% \# w* u7 x& i
    16( U2 M: \8 t' d; D
    17
    5 P: m# _6 N) \) U7 ]; c* [18
    $ h4 H# P3 z8 V2 v7 R7 b199 G2 z$ V/ g2 o2 e, v3 u
    上面的这些字符串,等价于使用如下的 Offset 对象:
    ' R: t) W* k5 k% O, D, M4 D+ f) @
    % e1 B0 D- O" q% gpd.date_range('20200101','20200331',7 P7 O# U% a9 I
                  freq=pd.offsets.MonthBegin())' B% G. \( s1 M0 P! d: T3 p6 z& l* C
    $ _' {  O# ^! e6 ^9 I
    Out[96]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')- K# \5 F. x; n
    ; x- e0 {$ }( s  b" b6 Y" K, @
    pd.date_range('20200101','20200331',% B6 `8 m  y$ S$ R' S
                  freq=pd.offsets.MonthEnd())
    ( J6 Y0 R$ L2 E9 J  i) F/ P! H; I: b
      L; N/ Z3 D3 O1 e/ d+ N, [  a4 N* WOut[97]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')1 T8 r+ h+ d4 q( r

    7 r4 E! v" y5 ^. opd.date_range('20200101','20200110', freq=pd.offsets.BDay())
    ! g! f3 \& D$ I' W# y+ j- jOut[98]: ! `- j" z' R& a9 Y
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
    3 p# w; f3 s! V+ C) N4 Z               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],: u% d+ G9 p7 K
                  dtype='datetime64[ns]', freq='B')7 H6 P  _: t% U2 l% L+ C
    # I, y6 x" h8 ~, T% R
    pd.date_range('20200101','20200201',  [( }: O, K! j% v* d
                  freq=pd.offsets.CDay(weekmask='Mon'))
    4 |' o8 q+ |% @9 R) A) N6 e' h- {7 N7 e- `+ X, \. y) R
    Out[99]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='C')8 D" z- P$ f) C  I" m4 T/ ]

    2 }/ q" s% J" x. C+ i. U1 ^  j. p. u; t) ipd.date_range('20200101','20200201',
    7 B+ W3 `* @) D. _              freq=pd.offsets.WeekOfMonth(week=0,weekday=0))
    ) T. s  J. B0 q8 \  H  }2 d+ A& x. y
    Out[100]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')
    1 N7 E* \* \4 W' L4 J6 p$ E
    ( o% B( g! k7 [' H4 M1/ `8 {  S( V! p+ |( F+ m  ^
    23 A/ B! o/ q* r8 c- G
    3" i. G# N: }) m. T. M2 m
    4
    % p& }8 O6 N5 x+ @2 r. W5. y% T* B5 t! O( P( c! y
    67 L# V% [3 l3 o, Z9 }9 h1 m. W& W( S
    7
    * ?1 D6 _% ?5 r4 \8
    9 I& w! ]( r* b  a  [3 C3 ^3 @( T) _9! a% O3 R+ `% Q0 \# V0 o( k0 O5 }: Q) g
    101 j8 K  A$ z* J, `( s$ [
    11
    2 B+ L% w8 V2 [+ T3 O/ l$ m' G12
    + H7 m5 Q0 O) G9 n) @- \& x9 Y0 }13
    % e# g/ B. A3 E14% l7 k2 r0 S. z" ]6 H
    15
    , S; a9 p, w- i1 B) q16
    0 z% b- T% J& g6 W" D2 P17
    4 A3 U2 o- J9 y0 k+ ^6 m, e18/ ?% z7 _0 o8 x! i+ G
    190 }, F- Y, n' T, s; H/ {% q
    20  a& x! p% J8 ?2 L. C5 m2 ]4 R
    21
    # e; f4 B5 z1 }: o, @$ H22. A1 j/ _7 ^" s3 H
    234 @9 x' g4 w& k/ g
    24
    0 M% c6 c, _2 g25
    ) L$ |6 F3 @! C" h, L3 P【CAUTION】关于时区问题的说明
    , G5 W# V) F$ f3 I. `; P  B. E* d  各类时间对象的开发,除了使用python内置的datetime模块,pandas还利用了dateutil模块,很大一部分是为了处理时区问题。总所周知,我国是没有夏令时调整时间一说的,但有些国家会有这种做法,导致了相对而言一天里可能会有23/24/25个小时,也就是relativedelta,这使得Offset对象和Timedelta对象有了对同一问题处理产生不同结果的现象,其中的规则也较为复杂,官方文档的写法存在部分描述错误,并且难以对描述做出统一修正,因为牵涉到了Offset相关的很多组件。因此,本教程完全不考虑时区处理,如果对时区处理的时间偏置有兴趣了解讨论,可以联系我或者参见这里的讨论。& e8 n6 r/ X3 @

    : [& D( g* {' l10.5、时序中的滑窗与分组
    + ^: E$ ]5 b; @10.5.1 滑动窗口
      b/ G/ {; [* [7 I6 R  所谓时序的滑窗函数,即把滑动窗口windows用freq关键词代替,下面给出一个具体的应用案例:在股票市场中有一个指标为BOLL指标,它由中轨线、上轨线、下轨线这三根线构成,具体的计算方法分别是N日均值线、N日均值加两倍N日标准差线、N日均值减两倍N日标准差线。利用rolling对象计算N=30的BOLL指标可以如下写出:% o" V+ b$ V& ~& t; `

      v4 e0 l3 L0 X9 vimport matplotlib.pyplot as plt0 b1 \7 `) H, G# f
    idx = pd.date_range('20200101', '20201231', freq='B')& ~+ c7 ~+ c# {7 s: r7 X
    np.random.seed(2020)
    9 f2 b* n( }( J0 X6 Q9 @* C7 g& |5 ^
    4 v$ r" V+ G) bdata = np.random.randint(-1,2,len(idx)).cumsum() # 随机游动构造模拟序列,cumsum表示累加
    2 c0 M: h4 D6 {. f1 c1 xs = pd.Series(data,index=idx)
    - b/ g9 c% b* M7 is.head()
    7 i' w; N6 ^! u0 N0 g2 L: GOut[106]: % g) U# {3 a0 M5 ^: J$ _% e
    2020-01-01   -1
    8 n* `! \) ~8 |% E( S2 X# v2020-01-02   -2; ]6 a& m% L! D8 |- Z
    2020-01-03   -1
    ( i1 k6 w, X! w6 }0 p2020-01-06   -12 L8 E) F4 Y& B, @: w. W3 k$ o
    2020-01-07   -2
    8 a; M0 M, \' z0 s1 fFreq: B, dtype: int321 O* q6 a& M; o1 \7 }( ^; D
    r = s.rolling('30D')# rolling可以指定freq或者offset对象
    . }3 O1 z& A' j
    2 W# c1 B& U( h2 Tplt.plot(s) # 蓝色线* _& g. [8 V7 g
    Out[108]: [<matplotlib.lines.Line2D at 0x2116d887eb0>]. X: T2 N, d8 x
    plt.title('BOLL LINES')
    ; U3 w9 H2 G" A* \1 P# ?Out[109]: Text(0.5, 1.0, 'BOLL LINES'): I& r6 l. g; R7 ]) `

      B7 C+ t7 ]  J. T  |* I& \plt.plot(r.mean()) #橙色线
    3 N4 w5 T% ~: @* z3 t* ZOut[110]: [<matplotlib.lines.Line2D at 0x2116d8eeb80>]0 h$ r5 L9 r) T
    ' z; q- j) X8 L+ D- a; L* t  ?' m) W
    plt.plot(r.mean()+r.std()*2) # 绿色线/ R% `9 V: L/ j+ N
    Out[111]: [<matplotlib.lines.Line2D at 0x2116d87efa0>]+ [, B# r5 z5 H, d7 Q

    / s% d$ u5 V0 b, O/ F; dplt.plot(r.mean()-r.std()*2) # 红色线
    % p% `' l. u: f, [. ]3 I% oOut[112]: [<matplotlib.lines.Line2D at 0x2116d90d2e0>]
    * ]: f$ |8 C2 B. ^/ K
    ) Z+ P( h& h6 ]9 p1 J1
      T8 r) ^! j+ ~2$ O6 O6 f$ _+ D% q
    3
    , P- c1 M, C* n/ a4+ ]" \5 i8 g+ ?
    5+ a- q6 c/ T) n
    6
    3 N, N' f  j2 c6 I7
    8 u( ~! J2 a0 a+ j) ]+ n; C8, `+ w& E: C  }& ]8 d  c
    9" z8 [& b0 r0 ~$ L
    10
    # g) R3 x( {6 E" ~' A11
    % O- Z! N2 H! Y( s( ?12' H* @8 h7 v  s3 o9 @8 S6 l
    13
    ( E) }- z, Q" a" Q14
    : ^  Q% ~. u4 r  Z15
    1 b# O" _% h& Q0 J16
    ) G' h$ h" S- b* k4 h8 Q174 l- G! x0 m6 X6 z
    18
    ! e; T- L$ d) w) Q8 x19
      q$ @9 ~- {9 T8 t0 X20
    : T" W  q  A/ `& E3 g( m! I: ?21
    $ Y1 f+ e% w  i22; G: y/ N! ~; n
    239 _3 N2 S2 e$ K! w# O: A3 {
    24
    * ?" S% |' Z, U" H/ `25
    ( B( I& S( G0 h' v9 z26
    # z; ?; G: r* s  k% z6 o8 p$ Q27! |- `; U1 b& S! n
    28, U- D  L; d, R" ~
    290 |8 w7 E9 m; {5 i: t. m( E; ^; o
    ' r+ Z" U/ L# Q. y
       这里需要注意的是,pandas没有实现非固定采样频率的时间序列滑窗,及此时无法通过传入freq字段来得到滑窗结果。例如统计近7个工作日的交易总额。此时可以通过传入多个函数的组合来实现此功能。
    ' _" E' Z5 l+ j' R& H   首先选出所有工作日,接着用普通滑窗进行7日滑窗加和,最后用reindex()恢复索引,对于双休日使用前一个工作日的结果进行填充。% X$ s  }% a5 f# Q4 y
    % P" C4 c* h! h4 v8 H; u+ H
    select_bday=s[~s.index.to_series().dt.dayofweek.isin([5,6])]
    + c2 A( I6 ?7 r3 Z9 p6 V& mbday_sum=select_bday.rolling(7,min_periods=1).sum()
    & D  o' P0 t, cresult=bday_sum.reindex().ffill()& o- a  c. D# }1 t
    result
    9 y; }$ u" `( V
    / E' e1 q3 i1 V, b2020-01-01     -1.0
      _1 N+ I! G2 E' q6 J0 y4 f2020-01-02     -3.0+ d: m+ d$ X, a" }" F- o
    2020-01-03     -4.0* m8 j0 y0 ^( c  v! Z' a; @
    2020-01-06     -5.0
    $ K1 H/ J* b6 j0 k9 {& p2020-01-07     -7.0
    - `2 K( j0 C: d9 V              ...  & y/ {/ y2 W7 Y. L
    2020-12-25    136.0
    : O* ^0 V7 Q9 ^7 s: Q4 B" p2020-12-28    133.01 W# C  K: Y6 w
    2020-12-29    131.0
    : r6 R# M. N1 e$ k2 b! k0 H' x$ V2020-12-30    130.0
    ' K& z% Z+ v% \. V! s# A. |3 {2020-12-31    128.0
    & x& ^6 S& h' G& y, wFreq: B, Length: 262, dtype: float64
    6 u) p2 j( }7 @: q* W6 r! {( v$ k% z6 P- r9 }* _. Y
    1# Y* [4 }! w3 i" v" F4 {6 o" Q
    2
      W6 b+ D0 B. V- c/ A( [3
    ; z* y6 S4 c- k. G43 O. v0 z$ f1 j* T$ M  e, M, {$ I
    5" w. U9 p  L, T4 n+ b
    6
    2 w$ X* {" y, k  `3 x7 ~7
    5 b$ }& o# r- u5 }8
    ! i" Z: U7 c, j' ]" b/ x* q9
    2 P& r% f7 d- w% h, M& M1 R3 u10
    ) Q  y: C. w. V4 ~" `: M. g9 u119 i1 o) |) v4 A- @5 u4 d) i
    12
    1 q$ n: Z2 J, J- l# T138 }/ S& D* {3 k8 V0 O0 ^
    14" k/ g6 B; u- Q' {' h
    155 n: f& J% L/ ?2 R
    16: ~" }! _2 Y8 h8 D
    17: _* s8 ^3 W/ \# I
      shift, diff, pct_change 是一组类滑窗函数,它们的公共参数为 periods=n ,默认为1,分别表示取向前第 n 个元素的值、与向前第 n 个元素做差(与 Numpy 中不同,后者表示 n 阶差分)、与向前第 n 个元素相比计算增长率。这里的 n 可以为负,表示反方向的类似操作。. ~0 N1 j/ ]- B

    2 [2 x" _; W. K2 B3 ?* a  h  对于shift函数而言,作用在datetime64为索引(不是value)的序列上时,可以指定freq单位进行滑动:+ P7 \6 H$ |% F7 G0 Z
    ; R4 u4 B# w1 m; F* w
    s.shift(freq='50D').head()
    8 f1 t  E$ X- u* g- Q& S* QOut[113]: 7 n: S: p# R; t4 s
    2020-02-20   -1  d6 V3 I! _$ ]4 k
    2020-02-21   -2% h5 @6 E' M; b( S  W! C2 g0 I3 `  u
    2020-02-22   -14 h7 E. J1 P9 f
    2020-02-25   -1
      M6 F, G# F) t& ?9 h7 h8 D2020-02-26   -2
    9 t+ {: w: I9 }# j" o& T# g/ A4 {dtype: int32* }) [) ]  O8 f  M$ P
    1! J! [6 ^( E# ]9 d, k+ U" R
    2
    8 v6 g) P& F2 A7 g! z0 m% l35 I0 R4 m$ I# N* X- E# r& p
    4  G/ q9 U4 n2 c7 L! C  I
    5
    ) Q% u  v- E, G- z& k+ J6 [0 Z6
    1 E' C$ d  Q; A0 @1 @1 G  I8 E$ I7
    0 N  R- M) G8 L* B0 i' S8
    5 d1 p2 e! v1 _% \% h) z  另外,datetime64[ns]的序列进行diff(前后做差)后就能够得到timedelta64[ns]的序列,这能够使用户方便地观察有序时间序列的间隔:
    " ]9 A4 Y) ?/ X: s# J" @; c+ P: ^, N* ]3 W  Z+ f
    my_series = pd.Series(s.index)& F! @; x" f$ G; |5 K7 n
    my_series.head()8 I- c! Z4 B- E- o9 Y* V
    Out[115]: 4 j+ W" a; T$ U# H% x* L
    0   2020-01-01
    2 s9 O6 n3 w$ Z8 W1   2020-01-022 \* C; x& W3 U) ]! D
    2   2020-01-033 }+ ^) a1 q. L9 o
    3   2020-01-06+ G9 {6 {- f; o& _
    4   2020-01-07
    ( B4 W. w2 p4 f3 I2 E+ @& u8 vdtype: datetime64[ns]  v" N1 I: e; W. K1 G' D9 F4 q8 Z4 i

    3 l  ?' t) S! q" ?7 Z+ Ymy_series.diff(1).head()/ n  R9 ?& }" X- q/ U0 a* @
    Out[116]:
    & p) D5 q* V( {3 f0      NaT; q& K. z( |, y
    1   1 days) ]' B4 U" r+ Y$ j9 h
    2   1 days
    , |: J1 x' R1 O/ U3 D3   3 days
    , N+ a1 J& m% R4   1 days/ r9 [% j1 x2 J( Q7 b  j6 s
    dtype: timedelta64[ns]. G$ q1 u# H2 u' U
    8 N7 R/ }# D5 }- y6 t+ c
    10 B: D7 b9 [# j
    2
    ' A- T* w: c! A) M7 S6 T6 ^3
    , m0 T: U  w- v5 _& w  G# d4
    2 T  J- P* W* v) K5/ \; A- j. [  j0 b1 \4 w& Z/ o! l* p
    64 K, o' V" g) t; z9 |5 F$ x' m. c% Z
    7
      z) A' L4 R3 o8
    + @! a+ {- B7 j9
    6 _1 F/ `' `# L+ A8 z  P2 g3 f2 K103 \/ E' Z* g7 ?8 ~6 x, S- u
    11
    5 c2 a) Z/ i' d12; k2 }% b6 C1 L% S! D* o% ?
    13
    " K; G" _& \% O- r* q/ l& P14# }  N& |. Z! f; a% q  U& C. D
    15  K3 c7 g& U6 `# W2 V$ P
    161 I$ u* z% M! `% L! o4 I
    17; f, \0 ?. H4 R
    18& v0 D  T5 S+ P+ A- r
    10.5.2 重采样
    + B6 r: o' \1 v& h3 z8 x  U) ~  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)/ x2 m5 Z: @; t$ R% A0 P5 `7 X
    常用参数有:8 q7 \3 H$ q) u5 J0 P3 {3 X
    & _' e& b7 L! J) b' B
    rule:DateOffset, Timedelta or str类型。表示偏移量字符串或对象! R) w  A5 V) g: ?7 h
    axis:{0 or ‘index’, 1 or ‘columns’}, default 0。使用哪个轴进行上采样或下采样
    / f; t( {8 M0 a* k2 \closed:{‘right’, ‘left’},默认None。表示bin 区间的哪一侧是闭合的。所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。
    8 F# i' M/ S6 P4 V; k( llabel:{‘right’, ‘left’}, 默认 None。hich bin edge label to label bucket with,所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。( _) U/ r9 D- n- Y2 o  w3 o. j+ `
    convention{:‘start’, ‘end’, ‘s’, ‘e’}, default ‘start’。仅针对 PeriodIndex,控制是使用rule的开始还是结尾。( b- g) i9 W/ W8 o( e4 x
    on:字符串类型,可选。对于 DataFrame,使用列而不是索引进行重采样。列必须类似于日期时间。
    3 z9 o/ _1 Y4 jlevel:str 或 int,可选表示多重索引MultiIndex的级别,这个级别的索引必须类似于日期时间。
    + u- t0 m9 k* q: Z8 ~origin参数有5种取值:
    ; ]. i" z0 V9 s‘epoch’:从 1970-01-01开始算起7 M7 E+ _& u/ }0 L6 n
    ‘start’:原点是时间序列的第一个值. m0 Y4 W, V6 {1 `% N. p% u% T. I
    ‘start_day’:默认值,表示原点是时间序列第一天的午夜。- H, H8 ^) ^" M+ E2 g. f
    'end':原点是时间序列的最后一个值(1.3.0版本才有)2 }5 x0 Z0 B! x8 i  |/ O
    ‘end_day’:原点是序列最后一天的午夜(1.3.0版本才有)
    - P& w# J/ H- U* Poffset:Timedelta 或 str,默认为 None,表示对时间原点的偏移量,很有用。  T2 v7 Y& ^( U
      closed和计算有关,label和显示有关,closed才有开闭。" i4 \, x6 Y0 N) E" p0 {5 e) ]3 j) U
      label指这个区间值算出来了,索引放区间的左端点还是右端点,closed是指算的时候左端点或右端点是不是包含。
    - s( ?: Q2 j9 N$ \5 \3 a7 }7 F' B
    & p3 t+ Q5 H5 ]0 ^6 ]5 m重采样对象resample和第四章中分组对象groupby的用法类似,resample是针对时间序列的分组计算而设计的分组对象。例如,对上面的序列计算每10天的均值:" o" k' V6 f+ [  V: S) n
    s.resample('10D').mean().head()  J) D9 `8 A+ S/ ^) U
    Out[117]:
    - s& @  U4 Y2 s' Y! H  V2020-01-01   -2.000000/ l7 T/ K! J( x$ b
    2020-01-11   -3.166667
    " h& J" K, X( r+ A, S7 f2020-01-21   -3.625000
    & T1 Z2 t- `3 v: I7 D' {2020-01-31   -4.000000+ q0 h4 V# P$ _, b$ k
    2020-02-10   -0.375000
    3 v2 w" ^$ l# a0 Y' ^" M# l! oFreq: 10D, dtype: float64% j4 S) Y+ |& T1 c' V6 g
    1
    6 z& j( x, k2 P- N, Y# [; [23 p2 I5 b1 K' C% k8 }
    3
    # O) `; b4 U! l1 Y' M: d+ _4+ m& J9 h5 \! o5 ?, W
    5" \7 l. J/ a0 {2 }" ~1 |
    6# C5 j# q8 y# S2 B3 h6 e
    7
    % I3 P1 i) @0 w1 ~9 |8
    ! I& d* i3 I  g可以通过apply方法自定义处理函数:
    + ]# S: M- H1 A+ S( Is.resample('10D').apply(lambda x:x.max()-x.min()).head() # 极差
    0 b( I/ u" K/ V4 _2 s8 ~0 X7 ^4 v$ O, @
    Out[118]:
    : q; G, `( X% n" a" O' _9 |2020-01-01    3* j: e+ h. \9 R9 |3 a' d
    2020-01-11    4
    : S: w) q. B& Q& ]2020-01-21    45 z6 v  Z- N5 @" c, b
    2020-01-31    2
    5 \) N. k9 e7 ?' G7 |- m0 x2020-02-10    4& b7 l, J3 P; V8 h* f
    Freq: 10D, dtype: int32
    ; M1 Y! |/ Z+ }8 h: M! m7 Q# V! Z/ m1
    ) T! v6 K8 K- j8 Q8 S3 T21 [; Q/ O" g# e& z. q
    3! u) ]: J1 l" q0 x
    49 G2 n. D. |" m- v: D  h
    5
    ) F! s; r9 N$ D, z8 p* G0 ^3 Z6
    7 Q  v2 G( R: _. D/ v5 F) M7/ `. W1 i  E$ Z: h
    8
    # H6 _2 G% S" T- s; d% Z99 s2 {- Z( P" S# M: \, c
      在resample中要特别注意组边界值的处理情况,默认情况下起始值的计算方法是从最小值时间戳对应日期的午夜00:00:00开始增加freq,直到不超过该最小时间戳的最大时间戳,由此对应的时间戳为起始值,然后每次累加freq参数作为分割结点进行分组,区间情况为左闭右开。下面构造一个不均匀的例子:- h4 \6 k7 Z2 ^! I( u" @

    3 n, D0 H! E8 i  A- Pidx = pd.date_range('20200101 8:26:35', '20200101 9:31:58', freq='77s')& x. I/ U: G* w) P5 l* v9 N# i
    data = np.random.randint(-1,2,len(idx)).cumsum()8 y* H( s4 R. v  T; m
    s = pd.Series(data,index=idx)
    6 }  L! v# G& {7 ]s.head()
    7 r: H7 |% U, U) {( i8 @# S0 d9 u0 v3 Q' K/ c( d
    Out[122]: ( J! b, h/ @) C6 O5 P  I, X( @
    2020-01-01 08:26:35   -1& E9 m: I1 z0 h+ b. E
    2020-01-01 08:27:52   -1; `# N0 x; w: f2 y0 F
    2020-01-01 08:29:09   -2+ C$ w: O. j+ E0 o0 K- `1 V6 @
    2020-01-01 08:30:26   -3
    * |, R4 d4 ^8 O- O2020-01-01 08:31:43   -4
    , t# k! B( x$ u1 X2 Z1 v9 p. ^Freq: 77S, dtype: int32
    ) j' t3 k. W; W" w9 B1
    / y) W* A% s, M' R6 j5 g  s2& j1 ]- B( ]7 X* Z* `
    3- ?4 n/ K3 S1 i) r: B
    4
    ( F, W) H( V* w5 c; F" C5
    1 v, p. i7 j: h' @6
    2 C, Q1 T$ c" O: J# q( m3 J' h76 ]6 _0 b3 w/ H8 U2 T
    8
    " j. c2 F( n- b/ a# [9
    , l* Q4 d& c) w4 [0 m% c2 S10. l6 p% K6 w+ c. S$ K) ^- C
    114 p: R2 g4 y5 S, _+ v! N
    12
    % _% \( f. c, d4 K2 }  下面对应的第一个组起始值为08:24:00,其是从当天0点增加72个freq=7 min得到的,如果再增加一个freq则超出了序列的最小时间戳08:26:35:
    - |" F, B. J" v; B$ D
    ) X$ Z' Y6 q* ~5 s0 H, G* ns.resample('7min').mean().head()
    ! T2 R- ?8 y$ i5 i  zOut[123]: . W5 a7 o9 m' ~4 [0 t2 _
    2020-01-01 08:24:00   -1.750000  # 起始值,终点值包含最后一个值
    , @% F  E/ Q+ s1 j2020-01-01 08:31:00   -2.600000: {# L: E1 V# D# R" u$ W+ R- c
    2020-01-01 08:38:00   -2.166667
    * A. l% `6 ^7 X, c0 x& ?2020-01-01 08:45:00    0.200000
    7 O# l  [- I3 G2020-01-01 08:52:00    2.8333335 n' I2 v( P+ b2 k
    Freq: 7T, dtype: float64; ~8 x! L% b2 h1 E* V0 q7 b
    1; @- I3 ^* h* P+ H! v5 L, ?
    2' T6 I* |$ T4 i& p& ]
    35 O- O7 U5 [2 P2 i
    4% v! z6 y4 Z$ i/ ~# y( V
    5
    % H( O9 b% e- V- R+ ?# ^6
    6 L$ @) I8 r  ~/ M4 j7& F1 `. ?6 [( q+ `- X6 S) u7 k
    83 ]* U4 H; ]. c6 z$ |* P
      有时候,用户希望从序列的最小时间戳开始依次增加freq进行分组,此时可以指定origin参数为start:
    4 R  Y0 r3 Z4 N, {. X( n; n- h( T' j4 p' b1 J
    s.resample('7min', origin='start').mean().head()
    4 K  U- p3 v+ T( VOut[124]:
    ! C4 c' ]! l7 l  a! W2020-01-01 08:26:35   -2.333333+ S, D2 b- r6 [& x2 q7 }- R& g9 Z
    2020-01-01 08:33:35   -2.400000
    : N: z- v9 p0 m" h3 L9 z2020-01-01 08:40:35   -1.333333# s0 c, K8 C( `* ]. b. j
    2020-01-01 08:47:35    1.200000  w+ R) h8 F* s% Y3 i
    2020-01-01 08:54:35    3.1666670 l& C, m: H( f$ ~2 ^
    Freq: 7T, dtype: float64
    ( t/ K, p' l9 I; r5 ~3 f- D1
    # U  y. _6 R( Z/ B3 p  P5 O26 q0 ]3 }0 v1 y/ j# s: m& W( _
    3# J* F- F" ^9 W* S  j# p
    4
    ( O; H; U# F% M# y( T# R6 K, a  R5* {+ I  A2 C& @: [
    6
    ) r& c/ n' [& p9 p) e- s) c5 n; E7
    5 J4 S& `$ D9 o, c2 `$ x8
    $ B' s* Q9 T" @4 G/ M( S- l  在返回值中,要注意索引一般是取组的第一个时间戳,但M, A, Q, BM, BA, BQ, W这七个是取对应区间的最后一个时间戳。如果想要得到正常索引,用’MS’就行。, a& s4 s7 l2 h5 q" r6 A% i( Z9 v

    , _3 \3 i5 G$ W4 B: F. K" Ms = pd.Series(np.random.randint(2,size=366),: R, [3 I$ O' D7 z" J& R3 O6 G
                  index=pd.date_range('2020-01-01',
    2 j& J6 j& C7 B                                  '2020-12-31'))
    , ]6 e1 }: b4 {8 O+ O7 d* W/ n2 k
    9 k# a! l) h( I- u$ B
    s.resample('M').mean().head()
    9 ?8 l) o# X; }+ G2 @7 COut[126]:
    : A$ t8 h) ]6 H. @8 ?9 c2020-01-31    0.451613, h9 Q1 B( Q2 P
    2020-02-29    0.448276
    . }8 s0 G  n3 F0 Z& w2 @2020-03-31    0.516129
    & j4 h+ Y9 v. {  d# H) R$ [2020-04-30    0.566667) q- m9 v3 b! g2 d% `# m, m, _
    2020-05-31    0.451613- b0 m6 n: @8 H' T7 d
    Freq: M, dtype: float64# ^( @9 _9 Q: x( U& Y- G/ j
    : O. p; }6 y9 `( F$ S' s
    s.resample('MS').mean().head() # 结果一样,但索引是跟正常一样) b" s5 H* _8 a& ~  H" r
    Out[127]: 0 Y! z, V/ o. p+ |9 q  c
    2020-01-01    0.451613
    & t1 @: i% @8 g1 Y# W8 W2 y5 x( }2020-02-01    0.4482766 G5 H& r. w/ l2 W8 M: J
    2020-03-01    0.5161293 Y0 i; T* K9 F: ]
    2020-04-01    0.5666676 Y- c' R7 l! T% B, z; [( g+ R! |4 m" ]
    2020-05-01    0.451613% `; k8 d+ r+ l6 i8 t
    Freq: MS, dtype: float64( C( Y7 q1 o  p6 m& b6 G4 @

    - f, k' J- Q4 s5 _7 C. q, J0 c1( M5 B4 ]  ]/ P, G' P
    20 Q& d$ b$ Z+ k% s7 w2 f
    3
    1 Z5 a) L) D" s! g4
    0 M0 Y0 O; F6 h1 E5, v' s7 J' k. R& _' W! t7 K4 Y, C
    6
    2 t$ G9 y' |  i7! @6 k8 F- s, q  K& q
    8$ g1 H+ x" Q; H# N" y
    9
    7 G0 j+ U6 O' k- Z10
    7 x. Q+ l$ X, ?9 \+ w117 k0 D/ {* z/ A; m; v7 o, B
    12/ B& x9 t$ t1 r( ?/ g# E" {1 q
    13) k$ M8 W5 N9 @7 i" {' O7 k
    14, ^, ~# Y3 I. [) @2 f
    15
    0 |5 a: Y# `+ E4 k16
    0 ]0 Q' N" r8 s" M$ I5 {9 f* M17" A7 R* H% F4 `! n( y! e- w
    18
    6 [( {" L. F/ X, a# W; L191 V) g) h/ o) z& O8 E# u
    20  N0 i0 \" T. u1 z1 F
    210 @, D, o! }! W4 C) T! v7 h
    22- N% r+ c% f' {& i# W7 Z
    对于 DataFrame 对象,关键字 on 可用于指定列而不是索引以进行重采样:
    + e7 |: W1 E0 d! {8 S- ^d = {'price': [10, 11, 9, 13, 14, 18, 17, 19],  ~' [5 |& `; @& b6 F* T
         'volume': [50, 60, 40, 100, 50, 100, 40, 50]}$ S% L3 m( d' E& O6 N
    df = pd.DataFrame(d)
    ; @1 Y8 y% D* }/ Ydf['week_starting'] = pd.date_range('01/01/2018',
    & p) ]. Y) O" j0 V# O: V                                    periods=8,
    7 @) k, G1 m  j) B' X                                    freq='W')3 i# I- k- S# j- Q, C, @3 c0 M2 I
    df
    * Z: l/ }; c( d+ H) M# P   price  volume week_starting
    8 g0 @9 [  a& F; S& s- R4 H3 j0     10      50    2018-01-07
    . u' m% }, {' W7 |; u4 r1     11      60    2018-01-14
    ( O  p' ~; D& m# m3 g3 ?5 o2      9      40    2018-01-21
    + K# K4 Z4 e, ]' }. r7 L3     13     100    2018-01-28% y2 M! V7 h, o. r
    4     14      50    2018-02-04
    1 {6 C& z* _+ r2 I8 Y+ z  K5     18     100    2018-02-11
    ' B* z- p' c" d% U; e6     17      40    2018-02-18: B/ W0 N! `0 a5 ~+ I
    7     19      50    2018-02-25
    3 k+ j: _/ c$ v+ ~df.resample('M', on='week_starting').mean()5 ^; ?- ~# Y3 w+ S
                   price  volume/ L* j7 c2 m6 C& C" n7 ^) [
    week_starting' _, u, P& F- p# T* o3 P/ a
    2018-01-31     10.75    62.53 p9 r/ j8 y# K; J- H* T7 c' |
    2018-02-28     17.00    60.0
    # h9 f- r1 P3 d8 O4 F/ u/ |+ |. V. R& `1 R5 Z3 |: d
    1* J- y. G5 D" n
    2
    " g3 o: P5 Y+ T3  q! ^+ y8 S: A' Q& ]  _
    4, f  Q0 C8 k. h" b( Z! |; ]& F
    5
    0 i6 `; f( ~. B/ ~4 S1 d6
    % q* ~1 P  g6 S' O+ A7" h! G) A3 B4 I; p2 ~1 m
    8- @* B& T% ^1 L
    9  Y/ B  B3 ~5 ~0 e. s. d: U
    106 G7 V$ y9 c  ]
    11  F8 q4 f/ r/ H# F. z
    12
    / ^/ z( G# u0 _8 I2 z6 [13* _/ [7 o' U5 X9 y& Z9 r9 [
    14
    ( k4 {5 I2 ~( O& V( J15( `  |9 r. g  ]0 \
    16
    & e2 J) }9 }. L5 g6 J17
    3 O+ H6 a; d, }: a+ a18) @8 e5 l, v6 u$ Q# ?, G
    199 g6 f6 d# b9 \5 Y' ]
    20
    2 `* {7 j% O9 b, Q. g21
    6 }( g& P& W+ n8 r8 Q3 X& k' j/ D9 Y7 j对于具有 MultiIndex 的 DataFrame,关键字 level 可用于指定需要在哪个级别进行重采样。4 F, z. [% Z+ r5 f
    days = pd.date_range('1/1/2000', periods=4, freq='D')
    1 r9 N. y) N! G7 d9 @d2 = {'price': [10, 11, 9, 13, 14, 18, 17, 19],% X7 \8 r- ?/ S: s  @3 d/ m% O' \" R
          'volume': [50, 60, 40, 100, 50, 100, 40, 50]}
    $ o5 Y9 p3 ?; Zdf2 = pd.DataFrame(! n+ x) H0 E) c! k' B
        d2,1 \1 `- G) _4 x8 r$ N8 @' T1 T
        index=pd.MultiIndex.from_product(
    + q: m! Q6 e- D0 ~        [days, ['morning', 'afternoon']]
    $ o7 _2 {" }/ N! v+ c; m, R    )4 K0 h1 t2 z0 y1 i: x, K* m; ?
    )3 j) C* a" r' X" l" Q# k
    df29 d2 G# c8 Z, ^- P! m+ h
                          price  volume
    $ T7 k6 ~5 x* a2000-01-01 morning       10      50# ]5 M% ^7 s  c: e, b
               afternoon     11      60. S; I& u2 ~; m. C
    2000-01-02 morning        9      40$ ^- Q2 I* L1 ?/ ]) T: t
               afternoon     13     100
    4 B& {  S% p. Q* `7 z2000-01-03 morning       14      50) h) W" w" j6 y/ i
               afternoon     18     100
    + ?& V; n& E. s# G$ V2000-01-04 morning       17      40& r  T+ L. L. P; J' ], G
               afternoon     19      50
    ) ?. Z# ]1 C" S) I, ]2 l; R/ {2 gdf2.resample('D', level=0).sum()
    # G$ d; H. G- ^) f  k3 n# I; I* L            price  volume8 Q' A3 w8 u6 E/ H1 {8 _( N; W) Z
    2000-01-01     21     110
    ) U" h6 B! Q- J# Q2000-01-02     22     1404 c: ]: f0 m# O) J1 z
    2000-01-03     32     150
    8 J- r4 F1 F( h/ Q# b2 ?6 n2000-01-04     36      90
    4 _1 g( d4 _+ x
    ( u: J  f1 O% c; S8 x4 x1
    * Z3 i6 I+ L2 R' X6 M1 R2& G! X' c. Y& |6 ], N6 V
    3, P  L% g; O9 M4 s! _! u5 n
    4
    & ?. `* A, J8 e, E5
    / p1 L  \1 Z: H61 }1 k8 C. ~  D: C5 x9 U
    7
    ( E! m  }9 x' s! t8: o9 v, m. Q2 p9 D: ?/ q; @8 E& J
    9: f7 L/ j! ]' @
    10
    6 y/ i8 M  ]! A  N+ w116 t- P) G! C8 j: t5 e. f) x
    12
    % @8 r6 C. ~+ {  B% F' |( K( B135 X, c+ r4 w& J
    146 E5 I: M2 M- [) p  _  V
    150 i, J- W" W% S
    16
    8 d, v6 S6 X+ t- i* t17
    * Y1 ~9 X% w6 r18
    * J1 ~+ u4 R+ B$ t, @; l19
    % \$ o, h( K9 d9 w20- |! k( q( M9 y' @6 t8 C! w
    21% k/ C8 I0 w5 L. q7 _
    22
    & ?' h) k: |1 B* M' M) n; T23
    ! {! e8 b5 t3 P4 G247 w0 h5 t/ b# o- k0 e; q
    25; l2 e, x6 i# H/ h( H3 E
    根据固定时间戳调整 bin 的开始:
    5 P. O; f; u7 o6 c3 ?start, end = '2000-10-01 23:30:00', '2000-10-02 00:30:00'$ [' Q, M$ k3 ]& n+ ?( r
    rng = pd.date_range(start, end, freq='7min')* i3 }1 d  W9 b: o9 W* |8 W
    ts = pd.Series(np.arange(len(rng)) * 3, index=rng)8 s0 ^3 f# T9 y. W1 J( S: C
    ts
    3 M8 X+ i9 H  u* A- j/ n2000-10-01 23:30:00     0* A- ]  b! L: ?/ A* C' ^
    2000-10-01 23:37:00     3
    ( Q8 n- {0 B4 r$ a8 r2000-10-01 23:44:00     6
    4 f7 ~! c( J% }# }2000-10-01 23:51:00     9
    5 s& g8 v' f' s( p* u$ j0 ^* H% o) k2000-10-01 23:58:00    128 X$ f4 r* n! G, T4 J0 I
    2000-10-02 00:05:00    15& @3 b$ [# [) s2 h4 R2 W' U
    2000-10-02 00:12:00    18
    8 t8 s- W  y& K9 J7 t- s2000-10-02 00:19:00    21' R8 Q. [) w; g4 a. b0 i
    2000-10-02 00:26:00    248 R2 L# l) ^4 V! O2 n& I1 R0 V( y
    Freq: 7T, dtype: int64
    3 O% ?; T$ q! L$ q! A
    ; w( S( m; t- ?; ^; Kts.resample('17min').sum()
    1 e4 J& U; S* e( L/ M5 O' _2000-10-01 23:14:00     0
    - D( M( i& d. {% m2000-10-01 23:31:00     9% g6 [1 j8 |- x) w1 k( K/ P3 H
    2000-10-01 23:48:00    21
    $ P" `, R5 ~( W$ q0 A2000-10-02 00:05:00    54
      Z! {/ L1 M/ m' p2 M2000-10-02 00:22:00    24% k4 j3 |, f- q' G# i! j
    Freq: 17T, dtype: int64
    4 R( Q) s! g8 D1 i4 o8 R0 {- i. E8 R  i; S# [3 @
    ts.resample('17min', origin='epoch').sum()% A! k2 x& N6 z5 e
    2000-10-01 23:18:00     0
    ( g# S1 g: Y/ l! x* X2000-10-01 23:35:00    18
    # N- v6 Y4 `- B. h7 [/ W7 l2000-10-01 23:52:00    27- r  C( |: E7 i" a! U! y+ w
    2000-10-02 00:09:00    39* r$ Z' J: g* y
    2000-10-02 00:26:00    24* H$ c$ f8 w* |2 j
    Freq: 17T, dtype: int64
    - ?* c5 l2 f" D3 L- @( ^! O4 v  t  F( m
    ts.resample('17min', origin='2000-01-01').sum(). e- y+ n. y* v% ~, Z( z3 ~
    2000-10-01 23:24:00     3
    2 p8 g% A( P. p' b7 Q2000-10-01 23:41:00    15
    . m2 U, b2 o1 P2 S7 O& Q4 f5 r  _2000-10-01 23:58:00    45* e8 a3 {3 F# s
    2000-10-02 00:15:00    45
    % f0 Q) {% Q& U) M# c# q# e0 [9 fFreq: 17T, dtype: int64
    : H$ l/ U/ Z! |7 h  s/ n) P
    9 `  _& V9 q& K7 t1" F  Z, k4 Z& e! u) l4 `
    2
    0 a; y; M8 M; F( X7 E3 [0 m3
    ' \: }& ]" v4 r. m48 C: c# a# ]9 O7 K
    5
    ) U$ S2 L! b* S1 E  g  H( b6 L6
    : q2 F4 |0 X' _* O. Q8 m7& t, j& \; Y) {9 K
    8
    ' f' x3 V% Z( s% W. J9
    ! w% x4 ^/ m: J10' Q+ u. ^/ v3 g1 C5 Y2 F
    11
    ! E" x' T; p8 Y* R9 ]. }3 u7 a12
    ( s9 r$ [0 u9 o13
    3 r4 v! }% E* D# X! Q' j7 \$ H( \! L  i146 U7 Y8 h2 V: n0 Q* _! E2 g, J
    153 d1 u- T, r' [& x
    16
    $ B# K4 }3 Q! _' V3 m" V17
    1 g' c% g8 F. X& S5 ]18
    , b- u; J1 n# i% C6 a19
    ) a3 }- R' o# j3 Z# f# K  G1 V" h4 x2 r20
    6 ?2 y: c/ \  W0 O1 d/ L, d21- `. g3 P& c- N2 @
    22/ V* o7 J: C, ?$ \4 D1 u% m
    23, }( z9 {5 C0 E/ z- g! C
    24
    " d8 ~' ?; z' b! A2 v, l4 e$ u% J% H25& n+ y7 b0 H5 F$ i. Q( e
    26/ A6 ?/ U. E% c6 z+ p# M. r% G
    272 f& C3 {2 E7 r, [0 p! ~
    28
    ' x. l; X" b5 V  T) w3 ?29" C( u0 g' T6 j. S9 h. `
    30
    0 T  n9 z" K- M  v310 j: m- K" c3 U3 L3 o' [* q! @
    32
    , L% [9 G  ~- {/ f* E( N5 E4 B332 H7 l9 X/ ~/ K9 \
    34
    ( W9 F$ T& i$ r  c3 N9 U# o1 t35$ U, }4 A% w, s& a
    36! o/ S: _* A* N3 c6 \( d! E
    37
    % Q$ h6 P# f8 @2 [2 z$ n6 m如果要使用偏移 Timedelta 调整 bin 的开始,则以下两行是等效的:6 Y5 s9 p$ ^! p& U
    ts.resample('17min', origin='start').sum()
    - L' R# D9 p. P3 bts.resample('17min', offset='23h30min').sum()
    9 O+ G0 P2 d& a- N: i2000-10-01 23:30:00     94 g9 U0 _9 E+ {2 `2 U. @
    2000-10-01 23:47:00    21% E; a6 {) B. |- q  A
    2000-10-02 00:04:00    54
    ' r& w( y7 H' J1 o2 M2000-10-02 00:21:00    24
    $ ^5 p/ Q/ U1 ~Freq: 17T, dtype: int64! J( X5 L! L% t9 t. U4 ?
    12 p0 l+ y% `+ A% E: a
    2
    & P3 H* O9 ~- @+ l3  d' w' I; ?0 p
    4
    ( C8 e8 N4 a' h1 u# P; M5% z( X; g8 ]3 I7 H3 P0 r4 S
    6
    * G! G% e! z6 S+ P* l' V0 U7
    $ F' p; Q3 Z, R8 u  a- U8 p10.6 练习0 A4 b# |" f5 F
    Ex1:太阳辐射数据集
    ; @) W. K8 x0 @  U% B0 ~现有一份关于太阳辐射的数据集:, L+ i' w" c/ S$ d

    & e8 g& `2 ?6 ndf = pd.read_csv('../data/solar.csv', usecols=['Data','Time','Radiation','Temperature'])
    - O! R4 u1 w% Adf.head(3)
    $ b' H7 A4 E  g, N
    ; s8 @8 H2 y- X/ _" JOut[129]: ; x$ v) ?& m% p
                        Data      Time  Radiation  Temperature+ b/ e6 @& F; g- C
    0  9/29/2016 12:00:00 AM  23:55:26       1.21           48! F' m( Z/ K; J; J5 I; `
    1  9/29/2016 12:00:00 AM  23:50:23       1.21           483 D. z4 c) A4 Y. c' Q! u+ I9 P
    2  9/29/2016 12:00:00 AM  23:45:26       1.23           48
    0 n' b; o; I6 Z1: K. [' U  ?% h! k
    2
    0 j' P8 V4 j; j( N1 D' H- h3" b+ f" `. W7 M  a/ r
    4+ S  L' `! U! H. k- h6 h
    5
    1 T1 ]6 g  p$ p6/ F  G+ [! J3 n7 a0 B8 x, i
    7+ Q" y2 N+ e# v3 j
    8
    : z4 R2 W1 n* x将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。
    . |3 Y4 q. p% c$ K# N( p每条记录时间的间隔显然并不一致,请解决如下问题:
    6 s6 V0 u& O: E8 w( {- s. D+ K, J8 D找出间隔时间的前三个最大值所对应的三组时间戳。0 z0 Y2 g' w5 _
    是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。5 q. ?" j3 g6 R7 ~
    求如下指标对应的Series:+ t6 q5 L. }- k; e$ P- D! _
    温度与辐射量的6小时滑动相关系数
    7 z" R. i* X1 S8 ?4 f8 A以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列
    ! w& M( _% y7 f1 t3 I* Y每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)
    1 J: g- L3 [- d6 _import numpy as np
    2 J3 I. B4 \* ?- ]import pandas as pd/ U+ l  p8 K  b8 b, a! R" _
    1; l* _8 U# O3 ~
    2% ?. p6 J% Z  u5 L" x+ y- y+ ~
    将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。
    ' K9 D$ H3 U# l( b9 b3 idata=pd.to_datetime(df.Data) # 本身是object对象,要先转为时间序列. P! h" q3 {7 F6 ~' J9 X- {) o
    times=pd.to_timedelta(df.Time)
    + Z. d' Q. z4 o( a+ A/ M# fdf.Data=data+times
    : D0 G3 s1 S  Q3 _( f9 udel df['Time']" j% C6 s! b/ I& |
    df=df.set_index('Data').sort_index() # 如果写的是set_index(df.Data),那么Data作为索引之外,这个列还另外保留9 O6 B9 C! {, [; `5 I" q
    df
    5 @; I. Y6 a" R                                        Radiation        Temperature  ^( d' B! F+ O. b; b1 l* @
    Data               
    3 t6 _+ Y* @. j1 j2016-09-01 00:00:08                2.58                51
    . M, {6 x/ @2 y: X2016-09-01 00:05:10                2.83                51
    & {5 s, p+ B) j" y2016-09-01 00:20:06                2.16                51! C  s9 i6 \* w4 {" M
    2016-09-01 00:25:05                2.21                51
    . l) d1 J. k; p% z% S) {4 _2016-09-01 00:30:09                2.25                51
      [1 [# x0 u9 |* z...        ...        ...
    & }' q- I- c" I2 ], V( T2016-12-31 23:35:02                1.22                41
    $ ^1 ?& v, h, ^; z9 I- y2016-12-31 23:40:01                1.21                41* c7 Y: L( o5 c% R
    2016-12-31 23:45:04                1.21                429 }* v! F. Q& k3 j% D
    2016-12-31 23:50:03                1.19                41
    & G; ^7 \" E5 u1 ^0 ]+ @2 S2016-12-31 23:55:01                1.21                415 F) i# b8 o( ^2 O$ A/ i

    3 C1 I  v4 O, q1$ g/ E; Y+ |. b4 Q  C
    2
    ! B# z: |' U: ^3
    " ^3 N% ?: m5 `* G4
    : n/ l& y1 b1 J1 @) K; z) H5
    0 W  r0 c" b$ }( N* g7 C7 _6
    5 \( F7 A. U1 Z) X" L, `  Y1 i7
    # o" f) Q  ~- |8  `7 c3 j5 X  G5 {& x" t
    9
      Q) @, y- Z6 ^& X' \, w: x10
    - |2 {: P, M' e2 T113 \  s% r" T$ C2 B8 J7 Q/ ^2 Y
    129 m  w/ U4 C2 Q2 j
    13
    9 \7 k" y/ {& V$ ]0 ?14# p% v* D& b3 V7 m5 p7 ^. J
    15
    " X& s, m0 B: I* M! r. {$ G, c6 V16
    6 \; Z2 [$ x7 ?, a+ d$ n# k  X17. D2 x7 F/ j1 b1 N; f
    18
    ' ^; Q2 E- R7 m3 o0 r$ `% A1 i3 Z19
    ' |: j1 R- V; b每条记录时间的间隔显然并不一致,请解决如下问题:
    + m2 P; E/ W2 ]! ?/ A+ G  E7 i, ^找出间隔时间的前三个最大值所对应的三组时间戳。0 u* @8 e0 @7 E- t2 j9 m
    # 第一次做错了,不是找三组时间戳
    - l0 B* V" P" L9 Fidxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]
    " l: x% ^% d  ~% t. \df.reset_index().Data[idxmax3,idxmax3-1]. x/ B( b, ]( x3 T; {" [2 N! S
    $ s2 [7 j! V/ x; Q4 A7 l2 G( G
    25923   2016-12-08 11:10:42
    - I3 j) K5 a) Q  O$ y& Y9 a24522   2016-12-01 00:00:02: [% y6 s  ^6 v1 r3 ?) Y
    7417    2016-10-01 00:00:19; l/ I+ \- F/ B4 L. l: v; Q& m
    Name: Data, dtype: datetime64[ns], J" u/ {$ s+ d; d( p3 ?9 t
    1
    9 {5 f, Z4 c0 t4 v2
    . D8 O1 A( Q0 J, G9 o& u6 b3/ C  \1 Q/ Q3 A( ]( Y
    4  p1 L* o7 T, f4 i* b
    5
    + G6 ^, A6 |0 r* l' I) b6
    : H( v2 |) h* r, ^; ?7: J5 s2 G3 f9 ~. R
    8
    , E7 |# O" e* Y" h5 \' K* |4 Pidxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]7 s* T% d7 _6 d4 ], T1 l9 E  M
    list(zip(df.reset_index().Data[idxmax3],df.reset_index().Data[idxmax3-1]))
    4 P: k# b. K% L5 R6 z) |% [+ i
    * g( X) h5 i+ a2 J+ G0 {[(Timestamp('2016-12-08 11:10:42'), Timestamp('2016-12-05 20:45:53')),, p8 D2 f: `  [# K' u4 Q9 r. w8 l
    (Timestamp('2016-12-01 00:00:02'), Timestamp('2016-11-29 19:05:02')),
    5 F! o/ A( T$ m( \& s (Timestamp('2016-10-01 00:00:19'), Timestamp('2016-09-29 23:55:26'))]) k1 `# x9 b' @; _8 [
    1" X3 i2 f, A/ l% u
    21 u! ?8 Y) u: }7 M
    3
    & z6 O8 g: W3 x- ]( W7 ?- N43 ^# s2 S0 c* i) Y, x* h0 m: s$ D
    5- u8 B) L* X8 p2 Z; f& L
    6
    , g2 x2 e, N  p6 P9 t" o参考答案:
    ! Y* i; P& q& m! w6 U0 `
    ! i& N. C3 g* s5 i0 v/ Qs = df.index.to_series().reset_index(drop=True).diff().dt.total_seconds(); j: S& Y0 O4 p( F# x
    max_3 = s.nlargest(3).index
    0 ?% x6 t& }0 ?0 L& R! H2 x+ m% j, Ddf.index[max_3.union(max_3-1)]
    & v, z& b# K' r- H9 c% N0 O8 t
    6 i3 f5 U/ s4 v! Z1 X" HOut[215]:
    9 n6 {2 M6 `# U& L( t2 j1 F7 IDatetimeIndex(['2016-09-29 23:55:26', '2016-10-01 00:00:19',. }/ b& W$ q- R
                   '2016-11-29 19:05:02', '2016-12-01 00:00:02',
    * v$ g+ _7 R7 G               '2016-12-05 20:45:53', '2016-12-08 11:10:42'],( ^% {6 x+ V: C
                  dtype='datetime64[ns]', name='Datetime', freq=None)
    % Q6 z3 ?: p4 F5 U3 `# T' g# g& h, ~1
    5 c, X& B9 {/ T- m2
      t1 b0 u& Y. E  ?% v, m" I) E3
    9 w+ k8 ^0 |& ?4 U/ Z+ y4
    : J8 ]; ?9 r! f, I# @0 X7 U, }6 Z* S5
    ) m) H' f" \% w- ?* I6# E/ Q9 e7 D' w3 o* s: Y
    7, x  u5 Q& g4 D$ _$ f
    8
    6 u: o! O8 \* A" }2 y& C9 ~94 m6 m$ D- K8 f. d, W4 V; M
    是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。
    ; W7 V: j" R2 C/ V7 v# 将df的indexydiff做差,转为秒数后排序。再求几个分位数确定取值区间3 E7 n- f* C- t- z# _6 G
    s=pd.Series(df.index).diff(1).dt.total_seconds().sort_values(ascending=False)1 W- i2 @6 e% g8 B! w
    s.quantile(0.9),s.quantile(0.95),s.quantile(0.99),s.quantile(0.01),s.quantile(0.03),s.quantile(0.05)
    - u+ [" ^8 }" y/ \7 l
    * h3 d% G. K' l( _(304.0, 309.0, 337.15999999999985, 285.0, 290.0, 292.0)
    ' F+ n% o3 ^( |, f& N1
    / u$ N9 H  v2 K, K- j2
    3 v: ?: l- l# w# K- _2 H3 ^0 ~8 V' |31 k- ?" J+ a$ C' i/ b
    4
      C, v) a* d* g% E5 v* i) E$ I% T5& `+ u( a+ X7 L) b
    %pylab inline; D. f1 A9 i' m: @
    _ = plt.hist(ss[(s.values<337)&(s.values>285)],bins=50)
    5 R. Y+ x7 g" ^' M" Y3 x; G- k* yplt.xlabel(' Timedelta'). n+ z  c. X  }; Z, ]
    plt.title(" Timedelta of solar")
    , C) T* f" I5 q! B3 |6 C; S( a- ^  z16 l% Q0 r% |: F& _7 _$ o3 [- p# Y
    20 n) V& {3 p9 C
    3' Q. l, x9 w$ H/ x# q2 u' a
    4% m4 k5 _( {, k0 p- g% C+ r+ T$ `
    , c9 V  |" b& e8 d+ _% X- g

    ! x3 E/ `# |+ r( P* e$ H4 [1 K求如下指标对应的Series:
    ) B3 X- t( `  C6 S' p: |) F, G( T温度与辐射量的6小时滑动相关系数
    + {# M9 e, ?& {; i* {) `* u' V, H以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列
    $ @) @$ Y) B* z# A" d( O- y' O每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量); g8 F2 ^" u% N# z
    df.Radiation.rolling('6H').corr(df.Temperature).tail()) e# |/ }/ s+ R

    + W2 J: K& q% }5 m$ o$ o# QData
    # k- m) }+ `/ V' v& y3 v2016-12-31 23:35:02    0.416187$ Z5 A( r1 p" I5 x1 g
    2016-12-31 23:40:01    0.416565
    " O: A1 u6 {$ ^7 l' E1 }  D- v2016-12-31 23:45:04    0.328574
    . F5 E9 l" @: R4 d& A+ U' Y2016-12-31 23:50:03    0.261883
    , L1 t/ }7 j' t& T& l& g2016-12-31 23:55:01    0.262406, i# Z8 z4 J/ p4 F! R3 J
    dtype: float647 Z! L6 T/ t* t$ a2 _( X1 T  D
    1
      T- V, @5 H% G4 s4 {, a20 n- ]  G5 y; I& t8 c2 Z
    3) b0 i$ ?! R/ b
    40 u* h) n4 o. _
    5
    " U5 n' X1 @6 G- B4 K5 ^, Y' g6( O2 V% G! \' S% e
    7- ]: R9 U5 J- y% Y( V" N6 W
    8
    $ D, }  S# y! m9
    & }  t; Y  E1 ydf['Temperature'].resample('6H',offset='3H').mean().head()
    5 v4 n. d+ b# z
    6 r# W4 p# P. k. SData8 j9 Z- c" I2 \
    2016-08-31 21:00:00    51.218750; g" P# v0 r+ Q3 q; a8 U* Y
    2016-09-01 03:00:00    50.033333
    2 G' _2 V4 ]9 w* A2016-09-01 09:00:00    59.379310. t& R  ^# j, g2 e) J$ r
    2016-09-01 15:00:00    57.984375
    $ b( l0 N0 B- W2 k! M& s3 I2016-09-01 21:00:00    51.393939
    0 `( R: v! Z  p( W7 UFreq: 6H, Name: Temperature, dtype: float64
    / m% G, \" Y# _/ V1
    - X: K4 `: l5 q2, h, Q+ N$ e* W. Q
    3# d, x2 `$ z" A& G$ I7 z
    4! \' I" |) ~% q5 V6 d+ z/ m+ z  G
    5
    + Z% a" {* c: m# e3 F: \0 K6
    : @5 @2 X1 M4 K% ~$ L8 l, h5 W7
    + c/ F1 Q7 p8 J; N8( z; n% l* W2 \
    9
    8 X* \# [/ E$ y2 K% \  X( O( H9 U2 u最后一题参考答案:
    , v1 W8 n- n7 N$ N/ x  U4 H8 ~5 J% r4 N- I; k& w; J0 N; N
    # 非常慢  p5 b7 i! a4 D. T2 m7 a7 s
    my_dt = df.index.shift(freq='-6H')9 q/ x5 R& Z4 ^  M7 s7 i
    int_loc = [df.index.get_indexer([i], method='nearest') for i in my_dt]1 ^' Q, h' U1 p* G
    int_loc = np.array(int_loc).reshape(-1)- g9 ^; h7 ?! U& }5 k7 C5 j/ q1 \
    res = df.Radiation.iloc[int_loc]0 ^( ?- z; `, u9 [
    res.index = df.index
    2 p9 W8 p' w; ares.tail(3): C. [  F# X( c: W' f" N
    1
    & S# t( p6 L. M* ?9 n; q' N/ h4 T2
    1 c* H3 W, J4 b0 E- E3
    4 {: A4 o; c7 W5 H4
    + b! c. W6 Y" s5; z5 H! E/ h! B7 D# {. w" r+ l
    65 M* Q& ]* x  ^' Y% A/ f
    79 g# }! S7 i( H6 M
    # 纸质版上介绍了merge_asof,性能差距可以达到3-4个数量级
    5 X, j# b# C, s' S1 z/ rtarget = pd.DataFrame(
    9 _/ x- J! H" {    {
    1 B  {; S% p( p( M        "Time": df.index.shift(freq='-6H'),* v1 b1 d+ G' V) W
            "Datetime": df.index,
    % d$ z5 p% P% p3 O) P' E    }
    & e# t1 N) Q0 w0 G% S7 q+ i5 z)
    9 W! N+ f4 o6 i' f, H& _1 i
    ' e% j: l+ M9 J5 S: [3 Fres = pd.merge_asof(% C3 c8 U3 e+ x0 f* E* z4 u: \
        target,
    ) Z  a* I' C0 t) N    df.reset_index().rename(columns={"Datetime": "Time"}),6 d/ X  f+ k1 @9 K; y# d
        left_on="Time",7 m6 k+ D7 H3 @7 Y: s( b0 r( H
        right_on="Time",
    ( F9 C3 _" `' ], m    direction="nearest"
    9 u& V) n+ G$ v1 w7 F).set_index("Datetime").Radiation
    ! t! E6 p  J3 [  E
    * b" v3 O9 ~% B* C8 s  E! j. Lres.tail(3)
    6 O* Q7 m& l. L0 u1 J* [  sOut[224]: # J* S# m% C/ V. e( Q  Z% V' y
    Datetime4 p. r* f! `- Z- j
    2016-12-31 23:45:04    9.33
    $ c9 ?: H$ ]+ x$ [+ n2016-12-31 23:50:03    8.491 v4 y4 d2 T* o  u! _% g' S& m
    2016-12-31 23:55:01    5.84
    2 [# k. _: u0 w% C+ ^6 wName: Radiation, dtype: float64
    9 ]: F# i3 N& U' d: J( G8 Y% |! `1 z4 A% f5 `
    11 v0 C  R0 ?2 ^3 U& \' `7 K, M+ i
    2
    4 z3 I( w( U3 R, N$ x. B% \3
    1 A5 s0 `2 m3 \! z& }7 w$ l47 l8 n# K( ?' K" R0 D- O
    5) K3 u- s1 l* p- H% p6 m8 R' @/ K5 h
    6
    $ A4 y" _7 R" U4 r: H& u6 W70 f; E# k: D- @1 E/ W
    8" L  Q. w: r+ U! F; _
    93 `" o0 j: r% b* m) H# a. q  P
    10; Y) W% N! [+ K0 I0 F2 q/ b: A; {; [5 ~
    11; B7 k4 ?  O+ P6 w- z# h
    12
    , h3 V4 ^. f" z+ o- o6 f13
    + P' B4 c. G1 S! G* j14
    + x$ w- K$ A2 G' {: |7 ?15
      Z) _4 I9 Q( {' G! y0 x# D5 x: k16: R3 F4 f  G: V7 Y
    17
    5 U( v- b! q! H5 C- E) U180 G& Z7 A/ {9 d5 p8 f
    19
    ) J4 S% A4 J8 i" f4 f9 U' ]  O20- |! p8 m+ P( K
    21
    + {* U! p8 t3 H( T22: z8 g7 H+ ~, V1 S9 ?% b- g9 a
    23
    5 F; Z, v3 I( e2 p+ B  q: dEx2:水果销量数据集7 Z# D4 Q, h5 W# E1 C
    现有一份2019年每日水果销量记录表:
    7 _* s2 ?" {* k* Y' r9 d* M/ A* m/ U3 S. J0 l" n/ |4 e) [
    df = pd.read_csv('../data/fruit.csv')
    ! T. e3 B& A! {( E8 h6 odf.head(3)
    0 c# F1 _' _6 f/ i% B+ i/ w3 y) k/ T: s, C
    Out[131]: + Q. q/ M( c- F
             Date  Fruit  Sale
    ( k- l$ Z0 p% u% c9 H% Q0  2019-04-18  Peach    15; V1 L. e) a4 N0 ?) z7 R1 \' }
    1  2019-12-29  Peach    15
    + t# O$ m5 p8 ^  W  I' Z2  2019-06-05  Peach    19
    / _( }5 [; i. a  v2 T1
    ; O5 i3 O7 T$ H; _; ]1 T* N2
    / v5 l7 ?" C& e2 N; ^3
    9 s0 w' M' A) M) F7 u7 ]: L4+ \9 m/ t0 N" j" ]  h( `% G& X
    5
    " V! \5 b: e' [; }" T7 {/ f6, t+ n. n) g1 O. H' p
    7
    ; l; r: {; d) Y. h  Q$ v. v: q87 |8 y% ?$ B% n! r4 L
    统计如下指标:
    5 e7 k; I8 w: {4 c& k* H6 X每月上半月(15号及之前)与下半月葡萄销量的比值* V& |- S$ H$ k& Y9 a6 x! {
    每月最后一天的生梨销量总和3 m! Z# N, D6 P/ Y1 Q! N
    每月最后一天工作日的生梨销量总和4 J6 \7 ]# J- u) ^' L7 L+ d
    每月最后五天的苹果销量均值
    & J& P! C1 ^" S* j4 _4 @# [按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。
    2 J; w) H/ ~1 X! d按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。
    . A9 n0 g( g1 P+ S1 V) Y( `import numpy as np/ b/ D8 x9 v# q! X9 p
    import pandas as pd0 l: k# L$ s2 r6 w
    1* `: U# E2 [: T" Z8 e" m: D* r( t# X
    28 y8 E, S( r# u* Y. }! ]- _
    统计如下指标:& h: [' u5 h4 H
    每月上半月(15号及之前)与下半月葡萄销量的比值
    8 |3 |# _. W3 x每月最后一天的生梨销量总和
      c- h2 s  f' c  D9 |5 [/ c+ H每月最后一天工作日的生梨销量总和, J" o( D* b  I5 O
    每月最后五天的苹果销量均值' B, T% V& ]8 V  k: y
    # 每月上半月(15号及之前)与下半月葡萄销量的比值; l, j0 I, x# G0 Q1 F- t* C
    df.Date=pd.to_datetime(df.Date)/ y3 ^' |4 o( `  }# }
    sale=df.query('Fruit == "Grape"').groupby([df.Date.dt.month,df.Date.dt.day<=15])['Sale'].sum()
    * t! }& ~, a7 b/ E# s" S. `% bsale.columns=['Month','15Dayes','Sale'] # 为啥这么改没用啊
    9 u+ @* @, p; q' M- C* B- t' Nsale=pd.DataFrame(sale)
    6 J% W) l9 x* C$ ^6 C3 g6 X7 W" n5 X3 ^sale=sale.unstack(1).rename_axis(index={'Date':'Month'},) c1 t$ w# a4 [% X3 l
                     columns={'Date':'15Days'}).stack(1).reset_index() # unstack主要是两个索引都是Date无法直接重命名
    . [5 Z4 D) D" x( `8 B1 t" W' Ksale.head() # 每个月上下半月的销量
    + c2 y! T" n4 ]8 b
    ( i# k) N; l8 D; ]5 I7 `  Month        15Days        Sale3 P/ m+ V  K2 J% Z
    0        1        False        10503
    1 c$ B( ~/ b, P9 z& e. u% j1        1        True        12341) C* R( q5 o4 q6 c# _8 t% E, E
    2        2        False        10001
    , F" o! _& q. C; W3        2        True        10106
    2 c( D; m. x; F* Z! s* J4        3        False        12814
    9 C/ H2 g% N3 A& T
    6 a" j8 ^1 U% e! }+ a; V) t2 {# 使用自定义聚合函数,分组后每组就上半月和下半月两个值,根据索引位置判断求比值时的分子分母顺序
    9 R- W/ M; O) Z- Jsale.groupby(sale['Month'])['Sale'].agg(
    4 y& O" h5 r3 o+ K; v  a                lambda x: x.max()/x.min() if x.idxmax()>x.idxmin()  else x.min()/x.max())0 y7 }  o" e' k% t9 j" w) ^  n

    , K9 N4 Z8 k" T; E7 ]3 CMonth
    2 ~! J. f/ [' f, w3 |3 B1     1.174998& F- W3 i6 A" ^  G. C2 k) V
    2     1.0104994 d2 F! @/ S7 {& U
    3     0.776338. n1 r3 s1 b- V' G0 j) A. x
    4     1.026345, [/ C- y+ p- W! [+ e& g) d
    5     0.900534
    ' R+ B( H+ r  ~' }4 m+ F; T6     0.980136
    2 u( L* v) K; G! \5 d7     1.350960
    6 g7 d, N! A0 l8     1.091584
    2 v. k% w6 a8 [$ J, I# g* `9     1.116508
    - e; ^  _6 t: d1 G* ?8 X" V9 l2 {10    1.020784$ k: X4 P2 |( h4 F; n& a+ X
    11    1.275911
    & y0 K& |) D* ~! @" m% z4 E3 M12    0.989662. C3 j( S3 ^9 T! X
    Name: Sale, dtype: float64
    / Z8 D. O$ `8 v2 c* d  @# Q, u8 v1 ?& W
    1+ F4 v% M3 P; g
    26 f3 l9 m" y! z. a- F+ X0 m/ N
    33 E* ?6 V, K1 n0 f' a! x* W
    48 G9 y( D$ G: x5 h, v0 _
    5" i; j( e3 h( |' I% ?* M, L% `
    61 e4 `1 O5 L* w5 O; ~
    7
    . u4 g  F8 ]8 z% `( Z0 ?87 G0 v( w. j' a4 z- z
    9
      g; Y8 u  c. [10
    / Y7 U; r2 m6 s5 e$ H* ~115 _1 S1 |  r( B7 l  Y1 n4 w
    12! b8 U7 |) A& N( Y  Q8 N/ M3 M8 j
    13% k7 ]- `! |. X
    14
    ' ]' m6 K" _) {. ]7 b6 }15* l; {$ U7 O, T/ q- {* T5 B( P4 u
    16: ]5 F% d" u# }/ Y9 q0 O( s" @
    17! `* V1 o% @+ H1 \+ P
    18
    ' \7 Z' q3 ?; q# R' U0 M19
    ; i# u) s; @" C6 @20. k& l) ^. y( h, V* X6 E' ^' k
    214 h0 T5 v4 C$ l7 x( ~/ w: a
    22
    7 B5 h" U" N2 |! z& m+ i+ B23
    0 v1 f0 I3 z. m/ A24# |  g4 S/ z0 `0 w- ]
    253 W5 o% ^- A5 \: S4 g. V! K" i4 k3 E
    262 A( E- g, j/ W. L" P
    27
    4 c0 w5 \; T. {/ b  F/ r28/ N( M7 ^8 N+ r; m, Z5 x* u9 X
    29
    3 {6 F; |$ n) j9 H$ a1 Z$ W* K302 r" o& V6 l5 g! z, H
    31& t2 _* A- W' p- }0 r
    324 {, F) h) V. e- W: F4 p9 C
    33
    1 H5 A# U# C7 }9 F34
    , c6 v+ p$ g2 b1 X, B3 Q* o5 L# 每月最后一天的生梨销量总和6 a. e& U, P; Z2 v1 a
    df[df.Date.dt.is_month_end].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()1 s, x& Y% X6 |

    6 w/ I' S! F  e7 ^Date2 u. L# c( s) q, V( k# A
    2019-01-31    847% u) ]) L" w( o( h: G6 r
    2019-02-28    7747 `" v$ a, N& B2 J0 j
    2019-03-31    761
    8 v5 \( @9 d0 v2019-04-30    648
    9 _+ i/ A% n: `) m4 v9 ^0 H2019-05-31    616
    ; T9 _, B4 O0 l' L18 j, @; w5 m3 ~5 [- w3 p
    2, [( S* r! V( G- r$ v: h) z
    3
    5 m6 d) @# J% `; b4 \4
    , k# L% k$ `' I/ M+ `7 b& ^$ j5" q2 P, i/ K4 ?. n4 m
    6
    + U& \& g6 p# m7 m7! h3 J& l; D1 T: ^: o7 d' O1 E
    8
    8 R) f: Y# L, \) i2 }/ J  p" A96 G+ Y+ |/ E* t+ q
    # 每月最后一天工作日的生梨销量总和& \. v* D2 b7 v: z
    ls=df.Date+pd.offsets.BMonthEnd()
    & F0 g3 e: m- N; X7 P* v: b% f7 pmy_filter=pd.to_datetime(ls.unique())
    7 m2 F+ z9 S1 K3 W2 F  ^3 x7 Odf[df.Date.isin(my_filter)].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()
    % a9 O# W; u! I$ h+ N* P8 W. ?7 f! @1 ~
    Date
    5 Z1 C; r. M: @( _2019-01-31     847
    6 E( u: Z5 B' Z# v( B$ N# `. B2019-02-28     774! x: O0 M$ _' ~. ~, c
    2019-03-29     5103 ^/ n& O0 j; L' A
    2019-04-30     648
    ! `8 Q$ J8 d, Y! R1 \( s4 r2019-05-31     616
    ' T/ U# Z& J, c1; ~: M' F# q- A
    2
    $ l9 V2 e2 ?) E! t5 Y4 P. m3 w, a3
    2 s0 R# @& G- {3 G# ~4
    + e" |( o# V* n7 K" N: w" C5% k9 \1 Z0 J0 M  d+ I
    6
    0 m* x* [' f  ?" h8 [7
    . w' p  J9 n/ V+ s1 D$ [8! D! B9 f+ `, |$ G* t6 N
    9
    3 q8 H" ]) X( T; c1 l4 @! z  h100 F8 ]3 v: m3 L
    11
    7 V$ c/ M7 v3 K: S. @0 _  `# R2 N# 每月最后五天的苹果销量均值
    ! l6 `3 U8 V# A: _4 P" Rstart, end = '2019-01-01', '2019-12-31'# k, ?( O7 z  `7 D9 g
    end = pd.date_range(start, end, freq='M')+ v5 i- [$ h( k: N) f, R+ j
    end=end.repeat(5) # 每月最后一天的日期列表,重复5次方便做差2 _/ m+ ]* q& M3 s

    / s/ J; h: C& r, |8 H2 A: E$ ptd= pd.Series(pd.timedelta_range(start='0 days', periods=5),)
    6 `  s/ {. C5 Gtd=pd.concat([td]*12) # 日期偏置,最后一天减去0-4天
    : h0 R( R6 j2 D$ [& r. Mend5=(end-td).reset_index(drop=True) # 每个月最后5天的列表
    4 Q: O' v8 \$ p8 F. S$ q! {) W
    apple5=df[df.Date.isin(end5)].query("Fruit == 'Apple'") # 每月最后五天苹果销量8 J0 ^  O) z  B1 n2 v* Q
    apple5.groupby(apple5.Date.dt.month)['Sale'].mean().head()
    2 o4 F3 U! a  X+ m
    0 h, h2 o! S4 w3 W% b: fDate7 P& h0 F) m) X
    1     65.313725
    2 E. N* o9 r  Z2     54.061538, z2 x- Y5 A4 n8 Q- ^, L  U* e# h7 k
    3     59.325581
    . |3 i5 r: p) i; _2 w4     65.795455
    / H: L4 r  o' @$ `! L  o5     57.465116  N1 ^8 f7 v. J

    / L) ~6 W/ P% h5 W: }  R, M9 B1
    # A  P: `- r) c7 q1 W0 K1 u" b2% d8 d$ L- M% C6 z- O$ ]
    30 w* V' @" g! u/ l1 l7 a! ]
    4
    # m' p; a$ f$ a! Z+ Y) ^0 v4 x7 B# V5: x& S0 o8 L4 }: u
    6
    " M( \/ L# F4 r/ F! |  i" G, c7
    6 \( A. f5 y! r+ C82 {; O7 @: k- {) ^$ Z) h
    9% U) {2 L5 h2 V
    10
    - W: k9 q0 Z( o  E11
    6 T4 K, C) H; X; ]12% d! R& H2 {6 y6 y6 [' O
    13
    - X* o4 |. c* \, D  U# v9 {+ U14
    - f6 e4 D8 F! e9 }157 ~: E. k: g& {$ W6 @( W
    16
    - i9 E; O0 ^9 t) e17
    * _& h$ H3 c# f" b+ ~6 X18: ^( h4 ^: w, O0 w* U
    # 参考答案:+ W4 p- Y' @' X" u. E  g
    target_dt = df.drop_duplicates().groupby(df.Date.drop_duplicates(
    4 O4 S: n7 S( F* g* V            ).dt.month)['Date'].nlargest(5).reset_index(drop=True)
    " Z+ K2 V' Q% q% U" S
    * N7 ^; u1 _8 g" U& k: Eres = df.set_index('Date').loc[target_dt].reset_index(3 W# M$ ^2 _: b7 U+ f5 h
                ).query("Fruit == 'Apple'")
    9 E, y1 C) K' u5 R
    " z& x7 }* `6 R* H/ U1 i+ g: Q: Gres = res.groupby(res.Date.dt.month)['Sale'].mean(0 p/ P, {9 A- C6 ?! b8 j4 M$ O
                ).rename_axis('Month')1 Z* f9 P, H" ~4 Y* c  u
    , C# m) z5 S7 b2 Q2 Q% @/ h3 b

    ( g0 l% V- Y" H% C; Pres.head()
    + U# K# Q* h, ]Out[236]:
    9 j1 |2 n+ }; C# G0 C5 h( w, iMonth, Z) o, G: m" c' Y4 b
    1    65.3137254 s; B0 Y* B. u1 m/ m, {! [; [
    2    54.0615385 ^2 v3 N# J4 h' ~1 X( W' I9 S
    3    59.325581
    $ y* K# w2 D" [: q: Y) t8 T% C4    65.795455
    5 i9 d, f: U# ?5    57.465116
    - }  B; [5 a9 W- cName: Sale, dtype: float64% l% X, `6 ]7 t( D* |9 H3 o! ]& p

    + K, H# A/ F" ?! y$ l: a$ X1
    , p& k& L& K; _, `/ l# r( o2, B: z7 V0 k. n% k9 U6 `
    3
    6 n  `6 K; c9 T+ P' a( |4
    ( ~2 ~; F  K" `. B' w) V5" L! r4 K7 \7 V6 L6 @: Q( E; x5 q
    6. ^% u+ C8 R5 W9 b# X2 s
    7, G1 ?# n0 g7 U
    8( t3 l1 p9 f" L4 o/ T4 D" O, V
    95 H  m4 Y0 p: q1 c$ x% w
    10
    0 V5 \8 k3 |) |3 P3 a8 Z, _7 s11
    , F4 z/ B, y* [$ s/ l* P12
    % v8 z: c3 n* t( @5 L& M* G13
    : n3 t5 C/ s1 x14
    " M9 s5 n' p' E( d2 z" {5 t15
    , T! i& Y# q8 ^' Y9 z16: F) F) t  L  h6 U
    17
    * y+ i( j" h2 J) k: \: y" T2 @: B18
    ! {0 g+ P: S1 V" R! F3 U* R19( h" Q5 [0 s7 c. R- E
    20! u2 L; E7 f2 x  u, I5 u7 W/ a
    按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。
    5 u8 t4 n6 n# d8 y$ jresult=pd.DataFrame(df.groupby([df.Date.dt.month,df.Date.; x* q8 `$ c* y  O5 e
                                            dt.dayofweek,df.Fruit])['Sale'].count()) # 分组统计
    ! u" w7 m/ U$ q$ [                                        5 [; [6 ]  X& a, _
    result=result.unstack(1).rename_axis(index={'Date':'Month'},: {: h" E0 ^* g1 w1 `
                     columns={'Date':'Week'})  # 两个index名字都是Date,只能转一个到列,分开来改名字.
    ) }( r# t* v3 F+ Q% f$ O- {result=result.swaplevel(0,1,axis=0).droplevel(0,axis=1)
    # u, a( r' \+ kresult.head() # 索引名有空再改吧
    / K; B" ?* C4 w: a' l) Y) A! C( U0 i1 d) o
              Week        0        1        2        3        4        5        6
    ) w) d! p6 G: `) lFruit Month                                                        ' s- g$ w/ r+ K* o4 h
    Apple        1        46        50        50        45        32        42        23, L9 H7 L- E1 G& N
    Banana        1        27        29        24        42        36        24        35
    * O. h8 f) Q& m2 tGrape        1        42        75        53        63        36        57        46  E6 w2 b: T+ F1 a. k+ a
    Peach        1        67        78        73        88        59        49        72/ B) Y; e  r4 g/ p. W% W+ h6 X. m
    Pear        1        39        69        51        54        48        36        40- E! u4 o7 J) ^2 f. S0 ]
    14 Y5 R9 P2 B* k! q& m/ Z
    2
    ( }: P1 I" b% c, H% q3
    , A! A9 |( ?" P* a  ^4
    2 ]) Y) y: D4 \# W- H9 b+ F59 w  z+ i" G: R. l. f) h1 S1 {
    6
    5 A. ]  n- u8 P$ T* r7* I7 m2 _' o( n! z! h$ I
    84 }( L0 S( `% ]
    9$ C  }! g; g) {' {, k( M8 T2 O+ H
    10; @- X' |. {. c  o5 v
    11; }3 @6 _  A$ P3 T
    12
    6 r/ y: o  n7 |/ F5 \13
    5 |) S" z/ e- }1 S% p14
    3 i. V; p$ h4 K8 k15
    - b; S; d: j$ A% ^( z按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。
      ^: E/ ^; q4 I7 ?! y5 l4 f# 工作日苹果销量按日期排序$ p: U5 p9 w' c4 K, D$ g" X
    select_bday=df[~df.Date.dt.dayofweek.isin([5,6])].query('Fruit=="Apple"').set_index('Date').sort_index()3 {; p! {' g  ~4 ]* T
    select_bday=select_bday.groupby(select_bday.index)['Sale'].sum() # 每天的销量汇总( z" {' H9 }% h6 r8 f+ `
    select_bday.head()
      W) V7 Q2 Z$ v( f' E0 g+ ~. R, z5 ~! F" N0 k+ m5 v/ G0 s0 r
    Date# i0 P$ }5 \7 I, ]
    2019-01-01    189
    ! N- S9 N3 ]5 G7 B6 ^2019-01-02    482
    % T' C* h7 Q* D* \9 X% p! E2019-01-03    890
    , b- C9 v5 m  _/ H$ D( t! J, \1 n2019-01-04    550
    7 f* ^4 s' F5 ~7 b2019-01-07    4941 U7 d% u0 x$ B1 v
    ' z+ M: m0 R( [
    # 此时已经是工作日,正常滑窗。结果重设索引,对周末进行向后填充。
    / Y2 U" l' p) uselect_bday.rolling('10D').mean().reindex(df.Date.unique()).sort_index().ffill().head()
    % g( q! }7 B) E0 b. l" _" q9 h5 M% K
    Date
    , ?3 |) o. C; i2019-01-01    189.000000! z5 ~4 B& j' {0 n; j( Y* i
    2019-01-02    335.500000% U2 S" i) p" ~7 v/ J1 Y7 e9 [! j5 z
    2019-01-03    520.333333
    3 X- B( b9 s* s# Q" k3 p+ P2019-01-04    527.750000! X4 ~% q8 T" _/ r, N/ G" S
    2019-01-05    527.750000
    . h- }) C% ^: [$ Z8 L' r* S/ u& c9 N) s" m" `9 Z  y! t9 p8 a  x
    ————————————————
    ) t8 j/ b4 E5 i版权声明:本文为CSDN博主「神洛华」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    ; U& D3 \5 R9 {  x5 C6 E原文链接:https://blog.csdn.net/qq_56591814/article/details/126633913
    9 \6 y" w- |. d! O2 z9 _- D# m) d/ u1 T( s8 i% Y0 `- K

    2 Z9 [+ |3 B. C* j
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
    您需要登录后才可以回帖 登录 | 注册地址

    qq
    收缩
    • 电话咨询

    • 04714969085
    fastpost

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

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

    蒙公网安备 15010502000194号

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

    GMT+8, 2026-7-30 02:43 , Processed in 0.712135 second(s), 50 queries .

    回顶部