QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2761|回复: 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
    9 k+ I. P" b% M) `2 l  Y- j

    9 N. `! ^+ q9 A8 [0 `! ~! n+ `7 w" U7 C, M6 l5 G  [; ~+ H3 |$ v
    文章目录0 r  J4 [1 O; M& m& T7 b! Y
    第八章 文本数据7 k9 c: g! p: e- F
    8.1 str对象
    % A( e( S2 I6 S$ p/ q$ j2 s8.1.1 str对象的设计意图
    2 g* k) \" l4 I) o8.1.3 string类型5 k5 C8 H3 y8 t0 ]2 w
    8.2 正则表达式基础
    ! Z6 L% \% w3 s/ F1 B! j8.2.1 . 一般字符的匹配" n2 A: _! h4 o) y- m: P- s/ ^
    8.2.2 元字符基础; p5 T9 i0 ^) `6 t) y% T
    8.2.3 简写字符集
    6 T& Z% r' P# L0 l! _  x8.3 文本处理的五类操作# t- U6 R* L$ z2 y2 l
    8.3.1 `str.split `拆分
    * ?/ V/ O" b$ \  A2 J# J) O& z9 i8.3.2 `str.join` 或 `str.cat `合并; C8 P  w9 w/ ^& J1 {. m5 p6 ~
    8.3.3 匹配4 W, B2 M6 U: H8 Q; `3 {% f! V4 Z: \
    8.3.5 提取
    1 ^' K$ b# A% R8 F- r9 {# f: U8.4、常用字符串函数
    , U9 H' K( N/ S/ l8.4.1 字母型函数/ Y: J5 x( T" _
    8.4.2 数值型函数4 E( F: c- a/ v0 f, h( k  o5 v
    8.4.3 统计型函数
      M+ V. g' m/ N4 N  |8.4.4 格式型函数
    3 ~6 R! j% l* x. H6 m0 e8.5 练习
    6 C. Q9 B: G. ]Ex1:房屋信息数据集% B  f1 J+ N4 A) X; A8 _  M4 [& j
    Ex2:《权力的游戏》剧本数据集1 L, Y: Z. \/ R7 Z# q% Z0 ^. h0 b
    第九章 分类数据  _6 m# I# J$ D$ ^' _8 g
    9.1 cat对象; H. X4 f  U# B; x' a
    9.1.1 cat对象的属性8 F7 y3 H1 \' V4 @& t+ v. |
    9.1.2 类别的增加、删除和修改, m, `- D5 r& P5 c! e, t
    9.2 有序分类+ ]* v. @, X8 D+ J& U1 f; ]! I
    9.2.1 序的建立2 R4 [0 l5 @! N0 G
    9.2.2 排序和比较$ J) y" j3 o# {; n9 ]) z0 _8 Y
    9.3 区间类别
    : ?( Y( s+ m3 N9.3.1 利用cut和qcut进行区间构造
    7 |$ _4 o; @4 E, P7 ?7 E# ?" M9.3.2 一般区间的构造
    2 A- \" k4 c7 K" L) |2 s" A9.3.3 区间的属性与方法% U; k+ x6 G6 Y. W& [3 S
    9.4 练习
    , d. U  x9 i7 n, v4 LEx1: 统计未出现的类别" |+ i0 ~9 X- W  b5 x) F
    Ex2: 钻石数据集7 e* m5 `, S3 S. b: \" Y* b: }
    第十章 时序数据
    . [$ T+ t% G7 y* F( Y10.1 时序中的基本对象& R( \( c$ B( H* G* \4 A
    10.2 时间戳
    9 }- Z0 m$ i' s1 m10.2.1 Timestamp的构造与属性
    : ~; m! f. g8 ^0 C, k  U) m10.2.2 Datetime序列的生成% w) x. X3 E5 G
    10.2.3 dt对象
    % z& R$ \6 a" Y4 M% s10.2.4 时间戳的切片与索引
    0 E! i9 S- G: M- ~8 e) x* D9 Y10.3 时间差& a6 C7 A, A1 _$ _9 Q0 Y0 Y
    10.3.1 Timedelta的生成0 f( K$ J# N# M( Q
    10.2.2 Timedelta的运算
    5 Q7 u% t3 o& T' D10.4 日期偏置
    ( p$ R7 `" _  k( a/ |% V; ?; @10.4.1 Offset对象
    6 B2 ^: Z+ F. l, w) g10.4.2 偏置字符串
    . _9 ^$ u: D; c- K- _( L10.5、时序中的滑窗与分组
    / i2 T3 Y) H2 q: a10.5.1 滑动窗口1 o8 V& F, s8 }7 Y8 H# }1 x
    10.5.2 重采样- O9 ?1 ~& I+ a, ]
    10.6 练习2 D9 g% r  A" A4 W) J' G# r9 ~
    Ex1:太阳辐射数据集- O( @4 N0 G+ o& J6 @
    Ex2:水果销量数据集
    7 ^6 \- I- d9 m. m: J4 N  课程资料《pandas数据处理与分析》、github地址、讲解视频、习题参考答案 、pandas官网* K* _& D( m" r: F
    传送门:4 r/ E. c. p' B3 }

    " j! B  \8 M4 L* K+ ~datawhale8月组队学习《pandas数据处理与分析》(上)(基础、索引、分组)
    : A# E* s, x& a/ [8 t3 \datawhale8月组队学习《pandas数据处理与分析》(中)(变形、连接、缺失数据)
    6 R  M; c" F/ j7 q+ e9 [第八章 文本数据: t+ T5 X6 w( I. j
    8.1 str对象
    & y# w! }9 a' a8.1.1 str对象的设计意图
    $ c8 ^/ `$ w( ^, t, P4 R" h  str 对象是定义在 Index 或 Series上的属性,专门用于处理每个元素的文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其 str 对象。在Python标准库中也有 str 模块,为了使用上的便利,在 pandas 的50个 str 对象方法中,有31个是和标准库中的 str 模块方法同名且功能一致,例如字母转为大写的操作:
    , ~5 T9 U3 |' S; z; B( V! S  |
    % L  W- C1 V$ Q4 a6 q* s/ qvar = 'abcd'
    . \( d" b& R( y) E+ U% M  Ystr.upper(var) # Python内置str模块9 W5 T2 h+ U- R( O
    Out[4]: 'ABCD'
    9 l1 O- |& v/ Y2 P- y5 u- ^0 d! Z8 {7 T7 j6 E( T! m' T
    s = pd.Series(['abcd', 'efg', 'hi'])
    2 ]) j; \  u9 @$ P- ^5 V) M5 T$ t
    9 ^$ O) c( x. f4 is.str
    ! H% y2 _. U) ?3 }  ^8 eOut[6]: <pandas.core.strings.accessor.StringMethods at 0x2b796892d60>8 [4 `1 z& x5 x; E1 _: h5 S
    2 d5 c+ C* h7 ?
    s.str.upper() # pandas中str对象上的upper方法& K9 f0 E# Q5 O0 O
    Out[7]: 3 x3 x' |/ E! O5 C
    0    ABCD" i0 {/ l5 p' @, X& e" [) I! C$ _
    1     EFG
    " c  R* h8 Z( t+ `2      HI
    5 u3 K$ e1 y8 Udtype: object
    2 J: Q' s( N% _- e1! K  s: B" |9 c" G2 H: f
    2
    + B* Y  Q$ j6 N+ b1 W& X  L3" ~* ~  ^( O8 {8 ^
    4
      L+ \6 d! K0 p2 e+ [5
    4 K9 M" I" k% {+ g1 h# F4 L: u: w6
    , A5 }. r; D5 h; l# K7
    : D" G8 ^1 [/ W2 s4 P' `5 S8
    # s: I& X( z3 {  [9& g9 R' G% m' t0 R4 ^/ F4 m
    10- b( a6 p% J: Z  S* V3 ^4 c9 d
    11
    4 O3 D( g3 O, S% z" t* f12
    0 M0 j/ K6 ]) v9 P5 ]9 S13/ R4 j' f/ ?7 v9 N7 p( s$ c3 a
    147 [* u9 Q; T! `8 K) `) ?8 R
    156 c3 N  f1 |! a( M2 t
    8.1.2 []索引器6 S" ^2 G/ |2 D$ o: z
      对于 str 对象而言,可理解为其对字符串进行了序列化的操作,例如在一般的字符串中,通过 [] 可以取出某个位置的元素,同时也能通过切片得到子串。9 ^+ L+ q- C5 k/ Q8 X" H
      pandas中过对 str 对象使用 [] 索引器,可以完成完全一致的功能,并且如果超出范围则返回缺失值:
    7 I% h2 k2 ?, C7 D2 R% Q! K! x$ t
    s.str[0]! C5 A' j1 S. o' ^* f/ z, z
    Out[10]: + W' {+ E5 S9 b
    0    a
    % j4 W0 j$ e9 @9 {1    e
    , D0 }$ I2 C, n; p7 m: o: h2    h
    ' `1 W0 j0 ?# W4 Idtype: object' @7 N. S' ~% _9 E& F$ p

    * R0 P( \' y4 y3 J* o/ Z  _5 ^% As.str[-1: 0: -2]2 u5 E* j, p( ~& @' K
    Out[11]: 9 b- E' Y& ^1 ^# |6 }: j8 h
    0    db1 h; O8 ]" V- t
    1     g
    4 }: L  P  _" J  z! ~4 r2     i
    ; W! c. I2 I/ s' c3 Fdtype: object
    : ]+ Z$ X2 C+ m5 z' \
    & L! e; ?% S* [; |9 V) b8 J3 ?, @s.str[2]" }! V0 x4 {3 ~. ^% u' a
    Out[12]: # e+ J4 o/ V1 e/ f0 m0 M
    0      c
    ; |1 U4 Q6 d6 V6 [1      g
    / m9 G# Y6 T, C; A" \5 o2    NaN5 V$ B, v3 G: {5 O
    dtype: object
    9 |7 c9 i  j+ l' Q! i( d
    # h$ a8 x/ S- d! r& o& X18 Z8 ~) k8 Q! _5 E7 c& O+ P
    2
    0 V+ _. r# t2 r9 `3/ D# `# h8 a9 e6 y) d$ q7 G! r
    4
    6 x7 h, ]7 G% n0 W# R  [5 |5
    ' }$ Y9 `+ H/ D- N* j8 F6
    % m$ s$ \3 [$ y) G' t6 o7
    / B( r7 E1 R; |8
    - Y6 p) @0 {: U9
    9 _+ q& U) j) a. n9 R100 N0 ~( d4 N. S9 Y. M* L  L& t, T
    11
    ( @9 \" y3 U1 q9 F3 V, U; Q7 S12% Z( @3 h6 ]8 J, Z4 b# O
    13
    8 f6 ?* w$ I. e$ k' z! b14
    / z+ t$ w3 w* y$ m0 l15" K9 s" I4 n4 b* A! b
    162 k# |. _. a% ?7 m! n
    17* W$ X) f; g" L
    18
    3 K8 \% |/ x3 ~: p1 ?193 Y/ J! Q" M' m# |$ _
    20" p; [) M- c' x/ |; B! _9 z
    import numpy as np0 C1 v( N8 f! g
    import pandas as pd
    ; S- M* i; @) G+ [. a' V% [' ?
    ' \/ L; }" S6 A4 S0 h3 z  Qs = pd.Series(['abcd', 'efg', 'hi'])7 h) i0 n. V" B+ B
    s.str[0]
    / L. c4 D3 c; R- Y3 t, {( O7 x10 H- d6 n5 s8 A  S7 W# `7 s) Z1 I" D
    2
      H( J, i5 @4 k+ ~; o; M3
    : V+ f( \. |3 b) F! Q4. \! ]+ J1 ^- |. T+ i# Y9 G, _
    5+ H' S! R7 p# n: u
    0    a
    ! {/ j7 @) ^4 |/ K4 d& g6 @# D1    e
    / y3 n9 i5 w3 y' B0 W% f( O2    h! ~* [" n; X0 s+ O; S+ Z# J
    dtype: object' J' _" f% B5 `3 F7 b8 I+ Y8 y
    1( B0 Q7 w# I7 W
    21 V. i" o- m$ d" U& }
    3
    - q5 g) ?$ c& u' k3 o4 J* K! W; [4
    : O0 h4 `% p9 t8.1.3 string类型
    5 F" {2 V  A5 R. e; i% ]: A1 I4 s  在上一章提到,从 pandas 的 1.0.0 版本开始,引入了 string 类型,其引入的动机在于:原来所有的字符串类型都会以 object 类型的 Series 进行存储,但 object 类型只应当存储混合类型,例如同时存储浮点、字符串、字典、列表、自定义类型等,因此字符串有必要同数值型或 category 一样,具有自己的数据存储类型,从而引入了 string 类型。3 J# i  A+ G3 f& p, b  y
      总体上说,绝大多数对于 object 和 string 类型的序列使用 str 对象方法产生的结果是一致,但是在下面提到的两点上有较大差异:
    % ~) S' x1 i7 ]3 S! G! p, y* P9 v! y9 v3 g" W
    二者对于某些对象的 str 序列化方法不同。3 q7 n1 u4 p2 ~  N- ?4 G0 y
    可迭代(Iterable)对象包括但不限于字符串、字典、列表。对于一个可迭代对象, string 类型和 object 类型对它们的序列化方式不同,序列化后str对象返回结果也可能不同。例如:
    ( u3 _* Y, J$ Ds = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a', 'b'], 0.5, 'my_string'])
    3 h  @" a* A$ H( Hs
    2 g1 H* V  d" ]3 h& r* H7 n- `/ Q1
    1 m3 g" q4 X# m2
    7 v5 H/ M3 x. S" o+ _0    {1: 'temp_1', 2: 'temp_2'}! m2 G( m* q! D1 L3 `: r
    1                        [a, b]( y( [+ n: @" I) P
    2                           0.5
    * v  ~% s7 \' ?- L/ A* Z" i3                     my_string$ a+ ]+ o. d; [- i4 {
    dtype: object" V' P9 i2 P% [, y+ I
    12 R4 `8 B8 z+ Y0 A5 q* M5 l4 ^
    2
    # y4 s1 k# N/ `/ f3
    * c6 j& y' X& ^# t# _7 a' n4
    3 J* U, o1 m( i# k& I5
    ( @$ W& G; g0 ds.str[1] # 对每个元素取[1]的操作
    " l/ v$ D$ Z# z' k" N" E1
    6 W6 Z/ M7 l7 ~7 _; q0    temp_1) e+ I5 V% J) K5 h
    1         b7 d: w" r* ]1 l( n8 {
    2       NaN
    ; ~& o! K; X8 t* h! B: W3         y5 |: ~# p& C3 R7 B8 d' b( ]' K: J
    dtype: object
    0 R& _7 y, x( M1( z0 P7 P6 q9 {, I
    2% ~7 K# ?4 `! c+ \
    3
    . [( M; m$ j* k, i0 B4' \0 F. D, M) _. X* Y6 g& H+ F
    5- E8 P2 S) y% Z3 c$ y
    s.astype('string').str[1]
    ) D+ Z+ P; T8 H2 p# F1
    ! d, E" `( F. ^, K0    19 N6 H) N4 p: z8 |) g8 G" O
    1    '
    # A# v8 q/ O: u$ u! j6 Z7 r2    .6 |4 ?/ [% e. _; V6 j' E
    3    y
    ) _6 {9 `8 {+ vdtype: string
    6 @8 E2 \0 q! c: ], C1
    2 W9 Q; b" E# @; z2( [& Y1 n% e- c, {3 b( ~
    3* v7 O$ ?$ k! ~$ X/ s3 v' X% S
    4
    : ~% z9 y! o) Z1 e54 N" y+ S' h: v* _& g# \+ b
    除了最后一个字符串元素,前三个元素返回的值都不同,其原因在于:
    % y- O' L: e+ O* i; M3 h6 }
    ! a. Q* v) m- J" i) u4 Y  R' _' _, k当序列类型为 object 时,是对于每一个元素进行 [] 索引,因此对于字典而言,返回temp_1字符串,对于列表则返回第二个值,而第三个为不可迭代对象,返回缺失值,第四个是对字符串进行 [] 索引。
    3 q8 K- W! @+ s% R% {/ o+ Sstring 类型的 str 对象先把整个元素转为字面意义的字符串,例如对于列表而言,第一个元素即 “{”,而对于最后一个字符串元素而言,恰好转化前后的表示方法一致,因此结果和 object 类型一致。
      i1 q5 v* l8 c5 Fstring 类型是 Nullable 类型,但 object 不是
    0 a9 o4 w, {9 i5 E" p$ ]4 `- {; P, V  这意味着 string 类型的序列,如果调用的 str 方法返回值为整数 Series 和布尔 Series 时,其分别对应的 dtype 是 Int 和 boolean 的 Nullable 类型,而 object 类型则会分别返回 int/float 和 bool/object ,不过这取决于缺失值的存在与否。+ s( D* U  t- B$ T0 }
      同时,字符串的比较操作,也具有相似的特性, string 返回 Nullable 类型,但 object 不会。: K: |; A' S8 M8 ]% M8 h  m+ m+ _
    s = pd.Series(['a'])
    7 _' u0 I- h' [& V  C$ n! q4 T; m
    s.str.len()
    / W. T4 X) ]/ W7 l; J9 m0 D8 wOut[17]:
    ) t& t. z# l+ I! b0    1
    - E) C, u2 q1 h( v- Sdtype: int642 q$ w  S5 l6 ~7 t# z

    ) Y2 q0 _' B/ x* J: xs.astype('string').str.len()& y$ K. m( l3 G4 k" W
    Out[18]: - e0 ?: |; T, S, Y
    0    1
    - X5 \; ?1 O& h% D+ @% x" Ddtype: Int64$ A' {2 U2 z% p) a

    - I# q$ F+ E1 D4 @+ j% Ds == 'a'
    2 w$ d4 D+ u, N- S% P! _  T! kOut[19]:
    , F2 k! ^6 ]6 S3 Y7 L( N1 H+ O" E- Y0    True. ]! M, r; X" A3 k3 O4 v
    dtype: bool
    - y$ }6 H5 ?; t1 D
    . U: X6 J  D3 R2 U; us.astype('string') == 'a'7 F# f' s8 S  K  |  [
    Out[20]: ) b0 @6 v/ D6 V7 Y) h) w
    0    True" @. m3 b% n  l. c
    dtype: boolean
    ) u. K; l5 K- l+ N. Z0 D' S7 i7 i& f* L
    s = pd.Series(['a', np.nan]) # 带有缺失值* {) Q  a, r1 T' q

    9 A/ T' C* ?  @3 }3 R+ cs.str.len()+ l2 F5 u; l- Z' l7 X
    Out[22]:
    * {6 S1 ^" ^. O, l: B+ N5 g0    1.0: V! v2 b1 O$ S
    1    NaN
    " Q# y; e- R5 ^% q2 h/ n# Ddtype: float64# V6 b5 w0 j: o7 v4 {) q# Q

    6 `' s; F% m/ l1 c1 ~( hs.astype('string').str.len()
    1 `5 K/ Q, A7 i6 M$ h% q6 GOut[23]: # E4 |. N4 R% j# L9 I4 ^0 O3 y
    0       1) S! D( X% s+ |" }- f
    1    <NA>$ I0 Q+ R, }' i% H
    dtype: Int64- k* B9 Y7 k5 ]7 E
    7 m- u! _% R8 X: h# J! ], q
    s == 'a'. w4 w3 J) X( C; f
    Out[24]:
    4 F- d. H5 [6 \6 F9 c0     True5 I5 ^$ z, v) s# x
    1    False; O. t5 `/ G: \8 M7 R' u( D9 ?
    dtype: bool
    6 M. N9 D8 D& ?. O: o3 H* {1 E2 _6 X- }% ^6 X$ i3 {/ E: a  b
    s.astype('string') == 'a'
    + J5 f& z1 N3 g0 Y# kOut[25]: $ |8 ]( p2 d5 t
    0    True
    ' A6 k4 N3 d* x+ C: C1    <NA>
    ' h1 S: D% i1 ?" i% v' s8 r9 \dtype: boolean/ w9 e' m/ q# v! j  r: \

    8 M9 [9 F( Y- {" j* ?1 ~' S  ^1
    7 f+ P6 M1 D# F7 I2: l7 Z% l$ M7 V0 e4 e
    37 H: |3 O: {0 F
    4: ~! f! b( a6 a& V$ @, b4 r5 O
    5
    % V( f0 g9 b; A& F3 \, \. G4 v6
    : Q2 D$ W9 p* E  J( c2 Y* n, i7( p3 j% z- e1 F  Q3 J" T' g( N
    8# ^( Q' C8 S! Y& k- {8 w8 s* `* c
    9
      q7 ]% @9 D- V# F( ]10
    : j6 z7 [9 B7 y# }; u! O/ P9 ~8 J11" O& R* X8 r! Q4 G5 W5 {
    12
    % `( K3 L) }% l13
      S8 ~$ F8 z% v" W) q14
    ' ^* z* @' Q  l. D155 L( w' ]8 P0 [; _& {3 b
    16
    # c& @! s6 N& N$ r7 `% A8 _17
    2 K2 W, E# N; Q- S' ^8 o2 ]18% g$ v, e( O$ j7 i& o2 G6 {
    19
    , [$ e8 W& o* d& q203 o3 n% C1 I% b+ M' F3 g3 h7 K
    21* [6 W, T6 |( Z# F- Q  i  U6 B
    222 ]  T' }8 {( ]. s! F( X3 t
    23
    ( w0 x4 C% o* m3 N6 |24
    7 c* V: ]# A2 Z3 x25
    & \. k6 [2 p* A5 ~: i. v0 ]; ]26# I" ]) j3 L% t5 k; A
    27: o! g% t  \* X# d+ U
    28
    0 q; z- Q! c; w29
    ' \6 v; p7 C! {8 }- N303 n# }, h; C; P2 d, w0 _$ M
    317 e: C; A4 R" ]$ E4 k/ R% u
    32
    $ k+ @/ f- j0 [  k  u* E33
    ! K& l5 i. i7 b( ~' K4 \34
    6 ]- k  O6 `2 ^35
      o  M. C' ^( n* S* E5 H+ A36$ g3 F. e' ?  r" i% B
    37
    7 U# E: J4 U; T" \38
    . L% |4 L. H& ]% g2 x39
    9 G( W/ Z0 z* o# a" o6 j' Y" @1 C3 r402 x% v1 z( q8 m* ?
    41
    . s  A8 s9 y" k( U6 X# O423 E: t2 B1 k6 d& W2 k; q* `( A
    43: i$ i0 n! `% Y5 n) p
    44- W6 t: f/ t! ^/ n% i5 C
    45
    ' \  U# B( j: r# d# J465 q8 O+ @( c2 y) G( d& l) |( ~
    477 I) J8 K: g; A' }/ T! H2 V
      对于全体元素为数值类型的序列,即使其类型为 object 或者 category 也不允许直接使用 str 属性。如果需要把数字当成 string 类型处理,可以使用 astype 强制转换为 string 类型的 Series :1 Y; [6 u2 b  h8 B& e  O# Z

    ) a6 o1 W/ t1 V$ T7 M. s" As = pd.Series([12, 345, 6789])
    / @0 Y- G" y7 k) G9 o5 t/ i, E8 G9 u2 T
    s.astype('string').str[1]/ C& q1 q! G. e6 N, Y! _( q# z
    Out[27]:
    ; l; {. ]8 Z/ W+ b0    2
    9 V/ V4 F/ T. G. c3 `1    4; L5 B  K' Q% Q9 e* d0 B
    2    7- D+ i% S9 Z: t) Y$ Q+ _$ H. [$ s
    dtype: string( w( q" r8 @$ F- ^7 a0 c
    1
    ' r. w. D8 Q! d" c7 ?2& ?( j% ?! S. @7 _* i
    3% W" W: u- P2 h5 r
    4/ w+ a/ b! K8 m6 Q
    5: V+ U' z. s6 @4 S' r9 }4 b" `  j
    6/ G1 L; {! H# I1 d2 d0 e  r) q
    79 ]9 q, V9 U1 F/ x* L5 h- k' k
    8
    % o7 _: @$ G8 d2 V8.2 正则表达式基础' f* k: @8 _/ h$ L; \  u- u
    这一节的两个表格来自于 learn-regex-zh 这个关于正则表达式项目,其使用 MIT 开源许可协议。这里只是介绍正则表达式的基本用法,需要系统学习的读者可参考《Python3 正则表达式》,或者《 正则表达式必知必会 》这本书/ h7 E$ E& \  X& ]

    ! U$ j7 Q- ]3 T" U8 {8.2.1 . 一般字符的匹配
    / X% K# [  x) Z7 v3 i正则表达式是一种按照某种正则模式,从左到右匹配字符串中内容的一种工具。对于一般的字符而言,它可以找到其所在的位置,这里为了演示便利,使用了 python 中 re 模块的 findall 函数来匹配所有出现过但不重叠的模式,第一个参数是正则表达式,第二个参数是待匹配的字符串。例如,在下面的字符串中找出 apple :: Q4 m6 r2 }9 [$ R6 Z: ?) G3 R
    ( T% U. t: x# G  a
    import re
    ) D5 b( G$ T  Y/ I
    . J( P% R! [  q$ `% u5 bre.findall(r'Apple', 'Apple! This Is an Apple!') # 字符串从左到右依次匹配, v- W7 {6 o% D7 r( W5 @: A
    Out[29]: ['Apple', 'Apple']" G1 b% E/ u( c' z2 I2 v& F( d( a
    1
    : w$ S7 o$ Q4 T# f$ G2
    + \- i3 V. a( @; Y# E7 D( b' w3
    . F* s% t* O6 g4 B5 e9 U, w4( a# e9 R5 Y9 O; n' N* S
    8.2.2 元字符基础
    ; E/ a2 _8 c2 k$ N4 ?5 [- }: c/ `元字符        描述+ a/ g9 T5 x9 k
    .        匹配除换行符以外的任意字符0 z; E$ M7 H$ |# v
    [ ]        字符类,匹配方括号中包含的任意字符; e7 \# `5 [, V  f6 f3 d) d% P
    [^ ]        否定字符类,匹配方括号中不包含的任意字符( p5 g; s' ], v. C9 g2 p: ^
    *        匹配前面的子表达式零次或多次$ D# B: y7 Q( l  `% \
    +        匹配前面的子表达式一次或多次。比如r’d+'就是匹配数字串,r’d’就是匹配单个数字
    3 W' v; U; Z% l% y?        匹配前面的子表达式零次或一次,非贪婪方式
    - P. Q+ f# G9 e' |: h{n,m}        花括号,匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式& q$ O$ f9 T7 @$ F  E8 N1 x* G( h
    (xyz)        字符组,按照确切的顺序匹配字符xyz1 ?3 j' J& \$ K4 n/ U4 x
    |        分支结构,匹配符号之前的字符或后面的字符
    4 U+ B" ], v8 e4 l\        转义符,它可以还原元字符原来的含义
    # |- R. u; l+ I1 s, {( k^        匹配行的开始
    1 W9 e2 E, o& d5 f7 v$        匹配行的结束
    # v3 o  I+ Z0 h$ M5 Aimport re
    # O6 P0 f( ~# C- D1 r- R8 Ire.findall(r'.', 'abc')
    * @' Q$ C: I/ x4 ~0 D0 }Out[30]: ['a', 'b', 'c']( }/ t, b* G/ _* W3 h
    * ^3 ?/ k3 Z! J7 I: D: v1 Y8 h
    re.findall(r'[ac]', 'abc') # []中有的子串都匹配4 K+ k7 g$ G0 k+ R1 W
    Out[31]: ['a', 'c']" w- C- j) @/ L6 a' W' m4 d9 |
    & E7 P4 n$ V8 s, e& P9 b
    re.findall(r'[^ac]', 'abc')
    7 b) o& M. h, i4 D. }Out[32]: ['b']$ l3 w! A" I) F! B  U

    - s6 o8 \. k( J+ U1 ~2 i, r  Hre.findall(r'[ab]{2}', 'aaaabbbb') # {n}指匹配n次2 Q- P9 n3 d& I" t
    Out[33]: ['aa', 'aa', 'bb', 'bb']
    1 L5 m5 b; N( t/ a6 L
    , O: E) L/ _, ~9 i  \$ `re.findall(r'aaa|bbc|ca', 'aacabbcbbc') # 匹配前面的或者后面的字符串
    & s" Z: e" y: v- QOut[34]: ['ca', 'bbc', 'bbc']
    # A! f7 b4 G" k( ?- l* Y+ O# ^2 T+ E1 D/ E
    # 上面的元字符都有特殊含义,要匹配其本来的意思就得用\进行转义。3 V9 H; q( Z- R. F" l0 F! }
    """
    4 k0 ~4 ~- p$ \1 R1. ?匹配的是前一个字符,即被转义的\,所以|前面的内容就是匹配a\或者a,但是结果里面没有a\,相当于只能匹配a。0 P9 j0 g4 u4 t
    2. |右边是a\*,转义之后匹配a*,对于竖线而言左边优先级高于右边* w9 u3 @; P! u* |; |4 W& i
    3. 然后看目标字符串aa?a*a,第一个a匹配左边,第二个a匹配左边,第三个a虽然后面有*,
    ! @3 u2 n' I+ w/ A2 h但是左边优先级高, 还是匹配左边,剩下一个a还是左边,所以结果是四个a
    . u; a% E* f. r0 g& c& A5 ~" i1 M"""
    & d/ L- b* f  p8 h. ?, B- R+ H/ u2 f
    re.findall(r'a\\?|a\*', 'aa?a*a')   # 第二次先匹配到a,就不会匹配a?。a*同理。
    : m8 D5 [$ V5 XOut[35]: ['a', 'a', 'a', 'a']
    0 u% k/ D& o9 w* a. o4 Q, v  R
    5 E! }+ B" x5 m' ~# 这里匹配不到是因为目标串'aa\a*a'中,\a是python的转义字符(\a\b\t\n等),所以匹配不到。
    3 a! z# L' d6 {) n6 l# 如果是'aa\s*a'之内非python的转义字符,或者'aa\\s*a',或者r'aa\\s*a'就可以匹配到\字符。
    1 _8 g" r: {" a) _% |. ]8 K" Cre.findall(r'\\', 'aa\a*a') + ?" P$ O% W+ I. A0 r" X) }& K
    []( O; K3 N6 h& ~/ X+ L
    / M- A7 {( M- t2 q/ i
    re.findall(r'a?.', 'abaacadaae')) ~! z4 o8 ]) D* f( L5 J: y0 z
    Out[36]: ['ab', 'aa', 'c', 'ad', 'aa', 'e']8 r% v: K" T- a* b3 [

    ) u( g. \, V& E, }5 F6 Wre.findall(r'(\w+)=(\d+)', 'set width=20 and height=10') # 多个匹配模式,返回元组列表
    3 x+ u% T+ t+ h& C/ z1 t5 f+ ~[('width', '20'), ('height', '10')]/ l0 h& L/ Z; T( V$ d
    7 O: R( }5 T) r* a9 t( q  Q8 f# l
    14 r1 q, R: |& ?, a9 C
    2
    : u3 k4 }! ~% o; V' q3 b# o3
    # w$ l+ T( p4 |- q, `# M4
    0 m/ w5 x1 u: ]* Z2 R- |5
    : A8 M% d" y$ H! N6 {6
    ! j' n+ _; C, X& g1 H7
    ' b- P0 T9 ]5 W- w( i8
    " G% o( ^% s  c4 a, G* x5 c- ~9
    + e' [( A/ i  x/ {) r, ]2 i& R' Q10
    0 s0 q$ a# e1 X11
    ) L9 g+ n' x9 U; e7 g6 L12
    # P% O( _* `" O6 G13- M6 y/ d) p; n1 \9 }% E* q& x) h4 d
    14
    1 R; G: O# _( x# m5 d3 b15
    ! i/ [' I: H( Q* Y/ z) H% M167 K; ]& p( Y+ g7 H( r( B( r$ d1 [4 f; @
    17
    * y4 A" h: [* _: C( H6 l18
    ; t- q' b5 `; [! V/ q7 W4 _19
    1 J( S) r( E* _200 ?+ N! c9 w' q6 z) k1 f
    217 K( a: K$ N0 K6 Y5 b
    22
    ! M4 [! V; r! G8 D- q23
    . _, v$ P0 @5 h7 O% g. V' X, ?24
    / B- V7 W# u, F: K8 m- y3 O25/ l4 d3 e1 [& H$ u
    26" m, i3 W9 T  u! E; B
    27# V. Y3 B8 Z& v9 w* o
    28
    ! C; _( C; [. I  M29
    7 K$ n" u: P- g# w" F; Z- ~30) l: d* p/ C8 w9 Q6 D  s' N
    31
    . b  C. S" x* S( d! X$ |  N32
    ) t# F' \# p  W! u( o  T: U33) }  y7 d  H, {0 _& I
    34' S: u- C. H/ v+ h! ?  A
    35+ v0 m! j* {1 A, d* ~& a
    36
    0 L' }- F0 h: [9 L37, p6 [! S2 r8 |
    8.2.3 简写字符集
    ( }3 G5 K) t% B/ U/ s2 H则表达式中还有一类简写字符集,其等价于一组字符的集合:
    3 W4 E6 ^7 A; y3 p. Y
    / z% M& |$ m. W简写        描述* Z' c, U( Z. i# i9 L$ p( A
    \w        匹配所有字母、数字、下划线: [a-zA-Z0-9_]
    5 c& o2 U9 W+ ]" J+ r& o) o1 X5 q7 Z\W        匹配非字母和数字的字符: [^\w]0 D% W/ P! N  o) O) g: N9 w# r
    \d        匹配数字: [0-9]
    / @% F) I3 r3 x/ G5 A\D        匹配非数字: [^\d]
    / E1 e/ r& h" r; V2 Y. y) p0 i\s        匹配空格符: [\t\n\f\r\p{Z}]4 `* T/ z4 P! F* j$ n
    \S        匹配非空格符: [^\s]
    9 I- D+ w% V% d; x7 W8 `6 `7 J\B        匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。
    % i0 V9 X; w+ f- J& y: V& ^8 }0 [. ?re.findall(r'.s', 'Apple! This Is an Apple!')0 O3 P" W5 I# d% G$ T
    Out[37]: ['is', 'Is']
    , ^# x& h7 E* c5 w5 r
    8 o  q" L3 z0 c+ S2 lre.findall(r'\w{2}', '09 8? 7w c_ 9q p@') # 匹配任意数字字母下划线的组合,但必须是两次
    9 k* i0 `+ E: Y$ p5 ?7 rOut[38]: ['09', '7w', 'c_', '9q']7 J7 h! L0 ~$ _% o( G8 |# h/ h

    0 k* Y- K, q. A& tre.findall(r'\w\W\B', '09 8? 7w c_ 9q p@') # 匹配的是两个字符串,前一个是任意数字字母下划线(\W),后一个不是(\W)
    2 L( C1 s3 x6 y* rOut[39]: ['8?', 'p@'], H/ N4 X4 ^. C5 G; V) n

    5 f3 x3 U, ?& j" Xre.findall(r'.\s.', 'Constant dropping wears the stone.')
    5 T- T- _4 D" k$ w; lOut[40]: ['t d', 'g w', 's t', 'e s']. p# q# r" v2 [' `/ q
    ! W# s9 ^1 z# z5 b# A
    re.findall(r'上海市(.{2,3}区)(.{2,3}路)(\d+号)',
    6 V. A2 T7 e, _           '上海市黄浦区方浜中路249号 上海市宝山区密山路5号')% _8 P  @3 L, x) G

    $ ]7 {; d; b& ]Out[41]: [('黄浦区', '方浜中路', '249号'), ('宝山区', '密山路', '5号')]
    0 s0 F& W$ }; M8 j9 t; h3 D1 w; b; c  W* `) p
    1
    1 t/ b( r$ X; g  ^% b) _2
    $ I5 s! x$ D2 P3 [, o6 e3
    ) o7 R3 _& D: J" q/ s8 {4
    & H0 p+ J) X8 X+ d5# H( c7 }" g# o
    6' i6 i/ j8 |; P1 _4 _
    7
    ! R5 X! q( y' ~# P, H/ z" h# c* k3 V85 G9 @2 R2 u4 v  _$ q% X: ^
    9/ E& d; A) {/ N$ f4 f
    10
      u0 }: C- F6 M8 W& ~* W11; |2 P& n  i- i7 _
    12/ o- W: V( e# b9 G
    13
    - }; G5 f; c/ t2 l14
    3 L9 R; \% k$ w0 A/ y: q! i15
    # T  M1 I" i5 H3 N* x) Q/ H162 H( U+ b! ?/ a+ R
    8.3 文本处理的五类操作; u/ {0 E2 x- s# \, k) t" d
    8.3.1 str.split 拆分3 V) ?( C  W; j9 u6 J" ?
      str.split 能够把字符串的列进行拆分,其中第一个参数为正则表达式,可选参数包括从左到右的最大拆分次数 n ,是否展开为多个列 expand 。
    5 _1 `; V/ L* T/ y' X* `( i# i+ U9 R* Q& h" S6 }& r4 f4 o% q
    s = pd.Series(['上海市黄浦区方浜中路249号',# }6 D/ u# d5 X0 W" W7 Q8 A2 s
                '上海市宝山区密山路5号'])
    + f, ]8 G0 c2 H5 b1 U9 d7 h3 d/ ^  @6 E: {+ ~- F9 d" [
    " A+ y4 M6 x, L  n& G: C0 D; L  H
    s.str.split('[市区路]') # 每条结果为一行,相当于Series: p3 w- k5 O- [/ X
    Out[43]:
    9 p# N( Y& m3 k0    [上海, 黄浦, 方浜中, 249号]6 ?9 j  E* K6 k
    1       [上海, 宝山, 密山, 5号]
    6 y- o" v4 n6 J4 Wdtype: object& A, R7 p6 u3 B$ M7 q

    - _3 G5 r. g! hs.str.split('[市区路]', n=2, expand=True) # 结果分成多个列展示,结果相当于DataFrame+ G/ l/ l2 h+ f5 v- l. [5 i
    Out[44]: + P+ ^" ~. E, y
        0   1         2
    ; L; y( \6 W8 _6 m3 v0  上海  黄浦  方浜中路249号* Q) O: a. I+ @) ]4 A  S" ~
    1  上海  宝山     密山路5号
    2 G4 D2 Q: B& S5 C4 A/ ^14 \& [) b; u( Q0 W# q5 i! E% |
    2. |$ _  M2 @$ w' }- ^+ m
    3" @3 S. R( s9 q0 o# x' T. ?  L
    4
    8 A- v: G9 ^! b( U9 J! z5
    & s! f  l+ q; a5 Q, j2 w6
    / r9 ~& w/ Z7 N" s8 l6 ?/ M: P7# a, H: s1 ~" h+ X
    8
    7 q  d! j6 v7 h8 d& t# V1 z9- D* D. d2 X+ S9 |
    102 j- e" V. u7 c! J' Y4 }
    116 L4 p( f2 A8 R( }% C
    12
    0 w, D* ?1 {% E- l/ K; }4 q7 v1 P13
    + E- g( W  P/ p# X0 @  w8 d1 W14
    0 F, c% U" [2 U# k" l15* i/ r3 w) A0 w
      类似的函数是 str.rsplit ,其区别在于使用 n 参数的时候是从右到左限制最大拆分次数。但是当前版本下 rsplit 因为 bug 而无法使用正则表达式进行分割:
    3 r  C) {' O9 Y; }" D' v$ o; |4 I
    * v0 r3 W  U5 s9 J8 k% Z# Ls.str.rsplit('[市区路]', n=2, expand=True)
    / C! ^' H4 R+ u2 `) v% G5 }Out[45]:
    $ j* o7 d) e& K0 C/ D, K                0( ~  T# t) r; i. X$ |- h7 `
    0  上海市黄浦区方浜中路249号
    % p0 K9 B. V9 I; t1     上海市宝山区密山路5号0 R: F' ]  ?4 s6 b$ @8 V
    10 h& j; H/ I5 ^" t2 m+ ]
    2
    , C) k9 i1 X0 `$ C- w7 O# G3
    " r8 @# l; s0 @  H8 p6 t4
    * \1 z! O" h  c2 o4 v5: p; r! X5 E! v4 D& O
    8.3.2 str.join 或 str.cat 合并! t8 U3 o$ t2 ^6 t" c; g/ T: b3 `
    str.join 表示用某个连接符把 Series 中的字符串列表连接起来,如果列表中出现了非字符串元素则返回缺失值。
    , q) q7 O0 n6 y* ~7 j. ystr.cat 用于合并两个序列,主要参数为:
    7 P. k  K: r+ M" T* s$ q( @sep:连接符、
    9 T: ~8 Y3 n5 ~- @4 _join:连接形式默认为以索引为键的左连接
    ! j" g/ A3 w, ]1 B& ona_rep:缺失值替代符号/ i+ G# A) V6 y2 m
    s = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']])/ T# f. Q; c7 A1 \
    s.str.join('-')
    $ D0 K3 D  I- gOut[47]: / {% |+ q/ ^) b0 m. l
    0    a-b9 V6 @8 h2 ^. t/ R
    1    NaN' ]; L0 Y: R! _$ W6 g
    2    NaN/ b6 S$ Y) u' c! v; Z& A
    dtype: object
    * k4 s' N8 `5 m3 y; c6 v  c1! z* I3 l! ^2 f* d1 {
    2' K: y) _, f/ T$ O; P/ c" x
    38 Y5 Z. C9 T$ h( q  I& U
    4; L0 E/ ]3 f" {
    5* Y8 I7 ?, }& o/ h; H' J2 n
    6% {- x$ g- B& M  E* X
    7; N8 i- ]4 G3 h0 s: a. W2 x- {
    s1 = pd.Series(['a','b'])7 y) N# W& Q2 |8 ]. x/ \, _
    s2 = pd.Series(['cat','dog'])' w) d; e2 ?4 J% Z, E3 @9 g# I
    s1.str.cat(s2,sep='-')
    3 n* b' M9 f' x' _0 o# A  k/ ZOut[50]:
    / n2 z5 j" N0 t8 `0 g0 G, P0    a-cat1 h1 O7 }  M$ c: q0 B; O
    1    b-dog8 U0 j" g. @- B  T, ~1 Q
    dtype: object" a. z: ^4 R6 O/ k' e6 R

    & s$ M7 V" n/ }8 F* |$ h2 n* P9 ]s2.index = [1, 2]
    1 g' h6 u7 z/ ?2 b4 |- a  Os1.str.cat(s2, sep='-', na_rep='?', join='outer'). r" V' M8 ~: A$ x, u
    Out[52]: 1 X. p; h( B; ^  z; Y
    0      a-?
    3 I6 X+ u* m1 w1    b-cat
    3 p0 @9 ?7 _# H; ^* t2    ?-dog
    - l. d7 N$ k" jdtype: object
    ; S! |* {8 u; x3 K/ Z1* y: h: d; ~6 E! _# c- m
    2
    5 l" p( M8 D* N2 L1 D  R39 A( G& i# q3 Q# Y9 W" Q+ |
    4* O7 I0 \& s3 }6 {' l# k" `
    5( e, F0 L' U* u( S6 G
    6) Q* ^* f+ _/ Z" T! I1 h2 `. j
    7
    5 x: W6 a0 _, j; J8
    0 g* E8 z9 s6 t( \8 ~7 D1 J& i  z9
    ' f: v: N# z; a' s; _) ~' n5 B10! l! e! R7 Q  h, M
    11
      }  C: C( `/ U, _) a: h12' W6 w5 `; I7 I4 C" P' @/ J
    13
    - R7 m$ Y9 m+ {. |) G14, |7 z( l# T* n, n/ p! I8 W
    15/ k- }! f" M, f2 x" L
    8.3.3 匹配% ?! S; ~% e; b6 ]" D  N, `
    str.contains返回了每个字符串是否包含正则模式的布尔序列:! K! }: ?* p0 I1 r7 U: J4 d
    s = pd.Series(['my cat', 'he is fat', 'railway station'])/ |7 Q! G- ]8 m8 t7 N( @7 G% i
    s.str.contains('\s\wat')' V% a7 O9 a  n" ?; e
    - d/ X+ V7 ]( b8 G6 n+ f
    0     True
      I. A. h6 B. y0 t  d! k9 \% u1     True! D; f7 f7 e6 H9 L* J& p, T
    2    False- l; V) i$ ]* P- [
    dtype: bool
    : H& w0 [* b! A9 o6 y. W1 {4 g! Y1
    : Z7 G2 M0 c, {. L8 ^2+ u, l8 n1 r1 r) C' m
    3( c* ~& w2 ~" V& K
    4
    + Q3 {/ y9 U5 z1 ]5 e, \0 |5
    & W  Z4 \/ K3 P7 t& Y: R6
    # P' B) g2 y8 a' I. `+ ?7* {/ Q- n2 g- }& I" k4 P1 C
    str.startswith和str.endswith返回了每个字符串以给定模式为开始和结束的布尔序列,它们都不支持正则表达式:
    6 d/ y: z: M. K& As.str.startswith('my')
    5 f8 h7 _# |: B6 z% S7 j( r' B; P. }! d' E& R0 L
    0     True0 B; _! A( B$ E" K- L$ N
    1    False# j3 {2 p6 |, e
    2    False, H+ o- {7 r3 d, _- ?1 ~5 p, E
    dtype: bool9 c7 C' c9 A5 q* ~6 r
    12 X1 s4 ^9 B  ]3 S  g7 K
    2# e3 Y$ O& `4 e0 z) a
    37 G& `, v  V3 a+ B5 [9 y
    4& r+ S1 e% n/ b+ Q- L
    5* k/ A  B) O2 X) Y
    6. z" w& w4 Y  ?' a9 v1 F; i! _2 a  m
    s.str.endswith('t')
    8 T4 [% Z0 j- F3 d! V+ t$ J1 q% q# [: s7 }1 [) y! ]
    0     True
    8 ?' @; W# o; C* n) j- S% J1     True% Q' d: y! G1 o4 B! N% }
    2    False$ T4 S/ v) l; }; q% |- n
    dtype: bool
    4 X0 k. ^$ k# W( K6 [, Y1
    ! n. Y0 u5 ~. u2
    0 \# C! S9 r7 f% E; |' s35 t4 ~: H. H) a3 w1 e3 E, G/ _
    45 m# g* j. o" \# I% F8 _
    5
    4 v5 h' k7 n, f! i6. r0 s9 s1 M! }! w1 L+ D0 H
    str.match可以用正则表达式来检测开始或结束字符串的模式,其返回了每个字符串起始处是否符合给定正则模式的布尔序列。当然,这些也能通过在str.contains的正则中使用^和$来实现。(貌似没有python里的search方法): r. g( T$ M" \! |
    s.str.match('m|h')! B: X2 f- B! a- G! o! X
    s.str.contains('^[m|h]') # 二者等价$ N4 y6 l& F. w0 k9 A4 o. [' J

    ( h% c* q2 }4 J# m3 F0 U5 t0     True
    2 S( W/ u* v# X& [1     True
    3 [/ ?: H* M) L0 s2    False% v4 [% W8 W! b; M( j
    dtype: bool
    3 Q( c; K1 @  A7 f5 E1
    , E8 @' v& Y6 h0 ~1 M2% a* V: o' W: y# T
    3
    , J3 Z6 e4 H8 f4, k! e6 m3 y3 w2 e! \& w# z  P
    53 ?  j8 l& ?3 ~8 P! P0 J8 H
    6
    " L7 Y2 P9 o$ U: b+ u79 u/ B/ g5 s- V" C
    s.str[::-1].str.match('ta[f|g]|n') # 反转后匹配: |0 u' B3 V' \
    s.str.contains('[f|g]at|n$')       # 二者等价$ _% u2 }: r4 q( H0 Z

    0 L) l: F7 d' z5 f0    False
    " b. R" }) u: Y8 ~1     True
    $ c4 x3 k  }3 z  R5 X% w2     True* K( T1 |! r$ r- w7 ?
    dtype: bool0 m- H& U9 J- N. `
    1
    $ ~0 \7 A# x! Q) S9 l; C6 M24 h0 R) O& m3 t/ s& p: `9 p
    3, _9 t+ G1 a2 S  T
    4
    ' t2 Y6 `; R6 ]- e5% o, F! N: ]! \( V; Y6 z
    6
    0 V* B1 u$ e  y* C$ I7 t7
    - h0 I! `7 T3 Z2 B  fstr.find与str.rfind返回索引的匹配函数,其分别返回从左到右和从右到左第一次匹配的位置的索引,未找到则返回-1。需要注意的是这两个函数不支持正则匹配,只能用于字符子串的匹配:
    4 d( R* V) H; k, H: l- ~/ Ys = pd.Series(['This is an apple. That is not an apple.'])) W/ f4 E% W4 e* y

    # a6 ]6 |! M$ R- I, Q$ w7 Ms.str.find('apple'): s* K5 e+ f( _; \% g8 K: q* r
    Out[62]:
    3 g& ^% _) Y7 ]: s! J8 m1 g- t0    11
    0 w; P' o. @# k) G/ x0 [0 ^( g5 l$ Vdtype: int64
      I" r# D& i) C
      f6 i2 A( \# K" F9 t" E7 Ms.str.rfind('apple')" i" h5 O5 C# J2 O$ {2 _
    Out[63]: & {$ q2 l/ S0 c7 R9 j' P
    0    33' X1 N3 Y* n: j* c6 y1 T+ G
    dtype: int64
    . J! d4 p1 A) |+ B5 ]1
    6 Y0 r; ~" q9 @$ b26 x7 g  L1 [! z% Q' I0 r
    3
    . X6 {1 @6 n# S1 e( m$ G! \0 g$ v4  [4 R0 G5 s  ?  a( L( D! m/ S
    5
    * |5 U8 U  V# z6
    - f4 c) i+ }/ S! `7. ^! R) h* ~: x$ c/ W: C, B/ w9 q
    8
    : `9 N+ d: v5 {& _0 o9
      e# W( |+ R2 M- r8 H- X# x& d0 b10: G2 M; V# W; |: D/ n
    11. B# v8 o# w: c! ?
    替换
    $ x% v# f3 m8 K9 Lstr.replace和replace并不是一个函数,在使用字符串替换时应当使用前者。
    & W/ r6 Q! D# c* ?* E1 T: }- k! _s = pd.Series(['a_1_b','c_?'])
    % V* U& n) t5 F  G- W# regex默认为True,表示是正则模式,否则第一个参数内容表示是单纯的字符串,也就是匹配字符串\d|\?6 v- l7 l8 Q/ K3 K+ [
    s.str.replace('\d|\?', 'new', regex=True)
    9 u" _# P  P9 X+ K, u8 J* X( S" k3 n. A0 }0 W1 U# O) w
    0    a_new_b
    ) R. }* C3 y2 U2 x* Y8 ?6 q1      c_new
    + C5 \+ {8 @6 D6 Z9 p: e, g! jdtype: object: S- I5 n3 ]) V$ D( Q# N% ^$ A
    1
    . X) C; p0 ?' Y# p3 g7 B2 F2
    0 M' D0 s# |  E8 V1 R  ^: {9 E3
    5 d2 L4 e% X3 q! Y4
    2 g3 G( `3 k& Z8 k2 L5 P5- m8 w; S4 y' s$ b( S
    6
    - b2 c( n6 W4 o3 u78 Y7 T# a: N' l, b6 w
      当需要对不同部分进行有差别的替换时,可以利用子组的方法,并且此时可以通过传入自定义的替换函数来分别进行处理,注意group(k)代表匹配到的第k个子组(圆括号之间的内容):0 k0 }' M+ g. W) n% G& a
    - F$ A. B/ ]: x, `* M
    s = pd.Series(['上海市黄浦区方浜中路249号',/ p: e9 ~' b4 Y& @" w
                    '上海市宝山区密山路5号',2 F) g. n  ~4 i+ n
                    '北京市昌平区北农路2号']): _! J/ M' k) N' T# Y/ W) c( X0 O# V
    pat = '(\w+市)(\w+区)(\w+路)(\d+号)'6 }: E5 |5 j& ~9 {( ?
    city = {'上海市': 'Shanghai', '北京市': 'Beijing'}# ?  g2 X* _7 d1 k7 [. S% ]7 L
    district = {'昌平区': 'CP District',# D$ Q4 x0 L+ [* e" \4 b. y
                '黄浦区': 'HP District',
    3 V2 l1 U; H) i! h8 x1 @            '宝山区': 'BS District'}
    3 P  J) H. @$ \road = {'方浜中路': 'Mid Fangbin Road',
    $ H3 i7 \8 B: d  e" q' N2 i' O        '密山路': 'Mishan Road',
    8 }5 |8 z2 B1 [) N$ |        '北农路': 'Beinong Road'}
    ) E6 n7 S" X2 v. q, }: xdef my_func(m):
    7 c; Z( i$ T$ k( B% L; P1 `" H    str_city = city[m.group(1)]
    ( C4 J9 h. |: x0 p: `    str_district = district[m.group(2)]# }8 ~3 I0 l7 }* x5 j* B
        str_road = road[m.group(3)]8 s, Z, x: }  g; _( `0 d- p& j6 k
        str_no = 'No. ' + m.group(4)[:-1]
    7 _( i! g1 H- X& M    return ' '.join([str_city,
    8 U, @7 x2 o" i; e! P" d- G                     str_district,7 l. u8 G( i8 l7 e# s/ |
                         str_road,3 h2 u: s. \9 [3 |2 x- y. J
                         str_no])$ n* S9 q: B' G7 I6 [' W6 d8 R
    s.str.replace(pat, my_func, regex=True)
    $ j2 Q+ W" b" K- j
    * a9 Z9 x! t: L" O+ T" N12 k4 a- u$ A1 @. R8 q% s
    29 e( d9 [) c, F# _4 B
    3
    ) C( H9 H7 ^, W* @( t# M9 c4
    . y6 E1 X9 Z, d! \) K, n4 q0 v5
    , Q0 @+ v  M) w- V- t8 k7 e6
    * Q4 v5 I/ M6 M- J4 f) H74 k: w) x5 b6 {# J! C$ a
    8) |: t3 }4 o' O, K, ?% ?4 `, s! [
    9
    * y; t. f0 e; J10
    4 \$ v! L8 C# P9 a. M4 S11* b0 p9 g# F. M$ C8 m
    12' L. m5 q2 e9 {" M
    13" r. r, e# A# Y: J8 G+ ~4 Z" V
    14
    " E& T/ R- y* C" u4 U* k15
    - o! v& Y! N9 c4 ~( J) w6 h16
    / }& \/ _+ W0 W17( A+ A0 F( Y  Z+ R
    18' `/ [6 s# B' ~) V7 g2 X" \
    19
    6 [% E, k6 _6 t% ?7 t! K/ w20
    # L5 M( r) [( `7 s21
    * |& e8 Q+ q2 b/ X0    Shanghai HP District Mid Fangbin Road No. 249* P9 O, Z4 i4 ?" `. K
    1           Shanghai BS District Mishan Road No. 5$ U) h( Q4 P. X* ^
    2           Beijing CP District Beinong Road No. 2+ |/ F3 I: j$ K+ Q! d
    dtype: object' W2 a' n; X3 B# w- C0 b. v
    13 J- l! {4 l6 {& f
    22 G4 |9 c/ ?8 x' ^
    33 |; \. D  |7 s# }( e( n- e. s
    4
    % V7 v  C; F! l5 @. M" K0 t3 h! B这里的数字标识并不直观,可以使用命名子组更加清晰地写出子组代表的含义:, |! Y* @7 R0 E6 h

    ( z) }7 d. J. H7 }! k7 ^- S# 将各个子组进行命名
    ( F9 ^! v6 w5 j9 f) _& E, rpat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'' j8 V9 p+ X6 E7 ]. Y, ]0 K; C& u5 }
    def my_func(m):& R* q$ `2 C6 E* F4 H; `
        str_city = city[m.group('市名')]7 d$ T" K! B# E: ]
        str_district = district[m.group('区名')]
    * `! P# }/ f7 K, ^8 M) ~    str_road = road[m.group('路名')]
    $ P0 p8 z; j3 m3 f    str_no = 'No. ' + m.group('编号')[:-1]3 v/ b2 |1 J2 _8 ~, S  l! \! {
        return ' '.join([str_city,. [/ ~5 Z& g7 U4 @! J  A
                         str_district,
    & t) K3 `7 Q$ o                     str_road,$ Y. W* n8 ]1 e4 e9 t
                         str_no])
    . r/ M$ T- E8 @7 e" J5 I& P0 O: u/ w& H6 os.str.replace(pat, my_func, regex=True)/ I) h  @# \& g
    1
    " p# |+ H7 }3 {/ I5 l2/ s' G3 h" S( y4 r3 W" |# F1 W; C# j/ |$ j
    3$ Z' ?; C, R' R% j! m3 t. ~+ L. m2 ~
    4
    6 Z" s% q. H" p5 w! d) l2 J5 Y5
    . q' ?- `: w7 d5 V1 a7 _60 q& U2 |0 I1 C) S# n* I7 ]  ~: X
    78 M2 \1 t7 k, M% u
    8/ p7 k2 t' i( [, R) G4 r
    9
    9 E1 L. B0 y5 S- I10
    , r7 m+ B/ ~$ a( k5 W8 o7 l, z% R11
    : j/ o: L4 y. e$ y' v: P8 S3 J121 N% M2 d% m( h$ A3 m
    0    Shanghai HP District Mid Fangbin Road No. 249& l( g+ V! l" H) ]* |( Q$ W4 C8 b
    1           Shanghai BS District Mishan Road No. 5  m4 k3 E  F  M' e
    2           Beijing CP District Beinong Road No. 2
    7 ~( W8 T! |! e' o5 l' Qdtype: object. i' F& |) e) f6 P
    19 Z- i; O( w; I0 ?
    2
    ( G$ `  m# g" S9 S  l3
    ) M" i$ s1 r1 M+ p; c+ D48 [- v! [0 y; `1 K/ i
      这里虽然看起来有些繁杂,但是实际数据处理中对应的替换,一般都会通过代码来获取数据从而构造字典映射,在具体写法上会简洁的多。
    - V* h$ O3 u1 f0 `! K1 h* x, i* F+ V, e: `& ^3 a5 b8 {! Y
    8.3.5 提取, m" v2 \+ v5 F; Y: D8 J* Z  W
    str.extract进行提取:提取既可以认为是一种返回具体元素值(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的str.split例子中会把分隔符去除,这并不是用户想要的效果,这时候就可以用str.extract进行提取:, D, b- l6 u3 U# a& X
    s.str.split('[市区路]')3 n9 O" \' D4 ^) A2 Z3 w% O. E
    Out[43]: + g) \0 q- |$ b8 k4 f
    0    [上海, 黄浦, 方浜中, 249号]
    & I! y8 ~5 n# e- o1       [上海, 宝山, 密山, 5号]
    ( H/ e* z- q7 ~) V- e. B: `3 }dtype: object
    6 x3 f# G! ?% x: a) o2 [- q- `. U7 \) N, A
    pat = '(\w+市)(\w+区)(\w+路)(\d+号)'
    0 J& N# E+ O' v7 vs.str.extract(pat)0 j$ X) e& F2 x* @) [" y/ Q: L5 }) f
    Out[78]:
    - {) v/ O/ \9 C: ~6 B" T+ k# x2 g% U    0    1     2     3
    3 r& X5 Z: L) U: ^0  上海市  黄浦区  方浜中路  249号
    ( l1 ?8 u" g# Z# i1 {2 _4 u1  上海市  宝山区   密山路    5号
    ( D- Q- |& |9 x+ T0 g2  北京市  昌平区   北农路    2号
    - w8 a1 b* B% H4 p+ N1) Z+ D) G" b! W0 e
    25 ~1 ^: v, Q4 T; x
    3
      k) b' k  A5 v4, V6 K" J/ k7 U7 ]* v7 Y* p; q) a
    5. E0 Z0 u. C$ Y  G. C0 o
    6
    % G! A: }- E6 M: y- ?* r* j: }7 [( u- Z70 ]9 P! |+ }4 Z2 N6 U7 ~$ o
    8
    + ^) T& S* E. V" W9
    ! t$ M4 l3 N3 H. R4 e8 K10
    9 r, s% s+ {' k% A6 {/ Y1 u11
    : M; w+ @, ^4 F12
    # i1 D4 P  k7 d/ e$ e1 Z13; Y4 P5 Q/ G1 ^5 P8 L! G
    通过子组的命名,可以直接对新生成DataFrame的列命名:
    ; f8 `! y/ E1 Z( ^: H
      T/ m: |2 A5 M+ upat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'( b% J& g7 _! Q) }5 E
    s.str.extract(pat)  K3 k$ X1 ^8 m% @$ j6 P
    Out[79]:
    / W8 @2 {8 x  m3 X6 T    市名   区名    路名    编号7 U, @! q5 k  X
    0  上海市  黄浦区  方浜中路  249号
    . G/ R4 u0 E  t2 E6 z1  上海市  宝山区   密山路    5号
    7 C& C1 Y% _/ Q- R2  北京市  昌平区   北农路    2号
    , A+ N* Z8 @. s6 F  s: a17 y2 [* u! j& k" `
    2
    , V( w* M* I$ M6 [  W2 V' |3- l/ x& T- A4 I! j3 f) I
    49 }3 k' Q# H) ?( t7 s
    5
    - U( ^1 k3 r8 O* m6
    + `$ f" E5 i- g9 K# K- c* q73 b# g5 ]( [( n$ ]. G* ]
    str.extractall:不同于str.extract只匹配一次,它会把所有符合条件的模式全部匹配出来,如果存在多个结果,则以多级索引的方式存储:2 z" |3 q4 w, ~1 z
    s = pd.Series(['A135T15,A26S5','B674S2,B25T6'], index = ['my_A','my_B'])
    ' o; Y1 O3 p4 `pat = '[A|B](\d+)[T|S](\d+)'/ I! Q4 O! S" I4 h9 D4 k
    s.str.extractall(pat)
    " z* L  ^6 |- I5 ?9 O9 tOut[83]:
    : X7 [1 a7 X9 Q" G7 b       0   19 X# s+ W6 k' ~9 m
         match         " V: V) w& J2 b! J+ M
    my_A 0      135  15
    7 Z! K2 [; Q) V     1       26   5
    ( t* [  Z5 a" C6 n( ymy_B 0      674   2
    4 @! l: v+ k* ~0 ~7 n$ l- u     1       25   6+ P, j/ Q8 J9 j0 z6 x: i$ e
    1) N) ]) t8 P) P# x, x/ J3 k0 N" @
    2
    1 L  |& i! |7 W* N3
    - C0 M- r; ]+ ?. s/ F" g7 Q, c4
    & O* P2 h2 d1 R- T; Z8 \; L5: F4 {+ M; s- Q+ W6 f+ y2 V0 {
    64 p* ?9 V  D. C$ j7 R
    7
    ) E* [4 l0 z, k% Y9 b8 o8
    0 N3 M+ o. n* y5 P4 @9/ o5 o; s4 B9 Q# i6 h& _% P# l( t
    10& i! e1 }0 n3 t: y& O% `, R4 Y
    pat_with_name = '[A|B](?P<name1>\d+)[T|S](?P<name2>\d+)'$ U, ~" H) I/ N0 w
    s.str.extractall(pat_with_name)- I- E3 {; _  e# j
    Out[84]: / r5 e; E  a! A* v/ l$ |
               name1 name27 n9 F% Q  _5 ~3 p9 `2 G. B  a
         match            0 f* @9 @2 ?6 `8 `( z6 l& a
    my_A 0       135    157 y( [' k( E1 H7 }+ _) J5 t! A
         1        26     5
    + j8 v/ t7 n2 Z+ ]; q* Ymy_B 0       674     2
    + l+ s7 W& X/ f% g5 G     1        25     6
    ( U" ~: B6 [7 c- p" }" h6 U5 `1, j# `) U0 D) v& W9 K8 V) w% s, m& l
    2
    + W2 R# i8 F, O3 _3! z2 R9 \) h$ Z+ N9 C3 x
    4
    " U1 w5 T& ^" T/ U! k( q58 E  Q8 X) E0 M, v2 x4 a- y
    6
    " h4 @# Q9 J& V4 }7! o* B, A2 D" W
    84 g' t/ K! J; x+ W+ v$ o
    9* m6 Y8 e# s$ W: A
    str.findall:功能类似于str.extractall,区别在于前者把结果存入列表中,而后者处理为多级索引,每个行只对应一组匹配,而不是把所有匹配组合构成列表。
    . ^% P" D" V% xs.str.findall(pat)
    & \. R# X$ y4 I9 ^# V) p1. g/ M% ]2 G. q0 _/ f! {
    my_A    [(135, 15), (26, 5)]
    . ^2 U3 }  {3 X! z: P7 b0 k1 M4 Imy_B     [(674, 2), (25, 6)]
    . h0 |7 `) |# Zdtype: object
      Y/ X5 B4 f$ E3 u- d12 |$ b5 P$ H- N2 B7 A0 A# W+ @
    2
    4 F2 n8 G7 R9 o: ~- O3& t- W- y4 D+ g- V; t# G" [3 m6 L& g
    8.4、常用字符串函数; J; t# p, x, v3 u* ^% t
      除了上述介绍的五类字符串操作有关的函数之外,str对象上还定义了一些实用的其他方法,在此进行介绍。2 I$ F8 _$ L& D0 B$ p
    5 B. k, O' H! l5 n# H
    8.4.1 字母型函数
    9 w9 X6 o. ?2 L  `/ R  upper, lower, title, capitalize, swapcase这五个函数主要用于字母的大小写转化,从下面的例子中就容易领会其功能:& y* a; i' z' R" ?2 p

    6 w  Y6 I$ E4 E) D2 A) t$ W; ms = pd.Series(['lower', 'CAPITALS', 'this is a sentence', 'SwApCaSe'])
    $ _+ Q$ f# g3 D, z6 w
    . N9 |8 s6 |3 ]. z# f2 ws.str.upper()% p9 C) Z* P% a, Z# \
    Out[87]: ' Q: P0 _' X: ]7 @: `
    0                 LOWER. k* z& O, S1 A8 F! ~; D5 p/ n
    1              CAPITALS
    # C  S% W+ E- A2    THIS IS A SENTENCE; r- i5 _! \2 }2 J2 n" R% O1 o
    3              SWAPCASE
    3 e8 K0 [: W) q( o6 {+ mdtype: object  u3 e% [9 z8 l$ e3 a! @
    4 J5 P" x/ K6 G: m2 v- b
    s.str.lower()' D) ]; R5 C( D4 O  J  e7 ?
    Out[88]: 1 _, F) x: \2 `% ]* B
    0                 lower6 I$ A9 b5 D3 i( L, {7 N* T
    1              capitals) P0 C! {! v! {! j: U. G* R
    2    this is a sentence' c8 _) p! ]1 O0 F: q( U! `8 h& z# b
    3              swapcase& r. O2 d" g' E* O' u
    dtype: object+ {1 ]+ y5 E/ x% x0 n* L7 F+ L

    , @" ~0 k# B; X' J" B) g) ?5 [s.str.title()  # 首字母大写
    2 F! @# ~: s3 A4 vOut[89]: % i3 b5 D8 @- }0 T: D
    0                 Lower# t; J8 V9 {) O3 ^4 n$ n
    1              Capitals
    1 s9 z% t! S8 z2    This Is A Sentence0 _4 s8 @2 n& E7 o( r
    3              Swapcase
    1 c5 @) p3 E6 q& g2 rdtype: object
    / t4 |3 o( d' f/ }' @0 O! C2 I! c& U6 Q6 i
    s.str.capitalize()  # 句首大写4 f6 o  {; r4 e- U( t
    Out[90]: % u4 Y& a- b6 r
    0                 Lower& W! K$ }, ^0 c  P( Y
    1              Capitals
    ; h6 c" ]" E; l  b' m2    This is a sentence
    ) F7 P; S' f* I0 c3              Swapcase
    - G* Z2 K  [; f+ q" pdtype: object% U! V1 f  ^: {8 m& g
    3 r" r5 t" O7 c  K- K% p
    s.str.swapcase() # 将大写转换为小写,将小写转换为大写。; J! \% L' o+ F% n
    Out[91]:
    % z  P! c9 Y1 [* B; |0                 LOWER7 @( ], s4 b% v: b5 H
    1              capitals; `0 ]& j7 F: y7 x% Q: ]: Y6 N
    2    THIS IS A SENTENCE
    / d' N4 K* v+ G2 v2 i" `3              sWaPcAsE, o; u3 Z: }. Q' i% R+ B  X
    dtype: object
    9 i& r5 K/ P- b0 C7 {0 A1 s% G5 h' |$ I3 f
    s.str.casefold()  # 去除字符串中所有大小写区别
    2 ^! f9 B5 C! H! ?4 |) Y9 A" [9 @$ X) X8 O9 {9 L; ~8 p7 z1 V
    0                 lower6 W/ [( Z3 Z- s- u) b& Y
    1              capitals$ c+ E8 M0 o! X
    2    this is a sentence
    1 f  M; J2 Q/ T6 a3 @2 k5 o' o3              swapcase
      r. K! U& H" m2 n% T
    9 }9 |! p& V( ^9 [( T" c* N1: m4 T# N% g# ?, `5 {9 s) H  V: n  u
    2
    + k# ~, j$ C# a/ ~% C3( t0 l) I) Y' _6 U
    4
    8 s  o8 d- [9 K, S  h4 k5 \5
    : L6 [( R" ~* v) _6) n/ _' w& [# s, X
    7' ~9 B- ]- D  d
    8
    % v/ \' M- }# Y4 \+ s9" W" g, F: o1 ]
    10
      r4 T! A  A2 n$ |; o11
    : R# V( k! l  q& ?1 ]12
    / @: f% z5 K& T# l8 q8 K7 c. z13# b9 f: z) F1 S4 I6 _8 Z/ y
    145 I, Q! F' c' S; l, A: Z) r
    15' b5 w& e9 @: ^; Y3 k5 h; V# L
    16, y4 x0 H+ H' j5 S9 A$ c% a
    174 }" e% p2 D' z: d5 {% n3 a  O
    18
    / N: s  r; ~, k/ G1 z" D19. N* H: e) m) `; |, C
    208 D- M8 v+ G6 B0 v( n3 m4 B
    21
    * I6 X+ ~* A& e, ]1 ]" u22
    5 g8 k% Q& b% f' A. U23* f4 I0 W; ?# J6 Z  n, m) ~
    24
    # k0 N# F  ]/ f" v259 ?5 c0 r. c! S6 j' V8 p& ^- Y: B
    26
      A( u! H) Z& O, U& M27. y& d' k: v# I/ ]5 @! L1 h4 B# V, c+ V
    285 E# s2 Q, {( C$ j) E1 s
    29
    + |' m( x6 ?1 F# |30
    % ]8 h' H) V9 ?: q1 j31  g. p/ [  R  j. S( x
    32
    ( O8 v' M0 n/ F/ Z- M) G1 U+ R/ M% s33! x" T! y$ I7 y' y  h
    34; k9 b+ z* x5 e" B
    35, a+ u6 D9 _  b, d0 v/ v: }* C3 @
    36
    7 B- Z( o7 {$ z  ^* b5 N( @6 x0 N: q! z37; l$ k( ?- F) `0 }/ U- N! Z) b
    38
    6 R% b; J, W' b! Q: B* k395 y: t1 u3 M. ~0 f  _
    40" O6 ?6 u3 h( o9 l  @# i
    41$ X1 X4 s3 V: h4 h( y) e" ^) @
    42
    9 t/ k) \: b/ f2 t43
    4 c4 Y9 M1 P7 u7 ^. l445 ?$ ~) w6 }$ U0 ?) A! ?
    45
    $ d2 r- C% X6 {# F0 m5 x% q8 \46
    ! L8 z' R* l7 C5 T$ h1 o47
    2 i: `  C- }; {48
    % v* ^9 v6 B# T! q8.4.2 数值型函数
    " }0 `0 g! [4 i# C$ {  这里着重需要介绍的是pd.to_numeric方法,它虽然不是str对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。其主要参数包括:
    : ]( g, t# _# S" _/ T( @0 x
    ; U& D0 q& ~$ z# p& V- g; x8 herrors:非数值的处理模式。对于不能转换为数值的有三种errors选项:. ~8 q/ Y2 I* A, M2 _
    raise:直接报错,默认选项
    $ k) @# O! L. s# g5 ycoerce:设为缺失值+ W( d7 ~5 A* a0 i
    ignore:保持原来的字符串。
    - k$ b! o$ V0 \' m# W1 I3 Wdowncast:转换类型,转成 ‘integer’, ‘signed’, ‘unsigned’, 或 ‘float’的最小dtype。比如可以转成float32就不会转成float64。6 L% ~( Z( s! v* x$ @
    s = pd.Series(['1', '2.2', '2e', '??', '-2.1', '0'])
    9 p/ X6 J! L% ^) z$ }5 a8 k( b% I& O8 L* {6 i  v
    pd.to_numeric(s, errors='ignore')4 A8 T* v/ Z+ e4 d$ Y
    Out[93]: 1 u0 {; X; b+ c+ L6 H; i  `) F
    0       1
    1 `1 x+ g- p& v) m0 Z5 w1     2.20 B" E  x8 h7 D8 O, V
    2      2e
    0 a8 B; @$ q/ y& H. b% {3      ??" k2 n* v6 H! x2 v  q3 ~
    4    -2.1
    ; k; @  c1 v% v; \& a1 Z) u5       0
    " m/ ~4 x" S& M0 H& [8 R  B* Odtype: object3 U" G; a5 L6 T- y& A+ ^

    5 ?1 |/ v% }7 E. ypd.to_numeric(s, errors='coerce')
    / \) _4 f1 W$ v2 W, Z9 V1 V3 YOut[94]:
    " F) j- ^( x0 G* D" S0    1.0
    2 u" B2 B$ M9 L8 {$ O5 ~* p$ c1    2.2
    3 ~/ L( J. i; u1 A& N- q8 I2    NaN7 `, }" j4 y, u, F1 C6 w) [
    3    NaN" Z" V# X3 C. ^' n
    4   -2.1- w* P/ a6 X5 \% y
    5    0.06 [( @$ v9 p2 h! f& |
    dtype: float64
    ! Z7 m" \0 P# [% b/ R% B9 S, U$ @( X+ a5 o. E" ?$ i2 S! _3 t  M8 |& u
    13 q" `. y6 j( K  t$ l
    2* r8 N+ i6 |  a# U# Q
    3
    $ M7 G! w  u8 ?' u- t) @2 ?- Q4
    ; ?; l' A( Q+ i- p5" J, j+ r9 t' f5 r+ v1 `- P& O  V( m
    6
    ) B' W; n  X0 ^7
    / L" g/ S, m: T- B, Z3 e/ N6 l8
    1 w& K4 p$ c2 G, N% h0 R; j6 D9& }4 B6 b% \  n9 M- r; ]" W6 H0 j
    101 i$ X' S2 L. q+ w4 r1 E
    11
    ! N2 t& J% Z$ x" u12+ s  f1 e1 m# ?( N# L6 `
    13
    2 g) R- _4 t, y: N5 m% R& F! n14
    ! Q4 X" \4 z  M$ @15
    4 Z, y8 B2 y# U* Y16/ B% I* |& p' c" y" y& Z% o+ r0 o
    17
    0 L6 G7 q8 `7 a" x; y18
    + X* R5 R, O- [19
    5 Z" f3 I3 c, {. `20
    % _1 r% T. s" o* Z  R% p21
    # @% Z" B- ~( m% |5 F5 _6 d  在数据清洗时,可以利用coerce的设定,快速查看非数值型的行:
    ; X! t) q5 z. l( {
    9 J" a: }) f! U0 J  Zs[pd.to_numeric(s, errors='coerce').isna()]* S7 o; e6 z! @$ I1 o
    Out[95]: . v& M' i* p- h9 ^+ Z6 C1 a3 {
    2    2e% t0 S6 v( q4 B( m+ @+ h6 \/ N) b
    3    ??
      F% a- }& J4 {( q; ddtype: object
    $ d8 j8 R+ c  L( v1
    0 {1 h1 u! {  T* V; E+ a2
    $ ^4 a  w" V" Q, |& _3
    & N. R9 M- L8 q$ I9 \7 w( |40 u: q! U9 i, I( h- o
    5
    % R0 x2 Q/ _$ {) F2 t  \8.4.3 统计型函数/ y+ X3 o% R8 A# e
      count和len的作用分别是返回出现正则模式的次数和字符串的长度:
    6 D3 q: l9 y1 k# t' v! J. ?& a7 n- b) q
    s = pd.Series(['cat rat fat at', 'get feed sheet heat'])9 Y& d1 W1 S1 e( r7 Y" L+ _: C
    ! N; x% D+ a3 X, ~- J, A! @( U$ M
    s.str.count('[r|f]at|ee') # |左右两种子串都匹配了两次
    9 c/ d& v0 ?! Y5 {1 |Out[97]:
    * c3 c8 |5 A- C0    2
    ! |- V3 ~3 w: I+ t7 _% |1    2
    * f) B! {, [3 P, |$ f! sdtype: int64
    . e) h0 W: b! q# G7 t( Y3 X& h! s4 s: k3 F# P* v: n- P" ]( v
    s.str.len()2 a/ U8 e* O3 x% I
    Out[98]: % G+ y5 H1 l8 c  |- S, _: V
    0    143 C. H# }( u- e6 N. n! a2 ^
    1    19
    # z  e9 ^* I+ M  udtype: int641 [9 s2 ?# n: w7 G; a( E
    1
    ! ]+ ]7 ^# ~$ @. g3 Q( n27 S' ?, c) E  N) {. b0 \7 z
    3
    5 g$ n7 o8 C. O8 n46 S" X& \$ h. z. C# U5 C+ g- Y
    5
    7 C" K3 e5 x+ ~7 h! z! D2 b) d6$ I! |5 i  _, S6 h2 E0 H
    7
    - }. m7 W/ g$ w* Q+ s& S  ~8
    ) \( T7 F7 i. |1 a93 h. q: k2 B4 F; ]) ~$ H
    10
    . Q. g, i  Y% j- ?; n: p8 m11) o2 e- T2 @) j) R6 G6 l, s5 D" ?, r' f9 {
    12
    3 L5 @( \9 S. K8 H% Q13
    . U) B" }. i- z0 x  s: k; M8.4.4 格式型函数  q; V$ R( p4 c% ~0 I2 N8 F
      格式型函数主要分为两类,第一种是除空型,第二种是填充型。其中,第一类函数一共有三种,它们分别是strip, rstrip, lstrip,分别代表去除两侧空格、右侧空格和左侧空格。这些函数在数据清洗时是有用的,特别是列名含有非法空格的时候。5 s! V. t" T" j% u* w
    & {$ q4 ^; [0 G! Q8 _) W/ A5 n/ p
    my_index = pd.Index([' col1', 'col2 ', ' col3 '])
    + U4 ?- ?2 V& a; C7 t
    & A" S) Y$ z" fmy_index.str.strip().str.len()
    : j$ d9 h" Z( S/ ~; ?Out[100]: Int64Index([4, 4, 4], dtype='int64')! z4 x, _! ^& s! z" x
    1 ?$ e! o& N# s0 f& w" R* y! s$ v
    my_index.str.rstrip().str.len()
    2 ?, Q/ j  m5 f. UOut[101]: Int64Index([5, 4, 5], dtype='int64')
      A2 i1 s2 ?0 P- d! B& `: M/ s( G* U9 A* _+ @0 C2 z1 u
    my_index.str.lstrip().str.len()
    # |5 w9 U% `/ o2 _Out[102]: Int64Index([4, 5, 5], dtype='int64')" L% }) P+ c: N0 H5 ]5 U
    19 W, K/ [* q7 C! u* i3 o6 ?
    2
    * T( C' ^* c6 X3
    ; j- g, e& p* t! F* X! V4
    5 G6 n6 Z! e9 u5
    & z1 U! t: y) F: {' H# L6
    # C- k& l  z! |72 v- ?5 H) p6 X& u+ ?% S, G
    8# _6 v7 N5 B. j, E& v
    99 X8 g# n6 z/ c" K& \/ o, s; W. ~
    10$ O& V, D0 t6 w6 ^6 D. ]# J
      对于填充型函数而言,pad是最灵活的,它可以选定字符串长度、填充的方向和填充内容:% d4 p" a: b+ b; z

    . ~: a2 N& a6 U( s; a* [s = pd.Series(['a','b','c'])0 S$ X' L& {+ d/ q7 j7 E

    : p$ W9 I9 T0 N0 T5 rs.str.pad(5,'left','*')3 _9 O; k+ B9 A
    Out[104]:
    3 N+ Y- \4 w/ y) C3 J" P# Y7 `( q, u0    ****a0 ?' w  g& \  ]3 V, p: ^$ B
    1    ****b
    " ]  C/ a* ^- J, Q: y8 m2    ****c
    ' ]; c6 A9 e4 `9 pdtype: object
    4 A" B8 Q! @: x: r" u
    + a  Y) _" K. ]0 Js.str.pad(5,'right','*')& [. n( j! h1 d6 Z+ r  p, e
    Out[105]: ! f/ L% R8 L2 o3 w3 g; {
    0    a****
    : v7 N' g: n9 G( y) A1    b****1 c$ }4 H3 n- _4 m, c2 S" C
    2    c****
    6 P! x! B" h% `9 t5 i9 Ldtype: object
    : ~& S7 {2 W& S' g4 ?5 d7 L" F
    : j! u8 D2 V  k, o/ ls.str.pad(5,'both','*')
    * \' @+ E! X. l% @7 [Out[106]: 7 x. B) D$ q1 ?, t) P) a5 O* {8 N" d
    0    **a**1 W6 k+ C% ]9 a1 h5 |
    1    **b**
    " V) l1 M, h  G2    **c**5 ~( ^9 y- I: e
    dtype: object
    * [& H* E2 q- Z6 [' s% V" J* x0 m) l7 j1 T% ~7 L; f
    15 x5 R# y4 _7 \" o
    2! |- F. I2 I. J! ]
    3) A( g1 G; [+ X: k0 B
    4
    5 b5 Z0 g5 ]9 S. R$ ?! h# ~5
    7 z+ v( t6 a/ N  [6" G7 t# T8 S) Z3 p, n
    7% \- u. j9 s% {% K9 K  ^7 ^
    8$ g# Q4 V4 T$ i" g, g$ P  ~) h
    9
    1 s0 }' g% a9 X# \8 z9 P10# z1 O% X  s# U* d0 c
    110 F( M7 }2 y4 t1 \
    12* _) H/ B! c4 Q+ }9 v+ F0 a
    13
    & {& W% h3 m; ~3 C) U14
    , \: H8 z0 [6 D( t6 ^15
    4 k0 ^$ g0 v# a$ `4 b/ t16
    $ ]  I  A  D& H: O, L1 o17& C  l  ?* Z3 g5 y0 Y
    18* Z3 r$ V+ l7 g: h
    19$ M' |0 ~& [) ^& h
    20
    . S0 w: c$ c$ _: j21
    & g/ i( |) T0 d" _" w223 h) f, Y2 T/ _
      上述的三种情况可以分别用rjust, ljust, center来等效完成,需要注意ljust是指右侧填充而不是左侧填充:/ E: |: a( l- L& d3 D  E
    6 ^: b$ q: H* `; G' x0 O# ?6 O
    s.str.rjust(5, '*')
    / d$ j( D7 P7 h2 BOut[107]: 8 T  v' {9 |4 b
    0    ****a
    ) w6 E; Y" b/ g2 c1    ****b
    - I0 e* R( x) J4 s8 i2    ****c9 q* ~1 U3 s* H
    dtype: object
    % m) z- n4 G) c& _4 w' F0 T+ B9 e; C2 ^0 I
    s.str.ljust(5, '*'), P: K* @4 R! ]
    Out[108]: , ~: ?* a. T" P, x7 u0 O
    0    a****
    : ^( V, _% a- X& M& ^2 E% Q1    b****. Z7 t: B; c% `% _
    2    c****5 g' i: t6 V+ F
    dtype: object
    ' A4 V" y* n5 j, r
    3 [- q; Q6 g; s2 P% M3 k& cs.str.center(5, '*')
    * _* W$ H5 P+ I* \, {Out[109]: 8 L8 d3 H( i6 M  E: `/ O2 L8 z+ v
    0    **a**
    - z* W" H. d: X+ v" [! m; ^' D* H. @1    **b**
      C8 Z4 D; j& V  R: C2    **c**
    ; p# I6 Y9 X& ?. ^" |dtype: object9 k# a  F3 ]1 v* \

    ; b/ }; g* T( B1 A6 {5 o5 v4 Y/ J1
    . p) D* g$ O+ K3 A2* s/ C1 F2 e; ^2 s) M
    3+ s( ]7 M; I+ q4 H
    4
    + M* v6 N- c5 X4 t! D5
      h8 _) x; L  l( ?6& y  V, e0 P3 x& @1 y
    71 v3 L$ u3 r$ r4 [! x# P+ P$ s6 N
    8  K" G- Q: I1 n- r, H' g
    95 i% _0 U# @6 [6 j- `% l* l
    10
    - @" a+ ~) f; y& S. G+ ~2 S11
    7 R& I2 L! m3 ]) B12
    + I7 E2 \# l& Y8 t137 H- Q- N( z5 B5 _
    14
    / ^6 {0 I1 m; @3 L' J5 W& ?15
    3 J! v% F$ q% p7 n# N16; D" W  K1 K) i" L* J- s
    17
    / T/ h3 N$ W& W0 i" B18
    2 X+ E4 E, e2 _. T9 a! w5 f19( F3 V6 J$ e" x% p  ~
    20
    0 R/ U5 k# {" u5 F4 _9 B  在读取excel文件时,经常会出现数字前补0的需求,例如证券代码读入的时候会把"000007"作为数值7来处理,pandas中除了可以使用上面的左侧填充函数进行操作之外,还可用zfill来实现。
    ; ^/ }% r! o; Q" M. ?  t
    + M. G* \$ w: M0 O" Vs = pd.Series([7, 155, 303000]).astype('string')
    " f+ ?) A2 v& S' {- B" `/ O7 @
    * C& G$ N- L3 ?/ O0 ws.str.pad(6,'left','0')
    9 u2 L* a5 n5 J( r- p( [' ROut[111]:
    5 z3 `" e- e2 Y" {$ P0    0000078 z  A$ y+ q2 Y
    1    000155
    * A2 V* B# _, o7 d: U) _2    303000
    ; f$ P8 P; w# n8 ]8 n1 A. D  h% ddtype: string
    " z* U2 _4 B* u4 ~2 }  G' R0 ?) }. I. C2 n9 \. p
    s.str.rjust(6,'0')3 g4 h: i( s  N6 e/ ~9 `
    Out[112]:
    - r4 {1 Y2 _, a6 M0    000007* f/ Y2 Z2 O4 E2 W( m' F
    1    000155; D7 b' _7 D, p& h9 m# l; z" }
    2    3030007 [3 c  S0 h5 ~# s# h8 A" v& c( S
    dtype: string
    ! G/ ]% A4 B: U8 ^9 }
    ) S7 p, Y; q- h% v$ [" n0 [s.str.zfill(6)
    # z9 I% m% F" x+ K* ^6 y1 y0 k$ EOut[113]:
    6 |- C! M% Y: i6 v, f8 }( x" d0    000007$ L# n: ?& h& U. C. @+ f" N1 o
    1    0001557 D# x( F' L8 G- C- X# }9 \: w4 d
    2    303000
    & u8 ]9 Y# B3 l3 K8 J! P6 I$ J5 Y+ l2 [dtype: string# p, F9 M# M. Q% q8 |5 X

    ) K3 v, ?; f9 ^15 }6 l( s9 H/ u' [
    2' W; |# R: {1 V/ H6 p. B( _
    3, W8 E% F' T5 ?
    4
    2 I3 _6 D. n3 U; }& }5 {57 z+ g5 C) K' @" P- q# a. I; p
    6" _" t1 f1 c" [: ^; m4 Q/ @9 s
    7
    ( L8 b# s6 ^! M; T  ?; d) I8
    9 X# h% z2 \4 x2 H9
    ! N0 j# ?3 m$ |5 T% _! u7 T102 S9 K2 w0 ^4 {4 A
    116 \' }2 `; y6 W/ H- E- ?& k( U% J
    12
    9 U3 }+ Z8 o" R! @1 ^, l; Z13+ d/ S9 N1 {$ G( M; J
    14# v  |4 g/ x( F3 q# ]
    15* @8 ~5 f  a6 Z' x. w; h1 I
    16
    $ N4 c8 S/ |; `, P17
    2 K: J  a7 J) a18
    5 N; f" k# y3 l- \( J19" p# k' x3 R2 \4 E, w. n. _2 M
    20/ m7 w6 n6 u* T2 ]
    21$ K  A; a) o6 U/ F$ N( b2 }3 [
    22
    / o+ [! y- O3 ]8.5 练习
    : O! g- ?% [6 ]  wEx1:房屋信息数据集
    + Z4 Y$ m7 ^4 o  C4 u0 j现有一份房屋信息数据集如下:7 ~. G4 `: [5 C' z; L
    1 x& ~5 D, c3 t2 g9 [8 d( }
    df = pd.read_excel('../data/house_info.xls', usecols=['floor','year','area','price'])
    - k; b1 U7 h9 ^! edf.head(3)- U9 q" S1 u$ b5 h( _
    Out[115]: : Y+ M! s% Y% |4 B/ E; ^1 `; Z
          floor    year    area price
    $ q6 y) {# U0 m; Y" Z4 G0   高层(共6层)  1986年建  58.23㎡  155万% }$ Q, y7 d4 |$ [- D9 e7 e
    1  中层(共20层)  2020年建     88㎡  155万* i- a& Y3 C; B
    2  低层(共28层)  2010年建  89.33㎡  365万/ l+ V% N) {/ W7 ~# v( t  u
    1
    2 j' k. h  \* T5 h2: B5 w: u& Z8 I% M. b4 V
    3/ F6 x% B3 v( |* ~
    4
    / L+ i" @$ `0 h$ G$ ]. V5) D; ?2 g) O4 P7 \* Y, S3 R4 {
    6
    0 t9 W( x% [. [7
    + x7 P; \/ {3 E7 a$ D/ `* F/ k( p将year列改为整数年份存储。, Z4 X5 {' |. ]
    将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。
    ( H! B3 V3 L: ~) q: i' ^' J计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数
    * `2 [. N4 k9 f8 Q7 D! K* {4 C将year列改为整数年份存储。5 `7 T: I9 F4 {5 x0 Z; @# j$ B& j& W
    """
    7 |& G& m! R' @5 a: F# u整个序列需要先转成Nullable类型的String类型,取出年份,再将年份转为Int64类型。
    4 [" @0 D! E* r5 \7 Z/ X" j% g注意,转换的类型是Int64不是int,否则报错。即使astype加参数errors='ignore'跳过缺失值,
    ) _7 ?/ G5 x- `) ^转成int后,序列还有缺失值所以,还是变成了object。" X; l( c5 |$ s4 C0 z
    而整个序列转为Int,就还是Int类型,缺失值变成了 pd.NA 。4 n- p# W+ i5 Q" n& ]% h
    """
    4 a6 O2 L; a, h0 a4 y9 |# b) y" u, Ddf = df.convert_dtypes(); D! [2 T9 X3 J) k$ I
    df['year']=df['year'].str.replace('\D','',regex=True).astype('Int64')
    # x( @! d4 E! h& t  t0 Zdf.loc[df.year.notna()]['year'].head()
    5 }$ a8 p- D& {1 ^! W% I  T. N: h+ s. K
    4 `1 q) j' o2 x, d+ a4 N2 h- Q0        19860 d* d3 J- _6 i. z) t
    1        2020
    + g. A, f1 Z" _+ t1 X8 u# n2        2010
    ' y( E8 Z+ r/ O" x5 [9 N# Q7 s3        20143 q  \6 D, ]! ^  |2 J
    4        2015: C( z( q  Y, i/ @( |# ~
    Name: year, Length: 12850, dtype: Int64. {' a: s# g! e9 G; i. |+ g3 j3 s

    ( \: i% G* m4 ]' K7 p7 |7 }9 l8 d, a1( K" M2 c. I1 o( o' Y) X
    29 _/ Y! C2 g1 ~/ K
    3( v% h2 B! l7 a8 S
    4/ i, t0 b) K" x/ O1 L
    5) _4 k; V& T1 E, L9 z# G7 N- [
    6
    9 c+ `3 K8 ~( d+ {) M5 o* H& ]7: ~7 U6 s( l' p2 h3 |/ c
    87 y( u" i# n0 K9 a( n( U8 K
    9( }* S) y4 A" D* C
    102 P1 M% t3 @: D! W% A
    11
    0 d9 C, D8 y7 C126 A3 F7 E0 h4 E
    133 ], j' p4 R" W! ?* |& |' j0 K3 [
    14) R8 C2 j" r; l3 P0 V7 {
    15
    2 s: W3 D7 J* h  N7 r% @1 I16
    7 L$ w# ~8 p! h$ i! g2 R! V- u参考答案:
    , o7 r4 ?5 r  O4 t( m/ H
    2 o8 n7 _! ?6 d9 K( r不知道为啥pd.to_numeric(df.year.str[:-2],downcast="integer")类型为float32,不应该是整型么, g# x. ~8 O: d7 D

    6 g4 w1 y2 U* {( l& `4 t* X5 d  jdf.year = pd.to_numeric(df.year.str[:-2]).astype('Int64') ' n0 s2 {" a& s# t# V
    df.loc[df.year.notna()]['year']1 K/ P& v4 h$ Z; V
    1
    9 x* l% p! W% R9 |23 w! M! f: e0 r5 T# y2 y+ T
    将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。& W! p1 C. N. e' T
    pat = '(?P<Level>\w+层)(?P<Highest>\(\w+层)'
    & L" A4 K" M- u4 r. _8 t( {df2=df['floor'].str.extract(pat)  # 拆分成两列,第二列还是(共6层得形式,所以还的替换一次8 e" F6 X. Z+ h6 b, i
    df=pd.concat([df,df2],axis=1).convert_dtypes()  # 新增列拼接在后面,再次转为Nullable类型
    + P! Y6 G6 G2 p( u( Vdf['Highest']=df['Highest'].str.replace('\D+','',regex=True).astype('Int64')              
    ; O6 r3 x: V. E5 l. n% Kdf=df[['Level','Highest','year','area','price']]
    ! Y# c9 ?  ?0 o' @; I( vdf.head()% e5 ~) v) l* {& J6 U
    ) q. Q* V3 K  V6 |( v$ y
       Level  Highest        year        area        price7 i3 n, P; g+ A6 D
    0        高层                6                1986        58.23㎡        155万
    / u0 o  }6 X. ?0 g* [1        中层                20                2020        88㎡        155万+ e" G6 i/ S" s
    2        低层                28                2010        89.33㎡        365万# Y! y& K$ P! K! _
    3        低层                20                2014        82㎡        308万* E; K3 K6 @# N8 j
    4        高层                1                2015        98㎡        117万- S0 T' p7 o" x) b4 I
    1
    - j& ~6 W9 B- `4 s* J" I" S2 T2( E- N: n4 r9 }# S/ M
    3. ^( E7 g8 j8 ]! h" b0 \
    43 ^% I8 J8 p" g; A, r3 P- X
    5
    6 n& ^6 b$ q1 k7 C& _( A0 u$ S6
      L; J6 y$ M- d! o0 q0 ?7, E. K; a- F/ O. o
    89 U1 G0 Q( G5 [# Y6 |+ g0 _/ i
    9
    3 e9 W  U2 C6 J5 @( ~/ v10- I1 B/ M' o; |0 p, }1 f; f. d
    11
    ) `( t& ^; L$ ?: e/ L/ R12% I; z2 J1 h8 _9 H9 {3 j4 \
    13
    % y* e* s6 U4 x# s% n# 参考答案。感觉是第二个字段加了中文的()可以准备匹配出数字,但是不好直接命令子组了- v4 x1 t# K' J7 a2 E1 b( D
    pat = '(\w层)(共(\d+)层)'8 H1 d' J% g  O0 D' ~: _
    new_cols = df.floor.str.extract(pat).rename(
    # C2 |$ Z1 _* `5 p                    columns={0:'Level', 1:'Highest'})
    7 @# l8 |- v7 f' }4 H/ ?2 }! h
    df = pd.concat([df.drop(columns=['floor']), new_cols], 1)
    2 q- z7 u, p) s3 |  gdf.head(3)3 F% f/ B  P8 ?  O, F+ N9 r

    + J/ |- I0 {1 S7 B$ F; @Out[163]: % G- W5 \# I' N* O: B$ \! C1 [. C
       year    area price    Level Highest
    * K; g- i1 p# Y/ E# i* @# {3 M0  1986  58.23㎡  155万    高层       6( O! x6 r" w% b) I6 p
    1  2020     88㎡  155万    中层      20
    % f5 z6 D. w5 }6 F+ x2  2010  89.33㎡  365万    低层      28
    : A+ q, _2 p4 @" H' ], G& t1) O  a3 g( `/ u  O: G
    2* I0 N" x$ w4 S  \9 j+ a
    3* |& v. L7 U4 T, U" `6 e3 W
    4
    5 O: o1 w- x# X7 z) @% l& L, [" Q5
    / i# y5 q, L0 r. l2 S: d6 g6
    4 N0 ^. ]9 s1 ~! d7: D4 E% U; @# ?' ]0 n2 Z
    8' \- t9 c( S6 ~3 F! u" i
    9
    ( s  }2 ]6 [' z  g1 q10
    3 Z3 ^$ E/ n! p/ N% _112 [: f. X4 m1 K1 b' N
    12
    3 s% ~4 y2 X' w( I13
    1 e- n( k" {, A8 ?1 O5 Z计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数。
    * c4 n! d2 E/ V- G"""- M8 O. d+ y" h' i1 C
    str.findall返回的结果都是列表,只能用apply取值去掉列表形式
    ) L8 ^2 T3 U/ S5 [参考答案用pd.to_numeric(df.area.str[:-1])更简洁6 e6 H. \, ^! m7 f5 p
    由于area和price都没有缺失值,所以可以直接转类型& P( }) W) b( h9 K3 P/ A0 _
    """
    9 B5 H' D8 C  b  P, ?: u  `df['new_area']=df['area'].str.findall(r'\d+.\d+|\d+').apply(lambda x:float(x[0])). o1 p9 e! x' w( |
    df['new_price']=df['price'].str.replace('\D+','',regex=True).astype('int64')
    ! Z" g: x& z% j  Y  L1 `df.eval('avg_price=10000*new_price/new_area',inplace=True)  z8 R2 e& s  y% P0 g* R" X% Z& h
    # 最后均价这一列小数转整型直接用.astype('int')就行,我还准备.apply(lambda x:int(round(x,0)))
    % o6 ?% F$ h& {) |( d# 最后数字+元/平米写法更简单
    ) t9 @0 s# e( b- r9 ^7 Kdf['avg_price']=df['avg_price'].astype('int').astype('string')+'元/平米'  R' i- u  q6 j8 H+ v: |3 j& e4 A
    del df['new_area'],df['new_price']
    1 a) r2 I& \4 ~2 Wdf.head()8 ~3 W  Z% x, L0 q4 Q9 j- A; |* k
    9 X3 B+ v; _% r6 Z+ p. r5 U
       Level        Highest        year        area        price        avg_price% L& s9 b# T! O4 _
    0        高层                        6        1986        58.23㎡        155万        26618元/平米
    4 @$ _  m5 |. s  I5 D1        中层                        20        2020        88㎡        155万        17613元/平米( \  N5 X! o: s2 P# J
    2        低层                        28        2010        89.33㎡        365万        40859元/平米+ x; Y. y. V( z: P4 S, V
    3        低层                        20        2014        82㎡        308万        37560元/平米
    * ?3 Y  h3 a2 N4        高层                        1        2015        98㎡        117万        11938元/平米
    " Q' T5 q( m- _5 {# m* _7 A/ d1 L
    1: F# f; X$ \1 }7 ^; g. e
    2
    & M9 l. c9 M2 j! R6 c0 f" x6 w5 }0 o34 B) a( J6 R: _/ f! q
    4- P) `' ?6 M; X& u, R% Z
    5
    8 N& o- J, x+ y+ r+ q' o1 ?0 [! i2 W; Q6
    , b! o. R" y. M1 a% u. k1 Y74 \! Y, f3 o7 o* d* N9 U# @
    8
    ! q" c9 x, ?) L& m9: |! _' C4 }% s- v
    10
    & b- N. ?% @; I- ^" W1 n, H8 [11, e3 U5 y, I/ u
    12
    / L! l' n6 U4 e! S, Q$ [13( r& w$ }; u' Z
    14* k9 `, p1 I# _' S0 p
    158 E1 h' P. W3 r6 s2 g& P' |
    163 d$ E' y9 i, |/ V0 b
    17: S- X$ y+ O" P0 m0 H! `( O
    18
    ) I3 N0 R9 A. Q( j8 ?  Y9 Y3 y4 I- S196 {* y- ?3 C: \
    20. _$ x8 Y8 t! p8 @; H. m
    # 参考答案
    ' B& S, d1 ]! _" |s_area = pd.to_numeric(df.area.str[:-1])) v% n" H. J4 T2 P1 h. m
    s_price = pd.to_numeric(df.price.str[:-1])$ o  x* W9 }% A
    df['avg_price'] = ((s_price/s_area)*10000).astype(& p& }1 j  n9 N% b
                        'int').astype('string') + '元/平米'
      L& f1 q/ D* K' b
    6 c: G" U, v8 F. N3 {6 `df.head(3)1 ~  }: C6 m  f' J- h* U1 A
    Out[167]:
    9 G# Z& S& m3 E5 k) A# I   year    area   price   Level Highest  avg_price: s* N3 I! X# C% F% \
    0  1986  58.23㎡  155万    高层     6          26618元/平米8 S/ M/ t" y' G: g/ J% y+ i9 T5 \
    1  2020     88㎡  155万    中层     20          17613元/平米
    - F7 S7 w6 w' e9 l8 X2  2010  89.33㎡  365万    低层     28          40859元/平米
    , n( P' i/ y, A1
      u4 G/ a! W$ P4 j: _) F6 t" v2/ T7 X) s* w" {/ V1 Z; n% ?
    3- D, \! S$ L: |6 L
    4
    9 v- i) C6 ^" i' U8 T5 O9 g: `9 O5
    ( g- G4 e  Z4 ?% w6
    . L, N" P3 L$ V; ?7
    9 r" s3 T  ?( T6 q7 J7 T89 M! T9 Q: f, b6 r" g6 g
    97 P1 p4 _) s; b2 f2 @  X
    10
    ' b7 R& O* V( W( c' b: o1 q; z" F% s9 N11
    ; A, E+ z: c  v12
    2 z& B- t2 N1 S0 fEx2:《权力的游戏》剧本数据集# P! J+ q) q; W" r+ o
    现有一份权力的游戏剧本数据集如下:
    + S- a8 m( ~4 S6 N5 R+ `( W! H! r' P6 W3 Z6 B
    df = pd.read_csv('../data/script.csv')# p$ m9 D: \( i8 T' }
    df.head(3)
    - E# d* C( c3 I8 a9 v6 V! ]
    * T' w+ j; h4 }1 s& g- Q7 XOut[115]: 8 }1 |% L3 v' z5 t" W
    Out[117]:
    ! O8 O& T7 Q* B3 a8 M! f& K  Release Date    Season   Episode      Episode Title          Name                                           Sentence( K' i! P7 @3 F4 g
    0   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce  What do you expect? They're savages. One lot s...
    3 i" I: @: _8 d1   2011-04-17  Season 1  Episode 1  Winter is Coming          will  I've never seen wildlings do a thing like this...
    8 Q9 \2 L3 X) V" Z2   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce : y8 y, O" L: ]
    1
    & u" I7 b3 @$ D  V4 P1 p2
    5 N: O2 `9 ]( H: o8 N3
    * J9 X* Y' f4 C4
    & y* b# i+ h" G# o. y. H3 C$ G5
    0 o  R+ }8 ?+ v62 U" ]. C" K. A# g. `: G, X! k
    7
    , ]0 A5 P7 k8 V; l" P8
    7 G: J9 m0 i" \% U( d. `94 R( @/ ]7 Q1 J4 A1 P
    计算每一个Episode的台词条数。/ b) L7 n& {7 J1 [) f
    以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
    8 u& `2 d- n( |5 ?若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有 &#119899; 个问号,则认为回答者回答了 &#119899; 个问题,请求出回答最多问题的前五个人。" r* F( @- j/ H6 d: l( P7 w% c1 }
    计算每一个Episode的台词条数。
    , e  W0 j6 V& l! bdf.columns =df.columns.str.strip() #  列名中有空格
    0 w& }& y& c/ D  Pdf.groupby(['Season','Episode'])['Sentence'].count().sort_values(ascending=False).head()
    ; _" g& h7 @, w/ Y+ \; x( o& w1 j" T& O
    season    Episode  
    - p4 ]! Z. O0 \" w9 Y( F8 V$ I2 ~+ ^, tSeason 7  Episode 5    5057 L7 m" S: B1 V
    Season 3  Episode 2    480# e6 t. ]3 [0 @$ ?! J( z
    Season 4  Episode 1    475, P% F" K' G6 Z+ }* @
    Season 3  Episode 5    440
    - B6 [* B  y7 m0 JSeason 2  Episode 2    432& ^0 K! s  P* i( S& ~; q( i- c
    1
    ! ]' K/ d  F' M; N5 i2
    7 l3 C1 _1 I( `1 [2 p, J- b( h) B3
    ( F2 V+ w+ `7 I, N47 n, {: ?; p' B
    5
    9 _3 C% v1 V' \6/ K& M" ~! n) R7 ^: b( \7 v
    7
    ( m9 \5 @5 _/ I' O80 ~0 z2 V/ o- b0 ^
    99 O, J; K9 J2 m  Q% r% b5 T+ Q: T8 ^
    以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。& P0 }, M9 X+ y0 F7 P
    # str.count是可以计算每个字符串被正则匹配了多少次,+1就是单词数
    2 y6 I; I5 E$ gdf['len_words']=df['Sentence'].str.count(r' ')+1
    / ^6 A) D' q5 y. n7 ddf.groupby(['Name'])['len_words'].mean().sort_values(ascending=False).head()0 O8 C. R- R2 g' k* E8 W

    % Z, T4 E9 z( s8 P7 NName
    0 {5 d, k5 C1 v1 L) Y4 U9 Umale singer          109.0000005 g  M) [! Q9 o' @/ t4 f" W
    slave owner           77.000000
    : I4 D: Z$ p$ }manderly              62.000000
    ) b. d& V$ x( V: ulollys stokeworth     62.0000000 J$ j* V. H: C7 M$ \0 _: Y
    dothraki matron       56.666667$ p+ @% w- o0 x& G2 F1 q0 w5 N. h
    Name: len_words, dtype: float64
    4 `$ Q. o/ O5 l: m- b' Z1/ P9 g& d6 z/ V
    2
    1 n/ v) d/ C5 E5 y0 f$ p. _3% {2 S4 d' [% R% s% s3 q
    4
    $ x9 A! A5 P+ v; [" _0 W5
    0 A9 R4 p6 B  y: [6
    ( ]1 _3 G7 ]5 M( l/ ?7( x7 Z3 a1 O; O6 d! M* m
    8
    : a  _) x  i' `6 [  `/ K9& n9 M0 H! u4 a$ G& U% P" ?" D
    104 |1 O+ ]0 q& a' F
    11" C# `& F2 O8 g* Q7 a  ]7 e
    若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有n nn个问号,则认为回答者回答了n nn个问题,请求出回答最多问题的前五个人。: V- Y2 g) V; _4 P
    df['Sentence'].str.count(r'\?') #  计算每人提问数
    4 w" m4 H, L& f- D- xls=pd.concat([pd.Series(0),ls]).reset_index(drop=True)# 首行填0$ L$ x) q+ ]4 ]" O' ?7 V
    del ls[23911] # 末行删去! }: ]% U3 e4 ]+ B, @2 K$ _
    df['len_questions']=ls& V+ T/ f) [* b( _
    df.groupby(['Name'])['len_questions'].sum().sort_values(ascending=False).head()  a4 v# I; b( I  g8 e1 b
    # M. u' n. Q' ~
    Name
    3 p1 [. S0 m" G8 o2 X" o* }: jtyrion lannister    527: c+ a, f0 s; j) u
    jon snow            374
    0 ]& f! @3 Y8 r% b) K0 z% djaime lannister     2833 O$ o8 [4 h$ h
    arya stark          265& L# T. ?" N: R+ {* t  j( Z+ ]' `
    cersei lannister    246
    . \" H/ F! [9 m; v* e+ {6 |Name: len_questions, dtype: int645 s. l8 }* i7 G: y5 U8 M3 Q/ ^

    ; L9 N8 o1 T0 y1 e# 参考答案% l" N4 }" M/ u; N4 e( Q
    s = pd.Series(df.Sentence.values, index=df.Name.shift(-1))
    ! ~! j# J6 ~+ q5 |s.str.count('\?').groupby('Name').sum().sort_values(ascending=False).head()% j/ E. |4 [0 R0 [# A6 G
    1 w- X% n( F9 Q) |" c
    1
    * s% g: c) j7 r  G2  }" v+ h* E  I' w
    3
    ; S7 B0 }- R4 W4
    0 s- ~! a* \( F* b; ?+ t- N59 F* y* ?  J! i6 J: M) j4 r* X
    6
    2 w" k+ m+ x5 h3 N. I7 w7 s) v0 B7
    ; Z" ^5 g" B* H/ t, O8- \/ r* \4 E3 O0 J
    9+ A! K$ _6 Q) c1 f2 a: {
    10
    $ w8 t* ?: {. H- H) ?11
    6 ?  @4 o, N2 @! ~( r% C9 x12
    1 M: {% j3 `7 P% a: {3 s13! P! @2 X& K2 K- O* d7 g0 C7 d- h
    14
    0 E) c4 c" n' f8 l- _, i8 C3 N15+ Q5 }8 j. v$ l+ o
    16
    + U4 V) W  [( d' E" M17
    & |& y5 r7 p- S, y% q3 F; x/ W第九章 分类数据
    5 X1 r& J# K! I; [/ bimport numpy as np
    % l# i6 u4 A/ f" R4 _( ~9 }9 Cimport pandas as pd' F* ?  Z& v* z4 M% r) }
    1
    * [" b8 B. _; T$ t) L2
    - U( c/ |, C' }( i: U7 a. B5 T9.1 cat对象: [+ A+ K, h/ a4 X
    9.1.1 cat对象的属性1 ~; W1 q- ^- [/ i2 M
      在pandas中提供了category类型,使用户能够处理分类类型的变量,将一个普通序列转换成分类变量可以使用astype方法。
    , @3 ^7 u- w( L5 R4 F
    ' k% S$ B8 l# E9 [) d4 {df = pd.read_csv('data/learn_pandas.csv',4 n& a7 R4 q' _* g( O
         usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight'])
    ( \0 K2 @5 g5 C9 b; Ds = df.Grade.astype('category')
    & v3 {& J0 ?9 H2 f3 j% ]' A; M0 ~9 v* `
    s.head()
    6 b: X  ^, B7 |& R+ R( POut[5]:
    * ^" O$ j, G' B2 V" w' _, n0     Freshman
    4 k, D0 s% c& G) a( y% Y4 h0 m1     Freshman. n5 c/ \" R, r: z3 Y& l% z
    2       Senior
    # }" J+ g- _: q: C4 U3    Sophomore( o! K% {7 z& f4 ~4 Y3 f
    4    Sophomore
    : n5 [6 I( x! K! a. b# N& CName: Grade, dtype: category
    . }1 A! u6 {/ k8 u2 R$ rCategories (4, object): ['Freshman', 'Junior', 'Senior', 'Sophomore']
    4 k- U( y2 g) X1. D) V& i2 S* C( ?  ~
    2
    6 [. V& S& `& P3- u, x' m6 l- O/ m3 v4 ]! L' r9 l
    4
    9 x7 n9 C* ~- S5 z( v7 |3 V& j5( X/ q/ {: I4 }: V
    6* T" H# `  l6 A2 U! G: g0 t
    7
    - F2 L) ^$ ]5 ]3 F! f* `83 b4 w* I/ v2 b! _! f* }) l
    9) Z" o" d2 X8 O7 N  j
    10
    , m: T7 S6 G" H6 Q! z8 J* E4 h11. P7 F  z. `& M5 Z7 O  o0 p
    12' y/ d5 N8 _' e* R% R  B1 g2 W
    13
    3 d+ ^) R  f: v+ H4 e  在一个分类类型的Series中定义了cat对象,它和上一章中介绍的str对象类似,定义了一些属性和方法来进行分类类别的操作。
    1 ~+ Z# s2 J2 r, M' m' S' |& L, J3 `+ `/ ^- X2 }4 B
    s.cat
    ( U$ A% B* T/ T/ YOut[6]: <pandas.core.arrays.categorical.CategoricalAccessor object at 0x000002B7974C20A0>+ R/ {" o0 m; q7 O& c: g6 p
    1
    2 v! Q; C/ p7 c* h8 z' P- p2% ?" F5 E6 g3 E. i  a. q
    cat的属性:
    4 k7 o7 s% P9 X; i2 U* |8 g- H8 z4 O( o: @
    cat.categories:查看类别的本身,它以Index类型存储
    . v7 a/ Z9 L2 B+ G- z) Ecat.ordered:类别是否有序
    , X6 i0 }! R: Y( |: r/ I" ^6 ucat.codes:访问类别编号。每一个序列的类别会被赋予唯一的整数编号,它们的编号取决于cat.categories中的顺序
    1 j9 R% ^" `3 N# f$ l3 bs.cat.categories( f: m6 E: N+ ^& K0 ~: T
    Out[7]: Index(['Freshman', 'Junior', 'Senior', 'Sophomore'], dtype='object')# f2 s" H" c. v* Q5 i1 D! {& H

    ( G- j: c/ o) y" ls.cat.ordered
    1 X8 v* q; e" y* t2 t+ sOut[8]: False
    ; R! A. k& d  x% H2 G- l
    3 P0 X; g7 @: r( v" ]s.cat.codes.head()& c$ a; w6 w- @3 O
    Out[9]:
    # c) L* `" W, G4 f0 |) ?0 W8 E0    04 J5 T- ?: Y+ D8 Z* L
    1    03 i: l. u# _' f, {6 c
    2    26 t+ ~1 c- T7 K# S" N
    3    3
    2 j1 o" q! ?0 B, d: a4    3
    4 r2 R* w5 z5 e6 r  \dtype: int82 X$ b. Y$ V6 e1 d$ r# j" n" ~
    1
    1 O, \7 A% c& J' h( G3 q7 D2
    1 Y3 G! g3 a6 b) D4 P/ K1 x3
    # `1 {* |& [8 t! @) A7 i+ i4& E: X! \4 ]) _  P& q$ _8 X% F4 L
    53 |7 ^6 C* h  N
    6  g6 b% Q7 x8 w3 P
    7. H* \$ ~, G! n1 c2 ]
    8
    % T' S9 q/ {- D. J+ U9
    9 J1 K/ ]4 B' S0 q6 S10
    * g) T. h- [' u, u  w8 k) b117 v& b% m5 b- }5 n. g) \
    12
    ( {) M$ @; W4 S1 ]0 G! T; ?+ F; v138 t1 m: k0 u9 e9 Q# R0 G8 n% l
    14) C& W  U5 ]& N% a
    9.1.2 类别的增加、删除和修改
      p1 p! ?" N* p6 l* i7 Z2 [  通过cat对象的categories属性能够完成对类别的查询,那么应该如何进行“增改查删”的其他三个操作呢?% t# A  A  A0 u( E- \
    4 I; ~  k  [' D& |+ B# Y2 t
    【NOTE】类别不得直接修改
    " ~0 B: Y$ R" f在第三章中曾提到,索引 Index 类型是无法用 index_obj[0] = item 来修改的,而 categories 被存储在 Index 中,因此 pandas 在 cat 属性上定义了若干方法来达到相同的目的。
    9 f4 u" ~+ C6 o1 A# ]% ~( {! G( v7 I( q3 O, l; s( ?9 U
    add_categories:增加类别
    ) _6 ^* C/ h8 \2 `s = s.cat.add_categories('Graduate') # 增加一个毕业生类别: Y5 Z0 U  A3 z, A8 F9 _1 U
    s.cat.categories
    & e8 i* k, V/ e' Z% I4 q
    4 t, E7 B) M/ z0 S. SIndex(['Freshman', 'Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
    * l# V- `$ v/ {8 [- H; F4 S) N1
    # g$ r' E, H6 e8 q0 A" t8 h2- j4 p/ H+ I/ d4 y& c$ n3 q
    3. c" k1 y0 Y0 u$ J. [
    4
    6 ~- [0 J2 d6 mremove_categories:删除类别。同时所有原来序列中的该类会被设置为缺失。
    1 a! ]% a/ S+ J" v" |s = s.cat.remove_categories('Freshman')& d8 O1 }1 [" X# a9 `9 ?5 K/ U

    & ~' T5 T8 l6 d) v) d0 Js.cat.categories
    ; l; `! N4 [/ P' a* a% |. HOut[13]: Index(['Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')) C6 T5 H0 b1 [
    5 f- ?/ Z! h1 |. s
    s.head()
    - Z# I. G# e& t2 Z& rOut[14]:
    ; P9 q& o5 S# o& K9 i: H- z0          NaN( ]- L  Y7 _4 G& u& ]1 @
    1          NaN: e) y- `! k; a( M, y- g
    2       Senior
      G. B6 L# b' M( g  L3    Sophomore
    8 b- R  ?( v1 Z* ~: y& t4    Sophomore
    - y5 ]7 L( \8 k( cName: Grade, dtype: category# }9 j1 D6 _: H4 O: q
    Categories (4, object): ['Junior', 'Senior', 'Sophomore', 'Graduate']  L* Z; n! E# \  c
    1/ B5 N% ]; n4 f" w
    22 n" L* o8 k! k, [4 H. o
    3; e3 h% i* u# A3 ?! D( l6 o
    4- ?& q- |* g0 \/ B
    5% u% D% E0 |; f. l) ^: q
    6
    6 w1 `" Q- Z1 _$ b7
    . q$ V0 [: i1 o$ g8# F5 M& w' G6 c, Z" o* a
    9& d" d; o0 L& K( q( W
    10
    ' m3 U; e1 p& z/ L  U; ~' k118 x$ e1 N. S( e
    12
    , V+ `" h1 w. i2 L136 V. g1 H: a0 I, j! Y; p4 C
    14% k/ n) X; y- d* f. B. E
    set_categories:直接设置序列的新类别,原来的类别中如果存在元素不属于新类别,那么会被设置为缺失。相当于索引重设。
    0 |$ c4 b2 j* xs = s.cat.set_categories(['Sophomore','PhD']) # 新类别为大二学生和博士
    0 Y3 {! ?3 \! _) Z9 ps.cat.categories9 x" y( S4 [5 T+ Q* G  k
    Out[16]: Index(['Sophomore', 'PhD'], dtype='object')! A" V* _) V% n, U  I- q& M: R
    4 B$ w+ m, N7 M8 Q; u
    s.head()
    ' V- ?$ \1 n+ J) v5 ?2 gOut[17]: ! z) _& y: Z- {9 L
    0          NaN
    # l" W- l- J9 T7 i5 x) w8 c1          NaN- s5 f# _; m6 J% L/ _, Z# {
    2          NaN7 g. p, x  W# G/ u* v  t! [+ ^- j4 f
    3    Sophomore
    ) K: {6 W, p! o# B4    Sophomore9 S1 M# Y2 \7 P! \% m" o
    Name: Grade, dtype: category
      I: \9 f: ?5 C6 b* FCategories (2, object): ['Sophomore', 'PhD']8 X  Z2 `" N/ r% f4 \" ^) j
    1
    ! t0 n1 O3 G& p, h2! g' y+ l+ `6 P1 v) t1 n4 g" |
    36 {) E* s2 ]: n, }$ ]
    49 k. g' i. x) [8 @; I1 k3 a" v
    5
    $ y# i% j) ~0 \2 g0 o" H8 I6
    " H. m9 u5 n4 }4 ?7  r$ ]4 B) Q6 C+ o/ w+ |
    8
    ! y3 B, d5 g% y( ]" \7 F, O* l9
    * p2 ~( w7 g+ t& p! o  @: t2 v) J10
    $ A0 J+ U! v" ]9 R: q4 B/ a11
    6 a7 f; ^% X, ^$ F7 [% s5 E12, b3 o0 U/ E3 V
    13# _6 f4 b* F% N: Z6 c
    remove_unused_categories:删除未出现在序列中的类别/ d# g! S& H5 G& r" [' {0 l$ C
    s = s.cat.remove_unused_categories() # 移除了未出现的博士生类别
    ) ]/ S$ Y, r8 k4 h7 U* Js.cat.categories- t% s  i) w1 K5 |2 ^1 p

    6 x3 p) v8 K7 [6 ~Index(['Sophomore'], dtype='object')
    8 O; p% J6 F$ G5 K1$ G- {1 n: S9 o! I
    2
    * G2 O$ n4 S' L" f4 H; L3! |  b$ C  Q! f' m" a+ P. p, Q, l0 A
    47 o$ @% Z( c) b- U! T
    rename_categories:修改序列的类别。注意,这个方法会对原序列的对应值也进行相应修改。例如,现在把Sophomore改成中文的本科二年级学生:: b2 Y  v, K- ?! ~8 q0 }
    s = s.cat.rename_categories({'Sophomore':'本科二年级学生'})
    9 t# R& U' f* ^! B0 G4 A! Es.head()4 A5 s- g4 q7 J, @
    9 p  |# j; _  W3 H5 H6 C
    0        NaN2 p7 W  {) V" N
    1        NaN% B6 c$ t2 u  }7 L6 k% E
    2        NaN8 I: D" q0 _+ N) e
    3    本科二年级学生& e8 A) M# d2 Z. a0 L
    4    本科二年级学生/ ]/ q& P4 m$ B* W, y
    Name: Grade, dtype: category
    0 Q$ I1 }) o0 [5 XCategories (1, object): ['本科二年级学生']
    ' [4 H: a/ G: U8 ]$ E5 p# e1
    # U2 h: {) r$ i, R9 N, V0 s2$ G# e8 o% a& N" P
    3/ z& w7 S+ D3 D; o6 l" A, F6 T
    4
    ' l% t& d& a2 ?' S' _. `5: \# R6 r6 ]4 y" s( H
    6$ e6 n0 P6 v. L8 k  H& G
    7
    4 N% a4 o; Z, \4 ]: `+ {% o/ m' C84 O+ C7 X; n% P) W  ?
    95 q' F" a  Z3 E% b. Y
    10
    ; }9 Z% ~7 F2 [9.2 有序分类# `+ m. D: i) Y/ I, P+ i
    9.2.1 序的建立; l7 ?; P3 t; i
      有序类别和无序类别可以通过as_unordered和reorder_categories互相转化。reorder_categories传入的参数必须是由当前序列的无序类别构成的列表,不能够新增或减少原先的类别,且必须指定参数ordered=True,否则方法无效。例如,对年级高低进行相对大小的类别划分,然后再恢复无序状态:- C* i" F) l: v5 }. D

    ' Z/ x. W! T) l$ Xs = df.Grade.astype('category')
    9 T2 d) m7 X- D4 H! ]s = s.cat.reorder_categories(['Freshman', 'Sophomore',
    * M- @$ G) L1 C0 M( Q  j                              'Junior', 'Senior'],ordered=True)9 R/ X% @. s( ~$ s! |8 ]; ^
    s.head()
    . \/ C8 j& B$ v( G/ N6 nOut[24]: " H! l. l9 B/ Z; N1 _- o; S
    0     Freshman
    ) V" y& A. Z# r1     Freshman
    / S! ~, ?& ]1 X2       Senior
    6 e/ M9 C2 h3 g. [) ~# m* ?6 U$ L) x0 g3 p( q3    Sophomore
    3 t1 x% D9 _9 N* V! e* [: N4    Sophomore
    0 z* R! r, `' ?8 `8 a( t- DName: Grade, dtype: category
    8 B" E* W& ?: \1 x7 Q. d: U' \  eCategories (4, object): ['Freshman' < 'Sophomore' < 'Junior' < 'Senior']) ?  D2 U2 i& @' F
    ( |; s, v: Q! }
    s.cat.as_unordered().head()
    ' z6 a5 H6 t. a0 `/ _! @* Y* iOut[25]:
    9 W# {+ T' T. C; V- v0     Freshman
    # H% i" g' W2 G1 T' Y1     Freshman
    + [# q7 r1 B2 B3 S8 W' {2       Senior
    + T# J5 l! R( Z2 a* M1 v7 l3    Sophomore
    & z- l9 x1 {& E; e$ J4    Sophomore% k+ x- E2 Y% {
    Name: Grade, dtype: category- g4 _& C5 p$ o
    Categories (4, object): ['Freshman', 'Sophomore', 'Junior', 'Senior']& n' B, S: o9 e  D

    + n' o7 b8 p+ n0 G4 X# _$ G1, ?( L' R" I0 J0 M4 @5 m
    2
    5 \! U1 C4 F/ @" [5 y2 K# i6 c( I3/ U3 O6 H# o! V: @$ B2 k
    44 z( q2 `* f: _6 |! A
    5
    6 B0 T: V$ J8 Z# G) |6
      M% s8 h# U/ Y% G# \! S7
    $ a) l1 z' j# z# o  o2 s% m8+ r. {, B. ^0 D/ j' A. v/ n
    99 P: Y- ]9 r" q* k( F& \! _8 s+ T; y
    10! {) E* |( W# A& f7 {
    11  x2 b; L3 x' Z2 z3 S3 I7 N/ o
    12
    , L0 S1 V: `9 R0 p132 b2 _" S" ~2 {* d$ j0 k& Z. f& X
    14
    % c& ~8 l/ y* _1 ]2 J+ I! @- Z15
    4 M0 w" }! w# I16
    0 q/ l& ^" {, h$ ~& a6 U8 `; U& Y& j17
    0 j" ~0 S4 G1 t( j7 C18  p* {+ M. H) p) K& L6 ?( f
    194 f" H) q9 ~" l" W
    20
    1 s/ a) X0 w4 \. T21
    & E; }( N+ o4 {! b1 Y  K7 Q22, ^: }% E3 e  ^$ b# e( F/ z6 n
      如果不想指定ordered=True参数,那么可以先用s.cat.as_ordered()转化为有序类别,再利用reorder_categories进行具体的相对大小调整。
    8 S0 A: v( \9 C  {/ N% Z- U  X+ _" p, Q. ?/ w4 \
    9.2.2 排序和比较$ t9 U- f) B( F; ^
    在第二章中,曾提到了字符串和数值类型序列的排序。前者按照字母顺序排序,后者按照数值大小排序。4 f4 O+ N* Q8 X; I& @

    2 Z& R3 n  g, o& D  分类变量排序,只需把列的类型修改为category后,再赋予相应的大小关系,就能正常地使用sort_index和sort_values。例如,对年级进行排序:. w8 c$ \2 K) i: a- G3 I7 j- |

    0 F- y$ V' W( c$ Ldf.Grade = df.Grade.astype('category')
      R/ o& T# }5 m9 R' H' l4 idf.Grade = df.Grade.cat.reorder_categories(['Freshman', 'Sophomore', 'Junior', 'Senior'],ordered=True)( c: M, s) ]3 r4 H! Z
    df.sort_values('Grade').head() # 值排序
    ( `, E- s0 v* ]$ S4 y7 X, aOut[28]:
    5 W) D! R! c3 @) b. U" a$ Z: m        Grade           Name  Gender  Height  Weight
    3 s; T6 o& o7 b4 f$ Y' J0    Freshman   Gaopeng Yang  Female   158.9    46.0
    ( z7 X, I3 {" b" \105  Freshman      Qiang Shi  Female   164.5    52.02 S- H3 D5 x* ]4 g0 K2 N; F8 }# k
    96   Freshman  Changmei Feng  Female   163.8    56.0) i- Q- a1 \$ W: r( [8 p
    88   Freshman   Xiaopeng Han  Female   164.1    53.0: V% t4 K8 {( T- j# m
    81   Freshman    Yanli Zhang  Female   165.1    52.07 u% w& O, @% F

    $ D# `! ~) [2 W/ R- C; Qdf.set_index('Grade').sort_index().head() # 索引排序
    . M7 ^3 ^) s! t" R, OOut[29]:
      X2 x6 s' e& D. q! n+ Y4 A                   Name  Gender  Height  Weight6 |( a" w/ f; l
    Grade                                          
    6 n# O% d6 [7 x+ S" J8 A; MFreshman   Gaopeng Yang  Female   158.9    46.00 n* [; W4 `# z; @8 M
    Freshman      Qiang Shi  Female   164.5    52.0
    . [0 r  H: I. y' p3 ^6 ^! DFreshman  Changmei Feng  Female   163.8    56.0! F1 ~0 a0 k  ^( l, R! D9 n! q) `
    Freshman   Xiaopeng Han  Female   164.1    53.0
    1 Y) C4 i) d! u5 p7 `Freshman    Yanli Zhang  Female   165.1    52.0: a( W% U- b  i9 c* d! r- }2 J
    ) k, W, c% [% N- {
    1
    " C0 K7 U' m7 ?" {8 X: [2
    . d$ d* c5 R' \# P) M3, B1 b+ a$ L1 _2 [
    4
    0 f) z6 Z4 z  Y+ x6 w+ Y( {5
    ; v" y0 x1 {5 v  R6
    ; m" p2 R& P4 I* D/ ?$ S: }" x7
    / T( R, P) X5 U81 L* Z& n" n9 H) n/ e+ w9 [. L
    9, }1 U/ w/ X- B3 j  I
    10
    : T$ d. u; F2 P) u  e, S117 S% r1 B9 T; d! k. l
    12/ y  F8 U9 V  I+ J% s
    13
    6 P. h7 t5 _5 c0 F14
    : S: Q# y) i  r5 k15, d' e( l' o! W* b% L5 S
    16
    0 I4 E8 D* t4 K6 S' B. e$ h3 V17' J9 D( k( Y, s7 A' s% A
    188 E3 K% ~. N! e2 M" R2 s# h
    19) l9 S4 g/ Q0 E- K1 S8 |7 L
    20' W, ?! L& z: q
      由于序的建立,因此就可以进行比较操作,方便后续索引操作。分类变量的比较操作分为两类:' g: m0 {7 B3 _3 a+ P

    ; g& i0 h9 p; [" z9 _" ^==或!=关系的比较,比较的对象可以是标量或者同长度的Series(或list)。(无序时也可以比较)
    ) }" m7 z& _/ r# \# T& |>,>=,<,<=四类大小关系的比较,比较的对象和第一种类似,但是所有参与比较的元素必须属于原序列的categories,同时要和原序列具有相同的索引。. }2 H& G2 n) Y0 V3 z& l# k
    res1 = df.Grade == 'Sophomore'
    % f: \8 M3 m: s1 K3 J* b, t/ l' N8 `) H/ ^5 H. W
    res1.head()& ?% ]! J9 F8 B. j
    Out[31]: 4 K# G7 C6 _; E
    0    False
    % A# }% H* z( ]1    False
    3 |9 e6 k3 w6 G/ y, I2    False" d. x* v% {8 U8 T8 I4 i9 z
    3     True. G3 I$ t" H/ z
    4     True
    . n4 B$ r4 a+ M$ s4 Z9 cName: Grade, dtype: bool8 g0 m( Z! I: P* O
    1 t" G1 r5 _4 o7 _+ ^
    res2 = df.Grade == ['PhD']*df.shape[0]
    # e' w9 F7 J( E" n
    & T- @0 S7 {  y. T/ f) Q; `res2.head()1 p& a# I6 |% @& `& z' e' n
    Out[33]: * q: k' b# `0 k' l
    0    False+ R+ b/ U! S1 L- k4 C! r) m
    1    False
      m8 h0 w4 R# v: k, m7 w7 m7 Z/ w2    False+ ^  R) }& P: c" e. M( }
    3    False
    # {8 b6 X# T1 d) i4    False3 `$ C  s3 b! i- D9 f: t
    Name: Grade, dtype: bool
    % |9 E+ J$ q$ Z- M3 O% s2 [+ A/ L8 e/ V! p$ N6 S" @# x7 L
    res3 = df.Grade <= 'Sophomore', V+ m/ {4 {; @) P3 m! |! }

    & Z; X" b* d8 d+ y6 ^- ^res3.head()
    4 J* p+ \( I4 r/ N0 a  g5 ]8 iOut[35]: * t- G' U# ?7 Q+ _2 f
    0     True, P7 ^2 \# h. ]- R0 n  ]
    1     True
    8 [5 ^5 c4 P; i* `0 ?2    False; k; h7 I9 X7 ^7 F
    3     True
    ' }/ x0 C( t0 [. E7 L; ]7 G4     True
    : z- g5 t% O6 I! x+ KName: Grade, dtype: bool
    + J) u! ^; I3 Q! x: u
    2 j/ N6 T3 ]1 q# sample(frac=1)表示将序列随机打乱。打乱之后索引也是乱序的,直接比较会出错,必须重置索引。) E, ?2 S" ?% q2 w
    res4 = df.Grade <= df.Grade.sample(frac=1).reset_index(drop=True)
    3 i" z( n2 }! e* ]0 u, Y% T" `1 A$ J- k- v: t
    res4.head()- W0 @; \! @6 F5 z
    Out[37]: 0 \' a7 i* l7 H
    0     True4 f2 O* i: I% c4 Y7 U$ s' Z
    1     True& F; N7 ]% p  S  S* b
    2    False
    5 I  o& p! A$ N7 }( w, T4 R3     True
    " @2 M" c+ w  B4     True
    1 f$ p7 D& x* {0 p/ z0 r- ^Name: Grade, dtype: bool- ], q; h! Z5 N0 d9 P8 `3 q% {
    * ~/ v- d1 A& _  i" c7 D
    1& p  z! h7 W" }6 }( |, M( M! c' B
    26 f5 j$ s7 w; p7 x
    3
      W) L/ U6 T! \3 r% X" n4 g4
    4 j* S+ Q# q5 N4 ?7 s# b5
    - @5 P" i* R& l6
    $ h* A# B8 V: M3 A# e' E' J* [7# T8 ]4 b" ?4 t5 f7 F
    80 `' U* E4 U( H0 J/ l1 y
    96 q' C2 o( j( ]; N( `
    105 R0 n9 v4 R& h" Y& _+ N
    114 b$ ^8 M- o# i: U- q
    12
    " G; E/ G6 n2 U! y$ m0 M: U' O13
    1 J2 P" y: j# Q, ?14$ F4 h( S% e; T# V+ d9 q2 f5 h' }
    151 P( }$ T( v7 }
    16, u5 l+ [7 n+ @, u9 ?5 d3 Q' u- ?
    179 S6 m0 x. X5 D; ?% `1 r( ~
    18
    ; [9 K. p7 W' D: P( Y# Y19
    ) V7 Q& i/ I0 c+ @20
    # Z4 E( ?! ~4 v0 V* J21, Y% `5 M: P* D2 a6 u
    227 a' d+ q2 [+ N. J9 L# J" O7 D
    23
    + I7 s, q. ^0 W. E24
    . y" n+ |9 i2 H6 B* J: ]2 y25
    1 B+ H0 E" j) \) X; ]0 V2 u261 R- K! G" p: ?+ L' `7 o" \
    27
    ; s# u- U6 C1 Y7 E; \6 T283 l4 Z7 C; J, r
    294 ]0 x; l( _3 [% K0 Q' s6 Z+ c
    30  n: l  a, F# j: h: H* K
    310 g8 N% G: b7 j. z8 a; I% c9 m
    32
    ' c( C( i& S( M9 W1 @$ y33
    4 p& A9 ?$ S) X/ S7 S344 O+ V4 T3 p3 Q7 A
    35
    + I& e: P- A  R& n& C36* D7 a' ?( y- @, W
    37$ {# }8 @- @; s( U9 }
    38
    5 _* Q) Y% X; e7 a) _8 I9 a39- G) Z9 ~) m* m7 C: v
    40  E% F  ?- e/ C# Y. n
    41
    - w5 B* p9 \$ v/ I' H+ R& @8 O42/ s- y. U$ c2 w3 l: r& E
    43
    ) d0 k/ V; I2 N" t1 ]446 t  U6 ~! \; O. L4 [' P0 h
    9.3 区间类别
    " J2 Q3 i+ Z- Q  ]4 Y5 e' }9.3.1 利用cut和qcut进行区间构造
    ' k  u) M! i9 I+ s  区间是一种特殊的类别,在实际数据分析中,区间序列往往是通过cut和qcut方法进行构造的,这两个函数能够把原序列的数值特征进行装箱,即用区间位置来代替原来的具体数值。
    & _: Q" @6 U( Z' Z- Y
    8 L) m. \7 i, s; I& j2 ]' mcut函数常用参数有:- E, J$ o5 `% l. c8 |
    bins:最重要的参数。
    , s: n1 T% o4 o如果传入整数n,则表示把整个传入数组按照最大和最小值等间距地分为n段。默认right=True,即区间是左开右闭,需要在调整时把最小值包含进去。(在pandas中的解决方案是在值最小的区间左端点再减去0.001*(max-min)。)
    ' |0 k% A3 z" f$ L; _: u4 \/ W也可以传入列表,表示按指定区间分割点分割。
    4 g* [" R, W. h' ~  如果对序列[1,2]划分为2个箱子时,第一个箱子的范围(0.999,1.5],第二个箱子的范围是(1.5,2]。
    0 `3 |7 K' T: |8 ^( X0 q; {  如果需要指定区间为左闭右开,需要把right参数设置为False,相应的区间调整方法是在值最大的区间右端点再加上0.001*(max-min)。$ D# j7 O( ~* i7 ]/ `

    . s4 V; r$ B& `; f; D8 P  ys = pd.Series([1,2])* z" T. D1 M% g5 x% }4 N
    # bin传入整数
    7 y7 Q0 t8 ^! R, L) Z
    ' Q) x1 D3 o9 b5 u' i- ~pd.cut(s, bins=2)7 f( t9 {* S. r
    Out[39]: 7 N1 F% j: D* q4 L% ~0 p' w
    0    (0.999, 1.5]$ H/ e  _+ s5 r: Y! w
    1      (1.5, 2.0]/ g3 w  g4 b+ a: H8 c' _
    dtype: category
    8 p7 j# k1 D6 w! E. P: qCategories (2, interval[float64]): [(0.999, 1.5] < (1.5, 2.0]]; S/ @4 ^8 g$ Z! {2 [

    . U% _, L( z0 e2 t) h! ~pd.cut(s, bins=2, right=False)( X+ A8 i" E) V# w1 Z
    Out[40]:
    " F6 @. ~$ ]) H/ @0 X/ S. x1 t0      [1.0, 1.5)
    7 p9 g0 L- \7 r/ O$ }1    [1.5, 2.001)
    * V" [5 r: i1 m; q, bdtype: category
    8 m+ v6 n7 P- ?3 _Categories (2, interval[float64]): [[1.0, 1.5) < [1.5, 2.001)]
    " [1 y6 I  s+ ]+ X' C& Z2 A  i: P, T5 H" W$ `  P2 X( S& x
    5 n* ]1 s5 C! h
    # bin传入分割点列表(使用`np.infty`可以表示无穷大):2 A/ r, |7 `# s7 O  p) d
    pd.cut(s, bins=[-np.infty, 1.2, 1.8, 2.2, np.infty])  ~# e) u/ A, v* i4 q1 q/ N
    Out[41]:
    . P/ X4 k( j: D8 Q0    (-inf, 1.2]
    5 ]7 d% M  @' I/ T1 Q, p$ O1     (1.8, 2.2]4 m3 ~; f4 Y* B# e/ Q5 Q3 }" }
    dtype: category
    " h- h: D7 r" ?) T0 N/ ECategories (4, interval[float64]): [(-inf, 1.2] < (1.2, 1.8] < (1.8, 2.2] < (2.2, inf]]& K: k1 R6 j. o1 X- g

    " H3 v- n! q& l& N' t* M18 k: J8 J: g2 i4 [" `9 y
    2- L! `' S* ~% y
    36 X, D$ |! S& [' k
    4* Y* C4 [+ Z, M2 g  r$ H6 M
    5
    ( D+ n5 T8 N; W! t1 ]0 s. F6
    , D: f# A0 F& {5 M7  c9 q0 Y4 \7 P: y* N+ E, m0 `
    8) b; \: R9 E- A, X9 x
    9
    9 v- V1 Y* ^8 R9 n" S8 Q+ O10
    3 i' x) X& @3 b5 |11
    3 R" k) U( s1 S. ?$ o7 h12
    7 O4 ~; k! l' E6 h) \0 K! J9 j136 f6 I  @8 M5 E# _
    14* i, \8 t7 {0 v  e+ n
    15( s5 o5 ?/ A+ X. O
    16# T3 w/ f$ h# P) y& ?
    17
    + K4 H- x; m+ d% u# x, V$ a+ N18
    , y  H# I% S' A1 t" j' y; [19! E: y! V# _; `+ l3 L
    20
    # n0 ?1 [' O0 p) u2 f21+ X* A2 {. V, h3 v# R
    22/ I* t% d. y; o/ J
    23
    8 G% T3 f% Z0 {8 H# r24
    . [: j0 O2 V6 s& Z0 y3 A' r, y25
    , k. H  I- L! |' H9 h# O1 ^labels:区间的名字$ A1 ?3 E. c7 }$ m3 P
    retbins:是否返回分割点(默认不返回)
    ; G* b3 n+ q* \! @7 _默认retbins=Flase时,返回每个元素所属区间的列表" X! L1 z1 D- X; c
    retbins=True时,返回的是元组,两个元素分别是元素所属区间和分割点。所属区间可再次用索引取值
    3 F3 _6 D; P! @/ M5 m& K2 ~+ E* L1 w% A6 f, k; \1 J$ J
    s = df.Weight, |% d( R5 w/ h
    res = pd.cut(s, bins=3, labels=['small', 'mid','big'],retbins=True)
    & `( k! E0 R) h+ ^; i0 [, W% @( sres[0][:2]# b2 a4 ^; X5 k' S

    / C/ I  O/ z; E1 |Out[44]: " j4 I2 `/ ~: k( a4 k
    0    small
    4 q5 i% L4 F" O! N1      big  l, q, M9 T: C* a- e7 q9 F
    dtype: category, m' }7 M, _; _: D3 |; N
    Categories (2, object): ['small' < 'big']) ~* C4 d# P* j/ `& x' Q* i

    0 f. `7 j4 ?$ ?0 Ores[1] # 该元素为返回的分割点
    1 F, M& ?; B  U: _! k, @Out[45]: array([0.999, 1.5  , 2.   ])
    % ?' {; m# o- W! l* Q1
    , `  T- T: `6 F' b* A, P2* a0 T7 q* T2 F  I1 Z6 t- L: [% h; V
    3
    0 ]0 c+ O6 c" u& ^7 G9 b1 i4. Q6 R" R, V; _
    5
    % r# ^0 p  L. W; d9 O6 G* F& O6
    , J; K9 |/ U! g7
    ! O! ~, ], O% {  p9 V82 V+ k6 t' |8 y9 N& k1 ~
    9
    ; X/ `: n4 Y, q6 e109 d# f' i1 T5 y7 Y+ o
    11! r* z8 d& t5 M) L6 g! y
    12" C7 d/ L$ u2 w# d7 w
    qcut函数。其用法cut几乎没有差别,只是把bins参数变成q参数(quantile)。$ t( ~7 ?7 G2 [. D$ p$ V: ?* @
    q为整数n时,指按照n等分位数把数据分箱9 h4 w0 O5 A/ `  x! x" X/ r. W
    q为浮点列表时,表示相应的分位数分割点。  G' E, a  t) Q- p) |0 [
    s = df.Weight
    % G1 H7 N/ t/ d$ x" L1 p7 V+ p/ A0 g: R0 Z9 B" y, G% _
    pd.qcut(s, q=3).head()
    8 B$ Q$ \- e- ~: s+ t4 z0 o& P. [Out[47]:
    8 ]; l9 M/ _9 ?9 P* s2 O) `0    (33.999, 48.0]
    9 J) g/ K. t3 _5 N: V# @% h; I8 _5 C1      (55.0, 89.0]
    - a* @# W8 s, L% O2      (55.0, 89.0]
    & ~7 L& ~- y: N: {. h1 z) y& X7 O3    (33.999, 48.0]
    - C* W" J6 w& ]; `4      (55.0, 89.0]3 P4 ^; T/ |- c. a
    Name: Weight, dtype: category
    5 u* K1 N& B! D) F% YCategories (3, interval[float64]): [(33.999, 48.0] < (48.0, 55.0] < (55.0, 89.0]]
    ' u5 d" ^5 \' u+ e& a' a9 `
    # M5 w; y# a9 D. bpd.qcut(s, q=[0,0.2,0.8,1]).head()8 b( H8 D/ I$ `
    Out[48]:
    * {. y1 [% j; v5 |3 t- l0      (44.0, 69.4]
    ) m/ b1 c' c' q5 o- W$ U( Z* g5 Q3 f1      (69.4, 89.0], l5 V* P- L) j& N7 I. R# U( Y" S
    2      (69.4, 89.0]
    & [( X  M# O+ f- p3 D3    (33.999, 44.0]
    9 T" }( R2 k, E) N, Z4      (69.4, 89.0]$ b' k" Z, k1 m2 F' i3 _3 m
    Name: Weight, dtype: category; y2 ~% q4 S4 l+ m, e, }
    Categories (3, interval[float64]): [(33.999, 44.0] < (44.0, 69.4] < (69.4, 89.0]]
    8 y/ j+ D9 m( b, t) Z* w5 z/ U" \8 G1 C8 a
    1
    1 d- p. @! s* [0 ]2
    * Y' [4 c2 F% X. I% y; o4 K9 n6 {3
    7 d) N0 z/ R; ]9 ~1 d  o4
    . O" q$ g% s" @: `; q57 M. Y, P6 U: A& V/ m. Y5 C
    6# W  ]8 I! O" x/ X- p4 B
    7! j1 S6 O" r5 ]% x
    8/ F) b. ~+ D" F& D$ ~
    9/ ~/ L) X4 J4 X( i6 ~7 C
    10  ~) ~8 {" T( u1 p0 d, R: j: `
    11
    " S! T$ Y/ x2 v% E124 A/ h" C! B4 l, C/ u& }3 `" G
    13$ N+ U, u8 I, x8 ~8 j' b
    14
    7 T  o" a# U/ f. ~+ Y! S15
    , s/ _! g- {8 e6 q  V16& Z+ t& T, \: X% v2 s5 B
    17( K7 H0 O1 f$ K8 p* ^" R0 K
    18* D) l- a3 i8 W
    19
    - q3 r+ v: r; {. C! d$ ~/ e20
    " r0 o( T' d; E, q+ N/ x8 X21# Y1 K+ P9 M2 r% x
    9.3.2 一般区间的构造, r; ?0 v: B6 P+ l. ~  [
      pandas的单个区间用Interval表示,对于某一个具体的区间而言,其具备三个要素,即左端点、右端点和端点的开闭状态。
    0 L+ Y7 V% S& D5 K* N- G
    2 @& U0 ?( O- ]% x/ z0 F2 I开闭状态:包含四种,即right(左开右闭), left(左闭右开), both(两边都闭), neither(两边都开)。
    - ~1 N  t% f# ~' o7 Emy_interval = pd.Interval(0, 1, 'right')) f$ _6 J& p  D

    0 R* c) w8 q7 ^# {$ T9 n9 {, Rmy_interval" m" o/ r& A; k, K1 Z7 a8 Z. E
    Out[50]: Interval(0, 1, closed='right'): Y4 T; X9 Y# F; \
    1
    , Y- n& V$ u, G3 i9 w7 V3 z2
    " P9 d" R3 i8 R/ b* U3
    8 v7 a5 Y1 T1 I" R5 H: s" Y4% u' B2 B  u3 m3 `# A
    区间属性:包含left,mid,right,length,closed,,分别表示左中右端点、长度和开闭状态。
    / h3 b! }4 ~7 f/ H+ t! q7 m使用in可以判断元素是否属于区间4 b: b1 c( F; d$ A3 x0 I
    用overlaps可以判断两个区间是否有交集:
    . N% Y6 o- ]& P; K0.5 in my_interval
    9 G# U) I" B6 @9 U6 r7 e4 q3 e) a6 ^
    - ~# Y* x( _# f$ c; KTrue  t3 L6 n! Y9 t. `( ?
    10 D- ]& b8 l# a' f5 R4 O
    2
    & j/ ~2 t8 ^, P( |  [8 ^! L3
    8 }9 T/ Q/ ?% m9 omy_interval_2 = pd.Interval(0.5, 1.5, 'left')
    * K4 E% L( ?! F2 D7 qmy_interval.overlaps(my_interval_2)
    ) P7 S( a- Q) q2 M7 l5 N  I9 s
    % ~$ m3 k: r: m1 W* R8 |3 m( UTrue% w% ?9 y  P! W( u+ y9 v# r: L
    1
    ; g: \% P) X7 e6 I( q: `2
    5 q0 B/ }3 K3 D/ H35 L  U5 ~% C- a, c1 h1 w
    4: L4 G+ |: ]0 G
      pd.IntervalIndex对象有四类方法生成,分别是from_breaks, from_arrays, from_tuples, interval_range,它们分别应用于不同的情况:
    5 ]& E1 [! s4 @3 T* @/ f: S7 Z; k3 `; w6 q1 w) T
    from_breaks:类似于cut或qcut函数,只不过后两个是通过计算得到的分割点,而前者是直接传入自定义的分割点:/ ]+ N; B# x) G: N" K/ \, v2 |) ~
    pd.IntervalIndex.from_breaks([1,3,6,10], closed='both')
    2 E$ I- G" x+ s# n3 ]/ U7 f' x9 U
    IntervalIndex([[1, 3], [3, 6], [6, 10]],5 V) Q" `) }9 l) w# H9 B$ ~
                   closed='both',
    % E3 h$ l( }3 e  n' F% k; d" J, O& P4 t               dtype='interval[int64]')$ D" v& l  f2 K) W' F
    1# J. b! U; \& e
    2
    - s7 l0 i5 v5 n" E3, ^. K" _9 b! d# |0 @/ s5 r* q
    47 ~. F1 V+ K+ U+ k% R
    5
    ' ~$ M. W& w5 R1 V( B5 a2 L. X8 u/ jfrom_arrays:分别传入左端点和右端点的列表,适用于有交集并且知道起点和终点的情况:; }) F5 f1 o- k+ q9 y
    pd.IntervalIndex.from_arrays(left = [1,3,6,10], right = [5,4,9,11], closed = 'neither'), b4 g2 ^' a5 e; X+ n! F% v
    . U2 z2 p5 _9 e3 I0 a; H
    IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],: Q- P; r) `7 ?
                      closed='neither',
    ( g; H- I6 _( p* [* J5 }9 u+ k# H                  dtype='interval[int64]')  p' s6 g. {3 g
    1/ a- n* W$ P+ ?! W# H: W
    22 Q* j  ~( p  \
    3- U- o7 e* s1 G9 P  P, n
    4+ m" S1 l7 [: }9 T2 m) Y
    5
    ' M9 V( c8 \# nfrom_tuples:传入起点和终点元组构成的列表:' U$ p* @  q) c: l
    pd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)], closed='neither')" }: l! ~+ n' B2 l3 {2 s7 B

    ! c1 `( E/ E0 [- z4 aIntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],
    " v* L2 _! N! s- g; v" k3 a              closed='neither',
    / I: O0 Z  P* M: y* p+ e- Y/ \  Q              dtype='interval[int64]')% V- M, W# T- W' D0 a& ]
    1
    9 k  `! {. t0 G) \$ u# V24 b. Z. o# k9 @8 G- G- s* i
    3
    . |& G1 `% g# ]# F1 L4/ y9 w% N$ u- ^" ^3 s9 O
    5
    ! j/ r) k6 w3 j0 ointerval_range:生成等差区间。其参数有四个:start, end, periods, freq。分别表示等差区间的起点、终点、区间个数和区间长度。其中三个量确定的情况下,剩下一个量就确定了,从而就能构造出相应的区间:
    1 Z% W7 [3 |7 W1 g% Y( rpd.interval_range(start=1,end=5,periods=8) # 启起点终点和区间个数, ^$ ^& Y- d1 p) \
    Out[57]:
    6 a' r9 u% H* N$ l- h* 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* Y! J, C2 E  d! A2 d* K
                  closed='right',
    ) \; `7 W$ i5 a/ Y/ i# s1 P; k" j- H2 v              dtype='interval[float64]')$ E2 d/ g* `/ X3 l7 E

    4 }, R0 {2 |* s" X8 j  ~pd.interval_range(end=5,periods=8,freq=0.5) # 启起点终点和区间长度
    1 B  [' @, ]4 ]; D3 Q- b$ eOut[58]:
    ! Z' a' C0 v8 {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]],. V- h1 [- B2 v, H* ~0 F, u8 @
                  closed='right'," a0 r% V7 r& \  T* {
                  dtype='interval[float64]')$ F0 D: V% y/ G0 D
    1  _* D) b/ z  C6 m
    2
    * E2 L# ?! A% l5 F5 n2 t4 J3
    6 E; V5 t6 o9 j( t. W/ [4
    9 U& V) a( U) I" g0 D( a$ S$ W5
    * i7 D# b3 a1 {+ @6
    7 {' P' Q' d7 {/ ?( U: M+ ~; y7
    $ l5 z  Q9 P# V. x# g# i8
    6 Y6 E8 C0 X( M/ D- F8 r90 F2 O6 M5 F1 Y0 s0 T/ |: I$ b$ G
    104 ?( |( F* i; P( E6 U: k' i8 Y
    11: Y" Z9 ]3 j0 B
    【练一练】, r, H" I& T% S! f9 q4 ~8 `
      无论是interval_range还是下一章时间序列中的date_range都是给定了等差序列中四要素中的三个,从而确定整个序列。请回顾等差数列中的首项、末项、项数和公差的联系,写出interval_range中四个参数之间的恒等关系。
    3 H" B3 ?. ~  A: I  F) E# t+ V: ?) D% ]
      除此之外,如果直接使用pd.IntervalIndex([...], closed=...),把Interval类型的列表组成传入其中转为区间索引,那么所有的区间会被强制转为指定的closed类型,因为pd.IntervalIndex只允许存放同一种开闭区间的Interval对象。
    : g3 Z+ m$ v0 Z9 x+ I" ~$ {# q. {! R# ~) O* ~0 T/ D
    my_interval4 d# l: ^. y  o4 O
    Out[59]: Interval(0, 1, closed='right')
    # I3 E3 x. p& g$ d6 H
    % u9 l/ U  c/ L1 z7 rmy_interval_2
    # ?) f4 T7 {% s4 r' j4 |Out[60]: Interval(0.5, 1.5, closed='left')% [. ?4 q' p7 u- d9 T. x7 v+ w7 r
    " [* g' _& L9 ?, ]0 g
    pd.IntervalIndex([my_interval, my_interval_2], closed='left')
    & }: f  g- Q) }7 m7 N) l3 e5 {Out[61]:
    0 ]- U, x! y, y* ^IntervalIndex([[0.0, 1.0), [0.5, 1.5)],' S2 B' i( ]8 V1 e' {  Y
                  closed='left',
    5 a2 L/ p" Z+ B* L' s              dtype='interval[float64]')8 y& Z% c0 ?8 k9 V, X
    18 i; }7 o; _, i) i' b, p+ C
    2
    2 x; \& I9 Y" [4 n/ I3
    8 `0 U% b/ }2 v) y# V+ h4+ s2 R: H$ d0 y
    51 X* m# H' `0 r/ T
    6! U* L5 m, V9 K6 ^2 S& k
    7
    7 g& g& v- c# K& c86 O/ c$ `# g! D
    93 u  S* d" c0 y0 Z7 m5 R1 N
    10
    8 U% ~+ \; X8 M- z! v& a9 X6 ?! u11
    4 G7 G8 W6 {+ W5 Q, ~9.3.3 区间的属性与方法% z7 c# p0 z% b/ I- X9 x" U: `
      IntervalIndex上也定义了一些有用的属性和方法。同时,如果想要具体利用cut或者qcut的结果进行分析,那么需要先将其转为该种索引类型:
    1 B0 |  S# _3 M8 Z. ?( Y( K4 w0 x6 m: M% S' Q
    s=df.Weight% L' n- D0 B/ D" D' ?% M
    id_interval = pd.IntervalIndex(pd.cut(s, 3)) # 返回的是每个元素所属区间,用具体数值(x,y]表示
    , p8 q$ ^7 {3 N( pid_interval[:3]# s% `8 ~1 [4 ^4 h# P/ p9 u1 H+ O' K
    ( y# I2 D& X. S8 \0 g
    IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0]],
    , ^, a* J- n4 B. d                 closed='right',
    ; q) r6 O) R1 A$ k                 name='Weight',8 b7 d4 ~" N' K
                     dtype='interval[float64]')' O7 ?$ N" T; G7 R; e& m! i) C
    1, Q% Q, N& z8 O4 e7 E  c
    2$ B) K8 N6 P# p+ S. f: L3 J
    38 n8 M1 N# b/ J3 m1 L) ^7 B
    4
    ) [. L. _+ @4 c) m0 e5
    8 ]2 O8 }8 L' k4 {3 {+ o9 R  w6$ V+ ~0 y8 w3 N& J$ C: S; I
    7
    ; e4 z3 ?  Q( [8
    9 x: J. L- y/ \) D1 l! w与单个Interval类型相似,IntervalIndex有若干常用属性:left, right, mid, length,分别表示左右端点、两 点均值和区间长度。5 E  M* V: G5 ~* Z% Z+ [, G
    id_demo = id_interval[:5] # 选出前5个展示( m$ V6 e4 T3 O
    ' s' H4 H' L1 F  N5 t
    id_demo: }& H7 p6 p2 @. q
    Out[64]:
    9 }( l! L! x: Q$ \: r" b( d& HIntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0], (33.945, 52.333], (70.667, 89.0]],
    + l6 `; R) g8 r) V9 l              closed='right',
    " d* V; R7 J3 i8 F! N7 ]1 Z              name='Weight',
    ' q! y% h! S- ]2 S              dtype='interval[float64]')
    5 X& Z  Q8 `4 P1 D8 j% }, [, }! j
    id_demo.left # 获取这五个区间的左端点
    " ~2 l  t. m9 J8 G( lOut[65]: Float64Index([33.945, 52.333, 70.667, 33.945, 70.667], dtype='float64'), [4 L3 l$ A6 P% E# ~! g

    + A: `0 @5 t- D/ |; {id_demo.right # 获取这五个区间的右端点
    1 A" c. I( `* bOut[66]: Float64Index([52.333, 70.667, 89.0, 52.333, 89.0], dtype='float64')
    , l+ n) M/ A' ?, `# P% E0 f6 L6 F* x+ u
    * ~3 d9 p1 ~, Pid_demo.mid" K. g$ E! X& S1 ?& o% C
    Out[67]: Float64Index([43.138999999999996, 61.5, 79.8335, 43.138999999999996, 79.8335], dtype='float64'): |6 n9 J1 m) v* m, v

    * F$ o/ ~) _- z, {id_demo.length
    % c$ n0 q2 P6 MOut[68]:
    ; v2 u9 ^: Z3 N3 Q/ p+ e8 RFloat64Index([18.387999999999998, 18.334000000000003, 18.333,
    ) `5 f6 a2 K* C              18.387999999999998, 18.333],+ l6 C; ?( q1 a0 R+ f, i# t8 }6 A
                 dtype='float64')
    " x. E+ C2 g$ w) }
    ' F  [2 E2 p4 r1
    : Y# f# n/ I" R; w7 G. G. s2 P2) m4 E: j' ?' u* T7 {, f: ~6 F
    3
    / V& e$ Z2 T" z44 c! s9 W/ J6 [1 U0 x
    5( c3 u$ D* O% m2 P! N
    6
    % U0 A6 u" p! p& i$ x6 s1 E5 q70 H2 m* I1 R4 i, i8 Y2 }
    82 ]) v  Y" j3 m! c* u/ \
    91 {% l$ b" c; F! t* B* w$ H
    10
    ) ]: P" i; u5 j  y5 Y; k3 _11
    / Q; S: }5 L' b12; I! ]2 S9 @# X/ s7 v2 f
    13
    0 b. z9 k( T; [/ z# }14
    ) q8 L8 h5 \* I9 [- n# R154 `4 R6 m* E% U  e  x7 a  ^
    16
    ' u1 h6 J2 {! ~2 G/ Q178 T0 I. F  z8 j; ?: _
    18
    9 b) i5 ], j) ^( {3 ^; e19( y6 `1 R5 M8 F0 f2 M/ j. c
    20
    8 b. D5 @& O" |) o7 c& j; G21
    / m1 p1 N% u. S: a22" H8 ]& [: i4 E1 K# E0 a1 H) N
    23& x! u0 q' \, N
    IntervalIndex还有两个常用方法:
    ) ^# E* i! S* m" pcontains:逐个判断每个区间是否包含某元素) |4 g% C7 O. I. U5 M7 W
    overlaps:是否和一个pd.Interval对象有交集。8 `) b- L/ A% G7 W1 `: c( C+ U
    id_demo.contains(50)9 F, Y3 P3 B. L
    Out[69]: array([ True, False, False,  True, False])
    & v* x: \/ T" T! H4 ]; }
    * B7 j2 r& a  `0 F8 K2 K  s/ S6 xid_demo.overlaps(pd.Interval(40,60))
    * T0 F6 e2 P$ ]  e7 f8 x$ NOut[70]: array([ True,  True, False,  True, False])* f. _; Y( U2 b
    1
    1 \. W- \* M! h2 o7 u9 y2# Q! m+ \& Z  x" y" b( ]
    3
    $ S2 i. a2 @2 T1 _6 E( [, o41 a/ h0 g3 T- e0 A- g; `9 q
    5! J) _- h' {5 C: B- H* t! P
    9.4 练习
    ( s6 }& @: l4 a+ U$ j$ KEx1: 统计未出现的类别5 j6 n2 \; S* M* }
      在第五章中介绍了crosstab函数,在默认参数下它能够对两个列的组合出现的频数进行统计汇总:% Y& M# W1 I# ~# C: m4 c1 D8 k

    : Q2 B8 E6 k8 ?0 [2 B+ M( Ddf = pd.DataFrame({'A':['a','b','c','a'], 'B':['cat','cat','dog','cat']})0 d& P! G+ r: s  z- U3 G
    pd.crosstab(df.A, df.B)
    4 {7 {/ ~$ w0 w# V( P1 w" n$ b' {( _$ Y9 o# ]0 C
    Out[72]: + \, @& U- y2 j- G+ U$ |1 y8 f
    B  cat  dog
    5 w, t$ C" @, k6 ~, h8 HA          4 P7 {# r9 m/ x
    a    2    0
      @/ Q" q; C8 a/ [b    1    0/ w% w- a9 n  e& v4 G% c
    c    0    1
    ( ]! `  `. {( r. E- r+ t1' J4 ]: y# v  b8 Y# ^# c
    29 \7 j9 D: R8 r% A3 A7 t( o
    3" z& H8 l' ]/ d3 x; u  @& z
    4$ X$ w. x) }, _8 \4 S2 m; r' G
    50 W. X+ n6 w' b( E, l. x8 W% m
    6
    , X+ Z! q" q- N. u" Q5 Y+ M7% u  S1 _6 ]& }' D/ u, h/ q
    81 [7 P0 `6 n% C1 ~3 e
    9  ~0 [+ ]6 Y( N# e- y; r
      但事实上有些列存储的是分类变量,列中并不一定包含所有的类别,此时如果想要对这些未出现的类别在crosstab结果中也进行汇总,则可以指定dropna参数为False:
    6 Z" L; o: u) C+ W) K* t; A- U! d6 Z$ z
    df.B = df.B.astype('category').cat.add_categories('sheep')3 ^: a7 E/ R# K5 S8 t8 g
    pd.crosstab(df.A, df.B, dropna=False)2 o  @8 [/ Q# N/ I/ y& @& B
    % N0 x" B$ T+ ?
    Out[74]:
    % {; z4 O" w1 w1 h) N$ qB  cat  dog  sheep
    ) z4 ?  x8 F& L/ iA                 : C7 h) [" e) G# }0 g. j7 R$ q5 S
    a    2    0      01 M; @3 g3 A) c* t) \
    b    1    0      0
    8 o/ m6 Y* ?$ W! t6 }0 Sc    0    1      0$ l2 k7 r2 h5 T& e5 R* e
    1
    / j. i  M3 v! q2
    5 R+ X, H- U1 L6 h3 c4 {+ ?) x7 r" X8 G( ]3
    - @. P- g# [- Z3 P4
    0 Y* \& d) U( |8 k9 k: c5
    $ k; g- D, a% A63 f; x" l' P, ]+ k! |
    7* U+ o) a. p' _" R$ o2 T
    81 B, N/ @: l( x- X# [
    9, n" @- }! i6 m+ o6 E% ?, ]
    请实现一个带有dropna参数的my_crosstab函数来完成上面的功能。' `7 P, n& z5 A1 s) I, S5 j* h3 Z

    / {1 X% o1 w% A+ @Ex2: 钻石数据集) y; x: z$ S6 y- j4 p
      现有一份关于钻石的数据集,其中carat, cut, clarity, price分别表示克拉重量、切割质量、纯净度和价格,样例如下:( G. ~$ ]7 F# D  n9 Q( d: x) U
    + s* E7 }# `2 n
    df = pd.read_csv('../data/diamonds.csv') 6 Z0 \7 o- p$ z
    df.head(3)
    % P/ H* o0 W4 S! L4 d# D) @7 Q0 n( z2 F, `. ^  J2 H
    Out[76]: 7 ~' y7 [% D0 ^+ G
       carat      cut    clarity  price
    ) {! m+ w9 k* T1 ~+ ~, g0   0.23     Ideal     SI2     326
    & R2 }; Q6 z: N* Q0 [; U8 E1   0.21    Premium    SI1     326+ s+ C3 x7 K, D, f
    2   0.23     Good      VS1     327
    ' _6 f: E& b4 x; M1 R: B7 d1. A: p$ N% x0 U# |
    2
    + j* G6 f1 `; t5 `4 K/ s  k37 k, B# N: v* p3 I( S0 a+ i
    42 C+ |& _' D: `% t
    5
    ( x$ A# k) e1 b1 |( @4 P8 I65 k& Z7 p( V' S! g& x: A2 B" ]. c6 z% H
    7
    , |0 @( _* k" d) q  ]' w8
    6 d5 o) t  E2 g) B分别对df.cut在object类型和category类型下使用nunique函数,并比较它们的性能。0 O# z" z5 m6 u# C( K
    钻石的切割质量可以分为五个等级,由次到好分别是Fair, Good, Very Good, Premium, Ideal,纯净度有八个等级,由次到好分别是I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF,请对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。$ ^9 q% F# p1 v% A
    分别采用两种不同的方法,把cut, clarity这两列按照由好到次的顺序,映射到从0到n-1的整数,其中n表示类别的个数。3 u' D, O7 b; g6 u% H: x( h
    对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。
      H0 S; T3 _& [3 U( ?2 m第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
    5 m1 d7 W/ f' _# b2 |& g对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。
    & {, [/ `# {. K: i9 f0 u1 a先看看数据结构:
    * |9 B$ i1 H9 x: m, ?6 w( Q* J7 Z  ^( m" M% m. @1 t1 q% k
    df.info()
    " H7 m4 O9 p0 {! rData columns (total 4 columns):
    " Q3 }. U: S  X; Y" |# r1 b #   Column   Non-Null Count  Dtype  
    - d& \3 [" b. i2 B; d---  ------   --------------  -----  
    7 l( O. z) ?# _. ]" w+ P5 N& } 0   carat    53940 non-null  float64
    4 V9 l" e8 X! ~; o: D 1   cut      53940 non-null  object
    5 `. A- a, F$ t* y+ R0 e 2   clarity  53940 non-null  object
    5 E5 b0 H/ @4 Q) u* J; t 3   price    53940 non-null  int64  
    # R4 w. k% l1 F: Cdtypes: float64(1), int64(1), object(2)3 |7 i3 u" @. f1 k0 {
    1+ P) b; B; E' z
    23 I6 K/ }! J) T
    34 b/ p% s/ W: c& P
    4
    : |4 A% ]  g+ c2 ^3 C5 X5
    # q" L3 h" e6 b6 h- O9 @* T. m6
    5 j# `3 j8 F) ]$ r  e0 F7* [, ]7 y6 d- ~8 b6 U/ z* s7 A' _. \8 Z
    8
    : t2 {* ]; e( t8 D; f' V. O' d9
    ; t" A7 {& H( I比较两种操作的性能
    ! ]) C" |" w' }; S- c%time df.cut.unique()
    # e# K7 r5 D. O
    ' L- \! p1 k& }; c5 Z' J9 cWall time: 5.98 ms( I; x5 j4 r/ K7 Y; @& ?5 `
    array(['Ideal', 'Premium', 'Good', 'Very Good', 'Fair'], dtype=object)( f8 @9 e- W6 Q4 b1 m" F( c
    1
    - G+ m- Q# T, d, {) l! A0 g& z2
    7 k4 Y1 o, w3 n" j6 @$ O3: e3 y' L* R: l, C0 B
    48 Q3 U5 L! ^9 Q! H" o! m; k
    %time df.cut.astype('category').unique()) E+ y' J% @$ b% k. U" l

    ' z. `0 b4 k, U( ?8 I+ z5 oWall time: 8.01 ms  # 转换类型加统计类别,一共8ms
    * f- V+ W7 j' j5 {) V. e9 S6 f['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']  d' A) w4 u5 p$ s. B3 _
    Categories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']6 H/ l9 M9 C' S: u' n- U
    1: g' D1 @8 g$ P3 k
    2+ u: f9 g& J) Y) v: n
    3
    & ^. e, m( r5 [2 r4# s3 P; k: j2 e( S
    5
    7 |5 d6 H# d, Idf.cut=df.cut.astype('category')
    ) {7 `# ^  Y6 j& l" Y%time df.cut.unique() # 类别属性统计,2ms$ x) |4 j# p/ k$ w# m

      `5 A$ c  v: M2 bWall time: 2 ms: A, m! j/ {4 Z! K: `- V. R* B
    ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']  Z6 w7 J: a% L6 @# ?2 z9 f
    Categories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    ! V; {& P5 ^+ S7 q) E& y6 W9 d$ @' Z1! z  z" `# D# W
    27 V8 J; ~0 V9 E! Z* m' D
    33 R8 b& D! S' {5 m- m$ l" \/ g$ d
    4$ J% |, p# B% D- s
    5
    / a& r" x1 N, F7 B8 A) S1 n6: r4 d7 ~  q+ s4 [% M5 T
    对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。
    7 N4 s+ a) Y; J0 |ls_cut=['Fair', 'Good', 'Very Good', 'Premium', 'Ideal']
    1 P, u; Z% D3 N) F& R! Zls_clarity=['I1','SI2', 'SI1', 'VS2', 'VS1', 'VVS2', 'VVS1', 'IF']
    0 p' T6 D. Z0 C$ |" ^df.cut=df.cut.astype('category').cat.reorder_categories(ls_cut,ordered=True)  # 转换后还是得进行替换
    * Z8 f( `1 E+ q: e" `df.clarity=df.clarity.astype('category').cat.reorder_categories(ls_clarity,ordered=True)( |3 }% A  V8 m& R5 }
    4 s# O3 I5 T( u# L$ |) B7 a+ W5 ]
    df.sort_values(['cut','clarity'],ascending=[False,True]).head(3)
    # `; h  H2 j% o) _5 j  T) j
    # S9 B* l( ^) @6 F9 }  {        carat         cut        clarity        price6 m( p) `5 Y% ?
    315        0.96        Ideal          I1        2801$ I) `/ ?$ K" O2 Y+ W
    535        0.96        Ideal          I1        2826! e, K/ H+ t) }
    551        0.97        Ideal          I1        2830) F: _& w' j' S0 a, E! O+ I% G
    1
    " @8 r# z; e+ \# K6 t2
    2 g7 b  |/ Y8 E1 _  v' }( X$ ]3
    $ [9 |! }" b" X& E; Z2 y/ d) L4: a7 r0 d* u& D, n) D
    5, {+ b% ]) P" Z  {: G/ i
    69 R2 Z3 }1 H; V" x1 _9 X/ G4 d/ _
    7- y5 V6 w1 u" d7 {, F
    8
    0 F6 |: a! q4 a% s+ P( z9
    5 U- u0 \5 f; [4 ]8 w10) e( v9 X9 g  i' A
    115 d4 z9 u, j% C" {4 x0 U
    分别采用两种不同的方法,把 cut, clarity 这两列按照 由好到次 的顺序,映射到从0到n-1的整数,其中n表示类别的个数。3 X+ v+ D( W5 }9 l2 ?; D  R& M3 M
    # 第一种是将类别重命名为整数- U" J4 @  |5 z: W3 \9 E
    dict1=dict(zip(ls_cut,[x for x in range (4,-1,-1)]))
    , n- k, |, b* x) k/ Q% bdict2=dict(zip(ls_clarity,[x for x in range (7,-1,-1)]))
    . A4 _, M2 j( I: \. L: g, S5 W8 X+ S. B. ?' }
    df.cut=df.cut.cat.rename_categories(dict1)( |; \; F: s$ \4 V, j
    df.clarity=df.clarity.cat.rename_categories(dict2)8 H% A$ R+ K4 r+ b; Y
    df.head(3)
    1 e# [+ [) g/ T# i* R$ Y( n3 r8 h) ~1 O2 V* ^' d  n
            carat        cut        clarity        price2 N# I, u* _! S7 k& G
    0        0.23        0          6                326) e5 B3 C. w6 P; N# q! i8 j$ \
    1        0.21        1          5                326  q% c. K' m, c3 g/ T5 I
    2        0.23        3          3                327
    % d* d' z! v# H, i1/ \0 X2 r0 J4 f$ O$ T( i" n3 `& W9 _
    2
      \" C) W) Z& Y; g- ?3
    / k( ?2 h! {" x+ `$ u9 J1 X% {9 C. a4
    + B! j$ a- P: ]6 C# q5
    3 M+ ^: w1 i4 [1 P6
    0 g" ?) e6 K) p4 ~7 Z" v7
    ! C; Y. }1 Y8 j  J0 {6 n8
    ( A  V" }$ ?* G9 J/ l2 Q5 A9
    1 I% N/ ?/ T1 J# Q10! Q% Z0 ?0 ?, _: V( ?
    11
    4 X: ?! y- a. P( ?- t' k126 l/ f4 B+ \5 Z1 J
    # 第二种应该是报错object属性,然后直接进行替换
    3 g" b! i% Q0 k9 [7 C! ydf = pd.read_csv('data/diamonds.csv')
    8 O( G* `/ k$ S4 X  {5 q- Sfor i,j in enumerate(ls_cut[::-1]):1 S% u' I4 O0 \6 c: l3 i& `4 C( n
        df.loc[df.cut==j,'cut']=i
    7 p8 R+ N: o* D, j8 c2 ?. n
    7 R, k" b. d% y$ D. B) |! bfor k,l in enumerate(ls_clarity[::-1]):
    2 v$ t7 Q7 k' h* B    df.loc[df.clarity==l,'clarity']=k8 \. |& g1 r- Y3 B" j0 t
    df.head(3)
    3 ^- m4 ~0 M/ j1 B
    & ^) [* _2 G, i! y  w        carat        cut        clarity        price4 `  s( w: s7 u8 j. ?+ q. D
    0        0.23        0          6                326# D* p* @  q/ p) `  r( d
    1        0.21        1          5                326
    , B* g2 V/ v7 `4 B2        0.23        3          3                327
    7 W4 ?. v' i7 ^1
    8 `& r5 [* y! R/ R' z$ S6 I21 X0 A" Q/ K- R. k$ x) I% h
    3
      q6 g( d6 U3 ~0 J6 x* `4
    3 K6 I7 L' s& P# Y/ r  a0 Z5
    , h7 q/ I' O: w6
    + F# Q' f4 L# D" r% w% @5 u. F; @71 U' F, G6 e7 A, c/ `* K% M
    8
    % g/ \# {4 M( t9
    ) p$ a; d; @  [10
    1 c; ?9 t" e/ R9 C11
    2 t& A  F, S3 T6 y$ ?12  W+ g& u, m: z* l9 C9 ^
    137 \- h) ~2 M$ {7 s4 Y6 r. b. r
    对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。" _6 f, y# l( ]6 O8 j
    # retbins=True返回的是元组,第一个才是要的序列,第二个元素是分割点
    0 g7 c$ q4 ~/ f" s/ ^2 xavg=df.price/df.carat
    9 f% ~$ l0 o) X
    5 l2 a! S( f) Adf['price_quantile']=pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],: z/ O" J# Q6 [2 ?/ z
                                  labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]- h- H2 J' H* Z. q$ p
    9 `7 X# A2 `, r: U8 E: f! x4 b
    df['price_list']=pd.cut(avg, bins=[-np.infty,1000, 3500, 5500, 18000,np.infty],
    3 Q/ L+ ~8 b7 Z                              labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]
    8 [- J+ P# ]0 Vdf.head()4 w9 L+ j9 [8 o

    , ^7 E) u. v- N7 _1 B, r+ t- X$ c        carat        cut         clarity        price        price_quantile        price_list# O$ r9 E& B' _, V) s8 B; S  ~! f5 o) m8 Q9 N
    0        0.23        0                6                326                        Very Low                Low
    6 g5 y; X8 \  u1 P6 \1        0.21        1                5                326                        Very Low                Low& d; o: |# |( L& W4 |4 z* V7 J( B
    2        0.23        3                3                327                        Very Low                Low+ }& @3 m. b8 g' g/ b) ^- \
    3        0.29        1                4                334                        Very Low                Low* K) w! Q+ z& c: T7 F4 ~
    4        0.31        3                6                335                        Very Low                Low                                       
    % X$ J, i& K' ^+ @6 _
    1 {& `! R0 z& Q7 p  [# Q19 f7 H- r9 E! L4 x
    2) `  d+ P7 m6 B& t7 y( d3 {- ~' f
    3
    5 ]) z% x, r! K- Z5 g2 X( J4! ^4 m6 t# M0 k4 Q8 k
    50 h. [* C. Q% `3 W
    6
      V" C0 F0 `9 h; T/ h7; W: Q: @; A1 X% H7 I
    8
    # W% j. Z6 ^3 f. L0 Q! D- W& M& I9. s# z6 v) b8 G7 s0 x
    10
    8 x3 K/ d% ]* y* h/ R9 J11* N/ \/ [  O& Q! D$ }% Q0 x
    12
    . y  U' d5 ?* f( `; x4 \' N130 b  Z8 X' y$ `* a) t1 R7 b
    14  y$ E. w; C3 j9 ?! z; S
    15
    2 e( D5 \+ Q9 M; E; N* i16
    $ e* C! B. ?: Z3 W1 g) ~' `! n分割点分别是:' d$ m3 B6 {4 A3 D7 y& K+ c

    # w; L6 z8 a7 Uarray([ 1051.16 , 2295. ,  3073.29,  4031.68, 5456.34, 17828.84])5 Q9 q' `2 a4 @& e
    array([  -inf,   1000.,    3500.,    5500.,   18000.,    inf])
    $ m  E9 j& ]6 r6 c: t1) h, a& u+ d- [. t- ]  i
    2
    * d7 {1 j' C2 c. k第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。/ S  ~& |2 E8 G4 a+ E% P" {  S. c+ ]4 }9 R
    df['price_list'].cat.categories # 原先设定的类别数
    6 o3 l4 ], {* H9 G/ JIndex(['Very Low', 'Low', 'Mid', 'High', 'Very High'], dtype='object')" z& U1 }) g- f
    : V  o; s5 c4 M" B9 A$ ^. l1 S
    df['price_list'].cat.remove_unused_categories().cat.categories  # 移除未出现的类别$ w, ^) ]+ Q" W0 m
    Index(['Low', 'Mid', 'High'], dtype='object')  # 首尾两个类别未出现
    * h( i8 w2 e( z18 r/ `1 u& F* m* H$ ?( B# g5 h
    22 l, @( A& ^7 L" ]& R
    3+ H. `! O- n7 R4 T1 g+ `/ X4 ?, k1 S
    4( X2 G0 i! W8 F2 i
    5( u, @4 T! G9 u" }% P1 N( \3 C
    avg.sort_values() # 可见首尾区间确实是没有的
    5 a0 |% y9 D. O31962     1051.162791
    # G; W+ H' Z, Q( N" E9 n15        1078.1250009 j! X- {$ O4 W( q1 v" o2 ^
    4         1080.645161- @' t+ |% f- E" g9 U
    28285     1109.090909: W  H" c5 ^4 v/ j, x2 G0 ~. ], z
    13        1109.677419
    8 o' O& ]0 B4 A6 P7 j+ J1 H             ...     ! ?4 P$ o# h+ M) P/ |. X0 c3 w
    26998    16764.705882! F! ^% l* j, t
    27457    16928.971963
    9 m3 E  u# x5 ]27226    17077.669903  G# O: g# G  m/ `2 J4 m- }
    27530    17083.177570
    7 H- D# R: O0 k& W' {: O27635    17828.846154
    0 {6 E3 c% n) w/ V. M1
    2 P$ ^  x, o( K4 Y; K29 m! I9 h% t' \% E2 W* n8 I& j
    3
    . o9 I5 l9 y# P6 d0 H4
    : o) B' b8 {+ a7 \# J52 l7 h1 J7 q1 S# `: x: f
    6
    ( A4 \. D4 k7 ?: P5 P4 {9 ~! H7& @( ]* \) h% U& z4 \
    82 R* i5 ?) u5 o8 \* J# l
    93 v$ }' g6 W; R
    10
    : m* M3 _$ m$ v# ~8 p114 d; o" _% r+ X# k' L
    12
    ; ~2 u& O; `3 P$ `2 [对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。
    ) C& X! x& G9 E5 p- M. M: }3 o( g$ U# 分割时区间不能有命名,否则字符串传入错误。+ O( [. J# o( o6 n. d
    id_interval=pd.IntervalIndex(
    - S0 A( ~: I5 J# L* C8 b    pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],retbins=True)[0]9 c; P( Y7 n2 O0 y6 c
                                )
    0 E- }" k  H/ |" [/ `) }id_interval.left5 ]7 l2 C% n( ]5 S" j4 M8 ^
    id_interval.right3 M8 `& F- N# U! D
    id_interval.length                           
    5 k$ F( m& L: C5 }0 Z) j1
    * e$ |5 C- D# l2. Z3 X5 c  x; A: ]0 f
    39 a# \/ i+ F: H7 {5 G& A; \
    4
    . E& ~5 e$ `8 [; v! l+ Z5
    % d$ |- O0 P' C: L' a3 a6
    9 [$ F4 @9 d; l0 t. Z( F3 \7- \5 n& b2 B; h) N# F9 D
    第十章 时序数据
    9 X) {& n0 {, ?1 _5 F6 k6 b9 jimport numpy as np
    6 U2 n: ?# y1 ~2 A6 {* _, C  oimport pandas as pd6 \4 c) s3 p  Z" s  K, r$ k
    1
    / s& U9 }7 V, _; k3 j2  J% r- j9 w5 l3 {# I5 X9 B7 ^# n/ x
    5 i, p' a- r! g; ]" G
      I- q" A& b) |* ^$ _; C% g! D
    10.1 时序中的基本对象
    0 ^6 S% e! a) Y, ~6 h) @; J  时间序列的概念在日常生活中十分常见,但对于一个具体的时序事件而言,可以从多个时间对象的角度来描述。例如2020年9月7日周一早上8点整需要到教室上课,这个课会在当天早上10点结束,其中包含了哪些时间概念?9 _% }4 ?( p* |0 y/ ]4 K

    6 ~' G6 ?. X7 P* u- w7 t会出现时间戳(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 [0 x$ u2 b1 a+ e* ~
    % V" c5 z1 v1 S- ?9 t2 b4 {
    会出现时间差(Time deltas)的概念,即上课需要的时间,两个Timestamp做差就得到了时间差,pandas中利用Timedelta来表示。类似的,一系列的时间差就组成了TimedeltaIndex, 而将它放到Series中后,Series的类型就变为了timedelta64[ns]。- q  B& n) ^' H3 D7 M1 W2 X. |

    0 \" q8 K1 {4 P- w会出现时间段(Time spans)的概念,即在8点到10点这个区间都会持续地在上课,在pandas利用Period来表示。类似的,一系列的时间段就组成了PeriodIndex, 而将它放到Series中后,Series的类型就变为了Period。
    ) R% ?5 n! W/ n4 X! W% }+ N$ ~1 R# r- ~' ?
    会出现日期偏置(Date offsets)的概念,假设你只知道9月的第一个周一早上8点要去上课,但不知道具体的日期,那么就需要一个类型来处理此类需求。再例如,想要知道2020年9月7日后的第30个工作日是哪一天,那么时间差就解决不了你的问题,从而pandas中的DateOffset就出现了。同时,pandas中没有为一列时间偏置专门设计存储类型,理由也很简单,因为需求比较奇怪,一般来说我们只需要对一批时间特征做一个统一的特殊日期偏置。
    # r! D2 p4 p6 z! g! }2 T; [: b& {: `4 |3 t- q( A
      通过这个简单的例子,就能够容易地总结出官方文档中的这个表格:* R. O- d' x3 C' z' E

    0 o+ H  ~$ R& ?( I3 [7 |, y& j概念        单元素类型        数组类型        pandas数据类型4 K4 i4 Q# W) A1 D+ o
    Date times        Timestamp        DatetimeIndex        datetime64[ns]
    & t$ P3 V' V2 }6 c. J4 kTime deltas        Timedelta        TimedeltaIndex        timedelta64[ns]
    # r/ B' B% \. x# _/ n: {* c* ETime spans        Period        PeriodIndex        period[freq]
    ( b; q7 U9 C9 ?" ^Date offsets        DateOffset        None        None
    9 Y4 T  S- Q: I; }- [! T  由于时间段对象Period/PeriodIndex的使用频率并不高,因此将不进行讲解,而只涉及时间戳序列、时间差序列和日期偏置的相关内容。, q2 F: ?' N4 }/ z

    / V2 q9 k, Z- b* R! d# z& X/ C10.2 时间戳
    0 T7 e! K7 V, A) U10.2.1 Timestamp的构造与属性. M1 M" ]% c0 `5 A6 z8 Q! T) ?
    单个时间戳的生成利用pd.Timestamp实现,一般而言的常见日期格式都能被成功地转换:9 i4 z  {1 m- T

    % x. Z' @3 w+ C- M$ {3 }ts = pd.Timestamp('2020/1/1')! y/ P1 s7 F6 n: p; c7 w7 H5 Q
    ) \: L$ @; `1 Z6 Z6 ?: `+ I
    ts
    5 Z3 Y5 f/ |% ?Out[4]: Timestamp('2020-01-01 00:00:00'): Q& ?' h* x% |$ s% R

    5 L, Y3 e2 m3 C2 s. u* t2 ]7 _ts = pd.Timestamp('2020-1-1 08:10:30')
    ' V/ y) ]! ^+ K6 k/ Y" G9 [+ C1 s" y0 B" Y
    ts
    * U. x  |5 L3 ?3 a* F. sOut[6]: Timestamp('2020-01-01 08:10:30')
    3 X: B- E* t# q# e+ X1; [+ Z; O. x+ a. M+ J3 Z
    24 p0 y; a) W2 |$ f
    3
    7 v0 h7 u7 G( z" i% c40 r, F# q, D/ B& j9 l
    5
    ) [. J" ^: h" Q. S* y6
    ( S4 [7 F' n; J/ p) [' \4 v7
    ' _3 B3 F! T$ V, P/ j8
    - s& e. o, W) S# C, ~: S9( Y. `, Z" ~( h" d# W3 f
    通过year, month, day, hour, min, second可以获取具体的数值:
    + T* ?2 l8 n4 R
    # [# @0 `) H. I  b0 `# ]ts.year+ |/ W; m2 t+ H
    Out[7]: 2020
    0 L$ l  v* O) T3 g2 w/ r0 p2 l$ y9 P: H. f+ }& b8 M
    ts.month4 A, A1 f9 R& V) O
    Out[8]: 1
    / _' l/ g& g# \8 u: ?  e; }/ }7 \2 j3 W- @
    ts.day
    / b- }/ J7 H" m! NOut[9]: 17 Y# o( @3 h+ ~5 [( m

    0 [+ a, a9 I1 tts.hour
    , c5 l7 b4 _, ]" s, IOut[10]: 8/ K. B( R* K  A! g1 J4 a# n
    ' r5 r( ~" I3 ~  `( J8 L! N
    ts.minute* ~: }2 Y$ x- \' K9 Y3 x) \/ l
    Out[11]: 10
    % b. f* O+ C. Q& a. M( T' e- R, G2 e9 x! d
    ts.second
    ) J( J8 R+ w$ j: n8 N% \3 aOut[12]: 30$ t) X# {+ |, u$ M+ G  l2 m

    & C$ `5 q* h' C; k" b/ g2 v  l+ y1
    ' E& A" [# `9 c; D* D2
    # R0 x$ [7 \" X" t30 l# F, ~' W8 N
    4$ T6 v2 H8 U  i5 t" ^$ `! H* `6 A$ h
    5! Z, v* Y1 @# m! @& r
    69 y9 Y" p# J1 T) B3 O( E2 t" p% B
    7
    ) |: H8 d- y. o. m' k8, s. u1 F# B+ y1 a
    9: y6 w- }0 P4 H8 Q+ V# \; b+ f
    10
    ) w( k' d5 z. W2 ]- Q+ V# A11* U" c5 @% J1 R% r1 x, j/ P
    12
    9 ]3 u5 U5 \" U+ J/ t13
      A6 _, T. _  \. B4 ~& }, L' y14( Q& T8 l5 O, B4 U; q
    15, u4 L3 K! l9 g) ~
    16: G+ n2 [4 J  b
    17* m- e* |0 I7 X+ L$ n; _( Y" D
    # 获取当前时间$ U& w. w, Q7 P7 z' b# n( e- j  c
    now=pd.Timestamp.now()
    $ J3 ?0 K0 j3 z- \7 s: E18 J0 I5 d  a/ W  f6 ~7 ~% `& v6 T
    2' [9 C7 S$ ^" y! r5 a
    在pandas中,时间戳的最小精度为纳秒ns,由于使用了64位存储,可以表示的时间范围大约可以如下计算:
    ) \5 Z1 r7 G' G6 [2 VT 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)7 R9 e: }% Q: ^
    TimeRange= 3 w  U9 e' M  D
    10 # z# s$ C" a  O3 K. Q
    9
    4 Y% c- t& A3 [ ×60×60×24×3654 l# L: t5 F9 L8 q: k$ S
    2 ( |7 M$ f- }8 r& F
    64, b4 S8 U% v) D

    0 L) v% a$ J$ g* S5 ^* ~$ N
    9 a4 b* V9 f" [8 O: \ ≈585(Years)# d5 W. E( ]& f( U2 P7 x, }3 E

    / K- r+ A: z5 J% d通过pd.Timestamp.max和pd.Timestamp.min可以获取时间戳表示的范围,可以看到确实表示的区间年数大小正如上述计算结果:
    6 L  J) X& T$ _& F! {
    ; y& ^' z! t3 O3 Jpd.Timestamp.max
    3 p* U5 L/ U$ D( f1 _# U9 {4 IOut[13]: Timestamp('2262-04-11 23:47:16.854775807')- V+ E% G8 Y; }
    8 O' a3 Y/ s. A6 i: Z
    pd.Timestamp.min6 u6 d! F, |6 Y- V
    Out[14]: Timestamp('1677-09-21 00:12:43.145225')- p& B8 i8 z( K

    6 N1 f+ c, a9 R- ^) x0 e; {/ epd.Timestamp.max.year - pd.Timestamp.min.year( C3 G3 q  M  D; X0 p* V
    Out[15]: 5858 O1 y/ j2 ?4 A( x8 r' [% ^8 a
    1
    ; T* U2 J9 B- Z  `$ L3 @( y& J2; F/ D2 D# E; v0 ^
    3
    3 d1 _/ f: m# z4* W0 b# [7 ]' J& f( Z, ^
    5
    $ d0 G; q" ?' T- J6$ u( }& T  f! N! e5 x* K/ I  Z$ g
    7
    # {* e# _' a: I1 d8
    . w! `  e6 {9 n" W10.2.2 Datetime序列的生成/ N4 Z( c! K' t3 D$ H
    pandas.to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, utc=None, format=None,9 r% L5 g7 |8 u0 c! g/ t$ p$ Q3 ^' I9 @3 a
                                      exact=True, unit=None, infer_datetime_format=False, origin='unix', cache=True)1 U2 ]+ p, |5 g8 |2 b0 v' A
    13 ~/ ^7 u- U7 [/ Q) z) |+ _
    23 @1 H% c- b9 G( Z7 c& ?/ |2 [, g! }
    pandas.to_datetime将arg转换为日期时间。
    , e. ?! H. E) B7 C/ k) g5 c) Q% R: q2 n2 c- I3 t
    arg:可以是argint、float、str、datetime、list、tuple、一维数组、Series、DataFrame/dict-like等要转换为日期时间的对象。如果提供了 DataFrame,则该方法至少需要以下列:“年”、“月”、“日”。8 k$ B* Z6 |2 m( g. h
    errors:  p( u+ Y. D& @
    - ‘raise’:默认值,无效解析将引发异常
    : D/ S- X+ ^. l; C4 Y7 E- ‘raise’:无效解析将返回输入* k  q- T3 O. l1 E) F) G: h
    - ‘coerce’:无效解析将被设置为NaT, Q: x" E/ z9 y& c; d! {
    dayfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析日期,例如“10/11/12”被解析为 2012-11-10。如果无法根据给定的 dayfirst 选项解析分隔日期字符串,会显示警告。7 }0 @' T9 [1 d5 y* O
    yearfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析年份,例如“10/11/12”被解析为2010-11-12。无法正确解析时会显示警告。(如果 dayfirst 和 yearfirst 都为 True,则 yearfirst 优先(与 dateutil 相同)。)
    8 P" o4 j2 g) p" Eutcbool:默认None,控制时区相关的解析、本地化和转换。请参阅:pandas 有关时区转换和本地化的一般文档
    ; i3 U1 j# t, e- D, \2 Uformat:str格式,默认None。时间戳的格式不满足转换时,可以强制使用format进行匹配。$ C( f/ d" I% v: Z
    unitstr:默认“ns”。它是arg (D,s,ms,us,ns) 的表示单位,可以是整数或浮点数。这将基于原点。例如,使用 unit=‘ms’ 和 origin=‘unix’ (默认值),这将计算到 unix 开始的毫秒数。
    3 v4 [9 ]! \0 ?# q+ o% i- Ito_datetime能够把一列时间戳格式的对象转换成为datetime64[ns]类型的时间序列:. [( T; n+ A8 r8 b1 m
    pd.to_datetime(['2020-1-1', '2020-1-3', '2020-1-6'])( Q2 T/ E/ E% E3 A% F7 ^
    % P3 J" V) k7 C' ^" T0 q' }
    DatetimeIndex(['2020-01-01', '2020-01-03', '2020-01-06'], dtype='datetime64[ns]', freq=None)0 }' Q. C6 e" ~) b3 c2 O
    1  m9 _9 `; R4 c: |
    2
    4 t# X6 l! q$ J7 _3# E% M0 p9 D: d
    在极少数情况,时间戳的格式不满足转换时,可以强制使用format进行匹配:1 |! K! R& A- m
    % e9 L$ S, p  i4 `* T
    temp = pd.to_datetime(['2020\\1\\1','2020\\1\\3'],format='%Y\\%m\\%d')
    " {6 G) A0 k: P& o# `# {0 r! @temp
    / }7 |$ O( U) f9 S# c$ H# R3 S* q/ |
    DatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)% p) C3 t" T  y' X. h, S. c
    1, j0 b* s1 Z) X$ ^" ^
    2$ S7 a! V) A3 ~5 Q, n0 x. x
    3' x$ x& J, u3 B8 U
    4
    / R" P+ M7 w$ Q6 b9 P5 `; S" |  注意上面由于传入的是列表,而非pandas内部的Series,因此返回的是DatetimeIndex,如果想要转为datetime64[ns]的序列,需要显式用Series转化:6 @" W' J( K, ], d- j. w$ n5 P

    + N7 A4 N9 S& p# k# i, E- l) P; l. lpd.Series(temp).head()
    : f3 Y# [" Y. @
    8 @+ y4 m9 I6 M7 [2 H0   2020-01-01/ S; R* Z- S" {) @/ y2 u
    1   2020-01-03
    . ^6 R$ F- ~9 o0 Z: k2 b7 wdtype: datetime64[ns]
    1 @4 T% u2 a5 s/ r' O8 I1
    , q+ P$ Y" a8 R) O. U4 M2
    : A/ V* O4 t# L( T- m! X3
    " E) j9 }6 b2 i- I) X9 j41 ~& W! l2 [5 w) {! V
    5
    7 P7 J( Q! y7 O. g9 C+ I! C下面的序列本身就是Series,所以不需要再转化。
    " z9 m( ]1 c9 N$ E( ~; L" N# c% r' E- ]7 p  A' s. _
    df = pd.read_csv('../data/learn_pandas.csv')
    ) G# w& v4 ?) E! _s = pd.to_datetime(df.Test_Date)% Y& Y2 b5 c+ J# d
    s.head()5 v2 ?* j9 k& ?. I0 `7 `  s

    9 Q6 X' H& l: A2 t8 _% J$ |* ^0   2019-10-05
    & Y6 u$ ]. ^8 B! R8 E1   2019-09-04
    # A" W- n! Y8 t! h/ w2   2019-09-12
    ! \0 k5 ]8 |1 c8 h8 r3   2020-01-03" y2 B3 x! Q; i! ]; ^
    4   2019-11-06
    3 d: b" v, I  j$ FName: Test_Date, dtype: datetime64[ns]5 m- ]" y, g" N9 G- w
    1
    * Y* X1 H4 ^# K, A2
    7 e( S' D! t- Z  Z* X3
    ; @7 n  \5 j" V" |( s( r& f) h0 u4& F' l+ ^4 A9 n& f; W; M2 H
    5% s$ n4 J2 Y9 j" R9 E
    6
    : ?& y& k  z) K1 l7. y8 K$ ]( D. T. v7 l; W- Y
    8
    3 I! Z  Q3 H  n, b  A9
    ' B) V" ?0 K$ K! A; z6 J4 U+ V10
    2 t! U4 q/ s# j- i0 z" _2 a把表的多列时间属性拼接转为时间序列的to_datetime,此时的列名必须和以下给定的时间关键词列名一致:
    - x& B, v" e2 L- Ydf_date_cols = pd.DataFrame({'year': [2020, 2020],
    4 y. `8 I. \# B+ y( V# h                             'month': [1, 1],6 H, t9 T/ m5 [3 y( h
                                 'day': [1, 2],
    8 `! L' t/ X) f. f( v( Z                             'hour': [10, 20],
    7 Y4 x$ S; x0 U% f1 ]: y                             'minute': [30, 50],
    ! G4 I% }0 z1 E& S) @3 [+ p2 k6 H9 x                             'second': [20, 40]})7 e3 {* e: g! D! o, \  j# }
    pd.to_datetime(df_date_cols)
    : G, U+ `  ~- W$ U/ U4 \. g6 F- H6 R& x+ B
    0   2020-01-01 10:30:20
    9 A4 N( Q- S0 c7 g2 S1 P1   2020-01-02 20:50:40: k  \( h) L3 }* ?3 ]7 V; {
    dtype: datetime64[ns]
    # I% q+ j1 A' z/ Q6 |1& e  E8 Z! m  _
    2
    $ n' v( O+ `/ M0 g* F3
    2 U% _4 w2 Z% |8 g6 t$ }" O* L4
    ! }" \/ p3 ]6 u" T3 A0 j- X4 ~5: u& \' u8 k5 m) K3 j5 c
    6) B6 }2 g, T4 u( h2 E2 N% V
    7
    * o$ l  G7 _# Z) H$ [. F8
    ) H8 V; Q% {( O( j! ]( p9
    ; h/ P. S, z* _1 f10
    % z9 n  Z/ r8 q3 g: a/ H+ w11" M  C3 B- M- ^% t! P
    date_range是一种生成连续间隔时间的一种方法,其重要的参数为start, end, freq, periods,它们分别表示开始时间,结束时间,时间间隔,时间戳个数。其中,四个中的三个参数决定了,那么剩下的一个就随之确定了。这里要注意,开始或结束日期如果作为端点则它会被包含:
    4 L) [  }* U! B( Z2 @8 tpd.date_range('2020-1-1','2020-1-21', freq='10D') # 包含
    + G- H  K5 n( o7 _Out[25]: DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21'], dtype='datetime64[ns]', freq='10D')- b$ J2 B8 C4 S' Q& j5 O* d

    6 ~" T2 T0 }: Q4 b6 @pd.date_range('2020-1-1','2020-2-28', freq='10D')
    5 d: R' k$ b6 d. hOut[26]: ( r4 _- j* m% m9 \0 \4 C  j
    DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21', '2020-01-31',$ Y, i) e$ v6 o4 D
                   '2020-02-10', '2020-02-20'],$ h/ K4 E/ F* v! ^, j$ f- o3 b
                  dtype='datetime64[ns]', freq='10D')
    9 j- W9 ]* o3 o2 Q7 I+ y; `5 ?3 ?% g' c3 t
    pd.date_range('2020-1-1',
    $ d, W/ Y; r. K& U              '2020-2-28', periods=6) # 由于结束日期无法取到,freq不为10天* z. J  Y! b! r, R5 _  N/ p6 T8 ~1 G
    " Z: M: `  @/ R  ~
    Out[27]:   d- ^/ X* K& e+ y* n: ]- g
    DatetimeIndex(['2020-01-01 00:00:00', '2020-01-12 14:24:00',
    ( G4 \4 ~& y0 `5 J               '2020-01-24 04:48:00', '2020-02-04 19:12:00',
    0 g) Y. A0 q! a0 ~4 m               '2020-02-16 09:36:00', '2020-02-28 00:00:00'],
    ; H: ~9 T: A! W! F3 ]' P              dtype='datetime64[ns]', freq=None)
    3 C! x3 @( ^7 {+ U7 @. }4 y: W9 ~2 R* }/ A# H1 [, _
    1: T9 f: ~' L2 ?8 _9 y
    20 I' Q$ h% n7 w& s
    3
    8 @7 h2 g  B& O9 [" S+ f( o9 y4
    ( S/ O: N  j6 H$ v) D! y' \5
    6 l1 d( s6 `/ V. R2 k6
    0 j0 e4 `" J6 E7
    " T. n  N0 X4 ~0 ^0 F9 b80 M3 F; E. u( r* J  a( }
    9
    ( u7 I, Y9 D# B10
    0 |# S& h7 L; N# z11
    5 _9 F1 i1 q% r6 g7 v. W! ?+ j12' K4 f- _6 q( m: P3 m4 I
    13
    ( H. |, X/ [/ j14
    / G0 s) z! ?' e+ v15  C9 @$ n$ J) O) N, S
    16
    2 E1 j; o9 N4 ^- S( O( }17
    $ p7 M! W5 C9 D" s这里的freq参数与DateOffset对象紧密相关,将在第四节介绍其具体的用法。: y2 d9 U4 r4 s' C1 F' U4 q

    ' u  V3 _, W; T' m【练一练】
    5 y; g3 k+ v/ A! R3 q" CTimestamp上定义了一个value属性,其返回的整数值代表了从1970年1月1日零点到给定时间戳相差的纳秒数,请利用这个属性构造一个随机生成给定日期区间内日期序列的函数。
    3 z  F) J9 j* Q: |6 {6 P, Y3 ~2 c
    * y8 p0 F3 V" U+ X% Jls=['2020-01-01','2020-02-20']$ `- \5 K& F% z- @$ G+ \( D
    def dates(ls,n):
    6 O: E1 G6 t; {$ h4 \    min=pd.Timestamp(ls[0]).value/10**9
    1 z; Y  F% P; z% Z8 }) _0 t    max=pd.Timestamp(ls[1]).value/10**9
    / N# X- r( H7 \1 R1 p* S* [    times=np.random.randint(min,max+1,n)
    ( F4 ~) K9 E2 x4 v! c    return  pd.to_datetime(times,unit='s'); j3 k1 c5 e3 v! y3 w0 u* Y$ g
    dates(ls,10) 4 ^$ E. D8 u: w, r5 E
    , r9 E# [+ ]+ J4 D% ?- n: C4 t
    DatetimeIndex(['2020-02-16 09:25:30', '2020-01-29 07:00:04',
    - m6 C' e. T3 R, X" p# B5 m               '2020-01-21 12:26:02', '2020-02-08 20:34:08',, o) _: P% L; z6 W3 j
                   '2020-02-15 00:18:33', '2020-02-11 02:18:07',
    5 F! e3 A5 \- @$ i# J4 C               '2020-01-12 21:48:59', '2020-01-12 00:39:24',
    & [% X: m. s& m& c5 J2 L5 h               '2020-02-14 20:55:20', '2020-01-26 15:44:13'],
    8 |  ~; ~, u3 @              dtype='datetime64[ns]', freq=None)( y: _: a2 k) k1 S+ y5 o0 j7 P3 I
    1: s7 g% K& r7 G" {- E: U3 }
    2
    & ^( s  p5 z+ N7 I3
    # j' b; h) v& |+ N4% Z% l6 I7 v- D- j5 W- Y
    51 K0 X. `, G1 c
    6  \5 S+ E; o6 z
    7
    - U2 n. j6 H6 \3 I% ~, B80 T% l- S" F( i2 W! u0 R$ T
    9
    ! b, {" ]* N. C" z3 F  S10
    0 j* O% H( O. ^, E3 M2 c11
    ( n5 T, k9 \/ U! a, t12
    7 P  u6 D* I7 Z, C8 K0 f+ X13
    2 p$ O8 R0 ^+ m' ?; t: @6 g14
    1 c+ a" [2 e. C, oasfreq:改变序列采样频率的方法,能够根据给定的freq对序列进行类似于reindex的操作:
    $ l: ~2 ?( M& ^2 c- \2 Hs = pd.Series(np.random.rand(5),
    6 \2 P5 Q  a- u            index=pd.to_datetime([
    $ x. T% c% U$ K                '2020-1-%d'%i for i in range(1,10,2)]))
    # b4 t3 e! \# {: ^
    : t4 Q6 n7 p1 o3 V8 N' l+ ~$ N! A3 J
    s.head()8 t1 A6 @6 H7 p! G/ D: c, `# W$ m/ V
    Out[29]: 0 r# N2 [6 P# R
    2020-01-01    0.836578
    5 L! ^4 W2 L8 `& J- X2020-01-03    0.678419& C& U7 t4 S7 g# d9 w
    2020-01-05    0.7118979 @" j" C/ u$ t  a7 f1 X
    2020-01-07    0.4874293 H% M, O6 X5 ]( U
    2020-01-09    0.6047050 |( S  F, @3 F. G+ d+ o
    dtype: float64
    : t. w# a1 \: Z: j7 \  c! z4 |+ T/ q4 P
    s.asfreq('D').head(). ]+ P$ N5 d3 ]' ~/ \9 u* k
    Out[30]:   [. u; o1 V9 q. z' E
    2020-01-01    0.8365788 T% |3 L" D. U" a) a3 S$ K
    2020-01-02         NaN7 {0 F& l4 P& j; w
    2020-01-03    0.678419/ Q& p4 C+ e. B7 _
    2020-01-04         NaN
    ; P( T4 W: ]. s+ A2020-01-05    0.711897; d+ J; Y1 |% A# L
    Freq: D, dtype: float64
    1 i" N* t4 w* @2 q* @  _6 j! i
    - g6 F8 R* A+ hs.asfreq('12H').head()7 y3 Q+ U; Y6 P, n/ h8 @$ D
    Out[31]:
    ; j7 x% `" k! A# D  C6 ^2020-01-01 00:00:00    0.836578
    " Z. ~: T/ o- M6 L7 z2020-01-01 12:00:00         NaN
    1 g, z& ], O3 O7 w1 k2020-01-02 00:00:00         NaN
    9 y3 C0 K, J# h' }) A2020-01-02 12:00:00         NaN
    - N# f8 w; }- G& b/ g2020-01-03 00:00:00    0.6784190 _, C: L- m0 m/ x
    Freq: 12H, dtype: float646 r& ^' g$ Q& I

    - l+ t/ Y, I% n1
    ; s5 t4 o0 P! `" i, w2 L9 W28 P; Y- ^) u3 W5 l
    3, h5 ?4 j7 s& r- o5 r6 j; \
    4
    + ?; E2 R+ D7 S9 e. [) M5
    . y: e6 R" z, R# o2 j4 N9 o) y6$ F! q- H2 k" `! @) A
    78 s) `& O4 i" a0 y
    8. E# L, Z+ _/ ~# T( ~( _4 R2 v5 E
    9, S7 u" @' S" W- t
    10) E4 d8 Q9 k; D. ^7 W, Y8 @, `
    11
    4 C6 q8 h5 J; C. @$ y: h$ W12
    / F* T& \1 T. m' \2 {13
    ! @. I, M/ G. j" D$ b3 _! X8 C* X14$ G9 X& S# Z- |. P6 H( I  d+ a
    159 ]. Y! I6 L" s. s
    16
    / U8 l5 `, N* c. T) P17/ b; N# c3 }: H, @; n# A7 {
    18
    2 b9 n( t$ ]. D& Z$ ?( i& X9 {8 o19
    8 _/ k* Q- T5 H! O203 @& `! g5 ]- f& Y/ s) ~  ^* V
    21) `& A1 Z3 H: }4 ^9 |3 Q8 n
    22& Z1 n- P5 }# [! x9 ~/ u) b
    230 a% [% |6 u8 t( N- q
    24, y  c( _' H1 k8 h: X' t; U
    257 G, V4 f8 k  z8 g" K+ \
    26
    1 \4 N- l8 R# d276 \2 \  h) j! ~! h* A
    288 q3 S0 S0 D* j7 F1 d8 ]9 B: o
    29- F! ?' g; f4 g6 G( _, |7 u6 a0 ]! O
    30
    5 u) @1 s' _) x31+ G; \1 E& q" D+ I- Y+ j2 L0 h- m
    【NOTE】datetime64[ns] 序列的极值与均值
    ' t# a2 m3 o! ]6 }3 Q( \8 D" {  前面提到了datetime64[ns]本质上可以理解为一个整数,即从1970年1月1日零点到给定时间戳相差的纳秒数。所以对于一个datetime64[ns]序列,可以使用max, min, mean,来取得最大时间戳、最小时间戳和“平均”时间戳。
    4 q  L; n/ S4 C  u" h) L9 b5 j/ o: A& J" u/ V; n) N
    10.2.3 dt对象
    & A& k8 y$ `& a: d! F  如同category, string的序列上定义了cat, str来完成分类数据和文本数据的操作,在时序类型的序列上定义了dt对象来完成许多时间序列的相关操作。这里对于datetime64[ns]类型而言,可以大致分为三类操作:取出时间相关的属性、判断时间戳是否满足条件、取整操作。7 L7 j  O  L" y
    , {" p- z) |. K
    第一类操作的常用属性包括:date, time, year, month, day, hour, minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter,其中daysinmonth, quarter分别表示该月一共有几天和季度。: H& [* K$ E( q2 M
    s = pd.Series(pd.date_range('2020-1-1','2020-1-3', freq='D')), I: _# V& L' d
    ; p6 R( ~) U' {" H7 d2 m
    s.dt.date6 I& c& @& O! V# G9 r2 l2 T% K
    Out[33]:
    - d) j, z$ F! S0    2020-01-01! k+ b& e( S; d3 {  y, k
    1    2020-01-02
    . w1 |3 [- |- Y! m4 J2    2020-01-03
    1 s7 g! N2 U; l. _dtype: object
    3 A8 l5 ^: `0 z4 _9 s! C5 j! h
    . N$ N7 `0 j, K  L9 H0 i/ rs.dt.time# a! O' T# {1 g* V
    Out[34]:
    ; X% [* \- R$ E: L- M" t$ i) e- j4 y0    00:00:00- b; _9 p' m, v9 J0 E
    1    00:00:00
    , L: _$ q* V- Z7 G9 l- {' I* k2    00:00:00# {+ o( N) \8 \+ f/ t5 n
    dtype: object# A$ j% h3 K3 e& `: ~
      B$ w4 g3 Q8 I. v6 M
    s.dt.day1 @- m0 B% `. T' `
    Out[35]:
    - F* e* o( D3 u0    1
    % E& ?+ h% T4 _$ C3 x1    2
    ' L8 |  Z4 ^2 T. T6 N& \1 A2 H/ D: Q2    33 C* n3 E1 T+ X! H! X4 ?
    dtype: int64
    1 q. U- z) e- c% g. f$ z& p& \5 _! U( T8 `0 p) k) ?! m# L  X6 M
    s.dt.daysinmonth
    7 z7 g; b4 A! I: gOut[36]: 4 ~/ z1 N2 m4 ?6 ]! q( V
    0    31
    1 h3 }, E8 w; x* g% X. C- `4 z1    31
    2 y; @1 ]; O2 l# M9 O- F) n* c2    31! d: q/ N, i9 X! H# O4 Z
    dtype: int64" |2 y8 g% Z) _8 b: p) f

    0 |8 @; w* M, r$ N! N! x7 ?" _13 y1 q+ t3 t" h  T6 J# ~% h
    2
    + _6 Q: U# n; h. q3
    ; f1 S2 U0 u! r$ j4" W9 M* y* V! u  _+ B! }7 m( g
    5
    + o& Z- G9 y3 m0 |" \6
    $ i) y/ K. X9 v0 e7 r+ M2 M7
    : z3 P& u/ |! K1 `8' \% ?8 l6 a) c1 l% z
    9
    / Y# P+ `$ ~( y% K% G- Y6 C9 G10; U/ X7 [" r# S; F* R1 V- w+ W3 ^8 N9 j
    11/ y1 e% L6 [  ?2 ?; S. l4 i
    121 d2 I; M% ]0 C. v- ?' n
    13
    . K) `6 u2 ]7 }14
    7 L8 o" }* O: |( \1 S% E15: e7 M9 s' T0 X6 d
    16- s$ r% \& R" V+ z5 V
    17/ {) l0 W" c( b
    18
    4 H8 i+ n# g  a# M( p" N4 K19
    8 K% _, k# z( u; z3 E20
    4 @4 Y1 x  l4 U: @+ v8 t21
    & q' q" b9 t/ ]; i( K, i3 ]22
    4 D. X2 w! A9 K% ]" o% J23) E. [* V9 V3 A- H) v: ]! n
    24$ @, U! V5 G; n& }6 }* F
    25
    . g: X. M1 |9 Z5 [" k26
    8 U# y( y* C9 ?( K9 ^275 X- K5 [) g0 _! w, R
    28  X3 }: V- `; Y9 o/ v, z; \
    29+ E, H/ @+ m( C7 a' s& I
      在这些属性中,经常使用的是dayofweek,它返回了周中的星期情况,周一为0、周二为1,以此类推。此外,还可以通过month_name, day_name返回英文的月名和星期名,注意它们是方法而不是属性:: _7 W/ I4 S2 e) |6 \
    2 @0 ~8 `! ^0 p. T' D: C
    s.dt.dayofweek( L3 F2 u% D: T6 [) ?
    Out[37]: ' h( o2 [3 G& s4 J* ^$ w' U
    0    23 u# ^$ L. K6 a" \2 [) A
    1    3
    / l) t9 s1 U3 P  N9 B" }2    4
    ( a( z1 d1 G8 ]dtype: int64( E, E  i2 H7 e6 w+ q3 }' b& P

    / z- N$ ?7 _6 Zs.dt.month_name()( a- J* o5 N/ B  K6 x0 d
    Out[38]: " n7 q6 \' S5 m$ N% t/ l. `4 D
    0    January' j% u7 W  B1 {2 q! m. j* m, O
    1    January
    4 Q+ B: |$ s! ?/ B6 I2 Z7 ]2    January2 t/ y: x) e1 h+ z4 f
    dtype: object
    - p; q( M1 }& R+ t# Z3 r' a5 m# A0 v" F
    s.dt.day_name()
    / P" x  O) e" A2 w2 O7 ROut[39]:
    3 y  V8 K' A( ]$ G0    Wednesday: P+ U7 ]! d% N- d5 f1 N, t7 y
    1     Thursday0 o+ I$ |1 ~* W  P" e
    2       Friday* B) T" Q7 G* r7 V8 Y7 U
    dtype: object
    ; ]1 T* B5 \0 y9 G" f/ B! L4 |* M
    9 a9 Q) V/ J9 m13 S* C+ x* L0 y  N. j2 x
    2" L2 G5 G  X. [1 Q" j
    31 N1 N) A' O( p& }. D5 t
    4
    " K0 Y+ \( b/ O5
    5 O; |$ G/ k/ f2 ?6$ F- a; s2 E  ]+ r$ n
    7
    ; Y- [, \- y0 ^/ _2 R) |1 n, G8" a/ G- a" ]) q  L& K3 q3 |
    9' E' A0 |/ s% n7 U/ e" ], F" T4 P/ T
    10
    0 ~& j% J7 r1 c11
    , E  F) r# |0 p# f2 \4 m12
    1 b: f7 d) t6 z" c. G+ T+ M138 {9 G4 m: }/ Z  q7 e
    14
    & [2 v8 t/ |. M15+ P# t& V# P5 j( k
    16
    4 d# c" j! [+ Q( j- i179 z8 C+ }, K$ C4 d/ V* j5 z1 j
    18
    * U9 `& M7 V' R5 B191 w- ?- t- D* y
    206 U# Y: M" A% F+ N. q, s
    第二类判断操作主要用于测试是否为月/季/年的第一天或者最后一天:/ t/ C+ p8 L! A
    s.dt.is_year_start # 还可选 is_quarter/month_start0 K  E* b7 s& [! L" C
    Out[40]: 7 W: g" S! u2 i5 t% i* p" P
    0     True
    6 y& M$ I% t" T' N( l  v1    False8 W0 q8 M: S  C- r; \
    2    False
    : I" W( E, |; u; t0 o" hdtype: bool
    * Q, ~# t3 {, T. W! j
    0 s9 n! N/ O& ^3 p, F$ h: us.dt.is_year_end # 还可选 is_quarter/month_end
    / O" q8 J( F/ H# J' h% x1 ?Out[41]:
    ( {! ?+ a7 i0 f2 H0    False
    # i# }. [) I' ?$ o& [- [' Z1    False
    ; H% m5 d# D4 X# B+ Z0 {0 n2    False
    6 F, y# |7 f: a7 R: |8 Bdtype: bool
    " L% g3 d- O- I- u) Q1
    , D$ U$ ?! S6 X2
    $ C, v& d) m& v; ^/ @; A9 O# G3
    6 `: n0 T6 D$ N/ a$ j, x# i4
    - w8 s. ^) u( A2 }/ H5' X6 Q9 H3 H8 }! J' r/ S. t6 W
    6
    / l- k( f) ^5 h2 g; [& F+ n2 n7
    7 x2 o, p% j8 Z+ @0 X& A87 s( y& I. @+ M  a% e; R! m; E
    9( x6 L+ K: h6 I; J5 u1 `
    10
    % Z# c# l/ I7 k" w. f0 O* K% m11
    4 v/ D- _* M. x9 {4 a# z) ?1 V( h8 l12: y& ]( C1 E& p7 d/ T& F
    131 g: K6 d+ u1 R: M- d
    第三类的取整操作包含round, ceil, floor,它们的公共参数为freq,常用的包括H, min, S(小时、分钟、秒),所有可选的freq可参考此处。5 N9 A+ v% U, N6 z$ I
    s = pd.Series(pd.date_range('2020-1-1 20:35:00',( O: D0 y. `5 s& t( c0 u6 R, S
                                '2020-1-1 22:35:00',
    7 M4 T: Z# {* _% V9 b$ H, K, R3 f                            freq='45min'))
    ( _, L% I' F0 V! G" V5 o' D  {& p% V; X3 O. L6 w; G
    # H; {+ Z; J7 s% G) K0 h# x" N
    s
    - \1 M/ n6 \+ W$ AOut[43]: ! B6 o8 e9 O) |& W
    0   2020-01-01 20:35:00
    . P" a7 K4 V& t* g1   2020-01-01 21:20:00
    % A# F# ^- o9 h) ~/ s, e( E7 ]$ _2   2020-01-01 22:05:00; @! P& E( T7 ]9 Z
    dtype: datetime64[ns]
    5 h5 m  g5 p4 Y9 e  G5 i) A
    9 D7 k6 i5 s0 Ls.dt.round('1H')6 B/ v: l9 B: D
    Out[44]:
    9 L1 L, z/ g- O* g" c4 ~$ j0   2020-01-01 21:00:001 n0 P; f( z/ q& S& E8 e8 ^
    1   2020-01-01 21:00:00
    - a* q) \( {( T  M2   2020-01-01 22:00:00' r* @2 h3 U$ x3 ]' f3 \  i
    dtype: datetime64[ns]
    + V8 E& X4 b: `% ~8 f$ O* Q+ M9 f- A; `
    9 @+ ^( O% @- B9 B2 xs.dt.ceil('1H')
    9 m8 I, V/ y) M2 d3 jOut[45]:
    ! w# ]( j- O# Q2 B# w& l1 g0   2020-01-01 21:00:00
    / Z, w2 K. l; ~# p: T4 i+ y1   2020-01-01 22:00:00
    # q& d) C+ U0 ^2 N' w& r2   2020-01-01 23:00:007 a& g9 {# h8 t
    dtype: datetime64[ns]0 [& p: B' X" ~/ N: O! ?# v* v% K
    8 h. Z& A7 |4 o3 m+ [  n( ~% t& q. Z
    s.dt.floor('1H')
    # S; p9 ]" I" a$ I! qOut[46]: # D1 C" |8 a# ?  W# `( C; @
    0   2020-01-01 20:00:00
    + t2 P8 b6 e7 [6 U2 W* G9 L# k1   2020-01-01 21:00:001 o& U5 A# B6 Q' _/ A) N% Y$ j
    2   2020-01-01 22:00:00% p8 S& G  j0 D
    dtype: datetime64[ns]
    0 f( Z) k" t1 D5 J; E+ l: a% ]% Z  u$ ~3 v/ K' ?" X! P
    1
    & k( d" ?) \  z2 }5 L* w( A2) ]5 E! i2 M% C& \! I$ o" B4 e4 ^
    3; y9 W4 f* V1 H) N+ p: r
    4
    & c3 g0 u- A+ I- L7 \6 |5) k$ y2 W: l: |! Q# d
    69 D* f; P2 O+ @' J5 L4 j7 {
    7
    2 c6 J, |4 N8 W84 Y4 U1 R/ r# @- O
    9# b+ {, H6 ~9 E* \' q
    10( C- ?2 E& z; z0 H
    11& A5 r' b( i7 P% L
    12
    , W- h# Q' P. }8 W2 M13* W6 K2 F+ [  r! }+ l
    14( u, [9 G9 H9 ~5 w
    15. w4 T8 C" M: v9 U
    16
    . B. t) W9 {& b0 }2 V17
    * b. E8 L% O; ]1 {$ g3 T5 \18
    / G' g1 z, c# ^4 w& S# m4 r; O19; p/ J9 T; l# X1 C1 q6 B/ C
    20
    ( _, B; x/ j/ \. u# E% b6 f2 p4 X218 T4 R* V3 c- U/ \
    228 D& j% j. L, L8 M
    23
    - o0 |; h$ k7 b# T; t5 p7 S245 n. ^$ i8 w$ ]+ F4 c  v
    251 ^( z6 }! u% q. T& v0 x+ b: x
    264 E0 j5 u# f  C6 m
    276 L  r4 ~9 a4 V/ |6 U" K9 i
    28$ z1 W9 X& ?7 p' K
    29
    7 v( z! x7 e2 ^4 \5 R301 s' R6 j* w1 T  r
    31
    ; ^7 P" f2 n0 [4 d32
    5 r: t) f! C7 Y" B9 _5 d7 {10.2.4 时间戳的切片与索引; e, @: @' `7 }, W- [* g! j- r
      一般而言,时间戳序列作为索引使用。如果想要选出某个子时间戳序列,有两种方法:
    $ P' _6 `7 K: k; L! k3 G' f, B8 N$ O8 F; O
    利用dt对象和布尔条件联合使用( X! i0 l* q2 f
    利用切片,后者常用于连续时间戳。3 Q" i4 P; k! c% A& d4 h
    s = pd.Series(np.random.randint(2,size=366), index=pd.date_range('2020-01-01','2020-12-31'))' n2 {& m8 `! l+ s8 S. L  ?  a
    idx = pd.Series(s.index).dt- b) c* u, }" J5 }! t
    s.head()2 w% n0 f& ?& [) A& ?- R% q6 Y
    : j9 a# F7 @8 r% f0 |
    2020-01-01    0' O, G) T& F- n3 A
    2020-01-02    1
    & M4 D7 F8 h& N3 d8 t, ^2020-01-03    16 z4 a, w, O1 _* g) ?8 Y
    2020-01-04    0
    1 a# }7 Z& N  r6 ^& e# @2020-01-05    06 ^! K! H' w5 F" v. n6 e
    Freq: D, dtype: int329 a6 ]% ?8 c  ]: h2 a  R
    13 H2 l* K* P! R1 U" I
    2
    * A) z% J" O2 v( f6 u3! i2 D$ h( D! Z
    4
    5 l/ M$ W3 T( Z; X. L" h53 O  e( z: m( {- x: T
    6
    ! F# h6 q7 o* H& l; t7
    * u% i9 n, |  q8
      N8 K8 b. b" p99 R" T* o9 b7 x: ~
    10+ m% i$ O; C( [
    Example1:每月的第一天或者最后一天
    % Q, x1 L- z. m6 p9 s/ W
    % j1 S# ?& c' Q1 _/ p# d/ j6 Os[(idx.is_month_start|idx.is_month_end).values].head() # 必须要写.values
    + T+ n  u; ~- P! h; DOut[50]: / i$ q' S* H/ M
    2020-01-01    1
    # A( V/ H- \/ {& q2020-01-31    04 N8 ?7 o$ U, i7 s, L* j
    2020-02-01    1( @" I. v  E- L; n  e( {
    2020-02-29    1' F5 b. G4 Q# J4 |/ d5 j: {  h
    2020-03-01    0
    , n  m5 W' N+ D0 C0 \7 v5 s. hdtype: int32
    7 w- J% K4 {; h; g. M: U: G1
    # O; t9 T2 g; f( _2
    + M1 P7 x/ {- V4 C/ T/ i  `31 p/ i0 m, Q/ j2 i/ a8 e7 B
    4
    / j& X/ M+ E+ Y2 |5
    " R( [/ h& `1 _/ f4 `6
    ( [* y) H5 l% \1 [7( G6 x' l7 m3 K2 g& y* B
    8
    / h& b7 J+ t) c1 ^, p  VExample2:双休日: s" m/ c) B+ v6 u1 J: @$ n
    , \9 M1 d( t/ C8 u. y2 N- w; @
    s[idx.dayofweek.isin([5,6]).values].head()
    ( R. X( J0 v  IOut[51]:
    : z' e3 N& k+ V$ {9 Q: F' C2020-01-04    1
    + t" P0 y  s4 C$ m2020-01-05    07 H/ W, Z2 P* g" h- ?" B
    2020-01-11    09 ^( a. S' F: X5 {* R$ E
    2020-01-12    1
    . n/ f% N( {& s, _' t" M) G- Q2020-01-18    1" }3 F5 G! t4 X0 Q; v0 L
    dtype: int32  e5 o6 x: c+ l' J: v
    15 }2 R, ]# o/ u- w# u( G
    27 D: a. }, i5 a# ?
    37 V, }" c9 j+ H9 U9 _6 p
    40 a* l7 G& V4 j8 c- p
    5
    2 n  u9 S- U% `  Z6
    ! W$ W6 I' J! i' B: `2 T3 q77 a7 I' Z9 d" T
    8
    / y& Q) J9 [# E5 N+ y* M/ U0 aExample3:取出单日值# y+ M. g# h  r9 v0 Z* s1 D, i; z
    . L1 g# f) y4 h( I! s+ I
    s['2020-01-01']4 H3 W& Y( d  n
    Out[52]: 17 h; O, V3 ^- U" X0 i/ s

    ' F, P  n# v* x) h: Us['20200101'] # 自动转换标准格式
    ' D  _0 F. u8 @* z9 D) DOut[53]: 1" D& ^6 n, Y) b, S) S
    1# o; D( [. ?# A0 D) p
    2
    $ ]) {" o) G* `" X) X/ |9 L+ ?1 z30 f9 q  Z& Z6 Q8 E  o
    4" W+ J, j# L) l" m
    5# ]  g$ X/ l) t4 w& ~8 }7 `
    Example4:取出七月
    ' ?  V5 ]8 ^1 ^1 v5 {' `. j
    0 q) E/ @! \5 n& |s['2020-07'].head()
    ) N% j4 [+ @- `0 C# JOut[54]:
    0 o7 e/ J6 h9 s- v" {# T2020-07-01    0
    * p( P2 v9 f$ _. \, U$ \2020-07-02    1  C5 v" Z" U0 P& h+ D
    2020-07-03    0
    ! l, O5 p2 M- K9 W9 t0 t2020-07-04    0" @9 H  \4 U6 _$ q1 A7 L
    2020-07-05    03 Z$ K1 k3 c; \/ G3 ?
    Freq: D, dtype: int32
    , n( B& @  P& ^3 C1
    - J9 H7 a. g; C: [% v2" F+ t- }* L* o
    3
    0 |" I7 v$ {0 }5 v( e; s  [4& l" O( n1 I# N6 {5 P7 `( E/ H
    5
      [+ E& D6 D8 Z1 b, T6
    / v; ?' Y' ~& A. U6 d7 F$ Q; w79 N8 e; g0 b: i* \# X: p. F9 i
    8
    ( ^! h9 T. V0 h3 q3 J2 k( M4 rExample5:取出5月初至7月15日% t$ r1 }% L4 q! f8 @- o5 k
    $ h6 y* y2 o' q# i% n
    s['2020-05':'2020-7-15'].head()7 |5 l1 I; J( G- s, V
    Out[55]:
    * ^1 J9 @" [7 K4 C7 `2020-05-01    0
    9 E9 n1 u% Y! `' _$ w# l2020-05-02    1# [/ T3 T* F2 U6 o
    2020-05-03    08 u6 T: C7 g8 Q7 B7 P# ^( w
    2020-05-04    1
    2 t: _4 R) E+ o' t8 ?+ C2020-05-05    17 B: V: o0 T9 ^/ @) k1 G
    Freq: D, dtype: int32+ y: u/ J! c/ G3 f& j. v; W! o
    2 l  D6 W& C7 K( C1 i( g8 {; T
    s['2020-05':'2020-7-15'].tail()9 q# w* |" B) I. }% _% E
    Out[56]: & P" o- c' ^6 F* C2 p# X9 ~0 Z8 C
    2020-07-11    0$ x* Q, }- `) `7 K% l! E* ~" {
    2020-07-12    0; |/ y/ ^/ Q) |& R" D3 h5 ]
    2020-07-13    1
      K8 s' \* D$ y/ ]2020-07-14    0
    0 n* _! g- q! E9 O" F# K+ A2020-07-15    15 x( |- r9 H: F7 Y5 J
    Freq: D, dtype: int32  v1 u  r+ y5 j; s8 Z& D0 H

    & v3 Z+ V% Q4 a1
    , g2 l3 w' O- T5 A6 j0 j. I/ d2/ R; ]) i+ G" A+ U) b4 q
    3) k, R9 j* {# K, I* F8 p
    4
    * _3 |5 \# i* L/ E" ]- \8 P9 A5
    - w1 W% e- h. @2 O, W" E7 {) l6
    ( e# J0 {6 i" M/ u& C7 ?" m+ u- i7) R$ e  o1 d2 A; u. J& U
    8* n# x: H) g! b5 F0 c
    92 I! F9 U$ v0 r: A6 I  B. ~* f
    10- n3 B3 I4 T* b( [& n
    11. `3 {4 y* R/ ?. b) M/ t0 L* _
    12- L$ e# O9 d3 @. h- o
    13
    $ k5 B1 ]1 Y7 n& j% G8 O14
    ; \: T/ C; Q1 \& v4 l15( q: O' o* w  ]/ u2 w0 v/ d
    16
    7 a2 A3 d% f6 n17
      S/ F$ X" a& J1 Q4 ^10.3 时间差% L( U) S7 a% R- f$ a: f* I" j/ c
    10.3.1 Timedelta的生成
    , t4 V0 y0 C2 U2 Spandas.Timedelta(value=<object object>, unit=None, **kwargs)
    0 d, {% [" A8 r3 B  unit:字符串格式,默认 ‘ns’。如果输入是整数,则表示输入的单位。/ I* d  f6 C# B2 o
      可能的值有:. T3 b, n8 s: I0 i

    5 ]5 b1 l5 W  n$ V" |8 p! v‘W’, ‘D’, ‘T’, ‘S’, ‘L’, ‘U’, or ‘N’
    + y" G6 I  j8 J' ?% j' Q: s/ i‘days’ or ‘day’
    / h0 B8 C6 w$ l; a: D‘hours’, ‘hour’, ‘hr’, or ‘h’$ R# }% v! r' {7 p7 j. W8 a7 M
    ‘minutes’, ‘minute’, ‘min’, or ‘m’% K6 i, R. r, r9 E- g
    ‘seconds’, ‘second’, or ‘sec’
    0 k8 h# h$ H5 a; N( q" I* K- G6 Y毫秒‘milliseconds’, ‘millisecond’, ‘millis’, or ‘milli’. |+ r! y' T! ~$ n7 x
    微秒‘microseconds’, ‘microsecond’, ‘micros’, or ‘micro’  D' K/ b/ C% e5 r! t" `( D8 Y
    纳秒 ‘nanoseconds’, ‘nanosecond’, ‘nanos’, ‘nano’, or ‘ns’.0 }+ F3 S0 f- Y- g
    时间差可以理解为两个时间戳的差,可以通过pd.Timedelta来构造:' ], G! r! T& t5 M+ \( N
    pd.Timestamp('20200102 08:00:00')-pd.Timestamp('20200101 07:35:00'); H- r0 \3 W, Q/ H, M7 V
    Out[57]: Timedelta('1 days 00:25:00')
    8 {$ Z# o$ c' O# e" }0 h0 ?9 C
    ' I$ M7 f5 B! L. E, t" Opd.Timedelta(days=1, minutes=25) # 需要注意加s( d+ e+ H; N+ c- U" b
    Out[58]: Timedelta('1 days 00:25:00')3 v% ^0 ~8 b: T; l
    * S: h. ~- Z1 h( K* X5 K5 z
    pd.Timedelta('1 days 25 minutes') # 字符串生成7 w  h, k& A: _( k6 E
    Out[59]: Timedelta('1 days 00:25:00')
    * L8 q7 w5 ^: N5 B2 u0 x5 ~# g' g( |
    pd.Timedelta(1, "d")
    & T$ E% S  d  z0 _; KOut[58]: Timedelta('1 days 00:00:00')+ |: ]' j8 _: y% ^& t/ T/ t- U2 X
    1# [8 y  U4 N1 _" t
    2
    ; `* I2 W% ^0 z# s0 V/ l3
    : Y0 f5 S% _" O3 g3 X4 f2 d4
    % l0 h. V) B4 O/ \2 H- [9 P: F5
    6 w9 F* y6 b  m5 x" D. N% y, `6
    1 Y  w- ^# p4 e9 C70 q3 G4 w% i3 X" V( K: [& D
    8  Q: V0 p' o0 W6 V9 g  T
    9* w, Q- O; G* A" i/ D' l" L
    10
    ) f# K7 X, k5 a  m$ ^11
    & F! e- ?( [" V生成时间差序列的主要方式是 pd.to_timedelta ,其类型为 timedelta64[ns] :
    # f4 o! `% c. J- Xs = pd.to_timedelta(df.Time_Record)
    - F) W$ w3 g+ F" m/ M$ L' y, h) k  S, K* I
    s.head()
    $ \0 f) g9 \, ]3 ?6 p  ]( ROut[61]:
    4 r$ m' H% G# N/ D2 E0   0 days 00:04:34
    ( x" A* [3 S& h1 @! x1   0 days 00:04:20
    % r" _! V5 X$ f2 ^2   0 days 00:05:22' ~, L( e& }/ Q, y
    3   0 days 00:04:08! U) ?+ |4 i9 o. v0 `" s
    4   0 days 00:05:22# D, l0 F( C5 m; D8 G( z: ?& N" I
    Name: Time_Record, dtype: timedelta64[ns]
    8 S, H! b. [4 {; k. I% B1
      ~; _) j+ l+ k! n' l' I5 s" R2
    , g- y" [7 G4 E- l' V$ V5 L  ?3$ ^  W6 S4 j; m2 W. {* c
    4, W0 [5 D; k! ]- ]1 k
    5
    * r  q+ p- U9 q6
    - a( q, U# `$ P2 \0 @7! Y  i/ C! Y+ i+ X1 I+ \# B# @
    84 _" B1 o1 a" u% v
    95 p+ L" H/ n& R- V, \
    10
    - o; m1 P6 Z- D; i. r与date_range一样,时间差序列也可以用timedelta_range来生成,它们两者具有一致的参数:/ a, ~3 e5 `7 A8 _
    pd.timedelta_range('0s', '1000s', freq='6min'); ?" A8 T7 w$ y0 w
    Out[62]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:06:00', '0 days 00:12:00'], dtype='timedelta64[ns]', freq='6T'), o) C( e$ I. }9 l" C% |& q

    , Z0 |' ]5 W- {1 V/ opd.timedelta_range('0s', '1000s', periods=3): E9 |6 U7 N8 Y0 Z+ z9 q" b
    Out[63]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:08:20', '0 days 00:16:40'], dtype='timedelta64[ns]', freq=None)
    6 a$ b$ g+ n4 K. i* p2 k6 B16 w6 T6 h6 E2 M8 n+ d* H" }4 n
    28 q4 V3 [4 |3 j! l& @% H/ u% d3 m
    31 W9 Z6 U: r1 w' K6 C* Z
    4! {# J2 b1 K+ g
    5
    1 p0 S: I( l% ~" ?4 L$ K  ~对于Timedelta序列,同样也定义了dt对象,上面主要定义了的属性包括days, seconds, mircroseconds(毫秒), nanoseconds(纳秒),它们分别返回了对应的时间差特征。需要注意的是,这里的seconds不是指单纯的秒,而是对天数取余后剩余的秒数:
    % @! m% C, R& z0 us.dt.seconds.head(). U/ X4 ?% `% F* Q9 k
    Out[64]: 7 `7 d! @  r" Z7 w2 c+ k
    0    274
    / y0 S4 d! V; s* U. A1    260
    # J5 d* B( T9 D" d& U1 p2    322# u! e0 A9 J) N! J, Z
    3    2487 h$ @9 u6 p  j
    4    322# C1 c9 X( ]8 q4 s* M
    Name: Time_Record, dtype: int641 u6 T3 P+ w" R( y( V% Z; i
    16 r  E! K3 y) y# Q( E9 x4 D; M; g
    22 \& o: o( c/ B7 r# L% H& v
    3+ v: v" }5 \) P3 n5 t9 ]6 a
    4- d; ?2 \4 `" M
    5
    ( {5 b8 `4 d) E" E. W6# K4 O/ ^% o0 R6 Q  K
    7: t8 a% }- E. t, \" F+ i
    83 Z* {: C3 H7 z9 z; |0 ]
    如果不想对天数取余而直接对应秒数,可以使用total_seconds2 h( X# E: _" n) k+ Q9 ~

    , _! e  ^! u' e  m0 e# ^s.dt.total_seconds().head()6 B1 }  X6 L0 ]( O- i9 E# p
    Out[65]: " A1 o* y2 O6 s
    0    274.0
    0 f. v, a! D' [, e3 U9 D1    260.0
    + S! w& d; A' h' }& B2    322.0
    & H' Q- Y( [7 Y( f5 ~8 O4 I5 {3    248.0' o. U! m) r+ [; R
    4    322.0
    ; i" }8 i& s# I) a& C5 v) {Name: Time_Record, dtype: float64, j  i& D% U: t) k- T% @
    1, Q: h7 v  l6 t, R  s
    2
    1 f. V% i, q; A" P$ Y3
    0 [  D) h" {! X( J$ ?% Q4
    * a  m# R+ w( e) t# b1 k2 g5
    3 V0 N2 @/ p- s( C  N3 r6 E64 M0 Z5 R) Q/ W. X* c0 I( u
    7
    / N, E4 \1 _9 ~0 L- q- s1 o! N8& B2 i) n- i7 m4 i: |, k- R7 _% n
    与时间戳序列类似,取整函数也是可以在dt对象上使用的:5 c) u+ o2 I4 K

    , o% O0 v) R; \) Ypd.to_timedelta(df.Time_Record).dt.round('min').head()
    ! ~/ i' o% z. P5 t- B- ?# c, }Out[66]:
    7 H$ U2 K. R* I. g0   0 days 00:05:00
    " }# p0 d- t9 [- J; h7 ~1   0 days 00:04:008 w1 v# N; [. p  ^
    2   0 days 00:05:00
    / J# A4 b5 i/ O, c3   0 days 00:04:00; i5 Z1 D0 c6 _2 o6 K& y- N8 J
    4   0 days 00:05:00; Y, `" r/ F3 O/ p4 c) W  ^% U
    Name: Time_Record, dtype: timedelta64[ns]
    / T9 L8 @# N9 ~* f  d0 Y! U1$ ]$ ?1 M; D; y
    2
    7 v" c5 T( B" e- K3$ Q/ a+ v+ ?4 {' R: y* v" g( B0 O1 b
    41 s( g* r- S; X! q7 O7 ~  j( F
    5
    7 I  Z) f. ^+ W66 ]) U: U+ e& _$ _$ t! f6 `/ T
    7
    $ P2 {5 l# |$ f8& Q# v& L* M* V: `) d$ M+ X2 H
    10.2.2 Timedelta的运算
    ( V# U- P. l$ F( {单个时间差的常用运算,有三类:与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算:
    8 f2 q6 O# L3 Std1 = pd.Timedelta(days=1)* i7 a# l. G+ [3 v3 c
    td2 = pd.Timedelta(days=3)2 Y% b( y- S* V
    ts = pd.Timestamp('20200101')
    3 V- t9 S3 C" B1 y2 J" p; a8 n/ L6 Y3 W  c+ n. P
    td1 * 2
    , d9 k  [( U1 h6 i, sOut[70]: Timedelta('2 days 00:00:00')
    / v% t3 b. Z/ y
    9 T5 j  K3 b; r* Ktd2 - td1
    / ^3 ^- \% Z( S* K/ }& COut[71]: Timedelta('2 days 00:00:00')
    ' y1 H) l( S, F
    # g. [5 A) u2 [! ~) \) o) z1 tts + td1$ W/ G  v1 w  L5 ], E' c" g6 s
    Out[72]: Timestamp('2020-01-02 00:00:00')
    6 c8 ^- ?5 Z1 }3 f
    ; x; C. `1 w3 [& D1 I* t% }ts - td1; O% [+ g- e* U( r
    Out[73]: Timestamp('2019-12-31 00:00:00')6 L( y+ s/ }" j1 {% u
    1
    ' `: u/ L7 _( F$ s7 Q( M  l# L. R2
    3 p: V4 q& ~% W. n' I6 s9 F; v6 d; j3
    + u9 n" C+ T% j" J8 g46 Y) v7 h4 P- L/ v8 v2 N
    5$ S* ?5 g- N* ]  L/ `. S/ o8 B
    6" N5 s. M( u; u
    7* F1 i; H4 ?( r
    88 ]; N, k; g' h  |
    9" T- M3 C* P: m8 z. o& C" J
    10, ?, ?" S  Y) H6 ?& w. g4 M
    11& c' f$ G2 Y, K( y: F9 \
    12
    4 k3 n5 L9 g3 h# e' k4 y13
    8 `5 R0 j8 v6 {% [14
    5 ~% y% x7 I! u5 E, {+ {154 q' s# s0 ?) d( c" ^- t( _: }) n
    时间差的序列的运算,和上面方法相同:2 l0 j8 o7 ^6 H" f2 \
    td1 = pd.timedelta_range(start='1 days', periods=5)
    : U' C; n/ h- P' k& W1 Gtd2 = pd.timedelta_range(start='12 hours',6 o' _2 P) s0 @3 ?( \
                             freq='2H',
    * N1 I: H6 A. I9 ~                         periods=5)) U/ z& J3 G! s3 ~: L# h3 @
    ts = pd.date_range('20200101', '20200105')7 v- \1 p% p. j
    td1,td2,ts8 j; e2 y0 U7 h4 I! U
    ' h, ~6 I* _0 v/ F
    TimedeltaIndex(['1 days', '2 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq='D')
    / y, G5 e9 u( `1 tTimedeltaIndex(['0 days 12:00:00', '0 days 14:00:00', '0 days 16:00:00',3 @! R$ a8 v7 f. t
                    '0 days 18:00:00', '0 days 20:00:00'], dtype='timedelta64[ns]', freq='2H')
    6 g& x1 w) M  E/ F% ]DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',1 u1 X, `2 U) d, D1 t7 W) m1 ~' @
                   '2020-01-05'],1 A4 a/ m" G8 d, w" C4 [+ d
                  dtype='datetime64[ns]', freq='D')" n+ e. k* Q4 ~6 h
    1
    & b+ D9 F' v% C2 _8 [2: k+ T- N( a1 i5 _0 O3 p
    31 Z: |0 @7 X8 @' c: y
    4/ m4 m8 u, z; l( D+ g
    5$ w$ f6 U! P/ j+ v
    6! J6 r) S1 d  K/ |- y/ w5 [
    7+ w0 [4 ~: \6 l9 e
    8
    " N( o( Z- f+ t* e$ l9# G9 Z4 X; e! \9 g6 h# |
    10/ n- W+ M7 k& w) e6 w: k2 K* _
    11
    7 a4 L4 E1 h' p4 p# j) j12
    : ~' O0 i2 v1 ^4 I1 T3 g( F4 _13
    ) x/ J0 Y8 }4 Ftd1 * 5
    8 e( o) G' [! x- q9 b0 QOut[77]: TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D')! b" ~- v8 n; Q  g; N# M
    . b* a" _& a8 A
    td1 * pd.Series(list(range(5))) # 逐个相乘& O( H2 V% f9 j3 T4 }$ ?6 r) J
    Out[78]:
    1 O% R, [0 P" D2 W9 n0    0 days
    & D% _( ~) S' Y. J" z+ \1 A$ H1    2 days
    & u: _5 i& d8 R6 L$ |+ T6 X/ e; y2    6 days
    4 u  a: U% N$ ]7 D% v; S3   12 days
    $ n/ |# C. t- s; O4   20 days4 a- e- ]: T# K
    dtype: timedelta64[ns]
    4 K! c1 ?2 ?: y0 z5 J; x5 X1 A3 Q$ i
    td1 - td2$ B/ T9 P4 k, Q! W
    Out[79]:
    " M, f* a5 `# o! M' zTimedeltaIndex(['0 days 12:00:00', '1 days 10:00:00', '2 days 08:00:00',
    8 n1 t: b! S) c* k                '3 days 06:00:00', '4 days 04:00:00'],
    # k: S! H4 {: L/ _               dtype='timedelta64[ns]', freq=None)$ F$ x9 z3 S$ D( B# G( @5 ~

    % Y' t* c: W* h' f4 P: ktd1 + pd.Timestamp('20200101')
    8 R/ S% ~* ^. ~% z: s; x2 R% }Out[80]: ) H: S8 X( F# J7 c
    DatetimeIndex(['2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05',
    4 I3 A$ L" m1 `6 Q( L2 l4 t               '2020-01-06'],dtype='datetime64[ns]', freq='D')
    ( A! y' {; H+ K; Q% L: B
    6 V" J( j: a" u5 {, Vtd1 + ts # 逐个相加
    ; W  @- e- _( Z, u7 P% [$ sOut[81]:
    " Y. X( X( X& B& c  i8 H. C+ |% x- @DatetimeIndex(['2020-01-02', '2020-01-04', '2020-01-06', '2020-01-08',
    2 S$ A% `8 ]1 z2 ~: P6 m$ v. j               '2020-01-10'],& r4 J8 z6 b1 A% P* g; R9 a1 E
                  dtype='datetime64[ns]', freq=None)/ j1 m! E. F& Z9 ~

    2 j0 t$ V( |/ x: V4 u0 z0 j- X1
    : S3 Q# E- [8 l7 y4 O5 b' w0 }23 D' `' e1 O, y4 ~
    3
    . g0 F9 z; P" ?% f4
    & F& |1 ?9 u* W. [& h5. U% S8 V4 p/ w
    6: t6 F  e1 @! Y
    7
    2 N5 L4 C: n& r4 w8( H9 G, Q( Y5 f1 N
    9
    8 b9 Y5 w/ {) l5 K' p2 ?10
    * b, K! C2 T  q# R. u6 |8 Q2 @11
    9 \& u- k" q1 d- c125 @& k' N% f1 B/ }# C
    13! x* {0 Q: L3 j
    14
    , Q- A: G4 l! C' G' x" _7 E( E+ H7 l# @155 ~1 _; E6 G0 {5 i
    16: [# c2 c; l! _' M6 D
    17
    . @& b4 m  q' ^7 Y18
    & @; u7 T& Q: Y( _19
    1 a' q* j6 O: l6 y  s; L20" j$ j- J/ b7 S2 n/ q1 f
    21
    ' Y2 @; n8 k! U  u; b( h( i8 ?) H, k224 p4 q9 s' P6 ~* E
    23
    ( S' [9 W6 h/ S& B& x9 g. i24
    " A8 S9 _4 f  G. d: T25
    - @" ?- R/ N2 f+ I26. }0 v/ t9 h' |! K0 A& u, Z
    27
    & E" J4 a: F4 H  o28$ g+ c$ b+ X/ v0 ~. H! y
    10.4 日期偏置
    ) H. v$ E" y( v9 m) V0 E3 R10.4.1 Offset对象
    2 L. Q: o# ^3 O5 t  日期偏置是一种和日历相关的特殊时间差,例如回到第一节中的两个问题:如何求2020年9月第一个周一的日期,以及如何求2020年9月7日后的第30个工作日是哪一天。% ?2 a& \4 I# A% Y. i
    ( ~/ G4 C4 O, Y+ S& s
    DateOffset 类有10个属性,假设s=pd.offsets.WeekOfMonth(week=0,weekday=0),则:
    ) N9 C/ W8 Q) M; h* I4 @
    ' i. L' o. P6 U+ o1 W1 B3 Ps.base:<WeekOfMonth: week=0, weekday=0>,返回 n=1 且所有其他属性一样的副本
    ( u% v2 f1 a% Ms.kwds:{‘week’: 0, ‘weekday’: 0}- P* \- W) |1 \! Z: e; S
    s.wek/s.weekday:顾名思义" z+ R+ @$ `# A
    有14个方法,包括:
    ! [$ V  K% Y+ L5 |6 K7 _; _" ]) b1 r, t
    DateOffset.is_month_start、DateOffset.is_month_end、DateOffset.is_quarter_start、DateOffset.is_quarter_end、DateOffset.is_year_start、DateOffset.is_year_end等等。" e* k# H& l1 r# ~. n  G
    pandas.tseries.offsets.WeekOfMonth(week,weekday):描述每月的日期,例如“每月第二周的星期二”。
    ( f, S& g7 o! W- O
    3 g6 ~1 t- s6 B/ _% }+ B有两个参数:5 V1 s8 C- o* ?
    week:整型,表示一个月的第几周。例如 0 是一个月的第 1 周,1 是第 2 周,以此类推。$ o( |  E6 y5 G# R( T, B
    weekday:整型,取值为[0,1,…6],表示周一到周日,默认取值为0(星期一)
    ; R6 K* D% T% K) U2 n; `! N$ {pandas.tseries.offsets.BusinessDay(n):相当于pd.offsets.BDay(n),DateOffset 子类,表示可能的 n 个工作日。: r6 Q8 H! L2 d# S- Q, @9 [) u

    : D# Y/ s% k7 K( S: opd.Timestamp('20200831') + pd.offsets.WeekOfMonth(week=0,weekday=0)
    # U5 s0 @0 J( v8 D5 V5 }2 X& B$ sOut[82]: Timestamp('2020-09-07 00:00:00')! E6 ?- J6 r: e. d/ V

    ; G2 K9 t& H; {6 M5 B4 [4 _3 @pd.Timestamp('20200907') + pd.offsets.BDay(30)5 u: v0 J4 g- h+ A* A
    Out[83]: Timestamp('2020-10-19 00:00:00')7 }8 X  |2 x2 b. {
    1
    1 V7 S9 O$ V0 q. l, D9 s2
    7 X( d$ N7 V1 M, N6 C3
    5 e5 s+ n/ v9 [9 }4
    ' B- g$ W# ~9 h5. j! Q  f5 J. B$ x
      从上面的例子中可以看到,Offset对象在pd.offsets中被定义。当使用+时获取离其最近的下一个日期,当使用-时获取离其最近的上一个日期:
    2 y/ I, Y1 k2 t  ?, p2 G- D
    , l& o, j6 J7 Q# x# spd.Timestamp('20200831') - pd.offsets.WeekOfMonth(week=0,weekday=0)2 W( k" m4 Q, M+ |
    Out[84]: Timestamp('2020-08-03 00:00:00'): ~5 w. `; l1 e9 g( y; X, G
    ! \0 ^: ^* Y+ Q6 Q' T) |. Q0 m
    pd.Timestamp('20200907') - pd.offsets.BDay(30)! c+ C4 c8 ]0 T% n2 m1 b0 @/ e
    Out[85]: Timestamp('2020-07-27 00:00:00')5 C+ J% \2 ?5 K4 A% f: ]+ q- g9 T& ^

    1 Q7 ^# C+ R- L: H0 }8 s; X# M4 ]% npd.Timestamp('20200907') + pd.offsets.MonthEnd()6 x* v9 s. T+ O& u0 p" E6 E, }  [
    Out[86]: Timestamp('2020-09-30 00:00:00')5 _" C4 V" U$ K8 P& O! J2 c2 x9 K
    1
    ' y4 s( `7 k: k; n' R$ ^28 g/ [) K! L, `# ^9 k& [3 P
    3/ l# u9 B( J! Y. u% H* \; F
    4
    ; b/ N; z# d- N: B5
    $ b) H/ e% x$ B60 L- j. F6 Y( Y) X% W
    7' P* W# q7 Z' H5 i% R8 \) Y* s
    8. D% }1 R# ~9 C0 n& j7 y
      常用的日期偏置如下可以查阅这里的DateOffset 文档描述。在文档罗列的Offset中,需要介绍一个特殊的Offset对象CDay。CDay 或 CustomBusinessDay 类提供了一个参数化的 BusinessDay 类,可用于创建自定义的工作日日历,该日历说明当地假期和当地周末惯例。
    3 e& Q9 p6 ?8 y7 T1 P5 w) Q7 J2 v6 X  其中的holidays, weekmask参数能够分别对自定义的日期和星期进行过滤,前者传入了需要过滤的日期列表,后者传入的是三个字母的星期缩写构成的星期字符串,其作用是只保留字符串中出现的星期:) G" Q7 t$ [0 I' Y; y, X

    ) o, O$ y" T4 s; d7 pmy_filter = pd.offsets.CDay(n=1,weekmask='Wed Fri',holidays=['20200109'])* I4 s! D& o4 |( N4 x, T: F
    dr = pd.date_range('20200108', '20200111')% Z0 \) ]; j( z$ ^1 c/ q4 d2 l. q, s

    / z5 E. a* B/ R1 O0 cdr.to_series().dt.dayofweek
    , o' V2 k" m1 M. M$ i, kOut[89]:
      F$ T+ b! f5 x2 y# A7 H5 d, f2020-01-08    2  g3 D9 b: W7 i! `! }& H0 @0 q
    2020-01-09    3
    + @4 d( z9 {) P% ?( }+ S( r2020-01-10    4( A3 U6 B* j" H
    2020-01-11    5# k; q; N+ k4 J
    Freq: D, dtype: int64+ W2 w/ V  h  d
    9 p% L) `0 j: s5 o
    [i + my_filter for i in dr], @9 @8 p0 b4 i! z+ G: a
    Out[90]: $ P4 B) P( ~3 v8 H
    [Timestamp('2020-01-10 00:00:00'),4 l* b2 ?% ~$ o8 n) h3 o  c7 r9 c0 X
    Timestamp('2020-01-10 00:00:00'),
    7 \+ `+ p- F, Q" U Timestamp('2020-01-15 00:00:00'),
    $ X% H0 s1 d3 Q( N5 a' p/ k- d+ g Timestamp('2020-01-15 00:00:00')]
    $ k) Y0 G$ i# C* F" t$ Y, e# p' m+ `4 p. u" c* h
    13 M  j9 [2 G7 H9 h
    2; @; J5 t% \: C8 |/ F2 x+ J* y
    3
    - u- E7 U8 U. {0 k3 m+ m1 m4
    2 y2 W1 [2 q! s56 C. p9 ?% x; l9 r8 j
    6# a' o, Q) d6 ]3 Z- Q& ]
    7; Y8 X- _' o% h! b
    8+ o6 B& l6 T6 B4 I0 W
    9
    6 h  m& j6 s6 \" J$ q) }10# k5 o4 k* @% q
    11
    0 m: y7 r5 Q8 ]; t$ ?( _! y128 b( _+ C' [9 I: H
    136 W+ X, q  z8 g$ X8 ]8 I7 S" T, `
    14
    ( w, h6 H6 w" Q/ A1 N) r15: r/ k& j( h0 E6 b. ?' W" R
    165 ~0 q: e- u! k% o* e2 o/ B
    17
    4 r9 l$ D& G; s0 a% u  上面的例子中,n表示增加一天CDay,dr中的第一天为20200108,但由于下一天20200109被排除了,并且20200110是合法的周五,因此转为20200110,其他后面的日期处理类似。/ [" f( S1 V9 v* g/ V: m( l

    - k) m9 @1 ]3 c% _2 F【CAUTION】不要使用部分Offset
    / v: t' W# i) G在当前版本下由于一些 bug ,不要使用 Day 级别以下的 Offset 对象,比如 Hour, Second 等,请使用对应的 Timedelta 对象来代替。
    " v5 @, m* a3 p1 U3 D8 X* v: G
    . i9 _' a% i) F3 f' U, Y$ Y( w10.4.2 偏置字符串
    : J, S3 Z4 A+ I5 B, U' V. q  前面提到了关于date_range的freq取值可用Offset对象,同时在pandas中几乎每一个Offset对象绑定了日期偏置字符串(frequencies strings/offset aliases),可以指定Offset对应的字符串来替代使用。下面举一些常见的例子。0 K' n) f3 g  [, f. E
    $ Q$ q  x3 A# k0 ^: ~
      Offset aliases:pd.date_range函数中的freq参数,为常见时间序列频率提供了许多字符串别名。 也称为偏移别名Offset aliases。偏移别名列表点此参看(大概27个)。4 i& f9 }+ A, U& e6 M3 w
    ; @( K6 E& j3 [  k; x, Q: Y+ Q
    pd.date_range('20200101','20200331', freq='MS') # 月初& ?& s" o, W0 @  E5 ]: Y
    Out[91]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')
    * \' p7 V" ^3 t& _- F4 `$ o
    . I. i, _' k  T. D: Fpd.date_range('20200101','20200331', freq='M') # 月末
    $ A; P& j& Q$ R3 {Out[92]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')8 P2 Z* `9 Z: m' t) ?( v* P
    4 W& o4 W) E7 r. h
    pd.date_range('20200101','20200110', freq='B') # 工作日. R, a! c- v+ b
    Out[93]: 2 V. p* b$ B, a4 {
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
    7 l% i4 ?# j# T4 g* }               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],7 K9 `3 q* Z  z. J0 M2 R* @" ~
                  dtype='datetime64[ns]', freq='B')0 L) L. ]) A; g: A  L
    + W& ^5 n3 e0 E! o5 g; R7 |, v
    pd.date_range('20200101','20200201', freq='W-MON') # 周一
    ; \) g0 g1 o9 ~( Z% q- a* sOut[94]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='W-MON')
    , o6 f/ G+ {3 F4 k( I2 q3 X
    ( }/ p1 k" n, {8 V- ?8 lpd.date_range('20200101','20200201',
    5 H  c0 E! O3 z  k              freq='WOM-1MON') # 每月第一个周一
    * M& ]( V. J5 N
    # u' D* p; m; \& ^Out[95]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')
    ) U; E% e6 |! W% H) n6 q& @' p
    5 M; {* e9 E4 f& K" @: f1
      k5 ^" A3 s/ d" w* C% j4 Z2# Z3 [2 n5 |4 _
    31 P  X; E. S3 q% c  k. I& n% L
    4
    7 h- e2 B% w; q! Z: B$ c+ C9 f3 q5* X$ V, w( P$ J! ?
    6
    , N8 h* O% Y: w" r7 A! C: _6 S7  {( b+ R# {' t3 F
    8
    5 L; d6 ^# f- X8 Y9
    8 W- ^4 Q8 g' a# E10' m1 Y% j, k6 d, Q& l6 ~" B7 Z
    11% C" K8 p" m' E3 w
    12
    7 O' V4 l: b7 ]3 S3 z5 i+ i13
    8 T9 Z. a, P+ q- a* N+ T  O148 I1 r, x( I* }1 M2 K( u! l
    15# h1 f; E  u3 x6 t5 d( v
    168 ?/ Z  {/ J8 z0 z; ^% g. G0 x8 r
    17
    ! I8 O6 V5 l9 l/ N+ g: }/ N18
    : L. K- B6 Y* G. l) q; E& V1 `19
    , q+ s2 U6 j/ m- z* Y" K上面的这些字符串,等价于使用如下的 Offset 对象:. L9 c9 \. Z$ }6 j/ t+ Q- ^

    % m8 n7 o1 o% O& U6 X, @pd.date_range('20200101','20200331',
    / K  |5 n* T, B; y3 {+ w( d              freq=pd.offsets.MonthBegin())
    8 s0 \7 ?& r8 M( m' ?' ~
    2 l3 H6 t" W" r1 n  H( LOut[96]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')! N$ {+ w* v  D0 H

    ! l9 z4 v* L/ X% }pd.date_range('20200101','20200331',& H) d  J. m  y* _# q; ]( ?
                  freq=pd.offsets.MonthEnd())
    1 D1 w/ A$ M* k( M( U7 Q- ?% |9 D) y6 F/ i! h
    Out[97]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')
    1 A8 a6 q2 j: a5 R1 O/ A
    ' U( y& b" o# `$ ?1 q  h4 K. Epd.date_range('20200101','20200110', freq=pd.offsets.BDay())7 K5 d* n6 I! Q1 I8 e8 r6 {1 p4 X
    Out[98]: . k% l1 ~8 X9 ~- O' {/ V/ u. u1 y
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',8 K9 x; q  t! O
                   '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],
    0 \& H! o/ ^; d% k              dtype='datetime64[ns]', freq='B')! c# c: ~+ h0 k# f( `; ^$ A1 u
    6 T' `6 q! ]5 g# L/ C5 R
    pd.date_range('20200101','20200201',! n6 y/ j6 |+ R
                  freq=pd.offsets.CDay(weekmask='Mon')); h& W7 n& L6 _# f0 f) G

    * l' D3 H! `! I6 fOut[99]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='C')& B; e+ R% m$ d4 B6 o

    ) q+ s" }; }. d8 `. xpd.date_range('20200101','20200201',
    & |( c9 {4 v# P2 v7 e              freq=pd.offsets.WeekOfMonth(week=0,weekday=0))
    7 q' h% c' ^! j: C$ P
    ' ^8 K! ?$ h6 K& w) Z* F2 q( X; {$ G/ hOut[100]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON'). V3 y! V- I5 w8 c
    & ~" Y, W: Z( v3 ^7 q
    1) _3 Y9 M, n7 |/ M: c5 O
    2  y& j# B- G; f- Z6 v+ E
    37 Q0 K( C" E0 v' M# h
    4  T6 I6 I) I. B3 U+ s
    5) i; X/ h! P3 s$ V" i
    6! @0 p% V) o& R0 X) q, n5 w/ L8 P
    7
    , ^8 N% ~& ?$ @0 y8* b) N% `$ a) R# Z
    9
    : m5 ^  z: w3 M* c* I+ A0 Z106 Z% y5 Y. p7 ^0 b% u, B
    118 s# F* @7 o1 V9 J
    124 ~2 g! H: V2 j& d3 O4 r
    13$ o5 _. Q+ p3 ?/ a7 _
    14
    5 }' N4 T  r7 M2 O0 X/ f15
      C* c3 f* p0 ?% q3 y. R7 v2 R16
    0 o0 F& H  u, H17; N$ X, q* D6 R7 f9 W7 t
    18
    2 S: @. F2 o" z1 z* ]! h3 R198 j( d7 _: x* H5 L3 Y7 x* Q
    20  b( V& u! M- ~" R) ^& N8 k
    21
    ) F" I) z5 d, y' B22
    4 j. Q( @6 b5 k. L7 F1 q/ A9 U23' y# f/ S1 m: {* W9 I. \/ k. _, l
    24
      d( K9 \5 w3 [/ N( j( f$ t3 m/ b25, c2 B" }, u2 P6 x6 t
    【CAUTION】关于时区问题的说明
    - L# y! N9 o. ^. [; u0 b& A  各类时间对象的开发,除了使用python内置的datetime模块,pandas还利用了dateutil模块,很大一部分是为了处理时区问题。总所周知,我国是没有夏令时调整时间一说的,但有些国家会有这种做法,导致了相对而言一天里可能会有23/24/25个小时,也就是relativedelta,这使得Offset对象和Timedelta对象有了对同一问题处理产生不同结果的现象,其中的规则也较为复杂,官方文档的写法存在部分描述错误,并且难以对描述做出统一修正,因为牵涉到了Offset相关的很多组件。因此,本教程完全不考虑时区处理,如果对时区处理的时间偏置有兴趣了解讨论,可以联系我或者参见这里的讨论。- d& B% G7 B' |: w% |% k  J
    + \% i  j/ U% x) A* g
    10.5、时序中的滑窗与分组" S" [  W. E! h: W
    10.5.1 滑动窗口0 O$ n" b+ v/ ?1 z2 U
      所谓时序的滑窗函数,即把滑动窗口windows用freq关键词代替,下面给出一个具体的应用案例:在股票市场中有一个指标为BOLL指标,它由中轨线、上轨线、下轨线这三根线构成,具体的计算方法分别是N日均值线、N日均值加两倍N日标准差线、N日均值减两倍N日标准差线。利用rolling对象计算N=30的BOLL指标可以如下写出:
    , [7 \, {7 d4 D; f
    / c$ X6 P/ [6 k5 c) X  W/ |import matplotlib.pyplot as plt) y! q. y( f6 q2 N# X
    idx = pd.date_range('20200101', '20201231', freq='B')( u. ?& l; j2 k. t  P
    np.random.seed(2020)
    0 z0 J  E8 h" L6 d
    % ]2 F$ O! q3 T  Q$ p# M1 P, fdata = np.random.randint(-1,2,len(idx)).cumsum() # 随机游动构造模拟序列,cumsum表示累加
    ; L% i, ?5 r  t" Ts = pd.Series(data,index=idx)
    9 o& c1 ]( H  u& Js.head()" Y* a& \3 O/ g9 F! K% L. R; _
    Out[106]: 9 R- y# f+ S. E/ W
    2020-01-01   -11 F3 i, l- Y) c, U! M9 V
    2020-01-02   -25 g; a4 V0 S; t- ~; s5 r# x% l
    2020-01-03   -1
    " r8 I: q, x$ Y2020-01-06   -1
    ) {- c2 I; Y8 [& ^2020-01-07   -2
    # L+ Z- H6 }$ @# ]Freq: B, dtype: int32' u' o& o% t' D! |$ O4 \
    r = s.rolling('30D')# rolling可以指定freq或者offset对象+ h% V1 F/ c% U# H
    + e0 f' q- C1 X$ D
    plt.plot(s) # 蓝色线
    - p3 G  F8 D6 t( U3 oOut[108]: [<matplotlib.lines.Line2D at 0x2116d887eb0>]7 P2 v) X: X* D: X/ b
    plt.title('BOLL LINES')
      `  j, E6 @! E& S" q1 A, SOut[109]: Text(0.5, 1.0, 'BOLL LINES')1 i5 ]5 f* ]3 N; t
    $ U' l' J0 B5 A5 a1 c& [' `
    plt.plot(r.mean()) #橙色线
    / i: r, j4 D" _; `) v9 @Out[110]: [<matplotlib.lines.Line2D at 0x2116d8eeb80>]
    8 Q4 X3 h1 Q$ ~2 D
    2 H& x; a+ j7 `( \3 Vplt.plot(r.mean()+r.std()*2) # 绿色线' P2 @$ }4 g: H& i. e
    Out[111]: [<matplotlib.lines.Line2D at 0x2116d87efa0>]
    . M/ x, A, d1 G
    " g  q- N6 X4 k  |plt.plot(r.mean()-r.std()*2) # 红色线6 L- [3 _# q2 r% [7 _8 t1 o
    Out[112]: [<matplotlib.lines.Line2D at 0x2116d90d2e0>]
    / n5 s6 g( l2 g3 w( v
    1 e% y6 B- y5 [* N  b1 {7 \$ Y( e. ?: Y11 |9 k8 t" P. o! L) A
    2
    5 L3 q% q- L+ ~& e3
    3 v; v* y$ V9 y' N2 m, I4 p& `% A4% c) i, d5 k& ]% ~8 U$ S9 \' o
    57 k) e- p, p% ]/ |* @
    6$ l( M+ |& X6 O" i
    70 ~* X0 f% W5 x7 a$ P
    83 q/ }& V& E$ P; m! C  ]
    9( `4 s. b) @4 @: U
    103 ]3 U' ^" j* @+ A! J0 _& T8 \
    11
    - W5 B* j7 u/ i9 v" ?12
    ; d  T9 s2 J, ~- D& S13
    ) z+ W; P/ m: X  ^7 v/ A+ W9 v14
    & T7 W9 R5 ^' G4 f1 A15
    $ i0 T/ R! t3 R; h/ K16) T' k, l  L6 A2 J" z
    17
    6 W, J8 S) \5 k; E5 u! g% k% Y0 S: x18
      K! E: E5 ^# y6 O19& E$ d% e. G/ n6 G. \! J
    20. f* H) [" g: O2 Q- F8 j8 I5 v7 P3 o
    215 O7 l$ T: \& y2 N
    22& \% F8 f) w$ G5 H
    23
    : h+ _, D! J: k  A+ Y24
    . I1 @* m- e2 K25/ j& H/ |) s8 V5 u5 S
    26+ ?3 z* X1 [) f0 i6 t2 j2 N
    270 k% U2 }% {( g" @
    28
    ( b5 _. ?+ }$ F3 X# M292 `8 |, t# V  ?8 h

    " W2 c5 A2 L3 R- {0 j   这里需要注意的是,pandas没有实现非固定采样频率的时间序列滑窗,及此时无法通过传入freq字段来得到滑窗结果。例如统计近7个工作日的交易总额。此时可以通过传入多个函数的组合来实现此功能。+ j+ f1 u: B# J5 I7 J9 k& n
       首先选出所有工作日,接着用普通滑窗进行7日滑窗加和,最后用reindex()恢复索引,对于双休日使用前一个工作日的结果进行填充。
    0 O" K& M6 e4 k2 v; |
    $ B& X( p: t3 u* Rselect_bday=s[~s.index.to_series().dt.dayofweek.isin([5,6])]
    1 [2 r( h& u  b) c) t. fbday_sum=select_bday.rolling(7,min_periods=1).sum()* `( s* F# |- k' Z6 o& j. f
    result=bday_sum.reindex().ffill()
    1 T9 ]- g% X* i$ @5 S9 |+ W' Y1 t/ |result
    + |7 e0 B/ n# Y: F( W/ m# K, F3 e' h9 [  [4 Z% Q
    2020-01-01     -1.0
    ! U1 I. S8 J+ x2 C3 s$ `* O  i' S4 ~" }2020-01-02     -3.08 ?/ y* q$ n9 m! r
    2020-01-03     -4.0; ^( P! O+ p5 o. t5 B9 s
    2020-01-06     -5.0
    ! V) W6 W2 M7 y) B6 b2020-01-07     -7.0
    * o; U3 C& _8 X$ u+ y              ...  5 d- B+ A7 H: p: {9 u! s
    2020-12-25    136.0
    5 Y# W& @" L/ b! E5 t  ^2020-12-28    133.0
    ' a8 @# {& z% H' |/ [5 ]& I2020-12-29    131.0! g+ r0 _% I2 }! l8 V: Z- @- C3 e
    2020-12-30    130.0
    6 p5 O$ [- _/ V0 }" Z3 A2020-12-31    128.08 G/ K2 s' ]) _: `8 [, t3 p) g
    Freq: B, Length: 262, dtype: float64
    * r( `, g! N* E/ a7 R! C# m) }9 }5 L, S4 ~4 S, ]4 G/ |
    1% q) K3 j1 X/ C5 \/ @
    2) z% ~* }! f7 p5 Y! v4 F9 U
    3. E  x* o( V6 ^; x) N- _4 S
    4
    : y6 S- \, q5 H  O( q5" P8 F  O! w1 \1 S  c# ^
    6
    . |; Y8 T, h6 g7
    ( c$ F& J+ F% b! e/ N7 ^89 _7 R" o+ E6 {7 x
    91 K' I- k* c# I1 u; H
    101 t& q9 I0 I+ @" y
    11
    - f3 Y# H7 e) E125 H- F8 x7 q. k
    13
    6 H! |- \1 V. W3 Q0 F4 M2 E2 B; ]14! H7 E" S' l$ `# W
    15! n4 v  n$ O2 y9 y
    16
    # e+ j. p* N% i: @- @, I17
    ; G1 U, j& l/ ]$ x% K' ^5 C  shift, diff, pct_change 是一组类滑窗函数,它们的公共参数为 periods=n ,默认为1,分别表示取向前第 n 个元素的值、与向前第 n 个元素做差(与 Numpy 中不同,后者表示 n 阶差分)、与向前第 n 个元素相比计算增长率。这里的 n 可以为负,表示反方向的类似操作。
    & B, S* X; P. o# I# e0 G
    3 C' Y# x3 y  H  f" y  对于shift函数而言,作用在datetime64为索引(不是value)的序列上时,可以指定freq单位进行滑动:. T5 g& n- ]3 I+ d( z) G4 q2 u

    7 N, z& @$ i' }& p( J# y1 O- \s.shift(freq='50D').head()* `- `. i( f' T* F7 C3 J
    Out[113]:
    ! y0 a/ C% G* a8 \& D2020-02-20   -1
    3 u1 E& z' m) ~' S0 N( D& L2020-02-21   -2" f& r' V9 A5 [( l& u) U* ]
    2020-02-22   -1$ {2 }; r0 j/ G7 T
    2020-02-25   -15 B! g, f4 m9 A
    2020-02-26   -29 U+ W6 L) Z9 O3 o, w$ @
    dtype: int32
      t2 L# _! Y; w% p18 o3 j& F: A) P* ~
    2
    8 }+ f9 f) s1 h! C+ F39 A" j- J4 W9 w
    4
    ! M2 d& c; W4 W/ I5
    8 d9 ~5 `, m2 D/ S& |- H+ [% H% m6
    0 a* A: E3 U  {. ^8 m' G74 h6 b1 \( L$ Q3 w" f* x2 ]0 x
    8% @* a# T* H+ n% A/ n+ L' ]; a
      另外,datetime64[ns]的序列进行diff(前后做差)后就能够得到timedelta64[ns]的序列,这能够使用户方便地观察有序时间序列的间隔:
    ! |- j! a$ r9 s; `2 F4 I7 \/ R$ ~4 D8 M* o; Z, F6 y8 \
    my_series = pd.Series(s.index)* d6 f4 v* n) n8 ?! s5 \5 U% Q1 x
    my_series.head()8 g' K0 G+ ^- {! L1 B1 Q% |
    Out[115]:
      S6 N5 |) @$ ]" G- A0   2020-01-01
    : c9 |+ d# U! v$ E' M1   2020-01-02
    7 {9 j1 ^/ Q' E$ j. G) Q7 a, Y2   2020-01-035 J) U! {0 L3 v( |. s+ n6 K; R
    3   2020-01-06  ~4 b9 @; q/ }( G. G/ G
    4   2020-01-07
    9 G* J0 f$ }/ S+ gdtype: datetime64[ns]; X8 x2 W1 M& E" f; R8 D! Y
    0 D1 M: [: s: L' s
    my_series.diff(1).head()
    # ?/ i) \( L" l2 R% OOut[116]: 6 A% X  j- ~+ Z! C7 @: }
    0      NaT
    " N) b1 @& o4 Z$ |. @1   1 days
    % K" z% B% z, f2   1 days* }5 [4 ~* z6 b' R& h% z
    3   3 days
    3 b7 n7 F2 T& ]+ T4   1 days
    - i# _/ j2 V. Ddtype: timedelta64[ns]
    ; Z: b3 X. Z& w! F& O
    ' c5 S: K5 v0 ~" y1
    . O# p/ A8 u+ n( Q, }21 h8 b  {  ~  [7 n' h9 V
    3# T  J* M: c) X  ?' C
    4
    2 Q0 A- a) p7 \0 O/ g+ h5! X2 k7 I" f4 h6 y: D
    6
    1 l: Q# @/ K& h+ F4 j8 f! `( }% \7
    & Q' u8 S( h5 e8
    3 H1 m4 ?, x, A$ W/ w' P) d; K9* N  y4 h+ A# _% e8 U; Y
    10* _5 C2 n  c" R1 D( q6 q: u$ K; H
    113 m* u: P& M% h5 E& ^2 f
    12
    7 Y: Q# o: W5 u- D9 k13
    8 G7 h1 Y  Z2 t2 m; y14
    ; ?  o3 m% a4 q: v, Y5 r0 B# g15; l; W$ K; E2 J+ E% u$ e
    169 B0 n( P/ D) X$ D& w! Q" s) s5 o/ T
    17. F. W! I& T! K& r9 y9 K* b5 y
    18
    7 }9 k: l0 U- S8 k10.5.2 重采样, h0 d) F3 T8 j
      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). ~8 r1 Y4 W4 Y1 E2 Z4 q- ^5 B
    常用参数有:& x* H( N+ p. k# f
    8 W+ `9 }: S, w
    rule:DateOffset, Timedelta or str类型。表示偏移量字符串或对象1 `5 }" Z$ F& Q7 ~
    axis:{0 or ‘index’, 1 or ‘columns’}, default 0。使用哪个轴进行上采样或下采样/ J' q0 ^' i2 W
    closed:{‘right’, ‘left’},默认None。表示bin 区间的哪一侧是闭合的。所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。( g2 V. @2 Q1 y5 D7 m, @; ?
    label:{‘right’, ‘left’}, 默认 None。hich bin edge label to label bucket with,所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。
    % i; E8 L! H! ^convention{:‘start’, ‘end’, ‘s’, ‘e’}, default ‘start’。仅针对 PeriodIndex,控制是使用rule的开始还是结尾。9 M9 x. k) V8 u! l* x# {6 s! n
    on:字符串类型,可选。对于 DataFrame,使用列而不是索引进行重采样。列必须类似于日期时间。
    ! W7 j+ e2 O' W. N( |; flevel:str 或 int,可选表示多重索引MultiIndex的级别,这个级别的索引必须类似于日期时间。
    + r" ~' ~! n; p5 [1 P) D4 j' Eorigin参数有5种取值:
    & k& d: o% V0 |5 y& C8 e‘epoch’:从 1970-01-01开始算起; D# ]! @$ [% Z' m  y$ F: Q6 s9 B
    ‘start’:原点是时间序列的第一个值
    : v1 P( P5 `9 a‘start_day’:默认值,表示原点是时间序列第一天的午夜。
    - H' \: Y  t7 X# W; }4 K'end':原点是时间序列的最后一个值(1.3.0版本才有). @( T, P' n; i7 j1 X/ W
    ‘end_day’:原点是序列最后一天的午夜(1.3.0版本才有)
    5 j7 x0 {! t3 w. f- E' j. }offset:Timedelta 或 str,默认为 None,表示对时间原点的偏移量,很有用。  t3 j3 T- N! N3 |, }+ V
      closed和计算有关,label和显示有关,closed才有开闭。5 s7 [. _5 Q/ @9 a
      label指这个区间值算出来了,索引放区间的左端点还是右端点,closed是指算的时候左端点或右端点是不是包含。+ k1 Y. I$ O, L
      q) a: p7 I0 P& e8 g/ s: \
    重采样对象resample和第四章中分组对象groupby的用法类似,resample是针对时间序列的分组计算而设计的分组对象。例如,对上面的序列计算每10天的均值:1 E! o" O. C& K/ v8 {' U
    s.resample('10D').mean().head()1 }! n* {, Z! E4 j
    Out[117]:
    " S8 E) w6 t/ N. V' i2020-01-01   -2.0000008 Y8 ~6 @* f" Y3 b: V1 K/ |
    2020-01-11   -3.166667
    $ E7 l7 y% @. }! B) h( }2 B) x2020-01-21   -3.625000
    " i2 C0 i' _* S+ C: v2020-01-31   -4.000000
    9 l5 }4 x0 C5 m2 Y3 [7 i2020-02-10   -0.375000
    9 h& u* X; ^# u# ^) G2 FFreq: 10D, dtype: float64- j8 c0 ]+ T6 N# D+ W
    1
    4 T/ X: a( F8 }7 r9 K( ~5 M1 F% k2# M" U5 {) H  E/ m
    32 j5 {$ P- K% B6 p! C
    4# X+ Y  K2 g% {  ~2 Z4 d
    5
    9 {/ `* T1 A! o; x# x, t. |6
    5 o6 k; O9 C! l6 A$ l! z- S; ]$ ]74 u% z2 o) |& C% Y( J
    8
    & r8 ?8 ]4 W( ?! q可以通过apply方法自定义处理函数:9 n+ _; \/ K* P4 l; ?4 W) [
    s.resample('10D').apply(lambda x:x.max()-x.min()).head() # 极差3 b# I4 T' Q) R  f) Y4 s

    1 S$ j" K0 Z' u4 V$ d8 z; rOut[118]: % G/ D" b& p+ N9 \& w
    2020-01-01    37 h5 j3 n7 t- n( e+ y7 ?0 t
    2020-01-11    4% [' P: G0 C7 R- q" g
    2020-01-21    4
    " j4 `+ Q" I' l2020-01-31    2' t& [$ p1 w! ~4 W& O; A$ }
    2020-02-10    4
    2 H# v4 ]+ V. }  C, k9 w0 I  mFreq: 10D, dtype: int324 v' B' b: Y9 f
    1. \7 X6 \2 ?* M' ^" I" m' a
    2
    ; L" U. J9 v- ?* z) |3
    3 e' Z& X8 t) k! v42 {! Q5 d8 G3 B
    54 T2 ?3 l6 |1 B/ A8 P3 [4 r0 v& d
    6
    . I( ]6 B, k+ n3 i* x5 D4 O  M7/ T& L! `0 s4 S# e- Q
    8
    & D6 S, i) o% ~/ p6 r93 s. Y: H  d) `. S
      在resample中要特别注意组边界值的处理情况,默认情况下起始值的计算方法是从最小值时间戳对应日期的午夜00:00:00开始增加freq,直到不超过该最小时间戳的最大时间戳,由此对应的时间戳为起始值,然后每次累加freq参数作为分割结点进行分组,区间情况为左闭右开。下面构造一个不均匀的例子:
    4 C: Y- s1 }3 @! K8 @) d: [( C8 Z9 d$ f
    idx = pd.date_range('20200101 8:26:35', '20200101 9:31:58', freq='77s')
    " o5 Z% a. _  n0 R8 U$ {- O0 qdata = np.random.randint(-1,2,len(idx)).cumsum()# ?7 p) f  a0 ^& L% N
    s = pd.Series(data,index=idx)
    7 E" H! @! w: W! {1 Vs.head()8 C( m! ^/ E8 X+ l
    ; m5 z. b( B, \7 y- \. f2 e
    Out[122]:   k! m1 V0 l& A
    2020-01-01 08:26:35   -1: ]" p% G8 d- O7 J% R
    2020-01-01 08:27:52   -1
    0 C3 g* k7 Z7 J, v0 ?5 T9 }) t2020-01-01 08:29:09   -2: b9 f) I8 ^3 a  C" R* f
    2020-01-01 08:30:26   -3
    & H% _/ I& h) S3 E0 F2020-01-01 08:31:43   -4* B' j* o" s+ y1 E' k! U$ Y
    Freq: 77S, dtype: int32
    " ], F* I- b7 q( _; b8 O13 ], E3 G$ U, n# H7 q0 P- L
    2
    - e" S# `5 k7 @  q' o4 _3
    2 h1 s/ y! l; O" o& }1 g1 T& b4/ n% A3 t7 @5 ]* ~7 U: x
    5
    : t0 D0 P% j+ q( w3 o& W6% n" e: K) v1 ]. A
    7; w5 I+ |0 h2 Q; |2 W( D
    82 S4 u9 K1 i' @; ?' k* s
    9
    : U8 }: X, e. P- Q, E; r% a10
    & n0 x: N* x& q2 j! o5 j113 r# j, ]. K, Z" ~% s# q1 G2 B
    12
    5 C& y4 P( m3 Q' \, K' P5 k  下面对应的第一个组起始值为08:24:00,其是从当天0点增加72个freq=7 min得到的,如果再增加一个freq则超出了序列的最小时间戳08:26:35:' l. h8 Y% g+ S% D

    5 g4 Y8 t9 c( Ts.resample('7min').mean().head(). g3 J6 F; w* S; m: X. b. n9 h
    Out[123]:
    1 i/ D- y3 H8 R7 q' @6 S# t1 J2020-01-01 08:24:00   -1.750000  # 起始值,终点值包含最后一个值
    - L: H2 _; A7 w7 ?2020-01-01 08:31:00   -2.6000003 u% l6 W1 H) x0 |' e! h! W; m
    2020-01-01 08:38:00   -2.166667
    , K0 c) l; {- V3 [( N7 v2020-01-01 08:45:00    0.200000
    0 D' v. n8 u2 ?' m2020-01-01 08:52:00    2.833333' I7 X4 y9 W5 x% Y
    Freq: 7T, dtype: float64$ R3 e: X' i4 U- ~$ Z% ^# q
    1
    ' i! `- c% A- |1 k2& r1 d" h  B) a# ^' X+ l; A/ S
    3
    ) s2 j: G/ K5 G$ H& ~* M7 L4
    7 d4 b4 r% u4 ]# c/ q5
    % L: p% v  F9 n9 }5 Y  B6
    # E8 ~+ h7 T! ~3 D1 e8 a8 i! ^$ E3 r7
    1 ?0 F( O1 W4 y3 C8# Z/ H% `! @% S- L9 d0 n
      有时候,用户希望从序列的最小时间戳开始依次增加freq进行分组,此时可以指定origin参数为start:
    ' Y$ m" `$ U# t# X* N  [2 S0 ^
    1 \$ p/ j. E8 w  F$ S- l) Ms.resample('7min', origin='start').mean().head()
    : u: W, s8 O0 J" H, {% |Out[124]:
    # p8 s: c/ O' Y1 y/ W* M0 a0 c2020-01-01 08:26:35   -2.333333
    % d& W5 Q' |$ F# G, y, b2020-01-01 08:33:35   -2.400000! U5 i6 Y$ p2 M5 D
    2020-01-01 08:40:35   -1.333333
      b$ y' D' b, l& v/ h) y2020-01-01 08:47:35    1.2000008 @9 f/ [! X) z, o: A8 r
    2020-01-01 08:54:35    3.166667
    ) r; R) k; K0 c; Q. fFreq: 7T, dtype: float64, I% g! G7 ?8 Y7 |
    19 L* V( P, \( G- t# {9 N
    2) ?1 E1 V  M% b0 `+ n/ [
    33 r) ?# h; ^3 s+ V; p
    42 j9 X, d& [: J! j$ T: }" t# I  X7 j
    5
    , S$ h, S6 o1 o- A6 H6
    3 g" u& j6 `9 t/ d3 }4 A! _7! Q/ _2 w/ Z# |7 V8 |
    8% t/ e0 W3 f- o& U; d9 S" L
      在返回值中,要注意索引一般是取组的第一个时间戳,但M, A, Q, BM, BA, BQ, W这七个是取对应区间的最后一个时间戳。如果想要得到正常索引,用’MS’就行。1 E+ t' o) O/ V/ ]- T

    : W! c: ]/ R9 W& R+ a5 Ys = pd.Series(np.random.randint(2,size=366),; I" _; X6 H# R/ g- _5 X# {) r  [
                  index=pd.date_range('2020-01-01',
    2 k* z- {) P8 ^+ i, d5 u                                  '2020-12-31'))) E) p; {9 j$ m1 Y1 h0 i8 y
    . ^! j2 o, D$ j" q; r0 a9 A! h

    0 b' [. e- n, H+ W7 a- Ps.resample('M').mean().head()
    ( E7 @$ A- U7 `% N/ YOut[126]: ( ]& J8 x8 w  ]
    2020-01-31    0.451613* }8 C8 Y+ h9 I' G
    2020-02-29    0.448276, R9 V7 S) I# k: k4 k* x3 ~4 T- [
    2020-03-31    0.516129
      l# z( Y" T$ J) z1 \$ k2020-04-30    0.5666674 B4 h% m; p  v: V8 d0 D6 c
    2020-05-31    0.451613( z- H: m0 U  m
    Freq: M, dtype: float64
    5 Y: F& O6 m8 k3 }& `) `
      c7 a6 v/ I) P, y3 w5 l8 Ps.resample('MS').mean().head() # 结果一样,但索引是跟正常一样
    : T/ i. K- _& ~Out[127]: % l5 M6 ~4 [& U3 a9 p! u
    2020-01-01    0.451613/ f( q7 p0 D: ~( n
    2020-02-01    0.448276
    4 k0 K+ }4 v6 E% ]2 T7 [2020-03-01    0.516129# n' q8 Y( z% b3 F
    2020-04-01    0.566667
    ' D$ }9 c# D6 F/ C9 \; q3 F2020-05-01    0.451613
    4 Z9 p0 H* ^: }$ w# ZFreq: MS, dtype: float64- _8 i4 h& Q# g7 j

    $ M1 v" Y5 p: L; Z' {" O6 ^16 C0 Y4 \# w! V2 p( h
    2
    $ j" B! N( ]$ p" E( z$ K! n30 b5 ?. c( }  h4 Z) ]8 }1 [
    4) W: m8 n: H9 s: H; e* m$ n6 b
    59 W, f5 q" S) Q3 g
    6
    0 u, ^) Q5 q, m/ f8 ~2 o/ q7. t; m& Y8 b, d# D8 y' {
    8
    3 P" V8 b9 U1 \  w7 \92 s: [5 l4 k6 Z& ]* L- E4 d  B
    10
    4 c/ T8 x8 |/ d11
    $ D# C, v' n& ?: G  z12
    3 ]' G5 h8 k/ Z4 t" Y* H+ g13
    " W# P5 b1 P  g7 Y14  c8 k9 c5 c; u: w8 G: H0 r
    15
    ! M3 _$ f+ |+ e' l6 O  [16! r4 G6 N+ M! f: q* ]6 c1 \9 w
    17
    ( l$ c7 m( {; m/ k6 Y$ p186 J! D. Q. `) w/ b9 Y" R
    198 E, T2 L6 ]7 h7 D
    204 _% h( p9 d* U% K- m
    216 j% m2 @! k' j
    22
    2 N, M' T. u" `0 {; q( a对于 DataFrame 对象,关键字 on 可用于指定列而不是索引以进行重采样:( G7 Q# F% S/ D* v
    d = {'price': [10, 11, 9, 13, 14, 18, 17, 19],% ?5 J/ q9 Q0 ?  w) i) [
         'volume': [50, 60, 40, 100, 50, 100, 40, 50]}6 A' E) t8 ~0 J. \5 x
    df = pd.DataFrame(d)/ L$ Q. P( T4 t* ?' Q! j5 O+ A1 ]
    df['week_starting'] = pd.date_range('01/01/2018',# Q9 h" a. b4 `# W- c7 L2 ~% ?3 b
                                        periods=8,( E7 B. u- U( y5 W5 J  v
                                        freq='W')9 K/ f# K; u0 @% @+ |  ~) k
    df
    # j6 s. m9 q/ H   price  volume week_starting8 M# D% j& w+ w* y( Q! h2 w
    0     10      50    2018-01-07
    ) W2 y7 U; w4 \7 c' x1     11      60    2018-01-147 k$ l4 L6 ?( {; e" @  q6 N2 G' s$ N
    2      9      40    2018-01-21
    9 Q& [# q4 e9 t% u3     13     100    2018-01-28
    1 o' z8 a5 M- l" Y) l/ Q4     14      50    2018-02-04
    / H  Q' [2 Q* f- `8 V5     18     100    2018-02-11( \5 Q7 {) D) ]
    6     17      40    2018-02-189 ~' f6 z, s) k7 @8 d1 p6 E& }
    7     19      50    2018-02-25
    9 N* _5 K& j* |7 a9 x% n1 e( W& X5 cdf.resample('M', on='week_starting').mean()4 u- }0 y" a" C; }
                   price  volume
    ) D  `% j2 {! E( }+ ^4 j8 Lweek_starting
    # z: o8 H3 i9 u% w( B3 D, P: f+ U2018-01-31     10.75    62.5
    * _2 u5 \. R8 _# v2018-02-28     17.00    60.0/ O, v/ W; H5 X
    - ^* I, u7 U& Q( V2 V5 D& O" E
    1
    + U) H8 V0 M* h2 q3 G* o2  s! L) D6 L" U1 x  ?8 r6 Q
    3
    ' {  R# _6 c( M3 ?/ t: U( h4
    " ]9 c- A2 e0 h8 u- B5
    0 z$ U9 D8 ]2 u) T. D8 t6, K1 }. G6 p) u3 d- h
    7( C& B  j9 X) X8 V5 I
    8+ M" R6 o7 n& y  x5 n) k9 |
    9
    * J8 E* J0 D% W# G1 @3 I& ?, U10
    ! [8 _# t0 x& w+ l4 m# e- O11
    - y. |5 ^0 g5 }12
    8 R; d; I3 t. X5 g13
    * b( z- e! g3 Q/ i' v7 L0 V14
    9 b* O& p; s7 b1 ]; r$ l; h15; q8 F/ I) I* E+ d+ g+ e
    16
    9 q( _4 q: {! A- J/ P17
    * U  A' t6 @- c- O# d18
    6 }% i1 A& N* c0 m6 ?/ M19& t7 H- T: J+ W5 j* ^
    20
    2 i: }! Z" K4 Q4 q, a21; t* j4 `3 v7 \
    对于具有 MultiIndex 的 DataFrame,关键字 level 可用于指定需要在哪个级别进行重采样。& {3 ^+ Q. o) J& t) i7 a
    days = pd.date_range('1/1/2000', periods=4, freq='D')
    ( j, w+ Z6 t( h4 r0 Md2 = {'price': [10, 11, 9, 13, 14, 18, 17, 19],) V6 F- N  C* Z5 k5 M  r
          'volume': [50, 60, 40, 100, 50, 100, 40, 50]}6 F1 N0 H, K7 o  v/ M7 Y
    df2 = pd.DataFrame(5 {. B0 u, h* x9 `+ y: v/ s
        d2,/ t, v9 u8 c3 S7 X, T3 N
        index=pd.MultiIndex.from_product(
    8 G; i2 w3 Y- \# F9 w        [days, ['morning', 'afternoon']]+ G( C& W5 b% ^
        )
    3 Y) ?" y" o- X* i" })1 @8 X" T2 W& J2 s3 M
    df2* E* D: B% i/ `6 C: m
                          price  volume4 `* q2 }( K$ t* {) {* r
    2000-01-01 morning       10      50
    1 B0 L$ A6 x7 [' H           afternoon     11      60
    ! E0 o* B- e% X' t/ ]# a7 G2000-01-02 morning        9      40# G9 u  m3 G0 q" u7 z! @
               afternoon     13     100
    7 O7 j: c: a3 S: P( W2000-01-03 morning       14      50
    . i0 o4 {2 S- j9 p9 q           afternoon     18     100
    : I4 m  v5 }0 i  N; e, ~2000-01-04 morning       17      40. z- z9 P; P( y5 t7 h2 s
               afternoon     19      50
    ) b- k# _+ H4 Y0 d$ G5 f1 Udf2.resample('D', level=0).sum()* E3 r4 B& _2 |* w( B; [* I
                price  volume
    6 ^, t  ?$ p" E6 m2000-01-01     21     1102 m: D  A' x0 D" [( y  P& k5 R1 y
    2000-01-02     22     140
    ' n9 e) c1 ~' C! |1 W" t  N1 o2000-01-03     32     1508 d1 c: t9 x0 n
    2000-01-04     36      90: J) u. d3 D9 F7 M8 o

    & D7 z0 ]3 C' D- J( r1; |* ]: h8 S3 I3 \
    2
    : g/ s! ]! I& N3/ [( A- }& @4 J0 }% ?, W" L
    4
    1 J+ u3 M4 n7 a% C52 F6 l; h- F7 `! k2 l) v% g- x: Z
    68 N1 L, h, F) J4 Y; g2 W
    7' c! O% i) ?6 B5 Z0 I  S' j7 `: ?
    8
    " U. A- G) N' k. b; h. y9  J; p1 E: q4 t- R
    10
    1 d; B- a2 ]- m7 d* d4 l111 ]4 j* ^! M9 t4 G
    12
    1 c' j' O0 X/ j$ O" V13
    ! U. |4 P  j& y, }5 y140 e. f% U! J; [0 r# C  c
    15* X6 e8 r  e5 R* r
    16
    ; a5 l$ z* w# X* G17
    5 K; y  E( H5 N2 C, C, H$ C( V18! }5 E0 x% d! h1 s2 c( `
    19
    7 f) L% B7 o" p0 Z4 Y5 p, J+ [205 u6 W$ I1 a/ ^' W2 ]' c1 i
    214 s* l; }5 n6 _  m' K( }
    22
    ! J; w6 s! V, d$ P233 j' e/ a1 j' B: Y# ^
    24
    , y" {* ~3 G4 t25$ |/ u) Y! V5 g4 w8 B6 I
    根据固定时间戳调整 bin 的开始:* S1 m' H2 t, l
    start, end = '2000-10-01 23:30:00', '2000-10-02 00:30:00'
    $ k! w9 x! S4 L. R8 K+ v/ Qrng = pd.date_range(start, end, freq='7min')
    + C9 N: p8 a9 Q# |- Tts = pd.Series(np.arange(len(rng)) * 3, index=rng)
    ; @* o5 {& A# Y  x9 wts
    4 e" h& p5 ~/ M6 m, ?9 Z2000-10-01 23:30:00     0. S+ Q5 ~  ?; Q" l- r" n
    2000-10-01 23:37:00     3
    9 H( j) [) f2 k7 o9 p' M* V0 `2000-10-01 23:44:00     64 R! f9 u1 _9 V" S4 {! V
    2000-10-01 23:51:00     9
    % S  @0 H) U) N) j2 D0 t# x2000-10-01 23:58:00    12; q9 I6 d/ c$ w2 Q9 m- m' x" u! @
    2000-10-02 00:05:00    15/ a: q1 q% {. l
    2000-10-02 00:12:00    18. N  ?- V/ v1 g4 d; q# @% d
    2000-10-02 00:19:00    21
    2 H( h3 N8 u9 R8 a- a  Q8 ]5 X2000-10-02 00:26:00    24
    : w8 w  r) }8 U' g& rFreq: 7T, dtype: int64" c- U* m$ Q* P- M  J5 E
    + x9 _9 [2 U- i+ `5 u* q$ C
    ts.resample('17min').sum()) K  _& C: t% {
    2000-10-01 23:14:00     0
    ' U* v# \  {! M0 ?. m5 A: y2000-10-01 23:31:00     9
    . n* A0 G: Y* x8 L" g6 e1 `. U2000-10-01 23:48:00    21
    9 z) u+ K/ v% ?$ Z; k8 m" Y/ ^5 z2000-10-02 00:05:00    54
    ' l, G& ~" q+ b* H3 b; \2 Y2000-10-02 00:22:00    24
    4 n, C" \  f# z# d& K0 ~) f3 k7 X7 MFreq: 17T, dtype: int64) a: p1 Q& S( e7 l

    0 t: e- F, T) Mts.resample('17min', origin='epoch').sum()5 W) P9 Q1 t" \( u4 z: K! \( c
    2000-10-01 23:18:00     0
    8 Q1 A) C* ^5 W  q/ x  w4 i% D+ p2000-10-01 23:35:00    182 d/ X# o0 H( W- A+ B
    2000-10-01 23:52:00    27
    3 J. x; Z2 m6 D  Z8 C! @- K; i2 Q3 T* @6 `2000-10-02 00:09:00    395 g" V$ U3 u: [! i
    2000-10-02 00:26:00    249 p3 q, e: P1 R( L- h% b$ A2 y. X
    Freq: 17T, dtype: int64
    7 x* R& Y, {+ I# o# D
    ( g+ w, B' O, E5 Q( Hts.resample('17min', origin='2000-01-01').sum()/ ]4 J- {" v  x
    2000-10-01 23:24:00     3
    6 i" {) ~& @! |7 x( o2000-10-01 23:41:00    15
    % |- ~6 L6 g3 w( q0 V% s2000-10-01 23:58:00    450 h+ l/ a0 F# r. D/ {2 L8 S# S
    2000-10-02 00:15:00    45
    & X+ T$ Q6 J0 T8 j( S  p% sFreq: 17T, dtype: int64
    - t4 _) c8 b! [
    . B! p# T# C8 p+ A1 m) h19 ^* u- I9 @0 c$ X' e
    2
    8 N) z. ]3 y7 t+ V* d3+ E4 Q# l8 d9 C, [6 {1 p
    4" \  _* l& {. J5 S* q+ F8 J5 G
    5. E% s+ y$ z" _5 v6 l
    6
    # m- [6 F; S: p4 w7 o7# x/ k2 P# t1 u. Y8 @# Z
    8
    , T2 h' v% n7 i2 r- I' r5 b9
    / f) W. \. W) |* q: p$ G8 g/ v; I10
    - F6 S) c0 }# j119 ^5 I6 q7 d$ E7 E" r: e) V6 N
    12! i' X" c5 G) H. z& f7 l: C
    13
    6 C: `6 @$ M, R% @142 ?( \/ V/ l9 i+ a+ ?' R
    15% U" O8 |+ \4 E, _& g1 W# C) z
    164 F. S9 V( s; d8 N" F
    17
    # Q( ?' s- ^( b5 w( t" X187 z# m# ~1 c- n, C( [4 @" g0 E
    19
    6 ?. X! `$ ^2 {2 o" ^20
    . `/ f9 C4 b4 A217 X! I3 Q$ ]: O8 x' R
    222 V3 w+ K7 g/ E( W7 W
    23% Z- }8 _) Y4 [0 g
    24) \7 ^8 u1 E' \' X7 J& x
    25
    5 I: V* U, Y1 N26
    . M  E- a) ]5 P- D+ }/ d27) a% X/ M" }. p0 ]( ?9 \. l
    28% N; C" C% d& ]2 s# F
    29* r( g" k* Y( Z6 c/ g& N
    30
    - j* b7 b+ |7 |2 H* y2 ~31
    . X. [9 h7 G5 h32
    + N! j- N1 A$ s; Y" g3 H336 q7 d  x% U' a# u5 A1 L+ v  \
    34
    : m; x9 S2 R9 B. v35. f! X4 J# h/ L) N1 q0 A' _
    36" ?- Q) U! d$ n, C
    37
    4 D1 _3 o+ P' W! l如果要使用偏移 Timedelta 调整 bin 的开始,则以下两行是等效的:
    0 h- n; U: H. tts.resample('17min', origin='start').sum()
    2 k3 ]& j- x  gts.resample('17min', offset='23h30min').sum()
    , E6 F, j: P9 u$ F/ B- K2000-10-01 23:30:00     9# x) b5 }8 j/ U7 x+ \8 a% ^$ E& _
    2000-10-01 23:47:00    21! {8 x' p' `% v  q
    2000-10-02 00:04:00    542 Z' N* `  \, x0 c
    2000-10-02 00:21:00    24
    : J& h8 X6 q* k4 f0 u# O2 r' tFreq: 17T, dtype: int64
    2 v) ^6 \5 a7 H, m1
    + H' t7 {0 a/ @# A( K2# B1 l( y/ b# P; b
    3' y2 g2 j; m5 P. a4 S5 W2 h: g
    46 e) t0 ?7 a# E2 i3 _
    5
    7 K2 X8 d& g: e0 L' W6
    1 C* u" H2 E3 ~% O8 B* T+ t7% G. v7 ~9 A0 u& u8 ?1 y4 j" H
    10.6 练习3 H2 J" E% S  k$ u; }. Z
    Ex1:太阳辐射数据集
    : i8 J' T. o9 ^2 U& b0 v, K2 G现有一份关于太阳辐射的数据集:; k. K+ }/ y: c/ S2 w) X, h6 K+ x
    ! p$ t( w. u% \+ G% B
    df = pd.read_csv('../data/solar.csv', usecols=['Data','Time','Radiation','Temperature'])' O' F' o1 r$ I; c- i& \8 a
    df.head(3)' i4 Q8 [& M; m/ m9 w7 _
    . U* M" i  C* ]  ?2 M" f% \
    Out[129]:
    ' `" l: \% D, x: M                    Data      Time  Radiation  Temperature
    ; ?( M8 V! L5 _0  9/29/2016 12:00:00 AM  23:55:26       1.21           48, @4 a0 O: N2 C9 o2 ]6 B6 M% g
    1  9/29/2016 12:00:00 AM  23:50:23       1.21           48  j$ X7 F: ~( J
    2  9/29/2016 12:00:00 AM  23:45:26       1.23           48( w  {! h* H' Y4 K! q" ?- H$ N
    1+ U1 F, \  l/ \1 z) n% B
    24 R, v5 d# \. o9 }) y: u$ K
    3
    " L) N9 K5 }8 m/ y/ g' }4 }! q$ b( ^4
    6 m6 ]! k) H  i; l5/ p0 \/ U8 i- J9 v7 f: `
    6
    0 J9 [/ B4 Q3 e+ e7
    ) V2 ~2 c4 K! w$ S6 j8 Z- ]8& Y5 n% S7 P- Y3 ^1 c# U, `
    将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。# w  |; x5 p# ?0 [
    每条记录时间的间隔显然并不一致,请解决如下问题:4 l7 Q4 R& s1 T6 ~5 [' V
    找出间隔时间的前三个最大值所对应的三组时间戳。- }7 ~, \3 Y* Z4 d1 W
    是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。6 v- c3 t- j3 z4 `( c( B& k
    求如下指标对应的Series:0 w' L" _3 R: [2 E0 H9 m* d6 H! `
    温度与辐射量的6小时滑动相关系数
    , o& z! s/ }7 g! m以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列
    ) i5 ?* J1 c3 b% v, H每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)+ f0 G. \1 C3 C* q* r# c2 ^8 I- i
    import numpy as np! `# h0 t3 p# M# {& q
    import pandas as pd
    % |6 A8 j9 s% ~8 T14 B' y8 b3 e/ j2 U/ l! K1 y% h1 C
    24 _0 Q9 l; k/ G& _  W0 y
    将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。
    0 m' k1 A' b0 @8 }+ Z; wdata=pd.to_datetime(df.Data) # 本身是object对象,要先转为时间序列* n5 n% i4 }7 s* p! C
    times=pd.to_timedelta(df.Time)1 [0 e; {' D" Z8 U- m1 S) C
    df.Data=data+times
    3 w! F, I( O6 ?' N( adel df['Time']1 Q. Q% e4 R# H7 M. |) R
    df=df.set_index('Data').sort_index() # 如果写的是set_index(df.Data),那么Data作为索引之外,这个列还另外保留
    * ?( y* z7 l4 Zdf
    6 K2 J) B/ j) H! y                                        Radiation        Temperature& T  H" |2 P- U
    Data                . C# e- J# @2 O/ W2 t. a
    2016-09-01 00:00:08                2.58                51
    & y$ c0 l: Q, n* g2016-09-01 00:05:10                2.83                51
    ; `0 A" \; u- e/ @) h2 d2016-09-01 00:20:06                2.16                51
    & X! z! Y: ?* Y2016-09-01 00:25:05                2.21                51' _: R7 v5 [5 [
    2016-09-01 00:30:09                2.25                51
    $ P; \# I: x7 t( c3 F* ?...        ...        ...) h: J( Q- ]0 A) y
    2016-12-31 23:35:02                1.22                41
    2 D# y& f6 Q3 v$ [: S% d2016-12-31 23:40:01                1.21                41+ ?7 W2 t0 q$ a5 V) X' \9 y
    2016-12-31 23:45:04                1.21                42
    0 u* H* ]3 r, t  y, w, }2016-12-31 23:50:03                1.19                41. _* Q4 o% O. C4 U) p! H- a$ x3 }
    2016-12-31 23:55:01                1.21                41
    # _! B6 ]$ u! A% ~. {4 D1 n' t+ C9 ^- _( c( e; o2 a1 Q! l
    1
    ' t  Z2 S9 c+ d+ O' u2- A% A. z7 V6 Q3 T7 k2 m/ f
    3
    - s% b' c5 j; e+ N1 J! T4
    3 K- k1 V- W8 A# ~2 p  x6 I& ^5- g- F  N6 p0 l8 g+ ]- X7 r; [
    6
    ( C5 u4 @$ E1 q1 E: P* X+ v+ w7
    $ d# y; t6 x' z' \8
    ! b& D$ E+ A: f$ p% ~1 t8 _+ C- L: g9
    % g2 ~7 m  y' {2 \1 Y7 V5 W10+ R' \; m) W. r, b1 h
    11
    ) M( m2 L4 ]3 p9 ~1 n0 t% ?$ X& q$ r" L12
    . a3 i) i0 E: X& B8 m+ E13/ v( G9 K0 ]: `/ r/ }7 p
    14
    ' l5 m. {3 }% {. _15" v  H8 g3 E% I* r! |
    16: D' d3 _" @/ U! m( G
    17
    8 l. [7 B+ u0 W) L; d18
    7 t6 H$ q6 n* b) C4 i198 V# G6 @5 F2 f4 [+ d( M8 V% X
    每条记录时间的间隔显然并不一致,请解决如下问题:
    " u, y0 ^. e7 Z( {: D找出间隔时间的前三个最大值所对应的三组时间戳。
      R. C( Y4 h! v: C# 第一次做错了,不是找三组时间戳  |; I, X2 d0 \% j' `8 K
    idxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]
    # D9 N! D# E; S" G8 ~df.reset_index().Data[idxmax3,idxmax3-1], J( u, Z$ Q3 S! r' @$ q- G& V6 @
    0 m# i' i$ _9 f! r8 O1 H
    25923   2016-12-08 11:10:42& E( O: Y, z* j
    24522   2016-12-01 00:00:028 n+ L  {: ^; |: \
    7417    2016-10-01 00:00:19& C, v1 x- U! i# G
    Name: Data, dtype: datetime64[ns]" C  a% \. l$ U) ?; ~' x
    1: E8 d4 v- B# ?7 n* z& X; s& _
    2- E5 E5 b, K! g) Z/ h
    3. S4 e5 V0 C. n7 x& v% ~& Y0 {+ }
    47 l3 ^6 p! O/ x$ e
    52 }/ ^% T& E" s7 r" v' Z
    6
    8 s4 R2 W! h4 V# j2 W7
    1 k6 C$ j8 }7 F  r. H" f0 {  z0 o8" {! a$ h/ d: d. J
    idxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]
    / F; M5 u2 F. Z/ d5 nlist(zip(df.reset_index().Data[idxmax3],df.reset_index().Data[idxmax3-1]))
    & V9 X7 s3 [8 x& E' ]4 g( I- c: ]9 F" x' L5 M! {# C/ k
    [(Timestamp('2016-12-08 11:10:42'), Timestamp('2016-12-05 20:45:53')),! I6 ~/ D; o+ o* N" a9 R
    (Timestamp('2016-12-01 00:00:02'), Timestamp('2016-11-29 19:05:02')),: y5 ~9 E8 G3 ^) ?$ P
    (Timestamp('2016-10-01 00:00:19'), Timestamp('2016-09-29 23:55:26'))]
    3 S  _) E  L3 Y0 ^/ L- ]10 \, }0 B- I7 P" y& ?
    2! x5 U' l# \. M7 `
    3
    3 N' V- L# j7 V/ H) V4
    , ?& p: r) j! L! [7 r/ K3 e4 F5
    $ i' k1 F( z& A) R0 E6: u& b0 E( R+ G
    参考答案:
    ) p3 ^0 d. S0 u* O
    - p- H- T: m! f$ |" x- l9 is = df.index.to_series().reset_index(drop=True).diff().dt.total_seconds()  E5 d- V1 h0 c9 e( c! z$ e
    max_3 = s.nlargest(3).index: K5 d1 _( A$ @' B5 u5 G
    df.index[max_3.union(max_3-1)]
    , A' }  w$ z" l5 k9 |
    $ y9 U0 F! g% S3 S9 O' m9 XOut[215]: % ^8 S% @! K+ h  T/ ^0 |
    DatetimeIndex(['2016-09-29 23:55:26', '2016-10-01 00:00:19',3 {* @# E1 [* G; E
                   '2016-11-29 19:05:02', '2016-12-01 00:00:02',
      v) l2 @2 s$ E               '2016-12-05 20:45:53', '2016-12-08 11:10:42'],
    7 I5 i' h6 _! i              dtype='datetime64[ns]', name='Datetime', freq=None)1 e& G& \* a. W: Y! w1 R
    1
      \: M/ g5 X( V- ]. O" _% ^2- |% l$ k. s7 `  ~
    30 L. L7 R$ X) S2 X
    48 r0 s& w% L' ^2 ]0 o/ x4 b' ?" R
    5: M6 s. ~# Y) y% S) f' @! g
    68 f6 ~" B; y( W9 J! ], O8 |
    77 f2 o' e* S+ d0 p0 k$ N
    8$ V# V0 w5 P0 r  g2 f$ B& a- n
    9
    # a" b4 D$ o1 K3 q# M6 e8 Q是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。
    / n" ], R# |, F6 x# ^# 将df的indexydiff做差,转为秒数后排序。再求几个分位数确定取值区间* Y* I5 L) L/ D  c! Z( Q- ]
    s=pd.Series(df.index).diff(1).dt.total_seconds().sort_values(ascending=False)
    . L! f. w( E) n/ Y5 x. r: m4 As.quantile(0.9),s.quantile(0.95),s.quantile(0.99),s.quantile(0.01),s.quantile(0.03),s.quantile(0.05)
    2 L# `( @. p6 n: M% P' y5 I6 Y/ l' q
    (304.0, 309.0, 337.15999999999985, 285.0, 290.0, 292.0). Q8 S) u% d" M: ]
    1
    & F" e1 z6 p3 {1 t0 K2! U1 c4 d# h1 `* L; Y
    32 X( \; I- f( v- E8 B4 G0 M
    4
    , N0 V1 c4 J- j; O0 y# Z3 s6 G5
    # f: ]8 G$ t1 c8 I+ Y* b& o%pylab inline
    , J3 n; O; x/ J_ = plt.hist(ss[(s.values<337)&(s.values>285)],bins=50)2 }* O, T' d+ @5 A  A5 ]3 `
    plt.xlabel(' Timedelta')
    1 |! k6 `& c* V; Yplt.title(" Timedelta of solar")
    / p# J7 R1 x9 d8 T# Q. |1
    6 n/ u# ~1 B7 ^2
    8 i+ ]" ?1 i+ d; x' Q, t# ~3  L, g3 n4 t+ \, e5 d
    4: J5 ~# j$ S; O7 b- J9 m, [

    ; Y3 i: o" ^; V5 [
    ! X$ b0 w* S6 X2 @$ z. w求如下指标对应的Series:% J6 X. ]/ g" L: T" @6 f7 y
    温度与辐射量的6小时滑动相关系数
    1 g( Y0 _& W# m+ B8 {$ }- H, k以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列4 \. y- v4 r+ u1 I6 J
    每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)
    1 Y$ `1 w9 j2 A" Wdf.Radiation.rolling('6H').corr(df.Temperature).tail()
    5 O" p3 e; b, `% ?: h3 X# j! C8 P9 i: P* D" d
    Data
    ' ^$ ?! r& I/ {0 c3 y: ]2016-12-31 23:35:02    0.416187, s. i6 d% k: x
    2016-12-31 23:40:01    0.416565
    0 k# c" g- @8 E. d2016-12-31 23:45:04    0.328574
    ) Q3 C1 f8 d. l' `% [) \) F6 g/ L2016-12-31 23:50:03    0.261883( f; U! t9 f' W1 b, a
    2016-12-31 23:55:01    0.262406
    5 b5 l( i- b7 j8 u2 M2 y  P' J4 ?dtype: float64
    7 Z4 S, J4 \6 ~2 b) |% z19 ?  f4 \+ K3 j4 b. |/ Y6 U
    2- G1 O3 ]) F5 y9 `* k+ ?
    36 m1 ^( t5 v" l4 x
    4
    % v3 v$ p" F1 @+ l5
    ) ]. ]4 |4 r. w5 ~( N) s# ~* H: O6: ~; }7 U- u' J" j
    7
    * ~9 A; s7 A9 g1 `7 w8( I6 G# R9 J$ H9 O; {9 ~7 k: ^
    9- Z$ S7 |7 i2 ?% G) F
    df['Temperature'].resample('6H',offset='3H').mean().head(); V( f/ C3 J8 [2 o- C
    : ~; Q9 I4 _" `1 f' G9 l: N
    Data
    : B; S  m. o, J4 D' ^2016-08-31 21:00:00    51.218750" v, `% m) b& [9 L  S
    2016-09-01 03:00:00    50.033333
    : Z2 D: d, T: J" F0 G: G1 c. F6 U2 v2016-09-01 09:00:00    59.379310% j7 u; G# ]. n
    2016-09-01 15:00:00    57.984375* J/ x; d+ j! G& X/ s* J
    2016-09-01 21:00:00    51.393939
    * Q% Q/ P) ]- R9 q* IFreq: 6H, Name: Temperature, dtype: float64
    2 N# d4 p& J" u. p% [1# F- E: U/ t; U* B. G6 {- P/ o- N% l
    2
    $ h$ L* W( V' x9 o9 U3
    4 ^7 L/ V1 o/ [  [0 x4 H- ]4
    5 _3 P/ q/ E( X6 n9 s& c5
      M; D' P& R, u5 ]& C6 s: c7 h6
    8 u& V) V- e" g8 f/ X9 J7$ B  G  B3 O& `4 N; V% b+ f
    8
    & k5 ^. N5 [2 K1 o) s# F8 Y$ e, A9
      c/ P/ H- G, G" t& b  j最后一题参考答案:2 w% X& g) [1 S; b

    0 x% ^) u8 O+ W. S9 w& k# ~# 非常慢
    % N/ O/ ~% C" R' b  Qmy_dt = df.index.shift(freq='-6H')4 y8 q. e( f8 Q8 r
    int_loc = [df.index.get_indexer([i], method='nearest') for i in my_dt]# z) A1 ]1 g% v5 q7 e/ o
    int_loc = np.array(int_loc).reshape(-1)
    & }6 Q9 u- L' W% `/ M+ t% Zres = df.Radiation.iloc[int_loc]/ @, p; G: `4 N6 a3 f
    res.index = df.index
    + z* |! i2 O: ?5 Gres.tail(3)% y6 ~0 s: P; Z, C' ^1 E- w+ s
    1
    6 t* U- B' S+ b5 X$ _: b  g  r2
    1 R" c* F2 Y3 I1 `3
    5 U( Y9 M; _' p0 F4
    3 }9 r, b! |+ x% [4 ^5
    0 V9 H- z0 E7 J8 [$ ]. Z4 V0 \5 ^( \1 g63 p6 r" c! ?4 F6 K9 Z
    7
    5 S3 Q0 e6 Q5 E* P/ P# 纸质版上介绍了merge_asof,性能差距可以达到3-4个数量级( B# Z3 B* v6 [8 `; o2 D8 G
    target = pd.DataFrame(. S# R9 H9 B. j) M) ^' A
        {
    9 u& y+ ~! ?5 a- |        "Time": df.index.shift(freq='-6H'),
    , _: d* i  [+ K% j( D( _  c* j* _        "Datetime": df.index,; [! G- I* h3 v! k3 b- S- g
        }* }: Q! i0 ~0 u
    )8 B- @7 m, Z- D7 |1 A

    ; W  n1 I5 a+ Bres = pd.merge_asof(
    . Q; i0 ^& q; d" x& m/ i0 \    target,
    " _: B% O2 s- f* p    df.reset_index().rename(columns={"Datetime": "Time"}),
    0 \& o# b6 Z2 n- ?    left_on="Time",+ T8 ^; D5 Z4 D& U/ z. d
        right_on="Time",
    2 P+ P2 c+ b3 B& Q5 Q( ^    direction="nearest"
    ; `( j' K6 _! u1 M& F) D2 h" M).set_index("Datetime").Radiation
    # x* Z; ]$ N5 L& G# T4 d. x" @. d  C! o1 |& _- U: @2 Z
    res.tail(3); E0 \& I7 D7 r0 q( s! b
    Out[224]:
    1 F4 P" b# K- f4 X$ @0 }Datetime
    $ C3 @, G% ~, ^$ A2 X2016-12-31 23:45:04    9.33
    , e0 G; R3 ^) m" x) `: p2016-12-31 23:50:03    8.49
    ( ^6 X( w, x. g+ C% U) |2016-12-31 23:55:01    5.84
    % c5 m, y) v" z: D4 d  OName: Radiation, dtype: float64  g+ a5 D! A" k! z! b

    0 N- h) e% T. ?- f# T1
    $ u5 B9 H1 e: e2
      m8 }: J  S* D" s8 q8 `) b4 @3
      H# v! B. U# i5 e7 g3 z# v4
    8 U: h# [: n8 I* R: a5% G' R. A! y) B! i$ t* x# B
    6/ a1 A' J/ a7 _( Z- ~
    7
    4 A( P1 C! i  h! J: v  y86 J# }6 {: D2 Q3 U3 T( v
    9
    ' i; z- a" z9 L+ q10
    4 k, h9 m, y9 R0 \% c11
    2 O9 M0 Z1 X1 ?12
    , u0 j5 _  f, z1 u! K4 F139 Q4 y5 k9 k: ^
    14
    1 S; ^, G+ |! K) N3 x3 E15& o' [; O* e7 v3 L
    16
      U( X) N% ^9 }6 T+ [" o/ h17
    4 ^9 v9 [2 A) w5 d7 h# J2 g1 J# O182 d) X7 c# q5 F- B; g5 n+ E, u* H
    19
    $ f+ z. b( y5 |/ T& X1 L3 }20
      _1 F8 a: g% r$ C& I6 z" G21" ~( K) [! H3 |2 l, f
    22) Y" F4 b& k1 y  A$ `
    23
    , e# e1 E% {. A- ZEx2:水果销量数据集1 m! I7 S) \, o  p& X- ^
    现有一份2019年每日水果销量记录表:$ H" i, e9 a2 T9 d- Y+ ^

    / L$ }; V$ A) p& qdf = pd.read_csv('../data/fruit.csv')
    9 F# U- Q2 U7 X0 g- Wdf.head(3)
    " [0 ^. P; `6 T
    3 z/ k# K+ w; p" N" I* gOut[131]: 6 r. M3 {( v# h5 ?! b( a+ R
             Date  Fruit  Sale" f/ A0 X, a. s" t
    0  2019-04-18  Peach    15
    6 x/ j3 ~! l' {% m( q/ r1  2019-12-29  Peach    15
    9 i( s. I6 d8 {( x2  2019-06-05  Peach    19) I- a5 T. |; M; e# f4 M4 ?& M
    1) }! G# ~$ @* v. p5 Q0 m; @
    2/ D3 I6 `  }( L' Q0 x/ c6 W" t
    3
    0 O1 P7 g% M5 ]6 ]! K# i3 \0 G9 Z4
    ; g+ ^2 K) t/ }6 o( s5. Z2 y# L$ n  O" I# q- j+ p
    6* `5 m! _2 h, |( C5 D+ `
    78 y2 t9 @2 T3 W2 R$ E! p7 k
    8% ]5 r" Q) j+ F  H# `
    统计如下指标:$ ?/ s* {9 {% ?2 y2 ^: J
    每月上半月(15号及之前)与下半月葡萄销量的比值
    ) W. c: s) ~3 ?6 ~5 V/ B每月最后一天的生梨销量总和
    9 [; p" X0 P2 B( s: @+ h" {, a- Z每月最后一天工作日的生梨销量总和5 {  y1 |3 r7 G% u5 o+ J
    每月最后五天的苹果销量均值
    . U( Q4 ?7 g! P9 [' h* z" F按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。4 z4 G0 y3 A7 I1 I5 L: B
    按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。: F4 c: V. e% d3 ^1 W+ m6 E) K
    import numpy as np7 w; `+ o* y$ W- E! H# a7 n
    import pandas as pd
    0 J! v6 L" y9 C( ^4 s3 w1 x1  A& s  S0 c, O) y% n% {9 b0 O5 D
    20 _/ p5 ^( }, L& P3 [! ~1 v
    统计如下指标:
    9 Y7 s- d+ e) p每月上半月(15号及之前)与下半月葡萄销量的比值
      K" B/ s. @1 f+ [9 p2 {5 ?0 s$ t; y每月最后一天的生梨销量总和" p( F1 N' _4 K) H& ]% [
    每月最后一天工作日的生梨销量总和+ Z$ U! J5 x9 W) p+ ?& M2 k
    每月最后五天的苹果销量均值
    8 i* x$ Q7 E5 Y7 \# 每月上半月(15号及之前)与下半月葡萄销量的比值
    6 J( g$ A" F( S. J; y( ^df.Date=pd.to_datetime(df.Date)" q( Q- q: _/ z8 n
    sale=df.query('Fruit == "Grape"').groupby([df.Date.dt.month,df.Date.dt.day<=15])['Sale'].sum()
    - y$ `! l! _9 H0 bsale.columns=['Month','15Dayes','Sale'] # 为啥这么改没用啊3 I- f& |5 L2 r) w
    sale=pd.DataFrame(sale)
    " w. F, Q7 M- ?- z0 jsale=sale.unstack(1).rename_axis(index={'Date':'Month'},
    1 Z/ k- A5 {9 S, O  ?, Y                 columns={'Date':'15Days'}).stack(1).reset_index() # unstack主要是两个索引都是Date无法直接重命名
    3 ]: S3 a) {( ^1 u8 asale.head() # 每个月上下半月的销量$ B0 y  ~7 k! f7 ~6 [
    4 V# O4 E' X7 u9 e# k; v
      Month        15Days        Sale; S) k' p: Y+ V- G
    0        1        False        105033 e7 y7 N" X; {# W6 h3 O% u- y% }3 T
    1        1        True        12341
    2 K; M# K" U7 s  Q% O2        2        False        10001
    5 n; k% Y6 U! R0 a$ D3        2        True        10106
    0 s+ S0 T! @" o$ Y0 f4        3        False        128142 ~8 c! W3 P) t9 c! m

    3 p2 `3 p& a- _+ `4 g+ L8 F# 使用自定义聚合函数,分组后每组就上半月和下半月两个值,根据索引位置判断求比值时的分子分母顺序  {1 b9 q, X8 e3 f+ I. J" S: D; L& E0 R1 q
    sale.groupby(sale['Month'])['Sale'].agg(1 o! ~# d& ?# i& z
                    lambda x: x.max()/x.min() if x.idxmax()>x.idxmin()  else x.min()/x.max())% h( b1 d/ B: f4 A+ y- y& b8 J- g
    % E- r0 A( x6 s7 }& d
    Month
    : G; l8 U7 }% J1     1.174998
    ! t: y5 K; H! Y2 K) j: z7 p2     1.010499
    2 x; c2 `6 |* K4 M6 U: X: U9 o3     0.776338
    : Q( G1 o: @1 {4     1.026345
    7 ^1 J- f8 @- a, C5     0.9005340 T2 A. v1 \6 f$ Q& m8 E4 s
    6     0.980136
    4 q0 @3 P: P6 Q; T/ H4 R0 P7     1.350960( j# a  S, B& I7 U. ]1 j6 w& o4 @
    8     1.091584
    " D; b1 T/ X$ y( U! r" m+ V" G6 S9     1.116508
    + S: G0 B* {+ l- }10    1.020784
    + ]/ l% W' X8 k4 c0 b6 B# b11    1.275911% l9 O7 }" _/ P2 d: @  T3 b
    12    0.989662
    : ~( M- V/ ^' P" X) n! rName: Sale, dtype: float64" M* m; y1 [- ~1 M+ ?* b% \  a- w

    : v; b& ^$ N* j! a. s1
    - t3 B& K7 w# c1 z1 ~$ ?( x; x2
    ! w+ O, @( S3 t$ ]0 r! y, n3
    : X& d  a' P  Q* |! K( D8 U3 `3 T8 `4 _4, [/ N* u: _8 c7 c" K1 G) u# L
    5
    , l( X% M8 O( V- T9 f( ^! z* X  w6 c65 R+ G1 ]8 V2 s, S( N4 J/ X2 |
    7
    - y# j& }; g& U8
    3 y; X8 \3 n3 D; l: d- T* i9
    1 \$ E! U: i% `1 R10
    # o9 S) X7 }3 m, s. [11
    ( h( s4 O+ u% [# e3 @8 T12
    ( ?' H3 s3 q/ H. O& V4 x& \13& ]* T0 k4 X& {/ C, Q  L9 Z6 N) G5 [
    14
    , G# N/ K/ q, u6 a$ E. h: F4 Y# |15
    & o, U, M- B) ~16
    5 Q3 M, [) x# D( L17
    ! M* n" b' w* V. C1 C0 B' b! r4 [18
    : h2 Z( P/ b3 @/ Y" V: M0 A: z193 g6 [" l$ O8 _! {" d
    20: Y2 i3 ^$ ?( K0 J* _
    21
      ]3 X2 B3 J8 i4 p, [, s( L22/ d$ g) u( O4 h. Y; U% r. f( n
    23; A$ S9 u: A* }
    24
    4 ]' B) `, V* A! i! l, M25
    ( x; v% w( {# A26
    4 n& \! U; O4 ^! U. Q27( S* M; \7 }& h9 U" b
    28
    % _" {* X; }" Z2 [: E  ^29( ^' O2 h* e7 N, a7 R0 J$ a
    30+ q9 ]% ^7 a- c  O+ A# K3 A
    31
    9 e1 k; X! @4 H+ O32) ]$ [3 {/ A% o$ Z
    33) j( s! F& c/ S  C
    34
    - T9 a2 t5 @; s! z# 每月最后一天的生梨销量总和# |: \0 v+ ]6 q
    df[df.Date.dt.is_month_end].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()( `6 A) j/ {  ]: M9 f5 n2 \

    - C% `* w& O4 G7 i8 \( ZDate
    4 W  o. o6 f2 F0 p2019-01-31    847
    * Q! F9 R) Z" Q9 p0 x2019-02-28    774. ?; ~' g2 O( h: u6 g
    2019-03-31    7618 ]+ u3 ^6 h& E3 h( w
    2019-04-30    6482 \' k& `& S9 P9 u) f. N8 K$ _# J
    2019-05-31    6169 k, [/ h4 b- s
    19 _+ N: D. }9 [9 D# |
    2
      a: N- p# k( F35 v' g9 R9 K- ^
    4" `! H4 E: ^0 m: f
    5  Z" P$ m3 D# {3 T! }8 O! S  G
    6
      `# j: g  C6 t* v0 }2 B7- \2 d6 `+ g7 m5 u2 O( m- I; D6 K" _
    8( ?; F" T, Y  |# U
    9
      S( J3 Q9 x/ H* U! j$ ~3 f. V# 每月最后一天工作日的生梨销量总和  j) N. l  p0 h
    ls=df.Date+pd.offsets.BMonthEnd()
    # `+ r: g; b2 Y/ n" cmy_filter=pd.to_datetime(ls.unique())( z1 s5 ~' A+ o- X& ]2 }4 M
    df[df.Date.isin(my_filter)].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()
    9 E5 b- X, r1 X: k
    / }4 ?; ]; Y& G4 k+ [Date
    5 @4 H$ ~8 |1 v$ F, |9 I2019-01-31     847
    / {  X! N6 e7 B2019-02-28     774% ~3 B1 k; Y9 R/ ]
    2019-03-29     5104 g- d5 F! y' O8 a
    2019-04-30     6487 K0 T- g& Z. f" q2 k& U
    2019-05-31     616
    + X1 `+ U/ a9 a% C4 _$ j9 W1  i9 K+ T; z' L8 C
    24 I- e  r4 G6 p
    3
    ( F+ ]/ O- v: ], o) G; l4- G  H4 P; ^( y$ N" d1 Y# a# x
    5
    " C" ^- X6 `3 w. x. o6
    6 V2 U& r$ n- Y) V/ y$ s" E% D7% s, l$ I- y4 W1 A
    8- b, V/ r4 Z7 W' y
    9+ G2 S1 b* t; p) _7 M7 ]9 x  z; @# \
    10
    - J4 _7 {5 I+ v' d0 o8 [11
    + e  O0 C+ ~6 I/ n, ^" C# 每月最后五天的苹果销量均值
    2 k# S: [2 I8 M8 U6 [0 G. ]start, end = '2019-01-01', '2019-12-31'' C% X9 w+ u! o- m6 a
    end = pd.date_range(start, end, freq='M')
    # A' Z$ R; N$ v1 c# Xend=end.repeat(5) # 每月最后一天的日期列表,重复5次方便做差
    / X6 v2 n. p& G& b! A- |5 w8 V+ C
    2 O9 I$ Z. ]) l# r" w5 ]td= pd.Series(pd.timedelta_range(start='0 days', periods=5),)
    - @' ^& j% q7 y( ~; ltd=pd.concat([td]*12) # 日期偏置,最后一天减去0-4天4 Q& r$ r) M: c7 t, P$ n4 N, w
    end5=(end-td).reset_index(drop=True) # 每个月最后5天的列表
    ' q( U& K1 @- w. X
    9 q% Z4 ?- X" D# i' Fapple5=df[df.Date.isin(end5)].query("Fruit == 'Apple'") # 每月最后五天苹果销量0 u: w! P! r  ~! S
    apple5.groupby(apple5.Date.dt.month)['Sale'].mean().head()$ P, a0 I1 ~6 z9 d) I+ r; N5 S
    * K' f# u" Y! [3 s. f& W
    Date' ^- s: p/ T  t. M$ u
    1     65.313725
    ) J+ {6 R) P) t" i( Z& K# M2     54.061538
    , j2 o; @0 x7 ?- v6 \$ m3     59.325581
    $ L8 G. H. W6 [: f6 ^& j) J4     65.7954556 [! p# F7 K" Q0 X- `" r
    5     57.465116
    2 |9 Y$ {( q2 G
    1 A: a8 w0 z) t8 E4 f; B1' n, t* P0 y2 ]: K  S% j* y
    2
    4 P. `0 R5 f% m6 N8 x7 ^0 ^5 W3
    3 v1 R5 j( n2 [4
    ( e2 H/ X6 p% H5 i9 X: I) V& C: l5
    ) e' a, O4 h1 \6 u' a/ f- _+ B9 R6
    : E9 L1 L" T1 ~! C$ G4 g1 ?! a7
    : }( f" I5 r2 [( }+ s3 W8
    $ I& ]3 e1 [6 Y90 U' |( N- ?! i' E7 M
    104 \! u0 _* q7 Y* `8 D; |
    11/ H+ s4 }0 _3 I; i( ]3 G
    12/ X) d  }+ n2 o: L$ v# `
    13+ b& Y$ I5 s( Z$ E5 [
    14$ M3 A8 w# _2 @( H. `
    15# M) j9 d* n4 ?" Z" P
    16" _1 G0 ~( j4 {9 R0 r7 S% l
    178 u$ o# @9 l) y2 |& s
    184 P, S3 R4 w4 w% K
    # 参考答案:
    : x7 k8 v$ a; u* G$ _9 S% {! x" D& utarget_dt = df.drop_duplicates().groupby(df.Date.drop_duplicates(
    ) }- |, }8 }) l) W$ Q' p            ).dt.month)['Date'].nlargest(5).reset_index(drop=True)5 Q* A& Y5 C3 }- j8 Z
    6 M9 D5 `, i7 E# Y0 y1 _  m
    res = df.set_index('Date').loc[target_dt].reset_index(
    % B9 h; c0 D  J$ y            ).query("Fruit == 'Apple'")8 f  C9 D" D* A+ w

    $ m" @5 R7 k- T6 a: h. eres = res.groupby(res.Date.dt.month)['Sale'].mean(8 \, O+ }9 a) |
                ).rename_axis('Month'): w3 K# x# i- j; P5 A
    & |" P7 I& \% @4 M# m# x# }
    & j( P( u7 ]" C; T  Z
    res.head()2 I3 u" [% W  ^* D! I
    Out[236]:
    / Q" k7 _" h) U; [* c3 @Month
    9 h6 [4 I% N% v% D/ o. `. A1    65.313725
    / S) f4 p( z% i2    54.061538
    $ `0 i7 v, L0 l! _  Y$ _3    59.325581( b, F8 E' {) u8 X
    4    65.795455
    5 _* ^% b( s6 i# t3 }5    57.465116
    . P: I8 E7 Q3 G+ X) t( d* K7 M2 HName: Sale, dtype: float64
    & K  \# C8 A" X" @" V
    # q& j8 K% M! K% m1! R7 N; s& i$ i
    27 W. h' X. J& L' n/ P/ ?
    3
    + y6 ~2 N/ I; i: T/ x2 M4 |4: N1 f  |- h* D# h  u, ?0 z- Q
    5
    # `! O8 f: b( J; n+ ~9 t$ D6) f: V& Q) _# ?: _3 e
    76 m6 C" u) F- j- e- M( N  S" j! I
    8
    5 w+ j  @  z6 s' b) P  D/ D9
    8 R8 \% F* I- A102 E: A! J  |0 `, D! t  ^
    11% c1 f0 u' t0 z+ n# x8 S8 k. l
    129 x, d! ^- }! I. p) r. w1 V% h
    138 ]% }/ E8 G, O, g/ T* i6 I
    14
    8 J5 c; b; ^5 m+ f- e7 s1 L. N- C15
    % W. P" q# W# k169 i0 r( m# `4 o9 V
    176 r- s6 Z3 x* c: R. ~* [
    18
    ' J; T( `7 _# n3 s' M19* L& N% Q3 b8 F* Z1 L. z1 {
    20
    8 k, D  c, [2 c4 a; K! u; G* E按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。8 F1 y! i  ]. V; F6 L
    result=pd.DataFrame(df.groupby([df.Date.dt.month,df.Date.+ w( o$ v2 Q# u: ]( j1 U! G' F
                                            dt.dayofweek,df.Fruit])['Sale'].count()) # 分组统计 3 y' o* P) \6 [6 b
                                            1 S1 m, B( T, m# g8 M  @
    result=result.unstack(1).rename_axis(index={'Date':'Month'},+ P0 D3 R& j1 C. ~3 a
                     columns={'Date':'Week'})  # 两个index名字都是Date,只能转一个到列,分开来改名字.
    # ~3 z; Y. k' T) N6 ?result=result.swaplevel(0,1,axis=0).droplevel(0,axis=1)4 U0 Z1 v4 P, h0 Q! K8 h9 q0 o4 @
    result.head() # 索引名有空再改吧# H2 [6 j2 i8 v# C6 n
    9 J/ u7 `7 O* D# Y5 i. C7 z
              Week        0        1        2        3        4        5        6" L* L2 E6 R! k% p
    Fruit Month                                                       
    , D, E* D" \! P8 z# a+ YApple        1        46        50        50        45        32        42        23% M! P5 d; a- k5 A3 l' T" l/ i
    Banana        1        27        29        24        42        36        24        35
    + c/ m3 T( {) V7 W0 X' A' lGrape        1        42        75        53        63        36        57        46
    2 N; A. q- S/ m1 x7 \) HPeach        1        67        78        73        88        59        49        723 f$ B. I# X7 H' z9 s
    Pear        1        39        69        51        54        48        36        404 o4 ?  _/ D3 p7 [7 t* F2 Z8 h
    1+ ]$ h/ M1 y. V
    2
    ! y9 s7 E1 R8 l* d1 I0 m30 R9 n( \, G- p+ z: \* w$ \
    4
    & N/ A  Q: q! T. r, t58 y) [' {. Z& Q" s
    6# f1 \  L( _* F
    72 r. P, B" w# Y$ d
    8% G& r# s3 [) j8 C
    9
    % {( q) Q. Y8 Z" j) g& w1 q10
    5 N0 l. T6 r" Q/ D11* g, P: K% ^* ^5 x, J& b' M
    124 y2 t# _- ?* y( w3 g4 [
    130 V& {* s* F% h$ c2 @
    14
    # B# W7 Y/ V$ f8 g15# x$ h/ |" H1 G# W: ~$ g/ r
    按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。
    4 ?& y' B" A1 T; Z# d# 工作日苹果销量按日期排序; y( F1 c! R! Y+ X( h/ @7 U
    select_bday=df[~df.Date.dt.dayofweek.isin([5,6])].query('Fruit=="Apple"').set_index('Date').sort_index()3 Z0 ]1 n$ }; h0 B
    select_bday=select_bday.groupby(select_bday.index)['Sale'].sum() # 每天的销量汇总
    & g7 q+ b+ }( E+ T* e; tselect_bday.head(), z7 P- p6 s7 T6 I

    3 q2 ?/ Z) g+ {4 FDate! _1 G6 v: \$ t# B
    2019-01-01    189" ]4 B* Q, u3 ]7 w5 A
    2019-01-02    482/ P( n2 @: p) @, e' w
    2019-01-03    890- W' S( T( [$ T/ p4 d
    2019-01-04    550+ ~- N; T& R- O: ^9 a5 I/ O$ `
    2019-01-07    494
    / j3 f. Y8 c/ O& ?7 Q* R& F6 A5 }, l. T+ S
    # 此时已经是工作日,正常滑窗。结果重设索引,对周末进行向后填充。  @# Y7 Z9 Z) g3 b0 \. i
    select_bday.rolling('10D').mean().reindex(df.Date.unique()).sort_index().ffill().head()& Y( f" T2 a. V5 _! n, d
    ! r. L+ U8 B, F% ]  O' I4 U: N: n
    Date
    ! ~5 g1 D+ R5 M# c) _: u9 h/ |2019-01-01    189.000000
    : z' F' I2 T/ N2019-01-02    335.500000
    1 j- \7 y; X( K# D4 h9 z2019-01-03    520.3333339 M* S$ f+ n' P. m5 j0 x
    2019-01-04    527.750000
    % K3 y4 z! @9 L' y* U2019-01-05    527.7500002 T; ^2 _7 a6 `# y$ e

    - Z8 B9 r6 Q! S- h! r  w* X1 C0 E  q————————————————
    + q5 L7 I' M7 s4 A7 j# Z, b6 J- r版权声明:本文为CSDN博主「神洛华」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    " R9 a; }% w& m' M; }# R) _  a% q原文链接:https://blog.csdn.net/qq_56591814/article/details/126633913
    2 [9 I1 K- Q9 X5 F! _1 Z$ w
    . X: o: ]* }7 e' i" g: V, D1 ~* Q9 `' Y4 R' L
    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-4-10 17:46 , Processed in 1.370083 second(s), 51 queries .

    回顶部