Calidad de Datos: Explicación Exhaustiva con Ejemplos Prácticos

Principio Fundamental: “Garbage In, Garbage Out” (GIGO)

El principio “Garbage In, Garbage Out” es un concepto fundamental en ciencia de datos que establece que la calidad de los resultados de cualquier análisis, modelo o sistema depende directamente de la calidad de los datos de entrada. Este principio, que tiene sus raíces en los primeros días de la computación, es más relevante que nunca en la era actual de inteligencia artificial y análisis de datos masivos.

Implicaciones del Principio GIGO

  • Modelos de Machine Learning: Incluso los algoritmos más sofisticados producirán resultados incorrectos si se entrenan con datos de mala calidad
  • Decisiones Empresariales: Las estrategias basadas en datos deficientes pueden llevar a pérdidas económicas significativas
  • Cumplimiento Regulatorio: Los datos incorrectos pueden resultar en violaciones de normativas y sanciones

Dimensiones de Calidad de Datos

1. Completitud (Completeness)

La completitud mide si todos los datos necesarios están presentes y disponibles para el análisis. Se refiere a la ausencia de valores faltantes o nulos en el conjunto de datos.

Fórmula de Completitud: Completitud=ValoresnonulosTotaldevalores×100%Completitud = \frac{Valores no nulos}{Total de valores} \times 100\%

Características:

  • Evalúa la presencia de todos los atributos requeridos
  • Distingue entre datos críticos y opcionales
  • Impacta directamente en la validez del análisis
# Ejemplo de análisis de completitud
import pandas as pd
import numpy as np

# Dataset con valores faltantes
data = {
    'nombre': ['Juan', 'María', None, 'Carlos', 'Ana'],
    'edad': [25, 30, 35, None, 32],
    'salario': [3000, 4500, 5500, 3800, None]
}

df = pd.DataFrame(data)

# Calcular completitud por columna
completitud = (1 - df.isnull().sum() / len(df)) * 100
print("Completitud por columna:")
print(completitud)
# Output:
# nombre     80.0%
# edad       80.0%
# salario    80.0%

2. Consistencia (Consistency)

La consistencia se refiere a la uniformidad de los datos a través de diferentes sistemas, bases de datos o puntos temporales. Los datos consistentes mantienen el mismo formato, estructura y representación en todos los lugares donde aparecen.

Tipos de Consistencia:

  • Consistencia de valor: Los mismos datos tienen valores idénticos en diferentes sistemas
  • Consistencia de formato: Mantener formatos uniformes (fechas, números, texto)
  • Consistencia estructural: Preservar relaciones y estructuras de datos compatibles
# Ejemplo de problemas de consistencia
data_inconsistent = {
    'ciudad': ['Madrid', 'MADRID', 'madrid', 'Barcelona', 'BARCELONA'],
    'telefono': ['123-456-789', '123456789', '+34-123-456-789', '987654321', '987-654-321']
}

df_inconsistent = pd.DataFrame(data_inconsistent)

# Normalizar consistencia
df_inconsistent['ciudad'] = df_inconsistent['ciudad'].str.title()
print("Ciudades después de normalización:")
print(df_inconsistent['ciudad'].unique())
# Output: ['Madrid', 'Barcelona']

3. Precisión (Accuracy)

La precisión mide el grado en que los datos reflejan correctamente la realidad o una fuente verificable. Los datos precisos representan fielmente las entidades del mundo real que pretenden describir.

Formas de Verificar Precisión:

  • Comparación con fuentes autoritativas
  • Validación cruzada con sistemas externos
  • Pruebas de veracidad (ej.: verificar números de teléfono)
# Ejemplo de detección de outliers (problemas de precisión)
import numpy as np

# Datos con outlier obvio
salarios = [3000, 4500, 5500, 3800, 4200, 300000]  # 300000 es outlier

