Salta el contingut

Les 5 dimensions de la qualitat de dades

Per poder millorar alguna cosa cal poder-la mesurar. Un framework professional de qualitat de dades no parla de "dades bones o dolentes" en abstracte, sinó que descompon la qualitat en dimensions concretes i mesurables. Les cinc dimensions següents formen el nucli del framework més utilitzat a la indústria; cadascuna es pot expressar com una mètrica (normalment un percentatge) i, per tant, es pot monitoritzar en el temps.

flowchart TB
    Q["Qualitat de dades"] --> C["Completesa"]
    Q --> U["Unicitat"]
    Q --> V["Validesa"]
    Q --> CO["Consistència"]
    Q --> F["Frescor"]

    style Q fill:#1e3a5f,color:#e2e8f0,stroke:#3b82f6
    style C fill:#1d4ed8,color:#ffffff,stroke:#3b82f6
    style U fill:#7c3aed,color:#ffffff,stroke:#a78bfa
    style V fill:#166534,color:#ffffff,stroke:#22c55e
    style CO fill:#9d174d,color:#ffffff,stroke:#ec4899
    style F fill:#b45309,color:#ffffff,stroke:#f59e0b

1. Completesa

Mesura si tots els valors obligatoris estan presents. Un camp email buit en una taula de clients impedeix enviar comunicacions; un camp preu nul en una taula de transaccions fa impossible calcular ingressos correctament (i, si no es tracta explícitament, pot desaparèixer silenciosament d'una suma o d'una mitjana).

import pandas as pd

df = pd.read_csv("clients.csv")

# Percentatge de completesa per columna
completesa = (1 - df.isnull().mean()) * 100
print(completesa.sort_values().round(2))

Completesa no sempre vol dir 100%

Un camp opcional (per exemple, telefon_secundari) pot tenir un NULL legítim. Fixa el llindar de completesa esperat per camp, no globalment: email hauria d'estar al 100%, telefon_secundari potser és acceptable al 60%.


2. Unicitat

Comprova que no hi ha duplicats on no n'hi hauria d'haver. Si la taula de clients té la mateixa persona registrada dues vegades (per un error d'importació, una doble inserció d'un pipeline no idempotent, o una fusió de sistemes), totes les agregacions per client quedaran inflades: el nombre de clients, la despesa mitjana, els KPI de retenció...

# Detectar duplicats per clau de negoci (no per clau tècnica autoincremental)
duplicats = df[df.duplicated(subset=["email"], keep=False)].sort_values("email")
print(f"Registres duplicats per email: {len(duplicats)}")

# Percentatge d'unicitat
unicitat = 1 - (df.duplicated(subset=["id_client"]).sum() / len(df))
print(f"Unicitat per id_client: {unicitat:.2%}")

Clau tècnica vs clau de negoci

Comprovar unicitat per id_client (una clau autoincremental) gairebé mai detecta res: la BD ja garanteix que és única. El duplicat real cal buscar-lo per la clau de negoci (DNI, email, combinació nom+data de naixement...), que és on es detecten els registres que representen la mateixa entitat real amb dos identificadors tècnics diferents.


3. Validesa

