File size: 3,370 Bytes
71175ed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import os
import pandas as pd
from typing import Tuple, Optional, Dict, List
import datetime

def cargar_dataframe(file_path: str) -> Tuple[Optional[pd.DataFrame], Optional[str]]:
    """
    Carga un DataFrame desde m煤ltiples formatos con validaci贸n robusta
    """
    if not os.path.exists(file_path):
        return None, f"Error: El archivo {file_path} no existe"
    
    try:
        ext = os.path.splitext(file_path)[1].lower()
        
        if ext == '.csv':
            df = pd.read_csv(file_path, encoding='utf-8', na_values=['', 'NA', 'NULL', 'NaN'])
        elif ext in ('.xls', '.xlsx'):
            df = pd.read_excel(file_path, na_values=['', 'NA', 'NULL', 'NaN'])
        elif ext == '.parquet':
            df = pd.read_parquet(file_path)
        elif ext == '.json':
            df = pd.read_json(file_path)
        else:
            return None, f"Error: Formato {ext} no soportado"
        
        # Convertir strings a categor铆as para optimizaci贸n
        for col in df.select_dtypes(include=['object']):
            if len(df[col].unique()) / len(df[col]) < 0.5:
                df[col] = df[col].astype('category')
                
        return df, None
        
    except Exception as e:
        return None, f"Error al cargar {file_path}: {str(e)}"

def analizar_nulos_por_columna(df: pd.DataFrame) -> List[Dict]:
    """
    Analiza valores nulos por columna con m茅tricas detalladas
    
    Returns:
        Lista de diccionarios con:
        - nombre_columna
        - tipo_dato
        - total_nulos
        - porcentaje_nulos
        - no_nulos
        - porcentaje_no_nulos
        - ejemplo_valores (3 primeros no nulos)
    """
    analisis = []
    total_filas = len(df)
    
    for columna in df.columns:
        nulos = df[columna].isnull().sum()
        no_nulos = total_filas - nulos
        porcentaje_nulos = (nulos / total_filas) * 100 if total_filas > 0 else 0
        valores_unicos = df[columna].nunique()
        
        # Ejemplos de valores (excluyendo nulos)
        ejemplos = []
        if no_nulos > 0:
            ejemplos = df[columna].dropna().head(3).tolist()
            # Convertir a string si es necesario para serializaci贸n
            ejemplos = [str(x) for x in ejemplos]
        
        analisis.append({
            'nombre_columna': columna,
            'tipo_dato': str(df[columna].dtype),
            'total_nulos': nulos,
            'porcentaje_nulos': round(porcentaje_nulos, 2),
            'no_nulos': no_nulos,
            'porcentaje_no_nulos': round(100 - porcentaje_nulos, 2),
            'valores_unicos': valores_unicos,
            'ejemplo_valores': ejemplos
        })
    
    # Ordenar por porcentaje de nulos descendente
    return sorted(analisis, key=lambda x: x['porcentaje_nulos'], reverse=True)

def get_file_metadata(file_path: str) -> Dict:
    """
    Obtiene metadatos detallados del archivo
    """
    stats = os.stat(file_path)
    return {
        'nombre': os.path.basename(file_path),
        'ruta': os.path.abspath(file_path),
        'tama帽o_bytes': stats.st_size,
        'tama帽o_mb': round(stats.st_size / (1024 * 1024), 2),
        'fecha_modificacion': datetime.datetime.fromtimestamp(stats.st_mtime),
        'extension': os.path.splitext(file_path)[1].lower(),
        'encoding': 'utf-8'  # Se puede detectar con chardet si es necesario
    }