如果某些行值与第二个DataFrame中的行值相同,则从DataFrame中有效地删除行

时间:2018-11-21 15:03:20

标签: python pandas performance dataframe

import pandas as pd

df1 = pd.DataFrame({'id':   [ 1,  1,  1,  2,  2,  2,  3,  3,  3], 
                    'nr':   [91, 92, 93, 91, 92, 93, 91, 92, 93], 
                    'val_a':[22, 23, 24, 33, 34, 35, 44, 43, 42]})

df2 = pd.DataFrame({'id':   [ 1,  1,  2,  3,  4,  4,  3,  5], 
                    'nr':   [91, 92, 91, 99, 92, 93, 92, 99], 
                    'val_a':[72, 27, 74, 83, 84, 85, 84, 83]})

def eliminate1 (): 
    for i1, row1 in df1.iterrows():
        for i2, row2 in df2.iterrows():
            if row1['id'] == row2['id'] and row1['nr'] == row2['nr']:
                df1.drop(i1, inplace=True)
    df1.reset_index(drop=True, inplace=True)
    print(df1)

eliminate1()

我想删除df1中的所有行,其中“ id”和“ nr”在df2的任何行中都具有相等的值。 exclude1()效果很好,请参见下面的结果,但是对于大型数据集而言非常慢。

这是df1和df2:

   id  nr  val_a
0   1  91     22
1   1  92     23
2   1  93     24
3   2  91     33
4   2  92     34
5   2  93     35
6   3  91     44
7   3  92     43
8   3  93     42 

   id  nr  val_a
0   1  91     72
1   1  92     27
2   2  91     74
3   3  99     83
4   4  92     84
5   4  93     85
6   3  92     84
7   5  99     83 

这里的结果应该像这样:

   id  nr  val_a
0   1  93     24
1   2  92     34
2   2  93     35
3   3  91     44
4   3  93     42

有人知道如何编写更快的代码和/或使用现有功能吗?

3 个答案:

答案 0 :(得分:4)

merge

您可以将mergeindicator=True一起使用,并仅包括标记为'left_only'的行。

res = df1.merge(df2.drop('val_a', 1), how='left', on=['id', 'nr'], indicator=True)
res = res.loc[res['_merge'] == 'left_only'].drop('_merge', 1)

print(res)

   id  nr  val_a
2   1  93     24
4   2  92     34
5   2  93     35
6   3  91     44
8   3  93     42

根据'left_only''right_only''both',该解决方案可轻松适应任何条件。

答案 1 :(得分:4)

方法1 isin在将merge列压缩到tuple

之后
df1[~df1[['id','nr']].apply(tuple,1).isin(df2[['id','nr']].apply(tuple,1))]
Out[43]: 
   id  nr  val_a
2   1  93     24
4   2  92     34
5   2  93     35
6   3  91     44
8   3  93     42

方法2 numpy广播

s1=df1[['id','nr']].values
s2=df2[['id','nr']].values
df1[~np.any(np.all(s1==s2[:,None],-1),0)]
Out[64]: 
   id  nr  val_a
2   1  93     24
4   2  92     34
5   2  93     35
6   3  91     44
8   3  93     42

我的方法计时

%timeit df1[~df1[['id','nr']].apply(tuple,1).isin(df2[['id','nr']].apply(tuple,1))]
100 loops, best of 3: 3.67 ms per loop
def m2():
    s1 = df1[['id', 'nr']].values
    s2 = df2[['id', 'nr']].values
    return df1[~np.any(np.all(s1 == s2[:, None], -1), 0)]
%timeit m2()
1000 loops, best of 3: 926 µs per loop

答案 2 :(得分:1)

inner join是否可以解决您的问题?获取匹配条件的参数索引,然后将其过滤掉。之后,您只需要reset_index()即可。

df3 = df1.merge(df2, how = 'inner', on = ['id','nr']).reset_index()
id_list = df3['id'].tolist()
df4 = df1[~df1['id'].isin(id_list)]