Удалить аутлайнеры панд через groupby?

У меня есть простая функция, которая удаляет контуры и возвращает новый 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 добавляет некоторые столбцы, но их можно удалить. Так как разные товары могут иметь разные ценовые шкалы, я не могу применить фильтр только к цене и должен работать с группами.

Вы пробовали простую df.groupby(["product"]).filter(remove_outliner)? Что случилось?

Quang Hoang 11.06.2019 11:56

предоставить образцы данных и ожидаемый результат

Sociopath 11.06.2019 11:57

@AkshayNevrekar Я добавил пример.

MichaelRazum 11.06.2019 12:08

@QuangHoang Это не работает, так как фильтр ожидает логического ввода

MichaelRazum 11.06.2019 12:09

Я имею в виду apply: df.groupby(["product"]).apply(remove_outliner).

Quang Hoang 11.06.2019 12:12

@QuangHoang Это выглядит действительно хорошо. Он возвращает DataFrame с MultiIndex (для каждого продукта). Так что все еще изо всех сил пытаюсь удалить MultiIndex и прибегнуть вовремя.

MichaelRazum 11.06.2019 12:16

Просто используйте reset_index, чтобы пройти каждый уровень за раз. Вы также можете хотеть groupby('product', group_keys=False).

Quang Hoang 11.06.2019 12:17
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
7
85
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Попробуй это:

df_wo_outliers = df[~((df < (Q1 - 1.5 * IQR)) |(df > (Q3 + 1.5 * IQR))).any(axis=1)]

Также есть хорошая статья, в которой рассказывается о обнаружение и обработка выбросов.

Надеюсь, поможет.

Это в основном то же самое, что делает функция remove_outliner. Но в моем случае это должно применяться к разным группам.

MichaelRazum 11.06.2019 12:10

Истинный. Пока ваша функция указывает столбцы, я применяю решение ко всему фрейму данных.

Sagar Dawda 11.06.2019 13:47
Ответ принят как подходящий

Поэтому я попытался немного изменить вашу функцию (вам, вероятно, не нужны 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()

MichaelRazum 11.06.2019 12:22

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