Series temporales: (3) Estacionalidad

Estacionalidad

¿Qué es la estacionalidad?

Se dice que una serie temporal exhibe estacionalidad siempre que haya un cambio regular y periodico en la media de la serie. Generalmente, los cambios estacionales siguen el reloj y el calendario. Son comunes las repeticiones en un día, una semana o un año. La estacionalidad suele estar dirigida por los ciclos del mundo natural durante días y años o por convenciones de comportamiento social en torno a fechas y horas.

patrones de estacionalidad

Veremos que hay dos tipos de variables que modelan la estacionalidad. El primer tipo, los indicadores, es mejor para una estacionalidad con pocas observaciones, como la estacionalidad semanal de observaciones diarias. El segundo tipo, variables Fourier, es mejor para una estacionalidad con muchas observaciones, como la estacionalidad anual de observaciones diarias.

Gráficas de estacionalidad e indicadores de estacionalidad

Al igual que usamos una gráfica de media móvil para descubrir la tendencia en una serie, podemos usar una gráfica estacional para descubrir patrones estacionales.

Una gráfica estacional muestra segmentos de la serie temporal representados en un periodo común, siento el periodo la “estación” que queremos observar. La siguiente figura muestra una gráfica estacional de las vistas diarias del artículo de Wikipedia sobre Trigonometría: las vistas diarias del artículo dibujadas sobre un periodo semanal común. En ella se puede observar claramente un patrón estacional semanal, más grande entre semana y descendente hacia el fin de semana.

season_trigonometry

Indicadores estacionales

Los indicadores estacionales son features binarias que representan diferencias estacionales en el nivel de una serie temporal. Los indicadores estacionales son lo que obtenemos si tratamos un periodo estacional como una variable categórica y le aplicamos codificación one-hot.

Al codificar one-hot los días de la semana, obtenemos indicadores estacionales semanales. La creación de indicadores semanales para la serie Trigonometría nos proporcionará seis nuevas features “dummy”. (La regresión lineal trabaja mejor si eliminamos unos de los indicadores; elegimos el lunes en la tabla siguiente.)

one_hot_trigonometry

Añadir los indicadores estacionales a los datos de entrenamiento ayuda a los modelos a distinguir medias dentro de un periodo estacional:

linear_regresion_estacionalidad

Los indicadores actúan como interruptores encendido/apagado. En cualquier momento, como máximo uno de esos indicadores puede tener un valor de 1 (encendido). La regresión lineal aprende un valor de línea base de 2379 para Mon y luego lo ajusta según el valor de si el indicador está encendido para ese día; el resto son 0 y desaparecen.

Variables Fourier y el Periodograma

Este tipo de features es más apropiado para grandes estacionalidades en muchas observaciones, donde los indicadores serían impracticables. En lugar de crear una variable para cada fecha, las features Fourier intentar capturar la forma global de la curva estacional con solo unas pocas features.

Echemos un vistazo al gráfico de la estacionalidad anual de Trigonometría. Observemos las repeticiones de varias frecuencias: un movimiento largo ascendente y descendente tres veces al año, movimientos cortos semanales 52 veces al año y tal vez otros.

fourier_trigonometria

Estas son las frecuencias dentro de una estación que intentamos capturar con las features Fourier. La idea es incluir en nuestros datos de entrenamiento curvas periodicas que tengan las mismas frecuencias que la estación que estamos intentando modelar. Las curvas que utilizamos son las de las funciones seno y coseno.

Las features Fourier son pares de curvas seno y coseno, un par para cada potencial frecuencia en la estación empezando con la más larga. Los pares de Fourier que modelan la estacionalidad anual tendrían frecuencias: una vez al año, dos veces por año, tres veces por año y así sucesivamente.

fourier_pares

En la figura anterior se muestras los primeros dos pares Fourier para la estacionalidad anual. En la parte superior, frecuencia de una vez por año. En la parte inferior, frecuencia de dos veces por año.

