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