<jupyter_start><jupyter_text>![image](./images/pandas.png)Pandas est le package de prédilection pour traiter des données structurées.Pandas est basé sur 2 structures extrêmement liées les Series et le DataFrame.Ces deux structures permettent de traiter des données sous forme de tableaux indexés.Les classes de Pandas utilisent des classes de Numpy, il est donc possible d'utiliser les fonctions universelles de Numpy sur les objets Pandas.<jupyter_code># on importe pandas avec :
import pandas as pd
import numpy as np
%matplotlib inline<jupyter_output><empty_output><jupyter_text>Les Series de Pandas- Les Series sont indexées, c'est leur avantage sur les arrays de NumPy- On peut utiliser les fonctions `.values` et `.index` pour voir les différentes parties de chaque Series- On définit une Series par `pd.Series([,], index=['','',])`- On peut appeler un élément avec `ma_serie['France']`- On peut aussi faire des conditions :```pythonma_serie[ma_serie>5000000]``````'France' in ma_serie```- Les objets Series peuvent être transformés en dictionnaires en utilisant :`.to_dict()` **Exercice :**    Définir un objet Series comprenant la population de 5 pays puis afficher les pays ayant une population > 50’000’000.<jupyter_code>ser_pop = pd.Series([70,8,300,1200],index=["France","Suisse","USA","Chine"])
ser_pop
# on extrait une valeur avec une clé
ser_pop["France"]
# on peut aussi utiliser une position avec .iloc[]
ser_pop.iloc[0]
# on applique la condition entre []
ser_pop[ser_pop>50]<jupyter_output><empty_output><jupyter_text>D'autres opérations sur les objets series- Pour définir le nom de la Series, on utilise `.name`- Pour définir le titre de la colonne des observations, on utilise `.index.name` **Exercice :**    Définir les noms de l’objet et de la colonne des pays pour la Series précédente<jupyter_code>ser_pop.name = "Populations"
ser_pop.index.name = "Pays"
ser_pop<jupyter_output><empty_output><jupyter_text>Les données manquantesDans pandas, les données manquantes sont identifiés avec les fonctions de Numpy (`np.nan`). On a d'autres fonctions telles que :<jupyter_code>pd.Series([2,np.nan,4],index=['a','b','c'])
pd.isna(pd.Series([2,np.nan,4],index=['a','b','c']))
pd.notna(pd.Series([2,np.nan,4],index=['a','b','c']))<jupyter_output><empty_output><jupyter_text>Les dates avec pandas- Python possède un module datetime qui permet de gérer facilement des dates- Pandas permet d'appliquer les opérations sur les dates aux Series et aux DataFrame- Le format es dates Python est `YYYY-MM-DD HH:MM:SS`- On peut générer des dates avec la fonction `pd.date_range()` avec différente fréquences `freq=`- On peut utiliser ces dates comme index dans un DataFrame ou dans un objet Series- On peut changer la fréquence en utilisant `.asfreq()`- Pour transformer une chaine de caractère en date, on utilise `pd.to_datetime()` avec l’option `dayfirst=True` si on est dans le cas français-On pourra aussi spécifier un format pour accélérer le processus `%Y%m%d` **Exercice :**Créez un objet Series et ajoutez des dates partant du 3 octobre 2017 par jour jusqu’à aujourd’hui. Afficher le résultat dans un graphique (on utilisera la méthode `.plot()`<jupyter_code>dates = pd.date_range("2017-10-03", "2020-02-27",freq="W")

