[apps/regression] Power regression uses logarithm of series

This matches other apps results and we directly compute the values from
the data instead of doing a gradient descent.
This commit is contained in:
Léa Saviot
2020-01-15 11:53:12 +01:00
parent 97b495a4dc
commit f6c15198bc
8 changed files with 60 additions and 38 deletions

View File

@@ -176,8 +176,8 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int
const int numberSignificantDigits = Preferences::LargeNumberOfSignificantDigits;
if (i > 0 && j > 0 && j <= k_totalNumberOfDoubleBufferRows) {
ArgCalculPointer calculationMethods[k_totalNumberOfDoubleBufferRows] = {&Store::meanOfColumn, &Store::sumOfColumn, &Store::squaredValueSumOfColumn, &Store::standardDeviationOfColumn, &Store::varianceOfColumn};
double calculation1 = (m_store->*calculationMethods[j-1])(seriesNumber, 0);
double calculation2 = (m_store->*calculationMethods[j-1])(seriesNumber, 1);
double calculation1 = (m_store->*calculationMethods[j-1])(seriesNumber, 0, false);
double calculation2 = (m_store->*calculationMethods[j-1])(seriesNumber, 1, false);
EvenOddDoubleBufferTextCellWithSeparator * myCell = (EvenOddDoubleBufferTextCellWithSeparator *)cell;
constexpr int bufferSize = PrintFloat::charSizeForFloatsWithPrecision(numberSignificantDigits);
char buffer[bufferSize];
@@ -196,8 +196,16 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int
}
if (i > 0 && j > k_totalNumberOfDoubleBufferRows && j < k_regressionCellIndex) {
assert(j != k_regressionCellIndex);
CalculPointer calculationMethods[] = {&Store::doubleCastedNumberOfPairsOfSeries, &Store::covariance, &Store::columnProductSum};
double calculation = (m_store->*calculationMethods[j-k_totalNumberOfDoubleBufferRows-1])(seriesNumber);
double calculation = 0;
const int calculationIndex = j-k_totalNumberOfDoubleBufferRows-1;
if (calculationIndex == 0) {
calculation = m_store->doubleCastedNumberOfPairsOfSeries(seriesNumber);
} else if (calculationIndex == 1) {
calculation = m_store->covariance(seriesNumber);
} else {
assert(calculationIndex == 2);
calculation = m_store->columnProductSum(seriesNumber);
}
constexpr int bufferSize = PrintFloat::charSizeForFloatsWithPrecision(numberSignificantDigits);
char buffer[bufferSize];
PoincareHelpers::ConvertFloatToText<double>(calculation, buffer, bufferSize, numberSignificantDigits);
@@ -228,8 +236,8 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int
if (j > k_regressionCellIndex + maxNumberCoefficients) {
// Fill r and r2 if needed
if (modelType == Model::Type::Linear) {
CalculPointer calculationMethods[2] = {&Store::correlationCoefficient, &Store::squaredCorrelationCoefficient};
double calculation = (m_store->*calculationMethods[j - k_regressionCellIndex - maxNumberCoefficients - 1])(seriesNumber);
const int calculationIndex = j - k_regressionCellIndex - maxNumberCoefficients - 1;
double calculation = calculationIndex == 0 ? m_store->correlationCoefficient(seriesNumber) : m_store->squaredCorrelationCoefficient(seriesNumber);
constexpr int bufferSize = PrintFloat::charSizeForFloatsWithPrecision(numberSignificantDigits);
char buffer[bufferSize];
PoincareHelpers::ConvertFloatToText<double>(calculation, buffer, bufferSize, numberSignificantDigits);

View File

@@ -12,7 +12,7 @@ public:
I18n::Message formulaMessage() const override { return I18n::Message::LinearRegressionFormula; }
double evaluate(double * modelCoefficients, double x) const override;
double levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) override;
virtual void fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) override;
void fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) override;
double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override;
int numberOfCoefficients() const override { return 2; }
int bannerLinesCount() const override { return 3; }

View File

@@ -61,6 +61,12 @@ double PowerModel::partialDerivate(double * modelCoefficients, int derivateCoeff
return 0.0;
}
void PowerModel::fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) {
/* Y1 = aX1^b => ln(Y1) = ln(a) + b*ln(X1)*/
modelCoefficients[0] = exp(store->yIntercept(series, true));
modelCoefficients[1] = store->slope(series, true);
}
bool PowerModel::dataSuitableForFit(Store * store, int series) const {
if (!Model::dataSuitableForFit(store, series)) {
return false;

View File

@@ -13,10 +13,11 @@ public:
double evaluate(double * modelCoefficients, double x) const override;
double levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) override;
double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override;
void fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) override;
int numberOfCoefficients() const override { return 2; }
int bannerLinesCount() const override { return 2; }
protected:
virtual bool dataSuitableForFit(Store * store, int series) const override;
bool dataSuitableForFit(Store * store, int series) const override;
};
}

View File

