From 8761ef5f0e0f74183567d47c1066df6cff0a4df0 Mon Sep 17 00:00:00 2001 From: OliE Date: Fri, 22 Sep 2017 10:52:31 +0200 Subject: [PATCH] add an optional goal and linear regression weight line --- .../gui/fragments/GraphFragment.java | 132 ++++++++++++++++++ .../app/src/main/res/values/strings.xml | 3 + .../src/main/res/xml/graph_preferences.xml | 3 +- 3 files changed, 137 insertions(+), 1 deletion(-) diff --git a/android_app/app/src/main/java/com/health/openscale/gui/fragments/GraphFragment.java b/android_app/app/src/main/java/com/health/openscale/gui/fragments/GraphFragment.java index a0d27981..6b202b34 100644 --- a/android_app/app/src/main/java/com/health/openscale/gui/fragments/GraphFragment.java +++ b/android_app/app/src/main/java/com/health/openscale/gui/fragments/GraphFragment.java @@ -20,6 +20,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.res.ColorStateList; import android.graphics.Color; +import android.graphics.DashPathEffect; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.design.widget.FloatingActionButton; @@ -332,6 +333,137 @@ public class GraphFragment extends Fragment implements FragmentUpdateListener { enableMonth.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#d3d3d3"))); } + if (prefs.getBoolean("goalLine", true)) { + Stack valuesGoalLine = new Stack(); + + float goalWeight = openScale.getSelectedScaleUser().goal_weight; + + valuesGoalLine.push(new PointValue(0, goalWeight)); + valuesGoalLine.push(new PointValue(31, goalWeight)); + + Line goalLine = new Line(valuesGoalLine) + .setHasPoints(false); + + goalLine.setPathEffect(new DashPathEffect(new float[] {10,30}, 0)); + + lines.add(goalLine); + } + + if (prefs.getBoolean("regressionLine", true)) { + + /* + // quadratic regression y = ax^2 + bx + c + double x_value = 0.0; + double y_value = 0.0; + + double s40 = 0; //sum of x^4 + double s30 = 0; //sum of x^3 + double s20 = 0; //sum of x^2 + double s10 = 0; //sum of x + double s00 = scaleDataList.size(); + //sum of x^0 * y^0 ie 1 * number of entries + + double s21 = 0; //sum of x^2*y + double s11 = 0; //sum of x*y + double s01 = 0; //sum of y + + for(ScaleData scaleEntry: scaleDataList) { + calDB.setTime(scaleEntry.getDateTime()); + + x_value = calDB.get(field); + y_value = scaleEntry.getConvertedWeight(openScale.getSelectedScaleUser().scale_unit); + + s40 += Math.pow(x_value, 4); + s30 += Math.pow(x_value, 3); + s20 += Math.pow(x_value, 2); + s10 += x_value; + + s21 += Math.pow(x_value, 2) * y_value; + s11 += x_value * y_value; + s01 += y_value; + } + + // solve equations using Cramer's law + double a = (s21*(s20 * s00 - s10 * s10) - + s11*(s30 * s00 - s10 * s20) + + s01*(s30 * s10 - s20 * s20)) + / + (s40*(s20 * s00 - s10 * s10) - + s30*(s30 * s00 - s10 * s20) + + s20*(s30 * s10 - s20 * s20)); + + double b = (s40*(s11 * s00 - s01 * s10) - + s30*(s21 * s00 - s01 * s20) + + s20*(s21 * s10 - s11 * s20)) + / + (s40 * (s20 * s00 - s10 * s10) - + s30 * (s30 * s00 - s10 * s20) + + s20 * (s30 * s10 - s20 * s20)); + + double c = (s40*(s20 * s01 - s10 * s11) - + s30*(s30 * s01 - s10 * s21) + + s20*(s30 * s11 - s20 * s21)) + / + (s40 * (s20 * s00 - s10 * s10) - + s30 * (s30 * s00 - s10 * s20) + + s20 * (s30 * s10 - s20 * s20)); + */ + + // linear regression y = a + x*b + double sumx = 0.0; + double sumy = 0.0; + + double x_value = 0.0; + double y_value = 0.0; + + for(ScaleData scaleEntry: scaleDataList) { + calDB.setTime(scaleEntry.getDateTime()); + + x_value = calDB.get(field); + y_value = scaleEntry.getConvertedWeight(openScale.getSelectedScaleUser().scale_unit); + + sumx += x_value; + sumy += y_value; + } + + double xbar = sumx / scaleDataList.size(); + double ybar = sumy / scaleDataList.size(); + + double xxbar = 0.0; + double xybar = 0.0; + + for(ScaleData scaleEntry: scaleDataList) { + calDB.setTime(scaleEntry.getDateTime()); + + x_value = calDB.get(field); + y_value = scaleEntry.getConvertedWeight(openScale.getSelectedScaleUser().scale_unit); + + xxbar += (x_value - xbar) * (x_value - xbar); + xybar += (y_value - xbar) * (y_value - ybar); + } + + double b = xybar / xxbar; + double a = ybar - b * xbar; + + + Stack valuesLinearRegression = new Stack(); + + for (int i = 0; i < 31; i++) { + y_value = a + b * i; // linear regression + //y_value = a * i*i + b * i + c; // quadratic regression + + valuesLinearRegression.push(new PointValue((float) i, (float) y_value)); + } + + Line linearRegressionLine = new Line(valuesLinearRegression) + .setColor(ChartUtils.COLOR_VIOLET) + .setHasPoints(false); + + linearRegressionLine.setPathEffect(new DashPathEffect(new float[] {10,30}, 0)); + + lines.add(linearRegressionLine); + } + LineChartData lineData = new LineChartData(lines); lineData.setAxisXBottom(new Axis(axisValues). setHasLines(true). diff --git a/android_app/app/src/main/res/values/strings.xml b/android_app/app/src/main/res/values/strings.xml index c6172538..2f985fcf 100644 --- a/android_app/app/src/main/res/values/strings.xml +++ b/android_app/app/src/main/res/values/strings.xml @@ -150,6 +150,9 @@ Ignore data that are out of range Initial weight Calculate average per day/month + Regression weight line + Goal line + Maximum number of concurrent scale users reached. Please step barefoot on the scale for reference measurements. diff --git a/android_app/app/src/main/res/xml/graph_preferences.xml b/android_app/app/src/main/res/xml/graph_preferences.xml index 68432cf8..66335eb8 100644 --- a/android_app/app/src/main/res/xml/graph_preferences.xml +++ b/android_app/app/src/main/res/xml/graph_preferences.xml @@ -4,4 +4,5 @@ - \ No newline at end of file + + \ No newline at end of file