# Método IQR para detectar outliers
Q1 = np.percentile(salarios, 25)
Q3 = np.percentile(salarios, 75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

outliers = [x for x in salarios if x < lower_bound or x > upper_bound]
print(f"Outliers detectados: {outliers}")
# Output: 

4. Validez (Validity)

La validez indica si los datos cumplen con las reglas de negocio, restricciones y formatos predefinidos. Los datos válidos se ajustan a los estándares y definiciones establecidos para cada campo.

Ejemplos de Reglas de Validez:

  • Códigos postales con formato correcto
  • Fechas dentro de rangos válidos
  • Categorías pertenecientes a conjuntos predefinidos
# Ejemplo de validación de reglas de negocio
def validate_data(df):
    validation_results = {}
    
    # Validar códigos postales (5 dígitos)
    if 'codigo_postal' in df.columns:
        valid_cp = df['codigo_postal'].astype(str).str.match(r'^\d{5}$')
        validation_results['codigo_postal'] = valid_cp.mean() * 100
    
    # Validar categorías
    if 'categoria' in df.columns:
        valid_categories = ['A', 'B', 'C']
        valid_cat = df['categoria'].isin(valid_categories)
        validation_results['categoria'] = valid_cat.mean() * 100
    
    return validation_results

# Ejemplo de uso
data_validation = {
    'codigo_postal': ['28001', '08001', '123', 'ABCDE', '41001'],
    'categoria': ['A', 'B', 'X', 'C', 'A']
}

df_val = pd.DataFrame(data_validation)
results = validate_data(df_val)
print("Resultados de validación:")
print(f"Códigos postales válidos: {results['codigo_postal']:.1f}%")
print(f"Categorías válidas: {results['categoria']:.1f}%")

5. Unicidad (Uniqueness)

La unicidad asegura que no existan registros duplicados cuando no deberían existir. Esta dimensión es crucial para mantener la integridad referencial y evitar distorsiones en los análisis.

# Ejemplo de detección y eliminación de duplicados
data_duplicates = {
    'id': [1, 2, 3, 2, 4],
    'nombre': ['Juan', 'María', 'Carlos', 'María', 'Ana'],
    'email': ['juan@email.com', 'maria@email.com', 'carlos@email.com', 'maria@email.com', 'ana@email.com']
}

df_dup = pd.DataFrame(data_duplicates)

# Detectar duplicados
duplicados = df_dup.duplicated().sum()
print(f"Registros duplicados encontrados: {duplicados}")

# Eliminar duplicados manteniendo el primero
df_unique = df_dup.drop_duplicates()
print(f"Registros después de eliminar duplicados: {len(df_unique)}")

6. Oportunidad (Timeliness)

La oportunidad se refiere a si los datos están disponibles cuando se necesitan y si están actualizados. Los datos oportunos son aquellos que llegan en el momento correcto para la toma de decisiones.

# Ejemplo de análisis temporal
from datetime import datetime, timedelta

# Datos con timestamps
data_temporal = {
    'evento': ['Venta A', 'Venta B', 'Venta C'],
    'timestamp': ['2023-01-01 10:00:00', '2023-12-01 15:30:00', '2025-01-01 09:15:00']
}

df_temporal = pd.DataFrame(data_temporal)
df_temporal['timestamp'] = pd.to_datetime(df_temporal['timestamp'])

# Calcular antigüedad de los datos
now = datetime.now()
df_temporal['dias_antiguedad'] = (now - df_temporal['timestamp']).dt.days

print("Análisis de oportunidad:")
print(df_temporal[['evento', 'dias_antiguedad']])

# Identificar datos desactualizados (más de 365 días)
datos_desactualizados = df_temporal[df_temporal['dias_antiguedad'] > 365]
print(f"\nEventos con datos desactualizados: {len(datos_desactualizados)}")

Procesos de Limpieza de Datos

1. Tratamiento de Valores Ausentes

Los valores ausentes son uno de los problemas más comunes en la calidad de datos. Existen diferentes tipos de valores faltantes:

Tipos de Valores Faltantes:

  • MCAR (Missing Completely at Random): Los valores faltantes son completamente aleatorios
  • MAR (Missing at Random): Los valores faltantes están relacionados con otras variables observadas
  • MNAR (Missing Not at Random): Los valores faltantes están relacionados con el valor faltante mismo

Técnicas de Imputación

a) Imputación por Media, Mediana y Moda:

# Imputación por diferentes estadísticos
from sklearn.impute import SimpleImputer

