Reputation: 317
I'm trying to add all elements in a list of lists by index and average them:
List<List<double>> data = [[1.0, 2.0, 3.0], [2.0, 3.0, 5.0], [8.0, 7.0, 2.0]]
The result should be a list of doubles each summed up and then divided by the total lengths of above data:
[11.0, 12.0, 10.0] // divide these by 3
[3.67, 4.0, 3.33] // should be the result
What is the best way to do this? This is taking too long in my flutter app (actual data list contains 60 lists of 2000 doubles each) and yes, I am also asserting the length of each list is equal. and is not empty or having unexpected data types.
Upvotes: 2
Views: 2980
Reputation: 621
Let me suggest more compact solution that uses 'collection' package:
import 'package:collection/collection.dart';
...
List<List<double>> data = [[1.0, 2.0, 3.0], [2.0, 3.0, 5.0], [8.0, 7.0, 2.0]]
var result = <double>[];
final innerListSize = data[0].length;
for (int i = 0; i < innerListSize; i++) {
result.add(data.map((e) => e[i]).average);
}
Upvotes: 0
Reputation: 6229
My contribution for a Matrix NxM, I've modified your data for a more generic solution:
extension chesu<T> on List<List<T>> {
List<List<T>> transpose() {
final rowNum = this.length;
final colNum = this[0].length;
var list = List<List<T>>.generate(colNum, (i) => List<T>());
if (rowNum == 0 || colNum == 0) return null;
for (var r = 0; r < rowNum; r++)
for (var c = 0; c < colNum; c++) list[c].add(this[r][c]);
return list;
}
}
main(List<String> args) {
final data = [[1.0, 2.0, 3.0, 4.0], [2.0, 3.0, 5.0, 8.0], [6.0, 7.0, 7.0, 9.0]];
print(data.transpose().map((e) => e.reduce((v, e) => v+e)).map((e) => e/3));
}
Result:
(3.0, 4.0, 5.0, 7.0)
There is a new package named scidart that contains interesting functions like transpose
, it tries to emulate numpy
from Python.
UPDATE 1: Working with Big Data
UPDATE 2: I made a fool mistake with sum()
, extension
is fixed now, sorry.
import 'dart:math';
extension on List<List<double>> {
List<List<double>> transpose() {
final rowNum = this.length;
final colNum = this[0].length;
var list = List<List<double>>.generate(colNum, (i) => List<double>());
if (rowNum == 0 || colNum == 0) return null;
for (var r = 0; r < rowNum; r++)
for (var c = 0; c < colNum; c++) list[c].add(this[r][c]);
return list;
}
List<double> sum() => this.map((l) => l.reduce((v, e) => v+e)).toList();
}
class Stats {
List<List<double>> _data;
int _rows, _cols;
List<double> _sum, _avg;
Stats(List<List<double>> data) {
_data = data.transpose();
_rows = _data.length;
_cols = _data[0].length;
}
int get rows => _rows;
int get cols => _cols;
List<double> get sum {
if ([0, null].contains(_sum)) _sum = _data.sum();
return _sum;
}
List<double> get avg {
if ([0, null].contains(_avg)) _avg = sum.map((e) => e / _cols).toList();
return _avg;
}
}
main(List<String> args) {
// final data = [[1.0, 2.0, 3.0, 4.0], [2.0, 3.0, 5.0, 8.0], [6.0, 7.0, 7.0, 9.0]];
// final data = [[1.0, 2.0, 3.0], [2.0, 3.0, 5.0], [8.0, 7.0, 2.0]];
final data = List<List<double>>.generate(60, (i) => List<double>.generate(2000, (j) => Random().nextDouble()*30));
print('Creating big data...');
final stats = Stats(data);
final stopwatch = Stopwatch()..start();
print('Calculating statistics...');
print('Sum: ${stats.sum.take(5)}');
print('Average: ${stats.avg.take(5)}');
stopwatch.stop();
print('\nJob finished at ${stopwatch.elapsedMilliseconds/1000} seconds.');
}
Result:
Creating big data...
Calculating statistics...
Sum: (928.8075263386316, 934.3418807027017, 815.2172548417801, 833.6855783984151, 828.1013228547513)
Average: (15.480125438977193, 15.572364678378362, 13.586954247363002, 13.894759639973586, 13.801688714245854)
Job finished at 0.024 seconds.
Upvotes: 2
Reputation: 3273
Well I really don't know about the speed you need.
Try this method to see if it works for your dataset. Maybe your method is faster!
void main() {
List<List<double>> data = [[1.0, 2.0, 3.0], [2.0, 3.0, 5.0], [8.0, 7.0, 2.0]];
List<double> dataSum = List<double>();
// original data
print(data);
for (var item in data){
dataSum.add(item.fold(0, (p, c) => p + c) / item.length);
}
// output
print(dataSum);
}
Important:
Anyways, if you do a large task in time, you can use a async
function with a Future
to prevent the application to hang and not suffer because of the long wait.
UPDATE: After OP's comment
This code should give you the result you are expecting.
void main() {
List<List<double>> data = [[1.0, 2.0, 3.0], [2.0, 3.0, 5.0], [8.0, 7.0, 2.0]];
int numLists = data.length;
int numElements = data[0].length;
double sum;
List<double> dataSum = List<double>();
for(var i = 0; i < numElements; i++ ) {
sum = 0.0;
for(var j = 0; j < numLists; j++ ) {
sum += data[j][i]; //inverted indexes
}
dataSum.add(sum/numLists);
}
// output
print(dataSum);
}
Upvotes: 3
Reputation: 3524
For operations on list you have to iterate over it's items (for, map, forEach ...etc) which would take time (depending on the list length) so I guess you have to do some benchmarking.
Try this
List<double> sum = [];
List<double> average = [];
sum = data.reduce((a, b) {
List<double> list = [];
for (int i = 0; i < b.length; i++) {
list.add(a[i] + b[i]);
}
return list;
});
average = sum.map((e) => e / 3).toList();
print(sum);
print(average);
Upvotes: 2