Si añadimos un conjunto de estas curvas seno/coseno a nuestros datos de entrenamiento, el algoritmo de regresión lineal averiguará los pesos que ajustarán el componente estacional en la serie objetivo. La siguiente figura ilustra cómo la regresión lineal usó cuatro pares Fourier para modelar la estacionalidad anual en la serie Trigonometría de la wiki.

trigonometria_pares_fourier

En la parte superior, las curvas de cuatro pares, una suma de senos y cosenos con coeficientes de regresión. Cada curva modela una frecuencia diferente. En la parte inferior, la suma de esas curvas aproxima el patrón estacional.

Tengamos en cuenta que solo necesitamos ocho features (cuatro pares seno/coseno) para obtener una buena estimación de la estacionalidad anual. Comparemos esto con el método de indicadores estacionales, que habría requerido cientos de features (una por cada día del año). Al modelar solo el “efecto pincipal” de la estacionalidad con features Fourier, normalmente necesitaremos añadir muchas menos funciones a los datos de entrenamiento, lo que reduce el tiempo computacional y disminuye el riesgo de overfitting.

Elegir features Fourier con el Periodograma

¿Cuántos pares Fourier se deben añadir realmente en nuestro conjunto de features? Podemos responder esta pregunta con el periodograma. El periodograma nos dice la fortaleza de las frecuencias de un serie temporal. Específicamente, el valor del eje y del gráfico es (a ** 2 + b ** 2) / 2, donde a y b son los coeficientes del seno y coseno en esa frecuencia (como vimos en el grafico anterior).

periodograma

De izquierda a derecha, el periodograma cae después de trimestral (quarterly), cuatro veces al año. Por eso elegimos cuatro pares Fourier para modelas la estacionalidad anual. La frecuencia semanal la ignoramos, dado que es mejor modelarla con indicadores.

Calculo de las features Fourier

Saber cómo se calculan las features Fourier no es esencial para saber usarlas, pero si se ven los detalles se pueden aclarar cosas. A continuación, se muestra cómo se pueden derivar un conjunto de features Fourier del índice de una serie temporal. Sin embargo, para nuestros ejemplos usaremos una función de statsmodels.

import numpy as np
import pandas as pd

def fourier_features(index, freq, order):
    time = np.arange(len(index), dtype=np.float32)
    k = 2 * np.pi * (1 / freq) * time
    features = {}
    for i in range(1, order + 1):
        features.update({
            f"sin_{freq}_{i}": np.sin(i * k),
            f"cos_{freq}_{i}": np.cos(i * k),
        })
    return pd.DataFrame(features, index=index)


# Calcula features Fourier de 4º orden (8 nuevas features) para una
# serie y con observaciones diarias y estacionalidad anual:
#
# fourier_features(y, freq=365.25, order=4)

Ejemplo - Tráfico del Túnel

Utilizaremos el dataset de Tráfico del Túnel. Vamos a definir 2 funciones: seasonal_plot y plot_periodogram.

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from sklearn.linear_model import LinearRegression
from statsmodels.tsa.deterministic import CalendarFourier, DeterministicProcess
# Set Matplotlib defaults
plt.style.use("seaborn-whitegrid")
plt.rc("figure", autolayout=True, figsize=(11, 5))
plt.rc(
    "axes",
    labelweight="bold",
    labelsize="large",
    titleweight="bold",
    titlesize=16,
    titlepad=10,
)
plot_params = dict(
    color="0.75",
    style=".-",
    markeredgecolor="0.25",
    markerfacecolor="0.25",
    legend=False,
)
%config InlineBackend.figure_format = 'retina'
def seasonal_plot(X, y, period, freq, ax=None):
    if ax is None:
        _, ax = plt.subplots()
    palette = sns.color_palette("husl", n_colors=X[period].nunique(),)
    ax = sns.lineplot(
        x=freq,
        y=y,
        hue=period,
        data=X,
        ci=False,
        ax=ax,
        palette=palette,
        legend=False,
    )
    ax.set_title(f"Gráfico estacional ({period}/{freq})")
    for line, name in zip(ax.lines, X[period].unique()):
        y_ = line.get_ydata()[-1]
        ax.annotate(
            name,
            xy=(1, y_),
            xytext=(6, 0),
            color=line.get_color(),
            xycoords=ax.get_yaxis_transform(),
            textcoords="offset points",
            size=14,
            va="center",
        )
    return ax
