Lilleman 的代码很棒。我真的很欣赏他处理中位数和 q1/q3 的方式。如果我先回答这个问题,我会以更难但不必要的方式处理奇数和偶数的值。我的意思是对 4 种不同的模式情况( count( values ) , 4 )使用 if 4 次。但他的方式只是干净整洁。我真的很佩服他的工作。
我想对 max、min、higher_outliers 和 lower_outliers 做一些改进。因为 q1 - 1.5*IQR 只是下限,我们应该找到大于该下限的最小值作为“min”。这对于“最大”是相同的。此外,可能存在不止一个异常值。所以我想在Lilleman的工作的基础上做一些改变。谢谢。
function box_plot_values($array)
{
$return = array(
'lower_outlier' => 0,
'min' => 0,
'q1' => 0,
'median' => 0,
'q3' => 0,
'max' => 0,
'higher_outlier' => 0,
);
$array_count = count($array);
sort($array, SORT_NUMERIC);
$return['min'] = $array[0];
$return['lower_outlier'] = array();
$return['max'] = $array[$array_count - 1];
$return['higher_outlier'] = array();
$middle_index = floor($array_count / 2);
$return['median'] = $array[$middle_index]; // Assume an odd # of items
$lower_values = array();
$higher_values = array();
// If we have an even number of values, we need some special rules
if ($array_count % 2 == 0)
{
// Handle the even case by averaging the middle 2 items
$return['median'] = round(($return['median'] + $array[$middle_index - 1]) / 2);
foreach ($array as $idx => $value)
{
if ($idx < ($middle_index - 1)) $lower_values[] = $value; // We need to remove both of the values we used for the median from the lower values
elseif ($idx > $middle_index) $higher_values[] = $value;
}
}
else
{
foreach ($array as $idx => $value)
{
if ($idx < $middle_index) $lower_values[] = $value;
elseif ($idx > $middle_index) $higher_values[] = $value;
}
}
$lower_values_count = count($lower_values);
$lower_middle_index = floor($lower_values_count / 2);
$return['q1'] = $lower_values[$lower_middle_index];
if ($lower_values_count % 2 == 0)
$return['q1'] = round(($return['q1'] + $lower_values[$lower_middle_index - 1]) / 2);
$higher_values_count = count($higher_values);
$higher_middle_index = floor($higher_values_count / 2);
$return['q3'] = $higher_values[$higher_middle_index];
if ($higher_values_count % 2 == 0)
$return['q3'] = round(($return['q3'] + $higher_values[$higher_middle_index - 1]) / 2);
// Check if min and max should be capped
$iqr = $return['q3'] - $return['q1']; // Calculate the Inner Quartile Range (iqr)
$return['min'] = $return['q1'] - 1.5*$iqr; // This ( q1 - 1.5*IQR ) is actually the lower bound,
// We must compare every value in the lower half to this.
// Those less than the bound are outliers, whereas
// The least one that greater than this bound is the 'min'
// for the boxplot.
foreach( $lower_values as $idx => $value )
{
if( $value < $return['min'] ) // when values are less than the bound
{
$return['lower_outlier'][$idx] = $value ; // keep the index here seems unnecessary
// but those who are interested in which values are outliers
// can take advantage of this and asort to identify the outliers
}else
{
$return['min'] = $value; // when values that greater than the bound
break; // we should break the loop to keep the 'min' as the least that greater than the bound
}
}
$return['max'] = $return['q3'] + 1.5*$iqr; // This ( q3 + 1.5*IQR ) is the same as previous.
foreach( array_reverse($higher_values) as $idx => $value )
{
if( $value > $return['max'] )
{
$return['higher_outlier'][$idx] = $value ;
}else
{
$return['max'] = $value;
break;
}
}
return $return;
}
我希望这对那些对此问题感兴趣的人有所帮助。如果有更好的方法可以知道哪些值是异常值,请给我添加评论。谢谢!