Statistics<N extends num>.compute constructor

Statistics<N extends num>.compute(
  1. Iterable<N> data, {
  2. bool alreadySortedData = false,
  3. bool computeLowerAndUpper = true,
  4. bool keepData = false,
  5. bool useBigIntToCompute = false,
})

Computes a Statistics summary from data.

  • alreadySortedData if true will avoid sorting of data. This allows some usage optimization, do not pass an inconsistent value.
  • computeLowerAndUpper if true will compute lowerStatistics and upperStatistics.
  • keepData if true will keep a copy of data at data.
  • useBigIntToCompute if true will force use of BigInt for internal computation to avoid overflow.

Implementation

factory Statistics.compute(Iterable<N> data,
    {bool alreadySortedData = false,
    bool computeLowerAndUpper = true,
    bool keepData = false,
    bool useBigIntToCompute = false}) {
  var length = data.length;
  if (length == 0) {
    var statistics = Statistics._empty(data);
    if (keepData) {
      statistics.data = data.toList();
    }
    return statistics;
  }

  if (length == 1) {
    var statistics = Statistics._single(data.first);
    if (keepData) {
      statistics.data = data.toList();
    }
    return statistics;
  }

  var listSorted = List<N>.from(data);
  if (!alreadySortedData) {
    listSorted.sort();
  }

  var first = listSorted.first;
  var min = first;
  var max = listSorted.last;

  var evenSet = length % 2 == 0;
  var medianHighIndex = length ~/ 2;
  var medianHigh = listSorted[medianHighIndex];
  var medianLow = evenSet ? listSorted[medianHighIndex - 1] : medianHigh;

  if (alreadySortedData) {
    if (min > max || medianLow > medianHigh) {
      throw ArgumentError(
          "Inconsistent argument 'alreadySortedData': min:$min > max:$max ; medianLow:$medianLow > medianHigh:$medianHigh");
    }
  }

  num sum;
  num squaresSum;
  BigInt sumBigInt;
  BigInt squaresSumBigInt;

  double mean;
  double standardDeviation;

  if (useBigIntToCompute || max > (maxSafeIntSqrt ~/ length)) {
    var firstBigInt = first.toBigInt();
    sumBigInt = firstBigInt;
    squaresSumBigInt = firstBigInt * firstBigInt;

    for (var i = 1; i < length; ++i) {
      var n = listSorted[i];
      var nBigInt = n.toBigInt();
      sumBigInt += nBigInt;
      squaresSumBigInt += nBigInt * nBigInt;
    }

    sum = sumBigInt.toInt();
    squaresSum = squaresSumBigInt.toInt();

    var lengthBigInt = length.toBigInt();

    mean = sumBigInt / lengthBigInt;

    standardDeviation = math.sqrt(
            ((squaresSumBigInt * lengthBigInt) - (sumBigInt * sumBigInt)) /
                lengthBigInt) /
        math.sqrt(length);
  } else {
    sum = first;
    squaresSum = first * first;

    for (var i = 1; i < length; ++i) {
      var n = listSorted[i];
      sum += n;
      squaresSum += n * n;
    }

    sumBigInt = sum.toBigInt();
    squaresSumBigInt = squaresSum.toBigInt();

    mean = sum / length;

    standardDeviation =
        math.sqrt((squaresSum - (sum * (sum / length))) / length);
  }

  Statistics<N>? lowerStatistics;
  Statistics<N>? upperStatistics;

  if (computeLowerAndUpper) {
    List<N> lower;
    List<N> upper;

    if (evenSet) {
      lower = listSorted.sublist(0, medianHighIndex);
      upper = listSorted.sublist(medianHighIndex);
    } else {
      lower = listSorted.sublist(0, medianHighIndex + 1);
      upper = listSorted.sublist(medianHighIndex);
    }

    lowerStatistics = Statistics.compute(lower,
        computeLowerAndUpper: false, keepData: false);
    upperStatistics = Statistics.compute(upper,
        computeLowerAndUpper: false, keepData: false);
  }

  var statistics = Statistics<N>(
    length,
    min,
    max,
    medianLow: medianLow,
    medianHigh: medianHigh,
    sum: sum,
    squaresSum: squaresSum,
    sumBigInt: sumBigInt,
    squaresSumBigInt: squaresSumBigInt,
    mean: mean,
    standardDeviation: standardDeviation,
    lowerStatistics: lowerStatistics,
    upperStatistics: upperStatistics,
  );

  if (keepData) {
    statistics.data = data.toList();
  }

  return statistics;
}