La lliçó anterior va acabar amb les respostes en taules: dissabte 130 unitats, abril 168, Faust amb un 11.9 % de conversió. Correctes, però l'Anna no pensa en taules — i el seu cervell, com el de tothom, processa una barra més alta que una altra en mil·lisegons i una columna de números en... força més. Matplotlib és la llibreria de visualització de referència en Python: la més antiga, la més flexible i la base sobre la qual es construeixen gairebé totes les altres. Aquesta lliçó ensenya a triar el gràfic segons la pregunta, a muntar-lo peça a peça i a no mentir pel camí, per acabar penjant a la rebotiga de Papyrus un panell amb els números del semestre.
Contingut
- Per què visualitzar: la lliçó d'Anscombe
- Anatomia d'una figura
plt.plot: l'evolució mensual (i Sant Jordi)baribarh: comparar títolshist: la distribució de quantiesscatter: preu contra unitatspiei quan no usar-losubplots: el panell 2×2 de la botiga- Estil i llegibilitat
- La drecera
df.plotisavefig - Quin gràfic per a quina pregunta
Per què visualitzar: la lliçó d'Anscombe
El 1973 l'estadístic Francis Anscombe va construir quatre conjunts de dades amb les mateixes estadístiques — mateixa mitjana de x i y, mateixa variància, mateixa recta de regressió — que en dibuixar-los resulten ser quatre històries completament diferents: un núvol lineal, una corba, una recta perfecta amb un punt rebel i un cas degenerat on un únic punt fabrica tota la correlació. La moralitat és doble i governa aquesta lliçó:
- Els resums numèrics amaguen la forma. El
describe()de 11-03 no distingeix aquests quatre mons; un cop d'ull, sí. - Dibuixa sempre abans de concloure. Especialment abans d'ajustar el model de 11-05: la regressió d'Anscombe era "correcta" en els quatre casos i només tenia sentit en un.
Anatomia d'una figura
Matplotlib distingeix el llenç (Figure) de cada zona de dibuix (Axes — uns eixos amb les seves dades, títol i llegenda; una Figure en pot contenir diversos). Gairebé tots els problemes de "on poso el títol?" es resolen sabent a quin dels dos pertany cada peça.
flowchart TB
F["Figure (el llenç)"] --> A["Axes (la zona de dibuix)"]
A --> T["títol — set_title"]
A --> X["eix X — set_xlabel, ticks"]
A --> Y["eix Y — set_ylabel"]
A --> D["les dades — plot, bar, scatter..."]
A --> L["llegenda — legend (usa els label=)"]
La manera recomanada de treballar és la interfície orientada a objectes: plt.subplots() et dona la parella fig, ax i a partir d'aquí tot són mètodes d'ax — res d'estat global amagat:
import matplotlib.pyplot as plt # l'alies plt es convencio, com np i pd
fig, ax = plt.subplots() # 1 Figure amb 1 Axes
ax.set_title("El meu primer grafic")
plt.show() # a Jupyter apareix sol; en un script .py, show() obre la finestraplt.plot: l'evolució mensual (i Sant Jordi)
Les línies expliquen evolució en el temps. Les dades: les unitats mensuals que groupby ens va donar a 11-03.
mesos = ["gen", "feb", "mar", "abr", "maig", "juny"]
unitats = [62, 58, 71, 168, 84, 77]
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(mesos, unitats, marker="o", label="unitats/mes")
ax.annotate("Sant Jordi (23 abr)", xy=("abr", 168), xytext=("feb", 150),
arrowprops=dict(arrowstyle="->"))
ax.set_title("Papyrus — unitats venudes per mes (2026)")
ax.set_xlabel("Mes")
ax.set_ylabel("Unitats")
ax.legend()
plt.show()El marker="o" assenyala les dades reals (només hi ha 6 punts; la línia entre ells és una guia visual, no mesurament), i annotate clava la fletxa al pic: abril, 168 unitats, més del doble d'un mes normal. És la mateixa dada de l'axis=0 de 11-02 i del groupby de 11-03 — però ara es veu, i l'Anna ho entén sense saber Python.
bar i barh: comparar títols
Les barres comparen categories. Unitats per títol (11-03):
titols = ["El Quixot", "L'Odissea", "Hamlet", "Faust"]
unitats_titol = [182, 154, 121, 63]
fig, ax = plt.subplots()
ax.bar(titols, unitats_titol)
ax.set_ylabel("Unitats (gen-juny)")
ax.set_title("Vendes per titol")
plt.show()Dos consells que valen or: ordena les barres per valor (ja ho estan: és el sort_values de 11-03 treballant per al gràfic) i, amb noms llargs o moltes categories, usa barh (barres horitzontals): les etiquetes es llegeixen d'una tirada sense girar el cap — ax.barh(titols[::-1], unitats_titol[::-1]) (invertits perquè el més gran quedi a dalt).
I una regla d'honestedat: l'eix d'una barra comença a 0. Una barra que arrenca a 100 converteix una diferència del 5 % en un abisme visual. Matplotlib ho fa bé per defecte; no ho "arreglis".
hist: la distribució de quanties
L'histograma trosseja un rang en intervals (bins) i compta quants valors cauen a cadascun: és la forma de les dades, allò que describe() resumeix a cegues.
df = pd.read_csv("dades/vendes_2026.csv", parse_dates=["data"])
fig, ax = plt.subplots()
ax.hist(df["quantia"], bins=20, edgecolor="white")
ax.set_xlabel("Quantia de la venda (€)")
ax.set_ylabel("Nombre de vendes")
ax.set_title("Distribucio de quanties")
plt.show()El dibuix confirma la sospita de 11-01: una muntanya concentrada entre 9 i 16 € (les vendes d'un exemplar) i una cua llarga a la dreta — els packs de Sant Jordi fins a 63.60 € — que és la que separa la mitjana (15.03) de la mediana (12.50). Prova bins=5 i bins=50: pocs bins amaguen la forma, massa la converteixen en soroll. No hi ha número màgic; es tanteja.
scatter: preu contra unitats
El diagrama de dispersió busca relació entre dues variables numèriques:
preus = [15.90, 12.50, 9.95, 21.00]
unitats_titol = [182, 154, 121, 63]
fig, ax = plt.subplots()
ax.scatter(preus, unitats_titol, s=80)
for p, u, t in zip(preus, unitats_titol, ["El Quixot", "L'Odissea", "Hamlet", "Faust"]):
ax.annotate(t, (p, u), xytext=(5, 5), textcoords="offset points")
ax.set_xlabel("Preu (€)")
ax.set_ylabel("Unitats venudes")
ax.set_title("Ven menys el que costa mes?")
plt.show()Quatre punts i una història: no hi ha una recta neta (El Quixot, a 15.90, és el supervendes), però Faust està sol al seu racó — el més car i el que menys ven, coherent amb el seu 11.9 % de conversió de 11-03. Amb només 4 punts això és una pista, no una llei: exactament la prudència que necessitarem en ajustar la regressió de 11-05 sobre aquest mateix parell de variables.
pie i quan no usar-lo
fig, ax = plt.subplots()
ax.pie(unitats_titol, labels=["El Quixot", "L'Odissea", "Hamlet", "Faust"],
autopct="%1.0f%%")
ax.set_title("Quota d'unitats per titol")
plt.show()L'advertiment honest: l'ull humà compara longituds molt millor que angles. És més gran la porció de L'Odissea (30 %) o la de Hamlet (23 %)? Al pastís costa; a la barra és evident. El pie només funciona amb poques categories (2-4), parts d'un tot i diferències grans — el repartiment botiga/web (60/40) passa el tall; gairebé tota la resta està millor en barres. Si dubtes, barres.
subplots: el panell 2×2 de la botiga
Tot junt: el panell que l'Anna vol imprimir i penjar a la rebotiga.
fig, axs = plt.subplots(2, 2, figsize=(11, 7)) # axs es un array 2x2 d'Axes (11-02!)
axs[0, 0].plot(mesos, unitats, marker="o")
axs[0, 0].set_title("Unitats per mes")
axs[0, 1].barh(titols[::-1], unitats_titol[::-1])
axs[0, 1].set_title("Unitats per titol")
axs[1, 0].hist(df["quantia"], bins=20, edgecolor="white")
axs[1, 0].set_title("Distribucio de quanties (€)")
dies = ["dl", "dt", "dc", "dj", "dv", "ds", "dg"]
axs[1, 1].bar(dies, [48, 52, 55, 60, 78, 130, 97])
axs[1, 1].set_title("Unitats per dia de la setmana")
fig.suptitle("Papyrus — panell del primer semestre 2026", fontsize=14)
fig.tight_layout()
plt.show()subplots(2, 2) retorna la Figure i una matriu 2×2 d'Axes que s'indexa com els arrays de 11-02 (axs[1, 1] = a baix a la dreta). suptitle titula el llenç sencer i tight_layout() ho recol·loca tot perquè res no es trepitgi — sense ell, els títols de dalt es mengen els eixos de baix.
Estil i llegibilitat
- Etiquetes SEMPRE. Un gràfic sense
xlabel/ylabel/titleés una endevinalla. Si l'eix són euros, que ho digui; el "jo ja sé què és" caduca en una setmana. - Colors amb criteri. El blau per defecte està bé; l'arc de Sant Martí, no. Usa un color neutre per a tot i un de sol d'accent per a la dada que importa (Faust a l'scatter, l'abril a la línia). El color ha de significar alguna cosa o no ser-hi. I recorda que ~8 % dels homes confonen vermell i verd: no recolzis en aquesta parella l'única distinció.
- Dates rotades. Amb moltes dates a l'eix X s'amunteguen:
ax.tick_params(axis="x", rotation=45)ofig.autofmt_xdate(). - Menys és més. Cada element (graella, vora, llegenda d'una sola sèrie) ha de justificar la seva tinta.
ax.grid(alpha=0.3)com a molt.
La drecera df.plot i savefig
pandas embolica Matplotlib: tota Series o DataFrame té .plot, ideal per mirar ràpid mentre explores:
df.groupby(df["data"].dt.month)["unitats"].sum().plot(kind="bar", title="Unitats per mes")
df["quantia"].plot(kind="hist", bins=20)Una línia des del groupby fins al dibuix. Retorna l'ax de sempre, així que pots continuar retocant (ax.set_ylabel(...)). Per explorar, df.plot; per a la figura definitiva i a mida, la interfície fig, ax.
I el pas final — l'Anna no executa Python, l'Anna rep un PNG:
Al directori informes/ de M6, com els CSV de 11-03. dpi=150 dona qualitat d'impressió raonable i bbox_inches="tight" retalla marges morts. Important: crida savefig abans de show() — després de mostrar, la figura pot quedar en blanc.
Quin gràfic per a quina pregunta
| Tipus de pregunta | Gràfic | Exemple Papyrus |
|---|---|---|
| Com evoluciona en el temps? | plot (línies) |
Unitats per mes, pic de Sant Jordi |
| Com es comparen categories? | bar / barh |
Vendes per títol, per dia de la setmana |
| Quina forma tenen les meves dades? | hist |
Distribució de quanties |
| Es relacionen dues variables? | scatter |
Preu vs unitats |
| Quina part del tot? (poques parts) | pie (amb cautela) |
Repartiment botiga/web |
| Diverses preguntes alhora | subplots |
El panell 2×2 |
Quan aquesta taula se't quedi curta: seaborn (gràfics estadístics elegants sobre Matplotlib, en una línia el que aquí són deu) i plotly (gràfics interactius amb zoom i tooltips, perfectes per a la web de M10) són les següents parades naturals. Tot el que has après aquí — figures, eixos, tipus de gràfic, honestedat — es transfereix tal qual.
Errors Comuns i Consells
- Oblidar
plt.show()en scripts. A Jupyter la figura apareix sola; en un.pysenseshow()el programa acaba sense mostrar res. (Isavefigva abans deshow.) - Reutilitzar la figura sense voler. Dibuixar dos gràfics seguits sobre el mateix
axels superposa. Cada figura nova, el seufig, ax = plt.subplots(). - Truncar l'eix Y en barres. El clàssic dels gràfics mentiders: diferències minúscules semblen enormes. En barres, des de 0; en línies s'admet retallar si se senyalitza.
- Massa sèries en un gràfic. Cinc línies creuant-se són espaguetis. Millor
subplotsamb una per panell, o ressalta'n una i apaga la resta en gris. - Eixos sense unitats. "168 què? euros, unitats, visites?" Cada informe que surt d'
informes/ha de sobreviure sense tu al costat explicant-lo.
Exercicis
- Dibuixa l'evolució mensual de la facturació (suma de
quantiaper mes) com a línia amb marcadors, anotant Sant Jordi, partint delgroupbycorresponent de 11-03. Etiqueta els eixos amb unitats (€) i desa-la ainformes/facturacio_mensual.png. - Fes un gràfic de barres agrupades amb les unitats per títol i canal (la taula
unstack()de 11-03: botiga 121/38/74/79, web 61/25/47/75). Pista:taula.T.plot(kind="bar")ho fa en una línia; interpreta quina història explica. - El panell 2×2 de la lliçó usa el mateix color per a tot. Modifica'l perquè només la barra del dissabte (al gràfic de dies) i el punt de l'abril (a la línia mensual) quedin en un color d'accent, i la resta en gris. Pista:
baraccepta una llista de colors, i pots superposar unax.plotd'un únic punt.
Solucions
-
La corba calca la d'unitats — pic a l'abril — perquè el tiquet mitjà gairebé no varia entre mesos. Comprovar que dues mètriques expliquen la mateixa història també és una troballa.fact = df.groupby(df["data"].dt.month)["quantia"].sum() fig, ax = plt.subplots(figsize=(8, 4)) ax.plot(["gen", "feb", "mar", "abr", "maig", "juny"], fact.values, marker="o") ax.annotate("Sant Jordi", xy=("abr", fact[4]), xytext=("feb", fact[4] * 0.9), arrowprops=dict(arrowstyle="->")) ax.set_title("Papyrus — facturacio mensual 2026") ax.set_xlabel("Mes"); ax.set_ylabel("Facturacio (€)") fig.savefig("informes/facturacio_mensual.png", dpi=150, bbox_inches="tight") plt.show() -
La història: en gairebé tots els títols la barra de botiga guanya... llevat de L'Odissea, on la web gairebé iguala la botiga (75 vs 79) i supera amb claredat el Quixot web. La troballa de 11-03, ara visible d'un sol cop.taula = df.groupby(["canal", "titol"])["unitats"].sum().unstack() ax = taula.T.plot(kind="bar", figsize=(8, 4)) # .T: titols a l'eix X, una barra per canal ax.set_ylabel("Unitats"); ax.set_title("Vendes per titol i canal") ax.tick_params(axis="x", rotation=0) plt.show() -
El gris apaga el que és secundari i l'accent dirigeix l'ull exactament a les dues dades que sostenen la decisió de l'Anna: el dissabte i Sant Jordi. Aquest "apagar per ressaltar" és probablement el truc de comunicació amb més rendiment per línia de codi de tota la lliçó.colors = ["#9e9e9e"] * 7 colors[5] = "#d62728" # dissabte, index 5 axs[1, 1].bar(dies, unitats_dia, color=colors) axs[0, 0].plot(mesos, unitats, marker="o", color="#9e9e9e") axs[0, 0].plot(["abr"], [168], marker="o", color="#d62728", markersize=10)
Conclusió
Ja saps convertir els números de Papyrus en històries: línies per a l'evolució (i la fletxa clavada a Sant Jordi), barres per comparar títols i dies, histogrames per veure la forma que describe() amaga, scatter per sospitar relacions — amb Faust sol al seu racó car —, el pastís sota fiança, i el panell 2×2 desat com a PNG a informes/ per a la rebotiga. I el principi que ho governa tot, herència d'Anscombe: dibuixa abans de concloure, perquè els resums amaguen la forma. Aquest consell serà immediatament pràctic: la pregunta que queda viva — quant estoc demanar per al proper Sant Jordi? — no es respon descrivint el passat, sinó projectant-lo. Aprendre patrons de l'històric per predir el que ve és aprenentatge automàtic, i l'última lliçó del mòdul ho fa de veritat, amb scikit-learn, dos casos complets i els advertiments honestos que el màrqueting sol saltar-se.
Curs de Programació en Python
Mòdul 1: Introducció a Python
- Introducció a Python
- Configuració de l'Entorn de Desenvolupament
- Sintaxi de Python i Tipus de Dades Bàsics
- Variables i Constants
- Entrada i Sortida Bàsica
- Entorns Virtuals i Gestió de Paquets
Mòdul 2: Estructures de Control
Mòdul 3: Funcions i Mòduls
- Definició de Funcions
- Arguments de Funció
- Funcions Lambda
- Mòduls i Paquets
- Visió General de la Biblioteca Estàndard
Mòdul 4: Estructures de Dades
Mòdul 5: Programació Orientada a Objectes
Mòdul 6: Gestió de Fitxers
- Lectura i Escriptura de Fitxers
- Treball amb Fitxers CSV
- Gestió de Dades JSON
- Operacions amb Fitxers i Directoris
Mòdul 7: Gestió d'Errors i Excepcions
- Introducció a les Excepcions
- Gestió d'Excepcions
- Llançament d'Excepcions
- Excepcions Personalitzades
- Bones Pràctiques i Registre d'Errors amb logging
Mòdul 8: Temes Avançats
- Anotacions de Tipus (type hints)
- Decoradors
- Generadors
- Gestors de Context
- Concurrència: Fils i Processos
- Asyncio per a la Programació Asíncrona
Mòdul 9: Proves i Depuració
- Introducció a les Proves
- Proves Unitàries amb unittest
- Proves amb pytest
- Desenvolupament Guiat per Proves
- Tècniques de Depuració
- Ús de pdb per a la Depuració
Mòdul 10: Desenvolupament Web amb Python
- Introducció al Desenvolupament Web
- Fonaments del Framework Flask
- Construcció d'APIs REST amb Flask
- Introducció a Django
- Construcció d'Aplicacions Web amb Django
Mòdul 11: Ciència de Dades amb Python
- Introducció a la Ciència de Dades
- NumPy per a la Computació Numèrica
- Pandas per a la Manipulació de Dades
- Matplotlib per a la Visualització de Dades
- Introducció a l'Aprenentatge Automàtic amb scikit-learn
