Улучшение производительности повторяющихся групповых операций

python pandas numpy

213 просмотра

1 ответ

У меня есть DataFrame с MultiIndex, который в основном представляет собой двоичную матрицу:

day        day01                      day02                  
session session1 session2 session3 session1 session2 session3
0              1        0        0        0        0        0
1              0        0        1        1        1        0
2              1        1        1        0        0        1
3              1        0        0        1        0        0
4              1        0        1        0        0        0

Из этого DataFrame мне нужно рассчитать ежедневные суммы для каждой строки:

     day01  day02
0        1      0
1        1      2
2        3      1
3        1      1
4        2      0

И получите число 0, 1 ... (значение имеет значение) в этой сумме:

0    2
1    5
2    2
3    1

Мне нужно сделать это и для сессий тоже. Суммы сессий для каждой строки:

         session1  session2  session3
0               1         0         0
1               1         1         1
2               1         1         2
3               2         0         0
4               1         0         1

И получите значения счетчиков:

0    5
1    8
2    2

В качестве основы, это результат df.groupby(level='day', axis=1).sum().stack().value_counts()df.groupby(level='session', axis=1).sum().stack().value_counts()). DataFrame изменяется в каждой итерации алгоритма имитации отжига, и эти значения пересчитываются. Когда я профилировал код, я увидел, что значительное количество времени уходит на групповые операции.

Я пытался сохранять групповые объекты и получать суммы по этим объектам в каждой итерации, но улучшение составило около 10%. Вот код для создания большего DataFrame (похожего на тот, который у меня есть):

import numpy as np
import pandas as pd
prng = np.random.RandomState(0)
days = ['day{0:02d}'.format(i) for i in range(1, 11)]
sessions = ['session{}'.format(i) for i in range(1, 5)]
idx = pd.MultiIndex.from_product((days, sessions), names=['day', 'session'])
df = pd.DataFrame(prng.binomial(1, 0.25, (1250, 40)), columns=idx)

На моем компьютере следующие два метода занимают 3,8 с и 3,38 с соответственно.

def try1(df, num_repeats=1000):
    for i in range(num_repeats):
        session_counts = (df.groupby(level='session', axis=1, sort=False)
                            .sum()
                            .stack()
                            .value_counts(sort=False))
        daily_counts = (df.groupby(level='day', axis=1, sort=False)
                          .sum()
                          .stack()
                          .value_counts(sort=False))
    return session_counts, daily_counts

def try2(df, num_repeats=1000):
    session_groups = df.groupby(level='session', axis=1, sort=False)
    day_groups = df.groupby(level='day', axis=1, sort=False)
    for i in range(num_repeats):
        df.iat[0, 0] = (i + 1) % 2
        session_counts = session_groups.sum().stack().value_counts(sort=False)
        daily_counts = day_groups.sum().stack().value_counts(sort=False)
    return session_counts, daily_counts

%time try1(df)
Wall time: 3.8 s

%time try2(df)
Wall time: 3.38 s

Примечание. Циклы в функциях предназначены только для синхронизации. Для второй функции, чтобы получить правильное время, мне нужно было изменить DataFrame.

В настоящее время я работаю над другим методом, чтобы напрямую отражать изменения в DataFrame для подсчетов без пересчета групп, но я пока не добился успеха. Отслеживание поврежденных строк и обновление сохраненных DataFrames оказалось медленнее.

Есть ли способ улучшить производительность этих групповых операций?

Автор: ayhan Источник Размещён: 08.11.2019 11:15

Ответы (1)


2 плюса

Решение

Предполагая обычный формат данных (равное количество дней и сессий в каждой строке), вот подход, основанный на NumPy, использующий np.uniqueвыходные данные, имеющие свои индексы в отсортированном порядке

# Extract array
a,b = df.columns.levels
arr = df.values.reshape(-1,len(a),len(b))

# Get session counts
session_sums = arr.sum(1)
unq,count = np.unique(session_sums,return_counts=True)
session_counts_out = pd.Series(count,index=unq)

# Get daily count
daily_sums = arr.sum(2)
unq,count = np.unique(daily_sums,return_counts=True)
daily_counts_out = pd.Series(count,index=unq)

Если вас интересуют только значения без индексов, вот альтернатива, np.bincountкоторая, по сути, просто выполняет подсчет, как это делается по return_countsчастям с np.unique -

# Get session counts
session_sums = arr.sum(1)
count = np.bincount(session_sums.ravel())
session_counts_out = count[count>0]

# Get daily count
daily_sums = arr.sum(2)
count = np.bincount(daily_sums.ravel())
daily_counts_out = count[count>0]
Автор: Divakar Размещён: 20.08.2016 12:50
Вопросы из категории :
32x32