def plot_periodogram(ts, detrend='linear', ax=None):
    from scipy.signal import periodogram
    fs = pd.Timedelta("1Y") / pd.Timedelta("1D")
    freqencies, spectrum = periodogram(
        ts,
        fs=fs,
        detrend=detrend,
        window="boxcar",
        scaling='spectrum',
    )
    if ax is None:
        _, ax = plt.subplots()
    ax.step(freqencies, spectrum, color="purple")
    ax.set_xscale("log")
    ax.set_xticks([1, 2, 4, 6, 12, 26, 52, 104])
    ax.set_xticklabels(
        [
            "Annual (1)",
            "Semiannual (2)",
            "Quarterly (4)",
            "Bimonthly (6)",
            "Monthly (12)",
            "Biweekly (26)",
            "Weekly (52)",
            "Semiweekly (104)",
        ],
        rotation=30,
    )
    ax.ticklabel_format(axis="y", style="sci", scilimits=(0, 0))
    ax.set_ylabel("Variance")
    ax.set_title("Periodogram")
    return ax
tunnel = pd.read_csv("../data/tunnel.csv", parse_dates=["Day"])
tunnel = tunnel.set_index("Day").to_period("D")

Echemos un vistazo al gráfico estacional durante una semana y durante un año.

X = tunnel.copy()

# días dentro de una semana
X["day"] = X.index.dayofweek  # eje x (freq)
X["week"] = X.index.week  # periodo estacional (period)

# días dentro de un año
X["dayofyear"] = X.index.dayofyear
X["year"] = X.index.year
fig, (ax0, ax1) = plt.subplots(2, 1, figsize=(11, 6))
seasonal_plot(X, y="NumVehicles", period="week", freq="day", ax=ax0)
seasonal_plot(X, y="NumVehicles", period="year", freq="dayofyear", ax=ax1);

png

Veamos ahora el periodograma:

plot_periodogram(tunnel.NumVehicles);
C:\Users\futit\AppData\Local\Temp\ipykernel_8772\272637971.py:1: FutureWarning: Units 'M', 'Y' and 'y' do not represent unambiguous timedelta values and will be removed in a future version.
  plot_periodogram(tunnel.NumVehicles);

png

El periodograma concuerda con el gráfico estacional anterior: una fuerte estacionalidad semanal y una estacionalidad anual más débil. La estacionalidad semanal la modelaremos con indicadores y la estacionalidad anual con features Fourier. De derecha a izquierda, el periodograma cae entre Semestral (6) y Mensual (12), así que usaremos 10 pares Fourier.

Crearemos nuestras features estacionales usando DeterministicProcess, la misma utilidad usada para crear la features de tendencia. Para usar dos periodos estacionales (semanal y anual) necesitaremos instanciar uno de ellos como un “término adicional”:

from statsmodels.tsa.deterministic import CalendarFourier, DeterministicProcess

fourier = CalendarFourier(freq="A", order=10)  # 10 pares seno/coseno para estacionalidad "A"nual

dp = DeterministicProcess(
    index=tunnel.index,
    constant=True,               # feature dummy para bias (y-intercept)
    order=1,                     # tendencia (orden 1 significa lineal)
    seasonal=True,               # estacionalidad semanal (indicadores)
    additional_terms=[fourier],  # estacionalidad anual (fourier)
    drop=True,                   # eliminamos términos para evitar colinealidad
)

X = dp.in_sample()  # crear features para fecha en tunnel.index
X.head()

