首页 > 解决方案 > 查找作为列表存在的列元素的数据框索引的最快方法

问题描述

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

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]

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

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

我写了一个运行良好的代码,我可以得到一个结果。我的问题更多与计算速度有关。我的实际数据框有大约 350,000 行,“B”列中的列表最多可以包含 1,000 个元素。但目前代码运行了几个小时!我想知道我的解决方案是否效率很低。任何以更快更有效的方式提供的帮助将不胜感激!这是我的解决方案代码:

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 循环中最后一行的代码是从这里借用的:Combine values of same keys in a list of dicts , and remove None value from a list without remove the 0 value

标签: pythonpandaslistdictionaryindexing

解决方案


用这种方法扩展你的列表:https ://stackoverflow.com/a/46740682/9177877

然后 groupby 并应用列表:

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]}

它在 150,000 行上非常快:

# 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)

推荐阅读