我需要计算大量数据的分位数。
假设我们只能通过某些部分(即大矩阵的一行)获取数据。要计算Q3分位数,需要获取数据的所有部分并将其存储在某处,然后对其进行排序并计算分位数:
List<double> allData = new List<double>();
// This is only an example; the portions of data are not really rows of some matrix
foreach(var row in matrix)
{
allData.AddRange(row);
}
allData.Sort();
double p = 0.75 * allData.Count;
int idQ3 = (int)Math.Ceiling(p) - 1;
double Q3 = allData[idQ3];
我想找到一种获取分位数的方法,而不将数据存储在中间变量中。最好的解决方案是计算第一行中间结果的一些参数,然后逐步调整下一行。
注意:
这个问题类似于“On-line” (iterator) algorithms for estimating statistical median, mode, skewness, kurtosis,但我需要计算分位数。
此主题中的文章很少,即:
在尝试实施这些方法之前,我想知道是否还有其他更快的方法来计算0.25 / 0.75分位数?
答案 0 :(得分:1)
我的第二个想法是使用桶。不要将自己限制在100个桶中 - 不妨使用100个桶。棘手的部分是选择您的铲斗范围,以便一切都不会在一个桶中结束。估计铲斗范围的最佳方法可能是采用合理的随机数据样本,使用简单排序算法计算10%和90%分位数,然后生成相等大小的铲斗以填充该范围。它并不完美,但如果您的数据不是来自超级奇怪的发行版,那么它应该可以正常工作。
如果你不能做随机抽样,你就会遇到麻烦。您可以根据预期的数据分布选择初始分段猜测,然后在处理数据时,如果任何存储桶(通常是第一个或最后一个存储桶)过满,请使用新的存储区范围重新开始。
答案 1 :(得分:1)
有一种更新,更简单的算法可以很好地估计极端分位数。
基本思想是在极端情况下使用较小的二进制位,这两种方式都限制了数据结构的大小,并保证了小或大q的更高精度。该算法有多种语言和许多包。 MergingDigest版本不需要动态分配...一旦实例化MergingDigest,就不需要进一步分配堆。
答案 2 :(得分:0)
答案 3 :(得分:0)
如果您的数据具有高斯分布,则可以从标准差估计分位数。我假设您的数据不是高斯分布的,或者您只是使用SD。
如果您可以两次传递数据,我会执行以下操作:
这应该会为你提供一个非常好的线性时间算法,对大多数不完全反常的数据集都可以。
答案 4 :(得分:0)
受this answer的启发,我创建了一种估算分位数非常好的方法。它的近似值足够接近我的目的。
这个想法如下:0.75分位数实际上是位于全局中位数之上的所有值的中位数。并且0.25分位数分别是低于全局中值的所有值的中值。
因此,如果我们可以近似中位数,我们可以以类似的方式近似分位数。
double median = 0;
double q1 = 0;
double q3 = 0;
double eta = 0.005;
foreach( var value in listOfValues) // or stream, or any other large set of data...
{
median += eta * Math.Sign(p.Int - median);
}
// Second pass. We know the median, so we can count the quantiles.
foreach(var value in listOfValues)
{
if(p.Int < median)
q1 += eta*Math.Sign(p.Int - q1);
else
q3 += eta*Math.Sign(p.Int - q3);
}
备注:
eta
才能适应奇怪的数据。但准确性会更差。eta
参数:在预设集合中eta
几乎相等一些大的价值(即0.2)。当循环通过时,降低eta
的值,所以当你几乎到达集合的末尾时,eta
几乎等于0(例如,在循环中计算它如下:{{1 }} 答案 5 :(得分:0)