const trend s(2,7) s(3,7) s(4,7) s(5,7) s(6,7) s(7,7) sin(1,freq=A-DEC) cos(1,freq=A-DEC) ... sin(6,freq=A-DEC) cos(6,freq=A-DEC) sin(7,freq=A-DEC) cos(7,freq=A-DEC) sin(8,freq=A-DEC) cos(8,freq=A-DEC) sin(9,freq=A-DEC) cos(9,freq=A-DEC) sin(10,freq=A-DEC) cos(10,freq=A-DEC)
Day
2003-11-01 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 -0.867456 0.497513 ... -0.017213 0.999852 -0.875892 0.482508 -0.854322 -0.519744 0.025818 -0.999667 0.880012 -0.474951
2003-11-02 1.0 2.0 1.0 0.0 0.0 0.0 0.0 0.0 -0.858764 0.512371 ... 0.085965 0.996298 -0.811539 0.584298 -0.917584 -0.397543 -0.128748 -0.991677 0.785650 -0.618671
2003-11-03 1.0 3.0 0.0 1.0 0.0 0.0 0.0 0.0 -0.849817 0.527078 ... 0.188227 0.982126 -0.735417 0.677615 -0.963471 -0.267814 -0.280231 -0.959933 0.668064 -0.744104
2003-11-04 1.0 4.0 0.0 0.0 1.0 0.0 0.0 0.0 -0.840618 0.541628 ... 0.288482 0.957485 -0.648630 0.761104 -0.991114 -0.133015 -0.425000 -0.905193 0.530730 -0.847541
2003-11-05 1.0 5.0 0.0 0.0 0.0 1.0 0.0 0.0 -0.831171 0.556017 ... 0.385663 0.922640 -0.552435 0.833556 -0.999991 0.004304 -0.559589 -0.828770 0.377708 -0.925925

5 rows × 28 columns

Con nuestro conjunto de features creado, estamos listos para entrenar el modelo y hacer predicciones. Añadiremos un pronóstico de 90 días para ver cómo extrapola nuestro modelo más allá de los datos de entrenamiento.

y = tunnel["NumVehicles"]

model = LinearRegression(fit_intercept=False)
_ = model.fit(X, y)

y_pred = pd.Series(model.predict(X), index=y.index)
X_fore = dp.out_of_sample(steps=90)
y_fore = pd.Series(model.predict(X_fore), index=X_fore.index)

ax = y.plot(color='0.25', style='.', title="Tráfico del Túnel - Pronóstico estacionalidad")
ax = y_pred.plot(ax=ax, label="Estacional")
ax = y_fore.plot(ax=ax, label="Pronóstico estacional", color='C3')
_ = ax.legend()

png

Ejercicio

Vamos a realizar un ejercicio para ampliar lo que acabamos de ver. Para ello cargaremos algunos datasets.

store_sales = pd.read_csv(
    "../data/store_sales/train.csv",
    usecols=['store_nbr', 'family', 'date', 'sales'],
    dtype={
        'store_nbr': 'category',
        'family': 'category',
        'sales': 'float32',
    },
    parse_dates=['date'],
    infer_datetime_format=True,
)
store_sales['date'] = store_sales.date.dt.to_period('D')
store_sales = store_sales.set_index(['store_nbr', 'family', 'date']).sort_index()
average_sales = (
    store_sales
    .groupby('date').mean()
    .squeeze()
    .loc['2017']
)
average_sales.head()
date
2017-01-01      6.780303
2017-01-02    786.928406
2017-01-03    619.740234
2017-01-04    555.608032
2017-01-05    436.375397
Freq: D, Name: sales, dtype: float32

Determinar la estacionalidad

Examinemos el siguiente gráfico estacional:

X = average_sales.to_frame()
X["week"] = X.index.week
X["day"] = X.index.dayofweek
seasonal_plot(X, y='sales', period='week', freq='day');

png

y el periodograma:

plot_periodogram(average_sales);
C:\Users\futit\AppData\Local\Temp\ipykernel_8772\69537304.py:1: FutureWarning: Units 'M', 'Y' and 'y' do not represent unambiguous timedelta values and will be removed in a future version.
  plot_periodogram(average_sales);

png

¿De qué tipo de estacionalidad se ve evidencia?

Ambos gráficos sugieren una fuerte estacionalidad semanal. Del periodograma parece que también puede haber algunos componentes mensuales y bisemanales (quincenales) De hecho, las notas del dataset dicen que los salarios en el sector público se pagan quincenalmente, el 15 y último día de cada mes (un posible origen para esta estacionalidad).

Crear features estacionales

