【问题标题】:pandas outliers with and without calculations有和没有计算的熊猫离群值
【发布时间】:2022-11-01 22:07:20
【问题描述】:

我正在考虑对具有 300 多个特征的数据集上的异常值做出决策。我想在不匆忙删除数据的情况下分析框架。我有一个框架:

 |    |   A |   B |    C |   D |   E |
 |---:|----:|----:|-----:|----:|----:|
 |  0 | 100 |  99 | 1000 | 300 | 250 |
 |  1 | 665 |   6 |    9 |   1 |   9 |
 |  2 |   7 | 665 |    4 |   9 |   1 |
 |  3 |   1 |   3 |    4 |   3 |   6 |
 |  4 |   1 |   9 |    1 | 665 |   5 |
 |  5 |   3 |   4 |    6 |   1 |   9 |
 |  6 |   5 |   9 |    1 |   3 |   2 |
 |  7 |   1 | 665 |    3 |   2 |   3 |
 |  8 |   2 | 665 |    9 |   1 |   0 |
 |  9 |   5 |   0 |    7 |   6 |   5 |
 | 10 |   0 |   3 |    3 |   7 |   3 |
 | 11 |   6 |   3 |    0 |   3 |   6 |
 | 12 |   6 |   6 |    5 |   1 |   5 |

我编写了一些内省代码,以保存在另一个名为 _outliers 的框架中:

Q1 = df.quantile(0.25)
Q3 = df.quantile(0.75)
IQR = (Q3 - Q1)
min_ = (Q1 - (1.5 * IQR))
max_ = (Q3 + (1.5 * IQR))
# Counts outliers in columns
_outliers = ((df.le (min_)) | (df.ge (max_))).sum().to_frame(name="outliers")
# Gives percentage of data that outliers represent in the column
_outliers["percent"] = (_outliers['outliers'] / _outliers['outliers'].sum()) * 100
# Shows max value in the column
_outliers["max_val"] = df[_outliers.index].max()
# Shows min value in the column
_outliers["min_val"] = df[_outliers.index].min()
# Shows median value in the column
_outliers["median"] = df[_outliers.index].median()
# Shows mean value in the column
_outliers["mean"] = df[_outliers.index].mean()

这会产生:

|    |   outliers |   percent |   max_val |   min_val |   median |     mean |
|:---|-----------:|----------:|----------:|----------:|---------:|---------:|
| A  |          2 |   22.2222 |       665 |         0 |        5 |  61.6923 |
| B  |          3 |   33.3333 |       665 |         0 |        6 | 164.385  |
| C  |          1 |   11.1111 |      1000 |         0 |        4 |  80.9231 |
| D  |          2 |   22.2222 |       665 |         1 |        3 |  77.0769 |
| E  |          1 |   11.1111 |       250 |         0 |        5 |  23.3846 |

我想通过计算没有它们的平均值和中位数来计算异常值对列的影响。我不想删除它们来进行此计算。我想最好的方法是将“~”添加到异常值过滤器中,但我迷失在代码中......这将使很多人受益,因为搜索删除异常值会产生很多结果。除了他们首先偷偷进入数据的原因之外,我只是认为不应该在不考虑潜在影响的情况下做出删除决定。随意添加其他考虑因素(偏度、西格玛、n 等)

一如既往,我感谢这个社区!

编辑:我添加了方差及其平方根标准差,有无异常值。在某些领域,您可能希望保留异常值并直接进入 ML。至少,通过事先检查您的数据,您会知道它们对您的结果有多大贡献。与异常值列中的 nlargest() 一起使用,您可以快速查看哪些特征包含最多。您可以通过设置方差或均值的阈值将其用作过滤特征的基础。感谢贡献者,我现在拥有了一个强大的分析工具。希望它对其他人有用。

【问题讨论】:

  • 您的数据集显然不正常,只有很少的观察。使用经典的 IRQ 标准将审查大量数据,因为异常值支配均值并提供一个完全没有数据的区间。例如列 A 将返回类似 [54; 69]但在这个区间内根本没有数据。为什么不根据百分位标准拒绝高价值,价值 > P99 被审查?
  • 这只是一个例子,也许它构造不正确,如果要删除异常值,我正在寻找计算平均值和中位数的代码......

标签: pandas average median outliers


【解决方案1】:

利用DataFrameapply 方法。

串联发电机

只需通过创建一个使用Series 并返回标量并将其应用于DataFrame 的方法来定义您希望应用稳健均值的方式。

对于 IRQ 均值,这是一个简单的 sn-p:

def irq_agg(x, factor=1.5, aggregate=pd.Series.mean):
    q1, q3 = x.quantile(0.25), x.quantile(0.75) 
    return aggregate(x[(q1 - factor*(q3 - q1) < x) & (x < q3 + factor*(q3 - q1))])

data.apply(irq_agg)

# A     3.363636
# B    14.200000
# C     4.333333
# D     3.363636
# E     4.500000
# dtype: float64

