Javascript 中给定相同时间戳的不同长度的平均时间序列数组

问题描述 投票:0回答:5

我想使用 Javascript 以有效的方式对多个不同长度的时间序列数组进行平均。

  • 长度不同的原因是某些数组中缺少某些数据。
  • 时间应该用作所有数组中的公共变量来求平均值,即对给定时间戳的可用值进行平均。

例如:

array1 = [[1, 100], [2, 200], [3, 300], [4, 400]];
array2 = [[1, 150], [3, 350], [4, 450]];
array3 = [[1, 250], [2, 350], [4, 450]];

allArrays = [array1, array2, array3];

// This is the result I would like to get, i.e. average of same timestamp 1, 2 .... n
average = [[1, 166.7], [2, 275], [3, 325], [4, 433]];

其中每对都是

[time, value]
。时间是 Unix 格式的数字。

我的想法是使用 Lodash 首先将这些对转换为对象,然后对所有相等的时间戳进行分组并对值进行平均。然后转换回数组。

// Convert arrays to objects
for (int i = 0; allArrays.length; i++){
var array = allArrays[i];
allArrays[i] = toObject(array, ['time', 'value']);
}

// Group based on similar timestamp and average
???

// Convert averaged object to array
???

// Functions
function toObject(array, names){
  var obj = _.chain(array).map(function(p) {
    return _.zipObject(names, p)
}).value()

我不是一名 Javascript 程序员,如果您对此有任何帮助,我将不胜感激。

javascript arrays object time-series lodash
5个回答
2
投票

不使用

lodash
,您可以使用以下内容:

const array1 = [
  [1, 100],
  [2, 200],
  [3, 300],
  [4, 400],
];
const array2 = [
  [1, 150],
  [3, 350],
  [4, 450],
];
const array3 = [
  [1, 250],
  [2, 350],
  [4, 450],
];

const groupedByTime = [...array1, ...array2, ...array3].reduce(
  (acc, [time, value]) => ((acc[time] ??= []).push(value), acc),
  {}
);
console.log("Grouped by time:");
console.log(JSON.stringify(groupedByTime, null, 4));
const result = Object.entries(groupedByTime).map(([time, values]) => [Number(time), (values.reduce((total, value) => (total += value), 0) / values.length).toFixed(1)]);
console.log("Result:");
console.log(JSON.stringify(result, null, 4));
/* Stackoverflow: show only console */
.as-console-wrapper {
  max-height: 100% !important;
  top: 0;
}

总体思路

首先您想按时间分组,然后为每个分组计算平均值并将其呈现为所需的形式。如果需要,您可以使用

toFixed()
对平均值进行四舍五入。

更详细的解释

首先,您想使用 spread 语法

[...array1, ...array2, ...array3]
创建一个包含所有值的平面数组。然后使用
reduce()
将属于同一时间的所有值分组在一起。我在这里使用一个 JavaScript 对象,然后
push()
当我遇到它们时将每个值放入组中。

现在是将创建的 JavaScript 对象的键和值(= 时间和该时间的值)(我们通过使用

Object.entries()
获得)映射到预期结果结构(即一个数组)。但我们还需要首先计算平均值。同样,我们可以使用
reduce()
来对组总数求和,然后除以组中的元素数量。如果需要,我们可以使用
toFixed()
对结果进行四舍五入。通过使用 Number 构造函数
Number()
,我确保 JavaScript 将时间视为
number
。这也是可选的,但我认为这是有意义的,因为时间尤其是在您的情况下是一个数值。

另请参阅:


1
投票

const array1 = [[1, 100], [2, 200], [3, 300], [4, 400]];
const array2 = [[1, 150], [3, 350], [4, 450]];
const array3 = [[1, 250], [2, 350], [4, 450]];

const allArrays = [array1, array2, array3];

// get largest id
const last = Math.max(...allArrays.map(arr => arr[arr.length-1][0]));
// convert arrays to objects so we can access values by id
const dicts = allArrays.map(arr => Object.fromEntries(arr));

const results = []
for (let i = 1; i <= last; i++) {
  let [count, sum] = dicts.reduce(
    (r,v) => (v[i] === undefined ? r : [r[0]+1, r[1] + v[i]])
    , [0,0]);
  results.push([i, sum / count]);
}

console.log(results);


1
投票

这是实现这一目标的逐步过程,最初您可以在

Spread Syntax
的帮助下将所有这些单独的数组组合成一个数组,然后迭代它以使用
timestamp
键和数组创建一个对象作为一个值,然后最终通过基于
keys
迭代对象来计算平均值。

现场演示

const array1 = [[1, 100], [2, 200], [3, 300], [4, 400]];
const array2 = [[1, 150], [3, 350], [4, 450]];
const array3 = [[1, 250], [2, 350], [4, 450]];

const allArrays = [...array1, ...array2, ...array3];

const obj = {};
const average = [];

allArrays.forEach(([timestamp, value]) => {
  if (!obj[timestamp]) obj[timestamp] = []
  obj[timestamp].push(value)
});

Object.keys(obj).forEach(key => {
  const avg = calculateAverage(obj[key])
  average.push([Number(key), avg]);
})

function calculateAverage(array) {
  const sum = array.reduce((acc, value) => acc + value, 0);
  const average = sum / array.length;
  return average;
}

console.log(average);


1
投票

无需将数组转换为对象,然后再转换回数组,您可以在迭代数组时直接计算平均值。具体方法如下:

const array1 = [[1, 100], [2, 200], [3, 300], [4, 400]];
const array2 = [[1, 150], [3, 350], [4, 450]];
const array3 = [[1, 250], [2, 350], [4, 450]];

const allArrays = [array1, array2, array3];

const averageMap = new Map();

// Iterate through all arrays
for (const array of allArrays) {
  for (const [time, value] of array) {
    if (!averageMap.has(time)) {
      averageMap.set(time, { sum: value, count: 1 });
    } else {
      const existing = averageMap.get(time);
      averageMap.set(time, {
        sum: existing.sum + value,
        count: existing.count + 1,
      });
    }
  }
}

// Calculate averages and convert to array
const average = Array.from(averageMap.entries()).map(([time, data]) => [
  time,
  data.sum / data.count,
]);

console.log(average);

此代码片段将为您提供所需的平均数组:

[
  [1, 166.66666666666666],
  [2, 275],
  [3, 325],
  [4, 433.3333333333333]
]

代码使用“Map”来跟踪每个时间戳的值的总和和计数。它迭代每个数组,使用总和和计数更新映射,然后根据累积的总和和计数计算平均值。最后,它将地图转换回所需的平均数组格式。


0
投票

这里有一个有趣的案例。看来这正是 JS Map 的工作。

看这里:

const array1 = [[1, 2], [2, 2]];
const array2 = [[3, 3], [1, 4]];
const resultMap = new Map();

// here we flatten arrays and iterate through the  resulting array
[...array1, ...array2].forEach(([time, value]) => {
   // and here we set the average value
   resultMap.set(time, resultMap.has(time) ? (resultMap.get(time) + value) / 2 : value);
});

console.log([ ...resultMap.entries() ]); // [ [ 1, 3 ], [ 2, 2 ], [ 3, 3 ] ]
© www.soinside.com 2019 - 2024. All rights reserved.