<jupyter_start><jupyter_text>Cours - Traitement des données en table Fiche 3 - Manipulation de tables avec la bibliothèque Pandas 1. Lecture de fichiersDe façon classique en Python, nous allons commencer par charger le module.<jupyter_code>import pandas<jupyter_output><empty_output><jupyter_text>La lecture d’un fichier csv se fait alors aisément grâce à la commande:<jupyter_code>villes = pandas.read_csv("villes_virgule.csv", delimiter=",", keep_default_na=False)<jupyter_output><empty_output><jupyter_text>**Remarques :*** "," : délimiteur du fichier CSV* keep_default_na=True : si une cellule contient la valeur NA, pandas la convertit en NaN (Not a Nombre)* keep_default_na=False : si une cellule contient la valeur NA, il n'y a pas de conversion, NA est conservéAffichages de test<jupyter_code>villes.head(4) # affiche les premières entrées de la table
villes.sample(7) #affiche 7 enregistrements de la table pris au hasard<jupyter_output><empty_output><jupyter_text>affiche les dernières entrées de la table<jupyter_code>villes.tail(4) # affiche les premières entrées de la table
villes.columns #retourne la liste des champs
 villes.dtypes #affiche la liste des champs avec, à chaque fois, le type de données correspondant<jupyter_output><empty_output><jupyter_text>**Remarque :**On remarque en particulier que pandas a reconnu que les champs latitude, longitude et nombre d'habitants correspondent à des données numériques, et le traîtent comme tels.On peut aussi avoir des données statistiques.<jupyter_code>villes.describe()<jupyter_output><empty_output><jupyter_text>**Remarque :**Les statistiques correspondent à des séries numériques.**Notation :*** std : écart-type* 25% : premier quartile* 50% : médiane* 75% : troisième quartileEnfin, on peut facilement ne conserver que les champs qui nous intéressent. Par exemple, si l’on ne veut que les noms des villes et leurs coordonnées, on utilise :<jupyter_code>villes[['nom', 'long', 'lat']]<jupyter_output><empty_output><jupyter_text>**Exercice :**Afficher les statistiques des supericies des villes uniquement : **Dataframes et series**Les **tables** lues dans les **fichiers csv** sont stockés par pandas sous forme de **dataframes**. On peut les voir comme un tableau de p-uplets nommés. **Exemple :** L’enregistrement (objet, p-uplet) numéro 4 s’obtient en utilisant la commande **loc** :<jupyter_code>villes.loc[4]<jupyter_output><empty_output><jupyter_text>L'affichage de plusieurs enregistrements  s’obtient en exécutant :<jupyter_code>villes.loc[[4, 10]]<jupyter_output><empty_output><jupyter_text>**Remarques**Les données précédentes ne sont pas du même type.<jupyter_code>type(villes.loc[4]), type(villes.loc[[4, 10]])<jupyter_output><empty_output><jupyter_text>L'affichage de la valeur de l'attribut nom s’obtient comme pour un dictionnaire :<jupyter_code>villes.loc[4]['nom']<jupyter_output><empty_output><jupyter_text>**Une série** est ce que l’on obtient à partir d’un dataframe en ne sélectionnant qu’un **seul champ**.<jupyter_code>villes['nom']
