Zicht op de Toekomst

Een tijdreeks modellering van de drukte per dag

Zicht op de Toekomst

In dit artikel wordt toegelicht hoe we een prognose hebben geïmplementeerd in ons BI-platform om een beter beeld te creëren van de dag drukte in de komende periode.

Het artikel zal ingaan op de technische analyse en modellering van de tijdreeks van het project. Specifiek zal de aandacht besteedt worden aan de tijdreeks van een van onze afhaalklanten.


Aanleiding

Het doel van dit project is om met een bepaalde zekerheid een uitspraak te kunnen doen over de drukte per dag van het aantal orders in de komende weken. Deze informatie is gewenst en kan gebruikt worden voor twee verschillende doeleinden:

  1. Om de planners te kunnen ondersteunen met vervroegde informatie middels een week-model voor onze grootste afhaalklanten;
  2. Om enige uitspraak te kunnen doen over de globale trend over het jaar met behulp van een zes maanden prognose.
Geografische weergave van de afhaalprognoses

Voor een aantal van onze klanten bestaat een patroon in het aantal afhaalorders dat zij per dag vervoeren. Middels een wiskundig model proberen we zogoed mogelijk dit patroon op te pakken en door te trekken in een prognose voor de komende week. Op deze manier heeft de planning een beter beeld van de dagdrukte die er naar waarschijnlijkheid gaat komen en kan hier beter op anticiperen.

Evenals een weekprognose hebben we voor het totaal aantal orders een model geïmplementeerd welke enkele maanden vooruit voorspeld. Door deze trend visueel te presenteren kan een beter beeld gevormd worden van de drukke en minder drukke weken in het jaar. De planners kunnen op basis van deze informatie proactief inspringen op vakantieaanvragen van chauffeurs, inhuren van charters en het aannemen van additionele orders.

Technische uitwerking

Voor de tijdreeks modellering is gebruikgemaakt van de ‘statsmodels’ API in Python, welke geavanceerde tijdreeks analyse en modellerings mogelijkheden tot beschikking heeft. Daarnaast geeft de API toegang tot vele visualisaties die noodzakelijk zijn in een tijdreeks modellering.

Tijdreeks analyse

De historische data wordt per klant op basis van afhaaladres opgehaald en met een frequentie per werkdag geïmplementeerd, waarbij de missende dagen worden gevuld met de waarde 0. In onderstaande figuur wordt het verloop van het aantal orders weergegeven van een van onze afhaalklanten.

dataset = dataset.set_index("Date").asfreq('B', fill_value=0)
series = dataset["Orders"]

series.plot();

Duidelijk te zien is een lineair stijgende trend over tijd welke sterk afhankelijk is van het wekelijkse seizoenscomponent (de dagen in de week). Enkele uitschieters in de data zijn zichtbaar en zullen gecorrigeerd moeten worden om ze minimaal effect te laten hebben op de prognoses.

series_adjusted = series.copy()

temp = series_adjusted.shift(5)
series_adjusted.loc[(series_adjusted > 48)|(series_adjusted == 0)] = np.nan
series_adjusted = series_adjusted.fillna(temp)

Uit het verloop van het aantal orders is zichtbaar dat de reeks niet stationair is, wat inhoudt dat er enkele transformaties uitgevoerd dienen te worden. Om statistisch te toetsen of de reeks stationair is wordt een Dickey Fuller test uitgevoerd.

from statsmodels.tsa.stattools import adfuller
def dickey_fuller_test(timeseries):
    dfoutput = pd.Series(
        adfuller(timeseries)[0:4],
        index=["statistic", "p-value", "# lags", "# observations"]
    )
    print(dfoutput)

Onderstaande toets is uitgevoerd op een getransformeerde reeks waarbij een differentie van 1 is toegepast. Met een zeer kleine p-waarde kunnen we aannemen dat de reeks stationair is.

Model specificatie

De reeks laat een duidelijk patroon van een wekelijks seizoenscomponent zien en dient stationair gemaakt te worden. Vervolgens wordt er een autoregressie en moving average lag meegenomen in het model.

from statsmodels.tsa.statespace.sarimax import SARIMAX
mod = SARIMAX(
    series_adjusted,
    order=(1, 1, 1),
    seasonal_order=(0, 1, 1, 5),
    freq='B'
)
res = mod.fit()

