制作数组数据的平均值

时间:2016-10-19 11:39:31

标签: javascript arrays algorithm average

我坚持如何在Javascript中实现这种相对简单的操作:

我有一个以这种方式定义的对象列表:

[
    {id: 1, region: "America", country:"USA", values:[1,2,3,4] },
    {id: 2, region: "America", country:"Canada", values:[3,4,5,6] },
    {id: 3, region: "Europe", country:"France", values:[1,2,3,4] },
    {id: 4, region: "Europe", country:"Italy", values:[1,2,3,4] },
    {id: 5, region: "Europe", country:"Spain", values:[5,9,1,7] },
    {id: 6, region: "Europe", country:"Germany", values:[1,6,2,8] },
    {id: 7, region: "Europe", country:"Ireland", values:[6,4,6,9]}
      ]

我试图计算值字段中包含的数字的平均值,按地区分组。 所以在上面的列表中,我将有两个元素,一个用于美国,一个用于欧洲,包含值的平均值:

[
    {id: 1, region: "America", country:"USA", values:[1,2,3,4] },
    {id: 2, region: "America", country:"Canada", values:[3,4,5,6] },
    {id: 3, region: "Europe", country:"France", values:[1,2,3,4] },
    {id: 4, region: "Europe", country:"Italy", values:[1,2,3,4] },
    {id: 5, region: "Europe", country:"Spain", values:[5,9,1,7] },
    {id: 6, region: "Europe", country:"Germany", values:[1,6,2,8] },
    {id: 7, region: "Europe", country:"Ireland", values:[6,4,6,9]},
    {id: 8, region: "America", country:"avg", values:[2,3,4,5]},
    {id: 9, region: "Europe", country:"avg", values:[2.8,4.6,2.8,6.4]}
      ]

有关如何做的任何想法? 请记住,聚合的元素数量可能在10~15左右,值字段的数量可以在150~200左右 value字段包含所有元素的相同数量的值。 某些值可能为null,因此在这种情况下我需要计算平均值,因为null不是0!

我可以做很多循环来扫描所有内容并进行计算,但我想知道是否有更容易,更快的东西,保持良好的表现。

说明/实施例:
美国的第一个平均值将按如下方式计算:

(sum of first value of 'values' for each country with region 'America')
-----------------------------------------------------------------------
            (number of countries with region 'America')

伪代码:

America.avg.values[0] = (USA.values[0] + Canada.values[0]) / 2 /*(1+3)/2 = 2*/;
America.avg.values[1] = (USA.values[1] + Canada.values[1]) / 2 /*(2+4)/2 = 3*/;
...

6 个答案:

答案 0 :(得分:3)

您可以使用一个对象来保持元素的总和和计数,并将值的每个循环的平均值分配给分组对象。

var data = [{ id: 1, region: "America", country: "USA", values: [1, 2, 3, 4] }, { id: 2, region: "America", country: "Canada", values: [3, 4, 5, 6] }, { id: 3, region: "Europe", country: "France", values: [1, 2, 3, 4] }, { id: 4, region: "Europe", country: "Italy", values: [1, 2, 3, 4] }, { id: 5, region: "Europe", country: "Spain", values: [5, 9, 1, 7] }, { id: 6, region: "Europe", country: "Germany", values: [1, 6, 2, 8] }, { id: 7, region: "Europe", country: "Ireland", values: [6, 4, 6, 9] }];

data.forEach(function (a, i, aa) {
    if (!this[a.region]) {
        this[a.region] = { sum: [], count: [], values: [] };
        aa.push({ id: aa.length + 1, region: a.region, country: 'avg', values: this[a.region].values });
    }
    a.values.forEach(function (b, i) {
        if (b !== null) {
            this[a.region].sum[i] = (this[a.region].sum[i] || 0) + b;
            this[a.region].count[i] = (this[a.region].count[i] || 0) + 1;
            this[a.region].values[i] = this[a.region].sum[i] / this[a.region].count[i];
        }
    }, this);
}, Object.create(null));

console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 1 :(得分:1)

减少原始数据,跟踪对象中的中间结果:

  • Object为其遇到的每个新区域创建一个新密钥
  • 当考虑区域的第一个数据条目时,存储两件事:
    • 平均值,以第一项
    • 的值的副本开头
    • 平均值表示的计数,从1
    • 开始
  • 当出现第二个或第n个项目时,使用移动平均公式计算新的平均值
  • 返回结果对象,直到处理完所有条目
  • 将对象转换为两个数据点,并将它们连接到原始数组

var data = [
    {id: 1, region: "America", country:"USA", values:[1,2,null,4] },
    {id: 2, region: "America", country:"Canada", values:[3,4,5,6] },
    {id: 3, region: "Europe", country:"France", values:[1,2,3,4] },
    {id: 4, region: "Europe", country:"Italy", values:[1,2,3,4] },
    {id: 5, region: "Europe", country:"Spain", values:[5,9,1,7] },
    {id: 6, region: "Europe", country:"Germany", values:[1,6,2,8] },
    {id: 7, region: "Europe", country:"Ireland", values:[6,4,6,9]}
];

var avg = data.reduce(function(result, current) {
  if (result[current.region]) {
    var obj = result[current.region];
    obj.avg = current.values
      // Map to a moving average: 
      //  - the current avg at pos `i` represents `count` samples
      .map(function(v, i) { 
        if (v === null) return obj.avg[i];
      
        return (v + (obj.avg[i] * obj.count[i])) / (++obj.count[i]);
       });
  } else {
    result[current.region] = {
      count: current.values.map(function(v) { 
        return v !== null ? 1 : 0; 
      }),
      avg: current.values.map(function(v) {
        return v !== null ? v : 0;
      })
    };
  }
  
  return result;
}, {});

