Insaned
@Insaned

Существует ли такая pandas магия?

Есть большой датафрейм с чеками и товарами в них. Количество уникальных товаров 28000, количество чеков 3.7 млн.
Мне надо подсчитать для всех пар товаров как часто они оказываются в одном чеке. Мой говнокод, который приведен ниже по предварительным расчетам будет работать до тепловой смерти вселенной.
Я уверен, что существует какая-нибудь pandas-магия, которая значительно упрощает мою задачу, но ничего придумать не смог.

ee447454.png

uniq_itm=train['item_name'].unique()
i = 0
for itm_x in uniq_itm:
    i = i + 1
    if i > len(uniq_itm)/2:
        break
    for itm_y in uniq_itm:
        percent_complete = round((i/(len(uniq_itm)/2))*100,2)
        if itm_x != itm_y:
            k = len(list(set(train.query('item_name==@itm_x')['receipt_id'].unique()) & set(train.query('item_name==@itm_y')['receipt_id'].unique())))
            if k > 0:
                print (itm_x+' '+itm_y+' '+str(k)+' '+str(percent_complete)+'%')
  • Вопрос задан
  • 151 просмотр
Решения вопроса 1
adugin
@adugin Куратор тега Python
За 30 минут посчиталось на слабеньком 2-ядерном CPU.
31.5 млн строк (в ~5 раз больше, чем у вас), до 14 предметов в чеке (у вас в среднем 1.75).
Ваш кейс посчитается за минуту-другую.

Эмуляция данных
import pandas as pd
from random choices, randint
from string import ascii_uppercase

items = [''.join(choices(ascii_uppercase, k=randint(4, 11))) for i in range(28000)]

def generate():
    for receipt_id in range(3700000):
        if receipt_id % 100000 == 0:
            print(receipt_id)
        for item in sample(items, randint(2, 15)):
            yield receipt_id, item
            
data = pd.DataFrame(generate(), columns=['receipt_id', 'item_name'])

6046635e5b8fa266367922.png
from collections import Counter
from itertools import combinations
# from sys import intern

# В эмуляции все строки в DataFrame уже интернированы по умолчанию
# data['item_name'] = data.item_name.map(intern) 

statistics = Counter(
    pair_of_items  # Для экономии памяти можно взять hash(pair_of_items)
        for items_in_receipt in data.groupby('receipt_id', sort=False).item_name.agg(sorted)
            for pair_of_items in combinations(items_in_receipt, 2)  # сортировка сохраняется
)

Возможно, имеет смысл посмотреть в сторону Dask.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
Insaned
@Insaned Автор вопроса
В другом месте предложили вот такой вариант. В принципе работает, но на моём датасете требует 160Гб ОЗУ, чего у меня к сожалению нет
import pandas as pd
from mlxtend.preprocessing import TransactionEncoder
from mlxtend.frequent_patterns import fpgrowth

# Sample data in a similar structure to yours
df = pd.DataFrame({
    'reciept_id':[1,1,2,2,3,3],
    'reciept_dayofweek':[4,4,5,5,6,6],
    'reciept_time':['20:20','20:20','12:13','12:13','11:10','11:10'],
    'item_name':['Milk','Onion','Dill','Onion','Milk','Onion']
    
})

# Create an array of items per transactions
dataset = df.groupby(['reciept_id','reciept_dayofweek','reciept_time'])['item_name'].apply(list).values

# Create the required structure for data to go into the algorithm
te = TransactionEncoder()
te_ary = te.fit(dataset).transform(dataset)
df = pd.DataFrame(te_ary, columns=te.columns_)

# Generate frequent items sets with a support of 1/len(dataset)
# This is the same as saying give me every combination that shows up at least once
# The maximum size of any given itemset is 2, but you could change it to have any number
frequent = fpgrowth(df, min_support=1/len(dataset),use_colnames=True, max_len=2)

# Get rid of single item records
frequent = frequent[frequent['itemsets'].apply(lambda x: len(x))==2]

# Muliply support by the number of transactions to get the count of times each item set appeared
# in the original data set
frequent['frequency'] = frequent['support'] * len(dataset)

# View the results
print(frequent[['itemsets','frequency']])
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы