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

时间:2019-04-11 19:48:18

标签: python pandas list dictionary indexing

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

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 dictsremove None value from a list without removing the 0 value

3 个答案:

答案 0 :(得分:1)

使用以下方法展开您的列表:https://stackoverflow.com/a/46740682/9177877

然后分组并应用列表:

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)

答案 1 :(得分:1)

我认为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)

输出:

defaultdict(list, {'a': [0, 1], 'b': [0, 1], 'c': [0, 2], 'd': [2]})

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)

答案 2 :(得分:0)

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

我将B列设为自己的DF,将其转置为Index的列,将其拆开,然后完成清洁。看起来像:

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列分组,将其列出,然后是字典。