Python小白数据科学教程:Pandas (下)

栏目: Python · 发布时间: 5年前

内容简介:福利干货,第一时间送达!

点击 “简说Python” ,选择 “置顶/星标公众号”

福利干货,第一时间送达!

Python小白数据科学教程:Pandas (下)

本文转载自公众号 | 王的机器

作者 | 王圣元

全文共 14270 字,73 幅图或表,

预计阅读时间 36 分钟。

0

引言

Python数据分析必学模块Pandas,接着上篇继续后面三个章节。

  • 数据表的合并和连接

  • 数据表的重塑和透视

  • 数据表的分组和整合

Python小白数据科学教程:Pandas (下)

4

数据表的合并和连接

数据表可以按「键」合并,用 merge 函数;可以按「轴」来连接,用 concat 函数。

4.1

合并

按键 (key) 合并可以分「单键合并」和「多键合并」。

单键合并

单键合并用 merge 函数,语法如下:

pd. merge ( df1, df2, how=s, on=c )

c 是 df1 和 df2 共有的一栏,合并方式 (how=s) 有四种:

  1. 左连接 (left join):合并之后显示 df1 的所有行

  2. 右连接 (right join):合并之后显示 df2 的所有行

  3. 外连接 (outer join):合并 df1 和 df2 共有的所有行

  4. 内连接 (inner join):合并所有行 (默认情况)

首先创建两个 DataFrame:

  • df_price :4 天的价格 (2019-01-01 到 2019-01-04)

  • df_volume :5 天的交易量  (2019-01-02 到 2019-01-06)


  

df_price = pd.DataFrame( {'Date': pd.date_range('2019-1-1', periods=4),

'Adj Close': [24.42, 25.00, 25.25, 25.64]})

df_price

Python小白数据科学教程:Pandas (下)


  

df_volume = pd.DataFrame( {'Date': pd.date_range('2019-1-2', periods=5),

'Volume' : [56081400, 99455500, 83028700, 100234000, 73829000]})

df_volume

Python小白数据科学教程:Pandas (下)

接下来用 df_price df_volume  展示四种合并。

left join

pd.merge( df_price, df_volume, how='left' )

Python小白数据科学教程:Pandas (下)

df_price 里 Date 栏里的值来合并数据

  • df_volume 里 Date 栏里没有 2019-01-01,因此 Volume 为 NaN

  • df_volume 里 Date 栏里的 2019-01-05 和 2019-01-06 不在 df_price 里 Date 栏,因此丢弃

right join

pd.merge( df_price, df_volume, how='right' )

Python小白数据科学教程:Pandas (下)

df_volume  里 Date 栏里的值来合并数据

  • df_price  里 Date 栏里没有 2019-01-05 和 2019-01-06,因此 Adj Close 为 NaN

  • df_price  里 Date 栏里的 2019-01-01 不在  df_volume  里 Date 栏,因此丢弃

outer join

pd.merge( df_price, df_volume, how='outer' )

Python小白数据科学教程:Pandas (下)

df_price   df_volume  里 Date 栏里的 所有 来合并数据

  • df_price  里 Date 栏里没有 2019-01-05 和 2019-01-06,因此 Adj Close 为 NaN

  • df_volume  里 Date 栏里没有 2019-01-01,因此 Volume 为 NaN

inner join

pd.merge( df_price, df_volume, how='inner' )

Python小白数据科学教程:Pandas (下)

df_price    df_volume  里 Date 栏里的 共有值 来合并数据

  • df_price  里 Date 栏里的 2019-01-01 不在  df_volume  里 Date 栏,因此丢弃

  • df_volume  里 Date 栏里的 2019-01-05 和 2019-01-06 不在  df_price  里 Date 栏,因此丢弃

多键合并

多键合并用的语法和单键合并一样,只不过 on=c 中的 c 是多栏。

pd. merge ( df1, df2, how=s, on=c )

首先创建两个 DataFrame:

  • portfolio1:3 比产品 FX Option, FX Swap 和 IR Option 的数量

  • portfolio2:4 比产品 FX Option (重复名称), FX Swap 和 IR Swap 的数量


  

porfolio1 = pd.DataFrame({'Asset': ['FX', 'FX', 'IR'],

'Instrument': ['Option', 'Swap', 'Option'],

'Number': [1, 2, 3]})

porfolio1

Python小白数据科学教程:Pandas (下)


  

porfolio2 = pd.DataFrame({'Asset': ['FX', 'FX', 'FX', 'IR'],

'Instrument': ['Option', 'Option', 'Swap', 'Swap'],

'Number': [4, 5, 6, 7]})

