Python Pandas — Более быстрый способ перебора категорий в данных При удалении выбросов (Без цикла For)

#python #pandas

#питон #панды

Вопрос:

Предположим, у меня есть такой фрейм данных:

 import pandas as pd import numpy as np  data = [[5123, '2021-01-01 00:00:00', 'cash','sales

Магазин Дата Способ оплаты Атрибут Ценность
5123 2021-01-01 00:00:00 Наличными продажи$ 105
5123 2021-01-01 00:00:00 Наличными Товары 20
5123 2021-01-01 00:00:00 карта продажи$ 190
5123 2021-01-01 00:00:00 карта Товары 40
5123 2021-01-02 00:00:00 Наличными продажи$ 75
5123 2021-01-02 00:00:00 Наличными Товары 10
5123 2021-01-02 00:00:00 карта продажи$ 170
5123 2021-01-02 00:00:00 карта Товары 35
5123 2021-01-03 00:00:00 Наличными продажи$ 1000
5123 2021-01-03 00:00:00 Наличными Товары 500
5123 2021-01-03 00:00:00 карта продажи$ 150
5123 2021-01-03 00:00:00 карта Товары 20
I would like to filter outliers and replace them with the average value from the preceding 2 days. My "outlier rule" is such: if a value for an attribute/payment method is more than twice as big, or smaller than half as big, as the average value for that attribute/payment method from the preceding two days, then replace that outlier with the average value from the preceding two days. Otherwise, leave the value. In this case, all values should remain except for the $1000 sales and 500 items for 5123/'2021-01-03'/'cash'. Those values should be replaced with $90 for sales, and 15 for items. Here is my attempt (using a for loop, which doesn't work). Whenever I am using a loop and Pandas together, a red flag goes off in my head. What is the correct way to do this?
 stores = df['Store'].unique() payment_methods = df['Payment Method'].unique() attributes = df['Attribute'].unique()  df_no_outliers = pd.DataFrame()  for store in stores:  for payment_method in payment_methods:  for attribute in attributes:   df_temp = df.loc[df['Store'] == store]  df_temp = df_temp.loc[df_temp['Payment Method'] == payment_method]  df_temp = df_temp.loc[df_temp['Attribute'] == attribute]   df_temp['Value'] = np.where(df_temp['Value'] lt;= (df_temp['Value'].shift(-1)   df_temp['Value'].shift(-2))*2/2,  df_temp['Value'],  (df_temp['Value'].shift(-1) df_temp['Value'].shift(-2))/2)   df_temp['Value'] = np.where(df_temp['Value'] gt;= (df_temp['Value'].shift(-1)   df_temp['Value'].shift(-2))*0.5/2,  df_temp['Value'],  (df_temp['Value'].shift(-1) df_temp['Value'].shift(-2))/2)    df_no_outliers = df_no_outliers.append(df_temp)  

На случай, если кому-то интересно, почему я использую этот метод скользящего среднего вместо чего-то вроде метода Тьюки, отсекающего данные более или менее 1,5*IQR от 1Q и 3Q, мои данные представляют собой временные ряды за период COVID, что означает, что IQR очень большой (высокие продажи до COVID, а затем глубокая яма отсутствия продаж после), поэтому IQR в конечном итоге ничего не фильтрует. Я не хочу удалять удаление COVID, а скорее удаляю некоторые ошибочные ошибки ввода данных (некоторые магазины плохо относятся к этому и могут вводить несколько дополнительных нулей в некоторые дни...). Вместо того, чтобы использовать последние два дня в качестве скользящего фильтра, я, вероятно, в конечном итоге буду использовать 5 или 7 дней (в течение недели). Я также открыт для других способов выполнения этой очистки / удаления выбросов.

Ответ №1:

Попробуй:

 #groupby the required columns and compute the rolling 2-day average average = (df.groupby(["Store","Payment Method","Attribute"], as_index=False)  .apply(lambda x: x["Value"].rolling(2).mean().shift())  .droplevel(0).sort_index()  )  #divide values by the average and keep only those ratios that fall between 0.5 and 2 df['Value'] = df["Value"].where(df["Value"].div(average).fillna(1).between(0.5,2),average) gt;gt;gt; df  Store Date Payment Method Attribute Value 0 5123 2021-01-01 00:00:00 cash sales$ 105.0 1 5123 2021-01-01 00:00:00 cash items 20.0 2 5123 2021-01-01 00:00:00 card sales$ 190.0 3 5123 2021-01-01 00:00:00 card items 40.0 4 5123 2021-01-02 00:00:00 cash sales$ 75.0 5 5123 2021-01-02 00:00:00 cash items 10.0 6 5123 2021-01-02 00:00:00 card sales$ 170.0 7 5123 2021-01-02 00:00:00 card items 35.0 8 5123 2021-01-03 00:00:00 cash sales$ 90.0 9 5123 2021-01-03 00:00:00 cash items 15.0 10 5123 2021-01-03 00:00:00 card sales$ 150.0 11 5123 2021-01-03 00:00:00 card items 20.0  

Комментарии:

1. Почти! Когда я это делаю, я теряю около 10% своих данных (мои фактические данные находятся в десятках миллионов строк). Не могли бы вы, пожалуйста, описать, что делают цепные методы?

2. Группировка по разным столбцам и вычисление скользящего среднего значения за 2 периода. Затем мы используем это для фильтрации значений от 0,5 до 2. Какая часть неясна?

3. Что здесь делает .shift ()? Я вижу, что выполнение этого кода без .shift() создает серию из 8 ненулевых записей, а выполнение с .shift() создает правильную серию из 4 ненулевых записей. Я думал, что сдвиг перемещает строки вниз, поэтому я бы подумал, что 537,5 (последняя запись в серии без использования .shift()) отвалилась бы, оставив 7 ненулевых записей. Отдельно от вопроса о сдвиге, похоже, что две строки с выбросами были просто полностью удалены, а не заменены средним значением, что объясняет, почему сейчас у меня отсутствуют данные.

4. Понял. Отредактирую ваш пост, чтобы показать, что я сделал. В итоге я добавил sort_index() в средний ряд, а затем использовал np.where() для заполнения начальных значений фрейма данных средним значением, где раньше был выброс.

5. @edutt - Слегка изменил ваше редактирование, чтобы использовать df.where вместо np.where . Это устраняет необходимость import numpy 🙂

, 105], [5123, '2021-01-01 00:00:00', 'cash','items', 20], [5123, '2021-01-01 00:00:00', 'card','sales

Магазин Дата Способ оплаты Атрибут Ценность
5123 2021-01-01 00:00:00 Наличными продажи$ 105
5123 2021-01-01 00:00:00 Наличными Товары 20
5123 2021-01-01 00:00:00 карта продажи$ 190
5123 2021-01-01 00:00:00 карта Товары 40
5123 2021-01-02 00:00:00 Наличными продажи$ 75
5123 2021-01-02 00:00:00 Наличными Товары 10
5123 2021-01-02 00:00:00 карта продажи$ 170
5123 2021-01-02 00:00:00 карта Товары 35
5123 2021-01-03 00:00:00 Наличными продажи$ 1000
5123 2021-01-03 00:00:00 Наличными Товары 500
5123 2021-01-03 00:00:00 карта продажи$ 150
5123 2021-01-03 00:00:00 карта Товары 20

I would like to filter outliers and replace them with the average value from the preceding 2 days. My "outlier rule" is such: if a value for an attribute/payment method is more than twice as big, or smaller than half as big, as the average value for that attribute/payment method from the preceding two days, then replace that outlier with the average value from the preceding two days. Otherwise, leave the value. In this case, all values should remain except for the $1000 sales and 500 items for 5123/'2021-01-03'/'cash'. Those values should be replaced with $90 for sales, and 15 for items.

Here is my attempt (using a for loop, which doesn't work). Whenever I am using a loop and Pandas together, a red flag goes off in my head. What is the correct way to do this?


На случай, если кому-то интересно, почему я использую этот метод скользящего среднего вместо чего-то вроде метода Тьюки, отсекающего данные более или менее 1,5*IQR от 1Q и 3Q, мои данные представляют собой временные ряды за период COVID, что означает, что IQR очень большой (высокие продажи до COVID, а затем глубокая яма отсутствия продаж после), поэтому IQR в конечном итоге ничего не фильтрует. Я не хочу удалять удаление COVID, а скорее удаляю некоторые ошибочные ошибки ввода данных (некоторые магазины плохо относятся к этому и могут вводить несколько дополнительных нулей в некоторые дни...). Вместо того, чтобы использовать последние два дня в качестве скользящего фильтра, я, вероятно, в конечном итоге буду использовать 5 или 7 дней (в течение недели). Я также открыт для других способов выполнения этой очистки / удаления выбросов.

Ответ №1:

Попробуй:


Комментарии:

1. Почти! Когда я это делаю, я теряю около 10% своих данных (мои фактические данные находятся в десятках миллионов строк). Не могли бы вы, пожалуйста, описать, что делают цепные методы?

2. Группировка по разным столбцам и вычисление скользящего среднего значения за 2 периода. Затем мы используем это для фильтрации значений от 0,5 до 2. Какая часть неясна?

3. Что здесь делает .shift ()? Я вижу, что выполнение этого кода без .shift() создает серию из 8 ненулевых записей, а выполнение с .shift() создает правильную серию из 4 ненулевых записей. Я думал, что сдвиг перемещает строки вниз, поэтому я бы подумал, что 537,5 (последняя запись в серии без использования .shift()) отвалилась бы, оставив 7 ненулевых записей. Отдельно от вопроса о сдвиге, похоже, что две строки с выбросами были просто полностью удалены, а не заменены средним значением, что объясняет, почему сейчас у меня отсутствуют данные.

4. Понял. Отредактирую ваш пост, чтобы показать, что я сделал. В итоге я добавил sort_index() в средний ряд, а затем использовал np.where() для заполнения начальных значений фрейма данных средним значением, где раньше был выброс.

5. @edutt - Слегка изменил ваше редактирование, чтобы использовать df.where вместо np.where . Это устраняет необходимость import numpy 🙂

, 190], [5123, '2021-01-01 00:00:00', 'card','items', 40], [5123, '2021-01-02 00:00:00', 'cash','sales

Магазин Дата Способ оплаты Атрибут Ценность
5123 2021-01-01 00:00:00 Наличными продажи$ 105
5123 2021-01-01 00:00:00 Наличными Товары 20
5123 2021-01-01 00:00:00 карта продажи$ 190
5123 2021-01-01 00:00:00 карта Товары 40
5123 2021-01-02 00:00:00 Наличными продажи$ 75
5123 2021-01-02 00:00:00 Наличными Товары 10
5123 2021-01-02 00:00:00 карта продажи$ 170
5123 2021-01-02 00:00:00 карта Товары 35
5123 2021-01-03 00:00:00 Наличными продажи$ 1000
5123 2021-01-03 00:00:00 Наличными Товары 500
5123 2021-01-03 00:00:00 карта продажи$ 150
5123 2021-01-03 00:00:00 карта Товары 20

I would like to filter outliers and replace them with the average value from the preceding 2 days. My "outlier rule" is such: if a value for an attribute/payment method is more than twice as big, or smaller than half as big, as the average value for that attribute/payment method from the preceding two days, then replace that outlier with the average value from the preceding two days. Otherwise, leave the value. In this case, all values should remain except for the $1000 sales and 500 items for 5123/'2021-01-03'/'cash'. Those values should be replaced with $90 for sales, and 15 for items.

Here is my attempt (using a for loop, which doesn't work). Whenever I am using a loop and Pandas together, a red flag goes off in my head. What is the correct way to do this?


На случай, если кому-то интересно, почему я использую этот метод скользящего среднего вместо чего-то вроде метода Тьюки, отсекающего данные более или менее 1,5*IQR от 1Q и 3Q, мои данные представляют собой временные ряды за период COVID, что означает, что IQR очень большой (высокие продажи до COVID, а затем глубокая яма отсутствия продаж после), поэтому IQR в конечном итоге ничего не фильтрует. Я не хочу удалять удаление COVID, а скорее удаляю некоторые ошибочные ошибки ввода данных (некоторые магазины плохо относятся к этому и могут вводить несколько дополнительных нулей в некоторые дни...). Вместо того, чтобы использовать последние два дня в качестве скользящего фильтра, я, вероятно, в конечном итоге буду использовать 5 или 7 дней (в течение недели). Я также открыт для других способов выполнения этой очистки / удаления выбросов.

Ответ №1:

Попробуй:


Комментарии:

1. Почти! Когда я это делаю, я теряю около 10% своих данных (мои фактические данные находятся в десятках миллионов строк). Не могли бы вы, пожалуйста, описать, что делают цепные методы?

2. Группировка по разным столбцам и вычисление скользящего среднего значения за 2 периода. Затем мы используем это для фильтрации значений от 0,5 до 2. Какая часть неясна?

3. Что здесь делает .shift ()? Я вижу, что выполнение этого кода без .shift() создает серию из 8 ненулевых записей, а выполнение с .shift() создает правильную серию из 4 ненулевых записей. Я думал, что сдвиг перемещает строки вниз, поэтому я бы подумал, что 537,5 (последняя запись в серии без использования .shift()) отвалилась бы, оставив 7 ненулевых записей. Отдельно от вопроса о сдвиге, похоже, что две строки с выбросами были просто полностью удалены, а не заменены средним значением, что объясняет, почему сейчас у меня отсутствуют данные.

4. Понял. Отредактирую ваш пост, чтобы показать, что я сделал. В итоге я добавил sort_index() в средний ряд, а затем использовал np.where() для заполнения начальных значений фрейма данных средним значением, где раньше был выброс.

5. @edutt - Слегка изменил ваше редактирование, чтобы использовать df.where вместо np.where . Это устраняет необходимость import numpy 🙂

, 75], [5123, '2021-01-02 00:00:00', 'cash','items', 10], [5123, '2021-01-02 00:00:00', 'card','sales

Магазин Дата Способ оплаты Атрибут Ценность
5123 2021-01-01 00:00:00 Наличными продажи$ 105
5123 2021-01-01 00:00:00 Наличными Товары 20
5123 2021-01-01 00:00:00 карта продажи$ 190
5123 2021-01-01 00:00:00 карта Товары 40
5123 2021-01-02 00:00:00 Наличными продажи$ 75
5123 2021-01-02 00:00:00 Наличными Товары 10
5123 2021-01-02 00:00:00 карта продажи$ 170
5123 2021-01-02 00:00:00 карта Товары 35
5123 2021-01-03 00:00:00 Наличными продажи$ 1000
5123 2021-01-03 00:00:00 Наличными Товары 500
5123 2021-01-03 00:00:00 карта продажи$ 150
5123 2021-01-03 00:00:00 карта Товары 20

I would like to filter outliers and replace them with the average value from the preceding 2 days. My "outlier rule" is such: if a value for an attribute/payment method is more than twice as big, or smaller than half as big, as the average value for that attribute/payment method from the preceding two days, then replace that outlier with the average value from the preceding two days. Otherwise, leave the value. In this case, all values should remain except for the $1000 sales and 500 items for 5123/'2021-01-03'/'cash'. Those values should be replaced with $90 for sales, and 15 for items.

Here is my attempt (using a for loop, which doesn't work). Whenever I am using a loop and Pandas together, a red flag goes off in my head. What is the correct way to do this?


На случай, если кому-то интересно, почему я использую этот метод скользящего среднего вместо чего-то вроде метода Тьюки, отсекающего данные более или менее 1,5*IQR от 1Q и 3Q, мои данные представляют собой временные ряды за период COVID, что означает, что IQR очень большой (высокие продажи до COVID, а затем глубокая яма отсутствия продаж после), поэтому IQR в конечном итоге ничего не фильтрует. Я не хочу удалять удаление COVID, а скорее удаляю некоторые ошибочные ошибки ввода данных (некоторые магазины плохо относятся к этому и могут вводить несколько дополнительных нулей в некоторые дни...). Вместо того, чтобы использовать последние два дня в качестве скользящего фильтра, я, вероятно, в конечном итоге буду использовать 5 или 7 дней (в течение недели). Я также открыт для других способов выполнения этой очистки / удаления выбросов.

Ответ №1:

Попробуй:


Комментарии:

1. Почти! Когда я это делаю, я теряю около 10% своих данных (мои фактические данные находятся в десятках миллионов строк). Не могли бы вы, пожалуйста, описать, что делают цепные методы?

2. Группировка по разным столбцам и вычисление скользящего среднего значения за 2 периода. Затем мы используем это для фильтрации значений от 0,5 до 2. Какая часть неясна?

3. Что здесь делает .shift ()? Я вижу, что выполнение этого кода без .shift() создает серию из 8 ненулевых записей, а выполнение с .shift() создает правильную серию из 4 ненулевых записей. Я думал, что сдвиг перемещает строки вниз, поэтому я бы подумал, что 537,5 (последняя запись в серии без использования .shift()) отвалилась бы, оставив 7 ненулевых записей. Отдельно от вопроса о сдвиге, похоже, что две строки с выбросами были просто полностью удалены, а не заменены средним значением, что объясняет, почему сейчас у меня отсутствуют данные.

4. Понял. Отредактирую ваш пост, чтобы показать, что я сделал. В итоге я добавил sort_index() в средний ряд, а затем использовал np.where() для заполнения начальных значений фрейма данных средним значением, где раньше был выброс.

5. @edutt - Слегка изменил ваше редактирование, чтобы использовать df.where вместо np.where . Это устраняет необходимость import numpy 🙂

, 170], [5123, '2021-01-02 00:00:00', 'card','items', 35], [5123, '2021-01-03 00:00:00', 'cash','sales

Магазин Дата Способ оплаты Атрибут Ценность
5123 2021-01-01 00:00:00 Наличными продажи$ 105
5123 2021-01-01 00:00:00 Наличными Товары 20
5123 2021-01-01 00:00:00 карта продажи$ 190
5123 2021-01-01 00:00:00 карта Товары 40
5123 2021-01-02 00:00:00 Наличными продажи$ 75
5123 2021-01-02 00:00:00 Наличными Товары 10
5123 2021-01-02 00:00:00 карта продажи$ 170
5123 2021-01-02 00:00:00 карта Товары 35
5123 2021-01-03 00:00:00 Наличными продажи$ 1000
5123 2021-01-03 00:00:00 Наличными Товары 500
5123 2021-01-03 00:00:00 карта продажи$ 150
5123 2021-01-03 00:00:00 карта Товары 20

I would like to filter outliers and replace them with the average value from the preceding 2 days. My "outlier rule" is such: if a value for an attribute/payment method is more than twice as big, or smaller than half as big, as the average value for that attribute/payment method from the preceding two days, then replace that outlier with the average value from the preceding two days. Otherwise, leave the value. In this case, all values should remain except for the $1000 sales and 500 items for 5123/'2021-01-03'/'cash'. Those values should be replaced with $90 for sales, and 15 for items.

Here is my attempt (using a for loop, which doesn't work). Whenever I am using a loop and Pandas together, a red flag goes off in my head. What is the correct way to do this?


На случай, если кому-то интересно, почему я использую этот метод скользящего среднего вместо чего-то вроде метода Тьюки, отсекающего данные более или менее 1,5*IQR от 1Q и 3Q, мои данные представляют собой временные ряды за период COVID, что означает, что IQR очень большой (высокие продажи до COVID, а затем глубокая яма отсутствия продаж после), поэтому IQR в конечном итоге ничего не фильтрует. Я не хочу удалять удаление COVID, а скорее удаляю некоторые ошибочные ошибки ввода данных (некоторые магазины плохо относятся к этому и могут вводить несколько дополнительных нулей в некоторые дни...). Вместо того, чтобы использовать последние два дня в качестве скользящего фильтра, я, вероятно, в конечном итоге буду использовать 5 или 7 дней (в течение недели). Я также открыт для других способов выполнения этой очистки / удаления выбросов.

Ответ №1:

Попробуй:


Комментарии:

1. Почти! Когда я это делаю, я теряю около 10% своих данных (мои фактические данные находятся в десятках миллионов строк). Не могли бы вы, пожалуйста, описать, что делают цепные методы?

2. Группировка по разным столбцам и вычисление скользящего среднего значения за 2 периода. Затем мы используем это для фильтрации значений от 0,5 до 2. Какая часть неясна?

3. Что здесь делает .shift ()? Я вижу, что выполнение этого кода без .shift() создает серию из 8 ненулевых записей, а выполнение с .shift() создает правильную серию из 4 ненулевых записей. Я думал, что сдвиг перемещает строки вниз, поэтому я бы подумал, что 537,5 (последняя запись в серии без использования .shift()) отвалилась бы, оставив 7 ненулевых записей. Отдельно от вопроса о сдвиге, похоже, что две строки с выбросами были просто полностью удалены, а не заменены средним значением, что объясняет, почему сейчас у меня отсутствуют данные.

4. Понял. Отредактирую ваш пост, чтобы показать, что я сделал. В итоге я добавил sort_index() в средний ряд, а затем использовал np.where() для заполнения начальных значений фрейма данных средним значением, где раньше был выброс.

5. @edutt - Слегка изменил ваше редактирование, чтобы использовать df.where вместо np.where . Это устраняет необходимость import numpy 🙂

, 1000], [5123, '2021-01-03 00:00:00', 'cash','items', 500], [5123, '2021-01-03 00:00:00', 'card','sales

Магазин Дата Способ оплаты Атрибут Ценность
5123 2021-01-01 00:00:00 Наличными продажи$ 105
5123 2021-01-01 00:00:00 Наличными Товары 20
5123 2021-01-01 00:00:00 карта продажи$ 190
5123 2021-01-01 00:00:00 карта Товары 40
5123 2021-01-02 00:00:00 Наличными продажи$ 75
5123 2021-01-02 00:00:00 Наличными Товары 10
5123 2021-01-02 00:00:00 карта продажи$ 170
5123 2021-01-02 00:00:00 карта Товары 35
5123 2021-01-03 00:00:00 Наличными продажи$ 1000
5123 2021-01-03 00:00:00 Наличными Товары 500
5123 2021-01-03 00:00:00 карта продажи$ 150
5123 2021-01-03 00:00:00 карта Товары 20

I would like to filter outliers and replace them with the average value from the preceding 2 days. My "outlier rule" is such: if a value for an attribute/payment method is more than twice as big, or smaller than half as big, as the average value for that attribute/payment method from the preceding two days, then replace that outlier with the average value from the preceding two days. Otherwise, leave the value. In this case, all values should remain except for the $1000 sales and 500 items for 5123/'2021-01-03'/'cash'. Those values should be replaced with $90 for sales, and 15 for items.

Here is my attempt (using a for loop, which doesn't work). Whenever I am using a loop and Pandas together, a red flag goes off in my head. What is the correct way to do this?


На случай, если кому-то интересно, почему я использую этот метод скользящего среднего вместо чего-то вроде метода Тьюки, отсекающего данные более или менее 1,5*IQR от 1Q и 3Q, мои данные представляют собой временные ряды за период COVID, что означает, что IQR очень большой (высокие продажи до COVID, а затем глубокая яма отсутствия продаж после), поэтому IQR в конечном итоге ничего не фильтрует. Я не хочу удалять удаление COVID, а скорее удаляю некоторые ошибочные ошибки ввода данных (некоторые магазины плохо относятся к этому и могут вводить несколько дополнительных нулей в некоторые дни...). Вместо того, чтобы использовать последние два дня в качестве скользящего фильтра, я, вероятно, в конечном итоге буду использовать 5 или 7 дней (в течение недели). Я также открыт для других способов выполнения этой очистки / удаления выбросов.

Ответ №1:

Попробуй:


Комментарии:

1. Почти! Когда я это делаю, я теряю около 10% своих данных (мои фактические данные находятся в десятках миллионов строк). Не могли бы вы, пожалуйста, описать, что делают цепные методы?

2. Группировка по разным столбцам и вычисление скользящего среднего значения за 2 периода. Затем мы используем это для фильтрации значений от 0,5 до 2. Какая часть неясна?

3. Что здесь делает .shift ()? Я вижу, что выполнение этого кода без .shift() создает серию из 8 ненулевых записей, а выполнение с .shift() создает правильную серию из 4 ненулевых записей. Я думал, что сдвиг перемещает строки вниз, поэтому я бы подумал, что 537,5 (последняя запись в серии без использования .shift()) отвалилась бы, оставив 7 ненулевых записей. Отдельно от вопроса о сдвиге, похоже, что две строки с выбросами были просто полностью удалены, а не заменены средним значением, что объясняет, почему сейчас у меня отсутствуют данные.

4. Понял. Отредактирую ваш пост, чтобы показать, что я сделал. В итоге я добавил sort_index() в средний ряд, а затем использовал np.where() для заполнения начальных значений фрейма данных средним значением, где раньше был выброс.

5. @edutt - Слегка изменил ваше редактирование, чтобы использовать df.where вместо np.where . Это устраняет необходимость import numpy 🙂

, 150], [5123, '2021-01-03 00:00:00', 'card','items', 20]] columns = ['Store', 'Date', 'Payment Method', 'Attribute', 'Value'] df = pd.DataFrame(data = data, columns = columns)

Магазин Дата Способ оплаты Атрибут Ценность
5123 2021-01-01 00:00:00 Наличными продажи$ 105
5123 2021-01-01 00:00:00 Наличными Товары 20
5123 2021-01-01 00:00:00 карта продажи$ 190
5123 2021-01-01 00:00:00 карта Товары 40
5123 2021-01-02 00:00:00 Наличными продажи$ 75
5123 2021-01-02 00:00:00 Наличными Товары 10
5123 2021-01-02 00:00:00 карта продажи$ 170
5123 2021-01-02 00:00:00 карта Товары 35
5123 2021-01-03 00:00:00 Наличными продажи$ 1000
5123 2021-01-03 00:00:00 Наличными Товары 500
5123 2021-01-03 00:00:00 карта продажи$ 150
5123 2021-01-03 00:00:00 карта Товары 20

I would like to filter outliers and replace them with the average value from the preceding 2 days. My «outlier rule» is such: if a value for an attribute/payment method is more than twice as big, or smaller than half as big, as the average value for that attribute/payment method from the preceding two days, then replace that outlier with the average value from the preceding two days. Otherwise, leave the value. In this case, all values should remain except for the $1000 sales and 500 items for 5123/’2021-01-03’/’cash’. Those values should be replaced with $90 for sales, and 15 for items.

Here is my attempt (using a for loop, which doesn’t work). Whenever I am using a loop and Pandas together, a red flag goes off in my head. What is the correct way to do this?


На случай, если кому-то интересно, почему я использую этот метод скользящего среднего вместо чего-то вроде метода Тьюки, отсекающего данные более или менее 1,5*IQR от 1Q и 3Q, мои данные представляют собой временные ряды за период COVID, что означает, что IQR очень большой (высокие продажи до COVID, а затем глубокая яма отсутствия продаж после), поэтому IQR в конечном итоге ничего не фильтрует. Я не хочу удалять удаление COVID, а скорее удаляю некоторые ошибочные ошибки ввода данных (некоторые магазины плохо относятся к этому и могут вводить несколько дополнительных нулей в некоторые дни…). Вместо того, чтобы использовать последние два дня в качестве скользящего фильтра, я, вероятно, в конечном итоге буду использовать 5 или 7 дней (в течение недели). Я также открыт для других способов выполнения этой очистки / удаления выбросов.

Ответ №1:

Попробуй:


Комментарии:

1. Почти! Когда я это делаю, я теряю около 10% своих данных (мои фактические данные находятся в десятках миллионов строк). Не могли бы вы, пожалуйста, описать, что делают цепные методы?

2. Группировка по разным столбцам и вычисление скользящего среднего значения за 2 периода. Затем мы используем это для фильтрации значений от 0,5 до 2. Какая часть неясна?

3. Что здесь делает .shift ()? Я вижу, что выполнение этого кода без .shift() создает серию из 8 ненулевых записей, а выполнение с .shift() создает правильную серию из 4 ненулевых записей. Я думал, что сдвиг перемещает строки вниз, поэтому я бы подумал, что 537,5 (последняя запись в серии без использования .shift()) отвалилась бы, оставив 7 ненулевых записей. Отдельно от вопроса о сдвиге, похоже, что две строки с выбросами были просто полностью удалены, а не заменены средним значением, что объясняет, почему сейчас у меня отсутствуют данные.

4. Понял. Отредактирую ваш пост, чтобы показать, что я сделал. В итоге я добавил sort_index() в средний ряд, а затем использовал np.where() для заполнения начальных значений фрейма данных средним значением, где раньше был выброс.

5. @edutt — Слегка изменил ваше редактирование, чтобы использовать df.where вместо np.where . Это устраняет необходимость import numpy 🙂