QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2289|回复: 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
    % F6 [' {. `% ?/ n

    9 X: X% `% F( d2 I3 R6 F2 n5 U, x+ e( Y1 s4 I7 D+ Z4 U
    文章目录
    , {0 F2 v7 m; B, `0 d+ O第八章 文本数据+ ]  V7 j! C; w3 G' }3 P( V
    8.1 str对象
    8 R, O6 I- l1 i( |$ X( \8.1.1 str对象的设计意图; S$ q( p4 k& [9 \& Y
    8.1.3 string类型
    7 A6 X2 F& a! y+ J- h6 f8.2 正则表达式基础
    % U! x. c* v. R. y. T, [8.2.1 . 一般字符的匹配" W, {* Z' p& e# e/ S
    8.2.2 元字符基础
    8 Q9 K# p2 k2 p% h( x7 ~8.2.3 简写字符集7 K0 B2 p. I3 ?3 {; H, C
    8.3 文本处理的五类操作9 B0 d9 z$ v' X" z5 I
    8.3.1 `str.split `拆分5 U8 u$ f: g$ V
    8.3.2 `str.join` 或 `str.cat `合并) O& a/ w* v. V1 R
    8.3.3 匹配; e$ l4 B. }+ b8 s' Q7 p5 \9 q) F
    8.3.5 提取4 u7 G5 n* k, P: `/ W# b' a
    8.4、常用字符串函数% z4 t6 E& ?( |  d
    8.4.1 字母型函数
    / H3 @  |# `, i& r! `8.4.2 数值型函数3 g5 F! a  c5 p4 t2 y# |* P
    8.4.3 统计型函数# K8 S+ K% I1 {: A
    8.4.4 格式型函数9 A3 X- J, U$ a
    8.5 练习% j& X# B  [2 O  f
    Ex1:房屋信息数据集  ~+ V  [; a; K' {
    Ex2:《权力的游戏》剧本数据集$ X+ G, n+ u: L# @6 y# H5 ?
    第九章 分类数据
    ( j1 i2 {- S, G' F' n% H9.1 cat对象! P3 t. R* X6 F# k8 U
    9.1.1 cat对象的属性
    , @6 W, r$ ~8 {# r  ?9.1.2 类别的增加、删除和修改
    1 W" B) m8 V$ u$ }( `9.2 有序分类6 u+ ]1 X' z# A* u4 c0 q* D& ?
    9.2.1 序的建立; u: }1 y4 L( {7 E6 I7 g# a' F
    9.2.2 排序和比较% E: u+ a5 l- H, W0 B6 B! x- ~
    9.3 区间类别( x3 t$ t2 u8 ]5 M) `6 C; J. @
    9.3.1 利用cut和qcut进行区间构造+ j; n0 H6 D: t! ?& {4 \' q( K! J
    9.3.2 一般区间的构造) N8 c; z3 J) q+ \- N! j" I; m
    9.3.3 区间的属性与方法
    , \& c" T, E& j$ C+ y9.4 练习4 r  S- H9 B, q$ h$ [4 a* C
    Ex1: 统计未出现的类别
    6 g+ i' c6 C( L$ F' u* |# p* cEx2: 钻石数据集& Q1 ]" @' A* F! R$ p8 C5 d5 N
    第十章 时序数据
    : R; z+ b" i! b8 F$ B1 C10.1 时序中的基本对象
    ; y# K# K7 d$ o10.2 时间戳+ a3 ~8 T+ v7 d8 }" G
    10.2.1 Timestamp的构造与属性
    : G* L! ^/ D; s( I10.2.2 Datetime序列的生成6 f+ r8 l9 A6 S% q
    10.2.3 dt对象
    ' I% y2 |8 S2 h# F6 X6 q8 a10.2.4 时间戳的切片与索引* _. E8 K  X; X' b/ |1 c0 ]! I+ {
    10.3 时间差
    - L; M' ?) h  C$ J# K7 q10.3.1 Timedelta的生成; @6 ^% E0 I- b
    10.2.2 Timedelta的运算
    # G7 b8 q. r, m3 W/ |: N1 ]10.4 日期偏置8 L8 \9 e2 s" X% Y: ]
    10.4.1 Offset对象3 c2 W( n, Z: C" C: X
    10.4.2 偏置字符串1 n, |8 g% D" I5 u
    10.5、时序中的滑窗与分组
    ( c# @, T  A, T/ G# f' m( l10.5.1 滑动窗口
    - g( q3 A+ b/ T3 @1 t* J. T10.5.2 重采样
    $ J) R1 Z! N2 u% t* ?10.6 练习- N/ U) Q' a  Q9 P
    Ex1:太阳辐射数据集8 t% P: M% x5 P0 L" l6 N, u7 B! L% N
    Ex2:水果销量数据集0 h9 J5 ]; q1 R7 f+ l/ G" x2 O
      课程资料《pandas数据处理与分析》、github地址、讲解视频、习题参考答案 、pandas官网7 m) Y* o7 J; ?2 x
    传送门:$ e, }, G2 w; D" @0 B& w

    & K$ L/ V7 o' ^& }8 cdatawhale8月组队学习《pandas数据处理与分析》(上)(基础、索引、分组)
    1 P2 [- J" [4 ]6 kdatawhale8月组队学习《pandas数据处理与分析》(中)(变形、连接、缺失数据)
    " e# ~7 s! Q- G. `" q* X4 Y第八章 文本数据
    & f( t: v' C, J. f8.1 str对象! J( z$ ~7 y$ t4 M% ?* w, J" G1 x) a
    8.1.1 str对象的设计意图
    * C8 H1 O9 a3 S8 c. `& w2 {5 c  str 对象是定义在 Index 或 Series上的属性,专门用于处理每个元素的文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其 str 对象。在Python标准库中也有 str 模块,为了使用上的便利,在 pandas 的50个 str 对象方法中,有31个是和标准库中的 str 模块方法同名且功能一致,例如字母转为大写的操作:
    ! u+ U- [: i9 ]1 o8 V3 g& A6 Z* z3 Z+ l5 _
    var = 'abcd'
    4 d0 l0 p" v; a. P- W+ estr.upper(var) # Python内置str模块
      p. l/ K& I$ }: }6 E$ MOut[4]: 'ABCD'! w& L9 }% C2 n! |+ T8 W
    / M8 s+ F6 K" R; D
    s = pd.Series(['abcd', 'efg', 'hi'])
    8 [, a0 b: ?; P! z
    6 q3 E( p/ n- W! Z) rs.str
    * D, N) A# {; q8 g* JOut[6]: <pandas.core.strings.accessor.StringMethods at 0x2b796892d60>
    6 d* a& s% R5 C" s* A% X; Y1 W! E  b
    0 @4 E1 G3 O8 ]& M# i& v0 c2 Hs.str.upper() # pandas中str对象上的upper方法. w! Q) [3 V7 n- w' b$ @
    Out[7]:
    ; x" H8 {! y( t1 i1 h) K5 b0    ABCD* F- U# P2 ]! B5 V+ ^% F
    1     EFG
    6 l8 e( N* q# p7 O% k/ w2 u. B2      HI; Z, W6 O3 N; v! v2 X( K
    dtype: object
    0 l1 [) a0 ?& Y+ `4 H1 z4 ^1 Q6 z1& j1 H' B8 [* S; L  V8 A6 n; H
    2
    # l& ]+ c! y8 F; p& g* F9 h3
    : Y! M6 \8 Y' R5 n( [4. m3 p% m& X+ B6 c4 s
    54 ]$ B  q/ j* D! U0 Q
    6
    3 |! k; ^6 f  C  T# D# Z77 R1 K) f, U# @' R
    8$ }+ U% j( |3 X
    9
    6 w8 t+ f8 K( T: b4 y10, D8 y/ ]5 F6 p- y! h
    11- v. g9 _6 `' j1 J" ^
    12
    & f4 S% h8 g' |134 c( v" z7 s9 L* ]4 z$ a
    145 E) C" R; S  U7 z/ Z
    157 n9 v0 ?: J0 U- ^+ M5 F
    8.1.2 []索引器
    , @- `6 c0 n, E1 n3 B; T  对于 str 对象而言,可理解为其对字符串进行了序列化的操作,例如在一般的字符串中,通过 [] 可以取出某个位置的元素,同时也能通过切片得到子串。. X# K# @( I7 G! N- D9 _* x& Z
      pandas中过对 str 对象使用 [] 索引器,可以完成完全一致的功能,并且如果超出范围则返回缺失值:
    : J8 h: F6 _& d' I- e5 u7 t1 z3 J. B. c2 z+ x3 S5 b/ n
    s.str[0]
    - [9 S, |6 q5 EOut[10]:
    0 V- p2 F. p# l2 ?# n5 P+ l* |0 X# {0    a
    8 L1 `2 }! e$ [' @+ S1    e
    + q/ A) d9 c4 I8 N2 ]4 I2    h
    ; |+ g0 ]- e; A. A! X) a" n0 edtype: object
    9 |0 x, x' @; |4 B* ~
    0 P0 b5 k" ~- P( ^s.str[-1: 0: -2]
    3 z$ P3 A' k% k# D$ T3 [2 EOut[11]:
    , I8 ~" W+ M6 l' s( p1 H8 z0    db
    . Y) X4 ^5 q+ [( T9 |% k8 ^1     g5 Z) \% L0 Y8 N) g% p/ T
    2     i
    2 ]( V* U3 ^% S. @; Ndtype: object- e& s6 Y# o: J" j+ }0 q: A% C: X
    3 v2 {: [8 m& r9 o( H
    s.str[2]
    7 C2 X" F' n1 B5 ^5 W, y1 lOut[12]: 1 L7 C& A& f! I1 w, R1 E
    0      c
    9 u, l2 E. ]  }1      g8 A2 Y( a7 s, {$ B* d5 A: ^
    2    NaN" r! D6 ^- |# ]/ ?" |' W; r1 Q
    dtype: object' ?. A( B% ^) X. m- g- `/ i% J9 d

    ; z2 W0 G2 {/ V+ F- w1
    . ?1 b# j3 j2 A2* H7 Y- T0 h3 t" i. e; K
    3
    ! n6 c4 p* R9 z4
    2 H# E2 I3 m# A: U' r+ U5) |. C6 L( M9 o
    62 a8 z! L' A3 p
    7
    $ h9 I& m' u. f0 z( z+ {6 q+ h8& u$ s, `" D' W. A
    9
    1 Y3 F( W5 k8 o' }# F0 S% P10
    3 ]; N# P" k7 E1 \1 ]  K117 ~! k' P8 J3 l( ]9 N( a1 J: S
    12
    : C4 O6 W' ?! ?9 `: L0 i13
    ; m  Y) ^: \* O' m9 m14* `9 t# q9 T( A; J
    15
    7 u& p$ L/ u, ]163 |6 H8 P1 L  \- j2 X: x
    17
    : S% \  o3 r6 p, S5 p: N180 p0 }4 g9 w8 i/ u
    19! G- n2 }8 R# z& A4 ?
    20
    - G$ N5 U8 g) x+ f0 I2 v  Cimport numpy as np% s- }* [# B" B8 J
    import pandas as pd, J# Q" w3 [- U
      n+ F' g1 [/ m
    s = pd.Series(['abcd', 'efg', 'hi'])' @! k% o5 A9 |% @; R
    s.str[0]$ v. J3 N- R9 W
    1
    6 W0 Z! `+ m0 h! \1 Y0 N2  L8 h- P3 M. O: b% ?' G
    3
    & \9 j5 ]+ ~$ r42 r- E9 x( ~0 I) @: A4 Z( U% ^
    5
    - p: h0 V( r( l0 z- c0    a
    ! C3 ?: B  V! r$ z1    e
    & m  D5 \! Y+ w# A' z. A+ q: L; S( k* T2    h4 P- k3 |, e' l" a( h: ~& y
    dtype: object: ]- d$ v8 C+ i* i* z4 m
    1
    " o  Q' h0 |! t- v  E# N2 Q2
      J6 f  z& _7 u0 R- N2 r& e/ o3
    # p9 B* q( h& y2 d) T; |6 x, q4. k. b( R  i. C- @+ x
    8.1.3 string类型
    9 |8 V/ f* f! G# \5 R% H  在上一章提到,从 pandas 的 1.0.0 版本开始,引入了 string 类型,其引入的动机在于:原来所有的字符串类型都会以 object 类型的 Series 进行存储,但 object 类型只应当存储混合类型,例如同时存储浮点、字符串、字典、列表、自定义类型等,因此字符串有必要同数值型或 category 一样,具有自己的数据存储类型,从而引入了 string 类型。
    + Q1 V- U" X' c2 \  总体上说,绝大多数对于 object 和 string 类型的序列使用 str 对象方法产生的结果是一致,但是在下面提到的两点上有较大差异:- L% [/ Y" h$ E% |: h0 V. a
    & K* Q, }6 w. h* I
    二者对于某些对象的 str 序列化方法不同。
    ! H& d, B; e; I1 Q可迭代(Iterable)对象包括但不限于字符串、字典、列表。对于一个可迭代对象, string 类型和 object 类型对它们的序列化方式不同,序列化后str对象返回结果也可能不同。例如:
    9 o0 q, q+ W* ]' vs = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a', 'b'], 0.5, 'my_string'])
    1 Z. R6 U" O9 Fs! V5 L" u. |/ Y- F2 a  G
    11 e' L3 R, J, R6 I' c6 ~0 d- D5 F6 z/ S
    2. p3 [2 k) B. Q& H0 U
    0    {1: 'temp_1', 2: 'temp_2'}
    % ^6 q1 L, `$ m' r1 ]1 \* ^1                        [a, b]; o1 t! p! o' @4 o4 E1 p
    2                           0.5
    6 O5 z+ T& D3 J; g: [" k* c3                     my_string
    1 C  t" x4 t9 q, ^2 l5 c% D: R, U: _dtype: object
    * g* }, G. d7 E1
    . d0 p5 T4 `- V3 c$ Q8 M) s9 R& N4 @2
    , |6 I) p) {. I5 c3
    ) D3 I$ E4 e; N5 l* a; o4/ |+ f( a5 [5 _5 ?6 ?
    5
    8 r: J4 V* Q# b* K& u3 H# xs.str[1] # 对每个元素取[1]的操作4 f9 W5 V& L, ~1 i5 X3 n
    1
    ; S# M' Q% J7 W& H2 z) m0    temp_1( S, q+ t% O4 H0 v6 Z
    1         b0 Y6 O7 f3 n0 o5 c$ S8 ?: t
    2       NaN# A- f: U1 u0 b) k% h
    3         y
    % U# i4 n! F/ H) _dtype: object9 I7 \/ t/ T  l" D
    1, D9 O" [: A( W" d& |0 p
    25 a* Y2 ]* [0 p- J7 F
    3
    ! e- U  p4 @# C& b7 c) \4 j+ Z8 }4
    # X' p1 p& ~6 a2 S3 e5
    / }; z4 ^" H. u% _; l' H6 gs.astype('string').str[1]9 }# E' ~" N$ V7 Z- D/ j; T
    1; Z# {( w) l1 r
    0    1
    8 K! s( k* C" Y3 q, S. k1    '- ~  e' t% t: P/ H' S) Q8 c
    2    .
    + E+ c* G: p/ J4 A0 o2 r# \' j! h3    y+ Q, l: ~' G6 u  A% g" ^, u
    dtype: string
    + ~, {+ G, S! V2 u4 y1# I7 n  @7 t8 ~, F' e4 k8 [
    2
    $ j& d4 Y, D6 L6 [/ I) B3 L  d% G3" \) u! s' p# T; K, _7 H$ R8 t3 u
    4
      e) G  Y' S; t8 @  L' I" _5* ~" m, f0 {5 K+ h% t
    除了最后一个字符串元素,前三个元素返回的值都不同,其原因在于:- R4 v% T8 `! k" i( S

    7 F8 _# j4 D& v. l当序列类型为 object 时,是对于每一个元素进行 [] 索引,因此对于字典而言,返回temp_1字符串,对于列表则返回第二个值,而第三个为不可迭代对象,返回缺失值,第四个是对字符串进行 [] 索引。
    8 K6 x- r+ n; d9 I4 cstring 类型的 str 对象先把整个元素转为字面意义的字符串,例如对于列表而言,第一个元素即 “{”,而对于最后一个字符串元素而言,恰好转化前后的表示方法一致,因此结果和 object 类型一致。
    5 _) z/ \* d- c/ i; S( H  T' n4 kstring 类型是 Nullable 类型,但 object 不是) y6 [/ T! `; K4 L7 F3 w" `8 u6 y8 J& w# K
      这意味着 string 类型的序列,如果调用的 str 方法返回值为整数 Series 和布尔 Series 时,其分别对应的 dtype 是 Int 和 boolean 的 Nullable 类型,而 object 类型则会分别返回 int/float 和 bool/object ,不过这取决于缺失值的存在与否。3 @& \2 l0 L; i  n  r
      同时,字符串的比较操作,也具有相似的特性, string 返回 Nullable 类型,但 object 不会。) f- ^* O, c5 f# x) m
    s = pd.Series(['a'])
    . N2 e; |2 v& Q2 H$ ^* h( S7 f. M7 {
    s.str.len()# z+ G1 X+ C! J- O7 @
    Out[17]: , o- T" ?7 ]7 G. f
    0    1
    / s' I6 H% O/ o4 I! g# b/ E$ M$ Kdtype: int64/ t9 {" \; j  x4 i
    6 X5 a+ `8 K5 S3 m3 B9 I( c" r5 ^0 r% e
    s.astype('string').str.len()% c/ U$ W0 ?  S2 E1 s. h# W% C
    Out[18]:
      k1 ]" g& @5 _  D0    1
    / c% D5 o* H$ b& G9 @- F- Rdtype: Int64, h& c. G9 w3 W6 B0 g/ l7 @9 k

    1 u0 c' N$ F; S' L5 qs == 'a'
    ) R, U! H6 A; X8 l1 ~Out[19]:
    5 U7 _2 p- F/ ?, l0    True
    ' i& S( _$ c* a& f4 G: h1 |dtype: bool, ]2 _( K0 I( \6 e2 Y. _: ~- v8 V
    . [6 m  C+ H2 ]: ^2 z* [( u- Q, d
    s.astype('string') == 'a'9 M2 D: r( N$ q
    Out[20]:
    3 o; P( Y  B# O0    True
    5 W( P8 ]) P1 \  sdtype: boolean
    . T9 s$ i: K( y$ q9 b: G' i: T" S8 _6 z7 N6 h) o, S' `$ `0 A$ g
    s = pd.Series(['a', np.nan]) # 带有缺失值
    * O! C& k3 n! b: G7 @
    1 q( ?6 ]2 v1 ps.str.len()
    9 r, s5 t% `6 Y! d: a5 XOut[22]:   G8 K& t! r/ }% Y
    0    1.07 b. h4 p! {3 d% F, t
    1    NaN
    8 u. k4 Q& P2 y2 O6 Q  ^6 [dtype: float64* Z7 X' n! ^( z8 O: k8 R
    - V- s7 H! m: @7 q+ D
    s.astype('string').str.len()
    # m$ K/ X% h! `  W( Q2 zOut[23]: 0 `, z" e# r, z2 v' Q/ i/ w' J
    0       1* s5 ]2 _) J8 }0 Y/ H
    1    <NA>
    ; R' C: E- o: Z: n4 ?dtype: Int64
    % ?0 [6 h& m9 y9 A# B* Y$ m7 ~8 N( U8 R; O
    s == 'a'; ~4 s0 R$ [6 O8 ?" }  W- j( I
    Out[24]: * ~3 }  z- ]/ b/ C
    0     True" A7 {5 A3 k# E- h$ f7 h, ?
    1    False
    + p8 }1 }! B2 {dtype: bool
    4 C) I/ X, s0 Z3 b, \/ @, T. U
    ( p- K/ r' |" D! G# v, ws.astype('string') == 'a'
    ! c5 u0 @8 u) ]% u3 x3 |Out[25]:
    " J$ S2 l; N4 g0    True* i! {- J, n; n, v# S" ^
    1    <NA>8 o; ~3 y5 i+ S  M$ {" t9 Q0 {
    dtype: boolean
    % k$ B; d3 U1 s% s3 D% X( s/ m5 p+ |7 _
    14 U3 k* w$ ~3 @/ e: D/ S/ [
    2
    3 g& A! o. U& a* ~3# m4 H4 h0 A* y& p0 g5 C& p! \
    49 t- \; a) U/ W* l; }3 M5 G5 g. s
    51 b+ Y) H" x/ c1 e9 x; B# }( O
    65 ~" i5 Y* ~& e* |5 o& Z
    7
    " Y9 }1 q3 j4 [% Q1 {  @82 X* @. T6 v$ M0 q* [3 q
    9  d- f6 T  R( L) x# Q# r, f
    10
    : M* U4 u4 m& }" `5 Z) n11! D/ ~# T/ J  A4 D# `' t) Z3 x
    12
    & |8 T5 S7 L' b% t  `13
    ! C* S! R0 Z% X& F" f14  x4 x- A8 f! k6 R) W
    15
    ( T8 z/ u4 K& D! N& o  K2 U* U16
    # ~) @, F. A# n- K; ~5 a) n  R170 q3 l0 n; ~/ ]/ X" X+ l
    18
    3 b7 u$ a$ Y+ T7 w( R% h+ B7 E; e19* n: Z$ S' M# g- V
    20  K1 t& U0 q1 z" I; y
    21
    * Z( x" D; X( E" U22* h6 J) A. {0 Z, O: ?
    23
    7 |7 ]% `9 Q+ f! d24/ x. k3 [6 x; ^8 G# O' o6 u# _
    25- z. X8 g3 s9 }9 d
    269 J& n7 B4 j* U% E! C. q/ [
    27- _6 m. }% g! c8 k/ C1 {5 |% e
    28
    9 O3 L9 B7 c! J29
    0 w/ `4 P6 @1 }1 g: a7 j" C' e" _307 ~+ A! l. A3 X
    31: w- {! y! r! g1 w* z) M  R
    32) O) I+ c) \( s- p* R2 M! a
    33
    * T# m- L. w$ e34" i% K& h% Q* ?; v3 J
    35) j+ Y( h3 E2 a# m4 h: }5 D! Q& G
    36
    3 g  z# D; C7 F0 [$ b" U378 [! S2 V. {$ \) Z3 T
    38; M8 a) \7 V0 N/ b5 q4 O
    39% I" V# q2 p; h- Q$ i
    409 L, T9 F6 S& c- E+ H0 K! n2 v
    41& s3 l- q1 b+ i
    42
    9 k! {# m% W* [* \) D* ^43
    + J$ V& Q1 O) n" |" j448 j7 W2 f9 A  F& K9 @7 o
    45. [- _5 w2 ^( R/ |0 e5 z& `
    46, J- a6 Y" C0 O# ]- B) ~9 O
    47& c/ y8 `1 E: m; A& e. k
      对于全体元素为数值类型的序列,即使其类型为 object 或者 category 也不允许直接使用 str 属性。如果需要把数字当成 string 类型处理,可以使用 astype 强制转换为 string 类型的 Series :3 t2 B$ |' Q2 j1 {7 I5 d5 N7 B" O

    % }/ ~9 S, y; K* xs = pd.Series([12, 345, 6789])
    ( {3 X+ X1 ~$ D5 Y( u
    # I& Y$ J- ?; Z( [* ]s.astype('string').str[1]  p: w3 @* X8 u1 K2 u% Z  t; G3 c; g
    Out[27]: 6 v8 k) T8 W0 s$ m( L8 V
    0    2
    6 a8 z" L$ B& S+ w4 |1    45 T( M* v2 L4 o& {. G
    2    7
    / o" v, L9 i; d) `- udtype: string' Z0 K, m& y" v& U" t. Q: E: F0 z$ C
    1
    . ^+ j6 w0 H2 @: G8 N! j2. r. }4 Y2 o: o/ P! j& J( P
    35 U# q/ B5 B1 L+ _
    4
    : Y/ s& U. \9 C8 q7 m  h$ w1 n5  P& |- R$ T6 n
    6
    " r; M4 L' N6 S3 P8 c; w7
    7 y  t3 z9 E: T, S; T) ~. i1 }8( m& P  O3 d* z* u9 M4 w3 {" S* L1 C
    8.2 正则表达式基础! H: c6 W6 H7 d
    这一节的两个表格来自于 learn-regex-zh 这个关于正则表达式项目,其使用 MIT 开源许可协议。这里只是介绍正则表达式的基本用法,需要系统学习的读者可参考《Python3 正则表达式》,或者《 正则表达式必知必会 》这本书# O  M+ @6 k, n0 U

    # J+ O) d: Y0 b# @: G% S8.2.1 . 一般字符的匹配
    ! D7 ]5 C: r, g& d) x4 L1 s正则表达式是一种按照某种正则模式,从左到右匹配字符串中内容的一种工具。对于一般的字符而言,它可以找到其所在的位置,这里为了演示便利,使用了 python 中 re 模块的 findall 函数来匹配所有出现过但不重叠的模式,第一个参数是正则表达式,第二个参数是待匹配的字符串。例如,在下面的字符串中找出 apple :+ ?: a2 C4 T6 i3 C: y: D5 |
    2 c; ]2 p) e6 d6 L+ m( s
    import re
    4 E* B) k2 i$ m5 v+ z
    : C$ Z- Z' X! q! k8 Wre.findall(r'Apple', 'Apple! This Is an Apple!') # 字符串从左到右依次匹配. S+ R; a1 {5 i& Y
    Out[29]: ['Apple', 'Apple']& @5 P7 z3 d# L+ r+ X$ t# w" @
    1- w$ J# v% I: n/ m! f+ U
    2
    ( d3 e) v: z) ~! p$ L( k3: Q6 R; T1 m  \" N
    49 A2 T# D  P# ^/ r+ i$ w# M
    8.2.2 元字符基础1 U( V( ]! [* b
    元字符        描述" V6 Y, W* F( `6 J
    .        匹配除换行符以外的任意字符
    : q4 Z" W% y  H& V/ l[ ]        字符类,匹配方括号中包含的任意字符
    0 N7 j, W. r1 S7 I% m+ ?[^ ]        否定字符类,匹配方括号中不包含的任意字符
    6 W2 g/ ?  [, b8 `* Z*        匹配前面的子表达式零次或多次
    & n7 J, M# g& c( y$ ]8 z) V+        匹配前面的子表达式一次或多次。比如r’d+'就是匹配数字串,r’d’就是匹配单个数字- _9 q- T9 ?! \# l2 @
    ?        匹配前面的子表达式零次或一次,非贪婪方式
    & g" {* \9 Z+ B) K3 m{n,m}        花括号,匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
    % o5 I+ h9 i) z- [+ k(xyz)        字符组,按照确切的顺序匹配字符xyz' A) U- D$ R8 W+ ~% w* |
    |        分支结构,匹配符号之前的字符或后面的字符- |% g( i  d2 H8 I: P  ~* E) v
    \        转义符,它可以还原元字符原来的含义% }1 c- E- ^, Y3 Z
    ^        匹配行的开始$ u* B9 m% s8 z3 ~8 l2 h! H
    $        匹配行的结束
    / \2 y  C8 T7 K# @( fimport re
    ( z' m7 ^" z  l# ?, B% F, mre.findall(r'.', 'abc')
    : g# g) M. d$ a9 c% x% T- }Out[30]: ['a', 'b', 'c']
    ; z) @- J& }6 d' j7 `3 t9 D& P$ F5 ~- G) f- {
    re.findall(r'[ac]', 'abc') # []中有的子串都匹配; B. X! [8 U1 `( j- Z
    Out[31]: ['a', 'c']
    - v8 w" x: f! f% ^3 h1 Q+ S- o2 h$ L
    re.findall(r'[^ac]', 'abc')
    ' r4 x' C$ W8 g3 oOut[32]: ['b']4 v, ], _: S, o+ l$ e
    ; W, {4 m* z; B
    re.findall(r'[ab]{2}', 'aaaabbbb') # {n}指匹配n次7 I7 O* {- \6 g) u7 S$ m
    Out[33]: ['aa', 'aa', 'bb', 'bb']* g: g4 k8 G5 S  [3 \
    % {! _. U2 I# k; }% v4 Q% I7 L
    re.findall(r'aaa|bbc|ca', 'aacabbcbbc') # 匹配前面的或者后面的字符串
    & C0 O* r: |+ F1 b  J/ o% J: e% kOut[34]: ['ca', 'bbc', 'bbc']
    % n' S: X: d+ k- A' R8 W! T2 H  W
    * {4 ?4 m5 }1 @4 X7 Z* L# 上面的元字符都有特殊含义,要匹配其本来的意思就得用\进行转义。
    / _+ s# ~# c+ u/ e" `  V. k5 d6 e5 X"""5 g; k. C' {* r" |9 q7 D, Y+ j
    1. ?匹配的是前一个字符,即被转义的\,所以|前面的内容就是匹配a\或者a,但是结果里面没有a\,相当于只能匹配a。
    ! Y# M' {* Z# s8 ?  {: o' V7 b2. |右边是a\*,转义之后匹配a*,对于竖线而言左边优先级高于右边* m5 Z6 h! c/ }0 I/ w3 T+ _0 b3 q. f
    3. 然后看目标字符串aa?a*a,第一个a匹配左边,第二个a匹配左边,第三个a虽然后面有*,
    : P6 j7 p% a0 u! ~4 H( F& j但是左边优先级高, 还是匹配左边,剩下一个a还是左边,所以结果是四个a" r; R' k9 W( q9 k9 |$ e1 X
    """
    % n% Q. z0 P# \2 P7 `2 o
    . a& J7 {7 r/ R, Z% Y& D! V- [re.findall(r'a\\?|a\*', 'aa?a*a')   # 第二次先匹配到a,就不会匹配a?。a*同理。
    / h' R7 o0 c0 u8 I' xOut[35]: ['a', 'a', 'a', 'a']2 p! I0 k* {7 D: y4 x. C: s. A" ]
    , O+ T" a' e& `! K* j1 c
    # 这里匹配不到是因为目标串'aa\a*a'中,\a是python的转义字符(\a\b\t\n等),所以匹配不到。) L3 L3 K7 b4 x0 ]8 b. c/ h: @
    # 如果是'aa\s*a'之内非python的转义字符,或者'aa\\s*a',或者r'aa\\s*a'就可以匹配到\字符。! _9 g7 r/ B9 k3 }5 T/ o2 e$ {" l
    re.findall(r'\\', 'aa\a*a')
    1 z  v; b& B& i+ T. e. z; r[]! Z% v1 ^) `8 ^. V' F/ s' I

    2 {8 C) N+ p& v( ^( `- p& n9 qre.findall(r'a?.', 'abaacadaae')7 K1 u( O' ^  R/ P& R
    Out[36]: ['ab', 'aa', 'c', 'ad', 'aa', 'e']7 i* m8 ]' `$ s

    + y4 n, {7 i6 I. Are.findall(r'(\w+)=(\d+)', 'set width=20 and height=10') # 多个匹配模式,返回元组列表% h' g0 }0 |: R8 \" ~
    [('width', '20'), ('height', '10')]9 |6 \8 a& P* V
    9 a4 U. E( z( c9 n
    1
    ' b) P, y  }' K# L8 Y+ L! ?, ^- f2
      f" y5 ]  z6 a3
    1 |6 K7 l9 j; E5 c, f  _1 N8 l4
    ! F. F' }, C9 y5 J5
    6 K  s/ d6 Y3 Q% T" u1 L6
    " F  k" U. _) h9 N* u+ R9 N7
    1 \: A. n: O3 L$ |8- }- b- a# U3 K8 j) W. \" l( W
    9! o/ {/ z% ~, q' v
    10
    % \. z# A3 T3 s6 S( q; J# E11
    3 a9 j3 }) e0 [* y12: A4 p* a' d8 ~* ^1 @( c# q
    13) @5 y& X% r$ E; }
    14
    : d4 D. P8 D. Q6 n0 V. W* h15+ D: V3 ]* y7 X8 k/ ]1 g4 U
    16+ b! a8 J7 A" }. ^
    179 V& T) L" a$ F4 j# T2 E! c2 b
    18
    6 P; y1 ^0 e) B+ ^19
    + A" S  F' i% u20
    - g) p# D  {8 N- J213 a8 ]" {& x; O
    22
    % A6 L5 a) d8 Q5 i/ V# ]' f23
    ! F) v3 l* T5 Z1 \* U1 D/ Q9 @24, a( z& k$ G% R; {$ I3 q4 i
    25( {# {& h9 T5 o
    26* T9 {" y5 w1 o! J% Y# A$ v/ y
    274 J& h( w8 M( I0 A' W# |
    285 a- D1 E' H. D/ Y/ w6 ?* h3 y* G
    29
    7 @9 p" G% S3 `! U( t1 j* t30
    % V' m$ O% ]- e; R' C. ~31
    # n6 U5 X8 A! K2 y% @32
    # R* s+ u6 L( C& p) T7 y  [33) Q. D8 s) ?3 P. O' h7 q# H
    34
    . f3 `4 J: q6 E5 C  [" E( U# N35' g& Y9 h' @% a6 U" I' B+ a
    36
    8 H. m+ |7 u( z% z& {37
    5 U7 `, X' ?9 _# T1 \9 U7 z5 \8.2.3 简写字符集
    ; p, C- K8 P1 e# Z! v则表达式中还有一类简写字符集,其等价于一组字符的集合:
    6 U4 y9 J8 l! T, ]5 g. J
    ! E6 R! \$ `" ?! q. T简写        描述
    6 r' E5 p+ ]5 A9 `\w        匹配所有字母、数字、下划线: [a-zA-Z0-9_]
    ; b9 ^# E3 |! J) ]+ Z2 W\W        匹配非字母和数字的字符: [^\w]/ c: V1 C4 W9 x
    \d        匹配数字: [0-9]
    ; s3 j' W, E0 M5 g, f8 @, m- E* b$ w\D        匹配非数字: [^\d]1 w/ [' d2 n+ ~: a0 L
    \s        匹配空格符: [\t\n\f\r\p{Z}]
    " x- s, t( F6 A9 s" `! S\S        匹配非空格符: [^\s]
    & Y& K4 L  h% o' o3 i- N# f+ g+ r9 `\B        匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。
    ( C, p4 H+ U3 x6 j$ Y/ m( U) Vre.findall(r'.s', 'Apple! This Is an Apple!')
    * ~& c9 E+ \/ C5 y/ K. e/ UOut[37]: ['is', 'Is']
    3 R0 z$ q% M* I$ B2 R7 h, X; ]& J# w
    ) i+ A5 I( |" L4 T% `" U: L6 t% ore.findall(r'\w{2}', '09 8? 7w c_ 9q p@') # 匹配任意数字字母下划线的组合,但必须是两次
    - G. r- Q/ {6 ~0 u$ ZOut[38]: ['09', '7w', 'c_', '9q']
    8 g3 x* ]# Q3 b* d' P& P4 M, F/ P6 Y' }0 n
    re.findall(r'\w\W\B', '09 8? 7w c_ 9q p@') # 匹配的是两个字符串,前一个是任意数字字母下划线(\W),后一个不是(\W)0 D4 _8 u! o3 t7 |7 t
    Out[39]: ['8?', 'p@']
    - }9 G- O8 H; k  q# _8 m7 t9 ~. q8 b% I0 d5 K
    re.findall(r'.\s.', 'Constant dropping wears the stone.')
    7 I$ Y& G; d) R2 q( U' j! V( Q7 iOut[40]: ['t d', 'g w', 's t', 'e s']
    7 M7 i) l+ i: u
    " k5 s( O, ~5 V6 I3 T8 [! vre.findall(r'上海市(.{2,3}区)(.{2,3}路)(\d+号)',5 J- ]4 C1 D5 S
               '上海市黄浦区方浜中路249号 上海市宝山区密山路5号')1 S* U7 Q" p: X& m; J3 |/ n* p
    7 n6 p/ G/ |: {( W6 e! W) e
    Out[41]: [('黄浦区', '方浜中路', '249号'), ('宝山区', '密山路', '5号')]
    " g$ p1 b' [; p9 {2 d" v# I; f8 _) h
    1
    , ~3 H' }7 S3 N4 T. O2) E& h1 l# s2 k2 L; S6 z( t
    3; {$ f  U5 R! u+ d/ t; M6 [' i
    4$ Z  ?. _: |8 R- B  R9 I% u9 G
    5) I, r; V. n# i& {
    6# S0 F* d% c8 R6 k% S0 H
    7
    1 T. j8 |; b0 ]: |% B& C: F8& }( ~7 d: ]2 \7 @; u
    9# C2 k( R# a3 A% a1 ?
    10
    9 t& c8 W% p; o  A, g114 n+ m( Y! V+ k) l2 b1 k
    12
      Y* y8 m  w4 H* X" c) h% {1 S: Z13$ R  Q/ Z  c. r4 u" m, j3 R+ n
    14
    3 B6 p2 q! V; i0 P' e7 g15& V) v+ J& \: K/ E% p- I. q* m
    16& D4 o) I7 Y" m; m
    8.3 文本处理的五类操作
    3 H0 F7 o0 \8 o8.3.1 str.split 拆分
    $ i- S) c* k6 Y! h6 z  str.split 能够把字符串的列进行拆分,其中第一个参数为正则表达式,可选参数包括从左到右的最大拆分次数 n ,是否展开为多个列 expand 。! R6 m" W1 a5 q" C$ j- i% S) [( Z
    / b8 {- H4 g5 i( S4 }+ G; L
    s = pd.Series(['上海市黄浦区方浜中路249号',/ G& q$ i9 Z0 |
                '上海市宝山区密山路5号'])9 g+ p! I: M. p0 T! H

    / A: j0 D7 d6 y
    7 v* P7 q+ G& g: G% j$ ss.str.split('[市区路]') # 每条结果为一行,相当于Series6 ?. N' I; [% J4 Z0 A8 h
    Out[43]: ! t7 F% b) |) g" D1 J
    0    [上海, 黄浦, 方浜中, 249号]% q  c4 F) S: Q  {: R) B! D
    1       [上海, 宝山, 密山, 5号]; D1 \8 F& r' T+ C; J2 O  X7 c
    dtype: object  ^4 n( l! R. {
    - v% q8 C* L& O  c) a4 M, O6 Q
    s.str.split('[市区路]', n=2, expand=True) # 结果分成多个列展示,结果相当于DataFrame' W- ^3 W  U  O3 O" h# f! m% O
    Out[44]:
    + k. C) L1 M1 f    0   1         2( o$ D3 p- s! ~
    0  上海  黄浦  方浜中路249号2 Y2 M# k5 ^: v4 G- Q7 i
    1  上海  宝山     密山路5号
    / R3 d$ g1 q. S& S1 x9 o! L1
    ! [5 b1 t0 {3 D3 }4 ]' o) v2
    0 Q  l- e2 U6 @& |33 g& |: g6 f7 x2 ]8 c
    4
    ! A+ w, Z8 E8 l, u, o% N5
    . V( x+ y  ~' y$ V+ ?' ~  d( ~  _6
    7 p; ?6 Z- V  X5 O; C! p. S7
    . z. x' o  k; n8
    , F: e, x- a9 \& j( k6 W! B9" u: `# R( S5 I+ G0 V3 }; R
    10# v9 e. L2 ]2 m% E* ~2 b9 D
    11' ~  \8 d- t2 J* s/ ?8 `
    123 w( g9 r, ?& P5 V$ Q% w+ p9 e, z
    137 Q1 n" |& Z/ U3 b
    14
    " u2 M  [- H  @15
    " W' p5 t5 R9 ~& \# k+ i3 G7 ~/ A: X  类似的函数是 str.rsplit ,其区别在于使用 n 参数的时候是从右到左限制最大拆分次数。但是当前版本下 rsplit 因为 bug 而无法使用正则表达式进行分割:
    1 D6 E' {9 e2 T% m, F. a
    : N$ B8 j! E+ a, X. ds.str.rsplit('[市区路]', n=2, expand=True)0 J( i' M9 Z. _5 n- b4 e
    Out[45]: * B3 i& ]/ Y+ f- h" S! j* s
                    0
    ) q& Y6 w: l/ D! B: m0  上海市黄浦区方浜中路249号
    ) S& R% W/ c2 U: @/ X1     上海市宝山区密山路5号+ x" R% ]5 j8 O+ D9 u
    1! i4 g6 `+ g7 y3 i5 F
    24 V7 d+ Z5 |- }' i6 O8 ]
    3
    / ?/ e0 \) a$ }7 X0 W4
    / j7 U" ^9 ]! r8 o5
    * I1 u* R, c1 W. f5 _8.3.2 str.join 或 str.cat 合并/ T* Y; K; Q% t; S8 F
    str.join 表示用某个连接符把 Series 中的字符串列表连接起来,如果列表中出现了非字符串元素则返回缺失值。4 x' M5 R" ]5 {0 h/ g
    str.cat 用于合并两个序列,主要参数为:9 v1 g9 k' `$ [' T! z1 s% I
    sep:连接符、7 n" F, g3 U) D
    join:连接形式默认为以索引为键的左连接
    * O$ W# K5 D- C) l$ Q/ tna_rep:缺失值替代符号/ R8 U% J1 P( R& |, a- T
    s = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']])6 \+ z6 n  `3 |" J" x" y# c- c1 b
    s.str.join('-')9 O* f- y. R" I9 k2 G' ]! N* }* g& ?
    Out[47]: * J- o% L- h. p% M- S0 h
    0    a-b/ o4 Z3 j6 r% g3 r
    1    NaN, C4 J& A! R1 i! K
    2    NaN
    6 C: L! N2 \; q/ Cdtype: object, t$ i- _8 ~* y6 v
    1* x1 c. v, W" v' s( e" t: ^
    2
    3 G$ B! _: i8 |% w% z3 m2 S, P3
    & v9 b& b/ C2 O4 O2 u4
    / M( f" \% X0 C# F5
    ; Z, A  n0 e2 x# [6
    6 I1 I% D* F( l1 n7
    7 J$ m$ I0 v8 m9 Z' K$ ?s1 = pd.Series(['a','b'])) E  O, q1 @# V4 N
    s2 = pd.Series(['cat','dog'])
    8 A: W) b; S$ Q& S6 ms1.str.cat(s2,sep='-')9 r8 M5 \. S; n* ]3 Y' d
    Out[50]:
    * {: o2 s/ z1 H7 t# k* G0    a-cat
    : y; g, s2 o( B' V( w& I: [& W: q1    b-dog; q0 {: @5 D' }* }. i( v% J5 \4 q
    dtype: object* g  d5 o' O8 r
    " }% N$ d5 U+ A/ x5 J# P% _
    s2.index = [1, 2]
    2 V* p6 k; N( V8 D2 |s1.str.cat(s2, sep='-', na_rep='?', join='outer')
    : A+ h4 M9 x  ZOut[52]:
    : `9 `: Q: W* k% c" F& j4 }8 g7 Q* A0      a-?5 X9 u# a# P6 O' ^
    1    b-cat
    & A' {! Z9 N4 I7 c5 p2    ?-dog
    5 d2 l, R. C/ l& R0 ~dtype: object0 m; M: f6 q8 b! a5 x3 N7 ~( ^
    1
    3 T8 }; O6 L) e% r2 `" j' c6 a2
    $ s/ c0 a. K1 h9 i) x) W3) d0 o) u; J6 o% L, i% H  b
    4
    & a# f( v6 u: h5( ?3 F% b/ {! B6 R- G
    65 G: I$ }. S8 [8 k& M
    7
    ) E9 ]* D/ o: U8 e, B; p8
    " l; F" l2 d6 g9
    - r2 ]* s* t% _" `105 L6 q( P! ]! o+ F( C* Z
    11
    / F) e6 `3 S6 b) b12
    5 `, L  \3 a! p+ E9 S13
    0 G; p8 Y5 i$ s0 K14& Q4 d; ^5 i; m# ?# b. L9 K/ ]6 |
    150 Q, j: ~% O$ w: [* |4 c# d1 f
    8.3.3 匹配
      {( ]9 J" @9 Y& Kstr.contains返回了每个字符串是否包含正则模式的布尔序列:
    0 s# o( ~; @" t3 c* M5 I: |8 Es = pd.Series(['my cat', 'he is fat', 'railway station'])
    6 l5 o9 O( D: e/ `7 V2 qs.str.contains('\s\wat')- t: q" ^; u4 y( y, P- G

    ) b/ x' r5 f$ i/ Y6 k$ E1 I0     True2 j  K, S$ d: _6 t/ o4 N
    1     True0 R# }% `( i' H: M3 Z* H" a3 `4 Y
    2    False6 F. \; x: h  {8 `. S
    dtype: bool# t, r: l* ~. E- t, W
    1
    $ z1 s% o5 f  {  j; J( `& `4 g2
    ; W7 B6 ~8 m+ S; U, U( d; J9 B/ M3$ @1 H% S" m- F' h
    4
    ( ~. H8 G2 {0 t% Y* N  H9 n+ q1 w' X5( D; M) M2 h1 P4 Z- G
    69 u4 V) V6 J$ e/ G: ^8 U
    7
    ; [6 h3 E* L" xstr.startswith和str.endswith返回了每个字符串以给定模式为开始和结束的布尔序列,它们都不支持正则表达式:0 j3 X% r! ~( i+ u1 M
    s.str.startswith('my')
    ! q5 M9 j' h6 c; t* k' o5 @8 Y& M. _5 G  \
    0     True
    $ i; a7 {- y9 [/ t; f$ R1    False2 J5 f0 z& U5 `8 f" D
    2    False7 l' J! K; D0 X! T5 P1 l
    dtype: bool" t: k: L# l* C
    1" Q" F8 n* m5 u2 T8 j6 T
    2- Y2 Y7 l% @7 e+ z- i' A# l
    35 U8 H$ }! @: B& ^0 D
    4
    9 C; D$ ^% G: N* O0 r7 e* j5
    * [5 h3 Y2 n+ D0 m* N4 X67 V% n& ?9 y/ _! @* P& j3 K: ^& O: e
    s.str.endswith('t')# B  Z  {$ B9 ?2 _8 y! {

    : O2 u+ r% j2 ~4 R4 a$ Y0     True& s  b" i% k# e, d7 V& y
    1     True( S2 n" F# J" Y0 Q$ }2 r
    2    False
    : k! ?: [( z+ W% R9 ?$ U- ?dtype: bool) b0 |! r" ~2 o
    1- T: P2 R4 m8 n4 D+ y
    29 z) e! E5 y+ M
    3' [0 t9 s: T/ h$ k- V. m8 C* l$ {* ]
    4  S* H0 o1 A9 a9 T% O* u
    5: H) \7 k& d7 r8 o
    6* t% P6 l  O0 b, |* [
    str.match可以用正则表达式来检测开始或结束字符串的模式,其返回了每个字符串起始处是否符合给定正则模式的布尔序列。当然,这些也能通过在str.contains的正则中使用^和$来实现。(貌似没有python里的search方法); N  j( R! x3 F
    s.str.match('m|h')
    / i- ?. S; b/ [  l( w1 [6 zs.str.contains('^[m|h]') # 二者等价1 F( d4 F/ e& B) e2 ?+ M7 [
    ; v: Y. w' ^3 U. |: c8 B1 Q
    0     True" ]) X  H+ i4 U# i; f- w
    1     True
    6 q2 {( [$ a6 o3 @% d2    False* J  U1 q& z$ O2 ]4 m
    dtype: bool
    ( y+ E1 q; l* v- p13 a+ F/ M9 x$ T4 L$ C+ ]1 p
    2' \- R  {3 H( J" e( V# V
    3
    + L7 }9 X8 g; l$ x$ l4
    # p  J# |1 ?& J2 c% \0 R5
    2 V* [$ v& h  a2 u& H6# h, v! ^1 d& X" s" F
    77 c$ {) e8 b- `* L$ y& X
    s.str[::-1].str.match('ta[f|g]|n') # 反转后匹配% M, Y7 @" y4 x* a6 c
    s.str.contains('[f|g]at|n$')       # 二者等价
    2 l$ F4 ?% C1 ]; N0 r' }
    6 G; w! O, U7 Y/ M8 b) L- b; U! ?+ j0    False# k2 U2 X) D4 L4 N; p
    1     True" ?+ G# ~/ ~9 O0 s% ?1 d1 N
    2     True5 U4 m( r! r7 h
    dtype: bool% z3 o. I) m/ N) y
    1, E# r* x+ \$ W
    2
    % z- p* m! d2 l8 B' K3
    6 a" C5 T7 i4 p/ Q; I, D3 H4, f* M% O3 E" m+ N1 C. z
    5
    7 {5 }1 k! F6 H! a# a6
    ' ~, r% H1 V: N* B* D, e) |7
    2 P' c4 [( e( t0 ustr.find与str.rfind返回索引的匹配函数,其分别返回从左到右和从右到左第一次匹配的位置的索引,未找到则返回-1。需要注意的是这两个函数不支持正则匹配,只能用于字符子串的匹配:
    + t: V8 U( w) H3 |6 a& W- r/ Us = pd.Series(['This is an apple. That is not an apple.'])
    5 \2 s- C  `) d( L0 O' i0 a0 ]8 E, Q6 x, Z% |- b2 \! m
    s.str.find('apple'). k6 ^) @2 b! u/ f; V
    Out[62]:
    5 ~' b/ o9 |# N& r0    11, [4 B# n8 k# Y, [( M: ?. {, o
    dtype: int64$ f( e$ ?/ I: w, w

    # t( `  D6 n- `( T3 m7 N: N/ Ls.str.rfind('apple')3 A* E! O8 c& t
    Out[63]:
    1 A+ ?9 s+ ^/ F1 i' s$ B0    33. Q% b7 h& o$ J9 _7 P: }; a
    dtype: int64# G$ {8 l# ?7 K( n) p/ U- a
    1
    6 n+ C% R! \/ l6 _1 v2
    , y- o2 U7 z5 n" }4 W4 x. ~3$ m, R6 i: V: H, X8 s1 }. r! R7 H
    4
    / E0 |* a- Z/ |' {1 I& Q5: w( I6 F2 F' c  D
    6/ ?6 \5 F$ `: r- r3 _0 F
    76 L' _2 g. Y! E- \9 f; }8 S
    8! t( E) }! Z( S7 v% N
    9
    ( y$ `7 J+ {' P% F10
    7 j9 X% p" F! Z11
    2 n6 p! m( x  Z) ]1 X1 @/ `# F& l( b替换# t1 x# h7 m2 X
    str.replace和replace并不是一个函数,在使用字符串替换时应当使用前者。
    ( u8 h, |& O4 M. cs = pd.Series(['a_1_b','c_?'])
    1 L. X- G1 `3 ~# regex默认为True,表示是正则模式,否则第一个参数内容表示是单纯的字符串,也就是匹配字符串\d|\?
    3 K/ G8 u; c+ @" P; r6 Ss.str.replace('\d|\?', 'new', regex=True)
    1 v  D; k4 l) ]) q  P7 I# c5 D/ x% m2 R" R3 E
    0    a_new_b/ _  @- m$ R: a) X% ?
    1      c_new1 K. `% z% q: i9 W8 k1 \. M
    dtype: object
    ' s9 ~9 v6 w; T# s" z6 E1$ D0 f8 D. P, s' Y1 s& U
    2
    8 A. h7 T+ i0 o" b3 m& L" g3% S: z8 P4 F; {  T  m
    4+ C6 H+ d& H9 v# o+ U( t
    50 ]) y2 [: i) I0 J
    6- R( F2 d  d: g
    7( ^5 F; t0 n; G  I2 c6 _5 p
      当需要对不同部分进行有差别的替换时,可以利用子组的方法,并且此时可以通过传入自定义的替换函数来分别进行处理,注意group(k)代表匹配到的第k个子组(圆括号之间的内容):+ {' z" c5 m! x- e" m6 K
    % b: F% M2 P9 I8 ]+ j
    s = pd.Series(['上海市黄浦区方浜中路249号',
    2 s% ^/ q0 R, @                '上海市宝山区密山路5号',
    8 T- B, ~/ C4 C, m: }/ h) J                '北京市昌平区北农路2号'])
    / b3 @$ q  m1 wpat = '(\w+市)(\w+区)(\w+路)(\d+号)'. ]& y! x' G. ]
    city = {'上海市': 'Shanghai', '北京市': 'Beijing'}3 ?2 s: z- e5 s, L  [( O+ n8 p0 }; k
    district = {'昌平区': 'CP District',1 A: G' t8 s; _0 n6 a$ D0 ^& {: _1 V
                '黄浦区': 'HP District',3 L" O* V: g) N+ X; I
                '宝山区': 'BS District'}' Y. x2 G4 g! q/ ]! Y
    road = {'方浜中路': 'Mid Fangbin Road',
    / a/ Q2 ?9 S9 z& i) d        '密山路': 'Mishan Road',1 U, W7 R" X5 E0 e! p1 G' }( I+ {
            '北农路': 'Beinong Road'}
    3 d$ J* V; \# j; |% O/ W5 qdef my_func(m):  ]$ I- m. u1 r; N7 P
        str_city = city[m.group(1)], B, p$ l( d, U+ h2 H
        str_district = district[m.group(2)]8 Y0 L# G$ M) F3 Q" [+ O. O6 x
        str_road = road[m.group(3)]
    6 P0 v* i) S3 x6 u! W* z1 R$ k    str_no = 'No. ' + m.group(4)[:-1]- b. B+ ^) e1 {; D/ W
        return ' '.join([str_city,: G# {+ e; R% R$ M! u
                         str_district,
    ; s% U+ @/ V3 a$ i. {& s  V, H, M                     str_road,
    ( t# _0 p0 h" C6 g) ~                     str_no])
    % |1 W- K5 q$ S+ bs.str.replace(pat, my_func, regex=True): a! ]; x% s- ~0 B

    0 _6 |0 }# o8 x1
    " A0 |: }1 F3 ^( @" X2, L% h3 b* \! e2 b3 @
    3
    / k5 ]7 }2 V7 _8 X& R7 W  J% b5 [. y, E4; Q1 _! C! h% e& F& k" L. j8 R
    55 k1 ~$ ~, N" g9 |& X, }
    6" m1 w6 _2 w1 f( ^1 R
    7
    & T1 P! k/ X! v! [8+ Q* g2 P. @4 @- r9 E# c
    9
    / j$ H( I& O6 E) E6 {; }& @10( u9 k5 F& r5 l9 m
    11  c+ |' J; y; A5 k4 j
    12
    4 d) r" H4 l, r5 D13: T& v1 d1 m( P: G- v9 n$ @
    14
    6 y8 r; `3 b4 p& k  Y7 |. [1 g15# `; ?7 b6 W6 u
    16% m! D* @' E! S8 x8 Q
    17% l; `9 @( R6 s. }3 b
    18
    * `6 P6 ^/ \7 f19( c6 ?* t7 \8 E+ q3 d
    20" \( h6 A3 e1 _9 @2 z
    21
    . c8 q$ D, \2 K( h- L7 X8 u5 g0    Shanghai HP District Mid Fangbin Road No. 249
    - C8 P) p+ [& T6 B5 V- N1           Shanghai BS District Mishan Road No. 5
    : `7 s7 S" R5 g4 M" W2           Beijing CP District Beinong Road No. 25 c4 F. b3 _; k9 r+ c7 v# p
    dtype: object
    & D- _! H$ L5 o" z1! f4 j' E! ^$ Z, E5 u
    24 ^0 A& u2 m2 K5 r' Y
    3
    4 v1 M8 i$ o8 E" s8 G9 O4
    9 p" G) J$ m8 h% d7 f% ?这里的数字标识并不直观,可以使用命名子组更加清晰地写出子组代表的含义:
    4 P0 R& ^$ d( L9 D3 h
    - k- y8 b& G$ R# 将各个子组进行命名
    & N/ c0 {$ r5 g. Qpat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'1 K8 J% g1 s4 o* n! E2 y
    def my_func(m):
    ' x. b" D: q# X    str_city = city[m.group('市名')]( Z; L1 B, I$ I
        str_district = district[m.group('区名')]3 m$ h; a# g/ y( @1 l5 H# \2 ~+ V
        str_road = road[m.group('路名')]
    ) m# e& R0 d  S3 O8 e( E1 Y    str_no = 'No. ' + m.group('编号')[:-1]
    6 h# B6 i9 I) E9 ]: D' }    return ' '.join([str_city,' p$ N6 Q) d8 E/ D2 g
                         str_district,$ E9 |: x4 z8 R! t
                         str_road,; _7 ^# G  V/ ?2 ^! f/ c, `/ w0 R8 d
                         str_no])
    * j  R( \! Y0 ~; b  Bs.str.replace(pat, my_func, regex=True)
    ' G4 M0 U' {# d0 o0 L1
    & \% m/ Q( w8 ^# R  m9 ?% r2 A5 t2
    2 H  T$ r6 g8 _- Z$ X* M( h3( q5 w( @1 T9 ^* Q' W" u
    4
    , v% F" z* L1 `+ J5& [, F1 O# K! Q
    6
    : e/ g: T" \, e" z; L9 ~+ J, F7
    ( ?* G& U8 T  F! c8" w# T+ b! `3 F' V
    9% F; o% Q4 S5 O" O* E2 N6 a* v& \
    10* v+ L: y* ?0 l
    11) P* M; t- n; y  Y$ I0 {  g
    120 o, R2 c% }% `$ q
    0    Shanghai HP District Mid Fangbin Road No. 249( y' C9 L! H3 ]6 L5 X) b
    1           Shanghai BS District Mishan Road No. 5: g1 ~% _8 F7 z. |, d/ d
    2           Beijing CP District Beinong Road No. 2
    * Y; c  f$ x% B  g" ^1 d8 W1 \  }dtype: object
    : E8 @4 U  S0 F$ e! [/ ]0 p1
    % ~- ~5 m( Y8 R2
    - t+ A8 \  t) @3& ~+ m8 m& V1 z0 a8 G9 k
    4
    0 s" b! [" S7 c/ p  这里虽然看起来有些繁杂,但是实际数据处理中对应的替换,一般都会通过代码来获取数据从而构造字典映射,在具体写法上会简洁的多。4 i$ Q: Z3 e% m# A' ?" C" R
      N0 K! M6 {6 c& Q/ c( ^
    8.3.5 提取6 R6 d* J8 o7 |# ^
    str.extract进行提取:提取既可以认为是一种返回具体元素值(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的str.split例子中会把分隔符去除,这并不是用户想要的效果,这时候就可以用str.extract进行提取:' J2 C2 C1 A1 K/ W7 h
    s.str.split('[市区路]')! w4 K, b7 O/ r8 i
    Out[43]:
    2 ~- @! M+ ~$ X8 t* O1 f- A0    [上海, 黄浦, 方浜中, 249号]
    ! {, t% d1 G9 V1       [上海, 宝山, 密山, 5号]! j6 b$ n5 ?. H+ W; T: S1 C
    dtype: object
    . ~: O9 j- r! A
      Y" ~3 n4 j! p/ ~! t" v; hpat = '(\w+市)(\w+区)(\w+路)(\d+号)'+ S& G; d" Z- j
    s.str.extract(pat)
    $ b% g# K. ]$ ?& WOut[78]:. `3 R; V  c; b+ F' F8 K/ d
        0    1     2     35 {1 z5 b! o1 L/ h) g
    0  上海市  黄浦区  方浜中路  249号
      E4 [$ i9 I% L: T% r" P4 P, E" e" @1  上海市  宝山区   密山路    5号
    " f# @* ~. {9 ^) W2  北京市  昌平区   北农路    2号
    6 v# O# N0 T4 P/ a( K* v( c1
    . F, N  }, k9 {0 n% E2
    & `* y  K: n; Q) X5 J3* a. b1 O" H9 \: K" R
    4
    % [3 D" p; l  o3 s* [5
    7 W; f4 _, j/ d2 t- W6, F% r+ o+ D% s; ~# d! q
    7% [/ t. i* ^& L9 z/ J9 M( |7 y1 b
    8
    " a$ _) e+ ^& Y; ]. ~- R9
    . n) B+ `6 N6 Y* v5 h104 F; F7 ^0 ~1 W6 n2 c! ?* a
    11# d/ i/ c1 b% E' Y, ?  E6 _% P
    12
    : L) k  y' X' i! x% D13: Y% L1 h% Y" R5 k5 P
    通过子组的命名,可以直接对新生成DataFrame的列命名:
    2 [- J+ O# R$ C( o$ m, |* W; ~! Q7 s  O! S! z6 x5 ?% c" b
    pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'5 U9 a0 H" L) p  [* x0 `
    s.str.extract(pat)
    6 T9 v7 K' G1 t% d& VOut[79]:
    9 }3 ?- B* b; j1 s4 U3 R0 F    市名   区名    路名    编号
    + i- X/ t7 E4 j+ Q( T0  上海市  黄浦区  方浜中路  249号
    * j3 u8 a  R. I1  上海市  宝山区   密山路    5号
    - l3 n# P- R6 Z2 K/ W7 U. R$ G  _2  北京市  昌平区   北农路    2号0 E! m7 _9 W; R( G
    1
    5 d/ N- y- E! D' |) h+ v2/ d' R. X0 ?) m! F) Q& H: M- [
    3
    3 u; D, q! l, D# {* G4
    : R9 e7 J+ @: v# |$ ~1 u- F( I& D5
    $ S+ I$ f: d1 x& z- E. _" L* f( a' O" p68 u8 |) `7 k$ Q6 g4 l$ q
    7
    + Q, S; b: x. Q3 b' h3 {" A9 rstr.extractall:不同于str.extract只匹配一次,它会把所有符合条件的模式全部匹配出来,如果存在多个结果,则以多级索引的方式存储:
    ( H! r1 l* \: k2 ns = pd.Series(['A135T15,A26S5','B674S2,B25T6'], index = ['my_A','my_B'])
    ) H7 l3 [9 w$ Q4 K* Q* |pat = '[A|B](\d+)[T|S](\d+)'
    & P3 y6 o' D8 j9 ?1 P7 {s.str.extractall(pat)0 s" T0 L+ {+ v
    Out[83]:& L& z4 T: n* m( N/ m' s* e7 N
           0   1
    , q+ n/ I0 Z6 C6 ]2 l: x. o( a     match         
    7 b2 j; m& b7 r& N$ \4 i0 nmy_A 0      135  15: ^  u4 K7 j5 q
         1       26   5
    3 T( Z* [2 R# A# _my_B 0      674   2
    + t+ q' e4 S5 R1 F. s; L     1       25   6+ O- F+ n9 c8 j4 _  ]
    1
    . c1 `  P/ C' u; y# N# U/ [( S% V( a2
    ( g" w, E5 J  b3+ z+ d  S: h0 u' \+ K  P
    4
    ! }( P. r4 O( i# y$ k  O5. }* h& E* q& M! ]$ `. N
    6
    # b2 t5 Z$ ]" V" |7 ~- D7
    6 S# n( l) t4 t6 q! t7 X: m8  `. L0 ]% j% _) y$ Q
    9
    & C( \: v8 N) k: M/ ]! S$ \10
    : p9 x5 Y" e. O( r* xpat_with_name = '[A|B](?P<name1>\d+)[T|S](?P<name2>\d+)'2 a) N; ^/ o; X0 Y) U$ p9 j
    s.str.extractall(pat_with_name), v9 m) n% U& z! p- }/ {
    Out[84]:
    3 E1 `( n" D9 q! B4 F           name1 name2. z# M, w- N' n  X6 r3 K
         match            
    / B7 F. p- K2 ^- Xmy_A 0       135    15
    , t( P+ @. Q3 r0 R( ?1 ^$ R     1        26     5* G4 g( G' s) v: ]
    my_B 0       674     2
    ! L: O3 X5 M, Q; c4 p9 F, B1 D+ J6 S) T6 U     1        25     6
    & n9 k! T4 U% u1) O& o3 P& ?* i) v* G
    2
    : i% t5 ~. B/ e( c& M3 g3( |9 G" e, A# |3 I- X: Y; l
    4
    + g/ B, h& e: J' e- g% p5
    : E$ B1 L2 f9 z9 @$ d( ~8 i6
    # L* l4 C4 U% W7
    - V% v! ^+ Z& ~2 Q0 c8
    # x" c) U. s, T- N* W  I9. c) K8 l9 b/ t. o
    str.findall:功能类似于str.extractall,区别在于前者把结果存入列表中,而后者处理为多级索引,每个行只对应一组匹配,而不是把所有匹配组合构成列表。$ G6 M, d# h+ L% e2 o# C, X
    s.str.findall(pat): M6 A1 O0 w3 V9 Q- r7 Z# t& U7 F
    1
    * W- v: v, W$ c( Z7 Jmy_A    [(135, 15), (26, 5)]$ e# I! A( Z4 Q9 o6 }
    my_B     [(674, 2), (25, 6)]7 q0 t  _% J2 T4 ^. ^& [) h
    dtype: object) U+ e% p$ k. c! w& I
    1) F7 A0 z* z7 P' r
    2
    9 L' R& z! }+ b( V3
    / W* X8 W: e6 X- F/ @- u  _8.4、常用字符串函数
    . g2 T; z2 T4 r& a" T( {  除了上述介绍的五类字符串操作有关的函数之外,str对象上还定义了一些实用的其他方法,在此进行介绍。
    , p3 ~8 R* F. X: x$ ]8 G3 d# a- p! s- |/ K2 h  H; W
    8.4.1 字母型函数
    ; a- v8 f" f8 k! D* z  upper, lower, title, capitalize, swapcase这五个函数主要用于字母的大小写转化,从下面的例子中就容易领会其功能:, u' N2 b, c9 R6 W% k
    1 h- ~$ u$ o; q) N
    s = pd.Series(['lower', 'CAPITALS', 'this is a sentence', 'SwApCaSe'])
    / L0 S  n5 Q- o6 \# e
    ' Z" `" S" r! [; Ns.str.upper()4 a; `0 m+ W3 K* Z9 E3 P5 P
    Out[87]:
    * c) A, B. m+ \: @) m0                 LOWER$ U* _- u3 H- o
    1              CAPITALS
    / w6 h: H4 w  \2    THIS IS A SENTENCE' S  c4 ]+ D& R8 i  Y3 \" A. A
    3              SWAPCASE
    + c! @/ x" e2 e( odtype: object
    ! b$ w( _, q- i1 q1 q6 |- T
    5 X* p7 C$ |0 ^4 Bs.str.lower()% H+ x, a. d7 j$ s
    Out[88]: & {$ ^) p! Q# n" z
    0                 lower, D% m( C5 ?! b6 }3 ]) v
    1              capitals: L5 o) d2 h+ V( y
    2    this is a sentence' s- Z) l0 s3 c$ F5 k- [, r9 h9 W
    3              swapcase  d; u+ g; L& x, P
    dtype: object
    ( B5 v+ c1 Y7 L0 T6 O
    ) n; e5 Q# u$ W9 d' ps.str.title()  # 首字母大写$ @' D- O& ~8 a
    Out[89]: 5 N2 y: E+ q: _7 t* f2 u
    0                 Lower& t+ D  B& l5 ]/ k7 \+ ?' A2 Z+ _
    1              Capitals4 _( ?7 Z( R, Z$ L4 _, K
    2    This Is A Sentence
    - [  z% I5 g* _% g* Z% s3              Swapcase
    2 D8 z) [' ?* Q- |2 `: Odtype: object
    ! c; L% ~, N8 ~5 g; i
    4 T5 P4 F/ _& ds.str.capitalize()  # 句首大写# w7 N$ L% d1 w) t) P* C
    Out[90]:
    + k: i% ?! f6 C( r6 ~& C: u9 L* N0                 Lower) b" F, S( ~2 ~
    1              Capitals8 S  O1 r' L, s( m
    2    This is a sentence2 c) M7 m1 G* s: w3 s( H- ~- {# ]/ D, B
    3              Swapcase
    9 p/ M/ N( B7 Ldtype: object
    7 s8 u/ s1 n2 b! W8 l
    # L# Q5 m: g: Rs.str.swapcase() # 将大写转换为小写,将小写转换为大写。
    ! d. u# P: N- h- q4 @* i& lOut[91]:
    - ~2 M8 @: o* F- i, V7 y( G4 |0                 LOWER/ F6 E5 _# |1 h+ b% C5 t
    1              capitals
    3 Q! K5 B# u, C0 R. Y/ I2    THIS IS A SENTENCE% @9 ?+ [; P1 j) @6 r4 s2 O
    3              sWaPcAsE4 N1 `+ T5 E' z+ E7 Q
    dtype: object
    : i. k3 P0 }& F. L; P; y0 `" G" n* t7 \. Z4 H8 z
    s.str.casefold()  # 去除字符串中所有大小写区别% g  i6 a* `# a! s, y
    ' X! i) F0 b# G5 ]
    0                 lower8 k- r- g( N& k; V' e3 a
    1              capitals
    6 A- v; g7 R7 w$ V: t6 R( j1 n2    this is a sentence+ ~7 O3 Z" f. [9 e* I- }4 B
    3              swapcase7 }! \$ O# B4 s
    . S0 N( P# `+ E: S; d) w
    18 |& ]  `  s; a9 @  O, V+ e
    2
    0 V+ u1 j$ E2 Y7 }  O1 X3
    * S; i( ~6 U7 _9 C& @  {9 t48 J  D% o$ h/ b, ?/ g. X) {
    5
    ! w4 o# y7 x  }  K: T- @7 ~) s6
    7 k3 \( {: `4 J2 q2 g74 V) P' A8 e6 K, W% r# h7 q% k. ]
    8' y; D- Q9 Y) I) D. y$ Q5 _
    9/ p. i% ~: E# v( P
    10  c& l" `" [+ o) t& F
    11) j1 T* [% R1 [7 p% G6 G5 K# L
    12
    2 F4 y* U) I9 ^4 J* @% x13
      ?0 z! h; A' T/ r+ U% n14
    9 Q8 t! S5 J3 b1 h, z15
    & r& A7 q# f1 k1 f7 I, S7 P- R16
    ) Y" }8 M+ e7 y( c# \2 B# R# `17: s0 i+ Z. w. \% k
    18
    : @" K$ I$ Y4 f: I! O19
    , X- l1 k  R: u  C! C* l8 A20
    ; ]( ?" e' {; a& M* H+ ?3 I21
    5 e2 C8 X( X7 O- z: |4 i22
    ) K1 }2 ]& F" m# J9 @23$ Y* K( C* x6 @6 k6 h* q* r1 Z
    24
    / n4 G( c8 [5 E4 Y) Z25% E* [4 F) X/ ^+ S7 \+ W
    26# @% ~+ O7 i0 h
    27; N( }0 H! t) q& Z6 z3 U
    28; Q% O; v) C' r2 s4 z0 K
    29
    " R+ B8 n  m4 ?8 z, s# D. q+ P  q30& g5 |% [! D% R$ J+ v
    31
    - m8 m7 O/ L5 ?" N7 ]$ I& m32$ S+ C3 C0 E* H& |9 H
    33; A1 Q8 s6 _1 a6 R2 Q
    341 s5 U- N& P% v
    35
    4 ^7 n% `- X7 Q& W365 v9 G- }. j6 C* O
    37
    0 W' J$ ?: k4 r/ i: a8 A8 l38, H- [9 h5 ~8 E, z) v0 ], L- G
    39  d( J( d4 M+ y( b7 [7 G
    40
    - N$ Y$ B0 Z# f41
    3 L# k6 U& z3 X% b) `4 H42
    6 g; b& I$ `* G432 R" g( w6 n' v+ o* J  H4 X, f
    44
    - S2 S7 k* d' K# h2 r7 L" P% G453 O& y& }+ f% [
    46
    ; O; P# Y' m$ [  ?9 G' C5 r47
    * l  ^$ \1 ]2 O; p" N) f1 J, r48- d: Z' k( R3 k8 d: K: [4 X
    8.4.2 数值型函数0 J% t+ _$ b: `+ R) O1 m" f% r, o
      这里着重需要介绍的是pd.to_numeric方法,它虽然不是str对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。其主要参数包括:
    5 d! s# P( ^& G5 o; s9 \  I- A  t* a5 f/ K6 S# q# O3 x
    errors:非数值的处理模式。对于不能转换为数值的有三种errors选项:) ~7 ?7 U4 l% f- ]
    raise:直接报错,默认选项% W( f* Y# q  _
    coerce:设为缺失值8 }3 Y5 @1 u$ V$ r) V
    ignore:保持原来的字符串。) ]1 [$ D; g9 ]
    downcast:转换类型,转成 ‘integer’, ‘signed’, ‘unsigned’, 或 ‘float’的最小dtype。比如可以转成float32就不会转成float64。& F; T1 h/ O1 h/ ?! R5 ]2 R: O
    s = pd.Series(['1', '2.2', '2e', '??', '-2.1', '0'])1 I& J8 s& \1 j1 Z& k8 {

    2 C- q( g+ S0 j. m3 I) apd.to_numeric(s, errors='ignore')
    5 n. ?( F+ [$ ]1 N' x9 ?Out[93]: - l3 M/ Q! `4 Y8 @+ [5 D- H* o
    0       12 r6 }9 X  O3 [6 P
    1     2.20 g' n, e) i/ t" f0 E! _& e
    2      2e
    / W0 p: I, t# P5 G  B: O5 |3      ??: B! K( N+ q: N8 O3 G/ G
    4    -2.1
    ( x9 }' ~" {7 i5       0" Q3 w' k7 d8 x; Z
    dtype: object4 q7 X9 t/ {8 G3 q; j1 H5 V9 J7 W

    ( }& d5 s+ y, G0 lpd.to_numeric(s, errors='coerce'); k& q6 O6 X1 s& R
    Out[94]:
      n: q' k$ L2 _' s# `0    1.0
    * y% g! U" c$ a' W. A& o1    2.27 H) Q$ ~# w' @. V, d7 t
    2    NaN7 d' Q* i( g" P, P$ E
    3    NaN1 R* e& I/ O& L& K
    4   -2.1' c- H" I" y4 E' Y) R; b5 |
    5    0.0
    ) G: n* G4 y0 U. b$ xdtype: float64
    6 t3 y, }' Q: P. H* ~
    4 y# s. O) D0 r5 ?  C1 I  f0 @1
    ( y1 k6 z2 x1 a2
    % p( _# s7 t4 N2 K+ Z* W31 E& j/ X9 ?; [1 y- h, s9 ~, d  F
    43 K% w5 k5 R# c9 ~% C# i
    5
    9 W- e# i, H: ]' o+ k4 b6, _% K5 p- D3 F, Y4 N$ r: e; P9 T- ~
    7! V: x2 ?8 j/ s0 _# S0 O0 f
    84 G, a1 F; N/ Z; {
    9
    0 Y  t  q. P* w# }3 P10
    - S# o% |) }% @2 z* ?11
    4 U8 R( ^2 r: ]12
    * h  P+ _4 G1 t4 |9 C. }5 X' a13
    $ T' W% E9 Z6 Z8 Q/ A5 t14$ H$ _9 \6 @4 U0 L: K
    15
    ; \0 v6 Z5 G$ q9 O0 w, U* r169 h7 S/ G" H' A
    170 E- z  Z9 i1 G- X. O& q, t$ |
    18- F7 |# c; e8 C" U
    191 P5 J  {( n7 c. \% |
    20/ ~$ I! k* M: m! C- u8 ^) O
    21
    0 A* _* d4 ^' m, N% Y) [4 U  在数据清洗时,可以利用coerce的设定,快速查看非数值型的行:
    : s; L' L% W7 N8 D; A5 D% O9 a0 |4 J4 h& ^) M
    s[pd.to_numeric(s, errors='coerce').isna()]
    0 X' f, P1 n" x! f# nOut[95]:
    1 B! r( B' S2 P2    2e
    0 F- ]5 }6 B7 I3    ??
    & G' I1 R) _- w% a' ?dtype: object# G( \2 H% U! q& N
    1- F8 e& U# C% _
    2
    & Z# v9 W7 N3 T8 s+ b! I3
    * R' u1 W4 B/ f% ]4
    / ~' r; E) e) A( E$ n$ `5% [; Q/ J, s! D; ]
    8.4.3 统计型函数! E- Q9 a; z8 I) l( H; b
      count和len的作用分别是返回出现正则模式的次数和字符串的长度:% M! F) |1 m& z- T8 |# P

    , ?" _( ~/ m) v3 D7 }s = pd.Series(['cat rat fat at', 'get feed sheet heat'])
    9 j1 \3 v$ z8 r0 [
    ' E) y( b( R# Y  u# qs.str.count('[r|f]at|ee') # |左右两种子串都匹配了两次
    ' O; w) q1 A& B, U  ?Out[97]: & E' t8 j6 g3 U, r3 x$ k
    0    2( J1 |2 a& P) X6 O# I" r# z, o
    1    2
    # D2 z% M) L3 W  H' J* b# mdtype: int64
    ! K) v9 [( `6 X& t9 f. W, A% E& b) Z# E
    s.str.len()
    9 z1 s8 C  ~& i3 A5 d$ D# W' HOut[98]: 2 q; X0 O9 \5 A9 j5 t
    0    14
    ( c. l( I% y+ N1    191 R) N: Q# Q  d. q9 b1 L
    dtype: int64
    ( E# j2 N& X  ]( @# T6 R$ g* O! \1
    1 _& i8 F( P. @, n- A( |! K3 w2- v- g2 N+ H) I
    35 ^+ ?* r& n0 O. ~3 O/ J
    4
    8 n7 W& \7 P$ Z9 ~3 B5
    1 U$ x1 f( l% F8 @" h6* a- [2 W# v9 L8 y$ t! X6 F0 S
    79 S1 Z4 E3 T( R& ]
    8
    * `, A$ y: N5 [# s2 |* L0 i2 e. M96 e+ r3 ]8 p9 @2 T
    10
    1 A" _% X. j1 c- R11
    8 G, r1 A) u! o. j5 E+ |# Q12
    7 J- D) o6 u( Q/ q% P" |136 X3 A1 g' |) k, a
    8.4.4 格式型函数
    2 U) I: w; R" C3 U. L  格式型函数主要分为两类,第一种是除空型,第二种是填充型。其中,第一类函数一共有三种,它们分别是strip, rstrip, lstrip,分别代表去除两侧空格、右侧空格和左侧空格。这些函数在数据清洗时是有用的,特别是列名含有非法空格的时候。
    0 n& F3 R0 v' s& S& `
    # P8 \- U! u3 F" Tmy_index = pd.Index([' col1', 'col2 ', ' col3 '])% o2 N' D2 M7 V+ Q# n% Q

    , A% O  Y7 l% y- G& O4 Fmy_index.str.strip().str.len()
    / H  d. z0 H0 i# V1 SOut[100]: Int64Index([4, 4, 4], dtype='int64')( @" `  |1 y0 a5 y" x' L/ M2 |7 p

    1 o- a. m* i  y! ^7 N$ Hmy_index.str.rstrip().str.len()5 J' G1 l, Y4 Q) o
    Out[101]: Int64Index([5, 4, 5], dtype='int64')
    4 s- U  n# @1 P% b
      ?8 k! O% w7 l' L. a: M: V2 \my_index.str.lstrip().str.len()
    4 b4 N3 @" o) R' L- POut[102]: Int64Index([4, 5, 5], dtype='int64')7 {& N1 f: k/ r. t1 C7 _/ k
    1" r% U, b7 Y/ E; |2 s0 B
    2
    # ?3 I9 c- y. ]( l2 h8 ~34 U/ n4 @- e4 E/ N! A3 u& ]
    4% B1 j; P2 I) r3 X2 [9 S  \0 g
    5
    7 H$ I4 P# P. F( ?7 S62 V* f% C4 ?% S
    7
    0 d( _! R) o7 n) r2 [8! T+ V6 m! e/ p( M3 h" u/ _
    9
    1 A, A' Q8 A, {' u10
    ( K- o; [" M, z3 Y, g4 ?: e( R  对于填充型函数而言,pad是最灵活的,它可以选定字符串长度、填充的方向和填充内容:
    0 a% Z, n( v2 g4 v$ a/ n, K& w7 M/ X! c- x$ C: E  p
    s = pd.Series(['a','b','c'])5 e) e* V! T/ H; t: }: n/ e! n% |
    ' b8 ]9 u  y/ n$ A+ ^0 Y2 r% b
    s.str.pad(5,'left','*')
    + q2 W# t* o# g9 T0 V. zOut[104]:
    3 H9 E& S: S# Z0    ****a
    6 @1 u; X' b: m  }1    ****b- F0 B+ u3 A7 g
    2    ****c3 J+ s8 @% W) C) }% y  q
    dtype: object
    7 g& j0 S0 ]! B/ m# u, j2 d" H  c  d* l' ]
    s.str.pad(5,'right','*')
    * p+ T( |9 G0 }7 JOut[105]: . \! _. \: k) y
    0    a****
    % R6 c/ U0 L- X  O  z1    b****, s* ]: ~# f5 B- h
    2    c****! ], ]( m$ O# }- E# I
    dtype: object. f" E; c" a4 E5 }: T
    8 [' V6 V2 K# @' F" F) A
    s.str.pad(5,'both','*')
    ) ~' h, Z% @! SOut[106]:
    5 V1 u! O6 `) c9 u9 O1 z" q0    **a**
    & L! Y9 r6 V4 b1 ~9 ]% A/ k" {1    **b**
    5 ?- Z( l. o0 E( J& `- k9 n1 u2    **c**3 _" P: Q+ ^; M2 m8 n& W
    dtype: object% X# N4 a8 D1 \; y: ^

    & C" u% e& [4 Z1* m9 Q( a1 i6 L5 J
    2: u1 v8 V) ]/ ?+ D: o9 @/ ]& `. C/ d" K
    3! H0 o# S7 v% g. z# Y) {
    42 H1 G8 A0 p' c# h( p
    5' M. [, q* T% t4 V) K' |( y1 T
    6
    9 n( B0 X3 n- M- v7
    8 ?" P6 ?* L4 I6 e+ w  r3 l( Y8( q8 }# M  f" t- `/ j
    9" u( C4 f2 K" J. p! ^
    107 j& [; |0 p, `1 R
    11
    5 z  ]* e1 m) t3 m( y: A& j/ R: M+ P12) v  B  s7 G; G# `
    13( u! p9 Z* x& Z7 ~" N
    14
    1 z) ~2 k9 @3 h5 @, I" t( z( _; Y8 D, N5 Y150 W1 t6 |1 k: d- m7 ]
    16
    ! R: [; N+ D7 u# s" p1 [" F2 r6 l17$ h3 z4 v& D$ }; K
    18
    + g: V6 M5 W1 ]0 k+ F/ Q9 u1 a19, J$ M6 S; U: ]' N: X
    20
    / w2 t, C$ _3 D, u. l" P) Y' ]' G21$ x4 r3 H- A1 b  C* U
    229 Y  A+ B" F8 ~1 B0 F2 w
      上述的三种情况可以分别用rjust, ljust, center来等效完成,需要注意ljust是指右侧填充而不是左侧填充:
    # P: V2 i( w! V: s% S/ J$ |, d0 t: C, p
    s.str.rjust(5, '*')
    8 p) C9 L  D1 C9 L* ^( U4 [: ^Out[107]: ' u, R5 Y6 W2 {( O- \6 E
    0    ****a' V- C* f& p9 W2 X& r; o2 h
    1    ****b- p8 f$ V+ h7 s! S: P& K5 a
    2    ****c
    . T- n) V# V8 U+ n* S) Qdtype: object  |/ v' L8 M- t. O4 ^2 a9 c

    ; V5 S& [5 n/ e3 [* X; ds.str.ljust(5, '*')
    5 `* }1 g& [& `# }2 D6 m/ c; `Out[108]: ; d. O; I6 Z4 P$ \) Z
    0    a****
    * z1 B6 L+ y7 o' J3 }( G; Z1    b****8 C4 U/ V4 ~( d
    2    c****
    " T; r% ]; r- ~. |( ~+ P& wdtype: object
    7 b3 V+ S. V9 ?1 ?+ X: R: }
    3 j/ D* |' r1 r+ @/ Ps.str.center(5, '*')9 e7 d  d4 j1 [9 k9 K& t: z6 j
    Out[109]: 0 v; [4 b" ?) \" X
    0    **a**
    / X) O- n3 ]" o2 t2 e4 G" N1    **b**
    1 u# r. y# Q% a- r$ ]2    **c**0 a" s* R# F  q' c$ ?  F
    dtype: object. F$ l/ F) P' J  a9 B, C+ p. Z- b; U
    . H. V! S: [" A& r) j
    1; Q/ g3 a! f, G! j$ ^- h
    23 c. [9 b# B5 q1 ]. x
    3
    ) s3 d! j/ \" h4/ V, p+ ~- }* ^2 W+ J
    5: K$ ~# d# u  H
    6
    5 f+ w9 O* P: v0 o4 ]7# T! f4 [) C: I# C/ v5 s$ c
    81 d7 Z4 V$ g2 A8 f2 H
    91 A7 @" d* w& k0 x5 F: A- \
    10
    # P1 M: g- B: R  B$ h6 K& @11
    ; o2 U0 O! C* j' Q12& F; A, }8 r% |0 _1 B6 ^9 M
    13
    " F6 p) J( u: X/ j& D: J- L14
    & P9 v  C! ~. a2 b5 E# w) H15
    ! N* _/ j+ {- M8 r6 K4 D16
    9 n9 Y: F, S8 K8 j5 k3 L174 C, P' x% E& j0 l& e1 ]
    18( c: U& u: s6 i3 c0 Y! x' L. {9 o
    19& |. e1 }4 b* J3 R1 \0 S
    20
    : e, T% W- E" ^: y+ c6 s  在读取excel文件时,经常会出现数字前补0的需求,例如证券代码读入的时候会把"000007"作为数值7来处理,pandas中除了可以使用上面的左侧填充函数进行操作之外,还可用zfill来实现。
    2 v2 L8 ], y; [' l  k  U* J5 ?1 n
    s = pd.Series([7, 155, 303000]).astype('string')% O. L. W: y1 _6 J

    - U6 J  O6 q6 A# `: ?. W" @  H: T6 j2 [s.str.pad(6,'left','0')
    7 l( o4 D( [0 L6 D6 ~0 z3 U5 G/ MOut[111]:
    , u0 Y* _) I2 l* \1 L0    000007
    4 r8 {5 j' C+ \: O+ T1    0001550 t; A7 T4 u; R, Z, b
    2    303000+ I% y2 M- x0 I: l$ @6 ]6 t
    dtype: string/ S4 z; `( u$ D  Q& [% P; L
    " T* L6 x" B) J0 I: a
    s.str.rjust(6,'0')
    . I- i/ L4 g! ]/ M7 G4 z' W8 nOut[112]: ) C3 ^4 p- \1 D3 K
    0    000007  Y: z9 y/ T  N  d( P1 K. s
    1    000155
    ' L* z. F2 s4 f; Z. M9 d* X1 _# i2    303000
    / k! e  @% }& _" Z/ n) @dtype: string
    ! ?0 F9 ]/ C- \5 K: k8 h" Q: m
    / f) F" v. S+ L% ?5 `s.str.zfill(6)
    , a8 D; O, E( j' EOut[113]: 5 Q# s& w/ `6 s7 m1 z. D
    0    000007
    . e$ S) D; F! L+ k9 e" q$ D4 ~7 m1    0001557 R5 R/ y# q4 D( f! o
    2    3030001 G$ E1 I/ z6 P. b
    dtype: string. c9 h& N+ H# P- H5 S1 Q, h
    ' L' p! g2 N( B8 |& q& \
    1: f3 r- u, ^! c8 k
    2
    3 S) B; x. H9 m; U4 l/ ?& k3 n, C3
    ' s! l5 i. k, t* Y4
    4 ]+ f+ L: t6 y$ Z/ o; W5. w2 Y+ ]/ }. k
    60 M& d0 D( Q7 i4 ~1 j2 n# J
    7
    2 ?" ?! [' v" v8' n& T& ]2 K5 D$ \3 g
    9
    . P% l2 P8 U* \( i2 {$ J7 A8 z10, A, D/ i! G( Z! w$ l; B& @% I
    11- U* c6 D0 D# D" ^! d% _% X
    12  L  w2 N& d; ^+ f
    13, c5 ]2 {4 U4 b
    14! I; i  c& b4 G
    152 I5 a# ^. Z: W. P' H7 y
    16
    . b& U  w! n; S( e* B173 M  a. h, a+ r5 G
    18
    " P3 y& Z/ r% o19
    3 f, o8 f( C: F  L/ n9 w! T& I20
    3 q$ o4 E& W# I4 C. o9 T210 b( O" O% Y7 M4 g7 Q% g
    226 u+ v9 f$ W* s' N: u' h1 w
    8.5 练习! v- m5 V  {9 m4 k1 |# d
    Ex1:房屋信息数据集
    " x/ R2 ~3 C+ u8 C现有一份房屋信息数据集如下:- A) [7 O; }" S# Z- u/ {

    1 K/ i2 d* V- J( D# mdf = pd.read_excel('../data/house_info.xls', usecols=['floor','year','area','price']). x/ @0 ^" o3 ~8 l1 K& k
    df.head(3)
    # E* S1 C+ J7 P5 F4 JOut[115]: ( d4 f+ D2 {6 n
          floor    year    area price
    - ~7 v9 k, W/ ?' w. ?0   高层(共6层)  1986年建  58.23㎡  155万! R6 b+ y, H8 W: g& b$ z( l
    1  中层(共20层)  2020年建     88㎡  155万
    & M, P' U" }4 E5 Q2 o! ]2  低层(共28层)  2010年建  89.33㎡  365万. c& s: r1 R/ j$ E/ N5 @3 r+ y
    1
    / T# u6 t% P# i% M3 E  b# Z2
    7 C) B- w4 [0 W  B* e. P9 D3
      n) j+ j! k) N" }/ @( J" F1 j4
    3 G: H: y. T* z6 V" w7 h1 o* ]5  q5 Z0 c, M8 d9 @, Z- d
    6! D- c8 S! y# A: u# }/ p# c
    7
    1 Z5 r- M/ B% p将year列改为整数年份存储。
    1 e$ Q) F% V/ l. y. j将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。
    & h* e) a+ k- Y; H* ]1 o, h计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数6 O3 I$ v& \9 k  s# T, ]
    将year列改为整数年份存储。
    4 n) F, J$ }. B' g" |' @  h"""9 Z7 t9 s* ?) e4 S7 U# {1 C3 {
    整个序列需要先转成Nullable类型的String类型,取出年份,再将年份转为Int64类型。0 y' A" A2 r3 r$ {: O
    注意,转换的类型是Int64不是int,否则报错。即使astype加参数errors='ignore'跳过缺失值,
    , e% d+ f9 }1 Z3 r9 v' a, y转成int后,序列还有缺失值所以,还是变成了object。* b( D! E1 t  P
    而整个序列转为Int,就还是Int类型,缺失值变成了 pd.NA 。
    * Q7 J: c1 e1 B6 v9 k" a""", R# s4 W! c- L  r
    df = df.convert_dtypes()
    ) o) T6 g) A) X) f6 y" N! ldf['year']=df['year'].str.replace('\D','',regex=True).astype('Int64')
    * g* z( f  l% Ydf.loc[df.year.notna()]['year'].head()  f$ N/ I1 e! U
    & |$ R( @' W2 v% d+ i
    0        1986  y/ G/ D8 g) Z% c( |
    1        2020
    $ l' w: I& Q6 W% X" a9 ]- E0 }2        2010
    $ d! X: W; P  g( q; b& d: m: w3        2014
    2 B5 T" m$ c8 A4 b5 y4        20152 ?  V( Y# \! W/ L- b
    Name: year, Length: 12850, dtype: Int64
    ! \/ {; B- T5 N, L/ w+ U/ K" a7 N& e7 }
    1
    ' u# S  F6 o; N8 M  ?! P4 r* L2- B( e4 [( C* ^
    3" s. k+ k% ~7 m9 l2 i
    4
    1 j1 {$ \+ y1 N9 _  `" n$ |5+ ^& d. a8 d2 I, T3 L1 F
    6
    " e5 D* I# R5 z7
    ( B9 e) a; J: A* i# s. _8
    5 c9 E. W; E( Z0 `3 m9, p+ I5 v. t, \' o% Z' m
    10
    % M7 j. R0 ?3 n8 F5 w$ t) P) _11
    - A% h2 \: p3 G+ Z1 E" G, ?. }2 w12
    & O1 g/ l( Z2 V8 n: s13- I4 h8 e+ Q* S7 F; _2 s8 c  u
    144 l! j% m" V0 o$ K/ X/ h! t
    15
    ' x: [" ~0 F- V# K3 U+ {16
    # Z3 d- H, e" x/ o- j) t% x: y* Y参考答案:: z1 [$ `' M, }) v% z$ `" c

    ' ?0 V: }7 p) J+ g' i# R不知道为啥pd.to_numeric(df.year.str[:-2],downcast="integer")类型为float32,不应该是整型么
    * P3 l3 e6 n; e5 c* N- H" p" _$ G& H# l
    df.year = pd.to_numeric(df.year.str[:-2]).astype('Int64') $ g% C( S. V/ g: p3 y$ P
    df.loc[df.year.notna()]['year']
    & S5 j! R( v4 W4 [1( ^# T: e& z: b+ d
    25 {# l" f( K6 q; N5 j" H* H6 h* k
    将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。' }' @( L+ {" x" B9 W
    pat = '(?P<Level>\w+层)(?P<Highest>\(\w+层)'
    7 ]3 V$ U. ^" @- Y; Xdf2=df['floor'].str.extract(pat)  # 拆分成两列,第二列还是(共6层得形式,所以还的替换一次; {  x! s7 ]. }
    df=pd.concat([df,df2],axis=1).convert_dtypes()  # 新增列拼接在后面,再次转为Nullable类型* ?/ O$ e" B, m3 s+ n- f6 U
    df['Highest']=df['Highest'].str.replace('\D+','',regex=True).astype('Int64')              + {0 Q9 [& l: h3 v. [
    df=df[['Level','Highest','year','area','price']]! K2 y3 N9 r( i9 _; X9 C5 u; V
    df.head()! ~, w0 m& K! E  t  P, P, Z* A

    9 p1 I. a6 l/ Y4 w   Level  Highest        year        area        price7 X+ c2 ^% a: L6 q8 ~( ]  ~
    0        高层                6                1986        58.23㎡        155万
    & B+ a! l' [" ]$ w1        中层                20                2020        88㎡        155万7 E' `! @6 ]! G1 M9 }
    2        低层                28                2010        89.33㎡        365万, b: b7 d6 N( B( c
    3        低层                20                2014        82㎡        308万# T$ I7 g$ d$ T, |8 |
    4        高层                1                2015        98㎡        117万
    ( h9 d2 ]% ^6 d/ I, `, H  z! d" K1
    9 T# W& Q6 o, A# S21 d9 n9 O7 o5 y6 e3 ?$ `! n( r
    3
    . P7 i, }. z" X$ T$ O4 T( N47 W0 v# p( |2 u% T
    5
    # W- T: T/ x+ D$ d: G6
    ' G2 |5 H# b9 A3 M  w7. ~( q* U! ^. {
    8- Q* X  ^: @, D' ~8 p5 v
    9
    ) \: h1 B' {5 P( U10
    , _% W' L- Y$ t7 I/ y11
    7 l1 Z: g5 E/ N2 W; y* d1 h3 D12; m' l6 S5 f: ~5 R% z
    131 q( i# C4 O' S) j* X
    # 参考答案。感觉是第二个字段加了中文的()可以准备匹配出数字,但是不好直接命令子组了
    9 B& y" G( `- w$ A2 o0 y, M/ r% Spat = '(\w层)(共(\d+)层)'
    . ^# O& q. ]6 }9 A: Tnew_cols = df.floor.str.extract(pat).rename(
    - p) |) D5 d$ A6 q4 K                    columns={0:'Level', 1:'Highest'})
    % t1 J4 d; Y: z  Q* a; e7 q- ]# R7 i, e( ?- d6 G5 `
    df = pd.concat([df.drop(columns=['floor']), new_cols], 1)
    , b) ]; D9 w% r4 F. x5 {df.head(3)" w9 ~3 x9 {6 U- `/ h

    2 d' t: N) n# `: R$ Z- Z, R3 q* a+ VOut[163]: * L) s& t8 k% z" N; u$ ?9 L
       year    area price    Level Highest
    ; _7 \6 u" X- }0  1986  58.23㎡  155万    高层       6
    7 P, _: r6 ]0 R* c$ e) X1  2020     88㎡  155万    中层      20) n- B. U" i( G" F# Q3 K" G4 ~
    2  2010  89.33㎡  365万    低层      28
    % ^: U. x3 z6 U$ x) F4 }1
    ' l4 a' h' T' ^7 f4 P5 q5 d. r2
    ) l' z( p5 W4 @3
    7 n5 f& r( R' V; ~49 \6 \  u. V7 i: W' l$ u) u% q
    53 r: v/ C# u! f0 n7 U# r+ [) M
    6: ~/ x9 D, V+ U$ e  e
    7
    ' Q% i: u7 H9 G5 o$ N4 t8
    - c9 N3 [- j! N/ K6 X! g7 o8 }. D9' d# z1 \9 ~9 @  z. i' q; ]$ L6 r; B
    10/ h& ~0 z6 h  w. k
    11
    7 ~* [  o6 Y3 V( R+ P12
    # G* b- t4 q4 F3 Y( x13/ u) g  M9 d8 I7 h4 c3 Z1 U0 i
    计算房屋每平米的均价avg_price,以***元/平米的格式存储到表中,其中***为整数。
    0 f( s) V$ M  Z6 m* |# {" }""". \7 P5 N( n4 C  Z9 ^$ R
    str.findall返回的结果都是列表,只能用apply取值去掉列表形式, u4 h- u% ~7 b% N8 C1 e; Y
    参考答案用pd.to_numeric(df.area.str[:-1])更简洁
    9 I% w; o! J2 s: B2 d由于area和price都没有缺失值,所以可以直接转类型
    ) ~+ f/ v! v; y, f6 p* [0 q3 ["""
    # j8 \; b$ h  M) ?8 jdf['new_area']=df['area'].str.findall(r'\d+.\d+|\d+').apply(lambda x:float(x[0]))3 ?# l; D. k  T3 J$ N) B! z0 H4 {
    df['new_price']=df['price'].str.replace('\D+','',regex=True).astype('int64'), F, l/ v: m) s  w9 I# O* j) K3 M+ U
    df.eval('avg_price=10000*new_price/new_area',inplace=True)
    % a  M: G& c% i4 v" \( p* S  C# 最后均价这一列小数转整型直接用.astype('int')就行,我还准备.apply(lambda x:int(round(x,0)))
    3 E7 [! n" y% g0 P# 最后数字+元/平米写法更简单2 G2 z- ?# ?2 p3 N
    df['avg_price']=df['avg_price'].astype('int').astype('string')+'元/平米'
    1 e- U! N0 ^4 G! l1 v; O& edel df['new_area'],df['new_price']
    ) I7 H2 O) s8 A% D* ]. R; m. Ldf.head()
    5 S! V5 H) E' w! \8 l* s) R: x) a( Y6 r
       Level        Highest        year        area        price        avg_price
    + O4 x, v( T: c- X0        高层                        6        1986        58.23㎡        155万        26618元/平米: v" e+ r, ~% K8 n* N
    1        中层                        20        2020        88㎡        155万        17613元/平米; Y3 s# F; g! o! d& B" r1 X
    2        低层                        28        2010        89.33㎡        365万        40859元/平米, H1 T. B7 }1 m' C
    3        低层                        20        2014        82㎡        308万        37560元/平米
    : G8 {; ?9 U  _' o4        高层                        1        2015        98㎡        117万        11938元/平米
    $ K) N% X+ F$ L) a2 q4 G$ ^% Y
    % T; h; x* ~6 G) C6 m' m8 r; T3 u; |1, p7 n% d5 m* A8 O5 O1 D
    2
    & |2 o. j* Q1 v8 u8 y* R3
    % K  |, M8 C' n& A( {45 a, H( N* z% @  b* J1 R! S) ?
    54 ?2 B1 \3 U8 Q. c1 Y! S
    6' R8 X* z; S- Q/ L" M/ `
    7
    . Y& q8 m1 P2 x* Q89 T- i4 f9 D1 C; K9 Q3 q
    9( Q) m- I8 V4 x. v% L3 F8 i
    10
    9 n: Q1 r1 ~0 V" ^( k11
      N# }5 c" _# x! u$ }12( Q* T( O* Z  q  T
    133 N: T4 W/ g6 x+ x+ u0 Y' M6 g$ @2 f
    14
    . m' Z* A- L* m* B: V/ m8 o15  U3 x& c- Y' Q% s9 ~, ?
    164 n3 d3 R$ D" s, l8 l
    17; N, X( p! {$ C; Q9 T( c0 \, C' u
    187 |! q! m2 [* q1 o5 L8 P; N0 X- d
    192 n; X2 W6 H8 h3 a8 ?
    20
    - J  C" o' I- J5 T4 u# 参考答案
    . Y, A8 R4 D& gs_area = pd.to_numeric(df.area.str[:-1])5 w9 v/ s4 X* ], ~
    s_price = pd.to_numeric(df.price.str[:-1])
    8 l5 B6 B2 k2 l) edf['avg_price'] = ((s_price/s_area)*10000).astype(
    & F# L2 f0 s  t" |- [) M! z                    'int').astype('string') + '元/平米'$ m2 [& ~* t! y2 u+ A- {& U
    4 }5 o  ^& r; o- T% r
    df.head(3)
    % N8 k' `: c9 lOut[167]: / E5 i5 M' G: B! b1 J% x" ]: r
       year    area   price   Level Highest  avg_price
    $ a9 P3 ~, n! ?0  1986  58.23㎡  155万    高层     6          26618元/平米+ I+ F4 ?$ {* B% A: p
    1  2020     88㎡  155万    中层     20          17613元/平米6 ~! Z2 ]- u. Z* h
    2  2010  89.33㎡  365万    低层     28          40859元/平米
    6 m6 B6 }' |9 ^0 v6 Z" C# d1! B. V- }# }9 Q
    2. Q: v( _5 e( o% t! a% X; t
    3
    0 m6 A8 s5 g, @- Z" M7 o4' W, T4 m, s, R# u! n, }8 S; r3 n" R2 l
    5
    & d3 E" o7 q7 m+ N6 b6
    5 `0 R  x7 F5 a% d) C7; Z- S0 ^2 p( L  t( M
    8
    5 W6 m2 _  o. O, Z) P0 X. |0 {9+ Y% V2 E# y  e% q
    10
    " C* W% w# a/ q# c# p11
    # G2 N2 b  P+ n; J12; e2 r, Y; k2 z
    Ex2:《权力的游戏》剧本数据集7 J2 E" [; s+ U, G! }) G
    现有一份权力的游戏剧本数据集如下:0 ?' W1 ^4 V9 t* Y9 A6 i

    : D; s+ q$ K6 N9 G8 qdf = pd.read_csv('../data/script.csv')
    - y) ?3 F' d, ndf.head(3)" l8 P6 w. Y1 b

    8 I0 [4 M- j  G) a$ }% L2 oOut[115]: : C! N$ V4 E3 l' O$ a. ~
    Out[117]: + J# ~( @; @  b8 ^
      Release Date    Season   Episode      Episode Title          Name                                           Sentence" i/ J6 j( W' m0 }
    0   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce  What do you expect? They're savages. One lot s...5 i& i$ R  `+ ?% O/ J
    1   2011-04-17  Season 1  Episode 1  Winter is Coming          will  I've never seen wildlings do a thing like this...6 D' p! n1 {' [: ^/ P
    2   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce
    ! q3 x! m4 z' [4 V% l12 @, o, P' D: ]9 R- ?
    2. i( Y  @" r* T. j3 R$ n
    3
    : r. L" Q' @8 d* _3 ^46 L* v; w! k# B
    54 B' r) z7 i" w' z5 t
    64 U) T& i' o/ u
    7
    / _: ?0 Y. K& z7 J8 M88 ^& m; J' k6 A( N9 S2 L5 \
    9
    0 q0 F5 M4 x( B8 [计算每一个Episode的台词条数。  V9 p$ P+ n2 X+ N5 R' x0 E' N
    以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。0 U! Q- K) u1 Y# T- ~6 N) E9 k
    若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有 &#119899; 个问号,则认为回答者回答了 &#119899; 个问题,请求出回答最多问题的前五个人。
      i' Z4 y6 J. R& H3 d计算每一个Episode的台词条数。6 A2 S% R/ z+ y1 h
    df.columns =df.columns.str.strip() #  列名中有空格
    % ]; X' _3 r+ N) Q9 ~! tdf.groupby(['Season','Episode'])['Sentence'].count().sort_values(ascending=False).head()2 ?: d9 q' t2 r1 z) H. [
    9 r& f0 R2 j6 I1 V: P- c
    season    Episode  
    2 k. n2 S: A9 `$ \2 @7 R1 fSeason 7  Episode 5    505
    ' ~  e0 w8 V( j4 F$ OSeason 3  Episode 2    4801 o. f/ m( M* ~8 E2 h
    Season 4  Episode 1    475
    + G2 b8 x1 m% w  X0 l! fSeason 3  Episode 5    4401 ?9 L$ ?" ^8 S9 y3 g
    Season 2  Episode 2    432
    * K" T$ J2 \+ H& s8 |1
    9 A/ @$ g- C1 q2 ?, [: l2
    * G/ O% ^0 k4 b( H+ o3
    ( S. A! s% Z2 R7 S, t4: O' R8 Z& Y9 H8 V* B& Q
    5- x' B. @( V, `- I
    6
    * A2 T$ S7 o. T- b3 y3 G5 y7: \% q1 q* w' y. Z5 F- f# s: S  w
    8
    # `8 ]- G0 [& I4 ]/ h9
    ( j4 ]: l  m7 m- P以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
    . ]( c( Y' \, m8 T: d# str.count是可以计算每个字符串被正则匹配了多少次,+1就是单词数# d# Y. A  A1 u! w! `& V
    df['len_words']=df['Sentence'].str.count(r' ')+1
    $ B, R, ^6 b2 _/ W8 y) T: O  Fdf.groupby(['Name'])['len_words'].mean().sort_values(ascending=False).head()  U( L) t% k5 _6 w! ^4 t1 T- L
    + \% `( _5 p& n+ v* f1 y( s' e$ k
    Name
    " y, a) v7 g% @0 rmale singer          109.000000) o# h& O( b! F* @0 M5 m
    slave owner           77.000000
    ' A) w, A; X, Z/ qmanderly              62.000000
    * Q$ P" H! L* ~8 qlollys stokeworth     62.000000( v; C8 R7 L4 o3 w3 O' a* Q6 X
    dothraki matron       56.666667
      Z  [4 n7 D2 YName: len_words, dtype: float64
    + {' r0 h/ l4 s3 v  w4 Z; M1% Q4 t1 W0 L2 ~
    2  O5 ]" s0 Y# m! {5 R
    3
    2 p. Z# m: a6 w7 p4
    9 f; Y; x3 r+ g& x( D58 K  O6 j% P" b7 ^3 R
    66 ^1 |% S! `% |- ]
    7* s7 z( d. Z6 Z& |$ _
    8
    ! k$ `7 B+ `0 K7 F' w8 _92 C, M6 y/ W9 C9 }
    109 F% p$ W- [- b4 n# b8 h7 T
    11. x* z  \' b) r% J6 j! C1 B
    若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有n nn个问号,则认为回答者回答了n nn个问题,请求出回答最多问题的前五个人。
    ; A# ^# m$ o2 o6 k. d* Cdf['Sentence'].str.count(r'\?') #  计算每人提问数8 b: H1 j1 M" R
    ls=pd.concat([pd.Series(0),ls]).reset_index(drop=True)# 首行填0) e5 D. F4 @0 x
    del ls[23911] # 末行删去
    ! _8 K7 [# f, ndf['len_questions']=ls
    ( K7 _3 z- l' o2 ^df.groupby(['Name'])['len_questions'].sum().sort_values(ascending=False).head()
    , t7 p' H( B8 l" t: }* W
    ; a( T; a$ n! ^- r% ]2 S, H  AName5 Z9 S6 r0 n: P; y( w* e
    tyrion lannister    527- Z- v# A: C, E( ~7 }& g- N0 c4 v) D
    jon snow            374
    + _( F+ @: w2 ^2 \jaime lannister     283
    ) g$ h0 ~3 @+ y, m+ \arya stark          265. ~- G, A3 E) O' M& Y
    cersei lannister    246% C6 |% J0 V/ G& s
    Name: len_questions, dtype: int64
    6 L8 k+ t9 {) d% k# E0 D- G0 P2 C0 M0 L0 u& V
    # 参考答案
    ; S4 k4 Z5 ?  h; @/ Gs = pd.Series(df.Sentence.values, index=df.Name.shift(-1))
    # z* C5 v, m& L6 ^+ f. B# ^s.str.count('\?').groupby('Name').sum().sort_values(ascending=False).head()
    9 A0 B% E! O3 t  ^! Z0 y, f. z' L6 D' ^" T2 a$ Z- j
    1+ `9 H" d! S0 g
    2% |4 ~' W' F. x
    3
    - f' _$ U" s7 u) h3 T0 G) t4! A8 M& Y, y, F. a9 Q9 ^% Q( {
    55 B# W2 M# O9 }, j. x0 G6 X
    6
    + G6 R; `* P% y6 I" F& Y* p7) |0 v- G; L  d$ c- ^- a
    8) \1 Q0 c5 p6 g2 Z; V/ t. Y/ U
    9
    : B/ `2 n. P$ i" N10( l  H- ^( j" n' J: q2 B
    11
    ' B3 v8 [/ m$ T, B: {* n) H+ U* t12. H3 O( @1 T/ U9 ~2 W  H
    13- T1 y& p& N* J& n' P" i
    145 i' Z+ b- u9 Y4 k! @' I
    15% s8 n) y6 s& |% u. q
    16& O) u; A5 L, D- b- d; e
    17
    - F) Q* \. v; s/ w9 j! k( P8 z$ z第九章 分类数据
    0 z2 E# D& C2 ~) M& wimport numpy as np  M. b& M0 \- X0 ^9 Q) v/ w
    import pandas as pd
    4 _( T0 e9 s6 B6 {1
    : u# g8 ~7 N# R6 m1 V2
    2 @; w2 O: ?8 N9.1 cat对象* [5 J- a; P8 `) |2 m1 {
    9.1.1 cat对象的属性% ~) u6 W7 j2 Q1 F
      在pandas中提供了category类型,使用户能够处理分类类型的变量,将一个普通序列转换成分类变量可以使用astype方法。1 f8 i! d1 @) @3 I, E

    / q9 u, n' e! N5 @' @8 jdf = pd.read_csv('data/learn_pandas.csv',) V4 i1 M" h4 C4 G+ u9 Z0 E7 f
         usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight'])( O! w" R6 _6 ~% R& A  u9 u
    s = df.Grade.astype('category')
    % k1 |9 X9 K1 A  i+ x4 u+ ^! ]) Z1 C' h. S* i
    s.head()
    + y' M. v3 k% i0 W. M8 }Out[5]: * e. b/ J! j. [2 h8 [
    0     Freshman1 r7 E- n5 S# w$ X
    1     Freshman& [5 l6 k( p7 z
    2       Senior
    ; I: X! T/ b( K% S# \3 W( M4 r, E3    Sophomore& V4 ^9 @! Z: W! m2 Q1 m8 k
    4    Sophomore
    0 s. O' R& n, q# @) W/ KName: Grade, dtype: category
    1 A) @- Z2 y8 O4 ]7 u; [. FCategories (4, object): ['Freshman', 'Junior', 'Senior', 'Sophomore']7 f7 ?6 C5 {7 ^  h* W2 L: }
    1
    0 d% D. u2 E3 H/ m+ {# F: h$ R% C2: p& X+ g2 s: m# R8 z& p6 q" d. H
    3
    : @5 F/ ?# m! b4 g4( _1 X6 @+ d9 n  @/ P- M9 ~
    5
      d' C8 g$ O7 S- Y6/ |/ J9 T1 J) F$ J+ j6 Y0 y
    7- Y1 K3 f8 r7 Q# ~. G& Y+ b  ^
    8. H% C) F$ P& z3 D' O8 j7 Q
    9
    $ X2 C# _: d1 y/ C2 l10
    8 }, ~- a% V6 j' I+ H11
    6 l( P; G, i; k+ W12
    ( x* X' a- K) x) w' g( n/ G13
    4 A' F" G- B5 f( `9 h6 Z  在一个分类类型的Series中定义了cat对象,它和上一章中介绍的str对象类似,定义了一些属性和方法来进行分类类别的操作。
    & B' k3 h& D. F# r) R9 Q3 M
    ) G; K- u! Q+ ]% i  zs.cat$ w2 A3 ^8 c. h0 v
    Out[6]: <pandas.core.arrays.categorical.CategoricalAccessor object at 0x000002B7974C20A0>
      s+ g; v  _/ }2 Y& ~$ z0 [14 t+ A+ P# v5 @5 ?9 l7 ]2 M
    2
    3 L$ T" c1 i4 {: Gcat的属性:  p- {: g& {$ _3 B* J7 a

    ( T+ V. Q# W1 X3 A5 Bcat.categories:查看类别的本身,它以Index类型存储
    ; d' n$ D5 l& S% Pcat.ordered:类别是否有序% B" l: `& z1 t( z
    cat.codes:访问类别编号。每一个序列的类别会被赋予唯一的整数编号,它们的编号取决于cat.categories中的顺序# v4 g- \% D9 d0 _2 _! r9 v6 {8 e
    s.cat.categories
    6 }* M: I  x7 c) t) H3 `8 \5 ROut[7]: Index(['Freshman', 'Junior', 'Senior', 'Sophomore'], dtype='object')5 |, `5 D) h& @; {' h0 d+ |4 ?- }+ @, ]

    % n3 Z# E# [* f' L8 k) c) A- Qs.cat.ordered
    7 f3 z& L* n' N# HOut[8]: False
    6 |) Y: ]/ a1 q0 r9 M
    - B  n9 ]! A/ ps.cat.codes.head()0 s/ O* }! f; s- Z7 }+ J% D
    Out[9]: & b- u6 l' y6 C0 m; D1 j
    0    04 O6 @) K! P4 d5 d
    1    0! y8 r' a; H+ @, I, d
    2    2
    ( S- V; b% U& B3    36 t( q4 Z& r) Z" l& T) J4 o5 W
    4    3
    ( Y9 j5 s& M# W  K% Ddtype: int8/ N& N( H" l* Y8 \, G# d" I
    1/ R* l& \/ j8 Q$ m' b+ o$ s) Z1 s
    2' y8 ?7 q( q$ @3 G3 M9 O
    3
    , |# Y6 u( Y1 q3 i" [9 u4
    # g- K0 u! i- C4 D( F  A1 N59 H& ^; ]8 |# D
    62 Q+ {8 W- R3 a0 k# {6 u  ?
    7
    + g( F5 Y( V( ]" A8+ }' n4 g* i5 R8 c4 |
    9
    ' a4 o. A: C6 M: \5 p7 Z108 z! @8 F" Z! h$ T  N; T$ a
    11
    / S; p6 L4 b. w* K- ^+ j12
    / g8 q3 H8 f3 ^3 i3 M9 s6 Q3 d13: l, a* [+ h, b# i; L4 m
    14
    $ A- i/ S8 c- P9.1.2 类别的增加、删除和修改: M; p5 p4 I7 [: [$ @4 O, t& L3 j
      通过cat对象的categories属性能够完成对类别的查询,那么应该如何进行“增改查删”的其他三个操作呢?
    6 J$ {+ U! G1 k. X# u# z2 X1 t+ e( _% g* P* }
    【NOTE】类别不得直接修改
    ; V& o3 e" ?% l) r在第三章中曾提到,索引 Index 类型是无法用 index_obj[0] = item 来修改的,而 categories 被存储在 Index 中,因此 pandas 在 cat 属性上定义了若干方法来达到相同的目的。" u0 I% g- |- ]  I& J% G

    & ~/ D. _% a. p2 p6 w+ D% kadd_categories:增加类别1 i# i) k( D" t
    s = s.cat.add_categories('Graduate') # 增加一个毕业生类别# v3 ?( K% {1 |) a+ K0 E$ |$ v
    s.cat.categories0 |. f0 |: M' O& {
    $ `0 n( j; l  p
    Index(['Freshman', 'Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object'). R: j1 }& F* C
    1
    1 z+ a9 \8 v4 n/ P/ X. t7 f/ b4 o  s2" p& M6 H% J- Y2 M; y' k6 P
    3
    9 c1 W4 Q; \5 j+ `( y# k4. j# [" X6 Y% k- _1 Z6 x
    remove_categories:删除类别。同时所有原来序列中的该类会被设置为缺失。/ l& Z, C1 M/ |$ r- F
    s = s.cat.remove_categories('Freshman')
    1 R$ Y7 \% T, ?) {& s6 H! c
    ( [6 j- x$ S. j/ k8 s4 k2 Os.cat.categories
    & n5 W. N% g/ N' p/ x8 R- F1 xOut[13]: Index(['Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')# ]' w2 h5 I# W# C) F, u0 {6 N

    + R9 s) n& @3 t8 v  k; }5 d' As.head()
    ' }4 E( I  O) W, \; P8 ?. rOut[14]: ! s# L  U6 ~5 t  a; f0 ~
    0          NaN+ ~5 ?+ V  w) m" `  v' N
    1          NaN& a( G4 w, n% X9 f) H5 h% x/ F
    2       Senior; Z3 X  F, L  N. @  A
    3    Sophomore
    / f$ b1 F. O. U, l" J2 g1 u0 g/ Y4    Sophomore$ m" H/ a7 z! k' l) z- a+ s
    Name: Grade, dtype: category6 k  ?: k% }  P6 R, t
    Categories (4, object): ['Junior', 'Senior', 'Sophomore', 'Graduate']
    & M, E) A, m2 L' k3 G6 H- m1
    . ?9 m$ Q( w3 e9 E" j# k9 w27 |$ b  c/ J+ x/ @1 V! P$ H# ^
    3
    / x- V% m& p9 V) D$ P4
    ; p! R0 \+ U6 g8 W7 \+ u4 O5 R7 J+ \5
    / k0 r( n6 t" Z- K; H( _4 J6
      B6 e' G' }# H' {, w7' l& E  L( R- f
    8. k5 P9 {9 j0 s+ w% A0 O0 ^! |
    9; C( _/ H9 o, [. r' J
    10: D. g% N- }: g/ d5 b
    11
    5 o$ E1 D, n# V) b4 s1 i5 y* J12
    ' G# g7 t) B# k7 ~! j. P- m: W13# i. U# d. l: s" P5 R
    14! M$ _; w5 v- c/ R: E) j
    set_categories:直接设置序列的新类别,原来的类别中如果存在元素不属于新类别,那么会被设置为缺失。相当于索引重设。
    & Y& i' A& z$ Q  m: ms = s.cat.set_categories(['Sophomore','PhD']) # 新类别为大二学生和博士( r. D& W" A, |- t# }' s3 N6 a9 o
    s.cat.categories
    1 T3 p' `0 t4 A( |7 K( V' q+ _Out[16]: Index(['Sophomore', 'PhD'], dtype='object')
    8 C  w# \, F  S  d" r
    + u) S# c6 G8 Z) f8 d, X* ds.head()5 `: z. t: I4 a$ M6 U
    Out[17]:
    ' U' d' L! |# Z' ?7 b% R0          NaN' A2 a3 x5 W+ {5 w! C$ N$ g
    1          NaN
    ' c4 w) t4 ~! E3 v5 _3 ?3 `4 c1 Y2          NaN
    ! G" j) {5 D! G5 K+ y& J* s2 i3    Sophomore( S! m1 [3 I; q5 V0 i# ]' L; {' `
    4    Sophomore, P6 L8 P1 n5 j' K5 K9 {4 K
    Name: Grade, dtype: category, F" I) ?4 N  |9 J' N( A
    Categories (2, object): ['Sophomore', 'PhD'], k/ J" K$ m5 ^
    17 W8 {# \: d$ P/ [9 \
    2
    4 ?1 ~6 P% u; K$ J, l7 P3
    $ q3 L6 ^( k( y; `2 b, m45 T$ e  v5 r! m
    56 G9 _" f+ c+ O8 c7 z3 Y
    6
    : B8 |/ I' \9 I4 C8 g7
    5 w( x8 L( z: t) S8& w% M, h8 g4 }* I1 f" A6 H
    9
    ) V; B' w# c, w, _6 e10" D+ Y$ e, d9 ]/ s" M
    118 l. B9 m/ w8 c/ e1 \3 G5 \
    12
    1 s9 l  e6 O& k& V0 ?+ v+ b136 l9 `8 l2 x- h7 {" e
    remove_unused_categories:删除未出现在序列中的类别
    + t+ w3 F' a( d/ y. u: bs = s.cat.remove_unused_categories() # 移除了未出现的博士生类别
    ( Z; m9 j3 }/ r; u. n7 Js.cat.categories
    : ~. y% f2 a: Q- d; @8 w! }
    ( U* L7 u; @3 b! bIndex(['Sophomore'], dtype='object')
    & E: t4 [7 Q, I/ U1
    4 F$ ]5 P5 a6 w* Z5 j2
    4 [4 ^, C: D* Q2 w* @4 P, [$ I) H3
    9 f6 r* R3 p: {: {( m46 X7 ?" H* H+ Y! Z' v# d
    rename_categories:修改序列的类别。注意,这个方法会对原序列的对应值也进行相应修改。例如,现在把Sophomore改成中文的本科二年级学生:
    9 X7 V+ F7 u6 ^! Is = s.cat.rename_categories({'Sophomore':'本科二年级学生'})
    + s' V/ R/ Z" p9 w+ q5 Z  bs.head(). [* h8 p4 Q9 T5 g
    3 N$ V# W, B+ A- l7 t/ z/ X
    0        NaN/ l5 J  z# v- k7 k* y! e( ^
    1        NaN5 S6 K% T2 w/ @. K8 e
    2        NaN
    + ?( ?$ _4 L1 u) z( }3    本科二年级学生
    6 c% O) |/ r2 [' F6 l% [4    本科二年级学生
    ; T! H. X1 [) r4 X3 E4 ~; nName: Grade, dtype: category9 _4 H8 L9 d4 w% A/ \" r2 v' {8 \
    Categories (1, object): ['本科二年级学生']7 Z1 m/ c+ x) [, n: P; y  v
    18 o3 z3 j9 m4 p& ~
    22 O* z) ?2 c: R5 X! U7 I! A
    3
    * y# N9 ?/ c0 r. |. P" W( [0 d1 n4
    - E/ |4 ^$ Q4 R) X+ F5
    8 q4 _0 x4 ]* h# n: H* S1 f, B2 d6
    * K+ z/ @) @$ Y0 x7  I8 @: h1 h( ~4 I1 j3 l% j
    87 e- A. {: I! k
    92 r0 r, A8 a4 L0 `3 j
    10
    6 m  S5 g7 q/ k, G' d9.2 有序分类
    + {* o8 f( a& k7 T2 {. U$ B9.2.1 序的建立
    . |1 e' g. V: g% i  有序类别和无序类别可以通过as_unordered和reorder_categories互相转化。reorder_categories传入的参数必须是由当前序列的无序类别构成的列表,不能够新增或减少原先的类别,且必须指定参数ordered=True,否则方法无效。例如,对年级高低进行相对大小的类别划分,然后再恢复无序状态:
    3 b8 _, v7 w  q0 o+ E7 k
    2 n( J6 t0 n; q0 g5 M: h# @s = df.Grade.astype('category')
    7 y1 w8 |' y0 |s = s.cat.reorder_categories(['Freshman', 'Sophomore',9 O" M/ ]6 H# H- O0 X; B1 w8 g
                                  'Junior', 'Senior'],ordered=True); n1 m( D: `, z1 i8 o
    s.head()
    $ n0 q7 u4 b7 C' U+ v7 POut[24]:
    % @6 I" J; n! l% s0     Freshman3 _2 e* F4 l% o( I- M
    1     Freshman
    2 u- W) r6 q0 p6 c+ |2       Senior7 R. @  a$ f5 G7 e3 a6 `1 d1 X
    3    Sophomore
    1 F  `. G5 I; s% X4    Sophomore
    ( P# z: ]' y7 \' Y# yName: Grade, dtype: category% Q6 E% P7 M  p/ E  X
    Categories (4, object): ['Freshman' < 'Sophomore' < 'Junior' < 'Senior']! H3 n" [0 d* x  K. m9 Z. E3 t

    : a# r. y% z& `$ F( [, y9 W& Vs.cat.as_unordered().head()+ e7 T7 E+ I: F& z$ z5 d7 M- W, w
    Out[25]: * _) d/ ^& S$ ]5 u
    0     Freshman
    " {; h" W3 b1 d1 j1     Freshman  U+ e7 g) Z' m  ^9 w
    2       Senior: \- o; _) e: m" ]- l0 l( ?4 x7 E8 p
    3    Sophomore- R* Y9 r0 l5 J3 |, s. x# a
    4    Sophomore
    ) b/ S+ \8 r- U5 [) ~3 e  WName: Grade, dtype: category
    6 f" Z- g/ N5 W8 tCategories (4, object): ['Freshman', 'Sophomore', 'Junior', 'Senior']
    + J1 `( o9 g2 {/ s/ i: b" L
    4 a. i) X% B, O( h1. Z, Y7 O5 R2 Y; [  @. j
    2
    ; h+ j' q4 T' L  ?5 ^* V) B7 E3. X+ ?2 N$ W8 ]+ I
    4
    - E# n/ }7 R; r, q3 r5
    & m3 B9 `0 ]5 R. R6" z1 w2 p$ ?8 d3 c9 V/ O) C: |0 ]
    75 G; P+ M5 C; ~* f& H
    8
    ; ^; K2 }; n7 K$ q  u& J& K. q5 \5 n/ g9. F' ~; I- @) D- u" L8 [
    10
    2 |4 ?- V) `/ }, ]5 [11& l0 U, A8 l. `6 I. s
    12
    . R2 {0 F* }6 E) |; v8 F9 W* I13! q# D! C# A# }
    14
    , U* ]8 l( N( P7 p15+ E2 X: r2 Q& p# V
    16
    3 d6 v7 C6 S. [17
    # `/ {$ y' H+ `. G" W18- _& j3 L2 l% ]$ X& H- U* I
    19
    2 v, z# G% Q+ ^3 S" F0 M1 A7 G20
    - D# K% s- a6 j+ d0 ~( t7 o21
    ! b' J( J+ o- {: @22' H4 h9 d, G' C, K
      如果不想指定ordered=True参数,那么可以先用s.cat.as_ordered()转化为有序类别,再利用reorder_categories进行具体的相对大小调整。5 T7 N6 d7 [" T+ R

    + [; E! n' J3 M) e9.2.2 排序和比较
    ( B1 i+ e' l/ {0 M- `) u8 L在第二章中,曾提到了字符串和数值类型序列的排序。前者按照字母顺序排序,后者按照数值大小排序。
    , Y$ m1 s' ]8 j' _( ]
    + \5 A- L  {& f  F& r* @  分类变量排序,只需把列的类型修改为category后,再赋予相应的大小关系,就能正常地使用sort_index和sort_values。例如,对年级进行排序:
    4 r. d0 j- q8 d/ @" ~! M7 ~$ p# T7 t7 y* [6 }9 N/ e
    df.Grade = df.Grade.astype('category')- e8 c! k7 N' w4 Z* L- h% I
    df.Grade = df.Grade.cat.reorder_categories(['Freshman', 'Sophomore', 'Junior', 'Senior'],ordered=True), T& F: w' h. z. u  H1 Z& E
    df.sort_values('Grade').head() # 值排序
    # \' m0 n' \* j, N( ZOut[28]:   i/ |1 v' Z" R3 O6 ^$ s
            Grade           Name  Gender  Height  Weight+ y0 M  \( Y7 z+ @0 F& E) F& ^( g
    0    Freshman   Gaopeng Yang  Female   158.9    46.06 i& d4 s  s6 h3 B5 P& y
    105  Freshman      Qiang Shi  Female   164.5    52.0
    : u  ^2 }/ Q* s+ g96   Freshman  Changmei Feng  Female   163.8    56.0
    1 [% G; B+ P2 J( p5 c88   Freshman   Xiaopeng Han  Female   164.1    53.01 n6 q2 U. o# F2 D) V! e3 V% E
    81   Freshman    Yanli Zhang  Female   165.1    52.07 p# p, a/ f  S+ J( m9 \8 K
    " h0 L& A) E) K6 J* N, A
    df.set_index('Grade').sort_index().head() # 索引排序# L, h, w0 d" g4 |7 Y6 ~
    Out[29]:
    2 n( |# {# v$ X( Y% x% L' u3 E                   Name  Gender  Height  Weight! Y% h3 }* g' C- q$ R
    Grade                                          
    3 E2 \; A1 ]) Y7 P; ?/ RFreshman   Gaopeng Yang  Female   158.9    46.0
    8 ^8 E6 F0 S+ v# h9 P5 I" }Freshman      Qiang Shi  Female   164.5    52.0
    * t+ H7 x2 a& \6 d* [" ^9 QFreshman  Changmei Feng  Female   163.8    56.0) D9 f2 t9 @$ p3 T& h# w4 c
    Freshman   Xiaopeng Han  Female   164.1    53.0
    * O+ W9 ?" S4 }1 N& t% kFreshman    Yanli Zhang  Female   165.1    52.0/ W1 B0 @$ d& v! W8 g9 V4 e4 _
    : {, G/ Y7 D6 S$ Z
    1& f7 Q" I: |6 {/ r
    2; X* A7 b' a: I- ^
    3
    ( o# V1 ~" W5 Q4
    8 k3 a: m) d4 G6 \6 B2 `5
    0 l  Q1 j- j3 c6 l0 S6 o! q6
    3 P2 o9 z. v+ i* f7# }1 ], i* W4 M7 w
    8
    7 c9 j5 t6 }& Z2 S9
    " @* h: f, }7 s  K: h6 x- k; J108 B( [0 s7 x+ q& C2 X
    11
    2 K1 @! \! j0 M) }! ~# K' I12
    8 V& \7 c0 e; K7 J2 Q! v2 T138 }. j$ @: k- {+ V. b( C
    14
    6 S4 z2 T  [/ f# \- O8 X* v15
    ( Q8 `/ x3 u0 z& L7 u- v! M164 S! Q6 x, N, I# ~8 |0 w
    17
    ( }5 a; r0 O5 f+ f1 z; K1 {* M18- n+ M% a6 V9 k9 L" M
    19
    ; K' T# F3 A" D/ k20, ^( K6 r  E4 {% g. t  n
      由于序的建立,因此就可以进行比较操作,方便后续索引操作。分类变量的比较操作分为两类:
    ) e7 T6 d# _2 k8 R1 |9 H4 {! P9 S* [/ u) ~
    ==或!=关系的比较,比较的对象可以是标量或者同长度的Series(或list)。(无序时也可以比较)* b' M4 R5 ?" i0 }1 C
    >,>=,<,<=四类大小关系的比较,比较的对象和第一种类似,但是所有参与比较的元素必须属于原序列的categories,同时要和原序列具有相同的索引。
    % i5 a" y2 q4 R, W$ cres1 = df.Grade == 'Sophomore'
    * ^/ ^9 G2 ~, a, ?# ^0 @
    * E( U* w9 C* S) l0 D+ Y7 gres1.head(). l, E) f* N0 A0 y) L) r
    Out[31]:
    + c) [0 u+ V4 Q0    False1 W/ l# f  L* R2 L0 s
    1    False
    3 ^; T0 W- h% x9 h: ?2    False/ E3 t2 H  Q% J
    3     True( o( m7 d1 X# k6 w" ]1 M
    4     True& U9 L$ E+ a$ l3 r  V
    Name: Grade, dtype: bool
    1 v! S: T: K3 v& Y6 X- ~+ c5 R8 o! C  l& R
    res2 = df.Grade == ['PhD']*df.shape[0]/ R8 i( y2 }* \+ W0 ^4 R
    ( s: N/ Z( c! s- _; {
    res2.head()) z  S" L* Y! B7 Q& U9 ?# ?5 X
    Out[33]: : ^8 _! a% O3 r/ y" m/ w8 V4 k
    0    False
    ' }0 u" `& C& U/ L; Y+ ^, ]: g1    False
    2 E" P  {' ^8 `2    False+ H4 z$ Y2 Z- _) p
    3    False$ @0 j1 P' B1 d7 o: D: s+ I' }
    4    False! G% _) _8 s% O9 g* V
    Name: Grade, dtype: bool9 K: [1 |9 R8 x. w! s6 m0 }

    ' b/ @- p* j" N6 j0 i2 n3 Gres3 = df.Grade <= 'Sophomore'0 c  ]6 a3 I* x& L( I; W2 u" x
    # ^$ o$ ~& V: [0 H1 k
    res3.head()/ n6 W0 i% n3 n! ~" S! G* S6 t
    Out[35]: 7 G# O- V4 O( x* B
    0     True
    % s7 v* Z! [% j, g1     True
    0 i% S' ^# \- n2    False4 c6 f, M1 Y2 x8 L3 _: P
    3     True
    : ?/ {8 b! W4 M& ?4     True
    2 X, ^4 Q( q# X, }* ^! K5 {Name: Grade, dtype: bool7 z) q: A1 K4 \! Z+ [3 z  b, x
    1 N  g# y3 K, I6 T# d/ Y3 h8 U
    # sample(frac=1)表示将序列随机打乱。打乱之后索引也是乱序的,直接比较会出错,必须重置索引。
    3 N9 N+ Z! i2 Z4 Y2 d! Eres4 = df.Grade <= df.Grade.sample(frac=1).reset_index(drop=True)
    # m7 H4 q/ `# J) o1 ]/ O' Q
    5 H9 _: j  U+ i9 yres4.head()+ [1 L9 b$ }5 m1 Z9 T3 B
    Out[37]: ! A# N3 j# s$ `/ J' d
    0     True
    % Y- a" |. N& v; F/ k1     True( E1 k* u4 Z: m6 c. f
    2    False) H0 m3 M$ M. ]8 e3 G! L$ h
    3     True
    : |+ I4 e/ U; h4     True% d# A5 P$ ]) ?# h
    Name: Grade, dtype: bool
    + O( J& _: Q0 w$ [2 q
    ; Y1 b6 o! P9 U+ B+ v$ ?0 B1+ h! \# h3 c% A) b1 m1 U! [
    2) B% j/ c, L# l, z9 h. u+ ^9 V8 C4 n
    3' W5 ^0 C- ]; a6 _2 [
    4
      R# {& \3 c) x: W; \8 N% v( l! V. N5
    ' J3 S9 y1 i" [, Z1 s4 `9 q" L63 K# d9 Q; r1 L: O
    7
    & |0 ?) s# K& s2 e& m' {6 k8
    ! N% S3 v+ q* |5 z6 l+ Y8 p9
    # K  L! {/ c* b) K" _- e1 Q# T106 r- Q4 X9 C) i  [0 S5 |
    115 x8 I  t5 D! Q8 F5 H
    128 R8 X/ n; F7 @7 w! `' j% @
    13
    6 n: V: h; ?) e14
    " k: e7 f; R- j) v3 m3 n+ A1 u- r157 h1 Z2 b- Z% H4 y( @( s
    16
    ' t* V9 b& z- k! Z3 F  d175 Y. k& n! I- V! w! e9 y
    18# m1 G4 o% {) d5 O1 `# ^1 w
    19. B+ ]  b' W  q1 y; V& V$ P
    20/ F1 X0 G) o$ X! s+ N
    21
    ; e6 Y: V# T4 ~, r3 b) ^22
    $ ?( v2 g) x5 T2 s4 ~# \23: L4 X7 C& T) ]
    24' b8 A- N5 w  Z2 }" s
    250 H8 J( L* M: L3 u" e) O
    266 S: N# W  v9 a: i
    27
    1 |! S# A" s; t& I( z+ A28( e7 v( p5 R7 ]# \: @* Z' s, K
    29; V, \' q( p7 O/ I# L: E' G. W
    30
      [/ z  d; d* o31
    $ I/ A0 `" @' x. Z3 t, b32
    * r3 ~# x# [7 w; f! c0 ~33
    ) p: H) \# @/ E0 M8 C* y" x34
    3 P0 O- v5 C" L% k) O6 K35: k, w/ x6 v6 @; d6 ]+ T- A" p
    36
    $ R6 o% d2 h' t  I) n8 R8 R) Q$ E37
    + m0 j+ C4 S( M: {4 p! P3 p38: D; A7 d; X: B
    393 e# D* m9 m; V) ]6 i
    401 w. u# a3 t, a- H
    41
    9 M, x& C( E& D0 h( l  W, X' n9 F0 _42
    : Q  P& L: c2 ?2 B" h+ l# \43. B9 d; z  ]( e. g
    44
    ! F0 s$ ]  r1 H1 R; K1 b* q9.3 区间类别/ E' w8 c6 c. M6 V1 l$ u% k' p
    9.3.1 利用cut和qcut进行区间构造, q  P* |' }, T8 n+ y
      区间是一种特殊的类别,在实际数据分析中,区间序列往往是通过cut和qcut方法进行构造的,这两个函数能够把原序列的数值特征进行装箱,即用区间位置来代替原来的具体数值。
      ]3 j4 E0 U0 w: @5 w9 S# a4 k7 s# W3 K$ h8 S2 E7 C  `2 E
    cut函数常用参数有:
    + p/ _2 O9 {* l/ i( @2 R3 V! ]bins:最重要的参数。/ k% Y: p3 S' `
    如果传入整数n,则表示把整个传入数组按照最大和最小值等间距地分为n段。默认right=True,即区间是左开右闭,需要在调整时把最小值包含进去。(在pandas中的解决方案是在值最小的区间左端点再减去0.001*(max-min)。)4 I# s" O, u8 F
    也可以传入列表,表示按指定区间分割点分割。; a2 q) ?) Z. }2 y. g, B
      如果对序列[1,2]划分为2个箱子时,第一个箱子的范围(0.999,1.5],第二个箱子的范围是(1.5,2]。
    4 {2 s0 B1 f" n! r* U  如果需要指定区间为左闭右开,需要把right参数设置为False,相应的区间调整方法是在值最大的区间右端点再加上0.001*(max-min)。
    + V5 [! `. u4 G$ c' p) Z' l9 W" u1 }) w! k
    s = pd.Series([1,2])
    : R  u  i% m, s9 r8 F# bin传入整数$ R9 _0 o! {- z: Z4 R$ g  K9 y4 w6 Q

    ' Y4 i# o+ f. D' R7 S6 gpd.cut(s, bins=2)  P+ Q) S( z6 \, T* `# }
    Out[39]: 1 `, q4 A% c3 M9 H
    0    (0.999, 1.5]8 R3 k$ [% `; t% W/ h1 ~, y. Z
    1      (1.5, 2.0]6 N* H9 R& d- x( p5 N) V" A
    dtype: category- A" u, w5 h# X: P7 n0 d
    Categories (2, interval[float64]): [(0.999, 1.5] < (1.5, 2.0]]; n- B% ]* {7 l
    0 w8 H6 ]$ L$ V. Q: w2 S4 E1 K# g5 R
    pd.cut(s, bins=2, right=False)# b( E* F4 B& [: W! i
    Out[40]:
    " ~# K+ R0 e3 k- x; _% M2 V- ?0      [1.0, 1.5)
    & ~, {" m  _* v3 I2 [+ M1    [1.5, 2.001)
      c, c7 W. q, E! ^: G) c6 Jdtype: category
    4 s) p9 K2 q0 }+ R3 n0 QCategories (2, interval[float64]): [[1.0, 1.5) < [1.5, 2.001)]
    2 W0 S4 t  l# `, J
    2 U9 i+ b) g8 y, B) w8 Q7 G; a. [+ `
    # bin传入分割点列表(使用`np.infty`可以表示无穷大):9 N( y4 u) W$ g$ O0 T1 [9 G. j0 Q* K
    pd.cut(s, bins=[-np.infty, 1.2, 1.8, 2.2, np.infty])
    ! s8 N4 e5 n7 B8 u* ?Out[41]: 2 M+ J! v6 v8 I- b
    0    (-inf, 1.2]
    8 C! I* d9 c8 y, F1     (1.8, 2.2]+ d3 q2 O1 E/ n1 z7 ~& Z* c  y- i+ t  X! [
    dtype: category; @, V$ j4 B' a6 n
    Categories (4, interval[float64]): [(-inf, 1.2] < (1.2, 1.8] < (1.8, 2.2] < (2.2, inf]]+ M" v+ Q9 k# i2 }
    ! \. X" |* U8 u: k. n" T' \' A# x
    1
    - ^9 I# D& H' `) W6 M) R9 c" g20 P  K1 @: [1 ?8 F
    3
    & Y* h9 D, ?7 x% |- T8 f4
    ) }  z6 Q/ o* M7 c/ E; e+ ?5
    , a2 w! C( [2 m* {3 C  ?0 ~6 v6
    . C  A8 |) X( c4 E4 [0 M7
    4 \6 X: I- a' N8: ~+ {/ a- _* {+ F% a
    97 D( O5 q; f* K- l4 x/ O# p! l# z( E
    10
    - N6 ?4 w/ M4 n* I11" X! Y8 [! X5 Q" h$ T
    12
    * Q; ~8 l( V% B2 p+ F0 O* `& Z  r135 l+ B4 B' o5 N- o4 W
    14- Z1 q. E) L0 e& K' L
    15
    ) b: Z5 l/ Q; m0 \. Y$ y1 w; a165 j, r- W8 X; @8 T% M, Q
    17) W, ~% h) R& `9 C( m
    18
    / B% [, f. {7 \% r4 R19
    , d0 n' Y; Y1 w8 e& o  h8 T20; B4 j0 u! b+ w7 s: O
    214 [6 V* p4 K2 I- P  B) w7 M
    22
    * O  X4 U. S0 ~238 F! g  ?: M! l+ u+ @+ B% W
    24* E0 B. z: o: N- }
    25* ?2 [- F+ w. v: b
    labels:区间的名字6 j# o4 t" L* S! d7 d& m
    retbins:是否返回分割点(默认不返回)8 o% n% u- K) O+ q/ N5 i& b6 M
    默认retbins=Flase时,返回每个元素所属区间的列表3 O$ m) G8 i$ n% W# {* C
    retbins=True时,返回的是元组,两个元素分别是元素所属区间和分割点。所属区间可再次用索引取值
    6 z% M1 a& x; N
    , [" G- c% @+ es = df.Weight
    ) T7 R& @3 H7 s( x) }res = pd.cut(s, bins=3, labels=['small', 'mid','big'],retbins=True)6 C' I9 [: I( x6 P5 t8 ?. z1 i$ V2 M
    res[0][:2]
    + ?7 c5 l; z" d4 X( E
    ) A2 X3 q# E; s2 F$ @# I9 MOut[44]: 3 }0 C% B' _. f7 K; ^
    0    small
    + @3 D2 \6 X; N1 \" D* R4 y: I1 c1      big  |( r4 l; l' v% T5 X3 @3 E
    dtype: category
    : u- X/ g4 r" Y- jCategories (2, object): ['small' < 'big']0 T5 n+ t& o9 m
    4 c. |  e5 O9 o3 x7 z
    res[1] # 该元素为返回的分割点: @; W: k2 P7 z: b. U5 ]
    Out[45]: array([0.999, 1.5  , 2.   ])* p) R1 G& H6 e. E6 m, f+ C% n
    1
      c6 e- t2 m2 i- m- Q) \6 E, t. y( a2& x% |4 A* l0 P( ?' S
    3* q: V5 [% k, f7 `) k1 \) `: ^
    4
    4 m% ~) f' U& W, S$ n51 r( |$ h1 w6 [: S. |6 s! o
    6
    , }8 A' z8 @5 l7 b9 t' `7
    1 i4 {  o, H9 w8
    6 e: V0 x$ I1 Q+ d! i9
    9 u2 f5 l8 U6 C4 ]$ v' W. ~7 q+ V7 R10
    7 n" S  P2 t3 m6 `& P11, u! E. _( k& `9 t4 |& s" K
    12& o& E4 N$ b! N4 \6 H2 a
    qcut函数。其用法cut几乎没有差别,只是把bins参数变成q参数(quantile)。  q* Y3 D4 r6 a4 {5 z" g
    q为整数n时,指按照n等分位数把数据分箱
    * z+ [1 E6 Y7 n2 q6 p* jq为浮点列表时,表示相应的分位数分割点。
    - D( N5 d) b9 o( I4 Ps = df.Weight
    * Z* u3 W1 d  z5 R, V
    ! g6 J# h' y) s3 `. upd.qcut(s, q=3).head()
    7 g* k) @8 Y, I3 o/ e, W, ZOut[47]: . a% G; j' z' H, K- }1 n& X) q
    0    (33.999, 48.0]1 l" B! S2 v: v5 A
    1      (55.0, 89.0]+ c" P3 J) G  X. [) \* h! |
    2      (55.0, 89.0]
    0 \/ v6 m) N* j" Q3    (33.999, 48.0]
    0 P' x, I& t6 F- N( M# `) m( F4      (55.0, 89.0]8 v  R# U0 L; y& ?' P- L$ T
    Name: Weight, dtype: category
    5 k# x: g' i0 j/ rCategories (3, interval[float64]): [(33.999, 48.0] < (48.0, 55.0] < (55.0, 89.0]]6 d" W% F5 b0 u% K. T4 ~9 U5 p

    5 }8 S( H% x( V7 R/ T5 O0 i% ]pd.qcut(s, q=[0,0.2,0.8,1]).head()
    2 r4 f) I6 V. I3 b. N1 Z* c  y( D& iOut[48]: , h! `9 g" u. z5 X+ `6 _! g
    0      (44.0, 69.4]
    9 N% B. t( `! g2 E- ]; n1      (69.4, 89.0]
    + A6 G: ^8 O- B2      (69.4, 89.0]
    7 s, g, W! O6 {4 \  r- t3    (33.999, 44.0]
    * g* f' f0 z  m4      (69.4, 89.0]- ~# y4 `0 |: ]5 f$ j* ]
    Name: Weight, dtype: category
    , N5 O  s( U. A) ~; v  uCategories (3, interval[float64]): [(33.999, 44.0] < (44.0, 69.4] < (69.4, 89.0]]2 ?1 I, g; B$ e9 d3 v

    7 C$ x( g$ ]+ W( n1! v2 H! |& c' ?$ {0 ~  c
    2( R" [: l8 t* W2 b4 y
    3* X8 y- Q, [5 N; Q
    4+ `: m# r# Y" r$ y/ P% U. z
    5
    * a8 Q0 q3 [" i6 e0 X8 z6
    * F: P& K' p# n3 i# `) j7
    & ~: Z( V, g! ?" D1 M. G$ _  m8
    7 t/ k- Z) b% P5 ~% p9
    & i9 E. Z2 s5 b$ R5 x: b104 T$ f8 }. r% `% S- w1 T
    11
    + P% ^) r/ }* u; L12$ @$ b  Z% H1 o! |! P3 Q
    13
    4 V8 a" O/ H* q4 `* u& l0 R) Z% m14
    8 Q8 D6 U/ i# p! K! z& ~15/ d7 n: M# ?+ ]$ a0 v
    16
    ' B* s5 D2 e( y+ C" O175 W7 Q4 ]" v/ p7 x: j3 w& [
    18
    4 ]! n, |0 @7 M3 T19
    ' k" @/ |+ s$ {9 r- O  `$ T- R$ C20
    : E5 I) G6 B  D. O$ i21% |2 ?" C0 V" Z2 X/ Q1 A
    9.3.2 一般区间的构造% d/ F: g- k" l& D1 G6 }' U
      pandas的单个区间用Interval表示,对于某一个具体的区间而言,其具备三个要素,即左端点、右端点和端点的开闭状态。+ v% J5 I; H& j' a3 U1 |

    - x& `7 W$ D# ]0 ?( |2 g/ X开闭状态:包含四种,即right(左开右闭), left(左闭右开), both(两边都闭), neither(两边都开)。, p1 G+ o# W( J! T! r
    my_interval = pd.Interval(0, 1, 'right')
    / f/ A; ^, M+ B! _
    1 u2 A4 h: j- G3 l; Vmy_interval4 u0 b- O- t( R8 E- e
    Out[50]: Interval(0, 1, closed='right')
    " R) O/ I, u6 \! l1 B1
    / W; |/ F* r. `8 m4 x8 k% ?( |2
    4 W1 V0 X7 a9 E+ D3% \. L  f4 o' m5 G9 M+ Y
    4/ e* `* A, Q$ O) m9 W- H% E9 D
    区间属性:包含left,mid,right,length,closed,,分别表示左中右端点、长度和开闭状态。6 l' i' O% T. V( |# P" l4 Z
    使用in可以判断元素是否属于区间
    + M5 V/ C0 T# _- v2 o- U用overlaps可以判断两个区间是否有交集:
    - F  B# U2 {5 t2 E* K0.5 in my_interval. U+ S  n( U* ~7 K0 P
    & }- N9 j% |+ n" ?8 O# W& D4 Y6 j2 q
    True
    . U$ k+ B% J, b- G/ h1
    , I: S- g8 s, ~& o20 X, H- q+ q$ J' M  D* g: r  a
    3, w8 o! q6 i$ _2 K& G
    my_interval_2 = pd.Interval(0.5, 1.5, 'left')
    + x0 S7 K9 Q: [' X6 |, Lmy_interval.overlaps(my_interval_2)( ]; l. t7 o, M! B1 h

    7 z; V9 ~% g7 q2 S( N2 ~6 t/ QTrue
    ' w+ a1 }) }/ r5 ?- v1
    0 Y% c: h9 k! U; S1 t2
    & y1 x& P1 V: c. e: T3
    $ R4 d) s, C& i# w; \46 F6 ~4 `  V0 Z6 v9 Z5 ?
      pd.IntervalIndex对象有四类方法生成,分别是from_breaks, from_arrays, from_tuples, interval_range,它们分别应用于不同的情况:
    / i- N( d) }( b- d! Y
    - L; W/ ?# ]+ `from_breaks:类似于cut或qcut函数,只不过后两个是通过计算得到的分割点,而前者是直接传入自定义的分割点:+ L2 `' b9 O8 D/ J% \9 a& m3 e
    pd.IntervalIndex.from_breaks([1,3,6,10], closed='both')
    % r9 u$ K9 G/ J- [1 A' k8 q: ]
    ) g/ L+ L$ m) W* N" N' ~IntervalIndex([[1, 3], [3, 6], [6, 10]],, [3 A3 `/ l1 b. }3 d% R+ m
                   closed='both',
    & f3 f; Q6 ^# J  \( Q* J, w               dtype='interval[int64]')) b. @7 Z2 n  p3 @
    1
    9 ?3 u/ N6 u: V3 ?  l- ?7 M8 G% u' p2
    ) X: z& R" ?* e8 ^& }3- c* F+ y8 s# F
    4. I1 T" O. j3 {0 B
    5( ^0 ?! y3 U! @6 K7 J# |
    from_arrays:分别传入左端点和右端点的列表,适用于有交集并且知道起点和终点的情况:! n6 X! E0 @  M: p; J" r
    pd.IntervalIndex.from_arrays(left = [1,3,6,10], right = [5,4,9,11], closed = 'neither')/ V  O& S3 v& m, u, O

    # h6 x2 K4 K; k: c3 z* v6 s3 ZIntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],
    3 Z7 C$ U! a9 L+ B$ L                  closed='neither',3 _7 \# v* a) ]5 K) ^6 d( w  x
                      dtype='interval[int64]')  h3 c6 c) d, P3 n# ]
    1( t, Q3 N, w2 Y+ A
    26 X6 d5 _5 N& \* T
    3
    ! Q9 M" e0 Q9 S0 ~. C3 [3 K$ x/ h3 e4
      n! |: T& S' q; O5
    7 S/ |7 |0 S. y( f9 d. dfrom_tuples:传入起点和终点元组构成的列表:3 }/ v* X0 g: K8 B8 y8 m
    pd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)], closed='neither')# j8 r: t9 _) R' |4 e; L: z1 ^) T" ]
    9 {1 S- L2 p: j3 ]7 i4 }
    IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],( b5 n8 O1 h% j
                  closed='neither',
    . ^5 a2 R0 L: t$ I6 |3 s              dtype='interval[int64]')3 f- f* y1 d% N: f. b- g! |
    18 N* b4 q' x' X9 @
    2
    ) `$ S6 P: O( p0 l3( ^+ s* c$ G; G- T* x
    4
    + }% \( [3 G3 I  R& [. s' o/ F5  g! N; R0 I1 o+ i4 ?( `) \
    interval_range:生成等差区间。其参数有四个:start, end, periods, freq。分别表示等差区间的起点、终点、区间个数和区间长度。其中三个量确定的情况下,剩下一个量就确定了,从而就能构造出相应的区间:
    * q! S2 M9 M; a* T+ p8 Jpd.interval_range(start=1,end=5,periods=8) # 启起点终点和区间个数
    & [, M$ b3 u) E3 }& oOut[57]: 2 E% N9 z& m! P) p: n
    IntervalIndex([(1.0, 1.5], (1.5, 2.0], (2.0, 2.5], (2.5, 3.0], (3.0, 3.5], (3.5, 4.0], (4.0, 4.5], (4.5, 5.0]],
    , H: S' b8 ?$ w" j              closed='right',; l5 |2 _* W6 M' A8 `8 }" `
                  dtype='interval[float64]')
    4 m* U2 |2 a% C( b1 |
    7 n3 ?' H3 P. D( Mpd.interval_range(end=5,periods=8,freq=0.5) # 启起点终点和区间长度4 \/ _* f+ c$ s* ^9 x
    Out[58]:
    7 r7 S. q/ {2 R2 {5 FIntervalIndex([(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]],* Q7 C5 [$ f& x; }3 z" f0 ~* w
                  closed='right',
    . S+ i' P3 Q2 z. ]              dtype='interval[float64]')- }4 [' Z, g4 g
    1
    0 b2 Q+ ]+ c/ O% b$ j4 S2
    + _. Z( ^% p  B4 y$ S9 A3
    1 D1 T; U1 c3 \2 \  q4 K4
    ( w0 \! ~, u9 V& v& D# \5
    " v' |  e. v6 K- W3 o# P, ~- t6
    4 k* ]! \0 e6 I0 `7
    1 w" y- x0 @7 Y4 Z9 b8  w+ d# R7 ?! r5 ^. x! D/ G8 L( s
    9- g9 U: q& y, _: }5 \
    10: V9 A7 T8 c/ ^- j$ S! {
    11
    # `- o4 e8 e$ J9 k, U【练一练】
    2 _* L2 C. ]6 x( L: ?  无论是interval_range还是下一章时间序列中的date_range都是给定了等差序列中四要素中的三个,从而确定整个序列。请回顾等差数列中的首项、末项、项数和公差的联系,写出interval_range中四个参数之间的恒等关系。+ h8 c4 s8 D7 f4 f! S: M

    7 J; |; r8 Y0 q! [  除此之外,如果直接使用pd.IntervalIndex([...], closed=...),把Interval类型的列表组成传入其中转为区间索引,那么所有的区间会被强制转为指定的closed类型,因为pd.IntervalIndex只允许存放同一种开闭区间的Interval对象。
    % {, t. K4 G' n$ H7 R9 u; R5 h! w! o6 F4 A* }4 o8 |: V
    my_interval( w4 \  @( w7 o* w! i+ Z
    Out[59]: Interval(0, 1, closed='right')
    0 z, B1 @$ m/ T- R7 |* m" ~
    3 V2 W- f: N! {/ `( S6 Vmy_interval_2
    ! a6 p( S2 K; s- cOut[60]: Interval(0.5, 1.5, closed='left'). J! E0 h. u- x( M! e! S7 W8 v" X
    ( |7 Q0 A/ e8 c0 y) L& _! Z
    pd.IntervalIndex([my_interval, my_interval_2], closed='left')3 W" o8 [" ]* Y) X
    Out[61]: + Q/ u# ?% ~3 I6 U+ T# }
    IntervalIndex([[0.0, 1.0), [0.5, 1.5)],
    , f  o3 ], D. ~: H; {$ H# n              closed='left',
    9 L2 I: j* U% g9 B( N, {              dtype='interval[float64]')% @+ s: c( z) m# n: l
    1$ f4 M, j: E: w
    20 n% Y0 Z! o# i, H* D/ t/ D
    3% w9 d' v* O- i5 {: p1 C  Y8 L
    4
    ! N7 h: q9 s* }. v$ I& n- A% D5
    1 B9 |' f$ ?3 Q0 B; A5 r6/ C6 p) n/ r; O/ r6 C9 T1 c' v7 U
    7
    % o' ?! g, n+ T  \8) Q, D! l( @5 }6 y& q2 W# L
    9
    $ }$ r$ F2 v/ J- w9 q10
    7 K/ ?8 i" q1 m" }: D' \/ o8 a116 ]# r. z) c( {) c' u
    9.3.3 区间的属性与方法
    / j- @5 g+ m1 s+ j' ~  IntervalIndex上也定义了一些有用的属性和方法。同时,如果想要具体利用cut或者qcut的结果进行分析,那么需要先将其转为该种索引类型:6 R: V2 ]# z# T4 j1 S9 k. U8 X
    4 q/ N7 Q% T! ~
    s=df.Weight* A) M0 V* i4 j% u' w: L
    id_interval = pd.IntervalIndex(pd.cut(s, 3)) # 返回的是每个元素所属区间,用具体数值(x,y]表示( m' a% K# q8 d
    id_interval[:3]' C% R1 J/ ~" b  H! S1 l1 F
    ; p4 ~2 J% O5 X; `& c# N. p
    IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0]],
    3 v+ L: K& O! s& i* i* }                 closed='right',
    $ g2 Z3 l/ s" v5 s. B; _' H+ m                 name='Weight',! C  _( L8 Q+ H3 M4 K! N$ K
                     dtype='interval[float64]')0 x# [! |) d7 Q1 }0 s; b) `! W: I
    1
    8 Y1 q, K( N4 @5 D: c4 h3 ~( p2- u9 @' n( S) H
    3
    ; i. R  p8 t9 e* Y$ g" a) [40 g( M: d0 M' B- l
    5) q4 `* C. n0 f- q* Y, m+ Q
    6
    1 s: ^0 B- L  m: C# F7' J. C$ x: W# t6 v
    8' d: W$ ~# b* J1 n  V1 z
    与单个Interval类型相似,IntervalIndex有若干常用属性:left, right, mid, length,分别表示左右端点、两 点均值和区间长度。$ k! q  p  a5 d. O5 R
    id_demo = id_interval[:5] # 选出前5个展示) y6 `( K& k' `$ [7 a) _% }
    # d8 ~$ j' Q' U! H, d% ~. s
    id_demo
      ?) H# r/ S' f3 IOut[64]:
    0 z% t- ~. {3 o0 hIntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0], (33.945, 52.333], (70.667, 89.0]],
    $ L- L( B8 ^/ M7 S. q- e2 K              closed='right',( R" P* K! S2 ^, T% f" E
                  name='Weight',% k' w! U/ v+ {, R
                  dtype='interval[float64]')
    ( s" l; G8 o5 j( J# l
    ! ^) ~1 @+ q" V& D) F4 M) M, i6 L" _id_demo.left # 获取这五个区间的左端点
    ) W2 ~. S; ~% Y. C6 XOut[65]: Float64Index([33.945, 52.333, 70.667, 33.945, 70.667], dtype='float64')3 M* A' z3 X) [' h. Z, l+ S

    % m+ L  s. C. G' s2 v1 o+ _, t2 Qid_demo.right # 获取这五个区间的右端点+ X8 T- @$ B) a8 F$ S& I/ n
    Out[66]: Float64Index([52.333, 70.667, 89.0, 52.333, 89.0], dtype='float64')
    9 `0 @8 s( R7 h; e& L- Q% s' T$ l3 |% A  w$ u1 b. E# l# l
    id_demo.mid
    ( ?8 ~( s" t. F, Q* w+ e9 wOut[67]: Float64Index([43.138999999999996, 61.5, 79.8335, 43.138999999999996, 79.8335], dtype='float64')9 ~; N6 ?: C0 A2 g- R: u
    + S; {' @4 {# J+ m  v
    id_demo.length
    % y( t( e: Y9 F9 FOut[68]:
    ' c7 v9 z4 ?; S$ SFloat64Index([18.387999999999998, 18.334000000000003, 18.333,
    . A6 w- Y$ j4 o              18.387999999999998, 18.333],
    ) O* F+ i7 h/ l3 z             dtype='float64')
    ' E! H) F. p& J! j9 {
    9 S) G1 I1 G- x* [1 C1 S1
    3 m* ?# O- m0 k. P. J2
    / w3 y; C$ A4 k3
    4 n; |3 Z7 n+ Q# h0 x: q4
    # j- z- Z% ~. o# _, q4 S5* `3 C+ k: C. s- a* m1 q0 e, ^# e
    6
      h4 V+ \7 w9 p/ F% w$ H7& e4 `1 t# B* o0 }) Y
    8/ |8 K, f7 g1 F1 H% k+ s. l
    9
    7 C* R& k% x. E10
    2 z8 g/ w* D1 k8 }1 s# Y3 g11" [+ X8 L- k* t6 \% f6 T: m5 H- T
    12( _. V$ }4 w. z/ J! p
    13
    # [, _2 c6 ?) l! z8 V- n+ z* ?141 f9 \2 b) ?# G7 J- ?# C+ [
    15& e- x' A% x& i8 |% }) p
    16: {( Q% C6 ]/ R/ m- w: Z
    17( o% z& N+ i' O( x) T3 f8 O
    18
    2 b; \% ?$ ]% [; e. p* g19/ K3 s& J& ?# ~* E) J) E
    206 l! m6 m- O6 G& O( f3 Q
    214 b- \7 \7 A7 w
    22
    1 m5 N" W; B  n: {0 `' A+ P23
    : P! s  W; h' K3 `5 CIntervalIndex还有两个常用方法:
    " U$ C5 B/ A* B+ a: a4 f' U+ Y" N: Y) ocontains:逐个判断每个区间是否包含某元素3 w# {1 y) @, A$ `( H
    overlaps:是否和一个pd.Interval对象有交集。! M6 N, \' N. W  |& A& j
    id_demo.contains(50)% i( @( X5 n" m7 n, G
    Out[69]: array([ True, False, False,  True, False])% N  R% N- q8 o' S* e8 R& |

    3 Y: f" P1 D3 A& y6 b, F3 R! mid_demo.overlaps(pd.Interval(40,60))7 l2 z2 E$ Q& e  x, ?3 P
    Out[70]: array([ True,  True, False,  True, False])
    ' l3 T  G, G9 @9 F% ?5 V  i1
    $ K2 C5 s6 g8 r- B% K8 M2
    * y* n1 L6 l8 Z; M0 Z& @& A) K; `3, Q8 {. J5 I; e' @5 w2 A. P$ C% v
    4) g6 r1 F+ a  K( g
    5
    $ V; Z+ Z( e9 s  c% t0 v9.4 练习  c0 E2 g, w4 B# d: X
    Ex1: 统计未出现的类别
    ! U$ N- `3 ?! Q  在第五章中介绍了crosstab函数,在默认参数下它能够对两个列的组合出现的频数进行统计汇总:
    ) E# z4 Y) y- Q* O1 ~
    ' U/ j0 w0 V% W: T# J0 e- rdf = pd.DataFrame({'A':['a','b','c','a'], 'B':['cat','cat','dog','cat']})
    5 I; J4 I  h( V4 j" dpd.crosstab(df.A, df.B)
    6 k# i1 ^. ^1 V7 P$ ~2 [5 t
    " t' x8 I; P- xOut[72]:   g' o) H/ G- S6 [
    B  cat  dog) f3 S1 p) t7 w  m  _; M' W3 {, M
    A         
    9 Z, F2 U1 D5 ?5 F" A3 Y9 i! Va    2    0, ^% D. i- ~, b7 k1 W; t5 Y
    b    1    0; z* Y" f$ t  o9 s. B' z) o! r
    c    0    1
    ; l2 F, k0 |. @% Y2 I6 [8 F+ S1
    3 A8 _' S; l2 A, \( A; L2; f+ @7 @% z2 g9 L, @5 E7 u8 y
    3
    2 ~6 z8 e" v+ [( ~! N8 {% R4
    5 Y, _; `7 I. Q: x, B' p+ L& E55 D" _2 j9 N$ d: p& u2 V! {
    6
    7 u& R. \" j( S7$ R, g9 ]( X) G9 {
    8
      h3 l7 F# W: M. U+ c$ i9: ]  G' z& z" \+ h' q
      但事实上有些列存储的是分类变量,列中并不一定包含所有的类别,此时如果想要对这些未出现的类别在crosstab结果中也进行汇总,则可以指定dropna参数为False:3 o5 k# c% ~2 t, W% R& q

    * A0 y) y% b, B# M5 ^$ n5 }df.B = df.B.astype('category').cat.add_categories('sheep'), X* _7 H$ Q+ [6 {
    pd.crosstab(df.A, df.B, dropna=False)6 H( k1 p. F# J# x# ]. l
    8 d- U1 j4 ?2 C) B2 ^9 ]% a
    Out[74]:
    0 Y9 w2 A  z& H$ G: @B  cat  dog  sheep+ r/ _6 e# z7 i0 Y8 q
    A                 % T9 f! G7 e4 c' j# T# H
    a    2    0      0
    / T7 _! D; {* H: Y: Mb    1    0      03 U" T9 N  ]4 n+ J4 T
    c    0    1      0
    9 {. O( E3 T+ z, X$ h) X1
    & Z7 Q1 n. }+ y( y21 P! }) J7 b. V4 D0 B
    3
      X9 A" k' ?# I0 U0 W6 c  x# W4
    & F) L# p% H5 @: c- |' M5
    ! q. c( F  y6 Y5 L6
    : F/ g3 M: _2 h1 u0 i6 @7
    ' }7 b6 v' }' A. s8
    3 p- {4 y' R4 _! H* c93 }1 |& L: s3 t# `6 G. g8 n  x
    请实现一个带有dropna参数的my_crosstab函数来完成上面的功能。
    ( A2 o$ u5 f) R  O4 O% v( j& ?6 K
    Ex2: 钻石数据集6 E! S, n* X) ?6 E) P( y
      现有一份关于钻石的数据集,其中carat, cut, clarity, price分别表示克拉重量、切割质量、纯净度和价格,样例如下:
    - O. y$ G& t# v1 S" i- @1 X; Y; V0 y% n8 l" b# l3 D
    df = pd.read_csv('../data/diamonds.csv')
    7 R5 g4 s) J5 Y- H6 R  Cdf.head(3)
    1 k( d: `) B1 {" I6 s4 w/ k9 t* D  n6 }
    Out[76]:
    8 e& n/ t0 w' t7 N9 Z3 t+ Y   carat      cut    clarity  price7 M0 D4 `5 ]5 ~' b. a$ b+ Z
    0   0.23     Ideal     SI2     326
    7 S2 C8 E( t5 a* N0 T1   0.21    Premium    SI1     326
    # n& B, N: M; c& g& b3 f- k( C6 Y2   0.23     Good      VS1     327
    ' @& |$ k$ _; I* H& q' a1 _) ~1
    6 P) K+ F, ]) F  d  C( K2
    ) o4 L0 c- P! \7 F5 ]32 F9 `0 s* \+ y
    4/ f" S/ w& q" z4 \
    5" ^# T8 o' l" _% f6 Q/ }6 C
    6
    7 h$ R$ _: S7 `/ F# l5 L7
    6 u' X7 P7 a5 f- s0 t8
    / R0 T1 y7 O. ?1 i分别对df.cut在object类型和category类型下使用nunique函数,并比较它们的性能。
    , v* [8 J: q2 e8 O6 [* g钻石的切割质量可以分为五个等级,由次到好分别是Fair, Good, Very Good, Premium, Ideal,纯净度有八个等级,由次到好分别是I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF,请对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。5 v* |1 V: F0 @3 e! ]: f6 ~! _% A
    分别采用两种不同的方法,把cut, clarity这两列按照由好到次的顺序,映射到从0到n-1的整数,其中n表示类别的个数。
      B, n% S+ {; [' i. q. g对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。
    ! R4 H3 N8 h( X6 q' C第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
    $ G- H% J4 M+ j对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。
    $ y$ h) v& ^6 E先看看数据结构:% s2 b; X" W( J% J0 }& ?0 L! c

    3 j9 `* F/ M* e* Y* Fdf.info()
    " I; |& B, w# Q" ~) R" I; Y+ JData columns (total 4 columns):$ ?0 _) F3 T$ b" X8 m
    #   Column   Non-Null Count  Dtype  ( v4 c! {! v6 t9 W; g6 r2 r# m& }
    ---  ------   --------------  -----  
    2 i4 H% }1 K, Q, ~: B$ S 0   carat    53940 non-null  float64
    * L% l  k& x  U0 m4 } 1   cut      53940 non-null  object
      p2 h  C1 f' j 2   clarity  53940 non-null  object
    7 k+ g: d" k% @) ?9 s0 \ 3   price    53940 non-null  int64  6 L" G; |( o# W
    dtypes: float64(1), int64(1), object(2)
    & V* D, e/ t8 G8 W* f; _1+ z" X' x! W) h$ W" ]( e) n0 ]
    2
    . \) c. |" s! Z' j8 r. B3
    - w: A) o# N' G2 |4! J* G6 _* k' k; @" d* t* i, L
    5
    . `7 F7 k3 I4 \: L2 v5 e3 n6
    # M9 G" t# g# h; o& R% n$ K& d79 k. M" i  s/ K
    8# [1 x; t( W) `: Z
    9" J; c, s7 }! p0 F$ \- S$ S4 u
    比较两种操作的性能
    : e7 I4 r$ N9 }$ C%time df.cut.unique()( x1 t# ~" R8 g, @# ^
    3 q1 a5 ~% O( o
    Wall time: 5.98 ms
    9 O* B) x3 Z2 C: D- ~! uarray(['Ideal', 'Premium', 'Good', 'Very Good', 'Fair'], dtype=object)  G; t, {4 u+ |: Y
    1/ I5 K/ c, t3 X1 p
    2( i+ R' F$ }# p  L  S$ t
    3
    & k% s3 M8 [9 M. C# T. L1 S4) e1 o# R8 k3 V
    %time df.cut.astype('category').unique()2 A/ K; _) M' f7 R" p7 B

    0 [& @8 W' V' @/ ]2 O- CWall time: 8.01 ms  # 转换类型加统计类别,一共8ms
    8 J- n9 r1 Y( o$ N' K! a5 T7 r4 {5 w['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    - Z; H; \; Y; ECategories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    ) R" a+ u- u3 q) Y% Y( H8 j& v1
    5 l- T6 y2 l. v+ z- c/ j/ M26 i7 h4 }# M% b* o2 G
    36 M- C" k; K2 n' ^& k+ y. O
    4
    2 I9 ?7 W% c( \- O& }5) x  ~5 w9 u" \7 t9 e
    df.cut=df.cut.astype('category')" U1 R5 N( Z( n( {
    %time df.cut.unique() # 类别属性统计,2ms5 H- j( U7 @1 c( {3 U5 c2 [( \3 ~
    : [+ q* w& r2 j4 q3 q
    Wall time: 2 ms  X3 |. `& ]+ m  h' ]6 x" W
    ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']
    ) k5 R+ {9 S! ^" N$ z/ `: eCategories (5, object): ['Ideal', 'Premium', 'Good', 'Very Good', 'Fair']# B3 ]4 {8 N( O' t8 _
    1
    $ }/ L6 A7 d' r$ K7 `25 P* k; v  [7 S+ d( U( c! p
    3
    " y( Y" ^& g+ J0 L4
    ( @6 t6 Y# k- F) w3 l54 N# L) d  f. `. C7 a! t: Q
    6
    5 N" M! L9 @- n7 }1 E, ]对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。
    : a( T( V) U0 |; m0 r/ Mls_cut=['Fair', 'Good', 'Very Good', 'Premium', 'Ideal']2 J) S1 e- _# e2 \9 I! n- p
    ls_clarity=['I1','SI2', 'SI1', 'VS2', 'VS1', 'VVS2', 'VVS1', 'IF']
    ! e  R6 V" m% T/ J: N( Z+ Zdf.cut=df.cut.astype('category').cat.reorder_categories(ls_cut,ordered=True)  # 转换后还是得进行替换
    0 y. F6 Y5 a2 i) }' Odf.clarity=df.clarity.astype('category').cat.reorder_categories(ls_clarity,ordered=True)
    6 u2 A4 R: J& U5 K
    $ `) B9 g! f: |# g4 qdf.sort_values(['cut','clarity'],ascending=[False,True]).head(3)
    ( n7 i; C2 d5 N) M( ?, N2 O8 v7 @* {
            carat         cut        clarity        price( F, J( {6 [# u& c. O; K
    315        0.96        Ideal          I1        2801' B: s# D7 a" n' G
    535        0.96        Ideal          I1        2826
    ( q- ?' G0 l7 F6 L* [551        0.97        Ideal          I1        28305 S6 z0 u0 x' v. D; N( t
    1  m/ {2 @: H; v. f5 H
    2% F% f# F- R' O" g8 p
    39 y& R( s* f5 e  k2 j3 X  D; j
    4
    2 ]) M1 t. \9 g2 W) r/ d+ n5. v5 q( `7 T& D
    6, ~1 h$ n) }$ ]
    77 {/ r5 t# B9 I6 T4 d' r6 g
    8  _3 @) ^0 ]: d! B( \; g' X
    9- I! m; U/ w& `: A+ @5 @0 }
    10+ T( P3 \. N  @$ w
    118 Y8 m) Q( q9 T. A/ Q
    分别采用两种不同的方法,把 cut, clarity 这两列按照 由好到次 的顺序,映射到从0到n-1的整数,其中n表示类别的个数。7 l; I- \- M+ `
    # 第一种是将类别重命名为整数
    8 K) h4 K, D/ r* r9 W, ydict1=dict(zip(ls_cut,[x for x in range (4,-1,-1)]))
      O' I. B% m/ w  L3 r/ d0 Pdict2=dict(zip(ls_clarity,[x for x in range (7,-1,-1)]))
    # p& u6 A" @2 f) h& h$ s2 t5 g( L* s& ?8 m* C- c
    df.cut=df.cut.cat.rename_categories(dict1)! {1 n+ T6 W, M+ p; z
    df.clarity=df.clarity.cat.rename_categories(dict2)7 S6 i4 o! Q2 o1 `& A
    df.head(3)
    ' D( [) ^( A8 m  d$ Q" Q; H6 G, n) g- W1 Y' A
            carat        cut        clarity        price
    ; J4 U! z: i4 R  D& ?- d0        0.23        0          6                326  E% l/ P+ q1 h. k
    1        0.21        1          5                326# d& q& j  I$ \+ ~- ~
    2        0.23        3          3                327: c: M& V" a$ o4 ^6 e
    1. d# u. I7 O) |' x; Z
    2" _+ v; I8 b9 P' L% f$ a2 U' l7 j. v
    3: ?+ s% N' m2 t4 _
    42 P" e) b" S0 n
    5
    : x! X5 C: `/ u$ C2 p6; u0 N. u( d$ l" `
    74 m' d" s$ {4 y
    8. H; {9 I8 Y* r- V. p6 c- U2 b
    9
    8 A/ ]& m+ M) q; Y$ i' l  }; {10. W! L! v' A& B# Q" d3 m
    11
    4 q9 C* n6 v9 h6 s12
    3 ^# _6 }* B" i* S' d# 第二种应该是报错object属性,然后直接进行替换
    0 y( z8 E: ~0 R) G* G( [6 V$ P4 cdf = pd.read_csv('data/diamonds.csv')- E9 N  W0 \7 G1 f9 V3 q# s- f
    for i,j in enumerate(ls_cut[::-1]):1 D4 u5 A8 B& Q4 t4 ^
        df.loc[df.cut==j,'cut']=i
    ( F5 t) ^( ]/ O+ J& |0 G  G
    ! O  x8 @& z! q4 u* z  n2 o+ @8 jfor k,l in enumerate(ls_clarity[::-1]):8 I5 Q, Y/ ]8 E+ o5 G  y
        df.loc[df.clarity==l,'clarity']=k
    # y  I4 K7 Y! tdf.head(3)5 I% _" i$ E, C; f
    4 h' K1 o& f0 T( z& L2 ~
            carat        cut        clarity        price
    - m% ~, N6 B3 r6 `6 Z/ x3 m/ j0        0.23        0          6                3261 E' X8 `* {4 S0 Q3 k) P9 \
    1        0.21        1          5                3265 ?: T  R2 m# Y0 l" G' S8 e
    2        0.23        3          3                327! x5 y* E6 {3 u7 H& [
    1" H- c7 `* C6 D- c3 G2 u& o
    2, c  s/ z, H1 t# l' Z9 w- {( B" S
    3
    # L/ Q7 S/ H/ q4 [. c4 P4  y3 X3 L+ E5 {, q) N' ~
    5
    " [9 a* G( c3 Z2 z' z% P  @6' j7 P- W& m6 P# E4 @+ _
    73 z* e$ P* _8 Q% R3 C6 h  n7 I
    83 d# `7 V$ k  a9 E
    9
    2 Y" s% j. F% u10
    " N& b! ^# w9 b$ N11
    1 y. g& k" z: |4 V" G8 C4 {; `12! ^7 L! k) p0 h) ~0 f
    137 [. a# f4 ~/ ^- @; _
    对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。  W5 X1 G3 W. J2 L1 g+ k
    # retbins=True返回的是元组,第一个才是要的序列,第二个元素是分割点+ F; |8 G  ?4 T# C' ^# i9 m
    avg=df.price/df.carat
    # `  ?" Y  M4 b0 P8 ?. H
    1 }* a1 f7 [" n6 I& \# H% f0 Fdf['price_quantile']=pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],4 d. K9 i# s' B, n/ i  @
                                  labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]7 {9 V* s) N5 ~
    : j9 U& A* p# }( h% U+ w
    df['price_list']=pd.cut(avg, bins=[-np.infty,1000, 3500, 5500, 18000,np.infty],$ i+ l! |- k3 N- `( b) @( R7 i
                                  labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'],retbins=True)[0]7 k' j- Q3 ?! o, [
    df.head()6 Y9 h" f' H" n- g1 N# E( ~$ N' l

    ) j( j3 q3 C: k0 g        carat        cut         clarity        price        price_quantile        price_list
    % j& r* x! D, @8 o0        0.23        0                6                326                        Very Low                Low9 c, l! j0 h) _
    1        0.21        1                5                326                        Very Low                Low8 c& |: ]/ s( c9 |
    2        0.23        3                3                327                        Very Low                Low
    - w5 r3 Y# H0 r; t2 g4 X; ^* g3        0.29        1                4                334                        Very Low                Low
      _; [0 A8 Z' l4 P7 d$ j# E4 y0 D4        0.31        3                6                335                        Very Low                Low                                       
    / z( i/ I9 [  W6 W+ I# B1 m3 J6 q4 {
    1
    ; J/ J5 t; \  J4 ~6 z5 T  ~2
    % d4 X9 A- W5 n% @! r9 a3
    % T. M! f1 I" D' Q+ q4
      ?" i9 f1 S3 d, K  c9 S! q58 Z8 Q1 s8 W6 b: t! Q' ~; c6 {- \
    6
    ' x: k) j1 J/ s: e73 d  L% O% C* k. ^( ?; m1 [( R' F- M( d
    8& x, r  j' S$ K
    9
      S, O: E" Y7 M/ U' M10# e/ S% Z" m, W* J. H
    11
    ; f, c" c$ i! C2 e& I- S12
    6 u( a. ]+ B* ^, A13
    6 J4 y& v4 }5 t6 U% s9 t3 B147 t( d: ~7 \* j3 z
    15- d6 o2 K9 b& h. a, E/ V
    16
    - M6 X1 g3 e2 C% }3 A" J分割点分别是:
    " _$ a3 ^7 o& b. G# S' O0 o3 r0 a4 A5 g  |4 s
    array([ 1051.16 , 2295. ,  3073.29,  4031.68, 5456.34, 17828.84])7 G1 J" K5 T" K9 x
    array([  -inf,   1000.,    3500.,    5500.,   18000.,    inf])
    # N' D3 ^, o5 W1# [2 b& M: R+ Q# k* H
    2, m" \( t$ ]+ l% o% O$ ~- K9 j6 g
    第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。3 e6 h& N# K) i2 p
    df['price_list'].cat.categories # 原先设定的类别数
    # S5 C/ V4 y& ^% n! F& CIndex(['Very Low', 'Low', 'Mid', 'High', 'Very High'], dtype='object')
    / D5 k$ W8 W+ E# k9 I6 }0 d' m3 ]6 M$ O. ~% N: r" P3 l0 Z
    df['price_list'].cat.remove_unused_categories().cat.categories  # 移除未出现的类别
    6 s" m9 p, z1 W6 d; \" m" ]Index(['Low', 'Mid', 'High'], dtype='object')  # 首尾两个类别未出现
    + h2 F3 b8 _! W& Q1 g1
    & H2 m6 H0 c8 y& {, }- _2
    3 N% }' }- W* m0 m- w$ u$ b  o9 w3" V* U3 \+ l/ Q* y
    4
    7 S) H& h$ m2 i5
    $ G3 Y" r) j5 V4 ravg.sort_values() # 可见首尾区间确实是没有的
    : j9 |; c: |7 s. Z/ n/ @9 s; S31962     1051.162791! m3 C/ R% V% n1 }$ L. c
    15        1078.125000
    % x" _- v2 ]6 t" _4         1080.645161
    8 [0 X7 i4 V8 D' m/ ]# _% e28285     1109.090909
    $ P9 w! |3 P6 G. ^5 q9 n; y, ?# x6 j13        1109.677419& n  u" G$ J7 f& F) U1 {6 l
                 ...     
    - Z' V+ e1 }/ Z0 }26998    16764.705882
    ' U! T! t; d; Z* }# l( H( t5 D27457    16928.971963: Z# r& i6 z) a) ~5 F
    27226    17077.6699039 ~* t1 B& y6 z2 z7 H. ~3 B
    27530    17083.177570
    2 a0 V5 a+ O: C0 ]1 |/ x$ H27635    17828.846154
    1 H1 g; |* {) `3 T1  g# r2 g; E! Y# z4 ^
    2# X- o2 O* |9 h5 L* S0 b
    3
    2 Y: D7 `1 M0 Y8 t4
    , o$ [. ]& J9 v4 b+ N8 X. u% i5
    1 _* M  f1 |3 Y4 U0 y" ^6# }- h$ x7 q+ B4 [& [) S
    75 n) d7 z  `3 V; g, }( q$ n
    8
    ( `+ Y, z6 G0 z4 M/ i( c, F* R9
    ! ?4 b9 j8 z6 W10
    8 I7 W& S. f5 x- X11
    " U/ ?9 k% ~( u( }3 x7 v; ?12
    2 m3 j: H, Q' h3 m) p对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。/ U# v7 b8 m3 u; ~
    # 分割时区间不能有命名,否则字符串传入错误。
    3 D+ U! B% f, L6 W: i& Qid_interval=pd.IntervalIndex(
    $ h. p" ^* f# ]7 d8 b' H4 Q    pd.qcut(avg, q=[0,0.2,0.4,0.6,0.8,1],retbins=True)[0]1 [$ [$ W  C; ^+ z, e1 f7 d6 }
                                )  r, }  G1 F" L3 Z1 g* L) u
    id_interval.left- U! w2 x/ E& f- n- s; n
    id_interval.right
    % o3 ~  M+ O4 J4 t- V# A+ _: ~id_interval.length                            3 k5 `/ F2 G8 Y# m4 C1 N
    1
    3 j/ j3 @+ u% K1 I  p2% W: W6 i4 j/ w& T) ?+ Z7 ~
    36 q: Z- H/ K: G8 G- ~% f% w: Q  |
    4$ ~5 T0 f% {  U7 W* r7 c1 ?
    55 M' o5 U. o' F% A( |$ g1 R4 q# A
    6
    6 |  R5 R& [  K8 V7
    $ a) t& E' @* S, r+ g& @第十章 时序数据  ]9 S) W  s& z8 x
    import numpy as np
    6 n5 n6 l% K. Rimport pandas as pd
    0 o) Q" d" u5 j3 G# K' b14 x* @/ g1 n5 F
    2
      t# Q7 @% R( w9 J* ?  q/ Q4 x3 q9 x- w7 u' E2 \" u  E

    , q! A- v3 V4 t10.1 时序中的基本对象
    * `; W3 P# |1 D: u+ E( d  时间序列的概念在日常生活中十分常见,但对于一个具体的时序事件而言,可以从多个时间对象的角度来描述。例如2020年9月7日周一早上8点整需要到教室上课,这个课会在当天早上10点结束,其中包含了哪些时间概念?. |5 {1 c* R6 A/ h- x1 u, P, r

    6 Y. R, w( r) E5 ^会出现时间戳(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的简写。  @1 ~, e( Z9 `# a! \
    & A% u! Y% n+ [* m+ {( @# o2 D' M' `
    会出现时间差(Time deltas)的概念,即上课需要的时间,两个Timestamp做差就得到了时间差,pandas中利用Timedelta来表示。类似的,一系列的时间差就组成了TimedeltaIndex, 而将它放到Series中后,Series的类型就变为了timedelta64[ns]。
    ; E) V. R1 q  b2 V; ~4 a
    ' R' i- N9 R4 x* j! O* G3 ^会出现时间段(Time spans)的概念,即在8点到10点这个区间都会持续地在上课,在pandas利用Period来表示。类似的,一系列的时间段就组成了PeriodIndex, 而将它放到Series中后,Series的类型就变为了Period。. Z) }* x; \) \, k3 L) r" a
    ! G  |; p5 d/ W& Q+ G0 u( a" {
    会出现日期偏置(Date offsets)的概念,假设你只知道9月的第一个周一早上8点要去上课,但不知道具体的日期,那么就需要一个类型来处理此类需求。再例如,想要知道2020年9月7日后的第30个工作日是哪一天,那么时间差就解决不了你的问题,从而pandas中的DateOffset就出现了。同时,pandas中没有为一列时间偏置专门设计存储类型,理由也很简单,因为需求比较奇怪,一般来说我们只需要对一批时间特征做一个统一的特殊日期偏置。
      a3 b, w( s8 _! x+ g5 D0 S
    # S9 w5 u2 ~2 o' S- B  通过这个简单的例子,就能够容易地总结出官方文档中的这个表格:% N; }; g) X; w

    & J0 O% e: b8 N# \- O/ S概念        单元素类型        数组类型        pandas数据类型
    4 H1 Q* L" V6 U, B8 H. l/ L. dDate times        Timestamp        DatetimeIndex        datetime64[ns]- J# M* M- v' ?/ E/ \) Q
    Time deltas        Timedelta        TimedeltaIndex        timedelta64[ns]4 U! |1 R* b1 O6 v5 w4 ?
    Time spans        Period        PeriodIndex        period[freq]* R' x5 b' R2 k" k+ S
    Date offsets        DateOffset        None        None( ?' n$ m% d  c) m5 ~
      由于时间段对象Period/PeriodIndex的使用频率并不高,因此将不进行讲解,而只涉及时间戳序列、时间差序列和日期偏置的相关内容。
    5 w4 v- l4 m. c9 \/ Q2 p; U3 t) s  K
    10.2 时间戳
    % R2 z  Q6 T1 H3 l: S1 k, m10.2.1 Timestamp的构造与属性
    , n% q/ V. W" h+ @* l$ ]; t% @单个时间戳的生成利用pd.Timestamp实现,一般而言的常见日期格式都能被成功地转换:1 D$ Q3 ^* E* H

      O! X" q5 S  |  o( gts = pd.Timestamp('2020/1/1')
    2 G: c! m, r- i/ T. V* |9 v: ~0 l) G1 R4 z% U2 i
    ts6 o/ M6 ?  j6 K$ q
    Out[4]: Timestamp('2020-01-01 00:00:00')* R7 R- G0 ]# R( B2 o; O1 i* G

    9 e2 p7 t% ^/ H* D% E  Dts = pd.Timestamp('2020-1-1 08:10:30')
    . r# o9 L5 o9 Q% x9 p" J/ }  z# A
    : M  m! i$ d! Q! }; j8 X- vts: w6 c7 @" c( E& O% A* O7 x
    Out[6]: Timestamp('2020-01-01 08:10:30')
    : P. A3 Q' Y/ m3 K1
    5 n. a! u& @9 \3 u4 `5 s2& H- {# }# Z7 [4 t6 `. l6 R  W
    3: t: O7 C6 l: E8 t) C0 Z  O# U
    4
    : V+ i* m$ M+ M' m+ U9 u* d! _5' O$ w) R- {; F; p: X/ L) j, @- V
    6
    ' t7 x! v# S0 ?9 i7 p( N7  n0 k5 i% n( E! y  g
    87 |1 e$ L9 u; V
    9( x; x& Z5 X. F6 S9 o5 r, \
    通过year, month, day, hour, min, second可以获取具体的数值:2 \6 U. w! r1 i. j' B, f) m5 y
    1 c) o% d! W+ e/ B" j6 s; b6 }6 c
    ts.year8 k6 V  p; M5 A  c
    Out[7]: 2020
    7 p% {3 [1 H2 |4 M: T/ F7 a. ~7 E, L8 ~7 [% q# t& v/ A
    ts.month: f! p; s1 R; `% j/ `* @1 w2 D
    Out[8]: 1
    6 |" e' i8 X+ b3 q- |' v7 f; y0 d' ?2 m& g6 d
    ts.day, R: L$ s6 D% }5 B% L3 M8 y/ B
    Out[9]: 1
    4 |- D; w& m9 a* O1 q+ a, G. a9 \+ U1 ?5 ~4 o& J0 w2 |
    ts.hour1 j5 F9 w6 E  B: k4 k
    Out[10]: 8
    9 i1 S  |+ a: a; f
    6 s' Y' K) ^9 Q1 S2 W  d' Sts.minute& p) u* z+ Z8 w9 I
    Out[11]: 10" o# @5 y7 }# h' U5 ]5 a$ w. ?( y  q

    + C( k: r, A8 \: qts.second: p6 V( E9 n5 S/ k) p
    Out[12]: 300 k/ h/ U8 t0 G/ A
    ( G# H5 F, g9 V9 A1 k0 N: h$ G
    1# }4 c+ N. E7 Q9 ]4 i: w: C0 h
    2
    9 ?- [2 H, q9 j5 F3% u, z1 c+ N7 C% H4 v$ M( `0 ]
    4/ [, F- h5 D# w0 {+ f" Y+ O- H8 [
    5
    ; {- K, q! C" \+ A3 C/ i3 T6
    1 r% Y) W9 x! e( F. H& b7. t  B5 {2 i9 x# I" y7 L) o
    8
    # S8 G0 \9 Y" p* v# b2 E& E9
    : V2 p$ T, U" W) ~  A10
    , C% c, T- d2 B  p" b* M11
    ' X1 m1 |1 L) n& x4 ~12
    - V1 j0 R/ |! P" l13
    % E6 X' @5 s% t/ N14
    / Y) c+ G' |+ C+ q* y15' O2 T3 `& y4 J) g7 v
    16
    : k5 k% ]7 Q. @, T2 c" H17
    8 |3 ^: W! r' |5 ~  [# 获取当前时间
    . n) a2 b5 i+ t9 `+ t8 ?now=pd.Timestamp.now()
    8 k4 e" N. w1 Y6 Z7 m, V! }9 c( L% ~1/ ?9 x, a. ~( u( s: [$ X1 j- _
    2
    % m7 J8 Y, X6 a) J" {在pandas中,时间戳的最小精度为纳秒ns,由于使用了64位存储,可以表示的时间范围大约可以如下计算:$ n& J- d. J+ f, ?- _$ Z
    T i m e   R a n g e = 2 64 1 0 9 × 60 × 60 × 24 × 365 ≈ 585 ( Y e a r s ) \rm Time\,Range = \frac{2^{64}}{10^9\times 60\times 60\times 24\times 365} \approx 585 (Years)
    0 @$ l# N9 `" U9 _3 cTimeRange=
    7 v- c/ ]4 m2 |: k0 O, J( A7 m10 / t- ^6 l% d  s) M$ w/ i3 Z
    9
    ! R: b! }7 t" _) W ×60×60×24×365, d- k+ \& X$ ]7 v
    2 7 n- u& H% v. S' |" b0 a
    64
    $ D. `$ v% E* }+ J2 }9 A$ A% _, P2 x2 ~& G" V9 Q' F

    1 E) }0 a  t, C! T  P* W1 [ ≈585(Years)
    ( M: M0 V5 ^; W
    & J) p5 X4 \- ~; d( t7 y8 l& J通过pd.Timestamp.max和pd.Timestamp.min可以获取时间戳表示的范围,可以看到确实表示的区间年数大小正如上述计算结果:
    + L0 u# S7 }* N
    9 p9 j* _$ [( N0 t4 j: gpd.Timestamp.max
    % D/ D8 v: n* \) G/ y* b* y) N. xOut[13]: Timestamp('2262-04-11 23:47:16.854775807')
      x$ l6 |2 r9 p+ Y2 M4 t
    . G' H! O6 \1 w( \! lpd.Timestamp.min
    ' h; t. m# e: }: N6 ~Out[14]: Timestamp('1677-09-21 00:12:43.145225')
    / e+ i, O. f8 |/ K0 G1 F$ @% v, [
    pd.Timestamp.max.year - pd.Timestamp.min.year: s8 D) w! B% @. I! x1 p, C
    Out[15]: 585! S4 g3 D* ^; b  V0 B8 c4 c; j0 d
    1
    ' `/ O4 L( k, r1 M$ y2
    9 L: \4 X& k4 t& N: s( j3 ]3' C; H2 E2 Q4 N% G7 N% a) Q
    4
      Q, N# d: S7 J5 z5
    / i3 Y) h- L) R( f6
    ( ~/ q3 `8 S3 D1 S1 Z' O7
    ' x1 p; A: Q3 k+ [2 S% B85 ?5 L$ C+ o' n7 k
    10.2.2 Datetime序列的生成
    ! D- ?" i7 K* B/ Epandas.to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, utc=None, format=None,& W6 ]" L" \0 ~
                                      exact=True, unit=None, infer_datetime_format=False, origin='unix', cache=True)+ u. }( L$ Y" p3 l. v
    1+ o( B9 Y& O: {& f' K
    2  M. h9 V* x+ d" F
    pandas.to_datetime将arg转换为日期时间。
    - j9 b' \% }; W% ]) {. _. E: A4 t# Y8 u/ @5 v) l
    arg:可以是argint、float、str、datetime、list、tuple、一维数组、Series、DataFrame/dict-like等要转换为日期时间的对象。如果提供了 DataFrame,则该方法至少需要以下列:“年”、“月”、“日”。5 s# o$ t4 t& C- |
    errors:
    : L8 K" [* E7 w1 e7 O- ‘raise’:默认值,无效解析将引发异常8 p! L# ~( a9 [0 a: Z- l1 z. q
    - ‘raise’:无效解析将返回输入2 x- r& y, g  U; p
    - ‘coerce’:无效解析将被设置为NaT) e1 ^- I- u5 V6 c5 o
    dayfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析日期,例如“10/11/12”被解析为 2012-11-10。如果无法根据给定的 dayfirst 选项解析分隔日期字符串,会显示警告。
    + U, v7 w0 M+ v, j5 n) pyearfirst:如果 arg 是 str 或类似列表时使用,表示日期解析顺序,默认为 False。如果为 True,则首先解析年份,例如“10/11/12”被解析为2010-11-12。无法正确解析时会显示警告。(如果 dayfirst 和 yearfirst 都为 True,则 yearfirst 优先(与 dateutil 相同)。)! L8 u8 B, T, a* H: h
    utcbool:默认None,控制时区相关的解析、本地化和转换。请参阅:pandas 有关时区转换和本地化的一般文档3 S$ W# O  p5 R. x5 ^# b6 q
    format:str格式,默认None。时间戳的格式不满足转换时,可以强制使用format进行匹配。
    % n$ [# V" J. j3 p+ p( i( l! T$ R7 Junitstr:默认“ns”。它是arg (D,s,ms,us,ns) 的表示单位,可以是整数或浮点数。这将基于原点。例如,使用 unit=‘ms’ 和 origin=‘unix’ (默认值),这将计算到 unix 开始的毫秒数。7 }) c$ y, j, D2 k1 y, v# Q
    to_datetime能够把一列时间戳格式的对象转换成为datetime64[ns]类型的时间序列:
    * G& m0 q0 O" c( O) G  v. ^  l2 ~pd.to_datetime(['2020-1-1', '2020-1-3', '2020-1-6'])& A: J% T' Y7 S7 L  ~
    9 n8 a. X, ^- c! d* r7 C! q- F6 E9 Z
    DatetimeIndex(['2020-01-01', '2020-01-03', '2020-01-06'], dtype='datetime64[ns]', freq=None)$ C4 A& Q7 L; C; c2 ]4 @6 ]* ~
    1- [9 V8 Y9 p( {2 `: m; C
    2
    ( I4 h8 k0 s2 ~; c0 m( X* c! ~$ K6 @, ?38 n: d3 [% x6 E" v2 \
    在极少数情况,时间戳的格式不满足转换时,可以强制使用format进行匹配:' f* v. X; k- a) b) z  X: \# m

      J# }0 [& _8 h$ k: G+ n' j' Atemp = pd.to_datetime(['2020\\1\\1','2020\\1\\3'],format='%Y\\%m\\%d')7 A) r0 Y* Y6 e+ ~3 g; M" A
    temp3 j$ j. n6 q/ d6 \# t

    ( ]0 R/ `; ]* \0 @% A! eDatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)
    + q' W9 z! j8 |7 P8 F# D1 s. Q/ p0 \1
    ( {3 p1 F' f$ u3 z& l" A( y" f% o2& B1 ~* q% b, c# Y0 i
    3
    : m! U! \' H1 ^" t. `  ?0 f4
    ) ?, Y4 D) T- Z; N" `8 |' O* O  注意上面由于传入的是列表,而非pandas内部的Series,因此返回的是DatetimeIndex,如果想要转为datetime64[ns]的序列,需要显式用Series转化:1 X' ?+ k' [; q+ a) G

    0 R- F3 N6 ~& q) c" d  U2 S: ~pd.Series(temp).head()1 [. \" k* b/ I, t- V

    ! |( G# M3 {2 {( i0   2020-01-012 N3 q. X* g$ i1 v1 }. `
    1   2020-01-03
    0 `! S& H: i" z( zdtype: datetime64[ns]' M; y! w' a8 K! T" _
    1
    4 v3 ]9 @3 d  I0 r) y  D' E8 _0 |- @26 ^' X6 H) n' z1 q
    3
    ! ~" P9 `% W! _3 ^4
    8 Q2 o: N5 F, ^+ z, N5" I6 B4 ?2 A" U0 V4 g  v. |
    下面的序列本身就是Series,所以不需要再转化。7 o+ t  V7 O) W* [$ t) X

    $ |0 O2 u) |" d* f* T" Sdf = pd.read_csv('../data/learn_pandas.csv')
    6 C: V. D: `  o0 S7 g0 Ks = pd.to_datetime(df.Test_Date), ?5 K. l1 F6 Z) g
    s.head()& ?# h+ s& U% ?4 K9 k/ h3 U# W
    9 z  b. {" @0 E0 a, K- i
    0   2019-10-05
    0 b3 z( z! w* U1   2019-09-04
    ) _% V$ T; e3 l9 g( O  b( T2   2019-09-12
    ( Z9 O4 B; _. i/ N1 d0 w% q1 u3   2020-01-032 T1 u/ B$ L# V. q  `& e
    4   2019-11-06- r& ?2 Y; q  D5 u8 j9 r
    Name: Test_Date, dtype: datetime64[ns]5 J9 Z' k; o- P7 L
    1
    ; V  w8 [3 A% B4 ]- g5 ?( a2
    - Y. f2 u: r7 u- V7 B- g3
    3 g) G# D5 d& T6 h( G( T6 X) f4
    / I) m! @. f. C. X3 A9 x' h' a3 ^56 O) u. q; j; e8 K
    6/ c+ `3 J6 C7 [* R$ {3 L4 f# n, i7 Z
    7
    0 u$ K+ Z& C' {3 H8 C8
    ) n& G& D4 O! Q* f5 b# o0 J. \9
    % D3 z6 S4 ?; D  f' {5 C3 u10+ D3 P' k/ r3 i' ]& _, I! i! d
    把表的多列时间属性拼接转为时间序列的to_datetime,此时的列名必须和以下给定的时间关键词列名一致:: q" s! G6 _# l; V8 k8 K6 J0 o
    df_date_cols = pd.DataFrame({'year': [2020, 2020],0 u1 g. `! k; Y  p4 ^) F' |+ A( w
                                 'month': [1, 1],
    1 }# T9 x0 s) Q. w" Y# p: G- l; C                             'day': [1, 2],
    " L: J! a( t2 `                             'hour': [10, 20],
    4 `& O; ^. n" l                             'minute': [30, 50],
    2 |1 }" {& T6 F/ o; d8 s' J% @                             'second': [20, 40]})! c, g' i% P6 f% |7 s" z+ s* Q
    pd.to_datetime(df_date_cols)2 q3 v: ~7 H  S3 S) O
      p6 @( ]  |2 u, ^2 V7 k# Q
    0   2020-01-01 10:30:20
    ; C" z5 M1 v/ P" R+ g3 B0 V, ?" E1   2020-01-02 20:50:40
    # ^6 M% M+ v) F) T. tdtype: datetime64[ns]
    6 F! w" G- @. C# c1" T' w9 Q# \& E0 d8 N! ?- ]
    2
    ; k1 n! g- G2 H4 C. {' W3) r) a/ F& U' Q& T! L
    45 B$ Q) \2 w% C4 \7 s2 n2 D" V) ~
    5
    4 V6 p% s- ?1 D5 M. J( h* g67 n- q. ~2 ]) \# {+ s7 A( ]- z
    7; y$ Y3 b5 ?7 t! L) ]
    88 M: w- d( l3 \  n6 O
    98 R3 ^" P) `+ o* n& ~" z* e
    10
    / I1 ^; L# j0 v' ~11+ D6 f* b2 W: l8 ]; w, t4 J- ?
    date_range是一种生成连续间隔时间的一种方法,其重要的参数为start, end, freq, periods,它们分别表示开始时间,结束时间,时间间隔,时间戳个数。其中,四个中的三个参数决定了,那么剩下的一个就随之确定了。这里要注意,开始或结束日期如果作为端点则它会被包含:
    8 t" U+ v* O! \7 E& J* }4 ?pd.date_range('2020-1-1','2020-1-21', freq='10D') # 包含9 Q( }$ A, |# \& S; N/ p7 Y- F
    Out[25]: DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21'], dtype='datetime64[ns]', freq='10D')
    ( I0 {# B7 O$ G: I: n; [- X6 h6 ^
    pd.date_range('2020-1-1','2020-2-28', freq='10D')1 F' T2 n4 ?3 C7 r; {) }! s
    Out[26]: , M, U4 z- J. Z+ i  B1 @
    DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21', '2020-01-31',
    % o: J, l3 D1 N+ `/ ?8 H% W               '2020-02-10', '2020-02-20'],
    ! k& o9 U7 z' y. L' S              dtype='datetime64[ns]', freq='10D')
    5 f( G/ P: V& C7 t% A5 f* l+ Y5 f- v- A1 e# ^/ u0 p
    pd.date_range('2020-1-1',
    9 d% I/ b& i! k8 Y              '2020-2-28', periods=6) # 由于结束日期无法取到,freq不为10天
    9 T! Z. d0 `2 F9 s# A0 T( [% o& I! }1 L
    Out[27]: 9 h  ~6 F  c3 _9 G5 u9 q
    DatetimeIndex(['2020-01-01 00:00:00', '2020-01-12 14:24:00',
    # s4 }* V8 A0 v2 W6 F& O               '2020-01-24 04:48:00', '2020-02-04 19:12:00',, D* G$ v: P- O# e
                   '2020-02-16 09:36:00', '2020-02-28 00:00:00'],
    ' \+ R7 }" J# D  i  t              dtype='datetime64[ns]', freq=None)" s  A1 M( a, b2 o) W9 K2 [. _
    ; H: S2 ~! Z, E% \
    1
    ) B. x& D" S" i" T0 a+ s. t, P2
    6 }! N, |: X8 @0 d, x33 [0 J9 g+ A; I/ d" _
    4
    $ t; q; ?. \# r6 q5# q7 A) ~; x' y7 c2 F% s* B0 a
    69 J  }4 d" |7 \: n- m; N
    7
    6 A& r6 y, T9 n" m3 \1 ^& i8% d. k, {/ \0 \! c; u+ k1 N
    9& k% u7 \$ a; C2 @
    10  i  R/ T1 `3 Z0 x, d; y6 Q& K! @
    11' G: Q, g* @8 S& u, W
    128 e( ~8 q8 s6 k7 K
    13: @; [7 r/ ]. G1 J2 K
    14
    . ~7 c$ v! P) m, Q* [9 u150 i' k" s5 U  m; [* T4 S: v1 o+ T6 N
    16* [! B) Y. K$ a
    17
    ) L" p! g  e$ d# i3 _这里的freq参数与DateOffset对象紧密相关,将在第四节介绍其具体的用法。5 G4 Q  a7 E, }3 V  f3 w' J2 {
    ; K/ Z) g' ]& j; \6 b( i
    【练一练】
    ) N6 i& X3 f$ KTimestamp上定义了一个value属性,其返回的整数值代表了从1970年1月1日零点到给定时间戳相差的纳秒数,请利用这个属性构造一个随机生成给定日期区间内日期序列的函数。
    6 T) v0 n1 k2 a2 e+ L' U$ ?( x
    1 }5 A# d# ]" q$ Als=['2020-01-01','2020-02-20']
    , o& M; ?( H1 E  Y- a! Hdef dates(ls,n):
    8 s! m. e& B5 r7 w$ k/ X. C    min=pd.Timestamp(ls[0]).value/10**9
    ; D" E: U: o3 E5 s6 a* }  h& W    max=pd.Timestamp(ls[1]).value/10**91 A' ^: A) E) I0 Z1 K
        times=np.random.randint(min,max+1,n)
    * r! P; @& N- J$ B! ?8 U, |    return  pd.to_datetime(times,unit='s')
    * {. Y! i, u: D8 y* x$ K$ gdates(ls,10)
    $ }: n5 M' ~0 b' k6 t$ ~& v$ Q, z( H* A8 N! }7 t' A4 t6 v
    DatetimeIndex(['2020-02-16 09:25:30', '2020-01-29 07:00:04',4 a9 M; t2 `/ r" V- E2 ]
                   '2020-01-21 12:26:02', '2020-02-08 20:34:08',
    ( S3 h' t- }' ^: Y) W               '2020-02-15 00:18:33', '2020-02-11 02:18:07',
    / K4 k/ l# k6 |3 v7 O  q- t4 E+ w               '2020-01-12 21:48:59', '2020-01-12 00:39:24',
    9 k- j7 K2 Z4 i3 |+ L, R2 o! a. [               '2020-02-14 20:55:20', '2020-01-26 15:44:13'],2 x1 a# F- ^1 k2 H+ {8 j
                  dtype='datetime64[ns]', freq=None), I' e) X' U$ p* k- O" {! g9 b1 p! w6 h
    12 Y: Z% ?: R$ V0 L, V
    2( R$ N3 v: I5 i5 T
    3
    9 ^6 Z3 ?* b- ^: |3 b4
    0 c) d7 V1 x$ J$ ?- K3 A4 c5; G/ W: V! d0 [( L, N
    6
    : A+ s: P/ r( v4 J7
    , @0 c0 H6 [: }8: z4 j4 v* b4 q; w) n
    9' {8 t  V$ X" {3 a& U7 c  Q; @  }# b
    10
    ) A, ?" l9 b' y# @/ V5 J11& q' F' l) h" q* I
    12/ {; T9 L5 u6 F# v2 D0 Q, A
    137 v- z3 M  c1 C; v
    14
    6 x* f0 {2 d" F5 jasfreq:改变序列采样频率的方法,能够根据给定的freq对序列进行类似于reindex的操作:" i$ r) I+ M8 h* {! g
    s = pd.Series(np.random.rand(5),3 ^% h1 |6 @# c/ X" x
                index=pd.to_datetime([
    9 Q7 H' g3 w. ?$ H                '2020-1-%d'%i for i in range(1,10,2)]))
    / l7 g/ _: F4 k6 \0 X3 J' m, }  s" T* y, F
    ) q( ?$ k3 [4 _' N: T& g
    s.head()
    0 A9 M" h% y, @* ~, V, E6 y4 w3 kOut[29]:
    - `* {. T! v" t. w5 @2020-01-01    0.836578' `" g  m. {0 I7 H
    2020-01-03    0.678419
    3 R# Z( T/ t% P1 G( Z$ h2020-01-05    0.711897
    ' Y0 T6 i/ v# [% O8 B; R) z2020-01-07    0.487429
    & |0 O; A* ^! p# H8 z7 Y$ X: d2020-01-09    0.604705# b; t. O! {# O7 s6 g0 @' H1 ?
    dtype: float641 e, b9 v% R8 \- `- f4 S+ H. S& J
    2 W4 _8 S/ J+ E1 N( D8 y! A& @
    s.asfreq('D').head()/ M* ?- `6 _. D1 f8 w
    Out[30]: ! |  g1 P9 b$ l, ~/ _! n
    2020-01-01    0.836578& I- ~: H5 z% a, c* H7 I
    2020-01-02         NaN
    , {5 I$ j# F. Z( x! ]: R5 {2020-01-03    0.6784199 q- J! `" V6 t9 D% g
    2020-01-04         NaN
      ^* U. u( m; U3 `+ ]2020-01-05    0.711897
    # t4 E5 g! s- Z) EFreq: D, dtype: float64
    + n- R% u7 Q) U$ X& Q3 _; R2 k: N0 L5 L- z4 K
    s.asfreq('12H').head()
    , g: g) l. d( ~Out[31]: : k# u* R, z1 O2 a1 {, o
    2020-01-01 00:00:00    0.836578+ @. o) Q4 m; E" f, ?4 U) ~
    2020-01-01 12:00:00         NaN
    . u9 `( p7 z, P# ?% d' Y* A+ c2020-01-02 00:00:00         NaN
    : G$ W0 g) K2 ]  s0 E. X" f2020-01-02 12:00:00         NaN: n9 N  r5 x3 N. p+ h0 i
    2020-01-03 00:00:00    0.678419
    . _7 K3 n/ B+ Y/ p0 G4 U8 \: tFreq: 12H, dtype: float64* ?* X/ D. ^+ i# n) `

    % ^0 a/ ?& s: @8 V# E16 ~( |. u: f. q0 @; o, Z
    2; Q1 v2 j1 j9 C4 ]) z
    3: _; |* p! b0 @" a+ m/ i5 G5 @" }
    47 w% J& I2 z7 |0 w1 p; G
    5
    3 c9 u1 p, a8 C6 ~# x64 z( ~% G4 Q: U& X" t
    7
    . p6 H% b, n: J% H$ P9 y* o2 l# ~/ K8
    8 o9 [8 l4 q3 p1 t* J) I9
    3 o) e" R. ?% u' N) _' d4 a10
    1 d$ n7 o; I! k+ |% D; l11! [; T( k" N- T4 ]8 T" h4 N
    129 }$ P* ~  w/ p, y, l, A
    134 L9 ?2 {. ?. A5 c
    14* b7 F2 l1 u# ^3 Y) ^
    15
    $ R7 i5 z8 Z/ S5 f9 m; p' Z16' j6 |/ i$ R; f' N; E, m% m
    17
    0 e  l) [, o$ v  H; ^18, u( t6 {, }4 N: ^+ s7 l- j
    19
    $ ]7 O, n! D& }0 {" N1 G% v20
    ! N2 b3 g' _9 \3 S: h, F21' p1 N0 V! J/ H
    224 s+ @7 a' R/ r% a4 P0 ]
    23
    9 X9 U* G+ w" l6 }7 L24
    / I$ y4 Z1 v' H25  Y" p+ c1 V2 v5 n3 H, R4 [5 N
    26
    6 p! N- j1 h( t# k& V/ d27
    , }* ?! Y5 z- s1 A$ |+ G1 A28! o  n5 N, p7 T
    29
    ' J4 H4 s. L- ]$ y30
    ; C' `* z4 h. Q5 G% ^1 j+ w) \31
    $ ?4 n0 i8 t2 q【NOTE】datetime64[ns] 序列的极值与均值4 R+ q: t$ I+ Q) I5 a# `
      前面提到了datetime64[ns]本质上可以理解为一个整数,即从1970年1月1日零点到给定时间戳相差的纳秒数。所以对于一个datetime64[ns]序列,可以使用max, min, mean,来取得最大时间戳、最小时间戳和“平均”时间戳。! N; ^0 P: J% q% B8 }: K, u$ L

    1 g5 t7 E& @& D) l. H$ t4 {10.2.3 dt对象, N8 @, x" C' N8 Q
      如同category, string的序列上定义了cat, str来完成分类数据和文本数据的操作,在时序类型的序列上定义了dt对象来完成许多时间序列的相关操作。这里对于datetime64[ns]类型而言,可以大致分为三类操作:取出时间相关的属性、判断时间戳是否满足条件、取整操作。
    ) ~. m1 ^$ v; \) O; v. P  ?2 [2 g! ?; D) A( T& p
    第一类操作的常用属性包括:date, time, year, month, day, hour, minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter,其中daysinmonth, quarter分别表示该月一共有几天和季度。
    , a( S+ M% j5 T5 ]/ p5 ^1 {& h/ V' E; \s = pd.Series(pd.date_range('2020-1-1','2020-1-3', freq='D'))3 C' v" \+ x. ?" e# Q5 B
    ( i& r5 E$ X8 T1 j
    s.dt.date- f2 L4 J" _7 j  q
    Out[33]: 0 R% s, x) u( V( A" t
    0    2020-01-017 @) U* j5 r0 f, C: i0 ]
    1    2020-01-02% R4 ]( h1 K+ H+ x! l
    2    2020-01-03
    : s6 _8 q' T4 b" _& x) Zdtype: object6 T; G1 J! r; L/ B

    ( V7 j* J; \9 o' js.dt.time
    + i# ~5 k) n  W6 ^+ sOut[34]: " Q2 Y5 c# Z  I% c0 p. u
    0    00:00:00
    . C2 V% ?0 o6 Y+ E1    00:00:00. V: e( Z5 S2 `/ h  e/ D- g/ P
    2    00:00:00
    ' j4 n# j; p+ Q/ \- K4 \2 R2 h* B6 adtype: object& K! i) ^# a" B7 H% N8 r/ _2 S' v

    2 M+ g' _; n9 u- d4 _" t5 ns.dt.day0 V, m8 e& y) U- Y; l4 B( u2 d# D
    Out[35]: ) P' b8 ~7 u: y! ~
    0    1
    5 s  N0 }: ^$ ~! r1    20 }' r% V, w" Y
    2    3' V- N5 z; l5 o8 z1 Z
    dtype: int64
    3 q7 ^0 b1 L9 w/ Z0 j2 U
    / p6 H  F4 [" D( s% L8 Q+ Q* ?s.dt.daysinmonth1 t* D' g3 n2 b" e
    Out[36]:
    / I6 j5 ~9 v! n0    31. g! K- G" V( q" }9 {- Q4 L( p" p6 ]
    1    31  H  M  p4 D: j% E
    2    31
    " b* C8 [0 b. Pdtype: int64
    1 w& Z& v8 A2 K2 m
    9 N4 {* A2 j, _  ~9 N6 X/ ~, _2 x* q1
    ! }4 d0 N; T  p" Z9 x2 w' |5 U2
    1 o, a- L9 c! \# m6 N4 u  _3
    % ~' Q5 o1 F$ |& E; ^4
    9 I( k: p0 a" B+ S3 t) w5
    0 f0 P7 [. N# l4 ]( w9 V1 o2 [62 J4 P  m/ e' Z( _
    77 N2 j6 J1 u% e
    8+ g; d9 k7 G: E: ?9 f
    9
    9 T* L( c8 p) N3 V5 |. z10
    5 R5 \* a! Z* B3 q5 M5 n% X: V11
    * t$ \5 v% r5 I( K) u/ N% x12/ |# X) t& L3 m# g# c( A4 O
    13
    / _$ F0 m/ ~- m! y4 U14
    - y: z6 u$ k7 w: s/ w0 V) n15/ q) n. c# U# W2 [
    162 x4 e" W3 L) l: ~. Y, H: U- ?- `$ f
    17
    2 _/ S/ I  Y8 p18
    3 K( Y3 R! I) j6 \198 g. j+ R8 k# O" ?6 o  J1 O
    20
    4 j9 Q8 n* G( e3 d) ?7 ?215 s% ~* ~) I. w: I% o+ E0 f7 T' ~1 @' @
    22
    : X! m' q3 s$ S! T1 {23
    ) B8 S, f7 U0 V! s! D6 f24
    ! ]) a/ \7 h3 T* w! ]4 D' A9 `25
    ( X- _$ n8 V3 N26" |7 J4 s! h; O' K: d
    27' F7 D; I5 ]9 @6 M& h
    28
    ) O6 T. k# f$ h4 ?5 [* o7 I293 V, ?, m; }. q* v! [; R
      在这些属性中,经常使用的是dayofweek,它返回了周中的星期情况,周一为0、周二为1,以此类推。此外,还可以通过month_name, day_name返回英文的月名和星期名,注意它们是方法而不是属性:
    9 [: ~! f# @' \% y7 y2 q% d1 b$ \6 j, m2 b! o; T4 [) U
    s.dt.dayofweek# ~# Q" o7 C/ i/ h2 l$ Q( K- l
    Out[37]: 0 m# A! V9 \8 c
    0    2
    , u# b% t& A& Z- i1    3
    4 y  p$ U: A- W' ^3 B' r4 e1 z( ?# j2    4; L! w4 ]+ }, B
    dtype: int64; Y/ M  R# W, _/ W3 r

    4 i# C( X" `' Zs.dt.month_name()
    & d; {/ a1 T! F2 t/ O  hOut[38]:
    # h( l, o1 }' Y1 ^; C0    January0 _4 d- j7 ^+ ~* F: j
    1    January1 {/ Y% a- z& P6 l% g* n5 A1 F9 X
    2    January0 R1 u# h/ a! y3 c. Q3 e) O- G
    dtype: object  j* f/ M+ N, _, ~  k0 d0 x/ s

    8 s# s3 e/ \# n+ k' T1 U3 Ss.dt.day_name()1 i9 q* a0 ]5 v8 O" b
    Out[39]: % |+ f+ s' q3 W" J! }( o6 c/ A$ l$ }
    0    Wednesday9 ]8 _: Y  ]0 \$ J& T% [
    1     Thursday& X9 N, K1 `, r0 `# @$ U  I: a( R9 q
    2       Friday, f" m1 M* p* ?8 p% K! P1 A
    dtype: object+ r7 ^; x  z. U8 l1 `
    $ _. M% K; [9 L8 M1 i4 w
    1
    + j. a5 M5 D, |6 ?& X21 L( I5 [! r1 x; o" T# ]
    3/ H: [! e: V6 c
    4
    5 A8 O6 `1 G* }3 S. F  K( M: M53 V  A4 _6 S1 t8 Q; d+ X% j" E6 V; Y
    6& i% e; b* t- t+ M
    7* e& w% B: i- V* B' O! w0 d( L
    8
    & d; B: O' L; m2 I" E9  y, U0 w% F1 \" P8 d0 L# u
    10) x/ s7 `# \# a+ n$ h# j. M2 G8 D
    11
      o* i) A5 r; O; G12
    # F  u6 i: }% S/ r0 s2 C13( D- u& A$ v4 @
    14
    . v) P$ l4 o6 |: d* O1 Y0 z15& t, B- t2 B% t8 ~9 r: U& J, C
    16& |- |$ p9 `# I: v
    17
    3 @: I8 A2 d/ ?" H) W8 M4 }$ s18/ ^* x" m( M, Q8 y, C+ ^. k
    192 k% K6 q6 |1 n6 c% l2 I0 L* C" e8 E
    20
    8 u% G# y0 |% i* E+ Q( W- G第二类判断操作主要用于测试是否为月/季/年的第一天或者最后一天:# v1 h9 z% `" ~. N' r
    s.dt.is_year_start # 还可选 is_quarter/month_start
    & u7 A# M) Z/ c. E- i& d  a  s2 |3 IOut[40]: 0 K7 L+ Y  Q* A+ v3 c; ]
    0     True
    1 ^$ K: ]1 m6 z% d( L# M3 e1    False% H8 U( M. @! y4 T. H( \
    2    False7 j5 J& _/ J% \* v& d9 z8 l, {
    dtype: bool
    2 y$ |( [% C2 j) Z* v, y% `* @3 {, g  i" s% m
    s.dt.is_year_end # 还可选 is_quarter/month_end
    4 `- S) J+ P3 M+ v% jOut[41]: ) F5 p3 U; e1 N2 |
    0    False* T. ~: C/ m2 f1 ^, i/ V
    1    False% ~+ ^  ]: i! i1 b: l8 O( J
    2    False
    4 j* M0 ^& [! |6 edtype: bool# O. ]5 ?/ A5 m! H' P
    16 ~. V9 S0 h! X+ H2 r" w1 [; @9 d% w
    2
    1 K, z* {! I8 e9 O8 V3
    3 P. y1 t& C# ~, G4& |# T/ R8 P, d( F: T$ {# C
    50 [$ u5 l1 `/ p+ w
    6
    $ {5 X) w% Z  Y+ O9 B( K! y2 k7
    . ~. {  `: S/ S8 n4 A+ j8+ v! A) S) M. r) p0 T' C! S
    9$ O/ T. @$ }: `" \+ v
    102 v9 R  d: o0 t$ X
    11
    " e, i) u' M" s. `12& ]# _; f' m. Y# I3 @
    139 ^" w1 `3 j# w2 y
    第三类的取整操作包含round, ceil, floor,它们的公共参数为freq,常用的包括H, min, S(小时、分钟、秒),所有可选的freq可参考此处。0 v% n6 v1 M; g  R
    s = pd.Series(pd.date_range('2020-1-1 20:35:00',
    + F' |" B, h& T! }                            '2020-1-1 22:35:00',! p+ `" E" M. X* R$ C# K
                                freq='45min'))' K# b; `0 N4 t; {8 P4 P# D4 W. p* v
    , D7 g; Z' F8 B! {8 j1 u1 I
    0 W5 W3 M2 m2 h* i$ v
    s( v; ~; I3 }$ L4 j
    Out[43]: ' B# J( o: @7 v& g& J, |% P0 c  U
    0   2020-01-01 20:35:001 ?/ R  c3 F" M" c, v9 y# x+ k
    1   2020-01-01 21:20:00
    " M2 P/ K6 M/ t- M2   2020-01-01 22:05:00: h. o0 ~: I( ^: b3 c
    dtype: datetime64[ns]# t; U0 C; I1 s, O) P$ M+ B
    % u8 z: P! `$ H0 W
    s.dt.round('1H')1 X- k1 ]: ]+ Y; \5 X& k
    Out[44]: 3 P3 J% j7 h" S* \6 q% W
    0   2020-01-01 21:00:00
    , f. m5 Q8 K4 P9 S; l1 W/ X1   2020-01-01 21:00:00
    4 Z1 I& P; k5 I; L2   2020-01-01 22:00:00
    ; l% [& q0 m6 o( T" V2 c: K. x/ Q) Odtype: datetime64[ns]* B6 [( X( ~6 k$ a2 z$ A! a9 i! t

    & ?9 }& q" o4 b/ G6 Qs.dt.ceil('1H')& I) Z) D0 @9 \# ]) h# ~( h
    Out[45]: ! H6 f, O4 X9 U- f" A$ k# a
    0   2020-01-01 21:00:00  z0 C/ D) B2 C. q
    1   2020-01-01 22:00:00
    * f# A9 W9 R! \/ s# m! ^2 n  s2   2020-01-01 23:00:00
    ( I9 X0 p% X, U, w" c4 pdtype: datetime64[ns]+ V' [- K* f4 M

    2 Z" D" Q" {; j2 Y1 t  B8 D- Fs.dt.floor('1H')
      U* q9 ]% Y8 yOut[46]: * L+ o- [/ B7 ]& _2 y) H
    0   2020-01-01 20:00:00( _& g% K& o" R4 |, H5 i
    1   2020-01-01 21:00:004 ~. A9 ?0 y3 j  S' Z9 ^  n- \
    2   2020-01-01 22:00:00( q- Z% j* Y) [3 P. u
    dtype: datetime64[ns]
    6 E+ R5 h6 K$ E4 a3 I- F
    5 i1 T9 [& a/ B; u1
    " j0 d' |5 o. V5 `- m2' W. E' Z7 o! e2 g
    3
    5 L, K% v3 w$ Q. y' [3 h4* [4 `" {8 U+ p" s4 V
    5) S6 J; m. D3 C  {, C' X& b& ]
    6
      g0 C1 l5 n" l/ T1 A3 B" u7! l: N; P+ z8 [
    8
    + K4 F' \( K5 I. }$ W99 G; }' E4 t' a( I0 j3 q* z
    10" p" J  b" b$ H+ C) s
    11
    ' d& \6 F! D; `' L$ E12
    1 Y6 L; h* l1 V! x13* I4 d( ?! t$ l  A& B- k
    14
    : V3 |  H3 I! B+ n9 q& c15
    4 S/ S4 n& W, M  M8 b& h16
    + R* z8 N* E& b7 f1 J174 A+ S# P4 `- u3 c
    18
    . R1 }0 g+ S: M: |# h19* |* K, |7 I- ^7 f4 W/ `# [8 ]
    20
    / S+ x# e- i* X/ O, d* Y% t21
      D, h" K4 T" ]: T221 q: }1 ], h+ d
    23
    $ h' q* Q) c! N' v) f24
    7 s7 Q( q/ c& {, r25
    3 x" S2 I( E1 ~/ ~4 N! X' q263 Q5 C1 }& H/ r( P
    272 {0 e% @( G3 h! @2 {
    28
    1 n; X( R! {/ _. s% k296 c" s4 A7 D6 S6 @, i" B. a
    30
    0 r- r% Y, ~* p; e* s: v31: ]! w" E- g- M
    32
    : ~1 J4 Z/ B/ r- E! q* \( H$ I10.2.4 时间戳的切片与索引
    & D% w$ N! s9 h$ o, j( r  一般而言,时间戳序列作为索引使用。如果想要选出某个子时间戳序列,有两种方法:
    , A) f4 N. s6 z5 ?8 S) z6 G" k2 d' P7 S; U
    利用dt对象和布尔条件联合使用+ r: Z$ O' X* R( T& c
    利用切片,后者常用于连续时间戳。" r9 j7 I6 y) r' H. c$ X2 L
    s = pd.Series(np.random.randint(2,size=366), index=pd.date_range('2020-01-01','2020-12-31'))9 Y4 Y+ m$ G, n' H0 y; _6 T: e7 ?
    idx = pd.Series(s.index).dt% K; A6 U5 i# X- i
    s.head()
    2 {+ q, @' H# B9 _% X; t
    ' l; h- I0 m4 G5 {$ e2 F7 n) z* [2020-01-01    0
    - H. a# R( Y4 A3 U, O# K/ {$ I2020-01-02    15 y8 X  j- |. M% f
    2020-01-03    19 R% f0 C# S+ x* S; T- B. l! h' C
    2020-01-04    00 e, T4 x6 x$ l, M
    2020-01-05    0$ {/ y( a( P, V4 a% I( v+ E0 }
    Freq: D, dtype: int32
    " O, z- T5 l1 w10 s! w" E7 i) p& {
    2
    * }  A2 h0 i" g6 |& X8 q; }3* E; C* P3 i: I5 L
    4
    6 Y% {; @  D' e3 ^% v# i, I: k5$ Y3 ]8 ?/ J2 H6 g8 u% V, \
    6
    6 s1 m* O: S4 X' E7
    : h! {6 z: k! @1 j- D8% g' V9 l1 u( J9 a' K
    9
    7 L/ `. I6 c! Q! V- Z) [- k6 ~10/ E  i8 N) t: t/ |
    Example1:每月的第一天或者最后一天
    ' ]+ F5 O: k3 B# g; y* A5 }6 h2 d2 \) E* K
    s[(idx.is_month_start|idx.is_month_end).values].head() # 必须要写.values9 Q3 B/ |# o; K
    Out[50]: ' U0 A/ o2 U" ]( d  M9 H& D# q8 a
    2020-01-01    1
    * `3 ^* Z' r, X+ d: \0 i- b$ t, P2020-01-31    0  U+ n- }$ O) Z7 [6 O: W. F0 I
    2020-02-01    1
    9 E4 m! [- _7 ?. _2 ~$ J2020-02-29    17 P( g) C. `( X( }
    2020-03-01    0$ y1 a# }6 _2 m3 E; T$ t
    dtype: int32+ b8 ~8 R6 f2 f9 X
    1# T- O# z. q4 J' G. ?* q* l, X
    2
    % J' d6 h3 e4 Q3 i! h3# d) t. X# l5 v9 r$ r$ v
    4
    $ V* i/ {) ?, h" a5; _3 m" j! C# q$ h9 `$ I7 i& M
    6
    * |0 _# a$ {9 Y+ A, y; F. f4 v% D% B7
    $ ~1 u: s+ y8 ]86 h' h* Y* r' t- |7 K" R/ \% _( k
    Example2:双休日# S2 b+ H" ?* l, N/ \

    1 n& R! p  ?. J: Z6 I- J0 P( rs[idx.dayofweek.isin([5,6]).values].head()% R- s& N9 p" N* F0 N0 s! V( `
    Out[51]:
    ( t4 C# h- J' c) j2020-01-04    1# Z& O, I6 ?5 x0 V4 ^4 W" T
    2020-01-05    0
    ) G6 @1 b# k6 t: v7 j2020-01-11    0/ Y0 c: Y$ \' g" I
    2020-01-12    1* o% F: x5 a$ q% ]# M* @# t9 F8 Y
    2020-01-18    1
    ; |" C2 y+ _$ M/ b% p& o; d0 Xdtype: int329 v8 n1 J; Z, K; U0 P# v; y2 v
    1
    4 a" ^. \3 ~! @2 d20 t2 h  p/ `4 q0 T
    3
    & {  m1 c2 t+ a1 S# `48 T) S! C/ e" j; w/ `
    51 ~* I% U* w# H$ e# E% R! q7 b4 v; o
    62 l7 z6 Q2 E# C
    7! w# n2 i$ O" T+ i% l
    8$ C7 D. ^9 y$ {( i3 w
    Example3:取出单日值
    2 [4 q; ^" J% [& q6 P+ q7 j6 l
    0 `, ^% r2 P1 v8 ls['2020-01-01']9 }) n: O( Y3 S1 b% `: f
    Out[52]: 1
      j' u, T  L$ }& z
      f8 k: J8 F. p. E, |1 |s['20200101'] # 自动转换标准格式
    . n: z4 [! |3 |Out[53]: 1& E- p1 g1 S+ u- c" l; I
    1/ |! v* b, M4 Y7 }4 t5 h
    2/ x2 G& V' K, Y* K+ H6 f/ m7 [
    3/ ?# G+ J+ {% n5 M* `
    4" ]$ f4 N0 A) `: H" j7 {) p
    5
    : Y7 D# _/ }# @) S; D. j( bExample4:取出七月
    ' k, I: f, d& y6 V( p, s! L5 r! ^) x9 I
    s['2020-07'].head()* x% B8 o: X0 U8 [1 H0 _; f! F
    Out[54]: 4 s4 J8 E/ v) H% f9 |
    2020-07-01    0. [+ a/ ]) J+ S# C1 J
    2020-07-02    1
    2 c; f, t: }( l( _* |3 k2020-07-03    0
      y' O) l* D6 e/ w4 F2 q" E2020-07-04    0) g2 j/ R4 c; H
    2020-07-05    0
    $ |. S3 K/ r: U7 R( p, {& xFreq: D, dtype: int32
      K" I& Z- N. W1
    " p- E4 ]7 }" t9 \- x+ `3 {2
    2 {  m6 M$ D# ]0 A5 `6 v  z+ E/ Z3
    + r/ o! c/ v7 [0 k, ^2 s9 Q5 G+ z& Q& o4+ h' V6 p9 b: f( ?5 C
    5
    " B- L2 N. Y  s8 @0 _6
    " w! M* A7 s) \: K6 I3 u2 ~* m* M7; v" @0 \5 a+ i5 b' h
    8
    ) K5 R3 Z) e+ e, Q8 e# w$ J/ @Example5:取出5月初至7月15日& `. [8 l9 W& X2 n# P9 ]

    ' ~5 w, z8 S1 w; L% j% X  m3 n( is['2020-05':'2020-7-15'].head()6 Y) ^9 M' H: s6 ~+ U1 O2 G! p
    Out[55]:
    ) a- T- F' V$ j# [4 J$ h2020-05-01    0
    4 `$ {' j6 ?5 ^; h: Y& C5 P; E2020-05-02    1
    : k3 w; t$ c5 T1 A2020-05-03    0
    * E$ h8 A# @, C6 I2020-05-04    16 v8 i3 h- c( Z+ p6 p, v
    2020-05-05    1
    ! K! \$ S9 p" i5 D6 X# L6 i" [Freq: D, dtype: int32
    7 |3 \" O/ l$ V& A
    ( ^  g3 [0 D  ?/ W+ {8 s7 ]: ]s['2020-05':'2020-7-15'].tail()
    : j4 ?7 r7 H# [Out[56]:
    0 C5 V) Q& H7 \7 D2020-07-11    0
    : G' O) e8 g  o( {: ~2020-07-12    0
    , R, Q$ r0 f6 q* F2020-07-13    1
    ; A) x+ s. t7 a  D. U$ C! D" b2020-07-14    0$ p7 W4 _  T' m2 F4 S+ u- i5 c7 ^
    2020-07-15    1) X; V' _' L9 M' V8 G7 M6 x
    Freq: D, dtype: int323 |8 \9 T7 |$ v# Q2 l' f+ g; h

    & E7 X- ~$ X: U( Y& m' ^7 I) O1' x- {6 R  v& s. ?; j4 v* f
    2
    2 |2 S# ?3 D% t( o4 A$ ~( b3
    2 |1 d: z2 Z8 D4% Y2 o4 c: l" e: ~% W( a& ^
    5
    7 F0 b4 ^* i% ^/ `6
    . ~+ }3 E1 B1 `$ L& q! a77 ^: x, o, f- X
    8
    * `4 d  Y1 r( L! N# |+ a9 S7 z9
    ! s& ^7 l! E& D6 s10) M9 ~+ ]6 H( D/ u7 b. K5 Y2 J, o) N
    117 e0 C2 z, ~% g& K) Z
    126 u+ H" a& f! ?. _2 l2 c% V3 k
    13
    ) n: G5 z6 u$ T7 B14& S+ W/ a* `$ b. Q
    15) M) p+ v3 t2 p1 u4 z" w
    16
    0 m% z2 |6 a% F  ~17" [6 j$ q( G# ^) u+ N4 A# _' c6 Z
    10.3 时间差8 |: N1 E" Y. P$ q6 E' Y5 e
    10.3.1 Timedelta的生成2 H" \: I% D, J( B& x5 Z1 \% N
    pandas.Timedelta(value=<object object>, unit=None, **kwargs)
    / Q2 A) H5 q, w( s- G$ T, z  unit:字符串格式,默认 ‘ns’。如果输入是整数,则表示输入的单位。
      F& [8 \5 ^; N' W* T2 ?  可能的值有:
    % x8 m7 q. u( Z4 u, C; {, H, S& [# p% J0 ~
    ‘W’, ‘D’, ‘T’, ‘S’, ‘L’, ‘U’, or ‘N’2 u4 Y; n& d( K  G4 b) \' P3 |8 ~: D
    ‘days’ or ‘day’
    + ]" T  ?. `6 A  _) \‘hours’, ‘hour’, ‘hr’, or ‘h’) Z+ D7 v& C* Q/ b0 x/ n' p- }8 z; j
    ‘minutes’, ‘minute’, ‘min’, or ‘m’8 D2 }# H; W/ ^/ s& a- E
    ‘seconds’, ‘second’, or ‘sec’0 A' c* ?" D- v8 q! v
    毫秒‘milliseconds’, ‘millisecond’, ‘millis’, or ‘milli’9 C  k9 ]" \  ?7 ]# f6 h" R7 J3 B
    微秒‘microseconds’, ‘microsecond’, ‘micros’, or ‘micro’( F* d/ x' e9 f! P" ]
    纳秒 ‘nanoseconds’, ‘nanosecond’, ‘nanos’, ‘nano’, or ‘ns’.- n) t4 t: |, U8 X% E  C' K7 M
    时间差可以理解为两个时间戳的差,可以通过pd.Timedelta来构造:
    " p4 `, @5 n  j. ypd.Timestamp('20200102 08:00:00')-pd.Timestamp('20200101 07:35:00')
    ) C, |8 c- I! \$ {: p/ mOut[57]: Timedelta('1 days 00:25:00')
    * x; h, E4 l! Y
    ) Y# ^3 g% t, ]% ~7 A* {' E5 Epd.Timedelta(days=1, minutes=25) # 需要注意加s. s: {! E% d! ^/ p$ X* b  r& j0 G
    Out[58]: Timedelta('1 days 00:25:00')
      ?9 N1 M8 G6 F! ~' l. p
    3 T8 d3 i$ B4 |pd.Timedelta('1 days 25 minutes') # 字符串生成  }: C" `1 }1 q/ R  c) a$ o
    Out[59]: Timedelta('1 days 00:25:00')
      o) W: m" i4 a9 j
    + \# q. C5 B; l) P/ c! q2 X8 `& mpd.Timedelta(1, "d")& c9 v4 q9 }. d8 X6 M! J
    Out[58]: Timedelta('1 days 00:00:00')' I6 ~( O( {- T) n
    1
    . C: ^) B/ s( |4 ^! }, x% Z2
    1 q- {2 [# m' J) F) q3
    / p5 k8 L% R9 e8 w. Y  k4% g- ]+ V- ?1 {, P
    5
    9 n/ {0 {* L( G; z/ e6& C' n: ~/ ^6 F. C& v
    7
    8 q2 `/ _: @" z/ R: g/ P+ [8
    3 }' J$ S/ w/ i$ S8 |3 L9) Y' }+ U/ d+ d4 _4 }
    10- E( j% [- n8 N) @( n
    11
    - B; |' y& A  l$ h生成时间差序列的主要方式是 pd.to_timedelta ,其类型为 timedelta64[ns] :
    9 u) A! W/ u( ds = pd.to_timedelta(df.Time_Record)9 V( r% T, {# R' k
    $ ]! N% j/ N/ v0 D3 D5 B9 o+ e
    s.head()
    $ B0 b, V2 r+ l" V' R9 COut[61]:
    8 e; ?5 H! f4 k0   0 days 00:04:34
    . F+ r! K8 _6 j7 `8 B; m0 r* E5 t1   0 days 00:04:209 X# [" V( i1 k; E# o/ e" ~9 D
    2   0 days 00:05:22- B( A9 i. M, ~7 f& g
    3   0 days 00:04:08- b3 g7 y9 c( K. W5 m
    4   0 days 00:05:22
    , e( b' v+ n6 L7 x8 T1 xName: Time_Record, dtype: timedelta64[ns]
    & F3 e2 |9 K" A5 P1
    " o  n1 t' Z/ R' q+ C2" M( B& {9 w# @: K/ R. e- y5 q
    3
    * f3 p- }. R/ Z. m47 M2 {, A8 v0 M/ Y9 L  I
    5
    % n; r2 g% z" e5 T: P6
    $ @' p" e+ R( f4 g3 \7  `: g( G! @# ]* W
    87 T  _8 J$ c+ r% W5 b& O  C
    9
    2 D5 U7 g$ w9 b10( B4 I9 ]/ f: z0 V4 ~
    与date_range一样,时间差序列也可以用timedelta_range来生成,它们两者具有一致的参数:% ?8 P0 P% ^* n* a; B
    pd.timedelta_range('0s', '1000s', freq='6min')
    # Z2 B, `, v! GOut[62]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:06:00', '0 days 00:12:00'], dtype='timedelta64[ns]', freq='6T')
    7 W' E) R; l7 t9 j# n0 h4 }" \
    0 }! \6 f' ]* {3 l- T$ Spd.timedelta_range('0s', '1000s', periods=3)
    " S/ G& j4 G$ _& Y  Z$ BOut[63]: TimedeltaIndex(['0 days 00:00:00', '0 days 00:08:20', '0 days 00:16:40'], dtype='timedelta64[ns]', freq=None)
    . m6 A% w# y4 k) ?9 i( b$ v4 X1
    1 U- D1 a  f9 ~0 x9 H2
    7 o% H% O3 r2 c* s6 O# ~3
    * L2 o' d' X+ f' I2 C! r& o4
    ; u0 }; X) A; u/ X! L5) W5 \( a6 F+ t' x8 p2 M/ `
    对于Timedelta序列,同样也定义了dt对象,上面主要定义了的属性包括days, seconds, mircroseconds(毫秒), nanoseconds(纳秒),它们分别返回了对应的时间差特征。需要注意的是,这里的seconds不是指单纯的秒,而是对天数取余后剩余的秒数:
    # f4 p1 C  G6 u# C# }( ~+ Y0 Hs.dt.seconds.head()* }5 M/ u" ~+ X2 ~! T  y  ~& h- H
    Out[64]: + ~7 U0 u" c. j/ E
    0    274
    % B+ @- p0 E* k7 K: J% I( l, Y1    260
    & e  H. n1 ~: `$ S2 K2    322
    3 C2 y2 X5 T: [+ S& v# v3    248
    8 b2 f: D) S# f! ]4    3225 M7 H" k/ H/ L# z- @
    Name: Time_Record, dtype: int64
    1 W, `! F& x3 \- t8 g" w0 ^6 F4 t1# r* z$ T  E* a9 h- v
    2
    7 M" V- @8 c' P# {. X4 U9 P& o3
    1 q3 I6 N8 E& `! \$ b# N4
    / F! m! @1 o3 S4 ]5
    ) I5 T% u* x0 I- d) p% P6
    ' g2 H& i( J& _& U$ b8 ?2 h  y7
    3 ?* c) c/ `- {' U5 r9 B5 X# \8! O$ l: l$ N* i7 y5 \  U. v
    如果不想对天数取余而直接对应秒数,可以使用total_seconds
    - V& X* V5 i! P, C2 a  K& h" k" Z; ^; ]8 V9 e
    s.dt.total_seconds().head()
    6 P" s, x) @9 t$ L  a4 N8 u. XOut[65]:
    $ P' W( q# x) R/ |& ?: |( s0    274.0/ }: U) |& T4 @" V% `4 M
    1    260.0. z4 Y) g% ?) _" ]
    2    322.0
    / m* {: s" @( y3    248.0- H! U2 ~( J9 m9 I
    4    322.0
    : x8 w* `3 Q- y/ l5 U$ c  V5 t- xName: Time_Record, dtype: float646 F$ }: `8 b$ K/ a
    1, r; d/ v+ h+ y" r2 q3 z: J: v& e
    21 ]3 j; r- I- |# M" u# @
    3/ S* ~3 H4 l9 l0 h+ X- h) ?
    4  a; K5 f+ p' p  S
    53 Z# P& R. E# |- ?. R
    64 |  q% i4 c: T7 K6 e; X) R
    7' X' D4 D3 y4 Z# b+ M& \' C3 i
    8+ }- J1 N- X( \4 w5 ?
    与时间戳序列类似,取整函数也是可以在dt对象上使用的:
    2 s  f. W6 t5 j( Q' r7 U) C5 N2 @' n
    pd.to_timedelta(df.Time_Record).dt.round('min').head()9 B2 u/ P  H; p/ a
    Out[66]:
    : `2 X* Y) P" L, V" l0   0 days 00:05:00
    4 N5 [6 q" }: D. W: y# i1   0 days 00:04:001 n. C0 f+ m8 q8 X9 |- A
    2   0 days 00:05:00+ @$ U7 t4 e8 _$ G
    3   0 days 00:04:00; t3 r& o; o5 d7 D: w* [
    4   0 days 00:05:00# K* c: I7 I+ L& ]0 |) e
    Name: Time_Record, dtype: timedelta64[ns]2 V; i& ]  T$ O4 K1 `
    1# h. H' {: e9 x% R* \
    26 Y/ n. {1 \( i  N$ }
    3
    : P1 q& C, ^; {5 h& k4
    ( v' l( b+ p2 |5 m% {7 t5
    / I3 v2 g7 ]% C6 I& y66 G: ~: f5 X  ]2 c1 u# e
    77 B1 b1 @& |+ |" T
    8: C* L+ k' T4 J$ f1 g
    10.2.2 Timedelta的运算. }$ D6 X" r. M- e
    单个时间差的常用运算,有三类:与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算:
    % b- {: w$ q8 S6 A# Wtd1 = pd.Timedelta(days=1)) y4 \9 H" G7 j* p3 V3 \
    td2 = pd.Timedelta(days=3)
    # s+ I% W- w8 i0 Fts = pd.Timestamp('20200101')" D1 ]. S2 s6 g0 T. k2 e
    5 `9 Z4 E* _* Z( L6 H( Z
    td1 * 2# S: m4 h4 y  [. O9 o2 [" s
    Out[70]: Timedelta('2 days 00:00:00')) L$ Q8 A2 k$ ]+ K$ U, g
    ' {/ b: J* K, u/ Y+ v
    td2 - td1
    % n9 g0 l" _* I: a, R' G7 _Out[71]: Timedelta('2 days 00:00:00')
    / D8 }- d1 i0 t9 v, v$ F; }0 N3 W
    6 n' b: v: E# T% q9 A) y* ?ts + td1
    5 ~/ r: A" f9 G3 u7 d! Q* o! a! g. gOut[72]: Timestamp('2020-01-02 00:00:00')( C" R0 N) q: P$ ^* j
    : T5 X+ p: q7 L/ ^
    ts - td1
    * e; P4 q0 G4 x) C2 rOut[73]: Timestamp('2019-12-31 00:00:00')
    % a$ _9 h( T$ L# k1
    7 R5 T) d, `( y+ Y2
    + h0 ]% q& d- X: @6 u3
    + R1 h9 W5 A# a; u9 S$ G48 C% C( ]% j7 ?  ?" ?
    5) o5 u- X0 A# x7 O! }: o  y3 e
    6. X: B8 l9 {5 |) d" w+ ^6 V6 L4 X4 b' M
    7
    7 e7 m  E2 ]; {8
    ( r( u; s' M6 @( H9 ^+ J9- s. x5 g7 z) D& {$ V; i8 _
    106 n/ G1 G( a) {0 J0 b4 n- o$ {
    117 P! |: Q( I- U: A
    121 j. A! E2 ]# e1 {4 `, E4 F" p
    13# N+ m+ [! [' ^2 Z0 d1 l
    14
    , b3 A  y% m  A+ H15& g4 u& N1 H# k3 z# R
    时间差的序列的运算,和上面方法相同:
    ) f3 c& x# y9 v' r1 ntd1 = pd.timedelta_range(start='1 days', periods=5)# J  \+ T* R$ j4 P+ N# h& U
    td2 = pd.timedelta_range(start='12 hours',4 G% L2 M2 ]  f6 [
                             freq='2H',! M% S" u7 v# D
                             periods=5)
    5 i& ]7 ]/ C8 Z- E5 U# ets = pd.date_range('20200101', '20200105')
    ; U/ [: f7 g2 _2 G0 ?td1,td2,ts
    2 B% z5 ^2 a) D4 n* T! z
    1 D3 \5 J2 q) j& _: XTimedeltaIndex(['1 days', '2 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq='D')
    $ E8 {0 N1 Q# E, S) ]* QTimedeltaIndex(['0 days 12:00:00', '0 days 14:00:00', '0 days 16:00:00',
    7 S7 T1 l+ }# G8 ]* I1 x* X                '0 days 18:00:00', '0 days 20:00:00'], dtype='timedelta64[ns]', freq='2H')$ ?" s5 s# a- u6 H
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',
    , ^/ J  N! y+ }( {* L: i# j% L               '2020-01-05'],
    2 j( Y, q- R8 j* C% K9 N              dtype='datetime64[ns]', freq='D')' V7 Z0 O0 G4 w% m
    1
    ) c; w0 D% |' w: p, e2
    / j0 \, p4 o" d3
    % g6 B* M% l% n4
    ) O+ d. R3 {$ d7 V54 Q) U: b' a, q3 f
    6" ?$ J8 v# t/ P; a0 e
    72 _) F# M6 B& ?% I% V
    8& K- ^4 c8 d5 R  u
    9
    3 B* d2 |# [' U4 S: Y2 W10
    4 F  a/ k* F' w4 u11! _% u5 G6 j) P$ o* Z1 u+ X# G
    12; B) }) t. j+ M1 t
    13
    4 y' j) X9 @+ f) Y8 T% Itd1 * 5
    6 h! h0 v! @. q1 `Out[77]: TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D')" G4 {4 w2 E2 I% O2 }' m2 l
    " o2 Y" F0 ~; D5 @# O1 I
    td1 * pd.Series(list(range(5))) # 逐个相乘. T& M+ |; A. }4 l  K$ b
    Out[78]: , q4 _$ L: X* f! A) K
    0    0 days/ e( J' F( R( D# w* s2 C: u/ ~
    1    2 days) f- h+ m, F  G+ i
    2    6 days8 k0 p, d* K3 Q0 t
    3   12 days
    7 u3 k( V" {1 n3 B4   20 days, v2 K; a: V4 x( b
    dtype: timedelta64[ns]
    8 o# a* X$ ]- _) n( P- S7 O+ H6 ]  A+ D9 D% Z
    td1 - td27 K# M7 d; F; f4 g: \
    Out[79]: 0 H# g2 G+ x- v+ z" c: i" a
    TimedeltaIndex(['0 days 12:00:00', '1 days 10:00:00', '2 days 08:00:00',
    # T5 w3 Y$ }8 }% X& q) R                '3 days 06:00:00', '4 days 04:00:00'],
    8 R0 F! w6 R& ]3 B               dtype='timedelta64[ns]', freq=None)" i' J7 C) ?6 P# h7 W" @

    $ T) I! H, h; J* G) a5 ~3 ^td1 + pd.Timestamp('20200101')$ P' h' l% ~7 w8 r! o/ i4 z$ i6 e
    Out[80]:
    ) I3 \4 X$ P+ pDatetimeIndex(['2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05',5 V2 S! o- _! W( x$ \1 ]& b4 U  ?
                   '2020-01-06'],dtype='datetime64[ns]', freq='D')& ?6 z! `! K* K7 z! H. e/ i

    . M' J  n! d- H, o0 [td1 + ts # 逐个相加
    , y" v6 ]: z: n) ]8 mOut[81]:
    / }1 O, Y- ^, D. f* u! M6 p$ m: Z. fDatetimeIndex(['2020-01-02', '2020-01-04', '2020-01-06', '2020-01-08',% d+ F0 b6 q9 r
                   '2020-01-10'],
    , g  p6 I. R/ M( Q& x              dtype='datetime64[ns]', freq=None)2 g3 N1 s/ R! u' T1 y( x

    8 b9 Z* R1 T, n6 p/ ~4 [1
    - Y7 g, O" ^4 j, M, [: ?: k; h2  W. X3 O2 b0 ^7 w/ }* \, X. w  u/ y
    35 D& l0 |5 d8 U6 T
    43 W( J% b1 z7 l5 W7 u2 O! e7 R
    5- j$ C  E9 e' h1 @+ w+ h8 Y
    63 ?5 h4 b2 L+ j
    7
    ) q1 j' ~) J1 d( t$ C# O) X3 ?8
    + Y( i) P9 M# W$ P* w1 C9
    : Q0 B: j- L) s10% o6 o: E' }7 x* ~% j
    11- V9 c9 _! [9 l0 N/ X# w. m
    12" W: M$ B' X0 \; Z6 s, g! m
    13# ~! n2 K4 m: E3 g5 K- @& l% E
    140 P8 b6 H  Q7 _; M3 O* J$ H# F/ H
    15$ \$ _8 M" z' _" H9 h! _
    16" I& c, D  `' A. g$ r* ?
    17
    7 _# k, m* n$ J; v( [" |; F1 \2 a18
    , X/ O$ @# @" t) m: P1 v: l6 r19
    $ G  p3 C! }3 t! L8 U20
    7 G9 ]* a6 c: C7 T# d21; ~' L. B9 G1 z2 t) o
    22
    : g& c1 U) C7 d. P1 G3 P' E$ V2 A23+ F- e( |/ J4 k
    24' J& L' K) p# I4 J, s) k
    25
    . M/ ?( U) K( E% s$ ~: H. _26
    % r1 _* f1 p) W+ S3 N% u271 m' |" y( ^, @0 q" z! v3 D
    283 r- D) Q0 w8 e  l, D
    10.4 日期偏置4 ]& K0 [8 A4 f, D" O
    10.4.1 Offset对象
    , }9 S7 C6 g5 {- Q* j' o  日期偏置是一种和日历相关的特殊时间差,例如回到第一节中的两个问题:如何求2020年9月第一个周一的日期,以及如何求2020年9月7日后的第30个工作日是哪一天。' [: U$ o0 j4 h: ?5 j) |" A/ {

    ) n7 N! v# X4 c& S; ]DateOffset 类有10个属性,假设s=pd.offsets.WeekOfMonth(week=0,weekday=0),则:
    9 h, i! w) \& s) p$ ]  k) L9 O
    " g0 c3 u6 h  C$ D/ y4 F! ys.base:<WeekOfMonth: week=0, weekday=0>,返回 n=1 且所有其他属性一样的副本4 n7 X" k& Y( P" b- P, W
    s.kwds:{‘week’: 0, ‘weekday’: 0}
    & B' p) A7 o! \; t" _" w* Zs.wek/s.weekday:顾名思义0 c$ g# i' M+ i0 ~
    有14个方法,包括:5 E5 e9 x4 b' p, Q; J/ F

    9 E0 H& c- }3 t# A3 ODateOffset.is_month_start、DateOffset.is_month_end、DateOffset.is_quarter_start、DateOffset.is_quarter_end、DateOffset.is_year_start、DateOffset.is_year_end等等。! `* ^' Z  X' C9 N
    pandas.tseries.offsets.WeekOfMonth(week,weekday):描述每月的日期,例如“每月第二周的星期二”。" v3 [% L) X$ V3 L
    1 G! I( R4 I% D3 ~0 N& E7 R' I0 _, V2 i
    有两个参数:
    ) y0 t3 X4 H+ q/ I- Lweek:整型,表示一个月的第几周。例如 0 是一个月的第 1 周,1 是第 2 周,以此类推。
    ! g" j% Y2 C3 Y* E& n" |) T; I* ]0 ^weekday:整型,取值为[0,1,…6],表示周一到周日,默认取值为0(星期一)
    9 U4 ]  s, m2 s3 \' ?pandas.tseries.offsets.BusinessDay(n):相当于pd.offsets.BDay(n),DateOffset 子类,表示可能的 n 个工作日。
    & p+ T- V0 Y4 ^/ I' a8 }, ?1 u
    8 V# |7 }8 L- S" p+ R0 s1 Apd.Timestamp('20200831') + pd.offsets.WeekOfMonth(week=0,weekday=0)
    ; S6 @3 ?) M0 }% g: h( ?  {, COut[82]: Timestamp('2020-09-07 00:00:00')
    % r4 y; }: C0 O) r0 v  }7 o
    * \7 }# g8 G! L4 d- Rpd.Timestamp('20200907') + pd.offsets.BDay(30)1 D% D* F6 |4 z3 M
    Out[83]: Timestamp('2020-10-19 00:00:00')
    6 X# w" z: P2 {/ b$ }4 w) ]1
    9 ~* G- y) A0 Y6 q/ C" I+ a, Q- o2
    ' b7 V( f) o+ f/ _8 d* l9 U  u3
    ; }. J: A; |4 c' ]; a) \- S46 M3 ^/ K7 v+ f0 O
    5
    / f- C5 ?: o- v0 ]  从上面的例子中可以看到,Offset对象在pd.offsets中被定义。当使用+时获取离其最近的下一个日期,当使用-时获取离其最近的上一个日期:, u6 E/ d# K% x

    - v9 o1 |# Y& W  }2 K& m7 O' l  @pd.Timestamp('20200831') - pd.offsets.WeekOfMonth(week=0,weekday=0)
    , j# m; R2 I! E4 |( POut[84]: Timestamp('2020-08-03 00:00:00')
    9 N( w4 F7 j# M# q, F
    $ h. J* \* k; n! ypd.Timestamp('20200907') - pd.offsets.BDay(30)
    * U4 S- m2 t) j  d  BOut[85]: Timestamp('2020-07-27 00:00:00')
    " t! \' x- z6 X$ z0 X! \& q: c- g. N
    pd.Timestamp('20200907') + pd.offsets.MonthEnd()) O& z7 y# b. I
    Out[86]: Timestamp('2020-09-30 00:00:00'): o) y. A( H9 R* {# \7 y
    1
    4 W" q3 P5 m4 x5 w2
    & @+ r* j% s' Y/ F- Q$ |4 X7 |3
    2 t; T! P/ G5 R2 j4
    6 b& }* `7 {. }, n; G+ a57 f0 m$ c  ]. l5 H" l6 R" ?
    6
    - |1 T: ?4 }# f6 ^! k76 B5 A! [* f) _" r1 z* M: s: }! M# T
    8( J! @8 @9 O- i; a
      常用的日期偏置如下可以查阅这里的DateOffset 文档描述。在文档罗列的Offset中,需要介绍一个特殊的Offset对象CDay。CDay 或 CustomBusinessDay 类提供了一个参数化的 BusinessDay 类,可用于创建自定义的工作日日历,该日历说明当地假期和当地周末惯例。
    . p: h+ s& r3 H  q/ \: N  其中的holidays, weekmask参数能够分别对自定义的日期和星期进行过滤,前者传入了需要过滤的日期列表,后者传入的是三个字母的星期缩写构成的星期字符串,其作用是只保留字符串中出现的星期:
    * t7 J$ X' ^% d7 `
    $ \: \8 Q5 A1 T$ ?+ L# g( i4 lmy_filter = pd.offsets.CDay(n=1,weekmask='Wed Fri',holidays=['20200109'])
    & F$ X% n, h0 v- ^  `: q8 ddr = pd.date_range('20200108', '20200111')# \% |3 R9 P! q  D7 [' H6 X* [
    # }$ j8 |% ^0 v# p' ]& p1 e- L
    dr.to_series().dt.dayofweek. e  v! ?  k: g& c) i* P9 W
    Out[89]:
    - q2 Q$ K( t& X3 `8 d6 J; b2020-01-08    2
    % E( ?8 T; }- ?3 u2 k2020-01-09    3- r% l: e9 b" b! l1 C
    2020-01-10    4
    7 Q9 ]0 Y6 J0 T( ?2020-01-11    5
    ' q* l* n5 j/ g: b. WFreq: D, dtype: int645 n$ m, S0 x) U" R" u

    % I8 }$ O+ p# Q% w[i + my_filter for i in dr]; w9 A" E( i0 X% o0 A: q' O
    Out[90]: : c# E5 G2 e$ \. ^
    [Timestamp('2020-01-10 00:00:00'),
    7 {( p: N. V2 _) u+ M' C Timestamp('2020-01-10 00:00:00'),, g5 K1 x* ]& F" n8 H6 d
    Timestamp('2020-01-15 00:00:00'),
    . _0 V* k1 R  a/ }1 p Timestamp('2020-01-15 00:00:00')]5 \7 `4 S1 G4 Q" Z1 [8 N
    % K+ E! U0 Q5 o+ U5 Q
    1
    5 k! ^# G$ }! \# ~' i4 u- a$ W7 L8 {2
    : `$ W) i" G$ u: H# a1 n2 e) A35 ?& O& Q( X" c  ^7 L5 [+ t1 H, _0 ?
    4; G% D4 E0 K' e& r9 z
    5
    7 ^8 p. ^2 j" ~) h% L; t68 T- p! M) H. G) Z: r  q  W
    77 E" S( _. X* h3 d+ `# `/ C
    8
    # t/ Q2 f0 ~' A7 S& ~97 p+ C6 p3 `. R( F8 `
    10+ U2 M5 |/ [0 a9 d7 A4 {' y7 u
    11  G- C: G3 z! V5 g; N
    12% C7 C+ C  }# D: m! J& ~
    13
    9 F2 S& y' u' h0 R% D14+ L( C3 {. u9 E1 ]- n6 ]2 V
    15% q* W! H' _+ h3 [  m1 K
    16
    ! ]: F# v- V8 s( P* G) H/ E3 O1 L2 C170 _& h7 o+ Q# d  M. ~
      上面的例子中,n表示增加一天CDay,dr中的第一天为20200108,但由于下一天20200109被排除了,并且20200110是合法的周五,因此转为20200110,其他后面的日期处理类似。
    & S9 ]7 t/ P7 u& W/ M# r& y$ d+ o+ D! Z/ i( y8 j) _
    【CAUTION】不要使用部分Offset2 P5 r& {. m. Y. s6 n/ s6 e9 I; v
    在当前版本下由于一些 bug ,不要使用 Day 级别以下的 Offset 对象,比如 Hour, Second 等,请使用对应的 Timedelta 对象来代替。
    & C1 c+ }" m, L3 z$ U+ m; f  ]
    - i5 ^( y5 O* |0 e$ j10.4.2 偏置字符串& P+ Q/ `. \  k& L* ^
      前面提到了关于date_range的freq取值可用Offset对象,同时在pandas中几乎每一个Offset对象绑定了日期偏置字符串(frequencies strings/offset aliases),可以指定Offset对应的字符串来替代使用。下面举一些常见的例子。( t8 P5 O2 F' R- C' l. s2 O( t

    % G! f+ E8 u! H$ l4 Z- T  Offset aliases:pd.date_range函数中的freq参数,为常见时间序列频率提供了许多字符串别名。 也称为偏移别名Offset aliases。偏移别名列表点此参看(大概27个)。
    ( p2 ^, S* c; o* l2 q0 O6 q3 T, }7 N( O; E2 W
    pd.date_range('20200101','20200331', freq='MS') # 月初  J" t; w  B4 |. X! a1 R1 ^
    Out[91]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS'), N. N/ @5 |# i8 f( ]2 m
    - }" {* T- V/ o/ k1 l
    pd.date_range('20200101','20200331', freq='M') # 月末
    ) [: _: Q6 i8 {! O7 MOut[92]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')0 F+ n3 {7 R1 r9 q; E
    7 E- u6 }/ }, F
    pd.date_range('20200101','20200110', freq='B') # 工作日
    & \  V5 m& b1 F; z/ E+ Q6 {Out[93]: 4 L1 h5 p7 ]6 E; V) h3 o
    DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',; n. ]. [7 i3 f* ~
                   '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],. X3 `, R' q% h. x7 a2 G. v: P7 T
                  dtype='datetime64[ns]', freq='B')
    8 S2 E9 l, N2 ~( X# T
    ! d# y  [+ o2 Q! {pd.date_range('20200101','20200201', freq='W-MON') # 周一
    7 W) v4 p2 |6 A. ]Out[94]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='W-MON')
    9 E7 K8 f3 R' U! j' Q' M; L
    # t( m. y( z1 a( dpd.date_range('20200101','20200201',+ R0 W+ m: |' s7 u! g
                  freq='WOM-1MON') # 每月第一个周一  y4 q" f- A$ p4 \/ v7 W
    8 `! |8 h3 @1 }" t: q/ O
    Out[95]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')6 q1 X( V* q1 ^
    ( h6 S0 N' R' K' W
    1
    9 g) o5 d, n2 b8 Z( x& y' B2/ Q7 _- c# j% g6 X; [2 v
    3
    - z  ~* {0 u; P- z* k. a4
    8 O. D6 ~6 J4 a9 {0 _6 O, l5* b3 `  o/ _6 Z) _# a5 }- y
    6
    " {9 `7 B# d, c! F  ^9 o7
    / t9 g; {" w1 b5 {7 e) Y8  O/ V9 T9 B, L( c1 E: `$ {
    9
    3 A+ T# D' n% j6 C10- V9 C9 Q1 c3 ?7 Q! L/ @
    11/ ?/ U+ z/ w* b5 d) J0 F
    12
    ( C+ T7 s2 x3 f' t138 B3 _/ o2 J& Q0 N0 n
    14
    : |$ E4 P( f8 w2 y& B0 N0 P9 i15
    6 w  [+ Y1 q+ V9 F16& e2 M8 ~5 o  L2 Y; Y+ y
    17" Y' I( V5 ?. ]$ n9 @; A' u/ C
    18" T, f5 l, g% J( A9 _* f! Y$ P+ S
    195 }: T0 q3 ?9 _( {1 ^8 B1 _& z
    上面的这些字符串,等价于使用如下的 Offset 对象:
    * H7 L6 U) u- P. H2 N. v9 V! x3 A: G: Y# R+ f. |* S6 m9 V
    pd.date_range('20200101','20200331',7 x6 l7 G/ u; E/ }' {0 Q8 c, s
                  freq=pd.offsets.MonthBegin())
    $ i0 k% ]4 ?: b. U% q
    ( c: c& Z; s7 F4 j+ c! KOut[96]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')( r) e/ v4 G; ]/ s( M

    - y/ E$ U+ N, l" ?  _pd.date_range('20200101','20200331',
    % [, Y4 S) |: a8 x" K) K              freq=pd.offsets.MonthEnd())
    6 a: p% i- [8 U/ u; S3 m+ n1 t
    & C; M; r3 Z" ]' X/ P3 Z0 y3 }Out[97]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M'). L( _  u9 g% w: J

    / z+ m7 z( G, wpd.date_range('20200101','20200110', freq=pd.offsets.BDay())
    5 k! ~2 R2 X- c, }Out[98]:
    ) W! K- u2 N: Z  V' KDatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
    ; _" P6 M4 i9 Q  S6 u! a               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],( V: Y8 [+ [% S0 D: P+ k6 Z7 x
                  dtype='datetime64[ns]', freq='B')2 I" o8 a6 G8 a1 Y9 s

    2 C% z: G$ W5 L+ z9 K" [: p+ n4 zpd.date_range('20200101','20200201',
      D0 ?" z( _9 ~9 a: `- {              freq=pd.offsets.CDay(weekmask='Mon'))) a& z$ Z$ B+ @8 Z- A/ ]

    2 c0 }4 B5 N- E) B: w. dOut[99]: DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='C')& u9 q) `0 W% w+ G1 A
    6 ?& E8 m1 y7 B3 W6 {
    pd.date_range('20200101','20200201',
    + E$ q/ l  Z) _1 C1 @6 [: \; U              freq=pd.offsets.WeekOfMonth(week=0,weekday=0))
    , y! B+ h5 \/ Q* m1 I6 Y
    & f: N' h2 M' T9 QOut[100]: DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')9 F& D. N+ C5 I( t/ {0 I
    2 f0 M- M' J% Q" p% X$ j
    1/ C: }1 H% m1 W  I8 {& `6 P
    23 p9 x: J9 @8 v% P! C% `( P
    3
    ! ]8 T; L' Y9 Y# v) H" E; B  ~! {4" Z* \# w% O! h( _
    56 h' c' l+ i8 W' q1 Y+ b. A
    6
    5 ]. R% D. o% X4 A( I; M. V79 R7 ~8 B8 o# K! O# r( @6 P
    8
    + D5 e9 R, w" A' s1 ]1 j, X  I3 ?/ B9
    4 q& r% r( `' U, \10* |; v% _4 C* T& P
    11' P' l- J$ q3 c& Y
    12
    % o: O0 c% r1 l, u% M13$ P% e) G6 O0 N( \
    14
    # H8 n. P0 |8 G15
    2 r6 J0 o* ~8 Q+ h9 h$ A16* x) T1 d2 u4 G8 z8 `4 s$ `
    17; k, r- G, R0 ~) ~3 s* z+ H: e
    18" e1 O5 G# W8 s! Q4 H- e/ X  M
    19/ d* J0 x/ S5 u# i( T; {- n
    20' |1 n: R& ?9 I4 h4 H
    21
    & t) e9 b# C( c: Z/ F7 I2 |5 O4 G22
    ) |) L6 I4 [7 }: t1 y23
    ) l  W7 U4 v& x8 m0 w1 E243 f$ W6 y$ o+ @  r- ^+ b
    25
    + Q) O# f  J# h【CAUTION】关于时区问题的说明, g! |3 ]# U( s% i( |$ a/ U4 \
      各类时间对象的开发,除了使用python内置的datetime模块,pandas还利用了dateutil模块,很大一部分是为了处理时区问题。总所周知,我国是没有夏令时调整时间一说的,但有些国家会有这种做法,导致了相对而言一天里可能会有23/24/25个小时,也就是relativedelta,这使得Offset对象和Timedelta对象有了对同一问题处理产生不同结果的现象,其中的规则也较为复杂,官方文档的写法存在部分描述错误,并且难以对描述做出统一修正,因为牵涉到了Offset相关的很多组件。因此,本教程完全不考虑时区处理,如果对时区处理的时间偏置有兴趣了解讨论,可以联系我或者参见这里的讨论。7 _" H1 G$ X$ j

    ( P- W% m. l3 U$ g( G10.5、时序中的滑窗与分组
    8 h! d/ \' f# ~* H: d: N10.5.1 滑动窗口7 W2 n. e! O. B- t' U! z6 Q$ k
      所谓时序的滑窗函数,即把滑动窗口windows用freq关键词代替,下面给出一个具体的应用案例:在股票市场中有一个指标为BOLL指标,它由中轨线、上轨线、下轨线这三根线构成,具体的计算方法分别是N日均值线、N日均值加两倍N日标准差线、N日均值减两倍N日标准差线。利用rolling对象计算N=30的BOLL指标可以如下写出:
    / Y- E% G$ J0 e8 u; y: o
    ; y  i- C5 I# X; simport matplotlib.pyplot as plt
    . `% u* A. U4 }& H( p8 Gidx = pd.date_range('20200101', '20201231', freq='B')& s; Q- q) t' F( R# Y
    np.random.seed(2020)+ w& b+ H' x" G

    9 C" C7 q, J  ^% ?5 a9 v0 ]data = np.random.randint(-1,2,len(idx)).cumsum() # 随机游动构造模拟序列,cumsum表示累加
    ' z% Z: W# b/ M3 W5 ps = pd.Series(data,index=idx). h3 V7 A% P; E# J3 x6 d, D
    s.head()
    ) x3 c2 t  H6 ]! ?4 h( C' q0 O/ EOut[106]:
    ( e3 l6 H* O& H- }2020-01-01   -1# u- j0 s# V/ O4 W( O" Y/ ?8 h% m
    2020-01-02   -2' g( R* b' t  K6 S
    2020-01-03   -16 `5 q( @3 U5 t% d2 L
    2020-01-06   -1
    0 p) U5 I6 o; T; J9 |% L2020-01-07   -2$ e" K9 ~( C( T, L9 t8 e1 o  J
    Freq: B, dtype: int32
      E  }2 B/ s5 u7 [r = s.rolling('30D')# rolling可以指定freq或者offset对象; _; D- s8 D, ^3 e: d9 P

    + o) s) N7 b# Lplt.plot(s) # 蓝色线: v4 ~, s$ y) w" A* z+ E
    Out[108]: [<matplotlib.lines.Line2D at 0x2116d887eb0>]
    & P3 l5 d3 d5 E, g3 Pplt.title('BOLL LINES')' x( c  z' F/ }2 f9 l8 R! S
    Out[109]: Text(0.5, 1.0, 'BOLL LINES')6 w9 Z0 s! o5 S
    : ?' t) O4 S  b" e
    plt.plot(r.mean()) #橙色线
    - i# p* A8 E) g) A: F3 WOut[110]: [<matplotlib.lines.Line2D at 0x2116d8eeb80>]$ F  }  b) w6 Z. p% J) q

    - x$ C# n: N6 v0 _- S& h( d" _; p/ \plt.plot(r.mean()+r.std()*2) # 绿色线
    ; U; m& [2 N, E  x; y; UOut[111]: [<matplotlib.lines.Line2D at 0x2116d87efa0>]$ o7 W( g' j! K; h; n( L. M

    , q! t, }# ?  q: V3 F6 mplt.plot(r.mean()-r.std()*2) # 红色线
    7 C. ~# D$ V2 l8 LOut[112]: [<matplotlib.lines.Line2D at 0x2116d90d2e0>]
    3 W! K2 a- W. o- R. d' c" e7 H+ n' x( i+ |  b- N
    18 \& x+ ?3 U- c6 b3 l
    2
    * @2 I! |8 t  W( d2 C3' K! e) K- @' p% \- w" @8 H& b' n
    48 y$ Y: {( j" j9 |1 O. _1 E
    5
    / \8 U# T+ j. A  F% C3 C( U7 }- m6
    4 ]5 i1 Z2 u0 i6 a8 E$ j7/ a: L1 V9 y) d
    84 Q* F+ v, c6 `9 b# c8 p
    97 ]. M/ b6 u7 U' w
    10( a5 p- x3 ^7 v
    11+ e. k# `* v6 Q  P. N
    12& S% l7 B" Q$ s/ A( H" }
    13% D2 j" m% ^3 W
    142 c( `  w# Z( w! p1 k
    15
    6 u* `7 h5 ]" n16
    , k/ y, I0 @, u17
      o& b( P1 k3 v18
    & A- P5 j# a6 `1 s19
    - B% L& o' p* C4 `20
    ) R  C4 W! S+ ^) w( O$ d  g; r) ~21, d% q& r1 I$ p5 I& S2 K
    22" d* X7 |, g6 ?; M5 F6 z1 t
    23
    - }2 U4 y) A. G: X24& ^8 X1 O: J2 x- K4 w
    25% e" y4 f6 X3 d0 U% B/ X$ G
    269 C$ d* p9 G0 k, Q7 r, I7 M: v3 b( l! @
    27  B6 v' G$ v! B3 A9 R  S, {
    28
    9 t% u* g6 x" k! F29
    6 L; i& c4 X) P3 v$ \) ]
    ) u. ]- v9 [: r# i$ d   这里需要注意的是,pandas没有实现非固定采样频率的时间序列滑窗,及此时无法通过传入freq字段来得到滑窗结果。例如统计近7个工作日的交易总额。此时可以通过传入多个函数的组合来实现此功能。" o# r! p: S! k, |
       首先选出所有工作日,接着用普通滑窗进行7日滑窗加和,最后用reindex()恢复索引,对于双休日使用前一个工作日的结果进行填充。
    ( o' ]  b! S( i& k4 A. O5 b' t) h4 z2 q8 Y% n, v" p
    select_bday=s[~s.index.to_series().dt.dayofweek.isin([5,6])]( |2 E- @  ^2 K5 t' b
    bday_sum=select_bday.rolling(7,min_periods=1).sum()3 q. M5 C: ^$ Y1 }. s# S& I4 o
    result=bday_sum.reindex().ffill()+ w* U: N/ d- I" w5 }& ~( E. U
    result/ ]& u; ]1 w3 G9 i
    8 m, p# W! T7 O
    2020-01-01     -1.0
    & h0 E; F& r4 V; ^2 b2020-01-02     -3.09 s& y( E: g$ I! T' S* U
    2020-01-03     -4.05 {3 @1 y. G; g* a. P9 ^
    2020-01-06     -5.0
    / H1 d. w$ h* o8 I, X2020-01-07     -7.0- I6 M1 i3 Y9 l7 P( I6 ^$ h
                  ...  $ }* e+ o/ G* b( c) l9 @. x9 X
    2020-12-25    136.0' h6 }; z5 T& l1 D4 \6 C
    2020-12-28    133.06 T  s$ s, h- F2 r3 R; U+ t- e, b
    2020-12-29    131.0
    2 u% Y# {/ p+ J, e' Y; V; p% s2020-12-30    130.0
    / r: q# O# w  @) R2020-12-31    128.0* v9 g4 _7 c4 {- z, ]. L& p1 t
    Freq: B, Length: 262, dtype: float64
    7 j& s7 [7 _+ v9 E1 C, s+ W9 O! K8 N! i9 D5 \
    1" C( V: W9 ?" @$ B# [2 ^, h
    2
    5 G, d3 g# w$ L6 F4 B7 X4 l3
    - ]/ K7 P( i. m, m: |4: B; F7 n: |( R! Y
    5
    ; D  P, J' w7 w& j64 J- i4 Y) ?. K! B5 v/ z
    7; c4 D; l& o; o
    83 H" W4 A! ]: O& \
    97 C& V* n; T  d1 b
    10
    4 N# i# r2 T+ t' _11
    ; H( q( f% Q3 r/ q* |12
    4 ~# `$ r  n  w. p8 a* P; [, k0 j% r13
    " @0 [/ ?* Y- i" a7 L$ l140 [& W8 U' O) x9 T( b
    15- w2 Y; j- B1 s) Q: A- ]
    16+ U  I9 M4 l5 T6 C) v
    17
    0 `% W6 ~% R: b* q0 z6 W; ^7 D  shift, diff, pct_change 是一组类滑窗函数,它们的公共参数为 periods=n ,默认为1,分别表示取向前第 n 个元素的值、与向前第 n 个元素做差(与 Numpy 中不同,后者表示 n 阶差分)、与向前第 n 个元素相比计算增长率。这里的 n 可以为负,表示反方向的类似操作。
    - \) q- N) l9 G
    7 H0 E+ a, ~  a7 ~: {/ o, x3 X/ o# Z  对于shift函数而言,作用在datetime64为索引(不是value)的序列上时,可以指定freq单位进行滑动:: T% n0 y0 y5 @: U- Z' H

    % K- Y0 x/ q+ g! Ws.shift(freq='50D').head()5 V% k5 D3 b" e1 r* P8 |( b( m
    Out[113]:
    & E# l# l& L4 I- Y4 C7 w$ l( s% N2020-02-20   -1
    / A6 h, t8 i6 d; {8 b2 `% M2 Z" E2020-02-21   -2
    # \: N2 @* H% N9 h# `9 ^2020-02-22   -1
    % a' m: K4 U4 t. g2020-02-25   -14 v1 U  m* Z. `; h
    2020-02-26   -2
    $ T+ V6 Z5 a4 rdtype: int32. t/ q+ R! W0 H
    15 v  H# c$ n5 m
    2
    9 V% `' w4 }% A7 J3
    3 H' }5 \: O8 E# w( ]4
    7 M1 o& Z6 d( `0 o# @5
    - ^# q* I9 x' S. w9 ]7 ]: A7 c69 K) c3 s& f/ T& c9 R6 N& F2 C  p) U* j, c
    7
    3 v, @7 q  v& x' p& f9 \9 |% W8 |8' V; L* H4 G8 T7 |4 L. F  R7 s
      另外,datetime64[ns]的序列进行diff(前后做差)后就能够得到timedelta64[ns]的序列,这能够使用户方便地观察有序时间序列的间隔:/ f% y' P& o1 v

    3 b3 [( q( Z: A* O, p$ U4 cmy_series = pd.Series(s.index)7 _1 v/ O' m+ u: q7 w
    my_series.head()2 w1 V( B, e! A6 D/ o" N0 M
    Out[115]:
    1 d6 q7 ~2 f8 d" f8 r( j% F0   2020-01-018 a0 \' ^3 s4 Q. {
    1   2020-01-02
    * ~4 |; ?% i8 C3 n, z+ j: X9 k2   2020-01-037 E" G6 m+ j" F
    3   2020-01-063 r7 E7 v% ]2 K7 F: m% g5 d0 }! N7 X
    4   2020-01-07# d' [5 ~+ u9 ?9 L4 Z
    dtype: datetime64[ns]
    & w+ O9 n1 g8 w
    4 ]9 l; G8 g  H1 ?$ u1 cmy_series.diff(1).head()
    & B5 [8 M. z0 J) S; fOut[116]: 0 @, {6 d9 k8 ~% U
    0      NaT
    ( t2 J, Y4 r; N! u* t1 T3 U" a2 R1   1 days; K" ~8 l4 u/ \. a& K9 Y
    2   1 days
    " r. z( _6 G& z( |) s1 _3   3 days
    % r5 Y) G( E& C4 e0 Q4   1 days
    6 s# g$ n% k& x  ~. @  Q! n2 Udtype: timedelta64[ns]3 i6 Z) K& n+ z
    7 @, u8 p, I+ [: `! O3 k/ l2 @: Z2 |6 f
    1
    / m  s' W, f" _( K23 N5 r0 o) C9 l7 f
    3% F7 R7 J! \" g# y
    4
      Y1 c; n1 i5 O) `8 c5# a; `/ L1 s$ {  V4 o3 n
    6$ {/ b" \8 \" B0 n- G8 g3 t
    7
    2 f8 s4 D8 Z0 m  x: {8
    8 J8 ?! V3 _; i9) {& F; W8 B3 s0 D0 q
    103 _* d7 P/ h. h1 z+ A0 C( D- b
    11
    ! z3 t/ x. w# N) ]- X5 w1 V12/ y7 M3 Z- a( N$ m9 @( g8 Z
    13
    & [8 Q9 T# G6 K8 C5 q' Z141 o8 A' G/ b) S+ V# I
    15
    1 Q' p& C' N; f* r5 w: k( S16  t2 g. G" a* Y( p! z% H
    17$ q" z- N- C: D, O/ l
    18# ]( [4 M5 I( D. s  T4 _2 Y
    10.5.2 重采样0 G. r1 M, r# Z
      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)* a2 m) W7 k! c) |
    常用参数有:
    ) a5 g! G5 k9 |2 b$ g9 N  q% @- p) t* @  z. e5 ^7 Y: Q
    rule:DateOffset, Timedelta or str类型。表示偏移量字符串或对象
    1 n* F% V9 k( i) z6 F+ yaxis:{0 or ‘index’, 1 or ‘columns’}, default 0。使用哪个轴进行上采样或下采样
    # ]0 s7 h3 R( d' rclosed:{‘right’, ‘left’},默认None。表示bin 区间的哪一侧是闭合的。所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。
    ; l# @/ ]5 U8 E" a& d- M# Qlabel:{‘right’, ‘left’}, 默认 None。hich bin edge label to label bucket with,所有offsets的默认值为“left”,但“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”均默认为“right”。6 \2 n0 M' F: ]. X' V
    convention{:‘start’, ‘end’, ‘s’, ‘e’}, default ‘start’。仅针对 PeriodIndex,控制是使用rule的开始还是结尾。
    2 h" o, Y3 {! y% con:字符串类型,可选。对于 DataFrame,使用列而不是索引进行重采样。列必须类似于日期时间。' `% t  I, C4 d2 G
    level:str 或 int,可选表示多重索引MultiIndex的级别,这个级别的索引必须类似于日期时间。
    5 G1 i( z8 K3 u( G' u5 porigin参数有5种取值:5 e! P' c7 p. K& l. o/ Q3 \
    ‘epoch’:从 1970-01-01开始算起
    - F) h; M/ Q" q% d- E‘start’:原点是时间序列的第一个值; ~& @! f' F/ Z2 a# n
    ‘start_day’:默认值,表示原点是时间序列第一天的午夜。  n& E' |% h4 Z* F7 E. o/ g# n
    'end':原点是时间序列的最后一个值(1.3.0版本才有)
    & T8 ]. w3 _- W) |# }‘end_day’:原点是序列最后一天的午夜(1.3.0版本才有)
    3 e: J- s  {* N4 \offset:Timedelta 或 str,默认为 None,表示对时间原点的偏移量,很有用。. P8 o* j$ t& e0 ?' l
      closed和计算有关,label和显示有关,closed才有开闭。
    0 r- G+ G5 x' \/ r  label指这个区间值算出来了,索引放区间的左端点还是右端点,closed是指算的时候左端点或右端点是不是包含。
    % s/ R. u. |: x. I
    9 z3 r2 U" S' q3 Q" o) {4 e重采样对象resample和第四章中分组对象groupby的用法类似,resample是针对时间序列的分组计算而设计的分组对象。例如,对上面的序列计算每10天的均值:
    # S- W9 w' n9 k( _) X/ Q! ^  bs.resample('10D').mean().head()
    ( V$ v4 o! D4 OOut[117]: $ F; B5 \) e7 K# y* H& B/ w# l
    2020-01-01   -2.000000
    7 F4 F( z+ B# y/ G7 z2020-01-11   -3.1666679 I; f& c# n7 ^# s0 D4 b' M7 _* c. E: u
    2020-01-21   -3.625000
    : J) T- H7 A3 D1 Z$ R6 t2020-01-31   -4.000000
    2 _2 ?2 n8 b2 n0 V# K2020-02-10   -0.375000
    0 x* r* G2 T# Y0 t8 q: uFreq: 10D, dtype: float640 k; W8 [5 o- p6 l- ^# y& A
    1: C! ^9 p  A1 A9 V
    2- P8 c6 g* _7 D% U
    32 ^) W& }1 D! W- U$ v, b, L/ A
    4! U: ?4 O" H1 U, u9 c5 d
    5
    $ q/ r, q$ ?4 a8 s6: b1 X6 A3 m1 i" s0 F" ^
    7+ h, V% ~, L6 Z
    8
    6 C6 {' O+ h0 M3 w# g1 ~! M$ z! F  K可以通过apply方法自定义处理函数:9 w6 ?% N  Z2 x: K3 n+ w0 i, E
    s.resample('10D').apply(lambda x:x.max()-x.min()).head() # 极差% T6 g. y+ z" K( x/ g& m+ N
    , N# E+ v" U, g
    Out[118]: 8 W' y* ?5 a+ V; G# q3 K
    2020-01-01    3* B' k6 f& X: K* K9 W) `* R# K
    2020-01-11    46 s. S2 D. K# O9 [1 x+ Z* e' ]
    2020-01-21    4" S7 r' [+ z  l" e" B! p5 Y/ Z( l" _
    2020-01-31    2
    ' C4 g- c+ \5 t2 c2020-02-10    4
    ' I& o  c0 G, g+ B% A% J6 ]! MFreq: 10D, dtype: int32
    # j: y+ W" @; X" [4 B1
    8 n3 M2 c% @6 z2 `2, ^4 `1 S7 h& Z: y) T
    3. z) i8 G+ k5 Y4 O! g+ w
    4- T0 a8 b& v+ B
    5
    * ^8 J) Y  x: _+ B# D6
    : L& \* J  p5 _4 V" n+ F+ ~7
    9 c- U. Q% K$ P  m! P0 X3 N82 y: b5 A( o  ?3 i
    9
    3 X& i! [3 C2 F/ F  在resample中要特别注意组边界值的处理情况,默认情况下起始值的计算方法是从最小值时间戳对应日期的午夜00:00:00开始增加freq,直到不超过该最小时间戳的最大时间戳,由此对应的时间戳为起始值,然后每次累加freq参数作为分割结点进行分组,区间情况为左闭右开。下面构造一个不均匀的例子:1 }! Z) F* k* B; ~/ i

      ]5 _- X$ R! d% O+ q3 F: Fidx = pd.date_range('20200101 8:26:35', '20200101 9:31:58', freq='77s')1 M) ^: ]+ r7 J8 v$ b5 q
    data = np.random.randint(-1,2,len(idx)).cumsum()2 i( L+ R& `/ U& {& [0 [* T
    s = pd.Series(data,index=idx)
    ) d& U( O! V# b1 as.head()( k: q) K% G$ _2 a  ^# J- z9 \  A0 d7 [
    + y3 U5 {6 t2 e. y6 ?
    Out[122]: % d5 E) o7 X: [2 i
    2020-01-01 08:26:35   -1' |' l7 D, m/ ?# f  p& I
    2020-01-01 08:27:52   -11 ~4 N" K" h8 b9 l, K' W; c9 P
    2020-01-01 08:29:09   -29 G& D8 r1 c& i/ K+ q
    2020-01-01 08:30:26   -3
    , f/ o: l3 J; e6 }2020-01-01 08:31:43   -4
    6 F  a5 h: y, a4 i$ N* C5 n0 sFreq: 77S, dtype: int326 \; j6 q- `2 u( e  m2 D) m$ u( j6 M: {6 y
    1
    - i3 @& ^9 p9 |; d9 w( K2
    - o) _! n8 [3 S& R3
    4 [7 ?2 E' ?$ Q7 {' z! F4! Q( z5 U! Y. @5 ~- |9 T- ]
    5
    . y, I1 q+ g4 b/ X: j2 y1 l3 e5 T6
    ; h) d( C$ o- ?3 G* y7
    3 p# L8 |' w, v- z' B81 E/ o  k7 M. u6 ^# ^) `6 ~
    92 I0 k  O! f  I. T% o
    10" K. B  ~, K* E
    11% |6 l1 q  R) H
    12" D% T! f8 e2 j" Z
      下面对应的第一个组起始值为08:24:00,其是从当天0点增加72个freq=7 min得到的,如果再增加一个freq则超出了序列的最小时间戳08:26:35:+ C' l9 @# t: P- ^" z9 s) }
    + b% L! }# e6 J) V. S1 t3 Y
    s.resample('7min').mean().head()6 c3 y. l  k6 B0 d2 G8 F+ M2 n
    Out[123]:
    ' f; z7 w' u; j1 J2020-01-01 08:24:00   -1.750000  # 起始值,终点值包含最后一个值6 K( z. j, i' h  @6 o
    2020-01-01 08:31:00   -2.600000
    4 h1 G' [1 c, ?2 Z# C4 _! Z8 j( Y0 e2020-01-01 08:38:00   -2.166667
    % ^( y2 L+ u7 n) f7 z9 o2020-01-01 08:45:00    0.2000008 e7 I/ |6 a/ F
    2020-01-01 08:52:00    2.8333333 u7 s* _8 y1 O+ f. H" [0 i+ E( L$ K
    Freq: 7T, dtype: float64
    # O* O9 u9 y( K) u4 g, a, c1
    , E9 Q  I6 L9 Q; _( H4 @0 a2$ ^( N- F: ~# K7 M5 E# z) X
    3+ ?3 I% T3 I8 U  [% V. u
    4  I( L* z! A# Z5 ?7 Q
    53 A" E2 C! C5 l6 Y! Q$ g
    6
    ) J6 X, t( _/ A9 r4 l, N7
    ) g. p7 h" b: B8
    4 V4 A6 g8 t' m7 Y9 P# }, m  有时候,用户希望从序列的最小时间戳开始依次增加freq进行分组,此时可以指定origin参数为start:
    ) O; y& n9 P4 N5 v$ G4 a9 h. n8 w, F
    s.resample('7min', origin='start').mean().head()
    * n& ^* w9 C8 b0 N1 I3 q% fOut[124]: * }' q& C" L* n) p# M+ g  Q
    2020-01-01 08:26:35   -2.333333/ S+ F8 n2 u! q; Z; ^
    2020-01-01 08:33:35   -2.400000
    9 \+ F) f. }4 x6 x. a' {: |. ]+ q2020-01-01 08:40:35   -1.333333
    9 p- m( I$ d# f& h9 a. T: K2020-01-01 08:47:35    1.200000  q% _) j7 t9 D/ y3 A& b! d
    2020-01-01 08:54:35    3.1666671 G5 M; M4 ?: Z, @' N
    Freq: 7T, dtype: float64& [) i1 p! L; q4 @! ?
    1# o; X3 C) B0 U* R% V  J; @; l) ?  [
    2" ?+ W  w& \# C! Y5 ^8 ^$ c
    3
    ( L' i; ~- ^3 A% a4
    1 L2 g; k8 d: `! }, r5& ~: `3 o$ s" q+ g$ t- i7 {
    6
    ! @- @" j! k; @4 M/ k6 o7
    1 Q7 j: {; [" v: B( R' g$ i8
    , y4 u' q  Z0 k' i: [  在返回值中,要注意索引一般是取组的第一个时间戳,但M, A, Q, BM, BA, BQ, W这七个是取对应区间的最后一个时间戳。如果想要得到正常索引,用’MS’就行。
    $ _- p- ^+ V4 ?! ]' x$ \" F0 d; @3 w0 t- }" {+ S
    s = pd.Series(np.random.randint(2,size=366),+ D9 A6 H0 |# T# {4 a+ S* U
                  index=pd.date_range('2020-01-01',
    $ u, U( I$ }/ x9 t" B) L7 ^                                  '2020-12-31'))7 r( A' ?8 d  s( I; m" I! r! u
    % j0 Y, B7 U1 u! F4 n4 u
    ! n+ J4 x/ F! ?6 \$ `- ^3 V/ E& B
    s.resample('M').mean().head()
    5 H" u" X0 N# [- ~0 g" o  ~Out[126]: . @9 U1 t0 P! T3 s* ]0 H) D
    2020-01-31    0.451613
    ) o+ }6 p) w& A! D- c2020-02-29    0.448276! O; K: K8 b' o
    2020-03-31    0.516129" q4 w& Y; u; u8 z
    2020-04-30    0.566667
    + y6 \5 }4 |9 t; W  L6 [; Q2020-05-31    0.451613- a+ V6 }$ I- C) n  Q9 v9 w& ^  S
    Freq: M, dtype: float64& A) B8 H2 r$ F: ]- B) m) a

    ( a+ {# M. A: f* E/ u' bs.resample('MS').mean().head() # 结果一样,但索引是跟正常一样. R) }  Y) T; O
    Out[127]:   {$ N+ Q" f8 B2 }) s
    2020-01-01    0.451613
    , v; C1 m* m# t& z9 U2020-02-01    0.448276
    + R9 g5 h$ m. E- S; X. w. {2 }5 S% j2020-03-01    0.516129
    $ d/ a* j1 K$ [/ |/ Q2020-04-01    0.566667
    ; Y* n1 |7 z0 G4 K/ ^2020-05-01    0.451613- I# V. p- T' A4 l
    Freq: MS, dtype: float64) [) T% U2 H7 o  \5 f6 ^  k" e- {

    7 X% l( a# ?/ S1& I/ t% r: b- n2 r
    2% @% [/ K& |/ _3 Z  V1 e4 ^! U
    3
    % I. [3 F4 p8 ~2 D9 t2 {4
    8 b, j* o# f0 Z5
    & j& G& I3 K7 L- b+ W64 d- h, m4 g3 s' q; o5 p. B2 V0 e9 X
    7
    , {( U  i0 J/ @# J5 f  q& i# j) V8
    * A, d* o0 l/ t, E3 N$ a9
    5 Q9 f) r! ^3 `* I104 k: l# [8 ]$ l! `& f
    11
    ! _+ O* }' L2 s1 }3 z. z12
    ) X# A% x* t" f/ ]/ A: p  p! ^+ P3 u13
    7 M$ `* p' X: }! u9 ~, Z14( L; ]. I* B3 r" t" w
    15& J$ N; s2 U; X5 n' E: K. }, n
    169 H6 i+ @, e+ K4 @/ o! Q
    17
    $ \; r0 s  i$ s18. j+ T+ W1 ]% y) f. N4 N+ n
    19
    : N. W  j, P. ~: M. ^20
    $ X2 o. D' ?& e+ i! G6 o21, k' o  `1 E: Z$ _* r- i
    220 W8 n# s5 ~1 `8 D. t9 s+ o' _
    对于 DataFrame 对象,关键字 on 可用于指定列而不是索引以进行重采样:
    2 s9 s, P  ?1 ?" nd = {'price': [10, 11, 9, 13, 14, 18, 17, 19],
    6 G( D  d, O$ a4 G     'volume': [50, 60, 40, 100, 50, 100, 40, 50]}( q- w* A, t' ]0 j
    df = pd.DataFrame(d)
    3 E  x- k5 T) L; j5 Rdf['week_starting'] = pd.date_range('01/01/2018',
    ; ~% H; b5 M* C; I, q9 c% z                                    periods=8,7 \: E0 z) M) w9 e: g/ J) |
                                        freq='W')
    4 h2 |, I& B7 m6 u7 T$ v9 Adf
    ( k* q1 D& n' H8 s   price  volume week_starting" O, M4 \9 ^0 ?$ ^/ \& K. j
    0     10      50    2018-01-07
    & d5 Y/ y5 z' p* h1     11      60    2018-01-14) x' _0 U4 r6 G5 |+ Y1 l
    2      9      40    2018-01-21) e+ [4 n( X% W' Z* w. t
    3     13     100    2018-01-280 |# N! W' C2 v# M
    4     14      50    2018-02-04
    7 Z! G. r( \& P3 d5     18     100    2018-02-11
    ' A4 F' [. E8 z* x, l4 b* Y4 Y6     17      40    2018-02-183 @# c/ }3 q+ K; T; n$ l6 L  D
    7     19      50    2018-02-25' U; o! z) ?; A2 {. w" d: r& s
    df.resample('M', on='week_starting').mean()
    # u/ c, q: d$ v               price  volume
    ' G* a: Z& a3 Z( p+ A' J0 }week_starting. ]1 a1 P9 d) \' z" Z- j
    2018-01-31     10.75    62.5) @" d9 U( g9 E5 [% i- d% ]
    2018-02-28     17.00    60.0
    % T0 z9 z. M2 t% `6 c, E5 c5 ?8 ^" d8 |
    1
    4 Z7 M7 X# q' D2 J2 B) E, [! w4 M2
    & t& i% o. ^0 R& S8 W9 v3' E, c  k  r3 b8 q6 T- d
    46 h2 E) F% g5 b' i$ y
    5" K9 W7 H8 x4 G$ ~: j
    6
    % G* u0 I( V7 p9 a1 S4 d7
    ! L+ o! B8 C! t$ y0 P0 G- W8  M! a! ~  G; J4 v  ~
    91 ]% c9 U) \. I  Q2 i% [4 p
    10
    % b4 T/ Y/ \+ a) {9 I# ?, {111 P" K) z* B. t6 A5 T. y
    12
    : [$ T- X0 a! Y4 m# v7 a9 m  R130 v) Z9 S3 ]+ }! G5 E
    14
    ( E2 _+ a' ?7 @$ v* X) W15
      ?8 T; L' z6 _" Z- C4 G16
    7 W9 F1 k/ @. i1 c, |- H5 B( A. d$ i17
    ) k7 c# j: b8 v2 G5 p/ Y18
    + b* L' r* v  i, a7 l. [19
    / j8 ^& d7 p- R. i: p20( z: S2 M8 A# K% s9 ^* x3 o; U# y' w
    21
    ) u+ L$ C" z8 `对于具有 MultiIndex 的 DataFrame,关键字 level 可用于指定需要在哪个级别进行重采样。
    : o5 h+ o) I' E: tdays = pd.date_range('1/1/2000', periods=4, freq='D')
    7 N5 o7 [) b! S. B4 ad2 = {'price': [10, 11, 9, 13, 14, 18, 17, 19],1 t( u, g$ s( u0 c
          'volume': [50, 60, 40, 100, 50, 100, 40, 50]}
    8 D6 p6 t2 N$ s& ^- P/ L; Qdf2 = pd.DataFrame(; }% r5 M+ h, H/ A% [  C
        d2,
    . e, o- L" X3 T& |' f: i& x( v8 U& I    index=pd.MultiIndex.from_product(# z( A9 l( N$ t( s% X+ d  E+ B6 B
            [days, ['morning', 'afternoon']]
      M  c) \9 k9 Y! v; B4 V    )/ E5 h; ^/ a% P) K9 y8 Y
    )
    : d3 K; e9 T2 B/ n' @2 ~df22 \2 |  }# x- t+ c9 y6 y
                          price  volume9 e. U  z4 j7 i) w7 E
    2000-01-01 morning       10      50
    3 T3 O, L: v+ _: d           afternoon     11      60
    ) m9 R# W' |3 {# S2 W2000-01-02 morning        9      40
    ) l# m) j* ?) _2 P. Y! R2 ^+ m, o           afternoon     13     100
    + w- l$ h8 S4 f$ U1 j; H2000-01-03 morning       14      50, z7 o# m2 b9 A
               afternoon     18     100, ^8 R3 V# u* J% o
    2000-01-04 morning       17      40
    " I: @  `' z7 S! [/ A( x           afternoon     19      50
    : S  B; e- @4 kdf2.resample('D', level=0).sum(); b& T% p+ R4 C$ y! x
                price  volume
    8 \3 R, o8 O" f$ s# O2000-01-01     21     1103 j$ b) ]" U& b9 ?8 K; x% i. P
    2000-01-02     22     140
    6 T$ c( ?9 Y- c3 A  Z1 @2000-01-03     32     150
    . ?; {* f6 I) \/ m2000-01-04     36      90
    7 r6 p! V# o5 K1 f3 P0 X) L3 j
    1
    . x( O, h# B* E; H& J2  n4 A4 V) c% L7 N" @! p9 F
    3: X/ E: G& J7 |
    4
    & A: S3 }5 H' F/ W0 ]$ [2 ~5
    - Y  W1 e4 E* `2 Z" _6
    % N. r7 B  d# w6 Z( m5 o7* _; x# F5 p  z, Q* s
    8$ c) S- U0 a/ Q. Q. K
    9" D& b+ F0 [' r
    10& _) v" H$ {, x$ E: A* l
    11' f) S# S9 e2 L: U! b; l1 [
    12
    : `5 E; V1 j: }8 m/ y3 B13
    & I2 U' l5 n6 n& p3 W& e- ^14
    : R9 ~' o- {+ @8 P15
    # W% S5 O7 P* j/ D$ V3 }16
    ; J) `0 s4 N6 s17. G  U5 j+ ^2 @( `
    18
    4 @' X$ r  p. c- }3 S) a19' w8 o5 W2 y; H9 {' l
    20+ X( k. {% p( Q& n* E7 n
    21; F; H0 k0 m. g
    22& Y2 \  f3 J6 c* r+ _" G
    239 W3 c) Q+ A# k7 m  L. a
    24
    , \' H0 b& Q) D8 G25
    4 s2 U; u  F, O& ]3 B/ o# l根据固定时间戳调整 bin 的开始:
    , z3 H7 O9 M! B8 w) Rstart, end = '2000-10-01 23:30:00', '2000-10-02 00:30:00'
    9 C  ~, S. V# I( ?% orng = pd.date_range(start, end, freq='7min')( d3 \2 @, v9 s1 a: O, d  k
    ts = pd.Series(np.arange(len(rng)) * 3, index=rng)2 Y" \, Y( i& m& C6 H
    ts
    4 |# W% w+ f- s- q7 p( L4 }2000-10-01 23:30:00     0
    , P/ Q- H/ D; ?4 w4 i2000-10-01 23:37:00     3
    6 m# y0 j# y% V- |& z2000-10-01 23:44:00     60 j- q% D8 ^5 W" Q: O6 v
    2000-10-01 23:51:00     9
    / U( Q' F% f& m  }2000-10-01 23:58:00    12
    - V6 S# i- |) @) ?8 |3 B- h2000-10-02 00:05:00    15; [+ j3 ?( q9 ~, i
    2000-10-02 00:12:00    18
    ) j+ {7 j- \* V; J8 ]* B, B4 x3 G2000-10-02 00:19:00    21
    # p5 R* p9 k3 i9 {2000-10-02 00:26:00    24, [6 e( D' W+ r5 R3 |
    Freq: 7T, dtype: int648 g$ O* M3 U: x7 F  \+ n2 O
    / ]# d2 ]! x, c7 [+ F* K0 a
    ts.resample('17min').sum()$ t2 i/ I0 I) ^; J! N" O1 x
    2000-10-01 23:14:00     0
    + w# P- z: U5 p1 m; ]: u7 J2000-10-01 23:31:00     9
    ) B9 @( R* [2 O4 U) G- _; P2000-10-01 23:48:00    21
    + r( Y, E% o2 J2 P2000-10-02 00:05:00    54
    2 g1 U( E/ ^/ i2 h9 S' L" N1 [2000-10-02 00:22:00    24. t+ o( J7 X1 R0 h2 V8 m
    Freq: 17T, dtype: int64
    6 J" r4 D1 I/ d7 x  \8 k% }1 {4 d4 \$ ]9 q
    ts.resample('17min', origin='epoch').sum()7 Y2 o2 K5 k8 ?, p- p1 ^
    2000-10-01 23:18:00     0
    . g* e, r6 ~, e( R' [/ L2000-10-01 23:35:00    18
      F4 v1 ?6 u  b, ]- J- L8 L2000-10-01 23:52:00    27  E3 m% r1 x3 l- e# h, E
    2000-10-02 00:09:00    39" _& g9 o! x8 ]
    2000-10-02 00:26:00    243 S" l; }# C/ w# Q; u! s" o8 o1 y
    Freq: 17T, dtype: int64$ ]! `. |: L. y0 `* A
    & a4 t9 n: \" T# V  B; ?0 S
    ts.resample('17min', origin='2000-01-01').sum()6 u, D: _$ _4 o" B8 G) x& Z: w
    2000-10-01 23:24:00     3
    " d. Q2 r+ m2 Z1 F& E; @, R( M2000-10-01 23:41:00    15  e' D/ q' N' f1 h( u- f
    2000-10-01 23:58:00    451 E+ P  R. r' d. X4 Z! \
    2000-10-02 00:15:00    45
    - I# T+ [( f) X2 b& WFreq: 17T, dtype: int64; D" K; G0 J$ A8 S5 j! m2 ~4 \6 v

    & s; w! V4 F- l7 ?; q; ~1
    0 f/ l/ a! K( i* ^; Y$ E) [, ]+ ~2
    1 e- b: o4 ~, D1 l3
    4 L" P( r  Z9 v5 s& U" g  ?2 _4
    3 ^1 ^; ~1 x% P$ Z* F& S5
    * G0 ]0 Z( m# X- K4 c$ {4 W6. e  ^! e+ B, M' x1 \; G% i4 Z
    7
    ) g) D' a3 W# Z* ^* f* H81 h" b4 y. V% m* _
    9; V  A# {5 N9 j9 [8 z! G& G
    10
    " v7 t0 V3 ~* Z: x* i; s# f11
    1 c3 u" `/ }9 z( p% c8 i$ M12
    % X1 T' Y: Z. q" H4 G13! x1 b0 G# T* U2 c; m, [
    14
    * x5 H" b- l$ s) f, S: n* `0 y15" @( h2 ?0 @6 a+ q4 }2 f
    164 h& ?2 j4 b/ l# N1 C$ z$ M# Y
    176 f+ m& {. U' ]& J
    181 R; X# V0 |9 I& V  n3 U) F
    199 \8 L2 u1 w. d3 G
    20! T8 r7 w" W2 k+ n9 P' C
    21
    5 Q7 d% P6 L# ]4 s, R  H22
    2 x8 `& u! o9 \% n' o- C23; B/ |3 o, T% V
    24
    8 @  v) Q+ ]6 y% G0 K) r/ z/ I3 X25
    " Y/ A1 r: r+ |* x) u" r26
    4 {5 q, {" t9 N, J  V27' x* D6 f4 t! l$ l- ?
    28) g' I9 F, S0 o4 }
    29
    * Q- C4 g, C* ]4 a306 w: u( D# q- k6 O, _1 W  }
    31- W' G# C3 x3 g; u
    32" ^8 p; t( m3 f/ a
    33  h( A' W( \& h" f# p
    34+ ]( h1 h( i" M  q
    35# ?4 M- y8 q8 I+ w% O5 e
    36
    " D$ C5 G+ w/ H7 g8 c1 d37$ m$ z: R! c" p$ ?% }
    如果要使用偏移 Timedelta 调整 bin 的开始,则以下两行是等效的:" K  w. k& ], o) T
    ts.resample('17min', origin='start').sum()
    . |) u) d& _8 h1 P0 Nts.resample('17min', offset='23h30min').sum()
    , N5 U5 p4 i9 v3 w- W& Y2000-10-01 23:30:00     9! C6 i0 L. V: [; M' P
    2000-10-01 23:47:00    21
    ! A/ c! m2 F! o( f2000-10-02 00:04:00    54
    , e) O: ~" X2 N2000-10-02 00:21:00    24
    4 M) d0 w- i% nFreq: 17T, dtype: int642 Y4 K8 U7 m  J0 M; j. n2 n8 K
    1. Z) p0 J3 W4 M. f; L; ~% h
    2
    6 Z& y2 [3 l; s9 R  ~( |/ P3, N$ Q- A! ?, p) Z
    40 K, O. Q6 Z: A/ |
    52 T; E$ |$ F9 ~2 Y
    6
    , f, H' Z! S9 c$ ~  D' o3 A7( w  _; i6 R* v, v
    10.6 练习
    % l% O1 p9 u! c/ [: [1 _Ex1:太阳辐射数据集; p) i" a, X8 z0 R: [+ P
    现有一份关于太阳辐射的数据集:
    ! Q6 ?6 R0 Z& G( ?
    , ^! J( R4 U; r; }! I; X" jdf = pd.read_csv('../data/solar.csv', usecols=['Data','Time','Radiation','Temperature'])) N. J! N' ~4 z2 K
    df.head(3)
    . @8 l! o, o+ W1 D5 z
    $ |* S1 b( C/ J6 p/ uOut[129]: " y5 r4 D( h- S' ?$ _; W  @* y
                        Data      Time  Radiation  Temperature
    " ]; m+ E, [. h, Y0  9/29/2016 12:00:00 AM  23:55:26       1.21           485 b, E# m6 V8 p& o! a
    1  9/29/2016 12:00:00 AM  23:50:23       1.21           48& X# z% s% N( T' b6 }- T: C
    2  9/29/2016 12:00:00 AM  23:45:26       1.23           488 M1 _3 a7 c- V  \3 S: N9 M6 \3 O# Y
    19 A' [0 e& `" x3 l+ w2 C
    2
      }+ {- K, U: ?% }3
    / I8 _4 A6 C/ h; i6 t& Q. {4
    0 U( Z8 y3 P  v: R1 t56 l+ ~; C/ ?6 B7 \& q) p( V
    6* O8 Y! X$ H; i% }9 f
    7: K0 O3 a8 ?4 r5 B
    8( {2 {* J4 Y1 {* b+ ~7 ]* ]
    将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。
    ; d9 t7 G5 ~- e2 P; r) z/ ~; b$ K/ b% @每条记录时间的间隔显然并不一致,请解决如下问题:  B# g9 w6 c2 \: n8 S
    找出间隔时间的前三个最大值所对应的三组时间戳。
    + V; w" ^$ \; K* S是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。8 ^8 n. E: _: l/ `$ Z6 r" c0 m
    求如下指标对应的Series:
      j, g' F9 [; {* b5 V- I1 b% J温度与辐射量的6小时滑动相关系数
    0 Q& x/ O9 J) N/ J6 J$ f以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列
    & p' G" O$ t( o# i0 B6 X% i每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)$ w  `9 ~3 I$ p+ ]5 y
    import numpy as np1 I7 m! \; Z9 `$ q" Y. g
    import pandas as pd+ @" l: S6 r: h" E
    15 J, P3 w+ x* b9 ]; s. K: _2 I0 q
    2
    - r1 L4 Q) `/ D将Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。
    % R* J+ F4 x6 Bdata=pd.to_datetime(df.Data) # 本身是object对象,要先转为时间序列; w( E* q1 `8 m: Z# R' X* s
    times=pd.to_timedelta(df.Time)
    " @2 l  `! q/ p) \8 Ldf.Data=data+times
    ! `; `- f- n4 C- o8 l, g( sdel df['Time']
    ' T- Q$ c3 z! }! X" ldf=df.set_index('Data').sort_index() # 如果写的是set_index(df.Data),那么Data作为索引之外,这个列还另外保留. ?, O0 |: S6 n8 g9 L" i4 ^% [
    df/ Y6 g  z5 X! q* |/ s4 u
                                            Radiation        Temperature
    2 [& u  O) q# h: e6 ?Data                3 W- d1 z; x0 }8 k- I
    2016-09-01 00:00:08                2.58                51
    - B8 T* a# g4 c$ G# U2016-09-01 00:05:10                2.83                51
    ; v7 P" {2 Q  X( j. O' ]2016-09-01 00:20:06                2.16                51
    / p0 u3 ^- G" Y0 [2016-09-01 00:25:05                2.21                51
    0 h; D" p( k1 a4 c4 o2016-09-01 00:30:09                2.25                51
    ' e. s+ _: D, K1 D) Y: K...        ...        ...( c, L+ M7 w. U/ f- Y5 S! V/ o; D
    2016-12-31 23:35:02                1.22                41
    ! x  D) y6 _1 i2016-12-31 23:40:01                1.21                41
    $ R( ?1 C* b( c% I. v% y7 r2016-12-31 23:45:04                1.21                42( G- T; l6 L5 K! c( l
    2016-12-31 23:50:03                1.19                41
    + [" w. m7 b  _8 |2016-12-31 23:55:01                1.21                41% @2 i7 ]1 a* z1 N

      a2 {4 ]  ~$ b3 I7 V, v5 \1' w6 }$ e0 J, f$ W$ u8 T
    2
    6 N5 b0 o9 n- x: \3 O; }39 t) p1 ?8 X2 j3 q
    4+ n5 i$ X3 f" F8 w& \( Y+ T
    5  G" [0 f2 m. R# K+ C
    6* d8 u" q- v4 e, p4 K, G$ P/ a* _
    71 C2 M4 {$ \# d6 M
    8
    ! Z7 q* z- N, r% m  d( t9& h" y7 \$ V" J/ J/ }* I
    10+ p) r$ |3 q5 s) u1 u  d  n
    11
    . _5 H. k4 _' ~9 w) N126 v/ g# p- L- [& ]
    13. r( e: d6 k/ V  A: I7 t6 C
    14
    , f9 W+ n8 n- s' @4 b- M( ?151 Q4 H7 l7 f% o& o
    16
    , T: d* V) Y( g) q( U1 W17
    : Z& V8 q6 {+ y$ N+ a  u6 t18
    9 ]( l% l$ G* x1 ?( _19
    + m5 R. M+ S; V+ @3 f  q+ U每条记录时间的间隔显然并不一致,请解决如下问题:
    & H/ @# ^) T6 A! l, S' n找出间隔时间的前三个最大值所对应的三组时间戳。4 L  ?9 G8 f% j
    # 第一次做错了,不是找三组时间戳5 f5 G$ C& S8 ~9 h! `
    idxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]' A! R6 A1 Y$ w
    df.reset_index().Data[idxmax3,idxmax3-1]
    " V& j: L# N, G9 G1 Z7 W4 C% B/ S' q
    25923   2016-12-08 11:10:42$ R' U5 g. ?$ {% Y5 g# d
    24522   2016-12-01 00:00:02; u. L& I1 H3 y% U, _
    7417    2016-10-01 00:00:19
    - e$ {5 G; @0 P1 EName: Data, dtype: datetime64[ns]4 K; g- j. L. c9 ]4 B7 T
    1$ \/ Z, Z* {& ?* e& j  a" r
    2' S3 \7 A; i% u$ |+ X& R
    3* q* B# `3 t7 f8 Z; M
    4+ M! |5 n7 D7 k3 ]( Q; ~/ J3 O
    5& j! H$ L. C" ]7 H0 b( G: |0 {
    6
    1 o, F3 `5 N8 g" f7
    3 O" x5 R0 ~5 x8 S) N* Y8
    8 c5 z6 j2 N& s2 f7 ~idxmax3=pd.Series(df.index).diff(1).sort_values(ascending=False).index[:3]; |2 F" W1 \$ ^% {6 i9 J' ^
    list(zip(df.reset_index().Data[idxmax3],df.reset_index().Data[idxmax3-1])); d. {" X) g; u9 g. z4 z# v
    9 Z6 H# `0 ~. Z
    [(Timestamp('2016-12-08 11:10:42'), Timestamp('2016-12-05 20:45:53')),# F9 E' U5 R! h* c/ |
    (Timestamp('2016-12-01 00:00:02'), Timestamp('2016-11-29 19:05:02')),
    1 {0 J4 N8 i9 q' e2 N1 ]# H) P! K( p( Y (Timestamp('2016-10-01 00:00:19'), Timestamp('2016-09-29 23:55:26'))]
    2 l0 I! e6 A5 t. s4 p10 P# ~$ {" N* [* ?
    2
    4 ^+ h6 \$ n  @3. U; E+ c" _% i% d
    4
    2 c, d  O* v6 R$ J  R% v5% i3 K6 I( s& M0 q( E
    6
    9 w. Y4 a" A& `* R& |& {参考答案:0 \. m5 l9 F% N6 Q# U# H

    6 _0 X% ]( M) F# Ss = df.index.to_series().reset_index(drop=True).diff().dt.total_seconds()
    ; [3 |; Z2 B+ I0 K  Nmax_3 = s.nlargest(3).index
    $ D+ c! z4 z6 z' Z( Kdf.index[max_3.union(max_3-1)]
    ) s5 E* y: s1 b/ b3 d7 _
    5 k9 Z2 c3 V* t. ~. AOut[215]: ( z$ D! v) E! V" l+ N
    DatetimeIndex(['2016-09-29 23:55:26', '2016-10-01 00:00:19',
    " y9 R: i( k& c* h               '2016-11-29 19:05:02', '2016-12-01 00:00:02',7 C# j* w) W3 V- X2 b/ r
                   '2016-12-05 20:45:53', '2016-12-08 11:10:42'],  S5 l- [8 V7 J" e: ?+ m/ {% T
                  dtype='datetime64[ns]', name='Datetime', freq=None)
    ( S* a& x1 t" d- ]* J9 p, w1
    + z' A# A2 i. W2 B6 J& X- ~, T2
    2 L2 y$ O% e$ {! R3
    : m. W! n# j# L' G7 i( |3 M+ z! v! W46 z  g" h# P' Q  I
    5
    # ^" S7 F5 x! Z, P8 x# z) B6
    # |* H5 U! h! V6 O$ q77 s- \8 ]2 t2 T# B
    8* M0 J3 R0 d% s6 u
    9
    & O: ^/ L0 |. W; C/ j+ Y是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50。
    * @  r9 u' m6 [7 d8 z6 L9 J: R; j# 将df的indexydiff做差,转为秒数后排序。再求几个分位数确定取值区间6 B7 y' @% x$ Q9 x% p- a- R; `( J9 j, }
    s=pd.Series(df.index).diff(1).dt.total_seconds().sort_values(ascending=False)
    ; w& |* m: F4 ?s.quantile(0.9),s.quantile(0.95),s.quantile(0.99),s.quantile(0.01),s.quantile(0.03),s.quantile(0.05)/ Q, I6 p/ \' V2 `

      g/ c0 [' n, U1 Z% f2 N9 u/ L3 T(304.0, 309.0, 337.15999999999985, 285.0, 290.0, 292.0)
      I& j, r$ h* u! @6 X1 k3 ]4 ^1* a& d( O0 m0 b/ I* r" U( O
    2
    / Z& J' f$ u% v38 `  a# X! ^4 w- H
    4
    0 m8 }- r# R5 t: F7 T5) R6 `/ \; T7 v7 H% b  v/ R0 G
    %pylab inline
    " O6 G" B' J: c2 t_ = plt.hist(ss[(s.values<337)&(s.values>285)],bins=50)
    6 A+ F% h/ |0 H$ s( E9 \% L6 Nplt.xlabel(' Timedelta'), ^" a( S& [. T/ Y; Y$ D9 I
    plt.title(" Timedelta of solar")
    6 H! @9 i* M3 ^; a1 P( k1' b' A, \3 t, H9 ^. V% P
    22 ~2 |( Y3 W; D7 u+ r
    3$ l7 C+ p( C1 `3 V! P
    4
    - N: I/ q' S( d- w$ ?3 A; ?9 v5 O5 R! T0 O/ {3 S

    - p7 }1 C" d/ k2 P+ t2 T求如下指标对应的Series:
    ) K: d7 V! Y( C6 P+ E( i4 j温度与辐射量的6小时滑动相关系数2 t% |  ^7 D, ~% k2 k( a
    以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列/ h  X, r- W" A  P4 d6 X
    每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)
    8 d0 o$ ]: H. z: `  G9 A+ `df.Radiation.rolling('6H').corr(df.Temperature).tail()
    9 M" [. }& D) m3 [  ^+ H  _0 @/ p6 \; v# v! S# h* a1 G
    Data3 X6 L- N+ b0 J
    2016-12-31 23:35:02    0.416187
    , t9 M6 q% p9 p7 R7 G2016-12-31 23:40:01    0.416565: _+ @0 Z3 r  a  U7 f
    2016-12-31 23:45:04    0.328574: p6 y3 |7 {. |1 G6 x. T: q
    2016-12-31 23:50:03    0.261883/ Z# w0 w, f/ q$ e' I& N' P
    2016-12-31 23:55:01    0.262406* `& o1 K% \; m
    dtype: float64
    / u) q2 K7 `& O7 W* a+ D1. l/ C$ L" @' q1 X' R# @! c6 s
    2  S3 [$ ?! R  h; R' ~
    3
    : A. W7 L! W9 `8 @4
    7 U! M: u6 H8 C0 J2 p- f7 J' m1 N5; P* R; y6 R& W9 O/ M
    6; ^7 @1 u- {4 I) Q. y  y1 A
    7
    $ P+ b  D5 E: r+ p. p8
    % c2 w/ x. h# {9 Q- r, F5 d% E9
    , \7 O# x/ F$ W& D# Jdf['Temperature'].resample('6H',offset='3H').mean().head()
    4 A5 N2 |; O. K5 X8 {! r4 C' J! @! r2 R. Q9 t6 d2 S# u) ]1 k
    Data, a# m! e5 Q7 d, {) g% V  W
    2016-08-31 21:00:00    51.218750: _0 [( e6 D3 g) u+ _( S; i
    2016-09-01 03:00:00    50.0333333 Y. |8 {9 O3 E' Y# Z: q' v, ~
    2016-09-01 09:00:00    59.379310
    % [7 T" H0 ]9 X2016-09-01 15:00:00    57.984375: Y) s) y* q2 r3 [7 }& r3 o( J* y
    2016-09-01 21:00:00    51.393939' l: Y# I) M* _' E
    Freq: 6H, Name: Temperature, dtype: float64
    ; H4 t0 [" c5 p% a- ^* P1  w3 L$ D6 r/ T- K6 `0 @4 v
    2
    * ^$ g, @4 B- Y7 T3
    9 Q" j  H, h. p) G0 v/ H4
    $ ?6 E& ~3 t8 K' E- ?5
    $ B0 t( [0 ]. ]; u9 }) |$ y* B/ P6
    ! \* ?7 W; t0 d  e7
    ) i! i6 ]2 N; ?! o! h8
    8 V$ T0 x$ N# n' B92 U* a' j1 N4 ~3 a1 U7 h3 w
    最后一题参考答案:, C$ C) B0 Q% q+ b6 ^

    1 n9 Z! {: Y( K# 非常慢
    * y+ j! X1 k# M( d& mmy_dt = df.index.shift(freq='-6H')# p/ P% M! w$ M5 Q5 ?6 u* F/ |
    int_loc = [df.index.get_indexer([i], method='nearest') for i in my_dt]! c6 Q$ M# J  m' T  R. ?0 C
    int_loc = np.array(int_loc).reshape(-1), x. U! _" @* e- z# t8 A
    res = df.Radiation.iloc[int_loc]* \$ W( Q: k5 O1 q- C. N3 h3 W
    res.index = df.index
    # S5 t) l. r& N) `/ ?. ires.tail(3)2 R0 f' r% q0 w) b
    1
    * l) D: v+ t0 p1 o/ N9 E' l2. y# {1 w8 [& |
    36 Y6 o( t9 W" [/ o4 m
    4
    2 M5 J8 Q  \  ^% l* {8 s* ^5
    - l0 f) e4 a. [% G6
    4 |! _6 p! a: k- M7
    ; g" v  S# _+ o/ R5 b; ~9 v1 G; m# 纸质版上介绍了merge_asof,性能差距可以达到3-4个数量级
    + L- n9 u+ d% C' Itarget = pd.DataFrame(; Y! E9 _; `: B8 y& s7 r
        {
    5 t- ^4 L$ X. J: b2 C        "Time": df.index.shift(freq='-6H'),
    ) M7 x. l# Q, ?' U) u& x8 z        "Datetime": df.index,
    8 m: c1 v/ a4 o& h0 e    }
    ) U0 T! }6 h* k. Y)
    . J. \: \& p8 p( F& \8 Q
    - u, g# o+ D0 {3 F5 m' mres = pd.merge_asof(& x7 x9 S* K4 r
        target,- a* E; d& R( ~6 a2 P1 [; @1 [% g
        df.reset_index().rename(columns={"Datetime": "Time"}),5 `" t& A# }( L- t% _' H1 i$ T& w
        left_on="Time",) E" i" l: a& @: Q! T5 j! J( h
        right_on="Time",
    " b0 l! Y0 f( F& {+ O: e    direction="nearest"
    3 y5 D& F1 q) O& t. c).set_index("Datetime").Radiation
    * P; I3 ~2 I5 {, b- H+ V* O+ H4 c' G
    res.tail(3)
    ) Y$ g+ e* O  L8 ROut[224]: . r6 ?% m5 r' U" ~
    Datetime
    " {4 V: l7 E5 N! ~5 j/ P2016-12-31 23:45:04    9.33
    ! X5 B( A9 F. K; m3 V3 G( D2016-12-31 23:50:03    8.49
    1 \8 @/ ~8 G0 I6 x/ b$ u; H2016-12-31 23:55:01    5.84& q5 l" M' k6 V
    Name: Radiation, dtype: float64
    4 ]7 h! T; u! p: \6 g3 V0 }% x& X" B6 [
    1
    9 Z* r& }- T" A; U1 j) e29 x2 q9 A0 X* d
    3
    5 V6 N% q2 e7 ^. s5 o% b4
    5 d( r$ E0 Y6 Z; f5  I; @, @- I( H  t' X
    6
    . {( ~4 V& G; M7
    " b- R, P6 x! L1 }! [8; v/ f- _0 N, u8 v6 r
    9) x1 q# q7 S% L4 u4 s
    10
    ; h+ O1 q' J# I" a+ Y4 ?; G: Y/ J11
    ) y) {! B2 Y& [) r; o& _120 s; T0 h8 i, c3 T: O: K& W& q
    13
    & _, J7 h/ m3 A& j+ C14+ r2 ~6 ^! S( g5 V7 v
    15
    : U- p6 J3 ~& B2 M16$ d5 }: u/ S0 i" R, D
    17
    4 \( y1 Y5 I8 o3 r; B% T18
    ! t0 v+ Z* C) H0 c. a+ U19
    7 n- W. N3 ?7 w4 m9 N20
    . N7 |7 b5 l& q21
    2 U3 l+ z# G. ^2 w' K22; ^8 f/ O1 C4 h2 {: m
    23( o+ T' Y6 [9 Z" i7 Z
    Ex2:水果销量数据集
    * E# U" o- L. s# k* \! q7 P现有一份2019年每日水果销量记录表:
    . s' I$ h; h4 z0 P
    ' E& j& l9 t0 N# Ldf = pd.read_csv('../data/fruit.csv')
    $ u! g2 Y- }$ Kdf.head(3)7 [; C2 d0 b8 k2 d

    . U  i2 a  F5 Q. EOut[131]: # M% a4 s6 j: H: `6 q! f' b* o9 B
             Date  Fruit  Sale# k3 n5 U2 M5 z: k
    0  2019-04-18  Peach    15- B( j) J: `/ `1 T, |6 B1 K
    1  2019-12-29  Peach    15" Z4 M2 u) ?% S' P$ E
    2  2019-06-05  Peach    19$ M, r3 T1 C6 E
    1
    ( b% ~$ \& T  |5 I7 W& y9 {24 E7 {5 h$ d- t, ?' `% s
    3
    * O+ d/ q% L, r; Y4
    0 n  N9 Z5 O7 p& ]0 D5 t5
    ! Y) y' I! S, l3 ?65 O; i4 @) u& G( ^. n
    78 `# R5 _' c1 |/ }2 a
    8
    3 N5 @! p% o( H7 E- K% J5 N统计如下指标:. O$ d$ w$ b: s% y3 o' _. d1 f
    每月上半月(15号及之前)与下半月葡萄销量的比值
    1 y9 a8 D: U& z& d/ [每月最后一天的生梨销量总和* W6 p7 n. f$ `. d5 b. ~; D
    每月最后一天工作日的生梨销量总和
    0 W5 K+ S, ?" m" P1 v* o. \每月最后五天的苹果销量均值
    - b: @' T6 k8 ]+ G5 i& Q; V# w按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。
    ; y1 D4 q' |( z! A* E0 l按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。
    1 t: P# \* a* [4 ~; Pimport numpy as np. M& F; x6 Q, U0 k8 u( O& K
    import pandas as pd/ |) G9 r% M5 P6 w/ o! _
    1
    % b6 A6 ~9 n: G2% {7 ~( G3 P+ y
    统计如下指标:
    + X! V5 t# q2 @! F& T每月上半月(15号及之前)与下半月葡萄销量的比值* L5 C/ j8 B8 o- r
    每月最后一天的生梨销量总和' j" M5 Q9 o& A( Q" X
    每月最后一天工作日的生梨销量总和
    8 m+ S4 ^: J+ e; h  [" e5 ^每月最后五天的苹果销量均值
    " F8 \" v! `9 F$ I# 每月上半月(15号及之前)与下半月葡萄销量的比值
    * t+ d. M7 P2 F! E; |df.Date=pd.to_datetime(df.Date)
      I2 ]1 X' `7 ]) x. I9 Vsale=df.query('Fruit == "Grape"').groupby([df.Date.dt.month,df.Date.dt.day<=15])['Sale'].sum()
    " {1 S0 V" t/ r$ T9 rsale.columns=['Month','15Dayes','Sale'] # 为啥这么改没用啊
    0 ^$ k9 G4 c# C5 H3 ]sale=pd.DataFrame(sale)
    4 V) B/ |: y- V* @% \- @; d+ B3 Osale=sale.unstack(1).rename_axis(index={'Date':'Month'},1 E# q4 O8 W, O0 N9 a- L0 D3 u/ t& U( f
                     columns={'Date':'15Days'}).stack(1).reset_index() # unstack主要是两个索引都是Date无法直接重命名
    8 _, y  ]3 E4 Z: qsale.head() # 每个月上下半月的销量
    " v, v' g2 a3 E' R8 w
    ! P( I$ `" b% X4 Q* l8 ^+ L8 u  Month        15Days        Sale# h/ ?1 x  B% o9 k2 R
    0        1        False        105031 [6 Z0 q2 a) S: n; t
    1        1        True        123413 W( f" z4 t2 K; h1 C
    2        2        False        10001
    : e! L1 M2 W! t1 v3        2        True        10106
    # l: C+ P3 a! ~+ f& S4        3        False        12814
    4 h. K- z! C2 r! G( ^1 }* k( }4 J1 b
    # 使用自定义聚合函数,分组后每组就上半月和下半月两个值,根据索引位置判断求比值时的分子分母顺序$ [  X, M4 K, b3 G& d9 Y
    sale.groupby(sale['Month'])['Sale'].agg(
    0 G7 H4 t: d2 ?3 ?' H3 Y                lambda x: x.max()/x.min() if x.idxmax()>x.idxmin()  else x.min()/x.max())) ~! W6 \; h, A6 E  n
    . x5 i4 `# W! a! @' \, c2 `
    Month
    # q* C, T, G+ o( E) ]# a& ~1     1.174998+ V4 G! U' u6 B9 _- r
    2     1.010499
    9 F$ [1 N3 d5 @; X/ `1 c0 l$ Z) @3     0.776338
    4 h; Z$ P; x' Q& \4     1.0263458 ^9 A! ~# S  d$ f, }7 k
    5     0.900534. g0 L- I% U. J" o$ m# L  ^5 ^
    6     0.980136
    - y0 P" W1 @6 K/ {, g$ f7 O7     1.350960
    ; {, S+ \; ?' r7 F' q1 Y$ A9 W7 u" d3 v8     1.091584: Q' P6 K5 a) x: @. e
    9     1.116508, E" J9 f$ C/ J9 o- {$ l; x
    10    1.0207848 v) Z5 A+ Y7 Z/ U+ a$ L  e
    11    1.2759111 a; w1 c( s! o; ]: f- L* u
    12    0.9896622 x- i4 A+ m9 c6 O# |
    Name: Sale, dtype: float64
    ( b" M4 L, d. U
    ' F" O" _0 p$ D1
    : \: ^4 q- t2 J% y  |  ^+ L9 |" C2% U) q2 q# [1 M' }: [3 H$ K
    34 J4 v* O% a- u  p5 ?$ ^# [5 d
    44 w9 E* W' F, \& a
    5$ D; N' l  i* J  y& |: u
    6; h- _6 s) L- o! Q$ C: T! ]
    74 f  O, |, q6 p, h
    8/ U# b+ L# F2 S- B& g# {( }4 b/ Y2 Z
    95 _" T/ `5 v1 q# ^) t& W! }
    10% N  W, z' O5 h* ^& G6 T3 F: P
    11" C0 ^) l5 b* e9 [% v  O2 c
    12
    2 h5 Z" _& p! f9 V- F& u5 l13
    9 N0 @9 @$ w7 t: q7 B, c14
    # i9 J/ g0 i- g7 y, ~15+ s: V, N; v3 W% x
    16
      s  |. ?- U* F1 r17
    4 G+ ~  r+ p7 ^5 n5 N* e$ O8 j18
    / A. v8 a4 X9 U# m' t  c193 X' a" {* I" s+ d8 o- m9 ]
    20) d5 ?' A- A$ b" h8 V# b
    21
    & O! d/ @8 o5 ^. s: q22
    , {# s4 y+ v& n4 R% c) c23
    0 I- O  H" n9 {24
    8 N; X& b7 l5 r: H. z) ]+ h25
    " J( V4 E  P# N$ ?26
    # Y1 G) W# W8 Q. o$ Q) l27
    " m; j. P: W/ P( m5 @  a5 {28) l7 R! n: A4 b$ q
    299 V) p4 q0 ^, E6 M
    30
    # a5 O7 i: R+ ?: B, T( m) t- J8 C  z31! I: \* ~8 @# D. D
    32
    0 D. s; B8 L5 u8 ~, @2 Z; H# }& s33
    / ]+ H+ W7 [' x348 l+ I8 N6 }# r8 q9 k  j3 |
    # 每月最后一天的生梨销量总和+ m! s: ~' ^% S3 b1 e
    df[df.Date.dt.is_month_end].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()& U$ I7 _" A* h* E# P
    , j& I. O" j$ G4 h
    Date
    ) K2 c% s- W- w# e0 [2019-01-31    847
    , H  F* f: c) k2019-02-28    774- f# ~# d, _- V% X
    2019-03-31    761" v$ O& f1 W- X3 C9 u% E. ]# Q) M
    2019-04-30    648
    ; A* j  Z' F9 l  h7 _2019-05-31    616
    5 w& ]& t' K, J% R6 m4 y8 W1
    ) b: l* {# J3 ?7 k22 P* L1 {# r, v; K
    3
    7 j* I3 ~5 F. J! p+ M4, ^/ |0 I, J8 O! L* q
    5
    6 }) A4 ~# r/ i+ x0 j( E5 |6 U6
    ' {: ]# O( T, W+ |/ I0 l, ]79 s% p7 Y: F7 c. @4 L
    8
    + R3 O6 W1 y, E3 y: f9
    + N+ n6 T# m# \4 _# 每月最后一天工作日的生梨销量总和
    5 D& K0 C' R" i" Q1 z2 u# Uls=df.Date+pd.offsets.BMonthEnd()9 A$ n& P, G8 ]
    my_filter=pd.to_datetime(ls.unique())- k# }% [7 @8 u" {0 V
    df[df.Date.isin(my_filter)].loc[df.Fruit=='Pear'].groupby('Date')['Sale'].sum()
    * a3 V2 m4 K8 K3 r& t+ u4 y
    1 ^$ U* G5 V) x) i: ^! [Date' `/ R5 I, J3 K
    2019-01-31     847
    ) o+ b3 b3 Y+ B% p0 D, j2019-02-28     774$ P1 U5 t- K" D$ A& n/ @
    2019-03-29     510
    0 E& `4 ?* L9 ?9 t# A" k" _2 `; @7 N# z2019-04-30     648
    : v! ?$ U7 \* C" O' K/ U. w# W2019-05-31     616+ p# @  N( k. k) {. s# R
    12 j6 ~4 P  [" s) S8 K. Z
    2
    6 v1 F$ ~( l3 }3 H- F3$ \* s# ]) y( z4 p- ~
    4+ l' }8 Q$ M$ z! A5 \
    5
    ( X( O. i. H- n- V6# F4 F* M' M6 J6 r) C1 ^# V/ X1 [+ x
    75 Q4 E# K( u" p. V" {! H# @% G
    88 l& D( K. _! k+ v5 t# n7 Y$ Y
    9
    7 I3 u) `$ `( E7 o10% M: m& y7 W; v( m
    11
    9 H; ^2 A8 Z! U  |# 每月最后五天的苹果销量均值
    , r% ?- }5 ?5 [0 K' W. Hstart, end = '2019-01-01', '2019-12-31'
    & J1 A6 u  l# Iend = pd.date_range(start, end, freq='M')
    # C- ]$ v0 t# p& V. I" iend=end.repeat(5) # 每月最后一天的日期列表,重复5次方便做差5 Q* |/ i$ q' H6 ?

    ) ~+ ]8 n# p( o- {. x) C4 Ptd= pd.Series(pd.timedelta_range(start='0 days', periods=5),)
    5 f$ o( k4 C: w9 Ctd=pd.concat([td]*12) # 日期偏置,最后一天减去0-4天
    2 l* B7 q; H7 o' Y7 p+ pend5=(end-td).reset_index(drop=True) # 每个月最后5天的列表5 ~& ~3 U, k+ x. z" D
    ; }; B4 S/ s6 f% D! s8 |
    apple5=df[df.Date.isin(end5)].query("Fruit == 'Apple'") # 每月最后五天苹果销量$ e2 O# R' L* H; w  T
    apple5.groupby(apple5.Date.dt.month)['Sale'].mean().head(): `7 Y$ V# n; |8 e4 `
    0 ~7 f( g" P# L, `
    Date
    $ {9 a3 a* M! ]& }1     65.313725+ |  m- y6 K/ k8 S
    2     54.061538
    + I: V3 r2 I! G5 `3     59.325581
    3 O* w* j" Z; o! I( W4 o4     65.795455
    9 v$ l1 x. h8 G& N7 q5     57.465116; H$ C3 h6 s) l* Q8 B

    - k9 J# K# [/ z) k& J6 u8 h1
    " T" }! c% J- M3 I1 }. C( l2/ w, _. \$ N* U* P  _& @
    3
    : N9 Q7 H, b$ ~1 B* u4! N; ?- \) R. e7 M2 }' R
    5
    ! X# j* M( S9 ~* M) P4 O6
    $ y7 }+ ?$ y- P7
    5 }) w+ c* w8 g8) }$ u. V" I! h2 H" H3 C* q/ Z. @
    9
    6 A, a& J/ r$ n, g109 ~) _8 Q1 V  u3 F* q' a
    11' w* B. n9 ~4 E7 ?) A1 [- I
    12) r) K+ b* U0 k! t1 c
    13
    , k% p9 G) ?3 A2 D+ O14+ g- r8 b3 h- r7 ~) @1 ^9 n
    15
    & q1 L- x# v; k3 A, c1 U16
    8 u6 y4 t: g/ Q1 [9 z; j; s6 V17; X( U! C6 \  W1 X- \& `
    18
      [7 Z1 [# O; S% C# 参考答案:5 }# g  Z' B! ^9 U3 e# r7 ~8 o
    target_dt = df.drop_duplicates().groupby(df.Date.drop_duplicates(
    . |; [0 D* `7 _0 i* [5 B" v            ).dt.month)['Date'].nlargest(5).reset_index(drop=True)
    ( V6 ~7 ~& c9 K. C
    # J( D) T& B7 s4 W& G9 L4 |9 Gres = df.set_index('Date').loc[target_dt].reset_index(# {" X  Y2 v" P- R  _
                ).query("Fruit == 'Apple'")9 W5 w. I0 g& P

    5 g( j8 g6 Z0 u/ Q# lres = res.groupby(res.Date.dt.month)['Sale'].mean(& s3 [, D8 L) {7 j7 F0 J
                ).rename_axis('Month'): w( O: j9 M4 u

    4 K$ g! o0 d0 w. H( h1 Y( Y. a. G: T2 e. B! C7 P7 \
    res.head()
    , [; p7 s) m$ {- _9 SOut[236]:
      T) J1 D8 V6 o/ V: QMonth  v2 u% F8 G6 R$ ?! |' e) a2 j
    1    65.3137251 e: o/ b7 H$ [+ \3 \
    2    54.061538
    6 h) e1 p! a7 X3    59.325581  C) T: E# u: ~) z. y5 N
    4    65.795455- i9 G6 d9 I3 q
    5    57.465116. k" \+ y3 P4 p: W
    Name: Sale, dtype: float649 h6 y5 f0 D% N1 e- W5 [2 q; @1 }' o
    + T: q1 H: Z9 W
    1  z# N! Y; c5 E( N( T( _  V
    2
    ) X5 q3 x0 O* w30 R$ g2 Y$ C% U% s' }9 v
    4; ~9 z# |8 _6 u. p* ?
    5% ^# {- i8 p: ?: j0 u. j
    6
    # Y7 g3 r+ c% e, a) [9 ?. O" T- ~7
    2 O; c6 |" G9 [* s8 j  I8
    0 f9 l7 u0 \' C8 U, ?9
    6 q5 L; q5 l* _  `1 |+ s3 o10
    1 f0 m# Z2 o# x' d11
    + D' X) s7 {+ ~5 f9 R' J12; ^" ]/ y& _: J- p% V+ D' L
    13
    ; ~' d* M* V( G! H& k' W14# M" q# V" S4 }. i7 J' E
    15
    $ `6 B, ^# A' P165 D1 r2 A6 l# J, o/ u" v  s" i7 o% Z
    17
    + E8 \# B) U5 `, J3 ^- T4 \' n5 h5 e18) Y  q4 P% H1 A1 B
    19
    % z* w1 g8 \! E2 P20
    ) e1 W& m+ H1 ~, G按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。
    ; Q( |. i6 z$ Z9 L/ ]/ I: y' M1 oresult=pd.DataFrame(df.groupby([df.Date.dt.month,df.Date.
    2 m8 Y9 `& p% D' ]+ E6 P                                        dt.dayofweek,df.Fruit])['Sale'].count()) # 分组统计
    " [8 f' i- q* v1 _                                       
    6 W' y+ v+ P3 \: ?: \result=result.unstack(1).rename_axis(index={'Date':'Month'},
    : m# {& ^  I1 q9 D5 E/ s( n                 columns={'Date':'Week'})  # 两个index名字都是Date,只能转一个到列,分开来改名字., @2 L6 z, w6 X0 V
    result=result.swaplevel(0,1,axis=0).droplevel(0,axis=1)8 b3 N0 f7 W5 n8 j$ q' @, t( T
    result.head() # 索引名有空再改吧9 I7 T7 t. Q6 D8 h+ O. S& Z$ f0 g
    ' _5 ~2 i/ w$ M: b
              Week        0        1        2        3        4        5        6
    7 ?7 k4 }; \; _8 z: p  ~Fruit Month                                                       
    , k7 E) l1 u9 w/ s8 KApple        1        46        50        50        45        32        42        23
    / i4 c3 c; m* o; Y. A3 {Banana        1        27        29        24        42        36        24        35
    1 t% {! W# I! u6 k4 bGrape        1        42        75        53        63        36        57        46/ `  t- R; z5 [/ w: z# n. C  @
    Peach        1        67        78        73        88        59        49        72
    1 y$ q; x, t; o7 OPear        1        39        69        51        54        48        36        40
    7 S! |2 k' a) a$ G# W14 O3 A) G1 o2 s* S* ?* D0 x: ]4 p
    24 H/ }: }5 M  z! R  }1 `% b
    3- _9 v9 _7 Z9 Q) R( |
    4
    9 g& g' h$ E' V$ f% @) \2 K, Q59 @' k9 b# k9 u5 |2 v
    6
    + @9 a3 l4 j- v: f# V- ~73 p! h; `+ d, ?2 @. `8 `/ h: R
    8
    6 ^8 i- N/ O8 h' n4 p# A9) a/ ^) @. A2 }7 i1 V) b
    10
    8 a0 B( M) Y/ r1 O) A5 ?6 m11, l4 B8 T4 ?4 q5 }3 a" \
    12
      [# j. d6 K- V, Z% p" f3 I139 m4 d. w5 p( e. p
    143 t' H# ]' K3 K3 j& A
    15; o1 |- r2 e* H4 j, n
    按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。
    + ]# Q6 R& ~' n# ?: m* v  t# 工作日苹果销量按日期排序
    $ ^' R/ H  e* w$ n+ nselect_bday=df[~df.Date.dt.dayofweek.isin([5,6])].query('Fruit=="Apple"').set_index('Date').sort_index(). H0 v/ F/ P! x3 y/ O- U& X
    select_bday=select_bday.groupby(select_bday.index)['Sale'].sum() # 每天的销量汇总+ D5 I. _+ t# R5 x% i- V
    select_bday.head()$ X. }+ V5 l7 ?- _1 t

    $ M9 M' o- Y1 F- p$ c! RDate1 ~6 T1 g1 G$ ~4 G4 r
    2019-01-01    189
    1 l; h4 s* }, ^6 q% V# z2019-01-02    482+ H( z6 y; b! x, f7 |
    2019-01-03    890; w% z" D0 V# [2 y4 u6 Z
    2019-01-04    550! z9 }& k8 |7 O
    2019-01-07    494
    1 @% {. F1 \! f6 K4 y9 w
    4 Y' a. _% K8 Q6 t6 a$ d# 此时已经是工作日,正常滑窗。结果重设索引,对周末进行向后填充。
    . c) Z+ p+ y( c3 m4 X- k! Iselect_bday.rolling('10D').mean().reindex(df.Date.unique()).sort_index().ffill().head()" i7 _0 g7 D8 O, ?
    - V8 o4 ?/ z, `  y$ Z9 u4 c
    Date  A! R. q( K* e; ]* p- N8 B
    2019-01-01    189.000000
    ! \1 L! L  U) Q! E; E2019-01-02    335.500000
    $ z) Z0 P( a' v6 y0 e: [2019-01-03    520.333333
    ) P, h3 q" {9 [2019-01-04    527.750000
    " \, q: ^6 }- R0 P9 V9 ~2019-01-05    527.750000
    - u& X( [& e& U5 V2 i4 t! S: V3 E+ \( w9 B
    ————————————————7 _0 S7 e+ x0 y' t  d' Z
    版权声明:本文为CSDN博主「神洛华」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    / H8 i, @5 A4 f  N6 f* {! j. I原文链接:https://blog.csdn.net/qq_56591814/article/details/126633913" x4 r8 [4 l& X, T
    ; W- ]. U& y1 Y7 O

    + S  G. j/ @5 ]5 f7 t) D
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
    您需要登录后才可以回帖 登录 | 注册地址

    qq
    收缩
    • 电话咨询

    • 04714969085
    fastpost

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

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

    蒙公网安备 15010502000194号

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

    GMT+8, 2025-8-14 09:16 , Processed in 0.709968 second(s), 50 queries .

    回顶部