Вопрос или проблема
У меня есть функция, которую я упрощаю ниже, но которую я использую для создания нового столбца в существующем датафрейме. В настоящее время я использую iterrows, но я пытался использовать .apply, фильтруя по уникальным группам Года и Недели (нет, это нельзя преобразовать в даты по определённым причинам), но это не значительно ускорило процесс. Есть идеи, как использовать векторные функции для более эффективного выполнения этой задачи?
def add_variability(input_df, historical_df):
variability_calcs = []
for index, row in input_df.iterrows():
filtered_historical_df = historical_df[
((historical_df['Year'] < row['Year']) | ((historical_df['Year'] == row['Year']) & (historical_df['Week'] < row['Week']))) &
(historical_df['Company'] == row['Company'])
]
filtered_historical_df = filtered_historical_df.sort_values(by=['Year', 'Week'], ascending=False)
recent_weeks = filtered_historical_df.head(16)
if recent_weeks.shape[0] > 1:
if recent_weeks['Volume'].mean() != 0:
cv = recent_weeks['Volume'].std() / recent_weeks['Volume'].mean()
else:
cv = None
else:
cv = None
variability_calcs.append(cv)
input_df['Variability'] = variability_calcs
return input_df
Ответ или решение
Для оптимизации вашего кода и создания нового столбца в DataFrame без использования iterrows()
, можно использовать более эффективные методы в Pandas, такие как векторизация и группировка. Я представляю вам улучшенную версию вашей функции, которая минимизирует цикл и избавляется от применения iterrows()
. Вместо этого мы будем применять функции агрегации и фильтрации к группам данных.
Вот как можно переписать функцию с использованием методов Pandas:
import pandas as pd
def add_variability(input_df, historical_df):
def calc_variability(row):
# Фильтрация исторических данных по критериям из текущей строки
filtered_historical_df = historical_df[
((historical_df['Year'] < row['Year']) |
((historical_df['Year'] == row['Year']) & (historical_df['Week'] < row['Week']))) &
(historical_df['Company'] == row['Company']))
]
recent_weeks = filtered_historical_df.sort_values(by=['Year', 'Week'], ascending=False).head(16)
if recent_weeks.shape[0] > 1 and recent_weeks['Volume'].mean() != 0:
return recent_weeks['Volume'].std() / recent_weeks['Volume'].mean()
else:
return None
# Применяем функцию к каждой строке DataFrame
input_df['Variability'] = input_df.apply(lambda row: calc_variability(row), axis=1)
return input_df
Объяснение:
-
Функция
calc_variability
: Мы определяем внутреннюю функцию, которая принимает строку и выполняет логику фильтрации и вычислений в соответствии с вашим исходным кодом. -
Использование
apply
: Вместоiterrows()
и цикла for по строкам, мы используемapply
сlambda
, которая применяет функциюcalc_variability
ко всем строкам DataFrameinput_df
. Это более эффективно по сравнению с итерацией, особенно для больших DataFrame. - Группировка и фильтрация: Внутри
calc_variability
мы выполняем фильтрациюhistorical_df
на основе условий данной строки. Используемhead(16)
для получения 16 наиболее недавних недель.
Таким образом, данная версия функции більш эффективна с точки зрения производительности, так как мы избегаем вложенных циклов, и используем векторные операции, которые лучше оптимизированы в библиотеке Pandas.
В заключение:
Когда вы работаете с большими данными в Pandas, всегда старайтесь избегать явной итерации по строкам DataFrame, так как это может замедлить работу программы. Вместо этого используйте векторизацию и агрегацию, чтобы ускорить обработку данных.