如何在java中获得白天的均值和标准差

Posted

技术标签:

【中文标题】如何在java中获得白天的均值和标准差【英文标题】:How to get mean and standard deviation of daytimes in java 【发布时间】:2021-06-17 10:51:40 【问题描述】:

对于一个用 Java 编写的 android Studio 项目,我有一个白天列表,它以整数形式收集小时和分钟,如下所示:

List<Integer> times = new ArrayList<>();
int hour = 16;
int minute = 25;
int time = hour * 60 + minute;
times.add(time);

我需要时间的平均值和标准差才能获得非异常值时间列表。但是,普通的平均值和标准差似乎不起作用。这是我现在正在做的事情:

private List<String> getNonOutlierTimes() 

   int mean = convertToTime((times.stream().mapToInt(Integer::intValue).sum()) / times.size());
   int sd = (int) calculateStandardDeviation(mean);
   int maxTime = (int) (mean + 1.5 * sd);
   int minTime = (int) (mean - 1.5 * sd);

   List<Integer> nonOutliers = new ArrayList<>();

   for (int i = 0; i < times.size(); i++) 

       if ((times.get(i) <= maxTime) && (times.get(i) >= minTime)) 
                nonOutliers.add(times.get(i));
       
   

   List<String> nonOutliersStr = new ArrayList<>();

   for (Integer nonOutlier : nonOutliers) 
        nonOutliersStr.add(convertIntTimesToStr(nonOutlier));
   

   return nonOutliersStr;



private int convertToTime(int a) 

   if ((a < 24*60) && (a >= 0)) 
            return a;
         else if (a < 0) 
            return 24*60 + a;
         else 
            return a % (24*60);
        



private double calculateStandardDeviation(int mean) 

        int sum = 0;
        for (int j = 0; j < times.size(); j++) 
            int time = convertToTime(times.get(j));
            sum = sum + ((time - mean) * (time - mean));
        
        double squaredDiffMean = (double) (sum) / (times.size());

        return (Math.sqrt(squaredDiffMean));
    


private String convertIntTimesToStr(int time) 

        String hour = (time / 60) + "";
        int minute = time % 60;
        String minuteStr = minute < 10 ? "0" + minute : "" + minute;

        return hour + ":" + minuteStr;
    

尽管所有计算均基于有效统计数据,但计算出的均值和 sd 似乎无关紧要。例如,当时间列表包含以下内容时:

225(凌晨 3 点 45 分), 90(上午 1 点 30 分), 0(上午 12:00), 1420(晚上 11 点 40 分), 730(下午 12 点 10 分)

我需要一个非异常值列表,其中包含:

1420(晚上 11:40), 0(上午 12:00), 90(上午 1 点 30 分), 225(凌晨 3 点 45 分)

实际输出在哪里:

0(上午 12:00), 90(上午 1 点 30 分), 225(下午 3 点 45 分), 730(下午 12:10)

也就是说,我需要在大多数情况下保持平均水平。更具体地说,考虑一个包含整数 1380(23:00 或 11:00 pm)、1400(23:20 或 11:20 pm)和 60(01:00 am)的时间列表。这些时间的平均值是 945(15:45 或 03:45 pm),我需要平均值介于 23:00 和 01:00 之间。

我已经找到this solution 的列表两次。但是,我的 times.size() 总是大于 2,我也想计算标准偏差。所以,感谢您在这方面的帮助。

提前致谢。

【问题讨论】:

“计算出的平均值和 sd 似乎无关”是什么意思?对于 1380、1400、60,计算出的 sd 和平均值对我来说似乎是正确的。您能否显示您的预期输出和实际输出,以便清楚您的代码在哪里不工作? 我注意到一件事:在计算 minTimemaxTime 时调用 convertToTime 没有意义。如果 sd 真的很大,这可能会使minTime 大于maxTime,这可能不是您想要的...... 您预计上午 12 点、上午 8 点和下午 4 点的平均值是多少? 我的例子想强调你的问题是不适定的:像时间这样的循环值的平均值没有定义。根据您选择的决定,您将有 3 个不同的值:上午 8 点(从 0 到 24 小时)、上午 12 点 (-12--12) 或下午 4 点 (-24--0)。你能解释一下你原来的问题吗? 如您所知,您正在处理在圆上定义的点。在这种情况下,不是隐式假设普通的高斯分布,而是使用圆形高斯分布更好地拟合问题;我认为这被称为冯米塞斯分布。大概有您可以计算的汇总统计数据。在任何情况下,您都希望找到一个高密度的弧线或楔形,而不是线间隔,并使用该弧线来识别异常值。此时,您可能应该跟进 stats.stackexchange.com。 【参考方案1】:

