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:
Mark Liversedge
2020-03-15 11:38:30 +00:00
parent 48fbdbd542
commit 7f81ac76a9
3 changed files with 72 additions and 4 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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") {