【问题标题】:How to find values below (or above) average如何找到低于(或高于)平均值的值
【发布时间】:2017-02-18 10:58:27
【问题描述】:

从以下摘要中可以看出,9 月 1 日的计数 (1542677) 远低于每月的平均计数。

from StringIO import StringIO

myst="""01/01/2016  8781262
01/02/2016  8958598
01/03/2016  8787628
01/04/2016  9770861
01/05/2016  8409410
01/06/2016  8924784
01/07/2016  8597500
01/08/2016  6436862
01/09/2016  1542677
"""
u_cols=['month', 'count']

myf = StringIO(myst)
import pandas as pd
df = pd.read_csv(StringIO(myst), sep='\t', names = u_cols)

是否有一个数学公式可以定义这个“远低于或太高”(模棱两可)的概念?

如果我定义一个限制(例如 9 或 10%),这很容易。但是如果最低值和倒数第二个最低值之间的差值超过总体 5%,我希望脚本为我决定并返回值。在这种情况下,应返回 9 月的月份计数。

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    首先,您提到的“低于或太高”的概念被称为Outlier,并引用维基百科(不是最佳来源),

    对于异常值的构成,没有严格的数学定义;确定观察结果是否为异常值最终是一项主观活动。

    但另一方面:

    一般来说,如果人口分布的性质是先验已知的,则可以测试异常值的数量是否显着偏离预期值。

    所以在我看来,这归结为一个问题,是否有可能对您的数据的性质做出假设,以便能够自动化此类决策。

    直截了当的方法

    如果您有幸拥有相对较大的样本量,并且您的不同样本不相关,则可以应用central limit theorem,它表明您的值将遵循正态分布 (有关 python 的解释,请参阅this)。

    在这种情况下,您也许能够快速获得给定数据集的平均值标准差。通过将the corresponding function(使用这两个参数)应用于每个给定值,您可以计算其属于“集群”的概率(请参阅此stackoverflow post 了解可能的python 解决方案)。

    那么您确实必须设置一个下限,因为只有当一个点与平均值无限远时,此分布才会返回 0% 的概率。但好消息是(如果假设成立)这个界限将很好地适应每个不同的数据集,因为它具有指数级、标准化的性质。此界限通常以 Sigma 单位表示,并广泛用于科学和统计中。事实上,致力于发现希格斯玻色子的 2013 年物理学诺贝尔奖是在达到5-sigma 范围后授予的,引用链接:

    高能物理学需要更低的 p 值来宣布证据或发现。 “粒子证据”的阈值对应p=0.003,“发现”的标准是p=0.0000003。

    替代方案

    如果您无法对数据的外观做出如此简单的假设,您始终可以让程序推断它们。这种方法是大多数机器学习算法的核心特征,如果微调得当,它可以很好地适应强相关甚至倾斜的数据。如果这是你需要的,Python 有很多很好的库可以用于这个目的,甚至可以放在一个小脚本中(我最了解的是来自 google 的 tensorflow)。

    在这种情况下,我会考虑两种不同的方法,这又取决于您的数据看起来如何:

    • 监督学习:如果您有一个训练集可供使用,它会说明哪些样本属于哪些样本不属于(称为标记),有像 support vector machine 这样的算法,虽然轻量级,但可以惊人地适应高度非线性的边界。

    • 无监督学习:这可能是我首先要尝试的:当您只拥有 未标记 数据集时。我之前提到的“直截了​​当的方法”是异常检测器的最简单情况,因此可以进行高度调整和定制,以考虑甚至无限维度的相关性,这要归功于kernel trick。要了解基于 ML 的异常检测器的动机和方法,我建议您查看 Andrew Ng 的videos

    希望对你有帮助! 干杯

    【讨论】:

    • 透彻而深刻。当然值得一票:-)
    【解决方案2】:

    过滤异常值的一种方法是四分位间距(IQR,wikipedia),即 75%(Q3)和 25% 四分位(Q1)之间的差值。

    如果数据低于 Q1 - k * IQR resp,则定义异常值。高于 Q3 + k * IQR。

    您可以根据您的领域知识选择常数 k(常见的选择是 1.5)。

    根据数据,pandas 中的过滤器可能如下所示:

    iqr_filter = pd.DataFrame(df["count"].quantile([0.25, 0.75])).T
    iqr_filter["iqr"] = iqr_filter[0.75]-iqr_filter[0.25]
    iqr_filter["lo"] = iqr_filter[0.25] - 1.5*iqr_filter["iqr"]
    iqr_filter["up"] = iqr_filter[0.75] + 1.5*iqr_filter["iqr"]
    df_filtered = df.loc[(df["count"] > iqr_filter["lo"][0]) & (df["count"] < iqr_filter["up"][0]), :]
    

    【讨论】:

    【解决方案3】:

    过滤异常值的一种非常常见的方法是使用标准差。在这种情况下,我们将计算一个zscore,它将快速确定每个观察值与平均值相差多少标准差。然后我们可以过滤那些大于 2 个标准差的观察值。对于正态分布的随机变量,这应该发生在大约 5% 的时间。

    定义一个 zscore 函数

    def zscore(s):
        return (s - np.mean(s)) / np.std(s)
    

    将其应用于count

    zscore(df['count'])
    
    0    0.414005
    1    0.488906
    2    0.416694
    3    0.831981
    4    0.256946
    5    0.474624
    6    0.336390
    7   -0.576197
    8   -2.643349
    Name: count, dtype: float64
    

    请注意,9 月的观测值相差 2.6 个标准差。

    使用absgt 识别异常值

    zscore(df['count']).abs().gt(2)
    
    0    False
    1    False
    2    False
    3    False
    4    False
    5    False
    6    False
    7    False
    8     True
    Name: count, dtype: bool
    

    9 月再次回归。

    将它们捆绑在一起以过滤您的原始数据框

    df[zscore(df['count']).abs().gt(2)]
    

    以另一种方式过滤

    df[zscore(df['count']).abs().le(2)]
    

    【讨论】:

    • 如果我将 April 值从 9770861 更改为 977086。它不会返回任何内容。在这种情况下,我预计四月和九月。超出范围可能超过 2 个月!
    • @shantanuo 当然,您正在用很少的观察重新定义分布的样子。如果这些是您仅有的观察结果,那么 4 月的新值将意味着 9 月的值不再像我们想象的那样“奇怪”。您仍然可以通过将阈值从 2 降低到 1.5 来捕获两者,我不推荐这样做。或者您可以相信,由于您有更多的观测值要计算平均值,这两个值实际上会被识别为异常值。
    • 将阈值从 2 更改为 1.5 解决了我的问题。这正是我一直在寻找的。你有什么不推荐这个的具体原因吗?
    • @shantanuo,这种方法仅适用于您的样本量很大(以及其他条件)。在这种情况下,您可以确定样本的平均百分比是多少,即:2 sigma 容差为 95%,1.5 为 86%。如果您的示例很少,但仍然不希望有固定的容差,您可能真的想手动完成,或者尝试像我描述的那样的 ML 方法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-02
    • 2020-05-23
    相关资源
    最近更新 更多