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

  1. Per què visualitzar: la lliçó d'Anscombe
  2. Anatomia d'una figura
  3. plt.plot: l'evolució mensual (i Sant Jordi)
  4. bar i barh: comparar títols
  5. hist: la distribució de quanties
  6. scatter: preu contra unitats
  7. pie i quan no usar-lo
  8. subplots: el panell 2×2 de la botiga
  9. Estil i llegibilitat
  10. La drecera df.plot i savefig
  11. 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.
pip install matplotlib

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 finestra

plt.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) o fig.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:

fig.savefig("informes/panell_papyrus.png", dpi=150, bbox_inches="tight")

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 .py sense show() el programa acaba sense mostrar res. (I savefig va abans de show.)
  • Reutilitzar la figura sense voler. Dibuixar dos gràfics seguits sobre el mateix ax els superposa. Cada figura nova, el seu fig, 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 subplots amb 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

  1. Dibuixa l'evolució mensual de la facturació (suma de quantia per mes) com a línia amb marcadors, anotant Sant Jordi, partint del groupby corresponent de 11-03. Etiqueta els eixos amb unitats (€) i desa-la a informes/facturacio_mensual.png.
  2. 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.
  3. 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: bar accepta una llista de colors, i pots superposar un ax.plot d'un únic punt.

Solucions

  1. 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 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.
  2. 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()
    
    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.
  3. 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)
    
    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çó.

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

Mòdul 2: Estructures de Control

Mòdul 3: Funcions i Mòduls

Mòdul 4: Estructures de Dades

Mòdul 5: Programació Orientada a Objectes

Mòdul 6: Gestió de Fitxers

Mòdul 7: Gestió d'Errors i Excepcions

Mòdul 8: Temes Avançats

Mòdul 9: Proves i Depuració

Mòdul 10: Desenvolupament Web amb Python

Mòdul 11: Ciència de Dades amb Python

Mòdul 12: Projecte Final

© Copyright 2026. Tots els drets reservats