在连接/合并类型操作的情况下,pandas提供了各种功能,可以轻松地将Series或DataFrame与各种用于索引和关系代数功能的集合逻辑组合在一起。
此外,pandas还提供实用程序来比较两个Series或DataFrame并总结它们之间的差异。
拼接对象
该concat()
函数(在pandas主命名空间中)在执行沿轴的串联操作的所有繁重工作,同时在其他轴上执行索引(如果有)的可选设置逻辑(联合或交集)。请注意,我之所以说“如果有”,是因为Series只有一个可能的串联轴。
在深入探讨其所有细节及其功能之前concat
,这里有一个简单的示例:
In [1]: df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
...: 'B': ['B0', 'B1', 'B2', 'B3'],
...: 'C': ['C0', 'C1', 'C2', 'C3'],
...: 'D': ['D0', 'D1', 'D2', 'D3']},
...: index=[0, 1, 2, 3])
...:
In [2]: df2 = pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
...: 'B': ['B4', 'B5', 'B6', 'B7'],
...: 'C': ['C4', 'C5', 'C6', 'C7'],
...: 'D': ['D4', 'D5', 'D6', 'D7']},
...: index=[4, 5, 6, 7])
...:
In [3]: df3 = pd.DataFrame({'A': ['A8', 'A9', 'A10', 'A11'],
...: 'B': ['B8', 'B9', 'B10', 'B11'],
...: 'C': ['C8', 'C9', 'C10', 'C11'],
...: 'D': ['D8', 'D9', 'D10', 'D11']},
...: index=[8, 9, 10, 11])
...:
In [4]: frames = [df1, df2, df3]
In [5]: result = pd.concat(frames)
与其在ndarrays上的同级函数一样numpy.concatenate
,,pandas.concat
获取同类类型对象的列表或字典,并将它们与“对其他轴的操作”的一些可配置处理进行连接:
pd.concat(objs, axis=0, join='outer', ignore_index=False, keys=None,
levels=None, names=None, verify_integrity=False, copy=True)
-
objs
:Series或DataFrame对象的序列或映射。如果传递了dict,则排序的键将用作keys参数,除非传递了dict ,在这种情况下,将选择值(请参见下文)。除非所有对象都为None,否则所有None对象都将被静默删除,在这种情况下将引发ValueError。 -
axis
:{0,1,…},默认值为0。要沿其连接的轴。 -
join
:{'inner','outer'},默认为'outer'。如何处理其他轴上的索引。外部为联合,内部为交叉。 -
ignore_index
:布尔值,默认为False。如果为True,则不要在串联轴上使用索引值。结果轴将标记为0,…,n-1。如果要串联对象时,串联轴没有有意义的索引信息,这将很有用。请注意,联接中仍会考虑其他轴上的索引值。 -
keys
:序列,默认为无。使用传递的键作为最外层级别来构造层次结构索引。如果通过了多个级别,则应包含元组。 -
levels
:序列列表,默认为无。用于构造MultiIndex的特定级别(唯一值)。否则,将从按键推断出它们。 -
names
:列表,默认为无。生成的层次结构索引中的级别的名称。 -
verify_integrity
:布尔值,默认为False。检查新的串联轴是否包含重复项。相对于实际数据串联而言,这可能非常昂贵。 -
copy
:布尔值,默认为True。如果为False,则不要不必要地复制数据。
没有一点上下文,这些论点中的许多就没有多大意义。让我们回顾一下上面的例子。假设我们想将特定的键与切碎的DataFrame的每个片段相关联。我们可以使用keys
参数来做到这一点 :
In [6]: result = pd.concat(frames, keys=['x', 'y', 'z'])
如您所见(如果您已经阅读了文档的其余部分),结果对象的索引具有层次结构索引。这意味着我们现在可以通过键选择每个块:
In [7]: result.loc['y']
Out[7]:
A B C D
4 A4 B4 C4 D4
5 A5 B5 C5 D5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
看到这如何非常有用并不是一件容易的事。有关此功能的更多详细信息,请参见下文。
注意
值得注意的是concat()
(并因此 append()
)制作了数据的完整副本,并且不断地重用此功能可能会严重影响性能。如果需要对多个数据集使用该操作,请使用列表推导。
frames = [ process_your_file(f) for f in files ]
result = pd.concat(frames)
在其他轴组逻辑
将多个DataFrame粘合在一起时,可以选择如何处理其他轴(而不是串联的轴)。这可以通过以下两种方式完成:
-
把它们全部结合起来
join='outer'
。这是默认选项,因为它导致零信息丢失。 -
以十字路口为准
join='inner'
。
这是每种方法的一个示例。一,默认join='outer'
行为:
In [8]: df4 = pd.DataFrame({'B': ['B2', 'B3', 'B6', 'B7'],
...: 'D': ['D2', 'D3', 'D6', 'D7'],
...: 'F': ['F2', 'F3', 'F6', 'F7']},
...: index=[2, 3, 6, 7])
...:
In [9]: result = pd.concat([df1, df4], axis=1, sort=False)
警告
在版本0.23.0中更改。
默认行为join='outer'
是对其他轴排序(在这种情况下为列)。在未来版本的熊猫中,默认设置为不排序。我们指定sort=False
现在选择加入新行为。
这是同一件事join='inner'
:
In [10]: result = pd.concat([df1, df4], axis=1, join='inner')
最后,假设我们只是想重用原始DataFrame中的确切索引:
In [11]: result = pd.concat([df1, df4], axis=1).reindex(df1.index)
同样,我们可以在连接之前建立索引:
In [12]: pd.concat([df1, df4.reindex(df1.index)], axis=1)
Out[12]:
A B C D B D F
0 A0 B0 C0 D0 NaN NaN NaN
1 A1 B1 C1 D1 NaN NaN NaN
2 A2 B2 C2 D2 B2 D2 F2
3 A3 B3 C3 D3 B3 D3 F3
使用串联append
一个有用的快捷方式concat()
是append()
在实例方法Series
和DataFrame
。这些方法实际上早于 concat
。它们串联在一起axis=0
,即索引:
In [13]: result = df1.append(df2)
对于DataFrame
,索引必须是不相交的,但列不必是:
In [14]: result = df1.append(df4, sort=False)
append
可能需要多个对象来串联:
In [15]: result = df1.append([df2, df3])
注意
与append()
追加到原始列表并返回的方法不同None
,append()
这里的方法不会修改 df1
并返回带有df2
追加内容的副本。
忽略串联轴上的索引
对于DataFrame
没有有意义索引的对象,您可能希望追加它们,而忽略它们可能具有重叠索引的事实。为此,请使用ignore_index
参数:
In [16]: result = pd.concat([df1, df4], ignore_index=True, sort=False)
这也是有效的参数DataFrame.append()
:
In [17]: result = df1.append(df4, ignore_index=True, sort=False)
与混合ndim串联
您可以将Series
和混合在一起DataFrame
。该 Series
会转化为DataFrame
与列名的名称Series
。
In [18]: s1 = pd.Series(['X0', 'X1', 'X2', 'X3'], name='X')
In [19]: result = pd.concat([df1, s1], axis=1)
注意
由于我们将a串联Series
到a DataFrame
,因此我们可以用达到相同的结果DataFrame.assign()
。要串联任意数量的熊猫对象(DataFrame
或Series
),请使用 concat
。
如果未命名Series
,则将连续编号。
In [20]: s2 = pd.Series(['_0', '_1', '_2', '_3'])
In [21]: result = pd.concat([df1, s2, s2, s2], axis=1)
通过ignore_index=True
将删除所有名称引用。
In [22]: result = pd.concat([df1, s1], axis=1, ignore_index=True)
与组键更多串联
该keys
参数的一个相当普遍的用法是在DataFrame
基于现有的创建新参数时覆盖列名称Series
。请注意,默认行为是如何使结果(如果存在)DataFrame
继承父Series
名称的。
In [23]: s3 = pd.Series([0, 1, 2, 3], name='foo')
In [24]: s4 = pd.Series([0, 1, 2, 3])
In [25]: s5 = pd.Series([0, 1, 4, 5])
In [26]: pd.concat([s3, s4, s5], axis=1)
Out[26]:
foo 0 1
0 0 0 0
1 1 1 1
2 2 2 4
3 3 3 5
通过keys
参数,我们可以覆盖现有的列名。
In [27]: pd.concat([s3, s4, s5], axis=1, keys=['red', 'blue', 'yellow'])
Out[27]:
red blue yellow
0 0 0 0
1 1 1 1
2 2 2 4
3 3 3 5
让我们考虑第一个示例的变体:
In [28]: result = pd.concat(frames, keys=['x', 'y', 'z'])
您还可以将dict传递给,concat
在这种情况下,dict键将用作keys
参数(除非指定了其他键):
In [29]: pieces = {'x': df1, 'y': df2, 'z': df3}
In [30]: result = pd.concat(pieces)
In [31]: result = pd.concat(pieces, keys=['z', 'y'])
创建的MultiIndex具有从传递的键和DataFrame
片段的索引构成的级别:
In [32]: result.index.levels
Out[32]: FrozenList([['z', 'y'], [4, 5, 6, 7, 8, 9, 10, 11]])
如果您希望指定其他级别(有时会这样),则可以使用以下levels
参数:
In [33]: result = pd.concat(pieces, keys=['x', 'y', 'z'],
....: levels=[['z', 'y', 'x', 'w']],
....: names=['group_key'])
....:
In [34]: result.index.levels
Out[34]: FrozenList([['z', 'y', 'x', 'w'], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]])
这是相当深奥的,但是实际上对于实现GroupBy之类的东西(分类变量的顺序有意义)是必要的。
行追加到数据帧
尽管效率不是特别高(因为必须创建一个新对象),但您可以DataFrame
通过向传递一个Series
或dict来将单个行附加到上 append
,这将DataFrame
如上所述返回一个新值。
In [35]: s2 = pd.Series(['X0', 'X1', 'X2', 'X3'], index=['A', 'B', 'C', 'D'])
In [36]: result = df1.append(s2, ignore_index=True)
您应该使用ignore_index
此方法来指示DataFrame放弃其索引。如果希望保留索引,则应构造一个索引适当的DataFrame并附加或连接这些对象。
您还可以传递字典或系列的列表:
In [37]: dicts = [{'A': 1, 'B': 2, 'C': 3, 'X': 4},
....: {'A': 5, 'B': 6, 'C': 7, 'Y': 8}]
....:
In [38]: result = df1.append(dicts, ignore_index=True, sort=False)
数据库样式的DataFrame或命名的Series加入/合并
pandas具有功能全面的,高性能的内存中连接操作,这在逻辑上与SQL等关系数据库非常相似。与其他开放源代码实现(如base::merge.data.frame
R中)相比,这些方法的性能明显更好(在某些情况下要好一个数量级)。这样做的原因是仔细的算法设计和.NET中数据的内部布局DataFrame
。
有关某些高级策略,请参见本食谱。
熟悉SQL但对熊猫不熟悉的用户可能会对与SQL的比较感兴趣 。
pandas提供了一个功能,merge()
作为DataFrame
或命名Series
对象之间所有标准数据库联接操作的入口点:
pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None,
left_index=False, right_index=False, sort=True,
suffixes=('_x', '_y'), copy=True, indicator=False,
validate=None)
-
left
:一个DataFrame或命名为Series的对象。 -
right
:另一个DataFrame或命名为Series的对象。 -
on
:要加入的列或索引级别名称。必须在左右DataFrame和/或Series对象中找到。如果没有通过,left_index
并且right_index
是False
在DataFrames和/或系列列的交叉点会被推断为联接键。 -
left_on
:左侧DataFrame或Series中的列或索引级别用作键。可以是列名称,索引级别名称,也可以是长度等于DataFrame或Series长度的数组。 -
right_on
:右侧DataFrame或Series中的列或索引级别用作键。可以是列名称,索引级别名称,也可以是长度等于DataFrame或Series长度的数组。 -
left_index
:如果为True
,则使用左侧DataFrame或Series中的索引(行标签)作为其连接键。对于具有MultiIndex(分层结构)的DataFrame或Series,级别数必须与正确的DataFrame或Series中的联接键数匹配。 -
right_index
:与left_index
正确的DataFrame或Series的用法相同 -
how
:其一'left'
,'right'
,'outer'
,'inner'
。默认为inner
。有关每种方法的详细说明,请参见下文。 -
sort
:按字典顺序按连接键对结果DataFrame进行排序。默认为True
,设置为False
可以在许多情况下显着提高性能。 -
suffixes
:适用于重叠列的字符串后缀元组。默认为。('_x', '_y')
-
copy
注意:始终True
从传递的DataFrame或命名的Series对象复制数据(默认),即使不需要重新索引也是如此。在很多情况下都无法避免,但是可以提高性能/内存使用率。可以避免复制的情况有些病态,但是仍然提供了此选项。 -
indicator
:将一列添加到输出DataFrame中_merge
,并在每一行的源上提供信息。_merge
是分类类型的,left_only
对于其合并键仅出现在'left'
DataFrame或Series中right_only
的观察值,对于仅其合并键仅出现在'right'
DataFrame或Series中both
的观察值以及在两个视图中均找到观察值的合并键,则取值为。 -
validate
:字符串,默认为无。如果指定,则检查合并是否为指定的类型。-
“ one_to_one”或“ 1:1”:检查合并键在左右数据集中是否唯一。
-
“ one_to_many”或“ 1:m”:检查合并键在左数据集中是否唯一。
-
“ many_to_one”或“ m:1”:检查合并键在正确的数据集中是否唯一。
-
“ many_to_many”或“ m:m”:允许,但不进行检查。
-
注意
用于指定索引水平支撑on
,left_on
以及 right_on
在0.23.0版加入参数。Series
在版本0.24.0中添加了对合并命名对象的支持。
返回类型将与相同left
。如果left
是DataFrame
或的名称,Series
并且right
是的子类DataFrame
,则返回类型仍为DataFrame
。
merge
是pandas命名空间中的一个函数,它也可以作为 DataFrame
实例方法merge()
使用,调用 DataFrame
被隐式视为联接中的左侧对象。
相关join()
方法在merge
内部用于索引索引连接(默认情况下)和索引列连接。如果仅在索引上加入,则可能希望使用DataFrame.join
来节省一些输入。
合并方法简要入门(关系代数)
诸如SQL之类的关系数据库的经验丰富的用户将熟悉用于描述两个类似于SQL表的结构(DataFrame
对象)之间的联接操作的术语。需要考虑的几种情况非常重要:
-
一对一联接:例如,在两个
DataFrame
对象的索引上联接时(必须包含唯一值)。 -
多对一联接:例如,将索引(唯一)联接到different中的一个或多个列时
DataFrame
。 -
多对多联接:在列上联接列。
注意
在列上连接列时(可能是多对多连接),传递的DataFrame
对象上的任何索引都将被丢弃。
值得花一些时间来理解多对多 连接案例的结果。在SQL /标准关系代数中,如果两个表中的键组合都出现多次,则结果表将具有关联数据的笛卡尔积。这是一个具有一个唯一键组合的非常基本的示例:
In [39]: left = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
....: 'A': ['A0', 'A1', 'A2', 'A3'],
....: 'B': ['B0', 'B1', 'B2', 'B3']})
....:
In [40]: right = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
....: 'C': ['C0', 'C1', 'C2', 'C3'],
....: 'D': ['D0', 'D1', 'D2', 'D3']})
....:
In [41]: result = pd.merge(left, right, on='key')
这是带有多个联接键的更复杂的示例。由于默认情况下,只有出现left
和出现的关键right
点(相交) how='inner'
。
In [42]: left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'],
....: 'key2': ['K0', 'K1', 'K0', 'K1'],
....: 'A': ['A0', 'A1', 'A2', 'A3'],
....: 'B': ['B0', 'B1', 'B2', 'B3']})
....:
In [43]: right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
....: 'key2': ['K0', 'K0', 'K0', 'K0'],
....: 'C': ['C0', 'C1', 'C2', 'C3'],
....: 'D': ['D0', 'D1', 'D2', 'D3']})
....:
In [44]: result = pd.merge(left, right, on=['key1', 'key2'])
的how
到参数merge
指定如何确定哪些键要被包括在所得到的表。如果左表或右表中都没有按键组合,则联接表中的值将为 NA
。以下是how
选项及其SQL等效名称的摘要:
合并方式 |
SQL连接名称 |
描述 |
---|---|---|
|
|
仅使用左框中的关键点 |
|
|
仅从右框使用按键 |
|
|
使用两个框架中的键并集 |
|
|
使用两个帧的关键点交集 |
In [45]: result = pd.merge(left, right, how='left', on=['key1', 'key2'])
In [46]: result = pd.merge(left, right, how='right', on=['key1', 'key2'])
In [47]: result = pd.merge(left, right, how='outer', on=['key1', 'key2'])
In [48]: result = pd.merge(left, right, how='inner', on=['key1', 'key2'])
如果MultiIndex的名称与DataFrame中的列相对应,则可以合并多索引的Series和DataFrame。Series.reset_index()
合并之前,使用系列将Series转换为DataFrame ,如以下示例所示。
In [49]: df = pd.DataFrame({"Let": ["A", "B", "C"], "Num": [1, 2, 3]})
In [50]: df
Out[50]:
Let Num
0 A 1
1 B 2
2 C 3
In [51]: ser = pd.Series(
....: ["a", "b", "c", "d", "e", "f"],
....: index=pd.MultiIndex.from_arrays(
....: [["A", "B", "C"] * 2, [1, 2, 3, 4, 5, 6]], names=["Let", "Num"]
....: ),
....: )
....:
In [52]: ser
Out[52]:
Let Num
A 1 a
B 2 b
C 3 c
A 4 d
B 5 e
C 6 f
dtype: object
In [53]: pd.merge(df, ser.reset_index(), on=['Let', 'Num'])
Out[53]:
Let Num 0
0 A 1 a
1 B 2 b
2 C 3 c
这是另一个在DataFrames中具有重复联接键的示例:
In [54]: left = pd.DataFrame({'A': [1, 2], 'B': [2, 2]})
In [55]: right = pd.DataFrame({'A': [4, 5, 6], 'B': [2, 2, 2]})
In [56]: result = pd.merge(left, right, on='B', how='outer')
警告
在重复键上进行联接/合并会导致返回的帧是行尺寸的乘积,这可能导致内存溢出。在加入大型DataFrame之前,用户有责任管理键中的重复值。
检查重复键
用户可以使用该validate
参数自动检查其合并键中是否有意外的重复项。在合并操作之前检查键的唯一性,因此应防止内存溢出。检查密钥唯一性也是确保用户数据结构符合预期的好方法。
在以下示例中,B
right中 存在重复的值DataFrame
。由于这不是validate
参数中指定的一对一合并,因此 将引发异常。
In [57]: left = pd.DataFrame({'A' : [1,2], 'B' : [1, 2]})
In [58]: right = pd.DataFrame({'A' : [4,5,6], 'B': [2, 2, 2]})
In [53]: result = pd.merge(left, right, on='B', how='outer', validate="one_to_one")
...
MergeError: Merge keys are not unique in right dataset; not a one-to-one merge
如果用户知道右边的重复项,DataFrame
但要确保左边的DataFrame中没有重复项,则可以改用该 validate='one_to_many'
参数,这不会引发异常。
In [59]: pd.merge(left, right, on='B', how='outer', validate="one_to_many")
Out[59]:
A_x B A_y
0 1 1 NaN
1 2 2 4.0
2 2 2 5.0
3 2 2 6.0
该合并指标
merge()
接受论点indicator
。如果为True
,_merge
则将将一个名为Categorical-type的列添加到采用值的输出对象:
观察原点
_merge
值仅在
'left'
框架中合并关键点
left_only
仅在
'right'
框架中合并关键点
right_only
在两个框架中合并关键点
both
In [60]: df1 = pd.DataFrame({'col1': [0, 1], 'col_left': ['a', 'b']})
In [61]: df2 = pd.DataFrame({'col1': [1, 2, 2], 'col_right': [2, 2, 2]})
In [62]: pd.merge(df1, df2, on='col1', how='outer', indicator=True)
Out[62]:
col1 col_left col_right _merge
0 0 a NaN left_only
1 1 b 2.0 both
2 2 NaN 2.0 right_only
3 2 NaN 2.0 right_only
该indicator
参数还将接受字符串参数,在这种情况下,指标函数将使用传递的字符串的值作为指标列的名称。
In [63]: pd.merge(df1, df2, on='col1', how='outer', indicator='indicator_column')
Out[63]:
col1 col_left col_right indicator_column
0 0 a NaN left_only
1 1 b 2.0 both
2 2 NaN 2.0 right_only
3 2 NaN 2.0 right_only
合并dtypes
合并将保留联接键的dtype。
In [64]: left = pd.DataFrame({'key': [1], 'v1': [10]})
In [65]: left
Out[65]:
key v1
0 1 10
In [66]: right = pd.DataFrame({'key': [1, 2], 'v1': [20, 30]})
In [67]: right
Out[67]:
key v1
0 1 20
1 2 30
我们能够保留联接键:
In [68]: pd.merge(left, right, how='outer')
Out[68]:
key v1
0 1 10
1 1 20
2 2 30
In [69]: pd.merge(left, right, how='outer').dtypes
Out[69]:
key int64
v1 int64
dtype: object
当然,如果您缺少引入的值,那么生成的dtype将被转换。
In [70]: pd.merge(left, right, how='outer', on='key')
Out[70]:
key v1_x v1_y
0 1 10.0 20
1 2 NaN 30
In [71]: pd.merge(left, right, how='outer', on='key').dtypes
Out[71]:
key int64
v1_x float64
v1_y int64
dtype: object
合并将保留category
人种的dtypes。另请参阅“类别”部分。
左框架。
In [72]: from pandas.api.types import CategoricalDtype
In [73]: X = pd.Series(np.random.choice(['foo', 'bar'], size=(10,)))
In [74]: X = X.astype(CategoricalDtype(categories=['foo', 'bar']))
In [75]: left = pd.DataFrame({'X': X,
....: 'Y': np.random.choice(['one', 'two', 'three'],
....: size=(10,))})
....:
In [76]: left
Out[76]:
X Y
0 bar one
1 foo one
2 foo three
3 bar three
4 foo one
5 bar one
6 bar three
7 bar three
8 bar three
9 foo three
In [77]: left.dtypes
Out[77]:
X category
Y object
dtype: object
正确的框架。
In [78]: right = pd.DataFrame({'X': pd.Series(['foo', 'bar'],
....: dtype=CategoricalDtype(['foo', 'bar'])),
....: 'Z': [1, 2]})
....:
In [79]: right
Out[79]:
X Z
0 foo 1
1 bar 2
In [80]: right.dtypes
Out[80]:
X category
Z int64
dtype: object
合并结果:
In [81]: result = pd.merge(left, right, how='outer')
In [82]: result
Out[82]:
X Y Z
0 bar one 2
1 bar three 2
2 bar one 2
3 bar three 2
4 bar three 2
5 bar three 2
6 foo one 1
7 foo three 1
8 foo one 1
9 foo three 1
In [83]: result.dtypes
Out[83]:
X category
Y object
Z int64
dtype: object
注意
类别dtype必须完全相同,这意味着相同的类别和有序属性。否则结果将强制为类别的dtype。
注意
与category
dtype合并相比,在相同的dtype上合并可能会表现出色object
。
加入索引
DataFrame.join()
是一种将两个可能具有不同索引的列DataFrames
合并为单个结果 的便捷方法DataFrame
。这是一个非常基本的示例:
In [84]: left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
....: 'B': ['B0', 'B1', 'B2']},
....: index=['K0', 'K1', 'K2'])
....:
In [85]: right = pd.DataFrame({'C': ['C0', 'C2', 'C3'],
....: 'D': ['D0', 'D2', 'D3']},
....: index=['K0', 'K2', 'K3'])
....:
In [86]: result = left.join(right)
In [87]: result = left.join(right, how='outer')
与上述相同,但带有how='inner'
。
In [88]: result = left.join(right, how='inner')
此处的数据对齐在索引(行标签)上。使用merge
指示其使用索引的附加参数可以实现相同的行为:
In [89]: result = pd.merge(left, right, left_index=True, right_index=True, how='outer')
In [90]: result = pd.merge(left, right, left_index=True, right_index=True, how='inner');
在索引上关键列
join()
接受一个可选on
参数,该参数可以是一列或多个列名,它指定传递的DataFrame
内容将与中的该列对齐DataFrame
。这两个函数调用是完全等效的:
left.join(right, on=key_or_keys)
pd.merge(left, right, left_on=key_or_keys, right_index=True,
how='left', sort=False)
显然,您可以选择任何一种更方便的形式。对于多对一连接(其中的一个DataFrame
已通过连接键索引),使用join
可能会更方便。这是一个简单的示例:
In [91]: left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
....: 'B': ['B0', 'B1', 'B2', 'B3'],
....: 'key': ['K0', 'K1', 'K0', 'K1']})
....:
In [92]: right = pd.DataFrame({'C': ['C0', 'C1'],
....: 'D': ['D0', 'D1']},
....: index=['K0', 'K1'])
....:
In [93]: result = left.join(right, on='key')
In [94]: result = pd.merge(left, right, left_on='key', right_index=True,
....: how='left', sort=False);
....:
要加入多个键,传递的DataFrame必须具有MultiIndex
:
In [95]: left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
....: 'B': ['B0', 'B1', 'B2', 'B3'],
....: 'key1': ['K0', 'K0', 'K1', 'K2'],
....: 'key2': ['K0', 'K1', 'K0', 'K1']})
....:
In [96]: index = pd.MultiIndex.from_tuples([('K0', 'K0'), ('K1', 'K0'),
....: ('K2', 'K0'), ('K2', 'K1')])
....:
In [97]: right = pd.DataFrame({'C': ['C0', 'C1', 'C2', 'C3'],
....: 'D': ['D0', 'D1', 'D2', 'D3']},
....: index=index)
....:
现在,可以通过传递两个关键列名称来进行连接:
In [98]: result = left.join(right, on=['key1', 'key2'])
默认的DataFrame.join
是执行左联接(对于Excel用户,本质上是“ VLOOKUP”操作),该联接仅使用在调用DataFrame中找到的键。其他联接类型(例如内部联接)也可以轻松执行:
In [99]: result = left.join(right, on=['key1', 'key2'], how='inner')
如您所见,这将删除所有不匹配的行。
加入单一指数为多指标
您可以将一个索引DataFrame
与一个MultiIndexed级别连接DataFrame
。级别将在单索引帧的索引名称与MultiIndexed帧的级别名称上匹配。
In [100]: left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
.....: 'B': ['B0', 'B1', 'B2']},
.....: index=pd.Index(['K0', 'K1', 'K2'], name='key'))
.....:
In [101]: index = pd.MultiIndex.from_tuples([('K0', 'Y0'), ('K1', 'Y1'),
.....: ('K2', 'Y2'), ('K2', 'Y3')],
.....: names=['key', 'Y'])
.....:
In [102]: right = pd.DataFrame({'C': ['C0', 'C1', 'C2', 'C3'],
.....: 'D': ['D0', 'D1', 'D2', 'D3']},
.....: index=index)
.....:
In [103]: result = left.join(right, how='inner')
这是等效的,但是比它更冗长,存储效率更高/更快。
In [104]: result = pd.merge(left.reset_index(), right.reset_index(),
.....: on=['key'], how='inner').set_index(['key','Y'])
.....:
有两个MultiIndexes加入
只要在连接中完全使用了右参数的索引,并且该参数是左参数中索引的子集,就可以用有限的方式来支持它,如下例所示:
In [105]: leftindex = pd.MultiIndex.from_product([list('abc'), list('xy'), [1, 2]],
.....: names=['abc', 'xy', 'num'])
.....:
In [106]: left = pd.DataFrame({'v1': range(12)}, index=leftindex)
In [107]: left
Out[107]:
v1
abc xy num
a x 1 0
2 1
y 1 2
2 3
b x 1 4
2 5
y 1 6
2 7
c x 1 8
2 9
y 1 10
2 11
In [108]: rightindex = pd.MultiIndex.from_product([list('abc'), list('xy')],
.....: names=['abc', 'xy'])
.....:
In [109]: right = pd.DataFrame({'v2': [100 * i for i in range(1, 7)]}, index=rightindex)
In [110]: right
Out[110]:
v2
abc xy
a x 100
y 200
b x 300
y 400
c x 500
y 600
In [111]: left.join(right, on=['abc', 'xy'], how='inner')
Out[111]:
v1 v2
abc xy num
a x 1 0 100
2 1 100
y 1 2 200
2 3 200
b x 1 4 300
2 5 300
y 1 6 400
2 7 400
c x 1 8 500
2 9 500
y 1 10 600
2 11 600
如果不满足该条件,则可以使用以下代码完成具有两个多索引的联接。
In [112]: leftindex = pd.MultiIndex.from_tuples([('K0', 'X0'), ('K0', 'X1'),
.....: ('K1', 'X2')],
.....: names=['key', 'X'])
.....:
In [113]: left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
.....: 'B': ['B0', 'B1', 'B2']},
.....: index=leftindex)
.....:
In [114]: rightindex = pd.MultiIndex.from_tuples([('K0', 'Y0'), ('K1', 'Y1'),
.....: ('K2', 'Y2'), ('K2', 'Y3')],
.....: names=['key', 'Y'])
.....:
In [115]: right = pd.DataFrame({'C': ['C0', 'C1', 'C2', 'C3'],
.....: 'D': ['D0', 'D1', 'D2', 'D3']},
.....: index=rightindex)
.....:
In [116]: result = pd.merge(left.reset_index(), right.reset_index(),
.....: on=['key'], how='inner').set_index(['key', 'X', 'Y'])
.....:
合并列和索引级别的组合
0.23版中的新功能。
字符串作为传递on
,left_on
和right_on
参数可以指列名或索引级别名称。这样就可以DataFrame
在索引级别和列的组合上合并 实例,而无需重置索引。
In [117]: left_index = pd.Index(['K0', 'K0', 'K1', 'K2'], name='key1')
In [118]: left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
.....: 'B': ['B0', 'B1', 'B2', 'B3'],
.....: 'key2': ['K0', 'K1', 'K0', 'K1']},
.....: index=left_index)
.....:
In [119]: right_index = pd.Index(['K0', 'K1', 'K2', 'K2'], name='key1')
In [120]: right = pd.DataFrame({'C': ['C0', 'C1', 'C2', 'C3'],
.....: 'D': ['D0', 'D1', 'D2', 'D3'],
.....: 'key2': ['K0', 'K0', 'K0', 'K1']},
.....: index=right_index)
.....:
In [121]: result = left.merge(right, on=['key1', 'key2'])
注意
当将DataFrame合并到与两个框架中的索引级别都匹配的字符串上时,索引级别将保留为结果DataFrame中的索引级别。
注意
当仅使用MultiIndex的某些级别合并DataFrame时,多余的级别将从结果合并中删除。为了保留这些级别,请reset_index
在执行合并之前将这些级别名称用于将这些级别移动到列。
注意
如果字符串与列名和索引级名称都匹配,则会发出警告,并且列优先。这将在将来的版本中导致歧义错误。
值列重叠
mergesuffixes
参数将字符串列表的元组附加到输入DataFrame
s中重叠的列名称上,以消除结果列的歧义:
In [122]: left = pd.DataFrame({'k': ['K0', 'K1', 'K2'], 'v': [1, 2, 3]})
In [123]: right = pd.DataFrame({'k': ['K0', 'K0', 'K3'], 'v': [4, 5, 6]})
In [124]: result = pd.merge(left, right, on='k')
In [125]: result = pd.merge(left, right, on='k', suffixes=('_l', '_r'))
DataFrame.join()
具有lsuffix
和rsuffix
行为相似的参数。
In [126]: left = left.set_index('k')
In [127]: right = right.set_index('k')
In [128]: result = left.join(right, lsuffix='_l', rsuffix='_r')
连接多个DataFrames
DataFrames
也可以传递一个列表或元组以join()
将它们连接到它们的索引上。
In [129]: right2 = pd.DataFrame({'v': [7, 8, 9]}, index=['K1', 'K1', 'K2'])
In [130]: result = left.join([right, right2])
将Series或DataFrame列中的值合并在一起
另一个相当常见的情况是有两个像索引(或类似索引)Series
或DataFrame
对象和从值想“补丁”值在一个对象匹配的其他指数。这是一个例子:
In [131]: df1 = pd.DataFrame([[np.nan, 3., 5.], [-4.6, np.nan, np.nan],
.....: [np.nan, 7., np.nan]])
.....:
In [132]: df2 = pd.DataFrame([[-42.6, np.nan, -8.2], [-5., 1.6, 4]],
.....: index=[1, 2])
.....:
为此,请使用以下combine_first()
方法:
In [133]: result = df1.combine_first(df2)
请注意,DataFrame
如果左侧缺少这些值,则此方法仅采用右侧的值DataFrame
。相关方法update()
更改了非NA值:
In [134]: df1.update(df2)
时间序列友好合并
合并排序的数据
一个merge_ordered()
功能允许组合时间序列和其他有序数据。特别是,它具有一个可选fill_method
关键字来填充/内插丢失的数据:
In [135]: left = pd.DataFrame({'k': ['K0', 'K1', 'K1', 'K2'],
.....: 'lv': [1, 2, 3, 4],
.....: 's': ['a', 'b', 'c', 'd']})
.....:
In [136]: right = pd.DataFrame({'k': ['K1', 'K2', 'K4'],
.....: 'rv': [1, 2, 3]})
.....:
In [137]: pd.merge_ordered(left, right, fill_method='ffill', left_by='s')
Out[137]:
k lv s rv
0 K0 1.0 a NaN
1 K1 1.0 a 1.0
2 K2 1.0 a 2.0
3 K4 1.0 a 3.0
4 K1 2.0 b 1.0
5 K2 2.0 b 2.0
6 K4 2.0 b 3.0
7 K1 3.0 c 1.0
8 K2 3.0 c 2.0
9 K4 3.0 c 3.0
10 K1 NaN d 1.0
11 K2 4.0 d 2.0
12 K4 4.0 d 3.0
合并ASOF
Amerge_asof()
与有序左联接相似,不同之处在于我们在最接近的键而不是相等的键上进行匹配。对于中的每一行left
DataFrame
,我们选择right
DataFrame
其on
键小于左键的最后一行。两个DataFrame都必须按键排序。
可选地,asof合并可以执行逐组合并。by
除了键上最接近的匹配之外,此 键均等地匹配on
键。
例如; 我们可能有trades
,quotes
我们想asof
将它们合并。
In [138]: trades = pd.DataFrame({
.....: 'time': pd.to_datetime(['20160525 13:30:00.023',
.....: '20160525 13:30:00.038',
.....: '20160525 13:30:00.048',
.....: '20160525 13:30:00.048',
.....: '20160525 13:30:00.048']),
.....: 'ticker': ['MSFT', 'MSFT',
.....: 'GOOG', 'GOOG', 'AAPL'],
.....: 'price': [51.95, 51.95,
.....: 720.77, 720.92, 98.00],
.....: 'quantity': [75, 155,
.....: 100, 100, 100]},
.....: columns=['time', 'ticker', 'price', 'quantity'])
.....:
In [139]: quotes = pd.DataFrame({
.....: 'time': pd.to_datetime(['20160525 13:30:00.023',
.....: '20160525 13:30:00.023',
.....: '20160525 13:30:00.030',
.....: '20160525 13:30:00.041',
.....: '20160525 13:30:00.048',
.....: '20160525 13:30:00.049',
.....: '20160525 13:30:00.072',
.....: '20160525 13:30:00.075']),
.....: 'ticker': ['GOOG', 'MSFT', 'MSFT',
.....: 'MSFT', 'GOOG', 'AAPL', 'GOOG',
.....: 'MSFT'],
.....: 'bid': [720.50, 51.95, 51.97, 51.99,
.....: 720.50, 97.99, 720.50, 52.01],
.....: 'ask': [720.93, 51.96, 51.98, 52.00,
.....: 720.93, 98.01, 720.88, 52.03]},
.....: columns=['time', 'ticker', 'bid', 'ask'])
.....:
In [140]: trades
Out[140]:
time ticker price quantity
0 2016-05-25 13:30:00.023 MSFT 51.95 75
1 2016-05-25 13:30:00.038 MSFT 51.95 155
2 2016-05-25 13:30:00.048 GOOG 720.77 100
3 2016-05-25 13:30:00.048 GOOG 720.92 100
4 2016-05-25 13:30:00.048 AAPL 98.00 100
In [141]: quotes
Out[141]:
time ticker bid ask
0 2016-05-25 13:30:00.023 GOOG 720.50 720.93
1 2016-05-25 13:30:00.023 MSFT 51.95 51.96
2 2016-05-25 13:30:00.030 MSFT 51.97 51.98
3 2016-05-25 13:30:00.041 MSFT 51.99 52.00
4 2016-05-25 13:30:00.048 GOOG 720.50 720.93
5 2016-05-25 13:30:00.049 AAPL 97.99 98.01
6 2016-05-25 13:30:00.072 GOOG 720.50 720.88
7 2016-05-25 13:30:00.075 MSFT 52.01 52.03
默认情况下,我们采用引号的形式。
In [142]: pd.merge_asof(trades, quotes,
.....: on='time',
.....: by='ticker')
.....:
Out[142]:
time ticker price quantity bid ask
0 2016-05-25 13:30:00.023 MSFT 51.95 75 51.95 51.96
1 2016-05-25 13:30:00.038 MSFT 51.95 155 51.97 51.98
2 2016-05-25 13:30:00.048 GOOG 720.77 100 720.50 720.93
3 2016-05-25 13:30:00.048 GOOG 720.92 100 720.50 720.93
4 2016-05-25 13:30:00.048 AAPL 98.00 100 NaN NaN
我们只在2ms
报价时间和交易时间之间进行交易。
In [143]: pd.merge_asof(trades, quotes,
.....: on='time',
.....: by='ticker',
.....: tolerance=pd.Timedelta('2ms'))
.....:
Out[143]:
time ticker price quantity bid ask
0 2016-05-25 13:30:00.023 MSFT 51.95 75 51.95 51.96
1 2016-05-25 13:30:00.038 MSFT 51.95 155 NaN NaN
2 2016-05-25 13:30:00.048 GOOG 720.77 100 720.50 720.93
3 2016-05-25 13:30:00.048 GOOG 720.92 100 720.50 720.93
4 2016-05-25 13:30:00.048 AAPL 98.00 100 NaN NaN
我们仅在10ms
报价时间和交易时间之间进行结算,并且不包括准时匹配。请注意,尽管我们排除了(引号)的完全匹配项,但先前的引号确实会传播到该时间点。
In [144]: pd.merge_asof(trades, quotes,
.....: on='time',
.....: by='ticker',
.....: tolerance=pd.Timedelta('10ms'),
.....: allow_exact_matches=False)
.....:
Out[144]:
time ticker price quantity bid ask
0 2016-05-25 13:30:00.023 MSFT 51.95 75 NaN NaN
1 2016-05-25 13:30:00.038 MSFT 51.95 155 51.97 51.98
2 2016-05-25 13:30:00.048 GOOG 720.77 100 NaN NaN
3 2016-05-25 13:30:00.048 GOOG 720.92 100 NaN NaN
4 2016-05-25 13:30:00.048 AAPL 98.00 100 NaN NaN
比较对象
使用compare()
和compare()
方法,您可以分别比较两个DataFrame或Series,并总结它们之间的差异。
V1.1.0中添加了此功能。
例如,您可能想要比较两个DataFrame并并排堆叠它们的差异。
In [145]: df = pd.DataFrame(
.....: {
.....: "col1": ["a", "a", "b", "b", "a"],
.....: "col2": [1.0, 2.0, 3.0, np.nan, 5.0],
.....: "col3": [1.0, 2.0, 3.0, 4.0, 5.0]
.....: },
.....: columns=["col1", "col2", "col3"],
.....: )
.....:
In [146]: df
Out[146]:
col1 col2 col3
0 a 1.0 1.0
1 a 2.0 2.0
2 b 3.0 3.0
3 b NaN 4.0
4 a 5.0 5.0
In [147]: df2 = df.copy()
In [148]: df2.loc[0, 'col1'] = 'c'
In [149]: df2.loc[2, 'col3'] = 4.0
In [150]: df2
Out[150]:
col1 col2 col3
0 c 1.0 1.0
1 a 2.0 2.0
2 b 3.0 4.0
3 b NaN 4.0
4 a 5.0 5.0
In [151]: df.compare(df2)
Out[151]:
col1 col3
self other self other
0 a c NaN NaN
2 NaN NaN 3.0 4.0
默认情况下,如果两个对应的值相等,它们将显示为NaN
。此外,如果整个行/列中的所有值都将从结果中省略。其余差异将在列上对齐。
如果需要,可以选择将差异堆叠在行上。
In [152]: df.compare(df2, align_axis=0)
Out[152]:
col1 col3
0 self a NaN
other c NaN
2 self NaN 3.0
other NaN 4.0
如果您希望保留所有原始行和列,请将keep_shape参数设置为True
。
In [153]: df.compare(df2, keep_shape=True)
Out[153]:
col1 col2 col3
self other self other self other
0 a c NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN 3.0 4.0
3 NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN NaN
您也可以保留所有原始值,即使它们相等。