# Para variables numéricas
def impute_numerical(df, column, strategy='median'):
    """
    Imputa valores faltantes en columnas numéricas
    strategy: 'mean', 'median', 'most_frequent'
    """
    if strategy == 'mean':
        # Usar media para distribuciones normales
        df[column].fillna(df[column].mean(), inplace=True)
    elif strategy == 'median':
        # Usar mediana para distribuciones sesgadas
        df[column].fillna(df[column].median(), inplace=True)
    
    return df

# Para variables categóricas
def impute_categorical(df, column):
    """Imputa valores faltantes con la moda"""
    mode_value = df[column].mode().iloc if not df[column].mode().empty else 'Desconocido'
    df[column].fillna(mode_value, inplace=True)
    return df

# Ejemplo de aplicación
data_missing = {
    'edad': [25, 30, np.nan, 28, np.nan],
    'salario': [3000, 4500, 5500, np.nan, 4200],
    'ciudad': ['Madrid', np.nan, 'Barcelona', 'Valencia', 'Madrid']
}

df_missing = pd.DataFrame(data_missing)

# Aplicar imputaciones
df_missing = impute_numerical(df_missing, 'edad', 'median')
df_missing = impute_numerical(df_missing, 'salario', 'mean')
df_missing = impute_categorical(df_missing, 'ciudad')

print("Datos después de imputación:")
print(df_missing)

b) Imputación KNN (K-Nearest Neighbors):

from sklearn.impute import KNNImputer

# KNN Imputation considera las relaciones entre variables
knn_imputer = KNNImputer(n_neighbors=3)

# Datos de ejemplo
data_knn = {
    'edad': [25, 30, np.nan, 28, np.nan, 45],
    'salario': [3000, 4500, 5500, np.nan, 4200, 6000],
    'experiencia': [2, 5, 8, 3, np.nan, 15]
}

df_knn = pd.DataFrame(data_knn)
print("Antes de KNN imputation:")
print(df_knn)

# Aplicar KNN imputation
df_knn_imputed = pd.DataFrame(
    knn_imputer.fit_transform(df_knn),
    columns=df_knn.columns
)

print("\nDespués de KNN imputation:")
print(df_knn_imputed)

2. Tratamiento de Valores Extremos (Outliers)

Los outliers pueden afectar significativamente el rendimiento de los modelos y la validez de los análisis estadísticos.

Métodos de Detección de Outliers

a) Método IQR (Interquartile Range):

def detect_outliers_iqr(df, column):
    """Detecta outliers usando el método IQR"""
    Q1 = df[column].quantile(0.25)
    Q3 = df[column].quantile(0.75)
    IQR = Q3 - Q1
    
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    
    outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]
    return outliers, lower_bound, upper_bound

# Ejemplo
data_outliers = {
    'salario': [3000, 4500, 5500, 3800, 4200, 6000, 4800, 50000, 3500, 5200]
}

df_outliers = pd.DataFrame(data_outliers)
outliers, lower, upper = detect_outliers_iqr(df_outliers, 'salario')

print(f"Límites: [{lower:.0f}, {upper:.0f}]")
print(f"Outliers encontrados: {outliers['salario'].tolist()}")

b) Método Z-Score:

def detect_outliers_zscore(df, column, threshold=3):
    """Detecta outliers usando Z-score"""
    z_scores = np.abs((df[column] - df[column].mean()) / df[column].std())
    outliers = df[z_scores > threshold]
    return outliers

outliers_zscore = detect_outliers_zscore(df_outliers, 'salario')
print(f"Outliers por Z-score: {outliers_zscore['salario'].tolist()}")

Técnicas de Tratamiento de Outliers

a) Eliminación:

# Eliminar outliers
df_no_outliers = df_outliers[~df_outliers.index.isin(outliers.index)]
print(f"Registros después de eliminar outliers: {len(df_no_outliers)}")

b) Transformación Logarítmica:

# Transformación logarítmica para reducir impacto de outliers
df_outliers['salario_log'] = np.log1p(df_outliers['salario'])
print("Datos originales vs transformados:")
print(df_outliers[['salario', 'salario_log']].head())

c) Capping (Limitación):

