Оптимизация обработки DataFrame для интервалов входа пользователей в Pandas

Я работаю над функцией, которая обрабатывает время входа пользователей на веб-сайт. Это время хранится в DataFrame Pandas, где первый столбец указывает интервал времени, а остальные указывают, входил ли пользователь в систему в течение этого интервала. Моя программа воздействует на этот DataFrame и группирует пользователей, создавая столько столбцов, сколько существует возможных комбинаций пользователей. Затем он проверяет, были ли эти пользователи подключены в течение интервала времени, определенного строкой.

Например, в случае с тремя пользователями: A, B и C, если A и B вошли в систему в определенной строке, столбец A, B будет иметь 1, а столбцы для A и B будут 0. Если все три пользователя активны одновременно, в столбце A,B,C будет 1, а в остальных — 0.

В моем реальном случае столбцов много, поэтому экспоненциальная стоимость функции делает ее непомерно высокой. Я пытался создать код, который ищет группы, которые никогда не совпадают, чтобы избежать создания избыточных столбцов. Например, если B и C никогда не имеют общей строки со значением 1, нет смысла создавать столбцы B,C или A,B,C.

Я попробовал использовать GitHub Copilot, но он не смог предоставить полезного решения. Может ли кто-нибудь помочь мне оптимизировать мой код?

Вот код, который я использую:

def process_dataframe_opt2(df):
    # List of columns for combinations, excluding 'fecha_hora'
    columns = [col for col in df.columns if col != 'fecha_hora']
    
    # Generate all possible combinations of the columns
    for r in range(1, len(columns) + 1):
        for comb in combinations(columns, r):
            col_name = ','.join(comb)
            df[col_name] = df[list(comb)].all(axis=1).astype(int)
    
    # Create a copy of the original DataFrame to modify it
    df_copy = df.copy()
    
    # Process combinations from largest to smallest
    for r in range(len(columns), 1, -1):
        for comb in combinations(columns, r):
            col_name = ','.join(comb)
            active_rows = df[col_name] == 1
            if active_rows.any():
                for sub_comb in combinations(comb, r-1):
                    sub_col_name = ','.join(sub_comb)
                    df_copy.loc[active_rows, sub_col_name] = 0
    
    # Remove columns that only contain 0
    df_copy = df_copy.loc[:, (df_copy != 0).any(axis=0)]
    
    return df_copy

И чтобы создать пример

import pandas as pd
from itertools import combinations
# Create a range of time
date_rng = pd.date_range(start='2024-05-13 15:52:00', end='2024-05-13 16:04:00', freq='min')

# Create an empty DataFrame
df = pd.DataFrame(date_rng, columns=['fecha_hora'])

# Add the login columns with corresponding values
df['A'] = 1  # Always active
df['B'] = [1] * 6 + [0] * 5 + [1] * 2  # Active in the first 6 intervals
df['C'] = [1] * 5 + [0] * 6 + [1] * 2  # Active in the first 5 intervals
df['D'] = [1] * 4 + [0] * 7 + [1] * 2  # Active in the first 4 intervals
df['E'] = [1] * 3 + [0] * 3 + [1] * 2 + [0] * 3 + [1]*2  # Active in two blocks
df['F'] = [0] * 7 + [1] * 3 + [0] * 3  # Active in a single block towards the end
df['alfa'] = [0] * 10 + [1] * 1 + [0] * 2

# Adjust some rows to have more than one '1'
df.loc[1, ['A', 'B', 'C']] = 1  # Row with multiple '1's
df.loc[8, ['D', 'E', 'F']] = 1  # Another row with multiple '1's

df_copy = process_dataframe_opt2(df)

Может ли кто-нибудь предоставить информацию или предложения о том, как оптимизировать эту функцию, чтобы избежать экспоненциальных затрат и повысить производительность?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Каждая запись ожидаемого результата содержит одно поле, равное 1, которое расположено в столбце, указанном активными пользователями, а остальные поля равны 0. Итак, вот что мы можем сделать, чтобы создать эту таблицу:

  • заменить каждую запись последовательностью активных пользователей;
  • используйте pivot, unstack, get_dummies и т. д., чтобы преобразовать полученный ряд во фрейм данных.

Пример с get_dummies:

result = pd.get_dummies(
    df.astype(bool).apply(lambda x: ','.join(df.columns[x]), axis=1),
    dtype=int
)

Пример с unstack:

result = (
    df.astype(bool)
    .apply(lambda x: ','.join(df.columns[x]), axis=1)   # select active users
    .to_frame('users')                                            
    .set_index('users', append=True)       # push users to the second index level
    .assign(mark=1)                        # mark records before pivoting
    .squeeze()
    .unstack(fill_value=0)
)

Код для экспериментов:

import pandas as pd

data = { 
    'A': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    'B': [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1],
    'C': [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1],
    'D': [1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1],
    'E': [1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1],
    'F': [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
    'alfa': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
}
index = pd.date_range(
    start='2024-05-13 15:52:00', 
    periods=len(data['A']), freq='min'
)
df = pd.DataFrame(data, index)

# option with get dummies
result1 = pd.get_dummies(
    df.astype(bool).agg(lambda x: ','.join(df.columns[x]), axis=1)
    , dtype=int
)

# option with unstacking
result2 = (
    df.astype(bool)
    .apply(lambda x: ','.join(df.columns[x]), axis=1)
    .to_frame(None)
    .set_index(None, append=True)
    .assign(just_mark=1).squeeze()
    .unstack(fill_value=0)
)

assert result1.equals(result2)
>>> print(result1)
                     A,B  A,B,C  A,B,C,D  A,B,C,D,E  A,D,E,F  A,E  A,E,F  A,F  A,alfa
2024-05-13 15:52:00    0      0        0          1        0    0      0    0       0
2024-05-13 15:53:00    0      0        0          1        0    0      0    0       0
2024-05-13 15:54:00    0      0        0          1        0    0      0    0       0
2024-05-13 15:55:00    0      0        1          0        0    0      0    0       0
2024-05-13 15:56:00    0      1        0          0        0    0      0    0       0
2024-05-13 15:57:00    1      0        0          0        0    0      0    0       0
2024-05-13 15:58:00    0      0        0          0        0    1      0    0       0
2024-05-13 15:59:00    0      0        0          0        0    0      1    0       0
2024-05-13 16:00:00    0      0        0          0        1    0      0    0       0
2024-05-13 16:01:00    0      0        0          0        0    0      0    1       0
2024-05-13 16:02:00    0      0        0          0        0    0      0    0       1
2024-05-13 16:03:00    0      0        0          1        0    0      0    0       0
2024-05-13 16:04:00    0      0        0          1        0    0      0    0       0

Другие вопросы по теме

Функция Python Apply для создания новых строк в цикле
Как правильно преобразовать файл JSON в фрейм данных Pandas?
Получите панели ошибок в ячейки набора данных с помощью начальной загрузки
При циклическом просмотре кадра данных в поисках строковых значений. Как распечатать строки без дублирования, если поисковый запрос встречается несколько раз?
Python pandas-market-календари
Почему преобразование вложенного словаря Python в фрейм данных pandas приводит к ошибке «не имеет атрибута 'items'»?
Как заменить определенное поле внутри строки JSON в каждой строке файла csv в Python случайным значением?
Как создать иерархическую карту цветов в matplotlib?
Расширенная логика с группировкой, применением и преобразованием - сравнение значения строки с предыдущим значением и создание нового столбца
Каков самый быстрый способ расчета ежедневного баланса со сложными процентами в Pandas или Spark?