Soothsayer v0.1.0
# Livebook Setup
Mix.install([
{:soothsayer, path: "~/code/soothsayer"},
{:explorer, "~> 0.9.1"},
{:vega_lite, ">= 0.0.0"},
{:kino_vega_lite, ">= 0.0.0"}
])
alias Explorer.DataFrame
alias Explorer.Series
alias VegaLite, as: Vl
Nx.global_default_backend(EXLA.Backend)
Section
# Generate synthetic data
start_date = ~D[2020-01-01]
end_date = ~D[2023-12-31]
dates = Date.range(start_date, end_date)
y =
Enum.map(dates, fn date ->
days_since_start = Date.diff(date, start_date)
trend = 1000 + 0.5 * days_since_start
yearly_seasonality = 50 * :math.sin(2 * :math.pi() * days_since_start / 365.25)
weekly_seasonality = 20 * :math.cos(2 * :math.pi() * Date.day_of_week(date) / 7)
noise = :rand.normal(0, 20)
trend + yearly_seasonality + weekly_seasonality + noise
end)
df = DataFrame.new(%{"ds" => dates, "y" => y})
Vl.new(width: 800, height: 600)
|> Vl.data_from_values(df, only: ["ds", "y"])
|> Vl.mark(:line)
|> Vl.encode_field(:x, "ds", type: :temporal)
|> Vl.encode_field(:y, "y", type: :quantitative)
# Create and fit Soothsayer model
model =
Soothsayer.new(%{
trend_config: %{enabled: true},
seasonality_config: %{
yearly: %{enabled: true, period: 365.25, fourier_terms: 10},
weekly: %{enabled: true, period: 7, fourier_terms: 8}
},
epochs: 100
})
fitted_model = Soothsayer.fit(model, df)
# Make predictions and extract components
predictions = Soothsayer.predict(fitted_model, df["ds"])
components = Soothsayer.predict_components(fitted_model, df["ds"])
df_with_predictions =
df
|> DataFrame.put("yhat", predictions)
|> DataFrame.put("trend", components.trend)
|> DataFrame.put("yearly_seasonality", components.yearly_seasonality)
|> DataFrame.put("weekly_seasonality", components.weekly_seasonality)
Vl.new(width: 800, height: 600)
|> Vl.data_from_values(df_with_predictions, only: ["ds", "y", "yhat"])
|> Vl.layers([
Vl.new()
|> Vl.mark(:line)
|> Vl.encode_field(:x, "ds", type: :temporal)
|> Vl.encode_field(:y, "y", type: :quantitative),
Vl.new()
|> Vl.mark(:point)
|> Vl.encode_field(:x, "ds", type: :temporal)
|> Vl.encode_field(:y, "yhat", type: :quantitative)
])