porfolio2

Python小白数据科学教程:Pandas (下)

'Asset''Instrument' 两个键上做外合并。


 

pd.merge( porfolio1, porfolio2,

on=['Asset','Instrument'],

how='outer')

Python小白数据科学教程:Pandas (下)

df1 和 df2 中两个键都有 FX Option 和 FX Swap,因此可以合并它们中 number 那栏。

  • df1 中有 IR Option 而 df2 中没有,因此 Number_y 栏下的值为 NaN

  • df2 中有 IR Swap 而 df1 中没有,因此 Number_x 栏下的值为 NaN

当 df1 和 df2 有两个相同的列 (Asset 和 Instrument) 时,单单只对一列 (Asset) 做合并产出的 DataFrame 会有另一列 (Instrument) 重复的名称。这时 merge 函数给重复的名称加个后缀 _x, _y 等等。


 

pd.merge( porfolio1, porfolio2,

on='Asset' )

Python小白数据科学教程:Pandas (下)

当没设定 merge 函数里参数 how 时,默认为 inner (内合并)。在 Asset 列下,df1 有 2 个 FX 和 1 个 IR,df2 有 3 个 FX 和 1 个 IR,内合并完有 8 行 (2×3+1×1)。

如果觉得后缀 _x, _y 没有什么具体含义时,可以设定 suffixes 来改后缀。比如 df1 和 df2 存储的是 portoflio1 和 portfolio2 的产品信息,那么将后缀该成 ‘1’ 和 ‘2’ 更贴切。


 

pd.merge( porfolio1, porfolio2,

on='Asset',

suffixes=('1','2'))

Python小白数据科学教程:Pandas (下)

4.2

连接

Numpy 数组可相互连接,用 np.concat ;同理,Series 也可相互连接,DataFrame 也可相互连接,用 pd.concat

连接 Series

concat 函数也可设定参数 axis,

  • axis = 0 (默认),沿着轴 0 (行) 连接,得到一个更长的 Series

  • axis = 1,沿着轴 1 (列) 连接,得到一个 DataFrame

被连接的 Series 它们的 index 可以重复 (overlapping),也可以不同。

overlapping index

先定义三个 Series,它们的 index 各不同。


 

s1 = pd.Series([0, 1], index=['a', 'b'])

s2 = pd.Series([2, 3, 4], index=['c', 'd', 'e'])

s3 = pd.Series([5, 6], index=['f', 'g'])

沿着「轴 0」连接得到一个更长的 Series。

pd.concat([s1, s2, s3])
a 0
b 1
c 2
d 3
e 4
f 5
g 6
dtype: int64

沿着「轴 1」连接得到一个 DataFrame。

pd.concat([s1, s2, s3], axis=1)

Python小白数据科学教程:Pandas (下)

non-overlapping index

将 s1 和 s3 沿「轴 0」连接来创建 s4,这样 s4 和 s1 的 index 是有重复的。


 

s4 = pd.concat([s1, s3])

s4

a 0
b 1
f 5
g 6
dtype: int64

将 s1 和 s4 沿「轴 1」内连接 (即只连接它们共有 index 对应的值)

pd.concat([s1, s4], axis=1, join='inner')

hierarchical index

最后还可以将 n 个 Series 沿「轴 0」连接起来,再赋予 3 个 keys 创建多层 Series。


 

pd.concat( [s1, s1, s3],

keys=['one','two','three'])

one a 0
b 1
two a 0
b 1
three f 5
g 6
dtype: int64

连接 DataFrame

连接 DataFrame 的逻辑和连接 Series 的一模一样。

沿着行连接 (axis = 0)

先创建两个 DataFrame,df1 和 df2。


 

df1 = pd.DataFrame( np.arange(12).reshape(3,4),

columns=['a','b','c','d'])

df1

Python小白数据科学教程:Pandas (下)


 

df2 = pd.DataFrame( np.arange(6).reshape(2,3),

columns=['b','d','a'])

df2

Python小白数据科学教程:Pandas (下)

沿着行连接分两步

  1. 先把 df1 和 df2 列标签 补齐

  2. 再把 df1 和 df2 纵向 连起来

pd.concat( [df1, df2] )

Python小白数据科学教程:Pandas (下)

得到的 DataFrame 的 index = [0,1,2,0,1],有重复值。如果 index 不包含重要信息 (如上例),可以将 ignore_index 设置为 True,这样就得到默认的 index 值了。

pd.concat( [df1, df2], ignore_index=True )

