I有一个数据帧df
:
A B C D E
0 string1 string4 [string7, string10, ...] [string11, string 12] [string15, ...]
1 string2 string5 [string8, ...] [string13, ...] [string16, ...]
2 string3 string6 [string9, ...] [string14, ...] [string17, ...]
以及列表to_delete
:
to_delete = [string7, string9, string17]
我想删除数据帧中包含to_delete
中字符串的所有行,但我不知道如何将.isin
与列表一起使用。
您可以通过以下步骤进行处理:
-
您可以在每个列/元素上使用
pd.Series.explode()
将字符串列表扩展为多行,每行仅包含字符串(所有列表都已扩展/分解为行(。 -
然后使用
.isin()
检查数据帧中to_delete
列表中的字符串。 -
按索引级别0分组(包含分解前的原始行索引(,将匹配结果的多行聚合并汇总回一行(使用
groupby()
下的.sum()
(。 -
然后
.sum(axis=1)
逐行检查要删除的任何匹配字符串。 -
检查0匹配的行(要保留的行(,并形成结果行的布尔索引。
-
最后,使用
.loc
过滤不匹配的行以保留。
df.loc[df.apply(pd.Series.explode).isin(to_delete).groupby(level=0).sum().sum(axis=1).eq(0)]
结果:
A B C D E
1 string2 string5 [string8] [string13] [string16]
可以根据以下代码构建原始数据帧进行测试:
data = {'A': ['string1', 'string2', 'string3'],
'B': ['string4', 'string5', 'string6'],
'C': [['string7', 'string10'], ['string8'], ['string9']],
'D': [['string11', 'string 12'], ['string13'], ['string14']],
'E': [['string15'], ['string16'], ['string17']]}
df = pd.DataFrame(data)
我看到已经有几个答案了,但如果可以的话,我想加入我的答案。我认为SeaBean的解决方案也是正确的,我喜欢它,与其他提出的解决方案不同,你不必指定哪些列有列表。
我无法像SeaBean一样解释我的解决方案,因为这是我不久前遇到的东西,我诚实地通过反复试验得出了它。
d = {'A':['string1','string2','string3'],
'B':['string4','string5','string6'],
'C':[['string7','string10'],['string8'],['string9']],
'D':[['string11','string12'],['string13'],['string14']]
'E':[['string15'],['string16'],['string17']]}
df = pd.DataFrame(data=d);
res = df[~df.stack().explode().isin(to_delete).any(level=0)]
我试着用更多的项目来计时,似乎我的解决方案稍微好一点。
d = {'A':['string1','string2','string3']*10000, 'B':['string4','string5','string6']*10000, 'C':[['string7','string10'],['string8'],['string9']]*10000,'D':[['string11','string12'],['string13'],['string14']]*10000,'E':[['string15'],['string16'],['string17']]*10000}
df = pd.DataFrame(data=d);
to_delete = ['string7', 'string9', 'string17']
start = time.time()
print(df[~df.stack().explode().isin(to_delete).any(level=0)])
end = time.time()
print(end - start)
start = time.time()
print(df.loc[df.apply(pd.Series.explode).isin(to_delete).groupby(level=0).sum().sum(axis=1).eq(0)])
end = time.time()
print(end - start)
输出:
A B C D E
1 string2 string5 [string8] [string13] [string16]
4 string2 string5 [string8] [string13] [string16]
7 string2 string5 [string8] [string13] [string16]
10 string2 string5 [string8] [string13] [string16]
13 string2 string5 [string8] [string13] [string16]
... ... ... ... ... ...
29986 string2 string5 [string8] [string13] [string16]
29989 string2 string5 [string8] [string13] [string16]
29992 string2 string5 [string8] [string13] [string16]
29995 string2 string5 [string8] [string13] [string16]
29998 string2 string5 [string8] [string13] [string16]
[10000 rows x 5 columns]
0.08804535865783691
A B C D E
1 string2 string5 [string8] [string13] [string16]
4 string2 string5 [string8] [string13] [string16]
7 string2 string5 [string8] [string13] [string16]
10 string2 string5 [string8] [string13] [string16]
13 string2 string5 [string8] [string13] [string16]
... ... ... ... ... ...
29986 string2 string5 [string8] [string13] [string16]
29989 string2 string5 [string8] [string13] [string16]
29992 string2 string5 [string8] [string13] [string16]
29995 string2 string5 [string8] [string13] [string16]
29998 string2 string5 [string8] [string13] [string16]
[10000 rows x 5 columns]
0.11187744140625
我能想到的最糟糕的解决方案是使用apply
:
g = lambda x: (set(to_delete) - df[x].apply(set)).str.len()==len(to_delete)
res = df[g('C') & g('D') & g('E')]
df(输入(:
A B C D E
0 string1 string4 [string7, string10] [string11, string12] [string15]
1 string2 string5 [string8] [string13] [string16]
2 string3 string6 [string9] [string14] [string17]
res:
A B C D E
1 string2 string5 [string8] [string13] [string16]