Prévision de l’étendue des eaux de surface dans le delta de l’Okavango

En utilisant les résultats de notre débit modélisé, des précipitations en amont et de la régression vectorielle automatique

Charger des paquets

Importez les packages Python utilisés pour l’analyse.

[1]:
from statsmodels.tsa.vector_ar.var_model import VAR
from statsmodels.tsa.stattools import adfuller
from statsmodels.tools.eval_measures import rmse
from statsmodels.tsa.stattools import grangercausalitytests
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

Paramètres d’analyse

[2]:
freq = 'Q-DEC'
data = 'results/okavango_all_datasets.csv'
modelled_discharge = 'results/modelled_discharge_Q-DEC.csv'

Lire les données

[3]:
we=pd.read_csv(data, index_col='time', parse_dates=True)[['water_extent', 'okavango_rain']]
df=pd.read_csv(modelled_discharge, index_col=0, parse_dates=True)
df=df.join(we).drop(['water_discharge'],axis=1).dropna()
df.head(2)
[3]:
upstream_rainfall predicted_discharge water_extent okavango_rain
2013-12-31 100.739380 11902.015733 52.2846 61.88324
2014-03-31 120.626221 27059.141065 163.8918 157.48248

Tester la causalité

La base de l’autorégression vectorielle est que chacune des séries temporelles du système s’influence mutuellement. Autrement dit, vous pouvez prédire la série avec ses valeurs passées ainsi que celles d’autres séries du système. Ci-dessous, nous effectuons un test de causalité de Granger pour voir si les variables sont liées les unes aux autres. Dans le tableau qui est imprimé après l’exécution des deux cellules ci-dessous, une valeur p donnée est < niveau de signification (0,05), puis la série X correspondante (colonne) provoque le Y (ligne).

[4]:
# def grangers_causation_matrix(data, variables, maxlag=3, test='ssr_chi2test', verbose=False):
#     """Check Granger Causality of all possible combinations of the Time series.
#     The rows are the response variable, columns are predictors. The values in the table
#     are the P-Values. P-Values lesser than the significance level (0.05), implies
#     the Null Hypothesis that the coefficients of the corresponding past values is
#     zero, that is, the X does not cause Y can be rejected.

#     data      : pandas dataframe containing the time series variables
#     variables : list containing names of the time series variables.
#     """
#     df = pd.DataFrame(np.zeros((len(variables), len(variables))), columns=variables, index=variables)
#     for c in df.columns:
#         for r in df.index:
#             test_result = grangercausalitytests(data[[r, c]], maxlag=maxlag, verbose=False)
#             p_values = [round(test_result[i+1][0][test][1],4) for i in range(maxlag)]
#             if verbose: print(f'Y = {r}, X = {c}, P Values = {p_values}')
#             min_p_value = np.min(p_values)
#             df.loc[r, c] = min_p_value
#     df.columns = [var + '_x' for var in variables]
#     df.index = [var + '_y' for var in variables]
#     return df

# grangers_causation_matrix(df, variables = df.columns)

Effectuer des back-tests itératifs pour valider la capacité de prévision

Nous allons ici effectuer une prévision, mais sur un intervalle de la série temporelle pour lequel nous disposons déjà d’observations. Cela nous permettra de tester la capacité de prévision du modèle.

Commençons d’abord par initier un modèle

[5]:
model=VAR(df, freq=freq)

Trouver le meilleur ordre de décalage en utilisant le critère d’information d’Akaike (AIC)

[6]:
#calculate AIC
x=model.select_order(maxlags=3)
aic=pd.read_html(x.summary().as_html(),header=0, index_col=0)[0][['AIC']]
aic['AIC']=[float(i[0:5]) for i in aic.values.flatten()]
lag_order = aic.idxmin().values[0]

#plot
aic.plot()
plt.scatter(lag_order, aic.loc[lag_order], color='r', label='best lag order')
plt.title('AIC Values')
plt.ylabel('AIC')
plt.xlabel('Lag Order')
plt.legend()
print("Lag order to use is "+str(lag_order))
Lag order to use is 1
../../../../_images/sandbox_notebooks_Use_cases_Okavango_6_Forecasting_water_extent_12_1.png