Python小白数据科学教程:Pandas (下)

沿着列连接 (axis = 1)

先创建两个 DataFrame,df1 和 df2。


 

df1 = pd.DataFrame( np.arange(6).reshape(3,2),

index=['a','b','c'],

columns=['one','two'] )

df1

Python小白数据科学教程:Pandas (下)


 

df2 = pd.DataFrame( 5 + np.arange(4).reshape(2,2),

index=['a','c'],

columns=['three','four'])

df2

Python小白数据科学教程:Pandas (下)

沿着列连接分两步

  1. 先把 df1 和 df2 行标签 补齐

  2. 再把 df1 和 df2 横向 连起来

pd.concat( [df1, df2], axis=1 )

Python小白数据科学教程:Pandas (下)

5

数据表的重塑和透视

重塑 (reshape) 和透视 (pivot) 两个操作只改变数据表的布局 (layout):

  • 重塑用 stackunstack 函数 (互为逆转操作)

  • 透视用 pivot 和  melt 函数 (互为逆转操作)

5.1

重塑

在〖 数据结构之 Pandas (上) 〗提到过,DataFrame 和「多层索引的 Series」其实维度是一样,只是展示形式不同。而重塑就是通过改变数据表里面的「行索引」和「列索引」来改变展示形式。

  • 列索引  行索引 ,用  stack 函数

  • 行索引 列索引 ,用  unstack 函数

单层 DataFrame

创建 DataFrame df (1 层 行索引 ,1 层 列索引 )


 

symbol = ['JD', 'AAPL']

data = {'行业': ['电商', '科技'],

'价格': [25.95, 172.97],

'交易量': [27113291, 18913154]}

df = pd.DataFrame( data, index=symbol )

df.columns.name = '特征'

df.index.name = '代号'

df

Python小白数据科学教程:Pandas (下)

从上表中可知:

  • 行索引 = [JD, AAPL],名称是 代号

  • 列索引= [行业, 价格, 交易量],名称是 特征

stack:  列索引  →  行索引

列索引( 特征 ) 变成了 行索引 ,原来的 DataFrame df 变成了两层 Series (第一层索引是 代号 ,第二层索引是 特征 )。


 

c2i_Series = df.stack()

c2i_Series

代号 特征
JD 行业 电商
价格 25.95
交易量 27113291
AAPL 行业 科技
价格 172.97
交易量 18913154
dtype: object

思考:变成 行索引 的特征和原来 行索引 的代号之间的层次是怎么决定的?好像特征更靠内一点,代号更靠外一点。

unstack:  行索引 →  列索引

行索引   ( 代号 ) 变成了 列索引 ,原来的 DataFrame df 也变成了两层 Series (第一层索引是 特征 ,第二层索引是 代号 )。


 

i2c_Series = df.unstack()

i2c_Series

特征 代号
行业 JD 电商
AAPL 科技
价格 JD 25.95
AAPL 172.97
交易量 JD 27113291
AAPL 18913154
dtype: object

思考:变成 列索引 的特征和原来 列索引 的代号之间的层次是怎么决定的?这时好像代号更靠内一点,特征更靠外一点。

规律总结

对 df 做 stackunstack 都得到了「两层 Series」,但是索引的层次不同,那么在背后的规律是什么?首先我们先来看看两个「两层 Series」的 index 包含哪些信息 (以及 df 的 index 和 columns)。

df.index, df.columns
c2i_Series.index

Python小白数据科学教程:Pandas (下)

i2c_Series.index

Python小白数据科学教程:Pandas (下)

定义

  • r = [JD, AAPL],名称是 代号

  • c = [行业, 价格, 交易量],名称是 特征

那么

  • df 的 行索引 = r

  • df 的 列索引 = c

  • c2i_Series 的索引 = [ r c ]

  • i2c_Series 的索引 = [ c ,   r ]

现在可以总结规律:

  1. 当用 stack 将 df 变成 c2i_Series 时,df 的 列索引 c 加在其 行索引 r 后面得到 [ r c ] 做为 c2i_Series 的多层索引

  2. 当用 unstack 将 df 变成 i2c_Series 时,df 的 行索引 加在其 列索引 c 后面得到 [ c ,   r ] 做为  i2c_Series 的多层索引

基于层和名称来 unstack

对于多层索引的 Series, unstack 哪一层有两种方法来确定:

  1. 基于层 (level-based)

  2. 基于名称 (name-based)

拿 c2i_Series 举例 (读者也可以尝试 i2c_Series):

