diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-08-24 09:27:29 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-08-24 09:27:29 +0200 |
| commit | 8b921624666141c09cf20e2e0ee3cbe260a6412d (patch) | |
| tree | 7f708dfe7807dda1f54e4d010d58858d5e7c5999 /cli | |
| parent | 34b8ae8f4c16b86281aa578e8f6891fca3666d37 (diff) | |
Add type filtering, drop hard-coded filters and route filter, tune backtest
Diffstat (limited to 'cli')
| -rw-r--r-- | cli/cli.go | 22 | ||||
| -rw-r--r-- | cli/cli_test.go | 28 |
2 files changed, 32 insertions, 18 deletions
@@ -28,11 +28,6 @@ var ( enablePprof = flag.Bool("pprof", false, "expose pprof endpoints at /debug/pprof") ) -const ( - backtestMinimumDistanceM = 10_000 - backtestMinimumMovingRatio = 0.8 -) - func Run(db *sql.DB, configPath string, appConfig config.Config) { switch { case *fetchOSM: @@ -127,16 +122,21 @@ func runBacktest(db *sql.DB) error { fmt.Fprintf(table, "p%g\t%.1f\n", p, distribution[p]) } table.Flush() - slog.Info("Moving time backtest", "rides", len(samples), "min_distance_m", backtestMinimumDistanceM, "min_moving_ratio", backtestMinimumMovingRatio, "mean_err_pct", math.Round(meanAbsErr(absErrors)*10)/10, "stderr_pct", math.Round(stdErrAbsErr(absErrors)*10)/10) + slog.Info("Moving time backtest", "rides", len(samples), "mean_err_pct", math.Round(meanAbsErr(absErrors)*10)/10, "stderr_pct", math.Round(stdErrAbsErr(absErrors)*10)/10) return nil } func isBacktestEligible(item ride.StoredRide) bool { - return item.GPXPath != "" && - item.DistanceM >= backtestMinimumDistanceM && - item.ExternalMovingTimeS > 0 && - item.ElapsedTimeS > 0 && - float64(item.ExternalMovingTimeS)/float64(item.ElapsedTimeS) >= backtestMinimumMovingRatio + if item.GPXPath == "" || item.ExternalMovingTimeS <= 0 || item.ElapsedTimeS <= 0 { + return false + } + if !item.IsCategorised() { + return false + } + if item.RideType == ride.RideTypeIndoor || item.RideType == ride.RideTypeTransport { + return false + } + return true } func meanAbsErr(absErrors []float64) float64 { diff --git a/cli/cli_test.go b/cli/cli_test.go index 3e2afe2..28260e0 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -8,23 +8,37 @@ import ( ) func TestBacktestEligibilityExcludesShortDistances(t *testing.T) { - short := ride.StoredRide{GPXPath: "ride.gpx", DistanceM: 10_000 - 1, ExternalMovingTimeS: 8_000, ElapsedTimeS: 10_000} - minimum := ride.StoredRide{GPXPath: "ride.gpx", DistanceM: 10_000, ExternalMovingTimeS: 8_000, ElapsedTimeS: 10_000} + short := ride.StoredRide{GPXPath: "ride.gpx", DistanceM: 10_000 - 1, ExternalMovingTimeS: 8_000, ElapsedTimeS: 10_000, BikeType: ride.BikeTypeRoad, RideType: ride.RideTypeTraining} + minimum := ride.StoredRide{GPXPath: "ride.gpx", DistanceM: 10_000, ExternalMovingTimeS: 8_000, ElapsedTimeS: 10_000, BikeType: ride.BikeTypeRoad, RideType: ride.RideTypeTraining} - assert.False(t, isBacktestEligible(short)) + assert.True(t, isBacktestEligible(short)) assert.True(t, isBacktestEligible(minimum)) } func TestBacktestEligibilityExcludesLowMovingRatio(t *testing.T) { - low := ride.StoredRide{GPXPath: "ride.gpx", DistanceM: 10_000, ExternalMovingTimeS: 7_999, ElapsedTimeS: 10_000} - boundary := ride.StoredRide{GPXPath: "ride.gpx", DistanceM: 10_000, ExternalMovingTimeS: 8_000, ElapsedTimeS: 10_000} - noElapsed := ride.StoredRide{GPXPath: "ride.gpx", DistanceM: 10_000, ExternalMovingTimeS: 1} + low := ride.StoredRide{GPXPath: "ride.gpx", DistanceM: 10_000, ExternalMovingTimeS: 7_999, ElapsedTimeS: 10_000, BikeType: ride.BikeTypeRoad, RideType: ride.RideTypeTraining} + boundary := ride.StoredRide{GPXPath: "ride.gpx", DistanceM: 10_000, ExternalMovingTimeS: 8_000, ElapsedTimeS: 10_000, BikeType: ride.BikeTypeRoad, RideType: ride.RideTypeTraining} + noElapsed := ride.StoredRide{GPXPath: "ride.gpx", DistanceM: 10_000, ExternalMovingTimeS: 1, BikeType: ride.BikeTypeRoad, RideType: ride.RideTypeTraining} - assert.False(t, isBacktestEligible(low)) + assert.True(t, isBacktestEligible(low)) assert.True(t, isBacktestEligible(boundary)) assert.False(t, isBacktestEligible(noElapsed)) } +func TestBacktestEligibilityExcludesUncategorisedAndIndoorTransport(t *testing.T) { + base := ride.StoredRide{GPXPath: "ride.gpx", ExternalMovingTimeS: 8_000, ElapsedTimeS: 10_000, BikeType: ride.BikeTypeRoad, RideType: ride.RideTypeTraining} + uncategorised := ride.StoredRide{GPXPath: "ride.gpx", ExternalMovingTimeS: 8_000, ElapsedTimeS: 10_000} + indoor := ride.StoredRide{GPXPath: "ride.gpx", ExternalMovingTimeS: 8_000, ElapsedTimeS: 10_000, BikeType: ride.BikeTypeRoad, RideType: ride.RideTypeIndoor} + transport := ride.StoredRide{GPXPath: "ride.gpx", ExternalMovingTimeS: 8_000, ElapsedTimeS: 10_000, BikeType: ride.BikeTypeRoad, RideType: ride.RideTypeTransport} + race := ride.StoredRide{GPXPath: "ride.gpx", ExternalMovingTimeS: 8_000, ElapsedTimeS: 10_000, BikeType: ride.BikeTypeMTB, RideType: ride.RideTypeRace} + + assert.True(t, isBacktestEligible(base)) + assert.False(t, isBacktestEligible(uncategorised)) + assert.False(t, isBacktestEligible(indoor)) + assert.False(t, isBacktestEligible(transport)) + assert.True(t, isBacktestEligible(race)) +} + func TestBacktestStatsUseUnroundedSamples(t *testing.T) { absErrors := []float64{0, 0.1, 0.1} |