// Add to array (assume sorted by id)
var extendedData = data.concat(Object.keys(avg).map(function(k, i) {
  return {
    id: data[data.length - 1].id + 1 + i,
    region: k,
    country: "avg",
    values: avg[k].avg
  };
}));

console.log(extendedData);

答案 2 :(得分:0)

我只是遍历整个数组并计算平均值,然后在数组中添加avg属性。



var data = [
    {id: 1, region: "America", country:"USA", values:[1,2,3,4] },
    {id: 2, region: "America", country:"Canada", values:[3,4,5,6] },
    {id: 3, region: "Europe", country:"France", values:[1,2,3,4] },
    {id: 4, region: "Europe", country:"Italy", values:[1,2,3,4] },
    {id: 5, region: "Europe", country:"Spain", values:[5,9,1,7] },
    {id: 6, region: "Europe", country:"Germany", values:[1,6,2,8] },
    {id: 7, region: "Europe", country:"Ireland", values:[6,4,6,9]}
];

data.forEach(function(element,index,array){
  var sum = 0;
  element.values.forEach(function(element,index,array){
    sum += element;
  });
  element.avg = sum / element.values.length;
});

console.log(data);




答案 3 :(得分:0)

你可以这样:

var data = [{ id: 1, region: "America", country: "USA", values: [1, 2, null, 4] }, { id: 2, region: "America", country: "Canada", values: [3, 4, 5, 6] }, { id: 3, region: "Europe", country: "France", values: [1, 2, 3, 4] }, { id: 4, region: "Europe", country: "Italy", values: [1, 2, 3, 4] }, { id: 5, region: "Europe", country: "Spain", values: [5, 9, 1, 7] }, { id: 6, region: "Europe", country: "Germany", values: [1, 6, 2, 8] }, { id: 7, region: "Europe", country: "Ireland", values: [6, 4, 6, 9] }]

// So that regions are configurable
var regions = ['America', 'Europe'];
var result = [];

// for cases when data is not sorted by id.
var lastIndex = Math.max.apply(null, data.map(function(x){ return x.id}))

regions.forEach(function(r) {
  var val = [];
  data.forEach(function(c,i){
    if(c.region === r && c.values && c.values.length > 0){
      c.values.forEach(function(v, i) {
        if (!v && v!== 0) return
          val[i] = (val[i] || []);
          val[i].push(v)
      })
    }
  });
  
  var avg = val.map(function(v){
    return (v.reduce(function(p,c){ return p+c }) / v.length);
  })
  
  // not pushing into data to prevent extra iterations
  result.push({
    id: ++lastIndex,
    region: r,
    country: 'avg',
    values: avg
  })

});

data = data.concat(result)
console.log(data)

答案 4 :(得分:0)

以下是适用于任意数量地区的一般解决方案。我知道问题是陈旧(2小时),但考虑到方法的简单性。对于每个独特的区域,计算平均值:

1 - 将所有values数组作为给定区域的一个2d数组 2 - 转置此阵列
3 - 平均每个数组

var data = // your array here

function getArr(data,reg) {
    return data.filter(el => el.region === reg)
               .map(el => el.values)
}

const xpose = x => x[0].map( (c,i) => x.map( r => r[i] ) )

function avgArr(a) {
    a = a.filter(el => el !== null);
    return a.reduce((x,y) => x+y) / a.length;
}

function calcAverages(data) {
    let i = 1 + Math.max.apply(null, data.map( el => el.id )) // max id
    let regions = [...new Set (data.map (a => a.region))]  // unique regions

    regions.forEach( region => {
        data.push( { id: i++,
                     region: region,
                     country: "avg",
                     values: xpose(getArr(data,region)).map(a => avgArr(a))})})

    return data
}

// > calcAverages(data)
//
// ...
//  { id: 8,
//    region: 'America',
//    country: 'avg',
//    values: [ 2, 3, 4, 5 ] },
//  { id: 9,
//    region: 'Europe',
//    country: 'avg',
//    values: [ 2.8, 4.6, 3, 6.4 ] } ]

答案 5 :(得分:0)

我想做如下



var data = [
    {id: 1, region: "America", country:"USA", values:[1,2,3,4] },
    {id: 2, region: "America", country:"Canada", values:[3,4,5,6] },
    {id: 3, region: "Europe", country:"France", values:[1,2,3,4] },
    {id: 4, region: "Europe", country:"Italy", values:[1,2,3,4] },
    {id: 5, region: "Europe", country:"Spain", values:[5,9,1,7] },
    {id: 6, region: "Europe", country:"Germany", values:[1,6,2,8] },
    {id: 7, region: "Europe", country:"Ireland", values:[6,4,6,9]}
      ],
 newData = [...new Set(data.map(c => c.region))]
           .reduce((f,c,i) => (f.push(...data.filter(o => o.region === c)
                                             .reduce((p,q,_,a) => { p[0].values = q.values.map((v,j) => v/a.length + p[0].values[j]);
                                                                    p.push(q);
                                                                    return p;
                                                                  } ,[{     id: data.length + 1 + i,
                                                                        region: c,
                                                                       country: "avg",
                                                                        values: Array(data[0].values.length).fill(0)
                                                                      }])),f),[]);
console.log(newData);