从长数组计算百分位数?

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&lt;Double&gt; 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 .. 分位数函数是不是需要排序数据来计算百分位数?

R语言分位数计算Percentiles

在 SQL 中分析并形成分位数并计算落在各个分位数中的值的百分比

JavaScript中的分位数/百分点/百分位数/逆累积分布函数

计算百分位数的条件数组