如何在不使用任何其他库的情况下计算java中的百分位数

时间:2013-04-22 01:55:25

标签: java concurrenthashmap percentile

我正在尝试从我在95th Percentile下方填充的数据集中计算ConcurrentHashMap

我有兴趣了解在第95个百分点的时间内有多少来电回来

我的地图将如下所示,它将始终按键上的升序排序 - 其中

key - means number of milliseconds
value - means number of calls that took that much milliseconds

以下是我的地图数据 -

Milliseconds    Number

0               1702
1               15036
2               14262
3               13190
4               9137
5               5635
6               3742
7               2628
8               1899
9               1298
10              963
11              727
12              503
13              415
14              311
15              235
16              204
17              140
18              109
19              83
20              72

例如,从上面的数据集中,它意味着

  

1702次呼叫在0毫秒内恢复

     

15036次呼叫在1毫秒内恢复

现在,我可以通过在Excel sheet中插入上述数据来计算第95个百分点。但我正在考虑计算Java代码中的百分位数。

我知道算法看起来像这样 -

  

对地图中的所有值求和,计算总和的95%,迭代地图   按升序排列的密钥保持运行的值总和,以及何时   和等于或超过先前计算的总和的95%,   关键应该是我想的第95个百分位。

下面是具有上述数据集的地图。

Map<Long, Long> histogram = new ConcurrentHashMap<Long, Long>

我不确定我的算法是否正确。我只是想知道在第95个百分点的时间里回来了多少次电话。

以下是我迄今根据上述算法得到的代码。

private static void logPercentileInfo() {

    double total = 0;
    for (Map.Entry<Long, Long> entry : CassandraTimer.histogram.entrySet()) {
        long value = entry.getKey() * entry.getValue();
        total += value;
    }

    double sum = 0.95*total;

    double totalSum = 0;
    for (Map.Entry<Long, Long> entry : CassandraTimer.histogram.entrySet()) {
        totalSum += entry.getValue();

        if(totalSum >= sum) {
        System.out.println(entry.getKey());//this is the 95th percentile I guess
        }
    }
}

如果我从上述数据集中计算出第95个百分点,我是否知道一切正确。如果有任何改进,请告诉我。

更新代码: -

以下是我更新的代码,它解决了按键升序的问题

/**
 * A simple method to log 95th percentile information
 */
private static void logPercentileInfo() {

    double total = 0;
    for (Map.Entry<Long, Long> entry : CassandraTimer.histogram.entrySet()) {
        long value = entry.getKey() * entry.getValue();
        total += value;
    }

    double sum = 0.95*total;

    double totalSum = 0;

    SortedSet<Long> keys = new TreeSet<Long>(CassandraTimer.histogram.keySet());
    for (long key : keys) {

        totalSum += CassandraTimer.histogram.get(key);

        if(totalSum >= sum) {
           //this is the 95th percentile I guess
            System.out.println(key);
        }
    }

}

任何人都可以看看,让我知道我是否正确计算百分位?

2 个答案:

答案 0 :(得分:1)

迭代ConcurrentHashMap不会按顺序返回键。您首先需要创建一个键的排序列表,然后在后一个循环中从histogram中提取值时迭代它。

答案 1 :(得分:0)

根据我对你问题的评论:

  

由于您使用的是哈希映射,因此您的密钥不会按排序顺序存储。即,如果在循环中打印出entry.getKey(),您将看到键不按顺序排列。这是你的主要问题。 TeeMap或ConcurrentSkipListMap将按顺序保持其键

更改Map<Long, Long> histogram = new ConcurrentHashMap<Long, Long>

Map<Long, Long> histogram = new ConcurrentSkipListMap<Long, Long>()

将为您提供一个地图,该地图将按排序顺序返回您的密钥。

代码中的另一个问题是当你计算总和时:

total += entry.getKey() * entry.getValue(); // total += key*value

当你第二次计算总和时:

totalSum += CassandraTimer.histogram.get(key); // totalSum += value

我认为您想要计算观察总数,然后将其乘以0.95。这将为您提供低于第95百分位数的观测数量。

L = .95 * total_observations

然后迭代你的地图,总结观察的数量。一旦观察总数超过L,则相应的密钥是第95百分位的值。

private static void logPercentileInfo() {
    double total = 0;
    for (Map.Entry<Long, Long> entry : CassandraTimer.histogram.entrySet()) {
        long value = entry.getValue();
        total += value;
    }

    double sum = 0.95*total;
    double totalSum = 0;

    SortedSet<Long> keys = new TreeSet<Long>(CassandraTimer.histogram.keySet());
    for (long key : keys) {

        totalSum += CassandraTimer.histogram.get(key);

        if(totalSum >= sum) {
           System.out.println(key);
           break;
        }
    }
}