<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Timeseries
Ce notebook présente quelques étapes simples pour une série temporelle. La plupart utilise le module statsmodels.tsa.
Step1:  Données
Les données sont artificielles mais simulent ce que pourraient être le chiffre d'affaires d'un magasin de quartier, des samedi très forts, une semaine morne, un Noël chargé, un été plat.
Step2:  Premiers graphiques
La série a deux saisonnalités, hebdomadaire, mensuelle.
Step3:  Elle a une vague tendance, on peut calculer un tendance à l'ordre 1, 2, ...
Step4:  Autocorrélations...
Step5:  La première saisonalité apparaît, 7, 14, 21... Les autocorrélations partielles confirment cela, plutôt 7 jours.
Step6:  Comme il n'y a rien le dimanche, il vaut mieux les enlever. Garder des zéros nous priverait de modèles multiplicatifs.
Step7:  On décompose la série en tendance + saisonnalité. Les étés et Noël apparaissent.
Step8:  On cherche maintenant la saisonnalité de la série débarrassée de sa tendance herbdomadaire. On retrouve la saisonnalité mensuelle.
Step9:  Test de stationnarité
Le test KPSS permet de tester la stationnarité d'une série.
Step10:  Comme ce n'est pas toujours facile à interpréter, on simule une variable aléatoire gaussienne donc sans tendance.
Step11:  Et puis une série avec une tendance forte.
Step12:  Une valeur forte indique une tendance et la série en a clairement une.
Prédiction
Les modèles AR, ARMA, ARIMA se concentrent sur une série à une dimension. En machine learning, il y a la série et plein d'autres informations. On construit une matrice avec des séries décalées.
Step13:  On ajoute ou on réécrit le jour de la semaine qu'on utilise comme variable supplémentaire.
Step14:  Etrange autant de grandes valeurs, cela veut dire que la tendance est trop forte pour calculer des corrélations, il vaudrait mieux tout recommencer avec la série $\Delta Y_t = Y_t - Y_{t-1}$. Bref, passons...
Step15:  Une régression linéaire car les modèles linéaires sont toujours de bonnes baseline et pour connaître le modèle simulé, on ne fera pas beaucoup mieux.
Step16:  On retrouve la saisonnalité, $Y_t$ et $Y_{t-6}$ sont de mèches.
Step17:  Auparavant (l'année dernière en fait), je construisais deux bases, apprentissage et tests, comme ceci 
Step18:  Et puis scikit-learn est arrivée avec TimeSeriesSplit.
Step19:  Et on calé une forêt aléatoire...
Step20:  2 ans coupé en 5, soit tous les 5 mois, ça veut dire que ce découpage inclut parfois Noël, parfois l'été et que les performances y seront très sensibles.
Step21:  On compare avec le $r_2$ avec le même $r_2$ obtenu en utilisant $Y_{t-1}$, $Y_{t-2}$, ... $Y_{t-d}$ comme prédiction.
Step22:  En fait le jour de la semaine est une variable catégorielle, on crée une colonne par jour.
Step23:  On met tout dans un pipeline parce que c'est plus joli, plus pratique aussi.
Step24:  C'est plus facile à voir visuellement.
Step26:  Templating
Complètement hors sujet mais utile.
<ASSISTANT_TASK:>
Python Code:
from jyquickhelper import add_notebook_menu
add_notebook_menu()
%matplotlib inline
Explanation: Timeseries
Ce notebook présente quelques étapes simples pour une série temporelle. La plupart utilise le module statsmodels.tsa.
End of explanation
from ensae_teaching_cs.data import generate_sells
import pandas
df = pandas.DataFrame(generate_sells())
df.head()
Explanation: Données
Les données sont artificielles mais simulent ce que pourraient être le chiffre d'affaires d'un magasin de quartier, des samedi très forts, une semaine morne, un Noël chargé, un été plat.
End of explanation
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 2, figsize=(14, 4))
df.iloc[-30:].set_index('date').plot(ax=ax[0])
df.set_index('date').plot(ax=ax[1])
ax[0].set_title("chiffre d'affaire sur le dernier mois")
ax[1].set_title("chiffre d'affaire sur deux ans");
Explanation: Premiers graphiques
La série a deux saisonnalités, hebdomadaire, mensuelle.
End of explanation
from statsmodels.tsa.tsatools import detrend
notrend = detrend(df.value, order=1)
df["notrend"] = notrend
df["trend"] = df['value'] - notrend
ax = df.plot(x="date", y=["value", "trend"], figsize=(14,4))
ax.set_title('tendance');
Explanation: Elle a une vague tendance, on peut calculer un tendance à l'ordre 1, 2, ...
End of explanation
from statsmodels.tsa.stattools import acf
cor = acf(df.value)
cor
fig, ax = plt.subplots(1, 1, figsize=(14,2))
ax.plot(cor)
ax.set_title("Autocorrélogramme");
Explanation: Autocorrélations...
End of explanation
from statsmodels.tsa.stattools import pacf
from statsmodels.graphics.tsaplots import plot_pacf
plot_pacf(df.value, lags=50);
Explanation: La première saisonalité apparaît, 7, 14, 21... Les autocorrélations partielles confirment cela, plutôt 7 jours.
End of explanation
df["weekday"] = df.date.dt.weekday
df.head()
df_nosunday = df[df.weekday != 6]
df_nosunday.head(n=10)
fig, ax = plt.subplots(1, 1, figsize=(14,2))
cor = acf(df_nosunday.value)
ax.plot(cor)
ax.set_title("Autocorrélogramme");
plot_pacf(df_nosunday.value, lags=50);
Explanation: Comme il n'y a rien le dimanche, il vaut mieux les enlever. Garder des zéros nous priverait de modèles multiplicatifs.
End of explanation
from statsmodels.tsa.seasonal import seasonal_decompose
res = seasonal_decompose(df_nosunday.value, freq=7)
res.plot();
plt.plot(res.seasonal[-30:])
plt.title("Saisonnalité");
cor = acf(res.trend[5:-5]);
plt.plot(cor);
Explanation: On décompose la série en tendance + saisonnalité. Les étés et Noël apparaissent.
End of explanation
res_year = seasonal_decompose(res.trend[5:-5], freq=25)
res_year.plot();
Explanation: On cherche maintenant la saisonnalité de la série débarrassée de sa tendance herbdomadaire. On retrouve la saisonnalité mensuelle.
End of explanation
from statsmodels.tsa.stattools import kpss
kpss(res.trend[5:-5])
Explanation: Test de stationnarité
Le test KPSS permet de tester la stationnarité d'une série.
End of explanation
from numpy.random import randn
bruit = randn(1000)
kpss(bruit)
Explanation: Comme ce n'est pas toujours facile à interpréter, on simule une variable aléatoire gaussienne donc sans tendance.
End of explanation
from numpy.random import randn
from numpy import arange
bruit = randn(1000) * 100 + arange(1000) / 10
kpss(bruit)
Explanation: Et puis une série avec une tendance forte.
End of explanation
from statsmodels.tsa.tsatools import lagmat
lag = 8
X = lagmat(df_nosunday["value"], lag)
lagged = df_nosunday.copy()
for c in range(1,lag+1):
    lagged["lag%d" % c] = X[:, c-1]
