Javascript:从数组中删除异常值?

时间:2013-12-28 04:52:56

标签: javascript statistics

values = [8160,8160,6160,22684,0,0,60720,1380,1380,57128]

如何删除0,57218,60720和22684等异常值?

是否有可以执行此操作的库?

5 个答案:

答案 0 :(得分:14)

这完全取决于您对 “异常值”的解释。一种常见的方法:

  • 高离群值超出第3四分之一+ 1.5 * 四分位数范围(IQR)
  • 低离群值低于第一四分位数 - 1.5 * IQR

这也是Wolfram's Mathworld描述的方法。

这很容易包含在一个函数中:)我试着写清楚以下内容;确实存在明显的重构机会。 请注意,使用此常用方法时,您的给定样本不包含任何外围值。

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;
}

答案 1 :(得分:3)

这是@ james-peterson解决方案的改进版本,它将语法更新为当前的Javascript标准,并添加了更健壮的方法来查找两个四分位数(根据https://de.wikipedia.org/wiki/Interquartilsabstand_(Deskriptive_Statistik)处的公式实现  )。它使用更快的方式复制数组(请参阅http://jsben.ch/wQ9RU进行性能比较),仍适用于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));
}

请参阅此要点:https://gist.github.com/rmeissn/f5b42fb3e1386a46f60304a57b6d215a

答案 2 :(得分:1)

这里是从给定集合中筛选出离群值的实现。这种方法采用与上面提供的答案类似的方法。

if情况将检查集合的长度是4n还是4n + 1。在这种情况下,我们需要平均获得两个元素才能拥有四分位数。

4n and 4n+1 cases

否则,对于4n + 24n + 3,我们可以直接访问上/下四分位数。

4n+2 and 4n+3 cases


const outlierDetector = collection => {
    const size = collection.length;

    let q1, q3;

    if (size < 2) {
        return collection;
    }

    const sortedCollection = collection.slice().sort((a, b) => a - b);

    if ((size - 1) / 4 % 1 === 0 || size / 4 % 1 === 0) {
        q1 = 1 / 2 * (sortedCollection[Math.floor(size / 4) - 1] + sortedCollection[Math.floor(size / 4)]);
        q3 = 1 / 2 * (sortedCollection[Math.ceil(size * 3 / 4) - 1] + sortedCollection[Math.ceil(size * 3 / 4)]);
    } else {
        q1 = sortedCollection[Math.floor(size / 4)];
        q3 = sortedCollection[Math.floor(size * 3 / 4)];
    }

    const iqr = q3 - q1;
    const maxValue = q3 + iqr * 1.5;

    return sortedCollection.filter(value => value >= maxValue);
};

答案 3 :(得分:0)

如果您的数据集包含重复值,则此方法实际上会失败。例如。 1, 2, 2, 2, 2, 2, 3, 10

我挣扎了一段时间,但后来我发现了一种名为Grubbs&test的东西。到目前为止,至少在我的情况下它似乎是可靠的。

这是指向演示(和来源)的链接:http://xcatliu.com/grubbs/

答案 4 :(得分:0)

其他两个解决方案都存在一些问题。由于索引错误,诸如NaN值分别为q1和q3的问题。由于索引为0,因此数组长度必须为-1。然后检查索引是int还是小数,如果是小数,则提取两个索引之间的值。

function filterOutliers (someArray) {
    if (someArray.length < 4) {
        return someArray;
    }

    let values = someArray.slice().sort((a, b) => a - b); // copy array fast and sort

    let q1 = getQuantile(values, 25);
    let q3 = getQuantile(values, 75);

    let iqr, maxValue, minValue;
    iqr = q3 - q1;
    maxValue = q3 + iqr * 1.5;
    minValue = q1 - iqr * 1.5;

    return values.filter((x) => (x >= minValue) && (x <= maxValue));
}

function getQuantile (array, quantile) {
    // Get the index the quantile is at.
    let index = quantile / 100.0 * (array.length - 1);

    // Check if it has decimal places.
    if (index % 1 === 0) {
        return array[index];
    } else {
        // Get the lower index.
        let lowerIndex = Math.floor(index);
        // Get the remaining.
        let remainder = index - lowerIndex;
        // Add the remaining to the lowerindex value.
        return array[lowerIndex] + remainder * (array[lowerIndex + 1] - array[lowerIndex]);
    }
}