我想使用 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 程序员,如果您对此有任何帮助,我将不胜感激。
不使用
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
。这也是可选的,但我认为这是有意义的,因为时间尤其是在您的情况下是一个数值。
另请参阅:
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);
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);
无需将数组转换为对象,然后再转换回数组,您可以在迭代数组时直接计算平均值。具体方法如下:
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”来跟踪每个时间戳的值的总和和计数。它迭代每个数组,使用总和和计数更新映射,然后根据累积的总和和计数计算平均值。最后,它将地图转换回所需的平均数组格式。
这里有一个有趣的案例。看来这正是 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 ] ]