我在数据框df1
的一列中有一个列表,我想检查每一行是否该列表的所有元素都在第二个数据框df2
中的另一列中。
两个数据框是这样的:
df1 df2
id | members | num | available |
1 |['a',b'] | one | ['a','b','c','d','e']|
2 |['b'] | two | ['a','b'] |
3 |['a','b','c'] | three| ['b','d','e'] |
我正在尝试提出一种方法,该方法可以为df2
中的每一行提供members
中哪些行具有df1
的所有元素。也许是这样的:
id | members | which_cols |
1 |['a',b'] | ['one','two'] |
2 |['b'] | ['one','two','three'] |
3 |['a','b','c'] | ['one'] |
我认为将其转换为{k: list(v) for k,v in df1.groupby("id")["members"]}
和{i: list(j) for i,j in df2.groupby("num")["available"]}
之类的字典可能会使其更灵活地实现所需的输出,但仍然找不到找到我要寻找的方法。
df2
将具有约300
行,其中available
的长度与25,000
一样大。而且df1
可以和1M
行一样大,列表长度在members
中最多为15。因此,我认为效率也很重要。
答案 0 :(得分:0)
问题的核心在于您的数据设置。如果您进行一些预处理,则可以避免繁琐地遍历每个列表多次。
df1 = pd.Series([['a', 'b'], ['b'], ['a', 'b', 'c']], name = 'members').to_frame()
df2 = pd.Series([['a', 'b', 'c', 'd', 'e'],
['a', 'b'],
['b', 'd', 'e']], name = 'available').to_frame()
df2.index = ['one', 'two', 'three']
>>> df1
members
0 ['a', 'b']
1 ['b']
2 ['a', 'b', 'c']
>>> df2
available
one. ['a', 'b', 'c', 'd', 'e']
two ['a', 'b']
three ['b', 'd', 'e']
如果您在处理数据之前先对数据进行一次编码,那么您在进行子集检查方面将有很大的优势:
# You can do this many ways, but sklearn makes this very easy with:
from sklearn.preprocessing import MultiLabelBinarizer
mlb = MultiLabelBinarizer()
df1 = df1.join(pd.DataFrame(mlb.fit_transform(df1.pop('members')),
columns=mlb.classes_, index=df1.index))
mlb = MultiLabelBinarizer()
df2 = df2.join(pd.DataFrame(mlb.fit_transform(df2.pop('available')),
columns=mlb.classes_, index=df2.index))
>>> df1
a b c
0 1 1 0
1 0 1 0
2 1 1 1
>>> df2
a b c d e
one 1 1 1 1 1
two 1 1 0 0 0
three 0 1 0 1 1
关于这种数据格式的聪明之处在于,现在您可以从df1
中减去df2
,并且如果所有结果值都不为-1(表明{{1}中没有元素},然后将其添加到列表中。您可以将其视为覆盖两个数据帧(对齐每个资源)然后减去。当然,可以将其向量化:
df2