平滑数组的值

Smoothing out values of an array

如果我有一个数字数组,例如 [3, 5, 0, 8, 4, 2, 6],有没有办法“平滑”这些值,使它们彼此更接近并且显示更少的方差?

我已经研究过使用称为高斯函数的东西对 1 维情况 进行数据窗口化,这是我的数组,但在实现它时遇到了问题。 This thread 似乎正好解决了我需要的问题,但我不明白用户 naschilling(第二个 post)是如何得出高斯矩阵值的。

上下文: 我正在研究一个音乐波形发生器(借鉴了 SoundCloud 的设计),它映射了歌曲在时间 t[=49 的振幅=] 到相应的条形高度。不幸的是,有很多噪音,当程序映射一个微小的幅度导致高度突然下降时,它看起来特别难看。我基本上想平滑条形高度,这样它们就不会那么多变了。

我使用的语言是Javascript。

编辑:抱歉,让我更具体地说明一下 "smoothing out" 值。根据上面链接的线程,用户拿了一个数组

[10.00, 13.00, 7.00, 11.00, 12.00, 9.00, 6.00, 5.00]

并使用高斯函数将其映射到

[ 8.35,  9.35, 8.59,  8.98,  9.63, 7.94, 5.78, 7.32]

请注意数字彼此之间的距离如何。

编辑 2:成功了!感谢用户 Awal Garg 的算法,结果如下:

无平滑 一些平滑处理 最大平滑度

编辑 3:这是我在 JS 中的最终代码。我对其进行了调整,以便数组的第一个和最后一个元素能够通过 环绕 数组而不是调用自身来找到它的邻居。

var array = [10, 13, 7, 11, 12, 9, 6, 5];

function smooth(values, alpha) {
    var weighted = average(values) * alpha;
    var smoothed = [];
    for (var i in values) {
        var curr = values[i];
        var prev = smoothed[i - 1] || values[values.length - 1];
        var next = curr || values[0];
        var improved = Number(this.average([weighted, prev, curr, next]).toFixed(2));
        smoothed.push(improved);
    }
    return smoothed;
}

function average(data) {
    var sum = data.reduce(function(sum, value) {
        return sum + value;
    }, 0);
    var avg = sum / data.length;
    return avg;
}

smooth(array, 0.85);

有趣的问题!

平滑值的算法显然可以变化 很多,但这是我的看法:

"use strict";
var array = [10, 13, 7, 11, 12, 9, 6, 5];

function avg (v) {
  return v.reduce((a,b) => a+b, 0)/v.length;
}

function smoothOut (vector, variance) {
  var t_avg = avg(vector)*variance;
  var ret = Array(vector.length);
  for (var i = 0; i < vector.length; i++) {
    (function () {
      var prev = i>0 ? ret[i-1] : vector[i];
      var next = i<vector.length ? vector[i] : vector[i-1];
      ret[i] = avg([t_avg, avg([prev, vector[i], next])]);
    })();
  }
  return ret;
}

function display (x, y) {
  console.clear();
  console.assert(x.length === y.length);
  x.forEach((el, i) => console.log(`${el}\t\t${y[i]}`));
}

display(array, smoothOut(array, 0.85));

注意:它使用了一些 ES6 功能,例如粗箭头函数和模板字符串。 Firefox 35+ 和 Chrome 45+ 应该可以正常工作。否则请使用babel repl。

我的方法基本上是预先计算数组中所有元素的平均值,并将其用作主要因子来计算新值和当前元素值,它之前的一个,它之后的一个。我还使用先验值作为 newly 计算的值,而不是原始数组中的值。随意根据您的需要进行试验和修改。也可以传入一个"variance"参数来控制元素之间的差异。降低它会使元素彼此更接近,因为它会降低平均值。

放松平滑的轻微变化是这样的:

"use strict";
var array = [10, 13, 7, 11, 12, 9, 6, 5];

function avg (v) {
  return v.reduce((a,b) => a+b, 0)/v.length;
}

function smoothOut (vector, variance) {
  var t_avg = avg(vector)*variance;
  var ret = Array(vector.length);
  for (var i = 0; i < vector.length; i++) {
    (function () {
      var prev = i>0 ? ret[i-1] : vector[i];
      var next = i<vector.length ? vector[i] : vector[i-1];
      ret[i] = avg([t_avg, prev, vector[i], next]);
    })();
  }
  return ret;
}

function display (x, y) {
  console.clear();
  console.assert(x.length === y.length);
  x.forEach((el, i) => console.log(`${el}\t\t${y[i]}`));
}

display(array, smoothOut(array, 0.85));

不以平均值为主要因素

随时尝试,希望对您有所帮助!

您描述的技术听起来像是 Gaussian blur 的一维版本。将一维高斯数组的值乘以数组中给定的 window 并对结果求和。例如

  1. 假设一个高斯数组 {.242, .399, .242}
  2. 要计算输入数组位置 n 的新值 - 将输入数组的 n-1、n 和 n+1 处的值乘以 (1) 中的值,然后求和结果。例如对于 [3, 5, 0, 8, 4, 2, 6], n = 1:

    n1 = 0.242 * 3 + 0.399 * 5 + 0.242 * 0 = 2.721

您可以改变高斯方差来增加或减少模糊的影响。