在数组中查找并不总是一致的模式

Finding a pattern in an array that is not always consistant

我有一个有序的十进制数数据集。这些数据总是相似的——但并不总是相同的。预期数据是一些 0 - 5 个大数字,然后是几个 (10 - 90) 个平均数字,然后是较小的数字。在某些情况下,可能会在平均数中混入大量数字'请参阅以下数组。

let expectedData = [35.267,9.267,9.332,9.186,9.220,9.141,9.107,9.114,9.098,9.181,9.220,4.012,0.132];



let expectedData = [35.267,32.267,9.267,9.332,9.186,9.220,9.141,9.107,30.267,9.114,9.098,9.181,9.220,4.012,0.132];

我正在尝试通过获取平均值来分析数据,而不是前面的高数字和背面的低数字。中间 high/low 可以保持在平均水平。我在下面有一个部分解决方案。现在我有点蛮力,但解决方案并不完美。在较小的数据集上,第一个平均计算受较大数字的影响。

我的问题是:有没有办法处理这类问题,即识别数字数组中的模式?

我的算法是:

  1. 获取数组的平均值
  2. 计算 above/below 平均值
  3. 移除前 (n) 个高于平均值的元素
  4. 删除低于平均水平的结束元素
  5. 重新计算平均值

在 JavaScript 我有:(这是部分遗漏低于平均水平)

let total= expectedData.reduce((rt,cur)=> {return rt+cur;}, 0);
let avg = total/expectedData.length;
let aboveAvg = avg*0.1+avg;
let remove = -1;
 
for(let k=0;k<expectedData.length;k++) {
    if(expectedData[k] > aboveAvg) {
    remove=k;
    } else {
        if(k==0) {
            remove = -1;//no need to remove
        } 
          //break because we don't want large values from middle removed.
        break;
        }
}
if(remove >= 0 ) {
//remove front above average     
 expectedData.splice(0,remove+1);
 
}

//remove belows
//recalculate average 

首先,我会尝试使用滑动 window 和滞后/带通滤波器来检测高值峰值。

然后,当你的滑动 windows 前进时,你可以将之前的第一个值(现在是分析的最后一个值)添加到全局总和,并将总值的数量加 1。

当您遇到峰值(=导致滞后移动或溢出频带滤波器的东西)时,您可以删除这些值(可能代价高昂),或者更好的是,将值设置为 NaN所以你可以安全地忽略它。

您应该在滑动 window 中继续计算滑动平均值,以便能够 auto-correct hysteresis/band 过滤器,因此它只会拒绝峰值的起始值(结束值是下一个的起始值),但一旦值稳定到一个新的水平,值将再次保持。

滑动的大小window将设置需要保留多少连续的“稳定”值,或者换句话说,当你达到一个新的水平时,有多少不稳定的值被拒绝。

我相信您正在寻找一些离群值检测算法。 Stack overflow 上已经有一堆与此相关的问题。

但是,每种异常值检测算法都有其优点。

以下是其中的一些

  1. https://mathworld.wolfram.com/Outlier.html

    • 高异常值是超出第三个四分位数 + 1.5 * inter-quartile 范围 (IQR)

      的任何值
    • 低异常值是低于第一个四分位数的任何值 - 1.5 * IQR

  2. 格拉布斯试验

    • 您可以查看它如何符合您的期望here

除了这两个,还有一个比较计算器here。您可以访问此网站以根据需要使用其他算法。

为此,您可以检查值的众数(四舍五入),然后取众数附近一定范围内的所有数字。该范围可以取自数据本身,例如取 max - min 值的 10%。这有助于您过滤数据。您可以 select 满足您需求的百分比。像这样:

let expectedData = [35.267,9.267,9.332,9.186,9.220,9.141,9.107,9.114,9.098,9.181,9.220,4.012,0.132];

expectedData.sort((a, b) => a - b);

/// Get the range of the data
const RANGE = expectedData[ expectedData.length - 1 ] - expectedData[0];
const WINDOW = 0.1; /// Window of selection 10% from left and right

/// Frequency of each number
let dist = expectedData.reduce((acc, e) => (acc[ Math.floor(e) ] = (acc[ Math.floor(e) ] || 0) + 1, acc), {});
let mode = +Object.entries(dist).sort((a, b) => b[1] - a[1])[0][0];

let newData = expectedData.filter(e => mode - RANGE * WINDOW <= e && e <= mode + RANGE * WINDOW);

console.log(newData);