Spaces:
Sleeping
Sleeping
| import os | |
| import tempfile | |
| import pandas as pd | |
| from ydata_profiling import ProfileReport | |
| from load_dataframe import cargar_dataframe, analizar_nulos_por_columna, get_file_metadata | |
| from graficos_r import generar_histograma_r, generar_barras_r, generar_boxplot_r | |
| class AnalizadorDatos: | |
| def __init__(self, file_path: str): | |
| self.file_path = file_path | |
| self.df = None | |
| self.metadata = None | |
| self.nulos_por_columna = None | |
| self.reporte_path = None | |
| self.graficos = {} | |
| def cargar_datos(self) -> bool: | |
| """Carga y valida los datos""" | |
| self.df, error = cargar_dataframe(self.file_path) | |
| if error: | |
| print(error) | |
| return False | |
| self.metadata = get_file_metadata(self.file_path) | |
| self.nulos_por_columna = analizar_nulos_por_columna(self.df) | |
| return True | |
| def generar_reporte_nulos(self) -> str: | |
| """Genera un reporte detallado de valores nulos por columna""" | |
| if not self.nulos_por_columna: | |
| return "No hay datos de nulos disponibles" | |
| reporte = "=== ANÁLISIS DE VALORES NULOS POR COLUMNA ===\n\n" | |
| reporte += f"Total filas en dataset: {len(self.df)}\n" | |
| reporte += f"Total columnas: {len(self.df.columns)}\n\n" | |
| for col in self.nulos_por_columna: | |
| reporte += f"Columna: {col['nombre_columna']} ({col['tipo_dato']})\n" | |
| reporte += f" - Valores nulos: {col['total_nulos']} ({col['porcentaje_nulos']}%)\n" | |
| reporte += f" - Valores no nulos: {col['no_nulos']} ({col['porcentaje_no_nulos']}%)\n" | |
| reporte += f" - Valores únicos: {col['valores_unicos']}\n" | |
| if col['ejemplo_valores']: | |
| reporte += f" - Ejemplos: {', '.join(col['ejemplo_valores'])}\n" | |
| else: | |
| reporte += " - Ejemplos: TODOS SON NULOS\n" | |
| # Recomendación basada en porcentaje de nulos | |
| if col['porcentaje_nulos'] > 30: | |
| reporte += " - RECOMENDACIÓN: Considerar eliminar o imputar esta columna\n" | |
| elif col['porcentaje_nulos'] > 0: | |
| reporte += " - RECOMENDACIÓN: Evaluar imputación de valores\n" | |
| else: | |
| reporte += " - RECOMENDACIÓN: Sin problemas de nulos\n" | |
| reporte += "\n" | |
| return reporte | |
| def generar_reporte(self, minimal: bool = True) -> str: | |
| """Genera reporte de profiling""" | |
| try: | |
| self.reporte_path = tempfile.NamedTemporaryFile( | |
| suffix='.html', | |
| delete=False | |
| ).name | |
| profile = ProfileReport( | |
| self.df, | |
| title=f"Reporte de Análisis - {self.metadata['nombre']}", | |
| minimal=minimal, | |
| explorative=True | |
| ) | |
| profile.to_file(self.reporte_path) | |
| return self.reporte_path | |
| except Exception as e: | |
| print(f"Error generando reporte: {e}") | |
| return None | |
| def generar_graficos(self, columnas_analisis: list) -> dict: | |
| """Genera gráficos para las columnas especificadas""" | |
| os.makedirs("graficos", exist_ok=True) | |
| for col in columnas_analisis: | |
| if col not in self.df.columns: | |
| continue | |
| output_path = f"graficos/{col}.png" | |
| if pd.api.types.is_numeric_dtype(self.df[col]): | |
| path = generar_histograma_r(self.df, col, output_path) | |
| if path: | |
| self.graficos[col] = {'tipo': 'histograma', 'path': path} | |
| # Gráfico adicional si hay una columna categórica adecuada | |
| categoricas = [c for c in self.df.columns | |
| if pd.api.types.is_categorical_dtype(self.df[c]) | |
| or pd.api.types.is_object_dtype(self.df[c])] | |
| if categoricas and len(self.df[categoricas[0]].unique()) <= 10: | |
| path = generar_boxplot_r(self.df, col, categoricas[0], f"graficos/{col}_by_{categoricas[0]}.png") | |
| if path: | |
| self.graficos[f"{col}_by_{categoricas[0]}"] = {'tipo': 'boxplot', 'path': path} | |
| else: | |
| path = generar_barras_r(self.df, col, output_path) | |
| if path: | |
| self.graficos[col] = {'tipo': 'barras', 'path': path} | |
| return self.graficos | |
| def resumen_analisis(self) -> dict: | |
| """Genera un resumen estructurado del análisis""" | |
| return { | |
| 'metadata': self.metadata, | |
| 'estadisticas': { | |
| 'filas': len(self.df), | |
| 'columnas': len(self.df.columns), | |
| 'columnas_con_nulos': sum(1 for col in self.nulos_por_columna if col['total_nulos'] > 0), | |
| 'columnas_completas': sum(1 for col in self.nulos_por_columna if col['total_nulos'] == 0) | |
| }, | |
| 'nulos_por_columna': self.nulos_por_columna, | |
| 'graficos_generados': list(self.graficos.keys()), | |
| 'rutas': { | |
| 'reporte': self.reporte_path, | |
| 'graficos': {k: v['path'] for k, v in self.graficos.items()} | |
| } | |
| } | |
| def procesar_valores_nulos(self, metodo: str) -> str: | |
| """Procesa los valores nulos del DataFrame según el método especificado""" | |
| if self.df is None: | |
| return "Error: No hay datos cargados" | |
| df_original = self.df.copy() | |
| columnas_numericas = self.df.select_dtypes(include=['number']).columns | |
| columnas_categoricas = self.df.select_dtypes(include=['object', 'category']).columns | |
| try: | |
| if metodo == "media": | |
| # Para columnas numéricas: llenar con la media | |
| for col in columnas_numericas: | |
| if self.df[col].isnull().any(): | |
| media = self.df[col].mean() | |
| self.df[col].fillna(media, inplace=True) | |
| # Para columnas categóricas: llenar con la moda | |
| for col in columnas_categoricas: | |
| if self.df[col].isnull().any(): | |
| moda = self.df[col].mode() | |
| if not moda.empty: | |
| self.df[col].fillna(moda[0], inplace=True) | |
| elif metodo == "mediana": | |
| # Para columnas numéricas: llenar con la mediana | |
| for col in columnas_numericas: | |
| if self.df[col].isnull().any(): | |
| mediana = self.df[col].median() | |
| self.df[col].fillna(mediana, inplace=True) | |
| # Para columnas categóricas: llenar con la moda | |
| for col in columnas_categoricas: | |
| if self.df[col].isnull().any(): | |
| moda = self.df[col].mode() | |
| if not moda.empty: | |
| self.df[col].fillna(moda[0], inplace=True) | |
| elif metodo == "moda": | |
| # Para todas las columnas: llenar con la moda | |
| for col in self.df.columns: | |
| if self.df[col].isnull().any(): | |
| moda = self.df[col].mode() | |
| if not moda.empty: | |
| self.df[col].fillna(moda[0], inplace=True) | |
| # Actualizar análisis de nulos | |
| self.nulos_por_columna = analizar_nulos_por_columna(self.df) | |
| nulos_antes = df_original.isnull().sum().sum() | |
| nulos_despues = self.df.isnull().sum().sum() | |
| return (f"Procesamiento completado con método: {metodo}\n" | |
| f"Valores nulos antes: {nulos_antes}\n" | |
| f"Valores nulos después: {nulos_despues}\n" | |
| f"Valores procesados: {nulos_antes - nulos_despues}") | |
| except Exception as e: | |
| # Restaurar DataFrame original en caso de error | |
| self.df = df_original | |
| return f"Error al procesar valores nulos: {str(e)}" | |
| def analisis_completo(file_path: str, columnas_analisis: list = None): | |
| """Función principal para ejecutar análisis completo""" | |
| analizador = AnalizadorDatos(file_path) | |
| if not analizador.cargar_datos(): | |
| return None | |
| # Si no se especifican columnas, usar las que tienen menos del 50% de nulos | |
| if not columnas_analisis: | |
| columnas_validas = [ | |
| col['nombre_columna'] for col in analizador.nulos_por_columna | |
| if col['porcentaje_nulos'] < 50 | |
| ] | |
| columnas_analisis = columnas_validas # Limitar a 5 columnas por defecto | |
| # Generar outputs | |
| analizador.generar_reporte() | |
| analizador.generar_graficos(columnas_analisis) | |
| # Imprimir reporte de nulos en consola | |
| print(analizador.generar_reporte_nulos()) | |
| return analizador.resumen_analisis() | |
| if __name__ == "__main__": | |
| # Ejemplo de uso con análisis mejorado de nulos | |
| resumen = analisis_completo( | |
| file_path="Grammy Award Nominees and Winners 1958-2024.csv" | |
| ) | |
| if resumen: | |
| print("\n=== RESUMEN GENERAL ===") | |
| print(f"Columnas con nulos: {resumen['estadisticas']['columnas_con_nulos']}") | |
| print(f"Columnas completas: {resumen['estadisticas']['columnas_completas']}") | |
| print("\nTop 5 columnas con más nulos:") | |
| for col in resumen['nulos_por_columna'][:5]: | |
| print(f"- {col['nombre_columna']}: {col['porcentaje_nulos']}% nulos") | |