Javascript: remove outlier from an array?
This is an improved version of @james-peterson solution that updates the syntax to the current Javascript standard and adds a more robust way of finding the two quartiles (implemented according to formulas at https://de.wikipedia.org/wiki/Interquartilsabstand_(Deskriptive_Statistik) ). It uses a faster way of copying the array (see http://jsben.ch/wQ9RU for a performance comparison) and still works for q1 = q3.
function filterOutliers(someArray) {
if(someArray.length < 4)
return someArray;
let values, q1, q3, iqr, maxValue, minValue;
values = someArray.slice().sort( (a, b) => a - b);//copy array fast and sort
if((values.length / 4) % 1 === 0){//find quartiles
q1 = 1/2 * (values[(values.length / 4)] + values[(values.length / 4) + 1]);
q3 = 1/2 * (values[(values.length * (3 / 4))] + values[(values.length * (3 / 4)) + 1]);
} else {
q1 = values[Math.floor(values.length / 4 + 1)];
q3 = values[Math.ceil(values.length * (3 / 4) + 1)];
}
iqr = q3 - q1;
maxValue = q3 + iqr * 1.5;
minValue = q1 - iqr * 1.5;
return values.filter((x) => (x >= minValue) && (x <= maxValue));
}
See this gist: https://gist.github.com/rmeissn/f5b42fb3e1386a46f60304a57b6d215a
This all depends on your interpretation of what an "outlier" is. A common approach:
- High outliers are anything beyond the 3rd quartile + 1.5 * the inter-quartile range (IQR)
- Low outliers are anything beneath the 1st quartile - 1.5 * IQR
This is also the approach described by Wolfram's Mathworld.
This is easily wrapped up in a function :) I've tried to write the below clearly; obvious refactoring opportunities do exist. Note that your given sample contains no outlying values using this common approach.
function filterOutliers(someArray) {
// Copy the values, rather than operating on references to existing values
var values = someArray.concat();
// Then sort
values.sort( function(a, b) {
return a - b;
});
/* Then find a generous IQR. This is generous because if (values.length / 4)
* is not an int, then really you should average the two elements on either
* side to find q1.
*/
var q1 = values[Math.floor((values.length / 4))];
// Likewise for q3.
var q3 = values[Math.ceil((values.length * (3 / 4)))];
var iqr = q3 - q1;
// Then find min and max values
var maxValue = q3 + iqr*1.5;
var minValue = q1 - iqr*1.5;
// Then filter anything beyond or beneath these values.
var filteredValues = values.filter(function(x) {
return (x <= maxValue) && (x >= minValue);
});
// Then return
return filteredValues;
}