%matplotlib inline
import matplotlib.pyplot as plt
plt.style.use('ggplot')
import pyensae
from pyquickhelper.helpgen import NbImage
from jyquickhelper import add_notebook_menu
add_notebook_menu()

"""
Explanation: Cube multidimensionnel - énoncé
Ce notebook aborde différentes solutions pour traiter les données qu'on représente plus volontiers en plusieurs dimensions. Le mot-clé associé est OLAP ou cube OLAP. Mondrian est une solution open source, cubes est écrit en python. Kylin propose ce service sur des données stockées sur Hadoop. L'objectif est ici de découvrir pas d'explorer ces solutions.
End of explanation
"""


NbImage("cube1.png")

"""
Explanation: Représentation
Le module pandas manipule des tables et c'est la façon la plus commune de représenter les données. Lorsque les données sont multidimensionnelles, on distingue les coordonnées des valeurs :
End of explanation
"""


NbImage("cube2.png")

"""
Explanation: Dans cet exemple, il y a :

3 coordonnées : Age, Profession, Annéee
2 valeurs : Espérance de vie, Population

On peut représenter les donnés également comme ceci :
End of explanation
"""


from actuariat_python.data import table_mortalite_euro_stat 
table_mortalite_euro_stat()

import os
os.stat("mortalite.txt")

import pandas
df = pandas.read_csv("mortalite.txt", sep="\t", encoding="utf8", low_memory=False)
df.head()

"""
Explanation: C'est assez simple. Prenons un exemple : table de mortalité de 1960 à 2010 qu'on récupère à l'aide de la fonction table_mortalite_euro_stat. C'est assez long (4-5 minutes) sur l'ensemble des données car elles doivent être prétraitées (voir la documentation de la fonction). Pour écouter, il faut utiliser le paramètre stop_at.
End of explanation
"""


from actuariat_python.data import table_mortalite_euro_stat 
table_mortalite_euro_stat()
import pandas
df = pandas.read_csv("mortalite.txt", sep="\t", encoding="utf8", low_memory=False)
df.columns

"""
Explanation: Cubes de données avec xarray
création
Cette réprésentation sous forme de table n'est pas toujours très lisible. Les informations sont souvent répétées et les données sont vraiment multidimensionnelles. Le module xarray propose de distinguer les coordonnées des valeurs pour proposer des manipulations plus intuitives. Le module propose un DataFrame multidimensionnelle DataSet.
End of explanation
"""


df2 = df[["annee", "age_num","indicateur","pays","genre","valeur"]].dropna().reset_index(drop=True)
df.columns, df2.columns

df2.head()

df2["indicateur"] = df2["indicateur"].astype(str)
df2["genre"] = df2["genre"].astype(str)
df2["pays"] = df2["pays"].astype(str)

df2.dtypes

"""
Explanation: Dans ce cas-ci, pour reprendre la temrinologie du module xarray, nous avons :

les dimensions : annee, age_num, pays, genre
les valeurs : une valeur par indicateur

On peut passer d'un DataFrame à un DataSet de la façon suivante :

les colonnes indéxées représentent les dimensions
les colonnes non indéxées sont les valeurs

On garde supprime les colonnes qui ne nous intéressent pas et les valeurs manquantes :
End of explanation
"""


piv = df2.pivot_table(index=["annee", "age_num","pays","genre"],
                columns=["indicateur"],
               values="valeur")
piv.head()

piv.dtypes

"""
Explanation: L'opération complexe consiste à faire passer les valeurs de la colonne indicateur en tant que colonnes. C'est l'objet de la méthode pivot_table :
End of explanation
"""


import xarray
ds = xarray.Dataset.from_dataframe(piv)
ds

"""
Explanation: Les données sont maintenant prêtes à passer sous xarray :
End of explanation
"""


ds.sel(pays=["FR"])

"""
Explanation: sélection
Il est facile ensuite d'extraire les données d'un pays avec la méthode sel :
End of explanation
"""


ds.sel(pays=["FR", "BE"])

"""
Explanation: Ou de plusieurs pays :
End of explanation
"""


ds.sel(pays="FR", annee=2000)

"""
Explanation: Ou plusieurs dimensions :
End of explanation
"""


(ds.sel(pays="FR", annee=2010, genre="T")["LIFEXP"] - 
 ds.sel(pays="FR", annee=2000, genre="T")["LIFEXP"]).to_dataframe().head()

"""
Explanation: Pour accéder à la série LIFEXP pour les hommes, les années 2000 et 2010, le pays FR, on fait la différence, puis on la récupère sous forme de DataFrame :
End of explanation
"""


(ds.sel(pays="FR", annee=2010, genre=["F","M"])["LIFEXP"] - 
 ds.sel(pays="UK", annee=2010, genre=["F","M"])["LIFEXP"]).to_dataframe().head()

"""
Explanation: On a gagné presque deux ans et demi d'espérance de vie à la naissance en 10 ans.
End of explanation
"""


ds.sel(annee=2010,age_num=1,genre="T")["LIFEXP"].to_dataframe() \
            .sort_values("LIFEXP", ascending=False) \
            .plot(y="LIFEXP", kind="bar", figsize=(16,6), ylim=[65,85])

ds.sel(annee=2010,genre="T",pays="FR")["LIFEXP"].plot()

"""
Explanation: Quelques graphes
End of explanation
"""


ds.assign(LIFEEXP_add = ds.LIFEXP-1)

meanp = ds.mean(dim="pays")
ds1, ds2 = xarray.align(ds, meanp, join='outer')

joined = ds1.assign(meanp = ds2["LIFEXP"])

joined.to_dataframe().head()

"""
Explanation: Exercice 1 : que font les lignes suivantes ?
On pourra s'aider des pages :

align nad reindex
transforming datasets
End of explanation
"""


ds.data_vars

try:
    ds.to_netcdf('mortalite.nc')
except ValueError as e:
    # it breaks with pandas 0.17
    # xarray has to be updated
    print("l'écriture a échoué")
    pass

"""
Explanation: Lire, écrire des datasets
Le module xarray s'appuie sur le module netCDF4 qui lui-même est un wrapper poython de la libraire netCDF-c. Celle-ci est spécialisée dans la lecture et l'écriture de données scientifiques. Concrètement, ce n'est pas xarray qui s'en occupe mais netCDF4. Cela explique la syntaxe décrite par Serialization and IO :
End of explanation
"""


import os
if os.path.exists("mortalite.nc"):
    os.stat('mortalite.nc').st_size, os.stat('mortalite.txt').st_size

"""
Explanation: C'est un format binaire plus efficace que le format texte :
End of explanation
"""


if os.path.exists("mortalite.nc"):
    ds_lu = xarray.open_dataset('mortalite.nc')
    ds_lu

"""
Explanation: On relit :
End of explanation
"""


pays = list(_.values for _ in ds["pays"])
pays[:5]

for p in pays[:5]:
    print("enregistre", p)
    d = ds.sel(pays=[p])
    try:
        d.to_netcdf("mortalite_pays_%s.nc" % p)
    except ValueError:
        print("l'écriture a échoué pour", p)

"""
Explanation: Le module xarray propose également de lire des données de plusieurs fichiers pour ne former qu'un seul dataset (voir Combining multiple files) :
End of explanation
"""


import os
if os.path.exists("mortalite_pays_AM.nc"):
    ds_lu2 = xarray.open_mfdataset('mortalite_pays*.nc')
    ds_lu2

"""
Explanation: On relit :
End of explanation
"""