lagged.tail()
Explanation: Une valeur forte indique une tendance et la série en a clairement une.
Prédiction
Les modèles AR, ARMA, ARIMA se concentrent sur une série à une dimension. En machine learning, il y a la série et plein d'autres informations. On construit une matrice avec des séries décalées.
End of explanation
lagged["weekday"] = lagged.date.dt.weekday
X = lagged.drop(["date", "value", "notrend", "trend"], axis=1)
Y = lagged["value"]
X.shape, Y.shape
from numpy import corrcoef
corrcoef(X)
Explanation: On ajoute ou on réécrit le jour de la semaine qu'on utilise comme variable supplémentaire.
End of explanation
X.columns
Explanation: Etrange autant de grandes valeurs, cela veut dire que la tendance est trop forte pour calculer des corrélations, il vaudrait mieux tout recommencer avec la série $\Delta Y_t = Y_t - Y_{t-1}$. Bref, passons...
End of explanation
from sklearn.linear_model import LinearRegression
clr = LinearRegression()
clr.fit(X, Y)
from sklearn.metrics import r2_score
r2_score(Y, clr.predict(X))
clr.coef_
Explanation: Une régression linéaire car les modèles linéaires sont toujours de bonnes baseline et pour connaître le modèle simulé, on ne fera pas beaucoup mieux.
End of explanation
for i in range(1, X.shape[1]):
    print("X(t-%d)" % (i), r2_score(Y, X.iloc[:, i]))