代号 特征
JD 行业 电商
价格 25.95
交易量 27113291
AAPL 行业 科技
价格 172.97
交易量 18913154
dtype: object

1. 基于层来  unstack() 时,没有填层数,默认为最后一层。

c2i_Series.unstack()

Python小白数据科学教程:Pandas (下)

c2i_Series 的最后一层 (看上面它的 MultiIndex) 就是 [行业, 价格, 交易量] ,从行索引转成列索引得到上面的 DataFrame。

2. 基于层来  unstack() 时,选择第一层 (参数放 0)

c2i_Series.unstack(0)

Python小白数据科学教程:Pandas (下)

c2i_Series 的第一层 (看上面它的 MultiIndex) 就是 [JD, AAPL] ,从行索引转成列索引得到上面的 DataFrame。

3. 基于名称来  unstack

c2i_Series.unstack('代号')

Python小白数据科学教程:Pandas (下)

c2i_Series 的代号层 (看上面它的 MultiIndex) 就是 [ JD, AAPL ] ,从行索引转成列索引得到上面的 DataFrame。

多层 DataFrame

创建 DataFrame df (2 层 行索引 ,1 层 列索引 )


 

data = [ ['电商', 101550, 176.92],

['电商', 175336, 25.95],

['金融', 60348, 41.79],

['金融', 36600, 196.00] ]


midx = pd.MultiIndex( levels=[['中国','美国'],

['BABA', 'JD', 'GS', 'MS']],

labels=[[0,0,1,1],[0,1,2,3]],

names = ['地区', '代号'])


mcol = pd.Index(['行业','雇员','价格'], name='特征')


df = pd.DataFrame( data, index=midx, columns=mcol )

df

Python小白数据科学教程:Pandas (下)

从上表中可知:

  • 行索引第一层 = r1 = [中国, 美国],名称是 地区

  • 行索引第二层   = r2 = [BABA, JD, GS, MS],名称是 代号

  • 列索引= c = [行业, 雇员, 价格],名称是 特征

查看 df 的 index 和 columns 的信息

df.index, df.columns

Python小白数据科学教程:Pandas (下)

那么

  • df 的 行索引 = [ r1 r2 ]

  • df 的 列索引 = c

1. 基于层来  unstack() 时,选择第一层 (参数放 0)

df.unstack(0)

Python小白数据科学教程:Pandas (下)

df 被 unstack(0) 之后变成 (行 → 列)

  • 行索引= r2

  • 列索引= [ c r1 ]

重塑后的 DataFrame 这时行索引只有一层 ( 代号 ),而列索引有两层,第一层是 特征 ,第二层是 地区

2. 基于层来  unstack() 时,选择第二层 (参数放 1)

df.unstack(1)

Python小白数据科学教程:Pandas (下)

df 被 unstack(1) 之后变成 (行 → 列)

  • 行索引=  r1

  • 列索引= [ c r2 ]

重塑后的 DataFrame 这时行索引只有一层 ( 地区 ),而列索引有两层,第一层是 地区 ,第二层是 代号

3. 基于层先  unstack(0)  再 stack(0)

df.unstack(0).stack(0)

Python小白数据科学教程:Pandas (下)

df 被 unstack(0) 之后变成 (行 → 列)

  • 行索引=  r2

  • 列索引= [ c r1 ]

再被 stack(0) 之后变成 (列 → 行)

  • 行索引= [ r2 , c ]

  • 列索引=  r1

重塑后的 DataFrame 这时行索引有两层,第一层是 代号 ,第二层是 特征 ,而列索引只有一层 ( )。

4.  基于层先  unstack(0)  再  stack(1)

df.unstack(0).stack(1)

Python小白数据科学教程:Pandas (下)

df 被 unstack(0) 之后变成 (行 → 列)

  • 行索引=  r2

  • 列索引= [ c r1 ]

再被 stack(1) 之后变成 (列 → 行)

  • 行索引= [ r2 r1 ]

  • 列索引=  c

重塑后的 DataFrame 这时行索引有两层,第一层是 代号 ,第二层是 ,而列索引只有一层 ( 特征 )。

5.  基于层先  unstack(1)  再  stack(0)

df.unstack(1).stack(0)

Python小白数据科学教程:Pandas (下)

df 被 unstack(1) 之后变成 (行 → 列)

  • 行索引=  r1

  • 列索引= [ c r2 ]

再被 stack(0) 之后变成 (列 → 行)

  • 行索引= [ r1 c ]

  • 列索引=  r2

