Evgeny P
Evgeny P

Reputation: 1

Median JS function. Wrong

Recently I did a test task in which it was necessary to calculate the median and other things. I wrote next function

function quartile(n) {
        var observation = n * (len + 1) / 4 - 1;
        if (n * (len + 1) % 4 === 0) {
          return sortedSet[observation];
        } else {
          return Math.round((sortedSet[Math.floor(observation)] + (observation - Math.floor(observation)) *
              (sortedSet[Math.ceil(observation)] - sortedSet[Math.floor(observation)])) * 10e3) / 10e3;
        }
}

In feedback I get that median calculated in the wrong way. Now I can't figure out in which cases this function will behaves incorrectly. Can you explain what is wrong here? P.S Use like this quartile(2)

Just sorting
I found what the problem was. I have used array.sort() and did not know that sorting in non natural order.

Upvotes: 0

Views: 154

Answers (1)

Thomas
Thomas

Reputation: 12657

Well, your equation is wrong, and your condition too.

Your calculation of observation is off by 0.5 for the 1st and 3rd quartille, and by 1 for 0 and 4. And your condition, better would have been if(observation%1 === 0) or if(observation === Math.floor(observation))

Here a fixed version, and as mentioned in the comment, formatting/rounding float's is not the job of this function.

function quartile(n) {
    //compute the position in the Array you want to fetch
    //n==0 return the first, n == 4 returns the last item in the Array
    var pos = n/4 * (len-1), 
        //separate the index ...
        i = Math.floor(pos), 
        //and the fractional part
        t = pos - i;

    return t?  //if there is a fractional part
        //interpolate between the computed and the next index
        (1-t) * sortedSet[i] + t * sortedSet[i+1]:
        //otherwise return the value at the computed index
        sortedSet[i];
}

Edit:

I took a look at wikipedia on that topic. God that's a weird, recursive, imo un-mathematical approach. And even worse, there's not one, there are three different definitions on how to calculate these quartiles, and they return different results.

//a utility, processes the part left <= indices < right
function _median(arr, left, right){
    var i = (left+right)>>1;
    return (right-left) & 1? arr[i]: .5 * (arr[i-1] + arr[i])
}

methods 1 and 2 are a more practical than mathematical approach, but through their recursive nature somehow quite straight forward

var method1 = {
  Q1(arr){
    return _median(arr, 0, arr.length>>1)
  },
  Q2(arr){
    return _median(arr, 0, arr.length);
  },
  Q3(arr){
    var len = arr.length;
    return _median(arr, (len>>1)+(len&1), len);
  }
}

var method2 = {
  Q1(arr){
    var len = arr.length;
    return _median(arr, 0, (len>>1) + (len&1))
  },
  Q2(arr){
    return _median(arr, 0, arr.length);
  },
  Q3(arr){
    var len = arr.length;
    return _median(arr, len>>1, len);
  }
}

method 3 feels more like: "we can't agree wich method to use, 1 or 2. Let's take the average of both and finally close this topic"

var method3 = {
  Q1(arr){
    var len = arr.length,
        r = (len >> 1) + (len&1),
        i = r>>1;
    return (r & 1)? arr[i]: .75 * arr[i-1] + .25*arr[i];
  },

  Q2(arr){
    return _median(arr, 0, arr.length);
  },

  Q3(arr){
    var len = arr.length,
        l = len>>1,
        i = (l+len)>>1;
    return (len-l)&1? arr[i]: .25 * arr[i-1] + .75*arr[i];
  }
}

Upvotes: 1

Related Questions