# Limitar valores extremos al percentil 95
percentil_95 = df_outliers['salario'].quantile(0.95)
df_outliers['salario_capped'] = df_outliers['salario'].clip(upper=percentil_95)
print(f"Valor máximo después de capping: {df_outliers['salario_capped'].max()}")

3. Normalización de Variables Categóricas

Label Encoding

from sklearn.preprocessing import LabelEncoder

# Para variables categóricas ordinales
le = LabelEncoder()
education_levels = ['Básico', 'Medio', 'Superior', 'Básico', 'Superior']
education_encoded = le.fit_transform(education_levels)

print("Mapeo de educación:")
for i, level in enumerate(le.classes_):
    print(f"{level} -> {i}")

print(f"Valores codificados: {education_encoded}")

One-Hot Encoding

# Para variables categóricas nominales
colors = ['Rojo', 'Verde', 'Azul', 'Rojo', 'Verde']
colors_df = pd.DataFrame({'color': colors})

# Crear variables dummy
colors_encoded = pd.get_dummies(colors_df, prefix='color')
print("One-Hot Encoding:")
print(colors_encoded)

4. Escalamiento de Datos

El escalamiento de datos es crucial para que las variables con diferentes unidades y rangos tengan el mismo peso en los algoritmos de machine learning.

Min-Max Scaling (Normalización)

Escala los datos a un rango específico, típicamente :

Xnorm=XXminXmaxXminX_{norm} = \frac{X - X_{min}}{X_{max} - X_{min}}

from sklearn.preprocessing import MinMaxScaler

# Min-Max Scaling
scaler_minmax = MinMaxScaler()
data_numeric = [[25, 3000], [30, 4500], [35, 5500], [28, 3800]]
data_minmax = scaler_minmax.fit_transform(data_numeric)

print("Datos después de Min-Max Scaling:")
print(data_minmax)

Z-Score Standardization

Escala los datos para tener media 0 y desviación estándar 1:

Xstd=XμσX_{std} = \frac{X - \mu}{\sigma}

from sklearn.preprocessing import StandardScaler

# Z-Score Standardization
scaler_zscore = StandardScaler()
data_zscore = scaler_zscore.fit_transform(data_numeric)

print("Datos después de Z-Score Standardization:")
print(data_zscore)

Comparación de Métodos de Escalamiento

AspectoMin-Max ScalingZ-Score Standardization
Fórmula(x - min) / (max - min)(x - mean) / std
Rangoo [-1, 1]Media = 0, std = 1
Sensible a outliersNo
Caso de usoDatos acotados, redes neuronalesDatos con outliers, clustering
Distribución resultanteMantiene distribución originalDistribución normal

Implementación Práctica Completa

Métricas de Calidad de Datos

def calculate_quality_metrics(df):
    """Calcula métricas comprehensivas de calidad de datos"""
    metrics = {}
    
    # Completitud
    completeness = (1 - df.isnull().sum() / len(df)) * 100
    metrics['completeness'] = completeness.mean()
    
    # Unicidad
    duplicates = df.duplicated().sum()
    metrics['uniqueness'] = (1 - duplicates / len(df)) * 100
    
    # Consistencia (ejemplo para variables categóricas)
    categorical_cols = df.select_dtypes(include=['object']).columns
    consistency_scores = []
    
    for col in categorical_cols:
        if not df[col].empty:
            # Medir variabilidad en formato
            unique_formats = len(df[col].dropna().astype(str).str.len().unique())
            consistency_score = 100 / unique_formats if unique_formats > 0 else 100
            consistency_scores.append(consistency_score)
    
    metrics['consistency'] = np.mean(consistency_scores) if consistency_scores else 100
    
    return metrics

# Ejemplo de uso
sample_data = {
    'id': [1, 2, 3, 4, 5],
    'name': ['Juan', 'María', None, 'Carlos', 'Ana'],
    'age': [25, 30, 35, 28, None],
    'city': ['Madrid', 'madrid', 'MADRID', 'Barcelona', 'barcelona']
}

df_sample = pd.DataFrame(sample_data)
quality_metrics = calculate_quality_metrics(df_sample)

