App-Chart
view release on metacpan or search on metacpan
lib/App/Chart/Series/Calculation.pm view on Meta::CPAN
# add new
$total += $value;
$total_squares += $value * $value;
return ($total / $count,
sqrt(max (0, $total_squares*$count - $total*$total)) / $count);
};
}
sub sum {
my ($class, $N) = @_;
my $delay_proc = $class->delay ($N);
my $total = 0;
return sub {
my ($value) = @_;
# drop old
my $old = $delay_proc->($value);
if (defined $old) {
$total -= $old;
}
# add new
$total += $value;
return $total;
};
}
sub ma_proc_by_weights {
my @weights = @_;
# $a[0] is the newest point, $a[1] the prev, through to $a[$N-1]
my @a;
my $total_weight;
return sub {
my ($value) = @_;
unshift @a, $value; # add new
# keep last $N points
if (@a > @weights) {
pop @a; # drop old
} else {
# new total weight for bigger @a
$total_weight = List::Util::sum (map {$weights[$_]} 0 .. $#a);
}
if ($total_weight == 0) {
return 0;
}
return (List::Util::sum (map {$a[$_] * $weights[$_]} 0 .. $#a)
/ $total_weight);
};
}
#------------------------------------------------------------------------------
# http://mathworld.wolfram.com/LeastSquaresFitting.html
# Least squares generally, including deriving formula using
# derivative==0 as follows:
#
# The sum of squares is
#
# R^2(a,b) = Sum (y[i] - (a + b*x[i]))^2
#
# Partial derivative with b is
#
# d R^2(a,b)
# ---------- = Sum 2 * (y[i]-b*x[i]) * -x[i]
# db
#
# And want that to be zero at the minimum, so
#
# Sum -2*x[i]*y[i] + 2*b*Sum x[i]^2 = 0
#
# Sum x[i]*y[i]
# b = -------------
# Sum x[i]^2
#
# Return a procedure which calculates a linear regression line fit over an
# accumulated window of $N values.
#
# Each call $proc->($y) enters a new value into the window, and the return
# is two values ($a, $b) where the line is $a+$b*X. The last point entered
# is at X=0 and the preceding ones at X=-1, X=-2, etc. A and B are
# #f if not enough data yet.
#
# To prime the window initially, call $proc with $N-1 many points preceding
# the first desired.
#
sub linreg {
my ($class, $N) = @_;
# The X values used are centred around 0,
# $count=4: -1.5, -0.5, 0.5, 1.5
# $count=5: -2, -1, 0, 1, 2
# $count=6: -2.5, -1.5, -0.5, 0.5, 1.5, 2.5
# etc
# But the return is then adjusted $a is based on the last point as X=0
#
# @array,$pos is a circular list of $count many values. The one at
# @array[$pos] is the oldest and is replaced by a new value to cycle that
# in.
#
# $count is how many points are in @array.
#
# $y_total is the total of the past $count many Y values, ie. the values
# in @array.
#
# $xy2_total is the sum of 2*X*Y for each Y value in @array.
#
# $xx2_total is 2 * the sum of X*X for each X value used. This is a
# constant once $count stops at COUNT. A minimum 1 is enforced for the
# degenerate case of $N==0 (there's no slope to in that case but at least
# avoid a divide by zero).
#
my @array;
my $pos = $N - 1; # initial extends
( run in 0.844 second using v1.01-cache-2.11-cpan-df04353d9ac )