2

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}
Sadie
  • 121
  • 1
  • 7

1 Answers1

0

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

Community
  • 1
  • 1
Cam
  • 921
  • 7
  • 13
  • Thanks. I think I might not have described what I'm trying to do as clearly as I could have... I'm looking to average together the same value from each... so, the average of all the 12:00 AM values, the avg of all the 12:03 AM values, etc. so that I will get back an array with these values (given the 3 array inputs above): {"12:00 AM":2.7972,"12:03 AM":1.688,"12:06 AM":1.544,"12:09 AM":2.5633} – Sadie Jun 25 '16 at 18:04
  • Thanks so much. That was exactly what I was looking to do. And, thanks for coming back to it! – Sadie Jun 25 '16 at 21:58
  • I have one more question... How can output an array that has all of the sums? What I want to do, is for each of my entries, store the average in my database, but also store the sums, so that for future entries, I don't need to recalculate all of the entries together. – Sadie Jun 26 '16 at 01:04
  • The helper array `$a_helper` is tracking the sum and count data in internal 2-item arrays: [sum,count]. You need the sum and counts to calculate the average correctly... To output it, you can ` json_encode($a_helper);` which generates the following JSON string: `{"12:00 AM":[8.3917539,3],"12:03 AM":[5.064,3],"12:06 AM":[4.632,3],"12:09 AM":[7.69,3]}` – Cam Jun 26 '16 at 03:32