.. _pandasiteratorcorrectionrst: =========================================== 2A.data - Pandas et itérateurs - correction =========================================== .. only:: html **Links:** :download:`notebook `, :downloadlink:`html `, :download:`python `, :downloadlink:`slides `, :githublink:`GitHub|_doc/notebooks/td2a/pandas_iterator_correction.ipynb|*` `pandas `__ a tendance a prendre beaucoup d’espace mémoire pour charger les données, environ trois fois plus que sa taille sur disque. Quand la mémoire n’est pas assez grande, que peut-on faire ? .. code:: ipython3 from jyquickhelper import add_notebook_menu add_notebook_menu() .. contents:: :local: .. code:: ipython3 from sklearn.datasets import load_iris data = load_iris() import pandas df = pandas.DataFrame(data.data) df.column = "X1 X2 X3 X4".split() df["target"] = data.target df.head(n=2) .. raw:: html
0 1 2 3 target
0 5.1 3.5 1.4 0.2 0
1 4.9 3.0 1.4 0.2 0
On mélange les lignes car le dataframe est trié et cela masque quelques effets aléatoires. .. code:: ipython3 import sklearn.utils df = sklearn.utils.shuffle(df) .. code:: ipython3 df.to_csv("iris.txt", sep="\t", index=False) Exercice 1 : itérer sur un grand fichier ---------------------------------------- Le paramètre *iterator* de la fonction `read_csv `__ sert à parcourir un fichier par blocs dont la taille est définie par le paramètres *chunksize*. La fonction `read_csv `__ implémente ce mécanisme. .. code:: ipython3 for df in pandas.read_csv("iris.txt", sep="\t", iterator=True, chunksize=60): print(df.shape) .. parsed-literal:: (60, 5) (60, 5) (30, 5) Exercice 2 : split train test ----------------------------- La solution proposée est implémentée par `train_test_split `__. .. code:: ipython3 from sklearn.model_selection import train_test_split df_full_it = pandas.read_csv('iris.txt', sep='\t', chunksize=10, encoding='utf-8', engine='python') first_exec = True for df_full_chunk in df_full_it: X_train_chunk, X_test_chunk = train_test_split(df_full_chunk) if first_exec: X_train_chunk.to_csv("X_train.csv", sep="\t", index=False) X_test_chunk.to_csv("X_test.csv", sep="\t", index=False) first_exec = False else: X_train_chunk.to_csv("X_train.csv", sep="\t", index=False, mode='a', header=False) X_test_chunk.to_csv("X_test.csv", sep="\t", index=False, mode='a', header=False) .. code:: ipython3 X_train = pandas.read_csv("X_train.csv", sep="\t") X_train.head(n=2) .. raw:: html
0 1 2 3 target
0 5.7 2.9 4.2 1.3 1
1 6.1 3.0 4.6 1.4 1
.. code:: ipython3 X_train.shape .. parsed-literal:: (105, 5) .. code:: ipython3 X_test = pandas.read_csv("X_test.csv", sep="\t") X_test.head(n=2) .. raw:: html
0 1 2 3 target
0 7.0 3.2 4.7 1.4 1
1 5.6 3.0 4.5 1.5 1
.. code:: ipython3 X_test.shape .. parsed-literal:: (45, 5) .. code:: ipython3 X_train.groupby("target").count() .. raw:: html
0 1 2 3
target
0 30 30 30 30
1 36 36 36 36
2 39 39 39 39
La répartition des classes n’est pas uniforme. Lorsque les classes sont bien représentées, cela ne nuit pas aux résultats. En revanche, des classes sous-représentées pourraient disparaître de l’une des deux parties. Exercice 3 : stratify ? ----------------------- Le paramètre *stratify* est intéressant pour un problème de classification et quand une classes et sous-représentée. Il est fort probable que cette classe ne soit pas assez représentée dans l’un des deux jeux et c’est pourquoi il existe une option pour imposer un nombre d’exemples de cette dans chaque des deux jeux (train, test). La qualité des modèles est accrue tout comme la qualité des sondages sur un `échantillonnage stratifié `__. Si jamais tout ces exemples sont placés au début du gros fichier à lire, le programme commence à avoir une fausse idée de la répartition des classes. La seule façon de faire est de faire d’abord une division train/test par classe (indiqué par la variable de stratification) puis de recomposer les bases d’apprentissage et de tests en imposant les proportions voulues. .. code:: ipython3 from sklearn.model_selection import train_test_split strat_name = 'target' df_full_it = pandas.read_csv('iris.txt', sep='\t', chunksize=10, encoding='utf-8', dtype=object, engine='python') strat_list = [] for df_full_chunk in df_full_it: for current_strat in df_full_chunk[strat_name].unique(): if str(current_strat) in strat_list: selection = df_full_chunk[df_full_chunk[strat_name] == current_strat] selection.to_csv("strat_{}.csv".format(current_strat), sep="\t", index=False, encoding='utf-8', mode='a', header=False) else: strat_list.append(str(current_strat)) selection = df_full_chunk[df_full_chunk[strat_name] == current_strat] selection.to_csv("strat_{}.csv".format(current_strat), sep="\t", index=False, encoding='utf-8') first_exec = True for current_strat in strat_list: df_strat_it = pandas.read_csv("strat_{}.csv".format(current_strat), sep='\t', chunksize=1000, encoding='utf-8', dtype=object, engine='python') for df_strat_chunk in df_strat_it: X_train_chunk, X_test_chunk = train_test_split(df_strat_chunk) if first_exec: X_train_chunk.to_csv("X_train_strat.csv", sep="\t", index=False, encoding='utf-8') X_test_chunk.to_csv("X_test_strat.csv", sep="\t", index=False, encoding='utf-8') first_exec = False else: X_train_chunk.to_csv("X_train_strat.csv", sep="\t", index=False, encoding='utf-8', mode='a', header=False) X_test_chunk.to_csv("X_test_strat.csv", sep="\t", index=False, encoding='utf-8', mode='a', header=False) On vérifie que l’échantillon est stratifiée. .. code:: ipython3 X_train = pandas.read_csv("X_train_strat.csv", sep="\t") X_train.head(n=2) .. raw:: html
0 1 2 3 target
0 5.1 3.8 1.6 0.2 0
1 4.7 3.2 1.3 0.2 0
.. code:: ipython3 X_train.groupby("target").count() .. raw:: html
0 1 2 3
target
0 37 37 37 37
1 37 37 37 37
2 37 37 37 37
.. code:: ipython3 X_test = pandas.read_csv("X_test_strat.csv", sep="\t") X_test.head(n=2) .. raw:: html
0 1 2 3 target
0 5.7 3.8 1.7 0.3 0
1 4.8 3.4 1.9 0.2 0
.. code:: ipython3 X_test.groupby("target").count() .. raw:: html
0 1 2 3
target
0 13 13 13 13
1 13 13 13 13
2 13 13 13 13
Les classes sont bien réparties. Exercice 4 : quelques idées pour un group by ? ---------------------------------------------- La fonction `groupby `__ implémente une façon de faire. Il faut distinguer deux cas possibles. Premier cas, l’agrégation aboutit à un résultat qui tient en mémoire auquel on peut s’en sortir aisément. Dans le second cas, l’agrégation ne tient pas en mémoire et il faudra probablement passer par un fichier intermédiaire ou comme le suggère la solution proposé par `StreamingDataFrame.groupby `__, on peut agréger un cours de route à condition que l’agrégration implémentée soit compatible avec ce type de méthode. Notes ----- *Notebook en grande partie issue de la contribution des étudiants.*