熊猫获取不在其他数据框中的行


228

我有两个大熊猫数据框,它们有一些共同点。

假设dataframe2是dataframe1的子集。

如何获取dataframe1中不在dataframe2中的行?

df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5], 'col2' : [10, 11, 12, 13, 14]}) 
df2 = pandas.DataFrame(data = {'col1' : [1, 2, 3], 'col2' : [10, 11, 12]})

1
@TedPetrou我看不到您提供的答案是正确的答案。如果我有两个数据框,其中一个是另一个的子集,则需要删除所有在子集中的那些行。我不想删除重复项。我完全想删除该子集。
点唱机

Answers:


172

一种方法是存储两个dfs内部合并的结果,然后我们可以简单地选择当一列的值不在此通用值中时的行:

In [119]:

common = df1.merge(df2,on=['col1','col2'])
print(common)
df1[(~df1.col1.isin(common.col1))&(~df1.col2.isin(common.col2))]
   col1  col2
0     1    10
1     2    11
2     3    12
Out[119]:
   col1  col2
3     4    13
4     5    14

编辑

您发现的另一种方法是使用isin它将产生NaN可删除的行:

In [138]:

df1[~df1.isin(df2)].dropna()
Out[138]:
   col1  col2
3     4    13
4     5    14

但是,如果df2不能以相同的方式开始行,那么它将行不通:

df2 = pd.DataFrame(data = {'col1' : [2, 3,4], 'col2' : [11, 12,13]})

将产生整个df:

In [140]:

df1[~df1.isin(df2)].dropna()
Out[140]:
   col1  col2
0     1    10
1     2    11
2     3    12
3     4    13
4     5    14

13
df1[~df1.isin(df2)].dropna(how = 'all')似乎可以解决问题。无论如何,谢谢-您的回答帮助我找到了解决方案。
想好事

5
请注意,使用isin要求两个df都以相同的行值开头,因此例如,如果df2 df2 = pd.DataFrame(data = {'col1' : [2, 3,4], 'col2' : [11,12, 13]})则您的方法将不起作用
EdChum 2015年

2
这将所有整数转换为浮点数!
克里斯·尼尔森

3
@SergeyZakharov大约3年前发布的这个答案对OP来说是正确的,对于他们的问题,另一个答案是一个更好的答案,可以处理一个更广泛的问题,而这个问题从来都不是原始问题的一部分,这是不正确的答案是错误的,鉴于存在的问题,这是正确的。另外,有人对此投票没有解释,我几乎无能为力,因为这是一个可以接受的答案,OP并没有改变主意,我也不会蚕食其他答案以使其正确
EdChum

1
@Cecilia,您需要传递keep=Falsedf0.append(df1).drop_duplicates(keep=False),默认情况下,它保留第一个重复项,您想删除所有重复项
EdChum

189

当前选择的解决方案产生不正确的结果。为了正确解决此问题,我们可以执行从df1到的左联接df2,确保首先仅获得的唯一行df2

首先,我们需要修改原始DataFrame以添加包含数据的行[3,10]。

df1 = pd.DataFrame(data = {'col1' : [1, 2, 3, 4, 5, 3], 
                           'col2' : [10, 11, 12, 13, 14, 10]}) 
df2 = pd.DataFrame(data = {'col1' : [1, 2, 3],
                           'col2' : [10, 11, 12]})

df1

   col1  col2
0     1    10
1     2    11
2     3    12
3     4    13
4     5    14
5     3    10

df2

   col1  col2
0     1    10
1     2    11
2     3    12

执行左联接,消除中的重复项,df2以便df1联接的每一行都恰好有1行df2。使用参数indicator返回额外的一列,指示该行来自哪个表。

df_all = df1.merge(df2.drop_duplicates(), on=['col1','col2'], 
                   how='left', indicator=True)
df_all

   col1  col2     _merge
0     1    10       both
1     2    11       both
2     3    12       both
3     4    13  left_only
4     5    14  left_only
5     3    10  left_only

创建一个布尔条件:

df_all['_merge'] == 'left_only'

0    False
1    False
2    False
3     True
4     True
5     True
Name: _merge, dtype: bool

为什么其他解决方案是错误的

一些解决方案会犯同样的错误-他们仅检查每个值在每一列中是否独立,而不是在同一行中。添加最后一行,这是唯一的,但具有两列中的值,则会显示df2以下错误:

common = df1.merge(df2,on=['col1','col2'])
(~df1.col1.isin(common.col1))&(~df1.col2.isin(common.col2))
0    False
1    False
2    False
3     True
4     True
5    False
dtype: bool

此解决方案得到相同的错误结果:

df1.isin(df2.to_dict('l')).all(1)

2
但是,我想,他们假设col1是唯一的,是一个索引(问题中没有提到,但是很明显)。因此,如果永远不会出现这样的情况,即col1的相同值有两个col2的值(不能有两个col1 = 3行),那么上面的答案是正确的。
pashute

14
这当然不是很明显,所以您的观点是无效的。我的解决方案适用于更多情况。
泰德·彼得鲁

问题,创建切片而不是布尔数组会更容易吗?由于目标是获取行。
马蒂亚斯·罗莫

5
使用df_all[df_all['_merge'] == 'left_only']具有结果的df
gies0r

77

假设索引在数据框中是一致的(不考虑实际col值):

df1[~df1.index.isin(df2.index)]

