【问题标题】:How can I calculate percentile in java without using any other library如何在不使用任何其他库的情况下在 java 中计算百分位数
【发布时间】:2013-04-14 20:03:29
【问题描述】:

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

我很想知道在 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);
        }
    }

}

谁能看一下,让我知道我是否正确计算了百分位数?

【问题讨论】:

  • 由于您使用的是哈希映射,因此您的密钥不会按排序顺序存储。即,如果您在循环中打印出 entry.getKey(),您将看到键不按顺序排列。所以这是你的主要问题。 TeeMap 或 ConcurrentSkipListMap 将保持其键的顺序。
  • 是的,你是对的。我忘了这样做。但请记住,我的地图已经按顺序排列了数据,那么逻辑是否正确?
  • 你确定吗?打印你的钥匙我敢打赌你会发现它们不按顺序排列。
  • 是的,我忘记了那部分。我的印象是,我按排序顺序存储它。让我更正一下,然后我会更新我的代码。

标签: java concurrenthashmap percentile


【解决方案1】:

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

【讨论】:

  • 让我在我的代码中进行更正,然后再次更新代码。感谢您的帮助。
  • 感谢 Ed 的建议。我已经用最新的代码更新了我的问题。请您看看,让我知道我这次的做法是否正确?
【解决方案2】:

根据我对您问题的评论:

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

更改Map&lt;Long, Long&gt; histogram = new ConcurrentHashMap&lt;Long, Long&gt;

Map&lt;Long, Long&gt; histogram = new ConcurrentSkipListMap&lt;Long, Long&gt;()

会给你一个地图,它会按排序顺序返回你的键。

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

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;
        }
    }
}

【讨论】:

  • 感谢马特的建议。我已经用最新的代码更新了我的问题。请您看一下,让我知道我这次的做法是否正确?
  • 您在不同的循环中以不同的方式计算总和。我更新了我的答案。
  • 我认为这是正确的做法?看看我的算法来解决我的问题。在我的问题中,我有关键和价值。键是毫秒数,值是该毫秒的调用总数? For example, 72 calls came back in 20 milliseconds
  • 我同意马特的观点,要在第一个循环中获得 total,您只需要将地图中的所有值相加,而不是 (key * value)
  • @Matt 您能否根据我的代码为我提供一个示例。我想这现在让我感到困惑。如果你能给我一个例子,那将有很大的帮助。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 2016-04-12
  • 1970-01-01
  • 2014-09-02
  • 2012-11-18
  • 1970-01-01
  • 1970-01-01
  • 2016-07-12
  • 2015-03-09
相关资源
最近更新 更多