有什么好的算法可以估算巨大的一次读取数据集的中位数?


47

我正在寻找一种好的算法(意味着最小的计算量,最小的存储需求)来估计太大而无法存储的数据集的中值,这样每个值只能被读取一次(除非您明确存储该值)。可以假设的数据没有界限。

只要知道精度,就可以近似。

有指针吗?


4
也许,询问Stackoverflow可能会得到更好的答案。

2
@Srikant:>这是统计学中非常活跃的研究领域:)就存储而言,最接近理论下限的解决方案也包含一些非常聪明的概率构造。总而言之,几个月前我第一次看到它时,我感到非常惊讶。这里有更多的统计数据。
user603 2010年

Answers:


6

您是否可以将数据集分组为更小的数据集(例如100或1000或10,000个数据点),然后再计算每个组的中位数。如果使用足够多的数据集进行此操作,则可以通过运行足够多的较小数据集来收敛到“平均”解决方案,从而绘制出每个较小集的结果的平均值之类的东西。


这很有趣,可以从中获得一些统计建议!假设我总共得到了(比如说)500,000个iid点,我查看了(比如说)其中的1000个点,然后计算每个组的中位数。现在我有500个中位数。有没有理论可以让我根据这500个中位数来计算总体中位数的置信区间?
PeterR

4
因此,根据一位长期失散的同事的说法,最好的办法似乎是Chiranjeeb Buragohain和Subhash Suri。流上的分位数。cs.ucsb.edu/~suri/psdir/ency.pdf 我也喜欢Ian的方法,因为这些较小数据集的中位数将收敛为正态分布,因此我可以为中位数形成conf间隔。
PeterR

9

诸如分箱程序之类的东西怎么样?假设(出于说明目的)您知道值在1到1百万之间。设置N个大小为S的容器。因此,如果S = 10000,则将有100个容器,对应于值[1:10000,10001:20000,...,990001:1000000]

然后,逐步浏览这些值。无需存储每个值,只需在适当的bin中增加计数器即可。使用每个bin的中点作为估计值,您可以合理地近似中位数。您可以通过更改垃圾箱的大小将其缩放为所需的精细或粗略分辨率。您仅受拥有多少内存的限制。

由于您不知道您的值可能会变大,因此,请使用一些快速的信封计算方法,选择一个足够大的bin大小,这样就不会用完内存。您也可能会稀疏地存储垃圾箱,以便仅在垃圾箱包含值的情况下添加垃圾箱。

编辑:

链接ryfm提供了一个执行此操作的示例,其中还有一个额外的步骤,即使用累积百分比来更准确地估计中位数bin中的点,而不仅仅是使用中点。这是一个不错的改进。


分箱方法的问题在于,我们没有一个很好的数据上限,因此,最大分箱的中点必须很大。因此,我们需要大量的垃圾箱(没有足够的存储空间),或者需要相当宽的垃圾箱(这将导致答案相当不准确。)而且数据不是很稀疏。
PeterR 2010年

既然您只对中位数感兴趣,为什么不能在变量值较高的情况下使垃圾箱更宽?
russellpierce

drknexus-因为我们不知道最大的垃圾箱应该是什么。
PeterR 2010年

你有任何直觉的范围将是什么?如果您相当确定一半以上的答案将在数字N以下,那么您可以根据需要将最后一个垃圾箱设为任意大。也许您的最后一个垃圾箱是所有数字都大于1万亿-足够高了吗?借助现代系统中的大量内存,您可以存储很多垃圾箱并获得相当高的分辨率。在数据结构方面,我们在这里并没有谈论任何花哨和占用大量内存的内容。
chrisamiller

有直觉吗?是。您的方法通常可以正常工作。但是,在这种情况下,我们不能拥有太多的内存/计算量。它是在网络应用程序中,设备每秒可以看到成千上万的项目,并且为此保留的处理很少。我知道,这不是理想/典型的情况,但这就是使它变得有趣的原因!
PeterR

9

我将您重定向到我对类似问题的回答。简而言之,它是一次读取的“即时”算法,具有最坏情况的复杂度,可以计算(精确的)中位数。O(n)




2

我从来没有这样做,所以这只是一个建议。

我看到两种(其他)可能性。

一半数据

  1. 加载一半数据并排序
  2. 接下来读取剩余的值,并与您的排序列表进行比较。
    1. 如果新值较大,则将其丢弃。
    2. 否则,将该值放入已排序列表中,并从该列表中删除最大值。

抽样分布

另一种选择是使用涉及采样分布的近似值。如果您的数据为“正常”,则中等n的标准误差为:

1.253 * SD /平方英尺(n)

为了确定您满意的n的大小,我在R中运行了快速蒙特卡洛模拟

n = 10000
outside.ci.uni = 0
outside.ci.nor = 0
N=1000
for(i in 1:N){
  #Theoretical median is 0
  uni = runif(n, -10, 10)
  nor  = rnorm(n, 0, 10)

  if(abs(median(uni)) > 1.96*1.253*sd(uni)/sqrt(n))
    outside.ci.uni = outside.ci.uni + 1

  if(abs(median(nor)) > 1.96*1.253*sd(nor)/sqrt(n))
    outside.ci.nor = outside.ci.nor + 1
}

outside.ci.uni/N
outside.ci.nor/N

对于n = 10000,统一中位数估计值的15%在CI之外。


3
数据集可能太大而无法读取一半...在网络环境中,正在处理的设备每秒可以看到数万个项目,并且可能有足够的内存来仅存储数百个。而且数据绝对不是高斯的。实际上,它不适合任何常见的发行版。
PeterR


1

这是在stackoverflow上提出的问题的答案:https : //stackoverflow.com/questions/1058813/on-line-iterator-algorithms-for-estimating-statistical-median-mode-skewness/2144754#2144754

迭代更新中位数+ = eta * sgn(sample-中位数)听起来像是要走的路。


1
但是然后如何选择eta,这在统计上又意味着什么?即如何根据该结果形成中位数的置信区间?
PeterR 2010年

@PeterR,嘿,您使用的最终解决方案是什么?
Aakash Goel

1

所述Remedian算法(PDF)给出具有低存储要求和良好限定的精度的一个通位数估计。

以b为基础的救济者通过计算b个观察值组的中位数,然后计算这些中位数的中位数,直到仅剩下一个估计值来进行处理。此方法仅需要k个大小为b的数组(其中n = b ^ k)...


1

如果您使用的在一定范围内(例如1到100000),则可以使用整数存储桶高效地计算大量值(例如,数万亿个条目)的中位数(此代码取自BSD许可的ea -utils / sam-stats.cpp)

class ibucket {
public:
    int tot;
    vector<int> dat;
    ibucket(int max) {dat.resize(max+1);tot=0;}
    int size() const {return tot;};

    int operator[] (int n) const {
        assert(n < size());
        int i;
        for (i=0;i<dat.size();++i) {
            if (n < dat[i]) {
                return i;
            }
            n-=dat[i];
        }
    }

    void push(int v) {
        assert(v<dat.size());
        ++dat[v];
        ++tot;
    }
};


template <class vtype>
double quantile(const vtype &vec, double p) {
        int l = vec.size();
        if (!l) return 0;
        double t = ((double)l-1)*p;
        int it = (int) t;
        int v=vec[it];
        if (t > (double)it) {
                return (v + (t-it) * (vec[it+1] - v));
        } else {
                return v;
        }
}

此外,这可以扩展到使用槽进行实时位数等的有限数目的
埃里克Aronesty
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.