QQ登录

只需要一步,快速开始

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

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

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

5273

主题

82

听众

17万

积分

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

    [LV.4]偶尔看看III

    网络挑战赛参赛者

    网络挑战赛参赛者

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

    群组2018美赛大象算法课程

    群组2018美赛护航培训课程

    群组2019年 数学中国站长建

    群组2019年数据分析师课程

    群组2018年大象老师国赛优

    跳转到指定楼层
    1#
    发表于 2022-9-5 16:11 |只看该作者 |正序浏览
    |招呼Ta 关注Ta
    6 t+ u5 A3 ?0 j# |

    ' N  W7 d) B) Y7 E
    6 e% K( g  v9 R+ O3 \+ @6 u文章目录
    ( \4 r# Z" _+ `4 v9 \第八章 文本数据, M% [7 B. E' q/ _; K: Y
    8.1 str对象
    $ ~" r4 X% R4 `/ G, V( G' H8.1.1 str对象的设计意图
    * V' X" C" j, d" R# K* S8.1.3 string类型
    8 T. G4 [# O+ i$ M% e& h8.2 正则表达式基础8 r( e3 \- s: g" ?* P+ R- o
    8.2.1 . 一般字符的匹配% t+ t  L2 p+ y" O1 L
    8.2.2 元字符基础+ k" d# S" Q% A
    8.2.3 简写字符集1 D; v/ F# D* f' i: o5 w
    8.3 文本处理的五类操作
    ' i7 c+ C" l1 G% h5 C: s# i8.3.1 `str.split `拆分
    - G  O0 A4 O8 }% b  \2 K8.3.2 `str.join` 或 `str.cat `合并
      L0 E5 b3 t! y' ~" B/ K  `. S8.3.3 匹配: x, g7 g5 I3 X  X0 V  ^& ~2 h
    8.3.5 提取
    9 J9 @+ v3 B. q8.4、常用字符串函数! e4 K% i2 I# ^1 L3 F3 @
    8.4.1 字母型函数
    " m. H' B3 ?( E  h# H2 U8.4.2 数值型函数$ X& W( B$ Q6 P# I* R6 d$ ?' R8 e
    8.4.3 统计型函数8 ~  B3 M: }; d! z
    8.4.4 格式型函数
    ! i/ k3 l5 q! r3 z1 d8.5 练习
    ; u" D9 Q3 f/ c; K8 _Ex1:房屋信息数据集9 g& c. m/ y9 |! \% j1 ~3 Z7 |
    Ex2:《权力的游戏》剧本数据集/ @1 y/ [& `5 J
    第九章 分类数据
    2 H3 @! Q0 @7 ^* p. x: H9 J1 f+ ~# ]9.1 cat对象, X! d1 S8 Z) z% @6 u2 K
    9.1.1 cat对象的属性6 U' D  U9 c1 T8 L2 B2 M: S
    9.1.2 类别的增加、删除和修改: Z6 s! Z* d8 B: `, m( o+ S* B
    9.2 有序分类
    ' m7 M* @+ m! p9.2.1 序的建立7 g0 |; U! r" Z# p
    9.2.2 排序和比较. K5 J; V" F  L; Q& o6 j1 [
    9.3 区间类别! F8 W2 S- w8 n$ z1 ^1 y- ~: x
    9.3.1 利用cut和qcut进行区间构造" x1 I) p" R; R+ U8 X5 Z
    9.3.2 一般区间的构造
    ; }- Z0 ~( p+ t: P% A9.3.3 区间的属性与方法
    $ ?: x; p! W9 M0 m1 X9.4 练习
    8 S5 c8 b3 A! w1 pEx1: 统计未出现的类别
    6 S2 X. c- F) [# h% x9 YEx2: 钻石数据集* U, g6 i5 \' i8 m% T0 c
    第十章 时序数据  s2 d: v' w! }5 J  C
    10.1 时序中的基本对象
    + [2 d& {* u; I1 p10.2 时间戳. G+ e! S5 R" H# i# ^) l
    10.2.1 Timestamp的构造与属性
    5 Y. R7 O6 Y. k$ s; H9 @6 N10.2.2 Datetime序列的生成
    - l  d. T( ]( S10.2.3 dt对象
    & \, V3 ^; m7 g7 Q3 M, w10.2.4 时间戳的切片与索引8 ?: ^8 K4 f7 T3 `+ a( h: ^
    10.3 时间差
    . Y% b, o5 [6 o# R9 u10.3.1 Timedelta的生成
    6 E/ \; d7 k( b10.2.2 Timedelta的运算
    . C) x( d/ u2 ^4 C10.4 日期偏置( e0 }% ]8 W3 M. y. Y
    10.4.1 Offset对象
    , t2 y3 U- X. I9 h- i: c10.4.2 偏置字符串
    % W; b; ~6 [9 q+ |10.5、时序中的滑窗与分组
    ( \- Y" O- @1 S6 Q, M2 H10.5.1 滑动窗口# f$ L' \8 j) j) s
    10.5.2 重采样
    7 q6 ^8 \& y6 R& ^10.6 练习$ R- ?( U, j! {1 a
    Ex1:太阳辐射数据集
    : x; |/ E  h1 b# k) ^Ex2:水果销量数据集
    1 f. f0 b& Q- u8 e  课程资料《pandas数据处理与分析》、github地址、讲解视频、习题参考答案 、pandas官网6 m# E& V/ d% u& ?  e/ _
    传送门:
    ; K( I4 L( E  F) ~9 i5 ?! T5 ~+ _/ J2 p1 j9 L+ Z
    datawhale8月组队学习《pandas数据处理与分析》(上)(基础、索引、分组)/ n# D+ C+ s+ }
    datawhale8月组队学习《pandas数据处理与分析》(中)(变形、连接、缺失数据)1 A  d# D; y( G* P7 r' ~
    第八章 文本数据
    5 l/ r6 a; A& J& f, O* @8.1 str对象
    8 ^0 u6 J& \( G4 \% ^: j8.1.1 str对象的设计意图5 {9 T5 X$ M2 x
      str 对象是定义在 Index 或 Series上的属性,专门用于处理每个元素的文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其 str 对象。在Python标准库中也有 str 模块,为了使用上的便利,在 pandas 的50个 str 对象方法中,有31个是和标准库中的 str 模块方法同名且功能一致,例如字母转为大写的操作:
    / K2 L, [$ L4 L) X6 m7 w5 U- q  {9 E3 h( f7 g
    var = 'abcd', Y# j& k# S3 t; S& D
    str.upper(var) # Python内置str模块1 H; W& G3 H8 u3 p
    Out[4]: 'ABCD'1 W7 J8 C, G: N3 @5 I( J

    3 Y; `3 R6 ~5 n  c% O% t0 z( i& ?s = pd.Series(['abcd', 'efg', 'hi'])
    ' H& f' h4 B0 S# s9 }
    6 O, R) C# Z+ R3 Bs.str
    , K$ o1 G5 I# _, o3 cOut[6]: <pandas.core.strings.accessor.StringMethods at 0x2b796892d60>
    + B1 G: g1 E) T
    . e0 W6 i! z: Y. Zs.str.upper() # pandas中str对象上的upper方法; Z( g- p/ D* ]) q, r& c
    Out[7]:
    " B4 o& f4 @5 [9 x5 \) R* ^$ f0    ABCD2 j! ~! R. k/ N5 _0 B
    1     EFG
      K) W4 z) ~. a' o: W7 n  n2      HI
    % E, l4 c0 a3 g8 c* W/ ]dtype: object! R& K: B7 o# h& l/ E( s- e5 Q9 T
    1! k3 B. F# Q1 z# L* j
    2
    ) B* R/ J9 f# l6 L3
    * x2 M) f4 Y9 o9 r8 C49 k" w! Q* C% s) Z( t! O
    5- W/ S8 S: P/ ?; s2 Y2 p% c* {
    6
    7 h( }% I, _9 F) J7, \( L2 A' T7 R
    8+ S# B4 j2 B# o6 c
    98 R7 l, w4 B7 `) `
    10
    ; P6 f! U8 }, O! g8 Z8 R11
    8 k! ]- t9 @7 C- y+ p, M$ m12
    . r) [5 _6 c- \4 r- j+ \& }! z13
    2 l* Y; K: e; q14& F. q2 M' f. [1 H9 N6 l
    159 P5 _9 \7 L& L2 W' B+ [; @2 i) P4 p
    8.1.2 []索引器
    . O7 ~. g  T$ I/ b* f# H  对于 str 对象而言,可理解为其对字符串进行了序列化的操作,例如在一般的字符串中,通过 [] 可以取出某个位置的元素,同时也能通过切片得到子串。' ^3 ]: h; C6 F9 Z3 I0 Z1 Q! A
      pandas中过对 str 对象使用 [] 索引器,可以完成完全一致的功能,并且如果超出范围则返回缺失值:
    ! V! Y4 _$ P+ v3 C+ G9 t) @6 y* p3 R' z9 t: S2 f5 f
    s.str[0]
    ( E* W& h9 y8 B. D; BOut[10]:
    * K  ~9 n# ]2 y6 m- |# B8 r) x0    a" N( S, m% [5 q7 ^7 Q  y
    1    e+ O. w& b- X. `: \3 T
    2    h* @2 t* d" |0 O; Z) L
    dtype: object, @0 V, L; R8 d" L* `% C( }  {2 T

    8 c' {  v8 d4 O: I5 Cs.str[-1: 0: -2]$ z8 N' f7 U8 Q4 Y
    Out[11]: 7 @3 A$ w; X# l
    0    db1 c- |. p8 d/ T+ z/ m3 p" C
    1     g
    7 k7 u0 \8 u& `# g/ l4 q2     i& P1 J8 {- t# s6 e7 s
    dtype: object
    ( W, I4 K. D/ l' e
    ' ?6 l3 G+ X1 Vs.str[2]
    ) o% ^; E1 r2 i! ^6 yOut[12]: # t! W5 a9 {" @9 U; @
    0      c$ o7 n0 |3 ?: ~+ ?6 ]- i3 B
    1      g
    + |% N2 {* G! y2    NaN
    , z( m+ |. v& a7 W5 v. sdtype: object  y" `1 N1 ]% E, d

      ]% Y6 o0 ~+ D9 C1& t" p4 l2 o" X1 ?4 o3 r
    2% m; l, U4 o3 n- p
    3
    " J; b8 G1 c% u! h% g4
    : g/ P. w- |& V2 P; `5
    6 ]( q! i6 g) s2 r% D! \8 B6
    % z* l! |; k% V) D2 e$ `1 s4 c! i78 A& j! O3 M2 f8 v* Y% V( u
    86 X/ z$ Q3 t7 a/ [) @$ w) J
    9
    ! ~( E% G0 ?$ |% g# Q10, Y# g! o/ H' _7 z, P9 X
    11
    9 I' U" g4 C  W7 H12' l- _- j6 d3 w/ d, X9 r7 K
    137 R  g8 \0 R* b  y8 J) x$ O8 J5 v
    149 v4 \6 l: T% q; W1 v! i6 b, |  ?7 u
    15
    . }% B; x! V4 t& {! J' r* v% y16; J& n3 p1 `5 i3 B  X0 X
    17* x1 ~$ t$ a/ k7 H/ |' H9 Y, }
    18
    7 ^2 k+ P# Q  d  S# `7 O19" u, K, D: e1 \
    20( ^$ v. q% P& g) a, L1 a
    import numpy as np6 B4 j- G2 W! }' J' B! ^
    import pandas as pd
    / e; ^$ n+ g; c, E2 I2 ?* T- H9 I" D7 d2 z- e( Z8 ^  P# x
    s = pd.Series(['abcd', 'efg', 'hi'])0 a5 @( @* Z3 j4 \5 J
    s.str[0]# ?8 T3 f  V7 i/ Y
    1
    # p$ `9 P+ d) L0 b3 \2
    / T' h* ?& S: k: Z35 v7 ~. Z$ O, O$ p( Y* L. e" F# i: S
    4. L+ V( _! w1 E& J! x" o) S
    5
    4 F9 Q2 n7 i7 Q  i3 [+ K* O$ U0 G1 q; h+ E0    a/ s. P# t3 T; N5 y. N
    1    e  R4 s( T2 B4 A( ~- O! e/ v2 i: q
    2    h
    ' C# g, H; R4 Z2 J1 ~dtype: object; Q7 {! q, p7 |/ R' r
    1
    3 p' c, }% ^6 [27 H6 f$ A5 a. e% j3 x; r# [6 e' u5 z
    36 Y& _% T$ V7 E
    4
    7 s; s$ j6 g, q* j8.1.3 string类型$ w# W7 S. w( K: x* B# j/ t! q* u
      在上一章提到,从 pandas 的 1.0.0 版本开始,引入了 string 类型,其引入的动机在于:原来所有的字符串类型都会以 object 类型的 Series 进行存储,但 object 类型只应当存储混合类型,例如同时存储浮点、字符串、字典、列表、自定义类型等,因此字符串有必要同数值型或 category 一样,具有自己的数据存储类型,从而引入了 string 类型。& M1 V- K1 f2 G1 J8 o6 ?$ K
      总体上说,绝大多数对于 object 和 string 类型的序列使用 str 对象方法产生的结果是一致,但是在下面提到的两点上有较大差异:
    & W' Y6 D* `, c; T/ |& s) T. d9 ?8 |0 k6 s2 ^5 T
    二者对于某些对象的 str 序列化方法不同。5 a4 P' Z" h& s
    可迭代(Iterable)对象包括但不限于字符串、字典、列表。对于一个可迭代对象, string 类型和 object 类型对它们的序列化方式不同,序列化后str对象返回结果也可能不同。例如:% ?( a$ U8 ~- t8 e5 V  q8 l
    s = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a', 'b'], 0.5, 'my_string'])
    - z: Z; P0 T6 _+ P2 m% hs. a! n. _0 [9 p) \
    1% Y0 k3 J; @$ b! Q
    2
    & l& k' S! }2 M7 B0    {1: 'temp_1', 2: 'temp_2'}
    ' D1 W2 E+ c2 y, B0 j9 N# P5 r& E# u1                        [a, b]
    ; e' }2 P/ _$ O1 y1 ^% m2                           0.5, c- r+ x; Z8 u+ ^) \# i1 H& R
    3                     my_string
    - }1 U1 w4 d5 t- Kdtype: object
    $ V; s; l5 T1 ~: _1
    4 m# S! f+ ~4 s8 F* t3 W1 u, m2
    8 J: S4 Q% r: i5 u6 |7 Q! W/ c4 R30 g$ g" p' {3 M/ y
    4/ \! E) ]7 K+ n, k! p- l
    5
    % h) }- K9 a6 J( o7 `. i1 R2 \+ bs.str[1] # 对每个元素取[1]的操作
    . d$ L. A: M' K& T/ ~6 L8 s8 ?$ C. Z# o11 z- Y  P/ K! {* s$ w
    0    temp_1
    * G- s' m4 x, L/ Q; W; n1         b
    " {! g- o! W: p7 c* Z5 M' R2       NaN. ^, e6 ^' J# d. }6 m/ E
    3         y+ ~" ]. t3 u$ Z
    dtype: object) G: l. K0 {) D; A/ x
    13 g$ `# `0 {* f- x
    2; [; x$ F$ J, `' f& ~
    3
    & J1 w# l& o2 y1 P48 {9 j' z/ i! O+ W9 x$ g) I- }" e/ F
    5
    / _# H9 O8 B7 e5 ns.astype('string').str[1]% P' q0 n7 r2 W" B9 ~
    1
    . K6 W9 f' d9 Z& G# Z0    1
    ! N' m6 ^3 ^- [1    '* j( [. T8 {& ?2 g& G
    2    .. C4 ?' g9 H# X! B
    3    y
    ' O2 C3 R: e$ j% H7 \( vdtype: string" N" k( }5 c# @- j
    1
    8 F: Z" ^2 J: O2
    + r& p2 o. z+ p3 l9 A$ K% T3  G& M/ [! ^, I& d0 C; s' F
    48 ^8 o# U& r. S) S
    53 C8 D/ N. D+ G
    除了最后一个字符串元素,前三个元素返回的值都不同,其原因在于:
      P; l( f9 {- ^+ _7 l5 N2 m! G' N! D/ K3 N/ h  Z4 Z5 L+ h2 n
    当序列类型为 object 时,是对于每一个元素进行 [] 索引,因此对于字典而言,返回temp_1字符串,对于列表则返回第二个值,而第三个为不可迭代对象,返回缺失值,第四个是对字符串进行 [] 索引。
    7 m/ W. ^3 r: h9 _, z; [. F& ystring 类型的 str 对象先把整个元素转为字面意义的字符串,例如对于列表而言,第一个元素即 “{”,而对于最后一个字符串元素而言,恰好转化前后的表示方法一致,因此结果和 object 类型一致。
    9 H) t! j% F6 q8 Mstring 类型是 Nullable 类型,但 object 不是
    1 ]6 r  w# D% l7 q! Z$ J7 N  这意味着 string 类型的序列,如果调用的 str 方法返回值为整数 Series 和布尔 Series 时,其分别对应的 dtype 是 Int 和 boolean 的 Nullable 类型,而 object 类型则会分别返回 int/float 和 bool/object ,不过这取决于缺失值的存在与否。% p+ s* D: A2 F4 k4 f: i; |; O2 A
      同时,字符串的比较操作,也具有相似的特性, string 返回 Nullable 类型,但 object 不会。
    # ]) m* {) ?$ b$ M& `: Es = pd.Series(['a'])1 _5 J. _! T! F; @
    7 Z( ]3 g6 ^- N/ y# ~
    s.str.len(): ^; V$ w+ x! B$ V' z+ I
    Out[17]:
    : `: E4 _/ h2 o2 H2 n0    1  p3 L. l' f/ d0 u' i. M% q* a
    dtype: int643 d& V3 |+ }8 |6 N; Y& `4 `
    9 N0 Z1 M( w7 \( o
    s.astype('string').str.len()2 }% F8 v" s! O! D, J* X' I8 U
    Out[18]: 4 h* M* F0 h& Y8 d
    0    1
    0 t# J: z4 q6 adtype: Int64
    5 V" y) G& [& H, I- a6 ^2 L9 {' ^/ r1 i% A0 ^9 X- ^. E  {
    s == 'a'
    " ~0 O; G6 C% D6 D4 yOut[19]:
    ' p; @- D& K/ S3 Q0    True6 Z. V. z4 O* `3 j; r' N
    dtype: bool" k) U! ?2 b* t% r& A% o

    0 x5 `4 I" j; J8 zs.astype('string') == 'a'7 a/ L( @6 a) d2 w6 H4 C
    Out[20]:
    3 M. \- Z" p1 ^  \) b$ ]+ |0    True4 b! k1 P0 b5 N8 C" K6 Q2 N
    dtype: boolean7 ~( U( X5 v  a( t/ w+ L

    : y6 y' J) ?) S: D' f* ys = pd.Series(['a', np.nan]) # 带有缺失值# M, j6 l! D2 B# t

    ! p$ a) \' Z0 S3 y8 ds.str.len()! ?( ]! B3 E& R( k" f
    Out[22]: ' I* i' |/ B5 M6 ~- V, M
    0    1.0
    + i6 o9 J! k1 h! s4 }' {1    NaN5 N' q4 n, x2 }5 s
    dtype: float64
    % ?7 L, e) [" z# r+ P8 J7 D. `! ~$ t" P6 o, z" L8 W; Q
    s.astype('string').str.len()0 G+ R2 \: i2 `4 I$ L# ~9 M  s2 n3 ?
    Out[23]:
    % ]9 v$ w* Y. x* [0       1! o6 l# c9 m% @  D4 g$ d
    1    <NA>
    # ~' L7 k2 O7 f% V; `dtype: Int64
    ! n6 q$ u' V  O: N* b, k+ N# U- \  @1 A( Y
    s == 'a'
    . M% O3 U  M" p& I" K0 VOut[24]: ) V  O  z4 u* _+ B! q
    0     True
    9 h% w/ w4 e( R' U8 [6 f& m3 o1    False, w% Q) V& d/ k& [* @: S  h
    dtype: bool
    ' z  m% h' o1 ~: J) o2 D% j6 F/ E; p$ P. W! c# {
    s.astype('string') == 'a'- X4 V' ~* Q" e6 y. Y
    Out[25]:
    ( K" I4 f3 N( J0    True
    + i- U9 G" y; k  K7 d1    <NA>" P0 H& {" E1 k0 R$ u9 \8 X
    dtype: boolean
    ' e( ~/ r3 e! I3 F8 e
    2 `& \; k6 V, K  n2 z19 i. a; s: C" V3 e0 N* z0 D& k+ e* l
    29 ~- Q4 f' j( s6 c" J7 M9 M" @  ]
    3- T4 t3 P: w2 y
    4
    . @4 d' x' w' d( S5+ x3 z  g/ t/ R  N" o7 g1 m
    6
    ) D9 I/ E9 r. ?* I% R7( c2 J  t. i7 D; l5 ]0 m  A$ |
    8; i+ x% u) o: w& @  D% K
    9
    % h# Z1 T; X6 h# X0 {10" G9 \5 K+ U5 d# I# r) W
    11
    # ?5 \7 s) n' D; U; @3 {' s& ?8 x12
    $ V$ ?$ ^" w+ ?9 T13
    , e+ \1 z: g* }' b14
    ! B$ X- K7 l  _153 P. y) ]1 W, v' F5 G' d( J
    16
    8 t8 ?- z7 S2 o; \170 |; n- T  l& L  A& [( I* \
    18" v, u! m6 n: h- b/ M) I
    19* U; X4 W2 r( N1 A+ {, w
    20
    7 |1 d6 w% C: O0 Y21
    ; {  o: t5 ~8 R6 c, Z22
    / p9 s! R1 ?& g2 `/ J23
    1 v$ x$ V) Y3 d' I' a$ _9 f240 Y) I0 T0 O* U/ o4 A( `
    25
    ! u) `" w% p4 m8 \6 ]8 \26
    7 J3 b5 _  b% r) f$ g$ b$ e27
    " ]- [/ E5 x" ]! P9 _6 X9 J28
    $ ]$ ^& s1 ^; q% h' U% P29! M9 w* k. F5 @8 X4 B0 I4 H
    30
    & q+ M+ R. U. o" |2 Z% k31
    9 e/ }' F9 _2 I; j# L32
    7 d0 F8 C+ l; u5 Y33
    ! m! |" I3 Z: X6 m34
    * ^6 I0 r  X9 W0 w8 j35
    4 Y# G2 h& o& b" g36
    8 L& y' A  G6 S1 J37
    ! b( P' E. W  I  I( c# G38! U: e3 y. e( V' @
    39) z  E3 l- P: p" Q$ F7 g
    400 S- n6 R4 Z* o* ^3 l
    41
    ( H( r% d+ M1 V- h( c. X8 R' O  Z42( v6 \# I( E1 `
    43# ^" d0 H, U, y  @
    44
    " ^# u" I2 c7 X! S( R" ~) M45
    , w$ f8 y' P2 k, C7 j. S) u46/ M. X! z  ]2 L% g$ m0 X
    471 Y* z% d4 U' s6 _/ h
      对于全体元素为数值类型的序列,即使其类型为 object 或者 category 也不允许直接使用 str 属性。如果需要把数字当成 string 类型处理,可以使用 astype 强制转换为 string 类型的 Series :. m8 |0 C8 o9 @$ K

    - [% }6 R* N& V/ `s = pd.Series([12, 345, 6789])
    0 ^/ h$ {' Y7 u& I7 Q- ~3 s3 s$ ^4 A% I. h# @) h4 a" B0 T+ S" L  V" [
    s.astype('string').str[1]
    + g6 \3 x9 K1 o6 L0 h* EOut[27]: ( y# s5 q5 n. m4 n8 F$ I
    0    2
    ) Z0 z$ }* I: {; i1    42 d# v* r. c/ L; \
    2    7) ?* G* p; Q: j! q3 I! Z
    dtype: string
    % i2 h8 r2 C6 E0 h19 g5 j$ f2 U' @* e( }  `3 K/ Z
    22 G/ u- H2 ?* Q. t: l- k
    3
    ; Q5 R  t5 W+ c: x3 D- P4
    / U) e3 N0 s3 j+ U3 ]9 u5! J0 R8 A' I  b
    6. s. Y! }, q$ o: z) y/ J
    7
    5 ?4 x  y9 ]7 M$ T84 E# ~% U0 u/ x$ L
    8.2 正则表达式基础
    8 H1 M, i2 E' g, @7 I8 N3 t! R* B# }这一节的两个表格来自于 learn-regex-zh 这个关于正则表达式项目,其使用 MIT 开源许可协议。这里只是介绍正则表达式的基本用法,需要系统学习的读者可参考《Python3 正则表达式》,或者《 正则表达式必知必会 》这本书1 b$ }' k/ c; Z5 q0 `
    ' `+ G5 N- F6 w0 _  w3 E$ t7 f, O9 f, l
    8.2.1 . 一般字符的匹配$ z" a& d2 E  A" U4 b/ s
    正则表达式是一种按照某种正则模式,从左到右匹配字符串中内容的一种工具。对于一般的字符而言,它可以找到其所在的位置,这里为了演示便利,使用了 python 中 re 模块的 findall 函数来匹配所有出现过但不重叠的模式,第一个参数是正则表达式,第二个参数是待匹配的字符串。例如,在下面的字符串中找出 apple :
    , _( D. z& R9 ~% r: M; B# s
    4 X" P- y! w8 O8 C4 x, P. fimport re
    6 P! }" F- b2 l# r; ^% p' }& J
    + D& |, a! B5 O3 f' T6 Bre.findall(r'Apple', 'Apple! This Is an Apple!') # 字符串从左到右依次匹配
    1 J( }) _( M! F; Y6 B3 R4 xOut[29]: ['Apple', 'Apple']& A7 E: R0 ]; I. E7 B7 O# j8 W+ p! c
    1, ~' m$ n5 G" K4 R
    2
    3 R6 [. A# u- H' {; [1 C; @31 {  @$ {# a7 \: M
    4
    $ A0 @8 `3 Y, H0 F/ ]: D6 Z5 I8.2.2 元字符基础
    7 e& f% B, M( H1 x# Q: D2 w元字符        描述
    3 Q* k- f0 l- p# y1 M2 ?, d.        匹配除换行符以外的任意字符
    7 ~% w7 N: {1 m0 H[ ]        字符类,匹配方括号中包含的任意字符
    , F! ]3 p) }) q: t0 K: [[^ ]        否定字符类,匹配方括号中不包含的任意字符
    ' h4 N; S4 M7 C9 v- a+ h; T*        匹配前面的子表达式零次或多次
    2 W; `8 b+ s1 Y& T( _; p" G: w2 {+        匹配前面的子表达式一次或多次。比如r’d+'就是匹配数字串,r’d’就是匹配单个数字7 Y0 w. w7 r( B# r( ?! g
    ?        匹配前面的子表达式零次或一次,非贪婪方式* p1 E1 d  C& F: p5 j; V3 r, M4 U; X
    {n,m}        花括号,匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式5 e" ?, i+ X; m4 E
    (xyz)        字符组,按照确切的顺序匹配字符xyz0 Q; K& K% V( \. F) A9 S; I: _- T
    |        分支结构,匹配符号之前的字符或后面的字符
    2 y) n/ T7 \+ b0 z- u4 V* p; z\        转义符,它可以还原元字符原来的含义
    1 n4 s1 m! M7 P* q( T7 _^        匹配行的开始
    4 U- k5 z6 P3 a/ ?! s5 N$        匹配行的结束& A; ^$ J( z5 H
    import re' A+ Q: X2 s' U
    re.findall(r'.', 'abc')  F  [: h3 t* K7 [( Q
    Out[30]: ['a', 'b', 'c']* Z$ G" w$ [3 w% I9 D

    " F, O( w: u% H( ]1 Q4 hre.findall(r'[ac]', 'abc') # []中有的子串都匹配' {4 Y+ d- s$ N6 d* d- d1 _
    Out[31]: ['a', 'c']
    7 ?  P$ Z& c" q, |( V  s- f6 L
    7 X# H. x  X. E2 U/ D6 N) S4 J+ Dre.findall(r'[^ac]', 'abc')
    4 h2 u- t( O8 @4 n; Y- v2 w) }Out[32]: ['b']
    ) j- [, J- C" q1 i0 d7 d% a2 |
    4 L5 F* Z0 J$ p7 @! F; M3 zre.findall(r'[ab]{2}', 'aaaabbbb') # {n}指匹配n次* \: Y  E  O6 M8 i/ A, c
    Out[33]: ['aa', 'aa', 'bb', 'bb']
    5 }% }3 e1 |! }( i) F  W, ^5 S/ W1 x
    re.findall(r'aaa|bbc|ca', 'aacabbcbbc') # 匹配前面的或者后面的字符串
    1 i8 [) e- P$ a: kOut[34]: ['ca', 'bbc', 'bbc']5 k' l; ]5 j) f, h; z5 c' {/ a

    ; M) k8 Y- V3 ?$ I# 上面的元字符都有特殊含义,要匹配其本来的意思就得用\进行转义。
    " R( d( X/ T+ ^/ M"""
    8 ^8 M0 L- c# A# ~* }1. ?匹配的是前一个字符,即被转义的\,所以|前面的内容就是匹配a\或者a,但是结果里面没有a\,相当于只能匹配a。8 j1 _1 ?7 n+ K$ Q
    2. |右边是a\*,转义之后匹配a*,对于竖线而言左边优先级高于右边" E) B% u: j2 L+ M4 `
    3. 然后看目标字符串aa?a*a,第一个a匹配左边,第二个a匹配左边,第三个a虽然后面有*,
    ) o- y+ F' f1 m: o但是左边优先级高, 还是匹配左边,剩下一个a还是左边,所以结果是四个a
    : W1 X" M4 X7 @: Q5 W* V"""9 ^! P9 V5 b" W* y" t0 y- c

    & g. Z0 k, ]* Q1 U% r+ G- @8 a& Dre.findall(r'a\\?|a\*', 'aa?a*a')   # 第二次先匹配到a,就不会匹配a?。a*同理。
    0 C! c1 |3 Q+ |Out[35]: ['a', 'a', 'a', 'a']7 m8 ?$ g* J# O+ X

    - L. W- x# o) O9 l# 这里匹配不到是因为目标串'aa\a*a'中,\a是python的转义字符(\a\b\t\n等),所以匹配不到。
    - c' _7 r4 x" n, D% ^# 如果是'aa\s*a'之内非python的转义字符,或者'aa\\s*a',或者r'aa\\s*a'就可以匹配到\字符。- U, l) P! s% @% v6 T* e
    re.findall(r'\\', 'aa\a*a')
    % j& |: t9 Q% l& a/ {- {& d( j, m[]$ L3 q0 X' v2 q* R. r
    1 A( }2 |+ {7 W3 l8 a5 i- |( o1 g
    re.findall(r'a?.', 'abaacadaae')
    0 T0 `3 A( `0 q, h1 Z  Q* OOut[36]: ['ab', 'aa', 'c', 'ad', 'aa', 'e']5 [/ V; u: F5 ~3 H7 W' g1 p8 d$ W

    6 L+ i* m2 q* tre.findall(r'(\w+)=(\d+)', 'set width=20 and height=10') # 多个匹配模式,返回元组列表& r+ k' L) D4 z# Y0 \
    [('width', '20'), ('height', '10')]
    $ A. L& h/ d8 ]* v2 [# \$ x. H) ^/ ~; t# S8 @* L+ G* Y% V/ m
    1; w8 l5 @3 h. |. `5 f
    2" o0 h8 v; `0 P3 e& n
    3
    / U( f, [- V# M41 ~& B- N, N8 v, M! C3 c
    5
    7 F. u  d% I! b+ \6- o% D5 r" {0 u% u3 ?9 P4 O
    7
    - b) f: p4 i6 W8
    ! {" m2 W6 ]: a& G. K  {: L9. {, Y: N5 F1 D1 ~
    10
    / U( ]# s% M1 ?( ]# D11
    & S8 [+ H/ b  z12
    % g, m; q. k2 \) \6 A: @3 O! [136 c, L6 Y7 O7 W* R# R& Z( a5 B4 H- b
    14
    2 l5 c" |) b9 N* I. L: v, u153 V! z! |* w9 X+ |3 s9 s
    163 C  w8 D8 k$ a6 F. T8 R
    17
    ; O1 L# Z+ j, P: @2 a# Q& X2 p8 M18
    , v7 s, m* ^6 e: d% C19* ?( A( A  \9 C4 |* o9 ~5 c$ A
    20
    / ~. Y7 Y; T% |21% n7 {! ^2 K8 n/ h  B( e0 Z
    22. G5 _( s0 e/ W) y) L$ T9 g% z
    23& l7 u8 {0 ~$ k: M9 W, h$ N
    24% g4 A+ v$ A5 ]; q! o) o. B$ {" _
    25' z* \0 g0 J: }
    263 D0 ?# g# ]! U1 j8 B
    27( U8 ]7 s6 s3 |+ j% e
    28. r7 e  b- ]0 G- P2 U
    29
    : l1 z  n" T; Y$ p) p/ ]% C30
    . W/ R9 \5 v( u: G' O' v: g. e* k& l31
    ) P" C: q& j5 n: k; o/ f( {3 C32
    " ~6 P6 G' z; O. t33$ E# H- p+ m# o' W  m: u; T% j/ ~
    34
    # ~9 S7 f2 Y( Y) l9 T( Y4 d35
    % y% s% D7 U" d3 N' z# P) v9 E367 v4 |5 M1 z- W  J9 r9 h) F, x
    376 D# C# O$ r9 X+ I% G
    8.2.3 简写字符集+ T1 x( M' o% H' O0 S# J! G& x" H: k
    则表达式中还有一类简写字符集,其等价于一组字符的集合:
    . W, s7 m  U/ C/ g) [' G- @4 o9 r) s# k/ ~2 S
    简写        描述1 y( ~5 ^- o! f% B( W9 U
    \w        匹配所有字母、数字、下划线: [a-zA-Z0-9_]
    4 o6 `3 z' a  y1 n' O0 M. d\W        匹配非字母和数字的字符: [^\w]
    + \7 x4 ?. s$ Q\d        匹配数字: [0-9]. s* D$ `! g! v
    \D        匹配非数字: [^\d]
    / R' H2 Z2 W( N) _! S4 ]" w" }+ z\s        匹配空格符: [\t\n\f\r\p{Z}]
    * W2 W: l5 W" z8 I: M. ~1 y\S        匹配非空格符: [^\s]
    6 a$ O" H$ k/ H, e, M0 }\B        匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。8 B# a( `! o9 i5 j5 T% B
    re.findall(r'.s', 'Apple! This Is an Apple!')
    * h# h+ R" e7 @Out[37]: ['is', 'Is']
    3 B, X) @9 h. W" W
    ! H- U8 S# u8 s0 c$ Z! v. |! `re.findall(r'\w{2}', '09 8? 7w c_ 9q p@') # 匹配任意数字字母下划线的组合,但必须是两次
    2 h4 j0 W' @8 g! ~' `: Z8 AOut[38]: ['09', '7w', 'c_', '9q']7 b: ], o$ `9 i  X/ T
    * A7 q" k3 q9 R& _7 R& r3 F
    re.findall(r'\w\W\B', '09 8? 7w c_ 9q p@') # 匹配的是两个字符串,前一个是任意数字字母下划线(\W),后一个不是(\W)" w8 f5 Z8 }' `1 g! G
    Out[39]: ['8?', 'p@']
    ( h) t5 h2 \4 t+ |
    $ K0 ^# R$ U% V# W$ _re.findall(r'.\s.', 'Constant dropping wears the stone.')+ ~8 i) H* P3 G# h3 m( j+ ^6 v5 u' t
    Out[40]: ['t d', 'g w', 's t', 'e s']7 i4 u3 H. x( J/ ?! S5 v+ m

    7 v; G9 m$ b/ ]: Y4 kre.findall(r'上海市(.{2,3}区)(.{2,3}路)(\d+号)',9 @7 }  r- Z. s, \2 R0 @7 e* I9 X6 Z
               '上海市黄浦区方浜中路249号 上海市宝山区密山路5号')# `, f% j0 j( F( C2 D( m

    0 f& A$ ~8 r7 b$ R3 P/ MOut[41]: [('黄浦区', '方浜中路', '249号'), ('宝山区', '密山路', '5号')]# M9 N* L# h3 k* K% H8 Y" G
    + Q# W2 c% i0 ?/ U
    1
    ; \" ?  {$ h  E2
    : l: Z7 A- G9 v' f7 g+ }3
    + |# h) R% {& B$ n+ F, @* S9 G4' r7 q. B* F/ S" c/ `
    5% D; g# M4 w6 A. B# I# D
    6* j/ C4 a. q! {* a2 y5 \
    7
    # a* J8 i3 Q5 B7 ^: H8
    8 s& K! Y* g8 O  w* {6 Y# X9 ~9
    3 L* I: G0 l4 X8 d( z10
    6 R2 R% {8 k* t) A7 M" M5 @- q11- f6 L  P4 y" C  x# w2 v
    12
    5 w4 H" d/ P. y- ^7 V5 R1 Z  Z13
    3 T& ^* i5 p. Z2 _14( t$ f& F  L: i" x
    15
    * I# N- ^3 r5 k6 ?7 X7 ]16
    % Z- D2 @3 z, ~# ~- e- Z! t+ P8.3 文本处理的五类操作
    ( r9 h$ g$ U4 F" K4 n8.3.1 str.split 拆分# o4 R! F' w4 R
      str.split 能够把字符串的列进行拆分,其中第一个参数为正则表达式,可选参数包括从左到右的最大拆分次数 n ,是否展开为多个列 expand 。
    / z/ N6 }+ \0 R1 O* j% i, W+ h9 {" N
    s = pd.Series(['上海市黄浦区方浜中路249号',
    . `6 a4 `' f5 O: L! w/ x            '上海市宝山区密山路5号'])  I- B8 [+ J( \$ v8 v$ U8 i: }
    $ S" [4 `5 a5 o" \7 ~2 ^; I
    ! e% Q3 E: D4 C. }* k
    s.str.split('[市区路]') # 每条结果为一行,相当于Series
    ' `( p4 u1 ], x" eOut[43]: 7 M7 j2 A* @3 x$ F" N7 a) ^5 h
    0    [上海, 黄浦, 方浜中, 249号]
    ; s% k; ~9 O- E& z1       [上海, 宝山, 密山, 5号]
    : r0 v4 Y) g  y1 Ndtype: object
    : u5 z2 \2 p6 m- p+ p/ n  T  a, ]# |; t1 \
    s.str.split('[市区路]', n=2, expand=True) # 结果分成多个列展示,结果相当于DataFrame
    , R/ j, n: D$ iOut[44]: # A: l( o; V& r. ?
        0   1         2- p" T+ w2 _2 i- C( K+ S6 J! B5 i0 n
    0  上海  黄浦  方浜中路249号
    " q0 b. g0 f0 @) u& k5 f1  上海  宝山     密山路5号( Q3 |7 J/ ]0 x( N0 ^
    1
    & n. Z2 W4 p: {) m+ ?, s" w3 ^6 C26 K6 T8 b/ ^2 W+ d8 n- z
    3
    " ~$ O' H( s$ x0 h46 R4 W/ u' W' |
    5; Z7 x; \( Y6 l' R$ D! L& r
    6
    + D) U- o0 F3 {2 M6 C& p7! N& s7 u" _. J! b0 U5 D( w; J# q
    8! U: ^( ]* V4 L! o% c
    9
    % X! n! d' u4 B  ^4 S9 Z$ u/ o10( V/ U% ]9 N- g$ J9 M
    11
    : o: H9 O7 j) j7 b% x3 S12; a' ]5 `6 }- P0 E# n
    130 v* N8 M- o; |2 \
    14
    2 Y+ k% a' s: _' n- |15
    0 K% I4 j* k% c9 j  类似的函数是 str.rsplit ,其区别在于使用 n 参数的时候是从右到左限制最大拆分次数。但是当前版本下 rsplit 因为 bug 而无法使用正则表达式进行分割:
    & G) P  O* E$ A4 |7 r1 {! F9 _3 M1 ?0 n  N: b7 u" P
    s.str.rsplit('[市区路]', n=2, expand=True), ^" c& q7 b# V' R2 p( s/ r8 y$ B
    Out[45]: 6 L7 m5 T$ f4 ?- Y4 z# j4 V2 G
                    0
    7 D1 ]1 y9 Z* [7 K% q6 P# y0  上海市黄浦区方浜中路249号
    9 }2 \" T: C* H6 e: E7 r/ s1 `( P) d1     上海市宝山区密山路5号! }. j/ C6 d/ o* S& K$ P8 w
    12 P9 \2 c% \; V0 P/ e0 D# e
    22 l2 w* I) P& {/ Q
    3
      |+ h9 c* f5 X& O" y4' J' Y; P' [+ B2 b* z$ s& _
    59 }9 g0 k8 _0 K+ Q% i5 s8 H
    8.3.2 str.join 或 str.cat 合并
    - l/ M, k/ I% Wstr.join 表示用某个连接符把 Series 中的字符串列表连接起来,如果列表中出现了非字符串元素则返回缺失值。" }- m( [" p/ \  t! [
    str.cat 用于合并两个序列,主要参数为:
    0 M  U+ j8 c, ?9 fsep:连接符、
    / j% ^! H. x# X) e4 O! s! F+ ojoin:连接形式默认为以索引为键的左连接
    / G  s% l( n. t( m$ @0 Q8 B6 \na_rep:缺失值替代符号
    8 V% ?. r( t: N* Ws = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']])
    5 ]6 N* ~+ ?6 m) As.str.join('-')/ g% t0 u- N+ X, R# E* I
    Out[47]:
    0 P) z1 z. h  X8 D  H/ B, p/ Z0    a-b9 `' N5 H; Z# ^4 u" c/ e: J
    1    NaN5 W; O$ ]# q5 D8 J# r+ ^  C) i
    2    NaN0 _4 J4 Z( k, E: f/ _7 u1 i
    dtype: object
    ; f5 @" V+ l( q1. r- k: n- Q/ B1 d7 V, |+ ^
    2
    * n* D7 w1 o/ \2 z; p. J$ l& I* d3# h' D: \& Y* O8 y3 }. @3 e, p
    4, b9 H( M8 N9 t
    5  n4 J  b7 X. E0 d
    6) s8 ~3 F# i/ x/ |5 j' Y( l
    7
    9 o* p. E$ J9 f. X2 M+ K, Js1 = pd.Series(['a','b'])
    ; S; Y% }( o3 j1 o$ @s2 = pd.Series(['cat','dog'])% k) v! Z, k& E
    s1.str.cat(s2,sep='-')2 [9 f8 s5 q% m3 r  C9 h  Q( M6 I
    Out[50]:   s9 E% \4 E- C* i1 v
    0    a-cat
    1 h. n, B  C/ G+ Q7 b: }; C8 {1    b-dog
    0 F) q2 e4 {' s* J. a* ydtype: object
    7 w# Y; Z4 ]! V& X
    ! g+ I% ~7 \/ ks2.index = [1, 2]3 W( Y% I0 R  a, M2 O0 K" X
    s1.str.cat(s2, sep='-', na_rep='?', join='outer')
    7 Y8 J8 t8 p% N( z6 yOut[52]:
    ' T$ K. d  \7 u0      a-?
    7 r# U$ j  S! N, d! d$ J7 |1 S1    b-cat
    + G( R$ O9 d& _8 \& V  _7 e2    ?-dog* {6 c5 E4 f2 v' ^
    dtype: object0 i9 Z6 {: G! u  s: v4 ~
    1
    5 I# s0 e9 K+ J& {28 g' w- y0 m( D5 B. Z3 w
    38 z) f9 l7 a# q
    4
    + N1 B4 B( `& j5, h/ A* y" {8 S$ h# ]9 F
    6# U6 K, N$ k3 \# W
    7( n. y+ w6 T5 y+ F
    8
    6 b( r" K$ a* d  x9
    3 z8 N, h7 f8 c! m! ]& V# p) e; _1 h10
    2 E+ `0 N) F3 j11
    # ~* R* `6 ^' d  }8 o( z8 E120 s, y& u6 M+ ?& q
    13" b& l3 k: U; j( O+ ~( z
    14
    . G& `7 @' [2 R  ~2 k4 Y/ n) s) ^15
    , _4 F. F- k6 N% D5 v& W: G8.3.3 匹配
    ; ?' N% g2 e. Rstr.contains返回了每个字符串是否包含正则模式的布尔序列:
    . L& Y) Y8 i/ j7 |s = pd.Series(['my cat', 'he is fat', 'railway station'])) E2 X7 p+ U: U4 u. {& [. V3 o
    s.str.contains('\s\wat')
    8 r* l! K: a2 D6 b" G3 h: E
    $ i1 D. C) O9 }+ {2 i. D7 q4 A0     True. [( U1 r6 `; C" u' M) p6 B' x
    1     True
    , z, Q/ ]) g. b5 F4 Q' I2    False
    * t; f8 }* E! ~9 A/ e) e7 R- Cdtype: bool
    $ `/ e6 ~7 N; R1
    * ~" y+ z& J- C2
    8 i5 @0 T5 h8 ^, V6 e' G, b3  ~$ W1 d; u) V* ?
    4' C" h$ h. V2 x$ S
    5
    $ D& A2 D" w+ X8 v6
    2 N: Y9 T8 r) ~  ~- h. [70 J8 C  v) f7 A* Z5 o$ D
    str.startswith和str.endswith返回了每个字符串以给定模式为开始和结束的布尔序列,它们都不支持正则表达式:5 f% Y, [: j, ?  r2 h; ?6 z
    s.str.startswith('my')6 ^% a! |" i3 Q8 O3 M7 o: G7 r; J
    ) K- T8 Q. t) y/ n. e# d
    0     True8 h: A4 b* I; D( g
    1    False
    $ c  K( V9 ]* R4 A6 l( W( u' h2    False9 _8 ^# C$ O0 ?
    dtype: bool: W' i1 Y5 R+ ?: w% `" z
    11 h8 ]! a6 o3 \2 M# x5 u$ ~
    2& a) n9 n  j% {& t
    3
    0 w- f) @+ @% w  U: L9 v4
    ) ?' }1 L7 u( b2 E5
      c5 d9 l+ c2 a8 ]8 f' [" B* m( n" A63 N) M* B& a0 i1 ?8 w8 v
    s.str.endswith('t')# Z3 D* c% R- J, Q

    ) I1 o& W5 w7 t0     True: {  D7 z) F/ I! P! m! y5 @
    1     True
    ) b/ J5 b2 S& D& a* x$ p8 ^5 f1 K2    False
    . K  t& f' D; {. g% L2 _dtype: bool/ q. q% Z( D# k9 S+ K1 q& P/ |5 i
    1
    9 e& r7 L- V* Z2) |- A& ^7 S; F) L; @
    3
    - F& M; ^- B1 @% @4
    0 E3 W, ?  \6 @5
    " l  H; P+ P1 ?! q7 [62 I- D/ l$ W4 _3 [5 i. l# F
    str.match可以用正则表达式来检测开始或结束字符串的模式,其返回了每个字符串起始处是否符合给定正则模式的布尔序列。当然,这些也能通过在str.contains的正则中使用^和$来实现。(貌似没有python里的search方法)
    % {( I4 n3 x% b! Z( }" Y) Fs.str.match('m|h')( r# X; _8 b' o6 r4 K7 {: p3 w
    s.str.contains('^[m|h]') # 二者等价
    . R1 c2 ]+ l; t/ a+ M: q8 D& w$ _& K4 d8 y3 n- n0 h+ ], V
    0     True
    ; W# P# @& V+ U6 Q% {/ g2 t# m1     True
    4 `4 F$ D) \7 \* _2    False; b5 G7 q! |2 N+ c
    dtype: bool
    " }: o7 S" `- v0 D, }2 P7 ?/ L1$ o" f1 f' M" t" {
    26 v$ j2 i" N8 x. j% ?1 }3 X( o
    3" @, D- Q% Y7 w4 N- {1 U% x
    4
    4 w5 w! O% A2 R8 L9 F* {( G. M5
    1 {3 y1 I/ @7 z. x6
    ! ^( D  Z  W9 K8 k0 x/ v) x71 b1 F( j$ O3 q$ ]
    s.str[::-1].str.match('ta[f|g]|n') # 反转后匹配" v. |% B6 i3 n0 m
    s.str.contains('[f|g]at|n$')       # 二者等价
    8 ]) m# C* v0 @2 d; T+ y' q
    ; ]) w0 o+ M3 ~$ O4 e4 p0    False, S6 B6 `1 W. e' y3 D& o
    1     True( l" v! B9 L' s( d$ r
    2     True
    ( [; t. C  e3 u+ W% Rdtype: bool+ A  \: V! W% O' Q7 O
    1
    - L& u/ e) {; q4 m2
    / e$ Z8 F- X6 P2 Q$ h3
    % r8 c; n* f9 X  }4
    ; V2 s2 Z- J7 L9 a9 r53 _: K/ U' Z  I' i4 ]* J
    6. p* X, R5 g- \- }0 W3 a/ ]
    7/ U! B" I, [$ h  m. P2 [% k
    str.find与str.rfind返回索引的匹配函数,其分别返回从左到右和从右到左第一次匹配的位置的索引,未找到则返回-1。需要注意的是这两个函数不支持正则匹配,只能用于字符子串的匹配:
    1 o6 J4 e$ }, w" L) j0 U- Qs = pd.Series(['This is an apple. That is not an apple.'])5 w6 ~6 |/ w; R9 Z$ o4 f$ J1 k
    ) R  r. i4 i( I
    s.str.find('apple')7 v; Z- b- q$ e! ?' e& w) A
    Out[62]:
    ) u3 ~- \+ t* y8 V2 l! n9 S, Z& n0    116 [9 P& R* j- ?# v6 ]5 U, ~  M# y
    dtype: int64
    8 w5 @; C) ^; W3 A( x4 H7 K1 Q' X" Z8 R" S, k% b' y% e8 D+ y
    s.str.rfind('apple'): U7 {0 U) `0 D! w' F8 s8 ^
    Out[63]:
    # o% \; `! b3 W: F3 A2 N0    33( ]$ j$ i- C# D5 C2 Q5 ?0 Q
    dtype: int641 v3 p- C5 d' U6 D. u
    1
    0 O4 a7 G9 C- K/ K- s1 V% i) t2 f1 N6 H2
    4 x7 R0 R, J; S! |( o& N% [0 a" L6 W3
    5 Y7 ^: i! d; W" R' Z. }" X4
    1 \* x+ w# y. y& I+ w5
    ( A  x; `5 k- N" \  [6
    - f2 J7 N9 i0 O  D6 j2 Z75 H* Y( p1 i5 w4 N8 P; @; \
    85 a( ^8 x/ \$ p# h' l
    9
    - m9 ^9 _7 m5 _" K( V) u9 k- w$ n10
    9 B, }) ~: m2 J' R# y4 `% ]11
    5 G8 v2 `, L, [# @替换
    + K! q; n* W( Pstr.replace和replace并不是一个函数,在使用字符串替换时应当使用前者。
    " C3 ^" H& g$ n6 Os = pd.Series(['a_1_b','c_?'])
    ! f* a$ ?% L0 c2 a9 O) @# regex默认为True,表示是正则模式,否则第一个参数内容表示是单纯的字符串,也就是匹配字符串\d|\?: e% q* d: K3 a: |
    s.str.replace('\d|\?', 'new', regex=True) 8 {0 u& Z9 N' Q; N- K; S* E
    7 S$ |8 ^: D. |0 o5 P$ }, R
    0    a_new_b- U8 ]0 W8 a3 Y9 P& y
    1      c_new( a! q' c( [( j6 _% M; |& X
    dtype: object; X* i  M: n. N9 H" U' Z9 ^
    1
    $ Q' C4 ?; G  o6 h5 E2
    - i. Q0 H. Z! R9 o" h3  g% Z; S4 T4 o4 C  Z! I8 I  h* u
    4
    3 X1 N( m3 `% Q! z& R- u52 b0 g8 z- v: g4 N2 f
    6) f3 O* p. U9 F4 h, T
    7
    3 X8 ~9 D, h4 U: y5 ^0 }- H3 e  当需要对不同部分进行有差别的替换时,可以利用子组的方法,并且此时可以通过传入自定义的替换函数来分别进行处理,注意group(k)代表匹配到的第k个子组(圆括号之间的内容):1 P9 u& {/ a8 w/ P  j
    & U" Q! U& s; ~6 U! k
    s = pd.Series(['上海市黄浦区方浜中路249号',5 n: ^7 g9 N7 B: H# H! l% C) Z
                    '上海市宝山区密山路5号',
    ( [& D; K& W* J" E                '北京市昌平区北农路2号'])
    3 v' `4 k! k& ]& l+ t( Vpat = '(\w+市)(\w+区)(\w+路)(\d+号)'9 y- ]3 j  l% E2 F! E
    city = {'上海市': 'Shanghai', '北京市': 'Beijing'}
    1 n& T" f( z2 _; O6 n, C/ P3 ldistrict = {'昌平区': 'CP District',6 @* i0 |# O6 @8 R' G2 |
                '黄浦区': 'HP District',  s* O% v4 ]" I1 m2 J
                '宝山区': 'BS District'}8 c" M  I: E# ~. i, e
    road = {'方浜中路': 'Mid Fangbin Road',
    & e" Y5 Z/ s5 x4 s        '密山路': 'Mishan Road',
    + q) T; t$ N. \" f& O4 ^* M        '北农路': 'Beinong Road'}6 g0 Y# G. x8 b# u! S3 a
    def my_func(m):
    7 l" r7 ^$ a7 }$ Z1 q! r    str_city = city[m.group(1)]# P9 }; p# A- M* y, n/ }% d
        str_district = district[m.group(2)]# }3 u; P# x$ |2 A+ q& Q7 J$ O
        str_road = road[m.group(3)]; R  M+ i5 n; r7 O* c/ w
        str_no = 'No. ' + m.group(4)[:-1]
    3 ]3 z" N; _! ^2 s1 P4 t2 j    return ' '.join([str_city,/ p$ l/ ]1 b7 c& X9 A* Q& I
                         str_district,
    ( X  V% o3 X8 B8 p1 A                     str_road,# i9 @+ |$ m/ m  r6 {- A2 ^% j% Y5 D
                         str_no])
    2 h2 z* ?1 U7 n5 G( k& Ls.str.replace(pat, my_func, regex=True)7 P( l& t* ?) B4 j$ E+ c

    ! J( e' v6 Z9 Z1
    5 j  M" _" T! b( ?+ R1 K2
    8 k! `1 D" `7 J# b# H3
    / B4 O. w" t7 X  M4
    . ]4 l& E6 h" i5
    % L& Z' p0 t& H; P6
    0 Q6 c: {. O6 D& Q7
    + u' y8 p( C% [8
    : M3 E! c4 V& M2 m& h& E9
    5 Q; a) ~' H9 f4 \10
    - j  q5 n# M0 G4 g4 d1 {! K119 Y5 Z5 u% t7 w% L
    127 _5 Q) L+ o( s% m7 u
    13" q$ P6 _% E/ y7 ^# S
    14% f, d+ Z5 k! v* N) q3 S
    15
    + u2 Q* N& T8 j0 s# F16" C  p4 k9 `) [) \$ j- V
    178 G4 h/ f+ k1 o7 [' n& t
    18# ]/ T! n% ~" ~, W
    196 ?; I% P. [4 C
    206 @3 j6 H# r- W0 e& h# H  N
    21
    " N. Z' R6 m8 Z) V0    Shanghai HP District Mid Fangbin Road No. 249
    ) X8 l* B+ \1 L) n  O; R7 g* \/ X8 _1           Shanghai BS District Mishan Road No. 5
    1 r$ ]3 ]5 w5 i2           Beijing CP District Beinong Road No. 28 e/ e+ B' Z% D4 e0 ^
    dtype: object; X# C, E  U. Q  t
    1
    ) b- k% G2 E  n% q6 g' m2
    + S( t7 o7 P5 z2 d9 a7 \38 M- U* T7 f" d. }0 @" L* \
    4
    6 `8 o8 a4 [, _这里的数字标识并不直观,可以使用命名子组更加清晰地写出子组代表的含义:
    ; w: k, Y  h* B9 K9 u+ h0 I# |1 \% Y7 P0 E& z& _# B* |
    # 将各个子组进行命名! [7 C! H/ A: L& u
    pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'
    " ^1 {/ E9 r' Q$ u7 Mdef my_func(m):) J: n% @/ R2 c3 `, _
        str_city = city[m.group('市名')]
    1 i  |; @9 p  K- w; B    str_district = district[m.group('区名')]
    / K; M5 Q* g% x% C    str_road = road[m.group('路名')]
    6 v0 h$ ?3 t  i9 U  L" H4 I    str_no = 'No. ' + m.group('编号')[:-1]
    + c+ T, U' n) M9 J% \3 J    return ' '.join([str_city,
    - v& o6 b6 I) I( e. r                     str_district,
    . [5 K8 M" L" j* N( A- l8 ?                     str_road,
    ; a; F- V2 w5 l' w                     str_no]): w5 |% {2 t% M( u9 m
    s.str.replace(pat, my_func, regex=True)
    $ b0 `& E, F$ c1$ Y( O4 I" G# X1 _! d8 k! m
    2
    6 d4 ^$ m  w  @) U0 T+ d3
    / J! x+ E& K* \4
    0 ^" q9 Z- a9 ~, B8 |4 F5
    : a6 q4 \, F; t0 y" T2 r. t8 a68 o' X6 \6 j( t. n
    7
    2 p5 f  Y# Z: O. Q6 T9 J8
    1 P3 @( v% T' d5 C- I92 f. X6 r9 B; E; [& x5 R
    10
    6 R! r* ]. m% `( t( j8 W. G% Y11
    * Y7 Y1 U% m' c2 l7 S% b  h: {" |12
    : c4 A9 |( ?0 r0    Shanghai HP District Mid Fangbin Road No. 249
    9 U) h: v  v- s) X/ I: n( `6 ~3 B1           Shanghai BS District Mishan Road No. 57 M/ M! w) u0 j1 w3 f! k+ v- m
    2           Beijing CP District Beinong Road No. 2
    ' l* @& Y; [: l) Idtype: object
    3 g  Z$ \, k5 ^1" ]+ ^: p4 q7 v; h
    2
    $ L9 c' |* R7 s  I0 u3
    / W7 j- N% ?! E6 Q6 V4  E( ^- q1 j& ?3 s" C7 t1 T, H; D
      这里虽然看起来有些繁杂,但是实际数据处理中对应的替换,一般都会通过代码来获取数据从而构造字典映射,在具体写法上会简洁的多。% g0 i7 p7 I" x6 Y. q) Q
    ' t! t: z8 n* U7 F& a' P* V8 W
    8.3.5 提取
    & C% D. k, k" w( Gstr.extract进行提取:提取既可以认为是一种返回具体元素值(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的str.split例子中会把分隔符去除,这并不是用户想要的效果,这时候就可以用str.extract进行提取:4 @+ K' n' {9 k; G1 {# D" e4 |2 X' f
    s.str.split('[市区路]')+ }  G% M! r7 R4 c& v* O" s
    Out[43]: : k. i" f% I1 c" l' e! e! h
    0    [上海, 黄浦, 方浜中, 249号]
    9 p) ^1 q8 F* E! j! m& `# J1       [上海, 宝山, 密山, 5号]/ h* ^, [$ x1 `. j
    dtype: object
    , Z# `9 _* x5 z4 I" a$ I$ g- e- v, |2 S
    pat = '(\w+市)(\w+区)(\w+路)(\d+号)'
    5 m5 x) o+ Z2 s' v. U! `s.str.extract(pat)
    8 F) A$ s  E* ~2 k" z7 T* f9 sOut[78]:$ F  _) d6 E# Z# _
        0    1     2     3: z3 x+ i4 a1 L" \# D) @$ H9 ^
    0  上海市  黄浦区  方浜中路  249号+ r+ l5 S2 v  _  @3 S4 \) v" f- k
    1  上海市  宝山区   密山路    5号
    " F: N6 D5 m3 ?2  北京市  昌平区   北农路    2号
    - ]2 {8 O) V" r# k4 i9 b1
    ( e- w. r! s9 {* e+ L2
    , S: K# P. X* Y& M- Q3
    & B5 D0 W/ o6 _1 i+ T5 {- W9 M4; T' y1 F  ~! q9 `* h
    51 y  ]1 q! A5 l
    6
      H$ Q1 {  K- J7
    + ?/ J$ k; n" F/ y( h3 ?8
    0 c4 f. L2 \6 w& x# f5 \. S: ~6 o' |9; |1 _8 L9 w$ H
    10
    9 j3 Y. p! }; ]* d* }% D  H11
    8 z$ X. ?% Y2 k  A12
    9 T7 N" O4 f- Y7 ^# Z13
    * ]8 C8 H- _/ \  ~& o通过子组的命名,可以直接对新生成DataFrame的列命名:
    1 |4 t5 V9 c5 k" d/ C' V5 E5 z: |% Q" |* D6 B! w' N. A
    pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'/ ^- R* Q1 _9 r8 u) \1 V& }; @8 B
    s.str.extract(pat)
    6 X0 z) s* M$ v+ OOut[79]: & K* G+ h8 p& Z& ~' e. Y! e
        市名   区名    路名    编号
    ! C9 o, x, z2 z# J; U. @" T0  上海市  黄浦区  方浜中路  249号7 l$ G5 z( ]8 W! h6 G9 h. C
    1  上海市  宝山区   密山路    5号
    9 m( C; h  c1 h: a; t* `. M; \2  北京市  昌平区   北农路    2号  q+ e: R" v; S" O& S7 _
    1
      ]0 [( ]8 V6 n2 d1 l- }* s0 E6 _4 J* t2
    " E% ?2 g$ ~8 i) I5 Z, t3
    7 E0 Y2 e* _! O( S: E4
    1 G" g& O" a9 N5
    6 q6 g% O( w  z8 W* r, T. L6
    # |; R+ B2 V& h! N7
    6 w& Q% F2 D/ `- ~$ Sstr.extractall:不同于str.extract只匹配一次,它会把所有符合条件的模式全部匹配出来,如果存在多个结果,则以多级索引的方式存储:; ?, z' X( Q' s: z, n
    s = pd.Series(['A135T15,A26S5','B674S2,B25T6'], index = ['my_A','my_B'])
    5 N; Q6 ]8 z" d& F. qpat = '[A|B](\d+)[T|S](\d+)'6 P% X3 V' A- ~3 b
    s.str.extractall(pat)
    ) b8 f, M) O. s6 O8 YOut[83]:, v7 q* P( t# R: A! L/ M3 \% V
           0   1
    . e3 f$ y3 `' M- Q3 b; U) m' n     match         
    0 q3 W: r7 q6 A2 w; pmy_A 0      135  15
    $ b( R+ g0 \( }* b     1       26   5
    # K1 v: `9 H' r# S- o9 ]5 v7 J2 Dmy_B 0      674   2/ ^' r8 Z7 c$ b4 r2 u, b5 W8 z" I
         1       25   6/ A; d' q% q5 ^; N3 k
    1& ~% z4 o8 q# J+ {8 B( K) C$ B
    2
    6 i# j9 J7 }* m7 o1 {" K9 l! ?3, _5 N. X0 p/ C7 H: [% A
    4
    1 \5 o$ U0 ?) [- @5
    ) R4 ?4 W4 n# ]1 @& T1 y$ ?62 f% S3 a3 \1 l$ F0 S4 R% Y  t* a
    78 d  |+ R$ L' Z& ~0 k
    8
    : y& b. R; S2 g: H9
    3 B3 I1 ]* m& n4 {8 R/ W8 c10
    ! q6 _3 ^# ]% y: upat_with_name = '[A|B](?P<name1>\d+)[T|S](?P<name2>\d+)'
    5 x3 l8 c' B  L# f/ }s.str.extractall(pat_with_name)
    . Z$ i; [9 ?8 w7 ^5 ]: NOut[84]:
    2 E- }# X6 J  o# e. M           name1 name2
    4 `  E5 [" p3 x5 ]! u8 }     match            
    / j6 Y* U3 ]  t: _, {% e9 |my_A 0       135    15" n; F, G( x7 \& _# i1 B& i
         1        26     51 q# x3 |/ M8 Q8 s8 X& q
    my_B 0       674     2
      j! Z9 P: ^3 T+ o9 }  n" I     1        25     6% l1 e) I6 e1 K: U$ C& C2 [& C
    1
    + h1 J% q1 |2 G' T2* R; `0 i$ J6 z! T
    37 ^6 g. P; }  t, p& p
    4
    : K& i5 P2 d6 d( _6 ]) G# V5
    ; o5 ?. b4 @" I3 l+ z6
    6 Y7 c9 R* Q6 ~5 K( f78 C1 \9 c" Y) p! n
    8
    0 W; D" D2 V2 G; M1 c9' E- H* {5 ~* M7 G
    str.findall:功能类似于str.extractall,区别在于前者把结果存入列表中,而后者处理为多级索引,每个行只对应一组匹配,而不是把所有匹配组合构成列表。; x% p* o* U9 e1 G, _& s
    s.str.findall(pat)" h1 \1 |% I0 N
    1* g2 j! A4 _1 L) ^% \7 [
    my_A    [(135, 15), (26, 5)]
    ' z0 x# Y2 Q  r0 L$ B  l5 Q5 K2 |my_B     [(674, 2), (25, 6)]+ w" z- V$ @) E1 t+ v% p2 b% g
    dtype: object
    4 J' ?. Z7 j0 |; b' C% r  n1/ c) a) F, ~3 Q! ]! r; N
    2. f3 q! r) _+ |9 q
    3% s8 O$ b7 @' X# U0 |, W2 H/ I
    8.4、常用字符串函数
    ; \* p. ~2 n9 ]; I: B  除了上述介绍的五类字符串操作有关的函数之外,str对象上还定义了一些实用的其他方法,在此进行介绍。
    ( F& e! g. U. |: j
    ( b, ^8 A' l4 g, y8.4.1 字母型函数3 b: R* \0 L  O/ G
      upper, lower, title, capitalize, swapcase这五个函数主要用于字母的大小写转化,从下面的例子中就容易领会其功能:" H9 x. C0 N$ r" s7 l" S/ K# S
    ' s* a0 N$ s# d# x( T
    s = pd.Series(['lower', 'CAPITALS', 'this is a sentence', 'SwApCaSe'])) y+ ~8 E  E/ Z, ^

    ) Z. B  j/ l2 a. C0 U% o: U) v0 [; \s.str.upper()5 _. L; M1 ?" [7 k6 d9 ]0 }/ ?4 K
    Out[87]:
    8 j" v6 y& h/ R9 _. F5 B, m  A$ I0 W0                 LOWER: y5 n0 I& ~2 n& q! F
    1              CAPITALS: f3 G' t6 E" W* \6 ~( ?
    2    THIS IS A SENTENCE3 q. f- G; |6 ?6 g" K. l
    3              SWAPCASE1 Q1 _: |( K5 S
    dtype: object! `7 o0 B9 F7 y( A6 q! Z/ ?& r
    $ S/ @$ J) u( k! a" ?
    s.str.lower(), \% g, _; l, F) X. x! Q
    Out[88]:
    ( o1 [- ~* }' d0 U0                 lower8 R  K4 {8 `, ^7 F
    1              capitals/ [4 ~. S( x; X  m. H" ]
    2    this is a sentence, Y+ K. Q6 A: x5 h& Z+ d: H
    3              swapcase. Y, r8 Q" [# `; C/ q6 }' P7 v/ z
    dtype: object+ J' @- d" w+ {  ]$ j1 r

    2 ]) {! i9 t/ z, t8 x* h# ^s.str.title()  # 首字母大写7 C0 u+ d3 I) X) N# J6 e
    Out[89]: % q3 S8 r5 b/ f9 c2 v4 P
    0                 Lower
    3 e6 s3 [& h1 ^! r) [0 K9 `: m) D1              Capitals5 u. h" q4 `$ V+ X1 }# P6 E3 o
    2    This Is A Sentence
    ! M  [* _0 X2 w/ a: J* q) _3              Swapcase
    . {0 q0 g' o' ^) Fdtype: object9 @, X- V' Q1 H4 g4 r0 J

    / o% T; E8 T1 i2 I7 Ts.str.capitalize()  # 句首大写! ?2 C. ]8 B) A/ ?. d
    Out[90]:
    ) G9 d1 e) Y, z( K. P1 E0                 Lower, l  I1 F: j7 Q. ]- r/ t
    1              Capitals
    4 d3 V$ E1 ?# L6 @/ {% D2    This is a sentence
    : k. r3 Q- L7 }2 O3              Swapcase$ |) X8 @. Q; M4 m4 O5 ]
    dtype: object: i6 T% b; e1 k: O" p7 [' f9 Z
    , i6 E4 P( A8 _9 C/ G
    s.str.swapcase() # 将大写转换为小写,将小写转换为大写。' T* G* i1 A  @$ H! n% V
    Out[91]: ' z6 _: n  k6 U( [; _  ?& Y& t  ]
    0                 LOWER9 d# a' t2 _( ?/ ^- K7 N8 s: E! j
    1              capitals
    1 x$ M7 |3 `; w2    THIS IS A SENTENCE
    # r3 [1 V" n* c& E. C3              sWaPcAsE& Y) B& T" S( i" s' r3 O
    dtype: object4 h) Y3 Y. T: y7 h' ~7 R! {

    % q0 p0 x% z5 w5 `8 as.str.casefold()  # 去除字符串中所有大小写区别
    / r7 f9 i9 Y7 _3 s0 {' h" l) O" j* l4 y3 U( L8 O4 [% H6 D/ P
    0                 lower9 ^3 @6 y# J: S, |
    1              capitals
    , l# P; K3 ^& Y9 c6 U2    this is a sentence
    ( s7 g) }7 a) b: b" T3              swapcase- X/ B5 f- W1 x

    - a0 p9 ~7 u4 ~  l7 ^1
    & O7 N" q7 M' P" p2
    . Y% C1 c% ~  l6 S  p3
    4 O" k0 U$ ^' f" b% y9 U  b' A4 U4
    , C, h$ W! M$ c( J! o% }( n6 ?5 h5- T- L8 B+ b+ o9 M, c4 ^
    6
    $ z1 v4 d  F9 g! L7
    ; d( A( n% Z5 I/ [1 ?  h4 _80 u& q: x7 u4 _! w, o
    9
    7 K2 }# P. [" \/ v/ H& n10" _( `7 o( q7 j
    11
    ) N4 y) _2 g9 U+ G1 V& }, O129 H- {- Z" Z6 V% U, N5 \
    13& R  F/ P7 ^: |
    14, x' k; A  F8 G& K& ~
    15
    , [6 Z' [7 q2 k7 R! z169 `0 X# |0 Z4 {
    17
    / v1 |2 `# n" g' \) A/ L9 N6 h18
    ! y: {4 \+ a- t  s19- L; J& _1 t  c& g2 K
    20" n2 v* T8 x$ {. D% D7 H0 S
    21- f8 H' ^# w; Q: |& j2 K& f
    22
    ( y! R/ }8 Q2 ^+ G" e23
    - ]) X' C. K0 n* z' L) U. A' d3 P247 ^/ }: w8 B! W- `4 K; i
    25% e2 a. W! |: T# m. G; X, \
    26, ~) ?7 Q0 u& j; \% Z! J
    27
    7 |5 g8 s7 T+ c7 @# W8 {28
    9 y5 |$ t1 B3 q" z1 Q/ W; [: U% q29" F4 i; K4 ]; T2 C, }1 `% e( X2 ]! f
    304 a1 M' D$ l& X1 u0 |
    312 w0 F7 p! v2 A2 j7 }! ]! z
    32
    ) N: d, M# K2 ^+ `% P# a+ ]1 V7 r( `- F33
    2 }$ i" C* R/ C: _" @( _342 n4 H+ ?0 ?8 W0 d4 x) a
    35$ g4 h6 O6 w! d3 H/ f
    367 k9 a' E! g4 D3 @9 c
    372 Y9 M3 K) R: r* c; C$ P0 J
    38) S6 D% b, S) m* T
    39
    ! C0 ]! r3 }( ^; F+ l7 e) |# ?40
    # |' M, r0 i- U$ O, }( l+ L7 @3 ~; h0 g41
    , g0 {7 ?2 T( ^42
      {% f1 e' ]6 o8 U: i1 M43
    & Z% v- o) u7 e# ]/ L44* w" F5 A1 }0 t7 Z% ?9 m
    450 _8 X( q7 @. {. z6 M
    46
    7 x, v) I# h' G8 U, U* ^) G47
    - |, C- j1 T9 @# C' R48
    6 ?8 l9 k* p* z, ~, g8.4.2 数值型函数
    ' s7 {9 [( O7 w5 n/ y: A  这里着重需要介绍的是pd.to_numeric方法,它虽然不是str对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。其主要参数包括:0 x3 J" c0 ?7 u1 ^) c$ F
    7 U1 F+ R7 I8 N; {7 |
    errors:非数值的处理模式。对于不能转换为数值的有三种errors选项:
    ; d0 G3 X/ L0 j* s0 ^raise:直接报错,默认选项, z+ a- j+ O& A
    coerce:设为缺失值
    : ?3 N7 c+ [. x9 jignore:保持原来的字符串。
    * p8 D, h! j! k7 b. n, G# \- ?4 ]downcast:转换类型,转成 ‘integer’, ‘signed’, ‘unsigned’, 或 ‘float’的最小dtype。比如可以转成float32就不会转成float64。- \$ k5 P8 p5 ^+ ~
    s = pd.Series(['1', '2.2', '2e', '??', '-2.1', '0']): W( `: w, K- L- D8 m3 o
    ) C' e$ p! J: j% F( \$ E
    pd.to_numeric(s, errors='ignore')* e8 l8 F1 X9 q& K% u+ M
    Out[93]: / Y( y7 V4 l! `8 N& q1 G& [; E$ ?( c
    0       1
    , s1 D* o7 z9 g5 y" z4 n1     2.2* `& T& A! h# j) M; W4 M* d
    2      2e
    " ^# h. c/ m, D6 o# z) b8 K3      ??6 F2 P( S; n1 H2 ]0 r: ?/ Q4 X+ d" ^
    4    -2.1
    6 p  J* b: `/ ?5       0
    1 H% ~- o1 W& \# W0 _dtype: object
    6 }# F. t. G) \
    - t8 w; P9 [* u  k$ qpd.to_numeric(s, errors='coerce')
    / }  E/ C" d* H" vOut[94]:
    0 T& y# V5 d+ u1 i' j) @0    1.0
    " t+ E: z5 C2 K- u' t  Z1    2.2  T2 Z6 c; t& q2 h
    2    NaN; T) W# w  k, W5 Q
    3    NaN
    ; N$ [; E  K$ N" N1 ?6 Y) `: `; Q4   -2.1
    , x& s4 n8 i, p7 X8 V2 k5    0.0. r8 {' S. t5 t- i& F
    dtype: float64
    * D8 _# t+ V' C+ Y/ P! N4 R! q- I( r
    1" Q+ m! b5 }2 c& }' h; u: P# I
    2
    - n. [2 n  a- s5 w3
    2 n- F: D& v+ j0 V/ i4% K$ q+ A. U& h1 o
    5, f' x# n% t, A  d/ Z
    6: c( {" b4 @& G9 E5 ^
    7
    4 A& a5 \# v2 ?' t, \1 T. ^8
    . Y+ d& c: E, k; m: d9, l% a. N5 v2 F, N& U. z5 Z# N' `3 R
    10
    7 c9 s/ f5 E; h11  }8 T+ U: }) O
    12; D  {# e7 j: T5 A6 [
    13) e2 h& G+ ?1 z# w" {( N8 M) ?3 T
    14
    " P$ o( \7 s" q5 w& {2 d- y15
    5 \* p: b" O9 b, B6 W& ~166 L% [( d, t0 C
    17$ [% `* Y1 m9 y' N! _
    18# Q  ]: R' }9 `6 q
    19
    ) f! {0 n$ d* B% m20
    1 J8 b& D: x6 J, f21
    * h; d4 t+ m; k8 R6 a  v0 A" t0 |  在数据清洗时,可以利用coerce的设定,快速查看非数值型的行:
    4 F% G, b; M% m5 ?9 g8 j
    8 e$ Q2 w2 H$ i* H. ?* q' Q6 j+ [4 Ws[pd.to_numeric(s, errors='coerce').isna()]
    , Z( ], N. h: i8 ]  ]Out[95]:
    + I) X! K5 W& K& J8 j. }2    2e" o' ^4 z. ~& F9 C9 W4 n
    3    ??0 j# X4 G) g3 [& m% s  C
    dtype: object
    / V1 {; V, v: w( b6 D9 f1- |  }' i4 A4 h) d0 S
    2% s4 g# h. O5 N+ a4 |$ M0 n
    3
    + p- ?$ s( h" b& ?1 I6 e, H* J3 s4
    5 c- @8 t. S( C: e& p5 d, D6 F+ r! b5
    # W9 A/ \/ V4 d0 D* \8 ]$ g3 q8.4.3 统计型函数; |$ p, P2 m! i# s6 z3 k$ j& l: [
      count和len的作用分别是返回出现正则模式的次数和字符串的长度:* V+ a6 X* K4 e. A' B3 K5 p' J/ e

    ( T3 x* T0 v, Y- o3 b7 [s = pd.Series(['cat rat fat at', 'get feed sheet heat'])
    , t# k* ~. O4 t) y; P4 Z2 @* M6 C7 B! P3 m' a0 x/ E( u3 E
    s.str.count('[r|f]at|ee') # |左右两种子串都匹配了两次
    : p$ u4 ~" k+ e$ k- v+ G  POut[97]:
    - S+ H8 L  F# g. M0    2
    * }# Z1 V. n3 l, P/ w1    2% A+ q/ N5 p% ]! k4 C$ f+ m8 E
    dtype: int64( M1 \; p  w9 x. q
    7 V. ~5 R- b# P& v4 @  d1 X5 e
    s.str.len(), I% J8 j5 d) y/ S- d% R
    Out[98]: - F  S: a6 V0 Z$ G3 X# v
    0    14' [8 _0 y3 Z0 e  L. \4 @* m" D, @  m
    1    19. j/ m" y9 \* F
    dtype: int64; G# I! p+ m7 v$ X: z. I8 s
    1+ n1 Z2 V$ c& p0 O
    2+ k" t2 b5 E: ^
    3
      n, Y; g0 Q% b4
    . a) C2 Z' C9 ^" P2 V4 [5
    2 F* s/ v) b# ~( ^9 }# K% K6
    # O6 D) x/ D3 N% [7- _: k6 I6 g. d' {* c2 P1 z$ A* y
    8
    ' b5 u# K1 f2 g$ ?, ]( P7 `# t9# D, x. S' ?. c2 F) r% C
    108 c2 g/ i  G5 X; F
    11  T1 Z7 C; i' `8 R" H1 k( V
    12
    4 {" n& i3 f: Z" @! L! k13
    . q* f- v: Q$ {. w& t8.4.4 格式型函数3 p5 l6 G  i4 n
      格式型函数主要分为两类,第一种是除空型,第二种是填充型。其中,第一类函数一共有三种,它们分别是strip, rstrip, lstrip,分别代表去除两侧空格、右侧空格和左侧空格。这些函数在数据清洗时是有用的,特别是列名含有非法空格的时候。- ^; S: I$ A1 ~& G4 }# @
    3 R! D  Q6 \: {+ Q- S3 u: g
    my_index = pd.Index([' col1', 'col2 ', ' col3 ']): W% d8 I. @" q" Y# y0 G
    $ m# G3 }2 |5 i
    my_index.str.strip().str.len()/ i$ K1 H" N8 V$ r2 A6 p
    Out[100]: Int64Index([4, 4, 4], dtype='int64')
    1 ?* j7 ~0 g: h, W* M
      i$ o9 @9 }0 z0 bmy_index.str.rstrip().str.len()
    ) j$ r- y8 `; Z' }; R- C* NOut[101]: Int64Index([5, 4, 5], dtype='int64')
    / W: q: J9 A2 C4 |, N2 I0 M: y# K- k$ ?, _/ w7 A
    my_index.str.lstrip().str.len()
    . B2 R5 q2 e+ C/ [# l( POut[102]: Int64Index([4, 5, 5], dtype='int64')* j7 h6 d( z, W' d3 X
    1
    + U$ F. Y+ m5 s/ ^2 K' C  A, c' n23 a( D; j: u* B( u
    3# e( S. h  B+ q: [
    47 f0 n8 ?1 F4 A( T
    5
    , C1 H/ d) M: v2 \' I1 s/ Y6
    6 p3 K" ~$ u1 S5 C) p' R3 Y  T7, d5 M% U0 U5 q8 U  j
    8" O- d) s0 p: ^! ?' x
    9
    7 G5 n0 O6 }0 \5 _- U10% A" ~- ?2 e+ `/ _! j
      对于填充型函数而言,pad是最灵活的,它可以选定字符串长度、填充的方向和填充内容:! g: ^) R" e5 M) ?
    3 J+ ^* ~$ P! [- `
    s = pd.Series(['a','b','c'])  {8 i' {% \$ R$ p% m

    . a( _( h: T" @* `& t' L+ s: k+ Ts.str.pad(5,'left','*')  C1 z0 U: w: S
    Out[104]:
    8 C" E/ i9 N' K& A7 _0    ****a/ H8 ?3 Y" I6 Y7 s* ?0 \( C7 L
    1    ****b
    7 P* k) i, u" N: l6 d+ t( v1 |2    ****c) d: ^: G1 {) [* M- A
    dtype: object
    ( S: x( R" Y; q7 p2 S. Q6 i0 [- H% N! t# V
    s.str.pad(5,'right','*')
    8 M/ ?! ?1 d( T1 X, oOut[105]:   s( T; v0 y7 \: Z; T
    0    a****7 A0 s* ~5 Y, A
    1    b****
    0 _. m$ Y' ?8 b6 u2    c****
    4 ^6 t7 i/ V2 r- F! f* K7 d0 d; [* gdtype: object
    ! _& N! z0 q4 k- q0 l5 z, m
    ( B% U2 U9 G7 [" Cs.str.pad(5,'both','*')4 ^: g1 j1 I; m* u
    Out[106]:
    1 Q9 s7 @1 L8 A; q5 O6 |0    **a**" J& |4 e2 d; M$ X
    1    **b**+ P/ k/ K' w/ [+ p
    2    **c**) s! B- N/ S7 f4 s% p
    dtype: object
    1 ?+ e, y7 t2 i+ [  v
    / d* N. u( D2 W1 s5 y10 S# f5 L' v4 t$ y! d7 k+ z
    2. L) G" v' y7 I/ w# h3 w' C
    3
    0 |! ~8 V( K8 g4
    $ }, M( h3 [* Q% a  ^50 F& ]# X2 p3 K5 W( @
    6
    5 U5 }6 x! [; E8 b3 R) A4 n74 R$ [+ f$ W) h9 x
    8
    $ ]4 c0 x" I, z! ?0 n0 M9
    3 A% M4 [, f2 J7 d102 `' K: l2 E5 n1 c* b0 Z  E
    11! C" t8 l3 c/ P5 P3 [& t* n
    12
    4 J+ v% i2 F* O1 Z6 {& F: l, ?13
    & @+ j; W& N- d! K) I147 l1 U. h* S9 l, Z7 ~% f, n7 E
    15
    / ?& p) \" m. H/ I! `16
    ' o9 }7 n" b  L, W# Q7 E17
    ! H% r7 s) J/ C  N+ y# S. v18% t0 a* `7 P1 c& e
    19
    3 m3 x/ _; z9 v2 i& G20( }4 S% M' G' C/ Y0 p
    21
    ; N  W- r0 g: n* N1 W5 v22
    * q- g8 Y# C) F' d/ b; c7 |. A  上述的三种情况可以分别用rjust, ljust, center来等效完成,需要注意ljust是指右侧填充而不是左侧填充:
    3 r4 Y! _& g* T0 n# M7 |6 ]7 Q3 ^( I+ {: e0 W) Z' s5 x
    s.str.rjust(5, '*'): V4 i4 @4 @9 ^' o. o( A) B0 g
    Out[107]: $ F3 b. m' o2 o# A
    0    ****a- l+ [  i8 E6 a' R; v/ Y
    1    ****b
    - Y( V$ h8 o. T2 d) @  b3 ]+ R2 J& t1 T. v2    ****c" B" R, b. C; I) J* S' {
    dtype: object8 ^0 c+ l0 N' m/ b9 B

    ! C3 M$ F! x! }  f' ~5 T: f) s+ As.str.ljust(5, '*')+ U1 W7 W& V+ A, k" g
    Out[108]:
    $ a% H3 k, [$ `+ c+ Q+ \0    a****6 V2 b6 K  c: ]5 u( D7 a
    1    b****
    8 i/ x. g" f3 C$ b7 f  ]; w2    c****/ e  a. U7 v2 g& ]2 C7 R
    dtype: object8 }3 d6 D% [/ a8 _( O! J: W

    $ A8 N, V' ~; H( H6 rs.str.center(5, '*')
    ; y% V! X) R: l; l; w. J6 fOut[109]: / F/ S+ T5 x6 x5 |$ p
    0    **a**
    . I! r/ U. @0 {1    **b**
    4 N: F+ z. ?. \9 K# p: b2    **c**0 O! N* d* C$ b( H4 [( D. v
    dtype: object
    2 `* D1 C0 t: D' g, g# q
    % I# L6 i8 p% H  V0 h( S5 v1( V5 x' G( `( J. Z( n# J
    2
    + F, r3 n' K5 Y/ B* z  t39 K) O, o% J' ~/ l
    43 C$ A1 s% S! `# w; i
    5& C% e8 {: X8 y& K
    69 _# U' d' [5 s
    77 X" [" ~6 r6 Z0 B3 m! e! g
    8
    4 ?# i( F& L+ q2 B97 f' B: `( `6 T+ u8 I9 h8 C
    10& t' O  ?( ?( G! ?# j. {
    11& J. N* n6 S* G( O9 y
    12" [9 a& I- g' q0 {
    13
    - w" `* Z+ W; d& b3 I, `14; e+ J9 q* [+ y' d3 i
    15
    ( D- z$ t+ l" }$ T5 w& `16! ?  A% B' v0 E0 r; _9 i
    17
    5 b, S! ]3 p$ X18
    & }/ F  [( L! d# R19
    , y% |; f4 M  n20
    3 D$ J! Q# a9 B- N* \  在读取excel文件时,经常会出现数字前补0的需求,例如证券代码读入的时候会把"000007"作为数值7来处理,pandas中除了可以使用上面的左侧填充函数进行操作之外,还可用zfill来实现。
    $ {, B! U$ [6 P. s1 f* T
    ; ^) {8 P$ r3 f- A# L! ^, D( ws = pd.Series([7, 155, 303000]).astype('string')" s3 U$ B& E; U* T2 O+ R

    1 \1 j9 `6 C  b& F' Ps.str.pad(6,'left','0')
    1 N* s1 m4 \+ i( `& {! d3 mOut[111]:
    % t4 E4 G& I6 E6 o* @0    000007
    " C% K& T2 g  v6 p3 T: G$ b0 @. m+ Y1    0001557 o8 A# ]" L0 t5 c
    2    303000* J: u3 W+ e/ x& C9 _
    dtype: string% i$ A6 w+ ]9 b* X9 ~/ r9 b
    8 T' F4 a9 g+ D& {. E  d! A
    s.str.rjust(6,'0'); Q; u3 m/ p' B  I7 x$ N" r$ G3 h
    Out[112]:
    3 l2 z/ y5 _& k* G/ z0    000007
    % n3 w/ F4 I' X! d1    000155
    - T% Z$ U1 {" s7 ]2    303000
    : u$ B% ^* t. e; L9 ?dtype: string
    9 k5 a% B% {, L+ @! @/ e4 p% U
    " D2 e' s6 \8 N- \2 Gs.str.zfill(6)
    5 L- g1 h% a' y. v7 r0 |$ j% MOut[113]: & V8 u) c+ x; Q; @0 D, `$ Q
    0    000007
    2 [6 J1 ]  {* y+ `7 l1    000155% l- R3 X- X! K2 D
    2    303000- h: c# A# ^4 T, A6 {+ |' C
    dtype: string6 U3 z$ M9 K: O5 i4 x+ {
    : x) {/ \& ~/ M9 L$ w2 X
    1
    / `, ?& X( l8 E' R5 y23 y2 ?- `1 N6 E. F% a! e
    3
    7 p/ `+ w( S0 u0 R' N: c- n9 `) H47 W  H( X2 W" }, m6 z5 z
    5
    ' D; x# Y' G& A  {6
    7 @. [) C% v  V8 v. V7
    % P* j3 v. g$ ?% x3 N7 I& t- I3 |9 k4 \8  ^& R& X4 A4 v( C! f5 d
    9
    / \' r* P; I9 d0 F. `% R10
    3 C1 ~$ v/ a, T* }11
    2 U) N$ }; c6 |) p12& ]1 I4 t. P, }
    138 E# R& |9 D5 }- J7 A% M
    14, \! |9 l) ^4 y2 y5 i
    15
    % X) [4 a8 e# T2 a8 I& |! @16
    ! `+ F- u6 z! U- z; N: ?9 q& c! K17# ?2 [0 V5 k4 ^/ A% j1 _) j7 \
    18
    ) Q3 t  O- W7 N/ ]% z/ k191 S- P6 P. z( z3 U! j/ Z
    20
    / P1 Q1 T3 v8 f21
    ! ], I& A( s9 _0 U$ m22
    ' _7 y" G- A4 l7 d- v- f1 A8.5 练习
    % Z9 R7 [( d4 ~; ?Ex1:房屋信息数据集
    " c: M: y& N* g! E1 J现有一份房屋信息数据集如下:  X' d( O7 O+ L- r' i7 y- v0 f6 l
    ; Z2 f( v: L+ e) I7 q( n4 x
    df = pd.read_excel('../data/house_info.xls', usecols=['floor','year','area','price'])
    / C! W1 M2 |, udf.head(3)
    / i: m; {4 B: z) ?- \  tOut[115]: 1 [4 M! Z1 p" u  f8 ^7 _
          floor    year    area price
    $ k( |. C; C( K' w( \0   高层(共6层)  1986年建  58.23㎡  155万9 R" {" T6 [; J, y! d4 t( G' D
    1  中层(共20层)  2020年建     88㎡  155万
      W+ r; U- G$ h3 H$ k. N2  低层(共28层)  2010年建  89.33㎡  365万
    1 M& f% ?: m% N; Y% w3 v1
    ! L7 `8 Z0 G3 Q# M' a$ W2
    , E3 ~- x8 s6 @5 i  z6 g$ m3& q5 k) O3 F  j
    4# X' q  n$ ^. [1 X1 x
    5' \6 U$ a- d+ h9 i! m" B- _
    6
    % Z9 H; e* f. _6 t' M7: s3 |8 h8 ~! X) `' E# S
    将year列改为整数年份存储。
    + d9 O8 G1 d% V" U将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。& |* g& M$ Z: n- U/ T
    计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数( z3 s9 P  |* o
    将year列改为整数年份存储。
    8 o2 K6 x4 k$ @7 t2 J# ]"""* Y. y+ T! E3 a, ]! |8 S) C
    整个序列需要先转成Nullable类型的String类型,取出年份,再将年份转为Int64类型。% @" h9 Y3 M) H1 A+ H- a2 H% S
    注意,转换的类型是Int64不是int,否则报错。即使astype加参数errors='ignore'跳过缺失值,
    ' L: J8 B8 q+ F8 J8 i# R9 @转成int后,序列还有缺失值所以,还是变成了object。
    " H8 R- x& g$ {. a) Q而整个序列转为Int,就还是Int类型,缺失值变成了 pd.NA 。
    " \( o2 n0 p0 E- z3 _"""
    , v5 t! Y! Y# y% Gdf = df.convert_dtypes()* @+ M: S# c* D8 f9 p5 t& t' ?9 ~
    df['year']=df['year'].str.replace('\D','',regex=True).astype('Int64')
    % I! z) P8 e  i0 l3 h, t7 Xdf.loc[df.year.notna()]['year'].head()/ i- O; P, R. s. _% W8 h9 Y9 H
    * Z% Z; r0 D; f
    0        1986
    6 U& w, ]: h2 }* N. [: [9 w1        2020
    ' p: p+ x& Y4 B4 Q$ V# f. S* T2        2010
    2 I( ^( ?- A( h- K7 L3        2014
    ; ]- g5 o; E9 a2 W! I4        20153 s+ X4 e' m8 R& B0 C
    Name: year, Length: 12850, dtype: Int643 `8 ?8 N) T+ g- Z5 r: |

      v# c5 r; J1 A, C1 n) x1 ~1
    # m4 t! X" h- T2 v1 C. t9 h7 e2
    & h# }. Z, Y  Z: O7 n( x3
      t# ~! R3 s/ E3 y+ }  W1 g6 y4' H4 T) f7 e4 R  Y" b
    57 n4 L$ L8 B! L9 ]& s# f" U
    60 \  U' x: z' T0 t" D) W: W
    7
    7 F$ e% W. _& [  V8, r- E, r& S# S9 D( b8 Z
    9% J. l, G, _' z$ z
    10
    * S: |. e7 o7 D5 D# Y11% [8 _1 I* W  ?7 }+ c
    12
    9 \0 U' @( Y' F, s9 f9 m134 e  x9 @( k0 i8 ]+ F0 t) `
    14( }5 |% V, e5 z3 ?2 k# Z
    15" V; s) v: E! n/ W, ~9 M
    16( ~9 r6 N$ S# C2 G3 l
    参考答案:, s# y  K' v( J7 W9 K

    ) `8 Y# {7 ^& f$ o4 z8 q不知道为啥pd.to_numeric(df.year.str[:-2],downcast="integer")类型为float32,不应该是整型么
    7 Z4 M5 `3 z$ B8 D7 ]8 n! L1 u& c/ [/ p! Z8 I0 \& F0 b3 Z
    df.year = pd.to_numeric(df.year.str[:-2]).astype('Int64') ) z& f: ?, h' X0 v3 k0 ^( f
    df.loc[df.year.notna()]['year']
    3 t4 k9 n0 a# {: U- _- s+ H# r& J1
    2 s% [: S6 ~8 V% \% w2' d% T) \. y$ z
    将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。
    " n7 M% l& d. Qpat = '(?P<Level>\w+层)(?P<Highest>\(\w+层)'+ f' e  ^' K) H+ b& W
    df2=df['floor'].str.extract(pat)  # 拆分成两列,第二列还是(共6层得形式,所以还的替换一次/ \% z. Q9 j* X1 n
    df=pd.concat([df,df2],axis=1).convert_dtypes()  # 新增列拼接在后面,再次转为Nullable类型
      b1 K3 _( u1 T6 V% [& edf['Highest']=df['Highest'].str.replace('\D+','',regex=True).astype('Int64')              
    5 k/ Z$ A; L* u2 \1 Rdf=df[['Level','Highest','year','area','price']]5 }7 ?3 u9 b, o. O$ U! ^$ u
    df.head()
    8 a. n: d3 D  M3 V. B
    " s% N- q% `- r' t% S   Level  Highest        year        area        price9 r; t1 ]2 O8 s# K! ~; E* }4 X
    0        高层                6                1986        58.23㎡        155万
    ) t- c9 c* N5 X; Y; P1        中层                20                2020        88㎡        155万2 s5 q  J2 \2 G4 [
    2        低层                28                2010        89.33㎡        365万& J* e  A' n' l+ W  u9 q, d+ j
    3        低层                20                2014        82㎡        308万
    ( o: |4 f+ V+ s$ c8 g4        高层                1                2015        98㎡        117万3 |! d/ [6 q! C2 h
    1" P: K0 I9 u+ V2 G4 W! S
    2
    0 Q- B$ `- T+ ]3
    : v3 m; R6 X. f6 }1 m4
    + e( y. X/ l  W+ V$ B% e2 j9 G5+ D: |  U9 s# n4 X1 R# Y
    60 ~: ?  y9 Q' v3 Z: K
    7
    # L5 U+ y/ v* [* q, ]" Z3 z8
    - c, _; L- T/ S/ O2 ^( {9$ W3 I7 ^# g' n  }! z
    10. N9 h8 i9 O- n! a- u
    11  h* S) W- q# j- t$ ]
    12
    ( ^/ s- L& s1 z5 g, B! \13. @6 z( x3 h: y5 k  k6 z- v
    # 参考答案。感觉是第二个字段加了中文的()可以准备匹配出数字,但是不好直接命令子组了
    $ }6 @! L: Y! L; @+ O. dpat = '(\w层)(共(\d+)层)'
    " d7 A4 C5 h7 w! W& }4 Gnew_cols = df.floor.str.extract(pat).rename(3 A% D, f' G2 \6 R+ s% q
                        columns={0:'Level', 1:'Highest'})
    ! k9 f7 z  A( P0 S: A; ?; J1 Z0 B) N) ]3 _" {& a. u
    df = pd.concat([df.drop(columns=['floor']), new_cols], 1)
    / }7 x' P  Y0 Y1 o0 O: M$ Ddf.head(3)4 ?0 z0 u3 {2 b; e$ L3 M) |  A
      t, g' n7 O( \3 q9 Z
    Out[163]: : `; U1 s% B* m6 d2 g6 C7 h% I
       year    area price    Level Highest9 G, z; z+ }. s8 b2 g4 L
    0  1986  58.23㎡  155万    高层       6
    : W8 m- W  D" e. c' ~1  2020     88㎡  155万    中层      20
    ( T7 J3 M" y- C5 h+ j2  2010  89.33㎡  365万    低层      28* {6 D( c- m; s
    1
    8 [/ M3 h( _7 v/ V3 j* I" V27 P1 L$ C# k# r  y: K$ A
    3
    7 z' P! n4 f- Y4
    6 {% h0 x6 p7 d0 }58 f6 G  C- Z) o2 ~/ K
    6
    : _0 U' s9 F; b% n. O7
    # Q+ `3 s1 H5 M1 |; m* g8
    5 B  X% d9 w/ j* k9% B2 N" Y( ?2 C! H0 ^
    10" z6 y; o; v* a
    110 c' o! x' |/ l2 w* T% {
    126 v' y; W7 V9 G; D9 w/ |
    13' {/ T5 ]0 i1 U
    计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数。& _8 F- N5 n, C7 s
    """
    : ]7 R0 V) V4 F/ _( w2 T- q9 L* Istr.findall返回的结果都是列表,只能用apply取值去掉列表形式6 U+ \* Z' f" c/ G* S4 g/ M! I) A
    参考答案用pd.to_numeric(df.area.str[:-1])更简洁8 d5 {8 P% }- s2 R. E# M
    由于area和price都没有缺失值,所以可以直接转类型! t2 B; B  S3 ?- Y
    """
    ( }5 c( M5 |1 B# e0 c1 Kdf['new_area']=df['area'].str.findall(r'\d+.\d+|\d+').apply(lambda x:float(x[0]))5 `# ~$ w, s, H7 _  c
    df['new_price']=df['price'].str.replace('\D+','',regex=True).astype('int64')
    5 @7 u& ?9 X) N$ h: xdf.eval('avg_price=10000*new_price/new_area',inplace=True)
    , I; d% E: U7 X% ^: \2 h# 最后均价这一列小数转整型直接用.astype('int')就行,我还准备.apply(lambda x:int(round(x,0)))
    / ^% f, ?- o! m2 E# 最后数字+元/平米写法更简单7 P$ G7 {1 q- W2 |/ x% t
    df['avg_price']=df['avg_price'].astype('int').astype('string')+'元/平米'
    $ W) j8 s  R4 G9 y( Xdel df['new_area'],df['new_price']* \1 I' ~% }! q8 H  @
    df.head(), A  P) r3 M6 s
    5 ], D+ I, S3 |. |: m7 w# D- d/ q
       Level        Highest        year        area        price        avg_price! y0 c1 }& U3 w* J1 W7 J; p
    0        高层                        6        1986        58.23㎡        155万        26618元/平米% A* z! g! H+ A3 m
    1        中层                        20        2020        88㎡        155万        17613元/平米
    + k* i% x1 V, }5 F9 ]# E; E2        低层                        28        2010        89.33㎡        365万        40859元/平米
    " D# E6 ?. h' ^1 h( m3        低层                        20        2014        82㎡        308万        37560元/平米
    2 \0 d( v$ K2 u: }4        高层                        1        2015        98㎡        117万        11938元/平米
    6 M$ G8 o4 b  R+ g& |1 @7 X- R3 \
    2 F! [7 e" O5 I$ B! g7 h5 I11 G! Y7 v6 `$ p- O! M, m
    2' n9 {* ~5 d: B" {- H" K5 V
    30 Z+ H0 A" K/ [- T: v) `3 j
    4
    9 m% A7 O* t4 q! z50 N* o' q$ a; g
    6
    9 @  Y) }- V2 M2 s0 [7* b( O+ L8 d& l' S
    81 T, \6 _7 ?/ e6 n0 v5 T# v- T3 f$ V
    9
    , m6 ?) M8 [. U6 T- D% E10
    9 f7 |! O) P' B: Z- n11
    , ?. E6 ]8 N2 i9 W7 w12
    6 t, Z# I$ J' |( e- s; [' h13; Y. a. \: t4 ~3 j: Z! i( _
    14
    8 o  }- G: Q3 B" x# j; z15
    1 G4 L6 ~0 l& k3 i- m4 N& x16; V  m7 y" `0 `3 [# h8 h
    17
    & [4 i/ R7 B4 F4 {- {- E+ l18% I! A* l9 |- G2 y( W6 i  q1 K/ q
    19
    # a& f! a! t0 t; ]# I1 A4 E/ r2 o. }203 f1 M0 _4 W+ T0 }2 k8 L/ o* N# y
    # 参考答案
    ( g6 M: d0 y- J% d+ \s_area = pd.to_numeric(df.area.str[:-1])& u* n, ^/ e6 a: ~/ j6 _  a% U
    s_price = pd.to_numeric(df.price.str[:-1])# _7 n! r( I2 E
    df['avg_price'] = ((s_price/s_area)*10000).astype(7 @3 j7 `/ |) S& w
                        'int').astype('string') + '元/平米') w0 ?' `8 p; V  a0 V" \- e  d
    / j; E3 p4 O: [$ L9 y2 _$ H5 `1 b' t: F
    df.head(3): W; z# t7 q9 @  {* k
    Out[167]:
    , n2 g0 B  T* d: Z( P  m/ `   year    area   price   Level Highest  avg_price
    ( M! h& ~% ]+ ]0  1986  58.23㎡  155万    高层     6          26618元/平米
    ! u5 B% w! r. w; V1  2020     88㎡  155万    中层     20          17613元/平米
      ^7 M0 b2 s2 ?' l, s+ y2  2010  89.33㎡  365万    低层     28          40859元/平米
    7 Q8 H8 B5 f& j% y1 @$ C1
    . S* \" `0 K) v9 A$ J7 _: s' R! A2% ~" M% S' L2 j* \
    3% w1 d/ e+ S0 G% z! {3 F5 b
    4* F" M7 [6 Q0 a2 w7 {+ z0 e
    5
    - _. q' a+ B; M% b  w67 P' n0 U, A8 m1 s& P5 m+ Z
    7+ h2 m$ Y* c2 u- y, c( s$ ]" p8 @& B+ R
    86 _0 Y. T$ L" {+ y
    9
    # ]& S. |5 U8 B4 u( ?' h10
    % ~  @+ A# M7 |. x11
    $ D, ~( ]/ e  F6 D2 u' O12( T) W1 E  ]& m: ^
    Ex2:《权力的游戏》剧本数据集2 ^7 o1 V. x5 J  G9 M
    现有一份权力的游戏剧本数据集如下:
    9 p) Q: p+ e3 y$ x* i
    3 ?7 f" H5 Q" Idf = pd.read_csv('../data/script.csv')
    2 C; r1 o; i; i8 R- h+ pdf.head(3)
    ) Y: W: y( A2 n, ~6 v( D0 ?& f7 g9 O3 \. g
    Out[115]: 8 {- S& T. v6 X
    Out[117]: ! f0 d, S3 y8 F+ c/ ]" T0 M" S2 Z
      Release Date    Season   Episode      Episode Title          Name                                           Sentence
    # U8 z& ^8 ~& ]* k) e" |0   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce  What do you expect? They're savages. One lot s...: i' ]+ y5 l2 t" @6 q3 M
    1   2011-04-17  Season 1  Episode 1  Winter is Coming          will  I've never seen wildlings do a thing like this...9 u1 b/ S& }: ^! |8 Y! x
    2   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce 7 n% d6 d( o# F  D* ^4 Y
    1& r) g; v8 s. b$ K& [3 g" @
    2
    ( O4 u; Z( L& q$ N3: W/ i# Y0 H3 p+ U; v( U
    48 \- d6 A  _. W: b; S2 m: o" M
    5
    6 E& x/ J* l1 O$ D' S' n6' u9 M* E1 j# b, h) b* ?1 k
    7
    8 L" G& M. y# r% Y8& v/ S, L( D$ }0 ^  w
    9
    8 v% `) g; |9 m/ r! f  f计算每一个Episode的台词条数。0 O" i  h3 i# z# E
    以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
    9 J9 r3 ?7 W' J% m若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有 &#119899; 个问号,则认为回答者回答了 &#119899; 个问题,请求出回答最多问题的前五个人。
    4 @6 |+ g8 y7 h7 m( x* Y8 `计算每一个Episode的台词条数。
    . ?$ W$ G2 N7 F" s3 w8 _! M, M# C7 Z8 Idf.columns =df.columns.str.strip() #  列名中有空格9 m) S* ^( [$ ^4 I
    df.groupby(['Season','Episode'])['Sentence'].count().sort_values(ascending=False).head(). \' r1 ]. m9 Q# n5 B
    1 d) R# B+ Z7 Q" _) n
    season    Episode  8 C9 B  o: b8 r/ V7 t2 ^
    Season 7  Episode 5    5056 I, p- N2 {, Z0 g2 F2 g6 X* g
    Season 3  Episode 2    480
    4 z) m" c$ `$ gSeason 4  Episode 1    475
    ) c, @/ I, j% n/ `: |+ ]! NSeason 3  Episode 5    440) K- v- r: p0 R2 H# K5 I9 ^
    Season 2  Episode 2    432
    % U! o& n5 Q/ ~' n6 x: @5 P1% ~9 g  {* w  S( K
    2
    - X- i) N( y6 V& f! c3
    : G' t0 B3 i! D- H6 _% H3 J0 s% Y4
    6 N# |& t6 _( a5
    8 {5 a$ a+ ~0 Z2 j& `0 c6
    " Z* @% m1 q2 d) g8 }" \) d9 N, U1 i8 b7( G% \6 M" B& i  F3 W/ A" v+ D. \3 \
    8
    ( h. j9 G9 j0 b4 l% u- w* p/ ^9
    9 |7 P3 n% c& ]' u. R4 {, t4 B以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
    1 p/ `% A3 F. l& J3 L# str.count是可以计算每个字符串被正则匹配了多少次,+1就是单词数) x! o  i6 [* d5 M& Q2 ^/ v; M
    df['len_words']=df['Sentence'].str.count(r' ')+1
    + X; E! ~/ }3 R2 Rdf.groupby(['Name'])['len_words'].mean().sort_values(ascending=False).head()
    % s" D' Z/ Y8 \/ _" s# q6 x* ^* O) B$ {
    * V. `0 V! D% R- A. J2 o; e" JName
    & v$ R- p0 O: U9 dmale singer          109.000000
    9 `1 t( b5 |" t& I9 hslave owner           77.000000
    0 g" n( P8 N, H- {, R! T# h& Lmanderly              62.0000003 J* w& F7 F0 j0 M. b$ l* p
    lollys stokeworth     62.000000
    6 v7 K2 [- W. N7 P" Y5 e* D3 Gdothraki matron       56.666667% m! z5 h: A4 ?
    Name: len_words, dtype: float64' @: A' z, N' E# E* X% z4 W
    1
    5 P' p& \6 h% k0 N6 \2
    ) R. X+ z# J: x3
    , m, u  z) d* r0 D4
    3 L6 \& Q- D* V; Y5
    - V7 N8 y; C. D6
    4 S. H9 ^, i' ]3 p2 h7
    2 g0 s! ^0 t" A! m. Z82 P9 N: M: j9 P( a: Y
    91 y- D: F3 p6 ?) W
    10
    6 ^" w7 r' ~3 X% q! B" x1 a9 |# w11
    1 T7 j+ t0 ~' E$ T$ Q7 c  S* L若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有n nn个问号,则认为回答者回答了n nn个问题,请求出回答最多问题的前五个人。
    / [( P9 r: K3 n& c+ y% Adf['Sentence'].str.count(r'\?') #  计算每人提问数
    + \+ ~7 f/ I! r: Pls=pd.concat([pd.Series(0),ls]).reset_index(drop=True)# 首行填04 G9 H' o% e7 O% L- z
    del ls[23911] # 末行删去3 l4 {) I/ W/ b+ J7 _" o
    df['len_questions']=ls
    1 H, D/ H$ N; A& w! mdf.groupby(['Name'])['len_questions'].sum().sort_values(ascending=False).head()
    0 F5 C( I+ e, h1 m" O0 `$ O. x0 g/ B/ i  Y, V
    Name" D" V: C- s& U7 U9 H9 \9 a1 o
    tyrion lannister    527
    2 U) `9 e) F% q' f# M2 Tjon snow            374
    6 v8 L) Y+ `  v% O& r0 |$ G7 Djaime lannister     283
    # a2 v, U3 I( F  Y  Carya stark          265
    , H4 ~- ]' D1 A6 ~cersei lannister    2467 I# s8 W. w+ @$ q5 ]) n. ?
    Name: len_questions, dtype: int648 ~, p& K5 ^/ Z

    - X: G  f- \; i4 Y( T+ X: u# 参考答案
    3 x) t0 O8 D& U6 T$ L7 gs = pd.Series(df.Sentence.values, index=df.Name.shift(-1))* R* b* e; z0 U2 A* v8 B) d
    s.str.count('\?').groupby('Name').sum().sort_values(ascending=False).head()+ Q3 F2 z$ e: ?, J7 E# K
    1 D5 B8 b/ p: @+ s5 F
    1$ ?3 S0 ^, A5 c; t  o
    2
    ' y1 v# z+ O2 |9 I3
    / ~8 |. B2 x+ D% `2 S4
    ' p) Z3 I3 N2 u! q5 ]  o  b5
    # _7 Y. h5 S" R% n/ o7 p. O6/ e- G. m6 N) Q2 v6 y
    71 V. t0 u5 }# _+ ~
    8% E, ]: V4 T8 P. D
    9
    3 r! L. X+ p$ c* n3 n* D  ^( W10( U) W4 @- K. j  ~. g7 o
    11
    " _# ]. M) q  O" r( o: z1 a" l# M* @' r- D12+ |% c% v* q4 x. Q' a1 N+ i
    134 {* S! \. ?. D) k
    14
    " o. d' B1 [4 w) f1 z5 ?* Q" T/ F15
    6 s2 M+ n8 G. K0 ~2 r9 S16# {' M" }. |0 g4 Q9 L
    17
    9 m& z: x) ^( v. U" J% V  v9 Z第九章 分类数据
    ! T/ J( h2 O) j; v6 O  ]: r( Limport numpy as np
    7 W5 T# T, U* Aimport pandas as pd
    2 \  X. R. P( t) j$ V& e7 V1
    6 F& q1 ~3 Q$ i# W" d24 x. ]+ R! e3 L! Y
    9.1 cat对象% Q, h1 l4 X% e" ?1 Q& R5 K$ m
    9.1.1 cat对象的属性
    ! N% D: u* O3 k2 p1 A2 p  在pandas中提供了category类型,使用户能够处理分类类型的变量,将一个普通序列转换成分类变量可以使用astype方法。
    $ B. i$ l* T$ B( k7 R* w, @! C( {4 b
    df = pd.read_csv('data/learn_pandas.csv',
    / \9 M6 j* E/ U' O. v( b3 U/ @; z     usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight'])" W( |# W3 E  K/ k0 s2 V! A. U
    s = df.Grade.astype('category')
      F1 {! c$ ^' {. ^. u) W7 p
    9 P$ v' m' `4 s( js.head()
    3 s6 D9 K' q/ [6 Y4 J2 P  m; a) u. WOut[5]: $ a# t' t- q0 m
    0     Freshman4 L' O$ x2 b, g
    1     Freshman; o0 Z3 F& d( j& y
    2       Senior( d( S! _+ ^: Z& e5 l# @
    3    Sophomore
    6 P& V& l+ x: @$ \5 H& A& D$ l4    Sophomore
    - d) W+ w" g* bName: Grade, dtype: category
    & A2 R7 E4 b! _$ O+ \% j% fCategories (4, object): ['Freshman', 'Junior', 'Senior', 'Sophomore']2 s  ~  v: A7 A& o' I
    1, k5 z1 F9 e& W! J/ a
    2
    9 a* G9 f+ a2 o0 H3% Q9 A! u' N' @* w- m/ j3 X
    4
    6 t0 P9 x* ^/ P) ~7 a! ^( g. W5
    # o/ ?6 T) n4 l) E" J; k7 ~7 V6, t2 X) F2 r, `# b1 X4 y
    71 t. P" I* C2 C5 q
    8
    & t+ `; R. |3 A0 l! q6 y9
    * P# I8 g. w3 v# j3 ]3 B0 Z: |10
    + R' U1 a7 D0 G$ a7 h! X$ R2 O% l11/ Q$ {, d8 g& Y0 e; C9 z+ M, p
    12+ T% X0 \, ]3 i% ]9 _
    13
    + g+ w' {! p$ }/ f/ ?4 y0 X  在一个分类类型的Series中定义了cat对象,它和上一章中介绍的str对象类似,定义了一些属性和方法来进行分类类别的操作。
    ; W; p8 D; J3 a  E
    ) \9 R9 _# Q' t) ?- M& u* c4 l5 u- ?6 w9 Os.cat! y  |9 b, A: @/ _/ M4 y
    Out[6]: <pandas.core.arrays.categorical.CategoricalAccessor object at 0x000002B7974C20A0>& @0 l( E+ p3 G7 }2 O9 h( }1 u0 q- c. P. T
    11 v2 `2 l4 [4 A/ K8 o
    2
    " C2 Z. Y8 i4 Bcat的属性:
    4 T( o4 t0 M& e8 c1 G" d
    ) Y* I& p2 S! @; v# Z6 W: ~9 Scat.categories:查看类别的本身,它以Index类型存储6 B. B2 y4 Q( L/ g) h% ^7 b
    cat.ordered:类别是否有序
    3 A, I- V4 I$ z  o0 X  j( B5 jcat.codes:访问类别编号。每一个序列的类别会被赋予唯一的整数编号,它们的编号取决于cat.categories中的顺序0 h% p0 o% S: e  N# \6 Q
    s.cat.categories
    , F. k1 m* J2 q  eOut[7]: Index(['Freshman', 'Junior', 'Senior', 'Sophomore'], dtype='object')
    9 `9 U" A3 M( B1 M' X/ J
    2 h0 j# P# ]* Ns.cat.ordered
    5 z4 W0 P4 R, I2 Q0 OOut[8]: False
    3 @: G/ S" T" v3 K! m. m% n2 S5 c3 {
    s.cat.codes.head()$ b$ B' O/ i5 i- p' S9 F
    Out[9]: 0 A( o4 H" y: g) }
    0    00 D" v+ B" o2 R7 Z9 h
    1    0
    5 |# g2 q( u- m2    2% i0 ]8 w* T6 Y$ |8 Q
    3    3
    $ h# t& O; Z& F4 C7 }+ M4    3$ }  u$ p, z; K6 i- ~0 i
    dtype: int86 p9 h. t/ i) p* t. u/ ]4 u
    1
    $ }2 S5 V9 {: @# q% C2 q* r* u$ ?: d2
    / E; v+ K/ b$ ^/ E5 D5 S" I& p: e% r3  M  v' u- G& ]: `1 @* Y
    40 U+ J5 n# _: M4 Y+ k  P6 ~5 z/ h
    5
    : w6 |) I" h- y6$ X% n9 d! K* v) G) N" t; l
    7. F. K' }/ c2 o' n& V$ w
    88 u9 _, ]$ p0 I$ s, X1 t
    9
    9 p+ l6 m8 _( K( R4 U101 J1 P5 Z* W4 q7 J
    11
    2 q: }) @. ^0 Y( C& }12
    ( n5 ]5 U- \% O4 T0 i2 _13( K. R+ r4 ~! r3 u3 X
    14+ p4 @* O* m+ Q9 h+ l9 d
    9.1.2 类别的增加、删除和修改
    + V, ?% }. k& h8 P# E" [2 n  通过cat对象的categories属性能够完成对类别的查询,那么应该如何进行“增改查删”的其他三个操作呢?3 d- m: J9 \0 A! V

    % v& o! r* B$ {  |【NOTE】类别不得直接修改
    6 H) G& N8 w3 Z3 }* `在第三章中曾提到,索引 Index 类型是无法用 index_obj[0] = item 来修改的,而 categories 被存储在 Index 中,因此 pandas 在 cat 属性上定义了若干方法来达到相同的目的。  N" X. S& }* [+ E, u' Y) y

    1 ~/ L$ O) h* b. a, Kadd_categories:增加类别* y) o  i% g5 Q" m4 c
    s = s.cat.add_categories('Graduate') # 增加一个毕业生类别
    # ^, B: @& a& t, K. O* Rs.cat.categories
    & d( n" Y5 b: q! R  y* j; o) G- E% N  P8 L1 R! `" z7 A
    Index(['Freshman', 'Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
    8 P4 ~) ]  w# X+ a1
    - ~- r  V% k9 i( t  D, R) u28 w0 D+ J: u+ E  k9 ]5 J) b
    31 n, ?: {! R' c7 b8 a$ l
    4
    + T: d9 D/ \8 @9 dremove_categories:删除类别。同时所有原来序列中的该类会被设置为缺失。
    9 i& s; Y' c* O$ S6 os = s.cat.remove_categories('Freshman')
    # ]1 |; Y- s; [9 A" d& x2 `
    1 y; C5 B5 }1 T6 m  e6 y1 Us.cat.categories. n8 |3 M. C. h5 R5 e# {8 U
    Out[13]: Index(['Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
    + g; ]: e7 D9 x9 T! \$ [5 g
    & v# [/ L1 J; W1 Ss.head()& A% K& L! P9 J2 h) Z! c8 |, ]
    Out[14]:
    1 T/ S7 Q4 _) X3 }7 W0          NaN" O! F' X' Y" \  V/ {# ?. d
    1          NaN
    * A" X4 V1 ]0 @% C, [2       Senior
    9 [+ @  D; |( t8 i0 b/ Z3    Sophomore
    - i- p# g" P$ b. P4    Sophomore
    + B; S% i" A' u' j- W) m* K. I) SName: Grade, dtype: category
    ' K" V- ?% D- e, Z, r' S" Z( rCategories (4, object): ['Junior', 'Senior', 'Sophomore', 'Graduate']" C2 w' [+ b; w8 e' C& C: f8 T
    1
    9 f7 `' V6 I: f; H. A5 B, P# U2
    4 U8 i+ `" D5 n- W3 }% ]2 V: u7 j3- G, p: Y& }! g# w6 D$ J  E, J
    4' e  r  q2 b5 E% d' U
    5
    6 D7 `  @8 I( S( m; G6
    7 Y9 a8 U7 f" l4 ^6 C- D! M% P7
    . b- b/ c; R7 V8( t/ f# y7 B  U) ?! x
    9/ }& s" D1 B: \9 r; e! e
    10
    ! X$ [2 r5 Y! f7 H( X+ X5 p11/ Z  n  e, L& z  l0 V$ W5 ^
    12
    ( A2 w) E" ^+ d$ c# H13
    5 @; N) ?4 Y) ~" T14
    5 H% S( P! I1 x" A8 E+ W2 hset_categories:直接设置序列的新类别,原来的类别中如果存在元素不属于新类别,那么会被设置为缺失。相当于索引重设。9 \. E$ m4 c/ }) N) g/ c, U6 F% w
    s = s.cat.set_categories(['Sophomore','PhD']) # 新类别为大二学生和博士
    # E2 r6 ]5 y& W) j& G; f& h. ~) L8 os.cat.categories
    * v: o1 r2 Q, Y1 i) oOut[16]: Index(['Sophomore', 'PhD'], dtype='object')
    / J( o# {! f- _) U3 ]
    / T% V) O2 P% Ts.head()
    ) e* m- x& Q1 o2 Z' {  UOut[17]:
    2 w% i' D) L6 H- ]. n" w0          NaN
    3 L, k* _* \/ u) Z5 J1          NaN' d5 o! j7 o) ^( {7 A' S7 J6 V
    2          NaN
    ( U( `% J& I2 }3 i1 W' n" B" |3    Sophomore: u, C: W4 h+ M. ?
    4    Sophomore1 a% z1 ~' H$ c" ]) k2 K5 \
    Name: Grade, dtype: category. i  X( V3 w2 B  u. a2 X  i5 R
    Categories (2, object): ['Sophomore', 'PhD']
    0 m/ m0 U+ G1 h9 |( ~1
    * a+ C0 o) f7 {& o# K* y; k2
      ?7 S5 H0 I) f5 @  e3 L, _+ w35 O7 [( s( Z0 M; @2 B% S0 K( O
    4
    6 n- p9 F! ^2 A& _5
    * F& {( i$ ~  c6 X6
    0 _: M( z8 M( o4 g7
    ( L/ i% Z- h5 j1 t8
    4 E" l% R1 S9 `9 N) q9
    2 c; Z8 ^1 K# U" y10& `( ^1 k2 v' r7 ?/ [
    11' T# \4 ?. A( _  X6 l4 C; i2 E: g0 A& l
    12. U: H2 y  L: \5 k
    13$ r2 J' y. m9 F$ z2 ~6 a
    remove_unused_categories:删除未出现在序列中的类别
    4 G" E7 O+ ]/ M' w1 F5 T+ n' `4 \s = s.cat.remove_unused_categories() # 移除了未出现的博士生类别# X) u2 m0 A0 c* X! A* z
    s.cat.categories
    . y6 e0 f2 W2 a2 s9 Z4 _- y* r7 _4 {9 J0 C: l0 X
    Index(['Sophomore'], dtype='object')
    ( y# u) F  G; a. [# Y; @% D1
    # q1 f+ f7 D7 v3 C- l23 ~0 n7 |/ e+ c& o$ `3 G
    3
    ) f4 d  q" i! H2 N) d! o( a4, m) h1 O! O& h  v. g4 g2 K
    rename_categories:修改序列的类别。注意,这个方法会对原序列的对应值也进行相应修改。例如,现在把Sophomore改成中文的本科二年级学生:
    ! g8 W% ~* O& ]6 g5 Q9 C$ Z5 cs = s.cat.rename_categories({'Sophomore':'本科二年级学生'})) z5 s% H  n5 c  S% x
    s.head()/ y6 [8 ^; b. w7 X- T6 e

    ' _! ~: }8 c" }2 @$ Q3 Y% c0        NaN, @! d1 F- ?: V6 Z$ ]/ Z
    1        NaN) ?  d7 k, h$ \, u' I
    2        NaN
    , B. p  M1 K3 x3    本科二年级学生
    - [8 z- e$ K6 J" j; W4    本科二年级学生
    $ z) G/ x8 ?& [2 m' a7 c. p5 Y( TName: Grade, dtype: category8 q) f- N5 J0 D0 l. l
    Categories (1, object): ['本科二年级学生']; ]& g3 z$ C+ l
    1+ K5 j* }2 V8 I( d: n
    2
    " d) f( v* I* _1 x3 F2 T/ ?. Z; p2 L) s34 B0 }4 I8 F& ^# g
    4
    8 i3 }6 s5 M! Q  J# f( R; Z5" c6 o! G( q/ e% D- z
    6
      ~# \! `6 }. O: @6 r7
    ( M8 c) E' y! @0 c% X& e8
    2 x& s4 M. U: M9 Y, [4 ~4 s  I9
    7 `% l0 h3 S! n, H/ g- e10
    8 _5 O) t4 Q+ B; a2 Z- J1 r/ w' r9.2 有序分类
    % V5 m# L( j/ `; W9.2.1 序的建立
      Q0 z+ N7 d) l9 T% Y  有序类别和无序类别可以通过as_unordered和reorder_categories互相转化。reorder_categories传入的参数必须是由当前序列的无序类别构成的列表,不能够新增或减少原先的类别,且必须指定参数ordered=True,否则方法无效。例如,对年级高低进行相对大小的类别划分,然后再恢复无序状态:
    / i9 h8 P$ {% `+ F% y1 L4 }/ U7 O& F8 x2 J5 d2 s! Q# U; _7 f
    s = df.Grade.astype('category')( n4 t5 F& E3 \( R1 Y2 R1 B  r- i
    s = s.cat.reorder_categories(['Freshman', 'Sophomore',
    6 f$ W- t+ o0 K) A5 C                              'Junior', 'Senior'],ordered=True)
    : L2 S; I9 }6 Rs.head()
    4 V$ L2 ]( w2 ?% [0 gOut[24]:
      \, z! U/ U8 Z% O2 G& m$ W( a0     Freshman
    $ N% E+ {2 i$ s6 s! \1     Freshman. W1 v$ K: U5 G( e& {" Y
    2       Senior$ u# m+ ?4 c5 x: Y% t4 Z8 q
    3    Sophomore
    . l8 |- j9 \3 S6 ^: f4    Sophomore9 z5 Q7 R" Y7 ^! X1 \" B) n
    Name: Grade, dtype: category, n3 Y/ `  j. @6 x
    Categories (4, object): ['Freshman' < 'Sophomore' < 'Junior' < 'Senior']
    ( ?  A0 U% X7 X' ^' R
    % M% s. u- J6 ~1 |# Y; ps.cat.as_unordered().head()" q( b, {3 _: s/ d& g
    Out[25]:
    5 j) E! i4 o' P4 S" S0     Freshman! g' C7 Z( M: L+ o
    1     Freshman
    ! M4 Q; C+ [, K' P2       Senior
    2 U( y" P9 a+ T! a3    Sophomore2 q9 w) i7 s) d9 R
    4    Sophomore% _6 f  S: P* p4 P: b/ l3 a
    Name: Grade, dtype: category
    + s1 |* c3 q- i8 z- |Categories (4, object): ['Freshman', 'Sophomore', 'Junior', 'Senior']  X5 }0 e1 e9 K
    ) |5 e$ h8 g/ f5 P. t+ u
    1% p$ t2 c: |% p( p( L
    2
    . M' f! B' N9 W/ G- f3
    5 _6 f6 o6 Z9 p0 ^% {! f6 E# X4
    % e( N/ g: r2 x' p& H& |  i8 y5; O5 A0 H+ j; E' v
    62 n" V* M, a, n  G
    76 d1 T' Z" r- }7 S
    8
    0 A* v8 e+ n3 l8 W6 C7 |, t9: t& l+ i3 ~0 [/ e/ S9 k# t  h
    10
    ' g0 B% L3 i- V) D" W7 ^112 l0 p$ o6 x/ Y/ S* q9 R! {
    12, y+ N: g0 G8 I7 }4 V
    13  s3 v; ?5 e. U$ K/ c# }/ n0 W
    146 b8 a2 r. r$ @' G* \, a2 s
    15) q# x0 d1 P# B* L9 T0 d
    16
    / C! ]+ C* [* D# x. }17! b2 V5 v; J$ `
    18
    & N4 L* y" Z3 a) n$ J3 W198 t% d1 V0 ^  q, y. ^% S6 q4 j
    20: I$ ^/ T$ h2 M3 W1 ~6 ?2 h
    21
    2 j3 J2 @% G" v+ ]& L# X22& l; H+ R7 h  u0 N+ M. W$ I8 ~
      如果不想指定ordered=True参数,那么可以先用s.cat.as_ordered()转化为有序类别,再利用reorder_categories进行具体的相对大小调整。
    5 Y" N/ j+ u- d' ?) P/ A5 L$ x6 N6 y# Z
    9.2.2 排序和比较: h+ P( f! ?* u/ T
    在第二章中,曾提到了字符串和数值类型序列的排序。前者按照字母顺序排序,后者按照数值大小排序。7 I7 J6 V6 K/ ]) W0 n

    ( @9 h( ]$ M$ j  分类变量排序,只需把列的类型修改为category后,再赋予相应的大小关系,就能正常地使用sort_index和sort_values。例如,对年级进行排序:1 W& l, M, x' M) z& q

    # }# Z; }% {$ e( i0 I% N9 c% Idf.Grade = df.Grade.astype('category')
    1 V  \1 q: Y1 [. m  D, V) t0 @df.Grade = df.Grade.cat.reorder_categories(['Freshman', 'Sophomore', 'Junior', 'Senior'],ordered=True)
    6 t6 Y$ U& d/ C) a. Xdf.sort_values('Grade').head() # 值排序
    3 n& Y- O. v3 x7 W- t' V# r1 ^4 LOut[28]:
    0 y- V$ ~8 O# d% }% \: ]7 r; p1 F        Grade           Name  Gender  Height  Weight1 a0 f2 l' Q+ w7 q' |( Q! l+ ]" |0 o
    0    Freshman   Gaopeng Yang  Female   158.9    46.01 y; P* n9 g' m) `. ?' [8 s
    105  Freshman      Qiang Shi  Female   164.5    52.03 l6 ?( `  ?0 E& @+ Y
    96   Freshman  Changmei Feng  Female   163.8    56.06 B! C1 W" r, P+ d) r2 P3 ~8 w2 d
    88   Freshman   Xiaopeng Han  Female   164.1    53.0
    6 F9 [0 f8 l# b. p81   Freshman    Yanli Zhang  Female   165.1    52.0
    4 B2 j  P3 l. I( B3 w! f. M8 r: `, C! Q$ }! v) L
    df.set_index('Grade').sort_index().head() # 索引排序
    1 `7 |8 D. ^& M: [, z" COut[29]:
    9 |5 t  R- h+ Q: Y; u7 z                   Name  Gender  Height  Weight8 K# G! S6 {. s# B8 q
    Grade                                          : D! q2 ~% x( B. |% w+ j
    Freshman   Gaopeng Yang  Female   158.9    46.0+ A" w  [9 Q' l; [2 y
    Freshman      Qiang Shi  Female   164.5    52.0+ \8 w1 m' P" c$ u4 n& H
    Freshman  Changmei Feng  Female   163.8    56.0- R9 I' Z" N5 I
    Freshman   Xiaopeng Han  Female   164.1    53.0
    8 j# d& M5 G  Z/ H; W6 uFreshman    Yanli Zhang  Female   165.1    52.0
    8 q. O" a  C: f$ {1 U! ]( `
    ; F: N4 J, h1 Q, y1  J  g, X( f& w/ W/ ^- h% T
    2* s$ C5 @* s- o0 _% ?5 V
    35 M" F0 q, o, Y( J0 }0 T
    4
    7 v8 }. ~8 L* Z4 h  H, ]9 P/ G+ T/ c6 i5
    ( `  _! j& \# ^  w0 \6  ?  ]" C* z  t) l5 Z  _# @1 @
    7
    % D; @8 ^8 l4 H" L8
    ! r6 E' s: w0 W0 p" y9# u( R1 K6 w# w9 T+ Z
    10
    ! ?4 Y" Y# X  l1 \$ T/ }" S11& R; {' Y) T$ F$ `( P
    12
    " @$ \+ O5 m+ R, ?5 _13% x3 R$ C2 F, s
    146 Y$ \# }* L9 t' H* t3 f
    159 p3 _9 J- Q: l9 A$ r. k
    16/ Y; t5 `! m, }& ~- a; d  T
    17% B  K8 i) _& Y) ?' y
    18- p; {7 C# d& r7 p  f8 s: ^
    19$ q9 e5 X8 L- E! g/ [
    20* d* @+ E7 q$ l0 q7 Q; |
      由于序的建立,因此就可以进行比较操作,方便后续索引操作。分类变量的比较操作分为两类:
      w9 }% @+ Q* _! O0 b# e4 D$ Y; S' l, u* I6 I( ^
    ==或!=关系的比较,比较的对象可以是标量或者同长度的Series(或list)。(无序时也可以比较)5 a* M8 Q" N5 H$ A, G
    >,>=,<,<=四类大小关系的比较,比较的对象和第一种类似,但是所有参与比较的元素必须属于原序列的categories,同时要和原序列具有相同的索引。
    ) [) X3 ^6 V' j& L/ yres1 = df.Grade == 'Sophomore'# M' j1 B' c/ c: j6 |7 G" i
    . ?# a7 ]# x5 a8 Z6 U1 Q9 Y
    res1.head()
    ! M4 R8 t3 W3 o7 fOut[31]: 2 a. l& `& F& w5 E: L2 c
    0    False
    ( ^! R- T& q  F1    False
    " v/ u- f; I/ v" s1 F: ]2    False
    1 i. e+ A9 _2 @3 }; D3     True# U' ?0 v( d6 y1 n! _7 O  ~
    4     True
    ; _) J+ i1 S( t9 `9 V- XName: Grade, dtype: bool# W& `) M' A6 h

    # J/ K9 X( c/ O6 X- pres2 = df.Grade == ['PhD']*df.shape[0]
    / V- B* Y7 d* M% R: O# b: b9 d
    res2.head()
    5 O, Z% |; v# S+ P: K5 o3 t; xOut[33]:
    8 |$ K7 `9 F5 {& }) O6 s0    False
    8 x* U/ x/ f4 x5 U% G1 f1    False' Z5 Y! Q; B3 W. c! r0 I6 h  X+ m9 p
    2    False8 V+ w, \; \5 q+ X) x
    3    False
    2 L( T% G+ M* V' b1 v0 r4    False
    ; a# q, |/ `. ]4 _  xName: Grade, dtype: bool
    + v8 F% T4 n2 F, v# u! |$ Z& e0 E; t
    res3 = df.Grade <= 'Sophomore'
    . w3 c" b3 t+ j6 |8 y, U( Z9 Q+ T/ t  ]# x; M
    res3.head(); o; V/ a5 |5 {
    Out[35]:
    2 `4 P& d7 ?  [, ]; ~  o2 t# ~# }0     True2 v; K, A9 C. }- m& r
    1     True2 u) @+ o# A2 s- Y. p1 k7 M/ L
    2    False4 p. t- k2 y( b" ^/ [9 S2 T
    3     True
    $ Z( Y5 n0 w2 {5 u1 M4 I4     True/ O  s& e9 L  Q# l# n9 ^
    Name: Grade, dtype: bool
    . @2 ?1 y) X9 e5 O3 s5 A  n! S- o) f- P" m* \& d0 P* O
    # sample(frac=1)表示将序列随机打乱。打乱之后索引也是乱序的,直接比较会出错,必须重置索引。- [& n0 g! q1 A' d- u7 K
    res4 = df.Grade <= df.Grade.sample(frac=1).reset_index(drop=True) , _3 f) G! _, l, n" k
    6 f" X9 E5 w7 k! f" I, S5 M
    res4.head(); w2 E; {6 L6 U' r+ a
    Out[37]: ! @% H( A4 o1 I
    0     True
    " p! I. o0 _6 v3 _* j" u* @; T0 a1     True5 M' t" k. I; U  }2 X& C
    2    False
    / P7 U* U* {+ y, s4 u2 v3     True
    4 u) P  f2 u# E7 d( a& x7 B4     True0 S3 p8 c/ e* W  E
    Name: Grade, dtype: bool
    2 @* b# C4 U. p) ~3 q* W+ [- D. x
    ! x* T9 Z6 F; n$ W. [1) H! b% e) G' R( d
    2: d1 o, o  i, L: n- M- a
    3
      D- ^$ E9 k5 n  ]4
    % ~1 L0 ~% U# d* }! X' I' U1 O5+ }* S7 W, p' ~0 ^8 B
    6
    9 C0 E* {  T2 w74 i, |; D/ e" I3 W
    8
    + G  ~9 l' ^# b& ]: H# b1 u9 p93 Q' V+ P4 |5 ~7 v# I3 Y) \
    10, I! T" y& h! I+ @1 C
    11
    % _% I8 q' Y! h/ U: H12, e8 x- N# z, z! H+ d- F& C
    132 r' c' A0 Y0 W* W. ?* E9 [
    141 F  H. p6 P+ @- U4 E; L
    15
    ; a, Z" k+ k- O8 l' \. H3 I( X16
    2 Y& k1 Z8 M! j' S17
    # i! u8 J" R+ O- j18% W4 _6 x" C/ j, X, U0 {- C+ \
    19
    / b% c0 J% o' u9 q0 G  W* X20
    5 T# B( [  ?# m$ w: Y0 m/ a  S214 N' @" L, y5 W9 j' C+ _
    22
    , T2 x: `1 ?$ e7 z, r" l, R23
    / \' @7 h' w* V5 P4 P0 D24" G0 N% w% p0 u+ L* l& ~
    256 v( K( c4 n. I& w( N$ O$ H0 B7 d
    264 A, v* I$ Q5 ]) e; ?) Y
    27/ k8 u$ R- x* G' @- t$ {' I) v
    28; v1 Q$ O. ?5 D/ P0 Y5 S: _7 s
    290 ^4 K+ w# U  s8 d. D3 X3 W3 P" N
    306 c0 h* O/ W3 E+ t/ L
    315 a; E2 x4 ?9 c- `4 X6 a3 V8 U
    32. A3 R1 u1 D* A
    33. d% B6 z2 [, T1 e) j; f( c
    34
    * |! u# B$ f* x# o35
    # z  @) d9 y7 ^6 `- L  l# y7 `360 S4 a0 B, M2 Y8 ^
    37
    " n. u) K5 t3 N9 l! ?38
    ' [  F4 [  c- S" e397 Z/ o7 t, B2 C0 X' I; W) b2 `
    40+ Y* s  M& c" a. B4 ]/ F+ z( x& Q
    41
    5 O1 Z# T3 [2 E0 q: t3 x' P42  H/ l7 y% v1 T' d0 i; E/ t& @
    43
    ! n9 s4 H) Q2 P( T, N8 w; F! m& u44
    2 s0 L. u6 Q% R2 N9 y9.3 区间类别& N+ S# v/ o% I
    9.3.1 利用cut和qcut进行区间构造6 ]  E: L7 ]/ s! o3 J. W
      区间是一种特殊的类别,在实际数据分析中,区间序列往往是通过cut和qcut方法进行构造的,这两个函数能够把原序列的数值特征进行装箱,即用区间位置来代替原来的具体数值。( Y: g, H( q5 T, }, f& ^
    + V8 u. Z* }, U- @* T
    cut函数常用参数有:
    3 S; E/ E1 k# L& n# Dbins:最重要的参数。+ N8 ~/ _' H( p4 u
    如果传入整数n,则表示把整个传入数组按照最大和最小值等间距地分为n段。默认right=True,即区间是左开右闭,需要在调整时把最小值包含进去。(在pandas中的解决方案是在值最小的区间左端点再减去0.001*(max-min)。)9 \8 K( p4 T$ ~1 _& R/ K
    也可以传入列表,表示按指定区间分割点分割。% N* ~+ j! `4 [9 o. p
      如果对序列[1,2]划分为2个箱子时,第一个箱子的范围(0.999,1.5],第二个箱子的范围是(1.5,2]。
    + E' d/ T  U% @. {. h" u! B  如果需要指定区间为左闭右开,需要把right参数设置为False,相应的区间调整方法是在值最大的区间右端点再加上0.001*(max-min)。
    " o+ k! L9 ^3 C. j2 ]8 j% g: N, W: P: f& C6 d9 k: P
    s = pd.Series([1,2])  ^8 y5 ~0 M  b% J2 j* a% d
    # bin传入整数
    ) r% o, ?& I! \- W! t
    + k- w) j3 e. W; E: Z2 c  A0 u1 E3 K7 Xpd.cut(s, bins=2)
    " z$ Y' z, n. p+ qOut[39]: / {0 q0 F1 H1 r/ V0 T( d) C! r
    0    (0.999, 1.5]
    4 O7 {' ~- b+ ]  l4 C9 Y: X. m1      (1.5, 2.0], {3 y5 V6 I2 Y$ Y) S
    dtype: category  q4 {1 R% L7 N
    Categories (2, interval[float64]): [(0.999, 1.5] < (1.5, 2.0]]
    9 T/ z$ J8 s& W% }6 j3 j+ T1 K  e" \- D4 O- Q9 b1 w
    pd.cut(s, bins=2, right=False)- c0 q9 L% [# U0 t
    Out[40]:
    8 l! s3 m% e6 |8 |7 ~  [0      [1.0, 1.5)
    8 G2 Y7 f2 h: }, S5 s$ b" F( b1    [1.5, 2.001)* K% h) S% J" |: D  C
    dtype: category9 C- I+ X& N; |+ @  S$ H
    Categories (2, interval[float64]): [[1.0, 1.5) < [1.5, 2.001)]
    9 v; e8 O: X1 z! h  N) J
    + T5 a6 R! D/ r5 x; i" J' a$ Q4 T
    9 K0 I/ V, y  R" P" q; Y; }# bin传入分割点列表(使用`np.infty`可以表示无穷大):
    7 \& M" W3 \' R" Zpd.cut(s, bins=[-np.infty, 1.2, 1.8, 2.2, np.infty])
    1 t! T/ v/ [) AOut[41]:
    ; e8 i9 {; q: u0    (-inf, 1.2]. @2 R' w0 I5 H4 b( S
    1     (1.8, 2.2]3 \& _1 R# U! t' Z/ `& f3 R
    dtype: category
    6 _* h# M- U8 Q+ l. v: ]; o% o; DCategories (4, interval[float64]): [(-inf, 1.2] < (1.2, 1.8] < (1.8, 2.2] < (2.2, inf]]# \4 g5 h6 L4 P  `  l, m4 o

    / ^) P  K' H8 X1; e3 v4 z8 u0 h, H/ P% V
    2
    " x0 n( ?/ E, i1 q; U3
    / F- R1 U+ f" P  T, {4
    5 z/ y- ?: m- v$ V8 u, R! c50 G8 P* ^' D) `0 N2 J  Z) _5 S
    6
      N/ T# E# c# _( ^7: P" M, F1 X; F# H% C/ ?
    8
    8 l+ D* X* C6 o; G$ c9, _& K. E5 J6 D7 G4 e5 B+ W9 M- h) q
    10: ~1 T' f! u7 v" }0 I0 b- b
    11
    0 M* z9 C# P0 ~5 O3 d# x( l12
    & b! ?4 J5 Q. ~132 A  F+ H* C  P  W( @  X  M
    14
    " `+ G9 ^% u+ W15
    5 A1 E7 p# U7 u' e! D. e- i/ Z16
    + N" M# d7 s, O+ V$ v% Z2 M176 A2 v4 ^( M' Q8 M+ Y8 w8 @
    18/ |$ V6 @* x. D" d! ?
    19
    4 o/ O" z2 c8 J0 l9 Q, H208 r6 {: V3 U2 k0 [8 J
    21; \, q: }$ W1 `' @4 q3 ]
    22, t/ l$ _$ J" }2 p
    23
    " @: ~7 E! o* w: f1 ~! _: v& [24
    " J5 J; t" K7 E; r/ O; P25
    ( i' \4 q/ k$ b, R4 n8 E; Tlabels:区间的名字- L' Q# l3 s4 c% U  t) h
    retbins:是否返回分割点(默认不返回)1 R. g0 b8 ]+ O, p" {, Y8 C7 [
    默认retbins=Flase时,返回每个元素所属区间的列表
    . g. h" o) l, N- B0 I2 K. Cretbins=True时,返回的是元组,两个元素分别是元素所属区间和分割点。所属区间可再次用索引取值+ \9 Z+ K4 P" e+ z

    $ h: L- |+ A7 Ms = df.Weight1 r) O% s2 a3 F" a' v, T) N2 t
    res = pd.cut(s, bins=3, labels=['small', 'mid','big'],retbins=True)6 _$ j5 f, w, c
    res[0][:2]
    / U/ M% e' r. L
    ' {% d4 U, u3 qOut[44]:
    & R& [& @2 N) ?; Q9 N& [; J0    small# o& ~+ F  }+ U' P. A, b& \) `
    1      big. f# R, M2 C( [! G
    dtype: category
    2 a* d9 b  U$ D& KCategories (2, object): ['small' < 'big']# f$ I4 y! J" H$ o  j$ A' I" x
    # }8 a9 J1 O# \/ x
    res[1] # 该元素为返回的分割点
    ( A, W  j2 _* b  F* cOut[45]: array([0.999, 1.5  , 2.   ])
    4 x- y' E, ?  ]* n1
    , ^6 }- Z! V- i2
    1 _1 q& ^# }* I' P5 v0 i3/ q& B& _' C& s/ c( I  `0 h
    43 ]& ?1 T7 z. R, J
    5
    ( J# d# r* f# v- M* G* Y6 t64 Q4 f2 f( I* U7 x! [7 \0 F) u
    7
    6 W8 T9 _: Q+ ^6 m. o8 H' ^1 [8
    ' Y" J9 E, ]. l& I! a8 P94 N" \* s: h  a' H8 \2 f! r2 q
    10
    4 O9 c! `6 D5 H4 B% K- Q: W11$ B% q5 A1 ]1 i2 {% u* T+ L8 S$ E
    12
    1 m! {* O' j) _1 s" d  A  o/ ~- M# Gqcut函数。其用法cut几乎没有差别,只是把bins参数变成q参数(quantile)。
      `9 p1 j' i" X& B/ h" iq为整数n时,指按照n等分位数把数据分箱# D8 P# n7 R! X. J# M" @
    q为浮点列表时,表示相应的分位数分割点。( N+ b. v; u! T- x' X( }9 \1 u  P
    s = df.Weight! @, e$ Y7 e: H1 P$ |
    % s: R2 u7 |* Q; J
    pd.qcut(s, q=3).head()
    9 k4 C1 L1 V5 c: L" qOut[47]:
    8 d, E0 r' a' @# n( B- R0 `/ Z0    (33.999, 48.0]
    6 i; b! o0 X  \/ q2 R+ [0 _1      (55.0, 89.0]
    ' d( R, U" q+ }" x, S& U2      (55.0, 89.0]5 f# }* B6 a8 d
    3    (33.999, 48.0]
    - c% T9 ?, h( r( n4      (55.0, 89.0]9 Z4 U. e1 E: ?9 k) J0 H3 W
    Name: Weight, dtype: category5 A- x+ m- r9 @$ k5 J" {
    Categories (3, interval[float64]): [(33.999, 48.0] < (48.0, 55.0] < (55.0, 89.0]]
    # D4 h; l5 W3 m/ R, @  J( H! e7 \: E1 r! S# M" }
    pd.qcut(s, q=[0,0.2,0.8,1]).head()
    ! R9 @' N' y  `5 ~# Q- U0 ^Out[48]:
    + O) ^) [% K7 U6 n+ U& U) B  ]! [0      (44.0, 69.4]
    % p% R. |" ^% f; }$ P. ~1      (69.4, 89.0]5 Z& l% i$ {# l* `" p
    2      (69.4, 89.0]) v) ?" {9 h- Y2 z" q: l- F7 h
    3    (33.999, 44.0]  P+ q6 n4 A1 y! z" o
    4      (69.4, 89.0]: B2 R( V) J; v& @6 m! C
    Name: Weight, dtype: category4 b" r4 J- s- x' P, I- k
    Categories (3, interval[float64]): [(33.999, 44.0] < (44.0, 69.4] < (69.4, 89.0]]5 n7 [* M# `/ @! N. C; V
    1 i! l% b& N  A0 O5 c, V
    17 }9 u0 s* z8 j; J
    2
    8 h# y- D! Q' q0 b35 t) O+ Q  x/ f  B% R. B+ q
    44 l  V- I" e+ U# W* P" S3 j
    5
    ' e3 ]+ J7 D3 u$ t" k# R) t6- A( u0 z4 \( h+ z$ A
    75 f1 z/ m, ~' L3 b" V
    8
      T' F* t8 w+ u/ }* |2 Q7 ]9
    % R6 m8 y% d! F- V* n10/ b* p2 w/ m5 ?2 Y
    11
    8 V8 G: S  a7 s' \, {129 ~2 Y& g# t; Z$ I2 w. U
    13
    " x3 m$ M4 y0 a+ \$ a' s8 q: f' W14
    * W- l  Q* u1 k$ _' T6 L: j! ?6 G6 x154 ^+ `+ d5 R. v0 T
    16" Q/ B. X% |9 M; l5 J
    17
    " M/ c; X0 Z  h6 z18% r$ I, ^7 ^+ O& e) |. h) i
    19
    8 U5 m4 g* d1 ]  h8 U9 L9 a% `$ C20
      U  k: E" R  W4 C8 y5 e# C) R" B21
    0 v9 B% p6 J2 v5 g4 j9.3.2 一般区间的构造
    % {6 A$ L/ P2 ~# \! s! u$ C  pandas的单个区间用Interval表示,对于某一个具体的区间而言,其具备三个要素,即左端点、右端点和端点的开闭状态。/ Y0 a8 Z/ S: |# Z% f$ A4 y

      |( S% J7 a2 V  c8 h! i* E* @$ J开闭状态:包含四种,即right(左开右闭), left(左闭右开), both(两边都闭), neither(两边都开)。; ^2 u* z: l4 Q3 S: D2 _) z
    my_interval = pd.Interval(0, 1, 'right')" q6 ]' S  H, x3 m

      v* B5 E) s3 \: fmy_interval# e, G# l! Z+ a2 a# K6 Y' Z# G
    Out[50]: Interval(0, 1, closed='right')
    / @7 |; V9 {0 i$ l12 i% X, }0 ^' G2 i3 D0 G: w% Z. [7 a
    2
    : B/ K' _! B) o* ~* G+ J8 T  ^% y$ t3. z' y; g8 v) q; x3 A
    4
    % a& F/ e) ^6 Y, h9 k- a区间属性:包含left,mid,right,length,closed,,分别表示左中右端点、长度和开闭状态。8 `: N) U& e1 f0 w- s
    使用in可以判断元素是否属于区间
    ! {6 n7 b! S8 H- Y用overlaps可以判断两个区间是否有交集:
    7 Q0 \, A: J1 A. x/ P; F0.5 in my_interval4 |' h; U/ d2 t+ ?" \

    : n7 e% |  `, y4 MTrue5 G  }. p, C, G5 Z
    1% w' U/ c9 i) d: J
    2) i( O! H7 C, z9 |" m
    35 d3 s$ ^5 F1 m2 }5 A# P( F; c
    my_interval_2 = pd.Interval(0.5, 1.5, 'left')0 y6 y$ F6 W8 p& T2 x: i
    my_interval.overlaps(my_interval_2)4 R4 I" x  O0 s& A2 \2 [& K1 K' O/ O
    # G- L: B9 i" I( k/ s
    True- E* i0 u0 N+ ~# e9 B6 V5 M" d& C* r* W
    1! E8 g% l' B% z. Q" M/ x% e
    2+ c! D5 Y( p5 S5 L
    3
    / h5 Q2 `" [4 D; P+ U45 ]# Q- @% K0 V# \" e) `+ h
      pd.IntervalIndex对象有四类方法生成,分别是from_breaks, from_arrays, from_tuples, interval_range,它们分别应用于不同的情况:
    / l5 I8 e: b: X0 ]  c
    + v% l, B7 J8 N9 W1 A- ~from_breaks:类似于cut或qcut函数,只不过后两个是通过计算得到的分割点,而前者是直接传入自定义的分割点:4 G$ O1 y# m  C# W( Q3 i5 @1 v, b" p
    pd.IntervalIndex.from_breaks([1,3,6,10], closed='both')/ \$ ~: q1 t" _* w: }( E# E2 \
    . k% u, \  `# I% c2 [# k4 D! S
    IntervalIndex([[1, 3], [3, 6], [6, 10]],
    2 I) c8 E7 S5 q0 N, U5 `               closed='both',
    ) v3 V3 u9 a& ?: ~- g/ i$ R. `+ i4 r* Z               dtype='interval[int64]')
    9 d% G1 d; f! l: n; j5 A* r, ^1
    9 N7 @) @# S# h8 f* z0 o2/ p) Z$ j3 x4 y5 J" O& n, b3 L
    3
    2 u- _* c% J7 G. a# W9 g$ t, r- p# u4& b4 }2 k  c$ f5 I0 M
    5
    $ D, q$ `- S3 L8 Efrom_arrays:分别传入左端点和右端点的列表,适用于有交集并且知道起点和终点的情况:
    4 i" p& l/ Q* m- `pd.IntervalIndex.from_arrays(left = [1,3,6,10], right = [5,4,9,11], closed = 'neither')$ ?+ t! [, U: L2 n5 K' q
    6 D3 P7 m8 U8 h5 A5 w* G, L5 x0 M
    IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],0 ?) v- j6 c0 ~3 Q) t
                      closed='neither',
    8 V5 ~5 M$ A4 F4 b  U: i- _/ R                  dtype='interval[int64]')
    ) ?$ t; R% K$ N8 z8 C1
    + E# |! s7 i; C" D. N2
    ! A1 t( \- s- y9 e; G2 m3
    : q* \: q: u9 b* ?8 z40 E# c& i, v( Y% H3 a8 \
    54 o. x# U! q' U1 \# K
    from_tuples:传入起点和终点元组构成的列表:2 e6 ~3 H) ]$ \* t
    pd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)], closed='neither')
    6 m/ d. n- \" l% N3 C
    / k6 n. R" R5 J! G4 y# t4 GIntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],
    8 m" n; _$ r* ]- }3 E              closed='neither',
    ) o# @' k- o0 x* I5 [              dtype='interval[int64]')
    " l$ A9 e1 }# c& x0 g10 w( Z9 ~- }) o+ R6 ^5 a
    29 R6 D8 T! L& J1 m, E
    3
    1 _5 e6 N/ V4 N: U6 I& q4" `' e* P2 P: s' `/ s! v3 q0 ?! W
    5
    . X; R4 g! [& V2 j" F2 kinterval_range:生成等差区间。其参数有四个:start, end, periods, freq。分别表示等差区间的起点、终点、区间个数和区间长度。其中三个量确定的情况下,剩下一个量就确定了,从而就能构造出相应的区间:
    ( S' Y- Q7 p1 g3 U! ]1 L' `' Kpd.interval_range(start=1,end=5,periods=8) # 启起点终点和区间个数
    / H6 D- G3 s$ }3 ^! W2 N) eOut[57]:   C- h+ h; |3 v
    IntervalIndex([(1.0, 1.5], (1.5, 2.0], (2.0, 2.5], (2.5, 3.0], (3.0, 3.5], (3.5, 4.0], (4.0, 4.5], (4.5, 5.0]],
    # K' f8 v: {* P9 }$ e# _2 `8 s              closed='right',& ^. r* k  k9 Z. L# ^6 T
                  dtype='interval[float64]')
    + E4 `, j+ d; |& x8 ]4 F# V: j9 w3 f9 n% e
    pd.interval_range(end=5,periods=8,freq=0.5) # 启起点终点和区间长度
    ! ~0 u, t% T; COut[58]: 5 ~8 g; P. i' C9 s" B
    IntervalIndex([(1.0, 1.5], (1.5, 2.0], (2.0, 2.5], (2.5, 3.0], (3.0, 3.5], (3.5, 4.0], (4.0, 4.5], (4.5, 5.0]],
    ( q% |+ }- u" A/ l2 r0 a$ t$ m: z              closed='right',2 T% @4 u3 a$ t5 |! W  W
                  dtype='interval[float64]')3 R( F& a; A( m/ V4 J
    1
    5 J9 n5 b! `6 l7 H6 x+ D9 T% f/ w28 N- Y0 u; \* R) K- n5 k1 X9 G
    3# I! J8 r+ [' g7 j
    44 C$ ?6 k9 l7 K& n0 J  {) s
    5
    6 u2 w9 z  W& L2 R& q5 u# ^2 M69 _: m# ~; h9 I0 S$ `
    78 R$ a1 |# [4 \* ?: I' v+ o- Y# \
    88 U' V" U! k7 v# ^4 n) R
    98 ^. d% e, k# A1 r  w3 e: M
    10; L, t; r8 p! f) L6 ?
    11
    # b6 d2 ?8 a. r* j* p2 m- y【练一练】
    8 @2 y' H  ^' y5 W" x) Q  无论是interval_range还是下一章时间序列中的date_range都是给定了等差序列中四要素中的三个,从而确定整个序列。请回顾等差数列中的首项、末项、项数和公差的联系,写出interval_range中四个参数之间的恒等关系。
    5 o4 A+ k: \2 {% b' e+ z$ i1 n( p
      除此之外,如果直接使用pd.IntervalIndex([...], closed=...),把Interval类型的列表组成传入其中转为区间索引,那么所有的区间会被强制转为指定的closed类型,因为pd.IntervalIndex只允许存放同一种开闭区间的Interval对象。6 y* i8 c5 g: O5 s: t# G5 S

    " [+ C' h; t# \4 S. rmy_interval
    % e% R0 T: U; P. G8 M% r! |, a! `: iOut[59]: Interval(0, 1, closed='right')! d. ^0 s" E+ z9 ]1 O& C3 r3 K# b
    1 J4 P+ Y8 O0 O* Y* k, ^  Z
    my_interval_2
    6 `$ G4 I0 C/ ]* P$ KOut[60]: Interval(0.5, 1.5, closed='left')5 W! d% z$ o7 Z9 [" ^0 q

    % y8 O7 O+ b$ jpd.IntervalIndex([my_interval, my_interval_2], closed='left')
    - b0 K) Z; z# W" {9 EOut[61]: . N. S/ l* g$ R! P. k2 U) E# Z
    IntervalIndex([[0.0, 1.0), [0.5, 1.5)],
      r( P; O& g* _5 _8 A2 C! n- f              closed='left',
      s6 h5 f7 Q7 m1 R              dtype='interval[float64]')
    $ f  |. d5 N3 U0 Y/ S1& M, `0 \: s. a0 g' B9 `' X
    21 ]* ]1 l' Y: v& @6 E4 n6 v( W
    3
    4 _& E# t0 L$ |/ i/ i4
    % W, Q; F' M& R' N59 z1 {. C9 Z9 f% C8 o! R; n
    6
    ; K' v% |/ |9 ~7: I* I4 J8 U" [+ @0 Q5 ~2 \$ U! n8 |
    8
    0 c6 ~2 b; ?; M0 i9 _& |; m' T9* v* x7 b9 l/ v" N7 p6 n( j
    10
    9 r" R' r7 {7 U11
    ) T3 C8 O6 l; N2 H, Y3 t9.3.3 区间的属性与方法
    % d8 }0 n( ~9 a  IntervalIndex上也定义了一些有用的属性和方法。同时,如果想要具体利用cut或者qcut的结果进行分析,那么需要先将其转为该种索引类型:
    % y9 N8 s. `% k
    7 Y) y' w- h3 r7 e' Rs=df.Weight, w6 Z6 `+ r* ~) [: V. k
    id_interval = pd.IntervalIndex(pd.cut(s, 3)) # 返回的是每个元素所属区间,用具体数值(x,y]表示
    % l/ A" y4 \$ _& V, ~8 ?id_interval[:3]
    ) K7 j8 L6 i( s" o' s  p; n& z; _1 L  J
    IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0]],
    $ ?. U1 t3 g. q                 closed='right',
    0 ~: Q+ d7 z% y/ N  o: p                 name='Weight',' Y5 s' ]! i; ~! b+ M; }
                     dtype='interval[float64]')9 s7 J/ F3 c2 T- J5 I2 d( ~
    1. N. T- s- R7 J7 f
    2, L' O' u5 t3 S$ |; c
    3
    $ r! ]1 H. q( z' C4 K4
    / H3 t: d4 x. }6 C5
    / R1 I1 c2 Q' w, @# v6
    * u5 P# R- |8 R. k+ ~- o73 |( l' p! X& S. U  V5 \6 U2 d
    8
    5 ^' ~# Y4 S) k5 F' r与单个Interval类型相似,IntervalIndex有若干常用属性:left, right, mid, length,分别表示左右端点、两 点均值和区间长度。
    ( A6 }' e0 D3 H$ ^) kid_demo = id_interval[:5] # 选出前5个展示
    3 B) A& s4 F, P' x, I  Q7 P
    0 y5 \0 W- P6 Uid_demo0 j$ g. l* {; c, P* J) M
    Out[64]:
    7 G$ g! ?2 Z( B6 i" e  cIntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0], (33.945, 52.333], (70.667, 89.0]],
    : s4 y  }. h9 ^* u6 ^4 A              closed='right',
    & f8 q2 U* B! N+ a% U              name='Weight',
    + X; I; \; F$ K1 i9 V0 ~, U              dtype='interval[float64]')6 J. O7 F9 s3 I3 m& t* _

    " p2 V; B3 D0 t2 Y/ F! ]. @id_demo.left # 获取这五个区间的左端点1 U+ f5 A+ Y: q/ e
    Out[65]: Float64Index([33.945, 52.333, 70.667, 33.945, 70.667], dtype='float64')
    8 A+ M7 L' U' }! ]8 f  m+ F, f! s& V- [+ X
    id_demo.right # 获取这五个区间的右端点; L. u3 a- ?: P4 e' [3 Q0 e
    Out[66]: Float64Index([52.333, 70.667, 89.0, 52.333, 89.0], dtype='float64')
    - R, e; [3 w* i# L. `% H+ L2 w7 _( e; ?+ y8 @
    id_demo.mid
    5 H8 R' V: k. T1 Y% m& I! EOut[67]: Float64Index([43.138999999999996, 61.5, 79.8335, 43.138999999999996, 79.8335], dtype='float64')
      |1 |" b* T& l4 a) |( i
    4 i5 ^7 T/ t7 q2 q' e. g3 x# R6 j0 mid_demo.length" \: X! g0 w% {7 U7 S* J
    Out[68]:
    8 ^: P* k0 f9 _1 kFloat64Index([18.387999999999998, 18.334000000000003, 18.333,
    5 ]4 {+ B6 X+ Q8 L              18.387999999999998, 18.333],
    6 D% c- p+ m  r& s             dtype='float64'): v+ v  o1 e4 ?. K

    ) {! m% m' {$ `" C4 I& t0 a4 z$ [1
    : a# O5 x( U3 h$ P. H20 g7 O1 X# @9 m7 D7 {  \- N
    3
    & K( c( b0 a* D+ ]8 j! z' c: P5 w8 ]; G4
    : F; F( W: a: {/ c1 F9 v8 N4 Q% e5  a" z$ ^2 z4 d5 K& s! k) O
    6; d- r- t3 u* s2 A2 O- R+ K0 ~, S
    7* Y( z7 O+ W" ?, D( n: H
    87 p  ^8 V& j' o5 Q# W2 U# A( A
    9
    , b* @! M) h/ ?; l( s0 Y" u2 J10/ Q( h& l- W! |+ s
    11
    4 }7 q/ a! C# g# W& \# r) h12# Q! ~: N( Z) |, p, {( E+ I6 i7 R8 b: Y  r
    13
    / ?- Z, m2 @. ^' K' n% j14& c+ j: \3 R7 W  b  B# Y
    15% F7 J7 T9 ^8 k+ r. }. x
    163 c$ ^+ Z3 t* W( S- z' D
    179 q4 i! z! ^5 b% N& r! H& O
    18
    " ~9 w1 c0 ?" s0 Y& m8 _7 O* P$ g19
    9 Z( p8 R$ \2 q6 j20
    9 c0 c; `2 N; T, c$ @' g1 w! G21; t" m* [3 c. m5 M; _6 |9 t+ m
    22
    - f- _- x1 Y; z% X23
    # _! w" t6 G  s' C7 ?IntervalIndex还有两个常用方法:
    9 w  S! g+ t6 f5 ?" I$ B9 m5 D, Pcontains:逐个判断每个区间是否包含某元素. i' t& c, D. D, ~
    overlaps:是否和一个pd.Interval对象有交集。
      B* F+ [! C# b* E) [5 `) Jid_demo.contains(50)
    9 ]! N$ ?8 o7 j* Y- j6 `; UOut[69]: array([ True, False, False,  True, False])# s( k6 w3 N5 ~" w9 \+ A
    9 M( u9 L% _. J4 ?" |' g
    id_demo.overlaps(pd.Interval(40,60)); Z& g8 O! e2 D0 D' {" T
    Out[70]: array([ True,  True, False,  True, False])
    7 k0 W' H- I* |% N12 D5 d8 \+ [+ I) E8 V7 n5 \, B
    2. [8 G$ i5 X2 P" ?8 g6 a+ F
    3
    ( {$ w# d0 u' [; i3 C) d" g4  F" f- z8 m2 v, }' T; ^
    5
    2 k, `" D5 Q- p  |2 q+ r0 K9.4 练习
      k* u1 K* Q4 q1 B1 OEx1: 统计未出现的类别
    1 X9 t/ Y7 C: O! [- {3 V' @  在第五章中介绍了crosstab函数,在默认参数下它能够对两个列的组合出现的频数进行统计汇总:
    + M  s( U5 Y6 V2 f  z: b
    0 w( c* U) _( U$ T) Wdf = pd.DataFrame({'A':['a','b','c','a'], 'B':['cat','cat','dog','cat']})
    0 g3 g0 p5 a3 V& D9 E5 ~pd.crosstab(df.A, df.B)! A- y4 E) I4 a8 b
    7 B$ z/ }2 e5 m7 m, q
    Out[72]: ' ?( L  I! q1 M. o4 ^
    B  cat  dog
    . y; V6 U& w* ~" K) FA         
    ; Q7 R3 K7 y( F% U7 w& O; x" @a    2    0; S$ D9 d' L) F7 }3 m7 r
    b    1    0
    , U6 v7 k/ a9 uc    0    1+ j& q' a- @! t  o- i2 A' Y7 U" Y
    1
    ' m  ?$ m; o" t) f1 `+ ~( X2: k6 C8 c' z4 T) L7 e" U
    37 p. w$ \# C! s) c5 h! H
    4% v, _0 C+ c6 V/ J- ^' b
    5& t6 {! G; y. j5 `1 k' d
    6
    ' Q$ H1 T8 p, x6 V5 f7
    , O3 d  F$ F2 y2 `5 ?$ Q9 v8
    5 U% |( K. G0 q+ l2 V9( [  f, @. j6 A3 |, @
      但事实上有些列存储的是分类变量,列中并不一定包含所有的类别,此时如果想要对这些未出现的类别在crosstab结果中也进行汇总,则可以指定dropna参数为False:" m: V* x# D( L/ ]# x- C2 L
    ' B; b/ V3 T1 K8 E: {6 ~
    df.B = df.B.astype('category').cat.add_categories('sheep')
    4 O% J+ N5 m- n7 f) Q/ Qpd.crosstab(df.A, df.B, dropna=False)( Q4 P( A% Z& X9 B/ Q% n8 p4 M' V7 r
    # g9 p: c, ~2 ]8 _
    Out[74]:
    ! ~9 ]' j9 a- g; O1 p6 mB  cat  dog  sheep
    , {# A! L' ~( I( |  I, S# aA                 4 G1 [6 |/ P8 o
    a    2    0      0
    7 _4 J6 k) C0 C/ b$ `$ ]b    1    0      06 O( F4 K2 }$ G7 j1 U$ i% r3 }; X
    c    0    1      0- S" Y& z0 }" I0 N$ ~
    1
    ) s6 B( J. x! v* x" q. Z/ Z2
    6 |* r- ^7 E! t) ]% r: g3
    * u3 P6 i- B6 F# T2 \4 J# D9 [# S4 L; A4
    ; z* ?' x/ I% ~5
    " Z8 F" {4 l; Y. l6( T2 ~5 v( [) O+ t
    7
    - l, s( `5 w, w3 m6 d- ~7 N81 T% s' f9 y4 H; Y- f0 k( k. J
    9& R  b; c/ F1 H; }9 [4 u
    请实现一个带有dropna参数的my_crosstab函数来完成上面的功能。7 R- _1 q0 G8 R) A8 Z7 Z* N' v

    , N5 ]8 I- T8 Z. FEx2: 钻石数据集2 @4 m  E2 u9 T3 g
      现有一份关于钻石的数据集,其中carat, cut, clarity, price分别表示克拉重量、切割质量、纯净度和价格,样例如下:
    9 n& h8 O" E; i2 t: y8 B' \& K+ X% F8 T' ?4 B2 p8 X# ^
    df = pd.read_csv('../data/diamonds.csv') ' I/ e9 B7 x% w  @0 L$ d
    df.head(3)6 k7 T) l& }+ L; ]: Y
    9 T, R/ _4 [5 Q  N, N
    Out[76]:   P5 G1 q9 ?3 o" w+ S/ j& K+ U
       carat      cut    clarity  price& e! f. D) I; g4 A9 y2 d# w+ R3 b
    0   0.23     Ideal     SI2     326% P" y  u& H' v  z
    1   0.21    Premium    SI1     3260 h: p" Q, _9 i2 U
    2   0.23     Good      VS1     327  o/ M' i; d, O8 r
    1$ e& {2 N! E, ?$ }0 r6 |* i3 Y
    2
    " X" _/ |- d7 v* ^3
    1 p( E, i/ f" U, Y/ w* n4
    % q) m) i1 z. P/ K$ U1 e7 |5
    " `# R) _! c( h. d  h% X' J( e6
    : r, ]* b8 K) i0 S3 h7
    - i* E0 w) v. A0 D8  G( i/ }! q  ^3 s7 G8 i0 {
    分别对df.cut在object类型和category类型下使用nunique函数,并比较它们的性能。
    2 x% t/ _  ]( K/ n' P# i钻石的切割质量可以分为五个等级,由次到好分别是Fair, Good, Very Good, Premium, Ideal,纯净度有八个等级,由次到好分别是I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF,请对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。
    & Z7 C" M8 s% A! z3 I; R* p' E分别采用两种不同的方法,把cut, clarity这两列按照由好到次的顺序,映射到从0到n-1的整数,其中n表示类别的个数。
    6 B- J# X, u, H对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。9 g/ z1 O( h* u: `+ b. @8 Y4 L
    第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
    * u+ o5 t9 q2 K% y- K9 W/ a; ?对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。( o, ]& Q4 X% {: V. L5 d$ t
    先看看数据结构:
    / ]: A6 k/ D/ T* h; q: `2 S; B9 x$ o; I1 H) ^0 x. Q
    df.info()# o& c! h" m3 j$ G: l) k* N6 i$ U
    Data columns (total 4 columns):
    ( ^0 ]$ A6 ^, o2 b! W& h% U8 w: f #   Column   Non-Null Count  Dtype  " }3 t2 X0 J' z! [- u
    ---  ------   --------------  -----  
    , T: b2 I/ N. y% ?7 T" y 0   carat    53940 non-null  float64  `5 G0 J7 m' k: f. |4 B: ]
    1   cut      53940 non-null  object ; f! N+ q7 @3 d7 g
    2   clarity  53940 non-null  object " K0 ~; x$ v" j. s* S
    3   price    53940 non-null  int64  ( r7 U  s2 y% H0 R- _, _( U
    dtypes: float64(1), int64(1), object(2)
    $ A' H8 v; C+ N) S& a* I13 v3 S+ S* w) L* ~2 U
    2  S5 A. c( l! U- e
    3
    $ o$ O* j4 k5 X1 o1 e+ p) K2 Z  y4* R/ B. l6 q/ v' x
    52 Y. Z. G" L4 ?8 M" F9 Y
    6
    " m" p' s% q$ c* b77 u% v. a- V/ A/ R2 y  u
    8
    9 t2 K$ k, l7 _$ I3 o! O+ ?  w9
    5 G8 S$ `* k2 D- p7 A  x比较两种操作的性能2 F# K  B. j0 X0 v6 U& @" V
    %time df.cut.unique()
    4 T/ B& p# q1 y
    $ B/ C0 X  p6 A, j' z& ^# r  d& aWall time: 5.98 ms
    ; j8 z/ f" d2 Jarray(['Ideal', 'Premium', 'Good', 'Very Good', 'Fair'], dtype=object)
    3 k2 b( Q7 x5 Y3 a6 k" x  s6 o1; p! C- f2 [) l! E2 N+ N
    2' K: t( d- [) n& H
    3* @1 k" ~! D" g- S4 Z
    4
    # H% ^! M, N4 G2 Y+ f- r%time df.cut.astype('category').unique()
    . g7 R+ j0 q& g" L) ^+ {
    2 ^! q3 D# g4 z# y( d' C) NWall time: 8.01 ms  # 转换类型加统计类别,一共8ms- N4 `5 V8 M3 n  x. U' Y- O
    ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    6 A" _* `& U  _Categories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    1 o  _3 J6 Z' u! E% ?1/ J, Z/ m0 _0 c
    2
    ; Q7 n/ r6 p9 q) n( {) A3 E3
    , o7 ^; Z8 O/ d' h45 p1 O8 E7 _6 L$ b7 m  d. q. H
    5
      S4 Q4 B( I" F% S4 F! |df.cut=df.cut.astype('category')# p% @5 g9 Y8 u+ {0 h8 f# y
    %time df.cut.unique() # 类别属性统计,2ms
    . Y5 D+ m/ _1 n) P' Z6 ~
    % B- Z* B8 Z' k4 u" V. Q2 KWall time: 2 ms* s' @4 E2 ]; N
    ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']( |& S0 Q, t* W
    Categories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']; j, P+ D" Z+ N) y
    1  A! z3 O) }  ^( j' L/ @# ^
    2
    * L; o& s% Z& @8 ?  i: o3
    * O! j7 V8 H, `% ~42 F. S: T: q2 X( r% f/ m' W" E
    5" R8 [; X1 J+ p
    64 ~- t! @* X" `' ~4 g
    对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。
    + i+ f& d+ O4 T6 F/ r# P# Qls_cut=['Fair', 'Good', 'Very Good', 'Premium', 'Ideal']
    4 x2 L6 G  L2 ols_clarity=['I1','SI2', 'SI1', 'VS2', 'VS1', 'VVS2', 'VVS1', 'IF']
    4 Z& \* M' ?5 S- ~df.cut=df.cut.astype('category').cat.reorder_categories(ls_cut,ordered=True)  # 转换后还是得进行替换
    4 w& L+ B) b0 {' Ddf.clarity=df.clarity.astype('category').cat.reorder_categories(ls_clarity,ordered=True)
    1 D/ O0 @  }' W7 F
    + _, ?8 k: U7 h, ^df.sort_values(['cut','clarity'],ascending=[False,True]).head(3)' S5 q) c& e7 R( a" Z/ Q

    ; Q" Y1 i1 F5 d/ |. d4 i        carat         cut        clarity        price3 {. G, S7 p/ Z- `$ ]6 l
    315        0.96        Ideal          I1        2801
    7 P" c! L8 @4 S( n) S535        0.96        Ideal          I1        28266 X2 R8 D8 N# Z" Y5 K7 N+ n# t$ `
    551        0.97        Ideal          I1        2830
    2 g: X' l  Q: A4 @$ p6 b* Y  |9 e1# g# g4 C0 i- L4 r) K) F! k
    29 _/ V  w: i4 q# Y" _
    3
    3 t: s& R- t6 M. I3 y4
    3 z; X# {) E. ^8 T! Q5
    . J7 H$ H( F1 Y6. p( z/ r/ w7 o0 e# {  }
    7' g; o1 C% D$ S# L
    8- X: X- B! h# \( R0 N
    95 J" |9 b! H! o( `1 w( x4 N
    10
    8 t4 B4 c/ Y/ K, M11
    , B7 \3 R" v( ?8 N0 A分别采用两种不同的方法,把 cut, clarity 这两列按照 由好到次 的顺序,映射到从0到n-1的整数,其中n表示类别的个数。
    8 ?, l2 I1 Z2 m# 第一种是将类别重命名为整数9 h& X3 D" C5 _; J9 D% v- H* a
    dict1=dict(zip(ls_cut,[x for x in range (4,-1,-1)]))
    " d+ I/ ^9 e1 ldict2=dict(zip(ls_clarity,[x for x in range (7,-1,-1)]))6 F+ [8 k6 J/ p7 c- |- z

    # s  J$ n6 ~# v: Rdf.cut=df.cut.cat.rename_categories(dict1)
    8 f7 R. b2 L& ^4 Qdf.clarity=df.clarity.cat.rename_categories(dict2)
    5 q/ s( q% \* t7 @' F4 g' f' Q7 }' Qdf.head(3)
    4 D; _% ~, q  _. K( t% F2 s" r6 [% i) ]. R
            carat        cut        clarity        price1 l2 o- @3 W4 R
    0        0.23        0          6                326
    - N5 x: s" P5 V) z2 E) ?1        0.21        1          5                326! X2 s) w; ]& V4 r3 `; x
    2        0.23        3          3                3276 R! t# o0 y' g9 d7 E0 W2 S0 l
    1
    ; f" p4 a) p& P9 ^% B4 B" {2& j& ^# E0 h1 G/ J2 W
    3
    ! `7 ?! |; ?  P5 _48 y) R: y8 K% a- p0 b8 J" i6 e# |
    5
    , x* b: f% s) O9 r- X! g7 s66 D& {/ I* [' L6 {
    7
    , K% v3 I  `1 K# q7 A. n+ H8
    # f) v/ K9 ^1 Q/ F1 z6 R9
    9 U" ~. Q* r* j; h10& D3 R  m, ?: J
    11
    ! d' ]. x$ f( K+ |- r. I- J% H125 p- a/ P3 y* d/ B6 C& ]1 f
    # 第二种应该是报错object属性,然后直接进行替换
    ) {1 _6 z: S* Y2 s; |  Gdf = pd.read_csv('data/diamonds.csv')
    3 ^7 ?- w9 d- n5 m5 zfor i,j in enumerate(ls_cut[::-1]):
      B, j; R' T( W) R& ~    df.loc[df.cut==j,'cut']=i
    ' @2 l' [7 z* N% I! M& W7 k+ M
    3 @# S% {, _# B! hfor k,l in enumerate(ls_clarity[::-1]):
    2 V( U# z5 @: ^( b    df.loc[df.clarity==l,'clarity']=k
    8 ~6 T& R6 s! t7 N. Z! Ldf.head(3)* `  f& D. P$ x+ Z" Q
    $ |9 T$ z4 O* Y( b( |1 H
            carat        cut        clarity        price
    4 r2 \& Q* ^3 `' ?5 ?$ b0        0.23        0          6                326
    6 f6 y" T$ I, |6 i+ j) O$ e' m1        0.21        1          5                326
    1 D' I" v, R9 Y7 |2        0.23        3          3                327. Z" r% [* s1 P5 y% x! n
    1
    0 \% P0 W& u$ F2
    ) U1 ]0 }9 w& d3 R9 c  A  k! X3- o6 d- f) S5 W9 X2 |, n
    48 w5 y6 n; T# B' k( h" y
    5
    , a: I6 ^: g* E. \; h6
    : l' z4 K$ P) [2 c2 R# b7
    5 X8 D1 o5 r5 z8  U8 L. u5 ?4 ~- z" S
    9$ m& H7 Y2 |! w' K( i% n. y
    100 {' r' o) ^8 n
    11
    , l1 ]+ m# B) r, I5 E12
    : I. G# M& r( Z. x& r: }- Z. E13
    + V( q2 }* h0 J3 ]* n对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。
    9 [" C0 }+ W# @4 Q- w# retbins=True返回的是元组,第一个才是要的序列,第二个元素是分割点
    3 Y7 t0 O7 Q5 Q9 R/ y5 ~' @3 i3 ^! \avg=df.price/df.carat
    4 `- g* ~, d2 A* C
    6 \9 r# ~" e+ tdf['price_quantile']=pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],
    % w/ d9 v  ?, s& y                              labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]$ n( t- T2 X4 X; M* f- U# V( F$ j
    , C* P& l+ f5 k, P0 }
    df['price_list']=pd.cut(avg, bins=[-np.infty,1000, 3500, 5500, 18000,np.infty],1 H; l* g6 Y! R: f9 }
                                  labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]- R- d# R+ s: M7 |& k) L
    df.head()
    5 a% l2 n7 P* L) a8 q4 c; M# _+ s3 M* _% f
            carat        cut         clarity        price        price_quantile        price_list- a$ \3 x9 h) X) y
    0        0.23        0                6                326                        Very Low                Low* z% K3 j# I, a* _8 p( P
    1        0.21        1                5                326                        Very Low                Low% x7 V" K8 D$ N- A5 t: U3 H
    2        0.23        3                3                327                        Very Low                Low- l6 ~+ v" }. u
    3        0.29        1                4                334                        Very Low                Low& H! u# R1 Z/ q4 `
    4        0.31        3                6                335                        Very Low                Low                                       
    2 N7 m0 |' z% H, K% h$ G/ d8 c% ^% w6 F+ y) }! d$ B; }
    18 B. Y) w  b! w9 R- {: }3 Q& p1 Y
    2
    . q2 j% y3 X' W; O3
    0 n5 W) u( W3 X4
      O6 q9 t$ _* z& y5 P5* h) L5 [: S% {( U6 _# n" h5 h5 d+ K
    6
    5 Z0 B3 U1 A6 g& `  Q7' u8 N" x( a, n
    8
    # C/ o+ S6 z9 Z- n/ d8 k9) Z- J0 _6 y; ^% f9 p' q
    10% c. P( c" [* y- ]1 G( |9 r
    11
    0 b) m  L0 {- R& {) m; L12
    4 ]8 C- m% _- c" y13
    # t# U$ Y0 r$ y% m$ s8 B# e14. ^1 N8 ?9 y: _, g! T) ]5 T
    15
    # E: O4 Z0 @% Q6 z4 V: D5 [! b( P7 C16
    $ i5 C3 K# J9 R8 S! E6 \分割点分别是:
    . P$ H3 n* ]' r* X7 Z1 B  D
    5 L5 U5 k! `7 d) `) |, I& harray([ 1051.16 , 2295. ,  3073.29,  4031.68, 5456.34, 17828.84])3 W# n! V& j# P3 _9 t5 M: y
    array([  -inf,   1000.,    3500.,    5500.,   18000.,    inf])
    6 }! b! a: B# c  S1 f. n! ^. S+ B  C1( S. U% F5 B3 Q: e( L9 l4 v! w
    2& R9 I4 x. ?* R% \  P% {9 \. h
    第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。- Z: m0 y6 E4 ?8 r
    df['price_list'].cat.categories # 原先设定的类别数! r* ?" ~- i, y, h3 ^- c; e
    Index(['Very Low', 'Low', 'Mid', 'High', 'Very High'], dtype='object')4 x4 I( S8 z$ S* g9 o! I4 F
    5 P% H# B5 M- U* U% ~0 t! V
    df['price_list'].cat.remove_unused_categories().cat.categories  # 移除未出现的类别
    + ]+ x) L# I! P: fIndex(['Low', 'Mid', 'High'], dtype='object')  # 首尾两个类别未出现, m, u0 m4 j/ _' O8 u
    1) T2 h, d% |5 }3 Q2 f. V
    2* ?  h" c# L3 V* Z! v& A. B
    35 s* e2 h. p) y+ c' B
    4) u0 c1 h2 m! H* ~
    54 ]7 c" I9 `+ _" U" n
    avg.sort_values() # 可见首尾区间确实是没有的0 \# Z/ u' Y. Y$ {$ I. M/ k
    31962     1051.162791" D0 ~' _& e6 a) W! l: x% C
    15        1078.125000" n; J+ ?/ L. [! C( L: O5 N2 `
    4         1080.645161
    6 @7 a" L; Q0 `2 y- f28285     1109.090909
    & f3 r# N+ u" \. a$ r13        1109.677419) R  p1 E; X' w6 k) b
                 ...     2 A1 L7 ^7 e$ {% A
    26998    16764.705882
    9 `/ V& D1 n3 L: ~) j27457    16928.971963. Y3 O! _) E- y' t% h
    27226    17077.669903
    : w, V- D) m" H1 v0 }  q27530    17083.177570
    ' T2 ^9 _& p8 Z+ g# V27635    17828.846154- k7 y" h: Q8 H+ Q% c
    1
    5 }+ j! B2 H( X; U0 _* r! ~2( e6 |2 t+ @# `$ E) @# F9 y
    34 h0 Y# ?* G- [/ E
    4
    7 J. d; L( [( S- _53 \  }5 _) o3 R7 W; q  |  J. {9 p
    6% ?7 s; K( u, N8 t: B1 Q
    79 p0 n$ h' |0 L( w' h
    8
    , Q2 H1 ~; |$ f) K% T4 A/ k9
    5 {, f5 L" L) m+ `9 R10
    8 R. j; a+ _$ [' w2 F118 f. e+ v, \) e% Z# ?9 y2 O- I) b
    12
    % n$ b' X" I$ e: i" u3 {) q对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。; q6 t( k" V( ]5 J" ^; t$ k
    # 分割时区间不能有命名,否则字符串传入错误。
    3 R. M3 ~4 N2 V1 q' N; u6 Did_interval=pd.IntervalIndex(
    0 V8 Q* T# s  [5 N: L" t    pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],retbins=True)[0]) Z2 f3 R  t! `& o' ^
                                )
    / t8 W6 i/ M# {# _id_interval.left
    7 O5 |$ L( ]4 R, y! Bid_interval.right4 m& y2 U7 S/ h; H. D
    id_interval.length                           
    9 i/ W# J' _8 N1% h! t% ?$ J, G0 E
    2
      Q& O  i: \' S! ?& F" Y33 U! a0 u% C/ y2 b4 S6 @2 X
    4
    . G/ y6 h0 G1 C4 n9 N: C5- p+ y+ b: y8 Y, F
    6
    ) c7 B; P! @0 l; b! X$ Q6 u7
      `* `& |5 Y" o0 O! M第十章 时序数据+ D/ k. a4 O9 e! X8 w
    import numpy as np
    " X% W: s$ e  P- Y" a/ V& t& dimport pandas as pd
    " s  }; p0 f0 X" j; G9 X7 B2 p* r7 S14 Q. ~0 W( p1 N' O1 ]) K# Q
    2
    ! F. @% Y5 a! i" ]6 v
    ; {' l1 S' Z6 [* Y- @' ]+ R/ b0 B& ?
    10.1 时序中的基本对象
    . B% M- L8 r0 q7 ]  时间序列的概念在日常生活中十分常见,但对于一个具体的时序事件而言,可以从多个时间对象的角度来描述。例如2020年9月7日周一早上8点整需要到教室上课,这个课会在当天早上10点结束,其中包含了哪些时间概念?
    2 X+ H2 p# p  _
    . g# x2 D, e+ ?9 I6 J( [2 D会出现时间戳(Date times)的概念,即’2020-9-7 08:00:00’和’2020-9-7 10:00:00’这两个时间点分别代表了上课和下课的时刻,在pandas中称为Timestamp。同时,一系列的时间戳可以组成DatetimeIndex,而将它放到Series中后,Series的类型就变为了datetime64[ns],如果有涉及时区则为datetime64[ns, tz],其中tz是timezone的简写。/ ^9 E; Q& R2 }6 U; n8 r

    ! [0 |# n7 h; X* E会出现时间差(Time deltas)的概念,即上课需要的时间,两个Timestamp做差就得到了时间差,pandas中利用Timedelta来表示。类似的,一系列的时间差就组成了TimedeltaIndex, 而将它放到Series中后,Series的类型就变为了timedelta64[ns]。
    9 H  Z) O6 ~- W" s0 e( |8 [. C: v: l$ z, K5 u( a; p4 z; a
    会出现时间段(Time spans)的概念,即在8点到10点这个区间都会持续地在上课,在pandas利用Period来表示。类似的,一系列的时间段就组成了PeriodIndex, 而将它放到Series中后,Series的类型就变为了Period。
    . b/ g7 _. }) A, r. {# w! d% b7 f6 _0 f
    会出现日期偏置(Date offsets)的概念,假设你只知道9月的第一个周一早上8点要去上课,但不知道具体的日期,那么就需要一个类型来处理此类需求。再例如,想要知道2020年9月7日后的第30个工作日是哪一天,那么时间差就解决不了你的问题,从而pandas中的DateOffset就出现了。同时,pandas中没有为一列时间偏置专门设计存储类型,理由也很简单,因为需求比较奇怪,一般来说我们只需要对一批时间特征做一个统一的特殊日期偏置。! G' H8 P4 ]: W6 X0 M+ M1 H% y

    " @: B( E" a: A2 }$ S  通过这个简单的例子,就能够容易地总结出官方文档中的这个表格:$ ^! J$ C& p: d& g' S) s
    & [% k  }6 N7 d% C; y1 w* @  d
    概念        单元素类型        数组类型        pandas数据类型
    / s* k: k# W6 B- u* i/ [0 bDate times        Timestamp        DatetimeIndex        datetime64[ns]5 ^  a  j3 t6 G9 ^, W
    Time deltas        Timedelta        TimedeltaIndex        timedelta64[ns]
    : q3 _7 l+ X- U( K4 e$ X9 gTime spans        Period        PeriodIndex        period[freq]( j% P$ _2 D( c5 D: [% \" |+ z
    Date offsets        DateOffset        None        None
    . ~2 c; E- \8 a* `" v  由于时间段对象Period/PeriodIndex的使用频率并不高,因此将不进行讲解,而只涉及时间戳序列、时间差序列和日期偏置的相关内容。
    # {- K" b& D8 w0 u/ o" }. \
    % R; p9 k' [' [  w10.2 时间戳
    6 E* Q+ i6 D/ b10.2.1 Timestamp的构造与属性& L  n) _1 ~3 g) M
    单个时间戳的生成利用pd.Timestamp实现,一般而言的常见日期格式都能被成功地转换:
    ' @6 u$ E% x/ F. g6 R7 }" w& k& l+ u9 G- ~5 H
    ts = pd.Timestamp('2020/1/1')
    0 S9 n! {' P" p1 k5 L/ ?4 _  l) I5 E$ o/ D0 x8 r* r$ h
    ts
    % h: W2 D1 w# U) \& c; |, B7 T' j% aOut[4]: Timestamp('2020-01-01 00:00:00')& G' @9 [  Z3 u( k4 X5 K* l

    ( D9 [8 v! c4 w$ q7 Z2 i0 X5 Sts = pd.Timestamp('2020-1-1 08:10:30')
    : m2 t; g" J  F" i2 e9 n6 P4 ~: J& s9 f& Y2 \
    ts. K# t, q. i# X2 ~& w
    Out[6]: Timestamp('2020-01-01 08:10:30')
    - p" N( }5 Y! {9 r/ x1# h+ S/ N# ^! O
    2
    : l5 n+ x- X1 i* [$ W) u8 m3) U. J+ w/ ?, D. u. C3 V3 r
    4$ j, n0 C$ F- ?3 l  j4 R5 B
    5. C, h# z0 z) U4 c1 }- k# Z( K
    6; O0 S, g  ^% y, I# \
    7
    / r/ a( b6 ~9 e  L5 }6 r8/ x* ^% [2 |0 x7 f# n
    9
    $ U1 I2 J' Z5 m0 P8 [& J/ A9 e通过year, month, day, hour, min, second可以获取具体的数值:
    ; o* ~5 R& P9 V6 X+ {* B3 Z1 I
    2 m7 B8 ?( q. D9 M8 x/ sts.year
    : H( L/ {4 Q, e4 @. wOut[7]: 2020
      Q! S0 M2 M  r" R- a' c& f/ v4 s6 X" \
    ts.month* @) @( p7 [# g3 m- b- Q
    Out[8]: 1
    + i. d/ q3 m$ k- _; G4 V/ e3 ^# E' |7 b
    ) B9 A( `' ^8 B# h; Hts.day
    + E/ `& y$ E& n- I4 hOut[9]: 1
    4 @9 Y0 x: q3 R  Y
    ! g& d9 a8 `% pts.hour- C( |; l" t% j/ I0 z
    Out[10]: 8. m, T' K* N: ~. b

    2 y* o; [, ]1 M7 N3 |0 [0 ?/ Dts.minute
    6 Q9 R( z8 i( D  P% EOut[11]: 10
    5 B# m' V' E- d, j% L) D2 ?( t' j# R, ?
    ts.second
    $ t% f8 {& d9 h$ F$ WOut[12]: 302 o% D# `( ^3 r

    ' E' \  d4 R1 X+ F0 Z; o" z1, @. P/ O! d2 v2 m& |4 ?
    2
    ! ?# B: I4 K9 ?! G3
    ( k6 B  K0 L3 I/ G5 ^3 [2 g4
    & u8 Q* A$ I+ w2 ?: O4 m5
    , H% K' Q  B( o7 n9 n5 z6( t  s4 x; v; k& J
    7
    3 F3 T5 ]1 b: F; B- Z% D8  a" X4 X: D, ?& K
    95 R- k- y' N" E" T
    10
      _" W& b3 X! V6 Y& V% D; e11. w+ O5 d! J% t& o
    12
    7 J3 _+ R4 ^7 }1 c6 b$ Q13" L9 Q3 n4 y# U# X7 @" H
    14' h) W, E8 s+ T0 K9 _
    15
    ) k- o2 l9 i3 Z# ?, G7 y! t* p: L163 j0 f6 v0 U* H3 }( z' ^4 {* U
    17
      T5 |4 \: V! f$ l6 u9 ]+ v( Z# 获取当前时间, e# B" i% f# Q- L
    now=pd.Timestamp.now()7 M: C. h# d% s! k
    1$ h: C7 a  N- _( I8 S- h
    24 c, {8 h0 ^! g" M1 b  L
    在pandas中,时间戳的最小精度为纳秒ns,由于使用了64位存储,可以表示的时间范围大约可以如下计算:7 v: b3 O1 X! z+ g- y! B* E
    T i m e   R a n g e = 2 64 1 0 9 × 60 × 60 × 24 × 365 ≈ 585 ( Y e a r s ) \rm Time\,Range = \frac{2^{64}}{10^9\times 60\times 60\times 24\times 365} \approx 585 (Years)
      B1 [- }; A$ m" [3 D: YTimeRange=
    , x1 Y4 a* E' o, i) J# m- _10
    ' ~4 q$ O  q/ p8 H1 O9 Z) t! s& |9' {; G3 u. X0 ]( G& u2 e$ o6 ?9 G
    ×60×60×24×365
    . n# G8 p8 k; N# |; t2
    1 ~4 T1 w& Z/ |* H) M9 ^" r( \) v" G64
    $ m9 P) T' U. {+ X+ q" c1 H% Y* m# z- a0 b0 E8 d

    & F: V1 d; ^: p# b6 C  s4 p: S& x' G ≈585(Years)
    & [4 B  ]& {1 b- p8 |- s' Y) Y- _( g
    通过pd.Timestamp.max和pd.Timestamp.min可以获取时间戳表示的范围,可以看到确实表示的区间年数大小正如上述计算结果:
    / Y( P$ T: }" j* M2 v* D
    $ i3 K& Y5 p6 U# v6 N7 Z  bpd.Timestamp.max
    # y( b! t1 e. q/ Q" z- v& ~8 ?Out[13]: Timestamp('2262-04-11 23:47:16.854775807')( T- j# {5 B& |& t

    : j6 f  V- L1 Z+ t5 v5 P# A1 {pd.Timestamp.min
    5 Y6 v8 W: E% l7 Y6 s9 y2 Q) y. i1 eOut[14]: Timestamp('1677-09-21 00:12:43.145225')3 f, C- o$ `8 B8 g: A0 [

    # P! w  u# \" F; Xpd.Timestamp.max.year - pd.Timestamp.min.year
    8 n& T# c& J, T; X6 {( Q* ^Out[15]: 5859 Z3 R, k# S% r- U" D; y+ Y1 B; K  ^
    1
    5 W( O, p0 F. r5 X2
    ( D! [; `7 p4 h, Y3( v3 z7 l- Y5 a8 [' X
    4' j  T7 n$ _, C2 D. U
    5
      y$ {6 v! k2 v$ k6" G4 t8 b: T0 l, J5 U9 Q' {) K  I4 C
    7/ h, U) p+ f8 g8 L; r0 `
    8) a: F) d6 ?, @/ H; a4 W. P) v
    10.2.2 Datetime序列的生成4 d, x9 i+ M8 `% R( F6 M, W1 T
    pandas.to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, utc=None, format=None,6 n, [& C. X6 i5 d- Q: S
                                      exact=True, unit=None, infer_datetime_format=False, origin='unix', cache=True)
    ( p. k# c) F$ V6 @# W1
    , k7 X+ e" w9 {" H( y2
    ; W7 S3 y; E! Y9 b& `pandas.to_datetime将arg转换为日期时间。' y; k! E: S, A* I( c1 _
    1 e* E% y$ T( P, V
    arg:可以是argint、float、str、datetime、list、tuple、一维数组、Series、DataFrame/dict-like等要转换为日期时间的对象。如果提供了 DataFrame,则该方法至少需要以下列:“年”、“月”、“日”。
    * ^( d" S8 j# T9 K0 s8 {errors:
    ( l2 K4 }3 m! U& J* ]2 Y5 t- ‘raise’:默认值,无效解析将引发异常
    4 A& Z2 X" o8 C- ‘raise’:无效解析将返回输入5 H6 ?) D* z/ v6 t& r  ]
    - ‘coerce’:无效解析将被设置为NaT
    , o% u+ F; D3 s" H7 o; F' Hdayfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析日期,例如“10/11/12”被解析为 2012-11-10。如果无法根据给定的 dayfirst 选项解析分隔日期字符串,会显示警告。
    # e" ^) k% F- Y. n8 Gyearfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析年份,例如“10/11/12”被解析为2010-11-12。无法正确解析时会显示警告。(如果 dayfirst 和 yearfirst 都为 True,则 yearfirst 优先(与 dateutil 相同)。)
    . z3 N' g$ `$ E/ Hutcbool:默认None,控制时区相关的解析、本地化和转换。请参阅:pandas 有关时区转换和本地化的一般文档
    3 r! U' f/ M3 B, T( Eformat:str格式,默认None。时间戳的格式不满足转换时,可以强制使用format进行匹配。
    0 a; B* J; U9 X' aunitstr:默认“ns”。它是arg (D,s,ms,us,ns) 的表示单位,可以是整数或浮点数。这将基于原点。例如,使用 unit=‘ms’ 和 origin=‘unix’ (默认值),这将计算到 unix 开始的毫秒数。) g% K4 _" J$ W# J
    to_datetime能够把一列时间戳格式的对象转换成为datetime64[ns]类型的时间序列:5 \8 n* }8 r) s) P3 j2 L, U
    pd.to_datetime(['2020-1-1', '2020-1-3', '2020-1-6'])4 h0 m  J7 \6 F/ G/ r( y' {5 u5 T
    / \% {" v" Y- {% Z8 @
    DatetimeIndex(['2020-01-01', '2020-01-03', '2020-01-06'], dtype='datetime64[ns]', freq=None)* T2 u) Y8 N- K; ?0 \& H
    11 q3 A; j$ ~! _# P, H
    2
    ) p0 B$ |# f2 N' o( O; V3
    ( ?+ D/ W* V  q, Q在极少数情况,时间戳的格式不满足转换时,可以强制使用format进行匹配:
    . A/ y7 r* K1 p4 D/ P( H
    $ D$ E8 b3 L! A) X' a1 rtemp = pd.to_datetime(['2020\\1\\1','2020\\1\\3'],format='%Y\\%m\\%d')/ y2 v- a2 p5 q) [& N
    temp; I4 n; L1 F- I% S% @

    9 {* u4 F3 B) g1 O4 `# c% XDatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)
    0 d8 p- G5 ~! V: d# C6 M1
    " {" c/ M5 N& o# p7 S2 ]4 m' M2
    % O: a; `5 @: k/ V& L3
    : C6 [4 N8 |$ v8 Z- x: v4
    5 ~% H0 V  }0 s: C$ @  注意上面由于传入的是列表,而非pandas内部的Series,因此返回的是DatetimeIndex,如果想要转为datetime64[ns]的序列,需要显式用Series转化:4 A# G+ ^! u. Q: ~$ W

    * S: Q) I+ g* g; |/ T! l! bpd.Series(temp).head()
    ; S  @" F8 }& A* W5 F8 L0 r
      B/ t( N. E1 Y( i0 U0   2020-01-01
    & B( R8 i; A2 [) G0 j1   2020-01-03
    1 {0 T. Q, M0 vdtype: datetime64[ns]
    - h% X' s0 ]) @# y1- p, \# F) {% G7 c
    28 Y: w6 I8 L( \' h  {5 _- y3 R6 H2 G
    3) C, \  e5 a! T$ U1 Y
    49 `. r' e7 p5 E0 o: Y" k# \
    5
    1 q* V% V) \9 D; o- z$ m. _1 M下面的序列本身就是Series,所以不需要再转化。' q9 C0 e4 f' ^/ ]: X( k: T' t
    . A0 d- v0 J8 Q+ _2 P7 ]. n
    df = pd.read_csv('../data/learn_pandas.csv')
    * d: @3 x0 T" `4 a( us = pd.to_datetime(df.Test_Date)1 m' {3 |8 x& `2 k: s
    s.head()
    % |5 c7 n/ M3 u& \0 y5 Q, _* D2 X* H# B: D+ n  y- g
    0   2019-10-058 G/ Q1 K; y: R7 X9 K+ Q
    1   2019-09-04" ^/ p2 q4 E( D# M) D/ e
    2   2019-09-126 ?3 T4 X6 F8 r  H" I/ t! \7 [( Y
    3   2020-01-03
    # Y+ B9 m* @. B% ~+ W4   2019-11-06
    1 d8 ?8 e6 s; O9 v# n8 u# D; z# z' lName: Test_Date, dtype: datetime64[ns]. K/ m& d% ^$ m  a, c
    1
      j$ k$ y+ O2 Z9 R( U0 g0 |# \25 _. j6 h- J0 I: m9 ]
    37 F1 M" e, J# R( c- ?8 t/ e
    4; [) s7 d" Z# E( A3 s; E
    5
    ( J+ a9 @' {, b6
    3 z0 w5 n/ k1 k/ u+ S& z7
    $ z2 [+ {1 k/ ?! p0 g1 `& y8
    5 @! c: p( e& e5 A8 |6 G3 b& B2 o9
    * o( ~5 Y. `1 |: \4 J: _10
    : C( I5 t# I7 G& s2 D. o把表的多列时间属性拼接转为时间序列的to_datetime,此时的列名必须和以下给定的时间关键词列名一致:# L& C2 |: p5 I- i7 c0 [5 b. m
    df_date_cols = pd.DataFrame({'year': [2020, 2020],
    0 y+ u- }+ X; i7 I                             'month': [1, 1],* I0 j& R- m, j
                                 'day': [1, 2],: m# d4 `/ b3 W8 _0 k3 ~! @0 G
                                 'hour': [10, 20],
    ( A+ a/ T) l* ~! [                             'minute': [30, 50],
    8 h% A1 I& L! u9 J                             'second': [20, 40]})
    ; r  w6 [% N5 O9 O: ]' r% c, spd.to_datetime(df_date_cols)' m" f' P7 ^& \5 ]5 k% R/ {
    5 }3 h* z1 o; `
    0   2020-01-01 10:30:20
    % f- g/ p& ~5 h, a6 x7 u9 Q1   2020-01-02 20:50:40
    6 ?6 m, I" F% R$ Z6 `dtype: datetime64[ns]
    + c1 k) }+ u; I# ~: C# c1
    5 m' G6 ?- n% T; @: v3 ?: X2
    , z3 \# ^; h+ \6 _" x) M; z& P3
    ; e+ @  H& t# i. Z4' t2 x. A" L; [" @* ^' g
    5+ G5 n: q# K$ I9 Y( u) d. j
    6
    " ~* b6 B7 Q( Y$ w( }6 k7
    . \; J; s2 Q1 W% S, W8
    6 v6 ?, f+ e: s; i9
    7 F2 x% E6 Y' z/ {0 ?10
    , |7 v( m; [2 R11
    6 d3 q3 y8 y8 X8 o8 {# adate_range是一种生成连续间隔时间的一种方法,其重要的参数为start, end, freq, periods,它们分别表示开始时间,结束时间,时间间隔,时间戳个数。其中,四个中的三个参数决定了,那么剩下的一个就随之确定了。这里要注意,开始或结束日期如果作为端点则它会被包含:/ ]: z4 @% r7 D
    pd.date_range('2020-1-1','2020-1-21', freq='10D') # 包含
    ) n0 [3 m9 f% Q% p1 q! g/ ]' M4 xOut[25]: DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21'], dtype='datetime64[ns]', freq='10D')' e1 V" g4 x0 t- ?) _' ^+ [
    $ f( j; Y1 @+ d9 q+ Q% M
    pd.date_range('2020-1-1','2020-2-28', freq='10D')
    / A8 p% g0 @. KOut[26]: 5 }$ l- z! N* f3 K, r0 Q+ L
    DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21', '2020-01-31',
    2 [9 o- v, b1 ^+ e) T" B               '2020-02-10', '2020-02-20'],: R( w- F6 A( r5 P0 t5 p
                  dtype='datetime64[ns]', freq='10D'); [; s$ G* m" I3 [$ e, ]" z4 p' e

      g3 p& I5 [( y$ b' {( p) L+ Zpd.date_range('2020-1-1',
    + r; }0 o5 h7 m  S! }; S              '2020-2-28', periods=6) # 由于结束日期无法取到,freq不为10天
    " {& l; I0 b$ B" i+ k
      z0 K8 L9 W, d5 n2 A* Q; D  }( uOut[27]:
    3 I. D& U  N, p; P( G2 A+ VDatetimeIndex(['2020-01-01 00:00:00', '2020-01-12 14:24:00',! A! u: K8 {* _
                   '2020-01-24 04:48:00', '2020-02-04 19:12:00',5 _7 F& Q6 B! r+ ?# M# P* Z, t
                   '2020-02-16 09:36:00', '2020-02-28 00:00:00'],' G% C4 e2 ^: P$ |9 G
                  dtype='datetime64[ns]', freq=None), o6 o( T( W1 `

    ( q( C+ a1 \1 I! e' n8 ~' i1/ d" B8 D- l3 R. v% @; g8 {
    2
    4 e4 K) M0 Z( k9 F5 ?' J" I9 P8 b5 U% o3! `8 v& F4 q8 M
    4
    # I, }- g5 N1 `8 {3 J1 |8 ]3 s50 }; x! U$ b, f* Y! K6 g
    6" @  T$ L) E+ @- M
    7
    8 W8 l( \6 j3 y" T8. P6 ~% x. @6 t8 ~2 y- S
    9! N' u& ~! V( w8 C# b0 O
    10) N+ ~7 E$ P9 A) d, |
    11  I, K+ G. U: m
    12. o4 Q+ P0 k/ v/ Z
    13
    6 W  ]2 H6 u6 s( Z4 H. B. r8 V14) t& ~( i8 s& y/ L
    15
    8 [+ b+ G- x2 _$ x+ E16
    ) j7 E) d4 E. t; {: I# L  m; u2 y, z17( Q% j4 Z  l& e0 ~! v) b
    这里的freq参数与DateOffset对象紧密相关,将在第四节介绍其具体的用法。
    % e' V; _. ^& S! P8 O. f+ m# Y/ k. }& L
      X+ a' O# f8 u【练一练】) i" ?) U9 s- j, K9 a3 ~( ^
    Timestamp上定义了一个value属性,其返回的整数值代表了从1970年1月1日零点到给定时间戳相差的纳秒数,请利用这个属性构造一个随机生成给定日期区间内日期序列的函数。  S, U7 Q6 o' m2 e

    2 w* B  g  S( }) E  v* ~ls=['2020-01-01','2020-02-20']5 r9 r6 J- a' L# B( ?& L2 I3 k
    def dates(ls,n):
    6 ]" S/ r9 f. ~" J) D    min=pd.Timestamp(ls[0]).value/10**9
    . x& l+ A  w6 B) F; ^    max=pd.Timestamp(ls[1]).value/10**97 ~1 i) H* g9 C
        times=np.random.randint(min,max+1,n)2 J  l0 C& {3 B& B. a! P& |
        return  pd.to_datetime(times,unit='s')
    , u1 v/ g7 f0 i- D1 Ndates(ls,10)
    1 i) ]$ @9 J/ P  K, Q8 s
    ) y4 Y5 R- @6 W2 P2 p  xDatetimeIndex(['2020-02-16 09:25:30', '2020-01-29 07:00:04',
    1 c) p' h* I' Y4 z2 S" r% }               '2020-01-21 12:26:02', '2020-02-08 20:34:08',
    . q2 r) I4 h% f               '2020-02-15 00:18:33', '2020-02-11 02:18:07',
    5 |* Y$ D+ E% y) I0 |8 Y               '2020-01-12 21:48:59', '2020-01-12 00:39:24',# I( n! m/ y/ k3 P! V
                   '2020-02-14 20:55:20', '2020-01-26 15:44:13'],' |1 S0 d3 N) s
                  dtype='datetime64[ns]', freq=None); A' j) {& p' B  ~" ]2 s
    1
    2 h8 m7 |2 v, k" p2
    : h: H" C+ s+ t& Q) K. U3! M5 N  U" Q% y& Y% J6 S' ^1 w1 G+ _
    4
    % L* \/ D5 Q8 x: Y; W53 b) A% X6 L* S$ j  h* S  M# K
    6, o2 z5 q0 {! u7 W3 s
    7
    ; \" @3 u2 ~7 ~: U& B$ W1 ]8' k5 o& a. j& @8 }+ k$ G
    9& b6 t% ~- m% T
    10" b- Q) _+ w* }/ J) w
    119 W7 e2 b' }3 \! k- z5 }
    12
      J) Y- O! g4 U+ Q' s8 S6 S6 V13
    0 k3 f4 i0 U3 R% Q+ t: S14
    - T! r1 c& Z. L# q+ P' E. Oasfreq:改变序列采样频率的方法,能够根据给定的freq对序列进行类似于reindex的操作:
    " J; C$ w6 X& @- b8 zs = pd.Series(np.random.rand(5),
    - |1 }# S1 A2 k$ J. b& m            index=pd.to_datetime([( U1 |4 n6 X" w8 f4 C
                    '2020-1-%d'%i for i in range(1,10,2)]))
    9 @' `) K5 x: n% A& f6 B5 U+ N, @* K
    : J3 }0 G: j5 i# ~; R6 Q3 Q
    s.head()
    2 A% X& A  L9 `, J8 ~1 mOut[29]: - _  n* a- x9 m9 S/ E
    2020-01-01    0.836578
    8 E" u7 A. a- {( }9 S- ?8 t7 y3 y  M- j2020-01-03    0.6784195 b+ W2 f* C- N; E" U
    2020-01-05    0.711897
    4 A% a. O7 P( d5 Z, ?2020-01-07    0.487429$ \0 c' b9 s* b
    2020-01-09    0.604705
    6 X7 q9 |; T, {7 i4 @* K* ldtype: float64
    $ P+ O" V; k- f( l9 b! L; Z6 Z; Q
    s.asfreq('D').head(); c( x$ G3 ^3 J9 m' h- s
    Out[30]:
    * V4 j( p, p4 b$ N: b2020-01-01    0.8365781 n8 R" l5 t4 L
    2020-01-02         NaN
    # q% G. t, l2 K% m& q% W2020-01-03    0.6784197 l! P* X/ e% F& p/ ]2 p9 I
    2020-01-04         NaN% r3 W5 U: F) F9 D; L
    2020-01-05    0.711897
    ; a; d( Z( _- z, F1 a" sFreq: D, dtype: float64  q8 t) ~. Y, C

    ; G3 P$ B/ U; L5 L$ ks.asfreq('12H').head()
    / u$ C# S: E; _9 s! d" hOut[31]: $ f9 W4 ^4 m7 C' z( Z
    2020-01-01 00:00:00    0.836578; B5 m0 l# l2 n7 @( F; B
    2020-01-01 12:00:00         NaN
    ! M4 d, U1 m" L# p" v2020-01-02 00:00:00         NaN  l6 W# X8 M; P& Q
    2020-01-02 12:00:00         NaN7 r6 p$ d. C3 B/ l
    2020-01-03 00:00:00    0.678419
    + x3 [0 T1 B; Z7 n' e, e0 MFreq: 12H, dtype: float64& n* R# i1 L& a1 A5 R4 i4 d
    & q- n- h5 @+ n: o9 M0 v4 B7 L
    1( J6 m( I) b, m) n7 c& }
    2
    # X' z1 m- P. p5 p& C5 b, p3% {. L  w8 ~. Y4 l, p
    4$ ^( Y& [( j7 ?: D, ~# @1 X( M
    5
    : S: M) {6 h2 t6& k4 X% z1 y  r3 S- `+ G
    7) M* u2 N: ?8 `9 x7 W0 ?
    8
    6 m  U4 _" y1 V' }. e* ~3 r9
    0 K' Q( O- R# Y; s3 i10
    5 {4 A& J0 T6 {6 Y* [11
    $ r1 p+ T, G+ y/ C/ ]- [12
    * A9 F* k) V/ Y. l/ N+ p13
    & Z. e) k& q& f) p2 E" _14
    - V1 y' h! B  L) V+ a15
    1 [, g3 v: f$ B, p3 V169 [% [& q- [8 {! K  I$ [  {
    17
    4 [! J0 a1 \" y" ~18/ N2 e6 d* d: v8 y7 s
    19
    - I& C6 u" }( i$ y: C8 a20
    ( K* z* W9 V' W21
    9 d% ?* u2 \. `" Y% r# o22
    1 O% Y' D: i5 l$ h23& V. r# n  P) x0 k' N. L
    24
    6 |  z  H6 u. n: x255 P: ~- h- H8 [
    26
    & t4 J% T  w0 U6 T+ M27
    6 ?" N, z* \3 a* ?& O8 a284 g) K4 D# }. q+ p+ U3 H
    29- A- e) A: y& j/ H
    306 ^, A8 N$ t+ p# T2 Y
    31/ d3 C! q, I. ~
    【NOTE】datetime64[ns] 序列的极值与均值1 F: S: d2 O! ?! t
      前面提到了datetime64[ns]本质上可以理解为一个整数,即从1970年1月1日零点到给定时间戳相差的纳秒数。所以对于一个datetime64[ns]序列,可以使用max, min, mean,来取得最大时间戳、最小时间戳和“平均”时间戳。9 P  @* ^2 V  O' k
    & q2 x! a9 |* v, s; ?: M  C
    10.2.3 dt对象2 N( v% A5 W; _8 W- G" ^
      如同category, string的序列上定义了cat, str来完成分类数据和文本数据的操作,在时序类型的序列上定义了dt对象来完成许多时间序列的相关操作。这里对于datetime64[ns]类型而言,可以大致分为三类操作:取出时间相关的属性、判断时间戳是否满足条件、取整操作。
    , ]8 n  T/ R" A/ r8 a% }' d$ v# J8 S0 S3 p% r( t
    第一类操作的常用属性包括:date, time, year, month, day, hour, minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter,其中daysinmonth, quarter分别表示该月一共有几天和季度。# }1 J# ~# t2 e8 d' s
    s = pd.Series(pd.date_range('2020-1-1','2020-1-3', freq='D'))
    ! b& N% G# Z& Z' r9 u
    ( ]8 @( N- `& W8 {- E$ ^$ S; ~7 Ss.dt.date
    & r  u: _7 _. R( |Out[33]: - A6 q# o7 `9 p' ?  B5 v8 o4 b
    0    2020-01-01
    0 u4 C9 T. W/ x) q& L" R2 |1    2020-01-02
    ! k- f% Y0 n: T2 d  M2    2020-01-03: ~; T$ f4 l. Y  c2 u9 `4 w# D
    dtype: object; ?4 I/ ^, n- X; L4 }
    # F; H& o+ [6 h0 }8 ?
    s.dt.time1 A) I* I+ T( k7 C5 p1 V4 K
    Out[34]:   ^3 }8 S: M' D
    0    00:00:009 c& ]. a9 M' G; D" z9 b
    1    00:00:008 Q$ C2 ~" H! Z9 V- t
    2    00:00:006 |7 |* ~. u% a' g1 Z2 h
    dtype: object, _9 }1 ]6 ]8 [- K1 w

    / @0 S/ g% F9 y! }0 F3 _9 m5 }s.dt.day5 `. z- q" I* B' e# U( ~, m
    Out[35]:
    0 a$ S1 q- w- l3 d0    1/ E. T: N- q; D0 _. [% x
    1    2
    0 w( y4 y( p0 ?2 H9 n$ ]% N2    3( g! ]) O% h3 n3 h4 J- V
    dtype: int64
    ) W( y5 ]: _. k6 t# Y4 I
    5 h  n! O1 b0 F2 Qs.dt.daysinmonth
    ; O( t  b& `. V2 r- k4 [Out[36]: - A( C* \, o1 ^: O
    0    31
    2 a( G+ {4 {+ l+ U& o0 @1    31: M2 f6 ?# F0 A& K- v
    2    31
    ; [4 }- g" L1 v1 w8 Q- mdtype: int64
    ; ]1 N) d$ h% ^1 i4 f
    2 d: u5 n+ i7 _6 K; n1
      l! |! `$ m' X9 S: G, w3 d2
    3 M2 ^/ U" ^- l3
    - y! z" g. D- g4 u% e/ c9 r4) v0 B, h* ~! y8 C
    51 S+ r9 A# V2 C* \& |' A3 n
    6
    - C( |' U9 T/ P4 p* J- x$ H70 d! {% _# A0 ?! l8 J$ a
    8
    . P  `2 z" O1 h93 O* D7 E  {1 V' ~! p
    10
    ' ?, C0 A3 ?; d116 ~% H, L/ p" P- V. R' m( Y3 U! h
    12( i" n- x( a5 Y1 \3 e
    13
    5 U: P+ r9 _" |! n14
    ( B! l" K, S2 P* i" y5 M. R15
    % G$ Q4 B! P: L  E16
    1 z( g. N# A* O17% @( w# ]$ s% |) l& ^7 J1 N% P0 e
    18
    $ v# x: L3 T, X" g% k7 _1 L* Q19
    / g+ E- }/ Y6 m8 Z, ~, X20
    8 {" g  V* @' F7 D8 \2 X. q9 z( l21
    6 d8 E$ o: B# r2 D22% |: W% _9 k$ g
    230 m) \$ F7 ], I- q  Z& C+ V# m1 B4 n
    24, Z9 g, e* K/ m3 u* w1 R  p/ f
    25* T  a9 y% g" ?% @2 Z% \
    26
    ' {7 U3 _) X) ^- {$ n; A+ e27
    ' K" F# }+ }5 l7 h28
    ' Y3 o" w. M6 G0 ~: N  y29
    : m( x& n9 h5 Y  在这些属性中,经常使用的是dayofweek,它返回了周中的星期情况,周一为0、周二为1,以此类推。此外,还可以通过month_name, day_name返回英文的月名和星期名,注意它们是方法而不是属性:; ?) I$ X' x7 E% a4 w9 v. E
    0 V/ O. q% }- P9 m1 J/ ?
    s.dt.dayofweek
    & c* v, D" k6 L/ [" {. H$ LOut[37]:
    : A- M+ N7 W* P: L0    21 [6 h( A) B: f4 P6 L0 O
    1    3  g4 G, x: w8 }# h0 C. F! Q
    2    40 J% H0 d: f% p" Q5 \+ m
    dtype: int64" ]# q- w; F- j- v- m* l

    ; b: O( R' X% o- i2 h1 ?" Fs.dt.month_name()
    . I: U5 p5 L' wOut[38]: ' \: R/ r  e# F3 \( B- A  x# B( y
    0    January' }* q: }; ~, D( [& B
    1    January1 N/ O' A- K! C
    2    January
    # i6 z& C3 ]2 T; N6 Adtype: object! ~( N: s! k- B2 K
    8 S% W; h- A3 C6 o
    s.dt.day_name()
    , h+ c4 [: j$ kOut[39]:
    % J5 @: f7 d: B3 P0    Wednesday
    2 X4 ~: O( c; X$ \1 q8 |% m1     Thursday- e% M$ l( z: Y7 E9 p0 T* P
    2       Friday( T* P- @- t: I9 T: J$ e
    dtype: object
    ; A3 o+ O; S: C# Y9 v
    : G/ j0 j  m7 ^; t+ f1% {0 P9 p4 X$ X4 a% l9 Q
    2
    & ^- J2 l. B1 U4 D3: D/ p: i9 p2 H. P/ ^# ^% Q
    45 u) d" X3 [2 }. ]- T* [( c
    5
      ?+ c4 _9 L1 z' U- n/ e: U) @$ _6& ^* F# n7 I: d1 X
    76 d7 n/ Q5 M% K3 B) Y" W) C) v
    8
    7 _- \4 G5 u+ B" d2 r; Z/ @9
    ! a4 j; }% B( v# r2 f1 _- m10
    6 I8 |. c, ~8 I* b" x11/ a0 I& k# U/ S1 W
    129 E6 ]( u! n% f  v5 Z1 u
    13
    / u4 H8 t4 h5 w& R/ Y14
    2 ^" Q8 w" R+ ^/ t3 f15
    1 d5 U& @; o# P4 H8 `16( L7 o2 }: n, x' \
    17
    : w) }' b9 u7 C* S+ G18! Q/ g8 m9 }6 b8 Y3 c' N) @
    19
    ; o* p( T' L$ ~# ~' C20
    2 ]2 i! P; Y, P6 K  C第二类判断操作主要用于测试是否为月/季/年的第一天或者最后一天:
    6 g- x# |1 X# A( y  I: r1 os.dt.is_year_start # 还可选 is_quarter/month_start0 t& J) h4 t' p0 i
    Out[40]:
    1 ~- V4 S- L% C2 w0     True
    7 [+ x2 }" j* _1 z1    False1 \9 k' f3 z: p+ t1 ]5 I+ l7 d3 ^. O0 K
    2    False* ~# ~2 a8 j' t( ?
    dtype: bool( \' `4 T$ w: A! B+ B% u
    ' M% D, e/ T3 i# s& S$ T
    s.dt.is_year_end # 还可选 is_quarter/month_end8 `$ g2 i1 b8 Y/ ?/ E' \
    Out[41]: ; d  X; G" h1 [  |( w/ L
    0    False2 G$ b1 g$ Z; b! V6 m
    1    False) C# q: {& y% H# _; r- k
    2    False
    5 n: R1 t' y1 _4 ^5 F# s6 q! `1 ?9 Mdtype: bool
    ; o: J+ D& _# _3 f! d9 J1
    9 k; I5 A! U: M+ \2
    , I+ ^. J! b1 C2 t0 w! x/ {% H3
    $ e* ?# c/ V; d, N% b4
    $ I* s4 s& \' l  a; g1 b5+ s" n5 k  D" V0 k" Z1 X# k. B
    6) z1 P0 b. W1 ~0 z! F- D, j! ^
    7
    ! g+ A3 `+ @2 o% A& I: m- _5 y7 G; N8
    ' c0 O' u" P2 a; e- j) [9# {8 @' \* ~, R0 j2 ~1 }" _
    105 l5 P$ x8 w6 v' ^' v& @! I) M# p
    11
    ) X- p! F, `3 w& v5 G* n: c12
    " I; N# r0 Z5 q( s3 S& ?2 ~13% @' L" i) S6 v. s( ^0 O! Y* L  h
    第三类的取整操作包含round, ceil, floor,它们的公共参数为freq,常用的包括H, min, S(小时、分钟、秒),所有可选的freq可参考此处。
    - l. j. n: w5 _) r7 Hs = pd.Series(pd.date_range('2020-1-1 20:35:00',0 W' m7 a1 q$ @5 C( U0 t* k( N- z
                                '2020-1-1 22:35:00',
    5 b* z8 D5 I' ]5 M* Z: w& D$ l/ F                            freq='45min'))
    # [+ c, }8 X) p- q( i6 i0 _7 H$ g4 C& ^) v/ X& P
    / Y  }& Q7 M! h/ K, j* h7 j0 [8 Y; R
    s. K0 F, o! S2 {/ r, w" f% n
    Out[43]:
    5 }, B" L5 X2 m6 C$ Y$ A% E0   2020-01-01 20:35:00
    ! Q, K! t. F& k+ I1   2020-01-01 21:20:00; B# L8 l  ^5 q; _
    2   2020-01-01 22:05:00
    $ y/ t' S, l# ^dtype: datetime64[ns]
    6 r! o5 }( e9 U4 X7 J0 l( ^) i
    & S2 z+ v' r) E2 Ms.dt.round('1H')% T. G0 Q: t$ C% R* f  h6 r) f% h: m
    Out[44]: & {* l6 ?: M$ J" A" G4 U
    0   2020-01-01 21:00:00
    2 R1 A# Z+ p/ U2 L: {8 B1   2020-01-01 21:00:00+ V3 O, s% v5 e; G- {) M* E
    2   2020-01-01 22:00:00
    1 K$ R. U, S6 u! h, {dtype: datetime64[ns]! {/ l/ i& r$ Y1 ^. o6 X

    2 w1 i; ^* b* h  R) \4 [8 j' Ls.dt.ceil('1H'); b3 P5 d) ]: Z' |+ [+ {2 z
    Out[45]: 9 f4 v. _6 b1 H2 o0 e
    0   2020-01-01 21:00:00& _; m% c* o5 A% j# P7 `; B* l
    1   2020-01-01 22:00:00
    # K% g8 u+ u( |2   2020-01-01 23:00:00
    ! |: e) b; k3 m9 C$ Ldtype: datetime64[ns]
    - w, k$ F& ^4 w4 N
    - ~5 s5 x; z9 ?( ]2 p; M. [s.dt.floor('1H')
    + S2 ]+ W" R8 k; W% XOut[46]: 9 O4 c; N5 {( `6 m
    0   2020-01-01 20:00:00
    & J1 ^' o/ ~# H9 {8 a- C. ^1   2020-01-01 21:00:00
    ; n( p& u' n& F/ J2   2020-01-01 22:00:00% ~0 v3 n; r& l8 m0 N/ j
    dtype: datetime64[ns]
    # b9 ?+ T1 T$ ?8 _4 G7 W0 {8 m. k) s% U
    1+ ?) @4 \% s  Q
    2( C" |: B& T, }, c* g/ P) k" C
    3
      \) T0 H8 p5 q4 v4 D: H% Y4* w9 \4 W2 w/ X' w9 O! S" ~
    5
    ) F9 a: Y6 T9 f0 S. c+ D! n; d4 R6/ X; h, i. R$ ?3 \7 p2 L9 V
    7
    3 A& x; N- Q* j2 p' ~( {& e5 X88 Z, h7 u1 |0 ]1 q' O
    9$ i& O$ v* g# [+ s
    10
    7 x5 H! F# r$ \" u11! ^( |5 z* X2 A9 ^
    12% ?* w1 V2 e2 R
    13& k% f/ z8 `8 g, H5 S- A+ G
    14- V( t2 R, H, J+ K( q
    15/ M. X4 m$ e# j# e; L) K
    16
    ! B! F2 u7 Q9 w. i& U* r% g) v# g8 J17
    # y- {. f9 a, z& h; [% p18! \) N* g' M7 j( W' [  \" W+ i. F
    19) v) l' l' S: U1 }/ a
    20
    0 E6 k8 y: \, [9 b2 g) z" |4 D2 e216 g7 j& g; s  Q* J9 M
    226 y9 C: n/ s, q( E1 F2 E
    23
    ; s6 M( a# V& s' s, V24
    ) e/ ~& G$ r, l/ P* Z/ @% M) a25
    6 ?. ?0 Y* H8 h/ M& E: f264 m2 ^/ u$ c( X. v8 V
    273 [: p& r& y" l3 G" K8 B* |2 j
    28
    0 M  z0 i7 y' Z* i- E( R6 t, {1 H29! k2 [2 u1 |8 T
    301 E3 a9 i* |: `* a3 I
    311 i0 M, Y4 Q8 }9 X% w3 f
    32
    6 r4 J3 o7 H! n0 q6 }- R10.2.4 时间戳的切片与索引
    : c& ]7 Y, m$ {6 D7 k) r) Z  一般而言,时间戳序列作为索引使用。如果想要选出某个子时间戳序列,有两种方法:
      h/ Q: H' |2 {" G. {- x4 R
    - f$ x  K; ?' U0 {  j利用dt对象和布尔条件联合使用
    : f! C" Z# b! l- B9 Y: Y( V利用切片,后者常用于连续时间戳。
    ; l3 N/ O1 l' X' [  i7 a7 z0 Es = pd.Series(np.random.randint(2,size=366), index=pd.date_range('2020-01-01','2020-12-31'))4 F& @0 i7 M- E2 I+ D0 T* d4 f
    idx = pd.Series(s.index).dt: V" v$ i* I6 g; X" Q& O' V
    s.head()0 [0 w! @) D) `; T2 P( m% c' z
    2 Z5 S. h$ E7 r6 t- Y5 n
    2020-01-01    0' H: L8 B+ }7 U, h( r- V3 |, K$ S2 ?
    2020-01-02    18 l  d, Y0 R& g+ K1 I$ i
    2020-01-03    12 l  f' R6 W) Q% C7 L
    2020-01-04    0
    . c& f. O# ^* e9 A2020-01-05    0
    9 z' X  @9 C4 v% fFreq: D, dtype: int32
    0 M! ?) H4 @% a1- A% k7 ]$ o8 B( Q& s, P
    2
    2 V" x9 R! n; T! V3
    ! s4 ]% a5 S6 ~$ T' r8 l4
    6 F6 `4 V  u; t50 m0 r+ [6 W: I! O; m9 y
    6
    7 m  y+ s3 o( n2 o6 m4 C) P72 v% }: z7 F5 ?3 k7 q6 }& s
    8
    6 Y7 e; u" m- i6 e7 m; z9/ ?: |! x0 i( n
    10
    * X+ U* T# Y2 B2 |Example1:每月的第一天或者最后一天
    # ]2 C  V" I8 ?  o' \3 b! Y; F6 v
    9 q# [" K* c# Y0 w% O! h8 _s[(idx.is_month_start|idx.is_month_end).values].head() # 必须要写.values2 m' n6 P6 q( |" V
    Out[50]:
    ) v) `$ i" R6 y$ q2 S2020-01-01    1/ J+ v5 w( P$ ?! B2 p& b
    2020-01-31    0
    1 S2 |( [/ `% a9 i9 p2020-02-01    1
    8 E+ P% J$ f! j9 E- H$ E7 ?- O2020-02-29    16 a% ^9 Y1 F3 J  c  Z/ {
    2020-03-01    0
    & T+ P. C6 S- `+ @' I7 d0 ?1 Adtype: int32
    $ W) x! U# i+ d2 Q0 ~1- S  V, s4 z$ r! h/ M
    2
    - J, j! }% [( a! A# I9 c37 w) w$ r) P1 K! ]0 ~
    4
    1 P" P$ E$ w- ]% L0 d* I2 y$ a53 D  D4 I4 |3 S3 x0 ]
    62 D9 b" o* U1 {2 ]% v9 ^
    7& {9 X- d& r! \. E4 V
    8( r3 m* N# D, E+ z
    Example2:双休日
    / D& E. _& u; A" }7 R8 n& [% z( I! M: J# T2 ]/ b4 B
    s[idx.dayofweek.isin([5,6]).values].head()
    / _) }* ^+ O+ I+ R. _Out[51]: ; T& w) ~% f9 _  {8 A
    2020-01-04    13 y/ _* [! u# H# _( T2 U9 P9 d
    2020-01-05    0
    / G, @4 a# U/ p! d6 B& @2020-01-11    02 C* t8 G# v7 U: Z6 c
    2020-01-12    1
    ! F' c. [# x/ ]$ `6 \( l2020-01-18    1
    : k( w4 D9 G9 x- i: _. `4 e/ `dtype: int32. \: w: ]$ _& t( |, a
    1
    2 Q" v! ^2 i+ h! @& V2
    # v. ]# O4 p! j1 c; l) r3, V4 f6 J8 m" ~4 f' @2 j$ {! X# p
    4" A( W. I  K0 q, X5 p
    5
    ' f7 s2 z3 g" K6( G( X2 T4 f5 M7 }
    7
    2 l. J6 Y  o( C1 ?2 O! U2 j8+ L* `# r# H. ?( \6 m
    Example3:取出单日值
    0 F# C% O5 J2 E7 N. [) `& n$ m" x) P
    s['2020-01-01']
    3 C/ n9 D! V$ W: _' Q( C! bOut[52]: 1! j* |; S( i  e. H9 y3 Y+ U/ ^
    0 i+ ?: E: b0 X9 D) F# P5 v  \2 v
    s['20200101'] # 自动转换标准格式
    + L: }: \# [1 Y, z4 e" JOut[53]: 15 R. @9 L5 E* C6 W( Q" K+ h; Y& G
    13 |& e; b8 |8 Z8 Y- p4 ?
    22 ]/ Z2 i( I: c" Y1 e+ i! H- }5 A# a
    3
    * n2 ]8 o$ A( A& D4
    / p; j, p7 q* O- e9 }5& `% a5 N" c5 }$ j3 Z/ x' G: g# |
    Example4:取出七月
    $ D5 n, P& z: c1 [* ]  x; ?+ g/ a" y7 r2 n' Q
    s['2020-07'].head()
    ' i* [3 ^, O. ?Out[54]: ) y3 X. I0 K& @, ]  r. T- _
    2020-07-01    0/ `, ]7 k  k4 n- [0 |8 v% s! }
    2020-07-02    1
    6 n# _' y) R/ I' M4 X2 J5 j9 U2020-07-03    00 G! c8 R; L8 }1 p& v% L
    2020-07-04    0+ Q0 i! Y; M0 F1 }
    2020-07-05    07 A1 f3 X+ ^6 ^% |8 o- `" B
    Freq: D, dtype: int32
    . I/ N, u3 B) k2 q4 L+ I, k/ ^1  H% O" h4 N! p6 ~, g4 l
    2: w2 {( P; O% B
    32 D/ a9 ^% O) `4 P% ~
    4
    % {4 @* t0 i; E0 t5
    * Z" O) m5 L! Y, ~' k9 k9 m6
    , w9 ?; _4 J, f, {8 V! C- v; u. O7, m$ c% G- `0 g% E- {
    8! J* f- i+ |: H2 x' m# F2 x
    Example5:取出5月初至7月15日; ~* T& r3 G# o$ c# I9 N3 J% {. [
    * S0 x4 \6 _; B; P" P
    s['2020-05':'2020-7-15'].head()
    - A1 l0 t0 ^- X. S' VOut[55]:
    5 {, S, s7 i5 w! T% d( P2020-05-01    0
    ' P2 n  j/ X- S2020-05-02    1
    + q/ F3 @: f" {+ ]$ q7 q2020-05-03    0
    . T, a" W; G: l2 U1 u9 z2020-05-04    1
    : R- L- _- J$ q; t/ v& p& C8 X  I2020-05-05    1# w! `* ]  }5 i& n; e
    Freq: D, dtype: int32
    0 F' k' n2 x0 Z/ T' U& w' D" u, v* c2 `" |  h% Z+ Y$ {# }
    s['2020-05':'2020-7-15'].tail()
    ( q( ~( Y: M0 Y& KOut[56]:
    & b, C) z3 f' ?0 |0 Y1 v2020-07-11    00 c0 s) X. l" v) M3 U1 Q* n+ x' N) S
    2020-07-12    07 c  }* ]! B! t, C: o# s0 i# r
    2020-07-13    1  Z9 M# `- b5 K) [# |
    2020-07-14    0* I2 B. w" l; ^/ ^1 x2 }. q2 ^
    2020-07-15    1
    # y! U  Z/ G* L0 \  JFreq: D, dtype: int32
    % K) q1 }0 d* t2 b) y5 W
    ) q6 b7 F  o  f* I% \1
    $ _2 y! A0 {6 q* b/ |9 E2' R. O4 R& j* k" a$ q
    3. @' h1 v( s: v+ x, J
    48 y. w" K! l! W7 q# h
    5
    # P  g) D0 K  L1 h& W9 b# Z/ d62 i. m6 Y" K" Q* v0 O* y; x( i: T, X
    7
    $ f1 u! B- H2 g1 Z. |80 }  O0 m7 A- W" y
    95 P# @- m4 ]/ @
    101 B9 h$ f# x4 k
    11$ p3 R1 M6 a* x" f( `/ e
    124 c7 A2 ]" |1 B7 U& O6 {4 q9 l
    13
    # C! A. h& d+ o7 q. [/ R14
    , R5 v$ D$ C- V  y% Y5 V# K+ @15- ]0 x: K4 ]7 }, H- Z
    16
    7 z$ R1 r9 l( W% ?6 T/ M17
    2 z' g5 X/ Z0 c8 K. H$ U10.3 时间差
    & \( P! F3 t  r& h- W4 q9 B10.3.1 Timedelta的生成6 A+ a! l& K" k8 r: E, \
    pandas.Timedelta(value=<object object>, unit=None, **kwargs)& I. b# V) o1 m0 A" B) I
      unit:字符串格式,默认 ‘ns’。如果输入是整数,则表示输入的单位。
    : ^( l0 u- b. o/ U1 X) |  可能的值有:- H! h) D% j7 f$ j& t3 x) c  y4 o

    & m( }. O9 G  V‘W’, ‘D’, ‘T’, ‘S’, ‘L’, ‘U’, or ‘N’
    1 X2 o+ d2 \* m6 d9 E‘days’ or ‘day’
    # f- N% ^+ I& l) ]% U) C‘hours’, ‘hour’, ‘hr’, or ‘h’
    6 H+ h' \" N! j7 {9 j3 p‘minutes’, ‘minute’, ‘min’, or ‘m’
    ) r& w  h  m& J0 b& y6 V‘seconds’, ‘second’, or ‘sec’
    5 D  e# n& f. z. P/ e) B毫秒‘milliseconds’, ‘millisecond’, ‘millis’, or ‘milli’
    1 O9 N: f. }  ?微秒‘microseconds’, ‘microsecond’, ‘micros’, or ‘micro’
    4 t$ p& P1 `6 c( @, @! H纳秒 ‘nanoseconds’, ‘nanosecond’, ‘nanos’, ‘nano’, or ‘ns’.
    , H9 X; P4 I5 V4 i2 |9 ?时间差可以理解为两个时间戳的差,可以通过pd.Timedelta来构造:+ F- i8 [' q+ ]3 v
    pd.Timestamp('20200102 08:00:00')-pd.Timestamp('20200101 07:35:00')1 b( M. z. M+ J# U) m
    Out[57]: Timedelta('1 days 00:25:00')
    8 f3 K) C, K' M  i) z
    8 k; @/ J" d- \( |3 u: }' Jpd.Timedelta(days=1, minutes=25) # 需要注意加s$ ~+ L( s( r+ J& J# i( f
    Out[58]: Timedelta('1 days 00:25:00')
    9 E& Y: d' ?& c( B
    & h& T3 T" T, @$ M1 kpd.Timedelta('1 days 25 minutes') # 字符串生成
    ) R9 j$ {9 _- p* f9 S& VOut[59]: Timedelta('1 days 00:25:00')
      `9 L' n1 x5 t' U( w# x/ f7 T% }# o/ t8 }2 C
    pd.Timedelta(1, "d")  r8 B& @  V) Y2 y$ R, o3 {- s
    Out[58]: Timedelta('1 days 00:00:00')
    8 {3 l/ Y+ W/ k1
    . k5 q1 i' ^/ [* @; x26 r  W) m+ W, a! w( E  f! }5 A
    3$ M; [; e7 L' U6 J
    40 _, s. M6 [) ~* l% h: V
    5
    1 X( z: E2 y: t+ @. u6
    , n. Z5 Z. h) a0 S: b7
    ' L4 s, N. B5 [  J/ p83 \- t( d, j. q
    97 B: t# t( B) d1 ]; B
    10# k0 @; R, u; F" v$ \4 h; f
    11
    2 y: N( I. [8 y8 F- T1 `7 J( r生成时间差序列的主要方式是 pd.to_timedelta ,其类型为 timedelta64[ns] :
    ' k' Y* M. ?. h+ J/ Gs = pd.to_timedelta(df.Time_Record)2 G9 ?- L$ K8 x  t' `
    & b' @/ a8 W: w$ t% Y
    s.head()
    ' X8 h  L% M3 @9 ~9 fOut[61]: " ~) |1 w  Z- t# N/ R$ Z
    0   0 days 00:04:34
    1 o. X7 l" l. o: `1   0 days 00:04:20
    : v5 e# ^& M9 y3 q2   0 days 00:05:224 ?) J- v0 r$ o1 ~0 w
    3   0 days 00:04:08
    % s# v: M4 Z  ~+ G) ?4   0 days 00:05:22
    % f" s( U' O' R8 ~4 E& LName: Time_Record, dtype: timedelta64[ns]5 \8 |" u* y" t- F. U5 }4 d# T/ X
    12 V+ l( L: \  _7 \% {
    2' z/ _, ^: w) Y' K6 Y% `
    3
    # V* B2 I5 h+ r. h4/ E' w) l. U+ y3 Z$ Q- g/ @
    5
    ' f  P+ g  e- \* W) c6
    + x  R3 X, `# R/ ^0 C7
    9 A( D, N# T: W" d8. W# F0 j7 E* I: @
    9' c# Q' y5 F+ _# V7 H0 e) ]
    10
    . a  n. B2 Y" e; T2 ^  N与date_range一样,时间差序列也可以用timedelta_range来生成,它们两者具有一致的参数:/ r2 F5 [9 P  z: G8 _
    pd.timedelta_range('0s', '1000s', freq='6min')3 H% R& P, C* j
    Out[62]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:06:00', '0 days 00:12:00'], dtype='timedelta64[ns]', freq='6T')& ]! R. S8 D0 K" `6 r, I
    3 t/ t: e" c2 g% i& y+ e, |
    pd.timedelta_range('0s', '1000s', periods=3)
    6 E$ r! {( e2 T6 `, P/ GOut[63]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:08:20', '0 days 00:16:40'], dtype='timedelta64[ns]', freq=None)
    % Q. H$ w7 N4 ~9 \1
    ) U8 G) D/ J/ `' H/ O; H" r2( k6 u$ I8 u) ^2 a9 o/ V
    3# o/ m1 H: s- A0 L3 P3 A5 r
    4& N, @% X8 I5 h+ d5 a6 r
    55 f" {6 Z. [' z* L( y) w
    对于Timedelta序列,同样也定义了dt对象,上面主要定义了的属性包括days, seconds, mircroseconds(毫秒), nanoseconds(纳秒),它们分别返回了对应的时间差特征。需要注意的是,这里的seconds不是指单纯的秒,而是对天数取余后剩余的秒数:
    * k$ P! I' j! @2 }* @8 `s.dt.seconds.head()  @7 U+ b+ f8 A0 G5 Y' x% z$ v* B7 Z
    Out[64]:
    : j7 p2 r/ \3 n+ J9 Z) k# f9 T6 i0    274
    # M$ q. z/ P  B1    260+ @) Y! I0 c! @# `, G, w
    2    322; _9 [, W( Q, s' D4 s8 ]9 _
    3    248
    ( o  H& P. m7 C) _4    322$ i% C8 P2 @2 u4 C1 w+ R1 W) A
    Name: Time_Record, dtype: int64
    5 U9 |, ]% i3 X8 q1
    * t6 M, a$ J/ T9 u$ v6 I) r6 {0 j2
    3 w" f. N- l* z& q3; J5 i6 x: |: ?5 x0 w
    4
    / q$ ]$ s6 K1 H1 h" B51 U3 _+ d+ D2 h
    6* p$ z  |4 @. h, Q. |5 X# l" @6 A: _
    7
    3 R' `6 u( U) X, A8
    2 q4 |  N: t' Z- e0 R如果不想对天数取余而直接对应秒数,可以使用total_seconds
    ! ]8 _8 v" `# g" S* ^/ }
    2 Q7 A! H. D1 rs.dt.total_seconds().head()
    & q$ }: }, U6 J' POut[65]: 0 E5 H; ?2 w4 M9 F: {
    0    274.0
      P5 z8 V! @! `2 w. Z) H/ S1    260.0- Z4 D" O& ]8 I( B
    2    322.0; j5 g2 x5 i$ S+ U( x* V$ u
    3    248.0
    , _, h2 Z! ^" G3 i4    322.0
    2 F9 W( v4 |$ d: Q6 {1 d. a( v  UName: Time_Record, dtype: float64
    % a7 _2 S# ?7 l" z1
    0 \1 E# n/ E/ ^3 F/ U7 l4 a: j9 ?2
    9 y: F; r8 Z9 n) g3, ~6 A6 _2 q+ Q- i
    45 R2 m+ N; [; _8 U
    5" {* i4 L/ t0 l' Z6 p
    63 C, S: C: [5 R( i! K- D
    70 J+ S  q: c& j, G1 N2 s( ?
    8  ]; y5 U" ~( v- H( y3 [& W" A
    与时间戳序列类似,取整函数也是可以在dt对象上使用的:5 I9 T: d2 \9 _+ ^% t
    0 y* S( C/ R" l" t. ?& l9 Y
    pd.to_timedelta(df.Time_Record).dt.round('min').head()  w3 \8 r; R* G9 E( p
    Out[66]:
    9 w' u# ~" ~& Z+ c5 |( B: y0   0 days 00:05:00
    * Z# Y7 G6 J  C( G2 e1   0 days 00:04:00
    & _" e* v  q+ |% g% u7 [2   0 days 00:05:00; d7 K+ G# o( h# a& ^4 Z& v$ [
    3   0 days 00:04:00
    - S; }5 ?1 V3 j9 c: D4   0 days 00:05:00
    * d! v  }0 T  v0 D3 h- TName: Time_Record, dtype: timedelta64[ns]0 V( f! l* s' K/ @+ l9 T2 S
    1' c3 [; Z: n/ o3 c9 S
    2
    , |3 t! h4 N- m# ^- V3& j' D" l2 U3 J1 y3 S! Y9 i
    4
    ' s0 E3 |6 p8 }! S; {+ F5
    8 }9 \/ g. o2 r& E64 w0 ]$ r* d4 @" T9 D- O& Q4 V
    7
    ! @: k* O3 `7 u; u8
    3 K* a" h2 o% u2 J: U# Q( D4 U$ v10.2.2 Timedelta的运算
    5 F3 d! z! O7 Q/ |: q; T( x8 @单个时间差的常用运算,有三类:与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算:
      Y* u/ A( B, e# u! Std1 = pd.Timedelta(days=1)
    $ y) A* h+ s* N$ L$ s: T; y; Dtd2 = pd.Timedelta(days=3)' N) L. y* Z! t% J% \
    ts = pd.Timestamp('20200101')! [3 M6 t( e( @. F

    , G) ~4 ]& `9 S6 q) ftd1 * 22 d: ]$ c& z$ X" j: X5 ?
    Out[70]: Timedelta('2 days 00:00:00')4 |0 H5 |" k; }3 M
    ' ?1 ^: S2 g' S. E7 u; W
    td2 - td1& h$ ?3 t) E' X  R3 G5 m7 D/ F. V, P
    Out[71]: Timedelta('2 days 00:00:00')# w/ W4 [! f& f9 e/ }2 H; @
    2 }3 }# ^0 L+ n$ I# S. T
    ts + td1
    4 N& Y0 ~4 W' z$ q2 A  ROut[72]: Timestamp('2020-01-02 00:00:00')! g0 v) a8 C3 U2 Q; Z

      v5 S8 q7 i3 z  its - td14 b8 V/ Y4 \8 y$ b$ r6 ~3 o
    Out[73]: Timestamp('2019-12-31 00:00:00')
    ' ]9 m2 z3 L" U. W( c1, E! c6 e: @$ Q! ~# d1 `
    2
    % W) t' c  Y' ]% l$ r8 g, ~3
    9 z/ y  I+ U" `6 s3 D! Z, O4" d7 x( B) U, l
    5' k, \# s0 {9 {2 V. y* Y$ y( p
    6
    * \6 L* i# C7 y! H7
    0 Y' b% d: p8 `- Y+ ^8
    6 C. p- J. Z3 C% Z/ [90 j$ J. V+ [& i) _
    10
    ; [' E* _! [: b) c' z3 H1 d11" ~* e  @8 J9 \$ A8 z
    12
    4 J; H6 W" e, d8 }8 R" f13- [7 E- y. ^' B: Y7 I
    14
    6 \* U. L+ H" q! b15
    # O$ ^4 R( Q8 G时间差的序列的运算,和上面方法相同:
    / z7 z: f* h- A; g* etd1 = pd.timedelta_range(start='1 days', periods=5)
    ( l1 A6 R& h( T  ^+ Ytd2 = pd.timedelta_range(start='12 hours',
    / n. S- |/ r" @3 f                         freq='2H',
    6 z8 V8 y4 c  C% I8 {7 ~                         periods=5): w8 n% K1 M/ w# K+ `
    ts = pd.date_range('20200101', '20200105')
    ) C. Z& H: X- }td1,td2,ts) q. M  G& r8 p% J% E" O9 T3 _( S0 S

    ! D# r9 b' E1 _TimedeltaIndex(['1 days', '2 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq='D')& s. ^0 C* x  Q) n
    TimedeltaIndex(['0 days 12:00:00', '0 days 14:00:00', '0 days 16:00:00',
    1 B% j! E$ m1 z7 Q; D3 v, s                '0 days 18:00:00', '0 days 20:00:00'], dtype='timedelta64[ns]', freq='2H')6 U8 V7 P- b2 ~  N3 X, {! U. e
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',
      P5 `: }* [* o! f               '2020-01-05'],6 s3 Q! _$ E4 r9 Z! _
                  dtype='datetime64[ns]', freq='D')% t  _+ t& j" L8 n$ u1 E# {
    1
    * p7 X/ e& p* g6 p7 y& ~- c) l20 _5 O! Y3 J- ^3 e! |/ O( M; V
    32 c; I. t$ M) d9 d. I# b% O" |8 n5 w
    4
    $ D+ _5 n$ j% P5& ]; I" |1 D8 k: v
    65 b+ Q, H6 ?7 }* U  @( }! |' w
    7
    , A6 x$ l% `- m/ I7 }1 D* n8. @5 y  {7 N$ F5 A( B# b/ t+ p
    95 x: Z: ^0 g" U9 J
    10
    2 R* Z0 \6 x0 z3 A1 D' E11! |. w: n( ~. s6 S7 H7 ]
    12
    ( R& i6 Y% e! {13: d& R* H# f; Y* K
    td1 * 5
    3 m& g& h" n- s/ @4 O# mOut[77]: TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D'); f: m  v8 w6 R% I: K9 n0 S& |
    6 l+ e8 e+ _# Z
    td1 * pd.Series(list(range(5))) # 逐个相乘
    : H* L" F# o& C& k+ b: eOut[78]: " ?$ f- P$ e; l/ }6 a
    0    0 days
    5 ?4 O" q! g3 ^- ?# o1    2 days4 I! o! `# F: m0 }1 B4 g
    2    6 days
      m) I: g% L% F1 T$ F3   12 days
      @$ M  x% c5 N% K2 D+ H7 ?4   20 days. p3 {5 E! s1 n, R7 d3 U/ g4 d& H4 l  {
    dtype: timedelta64[ns]
    ; J" t5 G- ?# Y$ P
    ( W) w# p$ Q, @7 n5 Y3 J& T# \2 qtd1 - td2
    8 d% R9 f; K% ^  uOut[79]:
    1 q1 d  U& B$ x; eTimedeltaIndex(['0 days 12:00:00', '1 days 10:00:00', '2 days 08:00:00',
    $ S7 z! f) x1 C; S/ `0 L! H+ s# j                '3 days 06:00:00', '4 days 04:00:00'],
    / A4 k2 a( a( G* m7 [5 c' y+ Z               dtype='timedelta64[ns]', freq=None)
    2 O' ]: J% Y1 w% B- `( @1 w. ]2 S2 Q  Y/ X: p" T+ N
    td1 + pd.Timestamp('20200101'), H8 E. q: S. H
    Out[80]:
    : E8 `5 [! S( T$ l  |# _/ dDatetimeIndex(['2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05',
    $ B, G3 ?" Y8 J9 z5 A0 d" D               '2020-01-06'],dtype='datetime64[ns]', freq='D')  I! H' U* ?' J
    1 S  b. a0 R/ O* c( s
    td1 + ts # 逐个相加
    ' Y( H3 v* {3 K% X' O% w+ tOut[81]: , r: B/ ?! U/ S' ^& J1 H, \
    DatetimeIndex(['2020-01-02', '2020-01-04', '2020-01-06', '2020-01-08',7 M! ^  `; Q+ D% e& n9 j! I  q
                   '2020-01-10'],
    - T( r6 T. @6 C4 k              dtype='datetime64[ns]', freq=None)
    0 t, K7 i6 ~* ~
    " d* K7 y" |$ W! H6 D: ?- U1& O- k, I+ Y2 `* v' r; x
    2, Q9 ]+ q3 p4 n5 F! x) s
    3
      U* g8 t6 K. {; t0 R% G/ S4 ^4
    - {1 T* E. P9 H6 b5
    + @* ]: m$ w. ]" ?9 ^6  N8 |5 \2 V) ]# n9 |9 z! O& Y& g
    7
    * x5 e- f7 h3 W, N- P' m# A; T80 W& v/ _; R0 \1 ?2 z& O
    9
      W4 N2 L5 O" m3 n% `10
    8 G# @  W# z# G$ G8 ~. S$ T8 L  c" q0 c11) x0 Z# ?+ W; N
    12
    2 a" e9 V. G9 s! B; E' t8 |6 A2 o13/ e" I; x4 f3 u' }1 r3 }& [* H/ _
    14) K; {+ A# ~; `& S+ ~! t
    152 c1 d" M% k! b& G1 y  [% e$ q$ u
    16
    9 J( H4 n. Z# _+ R4 [17! z# W! U" S2 r9 o% Q' l  h: ^
    18: l0 V) [* `- Q5 D& S. z
    19
    7 p$ X$ j1 E" w6 V7 g20
    0 a: b' Q7 f9 N$ Q5 R* G! N4 j21
    2 r/ O! f, U% ^" G, U6 E* n4 w8 R22
    2 ?3 \8 k0 M; y; s1 e; U3 n23. G# j! b* v* m- Q+ U. r3 c
    24* _; b* {( R* P" M8 L
    25
    " U2 h* E% `6 [: }26
    $ J( G( W3 u) s# A( C4 J271 J1 I+ c/ x3 @/ O( p) o7 U2 ^& _
    28% S4 w$ ]; j8 o" ?5 X' p/ ]) P
    10.4 日期偏置
    1 C) `8 d, a, [* u10.4.1 Offset对象
    5 B% G6 A& \2 {& h7 q% R( t  日期偏置是一种和日历相关的特殊时间差,例如回到第一节中的两个问题:如何求2020年9月第一个周一的日期,以及如何求2020年9月7日后的第30个工作日是哪一天。6 b0 T* [+ X7 P, p$ h6 k
    6 L3 V% j/ P$ A5 r. Y/ C4 u, B$ g
    DateOffset 类有10个属性,假设s=pd.offsets.WeekOfMonth(week=0,weekday=0),则:
    4 G5 ~% s) W8 C& r& a+ @' }5 P9 v0 k, _2 n# _# S
    s.base:<WeekOfMonth: week=0, weekday=0>,返回 n=1 且所有其他属性一样的副本
    : S, W  k; q7 t8 J, b% fs.kwds:{‘week’: 0, ‘weekday’: 0}6 i5 G; {# J' Q4 R  z( A+ z
    s.wek/s.weekday:顾名思义
    + N# k- @* D0 s( ?1 |, [- _, m有14个方法,包括:
      A$ |5 y! s- K, h  d1 N# ~9 M: S" y" O# c6 |4 M3 k3 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等等。
      W/ g4 P  Y$ g  j4 v" Z  q5 Mpandas.tseries.offsets.WeekOfMonth(week,weekday):描述每月的日期,例如“每月第二周的星期二”。7 X0 t* I; I- I" m6 Y
    ! k5 l) I0 J, ?- z$ m2 j
    有两个参数:( @+ p: q  H% o0 u. J
    week:整型,表示一个月的第几周。例如 0 是一个月的第 1 周,1 是第 2 周,以此类推。
    8 t2 I7 v3 Y3 t* F' c3 I6 zweekday:整型,取值为[0,1,…6],表示周一到周日,默认取值为0(星期一)2 T  V6 w% q7 P+ p
    pandas.tseries.offsets.BusinessDay(n):相当于pd.offsets.BDay(n),DateOffset 子类,表示可能的 n 个工作日。3 M; t4 K: C6 O+ h! Z8 k/ Q! ?+ G) ~2 w
    4 l2 }0 K6 z! `) Z( V4 H" ~/ J
    pd.Timestamp('20200831') + pd.offsets.WeekOfMonth(week=0,weekday=0)
    9 j+ K! q4 R* a& [7 V7 U% R+ dOut[82]: Timestamp('2020-09-07 00:00:00')
    ; _9 W. c5 C! }9 o+ w+ H
    2 g: ?2 q% L9 g+ W. `, |( Rpd.Timestamp('20200907') + pd.offsets.BDay(30): P7 j( F0 Q9 ^4 b! i" z
    Out[83]: Timestamp('2020-10-19 00:00:00')
    , ]8 T+ e) b: s( z% m1% O, D! _7 Y8 Q- ?* O& q
    2
    : X8 b$ k! Y. K8 [& R6 }3 E3$ K' {3 W, k* h' j8 p
    4
    ( [! X3 i) B# ]( r4 Y53 ]" {* ~  H. U
      从上面的例子中可以看到,Offset对象在pd.offsets中被定义。当使用+时获取离其最近的下一个日期,当使用-时获取离其最近的上一个日期:
    * Z% Z+ m: B3 C# j( w# h6 i+ J
    0 b5 u/ m) U& i- W8 e8 y* xpd.Timestamp('20200831') - pd.offsets.WeekOfMonth(week=0,weekday=0)
    3 o, Q3 }# a" BOut[84]: Timestamp('2020-08-03 00:00:00')
    + v4 a  m% y4 H' E$ w: J$ u' |. c& C# N0 I/ \, \* X& X
    pd.Timestamp('20200907') - pd.offsets.BDay(30)  J, n3 s1 R/ e4 t" b, r3 l: Q  k
    Out[85]: Timestamp('2020-07-27 00:00:00')( ~5 T, p( I) Z' e0 x
    5 W/ K" ]  J5 d4 i2 b7 b' S+ h
    pd.Timestamp('20200907') + pd.offsets.MonthEnd()
      e! ]4 j/ ]. z+ f5 T5 ^4 ROut[86]: Timestamp('2020-09-30 00:00:00'); y5 U$ U7 E. ?/ k9 R7 N: ?8 z4 f
    1: F, d9 A( c8 w. z" y7 Y
    2
    " N4 A' e$ X+ M. A: f7 a* o! A9 A3. m+ E7 v6 o7 }4 z: q' C) y* u- W
    4
    ! @, x. n% d/ \3 W/ v( g: E9 k50 B; E5 i$ S: V4 t- {
    6
    ) @2 l) @# T4 \7
    6 ~9 {1 t7 Q* V8 R: ]4 d! H/ i85 d, H& j' q& z5 g0 V+ I
      常用的日期偏置如下可以查阅这里的DateOffset 文档描述。在文档罗列的Offset中,需要介绍一个特殊的Offset对象CDay。CDay 或 CustomBusinessDay 类提供了一个参数化的 BusinessDay 类,可用于创建自定义的工作日日历,该日历说明当地假期和当地周末惯例。
    ! `1 X  d# K8 f# I. T  其中的holidays, weekmask参数能够分别对自定义的日期和星期进行过滤,前者传入了需要过滤的日期列表,后者传入的是三个字母的星期缩写构成的星期字符串,其作用是只保留字符串中出现的星期:, ]4 T% G3 u3 t/ T9 q! ?3 G
    2 I/ g- ~3 \: N- z4 b3 U2 `) _/ C' T
    my_filter = pd.offsets.CDay(n=1,weekmask='Wed Fri',holidays=['20200109'])! U# X, @6 \/ T! e3 K7 f8 p
    dr = pd.date_range('20200108', '20200111')2 ^5 u0 m" p! U& Y! t

    3 W7 Z4 {7 v5 {+ [5 b, y& e4 ndr.to_series().dt.dayofweek8 w" U5 I! x9 s3 i0 W4 ^
    Out[89]: , G$ g1 s/ b6 n/ H
    2020-01-08    2
    + k! }" S  W* E2020-01-09    38 h  Y4 K+ M! }  \. {, i  {9 s
    2020-01-10    4' ?( D# f5 x/ b7 E7 b7 V
    2020-01-11    5
    * b3 P7 ?) a. a; q' qFreq: D, dtype: int64
    * d; I( j- Y" U, c! R
    ) _2 I7 U* q9 O; K8 @! d9 R[i + my_filter for i in dr]+ _% _* g1 Y, ]$ r; F$ _( X/ N' b
    Out[90]:
    ' s. X, \4 x* L[Timestamp('2020-01-10 00:00:00'),
    - e& C) Y9 Z7 N4 U) t Timestamp('2020-01-10 00:00:00'),
    8 M6 a' l5 h1 E5 B Timestamp('2020-01-15 00:00:00'),5 U( [* A: T1 N  h6 P: I* ~
    Timestamp('2020-01-15 00:00:00')]) U2 y' o  m: K: Y7 h3 o. z

    * l9 |6 x6 E1 f13 a6 y. e! e5 r) F3 j, {6 J! v
    2
    ; R% f5 }0 [; M  D4 S" v; I3
    6 K2 K$ `; I: {8 t4
    : @* B+ y, P9 V" T! \' X7 x5
    4 B) L3 ?8 S8 d68 ]9 N; r) S, L- p9 v: s) C
    7
    ( q8 @: F! D9 I0 W8
    / u( Y4 B( e/ P! S9
    " n* X' f3 s# Z) \; g7 h10
    & C5 q1 B% U# ]5 T- y) Q% M/ c11
    ; \; Z9 [) A- y# j# W, T12  e% C; F- _5 q1 h. V
    130 X1 }( M. W9 y3 s' _; t+ h2 s
    14
    ) h: i2 J) F0 W+ ^15
    : M  g: D2 P. w' b* b. B3 p165 a' |& Q' Y# D( z
    174 W# H6 S% W9 \( E( k$ s9 v& O
      上面的例子中,n表示增加一天CDay,dr中的第一天为20200108,但由于下一天20200109被排除了,并且20200110是合法的周五,因此转为20200110,其他后面的日期处理类似。, r3 \" [  @  }7 v8 }4 l+ n

    ( n+ U9 \2 ~- S' C* o" ?【CAUTION】不要使用部分Offset# _( n0 [8 \0 u- H$ V! D
    在当前版本下由于一些 bug ,不要使用 Day 级别以下的 Offset 对象,比如 Hour, Second 等,请使用对应的 Timedelta 对象来代替。
    + Z: X; `: C5 q! l5 }0 K
    7 D* f$ Q9 A: D6 y5 @4 _  e10.4.2 偏置字符串
    3 p$ w1 a" _( U  前面提到了关于date_range的freq取值可用Offset对象,同时在pandas中几乎每一个Offset对象绑定了日期偏置字符串(frequencies strings/offset aliases),可以指定Offset对应的字符串来替代使用。下面举一些常见的例子。, v! n' n7 }  w$ b0 e4 I
    # b9 J$ a! a6 ?0 k- K# r
      Offset aliases:pd.date_range函数中的freq参数,为常见时间序列频率提供了许多字符串别名。 也称为偏移别名Offset aliases。偏移别名列表点此参看(大概27个)。# }. W% J) e# S9 `
    ; G! l/ H( t! y) R8 u; x% H
    pd.date_range('20200101','20200331', freq='MS') # 月初: T1 S) ~& }3 a0 ?" n) Z; T) x8 o
    Out[91]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')' L$ Z3 Y( A6 Z' k/ z& O9 |: C
    ! e3 K# T2 `" J1 ?
    pd.date_range('20200101','20200331', freq='M') # 月末
    8 M6 }( _9 ?) J( rOut[92]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')
    - }) c0 r6 F; K/ c
    , s. S# V8 `9 l2 r# {pd.date_range('20200101','20200110', freq='B') # 工作日
    3 ~8 `0 n8 A2 t$ u, P, S( T4 e0 bOut[93]:
    ; r! l/ L" B4 j. Y$ m' @. e' RDatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
    2 `, L+ V/ ~' p) i' }. h/ P8 G               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],. |# Z& d" l( g/ h
                  dtype='datetime64[ns]', freq='B')
    9 M8 V1 @0 x' D* r3 I( P
    3 G/ f! A. h! Ppd.date_range('20200101','20200201', freq='W-MON') # 周一, |. }" [" i6 C( x  c
    Out[94]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='W-MON')
    + ^6 \4 W. \3 l: ]) {1 w  y0 F' j. h9 g( Z6 X3 r* P" [
    pd.date_range('20200101','20200201',( @- L$ g6 ]0 ~( e1 r6 ?7 J
                  freq='WOM-1MON') # 每月第一个周一
    ' T' j" n4 V( ~; T" u4 b5 ~+ t% K4 M& ^% h; O0 w, G* Z
    Out[95]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')/ ~' c0 t* u9 W+ H7 [

    * v1 N. A: Z- G5 a6 ]11 ~) s9 O& ]. c
    2& c& q9 ^) V; ]3 Y; e! j( l. p
    3
    % Q% S0 q  {! Z0 y7 u  B" ?4
    ; z& S; x$ P" K2 `* U" @5
    ! Z" L8 q. b. B( \5 c3 l6
    / J- }# L3 \- {8 z. I, \7& f# a% |; F7 k. J9 ~# s# u
    8# G/ J& L- f) f  P" M
    97 k- V9 `! Z' D2 k9 \
    10- m3 \' }- k; {) j5 }! X
    11
    ' w$ g' ]. `4 w+ o" u12' x5 l5 {% L9 a  G
    13% [3 D6 v# H2 ?( H8 g
    14
    ; A% d0 U; y- o" I: w6 \) e3 L2 n154 q5 Y6 P; \7 s1 v5 `# E
    16! W5 v7 x4 f# M5 M, s8 i
    17
    - T4 p! W/ H! N, k9 \, y0 ?18) S3 e- f- {" i" S3 ?" B6 U7 [' V% L
    198 o* O4 {$ f4 C7 k
    上面的这些字符串,等价于使用如下的 Offset 对象:& p; e* u) d6 q: X" T2 F5 C+ |
    % L- M, [3 ]; O: W! @" N
    pd.date_range('20200101','20200331',
    6 u* w9 p0 J8 U! |5 a              freq=pd.offsets.MonthBegin())/ j! X- n) F: L( _" K  W

    ; X" E7 l9 O- q: }5 UOut[96]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')* f; g3 s; U* Q& s- V9 i; I& S

    $ a, r6 A9 {  x$ F- i' c7 j# Jpd.date_range('20200101','20200331',
      n* Z  V, k9 i" k4 e( T0 m6 e              freq=pd.offsets.MonthEnd())1 T6 T1 Y" }$ o% W# ]- b
    & y  Y) a3 C! ~* U
    Out[97]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')* I) X, |- k0 v( ?; t

    5 Z' F8 u6 K& ppd.date_range('20200101','20200110', freq=pd.offsets.BDay())
    % Z+ |( \2 s7 S7 C% D, c; [Out[98]:
    ( \# t; s# e" f* M3 T, cDatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',4 C' q$ F. J' r1 }& M, V
                   '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],. s, r- [2 D/ c2 K# B: Z, U
                  dtype='datetime64[ns]', freq='B')! U$ S0 B( m$ o6 S* F3 ~0 q

    % Z5 I2 g$ I& Z9 g0 Z4 _pd.date_range('20200101','20200201',. U( m" `; o" ]: @: g! P; Y7 A
                  freq=pd.offsets.CDay(weekmask='Mon'))
    ; n5 [* [8 S  a& U+ g( s3 H' Z8 V+ ^
    Out[99]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='C')
    6 k9 u3 T: _8 i& z, c
      d8 a9 x0 @& dpd.date_range('20200101','20200201',
    2 F& K. D' ?" O0 w! }. }0 P              freq=pd.offsets.WeekOfMonth(week=0,weekday=0))
    : U1 U  i& `3 j$ z) N5 a$ t$ o; o* `3 M1 ~5 S" d
    Out[100]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')( \( ]% a, \2 Y

    0 X/ Y) u! Z- e13 p/ j' |* x8 k( e2 z1 g! s  |
    2$ A1 R( ^) |, W$ ^% ]
    33 z  g0 n2 s- Q$ W+ {
    4" j& [& {) G' \5 U5 y2 G4 O
    58 ], J- p7 q# ^( }
    66 ^7 L/ k' G) V
    7" R. f3 A  g  x+ J
    8
    ' w" ]$ ^; N7 n5 S9
    / S9 ]; V0 n2 N/ ]# K) q10
    6 P4 X; c" K3 E2 o( U0 z# W11
    " C8 k- O& Z* |% @4 @9 _( J# ~12: @+ V. y$ G1 K
    133 g) w5 M& j3 n/ e1 \( a& k
    14
    6 W9 S% D8 D1 Z) F$ N154 \& [7 R% n+ f& L6 ^( @
    164 |1 J' J5 M. Y  ]5 R
    17$ \& @2 B1 M* A1 k
    18' _; q! Y  V6 y8 g- z: e$ M
    19
    1 k2 e" }# L+ A  Q20
    ( Y4 c& i; G' y7 o0 ], z21
    . w2 l9 |' j0 r8 p' k22$ t- }- t0 `  J, R$ X3 w- y& k+ X
    232 ]3 q, S0 @" X* U
    24
    3 s+ i! q2 [& U  k2 e- Y2 P  \( @1 \& h25
    $ c& D4 A# h' [; \8 b# p1 u3 e【CAUTION】关于时区问题的说明1 f' b* P/ N0 j3 J
      各类时间对象的开发,除了使用python内置的datetime模块,pandas还利用了dateutil模块,很大一部分是为了处理时区问题。总所周知,我国是没有夏令时调整时间一说的,但有些国家会有这种做法,导致了相对而言一天里可能会有23/24/25个小时,也就是relativedelta,这使得Offset对象和Timedelta对象有了对同一问题处理产生不同结果的现象,其中的规则也较为复杂,官方文档的写法存在部分描述错误,并且难以对描述做出统一修正,因为牵涉到了Offset相关的很多组件。因此,本教程完全不考虑时区处理,如果对时区处理的时间偏置有兴趣了解讨论,可以联系我或者参见这里的讨论。. N& u( g, A. T9 N& i1 m

    ! d( i( Y7 _/ X+ K, Q& o10.5、时序中的滑窗与分组
    ) o  C8 z. j9 K4 I8 y9 J1 Q0 W) i10.5.1 滑动窗口
    8 S7 H6 d: z. v, e  所谓时序的滑窗函数,即把滑动窗口windows用freq关键词代替,下面给出一个具体的应用案例:在股票市场中有一个指标为BOLL指标,它由中轨线、上轨线、下轨线这三根线构成,具体的计算方法分别是N日均值线、N日均值加两倍N日标准差线、N日均值减两倍N日标准差线。利用rolling对象计算N=30的BOLL指标可以如下写出:) F1 t. ?) W1 o3 p5 J" Y
    5 Q! E$ n1 x7 b! |# @
    import matplotlib.pyplot as plt- J1 I9 a; _( D4 m5 A
    idx = pd.date_range('20200101', '20201231', freq='B')% x& A3 v# S1 T# S( h
    np.random.seed(2020)
    5 H5 \, s2 J; Q, x& k& b% g; O4 D. U; |8 Y
    data = np.random.randint(-1,2,len(idx)).cumsum() # 随机游动构造模拟序列,cumsum表示累加0 g) A/ B. ~* u! ]# m
    s = pd.Series(data,index=idx)# e0 R, h- B/ F' G2 \' p5 M- O5 N
    s.head()* A. U5 m' N# @) D* q0 y
    Out[106]: ; s* z+ t: K, n- \
    2020-01-01   -1; u" V* t3 ], `) l* e
    2020-01-02   -2
    " d7 `3 M' t1 [3 I8 M0 k: c2020-01-03   -1
    & m, Z) A5 Y. }* t1 c# i# @2020-01-06   -17 g4 x" a% y. Y8 W
    2020-01-07   -2$ W4 b4 N; P6 l" {6 S0 |
    Freq: B, dtype: int32$ t3 L% Y4 l8 |! }7 W
    r = s.rolling('30D')# rolling可以指定freq或者offset对象9 s2 Z) |5 x4 I$ h# t

    2 p; U  o0 K! H/ R5 @, K6 Fplt.plot(s) # 蓝色线
    * ~) _# w! ]- u" X2 mOut[108]: [<matplotlib.lines.Line2D at 0x2116d887eb0>]
      W1 d! @, ~. N+ Oplt.title('BOLL LINES')0 g# }: ~9 v& N8 ^# U1 e8 W5 B) t+ {
    Out[109]: Text(0.5, 1.0, 'BOLL LINES')9 J( a  Q3 ~( Z7 A. n  v) |+ @2 u
      k- m& q, k# M' w; b
    plt.plot(r.mean()) #橙色线
    5 ]/ [9 z' y" [' sOut[110]: [<matplotlib.lines.Line2D at 0x2116d8eeb80>]
    + A( O- t8 ]4 \% C8 i2 J0 N
    4 G1 L: G4 }4 u9 i! P! S( P8 Eplt.plot(r.mean()+r.std()*2) # 绿色线7 G. @( s) l3 O4 ^. H4 T3 p
    Out[111]: [<matplotlib.lines.Line2D at 0x2116d87efa0>]
    " \& n0 S' y7 w! B. T* |8 p& P1 H+ p* Z
    plt.plot(r.mean()-r.std()*2) # 红色线, u2 t& o4 Q' Y$ T
    Out[112]: [<matplotlib.lines.Line2D at 0x2116d90d2e0>]9 `9 u# k0 F  G3 j0 S+ c& L

    / f9 l5 N$ O' m8 T) {1
    2 Q) s8 F& p4 O% y; P/ q  g2
    $ J! u& m3 A6 {" H( ?- ?) l3; [/ R9 V- i0 O+ x$ d" R/ d/ h) Y
    44 Y! G; P, X5 V$ W
    5
    9 e$ y3 V) t5 x6
    * d4 b  ~9 b, o( Y7% {- R0 @  L( u/ c. {, E/ `3 Q8 O
    8
    $ o0 H0 m0 t( a! E4 E* N( h8 n( P/ q9  c$ ]% x$ s. p, Z0 _
    10: [7 K* g$ ~& t. C* r
    112 \/ t, z) \9 [( s& E
    12% s, |) f' {2 j- G
    13  G6 T% o* f5 I& r/ y
    14
    ' K( A& t5 n8 \15
    ; X2 h! h' |8 k+ R* ~16
    ! q! e1 U$ P' h; j17
    , z: x3 ~1 G# \) G1 T' [$ P18
    ' h$ N. _4 h" |/ l! J( W: [19
    # [; `" D  O, }+ O8 [8 P) I' U209 c1 S& n4 l: e5 A
    211 L" Q0 I) X4 F" H
    22* h) \* o- B. N$ {7 j9 @' n
    23, Y0 f$ R" N- ]* C4 N
    244 q3 p" v$ j) A5 f6 D
    25
    1 P0 k8 y: Q$ ~6 }. z26
    4 L1 x4 U: j2 F6 ?1 Q9 g27
    6 P6 U: Z. z9 o# M/ R6 ~9 ?28
    # p- |0 M3 C  K) Y292 D; Z6 o- Q# K/ z  m- ?7 [

    2 H0 C$ |+ H( H& y$ r. s8 N   这里需要注意的是,pandas没有实现非固定采样频率的时间序列滑窗,及此时无法通过传入freq字段来得到滑窗结果。例如统计近7个工作日的交易总额。此时可以通过传入多个函数的组合来实现此功能。
    6 m: A, P& k- p% m: s4 T: e   首先选出所有工作日,接着用普通滑窗进行7日滑窗加和,最后用reindex()恢复索引,对于双休日使用前一个工作日的结果进行填充。1 h, p" @6 A6 S: F& J% [

    0 w4 Z3 e! p& vselect_bday=s[~s.index.to_series().dt.dayofweek.isin([5,6])]/ E* u; ~. j! @3 Z6 Z0 O0 ?4 s
    bday_sum=select_bday.rolling(7,min_periods=1).sum()
    ; Q2 |- O/ ?, J5 V) ]* D: {8 bresult=bday_sum.reindex().ffill()1 {% m/ }+ P. K1 x3 i* G% Q
    result0 J2 w2 E7 j* W% W
    * O0 p! ~+ Q/ P! m
    2020-01-01     -1.0
    % {# ]& l6 ~" g, b" w- x2020-01-02     -3.0
    ) {+ Y( L9 ^( ~0 [+ Y! I/ h* N2020-01-03     -4.0
    7 f1 _% Y3 h! E! Q3 X0 p; f1 m2020-01-06     -5.0  R& Q6 W! `( }/ Q. ~0 H* t5 d
    2020-01-07     -7.0, r2 Y$ I" T/ k% R# P& T6 I
                  ...  ( Z- D9 Y4 u4 K0 e1 T
    2020-12-25    136.0
    + v( [; |( l9 o4 g, Y9 B0 t2020-12-28    133.0! D+ c+ m5 z5 j
    2020-12-29    131.0
    * }& N2 N8 Y2 L# w- Z- P- w- t4 ~2020-12-30    130.0* n) Y* D, n+ D
    2020-12-31    128.0
    . _% j+ t/ r2 @. u; R, eFreq: B, Length: 262, dtype: float64! |" y9 R8 V1 f) M1 |$ {
    ' ~0 F; l! a5 k) K) r$ A% n7 s
    1
    ( J# V4 x3 I# [2
    & Y7 C/ F7 s# p) W2 C3
    ' Y! F8 X& s% ^* m4
    - }  U: H) n( }& d5
    % z/ }7 e8 n3 ]6 r1 _% m, V6
    - I! }% \3 w; ^9 f' h7
    7 `8 W+ J- C( T5 U9 ]% ?85 i, w1 O: b9 t
    9
    . b+ N4 Z/ p  h7 D0 A10
    * A3 i5 r# `/ `/ e* S11) N, p6 T1 M3 G! `: R
    12
    ) I" F/ N  C9 B" m8 j13/ h1 P2 z9 ]$ `+ x
    14
    9 i, C4 l$ ^/ S! p15
    / k- H2 T8 `4 t! {# U4 [& G; @168 l: ^$ @8 `  |% l3 e. @) R
    17
    1 b3 C3 N& n4 t# k  shift, diff, pct_change 是一组类滑窗函数,它们的公共参数为 periods=n ,默认为1,分别表示取向前第 n 个元素的值、与向前第 n 个元素做差(与 Numpy 中不同,后者表示 n 阶差分)、与向前第 n 个元素相比计算增长率。这里的 n 可以为负,表示反方向的类似操作。
    3 Y! p- @  S3 \* X, k% I8 n9 Q0 y$ z+ }
      对于shift函数而言,作用在datetime64为索引(不是value)的序列上时,可以指定freq单位进行滑动:1 _) _; |+ w" l0 C

    - g. Z# M: e1 W) w* @# Ts.shift(freq='50D').head()
    : r! `' A% B0 p0 X- m4 w8 w- KOut[113]:
    3 I* @3 A+ q- S. O2 a/ ]2020-02-20   -1
    # g3 Y( {1 U, c5 v2020-02-21   -29 d; ~! r& X9 W, G2 s/ S+ N0 ]9 `- r
    2020-02-22   -1% a* O( J+ f* p6 K5 l, h
    2020-02-25   -1
    ; J3 Q% R5 w) \, C# _4 _2020-02-26   -2
    : u, _! y( _8 w2 B, Ydtype: int321 |" u; M1 b/ ^, O' r
    14 k; K/ B' k* [$ A7 L1 o
    2
    ; X* }8 C, k0 i( e3. P  f# o' ]- \& R3 y- S3 b
    4* M$ K$ t- n! J" ?
    5
    3 X5 r$ w: \, H/ a% S* s6
    * U% I/ {( t) w8 ?7# H( ]1 x2 @% _0 d$ o$ }
    8
    7 X' g! t2 ?! ~  M4 Y  另外,datetime64[ns]的序列进行diff(前后做差)后就能够得到timedelta64[ns]的序列,这能够使用户方便地观察有序时间序列的间隔:
    / w/ O1 u6 X  b' i1 a& c+ `5 C5 v  V+ j* ^1 b6 B8 P' H
    my_series = pd.Series(s.index)
    + x: ?3 D6 g4 O/ k+ j5 Y& @6 G# Mmy_series.head()" o. r7 _! o* o3 |% E% j/ T! w
    Out[115]: 7 }, Z& x8 v5 Q& a" }
    0   2020-01-01
    9 `. t, z6 w+ x$ l0 P. [& e1 ], U1   2020-01-02. q* f- q; y+ m& Z4 ~5 N
    2   2020-01-03
    + a* x4 F, D' D5 o1 m3   2020-01-06
    0 g2 c/ a2 r! X5 [; D# W8 s4   2020-01-07
    & X( ^6 I& N/ B. Cdtype: datetime64[ns]5 _6 [9 O$ B6 x+ {) n

    / Q% e# N' X( B6 \my_series.diff(1).head()
    * Y6 _9 X. \1 ?0 {. B6 p; \' J4 DOut[116]: & e" c" Z5 X/ t$ T
    0      NaT
    9 T1 b2 n6 z, j5 ~1   1 days
    ! e6 Q! I  T9 F, q1 v2   1 days$ L. f; \4 @/ q
    3   3 days
    6 x5 j  K' p+ z% J4   1 days
    4 n. J. u0 F3 B$ Wdtype: timedelta64[ns]5 Q7 o( i* T9 x( U1 f
    . d& H0 t# ~5 g3 {. G& `, e
    17 ~& d+ ?6 a" Z* `3 `6 R
    2
    - N$ D! a1 @% F' r: M( t' k33 P! F8 k3 x6 \- \$ s" X3 U
    4
    0 K& O' [' a5 `4 A$ l, a5
    ' S, J# D3 s0 y" {" h6
    / B' m  y  N% @) R  B7) S# B' m+ I# n* h. v/ A$ u+ c, f8 b) f
    81 Z9 E5 G! I& ?) Q! z# d) y( i! Z1 f
    9/ w  L! T% F* R' o- i; U
    103 p( d$ E3 Y0 a/ U& F, T/ F
    11- A6 O0 c* Q% Q# _/ s7 t
    12
    0 M4 r( a* Y8 a9 S13
    2 ~% Q) j; P3 z14
    6 Z: p3 N: v) n; `157 `4 h' Y9 X1 H& c
    165 {) l. T$ I' k6 b' W
    17
    6 N/ \/ h& ?% G5 X0 p18) Q6 Q; k, U5 O1 ?  C1 a! r) ~+ ]1 v
    10.5.2 重采样
    ! A' h3 e: i* \5 h" n( C  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)2 V. U0 f! {/ ~
    常用参数有:, r1 F. j4 v) i( D' ]

    - o1 u4 M- O+ x, a$ S3 D) j2 q: Trule:DateOffset, Timedelta or str类型。表示偏移量字符串或对象
    & c0 }$ k  n& `axis:{0 or ‘index’, 1 or ‘columns’}, default 0。使用哪个轴进行上采样或下采样
    7 x+ N2 e: i$ }5 Y- A# M5 f: oclosed:{‘right’, ‘left’},默认None。表示bin 区间的哪一侧是闭合的。所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。; s0 r, m# ^2 h! \* Z# B% E6 f+ F
    label:{‘right’, ‘left’}, 默认 None。hich bin edge label to label bucket with,所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。- L0 |: p) d# c/ x7 q
    convention{:‘start’, ‘end’, ‘s’, ‘e’}, default ‘start’。仅针对 PeriodIndex,控制是使用rule的开始还是结尾。
    . ~0 n8 e) e( m" h9 y1 e0 pon:字符串类型,可选。对于 DataFrame,使用列而不是索引进行重采样。列必须类似于日期时间。$ C6 K7 h7 l  p9 A$ Q
    level:str 或 int,可选表示多重索引MultiIndex的级别,这个级别的索引必须类似于日期时间。
    6 }9 z: L' H2 l5 B% m, jorigin参数有5种取值:9 f6 Z& C9 F1 t( V( C
    ‘epoch’:从 1970-01-01开始算起
    5 C5 @; T' v: V! R" t. C5 Z, A8 W. s‘start’:原点是时间序列的第一个值
    3 s) E$ z% Q3 y5 }0 H. p‘start_day’:默认值,表示原点是时间序列第一天的午夜。8 u: S1 I+ U0 q! r
    'end':原点是时间序列的最后一个值(1.3.0版本才有)
    & n9 H$ p2 ]: v5 l# |  f‘end_day’:原点是序列最后一天的午夜(1.3.0版本才有)
    3 m, N: t; O) x& Ioffset:Timedelta 或 str,默认为 None,表示对时间原点的偏移量,很有用。
    ) j9 H9 g1 o% Q! z  closed和计算有关,label和显示有关,closed才有开闭。
    " d* b+ ^7 J# P* R& l  label指这个区间值算出来了,索引放区间的左端点还是右端点,closed是指算的时候左端点或右端点是不是包含。
    ' _" y; W& C' @- u6 L- P% N! f* P5 s) C
    重采样对象resample和第四章中分组对象groupby的用法类似,resample是针对时间序列的分组计算而设计的分组对象。例如,对上面的序列计算每10天的均值:
    % L  n/ J; g/ ws.resample('10D').mean().head()3 P) u3 |9 x9 E" r6 l
    Out[117]:
    ( g7 |+ G, \" ]* k) N3 s; L2020-01-01   -2.000000
    ! I% E7 T) _- M% B2020-01-11   -3.166667
    1 o& `7 b2 z1 P, E& K2020-01-21   -3.625000/ E  `, c$ @6 y# a1 n& V1 d8 y' m5 O9 s
    2020-01-31   -4.000000
    1 [4 \9 N0 A& Q6 G2020-02-10   -0.375000
    0 \/ q! f. F3 ]Freq: 10D, dtype: float642 C- d8 `& ^3 ~6 g2 X: F. ]1 I) _
    1
    0 D; e: o2 C$ M2 P2' }% b# Y5 `7 m* n
    3
    $ t) {+ c( W1 V( o; K+ H) }4$ M; z& q. O  a8 a6 \4 Z# l
    5
    ) V( ?2 x/ ^5 V8 t* W( M& G+ P6
    * X1 d7 f7 }9 {# }; _' N7, Q. o. ?" D7 W/ ~4 a
    8
    ; B2 v" P+ E7 z% k7 S* a% a! @" F! q4 J可以通过apply方法自定义处理函数:% D& i% T/ H8 w" g) @
    s.resample('10D').apply(lambda x:x.max()-x.min()).head() # 极差
    0 X) {9 n8 k, {2 H6 S5 ?8 o  n% u' T+ g% X# O
    Out[118]:
    % Q3 _7 t  W# z+ e. E! ]2 G! c2020-01-01    3
    ) w- `* U$ k& m2020-01-11    4' h  q2 u# \' I! {. K
    2020-01-21    4
    % o  N4 w6 u; ]2020-01-31    2
    3 U1 `# f: y. V8 f1 ]2020-02-10    43 a6 o. w  \3 O  e, [
    Freq: 10D, dtype: int32
    2 y0 c) P8 d/ b# M' |2 m: Z1! V+ k3 t  w. E/ K' J9 v
    2! P9 @8 D* \4 w( G
    3
    7 E  g/ {3 ^; S2 B% z4( i/ `; q+ @3 w- B) [& [6 z
    5
    5 f7 l4 g) |( E: Q  c6
    ; K* j# `3 ?& y  A+ K: ^8 b6 {7! E% o1 ^$ S8 q4 {% j
    8  y% Q9 i+ e9 }' a
    9$ x" k4 u7 W# [' {/ u& U# B  }
      在resample中要特别注意组边界值的处理情况,默认情况下起始值的计算方法是从最小值时间戳对应日期的午夜00:00:00开始增加freq,直到不超过该最小时间戳的最大时间戳,由此对应的时间戳为起始值,然后每次累加freq参数作为分割结点进行分组,区间情况为左闭右开。下面构造一个不均匀的例子:
    , }. X. v9 {7 M1 I& o2 ?0 f/ C. k( s
    8 t' ]6 A3 H! eidx = pd.date_range('20200101 8:26:35', '20200101 9:31:58', freq='77s')
    " L( q4 i- g$ Xdata = np.random.randint(-1,2,len(idx)).cumsum()$ Z& }" f3 H4 N
    s = pd.Series(data,index=idx)
    % Y# M  s$ b/ Y  Is.head()/ h& ~0 B) V0 o; F4 r

      g) O* Q! m+ b3 ^# IOut[122]:
    . X8 A1 n; m! y- K% }' l; [2020-01-01 08:26:35   -1( C* {* t! N5 a  q0 b7 z& @
    2020-01-01 08:27:52   -1
    % _: j6 t, O$ a) w+ K' r, V/ j2020-01-01 08:29:09   -2  t3 C6 G7 [3 c+ l
    2020-01-01 08:30:26   -3( Q$ Q& d2 h4 j2 S3 ~9 H$ E
    2020-01-01 08:31:43   -4
    - h9 t5 x: \4 g* |  ]; p7 g( ]Freq: 77S, dtype: int32# c$ ^4 ~2 G# u5 t
    1
    1 U( v5 c% D. O2; o' I, u# ]2 S9 m( c  [
    3
    4 P, l5 l, \* i. u- H- s- B7 u! F4
    & k" r6 {) B* u1 Z50 M" y/ O' {7 b6 r6 R# E
    67 t" j- B# Y5 Y+ `
    7
    7 n6 ]) X9 b# h  v88 x8 L4 r6 ?0 c- s+ X1 t' z5 x( L
    9# j* ~. ]7 z7 M0 b4 T
    10
    + L/ I/ S% U. E( J) A7 L11
    - ?& g8 @: r# ^7 O12
    * `* Y1 ?3 G. R) ~- ?# g$ M& X  下面对应的第一个组起始值为08:24:00,其是从当天0点增加72个freq=7 min得到的,如果再增加一个freq则超出了序列的最小时间戳08:26:35:. W9 I9 i# o" \
    1 U# R; @# o9 V& q
    s.resample('7min').mean().head(). g$ H% k: r! [. t+ Y2 u' K3 e# Y
    Out[123]:
    5 ?) V+ s' b8 v0 E* Y" Q2020-01-01 08:24:00   -1.750000  # 起始值,终点值包含最后一个值
    ' R" A& v$ w2 |& H  R2020-01-01 08:31:00   -2.6000006 S. L5 A, D/ m' }4 B
    2020-01-01 08:38:00   -2.166667
    & D2 \; Z, N% f' e: Y* f& K; A2020-01-01 08:45:00    0.200000
    ' V; M/ v* M9 v2020-01-01 08:52:00    2.833333; _. u) x2 s' S. I4 _1 A
    Freq: 7T, dtype: float64
    % Y, P3 I; k* D! F, {: h$ a17 p9 p. `# s: R& ?7 U
    24 f. o/ {% ]+ U* f: ~- O+ w7 S; g
    3
    # z) R! P$ B  r  e  @  b! I: k4
    ) ^" p, _1 b9 Q4 {( j& A& s" A& W+ x5& n7 f2 v; \+ d
    6
      ?+ g8 @4 B/ }7 Z: Y7
    0 N/ I$ X  U2 B9 |% @8
    / c: u) u5 W% Q0 n7 z+ D4 W  有时候,用户希望从序列的最小时间戳开始依次增加freq进行分组,此时可以指定origin参数为start:
    4 @: Z5 `5 y2 K0 x: j" p. u
    8 v# F; c1 [& b1 Os.resample('7min', origin='start').mean().head()
    9 `0 i- N9 G. U; o* vOut[124]:
    8 ]+ W4 L/ @% c9 O6 {! s2020-01-01 08:26:35   -2.333333! r/ Z0 }: e6 J1 O) ~/ ]+ m
    2020-01-01 08:33:35   -2.400000) I/ p' p9 @/ ?: R  u5 S3 A+ R7 f* D
    2020-01-01 08:40:35   -1.333333; }% v; Q* q: u5 H9 e6 `
    2020-01-01 08:47:35    1.200000" n- r" }( B- d: p; V. y
    2020-01-01 08:54:35    3.166667+ z- h& y4 [- @+ s) u: |
    Freq: 7T, dtype: float64
    1 G& n, r* q6 @1 q5 W4 G2 V1
    ; h: d4 V% I9 b7 h$ o2
    5 Y0 Y( i: o7 C. n) I3
    * g( D  X2 z: y$ A6 ]4
    ( w0 w5 }2 t1 ^, B" X: v5' b5 ~# m1 [4 c
    6
    : H7 J- W# L: O( `( _' q7
    ( W4 t: B; K7 W8 Z84 ]! G+ a  T! `  |% P& N  x8 y3 l$ \
      在返回值中,要注意索引一般是取组的第一个时间戳,但M, A, Q, BM, BA, BQ, W这七个是取对应区间的最后一个时间戳。如果想要得到正常索引,用’MS’就行。
      F5 j9 W9 I* n% g% z2 j4 t7 g
    - t1 C- X1 V. b4 r3 Cs = pd.Series(np.random.randint(2,size=366),
    * I2 [) \8 ~' Q  R: v! @. d              index=pd.date_range('2020-01-01',
    - e# \' F3 V2 T. `7 Y                                  '2020-12-31'))
      H! j+ y, a, x( e2 ^; J- z) H' |3 p$ L: B; m/ c  U
    3 I" w* M. P% S' {# ?# W! Y
    s.resample('M').mean().head()" X. T* M3 U5 [, s! M, \* U3 n8 Y
    Out[126]:
    3 E% i; M/ t, C; _, a8 L8 a/ G* d$ V' V2020-01-31    0.451613
    & s- ?" V  z( y" L3 i2020-02-29    0.448276
    $ q1 @: X2 h/ {+ m2020-03-31    0.516129/ L6 G% _% @( J) t8 Y' E
    2020-04-30    0.566667
    1 G: }* r3 r3 P' R. W! f, S5 X2020-05-31    0.451613- z2 |' c! h8 G: D- p& X
    Freq: M, dtype: float648 I8 o6 Z) y9 l) M1 b4 Q! y
    : U" H( A, }" S1 P7 ~" G2 J
    s.resample('MS').mean().head() # 结果一样,但索引是跟正常一样
    * {* W- i( ?7 b7 q! lOut[127]:
    * ^  x3 J& @* c) E; Q' h# {2020-01-01    0.451613  d1 ^) X  g. f2 l2 a' N: k/ n
    2020-02-01    0.448276
    9 p7 k1 I0 ~/ h3 z7 \2020-03-01    0.516129
    4 M) w: b! A* j. d2020-04-01    0.566667% O" W8 Z# X2 L  F) T
    2020-05-01    0.451613
    3 X. Q! o- x" l+ p! L( Y$ NFreq: MS, dtype: float64
    * O2 O; z( L% |0 t! M/ E- `* }; @/ _! Y3 e7 N0 @5 B( v7 f) D7 X
    1
    0 b1 b$ r! |& n2 ]+ a! y2& P) {9 B8 z9 C6 k2 U9 Z
    3
    , D2 t$ `7 B( d; ?4: m: A! e$ U7 p' c/ _, T
    5
    5 W8 a- c- U3 h( g6
    4 J- s4 ?  X) j6 S7
    / d- h- Z/ H2 Q5 B( u; s1 V0 c8; O" K7 O9 {! R, B) |2 B& M5 n6 ~
    9
    $ K1 A6 u, j; B# W10
    1 \. x4 m6 V7 }5 ~/ r) n11
    * D' T0 L- p. i  J12
    3 K4 z% l" E7 Q3 o  G, R" ?& o13- r! ^, n" q  B6 L: b9 Q
    14
    0 j2 M; f- W# q4 x3 O, k, z6 H0 B3 f0 j15
    . R% A9 ^* b" Y16' }7 Y( j& T1 |
    17
    ! Y7 ?" \3 {* y$ H18
    3 o% c8 W# |" z* h3 w$ m5 X19! s  N& n/ ]4 P/ l
    20" B' o1 E$ K7 A6 Z) u& I; l: O& X
    21
    & r  s- [- q/ r% y, D22
    2 f1 R% w; g2 g对于 DataFrame 对象,关键字 on 可用于指定列而不是索引以进行重采样:
    1 |' ~) F3 d* Z# Ld = {'price': [10, 11, 9, 13, 14, 18, 17, 19],
    , e2 Z, X: u9 Y# q4 [     'volume': [50, 60, 40, 100, 50, 100, 40, 50]}+ ^5 v+ h7 {0 ]4 f$ y, O
    df = pd.DataFrame(d)0 P, U: }, B7 t1 b  A+ C
    df['week_starting'] = pd.date_range('01/01/2018',
    ) o4 j2 L' ^! G0 |                                    periods=8,
    * G4 R4 i' T# k$ G! A4 T, [                                    freq='W')& N0 O1 q& ]: `% ]! ~
    df$ j6 A) q5 H' V9 g1 Y; s1 F
       price  volume week_starting1 k1 r* o5 Y& G0 s1 T% v
    0     10      50    2018-01-07
    2 B) q8 I& a3 {5 O$ Q# O1     11      60    2018-01-14
    * y9 F  `6 n  @  c2      9      40    2018-01-21$ t4 }  x! T1 o
    3     13     100    2018-01-28
    $ A5 J/ `% J) s4     14      50    2018-02-04' h( q& m% A, U; Z
    5     18     100    2018-02-119 O$ N7 t$ y2 d; [
    6     17      40    2018-02-188 \$ v1 D' I9 |' G. S, \# x
    7     19      50    2018-02-25
    : E4 G, q8 e+ {df.resample('M', on='week_starting').mean(): ]' v$ @% f; N5 D4 F
                   price  volume" E) W* t, t$ Z' p$ D% q; N& ^5 C
    week_starting
    3 [* k  x% E# t0 F+ w2018-01-31     10.75    62.5
    0 R0 o5 g( M$ J# P  u) h2018-02-28     17.00    60.0! l. |4 y  h/ E$ [" a

    5 H& x0 V/ M7 k2 F) y/ w' U$ U5 A! V1
    ) q: _; w& n/ B" Q  S1 Z2# h8 A- ^# _  a3 v& ^0 k' l5 J: l
    3, C; s* A6 U3 O4 ~3 J! h
    4% ?5 Z; [+ U) {( |
    5
    ' Q: x" E6 r$ d1 j' Q6% F6 j" u& `% ~8 `0 c
    7
    1 j9 U  q( H! R, r* i; O3 j0 }8  N! k9 I7 L9 M/ D3 U
    9/ c( _% G8 |' T
    10$ F: }) T' n  p% C" c
    119 S- x* F& t6 `+ A; {1 x
    122 T" z0 L. u+ O7 O# g1 j
    13# K0 ]# l( W& K3 u* l
    14
    # s5 a' L" F7 G& T5 x! b15: U/ K! ~2 a( s( o7 B
    16, N9 i) p6 w0 j
    17
    " B6 u! j3 x5 |+ s7 e; a18
    ; q# L/ S1 c+ U' z# n! T' a19
    . k5 p& R# O9 v2 i20* v  ?2 _0 Z" Z0 G* g$ h
    21
    % @3 }- e! w' c# E  |对于具有 MultiIndex 的 DataFrame,关键字 level 可用于指定需要在哪个级别进行重采样。
    7 A) U# C# M7 H. z3 S2 m2 zdays = pd.date_range('1/1/2000', periods=4, freq='D'), O: H6 U, ^6 g9 S8 j
    d2 = {'price': [10, 11, 9, 13, 14, 18, 17, 19],
    ; e" m; [' K" Z/ U; M/ H      'volume': [50, 60, 40, 100, 50, 100, 40, 50]}
    2 M2 B. V# ?; @) Edf2 = pd.DataFrame(
    2 R9 h' B2 X! f' P( t    d2,
    " ~$ G3 G3 z. t4 O( C    index=pd.MultiIndex.from_product(8 j6 @3 T1 A- E$ w( X' H
            [days, ['morning', 'afternoon']]
    8 z* {( t. z$ W+ d( N+ b    )
    8 V4 |& S3 G- n1 x# y)1 V0 |* U5 c/ V2 K: l
    df2
    4 u- ^  r/ e& a/ ^3 ?& S1 p                      price  volume7 M: R  V! Y: ]4 a& I
    2000-01-01 morning       10      50& A4 h" b/ k5 L- w
               afternoon     11      60
    & I0 e: q" P) i; u" {2000-01-02 morning        9      40
    , `- S0 E* n8 Z( R           afternoon     13     100& p4 m5 H3 c' F% F1 x
    2000-01-03 morning       14      50* G$ h' c+ w7 b! B1 O
               afternoon     18     100
    ' R: U& N) b  |# H5 Z2000-01-04 morning       17      40
    * r  ~& u: h. L/ M+ I           afternoon     19      50: Y+ M. N) a2 {# {/ [
    df2.resample('D', level=0).sum()
    * b. D* ^) D' b/ c7 x            price  volume
    # U4 \/ n# j5 R% E" G% \, ]2000-01-01     21     110  e  V! F5 v# ~( T) M% m8 {1 L0 x
    2000-01-02     22     140- L  u9 `% i7 }3 k8 q' \+ v
    2000-01-03     32     1508 G8 v7 l9 O; n  s' |% t
    2000-01-04     36      90
    * [5 r1 e- w7 w& K) e! e7 X+ D$ I* m3 D0 i4 r: a7 M3 \5 c
    1
    4 I5 r4 Z( p( E( I2
    ( q- \/ X3 I5 c6 a6 y3/ T8 C9 C5 D4 ^, O, c
    4
    0 @% U) g2 ~( V! y/ S) G  ~53 b: S9 Y; A: O, A" |
    63 c, V1 c$ s3 r8 \
    7/ u8 ]: e6 J7 \% T
    84 K; Z2 B9 u" Y! i, C
    9, J( F+ a5 M# }, \) Q( q
    10, z8 p3 S( |. \' o
    11
    6 P4 `' m# e8 r2 y12
    2 }, ~3 t9 F, p5 D7 g: |13
    & T+ [0 n8 R0 }5 q" K14
    6 G5 S/ T4 L7 V! L( I, V) G15
    % L: d! V9 N% s: N8 \. ]164 L% K  @5 u( f' N! T) F# r
    17  O" n) i" n7 X( Q# M
    18' M( }0 h, ?( _/ j0 u% F& Q
    190 Y( U; _. J, A5 P: O9 T
    20, D9 o+ O4 m0 n/ S3 K0 ^
    21& k6 N' E9 c% F+ j4 }0 @9 D1 L
    22: G0 {" x- T- B6 _4 z6 \! n, o
    23
    $ q/ ?- |3 `$ l' N. h2 _24
    # ^1 L4 e* \& ~4 _5 N25
    & ]# E& ~8 U# e$ e& N根据固定时间戳调整 bin 的开始:/ q0 E% Q8 E6 h/ q7 W' x) n
    start, end = '2000-10-01 23:30:00', '2000-10-02 00:30:00'! p* n/ z' ?7 J( U8 |* f. c7 L
    rng = pd.date_range(start, end, freq='7min')
    + I1 Z( x5 Y# {9 cts = pd.Series(np.arange(len(rng)) * 3, index=rng)
    7 S* f- {# ?2 D9 `  f0 W& _( Jts
    ) b! f5 ~. A; t# o8 L: S2000-10-01 23:30:00     04 m# o4 p& z0 E* a& Q
    2000-10-01 23:37:00     3
    7 |+ C1 h; ?6 J0 ]2000-10-01 23:44:00     6( `# o$ J5 O" B9 J* D; Z
    2000-10-01 23:51:00     9! e% y$ [- `' [' B$ H4 `1 [
    2000-10-01 23:58:00    12
    " A/ h3 r# s8 |- Y( n2000-10-02 00:05:00    15+ [' y& x- N) W+ C6 {
    2000-10-02 00:12:00    18
    3 r' D/ l- d; z/ g2000-10-02 00:19:00    21, q+ L8 i2 a/ m1 N
    2000-10-02 00:26:00    24
      m' g& P* Z2 r) m7 ]Freq: 7T, dtype: int64
    0 V# B# c) l6 z* l! K) q3 W' ]* t5 L. F/ N* _: [) c1 U1 F$ _
    ts.resample('17min').sum()
    $ I$ r& a- Y6 D6 c* d/ u+ a2 n2000-10-01 23:14:00     0
    ! ]1 b- s4 i7 \* t: _! v# P2000-10-01 23:31:00     9
    - a* X& a9 K1 {9 s2000-10-01 23:48:00    217 [, O3 v$ X# }9 E; p2 v9 Y
    2000-10-02 00:05:00    54! ~7 F% V% H" U" ^
    2000-10-02 00:22:00    24
    ; h5 P; V# V- N& D- R! wFreq: 17T, dtype: int64: \9 a5 |" z) D8 }

    % |- d6 c% r" v0 M4 Vts.resample('17min', origin='epoch').sum()
    3 q7 U' k0 M9 g0 r: z. a; E2000-10-01 23:18:00     0, u  Q% F5 R, {& n! \& I+ M" d
    2000-10-01 23:35:00    18
    : ?& A# H5 z$ f( a( {2000-10-01 23:52:00    27& b& m6 s/ k0 J5 e6 C3 i
    2000-10-02 00:09:00    39
    % V: J! v- v* o" m9 U2000-10-02 00:26:00    24
    6 i$ H( M) p0 Y) f/ KFreq: 17T, dtype: int64
    3 \/ W7 a3 D: y4 o% m# ^
    # ~4 G' o: B4 V7 r7 e7 A& U' N8 |ts.resample('17min', origin='2000-01-01').sum()
    ; d) N3 w# b; {9 h9 ]9 p7 }2000-10-01 23:24:00     3
    ' o4 @+ [! i, j8 H2000-10-01 23:41:00    156 P# E% Y* B+ x" B
    2000-10-01 23:58:00    45
    . ^7 F) X. V( @2000-10-02 00:15:00    45
    * D' v7 x; |9 b, T; tFreq: 17T, dtype: int645 V7 b, G5 T* X2 E0 N( a! k

    9 }2 c. B) g- ~1 `  H2 w  ^1
    5 Q# r: J( C. b9 m' u6 ~/ ~3 s2
    8 c! Q4 l6 V6 l! X- m7 P; z3) e1 P0 s, Y3 x9 X/ }9 Y1 P8 {1 q# l
    49 h1 T8 ?- s9 |, q/ E5 U  i; o: X
    5
    & F3 m; E: u* u  y6
    / {) B2 R) h, u2 w3 O7& A/ r9 E- i# k8 f
    8
    ) T7 A6 \1 i: b2 Y# r9
    ( d) I. F% a5 x# H9 F2 c% ?10
    ! b6 _* `% Z5 H" R9 v: ^11
    6 u9 J, a# N2 F3 P8 z# D! y12
    4 @, v! V0 [- s8 c1 ]& c13
    7 {% O) d4 W6 |* d" z- D; p14. [- q/ |) _6 O4 @9 q
    15
    6 S* k3 Y2 J5 P16( N# ]8 `: P* K2 g! _" x$ ^: v
    174 B# ?9 `/ [9 L- ?1 Z7 I
    18/ o: a/ m  j  `/ I
    19
    ' L  E9 j0 v5 r9 U. @: Z9 A" i& a8 b207 L6 y2 i4 g* p; }) v
    21
    1 O5 G" t6 O. h- K221 v2 v# L# K' A
    23
    - I6 K  T2 G9 R$ P" I" J24$ X# {/ T; c1 d; T
    254 R3 k. @" q2 T1 F, D) }- P
    26  A' y5 b# f  Y) ^9 J, P2 Z  E
    27
    ; H" w+ u6 M8 V! q28
    5 W5 X7 }8 l0 ~29: N( q7 l# _# d! l2 Y
    30
    , f. q- f% F( g6 F! q31
    & W+ I$ O. O: f1 C5 t/ t32; h6 ~! i; y# e8 L6 {
    33" A  Y" X7 m9 |( }& O. ?- f0 A( N
    34# `. Y. t# a5 A- j3 [# H" V
    35
      F8 M' k1 c7 ^8 ~* ?  ~+ x: _36( C6 e5 O* f# ~7 y; {! X9 I
    37. n  L5 g) v" x
    如果要使用偏移 Timedelta 调整 bin 的开始,则以下两行是等效的:
    1 g- K2 p1 L5 fts.resample('17min', origin='start').sum(), N7 t% P+ F, G' N1 H' n* z
    ts.resample('17min', offset='23h30min').sum()5 [: z7 z6 h$ L
    2000-10-01 23:30:00     9# x/ p) L: Y3 f  C6 G) J
    2000-10-01 23:47:00    21
    , V( O6 g! D. t/ t  C2000-10-02 00:04:00    54
    3 \1 v- I2 y/ j! x8 U2000-10-02 00:21:00    24) Y/ c9 s5 n9 s% H9 n
    Freq: 17T, dtype: int647 M' |  G# `- h" J' @% h9 ?
    1$ h( x5 T3 T! j
    25 K7 x  j" n/ U
    3
    , W# b, d, R0 m8 L1 c8 k% |2 t4& o. C% H! x4 [3 g: ~" D
    5: g. Y) X/ W+ \; Q  L* i. U4 W
    6
    * T0 N) Q' H* X1 S$ V$ |7
    9 P! \( v# v2 v7 S5 ~7 i10.6 练习
    ( ]5 }( ?5 x8 g( y3 ZEx1:太阳辐射数据集0 g8 O+ g6 A' ^( @8 w. X
    现有一份关于太阳辐射的数据集:% o$ B' V, {0 l0 R8 S

    " @. @3 y. V  C+ pdf = pd.read_csv('../data/solar.csv', usecols=['Data','Time','Radiation','Temperature'])
    ( M8 }9 ~9 s( I9 Rdf.head(3)
    ) Z/ Q$ c2 e  W: P
    - K. N/ B4 Y3 y2 G7 f6 N6 J. J) NOut[129]: 0 U: B  d- n9 o2 g
                        Data      Time  Radiation  Temperature' s/ q5 S3 ?# g$ z/ {6 ?$ W6 h3 i4 v
    0  9/29/2016 12:00:00 AM  23:55:26       1.21           484 x* z/ h0 `' p2 @/ }& |
    1  9/29/2016 12:00:00 AM  23:50:23       1.21           48
    : s6 C! B3 H7 I2  9/29/2016 12:00:00 AM  23:45:26       1.23           48/ \; S; j4 e1 t0 g* {- q  f
    1' L& L  z7 F& U, e1 K
    29 i. g) L- |) G% E/ j- F4 Q2 G
    3
    ; _/ ~' L" W/ ^, t0 R4
    # f  n# ^/ I* G; d54 ]: l1 o3 F* O) I
    6
    ( h& f4 Y7 d# |1 U7  r1 n" C* g+ ?2 Q( v! l$ W
    8
    5 g" y7 z4 v: o2 h+ y% t0 _: k% C将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。
    % Y4 f% l4 c: h$ V# V每条记录时间的间隔显然并不一致,请解决如下问题:
    9 h3 T5 G$ c! z6 C' m找出间隔时间的前三个最大值所对应的三组时间戳。
    ( U3 [* m9 [$ B是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。
    " B- O9 ]# y& ^: w) `( P求如下指标对应的Series:
    7 N2 K+ m5 z) [% _温度与辐射量的6小时滑动相关系数) s* P% `5 G& [' r
    以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列
    , l. }# d! j% `  u每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)% L/ R- Z$ Q7 B& _; m7 o
    import numpy as np
    0 Q2 \& t! m1 _" s* Mimport pandas as pd
    2 T  X7 f5 O% A( P( I1
    ( L$ r3 Q/ t1 L6 A; O& W2
    5 {/ {; ~# h: _* t6 N7 o将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。
    ( s- o& l9 O) n7 X8 a1 b6 zdata=pd.to_datetime(df.Data) # 本身是object对象,要先转为时间序列
    ! A' k+ u; W" }/ {times=pd.to_timedelta(df.Time)
    6 m2 O% k! b. hdf.Data=data+times2 n$ d6 c0 i3 _9 |( Q
    del df['Time']
    6 s$ {4 K( ~7 Ndf=df.set_index('Data').sort_index() # 如果写的是set_index(df.Data),那么Data作为索引之外,这个列还另外保留7 ]9 o4 W0 s5 C. \* N5 |
    df9 T0 Q; h1 E: V! f
                                            Radiation        Temperature
    ) o) y* V7 P1 [1 f9 j4 J$ cData                5 ]5 r, e5 \0 I! u' f
    2016-09-01 00:00:08                2.58                51. E, A5 K" J7 u3 {8 ?; \
    2016-09-01 00:05:10                2.83                51
    $ u& y" u6 ?6 P" x2016-09-01 00:20:06                2.16                51/ ]& s$ E. }2 E5 s1 B
    2016-09-01 00:25:05                2.21                517 `; m1 G  O3 N* s# i1 y$ @6 J) o
    2016-09-01 00:30:09                2.25                51
    . m% k! n# T- M9 U/ N3 v/ l...        ...        ...
    " @6 T4 R! u' \# ?* a6 G3 N2016-12-31 23:35:02                1.22                41
    % k: ?. \% h! n: x2016-12-31 23:40:01                1.21                41
    , v0 Y5 Q0 U) E3 @4 p3 K7 r! n9 ^& |2016-12-31 23:45:04                1.21                428 O1 C7 I  R- y& H2 a7 L7 e! r
    2016-12-31 23:50:03                1.19                41, v% K, A+ U; O% k
    2016-12-31 23:55:01                1.21                41
    & r, `1 v' t6 N1 h, {( U. X
    ( f* o: C3 i# t1) i7 S+ |1 U+ t/ V1 j) i3 t
    2% N+ c% S- w8 v$ N# i
    33 @& H: u- Q5 Z1 q4 \
    4" \) w: y6 {6 [+ `! M4 S" Z6 y
    5
    9 j$ t: C* n( Z+ C* q# O1 s) l9 }60 R# t; r6 d# a. o% h; j3 L
    7/ C. v' i% K  o. u- [+ |
    8
    5 ]0 a+ ^6 E* f( O# F( ~& f5 E9
    8 u! i% F  F5 r0 W10
    - G. ]8 d% v8 ^11) [; z. D1 T: z
    12
    0 `# E8 i- w; h+ I$ V3 J% |13
    0 N8 i  v* P$ R* e14
    7 v* J. t* ~! G15/ n3 D. p4 ?$ [- q
    16
    & q6 w8 m9 T7 W+ h/ N% E17; [" F2 E3 B' W7 [( {. _% z
    18
    ) l5 M7 k5 I% E" [( O$ g19
    ! D/ S0 ^/ E; A3 t+ G每条记录时间的间隔显然并不一致,请解决如下问题:
    & X3 p+ }/ B/ L+ W$ m$ h  z找出间隔时间的前三个最大值所对应的三组时间戳。
    * l' ]+ W9 p1 a! ^# 第一次做错了,不是找三组时间戳
    0 }$ j  Y2 b6 `2 nidxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]0 N- ?. ]% B* L1 b9 D
    df.reset_index().Data[idxmax3,idxmax3-1]
    & m( P9 q1 u" h0 [$ o" P! C) k1 ~, r, V3 L
    25923   2016-12-08 11:10:42
    ' `: J1 L( ^  Q4 [; c) M1 W5 r9 K, X24522   2016-12-01 00:00:023 N6 K. ~* c- h, [+ r- Y5 q
    7417    2016-10-01 00:00:19
    0 e1 w5 m) X( D  W  _, VName: Data, dtype: datetime64[ns]; g4 Q5 t7 q1 _: Z% M
    1
    5 q# {2 h6 D" j2
    3 Z) U0 j- P" ^( k0 K# V) N! Z3
    3 Q* _" U# P' v; I/ o4
    2 W: \) W! s7 I! a5* l1 Z6 A* n- A* q  F
    6
    ' \) G- k' ?9 n6 J/ g+ @* R1 M9 e7
    ; c. T+ p6 O* o. R$ `& W+ |3 V8
    ( g; _1 P3 k$ w2 A7 Jidxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]! @. G3 s) x$ E/ O! X# ?
    list(zip(df.reset_index().Data[idxmax3],df.reset_index().Data[idxmax3-1]))6 H# R1 v( @9 g
    " N' g: S3 M8 E) @* X, \
    [(Timestamp('2016-12-08 11:10:42'), Timestamp('2016-12-05 20:45:53')),
    + W4 v% q4 p- P$ p) o) _# O# V (Timestamp('2016-12-01 00:00:02'), Timestamp('2016-11-29 19:05:02')),$ Y3 _4 g, ?/ s' l7 u' |/ r0 m! C7 P- m' z% q
    (Timestamp('2016-10-01 00:00:19'), Timestamp('2016-09-29 23:55:26'))]
    7 \) e1 _$ X* b& ~; S1
    8 c" r: F/ C% N  P2, S! k6 q+ f3 B; s
    3
    . ~0 a7 d& E* w4 z4
    ) z1 W% n0 B% J( q$ U/ |  f: l0 ~5( K% ^. Z; {" E# G
    6# a# c6 J1 B0 t3 k9 E9 @" B7 ^: v
    参考答案:. d4 V+ {. a; y. n
    3 g3 G, K( G' U
    s = df.index.to_series().reset_index(drop=True).diff().dt.total_seconds()9 K, e, z# e2 i! s7 @
    max_3 = s.nlargest(3).index
      q- R. D3 E) j0 R9 r& |2 pdf.index[max_3.union(max_3-1)]
    4 B! S9 o9 H3 T. ]) J3 G, W5 ^. [7 i+ T$ g( Y
    Out[215]:
    2 x6 R' ^/ A* S' b+ VDatetimeIndex(['2016-09-29 23:55:26', '2016-10-01 00:00:19',% m2 N. W4 }2 _% K7 [, h
                   '2016-11-29 19:05:02', '2016-12-01 00:00:02',4 i% X6 Q. I# e# @6 @* V" Q
                   '2016-12-05 20:45:53', '2016-12-08 11:10:42'],* x1 h1 P4 s; J
                  dtype='datetime64[ns]', name='Datetime', freq=None)  e5 g( b; ]9 l$ B1 C2 X4 l! F5 V
    1# o/ O- P; N1 z! N4 Y3 s; v
    2/ v$ O1 v+ _3 N/ C
    3
    ; v7 @" |0 r6 P$ T) s4
    / t+ C& |# }" M! ]- p5% Q) v; c/ l1 R+ c$ a2 N0 @# l
    67 u! u+ }1 P$ ~  E2 c
    7+ n$ n0 H' I7 z: X$ p' A& ]# k8 f, r8 |
    8
    & }& z: i$ F# L) T9
    9 S/ A8 _0 b2 ]$ x$ q' ?' v是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。. W$ {+ n; @0 c3 S: t; K
    # 将df的indexydiff做差,转为秒数后排序。再求几个分位数确定取值区间
    5 c6 b) q" c. i* e9 {s=pd.Series(df.index).diff(1).dt.total_seconds().sort_values(ascending=False)
    : |' a" n" i- ?4 V( K. [* Us.quantile(0.9),s.quantile(0.95),s.quantile(0.99),s.quantile(0.01),s.quantile(0.03),s.quantile(0.05)
    1 ]" L( _) `* d0 z3 r& h
    ' b# r% u! c- i! g& k(304.0, 309.0, 337.15999999999985, 285.0, 290.0, 292.0)
    3 K8 A& ~/ X$ E3 z1 V1! s( B9 F: X/ _4 \
    2
    ' y% X1 C1 `5 z; u# S$ z8 s& H3
    ) i: s0 E$ v8 A1 W4
    , c$ ?8 z4 a& h2 u56 x3 [( O$ Y; c
    %pylab inline% b2 O2 F5 }: S9 m7 ?; l  X
    _ = plt.hist(ss[(s.values<337)&(s.values>285)],bins=50)( Q$ b2 G* Z0 o0 c  ?9 {- k
    plt.xlabel(' Timedelta')
    9 I% ?3 l9 l$ q! Lplt.title(" Timedelta of solar")
    ; f3 w1 ?  w" d2 h4 O( Z1; k' y0 B+ O6 O* m
    2
    ( t/ s, ~1 O9 n$ e& p3% x9 v0 V9 k( E8 M6 w' h
    4
    / ?/ \' p0 l+ r! k# |9 b/ [: C% k( R& p; A; u5 ?$ g
    ! @. S- o6 p" K$ D
    求如下指标对应的Series:0 ?$ R( R" a6 h. Q  ^
    温度与辐射量的6小时滑动相关系数
    : r, K, w' P0 ^以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列
    " \! A+ u4 l+ s' N% p" }2 g每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)) M( h& y! e2 l& w5 R$ q4 Q- @
    df.Radiation.rolling('6H').corr(df.Temperature).tail()
    3 H1 L6 ]) P+ j% q- D) R: J8 [# M( ]/ j' v
    Data
    8 f" p3 p) u. A! K: u/ L% q2016-12-31 23:35:02    0.4161876 u0 J( R- U% s' G. y/ `
    2016-12-31 23:40:01    0.416565$ ?7 \+ U$ `/ Y/ L6 J1 M
    2016-12-31 23:45:04    0.328574$ a4 d( w9 Z8 D% J4 F
    2016-12-31 23:50:03    0.261883
    $ z6 ?6 N3 H4 n0 `2016-12-31 23:55:01    0.262406
    ; i: ?" @8 }- f5 @dtype: float645 Q# g! }: h) K4 D6 V; k# p; n
    1" V" M$ ^/ H/ l/ N) B& r
    2& u: C& N0 C% ^6 @
    3
    1 o/ ~% j7 L6 f# t6 ^( a4
    - s, ~+ ?" c8 {/ F5/ _, b- Q1 u' D- Q: s! F
    6
    % ~8 F# F; V( T- R74 _( o8 p4 \6 i
    8
    , c. m0 I& N1 X$ f0 @; `% m9
    & E3 i9 N! g) @' Q3 g! I3 b* ~$ S; I7 ydf['Temperature'].resample('6H',offset='3H').mean().head()+ p! i4 E! ?. }
    : F- d  @% u5 D& j* s
    Data" Q! k7 D4 A: t! h" _0 F
    2016-08-31 21:00:00    51.218750) Y/ Q8 e, m8 S2 K2 O$ W; p) u
    2016-09-01 03:00:00    50.033333
    1 W& w- x2 b9 Y7 V2016-09-01 09:00:00    59.379310
    / k- ]" x# _' V, S* h8 A- V2016-09-01 15:00:00    57.984375
    0 |+ H+ f( G! C" D9 b2016-09-01 21:00:00    51.393939
    ( t, U  {1 g1 l6 J- _/ SFreq: 6H, Name: Temperature, dtype: float64
    . u  R& S) C1 M/ j% i1
    : Z/ W% q; W9 l* Q! T. p" D3 C  v2
    7 x8 @3 }# ~2 W: Y: z/ o+ F- `( ?3
    2 L4 t( s9 O" a; N3 q, k48 x) r6 i- Y  E/ H9 a
    5+ \8 [% k$ m" z: G- I6 }' T2 H  _
    6) g; B0 F  b) q! ~, _
    7
    # ~, D: G4 d9 a* c7 f# t6 N* q81 x# P, w" N- Y- L
    9- J- r. t6 X9 f4 v! T
    最后一题参考答案:( {5 B4 J& b( \9 @5 t- @- T' x
    2 c% `. x. D, z9 W/ L0 f* Z
    # 非常慢
    - {  o4 _/ m; S: U$ Vmy_dt = df.index.shift(freq='-6H')
    8 k- K, V# @. B. j4 }7 Fint_loc = [df.index.get_indexer([i], method='nearest') for i in my_dt]
    & _& ]0 J9 H, ^6 Y$ S- R  d- fint_loc = np.array(int_loc).reshape(-1)
    . V/ z9 `4 h$ Gres = df.Radiation.iloc[int_loc]$ _9 \! U2 T! S3 v% H7 H$ T5 d
    res.index = df.index
    ' }! p/ C! S' _+ C7 yres.tail(3)
    & @2 y; K4 i% A' J1
    & p7 O! ?! t! x# @6 C7 i4 d% K2
    ( @, i* |  e% X+ e3# h9 u# k' a, N/ C. t
    4
    4 f3 j# o# x/ c2 X5: V9 `8 f1 `: G4 N# d! O$ n
    6& a- M! N3 c2 w
    7: C. I2 L1 c5 K  z+ p
    # 纸质版上介绍了merge_asof,性能差距可以达到3-4个数量级" C/ T' h( p" @
    target = pd.DataFrame(
    ) P* q: Z* W6 N( C    {. u( W! ?( r2 G& Z" k
            "Time": df.index.shift(freq='-6H'),: ^- ?2 k8 Z$ _! c$ h8 r9 w' ~+ E
            "Datetime": df.index,
    ! Q) K- S; u! i8 o% l    }
    * H; y$ G, Y7 g2 s5 t0 C  Y)) g1 z, T' i3 A8 L; x8 `

    7 q3 A) ?# S; ~; |7 S+ _res = pd.merge_asof(7 j6 ?: N7 H+ p$ i5 g& p
        target,( ^- C! Q7 {+ [
        df.reset_index().rename(columns={"Datetime": "Time"}),3 _  \9 x: i+ k) ]! M
        left_on="Time",
    " o! ?- ~2 P* j" s* h    right_on="Time",
    + B& U. W; O) y, w    direction="nearest"% l+ l6 b& e' c; w
    ).set_index("Datetime").Radiation  D4 e7 v5 m# N( Q

    0 z+ t, ^1 Q# H6 O0 Tres.tail(3)6 N; X9 S* Z# z" Z7 f) V$ G8 j* V
    Out[224]:
    8 z4 a* |: Q4 c7 g. KDatetime
    ; K9 W5 j$ Z: r" p6 V3 G1 [/ v9 p2016-12-31 23:45:04    9.33
    % [  j* p0 K' c8 ]; S6 R2016-12-31 23:50:03    8.49# }' I* }" o. a* d
    2016-12-31 23:55:01    5.84
    ! ?) ^- C) p+ k" U7 FName: Radiation, dtype: float64
    3 B  X& t  k) F" L9 x: w  c5 i# `- O! A: V6 i" v1 w6 L/ |
    1
    0 t/ [! B  e5 F' s# z& s2- D7 R" \. y3 V9 Z2 U+ v: I
    3
    4 b( q. \) l9 l1 |& @4
    4 J8 }& W5 s0 h5
    : h- d# I* Q2 u8 T$ D9 Y6; G6 A) S$ Q" ~* _" O) Q
    7
    & v. I. k1 H. k, K) F: H  s* P8- |, Y* A; ~) J, T) |! g$ Q6 f
    9
    % J3 G% J5 E( W& b. ]4 ]10
    0 X, V0 V& ~  M- j/ @$ _9 W11
    & `, ?8 s! t# R( |4 s9 e120 W2 \( G4 A( V+ X
    13
    5 U! w/ C/ n7 z1 I14
    6 E  q/ u; g) ~( }$ Z15
    / n9 [3 @7 w+ z4 D* E16
    3 a* |* q2 M# ?2 t& A17
    ; L* q4 m4 ?+ z6 u; `7 F& k18* A+ M1 e5 G( M, Y4 ]: B
    19
    ! k5 g/ N. {' ]- \20' H0 F; K1 E' T& k+ p+ t% w  ]
    211 ]% N9 A* F" H7 x8 U+ w1 ^
    22! Q1 R; r: D9 |, Z- p' r4 o
    23
    , A. Y! u  U) A. cEx2:水果销量数据集9 E6 s8 [/ B# D9 X
    现有一份2019年每日水果销量记录表:6 a: C' K9 _# |+ l4 ~

    % O( T$ }1 q7 f; _1 R5 Udf = pd.read_csv('../data/fruit.csv')
    0 k; s5 K+ k  Vdf.head(3)( q7 S) w* W4 c7 a; U
    2 E0 G" _8 q8 }7 q
    Out[131]:
      D+ P% i0 b5 p' A( w         Date  Fruit  Sale( R8 ~- R. `1 e( _& b6 \
    0  2019-04-18  Peach    15( Z( k) ]+ Q( o
    1  2019-12-29  Peach    15
    & t* ]& i$ }5 q) f& n3 P3 d2  2019-06-05  Peach    19
    9 m. \- }/ ?" c' k1* {- N) d/ l( k% ~
    2
    ' V# p4 P. }6 ?5 f2 d31 G; }6 L* J/ g1 K: S8 D/ H
    44 |. Y& n" ?# T1 \0 Z
    5& z/ f* ~* U4 v- m, V0 i( Y
    6
    3 q9 t( {9 K3 q0 t74 a( o" U1 r" v5 P) t
    88 a& P; v& y. E. d: f, o
    统计如下指标:* a* L1 X: U, A  X
    每月上半月(15号及之前)与下半月葡萄销量的比值
    2 |8 S  Z2 |6 X每月最后一天的生梨销量总和: V4 p, [2 k8 t/ D
    每月最后一天工作日的生梨销量总和) A# l0 y. W0 h* d! P, K) t5 G
    每月最后五天的苹果销量均值5 }, ^/ u7 t  v$ f9 J
    按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。; u/ _; y4 n/ W9 q; e2 g) Q7 k- C
    按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。# `" q! Q* p  C$ }
    import numpy as np
    , X. c: J0 q* {* j" i0 W! _1 Limport pandas as pd9 O% H5 ?0 t$ h6 q- f/ Y$ g, G9 }
    1
    , x) F- r( S: z6 R' f8 b; h1 x2' F8 H. i* n, @
    统计如下指标:) n+ t+ r9 E- J- K  I9 A; h7 l
    每月上半月(15号及之前)与下半月葡萄销量的比值
    $ O/ A) Z% o3 F4 T每月最后一天的生梨销量总和) s8 D# x5 ]! l( R6 o' V7 l( v2 b
    每月最后一天工作日的生梨销量总和& l# h2 y6 x" X6 Q: _6 _
    每月最后五天的苹果销量均值* q2 A- f$ E7 S4 Y( v
    # 每月上半月(15号及之前)与下半月葡萄销量的比值  {$ X7 g5 D1 x. h
    df.Date=pd.to_datetime(df.Date)1 H" c9 N+ z4 t# ]# I
    sale=df.query('Fruit == "Grape"').groupby([df.Date.dt.month,df.Date.dt.day<=15])['Sale'].sum()9 l* b* K% s4 v; w0 q
    sale.columns=['Month','15Dayes','Sale'] # 为啥这么改没用啊0 a: ^- w$ P9 _& [* y, t: v% t- C% D
    sale=pd.DataFrame(sale)% Z+ d  T, I3 C5 G# s+ O( X
    sale=sale.unstack(1).rename_axis(index={'Date':'Month'},
    1 Q! }8 E1 v4 `  r/ m- B                 columns={'Date':'15Days'}).stack(1).reset_index() # unstack主要是两个索引都是Date无法直接重命名
      X; v+ Q4 V/ ?4 U3 M# xsale.head() # 每个月上下半月的销量( O* i3 x' S, N* L: g
    ; E( w3 m$ f$ M2 j. o$ X0 B% x
      Month        15Days        Sale
      n" d: a7 }; Z9 j% V0        1        False        10503
    3 K9 D& K& ]/ I( c3 x# _% V  Z; `1        1        True        123417 g. L# q. T" B# e
    2        2        False        100018 d2 Z- T" W. L! U; k3 B' e
    3        2        True        10106
    - F# b$ @. s, s  k4 u9 o. ?; i1 B4        3        False        12814
    3 Q8 l' J0 P, ]1 `' y
    : f: E8 ^  z; b6 J# 使用自定义聚合函数,分组后每组就上半月和下半月两个值,根据索引位置判断求比值时的分子分母顺序2 R6 N! x7 u2 \  w/ j, k' v$ V: M
    sale.groupby(sale['Month'])['Sale'].agg(
    : H1 K0 t8 z. c8 |                lambda x: x.max()/x.min() if x.idxmax()>x.idxmin()  else x.min()/x.max())  z4 i  f/ k8 T3 c; s! R9 `+ ?

    5 i, }" s& x, v7 t* i0 y( |Month
    4 {6 n5 Y4 F2 I" k( y( G1     1.174998/ r) B8 i! f4 _8 z( g5 v
    2     1.010499' b3 H2 D8 p/ N/ D7 l
    3     0.776338
    : T, X) l: T6 w& R* s4     1.026345( V& T( y% X. _2 W7 [
    5     0.900534
      \3 q: I' b0 Z  t! L3 k8 L# C6     0.980136
    3 }# n5 y8 O) l6 D1 @" }. Y7     1.350960
    : P% T* B& j* W4 R7 v9 a8     1.091584) j9 z3 U0 B4 q$ P, z+ v
    9     1.116508
    ) m+ z& _0 [7 ^10    1.020784
    $ p5 v( x8 [) r8 M11    1.275911' B5 i; F/ V" f* d; M( h
    12    0.989662
    , O) r* C- B) V9 H/ n$ \Name: Sale, dtype: float64) @+ R4 K2 A- U7 _, b6 z

    7 s6 G' X5 X  T" a0 u1
    + {4 x7 c4 Y! H4 ^. d3 {8 \2
    : y% j) f( ]) W0 L1 |33 x+ L; v0 _+ l6 ?
    4, E2 L+ w* g  ^/ E
    5
    5 [6 `! K4 ?, X2 j8 a! e6! ~- c, m. D5 q
    7
    " c2 V6 h2 I5 X. C2 t4 N# H8
    % ]' }& A" U1 P9+ B) Y" k4 H; w* O* b8 [8 Q2 P
    10
    " H4 P( b9 H- H& H3 b11
    7 A- y; C( x- ]6 }! |5 J12, W5 O( X1 F! s" B6 l, [
    133 F; \6 @0 Y: h: `
    14  z3 D1 {/ {; Z; t; e7 z
    15
    ( z( G0 M2 z. F1 n7 G  a16
    $ D$ n6 ^" O9 {; w1 `8 J17% c$ ^2 l7 x2 ^
    18
      N# l+ k/ ^8 T# U4 s+ n5 }# t19
    7 |$ I1 O8 `+ t* L: L+ E0 d20& Q# G" G( n0 ?& d" \( e* ^7 ?
    21+ m3 B/ b2 M) @! x0 J% B
    22
    0 g0 j% D1 e2 p6 s* @232 `/ g3 _+ x' L1 a1 V) T* Q- P$ Q
    246 S/ l2 o' S8 K$ G' M
    25
    % Y& x. s7 p3 D2 u; W: E. t26
    / ~, w  K  x7 L27
    / m) \5 [, C8 F% q28
    & M  I  R. z- S1 W" f6 v299 @. K& R# B: ]
    307 k! d: o2 V9 P7 ^+ a
    31
    ! n1 {, a, L8 g  A, c/ O# F; J" v32
    * n+ }& {6 j' p& u3 V. M33
    ) j! E: V) w: n34
    ; P5 g: A- v( ^+ ^7 {! q# 每月最后一天的生梨销量总和* o3 |- T+ J1 ~" P" u
    df[df.Date.dt.is_month_end].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()* t1 z- b) R! Y8 k- T# @# G' O

    , [  Z: J% I) n$ c' KDate
      A1 W. o0 U' \1 R" }8 L7 R7 V) o2019-01-31    847( D" H2 @% v  n* c
    2019-02-28    774: k) {# P$ k, u+ h
    2019-03-31    7619 ^# h# O2 I( M/ M3 ]# E  I+ X
    2019-04-30    6489 g: M6 u3 w* F& M$ E
    2019-05-31    616
    ) n8 N; X3 X% a- l1
    0 u' U3 r+ b+ K, t. K2
    ' I. t# S$ O# _30 y' Y  n6 a5 T: f: R: M# i
    4! ~/ L8 V2 b% ~) T& {, [& d2 S; ^! `
    5) j% k/ d0 V# o2 E% C& H. W
    6# {# n  S+ U: K
    7
    6 a: I, O5 G1 R+ y$ r7 @8, c6 D+ M% D0 _7 Q# p
    9
    , ^' q) y+ Y1 H- M7 o2 N% e# 每月最后一天工作日的生梨销量总和
    ) u# f, A$ e+ w' zls=df.Date+pd.offsets.BMonthEnd()
    2 k0 H% O  t" a; L5 Cmy_filter=pd.to_datetime(ls.unique())
    , d8 b6 ^$ O( {5 m! c9 Pdf[df.Date.isin(my_filter)].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()  t: ]; W% Y" I3 @# ^

      d# A0 R! u. p! Z3 d; _Date/ ^" j: U" s) a' L6 _0 A4 x% ]
    2019-01-31     847, t0 P( W& H% t
    2019-02-28     774/ S+ v1 k0 V9 a( m* m
    2019-03-29     510- a' ^& Q/ k# P  Z. v. g! Z" W
    2019-04-30     648
    - \$ F8 ]1 W& A; A, J! O2019-05-31     616
    . n  t, a! T/ E) @1
    9 t! p. c4 _4 _2 g( {20 m* |( ?" u! a/ H% z6 c
    3
    % @: A" ]# H7 ]6 ?0 T+ b4
    , K( A6 B$ O6 R8 Y; ^51 Y# e4 g4 y; c8 C) ~2 w0 |* @
    61 k: Y6 d, m! s+ e" Y# K2 V& w
    7+ h/ j% ^' _8 Z  j" @5 p
    8
    ; i/ K, K$ m" g' d! J' ~9
    6 }4 t8 w, k1 @+ _10) p# U$ P4 E( y
    11* ~8 l& z2 k; |6 ?% a
    # 每月最后五天的苹果销量均值
    + g* k+ N! ~3 \start, end = '2019-01-01', '2019-12-31') P( r( d3 S+ T6 M& z* k
    end = pd.date_range(start, end, freq='M')( E3 V1 ?6 u# ?1 R( s
    end=end.repeat(5) # 每月最后一天的日期列表,重复5次方便做差
    9 t+ R4 C" n6 ?8 `+ n8 _0 b$ K9 h) p0 r3 J$ W5 T5 a
    td= pd.Series(pd.timedelta_range(start='0 days', periods=5),)
    # t: ?' G: R6 n' Z1 m1 [td=pd.concat([td]*12) # 日期偏置,最后一天减去0-4天
    , h1 R# C3 n" Q' }* pend5=(end-td).reset_index(drop=True) # 每个月最后5天的列表1 f/ s9 P* U7 p; {& d

    3 k6 E9 P4 @5 j( d2 @) u( }apple5=df[df.Date.isin(end5)].query("Fruit == 'Apple'") # 每月最后五天苹果销量8 M. ]5 }: \- `+ P6 l9 x2 c; l! o" \
    apple5.groupby(apple5.Date.dt.month)['Sale'].mean().head()4 m) Y; a0 _6 I( d0 h5 r$ l
    : I" S9 T" J3 \5 {
    Date
    7 l, A% A  M' Y, ?( H* Z1     65.313725  h0 A& t: p' O3 {4 E  F- m" F* g/ T
    2     54.061538! j+ I5 J, C6 p. f; ^( F- u0 O
    3     59.3255814 F5 e; m5 a# T- I6 X' d- m, @
    4     65.795455" s+ R+ d; q- ~& v; X( I
    5     57.465116% a% Z* y* I# ]: e& m; d4 [' H1 H2 C

    5 X( L9 G! i& x) h& u1 u$ m) M1$ e. g- H8 n& q& N$ p' |8 ^
    2
      X. ~4 U; ^1 C+ s5 j4 v) \3. q! g* c6 e& F' z
    4& `7 a* `- Q' `5 L
    5/ ^3 g& j  W. S4 k$ S' \& ^
    6
    % N6 H- e% i. |0 q8 U76 Q; p( R9 P, _* m! w# e) O# `
    8, O4 a7 L+ t5 H) i, N, U
    9  w  A  c* b# U7 F$ v
    10% H0 Q9 ]5 v& O# U" a  Y4 M3 ?0 Z
    11
    6 B1 B  U7 K2 i2 m4 G12
    , V" v* E0 ~+ G13
    ) T: k1 k$ C9 G2 ~8 B4 D) m; d144 |$ @/ h0 m5 j9 n# |2 F
    15- z% A9 ?6 ]  B: ^
    16
    / ?' t5 |& U1 z( N+ a8 e17. Y1 w5 u( e. ^3 k. P+ B4 R
    18/ A, B! j+ U* J- x6 `, |
    # 参考答案:& j* D; E4 `! l% ^. [
    target_dt = df.drop_duplicates().groupby(df.Date.drop_duplicates(# b5 W0 G* c; m; J. o4 l) d/ n% K
                ).dt.month)['Date'].nlargest(5).reset_index(drop=True)4 V* K  w8 N4 p4 N! ]7 s

    & Z  O/ z$ z9 _2 ^( ~res = df.set_index('Date').loc[target_dt].reset_index(
    # k  M) W' |; l5 H            ).query("Fruit == 'Apple'")
    6 i, \9 F6 r( o0 |! b( o- o$ K" A$ F  \* |1 k- y/ M
    res = res.groupby(res.Date.dt.month)['Sale'].mean(
    , [- ]' A+ g# J& u3 F6 |/ P            ).rename_axis('Month')- N' f& y9 U0 B8 z4 Q2 o
    ; ?2 E. a" s( w/ T7 h9 Z" m

    : w- E% Q3 f7 t4 }2 [9 p' gres.head()
    7 ]# z, C. [( n- ^Out[236]: 1 J8 H9 U; q2 z% q+ u, H+ g5 w
    Month
    1 {3 w# ^  M, k, q1    65.313725
    ! q! Y" o/ E' h* |8 {, s2    54.061538  G$ l- y. q8 }6 R$ h* V
    3    59.3255814 J9 o2 j9 ?& Z
    4    65.795455
    " l2 d( ^& U7 t, `5    57.465116# k! i5 k. y/ l0 r8 ~" f$ A3 [
    Name: Sale, dtype: float64: x- [( A: T; u; w; W

    $ _/ ~8 }9 @5 y# u" R1 ?1
      ]( |- C* V$ k, `: d$ x28 e0 |, n* f+ @8 q0 k' ?1 ?
    3" [2 Y! L7 f1 l# v
    4
    . U/ C3 z  B5 @, \5% A7 r+ d& E  A% ^
    6
    3 K8 f. O- v' ^+ g, d7
    7 ~3 Z# ^3 S  ~' ]  Z- ]8  u# P* j( i- b6 m! {5 |" }
    9
    0 V9 S0 a9 h0 \10# ~% P6 i( ~" }  P, l# s8 j
    11
    5 x  R7 k7 X" Q1 Y9 [4 n124 k) ~6 l) h6 I2 I/ v
    13
      c6 x  Z% _. A- U1 w14# q( k- H. _2 i9 Q6 k9 [' H' s
    152 [* X3 c; W& Z4 t, b6 x" }" ?+ L
    16$ l0 v' v% a: A$ T/ X, l8 }8 ^, X
    17
    % q" p8 z# G. X4 J: i* _18' y" }3 J: N1 c1 }$ K
    19/ v3 e4 s' u. H$ l" o
    206 G# m8 E3 \8 D& e
    按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。
    8 [; U( k7 O0 W$ K$ [result=pd.DataFrame(df.groupby([df.Date.dt.month,df.Date.  u, s$ W4 u" ^7 P) j- W, d& y
                                            dt.dayofweek,df.Fruit])['Sale'].count()) # 分组统计 ) F) ~; n* }/ R' \% N2 ^2 B! `
                                            ) ?& z/ |8 J  f0 t, n9 d# w* w: m2 `
    result=result.unstack(1).rename_axis(index={'Date':'Month'},$ I! t( D2 y3 J. H. Q8 D% S
                     columns={'Date':'Week'})  # 两个index名字都是Date,只能转一个到列,分开来改名字.
    $ ^* i( M6 _8 p9 ]result=result.swaplevel(0,1,axis=0).droplevel(0,axis=1)* @8 G1 s4 D& j
    result.head() # 索引名有空再改吧  E: ^) C3 R: {

    ' p( a) P  B3 o          Week        0        1        2        3        4        5        62 b- V! v6 L( i+ V: s/ N) x; P8 a
    Fruit Month                                                       
    8 y  i, f$ W9 H8 X* {1 A" wApple        1        46        50        50        45        32        42        236 |, D( ~! Q8 |# j
    Banana        1        27        29        24        42        36        24        35
    & h) ?! u* D+ {3 ~Grape        1        42        75        53        63        36        57        46; B; `5 Z  [% S
    Peach        1        67        78        73        88        59        49        720 |0 n( I" j. n: c9 }
    Pear        1        39        69        51        54        48        36        40# Q  v; |7 F0 U' L2 c" N) c1 \
    1
      L! j% T2 l; S2
    . G  j4 B* H% X3: ^' @9 @4 D# C: J8 x
    49 J, O) s5 ?' g( E; e: G# @# k% M
    5
    8 A; Y- R$ B/ d# A( U) M6
    ' g! {( e8 f' y) i: w  ]5 B  G70 v3 K/ ]1 ~: r. z/ c( j. T( u2 l
    8- m1 F. ~& M1 ~7 K4 O8 ?
    9. w! T7 P9 [, W* I
    108 J: m! d$ [% w; L
    11/ B5 S& u, X7 W8 C
    12
    * h3 r! V& P$ E' v7 u13
    8 u7 t$ v" m$ p14. ]9 ?( s$ N7 M% d9 A* H# h! `
    15* s5 M( E- X! |" J' F% v
    按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。" `+ g1 B& Z. g+ d8 u7 J) C
    # 工作日苹果销量按日期排序# ^- C& H% U0 B. Z% ~
    select_bday=df[~df.Date.dt.dayofweek.isin([5,6])].query('Fruit=="Apple"').set_index('Date').sort_index()
    7 X9 U& P. W5 Z8 R. h* P" c1 bselect_bday=select_bday.groupby(select_bday.index)['Sale'].sum() # 每天的销量汇总
    3 E, k* h& U+ M* F5 s, D- Qselect_bday.head()" f. d, u/ c1 n

      T; ]: L/ P" d* P+ rDate
    3 D& q4 Z1 ^: T7 i9 A/ `2019-01-01    189  l' a# G: a7 Y1 R' Z5 t
    2019-01-02    482$ N; k4 c# {  {9 W
    2019-01-03    890
    ) |( `# U2 \7 j0 G" e: j+ c2019-01-04    550" p4 X+ S' B( G% ^6 |+ H  p
    2019-01-07    494
    ( ~! c4 O2 Z6 R1 P1 l3 A5 A( m4 B8 H5 `0 d9 O
    # 此时已经是工作日,正常滑窗。结果重设索引,对周末进行向后填充。# H2 [' t+ J8 J& k' k9 _  u
    select_bday.rolling('10D').mean().reindex(df.Date.unique()).sort_index().ffill().head()
    , ^+ Z. r2 g0 g; _) S0 {# U+ p! a- C. X7 X* J
    Date" o  z! r1 P4 y8 X
    2019-01-01    189.000000
    . W8 I$ e7 S! [2019-01-02    335.500000
    ! B' T; j9 ^: z# k( Z2019-01-03    520.333333
    ' S6 }8 |9 r. L( t( z2019-01-04    527.750000: V+ g& G. S' O4 s6 A; z
    2019-01-05    527.750000. c" y* p) q1 m  T
    4 O1 l4 ?/ X  \1 n4 S3 P, V9 K' X
    ————————————————
    % [1 `' r. F/ B4 C版权声明:本文为CSDN博主「神洛华」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。* q: M/ Q$ t$ D
    原文链接:https://blog.csdn.net/qq_56591814/article/details/126633913" h8 q- h5 {+ _+ T+ w+ v9 M

    + `/ Y" U, P# A* a: F
    7 f. F% {" q7 N' A: ?1 t
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
    您需要登录后才可以回帖 登录 | 注册地址

    qq
    收缩
    • 电话咨询

    • 04714969085
    fastpost

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

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

    蒙公网安备 15010502000194号

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

    GMT+8, 2026-6-13 08:20 , Processed in 0.643757 second(s), 51 queries .

    回顶部