valeurs = np.random.random(size=len(dates))
ma_serie=pd.Series(valeurs, index =dates)
ma_serie.plot()
len(dates)<jupyter_output><empty_output><jupyter_text>Le DataFrame - Les DataFrame sont des objets très souples pouvant être construits de différentes façon- On peut les construire en récupérant des données copier / coller, où directement sur Internet, ou en entrant les valeurs manuellement- Les DataFrame se rapprochent des dictionnaires et on peut construire ces objets en utilisant `DataFrame(dico)`- De nombreux détails sur la création des DataFrame se trouve sur ce site :  Construction de DataFrameOn peut simplement construire un DataFrame avec le classe pd.DataFrame() à partir de différentes structures :<jupyter_code>frame1=pd.DataFrame(np.random.randn(10).reshape(5,2),
             index=["obs_"+str(i) for i in range(5)],
             columns=["col_"+str(i) for i in range(2)])
frame1<jupyter_output><empty_output><jupyter_text>Opérations sur les DataFrameOn peut afficher le nom des colonnes :<jupyter_code>print(frame1.columns)<jupyter_output>Index(['col_0', 'col_1'], dtype='object')<jupyter_text>On peut accéder à une colonne avec :- `frame1.col_0` : attention au cas de nom de colonnes avec des espaces...- `frame1['col_0']`On peut accéder à une cellule avec :- `frame1.loc['obs1','col_0']` : on utilise les index et le nom des colonnes- `frame1.iloc[1,0]` : on utilise les positions dans le DataFrame  Options de visualisation et de résuméPour afficher les 3 premières lignes, on peut utiliser :<jupyter_code>frame1.head(3)<jupyter_output><empty_output><jupyter_text>Pour afficher un résumé du DF :<jupyter_code>frame1.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
Index: 5 entries, obs_0 to obs_4
Data columns (total 2 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   col_0   5 non-null      float64
 1   col_1   5 non-null      float64
dtypes: float64(2)
memory usage: 120.0+ bytes<jupyter_text>Importer des données externesPandas est l'outil le plus efficace pour importer des données externes, il prend en charge de nombreux formats dont csv, Excel, SQL, SAS... Importation de données avec PandasQuel que soit le type de fichier, Pandas possède une fonction :```pythonframe=pd.read_...('chemin_du_fichier/nom_du_fichier',...)```Pour écrire un DataFrame dans un fichier, on utilise :```pythonframe.to_...('chemin_du_fichier/nom_du_fichier',...)``` **Exercice :**    Importer un fichier `.csv` avec `pd.read_csv()`. On utilisera le fichier "./data/airbnb.csv"<jupyter_code># on prend la colonne id comme index de notre DataFrame
airbnb = pd.read_csv("https://www.stat4decision.com/airbnb.csv",index_col="id")
airbnb.info()
# la colonne price est sous forme d'objet et donc de chaîne de caractères
# on a 2933 locations qui coûtent 80$ la nuit
airbnb["price"].value_counts()
dpt = pd.read_csv("./data/base-dpt.csv", sep = ";")
dpt.head()
dpt.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 1300 entries, 0 to 1299
Data columns (total 38 columns):
 #   Column         Non-Null Count  Dtype  
---  ------         --------------  -----  
 0   CODGEO         1300 non-null   int64  
 1   LIBGEO         1300 non-null   object 
 2   REG            1300 non-null   int64  
 3   DEP            1300 non-null   int64  
 4   P14_POP        1297 non-null   float64
 5   P09_POP        1297 non-null   float64
 6   SUPERF         1297 non-null   float64
 7   NAIS0914       1297 non-null   float64
 8   DECE0914       1297 non-null   float64
 9   P14_MEN        1297 non-null   float64
 10  NAISD16        1296 non-null   float64
 11  DECESD16       1296 non-null   float64
 12  P14_LOG        1297 non-null   float64
 13  P14_RP         1297 non-null   float64
 14  P14_RSECOCC    1297 non-null   float64
 15  P14_LOGVAC     1297 non-null   float64
 16  P14_RP_PROP    1297 non-null   float64
 17  NBMENFISC14    1280 non-null   float64
 18  PIMP14  [...]<jupyter_text>D'autres types de données JSONLes objets JSON ressemblent à des dictionnaires.On utilise le module `json` puis la fonction `json.loads()` pour transformer une entrée JSON en objet json HTMLOn utilise `pd.read_html(url)`. Cet fonction est basée sur les packages `beautifulsoup` et `html5lib`Cette fonction renvoie une liste de DataFrame qui représentent tous les DataFrame de la page. On ira ensuite chercher l'élément qui nous intéresse avec `frame_list[0]` **Exercice :**    Importez un tableau en html depuis la page<jupyter_code>bank = pd.read_html("http://www.fdic.gov/bank/individual/failed/banklist.html")
# read_html() stocke les tableaux d'une page web dans une liste
type(bank)
len(bank)
bank[0].head(10)
nba = pd.read_html("https://en.wikipedia.org/wiki/2018%E2%80%9319_NBA_season")
len(nba)
nba[3]<jupyter_output><empty_output><jupyter_text>Importer depuis ExcelOn a deux approches pour Excel :- On peut utiliser `pd.read_excel()`- On peut utiliser la classe `pd.ExcelFile()`Dans ce cas, on utilise :```pythonxlsfile=pd.ExcelFile('fichier.xlsx')xlsfile.parse('Sheet1')``` **Exercice :**     Importez un fichier Excel avec les deux approches, on utilisera : `credit2.xlsx` et `ville.xls`<jupyter_code>pd.read_excel("./data/credit2.xlsx",usecols=["Age","Gender"])
pd.read_excel("./data/credit2.xlsx",usecols="A:C")
credit2 = pd.read_excel("./data/credit2.xlsx", index_col="Customer_ID")
credit2.head()
# on crée un objet du type ExcelFile
ville = pd.ExcelFile("./data/ville.xls")
ville.sheet_names
# on extrait toutes les feuilles avec le mot ville dans le nom de la feuille dans une liste de dataframes
list_feuilles_ville = []
for nom in ville.sheet_names:
    if "ville" in nom:
         list_feuilles_ville.append(ville.parse(nom))<jupyter_output><empty_output><jupyter_text>On crée une fonction qui permet d'importer les feuilles excel ayant le terme nom_dans_feuille dans le nom de la feuille<jupyter_code>def import_excel_feuille(chemin_fichier, nom_dans_feuille = ""):
    """ fonction qui importe les feuilles excel ayant le terme nom_dans_feuille dans le nom de la feuille"""
    
    excel = pd.ExcelFile(chemin_fichier)
    list_feuilles = []
    for nom_feuille in excel.sheet_names:
        if nom_dans_feuille in nom_feuille:
            list_feuilles.append(excel.parse(nom))
    return list_feuilles
list_ain = import_excel_feuille("./data/ville.xls",nom_dans_feuille="ain")
list_ain[0].head()<jupyter_output><empty_output><jupyter_text>Importer des données SQLPandas possède une fonction `read_sql()` qui permet d’importer directement des bases de données ou des queries dans des DataFrameIl faut tout de même un connecteur pour accéder aux bases de donnéesPour mettre en place ce connecteur, on utlise le package SQLAlchemy.Suivant le type de base de données, on utilisera différents codes mais la structure du code est toujours la même<jupyter_code># on importe l'outil de connexion
from sqlalchemy import create_engine<jupyter_output><empty_output><jupyter_text>On crée une connexion```pythonconnexion=create_engine("sqlite:///(...).sqlite")``` On utlise une des fonctions de Pandas pour charger les données```pythonrequete="""select ... from ..."""frame_sql=pd.read_sql_query(requete,connexion)``` **Exercices :**    Importez la base de données SQLite salaries et récupérez la table Salaries dans un DataFrame<jupyter_code>connexion=create_engine("sqlite:///./data/salaries.sqlite")
connexion.table_names()
salaries = pd.read_sql_query("select * from salaries", con=connexion)
salaries.head()<jupyter_output><empty_output><jupyter_text>Importer depuis SPSSPandas possède une fonction `pd.read_spss()`Attention ! Il faut la dernière version de Pandas et installer des packages supplémentaires !**Exercice :** Importer le fichier SPSS se trouvant dans ./data/<jupyter_code>#base = pd.read_spss("./data/Base.sav")<jupyter_output><empty_output><jupyter_text>Les tris avec Pandas Pour effectuer des tris, on utilise :- `.sort_index()` pour le tri des index- `.sort_values()` pour le tri des données- `.rank()` affiche le rang des observationsIl peut y avoir plusieurs tris dans la même opération. Dans ce cas, on utilise des listes de colonnes :```pythonframe.sort_values(["col_1","col_2"])``` **Exercice :**     Triez les données sur les salaires en se basant sur le TotalPay et le JobTitle<jupyter_code>salaries.sort_values(["JobTitle","TotalPay"],ascending=[True, False])<jupyter_output><empty_output><jupyter_text>Les statistiques simplesLes Dataframe possèdent de nombreuses méthodes pour calculer des statistiques simples :- `.sum(axis=0)` permet de faire une somme par colonne- `.sum(axis=1)` permet de faire une somme par ligne- `.min()` et `.max()` donnent le minimum par colonne- `.idxmin()` et `.idxmax()` donnent l’index du minimum et du maximum- `.describe()` affiche un tableau de statistiques descriptives par colonne- `.corr()` pour calculer la corrélation entre les colonnes **Exercice :**    Obtenir les différentes statistiques descriptives pour les données AirBnB.On peut s'intéresser à la colonne `Price` (attention des prétraitements sont nécessaires)<jupyter_code># cette colonne est sous forme d'object, il va falloir la modifier
airbnb["price"].dtype
airbnb["price_num"] = pd.to_numeric(airbnb["price"].str.replace("$","")
                                   .str.replace(",",""))
airbnb["price_num"].dtype
airbnb["price_num"].mean()
airbnb["price_num"].describe()
# on extrait l'id de la location avec le prix max
airbnb["price_num"].idxmax()
# on affiche cette location
airbnb.loc[airbnb["price_num"].idxmax()]<jupyter_output><empty_output><jupyter_text>Calcul de la moyenne pondérée sur une enquête<jupyter_code>base = pd.read_csv("./data/Base.csv")
#moyenne pondérée
np.average(base["resp_age"],weights=base["Weight"])
# moyenne
base["resp_age"].mean()<jupyter_output><empty_output><jupyter_text>Utilisation de statsmodels<jupyter_code>from statsmodels.stats.weightstats import DescrStatsW
# on sélectionne les colonnes numériques
base_num = base.select_dtypes(np.number)
# on calcule les stats desc pondérées
mes_stat = DescrStatsW(base_num, weights=base["Weight"])
base_num.columns
mes_stat.var
mes_stat_age = DescrStatsW(base["resp_age"], weights=base["Weight"])
mes_stat_age.mean<jupyter_output><empty_output><jupyter_text>On va construire une fonction permettant de calculer les stat desc pondérées d'une colonne<jupyter_code>def stat_desc_w_ipsos(data, columns, weights):
    """ Cette fonction calcule et affiche les moyennes et écarts-types pondérés
    
    Input : - data : données sous forme de DataFrame
            - columns : nom des colonnes quanti à analyser
            - weights : nom de la colonne des poids
    """
    
    from statsmodels.stats.weightstats import DescrStatsW
    mes_stats = DescrStatsW(data[columns],weights=data[weights])
    print("Moyenne pondérée :", mes_stats.mean)
    print("Ecart-type pondéré :", mes_stats.std)
    
stat_desc_w_ipsos(base,"resp_age","Weight")<jupyter_output>Moyenne pondérée : 48.40297631233564
Ecart-type pondéré : 17.1309963999935<jupyter_text>Le traitement des données manquantes- Les données manquantes sont identifiées par `NaN`- `.dropna()` permet de retirer les données manquantes dans un objet Series et l’ensemble d’une ligne dans le cas d’un DataFrame- Pour éliminer par colonne, on utilise `.dropna(axis=1)`- Remplacer toutes les données manquantes `.fillna(valeur)`  Les jointures avec PandasOn veut joindre des jeux de données en utilisant des clés (variables communes)- `pd.merge()` permet de joindre deux DataFrame, on utilise comme options `on='key'`- On peut utiliser comme option `how=`, on peut avoir :    - `left` dans ce cas, on garde le jeu de données à gauche et pour les données de droite des valeurs manquantes sont ajoutées.    - `outer`, on garde toutes les valeurs des deux jeux de données    - ...- On peut avoir plusieurs clés et faire une jointure sur les deux clés `on=['key1','key2']`Pour plus de détails :  **Exercice :**    Joindre deux dataframes (credit1 et credit2).<jupyter_code>credit1 = pd.read_csv("./data/credit1.txt",sep="\t")
credit_global = pd.merge(credit1,credit2,how="inner",on="Customer_ID")
credit_global.head()<jupyter_output><empty_output><jupyter_text>On fait une jointure entre les données des locations Airbnb et les données de calendrier de remplissage des appartements<jupyter_code>airbnb_reduit = airbnb[["price_num","latitude","longitude"]]
calendar = pd.read_csv("https://www.stat4decision.com/calendar.csv.gz")
calendar.head()
new_airbnb = pd.merge(calendar,airbnb[["price_num","latitude","longitude"]], 
                      left_on = "listing_id",right_index=True)
new_airbnb.shape<jupyter_output><empty_output><jupyter_text>On veut extraire des statistiques de basePar exemple, la moyenne des prix pour les locations du 8 juillet 2018 :<jupyter_code>new_airbnb[new_airbnb["date"]=='2018-07-08']["price_num"].mean()<jupyter_output><empty_output><jupyter_text>On extrait le nombre de nuitées disponibles / occuppées :<jupyter_code>new_airbnb["available"].value_counts(normalize = True)<jupyter_output><empty_output><jupyter_text>Si on regarde le part de locations occuppées le 8 janvier 2019, on a :<jupyter_code>new_airbnb[new_airbnb["date"]=='2019-01-08']["available"].value_counts(normalize = True)<jupyter_output><empty_output><jupyter_text>La moyenne des prix des appartements disponibles le 8 juillet 2018 :<jupyter_code>new_airbnb[(new_airbnb["date"]=='2018-07-08')&(new_airbnb["available"]=='t')]["price_num"].mean()<jupyter_output><empty_output><jupyter_text>On transforme la colonne date qui est sous forme de chaîne de caractère en DateTime, ceci permet de faire de nouvelles opérations :<jupyter_code>new_airbnb["date"]= pd.to_datetime(new_airbnb["date"])
# on construit une colonne avec le jour de la semaine 
new_airbnb["jour_semaine"]=new_airbnb["date"].dt.day_name()<jupyter_output><empty_output><jupyter_text>La moyenne des pris des Samedi soirs disponibles est donc :<jupyter_code>new_airbnb[(new_airbnb["jour_semaine"]=='Saturday')&(new_airbnb["available"]=='t')]["price_num"].mean()<jupyter_output><empty_output><jupyter_text>Gestion des duplications- On utilise `.duplicated()` ou `.drop_duplicates()` dans le cas où on désire effacer les lignes se répétant- On peut se concentrer sur une seule variables en entrant directement le nom de la variable. Dans ce cas, c’est la première apparition qui compte. Si on veut prendre la dernière apparition, on utilise l’option `keep="last"`. On pourra avoir :```pythonframe1.drop_duplicates(["col_0","col_1"],keep="last")```  DiscrétisationPour discrétiser, on utilise la fonction `pd.cut()`, on va définir une liste de points pour discrétiser et on entre cette liste comme second paramètre de la fonction.Une fois discrétisé, on peut afficher les modalités obtenues en utilisant `.categories`On peut aussi compter les occurrence en utilisant `pd.value_counts()`Il est aussi possible d’entrer le nombre de segments comme second paramètreOn utilisera aussi `qcut()` **Exercice :**    Créez une variable dans le dataframe AirBnB pour obtenir des niveaux de prix.<jupyter_code>airbnb["price_disc1"]=pd.cut(airbnb["price_num"],bins=5)
airbnb["price_disc2"]=pd.qcut(airbnb["price_num"],5)
airbnb["price_disc1"].value_counts()
airbnb["price_disc2"].value_counts()<jupyter_output><empty_output><jupyter_text>Les tableaux croisés avec PandasLes DataFrame possèdent des méthodes pour générer des tableaux croisés, notamment :```pythonframe1.pivot_table()```Cette méthode permet de gérer de nombreux cas avec des fonctions standards et sur mesure. **Exercice :**    Afficher un tableau Pivot pour les données AirBnB.<jupyter_code># on définit un 
def moy2(x):
    return x.mean()/x.var()<jupyter_output><empty_output><jupyter_text>On croise le room_type avec le niveau de prix et on regarde le review_score_rating moyen + le nombre d'occurences et une fonction "maison" :<jupyter_code>airbnb['room_type']
airbnb['price_disc2']
airbnb['review_scores_rating']
airbnb.pivot_table(values=["review_scores_rating",'review_scores_cleanliness'],
                   index="room_type",
                   columns='price_disc2',aggfunc=["count","mean",moy2])<jupyter_output><empty_output><jupyter_text>L'utilisation de GroupBy sur des DataFrame- `.groupby` permet de rassembler des observations en fonction d’une variable dite de groupe- Par exemple, `frame.groupby('X').mean()` donnera les moyennes par groupes de `X`- On peut aussi utiliser `.size()` pour connaître la taille des groupes et utiliser d’autres fonctions (`.sum()`)- On peut effectuer de nombreuses opérations de traitement avec le groupby<jupyter_code>airbnb_group_room = airbnb.groupby(['room_type','price_disc2'])

airbnb_group_room["price_num"].describe()
# on peut afficher plusieurs statistiques
airbnb_group_room["price_num"].agg(["mean","median","std","count"])
new_airbnb.groupby(['available','jour_semaine'])["price_num"].agg(["mean","count"])<jupyter_output><empty_output><jupyter_text>Essayez d'utiliser une fonction lambda sur le groupby **Exercice :**    - Données sur les salaires- On utilise le `groupby()` pour rassembler les types d’emploi- Et on calcule des statistiques pour chaque typeOn peut utiliser la méthode `.agg()` avec par exemple `'mean'` comme paramètreOn utilise aussi fréquemment la méthode `.apply()` combinée à une fonction lambda<jupyter_code># on passe tous les JobTitle en minuscule
salaries["JobTitle"]= salaries["JobTitle"].str.lower()
# nombre de JobTitle différents
salaries["JobTitle"].nunique()
salaries.groupby("JobTitle")["TotalPay"].mean().sort_values(ascending=False)
salaries.groupby("JobTitle")["TotalPay"].agg(["mean","count"]).sort_values("count",ascending=False)<jupyter_output><empty_output><jupyter_text>On peut aussi faire des représentations graphiques avancées :<jupyter_code>import matplotlib.pyplot as plt
plt.figure(figsize=(10,5))
plt.scatter("longitude","latitude", data = airbnb[airbnb["price_num"]<150], s=1,c = "price_num", cmap=plt.get_cmap("jet"))
plt.colorbar()
plt.savefig("paris_airbnb.jpg")
airbnb[airbnb["price_num"]<150]<jupyter_output><empty_output>
StarcoderdataJupyterStructuredCleanDedup