user3092108 user3092108 - 5 months ago 40
JSON Question

Averaging together values in multiple json arrays in php

I'm trying to put together a a PHP function that gets activity data and displays it in a graph. I have that part working, but what I would like to do is average together a weeks worth of activity data and averaging each point together with one another. There are 480 data points in each day – one every three minutes. Each day is stored as a Json array, like this(shortened) :

{"12:00 AM":5.8227539,"12:03 AM":0,"12:06 AM":0,"12:09 AM":2.44,}

{"12:00 AM":2.569,"12:03 AM":0.864,"12:06 AM":0,"12:09 AM":3.14,}

{"12:00 AM":0.0,"12:03 AM":4.2,"12:06 AM":4.632,"12:09 AM":2.11,}


Is there a way in PHP to average many json arrays together and output a json string with the averaged values? Thanks.

EDIT
So the average of the 3 arrays above would result in:

{"12:00 AM":2.7972,"12:03 AM":1.688,"12:06 AM":1.544,"12:09 AM":2.5633}

Cam Cam
Answer

You can use json_decode to transform the JSON data into a PHP object. Then, if you cast the result as an associative array, you can take advantage of PHP's array_sum and count functions to calculate the average:

// load your json data into a PHP array
$data = (array) json_decode( $json );
// calculate average
$avg = array_sum($data) / count($data);

That suffices for a single JSON object. If you want to perform the averaging on multiple JSON objects, you'll need to load them all together. The easiest way would be to encode them into a JSON array of objects:

// format your json data into an array of objects
$json = '[
  {"12:00 AM":5.8227539,"12:03 AM":0,"12:06 AM":0,"12:09 AM":2.44},
  {"12:00 AM":2.569,"12:03 AM":0.864,"12:06 AM":0,"12:09 AM":3.14},
  {"12:00 AM":0.0,"12:03 AM":4.2,"12:06 AM":4.632,"12:09 AM":2.11}
]';

Then you can perform the averaging calculation as you traverse the array:

// load your json data into a PHP array
$data = json_decode( $json );
$count = 0;  //let's track our count
$sum = 0;    //and sum
// traverse the array, calculating the total sum by
// aggregating the sum of each inner object
foreach( $data as $obj ) {
  $loc_array = (array) $obj;     //cast to array
  $count += count($loc_array);   //add to count
  $sum += array_sum($loc_array); //add to sum
}
$avg = $sum / $count;            //calculate average

The last step would be formatting your calculation as a JSON string:

$output = json_encode([ 'avg' => $avg ]);

Update with time-point averages

To calculate the average for each time-point, we could use array_walk with a helper function to aggregate the sums and counts. Then loop back over the resultant data to calculate the averages.

// load your json data into a PHP array
$data = json_decode( $json );

$a_helper = [];  // track the sums and counts
$a_avgs = [];    // we'll write the averages to this array

// We'll create a function we can use to write to our helper arrays
function generate_sums( $value, $key ) {
  global $a_helper;
  if( !isset($a_helper[$key]) ) {
    $a_helper[$key] = [0,0];    // [sum,count]
  }
  $a_helper[$key][0] += $value; // add sum
  $a_helper[$key][1] += 1;      // add count
}

// Have array_walk populate the helper arrays using our function
foreach( $data as $obj ) {
  $loc_array = (array) $obj;     //cast to array
  array_walk($loc_array, 'generate_sums');
}

// Loop over each value, calculating the averages
foreach( $a_helper as $key => $pair ) {
  $a_avgs[$key] = $pair[0] / $pair[1];
}
$output = json_encode( $a_avgs );
// => {"12:00 AM":2.7972513,"12:03 AM":1.688,"12:06 AM":1.544,"12:09 AM":2.5633333333333}

Note the helper function is referencing a global variable ($a_helper). While use of global variables is not ideal, array_walk doesn't appear to permit pass-by-reference within its $userdata parameter now that run-time passing-by-reference is prohibited. Other work-arounds are possible, but somewhat exotic:

Pass-by-reference the third parameter in PHP array_walk, without a warning

Comments