[英]Python Pandas - Faster Way to Iterate Through Categories in Data While Removing Outliers (Without For Loop)
假設我有一個 dataframe 像這樣:
import pandas as pd
import numpy as np
data = [[5123, '2021-01-01 00:00:00', 'cash','sales$', 105],
[5123, '2021-01-01 00:00:00', 'cash','items', 20],
[5123, '2021-01-01 00:00:00', 'card','sales$', 190],
[5123, '2021-01-01 00:00:00', 'card','items', 40],
[5123, '2021-01-02 00:00:00', 'cash','sales$', 75],
[5123, '2021-01-02 00:00:00', 'cash','items', 10],
[5123, '2021-01-02 00:00:00', 'card','sales$', 170],
[5123, '2021-01-02 00:00:00', 'card','items', 35],
[5123, '2021-01-03 00:00:00', 'cash','sales$', 1000],
[5123, '2021-01-03 00:00:00', 'cash','items', 500],
[5123, '2021-01-03 00:00:00', 'card','sales$', 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 |
我想過濾異常值並將它們替換為前 2 天的平均值。 我的“異常值規則”是這樣的:如果屬性/支付方式的值是前兩天該屬性/支付方式的平均值的兩倍多或小於一半,則替換它與前兩天的平均值的異常值。 否則,保留該值。 在這種情況下,除了 5123/'2021-01-03'/'cash' 的 1000 美元銷售額和 500 件商品外,所有值都應保留。 這些值應替換為 90 美元的銷售額和 15 美元的商品。
這是我的嘗試(使用 for 循環,它不起作用)。 每當我同時使用循環和 Pandas 時,我的腦海中就會出現紅旗。 這樣做的正確方法是什么?
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'] <= (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'] >= (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)
如果有人好奇我為什么要使用這種滾動平均方法,而不是像 Tukey 的方法將數據從 1Q 和 3Q 截斷多於或少於 1.5*IQR 的方法,我的數據是 COVID 期間的時間序列,這意味着IQR 非常大(在 COVID 之前銷量很高,之后是銷售不足的深坑),因此 IQR 最終沒有過濾任何東西。 我不想刪除 COVID 刪除,而是刪除一些錯誤的數據輸入失敗(有些商店對此不好,並且可能會在某些日子輸入一些額外的零......)。 我最終可能會使用 5 或 7 天(一周),而不是使用最后兩天作為滾動過濾器。 我也願意接受其他方式來進行這種清理/異常值刪除。
嘗試:
#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)
)
#divide values by the average and keep only those ratios that fall between 0.5 and 2
output = df[df["Value"].div(average).fillna(1).between(0.5,2)]
>>> output
Store Date Payment Method Attribute Value
0 5123 2021-01-01 00:00:00 cash sales$ 105
1 5123 2021-01-01 00:00:00 cash items 20
2 5123 2021-01-01 00:00:00 card sales$ 190
3 5123 2021-01-01 00:00:00 card items 40
4 5123 2021-01-02 00:00:00 cash sales$ 75
5 5123 2021-01-02 00:00:00 cash items 10
6 5123 2021-01-02 00:00:00 card sales$ 170
7 5123 2021-01-02 00:00:00 card items 35
10 5123 2021-01-03 00:00:00 card sales$ 150
11 5123 2021-01-03 00:00:00 card items 20
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.