python数据合并float_《利用Python进行数据分析》八章·数据规整:聚合、合并和重塑·学习笔记(一)...

一、数据规整:聚合、合并和重塑

在许多应用中,数据可能分散在许多文件或数据库中,存储的形式也不利于分析。本章关注可以聚合、合并、重塑数据的方法。

本章会介绍pandas的层次化索引,并介绍一些特殊的数据操作。

二、实例

2.1 层次化索引

层次化索引(hierarchical indexing)是pandas的一项重要功能,它使你能在一个轴上拥有多个索引级别。抽象点说,它使你能以低维度形式处理高维度数据。我们先来看一个简单的例子:创建一个Series,并用一个由列表或数组组成的列表作为索引:

In [9]: data = pd.Series(np.random.randn(9),

...: index=[['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'd'],

...: [1, 2, 3, 1, 3, 1, 2, 2, 3]])

In [10]: data

Out[10]:

a 1 1.584341

2 1.305029

3 1.089962

b 1 -0.151411

3 1.357535

c 1 -0.038812

2 0.143890

d 2 -0.955326

3 -0.969812

dtype: float64

看到的结果是经过美化的带有MultiIndex索引的Series的格式。索引之间的“间隔”表示“直接使用上面的标签”:

In [11]: data.index

Out[11]:

MultiIndex(levels=[['a', 'b', 'c', 'd'], [1, 2, 3]],

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

对于一个层次化索引的对象,可以使用所谓的部分索引,使用它选取数据子集的操作更简单:

In [12]: data['b']

Out[12]:

1 -0.151411

3 1.357535

dtype: float64

In [13]: data['b':'c']

Out[13]:

b 1 -0.151411

3 1.357535

c 1 -0.038812

2 0.143890

dtype: float64

In [14]: data.loc[['b', 'd']]

Out[14]:

b 1 -0.151411

3 1.357535

d 2 -0.955326

3 -0.969812

dtype: float64

有时甚至还可以在“内层”中进行选取:

In [15]: data.loc[:, 2]#各行2列

Out[15]:

a 0.478943

c 0.092908

d 0.281746

dtype: float64

层次化索引在数据重塑和基于分组的操作中扮演着重要的角色。例如,可以通过unstack方法将这段数据重新安排到一个DataFrame中:

In [16]: data.unstack()

Out[16]:

1 2 3

a 1.584341 1.305029 1.089962

b -0.151411 NaN 1.357535

c -0.038812 0.143890 NaN

d NaN -0.955326 -0.969812

unstack的逆运算是stack:

In [17]: data.unstack().stack()

Out[17]:

a 1 1.584341

2 1.305029

3 1.089962

b 1 -0.151411

3 1.357535

c 1 -0.038812

2 0.143890

d 2 -0.955326

3 -0.969812

dtype: float64

对于一个DataFrame,每条轴都可以有分层索引:

In [18]: frame = pd.DataFrame(np.arange(12).reshape((4, 3)),

....: index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]],

....: columns=[['Ohio', 'Ohio', 'Colorado'],

....: ['Green', 'Red', 'Green']])

In [19]: frame

Out[19]:

Ohio Colorado

Green Red Green

a 1 0 1 2

2 3 4 5

b 1 6 7 8

2 9 10 11

各层都可以有名字。如果指定了名称,它们就会显示在控制台输出中:

In [20]: frame.index.names = ['key1', 'key2']

In [21]: frame.columns.names = ['state', 'color']

In [22]: frame

Out[22]:

state Ohio Colorado

color Green Red Green

key1 key2

a 1 0 1 2

2 3 4 5

b 1 6 7 8

2 9 10 11

有了部分列索引,因此可以轻松选取列分组:

In [23]: frame['Ohio']

Out[23]:

color Green Red

key1 key2

a 1 0 1

2 3 4

b 1 6 7

2 9 10

2.2 重排与分级排序

有时我们需要重新调整某条轴上各级别的顺序,或根据指定级别上的值对数据进行排序。swaplevel接受两个级别编号或名称,并返回一个互换了级别的新对象:

In [24]: frame.swaplevel('key1', 'key2')

Out[24]:

state Ohio Colorado

color Green Red Green

key2 key1

1 a 0 1 2

2 a 3 4 5

1 b 6 7 8

2 b 9 10 11

