【问题标题】:Javascript: remove outlier from an array?Javascript:从数组中删除异常值?
【发布时间】:2013-12-28 04:52:56
【问题描述】:
values = [8160,8160,6160,22684,0,0,60720,1380,1380,57128]

如何去除 0、57218、60720 和 22684 等异常值?

有没有图书馆可以做到这一点?

【问题讨论】:

标签: javascript statistics


【解决方案1】:

这一切都取决于您对什么“异常值”的解释。一种常见的做法:

  • 高异常值是第三个四分位数之外的任何值 + 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;
}

【讨论】:

  • 它真的有效吗?我试过filterOutliers([8160,8160,6160,22684,0,0,60720,1380,1380,57128, 1000000000000]),它返回完全相同的数组。
  • 上述代码中出现了轻微的逻辑错误。过滤器应该返回 (x minValue);
  • 如果 q1===q3,则返回空数组。应该返回(x &lt;= maxValue) &amp;&amp; (x &gt;= minValue)
  • [4421, 3512, 5126, 6012, 7581, 2023, 5012, 2320, 17, 2125] 不会删除 17 这怎么可能? 17 肯定是个异常值吗?
  • @Frank : 17 不是异常值。您的数组 (1st.Quadrant - 1.5 * ( IQR )) 的下限远低于 17。
【解决方案2】:

这是 @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

【讨论】:

  • 第二个条件不适用于数组长度 Math.ceil(7 * (3/4) + 1) = 7。我猜Math.min 应该修复它
  • 如果数组长度为 4,则第一个条件中的 q3 也将是 NaN,因为 values[(values.length * (3 / 4)) + 1] 没有指向任何内容。因此,如果长度为 ,它可能应该退出
【解决方案3】:

我在使用其他两种解决方案时遇到了一些问题。诸如由于索引错误而将 NaN 值作为 q1 和 q3 的问题。由于 0 索引,数组长度需要有 -1。然后检查索引是整数还是小数,如果是小数,则提取两个索引之间的值。

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

【讨论】:

    【解决方案4】:

    这是从给定集合中过滤上层异常值的实现。这种方法遵循与上面提供的答案类似的方法。

    if 的情况将检查集合的长度,如果它是4n4n + 1。在这种情况下,我们需要得到两个元素的平均值才能得到四分位数。

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

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

    【讨论】:

      【解决方案5】:

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

      我挣扎了一段时间,但后来我发现了一个叫做 Grubbs'test 的东西。到目前为止,至少在我看来,它似乎是可靠的。

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

      【讨论】:

      • 你好,这个链接失效了,能发一下代码吗?
      • @BenHickson 我相信这是链接:github.com/xcatliu/grubbs 我已经有一段时间了,所以我不太记得具体细节了。
      猜你喜欢
      • 2020-08-28
      • 2016-08-20
      • 2017-04-24
      • 2016-06-15
      • 2021-01-16
      • 2015-12-24
      • 2019-05-12
      • 2019-09-11
      • 1970-01-01
      相关资源
      最近更新 更多