如何找到多维数组最深子数组的中位数?

How to find the median of deepest subarrays of multidimensional array?

我有一个四级多维数组。我需要按升序 (ASC) 对数字 "leaves" 进行排序,以便计算值的中位数。

我尝试了 array_walk_recursive()array_multisort()usort() 等,但无法找到可行的解决方案。

这是阵列的示意图:

(
    [2017-05-01] => Array
        (
            [DC] => Array
                (
                    [IT] => Array
                        (
                            [0] => 90
                            [1] => 0
                        )    
                    [DE] => Array
                        (
                            [0] => 18
                            [1] => 315
                            [2] => 40
                            [3] => 
                            [4] => 69
                        )    
                    [Other] => Array
                        (
                            [0] => 107
                            [1] => 46
                            [2] => 
                            [3] => 
                            [4] => 27
                            [5] => 22
                        )    
                )
        )
)

事实证明,有一种方法可以使用 usort() 和 array_walk() 的组合来执行 OP 寻求的操作,每个方法都需要一个回调,如下所示:

<?php
// median code: 
//http://www.mdj.us/web-development/php-programming/calculating-the-median-average-values-of-an-array-with-php/

function calculate_median($arr) {
    sort($arr);
    $count = count($arr); //total numbers in array
    $middleval = floor(($count-1)/2); // find the middle value, or the lowest middle value
    if($count % 2) { // odd number, middle is the median
        $median = $arr[$middleval];
    } else { // even number, calculate avg of 2 medians
        $low = $arr[$middleval];
        $high = $arr[$middleval+1];
        $median = (($low+$high)/2);
    }
    return $median;
}


$a = [];
$a["2017-05-01"] = ["DC"];

$a["2017-05-01"]["DC"]["IT"] = [90,0];
$a["2017-05-01"]["DC"]["DE"] = [18,315,40,"",69];
$a["2017-05-01"]["DC"]["Other"] = [107,46,"","",27,22];


function sort_by_order ($a, $b)
{
     if ($a == "") $a = 0;
     if ($b == "") $b = 0;
     return $a - $b;
}

function test($item,$key){
    echo $key," ";
    if (is_array($item)) {
       echo array_keys($item)[1],"\n";
       $popped = array_pop($item);
       foreach ($popped as $key => $arr) {
          usort($arr, 'sort_by_order');
          echo "Median ($key): ",calculate_median( $arr ),"\n";
        }
     }
}

array_walk($a, 'test');

查看演示 here. Also, see this example based on the OP's sandbox

虽然 OP 的代码没有显示引用的数组键,但请注意它们应该在实际代码中,否则 PHP 将对 2017-05-01 进行数学运算,您会看到一个键2011. 有趣的阅读 here 关于 usort。

我提取的中位数代码here.

有趣的是,关于对数字进行排序以确定中位数的传统智慧不一定是获得该结果的唯一方法。显然,它也可以通过找到一个主元数并将数字系列分成三部分来完成,也许更有效(参见 response)。

这将使用输入数组的结构输出最深子数组的中值。

如果值是空字符串,我将中值(一个或两个在子集中)强制转换为整数。我还假设如果子集为空,您将希望 0 作为输出。

代码:(Demo)

$array=[
    '2017-05-01'=>[
        'DC'=>[
            'IT'=>[90, 0],
            'DE'=>[18, 315, 40, '', 69, 211],
            'Other'=>[107, 46, '', '', 27, 22]
        ]
    ],
    '2017-05-02'=>[
        'DC'=>[
            'IT'=>[70, 40, 55],
            'DE'=>['', 31, 4, '', 9],
            'Other'=>[1107, 12, 0, 20, 1, 11, 21]
        ]
    ],
    'fringe case'=>[
        'DC'=>[
            'IT'=>[],
            'DE'=>['', '', '', 99],
            'Other'=>['', 99]
        ]
    ]
];

foreach ($array as $k1 => $lv1) {
    foreach ($lv1 as $k2 => $lv2) {
        foreach ($lv2 as $k3 => $lv3) {
            sort($lv3);                  // order values ASC
            $count = sizeof($lv3);       // count number of values
            $index = floor($count / 2);  // get middle index or upper of middle two
            if (!$count) {               // count is zero
                $medians[$k1][$k2][$k3] = 0;
            } elseif ($count & 1) {      // count is odd
                $medians[$k1][$k2][$k3] = (int)$lv3[$index];                        // single median
            } else {                     // count is even
                $medians[$k1][$k2][$k3] = ((int)$lv3[$index-1] + (int)$lv3[$index]) / 2; // dual median
            }
        }
    }
}
var_export($medians);

输出:

 array (
  '2017-05-01' => 
  array (
    'DC' => 
    array (
      'IT' => 45,
      'DE' => 54.5,
      'Other' => 24.5,
    ),
  ),
  '2017-05-02' => 
  array (
    'DC' => 
    array (
      'IT' => 55,
      'DE' => 4,
      'Other' => 12,
    ),
  ),
  'fringe case' => 
  array (
    'DC' => 
    array (
      'IT' => 0,
      'DE' => 0,
      'Other' => 49.5,
    ),
  ),
)

*为了记录,$count & 1 是一个按位比较,确定值是否为奇数 而无需 执行算术(并且是执行此检查的最有效方法php).

*另外,如果你想简单地覆盖输入数组的值,你可以通过在 $lv1 之前写 & 通过引用修改 $lv2 和 foreach 声明中的 $lv3 然后将中值保存到 $lv3Demo 这样做的好处是删除键声明并使您的代码更简洁。