Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentes Révision précédente | |||
public:appro-s7:td6 [2020/11/24 21:24] – edauce | public:appro-s7:td6 [2020/11/24 21:33] (Version actuelle) – edauce | ||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
+ | ===== Les Pandas, les Poneys et la Persistance des données ===== | ||
+ | |||
+ | Ici nous apprenons à utiliser plusieurs librairies de manipulation et de mise en forme des données. | ||
+ | |||
+ | Liens utiles : | ||
+ | |||
+ | * Notebook à partir de PyCharm : | ||
+ | * https:// | ||
+ | * Pandas : | ||
+ | * http:// | ||
+ | * Pony : | ||
+ | * https:// | ||
+ | |||
+ | < | ||
+ | Pour installer les librairies pandas et pony : | ||
+ | < | ||
+ | $ pip3 install pandas | ||
+ | </ | ||
+ | |||
+ | < | ||
+ | $ pip3 install pony | ||
+ | </ | ||
+ | </ | ||
+ | ==== Les notebooks Jupyter ==== | ||
+ | |||
+ | Ce travail sera réalisé à l'aide de " | ||
+ | permettent d' | ||
+ | Les résultats d' | ||
+ | |||
+ | * Si vous êtes sous Windows ou Mac, utilisez l' | ||
+ | |||
+ | * Sur un environnement Unix, Ouvrez un terminal dans votre dossier de travail et tapez : | ||
+ | < | ||
+ | $ jupyter-notebook | ||
+ | </ | ||
+ | |||
+ | Ceci ouvre un onglet de l' | ||
+ | * Créez un notebook vierge via le menu '' | ||
+ | * Ou bien cliquez sur le notebook sur lequel vous souhaitez travailler. | ||
+ | |||
+ | Pour utiliser un notebook, voir : | ||
+ | <note tip> | ||
+ | * {{http:// | ||
+ | * {{http:// | ||
+ | * {{http:// | ||
+ | * {{http:// | ||
+ | |||
+ | ---- | ||
+ | * {{https:// | ||
+ | </ | ||
+ | |||
+ | ==== Analyser les données : Pandas ==== | ||
+ | |||
+ | L' | ||
+ | <code python> | ||
+ | import numpy as np | ||
+ | import matplotlib.pyplot as plt | ||
+ | import pandas | ||
+ | </ | ||
+ | |||
+ | |||
+ | On considère une série d’enregistrements concernant des ventes réalisées par un exportateur de véhicules miniatures. Pour chaque vente, il entre dans son registre de nombreuses informations : | ||
+ | * nom de la société cliente | ||
+ | * nom et prénom du contact, adresse, téléphone | ||
+ | * nombre d' | ||
+ | * prix de vente | ||
+ | * etc... | ||
+ | Ces informations sont stockées dans un fichier au format ‘csv’ (comma separated values) : '' | ||
+ | Téléchargez ce fichier et copiez-le dans votre répertoire de travail. | ||
+ | |||
+ | Dans un premier temps, regardez son contenu avec un editeur de texte (**'' | ||
+ | Les ligne suivantes contiennent les valeurs d’attributs correspondant à une vente donnée. | ||
+ | En tout plus de 2000 ventes sont répertoriées dans ce fichier. | ||
+ | |||
+ | Ouvrez-le maintenant à l’aide d’un tableur (par exemple **'' | ||
+ | Les données sont maintenant “rangées” en lignes et colonnes pour faciliter la lecture. | ||
+ | |||
+ | Déplacez le fichier '' | ||
+ | |||
+ | == Lecture des données == | ||
+ | |||
+ | Les données sont au format '' | ||
+ | * '' | ||
+ | |||
+ | <code python> | ||
+ | with open(' | ||
+ | data = pandas.read_csv(f) | ||
+ | print(data) | ||
+ | </ | ||
+ | avec '' | ||
+ | |||
+ | Testez les commandes suivantes : | ||
+ | <code python> | ||
+ | print(len(data)) | ||
+ | </ | ||
+ | <code python> | ||
+ | print(data.columns) | ||
+ | </ | ||
+ | Syntaxe de type dictionnaire : | ||
+ | <code python> | ||
+ | print(data[" | ||
+ | </ | ||
+ | <code python> | ||
+ | print(data[[" | ||
+ | </ | ||
+ | Autre syntaxe : | ||
+ | <code python> | ||
+ | print(data.VILLE) | ||
+ | </ | ||
+ | <code python> | ||
+ | print(data.VILLE.head(10)) | ||
+ | </ | ||
+ | PS : Ça marche aussi avec la syntaxe " | ||
+ | <code python> | ||
+ | print(data[" | ||
+ | </ | ||
+ | |||
+ | === pour afficher les lignes === | ||
+ | Tout tableau de données possède un index: | ||
+ | <code python> | ||
+ | print(data.index) | ||
+ | </ | ||
+ | (il s'agit ici d'une indexation automatique par les entiers) | ||
+ | |||
+ | Les données peuvent être accédées par leur index: | ||
+ | <code python> | ||
+ | print(data.loc[0]) | ||
+ | </ | ||
+ | |||
+ | === Modifier les données === | ||
+ | Les prix augmentent de 1 euro : | ||
+ | <code python> | ||
+ | data.PRIX_UNITAIRE += 1 | ||
+ | data.MONTANT = data.PRIX_UNITAIRE | ||
+ | data.MONTANT *= data.QUANTITE | ||
+ | print(data.MONTANT) | ||
+ | </ | ||
+ | |||
+ | === Sélectionner les données === | ||
+ | <code python> | ||
+ | selection = data[data.MONTANT > 6000] | ||
+ | </ | ||
+ | l' | ||
+ | |||
+ | Pour faciliter l' | ||
+ | <code python> | ||
+ | print(selection[[" | ||
+ | </ | ||
+ | |||
+ | Sélection multi-critères : | ||
+ | <code python> | ||
+ | selection = data[(data.MONTANT > 6000) & (data.PAYS == ' | ||
+ | print(selection[[" | ||
+ | </ | ||
+ | |||
+ | < | ||
+ | <!-- | ||
+ | === Opérateurs d' | ||
+ | |||
+ | * usage : statistique sur les données | ||
+ | * principe : | ||
+ | * opérateur d’aggrégation : | ||
+ | * tout type de données : comptage (attention aux doublons) | ||
+ | <code python> | ||
+ | print(data[" | ||
+ | print(data[" | ||
+ | </ | ||
+ | * données quantitatives (et non qualitatives) : somme, moyenne, ecart-type (count, sum, mean, std, min, max, …) | ||
+ | <code python> | ||
+ | print(data[" | ||
+ | print(data[" | ||
+ | </ | ||
+ | |||
+ | |||
+ | === Affichage et figures === | ||
+ | |||
+ | Un histogramme simple | ||
+ | <code python> | ||
+ | data[" | ||
+ | plt.show() | ||
+ | </ | ||
+ | |||
+ | <code python> | ||
+ | data.QUANTITE.hist(by = data.TYPE_PRODUIT, | ||
+ | </ | ||
+ | |||
+ | |||
+ | === Calcul par groupes | ||
+ | Pandas offre la possibilité d' | ||
+ | |||
+ | Le découpage en groupe repose sur des valeurs d' | ||
+ | a de valeurs différentes pour l' | ||
+ | |||
+ | Par exemple si on prend le type de produit: | ||
+ | <code python> | ||
+ | groupes_selon_produit = data.groupby(' | ||
+ | </ | ||
+ | ici l' | ||
+ | |||
+ | Pour visualiser les groupes: | ||
+ | <code python> | ||
+ | print(groupes_selon_produit.groups) | ||
+ | </ | ||
+ | |||
+ | On peut ensuite effectuer des mesures et calculs par groupes. | ||
+ | Par exemple : | ||
+ | <code python> | ||
+ | nb_ventes_par_produit = groupes_selon_produit.size() | ||
+ | </ | ||
+ | l' | ||
+ | <code python> | ||
+ | print(nb_ventes_par_produit.index) | ||
+ | </ | ||
+ | On peut bien sûr l' | ||
+ | <code python> | ||
+ | print(nb_ventes_par_produit) | ||
+ | </ | ||
+ | |||
+ | Les fonctions sum(), mean(), max(), min() etc... s' | ||
+ | |||
+ | Exemple : le chiffre d' | ||
+ | <code python> | ||
+ | CA_par_produit = groupes_selon_produit.MONTANT.sum() | ||
+ | </ | ||
+ | |||
+ | Enfin on peut également effectuer une sélection sur les valeurs calculées (l' | ||
+ | |||
+ | Exemples: | ||
+ | * les produits générant un chiffre d' | ||
+ | <code python> | ||
+ | print(CA_par_produit[CA_par_produit > 1000000]) | ||
+ | </ | ||
+ | * le produit générant le plus haut chiffre d' | ||
+ | <code python> | ||
+ | print(CA_par_produit[CA_par_produit == max(CA_par_produit)]) | ||
+ | </ | ||
+ | |||
+ | Les groupes peuvent être définis sur des critères multiples : | ||
+ | <code python> | ||
+ | groupes_pays_ville = data.groupby([' | ||
+ | </ | ||
+ | |||
+ | === Affichage et figures === | ||
+ | |||
+ | <code python> | ||
+ | |||
+ | grouped = data.groupby(data.TYPE_PRODUIT) | ||
+ | print(grouped.NUM_COMMANDE.count()) | ||
+ | |||
+ | plt.figure() | ||
+ | grouped.NUM_COMMANDE.count().plot(kind = " | ||
+ | |||
+ | plt.figure() | ||
+ | grouped.NUM_COMMANDE.count().plot(kind = " | ||
+ | </ | ||
+ | |||
+ | Pour aller plus loin : | ||
+ | * {{http:// | ||
+ | * {{http:// | ||
+ | |||
+ | <note tip> ** A faire ** | ||
+ | * Trouvez le nombre de ventes, le nombre de clients référencés (sans doublons), et le nombre de références produits (sans doublons). | ||
+ | * Afficher le nombre de client et le chiffre d' | ||
+ | * par pays | ||
+ | * par pays puis par état | ||
+ | * par pays puis par état puis par ville | ||
+ | * Donnez le nombre de ventes en fonction du mois pour l' | ||
+ | * Donnez le chiffre d’affaires par année et trimestre pour les ventes réalisées aux états unis | ||
+ | * Quelle est la catégorie de véhicules la plus vendue? | ||
+ | </ | ||
+ | === Tables Pivot === | ||
+ | |||
+ | Agrégation des données selon différents attributs/ | ||
+ | |||
+ | exemple : on représente les ventes selon (1) la dimension géographique et (2) la dimension temporelle | ||
+ | |||
+ | <code python> | ||
+ | T = pandas.pivot_table(data, | ||
+ | print(T) | ||
+ | </ | ||
+ | |||
+ | <code python> | ||
+ | T.plot(kind=' | ||
+ | plt.show() | ||
+ | </ | ||
+ | |||
+ | Evolution des ventes au cours de l' | ||
+ | <code python> | ||
+ | selection = data[data.PAYS == " | ||
+ | T2 = pandas.pivot_table(selection, | ||
+ | print(T2) | ||
+ | |||
+ | T2.plot(kind=' | ||
+ | plt.show() | ||
+ | </ | ||
+ | <note tip> ** A faire ** | ||
+ | * Donnez le nombre de ventes ('' | ||
+ | * Donnez le chiffre d' | ||
+ | </ | ||
+ | |||
+ | --></ | ||
+ | |||
+ | ==== Organiser et transformer les données : Pony ORM ==== | ||
+ | |||
+ | La librairie Pony ORM est un gestionnaire de persistance qui permet la mise en correspondance entre les objets d'un programme et les valeurs d'une base de données, pour assurer leur conservation d'une session à l' | ||
+ | |||
+ | Pony effectue toutes les opérations de sauvegarde de manière transparente. La création et la mise à jour des objets persistants s' | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | === Initialisation=== | ||
+ | |||
+ | <code python> | ||
+ | from pony import orm | ||
+ | </ | ||
+ | |||
+ | <code python> | ||
+ | db = orm.Database() | ||
+ | </ | ||
+ | |||
+ | === Création du schéma de données === | ||
+ | |||
+ | Nous définissons ici trois schémas de classes correspondant aux ensembles d' | ||
+ | |||
+ | * **Client**(__id_client__, | ||
+ | * **Commande**(__num_commande__, | ||
+ | * **Produit**(__code_produit__, | ||
+ | |||
+ | Les clés étrangères de la table des commande définissent deux relations de un à plusieurs : | ||
+ | * une relation de un à plusieurs entre //un// produit et //des// commandes, | ||
+ | * et une relation de un à plusieurs entre //un// client et //des// commandes. | ||
+ | |||
+ | Dans un modèle ORM, les relations de un à plusieurs se traduisent par des attributs de type liste ou ensemble : | ||
+ | * A un client correspond un ensemble de commandes | ||
+ | * A un produit correspond un ensemble de commandes | ||
+ | * A une commande correspond //un// client et //un// produit | ||
+ | |||
+ | === Classe Client === | ||
+ | Les classes sont définies ici comme des schémas de données. | ||
+ | |||
+ | La classe Client hérite de la classe générique //Entity//. Les attributs des objets obéissent à une définition parmi quatre définitions possibles : | ||
+ | * attribut clé primaire : '' | ||
+ | * attribut requis (la valeur doit être renseignée) : '' | ||
+ | * attribut facultatif: '' | ||
+ | * relation de un à plusieurs : '' | ||
+ | |||
+ | <code python> | ||
+ | class Client(db.Entity): | ||
+ | id_client = orm.PrimaryKey(str) | ||
+ | telephone = orm.Required(str) | ||
+ | ville = orm.Required(str) | ||
+ | pays = orm.Required(str) | ||
+ | achats = orm.Set(' | ||
+ | </ | ||
+ | |||
+ | === Classe Produit === | ||
+ | <code python> | ||
+ | class Produit(db.Entity): | ||
+ | code_produit = orm.PrimaryKey(str) | ||
+ | type_produit = orm.Required(str) | ||
+ | prix_unitaire = orm.Required(float) | ||
+ | ventes = orm.Set(' | ||
+ | </ | ||
+ | |||
+ | ===Classe Commande === | ||
+ | |||
+ | Dans la classe Commande, il n'y a pas de clé étrangère (comme dans le modèle relationnel) mais : | ||
+ | * un attribut de type '' | ||
+ | * un attribut de type '' | ||
+ | |||
+ | <code python> | ||
+ | class Commande(db.Entity): | ||
+ | num_commande = orm.PrimaryKey(int) | ||
+ | quantité = orm.Required(int) | ||
+ | montant = orm.Required(float) | ||
+ | mois = orm.Required(int) | ||
+ | année = orm.Required(int) | ||
+ | client = orm.Required(Client) | ||
+ | produit = orm.Required(Produit) | ||
+ | </ | ||
+ | |||
+ | == Pour afficher == | ||
+ | |||
+ | La commande '' | ||
+ | |||
+ | <code python> | ||
+ | orm.show(Client) | ||
+ | </ | ||
+ | |||
+ | === Association à un gestionnaire de BD === | ||
+ | |||
+ | Les schémas de données définis dans les classes peuvent être implémentés dans différents gestionnaires de bases de données. | ||
+ | |||
+ | Nous choisissons ici le gestionnaire sqlite, ce qui évite de définir une connexion | ||
+ | |||
+ | <code python> | ||
+ | db.bind(provider=' | ||
+ | </ | ||
+ | |||
+ | |||
+ | == Mode debug == | ||
+ | |||
+ | Le mode '' | ||
+ | |||
+ | <code python> | ||
+ | orm.set_sql_debug(True) | ||
+ | </ | ||
+ | |||
+ | La commande '' | ||
+ | <code python> | ||
+ | db.generate_mapping(create_tables=True) | ||
+ | </ | ||
+ | |||
+ | === Transfert des données Client=== | ||
+ | Les données sont lues dans le dataFrame '' | ||
+ | |||
+ | <note important> | ||
+ | On vérifie avant l' | ||
+ | <code python> | ||
+ | if Client.get(id_client = c.CLIENT) is None: | ||
+ | ... | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <code python> | ||
+ | clients = data[[" | ||
+ | for i in range(len(clients)): | ||
+ | c = clients.iloc[i] | ||
+ | if Client.get(id_client = c.CLIENT) is None: | ||
+ | Client(id_client = c.CLIENT, telephone = c.TELEPHONE, | ||
+ | orm.commit() | ||
+ | </ | ||
+ | |||
+ | <note important> | ||
+ | On remarque que l' | ||
+ | </ | ||
+ | |||
+ | == Affichage == | ||
+ | Pour afficher la liste de tous les clients (et non le schéma de la classe Client), il faut faire appel à la méthode '' | ||
+ | <code python> | ||
+ | Client.select().show() | ||
+ | </ | ||
+ | |||
+ | On peut également afficher les clients un par un à l'aide leur index (ici le nom du magasin) | ||
+ | |||
+ | <code python> | ||
+ | print(Client[" | ||
+ | print(Client[" | ||
+ | print(Client[" | ||
+ | print(Client[" | ||
+ | print(Client[" | ||
+ | </ | ||
+ | |||
+ | On notera que la liste des achats est vide (les commandes n'ont pas encore été saisies) | ||
+ | |||
+ | L' | ||
+ | <code python> | ||
+ | requête = Client.select(lambda c : c.pays == " | ||
+ | </ | ||
+ | Une requête se comporte comme un itérateur sur les objets: | ||
+ | < | ||
+ | for c in requête: | ||
+ | print(c.id_client, | ||
+ | </ | ||
+ | |||
+ | === Transfert des données produits === | ||
+ | Les produits sont insérés de la même façon que les clients: | ||
+ | |||
+ | <code python> | ||
+ | produits = data[[" | ||
+ | for i in range(len(produits)): | ||
+ | p = produits.iloc[i] | ||
+ | if Produit.get(code_produit = p.CODE_PRODUIT) is None: | ||
+ | Produit(code_produit = p.CODE_PRODUIT, | ||
+ | orm.commit() | ||
+ | </ | ||
+ | |||
+ | == Affichage du contenu de la classe== | ||
+ | <code python> | ||
+ | Produit.select().show() | ||
+ | </ | ||
+ | |||
+ | == Affichage d'un produit particulier == | ||
+ | <code python> | ||
+ | print (Produit[' | ||
+ | print (Produit[' | ||
+ | print (Produit[' | ||
+ | print (Produit[' | ||
+ | </ | ||
+ | |||
+ | === Transfert des données ventes === | ||
+ | |||
+ | Pour créer les commandes, il faut ici définir deux références : | ||
+ | * une référence au client qui a effectué la commande | ||
+ | * une référence au produit commandé | ||
+ | qui sont des objets définis précédemment lors de l’insertion des données client et des donnés produit. Ils correspondent donc à des entrées de leurs classes respectives, | ||
+ | |||
+ | <code python> | ||
+ | ventes = data[[" | ||
+ | for i in range(len(ventes)): | ||
+ | v = ventes.iloc[i] | ||
+ | if Commande.get(num_commande = int(v.NUM_COMMANDE)) is None: | ||
+ | client = Client[v.CLIENT] | ||
+ | produit = Produit[v.CODE_PRODUIT] | ||
+ | Commande(num_commande = int(v.NUM_COMMANDE), | ||
+ | quantité = int(v.QUANTITE), | ||
+ | montant = float(v.MONTANT), | ||
+ | mois = int(v.MOIS), | ||
+ | année = int(v.ANNEE), | ||
+ | client = client, | ||
+ | produit = produit) | ||
+ | orm.commit() | ||
+ | </ | ||
+ | |||
+ | == Affichage == | ||
+ | <code python> | ||
+ | Commande.select().show() | ||
+ | </ | ||
+ | |||
+ | <code python> | ||
+ | print(Commande[10118]) | ||
+ | print(' | ||
+ | print(' | ||
+ | print(' | ||
+ | print(' | ||
+ | print(' | ||
+ | print(' | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | == Exemples de requête == | ||
+ | |||
+ | <code python> | ||
+ | requête = Commande.select(lambda c : c.montant > 10000) | ||
+ | for r in requête: | ||
+ | print(r.num_commande, | ||
+ | </ | ||
+ | |||
+ | Ou plus simplement : | ||
+ | <code python> | ||
+ | requête.show() | ||
+ | </ | ||
+ | |||
+ | == Autre écriture == | ||
+ | <code python> | ||
+ | requête = orm.select(c for c in Commande if c.montant > 10000) | ||
+ | </ | ||
+ | |||
+ | === Mise à jour automatique des contenus === | ||
+ | |||
+ | Maintenant que les commandes on été entrées dans la base, la liste des achats | ||
+ | <code python> | ||
+ | print(Client[" | ||
+ | print(Client[" | ||
+ | print(Client[" | ||
+ | print(Client[" | ||
+ | print(Client[" | ||
+ | </ | ||
+ | |||
+ | Et la liste des ventes est de même renseignée pour chaque produit de la classe Produit: | ||
+ | <code python> | ||
+ | print (Produit[' | ||
+ | print (Produit[' | ||
+ | print (Produit[' | ||
+ | print (Produit[' | ||
+ | </ | ||
+ | |||
+ | |||
+ | === Modifier les valeurs === | ||
+ | |||
+ | <code python> | ||
+ | Produit[' | ||
+ | orm.commit() | ||
+ | </ | ||
+ | |||
+ | === Supprimer un objet === | ||
+ | <code python> | ||
+ | Produit[' | ||
+ | orm.commit() | ||
+ | </ | ||
+ | |||
+ | |||
+ | <note tip> ** A faire ** | ||
+ | * Pour chaque client, calculer le montant total des achats | ||
+ | * Pour chaque produit, calculer le montant total des ventes | ||
+ | * Corriger le champ '' | ||
+ | * En profiter pour supprimer les doublons de la classe Client | ||
+ | * Créez un nouveau client | ||
+ | * Faites-lui commander plusieurs produits (n' | ||
+ | * Vérifiez que les nouvelles commandes apparaissent bien dans la liste des ventes de la table '' | ||
+ | </ | ||
+ | |||
+ | ==== Si vous avez le temps ==== | ||
+ | |||
+ | * Définissez un modèle ORM pour le schéma de données du [[public: | ||
+ | * Remplissez la base à l'aide des données contenues dans {{http:// | ||
+ | * Effectuez quelques requêtes pour vérifier que tout marche bien | ||
+ | |||