重塑后的 DataFrame 这时行索引有两层,第一层是 ,第二层是 特征 ,而列索引只有一层 ( 代号 )。

6.  基于层先  unstack(1)  再  stack(1)

df.unstack(1).stack(1)

Python小白数据科学教程:Pandas (下)

df 被 unstack(1) 之后变成 (行 → 列)

  • 行索引=  r1

  • 列索引= [ c r2 ]

再被 stack(1) 之后变成 (列 → 行)

  • 行索引= [ r1 r2 ]

  • 列索引=  c

重塑后的 DataFrame 这时行索引有两层,第一层是 ,第二层是 特征 ,而列索引只有一层 ( 代号 )。 还原成原来的 df 了

7.  基于层被  stack() ,没有填层数,默认为最后一层。

df.stack()
地区 代号 特征
中国 BABA 行业 电商
雇员 101550
价格 176.92
JD 行业 电商
雇员 175336
...
美国 GS 雇员 60348
价格 41.79
MS 行业 金融
雇员 36600
价格 196
Length: 12, dtype: object

df 被 stack() 之后变成 (列 → 行)

  • 行索引= [ r1 r2 c ]

  • 列索引= []

重塑后的 Series 只有行索引,有三层,第一层是 ,第二层是 代号 ,第三层是 特征

8.  基于层被  uns tack()   两次,没有填层数,默认为最后一层。

df.unstack().unstack()
特征 代号 地区
行业 BABA 中国 电商
美国 NaN
JD 中国 电商
美国 NaN
GS 中国 NaN
...
价格 JD 美国 NaN
GS 中国 NaN
美国 41.79
MS 中国 NaN
美国 196
Length: 24, dtype: object

df 被第一次 unstack() 之后变成 (行 → 列)

  • 行索引= r1

  • 列索引= [ c r2 ]

df 被第二次 unstack() 之后变成 (行 → 列)

  • 行索引= []

  • 列索引= [ c r2 r1 ]

重塑后的 Series 只有列索引 (实际上是个转置的 Series),有三层,第一层是 特征 ,第二层是 代号 ,第三层是

5.2

透视

数据 源表通常只包含行和列,那么 经常有重复值出现在各列下,因而导致源表不能传递有价值的信息。这时可用「透视」方法调整 源表 的布局用作更清晰的展示。

知识点

本节「透视」得到的数据表和 Excel 里面的透视表 (pivot table) 是一样的。 透视表是用来汇总其它表的数据:

  1. 首先把源表分组,将不同值当做行 (row)、列 (column) 和值 (value)

  2. 然后对各组内数据做汇总操作如 排序 、平均、累加、计数等

这种动态将·「源表」得到想要「终表」的旋转 (pivoting) 过程,使透视表 得以命名。

在 Pandas 里透视的方法有两种:

  • 用  pivot  函数将 「一张长表」变「多张宽表」,

  • melt 函数将「多张宽表」变「一张长表」,

本节使用的数据描述如下:

  • 5 只股票:AAPL, JD, BABA, FB, GS

  • 4 个交易日:从 2019-02-21 到 2019-02-26


 

data = pd.read_csv('Stock.csv', parse_dates=[0], dayfirst=True)

data

Python小白数据科学教程:Pandas (下)

从上表看出有 20 行 (5 × 4) 和 8 列,在 Date  和  Symbol 那两列下就有重复值,4 个日期和 5 个股票在 20 行中分别出现了 5 次和 4 次。

从长到宽 (pivot)

当我们做数据分析时,只关注不同股票在不同日期下的 Adj Close ,那么可用  pivot 函数可将原始 data「透视」成一个新的 DataFrame,起名 close_price。在 pivot 函数中

  • 将 index 设置成 ‘Date’

  • 将 columns 设置成 ‘Symbol’

  • 将 values 设置 ‘Adj Close’

close_price 实际上把 data[ ‘Date’ ] 和 data[ ‘Symbol’ ] 的唯一值当成支点(pivot 就是支点的意思) 创建一个 DataFrame,其中

  • 行标签= 2019-02-21, 2019-02-22, 2019-02-25, 2019-02-26

  • 列标签= AAPL, JD, BABA, FB, GS

在把 data[ ‘Adj Close’ ] 的值放在以如上的 行标签列标签 创建的 close_price   来展示。

代码如下:


 

close_price = data.pivot( index='Date',

columns='Symbol',

values='Adj Close' )

close_price

Python小白数据科学教程:Pandas (下)