而sort_index则根据单个级别中的值对数据进行排序。交换级别时,常常也会用到sort_index,这样最终结果就是按照指定顺序进行字母排序了:

In [25]: frame.sort_index(level=1)#level=1为key2,level=0为key1

Out[25]:

state Ohio Colorado

color Green Red Green

key1 key2

a 1 0 1 2

b 1 6 7 8

a 2 3 4 5

b 2 9 10 11

In [26]: frame.swaplevel(0, 1).sort_index(level=0)

Out[26]:

state Ohio Colorado

color Green Red Green

key2 key1

1 a 0 1 2

b 6 7 8

2 a 3 4 5

b 9 10 11

2.3 根据级别汇总统计

许多对DataFrame和Series的描述和汇总统计都有一个level选项,它用于指定在某条轴上求和的级别。再以上面那个DataFrame为例,我们可以根据行或列上的级别来进行求和:

In [27]: frame.sum(level='key2')

Out[27]:

state Ohio Colorado

color Green Red Green

key2

1 6 8 10

2 12 14 16

In [28]: frame.sum(level='color', axis=1)

Out[28]:

color Green Red

key1 key2

a 1 2 1

2 8 4

b 1 14 7

2 20 10

2.4 使用DataFrame的列进行索引

人们经常想要将DataFrame的一个或多个列当做行索引来用,或者可能希望将行索引变成DataFrame的列。以下面这个DataFrame为例:

In [29]: frame = pd.DataFrame({'a': range(7), 'b': range(7, 0, -1),

....: 'c': ['one', 'one', 'one', 'two', 'two', 'two', 'two'],

....: 'd': [0, 1, 2, 0, 1, 2, 3]})

In [30]: frame

Out[30]:

a b c d

0 0 7 one 0

1 1 6 one 1

2 2 5 one 2

3 3 4 two 0

4 4 3 two 1

5 5 2 two 2

6 6 1 two 3

DataFrame的set_index函数会将其一个或多个列转换为行索引,并创建一个新的DataFrame:

In [31]: frame2 = frame.set_index(['c', 'd'])

In [32]: frame2

Out[32]:

a b

c d

one 0 0 7

1 1 6

2 2 5

two 0 3 4

1 4 3

2 5 2

3 6 1

默认情况下,那些列会从DataFrame中移除,但也可以将其保留下来:

In [33]: frame.set_index(['c', 'd'], drop=False)

Out[33]:

a b c d

c d

one 0 0 7 one 0

1 1 6 one 1

2 2 5 one 2

two 0 3 4 two 0

1 4 3 two 1

2 5 2 two 2

3 6 1 two 3

reset_index的功能跟set_index刚好相反,层次化索引的级别会被转移到列里面:

In [34]: frame2.reset_index()

Out[34]:

c d a b

0 one 0 0 7

1 one 1 1 6

2 one 2 2 5

3 two 0 3 4

4 two 1 4 3

5 two 2 5 2

6 two 3 6 1

2.5 合并数据集

pandas对象中的数据可以通过一些方式进行合并:

· pandas.merge可根据一个或多个键将不同DataFrame中的行连接起来。SQL或其他关系型数据库的用户对此应该会比较熟悉,因为它实现的就是数据库的join操作。

· pandas.concat可以沿着一条轴将多个对象堆叠到一起。

· 实例方法combine_first可以将重复数据拼接在一起,用一个对象中的值填充另一个对象中的缺失值。

数据库风格的DataFrame合并:数据集的合并(merge)或连接(join)运算是通过一个或多个键将行连接起来的。这些运算是关系型数据库(基于SQL)的核心。pandas的merge函数是对数据应用这些算法的主要切入点。

以一个简单的例子开始:

In [35]: df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1': range(7)})

In [36]: df2 = pd.DataFrame({'key': ['a', 'b', 'd'],

....: 'data2': range(3)})

In [37]: df1

Out[37]:

key data1

0 b 0

1 b 1

2 a 2

3 c 3

4 a 4

5 a 5

6 b 6

In [38]: df2

Out[38]:

key data2

0 a 0

1 b 1

2 d 2

这是一种多对一的合并。df1中的数据有多个被标记为a和b的行,而df2中key列的每个值则仅对应一行。对这些对象调用merge即可得到:

In [39]: pd.merge(df1, df2)#依据key匹配

