QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2803|回复: 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
    % p+ G& ?2 v2 H- x& S3 R

    $ E5 z" V* r- t& \- }5 p. G+ C3 u: G% d: Y( z! I
    文章目录
    ' ~" O: I5 g- y" _. r8 u: h第八章 文本数据! F5 I. S, J  A
    8.1 str对象& N3 Z& G3 I2 S, K; B6 r) m- o
    8.1.1 str对象的设计意图" ?2 x/ `% o* Y  B, x
    8.1.3 string类型
    1 r; A/ R  {2 t- Y6 f8.2 正则表达式基础
    5 K3 k5 s9 `2 y' g* ]6 |8 e8.2.1 . 一般字符的匹配, g6 |2 y5 M9 ]
    8.2.2 元字符基础  y3 b! z9 ~" `. Y7 e7 U
    8.2.3 简写字符集
    ( X& H0 l: u: x- Y8.3 文本处理的五类操作7 i8 p) P! `  }5 I! @( b. i
    8.3.1 `str.split `拆分% _% M* I' |5 i/ V. j; N
    8.3.2 `str.join` 或 `str.cat `合并
    7 d0 g. v9 I! E, ]. m$ Q, {0 ^8.3.3 匹配  t/ c. S' e/ n0 j6 j9 j' J7 x
    8.3.5 提取
    . @  \! y' Q5 D7 n& d8.4、常用字符串函数' O3 n( P/ ?0 g  P
    8.4.1 字母型函数7 X; I8 j2 R$ ?
    8.4.2 数值型函数
    2 K- q4 [% B( z8.4.3 统计型函数
    * E/ b/ z6 `1 ~3 }8.4.4 格式型函数! ~# w  i" V: e
    8.5 练习& g8 |% p% U  l5 x
    Ex1:房屋信息数据集0 e$ N' q$ _7 \! K. d! Q
    Ex2:《权力的游戏》剧本数据集
    4 W0 q, V9 r# Y; H" V- y第九章 分类数据* a) z! H# q6 A' |  }9 T  L
    9.1 cat对象3 A; u2 z0 ?: i; i8 @8 f3 W3 E
    9.1.1 cat对象的属性
    5 I3 Y* f/ ~8 F& K/ o9.1.2 类别的增加、删除和修改& P& p# y; D, S( H
    9.2 有序分类
    $ p" ]  U" y, z6 Y2 _& O9.2.1 序的建立- N- `, Y! M# w. a
    9.2.2 排序和比较8 O2 c; _0 b, c6 z
    9.3 区间类别
      T+ g" O: `- R1 K4 L9.3.1 利用cut和qcut进行区间构造5 |0 j1 X; i! k$ o
    9.3.2 一般区间的构造$ V' W5 X. K/ X5 a  v' q" j
    9.3.3 区间的属性与方法+ j- L" O  L' F' b0 w8 @
    9.4 练习/ F2 U/ q& [7 ]! U/ a6 a
    Ex1: 统计未出现的类别# ?3 C' `3 J: i% _( x$ ^
    Ex2: 钻石数据集
    3 c7 E6 G$ G3 y+ c% O) a" H0 q5 P第十章 时序数据
    + t# l! h& v- L' Z- f/ B10.1 时序中的基本对象4 C/ f3 \0 V/ m& {0 d$ g
    10.2 时间戳
    . J  ?4 E2 ?' v' K+ c- F$ J10.2.1 Timestamp的构造与属性
    ' A( x" ^% x. E% P3 f1 [; r10.2.2 Datetime序列的生成# E0 Z( v0 i( u& o1 b7 `
    10.2.3 dt对象% O; f" z. z, `: p4 }6 s3 b+ R
    10.2.4 时间戳的切片与索引; O$ V5 X3 u+ ?1 r3 K, }7 Y
    10.3 时间差7 \8 n. b  I" n4 A4 k! h
    10.3.1 Timedelta的生成. B5 ?1 B7 f$ b% z/ S* t  Q! q
    10.2.2 Timedelta的运算3 J. o% }4 n7 o; r
    10.4 日期偏置* o2 A" ^4 h( {& Q# C1 }. u
    10.4.1 Offset对象
    : ]6 k7 V  i! q& p' }% w10.4.2 偏置字符串' h3 t2 x. o5 u7 k' I
    10.5、时序中的滑窗与分组
    * Z# ~( x, Q  Y  |/ r" H) k& a# a10.5.1 滑动窗口
    + d1 c5 ?$ q1 h10.5.2 重采样
    " t( ^3 M) I, P1 U10.6 练习; C1 f" V9 b) Q
    Ex1:太阳辐射数据集
    % v9 q3 S3 T8 p4 r1 O3 r2 _2 l' D2 ~Ex2:水果销量数据集
    / v& q  M) B' L, C  课程资料《pandas数据处理与分析》、github地址、讲解视频、习题参考答案 、pandas官网0 f/ o9 G, |" V
    传送门:0 W8 ]" |1 T' C9 Z
    . ?0 N- h" h3 w6 Z- y5 E! q, x. {
    datawhale8月组队学习《pandas数据处理与分析》(上)(基础、索引、分组)- `5 {3 t; _/ Q6 C
    datawhale8月组队学习《pandas数据处理与分析》(中)(变形、连接、缺失数据)( u; f$ C3 x4 k/ t( J4 J  O
    第八章 文本数据- m! ^) T6 `' I8 N
    8.1 str对象* w* _6 H+ I9 q. a
    8.1.1 str对象的设计意图( g; J! Y  q' u( `8 z
      str 对象是定义在 Index 或 Series上的属性,专门用于处理每个元素的文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其 str 对象。在Python标准库中也有 str 模块,为了使用上的便利,在 pandas 的50个 str 对象方法中,有31个是和标准库中的 str 模块方法同名且功能一致,例如字母转为大写的操作:" ?7 B% o) l# O8 o2 {' o% S' t

      V. O4 [- [2 Z# i! Fvar = 'abcd'8 o/ ?1 U, V: D. e
    str.upper(var) # Python内置str模块
    . K- T! U% T9 Z6 sOut[4]: 'ABCD'
    . Z2 H( T  L4 G/ X+ D+ e$ f' F9 m# Y, \7 E0 s2 y
    s = pd.Series(['abcd', 'efg', 'hi'])- W" i! [+ b6 d; `2 `6 [9 P

    1 x. H9 ^& M# }s.str4 w4 L! y4 ?& @. x+ {2 s$ Z
    Out[6]: <pandas.core.strings.accessor.StringMethods at 0x2b796892d60>
    ' V: }: t& ?; d% D' n( x. Y2 R9 U6 J! E, ~
    s.str.upper() # pandas中str对象上的upper方法% u+ G. w. J; v. K- ]' u
    Out[7]: + H4 U8 R5 @% Z5 |7 T. u6 F9 I0 u
    0    ABCD. J& O* h( j0 n6 K
    1     EFG1 f* X( M3 a. E1 f1 R4 n8 b( M
    2      HI
    0 W  L6 H2 X- ^/ l0 k& Z" Adtype: object
    9 K; z* Q8 D- V1
    ( ]' M. C8 S) ~2 t6 c2
    : W& q% J4 T+ x% p35 Y$ R/ K6 J7 M+ b. \
    4
    ! v) d+ S: ~3 s0 l54 D' N9 y5 N" O0 V2 l
    63 N# S8 p# L# b1 }( C  F
    7
    5 K6 S5 c: B" u# R* }+ T& y7 C& S8
    8 X; x( ~, v9 N6 k- \9
    $ R; Y5 G4 D  ]$ ?100 d/ I- L9 d, Z4 w" X% J
    114 q+ ~1 U' J- d. _) n
    12
    * @2 K2 ]$ T- o% h- C13# C/ e) R& W8 P; V% \- e+ |: }
    14
    & p& M1 F! l+ i2 G9 o15; f* t' G! s$ y, c( ^
    8.1.2 []索引器/ I$ Q# u2 V" f0 r& ?0 D6 I
      对于 str 对象而言,可理解为其对字符串进行了序列化的操作,例如在一般的字符串中,通过 [] 可以取出某个位置的元素,同时也能通过切片得到子串。
    ' k& z. w+ \. L, T  pandas中过对 str 对象使用 [] 索引器,可以完成完全一致的功能,并且如果超出范围则返回缺失值:  p0 ?. U2 s" R) d( s* h

    7 t% H8 j# Z7 k# [. n' c* y1 v+ es.str[0]
    ' E4 ~3 x' s- C; [9 h) jOut[10]:
    + u; y8 d- c  a5 Y( h" \0    a
    % i5 g2 R9 c/ r1    e
    6 E- ~1 Q0 e4 n; q! F4 ~+ F  t2    h
    6 s: S+ d. S/ Y3 v% |! kdtype: object" A9 |8 g. R" b) G: I5 v# x

    8 @$ L) E  R1 _0 W1 ds.str[-1: 0: -2]
    : R3 M5 x% ~/ I5 k; S0 DOut[11]:
    1 P3 T& Q. z3 Q4 t  X- U0    db+ f) S9 L, ]& m6 ~
    1     g
    ; n1 U6 N8 Z+ ^$ ?% O& u2     i
    7 [- X( h( @5 S) ]5 I/ X4 U' }" [dtype: object
    8 I, d0 U6 t$ U: `' @
    , L; J9 S9 Z1 z% c! }0 v$ D; zs.str[2]
    5 y) x+ R* @  aOut[12]:
    2 A. s+ @7 m5 x  L  D$ M0      c
    - f9 Z: R2 Q/ T' D( d+ \8 x1      g- `- z+ o' @) j+ c1 E
    2    NaN
    ' _4 E1 o* Y  i. `! {6 u1 vdtype: object% j, n4 P5 e' j1 S
    , a: S0 v5 i: v- Q' i
    1
    5 \1 P  q0 b" s2 E1 Q2
    3 Q; I- Z+ H5 G( P: a9 }  j' \- ?3
    " [4 F- j5 H- @( p. |4: S9 G' |4 r; A
    5
    9 |3 p: \7 i: A5 b" X9 Q69 A4 M, b2 ?/ P7 |' I1 q3 W8 r- X
    7$ l, V0 `! L+ V" y# o) L
    8
    0 W5 s9 V  z. E4 T2 W9+ M3 B' i$ _4 D5 q( i. [
    10
    1 j; G. B# t/ L% T- F11
    ) l& G. G  k. ]* e" }  e0 @% U12/ P! n% p# U$ K; w- v
    13' a" q+ J% F- b) h; e+ K% T. ^
    14
    ( b+ x! K/ u9 ^15
    3 S) f6 l+ b. @: P  k16' F+ }& J* ^; g' E
    17
    / Y" v5 D' S; T$ H4 N! d9 s18' @6 g0 a* T) \6 \% r, V' w
    197 A; M7 V" u* [+ [* x9 R
    20$ v$ h% P, \0 @
    import numpy as np
    " C" I( b* d" c2 B- Himport pandas as pd9 a5 N8 e( @( E* X% [9 v1 p' a
    ' V, Y+ E4 G" |: v+ x+ d
    s = pd.Series(['abcd', 'efg', 'hi'])
    ' }! l9 Y$ L2 w2 l! O, _s.str[0]
    , f& t3 r; U: _. R" ]$ t1
    3 M/ r5 K; \  F4 R6 R. ?! ^24 l1 i; i1 ]3 _/ e8 ?" X  K
    39 S9 S6 {2 }0 Y& T1 I4 N  \
    40 I9 w/ L$ o0 b
    54 D- c3 z: S# g% m2 v
    0    a, |2 Y4 v: s+ N  g) U( ?
    1    e
    / a, J, Z" y0 D% `2    h( w  p3 o- a! s4 d
    dtype: object+ J/ h% N* R6 L" G6 B
    1
    : ?- ?  C3 F4 m+ l" T6 F2 p4 A25 m& _+ Z+ F+ v5 K
    3+ V+ b" j% s6 D
    4; ?7 i! h3 P4 d- j) g! Y
    8.1.3 string类型
    # \6 I  B9 W' F6 b) h' U/ y  M. p  在上一章提到,从 pandas 的 1.0.0 版本开始,引入了 string 类型,其引入的动机在于:原来所有的字符串类型都会以 object 类型的 Series 进行存储,但 object 类型只应当存储混合类型,例如同时存储浮点、字符串、字典、列表、自定义类型等,因此字符串有必要同数值型或 category 一样,具有自己的数据存储类型,从而引入了 string 类型。. f; g. T7 t* |3 f
      总体上说,绝大多数对于 object 和 string 类型的序列使用 str 对象方法产生的结果是一致,但是在下面提到的两点上有较大差异:! Q0 Q. n% d1 Q

    % Q% J3 L! n, D4 s( D' r9 j二者对于某些对象的 str 序列化方法不同。, \7 d% M* N$ T( b
    可迭代(Iterable)对象包括但不限于字符串、字典、列表。对于一个可迭代对象, string 类型和 object 类型对它们的序列化方式不同,序列化后str对象返回结果也可能不同。例如:
    $ C) R2 P4 j/ ns = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a', 'b'], 0.5, 'my_string'])+ J, W6 K+ U5 A9 H
    s
    : B" u: Z: b0 t. p6 l1
    . n' l# Q; h0 G0 B" K2+ Y! [5 V' d% X; o! Y
    0    {1: 'temp_1', 2: 'temp_2'}% w. I  b/ K6 _) }! d6 `5 ~
    1                        [a, b]
    $ `. q# x9 @" Z6 B$ v2                           0.5! \1 r5 N/ E  J
    3                     my_string
    ) v8 {- x7 L$ hdtype: object9 I/ k  `( J# j
    16 B7 ^' n* n  ~$ G/ j, ~$ T
    2
    - X4 d3 r  T8 I2 D( y35 Y7 O, h4 }8 {7 y, H
    4
      l  k3 Q- a  O' j0 V6 ?5/ R- V, Z7 X' v) w  i8 ?# }
    s.str[1] # 对每个元素取[1]的操作
    . g: ]0 z1 g2 q5 a! z% w0 Y7 j1, X6 X# a0 s* N6 n& c7 \
    0    temp_1* x  q. @' d/ v5 j
    1         b
    5 W$ `+ o) `. N2       NaN
    6 O4 R  i4 r- U4 S3         y# X7 c, z* k& T" M8 Z
    dtype: object
    ' m: l8 x3 l; b* i5 ?  k0 ?1
    - c: k: h5 A' A& d- F2
    5 Q8 l; p  p3 U6 B4 l34 w2 f* U" @% y7 M2 o: e7 X+ a; B
    4
    0 r8 D! h: h6 q3 R) m9 e# X5! V9 ~6 V$ X! b* @) {/ R& B
    s.astype('string').str[1]
    ) G7 |# }0 P( I" ]( y0 D3 W1$ X& ~  B8 t9 s8 K  T! E
    0    1
    2 f, l& R9 ^" O8 Q8 K# d* y1    '5 W+ S1 _' y, y# N  _: O5 X
    2    .
    - ]5 j. l8 n$ p6 U6 e3    y7 B  g( I* Y' U
    dtype: string$ [9 E8 g7 F6 b( u  x, H1 X
    1' O& @" R/ q3 @. O
    2
    0 h) g$ w6 e% H0 D. d4 g2 K3
    3 A) B$ W4 N9 _4
    ( D' j$ p1 m% [0 w2 A7 @5 J57 b. u8 C3 @' R" C3 m
    除了最后一个字符串元素,前三个元素返回的值都不同,其原因在于:% S' \$ o; q! z
    " J4 T+ o/ q, ]5 `, L# }
    当序列类型为 object 时,是对于每一个元素进行 [] 索引,因此对于字典而言,返回temp_1字符串,对于列表则返回第二个值,而第三个为不可迭代对象,返回缺失值,第四个是对字符串进行 [] 索引。
    ; F- i  K! K7 nstring 类型的 str 对象先把整个元素转为字面意义的字符串,例如对于列表而言,第一个元素即 “{”,而对于最后一个字符串元素而言,恰好转化前后的表示方法一致,因此结果和 object 类型一致。
    3 ^5 F$ O" S; Mstring 类型是 Nullable 类型,但 object 不是
    * {8 Z5 o0 G  J& ]' o# i5 ^8 X  这意味着 string 类型的序列,如果调用的 str 方法返回值为整数 Series 和布尔 Series 时,其分别对应的 dtype 是 Int 和 boolean 的 Nullable 类型,而 object 类型则会分别返回 int/float 和 bool/object ,不过这取决于缺失值的存在与否。/ H4 z( I! A4 }1 b2 }3 U
      同时,字符串的比较操作,也具有相似的特性, string 返回 Nullable 类型,但 object 不会。- A/ T. E" X- G- [
    s = pd.Series(['a']). X' u9 `9 v+ e( c1 u& @0 T* I3 ^, l; G

    2 m. R5 N& Q: H4 O& zs.str.len()2 d7 T: R; ?8 E
    Out[17]: ( J, u/ J1 k# V2 A1 h' M" |
    0    1
    ! G! x* u7 x% @! m( Edtype: int64- \5 E) h1 H2 r( C6 h# T8 k

    ( ~4 [# C3 J+ Y; f: [3 U% }  Ns.astype('string').str.len()6 g& V) }2 \/ w- f5 Q/ Z$ e
    Out[18]:
    - r  k; m' m9 X9 ~1 e7 c0    1
    * B+ _/ C* e1 p. gdtype: Int64
    , q" ^, l: Q# ~# P: z! ^; k& I7 V* u- f
    s == 'a'' {$ x; I% C0 k$ t
    Out[19]:
    6 P8 e& _% R7 ]8 r) o1 S0    True
    - j$ e6 F; p# R; }+ adtype: bool" f. d2 A( Z* E# Q' o

    , x, m; ~6 Y8 U2 c) Ys.astype('string') == 'a'# U  m  Q; V0 w) E( j( W, b
    Out[20]:
      Z8 ?7 Z$ V' c9 b! j  y1 r0    True
    6 \$ w% X  L$ Edtype: boolean
    ; j$ v3 r& v) ]  D9 W/ T
    # z. L7 U" g4 d+ K: u: w! As = pd.Series(['a', np.nan]) # 带有缺失值
    ' V* D, s# s3 I" [! s0 ?4 B; l$ _0 B7 p/ ?  m: z+ U8 }
    s.str.len()6 l: x, D6 R" B
    Out[22]: $ ~" J" Z3 ], Y/ h# f9 M; o1 g0 M3 a
    0    1.0
    8 l% ], ?/ Q$ q/ n% O7 B" [1    NaN! o, p- I2 Z& J
    dtype: float64
    2 o, j: {& F+ I( h8 n
    + \* X  Q1 V) E0 Xs.astype('string').str.len()
    1 ?% s# G- Y# c& G$ _' A% z0 R; R# YOut[23]:
      c2 i! S. ]! U- g- L0       1
    / I! C" c/ W& u3 H1 h8 X1    <NA>2 _# d! u3 {6 x+ L8 p6 `* Z" l2 b1 |
    dtype: Int64
    5 o$ F$ y! J5 t. M  p$ v- c1 ~5 g& q! i5 X1 @9 A. Q' C6 ~4 y
    s == 'a'
      x' U; T, U- m( z3 B/ sOut[24]: . a* f' X" v$ N- Y+ N6 W( b
    0     True
    & u/ U" _7 p9 G% M1    False- @1 Y$ z1 k2 ?
    dtype: bool8 V) Q5 `0 u, O8 P" W) K1 p

    $ o5 b$ r( l- }! x' A1 _) r/ Us.astype('string') == 'a'
    ) m7 L/ W% ~; rOut[25]: 0 H6 o+ j# x3 [( M, W; H* K- e
    0    True
    8 g6 E6 q: w  [  @$ H1    <NA>
    % i  J, P+ m: Q# q3 n! Pdtype: boolean( j' k% _3 u' ?7 U5 f. T
    ) m  @. `; L$ n7 N3 u' j9 ~
    1
    6 r& r+ }" `8 V0 `2
    + s, d7 X5 j, \3 B3 m2 ?0 X9 [) R39 S# U$ Q) E) n( `9 ?! x
    4- T8 d4 i2 p; w1 L$ e
    56 }( X& R5 z; w7 b* n% T* k. h
    6
    " X" p$ M& I; d% u9 _7) s3 b% W+ o9 X. T4 y- V! @
    8+ J0 ~# i( _: |: ]
    9
    6 K& G- Q: j9 p# ~10
    . o3 C% Q$ _  H( g% T11
    7 [  y! E2 @5 Z; y2 z12' o7 {  x* ?0 {) N+ j
    13( G, J# P( P" I1 A) T+ F5 s& q
    14( Q% d! m/ e. X8 `  V# Q( @7 Q' o
    15% N3 m2 ], \% G! J! v
    16# V) j3 {/ A( U5 D- S8 N
    17
    ; m) L5 E' j+ t: ^% }' T% _18# _' a9 R+ m5 h: s) {
    19+ [  A9 h/ m5 s- S; G8 B, i0 @% ^; t
    20
    $ C2 W, |8 @' K8 K9 |/ X21( R. D$ O/ k$ y( {' P5 x9 Y
    22
    % G( H) Y3 b' p( a( C23! r( s# x0 t: J6 O* X
    24
    5 B- I: {. W1 i% K25
    - S/ {% C2 _- Z- L5 ~/ T# k6 O& @( I26
    4 O" z7 j2 v0 s4 C5 Z27
    % M1 z7 w( z# w4 `0 L2 k$ K/ O282 q- x. v+ l* G3 {2 t5 S# k
    291 V, F& D6 s, V0 a, D( p2 _4 R
    30
    . b* v' ~+ ^- Q; N; W% W31
    9 E3 y( J+ v7 |7 e+ |32
    ( l3 J2 z; f0 V' I  ?1 S( J  _, A. @% e" s33, i5 [$ ?7 x. K% N3 Q& `6 y
    34$ q; j% `2 e* |3 }- }3 x2 [& F
    35. X; W% j% l2 w3 d8 d+ v8 \8 q# T
    36
    5 g3 Q6 T3 U* B6 C37
    . x" D3 @) ?9 M9 U38
    : v8 p! e6 N. P, G( g: t5 U* g39+ H( Z& k+ n# g) L9 x
    40
    - H4 L6 C) G9 ?! c) T$ i% a0 w41# p# u4 C1 x/ A/ |* P
    42
    - y6 S) i4 e6 Z! \* |% K43
    0 U% R% I# ]* X: ~% J' \% h44
      `3 m9 ~8 r) G. ?8 c3 \( t45
    . d. g2 |: w6 c$ d; q3 U46
    - X. \4 u" M" p; ?1 d47
    8 ]& n! ^2 l5 S. d  对于全体元素为数值类型的序列,即使其类型为 object 或者 category 也不允许直接使用 str 属性。如果需要把数字当成 string 类型处理,可以使用 astype 强制转换为 string 类型的 Series :
    $ Q4 B0 L2 ]' L4 P
      N3 H% u4 ~/ Y5 n" t* `s = pd.Series([12, 345, 6789])
    2 `3 e1 ^4 f( ^) M, O+ @+ G; \- W- @; Z7 P- k2 n+ K
    s.astype('string').str[1]
      J) ^% z. A; H  C0 h9 O$ E& \Out[27]:   A3 W( [7 M) Z. g; j1 d
    0    2) q3 M5 _$ @" I( m5 u
    1    4' b# e5 B3 U! W! y. z
    2    7
    3 ~7 @- V" W" T! C5 S  Adtype: string3 @. _" B6 |9 @4 r4 A
    1
    5 z) g6 o# i, G' L9 h2
    " v6 K" k: _  V7 v5 ~3' r4 O$ C& P8 X  n3 q
    4
    & Y5 c  b" B$ S0 X- f% [5
    $ l$ S) l6 T$ X7 [, k- E63 S( }+ r! D/ v& `
    7
    3 C6 o  ?- O" m* |5 V- ^' S3 X% P8
    ; l% j& n' D9 ?5 F8.2 正则表达式基础
    * v: d# X$ \2 p. _% s5 H: e# L这一节的两个表格来自于 learn-regex-zh 这个关于正则表达式项目,其使用 MIT 开源许可协议。这里只是介绍正则表达式的基本用法,需要系统学习的读者可参考《Python3 正则表达式》,或者《 正则表达式必知必会 》这本书. h; [0 K% h* D7 u

    & Z$ ^$ J. S$ i8.2.1 . 一般字符的匹配
    + _; A7 p; N8 Y. D# d% j) _: Z正则表达式是一种按照某种正则模式,从左到右匹配字符串中内容的一种工具。对于一般的字符而言,它可以找到其所在的位置,这里为了演示便利,使用了 python 中 re 模块的 findall 函数来匹配所有出现过但不重叠的模式,第一个参数是正则表达式,第二个参数是待匹配的字符串。例如,在下面的字符串中找出 apple :
    , b3 h9 }: j# z* S+ U
    7 x. ]3 @0 t  g  v8 ^: ^+ nimport re& X  }) h  C( Z
    5 u7 w, t2 ^3 ~5 D8 O
    re.findall(r'Apple', 'Apple! This Is an Apple!') # 字符串从左到右依次匹配4 A2 x2 F4 ~8 H3 K1 J2 k7 [5 p
    Out[29]: ['Apple', 'Apple']9 `( n% k* l6 A* M! k. Z/ ]
    1
    9 E2 Z, r) L9 M! z2+ G' i! g% J) D
    3  s" x+ o  H% l, j/ \
    40 e2 x/ U. A; o8 Y) a
    8.2.2 元字符基础1 U% U1 \. C/ Q3 B8 O5 e4 p3 ?
    元字符        描述
    & E1 d3 [3 K' E! B: ]* E.        匹配除换行符以外的任意字符% Q/ P+ _- z+ H3 p" i/ c
    [ ]        字符类,匹配方括号中包含的任意字符6 c/ I0 o7 K" d, U
    [^ ]        否定字符类,匹配方括号中不包含的任意字符( \( K. p6 z1 n( j( Q
    *        匹配前面的子表达式零次或多次
    : K' k7 i; M& ~( V1 F% F9 j  O2 X% S+        匹配前面的子表达式一次或多次。比如r’d+'就是匹配数字串,r’d’就是匹配单个数字. {6 g; e* L1 q) N( }
    ?        匹配前面的子表达式零次或一次,非贪婪方式
    ; t  F0 I, ]( z% M0 q/ ~( V' B{n,m}        花括号,匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
      x& t& S" U( A7 d(xyz)        字符组,按照确切的顺序匹配字符xyz+ A; ^5 H* p5 }' s& {
    |        分支结构,匹配符号之前的字符或后面的字符  P6 X, U  D, \
    \        转义符,它可以还原元字符原来的含义# @: L, T9 \! A
    ^        匹配行的开始3 h5 ]% g/ s4 w% ^1 m- m- z( B
    $        匹配行的结束3 [: q! N7 Q" p; g& _% @$ {& h+ l- e; X8 r
    import re5 V5 r0 N& K4 f; C- o
    re.findall(r'.', 'abc')
    3 Y- j4 \3 g. c- k# M) TOut[30]: ['a', 'b', 'c']2 _4 v, Q3 o# V& c6 d  K

    9 Y6 H) y- o; k! o$ qre.findall(r'[ac]', 'abc') # []中有的子串都匹配1 J. s" f, ?+ D+ q6 Z) U- B
    Out[31]: ['a', 'c']1 @; `9 o! o$ W( {, l; g/ X

    2 S3 k2 h, i" Gre.findall(r'[^ac]', 'abc') / N0 ?2 f% s! g+ R, T2 C% a
    Out[32]: ['b']
    0 [  ]7 g  B, I
    ( n, j; l8 y5 ^re.findall(r'[ab]{2}', 'aaaabbbb') # {n}指匹配n次- [& o, q, z( t( }
    Out[33]: ['aa', 'aa', 'bb', 'bb']2 y, B' B, h& P! {: i4 Q% L4 ?
    ( _. {. b3 d/ v, W1 n
    re.findall(r'aaa|bbc|ca', 'aacabbcbbc') # 匹配前面的或者后面的字符串
    5 ^& y7 A1 s& hOut[34]: ['ca', 'bbc', 'bbc']# D/ i# }' T0 N
      h' R+ |4 l$ k  @8 ]0 M# m2 I
    # 上面的元字符都有特殊含义,要匹配其本来的意思就得用\进行转义。: P4 |: w/ `) ~! K
    """+ M0 a* j# Z. j( @
    1. ?匹配的是前一个字符,即被转义的\,所以|前面的内容就是匹配a\或者a,但是结果里面没有a\,相当于只能匹配a。
    % v. |! o1 f! X* i! b2. |右边是a\*,转义之后匹配a*,对于竖线而言左边优先级高于右边, b0 ^7 A9 D; y/ |. ~
    3. 然后看目标字符串aa?a*a,第一个a匹配左边,第二个a匹配左边,第三个a虽然后面有*,6 N( ~4 [* S* w+ Q% B' B' V! }
    但是左边优先级高, 还是匹配左边,剩下一个a还是左边,所以结果是四个a
    2 U: H  U" L8 r( O! \"""0 d" P$ ]' A/ a  w0 V+ u- }
    / f; ~: m8 k  N0 q
    re.findall(r'a\\?|a\*', 'aa?a*a')   # 第二次先匹配到a,就不会匹配a?。a*同理。
    ; d4 H6 g+ e2 `# S' COut[35]: ['a', 'a', 'a', 'a']4 M3 l$ y- _3 b
    & N$ l) `; z. o& \" G( L" D8 q3 f
    # 这里匹配不到是因为目标串'aa\a*a'中,\a是python的转义字符(\a\b\t\n等),所以匹配不到。# k2 J  P) {6 J% z7 G' b3 C
    # 如果是'aa\s*a'之内非python的转义字符,或者'aa\\s*a',或者r'aa\\s*a'就可以匹配到\字符。: d) Y& {- s* T) J
    re.findall(r'\\', 'aa\a*a') 3 \# D( t, x( G
    []
    - }( ]. Z9 N2 F2 d. {0 |# ?! K% P# `1 M' ~9 u% w% v+ T. H6 P
    re.findall(r'a?.', 'abaacadaae')
    ) C" O( S1 ]6 k- K5 x; R2 `  A$ tOut[36]: ['ab', 'aa', 'c', 'ad', 'aa', 'e']6 b! Z/ S/ v8 ~5 l
    & A+ i3 o4 E" B+ c& S) Z- H
    re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10') # 多个匹配模式,返回元组列表
    $ u2 ^- q# b6 j( Z6 X7 `[('width', '20'), ('height', '10')]% G* U) Y8 y* f7 G2 [0 q
    6 u6 S0 h4 c. @# h& e% Y
    13 ~1 x3 ?4 J  a$ `6 L; @
    2
    1 i+ h' u/ i8 Z  K6 y3
    ! N# b/ ]" l) c+ x5 \4
    $ B- b% F& g% u- c1 N% L5+ p& o/ u5 w% p# V4 S8 O7 G
    6
    ( r2 |# c& u# w7% f6 C! [0 f9 n  B% E& q3 K
    81 ~8 t; j. E) {- C, c, ?
    9
    + o, ]# Y* I4 y. P10
    ( E4 u& U5 o2 ~+ `+ o# X; v11
    # d4 n2 O8 J! y12! L, F3 [! y" V* B
    13
    * c" C0 i! ]1 x) N. Z+ u% t- B14
    / h9 e7 N; r+ E6 Y5 j15
    4 i. K. f2 Q/ S1 \- w4 Q5 c167 d) Y' ]7 E  t& U
    172 m  H- D# U+ e
    18! K7 q3 [6 Y  |0 I/ L: l9 t7 F+ p
    19" z; E5 ?: M; J& M4 T# Q( V, B
    20
    0 h2 r3 Z. F. g- A0 u4 V4 x, _21* d! {; s) s4 @: Z) b" |- N
    221 L& _% u# z4 P2 O. y$ G* z
    23
    ; z: E# ^+ d" U5 |; o249 f. e' L) J) Z$ K: D( ]5 m! F
    25
    % F5 p* S5 d3 B26
      ?  }- O/ ^  e27
    % q& x# s; Y/ p3 x284 S/ \1 Q+ C. L' k7 ]
    29
    / n9 K  h" Q4 {0 r7 k7 o& \2 v& h( ~3 `30
    7 J+ L% D+ O, l% q% O310 Q+ g9 L8 Z2 O# N# K8 [
    32
    , o1 I8 [, t. X( X338 }4 [- q! {/ g+ X5 g
    34" b2 M) r! D' K! U" K4 k* H
    35- `# h( W1 I& {; R2 T
    36
    3 C9 i5 ]4 X3 @- n" Z, W37
    6 u" |& J4 H5 l9 y. o) U+ Y1 U8.2.3 简写字符集
    1 l, s% z: s0 e/ o+ w# z则表达式中还有一类简写字符集,其等价于一组字符的集合:
    " Y6 o/ ~% E6 X+ }! P0 q+ }: D& b+ [% `0 w
    简写        描述
    8 a$ b" t0 ^0 K9 g: Y: u\w        匹配所有字母、数字、下划线: [a-zA-Z0-9_]( s/ h7 _, }# l6 J8 ]
    \W        匹配非字母和数字的字符: [^\w]0 C1 }2 p0 i! j; Q% e+ T
    \d        匹配数字: [0-9]  G/ }( e3 @* N/ b. h9 S
    \D        匹配非数字: [^\d]
    - a7 F( u( S0 @2 `\s        匹配空格符: [\t\n\f\r\p{Z}]  }+ _1 _' e1 B/ `- g5 G6 m  D
    \S        匹配非空格符: [^\s]* y+ S) u! P$ N/ L" i# T# p8 i
    \B        匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。  ?! Y% x, J( E
    re.findall(r'.s', 'Apple! This Is an Apple!'): |$ {4 Z. w  _' d4 f! N7 r1 e
    Out[37]: ['is', 'Is']6 {8 [. O7 Z1 `- _4 C& B: A9 S* H
    9 k- I. Y  o3 N" T) O  L  o
    re.findall(r'\w{2}', '09 8? 7w c_ 9q p@') # 匹配任意数字字母下划线的组合,但必须是两次, [7 D& ~3 K4 m/ Y
    Out[38]: ['09', '7w', 'c_', '9q']
    7 W1 Y- B3 R3 E: N- \' [# J& j* _0 W1 x% C  y+ p
    re.findall(r'\w\W\B', '09 8? 7w c_ 9q p@') # 匹配的是两个字符串,前一个是任意数字字母下划线(\W),后一个不是(\W)
    - ~' \, V2 l# J8 Y: lOut[39]: ['8?', 'p@']
    # L. y* \! i* C7 p1 c
    ( _% E$ M% Z! m+ lre.findall(r'.\s.', 'Constant dropping wears the stone.')' s& J) I: A# O) D3 Z+ C
    Out[40]: ['t d', 'g w', 's t', 'e s']
    & s" U4 T8 g3 W; V* o1 G! b
    * G2 S+ K, j) t& Vre.findall(r'上海市(.{2,3}区)(.{2,3}路)(\d+号)',
    1 G$ l" Q# M& f- z# t           '上海市黄浦区方浜中路249号 上海市宝山区密山路5号')
    0 J+ o$ w  @1 W# \: |! E, x. J+ V6 f' c
    Out[41]: [('黄浦区', '方浜中路', '249号'), ('宝山区', '密山路', '5号')]" c4 N4 Z9 a" }# r0 ?" ]; t/ n

    . g5 ]6 ?  \% D! k5 M1
    $ S; V% s. B4 i* G, y+ r2% w; n- ]4 J+ [2 L
    3
    ! Q1 `% }- b1 H9 v) B4
    3 Q- k& _$ E- ^6 T' D5
    # e! ?+ o3 Y% w2 |0 U! t( v6
    1 i) [. `# B3 v- l9 M79 Y' T5 |6 o' j! l' X1 t/ Y7 P
    8. Q0 V4 X; o8 D  d% D
    9* D1 a0 O7 q, S- a6 _4 @0 f, ~
    10
    5 t& o5 x$ x  A; }11
    4 N+ J! d4 z* q12, S; u: T$ h! ]8 E6 ~) y
    136 r% q; Y" z- C. O
    14# x9 I. X, P" V& [
    15
    : c  q0 a3 F: x0 _2 J2 }4 J' L16% s) f# n) a; @0 V9 x6 @8 e* U" `
    8.3 文本处理的五类操作# o( g) z( R6 Q- Q, i: N. k
    8.3.1 str.split 拆分
    1 I7 f+ f. n. Y1 }% z8 Q  str.split 能够把字符串的列进行拆分,其中第一个参数为正则表达式,可选参数包括从左到右的最大拆分次数 n ,是否展开为多个列 expand 。# \; n5 |9 p+ w

      K$ D  Y0 r) s. q' Qs = pd.Series(['上海市黄浦区方浜中路249号',9 L# `/ v  ?6 }
                '上海市宝山区密山路5号']); e3 B& @9 g3 p% w
    4 }8 m# {; f0 D

    2 R% w: V6 H9 s0 W" hs.str.split('[市区路]') # 每条结果为一行,相当于Series0 r3 s- z) r1 c. R# Y6 D" f
    Out[43]:
    " i! {1 {0 ^# e! u* R0    [上海, 黄浦, 方浜中, 249号]
    ; t" f0 t# ?& o  i7 ]8 @1       [上海, 宝山, 密山, 5号]6 ]; P  E! Y% h% f  y( q
    dtype: object
    % J' [) w3 ?  V* w/ o2 N0 y
    * c5 s! i' i; P$ |s.str.split('[市区路]', n=2, expand=True) # 结果分成多个列展示,结果相当于DataFrame% N6 V1 U7 m0 u! W$ x. c
    Out[44]: 4 X% ?# \, Q0 X3 K9 j- u' q: @
        0   1         27 _/ ]; X# M, p& [$ z
    0  上海  黄浦  方浜中路249号/ U9 V0 |6 \: X7 Q; w' w
    1  上海  宝山     密山路5号
    7 h4 \0 S7 }. _) O7 I8 M1- c" E, p: z( }
    2
    : |2 ~# J! u5 A# y3 c# M3
    4 l9 t! Q8 C1 o5 s% f1 Z42 n! K3 l1 C  ?3 S
    5
    9 y1 G4 Z6 B7 E, [# Q" m61 d  D; V( l1 l$ v
    7; G9 n5 P6 H" a' B8 F! }
    8$ `! Y6 Q1 _( p4 y
    9: E7 \3 z- u% J+ N. K, |
    10; |5 M2 w3 n* {) z
    11
    " F$ Q4 s' ]9 y& A6 J12
    5 h& P- F. u4 Y8 Y; P13
    ! k8 L5 Z$ p: R8 |$ f/ Z14
    & D! l0 r* @- l$ \/ {# c0 h) O15; f( T5 t7 V! O: I; {7 i, J( H2 C* o
      类似的函数是 str.rsplit ,其区别在于使用 n 参数的时候是从右到左限制最大拆分次数。但是当前版本下 rsplit 因为 bug 而无法使用正则表达式进行分割:
    ! l' J' |$ B8 u. \0 o- c2 W8 @$ ~5 }; N$ ?
    s.str.rsplit('[市区路]', n=2, expand=True)
    ) c. n3 A. t' ~: N8 z2 ]Out[45]: ) |6 |9 I( y2 W- n* d
                    0
    : Y" Y- B6 y# r. D5 N$ B0  上海市黄浦区方浜中路249号
    5 w; S* u4 @7 W( O1     上海市宝山区密山路5号
    0 O: m3 k" F9 R- c% S4 N. o# O/ l17 G- x: c* q4 g5 ~/ ?/ c
    2& }0 T8 s: F, o) z- Y7 c
    3
      s( y# l/ k6 D7 l' t7 I4
    ' ~5 y. }. r: ]$ j9 T5
    3 U8 w& V; E0 v8.3.2 str.join 或 str.cat 合并7 C- x: i+ C* V3 X$ i/ ^
    str.join 表示用某个连接符把 Series 中的字符串列表连接起来,如果列表中出现了非字符串元素则返回缺失值。
    , q+ q: m6 ~1 @& p& q+ j. {: d6 {str.cat 用于合并两个序列,主要参数为:
    ' o4 D6 Q) S$ Gsep:连接符、# V' w) Y! e! ~& _& o: v' u) {) R
    join:连接形式默认为以索引为键的左连接
    9 l* e  w0 \; z: d  Bna_rep:缺失值替代符号
    ( L# W* j3 y3 g1 E! Hs = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']])* n- n" d0 [" U6 n! \
    s.str.join('-')
    % r: h$ C6 v7 a& o' A! `Out[47]:
    + B# q) ]  w% \( C, [0    a-b) X, ^. t/ w3 H* U1 m* B7 F
    1    NaN
    # i$ F( ?$ b  v3 X2    NaN
    $ C" T6 F- C: E8 p* B/ odtype: object
    & n- v- u2 a7 x' z7 x16 X* X% @9 a( c: K2 l
    2
    * r1 s4 u. W3 u( ^( u3; o. ?" h) m& ?1 u3 o# P3 v& _: K
    4
    $ [( k" [9 }5 I6 ^( r9 M50 c+ R) Z8 A% L! Q" \  l" l% }
    6/ y6 I! g$ g! @2 E$ n7 W! D
    74 f) W2 F; {; j3 S  W
    s1 = pd.Series(['a','b'])7 }; n# l; Y% G( a9 Y
    s2 = pd.Series(['cat','dog'])) x/ g. P% q( z7 x  X4 U
    s1.str.cat(s2,sep='-')$ q$ t% v- |0 }% k( g1 j( z
    Out[50]: " a: e6 E& K2 \1 `, C/ @
    0    a-cat* k. V4 v# L( b5 I* f3 Z6 ^( N
    1    b-dog8 s; m; O2 Q4 ^; s' x2 a" X
    dtype: object
    1 ^/ y! ~0 c6 T2 A$ M0 b: y. o  q$ x6 Z) I( V4 r
    s2.index = [1, 2]
    3 b- R7 s$ k$ s' U) d7 q/ _s1.str.cat(s2, sep='-', na_rep='?', join='outer'). J9 \4 Z. K, H" Y
    Out[52]: 2 Y1 d+ Q. ~* T6 L- C" e, \
    0      a-?
    . l% t1 Y+ V* W% Y5 g1    b-cat& A) m1 G  G) q$ S: G1 ~
    2    ?-dog' P0 e; A0 b0 A
    dtype: object$ m. b  H! G4 N
    1
    2 {% L2 w1 s$ e6 J2 b6 T( C2
    6 Y% h- M4 x# T( T( V- ]34 d: Z1 ^# \( o1 T0 ^, O' G% H
    4
    8 G) l! x& B3 o8 A; n* L; p5
    * D$ x0 f- t6 j( b2 G9 }6
    + R2 ^5 S+ q6 K+ D7
    $ ^) w4 J3 @$ A8+ g/ [# c  }, J. U3 z+ ?5 S* v# y
    9
    + L  [0 C; x$ C5 |" g10
    1 g; X# y  K% L. ~; N, g+ c$ q11
    $ |$ ~& S6 x# }2 z" b6 d' S12- `9 Q6 b( G" |  Z
    13& i. p9 m1 @% f  V( {
    14
    " H1 h0 o% e% g1 p7 s1 K$ \- Y15" U: Q6 D0 n) D& c$ G; T
    8.3.3 匹配2 H( `, O% N2 Q* r! H
    str.contains返回了每个字符串是否包含正则模式的布尔序列:
    " i0 w( L; X% V4 V7 |* M$ e0 Y" Os = pd.Series(['my cat', 'he is fat', 'railway station'])
    % o' N- x; K0 ps.str.contains('\s\wat')
    9 T2 q6 ^$ ?$ I2 t3 r$ T& S# O! Q" ~7 @" l) [! Z
    0     True
    , ]% b+ H& Z( f# B. M1     True: k1 ^" O3 }: O7 ^' r
    2    False
    # _  w2 r* G5 Z( Y- Jdtype: bool
    $ v) x  A: J; Q  x) ?/ d1
    5 v$ L, N" E5 D* @; V' R8 U2" A9 [; I1 f+ `% C. P' F. R; Q
    3* O" |8 [# r3 T5 u0 E
    4
    " P4 M$ A. n5 U+ C: x+ i5& C# Q& g: H* q% q  U( ?1 D# B
    6: a4 j( l9 d0 N) D% W( w
    73 W' u2 Y5 Y) F7 }
    str.startswith和str.endswith返回了每个字符串以给定模式为开始和结束的布尔序列,它们都不支持正则表达式:# Z& B+ X+ Y8 S6 _8 X, K5 a
    s.str.startswith('my')' ]6 l( X* ?4 C
    9 p2 z# F) x9 O
    0     True% _" o3 C2 J( Y" ?0 H2 W
    1    False
    3 W4 u2 [" v$ [  D2    False
    - L* c' l' I& U$ [4 l$ p* g6 y  Ydtype: bool
      i& d$ s$ i1 O1
    ! e" J' u9 k0 u2
    8 U" F; K6 F; n  y8 Y39 H, M+ y  T) O4 @1 C  q8 n  I% b
    4. F& |" T  U, |: V
    5
    / ~& T$ `9 R, G" ^6
    ' V5 S7 z, G- P4 O% E2 \7 Ws.str.endswith('t')
    8 c) T# q" D1 K/ V& P* ^# E5 e0 F# M  H
    0     True
    $ [! _( b6 Q! q* ]- _; \; R1     True- e1 b. t* a6 ^. Z/ F- W, G: d
    2    False
    2 y" h2 g5 x: A- |4 t# Q/ `" tdtype: bool
    % m0 s: v- ~* ]) d; P10 k7 n  i7 t3 \+ I
    23 N' E! E  Q2 }* F( ]% e
    3
    6 o  e2 a! }" Q  o  c4) n4 z* v. I3 b% r; R1 E
    53 V0 r, g, i, h$ n
    6
    6 Y" b# D0 Y) W. }6 }6 h% R+ Tstr.match可以用正则表达式来检测开始或结束字符串的模式,其返回了每个字符串起始处是否符合给定正则模式的布尔序列。当然,这些也能通过在str.contains的正则中使用^和$来实现。(貌似没有python里的search方法)
    7 Y6 X- g4 x$ Y) P6 {4 Fs.str.match('m|h')
    5 b6 }- q2 f5 R) z7 ds.str.contains('^[m|h]') # 二者等价/ b% R# d6 Y  W
    , ]7 t+ s5 t. B6 r5 U. s9 V  Y
    0     True
    ! K; D; d. s5 c' y1 C+ n1     True
    6 `0 P( [: [  q6 s" x: O+ [2    False/ E7 X/ T; w6 }" j/ _  x. w8 C
    dtype: bool
    0 [  i; _$ e6 ^2 ^0 T1
      \+ a- @9 N  Z' ~  C( c% _" Q+ r2
    $ K7 T1 A6 C+ ?3$ _: Q" Y- m. Z% E1 r/ j
    4$ `3 a+ k; `4 v4 C* e: ?! ]
    5
    ; L2 ]. P1 G4 ?: ~% `" O63 `( P8 w1 i4 M! m2 D
    7' x( C% a; Z4 t, |7 ]+ {/ b
    s.str[::-1].str.match('ta[f|g]|n') # 反转后匹配5 ]4 _- p* O6 |+ T( `
    s.str.contains('[f|g]at|n$')       # 二者等价- r8 s+ w/ ?; d& E. E5 r
    1 W6 t( a+ B& Z# z2 t
    0    False) b4 y% a1 h" X- G2 f0 O
    1     True/ c, [1 d' x  V- k  Z! N) r
    2     True$ y3 W; P  n; @" C4 h5 T
    dtype: bool  F& ~( l' v5 j& k; @
    1
    ; M) H+ S% W$ ?6 }2
    , D3 X7 G, l6 ?9 x; T2 O8 w31 X* n! m0 R6 c: g( ?
    4
    $ K9 t1 V) L* b- P+ A5
    , }) O. H* @# [$ o# c* d7 t) }& a' b6, X& ?+ R+ x! [8 b3 r, Y
    7
    & j3 g, [; C$ |$ k9 u( g; @  S( Fstr.find与str.rfind返回索引的匹配函数,其分别返回从左到右和从右到左第一次匹配的位置的索引,未找到则返回-1。需要注意的是这两个函数不支持正则匹配,只能用于字符子串的匹配:! \. G& x# w4 h0 u# |2 R" c
    s = pd.Series(['This is an apple. That is not an apple.']): D, `+ ?4 q+ ?% I0 t2 k
    9 s4 t, t! E/ z, K" _  N8 V
    s.str.find('apple')6 l2 Q) g( l; W
    Out[62]: 5 F, o+ r7 L- w
    0    117 h* B% f. _+ l9 `" K/ k" B7 w
    dtype: int64
    1 O" }7 O) m7 {2 M) a( U( v% e1 `+ U( x
    " E4 F6 C6 ^2 gs.str.rfind('apple')$ d0 C7 j9 U* v. t* t% B
    Out[63]: 3 }8 x8 T, b, ]1 |+ s
    0    33# P: x1 \1 K, ~! z# {6 W7 K2 ^
    dtype: int64
    5 q; X# ]. ^( X# J9 M0 E. x1
    ! m) \/ b+ w5 S# {2# H: y/ j: V# L+ h" \! e& p" V! h
    3
    * o0 q1 r3 |: T! }+ ]$ }4
    ! L# w* W8 A3 ?& e$ m0 \( q5
    2 h+ ~1 d2 G1 v6
    + c, V4 g5 Y$ E0 l' r" J7- s* j. e  Q5 |) @; D
    8
      j+ A" H/ @8 d" g9# _7 n  \  ~( Q& c+ o
    100 s* ]  c& X: f: B7 m, ?1 u
    11
    3 u  |1 ^  z; a6 k& u替换
    ! ?* G  f1 N" T! s8 Cstr.replace和replace并不是一个函数,在使用字符串替换时应当使用前者。5 B8 a- v% w; V  Z0 H8 y" f
    s = pd.Series(['a_1_b','c_?']), `9 Y) p9 x' S# k
    # regex默认为True,表示是正则模式,否则第一个参数内容表示是单纯的字符串,也就是匹配字符串\d|\?
    ) `+ Q3 F8 s& X" O9 Cs.str.replace('\d|\?', 'new', regex=True)
    $ d3 k! d8 v1 _/ H6 o
    * k- j- f8 z* G- v9 l, f0    a_new_b
    9 ^# Y9 {1 |0 `5 Y# n: y1      c_new# y! J& o8 d  I" v7 U" W/ D' y$ B# w
    dtype: object
    5 r$ v, H8 {& u# {, I  s2 E1
    - }% E" }: {2 G# p2
    + W7 D2 E  ?4 s5 y2 N- A) d3 o& G3
    - U% e8 u- z' V2 ^44 h% ?. s: t5 M; m5 X2 f
    5
    5 \" r/ E8 E& k7 X0 H6$ W3 Q  @* w# I4 z
    7
    1 C1 |3 W, f8 ^% Q. Y9 ~  当需要对不同部分进行有差别的替换时,可以利用子组的方法,并且此时可以通过传入自定义的替换函数来分别进行处理,注意group(k)代表匹配到的第k个子组(圆括号之间的内容):( ^4 x% p" Y$ n. M2 h
    5 F6 L+ I8 G; O# \. |5 b" b
    s = pd.Series(['上海市黄浦区方浜中路249号',2 @/ J5 \+ m  F3 ^$ ^
                    '上海市宝山区密山路5号',
    7 W- S" |: f* ~' l& k0 T: [5 }                '北京市昌平区北农路2号'])
    ( w  n1 O+ V4 z" b# Opat = '(\w+市)(\w+区)(\w+路)(\d+号)'
    8 i( ?5 j, O  B. l8 z1 bcity = {'上海市': 'Shanghai', '北京市': 'Beijing'}' A2 N% \2 o6 Y
    district = {'昌平区': 'CP District',  Y- X- z2 Y2 G7 C9 w0 V' j
                '黄浦区': 'HP District'," d4 Y7 h2 c2 `% u
                '宝山区': 'BS District'}
    ) z% B0 o, X& _9 @+ a; O# _0 Troad = {'方浜中路': 'Mid Fangbin Road',9 V% K! t% E1 |) e7 W* G
            '密山路': 'Mishan Road',
    / g4 q2 e: {( K5 a' l        '北农路': 'Beinong Road'}
    9 Q( b4 J. [0 s' n- O- G% U" `def my_func(m):$ U9 e4 T. n* ?" j& N; u8 W
        str_city = city[m.group(1)]
    , V% `  l% r8 _! E0 [6 Y! Q    str_district = district[m.group(2)]
    6 v6 T. }& a, G" p& H    str_road = road[m.group(3)]
    2 a3 v1 H2 r1 m" |0 @    str_no = 'No. ' + m.group(4)[:-1]( v: J0 x2 F* t9 p) F1 `: b
        return ' '.join([str_city,6 h  `  D* h' t- o; z
                         str_district,' [+ P6 B( ?0 I. h# Z9 ]! d* L) V
                         str_road,
    6 K4 U3 L/ t# ~  v: d: Y# B                     str_no])
    , w: ~- t1 ?- F! ^) Gs.str.replace(pat, my_func, regex=True)% s' |9 o2 v% A; L
    5 t7 Z  ?. o# X  m1 O! ~
    14 o8 i; ?3 t( V& E, g
    2
    1 K5 P) @" e' `" X% S- P30 \& D9 n( _  ^
    4
    6 @+ n! ?/ L; t. j! e" `5' i+ U1 c) ]" X3 C* T
    6! w2 @6 i( G5 z# D. m
    7$ X; X0 s% z9 S, K4 o+ O3 D
    89 v  s9 M- `2 l. h) I6 O
    9* ]2 i1 k0 a# q" @: a
    10) v3 J9 o! R* _% [- U6 n
    11
    $ B5 n; a1 O. C12
    1 X; ~* a( O/ g5 p: Y4 b3 t1 n130 D$ H* I/ G6 f+ r+ V" o6 L
    14
      f- H3 Q8 j' I% Q/ v* F. Z15
    " v7 I) l+ K( @7 Z3 O/ F- \+ ]16! {" C: f; }% l  l, T% r
    17$ I- _$ q# |% x, w
    18
    ) c3 l. a# Z7 m" _8 E+ @* B, v19
    8 W4 F* p: ~& s, V# A* ~20
    9 H. W- t6 B. r0 R; w21
    " s# ~7 [- I# S; u( [( |0    Shanghai HP District Mid Fangbin Road No. 249
    : @/ Y& y4 }6 R: `3 u: o1           Shanghai BS District Mishan Road No. 5# Q$ H! Q1 E2 f5 i0 Z
    2           Beijing CP District Beinong Road No. 2' x2 w9 w2 w3 d( z* x8 D
    dtype: object" U8 `: s/ V; Y0 V7 L
    1
    ! _. N. u+ M# {% B0 k! ?/ ~# b2
    9 \+ C- a: k! J* i  Z! i) d3
    : x& Y& K/ i; Y9 ~0 _% z! r( T45 e5 x$ C- ]+ s4 Q
    这里的数字标识并不直观,可以使用命名子组更加清晰地写出子组代表的含义:  r! N( A- K" _% F8 K, Z

    2 ^& C" ?0 p/ d# Z: @$ K# v# 将各个子组进行命名/ q' R, j/ [( w. K1 l
    pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'
    6 u  ?( d/ H& Cdef my_func(m):
    ; P# Y/ O; Y" z" F5 N. Z. w. P    str_city = city[m.group('市名')]3 f9 k7 t% W- g) }+ s5 r0 `8 S
        str_district = district[m.group('区名')]: T5 l" Z5 _/ [, {9 C" X+ Z
        str_road = road[m.group('路名')], ]9 j" g( r4 h/ P1 s  Z( J
        str_no = 'No. ' + m.group('编号')[:-1]
    9 n; }! a' M3 g! b  ]    return ' '.join([str_city,) Q1 t: y1 q1 m6 f9 N9 a
                         str_district,
    & M0 L& D# l; ~/ H7 {4 }7 l                     str_road,* c5 w& b- S4 @4 }( z
                         str_no])
    + F7 n# N/ S7 ?: E' Ls.str.replace(pat, my_func, regex=True)! \2 n' f. N+ I
    1
    / M2 \( D1 Y+ @8 j, c27 k* A' i4 `: c8 @
    3
    8 t- s3 v( ^: J4 z/ g$ m$ f, t4
    9 D7 u* @. \! R: B% [2 |5
    & Q. U6 d/ h: i6
    ; n. P) e2 R# O4 @: I; D7& Q  h6 J2 g$ W/ l# ?7 Y6 m
    8
    9 J( \' R' S3 i4 P, L9 u9! \# S* T& ?, W$ O. }
    10& S+ g, C" ?5 L3 T/ Z" n
    11
    " U5 ^, y+ t& H$ e1 `# ?# T* }8 E2 b12
    6 D$ V% S# \# O/ Z8 V% x% \0    Shanghai HP District Mid Fangbin Road No. 249: G. @7 h) ^) }" X' u
    1           Shanghai BS District Mishan Road No. 5
    . A2 G+ F3 b- y& A8 U" C- Z) r2           Beijing CP District Beinong Road No. 2
    7 V3 d" i1 {; O7 |/ ydtype: object
    5 I8 E1 B+ Z- f" P1
    6 C1 H- f4 G9 }# r- W4 g. r2
    6 k1 T: Y) e" U8 s$ o) Y3/ q. r  P1 m3 v4 {4 H
    4
    ' g. L5 K! _& t9 c- v! [5 H, k( \  这里虽然看起来有些繁杂,但是实际数据处理中对应的替换,一般都会通过代码来获取数据从而构造字典映射,在具体写法上会简洁的多。
    & S) c+ ~# X) t
    % n  F2 `, c6 Q8.3.5 提取! Y' B6 l1 W$ k' k8 G
    str.extract进行提取:提取既可以认为是一种返回具体元素值(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的str.split例子中会把分隔符去除,这并不是用户想要的效果,这时候就可以用str.extract进行提取:: D8 `' S$ z4 ^
    s.str.split('[市区路]')
    1 D4 z; s) S8 k; J/ q5 jOut[43]: * C2 E3 N% T3 n
    0    [上海, 黄浦, 方浜中, 249号]% i0 k/ e0 |6 b! }3 D+ m4 M; w  U; j. w
    1       [上海, 宝山, 密山, 5号]  V) c+ h3 g8 z7 l3 A
    dtype: object
    " o1 {" H  _3 l0 X
    : _" P. {1 {- D. Z- T1 R7 Rpat = '(\w+市)(\w+区)(\w+路)(\d+号)'
    ( |% S$ [0 `& ]" ?  `s.str.extract(pat)- v3 j* y: ~7 ?
    Out[78]:
    2 i& b  i* l/ \, h4 I' c4 ?3 O    0    1     2     3
      T  P! ^% M' f0  上海市  黄浦区  方浜中路  249号% _5 Z9 G' E6 v
    1  上海市  宝山区   密山路    5号! ~: d, L, J4 c4 {: @
    2  北京市  昌平区   北农路    2号
    ! E, p9 }$ N+ T1
    % W2 P' s9 _% S2 o5 F" K2: h/ k& D) L' ?4 f
    3
    4 ~/ Q0 b: @5 X5 v) f4$ B  R# q) V) X' R3 h( m; r
    5& w  m8 _' v9 k; D; E
    66 Z* _2 r" Q7 L1 z
    76 P  E8 w' L- R5 ]0 c
    8/ P1 `' q4 S. {* |  a- N! k
    9  P! c! O  y5 e0 K' s
    10' q& F+ `# {7 W# N; G2 a
    11
    & p! w2 C2 E3 v12
    & G- H/ |/ \' {. R4 y; Y4 o13
    / Y# n# P$ m; a- \! G, k9 w通过子组的命名,可以直接对新生成DataFrame的列命名:
    ; U( _7 b8 T, T1 }' `9 Z% P6 e/ l/ `, `$ B- x3 E. S
    pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'1 V, ?, E, [( @% @# H- A) A5 @% z
    s.str.extract(pat)
    ! L! R' k# d3 T1 WOut[79]:
    ' _- k1 o% h- z    市名   区名    路名    编号7 w, l' m! K7 I2 m
    0  上海市  黄浦区  方浜中路  249号
    5 J9 V  E8 I) Z7 U8 F0 M1  上海市  宝山区   密山路    5号
    - j! Q3 V+ o: u$ P5 L8 `. d" ]2  北京市  昌平区   北农路    2号
    - p9 W3 F3 r% l5 P  P1 b" o1
    - L4 D2 f7 F+ }- y9 m3 m2" i, S/ i1 ?( y' y
    35 c' f0 U# O* Y/ }
    4; O2 p: m  w% h6 ~0 p- K# j
    5. A, `! N, \% I, n+ l: F) P
    6
    ; `* q  G# m- E8 Y7  \' z; d: k) N! S, N; Q& z
    str.extractall:不同于str.extract只匹配一次,它会把所有符合条件的模式全部匹配出来,如果存在多个结果,则以多级索引的方式存储:
    8 b% u% s: M8 y4 O3 V; W" Ks = pd.Series(['A135T15,A26S5','B674S2,B25T6'], index = ['my_A','my_B'])* N6 m8 w; u# [) o1 o3 R
    pat = '[A|B](\d+)[T|S](\d+)') B: Q5 F+ ]6 C+ S3 l
    s.str.extractall(pat)
    & e/ G  _1 n& dOut[83]:
    + U" j  U% {( Z       0   1
    - \' m/ w' }; n1 M7 z" o- x: q     match         0 K: g/ o* W3 M: u
    my_A 0      135  15
    * j# _; n( m, P& M2 p     1       26   53 d* n  z' p, b2 `9 M
    my_B 0      674   2" b% h% S$ K5 U. h. B
         1       25   6% O% y4 U% n4 R+ q# u1 u
    1
    & n0 G& f' c; D0 i- ]) B23 S% B& d( s. s- R
    3/ I% g* U+ X) R
    4) Q" V' f4 \7 y0 M$ t; s
    52 @. q: r- Y/ X; V/ m
    6
    - w# b! Q0 [1 N  X* P# e. o7
    & b1 x9 Q+ L9 H! O/ E8
    $ c, D* D5 {1 q% P9
    6 G7 t' s: V9 \1 @10/ L% I6 C1 h; a
    pat_with_name = '[A|B](?P<name1>\d+)[T|S](?P<name2>\d+)'
    8 ]9 y# |- f' X7 P% Ys.str.extractall(pat_with_name)
    4 s0 `: N; P. Z7 @+ G; iOut[84]: # T: @# A7 v9 Q4 `! H7 j% n
               name1 name2
    ) D$ G' }. b  O     match              w8 d9 N- U% c" `; y5 Z# S
    my_A 0       135    15" F( k1 ~" |! A6 e
         1        26     5
    - A9 B2 m0 _! @1 W9 C. pmy_B 0       674     2$ h2 ~. x% W7 Y: O& r
         1        25     6
    ' n& n3 x% _; N4 M4 m1  b! D* X& x! P8 T) x) }
    2
    3 C  N4 F: Z, ^+ z3
    , C$ z6 g$ \/ u* M& c7 Q0 L! q: s4" e3 H7 [( c1 n6 J. _  j
    5" F) |8 v9 p& m! o; V" |7 v7 y% N
    6
    9 o4 m1 u1 R+ E+ d: S' Y7
    & j  ^1 Q  @! B+ P8+ y4 U+ ~- v7 C7 l' }$ @
    9- u+ f6 @5 R; z  B7 {
    str.findall:功能类似于str.extractall,区别在于前者把结果存入列表中,而后者处理为多级索引,每个行只对应一组匹配,而不是把所有匹配组合构成列表。" q+ i! B1 f% z
    s.str.findall(pat)
    * T% f  n3 ?, Z- ^, A" f4 M1
    8 b* y4 @! U, R2 }5 \my_A    [(135, 15), (26, 5)]& K. w; F- d$ y" o9 b( G
    my_B     [(674, 2), (25, 6)]7 t) L) x" S! I
    dtype: object
    2 Q: _9 r: W( y9 P: y16 A; D' U" a+ b2 r% Z& h, _) ^. n& E
    2  x$ O+ K; U; A; S8 v% {: d
    3  C5 M; B' [9 I! }/ u; X& @0 G
    8.4、常用字符串函数
    . S: ^+ E. ~1 h! {9 X  除了上述介绍的五类字符串操作有关的函数之外,str对象上还定义了一些实用的其他方法,在此进行介绍。
    # K) I4 l9 _# r7 S! r4 m' v/ d/ \% r/ [8 ]* [2 m! E  f7 s
    8.4.1 字母型函数
    ) n1 p" l* r5 @7 U( R. X5 x# J  upper, lower, title, capitalize, swapcase这五个函数主要用于字母的大小写转化,从下面的例子中就容易领会其功能:. E; ]3 J# k) l# [1 r. p

    $ V9 I- e- J: gs = pd.Series(['lower', 'CAPITALS', 'this is a sentence', 'SwApCaSe'])
    8 E! g' d: \/ L
    : H' T' \, M/ @7 ~s.str.upper()
    $ ^% d! M6 u' X8 cOut[87]: . s, `' Q6 D6 n5 b- L3 u; A
    0                 LOWER9 r1 X- Z% z9 `! R3 Z6 m6 b
    1              CAPITALS- ?* M- k) D+ ?* G& y
    2    THIS IS A SENTENCE
    6 i3 r1 h7 X% R) X: Y, B3              SWAPCASE1 G7 T$ `7 {6 C; M% O
    dtype: object
    8 I  \' N5 z4 r' T# }; k' D! a) W8 ]
    9 s+ _6 d6 Z7 \: T* v% F0 ws.str.lower()
    + l3 v& w% V: [% F/ ?% K/ n0 k  JOut[88]:
    # x7 t: g+ ^. e% g0                 lower
    8 a1 |$ T; w4 k$ R% D1 G8 c1              capitals
    4 z; r' z$ g5 `2    this is a sentence8 I4 ~7 P" J  k; U5 K8 [" C
    3              swapcase
    % q1 S3 ~; _0 ?dtype: object
    # [2 [( b* F4 @
    . V$ v  V5 X5 x1 L. C7 h2 k3 L/ Us.str.title()  # 首字母大写- A( }. L( p# h1 k* F5 ~
    Out[89]:
    & {* ^' P5 t9 ~2 C& {0                 Lower
    1 D# _9 e. _9 G, Z1              Capitals1 {0 A" l* B; T8 j0 r" B* g: }
    2    This Is A Sentence
    % L- |- ~* l5 ~% h3              Swapcase4 g/ b3 [  d2 b2 b; _
    dtype: object- i- D5 U1 Q8 D) Q
    & @  o$ ~& u7 s' b: k0 {
    s.str.capitalize()  # 句首大写/ ^) }# m! c# p; K5 R
    Out[90]: # J$ V- R1 [4 P+ h" R
    0                 Lower
    ; G/ T& ]9 }- q" r. v1              Capitals# S/ e  X- Z0 K2 R
    2    This is a sentence; O5 k& |' W) S/ u
    3              Swapcase* v$ B3 ~3 b* U  c  C
    dtype: object
    " u- X: `5 F/ k) i! A' P1 M: o+ y8 E% }' _* p) J4 d. n# ?! Z
    s.str.swapcase() # 将大写转换为小写,将小写转换为大写。: w, x. {( m! t; l% N* y
    Out[91]: ( f, p# Q7 T6 u) q9 E' q, C
    0                 LOWER
    , i5 R) U+ t7 J1              capitals
    ! G2 c4 P6 u. N# _3 o" {3 M2    THIS IS A SENTENCE* ^: f" U1 W! P6 t* {& s
    3              sWaPcAsE1 _. \4 z: i' u! z
    dtype: object
    # |' p: s  v8 A( d
    7 j1 t2 o5 s* n4 {s.str.casefold()  # 去除字符串中所有大小写区别" M( u4 n+ W, N) N6 t# H
    7 J1 C# V5 B7 U9 o
    0                 lower
    / `# @7 Z# V: l7 `1              capitals
    ' C1 ^8 h# O+ P  [2    this is a sentence
    . t; h' n! J- ^/ A' V3              swapcase9 I! b, E8 }7 W
    ; Z2 o  `3 l. X
    1( m4 D# V, Z9 l" {2 k
    2/ N  Y. j& c! C: q% _7 w
    3
    ) O. W' ]! b0 N$ E- x- S43 `9 Z" M1 u0 |
    5! P8 c4 \6 W# X2 k% f3 c
    65 \, U! F' d1 F$ P, G1 b0 e# Q
    7+ B- G2 ^; ?4 j$ z. _: A7 [
    8. U$ C* v, G) ~
    9
    ) Z  B1 V' o7 w/ k. h8 N10/ m( _7 u- R+ |0 i% H
    11/ S/ L: u1 U1 O1 K+ r4 }" g$ S
    12
    + D1 S/ N: m; F, p136 V! r3 ?: a9 ~4 L% Y0 Q
    14
    " j2 h- g, V% r' x+ ]1 L15
    5 t  q0 x0 u7 j" q% n2 E& M: ~16
    & m* B# a6 [- Q! ^17
    * y$ J! V6 @9 v; _( ~9 `) K6 D18& @& ^, r/ b/ V/ M. Y4 w! K
    19. W6 C% d8 m! d
    20$ W6 W( j* o$ D
    21$ j! z* C% v" u0 x- F: T& {4 p
    229 b  f% J6 i+ \# }. i
    23
    2 o/ y1 m* _4 t* q; Y, s8 {24
      v+ ^- J% R- e- a4 E25  K$ w9 a& a* ~8 E4 _+ @2 O7 w
    26
    - {1 ~& H0 Z+ S0 P. i$ U27: l* i4 y5 u0 G( n
    285 j, n* B1 Z; k
    294 q' y) k5 n. c+ W" _. i
    30
    ' u4 }7 h  b. [! Z" s31
    * v* j. c4 ?2 a322 K6 J* J  v) @. K, [* C9 r& y
    33: O  E3 Q) ]7 ]6 M9 F4 B6 U3 E( _
    34
    ( {, @6 u0 t( r- Q' d: Z' [35
    ! Q& ~, c% u% `2 _: a36
    % ^! @& s+ c1 I$ C37
    9 \1 j5 G5 z% t386 f$ {; c8 \1 u4 c) G. v
    39! Y$ _- t8 i$ |- q/ X4 L% s
    40
    2 ]  J9 d  h9 F) R1 U9 r2 B419 B8 Q4 Z5 v) b
    42$ W2 o% I* B6 ~0 _8 Q
    431 H% P3 x$ u- Y  M0 g( ^4 V
    44. @  M: g7 t: s( W) L$ s
    45/ g/ a; B, \3 `3 j0 e+ M; M
    46
    8 G0 x. Y# |: X! b4 N: ~% c& c47# O8 ^/ i5 X8 y0 |
    48) }* ?3 R6 ^; t* v" P% [& w7 X
    8.4.2 数值型函数
    # L0 U* {5 ?( Z' d6 }6 r% d+ g# D  这里着重需要介绍的是pd.to_numeric方法,它虽然不是str对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。其主要参数包括:
    0 [1 r  t# |" m4 }+ |. x; m, A* x+ O4 }8 _! j  V
    errors:非数值的处理模式。对于不能转换为数值的有三种errors选项:# I4 r; p( K: q$ @
    raise:直接报错,默认选项& j. G9 _! f3 c3 k8 J
    coerce:设为缺失值
    0 `9 u) M& X# ?3 Z; s: h; qignore:保持原来的字符串。* a' Z# m. z% N0 O! {  O! b8 F8 ~
    downcast:转换类型,转成 ‘integer’, ‘signed’, ‘unsigned’, 或 ‘float’的最小dtype。比如可以转成float32就不会转成float64。. a9 x- F( Z5 S) W/ `. F
    s = pd.Series(['1', '2.2', '2e', '??', '-2.1', '0'])+ I- l0 L+ t  F7 z. q
    % }+ s9 e. S6 o) }: d
    pd.to_numeric(s, errors='ignore')
      Z6 _0 U5 h7 ^+ [- i( _# LOut[93]:
    : i! K% l+ F4 D9 P! b( N% |& K) ?0       16 l$ m# Z, \( y- U- x1 ^/ K
    1     2.2: W2 `! B" ^# x1 r: ?& h
    2      2e
    6 b, \% z, _0 C7 v+ d3      ??( A" d$ n! o6 v! N) h( k* A- |
    4    -2.12 ~  S$ c/ L, r/ \; y8 G( j
    5       0' L+ W3 Y/ E# p, L! z
    dtype: object
    5 _+ P5 E2 x1 e. b8 w+ r5 o! t# o" ]. I3 S% Y4 G- y
    pd.to_numeric(s, errors='coerce')  R! F- c- j- ~: B8 d, e
    Out[94]:
    ; q1 i) S3 T$ y% n) j+ H0    1.0
      p7 Y# _3 a% d# R0 N$ F/ E# l1    2.2, L/ P& c, B/ R3 r8 I8 x# u8 Q+ U
    2    NaN
    . U& s( o$ g" Q  |- l! z( h- ]3    NaN+ h- r' u" N, Y- C+ k& x9 G
    4   -2.10 W  F- @/ ^; x2 v' x
    5    0.0% @+ a/ i, |+ h4 X6 g/ l: Y
    dtype: float643 V- j% Y' Y/ k% P
    8 F) a0 r7 n$ Q  g6 r, K# D
    1% l! {, M! i3 u7 o7 i! I
    2; p9 `6 ~: N/ p. n% p# [# _
    37 k( h4 j# m6 \: K8 E
    4' ~! P# K  P  [1 ]" D
    5
    8 K8 V1 U" {: ]/ W/ B6: M) |3 o3 H) p' W
    70 Y" K+ b; Y# T* ~4 h; @
    8
    ) K# w, D. G+ W& ]: f9; w- |3 R7 W& x
    10
    - \, ]) D, U$ f2 g$ S; k8 \9 w4 n11
    ' B! R7 y: |! y3 G12
    1 g& {+ i) `- _9 q- t) V/ b( a% b13. _& {# T) D: E% A* A% S  i# ]
    14( y! f9 y% N+ M& H$ D
    15  j0 N4 l8 x9 P% z
    16+ l$ g1 _+ Y3 f
    17
    4 E/ e' ^' F6 ]# B  \18
    - e$ H* H( [& r( p( j( ^5 v19
    2 D& {0 K) r  d! E/ `& [6 R% |20+ W3 O+ L$ F; Q
    21
    ( b% T( t3 F* x3 q; F' x  在数据清洗时,可以利用coerce的设定,快速查看非数值型的行:
    2 T% X: P/ S  n' x) D
    ( l$ j- ?+ l$ r7 W6 X* ]0 js[pd.to_numeric(s, errors='coerce').isna()]& ]5 g$ ?# Q: a' Q' h" g
    Out[95]: 1 @- m% d; D" T
    2    2e
    2 _/ b3 }8 {" Z! x5 r3    ??2 b; f$ B# i, C/ j+ }. u. a
    dtype: object
    ; @8 i6 l  I/ B+ m) Q' w+ x* l) _- e1
    1 N& z5 h: I' k0 ~2
    % l- r5 R. ~0 c9 b! `$ p* \3
    2 w; X) M, R) X1 R. i2 Q% r' ?0 j/ u4
    $ C& q4 K! S; Q$ a5
    ! C, I* l1 ^$ A+ M9 W8.4.3 统计型函数; o2 t; z* |6 m& `
      count和len的作用分别是返回出现正则模式的次数和字符串的长度:
    0 Y, A/ ^: _; n; c6 |% h4 u- M- T$ U
    s = pd.Series(['cat rat fat at', 'get feed sheet heat'])% u# c) P+ o6 k3 L3 ]

    : u! H' k6 T1 T& n- z3 c6 g# _s.str.count('[r|f]at|ee') # |左右两种子串都匹配了两次
    * d3 j& W8 X  Z3 ~. Z; r8 mOut[97]:
    9 e$ T& Y  a* X, g! f' R& g' G0    2
    6 T" G$ |$ d' R: t1    2
    ' f, r+ a- l& b3 @  Fdtype: int64
    1 I0 e9 v- i9 ~; }# {
    2 Y: O1 x0 v- ~- q8 H; r3 ls.str.len()7 i5 l1 y& Z/ T
    Out[98]: 8 M0 o* D3 v  m4 P' e+ |% V
    0    142 _! [+ M) o* K/ M# T: x: ~
    1    19" {0 n+ z9 y5 t" f) ]* \" e! j3 n
    dtype: int64$ I" W1 C" v, u" n
    1. s3 H) r2 f# b- [
    2' ?" H& B) @, a2 L2 l
    3
    + x% F8 e' y4 G0 z4 B4
    ' v% j2 o3 F5 m1 g5! f. i! t, Z9 m0 `( W
    6& n- k; W. A2 S
    7, a& t& H. ^2 ]; \
    8
    2 u2 c5 t/ W6 {. \+ x+ p+ z& [1 q" ^: C9
    9 c" b) \: A6 K5 B) O. D. Y10* M  c; u1 a) W! v8 n
    11
    8 B$ V6 ^1 G" S# U8 {7 g120 Q* ~; J5 d% L
    13
    9 K) e9 B# P  ]9 ]$ n* M8.4.4 格式型函数" q( b- `' ]+ m/ }( i) U7 E* \7 K
      格式型函数主要分为两类,第一种是除空型,第二种是填充型。其中,第一类函数一共有三种,它们分别是strip, rstrip, lstrip,分别代表去除两侧空格、右侧空格和左侧空格。这些函数在数据清洗时是有用的,特别是列名含有非法空格的时候。
    ' a3 Z9 n3 E' _; v  T4 E
    3 E$ F& B  e1 R7 d1 Xmy_index = pd.Index([' col1', 'col2 ', ' col3 '])
    / W" c2 m1 }) ?' ]8 y# |1 C% w, Q% W2 e; L
    my_index.str.strip().str.len()
    : T- @1 }. K' G+ v. rOut[100]: Int64Index([4, 4, 4], dtype='int64')
    7 w7 f# E6 K5 j& k  H# u$ z
    5 l0 ]" y. ~* ~my_index.str.rstrip().str.len()
    0 p5 t/ E' J. l: \4 ?Out[101]: Int64Index([5, 4, 5], dtype='int64')$ L; T, N0 Z  h  Y

    & D6 i$ `- K4 [5 y8 r; f5 q6 u4 _. Bmy_index.str.lstrip().str.len()) I8 F; ?; s5 b' l2 [% Q7 B
    Out[102]: Int64Index([4, 5, 5], dtype='int64')1 u1 Z: g# k; O; I* ^
    1
    3 e$ d4 L1 y+ c4 a5 d$ ?2& U  h! t3 h, |5 g" r
    34 S7 P0 P' j2 @* S. T5 b% J& H) I
    4- h0 X  L3 a3 ?5 @& d# [
    5
    + ^  I  y' j, z- |% D+ [$ t9 k6
    & J- T; A0 u/ t3 M3 e7  ?6 m. Q# ]; E' P& @
    81 s+ c7 z9 y1 a  v/ L5 [8 b* |: [
    9( `& O. y7 z, l. U, X
    10
    # K( @$ A7 p- ~3 U0 D* T# n  对于填充型函数而言,pad是最灵活的,它可以选定字符串长度、填充的方向和填充内容:
    9 Q! a4 e% a( M8 n5 `" u' r" E7 c8 b' G$ h( ]: R; ]3 v
    s = pd.Series(['a','b','c'])
    , C3 A1 g1 F1 f* B
    % O& x# W* |: E  |) w2 Ts.str.pad(5,'left','*')' s! n8 O' ~6 A6 K( C% D5 y
    Out[104]:
    ) D  T# O2 p% ?+ R" d5 Z9 E% u9 Z0    ****a
    $ |' h5 G$ c/ _, {; h+ Z$ @1    ****b' G" Y' C$ D3 C! H
    2    ****c2 y, T/ w- J- Q+ k2 g
    dtype: object4 i5 I3 V. e9 T' {2 N) w
    ( M) o; F3 f: t* D4 B; o
    s.str.pad(5,'right','*')
      J- u3 t% N# ^0 aOut[105]: ' \% t, x; U0 i/ u* N1 s& u
    0    a****; `8 j/ o- l# X$ [
    1    b****
    ) c$ W# s# ~! D! R2    c****) j5 }  ?8 F& R" \( X0 w8 @: G3 ?
    dtype: object7 J3 T4 ]9 J# t) z# X2 Y: L

    . t6 @1 c/ a7 _s.str.pad(5,'both','*')
    + L7 I5 s2 G( TOut[106]:
      J# ^2 C: |$ L: Q0    **a**+ N% x$ U: U$ G1 b% J4 j8 g; V, ~
    1    **b**/ ?, ~. r8 ^% F! e
    2    **c**3 K9 W$ E1 ]& D7 [0 B) v
    dtype: object
    0 g9 x6 L" |0 @
    5 w! {8 s+ e+ I2 w1 r+ D  a$ C1: Y# Z* b1 j4 X0 r
    2" H, w  }5 A, t8 f! u2 L" D
    3( ~2 r' p" O5 a$ w4 Y" c
    4  M6 Y6 D0 M* t' |) ^
    50 l. c) L1 B5 j# g3 {9 r6 G# k1 D
    6
    ' B6 H$ G3 d" G# F+ v74 i$ ~9 Y  `" U/ |
    8
    7 d9 a  |6 I8 l* Q; j95 G5 D! U# u* p
    10
    4 ?. {# E& W/ w: ?9 P9 @1 \11- `# t6 X1 l! J$ @: [1 E& z+ g! a
    12
    * X: \) C& K5 A3 Z2 C0 Y2 T13) A/ G$ J, f& I0 R9 }1 q
    14; m! Z" x% O: @2 [9 K
    15) R7 o6 `: A' M" Q# o  Z$ m! ?* Z2 H
    16
    . m) ?/ b* I5 N$ v3 n$ X  R179 g( }$ P' S% [
    18
    ) b! [# H4 a' M! h- B+ @4 ~5 B3 @19$ L8 F- E6 g! _( c6 `+ R
    20/ @+ r7 t  Y5 C1 @( X  H( S9 J5 K
    213 w- G( r: ~" N1 X- u, g/ b/ U. V
    222 `* S+ x. Q) z: ~" c+ W, E
      上述的三种情况可以分别用rjust, ljust, center来等效完成,需要注意ljust是指右侧填充而不是左侧填充:! N6 r* l  ~% ?" x1 O

    + T, a! p3 W, D8 e- }* _0 t6 P: Vs.str.rjust(5, '*')2 T" ~# j, Y, N( _% `5 l. t
    Out[107]:
    3 B* l3 n  m- N5 e0    ****a
    7 L# m! t. z- j& c/ z1    ****b
    + k+ @6 |; i1 ]* q: z% H  ?2    ****c6 }, b* `/ Y: M/ f0 Y
    dtype: object
    8 x! Q$ @/ h) {( n7 T/ c. J8 z% E* q, `! f$ ]  C( j3 u* u& |
    s.str.ljust(5, '*')
    7 M( e; Z3 {/ P9 [$ U/ Y/ JOut[108]:
    8 A4 D8 T8 e. s# B1 l8 g0    a****0 e. f4 k0 L- }6 s& A. K
    1    b****" D/ U; x+ D* X3 _; \" G& J: ^
    2    c****0 n6 V- v$ w3 n8 y4 I: w
    dtype: object" @$ x; n' u6 L& K% c! G( z

    # s. j$ G, {% W" gs.str.center(5, '*')
    * n) a6 K" N6 x+ g3 c  A4 X& pOut[109]:
    9 X5 j- q. C, M3 H" q& Q: I& i0    **a**; p( j1 ?5 b9 k0 {, i2 K, g
    1    **b**
    , h( y3 ~* D7 g7 E* O3 R2    **c**& V. i" Y  @) L6 B5 ~  k* @
    dtype: object7 ^7 J( M, l* i& B

    " E. q. R9 `* g4 Z/ L, s# \* P6 c1
    $ d. X1 C" ]: n; c# b1 L2
      w9 X" L+ W8 _6 e3
    1 R3 \; @7 r" ^2 t1 |42 C+ A' H2 T. t, R
    5' ?9 ~+ b4 {- y
    6
    8 L1 H+ ~  L0 ?' W( q76 O+ x& ?) }/ V9 o5 q
    8
    7 }& e8 p. }  A! Z9" C9 t* b7 Q6 F: l. ^* t
    10
    . ~, b* z, W8 b+ D7 t11: T/ V9 _4 I( y$ A
    12. O( A2 l- ]5 |  j( v4 |
    13
    5 _3 V  T" S0 R7 k5 x, A5 c0 ^9 J- K14
    6 G% U7 l' y9 f6 b" o4 p$ x15" Z' {* |$ @2 u$ d, k! T, n
    16
    ) @! j: O. @7 J  ?17
    $ I' ~; j5 |! B& `5 }) b5 ^" T18
    $ ^. ?- B5 l8 a9 z/ W$ a19
    1 y; c/ V. v8 s9 Y$ S  L# X- x, S! i20
    ) r* r9 _' ^0 H3 j  在读取excel文件时,经常会出现数字前补0的需求,例如证券代码读入的时候会把"000007"作为数值7来处理,pandas中除了可以使用上面的左侧填充函数进行操作之外,还可用zfill来实现。4 `/ f3 w& _  u, X2 P) Z+ u$ l
      n/ s' S) d1 @; Y7 \4 [
    s = pd.Series([7, 155, 303000]).astype('string')
    ; l5 {! {, {) J2 o
    3 r7 t0 g: \3 y# fs.str.pad(6,'left','0')  L% k& @$ y. Q& J7 d
    Out[111]:
    * Q0 o. Y, S( P, P2 V0    000007
    ( b& X; {" _2 a; B# n9 Y$ D1    000155
    $ `. J0 R9 d3 h' o2    303000  h# b3 O+ X2 }0 L2 m+ R2 m
    dtype: string
    , r* r0 ]( v! K! D9 k
    ; u3 Q# P' s" z0 c- M) Y7 V9 V" zs.str.rjust(6,'0')
    9 \* U! p3 O) \0 ]Out[112]:
    $ k8 l3 v. `+ z. W7 t5 u0    000007
    / b( h% a! T" L" l8 @; p1    000155* {; h) b  Q! J2 l7 |" ?# j
    2    303000
    ! Q# k1 V: G, o- l5 e2 q- J$ vdtype: string
    ' r# @& h# e# K, O" w2 ]% B) j4 ~" R# s- y1 i6 H% i
    s.str.zfill(6): _3 W. y3 j" p9 E
    Out[113]: ! n  B1 Q2 n1 _7 Y2 [, z
    0    000007! ?5 D" `5 M" H  [* f7 Q$ k: a/ [
    1    0001556 M: G1 |8 s/ Y* |# n. F: S# E9 C
    2    303000
    : V1 E0 w- `; W, A/ a3 @dtype: string& w( j: d) W0 p  ?* J$ Z5 z4 m
    4 {5 H) r9 L4 y% ?
    17 n! J3 K& _$ A+ {5 }6 |( S8 M5 u- S
    2' G. U4 {3 P$ {. i
    33 b: s3 Q; D# s
    4( C' a! g3 w6 T
    5
    . D" C; d* w6 T6
    ( A2 Y1 s7 f) [9 P6 p7
    ; P( N; V2 b  l, W3 \+ e* Q8
    # ]/ i* p5 k( l. G# @" N97 S3 P& v, L$ U/ m0 ~
    10, F' G% a9 D7 r# m: x+ \
    119 I: d& c+ n; G
    12% ?" F( F. J2 Z% r' Q8 z
    13
    ; p# `8 s& e9 T2 I* V14
    7 {& h, }- h& P15
    6 H% q+ p; @" z  Q% w1 W16
    ! M' Q, G* W7 d* q17' T; k0 r) D9 o
    182 L% M- e) X2 M8 S7 j
    19, o8 g* m  f4 ^: Y+ B5 `8 L$ T
    20
    : v7 d& p3 C* _7 L% u21# J, e& Q6 `" o; e* D9 D
    22
    $ G% ]% X( m( u9 v( ]( p0 \/ B8.5 练习8 @6 @+ x# T8 y& V3 q7 K; _' y  |
    Ex1:房屋信息数据集' s  P2 s8 \- y1 V6 U2 g) F
    现有一份房屋信息数据集如下:& R$ r, Y  {, s

    6 o3 m' _+ ^; N; J; E( edf = pd.read_excel('../data/house_info.xls', usecols=['floor','year','area','price'])
    5 g0 k* Q* T6 `; F5 j- Z" ]4 F& _4 \df.head(3)9 |+ i! w! |  i) ?6 \: y
    Out[115]: & D2 M2 _- K# [1 b) m5 j* V
          floor    year    area price
    & z1 s$ {0 f& p/ s. t( c+ }0   高层(共6层)  1986年建  58.23㎡  155万. D9 F' K/ K9 R; s, O/ @. G
    1  中层(共20层)  2020年建     88㎡  155万# h. h/ W4 l) G3 _+ q2 d0 S6 T% D
    2  低层(共28层)  2010年建  89.33㎡  365万
    7 A# A2 W: @( p5 Q6 Q* E, x1# o) C( O, h, Q2 l9 O' _9 s
    2
    8 g# r( `1 h$ n3: ?- D1 v8 Z' m1 D6 {) B
    4
    9 s4 M. k+ F, l  s# |& |/ n" m59 `* ~9 e+ Y, v& h
    6& k7 h% f( m6 |/ N9 m" y6 \' `6 Q
    7+ c! k) y9 o1 V/ A4 S* d, c
    将year列改为整数年份存储。
    ' d/ |" O5 O4 v' J' o8 R* w将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。
      l- y+ q' @3 z7 l; y1 z8 y3 r计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数# D' B4 T1 Y4 G' [: A7 l& r0 e
    将year列改为整数年份存储。
    & m: H1 T% k& B6 x7 w% s"""
    ' V" X, z6 Q1 n  O' \6 i整个序列需要先转成Nullable类型的String类型,取出年份,再将年份转为Int64类型。
    7 R" Y5 U+ j4 V: {0 {注意,转换的类型是Int64不是int,否则报错。即使astype加参数errors='ignore'跳过缺失值,6 m# w$ l! }& ~$ ?# F
    转成int后,序列还有缺失值所以,还是变成了object。
    # r3 h" u. o1 [而整个序列转为Int,就还是Int类型,缺失值变成了 pd.NA 。$ ^5 `2 ^' q7 R, W
    """/ t9 I4 t$ Z3 C, n7 W
    df = df.convert_dtypes()) o- G# h/ k' Y: G! z# e
    df['year']=df['year'].str.replace('\D','',regex=True).astype('Int64')# {4 m4 Q; t; p3 W) S; x9 W4 b+ s
    df.loc[df.year.notna()]['year'].head()
    # w: Y/ t' |4 D: A1 |& i2 q4 r* I# X& Y0 o( {, ?
    0        1986( V7 x  m+ ~2 m, G9 V5 b4 e; u
    1        2020
    ) N5 B4 b. s2 k& F; X2        20100 ]9 i6 D- c7 H, A8 k
    3        20149 \0 X1 O! ]6 @# ~
    4        2015
    9 l8 `, c4 E1 {Name: year, Length: 12850, dtype: Int64- y1 p! Y, n* u! y3 z5 S) i

    ! d* X( T0 r2 ~7 C" l, m% n- p1
    4 x9 ^7 m* |. q; S2$ v: C7 C! l: ]0 ^: K
    3& V( Z) }( r0 X2 G3 r+ ]! d3 _
    4  Z: v, f% Q0 r; b% p6 Z: s
    52 v6 c) E6 ?1 N: d& P
    6
    5 z2 h: H% N+ T7& Y, _  L8 s/ A0 {1 u
    8
    0 a% y: O6 Z. {9
    8 o. H/ [/ R( m5 {0 H% k4 n! Y. l10$ Q! n+ K! Y) B7 I
    11
    * Q% u& J# k0 _7 s12# \4 D1 ^/ W0 b! y& z  ~# Z
    138 `3 I+ d7 r+ |7 D# l
    143 C9 F  h; q$ y8 R! G! P' S5 ?
    15. z, s$ y1 M2 G. [4 _8 T  ?
    166 M: v1 U& B, {' O& R3 ?
    参考答案:
    ' [' K" O# K+ b2 J: C; m9 W# e9 t7 D
    不知道为啥pd.to_numeric(df.year.str[:-2],downcast="integer")类型为float32,不应该是整型么
    . ^( \+ C% j0 d" r0 {5 F$ ?: ~+ i+ s6 S, r" d# t/ q6 T1 G
    df.year = pd.to_numeric(df.year.str[:-2]).astype('Int64')
    ; n- Z8 ?, N  O7 u) G( j. ]df.loc[df.year.notna()]['year']. [: y, M" ~- E9 f4 n& \
    1
    0 k, M. J8 A: v7 \2! g- \5 J; C) e7 r  a( u& r7 p
    将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。
    / ^# O5 C2 v& B6 q6 ^2 cpat = '(?P<Level>\w+层)(?P<Highest>\(\w+层)'( Z0 |; O& r1 G6 w
    df2=df['floor'].str.extract(pat)  # 拆分成两列,第二列还是(共6层得形式,所以还的替换一次
    ! D' C8 u& \1 qdf=pd.concat([df,df2],axis=1).convert_dtypes()  # 新增列拼接在后面,再次转为Nullable类型
    ( I. t+ I: Y. b& r0 Y) {df['Highest']=df['Highest'].str.replace('\D+','',regex=True).astype('Int64')              ; d) O0 s. H# ], t2 O
    df=df[['Level','Highest','year','area','price']]
    % \) o- N  N5 C6 Y8 u- j' ]" N# rdf.head()
    & `! _' y$ U1 \4 D1 O" ^8 G, x1 `, w# b# c" X: ^
       Level  Highest        year        area        price+ \- q0 ]) J1 G* {6 O9 v
    0        高层                6                1986        58.23㎡        155万
    5 y$ E9 _" R' }0 R8 @( S. R( M1        中层                20                2020        88㎡        155万0 Y  P. b# d5 M& ?  J
    2        低层                28                2010        89.33㎡        365万8 R" Y+ T6 ?0 R9 t+ w# v
    3        低层                20                2014        82㎡        308万
    + Q) r  }- m7 d. f/ V; e0 M4        高层                1                2015        98㎡        117万
    6 l4 x) `8 u, \4 t3 O1
    , |6 W% j" n9 h% K) R( T2 [2
    2 S1 V5 Y( w- Z# R- J3* q1 V! W9 k) U- C* k: O# M  ]
    4
    , n7 J0 A! k" g  n- R, r5
    2 c( }. h! U; M1 P/ F+ X' N( q3 W61 s6 U( L4 }" D; j' t
    7. s: H$ J- e: k! ^- G, C
    8
    ( `: ~7 d6 E( {  Z" h# Z- n2 P$ b( P9
    ! x( Q- F  D1 ~9 z8 a103 Y. R" [6 J$ v- [- {
    11! p2 |( k3 @7 h: g. _
    12
    % H* N' c8 \5 S$ C4 |0 h: e7 H130 L4 _3 h/ ]1 b) P! @
    # 参考答案。感觉是第二个字段加了中文的()可以准备匹配出数字,但是不好直接命令子组了
    " h9 z! ~% P- A, Xpat = '(\w层)(共(\d+)层)'
    ' m4 T& ~3 ?, p% L5 G: T% xnew_cols = df.floor.str.extract(pat).rename($ a+ k1 @2 @& I& @) ^* O+ b. \
                        columns={0:'Level', 1:'Highest'})
    % i; S+ K9 A/ X4 T) M* B, x4 C! s( ?' {' A. o
    df = pd.concat([df.drop(columns=['floor']), new_cols], 1)
    3 Z& C: x9 L# {/ ]2 j/ edf.head(3)3 c  k4 m7 n0 H! U+ N  r
    1 j6 k# l$ o* ~: e
    Out[163]: : X/ n$ B' b( Y  |: c
       year    area price    Level Highest
    ' P3 }) y8 B, \. ~0  1986  58.23㎡  155万    高层       6
    ' ]5 }" g( R' E4 j1  2020     88㎡  155万    中层      20
    ' ^; Z* V& G9 t  a5 j9 [/ `2  2010  89.33㎡  365万    低层      28
    4 v' G) E; M9 z; \( h8 q1
    . n& ?5 S& ^; m) R2 {& F7 Q# ]( E2' u0 }$ e# ~( ]
    3& ~6 X) G5 C" M1 c
    4
    9 u# v/ \* q6 B2 O/ j* S& b% i/ }5
    ' A% G8 k6 _$ m: h; ]7 h8 s( U6
    % o7 @: ?* U6 p9 s7, b+ C2 @- q" a$ z
    8# @0 K( Y7 T2 E
    9% Y4 @0 I! \* ~
    10
    9 m! K# C& k4 x% P4 O11# V6 t- L) {" }1 M# B3 d) T3 R: u# |
    125 W# }% [) F) E
    13- t% {! ?' e0 D' {
    计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数。
    + e3 b# {$ p0 g) X5 a4 w"""; ~# }' n, H: a2 l# [
    str.findall返回的结果都是列表,只能用apply取值去掉列表形式
    6 Y8 @, o; ~, F8 f* a; F, W参考答案用pd.to_numeric(df.area.str[:-1])更简洁
    ( L# b- n* J! V) w由于area和price都没有缺失值,所以可以直接转类型
    8 C5 M4 q3 R: n9 h" S"""( `; z0 T' a1 p5 A5 T: h+ ^: [
    df['new_area']=df['area'].str.findall(r'\d+.\d+|\d+').apply(lambda x:float(x[0]))
    2 r' Y, V9 ~# h3 y$ O8 ^( A( ^8 v& _df['new_price']=df['price'].str.replace('\D+','',regex=True).astype('int64')
    ) r/ m/ ~( f' l3 s1 Ddf.eval('avg_price=10000*new_price/new_area',inplace=True)
      `& l2 Z# G, x8 Q5 y  s. {# u# 最后均价这一列小数转整型直接用.astype('int')就行,我还准备.apply(lambda x:int(round(x,0)))1 z* X! T4 k0 j: ~9 U
    # 最后数字+元/平米写法更简单  v- i* W' W: M$ L2 i
    df['avg_price']=df['avg_price'].astype('int').astype('string')+'元/平米'
    , e* Y7 D  f+ Fdel df['new_area'],df['new_price']
    : @: }4 |+ L/ V$ b! Bdf.head()2 W) b! ?1 p. W5 V

    5 d, F1 @; L4 `0 z' h, ]   Level        Highest        year        area        price        avg_price* N/ U4 V# w! K- l% M7 d
    0        高层                        6        1986        58.23㎡        155万        26618元/平米
      \3 r+ s% c8 Z( ~% Y9 ^1        中层                        20        2020        88㎡        155万        17613元/平米- W. m) {# L' I( Z2 j! K0 y, O
    2        低层                        28        2010        89.33㎡        365万        40859元/平米  S4 ]/ z, ]: z6 }9 p5 P
    3        低层                        20        2014        82㎡        308万        37560元/平米
    5 o, c" f8 E% h4        高层                        1        2015        98㎡        117万        11938元/平米
    % ]: {0 w/ w$ b* z  n5 U7 R$ G
    , |6 Y" T. r- k3 t6 U* q15 F# O. c, p8 ^: W( U% B
    2
    ( p# w# H5 ]3 T  C- x8 t* A. G; F, k3
    . n4 F( }: [! T5 a, W( j/ |4
    6 m! b* b$ X# Y" ?5 R+ T$ I3 y5+ H9 @6 n$ Y  _: b' W# W
    6
    0 P8 W8 b) w/ V7
    : D  u$ Q: r; c. a8% X2 Y8 i3 U" d
    9
    . k* ^  J  A8 P. z10
    ) c" f# X) e- X) Z. y11
    . ]! Z1 P& p; V; G; W2 Q5 |12
    ! v. _- b3 M, T13* M$ n9 j4 B! C- I
    14  l5 B$ N/ Z% @8 }# F) O: x8 N
    151 F6 h% ?1 X  v' M% F4 d" y$ X
    16
    2 {7 p( e+ g( h( T, e' G17% w4 D9 S2 b7 ]  y5 i
    185 r  l) d' @7 s. h! Z
    19
    * ~4 R3 g1 C- ~20
    % W7 w: t( a+ }# U- |: }# 参考答案8 X4 J+ V$ r& d4 B* d* b
    s_area = pd.to_numeric(df.area.str[:-1]), M+ t0 P+ u9 Z% b1 }0 @7 Q
    s_price = pd.to_numeric(df.price.str[:-1])
    + c) g" _& u+ y/ E; m" Rdf['avg_price'] = ((s_price/s_area)*10000).astype(
    + `: j' E# R) q$ D/ g& J+ L$ R                    'int').astype('string') + '元/平米'
    % j9 }/ y% E4 L6 ?* T5 y' I
    1 C% j  s' Y, R# S: N8 wdf.head(3)3 P( A. J2 D, }" B" j5 n
    Out[167]: . o" u8 K& @6 K+ B
       year    area   price   Level Highest  avg_price4 X% k* }6 v+ i* J
    0  1986  58.23㎡  155万    高层     6          26618元/平米0 Y  i/ A& s: `3 T! w
    1  2020     88㎡  155万    中层     20          17613元/平米* `  N4 Q5 y# C/ w
    2  2010  89.33㎡  365万    低层     28          40859元/平米
      h8 N+ ~" i+ r7 L2 L0 y% U- |; a$ h11 i+ r/ j" \/ E% f2 t# V
    2+ v; Z! C, a8 d; t
    3! Q" I/ c* f6 }
    4
    : q% P9 Y; B4 k5 _6 m" U; v5
    % V* b8 |7 O  Y" W/ K6
    / {: a/ ]0 v* z  F8 h7" e" j# h2 m7 ~( c7 g3 A
    8( x7 _2 ?$ c1 c+ i2 r7 T. N9 O
    9, T2 D+ j7 Z+ r' Z" c5 w8 e/ _
    10
    5 v& w' X; n: P% r8 u1 f/ b" x111 n' P, ^) f" M& I* x( U
    12. [  f5 z; d/ _' d
    Ex2:《权力的游戏》剧本数据集
    6 k  y  t  h0 a) [7 m& Y现有一份权力的游戏剧本数据集如下:* R' G2 `; a! l

    3 B$ q" u7 f" w1 f- bdf = pd.read_csv('../data/script.csv')* t2 Q; f3 W' j
    df.head(3): G/ z( `, S& p  ]$ j

    9 h' `+ k; I, N- t+ k" z8 c: v! sOut[115]: 1 }  [5 B5 N( Z0 M
    Out[117]:
    ! D, H+ J% w0 O' Y  Release Date    Season   Episode      Episode Title          Name                                           Sentence+ w1 \# a& f' E+ I" h: T
    0   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce  What do you expect? They're savages. One lot s...
    ) M  S/ k! b! B4 x1   2011-04-17  Season 1  Episode 1  Winter is Coming          will  I've never seen wildlings do a thing like this...
    1 e: H& N% X# s+ t2   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce 8 F" n6 ]. z$ s; T) k; P  k: ~2 R$ v
    1! d+ }& t: ]9 R" T
    2! V' b7 m0 g. T3 j
    3, M. A* o2 s4 |" O, L4 ?3 z
    4
    & R- D: U+ r) T, J; h5
    : m  \' s$ E( m1 U9 {6 ?8 f61 J9 \: r$ \0 i9 Z  V! A
    7/ C* D" |2 E1 U4 R. O
    8
    . ^  W0 U- c! g% d9
    * f0 X+ ^# H0 Z+ S- F2 u+ o. H计算每一个Episode的台词条数。
    , R' \7 L- D- J6 q* n3 ^) r: q* Y以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
    " Z* u" P5 l+ k. x" L7 A若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有 &#119899; 个问号,则认为回答者回答了 &#119899; 个问题,请求出回答最多问题的前五个人。. W$ S) z. A" v1 L) \! \, O+ T) B' c' ?
    计算每一个Episode的台词条数。
    : d9 o" @: [# k9 c2 b# |df.columns =df.columns.str.strip() #  列名中有空格0 E6 D1 t  {) W/ P- n: \
    df.groupby(['Season','Episode'])['Sentence'].count().sort_values(ascending=False).head()
    $ \& C  X9 ~  g+ Y* C
    * j# Y4 A0 t$ M! U; D9 lseason    Episode  - |. H! d; k! m" Q: g, m
    Season 7  Episode 5    505
    5 ~% M" V# s# W" v& a: NSeason 3  Episode 2    480. P6 Z& |. e+ H' J
    Season 4  Episode 1    475
    $ j) o% b  K# w, KSeason 3  Episode 5    4405 Q2 u# a$ }9 i+ S$ N7 K1 Q
    Season 2  Episode 2    432
    - a' \5 u$ Z3 z: m) Q1# o5 Q5 f8 }3 D7 j( X6 \
    2* v; G8 o$ y' S% ^
    3
    ' w" A3 \+ a: a  j0 Y: ^4" R! C, c9 p% ]0 X; b; h+ j3 z( `
    5
    0 V) B6 t' `: f7 E6 w$ E/ Z6* d8 U" o8 h' s
    7" B5 F; ~% t& Y2 b+ n
    8
    0 g$ |  C7 I- f7 k- n9 R/ P4 F4 U7 |9# B2 O2 B0 {2 I$ ?
    以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。2 W# g2 T$ b6 f" ^" e4 {
    # str.count是可以计算每个字符串被正则匹配了多少次,+1就是单词数
    1 ~. {: x6 Q3 k/ w5 Vdf['len_words']=df['Sentence'].str.count(r' ')+1  d9 a; i3 V0 Z: N0 v  t
    df.groupby(['Name'])['len_words'].mean().sort_values(ascending=False).head()4 e9 X& q" A& @$ X; l9 C

    . V+ X3 J  z7 F; ?' AName0 Y4 T/ I$ Y% \5 z3 r2 @
    male singer          109.000000
    7 J# A5 Z. L% d" ^' Uslave owner           77.000000
    . C1 Y- D) d5 J/ }manderly              62.000000
    0 \1 N0 g% W+ `6 `( Tlollys stokeworth     62.0000001 \& g% G$ y4 H! E8 \
    dothraki matron       56.6666671 R- i5 `+ C( n% {3 g
    Name: len_words, dtype: float64
    5 f  u4 F5 P5 q5 |* v( @% B8 |0 F1
      [! p' H$ q7 w3 F" r2
    & `8 T4 H" z( A- n) e, z/ E3
    / M& y+ h; @$ f4 }8 x+ u4' x( T: x: h8 t0 Q- p4 m7 I# d
    5
    2 a9 D( ]. Q# Z/ s/ }' \6
    : Z4 @( Y: d9 |0 b& q* b7
    ! C5 }' ~" ]* N  ^" S; a8% Q+ R$ b0 x9 Q/ i) ]( ~
    9) f! d; w* x7 }1 X+ W( l
    10, u8 g3 Y8 J- E! R/ t* a9 e. _
    11$ n& ]# O% c- m3 x: y
    若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有n nn个问号,则认为回答者回答了n nn个问题,请求出回答最多问题的前五个人。( `6 i& Y3 T8 j) U
    df['Sentence'].str.count(r'\?') #  计算每人提问数
    5 |8 T6 t, x. M1 q# tls=pd.concat([pd.Series(0),ls]).reset_index(drop=True)# 首行填0! R; a( Q. j, v& E. p& ^
    del ls[23911] # 末行删去
    + Z4 A9 H$ m# u0 X, ?8 x$ h# cdf['len_questions']=ls
    ! p7 `5 b: k' p! T2 z9 ~3 G# X( }) Udf.groupby(['Name'])['len_questions'].sum().sort_values(ascending=False).head()9 H$ r* I! y! E. S
    ' [; q: u0 B7 Y! K: G, K
    Name$ o- H# A/ s7 d$ @5 T. z4 E% x1 W
    tyrion lannister    527
    4 m7 T! d% \! e3 h4 s$ ojon snow            374
    $ Y, ^7 Y( q$ r3 E+ djaime lannister     2832 x# ^1 ?$ ^" U- S
    arya stark          265( h1 e$ t" q& `( x
    cersei lannister    2469 K( P6 e' _, b7 X
    Name: len_questions, dtype: int64  l; i6 S) z3 X

    1 k( L2 N6 Q8 t& x/ d6 i+ m* }" W, w# 参考答案
    ; L1 u7 W: C1 u; x& [7 A% Z  Ks = pd.Series(df.Sentence.values, index=df.Name.shift(-1))
    + q+ o/ ]1 M! Y% v* I5 ds.str.count('\?').groupby('Name').sum().sort_values(ascending=False).head()1 \# P# v2 x$ k8 X0 V
    - e/ A9 R* h  u! v- M" t
    1
    # w/ y, i4 ^4 e* i2
    / P1 g0 t  a! C3 @3
    ' p) {" ?, z! g' R8 ?4# |) S" H4 _$ ]6 v2 Y
    5) q1 {" q( ^9 L8 X! |1 L
    6" d- O* r) r9 Y$ V( e: i
    7
    2 u# T  @6 ~9 N86 C% G& [( L- p  Q& W
    99 z( N) K9 q5 O; ~4 _
    10
    : L# s2 o* ~* y3 r; ?11
    % O% @# E6 @; E5 T12
    ( x  n; ^; T* F1 G, W13
      z5 _# w: F: Y& C. L% @142 Z, U9 a' j) S# ?# ^2 _$ G
    15
    4 Z# F: _8 p7 W) b16
    0 Z$ [% s7 Y3 O  }) x17, C  t, j% T7 `4 S, s3 o+ H4 a
    第九章 分类数据
    + M8 _, T9 r- C5 F. X4 dimport numpy as np
    # q! e% f; R* D! wimport pandas as pd# P9 a( K. _: Y7 }3 E+ ?
    1
    ! r# U, b7 \; n0 w& k6 i2
    4 y) f% q& S1 o4 T8 ]. W8 \9.1 cat对象/ }! C7 a, |9 d* R: ]8 y
    9.1.1 cat对象的属性
    2 F: L- Z! R7 c4 ^  {# l) [  在pandas中提供了category类型,使用户能够处理分类类型的变量,将一个普通序列转换成分类变量可以使用astype方法。
    / {2 d$ [+ L0 u! @
    # B% {/ @! N! v/ ddf = pd.read_csv('data/learn_pandas.csv',, s7 e% `7 j& A7 k
         usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight'])
    " S1 A4 T" A: R) i% I0 Os = df.Grade.astype('category')
    5 X: ?& l: q' Z
    8 I  z! ^8 X+ c3 D2 D# Z& Cs.head()  E2 J! J% q5 x' m) l
    Out[5]: ) }. B6 H8 O5 I7 Q1 `+ Y6 S; v
    0     Freshman; R# r1 T& y- F4 M7 B% n9 j
    1     Freshman! G# k  J, x& l2 w
    2       Senior7 C$ r% a4 w. i/ d* U7 F
    3    Sophomore  S/ O0 E8 V! h1 D
    4    Sophomore
    ! M5 B+ Q# |" V* N- l6 L# ~Name: Grade, dtype: category
    3 n$ J8 Q! x4 n8 ~' ACategories (4, object): ['Freshman', 'Junior', 'Senior', 'Sophomore']! f- t' \: D! G' B! u5 S
    1
    0 t4 N& ^+ D! t( H8 k2 O) c" \9 g2
    3 L1 t6 A* Z7 |3
    3 _5 ~! U  ~% K7 G6 Y( R49 Q& T0 A8 E3 C1 c6 l
    5
    / R# {' ^+ h  z% k/ b9 h5 y8 G" v6
    1 z2 Q8 \$ r6 B# v9 Q7
    * D( D5 H+ o' k, D# z85 Y! e# {% b% |/ |3 V4 b
    9
      a1 D1 E1 s' n: T/ P10
    4 r) ?! i, `2 _. U/ a' k: k11
    , x. K+ J8 E  w: R12) }/ z7 r) Q* z- l- k+ q
    13
    2 ?  i9 d  Y' W: d3 l  在一个分类类型的Series中定义了cat对象,它和上一章中介绍的str对象类似,定义了一些属性和方法来进行分类类别的操作。2 E- `2 h6 h0 R% j/ k) z$ `

    ; d. k+ \: Z% |  i- N9 ]s.cat5 n; x, }0 V+ i
    Out[6]: <pandas.core.arrays.categorical.CategoricalAccessor object at 0x000002B7974C20A0>
    ' A2 z/ e2 `* |/ S4 j/ T( I* ^) |1
    $ l/ J" W4 A1 j3 M20 K7 |$ F' X. w' H; b3 j
    cat的属性:
    : `& `; P/ P. |4 Z, [. G. b$ z+ v; Q; M  V& ^3 i
    cat.categories:查看类别的本身,它以Index类型存储! ~9 ?. w+ j' e/ ?
    cat.ordered:类别是否有序
    + ?! j' {* u1 D3 V) a, F* mcat.codes:访问类别编号。每一个序列的类别会被赋予唯一的整数编号,它们的编号取决于cat.categories中的顺序' w- q. O/ O0 w7 h
    s.cat.categories) q1 m2 y# m' S& K+ Y, J8 Q( D
    Out[7]: Index(['Freshman', 'Junior', 'Senior', 'Sophomore'], dtype='object')2 Y! n6 v2 P' T( z
    & T/ t9 F8 b% I) u/ m
    s.cat.ordered% h, B, m+ v( o: _( b& F
    Out[8]: False. Z; y0 @' \% m: Q
    ! [6 i/ S: M* }+ }& Q* g7 |6 [
    s.cat.codes.head()
    1 C; v3 I, b8 M& c% yOut[9]:
    3 x% @7 t6 \8 ~( |8 n! P0    0' f% v3 Q0 \  E7 b8 u& ~
    1    0
    4 \. J, b3 F1 E* s; u! s6 ~! P2    2, r% C  Z  ~2 i" ~
    3    3
    0 P5 g: ?/ [, J/ X9 I* u4    3
    ' B1 T" W# [/ Z9 Gdtype: int80 W' s" F. H( A$ I9 _  p& L
    1! T7 g5 L; w- X4 Y) D2 w/ f
    2) s5 j# G' L( J# |1 [  r" }' M
    33 y! O+ Y2 z' k/ \' O1 B
    4' u: i6 h8 N# S4 O2 r0 x& d6 v
    5  L0 Z6 ?+ F' ~% ]- ~% F
    60 v+ F( P4 D( }% H: o
    7& c- E) c, p1 y! F
    8
    9 C" _  {8 S* \6 `) L6 ?9
    2 y3 @1 f/ \9 D+ y2 |; h! Z' y108 z0 l1 W! `" S. B7 {+ U; s+ ^9 ~
    11
    2 P4 A7 Q1 c8 N2 ^  T! }12" v/ E! q; C0 {. Q# ~& m
    13
    9 D- z/ e5 j7 x8 J4 j' E% Z+ J5 d" L% `149 C1 [6 R7 ~5 M1 C  Q) I
    9.1.2 类别的增加、删除和修改# I. D8 Z7 w; K2 r
      通过cat对象的categories属性能够完成对类别的查询,那么应该如何进行“增改查删”的其他三个操作呢?
    & t! Z7 U: n9 V7 [
    ! t# \+ n' A6 F& V0 y8 Y【NOTE】类别不得直接修改  I: I6 q$ Z; d
    在第三章中曾提到,索引 Index 类型是无法用 index_obj[0] = item 来修改的,而 categories 被存储在 Index 中,因此 pandas 在 cat 属性上定义了若干方法来达到相同的目的。
    ! F1 I0 p; a$ u' X% w
    + u; ^2 z- F' U( r. sadd_categories:增加类别9 Y3 a5 N& E, ?6 k
    s = s.cat.add_categories('Graduate') # 增加一个毕业生类别
      o1 J( D5 _* \1 U4 e7 Q8 os.cat.categories
    $ N) p# K* }. ^# u  X( b
    / {" ^; Q% o/ |/ d, V3 K  i5 u, RIndex(['Freshman', 'Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
    % `0 Z: F& y3 C1
    / s5 ^  }: A( W8 y* M$ j2/ _- z9 f& K7 W% D1 k' u* {% V
    3. o3 b3 j# n, A/ X; ~9 }
    4
    3 ]% `& l( z" \7 E" Mremove_categories:删除类别。同时所有原来序列中的该类会被设置为缺失。
    9 b- K' A8 E: w! ^, Bs = s.cat.remove_categories('Freshman')
    7 ~: R6 K$ `5 K! h( W
    ( k# W" D9 ~) W1 `) T: h5 H$ ~! ~s.cat.categories
    7 W. _$ v8 F3 V9 D3 @3 g6 ZOut[13]: Index(['Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')# a4 W+ e& }' \4 D3 A: d# ^

    7 y0 U" P" |( Q, v# b  ws.head()9 r7 c2 }$ `' u: ~. y6 c
    Out[14]: 2 W. s* F/ T: Y/ V
    0          NaN
    + g& I0 }  Z7 e- q  ]1          NaN
    8 p" f& q3 l- w9 L& p2       Senior
    3 _0 D, x' P0 g. M6 Q' H5 V3    Sophomore$ l' v, s- E4 a  `" T' w7 P! W
    4    Sophomore, V' I# s# n+ ~6 B
    Name: Grade, dtype: category& ~- l" g" X, N8 t
    Categories (4, object): ['Junior', 'Senior', 'Sophomore', 'Graduate']
    & d" ~' ?2 w5 L  _$ U7 ]$ K1! N; Q5 ~* \3 ^5 r) D) o, ~
    2
    1 O; v- k1 C5 V# o! Q35 B# z: ~% S+ W* C4 {. p
    44 l: t9 S; r; T" {& s/ r  f# X
    5/ S- Q& C0 @8 T! _& a7 ?  N
    6
    ; X4 @2 A8 D8 ?7 k( L2 q$ d7; W" P5 L- S" v2 B( u
    80 N' A8 b- O! h+ g# r  c0 ^
    9" _/ k5 z& y# w0 K* N( R
    105 F8 s$ Q* \! q7 P0 l0 K0 f3 O
    11
    ' E. w6 H; {/ C: I$ r# B12, ]; {  a" f3 T
    13
    * A* |/ ?# R4 E0 R7 \14
    0 o) h" O$ m( g  Kset_categories:直接设置序列的新类别,原来的类别中如果存在元素不属于新类别,那么会被设置为缺失。相当于索引重设。
    + }- S% }+ K8 ^- L" @; ls = s.cat.set_categories(['Sophomore','PhD']) # 新类别为大二学生和博士
    / U$ T% v/ L- is.cat.categories6 W$ V4 I& m, p: ~( P
    Out[16]: Index(['Sophomore', 'PhD'], dtype='object')
    ( e: T9 M" l8 M5 W9 x4 I8 I6 @' u4 O
    s.head()
    " R! c2 {7 b: M- X/ J- ROut[17]: " W1 G. s, I0 {" @1 r
    0          NaN4 _4 D  z* d1 k& r$ y% [
    1          NaN. n) p2 {3 ]. `
    2          NaN/ [/ {( n( W1 T
    3    Sophomore
    / N. }0 G  q# W+ V% @4    Sophomore2 U$ Z7 H" H& K  B' S
    Name: Grade, dtype: category  u% v8 x! W; R' d( ~8 Q
    Categories (2, object): ['Sophomore', 'PhD']
    ! }7 A% ?. N: M) G1
    9 @3 S: b( A8 H. i+ X6 y0 Y* S2- l& O3 t6 Q, Z. k" Z
    35 A5 E. i: U7 {' w
    4
    8 ?  Q. w: `7 t% d. j& w, E9 w5& W) n! R9 N8 W3 X9 V
    6) ]( J  C7 N4 j1 y
    7
    / ~' C9 Q2 k( n+ n+ \! S4 V- k8
    ( M0 v' H  l5 [/ Z2 F& ]. J1 o9
    ) u- Z& v, ^3 }( Z- B1 w10
    6 ~) {5 o/ R$ V+ |0 A11
    ( ~) u9 Q9 K! s: O( p4 u0 r124 ]8 x$ V, `6 ~- k) Z4 [
    137 e' z4 ~$ G6 c* a
    remove_unused_categories:删除未出现在序列中的类别) w4 s+ \" G* q% V
    s = s.cat.remove_unused_categories() # 移除了未出现的博士生类别, ?- b7 D3 c+ ^* U; |' c+ _
    s.cat.categories
    ( z* L: \8 Q' |- |8 E/ q3 |3 b- I* a. Y& m7 w
    Index(['Sophomore'], dtype='object')
    ; d# [0 x- Q) |: j( B; ^: R1$ K2 A! c5 B9 R; P' b
    2
    ) o! h% D3 i4 s4 X4 J3
    4 ]9 w% p$ l& o8 C4
    ' Y$ \* J& b8 i  a' N1 y- Qrename_categories:修改序列的类别。注意,这个方法会对原序列的对应值也进行相应修改。例如,现在把Sophomore改成中文的本科二年级学生:
    9 J" C# G1 |: x5 ~6 xs = s.cat.rename_categories({'Sophomore':'本科二年级学生'})
    8 ~5 r+ N* w  m* e1 H% ys.head()/ E5 W( j9 q# H  u9 u

    0 m# J) U; L! _  I) h$ x8 E0 J( W6 n* b0        NaN
    7 ?- N# O8 R( B* p6 w  X1        NaN; N" B$ b) d7 T
    2        NaN& R  P- W: g% M1 l! m& L9 @) Q
    3    本科二年级学生, h% g/ ?8 h: E1 X1 i/ M0 S/ ?( {
    4    本科二年级学生$ W: v& T; `" p8 T$ X% o- X* o5 O
    Name: Grade, dtype: category
    $ L. u" ?8 Y, x# L. BCategories (1, object): ['本科二年级学生']& t4 F; N* x' R& k8 c
    17 X  W/ n" L. R: f" j' }
    2
    0 ?; \# z0 E  L& I% G; P8 e5 h6 y3, f: J+ U- Z( J. j
    4
    " h) F8 n! x. q! l" N+ R5. n9 ]% w( c# S- |
    6# o+ ^8 s" O( j0 U
    7
    . b2 }) \% A3 k' y" m3 \) s* J8
    - T1 n2 M- n' X3 C% ]6 h+ d92 z6 _; v; E" r  }: N. f
    10
    1 @# j: |; T9 e5 p6 J9.2 有序分类
    6 @2 r" V+ G4 G% x$ U9.2.1 序的建立7 s: o% m) ]* a  h  G+ b
      有序类别和无序类别可以通过as_unordered和reorder_categories互相转化。reorder_categories传入的参数必须是由当前序列的无序类别构成的列表,不能够新增或减少原先的类别,且必须指定参数ordered=True,否则方法无效。例如,对年级高低进行相对大小的类别划分,然后再恢复无序状态:
    ! \3 x6 `% r8 O8 {* m
    $ F3 _  |' K8 \9 G$ M- ~s = df.Grade.astype('category')8 S1 N' u  h! _' T3 g
    s = s.cat.reorder_categories(['Freshman', 'Sophomore',
    7 c# n$ Y: l, F( }1 O; I) Z                              'Junior', 'Senior'],ordered=True)! Z& Y5 |' d- v5 ~" C# j6 N
    s.head()
    $ i) R! d( \  z, f/ u) p0 L* lOut[24]:
    3 B5 ?( J% G5 V' K" C' J) r, @8 l( [0     Freshman
    ( M" K$ n6 o7 c1     Freshman& s% i& y: [3 W$ [; m- c+ ]- \0 N
    2       Senior1 E0 h6 f; ?9 e! G
    3    Sophomore+ W/ \: `+ n# k1 v
    4    Sophomore9 Z: Z: S7 l' f, I4 O& c/ f
    Name: Grade, dtype: category
    9 w( D6 c4 _8 y  J7 D5 oCategories (4, object): ['Freshman' < 'Sophomore' < 'Junior' < 'Senior']5 }2 {( p' e: P, i( K$ }0 e" j
    ! d0 n+ C+ m( B3 N: v7 p( A
    s.cat.as_unordered().head()
    # Y) O8 w: a4 W" d' {Out[25]: : K* i' M6 Y1 D- A& _4 h, K/ t
    0     Freshman# Y, y# E5 V4 S# O* j) G
    1     Freshman/ N" l9 t( `/ o% M" }0 t
    2       Senior
    8 X  A5 B2 e; l$ |+ q3    Sophomore1 r& ~. P" J0 @9 ]; M2 q7 N
    4    Sophomore) q3 \6 s. n4 W9 y4 ^* q
    Name: Grade, dtype: category6 ~2 `, t3 p8 V; L
    Categories (4, object): ['Freshman', 'Sophomore', 'Junior', 'Senior']* z, L6 H7 @0 |

      I. I3 U1 E* a! \1
    6 m+ Y, ]* M0 W& M$ ]1 g2 m+ x2  D! N5 ]8 d" H( ^( ^
    3
    * `- ^: X! R3 [# e; ^; h8 M2 \; Q  I4
    % z1 `1 r. C* M: |* Y: j1 w- |5
    & f0 a9 H8 z: Y4 ~4 d& F6
    ' e0 A: L% A' X- S( y7
    ! m0 @. ]) d0 M( s  z8# u# t6 H7 v4 [, C+ d
    9
    ! k  `0 q0 G0 Z# _3 O10
    9 o9 z3 Q! M& R; ?( S11; F' }- ^! p" c! R7 J( C
    12
    : u3 l+ [/ ~* p& [4 ~13$ a& L0 p! h- ^+ @8 x3 `
    14
    ) e8 p0 t  [8 [- b2 l" F& P8 [7 g154 P1 U+ B; X, Y+ S
    16
    % R9 u5 g8 Y, O9 _' W8 I# t  W17
    4 q. \6 k4 l2 r) G! m6 G9 }18
    # N1 Y4 A" ]# Z9 G  M2 g19+ U* k/ s7 x5 T( x7 i' [3 S6 F
    20! D& Q4 A+ q) h  t" }1 ~
    21' n3 F+ X8 S# N2 U; e* Z4 N
    22) p6 I8 ?: F- W
      如果不想指定ordered=True参数,那么可以先用s.cat.as_ordered()转化为有序类别,再利用reorder_categories进行具体的相对大小调整。
    . Q8 A. h1 m9 B2 T9 V% B+ A% N) }+ C! `# S: r% b  x( j5 w
    9.2.2 排序和比较
    ! R6 m( @# m+ E# B" b在第二章中,曾提到了字符串和数值类型序列的排序。前者按照字母顺序排序,后者按照数值大小排序。
    0 s# p* w; R0 S1 r2 C
    + B2 l! q! h  _  分类变量排序,只需把列的类型修改为category后,再赋予相应的大小关系,就能正常地使用sort_index和sort_values。例如,对年级进行排序:
    , X6 P5 Z/ Y* o" R' b* Z
    ( _# b1 `! u: Ndf.Grade = df.Grade.astype('category')6 _; h+ k) E) q) |; i2 o
    df.Grade = df.Grade.cat.reorder_categories(['Freshman', 'Sophomore', 'Junior', 'Senior'],ordered=True): I; ^6 i9 h- n  X
    df.sort_values('Grade').head() # 值排序( W& H" e: h1 A! M( j% V# T( h
    Out[28]:
    7 F' G7 R1 ?- U5 P0 D        Grade           Name  Gender  Height  Weight
    , d6 v. |- g4 _' d( b, H0    Freshman   Gaopeng Yang  Female   158.9    46.06 p. [" ?  I: |& s
    105  Freshman      Qiang Shi  Female   164.5    52.08 R" E/ j. r/ }# o' J! D$ `
    96   Freshman  Changmei Feng  Female   163.8    56.0
    $ v, f' I' c- o* {, }88   Freshman   Xiaopeng Han  Female   164.1    53.0
    0 G4 n" r* |+ L; f81   Freshman    Yanli Zhang  Female   165.1    52.0
    + h0 M& Z( q7 A5 G4 {
    . k& S! Y& ^2 r, r+ e/ e, @/ Q( odf.set_index('Grade').sort_index().head() # 索引排序
    4 c/ y2 `5 |0 X  ^% cOut[29]: / D7 ^& F( i) H1 o: Q6 n9 g! p; h
                       Name  Gender  Height  Weight
    - I/ s, s0 P# V1 P5 ?+ @3 uGrade                                          1 G! v( D+ _4 i
    Freshman   Gaopeng Yang  Female   158.9    46.0, u2 o: Q& z4 s$ W! F% b: R
    Freshman      Qiang Shi  Female   164.5    52.07 [7 U9 ?: f* v. t: g
    Freshman  Changmei Feng  Female   163.8    56.09 m( y& N$ q8 R$ c- ^7 U4 [
    Freshman   Xiaopeng Han  Female   164.1    53.04 {2 ^5 {' Z0 m% r# R. v
    Freshman    Yanli Zhang  Female   165.1    52.0
    0 p3 n4 k5 u+ R+ {" [- J0 D
    / |) h# c" ^2 i% f2 E$ q1# V" o& o% s% s1 E% l& c
    2/ K. d' w9 @! E* g' X* f
    32 ^) s2 \+ K( v; n
    4& z' l- u1 b- [' [/ F- N$ r; k% ~
    51 }$ G( r  a9 W
    6( k$ N7 O$ i8 s
    7/ \: s! T5 w$ ^3 Y" n, _4 C' J; h# ^
    8% v3 u) ~$ p% |$ e5 N5 C5 p
    9
    ! K: j# l0 s$ V' A) O% }. H106 U! x  r7 X" W5 {$ Z% F: }& L, h! [: y
    11& D. i6 T: K9 o; Q9 E
    12' D8 e; x$ P# A: _# Q6 Q8 j
    13
    # N; ?  B2 v4 q; H$ D4 T14
    4 l7 Y6 L* \) {; H: ~; [' S152 m% d( J+ O' |/ i: o" r" [, `
    16
    4 y# j3 O7 T6 `( m# h% a17, c+ c" Z& n# U2 H
    18
    8 a, u8 @1 v- r7 S19
    ; n& \, H# [- f( p6 i20
    2 i7 O7 w* g' m# Y* G4 x) n2 S  由于序的建立,因此就可以进行比较操作,方便后续索引操作。分类变量的比较操作分为两类:
    ( \; T0 D9 v" z/ u
    7 D. P8 X+ h* t6 L- }. G) _==或!=关系的比较,比较的对象可以是标量或者同长度的Series(或list)。(无序时也可以比较)! }7 S) D0 |7 S1 k
    >,>=,<,<=四类大小关系的比较,比较的对象和第一种类似,但是所有参与比较的元素必须属于原序列的categories,同时要和原序列具有相同的索引。, V1 `7 a, P/ x1 \% `/ a
    res1 = df.Grade == 'Sophomore'5 v5 l, G& z4 C& u9 O$ _7 L9 T

    ( r/ D8 }; x. D$ q5 }res1.head()
    1 F5 V. p6 v2 x9 r% C: O0 tOut[31]: ! J% j7 h; l, R# P: x" u
    0    False
    ) p0 |3 i1 \4 s1    False
    6 w3 f. o( S+ {' U! y, c! w2    False
    0 c9 A- p3 L5 e) v7 P, q+ i3     True" X9 H3 \- Z* {
    4     True% G$ t3 S( ~! ^( ?- s* f/ k4 J( k
    Name: Grade, dtype: bool
    / P2 _0 ], B, y, a$ m! B5 V+ Z2 [
    res2 = df.Grade == ['PhD']*df.shape[0], s2 E( k2 {) T+ S& J8 x1 T

    # v, b) I$ N6 E0 i+ }6 B7 E# D) H  Fres2.head()
    4 e7 i* S& r5 [5 h6 u2 [Out[33]:
    * Y6 ^, y# l. }& o! X) t4 f$ [0    False- s; L( U6 Q/ V0 o8 N
    1    False% H! H3 F: H$ S1 F7 V
    2    False7 G; E4 u  s9 N- q
    3    False# N5 a0 a1 n) J; [4 \1 A+ L. g
    4    False' a7 b) {( v0 m1 d8 V5 s3 s9 r
    Name: Grade, dtype: bool
      a3 W& d2 i; {5 P; y( p# q7 A
    $ v3 C2 K# S( J9 Zres3 = df.Grade <= 'Sophomore': M' H' |- H. x1 S/ R; ?/ Y
    9 `  J) K  Z# C8 V; w
    res3.head()1 X" l# g4 y& t
    Out[35]: % c! I& i( L9 M: ~
    0     True
    3 \$ Z1 m7 k2 G2 K; s1     True; d! L; @3 H' q
    2    False0 ]7 W, s7 L( D
    3     True
    . u  K: l! |$ X- Q2 H9 A4     True
    2 _) ?  t' r+ m3 Q9 OName: Grade, dtype: bool9 R" Z! o- N7 U1 V2 r4 h% s: K
    1 P8 I, S% q& M' j
    # sample(frac=1)表示将序列随机打乱。打乱之后索引也是乱序的,直接比较会出错,必须重置索引。
    + {5 Q) i' ?2 Q) fres4 = df.Grade <= df.Grade.sample(frac=1).reset_index(drop=True)
    6 C4 ?; d% F2 H7 @7 f' t& u6 e2 `! E/ ?
    res4.head()4 o8 y2 @: a2 e& Q0 j
    Out[37]:
    ! t- d4 M5 a* o" H: \8 V& C0     True$ G! b* u  e# o
    1     True
    3 l# r5 d9 T6 i2    False
    . |) h" h- w# k4 Y/ {" I% m# a3     True6 P1 _; R3 }/ A# m  |/ V9 c9 @
    4     True4 g# f0 Q- d1 E& T$ @; x
    Name: Grade, dtype: bool
    6 f" r9 p* h3 D' `, G5 T7 M  @9 r) _9 w% H$ T" ?% l' b$ L1 {; d4 V; x6 p
    15 E. `, I2 _  T- d% d7 x- e
    2
    , e6 u+ K8 e. t34 J8 T( A2 B2 h& {3 \
    4
    ! o! T8 f5 H: W8 |2 X5- g( F) }4 z6 ~! ]- g  u2 m$ [
    6
    6 ]8 W1 M9 z' k7 \70 ^& [+ L/ Y" P2 l1 V0 Y7 A
    8
    * b) b% [+ N' l, _3 _( ]9
    ( h4 @+ d) `0 B6 E% C2 L; q10
    7 U+ ^8 f9 R/ S! l% U1 a11, Z8 j; }# x4 V+ q; Q0 L' Y( b
    12
    ' {- D* d' r( I7 @% }: y13
    + {5 d, I+ s' {+ o14
    ; f$ g9 e/ M5 }/ t15
    5 W3 n( L0 W) F- m( Q16
      M. O) J# _2 U, p, R. w* |& ]17
    ! ?" u; R1 O: q6 P, _18
      [' u& h3 Q- n; H. l190 d2 d. q5 D5 A2 s, T/ H
    20
    0 p: f( P0 Q+ T0 T' k8 N$ s21
    0 o# r1 v' Z# }( m7 m3 h1 f220 k! o- Q1 g2 q) e
    23
    ! j5 ~' v5 n/ x' n- }8 ?0 N24
    $ O) _: h' ^" I( k" u25
    ( v8 x- g# ~/ i/ }3 N26" k7 I, n2 n5 p4 \- h" ]
    27
    , r# F) f& k0 V/ P9 v28! R+ t. d; ]' w/ [; G
    29% A9 D  h8 ]( w. Z* \# o/ K
    30
    / `, D0 o+ b. C( j0 V31* t9 u4 n& D0 h
    32" y9 Q+ E5 l- C% R) y, Z' c# s
    33
    9 @5 X8 O8 r  o2 x) f$ m34% e* F1 V* [  T6 B0 {7 c
    350 b& w+ w8 m: x5 Q' u1 l% F- Z9 A
    36# U0 H( D, ?7 J& z5 q2 e
    37
    3 p( J: J! _& z0 F. r  h38' k& [! F8 x$ E. e# F  }- T
    39- L! z  N* V1 h' `, U1 Y6 {
    40
    9 a6 N- a' K! k! {' K41
    2 _0 d1 L; P3 O" n4 V5 R7 ~% o42
    9 ?  Z# l+ w2 y43
    ( z" {/ a9 }4 p4 x44. T& G" h" F9 i* u) t
    9.3 区间类别
    , v6 M) B# j' _7 ?% G+ R9.3.1 利用cut和qcut进行区间构造; ]" P3 D( r$ _- W
      区间是一种特殊的类别,在实际数据分析中,区间序列往往是通过cut和qcut方法进行构造的,这两个函数能够把原序列的数值特征进行装箱,即用区间位置来代替原来的具体数值。! H4 B! U3 o1 R, j! l: ^! b

    ! a$ ^* S: A) tcut函数常用参数有:
    " g- ]% V5 S* m; P' obins:最重要的参数。
    / ~+ o! _/ _# g! e! C+ j) F如果传入整数n,则表示把整个传入数组按照最大和最小值等间距地分为n段。默认right=True,即区间是左开右闭,需要在调整时把最小值包含进去。(在pandas中的解决方案是在值最小的区间左端点再减去0.001*(max-min)。)7 |7 ^+ t: N1 g/ f
    也可以传入列表,表示按指定区间分割点分割。- a' @* W7 D+ Y' c/ G9 W
      如果对序列[1,2]划分为2个箱子时,第一个箱子的范围(0.999,1.5],第二个箱子的范围是(1.5,2]。& l, a% j8 M# q# X* V4 q4 L  w
      如果需要指定区间为左闭右开,需要把right参数设置为False,相应的区间调整方法是在值最大的区间右端点再加上0.001*(max-min)。
    - t9 d6 S4 b& U$ D; x
    8 C) Z1 U( J  K" c7 F$ As = pd.Series([1,2])  {7 @0 {. O: @! k+ H
    # bin传入整数7 D& C1 m: M" H! ^  P( O% s& n

    6 [+ S6 ]  j. z2 r" `7 H* t& Lpd.cut(s, bins=2). g# a7 X4 {' p1 k( A
    Out[39]: 4 T6 a  }( V+ m6 T
    0    (0.999, 1.5]* T1 R6 R2 ~- {$ B
    1      (1.5, 2.0]
    % n+ r7 @. v4 G! \" fdtype: category' a, k0 x" r6 J. D/ ~. I
    Categories (2, interval[float64]): [(0.999, 1.5] < (1.5, 2.0]]
    # J8 e$ a, v$ H' E- @; Z
    ' D9 p6 R! j2 ^, `/ \0 Vpd.cut(s, bins=2, right=False)
    ( \8 _1 s/ B8 I5 LOut[40]: ( u% i( z! }' y' b
    0      [1.0, 1.5): M  F# _# M' e! r- Z) n$ u, S
    1    [1.5, 2.001)
    ) Y, n  o$ r9 E, \. T+ Wdtype: category
    ! Z" b7 Y: v4 M+ x! ]% cCategories (2, interval[float64]): [[1.0, 1.5) < [1.5, 2.001)]/ ]5 s( X3 p) T" s9 ^

    ( a3 ^+ _0 _+ {0 J" Y8 i2 ]; C" O; i4 F1 Z
    # bin传入分割点列表(使用`np.infty`可以表示无穷大):
    ( Y' m( j1 g( n$ S9 {$ y7 A2 tpd.cut(s, bins=[-np.infty, 1.2, 1.8, 2.2, np.infty])
    9 l% e% e2 Y) v& {8 Z& pOut[41]: & m+ F# t: z' B
    0    (-inf, 1.2]3 @( O9 u3 j1 D  e4 p) @" @0 \
    1     (1.8, 2.2]0 o, R; C9 P& ?8 O4 D! g2 i2 A8 t
    dtype: category8 O0 l1 f- g& l! F5 v3 S
    Categories (4, interval[float64]): [(-inf, 1.2] < (1.2, 1.8] < (1.8, 2.2] < (2.2, inf]]
    2 H" d3 v: z$ E( y, Y5 U% g+ f) n8 N& Q/ F" s
    1
    $ V" n; f2 j# x. _+ \/ e2
    : n$ {1 `0 c9 B1 T$ v  n3, Q9 H: ]% Y, D$ {( {5 a( K
    4
    ) h9 j; `4 D' t* l1 G7 |5' `/ q% i$ D+ q  z+ ^
    6
    4 V6 x0 P! L: J6 z4 s7
    * k- Z$ [8 l0 k) J4 c4 c8 e. z* g5 k8
    8 \1 ~  J; B  ], n4 v0 b9 V! I9
    ) ~/ ?5 k7 a3 f0 N- y108 ~  o* h2 U( U& g
    11! F2 L" @: i' E- a
    12$ \7 [* v" g4 Q; @8 `9 c, ~4 `
    13; }9 l5 y1 X# N' o' s& M' f6 j- }8 Z, b
    14
    : k& L( j# j' m( _15
    3 K# F  O1 c, m' j" e2 U16
    / C& |! ]* _' i9 r; f1 c2 z17) U3 p5 ?" j  h  i6 F
    18# ~, y3 k7 k  o* B* x0 Z
    19
    , u' @' v! H; f# P20# J' _1 W$ G. r4 S! g
    218 r; R6 G! T. e2 r7 V
    22
    1 f/ U' `2 V1 M* l23
    4 H$ y  W! n& I1 \% A: S24
    ( p5 l2 b" J2 L0 S252 q  [0 o$ `2 F( a! {. h: i
    labels:区间的名字0 M& K' T( n& C, f
    retbins:是否返回分割点(默认不返回)! U- \$ r, H7 b) i  e+ [3 Z' S
    默认retbins=Flase时,返回每个元素所属区间的列表
    5 L; ^0 t6 q5 S% Q( T$ H9 `- Bretbins=True时,返回的是元组,两个元素分别是元素所属区间和分割点。所属区间可再次用索引取值
    5 I6 {, W6 ~0 t1 p0 d7 |; ~
    + W, Q# H. ?  L' Y5 Z: vs = df.Weight
    ( `% j# J' s1 j1 d5 x6 U( y) b+ ?res = pd.cut(s, bins=3, labels=['small', 'mid','big'],retbins=True)
    2 `1 l8 a$ i/ g- tres[0][:2]
    6 b# Y! O& |- R6 E" N. i1 m
    8 O+ u6 e: c) P/ vOut[44]:
    ; x9 I2 K- Q+ J0    small
    1 t; f0 _7 J8 N" ^8 k1      big
    2 o) [, x5 i4 o9 F! Adtype: category, K3 Y( L4 G$ c# q, e
    Categories (2, object): ['small' < 'big']
    $ m; H/ |4 G* V% {6 N
    9 B) k3 y, A* g# ]res[1] # 该元素为返回的分割点  a9 w" r0 b1 B3 w; a# h; Q
    Out[45]: array([0.999, 1.5  , 2.   ])3 ?' S% H( v& g
    1
    0 J- M4 X5 g+ ~- H2( W( D0 s& _) h+ I  J+ I
    3
    , h$ K9 P1 C4 G/ a4# k7 C9 M5 q- P0 |" _
    5
    * s$ q4 f; c4 k, i63 d# [# T1 P# t% R) v1 T& T
    7/ V  C. A6 V8 ^3 \6 c( E5 d
    84 J; \7 n. R0 E5 l1 [0 q: H
    91 B8 B7 a6 E7 Z- e4 N! \/ U
    10* y! ~) P1 F7 Z9 H% ^' M
    11
    + h. k- j- H+ g; q! \8 x125 m* S* t; A$ K8 X5 S
    qcut函数。其用法cut几乎没有差别,只是把bins参数变成q参数(quantile)。4 L  m& N6 ~& K" v0 R5 [
    q为整数n时,指按照n等分位数把数据分箱
    ) v; ^, t/ A. u( w. R0 o  }7 cq为浮点列表时,表示相应的分位数分割点。( o! b' N$ A5 R
    s = df.Weight8 P: @5 m6 S( z7 _
    3 ^5 v7 \/ G+ D
    pd.qcut(s, q=3).head()
    & V7 x. ]) Q) \0 C+ J; {0 }Out[47]: 2 [2 N, I# g, N
    0    (33.999, 48.0]- A7 w  N8 p6 h+ h0 d& @
    1      (55.0, 89.0]: W. G+ p0 Y. B4 [, C; K
    2      (55.0, 89.0]
    % j  @# w- _; J. `, Z" T- Y' z. h3    (33.999, 48.0]( G( I3 V& C# ]4 x/ Z& |
    4      (55.0, 89.0]
    . z9 C$ |: H' r2 \- BName: Weight, dtype: category
    # x. A3 g" S7 Q- W7 F2 n- UCategories (3, interval[float64]): [(33.999, 48.0] < (48.0, 55.0] < (55.0, 89.0]]
    8 c6 h: _. X: ]& X7 _
    ' _' _& i+ d3 I8 C7 c7 Lpd.qcut(s, q=[0,0.2,0.8,1]).head()
    ( w/ Z3 i% t9 v1 s5 q5 L. LOut[48]: 6 {/ U5 `. q, p% `  m/ G2 y" {
    0      (44.0, 69.4]
    , {( q( z0 u- W6 v1      (69.4, 89.0]
    7 n% {8 f; R- n2      (69.4, 89.0]8 N0 l2 |  L6 P/ a0 T' Z% \. l
    3    (33.999, 44.0]$ b. O- n8 m/ H7 J* t/ W. F! T
    4      (69.4, 89.0]( Q  L7 I& Z. r4 k0 w# J
    Name: Weight, dtype: category
    6 j0 c( x1 D5 g! F0 K  wCategories (3, interval[float64]): [(33.999, 44.0] < (44.0, 69.4] < (69.4, 89.0]]+ s9 }( t  _6 G4 M) u' a: y# w
    - s/ y% g" g+ Y" \$ C  q9 w1 \/ c
    1! E0 h$ H, n3 N# @+ y& [+ e
    2* K3 t4 J- }* j
    3- F$ }) J7 q9 T4 v7 q6 c
    43 e7 P. ^/ k  R2 @
    52 K( U/ Z' L0 G
    67 P1 T. }$ ?; ?: W
    71 y1 O$ v1 ]% q, O
    8
    4 H- r% T  n0 b  d9
    0 c0 }' ^7 e7 ~8 C% W10
    / ~) t4 C/ t" a/ o8 P11
    6 o$ W# S, A) p; |; s# W128 Z# t& d4 K1 `; i
    13
    7 A# k0 R, x0 i) O144 d' |! O' q3 j" P0 u: z. u
    15
    ! \' Z0 J4 v- W( ~- G. b- o16
      m% M9 c# f( T0 v1 d- l- p' n* ^5 ?17
    % X1 y+ G3 U% i: e: T18
    7 D+ x: v1 W: E& D+ W; n8 w9 Y19( q  T) A+ s  q
    20
    : U  D+ O( Y+ D& i* K6 {2 i. L0 h4 D21
    1 P9 K( D9 K" X7 O9.3.2 一般区间的构造8 M' }0 U" f; c2 z6 k: X6 }
      pandas的单个区间用Interval表示,对于某一个具体的区间而言,其具备三个要素,即左端点、右端点和端点的开闭状态。
    8 H+ g# A2 T& U1 ]* r
    2 J2 y& z8 k( R3 R! c开闭状态:包含四种,即right(左开右闭), left(左闭右开), both(两边都闭), neither(两边都开)。
      G4 R: i' V  Dmy_interval = pd.Interval(0, 1, 'right')( J& n3 g* W# ?# E3 A1 S" }
    + M1 T9 @( l+ m, e( o& |
    my_interval# b5 }* j7 j; M1 }
    Out[50]: Interval(0, 1, closed='right')
    1 k  E8 y9 G6 f1
    4 M( T8 s; S# D% ]! T9 q2* e8 p7 y+ k6 W
    3- g% d  {2 ]1 w' K' ~) k# Z
    4- l3 w& e8 k0 i4 F3 e) B6 F
    区间属性:包含left,mid,right,length,closed,,分别表示左中右端点、长度和开闭状态。
    : u4 p% |% q& n- y  @使用in可以判断元素是否属于区间
    ) n" L' _" z6 z用overlaps可以判断两个区间是否有交集:
    ( H- G7 t+ l5 M+ g" u' m% p  d  N0.5 in my_interval+ V* O. ~0 _% F/ o8 X

    4 L* n" |2 b+ g  r7 ~8 GTrue+ V9 ?( w0 K& d0 }7 v( [
    16 K) U2 _. Q+ @3 ?8 t3 s
    2
    9 D* J& }2 F, o1 A2 ]' T8 a6 w% `3
    & M& s% a4 `& g: o0 Dmy_interval_2 = pd.Interval(0.5, 1.5, 'left'): u  H+ e/ V, ~
    my_interval.overlaps(my_interval_2), d: {" m1 ^9 V4 R3 @- M- _
    4 k7 z) Q( s( [. k
    True
    - l; h4 @3 V+ p3 `3 X1 Y2 p9 n  A1
    # v/ V+ O- [$ a, j: F* r8 _8 Y2
    : ^- f1 b" {1 |1 U3 [! [3
    $ E' p9 x# \$ m4 m8 I" i4; q9 Q' Y9 |. c; u! h" C
      pd.IntervalIndex对象有四类方法生成,分别是from_breaks, from_arrays, from_tuples, interval_range,它们分别应用于不同的情况:
    " D0 L5 }. L9 S7 x/ L0 J
    6 _) }$ [; @3 t/ V  H8 f: a  k+ Bfrom_breaks:类似于cut或qcut函数,只不过后两个是通过计算得到的分割点,而前者是直接传入自定义的分割点:
    ! ?) W' r, s; i  ~pd.IntervalIndex.from_breaks([1,3,6,10], closed='both')
    ) G/ K; G. [, S" U: Y% N* C  Q. g6 h
    IntervalIndex([[1, 3], [3, 6], [6, 10]],
    ' K) m& U( S1 z. m& ^/ s5 @               closed='both',
    + m: I; }' T- L0 }* Q4 U* ^. o               dtype='interval[int64]')
    ( `/ b+ n$ I0 n, U1 x9 ]# G1" v0 y& ]- Z- U
    2. N8 ?) G4 a) |
    3
    $ i. H5 B2 I, b; e  |41 p% I$ l1 F! S3 p7 O' F
    53 D/ Y2 a' w- @6 Q4 o* u
    from_arrays:分别传入左端点和右端点的列表,适用于有交集并且知道起点和终点的情况:
    2 c- R. z4 O, V7 ]3 Q( S4 T7 w7 Q1 dpd.IntervalIndex.from_arrays(left = [1,3,6,10], right = [5,4,9,11], closed = 'neither')
    , e" y' \, q/ H8 ?" s% @, t0 O) u9 A9 H. s: _3 C
    IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],) n: }8 ]# i$ h. A+ n- c5 [
                      closed='neither',
    $ e$ E9 a( \+ q                  dtype='interval[int64]')1 `9 J7 g. n% t5 m7 O3 I5 w
    1: h% f9 M! P) `+ s6 ~' P& f
    2
    $ y$ s$ H$ [; u" F3
    ! p) _" u+ F; {; |4* M* D# g6 r6 Y! k! O( T, N( @0 R
    5: u  C  q! n" e5 I# i; i* Z- a: s6 L
    from_tuples:传入起点和终点元组构成的列表:2 i& r( g. \/ O# i  D
    pd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)], closed='neither')
    6 l6 D+ ^# B8 w# x* F) d$ S. @/ B! X
    IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],
    $ Q; F2 t' m( W7 y  g+ @7 C              closed='neither',! t8 b+ |7 o" `; f9 t0 E. e/ ~# z
                  dtype='interval[int64]')0 \  M( E1 C8 l1 Y
    1. u+ Z7 d7 c4 o6 f6 ]
    2
    , s" K# M0 e! g  O( g. V3
    5 U( j$ @& H+ D/ S* ?4
    - J9 \$ K9 n8 s8 Z  [5 v9 ?; [5
    ; C4 t/ b; C; ^) Ginterval_range:生成等差区间。其参数有四个:start, end, periods, freq。分别表示等差区间的起点、终点、区间个数和区间长度。其中三个量确定的情况下,剩下一个量就确定了,从而就能构造出相应的区间:
    ; t! ^' c2 z0 ?" u' Cpd.interval_range(start=1,end=5,periods=8) # 启起点终点和区间个数
    ' J/ S9 n3 O; q1 f# ?; JOut[57]:
    6 C9 Q; N0 n( v: W* [' p# J. f- kIntervalIndex([(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]],
    . a* I4 j5 ~/ o' B; W, \              closed='right'," w2 E' E  U: H, a
                  dtype='interval[float64]')& e+ P  A9 z) ?2 `2 @" g  w

    & O" L3 \  a) ]% h; ^pd.interval_range(end=5,periods=8,freq=0.5) # 启起点终点和区间长度+ E/ R. F. I; o4 S& n5 j
    Out[58]:
    ; U, ^4 [. w" k2 H$ c% @0 EIntervalIndex([(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]],- B8 a/ U  s( W+ c( Y& r
                  closed='right',
    , [, G! Z- m5 k3 h( \# L              dtype='interval[float64]')% n" \) ~4 k& h0 T# |5 W8 ~
    1
    2 M% ]+ |7 `% j$ O# Y0 }0 @, u5 b2
    4 x. `9 u6 e  ?32 K) ^9 ?. W, F. o1 b: E: U$ r% |
    4
    ' r: S2 v, B) w4 _53 ?+ T; \% v" G( N9 C/ N
    6
    - ~& z. b3 J% f* g6 n4 T+ l: [2 j, U7
    ) O+ {9 N2 _0 A3 ]3 \8 n1 }; h, E8
    & P  c+ C4 Q) h: S: K9
    8 Z# F( v' s( b$ H10* |; z) m/ J3 k) y. p
    11
    9 q/ E( D. p) c+ U( S【练一练】$ a, L/ ~( N6 F  E+ ^* v6 W" h$ _2 `
      无论是interval_range还是下一章时间序列中的date_range都是给定了等差序列中四要素中的三个,从而确定整个序列。请回顾等差数列中的首项、末项、项数和公差的联系,写出interval_range中四个参数之间的恒等关系。
    & L! D4 g' |' G# A
    0 j1 `( Q& D/ T/ A  除此之外,如果直接使用pd.IntervalIndex([...], closed=...),把Interval类型的列表组成传入其中转为区间索引,那么所有的区间会被强制转为指定的closed类型,因为pd.IntervalIndex只允许存放同一种开闭区间的Interval对象。0 }' B# w8 B) X+ i+ x- H4 W
    . o3 U( P* D4 k' A  j" @$ v1 l
    my_interval
    5 R; I$ _- }& _1 R/ R8 XOut[59]: Interval(0, 1, closed='right')8 ~  F8 o. W& P  ^2 s0 d
    . N* ~( O' I3 ]) N
    my_interval_2) b6 {/ {0 @! h9 C* j/ f
    Out[60]: Interval(0.5, 1.5, closed='left')
    $ w" P: {) d5 ?/ p) r5 y5 }# \% m" V8 X5 E
    pd.IntervalIndex([my_interval, my_interval_2], closed='left')0 n9 ^, Q( D0 p: o% X& R
    Out[61]:
    5 F8 X( V/ r& \2 iIntervalIndex([[0.0, 1.0), [0.5, 1.5)],
    , A* `& c) ^9 Q! i* L- j4 g              closed='left',; Q) ~$ S! K1 b
                  dtype='interval[float64]')9 J# ^1 r9 u% c
    14 _, Y  l3 r" j* X
    2
    ; g! a, t: ~/ N% n2 s4 B3
    0 b( N- O7 f: w* V- e- V1 U- J: D2 }8 W+ f4
    ; Q! B7 m2 y/ _# W6 h2 _57 l8 I: `1 L6 j& i) L
    6
    , E  P! B# G7 d/ D4 e9 b7 V7
    . _0 J+ c" [- T, P8 o, b84 g+ D" U' j3 T. k* D
    9
    6 L" {7 b# h* p' G' h% K6 {103 s: d/ O2 N" h8 C3 Q2 K
    119 T6 a8 Y( O, ]: E( F* q# N; P5 A
    9.3.3 区间的属性与方法8 B0 i' d8 i- e$ U
      IntervalIndex上也定义了一些有用的属性和方法。同时,如果想要具体利用cut或者qcut的结果进行分析,那么需要先将其转为该种索引类型:
    6 Y% B/ L" I, E' m
    + X, G# X# Z1 p( T0 n" K' n6 G4 Ns=df.Weight
    ; b! A: o0 k1 k4 x) rid_interval = pd.IntervalIndex(pd.cut(s, 3)) # 返回的是每个元素所属区间,用具体数值(x,y]表示+ L3 z; s$ |# I$ r( g+ \
    id_interval[:3]
    % W3 u9 f+ d, V; ]# z* s/ e( G" r6 X9 O2 T$ Z( ~
    IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0]],: W! e2 `+ ?# X) S* i; T1 r7 O
                     closed='right',
    9 @4 T; v3 W1 \& @+ T" D( Z5 g) Y                 name='Weight',
    8 i# P: Q1 _' k' g! x7 ~. A                 dtype='interval[float64]')
    . O: P! @3 g% U# }6 `2 I1
    % g  l) p% o" _  M/ _$ U3 T1 i# i24 J7 C! z/ d3 `  W. x
    3
    # Q( D2 h  u/ H: d/ j46 H0 [) |( n( C
    5, j( U4 d/ A+ i$ n, o2 I2 i
    6" u4 O! T/ I+ R5 D
    74 k0 G$ ]6 d2 ^) d7 H/ D
    8, p, W" ~8 v2 q; `# r
    与单个Interval类型相似,IntervalIndex有若干常用属性:left, right, mid, length,分别表示左右端点、两 点均值和区间长度。
    3 ^: t3 T" u2 k1 |id_demo = id_interval[:5] # 选出前5个展示4 b& i7 `' b  ~7 j' n: j
    . b+ ~3 Y8 p2 Z$ b/ X4 a
    id_demo
    * A7 d0 W, W2 [. H" |4 _: O" i" g- f, mOut[64]:
    3 I+ Y4 _& V% G: N/ |0 m. T+ Q2 DIntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0], (33.945, 52.333], (70.667, 89.0]],7 I  k, P' T, B9 D
                  closed='right',6 J8 t, ~+ n: N- U) ~8 f1 d
                  name='Weight',
    # s, K7 O( z1 [! P) d; f6 {              dtype='interval[float64]')4 N  g# u2 K+ v) T" d) |/ f( t

    + f; R1 c7 r$ |- S" xid_demo.left # 获取这五个区间的左端点& L& g$ m" q4 G1 x+ Y
    Out[65]: Float64Index([33.945, 52.333, 70.667, 33.945, 70.667], dtype='float64')& C) F) e' Y! M/ I3 W
    9 ?3 u6 L2 }  s! O. i  C
    id_demo.right # 获取这五个区间的右端点
    0 R1 D) ~0 I5 R8 i- m$ Z- JOut[66]: Float64Index([52.333, 70.667, 89.0, 52.333, 89.0], dtype='float64')3 F' r2 e6 `2 G" D
    / n. p% }' k9 q7 A) T* M
    id_demo.mid
    4 n/ B0 m: e, ~, S% T& [Out[67]: Float64Index([43.138999999999996, 61.5, 79.8335, 43.138999999999996, 79.8335], dtype='float64'), Q/ B& T6 N% X. m; O! [2 p) i

    + Z* w5 ?5 C. yid_demo.length8 g$ G+ j# ~$ a# {; H* L2 J5 q
    Out[68]: ' ]9 Y1 d0 _' S  z* l5 v
    Float64Index([18.387999999999998, 18.334000000000003, 18.333,
    * J8 f& A+ X6 {0 F+ G" F              18.387999999999998, 18.333],; O5 v$ S- ~- P: {% D' |0 v
                 dtype='float64')/ P5 z" E8 v) A8 u

    - V) t* ~& @6 y$ K  P" ~" y1* _& A6 b' i3 F% ~: {
    2" Z% D2 w& D" ]' v& w$ r
    3/ ^8 }! S% f7 n( v
    4
    4 n; p# m# I# h8 S2 e5: n3 j6 l; J" L$ B( @3 k
    6+ v* c3 K5 M7 m4 Y) w2 ~
    7
    # o' U7 L( Z9 M4 j0 _' t* I89 u( j' Y4 ]$ X4 x* M% x3 N/ m
    9
    & v; v& ]$ |1 N10
    7 k- Y7 v  Z: U  q0 n6 u- G7 k11( G  u( n- X9 Q1 }
    12$ L2 O2 F- S$ r. b1 }, |
    13/ c2 H7 \8 L- m6 `7 R3 t
    14+ G) @+ w: B. s+ u5 }
    15
    ! I& C% v- c) q$ B4 `  `+ |0 {; O16
    + B. A& O8 E( N7 D3 |175 s. b5 y6 _7 f# d$ d/ i9 G4 s$ |! {
    188 d2 `3 b- U' o
    19
    ; k; C* U9 I' v20( z+ N( M5 w( J, ~- W6 ]  w
    21  f9 s+ t/ S: p( y7 G4 r
    22/ |& T5 [& D. P2 o  B; l: d: `
    23' C5 U0 F/ z0 c1 x# J4 C- m
    IntervalIndex还有两个常用方法:, I  u: e" F- W
    contains:逐个判断每个区间是否包含某元素* R$ c5 P! u( l! l/ Y+ h- \: T
    overlaps:是否和一个pd.Interval对象有交集。
    4 P* M6 }1 q% X/ [7 s2 w  Vid_demo.contains(50)0 a1 Z' [( g- R0 T. r
    Out[69]: array([ True, False, False,  True, False])
    7 e8 {8 Z( a; ?  c1 v1 Z0 N
    7 U  r: p) n8 J3 M; Jid_demo.overlaps(pd.Interval(40,60))
    1 y' p! S  ]) y1 i1 mOut[70]: array([ True,  True, False,  True, False])
    + z" X2 V5 A  ]" T# h1
    2 P6 J5 M+ z  m5 R) K  y. c- z. U2
    ' E3 V: H; A/ j5 e; G  I3# h' a1 v  H+ N
    4
    8 y8 W4 K0 r, ]6 _; s. s! t53 j3 M* I2 Y% [9 K5 j* H
    9.4 练习
    ' U* o9 L) x% Y: c) ~( cEx1: 统计未出现的类别& G* B8 \  T8 w: v5 J: N
      在第五章中介绍了crosstab函数,在默认参数下它能够对两个列的组合出现的频数进行统计汇总:3 i, C1 o4 H' @2 ^* B8 i

    # [1 Z" \0 c6 q9 ?* |+ g  jdf = pd.DataFrame({'A':['a','b','c','a'], 'B':['cat','cat','dog','cat']}), I( A7 g: i: k1 N: h
    pd.crosstab(df.A, df.B)% n* ^' k+ s2 L3 B
    * p5 J  z7 @) Z; f% V
    Out[72]:
    5 Q1 R6 R* o& B1 Q# bB  cat  dog
    0 V& L4 R, H3 n4 t# F* p" R/ g6 t* CA         
    : b" G: N" i* u+ m  j  I8 D3 {* Q/ Ta    2    0$ z  ]0 W0 F- t" M& }
    b    1    0
    # B+ l- Z+ `. G) Y" J$ E% R3 k5 bc    0    1
    9 U+ y" \2 s9 b8 l1
    ) }8 o: v$ ^0 @% Y2
    8 C4 b7 F7 |; G) A9 U; }+ G33 g. w: A: s) j) |' ~1 q
    4
    ; |( g6 Z+ B" H5
    ( G8 s! T( s- L) Z* ?0 |* o6
    $ Z* O; j6 }8 {78 J9 }5 n" C3 F( x  W: H7 c5 t  C2 d
    87 s# e! c" s  Q3 L1 D5 W
    9
    : S$ A8 x! o  o  但事实上有些列存储的是分类变量,列中并不一定包含所有的类别,此时如果想要对这些未出现的类别在crosstab结果中也进行汇总,则可以指定dropna参数为False:
    8 `: L/ a8 Y7 g5 l5 d' \  Q3 e% u' J/ }8 {& `* l/ S
    df.B = df.B.astype('category').cat.add_categories('sheep')! o+ `9 q9 I8 t+ m& c8 T: ~4 `1 O
    pd.crosstab(df.A, df.B, dropna=False)
    ; d, r2 K( G. `# l0 s7 `; C1 ~- G
    Out[74]: $ A7 u3 O# z0 J& M# U
    B  cat  dog  sheep2 O( f, W9 x2 W6 V3 t9 N
    A                 ! U/ U# J& v7 }+ p+ p( k
    a    2    0      0; s: T# r; B- b- K
    b    1    0      0
    $ l& H' A6 ?* d" `. i$ N& N* Kc    0    1      0! V: L# m- \2 I$ T# |( Z
    14 O! G0 X7 Q3 {/ Z5 h+ _. F
    2
    ' V! D; m& `6 d% X* Q3 p  _3+ i" e  G( A4 X) G4 t( q1 X
    4# s9 m! D* d* }; O2 b
    5; x/ u7 O# R/ q) B
    6
    / C4 H; n. P: O7 j7
    / R0 P0 H! H  u1 a" A# F. t* c4 `' S8- E( O2 T+ @( V1 v6 P7 R; V, a' i" y
    98 @  o: M, z1 G# e" ?' R
    请实现一个带有dropna参数的my_crosstab函数来完成上面的功能。
    6 u" L& x0 C/ D; U. W+ V! Q/ ]% F7 f. }6 E, t# }
    Ex2: 钻石数据集$ n+ I+ ~0 H6 y( t  O1 V, q
      现有一份关于钻石的数据集,其中carat, cut, clarity, price分别表示克拉重量、切割质量、纯净度和价格,样例如下:
    ( f& v. d+ \' z. l4 A& C# g- }: ]) S: }# p6 Z' [+ c
    df = pd.read_csv('../data/diamonds.csv') ' a- |7 h) q% Q8 F/ j
    df.head(3)
    5 L4 v6 X" l' Q2 l+ v* Z" H! X& I: }
    Out[76]:
    * D  C) F% T$ k: S! X! E, e& n   carat      cut    clarity  price7 @' t8 P; W0 V" V2 s) v) C4 A
    0   0.23     Ideal     SI2     326
    # U5 l% K* H6 W0 w1   0.21    Premium    SI1     326  e" Z" S% T5 z8 z# N- i! W: Z7 \
    2   0.23     Good      VS1     3274 a+ V0 O6 q  h4 w( W
    1
    6 K" W9 N, h- ^% n: B27 \1 V/ B) j  o) U  K
    3
    ) u$ j  T! E8 ~( H" t0 Q3 R4# T1 q& Z/ N+ b0 Y1 _/ M
    5
    5 Z6 n9 M& F) J3 G6
    7 W8 L- ^" O* l9 O1 P3 z4 U78 N* q' G: a9 I0 I# w
    8
    , ?) y' D- ~0 C9 m分别对df.cut在object类型和category类型下使用nunique函数,并比较它们的性能。3 C6 g+ E* R: T
    钻石的切割质量可以分为五个等级,由次到好分别是Fair, Good, Very Good, Premium, Ideal,纯净度有八个等级,由次到好分别是I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF,请对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。# X$ i/ V8 B  [0 z1 s8 x4 k2 P
    分别采用两种不同的方法,把cut, clarity这两列按照由好到次的顺序,映射到从0到n-1的整数,其中n表示类别的个数。6 ^2 p" m& K5 ~2 x$ G
    对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。
    # u+ e' m( K# d' V第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
    6 I0 I  S! D" e- r: M对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。
    9 C* Y- m! J3 M5 x3 j) |$ y先看看数据结构:# l8 l+ J# s/ n; @0 i! ^3 S

    - s2 F7 _: {" K% G' wdf.info()& p' I" W5 Y7 Y! V0 U" j3 L/ B
    Data columns (total 4 columns):
    " ?; Z  [) t' y" U! | #   Column   Non-Null Count  Dtype  
    5 A9 Y0 R8 n$ v; j/ k1 i9 h) a---  ------   --------------  -----  
    3 q# n7 W% c' F( W6 L4 | 0   carat    53940 non-null  float646 j* L' D4 l- D+ l$ h7 @
    1   cut      53940 non-null  object
    # N6 O. J; F( k& ` 2   clarity  53940 non-null  object
      g* V8 i. R) n2 `. l" ^ 3   price    53940 non-null  int64  
    5 i8 q6 U* a5 W& v" \dtypes: float64(1), int64(1), object(2)9 x6 a  I' j" s2 L+ ]3 A7 _
    1
    % _: c% y' t- c6 ]: _' c2
    5 l+ h- x8 j! Y! ?( E/ X! |3
    $ Y" U; P# @# E) k4 o* @4, [8 d* n2 m7 O8 O8 y
    5
    8 U3 p, C# ]4 Q* f) a6 a% ^67 h+ l) F( b: d0 ?: |: a, t3 U% q
    7
    8 J7 d! ], |- \6 c. v* I. P8
    8 Z' O  B6 D3 ^1 W- ^& {9
    3 d4 I) @2 m, j6 F" R: K. j! V比较两种操作的性能% d! u  e/ A$ f" |+ e9 Z4 l8 G
    %time df.cut.unique()
    , b6 m+ d, [1 t! }" O- S+ l. \1 Q2 N* R
    Wall time: 5.98 ms1 g) |! Y  r8 o# S: ?$ }( w
    array(['Ideal', 'Premium', 'Good', 'Very Good', 'Fair'], dtype=object)2 e- I% Z9 M9 R/ d  g
    12 R" x1 W7 |# |: ]0 j2 a5 p/ X. f: b
    2
    1 z* L9 P, L1 B; S0 R$ G' z9 v) u3
    ( M7 K: u) Q9 i6 K: N4
    & K( p4 A. `7 d- M$ a! u; {- _* q  t+ s  ~%time df.cut.astype('category').unique()
    , j+ y* N% Q3 q5 F$ l0 i1 \1 i9 h# T+ r7 u, Q9 L! y" V
    Wall time: 8.01 ms  # 转换类型加统计类别,一共8ms
    2 P7 H/ I. a$ d* d" G9 J['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']4 m: j  f, P* |& _# u6 ~
    Categories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    + ~; f" Z# f2 c18 r9 g& y  v& a3 o( a% a) v  i
    25 ~& [" d. O$ y. P+ ~" I1 @
    33 b, v9 @. d9 w4 K# W% p8 o
    4. F% z9 v2 H5 v3 I# ~
    5
    9 Q: v" I9 b# U: G" I! Adf.cut=df.cut.astype('category'), T- v( m: C" X. C) E% K7 D, H1 B
    %time df.cut.unique() # 类别属性统计,2ms; g( w2 T9 l6 h9 o- _

      R$ @4 j% i7 m* jWall time: 2 ms
    . y5 ?0 `$ ?) p0 `['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']) S% l  ?$ ?* j+ t, @
    Categories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    7 P4 z% O2 j1 H) G# u- q19 \, f. o! \" P  I
    2
    - W, [+ D# U3 F- l$ A3* L7 r8 A1 d( }) X
    4
    # o2 G# B! z' B2 g8 a5( O* R6 U8 G( r! G
    6! r! S+ M4 }3 d+ m
    对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。
    0 X) T  u6 l. U+ H; j. _. Hls_cut=['Fair', 'Good', 'Very Good', 'Premium', 'Ideal']$ e1 Q1 F# `8 ]& m5 t8 E1 x# e
    ls_clarity=['I1','SI2', 'SI1', 'VS2', 'VS1', 'VVS2', 'VVS1', 'IF']  D/ {4 ?1 j& a9 g" t
    df.cut=df.cut.astype('category').cat.reorder_categories(ls_cut,ordered=True)  # 转换后还是得进行替换
    7 X) |( O7 E* V' Z! E4 |" ?1 Zdf.clarity=df.clarity.astype('category').cat.reorder_categories(ls_clarity,ordered=True)! P0 G. e2 u; h' v
    6 g. ~2 a' E2 _$ D0 A/ F
    df.sort_values(['cut','clarity'],ascending=[False,True]).head(3)( q7 Z: N- R; \7 a6 ~! S

    0 w4 Q; P7 e# e" j8 b        carat         cut        clarity        price
    : _# Z! j  M" W9 r$ g( G315        0.96        Ideal          I1        2801  v8 G9 u4 ?6 r
    535        0.96        Ideal          I1        2826
    " L' O) R/ h1 Y% R: [2 v* |8 q% f551        0.97        Ideal          I1        2830
    / q) ?& [! |. A  N/ V2 s+ ?1* H+ d6 L/ e) n7 y" T
    2
    ! M1 ~7 w' L+ r" y- |9 O3
    ) ~* A+ U9 t: Q- L$ t4' I1 d! t9 N# k! H4 l
    5, v3 g# x" J$ u2 i9 K, D5 Q& Q
    6
    ) d& v7 l9 [# N- Q5 W77 T7 X. a4 H9 ]& ]2 n
    8
    , h5 I% F, ]7 W6 H, V4 }) V9" W8 U' _  f$ ~
    10
    ' \3 B& h, W) Z" S% F/ x9 c118 i# H+ V% |$ o' R( N
    分别采用两种不同的方法,把 cut, clarity 这两列按照 由好到次 的顺序,映射到从0到n-1的整数,其中n表示类别的个数。
    8 Y: \8 a* c: q- d/ S0 {# 第一种是将类别重命名为整数
    ; ~0 i" R. e+ l$ wdict1=dict(zip(ls_cut,[x for x in range (4,-1,-1)]))+ K; J% n& S) {- }8 d% u/ F. W6 M
    dict2=dict(zip(ls_clarity,[x for x in range (7,-1,-1)])): m( X$ I4 n3 m( G. O

    : y0 b' k3 P$ D2 I4 odf.cut=df.cut.cat.rename_categories(dict1)" m3 J3 _+ A" K/ q+ z1 k/ Y5 d
    df.clarity=df.clarity.cat.rename_categories(dict2)% k5 S( w' P. g/ g  j- T5 o
    df.head(3)) k3 c& c  O! ?: e  ~8 n# b' {

    2 Y5 {3 H& V4 w# K% J        carat        cut        clarity        price
    . `. `2 g! i8 S& Y5 J0        0.23        0          6                326: h% E2 }$ O) P) e% o7 ^& L. {: h
    1        0.21        1          5                3269 y8 B9 ~+ p7 D# |
    2        0.23        3          3                327: }1 |- ?1 }7 S3 k  L+ [9 ]
    1
      R( ^1 y% e) f2  H4 A: t' O7 n; i' |
    3( u6 g9 m' Q( M, k2 t2 l: T) ]
    46 @: T# z+ d! B2 q+ P
    5
    5 C+ n  X! m' @( m6$ P% c+ a! @) ~. c4 ]7 W/ a
    7
    9 K$ h, i0 U& n0 k1 J0 }81 T2 e/ Z7 A3 C( d0 @! `
    96 ^7 W7 g4 [% M& a& A$ e. U
    10, U2 Q5 B1 |5 Z) V/ o3 s
    11" K! D. x' I  \* Q
    12
    4 n" ]% k' }$ \: L: u# 第二种应该是报错object属性,然后直接进行替换
    - V( ~7 P" X3 v( Q& zdf = pd.read_csv('data/diamonds.csv')4 F7 X5 M. S9 s8 B# p
    for i,j in enumerate(ls_cut[::-1]):
    - K/ ~% ^4 N' }  m0 X    df.loc[df.cut==j,'cut']=i
    : E. `7 ^* c: |
    1 C' a1 m% K% }8 f' ^3 }/ [for k,l in enumerate(ls_clarity[::-1]):: a2 W6 Q. ]" [( B' c# Q3 B% [
        df.loc[df.clarity==l,'clarity']=k4 }2 m- Q4 f3 N( e$ K
    df.head(3). A+ R2 L' E4 E% V: x  A" [
    / Z( f9 S9 `' ?2 \6 b! T; t
            carat        cut        clarity        price' w! y" L& E3 V* J% [  }( v
    0        0.23        0          6                326
    7 f; q0 k5 E/ c; R$ F1        0.21        1          5                326/ s1 f3 A' l* P/ ]( |  C4 n
    2        0.23        3          3                327/ R) J2 e% u  m+ {6 ~
    1
    # ]4 X3 p) H$ q5 K( d7 J: v21 R% }& s& h4 ?9 K/ C* M
    3
    ( t* @3 j8 H7 a, {: b0 A4
    ( x4 @, \7 _) Z. K8 S52 V. G% z/ x0 k( G" ?
    6
    0 ?- I4 C$ f& a; N9 ]7
    1 G1 V4 U- p+ ]8 y. v8' s8 }$ J2 u5 e+ z& E3 e4 _) i
    9
    ( j6 d: p* S  A0 W% R10) L! |* `& h' L, z7 i# @2 @# @
    11* R" `! z- X2 @' z4 Q5 t
    12
    ; D5 w, D, V4 {  N( e8 C3 W* P4 E2 O0 v13
    " [4 _; K; M" c9 n- a2 g' f对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。
    / ?* S+ C# q/ R8 O3 L. z# retbins=True返回的是元组,第一个才是要的序列,第二个元素是分割点/ a* |4 \) E3 l
    avg=df.price/df.carat; Q( s: ?& g. j* [
    ! u1 \+ Y2 c8 p! @' u! k3 p  |' W
    df['price_quantile']=pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],
    9 }. }3 X3 C! I& I/ ?                              labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]$ {; e* W* E# M  q- E
    ' v& L& Z6 j4 h, t4 D# }
    df['price_list']=pd.cut(avg, bins=[-np.infty,1000, 3500, 5500, 18000,np.infty],
    * X1 V4 {  {, h                              labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]
      `9 Q" f( V  H- n/ T- t& Ndf.head()6 \8 T( s2 b8 n: ^* b' q, ?. J, G
    . D  a- y1 Y) X
            carat        cut         clarity        price        price_quantile        price_list- w4 G4 A7 ~7 U4 ~1 i7 h
    0        0.23        0                6                326                        Very Low                Low
    , R$ r( \' T( R% c7 x! u2 f1        0.21        1                5                326                        Very Low                Low
    ' O. w5 D# o; a" H2        0.23        3                3                327                        Very Low                Low
    9 r3 E  r1 X0 Q4 O3        0.29        1                4                334                        Very Low                Low
    / U2 F, ]- D6 b4 S% N) W4        0.31        3                6                335                        Very Low                Low                                       
    " ]! U7 w. @2 b5 L% _7 p3 O
    ) w. ~! w5 b" r7 K+ ^15 |  p9 M+ z: g9 {/ z- m  \( ]) W
    2
    1 S# L! \& \" g3
    9 b2 n* J  u) @- T" @; R2 ]- `" y4
    8 d# l7 Q; i( n- N: u& w8 a56 a* @% I2 Q6 O3 e5 v
    6& i' j9 @; R2 P& m2 t
    75 h% j3 F! Q9 R; ^! X1 r( l
    8
    4 k& _. P; D8 f( `) Z9$ n1 s1 D' E& r+ j% R1 z
    10( H3 U4 M& c4 L, \& b+ G
    11
    : `' H9 X/ U- u12
    3 c0 N0 N5 _3 v4 ?13
    ; S( `" s9 R% a. y143 [) @- |9 X3 g! L* o
    156 L  W- \2 p2 R8 E2 Z
    16
    : W8 U+ I' x& J3 j分割点分别是:6 N1 f8 L' i# P" h4 T3 c: N- N( @

    ' x' N* n0 e1 X0 ^7 }+ Y% [array([ 1051.16 , 2295. ,  3073.29,  4031.68, 5456.34, 17828.84])- j$ M0 u6 \! E' e: [
    array([  -inf,   1000.,    3500.,    5500.,   18000.,    inf])
    6 i3 n) n. g, x3 E8 Q1
    # U( w- s8 ?8 R2
    2 p) G# }9 A( i/ q# h第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
    - H: C( _5 F6 b" J- N9 C+ ^df['price_list'].cat.categories # 原先设定的类别数/ m* a0 j. Z" {. U# a' Y
    Index(['Very Low', 'Low', 'Mid', 'High', 'Very High'], dtype='object')
    8 g% r# R0 }1 I( c7 Q! t
    " ^7 L5 \0 a9 L: g# m5 ]- ]4 sdf['price_list'].cat.remove_unused_categories().cat.categories  # 移除未出现的类别% r/ n( h1 c, u  k0 k& F+ m
    Index(['Low', 'Mid', 'High'], dtype='object')  # 首尾两个类别未出现
    + J- x$ C0 r: B* x1! Q* M# e. D, b+ m$ F
    2- s) l5 G9 y- c
    3/ y" M+ [9 d: ]  Q
    45 |7 \  t% U. j! c" N: R$ A
    59 J: g0 @- S6 L& o  v# m
    avg.sort_values() # 可见首尾区间确实是没有的
    4 j  O* y* E6 I31962     1051.162791
    ; [& ^* s; |- i2 j) F15        1078.125000
    " E. n2 f9 `' R% h4         1080.645161
    ( U& C, t0 [5 i, Z1 l28285     1109.090909$ E% c# u) P) x( b
    13        1109.677419! @7 K5 Q3 M) J6 J* J( |% `6 ~8 j* Z
                 ...     # B# t9 V$ ]  E7 ]$ B5 |) r
    26998    16764.705882" I: q- B4 k  q* b! a* }, y$ N! O
    27457    16928.971963
    6 C  C3 }7 k, r27226    17077.6699030 k! z/ q: Z. n/ F0 O; C0 c, J& O
    27530    17083.177570+ b/ }$ N" B0 o7 m: d4 o
    27635    17828.846154& |! o$ E1 O! \  i5 M2 j$ c
    1% t( V5 e3 {+ @5 e- m
    2# E$ p" K: Y0 E9 d, M) ]
    3: G4 C5 t- `% }1 ^8 [1 t6 ^1 y
    4, V* [7 v3 }1 E4 G" i" S
    5
    / l. S; x; K; `" C+ x6 C% I3 ?- c6; y  d: J: {5 Y' i6 }/ p! R7 r# Z4 `, Y
    7# v1 L' R3 N& ]; a. X% u
    8
    / N) T% w0 |# |: Y6 Q- z9
    8 R8 r' u( H! V, r9 v106 x5 @  ~& [4 Y' J. z
    115 t5 L' ^" O% `6 \. h7 V3 H" {2 F- k- P
    12
    : R; y: R0 N2 ^, t8 v4 G  q对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。
    . W# q6 z( ]# R# 分割时区间不能有命名,否则字符串传入错误。+ ?6 [- L' M, Z0 ?) s' n
    id_interval=pd.IntervalIndex(1 _$ e) v& }0 a  U/ G
        pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],retbins=True)[0]
    # l+ j) X: A- W6 m5 ^9 A6 n                            )2 L! _0 t, s: \+ T% Z, x
    id_interval.left7 P6 T9 ^+ m& S5 @
    id_interval.right' y; M. R$ Y0 @7 q( e6 `6 p: @$ O
    id_interval.length                           
    " m% Q; _  B; |9 K8 X" z. {14 V: h' z! j+ Y" h
    21 @& L8 C/ a8 A" V2 \% P/ i
    3- g' Y* n6 L3 m* Q+ w5 d, Q
    4$ |5 K& P2 C# c& Z4 Y( e
    5
    4 Q8 E) P# g- W0 B8 O60 b3 u1 c$ h& x+ V; G5 Y
    7
    0 s. M6 q+ F# v" ^  w第十章 时序数据
    4 i) S# @: O6 ?9 u. t. E$ w  gimport numpy as np6 ^' J7 E7 N, P0 G4 k. ]% r
    import pandas as pd6 Q# ~# A; d' u3 ^
    1
    ) k# w: K# r/ B# k; |& f/ p2
    $ W1 P1 B& j/ y$ U$ m5 X/ b7 P
    % |9 N! H! }( U5 \7 ^
    6 Q, j" B. Y# S; s) }10.1 时序中的基本对象$ b5 Y- Q- H9 l- K  }' W( f
      时间序列的概念在日常生活中十分常见,但对于一个具体的时序事件而言,可以从多个时间对象的角度来描述。例如2020年9月7日周一早上8点整需要到教室上课,这个课会在当天早上10点结束,其中包含了哪些时间概念?0 ?. S& Y: H0 Z" i+ `+ F
    ! }9 N8 y2 ~8 ]5 B6 w5 D
    会出现时间戳(Date times)的概念,即’2020-9-7 08:00:00’和’2020-9-7 10:00:00’这两个时间点分别代表了上课和下课的时刻,在pandas中称为Timestamp。同时,一系列的时间戳可以组成DatetimeIndex,而将它放到Series中后,Series的类型就变为了datetime64[ns],如果有涉及时区则为datetime64[ns, tz],其中tz是timezone的简写。4 m* N% Z6 e  Q) ^
    9 D  z5 X* j5 M& ^$ u, y+ D& S
    会出现时间差(Time deltas)的概念,即上课需要的时间,两个Timestamp做差就得到了时间差,pandas中利用Timedelta来表示。类似的,一系列的时间差就组成了TimedeltaIndex, 而将它放到Series中后,Series的类型就变为了timedelta64[ns]。) p) ?& t2 Q. u

    + x" |4 m+ Y1 x0 d; ]5 q6 p会出现时间段(Time spans)的概念,即在8点到10点这个区间都会持续地在上课,在pandas利用Period来表示。类似的,一系列的时间段就组成了PeriodIndex, 而将它放到Series中后,Series的类型就变为了Period。4 P7 h/ G* J/ F$ c/ s* m
    ; B: }2 D4 o: R1 A! s
    会出现日期偏置(Date offsets)的概念,假设你只知道9月的第一个周一早上8点要去上课,但不知道具体的日期,那么就需要一个类型来处理此类需求。再例如,想要知道2020年9月7日后的第30个工作日是哪一天,那么时间差就解决不了你的问题,从而pandas中的DateOffset就出现了。同时,pandas中没有为一列时间偏置专门设计存储类型,理由也很简单,因为需求比较奇怪,一般来说我们只需要对一批时间特征做一个统一的特殊日期偏置。
    4 A" J; a& E- W# V4 z  D( ]2 m3 J1 b% U7 e# h+ f- [9 ^
      通过这个简单的例子,就能够容易地总结出官方文档中的这个表格:
    5 g6 H4 g/ A2 i5 ]! E' g, i! t: ^2 W+ t/ v2 o7 T6 \
    概念        单元素类型        数组类型        pandas数据类型
    9 Z" M. \5 {/ l) v7 r% x& PDate times        Timestamp        DatetimeIndex        datetime64[ns]0 [8 h1 g# I; f) ?
    Time deltas        Timedelta        TimedeltaIndex        timedelta64[ns]5 q1 ^/ Q  g! M* _, q
    Time spans        Period        PeriodIndex        period[freq]- d. f, S$ w7 U. [- w! H( C# C
    Date offsets        DateOffset        None        None3 x8 g) C' B- u( a7 a
      由于时间段对象Period/PeriodIndex的使用频率并不高,因此将不进行讲解,而只涉及时间戳序列、时间差序列和日期偏置的相关内容。  }3 V0 `& R6 m) A* e# D
    ; b# j" b0 |$ W% T* U
    10.2 时间戳7 ^) ^. N  N$ [) T7 E0 s
    10.2.1 Timestamp的构造与属性; n: x3 m  b9 N; s
    单个时间戳的生成利用pd.Timestamp实现,一般而言的常见日期格式都能被成功地转换:. D, E/ R, K' B" Q5 E
    1 k2 \  h0 Y! L7 @+ }
    ts = pd.Timestamp('2020/1/1')
    - ^# K% s( y' Z( R& H; e6 ]) i
    - c8 z/ s4 Q! X8 its
    ) q! o. Q8 R! T% v% COut[4]: Timestamp('2020-01-01 00:00:00')& ^6 t: B" X* T  Q- R; t. A( N

    - Y8 ]* ?7 G: n/ {  Ets = pd.Timestamp('2020-1-1 08:10:30')6 N/ n' v$ Q! W, V2 h/ r; o. g
    + _- d& x: j4 @2 e
    ts
    : P( n' n" Q+ @& I' W. FOut[6]: Timestamp('2020-01-01 08:10:30')
    : D8 _. a5 {2 l8 E  z+ |0 S, E* F1# R3 u6 R1 z* W
    2
    / I* i  B; B9 w6 T, K3
    , B6 a2 Q2 w  _5 L5 m4' r. N" u2 J& k# p
    54 m" {/ g7 Q* M8 u3 Y+ ]5 }
    6) U/ m) I) l, _0 `% C# S
    7& W( H1 Y0 T- s
    81 h7 S* i% k+ n
    9
    * C+ a3 l3 V: B! R% f通过year, month, day, hour, min, second可以获取具体的数值:5 a1 D) g; V. N. M0 J- [  z
    3 A/ `: t# @' W4 c( s: `
    ts.year
      C- o# H# O4 T$ kOut[7]: 2020
    ) _) {0 Q4 m2 W. o3 F& y- ?. z. J. P# C* w
    ts.month) C5 O0 Z' |  L, D! i# @# s1 z+ s: E
    Out[8]: 1! U' f1 f3 A: g' q

    * L3 y: I8 k4 Z. ~2 r; \6 xts.day9 S  u1 Z9 ]- v
    Out[9]: 18 Z" j. a5 f. m8 }! J2 |
    / V0 q+ p5 w4 N4 [* M! O
    ts.hour1 w& i4 m$ q# @! I
    Out[10]: 8- z7 A: ?6 N. b! W# }
    # N% j: D" b" Z: f3 E' {/ G
    ts.minute+ W$ b( C6 Q: G; i
    Out[11]: 106 }% X6 E0 q) v- u
    $ k1 b% I& z3 \" v+ S! K' v
    ts.second- T- F" f7 _% S/ ^" M$ w, c
    Out[12]: 30
    : O/ L2 i2 }! o, A( s! {
    5 Y) o) g, [, ~% Y9 ^2 a1  s: d5 Q$ O, e/ }+ N/ o0 d
    2
    7 c5 w: u+ l0 N6 z3 `+ M5 Z: X+ Q3
    ( E+ \/ b4 N& H$ ]$ Y$ w  K( Q' S" l45 j1 Y$ n. O7 P/ @+ [+ t. H- ]
    5
    / Z5 B: C9 y8 R; O6 j6% R' [! ]% K: W! U# P) j
    7+ c2 l6 U; C5 S/ A: k' c
    8
    " J3 g, e1 B8 u6 p# T) ~: b8 n9
    0 R( e! M. H6 Q10+ L. }! s5 ^; _, l" u
    118 O( e4 e5 |; h- p: F* k8 y: V
    12
    ) D& @9 S' p% P133 D5 ]8 z" O- T) M, D
    14
    8 C0 j. W, U' y9 M15- M0 J" Y  S0 g
    16
    , H9 E1 |% ^* H" K& e/ [; o17- }. i3 w$ g0 [4 Y. W
    # 获取当前时间" x( Y8 {0 u. b' @' H
    now=pd.Timestamp.now()0 n; O% ?' g5 i( ]; O$ x
    1/ F0 T) l: E+ `; t# c
    2% e! @8 F$ V1 \" O7 N8 c( b) a) j
    在pandas中,时间戳的最小精度为纳秒ns,由于使用了64位存储,可以表示的时间范围大约可以如下计算:
    8 h5 i+ M9 j; i9 DT 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): @2 t# n  K- r/ u0 x3 D
    TimeRange=
    # m/ t1 ?# @4 x0 Z8 s( B5 ~9 H10 0 i0 i  H& r& A- L% e: ?5 i* U
    9/ y% q+ g) i, |. b  i/ y: P
    ×60×60×24×3650 f" f: c' K% Z9 w  R3 S% R5 E
    2 . g& V! H( w* W* w6 @$ Q; v8 s( R- n
    64
    ; w& }3 z) }% t) H4 p1 W3 _( S/ h
    & A9 A9 L  c4 \( e: K( ]1 i/ H1 R, J1 g3 c9 o
    ≈585(Years)
    # T0 s- X$ j2 ^, u; T) {+ d6 ~4 c! z, P9 W' [( v5 q3 g1 A; q
    通过pd.Timestamp.max和pd.Timestamp.min可以获取时间戳表示的范围,可以看到确实表示的区间年数大小正如上述计算结果:, _& `3 X0 d3 t* B5 I& o

    % Y2 L2 P% J) F! K9 ~( @pd.Timestamp.max& G$ }2 U/ }9 ?" i" Z2 ^+ \
    Out[13]: Timestamp('2262-04-11 23:47:16.854775807')  x/ ~! p7 U, H' W: \

    2 g& Z3 q6 F4 t/ m* p9 Kpd.Timestamp.min( M5 E7 `" p! f) v* F( {2 t4 z
    Out[14]: Timestamp('1677-09-21 00:12:43.145225')
    ( h% U* S% U$ M" F/ L
    0 h7 v# v  r+ q& a. \! Kpd.Timestamp.max.year - pd.Timestamp.min.year
    : i6 y" S3 n+ _7 N) u, aOut[15]: 585
    , @- E" K1 |+ E4 O3 }1
    ; p9 j/ C6 O$ I4 d( D. f* K2
    . V* @9 _4 E5 M* g6 A& X3
    3 M0 J- F; Z% k1 ]& M, D. @4' T4 {0 j" J9 }' l8 y: O% a
    5
    ' Y: k: H) n8 l6, q7 R0 W" Z( D! ~+ H. g9 f6 |
    74 z7 A+ s; Q7 p6 ]- @, K, c  r
    88 `* C# I4 L( [, M: r6 A
    10.2.2 Datetime序列的生成; e+ \" l" ]% n6 r4 u" \
    pandas.to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, utc=None, format=None,
    # O  Y: \; C+ _, Y! D' ]- D                                  exact=True, unit=None, infer_datetime_format=False, origin='unix', cache=True)! m7 ~5 G, Q( a6 c- f
    1
    ' l( c# ?, X6 u21 E' N+ @8 Q7 z4 b3 `* W
    pandas.to_datetime将arg转换为日期时间。! {- Y: [4 B+ {( C, Y; h

    8 S5 o2 v' Q4 W& ]1 targ:可以是argint、float、str、datetime、list、tuple、一维数组、Series、DataFrame/dict-like等要转换为日期时间的对象。如果提供了 DataFrame,则该方法至少需要以下列:“年”、“月”、“日”。
    5 V& g$ v' B$ D5 C; `errors:0 a1 a1 a3 y* r( v1 A% S
    - ‘raise’:默认值,无效解析将引发异常2 v: y/ q! B9 t6 [! k: V
    - ‘raise’:无效解析将返回输入* w% O& E6 {" M: W' a4 _' t
    - ‘coerce’:无效解析将被设置为NaT
      i# ^4 Z' y* B. G; E  ~9 ^dayfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析日期,例如“10/11/12”被解析为 2012-11-10。如果无法根据给定的 dayfirst 选项解析分隔日期字符串,会显示警告。+ ~' \4 N8 `% i
    yearfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析年份,例如“10/11/12”被解析为2010-11-12。无法正确解析时会显示警告。(如果 dayfirst 和 yearfirst 都为 True,则 yearfirst 优先(与 dateutil 相同)。)& v7 k$ h, v: l5 n/ V
    utcbool:默认None,控制时区相关的解析、本地化和转换。请参阅:pandas 有关时区转换和本地化的一般文档
    , U) E' N1 O1 p4 E# B4 m0 S% |format:str格式,默认None。时间戳的格式不满足转换时,可以强制使用format进行匹配。. ]' y: x2 b+ r/ {9 s2 A
    unitstr:默认“ns”。它是arg (D,s,ms,us,ns) 的表示单位,可以是整数或浮点数。这将基于原点。例如,使用 unit=‘ms’ 和 origin=‘unix’ (默认值),这将计算到 unix 开始的毫秒数。" j6 h# \0 _$ I  T+ v
    to_datetime能够把一列时间戳格式的对象转换成为datetime64[ns]类型的时间序列:- x* y1 i) Y3 y1 M
    pd.to_datetime(['2020-1-1', '2020-1-3', '2020-1-6'])' k: Z7 a8 J' D1 r$ Q

    ; n5 y  C1 q  o) r$ EDatetimeIndex(['2020-01-01', '2020-01-03', '2020-01-06'], dtype='datetime64[ns]', freq=None)# X7 H1 i/ ?0 z
    11 V: _( ?% X5 W9 W- b
    2! q; R- |, H/ p5 Q! @( O2 O. {
    3  z) \- a; h# s& m
    在极少数情况,时间戳的格式不满足转换时,可以强制使用format进行匹配:6 h/ j: z. }! y0 v" d$ \& g0 P" L

    ; }) g# A+ ~" q9 ~0 Z- s. ~temp = pd.to_datetime(['2020\\1\\1','2020\\1\\3'],format='%Y\\%m\\%d')
    1 d" z' q( }! i' V# J  s4 [temp7 q' B$ E: k, F5 K1 j7 @' N2 k$ u, J

    5 J7 `; F  ?( `# tDatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)) x7 x5 ^# x5 z) Y: ~
    1  E- D7 c8 L$ ~% k
    20 a/ ~9 t8 V+ b8 z
    3
    % z3 Q* A* F2 E3 `' X+ s* W0 w9 ?4
    $ t; v; _, w: Q7 K* T) c- ]( q: d  注意上面由于传入的是列表,而非pandas内部的Series,因此返回的是DatetimeIndex,如果想要转为datetime64[ns]的序列,需要显式用Series转化:9 Q' P: G$ o% @- k; n8 p
    , X) j  T* S, a; c
    pd.Series(temp).head()
    9 E, r. r- t9 U/ l, V# T3 @, ^" r5 V* w: i7 @( F; F
    0   2020-01-01# m* Z8 q& y$ P, Z, Q# d
    1   2020-01-03
    1 a: d' f1 `! r4 o2 qdtype: datetime64[ns]
    ) Q  z, \( v- w8 l; ?  U# u1
    ( k+ s& a/ _" [, `: l: \2
    ) O" D+ t8 D0 `2 y, y4 c3
    / i5 I. W8 M# L4 [4
    # u1 [7 Z5 y6 b3 D5
    . |  B+ T' j, C下面的序列本身就是Series,所以不需要再转化。0 b" \4 F& i6 h( y- s& ?& M" Y

    6 }$ ]. L, r% J: gdf = pd.read_csv('../data/learn_pandas.csv')8 ~! Z; i, w* |/ N6 B
    s = pd.to_datetime(df.Test_Date)3 M. y+ |. h; t0 \, L! H. F, j% g
    s.head()
    " H# F0 `3 J( Z# u
    + G% K* f& U4 `3 r0   2019-10-05
    : X9 n* _; [4 k7 @4 e' h' g( U% ~1   2019-09-04$ ]8 r* c/ J6 g2 Y: N
    2   2019-09-12
    0 `. L8 H1 e6 [+ R7 [3   2020-01-03
    ! o4 C/ ~* I4 q+ ?9 v4   2019-11-06
    : `4 M" N7 m6 ~  {! tName: Test_Date, dtype: datetime64[ns]' P3 _3 t. b( i* W8 m9 H
    1
    ' {/ V5 K! U  j/ U9 J2
    1 N; K" _/ j! e* R( X" Y3! |" M2 C9 P9 I/ Z/ @8 e
    40 t: j: d2 R* h& b
    5% P" i4 p4 ~, C' B4 o
    6! R, P' w) ^  x* E/ \) o
    7
    ' B( Z/ t6 l) P% A! l1 @( G85 p9 x% [: x8 B2 G/ j5 I" s6 p
    9
    1 x. _$ a/ {3 z0 L1 w( k& z104 S2 |. p; G& S' V; s' M
    把表的多列时间属性拼接转为时间序列的to_datetime,此时的列名必须和以下给定的时间关键词列名一致:% c9 p' c8 o" M' y9 k
    df_date_cols = pd.DataFrame({'year': [2020, 2020],& R, u) I1 R; P" i& ~3 U0 ]
                                 'month': [1, 1],
    9 w6 u( I; |% Y( \. ~8 P' w                             'day': [1, 2],# x/ Y0 [2 k( c3 w1 Y' L# r
                                 'hour': [10, 20],' \/ m: ?$ z: o7 ?
                                 'minute': [30, 50],' K% p9 ^6 ~* D8 H. {& B
                                 'second': [20, 40]})  t* P- g% W. v6 z- `
    pd.to_datetime(df_date_cols)0 r% `( O8 x# Y+ @: [2 D: @& V

    4 ~' [1 Z; j/ K) P8 Z4 M7 M0   2020-01-01 10:30:20
    # B5 s$ O0 d8 B0 d9 Z$ u6 p1   2020-01-02 20:50:409 w* l3 R5 p0 h% {+ r
    dtype: datetime64[ns]9 B: c; _6 f, q3 l0 b! }3 C
    14 u  D8 w0 O9 Y: T
    29 c# K1 E% a1 |9 |2 M2 \' b
    35 a. ?) E6 c' f& D& j
    4, h" \) ]- s+ J5 E
    5* c0 }8 U" U% A: P% S) K1 v& g& m% K) \6 v
    6
    ! y% n4 s0 r1 f: u$ Z! Z* s( u7' R" K) `* s( m0 X9 l8 B
    8
    + X4 ^. |  t3 [5 D4 e# F3 s9
    . O* g9 ?& l! X9 @/ r; M10) A0 `( D% G; G5 O1 R) G
    11
    ; u/ K: t& Y9 t, d: `: Qdate_range是一种生成连续间隔时间的一种方法,其重要的参数为start, end, freq, periods,它们分别表示开始时间,结束时间,时间间隔,时间戳个数。其中,四个中的三个参数决定了,那么剩下的一个就随之确定了。这里要注意,开始或结束日期如果作为端点则它会被包含:# p5 v0 Q7 d" p+ _
    pd.date_range('2020-1-1','2020-1-21', freq='10D') # 包含$ p: Q* Q: u: i" u
    Out[25]: DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21'], dtype='datetime64[ns]', freq='10D')' S$ Q' U$ x) k2 b* [' x
    7 k4 @0 K# J, k% F; n5 k" G/ a8 }
    pd.date_range('2020-1-1','2020-2-28', freq='10D')+ U) n8 Y- d! {) R
    Out[26]: : |# \1 f4 g  D
    DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21', '2020-01-31',
    $ G, T# I% M2 K6 w. l% l               '2020-02-10', '2020-02-20'],
    % f2 l; a  c5 r3 m; S# j! _              dtype='datetime64[ns]', freq='10D')
    $ `) ^# E( n% b. m" r+ E# n4 f- s. U# |, m
    pd.date_range('2020-1-1',, d- c' Z0 @9 k( X! n, ?
                  '2020-2-28', periods=6) # 由于结束日期无法取到,freq不为10天
    1 f% Y2 u, w6 |; X! W+ j/ w- @6 w5 K' f/ f0 H" S! D% Q% k  y
    Out[27]:
    ' I/ I2 X/ m  p0 TDatetimeIndex(['2020-01-01 00:00:00', '2020-01-12 14:24:00',
    9 E8 I& h6 I) r  k! l" {2 G- g               '2020-01-24 04:48:00', '2020-02-04 19:12:00',
    5 M! v/ p# y- O               '2020-02-16 09:36:00', '2020-02-28 00:00:00'],3 ~: w# E* V$ ^. @. @7 \* V; v; f
                  dtype='datetime64[ns]', freq=None)
    7 P8 x; S8 s4 I  @! v( a+ K+ ?/ C& t
    - U4 A2 G; o2 p8 r3 {13 ]( m& [2 S9 \4 C' C2 I0 @1 H
    2
    , a1 _) `( M4 F3 v+ \! r7 {3
    8 b2 n# h7 |2 x, J4
    9 ~, X) h/ H& ~5
      a0 J7 ^  V- v; [8 X2 w: \' |6
    ' K" p' |9 S/ t9 L# g' c' \  \7
    , ?- d6 L6 B, N8) y" V: i( y8 _
    9
    % T" `2 o- D8 e" g0 H10; S/ o1 j4 P2 y, a/ T
    11! z* m( R0 V0 s7 s& D
    12
    - T6 u' o1 g3 v" i13
    ! s% G+ l% w+ y5 D. r. x( f142 G' J/ d: ^4 e: {( \9 m" ~
    15
    ( M5 E$ Q4 ]* q3 f! \16
    1 ~& X4 k: ~7 i( g6 g17" x& O" p! e/ F
    这里的freq参数与DateOffset对象紧密相关,将在第四节介绍其具体的用法。
    4 V/ N4 r! S: w1 ?( i# C' `* v1 e8 ?% N/ o$ x( q! i' b
    【练一练】1 V5 f$ H8 y8 Q6 |
    Timestamp上定义了一个value属性,其返回的整数值代表了从1970年1月1日零点到给定时间戳相差的纳秒数,请利用这个属性构造一个随机生成给定日期区间内日期序列的函数。
    ' k4 M# M6 \1 A/ O% j+ k7 }/ N. f2 C+ \$ L, O( D! J. h5 @; v* C0 P, E$ g, o
    ls=['2020-01-01','2020-02-20']0 |- G3 p5 g! {4 e& j/ ^
    def dates(ls,n):9 v/ A" r! H! c
        min=pd.Timestamp(ls[0]).value/10**9
    ) N2 {: l% y- t# w4 A; E" y    max=pd.Timestamp(ls[1]).value/10**9
    1 U' B5 U- S* O3 I- M    times=np.random.randint(min,max+1,n)7 y, v3 x* g5 L* E, h2 t+ m
        return  pd.to_datetime(times,unit='s')  D- }6 {* Q* [! Y* K" D" F/ e
    dates(ls,10)   m  H0 k2 S: Z4 [3 s& g
    1 g# J2 V; `5 q; }& W2 e
    DatetimeIndex(['2020-02-16 09:25:30', '2020-01-29 07:00:04',
    0 H1 w. l, y8 S% Z! c               '2020-01-21 12:26:02', '2020-02-08 20:34:08',2 ]3 y1 O  Z0 p& O4 s5 h
                   '2020-02-15 00:18:33', '2020-02-11 02:18:07',! U4 `$ D# y6 O) `1 T) `
                   '2020-01-12 21:48:59', '2020-01-12 00:39:24',; J$ S4 G6 |- F( ?7 Q( n6 K
                   '2020-02-14 20:55:20', '2020-01-26 15:44:13'],
    : S* B; y& Y4 _              dtype='datetime64[ns]', freq=None)! }3 \# f: o9 _7 D3 h( V
    1
      B$ W$ W+ p; Q! {- F2
    ! [% K% L4 h4 u3 g$ H3
    . i( R. v( A8 w' w9 U9 p2 U1 q4
    9 Q& N/ W1 K) K$ T55 F& {- u& Q# N
    6# }# B# e! m6 I3 L% Y* A+ n
    7% G0 P3 j; [6 h1 T) Z: ^
    8. r, L+ w3 H0 l  o1 c4 ?9 e8 V
    9
      q  |: w- P1 d- q0 O4 h10
    + |2 `$ w* Q3 y' v  X% M116 @$ Z. b4 `! b5 ?/ Y
    12* w1 l' F- R9 y" I  G3 m
    134 r; @4 W) v1 f
    14
    # P4 {  e/ L7 r7 `2 fasfreq:改变序列采样频率的方法,能够根据给定的freq对序列进行类似于reindex的操作:. S" [; J) B$ P
    s = pd.Series(np.random.rand(5),
    , ~7 ]% k$ D# ?' @+ I( D            index=pd.to_datetime([
    9 a2 z4 ^6 ^8 O- b6 L) r. @                '2020-1-%d'%i for i in range(1,10,2)]))
    : e/ A' I1 _: {, N  T& p& B4 W9 B! y  X1 h' U+ @/ [% B3 J! q

    - _9 c* p! A! x7 Y% ss.head()
    5 x- S) r0 e! S+ ?3 f2 lOut[29]: ) I- R; x8 o3 \: R% Z  j4 k
    2020-01-01    0.836578# t7 P) }1 c9 Y" o
    2020-01-03    0.678419
    , y# S/ Z/ K- N% \# v- ~2020-01-05    0.7118976 k* V: _) k( N1 p
    2020-01-07    0.487429
    & ^' C+ S$ q7 e, w2020-01-09    0.604705, t" z, ]4 y* _0 a+ [' T
    dtype: float64
    2 N- A* T& S! U. x" z& K
    0 J9 H$ k- G. G( @s.asfreq('D').head()
    ! _; K, p7 b" I2 h# x& }Out[30]: # I( \) a: E* u& M+ {& D
    2020-01-01    0.836578
    - g; A: G+ U4 {* V& y2020-01-02         NaN
    % h/ O/ ?/ @' S- H8 _- @2020-01-03    0.678419
    # P/ J- v$ J5 x# R) T; U6 J2020-01-04         NaN' u! Q; F  [9 [2 e! R# g
    2020-01-05    0.711897
    # p8 f" C$ M( A& I7 C$ i2 d' lFreq: D, dtype: float64
    * L; P. n1 [+ n& |, H+ ^; p( k* w  N$ l
    s.asfreq('12H').head()$ w) F" z2 h; N: L0 O  }! `
    Out[31]:
    : h- u7 D6 e7 f6 N2020-01-01 00:00:00    0.836578" T( f5 G5 h) H. h0 C
    2020-01-01 12:00:00         NaN
    . x9 {" x1 ]! H2020-01-02 00:00:00         NaN% E; i6 G( Q1 v. M7 ^5 t7 F
    2020-01-02 12:00:00         NaN1 G! X$ ^2 H) i( i6 v' p+ v/ d: X
    2020-01-03 00:00:00    0.6784197 h3 B8 x8 i$ Z4 \# X8 ?
    Freq: 12H, dtype: float64; B3 s' f6 w$ M7 l

    ) e0 A* C! r7 V/ _1& d: e  u, V4 n* S# T
    2* Y- k" S; C, s& [  g1 D
    3
    ; o  r9 I; F8 b) x8 R) L4, ?9 H' b# ?$ I
    5
    , K8 `: [# p1 u4 l9 ?. m4 h9 i6, m: {( G6 E# Q- t/ i& J
    7
    $ p$ R6 O0 d8 Q7 o* M( ]8
    8 a+ a4 V" I# a* l' O/ r9, }" @; f6 X. S0 t
    10( }5 h2 z* F8 {: Y
    11- g6 N5 P2 G" `
    12
    1 A+ y, a  m$ F, A13* R  k4 t. L4 @  q- {
    14! E6 f. o9 f$ ~# H
    15
    * I' M' M- {1 S0 N! P16( b6 i. a, ~3 p* U0 z
    17" j# d2 n# b/ {! |0 ?, H
    18% v" y3 k6 S# E
    19
      v5 i% T( @! J, u$ z2 A6 i& h20
    6 I( v; O- w/ p0 p" Z21
    ( I1 O. v, c/ m22
    * B( C. |5 }( n4 \2 Z4 v+ \4 ?3 ^0 \235 h  A$ h8 ?* c, m
    24
    + C% P3 R7 ?+ L25# x3 L& F+ `) p4 ?: u# B9 `" u
    265 Y8 X; Q: D; s
    27  ?- j; f4 |/ ?; e8 }& E% T& B' Q
    28: K/ u, B# u! W* v+ ]& b  f
    29
    " X! K1 _( P" y% }% ~30& }/ X6 l6 v8 r1 `0 K+ H
    31
    " t- P* D- @# J: ~- q7 P【NOTE】datetime64[ns] 序列的极值与均值
    0 j+ ?6 [3 ^5 C5 A" w$ S, P  前面提到了datetime64[ns]本质上可以理解为一个整数,即从1970年1月1日零点到给定时间戳相差的纳秒数。所以对于一个datetime64[ns]序列,可以使用max, min, mean,来取得最大时间戳、最小时间戳和“平均”时间戳。$ Q7 R7 a1 z- a
    , S) w; |. W& |9 M7 d) i
    10.2.3 dt对象" {. N1 q% t# U. }( o$ @( B
      如同category, string的序列上定义了cat, str来完成分类数据和文本数据的操作,在时序类型的序列上定义了dt对象来完成许多时间序列的相关操作。这里对于datetime64[ns]类型而言,可以大致分为三类操作:取出时间相关的属性、判断时间戳是否满足条件、取整操作。3 l/ J/ e  ^- A$ D: D2 W4 |' W

    ; U( Q7 j+ G) m; |4 ^! e第一类操作的常用属性包括:date, time, year, month, day, hour, minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter,其中daysinmonth, quarter分别表示该月一共有几天和季度。+ o! e+ z. o/ k" S) b: f
    s = pd.Series(pd.date_range('2020-1-1','2020-1-3', freq='D'))8 X1 t& Z7 R& E5 V: j

    4 `  b' D8 T1 N6 i7 _# e& g! e- P$ Ms.dt.date0 \, \: S9 {+ v  Y7 v1 z) V$ W( l" d
    Out[33]:
      j$ U$ Q  f0 O1 v& K' V0    2020-01-01
    ( R% p- {. B4 f7 @& L1    2020-01-02
    ! P' z1 L2 ], ]% z* j) ~/ e$ V$ T2    2020-01-03- q# x  p8 j0 E
    dtype: object
    * ^/ X% c4 u& s* |. O# C/ K. u& u) \' ~& u
    s.dt.time
    9 e! C7 N+ ?% Q' B2 S# u2 N3 \Out[34]: : }/ b. s" K' A# c; c
    0    00:00:00
    4 i. h/ M- ]4 r" h3 @1    00:00:00- o3 D& G3 w, I: K7 A
    2    00:00:00
    " {# Y) X) y8 _' E9 v% j' t3 m- Ldtype: object
    ) [# T4 |  Z% A" p5 ]: t* m
    ; ?% W7 q6 r' O  C9 V8 hs.dt.day
    6 |3 i# X8 F6 a( E1 R7 `% F/ |Out[35]: 5 _7 m4 H* s7 i; K  K
    0    1
    ' p9 C2 c& P5 N) c; S- A. p: B# X3 Z1    2
    $ g+ `' r: [2 q2 ]% u- o4 m2    3
    1 q$ i5 e0 N" ?0 m4 [/ z. bdtype: int64
    0 }# ?% m' I- t0 F  l
    % }7 o) M- M  p) k* ]s.dt.daysinmonth' a4 V0 y0 T! t' D3 |! f% F* s0 x9 c/ `
    Out[36]:
    ) P2 F: l. X9 u! T9 N8 ]8 S0    31/ S- G2 G% Y' H8 Q/ Q* i, C
    1    31
    0 X( m. T8 z4 X& ?; g  j2    31
    " {) Y3 k' N4 `& K( wdtype: int647 W- d1 ~1 G  E
    2 |1 E# @/ C# ^- }2 Z
    12 M* Z$ p( e9 p4 t
    2* T; N5 w) P3 C% X( ^' O/ E
    3! l7 y, w1 [3 t/ N
    4& I- l6 W% d  F9 J5 @. }8 t' ]  x
    59 Z% Y6 L4 q! h  z% q' y1 o
    6! t5 p/ Y% Z( U- b2 A
    7
    ( \" y% ^8 U! o. ?, l8# L; A0 U" [2 ^  v2 h; k( e
    9
      U! L' f2 H1 t/ y  c! S/ N) c% G108 w! w6 l# u6 M" T$ d: {
    11
    : a1 M9 j; i( r) N: l128 Q$ H+ |+ u& j  v
    13* b* x, |6 z, s% ?! o, R* x9 c. x9 y
    14
    : ^: R3 `5 t! X3 s" v15" H0 H- x7 w% j6 ~! H7 E( E
    16
    ) Q5 _, E$ c; m) g2 M7 t( w6 r174 e* G! O! ^/ t
    18
    + o1 ^) t; D) M$ ]0 T19
    3 y( R! M1 m7 @& C% \% S. C203 c) R. ?+ I6 A" K8 v8 |8 y
    21
    $ q0 ~0 l3 p0 G2 c0 X  V22
    - h  o6 T  O7 I% I5 _$ j- L23- O5 _9 [3 C" g
    24
    / h3 I* A  b- \' _1 R) _25
    ( ~& l3 S' b' Y% J26
    * E) F+ T8 ^+ G27
    " Q4 y& K! Y+ s& m) u# Y28
    * u- M$ o4 z( z0 s; \. u299 ^; v6 k1 l& E
      在这些属性中,经常使用的是dayofweek,它返回了周中的星期情况,周一为0、周二为1,以此类推。此外,还可以通过month_name, day_name返回英文的月名和星期名,注意它们是方法而不是属性:5 }, Y  W6 M) M0 S: {# _& [8 i4 A/ ~

    9 ]8 c8 c. b7 ]& m7 D: zs.dt.dayofweek
    : T* d4 n: u: _/ g+ n% A' JOut[37]:
    9 w* P4 I. b. g. i' G& l0    2
    & A8 ^8 D& s6 Q0 ~/ y1    3# @1 v) x5 p/ D  Z0 Z6 y: q* _
    2    4- ~6 H: P+ f) Y
    dtype: int64
    ; ?" W* m5 K$ p/ v0 z4 f& n4 C" r5 H0 E. N- u4 S1 ]
    s.dt.month_name()) F- [5 H& \. t" Q2 _6 `8 t
    Out[38]:
    8 ~, o( V4 x* v0    January
    - [4 `$ x8 }/ ?1 {% P1 a1    January! u7 S4 C7 A+ L% |; u5 [
    2    January/ O/ w5 w+ _) X8 M( P
    dtype: object
    # ?' z  r: E+ @" N" W6 H6 Y4 R
    , J7 H; ]! W/ a" e  m: l1 M7 Ss.dt.day_name()5 B1 \5 K0 J9 k+ a2 @
    Out[39]: 5 @% z) \6 u* ?, d0 k* ?9 j5 q0 p& {
    0    Wednesday9 C& }0 H( s  E. @0 J& d
    1     Thursday
    . g) d! D* Q5 L1 J+ S2       Friday5 ?* M7 r6 j8 C4 b6 ?. u
    dtype: object9 ~3 [- Y6 f8 ^! _3 y
    ) L9 L; e, v' j+ D# f
    1
    , b3 k8 K0 H8 \# c/ h& e2
    7 N# y0 `, v4 q/ U) @3; s! q5 e  Z8 w( k+ [
    4! n4 Y& K+ w5 m
    5
    - F0 x" M) @2 P+ Q* X9 x* N3 F69 W, b* Y( Y% [2 h) c
    7
    1 V) ~- }- @. C8" @' K, L, K% C- P
    96 w% X9 v, @9 t8 }  C
    109 v; a7 g2 l8 |% v2 p! l- z
    112 g5 ]0 l$ a- U( Q7 U, @
    12
    $ t9 D" Y( J9 p" k; n. j, N; g13
    " P) b" B7 y( T& }6 |/ p2 G+ O0 S7 j14! ?! S" w- z& k
    158 j* D2 b( m+ K  ^! f
    16
    & g( \# M. H6 ]" a: ~0 t17- u% R3 {( P0 j* R: B
    18
    0 Z) E# c: q& C. [: z% L19
    1 P. p" n! u+ F9 t. }7 x8 }20
    " U5 b' k+ s5 b第二类判断操作主要用于测试是否为月/季/年的第一天或者最后一天:
    # B! f5 X6 o4 y. ^s.dt.is_year_start # 还可选 is_quarter/month_start. j" }3 m7 o5 }1 K$ ~" v' M
    Out[40]:   t  Z. `) [$ H6 i5 O
    0     True4 Z" ?1 q6 {7 k4 M3 _& G# e- L; A% U
    1    False- K+ [9 v, m% x5 \" i+ {
    2    False
    / f) L9 I* T& `  G/ {& ~) y, Q2 i% Tdtype: bool
    * [. p# Z! u% b$ w
    ; Y9 Z2 l( ~. w$ h0 f5 J( I  ms.dt.is_year_end # 还可选 is_quarter/month_end3 r$ F' ^1 t0 I" |7 B: u
    Out[41]:
    ! \5 W! A  A6 [2 h# C  c- Y9 b0    False
    7 N( l* A$ J5 o( p4 ?4 }7 m1    False
    ( |  G2 N5 B) K, u8 {4 S& Y2 U2    False
    ; l# _  m' |1 ?- ]/ Vdtype: bool
    , X! b$ e5 ?0 C7 e8 S, L, d  t% E1( h/ R, p5 g- V# o9 g
    2, _0 }- D& M# @' y: D2 T  j
    3
    8 b9 S. h& y, x3 ^, t4
    8 f3 @: b* r, l( V% P5
    : R4 ^$ M2 T; E! B1 w& p. H5 E% i64 Q- `+ o' F: i4 I$ ?, }
    7' s  x& c5 a/ a1 q) w# H
    8
    ( J+ G( o6 F3 N" t/ r: Y+ G9
    / ^, `- o& Q4 E+ V) t) n10
    6 g9 Q/ o0 c7 F: h- ~11. @# W0 G; f4 ?% j
    126 t7 r; m3 T5 X! O6 e
    13
    2 I1 Z6 s! A% ?) x第三类的取整操作包含round, ceil, floor,它们的公共参数为freq,常用的包括H, min, S(小时、分钟、秒),所有可选的freq可参考此处。. E. I9 B% L7 a- ~7 v
    s = pd.Series(pd.date_range('2020-1-1 20:35:00',
    2 U# r0 H" B2 N5 E/ p                            '2020-1-1 22:35:00',
    3 p6 Q- q, P6 r2 r$ o2 b& X0 H                            freq='45min')), j9 F* r- M0 [2 V3 k5 `9 _

    : ^7 z1 s, F( m
      E* K4 y$ d1 ]# y! P) l9 ss
    / o+ ?$ v$ c! V3 `' \8 w! X* A9 u4 LOut[43]: 6 S& K, V$ h6 G
    0   2020-01-01 20:35:00
    ) q1 s  v, Y; Z2 D3 a8 m1   2020-01-01 21:20:00
    ! F- H. C! B. F* W4 d2   2020-01-01 22:05:00+ F' K3 q! c3 J) ]. X6 ?2 T) L/ B+ A
    dtype: datetime64[ns]+ v6 Z( S$ X. T% d
    ( I* Z2 j4 f+ G! g" t
    s.dt.round('1H')7 V' |4 J* ?# \. @8 _
    Out[44]:
    ( U$ f) z; Z& W" r0   2020-01-01 21:00:00
    * T. i$ }3 d' P" H, ~1   2020-01-01 21:00:002 a* d  ]2 q8 w# r! b1 V( k
    2   2020-01-01 22:00:00
    1 A4 }- o) Q6 {8 Wdtype: datetime64[ns]
    " H1 U6 ~$ M9 J# h+ G. C8 F5 }: m, o0 n+ Q! p% K
    s.dt.ceil('1H')' @$ j0 h+ m7 k! R* w" R
    Out[45]:
    7 x7 L! R2 c7 U( Z& Y/ m; l! s0   2020-01-01 21:00:00
    , x1 V9 F7 i; l4 _, A+ _; b6 N1   2020-01-01 22:00:00) t  f$ S. r3 `) R, z5 m( j
    2   2020-01-01 23:00:00" u3 |9 j- b. E. L( T" u) V
    dtype: datetime64[ns]
    $ m1 i2 v- B: h( E# Z% ~
    ) h: g3 x: J5 R% zs.dt.floor('1H')
    ( P+ A! H6 }  dOut[46]:
    8 {0 b9 b: |  ?6 R( {0   2020-01-01 20:00:00
    9 b5 N& m! ~  ]  d4 M( ?1   2020-01-01 21:00:00
    " w1 \% ^7 G' z$ U- t2   2020-01-01 22:00:00+ b" N0 W( M! {: a3 m7 F1 G2 ?
    dtype: datetime64[ns]
    3 x# |9 b( X2 B! D; y' S4 y6 B! H0 n- i: R3 c9 B/ L
    1
    . k4 a3 n8 I+ n7 }" r& y2
    9 z; [. t" I- B, T9 b/ f3
    / Y- O% k% A8 m/ ~4
    . `7 j' D# M+ n58 N% B  f/ O  n6 Y) V" ~2 D
    6
    ( E/ x! }1 l( S( p7
    $ p3 x2 h4 b0 f7 p7 o+ O" J8
    4 h9 ?% n0 }2 M& P9+ d) P- Z/ C0 W# H8 o* v
    10, m) z6 \( D) J! k9 M" W6 L1 l! v
    11
    ! m: C" Q; M: ]! `; n" b, O125 G! B' U$ N6 f) d% Q. g
    130 y# \% z' V6 m) r* K
    149 p3 m( t. j% P- a; `  d
    15
    " P7 Q( S( [5 s# [: D) B16" F, N7 d+ S2 _) m8 t
    17
    ' a1 f8 O0 T9 f+ E$ V  @, ?18& G" M$ B; B4 g7 [8 N
    19
    : S: b8 y# l7 |5 `20+ ]: ~6 g7 X$ W% A" u) c
    21. f4 W0 g1 `6 s; w
    22
    & L5 f7 F& I' ]$ i. O$ L23
    ! P7 `8 H, I( S3 w: b8 C% ?# U7 m24
    " b+ y& ~  s$ M3 Y1 g25
    4 I, N0 h& Q4 v5 M26
    5 j7 e6 a& `0 `0 L9 ^27
    8 G2 H0 I) d: V, b6 w# [. [7 u28
    % p3 M( r  H% h1 S& a* E29
    . ?9 n' ?; h; f301 C  f/ J# j1 m* [
    31
    & a& P. k: ~3 h: p0 T" |) @; H32  O7 a  N* N" u" m' r
    10.2.4 时间戳的切片与索引
    , W0 ~. J: e5 C, F0 C1 I- T  一般而言,时间戳序列作为索引使用。如果想要选出某个子时间戳序列,有两种方法:) P/ q" f# A) x5 U4 ?" q* Y5 t* I

    7 @; t5 w+ _/ ~. C* t- Y4 Z3 n7 w利用dt对象和布尔条件联合使用4 w- D3 Y  w/ L. y( @( p. W) P
    利用切片,后者常用于连续时间戳。5 p6 J) I' S* x1 Q4 e$ N
    s = pd.Series(np.random.randint(2,size=366), index=pd.date_range('2020-01-01','2020-12-31'))
    5 A2 @& o- x4 o- v( F2 |2 yidx = pd.Series(s.index).dt1 D, E3 @, x& l6 U
    s.head()
    4 x% L4 y( p3 O+ k
    1 k1 D6 J. O/ A2020-01-01    0
    . @+ `8 d; P" A" d  v  [5 N2020-01-02    1  J% _+ y4 r5 L
    2020-01-03    1* e2 [& a* c  S
    2020-01-04    0
    7 K% A. ?/ U: T3 S  d" v2020-01-05    0, r7 O7 ~% W1 E- t: d, M4 ?
    Freq: D, dtype: int32, |6 L; B  c* D
    1
    # F; y  b, C8 A# o4 h' }27 ?: u) Q# U7 s8 Y" q
    3
    , |7 O7 i7 Z6 {' h+ e+ G4
    * T  ]3 d. J' \. \( }5
    + a3 N2 j0 \! r1 H" M6# B. t4 ?( e9 L2 V  H1 X
    7
    7 S7 \7 n+ x2 X8( w. U9 C' A& l  G; D! T
    90 \) ~! _+ J; n8 h" @" ^; t
    10  G9 q' p" F$ j* g" |  n
    Example1:每月的第一天或者最后一天" w  V  o. G2 W" }- [" E/ j0 `
    3 u1 ~- Z/ _* [2 @  ]) K0 a
    s[(idx.is_month_start|idx.is_month_end).values].head() # 必须要写.values
    - C$ f$ X7 V$ c1 ~Out[50]:
    7 N& ~5 G8 h. @; y1 o6 E2020-01-01    1- |& @; ]( V1 f& R+ L6 [
    2020-01-31    0* \3 N1 D3 K. s, Z; X
    2020-02-01    15 G4 {9 d' i% i" v+ b8 }9 V2 _
    2020-02-29    1
    ) F/ }6 t+ o* s+ ^& ^+ b2020-03-01    04 M# H0 m2 W; ^
    dtype: int32  P+ H9 x$ S$ W+ T& \
    18 H, F9 p0 g5 h" e. f: S
    2. K% N8 X6 t2 s
    3& J; v$ [: {! m+ R, p8 ?" m
    4
    * [- \/ p% i3 Y# m; {) T( e, S5
    $ J/ ~8 D3 d( ^9 k# f$ e6, s: h' Z2 i" _/ H3 h4 P  P3 Y
    7& V0 ~- ?% K4 W  \! |& w
    8
    9 E. g; _! y/ }9 IExample2:双休日
    ; H/ v( X% N3 t7 x
    ) X  o( Z- A# B2 L' C) Is[idx.dayofweek.isin([5,6]).values].head()
    5 h1 [! N7 ]6 ]; C' mOut[51]: 7 Y% g  h$ a0 V3 w; z
    2020-01-04    1
    3 o% D$ M# m5 p- b# w- }2020-01-05    0
    " e9 X) w1 Z5 g: n8 E2020-01-11    0- c/ F3 L; x9 k9 t8 _4 Y& z4 A& F! f
    2020-01-12    1" G% V0 _# B, a6 |2 b+ C
    2020-01-18    17 _' R6 s: I& S. m
    dtype: int32
    1 v9 u/ [) R1 i. E; B" b3 @1, m( [" N6 A/ [$ K  y1 s$ i
    2; [3 e' b* {6 |2 t5 ?: L- u* I
    3
    ; P( q, J& Q7 Z- B" r4
      h. \" H$ v" ~5 c2 ]5. y6 [# b, c+ a0 M
    6
    8 K2 m5 r; ?( A! e( x7 _7+ n9 G/ S9 a7 R, G% g& @& M% i/ J7 r
    8
    ( N+ q& W) m# [. @' G, E# pExample3:取出单日值" ?4 X. P; o) v1 s" ?) n/ X/ Q$ O7 w

    + u( m, B( P! w0 Z9 L4 ]. n" t: {- z- js['2020-01-01']: x# {/ I6 W" E; c0 E( N
    Out[52]: 1
    / }3 a$ a/ ^' z4 e6 b
    ; i& `. _( T/ j' @0 d: H7 us['20200101'] # 自动转换标准格式  a! m  o( }. ^: b3 {/ B
    Out[53]: 1
    , A# o2 G/ x, N7 u. k1
    / n+ D+ n' w; r/ ^3 L2
    * D+ f0 E) R% Y5 k7 U3+ R3 J3 T8 j7 o0 G: h: q1 S, Z: [8 x
    4; v# D$ k2 {' K  t# ?
    5( l: J! n! O% n6 g5 r- p( ]
    Example4:取出七月
    ( I# C( U: t. u5 l: F+ I7 ?4 W9 C: b$ F: c5 u' Z
    s['2020-07'].head()
    # G) k. J9 E( V3 _8 rOut[54]: $ V/ S8 }: X. F
    2020-07-01    0
    $ y( Y" p& ]/ w- Z5 Y  k9 Q2020-07-02    1
    0 Q. Z) j1 N" U2020-07-03    0+ }$ N1 _: `  e4 c5 A
    2020-07-04    0) S2 P  E1 V  T/ r) R7 d8 }
    2020-07-05    07 r1 D/ h- L) r. Z
    Freq: D, dtype: int32  C; ~# I7 u. u
    1
    / W, C* l, u8 y& h8 d2$ B3 @2 w3 b! v- U5 o% ]
    3, z; q) b8 Z0 B0 J) X8 L$ G0 A# ~
    4
    " b) ~5 \8 q* T" M5
    . T  ~# p7 n' P( w$ M) d61 k2 {% }" t! p' _/ Q
    77 c, e" U2 C0 `0 H/ h. A
    8
    ; _! J7 q9 ~/ x) JExample5:取出5月初至7月15日" p* u2 X" M2 |! p2 D& _" C
    ! @& ~. f% U" o+ l
    s['2020-05':'2020-7-15'].head()
    , d* v* `7 b; D! O: pOut[55]:
    2 L7 o8 u, a3 v. `! V6 c- p0 ?2020-05-01    0! s" K: h2 U% w
    2020-05-02    1
    , e. Z9 _$ q! d8 {3 ^' e. y2020-05-03    0# j2 j% }) ~2 M/ g
    2020-05-04    1
    . C3 Z3 {0 x; p2 j2020-05-05    1
    8 j- l2 O. z1 k$ ?) ZFreq: D, dtype: int32- \4 @5 q& J4 p) y$ N) q; J4 L* K

    7 q" [: D& E% p3 }! d) t* D6 y( e7 ds['2020-05':'2020-7-15'].tail()
    9 A( A. \* q% o0 _0 D3 q% JOut[56]: ) ~$ U- L5 W( n: H( x% Y
    2020-07-11    09 b0 h2 n) e9 n4 _+ A1 Y
    2020-07-12    0
    7 j" s. K1 t5 z5 o2020-07-13    1  B, {* ]; B! i1 R+ D. }8 \
    2020-07-14    0. E. @- ~# u  y- m' U
    2020-07-15    1
    8 v$ M, A+ u" r, k( v1 DFreq: D, dtype: int329 ^. b  k" W( t* s/ O7 ~) n, {# ~+ g
    4 _4 W4 E; `6 R' z9 Z7 `* R, X
    13 I- y) O# k: y; V* D2 e- F  J! U
    20 E5 L' N8 @* L
    3
      ~1 G7 \- Y) b: e4
    7 r) t, @0 W4 e% ?, b% ~57 G7 ?  Q5 g' Z0 T5 o' X
    6" E" O6 u2 t8 Z9 z
    7+ N* N; s8 Z& J; J5 L( x) u) N
    8' X3 d7 G- `- S( Z% C
    9
    ! t9 i) I1 X8 t8 }2 o( g& y10
    5 [( i/ q/ h6 H, U* J11
    4 }' j0 p7 N' ~4 H* W12
    , l/ P0 X1 D3 G0 y0 a" }13* M  d5 i5 O, s4 ?7 \
    143 L* ^. E, l, c% S+ N9 b# @! _9 P
    157 `- ]1 R: _0 @; [5 f# O9 {# p
    16- q; u+ U: i$ w3 Z; D9 f
    176 i3 v7 J+ B1 ^2 P' R; v8 M
    10.3 时间差
    / i( y% }/ J' r9 X6 u# [10.3.1 Timedelta的生成+ e7 N8 r7 V4 W1 ~8 a
    pandas.Timedelta(value=<object object>, unit=None, **kwargs): V  B. w& w. x: u6 c
      unit:字符串格式,默认 ‘ns’。如果输入是整数,则表示输入的单位。  b: M6 \$ e/ V$ I  T. z, c" r
      可能的值有:
    . c1 I+ P) G; }: q
    6 b# L$ y, j7 e‘W’, ‘D’, ‘T’, ‘S’, ‘L’, ‘U’, or ‘N’
    5 u  z2 C+ v5 T4 Z) k) @( z+ T‘days’ or ‘day’
    ( O2 p. a8 p: V7 A2 R5 F1 G6 F‘hours’, ‘hour’, ‘hr’, or ‘h’- y, h: \8 g5 k; \5 X$ Z9 ~4 t9 i8 K
    ‘minutes’, ‘minute’, ‘min’, or ‘m’
    + ~, C( r- S. c/ e  G7 w! {! a‘seconds’, ‘second’, or ‘sec’0 e+ h5 f+ _7 ~, X9 ?7 V2 D% o9 r
    毫秒‘milliseconds’, ‘millisecond’, ‘millis’, or ‘milli’5 @4 }9 P% O; ~0 q* D& K
    微秒‘microseconds’, ‘microsecond’, ‘micros’, or ‘micro’& g# p. l2 f% Z7 @8 @9 g
    纳秒 ‘nanoseconds’, ‘nanosecond’, ‘nanos’, ‘nano’, or ‘ns’.
    . K7 l9 K  ^9 M, S时间差可以理解为两个时间戳的差,可以通过pd.Timedelta来构造:; V2 w& X, s. I2 ~! Y" y: }
    pd.Timestamp('20200102 08:00:00')-pd.Timestamp('20200101 07:35:00')1 C1 T! J9 [; a) C! C- n
    Out[57]: Timedelta('1 days 00:25:00')
    3 u" |4 B& G2 B" @2 h) y/ F0 n: E6 k: A5 \+ B" E+ v
    pd.Timedelta(days=1, minutes=25) # 需要注意加s  ~) V& g, F. ?: `' o2 y2 m" W
    Out[58]: Timedelta('1 days 00:25:00')
    / B6 W. }" k* P6 l5 C1 F5 {' ?) A* _
    / d0 F* e4 p# ~: V& Z9 W& Xpd.Timedelta('1 days 25 minutes') # 字符串生成
    2 x, ~' J7 O% _6 x! ^5 A/ TOut[59]: Timedelta('1 days 00:25:00')
    9 f/ Z( Y0 x. v' X- u" B8 r) `9 ^" x, v1 u
    pd.Timedelta(1, "d")/ j9 I1 J7 j2 `& U' v! c  O
    Out[58]: Timedelta('1 days 00:00:00')8 {8 X( c4 E% M9 k7 l( j4 c1 p
    1( d* q+ O$ H+ }4 {& p3 p
    2
    ! [7 w( |$ r- p5 |3; G% y9 `  k$ r7 R; y2 S
    4
    ; {' i" h) F2 T7 m( S7 [& E; o- L' @- ]5
    ; u' Z2 J9 m# N2 q- ~* S  k! Y! Q6
    8 a. W9 D7 ~+ b- J7
    1 f  L2 V  q! w1 E* {1 Z) x' |8, d. X: i; Y3 H9 F
    96 C6 E5 o. S7 L; G+ j$ ~
    10
    8 L6 \$ {7 O# T% U! T8 Y& c3 w0 M11
    3 X4 Y8 j% b! Q5 ]8 z6 M) L: S% Z6 a生成时间差序列的主要方式是 pd.to_timedelta ,其类型为 timedelta64[ns] :
    7 S5 T" Z9 H9 Y8 d  ts = pd.to_timedelta(df.Time_Record)  C3 w' z6 A6 b% D; F
    + e3 I: Q  P0 s8 A& H4 U
    s.head()0 |, @; ^2 j; y5 h: o
    Out[61]: , I* b: f  k# C& J' f
    0   0 days 00:04:34
    , o! `$ g+ Z4 \" v2 ^5 v% P1   0 days 00:04:20
    7 g  S2 j1 g) |2   0 days 00:05:22
    : L4 L, d+ o% s1 ^+ R* q1 o3   0 days 00:04:08% ]3 C( h& l0 d! g# Y- t* g
    4   0 days 00:05:22
    ( ^9 L/ ^* L! A% n: ^Name: Time_Record, dtype: timedelta64[ns]1 c' j# _6 i( k% M! X8 A7 D
    1
    " U9 |$ `- T$ ?3 a$ G/ d6 H2; ]0 r8 y; X9 i! v! J) X- R0 h4 A
    3* y( B0 u! K+ C
    4
    % }. z: X% f9 C/ O0 F5+ S; c/ a- w* J
    6% E8 O. X8 |' `& H) Q" x7 J
    7
    # H3 \  d; K  r' ~0 E& O2 F/ r: b8
    3 t7 l0 }+ R9 [+ ?2 @$ N( ^$ H9; v3 O7 p" S1 \) o
    10
    " J. [5 e! v$ Z与date_range一样,时间差序列也可以用timedelta_range来生成,它们两者具有一致的参数:
    % |# P' [( r, G5 S5 m$ Ypd.timedelta_range('0s', '1000s', freq='6min')
    8 ~% X/ J: l8 M2 G+ s# k; UOut[62]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:06:00', '0 days 00:12:00'], dtype='timedelta64[ns]', freq='6T'); M$ N, V. K* e  q: E% M

    5 h5 K1 b, w' }pd.timedelta_range('0s', '1000s', periods=3)& p7 P" Y! z. A& M
    Out[63]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:08:20', '0 days 00:16:40'], dtype='timedelta64[ns]', freq=None)
    1 f5 [$ t( R$ Y) o! y+ L" Y1
    8 K. i  x3 d$ k! X, H! i- l; V2
    & a+ V- U3 d% S3
    # s+ y2 G' o2 ~4
    ) `3 |; w: x) ^5 [( X( @$ J5 J5
    # w. k9 N6 |: T  V, Z! W6 ~对于Timedelta序列,同样也定义了dt对象,上面主要定义了的属性包括days, seconds, mircroseconds(毫秒), nanoseconds(纳秒),它们分别返回了对应的时间差特征。需要注意的是,这里的seconds不是指单纯的秒,而是对天数取余后剩余的秒数:# w! q& V! R: _4 A3 Q
    s.dt.seconds.head(), ]) t  T7 g$ W. }% E: [- t7 z
    Out[64]:
    6 v0 D/ q3 o/ h& S0    274" i# B  j& @/ e9 k6 C, x
    1    260/ h  F* N  u1 x. ?1 e
    2    322" ~; J: d3 Y8 ~7 y3 o
    3    248, h( }* i& [* E
    4    322" w8 T$ p8 b* o4 w! {. z% g3 V- W
    Name: Time_Record, dtype: int64
    7 d( W4 c3 M1 ^9 L2 t1
    0 h. r' h9 Q1 w! |2
    / A. c$ `% K* r# R39 p' s% L4 R+ Q% D
    4! `1 N9 U) o4 B' d7 k3 D
    5- F9 a3 _' B3 @3 o7 N
    67 C* j/ \( u3 {( R9 A9 ~
    7- ?' F. z9 r1 g. x3 @. |$ m
    81 g3 e( {5 R( k2 D, \
    如果不想对天数取余而直接对应秒数,可以使用total_seconds! a) z& q  x3 q) R& b# Z0 B' p

    $ g  D. J# G" _" b; w& K4 y: S# os.dt.total_seconds().head(); h+ g& U3 N5 e9 h& W
    Out[65]:
    9 V4 }) E; N6 v& x) B0    274.0: q* O/ J% P/ ^, S; S
    1    260.0  U) K1 \) u% S6 T  `) e
    2    322.0
    8 }3 x) J# ~, s4 O% p  y3    248.0
    $ r8 I6 ?" ?+ T: ^3 f4    322.0; p# O0 G) f* v
    Name: Time_Record, dtype: float64
    3 F" o% \' p: L: Z1
    + @6 o* b/ v& r: V. P: x2+ I2 ~, E/ R# j* w, `
    3
    : M* u! [, o9 N0 g! w7 ~! u4* ?& r* {/ I4 s5 ^! h
    51 Z' a8 ^( F2 r5 r3 D2 Z
    6
    2 S$ q  c2 T2 j; p7 G- k7
    8 p, |# z) h* c  B8
    - X$ {. p- T7 A* _% x5 O/ @) q7 q与时间戳序列类似,取整函数也是可以在dt对象上使用的:
    " V$ @$ \6 C& X& E( {4 g% H& V2 t! H4 y
    pd.to_timedelta(df.Time_Record).dt.round('min').head()
    7 L% _) ?( M( q; ?9 mOut[66]: ( H% k- O4 M3 v4 b9 K
    0   0 days 00:05:00
    & k& P/ U6 E4 ]# n1   0 days 00:04:00
    : L6 |- P7 i8 `' }$ X2   0 days 00:05:004 {3 Z4 r* _2 P# D
    3   0 days 00:04:00
    1 X4 V4 \0 L& e* p4   0 days 00:05:007 c$ Y  W0 q4 U% w* O9 t
    Name: Time_Record, dtype: timedelta64[ns]
    - {( b3 x& d' n: _! d1 o6 z: h1
    9 y- d. O( n3 `$ |) x4 v2
    1 F( \7 h! j, @- ~6 V% B- U3% k! c- P( f, |! a7 }( E
    4
    ' r. F1 m5 l; K9 H9 F, ?8 x9 Y' b3 x5% U4 w% u1 ^( T
    6
    - H+ z4 u6 i7 e: Q2 O7% Z' [; H5 P9 P+ I! \' Y5 r6 X
    8
    " v# s) T( ^: f$ h" M10.2.2 Timedelta的运算. |( t& B$ l% ~: X9 h8 ?/ O
    单个时间差的常用运算,有三类:与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算:
    2 A- D$ K* N4 Y7 j5 o, I4 Y% M5 Qtd1 = pd.Timedelta(days=1)4 [0 K+ w- \& J3 F# K
    td2 = pd.Timedelta(days=3)  `  T. w- D1 `) b0 i) P
    ts = pd.Timestamp('20200101')
    ) C9 c& x; A/ _, R$ a# \; P; U( R& E7 M1 f. h* s
    td1 * 26 O, w3 e. u* _% z; q7 X
    Out[70]: Timedelta('2 days 00:00:00'). b4 ]# a$ ^  i9 i
    $ x* `6 N% j! d8 h% E0 e$ ?
    td2 - td1/ a6 v3 U: P& M6 l  ?1 g! ?6 X4 u
    Out[71]: Timedelta('2 days 00:00:00')
    & G& q3 v/ m) c& |  k+ f2 ~% W% }" v1 d5 U8 q$ x8 b' o
    ts + td1" h  i* f. t- z( G. P
    Out[72]: Timestamp('2020-01-02 00:00:00'). `! o3 a: `5 x# K" M; f4 L) j

    $ z& ~( R$ |/ L1 e* Q. f3 dts - td1
    ) ^# l+ D2 _' iOut[73]: Timestamp('2019-12-31 00:00:00')' {$ o( a, Y2 e' _5 S2 V
    1# b- p4 ~' W7 {" c( D6 _& |0 `) p
    2
    / z' t1 A3 ?# ^& C" n3
    # _$ R; Y6 l, s( v) }1 [40 D7 h2 H4 {8 |, B& u# q* d
    5! p0 C- u' b8 k( m4 g' o" U8 H
    62 p# j  H4 B" R' \$ Q. r
    7
    : u' l" B/ e* n) M8
    ; w) q! N0 n- ^/ m" K4 L9 b' V90 l/ F. A  ^' w6 ~/ a5 Z9 |( l4 |( E
    10
    # _% m) |: B* a/ n# v11
    ) S; o8 \3 ?9 S; ]. F12
    " s8 ~9 u  V) [- X: Y( d: ^13
    7 d% H, C4 }" @( h14" x: A! B. f% u8 M# V( i* e
    15
    0 W  M+ ^5 J) i# ^0 W' b时间差的序列的运算,和上面方法相同:1 m9 p) g1 H* H4 F! o0 l8 Q* t+ F0 U
    td1 = pd.timedelta_range(start='1 days', periods=5). ~6 d% i2 s# n" V
    td2 = pd.timedelta_range(start='12 hours',% B# e: m8 n) i: `* _% _
                             freq='2H',6 y4 V8 p) t9 a" Q* Q- g! s+ |
                             periods=5)# q2 D4 q8 p2 s4 L6 d
    ts = pd.date_range('20200101', '20200105')
    + S5 y9 K0 Z* qtd1,td2,ts
    ; {: p! P$ ?& R& z0 Z, T, |2 `/ S7 @" w4 ~; ?
    TimedeltaIndex(['1 days', '2 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq='D')
    0 l9 j( |9 ?% b' c+ m4 f% @5 ITimedeltaIndex(['0 days 12:00:00', '0 days 14:00:00', '0 days 16:00:00',1 _7 z/ p- f2 r5 t  Z9 d) y' h
                    '0 days 18:00:00', '0 days 20:00:00'], dtype='timedelta64[ns]', freq='2H'): p' s9 o3 e; l# w5 G( G( w4 O
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',: i/ U# X) W8 o. q$ F9 |9 \
                   '2020-01-05'],. k% M$ L% {" T
                  dtype='datetime64[ns]', freq='D')- [  W# D5 k$ K/ `
    1' a( c7 N5 `8 S' @" Z; D
    2
    $ W/ I8 B- C1 |- H, G33 Z7 z. u/ }) y5 j' R
    4/ g# P3 u4 D* N
    5, c) y2 D2 L8 A9 z: x
    6; p7 O, h" U& A
    7
    ( v* z1 G6 ~; t, \. P8
    7 q$ ~! R3 U8 H: E& R; V9
    / w) Y) I1 Z% N10! A) T7 Q! w. Y5 ^
    11
    , l# F% J% D0 a12
    2 |1 q  \  i0 n  {* [+ d/ s0 W, e13' ~. c7 w" ], m
    td1 * 5  d- g" \; q0 L. v1 o, ?
    Out[77]: TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D')0 q3 G! j' H1 H) O, b, Z+ P/ Q' B* r: I
    ) f" D* j8 ~' {
    td1 * pd.Series(list(range(5))) # 逐个相乘( s4 a! C% ^5 x, a) O
    Out[78]:
      [7 |- T& ]8 v* e& x! u9 ?3 B0    0 days
    / h3 B9 T8 V8 ]& h* y1    2 days
    " N! C, A/ d3 X/ h( Q$ }2    6 days) m% C% v$ ~- F4 O+ G
    3   12 days0 b& r5 Y9 @/ e7 m
    4   20 days
    7 }( w" {6 b2 A/ ndtype: timedelta64[ns]
    1 T# Y  H8 h" w5 M
    0 W5 B* d0 k4 v# x9 o: Otd1 - td21 @; q2 w3 K9 `
    Out[79]:
    : K* b: r1 W( A. HTimedeltaIndex(['0 days 12:00:00', '1 days 10:00:00', '2 days 08:00:00',! n8 O  a* ?  R
                    '3 days 06:00:00', '4 days 04:00:00'],( G  T$ h4 _3 |' i& S8 k
                   dtype='timedelta64[ns]', freq=None)
    $ Y; g& G9 x5 k5 ^' ~" E9 P% h( q: d- Q) _, ]2 z) S) }! Y
    td1 + pd.Timestamp('20200101')
    ( T! d  s: a" s& C$ t# eOut[80]: : K7 _# Z# `$ K# [* r6 M
    DatetimeIndex(['2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05',4 G+ w9 o0 `* k
                   '2020-01-06'],dtype='datetime64[ns]', freq='D')! u  l0 ~% S/ u  ~9 J& e0 A

    ) t) A# P' N& O$ a6 mtd1 + ts # 逐个相加+ \* \# j( w# s- K/ j2 {. C$ E
    Out[81]: + G) e/ [6 y" q; X/ G- B9 m0 X
    DatetimeIndex(['2020-01-02', '2020-01-04', '2020-01-06', '2020-01-08',
    " s4 J' v3 E- @, t+ k( _7 \3 u6 t               '2020-01-10'],' v' P7 r2 h0 c% Y3 F, I8 e; v
                  dtype='datetime64[ns]', freq=None)3 R0 m: K! Q& S! g, T' e
    8 @( t: o/ b2 s9 C6 r; q
    1
    # N: r2 P9 x% o/ F& p5 \2
    $ g! `$ E$ l+ T& A6 p3- R' A: M- \1 Q  V. j/ O1 @
    48 O8 Q; g7 Q& j& ?! R
    5
    # U- t% |+ l' v9 q  \) E6' ^* U/ N2 K' ]* {
    7" `# z* b# c: T' p- z' D# l6 C7 R
    8- z3 T# _" T, t. H' e8 P) b5 s
    95 f, C, l& S8 B: n
    10- I% |. C7 V, ?( e1 ~  l9 e0 g; w
    11
    . I! f! E" T' r5 S+ M, b6 G& `123 H7 A% ]  [; u; S3 m
    137 T( p; ~9 H7 Y6 I, X% C
    14, J8 e8 H8 S5 ~  L( }- u9 x
    15
    $ B9 f# ]/ ^- F+ ~. k* l8 c16
    # b0 V5 w. i2 f17
    ( S' L; y! l& _$ b4 }6 L" @" n18
    3 s8 B+ o. c" y! Y7 t, D/ J) Q193 z" g) x5 E' U" `9 V
    209 x- Q4 L0 E( V
    212 g# _) @) F- A0 ~- p5 D8 `
    22
    4 N& l' M* F& e( R) V: O$ w+ |23) s% i8 c( n4 Y. v6 b
    24
    7 R2 L0 o7 t( i0 m. f0 s254 x% C( R9 G2 F
    26
    ! Z; ~8 {! k1 _' R1 v/ ~27( A1 p& |9 N3 s% J6 R  e; t
    28
    , K: |1 r% I7 f6 k8 T10.4 日期偏置
    / V; m7 z! g5 \/ V4 @10.4.1 Offset对象
    0 l  s# @8 X5 V1 g8 @  日期偏置是一种和日历相关的特殊时间差,例如回到第一节中的两个问题:如何求2020年9月第一个周一的日期,以及如何求2020年9月7日后的第30个工作日是哪一天。/ c. c7 [2 p4 r, N) ^3 H' Z
    6 I7 l6 P% Z) f9 K$ k4 E5 B
    DateOffset 类有10个属性,假设s=pd.offsets.WeekOfMonth(week=0,weekday=0),则:
    * D8 \) h% F2 c9 m2 k: L0 o1 B. c8 l5 T& D3 J& i1 I/ a) |
    s.base:<WeekOfMonth: week=0, weekday=0>,返回 n=1 且所有其他属性一样的副本; b$ w( S* j/ q! e. N" E
    s.kwds:{‘week’: 0, ‘weekday’: 0}) j& J; c) h  [% B/ ]; _
    s.wek/s.weekday:顾名思义
    ( t4 V+ ?7 q2 \, c- F有14个方法,包括:1 j( J% L1 A( P% p! s+ K, A# x1 f
    ' o& M# u& f: O% D2 o
    DateOffset.is_month_start、DateOffset.is_month_end、DateOffset.is_quarter_start、DateOffset.is_quarter_end、DateOffset.is_year_start、DateOffset.is_year_end等等。/ F0 H3 z5 k& i/ N" O9 X
    pandas.tseries.offsets.WeekOfMonth(week,weekday):描述每月的日期,例如“每月第二周的星期二”。
    ) H. R4 Y  J- [2 P* ?4 u/ d( I" m- ?! h+ s& t, |( I
    有两个参数:/ f0 N) O1 N' q) O) f0 F* |- _$ H
    week:整型,表示一个月的第几周。例如 0 是一个月的第 1 周,1 是第 2 周,以此类推。
    & u( e# F" [+ m5 Eweekday:整型,取值为[0,1,…6],表示周一到周日,默认取值为0(星期一)
    . G2 }7 r  k& p6 cpandas.tseries.offsets.BusinessDay(n):相当于pd.offsets.BDay(n),DateOffset 子类,表示可能的 n 个工作日。% e8 \( ~$ h! R" {1 @) {  c' T
    ' L$ H- h1 @5 u$ a/ v
    pd.Timestamp('20200831') + pd.offsets.WeekOfMonth(week=0,weekday=0)5 K6 B3 p5 `$ V# Y9 w% T4 j2 y
    Out[82]: Timestamp('2020-09-07 00:00:00')
    / d0 H7 G" l7 @9 A, j5 d* H. \3 n5 b/ Z& B$ \) m5 x+ f
    pd.Timestamp('20200907') + pd.offsets.BDay(30)2 o8 n: g+ Z/ \
    Out[83]: Timestamp('2020-10-19 00:00:00')
    5 P+ U' g5 V- ^7 _# L* q0 h9 s1  h+ Q( Y, k: H# \
    2
    ; u4 F, ^; X4 G: p2 A8 V' I3  v7 B& @: [9 y, v# }: R" r7 Z" i
    4
    . e5 Q. v0 o% U$ I5' a$ |9 t" o! \' E; U2 s% o2 G
      从上面的例子中可以看到,Offset对象在pd.offsets中被定义。当使用+时获取离其最近的下一个日期,当使用-时获取离其最近的上一个日期:
    . \3 H5 O% g8 Z" ?8 y0 L- c- r& j1 }- b: T/ D
    pd.Timestamp('20200831') - pd.offsets.WeekOfMonth(week=0,weekday=0)/ \7 a, f/ V4 Q. @# D$ `) M3 i3 P7 R2 M
    Out[84]: Timestamp('2020-08-03 00:00:00')
    8 g1 K; I- I( n; X7 y  {0 B1 c
    & ?5 _! b7 p: xpd.Timestamp('20200907') - pd.offsets.BDay(30)
    % d; W3 T' P; X  ]% D5 G/ {# k; E8 XOut[85]: Timestamp('2020-07-27 00:00:00')
      b3 r) S! Q( D4 U9 F- B$ \6 _, S
    pd.Timestamp('20200907') + pd.offsets.MonthEnd()
    2 D/ ]1 Z' w$ C: f; bOut[86]: Timestamp('2020-09-30 00:00:00')6 G% u; x3 S- \& _
    1# B0 @8 ]" K7 u9 W& A- C
    2/ L( g( Q6 x0 _7 L' V7 q8 E/ A8 O2 z
    3( k. Q0 z" e; {7 j( C
    4) y- v; s1 c! f( v: H8 [
    5
    % E' d6 ^, j; I65 q' A1 i7 Q$ F4 R
    7% N( R$ L: Z* d  B- F3 ~3 ~
    8
    7 g7 c; c, W9 G' g- T  常用的日期偏置如下可以查阅这里的DateOffset 文档描述。在文档罗列的Offset中,需要介绍一个特殊的Offset对象CDay。CDay 或 CustomBusinessDay 类提供了一个参数化的 BusinessDay 类,可用于创建自定义的工作日日历,该日历说明当地假期和当地周末惯例。
    2 H& Y2 d% G, r8 r8 R  其中的holidays, weekmask参数能够分别对自定义的日期和星期进行过滤,前者传入了需要过滤的日期列表,后者传入的是三个字母的星期缩写构成的星期字符串,其作用是只保留字符串中出现的星期:+ N& l* ~  @# |: K3 d
    ( C' p. x) H# P; S/ ~) A
    my_filter = pd.offsets.CDay(n=1,weekmask='Wed Fri',holidays=['20200109'])
    ; T9 N7 V, Q7 w2 F0 c. vdr = pd.date_range('20200108', '20200111')1 e4 a/ T. x( g( m
    2 L1 x$ P9 F& b& s- m9 O# V2 O* s
    dr.to_series().dt.dayofweek5 A8 F# C- U+ E. P" H
    Out[89]:
    & _  B4 I* A5 w; z, \2020-01-08    2
    * c5 ~& k4 j; n3 Z2020-01-09    3: I2 ]& B6 {. E: w& t0 F; x4 u
    2020-01-10    4' x5 ]/ F; Q2 r( r2 R5 Z9 p) Q
    2020-01-11    5
    + g* x/ R' @- a" W6 lFreq: D, dtype: int64: |& L$ r& h2 u: r
    9 e( b# g. p8 _1 ]5 ^( F4 c
    [i + my_filter for i in dr]. B2 Y& P9 }2 I) V" n+ |* t7 |8 W+ X
    Out[90]: ; _/ c# F9 b6 n
    [Timestamp('2020-01-10 00:00:00'),
    # \+ G2 Z5 _4 _9 U Timestamp('2020-01-10 00:00:00'),
    7 z$ C( r8 O: s8 i  O Timestamp('2020-01-15 00:00:00'),5 K& K1 [8 i6 V
    Timestamp('2020-01-15 00:00:00')]: v' D1 y1 A, d8 P8 u

    , V& @4 S6 V  l3 a6 ~. D1& ^# g, w8 C/ z$ r
    2
    % {6 Q- g% M: A9 r+ O/ B) c' O3. Q/ M& N( n5 h; f
    4( x* P' A+ o, L. }' h/ V* f+ c( Z: t
    5
    - S2 G" l! v. A# A0 i& t9 p1 z6
    % _6 c$ ?: Z9 m: i/ a. s) h7
    3 H, H0 t7 q4 k& ^8
    7 v, g: E4 p: c4 G9
    " W6 E' r) U# H; ~' b; y# N6 t10+ L  g: j) c5 Q) X  s
    11
    ; C6 T7 L& R9 c12; F7 d1 D8 C! l! ^; Z" Y
    13
    & C: C$ s3 d; u5 x0 M) J% L) W% F# X14
    0 q( W* g  K  f& ^15
    ' p' |* f1 _5 l$ [5 S16
    % [) J& O6 c) V1 l( f0 p$ T17
    " h+ g: H$ o& l  上面的例子中,n表示增加一天CDay,dr中的第一天为20200108,但由于下一天20200109被排除了,并且20200110是合法的周五,因此转为20200110,其他后面的日期处理类似。
    ! x- {8 I1 u- y% K9 G4 H# u* G) U' h  n" G, T6 M, W( B
    【CAUTION】不要使用部分Offset* d( X* @8 P1 o" |- P
    在当前版本下由于一些 bug ,不要使用 Day 级别以下的 Offset 对象,比如 Hour, Second 等,请使用对应的 Timedelta 对象来代替。
    " R+ {: t, J( Y& T: o) o. t" E. o4 U4 ^# ~4 i
    10.4.2 偏置字符串/ @8 z# z8 P* {
      前面提到了关于date_range的freq取值可用Offset对象,同时在pandas中几乎每一个Offset对象绑定了日期偏置字符串(frequencies strings/offset aliases),可以指定Offset对应的字符串来替代使用。下面举一些常见的例子。; _: e& a3 I1 e  M/ e( u
    2 X) R/ Y2 r( ^6 g4 ]! r. o2 Z
      Offset aliases:pd.date_range函数中的freq参数,为常见时间序列频率提供了许多字符串别名。 也称为偏移别名Offset aliases。偏移别名列表点此参看(大概27个)。
    , L* q; U0 w$ M% ^" W2 C! L
      ^% K! c6 k9 B' W4 {pd.date_range('20200101','20200331', freq='MS') # 月初
    # }: V: c. j% r9 r( V6 j9 wOut[91]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')
    / U  F/ N  u* D6 S0 z
    , T' S! d: Q9 @) h& w5 i9 ]& ?- Hpd.date_range('20200101','20200331', freq='M') # 月末; `/ t  N7 o. T# S& X
    Out[92]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')9 E8 Y% t! a( b2 Z( f4 B
    9 [% {  h2 }# f" i! }
    pd.date_range('20200101','20200110', freq='B') # 工作日
    , z: F  p# v. V  D1 yOut[93]: - R$ {* J2 ^: Q
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',# t& `4 l( B/ A, b( T
                   '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],) f4 R8 e# c: i: o
                  dtype='datetime64[ns]', freq='B')& I. n; \0 M7 l- W$ f/ r

    : P5 g4 P2 V* Ipd.date_range('20200101','20200201', freq='W-MON') # 周一
    5 H9 U. z, C: r6 |Out[94]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='W-MON')
    ! J/ v0 u) D8 Q- U5 B, j
    ! @7 g6 O, }, P9 Q# h2 q& Tpd.date_range('20200101','20200201',
    7 y; }% V' F5 a2 z8 E              freq='WOM-1MON') # 每月第一个周一0 u! M; b$ Z- S6 [  J

    6 a! Z% G  O: P1 S* u- d$ LOut[95]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')
    0 \" H# Y9 ]! M7 x' ^, `
    / r3 d. N' }8 A5 {. v1
    & G2 e, Z+ r& o3 K  C2
    7 g( I* w; v6 Q8 E2 F. z38 ?; Q! u7 h; z
    4
    ; s! U1 `8 T  p1 @+ b* s5
    * A& W. G. M- l( _62 |. V6 @& E( d: U/ ^" f( P
    7
    & F, X4 w& [- ^5 ]' Z" C8
    5 }5 \1 U8 P3 h. X1 J! H, Y92 P: c9 W' E. q. h
    10
    - K2 ~" \& ?( ^$ v0 H  B) z. W11
    0 R- q, j0 ~* e: L) ~! W& F; r12
      K3 R- D$ e2 d3 ?( l13
    % q( ?1 N" O2 n8 f# P14
    / l! `9 c# J; |7 W! i! E15
    8 N& a, L- v% S) H16: I! Z0 a5 @5 Z* A' i3 r: c4 }
    17
    % n) `, t+ S- }/ }7 B1 S  d18) O. o# |. I& M5 ~1 S: b
    19
    / E: ~  ]" s: g7 W: Z上面的这些字符串,等价于使用如下的 Offset 对象:" z, E( J. X) w! e. a

    " V+ y: P# q' j0 H" Ipd.date_range('20200101','20200331',3 p" T0 m* X- i* p2 g5 a
                  freq=pd.offsets.MonthBegin())2 E& `0 y8 |' _# _- r7 e: A

    3 T8 n# F7 |8 C! zOut[96]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')
    8 n: ]# i, o" R0 o! B$ h6 P% U0 |- A1 [
    4 ^& }- X2 M+ a: `9 W( K6 cpd.date_range('20200101','20200331'," c8 f' e1 j- @6 u
                  freq=pd.offsets.MonthEnd())
    $ R; w7 t+ x( x% \+ N3 {$ f! p
    $ O( A( |% `' {" X; lOut[97]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')8 s0 S. g3 Y+ V* N

    0 R9 ~% y% e- B* R2 npd.date_range('20200101','20200110', freq=pd.offsets.BDay())
    - y, W7 Q8 y' o/ F: i! UOut[98]: 6 E' G/ R( Y* X$ Z" r, w5 M
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',3 U( D4 X* H7 l5 X5 B# {
                   '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],7 P3 z' ^% L$ [" y
                  dtype='datetime64[ns]', freq='B')
    & p& ?4 W$ ~9 F  ~; l& R: W
    3 S+ Q4 y+ p0 c: d, R0 ~6 J6 w) F' Fpd.date_range('20200101','20200201'," u" ^5 b- h2 z/ \
                  freq=pd.offsets.CDay(weekmask='Mon'))
    . p0 g# m, B) b9 P: S  x( I
    % j& |/ C  T3 ^- j0 D2 K5 n/ }Out[99]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='C')
    6 G% G0 a5 L! N' \9 S2 Q: n
    2 ^) U: k' k2 P2 r/ Gpd.date_range('20200101','20200201',7 F/ A+ [! L- C: S
                  freq=pd.offsets.WeekOfMonth(week=0,weekday=0))7 n9 c+ [, `% S7 q# j# W$ ]7 U+ L, m

    ) M3 D2 \" b7 B: n' [Out[100]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')% V! R6 j# P2 y5 {* `* k1 J

    0 J9 G: d/ x  h1
    7 ~! u( O1 v) r! R. _7 x3 y; d( L2( W' |3 ?8 S: l
    3
    , ?. S/ e/ @' E& u; @# E4$ f0 F$ u+ n: F- L4 m- ~
    5: i  f& Y& S2 Q1 N% l/ t9 d
    6
    5 t$ I+ P& ?8 c+ h; k8 @7" x. c" K, P( Z0 _& I
    81 X# n9 Y& s/ y4 [
    9
    ' j4 L$ e# X# k% I( P8 Z10
    5 j0 D0 |4 U! F/ N11. _9 g' Q, a4 }, j+ x3 [; \* \
    12/ C9 N( \, c  s* }0 w
    13* n7 w4 d0 V1 P5 H- U' p
    14; q4 Q3 h7 [7 A; x
    15
    2 k) C! i4 Z+ F- S$ o- |16: j# m5 g# m9 e* [% V
    17
    # N$ T# r: L9 a- {) J2 T18* w9 Y) w* h# _# x9 }: ~1 I" x
    19* m/ l* D; L% R3 ~3 \2 P: J
    206 }1 h4 E" d/ W- G
    21
    6 O  k! y; v* R- e) f& a. L: K. ~229 Y" R& s3 n# {9 H# v
    23
    & Z/ Z$ v* W: T+ }9 [24
    3 y; z5 N6 I+ a% m2 x2 c25
    6 a  b6 m" f0 |3 H【CAUTION】关于时区问题的说明
    : E  ?# J) z: z( x  各类时间对象的开发,除了使用python内置的datetime模块,pandas还利用了dateutil模块,很大一部分是为了处理时区问题。总所周知,我国是没有夏令时调整时间一说的,但有些国家会有这种做法,导致了相对而言一天里可能会有23/24/25个小时,也就是relativedelta,这使得Offset对象和Timedelta对象有了对同一问题处理产生不同结果的现象,其中的规则也较为复杂,官方文档的写法存在部分描述错误,并且难以对描述做出统一修正,因为牵涉到了Offset相关的很多组件。因此,本教程完全不考虑时区处理,如果对时区处理的时间偏置有兴趣了解讨论,可以联系我或者参见这里的讨论。
    % g+ G4 p, b& `/ c  e8 `, w. u. d9 ~
    10.5、时序中的滑窗与分组$ l" A: L" X' R7 m( i9 X
    10.5.1 滑动窗口' Q8 @& |( s$ ~9 n# b8 L+ I
      所谓时序的滑窗函数,即把滑动窗口windows用freq关键词代替,下面给出一个具体的应用案例:在股票市场中有一个指标为BOLL指标,它由中轨线、上轨线、下轨线这三根线构成,具体的计算方法分别是N日均值线、N日均值加两倍N日标准差线、N日均值减两倍N日标准差线。利用rolling对象计算N=30的BOLL指标可以如下写出:
    * o2 J# n" s+ G# y4 S- b6 u7 Z$ E+ v7 p" t
    import matplotlib.pyplot as plt9 J3 {& \$ o# M8 `& i
    idx = pd.date_range('20200101', '20201231', freq='B'); F$ H' {, x# x
    np.random.seed(2020)8 o% N! A% h; ]4 v* i
    1 v9 |- P+ _5 r' Z5 L
    data = np.random.randint(-1,2,len(idx)).cumsum() # 随机游动构造模拟序列,cumsum表示累加  k; n, s% A6 T& ?% D5 a& q( ?
    s = pd.Series(data,index=idx)
    1 n* [1 }! `6 J0 ~; Ts.head()
    3 e" W( Q8 ?& |  dOut[106]: 1 w; p; ?* F& x9 V% e7 v
    2020-01-01   -16 p6 R' I/ B3 H0 ]4 `
    2020-01-02   -2
    6 X: ]% q+ }& W8 W6 `6 E) k1 ^2020-01-03   -1
    7 W( u, W3 G5 G' \. ]& Q2 W. l2020-01-06   -1( j2 X# O  I& t7 U3 M9 q
    2020-01-07   -2/ a; E5 s4 q' c
    Freq: B, dtype: int328 m% }# w- E2 C: `( _) K+ u
    r = s.rolling('30D')# rolling可以指定freq或者offset对象9 N. o% \1 ~$ v2 d# x8 u2 i( f

    ' f& K/ T7 k$ X1 I4 ^plt.plot(s) # 蓝色线
    6 ~5 x; i8 M; LOut[108]: [<matplotlib.lines.Line2D at 0x2116d887eb0>]  V7 I* [$ ]3 _5 n1 c" O
    plt.title('BOLL LINES')/ |* |4 y- l: G$ ~$ O* S; X2 I
    Out[109]: Text(0.5, 1.0, 'BOLL LINES')
    # @# q- v! i" f; G$ B+ P5 Z' q/ Q( I  ?" L
    plt.plot(r.mean()) #橙色线( P# C0 I& ]" e2 A! x8 |; a2 a
    Out[110]: [<matplotlib.lines.Line2D at 0x2116d8eeb80>]
    # B$ B7 M! u4 H9 Z
    0 h3 u* G" X5 H) Q9 U3 ?plt.plot(r.mean()+r.std()*2) # 绿色线
    3 n/ s( S( S# C* ?! v! H; oOut[111]: [<matplotlib.lines.Line2D at 0x2116d87efa0>]) ~. U( g  v  T' q
    . p0 o1 h( `0 @2 u' i) k- V
    plt.plot(r.mean()-r.std()*2) # 红色线
    $ I9 G5 C6 {0 d. J5 c- f# wOut[112]: [<matplotlib.lines.Line2D at 0x2116d90d2e0>]
    ! _. I% M# ?3 Z" [4 t2 z
    * J  H( ~+ o8 r. K2 s: J1
    2 D* [2 V! O# m2 }7 D2
    % K. Q  z9 `* y' h, v3
    4 T2 k& I- ~) ?4
    5 K6 Z+ N2 E6 H( j+ B# ~" ^( i5
    8 L5 J, T/ K; E6
    0 w* Z  U3 d+ Y% J7 Y% s: c* B9 B7* z7 b: J4 P* @) g$ B" f  [
    8  d, I+ j6 @3 q9 d
    9' A3 {, E: x# g7 w# Y. _! `
    10
    9 T8 q9 q. N. b  l  \' ]! @8 x0 H11
    7 ~4 }7 b% ~9 }12
    5 N" m/ Q$ M" ~13+ e% u& e2 k% c
    14
    3 N. O8 S) v" Z& S) A15
    : w. G1 Y9 J. {) _, g" w16
    5 R) o( T# e+ ^5 L; u* g( M% p! b& s17
    ' u; j' M6 U8 `3 \182 d' P; p- n) i! ]2 v
    19. e( H7 C0 Q6 k& N: s" b4 g5 V
    20
    - k/ E- B" Q1 o' N21* a; Z4 d$ y* T& C2 X9 V$ `
    22
    ) d  ?* m4 v/ z' W) _" q: A23
    - [- M& G4 f+ w( m4 Y: o$ Y24
    & V! c% Y6 G, `# ~, U% {! a25+ v8 L5 ^) i* E
    26
    5 l% q" s% Q* l/ v/ ]27
    1 Y& F4 D9 v2 K( b2 r1 ?+ @2 T$ E8 S* p28
    ! w; x) l% N" \$ e) }$ C29: \6 W( C  F$ i9 X( v$ c# r# }! {
    / N& d$ o* T+ P% f5 g
       这里需要注意的是,pandas没有实现非固定采样频率的时间序列滑窗,及此时无法通过传入freq字段来得到滑窗结果。例如统计近7个工作日的交易总额。此时可以通过传入多个函数的组合来实现此功能。
    5 ?- X  Q5 N5 Z; }   首先选出所有工作日,接着用普通滑窗进行7日滑窗加和,最后用reindex()恢复索引,对于双休日使用前一个工作日的结果进行填充。
    5 n& P9 S, u6 y/ C' a2 z, G. Z/ b  D6 C" z/ E! v3 l
    select_bday=s[~s.index.to_series().dt.dayofweek.isin([5,6])]! d# r# b& C4 a" f3 y) X2 z
    bday_sum=select_bday.rolling(7,min_periods=1).sum()9 B1 V" k* P# Z- ]
    result=bday_sum.reindex().ffill()
    / g( l$ D( S2 s! @2 y* J' F5 Nresult8 o8 e% U; s' }" R; Y# ?
    & v) q8 \3 A. j0 X+ Z
    2020-01-01     -1.0
    8 y4 ~% ~2 I! D, n; r# x5 l4 d. X2020-01-02     -3.0" r2 M- }6 W. v2 S7 M: v) Y: o
    2020-01-03     -4.0
    3 t! b9 _8 [7 c0 |" _2020-01-06     -5.0
    ' q) a. d! g: U/ ]5 d' V" k2020-01-07     -7.0
    7 s/ h4 G$ X6 V% l/ L$ g              ...  8 l9 v$ i1 e; _  q+ Y
    2020-12-25    136.0
    5 l7 G6 K) i6 ?2020-12-28    133.09 U: ^7 h/ c* A7 E
    2020-12-29    131.04 ^. ^4 S: [. y+ i, b' N
    2020-12-30    130.0
    " n' K4 L) ]! ?2 X% F2020-12-31    128.05 N) A( I" J6 w- M
    Freq: B, Length: 262, dtype: float64- u1 D8 D4 W4 g' ^. U2 I
    / h7 U4 @5 R+ |
    1% U6 `/ \; H) y" R3 E+ r
    2, d4 X) t/ t9 v7 v
    3( }, ~- H3 B0 s+ ~6 J
    48 T! J" p: {2 `4 H9 [
    5
    ( r3 {$ }% y. v. ^6 p6# D: r+ V5 n; f
    7
    $ y1 J: V6 r; {- i0 v. @% y8
    ) a! k7 Q! \$ c9- X, b* q5 P$ u$ S2 q2 t  B8 N
    10
    9 h4 A- M( m  ?* Z& r- c/ a) Q11* L  U2 Z" W/ Q
    12
    4 z5 Z8 i  ^' a! ~7 E- Q; O% Q% U13
    ( n1 x+ n6 ]: I14
    & r' [- b( F8 i$ T. S5 f: E151 t. f4 [: B5 n/ `3 b- X3 ^+ ]9 ]
    16* n3 B) D. ^4 r' j1 `# E2 l
    17$ P4 \% P! z& w1 F6 L
      shift, diff, pct_change 是一组类滑窗函数,它们的公共参数为 periods=n ,默认为1,分别表示取向前第 n 个元素的值、与向前第 n 个元素做差(与 Numpy 中不同,后者表示 n 阶差分)、与向前第 n 个元素相比计算增长率。这里的 n 可以为负,表示反方向的类似操作。+ _$ `6 i; {6 P& w; h% Y

    ' A5 w7 Z. \/ I  对于shift函数而言,作用在datetime64为索引(不是value)的序列上时,可以指定freq单位进行滑动:! E7 }) c" \4 }/ a- J' K+ z( q

    - w, b9 |' r7 m" n/ gs.shift(freq='50D').head()$ N  _2 `2 c! G7 K7 z. {6 v$ @
    Out[113]:
    ' ^1 f$ X# ]' S5 Y4 z& f2020-02-20   -12 h  R+ A6 \: I2 D+ C8 j3 U) G
    2020-02-21   -2; `# O' ]  B: t6 \
    2020-02-22   -1
    0 \$ @; ]7 O0 T5 l% e2020-02-25   -1
    ( S% a/ i) f. c3 g. H/ ?7 x9 k5 [2020-02-26   -2* w" T" }5 w/ b: H  K9 b1 x* r
    dtype: int32
    . s4 ?8 c1 k' Q' I* }# J  U1: o+ Z5 Y, s! l' F
    2
    1 g0 e) X) v9 j7 R* N. K) ]2 P. b7 f3
    9 j3 z  Z/ ?  p9 |/ z1 n) d4
    3 g. I* }( S% }  O5 z% t+ k5
    - \1 ?" C% [* i5 w, B7 F6" C6 T+ a0 w$ |" d' C
    71 ]% e) y  B8 q6 D% O0 ?3 V
    8
    % n% a( S# s( j3 `/ B2 Q  另外,datetime64[ns]的序列进行diff(前后做差)后就能够得到timedelta64[ns]的序列,这能够使用户方便地观察有序时间序列的间隔:, V" Z4 `' F+ Z  q( i- o7 P' x

    ; Y4 K' p( l8 S) V% N% Jmy_series = pd.Series(s.index)
    5 B! ]3 C, K8 dmy_series.head()6 ^+ O7 ?: K/ _6 s7 G* J# w) C
    Out[115]: * p& w4 @2 e0 D6 f# g* Q$ H
    0   2020-01-01+ h5 g1 V: F" k* U5 Z! C- M
    1   2020-01-02
    $ D# }( s. F5 N+ A8 }5 n/ q: [2   2020-01-03
    - ]/ X8 ~& t3 _' J3   2020-01-06# W7 ~. y& k% \, e
    4   2020-01-07: T/ X& _# H( L6 D- z
    dtype: datetime64[ns]
    & `5 m9 }; t, ~' W7 z4 c5 v
    6 i( Z4 v9 n0 gmy_series.diff(1).head()
    9 Q. K% A; I9 D: G1 jOut[116]: : @. u7 N8 }; n* h) }
    0      NaT
    ! q8 z# r! H  T& ^; L, S1   1 days
    ) O% N) S( Y% I% h( t& H% Z2   1 days; R8 Y$ o( D9 A, ?* p
    3   3 days9 }/ j! X. ~) J( p
    4   1 days
    4 Q6 [, T, {$ d* {, V* Cdtype: timedelta64[ns]& R$ ^9 h- n" k
    3 V: K+ v  }# r7 @7 s
    1
    4 ^* X0 l6 e7 ~8 N/ w+ R! _) L  s2
      V8 b; J( z9 f0 z/ g3
    % R" C$ c8 H# ~/ Q2 ]  y4: R& P' h" N# W: [/ }
    5
    / F& p- f& l+ }- |* ~6
    ( \1 }/ D- I& b! B2 b8 L7
    , X6 s  ~% i* T- @  l! ?4 H% i2 k8
    $ J6 g2 t) p2 H1 J+ t9
    : T, \4 l9 q0 Q6 e; N, S3 f$ n10& `% p# o0 V5 W3 a4 d* `, U* A* ^
    111 I4 a  T  m* f* Q9 F
    12( @! M: k, N+ {; m6 ?: J
    13! _# H9 A" \& z6 t! |
    148 p- k5 j: i9 b9 v' ~
    15
    : |; K/ ?, n4 v8 L; T: j16, h% G8 V' X8 p7 J' d
    176 Z3 F+ m1 q+ G, E# y7 Q/ M
    18
      G% ~& @( V+ y8 b- d: v( J- s10.5.2 重采样/ r7 b8 f0 i, o( E' r' G; s
      DataFrame.resample(rule, axis=0, closed=None, label=None, convention=‘start’, kind=None, loffset=None, base=None, on=None, level=None, origin=‘start_day’, offset=None)6 v8 Z# h/ k) T  T/ @4 f4 y
    常用参数有:
    6 B, Z7 u2 M$ s4 m  d7 r' J3 u6 U7 e8 C, U3 Z: w: A( Y
    rule:DateOffset, Timedelta or str类型。表示偏移量字符串或对象% r" [, o, K* @. T# g! I' @
    axis:{0 or ‘index’, 1 or ‘columns’}, default 0。使用哪个轴进行上采样或下采样
    ( N, [0 P9 l8 ]5 N- B4 \5 {closed:{‘right’, ‘left’},默认None。表示bin 区间的哪一侧是闭合的。所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。% q2 `' z, r3 a& ^0 E3 n
    label:{‘right’, ‘left’}, 默认 None。hich bin edge label to label bucket with,所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。, D2 p7 b  }+ R3 o. x+ r1 A2 Z
    convention{:‘start’, ‘end’, ‘s’, ‘e’}, default ‘start’。仅针对 PeriodIndex,控制是使用rule的开始还是结尾。
    5 W! |# r) d0 V5 ]6 U; pon:字符串类型,可选。对于 DataFrame,使用列而不是索引进行重采样。列必须类似于日期时间。
    / y4 ^$ h9 d! {  p+ {& i. `! ]level:str 或 int,可选表示多重索引MultiIndex的级别,这个级别的索引必须类似于日期时间。
    ' y. s& Z/ i' V( S* i8 aorigin参数有5种取值:  \! l- {7 ~" e" H0 ]
    ‘epoch’:从 1970-01-01开始算起
    6 D) H0 s5 n) z‘start’:原点是时间序列的第一个值& f3 ?; s, S- s
    ‘start_day’:默认值,表示原点是时间序列第一天的午夜。% G# c$ L2 h; e6 k5 N/ i1 }
    'end':原点是时间序列的最后一个值(1.3.0版本才有)
    / N2 X5 t" P0 ^0 W% p( H" [‘end_day’:原点是序列最后一天的午夜(1.3.0版本才有)
    ' E# T: n, ]; o9 K, soffset:Timedelta 或 str,默认为 None,表示对时间原点的偏移量,很有用。) m3 l/ _) K1 s' m+ m4 y% F
      closed和计算有关,label和显示有关,closed才有开闭。
    - ~, u: V2 X; Z1 Q$ r2 ~6 p& B" u# E  label指这个区间值算出来了,索引放区间的左端点还是右端点,closed是指算的时候左端点或右端点是不是包含。, D& L' l$ V* U/ W; u5 e
    . M6 z0 b$ q' P3 t
    重采样对象resample和第四章中分组对象groupby的用法类似,resample是针对时间序列的分组计算而设计的分组对象。例如,对上面的序列计算每10天的均值:
    4 a5 F$ [2 O: z4 Y6 f2 G; p' Bs.resample('10D').mean().head()/ K1 S, c) L, a5 h- |+ A
    Out[117]: 5 x0 t* l* r' |$ ^( k
    2020-01-01   -2.000000
    7 l9 P/ {6 Z2 |2020-01-11   -3.1666678 Z. ^" ^# }3 o. a% a  h
    2020-01-21   -3.625000
    ) M) N; a5 d& |* ^) m2020-01-31   -4.000000$ a/ s% w3 T+ {: i& y
    2020-02-10   -0.375000
    8 `# ~& L$ e# f2 pFreq: 10D, dtype: float647 m3 N7 ~0 g4 O& }" z; H' z) G
    1$ y3 t) q7 R1 \" c0 ^$ {
    2/ c  D% }+ I& U: [
    3
    % c% M; K2 O! h. x/ Z8 p* P- l4- G( C/ H8 ?4 n
    5) N8 R; n6 ^# H3 a! Y
    6
    ! A* N4 t, E: \. V2 E7+ o: i: H( ]* [* B" S. h( T
    8
    3 i, Z) e( {; Q1 {9 m2 q可以通过apply方法自定义处理函数:9 S3 ?; c4 Y( }& ^
    s.resample('10D').apply(lambda x:x.max()-x.min()).head() # 极差
    3 t9 s7 [# r' R/ E2 x# N) ?# B
    $ N) r3 D+ D9 X3 j5 t- {Out[118]:
    3 W' W& _6 a. V2020-01-01    3" M/ B: L  A/ d- `- R' H
    2020-01-11    4, \) @4 l, C5 g' T6 Z* K6 g: K& |
    2020-01-21    42 }& m$ z0 W; Z( R
    2020-01-31    2. z" w) x3 A$ ], Z  T7 W2 M, m5 C
    2020-02-10    45 h' G2 G6 X) P0 Y
    Freq: 10D, dtype: int32  B! m; e8 d$ F6 b4 I
    1
    3 B3 r$ [; C+ c2! _- `/ l! d1 P1 \
    3$ L, W5 {; _- D0 Q) }
    42 Z7 t  q# V, e
    5
    8 R5 H( L6 F( L$ [5 A6' b# P: ^% h/ l7 T) ?7 @2 W' E
    7
    # F, M; i3 X( A( I. p8
    8 J' E; y4 E- W. ~' p9
    8 k1 q$ U$ z# y) v  在resample中要特别注意组边界值的处理情况,默认情况下起始值的计算方法是从最小值时间戳对应日期的午夜00:00:00开始增加freq,直到不超过该最小时间戳的最大时间戳,由此对应的时间戳为起始值,然后每次累加freq参数作为分割结点进行分组,区间情况为左闭右开。下面构造一个不均匀的例子:
    7 M3 B. X( q' i1 _: ~0 F  J) N8 |% z1 b% o6 u
    idx = pd.date_range('20200101 8:26:35', '20200101 9:31:58', freq='77s'): J+ W" z+ {& X8 {
    data = np.random.randint(-1,2,len(idx)).cumsum()
    7 K& [: D7 _2 u5 v/ b- `$ `) ps = pd.Series(data,index=idx): L2 _4 M; ?3 o1 n- e8 I1 y
    s.head()
    6 t, x/ G2 ^  W# \- ]
    1 T- \6 h% `+ F4 \0 GOut[122]: 7 C( q' D" w# `" X: O! d6 [, a
    2020-01-01 08:26:35   -1
    5 V* n2 W1 o6 [4 v8 K0 K0 U2020-01-01 08:27:52   -1: Q& _$ O4 T$ m6 b* I
    2020-01-01 08:29:09   -2
    ! ^0 I) S+ R5 }/ [# r. C2020-01-01 08:30:26   -3/ }3 v7 s$ a3 d2 K+ o" }6 C# q
    2020-01-01 08:31:43   -47 v- ]& ~( X$ @8 C1 a
    Freq: 77S, dtype: int32
      ^  W6 M2 e8 n1 r+ z1
    4 C1 |7 U& C+ o+ p! [3 ?2
    7 K7 J" L: y0 G' t" h% ^3: W8 {0 v" b3 a1 K' E8 ~
    4
    # p+ M$ o$ I. Y' e1 l/ x2 J5
    ( `4 ?2 [# Z" ^  |6/ S' q& W2 M+ [) e- G
    7
    3 `  ^; z, a/ w4 e$ i8
    ! k# X4 g4 q* b* A: R# ]6 [91 U1 S2 c9 i% ?0 G( ^4 U
    10
    $ {# U/ Z! h- g11  B6 X) m8 o, v! \5 a
    12
    $ S* g0 \  R! b- X, }  下面对应的第一个组起始值为08:24:00,其是从当天0点增加72个freq=7 min得到的,如果再增加一个freq则超出了序列的最小时间戳08:26:35:
    ; k$ A4 Z$ a6 g8 h5 D" B/ t$ W- q0 _" C6 R8 N# ^/ a
    s.resample('7min').mean().head()
      ~) j/ \# J3 N# l4 gOut[123]: ! l( e6 V+ r. q8 h: D9 a3 ?
    2020-01-01 08:24:00   -1.750000  # 起始值,终点值包含最后一个值
    3 W8 l2 H. V# n# I9 B% {6 n2020-01-01 08:31:00   -2.600000# ]. @" w& w  p5 A
    2020-01-01 08:38:00   -2.166667
    7 h! G4 U; c( }( W' J. m7 @/ d2020-01-01 08:45:00    0.200000! S# m2 M: u7 d* i" F1 o& _
    2020-01-01 08:52:00    2.833333
    , N8 H9 O7 }; s6 I6 f2 mFreq: 7T, dtype: float64, X' w  k3 l1 t6 e/ Z& j% @% L
    1' t8 Y8 i4 Q( E/ g8 ~, D3 O+ G) F
    22 }9 \3 h/ ^+ I) c
    3* Y* X8 A$ d+ ?8 J' r; j
    4' s! }- N) a& W' x7 a$ _! P
    5: s$ X; Q0 x* q4 d+ w
    6. T" K4 \2 ~; P
    7
    . U' J' }7 U) f- `* {6 @8: T0 a& d3 T  t/ m$ d/ f5 E
      有时候,用户希望从序列的最小时间戳开始依次增加freq进行分组,此时可以指定origin参数为start:/ K( a6 n+ |. ^5 h8 u# m" Z/ Q/ X

    1 W! `/ }5 Z7 Y# P$ Q5 R$ H9 ts.resample('7min', origin='start').mean().head()
    - W& q1 d+ {5 b# \+ A* E+ {Out[124]:
    " N: r+ v4 k2 @6 u$ W" _8 T7 M; I; c6 L2020-01-01 08:26:35   -2.333333
    ; R; w, X. J( e# o' z2 U2020-01-01 08:33:35   -2.4000005 g- t2 I" R/ c% w! |
    2020-01-01 08:40:35   -1.333333
    " F7 a, |- s( d  }2020-01-01 08:47:35    1.200000
    8 X7 ~# A! {+ i, H5 P: n2020-01-01 08:54:35    3.1666676 o: j4 g/ Y- I1 ?" i# ]- j& e
    Freq: 7T, dtype: float642 o) T; `* y6 e0 o6 w
    10 F% R0 D9 K/ p9 o7 d! G# K7 a
    29 G/ f& d) M& L1 `0 [4 f
    3
    9 `% D* s0 D) [. F( f& _45 I9 D( X. ]/ Z3 P8 k& e
    5
    - r1 C4 U% v: L/ E6+ H& f4 l2 ?2 e
    7
    ) s+ C0 U+ J/ d* Z8
    6 M, H6 A$ t# ^! `  在返回值中,要注意索引一般是取组的第一个时间戳,但M, A, Q, BM, BA, BQ, W这七个是取对应区间的最后一个时间戳。如果想要得到正常索引,用’MS’就行。
    5 m" u6 T; E9 F& P6 A) v* Z! e. ^1 W4 d* ^. X. B& S0 V, g# n
    s = pd.Series(np.random.randint(2,size=366),
    * x& x2 R) G( S              index=pd.date_range('2020-01-01',
    & ~+ `8 a6 J( Y' i: J- c2 O                                  '2020-12-31'))
    : x' u. _4 A: D8 b+ H/ n% Y0 y7 S) _9 ^+ G" {' k8 e( o9 g  A9 [
      w% |2 p, z& Q; _
    s.resample('M').mean().head()  y  m6 d% \- f% F' t% B
    Out[126]: ( r* f8 q4 f6 ~5 E5 g
    2020-01-31    0.451613( I6 \1 f5 F' R3 L$ Q! Q" w. a7 e
    2020-02-29    0.448276
    1 K3 t& I+ J; v/ `* \* L  z, b2020-03-31    0.5161293 d9 E9 ^9 Z& b4 C* P  r
    2020-04-30    0.566667& X9 V; w/ E" D: }" q" z8 x4 [
    2020-05-31    0.451613) C7 V& ?$ e/ O( ]: l5 @
    Freq: M, dtype: float64( }. c  J- g' s  g! Z; a1 O/ I

      e- l# g+ _0 G& Q- D5 Zs.resample('MS').mean().head() # 结果一样,但索引是跟正常一样! c/ @) y9 G* V+ g! y9 N
    Out[127]:
    $ k, p7 u% q! g9 H5 N. D3 ~. E2 t/ V# E2020-01-01    0.451613* U/ l7 F( P; Z9 G& w
    2020-02-01    0.448276* M/ P  o7 m' [) N
    2020-03-01    0.516129
    ' e+ n1 r0 |# W) O& t  a2020-04-01    0.5666672 s4 J. K- T( m- a
    2020-05-01    0.451613* G5 J  F0 t5 W
    Freq: MS, dtype: float64; S9 G) y" r9 X/ r

    0 L& _4 a2 p9 u& H' I0 r) i8 \1
    " g  c3 y! h" a/ h4 `2
    ! e2 s! c/ n' _! C& Y1 t3
    9 Q: z9 G/ j" r8 d8 K2 Y4
    6 P: D% ~. Q/ w5
    ' X# M: x3 l" \$ V* O! {6
    ( |& y: f1 u# ~/ z7
    ! e5 P! q- m7 B5 H' ^8
    ; h+ j/ c( _+ B, v* P$ d. l9
    ) k& G  M; q, p0 i/ R; S2 ~10& ?6 P2 _' B: ^+ C" O
    11
    ! v  h" [) W6 {1 T! g6 O( p! n12: c1 |0 a; }' R; M& T
    139 S* U& s4 V2 b3 P* w1 M1 I& c
    14
    8 b5 N8 [, t+ t  J15
    # o7 y0 F8 C: k; P+ p. x16
    ; G! c3 m9 U" M9 K- l17( W0 _3 N, Q+ p) a5 U3 p
    18: n+ w) G) T; d% T& u
    19( I' U0 L8 r1 m
    20$ b; M* z4 U: _8 X6 m/ {% `
    21' R* ^# y* v7 `* e  n: d( C
    22! J% H+ r5 a. M  k0 \
    对于 DataFrame 对象,关键字 on 可用于指定列而不是索引以进行重采样:
    : P0 ^* S  `) g% Z2 ~) X; J8 }d = {'price': [10, 11, 9, 13, 14, 18, 17, 19],6 Q0 n) g$ ^) u# h
         'volume': [50, 60, 40, 100, 50, 100, 40, 50]}
    ) ^  G+ o6 M+ T$ W, F$ j7 Hdf = pd.DataFrame(d)
    + x+ f9 z+ K) f& hdf['week_starting'] = pd.date_range('01/01/2018',
    / O. c; v  y/ e( R                                    periods=8,
    9 v1 k! k) N- g! z, F4 z                                    freq='W')
    : T- D+ L+ j7 c5 b( kdf
    - ^4 {) e' v# q/ y5 R   price  volume week_starting3 ?' {4 R$ l& J) l3 n
    0     10      50    2018-01-07
    & P. K. y5 `7 }* r/ C1     11      60    2018-01-14% R2 v$ K9 V; q6 P, W+ G7 \
    2      9      40    2018-01-21! Q8 w9 S+ d: I, k: a, x5 K; x
    3     13     100    2018-01-28
    + Q' y7 h0 W( W, [" @4     14      50    2018-02-04
    + ^* Y1 S! i. [3 ]2 d" L  Z5     18     100    2018-02-11
    6 @) t5 h2 }+ z1 o" F+ X6     17      40    2018-02-18
    3 o/ ~# s% y. b( Q9 Q' a8 M$ p: X7     19      50    2018-02-25
      |+ L* v' x4 U& l% _df.resample('M', on='week_starting').mean()
    9 E! y' l  m$ a# S               price  volume
    # Q6 W' O. ~  `& F( ^, Fweek_starting4 C" e0 i8 W- |, l4 I" j1 V9 b& y
    2018-01-31     10.75    62.5
    ) O: z5 y2 g  |* F; Y; l# I# p2018-02-28     17.00    60.0
    2 d- K' `! I2 C1 I& A  n8 J6 P4 n9 C/ d" M8 \( X* F/ g
    1
    $ f6 H. X  _$ n# c2
    8 B& o) T9 l$ }3) Q& w+ l5 W# p% @# C& A  r
    4
    3 n3 A0 T, F6 }* e/ R5
      u6 Y( O+ ^' N+ U) v3 e" _3 F' j& v" V6& A% x% u  F' O8 H; W- U. [0 l
    7
    5 L! C, H8 M- J, Z  Q2 W8; {# h6 F# {% S# w  Y* w! w
    9
    ) o: P1 W% M/ q- C5 T2 M10; h1 z) t. s, e
    11
    " H, U/ N$ M6 P) S. v129 E1 K! J" {: S8 G/ u, h0 x
    139 P0 z0 ^$ b, W2 E" M. P
    14
    # B* J' f. |: h  n. }% @: W1 E+ h152 b: k: R# p& m  v9 T
    16$ P2 f: u$ A' E% x$ K
    17
    + I" H# c8 ^% x1 A" B# E8 g2 Y9 ~181 O, L2 G2 q$ e/ O
    193 l$ L, A5 K  Y$ U1 {6 Y
    20
    ; k' o, @  ~) G21
    . M5 w. B( }+ l1 @  l对于具有 MultiIndex 的 DataFrame,关键字 level 可用于指定需要在哪个级别进行重采样。" M( c1 y4 j2 W3 S" o1 [. j+ ~  B
    days = pd.date_range('1/1/2000', periods=4, freq='D')2 u% R( h2 d) ^7 e- a
    d2 = {'price': [10, 11, 9, 13, 14, 18, 17, 19],  S& K) n( M, w1 j3 w/ N9 k
          'volume': [50, 60, 40, 100, 50, 100, 40, 50]}4 U. H+ K4 Z2 @+ [: R8 f. }+ j
    df2 = pd.DataFrame(
    * e& T# `" B$ s( m. n$ w    d2,
    " N2 y) P% S& z; G1 v    index=pd.MultiIndex.from_product(
    3 n3 u- L- Q4 O- q; R6 ]        [days, ['morning', 'afternoon']]
    ( `! y4 X8 P, S    )( n% Y" X) p) @. U9 v. J
    )8 y! |8 Q; p1 {; F0 p& a( G
    df2, I* x1 _+ r6 D) t' V' h
                          price  volume
    ' d, R3 z6 H+ w9 i) B& K3 G6 M7 ~2000-01-01 morning       10      50
    & `4 w+ W! H" r9 E) I3 w7 X           afternoon     11      60
    % P/ B0 ?8 N8 g5 M, w6 l1 q2000-01-02 morning        9      40
    . W' v0 ~% \% E9 E) ?           afternoon     13     100. D+ E0 [! Z7 e. @/ G- ?4 V
    2000-01-03 morning       14      50$ E8 h9 S1 D; p
               afternoon     18     1004 X4 ^. t: Z- @6 [% g" K' @* D
    2000-01-04 morning       17      40
    2 }3 p( t, Y6 O           afternoon     19      50
    ( f2 [  g! `+ D& g+ k' m! h* Fdf2.resample('D', level=0).sum()8 D" r$ d2 R# K7 K6 i
                price  volume8 y6 _& ~1 V) X: x9 B- h3 p
    2000-01-01     21     1106 a- m* ^4 K) Q- a% S
    2000-01-02     22     140, x; v& I8 {0 Q: s2 u& w5 y
    2000-01-03     32     150+ M/ J' Y& q4 h2 q  T1 }. j( z8 q
    2000-01-04     36      90
    ! L0 f9 |4 H% A& ~+ h
    + x* G7 n6 {4 l18 y2 D3 }8 I0 b/ R- K
    2$ G! h2 Q/ z7 L: l  V
    3# b4 ~+ T2 l2 G. O# ]% ~
    4
    & q! S4 @4 k1 Q8 [7 ]5
    6 ]8 I8 n) _9 o4 |4 m) u2 ~* k60 F8 f7 t/ h- [) L- x, Y
    7  a( q  m- k6 r0 \" W
    8
      C" S% ~/ W6 {* `98 x2 M) D0 i+ j! w
    10
    1 Z4 y& H6 `0 W11
    - ^+ A; q+ w: l" {" c12
    $ @7 R2 f# G9 O" z2 @! V2 ^; r13
    ) H7 h  J9 a& r9 A1 f) ?147 ^2 g  M6 p" r7 `4 B# n9 A
    15
    4 ]# y8 t! h& I$ |$ L168 _, v& p, f& o! m- C/ N' `& ^
    17& D$ y2 v/ M! ?1 P; M4 Z
    185 T- c( Q, L7 p7 `3 i
    197 [% w% c. ?* y& d7 r
    203 C0 {* g9 T; D
    21
    3 A: S3 s# Q$ m22* A: m7 W2 M) [6 ~, x. J
    23
    ) f; H; b+ k5 U. ~/ t24
    3 p) Z+ l$ q% ]1 B256 P0 z  {3 l$ Q! a6 b
    根据固定时间戳调整 bin 的开始:
    9 Q  s. f9 Q$ s" _  J6 o, y0 s, sstart, end = '2000-10-01 23:30:00', '2000-10-02 00:30:00'3 R4 l8 G; Q/ Z
    rng = pd.date_range(start, end, freq='7min')7 Z5 j% y+ M, D- a
    ts = pd.Series(np.arange(len(rng)) * 3, index=rng)! ^+ A3 S1 P, X7 h- x  K" x
    ts
    ; ^3 R. W1 Y" g* T7 V2000-10-01 23:30:00     0
    ; ~* G+ ]8 G& @: O6 v8 n3 f2000-10-01 23:37:00     3: \) W/ x) E& }  n+ X! X
    2000-10-01 23:44:00     6$ ^$ p( s$ X# {2 f) D5 d! }
    2000-10-01 23:51:00     9' E" c& i. B0 H/ H0 C
    2000-10-01 23:58:00    12, K0 m' k; J4 M
    2000-10-02 00:05:00    15+ Y+ N. z+ b4 j9 h' i: V4 C
    2000-10-02 00:12:00    18! `$ t2 r/ ]" e# E$ Y0 [9 v: _% F
    2000-10-02 00:19:00    216 A1 q8 J1 \$ W( m( v
    2000-10-02 00:26:00    241 `, g4 E6 k0 m- U- e
    Freq: 7T, dtype: int64
    2 R, @, n& F, p8 f7 X1 ~9 k6 U8 V# M7 W% F+ E5 q3 Y* m
    ts.resample('17min').sum()0 {( V3 z4 ]. l1 P  m8 X
    2000-10-01 23:14:00     0
    0 g  i) q4 t& C- V+ j2000-10-01 23:31:00     9
    . u3 F: Z: V  r% I  r1 w2000-10-01 23:48:00    21
    / ]  ?: M# x1 r" R1 G) S- g2000-10-02 00:05:00    54
    + S6 i  e! R8 i1 ]4 c9 |2000-10-02 00:22:00    24$ c" H4 Q  |/ K  s7 \/ D0 |  u
    Freq: 17T, dtype: int64
    8 c+ F3 P9 j( A5 I: g$ L" w" V
    . c% B7 v0 Q; F% Sts.resample('17min', origin='epoch').sum()
    ) F( [0 o3 n# a# ^5 h# A2000-10-01 23:18:00     02 g0 ?1 V# p; R6 S9 Y5 G! l& j& h, K
    2000-10-01 23:35:00    18
    7 t' y3 T  y# o2000-10-01 23:52:00    27$ N+ [" |9 T3 A2 i7 f% E: b+ h% m1 x( T
    2000-10-02 00:09:00    391 L! s: K9 b1 f) U
    2000-10-02 00:26:00    24* c5 |* i$ P5 @1 J! ~# u
    Freq: 17T, dtype: int640 B1 ^0 L" Y& w' N
    * W$ C# O6 e! Z0 e( J' u
    ts.resample('17min', origin='2000-01-01').sum()
    / q/ p( l/ r" P4 j2000-10-01 23:24:00     3
    % ]) I4 G  _: Y2000-10-01 23:41:00    15
    9 F5 h' C2 U! b& q+ i2000-10-01 23:58:00    45. ~6 l: x- N5 w9 W- G
    2000-10-02 00:15:00    45! L& g5 S4 {3 j: b8 B' M; S
    Freq: 17T, dtype: int647 b+ M9 G3 c/ h+ f3 T4 h5 l
    ! D# W% g! Y, R- e$ k
    1
    & [, P& v" W+ Y2
    8 [  }5 b8 U0 P3
    6 A- h4 J, e  t- l4
      g3 r! E7 ^2 k9 {5
    & Q, k& ?. t( ^6 m4 I6  d) Z: m' @: _+ D
    77 q8 A% v% l- D0 y4 V6 \
    8( t5 }& e% ?1 J/ T$ B
    9
    0 G; A) m- W. @7 _( v- ?, J10  Y4 i* R# y, F0 _* H$ P
    11& C, h: z+ |# m# e. O
    120 j& k# D! p& O! \
    13
    / V! i$ K# G/ H! U! Y14
    : S$ `: P0 u7 a) n7 j15
    : i2 k$ o5 v+ S. f4 F16
    * k. j* _. b# e' m! M2 m17
    5 `3 h6 j" c. Z. O& B6 A& j18
    6 [* c& H; x. ~( ^19, i4 Q0 R" X: x- t+ n
    20, k& C. j/ M+ k8 Y8 Q# M
    21
    + l; ?- E5 [0 [5 o! L+ B+ z" R22
    7 w3 A, }$ C3 L5 M- E9 \) B23: j0 H; g0 L# v6 ~
    24
    8 T, m* u+ I( a  ]& e! o0 t25
    $ ~$ }) a1 H3 a26
    7 u, G0 d4 b0 O271 \4 _, M8 z) i
    28: V8 T# ~9 ^0 p* h+ A% T3 `7 x9 H$ u
    296 r+ k* R# X0 k  O( ~: j
    30$ e' U/ J+ m) u! p# D% T+ V( C
    31+ A( l2 f1 E$ j: X
    32
    1 r4 U' ?  y, _3 |33. B" {9 D* u, ~1 A
    34/ j' U9 X! B$ l9 w/ E
    35
    $ D6 G" A6 Z# H36$ |1 D* U4 D) Z, {7 J
    370 `' i1 X2 e' l, _( w
    如果要使用偏移 Timedelta 调整 bin 的开始,则以下两行是等效的:
    ' X3 Y6 J) n) O4 hts.resample('17min', origin='start').sum()) N5 C! a3 _- \1 j3 C0 z
    ts.resample('17min', offset='23h30min').sum(), A0 G% h2 o1 p/ k: T
    2000-10-01 23:30:00     97 \  N1 v: y" g8 W5 Q0 P( c  X  C
    2000-10-01 23:47:00    21: c7 X) p& c, e  o* @% N9 U3 I
    2000-10-02 00:04:00    54
    " D2 v1 J0 M0 Q' j2 ^2000-10-02 00:21:00    24. ]$ m% U0 s, a! r; o9 c& `3 p
    Freq: 17T, dtype: int64
      h. }7 }8 G: n, e8 w# N1
    8 {& k& v( }) I9 [# H7 X! l3 x2& h3 c$ q; W1 u" G8 A' ~$ H
    3$ w4 X! \& m) G; b
    4
    ; L! o" [' a5 L5' h( D( a+ @, m
    6: W% u* y  N$ \
    76 L) I1 V0 W. i6 T: h' X
    10.6 练习
    $ B: y/ g. z, WEx1:太阳辐射数据集. J' K/ R7 [  e  e5 k0 [3 `3 f! Y
    现有一份关于太阳辐射的数据集:
    & i' E, t( s9 g* q
    , j$ e5 A+ N. @6 V& ~2 Ddf = pd.read_csv('../data/solar.csv', usecols=['Data','Time','Radiation','Temperature'])+ z2 F9 h( Z) ?9 Y" j, j
    df.head(3)
    5 t; s2 m8 ]) G4 `/ a2 z& F& U8 D$ t; e6 K$ N, l
    Out[129]:
    ' ^1 ^7 ]5 V1 \/ q. G                    Data      Time  Radiation  Temperature6 B  s; z  m- d% Q" k
    0  9/29/2016 12:00:00 AM  23:55:26       1.21           48
    ) @& I' T2 E) F8 v1 e- I1  9/29/2016 12:00:00 AM  23:50:23       1.21           48
    3 v: v$ Q, i1 h1 _- V+ E' _2  9/29/2016 12:00:00 AM  23:45:26       1.23           48
    3 ~/ b/ f% @" H# y1
    ( G; W; `% `, v( j4 I" @- G2
    4 O, P6 f' e: M! @3
    & B$ R4 M( k/ m+ e/ i4
    ; x4 n9 t! Z, o/ z+ h  t8 i& d5
    $ c0 O2 o1 ^& |' E. w- P6
    8 A' @9 ~  Q. L7 i( k8 E7
    $ m6 |% _: U2 z2 ?& H81 _* D' g7 y7 }' x9 D5 Z6 ?
    将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。
    2 ]" C, [. ]. u$ q% Q) S每条记录时间的间隔显然并不一致,请解决如下问题:
    . _$ |1 v: G+ Q, }找出间隔时间的前三个最大值所对应的三组时间戳。
    9 p6 l/ b: c! P( ?& [# g+ C是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。5 D+ p) C" u& Q- T+ J( U% E; p
    求如下指标对应的Series:
    . ~' m. B2 t2 y  |# m5 K+ w温度与辐射量的6小时滑动相关系数
    ! w0 W) V' |, |) \5 i/ }以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列
    8 T- \. L8 P) d9 V  P每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)
    5 Y7 ?3 f& z  O7 c9 I1 Jimport numpy as np
    4 j$ u( S+ t, Aimport pandas as pd
    & v1 M% O5 k6 p  r  |9 R18 j. q6 x- ^5 t" |; N1 T
    2
    1 v1 w/ Q( q, t5 j将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。( ^3 g8 k9 l$ C0 L- L
    data=pd.to_datetime(df.Data) # 本身是object对象,要先转为时间序列
    ( M* o3 h5 i" ltimes=pd.to_timedelta(df.Time), }1 R4 V7 Q( R7 n. o) C) D8 H
    df.Data=data+times
    6 T- f% }5 w% f2 M1 Tdel df['Time']
      E- d0 y. F; ^6 l5 d0 Xdf=df.set_index('Data').sort_index() # 如果写的是set_index(df.Data),那么Data作为索引之外,这个列还另外保留
    ! V, Y) s8 M' {) ~# f9 sdf: p& `' Y2 @6 `6 y4 p- \2 `0 Q
                                            Radiation        Temperature
    $ `5 i# f0 d: q  x7 i7 u3 CData                ' x7 z# P  \5 \  u
    2016-09-01 00:00:08                2.58                51
    " t( s1 c" c* R- u2016-09-01 00:05:10                2.83                51
    $ y8 m2 M1 K' z, o2016-09-01 00:20:06                2.16                518 e0 n# J1 P, H, @
    2016-09-01 00:25:05                2.21                51
      f( F5 d/ i/ F$ X1 u. e2 J9 |2016-09-01 00:30:09                2.25                51; l7 N$ t; y- d5 U( x# J1 P
    ...        ...        ...
    ' j& w8 s' o5 t0 d/ n/ M2016-12-31 23:35:02                1.22                41
    7 t- g* j( V  C( `3 k6 |& p2 _2016-12-31 23:40:01                1.21                41$ T1 t" z+ F. U3 M! L
    2016-12-31 23:45:04                1.21                420 C% y: a( `/ a2 x; w
    2016-12-31 23:50:03                1.19                41, y, T! b1 V2 o" W# Y
    2016-12-31 23:55:01                1.21                41
    # [1 t* D/ Z5 c5 x& D0 S* j& W
    / f# ]0 J& f+ A15 P( r( P1 r6 C: D" h
    2
    , k8 f; J1 V1 i& t* m: k: a, R33 @0 h/ ~6 D5 F* h
    44 m0 O% I; h8 M" ^2 S9 e
    5
    ( z. c+ f9 {0 Y( J1 a: B6- I! c* U: I/ @8 s5 ^* d8 r
    7
    7 H$ J0 e1 v( u. o9 C8
    6 [0 b8 s. {2 k6 H" E9
    8 Z* t, E& g* i5 `10% Q* ?4 u& F! i3 m7 Y0 `/ O9 x
    11
    9 A/ a6 M# z/ B. M- J! U/ C12
    6 ]. S+ J1 ]+ S: B/ L. J13
    0 ?: b1 ~5 Q* Q14: d1 k2 I+ h0 U5 ~  l
    154 @  H- c8 I) @4 v0 |7 G
    16
    ( D$ f, E% C. T6 T! y  K0 A( r17
    - z% R. f0 i' z1 E18
    : }7 [& O. l3 M# ~19
    0 l7 h' |- Q2 C- N& H8 r每条记录时间的间隔显然并不一致,请解决如下问题:
    ) O5 t. Q6 j! v, H! a! |- [( y找出间隔时间的前三个最大值所对应的三组时间戳。( q- M; z0 y! s7 W% d
    # 第一次做错了,不是找三组时间戳
    4 m8 S# t. D3 W' j; zidxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]7 a0 N9 L6 V% P% V
    df.reset_index().Data[idxmax3,idxmax3-1]
    # [- M* w/ D$ W6 A4 P9 k0 i* a8 H, X' x- ~
    25923   2016-12-08 11:10:42
    4 w. c* z# o1 G24522   2016-12-01 00:00:02" H2 }& `+ \2 C# Z: F& U* N
    7417    2016-10-01 00:00:194 t4 Q/ \$ S  n: n; a
    Name: Data, dtype: datetime64[ns]
    ! A3 E# q; g* e+ c1 ]11 i" {( F3 M8 s) }3 V
    2
      P1 b' c0 C2 e7 x3( R! t( O1 |" m8 ]
    4+ @  z  U. f; d
    5
    ; e* X$ H3 Z0 K5 E3 G" U6& v6 C& d  \$ o9 \: q( S/ f5 V$ @
    70 Q7 V" P) q6 ]6 G2 a
    8& q) U4 F2 U6 S. i
    idxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]
    1 Q8 N. i, z2 E4 o+ c1 ~- F7 slist(zip(df.reset_index().Data[idxmax3],df.reset_index().Data[idxmax3-1]))
    0 J8 A% Q1 `/ }1 }# B
    : W: P/ d/ G6 v[(Timestamp('2016-12-08 11:10:42'), Timestamp('2016-12-05 20:45:53')),8 E9 Z, a+ R+ q
    (Timestamp('2016-12-01 00:00:02'), Timestamp('2016-11-29 19:05:02')),
    , C0 K0 b) {/ o% @* i! ~* { (Timestamp('2016-10-01 00:00:19'), Timestamp('2016-09-29 23:55:26'))]
      p8 V# M5 S; N& ~% s' Q1
    / {$ Y2 @4 w) e3 Z/ R$ c& g( A, r2
    5 \) |! f* ^: E/ S- G3 F$ ^- C3+ l& z* O! h+ F( Z2 b/ i( _/ m
    4
      c' Q; A; i4 k7 j; y4 y9 U: y5
    9 S3 V/ V  ]/ z6
    $ \8 h+ r3 Z3 Q% l参考答案:
    ' Z+ z: a2 U. Q, @7 Z4 i8 t7 x1 @8 i
    s = df.index.to_series().reset_index(drop=True).diff().dt.total_seconds()) E; ^2 ~: Y+ K$ u' o6 G6 I% k
    max_3 = s.nlargest(3).index
    ( n, S# n. H4 K$ q8 R! Ldf.index[max_3.union(max_3-1)]
    , y" l% r7 {/ y5 b8 y. P" J% ^" W3 N, j: `- N; N5 i4 j
    Out[215]:
    4 ]! z) M4 B* u" Q$ gDatetimeIndex(['2016-09-29 23:55:26', '2016-10-01 00:00:19',
    ' q! ^. B8 R7 d9 O               '2016-11-29 19:05:02', '2016-12-01 00:00:02',+ p, J$ |( q0 L( p
                   '2016-12-05 20:45:53', '2016-12-08 11:10:42'],
    8 m/ n2 V3 y" d5 J! q: S- M: V% A4 f8 u: V. T              dtype='datetime64[ns]', name='Datetime', freq=None)
      S; x+ g2 Z( P- w/ ~& `! |+ @4 [15 t$ {( Y1 K1 Z4 I& \6 C
    2
    ' ^* S, O- i1 c0 ?3, f2 z( \. W$ z+ V1 `) C8 t: y
    4) ^/ ^% j! @% q- H+ f: {" n
    5
    , q6 r, c1 k4 ~- c6( {) q7 `' y. r$ |# u! z
    7
    ( k7 L# y# R+ X& J87 \- m* e; L; n$ j5 K$ B7 }
    9
    . B( [7 Z& W- a1 w! z是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。
    3 P, C2 W- B! |5 W: v- `' u* A# 将df的indexydiff做差,转为秒数后排序。再求几个分位数确定取值区间
    9 o; k# R4 _5 ^- r0 Ts=pd.Series(df.index).diff(1).dt.total_seconds().sort_values(ascending=False)
    4 ~0 ]& D3 _9 r# gs.quantile(0.9),s.quantile(0.95),s.quantile(0.99),s.quantile(0.01),s.quantile(0.03),s.quantile(0.05)% d: c% `6 B6 \1 j# r) j: h
    ! a1 Y5 B, F6 r% L! b' \# o
    (304.0, 309.0, 337.15999999999985, 285.0, 290.0, 292.0); V4 {. _4 y- \1 w/ P8 ]4 Z
    1
    ! j% S; {  \; E/ ^4 @1 ^2) I6 w, j- m; U3 f  V& b
    3
    ' O2 S4 y( g$ q% M2 K4# d! q  z. G  Q2 @6 \6 ?( _
    5" `8 e2 ?; q; s, y! M
    %pylab inline
    : Q) D+ R8 {! o9 C' `_ = plt.hist(ss[(s.values<337)&(s.values>285)],bins=50)
    ' Y% K1 v& `* |5 o/ e8 ~/ v1 T8 ~+ e# Bplt.xlabel(' Timedelta')
    & j4 y; i/ h0 ~. E4 ~* a- Z, Zplt.title(" Timedelta of solar")8 s( E7 L) Q" A* }1 g
    14 ~) a( j; U' |1 T' c4 q: R- S
    28 I2 M; m/ U3 \4 u1 ?
    3. K& ?# t- P9 T( Z
    4; r2 K* w. C" k
    " E1 ], j" M9 [) J' ^

    0 _  a, B( @( r3 n) Y' |求如下指标对应的Series:  Y3 E! ~8 J  x3 K
    温度与辐射量的6小时滑动相关系数
    # J8 f$ B  F+ H- M6 K! {以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列
    + o8 n8 l. E& K8 E% H2 X7 M每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)7 N. K. R) S3 V1 Z. X/ F; b
    df.Radiation.rolling('6H').corr(df.Temperature).tail()
    9 ?% s* ?* W. h$ ?1 M& @* s5 C; N/ F6 v/ K: x: z) f
    Data
    , L$ h8 Z! W8 a, i/ A2016-12-31 23:35:02    0.416187
    * [' Y& U" B7 ?2016-12-31 23:40:01    0.416565
    ' X+ g. D( Q7 O9 @- M9 [" p, U6 f' L2016-12-31 23:45:04    0.328574
    # m4 y* h* z/ e5 O) Q, q2016-12-31 23:50:03    0.261883
    / y; u" `2 b  q9 y' c2016-12-31 23:55:01    0.262406
    + ?, }( D1 E' O. Sdtype: float647 S6 ^# i8 w9 h; k7 E; s
    16 x2 K# u# m- q' K& N' v
    2
    . J8 V  z- D% Y( I3
    0 x+ p' c2 B7 h3 @, {6 b; a4
    1 m5 V' w/ z* k1 P, c' n5
    6 P, C) J' r0 B3 p# I6# d* ~  T# Y' _9 A7 _. B
    7
    , _1 b* x4 a  C& r6 s: N8 Z8
    . r9 Q5 x- F9 c0 x3 r! L( h6 g6 u9
    7 _% |( w" h! I: o' Q1 i8 {df['Temperature'].resample('6H',offset='3H').mean().head()
    ( m- G$ ^: l7 ]) L6 N
    $ b( m, G' G4 a0 `: e. b5 Y$ T- u+ k' mData( ?* _2 `& r+ F) G( v
    2016-08-31 21:00:00    51.2187501 d9 }1 d! {/ ?* ^  p# l( a
    2016-09-01 03:00:00    50.0333339 i' r5 m" v: d/ r9 D
    2016-09-01 09:00:00    59.379310, p' z0 E/ P  F% N# C' @/ R! L
    2016-09-01 15:00:00    57.984375
    $ P) a! c/ u% o2016-09-01 21:00:00    51.393939
    : N! v: `- U4 s% \( E) z" ~Freq: 6H, Name: Temperature, dtype: float640 c: z6 C+ }% Q& x* `' J
    1
      R0 I! t/ g, B27 y4 U7 h" k% d
    3
    : H. B2 j8 G7 [. {. t4
    * A, O: x7 j. J5* P. m; |" C1 A; v
    6  |) d4 o( X8 o) a% B% ^4 K! S! n
    7
    + Q  K5 V/ U3 R: @; V$ ]' K8
    9 w* g9 s# _, Z6 f$ @2 i9
    ! K0 [7 A+ H! \: X最后一题参考答案:
    ' b$ W2 W. K5 o1 M+ ~) C& n: i
    * m( z% \* U  C# \* d" V# 非常慢7 m3 @  \- {$ t& |) ]
    my_dt = df.index.shift(freq='-6H')
    ' T% C1 V8 a+ ^( u* ]$ \int_loc = [df.index.get_indexer([i], method='nearest') for i in my_dt], P% H( k! u: G5 A. c' E. A' X
    int_loc = np.array(int_loc).reshape(-1)
    " O; e' h! L; A% _' @$ L. zres = df.Radiation.iloc[int_loc]: X! `1 M; P0 x9 U+ m
    res.index = df.index
    # F( f- `: `7 _: Jres.tail(3)+ i" F" f) w1 O! G! O# B
    1
    : H) F$ j  M# @4 _+ n8 \0 _2
    - q+ W) v2 {5 W% x3
    * G0 r2 C% J4 w5 |7 x4 v4
    8 @- h. c9 b: z1 C$ s7 Q" h5 v55 K# s$ A4 ^. U/ I
    66 q. h; p- ]1 M/ _2 U  J6 T6 \
    7
    # x9 y# |! ~4 m' P! I0 f# 纸质版上介绍了merge_asof,性能差距可以达到3-4个数量级
    8 p' H! D4 z: o7 F9 k. [target = pd.DataFrame(
    " J* ]7 y* H6 b$ A8 B% h6 A    {
    1 L9 r3 m2 b% T        "Time": df.index.shift(freq='-6H'),5 T& c% m& ?% [& z
            "Datetime": df.index,) y, q+ _' R  Z7 {1 u9 Z
        }
    6 D5 d, \0 {- D9 O% L0 V4 n)
    - u8 w1 A$ i1 O! ^
    * G7 ?+ O# \: ~8 Z. C: s4 _' d4 bres = pd.merge_asof(
    & I+ k3 P. ^( m* i( W8 z    target,
    9 V: D4 Q6 e5 x( S0 q' A    df.reset_index().rename(columns={"Datetime": "Time"}),
    " _; }5 [5 ?8 Q" Y' H" l8 \: R3 J    left_on="Time",
    ) [3 j+ j/ H$ w7 e& W8 r6 e    right_on="Time",
    % n& f* r/ M6 F) w    direction="nearest"
    9 f) i, p, M9 g9 A+ M+ G' ]).set_index("Datetime").Radiation8 J6 q( G3 [+ c, q
      [* c9 H5 U: e; c
    res.tail(3)" h) |* N. B' Y3 D! ^: L
    Out[224]: ! _  r  i  C) T! y
    Datetime" ^; Y9 o2 {! H& [4 m- g/ U. J
    2016-12-31 23:45:04    9.33
    * `  p  X8 ?1 P: g- g( H2016-12-31 23:50:03    8.492 V: O, m" }$ B* d8 i) f
    2016-12-31 23:55:01    5.84/ d6 ~# i2 C2 R9 _$ E
    Name: Radiation, dtype: float642 }% |# j! X2 v; b( V- M  ^( e5 n
    - K: `: s# M5 H$ q/ G
    18 h. S4 D, n1 P3 e! B
    2/ f% N+ z9 l# Q; {: W
    3
    7 y* G& i" w; n; W% |( J$ `9 M4
    # E. @: K/ U; |" i9 b! H57 j/ B& r  w& d. T  w# r
    6, O& e  n: S9 B3 n
    7; R# F: Q0 M" P
    8, \. R0 |' h5 ?" q
    9) i6 H% u, ~% e  D) L* Z
    10/ z9 H3 g# s, ^/ A* S/ X; t1 K, R9 e
    11% U. ]1 |1 E. J9 N, Y4 K% f
    12
    + n# Q! ?6 m* k% s6 G13
    ! X% f% D0 G4 @( h( S14
    # `9 G: r) G- u4 |+ t, B( E$ n15! Q. ^7 K, y% [% y+ B0 a  E1 V
    16
    9 l- d& h+ v" c3 I$ N; {1 B) y17% p) c" a5 o2 d4 q
    180 s1 Z) M) z9 b: V& V
    194 q5 a( I+ ?2 A' c5 Q1 p3 C
    20. a3 Y$ [" u: T, _
    21
    3 f' G# m/ i9 p5 R3 u22  W6 r/ @& {# d! ]( |
    23
    9 q; I9 M0 f8 X& |% C7 z' gEx2:水果销量数据集
    $ e. z& a3 g% @& V0 U5 C' e现有一份2019年每日水果销量记录表:
    0 I. ]6 a) ]/ m5 g6 C" j2 n8 g1 U/ e- ?% Z) r  t
    df = pd.read_csv('../data/fruit.csv')
    8 @" P0 q7 n8 b2 N7 adf.head(3)# z" {7 D: _3 x% K" c

    # R: w) D) e' S' j. L' Z1 KOut[131]:
    " a3 Q5 d; b5 L. l         Date  Fruit  Sale" G& S4 |) U  v  o9 n! r( G8 C
    0  2019-04-18  Peach    15! d* B. z9 [* s3 o! i3 a
    1  2019-12-29  Peach    15( J; m3 b* x' m
    2  2019-06-05  Peach    19
    . Z$ i6 h" x& k) A1
    . Z( ]( p6 U! W* `29 J# C: S) g; g3 P5 n
    33 X  J4 v6 l& p( v
    4% W+ c: y+ ]- a: C# Y; T: u
    5* m8 B' D! a( ]7 T0 E2 ]
    6
    ( B: ~) F6 K: x; h9 e+ a$ [' n! W7
    / k; _5 m& a2 d3 C# [3 P8
    9 N" \  F/ V% o0 ]# W* d9 N统计如下指标:
    + L* g' f, r& K$ o每月上半月(15号及之前)与下半月葡萄销量的比值
    7 d/ F: T+ @1 X2 Z% Q每月最后一天的生梨销量总和
    ' x6 ]1 M# y5 d, F3 y8 @( S8 f: x' h每月最后一天工作日的生梨销量总和+ g* @4 y) w6 V
    每月最后五天的苹果销量均值
    + c9 m( B9 q+ t# s( z7 s6 S按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。6 {$ M1 ]* }2 J% H3 m
    按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。3 J9 s- Y% _3 K/ I- g9 f
    import numpy as np
    ) v+ S3 u7 ~8 j3 z7 N2 }0 J  {import pandas as pd& f4 W, U" ?; i* v! U* G
    1
    3 j+ O; u3 M( |# j2
    8 L. k- S( P: n- C  u- ^- y统计如下指标:
    . p6 Y5 x% l0 T* s- s8 Z每月上半月(15号及之前)与下半月葡萄销量的比值
    + L# F# A1 f, F9 U每月最后一天的生梨销量总和1 F9 X' r. ]' z/ I9 @$ F
    每月最后一天工作日的生梨销量总和
    0 B- I9 U# i4 y每月最后五天的苹果销量均值6 J+ e- F) W/ L8 z) y0 M
    # 每月上半月(15号及之前)与下半月葡萄销量的比值9 T; Y0 f7 a9 j" N! U4 l
    df.Date=pd.to_datetime(df.Date)# r  I9 a( j' I& a0 [3 |* u: P
    sale=df.query('Fruit == "Grape"').groupby([df.Date.dt.month,df.Date.dt.day<=15])['Sale'].sum()' R3 O; f  `, }$ y9 ]* C
    sale.columns=['Month','15Dayes','Sale'] # 为啥这么改没用啊
    + D7 L4 G; s8 {* L# J6 psale=pd.DataFrame(sale)
    / E# Q- `( ?. H) Z" Lsale=sale.unstack(1).rename_axis(index={'Date':'Month'},
    $ Y" j- R: _7 n- B8 ]8 {. I. R                 columns={'Date':'15Days'}).stack(1).reset_index() # unstack主要是两个索引都是Date无法直接重命名( I7 G  r: I2 s, R7 P* G2 r
    sale.head() # 每个月上下半月的销量0 @% O' k. }( z- d6 ^9 X! I
    - O8 g6 B! J0 m; t6 z2 F
      Month        15Days        Sale
    0 ~2 h* a: L/ y( W7 C" b0        1        False        10503* @1 e$ u- T0 A+ I( D( J/ b/ ~9 ]4 w
    1        1        True        12341% A( w9 M' k( T! {
    2        2        False        10001! b; Y% N5 m8 ]' L8 x' _
    3        2        True        10106& ~$ Q  r% j% b/ w
    4        3        False        12814
    & s+ ]  s/ a8 ?( F5 n- m" ?; B* Q: @; ]
    # 使用自定义聚合函数,分组后每组就上半月和下半月两个值,根据索引位置判断求比值时的分子分母顺序
    5 v+ U- l+ L- a. b- Qsale.groupby(sale['Month'])['Sale'].agg(6 l$ ^' ?$ P0 {
                    lambda x: x.max()/x.min() if x.idxmax()>x.idxmin()  else x.min()/x.max()), I! s( |4 v9 D7 m% {- Y
    ) k" V- t# Z- x
    Month
      K7 G$ ^# ]7 F+ @5 L) P1     1.174998& G  i$ D" v# q! `! k
    2     1.0104990 F% l0 e7 W* c
    3     0.776338
    ' y- F0 m! S' _' l2 \% R0 Z* m+ A4     1.026345
    - ?9 K( o4 k8 E3 W9 g  V' ?5     0.900534
      |- V9 m- M) S7 ^# p$ ^" j( b6     0.980136% j& h7 {9 Y1 S" a) E+ I7 |+ f
    7     1.350960
    ; a! q9 E; m! d3 F! i8     1.091584
    . m6 q- Y8 r0 R9     1.116508
    / @7 D* L5 L7 f6 m10    1.0207843 }6 S6 r* Z& G6 p, }% h$ ]
    11    1.275911' x8 w6 q5 p) m+ y
    12    0.989662, Q. \7 Q6 k. k7 K' p
    Name: Sale, dtype: float641 d( g  x" t: i! W/ }3 b6 S
    ; Y1 g) B. S4 x, C
    1; K5 f# [9 Q  Z  c' V/ D: @& j2 h
    2
    % o1 |/ }$ {# f6 u' {8 j! z3
    . t  M1 u- B, g$ }; N! k6 V4
    ; Q  w# L( X6 I4 F9 v2 s5
    . k( I- o2 ?2 i4 i6
    9 X2 D9 J: u% d" Y, V. g7 m7; v% |2 l  d6 w: O( L
    8! h0 W) e; M0 X' x0 _4 a
    9+ v9 q( E7 B8 [2 t  e4 T
    10
    - i1 f, Y* z8 W/ W" }11
    : H3 C% b2 R6 D! H8 m+ R" G12
    1 f. E& `% O) U1 m- R0 b13
    ( {  J' o+ i' R. T14) {- `& S4 n) F
    150 I& u9 G5 B4 L
    16
    6 f  J, S7 |" q2 k! g17
    + x0 r4 k. L2 t% s: r6 ]# K3 P18
    : ]; W4 F0 ]8 W# k% Y& m19
    + c: {' A* `& F4 c( R* I20
    " b( Y2 Q2 y, o3 ^/ R4 Q$ X  V. U21
    0 p8 S6 g* E8 I& T9 l22
    3 z+ Z! V9 `$ K+ J  j7 P8 }23
    - \3 ?- ~( b1 J6 T24
    8 L7 Y8 I; h9 i3 T25# f9 v& d& ]1 X, o# ~  d
    26
    + v: d: D' Z7 U% Q; t! G27
    + \1 E2 U: U7 ^+ z1 [  ]) Y' i. P& Y28
    2 g( B: J0 R9 f, W29
    4 Z5 p* q5 U6 K0 H, Y30$ a+ `6 J$ f, D! C9 ?# P
    31
    : ]# Z" A: I" p% Z/ P% Y32' _. R6 e& h2 b, ~$ k  _' K
    33' l( \! W' Z- F* m4 o
    34
    ; Y. q. n7 B' z- a8 o$ n# 每月最后一天的生梨销量总和
    + f, I" _$ H8 \df[df.Date.dt.is_month_end].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()
    2 I4 f1 G- B  q* u& B* h! D/ r: {% Z( k' h/ a
    Date
    2 g' N3 g" X8 @  [2019-01-31    8479 D. D5 H3 q0 s# K" Q$ i. P
    2019-02-28    7745 I0 H; L" f' l- x2 ~5 Q. e
    2019-03-31    761
    7 E6 }# }3 e7 R9 ~1 U2019-04-30    648
    3 p* D3 n2 f6 k6 w! o2019-05-31    6167 F$ s# j8 N8 i
    1# K' k4 U8 X7 L) C1 t# P! Y
    2
    # d! f  \) u$ Z, d+ T: F5 c2 f3
    $ ~" S/ L$ W" O) Y9 c0 i4
    2 e- y1 e* D4 \# I$ p5* C2 p0 D' [2 }, m2 z+ [5 k  H
    6
    ! }' _9 R0 b' Z! d7" Q1 b/ }$ d8 w$ N9 d6 ]) ^
    8
    . n& K' _7 I0 u$ e8 L90 E5 {5 T3 v7 J( x* x, @8 V# Y
    # 每月最后一天工作日的生梨销量总和9 E6 p7 F- b% a, X2 F1 ~
    ls=df.Date+pd.offsets.BMonthEnd()4 K: h6 r; D1 ]- [. K$ F( t
    my_filter=pd.to_datetime(ls.unique())
    / \/ `0 E5 f' O% h! @df[df.Date.isin(my_filter)].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()
    ( P( ]; h2 f, s8 S3 Z! j
    3 p; B+ g- f& C5 BDate
    # N/ F' {  W7 ^& @* D. x3 u+ K2019-01-31     8471 P; G; S7 N" P& H' q
    2019-02-28     7748 }2 Y: ^/ i. f+ Q  E* t
    2019-03-29     510
    $ U- P2 c! n* s9 e' k! |( |2019-04-30     648
    5 D9 B- h6 k2 {. [( q. d+ I1 }2019-05-31     6166 P: N  |  w  m$ Z3 ~" a
    1: N+ S8 ~3 h% _; R0 c2 m0 i
    2
    , z4 a5 h6 M! }- H: V3$ A0 @: l0 Y9 H% T$ h. h4 U) F
    4
    4 D) Z7 ?5 c0 i5 T$ O. R5. G' J5 i1 N- y, f1 g" n" L
    6* E9 v3 F, Y/ _0 A( f5 K
    7: x4 ~- t, B' x" w5 b( x0 a1 c
    87 X7 p. h, }- a  W
    9
    $ ?* f# M6 B% T3 V3 [" U( L2 {107 A1 Z+ I, s# ~; P
    11* G7 \7 ^) y; E! h/ x
    # 每月最后五天的苹果销量均值
    # L2 X5 F  a' ]+ cstart, end = '2019-01-01', '2019-12-31'
    1 f5 `9 B- X1 [6 S, \; F9 f7 N& Oend = pd.date_range(start, end, freq='M')4 ~/ S6 V  b* t$ n- y
    end=end.repeat(5) # 每月最后一天的日期列表,重复5次方便做差1 n6 R# X) M) M4 B& c' t
    & S. n0 J4 x9 [- ^, Q4 k. G$ X, e
    td= pd.Series(pd.timedelta_range(start='0 days', periods=5),)* O3 ?& t. l- T
    td=pd.concat([td]*12) # 日期偏置,最后一天减去0-4天
    # Y$ M2 K, _$ a7 ^7 _3 \- A4 \; rend5=(end-td).reset_index(drop=True) # 每个月最后5天的列表4 V' R, t0 q! D! M0 Z( b

    / l' _2 ~/ n1 P: j/ L; A* T2 o# fapple5=df[df.Date.isin(end5)].query("Fruit == 'Apple'") # 每月最后五天苹果销量
    ! m$ [$ d& R5 K' q: `apple5.groupby(apple5.Date.dt.month)['Sale'].mean().head()4 R7 S8 b6 v& q2 n! h$ U

    & U1 v0 s5 `" U, v+ qDate
    ( i$ z7 i1 b6 f1 j; U" i1     65.313725
    $ S9 o, Z! W! i+ x3 Y+ t5 ~7 [, O' p2     54.061538
    8 _; p2 y' l2 ~4 C3     59.325581
      i0 g% e+ Y  `4     65.795455- X1 k  K7 Y' a: j5 m! }) Z
    5     57.465116
    $ n! Y( f/ Z2 O: L! w3 S
    * _: C  o2 C5 ?3 M5 L1
    6 a! K* n* U4 k$ F7 S2
    + e3 U* k1 E& u) P9 S! g" A8 n3 [- T3
    9 B. K+ q( L+ W" h- B43 g+ K% r: S; d
    54 a! L6 |( C5 }& n* v6 B
    6
    8 y: I' }: ?; U  x# _7
    % H% w& x) o- J  `8& y1 |% G2 c% G
    9
    5 a! I* m* v$ q( c10
    , p& [" G! G0 p( y8 j- t+ R4 s11
    & ]; E4 M+ f. o2 R7 n1 m( b12; B* Q* H0 A) \2 |( T
    13
    1 K9 b& z3 q( m: F2 E: S0 s14
    $ [$ P, b$ T: Z/ O0 ~5 b6 a15
    9 `5 i# |' X3 P* u3 b% o" K5 F16
    ( A3 a+ f/ ^3 x' @9 U: ]7 x* p17
    $ |* C9 e9 P- L( P6 z1 x) h18+ q1 {5 u% T0 u, @( B7 R1 ?/ s5 h
    # 参考答案:  a+ t. ]2 W' L5 v( O3 i; P
    target_dt = df.drop_duplicates().groupby(df.Date.drop_duplicates(1 f7 T. R4 X  B) s4 ]! r+ z
                ).dt.month)['Date'].nlargest(5).reset_index(drop=True)/ r) m# L0 a; U1 M

    5 N9 Z5 C2 b0 y. o' f! ?0 Ares = df.set_index('Date').loc[target_dt].reset_index(
    4 e$ g  k5 T5 d            ).query("Fruit == 'Apple'")
    2 T! W/ c& X; ?
    5 v% [" U, b! \. y6 sres = res.groupby(res.Date.dt.month)['Sale'].mean(8 k4 \2 ?# O0 L% U
                ).rename_axis('Month')6 P! R2 x; i# q4 Q  k- B1 f

    $ z0 ^% _  F7 g/ X2 ^* |! B
    1 V" X. K2 T" qres.head()5 a, p2 E9 C! W7 f
    Out[236]:
    & i9 z, z* s+ E) p6 y, |Month
    % f- a6 D: U  S( {( I! \9 M+ a5 K1    65.313725
    " G4 K7 r: Q1 n  S- w# c: c2    54.061538
    " X4 ~  W" z$ A0 E2 y3    59.325581$ `. ?  [1 A8 V, t9 z
    4    65.795455# Q9 ~6 E( ^9 @1 C. O
    5    57.465116& N, E3 s1 B9 T5 z8 Z4 M1 t5 c$ {
    Name: Sale, dtype: float643 m2 _" l* V! E$ X/ s& d& p' M2 i
    " ]7 Y( q& c. }  f/ Z$ @. `
    1, S3 P$ o$ Y8 s: ]9 ^
    2# O7 o4 h$ _. {1 Z
    3* E! x0 A8 s& f3 Q' i0 u
    4
    ) I1 o+ }, J  h) t8 L( w5. F8 y3 ^. r- Z; q, ]& N# z7 ]
    6- ]7 c4 s7 f6 I: o* g6 w
    75 I- O% }; y, Y4 F2 C
    8' w6 ^6 L1 w3 p
    9  Z+ E. k6 k: `( d
    10
      N4 e9 r4 u* N" ^5 }$ ~& t115 i; w* M7 G7 N0 k5 ?$ Y
    12
    8 q! k% _1 k- S3 I3 a) ]1 H6 q- m13' C) _' a' P* |, q' Y1 n! z) |- e3 [
    14
    - l# e. {, o, n; V5 ]15" N4 O5 t/ Q% E* g" B
    16
    * W) e/ C& T; ?* G, P! ?; Q17
    $ k0 A/ W5 p0 u" m/ K5 w1 W# d& r8 P18
    % m$ F$ k- ?6 Y7 }; h, _' q19
    % \0 l+ |2 z+ M4 N/ N: p4 u: H20
    ' t) g7 ~7 y. H2 N, v6 ~, d  z0 N5 c按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。( M8 F+ i2 _# k/ O/ I' k- Y) f
    result=pd.DataFrame(df.groupby([df.Date.dt.month,df.Date.$ s3 M6 e+ v! v1 K1 M
                                            dt.dayofweek,df.Fruit])['Sale'].count()) # 分组统计 5 U: f' [( v  z7 z3 l
                                           
    3 q% T! y# [7 d/ L( Fresult=result.unstack(1).rename_axis(index={'Date':'Month'},7 F( k3 s+ w  M% j' Y* ]
                     columns={'Date':'Week'})  # 两个index名字都是Date,只能转一个到列,分开来改名字.
    # m0 K* j- ?: g# s! qresult=result.swaplevel(0,1,axis=0).droplevel(0,axis=1)
    3 ?# |2 S/ T7 n* x% ?result.head() # 索引名有空再改吧
    & T, s3 `7 \# Z' G' z) ~3 k+ w3 p7 N7 g  q* f1 J' n
              Week        0        1        2        3        4        5        6
    / O7 Q/ R" b- H% X0 P2 Q( j' F/ ^Fruit Month                                                       
    5 [9 K. T- ?, m+ UApple        1        46        50        50        45        32        42        23
    9 j# R: v: q$ V' Y8 I  s" d' RBanana        1        27        29        24        42        36        24        353 s2 D8 W' i7 t1 O9 r2 A& U: J. L" B0 F
    Grape        1        42        75        53        63        36        57        46
    2 u( G1 Y3 E. d- v5 t" TPeach        1        67        78        73        88        59        49        72
    / Y2 @0 C6 w/ ^9 jPear        1        39        69        51        54        48        36        40* i; n0 v, ?4 G+ @1 ]# o
    15 d# ]4 B8 W: f" X% _: A/ _% v' k1 O
    2
    ! ~  c9 Q) ]7 R  W3
    " U! f7 h; a4 H4
    : L/ d! C1 s) M5& _) ?( Y3 W; Q
    6
    8 q, [2 z7 C2 B( l7
    # }+ A& N& b$ [1 U* ~9 K: _8 t8
    + {  o4 S/ i( ?. @6 u, d9
    $ H* v/ ?$ W# h; y: e; r+ y10
    1 i: [/ i# x6 B. q5 J11* a/ q- l  i& i, g4 q
    12' r$ |- q7 L; W4 y7 Y' x
    13
    / p; l5 C5 d5 w' ~  P7 v$ T14
    + B& u2 @) Q! U7 ]155 s5 ~! ~( M3 t4 C
    按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。
    $ b9 l* f" ^) R4 i9 F# 工作日苹果销量按日期排序* W1 g: q( L* `- R& l- w
    select_bday=df[~df.Date.dt.dayofweek.isin([5,6])].query('Fruit=="Apple"').set_index('Date').sort_index()9 G2 X7 r9 l8 Q3 D9 t  q+ p- _
    select_bday=select_bday.groupby(select_bday.index)['Sale'].sum() # 每天的销量汇总
    $ v: L/ R$ x/ H( cselect_bday.head()
    & c& n5 i& G! G* N/ l& ?( Z& d3 g3 Y" O3 ]6 [
    Date4 t- f) O- G+ o0 U6 D- z
    2019-01-01    189: `. e% e- r6 q% P5 n: p
    2019-01-02    482' z" p  S0 f/ `% X  R! z
    2019-01-03    890
      J# L  {6 M9 s/ [+ T) _) a- r1 D2019-01-04    550
    ( {4 _8 L/ K5 {9 L. P) S! g9 ?2019-01-07    494
    2 r, x; h/ e5 {6 m. S& E: Y2 O) D4 B' }. E: l
    # 此时已经是工作日,正常滑窗。结果重设索引,对周末进行向后填充。2 s* d* @' T! s( v1 p* x
    select_bday.rolling('10D').mean().reindex(df.Date.unique()).sort_index().ffill().head()
    ! L3 P& e/ U  d. ?5 P% S% x- C* i
    : f& y0 r! X6 ^# }. ]" kDate# z  x0 F" e8 ^. p8 x! P/ r
    2019-01-01    189.000000% \! h. {4 ^- n! N
    2019-01-02    335.5000000 y; J, b" Q8 ~, ?% a% N
    2019-01-03    520.3333339 P" u% o& X% P8 P# p* }! y! Y
    2019-01-04    527.750000# s2 c0 X+ _: `* [, P9 e$ |
    2019-01-05    527.750000
    ! ]$ j& M2 @. N8 [  N$ d& l: a: K/ S* M: S: O2 o; [
    ————————————————
    8 L/ V& I6 s  B* B) A! ]3 p7 P" w/ ^版权声明:本文为CSDN博主「神洛华」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    9 W! K; e& r5 [5 s原文链接:https://blog.csdn.net/qq_56591814/article/details/1266339136 B* x+ N( B8 I  D6 g; M6 \3 E

    ; N5 ]% J2 e) r" B8 n1 r# t( Q- \
    & A; R) N! M4 K6 Q
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
    您需要登录后才可以回帖 登录 | 注册地址

    qq
    收缩
    • 电话咨询

    • 04714969085
    fastpost

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

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

    蒙公网安备 15010502000194号

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

    GMT+8, 2026-6-15 22:02 , Processed in 0.597569 second(s), 51 queries .

    回顶部