Update: 2026-07-06 19:27:17
This commit is contained in:
@@ -2,6 +2,7 @@ import { PricingTier, RideSample, RegressionResult } from './types';
|
||||
import {
|
||||
twoStageRegression,
|
||||
robustMultipleLinearRegression,
|
||||
simpleDistanceModel,
|
||||
calcRMSE,
|
||||
calcRSquared,
|
||||
detectMinimumFare,
|
||||
@@ -78,17 +79,29 @@ export function analyzeTier(tier: PricingTier): PricingTier {
|
||||
price: s.price,
|
||||
}));
|
||||
|
||||
// Run both models
|
||||
const modelA = twoStageRegression(input); // Stage 1: flag fall | Stage 2: km + min
|
||||
const modelB = robustMultipleLinearRegression(input); // Current robust approach
|
||||
// Run all three models
|
||||
const modelA = twoStageRegression(input); // Stage 1: flag fall | Stage 2: km + min
|
||||
const modelB = robustMultipleLinearRegression(input); // Iterative outlier removal
|
||||
const modelC = simpleDistanceModel(input); // distance-only: price = k × dist
|
||||
|
||||
const best = selectBestModel(modelA, modelB, samples);
|
||||
if (!best) {
|
||||
|
||||
// Distance-only fallback: if it's within 10% of the best model, prefer it
|
||||
let finalModel = best;
|
||||
if (finalModel && modelC) {
|
||||
const rmseBest = evalRMSE(finalModel.model, samples);
|
||||
const rmseDist = evalRMSE(modelC, samples);
|
||||
if (rmseDist <= rmseBest * 1.10) {
|
||||
finalModel = { model: modelC, name: 'distance-only' };
|
||||
}
|
||||
}
|
||||
|
||||
if (!finalModel) {
|
||||
tier.regression = null;
|
||||
return tier;
|
||||
}
|
||||
|
||||
const { model: mlrResult, name: modelName } = best;
|
||||
const { model: mlrResult, name: modelName } = finalModel;
|
||||
|
||||
// Compute final metrics using the winning model
|
||||
const actualPrices = samples.map(s => s.price);
|
||||
|
||||
@@ -209,6 +209,30 @@ export function twoStageRegression(
|
||||
return { baseFare, kmRate: rates.kmRate, minRate: rates.minRate };
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple distance-only model: price = kmRate × dist
|
||||
* Returns null if data is degenerate.
|
||||
*/
|
||||
export function simpleDistanceModel(
|
||||
samples: Array<{ distance_km: number; duration_min: number; price: number }>
|
||||
): { baseFare: number; kmRate: number; minRate: number } | null {
|
||||
const dists = samples.map(s => s.distance_km);
|
||||
const prices = samples.map(s => s.price);
|
||||
|
||||
// Mean of price/distance ratios, weighted by distance
|
||||
let sumRatio = 0, count = 0;
|
||||
for (let i = 0; i < dists.length; i++) {
|
||||
if (dists[i] > 0 && prices[i] > 0) {
|
||||
sumRatio += prices[i] / dists[i];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count < 3) return null;
|
||||
|
||||
const kmRate = Math.round((sumRatio / count) * 1000) / 1000;
|
||||
return { baseFare: 0, kmRate, minRate: 0 };
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Robust regression (iterative outlier removal)
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user