如果觉得 Adj Close 不够,还想加个  Volume 看看,那么就把 values 设置成 [ 'Adj Close''Volume' ]。这时支点还是 data[ ‘Date’ ] 和 data[ ‘Symbol’ ],但是要透视的值增加到 data[[ 'Adj Close''Volume' ]] 了。 pivot 函数返回的是 两个 透视表。


 

data.pivot( index='Date',

columns='Symbol',

values=['Adj Close','Volume'] )

Python小白数据科学教程:Pandas (下)

如果不设置 values 参数,那么 pivot 函数返回的是 透视表。(源表 data 有八列,两列当了支点,剩下六列用来透视)


 

all_pivot = data.pivot( index='Date',

columns='Symbol' )

all_pivot

Python小白数据科学教程:Pandas (下)

再继续观察下,all_pivot 实际上是个多层 DataFrame (有多层 columns)。假设我们要获取 2019-02-25 和 2019-02-26 两天的 BABA 和 FB 的开盘价,用以下「多层索引和切片」的方法。

all_pivot['Open'].iloc[2:,1:3]

Python小白数据科学教程:Pandas (下)

从宽到长 (melt)

pivot 逆反操作是 melt

  • 前者将「一张长表」变成「多张宽表」

  • 后者将「多张宽表」变成「一张长表」

具体来说,函数 melt 实际是将「源表」转化成 id - variable 类型的 DataFrame,下例将

  • DateSymbol  列当成 id

  • 其他列 OpenHighLowCloseAdj Close 和  Volume 当成 variable ,而它们对应的值当成 value

代码如下:


 

melted_data = pd.melt( data, id_vars=['Date','Symbol'] )

melted_data.head(5).append(melted_data.tail(5))

Python小白数据科学教程:Pandas (下)

新生成的 DataFrame 有 120 行 (4 × 5 × 6)

  • 4 = data[ 'Date' ] 有 4 个日期

  • 5 = data[ 'Symbol' ] 有 5 只股票

  • 6 = OpenHighLowCloseAdj Close 和  Volume  这 6 个变量

在新表 melted_data 中

  • 在参数 id_vars 设置的 Date  和  Symbol  还保持为 columns

  • 此外还多出两个 columns,一个叫 variable,一个叫 value

    • variable 列下的值为 OpenHighLowCloseAdj Close 和  Volume

    • value 列下的值为前者在「源表 data」中的值

函数 melt 可以生成一张含有多个 id 的长表,然后可在 id 上筛选出我们想要的信息,比如


 

melted_data[ lambda x: (x.Date=='25/02/2019')

& ((x.Symbol=='BABA')|(x.Symbol=='FB')) ]

Python小白数据科学教程:Pandas (下)

在  melted_data 上使用 调用函数 (callable function) 做索引,我们得到了在  2019-02-25 那天 BABA 和 FB 的信息。

6

数据表的分组和整合

DataFrame 中的数据可以根据某些规则分组,然后在每组的数据上计算出不同统计量。这种操作称之为 split-apply-combine,

6.1

数据准备

本节使用的数据描述如下:

  • 5 只股票:AAPL, JD, BABA, FB, GS

  • 1 年时期:从 2018-02-26 到 2019-02-26


 

data = pd.read_csv('1Y Stock Data.csv', parse_dates=[0], dayfirst=True)

data.head(3).append(data.tail(3))

Python小白数据科学教程:Pandas (下)

我们目前只对 Adj Close 感兴趣,而且想知道在哪一年份或哪一月份每支股票的  Adj Close 是多少。因此我们需要做两件事:

  1. 只保留 'Date' , 'Symbol'‘Adj Close‘

  2. ‘Date’  中获取 ‘Year’‘Month’ 的信息并插入表中

将处理过后的数据存在 data1 中。


 

data1 = data[['Date', 'Symbol', 'Adj Close']]

data1.insert( 1, 'Year', pd.DatetimeIndex(data1['Date']).year )

data1.insert( 2, 'Month', pd.DatetimeIndex(data1['Date']).month )

data1.head(3).append(data1.tail(3))

Python小白数据科学教程:Pandas (下)

6.2

分组 (grouping)

用某一特定标签 (label) 将数据 (data) 分组的语法如下:

data. groupBy ( label )

单标签分组

首先我们按 Symbol  来分组:


 

grouped = data1.groupby('Symbol')

grouped

<pandas.core.groupby.groupby.DataFrameGroupBy 
object at 0x7fbbc7248d68>