Het resultaat van het model wordt samengevat in onderstaande uitdraai en laat verschillende karakteristieken van het model zien. Alle coëfficiënten verschillen significant van nul, daarnaast geeft de Ljung-Box toets aan dat we mogen aannemen dat de residuen ‘witte ruis’ is.

print(res.summary(alpha=0.1))

Er wordt een prognose van 10 werkdagen gemaakt en geplot met de laatste 4 weken aan data. Een 90% betrouwbaarheidsinterval is voldoende gevonden en zullen in de prognoses meegenomen worden.

forecast = res.get_forecast(steps=10)
forecast_df = forecast.summary_frame(alpha=0.1)
fig, ax = plt.subplots(figsize=(20, 10))

# True
series.tail(20).plot(ax=ax)
# Forecast
forecast_df["mean"].plot(ax=ax, style="k--")

ax.fill_between(
    forecast_df.index,
    forecast_df["mean_ci_lower"], forecast_df["mean_ci_upper"],
    color='k', alpha=0.1
);

Om enig inzicht te geven in de betrouwbaarheid van het model wordt gekeken naar de residuen welke onderheven zijn aan verschillende aannames behorend bij een dergelijk tijdreeks model.

In de correlogram zijn geen significante correlaties te zien wat een indicatie is dat het model niet veel verbeterd kan worden. In de residuen lijkt geen verband te zitten en normaal verdeeld te zijn, uitschieters zijn aanwezig en lijken toe te nemen bij latere waarnemingen.

fig = plt.figure(figsize=(20, 10))
fig = res.plot_diagnostics(fig=fig, lags=20)

Afsluitend zouden extra variabelen kunnen worden toegevoegd om te kijken of dit de prognoses verbeterd. Dit zouden markt specifieke variabelen kunnen zijn als het weer of economische toestand, wat per klant afhankelijk is. Ook zou rekening gehouden kunnen worden met het aantal orders dat door klanten al is ingeschoten in de database op de tijd van het maken van de prognoses.

Implementatie

Het model is gepubliceerd op Azure en wordt op dagelijks niveau getriggerd om een vijf-daagse prognose te maken. Deze prognoses worden op verschillende wijze gevisualiseerd in Power BI.

Visuele presentatie en inzichten

Over het verloop van de weken is een redelijk patroon zichtbaar welke zich in bepaalde mate ieder jaar herhaald. In de vakantie maanden is een daling van ongeveer 20% ten opzichte van gemiddeld zichtbaar. Na deze periode is juist een sterke stijging in de trend te zien.

Verloop van het totaal aantal orders per week inclusie prognose

Naast de prognose wordt een betrouwbaarheid meegegeven om duidelijk te maken dat de cijfers kunnen afwijken van de berekende trend en dat hier met enige voorzichtigheid mee moet worden gewerkt.

“We hoeven niet te weten hoe warm het precies wordt, enkel of we een winterjas aanmoeten of niet”

Bij het opstellen van het model is gebruikgemaakt van drie jaar aan data. Bij enkele klanten is vanwege extreme veranderingen in de trend gewerkt met een kleinere dataset. In het model wordt gebruik gemaakt van een jaarlijks en wekelijks seizoenscomponent en wordt gecorrigeerd voor afwijkingen in de recente data.

Onvoorziene uitkomst van de toekomst

De toekomst is onzeker en er zullen altijd veranderingen in de trend over tijd blijven ontstaan. Om de gebruiker de mogelijkheid te geven de prognoses enigszins aan te passen wordt gebruikgemaakt van een correctie- en groeifactor welke gebruikers zelf naar wens kunnen aanpassen.

Parameter om de prognoses te beïnvloeden op onvoorziene toekomst

Wanneer er een situatie optreedt dat het verloop in orders hoger uitpakt of een klant een hogere groei doormaakt dan tot nu toe, kan dit effect in bepaalde mate zelf toevoegt worden.

Om de kwaliteit van een model te kunnen waarborgen zal periodiek gekeken moeten worden naar of de gemodelleerde trend zich blijft voordoen of dat een nieuw model getraind moet worden. Daarnaast moet in samenwerking met de planners bijgehouden worden hoe we de informatie zo relevant mogelijk kunnen houden en presenteren.