您使用的不是实数,而是模 1440 的数。在这种情况下,除以自然数没有很好的定义,或者更好的是 n x = a 对每个 a 都有 n 解决方案。例如。 3 x = 300 具有解决方案 300 / 31740 / 33180 / 330017403180 是同一元素 300 的不同表示形式。

因此,您不能在一天中的时间范围内谈论算术平均值。然而,一天中两个时间之间的距离明确定义的:21:00 和 23:00 之间的距离是 2 小时,23:00 和 1:00 之间的距离也是如此。因此我们可以对"mean"采取另一种定义:

让我们称平均一天中最小化数据平方距离总和的时间。这是实数通常均值的一个性质。

幸运的是,可以证明,这个新均值是n x = sum of values 的解决方案之一。这些解决方案之间的变化是与数据的平方距离之和,我们必须选择最小的那个。

假设我们有一个LocalTimes 列表:

   private static final long            DAY      = TimeUnit.DAYS.toSeconds(1L);
   private static final double          HALF_DAY = DAY / 2;
   private static final List<LocalTime> times    = Arrays.asList(
         LocalTime.of(3, 45),
         LocalTime.of(1, 30),
         LocalTime.of(0, 0),
         LocalTime.of(23, 40),
         LocalTime.of(12, 10));

我们可以在 “通常” 确定中计算平均值和平方和(我在几秒钟内完成,所以在 0 到 86400 之间):

   public static void printMeanVariance(final List<LocalTime> times) 
      final List<Double> dTimes = times.stream().mapToDouble(LocalTime::toSecondOfDay).boxed().collect(Collectors.toList());
      dTimes.sort(Double::compareTo);
      // A valid 'mean' must have max - HALF_DAY < mean < min + HALF_DAY
      double max = dTimes.get(dTimes.size() - 1);
      int count = 0;
      double sum = 0.0, sumOfSquares = 0.0;
      for (final Double time : dTimes) 
         count++;
         sum += time;
         sumOfSquares += time * time;
      
      // to be continued...

如果这是“平均”,它必须满足两个条件:

    “均值”必须介于max + DAYmin + DAY之间,其中minmax是当前确定的最小值和最大值, 通常的方差必须是最小的。

我们通过将每次 86400 添加到最小值来检查所有测定的这些条件:

      // continuation
      double average = -1;
      double sumOfDistancesSquared = Double.MAX_VALUE;
      for (final Double time : dTimes) 
         // Check if previous value is admissible
         final double tmpAverage = sum / count;
         final double tmpSumOfDistancesSquared = sumOfSquares - sum * sum / count;
         if (max - HALF_DAY <= tmpAverage && tmpAverage <= time + HALF_DAY && tmpSumOfDistancesSquared < sumOfDistancesSquared) 
            average = tmpAverage;
            sumOfDistancesSquared = tmpSumOfDistancesSquared;
         
         sum += DAY;
         max = time + DAY;
         sumOfSquares += DAY * (2 * time + DAY);
      
      // average has the "real" mean
      double sd = Math.sqrt(sumOfDistancesSquared / (count - 1));
      System.out.println("Mean = " + LocalTime.ofSecondOfDay((long) average) +
        ", deviation = " + Duration.ofSeconds((long) sd));
   

【讨论】:

非常感谢您提供全面的解决方案。

以上是关于如何在java中获得白天的均值和标准差的主要内容,如果未能解决你的问题,请参考以下文章

是否有一个函数可以让数组在java中找到总和、均值和标准差

如何计算 RDD[Long] 的标准差和平均值?

如何在蒙特卡洛模拟中添加均值和标准差线?

如何计算给定均值和标准差的正态分布中的概率?

如何进行 NxN 矩阵的均值、标准差和众数统计

在c#代码中使用fastai(pytorch),如何使用均值和标准差归一化位图?