Créer et adapter un modèle sur les données

En utilisant l’ordre de décalage défini ci-dessus

[7]:
model=VAR(df, freq=freq)
model_fit = model.fit(1)
[8]:
forecast_length = lag_order
[9]:
n_windows = int((len(df) / forecast_length) - 1)
window_size = forecast_length

aa = window_size
dfs=[]
for i in range(0, n_windows):
    start=aa+lag_order
    end=(aa)
    backtest_input = df.values[-start:-end]
    fc = model_fit.forecast(y=backtest_input, steps=window_size)
    if i == 0:
        index=df.index[-end:]
    else:
        index=df.index[-end:-(end-window_size)]
    fc = pd.DataFrame(fc, index=index, columns=df.columns)
    dfs.append(fc)
    aa+=window_size

#concat results together
fc=pd.concat(dfs)
fc.columns = fc.columns.get_level_values(0)
[10]:
test=df[df.index.isin(fc.index)]
for i in test.columns:
    print('rmse value for', i, 'is : ', round(rmse(fc[[i]], test[[i]])[0],2))
rmse value for upstream_rainfall is :  48.79
rmse value for predicted_discharge is :  15682.77
rmse value for water_extent is :  183.53
rmse value for okavango_rain is :  44.29
[11]:
col = 'water_extent'
plt.figure(figsize=(12,4))
plt.plot(df.index, df[col], label=col+' obs',linestyle='dashed', marker='o')
fc[col].plot(label=col+' forecast',linestyle='dashed', marker='o')
plt.ylabel(col)
plt.title(col+" forecast vs actual")
plt.legend();

../../../../_images/sandbox_notebooks_Use_cases_Okavango_6_Forecasting_water_extent_19_0.png

Prévision de l’étendue de l’eau

[12]:
#make final predictions
model = VAR(endog=df, freq=freq)
model_fit = model.fit(lag_order)

pred = model_fit.forecast(df.values[-model_fit.k_ar:], steps=forecast_length)
[13]:
#converting predictions to dataframe
cols = df.columns
fc = pd.DataFrame(index=range(0,len(pred)), columns=[cols])
for j in range(0,len(cols)):
    for i in range(0, len(pred)):
        fc.iloc[i][j] = pred[i][j]

fc.index = pd.date_range(freq=freq, start=df.index[-1], periods=len(fc)+1)[1:]
fc.head()

[13]:
upstream_rainfall predicted_discharge water_extent okavango_rain
2021-06-30 -12.659938 34071.346128 244.632889 -7.007658
[14]:
plt.figure(figsize=(12,4))
plt.plot(df.index, df['water_extent'], label='observations',linestyle='dashed', marker='o')
plt.plot(fc.index, fc[['water_extent']], label='forecast',linestyle='dashed', marker='o')
plt.ylabel('Water Extent')
plt.title("Water Extent Forecast")
# plt.ylim(0.0,0.9)
plt.legend();
../../../../_images/sandbox_notebooks_Use_cases_Okavango_6_Forecasting_water_extent_23_0.png

Informations Complémentaires

Licence : Le code de ce carnet est sous licence Apache, version 2.0 <https://www.apache.org/licenses/LICENSE-2.0>. Les données de Digital Earth Africa sont sous licence Creative Commons par attribution 4.0 <https://creativecommons.org/licenses/by/4.0/>.

Contact : Si vous avez besoin d’aide, veuillez poster une question sur le canal Slack Open Data Cube <http://slack.opendatacube.org/>`__ ou sur le GIS Stack Exchange en utilisant la balise open-data-cube (vous pouvez consulter les questions posées précédemment ici). Si vous souhaitez signaler un problème avec ce bloc-notes, vous pouvez en déposer un sur Github.

Dernier test :

[15]:
from datetime import datetime
datetime.today().strftime('%Y-%m-%d')
[15]:
'2023-08-21'