Usa DeterministicProcess y CalendarFourier para crear:

  • indicadores para estaciones semanales y

  • features Fourier de orden 4 para estaciones mensuales

y = average_sales.copy()

# YOUR CODE HERE
fourier = CalendarFourier(freq="M", order=4)  # 4 pares seno/coseno para estacionalidad "M"ensual
dp = DeterministicProcess(
    index=y.index,
    constant=True,
    order=1,
    seasonal=True,
    additional_terms=[fourier],
    drop=True,
)
X = dp.in_sample()

Ahora vamos a entrenar el modelo estacional.

model = LinearRegression().fit(X, y)
y_pred = pd.Series(model.predict(X), index=X.index)
ax = y.plot(**plot_params, alpha=0.5, title="Ventas promedio", ylabel="artículos vendidos")
ax = y_pred.plot(ax=ax, label="Estacional")
ax.legend();

png

Comprobar la estacionalidad restante

Eliminar de una serie su tendencia o estacionalidad se denomina “detrending” o “desestacionalizar” la serie. Observemos el periodograma de la serie desestacionalizada.

y_deseason = y - y_pred

fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, sharey=True, figsize=(10, 7))
ax1 = plot_periodogram(y, ax=ax1)
ax1.set_title("Componentes de Frecuenca de Venta de Productos")
ax2 = plot_periodogram(y_deseason, ax=ax2)
ax2.set_title("Desestacionalizado");
C:\Users\futit\AppData\Local\Temp\ipykernel_8772\3088312804.py:4: FutureWarning: Units 'M', 'Y' and 'y' do not represent unambiguous timedelta values and will be removed in a future version.
  ax1 = plot_periodogram(y, ax=ax1)
C:\Users\futit\AppData\Local\Temp\ipykernel_8772\3088312804.py:6: FutureWarning: Units 'M', 'Y' and 'y' do not represent unambiguous timedelta values and will be removed in a future version.
  ax2 = plot_periodogram(y_deseason, ax=ax2)

png

Basándonos en estos periodogramas, ¿cómo de efectivo parece que nuestro modelo capturó la estacionalidad? ¿El periodograma concuerda con la gráfica de tiempo de la serie desestacionalizada?

El periodograma de la serie desestacionalizada carece de valores grandes. Al compararlo con el periodograma de la serie original podemos ver que nuestro modelo fue capaz de capturar la variación estacional en el dataset.

Crear features de vacaciones

En el dataset de ventas se incluye una tabla de fiestas.

holidays_events = pd.read_csv(
    "../data/store_sales/holidays_events.csv",
    dtype={
        'type': 'category',
        'locale': 'category',
        'locale_name': 'category',
        'description': 'category',
        'transferred': 'bool',
    },
    parse_dates=["date"],
    infer_datetime_format=True,
)
holidays_events = holidays_events.set_index("date").to_period("D")

holidays_events.head()

type locale locale_name description transferred
date
2012-03-02 Holiday Local Manta Fundacion de Manta False
2012-04-01 Holiday Regional Cotopaxi Provincializacion de Cotopaxi False
2012-04-12 Holiday Local Cuenca Fundacion de Cuenca False
2012-04-14 Holiday Local Libertad Cantonizacion de Libertad False
2012-04-21 Holiday Local Riobamba Cantonizacion de Riobamba False
# Vacaciones nacionales y regionales en el conjunto de entrenamiento
holidays = (
    holidays_events
    .query("locale in ['National', 'Regional']")
    .loc['2017':'2017-08-15', ['description']]
    .assign(description=lambda x: x.description.cat.remove_unused_categories())
)

holidays

description
date
2017-01-01 Primer dia del ano
2017-01-02 Traslado Primer dia del ano
2017-02-27 Carnaval
2017-02-28 Carnaval
2017-04-01 Provincializacion de Cotopaxi
2017-04-14 Viernes Santo
2017-05-01 Dia del Trabajo
2017-05-13 Dia de la Madre-1
2017-05-14 Dia de la Madre
2017-05-24 Batalla de Pichincha
2017-05-26 Traslado Batalla de Pichincha
2017-06-25 Provincializacion de Imbabura
2017-08-10 Primer Grito de Independencia
2017-08-11 Traslado Primer Grito de Independencia

