substrat.cat
Dades

Auditar un conjunt de dades amb Python en cinc minuts

ydata-profiling genera un informe HTML amb duplicats, valors buits, cardinalitat i correlacions. I les comprovacions que has de fer a mà després.

·8 min de lectura

Abans de migrar dades, integrar-les amb un altre sistema o construir-hi res a sobre, s’han de mirar. No «obrir el fitxer i fer scroll»: mirar de veritat, amb números.

Aquest article explica com fer aquesta auditoria en cinc minuts amb ydata-profiling, i després què has de comprovar tu perquè cap eina automàtica t’ho dirà.

Què és

ydata-profiling (abans pandas-profiling) agafa un DataFrame de pandas i escup un fitxer HTML autocontingut amb l’anàlisi complet: una fitxa per variable, recompte de duplicats, mapa de valors absents, correlacions i una llista d’avisos.

És una eina d’exploració, no de validació contínua. Serveix per entendre què tens al davant el primer dia.

Instal·lar-lo i executar-lo

python -m venv .venv && source .venv/bin/activate
pip install ydata-profiling

I l’ús mínim, que ja val la pena:

import pandas as pd
from ydata_profiling import ProfileReport

df = pd.read_csv('clients.csv', dtype=str, keep_default_na=False, na_values=[''])

perfil = ProfileReport(df, title='Auditoria de clients', explorative=True)
perfil.to_file('informe.html')

Obre informe.html al navegador i ja hi ets.

Per què dtype=str

Aquesta línia és la més important de les quatre. Per defecte, pandas endevina el tipus de cada columna, i endevina malament exactament on més mal fa:

  • Un codi de client 0042 es converteix en el número 42 i perds els zeros.
  • Una columna de NIFs que casualment només conté números en aquest fitxer es torna numèrica i deixa de casar amb la del sistema de destí.
  • Un telèfon +34 600 … en una fila i 600… en una altra fa que tota la columna es quedi com a text… o no, segons quantes files miri pandas.

Llegint-ho tot com a text veus el que hi ha realment al fitxer, que és el que has d’auditar. Els tipus ja els posaràs després, a consciència.

El keep_default_na=False amb na_values=[''] evita l’altre clàssic: que pandas converteixi la cadena "NA", "None" o "null" —que pot ser un valor legítim— en un buit.

Què has de mirar de l’informe, i en quin ordre

L’informe és llarg. Aquest és l’ordre que fa que sigui útil.

1. La secció «Alerts»

És on va tot el que l’eina considera sospitós, i és la millor manera d’atacar-lo. Les etiquetes que importen:

Avís Què vol dir Per què t’importa
Duplicates Hi ha files completament idèntiques Migrar-les duplica registres a destí
Constant La columna té sempre el mateix valor No aporta res; sovint és un camp que es va deixar de fer servir
Unique Tots els valors són diferents Candidata a clau primària
High cardinality Moltíssims valors diferents Text lliure disfressat de categoria
Missing Molts buits Cal decidir si és un error o un «no aplica»
Zeros Molts zeros Sovint són buits mal codificats
Imbalance Una categoria s’ho menja tot El 98 % «Actiu» sol voler dir que ningú manté el camp

2. Overview → Duplicate rows

Et diu quantes files són completament idèntiques. Si en surten, la pregunta abans d’esborrar-les no és «com les trec» sinó per què n’hi ha: si el fitxer prové d’una exportació, sovint és un JOIN mal fet a origen, i esborrar-les amaga un problema que tornarà al pròxim volcat.

3. La fitxa de cada variable

Per a cada columna tens Distinct, Distinct (%), Missing, i per a les de text la llista de valors més freqüents. Aquí és on descobreixes que el camp «Província» té 63 valors diferents per a 52 províncies, perquè hi ha Barcelona, BARCELONA i Barcelona amb espai.

Mira’t sempre els valors més freqüents i els menys freqüents de cada columna categòrica. La cua és on viuen els errors.

4. Missing values

Quatre gràfics: el recompte per columna, la matriu (quines files tenen buits, i on), el dendrograma i el mapa de calor de coocurrència. Aquest últim és el més infravalorat: et diu quins buits van junts. Si Email i Telèfon estan buits sempre a les mateixes files, no tens dos problemes de qualitat: tens un grup de registres que va entrar per una altra via.

5. Correlacions

Útil sobretot per detectar columnes redundants: dues que sempre van juntes solen ser la mateixa cosa escrita de dues maneres, i una de les dues es pot deixar de mantenir.

Quan el fitxer és gran

L’informe complet calcula correlacions i interaccions entre totes les parelles de columnes, i això escala malament. Amb centenars de milers de files o desenes de columnes:

