欢乐时光总是过的特别快,清明小长假一转眼就过去了。而我还是一身懒气未脱,没办法,该干的活总是还要干。否则逆水行舟,不进则退,那就太不值得了。
同时我还发现致敬大神的视频其实是出到第八章的,好吧,看来神仔还能再陪我走一段。
那么,开始吧。
8.1 分层索引
分层索引是pandas的重要特性,允许你在一个轴向上拥有多个(两个或两个以上)索引层级。笼统地说,分层索引提供了一种在更低维度的形式中处理更高维度数据的方式。让我们以一个简单的例子开始,先创建一个Series,以列表的列表(或数组)作为索引:
In [223]: 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 [224]: data
Out[224]:
a 1 1.007189
2 -1.296221
3 0.274992
b 1 0.228913
3 1.352917
c 1 0.886429
2 -2.001637
d 2 -0.371843
3 1.669025
dtype: float64
你看到的是一个以MultiIndex作为索引的Series的美化视图。索引中的“间隙”表示“直接使用上面的标签”:
In [225]: data.index
这里出现了一个问题,在课本上,显示data.index的结果为:
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 [228]: data.index
Out[228]:
MultiIndex([('a', 1),
('a', 2),
('a', 3),
('b', 1),
('b', 3),
('c', 1),
('c', 2),
('d', 2),
('d', 3)],
)
我也百度了一下,各种读书笔记、视频中是两种情况都有。具体为什么,好像大家都没有说。我尝试了jupyter和ipython两种工具,似乎都只能输出我上面的这种格式。原因未知。
那么没办法,我们只能继续了。
通过分层索引对象,也可以称为部分索引,允许你简洁地选择出数据的子集:
In [229]: data['b']
Out[229]:
1 0.228913
3 1.352917
dtype: float64
In [230]: data['b':'c']
Out[230]:
b 1 0.228913
3 1.352917
c 1 0.886429
2 -2.001637
dtype: float64
In [231]: data.loc[['b','d']]
Out[231]:
b 1 0.228913
3 1.352917
d 2 -0.371843
3 1.669025
dtype: float64
在“内部”层级中进行选择也是可以的:
In [232]: data.loc[:,2]
Out[232]:
a -1.296221
c -2.001637
d -0.371843
dtype: float64
分层索引在重塑数据和数组透视表等分组操作中扮演了重要角色。例如,你可以使用unstack方法将数据在DataFrame中重新排列:
In [233]: data.unstack()
Out[233]:
1 2 3
a 1.007189 -1.296221 0.274992
b 0.228913 NaN 1.352917
c 0.886429 -2.001637 NaN
d NaN -0.371843 1.669025
unstack的反操作是stack:
In [234]: data.unstack().stack()
Out[234]:
a 1 1.007189
2 -1.296221
3 0.274992
b 1 0.228913
3 1.352917
c 1 0.886429
2 -2.001637
d 2 -0.371843
3 1.669025
dtype: float64
stack和unstack 的细节将在本章后续部分进行探索。
在DataFrame中,每个轴都可以拥有分层索引:
In [237]: 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 [238]: frame
Out[238]:
Ohio Colorado
Green Red Green
a 1 0 1 2
2 3 4 5
b 1 6 7 8
2 9 10 11
分层的层级可以有名称(可以是字符串或Python对象)。如果层级有名称,这些名称会在控制台输出中显示:
In [243]: frame.index.names=['key1','key2']
In [244]: frame.columns.names=['state','color']
In [245]: frame
Out[245]:
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
请注意区分分行标签中的索引名称‘state’和‘color’。
通过部分列索引,你可以选出列中的组:
In [285]: frame['Ohio']
Out[285]:
color Green Red
key1 key2
a 1 0 1
2 3 4
b 1 6 7
2 9 10
一个MultiIndex对象可以使用其自身的构造函数创建并复用。前面介绍的带有层级名称的DataFrame 的列可以这样创建:
In [292]: MultiIndex.from_arrays([['Ohio','Ohio','Colorado'],['Green','Red','Green']],names=['state','color'])
这里又出问题了,按照这么输入,会报错:
In [292]: MultiIndex.from_arrays([['Ohio','Ohio','Colorado'],['Green','Red','Green']],names=['state','color'])
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-292-1e7fbe28e171> in <module>
----> 1 MultiIndex.from_arrays([['Ohio','Ohio','Colorado'],['Green','Red','Green']],names=['state','color'])
NameError: name 'MultiIndex' is not defined
我也百度了下,如果在前面增加pd.,似乎可以达到效果:
In [303]: pd.MultiIndex.from_arrays([['Ohio','Ohio','Colorado'],['Green','Red','Green']],
...: names=['state','color'])
Out[303]:
MultiIndex([( 'Ohio', 'Green'),
( 'Ohio', 'Red'),
('Colorado', 'Green')],
names=['state', 'color'])
至于原因嘛,老样子,别问,问就是不知道。
8.1.1 重排序和层级排序
有时,你需要重新排列轴上的层级顺序,或者按照特定层级的值对数据进行排序。swaplevel接收两个层级序号或层级名称,返回一个进行了层级变更的新对象(但是数据是不变的):
In [304]: frame.swaplevel('key1','key2') # 将‘key2’与‘key1’互换位置
Out[304]:
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 [305]: frame.sort_index(level=1) # 0和1是按照顺序排列的,在下列数据中第一个是key1,所以是0。第二个是key2,就是1。
Out[305]:
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 [306]: frame.swaplevel(0,1).sort_index(level=0) # 这里组合了一下,首先0和1互换,也就是key2在前,key1在后。然后根据0的位置,也就是key2进行排序。
Out[306]:
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
如果索引按照字典顺序从最外层开始排序,那么数据选择性能会更好——调用sort_index(level=0)或sort_index可以得到这样的结果。
8.1.3 使用DataFrame的列进行索引
通常,我们不会使用DataFrame中的一个或多个列作为行索引;反而你可能想要将行索引移动到DataFrame的列中。下面是一个示例DataFrame:
In [317]: 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 [318]: frame
Out[318]:
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,新的DataFrame使用一个或多个列作为索引:
In [319]: frame2 = frame.set_index(['c','d']) # 将‘c’和‘d’作为列索引
In [320]: frame2
Out[320]:
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中移除,你也可以将它们留在DataFrame中:
In [321]: frame.set_index(['c','d'],drop=False)
Out[321]:
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 [322]: frame2.reset_index()
Out[322]:
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
8.2 联合与合并数据集
包含在pandas对象的数据可以通过多种方式联合在一起:
1、pandas.merge根据一个或多个键将行进行连接。对于SQL或其他关系型数据库的用户来说,这种方式比较熟悉,它实现的是数据库的连接操作。
2、pandas.concat使对象在轴向上进行黏合或‘堆叠’。
3、combine_first实例方法允许将重叠的数据拼接在一起,以使用一个对象中的值填充另一个对象中的缺失值。
8.2.1 数据库风格的DataFrame连接
合并或连接操作通过一个或多个键连接行来联合数据集。这些操作是关系型数据库的核心内容(例如基于SQL的数据库)。pandas中的merge函数主要用于将各种join操作算法运用在你的数据上:
In [323]: df1 = pd.DataFrame({'key':['b','b','a','c','a','a','b'],'data1':range(7)})
In [324]: df2 = pd.DataFrame({'key':['a','b','d'],'data2':range(3)})
In [325]: df1
Out[325]:
key data1
0 b 0
1 b 1
2 a 2
3 c 3
4 a 4
5 a 5
6 b 6
In [326]: df2
Out[326]:
key data2
0 a 0
1 b 1
2 d 2
这是一个多对一连接的例子;df1的数据有多个行的标签为a和b,而df2在key列中每个值仅有一行。调用merge处理我们获得的对象:
In [327]: pd.merge(df1,df2) #
Out[327]:
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 [328]: pd.merge(df1,df2,on='key')
Out[328]:
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
这里要说明一下,因为可能不是太好理解。首先,当df1和df2联合之后产生的新数据,排列是将重叠列名为‘键’(或直接指定‘key’)。新数据里,key是将df1和df2里重叠里的数据调出来,df1和df2中的key列,第一个重叠的字幕是b(df1的第二行和df2的第二行),所以先把b拿出来排列。
data1的第一个数字0,就是df1中第一个b对应data1的数据。1就是第二个b对应的data1的数据,以此类推。
data2中因为只有一个b,所以无论出现几个b,对应的都是原数据(df2)中的b的对应数据,也即1。
如果每个对象的列名是不同的,你可以分别为它们指定列名:
In [329]: df3 = pd.DataFrame({'lkey':['b','b','a','c','a','a','b'],'data1':range(7)})
In [330]: df4 = pd.DataFrame({'rkey':['a','b','d'],'data2':range(3)})
In [331]: pd.merge(df3,df4,left_on='lkey',right_on='rkey')
Out[331]:
lkey data1 rkey data2
0 b 0 b 1
1 b 1 b 1
2 b 6 b 1
3 a 2 a 0
4 a 4 a 0
5 a 5 a 0
Ni可能注意到结果中缺少‘c’和‘d’的值以及相关的数据。默认情况下,merge做的是内连接(‘inner’join),结果中的键是两张表的交集。其他可选的选项有‘left’、‘right’和‘outer’。外连接(outer join)是键的并集,联合了左连接和右连接的效果:
In [332]: pd.merge(df1,df2,how='outer')
Out[332]:
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
how参数的不同连接类型
‘inner’:只对两张表都有的键的交集进行联合
‘left’:对所有左表的键进行联合
‘right’:对所有右表的键进行联合
‘outer’:对两张表都有的键的并集进行联合
尽管不是很直观,但多对多的合并有明确的行为。下面是一个例子:
In [333]: df1 = pd.DataFrame({'key':['b','b','a','c','a','b'],'data1':range(6)})
In [334]: df2 = pd.DataFrame({'key':['a','b','a','b','d'],'data2':range(5)})
In [335]: df1
Out[335]:
key data1
0 b 0
1 b 1
2 a 2
3 c 3
4 a 4
5 b 5
In [336]: df2
Out[336]:
key data2
0 a 0
1 b 1
2 a 2
3 b 3
4 d 4
In [337]: pd.merge(df1,df2,on='key',how='left')
Out[337]:
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中有三个‘b’行,而在右边有两行,因此在结果中有六个‘b’。连接方法仅影响结果中显示的不同键值:
In [338]: pd.merge(df1,df2,how='inner')
Out[338]:
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 [343]: left = pd.DataFrame({'key1':['foo','foo','bar'],'key2':['one','two','one'],'lval':[1,2,3]})
In [344]: right = pd.DataFrame({'key1':['foo','foo','bar','bar'],'key2':['one','one','one','two'],'rval':[4,5,6,7]})
In [345]: pd.merge(left,right,on=['key1','key2'],how='outer')
Out[345]:
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 [346]: pd.merge(left,right,on='key1')
Out[346]:
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 [347]: pd.merge(left,right,on='key1',suffixes=('_left','_right'))
Out[347]:
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
这里又需要说明一下。
我们拿上面的一段代码来说明:
In [343]: left = pd.DataFrame({'key1':['foo','foo','bar'],'key2':['one','two','one'],'lval':[1,2,3]})
In [344]: right = pd.DataFrame({'key1':['foo','foo','bar','bar'],'key2':['one','one','one','two'],'rval':[4,5,6,7]})
In [345]: pd.merge(left,right,on=['key1','key2'],how='outer')
Out[345]:
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
在代码中,首先是key1和key2的展示,为什么出现3个foo和两个bar呢?这个好理解,简单来说,就是笛卡尔积再排重。说人话,就是left中的key1和key2两个列表进行组合。key1是[‘foo’,’foo’,’bar’],key2是[‘one’,’two’,’one’],就形成了foo+one、foo+two、bar+one三种组合。
right里面也有这种组合:foo+one、foo+one、bar+one、bar+two。
那么开始并集联合(左右一模一样的就保留一个),就变成了foo+one、foo+one、foo+two、bar+one、bar+two五种组合了。
在key1中三种组合分别对应1,2,3,即foo+one=1,foo+two=2,bar+one=3。key2中也是这个逻辑。
最后形成了上面的代码。
8.2.2 根据索引合并
在某些情况下,DataFrame中用于合并的键是它的索引。在这种情况下,你可以传递left_index=True或right_index=True(或者都传)来表示索引需要用来作为合并的键:
In [348]: left
Out[348]:
key1 key2 lval
0 foo one 1
1 foo two 2
2 bar one 3
In [349]: right
Out[349]:
key1 key2 rval
0 foo one 4
1 foo one 5
2 bar one 6
3 bar two 7
In [350]: left1=pd.DataFrame({'key':['a','b','a','a','b','c'],'value':range(6)})
In [351]: right1=pd.DataFrame({'group_val':[3.5,7]},index=['a','b'])
In [352]: left1
Out[352]:
key value
0 a 0
1 b 1
2 a 2
3 a 3
4 b 4
5 c 5
In [353]: right1
Out[353]:
group_val
a 3.5
b 7.0
In [355]: pd.merge(left1,right1,left_on='key',right_index=True)
Out[355]:
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
由于默认的合并方法是连接键相交,你可以使用外连接来进行合并:
In [357]: pd.merge(left1,right1,left_on='key',right_index=True,how='outer')
Out[357]:
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 [365]: lefth = pd.DataFrame({'key1':['Ohio','Ohio','Ohio','Nevada','Nevada'],'key2':[2000,2001,2002,2001,2002],'data':np.arange
...: (5.)})
In [366]: righth = pd.DataFrame(np.arange(12).reshape((6,2)),index=[['Nevada','Nevada','Ohio','Ohio','Ohio','Ohio'],[2001,2000,200
...: 0,2000,2001,2002]],columns=['event1','event2'])
In [367]: lefth
Out[367]:
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 [368]: righth
Out[368]:
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 [369]: pd.merge(lefth,righth,left_on=['key1','key2'],right_index=True)
Out[369]:
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 [370]: pd.merge(lefth,righth,left_on=['key1','key2'],right_index=True,how='outer')
Out[370]:
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 [371]: left2 = pd.DataFrame([[1.,2.],[3.,4.],[5.,6.]],index=['a','c','e'],columns=['Ohio','Nevada'])
In [372]: right2 = pd.DataFrame([[7.,8.],[9.,10.],[11.,12.],[13,14]],index=['b','c','d','e'],columns=['Missouri','Alabama'])
In [373]: left2
Out[373]:
Ohio Nevada
a 1.0 2.0
c 3.0 4.0
e 5.0 6.0
In [374]: right2
Out[374]:
Missouri Alabama
b 7.0 8.0
c 9.0 10.0
d 11.0 12.0
e 13.0 14.0
In [375]: pd.merge(left2,right2,how='outer',left_index=True,right_index=True)
Out[375]:
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 [376]: left2.join(right2,how='outer')
Out[376]:
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的某一列上连接传递的DataFrame的索引:
In [377]: left1.join(right1,on='key')
Out[377]:
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 [383]: left2.join([right2,aother])
Out[383]:
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 [384]: left2.join([right2,aother],how='outer')
Out[384]:
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
b NaN NaN 7.0 8.0 NaN NaN
d NaN NaN 11.0 12.0 NaN NaN
f NaN NaN NaN NaN 16.0 17.0
8.2.3 沿轴向连接
另一种数据组合操作可互换地称为拼接、绑定或堆叠。Numpy的concatenate函数可以在numpy数组上实现该功能:
In [385]: arr = np.arange(12).reshape((3,4))
In [386]: arr
Out[386]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [387]: np.concatenate([arr,arr],axis=1)
Out[387]:
array([[ 0, 1, 2, 3, 0, 1, 2, 3],
[ 4, 5, 6, 7, 4, 5, 6, 7],
[ 8, 9, 10, 11, 8, 9, 10, 11]])
在Series和DataFrame等pandas对象的上下文中,使用标记的轴可以进一步泛化数组连接。尤其是你还有许多需要考虑的事情:
1.如果对象在其他轴上的索引不同,我们是否应该将不同的元素组合在这些轴上,还是只是用共享的值(交集)?
2.连接的数据块是否需要在结果对象中被识别?
3.“连接轴”是否包含需要保存的数据?在许多情况下,DataFrame中的默认整数标签在连接期间最好丢弃。
pandas的concat函数提供了一种一致性的方式来解决以上问题。我将给出一些例子来表明它的工作机制。假设我们有三个索引不存在重叠的Series:
In [388]: s1 = pd.Series([0,1],index=['a','b'])
In [389]: s2 = pd.Series([2,3,4,],index=['c','d','e'])
In [390]: s3 = pd.Series([5,6],index=['f','g'])
用列表中的这些对象调用concat方法将值和索引粘在一起:
In [391]: pd.concat([s1,s2,s3])
Out[391]:
a 0
b 1
c 2
d 3
e 4
f 5
g 6
dtype: int64
默认情况下,concat方法是沿着axis=0的轴向生效的,生成另一个Series。如果你传递axis=1,返回的结果则是一个DataFrame(axis=1时是列):
In [392]: pd.concat([s1,s2,s3],axis=1)
Out[392]:
0 1 2
a 0.0 NaN NaN
b 1.0 NaN NaN
c NaN 2.0 NaN
d NaN 3.0 NaN
e NaN 4.0 NaN
f NaN NaN 5.0
g NaN NaN 6.0
在这个案例中另一个轴向上并没有重叠,你可以看到排序后的索引合集(‘outer’ join外连接)。你也可以传入join=‘inner’:
In [395]: s4 = pd.concat([s1,s3])
In [396]: s4
Out[396]:
a 0
b 1
f 5
g 6
dtype: int64
In [397]: pd.concat([s1,s4],axis=1)
Out[397]:
0 1
a 0.0 0
b 1.0 1
f NaN 5
g NaN 6
In [398]: pd.concat([s1,s4],axis=1,join='inner')
Out[398]:
0 1
a 0 0
b 1 1
在这个例子中,由于join = 'inner'的选项,‘f’和‘g’标签消失了。
你甚至可以使用join_axes来指定用于连接其他轴向的轴:
In [405]: pd.concat([s1,s4],axis=1,join_axes=[['a','c','b','e']])
到这步又报错了:
In [404]: pd.concat(join=ax)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-404-e76df5927a92> in <module>
----> 1 pd.concat(join=ax)
NameError: name 'ax' is not defined
In [405]: pd.concat([s1,s4],axis=1,join_axes=[['a','c','b','e']])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-405-633623e93fb8> in <module>
----> 1 pd.concat([s1,s4],axis=1,join_axes=[['a','c','b','e']])
TypeError: concat() got an unexpected keyword argument 'join_axes'
原因是因为没有join_axes这个关键字,原因未知。
拼接在一起的各部分无法在结果中区分是一个潜在的问题。假设你想在连接轴向上创建一个多层索引,可以使用kes参数来实现:
In [406]: result = pd.concat([s1,s1,s3],keys=['one','two','three'])
In [407]: result
Out[407]:
one a 0
b 1
two a 0
b 1
three f 5
g 6
dtype: int64
In [408]: result.unstack()
Out[408]:
a b f g
one 0.0 1.0 NaN NaN
two 0.0 1.0 NaN NaN
three NaN NaN 5.0 6.0
沿着轴向axis=1连接Series的时候,keys则成为DataFrame的列头:
In [409]: pd.concat([s1,s2,s3],axis=1,keys=['one','two','three'])
Out[409]:
one two three
a 0.0 NaN NaN
b 1.0 NaN NaN
c NaN 2.0 NaN
d NaN 3.0 NaN
e NaN 4.0 NaN
f NaN NaN 5.0
g NaN NaN 6.0
将相同的逻辑拓展到DataFrame对象:
In [410]: df1 = pd.DataFrame(np.arange(6).reshape(3,2),index=['a','b','c'],columns=['one','two'])
In [411]: df2 = pd.DataFrame(5 + np.arange(4).reshape(2,2),index=['a','c'],columns=['three','four'])
In [412]: df1
Out[412]:
one two
a 0 1
b 2 3
c 4 5
In [413]: df2
Out[413]:
three four
a 5 6
c 7 8
In [414]: pd.concat([df1,df2],axis=1,keys=['level1','level2'])
Out[414]:
level1 level2
one two three four
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0
如果你传递的是对象的字典而不是列表的话,则字典的键会用于keys选项:
In [415]: pd.concat({'level1':df1,'level2':df2},axis=1)
Out[415]:
level1 level2
one two three four
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0
还有一些额外的参数负责多层索引生成。例如,我们可以使用names参数命名生成的轴层级:
In [416]: pd.concat([df1,df2],axis=1,keys=['level1','level2'])
Out[416]:
level1 level2
one two three four
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0
最后需要考虑的是行索引中不包含任何相关数据的DataFrame:
In [417]: df1 = pd.DataFrame(np.random.randn(3,4),columns=['a','b','c','d'])
In [418]: df2 = pd.DataFrame(np.random.randn(2,3),columns=['b','d','a'])
In [419]: df1
Out[419]:
a b c d
0 -0.438570 -0.539741 0.476985 3.248944
1 -1.021228 -0.577087 0.124121 0.302614
2 0.523772 0.000940 1.343810 -0.713544
In [420]: df2
Out[420]:
b d a
0 -0.831154 -2.370232 -1.860761
1 -0.860757 0.560145 -1.265934
在这个示例中,我们传入ignore_index = True:
In [421]: pd.concat([df1,df2],ignore_index=True)
Out[421]:
a b c d
0 -0.438570 -0.539741 0.476985 3.248944
1 -1.021228 -0.577087 0.124121 0.302614
2 0.523772 0.000940 1.343810 -0.713544
3 -1.860761 -0.831154 NaN -2.370232
4 -1.265934 -0.860757 NaN 0.560145
在开篇时我说,神仔做完了这章,其实她只做了一半。这章实在是太无聊了,所以我打算跟随她的步伐,弃掉这章后面部分《重塑与透视》。
直接进入第9章的学习。
胭惜雨
2021年04月08日