Matplotlib
Introducció
Section titled “Introducció”Matplotlib és una llibreria de baix nivell que serveix per dibuixar figures, funcions i tot tipus de gràfiques estàtiques (en imatges png).
Funciona molt bé amb estructures de Python, arrays de Numpy, sèries i dataFrames de Pandas…
Tot i que hi ha llibreries estàtiques més modernes i senzilles a Python (Seaborn) i altres que ofereixen gràfics animats (Plotly, Bokeh) ens interessa aprendre com funciona Matplotlib, perquè és la que ofereix el màxim nivell de personalització i perquè totes aquestes llibreries tenen un funcionament similar a Matplotlib.
Per aquest motiu, encara hi ha molts treballs científics que presenten les funcions i gràfics amb Matplotlib.
Entorn de treball
Section titled “Entorn de treball”Crea un nou projecte:
$ poetry new plot --name app$ cd plot$ poetry add matplotlib numpy
Crea el fitxer app/main.py
:
import matplotlib.pyplot as plt
xs = [2, 1, 5, 7, 11, 12, 15, 13]
plt.plot(xs)plt.savefig("plot.png")
Obre el fitxer plot.png
.
Pots veure que s’ha generat un gràfic de la seqüència xs
.
Crear gràfics de senzills amb el mètode plot()
Section titled “Crear gràfics de senzills amb el mètode plot()”El gràfic més senzill que podem crear és una línia a partir d’una matriu plana (1D) o una llista.
A l’importar, ja creem un contenidor (un canvas = un lienzo) dins de l’objecte plt
.
El mètode plot
és el que dibuixa el gràfic dins del contenidor plt
.
Si no li diem res més crea un gràfic de punts amb les dades de la llista que li hem passat.
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlinel1 = [2, 1, 5, 7, 11, 12, 15, 13]plt.plot(l1)plt.show()
També podem crear un gràfic amb diverses línies (una des d’un array 1D i una altra des d’una linia)
import numpy as npimport matplotlib.pyplot as plt#%matplotlib inlinea1 = np.arange(2,17,2)l1 = [2, 1, 5, 7, 11, 12, 15, 13]plt.plot(l1)plt.plot(a1)#plt.show()plt.savefig('matplotlib1.png')
{% image “matplotlib1.png” %}
També pots veure com crear una matriu i dibuixar una recta i posar-li punts a damunt.
x = np.linspace(0, 5, 20)y = np.linspace(0, 10, 20)plt.plot(x, y, color = 'purple') # liniaplt.plot(x, y, 'o') # puntsplt.savefig('matplotlib2.png')
{% image “matplotlib2.png” %}
Amb Matplotlib, pots accedir a un enorme ventall d’opcions de visualizació 2D i fins i tot 3D. En aquest cas mostrem una ona sinusoidal radial.
Com a curiositat, aquesta funció es pot descriure matemàticament com:
Z = sin(R)
on
R = sqrt{X^2 + Y^2}
Desglossem-ho una mica més:
R
és la distància euclideana (equivalent al teorema de Pitagoras) des de l’origen (0, 0) fins a qualsevol punt ((X, Y)) en el pla.- La funció
sin(R)
pren aquesta distància radial i calcula el valor del sinus d’aquesta distància.
Les ones sinusoidals radials són útils per modelar fenòmens que es propaguen radialment des d’un punt central, com ara les ones de calor o electromagnètiques.
Aquest tipus de visualització només la mostrem com a demostració del potencial de la biblioteca.
fig = plt.figure()ax = fig.add_subplot(projection='3d')# Vectors de coordenadesX = np.arange(-5, 5, 0.15)Y = np.arange(-5, 5, 0.15)# Els transformem a matriu de coordenades.X, Y = np.meshgrid(X, Y)# Apliquem les funcionsR = np.sqrt(X**2 + Y**2)Z = np.sin(R)ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='viridis')plt.savefig('matplotlib3.png')
{% image “matplotlib3.png” %}
Paràmetres bàsics de la funció plot()
Section titled “Paràmetres bàsics de la funció plot()”Els més importants i comuns en tots els gràfics són:
- x —> Estructura de dades (llista, diccionari, array, dataframe …) on es troben les dades a dibuixar. Necessari.
- y —> Dimensió Y per figures 2D i gràfics de funcions que representin linies (siguin rectes o no) o punts en un pla.
- z —> Dimensió Z per gràfics 3D.
- color —> Color dels punts/rectes/figures.
- label —> posem llegenda a la línia.
- fmt —> format. Si tenim números decimals
Si no li diem res, la funció infereix els paràmetres (sobretot els x, y que són molt habituals), però és bona pràctica especificar paràmetres.
Apart dels paràmetres de la funció plot
, tenim altres mètodes importants dins el contenidor del gràfic (plt en el nostre cas).
Els més bàsics són els que mostren les etiquetes dels eixos i del títol del gràfic.
- ylabel(“Etiqueta eix vertical”)
- xlabel(“Etiqueta eix horitzontal”)
- title(“Titol gràfic”)
- legend() —> Mostra una llegenda de les funcions o barres
- grid() —> Mostra una graella per facilitar la visió
I com fer el gràfic més gran o més petit ? Podem usar aquest mètode abans de mostrar o guardar el gràfic, que indica les proporcions:
py plt.figure(figsize=(10,6))
Gràfics de linies.
Section titled “Gràfics de linies.”Usant els arrays de Numpy i llistes, podem crear gràfics de diverses línies.
Per exemple, la evolució de vendes de 3 models de targetes gràfiques en 4 mesos.
import matplotlib.pyplot as pltimport numpy as np
# Dades fictícies de vendes de 3 models de targetes gràfiques en 4 mesosmesos = ["Gener", "Febrer", "Març", "Abril"]gpu_models = ["RX 6400 Pulse", "AMD Radeon RX 7900 XTX", "RTX 3050 Dual OC 6 GB"]vendes_gpu = np.array([ [120, 150, 170, 200], # RX 6400 Pulse [90, 110, 140, 160], # AMD Radeon RX 7900 XTX [100, 130, 120, 180] # RTX 3050 Dual OC 6 GB])
# Crear el gràfic de línies utilitzant l'array de Numpy# Marcadors i colors per cada líniamarkers = ['o', 's', '^', 'D']colors = ['blue', 'green', 'red', 'purple']
# Crear el gràfic de línies amb un bucleplt.figure(figsize=(10, 6))for i in range(len(gpu_models)): plt.plot(mesos, vendes_gpu[i], marker=markers[i % len(markers)], label=gpu_models[i], linestyle='-', color=colors[i % len(colors)])
# Afegir títol i etiquetesplt.title('Evolució de Vendes de Targetes Gràfiques (4 mesos)', fontsize=14)plt.xlabel('Mesos', fontsize=12)plt.ylabel('Vendes (Unitats)', fontsize=12)plt.grid(True)plt.legend(title="Models de GPU")
# Mostrar el gràficplt.tight_layout()plt.savefig("plot.png")
Aquesta solució és correcta i escalable (si afegim mesos o productes funciona igual)
{% image “matplotlib32.png” %}
1.
Crea un gràfic amb 3 línies que representin la evolució del seu nivell de glucosa a la sang en mg/L que tenen 3 pacients amb grip i amb 7 columnes, una per dia; guardat en numpy.
Pots inserir manualment les mostres o bé generar-les aleatòriament; l’important és que siguin dades versemblants de persones hospitalitzades.
Una possible manera de definir les dades és la següent:
# Dades de glucosa per a tres pacients en 7 dies.days_x = np.arange(1, 8)gl_patients_y = np.array([[110,130,120,145,140,131,120], [125,135,151,143,132,120,111], [120,125,112,103,108,111,105]])
Fes que vegi una llegenda i/o la descripció del gràfic i els seus eixos, així com diferenciar cada pacient per un color diferent.
Una presentació suggerida del gràfic:
{% image “matplotlib4.png” %}
{% sol %}
Opció 1. Més senzilla, amb dades realistes.
import numpy as npimport matplotlib.pyplot as plt
# Dades de glucosa per a tres pacientsdays_x = np.arange(1, 8)gl_patients_y = np.array([[110,130,120,145,140,131,120], [125,135,151,143,132,120,111], [120,125,112,103,108,111,105]])
# Gràfica de línies (eix x--> dies, eix y--> pacients)plt.plot(days_x, gl_patients_y[0], color='blue', label='Pacient 1', linewidth=2, marker='o')plt.plot(days_x, gl_patients_y[1], color='green', label='Pacient 2', linewidth=2, linestyle='--', marker='o')plt.plot(days_x, gl_patients_y[2], color='red', label='Pacient 3', linewidth=2, linestyle='-.', marker='o')
# Afegir títol i etiquetesplt.title('Evolució Nivell de Glucosa a la Sang (mg/L) de pacients durant 7 Dies')plt.xlabel('Dies')plt.ylabel('Nivell de Glucosa (mg/L)')plt.legend()
# Afegir una graellaplt.grid(True)
# Mostrar la gràficaplt.tight_layout()# plt.show()plt.savefig('exercici11.png')
Opció 2. Conté subgràfics (opcionals en aquest cas) amb dades realistes generades aleatòriament.
import numpy as npimport matplotlib.pyplot as plt
# Configuració dels pacients i dels seus paràmetresnum_pacients = 3dies = np.arange(1, 8)
# Definició dels rangs per a cada pacientmin_values = [90, 120, 110] # valor mínim de glucosamax_values = [130, 160, 150] # valor màxim de glucosa
colors = ['blue', 'green', 'red']estils = ['-', '--', '-.']noms = ['Pacient 1', 'Pacient 2', 'Pacient 3']
# Generació de dades aleatòriesglucose_data = [ np.random.randint(min_values[i], max_values[i] + 1, 7) for i in range(num_pacients)]
# Creació de la figurafig, ax = plt.subplots(figsize=(10, 6))
# Gràfica de líniesfor i in range(num_pacients): ax.plot(dies, glucose_data[i], color=colors[i], label=noms[i], linewidth=2, linestyle=estils[i], marker='o')
ax.set_title('Nivells de Glucosa durant 7 Dies', fontsize=16)ax.set_xlabel('Dies', fontsize=14)ax.set_ylabel('Nivell de Glucosa (mg/dL)', fontsize=14)ax.legend()ax.grid(True)
# Mostrar el gràficplt.tight_layout()plt.savefig("lineplot1.png")
{% endsol %}
Diagrames de barres
Section titled “Diagrames de barres”Com els altres gràfics, es pot fer sense Numpy i aplicant el que hem après amb els diagrames de linies.
Aquests diagrames són molt adequats per mostrar les freqüències d’aparició de cada valor d’una variable.
Per exemple, els gols número de gols de cada jugadora o el número de d’aparicions de cada nucleòtid (A,C,G,T) en una cadena d’ADN.
import matplotlib.pyplot as plt
# Dades del gràfictitol = "Gràfic de màximes golejadores del Barça (Lliga 2022-2023)."etiq_y = "Núm gols"etiq_x = "Jugadores"llegenda_x = ["A.Oshoala", "S.Paralluelo", "A.Bonmati"]valors_y = [21, 11, 10]colors_y = ['tab:purple', 'tab:brown', 'tab:red']
# Creem el gràfic.bar_container = plt.bar(llegenda_x, valors_y, color=colors_y)plt.bar_label(bar_container, fmt='%.0f')plt.ylabel(etiq_y)plt.xlabel(etiq_x)plt.title(titol)#plt.legend(title='Jugadores')# Només si vols usar Collab (ipynb)#plt.show()plt.savefig('players.png')
Diagrames de barres agrupats
Section titled “Diagrames de barres agrupats”Amb Matplotlib i Numpy, també podem fer diagrames de barres agrupats; de manera semblant a com hem fet abans gràfics de diverses linies.
Per exemple, suposem que volem veure l’increment dels preus dels lloguers a ciutats de Catalunya.
Les posarem en un array de Numpy, on a l’eix Y hi haurà cada ciutat (Badalona a l’index 0, Barcelona a l’1…) i al de les X els anys (del 2021 al 2023, per exemple)
Les dades del gràfic les hem tret del dataset del preu mitjà dels lloguers als municipis de Catalunya (les hem preprocessat prèviament).
import numpy as npimport matplotlib.pyplot as plt
ciutats = ['Badalona', 'Barcelona', 'Cornella', 'L\'Hospitalet']anys = ['2021', '2022', '2023']
preusLloguers23 = np.array([ [730.4,808.02,848.91], #Badalona [918.84,1026.86,1136.4], #Barcelona [695.5,723.23,790.39], #Cornella [688.74,731.31,794.87] #L'Hospitalet])print(preusLloguers23)
# Generem el diagrama de barres per cada anyfig, ax = plt.subplots(figsize=(10, 6))
# Ample de les barresbar_width = 0.2# Posicions de les barres per cada anyx = np.arange(len(ciutats))
# Creem les barres per cada anyfor i, any in enumerate(anys): ax.bar(x + i * bar_width, data[:, i], width=bar_width, label=any)
# Configuració del gràficax.set_xlabel('Ciutats')ax.set_ylabel('Preu mitjà lloguer')ax.set_title('Increment preu mitjà lloguer ciutats AMB')ax.set_xticks(x + bar_width * (len(anys) - 1) / 2)ax.set_xticklabels(ciutats)ax.legend()
# Ajustar la visualitzacióplt.tight_layout()plt.show()
{% image “matplotlib5.png” %}
Classificació de mostres
Section titled “Classificació de mostres”Tingueu en compte que no sempre us vindran les dades “curades” i en ocasions tindreu, per exemple 100 observacions i les haureu de classificar d’alguna manera: per gènere, per edat…
Suposem que tenim en un array (o llista) els grups sanguinis de 40 pacients (A, O, AB, B) i volem fer un gràfic del número de pacients de cada grup.
Amb la funció de np.unique
aconseguirem aquesta agrupació.
Veiem-ho:
import numpy as npimport matplotlib.pyplot as plt
# Definir els grups sanguinis possiblesgrups_sanguinis = ['A', 'B', 'AB', 'O']
# Generar aleatòriament els grups sanguinis per a 40 pacientsnp.random.seed(0) # Fixem la llavor per a reproduir els resultatsmostres = np.random.choice(grups_sanguinis, 40)print(mostres)
# Agrupar les mostres per tipus.unique, counts = np.unique(mostres, return_counts=True)data = dict(zip(unique, counts))
print(data)
# Diagrama de barresplt.figure(figsize=(10, 5))plt.bar(data.keys(), data.values(), color=['red', 'blue', 'green', 'purple'])plt.title('Distribució de Grups Sanguinis (Diagrama de Barres)')plt.xlabel('Grup Sanguini')plt.ylabel('Nombre de Pacients')plt.show()
# Diagrama de sectorsplt.figure(figsize=(8, 8))plt.pie(data.values(), labels=data.keys(), autopct='%1.1f%%', colors=['red', 'blue', 'green', 'purple'])plt.title('Distribució de Grups Sanguinis (Diagrama de Sectors)')plt.show()
Les parts més interessants i novedoses del codi es troben al principi:
- Es generen les dades aleatòriament.
- Encara més important, aquestes linies que guarden els valors únics (unique) i el número d’ocurrències (counts)
- Al final guarden tot en un diccionari, per conveniència (però potser no caldria)
# Agrupar les mostres per tipus.unique, counts = np.unique(mostres, return_counts=True)data = dict(zip(unique, counts))
{% image “matplotlib6.png” %}
Ja sabem el més bàsic de generació de gràfics: la selecció de dades.
2. Crea un diagrama de barres que mostri la freqüència d’aparició dels 4 nucleòtids (A, C, G i T)
d’una cadena d’ADN.
dna_seq = “ACGTACGATGCAAGCTAGCTAGCAAGCTAGCTAGCTAGCTAGCTAGCTAGCTAGCTAGCTAGCTAGGTC”
{% sol %}
import numpy as npimport matplotlib.pyplot as plt
dna_seq = "ACGTACGATGCAAGCTAGCTAGCAAGCTAGCTAGCTAGCTAGCTAGCTAGCTAGCTAGCTAGCTAGGTC"
freq_seq = {nuc: 0 for nuc in 'ACGT'}
# Comptem les freqüències de les lletres A, C, G i T en una sola passadafor nucleotide in dna_seq: if nucleotide in freq_seq: freq_seq[nucleotide] += 1
# Diagrama de barresplt.figure(figsize=(10, 5))plt.bar(freq_seq.keys(), freq_seq.values(), color=['orange', 'green', 'blue', 'red'])plt.title('Freqüència d\'Aparició de Lletres en la Cadena d\'ADN')plt.xlabel('Lletres ADN')plt.ylabel('Frequència')plt.ylim(0, max(freq_seq.values()) + 1)plt.tight_layout()plt.savefig("lineplot1.png")
{% endsol %}
Heatmaps
Section titled “Heatmaps”Els heatmaps
o mapes de calor són una alternativa molt útil als diagrames de barra compostos, per tal de veure totes les freqüències d’aparició de cada variable.
Generen una taula bidimensional amb els valors de 2 variables, on la llegenda de colors de les dades més altes les marca d’un color intens (pex vermell) i les més baixs d’un altre color oposat (pex blau).
Veiem-ho amb un exemple que ja vam veure a la secció de Numpy: el fitxer CSV de les temperatures de Barcelona dels últims 8 anys
Anem a suposar que ja hem carregat les temperatures dels últims 8 anys i generem el gràfic:
import numpy as npimport matplotlib.pyplot as plt
dades = [ [2016, 10.7, 11.3, 11.1, 13.6, 16.4, 21.6, 24.9, 24.5, 22.3, 17.1, 12.7, 11.5], [2017, 7.9, 11.4, 13.3, 14.2, 18.3, 23.6, 24.2, 24.5, 19.5, 18.6, 12.5, 8.5], [2018, 10.5, 6.7, 10.8, 14.7, 17.1, 21.5, 25.3, 25.8, 22.5, 17.0, 12.4, 11.1], [2019, 8.1, 11.9, 13.5, 13.4, 15.6, 21.9, 25.4, 25.1, 21.8, 18.5, 11.9, 11.2], [2020, 10.0, 12.8, 11.9, 14.3, 19.4, 20.1, 25.0, 25.5, 21.7, 16.4, 14.7, 9.3], [2021, 7.7, 11.6, 12.1, 12.9, 17.3, 23.3, 24.8, 24.5, 23.0, 18.1, 11.3, 10.9], [2022, 10.2, 11.8, 10.8, 14.1, 20.7, 24.7, 26.7, 27.2, 22.5, 20.7, 15.2, 12.6], [2023, 9.2, 10.3, 14.1, 16.1, 18.1, 23.4, 25.5, 26.0, 23.2, 20.2, 14.8, 12.1]]data = np.array(dades)
# Separar les columnes per a les etiquetes dels anys i les temperaturesanys = data[:, 0]temperatures = data[:, 1:]
# Crear el heatmap anotatfig, ax = plt.subplots(figsize=(12, 8))cax = ax.matshow(temperatures, cmap='coolwarm')
# Afegir la barra de colorcbar = fig.colorbar(cax, ax=ax, orientation='vertical')cbar.set_label('Temperatura (°C)')
# Afegir anotacionsfor (i, j), val in np.ndenumerate(temperatures): ax.text(j, i, f'{val:.1f}', ha='center', va='center', color='black')
# Configurar les etiquetesax.set_xlabel('Mesos')ax.set_ylabel('Anys')ax.set_title('Heatmap de les temperatures mensuals a Barcelona (2016-2023)')
# Ajustar les etiquetes de l'eix X (mesos)ax.set_xticks(np.arange(12))ax.set_xticklabels(['Gen', 'Feb', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Oct', 'Nov', 'Des'])
# Ajustar les etiquetes de l'eix Y (anys)ax.set_yticks(np.arange(data.shape[0]))ax.set_yticklabels([str(int(year)) for year in anys])
plt.savefig("matplotlib7.png")
{% image “matplotlib7.png” %}
Però a la vida real no tindrem les dades tan curades, haurem de carregar-les d’un o més fonts i preprocessar-les.
En el nostre cas, necessitem descarregar-nos les dades si no les tenim al disc (connectar-se a una altra URL té un cost computacional i ambiental considerable).
Recordeu com podem aconseguir aquesta descàrrega i lectura de fitxers CSV que són en una URL externa:
xtec.dev -> Lectura i tractament de fitxers amb Python
Un cop les tenim, carreguem les dades de les temperatures en un array, i filtrem únicament les 8 últimes.
Guardem els últims anys (2016 - 2023) en un vector 1D separat de les temperatures (sinó, tindrem errors ja que els arrays han de ser homogenis).
La resta de passos, per a generar el gràfic, són els mateixos.
El codi final queda així:
import numpy as np# Per a ipynb:# !pip install nptypingfrom nptyping import NDArray, Int, Floatimport os.pathfrom urllib.request import urlretrieveimport matplotlib.pyplot as plt
url : str = 'https://opendata-ajuntament.barcelona.cat/data/dataset/73f09843-ab4e-4f13-81fb-b801ca371909/resource/0e3b6840-7dff-4731-a556-44fac28a7873/download/temperaturesbcndesde1780_2023.csv'
file : str = 'temperaturesbcn_2023.csv'
# Descarregar el fitxer CSV només si no existeixif not os.path.isfile(file): try: urlretrieve(url, file) print(f"Fitxer {file} descarregat correctament.") except Exception as e: print(f"No s'ha pogut descarregar el fitxer des de {url}: {str(e)}")
# Lectura del tot el fitxer CSV excepte la capçaleradata = np.loadtxt('temperaturesbcn_2023.csv', delimiter=',', skiprows=1)
# Seleccionem les últimes 8 files de temperatures (2016 - 2023)# Separem la columna dels anys i la eliminem de l'array de temperatures.temps_array = data[-8:,1:13]anys_array = data[-8:,0]
# print(temps_array)# print(anys_array)
# Crear el heatmap anotatfig, ax = plt.subplots(figsize=(12, 8))cax = ax.matshow(temps_array, cmap='coolwarm')
# Afegir la barra de colorcbar = fig.colorbar(cax, ax=ax, orientation='vertical')cbar.set_label('Temperatura (°C)')
# Afegir anotacions i etiquetesfor (i, j), val in np.ndenumerate(temps_array): ax.text(j, i, f'{val:.1f}', ha='center', va='center', color='black')
ax.set_xlabel('Mesos')ax.set_ylabel('Anys')ax.set_title('Heatmap de les temperatures mensuals a Barcelona (2016-2023)')
# Ajustar les etiquetes de l'eix X (mesos)ax.set_xticks(np.arange(12))ax.set_xticklabels(['Gen', 'Feb', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Oct', 'Nov', 'Des'])
# Ajustar les etiquetes de l'eix Y (anys)ax.set_yticks(np.arange(len(anys_array))) # Canviar el número de ticks a 8 (les últimes 8 files)ax.set_yticklabels([str(int(year)) for year in anys_array])
plt.savefig("matplotlib7.png")
El resultat del gràfic és el mateix, però el resolem amb un codi útil en més contextos i mantenible.
3.
Crea un Heatmap a partir de l’exemple dels preus de lloguer, en vermell els preus més alts i en verd els més baixos.
El codi de partida és el següent:
import numpy as npimport matplotlib.pyplot as plt
ciutats = ['Badalona', 'Barcelona', 'Cornella', 'L\'Hospitalet']anys = ['2021', '2022', '2023']
preusLloguers23 = np.array([ [730.4,808.02,848.91], #Badalona [918.84,1026.86,1136.4], #Barcelona [695.5,723.23,790.39], #Cornella [688.74,731.31,794.87] #L'Hospitalet])print(preusLloguers23)
{% sol %}
import numpy as npimport matplotlib.pyplot as plt
ciutats_llista : list[str] = ['Badalona', 'Barcelona', 'Cornella', 'L\'Hospitalet']anys_llista : list[str] = ['2021', '2022', '2023']
preusLloguers23 = np.array([ [730.4,808.02,848.91], #Badalona [918.84,1026.86,1136.4], #Barcelona [695.5,723.23,790.39], #Cornella [688.74,731.31,794.87] #L'Hospitalet])print(preusLloguers23)# Em ve de gust trasposar la matriu per presentar millor el heatmap.preusLloguers23 = preusLloguers23.T
# Crear el heatmap anotatfig, ax = plt.subplots(figsize=(10, 6))# Catàleg colors a:# https://matplotlib.org/stable/users/explain/colors/colormaps.htmlcax = ax.matshow(preusLloguers23, cmap='YlOrRd')
# Afegir la barra de colorcbar = fig.colorbar(cax, ax=ax, orientation='vertical')cbar.set_label('Preus (en €)')
# Afegir anotacionsfor (i, j), val in np.ndenumerate(preusLloguers23): ax.text(j, i, f'{val:.1f}', ha='center', va='center', color='black')
# Configurar les etiquetesax.set_xlabel('Ciutats')ax.set_ylabel('Anys')ax.set_title('Heatmap dels preus de lloguers aprop de Barcelona (2021-2023)')
# Ajustar les etiquetes de l'eix X (ciutats)ax.set_xticks(np.arange(len(ciutats_llista)))ax.set_xticklabels(ciutats_llista)
# Ajustar les etiquetes de l'eix Y (anys)ax.set_yticks(np.arange(len(anys_llista)))ax.set_yticklabels(anys_llista)
plt.savefig("matplotlib7.png")
{% endsol %}
Scatter Plot
Section titled “Scatter Plot”Els scatter plots són gràfics de punts en un pla (2D) o espai (3D). Podem pintar aqusts punts de diversos colors o formes si usem subplots. Això als científics els va molt bé per a classificar a mostres d’individus o espècies per dues carecterístiques que puguin tenir relació (per exemple, podem fer un mapa de punts de 2 colors: home i dona; tenint en compte la seva alçada i pes).
Però millor ho veiem amb un dataset
d’exemple mostra les diverses espècies de la planta Iris. Aquest conjunt de dades, de 50 plantes de 3 espècies i 4 carecterístiques importants, és un recurs típic per introduïr-nos al data science.
{% image “iris_samples.png” %}
import numpy as npimport matplotlib.pyplot as pltimport requestsimport csvimport os
url_file : str = 'https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv'local_file : str = 'iris.csv'
# Verificar si el fitxer ja existeix localmentif not os.path.exists(local_file): # Descarregar el fitxer si no existeix response = requests.get(url_file) with open(local_file, 'wb') as f: f.write(response.content)
# Llegir les dades del fitxer CSV amb NumPywith open(local_file, 'r') as f: reader = csv.reader(f) headers = next(reader) # Saltar la fila de capçaleres # Ho llegirà com array d'strings per evitar errors. data = np.array([row for row in reader])
# Convertir les dades a arrays 1D de Numpy amb tipus ben definit.sepal_length = data[:, 0].astype(float)petal_length = data[:, 2].astype(float)species = data[:, 4]
# Crear un subgràfic de punts per a cada espècie (n'hi ha 3)plt.figure(figsize=(10, 6))
# Recorrem les espècies úniques i dibuixem els punts corresponentsfor specie in np.unique(species): # Seleccionem les longituds de pètals i sèpals per a l'espècie actual petal_lengths = petal_length[species == specie] sepal_lengths = sepal_length[species == specie] # Dibuixem els punts per a aquesta espècie amb l'etiqueta corresponent plt.scatter(petal_lengths, sepal_lengths, label=specie)
plt.xlabel('Longitud dels pètals')plt.ylabel('Longitud de les sèpals')plt.title('Distribució de longituds de pètals i sèpals per espècie (Iris Dataset)')plt.legend()plt.grid(True)plt.savefig("matplotlib8.png")
{% image “matplotlib8.png” %}
Amb aquest gràfic, podem deduïr que la Iris Setosa sol tenir els pètals més petits respecte les altres 2 espècies i la Virginica els més grans.
Histogrames i corbes de distribució normal.
Section titled “Histogrames i corbes de distribució normal.”Una de les tasques més comuns en estadística (tant descriptiva = dades, com inferencial = probabilitats) és generar gràfics per veure quina distribució presenten les dades un cop sabem la freqüència de cada ocurrència d’una variable; i gràcies a Numpy i Matplotlib podem generar aquest gràfic molt fàcilment.
Una de les distribucions teòriques més utilitzada a la pràctica és la distribució normal, també anomenada distribució gaussiana
en honor al matemàtic Carl Friedrich Gauss.
Ara veurem la potència que té Numpy per a generar una colecció de dades que segueixen la distribució Normal i Matplotlib per a dibuixar tant un histograma per veure la distribució i una línia per veure com s’assembla la distribució que tenim respecte la Normal.
Provem aquest exemple fictici del nivell de colesterol a la sang de 300 pacients.
import numpy as npimport matplotlib.pyplot as plt
# Paràmetres per a la distribució normalmean = 180 # mitjana del colesterol (mg/dL)std_dev = 25 # desviació estàndard (mg/dL)n_samples = 300 # nombre de mostres
# Generar dades aleatòries amb una distribució normalcholesterol_levels = np.random.normal(mean, std_dev, n_samples)
# Crear l'histogramaplt.hist(cholesterol_levels, bins=20, density=True, alpha=0.6, color='g', edgecolor='black')
# Crear la corba de distribució normalxmin, xmax = plt.xlim()x = np.linspace(xmin, xmax, 100)p = np.exp(-0.5*((x-mean)/std_dev)**2) / (std_dev * np.sqrt(2 * np.pi))plt.plot(x, p, 'k', linewidth=2)
# Afegir títol i etiquetesplt.title('Distribució dels Nivells de Colesterol')plt.xlabel('Nivells de Colesterol (mg/dL)')plt.ylabel('Freqüència')
# Afegir línia vertical a la mitjanaplt.axvline(mean, color='b', linestyle='dashed', linewidth=2, label='Mitjana')
# Afegir línies verticals als intervals de confiança (±1 desviació estàndard)plt.axvline(mean - std_dev, color='r', linestyle='dotted', linewidth=2, label='-1 Desviació estàndard')plt.axvline(mean + std_dev, color='r', linestyle='dotted', linewidth=2, label='+1 Desviació estàndard')
plt.legend()plt.savefig("matplotlib9.png")
{% image “matplotlib9.png” %}
Exercicis. Gràfics anàlisi de la qualitat de vins.
Section titled “Exercicis. Gràfics anàlisi de la qualitat de vins.”El dataset de qualitat del vi (Wine Quality Dataset) és un conjunt de dades molt utilitzat en l’anàlisi de dades i el machine learning.
Conté informació sobre les característiques físiques i químiques dels vins, així com les puntuacions de qualitat que han rebut en tests de tast.
Pots trobar el resum del dataset a la seva web oficial:
De totes les columnes, les que ens interessa guardar per a les gràfiques que farem són:
- 9 - pH (els vins són àcids, el seu PH pot oscilar entre 2.5 i 4.5)
- 11 - alcohol (en %, sol oscilar entre 9 i 15)
- 12 - quality (puntuació entre 0 i 10)
Ens han demanat els següents gràfics per tal d’analitzar la qualitat dels vins que tenim:
4. Crear un gràfic de dispersió (scatter plot) que mostri la relació entre dues característiques del vi, per exemple, el contingut d’alcohol i la puntuació de qualitat. Pots provar qualsevol altre mesura, com el PH.
5. Crear un histograma que mostri la distribució de la puntuació de qualitat dels vins i afegeix la corba de distribució normal.
{% sol %}
Primer, creem un mòdul d’utilitat wineutils.py per llegir (i descarregar només si cal) els fitxers:
import numpy as npimport urllib3import os
def get_file(url, file): http = urllib3.PoolManager() if not os.path.isfile(file): response = http.request("GET", url)
# Comprovar si la petició ha estat exitosa if response.status == 200: print(f"Descarregant el fitxer: {file}") with open(file, "wb") as f: f.write(response.data) else: print(f"Error en descarregar el fitxer: {response.status}")
return file
def read_csv(local_file): data = np.genfromtxt(local_file, delimiter=';', skip_header=1) return data
Ex1.
import numpy as npimport matplotlib.pyplot as pltimport wineutils
if __name__ == '__main__':
url_file = 'https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv' local_file = 'winequality-red.csv'
file = wineutils.get_file(url_file, local_file) data = wineutils.read_csv(file)
# Seleccionar les columnes d'alcohol (column index 10) i qualitat (column index 11) alcohol_content = data[:, 9] # Contingut d'alcohol quality_scores = data[:, 11] # Puntuació de qualitat
# Crear el gràfic de dispersió plt.figure(figsize=(10, 6)) plt.scatter(alcohol_content, quality_scores, alpha=0.6, edgecolors='w') plt.title('Relació entre el Contingut d\'Alcohol i la Puntuació de Qualitat') plt.xlabel('% Alcohol del vi') plt.ylabel('Puntuació de Qualitat') plt.grid(True) # plt.show() No funciona sempre. plt.savefig('wine_scatter2.png')
Ex5.
import numpy as npimport matplotlib.pyplot as pltimport wineutils
if __name__ == '__main__':
url_file = 'https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv' local_file = 'winequality-red.csv'
file = wineutils.get_file(url_file, local_file) data = wineutils.read_csv(file)
# Seleccionar la columna de qualitat (column index 11) quality_scores = data[:, 11] # Puntuació de qualitat
# Paràmetres per a la distribució normal mean = np.mean(quality_scores) std_dev = np.std(quality_scores)
# Crear l'histograma plt.hist(quality_scores, bins=6, density=True, alpha=0.6, color='lightcoral', edgecolor='black')
# Crear la corba de distribució normal xmin, xmax = plt.xlim() x = np.linspace(xmin, xmax, 100) p = np.exp(-0.5*((x-mean)/std_dev)**2) / (std_dev * np.sqrt(2 * np.pi)) plt.plot(x, p, 'k', linewidth=2)
plt.title('Distribució de la Qualitat del Vi') plt.xlabel('Qualitat') plt.ylabel('Freqüència')
# Afegir línia vertical a la mitjana plt.axvline(mean, color='r', linestyle='dashed', linewidth=2, label='Mitjana') plt.legend()
plt.savefig('wine_dist.png')
{% endsol %}
Gràfics a la web amb FastAPI
Section titled “Gràfics a la web amb FastAPI”A continuació veurás com servir els gràfics que has generat via web.
Arrenca una màquina {% link “/windows/wsl/” %}.
Clona el projecte https://gitlab.com/xtec/python/matplotlib
> git clone https://gitlab.com/xtec/python/matplotlib
Obre el projecte amb {% link “/project/vscode” %}:
> code matplotlib
Ara, prova de mostrar algún dels teus gràfics a la web. 📈📊
Referències.
Section titled “Referències.”El contingut d'aquest lloc web té llicència CC BY-NC-ND 4.0.
©2022-2025 xtec.dev