import numpy as np
import pandas as pd
from sktime.split import temporal_train_test_split
= 1000
num_obs = pd.DataFrame(
y ={"value" : np.sin(np.arange(num_obs)*2*np.pi/30)*0.45 + 0.5},
data=pd.period_range(
index"2025-01-01",
=num_obs,
periods="D",
freq
)
) += np.random.normal(0, 0.1, size=y.shape)
y = y.clip(1e-6, 1 - 1e-6)
y
= temporal_train_test_split(y, test_size=0.4)
y_train, y_test
y_train.plot.line()
Forecasting Percentages
Recipe: use likelihood=βbetaβ for (0,1) targets
This howβto shows how to use the Beta likelihood for a proportion / percentage target using the same sktime-style API demonstrated in other tutorials.
Why Beta? Because the target is naturally bounded in (0,1), and Beta likelihood can provide probabilistic intervals in (0,1). The likelihood="beta"
option internally applies a link that guarantees valid predictions.
1. Load data and build a (0,1) target
2. Specify model components
We use a piecewise linear trend plus weekly and yearly seasonality. The API mirrors the univariate tutorial: pass trend=...
, supply exogenous_effects
as a list of tuples, and choose likelihood="beta"
.
import numpyro
from prophetverse.effects.trend import PiecewiseLinearTrend
from prophetverse.effects.fourier import LinearFourierSeasonality
from prophetverse.effects.target.univariate import BetaTargetLikelihood
from prophetverse.utils import no_input_columns
from prophetverse.engine import MAPInferenceEngine
from prophetverse.sktime import Prophetverse
numpyro.enable_x64()
= (
seasonality "seasonality",
LinearFourierSeasonality(="D",
freq=[30], # weekly + yearly
sp_list=[30],
fourier_terms_list=1,
prior_scale="additive",
effect_mode
),
no_input_columns,
)
= Prophetverse(
model ="flat",
trend=[seasonality],
exogenous_effects=BetaTargetLikelihood(noise_scale=0.2), # <β key change
likelihood=MAPInferenceEngine(),
inference_engine=1,
scale
)=y_train) model.fit(y
/opt/hostedtoolcache/Python/3.11.13/x64/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm
Prophetverse(exogenous_effects=[('seasonality', LinearFourierSeasonality(fourier_terms_list=[30], freq='D', prior_scale=1, sp_list=[30]), '^$')], inference_engine=MAPInferenceEngine(), likelihood=BetaTargetLikelihood(noise_scale=0.2), scale=1, trend='flat')Please rerun this cell to show the HTML repr or trust the notebook.
Prophetverse(exogenous_effects=[('seasonality', LinearFourierSeasonality(fourier_terms_list=[30], freq='D', prior_scale=1, sp_list=[30]), '^$')], inference_engine=MAPInferenceEngine(), likelihood=BetaTargetLikelihood(noise_scale=0.2), scale=1, trend='flat')
FlatTrend()
LinearFourierSeasonality(fourier_terms_list=[30], freq='D', prior_scale=1, sp_list=[30])
MAPInferenceEngine()
3. Forecast the next 180 days
import pandas as pd
= y_test.index
fh = model.predict(fh=fh)
pred_share pred_share.head()
value | |
---|---|
2026-08-24 | 0.520491 |
2026-08-25 | 0.579718 |
2026-08-26 | 0.659398 |
2026-08-27 | 0.747844 |
2026-08-28 | 0.873271 |
4. Plot
import matplotlib.pyplot as plt
= plt.subplots(figsize=(9, 4))
fig, ax
ax.plot(="observed daily share", lw=1, alpha=0.6
y.index.to_timestamp(), y, label
)="forecast", color="C1")
ax.plot(pred_share.index.to_timestamp(), pred_share, label"Daily share of monthly total")
ax.set_ylabel(
ax.legend() plt.show()
5. Probabilistic forecast (quantiles)
= model.predict_quantiles(fh=fh, alpha=[0.1, 0.9])
q = plt.subplots(figsize=(9, 4))
fig, ax
ax.fill_between(
q.index.to_timestamp(),0],
q.iloc[:, -1],
q.iloc[:, ="C1",
color=0.3,
alpha="80% PI",
label
)-(len(fh) + 100):].index.to_timestamp(), y.iloc[-(len(fh) + 100):], lw=1, alpha=0.6, color="k")
ax.plot(y.iloc["Daily share")
ax.set_ylabel(
ax.legend() plt.show()
Notes & Tips
- Ensure the target is strictly inside (0,1), excluding the boundaries.
noise_scale
controls dispersion of the Beta (smaller => tighter intervals).- The same pattern works for conversion rates, CTR, retention proportions, etc.
- Switch to full Bayesian inference by setting
inference_engine=MCMCInferenceEngine(...)
if you need richer uncertainty.