fix: keep growth analysis available per terminal
This commit is contained in:
@@ -17,20 +17,17 @@ func BuildForecast(history []DailyPoint, catalog []Event, start time.Time, horiz
|
||||
}
|
||||
result := Forecast{StartDate: normalizeDate(start), HorizonDays: horizonDays, Confidence: ConfidenceInsufficientData, SuppressionReason: SuppressionInsufficientHistory, Points: []ForecastPoint{}}
|
||||
ordered := normalizedHistory(history, start)
|
||||
if len(ordered) < minimumHistoryDays {
|
||||
if len(ordered) == 0 {
|
||||
return result
|
||||
}
|
||||
result.Backtest = backtest(ordered, catalog)
|
||||
result.Confidence = classifyConfidence(result.Backtest)
|
||||
if result.Confidence == ConfidenceInsufficientData {
|
||||
return result
|
||||
}
|
||||
result.SuppressionReason = ""
|
||||
result.Points = forecastPoints(ordered, catalog, result.StartDate, horizonDays)
|
||||
result.Points = forecastPoints(ordered, catalog, result.StartDate, horizonDays, result.Confidence)
|
||||
return result
|
||||
}
|
||||
|
||||
func forecastPoints(history []DailyPoint, catalog []Event, start time.Time, horizon int) []ForecastPoint {
|
||||
func forecastPoints(history []DailyPoint, catalog []Event, start time.Time, horizon int, confidence Confidence) []ForecastPoint {
|
||||
sessionResiduals, amountResiduals := modelResiduals(history, catalog)
|
||||
points := make([]ForecastPoint, 0, horizon)
|
||||
for index := 0; index < horizon; index++ {
|
||||
@@ -38,8 +35,8 @@ func forecastPoints(history []DailyPoint, catalog []Event, start time.Time, hori
|
||||
baselineSessions, baselineAmount, predictedSessions, predictedAmount, eventIDs := predictCenter(history, catalog, day)
|
||||
points = append(points, ForecastPoint{
|
||||
Date: day, BaselineSessions: baselineSessions, BaselineAmountRial: baselineAmount,
|
||||
PaidSessions: rangeFromResiduals(predictedSessions, sessionResiduals),
|
||||
PaidAmountRial: rangeFromResiduals(predictedAmount, amountResiduals), EventIDs: eventIDs,
|
||||
PaidSessions: forecastRange(predictedSessions, sessionResiduals, confidence),
|
||||
PaidAmountRial: forecastRange(predictedAmount, amountResiduals, confidence), EventIDs: eventIDs,
|
||||
})
|
||||
}
|
||||
return points
|
||||
@@ -64,6 +61,11 @@ func weekdayMedian(history []DailyPoint, weekday time.Weekday, value func(DailyP
|
||||
values = append(values, value(history[index]))
|
||||
}
|
||||
}
|
||||
if len(values) == 0 {
|
||||
for index := len(history) - 1; index >= 0 && len(values) < 8; index-- {
|
||||
values = append(values, value(history[index]))
|
||||
}
|
||||
}
|
||||
return median(values)
|
||||
}
|
||||
|
||||
@@ -100,6 +102,13 @@ func rangeFromResiduals(center int64, residualValues []int64) Range {
|
||||
}
|
||||
}
|
||||
|
||||
func forecastRange(center int64, residualValues []int64, confidence Confidence) Range {
|
||||
if confidence == ConfidenceLow && len(residualValues) == 0 {
|
||||
return Range{P10: center / 2, P50: center, P90: center + center/2}
|
||||
}
|
||||
return rangeFromResiduals(center, residualValues)
|
||||
}
|
||||
|
||||
func quantile(values []int64, percentile int) int64 {
|
||||
if len(values) == 0 {
|
||||
return 0
|
||||
@@ -186,7 +195,7 @@ func predictCenter(history []DailyPoint, catalog []Event, day time.Time) (int64,
|
||||
|
||||
func classifyConfidence(backtest BacktestResult) Confidence {
|
||||
if backtest.Origins < 4 {
|
||||
return ConfidenceInsufficientData
|
||||
return ConfidenceLow
|
||||
}
|
||||
worstMAPE := maxInt64(backtest.SessionMAPEBPS, backtest.AmountMAPEBPS)
|
||||
if worstMAPE <= 2_000 && backtest.IntervalCoverageBPS >= 7_000 {
|
||||
@@ -195,7 +204,7 @@ func classifyConfidence(backtest BacktestResult) Confidence {
|
||||
if worstMAPE <= 4_000 && backtest.IntervalCoverageBPS >= 5_000 {
|
||||
return ConfidenceMedium
|
||||
}
|
||||
return ConfidenceInsufficientData
|
||||
return ConfidenceLow
|
||||
}
|
||||
|
||||
type familyUplift struct{ sessions, amount int64 }
|
||||
|
||||
Reference in New Issue
Block a user