perfil = ProfileReport(df, title='Auditoria', minimal=True)

minimal=True desactiva correlacions, interaccions i mostres de text detallades. Segueix donant-te recomptes, buits, duplicats i distribucions, que és el 80 % del valor.

Una alternativa és auditar una mostra representativa i deixar els recomptes exactes per a les comprovacions manuals de la secció següent:

perfil = ProfileReport(df.sample(50_000, random_state=0), minimal=True)

Comparar dos volcats

Aquesta funció és poc coneguda i molt útil quan reps el mateix fitxer cada mes:

anterior = ProfileReport(df_juliol, title='Juliol')
actual = ProfileReport(df_agost, title='Agost')
anterior.compare(actual).to_file('comparativa.html')

Et posa les dues auditories una al costat de l’altra. És la manera més ràpida de veure que aquest mes ha aparegut una categoria nova, o que una columna que sempre venia plena ara ve buida al 30 %.

El que has de comprovar tu

Cap eina automàtica sap quines són les regles del teu negoci. Aquestes quatre comprovacions són manuals i són les que decideixen si les dades es poden migrar.

La clau única, de veritat

L’informe et dirà si una columna és Unique en aquest fitxer. Això no vol dir que sigui una clau: vol dir que en aquestes files no s’ha repetit. Comprova-ho explícitament, i comprova també les claus compostes:

def es_clau(df, columnes):
    total = len(df)
    unics = len(df.drop_duplicates(subset=columnes))
    buits = df[columnes].isna().any(axis=1).sum()
    return {'files': total, 'combinacions': unics,
            'repetides': total - unics, 'amb_buits': int(buits)}

print(es_clau(df, ['nif']))
print(es_clau(df, ['client_id', 'any', 'mes']))

Una clau amb buits no és una clau, encara que no es repeteixi cap valor.

La densitat, columna a columna

Quin percentatge de cada camp ve informat:

densitat = df.notna().mean().sort_values()
print((densitat * 100).round(1).to_string())

Ordenat de menys a més, la part de dalt de la llista és la conversa que has de tenir amb qui et dona les dades: «aquest camp ve informat al 4 %; el fem servir o el deixem anar?»

La integritat referencial

Abans de qualsevol integració, els identificadors d’una taula han d’existir a l’altra:

orfes = set(comandes['client_id']) - set(clients['client_id'])
print(f'{len(orfes)} clients referenciats que no existeixen')
print(list(orfes)[:10])

Aquest número és gairebé sempre més gran que zero, i gairebé sempre sorprèn.

Els formats dins d’una mateixa columna

Una manera ràpida de veure quantes maneres diferents d’escriure el mateix conviuen a una columna: substituir cada dígit per 9 i cada lletra per A, i comptar patrons.

patrons = (df['telefon'].fillna('')
           .str.replace(r'\d', '9', regex=True)
           .str.replace(r'[A-Za-zÀ-ÿ]', 'A', regex=True)
           .value_counts())
print(patrons.head(15))

Si surten dotze patrons per a una columna de telèfons, ja saps quanta feina de normalització tens al davant — i, sobretot, la tens quantificada.

Després de l’exploració, la validació

ydata-profiling és per al primer dia. Quan les regles ja les tens clares i el fitxer arriba cada mes, el que vols és que el procés s’aturi sol si les incompleix. Això ho fa pandera, amb un esquema declarat al codi:

import pandera.pandas as pa

esquema = pa.DataFrameSchema({
    'nif': pa.Column(str, pa.Check.str_matches(r'^[0-9]{8}[A-Z]$'), unique=True),
    'email': pa.Column(str, pa.Check.str_contains('@'), nullable=True),
    'import': pa.Column(float, pa.Check.ge(0)),
})

esquema.validate(df, lazy=True)   # lazy=True: recull tots els errors, no només el primer

Amb lazy=True, l’excepció que llança porta a dins la taula de totes les files que fallen i per què. Aquest és el fitxer que envies a qui genera les dades.

En resum

  • ProfileReport(df).to_file('informe.html') per veure què tens.
  • Llegeix el fitxer com a text i mira els avisos abans que res.
  • Les claus, la densitat, els orfes i els formats, comprova’ls tu.
  • Quan les regles siguin estables, passa de l’exploració a pandera i deixa que el procés es queixi sol.

Cinc minuts d’això abans de començar estalvien la setmana de descobrir a mitja migració que la clau no era clau.

El següent pas

Tens un procés
que odies fer?

Explica-m'ho i et diré si es pot automatitzar — i si no es pot, també t'ho diré. La primera conversa no es cobra — però el cafè el poses tu.

hola@substrat.cat