Я работаю над функцией, которая обрабатывает время входа пользователей на веб-сайт. Это время хранится в 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)
Может ли кто-нибудь предоставить информацию или предложения о том, как оптимизировать эту функцию, чтобы избежать экспоненциальных затрат и повысить производительность?
Каждая запись ожидаемого результата содержит одно поле, равное 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