Comprova que el format i el domini dels valors són correctes. Un camp email sense @, un IBAN amb longitud incorrecta, un codi_postal de 6 dígits (quan n'hauria de tenir 5) o una data en format DD/MM/YYYY quan s'espera YYYY-MM-DD són errors de validesa: el valor pot fins i tot no ser nul ni estar duplicat, però no té una forma que el sistema pugui interpretar correctament.

import re

invalids_email = df[~df["email"].str.match(r"^[^@]+@[^@]+\.[^@]+$", na=False)]
invalids_cp     = df[~df["codi_postal"].astype(str).str.match(r"^\d{5}$")]
invalids_edat   = df[~df["edat"].between(0, 120)]

print(f"Emails invàlids: {len(invalids_email)}")
print(f"Codis postals invàlids: {len(invalids_cp)}")
print(f"Edats fora de rang: {len(invalids_edat)}")

4. Consistència

La mateixa entitat apareix amb valors contradictoris entre dos sistemes o dues taules. El client amb id=1042pais="ES" al CRM però pais="España" al sistema de facturació: cap dels dos valors és "invàlid" per si sol (ambdós són strings ben formats), però són inconsistents entre ells, cosa que trenca qualsevol JOIN o agregació que combini les dues fonts.

crm = pd.read_csv("crm_clients.csv")
facturacio = pd.read_csv("facturacio_clients.csv")

comparativa = crm.merge(facturacio, on="id_client", suffixes=("_crm", "_fact"))
inconsistents = comparativa[comparativa["pais_crm"] != comparativa["pais_fact"]]
print(f"Clients amb país inconsistent entre CRM i facturació: {len(inconsistents)}")

La consistència sol necessitar un diccionari de referència

Un cop detectada la inconsistència ("ES" vs "España"), la solució habitual no és triar una font com a "correcta" a ull, sinó definir una taula de referència (ES → España, FR → Francia...) i normalitzar totes les fonts contra ella durant la fase de transformació del pipeline.


5. Frescor (freshness)

Mesura si les dades són prou recents per ser útils. Un dashboard de vendes "en temps real" que en realitat mostra dades de fa 3 dies no és inexacte (els valors poden ser correctes), però ha perdut la propietat que el feia útil: la freqüència d'actualització.

from datetime import datetime, timedelta

ultima_actualitzacio = pd.to_datetime(df["timestamp_carrega"]).max()
antiguitat = datetime.now() - ultima_actualitzacio

sla_frescor = timedelta(hours=6)
if antiguitat > sla_frescor:
    print(f"⚠ Dades desactualitzades: última càrrega fa {antiguitat}")
else:
    print(f"Dades fresques: última càrrega fa {antiguitat}")

Un timestamp amb data de l'època Unix (1970-01-01, epoch zero) sol indicar un error de conversió, no una dada real; i una data_lliurament anterior a la data_comanda és físicament impossible i sol combinar un problema de frescor amb un problema de validesa:

df["data_comanda"]     = pd.to_datetime(df["data_comanda"])
df["data_lliurament"]  = pd.to_datetime(df["data_lliurament"])

errors_temporals = df[df["data_lliurament"] < df["data_comanda"]]
print(f"Lliuraments anteriors a la comanda (impossible): {len(errors_temporals)}")

I l'exactitud (accuracy)?

Molts frameworks (inclòs el DAMA-DMBOK) afegeixen una sisena dimensió: l'exactitud, que mesura si el valor és correcte respecte a la realitat (no només si té un format vàlid). Una edat de 45 anys formatada correctament (validesa ✓) pot ser igualment inexacta si la persona en té en realitat 52. La diferència amb la validesa és subtil però important:

Validesa Exactitud
Què comprova El format/domini és correcte El valor és correcte respecte al món real
Exemple d'error edat = -5 (format invàlid: no pot ser negativa) edat = 45 quan en realitat són 52 (format vàlid, valor fals)
Com es detecta Regles i expressions regulars sobre el propi dataset Contrastant amb una font externa de veritat (document oficial, altre sistema verificat)

L'exactitud és, per aquest motiu, la dimensió més difícil d'automatitzar: sovint només es pot mesurar per mostreig, auditant manualment una part de les dades contra una font externa fiable.


Mesurar totes les dimensions alhora: Data Profiling

Abans de definir regles concretes de qualitat, sol interessar tenir una fotografia general del dataset. El data profiling genera estadístiques descriptives automàtiques que donen una primera idea de les cinc dimensions alhora:

def perfil_basic(df: pd.DataFrame) -> pd.DataFrame:
    """Perfil ràpid sense dependències externes: completesa i unicitat per columna."""
    return pd.DataFrame({
        "tipus":     df.dtypes,
        "no_nuls":   df.count(),
        "nuls":      df.isnull().sum(),
        "pct_nuls":  (df.isnull().mean() * 100).round(2),
        "unics":     df.nunique(),
        "mostra":    [df[c].dropna().iloc[0] if df[c].count() > 0 else None
                      for c in df.columns],
    })

print(perfil_basic(df).to_string())

Per a un informe HTML complet i navegable, la llibreria ydata-profiling (l'antiga pandas-profiling) genera automàticament histogrames, matrius de correlació i alertes de qualitat per a cada columna:

from ydata_profiling import ProfileReport

perfil = ProfileReport(df, title="Perfil clients — juny 2026", minimal=True)
perfil.to_file("perfil_clients.html")

El profiling no substitueix les regles, les inspira

El profiling és un punt de partida per descobrir quines regles de validesa, completesa o consistència té sentit definir; no és, per si sol, un sistema de validació contínua. Un cop identificades les regles importants, cal automatitzar-les amb els mecanismes de les properes pàgines: assertions en Python, Great Expectations o tests de pytest.


Mòdul M5074 Sistemes de Big Data | Institut Sa Palomera (Blanes) | Curs CEIABD 2026-2027