从长阵列计算百分位数?

时间:2017-01-01 05:19:21

标签: java math statistics apache-commons percentile

鉴于一系列延迟(以毫秒为单位),我想从中计算百分位数。我得到了下面的工作方法,但我不知道如何验证这是否能给我准确的结果?

  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。

3 个答案:

答案 0 :(得分:9)

这就是你要找的东西:

class Program
{
    static void Main(string[] args)
    {
        List<long> latencies = new List<long>() { 3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20 };

        Console.WriteLine(Percentile(latencies,25));
        Console.WriteLine(Percentile(latencies, 50));
        Console.WriteLine(Percentile(latencies, 75));
        Console.WriteLine(Percentile(latencies, 100));

        Console.ReadLine();
    }

    public static long Percentile(List<long> latencies, double Percentile)
    {
        latencies.Sort();
        int Index = (int)Math.Ceiling(((double)Percentile / (double)100) * (double)latencies.Count);
        return latencies[Index-1];
    }
}

enter image description here

答案 1 :(得分: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);
}

答案 2 :(得分:1)

根据Wikipedia,没有百分位数的标准定义;但是,它们给出了一些可能的定义。您发布的代码似乎最接近最近等级方法,但它并不完全相同。

他们给出的公式是

n = ceiling((P / 100) x N)

其中N是列表的长度,P是百分位数,n将是序数排名。你已经完成了除以100的除法。看看它们给出的例子,很明显“序数等级”是列表中的索引,但它是1 - 相对的。因此,要获得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”是相同的值,那么结果将是不同的。

维基百科的一个例子是当列表为{15,20,35,40,50}时计算第40个百分点。他们的答案是找到列表中的第二项,即20,因为0.40 * 5 = 2.0,而ceiling(2.0)= 2.0。

但是,您的代码:

int index = (int) (percentiles[i] * latencies.length);

将导致index为2,这不是您想要的,因为这将为您提供列表中的第三项,而不是第二项。

因此,为了匹配Wikipedia定义,您需要稍微修改索引的计算。 (另一方面,如果有人出现并且说你的计算是正确的并且维基百科错了,我也不会感到惊讶。我们会看到......)