从长数组计算百分位数?
Posted
技术标签:
【中文标题】从长数组计算百分位数?【英文标题】:Calculate percentile from a long array? 【发布时间】:2017-05-15 18:38:02 【问题描述】:给定一长串以毫秒为单位的延迟,我想从中计算百分位数。我得到了下面的方法,但我不确定如何验证这是否给了我准确的结果?
public static long[] percentiles(long[] latencies, double... percentiles)
Arrays.sort(latencies, 0, latencies.length);
long[] values = new long[percentiles.length];
for (int i = 0; i < percentiles.length; i++)
int index = (int) (percentiles[i] * latencies.length);
values[i] = latencies[index];
return values;
我想从latencies
数组中获取第 50、95、99 和 99.9 个百分位数。
long[] percs = percentiles(latencies, 0.5, 0.95, 0.99, 0.999);
在给定大量延迟的情况下,这是获得百分位数的正确方法吗?我正在使用 Java 7。
【问题讨论】:
请注意,您的percentiles
方法不仅计算百分位值(并不总是正确 - 请参阅我的答案)并返回值,它还使 latencies
数组排序,这是一个可能不希望出现的副作用。这在您尝试编写的小程序中可能是无害的,但一般来说,如果方法具有不是该方法目的的副作用,则不是一个好习惯。
【参考方案1】:
这就是你要找的:
public static void main(String[] args)
List<Long> latencies = new List<Long>() 3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20 ;
Collections.sort(latencies);
System.out.println(percentile(latencies, 25));
System.out.println(percentile(latencies, 50));
System.out.println(percentile(latencies, 75));
System.out.println(percentile(latencies, 100));
public static long percentile(List<Long> latencies, double percentile)
int index = (int) Math.ceil(percentile / 100.0 * latencies.size());
return latencies.get(index-1);
【讨论】:
嗯,你注意到问题上的Java标签了吗? 快速翻译成java:public static double percentile(List<Double> values, double percentile) Collections.sort(values); int index = (int) Math.ceil((percentile / 100) * values.size()); return values.get(index - 1);
当百分位数为 0 时它会崩溃,但我猜这是边缘情况。【参考方案2】:
public static double percentile(double percentile, List<Double> items)
Preconditions.checkArgument(percentile >= 0);
Preconditions.checkArgument(percentile <= 100);
Preconditions.checkArgument(!items.isEmpty());
Collections.sort(items);
return items.get((int) Math.round(percentile / 100.0 * (items.size() - 1)));
@Test
public void test1()
List<Double> list = Arrays.asList(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0);
assertThat(percentile(0, list)).isEqualTo(0.0);
assertThat(percentile(20, list)).isEqualTo(2.0);
assertThat(percentile(80, list)).isEqualTo(8.0);
assertThat(percentile(100, list)).isEqualTo(10.0);
@Test
public void test2()
List<Double> list = Arrays.asList(0.0, 1.0, 2.0, 3.0);
assertThat(percentile(51, list)).isEqualTo(2.0);
assertThat(percentile(49, list)).isEqualTo(1.0);
@Test
public void test3()
List<Double> list = Arrays.asList(42.0);
assertThat(percentile(0, list)).isEqualTo(42.0);
assertThat(percentile(100, list)).isEqualTo(42.0);
【讨论】:
【参考方案3】:根据Wikipedia,百分位数没有标准定义;但是,它们给出了一些可能的定义。您发布的代码似乎最接近最近排名方法,但并不完全相同。
他们给出的公式是
n = ceiling((P / 100) x N)
其中N
是列表的长度,P
是百分位数,n
是序数排名。您已经除以 100。看看他们给出的例子,很明显“序数排名”是列表中的索引,但它是 1-relative。因此,要获得 Java 数组的索引,您必须减去 1。因此,正确的公式应该是
n = ceiling(percentile * N) - 1
使用代码中的变量,Java 等效项是
(int) Math.ceil(percentiles[i] * latencies.length) - 1
这不是您编写的代码。当您将 double
转换为 int
时,结果会向 0 舍入,即它相当于“floor”函数。所以你的代码计算
floor(percentiles[i] * latencies.length)
如果percentiles[i] * latencies.length
不是整数,则结果相同。但是,如果是整数,使得“floor”和“ceiling”是相同的值,那么结果就会不同。
Wikipedia 中的一个示例是在列表为 15, 20, 35, 40, 50 时计算第 40 个百分位数。他们的答案是找到列表中的第二项,即 20,因为 0.40 * 5 = 2.0,并且上限 (2.0) = 2.0。
但是,您的代码:
int index = (int) (percentiles[i] * latencies.length);
将导致 index
为 2,这不是您想要的,因为这会给您列表中的第三项,而不是第二项。
因此,为了匹配 Wikipedia 定义,您的索引计算需要稍作修改。 (另一方面,如果有人过来说你的计算是正确的而***是错误的,我不会感到惊讶。我们会看到......)
【讨论】:
以上是关于从长数组计算百分位数?的主要内容,如果未能解决你的问题,请参考以下文章
python使用pandas中的groupby函数和agg函数计算每个分组数据的两个分位数(例如百分之10分位数和百分之90分位数)
Pandas .. 分位数函数是不是需要排序数据来计算百分位数?
在 SQL 中分析并形成分位数并计算落在各个分位数中的值的百分比