print("Métricas de Calidad:")
print(f"Completitud: {quality_metrics['completeness']:.2f}%")
print(f"Unicidad: {quality_metrics['uniqueness']:.2f}%")
print(f"Consistencia: {quality_metrics['consistency']:.2f}%")

Mejores Prácticas y Recomendaciones

1. Establecer un Pipeline de Calidad de Datos

class DataQualityPipeline:
    def __init__(self):
        self.steps = []
    
    def add_step(self, step_name, step_function):
        self.steps.append((step_name, step_function))
    
    def process(self, df):
        processed_df = df.copy()
        quality_report = {}
        
        for step_name, step_function in self.steps:
            print(f"Ejecutando: {step_name}")
            processed_df = step_function(processed_df)
            
            # Calcular métricas después de cada paso
            metrics = calculate_quality_metrics(processed_df)
            quality_report[step_name] = metrics
        
        return processed_df, quality_report

# Ejemplo de uso del pipeline
def remove_duplicates(df):
    return df.drop_duplicates()

def fill_missing_values(df):
    for col in df.select_dtypes(include=[np.number]).columns:
        df[col].fillna(df[col].median(), inplace=True)
    for col in df.select_dtypes(include=['object']).columns:
        df[col].fillna(df[col].mode().iloc if not df[col].mode().empty else 'Unknown', inplace=True)
    return df

def standardize_categorical(df):
    for col in df.select_dtypes(include=['object']).columns:
        df[col] = df[col].astype(str).str.strip().str.title()
    return df

# Crear y ejecutar pipeline
pipeline = DataQualityPipeline()
pipeline.add_step("Eliminar Duplicados", remove_duplicates)
pipeline.add_step("Imputar Valores Faltantes", fill_missing_values)
pipeline.add_step("Estandarizar Categóricas", standardize_categorical)

clean_df, report = pipeline.process(df_sample)
print("\nReporte de Calidad:")
for step, metrics in report.items():
    print(f"{step}: Completitud {metrics['completeness']:.1f}%")

2. Validación Continua

La calidad de los datos debe monitorearse continuamente. Implementar sistemas de alerta para detectar degradación en la calidad:

def monitor_data_quality(df, baseline_metrics, tolerance=5):
    """Monitorea la calidad de datos comparando con métricas base"""
    current_metrics = calculate_quality_metrics(df)
    alerts = []
    
    for metric, current_value in current_metrics.items():
        baseline_value = baseline_metrics.get(metric, current_value)
        difference = abs(current_value - baseline_value)
        
        if difference > tolerance:
            alerts.append(f"ALERTA: {metric} ha cambiado {difference:.2f}% respecto a la línea base")
    
    return alerts, current_metrics

# Ejemplo de monitoreo
baseline = {'completeness': 95.0, 'uniqueness': 100.0, 'consistency': 85.0}
alerts, current = monitor_data_quality(clean_df, baseline)

if alerts:
    print("Alertas de Calidad:")
    for alert in alerts:
        print(f"- {alert}")
else:
    print("✓ Calidad de datos dentro de los parámetros esperados")

Conclusión

La calidad de los datos es fundamental para el éxito de cualquier iniciativa de análisis de datos, machine learning o inteligencia artificial. Las seis dimensiones principales (completitud, consistencia, precisión, validez, unicidad y oportunidad) proporcionan un marco comprensivo para evaluar y mejorar la calidad de los datos.

Puntos Clave para Recordar:

  1. El principio GIGO es más relevante que nunca en la era de la IA
  2. La prevención es mejor que la corrección: implementar controles de calidad desde el origen
  3. La calidad es un proceso continuo, no un evento único
  4. Cada dimensión de calidad requiere técnicas específicas de medición y mejora
  5. La automatización de los procesos de limpieza mejora la eficiencia y consistencia

La implementación de un programa robusto de calidad de datos no solo mejora la precisión de los análisis, sino que también reduce costos, mejora la toma de decisiones y aumenta la confianza en los datos organizacionales. En un mundo cada vez más impulsado por datos, la calidad no es opcional: es un requisito fundamental para el éxito empresarial.

Calidad de Datos - Principios Fundamentales y Técnicas de Limpieza

Author

Juan Fuentes

Publish Date

04 - 19 - 2023