Explanation: On retrouve la saisonnalité, $Y_t$ et $Y_{t-6}$ sont de mèches.
End of explanation
n = X.shape[0]
X_train = X.iloc[:n * 2//3]
X_test = X.iloc[n * 2//3:]
Y_train = Y[:n * 2//3]
Y_test = Y[n * 2//3:]
Explanation: Auparavant (l'année dernière en fait), je construisais deux bases, apprentissage et tests, comme ceci :
End of explanation
from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)
for train_index, test_index in tscv.split(lagged):
    data_train, data_test = lagged.iloc[train_index, :], lagged.iloc[test_index, :]
    print("TRAIN:", data_train.shape, "TEST:", data_test.shape)
Explanation: Et puis scikit-learn est arrivée avec TimeSeriesSplit.
End of explanation
import warnings
from sklearn.ensemble import RandomForestRegressor
clr = RandomForestRegressor()
def train_test(clr, train_index, test_index):
    data_train = lagged.iloc[train_index, :]
    data_test = lagged.iloc[test_index, :]
    clr.fit(data_train.drop(["value", "date", "notrend", "trend"], 
                            axis=1), 
            data_train.value)
    r2 = r2_score(data_test.value,
                  clr.predict(data_test.drop(["value", "date", "notrend",
                                              "trend"], axis=1).values))
    return r2
warnings.simplefilter("ignore")
last_test_index = None
for train_index, test_index in tscv.split(lagged):
    r2 = train_test(clr, train_index, test_index)        
    if last_test_index is not None:
        r2_prime = train_test(clr, last_test_index, test_index)        
        print(r2, r2_prime)
    else:
        print(r2)
    last_test_index = test_index
Explanation: Et on calé une forêt aléatoire...
End of explanation
from sklearn.metrics import r2_score
r2 = r2_score(data_test.value,
              clr.predict(data_test.drop(["value", "date", "notrend",
                                          "trend"], axis=1).values))
r2
Explanation: 2 ans coupé en 5, soit tous les 5 mois, ça veut dire que ce découpage inclut parfois Noël, parfois l'été et que les performances y seront très sensibles.
End of explanation
for i in range(1, 9):
    print(i, ":", r2_score(data_test.value, data_test["lag%d" % i]))
lagged[:5]
Explanation: On compare avec le $r_2$ avec le même $r_2$ obtenu en utilisant $Y_{t-1}$, $Y_{t-2}$, ... $Y_{t-d}$ comme prédiction.
End of explanation
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
cols = ['lag1', 'lag2', 'lag3',
        'lag4', 'lag5', 'lag6', 'lag7', 'lag8']
ct = ColumnTransformer(
        [('pass', "passthrough", cols),
         ("dummies", OneHotEncoder(), ["weekday"])])
pred = ct.fit(lagged).transform(lagged[:5])
pred
Explanation: En fait le jour de la semaine est une variable catégorielle, on crée une colonne par jour.
End of explanation
from sklearn.pipeline import make_pipeline
from sklearn.decomposition import PCA, TruncatedSVD 
cols = ['lag1', 'lag2', 'lag3',
        'lag4', 'lag5', 'lag6', 'lag7', 'lag8']
model = make_pipeline(
         make_pipeline(
                 ColumnTransformer(
                    [('pass', "passthrough", cols),
                     ("dummies", make_pipeline(OneHotEncoder(), 
                                               TruncatedSVD(n_components=2)), ["weekday"])]),
        LinearRegression()))
model.fit(lagged, lagged["value"])
Explanation: On met tout dans un pipeline parce que c'est plus joli, plus pratique aussi.
End of explanation
from mlinsights.plotting import pipeline2dot
dot = pipeline2dot(model, lagged)
from jyquickhelper import RenderJsDot
RenderJsDot(dot)
r2_score(lagged['value'], model.predict(lagged))
Explanation: C'est plus facile à voir visuellement.
End of explanation
from jinja2 import Template
template = Template('Hello {{ name }}!')
template.render(name='John Doe')
template = Template(
{{ name }}
{{ "-" * len(name) }}
Possède :
{% for i in range(len(meubles)) %}
- {{meubles[i]}}{% endfor %}
)
meubles = ['table', "tabouret"]
print(template.render(name='John Doe Doe', len=len,
                     meubles=meubles))
Explanation: Templating
Complètement hors sujet mais utile.
End of explanation
<END_TASK>