Python 查找以列表形式存在的列元素的数据帧索引的最快方法

Python 查找以列表形式存在的列元素的数据帧索引的最快方法,python,pandas,list,dictionary,indexing,Python,Pandas,List,Dictionary,Indexing,我有一个数据框,其中列值作为列表存在。每个列表都有几个元素,一个元素可以存在于多行中。数据帧示例如下: X = pd.DataFrame([(1,['a','b','c']),(2,['a','b']),(3,['c','d'])],columns=['A','B']) X = A B 0 1 [a, b, c] 1 2 [a, b] 2 3 [c, d] 我想找到与列表中的元素对应的所有行,即dataframe索引,并从中创建一个字典。忽略这里的A列

我有一个数据框,其中列值作为列表存在。每个列表都有几个元素,一个元素可以存在于多行中。数据帧示例如下:

X = pd.DataFrame([(1,['a','b','c']),(2,['a','b']),(3,['c','d'])],columns=['A','B'])

X = 
 A          B
0  1  [a, b, c]
1  2  [a, b]
2  3     [c, d]
我想找到与列表中的元素对应的所有行,即dataframe索引,并从中创建一个字典。忽略这里的A列,因为B列是感兴趣的!所以元素“a”出现在索引0,1中,它给出了{a':[0,1]}。此示例数据帧的解决方案是:

Y = {'a':[0,1],'b':[0,1],'c':[0,2],'d':[2]}
我已经编写了一个工作良好的代码,我可以得到一个结果。我的问题更多地与计算速度有关。我的实际数据帧大约有350000行,列“B”中的列表最多可以包含1000个元素。但目前代码运行了几个小时!我想知道我的解决方案是否效率很低。 如果您能以更快、更高效的方式提供帮助,我们将不胜感激! 以下是我的解决方案代码:

import itertools
import pandas as pd
X = pd.DataFrame([(1,['a','b','c']),(2,['a','b']),(3,['c','d'])],columns=['A','B'])
B_dict = []
for idx,val in X.iterrows():
    B = val['B']
    B_dict.append(dict(zip(B,[[idx]]*len(B))))
    B_dict = [{k: list(itertools.chain.from_iterable(list(filter(None.__ne__, [d.get(k) for d in B_dict])))) for k in set().union(*B_dict)}]

print ('Result:',B_dict[0])
输出

Result: {'d': [2], 'c': [0, 2], 'b': [0, 1], 'a': [0, 1]}

for循环中最后一行的代码是从这里借来的:,并使用此方法分解列表:

然后是groupby和apply列表:

idx = np.arange(len(X)).repeat(X['B'].str.len(), 0)
s = X.iloc[idx, ].assign(B=np.concatenate(X['B'].values))['B']
d = s.to_frame().reset_index().groupby('B')['index'].apply(list).to_dict()

# {'a': [0, 1], 'b': [0, 1], 'c': [0, 2], 'd': [2]}
在150000行上速度相当快:

# sample data
X = pd.DataFrame([(1,['a','b','c']),(2,['a','b']),(3,['c','d'])],columns=['A','B'])
df = pd.concat([X]*50000).reset_index(drop=True)

%%timeit
idx = np.arange(len(df)).repeat(df['B'].str.len(), 0)
s = df.iloc[idx, ].assign(B=np.concatenate(df['B'].values))['B']
d = s.to_frame().reset_index().groupby('B')['index'].apply(list).to_dict()

# 530 ms ± 46.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

我认为一个
defaultdict
将在大约1分钟内工作:

from collections import defaultdict
from itertools import chain

dd = defaultdict(list)
for k,v in zip(chain.from_iterable(df.B.ravel()), df.index.repeat(df.B.str.len()).tolist()):
    dd[k].append(v)
输出:
我让你的B列成为它自己的DF,把它转换成索引中的列,取消堆叠,然后完成清理。它看起来像:

df

 level_0 0
0   0    a
1   0    b
2   0    c
3   1    a
4   1    b
6   2    c
7   2    d


然后,我按第0列分组,列出一个列表,然后写一个目录。

您要求的是代码优化,这是代码审查的一部分,可以找到,谢谢您的快速响应!我也在代码审查论坛上发表了文章。非常感谢你的富有洞察力的回答!defaultdict方法比这里建议的其他方法快得多。因此,我已将其标记为正确答案。谢谢@网络编码器。代码复查中两次循环的答案甚至比这快了一点,可能是因为.str.len()调用很慢。虽然是同一位总校长。谢谢你的解决方案,克里斯!你的方法比我原来的解决方案快得多。但还有一种更快的方法,如ALollz的defaultdict方法所示。所以我接受了这个解决方案。谢谢谢谢本的回复。这也很有用,但当数据的大小增加时,解决方案会变慢。我在这里贴了一个很好的问题:。将您的解决方案的计算效率与defaultdict解决方案进行比较,我已将其视为本例中最快的解决方案。谢谢
X = pd.DataFrame([(1, ['a', 'b', 'c']*300), (2, ['a', 'b']*50),
                  (3, ['c', 'd']*34)], columns=['A', 'B'])
df = pd.concat([X]*150000).reset_index(drop=True)

%%timeit
dd = defaultdict(list)
for k,v in zip(chain.from_iterable(df.B.ravel()), df.index.repeat(df.B.str.len()).tolist()):
    dd[k].append(v)
#38.1 s ± 238 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%%timeit
idx = np.arange(len(df)).repeat(df['B'].str.len(), 0)
s = df.iloc[idx, ].assign(B=np.concatenate(df['B'].values))['B']
d = s.to_frame().reset_index().groupby('B')['index'].apply(list).to_dict()
#1min 24s ± 458 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
X = pd.DataFrame([(1,['a','b','c']),(2,['a','b']),(3,['c','d'])],columns=['A','B'])

df = X['B'].apply(pd.Series).T.unstack().reset_index().drop(columns = ['level_1']).dropna()
df.groupby(0)['level_0'].apply(list).to_dict()
df

 level_0 0
0   0    a
1   0    b
2   0    c
3   1    a
4   1    b
6   2    c
7   2    d