Как использовать словарь, чтобы ускорить поиск и подсчет? - PullRequest
0 голосов
/ 05 августа 2020

Рассмотрим следующий фрагмент:

data = {"col1":["aaa","bbb","ccc","aaa","ddd","bbb"],
       "col2":["fff","aaa","ggg","eee","ccc","ttt"]}
df = pd.DataFrame(data,columns=["col1","col2"]) # my actual dataframe has
                                                # 20,00,000 such rows

list_a = ["ccc","aaa","mmm","nnn","ccc"]
list_b = ["ggg","fff","eee","ooo","ddd"]

# After doing a combination of 2 elements between the 2 lists in both orders,
# we get a list that resembles something like this:
new_list = ["ccc-ggg", "ggg-ccc", "aaa-fff", "fff-aaa", ..."ccc-fff", "fff-ccc", ...]

Учитывая огромный фрейм данных и 2 списка, я хочу подсчитать количество элементов в new_list, которые находятся в одном и том же фрейме данных. В приведенном выше псевдопримере результатом будет 3, как: «aaa-fff», «ccc -ggg», & «ddd - ccc» находятся в одной строке фрейма данных.

Прямо сейчас я использую алгоритм линейного поиска, но он очень медленный, так как мне приходится сканировать весь фрейм данных.

df['col3']=df['col1']+"-"+df['col2']
for a in list_a:
    c1 = 0
    for b in list_b:
        str1=a+"-"+b
        str2=b+"-"+a
        str1=a+"-"+b
        c2 = (df['col3'].str.contains(str1).sum())+(df['col3'].str.contains(str2).sum())
    c1+=c2
return c1

Может ли кто-нибудь помочь мне реализовать более быстрый алгоритм, желательно со словарными данными структура?

Примечание: мне нужно перебрать 7000 строк другого фрейма данных и динамически создать 2 списка и получить совокупное количество для каждой строки.

Ответы [ 3 ]

1 голос
/ 06 августа 2020

Вот еще один способ. Во-первых, я использовал ваше определение df (с двумя столбцами), list_a и list_b.

# combine two columns in the data frame
df['col3'] = df['col1'] + '-' + df['col2']

# create set with list_a and list_b pairs
s = ({ f'{a}-{b}' for a, b in zip(list_a, list_b)} | 
     { f'{b}-{a}' for a, b in zip(list_a, list_b)})

# find intersection
result = set(df['col3']) & s
print(len(result), '\n', result)

3 
 {'ddd-ccc', 'ccc-ggg', 'aaa-fff'}

UPDATE для обработки повторяющихся значений.

# build list (not set) from list_a and list_b
idx =  ([ f'{a}-{b}' for a, b in zip(list_a, list_b) ] +
        [ f'{b}-{a}' for a, b in zip(list_a, list_b) ])

# create `col3`, and do `value_counts()` to preserve info about duplicates
df['col3'] = df['col1'] + '-' + df['col2']
tmp = df['col3'].value_counts()

# use idx to sub-select from to value counts:
tmp[ tmp.index.isin(idx) ]

# results:
ddd-ccc    1
aaa-fff    1
ccc-ggg    1
Name: col3, dtype: int64
0 голосов
/ 05 августа 2020

Сначала соедините столбцы перед циклом, затем вместо цикла передайте необязательное регулярное выражение, которое содержит все возможные строки. этот вопрос

import re

data = {"col1":["aaa","bbb","ccc","aaa","ddd","bbb"],
   "col2":["fff","aaa","ggg","eee","ccc","ttt"]}
list_a = ["ccc","aaa","mmm","nnn","ccc"]
list_b = ["ggg","fff","eee","ooo","ddd"]

### build the regex pattern
pat_set = set('-'.join(combo) for combo in set(
    list(itertools.product(list_a, list_b)) +
    list(itertools.product(list_b, list_a))))
pat = '|'.join(pat_set)
    # use itertools to generalize with many colums, remove duplicates with set()

### join the columns row-wise
joined = ['-'.join(row) for row in zip(*[vals for key, vals in data.items()])]

### filter joined
match_list = list(filter(re.compile(pat).match, joined))
ct = len(match_list)

Третий вариант с series.isin(), вдохновленный ответом jsmart

joined = df.col1 + '-' + df.col2
ct = joined.isin(pat_set).sum()

Тестирование скорости

Я повторил данные 100 000 раз для тестирования масштабируемости. series.isin() занимает день, тогда как jsmart отвечает быстро, но не находит всех вхождений, потому что удаляет дубликаты из joined

with dicts: 400000 matches, 1.00 s
with pandas: 400000 matches, 1.77 s
with series.isin(): 400000 matches, 0.39 s
with jsmart answer: 4 matches, 0.50 s
0 голосов
/ 05 августа 2020

Попробуйте следующее:

from itertools import product

# all combinations of the two lists as tuples
all_list_combinations = list(product(list_a, list_b)) 

# tuples of the two columns
dftuples = [x for x in df.itertuples(index=False, name=None)] 

# take the length of hte intersection of the two sets and print it
print(len(set(dftuples).intersection(set(all_list_combinations))))

дает

3

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