Out[39]:

key data1 data2

0 b 0 1

1 b 1 1

2 b 6 1

3 a 2 0

4 a 4 0

5 a 5 0

注意,我并没有指明要用哪个列进行连接。如果没有指定,merge就会将重叠列的列名当做键。不过,最好明确指定一下:

In [40]: pd.merge(df1, df2, on='key')

Out[40]:

data1 key data20 0 b 11 1 b 12 6 b 13 2 a 04 4 a 05 5 a 0

如果两个对象的列名不同,也可以分别进行指定:

In [41]: df3 = pd.DataFrame({'lkey': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],'data1': range(7)})

In [42]: df4 = pd.DataFrame({'rkey': ['a', 'b', 'd'],

....: 'data2': range(3)})

In [43]: pd.merge(df3, df4, left_on='lkey', right_on='rkey')

Out[43]:

data1 lkey data2 rkey

0 0 b 1 b

1 1 b 1 b

2 6 b 1 b

3 2 a 0 a

4 4 a 0 a

5 5 a 0 a

可能你已经注意到了,结果里面c和d以及与之相关的数据消失了。默认情况下,merge做的是“内连接”;结果中的键是交集。其他方式还有"left"、"right"以及"outer"。外连接求取的是键的并集,组合了左连接和右连接的效果:

In [44]: pd.merge(df1, df2, how='outer')

Out[44]:

key data1 data2

0 b 0.0 1.0

1 b 1.0 1.0

2 b 6.0 1.0

3 a 2.0 0.0

4 a 4.0 0.0

5 a 5.0 0.0

6 c 3.0 NaN

7 d NaN 2.0

多对多的合并有些不直观。看下面的例子:

In [45]: df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'],

....: 'data1': range(6)})

In [46]: df2 = pd.DataFrame({'key': ['a', 'b', 'a', 'b', 'd'],

....: 'data2': range(5)})

In [47]: df1

Out[47]:

key data1

0 b 0

1 b 1

2 a 2

3 c 3

4 a 4

5 b 5

In [48]: df2

Out[48]:

key data2

0 a 0

1 b 1

2 a 2

3 b 3

4 d 4

In [49]: pd.merge(df1, df2, on='key', how='left')

Out[49]:

key data1 data2

0 b 0 1.0

1 b 0 3.0

2 b 1 1.0

3 b 1 3.0

4 a 2 0.0

5 a 2 2.0

6 c 3 NaN

7 a 4 0.0

8 a 4 2.0

9 b 5 1.0

10 b 5 3.0

多对多连接产生的是行的笛卡尔积。由于左边的DataFrame有3个"b"行,右边的有2个,所以最终结果中就有6个"b"行。连接方式只影响出现在结果中的不同的键的值:

In [50]: pd.merge(df1, df2, how='inner')

Out[50]:

key data1 data2

0 b 0 1

1 b 0 3

2 b 1 1

3 b 1 3

4 b 5 1

5 b 5 3

6 a 2 0

7 a 2 2

8 a 4 0

9 a 4 2

要根据多个键进行合并,传入一个由列名组成的列表即可:

In [51]: left = pd.DataFrame({'key1': ['foo', 'foo', 'bar'],

....: 'key2': ['one', 'two', 'one'], 'lval': [1, 2, 3]})

In [52]: right = pd.DataFrame({'key1': ['foo', 'foo', 'bar', 'bar'],

....:'key2': ['one', 'one', 'one', 'two'], 'rval': [4, 5, 6, 7]})

In [53]: pd.merge(left, right, on=['key1', 'key2'], how='outer')

Out[53]:

key1 key2 lval rval

0 foo one 1.0 4.0

1 foo one 1.0 5.0

2 foo two 2.0 NaN

3 bar one 3.0 6.0

4 bar two NaN 7.0

结果中会出现哪些键组合取决于所选的合并方式在进行列-列连接时,DataFrame对象中的索引会被丢弃。

对于合并运算需要考虑的最后一个问题是对重复列名的处理。虽然你可以手工处理列名重叠的问题,但merge有一个更实用的suffixes选项,用于指定附加到左右两个DataFrame对象的重叠列名上的字符串:

In [54]: pd.merge(left, right, on='key1')

Out[54]:

key1 key2_x lval key2_y rval

