QQ登录

只需要一步,快速开始

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

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

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

5273

主题

82

听众

17万

积分

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

    [LV.4]偶尔看看III

    网络挑战赛参赛者

    网络挑战赛参赛者

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

    群组2018美赛大象算法课程

    群组2018美赛护航培训课程

    群组2019年 数学中国站长建

    群组2019年数据分析师课程

    群组2018年大象老师国赛优

    跳转到指定楼层
    1#
    发表于 2022-9-5 16:11 |只看该作者 |倒序浏览
    |招呼Ta 关注Ta

    0 Q" g* w# @' I2 }' t8 w7 p7 N
    0 R5 |' R+ D. Y/ h1 ^! k  c* a  ^+ `2 F; U: S. m8 w% c
    文章目录
    * v6 F, _. m, x第八章 文本数据
    8 v" t. G3 h' E! R; O. ?. a8.1 str对象
    0 f* d9 L; L# ?. O1 _4 M3 H7 C" ~8.1.1 str对象的设计意图
    4 ?% t- z  \7 V) f0 h5 f8.1.3 string类型; H+ b. x+ g7 A9 ^. k& B, E
    8.2 正则表达式基础
    # S3 W/ A0 X5 t8.2.1 . 一般字符的匹配" Y5 G' y/ H7 y. _- ^9 [& u1 F" k
    8.2.2 元字符基础4 I" E. j1 L$ H# ?0 |6 e& s
    8.2.3 简写字符集( ~5 e: H2 W4 N% e! H# O8 T
    8.3 文本处理的五类操作
    : _" ]' g& W- W- f/ T) h8.3.1 `str.split `拆分
    / h; H: k+ @8 B: I! S8.3.2 `str.join` 或 `str.cat `合并" s2 l2 X& k* F% o0 ]
    8.3.3 匹配
    5 g6 e. z- L: X4 a# A/ p8.3.5 提取( E* W) d$ _: d/ h# t2 j$ |( `
    8.4、常用字符串函数
    + E* p4 U# t/ j  j4 t/ E; S( E/ J9 |8.4.1 字母型函数6 u( d) _( o: n# W: G4 b& ~& \9 R
    8.4.2 数值型函数
    , Q. a7 f. c$ J8 f; d1 J  ~, v8.4.3 统计型函数; {1 P& n' D3 l
    8.4.4 格式型函数) ~" Z+ c3 R$ X0 K
    8.5 练习
    $ M8 l. n) \" C! n9 g( {Ex1:房屋信息数据集
    7 C3 q) C* _* hEx2:《权力的游戏》剧本数据集" A' R1 O3 u8 Z
    第九章 分类数据
    7 o- ^) j1 D! |. m9 b( \8 Q9.1 cat对象: ?5 S3 C' R) \+ m0 J# T9 A
    9.1.1 cat对象的属性; _; D: Q5 M$ f. e" e6 w- n- X
    9.1.2 类别的增加、删除和修改
    1 [0 M! b) O* O- `5 O! [2 s9.2 有序分类
    * p2 M% [# j1 t; ]# `* I9 N, b# c* @6 X9.2.1 序的建立1 Z# R$ |0 n  V6 k) u! M, ]
    9.2.2 排序和比较
    4 h6 `4 u% \, Z2 p6 f9.3 区间类别
    , W9 u$ s$ \2 Y9.3.1 利用cut和qcut进行区间构造
    2 v0 y* z, E2 m1 Q( m! e9.3.2 一般区间的构造8 [- V9 P* H. x+ h
    9.3.3 区间的属性与方法2 M8 z8 R8 W/ O5 Z- W+ W
    9.4 练习
    ( `0 y+ C' p0 {- \( X8 tEx1: 统计未出现的类别
    8 a( t9 f' M& h+ d' ]  j3 ]Ex2: 钻石数据集
    ' I/ X9 @3 }; D第十章 时序数据4 T1 ]% S9 l0 ]+ Q
    10.1 时序中的基本对象4 t* L7 L8 w; z( E+ y$ A" n: E
    10.2 时间戳
    4 c$ D) \. v; P+ p& V, G10.2.1 Timestamp的构造与属性
    8 n5 N5 ^' T2 X0 T9 L- N1 z7 c10.2.2 Datetime序列的生成
    ( |: d, t7 ~/ p- [, C10.2.3 dt对象! p* ]! m& ?! S; X/ Z2 z! G
    10.2.4 时间戳的切片与索引
    , s4 V: ?$ M: x* O; \1 R10.3 时间差  k# {/ n) J- T/ y8 }" E# E
    10.3.1 Timedelta的生成, G$ j; K% d3 h: v& [: q8 e
    10.2.2 Timedelta的运算  C" w6 ~, ]/ U
    10.4 日期偏置$ Z3 v5 w7 J6 C! y; R: R
    10.4.1 Offset对象1 u' f: V6 y3 K& [" h5 o" f9 T! ^, c
    10.4.2 偏置字符串
    ! T% ]' f( W6 G; u' ~- h6 E, M$ T- H10.5、时序中的滑窗与分组
    & s: x( e' p' O; }3 r, P10.5.1 滑动窗口
    - {' ^: G3 J7 j  ^6 I7 _0 C10.5.2 重采样9 h. ]9 \0 \8 B/ N, O
    10.6 练习
    8 g4 S/ Q5 _; Q% f1 _% rEx1:太阳辐射数据集
    4 p; L  X. H& c9 o/ hEx2:水果销量数据集+ @  o; h/ P, t; a- ?6 s
      课程资料《pandas数据处理与分析》、github地址、讲解视频、习题参考答案 、pandas官网
    4 o' e2 \, x: d9 i传送门:
    % o' g; N) e& k; I$ W. y4 F% z# f1 ~+ e
    datawhale8月组队学习《pandas数据处理与分析》(上)(基础、索引、分组)
    * |5 R: T, k3 u; idatawhale8月组队学习《pandas数据处理与分析》(中)(变形、连接、缺失数据), \/ F: t" p  e7 _( d3 \- d
    第八章 文本数据2 J4 V" y  x9 V! J
    8.1 str对象
    6 o" y) U  \/ v" y) p8.1.1 str对象的设计意图
      Z# d2 X. D) J( P7 J: F  str 对象是定义在 Index 或 Series上的属性,专门用于处理每个元素的文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其 str 对象。在Python标准库中也有 str 模块,为了使用上的便利,在 pandas 的50个 str 对象方法中,有31个是和标准库中的 str 模块方法同名且功能一致,例如字母转为大写的操作:
    8 d7 e1 z+ w- i2 Y1 [7 p$ h4 h) h# z) [+ Z
    var = 'abcd'% k/ ~7 g2 `  k1 O+ k4 @
    str.upper(var) # Python内置str模块. f9 B2 I$ A3 R1 N8 ^2 m0 W3 ^
    Out[4]: 'ABCD'$ U5 c* s3 O: N! `
    ; c* u1 {! G+ a6 q$ u
    s = pd.Series(['abcd', 'efg', 'hi'])
    - I' x) P- y# o+ X+ {  O
    * _% m  t- m" J! b& t; Hs.str
    8 r4 T9 P) }- h+ NOut[6]: <pandas.core.strings.accessor.StringMethods at 0x2b796892d60>
    % T0 b5 l/ v0 a3 e5 S6 X
    6 A: R& ]/ T0 _! }4 Z. H3 Gs.str.upper() # pandas中str对象上的upper方法8 y4 c" s, J: H- F( E- E8 E
    Out[7]: - E8 x& w( q: d7 U4 h9 [- k
    0    ABCD
    $ @( V( q, {& ~8 d" Y- s0 k& f- M1     EFG
    : x+ r6 N0 \* P; W: [; y5 f( M2      HI" U& U* R6 U* z3 g+ s
    dtype: object
    ( i1 V, _5 ]2 t0 N- U1+ P  b- x! L- @, m8 r& V
    2
    + P9 ]5 X3 U3 m, E3
    ( o, T9 ~( R7 h! p5 Q4: s  r: T& O: v3 g9 |
    51 P. O3 {0 Y; [
    6
    1 z" t& S! [, x  g9 s3 q( |! |72 X) B( x5 j; ]* v
    8
    7 y' l& K+ i" W' X& N# l# y. h9; d' N, |0 H' e* Q  @" L& \
    10
    , \# w" C2 Z* x  n6 p" }6 D6 Z1 f11
    0 t4 ^" e* Z  i) B' `8 J% u12! o: q3 K2 J9 ^
    131 `1 i$ [4 a% W6 z# q) H
    14  v5 M( C$ T% l
    158 h- b  g# W7 S! D0 a0 i/ |
    8.1.2 []索引器
    8 j& l1 i1 R2 u- j  对于 str 对象而言,可理解为其对字符串进行了序列化的操作,例如在一般的字符串中,通过 [] 可以取出某个位置的元素,同时也能通过切片得到子串。/ F- T8 Q0 E. ?6 E! j* N& w
      pandas中过对 str 对象使用 [] 索引器,可以完成完全一致的功能,并且如果超出范围则返回缺失值:
      s* Z* d, v0 P  K5 j( w8 Y6 @
    3 o& _- T( Y3 v0 [) C. d6 C5 {s.str[0]2 r5 [3 L0 a4 o/ J
    Out[10]: 2 J. v4 s% I- r1 @4 F' x% ~
    0    a
    $ h, p4 K. M. ]- @8 K7 D1    e4 S9 i* A& I( W6 V& G
    2    h3 C: ]  c% |4 D3 o( P$ s9 \1 ^  d
    dtype: object
    + h7 R/ P  e* c$ a
    & ]1 ^) b1 V  J3 X$ k: _9 S$ ds.str[-1: 0: -2]! [* H0 g4 `, e6 C7 A! K5 e
    Out[11]: # S, `/ s: y. w7 l
    0    db
    9 p( _" {. M6 d1     g
    " |& [5 E* i. r9 V/ R' A: j# a+ i2     i: G$ R+ j$ D0 k! |3 A
    dtype: object
    0 E3 K- E$ p' {5 p
    2 e8 m" h; Y; x+ W- P# m8 }s.str[2]
    / m0 i9 x* d6 g3 y9 w( c- Z& B6 EOut[12]:
    - I8 P8 j2 ^# Y* I& k5 {. r% u0      c/ C1 m5 \; q, b2 w: p, C8 h* u4 ]# x
    1      g4 s4 M, |  G: X' m
    2    NaN
    ! k" a4 b+ B8 t7 Z5 [dtype: object4 L. A: {' J3 s# L; i9 e" R

    ( }) I$ G* p8 t, d% c4 ~1- }, W! M7 w4 e- v8 J+ a
    2
    8 y9 g0 k" d0 f1 a! G1 m2 Q3
    & i3 e3 L  w! _4- Y: l* W! j" X( c# q0 }
    58 w8 z: e. s7 q
    60 V0 W  q- Y' r% |# Z3 m, T
    7+ b9 W+ f. c7 ?7 ^3 l
    8
    9 U$ U! G" e; l1 w; u3 s" ~9
    & M+ I0 Z- Z+ ?4 c4 f0 X10
    - }! [5 P2 B8 f. s/ w117 b/ d2 c7 p0 l2 O* H, ?
    12
    ! v4 D' k) v6 K. r! H13& w' K$ ^6 G& j" K
    14
    ! n6 E+ I5 ]4 x' b- A15- f$ c+ e  g6 g; t+ c% Q
    16: ?/ Z- Y1 g- w1 t
    17
    9 ?) n& M  z$ m, D8 N& p2 L0 o2 W% J5 }18* q8 G. j; M3 `
    19
    ( L+ r0 s, i5 m. p; `4 m3 l) y" Z204 g9 Q) A* I; h5 f) @' a4 Y; T
    import numpy as np# {. I8 p' x; O3 ?
    import pandas as pd
    + {  j% {$ r4 v+ h9 y4 D% h
      n8 P5 j9 D0 ]+ z7 cs = pd.Series(['abcd', 'efg', 'hi'])
    0 V. F  r  \8 h1 O' a' os.str[0]% h, a: V1 e5 }& A
    1
    ! w1 Q4 [2 m5 Y2( v; i3 z& M7 q8 q- ~
    3
    6 v: P$ V* f. V' r+ u9 R- P( L9 L7 u; Q4
    ! r6 d3 u# W! o% W/ {5+ s& S) p% T6 S  m
    0    a2 V; @+ ^% e: S6 i; l
    1    e+ R8 k5 I6 c6 n2 h
    2    h# N$ s3 n( \& u0 O" u/ s
    dtype: object6 N8 t* H0 m+ Y' Y7 j- y2 v* R0 k
    1
    : v: ?- r+ ~! _% d0 ^. D2
    / F1 e! m6 d0 q/ O: {3
    9 `# y- B3 q# ]3 |4 c5 w4 o2 W; U4, A9 R* j! f" S, j3 ?
    8.1.3 string类型
    ! ?. }4 F% x3 x3 I8 v! Z  q  在上一章提到,从 pandas 的 1.0.0 版本开始,引入了 string 类型,其引入的动机在于:原来所有的字符串类型都会以 object 类型的 Series 进行存储,但 object 类型只应当存储混合类型,例如同时存储浮点、字符串、字典、列表、自定义类型等,因此字符串有必要同数值型或 category 一样,具有自己的数据存储类型,从而引入了 string 类型。* p! R& K- v% y2 m1 I+ i& I. m4 U
      总体上说,绝大多数对于 object 和 string 类型的序列使用 str 对象方法产生的结果是一致,但是在下面提到的两点上有较大差异:  D1 p% X. n8 P: W/ ^9 I

    : w' U' K1 K) x8 w  m8 S" w$ F. g二者对于某些对象的 str 序列化方法不同。
    ; b# O* L" d9 r3 V# P可迭代(Iterable)对象包括但不限于字符串、字典、列表。对于一个可迭代对象, string 类型和 object 类型对它们的序列化方式不同,序列化后str对象返回结果也可能不同。例如:
    4 x( t) D" j" `s = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a', 'b'], 0.5, 'my_string'])
    ( B! X: A. _  _8 l4 us
    1 H0 T2 G/ O+ t9 c6 |1 W0 l1
    8 i& a+ r+ U9 G5 q. Y2 `/ K: V2
    - r$ t: u1 T  {, m2 k, S3 Y0    {1: 'temp_1', 2: 'temp_2'}
    5 S" ^! Z" c8 V& [% z' f- |1                        [a, b]9 u$ l  \! R, A5 M" u+ v
    2                           0.5
    % m7 @% `! N) C. R# m3                     my_string
    + L$ f6 T: i. F  L; s, D! R' M& Ldtype: object0 p! ?# ^! ^; w& ^! _1 z1 X1 n
    1
    7 U+ g1 J* _2 \3 Z6 {; ]* ~2
    6 b) v' S0 F5 z35 v% ?& u- \# {% Q
    4
    3 w1 i9 U7 P: c/ y( O5
      J9 F2 ^. f9 K" p% Rs.str[1] # 对每个元素取[1]的操作
    ( Y& e2 w$ r& r4 \. m6 [1
    ; l# P0 J" ~$ o' P. |: M0    temp_1
    0 J$ H3 S% ]% |1         b
    . e0 q% n5 Z% Y9 G3 L/ L5 r2       NaN
    & B7 M: ?% r3 O- E) ?3         y
    - F5 F. J" `1 [& W  mdtype: object1 I/ C- N0 R7 F4 F) w% U/ S
    1+ p  h* d: N, T2 w# \5 V3 B/ ?
    29 I( ?; f  d5 u' T0 y& i' A4 S
    35 X. {1 i6 x, v9 @( z- i
    4
    5 G8 C1 K7 w% C3 r7 \2 t5" W5 D3 T" i5 n4 w% N- D) {
    s.astype('string').str[1]$ p# l2 ~" Q* x( ^
    1" m* q% H2 i( y' k1 Q& G/ ?
    0    11 N- _- e1 u0 s5 A  k
    1    '# a- I3 I2 C& s/ O5 e; @
    2    .- g& b  b- u& q" b1 p! v+ G* ]
    3    y; S  B* \) W) @+ D) q( c1 U( w
    dtype: string
    9 b% p" T% m9 D# Q7 m8 t1! |$ ^6 s& k+ r6 Z7 ~
    2( y! h% J6 o. m/ f7 ]% E" h' E
    3
    6 U  C* f' }! \' D4& W( o( U9 _% L7 h
    51 P( I& V8 H/ n  B; q
    除了最后一个字符串元素,前三个元素返回的值都不同,其原因在于:+ x+ L0 l6 i, [/ t2 b; b

    6 j6 K; R% d+ Y当序列类型为 object 时,是对于每一个元素进行 [] 索引,因此对于字典而言,返回temp_1字符串,对于列表则返回第二个值,而第三个为不可迭代对象,返回缺失值,第四个是对字符串进行 [] 索引。
    0 b, N" z5 q; p& ~8 ?; @. Mstring 类型的 str 对象先把整个元素转为字面意义的字符串,例如对于列表而言,第一个元素即 “{”,而对于最后一个字符串元素而言,恰好转化前后的表示方法一致,因此结果和 object 类型一致。
    9 o- W0 ?+ `3 N7 A' F. ostring 类型是 Nullable 类型,但 object 不是
    # M# h; `( V& Q5 W3 X9 ]  I  这意味着 string 类型的序列,如果调用的 str 方法返回值为整数 Series 和布尔 Series 时,其分别对应的 dtype 是 Int 和 boolean 的 Nullable 类型,而 object 类型则会分别返回 int/float 和 bool/object ,不过这取决于缺失值的存在与否。8 k2 m4 X4 Z+ c7 L
      同时,字符串的比较操作,也具有相似的特性, string 返回 Nullable 类型,但 object 不会。0 R6 s3 g- i. U* g0 ~; s2 K
    s = pd.Series(['a'])
    7 B  w6 J$ h3 [' h
    9 O8 s; B; N  e4 e& |0 C, ps.str.len()
    ) T: f" M3 G) i4 `: qOut[17]:
    ' n8 A% h1 l2 h( i0    1& Q. y/ l9 {* x
    dtype: int64
    : D, Z* R2 c! ]% O
    6 N% ^0 Q3 |( v  V8 Q4 K6 Rs.astype('string').str.len(); _& ?8 o1 {# q. ^; O0 y
    Out[18]: . X# [, `% B; u! J. l; ^
    0    1
    5 y; b/ g0 H5 k& G8 xdtype: Int64  X' Y: s. n% ?1 j5 k. a# ^

    % ?- q7 G1 c$ @  t' ]$ k7 @5 [8 q  Ms == 'a'
    ; Y4 b" X: c2 p4 x" LOut[19]: ! |8 s! N; d, j5 F: R- i* b
    0    True
    2 m0 v  Y( ]( ]. m" Bdtype: bool
    ! D, ]. E$ ?/ r! G, h
    ! w5 l6 O1 Q) @7 _5 N/ c- ]s.astype('string') == 'a'% F1 W5 w+ M/ B
    Out[20]: + ]! v! C7 ~# ?4 E+ d9 Y  v7 {
    0    True
    7 B  _% h$ J2 s- l1 O" Fdtype: boolean
    9 q% s" r( ]2 a, T* h9 u* G4 J8 U8 K
    s = pd.Series(['a', np.nan]) # 带有缺失值
    - [# s' Q. m1 p% c; Z( I& R2 A5 z2 Z5 @
    s.str.len()
    $ Z  M9 |( [  v: G7 M2 i7 v, BOut[22]:
    0 W& R- C2 e  C  ~; @0 U0    1.07 V6 H! h3 i" x; ?+ q( J
    1    NaN
    1 r) {! ]5 o0 ~) G( ~& e% K0 @7 X  ndtype: float64
    3 s3 t" K+ t: Z/ v6 Q  ]5 l& b- V4 b* ?
    8 T* `" {: w. w. g# b9 ss.astype('string').str.len()
    5 S' S1 w) X" U( }* x" _4 c$ YOut[23]:
    ) K5 I1 N9 m' `3 Z0       1
    6 s+ Z& E7 c1 _( l! e, s1    <NA>
    $ M5 u7 c! H( ^* P( Q- _0 Fdtype: Int64
    , ]! m# W' P% H: Z; K- u( M) l! `6 t9 W" i8 F( L3 m
    s == 'a'
    & V) d" k6 Z! s4 G/ p$ r( nOut[24]:
      G, N+ S2 X5 W' e0     True" L" H3 w  T1 ?/ [# A, z/ _5 U
    1    False) k# ]+ _) h' {1 o+ c# X
    dtype: bool
    % T) A/ @0 v$ ^1 R5 q
    % a$ O6 C! B% Q8 Ds.astype('string') == 'a'
    6 [0 k5 A! W9 ]/ K' a5 G8 w  XOut[25]:   ^% E, p8 h3 e/ ~, m
    0    True0 ?# a" R+ z) X" `- `
    1    <NA>1 i3 r* i; a$ |, _$ a, R
    dtype: boolean
    9 f+ F4 i0 C: l! O: _
      h3 r8 K' w$ V) }, I( c( W' s1, L0 N0 s5 f* d% a& @
    2
    2 C5 b5 M3 n% {/ x+ D6 u' F! S3
    ! @9 i4 _" g- ?; z4 i0 j: k4
    1 U9 v$ G$ L5 H+ \( [, ]( [5* W* X- d: H! L2 l
    6# h# F, W4 b5 O+ E
    7  Y/ S/ X9 [% d5 p+ r
    8
    + j2 w* }  g9 |: l% d9
    0 ]6 A3 M& z6 ^2 V6 F10, I$ \" \6 x, p: O$ k- P8 }: w, J
    11
    6 Z: V6 [7 M, F) K2 l12
    * C, G! S- f2 R, u13" u2 V, g1 ~- t
    142 D( ?1 N& T% }* y$ B4 j1 {, B
    152 I. q% n. G0 a  ?7 L+ R
    16
    , Z3 A5 {2 M# T$ P2 H17
    , g: \$ x& I( f% w7 u# v18
    & o* d8 D/ S8 N4 `$ H193 ]" a9 Q' {8 t" Y! b
    20
    5 o% L3 X  E, E5 N8 p* I21
    + ?) v+ Y7 T# s5 _3 h22+ Z) Z( k: R& R5 s" y7 N+ A
    23
    + K) m) U5 Y$ ?) Y% p4 w1 j5 e24( t* G; Q. N3 A# R" d
    25
    / J' i& c* Z" I$ Y  d# t26# R4 t. ^0 D4 x6 w* J
    276 N, }/ r8 |5 L+ t7 g, {; M: l+ ~
    286 j; K2 X7 S2 E
    29
    . E1 l3 `: I2 t: w303 R2 v. I. W# o% C6 g& A0 a) R) A
    31  j. r. {% y9 w8 \* @# G
    32
    8 _( e: g4 H6 U5 z5 ?1 Z33& Y4 f# Q  I, Y4 L
    343 |4 g4 O( v6 V  W" I0 ?, s
    35* [% |' b- w7 D, z, |$ K$ I
    36: _  z3 e; y! d
    378 L* l( {* i6 R7 ?% \3 t$ L
    384 `4 }& K4 t- D& d6 C/ w0 d
    394 O; B7 F* z( G8 A; S" k0 A
    40
    5 D6 x& D' A  n- ^+ o41
    7 H" g8 t, C/ ?428 m5 t+ E  D8 l8 E
    43! f+ {- \: r4 e4 S% V
    44
      ]+ h) O* j6 U  D/ j4 {0 k45
    " a: Y; O+ a1 w" F+ Z) h46; B- B# ]: O6 K$ x: n- j1 _
    47
    . ]3 y" [2 ^; j( B) \9 M  对于全体元素为数值类型的序列,即使其类型为 object 或者 category 也不允许直接使用 str 属性。如果需要把数字当成 string 类型处理,可以使用 astype 强制转换为 string 类型的 Series :
    $ B7 h8 a6 X- g8 r7 ^( ?6 j* t+ g. \4 O0 i( y
    s = pd.Series([12, 345, 6789])
    2 U+ U2 c$ h& l; L! X& I5 v
    7 J' b  `3 P% F5 o; _, bs.astype('string').str[1]. `# M3 O0 A' _! D
    Out[27]:
    + V' Q) z7 A; h' ~, Q0    2# T) {+ \. C* T2 P
    1    4
    2 Z: o/ L% W  O2 f2    7
    4 M6 C8 _; k) g9 |; K& j. f& f8 Q; Ldtype: string) X: X/ @7 _" C% [
    1
    3 z0 g' u6 e7 d2+ s+ n& B% n& l5 Z) E1 N
    3) Z. ^1 }3 C) h% S* V+ ^
    4& t$ V2 T, e. e6 d5 n8 U4 b) {" l. G, f
    55 j/ E$ q$ `; }6 o  f2 L
    6
      L7 n7 d$ c! D3 q1 r: `# ^7
    " D8 E; @/ t) Y! a  i- ]8) w2 E7 S1 ?% W1 `5 _
    8.2 正则表达式基础5 H$ m9 T5 L' u
    这一节的两个表格来自于 learn-regex-zh 这个关于正则表达式项目,其使用 MIT 开源许可协议。这里只是介绍正则表达式的基本用法,需要系统学习的读者可参考《Python3 正则表达式》,或者《 正则表达式必知必会 》这本书
    9 ?1 X& r3 G7 t  R
    ! Q1 h- `2 d( q; _8.2.1 . 一般字符的匹配7 [1 E; K$ H& ]1 d+ `% C8 Y
    正则表达式是一种按照某种正则模式,从左到右匹配字符串中内容的一种工具。对于一般的字符而言,它可以找到其所在的位置,这里为了演示便利,使用了 python 中 re 模块的 findall 函数来匹配所有出现过但不重叠的模式,第一个参数是正则表达式,第二个参数是待匹配的字符串。例如,在下面的字符串中找出 apple :
    9 V1 ~! G4 v! b. w# C/ `5 G% l
    import re
    ' L0 }2 {- c2 v* a: ?. v& Q
    - I) `9 [8 S  C" Ere.findall(r'Apple', 'Apple! This Is an Apple!') # 字符串从左到右依次匹配& N. w3 i( p0 ^; e! v
    Out[29]: ['Apple', 'Apple']
    2 M* [, _, e$ }3 l2 C1( S  F* {6 `+ w! [
    2# L6 o5 D+ n8 P
    3# k/ |, S$ R1 F) y- v0 D( i
    4
      S1 {/ N5 R, H3 o$ S$ G! I2 j8.2.2 元字符基础
    ' G( }* w) ^9 S( l" j) P1 w元字符        描述( H6 K! T4 D! a5 m
    .        匹配除换行符以外的任意字符* b5 w/ F8 V( q4 L
    [ ]        字符类,匹配方括号中包含的任意字符
    7 s1 F# W. u1 P. Q; v3 Z# \7 G[^ ]        否定字符类,匹配方括号中不包含的任意字符
    4 L- R: Q+ [% B; Q; A0 k" O*        匹配前面的子表达式零次或多次
    % W8 L# w0 b1 Q( s5 o$ m+ a6 j+        匹配前面的子表达式一次或多次。比如r’d+'就是匹配数字串,r’d’就是匹配单个数字
    . [  S" e; D3 z3 Y4 g?        匹配前面的子表达式零次或一次,非贪婪方式
    * e2 O1 k7 Q2 Y/ \8 o6 b{n,m}        花括号,匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
    5 w. n0 l5 p0 m" G2 R7 h& R. C5 L(xyz)        字符组,按照确切的顺序匹配字符xyz
    + `- F/ Z$ i7 A# C# M3 s- v|        分支结构,匹配符号之前的字符或后面的字符
    : d' J) z% m) I% c1 D2 L5 G' ^\        转义符,它可以还原元字符原来的含义1 u( `7 h: U+ t$ f1 W
    ^        匹配行的开始
    ' g! `: D% X# I0 o2 B9 S' @  Q4 c$        匹配行的结束5 U$ k( O* k5 |" C$ w/ l
    import re
    ) p- X0 q( h/ L7 R- Gre.findall(r'.', 'abc')
    6 Z+ H/ B! @) ?Out[30]: ['a', 'b', 'c']
    . L* X/ v6 D( Z& Z% ?8 H# y
    4 x4 a5 K( G1 U- u! s# I/ y, ere.findall(r'[ac]', 'abc') # []中有的子串都匹配* y* u2 s5 q2 O- B
    Out[31]: ['a', 'c']
    # t4 x6 V# v% f0 g# x* k- [4 m- @8 a; L' v
    re.findall(r'[^ac]', 'abc') # S# Y' m7 q; A$ J0 f# N5 y) U( _1 Q; N
    Out[32]: ['b']
    & M/ M/ x  K9 ^& R) y' l! j
    $ B: P& _/ j0 k. Jre.findall(r'[ab]{2}', 'aaaabbbb') # {n}指匹配n次, O7 [* {, a8 c/ l3 k
    Out[33]: ['aa', 'aa', 'bb', 'bb']+ f) F* s$ N0 @! {8 r
    5 |9 \' S; {1 A9 q0 g% B
    re.findall(r'aaa|bbc|ca', 'aacabbcbbc') # 匹配前面的或者后面的字符串6 X) y+ a& U! S/ {- U9 k
    Out[34]: ['ca', 'bbc', 'bbc']7 k, W) [* L% i; q4 u3 o
    / h$ v- R& C  K3 A3 F( @) n0 b
    # 上面的元字符都有特殊含义,要匹配其本来的意思就得用\进行转义。
    5 x4 ^% B& U- [# S  F' r, L"""2 ]$ R( p9 y9 t' z' Z
    1. ?匹配的是前一个字符,即被转义的\,所以|前面的内容就是匹配a\或者a,但是结果里面没有a\,相当于只能匹配a。
    # a$ V/ W7 P. l/ C, M2. |右边是a\*,转义之后匹配a*,对于竖线而言左边优先级高于右边
    8 S: F5 W& }- Y( K& T3. 然后看目标字符串aa?a*a,第一个a匹配左边,第二个a匹配左边,第三个a虽然后面有*,
    / f& @, U$ [" }/ e& r但是左边优先级高, 还是匹配左边,剩下一个a还是左边,所以结果是四个a
    ! d5 M: N5 a+ r6 c5 f; p"""
    / }4 J0 N& K8 o) O8 i# |* ?$ r8 e7 |# }# ]& b1 e# x( |
    re.findall(r'a\\?|a\*', 'aa?a*a')   # 第二次先匹配到a,就不会匹配a?。a*同理。1 o+ J9 [, m5 z# }
    Out[35]: ['a', 'a', 'a', 'a']6 o/ w* p0 p* j7 q# C
    7 Z5 ?# b4 V& N3 P9 n& j
    # 这里匹配不到是因为目标串'aa\a*a'中,\a是python的转义字符(\a\b\t\n等),所以匹配不到。6 ^& G* g7 f# |
    # 如果是'aa\s*a'之内非python的转义字符,或者'aa\\s*a',或者r'aa\\s*a'就可以匹配到\字符。
    ' n7 z1 y$ I7 M/ B) Jre.findall(r'\\', 'aa\a*a') 3 e' W- }% T3 z& E1 v5 ~# F
    []
    3 e! \7 }& x) Z$ @, X5 O- X
    2 C: y& o' T4 n% V* e% bre.findall(r'a?.', 'abaacadaae')6 I* P1 X* b$ u& ~3 Y
    Out[36]: ['ab', 'aa', 'c', 'ad', 'aa', 'e']
    5 K2 c0 I, D9 C7 f3 r3 M% f1 P/ I
    ' {/ k  R) P& m! q  F, n% u) n5 [re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10') # 多个匹配模式,返回元组列表
    & X  J) D( u9 L+ w[('width', '20'), ('height', '10')]( v6 y6 j! Y% E5 o7 ]7 W3 c$ V

    / `4 g* }- r) L; M7 Z% N1
    3 k$ C7 U4 _+ x& }24 q- x# X- @3 _0 ~! ?: u
    3( w# W9 W0 w0 \# V6 r; ~
    4" X  q" f3 l7 \, F3 J$ x( @: i3 {
    5
    8 @' ^7 M8 _; a( {9 i! [6# {" Q; c3 a7 M- A8 }# B
    7* U! Z# `. s8 |2 k# c: v; K7 l, x
    8
    , a3 j- z/ \" N- w: }  k7 n9
      \1 k) l2 C3 T10
    ( L4 s1 J: K2 W$ P11
    1 ^8 ?$ I, O+ v# Q, L+ `  ^- [12
    + j0 u8 L/ v' B% u8 T9 @* H6 n0 R13" _. \  U7 {) w# J0 M. [
    14
    , G  Y. {# g- P; j9 P6 E$ F: _6 y# O151 Y; a0 ?& w2 [( Q
    16* s7 A4 X  h, z0 K' a) N
    17
    $ T2 ?- ~. ]( G( m18. Q) v9 ~8 t! G! [$ U& h
    199 d% a7 f4 h6 W( N* F4 Y1 T+ {6 ?
    20, k: ]7 O- y: ^: X) C- K8 c2 [/ d
    21+ b; b9 _) `4 r2 n# \9 t
    22
    ( J; }' j% M8 P' b# u6 P23! b8 f0 `* M# K; j: Z# a
    24
    # X( c2 T& G2 \3 U* c, @, @) Q25. F) r3 p: @  ~( K: t$ p- q, p
    26+ I2 k4 E2 e2 G2 }: ?" \, v
    27
    ' N5 G" n3 K$ I# l, p7 Y+ }28: e2 y  C& d5 _1 O
    29
    1 z9 i+ ]; r( C$ ^; _30, F9 c" h% A' Q; H* _
    31( D8 l. p6 j* o1 Z( r8 {. K9 r* E
    32, m" M! ^& o8 A: p
    33
    6 r' ]! D! T1 A2 f2 P34% S5 t/ e8 R- x, [( E; C% m7 [
    35
    3 ^: k! n3 [: t' V3 `36& g$ X, B( O8 i- ?9 t; c) ]
    37# \  d, O  F# H, c
    8.2.3 简写字符集
    & A, e) _! c1 |8 j  @4 j8 i) P则表达式中还有一类简写字符集,其等价于一组字符的集合:  o6 H8 n0 e* l" [# K

    : w5 O. B$ O2 t/ G# H2 ]3 R; h2 g简写        描述
    % y+ C! `5 h  ^: W! a5 E\w        匹配所有字母、数字、下划线: [a-zA-Z0-9_]6 A0 n6 p, Y* E6 t' w  b! Z
    \W        匹配非字母和数字的字符: [^\w]+ K6 o: \- I3 o
    \d        匹配数字: [0-9]- l2 x$ H% O) k* _/ {- S; l6 B, q6 C
    \D        匹配非数字: [^\d]
    & T( A0 r+ F9 E/ E  M, H\s        匹配空格符: [\t\n\f\r\p{Z}]
    ; M* L! I! |9 h" i0 A; W# v\S        匹配非空格符: [^\s]1 Z2 X5 L" d( K5 o7 [$ q
    \B        匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。8 s1 H0 L- F$ |" ]" {* z6 k' G
    re.findall(r'.s', 'Apple! This Is an Apple!')) q  f; |4 ^, g' O1 h: J
    Out[37]: ['is', 'Is']
    % O* [( y% ]7 X+ `2 o" B: e! H: T- v5 X( |2 f7 w! W
    re.findall(r'\w{2}', '09 8? 7w c_ 9q p@') # 匹配任意数字字母下划线的组合,但必须是两次
    : a; X1 [- Q# q6 r8 fOut[38]: ['09', '7w', 'c_', '9q']
    % Z9 a' R: A  z9 k, i; Z  |) k& n8 S! z
    re.findall(r'\w\W\B', '09 8? 7w c_ 9q p@') # 匹配的是两个字符串,前一个是任意数字字母下划线(\W),后一个不是(\W): a! Z  l8 t6 ?& G2 e1 r6 x
    Out[39]: ['8?', 'p@']
    1 V( R$ p' r0 i9 t& M- `: N/ m* ~$ }7 d, X5 B5 F! Y  U
    re.findall(r'.\s.', 'Constant dropping wears the stone.')
    4 T5 x3 B3 D: |' b* f! }Out[40]: ['t d', 'g w', 's t', 'e s']9 [% u2 J/ X1 U  R+ [4 `

    6 `, I" r3 [7 L' }2 s8 r* v  n$ ?re.findall(r'上海市(.{2,3}区)(.{2,3}路)(\d+号)',
    3 O- P* C' r& G# X+ S           '上海市黄浦区方浜中路249号 上海市宝山区密山路5号')
    1 i( q  n" O! N: ~: |, F* p' x* S- s
    Out[41]: [('黄浦区', '方浜中路', '249号'), ('宝山区', '密山路', '5号')]
    ! J& a% c5 \& f; X+ b, a; C3 r0 k! J% @4 U/ N9 x
    1
    / @3 Y  Q: f) {3 k* E2, y) o1 [* J* y2 I$ A
    34 E/ x5 N; d7 }7 f+ j7 J6 `% ?# d3 z
    4
    6 @6 s* Z0 a: x9 ~54 I2 p* W! a: c4 {6 K4 G
    6' K& o1 k/ @" A$ y" }
    7
    7 a% x& P3 p/ g7 z; \7 T& o8
    " E) q0 {, \. a: [' S7 p9
    % F5 ^! I! |' \7 S# f+ b1 D10
    3 _; H. d$ z( R1 m& h3 v111 r+ K+ [! ^* L& _' k" w& S
    12
    0 B+ H' q% J$ G! O7 u  ^13- P% Q) c0 t4 T9 I  T  s1 i
    14
    ; o; |' }2 I9 n) H- ~. l7 ?15
    8 u% z  ]" s3 q16  C; F4 _1 ~9 y# M( |/ f
    8.3 文本处理的五类操作# E- H. }1 P& c' g5 r' b
    8.3.1 str.split 拆分) u1 m( m+ _1 Q! F8 v2 G
      str.split 能够把字符串的列进行拆分,其中第一个参数为正则表达式,可选参数包括从左到右的最大拆分次数 n ,是否展开为多个列 expand 。1 C% s* I$ A* P! ]9 M5 `+ @

    7 ?/ d8 A- k/ H0 g# \s = pd.Series(['上海市黄浦区方浜中路249号',7 K- D9 @( x5 O( X
                '上海市宝山区密山路5号'])
    - G1 q/ P- i  N' D2 T* O" p1 R3 I, x" Z, \2 {1 d1 ]9 \7 _. j
    , C8 b* J% v0 V8 A9 Z6 f
    s.str.split('[市区路]') # 每条结果为一行,相当于Series
    , ^" s# d& x& Y# E' F) C, zOut[43]: # i4 h) c# _- ?5 s$ R
    0    [上海, 黄浦, 方浜中, 249号]" T# X% J/ x, _( n) U# P' ?
    1       [上海, 宝山, 密山, 5号]
    + U- X& E3 y: X% i7 ~  Vdtype: object0 l7 s; V1 J+ \
    # G/ `" a( e4 K+ n3 h
    s.str.split('[市区路]', n=2, expand=True) # 结果分成多个列展示,结果相当于DataFrame
    - A1 l! M) W' H& N2 [; _, ~! jOut[44]: . h& X( U: ?. K, a& P" Q, r. D1 l
        0   1         2
    , o4 Z8 }# q; j0 F0  上海  黄浦  方浜中路249号3 c5 y8 p% g! W; H
    1  上海  宝山     密山路5号
    5 G/ w2 N9 ^. r+ D: v1( a2 P) n9 e# \, _0 D8 Z
    2' U6 j* }+ P  J
    3
    ! `0 M1 G' R& C2 L: h+ j# R0 F; v4) C, }, S( R/ m) {
    5
    # K3 r, \  ]9 {9 m6+ Z# t% i" M1 ^' t
    7
    # b, ]( G2 T7 T0 g- c( C: {8
    4 s4 r7 x5 X" ~* }9
    % v4 e* i' ]" \# a5 d10
    6 Y3 E; n0 v0 C/ d, r# p  ~11
    6 d  M5 `0 S! U0 l12& J* ?5 v& D7 V; m! t
    135 E3 ~7 y; ~2 C
    14, V* t; K0 F& E0 b! N
    155 g. A! |- A' u5 C
      类似的函数是 str.rsplit ,其区别在于使用 n 参数的时候是从右到左限制最大拆分次数。但是当前版本下 rsplit 因为 bug 而无法使用正则表达式进行分割:: N, q: T( A5 ~7 H

    2 J. _. C# F1 L- P$ G* [s.str.rsplit('[市区路]', n=2, expand=True)0 S. w/ X( z, T8 x- R# a
    Out[45]:
    1 G) F- C8 L, p8 J  w* a- T& Z                07 h/ s) c  R# ~+ ~/ G
    0  上海市黄浦区方浜中路249号) L8 n8 j# @* d  e' o2 c! q
    1     上海市宝山区密山路5号
    9 n$ c2 t" _, _5 m5 q0 b( Y1
    % W/ q- X; O' {; }; T0 g) }8 C2
    " H9 I. T, h) Y3
    - R& N0 t) y6 p# h7 E49 e. x9 ~2 L- q  U2 m* B
    5  }& w& C, H# G2 w1 B4 e/ \
    8.3.2 str.join 或 str.cat 合并2 C0 o8 G+ i- q7 k, R8 E# U: G
    str.join 表示用某个连接符把 Series 中的字符串列表连接起来,如果列表中出现了非字符串元素则返回缺失值。
    & V/ F5 P" ]  O7 o9 c$ ustr.cat 用于合并两个序列,主要参数为:: A% t7 z) ]$ o* F% O( ]% m
    sep:连接符、
    8 b  |5 A5 |" pjoin:连接形式默认为以索引为键的左连接
    + l4 y0 N6 ?. F! Kna_rep:缺失值替代符号
    . O  e" C( e$ {" |; ys = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']])
    % e, I+ {0 |4 B: Ds.str.join('-')
    : @- U! d2 A1 O7 V' iOut[47]: % I+ x8 F  Z! g$ E( Y1 e8 A
    0    a-b
    : a2 N4 X  ^& y$ ]8 p& K0 ^1    NaN
    ( j/ K% p: e' b8 Y: h* P2    NaN1 E6 @0 k0 z0 h
    dtype: object6 J8 z* g  @. c& V: p. U  ~
    10 [# r, f  j* T9 Y. T/ \0 C
    2
    4 z* {" z! f, f# m3! P) l1 t8 w' f7 ]  b
    4, W& E' |( \) B6 F" v( b. C7 {- w
    5
    2 S: [7 c. R0 i" m3 r+ G4 f60 G# Y/ t3 n  Z7 `. P( r% D# G
    7
    9 F% P! Y# v& g! R  m. z' ss1 = pd.Series(['a','b'])4 @5 W. A0 K( m0 G& o9 _- \
    s2 = pd.Series(['cat','dog'])9 ^9 J( P# t: S2 K' ]! X8 j& d. \, P1 F
    s1.str.cat(s2,sep='-')
    ' D, ?4 S0 d* s7 e7 F; y7 xOut[50]: & c+ Y# h$ j8 k, a
    0    a-cat
    ; J( \: n1 q, }) u  v1    b-dog
    $ |- S* K4 n( d& r" Xdtype: object
    5 W0 R' I* {2 ]7 q; g6 R4 x  E  o
    & [% e9 x, _/ x+ p) \& C1 os2.index = [1, 2]
    . r* k) s+ ~: o/ q0 ss1.str.cat(s2, sep='-', na_rep='?', join='outer')
    ( M- V6 h3 A, B6 ZOut[52]:
    ' Q0 u! c9 |' J1 }6 }6 X# B! {0      a-?
    1 o/ Z/ J. c, d3 x3 x9 a/ F7 y1    b-cat
    3 r6 E5 R2 d- t/ p* P& K  y2    ?-dog
      y% V) V6 H* S- \3 e( Idtype: object
    * f; Q/ S( Y1 L' i) H+ ]17 |: }: M8 G; K' L' N& q4 h1 v/ W
    2) U6 V  _+ D( e, m/ U6 ~9 F
    3; f( l8 o4 t2 Z  d
    4# f( k) f3 j$ f
    5. V5 K0 f2 q- e5 f
    6( h( A: @3 g# {- [. S
    7
    ( k& v5 B( H& q. p# P8 b88 J% g% j) [1 O  Q9 c5 c9 y- `
    9
    7 H* Z( t0 D+ Y6 q8 l104 w  l$ [. k+ Z# x
    11
    ) G) F# i: }3 @12# o9 c( S, @# [
    13
    # S, ~, P( R+ F5 a. t$ z3 x14
    8 O" C! Q: ]8 U15
    + Z& D4 c. U. p! z& _8.3.3 匹配
    $ L7 B4 J1 G9 s' A0 d: ~6 dstr.contains返回了每个字符串是否包含正则模式的布尔序列:) N% M9 X: ^7 a5 [1 o( `
    s = pd.Series(['my cat', 'he is fat', 'railway station'])& r3 f: R& G& g! M! h0 e9 J
    s.str.contains('\s\wat')
    2 G3 [0 C& P! k8 X( T3 h( B1 M2 ]* Q7 j  N
    0     True
    / n, n3 n: J* N8 S" E/ c1     True
    ; F9 B3 N3 o2 {( u+ T2    False: B0 O- r* M& K' u
    dtype: bool9 l* q/ I" Q5 U/ m) R7 v
    1% E1 p. }/ K2 |8 T, ]9 N
    2
    , z3 }# O: A$ w2 F& Y9 u& a3 M) n( N3
    5 f3 x' S0 r" U" z6 P1 C1 {4
    , k. n; c- @7 T  A2 [! J5
    - h: v2 T0 w) D# @67 e! O0 r& D8 ]6 ?1 ], b1 ]
    7
    ; T- k# W- E" O+ g6 S. Z% p$ sstr.startswith和str.endswith返回了每个字符串以给定模式为开始和结束的布尔序列,它们都不支持正则表达式:1 ~- ?9 y# Q1 m( Z- a& \4 f0 ~# M  G
    s.str.startswith('my')
    3 w. U7 f% b: m  b0 T) q% h" l& e" l- P* j+ z6 r4 h
    0     True
    ' L$ }) q! |! a5 f. n+ e$ a1    False
    3 u: t* y6 `0 K9 [0 q$ j2    False$ M. t& P8 w& c1 e
    dtype: bool
    ( A6 Q  `7 |% k  p: p! W1. G( b9 N+ y. @+ ?0 Q- u
    2' F/ ~8 K/ ?+ j" `% C8 @
    3. [( _0 b" p1 k: E
    4* o- M, ^# r' T2 U0 F4 b' Q2 {
    5
    & e% j- Z  Q( Y& J65 Z6 S* b& H$ u+ H
    s.str.endswith('t'), L% i4 A* V4 R3 u* i

    9 R" m% h: [: D0     True
      M. X' x, c$ }" W# {, C8 O1     True
    + \4 c$ R' F& r+ ~: M: ]2    False9 z8 b2 w# L; q
    dtype: bool
    ) N* M- H8 e! Z/ u7 j1
    1 C+ @5 i% f7 [3 g! d8 \% {2; L( m1 s/ u! C& t# P  ~
    3
    . A8 L) w# m2 f" n  H3 t' c# X45 A! l8 W: I- \: x
    50 H8 O3 g& T% W5 m. V
    6
    ) u! S2 q; q' \5 f2 q" `6 estr.match可以用正则表达式来检测开始或结束字符串的模式,其返回了每个字符串起始处是否符合给定正则模式的布尔序列。当然,这些也能通过在str.contains的正则中使用^和$来实现。(貌似没有python里的search方法)- C6 I/ f# K3 j6 n" `, c2 L
    s.str.match('m|h')
    " S8 H7 W$ F% K9 ps.str.contains('^[m|h]') # 二者等价
    ( E( h" A/ O% y
    2 d4 j  V9 b* E0     True
    3 P2 E9 F" E) R3 b1 v4 L1     True" c4 w2 x$ h6 i9 Y, F3 b' ~
    2    False/ {  |( }# y+ F& I( ]; C! O9 a
    dtype: bool# r6 l, Y6 C) y) K3 G3 t; u3 m
    1
    5 u0 y, ^/ H0 t+ v2+ }7 n! E* }  i4 B
    3: c  R) t: _4 I9 j6 y* B2 x! x* J
    4
    - {4 F  S- A' L0 A50 R4 ~7 r: `' S
    6
    / r1 {- V6 O% P: x" x0 c' B7. K! g8 H- W; H5 b0 S3 |
    s.str[::-1].str.match('ta[f|g]|n') # 反转后匹配
    0 `( o7 A) L, H1 Ds.str.contains('[f|g]at|n$')       # 二者等价' P  p2 d/ M* d) K& {8 f* W  S

    " O9 O6 f2 N. A! B0    False9 a, p3 T  r* _. N* X& P# e% `
    1     True& C$ O: s9 F2 ~9 o0 j( z
    2     True( Z3 L3 x# N, K6 I. p
    dtype: bool- [/ y: ~3 H) J; q0 p) O
    1
    3 E" n/ R9 c/ Z2$ D0 M% L4 F' W& ?
    3
    5 N; w+ G1 M9 F( U- \4
    $ H. u& E6 C! y- _5, H$ v  v9 L' ?% L7 P
    6& v7 ~) W0 T: K; [: _& R6 s
    7
    & E4 o. `" Y1 x0 N4 Xstr.find与str.rfind返回索引的匹配函数,其分别返回从左到右和从右到左第一次匹配的位置的索引,未找到则返回-1。需要注意的是这两个函数不支持正则匹配,只能用于字符子串的匹配:
    " m8 K9 U$ f8 _  {0 Q$ O- hs = pd.Series(['This is an apple. That is not an apple.'])
    - L0 {: H2 Y* B
    9 u- V+ i1 a1 K9 u+ Ss.str.find('apple')) x5 }; s2 `5 |
    Out[62]: + G, i; v, ^" ~1 T9 H7 K0 |
    0    11% e. |8 E0 k: q1 c* l# A
    dtype: int641 ~9 `, ]0 d- M8 R
    4 E  h8 H0 k! D( `- ?
    s.str.rfind('apple')
    0 E+ x+ d4 k& u* a# ^4 k5 n; @Out[63]: + E, q7 P9 B+ K7 K' ~' l" F9 ~
    0    33
    1 t% J$ n7 n/ |4 Y% v% ~4 u- ~dtype: int64
    + n5 W  L! O3 v/ ?1
    9 Y6 c  Z7 p# O8 z- q; P2' P: w) \& K, m: S4 k
    3
    + a* f% B6 Q( r2 S! r3 B8 s. s; ?5 R4) s. n3 {- C) Z4 F, r8 ^
    5
    , Z9 x7 l) x& x/ i: x* i2 ?& I; y6
      b. L% E6 |! f5 v7 a7$ q& w* M" M8 e. F% d' O% q/ M8 p
    8! J- S3 _3 u/ x  b
    98 W  T. d+ ~2 T2 c3 H4 C  B
    10# F2 R3 A) G5 n2 H) x8 U" s
    11
    * _3 `) E5 Q$ r/ t: D% e) ~( `4 A- F替换6 U: }! g. h& r, t& y9 K
    str.replace和replace并不是一个函数,在使用字符串替换时应当使用前者。& x1 ]) |, L/ _3 @: |7 w, J. z% r
    s = pd.Series(['a_1_b','c_?']); v5 u2 e$ U/ `) f" p; [2 F
    # regex默认为True,表示是正则模式,否则第一个参数内容表示是单纯的字符串,也就是匹配字符串\d|\?1 E% z/ D, o& `7 }% L1 n6 H3 D  q
    s.str.replace('\d|\?', 'new', regex=True) , E6 x, M4 J2 c# N

    0 k* r/ Y: C; C5 s( n$ E  V0    a_new_b+ t4 z$ l, e' K: y( v2 O% _$ {
    1      c_new8 D9 \- f$ n3 x- @# Y" \& I
    dtype: object
    ) O9 F) E3 N. ?" p* R1
    - ]8 v+ T2 Q, _2
    * M  T: v4 k7 d# B3
    8 ]$ F8 x* N- F45 m) v, Y  M6 z) S3 m+ r. O( J
    51 u- _5 O2 V7 }5 N/ y0 C; f
    6+ Q0 C% ^3 S: a  Z
    7
    . T3 z+ ~" }2 q& w6 ~3 r- O# ^- Q  当需要对不同部分进行有差别的替换时,可以利用子组的方法,并且此时可以通过传入自定义的替换函数来分别进行处理,注意group(k)代表匹配到的第k个子组(圆括号之间的内容):
    . \" H- z: B. X) Y3 p" ]! s* X! [& [/ S  N
    s = pd.Series(['上海市黄浦区方浜中路249号',
    , @- f4 g) y8 V6 c                '上海市宝山区密山路5号',
    7 f; X$ I! w# F* G% F                '北京市昌平区北农路2号'])
    3 L% [* D0 z1 B! f2 y( o- q$ ^pat = '(\w+市)(\w+区)(\w+路)(\d+号)'/ X# ~8 d/ f0 \5 I7 F
    city = {'上海市': 'Shanghai', '北京市': 'Beijing'}
    : r! D! ]! Y9 P0 Idistrict = {'昌平区': 'CP District',
    ; y) M) s# x; }8 R, i/ y; e* S            '黄浦区': 'HP District',
    4 t5 }( {  T5 [" }$ ?$ @: S% C            '宝山区': 'BS District'}( o9 x+ _- b8 p, T5 G
    road = {'方浜中路': 'Mid Fangbin Road',
    / t+ @9 Y- j5 ^; _        '密山路': 'Mishan Road',  Z6 Y7 D/ O4 c. X3 C
            '北农路': 'Beinong Road'}. w$ w$ N$ J" W% L  O
    def my_func(m):6 C) ^& g5 Z$ {% d0 r7 E
        str_city = city[m.group(1)]4 R/ E5 [, g. `7 _6 j: ~9 b! I
        str_district = district[m.group(2)]# `) ?* g0 Y% }5 U7 w9 G' J; Z
        str_road = road[m.group(3)]
    ! `7 u  F2 R3 S2 j    str_no = 'No. ' + m.group(4)[:-1]: I5 F# \. e( e" z1 h
        return ' '.join([str_city,
    7 @1 [2 C, E, \0 e1 S* t' V                     str_district,
    ' m! |  b6 ]2 S' Y                     str_road,4 G$ R. m0 f& |( f2 i$ v
                         str_no])
    8 a# n; U+ r, d7 @" {+ j8 \s.str.replace(pat, my_func, regex=True)
    5 e9 p2 B' H" s4 W
    7 w( A# ~3 q0 w1 h& S. U- [; P, Y1& r: S. d% Z' U; }" _! T
    2
    ( ?# [. A# u: j! i; h7 Q( M% Y3! B6 K' W& s4 ^$ I, [! e5 U% D0 x/ M
    44 E) y9 n- C' j
    5
    ! B0 o+ e5 d% ]- y8 v5 T/ s) e6" M8 T3 q# A. V2 T
    7# Q0 M: Y) q) C) t
    8
    ' \5 l/ l0 d: S8 L5 m9" @" ~7 g  T8 Y; {& y1 a
    10
    ; s5 s) L* A4 z+ O, d11* ]& Y) T7 u' ?) K+ `- U: @4 G
    12; B' @9 R6 s+ B4 ?! H
    13$ a% G$ @. M7 \8 V, U- K5 {7 {
    14
    7 }& j% Q% I4 u0 \8 P& F  Y' j15
    ' }( C' z$ n. C' L+ _: K2 L16- w* T4 i$ h' a+ a+ r& h( k5 e
    17
    * }& }2 h8 U, N2 P; B18/ s! t7 w: O- `" i& v3 k" ]
    194 }/ s5 R+ U* w: B
    20* U( N( n( J; T5 j( d
    21
    1 K% {6 C( G- |5 w: P, s( W0    Shanghai HP District Mid Fangbin Road No. 2496 X( f8 d6 ^. N" Z9 L
    1           Shanghai BS District Mishan Road No. 5& W/ i4 k) ]+ B! [; F# h+ {/ O
    2           Beijing CP District Beinong Road No. 2" n  j2 H3 d7 k6 F! D: Z; m+ d. ]
    dtype: object/ S4 V6 z6 R) I- l% ]( J
    1
    * l+ ^: x" H, |' q# _- A2
    " i, Y* y# N  {4 |' k! q37 C- d- |+ q+ ]5 ^* ^
    4# O+ B( ~: g. }' \
    这里的数字标识并不直观,可以使用命名子组更加清晰地写出子组代表的含义:
    7 x* o$ D, a' W; s1 c1 W; M; }6 N( P, L/ t/ |9 k1 g
    # 将各个子组进行命名2 u8 }7 o, J. J7 }0 t4 D, C) \
    pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)', Y) q5 D3 }: U$ H# I6 Z( M
    def my_func(m):) N$ R- ~0 p. w9 t" e! C; i
        str_city = city[m.group('市名')]! m) ?8 T- G1 w& W8 d+ k$ Y
        str_district = district[m.group('区名')]
    & q) w5 p2 j. v' @    str_road = road[m.group('路名')]
    6 ^& a. q: c% f7 t$ Z3 P; t    str_no = 'No. ' + m.group('编号')[:-1]
    $ z6 L! e+ P; {    return ' '.join([str_city,1 _0 O8 C3 q# I+ r
                         str_district,
    ( B  Z* d, g5 d                     str_road,: A7 l& b. G: n, @9 r2 d
                         str_no])
    ; E4 @( ^# t4 e. u7 `$ qs.str.replace(pat, my_func, regex=True)8 B3 h0 Z7 `: w, b& H& n5 c
    1
    4 j( O" n1 c- M/ o+ o4 {2+ R/ s, p' `' t0 v7 o4 m8 F! I
    3( A& S! i& W8 |
    43 b( i: q: p1 L
    5. K+ x, U* A* h% y- r
    6
    & c6 D6 ^, m* R9 m0 u75 r- z% N. W, D
    85 M, _. U, z/ x/ ]0 P
    9
    4 i9 W2 T% P6 |# m3 Q8 S! P10
    / I  N7 M7 m4 O6 ~* U/ d4 q5 F11& @! R$ E4 }. G# Z$ v: r
    12
    % V7 c' ]; U3 G3 h! M0    Shanghai HP District Mid Fangbin Road No. 249
    4 t  _; a$ ^" J! `8 W# n# m' _6 `5 Q1           Shanghai BS District Mishan Road No. 56 Y6 N1 D# D  i" f
    2           Beijing CP District Beinong Road No. 2
    ' A0 X' M% D0 y9 U- vdtype: object
    . I1 F! y% S, ~; i; B, p1
    2 x2 k  Z7 E! N2 x2 s9 I+ C2
    0 c. w: Q" A7 E7 a3
    ; c( R8 H7 x. @4# w/ Q$ z$ m. i, E( M2 e
      这里虽然看起来有些繁杂,但是实际数据处理中对应的替换,一般都会通过代码来获取数据从而构造字典映射,在具体写法上会简洁的多。
    + T; }! X8 _( B' E! _: m+ u9 H8 L. g2 f' C9 ~; P4 D' G
    8.3.5 提取
    ! k4 S5 g& j/ B5 J$ e+ estr.extract进行提取:提取既可以认为是一种返回具体元素值(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的str.split例子中会把分隔符去除,这并不是用户想要的效果,这时候就可以用str.extract进行提取:/ Z1 v! j$ i9 `- J3 R
    s.str.split('[市区路]')( G+ w# C# V& o# b
    Out[43]:
    0 T( W$ u4 z, _8 \: j! e  o0    [上海, 黄浦, 方浜中, 249号]
    6 u. t6 _3 V& v* W3 X& o1       [上海, 宝山, 密山, 5号]
    0 `4 ?0 g4 F8 o# C1 Jdtype: object% F( F" M1 k" |' @4 y
    " W, O# y2 R, Y! n4 b" h
    pat = '(\w+市)(\w+区)(\w+路)(\d+号)'
    0 a; F* S; a$ v- D# T9 |s.str.extract(pat)
    0 Y0 ?2 Z1 A  Q; rOut[78]:) w: o; p- w5 a- D; C* f2 G4 f
        0    1     2     3
    4 j, E  I: d4 l; O3 w0  上海市  黄浦区  方浜中路  249号
    5 K5 R" \4 q+ I! u7 p1 [1  上海市  宝山区   密山路    5号
    - \/ J6 J6 M$ H8 h( t2  北京市  昌平区   北农路    2号9 b' E0 Z/ m# b: F! v  v) _5 k
    1/ z3 ]0 t8 J* ^! M7 ?7 E  P$ z
    2
    " A) \. e; z( B6 i3) w0 C  U1 ~, V; z
    4
    6 D7 o/ `/ `, W+ Q# ?, q50 p; `" j% f5 E2 D" ~
    65 j9 O( |, w# R3 E
    7
    2 _: `' b  O  c( l8& G( G# X. O3 Z5 g+ ]6 S& T* z
    9
    % o8 q* \( s+ B) k10# L; O" {+ j# m" k' S
    11
    9 U9 K! q! n- d6 p8 D12/ X- Y, S2 o1 O' J
    13# d" A& J/ U/ e2 z
    通过子组的命名,可以直接对新生成DataFrame的列命名:9 K& u: `8 h/ P% l( c
    5 \' D$ S# e. v0 ^
    pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'
    9 u3 `- Y& O+ X, s9 f+ E" Ds.str.extract(pat)' x% Z: C! U" {" A$ C
    Out[79]:
    * N6 i& U, Q$ C( c    市名   区名    路名    编号; j0 B+ |" x/ k+ I- B. H: I6 c1 |0 _
    0  上海市  黄浦区  方浜中路  249号  @2 h* Y( E% {# ~2 ~! [9 {
    1  上海市  宝山区   密山路    5号
    " s7 Y* s, P+ `, A/ v5 ^2  北京市  昌平区   北农路    2号
    % X& U5 J! s% i11 F0 ]2 ?6 U: ^& R) O5 p0 z
    21 Z5 B7 \# g+ k. t
    3( x/ T$ v1 p. h
    4
    7 i8 E6 Q2 Y6 w# F9 w- E. P4 n59 ]+ F9 W$ \/ m
    6
    6 }1 k( O% o+ [- g+ e9 K2 f1 p& ~7
    5 t+ m# ~2 g) o0 x6 a. qstr.extractall:不同于str.extract只匹配一次,它会把所有符合条件的模式全部匹配出来,如果存在多个结果,则以多级索引的方式存储:7 w* J5 O# U$ D
    s = pd.Series(['A135T15,A26S5','B674S2,B25T6'], index = ['my_A','my_B'])2 D3 S' W3 ?4 o# Y) o% q
    pat = '[A|B](\d+)[T|S](\d+)'1 M- s# r3 Q: C/ S7 l
    s.str.extractall(pat)( Y3 U' [  P- i3 u( t9 _9 C
    Out[83]:
    % Q, J; E1 u+ ^# t; j8 s( y       0   1: b6 \; D( K4 E6 U' P6 s
         match         
    2 `2 Z: v! k* fmy_A 0      135  15& S3 v) J* i, Z  ~( t
         1       26   5
    ) h' R$ F8 R* \my_B 0      674   2
    " s! a  M+ Q" r! G; M0 z: v     1       25   6
    , M! ^0 ]) n9 X17 [* N, h+ K3 A
    21 E1 Y0 z7 }) d
    3
    % H2 i9 w/ L* F4
    ) x( L- I; f* F$ ]0 H5
    , i3 i( n) S4 a5 m$ V( k6
    - X3 ]3 l8 v( y% A! m# l7
    : @7 @" i+ V! z" M) j! Q8
    ( E/ _3 @! F8 W6 E9
    - ?, @' j! a* ]3 s& f2 S9 Z0 c104 c3 n$ B4 }# R8 r- a! O; o0 F7 G
    pat_with_name = '[A|B](?P<name1>\d+)[T|S](?P<name2>\d+)'
    ) H% y% @3 r/ v7 N) o% ns.str.extractall(pat_with_name)
    ' V) s' `" f# F( u! e+ b( YOut[84]: 7 R9 G$ \0 s- j, Q2 }
               name1 name2' h6 m3 z/ l- E# }: ?7 V/ t
         match            
    7 T8 Q( |9 p7 D1 G0 ?my_A 0       135    15
    # ~  [( S9 \3 I! m# _* Z/ ^) ^9 ]2 @0 p     1        26     5
    ) E# ~7 Z" n& y( G/ Xmy_B 0       674     2
    ) o$ c) Y8 ], Y9 e) h) u     1        25     6
    * p; P% c7 D6 T* G: h7 {- L- h1( j4 s. V( m. S: Z% b4 M
    2
    . P( V# d1 e9 Y& ~8 ~$ \3
    & [* d- z( Z  ?9 r4' I% c( p; `; ]6 y- z' g
    5, h) c1 r) r  v5 L  m* \! y
    6
    2 g7 B9 P) A  F2 D) n70 q4 o) r3 `5 a+ c0 W# I
    8
      `5 `2 L1 L/ d9
    % \$ }/ u- d3 ~  estr.findall:功能类似于str.extractall,区别在于前者把结果存入列表中,而后者处理为多级索引,每个行只对应一组匹配,而不是把所有匹配组合构成列表。
    , \, ~6 v2 Z! bs.str.findall(pat). H& `0 L8 O* g$ [: F9 h8 {  \# m! Z
    1$ a6 |. L; R. J
    my_A    [(135, 15), (26, 5)]' C0 C0 S: g% I* L# H' `
    my_B     [(674, 2), (25, 6)]
    3 Q- m) i' B' U2 A$ e! D) E8 odtype: object) L2 }6 O+ Y9 R
    1
    + F) v2 T9 Q& j5 L0 r2 o+ ~2! ?2 z% S2 G' C; n; ^- Q
    3/ f4 l! F8 m* Q& h% ^6 ]
    8.4、常用字符串函数
    * G2 ~6 m( b* Z6 c) m  除了上述介绍的五类字符串操作有关的函数之外,str对象上还定义了一些实用的其他方法,在此进行介绍。
    ) g: W+ |- n1 }+ U) f6 d' ^2 N2 A' |$ R- r) D! k- X" _
    8.4.1 字母型函数" W+ t) F8 Z+ y7 k. X4 X& R
      upper, lower, title, capitalize, swapcase这五个函数主要用于字母的大小写转化,从下面的例子中就容易领会其功能:# _- B, a" N; V6 J8 W; v- Q9 E9 T
    9 v5 R% [6 `/ o3 u
    s = pd.Series(['lower', 'CAPITALS', 'this is a sentence', 'SwApCaSe'])
    , H7 @+ e9 [; {! [* y( E1 }
    % J& J) V3 P8 M/ `( z% Vs.str.upper()3 q' _* G: ~/ t# @+ P, j# Z4 W* H- u
    Out[87]: + F) b8 _; S3 R. `) A
    0                 LOWER$ P1 Q0 Z" m0 K4 g( _
    1              CAPITALS
    " `& I7 c1 l; V2    THIS IS A SENTENCE4 E. c$ K* q. {" {7 Y& r1 `! L
    3              SWAPCASE
    1 E* w2 B" R+ U: w- zdtype: object
    ) [5 ~; S; n  g% m0 |, P) o  n+ L
    s.str.lower(); j7 n* h- I. |. r& q
    Out[88]:
    % G: ~1 K0 h, h" f( {0 h* S" \& S0                 lower  o; i7 q; L. T+ _$ p
    1              capitals
    # j: Q- V" P, x# g* E2    this is a sentence
    . b& n) T+ H3 D9 |- H- d' A3              swapcase3 i3 R4 T8 T3 f% S( D& Y% I8 Q+ R
    dtype: object( T) R9 `% B/ `$ `, b/ x& K
    8 s8 I2 i% v4 c; `* [' O
    s.str.title()  # 首字母大写
    + P( X! ]) n3 D$ U2 A. FOut[89]: 2 ]# G( M% [1 E- [4 h
    0                 Lower
    ; @! S; T5 `0 ^6 M% ]1              Capitals
    5 F- |6 I3 Y/ Z9 W2 V' v2    This Is A Sentence  A* n8 f# a3 E) F# b8 [6 T. E. [
    3              Swapcase" T, _: p) ^, {' s5 P. Q% ?+ T
    dtype: object
    + r0 s4 `) b, {5 T5 A
    ) ]  Y( d4 U. G9 _3 `) rs.str.capitalize()  # 句首大写
    ; z4 ]$ R; [6 I) i1 YOut[90]:
    & R0 r9 n6 Q# F1 z& e; ~2 N3 X* ^5 o! E0                 Lower& Y) m, f3 D% B' Z8 P
    1              Capitals
    ' `# ?0 U* n) f8 `% C) O4 s2    This is a sentence, T' ]- T$ D- A/ ?% v$ Z
    3              Swapcase
    8 Z' T$ q; K  ^# h. x- Kdtype: object
    5 l0 i: l+ _! H9 q/ D
    + v, J) s; R9 O  |  r; Y5 K; Js.str.swapcase() # 将大写转换为小写,将小写转换为大写。% q, t" F  X1 X$ }) k/ `
    Out[91]:
    5 |( n6 \9 v+ Y1 S4 d$ c; \  y0 F$ d0                 LOWER
    1 U* S" w% r8 K2 F" B+ a. K1              capitals0 M5 i3 |) ]: g# q4 B8 X6 d. p6 [
    2    THIS IS A SENTENCE
    + a; m( E5 X" A& i& l3              sWaPcAsE
    4 I4 H2 M/ n4 ~& V/ \3 K  R7 Zdtype: object
    , ]! t8 ^( X5 t: P  @. c" T1 ?2 h$ K) _0 X! q1 H% Y6 p7 Q  r
    s.str.casefold()  # 去除字符串中所有大小写区别  J: K  @: o' t& Q7 x( H* N2 I8 \3 g& ]

      ~( P& |* n. l2 M" ?: P6 M4 T0                 lower# H4 b% y- b" c
    1              capitals
    1 a& {5 x, v+ I2    this is a sentence5 A* L% U" ^9 l5 F
    3              swapcase
    ; T& ], c7 ^2 M1 s7 N) z# _
    ! Z6 U6 P4 y9 H1 Y0 G15 i! o: y( ^& X8 Q
    21 C) h; z; q" j4 q5 z
    3
    3 {7 A: ]. r  b7 q* @- M7 m" @4  J) Q; Q4 o! U
    5
    2 S8 \* t, U: w. Q/ J6
    , P+ J' ~+ y. S6 f7
    + M& R1 s! J7 b0 S) W4 L8+ W: W; V" D! `4 |& }; w
    9
    7 m' }  B8 o- H7 k) T/ S10
    7 @; J5 J5 P0 ?3 J" n9 ^; |11) N2 G% W- F$ ^1 ]3 s+ m+ Q3 w
    12
    ; `+ e+ u1 i2 ]% m. V13
    4 N+ ?  X' U; E' u7 X14- E2 ^* V4 ~6 u" W+ `. ?2 X
    156 m  C4 W) y4 S  p! t; U
    16
    ) p1 s; M9 M# _" |' E17" ]! g/ ^. p5 Y" j. Z
    18. Y" k1 [: v5 H! @
    198 |* c  h. K2 K
    201 k' _! Q' ~' _. U7 Q
    21
    ! |- x3 M  m' ?224 O7 g  O# y1 c& B# T% M
    23) I1 A# J& @! w$ V* \+ b
    24
    # {$ K" S- b( |  F3 T25
    # Y# U% K6 o# M' q3 y" h0 {; U26
    $ G. {0 u9 H" y+ N6 @27
    ' Z1 ?7 V( A+ r2 P284 k( l7 N, ~6 Z% r# X7 k/ Z
    291 I! R/ b- N7 s
    308 S& O  m8 X4 n8 Y" B2 t' b
    31
      a, L6 L; \6 S: W6 P7 L6 ^32
    . g# w8 e$ n8 W. L4 w* ~7 ?33
    , O6 n4 p3 n; F" C- \34# D* }! Z7 c4 Q+ m
    358 ^, X1 U+ |9 G9 }1 _( I
    36
    ( @' ]  j$ W( U6 w37
      r( C+ Y; ]: u& i  Y  i. Y7 u38
    ( B3 d6 i+ h' h& b39
    4 m& v0 P+ M: C; D40
    . D7 C' E% E6 p) `( v41$ [8 f% G: F7 M' M4 I- Y. y
    42
    6 w7 Q% ^1 \9 z$ }43, g0 o6 Z8 b; b) T
    44% F: i1 X. p$ F0 `: @
    45$ _2 e) P" }' h3 y9 }
    46# E* `4 O8 l5 {  t6 L1 w4 X
    47" v" N0 M: o5 D/ S
    487 B; m# P* K: P! ]6 I& z2 K
    8.4.2 数值型函数; M4 v' x8 B5 N0 r9 L- k
      这里着重需要介绍的是pd.to_numeric方法,它虽然不是str对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。其主要参数包括:- C$ P% v/ T+ {, t. _
    ; \4 _. n- S. q8 @. G0 X
    errors:非数值的处理模式。对于不能转换为数值的有三种errors选项:& C8 A4 T( W. s2 u9 l
    raise:直接报错,默认选项$ K" f- P  T) }2 e" Z3 U4 l/ d
    coerce:设为缺失值
    + @0 k- o1 ?# {2 o0 w- V( \ignore:保持原来的字符串。5 T( R7 o- L7 H" {4 Z
    downcast:转换类型,转成 ‘integer’, ‘signed’, ‘unsigned’, 或 ‘float’的最小dtype。比如可以转成float32就不会转成float64。2 t2 d; q4 u9 E: ^$ S
    s = pd.Series(['1', '2.2', '2e', '??', '-2.1', '0'])8 h" ?- S8 e2 N; L& x

    ; M' b6 u$ F" s) `pd.to_numeric(s, errors='ignore'), V1 ~7 r- F) _
    Out[93]:
    8 A6 X; ?/ x! u9 H0       1
    # r; g/ {9 h  D% H$ y; P! A1     2.2
    % ?1 @; J& Z  g, t2      2e2 c/ H1 i9 ]6 u; W! i; \; k
    3      ??
    , ?! _+ v4 @/ z# ]3 w4    -2.1
    9 u5 j$ i3 U$ Y5       01 M% S. ]; I0 ?. l. ?
    dtype: object
    + N8 T) s' S+ A- {/ {* \* F: V& `7 u
    pd.to_numeric(s, errors='coerce')6 m- f8 a( P. R) b7 H3 B. H6 D8 d
    Out[94]:
    / `+ Z' K# ]. n! p3 L. `/ R0    1.06 Z" ^" @0 `; o2 g) I
    1    2.2
    " k  R, n/ {' ^2    NaN
    0 {7 d; I' v( N8 y7 f) b& O6 I3    NaN
    0 v2 I5 E' G" G1 s9 j, T4   -2.1
    . @/ j. a( p4 @- e" L5    0.02 Z& B  \/ J0 {6 \& n
    dtype: float64
    $ _# e( W- `# w+ ?3 T6 \, \) P* {! Y
    1" R  v' _. A4 W: m/ c" ~
    2, G- J# _" ~: N9 R* |
    39 d* X$ G4 Q& w* ?
    4! ?/ G6 _  f  C: D' o7 G4 h! P
    58 |) |) E- K+ O/ Y2 w
    6
    3 X7 H' Q1 j/ W' R7
    1 ?7 S  h3 A* n- E4 [8
    , Z8 M" S: \; M$ A9 S, k9
    ( n5 d) X, y, O0 y10$ B, k: n# O- P
    11
    0 ?) A/ H; z: s$ R+ L, \12
    ! q' p2 H  }7 {+ G  A, I131 \# o9 X, T& R; ?( {
    14# d- y1 B8 X, {: J! b4 R0 G$ ~1 d4 L' _
    15
    : a) Y  d0 a5 t# I16
    $ ^. _* j6 {4 ^3 o2 C4 \/ n178 Y+ V9 r; R3 l% ]/ d( @5 M6 N
    186 M/ h0 y4 L! h4 m* T) v0 I; n
    191 Z8 x, B$ T& j/ _, ]5 e. x
    20
    , d1 j& L) b* C. h0 A21
    ; c' }; f. l) I  g; W  在数据清洗时,可以利用coerce的设定,快速查看非数值型的行:
    : y7 r! N" u& m
    ) T$ o7 V! ^) Z+ qs[pd.to_numeric(s, errors='coerce').isna()]8 }2 N9 D0 w* _! j: D, b
    Out[95]:   ?4 B( M7 W* w/ c8 V& n- m& [
    2    2e
    * y6 f' {4 F/ x' t5 K3    ??* h' b# x4 S# t: t- D# e
    dtype: object/ A+ H& J4 \; T, m, s) a( k5 G
    1. T: D# D5 t  q2 Z2 P
    2" H! g* n. z) z; J8 w- j5 H
    3
    - M4 ]0 S0 ]$ k6 t) n" V+ Y, m$ ~4
      ~: `/ d3 X( X9 h$ z5
    / a* A: S' o) B' `& F8.4.3 统计型函数' u; [- \* T8 ]/ z# V$ A
      count和len的作用分别是返回出现正则模式的次数和字符串的长度:
    % z/ E7 ]6 O5 X7 J6 @5 V# z% D' U, s  C5 {
    s = pd.Series(['cat rat fat at', 'get feed sheet heat'])
    5 C) ~, d( @1 D) Y" x! w& Y) }3 z. L. M
    s.str.count('[r|f]at|ee') # |左右两种子串都匹配了两次
    8 ?$ N1 F' _  U/ j+ ?$ w6 gOut[97]:
    6 w( r$ j" e) u0    23 }3 w, H# L; M) D( R
    1    22 u  V) e1 s$ h- U2 \
    dtype: int64
    9 h4 J6 v8 J  ?" \. b& {: i8 t: j# z+ @$ w9 ~. H
    s.str.len()
    - F% ?, B! x$ G& C' P9 S, iOut[98]:
    9 o5 I' j; M' `1 I% W6 a: Y! a! _0    14
    8 \  M+ H# |) C; |+ o$ e1    195 y) I! L6 w! |+ i0 S1 N  l2 f
    dtype: int64$ o) \0 `8 o& P2 R  b! h
    1! W% X7 C" w; g# K2 b+ X  E2 T4 L
    26 n& {* |8 l3 B! b, q
    3( l; @/ K1 @  }7 k4 f
    4
    ) d9 e( p0 f. `* V) v5
    9 P$ Q+ Y) M* D9 w) h1 [6 s- b6
    + L) i; b3 s" k1 x7
    - K+ A& A! y. p( y5 Y8
    4 x4 Q% b2 s% a! q% M4 R9
    0 q* ^3 h: `9 A5 s3 M) e10
    * l( L, c8 L8 j5 T* @+ [6 Y% O11/ T$ L; X* ~0 L0 O9 Z) C
    12- z1 ]$ n: P( C/ ?3 Z
    13: V0 X5 g; o, c! l5 q0 v
    8.4.4 格式型函数
    4 ]: W7 z7 X& U3 o, z  格式型函数主要分为两类,第一种是除空型,第二种是填充型。其中,第一类函数一共有三种,它们分别是strip, rstrip, lstrip,分别代表去除两侧空格、右侧空格和左侧空格。这些函数在数据清洗时是有用的,特别是列名含有非法空格的时候。  m' a( z7 `4 c5 ~* h2 Z

    & u' n" ^3 v1 M1 F  X( `1 z$ t! Zmy_index = pd.Index([' col1', 'col2 ', ' col3 '])
    + V, o( d8 r5 N7 o+ U& t6 T$ t4 |6 [/ x
    my_index.str.strip().str.len()2 I; A& y7 a) E- Y2 O- `
    Out[100]: Int64Index([4, 4, 4], dtype='int64')
    " `6 A8 y4 r; z* f! C+ y& S1 }, L! y* c4 P, O8 }: |
    my_index.str.rstrip().str.len()& u4 P5 O' \, t+ }: c+ \( M  |4 j
    Out[101]: Int64Index([5, 4, 5], dtype='int64')
    - N" }, _% s' }4 W: v- Q9 p) B. ^+ Q* s0 f3 @9 z
    my_index.str.lstrip().str.len()3 N. C! R$ ?4 A0 D5 ~; s( M
    Out[102]: Int64Index([4, 5, 5], dtype='int64')3 J8 r" T* b7 z4 ]9 z! e1 O6 r
    1
    & N1 g3 y' K+ U; y3 N1 o2
      u  z  o4 n' L' Z6 Z! U' b, L% E35 h; r# b* T( I2 s
    4
    * S! D% u- J4 F9 n+ R; ^5! G3 }- w+ Q6 n( X9 l2 m0 M
    60 d+ [) A" [; A# W1 O) Q
    7, R2 K5 w" n5 N/ K9 Y: ?
    8
    . ]& S7 a3 R7 w- J. E, B. F1 O/ l99 g0 |+ S- x5 V+ M7 R) b; b+ q
    103 K$ S7 ^4 F9 B2 _
      对于填充型函数而言,pad是最灵活的,它可以选定字符串长度、填充的方向和填充内容:2 F  W& e; t' l

    - H8 h: z5 B) C$ N8 Es = pd.Series(['a','b','c'])5 a/ k( O/ Y, {9 |# V
    - \7 W) y- O' M: H1 C- {& n9 R) ^
    s.str.pad(5,'left','*')+ Z! p; v2 U0 {. D. n9 C
    Out[104]:
    " B8 m% {4 _  _6 b0    ****a( Y6 V1 S9 x, r  |' Q
    1    ****b
    4 J) I4 f# ?3 H9 ^8 B7 j2    ****c5 |4 H% k* p" [! D1 F' ~
    dtype: object
    0 G% \- H0 }- w1 t9 S
    / J2 W, T2 g+ _' gs.str.pad(5,'right','*')
    3 P. ^# f1 }; F  O9 P0 fOut[105]: , ^0 {  D% A; ~
    0    a****, m9 [, R! c" B
    1    b****! H% W# J5 c) ?4 b1 w
    2    c****
    # d' P! {" I* T* B4 Q! sdtype: object
    / @" l# T9 H8 b: ]7 \% g2 _
      [+ v( j# {9 m# j; _s.str.pad(5,'both','*')8 W+ C4 ~3 z( z% `+ W) Y
    Out[106]:
    ' \1 w9 k; ]/ \+ j% R! S0    **a**" m) p4 @( c: {% @
    1    **b**
    $ {0 T3 g' m, ~2    **c**$ d+ X& T3 C' f& ^
    dtype: object( b, ^) S* w( N  E6 ?8 |
    ! c% h+ m6 G* \
    1
    9 ^9 A" V- a3 x7 k% E: [, d2
    $ h8 Q8 e" }- c! A3
    5 u5 u& F2 c. e! T" ~4
    ! E6 X9 O7 ?1 [: T: l  ^/ l) ]51 `, M  Z9 p- a+ e% f3 |$ q5 D
    6
    7 F: ]& ^) n! ?7 z7 F) ~7
    . Z! s0 E1 _3 ^: m) ]8$ T% L) Y- X; z! I( `" m0 P# t
    9
    4 l2 W0 ?  g- S: u. C. [10
    2 |* Q6 m) Z4 Q0 L11
    4 @7 n+ X" A8 k% F  _, @. T6 h  I12
    " s6 l9 t# W; Z  b* F. ?139 T3 F# f. X# \- c8 N
    14" R$ _( V' X/ x/ L
    15
    4 f0 Z7 H9 _1 B7 I. y3 k7 R9 b16
    / a. F- U' m  D17
    7 C  B: k  z) }' _* p# [: Y18
    6 y( W' @  u% y' s+ S; C& J19. D5 D% r9 J  I
    20
    & Y+ b' |! P( s' }21/ x. W. ~0 ?/ C
    22! |) ?$ }  e9 u
      上述的三种情况可以分别用rjust, ljust, center来等效完成,需要注意ljust是指右侧填充而不是左侧填充:
    : R9 |0 v7 g, s! x" J3 K
    5 f  a9 l; y% ~' b7 r& z7 qs.str.rjust(5, '*')$ h- l) x8 m& C6 D. `; r7 U
    Out[107]: ' O. A5 Z( ~1 ~0 T1 [
    0    ****a7 A  A% y. J8 v6 d  Z9 o3 r3 b7 ]$ q
    1    ****b+ i) b1 m3 W! P, `- R
    2    ****c. C% x) T5 i3 b) B3 [& U9 I9 w
    dtype: object/ s' f2 `, A0 M) E5 P

    1 T: I' H. \4 ~9 e/ Ls.str.ljust(5, '*')
    $ H3 w0 ?' _( U" g( sOut[108]:
    9 R, M* @6 g* O2 I# `! N+ T+ e: {; @2 b0    a****
    0 K( }) x: p$ q+ S. T  _1    b****+ o  V3 P3 A, @7 H2 z
    2    c****
    # }" ]2 C# i4 Kdtype: object6 s1 }  {# M; i) }% F

    ) _% o  T  O' Bs.str.center(5, '*')* L7 ?) E1 Q" Q9 @
    Out[109]: ; `6 e7 M" v# D
    0    **a**
    4 R( W3 T0 c. x" r" r1    **b**
    3 D, B) Z% M% k. p. m* x9 J2    **c**8 L! `9 q+ u$ Q( g; l6 Y/ ?6 j9 V' L
    dtype: object
    ; e. _# a6 n$ {/ p+ k
    0 h/ O( p- V+ V7 W5 {1
    % P3 @& D" f* K' ?" E+ a6 ?% ^2
    " A5 ]7 h& B" C% D# r33 e6 ]! N: ?7 |, w
    47 m1 y7 e; j" u
    5
    # d8 s0 }9 j4 ~, @- v) A# o0 H6
    9 f# j2 b, U( V" [* r# V+ \7
    % v- M* Y. A( v7 R, E& X$ X) {8
    3 F, V5 X% v: e$ }5 Y+ D93 U: ]* U! v- Y  S- s  T' N% b
    10  M4 j" `4 g' t! G
    11& v1 T" s+ d8 c& u
    12
    5 U% E: S% N. O+ o13
    6 X% N$ z# ?, f) k14- J$ `: W* \5 M: B
    15
    2 j9 o3 _* ^2 D0 d0 S16/ [' W3 e& L; h: l
    17
    7 |- w; x- L( F$ F18
    4 i7 G4 t; l$ }1 k19
    5 J2 Z( F6 @$ w8 p( ?  Z9 ]0 A20
    + b, U# q6 X, M: ]  d% Q! e1 g  在读取excel文件时,经常会出现数字前补0的需求,例如证券代码读入的时候会把"000007"作为数值7来处理,pandas中除了可以使用上面的左侧填充函数进行操作之外,还可用zfill来实现。* N0 E' Q  A, h* G9 ]
    2 p) w% R4 T7 w) q( [: s9 }4 ?
    s = pd.Series([7, 155, 303000]).astype('string')
    $ t4 h- p  [2 m, R# d
    " f& [# X$ Y, v( Ts.str.pad(6,'left','0')9 E) b# o  ?0 ^- H  e2 C
    Out[111]:
    0 C# N& R/ N& k2 c7 X9 N! Z0    000007
    ' l: L6 ^- W0 X, C( Q1    000155. E6 A  }5 V$ M* _- a; E
    2    303000) k- T/ i. V9 l4 H0 `- C
    dtype: string
    5 p. ~+ c% F, Q* p5 X7 x+ h' S) ?8 ]2 W
    s.str.rjust(6,'0')' X% k3 m& n- ?8 ]8 Z' W
    Out[112]: 3 o' o/ B" a% j
    0    000007% ^; p: v9 w( R
    1    0001553 u) D1 ]; F  o0 n( S
    2    303000( ], }8 y' O5 W0 |& v0 T
    dtype: string' Y& I* c* l  w" H
      o  P. r) M* s. r
    s.str.zfill(6)
    & _% {+ h0 s9 E- R" u4 J: F- WOut[113]: : O; u) ?& G; D1 t; E% \: C" \; ~
    0    000007
    . `7 [: b& I1 q1    000155
    & Q/ }9 J9 I. t  h) u0 e2    303000
    , H0 K$ J* o6 e$ }& i0 m! Bdtype: string
    % G3 u8 ~. t7 V% E! ~  l6 l
    7 I/ R& H  x8 e" H) @, U" T  y4 X' e; }11 X8 h% p% Q' ~% L' Z
    2( {5 [4 B( t% Y6 p
    3
    ; h$ R+ x$ h: n5 A" Q! {4
    , F/ i) W5 O. x+ r1 q0 x* [% g57 V, u3 x$ T- Z5 r3 r7 ^; a' ^
    6, d. _) l. S1 p: x- d& ^
    7/ z7 [# s# G1 `' s+ O. b0 t
    8* q7 G, m6 {; R: o1 `2 _  F3 g
    9
    7 \$ n* f4 H# b6 C5 P10
    , F" d% z+ B- F- V( m% v0 H117 |( `' o; _" |
    12; u8 P1 V" i! X, q7 U
    13  K1 J! A% Q" q9 ]4 _, ?8 s
    14
    $ l% [7 H( l& A% z3 h4 E+ H0 f! w/ C15
    7 _. P' H7 p8 w: t+ ]% [165 {! ^+ |7 X" `6 X: V2 @! D& E) F
    17
    $ b1 Z3 I5 L% _18
    1 o, X( H8 B- c( M9 Y# n19
    5 ^6 Z* N% T' b8 i207 E" r8 i% Q6 p# y- y, _
    21
    7 Y4 f( c, f8 o! x/ V6 r% K( }0 ^22
    9 p2 u3 m% {6 p2 ^8.5 练习
    0 [$ m( y7 G4 p8 PEx1:房屋信息数据集
    4 A% y/ i( T6 a5 c现有一份房屋信息数据集如下:% h  n- l6 `5 F, `; H

    + U! H8 [. o, p" Y7 k) i: udf = pd.read_excel('../data/house_info.xls', usecols=['floor','year','area','price'])
    " N9 y$ \2 _8 u; N5 cdf.head(3)
      D( i! m* k8 h$ ~Out[115]:   @3 t5 W! Z6 C" R' q
          floor    year    area price- s8 S% _: i: }4 o2 `
    0   高层(共6层)  1986年建  58.23㎡  155万
    5 v  e$ T3 [3 w4 B3 ^8 W( p: H1  中层(共20层)  2020年建     88㎡  155万: H1 K, s! c6 }  u& t- ?* P& A
    2  低层(共28层)  2010年建  89.33㎡  365万7 m. }3 S! E7 V, X
    1
    8 n! u" a/ }3 P. v' Y4 l2& R& a6 M8 p2 f2 |# d$ Q$ O
    38 x- n+ A8 j% ]6 l, S; m3 v- [
    44 h. g2 Z) @7 m. V' ?$ Q9 x, s7 [
    5
    # g1 B& ]. m4 ]( J) a$ Y6
    / C: X. e' M7 O6 i" l; K  X  A7; ^' C/ _. a" E2 o. B: ^* s
    将year列改为整数年份存储。; J2 q( p& N' H9 A9 d" }: i9 N4 d& _
    将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。& s! \6 R4 h) ]3 i7 u5 e  t3 p5 f
    计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数
    ' i5 ~- Y5 d% H/ M将year列改为整数年份存储。: p- y  e8 t: m/ ?- k
    """
    & F2 s7 \, T2 b整个序列需要先转成Nullable类型的String类型,取出年份,再将年份转为Int64类型。: j6 `. M7 {. ?& N
    注意,转换的类型是Int64不是int,否则报错。即使astype加参数errors='ignore'跳过缺失值,
    8 }7 l: }# o9 }转成int后,序列还有缺失值所以,还是变成了object。
    ! }6 B" T, Q' P, [3 c- \3 ]而整个序列转为Int,就还是Int类型,缺失值变成了 pd.NA 。1 G6 k  z3 w4 F" Y' H+ y
    """
    $ @. Z$ D4 Y& jdf = df.convert_dtypes()
    8 p4 ^4 W+ f- C9 p9 Fdf['year']=df['year'].str.replace('\D','',regex=True).astype('Int64')3 F! |. I5 h2 _3 r; o1 M: e
    df.loc[df.year.notna()]['year'].head()4 M  \6 U) _2 {9 u5 z& Z
    4 x4 Y* T4 m, V% H
    0        1986- L5 u3 `( C: _. d2 ^0 V% g( H# x
    1        20202 Y) {7 h  r7 f$ L% _( `. q
    2        20103 U0 z. H) V0 k; f
    3        2014
    6 Q5 z3 S8 n2 L* e$ ]6 H0 x  Z8 z4        2015
      m- Z' U( a: n) qName: year, Length: 12850, dtype: Int64
    . P8 O8 e5 {  P- ~9 d
    1 s: K6 C: B* A! |14 T5 q1 ], e0 t2 J6 O2 \) O+ k9 }
    2  [) _7 r, L' W* h5 E
    3
    2 g$ V) [/ S- D6 y4
    6 B3 A* R* n5 }, Q" {% i5
    % Y. B3 A% ^/ e0 V! {6% C* V& D$ n" D. q+ {6 e; Q
    7
    # ?7 q5 X9 _, }/ r! ?2 S  A8; t) U# @' U" x' `
    9
    ( G/ }* ?" a  q8 |" o" @10
    3 r7 F) d: y' r3 V/ _8 @" I11
    % w$ {+ W- ^1 N9 t" p% M12
    3 t2 U+ F/ g& p: R: E/ R! T13% y3 m7 _# A: R% j' n0 ^8 R
    14
    0 R4 M  I' \2 P2 A! `( `15
    3 f6 ^8 r9 R) ^& t16
    7 m- _- i+ e5 u) j; r4 U参考答案:
    ( `: ?& A3 |. W  Z6 e' q  X- {+ ?: u* o9 m4 H2 \8 [* e7 t8 B; }
    不知道为啥pd.to_numeric(df.year.str[:-2],downcast="integer")类型为float32,不应该是整型么- h) e+ p. I% Y( y* v# ^
    % ~# i" ?, P. v% |
    df.year = pd.to_numeric(df.year.str[:-2]).astype('Int64')
    0 Z& v* B  X0 [# k* Udf.loc[df.year.notna()]['year']( ~& ?6 y- z) ?$ F0 j0 ]1 n; Z5 Q( ^
    1
    ; ?6 I: K' w4 A1 N" H2
    & w! N4 M2 \+ l. a& M将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。
    - a0 y. i0 I- X& U: r, Npat = '(?P<Level>\w+层)(?P<Highest>\(\w+层)'
    ) W( {' p7 N6 S* B" n' E/ Idf2=df['floor'].str.extract(pat)  # 拆分成两列,第二列还是(共6层得形式,所以还的替换一次1 E+ Z" Y& d4 \& W# ^  [3 d
    df=pd.concat([df,df2],axis=1).convert_dtypes()  # 新增列拼接在后面,再次转为Nullable类型2 G( d( w: B4 Q5 ]( w
    df['Highest']=df['Highest'].str.replace('\D+','',regex=True).astype('Int64')              
    9 u& J- N- v+ {1 Bdf=df[['Level','Highest','year','area','price']]$ z7 M8 t+ q. ?9 H
    df.head()7 H6 v" @, h" i* @  ?, {

    " _, t" |1 N" V  u   Level  Highest        year        area        price  @5 M4 \7 m; h% |0 J5 Z: P; ^& j
    0        高层                6                1986        58.23㎡        155万
    ) I1 L7 t- x/ i8 h1        中层                20                2020        88㎡        155万. `8 x; v9 ^( y2 O: ], i
    2        低层                28                2010        89.33㎡        365万2 Z0 T: n- a' q0 h" k
    3        低层                20                2014        82㎡        308万/ R) r% T0 F1 M/ Y% M" S) a
    4        高层                1                2015        98㎡        117万
    6 u* B1 a. u( A. f1
    2 d8 P9 z$ A* d3 M( ?' z! B2
    / m  ~# s; R- k, g0 f3$ F' Z/ G; [$ j
    4
    " P3 e$ x8 z1 w9 g/ q5 Y/ V55 _6 @% u9 c$ F1 W" h
    6
      R: _! }8 D/ _: I' M, C7
    % V( n" d7 ~" Y9 b& E89 k% B0 u! ?3 S/ D  W" o- X  u
    9$ H% @( a6 M& _0 q, {/ L7 E
    102 W) S' [& n5 u, |2 z2 o
    116 n4 r1 P8 o' ]( v/ H3 R& o; O
    12
    5 k: ^0 n+ C6 t# t' s: L/ `$ W13
    / {4 O1 U& }+ @" l8 a# 参考答案。感觉是第二个字段加了中文的()可以准备匹配出数字,但是不好直接命令子组了
    ) L- ~; ~- T) T% t( ?pat = '(\w层)(共(\d+)层)'- D' K8 F+ w. [* E! h9 G
    new_cols = df.floor.str.extract(pat).rename(2 I2 l3 n  U$ ~7 B0 u. Y
                        columns={0:'Level', 1:'Highest'})
    ( ]) J" h) M* h( m6 d
    0 R, w5 Z; z0 v5 C8 Bdf = pd.concat([df.drop(columns=['floor']), new_cols], 1)  t* ]  K9 t8 E! o& P
    df.head(3)# r% t* X! d1 n( z

    % \. k/ p  R9 kOut[163]: , g1 @' ?  f* Q$ f. X" ?0 u
       year    area price    Level Highest
    & C5 f) p% k) T# W" L9 b+ ~7 @0  1986  58.23㎡  155万    高层       6
    & `; ~  Z" Y$ v1  2020     88㎡  155万    中层      20
      P' F: r, D& V( v5 V2  2010  89.33㎡  365万    低层      28" k  C6 q# u: {. e) `, c( M
    12 q3 g2 j& h$ D. a/ N& }6 N/ A/ n1 Y6 [
    2
    & ^4 I2 i6 Y; c3
    ; K" y+ ^, ]; [7 `6 g. _/ {3 l6 k  ]45 z' q, K) X2 i  }
    5% F! C4 L' o. Z. b- n* `
    6
    0 ]" w) w8 k1 T/ D* J0 v: w4 s7
    * c# I! X  l2 ~2 V) h8; Y( y6 \* g3 [! e
    9% @* o2 ^5 N5 _8 v
    108 I4 l8 t9 j1 k' [
    118 k/ x( |3 w# V4 L5 J$ O
    12' M/ f) Z5 i& }: s, p# R. v
    13
    + M: |' y8 }1 f; b4 S* O' ~1 a计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数。
    $ j6 g9 d9 }* T" ^# U( |- g2 A"""% w6 {0 l7 Y1 b' N; y
    str.findall返回的结果都是列表,只能用apply取值去掉列表形式
    & T! m9 C5 q/ D( Y$ b% [参考答案用pd.to_numeric(df.area.str[:-1])更简洁
    0 u+ T  D/ R4 z7 S& F# o1 p7 k1 E由于area和price都没有缺失值,所以可以直接转类型
    " P4 z+ D) j5 A) O9 H* ~& ^. {"""$ s' Y. n, @2 |7 t- f9 {9 S
    df['new_area']=df['area'].str.findall(r'\d+.\d+|\d+').apply(lambda x:float(x[0]))
    - d# f: ~8 w: h9 k+ I& A* {df['new_price']=df['price'].str.replace('\D+','',regex=True).astype('int64')# N1 g" |4 F% [% ^8 L
    df.eval('avg_price=10000*new_price/new_area',inplace=True)9 s6 ^9 @! w3 i" [: b! J7 A$ A
    # 最后均价这一列小数转整型直接用.astype('int')就行,我还准备.apply(lambda x:int(round(x,0)))
    # P/ {8 ?/ A4 X/ j0 l# 最后数字+元/平米写法更简单+ M3 r1 t9 y" X! T6 _/ g
    df['avg_price']=df['avg_price'].astype('int').astype('string')+'元/平米'& B; P% N4 V7 O3 O5 ]) n1 T
    del df['new_area'],df['new_price']
    : w9 o* K( r% ?1 J; j/ j6 kdf.head()
    2 g8 [/ W, O  I/ z6 Q3 }- P) Z% h% r  N  J) S0 A) ]
       Level        Highest        year        area        price        avg_price
    3 c  a+ [% m, h" T  U6 O; L0        高层                        6        1986        58.23㎡        155万        26618元/平米
    ( t5 v+ d1 V# N" N% L1        中层                        20        2020        88㎡        155万        17613元/平米
    $ y1 @4 X& C3 C- T' B2        低层                        28        2010        89.33㎡        365万        40859元/平米
    9 O* [' d" T! T) B& c& ]% j% \3        低层                        20        2014        82㎡        308万        37560元/平米, C5 `6 l  S7 i& ~; d
    4        高层                        1        2015        98㎡        117万        11938元/平米
    * }% y7 ?- Q* k2 B/ i+ v* B9 ?" U2 y; u+ w: t* D; N
    1
    ) V. c% [$ Z1 z' {2 O; t26 a# e7 B5 D1 B: i, W
    3
    5 z' f+ @8 k4 j4 R4
    9 b7 m, y: k' l3 B5
    ! L$ R" G2 @0 Y! j6
    3 U* q6 z+ A. ^* @+ ~3 Y, |7
    ; R6 T- U# o& j0 S1 ^2 W8/ N6 u. {9 h% m9 W- `- N5 t
    9! ~, s4 n. _2 M$ c7 j
    10: _( [5 S" e( [6 i; |8 w$ ?! y
    11
    - l3 l0 S) [: i- d% G: n: W) W3 |126 g- d8 b3 j4 H" g# `9 n
    13
    ' A9 H$ ~6 d7 V7 n" s; v8 q14
    - k; s' l- ~% e15
    & o0 ~% K! c3 [9 B16
    ' z  t0 i9 ?# q3 H5 j+ F7 k; G17
    6 H1 ?6 T2 u& {) g8 ]18
    ; `6 s$ @) @8 U; a+ N19( y; S; n* F, H! S4 m
    20
    / N3 m: w$ ~  M5 ]6 V9 }/ ?3 U. E# 参考答案
    1 \( {1 `' h' v$ S6 D# m; _5 es_area = pd.to_numeric(df.area.str[:-1])# R* v) G+ @+ u1 }( ~6 \4 Z3 _% X, |/ ?
    s_price = pd.to_numeric(df.price.str[:-1])
    1 h  z$ c  _& H2 X6 R& ]. Y# c; fdf['avg_price'] = ((s_price/s_area)*10000).astype(
    3 M) D* a1 i! j                    'int').astype('string') + '元/平米'% Y+ z5 y4 D5 R9 ~$ }" E0 i9 g8 p7 f

    % g, w" ?, W8 J% `1 Z& D7 i4 {df.head(3)
    0 \( X: B1 K/ C5 h6 J$ YOut[167]:
    0 f! ?# F& E0 e. b3 P   year    area   price   Level Highest  avg_price/ u$ h) O& \8 r( m! t8 g" A
    0  1986  58.23㎡  155万    高层     6          26618元/平米, Z2 Y# I$ q  H# {5 Y' K
    1  2020     88㎡  155万    中层     20          17613元/平米6 ~* R" t- H0 ~" g: X2 ~
    2  2010  89.33㎡  365万    低层     28          40859元/平米
    + q% y+ |+ d' @" e4 Z1' W7 l9 D4 ~# Z& p2 H
    2- `. m. G& t5 U6 `0 D/ x
    3" ]: x" t! Z# H
    4$ l7 d2 ?) l- p; O( i
    5& M+ n2 z# g; W3 s- @" G  C7 L
    6  d# L1 B* G& T* m0 K! K) L
    7
    8 D; H: b1 F8 O; w2 ?' s89 b) u6 [* _. ~; T- v3 D! v. T
    9# V9 s# J5 p/ @5 @2 ~, N0 f( v
    10
    & m, D0 E9 Z0 ^4 Y: I/ Z8 q5 u11
    / I  W& m! H6 M* d12
    , o+ N7 Q& ?: d$ V! dEx2:《权力的游戏》剧本数据集
    : Y: s4 ?# u% f7 n" G4 e现有一份权力的游戏剧本数据集如下:
    5 p9 N: P( u' P4 J6 o, z& O
    6 h$ o7 D7 H# x5 b' `df = pd.read_csv('../data/script.csv')
    2 ~+ j, {6 J9 @7 ?  W, r( Xdf.head(3)
    9 W  G! ]) g& \; ?) M. f! h2 O
    % i0 u8 c  X5 p' d/ D5 uOut[115]:
    - e& P0 F# }1 j, f, AOut[117]:
      V& h. Z; F( u, N: H+ \  Release Date    Season   Episode      Episode Title          Name                                           Sentence. U6 Q9 u0 C2 P" x! y
    0   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce  What do you expect? They're savages. One lot s...3 P9 Z+ s1 w) P- }3 Q! G
    1   2011-04-17  Season 1  Episode 1  Winter is Coming          will  I've never seen wildlings do a thing like this...6 c5 c' G( z7 g# s9 @& z5 a" @
    2   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce 6 K" c: l. f8 t3 s! M* w7 }5 q
    1
    ! v  e4 q2 p; S. S/ R" {2& T) Y3 B- J5 |
    3
    $ j1 V! i/ R  [4 s; d1 j% j! {4& k! F6 v  _4 O3 O' {. z1 b" H# x
    5
    / w8 X8 e( D/ }" C& N) z6" u% _# I/ P% c1 Z/ s, ?
    7# Y" Q4 {1 R; Y; A1 Y
    8* C4 w2 j1 q5 x: m0 _
    9
    $ X" |2 K* f( b计算每一个Episode的台词条数。
    & R, t4 ]8 u: V, L  f以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。5 Z6 k. i  b# C; C: j% y$ L
    若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有 &#119899; 个问号,则认为回答者回答了 &#119899; 个问题,请求出回答最多问题的前五个人。" }( e% @; W; y6 V
    计算每一个Episode的台词条数。
    + F5 O6 e: w' O! a2 L$ [df.columns =df.columns.str.strip() #  列名中有空格% o9 \/ D( u9 \8 y
    df.groupby(['Season','Episode'])['Sentence'].count().sort_values(ascending=False).head()  ^4 m7 o! s* t7 i3 s

    $ Q# U+ l" O. E" @5 S: o/ S" Nseason    Episode  
    ( {, Q# q" X1 ]& h: Z$ ?) zSeason 7  Episode 5    505: }& t+ |: l' Z% @7 D7 C3 I- B
    Season 3  Episode 2    4808 B7 n8 r9 V9 V& x$ A& Z) a
    Season 4  Episode 1    475
    0 c+ \+ x8 N# [. y4 B9 aSeason 3  Episode 5    440
    4 Q* `, S6 D3 |* t: T2 S% K/ U6 MSeason 2  Episode 2    432. {. Q6 g; b! W6 Y  j: ~8 Q
    1
    8 O! ?; P0 n8 X. r* F2( D4 V/ L' M) C
    3
    ; e+ j  S9 g8 P+ m6 k0 C9 |4 `4! @- w) n3 \, ?6 R
    59 o  v4 L; Z; }( w
    6
      U* ~% \7 {* v. W7
    ) D; g; i1 w% _8 [80 Y( E4 N$ M  h
    9
    % H# }- e6 P" b* b以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
    ' {* K6 |& u0 w( M7 `# str.count是可以计算每个字符串被正则匹配了多少次,+1就是单词数* ?2 T. F. O) X& _7 {) P
    df['len_words']=df['Sentence'].str.count(r' ')+1
    1 C% a. [. ^1 }df.groupby(['Name'])['len_words'].mean().sort_values(ascending=False).head()
    5 ^& v4 f: s9 y. L0 t$ m, w4 E( T9 ?  B  @
    Name; ]$ F. l4 o  H. P" h+ A
    male singer          109.000000
    % P' E# j; p2 r4 C) x8 pslave owner           77.000000
    - ~5 U# `9 X' o2 N7 qmanderly              62.000000& E" Y' K5 N) b4 {7 {* ~6 Y* x
    lollys stokeworth     62.000000
    % v% y! T' j+ k5 Adothraki matron       56.666667$ L9 f) g4 V4 f" v3 z+ A
    Name: len_words, dtype: float64, e& _. l* W7 s; G  f
    1
    , o$ P6 ~! O1 O- S25 U9 Z) [9 U/ F/ f  ?/ r6 P
    31 I' y2 [6 n! i* j6 J
    4
    * A: u0 t  @! w3 _3 [5
    * u* n+ ?; o9 }7 L6
    . E5 |0 K; V/ h( _7
    , J: H7 H6 r1 `! |# D8+ \  b) ?, c/ [  s: B
    9
    . k. _4 u! x, s0 t5 {105 y$ C  a) J! K1 T$ Q% }/ Y& F2 l  S
    11; R! C; d0 ]! m" l8 y; `4 H+ d9 _
    若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有n nn个问号,则认为回答者回答了n nn个问题,请求出回答最多问题的前五个人。
    3 z, R; S  ~$ U8 @df['Sentence'].str.count(r'\?') #  计算每人提问数& ~' g- ?% \# ], [
    ls=pd.concat([pd.Series(0),ls]).reset_index(drop=True)# 首行填04 b* g  \" \9 S6 P3 U& ~
    del ls[23911] # 末行删去$ M0 n. A: h5 f0 D
    df['len_questions']=ls
    % P' j( j4 o! edf.groupby(['Name'])['len_questions'].sum().sort_values(ascending=False).head()
    1 K7 ]# B5 R8 g5 [3 ^+ o; E. P. n* m% s% U3 l9 d* `4 @6 S
    Name. |# ~# o0 c9 \! Q# x
    tyrion lannister    527
    ( E: J3 h, `8 F( _0 {, Xjon snow            374
    # D7 g$ j3 R/ f3 wjaime lannister     283
    ; v# k7 {1 B) \" g, jarya stark          265& h/ {' O! s. i( R
    cersei lannister    246
    / L# k6 w* |; ~2 N3 `Name: len_questions, dtype: int64
    % e& {, K& O0 V6 ?4 n
    : N5 j; Z) V( P; n  c# 参考答案
      P4 o, }$ A& \8 y% ks = pd.Series(df.Sentence.values, index=df.Name.shift(-1)), l( H7 Q; I  g4 M8 w, l
    s.str.count('\?').groupby('Name').sum().sort_values(ascending=False).head()) S$ T2 F- k7 _( r* T
    * Z. k( n; V/ e
    1! e: c/ g1 v, d/ X# v. b2 p# l; b
    2  O& n& }$ R0 B( g
    3
    & G# S0 s% b" W% B; Y/ `4* e3 c( t3 ]1 e2 H0 z" A
    5
    1 u( R9 ?8 ?5 A0 \( ?6
    + V2 p6 ]: m4 L* h71 H7 q2 ^' Y  i9 n$ c/ y' Y
    86 b, `+ M5 A# Z. ]" Y! r
    9
    . Y2 o4 v& D/ e7 }2 ^* q# T10
    9 [1 k3 `1 v  f11
    ' p# F7 K. m) V8 b3 b5 z: Q12
    - c; ]3 d1 A' Q% P  t" f13
    ; D7 }: q8 E- k' U14
    & Z, V, B7 N/ P( Z15
    ; c; ]) ^' H% u% J) Q* k) p; i16+ B% k/ e+ ?" n& e! J
    171 a! U$ g' Y$ \3 j! F7 F
    第九章 分类数据
    2 T. ~+ ?+ _! e* z$ Mimport numpy as np
    6 F  K( J& q- P8 yimport pandas as pd
    % m  A* y8 `3 |8 j1 t1
    / e; }& D2 K* n  H2
    % E, ?# K3 s# o# I9.1 cat对象% i# i5 c( h; u5 M
    9.1.1 cat对象的属性# o1 \& f3 v4 j+ `
      在pandas中提供了category类型,使用户能够处理分类类型的变量,将一个普通序列转换成分类变量可以使用astype方法。
    1 `' f/ @7 Q& B$ N5 k" l. l$ D! l, u1 C) @& ~9 b4 ~. L3 u9 Z7 p# p
    df = pd.read_csv('data/learn_pandas.csv',
    ' R3 G4 S; c2 ^; t     usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight']); Y# U3 f, ~; u( c- ?& G7 K( A
    s = df.Grade.astype('category')" M0 |0 W* K+ a) N2 A2 k

    5 H; \# T1 S6 Y) I9 j* C5 {s.head()' Y0 _- F7 r+ r6 w
    Out[5]:
    , z- ^3 a3 q* `$ I4 E$ `% W& w' e0     Freshman
    : a$ v# E) ^7 ?! {, M1     Freshman
    , x. l5 f% l7 Y0 ?. _. |* E0 K2       Senior
    ! S# V7 J7 q2 _3    Sophomore
      _1 v6 e" u; s3 D2 C$ `# Z( @4    Sophomore/ S+ r4 C. }* N$ \: P9 {% m) G
    Name: Grade, dtype: category0 x, C" T9 L* k( }6 D
    Categories (4, object): ['Freshman', 'Junior', 'Senior', 'Sophomore']
    ) q4 R, j' d% b1
    : \8 a3 U# B6 i5 u# ~2) M$ N' X) J( P7 \6 [5 [: S
    3" w3 P, V$ I+ P  h  q2 z" E- B
    4' }* t& K* h. V% r* a4 {) {/ S( T' \
    5, R4 }  r/ D: N% C. `+ G5 `, |& W: ~
    6
    : `, K# I/ t0 O& A5 v7
    / \# }/ X; P6 B* l& M9 Z: i' O8
    % L4 O" U. d$ k8 T: c: d9
    9 }- U/ a3 A% h104 l: y  D/ W$ A( T" ]/ r1 G
    11
    , K+ d. T6 Z0 ~% `% T12
    , P2 E9 l6 q0 n: L135 h# A. W$ I! w/ d4 }
      在一个分类类型的Series中定义了cat对象,它和上一章中介绍的str对象类似,定义了一些属性和方法来进行分类类别的操作。
    # z- w3 ?$ A0 h  F! a2 Q2 m9 @
    ( a" v% l5 m- U$ h* h* Us.cat
    & c; c# Y, i- ~Out[6]: <pandas.core.arrays.categorical.CategoricalAccessor object at 0x000002B7974C20A0>6 ]6 H( R+ e) f: a1 d$ X" C
    1, B( o  Y/ V  d
    2
    2 }7 {" l7 d( ^3 m; m: wcat的属性:
    9 A9 T$ l/ `3 t1 J% r7 `9 D. z: R/ ~9 v0 U6 T- U
    cat.categories:查看类别的本身,它以Index类型存储% x/ U! u9 p9 g  I- X% ^
    cat.ordered:类别是否有序- \+ C& g. r+ H' e
    cat.codes:访问类别编号。每一个序列的类别会被赋予唯一的整数编号,它们的编号取决于cat.categories中的顺序6 S& W; G+ Z4 f3 c& A/ {' i
    s.cat.categories
    4 d+ S  a* M) ^, iOut[7]: Index(['Freshman', 'Junior', 'Senior', 'Sophomore'], dtype='object')& J4 {+ B$ N. }+ L6 t; o

    , |. u9 U9 ~+ b3 g5 zs.cat.ordered* K& v# ?( \( w  m% z  m
    Out[8]: False4 [- ]: b) k% [

    0 T! S. z& J: b% us.cat.codes.head()
    * D  n' a& G' E7 v; HOut[9]: 2 w! a6 y4 p7 V$ P& J
    0    0
    3 M" ~6 K# ]) S1    0* V2 k+ V8 V3 X3 v! P1 u' |
    2    20 ?, V' S$ P# g9 C
    3    3
    ! \) n1 [. g7 v, {; f2 b" F4    3
    2 l% p/ D' [/ ~9 x9 H6 qdtype: int8& l4 m. ~3 s+ H; m
    1; n& `" T6 C; K1 E) R
    2
    & R$ \3 m+ O/ t) M( [9 h6 A3
    : V# w; i) d: t, I/ c: G$ z4$ k) H; M+ o0 w; Y2 F! f9 _% ~4 D
    5% {! K3 v6 r, y  F" g
    6- a9 t* x' ^3 Z' K0 j
    7
    . i' a. q3 ^: D9 n" V4 w+ _8: u! w# t$ Z. a  N+ o2 }
    9: f3 T) o  l2 I$ R. J0 M: R6 a$ i
    10
    3 m8 A  k4 |8 {# t/ {11
    * K0 c- @+ h4 `0 d( C. h! ^12. ]2 n$ O( f: D$ V$ z7 L# w
    134 }" ~* K9 @# g, N% B- R& H. [
    14  y  l& m, n2 z7 R2 {( ~# d
    9.1.2 类别的增加、删除和修改
    8 f9 g& l4 _% a. ~3 s$ c  通过cat对象的categories属性能够完成对类别的查询,那么应该如何进行“增改查删”的其他三个操作呢?( J; ?! H' n* ~' ~3 k

    & T, w/ L6 J; w【NOTE】类别不得直接修改
    ( |: Y9 F$ W" M2 I8 U2 ?7 Z在第三章中曾提到,索引 Index 类型是无法用 index_obj[0] = item 来修改的,而 categories 被存储在 Index 中,因此 pandas 在 cat 属性上定义了若干方法来达到相同的目的。
    ; }- L# b2 n% ~$ |( \
    ; s3 |. Z  O( G4 n0 [2 nadd_categories:增加类别' _0 H& d$ [" |. U. B- \
    s = s.cat.add_categories('Graduate') # 增加一个毕业生类别9 y5 P1 J! y# r  ]5 \
    s.cat.categories
    % m' y4 `$ \* C' V
    5 b; W* l6 d, S) L- l$ QIndex(['Freshman', 'Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')7 T7 u+ ~/ O) h  K! _4 S
    1
    " N. @4 ], _* X9 Z; E" ^/ J2
    ) B4 u8 s" e, d; N39 w4 b, P' ^; Z6 |
    44 `/ ]4 a! ?( R4 c
    remove_categories:删除类别。同时所有原来序列中的该类会被设置为缺失。7 o/ B. e2 a' ]( K
    s = s.cat.remove_categories('Freshman')* T8 B% K/ x' F, ~; z

    1 \5 X9 Y% R4 X3 @  h+ ?s.cat.categories
    ) t8 k) h0 X9 E! I5 \Out[13]: Index(['Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
    6 T$ _8 L% M8 m3 p  c5 w$ I8 v/ _$ L
    s.head()
      ^& c7 U& b% y0 W8 k9 b: AOut[14]:   z' Q( V  j7 `5 G
    0          NaN
    1 C- C6 ~1 Z9 s- ^: e$ p- q% h3 s* W1          NaN
    - I3 b+ Z1 ]! z% V5 m% s( a/ X2       Senior# a& q8 }& Z, |9 l: D4 ]
    3    Sophomore" A  h) J1 U. E- D* X* a' t* e3 O
    4    Sophomore
    6 l! p7 S  ~, X) T* v/ h- gName: Grade, dtype: category1 g. V5 v" N  n$ j6 f3 {: B4 z& N$ r
    Categories (4, object): ['Junior', 'Senior', 'Sophomore', 'Graduate']
    ) W" t9 }8 p! q' }( o6 z1
    + e8 ^6 e: O( c' G2) B  z1 V2 K5 B2 P
    3
    " z5 c6 J7 c/ Q9 A5 Q0 U, A46 e( S, U. g+ v$ I
    5
    - M8 q, Z. L1 n61 S0 r' W/ r$ p" H. l0 {" ?
    7
    ( V, M6 {# C4 n8
    + F. e4 B( R! u. }9
    / l8 S5 i" O. n6 E104 ]6 D3 P5 j5 u3 K2 `+ D
    11( J" X. i: n! W4 R! m
    12
    / a+ T! d) d3 G, V) `& w7 {13# ~8 ?$ [9 ]. \  X% U$ y8 u( a$ l
    14
    & _: j- C3 J4 s1 n8 _3 S3 aset_categories:直接设置序列的新类别,原来的类别中如果存在元素不属于新类别,那么会被设置为缺失。相当于索引重设。9 G3 g( u+ {; g' A2 j7 T! j
    s = s.cat.set_categories(['Sophomore','PhD']) # 新类别为大二学生和博士# K4 `$ J$ e  e/ J% k' f
    s.cat.categories& x% z! Y  @" G/ P* G  H/ j- m
    Out[16]: Index(['Sophomore', 'PhD'], dtype='object')
    4 H$ @/ [9 Q" p9 k1 D) f* ?0 D9 W% e% F" `( m
    s.head()
    9 _) f: P' S5 f- J: O. mOut[17]: & z* P; ^; j" i8 Z: J
    0          NaN
    7 N6 Z; [: h7 |1          NaN
    ; A) P* ?9 s7 ]2 g2          NaN
    ) `5 n. A% Q0 e, z0 C6 F3    Sophomore, N& q: q: F: a. B4 I; L
    4    Sophomore3 D) M4 ]' N& C- z
    Name: Grade, dtype: category% Q2 A$ s; ]3 d( D; c& O& D! b
    Categories (2, object): ['Sophomore', 'PhD']9 {2 A( i7 W0 z* H  r: `
    19 p5 Q* @' d' c3 E& y# p2 ?
    2( ]0 q% g. B. q
    3
    8 O- U$ N5 R$ F2 ]* i" b( I4 c4
    ( y- M7 A4 X. O% j# U! [6 m' M) {58 i# e$ }$ t- e; o- w
    6% z; L$ D) u6 R
    7
    & `% ~& [1 Q  h) p83 L( c6 ~1 D. b5 F7 h
    9
    3 G, L. u6 D& C. H4 k10
    7 l' A# o; Z0 |2 Y! ], n11( x" l5 p: J6 e6 G7 B% M
    12
    0 N) m* D+ v( V/ H( ]7 X+ I* k0 z131 y/ N. a4 f' P6 t2 l0 x. t
    remove_unused_categories:删除未出现在序列中的类别8 j  i4 L: `+ s3 L8 b
    s = s.cat.remove_unused_categories() # 移除了未出现的博士生类别, F4 M/ U. K. S* f* ?) L
    s.cat.categories- X: K% R1 Y- c8 ]" q& n/ ]9 y  Q

    8 b( G8 q3 ]$ X8 o" SIndex(['Sophomore'], dtype='object'). o* h3 i9 _) x4 S
    1& b1 D' R* C; g  V  g' c
    2
    0 l8 c. b& v! x3
    + E0 u$ A  H: D; k4 X. N# A5 O44 q8 r+ K6 h7 L  ~( I9 H, p
    rename_categories:修改序列的类别。注意,这个方法会对原序列的对应值也进行相应修改。例如,现在把Sophomore改成中文的本科二年级学生:
    " E/ m: E: u$ G& y/ O7 }: ys = s.cat.rename_categories({'Sophomore':'本科二年级学生'}), I: c+ T% ?; p  F- t) R
    s.head()
    2 u8 g' v# ?6 w3 _8 Y( e; S+ g- h& F( P5 E' L
    0        NaN
    9 `6 U4 Q* [/ d; Z  k2 B$ ^1        NaN
    . `, }/ [/ V/ C* }$ C& ?, a  V2        NaN. m& K1 M6 U1 H+ F8 @+ X, S' y
    3    本科二年级学生
    3 [) P$ V& x) L& t4    本科二年级学生; e# q2 x% M5 m# f- j- G
    Name: Grade, dtype: category8 |! g9 {6 S2 ^9 J
    Categories (1, object): ['本科二年级学生']
    ; S' C, ^5 i1 @5 n8 Z1 ^1
    5 W5 W8 o- M# x4 Q* b20 m  v* h7 V2 V" b5 A* n
    3! f6 N$ F* C' D( f+ i) I+ m
    4
    ! r6 ^, T" G8 Z7 y* C4 [59 x" W4 ~* {% E+ Q
    6
    : \% {0 _" s) C+ p( Q$ @. T- J. u7/ D  R( Y% I; w2 }! [8 t. w1 F
    8
    ) q2 y' k. l) F2 _2 B! \1 ^9
    $ i2 x% x" V7 Z6 Q0 |10
    9 _2 M; T. z. o4 O3 |" f0 }5 R7 T9.2 有序分类
    4 z: h  D5 Q; G5 w; L+ u) j9.2.1 序的建立1 j0 @" T* W' {/ r" n& ~" M; }  {
      有序类别和无序类别可以通过as_unordered和reorder_categories互相转化。reorder_categories传入的参数必须是由当前序列的无序类别构成的列表,不能够新增或减少原先的类别,且必须指定参数ordered=True,否则方法无效。例如,对年级高低进行相对大小的类别划分,然后再恢复无序状态:
    4 j4 e4 ], S# `
    : O2 Q% @) i" O! Z! X' Ys = df.Grade.astype('category'): G8 n% j6 T1 E  O: J0 p9 Q
    s = s.cat.reorder_categories(['Freshman', 'Sophomore'," P+ `* r9 i" U; L% Z
                                  'Junior', 'Senior'],ordered=True)
    & d. j) y# K- ~s.head()1 ]3 @/ V: _6 e6 k) O# S! O( {
    Out[24]: % `5 O2 a: |, n/ |( }+ R
    0     Freshman
    . M  q) _9 w2 k) b$ N- x* ?7 m1     Freshman
    ' [, C6 b. m* W$ Z2 u$ O& \2       Senior
    7 C; b# m+ U! b. q  }+ Y3    Sophomore! S6 _4 r6 C; ?; h
    4    Sophomore" }5 S6 S- u$ W9 P: T( M8 ^% \
    Name: Grade, dtype: category& I$ H( r5 V, K  m  l3 @  v3 h
    Categories (4, object): ['Freshman' < 'Sophomore' < 'Junior' < 'Senior']
    * @, Q4 q# E& X* H! G4 [/ d% p& L$ M
    s.cat.as_unordered().head()! A) d7 u' K& l: s3 @1 D
    Out[25]:
    , }9 i7 v6 O3 j3 K1 M  p0     Freshman. B6 Y/ ?1 J- s: k% G+ M
    1     Freshman
      a! i& {+ k* H' u2       Senior
    / B& Q- r, N4 S* w+ T+ u- o3    Sophomore; O" M6 N1 Q" Z3 J- A
    4    Sophomore' i; ^4 k3 z# l: e# T) ^  q# H9 C6 e/ w
    Name: Grade, dtype: category' M1 c* B! x2 Q$ _
    Categories (4, object): ['Freshman', 'Sophomore', 'Junior', 'Senior']
    ; @# U) O% O& x; ~* F, g* A: _3 q3 o, I( S" H
    1" J2 ~: X; s2 z4 b5 Q
    20 T0 f; z! b8 K( B- _% [& p. w" x
    3
    ( a1 B* ?; @  Q* l4
    ; A( F) t/ T1 Q- o% w2 ~  G* M5
    5 i* {  @' t( L* ?) @1 {* u6
    $ t' v$ \  A: s, ^& b7
    ! B' C* `% n2 P/ T8
    3 k6 I4 v4 Q- t. s9$ Y- `2 j: l4 K7 W9 w; _# V
    10
    % h4 u. v+ l' ?. {( r9 p  g5 q11; P  I, \9 w% O; E8 I, A
    12( y+ i0 j. [( G$ P( U7 i' m
    13
    1 i0 c6 n# z" u6 _) f14. w2 b& I( [% Q+ K2 ^
    15
    # E% p7 R5 E, {4 d1 L5 I$ w( B16
    - ^9 Z4 \+ [7 J8 z* d17
    1 H* F. r1 Z( t5 g& k. v" s) D. ~4 k18
    " H+ H& V0 b  ~6 l( l# V: e2 L19
    + p. }/ Z- R: o; l8 n20
    " h4 y- s+ a' f; C& K  n: R217 b  m; [+ \. U7 @2 o; a
    22
    " D3 e3 [' B- _) P  如果不想指定ordered=True参数,那么可以先用s.cat.as_ordered()转化为有序类别,再利用reorder_categories进行具体的相对大小调整。4 O( I& }4 Y- l/ B

      F2 f4 e" W1 g8 X# S9.2.2 排序和比较
    8 \( z9 W6 K6 j  K* I6 v% ?' t在第二章中,曾提到了字符串和数值类型序列的排序。前者按照字母顺序排序,后者按照数值大小排序。( L3 j3 m7 o3 k+ f7 Y  O) S

    ( i6 v" H9 `6 @3 N/ E: C5 g  分类变量排序,只需把列的类型修改为category后,再赋予相应的大小关系,就能正常地使用sort_index和sort_values。例如,对年级进行排序:* o1 [" A! h$ G2 v- f
    ' [% i. j5 z# ~+ ?! l; p
    df.Grade = df.Grade.astype('category')
    6 u* r4 B$ d1 S6 o# B! udf.Grade = df.Grade.cat.reorder_categories(['Freshman', 'Sophomore', 'Junior', 'Senior'],ordered=True)
    6 S' Z" u# h, k0 U: Fdf.sort_values('Grade').head() # 值排序6 v1 ?$ C4 t! i; s
    Out[28]:
    8 l% x) e0 [$ D" [* R% C) E        Grade           Name  Gender  Height  Weight4 R" `  a9 O4 K
    0    Freshman   Gaopeng Yang  Female   158.9    46.0& o, M/ ]- Z' C
    105  Freshman      Qiang Shi  Female   164.5    52.0: w5 M1 D) F  P/ ~9 e
    96   Freshman  Changmei Feng  Female   163.8    56.0
    & P+ m1 f+ {( Y! `8 H3 D88   Freshman   Xiaopeng Han  Female   164.1    53.08 ~2 j, k% Y# F6 V1 P8 S4 S) u
    81   Freshman    Yanli Zhang  Female   165.1    52.0
    , N/ N: l! k$ [. T1 O* D! N& m  j$ {5 H# D( Z
    df.set_index('Grade').sort_index().head() # 索引排序
    : U, N, g+ X2 H4 u" }Out[29]: 6 v6 ]2 b/ s1 W8 r
                       Name  Gender  Height  Weight
    4 K* B8 Q5 ?+ V7 j/ c& z& `7 bGrade                                          / z3 y5 }" n+ O- o3 m
    Freshman   Gaopeng Yang  Female   158.9    46.0
    + c" k3 r/ M3 gFreshman      Qiang Shi  Female   164.5    52.0  L6 L$ V8 r5 o5 f. x! |, l/ `
    Freshman  Changmei Feng  Female   163.8    56.00 t5 C4 z# v5 k3 I8 Q4 n2 Q& E
    Freshman   Xiaopeng Han  Female   164.1    53.0
    3 H8 D. M# u4 p5 ?- SFreshman    Yanli Zhang  Female   165.1    52.0; c$ A. P8 X, K( ~; }

    - P  P' W  G( l, l7 J* i" T1
    ( @7 `3 a) h; V0 }: f1 z$ E* s2  ^! q( n* m, T
    32 L* j# ~3 N5 N- s$ g- b
    4
    6 ]  \7 D4 m9 k  T5
    1 h2 c: d6 M' {6 U1 t( v1 y& q6- @' I4 ~. J& r) B
    73 _/ j9 N* j$ d& l) b
    8% Z8 O/ t! e* B
    9
    . C& f& d, f, `10
    % h3 ^  v0 {. N2 T& a116 u; q6 b, G' ]. |1 A5 Z: U
    12  ~! `8 ]0 j; K* C. L/ b. }
    13
    1 I/ C! h5 w% @# b# O# l14
    3 a/ E, F- v" L15! B& Q5 ]0 b( ]. F4 G
    16
    ! U$ S; k1 W1 H17
    ' D: }  n7 b) F3 U6 n& \18
    . J) I6 S, \, i0 N$ `  a192 Y/ y5 O8 S8 ?9 e# V9 v
    20
    ! M% I9 V4 Z3 G$ w" a0 |5 R  由于序的建立,因此就可以进行比较操作,方便后续索引操作。分类变量的比较操作分为两类:. l8 I  k0 g9 }: Z3 Y: }/ q; Y
    & @+ z5 K1 x1 [# o
    ==或!=关系的比较,比较的对象可以是标量或者同长度的Series(或list)。(无序时也可以比较)* W% f* h2 ?; o
    >,>=,<,<=四类大小关系的比较,比较的对象和第一种类似,但是所有参与比较的元素必须属于原序列的categories,同时要和原序列具有相同的索引。0 {, {, L- ~7 q* g5 d; t, f7 l
    res1 = df.Grade == 'Sophomore'
    # I; N5 @) @7 P' v
    & t- ~; y- U5 d! v% X  Z  zres1.head()4 R8 E5 c: E' T, g
    Out[31]: 7 N7 X  F, W& Z  z2 ?4 ]! P
    0    False
    * b9 q! h: ^6 R3 F2 i1    False
    : U! B( Z( ?9 ?7 K6 C* L$ v2    False
    1 e4 E0 J* M2 V: j$ `3     True' `7 |! \& ?3 Y- V& Y
    4     True% J  c- O5 T, V' ]* A4 m
    Name: Grade, dtype: bool
    & B- N. J% H. \4 p' u# Z3 I% h4 n7 O9 O& R
    res2 = df.Grade == ['PhD']*df.shape[0]
    $ U$ s8 H: F/ L% a) X* F4 ?# ^1 f5 a, {' D, P
    res2.head()& u% \6 ^+ d6 T* I
    Out[33]:
    ! K( y3 N/ e7 S0    False
    ; Q; e( b* |/ z6 q9 {5 Y1    False
    ; @. m) F9 w6 L2    False
    - H% R) p/ r6 i+ a# b' b3    False
    % Y* N( G' T! C- Y+ Q0 j; H' A, Z4    False7 L1 b- M! ~8 E  A7 w8 M
    Name: Grade, dtype: bool3 D: f: ?" W* `9 g6 F
    0 T# l$ Y4 m# a
    res3 = df.Grade <= 'Sophomore'
    - i0 p6 R0 M$ {& N! @& H$ `4 o
    res3.head()7 Y  T8 C' v& O1 ^) _
    Out[35]:
    + r3 G- O) _( I' y2 ~2 |7 e5 u0     True: ~9 s$ V, W$ w0 e" `1 F' P/ Q
    1     True
      \( N9 p# B- `1 y/ _# Y, G% D5 v% o2    False
      x% m2 r- Y; Y, X3     True
    ) `* }% Q$ L/ t7 {4     True  H  ~: z$ L9 I- C/ b, x
    Name: Grade, dtype: bool
    * W6 Q0 y7 d0 u: L" t: e" N+ r: Y6 s
    # sample(frac=1)表示将序列随机打乱。打乱之后索引也是乱序的,直接比较会出错,必须重置索引。
    * \2 Y% B( `3 [. k, J) W# ares4 = df.Grade <= df.Grade.sample(frac=1).reset_index(drop=True) 4 F% \8 x7 |4 @8 P) [- _/ a
    , w, c# K. Z4 F( j
    res4.head()9 H# T3 x7 c% y7 o6 ^2 B. t& t$ `
    Out[37]: 4 y* @  e9 \& \3 ]  B7 t
    0     True' [# P( q* c& u1 g: F4 E$ ^
    1     True9 {' Z, c1 v1 Y0 {
    2    False
    + |" l/ I) a4 X3     True
    0 |! R4 e  o# L5 H6 I4     True
    0 M' \* F  n" F4 NName: Grade, dtype: bool
    4 o8 O" \. S) |/ h
    8 T& z9 I- w. W' T# `4 b& x. f1
    ( s$ H4 v. @" M, o/ `2
    + R8 l5 S. b& Z' ?" Y2 D8 T3
    : s2 q- W* R8 ^# j# a/ O4( \. q5 t) C/ \
    5* b, _5 x1 V6 o8 Y& I+ H
    6
    5 }# e. z3 @! h8 s3 g7
    % z6 ^+ I/ h2 z6 ^8; G( `! M" @/ I0 @2 G0 N
    9, R8 \  R5 o& @" |1 R$ N: l* B: l/ h* D
    10
    5 R2 x% r( ^% s0 Y* |3 z119 v. c) U' J* F6 G
    12
    ' j2 {9 M0 I4 ]) m13' K1 ~/ C0 j5 [
    14
    + b3 l! g4 g/ f5 d15
    0 ]; X3 V6 U& c. l4 a16
    8 ?+ \) q9 I. f8 t17
    5 k# J* |0 X$ K+ P8 U18
    & Y: E: v" s# V( ^4 A19
    ' j3 w7 {! W: @/ {4 d202 r& q, ?( N0 f7 v
    216 Y7 V0 n* y* ?! U. J$ A# y
    22
    + z  Q: W( f; N, Z+ i, B23. h8 @) r- I$ {# i- {* O; i
    24
    2 |* m- `9 B% C25
    ' ?& `* |6 a" V0 x% o. o( s0 ^26
    ) F' O* v$ i  v  v3 e" e4 ]0 a  e  U27$ O' X# ^6 i, X4 L/ c' Y/ O
    28. k) y1 f& q: l* l6 P/ j# M+ \) F5 T
    29
    & ]* o7 J7 b% e1 F30
    6 q2 o" e1 u4 a4 {5 c31
    ' I7 {6 \7 V2 Z( t2 b. a5 D32
    - b( Q( l6 R. K8 l4 f  I' ^; x330 e# C, @0 a9 |* k
    34$ o! H/ k+ z9 T" e" E3 d, k( I' Z
    35' Z0 w, i4 i5 V+ B
    36
    + r# D6 I" J' f' f  V+ E- }# V371 J& b9 n  |* R( G! J4 d0 W  m
    38
    $ I2 w( y, F% a4 ?* Z, o39. \& k, Q$ A- K9 u4 K! P. U5 _
    40' D+ r& f/ }7 c7 E4 m. P% ]7 C
    413 S- W' p) Z. m- I  r( d
    42# T( k2 H* p  s, ]
    43
    + \- K5 _& u0 ^" ?+ ]44
    # d1 g) z) L$ x; f) i% s, R* v9.3 区间类别
    - I* R& E2 M5 d7 A9.3.1 利用cut和qcut进行区间构造/ b! O6 n  k3 U) B
      区间是一种特殊的类别,在实际数据分析中,区间序列往往是通过cut和qcut方法进行构造的,这两个函数能够把原序列的数值特征进行装箱,即用区间位置来代替原来的具体数值。3 V' w9 x  D* Q: u8 K$ Y7 o7 _

    # }, M) ?( {1 ~6 C3 a  ^) c, X( g) [cut函数常用参数有:5 h$ W# i* t& _  I
    bins:最重要的参数。
    0 o0 f8 L& |' Z如果传入整数n,则表示把整个传入数组按照最大和最小值等间距地分为n段。默认right=True,即区间是左开右闭,需要在调整时把最小值包含进去。(在pandas中的解决方案是在值最小的区间左端点再减去0.001*(max-min)。)
    / T: W$ G9 i' {% H! Z  R0 z9 D* T也可以传入列表,表示按指定区间分割点分割。
    " M7 R& `& P$ U/ s+ P  如果对序列[1,2]划分为2个箱子时,第一个箱子的范围(0.999,1.5],第二个箱子的范围是(1.5,2]。* K- Q" Z, f+ \) |/ X* M6 |8 ^
      如果需要指定区间为左闭右开,需要把right参数设置为False,相应的区间调整方法是在值最大的区间右端点再加上0.001*(max-min)。
    2 M& w3 C8 ~  o$ Z  H4 |2 w% W% Q' I4 \
    s = pd.Series([1,2])
    ) E( n9 o# g# d# j) e# bin传入整数
    ) |) t2 O& L" W) G: j" r1 Y3 O
    2 F0 ^* F$ {; N' i/ `9 t& N+ B# vpd.cut(s, bins=2)
    $ E- G5 V' J8 z* N# u" y! W& bOut[39]: * i7 ?2 o7 d& f# E) k, m! X
    0    (0.999, 1.5]+ ~  T8 T8 J" b) M4 a2 b' W/ Q
    1      (1.5, 2.0]/ G( ]8 k5 i4 Y8 _! s, p- _# b/ {
    dtype: category* h" a& Q. j" j8 ]. m% ~
    Categories (2, interval[float64]): [(0.999, 1.5] < (1.5, 2.0]]
    / ?& ~5 P" k- T  C
    + A# _, R$ y: p8 |" upd.cut(s, bins=2, right=False)
    2 Y3 A8 F5 E9 u; d" bOut[40]: : m3 `/ B8 Y0 _3 O3 _1 g) j
    0      [1.0, 1.5)
      G% i, \" X4 C/ M) g+ N. \' \( u% z1    [1.5, 2.001)
    - ~6 c  u8 A; o+ S7 J. h: ydtype: category1 a9 l( w/ ^& ~
    Categories (2, interval[float64]): [[1.0, 1.5) < [1.5, 2.001)]. V0 y5 g, T1 v3 v- @0 [! T0 T' q
    3 d5 ]& @1 a5 L/ m) y& [
    2 \% R0 X3 ]* n! g7 J
    # bin传入分割点列表(使用`np.infty`可以表示无穷大):
    0 \1 Z+ E5 N1 n1 f& S1 Wpd.cut(s, bins=[-np.infty, 1.2, 1.8, 2.2, np.infty])# x4 ]" A2 g* Q' ]3 \8 t
    Out[41]: 7 G0 a' Y+ D4 M' Q0 V( K: |" i
    0    (-inf, 1.2]
    ; R; M9 X* h, j( ?3 I4 p1     (1.8, 2.2]2 m" x) c9 s3 t5 l6 x0 _3 o
    dtype: category1 t  v. H! R8 x- @
    Categories (4, interval[float64]): [(-inf, 1.2] < (1.2, 1.8] < (1.8, 2.2] < (2.2, inf]]
    3 ]7 _) P! m1 M" ?4 R2 Z+ F% v0 J; Z8 w* e: k
    1
    / j# i$ N, S0 F! H% |# _) E+ F2
    8 v; g) L' ?- n! T3
    3 I3 t( p$ r0 d( u4
    . _0 ^+ q5 i) I$ {5! ~$ X2 m; A, r" e+ I% K. o, u
    6- P* P7 N7 I4 _' a: ]
    7  t+ Y. J$ s3 O& u
    8
    * t4 Z* n- H& E, l5 e# ]& a* j9
    ) J% x/ k* w/ B4 c5 K+ C4 ]105 C8 R# \; f7 _# V, I
    11
    ! {9 Q. j7 |& `7 _12
    ; }' ]& G. r$ P% ]. j- d; t13- b" R1 K. a" I9 ?" J7 z4 i
    14" I( H: Y. M, {+ G( _4 Z
    159 s/ e7 H  h6 K' }2 l" E6 b
    16
    , j1 e. J  \7 |5 P* x176 g+ t! V& j$ E) E# t% s
    18# L" w+ J" q4 s/ Y
    19; a- {& P; C" D: T' O
    20
    3 [6 e9 l, C7 P9 o; n( w3 B4 R) S9 ^21# y* n9 R% ~. B: c0 w6 @, R
    22% y  I  F0 r% W8 s* }
    23
    " S2 A  H' [+ G; ?1 c4 K24
    - L; v/ E$ O6 z25) n# U/ ^$ t$ D# v) X
    labels:区间的名字
    - f) A: I* G1 X; p- pretbins:是否返回分割点(默认不返回)  v7 c* v; z& U1 x' N+ G6 \3 ]
    默认retbins=Flase时,返回每个元素所属区间的列表8 L3 b% R+ `- w4 k# R# j
    retbins=True时,返回的是元组,两个元素分别是元素所属区间和分割点。所属区间可再次用索引取值
    $ n' A$ n; J7 t$ J1 J5 G" {( Y7 {' X* d" i8 Y- G* A
    s = df.Weight
    0 X3 Z. Q) T8 hres = pd.cut(s, bins=3, labels=['small', 'mid','big'],retbins=True): _7 ^! W# k+ T. x( g! x, j- {
    res[0][:2]
    / {  }, z6 N* r# J. Q# B' I4 U6 q+ @& g/ P
    Out[44]:
    # `# g& H( i2 E, ?- ^& z0 w0    small. p1 J! R) ~/ A0 Y1 h9 K4 B) y/ y
    1      big8 p- t1 c; q. Z5 u  l& J& F8 R2 V
    dtype: category
    6 U0 I8 _$ Q# L7 f3 jCategories (2, object): ['small' < 'big']* n* j# H% ]8 K# c5 l; c; ?( M3 S
    0 e! _. g; ?% C( X8 X+ a5 n
    res[1] # 该元素为返回的分割点
    3 _  j4 Y: p* \5 K  `. }2 S' bOut[45]: array([0.999, 1.5  , 2.   ])9 ~0 t( F: T  i: `$ D7 a; j5 F
    1+ v& p" X( ^/ N. l2 r  M
    2
    & Z8 ^' \8 P- ]- J3. q4 S1 W% Z3 ]4 g& ^7 r- Y
    4
    ! @4 N% _0 ^6 p- E8 [9 q5% a+ {8 U6 E. a; s' V
    6
    3 T4 a: U! R& }( b. U  b  r7
    0 s2 l0 a  `5 _& q$ h2 J; c84 H# y7 U, Y+ n; L9 N
    90 R4 S- J9 \/ X
    10
    * G9 Q# `9 p# a) a4 f2 ?11+ k, ?1 ]" |0 m. e4 h+ V7 f2 V
    12
    - f5 [  V( n  n/ Q4 oqcut函数。其用法cut几乎没有差别,只是把bins参数变成q参数(quantile)。
    ' o+ O) X9 n, i0 U" C$ [q为整数n时,指按照n等分位数把数据分箱) \4 q2 z" C3 z0 h& g
    q为浮点列表时,表示相应的分位数分割点。
    $ u/ V4 d* T6 L  r  Es = df.Weight
    0 k' j+ v5 z% t7 ~& d, d: L) h0 |
    pd.qcut(s, q=3).head()) e: h4 v$ E" Z' d+ o! o
    Out[47]: ! D. ^5 @5 F. a$ I: z$ _8 G
    0    (33.999, 48.0]) t9 F# t9 u% S& f9 I
    1      (55.0, 89.0]
    0 T0 D: l3 F7 R" v2      (55.0, 89.0]
    " u3 A7 O8 ~- G; Z4 B3    (33.999, 48.0]7 I: ~9 f% J% g7 k& Y
    4      (55.0, 89.0]) P3 J/ C& V' r5 b* A9 k5 M  f
    Name: Weight, dtype: category
      z" Z/ D5 f/ ]$ k3 R9 I! cCategories (3, interval[float64]): [(33.999, 48.0] < (48.0, 55.0] < (55.0, 89.0]]" w: n( m2 g% v  D6 Y
    . S4 v8 W. I9 l! ^7 Y. K1 y7 A% ~7 c. X
    pd.qcut(s, q=[0,0.2,0.8,1]).head()
    5 Y& T6 M/ [4 z, y" k3 mOut[48]:
    0 W; T  {) w& m0      (44.0, 69.4]
    & m) Y/ B' i8 L1      (69.4, 89.0]6 z7 w2 C  }4 G: ?# x2 U
    2      (69.4, 89.0]4 [  [2 D3 l! O# w! y" B: p
    3    (33.999, 44.0]) f1 v6 i) F# b& @* _1 I+ i* d
    4      (69.4, 89.0], M6 G  W/ U, V, j# P
    Name: Weight, dtype: category
    * n1 \4 C! {# a+ H; M* LCategories (3, interval[float64]): [(33.999, 44.0] < (44.0, 69.4] < (69.4, 89.0]]
    ! v, v6 I9 Q* N, C# s7 @! ]# ~1 w6 B4 e0 Z, I; e+ u7 a* x/ J
    19 k4 V; ?- H- U6 a; o. f" k
    23 ?8 G" i+ d6 |
    3
    1 [: o- J0 Q: C+ n( v) `4
    9 d$ _1 E# j; U  e' S6 \5
    , ^  a) L9 w* J2 z" z3 N6& m7 X# b/ t1 V6 q, r
    7, Y& B8 E( w* v  ?0 o7 a; n
    8
    " [# E3 y6 Y4 c! }0 [& q5 K98 y' |* m& s1 [( Y
    10! h* l) c8 x- k, h% U
    11& v( X, S" E9 S8 ^# z  N8 D; W
    128 c- _; w" |* P$ U0 ^5 q: ^! c
    13- z- I2 a( u( M2 c) ]! v  y1 ~$ d
    14
    0 v2 h' D$ c! v# D- E8 z2 l3 e15
    2 i7 s- x% F% p7 |16  E+ V+ N6 z  P* B% q, N' `
    17
    * N; w( h# W- c7 I/ \18$ j# z2 h, k6 l& x. n, ]. j
    19  H$ ?2 F2 Z* w8 ?2 Y# P, A
    20
    & i- r+ B0 D8 K6 y% N# t211 Q( Z7 |1 F) E8 Q" O* C' `
    9.3.2 一般区间的构造
    2 [% W5 K& D/ ?$ w7 y8 {; X: E  pandas的单个区间用Interval表示,对于某一个具体的区间而言,其具备三个要素,即左端点、右端点和端点的开闭状态。
      G$ A2 z' v4 {9 X
    ! i% X, m  Q. _3 J) i开闭状态:包含四种,即right(左开右闭), left(左闭右开), both(两边都闭), neither(两边都开)。! @. K: G; t- \& |, ~
    my_interval = pd.Interval(0, 1, 'right')( n, d' p1 Y; k
    . B5 i- Y! e# ~7 Z/ F$ y. D
    my_interval5 G7 Y( k% A( z% M' S* u9 z. V4 J
    Out[50]: Interval(0, 1, closed='right')5 p+ O. E& P% I# X4 P
    1
    1 f% B0 y2 n4 p* W9 U( C  a2: u2 M$ u+ |" S; ^# c3 P
    33 z3 W- A8 \2 ^, O; t- E
    4
    6 K% S; X* Z1 u; s5 {区间属性:包含left,mid,right,length,closed,,分别表示左中右端点、长度和开闭状态。
    ( R2 `% Q5 |5 T2 c2 c; ]使用in可以判断元素是否属于区间
    $ ~& }$ e( m, k! A. R% O用overlaps可以判断两个区间是否有交集:. a, i' P8 _. R
    0.5 in my_interval" Z  K, k! T" j+ n3 F

    2 q  s8 S! C$ |1 `) }$ N+ v; pTrue
    ; x- ~) Q! p& G9 U1 e1% V1 f( }( H' ]; v, n1 O7 l& S2 R& U
    2" r) q0 W2 U0 z+ y5 ^- T& e( U
    3
    1 c/ ~. q1 S% b, Z( X2 Ymy_interval_2 = pd.Interval(0.5, 1.5, 'left')
    & H0 o. A* k5 ^- z' a$ tmy_interval.overlaps(my_interval_2)1 U4 A& Y5 W. h
    7 v* i9 M. N- P+ J9 _$ h( w8 ?4 ]+ |
    True+ g9 [0 c2 S" r' b4 O; R! e
    1/ Y1 j+ S4 i2 p# D' T
    2; y) k4 N6 a. {  o! l7 _
    31 [( m  j" G! ?* r8 L" \
    45 J% d* k) ^: J% |
      pd.IntervalIndex对象有四类方法生成,分别是from_breaks, from_arrays, from_tuples, interval_range,它们分别应用于不同的情况:
    1 i+ `3 M2 W2 x) b- K. D6 `  S, ^# ]! |! e; Q
    from_breaks:类似于cut或qcut函数,只不过后两个是通过计算得到的分割点,而前者是直接传入自定义的分割点:
    4 ?6 R0 ]* z1 ?& W* _* I/ q  B8 L/ fpd.IntervalIndex.from_breaks([1,3,6,10], closed='both')
    3 j9 p$ C- [- H
    # W1 `- N2 D6 t% m3 KIntervalIndex([[1, 3], [3, 6], [6, 10]],9 D2 H, m9 F7 v0 _' i
                   closed='both',
    : P( A' J  {, I4 s/ ~               dtype='interval[int64]')
    4 w' }  Q9 j3 Y" @9 f1; A: ?# Q6 e/ J' b- j
    2
    ; t7 C0 ]3 A8 U% d! @& I4 P6 G3+ C, w; C5 U0 ?2 o
    4" U2 ]% L: [6 Y& ^4 K! v
    5( E' r$ o3 q4 R( K8 t
    from_arrays:分别传入左端点和右端点的列表,适用于有交集并且知道起点和终点的情况:* Y6 n/ ?7 ?5 W: p1 H
    pd.IntervalIndex.from_arrays(left = [1,3,6,10], right = [5,4,9,11], closed = 'neither')
    8 K$ k! J/ {6 K* e0 A3 z% C8 M  v) P4 L/ P( M
    IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],9 k! y2 b5 R1 P" k1 l
                      closed='neither',
    9 x! k" K2 i% I) u9 K$ a; _- o                  dtype='interval[int64]'). [" k! y# c" V( o1 b
    1
    ' j9 g- m/ T+ ^+ R: s2) A( u' x  V/ u8 l/ C! w
    3
      h: y# F9 j* Z/ h4
    - O6 I( h: |- G) T8 x* q- D50 \, ~/ `+ w# q+ P) o$ z: F9 t; k! x
    from_tuples:传入起点和终点元组构成的列表:, P% k' ^: q3 x3 Q; Y9 ~
    pd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)], closed='neither')! Y9 A3 C" G6 n' _' p
    8 u" x; R. z7 O4 r. ]+ E# \7 E
    IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],* `* @3 C0 p. ]5 W
                  closed='neither',
    5 n1 f4 v" |) q7 R* @8 @% b              dtype='interval[int64]')
    ( o1 S+ i  I, {) m1
    $ C( j2 T0 [: K5 l" y: M2( }( A3 w! J2 r/ @* e
    3+ e  L$ _  L9 e, E& ?5 Y( r: Q& w
    4* H# w- W7 q- D$ N# `
    59 E7 H7 H& `4 X  [$ G
    interval_range:生成等差区间。其参数有四个:start, end, periods, freq。分别表示等差区间的起点、终点、区间个数和区间长度。其中三个量确定的情况下,剩下一个量就确定了,从而就能构造出相应的区间:3 g# T5 W2 ]3 d" H# M& v+ _# A
    pd.interval_range(start=1,end=5,periods=8) # 启起点终点和区间个数
    ; m6 k0 R* A' C! H1 ^0 SOut[57]:
    ' G  w, l* m9 Z2 CIntervalIndex([(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]],/ ]5 ]- C" E; }, M( r
                  closed='right',
      ^3 E* d- ]5 L/ V: _2 W; d              dtype='interval[float64]')
    1 I* b: k% E% W& D9 u: r2 W& E' Y
    2 u$ A: P. f$ c7 O6 \' c. k! apd.interval_range(end=5,periods=8,freq=0.5) # 启起点终点和区间长度# R& D4 A1 d+ |2 P
    Out[58]:
    ' S( r2 @2 J$ \( h, k# JIntervalIndex([(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]],8 U& N! L  H5 _7 V7 @; l
                  closed='right',
    & P6 E. ~) x- v5 r: A7 r              dtype='interval[float64]')1 o$ F" }! R5 Q
    1
    " p* F; C6 g  ?! H' ?5 l2# M' ]" B( K: W' F
    3
    , u( X9 n2 W) ?! ]3 {4( ?( ^7 e. m& R, t# W( u  |/ T4 F* T
    5
    ; f4 u0 y, i( T/ t$ C7 P2 O0 D4 v6
    - C  P. `* H  {( ~7; ~4 _5 {1 l! O2 I5 f$ _
    8
    & X* v! i2 Y! m: q0 [9
    2 K$ ~2 T; w$ c9 r10! \: R% {& c3 d0 `! J/ g* d
    11
    ( p% \0 F* {7 ?6 f【练一练】
    1 v$ }2 b7 e- ^+ q6 W+ g  无论是interval_range还是下一章时间序列中的date_range都是给定了等差序列中四要素中的三个,从而确定整个序列。请回顾等差数列中的首项、末项、项数和公差的联系,写出interval_range中四个参数之间的恒等关系。, ?1 Q4 ]: T/ w, ~! f) L. l

    9 W: ~  Y$ V3 X! l" i/ W* {  除此之外,如果直接使用pd.IntervalIndex([...], closed=...),把Interval类型的列表组成传入其中转为区间索引,那么所有的区间会被强制转为指定的closed类型,因为pd.IntervalIndex只允许存放同一种开闭区间的Interval对象。
    ; Q. i  |& _7 F& K
    7 W! D0 i6 X& |. U. O5 y0 Dmy_interval
    * Y* Z7 f3 I" GOut[59]: Interval(0, 1, closed='right')( `7 C7 O9 u$ c8 Q$ l
    1 O9 \& r% A1 {. D) v
    my_interval_2# V4 A6 Y8 d4 v6 a" z
    Out[60]: Interval(0.5, 1.5, closed='left')
    # ]. o$ y3 P4 v
    6 p& h- P$ |9 E0 v: s  q( spd.IntervalIndex([my_interval, my_interval_2], closed='left')
    8 e% X# d7 U" T6 }6 kOut[61]: ( u! x9 ]3 q. `# `7 Y
    IntervalIndex([[0.0, 1.0), [0.5, 1.5)],
    - l; i8 `- V+ O( e% x1 f- ^# Q              closed='left',) o# p: d$ Z1 i" n' a0 ^( j/ N
                  dtype='interval[float64]')
    # j# _- G1 k, m% {3 W% p1) j$ D- k- V/ {; w
    2
    ( [& D6 h, v+ v% \$ G6 [3$ Q9 e* B) H5 I* U) g% u7 c1 f& z
    4
    $ D2 i8 L$ V+ f/ B4 M& P5- j0 ~8 Z: s, _
    6
    8 I1 L+ {3 C0 j7
    2 |* J4 D1 j; i; C8/ j- a) c2 E$ Q
    9
    3 m& r5 j# U7 v! o2 Z. B103 u( s) V: x) S1 [
    115 W' m! h( o* N: B5 z* {& c
    9.3.3 区间的属性与方法
    9 F" c/ |9 O* t- W" A  IntervalIndex上也定义了一些有用的属性和方法。同时,如果想要具体利用cut或者qcut的结果进行分析,那么需要先将其转为该种索引类型:
    ' V; l/ j, _$ S! Y" M2 c8 `/ h
    4 a. V# N: P& L9 Z0 D& w" Ys=df.Weight# |' u$ x! b; H
    id_interval = pd.IntervalIndex(pd.cut(s, 3)) # 返回的是每个元素所属区间,用具体数值(x,y]表示" p+ |* L# V4 C# j3 j) H8 N1 @2 K# E
    id_interval[:3]
    $ |. b7 X/ \" b  x& R& i  K3 x" C- B) C; ]  e/ [
    IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0]],1 b) e4 }& i( t* o( N9 ]
                     closed='right',$ Z3 w6 |- [, u9 s
                     name='Weight',8 G: b" u' q+ ?+ K
                     dtype='interval[float64]')" _0 T% s5 ~: M0 J0 e3 u
    1
    5 z, `* H/ C! R& D9 P$ m2  g3 c9 t7 I' w2 n1 n- y; y* U2 j
    3! Y" o" n; ]) W! F# m7 ~, ?
    4  G# D/ j3 m, ], B7 ^) m/ k  {: E
    53 [9 S2 w$ R& q, t& q' U8 R
    6
    / Z. J) J0 G6 C( l3 B& Z' Z7. g+ g( r1 ~7 {" ^
    8
    0 Z* Z; u: u1 H+ v7 p" I3 y与单个Interval类型相似,IntervalIndex有若干常用属性:left, right, mid, length,分别表示左右端点、两 点均值和区间长度。3 \& K# K1 N2 o4 i5 d5 x
    id_demo = id_interval[:5] # 选出前5个展示
    - t/ d  e: e0 N) L
    , }$ \) h; I# e! |& Y) o1 Y) E) ^1 \id_demo# y' ~( f% \0 o. m, j% A% @1 |# I+ p
    Out[64]:
    2 y% I. H; c0 FIntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0], (33.945, 52.333], (70.667, 89.0]],- D3 n& Z8 z( F
                  closed='right',6 v; i2 }4 V2 c/ h
                  name='Weight',
    ) y) N. @8 F" H2 ^- d              dtype='interval[float64]')
    1 `, W6 D/ U( I5 v" }6 D% R0 T6 T: Q% I' z" F! f3 `2 m
    id_demo.left # 获取这五个区间的左端点! U5 |. }4 Y, a8 l6 h* v. O; _
    Out[65]: Float64Index([33.945, 52.333, 70.667, 33.945, 70.667], dtype='float64')
    + Q& z! l4 j4 K0 r- g1 _! [5 H2 `* }& O! ?' W2 C
    id_demo.right # 获取这五个区间的右端点
    ( O1 ~6 Z; A# ^) v6 a8 fOut[66]: Float64Index([52.333, 70.667, 89.0, 52.333, 89.0], dtype='float64')5 P* k( p) B% X' V! L
    ) \! I! a# J) u5 K% z" z
    id_demo.mid
    5 o( M% }7 {# ]2 |" @+ a9 jOut[67]: Float64Index([43.138999999999996, 61.5, 79.8335, 43.138999999999996, 79.8335], dtype='float64')4 E! ]2 F7 P9 ?1 `# R( C# D( b+ c

    3 @6 c/ s5 v9 c: `: s8 B; Rid_demo.length5 y8 z1 R1 q- J/ E# G7 Y% x" F: f
    Out[68]:
    ; k% R  G( e/ u0 b7 P$ ]Float64Index([18.387999999999998, 18.334000000000003, 18.333,
    - x' Y' F& Z) i1 _# `6 ~5 k              18.387999999999998, 18.333],0 s; m; c/ w, b$ i
                 dtype='float64')& Y  r/ m( o7 [. n% H
    4 y7 j& l- s- @' F: A! a
    12 k6 L" A: \5 t+ D4 n9 ]5 P/ [
    2$ P' K  u. E1 j6 Q. K0 v; F
    3
    ( R- |; A. }& P0 w. T2 |4% F" n. _0 d1 U/ Y, Q1 @
    5* J2 h* U7 {! v
    6: K% n, |: e7 \$ p2 p
    7+ S* V' ~2 }. S2 v* I  U: s
    8
    / D  ~% ~# A1 |6 p8 Z, F9
    8 Y# X& V% T) _! r, r+ O104 s  Y9 x9 o1 F+ L( ^+ |
    11
    / F/ u; D7 x% y12$ k! }" s2 y. |% T" v6 U; v; n' H/ ^
    133 r# Y6 D8 c( }* Z4 ~
    143 b" n6 @% \: ?% y' s7 @
    155 e- S! M# B! }  F* q4 H+ i: }4 Y
    16
    9 A3 p& ~: m# F  s17+ ^( y4 t& Y" `9 @. H! P& c+ [% ^" ~
    18
    ( J9 c; Y' g2 q$ n& A6 n197 t; l4 T" h; B
    20
    7 ^* u& `, b% R: }21
    ' `5 l. _" }5 L$ M8 V( N22$ t% m9 J( J2 h. M  g
    23
    4 v) p! l4 c0 {+ c4 @' MIntervalIndex还有两个常用方法:
    9 t3 s5 t& @3 f/ Ycontains:逐个判断每个区间是否包含某元素
    , j) D# a) E* E1 T' K+ Aoverlaps:是否和一个pd.Interval对象有交集。
    & S* Q$ L0 F+ b2 e. o" Cid_demo.contains(50)
    # B- J9 @" u! q; O3 C8 j$ yOut[69]: array([ True, False, False,  True, False])+ Q+ {5 Q9 S8 f  t- O1 S
    - z6 j# ~3 O/ a: N
    id_demo.overlaps(pd.Interval(40,60))
    ) H$ @9 U# ?1 H1 \& }, jOut[70]: array([ True,  True, False,  True, False])9 z- C% O& d# H# l1 n
    12 g3 }2 N( R- w* U3 ~8 Y' N
    2
    & f0 N* }+ f+ U; n2 T3
    3 {" F, _7 n- I4
    6 Q  d( a0 |  I" a0 L2 M( n. C0 C5
    1 J7 `' F; M5 _9 R9.4 练习" q8 P- G  H- l2 z7 Z6 l
    Ex1: 统计未出现的类别3 \2 V6 L* g; o( t+ E' {; Y
      在第五章中介绍了crosstab函数,在默认参数下它能够对两个列的组合出现的频数进行统计汇总:
    ; k5 k( U9 I% f0 {! ]: L0 w  P% P5 s1 m" ^
    df = pd.DataFrame({'A':['a','b','c','a'], 'B':['cat','cat','dog','cat']})
    0 Y. T* y: o9 ^pd.crosstab(df.A, df.B)
    * f/ Y; @7 N$ n  z) N+ R: I$ Q, ^5 q; ?- Q' Z) E5 t! n; P
    Out[72]:
    : D7 k% f8 t& s% X+ I8 lB  cat  dog& T2 T# _; u6 P4 X3 G, C8 E. m2 w
    A         
    / o$ y' J; r- U6 k9 va    2    0& W# L; d2 q+ v
    b    1    0+ F- S& o3 K8 j  J) y4 P
    c    0    1
    ; [6 b  u! D8 a3 j1/ x. v5 f7 Y2 G: T3 s' t
    2
    ( j0 b' T' `% G$ l3& X# y& f* a1 ]' e% z
    4
    * ~* \  w# F: \. x$ Y7 M52 Z8 M9 W2 X; F3 F/ |
    64 G: v+ A9 \. a
    7
    . u. ?# l1 q2 w/ \; g3 a; Z6 G8% d( q: E$ L6 h3 U0 M& N5 d
    9
    - P1 R+ \1 Z6 E  但事实上有些列存储的是分类变量,列中并不一定包含所有的类别,此时如果想要对这些未出现的类别在crosstab结果中也进行汇总,则可以指定dropna参数为False:
    ' }# |4 Y5 j" k0 _* L' X
    ) i$ C2 g  x; a( x( l3 C. xdf.B = df.B.astype('category').cat.add_categories('sheep')
    ) ]1 o8 q) e, l3 D9 P; N% npd.crosstab(df.A, df.B, dropna=False)0 s8 `7 g/ W& ]# Q' r* Z
    ) f; z# A7 z6 j3 `. X+ F( w
    Out[74]:
    # `  C( w6 T! j5 m$ u( ?B  cat  dog  sheep
    # R# g: ?4 ?- N" t1 [3 v+ gA                   }& p- f3 d1 P: r
    a    2    0      0
    " _2 d. {' N1 Db    1    0      0& B# O' J' C2 b8 ~8 [, h
    c    0    1      0
    6 @  d6 y) M/ Z0 j' ~1! n$ u' q6 x6 w" u
    2' v( ~; U7 q& c
    3( B, s# Z) e: e/ E7 w
    4
    . k- X6 s; K( _% v7 S5/ A* Y' J. `4 k! W3 {3 x2 [
    6
    0 r* D% G8 k5 F6 L; E0 z7
    + t! p) L1 H$ w( m2 U8$ o# B3 Q/ h6 \6 U- K
    9( G+ K! H: |) i0 c
    请实现一个带有dropna参数的my_crosstab函数来完成上面的功能。
    " m; b4 X0 s7 C: M( k7 X& \( Q1 p, J/ t; g/ i
    Ex2: 钻石数据集3 l4 T2 H6 X+ B2 @! @/ ^
      现有一份关于钻石的数据集,其中carat, cut, clarity, price分别表示克拉重量、切割质量、纯净度和价格,样例如下:
    2 o. I5 \# V3 ?3 t6 ^* n1 f
    % i' G/ ?! L  o: V8 O/ hdf = pd.read_csv('../data/diamonds.csv')
    + m2 U5 N6 E+ }/ n5 ~( k" jdf.head(3). y9 Q5 T2 d3 z* P1 m0 e; U

    - ]& K. l& W& ^Out[76]:
    1 o8 x% |4 d6 w% q& c" X- |9 u   carat      cut    clarity  price
    . D# D. q1 K( `8 N/ K( J3 d0   0.23     Ideal     SI2     326
    9 r/ ?; C7 X4 U3 }# j1   0.21    Premium    SI1     326
    ' _  o5 y; [! K/ R8 V, C2   0.23     Good      VS1     327
    + q7 d% c$ v* e  G1
    % {- s- e% X; C. v6 P5 f8 [& P2: z$ E# E" ]* D
    3* k: @: s( K( Q0 {; G& {
    4+ L! S, y/ `1 H
    5
    + B; `: s, v/ [- l7 H/ V5 ?9 i( R6; D' n5 L  H7 ?. r# q! ?
    7  |. b% P. S2 {* }3 a
    8
    # ^/ ]; e. t$ Z3 {/ r分别对df.cut在object类型和category类型下使用nunique函数,并比较它们的性能。  y+ k1 d: `6 b& G  K
    钻石的切割质量可以分为五个等级,由次到好分别是Fair, Good, Very Good, Premium, Ideal,纯净度有八个等级,由次到好分别是I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF,请对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。
    6 Z7 m/ M, c% C" `分别采用两种不同的方法,把cut, clarity这两列按照由好到次的顺序,映射到从0到n-1的整数,其中n表示类别的个数。
    3 l+ h  c, \% Z  S; `" t对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。
    0 x+ z1 ?% P1 K* z6 @- V第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
    ; a+ ~! J7 ?5 D7 B$ d; m对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。
    ! |% F  l. f$ R. D) ?7 {: T" ^先看看数据结构:+ \$ e, T& _: J$ P; T, j/ [9 i
    , D" [" j( U* i! n7 `9 q. z
    df.info()3 `+ Q3 b# x3 i. Q: d4 v* w0 f
    Data columns (total 4 columns):! y$ f. c* n% \9 a
    #   Column   Non-Null Count  Dtype  
    / Z% @. G2 j& y---  ------   --------------  -----  ; e# L# k9 p* ~. \; O  f8 _
    0   carat    53940 non-null  float64
    + g2 Q) x, N. n0 _# M4 ^8 T1 g 1   cut      53940 non-null  object 5 |! \: `$ f# G, X' u; \
    2   clarity  53940 non-null  object
    , j$ }& d1 X* `1 j 3   price    53940 non-null  int64    z$ f/ i5 N8 g+ x: ~
    dtypes: float64(1), int64(1), object(2)
    # _9 Z6 M- m" y8 C3 a0 @18 @! Z. d6 l' m# l# Y; m5 G
    2
    ) v% e% ^3 @) x( n8 J  s3
    & k3 @" l: \0 I+ V) A4" h# q# C- c, I0 H9 p4 e1 G6 R: |
    5
      i2 `5 \* @1 C4 i' H5 r6
    / F( [& v. L% e- h4 q. g72 U' k7 p- l! Y: X. ]
    8$ k" Z- Q" I5 {8 z+ \( ?( i# Y
    97 f5 X. C5 J. R! q1 p
    比较两种操作的性能
    ' u# }1 O( z; k" U* _) M; f, z& F" _%time df.cut.unique()
    & h/ x1 C. B' X* a. @- q  T" M" m/ s! Z" t8 [5 Y
    Wall time: 5.98 ms2 l. I2 A& x. b: ^9 c* U6 ^
    array(['Ideal', 'Premium', 'Good', 'Very Good', 'Fair'], dtype=object)& r: a9 I6 i3 [( s
    13 v! m$ `$ E# `8 |, z1 q
    2
    / o1 B1 ~- n9 d( Z% z8 M* p3
    : c- P. w6 x% M, G9 q, K4
    : g- c; m5 g3 P1 o, _) Q%time df.cut.astype('category').unique()
    3 P% K5 r1 w7 Z, Z( p; z1 L5 j/ L: c/ \3 T6 s
    Wall time: 8.01 ms  # 转换类型加统计类别,一共8ms
    - ]; B9 \3 \( d1 ]1 I8 N" u, s3 s; d['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    & K4 w, F, ?5 L+ J" H; WCategories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']' m) o8 d5 L7 ~4 S. \
    15 @( ]$ K" B7 ~' o4 ~
    2$ f0 n! L# W8 \7 P
    3
    8 o: X) D7 d. W. a3 b/ g4. J4 C5 @; Z3 `& s6 N2 w2 f
    55 I5 ^; i1 k& o& m; g
    df.cut=df.cut.astype('category')2 |6 N3 W9 u% \$ G! A
    %time df.cut.unique() # 类别属性统计,2ms. C3 H" f* d! @
    $ E, W8 o5 A' k( I4 k1 ?. Z
    Wall time: 2 ms
    6 }- }5 s9 K' `& |, `['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']8 t& _: R4 T1 w" S! L& T
    Categories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    ; I% }6 I9 B) M" z2 t8 j! q7 Y1
      c4 N& r$ f$ Q+ l6 n8 s" I2' t3 O; u! g3 c1 I4 P
    3
    ! i/ T- {0 U# o9 X4+ j1 x6 h6 w0 l2 l, |
    5) a* z3 @4 F3 i; }( H2 I$ X
    66 a0 V  [; ^6 N& \, v: ]) m
    对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。7 |- a. n& A& [2 M' u7 I7 K. e' B+ {
    ls_cut=['Fair', 'Good', 'Very Good', 'Premium', 'Ideal']" `. D( h- d$ ^3 G: {
    ls_clarity=['I1','SI2', 'SI1', 'VS2', 'VS1', 'VVS2', 'VVS1', 'IF']5 Q) @4 Q$ W9 ]& p7 z5 c( u: w
    df.cut=df.cut.astype('category').cat.reorder_categories(ls_cut,ordered=True)  # 转换后还是得进行替换
    0 M9 D* P% L" r% o% a' D+ Y4 Cdf.clarity=df.clarity.astype('category').cat.reorder_categories(ls_clarity,ordered=True)
    8 T$ _6 C- y! k+ i
    4 x+ C4 E0 u2 v6 ^5 r( ddf.sort_values(['cut','clarity'],ascending=[False,True]).head(3)9 c* ?. H$ {0 e8 I& ]

    9 v) B- L4 Q8 G4 a8 {0 f        carat         cut        clarity        price% v( M& K: q% H3 p9 Y
    315        0.96        Ideal          I1        2801
    % p6 L' W4 k3 P( G: g, T535        0.96        Ideal          I1        2826. l+ G- |1 X6 f2 h& Z0 K' P6 q
    551        0.97        Ideal          I1        2830' \. {1 P( l2 e# V4 h
    11 L  M8 m5 R" Y0 {0 u1 ?
    2
    ; t8 O" j6 N  m( B3, ~$ }7 G6 \! a/ `. n2 Y. @7 ^
    4( {" z1 A9 U0 j! Z. M* R) O( K
    5
    2 {) M' `2 C9 z/ o" n1 Q3 g4 E- F65 W4 v1 L  M, v- y5 P1 `  L) P* j
    7+ l& y: Z1 R9 y1 r( C$ [+ c
    81 I  [# [8 Q; m0 k6 ^3 S# g
    9% Y2 d8 a( C, n( c& O
    10% N- n8 M* F1 I% W- w" g2 r0 k$ ~# z
    11
    ; N7 M0 z0 K/ o( y8 [/ y/ ?5 X分别采用两种不同的方法,把 cut, clarity 这两列按照 由好到次 的顺序,映射到从0到n-1的整数,其中n表示类别的个数。) Q/ U6 z' F7 [6 t
    # 第一种是将类别重命名为整数
    $ R# w6 B$ O3 c6 ]' Bdict1=dict(zip(ls_cut,[x for x in range (4,-1,-1)]))1 W5 o6 q' W, C, g( g0 _( Y
    dict2=dict(zip(ls_clarity,[x for x in range (7,-1,-1)]))5 [* U5 \0 ^0 R8 i

    1 |. l1 r. K7 |4 W6 F7 G1 Tdf.cut=df.cut.cat.rename_categories(dict1)( o. E) `1 I( u/ \  a+ ?' d& ~
    df.clarity=df.clarity.cat.rename_categories(dict2)
    + _6 G. @  v0 t. @df.head(3)
    2 b' z8 `( @% \$ o/ ^5 v1 |4 X
    8 ^, r5 N8 u0 d' j9 z4 i5 `+ {        carat        cut        clarity        price
    " t& I9 S" M* P0        0.23        0          6                326
    , V0 j( S7 r( Y- U1        0.21        1          5                326; ~* j" V7 {  |
    2        0.23        3          3                327
    7 {: c) g2 E' d14 f$ ^: y4 ^6 ]% J7 s& y8 D
    2
    ( c+ u8 G# ]) h4 e" r$ `38 g  g/ D% M# d6 W. {8 l
    4$ N1 B5 H9 h1 I. s, E
    5
    ; I8 }$ h3 G; b! f; b, n& z* q6+ K+ Q' V. }# |* E8 [
    7
    5 j6 w* k$ @) C5 z9 @! U: r8; {! x/ v3 W4 p0 i5 }
    9
    ' {# V7 h% r( d! J& ?( r  o10( q/ w" n) ~: Y" V
    11
    . ?* F) ?3 H4 c$ |) I9 B12
    ( {" S8 j6 p8 `6 e3 ?$ ^# 第二种应该是报错object属性,然后直接进行替换
    1 P4 M, K5 o0 J4 B) ndf = pd.read_csv('data/diamonds.csv')* i) B) P+ }8 e( Z* J3 P1 A/ `
    for i,j in enumerate(ls_cut[::-1]):# ^! _! H( k1 f* Q$ Q$ x
        df.loc[df.cut==j,'cut']=i * x( t/ O  W% w+ _" M
    4 f6 a/ k! A9 ?
    for k,l in enumerate(ls_clarity[::-1]):
    6 a( \: ~5 z* T- ]! w: b2 [* E    df.loc[df.clarity==l,'clarity']=k0 j, Q+ q2 Y. H) S# b
    df.head(3)6 w9 V8 }9 y. S- Z/ _: p9 j2 p
    : C- i4 F# Y: r2 U: Q; a
            carat        cut        clarity        price: a) C# y/ r/ S% z9 y# F
    0        0.23        0          6                326
    . V' i5 c2 i) Q9 T: G  A% ~  p1        0.21        1          5                326( X  w) R6 F  m2 {
    2        0.23        3          3                327$ {7 S: a, |" `6 b8 H: l3 p  t
    1
    8 N1 y0 ^4 x: _: d, I1 {; N2, y. J" y5 E+ |2 y
    3; l+ q, y$ [9 W- s" B) o
    4& u4 U- r; i- M8 t# _
    5
    , b) F6 D+ C( O! l0 {67 v4 y$ R' ?# W) {
    7/ k% P0 V$ J" ?! M
    8
    7 Q8 k$ j1 D2 d5 O2 w9 W9+ z2 e& U- M& J5 g1 k
    10
    ! v$ x1 p  s! K2 B! r& O# w5 m113 f  m( U: w9 f" I
    12
    . Z8 {: S" N' \5 U0 A13
    ( m8 }6 A1 Q3 i2 s& T7 [对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。5 I& _4 P3 b# i  ~5 K2 d
    # retbins=True返回的是元组,第一个才是要的序列,第二个元素是分割点/ O% Z+ o3 N. I" G  `
    avg=df.price/df.carat
    3 X5 `- S' _" e) g- x" h! Y/ Z3 r  f* X, p
    df['price_quantile']=pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],
    5 [  B0 s+ Y& }                              labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]) v8 J% [- J, d9 T
    3 Q: k, m: ?5 L( L  |
    df['price_list']=pd.cut(avg, bins=[-np.infty,1000, 3500, 5500, 18000,np.infty],
    / G# k3 x  v& V3 l# y7 K3 |                              labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]
    $ e# N; t, ?& u! s8 i2 d4 F5 K. Pdf.head(): B/ h5 p% I  Z' u
    1 q' e. v6 J( I" _+ u% L
            carat        cut         clarity        price        price_quantile        price_list8 J$ z+ Y2 d" q: k5 c
    0        0.23        0                6                326                        Very Low                Low/ R+ c1 B& U% j9 _, V" W
    1        0.21        1                5                326                        Very Low                Low4 H% e6 L& X! \: W
    2        0.23        3                3                327                        Very Low                Low' T) p% Q2 h: K/ n/ L
    3        0.29        1                4                334                        Very Low                Low# S+ d. r5 g, b& ?- L) X% U/ n, [
    4        0.31        3                6                335                        Very Low                Low                                       
    4 Y6 V: p" A$ O+ k1 k
    9 n' w, `4 j* P% F1+ F7 R( d4 B- ^  k
    2- i$ N# J" v. Y' g6 I$ l0 r
    3
    / A9 O, @+ ^/ k9 b4. Y( N) Z# x' Z+ h6 Y
    5+ `) _% m; n$ @- o0 A! O9 g
    6; h$ Y1 ^: J0 E# a/ I8 u
    7. T6 A- T+ d3 L, x) ^
    8% v' d' K4 P% j! P$ d$ a3 v3 G- u4 J
    9' O$ `2 p8 X6 v: e# G3 S: i2 v
    10/ r/ ^, [! f# k
    11/ t" N; z& B( y& i5 ^/ w  T# K) }
    12/ t! P5 r& b9 U; s/ }$ B& ^
    139 Q: `/ q% O) E6 d! d: v' Y) i) V# d0 ]
    14- E; S# f1 }+ q" x. l
    15
    % E& Z, `$ E/ F# b& p16
    3 l9 W( @; S) ^7 }分割点分别是:
    ! n, K9 K+ k/ o  y( l8 s( l$ O7 T
    6 z: k; s9 k! K4 S" `" w% F" narray([ 1051.16 , 2295. ,  3073.29,  4031.68, 5456.34, 17828.84])
    3 |0 U- p6 n1 }6 S. |" b% `array([  -inf,   1000.,    3500.,    5500.,   18000.,    inf])' I$ S: Q0 V; h3 d- I+ v8 f
    1
    / x) K4 n0 D! N( T27 w) q4 ^: F& l* {
    第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。! R! p  S+ F! \7 p
    df['price_list'].cat.categories # 原先设定的类别数
    & `6 v5 D9 `  mIndex(['Very Low', 'Low', 'Mid', 'High', 'Very High'], dtype='object')- L. `/ f% e4 V3 K7 E
    2 B" y0 a" J' _0 e4 S
    df['price_list'].cat.remove_unused_categories().cat.categories  # 移除未出现的类别
    * c) M5 R; p3 V: _3 sIndex(['Low', 'Mid', 'High'], dtype='object')  # 首尾两个类别未出现
    ) A' x; U/ Q: h16 B3 l" {1 d/ ?
    2
    2 W. h& f9 }3 b7 W  |# R0 L3
    2 j$ v& A$ M) ]% [2 X: K1 p4
    2 r; t9 W6 N7 X' ~; F* Q50 r, J) g6 _, B. R) g8 o
    avg.sort_values() # 可见首尾区间确实是没有的
    " }4 N# Q+ r% g( l/ Z7 S& B; @31962     1051.162791
    2 D! L" z! A# P- {1 `8 s15        1078.125000- S8 K0 i# p" n7 ~+ O  P
    4         1080.645161
    $ Z) x  P6 ^: d1 _* ]4 q28285     1109.0909099 z8 p7 D3 W7 N: f3 e/ x
    13        1109.677419
    ) Y8 _% c7 c& ]: P% y             ...     1 x* K0 _- D+ E+ o
    26998    16764.705882, v& ?" P! Q; G3 |1 {8 A
    27457    16928.9719635 ]0 C5 D5 ^- m) Y, l8 ]2 [- \6 P! r, L
    27226    17077.669903
    % q& ]+ x) l6 A8 Y0 u, G$ C27530    17083.1775707 _5 ^7 @! I' r7 |* |6 M
    27635    17828.846154
    $ x  |8 T( b+ O% {! w  {# p( `14 U$ I7 }- V" E$ f9 h
    2: \# H1 H# u: R8 \9 m
    3
    - F0 |! q, T; w% t5 |7 m- C4
    ! P8 J4 U% z9 p% i5
    . T( d; ~. g1 @* S3 K% o, B6
    3 K) E# Z9 z! _2 k7 Q* a! h- s7* P3 s) I2 ~' l3 ?- p' Y# |- O
    8& ]" h' O* I) g" |+ g' a" z
    9$ y6 f1 S* X$ F9 z  G1 e( \
    10
    7 [4 ~4 ?- |+ q9 G/ w* }0 I& v11' q2 q5 D) ?: b
    12# M, m# f$ b+ R
    对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。# D7 K1 s4 m1 e, a0 C1 ~7 B" V
    # 分割时区间不能有命名,否则字符串传入错误。
      v) i8 }, t' X% P, sid_interval=pd.IntervalIndex(
      @. y* E+ _) U9 H- w( U* C( t    pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],retbins=True)[0]5 Q4 p, A* R8 r
                                )6 R5 ]! F. l2 j7 ?0 [
    id_interval.left
    # M/ @" q1 l6 Gid_interval.right2 m9 ^% c) v$ X# {
    id_interval.length                           
    ! ?% y! Y7 d5 Y2 F. r. M8 x) ]8 [$ @9 L& z1
    ( T, a. X3 K& l3 E22 Y5 I8 l/ I7 X+ s, `
    32 o% A  e9 C' }! Q" o% M
    4/ i! |1 c& V/ t
    5
    + e/ ^- j! ^; A$ J% i+ [6: A- J) A4 W4 A, S6 r* L
    7
    ) c, F" K* L; `& s% L! i第十章 时序数据
    8 l" w- J+ b! |& l( q$ {import numpy as np
    ! S. d, Q# \3 A3 x2 Yimport pandas as pd
    1 E" |% B5 a8 e/ }1" D! p4 z- w& ?2 B" j( y8 @" ^
    28 T3 X1 j9 Z+ h4 M. F% x! d+ `
    # w8 n) ^$ v1 }$ a+ U- C
    0 x7 l/ V4 w. R0 _; O
    10.1 时序中的基本对象
    ) ~7 g+ z# o7 l& h; w! z0 u' r  时间序列的概念在日常生活中十分常见,但对于一个具体的时序事件而言,可以从多个时间对象的角度来描述。例如2020年9月7日周一早上8点整需要到教室上课,这个课会在当天早上10点结束,其中包含了哪些时间概念?+ l% Q; m# _; f! E

    % d# t: I0 _, Y5 _# h; l会出现时间戳(Date times)的概念,即’2020-9-7 08:00:00’和’2020-9-7 10:00:00’这两个时间点分别代表了上课和下课的时刻,在pandas中称为Timestamp。同时,一系列的时间戳可以组成DatetimeIndex,而将它放到Series中后,Series的类型就变为了datetime64[ns],如果有涉及时区则为datetime64[ns, tz],其中tz是timezone的简写。0 P% n/ m% K9 P7 G2 y; |. C. F4 }

    ! w# [  ?) _9 f* J- F会出现时间差(Time deltas)的概念,即上课需要的时间,两个Timestamp做差就得到了时间差,pandas中利用Timedelta来表示。类似的,一系列的时间差就组成了TimedeltaIndex, 而将它放到Series中后,Series的类型就变为了timedelta64[ns]。
    ) n: q5 z7 H) h/ s7 t  k
    * ?+ |  o+ V4 P/ a0 X会出现时间段(Time spans)的概念,即在8点到10点这个区间都会持续地在上课,在pandas利用Period来表示。类似的,一系列的时间段就组成了PeriodIndex, 而将它放到Series中后,Series的类型就变为了Period。
    & x+ f' H% P! b$ o. {4 q8 U4 T* s" e0 z# I. y6 B, r6 P
    会出现日期偏置(Date offsets)的概念,假设你只知道9月的第一个周一早上8点要去上课,但不知道具体的日期,那么就需要一个类型来处理此类需求。再例如,想要知道2020年9月7日后的第30个工作日是哪一天,那么时间差就解决不了你的问题,从而pandas中的DateOffset就出现了。同时,pandas中没有为一列时间偏置专门设计存储类型,理由也很简单,因为需求比较奇怪,一般来说我们只需要对一批时间特征做一个统一的特殊日期偏置。  l9 q! C& I, K, V! |2 g; A' A

    ( B5 i$ E' E9 a* p) Q. h6 V  通过这个简单的例子,就能够容易地总结出官方文档中的这个表格:8 D  @1 h- p' y9 x( L, [
    5 E( _2 q; ^3 n2 ~0 L1 n) I
    概念        单元素类型        数组类型        pandas数据类型
    0 n$ d8 K' `" n- hDate times        Timestamp        DatetimeIndex        datetime64[ns]7 I6 p  p( g4 R, k" Q- k! Z  F
    Time deltas        Timedelta        TimedeltaIndex        timedelta64[ns]
    5 W5 R7 m" j, G/ t$ i0 dTime spans        Period        PeriodIndex        period[freq]
    # o# Y% i( j- x) y1 n* uDate offsets        DateOffset        None        None
    % Q, O' ], K+ }3 b  由于时间段对象Period/PeriodIndex的使用频率并不高,因此将不进行讲解,而只涉及时间戳序列、时间差序列和日期偏置的相关内容。* p; y& n& a& W3 W: l

    . p8 x& s) B1 {10.2 时间戳  A4 C+ C* |4 J6 G( x; Z
    10.2.1 Timestamp的构造与属性
    : N: U# o& D; K" {: V0 n9 R单个时间戳的生成利用pd.Timestamp实现,一般而言的常见日期格式都能被成功地转换:: U& e3 @% r8 F% e. J( ~" @
    6 H' y9 ]1 y- Y$ E, D9 ~
    ts = pd.Timestamp('2020/1/1')
    # e( f; A) c: \( h5 x
    1 h1 E  n! f! K) B- Its$ D, S; [- u1 M" G: f* o% Z
    Out[4]: Timestamp('2020-01-01 00:00:00')
    % s9 `0 K, E1 C3 l  Z  C, A
    ' T- H3 W6 M) Q' m  rts = pd.Timestamp('2020-1-1 08:10:30')
    # t$ @2 V2 p3 o' {! d8 q. c! U0 P' d1 P( ]6 \, g
    ts( i, C0 M$ X- c, Z- g
    Out[6]: Timestamp('2020-01-01 08:10:30')' k# v! J7 {: I- @5 f+ l% @% s; i. x) |
    1: |6 t: j6 r0 i+ I/ f
    2
    9 a4 t1 @& w+ W7 o' j% P* ]* U! n3
    + f/ D& d: Z. }% Z+ }. a/ T4
    ! O# y# Q& c# u5: X5 H" C" b4 \6 H
    6
    ! k' ]% E1 c0 H2 a2 I" S6 r4 n1 W" R7. P8 {1 T9 g4 _% S  M/ `
    8
    6 c( ~4 s( F' c/ j9* ~5 h# C. j0 b" b& f9 `
    通过year, month, day, hour, min, second可以获取具体的数值:
      H5 c" ^; x- H! U( \7 m: e6 ?; R9 x* n/ ~6 m: s+ e' g
    ts.year) }, L" b0 A8 V* }, Q
    Out[7]: 2020
    " H3 m1 |( Z- T! m; ?4 ?: y5 {; m
    9 |1 z  D8 W+ j8 U5 Lts.month0 Z/ {" G3 {6 l" W: n& V3 n8 j
    Out[8]: 1
    ) A, N- \, f8 z3 k& Q: c7 c8 W" e6 j* f$ n3 Q( t: c1 T
    ts.day2 `8 _* |% R+ f2 f* U
    Out[9]: 11 V5 G1 `$ j. q. z, F5 x& ^
    + F9 E$ ~) ]# |# D/ C+ |
    ts.hour
    ; j, w1 n& [2 s3 ^Out[10]: 8
    & ^( W# v* o1 [! [" y
    * j  E0 K. _" j' Mts.minute! L4 q# L+ V! z/ n& w6 \; p6 T$ N
    Out[11]: 10
    ! Z5 a9 {6 ^6 w# t& c
    . h' c, |/ P2 k4 Dts.second
    # s- h3 u+ t* C& S0 \1 n# B8 Z. \4 kOut[12]: 302 K9 V, H8 l* V9 |' P

    8 L- v5 U( n8 T  Y% D1
    " D. P9 t2 h& o+ n0 M% P0 D. F" M20 }6 C) _/ \: R3 k9 t
    3
    9 s( L: q+ f; X. z9 F% S3 N) P44 U! L' q7 G4 b* c3 Z7 x$ Y* C
    5; b, h  r% `9 B. q" q3 L. N$ R
    6. R: U" ~  c9 R
    7: w7 S# s7 j, ?' z* E0 O5 a0 w
    8+ w% K$ \* d1 D% c
    9" i, r) @: f0 C  w* `, P
    10- Q4 M5 Y3 g+ i$ Y, g$ r
    11+ q" s2 u* T6 ?0 w- T
    120 m- h$ U3 ^9 c, w5 @9 H' \2 r
    13* a$ R. ?6 U* b4 C& a* F
    14
      K4 K' W( a9 w15
    # F! @. V2 A9 `$ Y9 o16
    / J4 }5 X# M6 M6 {& l, r7 Q179 ]" D# q8 z# Q5 G/ q
    # 获取当前时间
    # j$ l# j1 I. R* ynow=pd.Timestamp.now(). A: ^. |* B/ b' K; Q
    1* X% |& s6 H$ j, v& o
    2
    & \: R. @' ]2 E0 _在pandas中,时间戳的最小精度为纳秒ns,由于使用了64位存储,可以表示的时间范围大约可以如下计算:
    8 D: _! J# Q% K. u* N4 iT 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). g. z$ K9 d' J" h+ Y$ M( Q$ T# H! z
    TimeRange=
    % ?3 j7 t& e" X10
      {. f6 v+ Z, I& D9
    6 Y4 S' k: D" t+ o% l7 n ×60×60×24×365
    4 ]1 N$ X, g" H2
    ; E) @/ G" S' p64( h+ P# V5 M' j
    1 N# E/ z+ o# b4 `  j! T- Z. ?6 ]
    9 F# D1 I/ r% ]/ A8 U; B
    ≈585(Years)
    5 w  _. g0 i% T5 m/ v. Z# f7 H1 i) A4 g, j3 C6 c7 @( W
    通过pd.Timestamp.max和pd.Timestamp.min可以获取时间戳表示的范围,可以看到确实表示的区间年数大小正如上述计算结果:
    " M8 a  ?3 @3 P0 R8 w; v3 Q' b( m/ |' }, l4 e
    pd.Timestamp.max
    - i! e' s* x6 O  C. q3 ], aOut[13]: Timestamp('2262-04-11 23:47:16.854775807')- K% X( c- I* k6 o5 C1 ~& h: @
      N. v4 N: V8 p+ ^; K
    pd.Timestamp.min
    6 U" m  e0 z8 F1 ^" iOut[14]: Timestamp('1677-09-21 00:12:43.145225')
    4 @+ Y1 A( b" I6 B
    # c+ z: s2 I. W( o' _8 Y$ wpd.Timestamp.max.year - pd.Timestamp.min.year
    ( I! b/ o0 W# T" B: E: GOut[15]: 585
    % R' M9 V1 U7 U  A' L1
    7 g. F8 ~6 G: I; H* O2
    9 Q/ C% G$ v- r# q. q3; D! _8 E: R! E/ ~/ f! y, a
    4' |$ _5 Q  W  z
    5
    * h/ j# S3 ?5 ?8 D2 q6
    6 Y  {" x  [8 Q: d71 p; h' l9 T$ X' a3 K. z
    8
    5 P. X6 X: K5 J6 @% F10.2.2 Datetime序列的生成
    4 f9 O! X& W  T+ F7 q% c2 A# u) {pandas.to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, utc=None, format=None,( [  [# _+ l+ g9 w, U9 v
                                      exact=True, unit=None, infer_datetime_format=False, origin='unix', cache=True)
    ' x) Q8 q1 h" d. |% a  E: x16 ~6 _: i+ a7 M5 n& m
    24 n0 F& r: M) ^+ o6 J
    pandas.to_datetime将arg转换为日期时间。% {! P  e* R' `+ D- L
    4 u( M0 h/ i/ |' X8 d
    arg:可以是argint、float、str、datetime、list、tuple、一维数组、Series、DataFrame/dict-like等要转换为日期时间的对象。如果提供了 DataFrame,则该方法至少需要以下列:“年”、“月”、“日”。
    5 N  u0 v0 e5 F! perrors:, Q0 `9 Y8 }) d
    - ‘raise’:默认值,无效解析将引发异常
    " G, g  y- X8 ]0 H+ `, W- ‘raise’:无效解析将返回输入! Y6 W5 O+ j% T) x% `* Z. K! c
    - ‘coerce’:无效解析将被设置为NaT
    : w. f$ o, }6 x  @dayfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析日期,例如“10/11/12”被解析为 2012-11-10。如果无法根据给定的 dayfirst 选项解析分隔日期字符串,会显示警告。9 `, e' r: }" c, s# n
    yearfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析年份,例如“10/11/12”被解析为2010-11-12。无法正确解析时会显示警告。(如果 dayfirst 和 yearfirst 都为 True,则 yearfirst 优先(与 dateutil 相同)。)3 e7 L5 j2 j  |7 k
    utcbool:默认None,控制时区相关的解析、本地化和转换。请参阅:pandas 有关时区转换和本地化的一般文档
    : E7 g' y4 w1 o, c+ f9 yformat:str格式,默认None。时间戳的格式不满足转换时,可以强制使用format进行匹配。
    ! a# y0 O: B0 I% I5 ^9 j7 {unitstr:默认“ns”。它是arg (D,s,ms,us,ns) 的表示单位,可以是整数或浮点数。这将基于原点。例如,使用 unit=‘ms’ 和 origin=‘unix’ (默认值),这将计算到 unix 开始的毫秒数。  h: i$ U. I' e1 ~" j
    to_datetime能够把一列时间戳格式的对象转换成为datetime64[ns]类型的时间序列:# _  ~; C8 Y) U& u- {
    pd.to_datetime(['2020-1-1', '2020-1-3', '2020-1-6'])
    " D& J3 A+ y( b% g0 H- Q
    " Q$ ?0 x/ M" Q- C% D) IDatetimeIndex(['2020-01-01', '2020-01-03', '2020-01-06'], dtype='datetime64[ns]', freq=None)  X9 x: C. t* Y+ O3 z% i2 |
    1
    + n, c7 }9 H/ }1 v2
    + }! K  `1 L) }/ u9 x0 u3
    % P; y% }1 F/ g, B- n在极少数情况,时间戳的格式不满足转换时,可以强制使用format进行匹配:- n2 g! b6 e7 v+ P4 s; T
    / S2 z; y/ [1 O; I, _
    temp = pd.to_datetime(['2020\\1\\1','2020\\1\\3'],format='%Y\\%m\\%d')
    5 }3 J6 B" N/ g; htemp
    ' A2 g! c" `3 g3 u% X. o6 w
    ) S; f% A& k3 }  m% W/ EDatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)
    ) @$ ?$ S1 J; E! L( K. p1
    5 L# u/ d" D/ E7 ~% A3 a' U8 n2
    3 Y: G: n2 @1 S0 |* b3
    + l2 Z. S$ N% E9 C4 P46 K' K7 A) `3 b( a8 b
      注意上面由于传入的是列表,而非pandas内部的Series,因此返回的是DatetimeIndex,如果想要转为datetime64[ns]的序列,需要显式用Series转化:+ M# X& H3 Z" ]  q, k

    # t8 [' g) p0 P* j* l. M3 R% mpd.Series(temp).head()
    / i, ~# s3 B4 }! L- R* I
    % C3 D) A) h  E/ O2 u& F0   2020-01-01& r5 u% c1 K. g3 P
    1   2020-01-032 ~2 k: }7 e" N2 d8 m' g6 l
    dtype: datetime64[ns]
    * `; V6 Z: b% d17 P( A: ]1 w& d1 u$ G! ]0 Q
    2
    : A! x* w, w1 }3( F- Y4 [3 k1 u# x% l
    4/ ^9 ^3 ~: ]5 _# o% ~) X
    5
    - X8 C/ |* N7 q3 X! S7 q3 g0 l下面的序列本身就是Series,所以不需要再转化。
    2 m0 S' e5 u$ i: `( u0 Q: e  R& f# a+ k' u, X
    df = pd.read_csv('../data/learn_pandas.csv')9 O5 _5 x0 f* {. E  q$ P# K
    s = pd.to_datetime(df.Test_Date)* Q  J7 j. Z* ~6 q3 C( P+ e% ?
    s.head()8 F2 B* w  ^$ H3 h8 \2 N

    $ r# }& }( y6 J" R3 j0   2019-10-05
    % c8 ?+ Q* e9 y( }1   2019-09-043 T( s8 [- w4 C5 K, r
    2   2019-09-122 v4 j. M' }8 Y: K5 B% a
    3   2020-01-03  r1 w5 J! s9 l' C5 L2 D% N
    4   2019-11-06) f; N: g+ u8 \% j# }
    Name: Test_Date, dtype: datetime64[ns]
    + G+ A3 M0 p- T) s- J1
    . x, D3 Y5 X+ A( ]7 G5 Y25 D1 a- w$ a. y: K( N( p# L2 S' N
    3) k# x- H( H5 |7 z
    4+ u$ G5 x6 R/ e, g7 n, O1 }; J% V
    5
    9 W, [2 ^0 Z3 V5 X0 `6
    . s$ v0 V0 y  A, X5 z7  v( p' \! l5 j5 T% K; B1 A
    8
    " b( k- {( D* ]. R# [- p9
    * O  O7 ~/ t4 _' V2 P10
    & B& \, p! x# W  C2 Q# g4 s) L把表的多列时间属性拼接转为时间序列的to_datetime,此时的列名必须和以下给定的时间关键词列名一致:
    8 @0 M' b6 W1 udf_date_cols = pd.DataFrame({'year': [2020, 2020],  [) u: t3 v6 g. l1 V: i; `
                                 'month': [1, 1],$ G: Q3 p$ c  B
                                 'day': [1, 2],
    , }( m' A/ V# \6 g* Z* m                             'hour': [10, 20],4 n- l' f) r; N5 U
                                 'minute': [30, 50],
    8 ~! O3 _2 M3 e/ p                             'second': [20, 40]}). r2 H2 O6 `7 ^, M8 y' e! C
    pd.to_datetime(df_date_cols)
    - Y* E% Q# Y" y& j1 k& b! o5 d: s( L+ e# x3 P
    0   2020-01-01 10:30:20
    . S' ~1 U" n0 H% R  e( @1   2020-01-02 20:50:40  B+ Q' g7 l; W
    dtype: datetime64[ns]
    1 K/ u( L1 Z/ t- |5 B/ M! r, p: }  v1
    " x8 D/ ~7 r- n9 p% ^7 w0 U0 T2
    $ R' R" `5 N# [$ |5 u$ n6 R3
    ; J1 m1 P, M1 B5 l+ F48 q% P  L- S! `+ F7 }$ Q$ \: G0 A, W
    59 c& H+ Q5 r' i
    69 L8 l% o) v. v+ v+ s8 L
    77 L  T5 _# K! v4 `
    88 \& ^6 q$ c. p# D6 w
    9; e" G: C/ Z7 I+ v3 S2 m) |" O
    103 D) S. H+ {3 T  C1 q
    11& R; W; o- r) n5 g, e
    date_range是一种生成连续间隔时间的一种方法,其重要的参数为start, end, freq, periods,它们分别表示开始时间,结束时间,时间间隔,时间戳个数。其中,四个中的三个参数决定了,那么剩下的一个就随之确定了。这里要注意,开始或结束日期如果作为端点则它会被包含:
    $ Q: L0 v7 o5 @- x" spd.date_range('2020-1-1','2020-1-21', freq='10D') # 包含
    + K$ w* P* A! v  g7 sOut[25]: DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21'], dtype='datetime64[ns]', freq='10D')
    ( y7 n& k8 w- B+ S  v" ~- x. r9 i' W& @8 d) R' \4 c- X( p
    pd.date_range('2020-1-1','2020-2-28', freq='10D')
    : w+ L- H% e3 h  KOut[26]: 9 A* `8 Y1 M$ ^0 T" s! c' x5 A! ?
    DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21', '2020-01-31',9 V4 l  F' I0 }  I: T; W
                   '2020-02-10', '2020-02-20'],
    8 n9 D/ ^  q. t* w              dtype='datetime64[ns]', freq='10D')
    3 [! w" F  p$ T( l0 d
    " G9 M6 j7 W8 e. o  m" Q- F3 kpd.date_range('2020-1-1',
    0 ]( }* l! S7 u9 k7 x& ~              '2020-2-28', periods=6) # 由于结束日期无法取到,freq不为10天8 Y. E/ X% U2 `
    9 {0 `- I$ a0 M: ~" C; X2 e
    Out[27]: " Q/ {+ H. F& r" A, R7 F
    DatetimeIndex(['2020-01-01 00:00:00', '2020-01-12 14:24:00',
    " e. |* B7 k. U* N  B               '2020-01-24 04:48:00', '2020-02-04 19:12:00',
    : q! M+ ~3 i+ ]% h" H* o2 q! n               '2020-02-16 09:36:00', '2020-02-28 00:00:00'],* o- T8 c( g9 f, G9 `% ?
                  dtype='datetime64[ns]', freq=None)
    6 ?8 _/ f* X/ o# S* P- o2 E: r  X8 A: B9 w9 Q, T
    1
    ) T0 m, y5 x0 i2
    7 A2 _( Q( W. m" c3( M0 i3 w5 Q, N9 w+ F% K1 @3 I
    4$ o4 w4 S3 P, ^4 L1 }
    5& g9 T) c4 T+ K( U0 A- R+ q/ U
    61 ]$ y+ T8 f  ]$ r( h0 {4 ~
    73 ?- K9 L# n! @% a* ^8 G1 o( g
    8
      H0 D9 z" ~! Q0 S9
    7 ?0 j) c- d) P* P" v' T10) K) m! C/ ~( N- o5 R+ I
    11
    + G* B) y  }& x7 c$ \+ n* r; T12
      X8 _" {: l" N# k( F7 {13+ d/ j) |9 z0 R" m$ _
    14! r8 `$ l4 ^) q# f) c
    15
    : |0 v% B+ c( [! H/ C161 F* K) R+ v7 X" K; W5 M, e
    171 K4 }+ b+ k/ x% F' r
    这里的freq参数与DateOffset对象紧密相关,将在第四节介绍其具体的用法。; x) `7 m- s  L4 D4 {: O; a
    : ]" O2 c- i; f9 F
    【练一练】
    . X: J* V9 D# g3 \  @Timestamp上定义了一个value属性,其返回的整数值代表了从1970年1月1日零点到给定时间戳相差的纳秒数,请利用这个属性构造一个随机生成给定日期区间内日期序列的函数。7 T- r" f+ [3 T4 M/ \/ @, }, k

    " ]2 L' Y5 q8 b1 }4 R7 Als=['2020-01-01','2020-02-20']
      o8 a) t7 d$ Wdef dates(ls,n):
    : D- f& E0 T5 o6 z" H    min=pd.Timestamp(ls[0]).value/10**9
    # c( U9 X- U0 D6 t4 e. _    max=pd.Timestamp(ls[1]).value/10**9
    ; c2 g& T2 ]5 B. d+ M( n    times=np.random.randint(min,max+1,n)/ L0 z; H* J, s8 ]: r6 C6 b6 @- ?. T
        return  pd.to_datetime(times,unit='s')
    0 J' L# E- L. m9 e( B5 C3 qdates(ls,10) 5 m- l1 i" Q0 z2 ?( a
    1 p8 f/ k8 Y' b; J; ]& r; p9 X
    DatetimeIndex(['2020-02-16 09:25:30', '2020-01-29 07:00:04',2 m2 f, ^# D6 K2 {
                   '2020-01-21 12:26:02', '2020-02-08 20:34:08',' ?% a( `" X% {% W2 J1 a& @
                   '2020-02-15 00:18:33', '2020-02-11 02:18:07',5 T4 u* k3 J9 B2 w, Y- l
                   '2020-01-12 21:48:59', '2020-01-12 00:39:24',2 C+ k5 Z3 A- n8 O  D
                   '2020-02-14 20:55:20', '2020-01-26 15:44:13'],
    5 [; t8 h' j) H$ [" U              dtype='datetime64[ns]', freq=None)! k% O2 K/ O7 l* X5 \
    1
    9 r, l8 k' O' D: S0 T6 @5 a2! N- `/ @9 Q% Z" ?! M) O/ T
    3
    8 Q- b( `# U- y41 `. L. ?: ]' Y& A+ j
    58 {- ]& }7 P/ F) f# [
    6( @6 W3 F) `  t: F" A2 W
    7( y$ |+ P% L* q: y2 d7 U
    8  z+ x# A& _" x- Z
    9
    # n# f9 W' V( J/ r$ ]% @10+ g3 v: U  v9 u1 y$ ~
    11
    / u# V* y0 n/ z4 Y123 D$ R5 \* {/ o5 L# ~& w: l" p* p
    13
    7 A5 v8 x1 Q8 q2 I14
    : a" E5 u  a) A2 H( c7 U: [asfreq:改变序列采样频率的方法,能够根据给定的freq对序列进行类似于reindex的操作:
    / l7 H/ Z( R& @0 Y! f$ N% cs = pd.Series(np.random.rand(5),
    7 D& y4 T  |2 f! W8 g2 W+ _1 U            index=pd.to_datetime([
    8 x3 P. U) ^, D1 C4 s                '2020-1-%d'%i for i in range(1,10,2)]))
    ) u" t& A0 d2 G
    5 n) z/ f0 ?2 S5 ]% r  v1 [  }. Z$ z! d
    s.head(): p* O. R; p9 P& _' x. V
    Out[29]: 8 p& H6 F0 M9 y" j/ Y/ I7 G% f
    2020-01-01    0.836578
    . k% I# `* J& T* x1 K  u! {  ]2020-01-03    0.678419
    ! F  ?/ E  g. z& y) x4 N& y2020-01-05    0.711897- G8 l& U/ J0 V  u( b
    2020-01-07    0.487429' O3 F9 ^  Y, i) b, d
    2020-01-09    0.604705; z+ [- w+ y1 M" D/ N
    dtype: float645 Z0 z) o* X6 d1 q5 m: C+ f$ y

    2 f: Q7 D& W, s* ~9 ws.asfreq('D').head()' i  E. V9 `- P" F6 O" l4 }
    Out[30]: # O& n7 u# b" q, ?. x
    2020-01-01    0.836578
    " r) N# L, N7 ~  w& \2020-01-02         NaN
    2 i6 A7 j: q) r( W& k" g! U* u* L2020-01-03    0.6784195 p6 \$ t  K3 P2 {7 j6 |1 _% K
    2020-01-04         NaN0 s* H4 P! l! J$ C
    2020-01-05    0.711897. F, z: t6 y1 n$ K
    Freq: D, dtype: float64
    / h5 p' r2 s: ]2 u
    - Q! l: \) x- M9 _2 W9 [s.asfreq('12H').head()
    1 j3 C/ o% Y0 o4 V, qOut[31]: . A1 y: U  o5 b4 N2 R' K: w/ s
    2020-01-01 00:00:00    0.8365789 d, W2 N  \. H* S* v- s- d
    2020-01-01 12:00:00         NaN/ d0 M3 t4 Z8 ^, D0 r0 u
    2020-01-02 00:00:00         NaN+ g: G) }; \, q5 ?/ _8 R+ S
    2020-01-02 12:00:00         NaN5 [* v. \; W  ?' z% q
    2020-01-03 00:00:00    0.6784192 {) {- v+ O2 a( s
    Freq: 12H, dtype: float64
    * n& F" O4 {$ u+ s) N" X
    $ C3 V: T6 o+ t- L. z: C1% O& |7 U: N8 \! h( ^8 p+ r4 ]
    2
    " Q8 E3 W6 J* k4 z- W! E31 x: h; R9 o9 [  i1 Q% y
    4' k4 |3 H5 \2 E7 [. f
    51 G7 S" J. q% K( G
    6/ r& _8 M' m( S4 Z. u7 {0 u! W
    7
    # C; ^4 ^0 y* F8 O# u$ j! ]8: k4 m7 S" h7 p* _$ f+ Q& n1 c
    94 ]9 j4 x7 S1 J2 R- F; Y3 }
    10( v$ k6 u! W2 q. G
    11$ N$ M/ G- W& F. p( S6 Y
    12
    5 p; `' K2 V& _6 L' b136 G2 O* W6 B* Q8 _1 u
    14
    , j) E, J0 ~# m1 [8 g& z3 S0 X' d15
    / Q! W6 x. k( X3 g1 v) y4 _% x+ q! U16
    + w) t' O0 v& _# A9 f" b& {! t17" B5 @$ K0 a8 d  H6 {7 B
    18" e- {( u8 ]4 T5 A) e
    198 ]1 A) u' s4 ^, o4 x* a; h
    205 T6 W4 ^7 C5 S% x  n
    21
    8 w; J8 f1 D( O$ H# C/ w; N! f6 G22
    " A' p1 G- |; T1 W23
    ; W& `$ ]) C  r; n1 r24
    ( i+ W' Z5 b8 d" z0 [" D25
    9 B. g5 O" D4 B26, b- z$ f2 ^+ C9 s5 J! \
    272 n' B5 s& q! k5 Z1 \0 K4 o
    28" R2 x5 ^, e: c3 R4 X5 b
    294 O2 P  v0 ]1 G# J3 ?' h* D
    30
    % [, D9 @+ a9 [+ `- r31
    " |1 B' V' i7 w/ x1 I- O. `【NOTE】datetime64[ns] 序列的极值与均值
    - C4 z$ c, e% S. b  前面提到了datetime64[ns]本质上可以理解为一个整数,即从1970年1月1日零点到给定时间戳相差的纳秒数。所以对于一个datetime64[ns]序列,可以使用max, min, mean,来取得最大时间戳、最小时间戳和“平均”时间戳。6 m7 J9 F9 x, Q2 q+ o" i

      ]+ B" w+ }. S3 R. s10.2.3 dt对象- y4 v" V/ Z- P8 |
      如同category, string的序列上定义了cat, str来完成分类数据和文本数据的操作,在时序类型的序列上定义了dt对象来完成许多时间序列的相关操作。这里对于datetime64[ns]类型而言,可以大致分为三类操作:取出时间相关的属性、判断时间戳是否满足条件、取整操作。  z! S, c: f1 c1 Y: {; M  q

    4 a5 ]: z, Z% J) D6 y6 F; H第一类操作的常用属性包括:date, time, year, month, day, hour, minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter,其中daysinmonth, quarter分别表示该月一共有几天和季度。
    4 `1 b- k3 s7 E6 Vs = pd.Series(pd.date_range('2020-1-1','2020-1-3', freq='D'))* X5 k& g, H8 `) `
    " ]/ @6 J. ~9 ]
    s.dt.date
    ) l7 }. W: e" w$ r" rOut[33]: + D: W. `$ s/ t! B6 _/ @3 P
    0    2020-01-01% N+ J( Q5 U: q: \7 l2 P8 P
    1    2020-01-02
    . c) V" r/ P2 f5 o/ S2    2020-01-03# a, |/ u3 X9 {6 `8 G# H5 |
    dtype: object2 E4 ?* T& u( R3 G

    , ]3 G% X! g( y: N5 t' {s.dt.time/ m) q9 w8 O4 b: |$ Z
    Out[34]: 2 R! L+ `# p2 p3 C* I- o* Y
    0    00:00:00
    / P$ B* u; c' p2 t$ P1    00:00:00
    $ w& h. Q4 x: U# E9 ]2    00:00:00
    : W, N1 q4 b$ V: K9 {, Pdtype: object8 T2 D8 L: ?: V0 i! I

    " p8 S7 d- d+ h+ Ss.dt.day
    ; H4 ?% r! z0 U8 ?: j+ q8 POut[35]: 6 E7 b" N8 b* Z. i3 s0 ^8 h4 u
    0    13 N0 d3 t- b( g5 B8 @8 ]$ b
    1    2
    ! [& y4 J% [4 a7 x: `5 T& K  u2    38 M/ i. u& e. u' D# [
    dtype: int64) ]5 M1 v6 B8 @* b" s4 V/ u& u0 r

    ) J. }" g. L6 Rs.dt.daysinmonth
    - X9 P4 C4 s2 q* f$ h" F9 ^$ FOut[36]:
    # X4 F  q2 ^+ N8 \& N. N' U( s0    31
    / r, q  B3 K" S/ Q7 L8 ^3 o1    31# g9 A& y) ]: I! Q* y) ?$ \/ F
    2    31
    ' K' j' o* e- B# @  ~* edtype: int64
    ' q& U( [4 Y! q" [# \& w4 n" M
    ' U" |5 w% O7 W" j9 Y1- c$ V- ~; ^; ^: M. H. K
    2
    " u, T/ M: O+ s3
    & T/ [( J+ ]0 _4
    # {. `4 F+ @* B% o7 z2 R5+ L& }  p6 Z: N% }
    6
    6 N9 t4 U( Z7 a3 h$ h% o7: I" O0 ?1 N$ p! J  t
    8
    7 e  ^& o( {7 Y+ Y4 a4 q: u" O96 E/ s+ C/ ^$ `
    10
    ! J* C6 ~- n' N/ h) k' j- K2 C* n111 v5 ^  J4 f: C1 k# Y
    12* b$ Y5 V2 p/ F- H
    13! d+ q2 R: S3 E1 `. @2 h
    14
    8 W7 z$ j+ x2 g2 V" M1 G$ ]) M15
    5 m' [; z8 W( U# W16
    # ~5 z" ^: P7 s) j" T" ?1 i4 }3 {5 m17$ c; Z. y5 F/ f9 \5 y
    18
    - g3 S  v: Q2 J19$ T* C4 |' x( M( ~# u/ S1 _+ G
    20
    - D! B& X$ q% F, G1 ^! W21
    . v5 t1 |" v5 F# o8 j22
    : q% y4 k- ?  [# {& o  Y23
      S  p8 I& ?3 A/ \  x) ~+ P/ _24% y" ?( Q+ z9 U9 a( }" c* D! b
    25: n; F* {- b) \; n# Y1 `
    264 O* u  [1 N& H# b: J
    276 _) s; n. C0 o8 s6 S8 c2 v1 I
    289 @. ?8 D% u) S- ]& K, Z
    29
    ) }/ v/ Z  v7 s* |5 P( t4 _  在这些属性中,经常使用的是dayofweek,它返回了周中的星期情况,周一为0、周二为1,以此类推。此外,还可以通过month_name, day_name返回英文的月名和星期名,注意它们是方法而不是属性:
    4 U5 B' @: t( d; ]. w, S- P0 H1 G. d
    s.dt.dayofweek8 |; ^+ u' O/ [1 O" P* K
    Out[37]: 8 k  o2 t9 ]: `5 r" J' m
    0    2$ c/ k0 ]6 U9 b' T
    1    3, O- s. k* K7 C0 @0 r! }
    2    4
    , ~8 G$ G5 M% Vdtype: int64
    4 ]$ t' e% _3 `/ a$ i4 ^7 G
    $ m& V. p3 x- I5 \+ O+ `3 _, S3 Hs.dt.month_name()+ g3 O  s' o. c8 f* }2 B2 U
    Out[38]:
    % _, d1 {0 J: }' ^7 P1 Q. }7 M0    January1 d/ `9 _, t7 v# p: A) j
    1    January
    1 x  T. t/ P) r2    January
    - x/ S# _6 M7 [dtype: object
    % j$ {, c& |* U8 G8 P* [( A# x, D5 s6 ^6 j$ A
    s.dt.day_name()! [8 R8 N: s) K3 f" ]
    Out[39]:
    5 h- t3 N( [3 S2 l6 i' E0    Wednesday5 T0 I5 D# T) V
    1     Thursday
    , A% Z- J& F) X# ?( R) j( _/ Z$ i/ f2       Friday
    1 C; C) W" W* Edtype: object
    & t  k, v7 N' j2 h$ D9 \
    - e8 B% Y- K7 S' n8 x19 K0 T7 p5 }# Z7 z9 K
    2! x& l$ C! l% I& [
    3" F5 {" d8 R- u; M& M* s* E/ B6 n
    44 j9 I6 ~* E: C
    5
    . v  H5 }: t3 f& G6
    ) e3 Y' f! n3 n. `* S/ u7
    ' ]5 n. l, ^+ T3 V+ B8
    8 Y0 b3 s. ]) @$ ?% x' m9
    . u9 X9 j- h* u9 y, r0 K109 ?# ?- n& e0 c' c+ f
    119 T% R* C  D" b( E5 `( n
    12; @# I; d6 s4 i# ~
    13
    + ?9 w# L" c$ W14! o8 z: H* ~- f6 M' x
    15
    3 i4 ?' ^0 u6 \2 Q167 D$ h+ G; C0 a: y( s2 ], w
    17
    1 a# x+ I0 E8 c2 f18, T0 ]7 ]2 O8 F) o# t6 B
    19
    $ e7 a! E2 h3 J6 d+ ]20
    , g1 L% Y8 S* r- j第二类判断操作主要用于测试是否为月/季/年的第一天或者最后一天:
    ) i; E. Y* A1 Hs.dt.is_year_start # 还可选 is_quarter/month_start% P" J- l) V0 Y+ }/ N6 B
    Out[40]:
    . p) ~& d9 x3 w( x& v7 t0     True" P" [: V: Q4 O- X4 W8 u9 u
    1    False
    , _* f8 ^$ e0 m: c' Q) }2    False
    . @, N$ v5 q' j9 _4 T3 S$ zdtype: bool/ c% i" V% b; X) R

    7 P9 U2 ?" s1 J" c6 f0 Ts.dt.is_year_end # 还可选 is_quarter/month_end
    3 J2 j6 C* \9 T6 T! u6 Z/ fOut[41]:
    $ _% C0 ]1 {- o' |4 R9 U6 q0    False
    " [3 W' Q- r4 s% n6 x3 x( `1    False5 ?/ L  D. z6 T3 y6 T7 e
    2    False
    ; Q+ E, M$ f& Bdtype: bool
    7 |' G$ N: b, x/ \13 {7 v/ ~4 b6 A. X* n% x: s
    2
    8 h, x" h7 u0 \  s: A3
    5 h1 G% P( Y: H' e1 J40 p( h( u; _5 E& K. `4 n1 j
    5/ O. v! H5 T( r$ Y" j& j
    6( R! d# V4 [, y! D" k
    7
    ' ~; }/ B( m: P3 E( W) Z8
      K# l- X' B: [, N- f9& ]( U3 Z: c% }& K: A2 R
    102 {' M9 U4 O. r2 n& b5 I
    11) Q* T6 C. a) o1 B
    12
    ) }5 ?4 |; j% `$ E( _; k) W13
    . l0 e0 \. Q6 y; m$ f! I9 q第三类的取整操作包含round, ceil, floor,它们的公共参数为freq,常用的包括H, min, S(小时、分钟、秒),所有可选的freq可参考此处。
    ) E9 U/ ~8 @: x- `$ Es = pd.Series(pd.date_range('2020-1-1 20:35:00',* I2 \5 H$ z6 V) Q
                                '2020-1-1 22:35:00',& P/ R& H) G" b( `
                                freq='45min'))9 k; n" v2 ^7 t9 C: G4 ^% R; L* j4 b
    % M2 s8 O4 {( x7 m0 j
    " s; V- j1 v  W. q$ M8 x" m( n8 H# W
    s
    / R6 D$ U# F* C" r' e; x. @$ COut[43]: 7 G3 P+ o' l5 T& G' [' Y& j
    0   2020-01-01 20:35:00
    ; l9 p2 h5 E* X; o6 O7 b1   2020-01-01 21:20:00
    6 W, s5 g9 y* Y$ n9 h; V6 b0 G1 Y7 C2   2020-01-01 22:05:00; q# F" p8 {) s! w% d6 H
    dtype: datetime64[ns]
    % N# W# d  Z! k. l+ m/ E* _* Y9 a. F
    s.dt.round('1H')+ E% _7 k. q% G$ J3 a  \% {
    Out[44]:
    9 f) ^2 E: k" G  O0   2020-01-01 21:00:001 W% m/ _, p! |' |% N
    1   2020-01-01 21:00:00
    3 F+ _3 v" t0 L$ X0 ~& U" F2   2020-01-01 22:00:00
    8 }. f, c8 I: j7 ldtype: datetime64[ns]
    2 {" t0 W4 P, y6 \" O
    ' C- ?" T' U( \# d8 l& Y2 ss.dt.ceil('1H')
    ; P; w. u, i# x( }! u4 w/ }Out[45]:
    * T+ C% E2 C6 r! v/ b/ u# w" M0 E0   2020-01-01 21:00:00
    8 ^9 p- M% V6 c  ?4 h! v+ P1   2020-01-01 22:00:00$ J' J- ~) ~/ T4 o/ r5 c
    2   2020-01-01 23:00:00
    + C9 o) c4 s: B! |. v% V9 wdtype: datetime64[ns]
    % N5 Z1 T# O" _" U8 ?) v; ?
    - u, A! H/ w$ j- J. |' Ts.dt.floor('1H')$ d8 B7 G5 f$ ?. b1 t
    Out[46]: 3 A7 G! ~- M9 c( b" d
    0   2020-01-01 20:00:00
    0 }3 z, p/ m: [7 f) \. ~: ]1   2020-01-01 21:00:004 O' ?7 E: `7 f3 D
    2   2020-01-01 22:00:00  t+ @% z: w0 Y# m# i/ O
    dtype: datetime64[ns]) K# W9 O4 Y1 ?6 D! E0 s  X

    5 L, ~: j$ {- j7 q0 h0 Y  r18 u7 w0 o5 `! I2 e6 W
    27 ?0 L2 {( M4 b$ o
    38 S' T! B7 j  \& S
    46 \5 f5 ]/ j2 [1 C% U) z+ f/ H+ l
    5
    8 d1 n: G( V) |6 H3 I4 Z+ l9 u63 a$ O- _" K7 n! m
    7' l. r9 U+ Z0 G5 v
    8. L9 Z; Y# c( Q2 V7 g
    9
    . m' `# g# a% ~" H0 w2 A. b" Z10( J, O( P/ I* m% V! D  }# |3 j8 K6 }; A
    11) b* l; ^/ ^6 H, Y) F9 N
    121 g9 x6 P7 f- y' r
    13! W2 g/ a* A  }% ?# f1 Z# Z$ S+ n0 `
    14
    ' v% S* h; w$ r- d9 |" J' E( ~" T15
    , u$ N4 q/ \, E9 B16
    5 c% T+ C8 |. J" ^17
      k5 Z+ i. M" \8 _4 L; y18# B' `. M* _6 t) _6 K
    197 R& o& g* B$ p- g% e# J4 m% u6 w
    20
    0 h* j9 H' j1 [8 G$ _5 R0 O21/ s' O$ n0 I* x- a6 \* }
    22" n3 z+ I- K+ b3 B1 F; y
    23. V: e; b' Q% ]9 u/ U# z0 w
    24- ~3 _5 [; G9 S/ R. }" o
    259 a8 |  M5 d" s% q
    26
    ) b6 E7 a1 B+ }% p3 H9 q" g27* d- V% O# r9 S1 p. Z
    28
    1 q0 _! @- }  U2 T, b# F29
    ' p: N  {  D% W1 v/ m' q30
    * v& Z4 n: S& y4 [- O2 X$ I3 l31
    / m" P1 R' |! q' |. s32! M6 n6 A/ }/ h% x
    10.2.4 时间戳的切片与索引" W3 J# u, r$ u  T; K9 t, r
      一般而言,时间戳序列作为索引使用。如果想要选出某个子时间戳序列,有两种方法:5 H; i- i" \* v
    * `( g  H" P# k, c1 J
    利用dt对象和布尔条件联合使用& b3 a6 _0 L2 _
    利用切片,后者常用于连续时间戳。
    # r( w( q% d' Q% s% Z+ ss = pd.Series(np.random.randint(2,size=366), index=pd.date_range('2020-01-01','2020-12-31'))
    ; c  x* A3 k' {+ |2 eidx = pd.Series(s.index).dt
    ! V/ B' f/ W5 u0 E, q5 ts.head()- R% y* y; y3 |2 S, h

    : Z) f% |) D- t( Z2020-01-01    0
    8 H, t5 T1 ]0 z) }. S; {9 E# ?2020-01-02    1
    % T8 j; D1 b& F& y2020-01-03    1. `1 b& H# J& r9 O3 n$ Q* h
    2020-01-04    08 q! t& M! S& b6 ^% ]$ L# f
    2020-01-05    0. s0 G7 ~# B- J6 B9 f/ T
    Freq: D, dtype: int327 X6 {% @/ w0 s& l: _+ X2 m8 \
    1
    5 P. |9 V2 e+ O9 c2
    # K* U/ m  I$ V  |9 |3
    4 J+ D% J9 X3 S& W# Z4 l4
    * L; _) I6 T8 a2 e5. Y/ g5 s! O: _/ o+ S' _- g- b  |  G/ \
    6
    ( |) T% N3 Q0 ]" h7' a1 T; ?: u+ |/ Y
    8% h" B& Q. x! n* N' m
    9
    : Q" d/ f7 d( A5 F10/ u6 Z1 \2 g$ m8 |
    Example1:每月的第一天或者最后一天
    : z% ]5 S; D' o, L/ w
    ; N1 \0 L( U; p$ i4 H5 h) Us[(idx.is_month_start|idx.is_month_end).values].head() # 必须要写.values
    0 v5 e& J+ z5 b% W$ S7 UOut[50]: + K3 s/ v' [! d' P' N
    2020-01-01    1
    8 t' q: r/ A* P2020-01-31    00 N* N+ n  U) ~* |* P8 c& ~* p
    2020-02-01    13 ~  z2 n( A1 I
    2020-02-29    1" _% q" g) e5 G- m* {: k, e
    2020-03-01    0! x" V% a9 R8 X0 ]5 x& w
    dtype: int32  P5 P% _7 Z1 f# w! s
    1
    ) t4 I  N5 L1 R, x* N% M% f8 _24 H& G  h6 n4 c- s$ r3 b# V
    38 N6 S" Z3 Z1 v
    49 U* T- b' V! n5 O* N
    5- g3 R6 p& D3 \( [" D
    6' o* d" C2 P. c8 e. l% V
    72 S$ X! \/ @0 O( Z1 {
    8
    : f. z/ R. |1 s8 J. t# V" xExample2:双休日' v: Y3 e- H# i6 o8 y9 Z# j/ j
    $ F6 P6 z) I0 C. Q; {
    s[idx.dayofweek.isin([5,6]).values].head()* m4 j- {% a7 B5 e" U' z1 Q/ h
    Out[51]:
    ( u# @; D% G( Y* n# O2020-01-04    1. P) [, f3 y  Y/ d7 P4 G
    2020-01-05    0, T: U9 v  r: w' @6 O2 M" J
    2020-01-11    0
    ! s# a; U9 f& p0 w2020-01-12    1, `' _+ P% Q& o
    2020-01-18    1
    5 ?( \  h7 w, q' v9 `( y3 u. W8 \dtype: int32* g" i/ j5 _1 |+ C
    1
    # o& c0 g+ c' s2
    2 z7 ~: r) x+ o, p1 K7 N9 t3" }# _! y+ c, E7 H, i# M: |& |
    4* c0 x- C$ r' V3 W# Q! o2 D
    5
    1 w4 ^% M% j" B& l, B' j$ M7 m67 [. s6 ]1 a( y% i
    70 Z; a6 n- o/ \* ~3 I/ v
    8
    3 D' p2 \& ?( K) ?Example3:取出单日值
    & H& a( x7 I( v0 |+ l# ^" b7 k$ z* `* e4 l/ m; z* w/ C
    s['2020-01-01']# y7 ~7 ^: D5 g
    Out[52]: 16 s$ j' ]6 H5 u1 G1 s9 a' D
    / e! \( ^5 L. B9 L/ j
    s['20200101'] # 自动转换标准格式
    5 |* ?5 G3 R' v" P# |) p, XOut[53]: 1$ \8 ?" o2 Q) R; c/ Y
    1, ]/ L+ b! b+ Z4 S7 @) r
    2
    : f2 W( o: y7 X/ d% A2 V3
    / k1 _$ A5 o2 C2 Y8 I2 |" g) S4$ z; o+ d/ b; }: q, [# F  p, L" {2 p
    5
    3 o1 [6 Q9 N) H: g, p( nExample4:取出七月3 X8 I- }* r9 W5 s
    ( m1 u7 H/ O: ^5 R5 z, \2 F" K% }
    s['2020-07'].head(), @( U% W. E5 p  Z, u, ]
    Out[54]: 8 D  e) B+ Y( E$ Y' O1 W
    2020-07-01    0- p7 D6 x! \0 i
    2020-07-02    19 K4 P# B. j& _, W
    2020-07-03    0
    ( Y% ]7 s6 C' M" h3 O) r2020-07-04    0
    4 X) \% y  j" `1 A" Q2020-07-05    0
    $ E( Q. y2 D9 e/ L5 hFreq: D, dtype: int32: g( ?( u$ I- ~' d+ _$ {5 _
    11 @! W0 o3 h. ?$ n3 V3 i6 E" ?( \/ \
    2
    5 w; [& M; b3 o; g( \2 q' i( B3
      p5 A& l' z  Z0 [2 e6 o$ M4
    . C& i. C3 w/ M7 k4 Q) V& A3 T5
    2 J% Y. ^- e* P' r' C" J: _6
    & u; z3 k$ B( s% Y" {71 Y5 b% S- H: |; k
    88 r3 M  K. T, H& `, m( @
    Example5:取出5月初至7月15日
    ; b3 p1 @4 E1 ~$ b2 L* ~7 a4 P, M7 o- V! A0 V
    s['2020-05':'2020-7-15'].head()
    $ c% V3 ^- C6 q- v2 gOut[55]:
    ; o9 ^1 q6 w+ ?+ w  H+ G: C2 @2020-05-01    0
      E9 x( d8 K+ {# ?2020-05-02    1% {" Q  z" H- V! S% T. P7 n
    2020-05-03    0/ {9 w3 ^& {# v1 [* q
    2020-05-04    1( Q! g5 f" s* t% a6 W$ r  S! L
    2020-05-05    1
    7 a" Q2 k0 A' S2 ^2 f! wFreq: D, dtype: int322 J* }$ ?( F+ N  @
    5 R+ t! S+ V$ |9 ?- d" @
    s['2020-05':'2020-7-15'].tail()
    : N, h. L! K0 i  d) Z" bOut[56]:
    $ f2 e, I% o% h  s. z. U2020-07-11    0
    9 K% v# B( u4 O% X4 g5 e' u0 M2020-07-12    01 o" j$ H. V0 b( D
    2020-07-13    1
    ( D- I! m% Q+ U0 j  d, F+ ?1 ]2020-07-14    0
    4 w+ q( b# [, c) q! Q; N, O2020-07-15    1
    . r! `; Q8 V+ m' k5 O7 jFreq: D, dtype: int32
    . s: N4 w' T* @4 D
    / V) |9 c& H: T3 x3 h7 ?1- y7 a, l0 g5 M2 w4 J
    2$ _4 I# [3 e' w1 ~. `2 A5 W
    32 q5 Q  K( d* i3 f" u5 m5 }
    4  C3 @+ S* N1 j$ e- b
    5. W0 P* |$ w/ {1 I) T
    6
    3 T3 N4 X& Z, c+ d7  D5 x+ C' ?& `) T) J' `! n
    8: O8 M* N, }" b1 t) J* y* j/ A5 x
    9, z3 F7 ?. U0 Q
    10" A& N# }2 O7 O9 O4 S# ~5 A4 {& J
    11" J* _, u4 W8 a
    127 r) I+ \! _* K% w# \( w' A7 c" l
    13" _) m7 y6 H$ i2 q$ C! O
    14# x1 l7 g1 R1 _1 O2 s
    15- R4 y: t0 |" x
    16. k, n) w" _( e! k
    17
    0 U: R  g" ?9 W  d" g10.3 时间差" N% _' H: G' ?3 x2 r; I8 u
    10.3.1 Timedelta的生成3 g6 E" C4 ]7 ^0 U) W1 e: o
    pandas.Timedelta(value=<object object>, unit=None, **kwargs)
    * E* C4 s3 C7 ]% E9 f/ V. o3 Q  unit:字符串格式,默认 ‘ns’。如果输入是整数,则表示输入的单位。/ c4 g8 i  `  t( S7 A
      可能的值有:7 r* ~! [' w: D7 y
    ( \# V: e% a! h  m, P& C0 X( r7 C
    ‘W’, ‘D’, ‘T’, ‘S’, ‘L’, ‘U’, or ‘N’
    ' F4 _( L  ]" y6 |/ L- V% H‘days’ or ‘day’  e7 v5 o- h% L! k4 J
    ‘hours’, ‘hour’, ‘hr’, or ‘h’7 D1 V: ]) k/ M& j6 a6 e
    ‘minutes’, ‘minute’, ‘min’, or ‘m’
    7 R* U4 Y2 H) N9 L‘seconds’, ‘second’, or ‘sec’
    - z7 t- T" I. j5 G4 a毫秒‘milliseconds’, ‘millisecond’, ‘millis’, or ‘milli’7 I$ p7 s& ?7 {0 F% V* g& {7 y
    微秒‘microseconds’, ‘microsecond’, ‘micros’, or ‘micro’
    , V* H; U; h4 \% E2 \& `- ^8 z) r5 z纳秒 ‘nanoseconds’, ‘nanosecond’, ‘nanos’, ‘nano’, or ‘ns’.
      ?% i# d% l4 ?$ r) K时间差可以理解为两个时间戳的差,可以通过pd.Timedelta来构造:% N$ z$ I8 y) x( l  q: w
    pd.Timestamp('20200102 08:00:00')-pd.Timestamp('20200101 07:35:00')2 b1 z0 w) o' t1 Q
    Out[57]: Timedelta('1 days 00:25:00')
    ' X+ B5 ^# }4 t
    % X8 P4 g# J' R+ h6 [. jpd.Timedelta(days=1, minutes=25) # 需要注意加s* {% Y! ^& H) W& ?( v
    Out[58]: Timedelta('1 days 00:25:00')
    1 k5 }9 s# U6 ?/ Y, e
    5 T" R, l! A: Cpd.Timedelta('1 days 25 minutes') # 字符串生成) Y, u$ t' z4 t- z6 A
    Out[59]: Timedelta('1 days 00:25:00')
    , s/ ]5 g4 V4 ~! y5 O. ?1 e# w" d. s6 L  y3 N  h6 x3 l
    pd.Timedelta(1, "d")
    1 k( h5 p; @. J5 L6 S5 S! |Out[58]: Timedelta('1 days 00:00:00')
    9 [" E) b* u2 T5 e1 H5 R1
    / }5 K4 ]) U( p* p5 i3 g' H% p27 t4 v- U& F& X9 s0 K- F1 y' q6 ?' g
    3
    ( N6 L. `# J8 Z' b& ^, q$ y& v- T+ H4
    0 W" R5 l' K7 @5$ n, K/ J. E7 `7 f1 [) ?5 G
    6, i2 |8 Z: H' M: a- ?' m
    7& n, _. G5 q" V
    8/ ^/ b3 K1 n9 L
    9. l& Y& i. G! H9 E+ l/ m
    10
    ' i& j) d/ z* }3 c- y5 l5 @11
      [) N7 `- F4 ?6 l生成时间差序列的主要方式是 pd.to_timedelta ,其类型为 timedelta64[ns] :
    & D: J2 o- R" T0 ]3 h3 }s = pd.to_timedelta(df.Time_Record)5 V* Y$ R! {1 q8 n5 o

    % Y$ l; V# ?8 L$ Y* [/ zs.head()
    9 x( r. p5 K+ `1 h3 ]5 H( D% `Out[61]:
    ! s0 W6 v3 U" @( z6 b0   0 days 00:04:34
    " }9 t2 I' [; T5 t' W: c1   0 days 00:04:20# t- T, D) ?" r) {# q, k- u& c
    2   0 days 00:05:22
    ( c1 d: B0 I8 k- s3   0 days 00:04:08
    / Y# C. y# M) o- @) h4   0 days 00:05:22
    6 D% u+ o' T! z* |* Q6 ~Name: Time_Record, dtype: timedelta64[ns]
    ! F: x7 S+ {8 J$ k; \! c. Y14 V4 l5 F2 ]' X5 f; h( @2 ^* i# q
    2
    1 P+ h& g( j1 b3
    ; V) w% M$ y. \) n+ }- s4
    * v  P9 a* {; @, ^' s4 ~" }5, i, T% w5 z, @& Y8 ^' Z  n
    6
    - w. l" A4 h6 H$ a% Z7
    % x) i6 f8 A: l; @$ u% M, f. Z! v$ V80 i9 _) C  w( E3 h
    9
    3 L! ]: c8 K3 |103 v% G9 S5 n! U) y# W7 t0 B
    与date_range一样,时间差序列也可以用timedelta_range来生成,它们两者具有一致的参数:
    # z9 M( O9 }1 f* d+ E) q& Z& ]pd.timedelta_range('0s', '1000s', freq='6min')1 j2 C; H8 h  ^" Q% X/ W* s& F
    Out[62]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:06:00', '0 days 00:12:00'], dtype='timedelta64[ns]', freq='6T')7 f8 }& E* y5 A( E  O
    + A8 U" _1 ~; z# y
    pd.timedelta_range('0s', '1000s', periods=3)
    3 j2 z4 |5 l  q/ z) M" zOut[63]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:08:20', '0 days 00:16:40'], dtype='timedelta64[ns]', freq=None)
    : j# [& W) x% e$ n. H1: R: W3 j9 \4 m
    2
      S% X9 u2 T, R3  F" N$ h* Z* E2 e2 o& _
    4. Q# r, E' {' }( X4 c; P  B  `+ ]
    5
    - n- f0 v2 N7 D8 Q' q对于Timedelta序列,同样也定义了dt对象,上面主要定义了的属性包括days, seconds, mircroseconds(毫秒), nanoseconds(纳秒),它们分别返回了对应的时间差特征。需要注意的是,这里的seconds不是指单纯的秒,而是对天数取余后剩余的秒数:
    2 z! ~0 v/ u* K5 Us.dt.seconds.head()
    # y, b: D% `& v# x6 s" R9 ZOut[64]:
    , B. M3 A. b. G- M0 L2 B0    274
    ' j& J1 P0 B3 H# R! Z" q7 t) M1    260
    ) K$ L& m. k% C! U2    322
    - |/ H4 G2 \' _% ^3    248
    4 O/ I4 \" r) C- f7 _: {( I4    322
    ) Y* b; `# o- x" lName: Time_Record, dtype: int64! W& b; T* {+ t# W
    15 Q% e$ G1 y8 J) `, q1 _* H
    2! e2 K7 o' o+ D! Q1 A! w& m
    3  ]7 O4 J+ S0 ?! n: o/ }
    4, t3 `& A% ], R9 I8 T- ^
    5& z; _* C, ]5 Y5 |) r& |* j
    6
    , n2 B" h9 k; r  ~72 z9 k/ ^% y/ I8 u& ~& u& L
    81 e) {3 v4 e2 T: {0 m
    如果不想对天数取余而直接对应秒数,可以使用total_seconds
    , v' V6 t* s; \2 ^7 z
    ! P6 g! d1 ?+ ts.dt.total_seconds().head(): B$ w. u4 H) {9 S( T6 F- Z; G
    Out[65]: 9 Z1 S" d; \" R/ I3 O4 r4 f; K, P
    0    274.08 S7 N( g5 i* [/ r8 C/ L
    1    260.0
    9 ], D& ^, K& o( n( g+ V2    322.0
    7 Y; L5 p/ ~5 Z' [3    248.0
    ) D" o2 j+ z% |$ t' M7 I8 G4    322.0+ F4 \; @8 t2 B8 I; _1 [5 _
    Name: Time_Record, dtype: float64
    5 w( h' |/ }( d4 P: k19 f% @1 ]/ U: P, Z( B6 e
    2
    - J3 u" f, k& u' O: e( h3  v) h$ c, M; k0 w. L7 H
    46 p) r: }  `, m$ s4 g5 v, ]7 y- e
    5
    5 j! E5 O  ^9 ]7 l% X. j6
    & A, }8 K% ~+ S( k7
    1 K  P0 C: F/ f8
    1 E& i3 b* F: D- O# K! q3 k与时间戳序列类似,取整函数也是可以在dt对象上使用的:
    % G; Y: G* M2 X3 {6 O4 \8 f* n8 g  Y: y9 Z0 q! P
    pd.to_timedelta(df.Time_Record).dt.round('min').head()) [) v) K. p: _
    Out[66]:
    " M3 V' s6 ^. R2 @  a+ M3 l0 c, S0   0 days 00:05:00  \( k3 X' J# f$ P4 p/ z5 `
    1   0 days 00:04:00
    5 L! a9 R3 B. H: K  R( j7 t) J; G2   0 days 00:05:002 C: ]* |  L, p- Q$ i
    3   0 days 00:04:00+ E0 p5 U( v5 f, G4 q4 N1 U$ l
    4   0 days 00:05:00& t8 G' m1 w6 a: u8 V( q
    Name: Time_Record, dtype: timedelta64[ns]
    7 s  L! x' p  P. H15 k/ h, ^' q7 a3 J* l! |
    2' ~! J4 X: ~! z8 F4 Z
    3
    9 K3 c0 X* r% T: O4
    - S7 J9 E+ h& M5 w2 Z! P5
    & J: u: t% C/ k3 l& y& C. _# ]6# N1 u; t; c9 a  a7 v9 h9 Z
    7
    : s- w3 J) N$ L3 a0 S2 f( F0 E, T3 `% i8( o0 ^, S1 @& ]" }
    10.2.2 Timedelta的运算
      [+ B' [1 T: e( {6 i: E$ K单个时间差的常用运算,有三类:与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算:6 ^5 u% {, g0 a6 Y* ?6 ?
    td1 = pd.Timedelta(days=1)
    * v  o9 {0 ?: z7 d  n: Vtd2 = pd.Timedelta(days=3)+ a5 [! T, l2 o6 ~
    ts = pd.Timestamp('20200101')5 Q7 E( B5 z% i; X
    - m* U) F2 p  l6 }( K0 V
    td1 * 2: M. z) ]7 _7 W7 N& e: {
    Out[70]: Timedelta('2 days 00:00:00')$ d& h% B( @5 ]# U8 e" d

    . \) [' }, w/ f6 ~! `td2 - td1  J& P, t$ z! N4 r5 |) B
    Out[71]: Timedelta('2 days 00:00:00')- u/ N8 E/ A+ p( @

    * ]! X, H$ r! T9 _ts + td1# |; o( w( X- _1 \* N
    Out[72]: Timestamp('2020-01-02 00:00:00')" {. k5 Y3 E! J; |8 ]9 E1 C/ E
    8 F6 q) X( G! d3 \
    ts - td1( d3 p' |9 p: \4 o, c  g
    Out[73]: Timestamp('2019-12-31 00:00:00')
    " w! R- F# A9 p( [0 J+ d6 h1
    ; j8 j$ v% _% M3 W; H7 `2* y+ q6 {7 U- x) W
    3
    # q3 Y' H- y, [- ^) N& c0 `42 E4 ?# ^; Z% w5 B* A' s) ?9 C6 H
    5
    ' Z% B7 D& G7 f( h68 _7 r. o! _/ N. \0 a
    7
    - Q2 @4 i  b' Z( w! W/ m$ s8
    / J& f4 Y7 m, Z9 L- i% c  C, K9
    - U' N6 o& k2 r+ w/ C0 F10; t% G3 x- D5 E5 b
    118 V3 D' l, i: R$ d- b
    12
    ! M; ~" w  f4 B) `0 V13
    & x# O  E4 S' B# i2 \14
    $ s% P9 f' d/ u1 l* `2 \15
    - n) ?$ c4 e! B& r时间差的序列的运算,和上面方法相同:8 F1 Y2 w7 B# H/ \8 j+ l" _0 l) |  b. {
    td1 = pd.timedelta_range(start='1 days', periods=5)
    8 a5 X. O$ `( ^9 ytd2 = pd.timedelta_range(start='12 hours',
    - h( Q3 r& R4 X0 y                         freq='2H'," V* U" E$ L' G8 `4 f& f% r( `5 Z% i* ^
                             periods=5)4 u/ R2 M$ p  {- U
    ts = pd.date_range('20200101', '20200105')
    8 B4 G7 r8 C1 x- \2 I) utd1,td2,ts5 w* ?, P9 t5 o( y/ g6 s! W

    ) y- V  r7 t6 R! ^, Y0 _TimedeltaIndex(['1 days', '2 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq='D')) Z5 l3 _# b) }
    TimedeltaIndex(['0 days 12:00:00', '0 days 14:00:00', '0 days 16:00:00',7 \- ~/ K$ s, J' G& r5 i2 F
                    '0 days 18:00:00', '0 days 20:00:00'], dtype='timedelta64[ns]', freq='2H')
    0 J. Q3 {/ \  m$ rDatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',
    4 e6 P  I- o2 ^               '2020-01-05'],8 Y1 F  U- ?( `, l
                  dtype='datetime64[ns]', freq='D'), O8 i4 g6 t, k2 k
    1
    ) z" i8 W2 Q% T7 I+ X2
    $ \" s3 c$ E- Q  s3
    # {' ]0 l9 c$ V& h4( y/ T5 g" y! ]( E0 y3 G/ m
    5% ?" X) P% P! J/ o' Z  P
    69 y2 c" c# ?0 }' V" D* \
    7  O+ v* p9 A2 C4 M  `8 m" |
    8" f6 p+ t" I) \: f& d! d
    9! Y  f+ B( U9 F4 n' l' |
    10
    ( W  F( I& Z, z' {7 ^! S11. L/ @3 z# ~& f& D
    12
    9 J  V3 I  H& t8 _3 I, J130 |7 d9 U# s3 @$ }/ \
    td1 * 5
    0 \" a6 ~% |# J/ w8 jOut[77]: TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D')
    ; U7 B- g: g. Z# Y0 O) |
      Z! Y4 G; m9 Rtd1 * pd.Series(list(range(5))) # 逐个相乘
    ! a. L8 r: `. U1 J* ~Out[78]: & S% c9 i: h# i$ O2 t$ t
    0    0 days( a% T6 t& B* i# n
    1    2 days
    ' |- f% m& m! d) c2    6 days* T! q: V/ ]% h1 a+ M9 i* y1 R
    3   12 days- n& W' \" p7 r" ?" A" u9 H# E$ r9 G: ~
    4   20 days2 i/ \3 f3 ]; b+ V. @, u
    dtype: timedelta64[ns]
    - N" |9 W5 C, `8 L# J( @  E4 l& T+ O/ H+ t
    td1 - td2
    7 ]0 z9 x0 C4 B) ROut[79]: : S9 _, d& S/ p$ l# h5 M
    TimedeltaIndex(['0 days 12:00:00', '1 days 10:00:00', '2 days 08:00:00',1 a/ c' n5 {# g
                    '3 days 06:00:00', '4 days 04:00:00'],
    , R4 ?3 n, V+ Q# }               dtype='timedelta64[ns]', freq=None)
    0 j# m. o/ f4 l0 f" D: v
    $ a+ i- M% i1 z; I+ [: H* Y4 g( Atd1 + pd.Timestamp('20200101'), ?, Q& J8 \, t2 W2 U+ y) r( i  z
    Out[80]:
    3 u, G  C7 j! m' pDatetimeIndex(['2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05',( v1 z4 {( D$ L( l$ p
                   '2020-01-06'],dtype='datetime64[ns]', freq='D')# \6 ?% W$ ^" V) K

    3 `3 p% ~/ \. i+ h5 p9 Ptd1 + ts # 逐个相加0 H1 {4 v' Z/ o' O& e0 I5 v
    Out[81]: : H3 R; v# L' Z2 l% t3 o0 o& T) l9 {
    DatetimeIndex(['2020-01-02', '2020-01-04', '2020-01-06', '2020-01-08',
    9 }7 H  N& B2 M' b. C% Z" o               '2020-01-10'],
    ) G1 ^! u6 t" I- M# N              dtype='datetime64[ns]', freq=None)
    " u  G3 `9 S$ J, u4 }7 I1 y" x: K( v3 I1 i/ K
    1
    8 W' ?" k4 E6 L: E, \9 R; Q28 Q& ?* t8 o& t- Q( T3 L' x
    3
    " E! U) M' B6 c3 ^8 a4
    , }$ o' ?: L& o+ ~1 x: Z9 z4 h57 X# m/ a/ w5 P/ r
    6
    ) v- b6 L6 S. q5 r) Y2 j7; y4 P! v* M0 t9 t
    8
    5 P: E+ \& K! @8 Y1 C9
    $ V2 E& e  n! C. h& L1 d10- O: Y2 m* E0 T* b( M: R
    11
    7 U; n, b) l" i) k' A12
    : [3 Y! B) U4 g, h6 l139 u$ [. X2 Q! u( c  ^; V
    14
    0 J2 Z1 h9 o- |2 G. Y152 g$ S$ P! s% W: H
    16
    ; A4 H- B# A" m  g4 W17& G1 L( K( _* }/ \! W3 R
    18
    ) A8 l/ @3 k2 e8 Y) T0 K9 R19
    6 g3 e' I# v5 _' s5 D20
    7 a+ r6 ?* H) W" ?21
    # L* c  M4 Y$ t) g! c9 V) \22
    ) f( ~! h, J; C6 }6 a% e23  E9 j. T5 l& Q# ~4 f4 c4 B
    249 L/ f' M8 N9 T5 F0 I1 b
    25
    7 M% x) {( }+ \; F3 X: x" E* D26
    5 M4 y" b: p) h7 c27
    - }2 g6 f! u7 c4 ^0 ~1 v# s6 y28* t% J. {* J: t
    10.4 日期偏置
    5 O: _3 u  g9 O* ~) B10.4.1 Offset对象
    2 U" e2 d2 j: [  日期偏置是一种和日历相关的特殊时间差,例如回到第一节中的两个问题:如何求2020年9月第一个周一的日期,以及如何求2020年9月7日后的第30个工作日是哪一天。! C: S5 {! P! Q1 w" s) K

    2 I' X. a6 e3 k& E3 SDateOffset 类有10个属性,假设s=pd.offsets.WeekOfMonth(week=0,weekday=0),则:
    , q' X* S5 D! y' \( {: }" a1 H
    4 P) c9 \) A6 {' s8 Q. @1 t1 T4 C! @s.base:<WeekOfMonth: week=0, weekday=0>,返回 n=1 且所有其他属性一样的副本8 N/ X7 d- \7 @; n: i, _; m" d/ {
    s.kwds:{‘week’: 0, ‘weekday’: 0}
    ' N7 U( z4 g4 |2 xs.wek/s.weekday:顾名思义
    * d. f6 {% P$ N& ?- k9 ]有14个方法,包括:
    $ P) A+ |9 c0 I# c) R6 ?( H# T& b3 S
    DateOffset.is_month_start、DateOffset.is_month_end、DateOffset.is_quarter_start、DateOffset.is_quarter_end、DateOffset.is_year_start、DateOffset.is_year_end等等。; u6 b& Z* j2 K6 L0 O
    pandas.tseries.offsets.WeekOfMonth(week,weekday):描述每月的日期,例如“每月第二周的星期二”。
    3 E5 L  ~" {) l0 {
    3 m3 G. |5 w& M7 @有两个参数:
    6 ^' ~3 y+ }5 I' t* L+ yweek:整型,表示一个月的第几周。例如 0 是一个月的第 1 周,1 是第 2 周,以此类推。  p4 R5 O1 c: v7 e+ m8 B
    weekday:整型,取值为[0,1,…6],表示周一到周日,默认取值为0(星期一)
    ; W) A+ g; K; Npandas.tseries.offsets.BusinessDay(n):相当于pd.offsets.BDay(n),DateOffset 子类,表示可能的 n 个工作日。+ {' J2 W) Y; B) e5 n! U  Q
    % `7 Z  q/ S3 M2 Y+ r8 q; \
    pd.Timestamp('20200831') + pd.offsets.WeekOfMonth(week=0,weekday=0)( U! N3 t- k: Q
    Out[82]: Timestamp('2020-09-07 00:00:00')
    ' J5 ^9 g: R- }6 a& ~
    ' E1 q% U2 s" ~& t* f3 ypd.Timestamp('20200907') + pd.offsets.BDay(30)
    ( v& E) m0 ]9 `6 o/ f1 ~Out[83]: Timestamp('2020-10-19 00:00:00')
    ! M0 l& x8 k$ g2 o+ T  @' Q. V; l19 E; I1 D  v1 H7 w
    2
    7 r! ]. y4 _7 H# K( a" Z3& d' L5 S  f7 t6 l! k8 R' g, ]# c
    4
    8 {+ I0 ^  _! i% F5  v+ Z; z/ s5 Y- D% |; M& i
      从上面的例子中可以看到,Offset对象在pd.offsets中被定义。当使用+时获取离其最近的下一个日期,当使用-时获取离其最近的上一个日期:
    - M2 g7 n1 M5 C) I9 ]
    : f. @3 @) y: Zpd.Timestamp('20200831') - pd.offsets.WeekOfMonth(week=0,weekday=0)6 Q  W9 N5 M8 U$ }8 d" O# v
    Out[84]: Timestamp('2020-08-03 00:00:00')/ J- }/ T! ]8 k6 y  x
    . |# p: L! v  U9 x- F" O4 g  c
    pd.Timestamp('20200907') - pd.offsets.BDay(30)( S$ M* ^6 G+ w; q$ _" L+ O
    Out[85]: Timestamp('2020-07-27 00:00:00')9 F8 m/ c5 d8 L) _3 m
    1 M2 n( F" [6 ]! @
    pd.Timestamp('20200907') + pd.offsets.MonthEnd()
      h9 o$ v  J3 Z+ kOut[86]: Timestamp('2020-09-30 00:00:00')
    ) _* o/ B4 v4 t# Y; p1 R1
    5 a3 f+ r7 a/ u7 P! y/ b! w5 d22 B, D* Z. _" F7 [
    3
    , p& Q' Q2 E8 |! e" q' j: W4
    6 h7 }3 q7 d+ i* n7 w/ Y+ b% Q53 N5 J! h2 Y% d" \+ @; m
    6! e2 D- d! m6 S3 O1 S
    7
    $ t& V6 V# M4 D3 Q83 |0 k: p; x1 X: n
      常用的日期偏置如下可以查阅这里的DateOffset 文档描述。在文档罗列的Offset中,需要介绍一个特殊的Offset对象CDay。CDay 或 CustomBusinessDay 类提供了一个参数化的 BusinessDay 类,可用于创建自定义的工作日日历,该日历说明当地假期和当地周末惯例。
      E$ B+ A6 b* P+ E# K  其中的holidays, weekmask参数能够分别对自定义的日期和星期进行过滤,前者传入了需要过滤的日期列表,后者传入的是三个字母的星期缩写构成的星期字符串,其作用是只保留字符串中出现的星期:) V$ i% [8 k( W/ Y  d, i! H  V
    9 l5 g; ^# s; Q" e- |" n8 T/ o
    my_filter = pd.offsets.CDay(n=1,weekmask='Wed Fri',holidays=['20200109'])
    ' C2 I/ K  b7 A% E* y2 Hdr = pd.date_range('20200108', '20200111')
    ( C0 m$ [# d! B& g; ~
    + z& [: l5 ]$ h. H4 S  Gdr.to_series().dt.dayofweek; T# v6 O8 [6 a3 c" \' N- v. M
    Out[89]:
    ' H/ ?6 I( R" A2020-01-08    2
    8 z  ]# j) n4 f0 V6 ]" j$ K2020-01-09    3
    4 u7 g! \0 t6 r2 W4 h7 `" ]2020-01-10    43 [  T* a7 ~. I" _, O8 l+ z
    2020-01-11    5$ f" [( [" p9 v; @: H0 m# |
    Freq: D, dtype: int64( O  P, e+ p3 p5 Y

    8 |; I. J' O# B4 q% ^; R! i' c[i + my_filter for i in dr]5 Y; `* P$ B, J, h7 o7 m5 M) A
    Out[90]:
    5 ?' @: ?4 t# Q4 D) U[Timestamp('2020-01-10 00:00:00'),
    : n* |/ V* j6 k9 ?8 d Timestamp('2020-01-10 00:00:00'),  K7 i/ M) M3 @$ m1 j; z3 u/ O
    Timestamp('2020-01-15 00:00:00'),- _7 }6 _- j* V  o. S, D
    Timestamp('2020-01-15 00:00:00')], |; r8 f/ C9 V# J) H
    3 T1 ?, w2 ?8 p5 ?. b  Z3 G
    1
    # E1 Z1 Y" u# Y( @8 g0 J; a, z2# e) q3 }6 P' C8 s! r& W+ D% s5 c% k
    3
    1 H2 `" V1 K& X3 A, [! D4; V* k$ o9 ^. f1 w: b- p
    5* Q0 ~  j' N5 {$ M0 `1 a" _" t- [
    6; @" O6 ^: ?, z; c6 j# d6 @
    71 H$ `% V9 _4 Y
    8
    - t7 @* a: ]3 s0 [. Y3 j1 k/ k9; C, E) w9 Z; q& W
    10
    ! m4 ~) M, t; x: u8 v3 I, o6 e11
    ; [. {$ x1 P: k12
    ( R! ^, I9 r& ~13# P/ Z# T, D  l- e
    14; X- }/ n% {" K6 q& y8 U1 o
    15
      }$ J3 n3 k* Q) D16
    / n: t# ], Y) g8 b4 r17
    7 L" T6 o- c9 \  Y# X  上面的例子中,n表示增加一天CDay,dr中的第一天为20200108,但由于下一天20200109被排除了,并且20200110是合法的周五,因此转为20200110,其他后面的日期处理类似。% W6 d3 {$ X) W& H
    4 Q: @+ M4 p  D1 z% H6 B
    【CAUTION】不要使用部分Offset" a8 N3 z* A4 k$ h7 }
    在当前版本下由于一些 bug ,不要使用 Day 级别以下的 Offset 对象,比如 Hour, Second 等,请使用对应的 Timedelta 对象来代替。
    5 M" a1 k& q) [! r8 g! G; A7 ]0 O- s: J
    % r2 w  ]5 [( b& ]& M7 Q10.4.2 偏置字符串( t+ O: \* E4 ^
      前面提到了关于date_range的freq取值可用Offset对象,同时在pandas中几乎每一个Offset对象绑定了日期偏置字符串(frequencies strings/offset aliases),可以指定Offset对应的字符串来替代使用。下面举一些常见的例子。# J; x& y3 z$ ]: u: M& R

    % M8 O2 C* x9 c9 W  Offset aliases:pd.date_range函数中的freq参数,为常见时间序列频率提供了许多字符串别名。 也称为偏移别名Offset aliases。偏移别名列表点此参看(大概27个)。
    . d5 L+ b* _2 ]4 M/ B+ A, k; Q* |& O2 j, Z; Q
    pd.date_range('20200101','20200331', freq='MS') # 月初
    : L! O; ^) T$ g$ mOut[91]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')
    # z) J& F, X! f8 q
    $ m" m5 m1 Q' U- D- M. }pd.date_range('20200101','20200331', freq='M') # 月末
    % O+ y" m9 }/ M' P. t/ T+ aOut[92]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M'), D0 A  x! J5 ]4 p8 w, Q  n) @
    4 W" @! X# j  p0 g. K  I+ J
    pd.date_range('20200101','20200110', freq='B') # 工作日
    # Q5 Q& [# Z* ^3 v; Q' SOut[93]:
    1 J5 K2 N' B. Z0 D) b$ J4 TDatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
    8 w! \% j) E7 F2 o               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],
    # V* o* z5 w5 y2 ?: y; |              dtype='datetime64[ns]', freq='B')
    # Y" V! W. n6 y
    7 @8 c1 j6 N) k% @pd.date_range('20200101','20200201', freq='W-MON') # 周一
    5 g! D- X6 M8 h1 c, ZOut[94]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='W-MON')) d( q" @) V& H, K/ K( b
    1 T7 M2 ~. v' y- m) g5 G$ Y
    pd.date_range('20200101','20200201',
    ; m, x8 w" ?/ n, I              freq='WOM-1MON') # 每月第一个周一
    ! F6 k/ Y1 V3 \* k4 ?2 [4 V3 Q, `
    $ p  Q+ U) e' l" c7 m* s4 ZOut[95]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')
    ! J( I( ^2 M  T. ~* |6 S  ?# i3 B" ^; A! g; G& w
    1
    ) ]1 e7 r+ X- s1 f- w6 m2
      f* q! s5 t& T  a3: F% y0 t! j6 m" A  ]) J+ f0 L
    40 ~4 l# u1 P2 G, W& I% y) t# |* G( D
    5
    ! z2 l$ x; T+ |! u+ o9 S* Z) Y66 v) r$ @7 j! p. v1 }# n8 @; o% M+ S& W
    7
      \7 d, p+ g4 S8 L# V" q( ^0 d8+ E0 y4 e  o, l+ g8 w6 W8 |$ w
    91 l8 e' W. o7 b
    10
    * a; c: D/ `' U3 c: t# v! A11- b& }! Q/ q; X0 [; D  X; N
    12
    6 r0 i. I: j9 W, U- o13
    . E0 U6 c, D/ C+ R) r14
    7 T/ r7 W' |9 j  V15* M" z. \3 `. ^5 {5 I
    16/ M7 z: N  k$ X. K
    17+ w0 ~# m+ Z* T6 Y3 d5 r  C: Z1 P
    18
    + _9 i  \6 v9 t& \1 p- l19: Q- i3 C5 Y& N4 v4 T. I% w
    上面的这些字符串,等价于使用如下的 Offset 对象:
    $ r" y( \' a4 n7 @
    8 g% V9 T: N- Z/ a  Hpd.date_range('20200101','20200331',  K* P( F5 Q+ v3 V
                  freq=pd.offsets.MonthBegin())0 H/ r; F9 i+ ~8 A2 Q; {$ g8 k
    # A2 w) E0 v  N+ f+ E. K3 @+ B6 {
    Out[96]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')
    , o& f. [* |2 t
    & ~7 O! R) S2 Y+ D- C: vpd.date_range('20200101','20200331',# t0 v& y0 [' ~7 l7 i9 |
                  freq=pd.offsets.MonthEnd())8 q$ q4 X% L, s
    2 B/ g6 p3 @$ n
    Out[97]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')8 y8 w: T7 O3 S- [- W6 K9 R( t8 d
    7 {( Y2 y( C% P$ R
    pd.date_range('20200101','20200110', freq=pd.offsets.BDay())
    % e, a$ N, m& n4 a0 JOut[98]:
      R. H% P7 X& a9 k% o% u: GDatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',0 S8 t$ T4 K) ?( ]9 J
                   '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],
    0 @1 H. v4 x8 C2 W- u3 \              dtype='datetime64[ns]', freq='B'); Q% a8 P# _; @0 t$ P3 |; P5 H0 g  j

    - ~. B0 N$ X2 A5 spd.date_range('20200101','20200201',7 U' C" f/ S1 y( a) k/ x9 v
                  freq=pd.offsets.CDay(weekmask='Mon'))6 C2 s0 e: {# Y. n+ Y: v% }

    6 Q( f$ V% U% sOut[99]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='C')- ?0 s' n+ K& ?- \1 C0 [
    ' w' U( ^9 B: ~  R/ F) S6 `
    pd.date_range('20200101','20200201',( X9 q" E  e' B$ r
                  freq=pd.offsets.WeekOfMonth(week=0,weekday=0))
    ; H+ D& |1 G# H$ S# v2 d; t
    7 R  Y- u4 _! G  ^2 Y# E4 tOut[100]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')& q) T& h. _  C& D6 r
    ' {, o; a. N  z1 V/ Q$ t
    1
    1 L. B4 @5 N- g7 }0 {  c8 g2
    " u8 W; P; Q/ B8 {" q30 T5 F0 _; q+ F/ |% u, u6 R
    49 b0 R, _) F& B' A( u3 `3 b
    5: V; }/ A" s, j: l2 u+ ]
    6: W, s/ q4 i0 _! S0 k% d
    71 S" r; o8 B8 v+ }& Q+ |* F
    83 U: ^5 X3 f8 Y0 H- ?+ r
    9
    + O  D$ _9 A  Q101 j; J. J- o+ C; M, f
    11# s( `; Z$ o7 s. \% j/ l
    121 B6 P- t/ f2 w; q& \( y4 G
    13' z& a5 g3 L7 n7 {1 P* M4 {
    14
    : n3 q  m+ L5 g5 Q% }; n9 X15
      k9 p) k* O  w0 Y% J8 e16
    0 P* }) c3 z; H9 K( C17
    8 U5 W( s. f9 ^1 J6 s18
    * e5 }, K3 L$ w9 M0 p2 e' k198 E/ t) K( V1 T3 Z
    20
    / D' G- ~- i2 P2 I4 o21
    / W4 y. b# Z8 |3 |! i" y- c5 N22# u( j- ?, x5 y  Z9 c% _
    238 C" `* ~4 t3 j5 |
    24
    ; @% ?5 K8 U, Z( X6 F25. H+ m) w  d% o1 J$ U9 i
    【CAUTION】关于时区问题的说明6 ^1 I4 R* |/ r9 G. i  z
      各类时间对象的开发,除了使用python内置的datetime模块,pandas还利用了dateutil模块,很大一部分是为了处理时区问题。总所周知,我国是没有夏令时调整时间一说的,但有些国家会有这种做法,导致了相对而言一天里可能会有23/24/25个小时,也就是relativedelta,这使得Offset对象和Timedelta对象有了对同一问题处理产生不同结果的现象,其中的规则也较为复杂,官方文档的写法存在部分描述错误,并且难以对描述做出统一修正,因为牵涉到了Offset相关的很多组件。因此,本教程完全不考虑时区处理,如果对时区处理的时间偏置有兴趣了解讨论,可以联系我或者参见这里的讨论。: M2 Z8 Z6 q4 y& c9 ^
    ! S+ y+ {0 u" |4 w
    10.5、时序中的滑窗与分组
    / K" |% N  z7 S+ Z0 _6 S3 o% p10.5.1 滑动窗口
    8 W4 ?4 [# n9 s  所谓时序的滑窗函数,即把滑动窗口windows用freq关键词代替,下面给出一个具体的应用案例:在股票市场中有一个指标为BOLL指标,它由中轨线、上轨线、下轨线这三根线构成,具体的计算方法分别是N日均值线、N日均值加两倍N日标准差线、N日均值减两倍N日标准差线。利用rolling对象计算N=30的BOLL指标可以如下写出:: a' T7 R: [+ b6 @* i

    8 o3 T6 V! f) R$ t3 n  Ximport matplotlib.pyplot as plt
    - j0 P( S6 s1 y1 @& Hidx = pd.date_range('20200101', '20201231', freq='B')! H* i! q$ O0 K  A
    np.random.seed(2020)
    # F7 k& Y' R3 D/ h! Z; p- E; B' [- @) s0 H8 A
    data = np.random.randint(-1,2,len(idx)).cumsum() # 随机游动构造模拟序列,cumsum表示累加
    7 o+ o+ [' o, o9 x/ `) rs = pd.Series(data,index=idx)
    ( R8 P7 j/ ~/ ]7 s' Rs.head()
    3 V/ V  k6 P9 R+ [& N2 gOut[106]:
    7 r' ^. a9 Q) ~1 {6 ~1 _% D2020-01-01   -1% u* |  r0 a! {  G5 M# ~5 b
    2020-01-02   -2% s( g5 g$ y+ ^! C: T' s# a
    2020-01-03   -17 A9 a7 n, v$ k: G' p( Q
    2020-01-06   -17 N5 d1 m- L& |) _9 t
    2020-01-07   -29 _5 S: m/ n+ a( G/ _
    Freq: B, dtype: int325 O! m8 J3 y+ U9 Y8 q; b
    r = s.rolling('30D')# rolling可以指定freq或者offset对象6 P; D0 l  ~4 I3 m4 G
    * R( }! t; b( ?
    plt.plot(s) # 蓝色线& U/ N2 V1 E1 C$ l5 w2 u7 S: j
    Out[108]: [<matplotlib.lines.Line2D at 0x2116d887eb0>]* b' R( w- U% C3 {4 X
    plt.title('BOLL LINES'); J1 S; Z3 n$ l, C7 C
    Out[109]: Text(0.5, 1.0, 'BOLL LINES')& ?1 @  k0 _: h' j4 Y2 r
    9 f) ?  I  f2 b: c% y+ ~1 I
    plt.plot(r.mean()) #橙色线
    ' A8 {* ]: b4 MOut[110]: [<matplotlib.lines.Line2D at 0x2116d8eeb80>]
    ! J+ J. ^' n7 s, j! s
    1 T. s9 |. e1 J% @, E; n: Bplt.plot(r.mean()+r.std()*2) # 绿色线
    ' l$ Y* d5 `, Z2 [" OOut[111]: [<matplotlib.lines.Line2D at 0x2116d87efa0>]* U* d2 B& f* Z7 F* X' F0 n4 f" F, O
    4 D; i% `" T0 I/ K* `( |
    plt.plot(r.mean()-r.std()*2) # 红色线6 s$ [3 x' P$ ~4 D, a$ o7 S
    Out[112]: [<matplotlib.lines.Line2D at 0x2116d90d2e0>]
    * {3 j2 s6 m& ]- z; G! C1 N
    % Y4 E8 T' ^. _- q4 C7 r1% f, {2 }. V' s* ?$ W$ O% W1 k( J
    27 ^, F; X) ^& Z( `0 Q
    3
    + I& ~8 Q/ P* r8 S; r8 b4 B4 D4
    ' O- T0 N$ G- n5 S% S: Y4 K3 \$ o5
    " |7 L( g, V1 ^( }6
    - \2 W# u, z- p& D- Y! C77 T1 S3 X, F( a) N" q
    8
    + @( K! R6 m7 j7 J; h# s7 a! E9: c8 H4 O7 `5 J" t- B
    10
    5 w4 j! _& s4 c2 t8 l; e11
    . A6 f, X( _' f$ b125 ~& e4 e1 l4 i! m$ {
    13
    + k! d4 |# D- I* v. L2 n3 v14' {( T- U2 `" N0 E: Z& g
    15
    / r9 }# T3 F& p16
    - `' {& ^/ W" S" |5 H17
    + B" w, X5 ~  L1 \1 `; ~4 L1 M18' t2 L% T. z: Z: o7 p& j
    19
    + u6 B8 c+ x7 H" Y203 N% @7 [7 q3 J3 Y
    21
      |* W# n" y" s$ S# A% \' B22: U. w) [8 |, ]
    23
    9 j! r2 _2 ^8 U. Q; Y$ r# _8 [6 ]24
    & _# H. d, |8 k/ i25# [& b2 @% H0 W
    26; _/ M+ K- i- ?/ q6 ]5 m
    27  N0 ?+ @6 W- K- O, V  {
    28' G* ^% V, }; q/ Y, z: b
    29
    $ c+ z% e0 Y6 a) Y/ n
    ; C: B, L) O$ d$ A. f$ o   这里需要注意的是,pandas没有实现非固定采样频率的时间序列滑窗,及此时无法通过传入freq字段来得到滑窗结果。例如统计近7个工作日的交易总额。此时可以通过传入多个函数的组合来实现此功能。  p) X5 a/ W( j( I: b
       首先选出所有工作日,接着用普通滑窗进行7日滑窗加和,最后用reindex()恢复索引,对于双休日使用前一个工作日的结果进行填充。
    0 U" ?$ Y( U8 t4 \1 ?/ f) y  Y& e7 ~8 i4 `2 W
    select_bday=s[~s.index.to_series().dt.dayofweek.isin([5,6])]. L# E8 \% R! J7 z+ A% F# J4 c
    bday_sum=select_bday.rolling(7,min_periods=1).sum()4 [  r: ?: T5 j6 W
    result=bday_sum.reindex().ffill()
    . T: B: D0 z: X& R" m* F3 hresult
    - X: g9 ]: z! C; S% I6 n2 C+ J6 b' m- j# J, I
    2020-01-01     -1.0
    3 J# |" D( B& E- g8 D* @2020-01-02     -3.0
    ; G0 r/ q& P; E) C8 M+ d" [4 @2020-01-03     -4.0$ Z  V' c" D; S$ h- M- ?
    2020-01-06     -5.08 |! z, V" _& x2 O% R, k9 P) l
    2020-01-07     -7.0; F& u% c2 u" w1 `. V6 d
                  ...  7 n* C5 c* r6 }; f+ I! K
    2020-12-25    136.0
    : t+ ?8 p' n! \( a0 K" l# A, n6 ]2020-12-28    133.00 F+ ^8 y# F9 c9 k& S0 ~
    2020-12-29    131.01 W7 V# X$ {. k7 W
    2020-12-30    130.0
    9 V9 l1 L/ k; n5 w% p3 F% p! k2020-12-31    128.0
    * m0 Q6 f" w7 y9 PFreq: B, Length: 262, dtype: float64
    1 C3 ]/ k, O% w. ?. R  p$ r; |0 v5 l$ I* b$ s$ @
    1
    % @( Y. T* M4 A) F/ E2 G: s2: x2 P8 p7 L! v/ B
    3
    ; e1 s& i% O7 e% ?: e1 S4- L& \" U, i9 x% C6 }9 X+ w) k1 q$ d2 n
    5
    1 J% ]: T. T' u/ P6
    ' y' H% Z* |4 T& T) e7
    7 a( n/ H2 g  q' }0 p% |0 {8
    0 L* @2 x/ \8 p( g( \  y9- S3 m$ D# y* y4 F
    10
    5 X9 ~  Z* n/ s112 W) F) G2 x( Z: N
    12
    / Q6 a7 ]3 g1 X1 t9 m13
    . a% K# d5 n! z  N7 Z8 F; X14) f5 P' G" s( F
    15
    1 b: S7 Z% O" O, _% D! H16
    & j6 ^/ Z% H- R9 j, I17" v" J* v9 z8 K, b3 o1 z0 q
      shift, diff, pct_change 是一组类滑窗函数,它们的公共参数为 periods=n ,默认为1,分别表示取向前第 n 个元素的值、与向前第 n 个元素做差(与 Numpy 中不同,后者表示 n 阶差分)、与向前第 n 个元素相比计算增长率。这里的 n 可以为负,表示反方向的类似操作。
    3 r' f( x+ q% [0 H1 ]
    $ P4 Q$ ^* G; h  k( Y# U" L; v; X; q  对于shift函数而言,作用在datetime64为索引(不是value)的序列上时,可以指定freq单位进行滑动:
    3 @/ h; k/ D. c5 x' `6 f0 @* n! p6 D: h! Y: Y/ `1 X3 R: p& e! e
    s.shift(freq='50D').head()9 g4 e0 H/ O% q
    Out[113]: 5 z/ b$ V, z9 h0 M; Q3 b
    2020-02-20   -1
    % g# ^/ ~! J4 P2020-02-21   -2
    ( R: [( O) C; R! T- t/ f& e2020-02-22   -14 k! L" H9 T: o+ ~
    2020-02-25   -1$ Y( |5 r- F9 g! ]8 X9 B
    2020-02-26   -2
    ) \2 v- N  l+ b+ y) Idtype: int32
    2 Z. Q' e2 v1 O' |1
    % v. M' q; ~3 Y# u" X1 w5 ~2. @5 R2 T. H4 i
    3& A! T2 P3 L# i# D2 T- S. ^7 {
    4
    * E% z" l1 ]0 H) L( q* _) ?8 T0 ^; o5
    ! \5 g, U3 n0 @$ m6! _6 J7 d- J, U1 Y8 w5 \* p
    7
    8 ~( H& u7 S3 m9 C+ z8
    . R8 ]. F0 \! O9 `$ [# C  另外,datetime64[ns]的序列进行diff(前后做差)后就能够得到timedelta64[ns]的序列,这能够使用户方便地观察有序时间序列的间隔:! x6 ]+ V5 D. ^. U( S# m+ Y
    6 g* J2 d! G; y7 v' w
    my_series = pd.Series(s.index)
    6 e5 w0 O* q: y# [my_series.head()
    & |3 p  |+ h: O4 R& nOut[115]: " h, r! q; @7 U& b# g7 p& C- ~
    0   2020-01-01
    " A) e2 k2 b" {1   2020-01-02
    % ?& q2 Q! o& ~5 U3 O: u2   2020-01-03
    ; V( l) y; ~& @" i3 w3 H3   2020-01-06: K/ q6 @' {3 x1 q) c, f$ {
    4   2020-01-07
    9 L/ _3 \& K: b& s+ g8 V* a/ fdtype: datetime64[ns]
    # x, F5 y, a/ o& W2 e% T, i; k
    % |) L' i" R9 c" g2 M/ e( Ymy_series.diff(1).head()
    1 q$ H% K" y5 U& e5 lOut[116]: 8 m/ r+ i3 A2 @! Y) Z
    0      NaT* u( _8 n8 l3 C1 n. s6 u
    1   1 days
    9 S/ i! P8 {, E7 P2   1 days( b) ~: A: I; z: n0 s
    3   3 days4 T) ~$ w4 [7 E! i/ q4 Z
    4   1 days
    2 i/ i! t& g9 |/ x6 b# h2 o  Wdtype: timedelta64[ns]
    9 x: o3 l% q8 f/ R8 w6 l+ r& V& ~3 e# E/ R7 r+ l
    1
    0 D% f6 W2 S* e. X9 ~7 h2
    ) Z& B* Q8 @# ?3 N$ l3: `3 w! T1 P8 e) o  I! M0 x% i: S
    4
    # }8 B( W4 f& w% T6 v3 w5
    6 p# U/ L' A, u6 \( ~. Y6
    % R9 ?! N( m8 B! b2 g9 f( {3 ?7$ ]0 J' u. z( ?% b1 F# d, K
    8/ x+ X9 Y- f5 r; F* o  I! ^) n/ e; Z
    9
    # W) _2 c) R2 H# g8 ~. k10
    8 t  M+ I% g% M) }3 r" e11
    6 q! h( H8 J! A2 D! {124 Y* R9 h3 P% x2 z
    13
    ! d) w. x+ R# |9 r2 Q8 l- c14% K( X9 D. W/ F5 ^+ O: B! L
    15
    ( R" a) Z9 m+ J166 p' I' n' U' k! |. [3 J7 R
    17
    3 F# \5 c: f( X& T3 b18: x# ]: l3 G0 q+ L9 M# A! _
    10.5.2 重采样
    # o4 W; `! [. e# `9 B5 I3 @  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)7 s2 w; P/ `; J4 V/ v
    常用参数有:& J0 P$ p2 F4 V; ~5 K+ ?

    # l: S/ m0 w& l& w; O2 g: `3 K+ }rule:DateOffset, Timedelta or str类型。表示偏移量字符串或对象
    7 @$ a' l* Q* Y' w9 \- V8 R2 Iaxis:{0 or ‘index’, 1 or ‘columns’}, default 0。使用哪个轴进行上采样或下采样' \* ~; C( |! R9 Z
    closed:{‘right’, ‘left’},默认None。表示bin 区间的哪一侧是闭合的。所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。; ]- ]( Q" J$ c) x9 Z  {: O
    label:{‘right’, ‘left’}, 默认 None。hich bin edge label to label bucket with,所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。& e& X$ O" U2 b* {0 j, G! b
    convention{:‘start’, ‘end’, ‘s’, ‘e’}, default ‘start’。仅针对 PeriodIndex,控制是使用rule的开始还是结尾。/ P( X: \. |, Q1 ?5 [7 D2 a) Q
    on:字符串类型,可选。对于 DataFrame,使用列而不是索引进行重采样。列必须类似于日期时间。& C. E1 l7 h, X, P
    level:str 或 int,可选表示多重索引MultiIndex的级别,这个级别的索引必须类似于日期时间。
    . }* F1 v! e! x7 {origin参数有5种取值:5 G0 ]7 G: l3 h
    ‘epoch’:从 1970-01-01开始算起
    ( a) d5 |1 |& a‘start’:原点是时间序列的第一个值
    ' ?7 ^- d7 c0 @) V5 c‘start_day’:默认值,表示原点是时间序列第一天的午夜。
    9 s5 x4 l: F% ?. k7 \/ I'end':原点是时间序列的最后一个值(1.3.0版本才有)6 t) z- n& |' @  Z( s& D1 A. D
    ‘end_day’:原点是序列最后一天的午夜(1.3.0版本才有)5 V% p& ^* ?+ H' R& C
    offset:Timedelta 或 str,默认为 None,表示对时间原点的偏移量,很有用。
    2 X: L8 G5 N; O2 z0 E4 U0 W  closed和计算有关,label和显示有关,closed才有开闭。' l1 a& j' N% a. y. x% E) a
      label指这个区间值算出来了,索引放区间的左端点还是右端点,closed是指算的时候左端点或右端点是不是包含。
    + z; u0 m: W: ]) i; R. o7 ]" K, X2 v! w3 Z
    重采样对象resample和第四章中分组对象groupby的用法类似,resample是针对时间序列的分组计算而设计的分组对象。例如,对上面的序列计算每10天的均值:" D. o8 e0 T% L! _4 d  E
    s.resample('10D').mean().head()
    2 C. k# d/ e. b" |8 g9 SOut[117]:
    1 x# ?: G6 }) F+ D2020-01-01   -2.000000" ]* j4 t5 W4 w$ q! R" U
    2020-01-11   -3.1666670 l, k. H# q2 b: B3 B; D3 u4 E
    2020-01-21   -3.6250006 ]& W. g5 l3 \, M3 N7 f
    2020-01-31   -4.0000008 a/ L& P" ~. U6 B' f
    2020-02-10   -0.375000. {2 i6 e3 M' A$ w( o
    Freq: 10D, dtype: float64
    . a; E$ q3 b; @9 s, W. h1
    , k2 h9 L1 E; D6 q) }2
      ^: E; k) @# l3
    4 ~# A+ l- d8 b) s4
    ; H+ U% D+ z- e. ?: |3 P4 `5
    / }( U! W7 ^4 t) k6
    * z9 c" e5 n6 P7; q& r1 ~# |/ \/ h
    8+ `8 \! D/ _% y
    可以通过apply方法自定义处理函数:
    $ @* O' j" [' l; Bs.resample('10D').apply(lambda x:x.max()-x.min()).head() # 极差" b' e; Y4 c3 D3 e5 q
    1 O0 _' O; f. A$ g- L
    Out[118]: + q  Q: B& ?. D: G2 o, L
    2020-01-01    32 Y$ m$ S3 a  O* Y+ P0 v2 l
    2020-01-11    4
    # C$ h/ m4 E! J+ R  j0 j, X2020-01-21    4; `2 l3 `5 D/ O* }  [" ?
    2020-01-31    2+ D/ E" Y8 n! I. p2 N2 {& R
    2020-02-10    4
    ' ^! j. F* K( ]Freq: 10D, dtype: int320 b- e$ R+ z; z
    1
    7 P. o( B0 P' Q( I2. v& G' b$ ^$ V2 g% l/ b7 Q* @, C+ P
    3% W8 y) M: p9 v% X, t; {9 O
    4
    + D  b) V, ~& J% `: C% k% c. ?53 S- `* T- K7 `' n  |7 o' w; j) j/ R
    6
    5 o4 H8 ?9 y: ?! A) I: _7
    / p( z; Z+ ?7 G3 g" Z8
      I4 p! t/ |& @7 D! O7 D, ^+ u) V9; w3 m% i2 ^! H7 _, o! K' b' ~  V8 X; ]
      在resample中要特别注意组边界值的处理情况,默认情况下起始值的计算方法是从最小值时间戳对应日期的午夜00:00:00开始增加freq,直到不超过该最小时间戳的最大时间戳,由此对应的时间戳为起始值,然后每次累加freq参数作为分割结点进行分组,区间情况为左闭右开。下面构造一个不均匀的例子:
    , L: [+ ^7 G4 z
    0 y( ], _; K1 q4 p" jidx = pd.date_range('20200101 8:26:35', '20200101 9:31:58', freq='77s')
      v1 w: K4 t5 M( S: Cdata = np.random.randint(-1,2,len(idx)).cumsum(): F0 V! y8 f. ]2 j1 s9 y6 q2 `
    s = pd.Series(data,index=idx)7 n5 e3 t+ S: t* a7 J4 ?
    s.head()4 a9 L/ h8 q% D1 E3 G

    9 G; Z( c; K4 S4 q' k+ P9 L+ n/ `Out[122]: 5 e3 X" s6 N% ^. ^5 c! Z
    2020-01-01 08:26:35   -19 g5 Z# h- `/ S: J5 y
    2020-01-01 08:27:52   -1  g6 m1 n- q, L- H
    2020-01-01 08:29:09   -2
    # ?- ^7 |8 @8 S; D2 E0 w' `2020-01-01 08:30:26   -3
    ; v- p. L0 H- z2020-01-01 08:31:43   -4
    ! W* S8 i8 a+ U  U. UFreq: 77S, dtype: int32; }0 g0 E# C+ N; N6 i$ @
    1
    : H% Q4 z! ]$ G' F29 _* z! c& c- c* }' d0 V
    3
    . h$ w' y1 T- D9 v2 S' \7 `( V4
    2 e) R/ k% o5 J6 R/ ^1 N% j5
      k/ i+ p( |, J& F( m1 R; r64 |( [1 D2 E" {$ N' u$ q% y. N. A
    73 ?; h* L0 q( W* T( A7 _
    8
    / p4 [' t2 i: j" E+ p9
    # c( ]- U6 n3 S7 g10
    1 o3 r3 |# e3 A% [11" b; K/ Q. a6 s. w% k/ q" y
    12( b* R- P; d$ X5 K8 _
      下面对应的第一个组起始值为08:24:00,其是从当天0点增加72个freq=7 min得到的,如果再增加一个freq则超出了序列的最小时间戳08:26:35:# g4 g7 D+ g9 D- h6 d2 A+ \3 m0 \

    8 O; D$ y& d6 y8 w7 W/ gs.resample('7min').mean().head(), W$ f, \- S+ J9 c2 t, I! M& @1 j
    Out[123]:
    ) P/ D+ \: v! B: r( t# I  n2020-01-01 08:24:00   -1.750000  # 起始值,终点值包含最后一个值# r3 f/ F( `* H5 d* D# c8 i. w
    2020-01-01 08:31:00   -2.6000003 N6 A, |2 g; t! \& O  |8 Q
    2020-01-01 08:38:00   -2.166667
    $ G3 L+ l% `# ~& @. G" Q! O2020-01-01 08:45:00    0.200000
    8 A! a0 @, Y( \# s; b2020-01-01 08:52:00    2.833333, r+ `8 x7 p+ [3 D
    Freq: 7T, dtype: float64( i0 ]* J! L% M1 X/ Z. T/ R
    1- V9 y0 Q) N+ ^
    2
    , Z; o; ?+ H5 N: w3. J* g: D: Y4 k- J* e5 X. O
    4
    " ?* `" x8 Z# `2 P& ?5
    " V" |7 U6 y# S: a+ q67 u) A6 j  E& w/ G- A! j6 P
    7& Q1 e2 j) h; b3 {7 u
    8
    : D4 C( b% N0 K- H; @  有时候,用户希望从序列的最小时间戳开始依次增加freq进行分组,此时可以指定origin参数为start:, P: y/ c1 {$ Y: }9 u3 E2 ^) i

    + Z+ x' L/ V9 E9 Rs.resample('7min', origin='start').mean().head()# D& U& d3 o  J" B
    Out[124]: * {2 r8 T, B1 o  |3 u
    2020-01-01 08:26:35   -2.333333
    ! |' I2 j6 |$ S* l2020-01-01 08:33:35   -2.400000  \9 @8 @/ }$ z3 t) a5 l
    2020-01-01 08:40:35   -1.333333
    $ v! i% s- y* H- h- n' L2020-01-01 08:47:35    1.200000$ Q! U1 Q+ [/ }* ^5 ]3 H
    2020-01-01 08:54:35    3.166667% w. X% z: z1 D7 ]' G( s% I/ Q: ^
    Freq: 7T, dtype: float64
    2 V0 J; t& P' n. `0 ]1  U! j( B+ i9 y; r& R, B
    2
    % U/ i. s0 Z- B4 L3 {6 v3! O" h* u* ]2 V, [: u
    41 \0 ]5 U. R1 y" n9 F
    51 s, l& I3 f; v3 N6 i7 w
    6$ N6 b% K3 f0 i) R8 C
    7) r2 ?. a1 p6 L) H6 X
    8& s  N* Q( f* J- \$ F
      在返回值中,要注意索引一般是取组的第一个时间戳,但M, A, Q, BM, BA, BQ, W这七个是取对应区间的最后一个时间戳。如果想要得到正常索引,用’MS’就行。* v4 o( G0 t9 n1 z9 s4 @6 m
    3 l$ q/ a% X; I9 u
    s = pd.Series(np.random.randint(2,size=366),
    0 R' Y# p5 D  {  q8 `" W+ R/ X              index=pd.date_range('2020-01-01',
    " _+ I! o% `# K' i/ H' x                                  '2020-12-31'))  a  {- K5 b1 N5 B* n. y: @
    & F+ X3 ?; J$ |: C2 r( T" |

    0 v3 j; T* p3 D% @' \( us.resample('M').mean().head()
    ( x3 w+ q2 z( _: P+ C# pOut[126]: ! f& P1 X* J( ?1 f9 j& A
    2020-01-31    0.451613
    ' C; T& i- X, t4 I$ M, I+ q2020-02-29    0.448276
    + K# |9 _! e# p* ^7 ]9 B5 a2020-03-31    0.516129# x  S/ l& m! ^
    2020-04-30    0.5666679 \5 M4 ?. o; U  `$ y
    2020-05-31    0.451613
    " V5 u9 e% M, M3 S2 VFreq: M, dtype: float64
    , V1 w* q# O4 @( u& t
    1 [0 ?$ M/ T' a1 c1 g. a, Zs.resample('MS').mean().head() # 结果一样,但索引是跟正常一样
    4 I6 l- B  j- T" U/ xOut[127]: : s+ f' Z0 a- Y9 @! r
    2020-01-01    0.451613& k1 Q" j) |* f- a! q- T
    2020-02-01    0.448276
    $ ?) ?9 x9 t4 d2 Z& g! B0 y. N7 q2020-03-01    0.516129
    * x* |, M( ^0 h2 `. q% E# z2020-04-01    0.566667
    , O5 B' A% D% t6 I0 y8 X! P, S2 V4 V2020-05-01    0.451613
    3 a1 |; ?( I3 ]( Z9 v5 n' i/ o% UFreq: MS, dtype: float64
    - l: c# C6 ]" O# L+ v2 ^: l/ T
    - {( C3 E7 A. e  O- w, l- q1
    % O9 ]; {, F( D+ J- B2
    $ C; `) Q0 f0 a# P2 }2 n/ V, Q9 m3( ^+ f+ j4 K& }3 B. ?  r" l- c' t0 Y
    4
    8 t- q3 e/ ]: l( l4 X! {5" v1 I7 U/ ?& H$ B! G5 v9 z
    6% S) T) }: G( q) ]" W
    7! e8 [$ F- c1 _# n1 Z
    8
    ; D2 T. [& d5 ^9 ~& _4 V) u9 k. N9  o  z" M$ o5 d7 ~; z1 _
    10- d# h% [; z4 M( _7 t% t
    11
    + Z+ u; ~+ D0 {( P1 J12
    3 B2 ^7 G; I  E: }! `2 o% ?) ^13) h8 X' D* j7 r5 [
    14/ O# ]) o% @! ^8 X
    15$ q) }+ F/ M6 y; Z  a
    16
    , T( z* ^. D% [4 m, v. }17
    3 y0 z/ `- \( v$ o3 N4 J: ?18" ^8 p& K; y0 k1 Y' z2 u
    19
    % N: w0 O5 W$ e2 A3 i206 |5 y0 U+ L  R1 Y$ W9 ^3 T3 d
    21
    2 @3 i7 [+ O" V0 x8 {' o' n22
    $ ], T8 u2 O; `& a/ i对于 DataFrame 对象,关键字 on 可用于指定列而不是索引以进行重采样:: ^: }! ]  g* g/ U$ l
    d = {'price': [10, 11, 9, 13, 14, 18, 17, 19],
    6 |3 H. a, Y$ k( v/ H     'volume': [50, 60, 40, 100, 50, 100, 40, 50]}
    : M( }4 L) N7 m" E+ ]' Udf = pd.DataFrame(d)
    6 U+ |8 _  z2 l6 F2 V6 d# ~0 bdf['week_starting'] = pd.date_range('01/01/2018',9 r0 e. G/ _7 S* Y- A( @
                                        periods=8,
    # H) |* t# z3 @' n                                    freq='W')" h' @* v; D, @' s) o# E
    df
    $ s# [1 B: y8 E+ q( X) R   price  volume week_starting
    2 O& K1 ^, \9 |2 |, M# `0     10      50    2018-01-07
    ; A9 o$ d. w* z1     11      60    2018-01-14
    + N: H% a6 |/ U5 h% B7 ?" n2      9      40    2018-01-21
    . Q) B7 h9 y* T" K3 O: B9 ^3     13     100    2018-01-28( A! b7 j# {4 |6 H! F: Y2 Q1 j( v
    4     14      50    2018-02-04
    7 A8 |9 s8 A! f2 ?7 }1 X3 ]$ L5     18     100    2018-02-111 Q6 @' c6 ^* e0 X1 y
    6     17      40    2018-02-18
    ' E! b9 C9 Z; t1 @7     19      50    2018-02-25
    ; H8 d+ w6 _+ D) f3 {df.resample('M', on='week_starting').mean()
    9 n( T. n9 x: a/ D& A9 c               price  volume  d2 M* |1 a4 m- Q& A- i5 n9 O
    week_starting
    & P' r, K( j2 \2018-01-31     10.75    62.5
    ; l* _9 o4 {# _5 c2018-02-28     17.00    60.0
    . p) t+ v% M- M1 C* @9 B" n
    * m' x- o+ g+ {! }1
    ; Z1 A7 R) Y4 f7 b2& Y! @8 ]9 Y( B7 k2 F1 x
    3
    3 Z; c& v0 d. ^$ A( N! N4! r+ F4 |8 A, Q2 p/ N
    5
    2 j" G+ a. ~. C9 Y) S! J6
    . U) \3 ?2 T1 U! w& g9 z7; A3 w. S2 {( R
    83 V; z- g+ b- p; Q# S: L
    92 f. m. }5 `/ [( `
    10
    1 p% Q6 m1 ?" q8 r/ Q11: F* o* V+ ^- e
    124 g, ^* e: @$ F- S
    132 K; \  J& |' @2 W+ C
    14! E6 S& `' I3 r+ C! u
    156 D) e3 q3 O* P+ |7 E" c2 B2 p
    16+ y6 y6 [! x  F( m) a7 n$ k- B$ U' }
    17! _- ?7 `9 j4 R* A% T  T: Z
    18* ^& m8 e: M0 n, g# f
    19
    9 p3 J0 a9 ]/ D' e! @, U  P20: ?) u7 J+ t# Z+ f
    21; \/ B2 U5 ^  [) _
    对于具有 MultiIndex 的 DataFrame,关键字 level 可用于指定需要在哪个级别进行重采样。
    & k# Y7 r) P* P# w( I- Hdays = pd.date_range('1/1/2000', periods=4, freq='D')
    5 ~( l6 d6 }7 n+ T6 Fd2 = {'price': [10, 11, 9, 13, 14, 18, 17, 19],
    9 Y. z& {, x, b# A# R) H5 r      'volume': [50, 60, 40, 100, 50, 100, 40, 50]}1 B" S& u! _( d1 m8 R6 \1 h8 d
    df2 = pd.DataFrame(
    , i6 |/ K+ ?" `4 V9 m) d5 D8 G4 a    d2,
    ' q- G2 D# N! p- f) Y8 b! G( S/ \    index=pd.MultiIndex.from_product(
    6 {# \4 r4 u& j- t4 S, p  y        [days, ['morning', 'afternoon']]5 U: l  a% t: T9 u/ z( x$ J
        )
    # a& A: e8 a: E* s8 K). S1 S, U* N" ?" J  d' C0 I$ H3 V( F
    df2
    0 A$ s$ [/ {5 n) f% ^                      price  volume
    & S& x! R: Z4 R& M- `2000-01-01 morning       10      501 p+ x( e1 w5 o6 U
               afternoon     11      60( V, `$ p. |1 J+ Q& _
    2000-01-02 morning        9      40
    9 R2 c+ s/ Z- F. ^6 k0 K           afternoon     13     100" F3 O" B6 Q2 q0 D6 z
    2000-01-03 morning       14      50
    8 i5 s# p5 _6 h% f+ c4 b# c: n           afternoon     18     100- N/ u6 E. _$ d3 H9 r" n
    2000-01-04 morning       17      40
    : h# B1 Y& D, e1 {: W           afternoon     19      50
    ; s) L8 m. h" Z' }" z6 Idf2.resample('D', level=0).sum()
    ! h& h" M* K. t) _: O$ @            price  volume
    . s* Q/ J0 {$ O2000-01-01     21     1106 R0 p1 F$ N5 H3 C+ Z
    2000-01-02     22     140
    * W( T; H5 l+ B' Y' M+ `1 l( `8 T2000-01-03     32     150
    ' D/ a4 m  u' H- c, h# ]2000-01-04     36      90
    - C8 H' I- c8 H8 l/ g7 v8 Z, x6 {' ]7 F9 o
    1
    / h$ p! z/ g7 K& K& f# z26 M3 ]& [  c, }$ F
    3
    1 i9 b/ `5 U! |. C4* |8 ^$ R9 ^/ p* O+ \8 p
    5* ~0 V! k: W  ]' J6 t8 Q6 R
    6
    & `2 W) q2 X" e' I; p( r7
    + w! S  V/ [3 C' A8: L, r3 M. a( F: l! Y* S- y
    9# i+ ~  w: |$ m* ?
    104 U- r# e. d' k" P. @
    113 g4 r# i1 f: N3 c0 i% r
    12
    ( ~0 s& r+ P- j+ u" W6 F  j13
    7 v( k' O6 f& x" J. M14+ C2 A) B$ Z9 G4 S0 [6 S- ^
    15- W3 ^- {! M$ Q" Z2 D3 O
    16
    - n: v4 l& \4 e% _172 i7 i6 y8 ?( j
    187 b1 q3 I7 }2 K5 B9 S/ d, t
    19
    2 `$ b: X1 \/ \, F20
    ! w$ Z  f: Z/ ^, M- w# x; t219 M) C7 p* h7 h! i/ J8 W* J7 g1 F
    22' n7 X) Y9 |+ M
    23  W  r1 U( s; l( Y: ], G
    24
    5 c$ L0 H* L- e! ^3 D" X0 @25! S$ z) v: d, [' d' }( S; z
    根据固定时间戳调整 bin 的开始:
    # Y: F) S) m5 H5 Z0 {start, end = '2000-10-01 23:30:00', '2000-10-02 00:30:00'8 K8 Q& K) i6 e" H0 z
    rng = pd.date_range(start, end, freq='7min')0 U3 j; K# o1 A
    ts = pd.Series(np.arange(len(rng)) * 3, index=rng)1 w$ O5 ]$ ^: n
    ts+ p8 J5 b" E  n, U+ S* }; W) V
    2000-10-01 23:30:00     09 r% R6 [* B+ G1 v9 L: ?0 h+ Y
    2000-10-01 23:37:00     3% B# ^9 O" w& h
    2000-10-01 23:44:00     6
    ' {6 X( K8 o' ?7 c1 d* Q2000-10-01 23:51:00     9
    1 ?* Q" V5 s$ {% S8 k4 M: V% B4 M2000-10-01 23:58:00    12! U! Z: J9 [& Y# ~
    2000-10-02 00:05:00    153 `4 f9 r4 N0 b9 f& C8 e/ Q' A3 K
    2000-10-02 00:12:00    187 V. t5 e9 d, x+ V& }. L1 X8 x
    2000-10-02 00:19:00    21
    $ X! ~& m8 `0 ]" s* Y$ v) |; h8 N2000-10-02 00:26:00    24
    / j2 W: L5 R3 R1 \; k1 o9 tFreq: 7T, dtype: int64
    " K5 F& m( I9 T' p/ S: E( y- U2 F. k
    1 }2 p; [$ U' k- j& E/ X3 ?ts.resample('17min').sum()) y- v% s& i4 l& c; v8 r
    2000-10-01 23:14:00     00 `7 w0 s; e) I" u* N* D  G6 J. o; q
    2000-10-01 23:31:00     9
    ) @5 s( _$ k/ |( s2000-10-01 23:48:00    213 Y/ [8 e4 h- X% o) Q5 K
    2000-10-02 00:05:00    54
    " P- e: r; P/ [8 j  ^  I2000-10-02 00:22:00    24
    , @9 ?, c: u6 A, IFreq: 17T, dtype: int64
    , Y3 r! E2 z1 [8 T% M# O) N5 q' \* u8 R7 k5 ]; `
    ts.resample('17min', origin='epoch').sum()
    : I; Q0 Z" Q% a# u4 ~' `2000-10-01 23:18:00     0+ d( A2 H0 K( f
    2000-10-01 23:35:00    18
    " [4 i5 o3 U  m& b/ b7 @2000-10-01 23:52:00    27
    % W1 |! ~6 U( x# _2000-10-02 00:09:00    39
    # E- E$ V5 M  v4 X$ g$ w2000-10-02 00:26:00    24; L& \; ]! l0 ^$ l
    Freq: 17T, dtype: int64
    0 ~( |; V: \" f% _: d8 l7 G
    , L7 w$ N/ L. M6 z/ ?ts.resample('17min', origin='2000-01-01').sum()
    , K2 f# k$ H# m2 |0 B+ g2000-10-01 23:24:00     3! ^. z, Y2 ^% t5 A- ]7 n
    2000-10-01 23:41:00    15- U1 O# }; E4 p0 ?
    2000-10-01 23:58:00    45- V/ l  C+ G" ]5 l
    2000-10-02 00:15:00    45
    3 v; |/ e( a8 u: ^) |6 FFreq: 17T, dtype: int64
    % U) v7 F' j0 o
      ^, W7 E) _! f0 x8 p# s, p. @15 h$ q+ y$ Q  }% Z
    2
    % [) t: D/ K) a5 b( X3: V. y1 B: k, J8 K$ p2 e
    4* E5 e& c( k5 a6 S7 {1 W
    5
    ' p6 w. ^2 T/ d: |. v" b9 e6
    " i  W4 ^; o( c7
    5 A/ Q" x' a* K& k6 U3 c8
    ) p- D4 Z5 o' L$ f9
    , t) i- I8 t. J' e+ _. x2 e10
    ) N# Z2 C" Y+ b8 W7 K11+ o5 ^& A$ G1 I" M; \
    121 G/ W* q, ?4 c. {7 A6 U3 u0 M
    13
    ; M* I6 y( J3 \! g  y! |7 v/ g, B14# M8 \$ R, X$ M' I: K  x
    155 g: z) r* y) Y. L
    16
    # i2 E- H4 V! r177 f+ ?: {& p6 }" @: E
    18# K1 [( ?) n3 k" ^6 m
    19
    ) r" l8 h7 \; S. v200 B9 J7 O5 e- v6 X' o: w2 Y
    21* X  g; J6 f8 x8 H  g2 x  L  x  y0 p# f
    22/ u7 f1 T" p" |% x9 y+ a8 c$ O
    23
    9 S  d$ R5 o4 F5 U: X9 @( R24; R! D% `& k" h: i
    25
    ' X3 k1 G3 s/ R6 y3 O3 m26
    , M* c# Y+ e* N  A276 k2 Z% N& V6 m8 _. b2 Y
    28! o- l+ |$ m1 W2 t+ `  t
    29
    ) \0 D7 l5 `+ }0 o30
    ! X9 p6 `* h% S& m+ Y316 U2 t8 o2 p: r5 r
    329 A' n1 z7 r$ V1 Q  r
    33& |+ p6 u/ k$ L; @( P# H
    344 k" o; U2 X+ u6 b) V& |
    35% R' t5 x* F4 g, k0 H) U# q) b+ @# p
    361 B8 G0 E5 _0 j' i& C
    37# s$ x" l0 M- }0 `4 J1 C) c
    如果要使用偏移 Timedelta 调整 bin 的开始,则以下两行是等效的:
    7 B8 I% i" K) q  nts.resample('17min', origin='start').sum()
    & ?4 |" b# E8 t: M! W- Z( pts.resample('17min', offset='23h30min').sum()
      L- K! ~0 w  k: _, @2000-10-01 23:30:00     9/ J: m0 Z% V; R1 s- Q
    2000-10-01 23:47:00    21
    : S3 ~! e" o7 g0 ^5 s2000-10-02 00:04:00    54
    - ]+ Q4 c) {1 K# u) |2000-10-02 00:21:00    24
    " q* D+ k$ j# TFreq: 17T, dtype: int643 I4 L+ |5 F5 C
    17 |) k1 E& I7 I# A) W. O& U9 v
    2
    $ y( M) c, N6 V0 L  E3! ]4 j; t/ z% c" i) a& G
    47 ]$ H+ |' W7 d
    5
    0 F5 k* }" @4 @7 r# D$ u% n" i6
    4 _, ?7 f. s1 Q" r& m. Y7
    / f! e& T" Y( j10.6 练习6 H- `: y/ s; g) K1 v3 z
    Ex1:太阳辐射数据集
    6 |4 r7 B7 Y; V  {9 {9 L: }: z2 w现有一份关于太阳辐射的数据集:* k) O. I! H+ ^! X

    / u4 K1 X3 G5 Q  j  vdf = pd.read_csv('../data/solar.csv', usecols=['Data','Time','Radiation','Temperature'])6 K2 o% }3 \9 [# d7 m3 f% A7 _
    df.head(3)8 G9 i* E4 B0 x  N- t

    0 {/ e. E5 f4 p" L# ?  j  ?Out[129]:
    , `9 I( X" x5 I. q9 Y- e3 E                    Data      Time  Radiation  Temperature& s& G& a: J/ C
    0  9/29/2016 12:00:00 AM  23:55:26       1.21           48
    ) Y. l: Y: A! X1  9/29/2016 12:00:00 AM  23:50:23       1.21           48. t# r: X  l+ {3 F
    2  9/29/2016 12:00:00 AM  23:45:26       1.23           48
    4 @1 k! R# ]6 {11 _3 k- X! s0 Q# D! b- w" X. C
    2
    4 R+ t) @" g* ^9 z7 x; v: W3
    : s1 i8 W) B5 }3 a4" K' A" |# L; G, ]1 T
    57 m: K$ Z4 R/ j/ W
    67 w' A1 g8 j2 W6 u
    72 v0 f- d" O) _' }! q1 a& A
    8$ l7 u' S- }3 J! Z  f3 C
    将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。
    0 S+ K$ z6 W2 t1 K  g每条记录时间的间隔显然并不一致,请解决如下问题:+ \8 L) y4 S% z6 w
    找出间隔时间的前三个最大值所对应的三组时间戳。
    6 t% v  T  l' f4 @- W: e. _/ X是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。) K7 U* r7 m4 d2 \; k- u" L8 Z
    求如下指标对应的Series:
    . U2 Y3 p% r/ l& w' B: C. @温度与辐射量的6小时滑动相关系数+ I1 J1 ^" U; f* T6 p* {
    以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列
    * ?+ T9 v: @; Y5 k4 A! E# q0 `每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)
    5 C+ J7 P& E; [9 simport numpy as np
    ( Z  b0 k2 `& z2 B3 _import pandas as pd
    1 M' w: k; ]& n7 t1
    # J! n* X% O& n8 l) U2$ w0 p( d! C* O# u2 U8 j
    将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。
    . p: m) E& b4 q6 c' ~6 M8 F6 Zdata=pd.to_datetime(df.Data) # 本身是object对象,要先转为时间序列
    7 W1 U2 v! L+ F/ I' W2 Ltimes=pd.to_timedelta(df.Time)
    , T9 {) |! [8 M% b; O2 `  v% Y. edf.Data=data+times- D2 T. J: ?% W/ d9 w
    del df['Time']. K+ X8 e+ x* J2 g, X" F
    df=df.set_index('Data').sort_index() # 如果写的是set_index(df.Data),那么Data作为索引之外,这个列还另外保留
    4 ~0 K8 ]) J& V) B5 D% Xdf
    ) X& d  o# k& |8 z                                        Radiation        Temperature- Z# M5 p; g: k$ |7 A5 B, f, l5 d
    Data               
    ( x+ V4 d5 m2 o! K4 f! V  Y" |2016-09-01 00:00:08                2.58                51
    " s* O! q7 N3 Y$ N/ h3 w- K9 x2016-09-01 00:05:10                2.83                51
    ) H( Y/ i3 c# M) R' M! N6 M/ s2 c! k2016-09-01 00:20:06                2.16                51) U8 R$ Z/ u$ d' j( Q& v
    2016-09-01 00:25:05                2.21                51! B$ A+ S; M9 |/ z9 r% S
    2016-09-01 00:30:09                2.25                51
    $ `; x& @% ~, x3 Q* l...        ...        ...
    9 Y7 ]: W1 p9 o: C2016-12-31 23:35:02                1.22                41
    - N% h" h6 X7 m* t' N, e1 a2016-12-31 23:40:01                1.21                41
    4 R7 O3 S7 t& v$ j2 s0 k/ z2016-12-31 23:45:04                1.21                42
    + e& ?+ C9 `& \( s8 f7 p2016-12-31 23:50:03                1.19                41# f) l! z; u0 e4 P) Q, J
    2016-12-31 23:55:01                1.21                41
    8 g, Z/ E8 O; C6 k% C$ E& G3 a7 c. R9 [" N  U
    13 d8 k- L* r6 |3 y- J
    2
    1 t0 e3 L+ I6 H0 u3
    & @! R0 ?8 C7 j0 m& w' C( d! q4& |( _, J  l6 n- v$ _7 d
    58 y  s, k5 s% ]8 `/ R+ [0 i' k! F8 j
    6
    3 T3 p' v: O$ j) Z# M7
    # c4 p* B: H# k- D( G% Z4 u' P* A8
    * q: @8 V' B0 p  [1 S# M# L9
    + h' s) e( b7 u8 D108 _# |( Q2 b: o
    11: B9 Z- C9 c" z8 L% e' d
    12
    7 x6 i7 S- ^" k13
    $ {) E5 s  v9 N- g! d0 V3 W14$ |5 ^" V: U& W+ b2 x
    153 i6 c  W7 p3 l; i0 |
    16* P8 z3 P, W3 a# [, D
    172 E6 _# N% j: V+ E7 e& j2 ^
    18: i* F6 G5 A- d+ E! ]9 M/ f+ o
    19
      G: k7 y/ [9 J- F每条记录时间的间隔显然并不一致,请解决如下问题:# ~" b% Y7 c; W* l) A: v; O1 x
    找出间隔时间的前三个最大值所对应的三组时间戳。4 Q) V: I/ z- I8 B
    # 第一次做错了,不是找三组时间戳: K2 ^+ v. x/ K( O1 f) p) p
    idxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]8 u& Q$ p) a* D1 G, ?/ }+ H
    df.reset_index().Data[idxmax3,idxmax3-1]
    % Y" S' L% T1 t5 L+ {9 G  i% R
    , u8 k$ j- |& {  ]25923   2016-12-08 11:10:42
    7 _$ m/ @3 k; V; m$ X; ^& }24522   2016-12-01 00:00:02
    5 h% y. d$ @3 \. C0 _& b7417    2016-10-01 00:00:19  d9 k8 `$ J* y" @* c8 Y: x5 X3 U  `
    Name: Data, dtype: datetime64[ns]
    ! J" K% @2 o( ?0 L# H  p& a1% J: Z9 ~9 J, _+ O: }( C0 q+ `
    2
    8 h! i5 k, g9 A! _8 \; U( {3% F4 c' [" b9 f4 T6 y" i" k
    43 M( y9 ^/ ~( t: P- h% g, v' B
    5& ~: S$ y$ E5 W) F, y! i
    6
    " I% b  w$ S" i2 @2 p3 t72 S8 Y* Q6 O! V9 X
    8+ i  O$ h: B, X" m0 Z
    idxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]
    7 g3 j" _8 X5 p  x9 ?list(zip(df.reset_index().Data[idxmax3],df.reset_index().Data[idxmax3-1]))
    2 w' B3 k4 P1 z" j  E0 R% J4 |+ n9 `, j" A' y. _: |
    [(Timestamp('2016-12-08 11:10:42'), Timestamp('2016-12-05 20:45:53')),
    ( P8 \) M& P, [2 H- _; c (Timestamp('2016-12-01 00:00:02'), Timestamp('2016-11-29 19:05:02')),
    0 j4 l4 L; D. w  w: t (Timestamp('2016-10-01 00:00:19'), Timestamp('2016-09-29 23:55:26'))]
    8 Q" T: ~. A4 `; `; V1
    9 N5 _+ o5 V  w, q+ _/ J2
    4 Q- x2 E+ I% C% B6 w% ?3
    1 K7 ~2 o! `8 L' W7 O4' m6 F& n* R. t& j3 }8 p& Q" s% H
    5  ^' d! W% |4 a6 J
    6
    5 Q9 T! F$ j9 V参考答案:
      i& L4 w7 b8 A) Z& @& J3 c- W: r3 ?# W- G! r# N
    s = df.index.to_series().reset_index(drop=True).diff().dt.total_seconds()# U' G% P3 j0 h& j5 N9 d, F
    max_3 = s.nlargest(3).index
    ! d! j, D0 H! z, i  c9 r+ ]' s/ \df.index[max_3.union(max_3-1)]
    1 [' U! I1 F, e8 z& I& L
    1 H' D8 X, L1 i. hOut[215]:
    + Q9 [0 [( F: o) B3 s" z* EDatetimeIndex(['2016-09-29 23:55:26', '2016-10-01 00:00:19',- t* H. ~4 Y! I" H# f- e5 A
                   '2016-11-29 19:05:02', '2016-12-01 00:00:02',
    ( s) J" K& ?# U# E( e( `               '2016-12-05 20:45:53', '2016-12-08 11:10:42'],
    0 ]- U+ L- @. P3 A0 a  w              dtype='datetime64[ns]', name='Datetime', freq=None), I" r% g3 u7 u- k
    1! @, w$ j4 y" ~+ q7 p$ `
    20 e+ e1 I. j3 U$ o! {
    3: a" B& L  n9 {
    4* W/ [. T# a* z% S
    5
    9 C+ x2 ?- r2 Y$ t6* y1 n# M( R  l+ X5 C) P" n% C
    7
    6 y6 c! ?  ?' r' K- |- b8- B9 x: h; x+ W0 K& l
    9- p3 M  |5 y) \$ k5 {& n2 j
    是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。9 v1 w  c8 @; i& c2 E
    # 将df的indexydiff做差,转为秒数后排序。再求几个分位数确定取值区间- a# k) d! f" s* L
    s=pd.Series(df.index).diff(1).dt.total_seconds().sort_values(ascending=False)1 `  j! V5 \# K8 o" j7 }
    s.quantile(0.9),s.quantile(0.95),s.quantile(0.99),s.quantile(0.01),s.quantile(0.03),s.quantile(0.05)# f" t4 b8 i$ T& _$ Q

    1 S! _" u& l, j1 b( y3 c/ N) d7 z. u(304.0, 309.0, 337.15999999999985, 285.0, 290.0, 292.0): o# S: C" Q% b+ a7 ~, f) m. z0 d
    1
    % |, M( A8 E, e. t8 J9 k( c7 i: L2- H4 e$ m: |. H1 ~/ ~
    3
    6 z; Z4 Y; r' E8 a7 b7 r4
    * B. G7 R" E$ i0 r9 V. V5
    " \+ x' ^) l* o% O4 @) t%pylab inline
    ) E+ V* t, y. I: L5 A_ = plt.hist(ss[(s.values<337)&(s.values>285)],bins=50): h4 ?2 d6 Z4 A# V6 ]
    plt.xlabel(' Timedelta')
    . f9 F* ^  s) n* B1 T7 Z% }. h* B3 `plt.title(" Timedelta of solar")
    . Q2 Y( ]7 d  \" f3 y4 }. K& y; x& i' o+ {1% q3 W( {6 f3 D) }/ x
    2
    1 q( D# [3 g4 v+ D3
    & T* _' v9 t  l) k6 e! E; [+ r4
    / f4 E" q" X. ^9 c
    ! `0 h6 m" _$ T! ?! N, O; `+ ?) h! ]* q, i, F3 \% N
    求如下指标对应的Series:- n1 t; F/ C1 o/ ]
    温度与辐射量的6小时滑动相关系数
    ! s/ K! ]- y; K- P& Y以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列1 F& F+ s; e; Z# m
    每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)% c+ r/ V: z* y6 v3 J8 I' y8 S
    df.Radiation.rolling('6H').corr(df.Temperature).tail()/ o8 R; |. Z9 w

    . ~1 i* j# m8 X5 ZData, R  X6 ~/ }: B7 `
    2016-12-31 23:35:02    0.4161872 G' G; v6 l1 r$ C* W  G
    2016-12-31 23:40:01    0.416565
    ! M4 I% f; d  U8 _' R2016-12-31 23:45:04    0.328574" e: `/ y/ C( u+ \" x- n  r+ e
    2016-12-31 23:50:03    0.261883" Y& C/ b1 n% V- Z+ u
    2016-12-31 23:55:01    0.262406
    # V7 O6 [' }6 i. |6 zdtype: float64
    ( f7 A* a$ O9 s4 y: _8 ]" a" H. y1! C5 {) M$ ?* k3 t' @
    2
    + ?) v. \, U( O& j7 [" J. g3
    9 X* u9 `0 X+ m6 w. U0 }5 c0 i4
    5 v# p4 P! K1 K+ a& X2 u5
    * J/ o- d/ v; d$ H6
    $ H( o6 A. [; t* ^# V3 w: q7
    - S5 I* A9 G: I4 U+ H$ V8; l1 R0 T6 e  w) c
    9/ v' m% v2 J/ q( ?+ c! V  T! _: l( Z
    df['Temperature'].resample('6H',offset='3H').mean().head()
    $ g2 M3 S+ c5 p- u" f% u: K6 I' |; x$ Q
    . g" |7 z1 s4 s' w- xData
    ; w: `0 M- v& y7 S( q0 k5 k4 Z2016-08-31 21:00:00    51.2187500 w  b3 I4 x: L8 g. y- ~: N- m/ M
    2016-09-01 03:00:00    50.0333330 w: M' v3 U7 \1 ?
    2016-09-01 09:00:00    59.379310( U" L( w6 C# g* J, e
    2016-09-01 15:00:00    57.984375+ u" [, _3 q0 R& E4 y
    2016-09-01 21:00:00    51.393939$ A$ e/ o) S+ F) _0 t+ X# y
    Freq: 6H, Name: Temperature, dtype: float640 w5 P. }9 ~- Z( J
    1
    5 ]" v# K* K* v- n' @# w) t2' Z. s& n& w9 ]# y! ]3 [0 F. g
    3
    % m0 o! K7 N0 [) v: t: E) G4
    * _7 N& Y6 W. i9 b( {5
    / t" r2 L0 \$ x1 F. v6. u3 ?! k) _3 S# m
    7
    8 a; p- Y" c% @! h5 k0 R* [, A8 y8
    ) O, X! Q6 l- r1 m1 W9  e. L8 s4 g( }, |' A% R0 u. g
    最后一题参考答案:9 ?# ]* b0 E, t: o! S: i
    ) F  F; R3 Z1 Q9 O  i7 N$ r) m( y$ z* j
    # 非常慢
    & e- D% c& @; }my_dt = df.index.shift(freq='-6H')( U* C+ J% O0 b+ F2 y! R3 A
    int_loc = [df.index.get_indexer([i], method='nearest') for i in my_dt]3 G3 H: T8 d8 B0 A8 k
    int_loc = np.array(int_loc).reshape(-1); @' L0 {1 p  T3 w
    res = df.Radiation.iloc[int_loc]
      M2 O& G9 _, N& t5 K, Fres.index = df.index' l9 I5 T- u8 o) V2 o& p1 o
    res.tail(3)
    4 e! ~2 M. a0 e$ [0 |, ~18 N  Z& p6 G- |% I! v, K
    24 X. I% X3 v- @" K4 ^; v  `7 V
    3
    $ @  N3 d6 n  \2 c# A4! m; {. z* n* e4 o2 |9 E6 Q3 a
    5
    ; e5 w5 \. {  u) v0 L6
      h) W7 W" M7 c' F7
    : j- N% E8 |7 S9 g1 X# 纸质版上介绍了merge_asof,性能差距可以达到3-4个数量级
    ) r' Z, W/ R# p) G% h) ktarget = pd.DataFrame(# b, x3 K: A, {/ j, y
        {6 Z5 S. U0 n% S8 d
            "Time": df.index.shift(freq='-6H'),
    . V8 [9 {( t- z! a        "Datetime": df.index,
    / t7 q3 T! x- |& G    }# n3 t) Z# R  \$ S- [* Q
    )
    ! N* z+ V4 G: x0 N$ d, P% U' e" {
    res = pd.merge_asof($ \( O7 E/ n7 C' r$ S' T
        target,
    0 e; C9 u$ p9 n# q: e" P    df.reset_index().rename(columns={"Datetime": "Time"}),
    " k" D) z" j- x/ ^9 B$ j    left_on="Time",
    ) I" _1 z/ g0 ?9 p8 r1 N+ u    right_on="Time",- M9 x8 f. V& Q5 n) f: v
        direction="nearest"
    $ q5 B& p  M1 o4 @5 u4 H0 F" v+ ~).set_index("Datetime").Radiation  T' P( r6 a+ {2 t' w) M# t
    - b9 p- F4 v7 |/ K
    res.tail(3)
    $ O+ E: s! a- oOut[224]:
    - @5 p# q; I8 N* cDatetime
    1 N; j3 q6 A. X/ j. k& T2016-12-31 23:45:04    9.33
    * j# w3 F7 v* D% N! y3 {+ T2016-12-31 23:50:03    8.494 @9 O: l% O7 w8 {, Q# e
    2016-12-31 23:55:01    5.84, a- e- H2 g4 A8 K6 L' Y2 I
    Name: Radiation, dtype: float64: n5 E) {' a6 S$ D# u

    5 y1 z3 m" K# n' H0 y" ^2 }18 d8 V* T" R$ T5 b
    2
    & s" O- U5 s1 Q; _6 Q! Z0 K, s35 `' u, ], s4 W$ T$ K3 n; U) Z4 x1 C
    4
    4 W: L; r- Z  }5: F. g$ _5 l! g+ T
    6# ^- z; m( f, @" J0 J1 E, z
    7  {+ y/ U/ h1 p* P7 [5 G9 k: l* ^; l
    8
    # \, N2 Q! p( x98 M5 D+ N, M, K: R
    10
    & S- L7 g) x% D- J. N( D% }11- Q+ u9 l  \+ O3 o$ a" |2 k5 f# g$ M
    12
    ) H8 _3 T5 o6 Y6 m* w$ ]! T, Z13
    7 X- R) p* o5 C& b14
    ! w/ U0 H/ {+ ~6 [" k( c15
    ) f9 C6 Z. t8 O% R" S3 H16) M$ |* y; s1 P& m' l
    170 S3 o! J( L* \! @" F: h' z' k
    18
    * A. Z) p- _9 h19
    - P" Q2 w9 o7 p* @  j  z7 m" W# Q20
    ! O2 T1 L7 `  C# u: e1 V1 M3 d' ~21
    , ~4 ?2 F1 W) i. k1 N$ A  q" t22
    . g" [( c: J4 F; M- ~23
    2 `6 w% t# J( w. O) pEx2:水果销量数据集
    - @- y+ I2 c0 j2 Z" B现有一份2019年每日水果销量记录表:, L0 a5 ?( R: b" k. U

    $ i$ O8 ^' u) }3 \$ Mdf = pd.read_csv('../data/fruit.csv')+ v* b4 D8 N4 _
    df.head(3)5 g8 R3 P6 z2 |. g, S) Q& a: y) G
    $ R5 [% x& o: t6 Q* u2 z' Q) \% I
    Out[131]: 1 X- V% f2 X# @
             Date  Fruit  Sale7 u3 Q5 U' O5 k1 {  D
    0  2019-04-18  Peach    15
    - G5 a& y. r7 t% S1 I& B/ I1 w1  2019-12-29  Peach    15' D; s3 Q2 k6 M1 \4 e1 Z
    2  2019-06-05  Peach    194 c8 A  k6 s& f: K# z
    1$ e2 ^: ~9 n4 \
    20 Z) ?# ?& P; I) S3 p) z7 q1 a
    30 ?* D3 r6 R0 E. s! K# R% k
    4' i0 Y1 `3 z% V% j0 [# h+ `( d" U
    58 G+ i# q# u1 q; R" j: d# X
    6% t4 z( Z' `* `% C$ x' p% [
    7
    ( J$ }$ }) w' ^' {( L5 E; t8+ V' u1 O( l  g2 F( |& u
    统计如下指标:- E" p; i3 \5 x* t' I( k, w
    每月上半月(15号及之前)与下半月葡萄销量的比值
    5 o7 j# n9 G7 n6 I! M( T每月最后一天的生梨销量总和
    : j. {  ^& H: O" r8 u- p每月最后一天工作日的生梨销量总和
    % `9 n5 p4 h4 D$ S: a4 Q每月最后五天的苹果销量均值
    ! w& a$ {: S) G" F5 A7 `0 \按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。& m+ X: f* _% T9 f- n
    按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。
    : N: q9 l2 j( \) w4 limport numpy as np3 G- g2 o: `- c- d' h
    import pandas as pd
    1 U6 ]" R+ D9 `/ f4 g/ _. i7 Q17 h7 O4 D; \$ p( i2 e0 X8 h4 x9 ^
    2
    % [6 y  z2 c+ b7 G! V. S. n" N& y统计如下指标:
    ) n) g( @3 t2 ?! t每月上半月(15号及之前)与下半月葡萄销量的比值
    , ?1 p! S. p" j9 o! d2 B+ A) u6 G  m每月最后一天的生梨销量总和
    ( Z1 Y  v4 y3 Q6 `% D6 e! o每月最后一天工作日的生梨销量总和
    ( E, ?* A3 k+ _9 M9 |每月最后五天的苹果销量均值
    0 E. v6 t# V7 j: }, R7 b" B# 每月上半月(15号及之前)与下半月葡萄销量的比值
    0 b- W$ a! _3 R+ x, A! ?df.Date=pd.to_datetime(df.Date)! B+ w  e2 f( X7 U9 ~0 K( W
    sale=df.query('Fruit == "Grape"').groupby([df.Date.dt.month,df.Date.dt.day<=15])['Sale'].sum()
    " s. S' ?$ y+ W% ?- e4 Fsale.columns=['Month','15Dayes','Sale'] # 为啥这么改没用啊8 [/ y1 h/ s# E3 l3 }2 f$ T( ]
    sale=pd.DataFrame(sale)/ f% `7 Z9 L$ |& E) E% J. V( F
    sale=sale.unstack(1).rename_axis(index={'Date':'Month'},
    " H' j- p4 q1 s# y                 columns={'Date':'15Days'}).stack(1).reset_index() # unstack主要是两个索引都是Date无法直接重命名
    2 S7 ?  C+ @1 hsale.head() # 每个月上下半月的销量& W2 n4 a9 O& L4 v# N+ f( F3 v

    8 ^  W/ r+ V! ?  F' O9 q" a6 y  Month        15Days        Sale
    7 Z6 @: d( C9 ?0 \4 t% q) c! y0        1        False        105036 U9 ^. Q0 i2 f' p0 X
    1        1        True        12341
    ' Z& V# X- Q  o4 t2        2        False        10001
    * k  {) c3 P, e. l" Y  f6 s& g3        2        True        10106
    3 h  m8 u0 X5 r& g) }( p4        3        False        12814
    7 A; S- ~  ]. H/ o  C, k- _5 O9 |) n2 H4 k- U) A4 J
    # 使用自定义聚合函数,分组后每组就上半月和下半月两个值,根据索引位置判断求比值时的分子分母顺序
    % O4 K8 x. e1 V; V6 L/ |sale.groupby(sale['Month'])['Sale'].agg(
    + [* M0 D& u- g2 J8 c" J                lambda x: x.max()/x.min() if x.idxmax()>x.idxmin()  else x.min()/x.max())
    9 q( H! h) _5 r
    4 m5 q" @; `" t6 A) RMonth/ A, M( B- m$ z( ?; U* C) h
    1     1.174998
    1 j) ]) F9 I& t. G) z2     1.010499
    6 E! U, q+ ^: q* U; G3 u3     0.7763381 J! M2 s! @+ q4 Z2 u  o$ i# ~
    4     1.026345
    # H( F. P0 w. _5     0.900534
    ( P$ B, }, e$ t# o/ x6     0.980136
    6 ^2 l9 j2 C( Z7     1.3509607 L9 e% \( G8 p! w
    8     1.091584
    5 c5 V  K! y) u9     1.1165081 D9 w9 F* J, Q  h# Q  R
    10    1.0207844 y" Q0 Y8 I" h8 \6 Q/ v8 T: Q
    11    1.2759112 q, {" r% c, Q
    12    0.989662; w8 X! V1 p+ d# @. j. n3 W/ n) \- q
    Name: Sale, dtype: float64/ ^+ X4 @  d8 k6 \+ d( k
    6 w, F! z1 M6 p: U
    11 B2 {: m* `- G1 _5 A
    2; L3 {+ E! {1 \- i( X" L
    3
    ' U& a; B* E  F& X5 Y4/ m; s% a, H" _% ^6 I
    5
      d1 D" E2 L& u3 R' q! h65 P& r. _& t: T. V/ D0 l
    7# t# b1 a  M! k9 L7 M& }9 {
    8
      ~+ m- Q8 Z; q( x9 c9
    : M  H9 d! b7 t- w0 z0 A3 r$ W10
    3 T  z+ z6 S1 I; d1 a7 @5 E: a' k11
    . c5 t" ]2 |/ H12
    0 z$ W* g, q7 r% x13
    2 d/ ]$ f+ S4 z/ N% d9 ]. c14
    0 r* h! C& `3 p- h" U) O- |15- O: s4 P( [' d" g  \
    16
    8 I9 }4 k' O& Y0 k% d17- C- C3 o; u; q- N# O, q- ~! g  @+ W
    18
    5 U  e. m: i% d1 t% Q8 ]0 Y/ s% R19
    8 s* }  d. h/ Y# U7 b209 W  w6 O0 ^( p. E0 W# A
    21
    " J% k" d& T2 l5 D22
    / C* K: N: A. I& t/ [; J* h2 ?23$ }6 }: ]4 {6 C
    24& I/ V5 N9 O' g, X' |
    25
    ) ]! I. e! v5 L5 c26
    2 O8 J! Q' T0 @, l5 X" m3 m27
    % z' `! I' u3 H- j% X4 v, Q1 T287 C5 E$ l7 N5 B
    294 {, g" R1 ~- `! p, a
    30; }) Y4 E4 g- p
    31
    0 a! _6 \- {' Z# f3 @9 }# C321 j2 o$ M: Q6 i4 B( k' e
    332 N- I( c6 X2 P' b' _
    34
    - k* {# j+ R: J( h# 每月最后一天的生梨销量总和
    , ^  i' X& J7 i% u5 c5 N5 p* tdf[df.Date.dt.is_month_end].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()
      b. I1 _+ u  z0 N
    ) p# R3 O* K' y% u+ Z0 W7 fDate- _, p  a. |. S, u0 |- I
    2019-01-31    847: l3 P1 _; Z! w9 @8 T% n$ P6 r6 E
    2019-02-28    774
    : a- }1 R" `) G' Z) m1 J) r2019-03-31    761
    & R( x: M0 |/ E7 V1 a2019-04-30    6484 D6 u4 d0 U$ _
    2019-05-31    616  G. W* [7 y. ~" L/ p) `5 T" x
    1" D. j- O2 A/ e) G0 a% U
    2
    ) u" v; e) `: _3
    " X. r1 w/ `. w5 S! ~+ t0 w4 `9 i4 ~# _4, |  |$ l9 z" N' B8 P
    57 x" V9 M: n9 Y: S/ r0 y
    6
    . m$ H, {) X9 r) s6 Q0 ~7! Z% G: E. |# g" f) F
    8
    ( F7 a/ U- K" X) ]9+ ~$ e% O! c( y% O  S: F; G0 M
    # 每月最后一天工作日的生梨销量总和
    ! F# L' [/ ~6 c7 a, f2 Tls=df.Date+pd.offsets.BMonthEnd()$ M) z2 O' S1 s& g, q) E
    my_filter=pd.to_datetime(ls.unique())2 i: o% V$ r7 }. G
    df[df.Date.isin(my_filter)].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()  [- z8 m* w$ o5 W2 c2 L" s
    * F: }8 z% e- O" g* J" u1 _1 S' |, C
    Date
    4 L* I& q1 x5 o, ]8 w( I; j! Y2019-01-31     847$ n+ q$ d# i) y- k& S
    2019-02-28     774
    / u, }: Y& U& [& ^  t2019-03-29     5100 j6 N5 o: {1 B" K6 p
    2019-04-30     6483 o) s- f: R0 L1 x
    2019-05-31     616
    / z& \2 F# f1 O* L, u1. l, A6 V* Z7 R0 h
    2
    ! w# q- B8 {  b' p3' F7 R# B. Q5 i$ \' _0 N& Z9 j
    4" E: g  ?4 N8 m2 _- r
    5
    / w- p8 ~6 \4 q: _: H6
    4 b% t$ U" U* p% t& `2 B; s7
    . B' v( x) O. g! S( U  C! c8
    * k% b' _# f4 S9: p! Q5 {+ ^  {. j# R) S- F0 ^2 \
    10
    + j& z/ F$ o1 [# E& |11' l6 c" i, y8 P! K" M4 W6 e
    # 每月最后五天的苹果销量均值
      r2 F- @- \9 b/ x9 gstart, end = '2019-01-01', '2019-12-31'- `  {, c9 D5 ~) H  ~
    end = pd.date_range(start, end, freq='M')
    ! @% J7 N8 \% ]end=end.repeat(5) # 每月最后一天的日期列表,重复5次方便做差7 i$ M' ], S) ?: I" j

      O  A: M+ E, b6 U2 x0 ltd= pd.Series(pd.timedelta_range(start='0 days', periods=5),)6 u+ Y" C" u8 L, n' b
    td=pd.concat([td]*12) # 日期偏置,最后一天减去0-4天! p9 q8 f% b, V8 d, \
    end5=(end-td).reset_index(drop=True) # 每个月最后5天的列表$ Y9 ?: P4 c9 p

    6 j$ q. I4 k3 }$ p+ vapple5=df[df.Date.isin(end5)].query("Fruit == 'Apple'") # 每月最后五天苹果销量. ]  T8 H: v- U# o/ C
    apple5.groupby(apple5.Date.dt.month)['Sale'].mean().head()
    $ N+ e5 {& O  c5 d# o* z& T3 c  W
    Date
    - D1 i: y9 x5 C; L, c4 q2 m' f1     65.313725
    2 ]/ c- H* w8 l/ ~& \2     54.061538: T& d7 d* B' y! V) _) }4 f1 }; M& w
    3     59.325581
    # {  S* I; o3 ^9 m9 _/ C) \4     65.795455
    $ _8 u$ R: B  J% J5     57.4651161 @* ?3 w9 W' e' a
    1 N" f6 f( K0 O
    1
    * b% B2 T9 S7 H# x# J* Q* J* E2
    0 p' X2 C5 h( N7 d9 X3
    ( h. @5 k$ m0 ^% o; R7 }2 _, [4
    $ I, g. ^. a6 N! T- X1 P: e5
    - u9 V6 S5 j; C; @1 Z6
    * x- v% o+ D7 b6 ~# T" V71 ~8 h) `) y: s4 {$ Z; X* k
    8" ^& U2 Q$ F8 ]1 b+ @) s3 T- B$ u
    9
    9 x+ A; l. X! N) {10
    / O" ]$ R* N+ }11/ C1 s. h$ F7 p$ j% a; g: \6 s1 e
    12( h, @( Y6 v# U' B
    132 K) T- s! H" g* F" N! t
    14
    ) d" C8 Q: W  q' V% m0 E7 e157 @# b$ n7 h# p
    16/ n1 `! z# e2 h9 E3 q; l
    17
    $ h% `" S4 }0 \6 k2 q; i" N6 b& U18
    0 f5 `8 C- t3 ]( ^9 x( H& P# 参考答案:+ k5 h7 g* ?5 R% w
    target_dt = df.drop_duplicates().groupby(df.Date.drop_duplicates(8 a3 K/ G5 w# X  \0 \+ u
                ).dt.month)['Date'].nlargest(5).reset_index(drop=True)3 `, h8 o! c& S

    ' D" A3 _8 T4 l9 eres = df.set_index('Date').loc[target_dt].reset_index(' s/ x2 z2 w4 ~8 R" v: z; h2 [
                ).query("Fruit == 'Apple'")5 J, R# y+ ~5 J7 q6 g3 U# C

    ! u! E0 A6 O: B( J2 x  h6 A! Kres = res.groupby(res.Date.dt.month)['Sale'].mean(: l6 f4 f5 y  E% R' b
                ).rename_axis('Month')
    % W4 u5 N6 Q: R; B! u- O; m  h& m2 @- S1 g. ^
    ) Q1 N4 I2 J% Z; }1 p" [- B% A
    res.head()% f& |' O. K+ W0 f% Z
    Out[236]: 5 K. Z( w7 Y1 h  C( z* k
    Month
    9 g$ a2 V2 x0 k) a2 p% @2 R$ J- P1    65.313725
    ' _6 w% w) F* f* v2 G0 a# m( Q! L8 i2    54.0615389 L2 M) a' y* `
    3    59.325581
    ' p+ K0 q4 Y  @( S6 L& \4 k$ F+ `4    65.7954555 Q; M3 L% i9 C
    5    57.465116* ~; n- i! o, D9 P+ H" i. m
    Name: Sale, dtype: float64
    / Y3 W2 O( g+ H# C: ^2 F
    & j9 \0 ?( I, w8 }) x; H1+ c9 o5 z6 M6 _$ j: q
    2
    & a& r1 b, K$ p  f8 [% W35 V: a! n) q. U: n1 L6 S* s4 k5 P
    4
    + b# _- D+ x6 W8 z5# z3 z" X1 r: K7 m
    6
    2 R' g8 ?9 p$ A. I/ N: K5 b7' I3 O! u$ z% j: ?
    83 V/ y5 i, X  [
    97 n3 P' w! S- ~5 A/ ^7 l
    10, \9 F5 S/ ^% `5 B2 {
    11+ @9 O* ]) ?5 S/ z# ~
    12
    7 ?9 c3 k1 F# G+ {+ q0 x2 I0 ^13
    ) M& j' C+ v7 _. q147 t3 I$ c% x/ a
    15
    1 z9 h1 ]; w* h! y3 u  `; f! Q/ |1 U16  J0 Y; V) m, g6 a. l: G9 ^  W% a
    179 ~' L; z& Z. B  B- }6 O
    18! {& v$ B) ]2 x0 Y% R: ~; U9 ~
    19
    & J8 T6 X! H7 _) ]# ^& G203 S8 v" e. T' A# B) L3 c2 X
    按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。
    . X8 l0 f  T' q% z8 Q: ]; p) U4 mresult=pd.DataFrame(df.groupby([df.Date.dt.month,df.Date.
    " A1 ^9 W+ Y7 j' |                                        dt.dayofweek,df.Fruit])['Sale'].count()) # 分组统计 ; u0 q4 [* S$ v2 i: x
                                            6 b. i. J2 v7 c2 N# g- l6 B) @
    result=result.unstack(1).rename_axis(index={'Date':'Month'},6 e, E" `7 ~. @+ M7 b
                     columns={'Date':'Week'})  # 两个index名字都是Date,只能转一个到列,分开来改名字.
    8 c+ ~, M- A# i3 x( ?9 c8 a' }result=result.swaplevel(0,1,axis=0).droplevel(0,axis=1). w/ w  b7 e. G- P7 R
    result.head() # 索引名有空再改吧% G' e# w/ c6 y4 Z, V% ?
    + V/ U, s2 i  D
              Week        0        1        2        3        4        5        6
    " Y5 h  Q5 S& R* PFruit Month                                                        6 A3 g. ~* o# Y" k
    Apple        1        46        50        50        45        32        42        232 u' m' I  J# Y7 [
    Banana        1        27        29        24        42        36        24        35) J9 I; y6 v: N8 M9 p% q3 |; s
    Grape        1        42        75        53        63        36        57        462 ?6 r  E5 w! Z/ @
    Peach        1        67        78        73        88        59        49        720 k+ E+ s! E3 Z' Y0 J2 s
    Pear        1        39        69        51        54        48        36        40
    ' z# ^- C9 ?- V- A# M2 }9 U% b1
    2 [+ _( ~5 `# o( I* b! L2
    / j$ D* s4 O" I. @3/ E0 j4 M  L& N2 y" D* u2 p
    4# O7 |1 q, i4 d! ?. c6 ^; n3 ]
    5
    * H9 k, [( {. ~  i5 X6
    8 e5 o" H4 D- h1 u: Z; p8 f. r70 C  a$ n. B# y
    85 |+ D3 B8 L5 X7 `
    9% l4 f: s/ X. S7 t% n: C8 {
    10
    ; W3 _, d, d. h! H) t114 w) }- u  U: N8 W" Y, z
    12( l. |8 x3 _9 h2 T: x
    13
    . N* v# t0 m) d: D1 ^14
    0 w( X) W7 H: n: E, W" I15
    3 o+ n* j# X# t; D0 b! X按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。+ r9 x7 W4 a5 T3 R2 H9 F2 Q* u
    # 工作日苹果销量按日期排序/ U, h3 r5 p7 m  S$ l( ^! ~
    select_bday=df[~df.Date.dt.dayofweek.isin([5,6])].query('Fruit=="Apple"').set_index('Date').sort_index()
    & d; R- ~4 U" c% N9 iselect_bday=select_bday.groupby(select_bday.index)['Sale'].sum() # 每天的销量汇总( q6 q2 b5 t2 g
    select_bday.head()
    0 n/ t) d7 W5 _. t+ l' v0 k* S" q9 Q( V+ I
    Date
    4 l! j9 \7 I! x4 x" u& v2019-01-01    189
    * X) y, w5 @* ]' `- {( a! H4 |: d2019-01-02    482, U0 e/ a! K& z4 s1 \& y2 ]
    2019-01-03    890
    , v' s- ?& F: X2 C; \: b1 p: l2019-01-04    550
    3 B" m7 X  u  w2019-01-07    494. E6 w% t" X" U/ T6 e

    ; W' s7 q  z7 p0 I# 此时已经是工作日,正常滑窗。结果重设索引,对周末进行向后填充。
    4 @, R% Y: Z% Y$ h2 T+ yselect_bday.rolling('10D').mean().reindex(df.Date.unique()).sort_index().ffill().head()2 i) s: K" l$ u* ^9 d0 J. n# P
    3 V$ @) A9 k# s" T  v' M
    Date
    * D& A5 @( v- a+ {! P. t2019-01-01    189.000000
    % H: a" P- O) Q: W2019-01-02    335.500000, \+ Q( U, ~" t4 e* `" C
    2019-01-03    520.333333; I3 A, G9 N3 t0 d$ Y. D
    2019-01-04    527.7500009 `# T0 G3 H: h) X# @" o9 s
    2019-01-05    527.750000  T7 e" @7 _5 o" l) @( v
    8 J# {' t/ P) x
    ————————————————
    ; @% j, ^; I1 e. B3 ?- p2 M3 }$ \版权声明:本文为CSDN博主「神洛华」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    ! S6 q- f' F3 U) V8 u原文链接:https://blog.csdn.net/qq_56591814/article/details/1266339133 a9 M. }8 x9 \$ w; s# E3 r

    6 g: P% K0 S  l: h
    & ~0 N! e' h' \6 t2 @) b/ E
    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, 2025-8-7 03:46 , Processed in 0.714863 second(s), 50 queries .

    回顶部