У меня есть простая функция, которая удаляет контуры и возвращает новый DataFrame:
def remove_outliner(df):
df.index = df.time
df['median']= df['price'].rolling(15).median()
df['std'] = df['price'].rolling(15).std()
df["std+"] = df['median']+3*df['std']
df["std-"] = df['median']-3*df['std']
#filter setup
df2 = df[(df.price <= df['median']+3*df['std']) &
(df.price >= df['median']-3*df['std'])]
return df2
Есть ли способ применить такую функцию с помощью groupby? Что-то вроде этого (псевдокод):
df.groupby(["product"]).filter(remove_outliner).concat_groups()
Мой наивный подход состоял бы в том, чтобы перебирать группы. Сохраните их в списке, а затем примените pd.concat. Но я надеюсь, что есть более элегантный способ. Большое спасибо за любую подсказку!
PS: пример ввода
product price
2014-08-25 01:00:00 A 1.2
2014-08-25 02:00:00 B 7.2
2014-08-25 03:00:00 A 1.2
2014-08-25 04:00:00 B 7.2
2014-08-25 04:00:00 A 1.2
2014-08-25 05:00:00 A 99.2
2014-08-25 06:00:00 A 1.2
2014-08-25 06:00:00 B 7.2
2014-08-25 21:00:00 A 1.2
2014-08-25 22:00:00 B 88.2
ожидаемый результат
product price
2014-08-25 01:00:00 A 1.2
2014-08-25 02:00:00 B 7.2
2014-08-25 03:00:00 A 1.2
2014-08-25 04:00:00 B 7.2
2014-08-25 04:00:00 A 1.2
2014-08-25 06:00:00 A 1.2
2014-08-25 06:00:00 B 7.2
2014-08-25 21:00:00 A 1.2
Если бы первые строки исчезли, это было бы хорошо. Функция remove_outliner добавляет некоторые столбцы, но их можно удалить. Так как разные товары могут иметь разные ценовые шкалы, я не могу применить фильтр только к цене и должен работать с группами.
предоставить образцы данных и ожидаемый результат
@AkshayNevrekar Я добавил пример.
@QuangHoang Это не работает, так как фильтр ожидает логического ввода
Я имею в виду apply
: df.groupby(["product"]).apply(remove_outliner)
.
@QuangHoang Это выглядит действительно хорошо. Он возвращает DataFrame с MultiIndex (для каждого продукта). Так что все еще изо всех сил пытаюсь удалить MultiIndex и прибегнуть вовремя.
Просто используйте reset_index
, чтобы пройти каждый уровень за раз. Вы также можете хотеть groupby('product', group_keys=False)
.
Попробуй это:
df_wo_outliers = df[~((df < (Q1 - 1.5 * IQR)) |(df > (Q3 + 1.5 * IQR))).any(axis=1)]
Также есть хорошая статья, в которой рассказывается о обнаружение и обработка выбросов.
Надеюсь, поможет.
Это в основном то же самое, что делает функция remove_outliner. Но в моем случае это должно применяться к разным группам.
Истинный. Пока ваша функция указывает столбцы, я применяю решение ко всему фрейму данных.
Поэтому я попытался немного изменить вашу функцию (вам, вероятно, не нужны median
и std
в вашем фрейме данных):
def remove_outliner(df):
roll_median= df['price'].rolling(15).median()
roll_std = df['price'].rolling(15).std()
#filter setup
df2 = df[(df.price.le(roll_median + 3*roll_std) &
(df.price.ge(roll_median - 3*roll_std)]
return df2
# set_index here, not inside the function:
df.set_index('time').groupby('product', group_keys=False).apply(remove_outliner)
Большое спасибо! Это решение. Узнал что-то новое! Не знаю о group_keys=False. Также просто пришлось добавить сортировку для восстановления порядка. sort_index()
Вы пробовали простую
df.groupby(["product"]).filter(remove_outliner)
? Что случилось?