Note
Click here to download the full example code
Seasonality¶
Forecast models learn seasonal (cyclical) patterns and project them into the future. Understanding the seasonal patterns in your dataset can help you create a better forecast. Your goal is to identify which seasonality patterns are most important to capture, and which should be excluded from the model.
This tutorial explains how to identify the dominant seasonal patterns and check for interactions (e.g. daily seasonality that depends on day of week, or weekly seasonality increases in magnitude over time). Such interactions are important to model if they affect a large number of data points.
We use the Peyton Manning dataset as a running example.
Quick reference¶
You will learn how to use the function
plot_quantiles_and_overlays
in UnivariateTimeSeries to assess seasonal patterns.
Steps to detect seasonality:
Start with the longest seasonal cycles to see the big picture, then proceed to shorter cycles. (yearly -> quarterly -> monthly -> weekly -> daily).
For a given seasonality period:
First, check for seasonal effect over the entire timeseries (main effect). Look for large variation that depends on the location in the cycle. Pick the time feature for your seasonality cycle. See available time features at
build_time_features_df.for yearly:
"doy","month_dom","woy_dow"for quarterly:
"doq"for monthly
"dom"for weekly:
"str_dow","dow_grouped","is_weekend","woy_dow"for daily:
"hour","tod"(“do” = “day of”, “to” = “time of”)
fig = ts.plot_quantiles_and_overlays( groupby_time_feature="doy", # day of year (yearly seasonality) show_mean=True, # shows mean on the plot show_quantiles=[0.1, 0.9], # shows quantiles [0.1, 0.9] on the plot xlabel="day of year", ylabel=ts.original_value_col, title="yearly seasonality", )
Then, check for interactions by adding overlays and centering the values. These may be present even when there is no main effect:
# random sample of individual overlays (check for clusters with similar patterns) fig = ts.plot_quantiles_and_overlays( groupby_time_feature="str_dow", # day of week (weekly seasonality) show_mean=True, show_quantiles=False, # shows every 5th overlay. (accepts a list of indices/names, a number to sample, or `True` to show all) show_overlays=np.arange(0, ts.df.shape[0], 5), center_values=True, # each overlay contains 28 observations (4 weeks) overlay_label_sliding_window_size=28, xlabel="day of week", ylabel=ts.original_value_col, title="weekly seasonality with selected 28d averages", ) # interactions with periodic time feature fig = ts.plot_quantiles_and_overlays( groupby_time_feature="str_dow", show_mean=True, show_quantiles=False, show_overlays=True, center_values=True, # splits overlays by month (try other features too) overlay_label_time_feature="month", # optional overlay styling, passed to `plotly.graph_objs.Scatter` overlay_style={"line": {"width": 1}, "opacity": 0.5}, xlabel="day of week", ylabel=ts.original_value_col, title="weekly seasonality by month", ) # interactions with an event (holiday, etc.) fig = ts.plot_quantiles_and_overlays( groupby_time_feature="str_dow", show_mean=True, show_quantiles=False, show_overlays=True, center_values=True, # splits overlays by custom pd.Series value overlay_label_custom_column=is_football_season, overlay_style={"line": {"width": 1}, "opacity": 0.5}, # optional, how to aggregate values for each overlay (default=mean) aggfunc=np.nanmean, xlabel="day of week", ylabel=ts.original_value_col, title="weekly seasonality:is_football_season interaction", ) # seasonality changepoints (option a): overlay against time (good for yearly/quarterly/monthly) fig = ts.plot_quantiles_and_overlays( groupby_time_feature="woy_dow", # yearly(+weekly) seasonality show_mean=True, show_quantiles=True, show_overlays=True, overlay_label_time_feature="year", # splits by time overlay_style={"line": {"width": 1}, "opacity": 0.5}, center_values=True, xlabel="weekofyear_dayofweek", ylabel=ts.original_value_col, title="yearly and weekly seasonality for each year", ) # seasonality changepoints (option b): overlay by seasonality value (good for daily/weekly/monthly) # see advanced version below, where the mean is removed. fig = ts.plot_quantiles_and_overlays( # The number of observations in each sliding window. # Should contain a whole number of complete seasonality cycles, e.g. 24*7*k for k weekly seasonality cycles on hourly data. groupby_sliding_window_size=7*13, # x-axis, sliding windows with 13 weeks of daily observations. show_mean=True, show_quantiles=False, show_overlays=True, center_values=True, # overlays by the seasonality of interest (e.g. "hour", "str_dow", "dom") overlay_label_time_feature="str_dow", overlay_style={"line": {"width": 1}, "opacity": 0.5}, ylabel=ts.original_value_col, title="daily averages over time (centered)", )
For additional customization, fetch the dataframe for plotting via
get_quantiles_and_overlays, compute additional stats as needed, and plot withplot_multivariate. For example, to remove the mean effect in seasonality changepoints (option b):grouped_df = ts.get_quantiles_and_overlays( groupby_sliding_window_size=7*13, # accepts the same parameters as `plot_quantiles_and_overlays` show_mean=True, show_quantiles=False, show_overlays=True, center_values=False, # note! does not center, to compute raw differences from the mean below overlay_label_time_feature="str_dow", ) overlay_minus_mean = grouped_df[OVERLAY_COL_GROUP] - grouped_df[MEAN_COL_GROUP].values # subtracts the mean x_col = overlay_minus_mean.index.name overlay_minus_mean.reset_index(inplace=True) # `plot_multivariate` expects the x-value to be a column fig = plot_multivariate( # plots the deviation from the mean df=overlay_minus_mean, x_col=x_col, ylabel=ts.original_value_col, title="day of week effect over time", )
The yearly seasonality plot can also be used to check for holiday effects. Click and drag to zoom in on the dates of interest:
fig = ts.plot_quantiles_and_overlays( groupby_time_feature="month_dom", # date on x-axis show_mean=True, show_quantiles=False, show_overlays=True, overlay_label_time_feature="year", # see the value for each year overlay_style={"line": {"width": 1}, "opacity": 0.5}, center_values=True, xlabel="day of year", ylabel=ts.original_value_col, title="yearly seasonality for each year (centered)", )
Tip
plot_quantiles_and_overlaysallows grouping or overlays by (1) a time feature, (2) a sliding window, or (3) a custom column. See available time features atbuild_time_features_df.You can customize the plot style. See
plot_quantiles_and_overlaysfor details.
Load data¶
To start, let’s plot the dataset. It contains daily observations between
2007-12-10 and 2016-01-20.
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | # necessary imports
from datetime import datetime
import numpy as np
import plotly
from greykite.framework.input.univariate_time_series import UnivariateTimeSeries
from greykite.framework.constants import MEAN_COL_GROUP, OVERLAY_COL_GROUP
from greykite.common.constants import TIME_COL
from greykite.common.data_loader import DataLoader
from greykite.common.viz.timeseries_plotting import add_groupby_column, plot_multivariate, plot_univariate
# Loads dataset into pandas DataFrame
dl = DataLoader()
df = dl.load_peyton_manning()
df.rename(columns={"y": "log(pageviews)"}, inplace=True) # uses a more informative name
# plots dataset
ts = UnivariateTimeSeries()
ts.load_data(
df=df,
time_col="ts",
value_col="log(pageviews)",
freq="D")
fig = ts.plot()
plotly.io.show(fig)
|
Yearly seasonality¶
Because the observations are at daily frequency, it is possible to see yearly, quarterly, monthly, and weekly seasonality. The name of the seasonality refers to the length of one cycle. For example, yearly seasonality is a pattern that repeats once a year.
Tip
It’s helpful to start with the longest cycle to see the big picture.
To examine yearly seasonality, plot the average value by day of year.
Use plot_quantiles_and_overlays
with show_mean=True and groupby_time_feature="doy" (day of year).
groupby_time_feature accepts any time feature generated by
build_time_features_df.
230 231 232 233 234 235 236 237 | fig = ts.plot_quantiles_and_overlays(
groupby_time_feature="doy", # day of year
show_mean=True, # shows the mean
xlabel="day of year",
ylabel=f"mean of {ts.original_value_col}",
title="yearly seasonality",
)
plotly.io.show(fig)
|