mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Datafilter vectors - lr()
.. lr(xdata,ydata) - perform a linear regression returning the results in a vector of 4 values [ slope, intercept, r2, see ]. .. as a by-product the slope on generic plots now show r2 and see as they are being calculated by GenericCalculator.
This commit is contained in:
@@ -227,7 +227,8 @@ void GenericSelectTool::paint(QPainter*painter, const QStyleOptionGraphicsItem *
|
||||
// slope calcs way over the top for a line chart
|
||||
// where there are multiple series being plotted
|
||||
if (host->charttype == GC_CHART_SCATTER) {
|
||||
QString lr=QString("y = %1 x + %2").arg(calc.m).arg(calc.b);
|
||||
QString lr=QString("y = %1 x + %2 \nr2=%3, see=%4").arg(calc.m,0,'f',3).arg(calc.b, 0,'f', 3)
|
||||
.arg(calc.r2,0,'f',3).arg(calc.see,0,'f',3);
|
||||
QPen line(calc.color);
|
||||
painter->setPen(line);
|
||||
painter->drawText(QPointF(0,0), lr);
|
||||
@@ -786,7 +787,7 @@ void
|
||||
GenericCalculator::initialise()
|
||||
{
|
||||
count=0;
|
||||
m = b = lrsumxy = lrsumx2 = x.lrsum = y.lrsum =
|
||||
m = b = lrsumxy = lrsumx2 = x.lrsum = y.lrsum = r2 = see =
|
||||
x.max = x.min = x.sum = x.mean =
|
||||
y.max = y.min = y.sum = y.mean = 0;
|
||||
xaxis=yaxis=NULL;
|
||||
@@ -837,16 +838,37 @@ GenericCalculator::addPoint(QPointF point)
|
||||
lrsumxy += lr.x() * lr.y();
|
||||
x.lrsum += lr.x();
|
||||
y.lrsum += lr.y();
|
||||
|
||||
// keep a record
|
||||
actual << lr;
|
||||
}
|
||||
|
||||
void
|
||||
GenericCalculator::finalise()
|
||||
{
|
||||
// add calcs for stuff cannot do on the fly
|
||||
if (count >=2) {
|
||||
if (actual.count() >=2) {
|
||||
m = (count * lrsumxy - x.lrsum * y.lrsum) / (count * lrsumx2 - (x.lrsum * x.lrsum));
|
||||
b = (y.lrsum - m * x.lrsum) / count;
|
||||
|
||||
// r2
|
||||
double sumActualErrors=0, sumPredictErrors=0;
|
||||
see = 0;
|
||||
for(int i=0; i<actual.count(); i++) {
|
||||
sumActualErrors += pow(actual[i].y() - y.mean, 2);
|
||||
sumPredictErrors += pow(((actual[i].x()*m)+b) - y.mean, 2);
|
||||
see += pow(((actual[i].x() * m) + b) - actual[i].y(), 2);
|
||||
}
|
||||
r2 = sumPredictErrors / sumActualErrors;
|
||||
|
||||
if (actual.count() >2) see = sqrt(see/(actual.count()-2));
|
||||
else see=0;
|
||||
|
||||
} else {
|
||||
m = b = r2 = see = 0;
|
||||
}
|
||||
|
||||
actual.clear();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -58,7 +58,7 @@ class GenericCalculator
|
||||
void finalise();
|
||||
|
||||
int count;
|
||||
double m,b,r2; // r2 and y=mx +b
|
||||
double m,b,r2,see; // r2 and y=mx +b
|
||||
double lrsumx2, lrsumxy; // used to resolve m and b
|
||||
struct {
|
||||
double max, min, sum, mean;
|
||||
@@ -66,6 +66,8 @@ class GenericCalculator
|
||||
} x,y;
|
||||
QColor color; // for paint
|
||||
|
||||
QVector<QPointF> actual;// keep track of data for finalise to use
|
||||
|
||||
// what were we calculated on? (so paint can transform)
|
||||
QAbstractAxis *xaxis,*yaxis;
|
||||
QAbstractSeries *series;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "LTMSettings.h"
|
||||
#include "VDOTCalculator.h"
|
||||
#include "DataProcessor.h"
|
||||
#include "GenericSelectTool.h" // for generic calculator
|
||||
#include <QDebug>
|
||||
#include <QMutex>
|
||||
|
||||
@@ -165,6 +166,7 @@ static struct {
|
||||
|
||||
{ "sapply", 2 }, // sapply(vector, expr) - returns a vector where expr has been applied to every element. x and i
|
||||
// are both available in the expr for element value and index position.
|
||||
{ "lr", 2 }, // lr(xlist, ylist) - linear regression on x,y co-ords returns vector [slope, intercept, r2, see]
|
||||
|
||||
// add new ones above this line
|
||||
{ "", -1 }
|
||||
@@ -289,6 +291,10 @@ DataFilter::builtins()
|
||||
// sapply
|
||||
returning << "sapply(list, expr)";
|
||||
|
||||
} else if (i == 61) {
|
||||
|
||||
returning << "lr(xlist, ylist)";
|
||||
|
||||
} else {
|
||||
|
||||
QString function;
|
||||
@@ -1490,6 +1496,17 @@ void Leaf::validateFilter(Context *context, DataFilterRuntime *df, Leaf *leaf)
|
||||
}
|
||||
}
|
||||
|
||||
} else if (leaf->function == "lr") {
|
||||
|
||||
if (leaf->fparms.count() != 2) {
|
||||
leaf->inerror = true;
|
||||
DataFiltererrors << QString(tr("lr(x, y), need x and y vectors."));
|
||||
|
||||
} else {
|
||||
validateFilter(context, df, leaf->fparms[0]);
|
||||
validateFilter(context, df, leaf->fparms[1]);
|
||||
}
|
||||
|
||||
} else if (leaf->function == "sapply") {
|
||||
|
||||
if (leaf->fparms.count() != 2) {
|
||||
@@ -2665,6 +2682,33 @@ Result Leaf::eval(DataFilterRuntime *df, Leaf *leaf, float x, long it, RideItem
|
||||
return returning;
|
||||
}
|
||||
|
||||
// linear regression
|
||||
if (leaf->function == "lr") {
|
||||
Result returning(0);
|
||||
returning.vector << 0 << 0 << 0 << 0; // set slope, intercept, r2 and see to 0
|
||||
|
||||
Result xv = eval(df,leaf->fparms[0],x, it, m, p, c, s, d);
|
||||
Result yv = eval(df,leaf->fparms[1],x, it, m, p, c, s, d);
|
||||
|
||||
// check ok to proceed
|
||||
if (xv.vector.count() < 2 || xv.vector.count() != yv.vector.count()) return returning;
|
||||
|
||||
// use the generic calculator, its quick and easy
|
||||
GenericCalculator calc;
|
||||
calc.initialise();
|
||||
for (int i=0; i< xv.vector.count(); i++)
|
||||
calc.addPoint(QPointF(xv.vector[i], yv.vector[i]));
|
||||
calc.finalise();
|
||||
|
||||
// extract LR results
|
||||
returning.vector[0]=calc.m;
|
||||
returning.vector[1]=calc.b;
|
||||
returning.vector[2]=calc.r2;
|
||||
returning.vector[3]=calc.see;
|
||||
|
||||
return returning;
|
||||
}
|
||||
|
||||
// pmc
|
||||
if (leaf->function == "pmc") {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user