psychopy.org | Reference | Downloads | Github

Error in JS median calculations

I’ve tried two different approaches to calculating the median, and have realised and solved the issue of alphabetical sorts. However, there still seems to be a problem with calculating the median of an array with an even number of elements.

In this example I have the following function in code_JS

median = function (values){
  if(values.length ===0) return 0;

  values.sort(function(a,b){
    return a-b;
  });

  var half = Math.floor(values.length / 2);

  if (values.length % 2)
    return values[half];

  return (values[half - 1] + values[half]) / 2.0;
}

Here are some arrays (from console.log)

rtMidSY=187
rtMidSN=378,329,445
rtMidSY0=102,152,441,200,256,216,440,234,392,358,270,506
rtMidMY=135,60,66,85,101,68,80
rtMidMN=36,222,56,86,87,95
rtMidMY0=1611,144,136,149,168,169,144,712,183,148,157,143

Here are the corresponding medians calculated by the function (in the same order as above)

187
378
128135
80
4343.5
74578.5

My previous attempt had a sort function in code_JS

sort = function(array) {
    return array.sort((a, b) => (a - b));
}

and then the following auto translated Python steps for the calculation

sort(rtListSY)
rtMidSY = (len(rtListSY)+1)/2-1
if rtMidSY == round(rtMidSY):
    results[0][5]=rtListSY[int(rtMidSY)]
else:
    results[0][5]=(rtListSY[int(rtMidSY-.5)]+rtListSY[int(rtMidSY+.5)])/2

It also gave huge numbers for arrays with an even number of elements.

Any ideas?

Any ideas @sotiri or @thomas_pronk ?

@wakecarter No problem, here is one using Wikipedia’s “caseless” formula:

That is assuming array contents have been sanitised to only include numbers. Also, if the input array is empty the result would be NaN

This makes the problem worse. Now all medians fail, rather than just the ones with an even number of digits.

median = function (inputMaybe = []) {
  // Sort in ascending order assuming data sanitized for e.g., `Infinity` and `NaN` values,
  // but get a copy first, because sorting happens in place
  const input = [...inputMaybe].sort((a, b) => a - b)

  // https://en.wikipedia.org/wiki/Median#Finite_data_set_of_numbers
  const i = 0.5 * (input.length + 1)
  const a = Math.floor(i) - 1
  const b = Math.ceil(i) - 1

  return 0.5 * (input[a] + input[b])
}

794,267,404,219 --> median 133702
207 --> median 103603.5
128,218,239,272,277,219,372,208,211,471,319,385,350 -- > median 136136
89,80,48,144,437,86,72,99,66 --> 4343
59,137,99 --> 4999.5
73,126,133,110,140,131,141,137,156,174,163 --> 68568.5

Are you sure your array contains numbers?

Let me try testing against the new samples you have posted, one moment please

I’m pretty sure, since earlier I realised that they were being sorted alphabetically and then tried the numeric sort and console logged the sorted array. If they didn’t contain numbers they presumably wouldn’t sort numerically.

Here’s an examples of how the array is built:

       rtListMY.append([round(key_resp.rt*1000)])
 

which is auto translating to

       rtListMY.append([Math.round((key_resp.rt * 1000))]);

I’ve just spotted something. I shouldn’t have square brackets in the append function, should I?

:blush:

Just checked, and yes that was it. I guess that the sort function still worked numerically but the averaging failed when all my reaction times were length one lists.