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

时间:2017-06-19 22:14:41

标签: php arrays sorting multidimensional-array median

我有一个四级多维数组。我需要按升序排序(ASC)数字"离开"为了计算值的中位数。

我尝试了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
                        )    
                )
        )
)

2 个答案:

答案 0 :(得分:2)

事实证明,有一种方法可以使用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。另外,请根据OP的example来查看此sandbox

虽然OP的代码没有显示引用的数组键,但要注意它们应该在实际代码中,否则PHP将使用2017-05-01进行数学计算,你会看到2011年的一个关键字。有趣的读取{{ 3}}关于usort。

我从here中提取的中位数代码。

有趣的是,关于排序数字以确定中位数的传统观点不一定是获得该结果的唯一方法。显然,它也可以通过找到一个支点号码并将一系列数字分成三个部分来完成,也许更有效率(见here)。

答案 1 :(得分:2)

这将输出最深的子阵列&#39;使用输入数组结构的中值。

我包括在值为空字符串的情况下将中值(一个子集中的一个或两个)强制转换为整数。我还假设如果子集为空,您将需要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中执行此检查的最有效方式)。 / p>

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