我正在尝试在组级别上删除异常值列(Property1、Property2 等),需要您的帮助。
这是我的示例数据框,包含 2 个组和 4 个属性。
团体 | 财产1 | 房产2 | 房产3 | 房产4 |
---|---|---|---|---|
A | 10 | 15 | 1.5 | 5 |
B | 11 | 14 | 1.7 | 4.8 |
B | 11 | 14.1 | 1.8 | 5 |
A | 9 | 13 | 3.5 | 21 |
B | 8 | 40 | 1.2 | 3 |
以下是我只有一组时使用的代码。我正在使用 GESD 方法来删除异常值并使用 PyAstronomy 库的 GeneralizedESD 函数。
for column in dataset.iloc[:, 1:]: #iterate through each column that needs outlier removed
column1 = dataset[column].dropna() #drop na
column1.reset_index(drop=True, inplace=True) # reset index to account for removed NAs
length = len(column1) #calculate number of rows
max_outlier = int(length*0.25) #set max number of outliers
if length>2:
r = pyasl.generalizedESD(column1, max_outlier, 0.05, fullOutput=False)
i = r[1] # array of indices of the outliers
a = (column1.loc[i]) # array of outlier values
for x in a:
dataset.loc[dataset[column]==x, column] = np.nan #replace outlier with nan
我想在组级别上执行此操作,其中该操作为两个组中的每一个运行 2 次,结果是用 NAN 替换异常值的单个 df。 谁能建议一种方法来做到这一点? 我尝试将上面的代码包装在一个函数中并使用 groupby 和 apply。但我没有得到想要的结果。
首先,您的示例很容易失败,因为如果每组的列数太少,您最终会得到
max_outlier = 0
,这是 generalizedESD
不喜欢的。因此,让我们从一个更好的示例数据集开始:
import numpy as np
import pandas as pd
from PyAstronomy import pyasl
# Create synthetic data of three groups with random outliers (values ~20)
n = 50
dataset = pd.DataFrame({
"Group":
np.random.choice(["A", "B", "C"], size=n),
"P1":
np.random.rand(n) + np.random.choice([0., 20.], p=[0.9, 0.1], size=n),
"P2":
np.random.rand(n) + np.random.choice([0., 20.], p=[0.9, 0.1], size=n),
"P3":
np.random.rand(n) + np.random.choice([0., 20.], p=[0.9, 0.1], size=n),
"P4":
np.random.rand(n) + np.random.choice([0., 20.], p=[0.9, 0.1], size=n)
})
dataset = dataset.set_index("Group")
接下来,您可以像这样逐列替换整个数据集上的值:
def replace_outliers_columnwise(col):
max_out = len(col) // 4
_, idxs = pyasl.generalizedESD(col, max_out)
col.iloc[idxs] = np.nan
return col
dataset = dataset.apply(replace_outliers_columnwise, axis=0)
print(dataset)
这将打印您开始使用的完全相同的数据集,但用 NaN 替换异常值。
请注意,上面的
generalizedESD
不只是返回一些东西,而是返回一个元组,相关的就是我所说的idxs
,一个包含被检测为异常值的元素的索引的数组。
相反,如果出于某种原因您想按组执行此操作,那么您可以先分组然后应用到每个组,就像您提到的:
def replace_outliers_groupwise(group):
return group.apply(replace_outliers_columnwise)
dataset = dataset.groupby("Group").apply(replace_outliers_groupwise)
print(dataset)
它将打印相同的数据帧,但现在带有稍微不同的索引,表示数据帧被分割成的不同组。