Самый быстрый способ найти индексы фреймов данных элементов столбцов, которые существуют в виде списков - PullRequest
1 голос
/ 11 апреля 2019

У меня есть пандас, в котором значения столбцов существуют в виде списков. Каждый список имеет несколько элементов, и один элемент может существовать в нескольких строках. Пример кадра данных:

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» могут содержать до 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 был заимствован отсюда: Объединить значения одних и тех же ключей в списке диктов и удалить значение None из списка без удаления значения 0

Ответы [ 3 ]

1 голос
/ 12 апреля 2019

Я думаю, что 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)
1 голос
/ 11 апреля 2019

разбить список с помощью этого метода: 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)
0 голосов
/ 12 апреля 2019
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, транспонирую его, чтобы сделать столбцы индекса, снимаю стопку, а затем заканчиваю очистку.Это выглядит так:

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, делаю его списком, затем диктом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...