经常需要将不同的数据源进行合并,pd中有函数提供方便。
import numpy as np
import pandas as pd
简单起见,先定义一个能创建DF数据的函数:
def make_df(cols, ind):
"""快速创建 DataFrame"""
data = {c: [str(c) + str(i) for i in ind]
for c in cols}
return pd.DataFrame(data, ind)
# 例子 DataFrame
make_df('ABC', range(3))
A | B | C | |
---|---|---|---|
0 | A0 | B0 | C0 |
1 | A1 | B1 | C1 |
2 | A2 | B2 | C2 |
定义一个display类,用于方便地并列显示数据:
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)
np数组可用函数 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])
array([[1, 2], [3, 4], [1, 2], [3, 4]])
np.concatenate([x, x], axis=1)
array([[1, 2, 1, 2], [3, 4, 3, 4]])
pd有函数 pd.concat()
与前述np的函数 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)
concat可以简单地合并一维Series或DF对象,与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
也可用来合并高维数据,如下面的DF:
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 |
默认情况下DF的合并都是逐行进行的,即默认设置 axis=0 。concat也可以设置合并坐标轴,如:
df3 = make_df('AB', [0, 1])
df4 = make_df('CD', [0, 1])
display('df3', 'df4', "pd.concat([df3, df4], axis=1)")
df3
A | B | |
---|---|---|
0 | A0 | B0 |
1 | A1 | B1 |
df4
C | D | |
---|---|---|
0 | C0 | D0 |
1 | C1 | D1 |
pd.concat([df3, df4], axis=1)
A | B | C | D | |
---|---|---|---|---|
0 | A0 | B0 | C0 | D0 |
1 | A1 | B1 | C1 | D1 |
pd与np在合并函数上的主要差异就是,pd在合并时会保留索引,即使索引是重复的,依然保留,如:
x = make_df('AB', [0, 1])
y = make_df('AB', [2, 3])
y.index = x.index # 复制索引
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 |
如果不想要重复的索引,有如下办法:
try:
pd.concat([x, y], verify_integrity=True)
except ValueError as e:
print("ValueError:", e)
ValueError: Indexes have overlapping values: Int64Index([0, 1], dtype='int64')
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 |
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 |
示例合并后的结果是多级索引的DF对象,可用3.6节介绍的方法进行进一步转化,变为需要的形式。
实际情况中往往合并的数据有不同的列名,concat有参数用于此种情况:
df5 = make_df('ABC', [1, 2])
df6 = make_df('BCD', [3, 4])
display('df5', 'df6', 'pd.concat([df5, df6])')
c:\program files\python36-32\lib\site-packages\ipykernel_launcher.py:1: FutureWarning: Sorting because non-concatenation axis is not aligned. A future version of pandas will change to not sort by default. To accept the future behavior, pass 'sort=False'. To retain the current behavior and silence the warning, pass 'sort=True'. """Entry point for launching an IPython kernel.
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 |
df5 = make_df('ABC', [1, 2])
df6 = make_df('BCD', [3, 4])
display('df5', 'df6', 'pd.concat([df5, df6], sort=False)')
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], sort=False)
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,如不想这样,用参数join:
display('df5', 'df6',
"pd.concat([df5, df6], join='outer')")
c:\program files\python36-32\lib\site-packages\ipykernel_launcher.py:1: FutureWarning: Sorting because non-concatenation axis is not aligned. A future version of pandas will change to not sort by default. To accept the future behavior, pass 'sort=False'. To retain the current behavior and silence the warning, pass 'sort=True'. """Entry point for launching an IPython kernel.
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='outer')
A | B | C | D | |
---|---|---|---|---|
1 | A1 | B1 | C1 | NaN |
2 | A2 | B2 | C2 | NaN |
3 | NaN | B3 | C3 | D3 |
4 | NaN | B4 | C4 | D4 |
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 |
该方法可以较小的代码实现合并功能,与concat效果相同,如:
display('df1', 'df2', 'df1.append(df2)')
# pd.concat([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列表中的append方法不同,pd的append实际上不直接更新原有数据对象的值,而是为合并后的数据创建一个新的对象。如需要合并多个DF,还是用concat一次合并更节省资源。