同样可以根据百分位数进行过滤(双方版本):

def quantile_agg(x, alpha=0.05, aggregate=pd.Series.mean):
    return aggregate(x[(x.quantile(alpha/2) < x) & (x < x.quantile(1 - alpha/2))])

data.apply(quantile_agg, alpha=0.01)

# A    12.454545
# B    15.777778
# C     4.727273
# D    41.625000
# E     4.909091
# dtype: float64

帧生成器

更好的是,创建一个返回Series 的函数,apply 将创建一个DataFrame。然后我们可以一次计算一堆不同的均值和中位数,以便比较它们。我们还可以重用上面定义的 Series 生成器方法:

def analyze(x, alpha=0.05, factor=1.5):
    return pd.Series({
        "p_mean": quantile_agg(x, alpha=alpha),
        "p_median": quantile_agg(x, alpha=alpha, aggregate=pd.Series.median),
        "irq_mean": irq_agg(x, factor=factor),
        "irq_median": irq_agg(x, factor=factor, aggregate=pd.Series.median),
        "standard": x[((x - x.mean())/x.std()).abs() < 1].mean(),
        "mean": x.mean(),
        "median": x.median(),
    })

data.apply(analyze).T

#       p_mean  p_median   irq_mean  irq_median   standard        mean  median
# A  12.454545       5.0   3.363636         3.0  11.416667   61.692308     5.0
# B  15.777778       6.0  14.200000         5.0  14.200000  164.384615     6.0
# C   4.727273       4.0   4.333333         4.0   4.333333   80.923077     4.0
# D  41.625000       4.5   3.363636         3.0   3.363636   77.076923     3.0
# E   4.909091       5.0   4.500000         5.0   4.500000   23.384615     5.0

现在,您可以通过多种方式过滤异常值,计算相关聚合,例如平均值或中值。

【讨论】:

  • 我不明白的是如何用“截止”捕获值范围的两端。它似乎在计算值,同时仅修剪列值的上部。例如,B 列有 3 个异常值,如果删除它们,您会得到 14,2 的平均值和 5 的中值。0.95 的截止值认为分位数介于 0<q>95% 的值之间。 .我需要IQR作为确定异常值的参考,而不是从0开始的列值范围。我们需要捕获上限和下限异常值。除非我没有得到你的方法。
  • @ Zen4ttitude 我在我的第一个版本中犯了一个错误,我使用了以平均值为中心的 IRQ,而应该分别从 Q1 和 Q3 中减去它。我更新了答案以反映这一变化,现在您可以根据需要过滤掉异常值并聚合。
【解决方案2】:

没有评论这是否是过滤异常值的合适方法。下面的代码应该可以满足您的要求:

q1, q3 = df.quantile([0.25, 0.75]).to_numpy()
delta = (q3 - q1) * 1.5
min_val, max_val = q1 - delta, q3 + delta
outliers = (df < min_val) | (max_val < df)

result = pd.concat(
    [
        pd.DataFrame(
            {
                "outliers": outliers.sum(),
                "percent": outliers.sum() / outliers.sum().sum() * 100,
                "max_val": max_val,
                "min_val": min_val,
            }
        ),
        df.agg(["median", "mean"]).T,
        df.mask(outliers, np.nan).agg(["median", "mean"]).T.add_suffix("_no_outliers"),
    ],
    axis=1,
)

结果:

   outliers    percent  max_val  min_val  median        mean  median_no_outliers  mean_no_outliers
A         2  15.384615     13.5     -6.5     5.0   61.692308                 3.0          3.363636
B         3  23.076923    243.0   -141.0     6.0  164.384615                 5.0         14.200000
C         1   7.692308     13.0     -3.0     4.0   80.923077                 4.0          4.333333
D         2  15.384615     16.0     -8.0     3.0   77.076923                 3.0          3.363636
E         1   7.692308     10.5     -1.5     5.0   23.384615                 5.0          4.500000

【讨论】:

  • 都在这里...df.mask(outliers, np.nan).agg(["median", "mean"]).T.add_suffix("_no_outliers") ], axis=1) 并且从我的技能中消失了!谢谢
  • 谢谢(刚刚意识到 outliers.sum().rename("percent") / len(df) * 100 有一点错误,因为它使用异常值的数量而不是它们的值来计算它们在列中的数据百分比称重。
  • 感谢您指出错误。固定的
  • 你所做的很好,但我想我可能已经不清楚地表达了我想要的东西。我想查看异常值相对于列中所有其他值的“权重”。由于我的示例被夸大了,因此数字会超出预期,但我正在寻找执行此操作的代码。在这里,我们只是简单地计算它们,所以百分比是不正确的。我们可以保留百分比但在值方面引入权重?
  • 你的代码中的这个 outliers.sum().sum() 应该是 len(df) 不是吗?
猜你喜欢
  • 1970-01-01
  • 2018-08-30
  • 1970-01-01
  • 2014-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多