RealRK
RealRK

Reputation: 317

Dart sum list of lists by element

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

Answers (4)

JustAnotherCoder
JustAnotherCoder

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

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

encubos
encubos

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

Mohammed Alfateh
Mohammed Alfateh

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

Related Questions