0 foo one 1 one 4

1 foo one 1 one 5

2 foo two 2 one 4

3 foo two 2 one 5

4 bar one 3 one 6

5 bar one 3 two 7

In [55]: pd.merge(left, right, on='key1', suffixes=('_left', '_right'))

Out[55]:

key1 key2_left lval key2_right rval

0 foo one 1 one 4

1 foo one 1 one 5

2 foo two 2 one 4

3 foo two 2 one 5

4 bar one 3 one 6

5 bar one 3 two 7

2.6索引上的合并

有时候,DataFrame中的连接键位于其索引中。在这种情况下,你可以传入left_index=True或right_index=True以说明索引应该被用作连接键:

In [56]: left1 = pd.DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'],

....: 'value': range(6)})

In [57]: right1 = pd.DataFrame({'group_val': [3.5, 7]}, index=['a', 'b'])

In [58]: left1

Out[58]:

key value

0 a 0

1 b 1

2 a 2

3 a 3

4 b 4

5 c 5

In [59]: right1

Out[59]:

group_val

a 3.5

b 7.0

In [60]: pd.merge(left1, right1, left_on='key', right_index=True)

Out[60]:

key value group_val

0 a 0 3.5

2 a 2 3.5

3 a 3 3.5

1 b 1 7.0

4 b 4 7.0

由于默认的merge方法是求取连接键的交集,因此你可以通过外连接的方式得到它们的并集:

In [61]: pd.merge(left1, right1, left_on='key', right_index=True, how='outer')

Out[61]:

key value group_val

0 a 0 3.5

2 a 2 3.5

3 a 3 3.5

1 b 1 7.0

4 b 4 7.0

5 c 5 NaN

对于层次化索引的数据,事情就有点复杂了,因为索引的合并默认是多键合并:

In [62]: lefth = pd.DataFrame({

'key1': ['Ohio', 'Ohio', 'Ohio','Nevada', 'Nevada'],

'key2': [2000, 2001, 2002, 2001, 2002], 'data': np.arange(5.)})

In [63]: righth = pd.DataFrame(np.arange(12).reshape((6, 2)),

index=[['Nevada', 'Nevada', 'Ohio', 'Ohio','Ohio', 'Ohio'],

[2001, 2000, 2000, 2000, 2001, 2002]],columns=['event1', 'event2'])

In [64]: lefth

Out[64]:

key1 key2 data

0 Ohio 2000 0.0

1 Ohio 2001 1.0

2 Ohio 2002 2.0

3 Nevada 2001 3.0

4 Nevada 2002 4.0

In [65]: righth

Out[65]:

event1 event2

Nevada 2001 0 1

2000 2 3

Ohio 2000 4 5

2000 6 7

2001 8 9

2002 10 11

这种情况下必须以列表的形式指明用作合并键的多个列(注意用how='outer'对重复索引值的处理):

In [66]: pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True)

Out[66]:

key1 key2 data event1 event2

0 Ohio 2000 0.0 4 5

0 Ohio 2000 0.0 6 7

1 Ohio 2001 1.0 8 9

2 Ohio 2002 2.0 10 11

3 Nevada 2001 3.0 0 1

In [67]: pd.merge(lefth, righth, left_on=['key1', 'key2'],

....: right_index=True, how='outer')

Out[67]:

key1 key2 data event1 event2

0 Ohio 2000 0.0 4.0 5.0

0 Ohio 2000 0.0 6.0 7.0

1 Ohio 2001 1.0 8.0 9.0

2 Ohio 2002 2.0 10.0 11.0

3 Nevada 2001 3.0 0.0 1.0

4 Nevada 2002 4.0 NaN NaN

4 Nevada 2000 NaN 2.0 3.0

同时使用合并双方的索引也没问题:

In [68]: left2 = pd.DataFrame([[1., 2.], [3., 4.], [5., 6.]],

....: index=['a', 'c', 'e'],columns=['Ohio', 'Nevada'])

In [69]: right2 = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [13, 14]], index=['b', 'c', 'd', 'e'],columns=['Missouri', 'Alabama'])

In [70]: left2

Out[70]:

Ohio Nevada

a 1.0 2.0

c 3.0 4.0

e 5.0 6.0

In [71]: right2

Out[71]:

Missouri Alabama

b 7.0 8.0

c 9.0 10.0

