反加入 Pandas
Anti-Join Pandas
我有两个 tables,我想附加它们,以便只保留 table A 中的所有数据,并且仅在 table B 中添加数据键是唯一的(键值在 table A 和 B 中是唯一的,但在某些情况下,一个键将同时出现在 table A 和 B 中)。
我认为执行此操作的方法将涉及某种过滤连接(反连接)以获取 table B 中未出现在 table A 中的值,然后附加两个 tables。
我熟悉 R,这是我在 R 中用来执行此操作的代码。
library("dplyr")
## Filtering join to remove values already in "TableA" from "TableB"
FilteredTableB <- anti_join(TableB,TableA, by = "Key")
## Append "FilteredTableB" to "TableA"
CombinedTable <- bind_rows(TableA,FilteredTableB)
我如何在 python 中实现这一目标?
考虑以下数据帧
TableA = pd.DataFrame(np.random.rand(4, 3),
pd.Index(list('abcd'), name='Key'),
['A', 'B', 'C']).reset_index()
TableB = pd.DataFrame(np.random.rand(4, 3),
pd.Index(list('aecf'), name='Key'),
['A', 'B', 'C']).reset_index()
TableA
TableB
这是做你想做的事情的一种方法
方法一
# Identify what values are in TableB and not in TableA
key_diff = set(TableB.Key).difference(TableA.Key)
where_diff = TableB.Key.isin(key_diff)
# Slice TableB accordingly and append to TableA
TableA.append(TableB[where_diff], ignore_index=True)
方法二
rows = []
for i, row in TableB.iterrows():
if row.Key not in TableA.Key.values:
rows.append(row)
pd.concat([TableA.T] + rows, axis=1).T
时机
4 行有 2 个重叠
方法 1 更快
10,000 行 5,000 重叠
循环不好
您将拥有两个表 TableA
和 TableB
,这样两个 DataFrame
对象在各自的表中都有具有唯一值的列,但某些列可能具有同时出现的值(一行具有相同的值)在两个表中。
然后,我们要将 TableA
中的行与 TableB
中不匹配 TableA
中任何 'Key' 列的行合并。这个概念是将其描绘为比较两个可变长度系列,如果 sB
的值不匹配 [=18],则将一个系列 sA
中的行与另一个 sB
中的行组合=]的。下面的代码解决了这个练习:
import pandas as pd
TableA = pd.DataFrame([[2, 3, 4], [5, 6, 7], [8, 9, 10]])
TableB = pd.DataFrame([[1, 3, 4], [5, 7, 8], [9, 10, 0]])
removeTheseIndexes = []
keyColumnA = TableA.iloc[:,1] # your 'Key' column here
keyColumnB = TableB.iloc[:,1] # same
for i in range(0, len(keyColumnA)):
firstValue = keyColumnA[i]
for j in range(0, len(keyColumnB)):
copycat = keyColumnB[j]
if firstValue == copycat:
removeTheseIndexes.append(j)
TableB.drop(removeTheseIndexes, inplace = True)
TableA = TableA.append(TableB)
TableA = TableA.reset_index(drop=True)
请注意,这也会影响 TableB
的数据。您可以使用 inplace=False
并将其重新分配给 newTable
,然后 TableA.append(newTable)
或者
# Table A
0 1 2
0 2 3 4
1 5 6 7
2 8 9 10
# Table B
0 1 2
0 1 3 4
1 5 7 8
2 9 10 0
# Set 'Key' column = 1
# Run the script after the loop
# Table A
0 1 2
0 2 3 4
1 5 6 7
2 8 9 10
3 5 7 8
4 9 10 0
# Table B
0 1 2
1 5 7 8
2 9 10 0
我遇到了同样的问题。 using how='outer'
and indicator=True
of merge 启发了我想出这个解决方案:
import pandas as pd
import numpy as np
TableA = pd.DataFrame(np.random.rand(4, 3),
pd.Index(list('abcd'), name='Key'),
['A', 'B', 'C']).reset_index()
TableB = pd.DataFrame(np.random.rand(4, 3),
pd.Index(list('aecf'), name='Key'),
['A', 'B', 'C']).reset_index()
print('TableA', TableA, sep='\n')
print('TableB', TableB, sep='\n')
TableB_only = pd.merge(
TableA, TableB,
how='outer', on='Key', indicator=True, suffixes=('_foo','')).query(
'_merge == "right_only"')
print('TableB_only', TableB_only, sep='\n')
Table_concatenated = pd.concat((TableA, TableB_only), join='inner')
print('Table_concatenated', Table_concatenated, sep='\n')
打印此输出:
TableA
Key A B C
0 a 0.035548 0.344711 0.860918
1 b 0.640194 0.212250 0.277359
2 c 0.592234 0.113492 0.037444
3 d 0.112271 0.205245 0.227157
TableB
Key A B C
0 a 0.754538 0.692902 0.537704
1 e 0.499092 0.864145 0.004559
2 c 0.082087 0.682573 0.421654
3 f 0.768914 0.281617 0.924693
TableB_only
Key A_foo B_foo C_foo A B C _merge
4 e NaN NaN NaN 0.499092 0.864145 0.004559 right_only
5 f NaN NaN NaN 0.768914 0.281617 0.924693 right_only
Table_concatenated
Key A B C
0 a 0.035548 0.344711 0.860918
1 b 0.640194 0.212250 0.277359
2 c 0.592234 0.113492 0.037444
3 d 0.112271 0.205245 0.227157
4 e 0.499092 0.864145 0.004559
5 f 0.768914 0.281617 0.924693
最简单的答案:
tableB = pd.concat([tableB, pd.Series(1)], axis=1)
mergedTable = tableA.merge(tableB, how="left" on="key")
answer = mergedTable[mergedTable.iloc[:,-1].isnull()][tableA.columns.tolist()]
应该也是最快的提议。
根据其他建议之一,这里有一个函数可以执行此操作。仅使用 pandas 函数,没有循环。您也可以使用多列作为键。如果将行 output = merged.loc[merged.dummy_col.isna(),tableA.columns.tolist()]
更改为 output = merged.loc[~merged.dummy_col.isna(),tableA.columns.tolist()]
,您将得到一个 semi_join。
def anti_join(tableA,tableB,on):
#if joining on index, make it into a column
if tableB.index.name is not None:
dummy = tableB.reset_index()[on]
else:
dummy = tableB[on]
#create a dummy columns of 1s
if isinstance(dummy, pd.Series):
dummy = dummy.to_frame()
dummy.loc[:,'dummy_col'] = 1
#preserve the index of tableA if it has one
if tableA.index.name is not None:
idx_name = tableA.index.name
tableA = tableA.reset_index(drop = False)
else:
idx_name = None
#do a left-join
merged = tableA.merge(dummy,on=on,how='left')
#keep only the non-matches
output = merged.loc[merged.dummy_col.isna(),tableA.columns.tolist()]
#reset the index (if applicable)
if idx_name is not None:
output = output.set_index(idx_name)
return(output)
indicator = True
in merge
命令将通过创建具有三个可能值的新列 _merge
来告诉您应用了哪个连接:
left_only
right_only
both
保留 right_only
和 left_only
。就是这样。
outer_join = TableA.merge(TableB, how = 'outer', indicator = True)
anti_join = outer_join[~(outer_join._merge == 'both')].drop('_merge', axis = 1)
简单!
这是与 piRSquared 的解决方案的比较:
1) 当运行在此示例中基于一列进行匹配时,piRSquared 的解决方案更快。
2) 但它只适用于匹配一列。如果您想匹配多列 - 我的解决方案与一列一样有效。
所以由你来决定。
一个班轮
TableA.append(TableB.loc[~TableB.Key.isin(TableA.Key)], ignore_index=True)
%%timeit
给出的时间与接受的答案大致相同。
我有两个 tables,我想附加它们,以便只保留 table A 中的所有数据,并且仅在 table B 中添加数据键是唯一的(键值在 table A 和 B 中是唯一的,但在某些情况下,一个键将同时出现在 table A 和 B 中)。
我认为执行此操作的方法将涉及某种过滤连接(反连接)以获取 table B 中未出现在 table A 中的值,然后附加两个 tables。
我熟悉 R,这是我在 R 中用来执行此操作的代码。
library("dplyr")
## Filtering join to remove values already in "TableA" from "TableB"
FilteredTableB <- anti_join(TableB,TableA, by = "Key")
## Append "FilteredTableB" to "TableA"
CombinedTable <- bind_rows(TableA,FilteredTableB)
我如何在 python 中实现这一目标?
考虑以下数据帧
TableA = pd.DataFrame(np.random.rand(4, 3),
pd.Index(list('abcd'), name='Key'),
['A', 'B', 'C']).reset_index()
TableB = pd.DataFrame(np.random.rand(4, 3),
pd.Index(list('aecf'), name='Key'),
['A', 'B', 'C']).reset_index()
TableA
TableB
这是做你想做的事情的一种方法
方法一
# Identify what values are in TableB and not in TableA
key_diff = set(TableB.Key).difference(TableA.Key)
where_diff = TableB.Key.isin(key_diff)
# Slice TableB accordingly and append to TableA
TableA.append(TableB[where_diff], ignore_index=True)
方法二
rows = []
for i, row in TableB.iterrows():
if row.Key not in TableA.Key.values:
rows.append(row)
pd.concat([TableA.T] + rows, axis=1).T
时机
4 行有 2 个重叠
方法 1 更快
10,000 行 5,000 重叠
循环不好
您将拥有两个表 TableA
和 TableB
,这样两个 DataFrame
对象在各自的表中都有具有唯一值的列,但某些列可能具有同时出现的值(一行具有相同的值)在两个表中。
然后,我们要将 TableA
中的行与 TableB
中不匹配 TableA
中任何 'Key' 列的行合并。这个概念是将其描绘为比较两个可变长度系列,如果 sB
的值不匹配 [=18],则将一个系列 sA
中的行与另一个 sB
中的行组合=]的。下面的代码解决了这个练习:
import pandas as pd
TableA = pd.DataFrame([[2, 3, 4], [5, 6, 7], [8, 9, 10]])
TableB = pd.DataFrame([[1, 3, 4], [5, 7, 8], [9, 10, 0]])
removeTheseIndexes = []
keyColumnA = TableA.iloc[:,1] # your 'Key' column here
keyColumnB = TableB.iloc[:,1] # same
for i in range(0, len(keyColumnA)):
firstValue = keyColumnA[i]
for j in range(0, len(keyColumnB)):
copycat = keyColumnB[j]
if firstValue == copycat:
removeTheseIndexes.append(j)
TableB.drop(removeTheseIndexes, inplace = True)
TableA = TableA.append(TableB)
TableA = TableA.reset_index(drop=True)
请注意,这也会影响 TableB
的数据。您可以使用 inplace=False
并将其重新分配给 newTable
,然后 TableA.append(newTable)
或者
# Table A
0 1 2
0 2 3 4
1 5 6 7
2 8 9 10
# Table B
0 1 2
0 1 3 4
1 5 7 8
2 9 10 0
# Set 'Key' column = 1
# Run the script after the loop
# Table A
0 1 2
0 2 3 4
1 5 6 7
2 8 9 10
3 5 7 8
4 9 10 0
# Table B
0 1 2
1 5 7 8
2 9 10 0
我遇到了同样的问题。 how='outer'
and indicator=True
of merge 启发了我想出这个解决方案:
import pandas as pd
import numpy as np
TableA = pd.DataFrame(np.random.rand(4, 3),
pd.Index(list('abcd'), name='Key'),
['A', 'B', 'C']).reset_index()
TableB = pd.DataFrame(np.random.rand(4, 3),
pd.Index(list('aecf'), name='Key'),
['A', 'B', 'C']).reset_index()
print('TableA', TableA, sep='\n')
print('TableB', TableB, sep='\n')
TableB_only = pd.merge(
TableA, TableB,
how='outer', on='Key', indicator=True, suffixes=('_foo','')).query(
'_merge == "right_only"')
print('TableB_only', TableB_only, sep='\n')
Table_concatenated = pd.concat((TableA, TableB_only), join='inner')
print('Table_concatenated', Table_concatenated, sep='\n')
打印此输出:
TableA
Key A B C
0 a 0.035548 0.344711 0.860918
1 b 0.640194 0.212250 0.277359
2 c 0.592234 0.113492 0.037444
3 d 0.112271 0.205245 0.227157
TableB
Key A B C
0 a 0.754538 0.692902 0.537704
1 e 0.499092 0.864145 0.004559
2 c 0.082087 0.682573 0.421654
3 f 0.768914 0.281617 0.924693
TableB_only
Key A_foo B_foo C_foo A B C _merge
4 e NaN NaN NaN 0.499092 0.864145 0.004559 right_only
5 f NaN NaN NaN 0.768914 0.281617 0.924693 right_only
Table_concatenated
Key A B C
0 a 0.035548 0.344711 0.860918
1 b 0.640194 0.212250 0.277359
2 c 0.592234 0.113492 0.037444
3 d 0.112271 0.205245 0.227157
4 e 0.499092 0.864145 0.004559
5 f 0.768914 0.281617 0.924693
最简单的答案:
tableB = pd.concat([tableB, pd.Series(1)], axis=1)
mergedTable = tableA.merge(tableB, how="left" on="key")
answer = mergedTable[mergedTable.iloc[:,-1].isnull()][tableA.columns.tolist()]
应该也是最快的提议。
根据其他建议之一,这里有一个函数可以执行此操作。仅使用 pandas 函数,没有循环。您也可以使用多列作为键。如果将行 output = merged.loc[merged.dummy_col.isna(),tableA.columns.tolist()]
更改为 output = merged.loc[~merged.dummy_col.isna(),tableA.columns.tolist()]
,您将得到一个 semi_join。
def anti_join(tableA,tableB,on):
#if joining on index, make it into a column
if tableB.index.name is not None:
dummy = tableB.reset_index()[on]
else:
dummy = tableB[on]
#create a dummy columns of 1s
if isinstance(dummy, pd.Series):
dummy = dummy.to_frame()
dummy.loc[:,'dummy_col'] = 1
#preserve the index of tableA if it has one
if tableA.index.name is not None:
idx_name = tableA.index.name
tableA = tableA.reset_index(drop = False)
else:
idx_name = None
#do a left-join
merged = tableA.merge(dummy,on=on,how='left')
#keep only the non-matches
output = merged.loc[merged.dummy_col.isna(),tableA.columns.tolist()]
#reset the index (if applicable)
if idx_name is not None:
output = output.set_index(idx_name)
return(output)
indicator = True
in merge
命令将通过创建具有三个可能值的新列 _merge
来告诉您应用了哪个连接:
left_only
right_only
both
保留 right_only
和 left_only
。就是这样。
outer_join = TableA.merge(TableB, how = 'outer', indicator = True)
anti_join = outer_join[~(outer_join._merge == 'both')].drop('_merge', axis = 1)
简单!
这是与 piRSquared 的解决方案的比较:
1) 当运行在此示例中基于一列进行匹配时,piRSquared 的解决方案更快。
2) 但它只适用于匹配一列。如果您想匹配多列 - 我的解决方案与一列一样有效。
所以由你来决定。
一个班轮
TableA.append(TableB.loc[~TableB.Key.isin(TableA.Key)], ignore_index=True)
%%timeit
给出的时间与接受的答案大致相同。