又要提起那句说了无数遍的话「万物皆对象」了。这个 grouped 也不例外,当你对如果使用某个对象感到迷茫时,用 dir() 来查看它的「属性」和「内置方法」。以下几个属性和方法是我们感兴趣的:

  • ngroups : 组的个数 ( int )

  • size() : 每组元素的个数 ( Series )

  • groups : 每组元素在原 DataFrame 中的索引信息 ( dict )

  • get_groups(label) : 标签 label 对应的数据 ( DataFrame )

下面看看这些属性和方法的产出结果。

数据里有 5 只股票,因此有 5 组。

grouped.ngroups

一年有 252 个交易日,因此每只股票含 252 条信息。

grouped.size()
Symbol
AAPL 252
BABA 252
FB 252
GS 252
JD 252
dtype: int64

苹果股票 (AAPL) 的索引从 0 到 251,...,一直到高盛股票 (GS) 的索引从 1008 到 1259。

grouped.groups

Python小白数据科学教程:Pandas (下)

查查 'GS' 组里的数据的前五行。

grouped.get_group('GS').head()

Python小白数据科学教程:Pandas (下)

接下来定义个 print_groups   函数便于打印组的名字和前五行信息。


 

def print_groups( group_obj ):

for name, group in group_obj:

print( name )

print( group.head() )

用这个函数来调用 grouped (上面用 groupBy 得到的对象)

print_groups( grouped )

Python小白数据科学教程:Pandas (下)

这个 print_groups   函数在下面也多次被用到。

多标签分组

groupBy 函数除了支持单标签分组,也支持多标签分组 (将标签放入一个列表中)。


 

grouped2 = data1.groupby(['Symbol', 'Year', 'Month'])

print_groups( grouped2 )

Python小白数据科学教程:Pandas (下)

Python小白数据科学教程:Pandas (下)

不难看出在 每组左上方 ,有一个 ( Symbol, Year, Month ) 元组型的标识:

  • 第一组:( ‘AAPL’, 2018, 2 )

  • 最后一组:( ‘JD’, 2019, 2 )

还记得〖 数据结构之 Pandas (上) 〗提到的重设索引 ( set_index ) 的操作么?


 

data2 = data1.set_index(['Symbol', 'Year', 'Month'])

data2.head().append(data2.tail())

Python小白数据科学教程:Pandas (下)

对 data1 重设索引之后,产出是一个有 multi-index 的 DataFrame,记做 data2。由于有多层索引,这时我们根据索引的 level 来分组,下面 level = 1 就是对第一层 (Year) 进行分组。


 

grouped3 = data2.groupby(level=1)

print_groups( grouped3 )

Python小白数据科学教程:Pandas (下)

注意 每组左上方 的标识是   Year

多层索引中的任意个数的索引也可以用来分组,下面 level = [0,2] 就是对第零层 ( Symbol ) 和第二层 ( Month ) 进行分组。


 

grouped4 = data2.groupby(level=[0, 2])

print_groups( grouped4 )

Python小白数据科学教程:Pandas (下)

Python小白数据科学教程:Pandas (下)

注意 每组左上方 的标识是 ( SymbolMonth )。

6.3

整合 (aggregating)

做完分组之后 so what?当然是在每组做点数据分析再整合啦。

一个最简单的例子就是上节提到的 size() 函数,用 grouped 对象 ( 上面 根据 Symbol 分组得到的 ) 来举例。

grouped.size()
Symbol
AAPL 252
BABA 252
FB 252
GS 252
JD 252
dtype: int64

一个更实际的例子是用 mean() 函数计算每个  Symbol  下 1 年时期的股价均值。在获取任意信息就用 DataFrame 的索引或切片那一套方法。

grouped.mean()

Python小白数据科学教程:Pandas (下)

除了上述方法,整合还可以用内置函数 aggregate() 或  agg() 作用到「组对象」上。用 grouped4 对象 ( 上面根据 Symbol , Year , Month 分组得到的 ) 来举例。


 

result = grouped4.agg( np.mean )

result.head().append(result.tail())

Python小白数据科学教程:Pandas (下)

函数 agg() 其实是一个高阶函数 (见〖Python 入门篇 (下)〗),里面的参数可以是另外一个函数,比如上例的  np.mean 。上面代码对每只股票在每年每个月上求均值。

那么参数可以是另外 一组 函数么?可以的!


 

result = grouped4.agg( [np.mean, np.std] )

result.head().append(result.tail())

Python小白数据科学教程:Pandas (下)

np.mean 和  np.std 放进列表中,当成是高阶函数  agg() 的参数。上面代码对每只股票在每年每个月上求均值和标准差。