d 11.0 12.0

e 13.0 14.0

In [72]: pd.merge(left2, right2, how='outer', left_index=True, right_index=True)

Out[72]:

Ohio Nevada Missouri Alabama

a 1.0 2.0 NaN NaN

b NaN NaN 7.0 8.0

c 3.0 4.0 9.0 10.0

d NaN NaN 11.0 12.0

e 5.0 6.0 13.0 14.0

DataFrame还有一个便捷的join实例方法,它能更为方便地实现按索引合并。它还可用于合并多个带有相同或相似索引的DataFrame对象,但要求没有重叠的列。在上面那个例子中,我们可以编写:

In [73]: left2.join(right2, how='outer')

Out[73]:

Ohio Nevada Missouri Alabama

a 1.0 2.0 NaN NaN

b NaN NaN 7.0 8.0

c 3.0 4.0 9.0 10.0

d NaN NaN 11.0 12.0

e 5.0 6.0 13.0 14.0

DataFrame的join方法默认使用的是左连接,保留左边表的行索引。它还支持在调用的DataFrame的列上,连接传递的DataFrame索引:

In [74]: left1.join(right1, on='key')

Out[74]:

key value group_val

0 a 0 3.5

1 b 1 7.0

2 a 2 3.5

3 a 3 3.5

4 b 4 7.0

5 c 5 NaN

最后,对于简单的索引合并,你还可以向join传入一组DataFrame,下一节会介绍更为通用的concat函数,也能实现此功能:

In [75]: another = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [16., 17.]], index=['a', 'c', 'e', 'f'],columns=['New York','Oregon'])

In [76]: another

Out[76]:

New York Oregon

a 7.0 8.0

c 9.0 10.0

e 11.0 12.0

f 16.0 17.0

In [77]: left2.join([right2, another])

Out[77]:

Ohio Nevada Missouri Alabama New York Oregon

a 1.0 2.0 NaN NaN 7.0 8.0

c 3.0 4.0 9.0 10.0 9.0 10.0

e 5.0 6.0 13.0 14.0 11.0 12.0

In [78]: left2.join([right2, another], how='outer')

Out[78]:

Ohio Nevada Missouri Alabama New York Oregon

a 1.0 2.0 NaN NaN 7.0 8.0

b NaN NaN 7.0 8.0 NaN NaN

c 3.0 4.0 9.0 10.0 9.0 10.0

d NaN NaN 11.0 12.0 NaN NaN

e 5.0 6.0 13.0 14.0 11.0 12.0

f NaN NaN NaN NaN 16.0 17.0

三、小结

data.index

data['b']

data['b':'c']

data.loc[['b', 'd']]

data.loc[:, 2]#各行2列

data.unstack()

data.unstack().stack()#unstack的逆运算是stack

frame.index.names = ['key1', 'key2']

frame.columns.names = ['state', 'color']

frame.swaplevel('key1', 'key2')

frame.sort_index(level=1)#level=1为key2,level=0为key1

frame.swaplevel(0, 1).sort_index(level=0)

frame.sum(level='key2')

frame.sum(level='color', axis=1)

frame2 = frame.set_index(['c', 'd'])

frame.set_index(['c', 'd'], drop=False)

frame2.reset_index()

pd.merge(df1, df2)#依据key匹配

pd.merge(df1, df2, on='key')

pd.merge(df3, df4, left_on='lkey', right_on='rkey')

pd.merge(df1, df2, how='outer')

pd.merge(df1, df2, on='key', how='left')

pd.merge(df1, df2, how='inner')

pd.merge(left, right, on='key1', suffixes=('_left', '_right'))

pd.merge(left1, right1, left_on='key', right_index=True)

pd.merge(left1, right1, left_on='key', right_index=True, how='outer')

pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True)

pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True, how='outer')

pd.merge(left2, right2, how='outer', left_index=True, right_index=True)

left2.join(right2, how='outer')

left1.join(right1, on='key')

left2.join([right2, another])

·后记

本章主要涉及一些跨不同来源的数据连接,待实际工作中需求用到时查阅不迟。

量化交易中可能会有涉及到一些简单的关联性内容,比如测算不同品种关联性等等。

欢迎点赞·评论·分享·收藏·浇筑树苗

(o・ェ・o)ノ r (苗·lv.0)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值