在 PHP 中的数字数组中查找和删除异常值/异常

Posted

技术标签:

【中文标题】在 PHP 中的数字数组中查找和删除异常值/异常【英文标题】:Finding and removing outliers / anomalies in an array of numbers in PHP 【发布时间】:2021-10-23 23:25:19 【问题描述】:

我在 php 中有一个这样的数字数组:

$numbers = [
    0.0021030494216614,
    0.0019940179461615,
    0.0079320972662613,
    0.0040485829959514,
    0.0079320972662613,
    0.0021030494216614,
    0.0019940179461615,
    0.0079320972662613,
    0.0040485829959514,
    0.0079320972662613,
    0.0021030494216614,
    1.1002979145978,
    85.230769230769,
    6.5833333333333,
    0.015673981191223
];

在 PHP 中,我试图在这个数组中找到异常值/异常值。

如您所见,异常是

1.1002979145978,
85.230769230769,
6.5833333333333,
0.015673981191223

我正在尝试查找并删除任何数组中的异常。

这是我的代码

function remove_anomalies($dataset, $magnitude = 1) 
    $count = count($dataset);
    $mean = array_sum($dataset) / $count;
    $deviation = sqrt(array_sum(array_map("sd_square", $dataset, array_fill(0, $count, $mean))) / $count) * $magnitude;
        
    return array_filter($dataset, function($x) use ($mean, $deviation)  return ($x <= $mean + $deviation && $x >= $mean - $deviation); );

    
function sd_square($x, $mean) 
    return pow($x - $mean, 2);

但是,当我将我的 $numbers 数组放入时,只有当那里明显有更多异常值时,它才会将 [85.230769230769] 作为异常值。 我试过摆弄$magnitude 并没有改善任何东西。

【问题讨论】:

解释是什么使它成为异常值,这样我们就不必对您的所有计算进行逆向工程。 您的标准差计算似乎是正确的,但除了 85.230769230769 之外,没有一个数字超出平均值的一个标准差。打印出$deviation,你会看到它是21.185657155859。 也意味着“集合中间的值”,您已经计算了平均值。即便如此,您的异常值与其余数据的步调如此惊人,而且您的数据集如此之小,以至于显着偏离了标准偏差。在这种情况下,如果您正在寻找一种算法来为您挑选数据,那么它可能会带有对数。或者像从集合中删除顶部和底部 5% 的结果一样简单。 另见:stats.stackexchange.com 【参考方案1】:

此处显示的算法使用平均绝对偏差 (MAD) 来识别异常值。 所有距离超过 MAD 倍数的元素都会被连续移除,并重新计算 MAD。

  function median(array $data)
  
    if(($count = count($data)) < 1) return false;
    sort($data, SORT_NUMERIC);
    $mid = (int)($count/2);
    if($count % 2) return $data[$mid];
    return  ($data[$mid] + $data[$mid-1])/2;
  
  
  function mad(array $data)
  
    if(($count = count($data)) < 1) return false;
    $median = median($data);
    $mad = 0.0;
    foreach($data as $xi) 
      $mad += abs($xi - $median);
    
    return $mad/$count;
  

  function cleanMedian(array &$data, $fac = 2.0)
  
    do
      $unsetCount = 0;
      $median = median($data);
      $mad = mad($data) * $fac;
      //remove all with diff > $mad
      foreach($data as $idx => $val)
        if(abs($val - $median) > $mad)
          unset($data[$idx]);
          ++$unsetCount;
        
      
     while($unsetCount > 0);
  

使用方法:

$data = [
 //..
];
cleanMedian($data);

参数$fac需要根据数据进行试验。 使用 $ fac = 2 你会得到想要的结果。

array (
  0 => 0.0021030494216614,
  1 => 0.0019940179461615,
  2 => 0.0079320972662613,
  3 => 0.0040485829959514,
  4 => 0.0079320972662613,
  5 => 0.0021030494216614,
  6 => 0.0019940179461615,
  7 => 0.0079320972662613,
  8 => 0.0040485829959514,
  9 => 0.0079320972662613,
  10 => 0.0021030494216614,
)

当 fac = 4 时,包含值 0.015673981191223。

【讨论】:

以上是关于在 PHP 中的数字数组中查找和删除异常值/异常的主要内容,如果未能解决你的问题,请参考以下文章

如何检测业务数据中的异常

从数组中删除重复项时出现超出范围异常

python 从numpy数组中删除异常值

php数组元素值的修改和删除问题

如何在一个键值不同的php数组中查找和删除几乎重复的行?

如何在数组中查找一个值并使用 PHP 数组函数将其删除?