#!/usr/bin/env python # coding: utf-8 # # Premiers pas en *matplotlib* # # *** # > __Auteur__: Joseph Salmon # > # ## Imports # In[1]: # Utile pour créer les slides du cours et avoir un affichage identique dans divers notebook, à laisser de côter par les élèves. import os import sys sys.path.append("./codes") # In[2]: import numpy as np import matplotlib.pyplot as plt import matplotlib plt.rcParams.update({'figure.max_open_warning': 0}) matplotlib.__version__ # In[3]: saving = False # option pour sauvegarder les images # In[4]: # Creations de tableau 1D avec valeurs numériques x1 = np.linspace(0.0, 5.0, num=50) x2 = np.linspace(0.0, 2.0, num=50) y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) y2 = np.cos(2 * np.pi * x2) # In[5]: # commande "magique" pour améliorer la visualiation get_ipython().run_line_magic('matplotlib', 'inline') # %matplotlib notebook # In[6]: fig1 = plt.figure(figsize=(5, 4)) plt.plot(x1, y1) plt.xlim(0,6) plt.ylim(-1,1) plt.show() # Pour forcer l'affichage, notamment sans %matplolib inline # **ATTENTION, Mauvaise pratique!** # # L'exemple ci-dessus est "léger" car il n'a pas de titre, de précision sur les axes, de légendes, etc. # ## Export de fichiers images sous divers formats: # # **Formats matriciels** (en anglais: bitmap): # - png (format brut, non compressé, s'ouvre avec Gimp) # - jpg (format compressé) # # **Formats vectoriels** # - pdf (à privilégier dans vos documents) # - svg (facile à modifier avec Inkscape) # - ps # - eps # ### Formats matriciels # Format qui encode pixel par pixel une image # #### Format png: # In[7]: # Export en png: if saving: fig1.savefig("ma_figure_pas_belle.png", format='png', dpi=90) # **Remarque**: *dot per inch* (point par pouce) est l'unité de résolution voulue pour l'enregistrement (historiquement pour l'impression papier) # ### Visualisation du png créé # In[8]: import skimage # Module pour la gestion des images from skimage import io skimage.__version__ # In[9]: fig_png = plt.figure(figsize=(8, 5)) array_from_png = io.imread("ma_figure_pas_belle.png") plt.imshow(array_from_png) plt.axis('off') plt.show() # In[10]: print(array_from_png.shape) # noter que l'image couleur est un tenseur de dimension 3! len('transparence') # **Interprétation**: l'image est représentée par un tenseur de dimension 3. L'image a 360 x 450 pixels. # Enfin l'encodage est le RGBA, qui a 4 dimensions: # - Red : code le niveau de Rouge # - Green : code le niveau de Vert # - Blue : code le niveau Bleu # - Alpha : code le niveau de transparence. # #### Format jpg: # In[11]: # Export en jpg basse qualité if saving: io.imsave("ma_figure_pas_belle20.jpg", skimage.color.rgba2rgb(array_from_png), quality=20) # Visualisation du jpg array_from_jpeg = skimage.io.imread("ma_figure_pas_belle20.jpg") plt.figure(figsize=(8, 5)) plt.imshow(array_from_jpeg) plt.axis('off') plt.show() # In[12]: # Export en jpg qualité normale if saving: io.imsave("ma_figure_pas_belle75.jpg", skimage.color.rgba2rgb(array_from_png), quality=75) # 75 is default value, 100 best, 1 worst array_from_jpeg = skimage.io.imread("ma_figure_pas_belle75.jpg") plt.figure(figsize=(8, 5)) plt.imshow(array_from_jpeg) plt.axis('off') plt.show() # **ATTENTION**: donc aux formats jpeg et jpg: # ces formats sont avec perte, il sont à **proscrire** pour les rapports, comptes rendus, thèse, slides, etc. # ### Format vectoriel: # # Une image vectorielle est une image numérique composée d'objets géométriques individuels, des primitives géométriques (segments de droite, arcs de cercle, courbes de Bézier, polygones, etc.), définis chacun par différents attributs (forme, position, couleur, remplissage, visibilité, etc.). # # Voir : https://fr.wikipedia.org/wiki/Image_vectorielle pour plus d'informations. # #### Format pdf: # format le plus populaire pour inclure des graphiques dans des rapports, compte rendus, etc. # In[13]: if saving: fig1.savefig("ma_figure_plus_belle.pdf",format='pdf', dpi=90) # In[14]: from IPython.display import IFrame IFrame("ma_figure_plus_belle.pdf", width=400, height=250) # #### Format svg: # c'est le format à privilégier pour inclure des graphiques si l'on compte faire de la "retouche" (par exemple rajouter des éléments trop compliqués à ajuster en ligne de code, en passant par exemple par Inkscape) # In[15]: if saving: fig1.savefig("ma_figure_plus_belle.svg",format='svg') # ## Améliorations visuelles # ### Style # https://matplotlib.org/api/markers_api.html # { # "." : "point", # "," : "pixel", # "o" : "circle", # "v" : "triangle_down", # "^" : "triangle_up", # "<" : "triangle_left", # ">" : "triangle_right", # "1" : "tri_down", # "2" : "tri_up", # "3" : "tri_left", # "4" : "tri_right", # "8" : "octagon", # "s" : "square", # "p" : "pentagon", # "*" : "star", # "h" : "hexagon1", # "H" : "hexagon2", # "+" : "plus", # "D" : "diamond", # "d" : "thin_diamond", # "|" : "vline", # "_" : "hline" # } # # In[16]: # Changer le le style en points ('o'), ligne ('-'), et couleur noire fig1 = plt.figure(figsize=(8, 5)) plt.plot(x1, y1,'-D', color='b',label="amortie") plt.legend(loc='lower right') plt.show() # ### Titre et noms des axes # In[17]: fig_jolie = plt.figure(figsize=(8, 5)) plt.plot(x1,y1) plt.title("Mon premier graphique: \n c'est lui!") plt.ylabel('Oscillation amortie') plt.xlabel('Temps (s.)') plt.show() if saving: fig_jolie.savefig("ma_figure_jolie.pdf",format='pdf', dpi=90) # ### Changer l'emplacement des axes # In[18]: fig = plt.figure() ax = fig.add_subplot(1, 1, 1) plt.title("Mon premier graphique: \n c'est lui!") plt.xlim(0, 6) plt.ylim(-1, 1) ax.plot(x1, y1) # spine placement data centered ax.spines['left'].set_position(('data', 0.0)) ax.spines['bottom'].set_position(('data', 0.0)) ax.spines['right'].set_color('none') ax.spines['top'].set_color('none') plt.show() # ### Graphes et sous graphes # In[19]: fig2 = plt.figure(figsize=(8, 5)) plt.subplot(3, 1, 1) plt.plot(x1, y1, 'o-') plt.title('Voici trois sous-graphes') plt.ylabel('Signal 1') plt.subplot(3, 1, 2) plt.plot(x1, y2, '.-') plt.ylabel('Signal 2') plt.subplot(3, 1, 3) plt.plot(x1, y1**2, '--') plt.ylabel("Amplitude de l'angle") plt.xlabel('Temps (s.)') plt.show() # In[ ]: # In[20]: fig, axes = plt.subplots(3, 1, figsize=(8, 5), sharex=True) axes[0].plot(x1, y1, 'o-', linewidth=1) axes[0].set_title('Voici trois sous-graphes') axes[0].set_ylabel('Angle') axes[1].plot(x1, y2, '.-', linewidth=3) axes[1].set_ylabel('Angle') axes[2].plot(x1, y1**2, '--', color='black', linewidth=5) axes[2].set_ylabel("Amplitude de l'angle") axes[2].set_xlabel('Temps (s.)') # In[21]: fig.savefig("../prebuiltimages/subplots.pdf", format="pdf") # ### Couleurs # https://matplotlib.org/api/colors_api.html # # Liste de couleurs en Python: {'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w'}, # **Couleurs par défaut**: # Les couleurs par défauts sont gérées par ce qu'on appelle la **colormap** (carte de couleur). # Il en existe de nombreuses (cf. http://www.futurile.net/2016/03/31/colormaps-in-matplotlib/) # In[22]: # Obtenir la colormap par défaut de Maptlotlib (viridis depuis la version 2) cmap_name = plt.rcParams['image.cmap'] cmap = plt.cm.get_cmap(cmap_name) print("La carte de couleur par défaut est : {}".format(cmap_name)) # In[23]: # Afficher la colormap par défaut colors = cmap(np.arange(cmap.N)) fig = plt.figure(figsize=(6, 2)) plt.imshow([colors], extent=[0, 10, 0, 1]) plt.title("Colormap par defaut") plt.show() # **Liste des colormaps** (palettes): # https://matplotlib.org/tutorials/colors/colormaps.html # In[24]: list_cmap = ['Paired', 'viridis', 'Pastel1', 'Pastel2', 'rainbow', 'jet', 'Accent'] # list_cmap = ['coolwarm', 'Paired', 'viridis', # 'Pastel1', 'Pastel2', 'rainbow', 'jet', 'Accent'] fig, axes = plt.subplots(len(list_cmap), 1, figsize=(8, 8), sharex=True) for idx, cmap_name in enumerate(list_cmap): cmap = plt.get_cmap(cmap_name) t = np.arange(5) for i in range(4): axes[idx].plot(t, i * (t + 1), color=cmap(i), linestyle='-') axes[idx].plot(t, i * (t + 1) + .3, color=cmap(i), linestyle=':') axes[idx].set_title(cmap_name) fig.subplots_adjust(hspace=0.85) plt.show() # # # Divers types de visualisation # ### Imshow: # affichage pour des matrices # In[25]: x = np.linspace(0, 10, 1000) I = np.sin(x) * np.cos(x[:, np.newaxis]) # make noise in 1% of the image pixels speckles = (np.random.random(I.shape) < 0.01) I[speckles] = np.random.normal(0, 3, np.count_nonzero(speckles)) plt.figure(figsize=(9.4, 3)) plt.subplot(1, 2, 1) plt.imshow(I, cmap='RdBu') plt.colorbar() plt.subplot(1, 2, 2) plt.imshow(I, cmap='RdBu') plt.colorbar(extend='both') plt.clim(-1, 1) # **ATTENTION**: a suite nécessite l'installation des packages **seaborn** et **pandas** # In[26]: import seaborn as sns import pandas as pd # In[27]: from download import download url = "http://josephsalmon.eu/enseignement/datasets/belgianmunicipalities.csv" path_target = "./belgianmunicipalities.csv" download(url, path_target, replace=False) # In[28]: cat belgianmunicipalities.csv # In[29]: df_belgium = pd.read_csv(path_target, index_col='Commune') df_belgium = df_belgium.drop(['Unnamed: 0'], axis=1) df_belgium.head() # In[30]: dictionnaire = {1: 'Anv.', 2: 'Brab.', 3: 'Fl.occ.', 4: 'Fl.or.', 5: 'Hainaut', 6: 'Liège', 7: 'Limb.', 8: 'Lux.', 9: 'Namur'} df_belgium = df_belgium.replace({'Province': dictionnaire}) df_belgium.head() # ### Barplots # Afficher la moyenne ou la médiane par région. # # # In[31]: plt.figure() fig_barplot = sns.barplot(x='Province', y='medianincome', data=df_belgium) # #### Échelle logarithmique sur l'axe des ordonnées # In[32]: plt.figure() fig_barplot = sns.barplot(x='Province', y='medianincome', data=df_belgium) fig_barplot.set_yscale('log') # # ### Boxplots # Visualiser la médiane, les quantiles 1/4 et 3/4, 1.5 inter-quartiles et outliers? # voir e.g. : http://www.itse.be/statistique2010/co/233_Cours_boxplot.html # In[33]: plt.figure() fig_boxplot = sns.boxplot(x='Province', y='medianincome', data=df_belgium) # ### Violons # https://datavizcatalogue.com/methods/violin_plot.html # In[34]: plt.figure() sns.violinplot(x='Province', y='medianincome', data=df_belgium) # ### Scatter plots (french: graphique de dispersion) # Corrélations entre la moyenne et la median des revenus # In[35]: fig2 = plt.figure(figsize=(6, 6)) plt.plot(df_belgium['medianincome'], df_belgium['averageincome'], '.', label='villes') plt.plot(df_belgium['medianincome'], df_belgium['medianincome'], label='bissectrice') plt.xlabel('Médiane impôt sur le revenu') plt.ylabel('Moyenne impôt sur le revenu') plt.xlim((0, 30000)) # Démarre l'axe des x a 0 et fini a 30000 plt.ylim((0, 50000)) # Démarre l'axe des y a 0 et fini a 30000 plt.legend() # In[36]: if saving: fig2.savefig("belgianmunicipalities.pdf", bbox_inches='tight') # **Remarque**: la moyenne tire toujours la richesse d'une ville vers le haut par rapport à la médiane (un seul foyer riche ne change pas la médiane, mais peut potentiellement changer la moyenne drastiquement). # #### Scatterplot avancé # Cette fois on va afficher les points par couleur selon la région pour observer à une granularité plus précise # In[37]: g = sns.lmplot(x='medianincome', y='averageincome', data=df_belgium, fit_reg=False, hue='Province') g.fig.set_figheight(6) g.fig.set_figwidth(7) # plt.plot(df['medianincome'], df['medianincome']) plt.xlabel('Médiane impôt sur le revenu') plt.ylabel('Moyenne impôt sur le revenu') plt.xlim((0, 30000)) # Démarre l'axe des x a 0 et fini a 30000 plt.ylim((0, 50000)) # Démarre l'axe des y a 0 et fini a 30000 # In[38]: if saving: g.savefig("belgianmunicipalities_clean.pdf", bbox_inches='tight') # ### Fonction de densité # kde: est l'abbréviation de "Kernel Density Estimation" # In[39]: plt.figure() plt.xlim(12000, 25000) cdf_plot=sns.kdeplot(df_belgium['medianincome'], cumulative=False) # ### Fonction de répartition (version lisse) # In[40]: plt.figure() plt.xlim(12000, 25000) cdf_plot=sns.kdeplot(df_belgium['medianincome'], cumulative=True) # #### Fonction de répartition (version constante par morceaux) # # In[41]: n_counts, bin_edges = np.histogram( df_belgium['medianincome'], bins=df_belgium['medianincome'].count()) cdf = np.cumsum(n_counts) # cdf not normalized, despite above scale = 1.0 / cdf[-1] ncdf = scale * cdf fig_cdf = plt.figure(figsize=(7, 5)) plt.plot(np.sort(df_belgium['medianincome']), ncdf) plt.xlim(12000, 25000) plt.title('Fonction de répartition empirique:\n revenus médians belges') # In[42]: if saving: fig_cdf.savefig("belgianmunicipalities_median_cdf.pdf", bbox_inches='tight') # In[43]: df_belgium_1 = df_belgium[df_belgium["Province"] == "Anv."] n_counts, bin_edges = np.histogram( df_belgium_1['medianincome'], bins=df_belgium_1['medianincome'].count()) cdf = np.cumsum(n_counts) # cdf not normalized, despite above scale = 1.0 / cdf[-1] ncdf = scale * cdf fig_cdf_anv = plt.figure(figsize=(7, 5)) plt.plot(np.sort(df_belgium_1['medianincome']), ncdf) plt.title( "Fonction de répartition empirique: \n revenus médians de la province d'Anvers") plt.xlim(12000, 25000) # In[44]: if saving: fig_cdf_anv.savefig("belgianmunicipalities_anvers_cdf.pdf", bbox_inches='tight') # ## Apparté: Quantiles et autres statistiques élémentaires # ### Quantiles # In[45]: np.percentile(df_belgium['averageincome'], [100 / 4, 100 / 2, 100 * 3 / 4]) # In[46]: np.percentile(df_belgium['medianincome'], [100 / 4, 100 / 2, 3 * 100 / 4]) # In[47]: quantiles = np.percentile(df_belgium['medianincome'], [25, 75]) quantiles[1] - quantiles[0] # In[48]: quantiles = np.percentile(df_belgium['averageincome'], [25, 75]) quantiles[1] - quantiles[0] # ### Dispersion # In[49]: np.var(df_belgium['medianincome']) # In[50]: np.std(df_belgium['medianincome'])