type(villes['nom'])<jupyter_output><empty_output><jupyter_text>Lors de la **sélection d’un unique champ**, pandas permet d’utiliser une syntaxe légère en n’écrivant que **villes.nom** plutôt que **villes['nom']**.Il convient, pour finir, de différentier :- la serie villes['nom'] (ou ville.name, donc) et- le dataframe à un seul champ villes[['nom']].<jupyter_code>villes.nom
villes[['nom']]
villes[['nom', 'dep']]<jupyter_output><empty_output><jupyter_text>**Exercice :**    Afficher la densité de l'enregistrement 100. **Exercice :**    Afficher le nom et la densité des enregistrements 100 et 101.  2. Interrogations simplesNous allons afficher les enregsitrements qui correspondent au département des Landes.<jupyter_code>villes[villes.dep == '40']<jupyter_output><empty_output><jupyter_text>**Exercice :**Afficher les villes qui au moins 200 000 habitants en 2012. **Exercice :**Compter le nombre de villes qui ont au moins 200 000 habitants en 2012.  3. TrisLes méthodes **nlargerst** et **nsmallest** permettent de déterminer **les plus grands** et **plus petits éléments** selon un critère donné. Ainsi, pour obtenir les villes les plus grandes en superficie et celles les moins peuplées en 2012, on peut écrire :<jupyter_code>villes.nlargest(5, 'surf')
villes.nsmallest(4, 'nb_hab_2012')<jupyter_output><empty_output><jupyter_text>**Exercice :**Afficher les 6 villes dont la densité est la plus élevée en 1999. Le **tri d’un dataframe** s’effectue à l’aide de la méthode **sort_values**, comme par exemple :<jupyter_code>villes.sort_values(by='surf')<jupyter_output><empty_output><jupyter_text>On peut trier selon **plusieurs critères**, en spécifiant éventuellement les monotonies. **Exemple :*** on construit un dataframe avec les villages qui ont au plus 500 habiatnts en 2012;* affichage par ordre décroissant du nombre d'habitants en 2012;* affichage par ordre croissant de surperficie.* on sélectionne les champs à afficher : nom, nb_hab_2012, surf<jupyter_code>villages = villes[villes.nb_hab_2012 <= 500]
villages.sort_values(by=['nb_hab_2012', 'surf'], ascending=[False, True])[['nom', 'nb_hab_2012', 'surf']]<jupyter_output><empty_output><jupyter_text>**Exercice :**Sélectionner les villes qui ont au moins 600 habitants en 2012.Les critères d'affichage :* habitants en 2012 par ordre croissant;* superficie par ordre croissant.Les champs affichés seront les suivants :* le numéro du département;* le nom de la ville;* le nombre d'habitants;* la superficie.<jupyter_code><jupyter_output><empty_output><jupyter_text>3. Manipulation de donnéesAprès ce survol des méthodes de base pour extraire et ordonner les données contenue dans une table, nous allons pour finir voirquelques méthodes de manipulation de tables. 3.1. Création d’un nouveau champIl est très facile de créer de nouveaux champs à partir d’anciens. Par exemple, pour calculer l'évolution de la population, il suffit d’exécuter :<jupyter_code>villes['evolution_2010'] =  villes.nb_hab_2010 - villes.nb_hab_1999<jupyter_output><empty_output><jupyter_text>On affiche le résultat avec la commande suivante :<jupyter_code>villes[['nom', 'nb_hab_1999', 'nb_hab_2010', 'nb_hab_2012', 'evolution_2010']]<jupyter_output><empty_output><jupyter_text>Les séries peuvent être utilisées avec le **module numpy**. Ainsi, on peut réaliser une carte des villes utilisant la [projection de Mercator](https://fr.wikipedia.org/wiki/Projection_de_Mercator) en effectuant :<jupyter_code>import numpy
villes['projection_y'] = numpy.arcsinh(numpy.tan(villes.lat * numpy.pi / 180))
villes.plot.scatter(x='long', y='projection_y')<jupyter_output><empty_output><jupyter_text>3.2. Fusion de tablesNous allons fusionner les deux tables ci-dessous :**TableNotes** | Nom   | Anglais | NSI  | Math ||:-:    |:-:      |:-:   |:-:   || Paul  | 10      | 15   | 10   || Marie | 15      | 12   | 18   || Alice | 11      | 15   | 18   |**TableInformations**| NomEleve | Age     | Mail          ||:-:    |:-:      |:-:            || Paul  | 17      | <EMAIL>  || Marie | 15      | <EMAIL> || Alice | 15      | <EMAIL> |Pour que le résultat de la fusion soit correct, il faut vérifier deux conditions :* chaque table doit avoir une colonne qui permet de repérer quaque ligne de manière unique (clé primaire dans le vocabulaire des bases de données);* les deux tables doivent avoir une colonne avec des informations communes et de même nature.La fusion se fait à l’aide de la fonction merge :<jupyter_code>elevesNotes = pandas.read_csv("eleves_notes.csv", delimiter=",", keep_default_na=False)
elevesNotes.head() # affiche les premières entrées de la table
elevesInformations = pandas.read_csv("eleves_informations.csv", delimiter=";", keep_default_na=False)
elevesInformations.head() # affiche les premières entrées de la table
nouvelleTable = pandas.merge(elevesNotes, elevesInformations, left_on='Nom', right_on='NomEleve')
>>> nouvelleTable<jupyter_output><empty_output><jupyter_text>(pour aller plus loin) Modifer les noms des colonnes.Il est possible de modifier le nom des colonnes soit pour éviter les conflits de noms, soit pour améliorer la lisibilité.**Exemple de renommage**<jupyter_code>elevesInformations

elevesInformations[['Age', 'Mail']].rename(columns={'Age': 'Âge', 'Mail': 'E-mail'})<jupyter_output><empty_output><jupyter_text>**Exemple : renommage et fusion des tables.**<jupyter_code>elevesInformations = pandas.read_csv("eleves_informations.csv", delimiter=";", keep_default_na=False)


nouvelleTable = pandas.merge(
    elevesNotes[['Nom', 'NSI', 'Anglais', 'Math']],
    elevesInformations[['NomEleve', 'Age', 'Mail']].rename(
         columns={'Mail' : 'E-mail'}),
    left_on='Nom', right_on='NomEleve')
>>> nouvelleTable<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11149228130360206, 'fraction_numerical': 0.01955403087478559, 'mean_word_length': 6.4055884843353095, 'pattern_counts': {'":': 0, '<': 97, '<?xml version=': 0, '>': 102, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '23', 'dir': 'jupyter-structured-clean-dedup', 'id': '279339', 'license': 'MIT', 'n_tokens_mistral': 3164, 'n_tokens_neox': 3031, 'n_words': 871, 'path': '07_donnees_tables/cours/fiche3_pandas_eleve.ipynb', 'repo_name': 'icecodder/nsi', 'size': '8762'}