1
@ChrisNielsen条件否定。因此,在此示例中,它的意思是“获取df1没有索引的行df2.index”。有关否定的更多信息:stackoverflow.com/q/19960077/304209(令人惊讶的是,我在熊猫文档中找不到任何关于波浪号的提法)。
丹尼斯·哥洛马佐夫'17

似乎df的长度必须相同,不是吗?我得到了ValueError: Item wrong length x instead of y.
wordsforthewise

@wordsforthewise不,他们没有。掩码的长度为df1,并且也应用于df1。你能提供你的例子吗?
丹尼斯·哥洛马佐夫

要更正项目长度问题,您应该添加.loc
Moreno,

13

如前所述,isin要求列和索引必须相同才能匹配。如果匹配仅在行内容上,则获得用于过滤存在的行的掩码的一种方法是将行转换为(Multi)Index:

In [77]: df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5, 3], 'col2' : [10, 11, 12, 13, 14, 10]})
In [78]: df2 = pandas.DataFrame(data = {'col1' : [1, 3, 4], 'col2' : [10, 12, 13]})
In [79]: df1.loc[~df1.set_index(list(df1.columns)).index.isin(df2.set_index(list(df2.columns)).index)]
Out[79]:
   col1  col2
1     2    11
4     5    14
5     3    10

如果应考虑索引,则set_index具有关键字参数append,以将列附加到现有索引。如果列未对齐,则可以用列规范替换list(df.columns)以对齐数据。

pandas.MultiIndex.from_tuples(df<N>.to_records(index = False).tolist())

可以替代地使用它来创建索引,尽管我怀疑这样做效率更高。


@ Dev_123删除〜开头。核心是创建一个谓词列表,以确定df1中的行是否也出现在df2中,因此df1中的行不是df1唯一的,这就否定了df1中的行是否不出现在df2中的谓词列表。
符文·林索

11

假设您有两个具有多个字段(column_names)的数据帧df_1和df_2,并且您要基于某些字段(例如,fields_x,fields_y)查找df_1中唯一不在df_2中的那些条目,请执行以下步骤。

步骤1.将列key1和key2分别添加到df_1和df_2。

步骤2合并数据框,如下所示。field_x和field_y是我们想要的列。

第三步:仅从df_1中选择key1不等于key2的那些行。

步骤4放下key1和key2。

这种方法将解决您的问题,即使使用大数据集也可以快速运行。我已经尝试将其用于具有超过1,000,000行的数据帧。

df_1['key1'] = 1
df_2['key2'] = 1
df_1 = pd.merge(df_1, df_2, on=['field_x', 'field_y'], how = 'left')
df_1 = df_1[~(df_1.key2 == df_1.key1)]
df_1 = df_1.drop(['key1','key2'], axis=1)

从技术上讲,我认为这不是他想要的-他想知道哪些行是哪个df唯一的。但是,我认为此解决方案会返回第一个df或第二个df唯一的df行。
合法堆栈


3

您可以使用isin(dict)方法进行操作:

In [74]: df1[~df1.isin(df2.to_dict('l')).all(1)]
Out[74]:
   col1  col2
3     4    13
4     5    14

说明:

In [75]: df2.to_dict('l')
Out[75]: {'col1': [1, 2, 3], 'col2': [10, 11, 12]}

In [76]: df1.isin(df2.to_dict('l'))
Out[76]:
    col1   col2
0   True   True
1   True   True
2   True   True
3  False  False
4  False  False

In [77]: df1.isin(df2.to_dict('l')).all(1)
Out[77]:
0     True
1     True
2     True
3    False
4    False
dtype: bool

这会产生错误的结果。请参阅下面的说明。
泰德·彼得鲁

2

您也可以Concat的df1df2

x = pd.concat([df1, df2])

然后删除所有重复项:

y = x.drop_duplicates(keep=False, inplace=False)

欢迎使用StackOverflow:如果您发布代码,XML或数据示例,请在文本编辑器中突出显示这些行,然后单击编辑器工具栏上的“代码示例”按钮({})或使用键盘上的Ctrl + K进行格式化和语法突出显示它!
WhatsThePoint

4
这将返回任一组中的所有数据,而不仅仅是返回仅df1中的数据。
杰米·马歇尔'18

1

这个怎么样:

df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5], 
                               'col2' : [10, 11, 12, 13, 14]}) 
df2 = pandas.DataFrame(data = {'col1' : [1, 2, 3], 
                               'col2' : [10, 11, 12]})
records_df2 = set([tuple(row) for row in df2.values])
in_df2_mask = np.array([tuple(row) in records_df2 for row in df1.values])
result = df1[~in_df2_mask]

1

这是解决此问题的另一种方法:

df1[~df1.index.isin(df1.merge(df2, how='inner', on=['col1', 'col2']).index)]

要么:

df1.loc[df1.index.difference(df1.merge(df2, how='inner', on=['col1', 'col2']).index)]

0

我这样做的方法涉及添加一个数据框唯一的新列,并使用此列选择是否保留条目

df2[col3] = 1
df1 = pd.merge(df_1, df_2, on=['field_x', 'field_y'], how = 'outer')
df1['Empt'].fillna(0, inplace=True)

这使得df1中的每个条目都有一个代码-如果它对于df1是唯一的,则为0;如果在两个dataFrames中都是唯一的,则为1。然后,您可以使用它来限制所需的内容

answer = nonuni[nonuni['Empt'] == 0]

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.