@@ -204,45 +204,53 @@ float Store::minValueOfColumn(int series, int i) const {
return minColumn;
}
double Store::squaredValueSumOfColumn(int series, int i) const {
double Store::squaredValueSumOfColumn(int series, int i, bool lnOfSeries) const {
double result = 0;
for (int k = 0; k < numberOfPairsOfSeries(series); k++) {
result += m_data[series][i][k]*m_data[series][i][k];
if (lnOfSeries) {
result += log(m_data[series][i][k]) * log(m_data[series][i][k]);
} else {
result += m_data[series][i][k]*m_data[series][i][k];
}
}
return result;
}
double Store::columnProductSum(int series) const {
double Store::columnProductSum(int series, bool lnOfSeries) const {
double result = 0;
for (int k = 0; k < numberOfPairsOfSeries(series); k++) {
result += m_data[series][0][k]*m_data[series][1][k];
if (lnOfSeries) {
result += log(m_data[series][0][k]) * log(m_data[series][1][k]);
} else {
result += m_data[series][0][k] * m_data[series][1][k];
}
}
return result;
}
double Store::meanOfColumn(int series, int i) const {
return numberOfPairsOfSeries(series) == 0 ? 0 : sumOfColumn(series, i)/numberOfPairsOfSeries(series);
double Store::meanOfColumn(int series, int i, bool lnOfSeries) const {
return numberOfPairsOfSeries(series) == 0 ? 0 : sumOfColumn(series, i, lnOfSeries)/numberOfPairsOfSeries(series);
}
double Store::varianceOfColumn(int series, int i) const {
double mean = meanOfColumn(series, i);
return squaredValueSumOfColumn(series, i)/numberOfPairsOfSeries(series) - mean*mean;
double Store::varianceOfColumn(int series, int i, bool lnOfSeries) const {
double mean = meanOfColumn(series, i, lnOfSeries);
return squaredValueSumOfColumn(series, i, lnOfSeries)/numberOfPairsOfSeries(series) - mean*mean;
}
double Store::standardDeviationOfColumn(int series, int i) const {
return std::sqrt(varianceOfColumn(series, i));
double Store::standardDeviationOfColumn(int series, int i, bool lnOfSeries) const {
return std::sqrt(varianceOfColumn(series, i, lnOfSeries));
}
double Store::covariance(int series) const {
return columnProductSum(series)/numberOfPairsOfSeries(series) - meanOfColumn(series, 0)*meanOfColumn(series, 1);
double Store::covariance(int series, bool lnOfSeries) const {
return columnProductSum(series, lnOfSeries)/numberOfPairsOfSeries(series) - meanOfColumn(series, 0, lnOfSeries)*meanOfColumn(series, 1, lnOfSeries);
}
double Store::slope(int series) const {
return LinearModelHelper::Slope(covariance(series), varianceOfColumn(series, 0));
double Store::slope(int series, bool lnOfSeries) const {
return LinearModelHelper::Slope(covariance(series, lnOfSeries), varianceOfColumn(series, 0, lnOfSeries));
}
double Store::yIntercept(int series) const {
return LinearModelHelper::YIntercept(meanOfColumn(series, 1), meanOfColumn(series, 0), slope(series));
double Store::yIntercept(int series, bool lnOfSeries) const {
return LinearModelHelper::YIntercept(meanOfColumn(series, 1, lnOfSeries), meanOfColumn(series, 0, lnOfSeries), slope(series, lnOfSeries));
}
double Store::yValueForXValue(int series, double x, Poincare::Context * globalContext) {

View File

@@ -56,14 +56,14 @@ public:
// Calculation
double * coefficientsForSeries(int series, Poincare::Context * globalContext);
double doubleCastedNumberOfPairsOfSeries(int series) const;
double squaredValueSumOfColumn(int series, int i) const;
double columnProductSum(int series) const;
double meanOfColumn(int series, int i) const;
double varianceOfColumn(int series, int i) const;
double standardDeviationOfColumn(int series, int i) const;
double covariance(int series) const;
double slope(int series) const;
double yIntercept(int series) const;
double squaredValueSumOfColumn(int series, int i, bool lnOfSeries = false) const;
double columnProductSum(int series, bool lnOfSeries = false) const;
double meanOfColumn(int series, int i, bool lnOfSeries = false) const;
double varianceOfColumn(int series, int i, bool lnOfSeries = false) const;
double standardDeviationOfColumn(int series, int i, bool lnOfSeries = false) const;
double covariance(int series, bool lnOfSeries = false) const;
double slope(int series, bool lnOfSeries = false) const;
double yIntercept(int series, bool lnOfSeries = false) const;
double yValueForXValue(int series, double x, Poincare::Context * globalContext);
double xValueForYValue(int series, double y, Poincare::Context * globalContext);
double correlationCoefficient(int series) const;
@@ -89,8 +89,7 @@ private:
Poincare::Preferences::AngleUnit m_angleUnit;
};
typedef double (Store::*ArgCalculPointer)(int, int) const;
typedef double (Store::*CalculPointer)(int) const;
typedef double (Store::*ArgCalculPointer)(int, int, bool) const;
typedef void (Store::*RangeMethodPointer)();
}

View File

@@ -98,12 +98,12 @@ int DoublePairStore::indexOfKthNonEmptySeries(int k) const {
return 0;
}
double DoublePairStore::sumOfColumn(int series, int i) const {
double DoublePairStore::sumOfColumn(int series, int i, bool lnOfSeries) const {
assert(series >= 0 && series < k_numberOfSeries);
assert(i == 0 || i == 1);
double result = 0;
for (int k = 0; k < m_numberOfPairs[series]; k++) {
result += m_data[series][i][k];
result += lnOfSeries ? log(m_data[series][i][k]) : m_data[series][i][k];
}
return result;
}

View File

@@ -47,7 +47,7 @@ public:
int indexOfKthNonEmptySeries(int k) const;
// Calculations
double sumOfColumn(int series, int i) const;
double sumOfColumn(int series, int i, bool lnOfSeries = false) const;
bool seriesNumberOfAbscissaeGreaterOrEqualTo(int series, int i) const;
uint32_t storeChecksum() const;
uint32_t storeChecksumForSeries(int series) const;