【问题标题】:Calculate average without being thrown by strays计算平均值而不会被流浪者抛出
【发布时间】:2010-10-19 19:16:34
【问题描述】:

我正在尝试计算一个平均值,而不会被一小组遥远的数字(即 1、2、1、2、3、4、50)甩掉,单个 50 会甩掉整个平均值。

如果我有这样的数字列表:

19,20,21,21,22,30,60,60

平均为 31

中位数为 30

众数为 21 & 60(平均为 40.5

但是任何人都可以看到,大多数都在 19-22 范围内(5 进,3 出),如果你只获得主要范围的平均值,它就是 20.6(与任何人相比都有很大的不同)以上数字)

我认为你可以这样得到:

c+d-r

其中c 是数字的计数,d 是不同的值,r 是范围。然后,您可以将其应用于所有可能的范围,最高分是从中获得平均值的最佳范围。

例如,19,20,21,21,22 将是 5 个数字、4 个不同的值,范围是 3 (22 - 19)。如果你把它代入我的方程,你会得到 5+4-3=6

如果您将此应用于整个号码列表,则为 8+6-41=-27

我认为这很有效,但我必须创建一个巨大的循环来测试所有可能的范围。在我的小例子中,有 21 个可能的范围:

19-19、19-20、19-21、19-22、19-30、19-60、20-20、20-21、20-22、20-30、20-60、21-21 , 21-22, 21-30, 21-60, 22-22, 22-30, 22-60, 30-30, 30-60, 60-60

我想知道是否有更有效的方法来获得这样的平均值。

或者如果有人一起拥有更好的算法?

【问题讨论】:

  • 您要做的是在数据集中识别outliers。像stackoverflow.com/questions/503345/… 这样的东西可能会帮助你。
  • 你有没有想过找到标准偏差,然后只接受由平均值 +/- 标准偏差形成的范围内的数字?
  • @Daniel:我想说这个问题甚至与stackoverflow.com/questions/503345/…
  • @Instantsoup 确实如此。当我发表评论时,我投票决定关闭。
  • 你的中位数在我看来是 21.5,而不是 30。

标签: php algorithm average


【解决方案1】:

您可能会在此处使用standard deviation,它主要衡量数据点的集中程度。您可以将异常值定义为与平均值相差超过 1 个标准差(或任何其他适合您的数字)的值,将它们剔除,然后计算一个不包括它们的新平均值。

【讨论】:

  • 这只有在你有一个正态分布时才真正有效。对于平坦的分布,它可能会对结果产生不利影响。
【解决方案2】:

这是一个非常幼稚的实现,您可以根据自己的需要进行修复。我故意保持它非常冗长。它基于通常用于解决这些问题的五数汇总。

function get_median($arr) {
    sort($arr);
    $c = count($arr) - 1;
    if ($c%2) {
        $b = round($c/2);
        $a = $b-1;
        return ($arr[$b] + $arr[$a]) / 2 ;
    } else {
        return $arr[($c/2)];
    }
}

function get_five_number_summary($arr) {
    sort($arr);
    $c = count($arr) - 1;
    $fns = array();
    if ($c%2) {
        $b = round($c/2);
        $a = $b-1;
        $lower_quartile = array_slice($arr, 1, $a-1);
        $upper_quartile = array_slice($arr, $b+1, count($lower_quartile));
        $fns = array($arr[0], get_median($lower_quartile), get_median($arr), get_median($upper_quartile), $arr[$c-1]);
        return $fns;
    }
    else {
        $b = round($c/2);
        $a = $b-1;
        $lower_quartile = array_slice($arr, 1, $a);
        $upper_quartile = array_slice($arr, $b+1, count($lower_quartile));
        $fns = array($arr[0], get_median($lower_quartile), get_median($arr), get_median($upper_quartile), $arr[$c-1]);
        return $fns;
    }
}

function find_outliers($arr) {
    $fns = get_five_number_summary($arr);
    $interquartile_range = $fns[3] - $fns[1];
    $low = $fns[1] - $interquartile_range;
    $high = $fns[3] + $interquartile_range;
    foreach ($arr as $v) {
        if ($v > $high || $v < $low)
            echo "$v is an outlier<br>";
    }
}

//$numbers = array( 19,20,21,21,22,30,60 ); // 60 is an outlier
$numbers = array( 1,230,239,331,340,800); // 1 is an outlier, 800 is an outlier
find_outliers($numbers);

请注意,这种方法虽然比标准差实现起来简单得多,但在您的示例中不会找到两个 60 个异常值,但效果很好。随便用代码,希望有用!

要了解该算法的工作原理以及我如何实现它,请访问:http://www.mathwords.com/o/outlier.htm

当然,这不会计算最终平均值,但在您运行 find_outliers() 后它有点微不足道:P

【讨论】:

    【解决方案3】:

    为什么不使用中位数?不是 30,是 21.5。

    【讨论】:

      【解决方案4】:

      您可以将值放入一个数组中,对数组进行排序,然后找到median,这通常是一个比平均值更好的数字,因为它会自动对异常值进行折现,不会比任何其他数字赋予它们更多的权重。

      【讨论】:

        【解决方案5】:

        您可以对数字进行排序,选择您喜欢的子范围(例如,中间的 90%),然后取其平均值。

        对于您的问题,没有一个真正的答案,因为总会有一些分布会给您一个有趣的答案(例如,考虑一个有偏的双峰分布)。这就是为什么通常使用盒须图来展示可能的统计数据,显示平均值、中位数、四分位数和异常值。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-01-14
          • 1970-01-01
          • 2018-11-05
          • 2021-09-04
          • 2012-06-19
          相关资源
          最近更新 更多