既然 agg() 是高阶函数,参数当然也可以是匿名函数 ( lambda 函数),下面我们定义一个对 grouped 里面每个标签下求最大值和最小值,再求差。注意 lambda  函数里面的 x 就是 grouped。


 

result = grouped.agg( lambda x: np.max(x)-np.min(x) )

result.head().append(result.tail())

Python小白数据科学教程:Pandas (下)

上面代码对每只股票在 DateYearMonth  和  Adj Close 上求「最大值」和「最小值」的差。真正有价值的信息在 Adj Close 那一栏,但我们来验证一下其他几栏。

  • Date : 365 days,合理,一年数据

  • Year : 1,合理,2019 年和 2018 年

  • Month : 11,合理,12 月和 1 月。

6.4

split-apply-combine

前几节做的事情的实质就是一个 split-apply-combine 的过程,如下图所示:

Python小白数据科学教程:Pandas (下)

split - apply - combine  过程有三步:

  1. 根据 key 来 split 成 n 组

  2. 将函数 apply 到每个组

  3. 把 n 组的结果 combine 起来

在看具体例子之前,我们先定一个 top 函数,返回 DataFrame 某一栏中 n 个最大值。


 

def top( df, n=5, column='Volume' ):

return df.sort_values(by=column)[-n:]

将 top 函数用到最原始的数据 (从 csv 中读取出来的) 上。

top( data )

Python小白数据科学教程:Pandas (下)

从上表可看出,在 Volume 栏取 5 个最大值。

Apply 函数

在 split-apply-combine 过程中,apply 是核心。Python 本身有高阶函数 apply() 来实现它,既然是高阶函数,参数可以是另外的函数了,比如刚定义好的 top()。

将 top() 函数 apply 到按 Symbol 分的每个组上,按每个 Symbol 打印出来了 Volume 栏下的 5 个最大值。

data.groupby('Symbol').apply(top)

Python小白数据科学教程:Pandas (下)

上面在使用 top() 时,对于 n 和 column 我们都只用的默认值 5 和 'Volumn'。如果用自己设定的值 n = 1, column = 'Adj Close',写法如下(下面使用在元数据上插入 Year 和 Month 的数据):

data1.groupby(['Symbol','Year']).apply(top, n=1, column='Adj Close')

Python小白数据科学教程:Pandas (下)

按每个 Symbol  和  Year  打印出来了  Adj Close 栏下的最大值。

7

总结

【合并数据表】merge 函数按数据表的 共有列 进行左/右/内/外合并。

Python小白数据科学教程:Pandas (下)

【连接 数据表   concat   函数对 Series 和 DataFrame 沿着不同轴连接。

【重塑数据表】   stack   函数将「列索引」变成「行索引」,用   unstack   函数将「行索引」变成「列索引」。它们只是改变数据表的布局和展示方式而已。

Python小白数据科学教程:Pandas (下)

Python小白数据科学教程:Pandas (下)

Python小白数据科学教程:Pandas (下)

【透视数据表】   pivot   函数将「一张长表」变成「多张宽表」,用   melt   函数将「多张宽表」变成「一张长表」。它们只是改变数据表的布局和展示方式而已。

Python小白数据科学教程:Pandas (下)

Python小白数据科学教程:Pandas (下)

【分组数据表】   groupBy   函数按不同「列索引」下的值分组。一个「列索引」或多个「列索引」就可以。

【整合数据表】   agg   函数对每个组做整合而计算统计量。

split-apply-combine apply 函数做数据分析时美滋滋。

Python小白数据科学教程:Pandas (下)

仔细阅读下面四篇文章, 2小时快速掌握 Python 基础知识要点

完整Python基础知识要点

Python小知识 | 这些技能你不会?(一)

Python小知识 | 这些技能你不会?(二)

Python小知识 | 这些技能你不会?(三)

Python小知识 | 这些技能你不会?(四)

我是老表,支持我请 转发分享本文

Python小白数据科学教程:Pandas (下)


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

轻资产创业

轻资产创业

蔡余杰 / 广东人民出版社 / 2017-11 / 45.00元

在互联网时代,资金和资源已经不是制约创业的关键因素。如今即便没有充足的资金和资产做后盾,创业梦依旧可以成为现实。相信轻资产创业模式能够帮助众多经营管理者和创业者实现管理与创业的梦想。 轻资产创业存在误区,如何跨过? 如何巧用四大模式让自媒体创业落地? 如何用一个点子引发创意型创业? 如何利用电商平台实现流量为王的营销型创业? 如何巧用知识节点做好知识产型创业? ......一起来看看 《轻资产创业》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具