A partir de un gráfico de ventas desestacionalizado, parece que estas vacaciones podrían tener algún poder predictivo.

ax = y_deseason.plot(**plot_params)
plt.plot_date(holidays.index, y_deseason[holidays.index], color='C3')
ax.set_title('Vacaciones nacionales y regionales');

png

¿Qué tipo de features podríamos crear para ayudar a nuestro modelo a hacer uso de esta información? Scikit-learn y Pandas tienen utilidades que permitirían hacer esto de forma fácil.

# solución Scikit-learn
from sklearn.preprocessing import OneHotEncoder

one_hot = OneHotEncoder(sparse=False)

X_holidays = pd.DataFrame(
    one_hot.fit_transform(holidays),
    index=holidays.index,
    columns=holidays.description.unique(),
)

X_holidays.head()

Primer dia del ano Traslado Primer dia del ano Carnaval Provincializacion de Cotopaxi Viernes Santo Dia del Trabajo Dia de la Madre-1 Dia de la Madre Batalla de Pichincha Traslado Batalla de Pichincha Provincializacion de Imbabura Primer Grito de Independencia Traslado Primer Grito de Independencia
date
2017-01-01 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0
2017-01-02 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0
2017-02-27 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
2017-02-28 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
2017-04-01 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0
# solución Pandas
X_holidays = pd.get_dummies(holidays)

X_holidays.head()

description_Batalla de Pichincha description_Carnaval description_Dia de la Madre description_Dia de la Madre-1 description_Dia del Trabajo description_Primer Grito de Independencia description_Primer dia del ano description_Provincializacion de Cotopaxi description_Provincializacion de Imbabura description_Traslado Batalla de Pichincha description_Traslado Primer Grito de Independencia description_Traslado Primer dia del ano description_Viernes Santo
date
2017-01-01 0 0 0 0 0 0 1 0 0 0 0 0 0
2017-01-02 0 0 0 0 0 0 0 0 0 0 0 1 0
2017-02-27 0 1 0 0 0 0 0 0 0 0 0 0 0
2017-02-28 0 1 0 0 0 0 0 0 0 0 0 0 0
2017-04-01 0 0 0 0 0 0 0 1 0 0 0 0 0
# Unión a los datos de entrenamiento
X2 = X.join(X_holidays, on='date').fillna(0.0)

X2.head()

const trend s(2,7) s(3,7) s(4,7) s(5,7) s(6,7) s(7,7) sin(1,freq=M) cos(1,freq=M) ... description_Dia de la Madre-1 description_Dia del Trabajo description_Primer Grito de Independencia description_Primer dia del ano description_Provincializacion de Cotopaxi description_Provincializacion de Imbabura description_Traslado Batalla de Pichincha description_Traslado Primer Grito de Independencia description_Traslado Primer dia del ano description_Viernes Santo
date
2017-01-01 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.000000 1.000000 ... 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0
2017-01-02 1.0 2.0 1.0 0.0 0.0 0.0 0.0 0.0 0.201299 0.979530 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0
2017-01-03 1.0 3.0 0.0 1.0 0.0 0.0 0.0 0.0 0.394356 0.918958 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
2017-01-04 1.0 4.0 0.0 0.0 1.0 0.0 0.0 0.0 0.571268 0.820763 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
2017-01-05 1.0 5.0 0.0 0.0 0.0 1.0 0.0 0.0 0.724793 0.688967 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0

5 rows × 29 columns

Vamos a entrenar el modelo estacional con las features de vacaciones añadidas. ¿Los valores ajustados parecen haber mejorado?

model = LinearRegression().fit(X2, y)

y_pred = pd.Series(model.predict(X2), index=X2.index)
ax = y.plot(**plot_params, alpha=0.5, title="Average Sales", ylabel="items sold")
ax = y_pred.plot(ax=ax, label="Seasonal")
ax.legend();

png

El modelo ha mejorado las predicciones en los días festivos.

Fuente: Kaggle

updatedupdated2022-05-242022-05-24