用于估计统计中值,众数,模式,偏度,峰度的“在线”(迭代器)算法?


86

是否有一种算法可以估计一组值的中值,众数,偏度和/或峰度,但是不需要一次将所有值存储在内存中?

我想计算基本统计数据:

  • 平均值:算术平均值
  • 方差:与平均值的平方偏差的平均值
  • 标准偏差:方差的平方根
  • 中位数:将数字大半部分与小半部分分开的值
  • 模式:集合中最常出现的值
  • 偏度:tl; 博士
  • 峰度:tl; 博士

计算其中任何一个的基本公式是小学算术,我确实知道它们。也有许多实现它们的统计资料库。

我的问题是我正在处理的集合中有大量(十亿个)值:在Python中工作,我不能仅仅创建包含数十亿个元素的列表或哈希。即使我用C编写此代码,十亿个元素的数组也不太实用。

数据未排序。它是由其他过程动态随机产生的。每个集合的大小是高度可变的,并且大小不会事先知道。

我已经弄清楚了如何很好地处理均值和方差,以任意顺序遍历集合中的每个值。(实际上,就我而言,我按它们生成的顺序进行处理。)这是我使用的算法,由http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#On-line_algorithm提供

  • 初始化三个变量:count,sum和sum_of_squares
  • 对于每个值:
    • 增量计数。
    • 将值相加。
    • 将值的平方添加到sum_of_squares。
  • 将总和除以计数,并存储为变量均值。
  • 将sum_of_squares除以计数,存储为变量mean_of_squares。
  • 平方均值,存储为square_of_mean。
  • 从mean_of_squares中减去square_of_mean,并存储为方差。
  • 输出均值和方差。

这种“在线”算法具有弱点(例如,由于sum_of_squares迅速增长到大于整数范围或浮点精度的精度问题),但是它基本上满足了我的需要,而不必在每个集合中存储每个值。

但是我不知道是否存在类似的技术来估算其他统计数据(中位数,众数,偏度,峰度)。只要处理N个值所需的内存大大小于O(N),我就可以使用有偏估计器,甚至可以使用在某种程度上损害准确性的方法。

如果该库具有“在线”计算这些操作中的一项或多项的功能,则将我指向现有的统计信息库也将有所帮助。


数据将按顺序传递,您是否会事先知道输入的数量?
chillysapien 2009年

:在计算器上有用的现有链接stackoverflow.com/questions/895929/...
dmckee ---前主持人小猫

那是整数数据还是浮点数据?您有最大值或最小值吗?
stephan

dmckee:我实际上使用的是Welford方法作为标准差。但是在该链接中我没有看到关于模式,中位数,峰度或偏度的任何信息……我错过了什么吗?
Ryan B. Lynch,2009年

斯蒂芬:一些数据集是整数,另一些是浮点数。总体分布非常接近正态分布(高斯分布),因此我们可以建立一个置信区间,但是没有硬性范围边界(在某些情况下,x> 0除外)。
Ryan B. Lynch 2009年

Answers:


53

偏度和峰度

有关偏度和峰度的在线算法(沿方差行),请参见此处的同一Wiki页面上的并行算法,以获取较高矩的统计信息。

中位数

没有排序的数据,中位数很难。如果您知道有多少个数据点,则理论上只需要部分排序即可,例如使用选择算法。但是,这对数十亿美元的价值没有太大帮助。我建议使用频率计数,请参阅下一节。

中频和模式与频率计数

如果它是整数,我将计算 频率,可能会截断最高和最低值,超出我确定不再相关的某个值。对于浮点数(或太多整数),我可能会创建存储桶/区间,然后使用与整数相同的方法。基于频率表,(近似)模式和中值计算变得容易。

正态分布随机变量

如果它是正态分布的,我将使用总体样本均值方差偏度峰度作为一小部分子集的最大似然估计量。您已经在使用(在线)算法来计算这些算法。例如,读取数十万或数百万个数据点,直到您的估计误差变得足够小为止。只需确保您从集合中随机选择即可(例如,通过选择前100000个值不会引入偏差)。相同的方法也可以用于正常情况的估计模式和中位数(因为两个样本均值都是估计量)。

进一步的评论

如果有帮助,可以并行运行以上所有算法(包括许多排序和选择算法,例如QuickSort和QuickSelect)。

我一直假设(关于正态分布的部分除外)我们谈论的是样本矩,中值和众数,而不是给定已知分布的理论矩的估计量。

一般而言,只要所有观测值都是相同随机变量(具有相同分布)以及矩,模式和该分布实际上存在中位数。最后的警告并非无害。例如,柯西分布的均值(以及所有更高的矩)不存在。在这种情况下,“小”子集的样本均值可能与整个样本的样本均值相差很大。


57

我使用这些增量/递归均值和中值估计量,它们均使用常量存储:

mean += eta * (sample - mean)
median += eta * sgn(sample - median)

