一些最有趣的数据研究在于将不同数据源组合在一起。这些操作既包括简单的将两个不同数据集连接在一起,也包括复杂的能恰当处理数据集间重叠的数据库方式的合并和融合操作。Series和DataFrame设计时考虑到了这些,Pandas带有使混乱的数据处理变得快速和直接的功能和方法。
这里我们先看看使用pd.concat函数做简单的Series和DataFrame数据级联,稍后我们将深入到Pandas实现的更复杂的内存合并和连接。
以标准导入开始:
import pandas as pd
import numpy as np
为了方便,我们定义一个可以创建特殊格式DataFramete数据的函数,它将在随后使用:
def make_df(cols, ind):
"""Quickly make a DataFrame"""
data = {c: [str(c) + str(i) for i in ind]
for c in cols}
return pd.DataFrame(data, ind)
# example DataFrame
make_df('ABC', range(3))
A B C
0 A0 B0 C0
1 A1 B1 C1
2 A2 B2 C2
另外,我们还创建一个快速类,它可以并排的显示多个DataFrames。代码使用特别的repr_html方法,这个方法是IPython用来实现富对象显示的。
class display(object):
"""Display HTML representation of multiple objects"""
template = """
{0}
{1}
"""
def __init__(self, *args):
self.args = args
def _repr_html_(self):
return '\n'.join(self.template.format(a, eval(a)._repr_html_())
for a in self.args)
def __repr__(self):
return '\n\n'.join(a + '\n' + repr(eval(a))
for a in self.args)
随着我们能在后面的章节继续讨论,这类用法将会变得更清楚。
回想:NumPy数组的级联
Series和DataFrame对象的级联非常像NumPy数组的级联,NumPyde级联可以通过使用np.concatenate函数。回忆一下,你可以将二两个或更多的数组内容合并到一个数组中:
x = [1, 2, 3]
y = [4, 5, 6]
z = [7, 8, 9]
np.concatenate([x, y, z])
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
第一个参数使想要合并数组的列表或元组。另外,可以使用axis关键字来指定合并的轴向:
x = [[1, 2],
[3, 4]]
np.concatenate([x, x], axis=1)
array([[1, 2, 1, 2],
[3, 4, 3, 4]])
使用pd.concat简单级联
# Signature in Pandas v0.18
pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False,
keys=None, levels=None, names=None, verify_integrity=False,
copy=True)
Pandas有个函数叫pd.concat(),它和np.concatenate语法差不多,只是包含更多的选项:
# Signature in Pandas v0.18
pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False,
keys=None, levels=None, names=None, verify_integrity=False,
copy=True)
pd.concat()可以被用在Series或DataFrame对象的简单级联上,就像np.concatenate()能被用在数组的简单级联一样。
ser1 = pd.Series(['A', 'B', 'C'], index=[1, 2, 3])
ser2 = pd.Series(['D', 'E', 'F'], index=[4, 5, 6])
pd.concat([ser1, ser2])
1 A
2 B
3 C
4 D
5 E
6 F
dtype: object
它也可以级联高维对象,例如DataFrame:
df1 = make_df('AB', [1, 2])
df2 = make_df('AB', [3, 4])
display('df1', 'df2', 'pd.concat([df1, df2])')
df1
A B
1 A1 B1
2 A2 B2
df2
A B
3 A3 B3
4 A4 B4
pd.concat([df1, df2])
A B
1 A1 B1
2 A2 B2
3 A3 B3
4 A4 B4
默认情况下,级联在DataFrame的行方向上进行,(例如,axis=0)。就像np.concatenate,pd。concat允许指定进行级联的轴向。看下面的例子:
df3 = make_df('AB', [0, 1])
df4 = make_df('CD', [0, 1])
display('df3', 'df4', "pd.concat([df3, df4], axis='col')")
df3
A B
0 A0 B0
1 A1 B1
df4
C D
0 C0 D0
1 C1 D1
pd.concat([df3, df4], axis='col')
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
指定axis=1由等同的效果,上面的例子用的是更直观的axis='col'
重复索引
np.concatenate和pd.concat的一个重要区别是Pandas级联会保留索引,即使结构会有重复的索引。看下面的简单例子:
x = make_df('AB', [0, 1])
y = make_df('AB', [2, 3])
y.index = x.index # make duplicate indices!
display('x', 'y', 'pd.concat([x, y])')
x
A B
0 A0 B0
1 A1 B1
y
A B
0 A2 B2
1 A3 B3
pd.concat([x, y])
A B
0 A0 B0
1 A1 B1
0 A2 B2
1 A3 B3
注意结果中重复的索引。虽然在DataFrame中索引重复是合法的,但通常并不是我们想要的。pd.concat()有几种方法来处理这种情形:
将重复作为错误
如果想简单的验证pd.concat()的结果没有重叠的,可以指定verify_integrity标记。当这个标记设为True时,如果有重复的索引,级联将会引起异常。下面的例子用来表示如果捕获和打印错误消息:
try:
pd.concat([x, y], verify_integrity=True)
except ValueError as e:
print("ValueError:", e)
ValueError: Indexes have overlapping values: [0, 1]
忽略索引
有时索引本身并不重要,可以选择简单的忽略它。使用ignore_index标记来表示这个选项。当ignore_index=True时,级联将会为Series的结果创建一个新的索引:
display('x', 'y', 'pd.concat([x, y], ignore_index=True)')
x
A B
0 A0 B0
1 A1 B1
y
A B
0 A2 B2
1 A3 B3
pd.concat([x, y], ignore_index=True)
A B
0 A0 B0
1 A1 B1
2 A2 B2
3 A3 B3
增加多级索引键值
另一个选择时使用keys参数来为源数据指定标签;结果将是一个带有层级索引的数据:
display('x', 'y', "pd.concat([x, y], keys=['x', 'y'])")
x
A B
0 A0 B0
1 A1 B1
y
A B
0 A2 B2
1 A3 B3
pd.concat([x, y], keys=['x', 'y'])
A B
x 0 A0 B0
1 A1 B1
y 0 A2 B2
1 A3 B3
结果是一个多索引的DataFrame,我们可以使用在层级索引章节的工具来将数据转换成我们感兴趣的形式。
使用joins级联
在刚看到的简单例子里面,我们主要进行对于共享列名称的数据进行级联。实际上,不同数据源的数据也许有不同的列名集合,在这种情况下pd.concat也提供几种选项。考虑对有部分(不是全部)列名称一样的两个DataFrame进行级联:
df5 = make_df('ABC', [1, 2])
df6 = make_df('BCD', [3, 4])
display('df5', 'df6', 'pd.concat([df5, df6])')
df5
A B C
1 A1 B1 C1
2 A2 B2 C2
df6
B C D
3 B3 C3 D3
4 B4 C4 D4
pd.concat([df5, df6])
A B C D
1 A1 B1 C1 NaN
2 A2 B2 C2 NaN
3 NaN B3 C3 D3
4 NaN B4 C4 D4
缺省情况下,没有数据的条目将会被填充成值NaN。为了改变这种行为,我们可以设置concat函数中的参数join 和join_axes。默认情况下,join是行为是输入列的并集(join='outer'),但是我们可以使用join='inner'来取输入数据列的交集。
display('df5', 'df6',
"pd.concat([df5, df6], join='inner')")
df5
A B C
1 A1 B1 C1
2 A2 B2 C2
df6
B C D
3 B3 C3 D3
4 B4 C4 D4
pd.concat([df5, df6], join='inner')
B C
1 B1 C1
2 B2 C2
3 B3 C3
4 B4 C4
另外一种选择是使用join_axes参数来指定要保留的列名称,该参数接收索引对象作为输入。下面例子中指定返回的列名同输入的第一个数据的列名一样:
display('df5', 'df6',
"pd.concat([df5, df6], join_axes=[df5.columns])")
df5
A B C
1 A1 B1 C1
2 A2 B2 C2
df6
B C D
3 B3 C3 D3
4 B4 C4 D4
pd.concat([df5, df6], join_axes=[df5.columns])
A B C
1 A1 B1 C1
2 A2 B2 C2
3 NaN B3 C3
4 NaN B4 C4
在合并两个数据集时,pd.concat函数的选项组合提供各种可能的行为;但使用自己数据时要记住这些工具。
append()方法
因为直接的数组级联非常普通,Series和DataFrame对象提供了append方法来实现同样的功能。例如:与其调用pd.concat([df1, df2]),可以简单的调用df1.append(df2):
display('df1', 'df2', 'df1.append(df2)')
df1
A B
1 A1 B1
2 A2 B2
df2
A B
3 A3 B3
4 A4 B4
df1.append(df2)
A B
1 A1 B1
2 A2 B2
3 A3 B3
4 A4 B4
记住并不像Python list的append()和extend()方法,Pandas的append()方法不会修改原始对象--相反它会用合并后的数据创建一个新的对象。它也不是一个高效的方法,因为这个方法会创建新的索引和数据缓存。因此,如果打算多次执行append操作,你最好是构建一个DataFrame序列然后把它们传递个concat()函数。
在下一节,我们将会看到另一种强大合并数据的方法:实现了数据库风格合并/连接的pd.merge方法。关于concat(),append()及其相关函数的相关信息,请看Pandas文档"Merge, Join, and Concatenate" section