<jupyter_start><jupyter_text>Manipulation des données et premières statistiques  0 La librairie PandasPandas est une bibliothèque écrite pour le langage de programmation Python permettant la manipulation et l'analyse des données. Nous allons ici détailler l'utilisation des deux structures de base de ce package que sont les Series et DataFrame.  Les objets Series de PandasOn charge le package Pandas<jupyter_code>import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline<jupyter_output><empty_output><jupyter_text>La première structure de Pandas est l'objet **Series**.On peut créer une Series à partir d'une liste:<jupyter_code>liste = pd.Series([8, 18, 10, 15])
liste<jupyter_output><empty_output><jupyter_text>Les objets DataFrame de Pandas On peut créer un DataFrame à partir d'une liste ou array:<jupyter_code>liste = np.array([[ 8,  0], 
         [18,  7],
         [10,  1,],
         [14,  3]])
df = pd.DataFrame(liste)
df<jupyter_output><empty_output><jupyter_text>Créer une nouvelle colonne à partir d'une liste<jupyter_code>df[2]=["Paris", "Lille","Montpellier","Toulouse"]
df<jupyter_output><empty_output><jupyter_text>Renommer les colonnes:<jupyter_code>df.columns = ['temperature' , 'precipitation', 'ville']
df<jupyter_output><empty_output><jupyter_text>On peut également définir une colonne comme index<jupyter_code>df = df.set_index("ville")
df<jupyter_output><empty_output><jupyter_text>Manipulation des colonnesSur un Dataframe, on peut:- selectionner  une colonne avec :<jupyter_code>df['temperature'] # ou df.temperature<jupyter_output><empty_output><jupyter_text>- construire de nouvelles colonnes à partir de colonnes existantes<jupyter_code>df["mon_calcul"] = 2 * df.temperature + df.precipitation
df<jupyter_output><empty_output><jupyter_text>Manipulation des lignesOn extrait une ligne par son index en utilisant :<jupyter_code>df.loc["Toulouse"]<jupyter_output><empty_output><jupyter_text>On extrait une ligne par sa position en utilisant<jupyter_code>df.iloc[3]<jupyter_output><empty_output><jupyter_text>On extrait des valeurs à partir de listes :<jupyter_code>df.loc[["Toulouse","Paris"],["precipitation"]]<jupyter_output><empty_output><jupyter_text>On extrait des valeurs à partir des indexs<jupyter_code>df.iloc[1:3,1:2]<jupyter_output><empty_output><jupyter_text>1 Les outils pour importer les données Dans le cadre de ce Notebook, pour chaque jeu de données trop volumineux, nous conservons une version avec un extrait des données dans ce répertoire et un lien vers un autre site avec les données complètes (le code pour récupérer ces fichiers volumineux est présent sous forme de commentaire.  Importer des données externesL’une des forces de Pandas est l’importation et l’exportation des données. Ce package possède un ensemble de fonctions très large pour charger des données en mémoire et les exporter dans divers formats. Nous allons développer de nombreux exemples. Importer un fichier csvLa fonction *read_csv()* de Pandas est une fonction avec un nombre de paramètres impressionnant, nous ne nous concentrons ici que sur quelques-uns qui sont importants.Dans le cas d’un fichier csv classique, un seul paramètre est nécessaire. Il s’agit du chemin vers le fichier. Votre fichier peut se trouver directement sur votre machine mais aussi en ligne. Dans ce cas, il vous suffit de rentrer une adresse web. D’autres paramètres pourront vous être utiles lors du traitement de csv :- *delimiter* : afin de donner le format des séparateurs entre valeurs dans le fichier. Utile dans le cas d’un csv avec des séparateurs points-virgules,- *decimal* : afin de spécifier le séparateur décimal. Utile dans le cas d’un csv avec des séparateurs décimaux utilisant une virgule,- *index_col* : afin de spécifier la position de la colonne servant d’index dans le DataFrame créé (attention les colonnes sont toujours indexées à 0),- *header* : afin de dire si le titre de la colonne se trouve dans la première ligne. Si ce n’est pas le cas, on peut utiliser le paramètre names afin de fournir une liste avec le nom des colonnes pour le DataFrame,- *dtypes* : dans le cas de gros jeux de données, il peut être intéressant de fournir une liste de types de colonnes ou un dictionnaire afin d’éviter à Python d’avoir à les deviner (ce qui vous évitera certains warnings),- de nombreux autres paramètres, notamment sur le traitement des données manquantes, sur la transformation des dates, sur le codage des chaînes de caractères…On utilise *pd.read_csv()* pour lire un fichier csvDans ce cas, on va récupérer les données des logements AirBnB de Paris.<jupyter_code># Import d'un extrait
listing=pd.read_csv("../data/listing_extrait.csv", index_col=0)
# Import du fichier complet
#listing=pd.read_csv("https://www.stat4decision.com/listing.csv.gz", index_col=0)
listing.head()<jupyter_output><empty_output><jupyter_text>**Remarque** Pour importer un fichier csv très volumineux, il existe deux solutions:- l'option 'chunksize' de read_csv qui permet de lire des morceaux d'un fichier de manière itérative- la librairie **Dask**  Importer un fichier ExcelMicrosoft Excel reste l’un des outils de base pour traiter de la donnée. Dans la plupart des projets de data science, vous serez amené à croiser un fichier Excel, que ce soit pour stocker des données ou pour stocker des références ou des informationsannexes.Pandas possède des outils pour importer des données en Excel sans avoir à passerpar une transformation en csv (souvent fastidieuse si vous avez des classeurs avec denombreuses feuilles).  pd.read_excel() :Cette approche ressemble à l’importation en csv. Pour récupérer le fichier Excel, il faut connaître le nom ou la position de la feuille qui nous intéresse :<jupyter_code># on peut ajouter le nom de la feuille
frame_credit=pd.read_excel("../data/credit.xlsx", sheet_name="donnees")
frame_credit.head(5)
# on peut utiliser uniquement certaines colonnes
frame_credit_af=pd.read_excel("../data/credit.xlsx", sheet_name="donnees", usecols="A:E")
frame_credit.head(5)<jupyter_output><empty_output><jupyter_text>Importer une table issue d’une base de données SQLLe langage SQL  est un langage central de la science des données. La majorité desbases de données relationnelles peuvent être requêtées en utilisant le langage SQL.C’est d’ailleurs aujourd’hui l’un des trois langages les plus utilisés par le data scientist(après Python et R). SQL va vous permettre d’extraire des tables de données quipourront ensuite être chargées en mémoire dans des DataFrames. Pour passer de la base SQL à Python, il faut donc un connecteur permettant dese connecter à la base et de faire des requêtes directement dessus. Un packagecentral de Python est très utile dans ce but : c’est SQLalchemy qui a aujourd’huiremplacé les nombreux packages spécifiques qui pouvaient exister afin de requêterdes bases de données SQL en fonction du type de base : MySQL, PostgreSQL,SQLlite… SQLalchemy a l’avantage de fournir une seule approche.On va utliser :<jupyter_code>from sqlalchemy import create_engine
ma_con = create_engine("sqlite:///../data/salaries.sqlite")
# on peut vérifier le nom des tables
ma_con.table_names()
# on peut charger les données
frame_sql = pd.read_sql_query("SELECT * FROM Salaries", ma_con)
frame_sql.head()<jupyter_output><empty_output><jupyter_text>2 Décrire et transformer des colonnes 2.1 Décrire la structure de vos données - On charge un nouveau jeu de données sur les salaires des employés de la ville de Boston<jupyter_code>boston=pd.read_csv("../data/employee-earnings-report-2017.csv")<jupyter_output><empty_output><jupyter_text>Quel que soit le type de structure que vous utilisez ; les arrays, les Series oules DataFrame, on utilise généralement une propriété de ces objets : la propriété    .shape. Celle-ci renvoie toujours un tuple, qui aura autant d’éléments que de dimensionsdans vos données.<jupyter_code>boston.shape<jupyter_output><empty_output><jupyter_text>On peut on obtenir le nom des colonnes en utilisant :<jupyter_code>boston.columns<jupyter_output><empty_output><jupyter_text>Cette information est importante mais reste peu détaillée. Lorsqu’on travaille surun DataFrame, on va chercher à avoir beaucoup plus de détails. Pour cela, nous allonsutiliser la méthode .info().<jupyter_code>boston.info()<jupyter_output><empty_output><jupyter_text>Une autre étape importante est l’étude de l’aspect de notre DataFrame, on peutpar exemple afficher les premières lignes du jeu de données.<jupyter_code>boston.head()
# on modifie les colonnes numériques 
# (nous repalerons de problème de type plus tard)
for col in boston.columns :
    if boston[col].dtype==object :
        boston[col]=pd.to_numeric(boston[col].str.replace(r"\(.*\)","")\
                                  .str.replace(",","").str.strip("$"),
                                  errors='ignore')
boston.head()<jupyter_output><empty_output><jupyter_text>Statistiques pour données quantitativesLorsqu'on calcule des statistiques descriptives spécifiques aux données quantitatives sur un DataFrame complet, Pandas n’affiche des résultats que pour les variables quantitatives (sans message d’erreur pour les colonnes non quantitatives).Statistiques descriptives de baseQuelques méthodes statistiques universelles de Pandas :<jupyter_code># moyenne
boston.mean()<jupyter_output><empty_output><jupyter_text>De manière similaire, on dispose des fonctions suivantes:- variance : .var()- écart-type: .std()- médiane : .median()Une autre fonction intéressante est la méthode .describe() qui affiche un certain nombre de statistiques pour les variables quantitatives (elle ne fait que cela par défaut mais nous verrons plus loin qu’elle peut s’appliquer aux variables qualitatives).<jupyter_code>boston.describe()<jupyter_output><empty_output><jupyter_text>On peut égalisement visualiser nos données. Pandas hérite de nombreuses fonctions de matplotlib :<jupyter_code>boston.hist(figsize=(10,10))
plt.show()<jupyter_output><empty_output><jupyter_text>Statistiques pour données qualitativesLes statistiques descriptives pour des variables qualitatives sont très différentes de celles pour des variables quantitatives. Ainsi, on s’intéresse généralement au mode et à la fréquence des modalités de la variable:<jupyter_code># nombre de modalités
boston['DEPARTMENT NAME'].nunique()
# liste de modalités
boston['DEPARTMENT NAME'].unique()[:10]
# liste et fréquence d’apparition des modalités
boston["DEPARTMENT NAME"].value_counts()
boston["DEPARTMENT NAME"].value_counts().head(10).plot(kind='bar')<jupyter_output><empty_output><jupyter_text>**Remarque**: la méthode .value_counts() possède un certain nombre de paramètres pour inclure les données manquantes, normaliser les résultats...  2.2 Quelles transformations pour les colonnes de vos données ? Votre objectif en tant que data scientist est d’extraire le plus d’information possible de ces données. Pour cela, il va falloir les mettre en forme de manière intelligente. Nous allons étudier différentes transformations souvent nécessaires pour travailler sur des données :- 1. les changements de types,- 2. la déduplication,- 3. le traitement des données manquantes,- 4. le traitement des colonnes avec des données qualitatives,- 5. les transformations numériques.  2.2.1 Les changements de typesLe typage des colonnes d’un DataFrame ou d’un array est très important pour tous les traitements en data science.Nous nous concentrons ici sur les structures en DataFrame de Pandas. Pandas va automatiquement inférer les types si vous ne lui avez pas spécifié de type à l’importationdes données ou à la création du DataFrame.Par défaut, Pandas va utiliser trois types principaux :- les entiers (int en 32 ou en 64 bits),- les nombres décimaux (float en 32 ou 64 bits),- les objets object qui rassemblent la plupart des autres types.On trouvera aussi des booléens et tous les types définis par NumPy. La base de données listing de AirBnB est obtenue par scrapping web et certaines informations ne peuvent pas être traitées directement. En effet, lorsqu’on affiche lesinformations sur les colonnes, on voit que la colonne price est typée en Object alors qu’il s’agit de valeurs décimales.<jupyter_code>listing["price"].head()<jupyter_output><empty_output><jupyter_text>Pour nous débarrasser du $ en première position :<jupyter_code># remplace tous les $
listing["price"].str.replace("$","")<jupyter_output><empty_output><jupyter_text>Il reste deux étapes à réaliser : éliminer les virgules et transformer la variable envariable numérique :<jupyter_code>listing["price"]= pd.to_numeric(listing["price"].str.replace("$","").str.replace(",",""))
listing["price"].head()<jupyter_output><empty_output><jupyter_text>Nous avons donc réussi à modifier notre colonne. Si on étudie la colonne "instant_bookable", on veut pouvoir prendre en compte cette colonne pour la passer en booléen :<jupyter_code>listing["instant_bookable"].head()
listing["instant_bookable_bool"]= listing["instant_bookable"].replace({"f" : False,"t" : True})
listing.instant_bookable_bool.head(3)<jupyter_output><empty_output><jupyter_text>Il existe de nombreux cas de nettoyages de données basées sur des erreurs detypage.   2.2.2 La gestion des duplications de lignesIl arrive souvent dans des données que des lignes soient dupliquées par erreur ou que vous désiriez vérifier la duplication de certaines lignes.Pandas possède deux outils pour traiter ce type de données : duplicated() et drop_duplicated().Si nous voulons vérifier si des lignes sont dupliquées dans le DataFrame sur les employés de la ville de Boston, il nous suffit de faire :<jupyter_code>boston.duplicated().sum()<jupyter_output><empty_output><jupyter_text>Il s’avère qu’il n’y a aucune duplication. Nous aurions pu nous concentrer uniquementsur le nom, le département et le titre des employés :<jupyter_code>cols = ['NAME','DEPARTMENT NAME','TITLE']
boston.duplicated(cols).sum()
# on a donc quatre éléments dupliqués, on peut maintenant les visualiser :

boston[boston.duplicated(cols, keep=False)].sort_values(by= cols)<jupyter_output><empty_output><jupyter_text>Nous pouvons maintenant nous débarrasser des duplications, on utilisera pour cela :<jupyter_code>boston_no_dup=boston.drop_duplicates(['NAME','DEPARTMENT NAME','TITLE'],keep="first")<jupyter_output><empty_output><jupyter_text>Dans ce cas, on garde le premier. On peut demander à garder le dernier (last) eton utilisera des tris afin d’ordonner les résultats pour se débarrasser des duplicationsnon pertinentes.  2.2.3 Le traitement des données manquantesLes données manquantes sont un domaine de la data science à part entière. Leur traitement nécessite une réflexion bien au-delà de quelques lignes de codes.Dans tous vos projets data science, vous serez confronté à des données manquantes, deux startégies peuvent pricipalement être adoptées:- supprimer les données manquantes,- attribuer des valeurs à ces données manquantes, on parle de complétion. Nous présenterons quelques startégies Pour compléter les valeurs manquantes.**Remarque**: Lorsque vous importez des données avec Pandas, celui-ci va automatiquementremplacer les données manquantes par des codes nan. Pandas utilise le type np.nan de Numpy. Lors de l'application de fonctions sur une série (ex: la moyenne .mean()), les données manquantes sont ignorées. Cela évite des bugs mais cela peut-être trompeur également. On n'a pas forcément conscience qu'il nous manque des données à l'usage car on ne reçoit pas de message d'erreur La suppression des données manquantesL’approche la plus simple pour traiter des données manquantes est de supprimer les observations comportant des données manquantes.Pandas comporte de nombreuses méthodes pour cela. Si nous prenons les données sur les salaires des employés de la ville de Boston, nous pouvons utiliser :<jupyter_code># la table globale
boston.shape
# la table lorsqu’on retire les lignes avec données manquantes
boston.dropna().shape
# la table lorsqu’on retire les colonnes avec des données manquantes
boston.dropna(axis = 1).shape<jupyter_output><empty_output><jupyter_text>On voit que dans cette table de nombreuses données manquantes existent surtout sur huit colonnes. Quatre colonnes sont complètes. La complétion par la moyenne, ou la médiane Il existe deux moyens de compléter des données par la moyenne ou par la médiane.Un premier en utilisant Pandas :<jupyter_code># pour la moyenne
boston.INJURED= boston.INJURED.fillna(boston.INJURED.mean())     
# pour la médiane
boston.RETRO= boston.RETRO.fillna(boston.RETRO.median())<jupyter_output><empty_output><jupyter_text>Le package Scikit-Learn permet aussi de faire des remplacements par la moyenneou la médiane :<jupyter_code>from sklearn.impute import SimpleImputer
# on crée un objet de cette classe avec la stratégie d’imputation comme
# paramètre
imputer = SimpleImputer(strategy = "mean")
# on construit un nouveau jeu de données en appliquant la méthode
# .fit_transform()
boston_imputee= imputer.fit_transform(boston.select_dtypes(np.number))<jupyter_output><empty_output><jupyter_text>2.2.4 Le traitement des colonnes avec des données qualitativesLes données qualitatives sont extrêmement présentes dans les données. Dès que vous travaillez sur des données socio-démographiques sur des individus, vous allez rencontrer des données qualitatives. Le traitement des données qualitatives est souvent négligé dans les ouvrages de traitement de la donnée. Il est donc primordial de bien expliquer le traitement qu’elles requièrent. Le type categoricalLes données qualitatives sont des valeurs textuelles par défaut. Pandas propose un type spécifique pour traiter ce type de données. Le type categorical permet d’optimiser le traitement de ce type de données.Il permet de créer et de transformer des données de ce type. Vous avez importé des données avec des variables qualitatives, Pandas va automatiquement les considérer comme du type object. Vous pourrez le voir en utilisant la propriété .dtype.Si vous désirez transformer ce type en un type categorical, vous pouvez utiliser la fonction pd.Categorical() :<jupyter_code>var_quali=pd.Categorical(["Boston","Paris","Londres","Paris", "Boston"])
var_quali.value_counts()<jupyter_output><empty_output><jupyter_text>La transformation des donnéesPour traiter des données qualitatives, il faudra les transformer. En effet, les algorithmes que vous aurez à utiliser sont basés sur des données numériques et donc des variables quantitatives.Si vous travaillez sur des données nominales, il va falloir transformer les variables en indicatrices. C’est-à-dire que vous allez obtenir une colonne pour chaque modalité de votre variable qualitative.Cette approche peut être appliquée avec deux packages que nous utilisons souvent : Pandas et Scikit-Learn.Dans le cadre de nos données sur les logements AirBnB, nous avons plusieurs variables qualitatives, notamment roomtype qui a trois modalités :<jupyter_code>listing["room_type"].value_counts()<jupyter_output><empty_output><jupyter_text>Approche Pandas avec get_dummies() :<jupyter_code>frame_room_type = pd.get_dummies(listing["room_type"])
frame_room_type.head()<jupyter_output><empty_output><jupyter_text>Cette fonction crée un nouveau DataFrame  Approche Scikit-Learn avec OneHotEncoder() :<jupyter_code># on importe la classe
from sklearn.preprocessing import OneHotEncoder
# on crée un objet de la classe OneHotEncoder
encode=OneHotEncoder(sparse = False)
# on l'applique directement sur la colonnes initiale
array_out=encode.fit_transform(np.array(listing["room_type"]).reshape(-1,1))
# on transforme la sortie en DataFrame
pd.DataFrame(array_out, columns=listing["room_type"].unique()).head()<jupyter_output><empty_output><jupyter_text>2.2.5 Les transformations numériquesLorsque vous travaillez sur des données, un certain nombre de transformations de base sont nécessaires. Deux packages pourront être utiles pour ce type de transformations : Scikit-Learn, Pandas.Avec Pandas, la plupart des transformations se font en faisant les calculs directement en utilisant les fonctions universelles de Pandas.Avec Scikit-Learn, l’approche est légèrement différente. Dans ce cas, on utilise des classes permettant de transformer les données.Nous utiliserons les données sur les employés de la ville de Boston desquelles nous extrayons les colonnes numériques :<jupyter_code>boston_num=boston.select_dtypes(include=[np.number])<jupyter_output><empty_output><jupyter_text>Centrer et réduire les données<jupyter_code># avec Pandas pour centrer et réduire
boston_std=boston_num.apply(lambda x : (x-x.mean())/x.std())
boston_std.head()
# avec Scikit-Learn, on utilisera la classe StandardScaler :
from sklearn.preprocessing import StandardScaler
scaler=StandardScaler(with_mean=True, with_std=True)
rescaled=scaler.fit_transform(boston_num)
pd.DataFrame(rescaled, index=boston_num.index, columns=boston_num.columns).head()<jupyter_output><empty_output><jupyter_text>Changer d’échelleOn utilise pour passer à une échelle 0-100 :<jupyter_code># avec Pandas
boston_0_100=boston_num.apply(lambda x : (x-x.min())/(x.max()-x.min())*100)
boston_0_100.head()
# avec Scikit-Learn
from sklearn.preprocessing import MinMaxScaler
minmaxscaler=MinMaxScaler((0,100))
boston_0_100=minmaxscaler.fit_transform(boston_num)
boston_0_100=pd.DataFrame(boston_0_100, index=boston_num.index, columns=boston_num.columns)
boston_0_100.head()<jupyter_output><empty_output><jupyter_text>3 Utilisation du groupby pour décrire des données PrincipeLa méthode .groupby est une méthode qui permet de construire un objet à partir d’un DataFrame. Cet objet sépare les données en fonction des modalités d’une ou de plusieurs variables qualitatives. On obtiendra ainsi de manière quasi-immédiate desindicateurs par modalités. De nombreuses méthodes sont disponibles sur ces objets groupby afin de maximiser la simplicité de manipulation de données.Généralement, on suppose que le groupby est basé sur trois étapes : séparation/application et combinaison.Par exemple, sur les données AirBnB, on peut faire cela par type de chambres :<jupyter_code>listing_group_room = listing.groupby("room_type")
listing_group_room["price"].mean()<jupyter_output><empty_output><jupyter_text>On sépare et on calcule la moyenne, et on rassemble les résultats dans un nouvel objet. On affiche donc dans un objet Series les prix moyens par type de chambre. On voit ici qu’on a utilisé la méthode .mean() de la classe des objets groupby.  Opérations sur les objets groupbyOn peut très simplement obtenir des statistiques plus poussées avec des groupby.De nombreuses méthodes de transformation de données pourront être appliquées avec une étape .groupby().  Apply : une méthode importante pour manipuler vos groupbyLa méthode apply permet d’appliquer n’importe quelle fonction sur vos données.Si par exemple, vous désirez calculer l’écart salarial au sein de chaque département sur les données des salariés de la ville de Boston, vous allez devoir utiliser la différence entre le maximum et le minimum. Il n’existe pas de fonction universelle. Nous allons donc utiliser un groupby et la méthode apply :<jupyter_code>diff_salaires_dep = boston.groupby('DEPARTMENT NAME')['TOTAL EARNINGS']\
                        .apply(lambda x : x.max()-x.min())
diff_salaires_dep.sort_values(ascending = False).head()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.06993996569468268, 'fraction_numerical': 0.005060034305317324, 'mean_word_length': 6.166871542716656, 'pattern_counts': {'":': 0, '<': 173, '<?xml version=': 0, '>': 173, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '43', 'dir': 'jupyter-structured-clean-dedup', 'id': '240128', 'license': 'Apache-2.0', 'n_tokens_mistral': 7437, 'n_tokens_neox': 7412, 'n_words': 2657, 'path': 'formation/02-Machine learning avec Pandas et Scikit-learn/2.1 Manipulation et description des donnees.ipynb', 'repo_name': 'meteofrance/formation-machine-learning', 'size': '23320'}