其中eta是一个小的学习速率参数(例如0.001),而sgn()是返回{-1,0,1}之一的signum函数。(如果数据是不稳定的,并且您想跟踪随时间的变化,请使用常量eta;否则,对于固定源,您可以使用eta = 1 / n这样的均值估算器,其中n是所看到的样本数,因此不幸的是,这似乎不适用于中位数估算器。)

这种类型的增量均值估计器似乎已在各处使用,例如在无监督的神经网络学习规则中使用,但中位数版本虽然有好处(对异常值的鲁棒性),但似乎不太常见。在许多应用中,中位数版本似乎可以替代均值估算器。

我希望看到类似形式的增量模式估算器...

更新

我刚刚修改了增量中位数估算器,以估算任意分位数。通常,分位数函数(http://en.wikipedia.org/wiki/Quantile_function)会告诉您将数据分为两个部分的值:p和1-p。以下是对该值的递增估算:

quantile += eta * (sgn(sample - quantile) + 2.0 * p - 1.0)

值p应该在[0,1]之内。这实际上将sgn()函数的对称输出{-1,0,1}移向一侧,从而将数据样本分为两个大小不等的bin(数据的分数p和1-p小于/大于分位数估算值)。注意,对于p = 0.5,这将减少为中位数估计量。


3
这个中值估算器很棒。您知道0.25 / 0.75分位数是否有类似的估算器吗?
加塞克2010年

1
@Gacek,请确保:将输入流分成Lohalf <中位数和Hihalf>中位数,并在每一半上使用运行中值。
丹尼斯2010年

2
@Gacek:我刚刚用一种增量方法更新了我的答案,以估计任何分位数,您可以将p设置为0.25、0.75或[0,1]内的任何值。
Tyler Streeter

10
这对于平均值而言非常有用,但是我没有看到它如何产生远接近中值的任何东西。以一系列的毫秒标记为例:[1328083200000, 981014400000, -628444800000, 318240000000, 949392000000]中位数为318240000000。该公式将前一个中位数偏移+/- eta,推荐值为0.001。对于像这样的大数字,这不会做任何事情,对于非常小的数字,可能太大了。您将如何选择一个eta实际上会给您正确答案而又不事先知道答案的答案?
mckamey 2012年

9
假设数字的单位为毫米。然后很明显,eta(用于中位数的估计)必须具有与度量单位相同的单位,因此像0.001这样的通用值根本没有任何意义。一种看似更好的方法是根据绝对偏差的运行估计值来设置eta:对于每个新值sample,请更新cumadev += abs(sample-median)。然后设置eta = 1.5*cumadev/(k*k),这k是到目前为止看到的样本数量。
2013年


7

瑞安(Ryan),恐怕您没有正确执行均值和方差...这是几周前在这里出现的。在线版本(实际上是用Welford方法命名)的优点之一是它特别准确和稳定,请参见此处的讨论。优点之一是您不需要存储总和或平方和的事实。

我想不出任何在线方式和中位数,这似乎需要立即考虑整个列表。但这很可能是一种与针对方差和均值的方法类似的方法也适用于偏度和峰度...


回复:skewness and kurtosis是的。看到这篇文章:johndcook.com/blog/skewness_kurtosis
Jesse Chisholm

3

问题中引用的Wikipedia文章包含在线计算偏度和峰度的公式。

对于模式-我相信-无法在线执行此操作。为什么?假设您输入的所有值都不同,除了最后一个与上一个重复的值之外。在这种情况下,您必须记住所有在输入中已经看到的值,以检测最后一个值与之前看到的值重复并使之成为最频繁的值。

对于中位数,几乎是一样的-直到最后一次输入,您都不知道如果所有输入值都不同,那么哪个值将成为中位数,因为它可能在当前中位数之前或之后。如果您知道输入的长度,则可以在不将所有值存储在内存中的情况下找到中间值,但是您仍将必须存储其中的许多值(我猜大约是一半),因为错误的输入序列可能会使中间值在输入方向上发生很大变化。下半部分可能会从上半部分的中间值得出任何值。

(请注意,我仅指的是精确计算。)


2

如果您拥有数十亿个数据点,则与精确答案相反,您不太可能需要确切的答案。通常,如果您有数十亿个数据点,则生成它们的基础过程可能会服从某种统计平稳性/遍历性/混合性。同样重要的是,您是否期望分布合理地连续。

在这种情况下,如果您不需要确切的答案,则可以使用在线,低内存,分位数估计(中位数是0.5分位数的特殊情况)以及模式的算法。这是一个活跃的统计领域。

分位数估算示例:http : //www.computer.org/portal/web/csdl/doi/10.1109/WSC.2006.323014

模式估计示例:Bickel DR。连续数据的模式和偏度的鲁棒估计。计算统计和数据分析。2002; 39:153–163。doi:10.1016 / S0167-9473(01)00057-3。

这些是计算统计的活动字段。您正在进入一个领域,其中没有任何一种最佳精确算法,而是多种多样的算法(实际上是统计估计量),它们具有不同的属性,假设和性能。它是实验数学。关于这一主题的论文可能有成百上千。

最后一个问题是您是否真的需要自己的偏度和峰度,或者是否更可能需要一些其他参数,这些参数在表征概率分布时会更可靠(假设您具有概率分布!)。您是否期望高斯?

您是否有清除/预处理数据的方法,以使其大部分为高斯语?(例如,金融交易金额在取对数后通常有点高斯)。您期望有限的标准偏差吗?您期望肥尾吗?您关心的是尾巴还是散装?


2

每个人都在说您不能以在线方式进行此模式,但这完全不是事实。这是一篇文章,描述了一种解决这一问题的算法,该算法由耶鲁大学的Michael E. Fischer和Steven L. Salzberg于1982年发明。从文章:

多数查找算法使用其寄存器之一临时存储流中的单个项目。这一项是目前多数元素的候选人。第二个寄存器是一个初始化为0的计数器。对于流的每个元素,我们要求算法执行以下例程。如果计数器读数为0,则将当前流元素安装为新的多数候选值(替换可能已在寄存器中的任何其他元素)。然后,如果当前元素与多数候选匹配,则增加计数器;否则,增加计数器。否则,递减计数器。在周期的这一点上,如果到目前为止所看到的流的一部分具有多数元素,则该元素位于候选寄存器中,并且计数器保存的值大于0。如果没有多数席位怎么办?如果不对数据进行第二次传递(这在流环境中是不可能的),则在这种情况下,算法无法始终给出明确的答案。它仅承诺正确识别多数元素(如果存在)。

还可以扩展它以找到具有更多内存的前N个,但这应该可以解决该模式。


4
那是一个有趣的算法,但是除非我缺少任何东西,否则所有的多数值都将是模式,而并非所有的模式都将是多数值。
jkebinger 2013年

该链接已终止,因此很高兴其中包含了描述。但是,如所描述的,仅当多数候选第二事件与第一事件相邻时,计数器才递增。隐含排序数据。在在线(流)数据情况下不能保证这一点。对于随机排序的数据,这不太可能找到任何模式。
杰西·奇斯霍尔姆

1

最终,如果您对分布没有先验参数知识,那么我认为您必须存储所有值。

也就是说,除非您要处理某种病理情况,否则医生(Rousseuw and Bassett 1990)可能足以满足您的目的。

非常简单,它涉及计算批中位数的中位数。


0

无法仅使用恒定的空间在线计算中位数和众数。但是,由于中位数和众数无论如何都比“定量”更具“描述性”,因此您可以例如通过对数据集进行采样来估计它们。

如果从长期来看数据是正态分布的,那么您可以使用平均值来估计中位数。

您还可以使用以下技术估算中位数:为数据流中的每1,000,000个条目建立中位数估算M [i],以便M [0]是前一百万个条目的中位数,M [1]第二百万个条目的中位数,等等。然后使用M [0] ... M [k]的中位数作为中位数估计量。这当然可以节省空间,并且可以通过“调整”参数1,000,000来控制要使用多少空间。这也可以递归地概括。


0

OK花花公子试试这些:

对于C ++:

double skew(double* v, unsigned long n){
    double sigma = pow(svar(v, n), 0.5);
    double mu = avg(v, n);

    double* t;
    t = new double[n];

    for(unsigned long i = 0; i < n; ++i){
        t[i] = pow((v[i] - mu)/sigma, 3);
    }

    double ret = avg(t, n);

    delete [] t;
    return ret;
}

double kurt(double* v, double n){
    double sigma = pow(svar(v, n), 0.5);
    double mu = avg(v, n);

    double* t;
    t = new double[n];

    for(unsigned long i = 0; i < n; ++i){
        t[i] = pow( ((v[i] - mu[i]) / sigma) , 4) - 3;
    }

    double ret = avg(t, n);

    delete [] t;
    return ret;
}

在这里,您已经可以计算出样本方差(svar)和平均值(avg),然后将它们指向函数。

另外,请看一下Pearson的近似值。在如此大的数据集上,它会非常相似。3(均值-中位数)/标准差,中位数为最大值-分钟/ 2

对于浮点模式没有任何意义。人们通常会将它们粘贴在尺寸足够大的垃圾箱中(例如1/100 *(最大-最小))。



-1

我倾向于使用存储桶,它可以是自适应的。铲斗尺寸应满足您的要求。然后,随着每个数据点的加入,您需要在相关存储区的计数中添加一个。通过将每个值作为其值加权后的值,这些值应为您提供中值和峰度的简单近似值。

一个问题可能是数十亿次操作后浮点分辨率的损失,即,增加一个不再改变值!为了解决这个问题,如果最大存储桶大小超出某个限制,则可以从所有计数中扣除大量的数字。


-1
for j in range (1,M):
    y=np.zeros(M) # build the vector y
    y[0]=y0

    #generate the white noise
    eps=npr.randn(M-1)*np.sqrt(var)

    #increment the y vector
    for k in range(1,T):
        y[k]=corr*y[k-1]+eps[k-1]

    yy[j]=y

list.append(y)

可以使用一些解释将其更好地与原始问题联系起来。
埃里卡
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.