Reputation: 2336
How would you calculate the median (P50) of an array using jq? The jq manual describes how to calculate the mean, but I'd like to get the median.
Algorithm: Given a list of numbers, sort it. If there are an odd number of entries then pick the middle one. If there are an even number of entries then calculate the mean of the middle two.
Examples:
echo '[1,5,9,3]' | jq <ANSWER> # should output 4, since it's the mean of 3 and 5
echo '[1,9,2]' | jq <ANSWER> # should output 2, since it's the middle element
echo '[]' | jq <ANSWER> # undefined
Upvotes: 2
Views: 919
Reputation: 36296
This approach takes advantage of the fact that the center of the array, which can elementwise be "the middle one" or "the middle two", is by definition always equidistant from the array's both ends. That is, in a sorted array the distance from the lower middle value back to the first element is the same as the distance from the upper middle value to the last element, regardless of the lower and the upper middle values residing in the same element or not. Thus we only have to compute one of the two, move that far from both ends, take those elements and calculate their mean value. (If they happen to be the same, calculating the mean of two identical values won't harm.) The only obstacle we have to overcome is that jq counts 0-based when starting from the beginning but 1-based (and negative) when starting from the end of the array, so the two indices must be one apart in absolute values and have opposing signs.
For calculating the mean value, one would traditionally first add up the values and then cut the sum in half. However, when dealing with empty arrays, adding up the (non-existent) values would yield null
, and a subsequent application of /2
would raise an error. However, if we halve the values first and use ?
to skip where it's not applicable, then getting null
for the final add
would not pose a problem as it is supposedly the expected output when inputting an empty array.
sort | [ .[length/2 | ceil | -.,.-1] /2? ] | add
Upvotes: 0
Reputation: 2336
Answer:
sort |
if length == 0 then null
elif length % 2 == 0 then (.[length/2] + .[length/2-1])/2
else .[length/2|floor] end
It's a straightforward implementation of the specified algorithm.
Upvotes: 7