La Minería de Datos se define como el proceso mediante el cual se descubren patrones, tendencias y relaciones ocultas en grandes volúmenes de datos, utilizando técnicas estadísticas, matemáticas y de aprendizaje automático.
Objetivo: Extraer conocimiento útil y accionable desde datos para la toma de decisiones.
Tareas principales de minería de datos:
graph TD
A[Minería de Datos] --> B[Supervisado]
A --> C[No Supervisado]
A --> D[Patrones]
B --> B1[Clasificación]
B --> B2[Regresión]
C --> C1[Clustering]
C --> C2[Asociación]
D --> D1[Detección de Anomalías]
D --> D2[Patrones Secuenciales]
fromsklearn.ensembleimportRandomForestRegressorfromsklearn.metricsimportmean_absolute_error,mean_squared_error,r2_scoreimportnumpyasnp# Dataset: Precios de viviendasdf=pd.read_csv('precios_casas.csv')X=df[['superficie_m2','num_habitaciones','num_baños','antiguedad','distancia_centro_km']]y=df['precio_euros']X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.2)# Modelomodelo=RandomForestRegressor(n_estimators=100,max_depth=10)modelo.fit(X_train,y_train)# Prediccionesy_pred=modelo.predict(X_test)# Métricasmae=mean_absolute_error(y_test,y_pred)rmse=np.sqrt(mean_squared_error(y_test,y_pred))r2=r2_score(y_test,y_pred)print(f"MAE (Mean Absolute Error): €{mae:,.0f}")print(f"RMSE (Root Mean Squared Error): €{rmse:,.0f}")print(f"R² Score: {r2:.3f}")# Ejemplo output:# MAE: €18,500# RMSE: €25,300# R² Score: 0.867## Interpretación:# - En promedio, el modelo se equivoca por €18,500# - El modelo explica el 86.7% de la varianza en precios
fromsklearn.clusterimportKMeansfromsklearn.preprocessingimportStandardScalerfromsklearn.decompositionimportPCAimportmatplotlib.pyplotasplt# Dataset: Comportamiento de clientesdf=pd.read_csv('clientes_comportamiento.csv')# Features: RFM (Recency, Frequency, Monetary)X=df[['dias_desde_ultima_compra','frecuencia_compras','gasto_total']]# Normalizar (importante para K-Means)scaler=StandardScaler()X_scaled=scaler.fit_transform(X)# Determinar K óptimo con método del codoinertias=[]K_range=range(2,11)forkinK_range:kmeans=KMeans(n_clusters=k,random_state=42)kmeans.fit(X_scaled)inertias.append(kmeans.inertia_)# Graficar codoplt.figure(figsize=(10,6))plt.plot(K_range,inertias,'bo-')plt.xlabel('Número de Clusters (K)')plt.ylabel('Inertia')plt.title('Método del Codo para K óptimo')plt.grid(True)plt.show()# Aplicar K-Means con K=4 (elegido desde codo)kmeans=KMeans(n_clusters=4,random_state=42)df['cluster']=kmeans.fit_predict(X_scaled)# Perfilar clustersprint("=== PERFIL DE CLUSTERS ===")perfil=df.groupby('cluster').agg({'dias_desde_ultima_compra':'mean','frecuencia_compras':'mean','gasto_total':'mean'}).round(2)perfil['tamaño']=df['cluster'].value_counts().sort_index()print(perfil)# Visualizar con PCA (reducir a 2D)pca=PCA(n_components=2)X_pca=pca.fit_transform(X_scaled)plt.figure(figsize=(12,8))scatter=plt.scatter(X_pca[:,0],X_pca[:,1],c=df['cluster'],cmap='viridis',s=50,alpha=0.6)plt.colorbar(scatter,label='Cluster')plt.xlabel('PC1')plt.ylabel('PC2')plt.title('Segmentación de Clientes (K-Means)')plt.grid(True,alpha=0.3)plt.show()# Interpretar clustersinterpretacion={0:"Champions: Compraron recientemente, alta frecuencia, alto gasto",1:"Leales: Compran frecuentemente, gasto medio",2:"En riesgo: No han comprado recientemente, bajo gasto",3:"Perdidos: Largo tiempo sin comprar, baja frecuencia"}forcluster_id,descripcionininterpretacion.items():tamaño=(df['cluster']==cluster_id).sum()print(f"\nCluster {cluster_id} ({tamaño} clientes): {descripcion}")
frommlxtend.frequent_patternsimportapriori,association_rulesimportpandasaspd# Dataset: Transacciones de supermercadotransacciones=[['leche','pan','huevos'],['leche','pan'],['leche','cerveza','pañales'],['pan','mantequilla'],['leche','pan','mantequilla'],['cerveza','pañales'],['leche','pan','huevos','mantequilla']]# Convertir a formato one-hot encodingfrommlxtend.preprocessingimportTransactionEncoderte=TransactionEncoder()te_ary=te.fit(transacciones).transform(transacciones)df_transacciones=pd.DataFrame(te_ary,columns=te.columns_)print("=== MATRIZ ONE-HOT ===")print(df_transacciones)# Encontrar itemsets frecuentesitemsets_frecuentes=apriori(df_transacciones,min_support=0.3,# 30% de transaccionesuse_colnames=True)print("\n=== ITEMSETS FRECUENTES ===")print(itemsets_frecuentes)# Generar reglas de asociaciónreglas=association_rules(itemsets_frecuentes,metric="confidence",min_threshold=0.6)# Ordenar por lift (medida de interés)reglas_sorted=reglas.sort_values('lift',ascending=False)print("\n=== REGLAS DE ASOCIACIÓN ===")print(reglas_sorted[['antecedents','consequents','support','confidence','lift']])# Interpretar métricas:# SUPPORT: % de transacciones que contienen el itemset# CONFIDENCE: P(consecuente | antecedente)# LIFT: Cuánto más probable es B dado A vs B solo# - Lift > 1: Regla positiva (A y B ocurren juntos)# - Lift = 1: A y B independientes# - Lift < 1: Regla negativa (A y B no ocurren juntos)# Ejemplo output:# antecedents consequents support confidence lift# {pan} {leche} 0.57 0.80 1.14# {huevos} {leche,pan} 0.29 1.00 1.75# {pañales} {cerveza} 0.29 1.00 3.50## Insight: Clientes que compran pañales tienen 3.5x# más probabilidad de comprar cerveza
CRISP-DM: Cross Industry Standard Process for Data Mining
CRISP-DM es la metodología más utilizada en proyectos de minería de datos y ciencia de datos.
Origen: Creada en 1999 como alianza entre SPSS, NCR, Daimler-Chrysler
Características:
- Independiente de herramientas y tecnologías
- Estructurada en 6 fases iterativas
- Aplicable a cualquier industria y tamaño de proyecto
Ciclo de vida CRISP-DM:
graph TD
A[1. Comprensión del Negocio] --> B[2. Comprensión de los Datos]
B --> C[3. Preparación de los Datos]
C --> D[4. Modelado]
D --> E[5. Evaluación]
E --> F[6. Despliegue]
F -.-> A
E -.-> A
D -.-> C
E -.-> D
style A fill:#e1f5ff
style B fill:#c8e6ff
style C fill:#afd7ff
style D fill:#96c8ff
style E fill:#7db9ff
style F fill:#64aaff
Fase 1: comprensión del negocio:
Objetivo: Entender el problema desde la perspectiva del negocio y traducirlo a objetivos de minería de datos.
Background:-Contexto de la organización-Situación actual y motivación del proyecto-Antecedentes históricos relevantesObjetivos del Negocio:-"Reducirtasadeabandonodeclientesen15%"-"Aumentarventascruzadasen20%"-"Detectarfraudescon95%deprecisión"Criterios de Éxito:-Métricas específicas (ROI, KPIs)-Plazos temporales-Restricciones presupuestarias
Tareas:
- Inventario de recursos disponibles
- Identificar requerimientos, supuestos y restricciones
- Analizar riesgos y contingencias
- Evaluar costos y beneficios
- Definir terminología del proyecto
Inventario de Recursos:Datos:-CRM con 5 años de historico (2M clientes)-Transacciones last 3 años (50M registros)-Encuestas satisfacción (250K respuestas)Personal:-1 Data Scientist Senior-2 Data Analysts-1 Domain Expert (Marketing)Tecnología:-Cluster Spark (10 nodos)-Licencias Python, R-Plataforma Cloud AWSPresupuesto:-€150,000 (6 meses)Requerimientos:-Datos históricos mínimo 2 años-Modelo debe ejecutar en <1 segundo (inferencia)-Accuracy mínimo 85%Restricciones:-Cumplimiento RGPD-No usar datos sensibles (salud, raza, religión)-Modelo interpretable (no "caja negra")Riesgos:-Calidad datos variable:ALTO-Rotación personal:MEDIO-Cambios regulatorios:BAJO
Tareas:
- Traducir objetivos de negocio a objetivos técnicos de minería
- Definir criterios de éxito de minería de datos
# Reporte de Recolección Inicial de Datosfuentes_datos={'CRM':{'ubicacion':'PostgreSQL crm_db.clientes','registros':2_150_000,'columnas':45,'periodo':'2019-01-01 a 2024-01-15','actualizacion':'Diaria','formato':'Tabla SQL','tamaño':'15 GB'},'Transacciones':{'ubicacion':'Data Lake S3 s3://empresa/transacciones/','registros':51_200_000,'columnas':12,'periodo':'2021-01-01 a 2024-01-15','actualizacion':'Tiempo real','formato':'Parquet particionado por fecha','tamaño':'120 GB'},'Soporte':{'ubicacion':'APIs Zendesk','registros':820_000,'columnas':8,'periodo':'2021-03-01 a 2024-01-15','actualizacion':'Tiempo real','formato':'JSON','tamaño':'2.5 GB'}}# Cargar datosimportpandasaspdimportpyarrow.parquetaspqdf_clientes=pd.read_sql("SELECT * FROM clientes",conn_crm)df_transacciones=pq.read_table('s3://empresa/transacciones/').to_pandas()df_soporte=pd.read_json('tickets_soporte.json')print("✓ Datos cargados exitosamente")
# Reporte de Exploración de Datosimportmatplotlib.pyplotaspltimportseabornassns# Correlación entre variables numéricasplt.figure(figsize=(12,10))corr=df_clientes.select_dtypes(include=[np.number]).corr()sns.heatmap(corr,annot=True,fmt='.2f',cmap='coolwarm',center=0)plt.title('Matriz de Correlación')plt.tight_layout()plt.savefig('correlacion_features.png')# Distribución target por segmentosfig,axes=plt.subplots(2,2,figsize=(15,12))# Churn por edaddf_clientes.groupby('rango_edad')['churn'].mean().plot(kind='bar',ax=axes[0,0],color='steelblue')axes[0,0].set_title('Tasa de Churn por Rango de Edad')axes[0,0].set_ylabel('% Churn')# Churn por antigüedaddf_clientes.groupby('antiguedad_meses')['churn'].mean().plot(ax=axes[0,1],color='coral')axes[0,1].set_title('Tasa de Churn por Antigüedad')# Churn por num productosdf_clientes.groupby('num_productos_contratados')['churn'].mean().plot(kind='bar',ax=axes[1,0],color='green')axes[1,0].set_title('Churn por Número de Productos')# Churn por saldodf_clientes.boxplot(column='saldo_cuenta',by='churn',ax=axes[1,1])axes[1,1].set_title('Saldo Cuenta: Churn vs No Churn')plt.tight_layout()plt.savefig('exploracion_churn_factores.png')# Insights inicialesinsights=["✓ Clientes con 1 solo producto tienen churn 40% mayor","✓ Churn aumenta significativamente after 24 meses","✓ Saldo promedio clientes churn: €1,200 vs €3,500 (no churn)","✓ Clientes edad 18-25 tienen churn 2x mayor que 45-60","⚠️ 15% valores faltantes en 'ingresos_anuales'"]print("\n=== INSIGHTS INICIALES ===")forinsightininsights:print(insight)
# Construcción de Featuresprint("=== FEATURE ENGINEERING ===\n")# 1. FEATURES TEMPORALESprint("1️⃣ Features temporales:")df['antiguedad_meses']=((pd.Timestamp.now()-df['fecha_registro']).dt.days/30).astype(int)df['anio_registro']=df['fecha_registro'].dt.yeardf['mes_registro']=df['fecha_registro'].dt.monthdf['trimestre_registro']=df['fecha_registro'].dt.quarterprint(f" ✓ 4 features temporales creados")# 2. FEATURES TRANSACCIONALES (de tabla transacciones)print("\n2️⃣ Features transaccionales:")# Agregar transacciones por clientetransacciones_agg=df_transacciones.groupby('cliente_id').agg({'monto':['sum','mean','std','count'],'fecha':'max'# última transacción}).reset_index()transacciones_agg.columns=['cliente_id','monto_total','monto_promedio','monto_std','num_transacciones','fecha_ultima_transaccion']# Join con dataset principaldf=df.merge(transacciones_agg,on='cliente_id',how='left')# Features derivadasdf['dias_desde_ultima_transaccion']=(pd.Timestamp.now()-df['fecha_ultima_transaccion']).dt.daysdf['transacciones_por_mes']=df['num_transacciones']/df['antiguedad_meses']df['monto_per_transaccion']=df['monto_total']/df['num_transacciones']print(f" ✓ 8 features transaccionales creados")# 3. FEATURES RFM (Recency, Frequency, Monetary)print("\n3️⃣ Features RFM:")# Recency: cuándo fue última interaccióndf['recency_score']=pd.qcut(df['dias_desde_ultima_transaccion'],q=5,labels=[5,4,3,2,1]# 5=muy reciente, 1=hace mucho).astype(int)# Frequency: cuántas veces interactúadf['frequency_score']=pd.qcut(df['num_transacciones'],q=5,labels=[1,2,3,4,5],duplicates='drop'# 5=muchas, 1=pocas).astype(int)# Monetary: cuánto gastadf['monetary_score']=pd.qcut(df['monto_total'],q=5,labels=[1,2,3,4,5],duplicates='drop'# 5=alto, 1=bajo).astype(int)df['rfm_score']=(df['recency_score']*100+df['frequency_score']*10+df['monetary_score'])print(f" ✓ 4 features RFM creados")# 4. FEATURES DE INTERACCIÓNprint("\n4️⃣ Features de interacción:")df['ratio_saldo_ingresos']=df['saldo_cuenta']/df['ingresos_anuales']df['productos_per_anio']=df['num_productos']/(df['antiguedad_meses']/12)df['es_cliente_activo']=(df['dias_desde_ultima_transaccion']<90).astype(int)df['es_cliente_premium']=((df['monto_total']>df['monto_total'].quantile(0.8))&(df['num_productos']>=3)).astype(int)print(f" ✓ 4 features de interacción creados")# 5. FEATURES CATEGÓRICOS ENCODEDprint("\n5️⃣ Encoding de categóricos:")# Target encoding para ciudad (usar mean churn por ciudad)ciudad_encoding=df.groupby('ciudad')['churn'].mean().to_dict()df['ciudad_churn_rate']=df['ciudad'].map(ciudad_encoding)# One-hot para generodf=pd.get_dummies(df,columns=['genero'],prefix='genero',drop_first=True)print(f" ✓ Variables categóricas encoded")print(f"\n✅ FEATURE ENGINEERING COMPLETO")print(f" Features totales: {len(df.columns)} columnas")print(f" Nuevas features: {len(df.columns)-len(df_clientes_filtered.columns)}")
Tareas:
- Combinar datos de múltiples tablas/fuentes
- Resolver conflictos de esquemas
# Integración de múltiples fuentes# Fuente 1: Clientes (ya preparado arriba)df_base=df.copy()# Fuente 2: Soporte (agregar)soporte_agg=df_soporte.groupby('cliente_id').agg({'ticket_id':'count','tiempo_resolucion_hrs':'mean','resuelto':'mean'}).reset_index()soporte_agg.columns=['cliente_id','num_tickets_soporte','avg_tiempo_resolucion_hrs','tasa_resolucion']# Mergedf_integrado=df_base.merge(soporte_agg,on='cliente_id',how='left')# Imputar 0 para clientes sin ticketsdf_integrado['num_tickets_soporte'].fillna(0,inplace=True)df_integrado['avg_tiempo_resolucion_hrs'].fillna(df_integrado['avg_tiempo_resolucion_hrs'].median(),inplace=True)df_integrado['tasa_resolucion'].fillna(1.0,inplace=True)# Asumimos no hay problema# Fuente 3: External (ejemplo: datos económicos por ciudad)# df_economico = pd.read_csv('datos_pib_ciudad.csv')# df_integrado = df_integrado.merge(df_economico, on='ciudad', how='left')print(f"✓ Datos integrados de {3} fuentes")print(f" Registros finales: {len(df_integrado):,}")print(f" Features finales: {len(df_integrado.columns)}")
Tareas:
- Reformatear datos para herramientas de modelado
- Aplicar transformaciones finales
# Reformateo final para modeladofromsklearn.preprocessingimportStandardScaler# 1. Separar features y targettarget_col='churn'feature_cols=[colforcolindf_integrado.columnsifcolnotin[target_col,'cliente_id','fecha_registro','fecha_ultima_transaccion']]X=df_integrado[feature_cols]y=df_integrado[target_col]# 2. Normalizar features numéricasnumeric_features=X.select_dtypes(include=[np.number]).columns.tolist()scaler=StandardScaler()X_scaled=X.copy()X_scaled[numeric_features]=scaler.fit_transform(X[numeric_features])# 3. Guardar datasets preparadosX_scaled.to_parquet('data/processed/X_train_ready.parquet')y.to_csv('data/processed/y_train_ready.csv',index=False)# Metadatametadata={'fecha_preparacion':pd.Timestamp.now().isoformat(),'registros':len(X_scaled),'features':len(feature_cols),'numeric_features':len(numeric_features),'target_distribution':y.value_counts(normalize=True).to_dict(),'scaler':'StandardScaler','features_list':feature_cols}importjsonwithopen('data/processed/metadata.json','w')asf:json.dump(metadata,f,indent=2)print("✅ DATOS PREPARADOS Y GUARDADOS")print(f" 📁 X_train_ready.parquet: {X_scaled.shape}")print(f" 📁 y_train_ready.csv: {y.shape}")print(f" 📁 metadata.json")
Fase 4: modelado:
Objetivo: Seleccionar y aplicar técnicas de modelado, calibrar parámetros óptimos.
# Entrenamiento y Tuning de Modelosfromsklearn.model_selectionimportGridSearchCV,cross_val_scorefromsklearn.metricsimportmake_scorer,recall_scoreimportmlflowimportmlflow.sklearn# Configurar MLflow para trackingmlflow.set_experiment('churn_prediction')resultados={}fornombre_modelo,infoinmodelos_candidatos.items():print(f"\n{'='*60}")print(f"ENTRENANDO: {nombre_modelo}")print(f"{'='*60}")withmlflow.start_run(run_name=nombre_modelo):modelo_base=info['modelo']# Entrenar modelo basemodelo_base.fit(X_train,y_train)# Evaluar en validationy_pred_val=modelo_base.predict(X_val)y_pred_proba_val=modelo_base.predict_proba(X_val)[:,1]fromsklearn.metricsimport(accuracy_score,precision_score,recall_score,f1_score,roc_auc_score)metricas={'accuracy':accuracy_score(y_val,y_pred_val),'precision':precision_score(y_val,y_pred_val),'recall':recall_score(y_val,y_pred_val),'f1':f1_score(y_val,y_pred_val),'roc_auc':roc_auc_score(y_val,y_pred_proba_val)}# Log en MLflowmlflow.log_params(modelo_base.get_params())mlflow.log_metrics(metricas)mlflow.sklearn.log_model(modelo_base,"model")print(f"\n📊 MÉTRICAS ({nombre_modelo}):")formetrica,valorinmetricas.items():print(f" {metrica:12s}: {valor:.4f}")resultados[nombre_modelo]={'modelo':modelo_base,'metricas':metricas}# Seleccionar mejor modelomejor_modelo_nombre=max(resultados,key=lambdak:resultados[k]['metricas']['roc_auc'])mejor_modelo=resultados[mejor_modelo_nombre]['modelo']print(f"\n🏆 MEJOR MODELO: {mejor_modelo_nombre}")print(f" ROC-AUC: {resultados[mejor_modelo_nombre]['metricas']['roc_auc']:.4f}")# Hyperparameter Tuning para mejor modeloifmejor_modelo_nombre=='XGBoost':print(f"\n🔧 Optimizando hiperparámetros...")param_grid={'n_estimators':[100,200,300],'max_depth':[3,5,7,10],'learning_rate':[0.01,0.05,0.1],'subsample':[0.8,1.0],'col samplebysample':[0.8,1.0]}grid_search=GridSearchCV(XGBClassifier(eval_metric='logloss',random_state=42),param_grid,cv=cv_strategy,scoring='roc_auc',n_jobs=-1,verbose=1)grid_search.fit(X_train,y_train)mejor_modelo_tuned=grid_search.best_estimator_print(f"✓ Mejores hiperparámetros:")print(f" {grid_search.best_params_}")print(f"✓ Mejor ROC-AUC (CV): {grid_search.best_score_:.4f}")
Tareas:
- Evaluar modelos según criterios técnicos
- Revisar configuración de parámetros
# Evaluación Detallada del Modelofromsklearn.metricsimportclassification_report,confusion_matrix,roc_curveimportmatplotlib.pyplotaspltimportseabornassns# Predicciones en test sety_pred_test=mejor_modelo_tuned.predict(X_test)y_pred_proba_test=mejor_modelo_tuned.predict_proba(X_test)[:,1]print("=== EVALUACIÓN EN TEST SET ===\n")# 1. Reporte clasificaciónprint("📋 REPORTE DE CLASIFICACIÓN:")print(classification_report(y_test,y_pred_test,target_names=['No Churn','Churn']))# 2. Matriz de confusióncm=confusion_matrix(y_test,y_pred_test)plt.figure(figsize=(8,6))sns.heatmap(cm,annot=True,fmt='d',cmap='Blues',xticklabels=['No Churn','Churn'],yticklabels=['No Churn','Churn'])plt.title('Matriz de Confusión - Test Set')plt.ylabel('Valor Real')plt.xlabel('Predicción')plt.savefig('reports/confusion_matrix_test.png')plt.show()# 3. Curva ROCfpr,tpr,thresholds=roc_curve(y_test,y_pred_proba_test)roc_auc_test=roc_auc_score(y_test,y_pred_proba_test)plt.figure(figsize=(10,8))plt.plot(fpr,tpr,color='darkorange',lw=2,label=f'ROC curve (AUC = {roc_auc_test:.3f})')plt.plot([0,1],[0,1],color='navy',lw=2,linestyle='--',label='Random')plt.xlim([0.0,1.0])plt.ylim([0.0,1.05])plt.xlabel('False Positive Rate')plt.ylabel('True Positive Rate')plt.title('Receiver Operating Characteristic (ROC) - Test Set')plt.legend(loc="lower right")plt.grid(True,alpha=0.3)plt.savefig('reports/roc_curve_test.png')plt.show()# 4. Feature Importanceifhasattr(mejor_modelo_tuned,'feature_importances_'):feature_importance=pd.DataFrame({'feature':feature_cols,'importance':mejor_modelo_tuned.feature_importances_}).sort_values('importance',ascending=False).head(20)plt.figure(figsize=(10,8))plt.barh(feature_importance['feature'],feature_importance['importance'])plt.xlabel('Importance')plt.title('Top 20 Feature Importances')plt.gca().invert_yaxis()plt.tight_layout()plt.savefig('reports/feature_importance.png')plt.show()print("\n📊 TOP 10 FEATURES MÁS IMPORTANTES:")print(feature_importance.head(10))# Guardar modelo finalimportjoblibjoblib.dump(mejor_modelo_tuned,'models/churn_model_final.pkl')joblib.dump(scaler,'models/scaler.pkl')print("\n✅ MODELO FINAL GUARDADO")print(f" 📁 models/churn_model_final.pkl")print(f" 📁 models/scaler.pkl")
Fase 5: evaluación:
Objetivo: Evaluar modelo desde perspectiva de negocio y decidir próximos pasos.
# Revisión del Procesoprint("=== REVISIÓN DEL PROCESO ===\n")lecciones_aprendidas=[{'fase':'Comprensión Datos','observacion':'Calidad datos mejor de lo esperado','accion_futura':'Replicar proceso limpieza para otros proyectos'},{'fase':'Preparación Datos','observacion':'Feature engineering RFM muy efectivo','accion_futura':'Documentar proceso RFM como best practice'},{'fase':'Modelado','observacion':'XGBoost superior a otros algoritmos','accion_futura':'Usar XGBoost como baseline en futuros proyectos'},{'fase':'General','observacion':'MLflow facilitó tracking experimentos','accion_futura':'Adoptar MLflow como estándar equipo'}]forleccioninlecciones_aprendidas:print(f"📌 {leccion['fase']}:")print(f" Observación: {leccion['observacion']}")print(f" Acción futura: {leccion['accion_futura']}\n")# Riesgos encontradosriesgos_encontrados=["Desbalanceo de clases (75%-25%) requirió técnicas especiales","Algunas features con alta correlación (multicolinealidad)","Modelo puede degradarse si distribución datos cambia (data drift)"]print("⚠️ RIESGOS IDENTIFICADOS:")forriesgoinriesgos_encontrados:print(f" - {riesgo}")
Tareas:
- Decidir acciones finales
- Planificar despliegue o iteración
# Monitoreo y Mantenimientoimportloggingfromprometheus_clientimportCounter,Histogram,Gauge# Métricas Prometheuspredicciones_total=Counter('predicciones_total','Total de predicciones')predicciones_churn=Counter('predicciones_churn','Predicciones positivas (churn=1)')latencia_prediccion=Histogram('latencia_prediccion_segundos','Latencia de predicción')probabilidad_promedio=Gauge('probabilidad_churn_promedio','Probabilidad promedio últimas 1000 predicciones')# Sistema de Monitoreomonitores={'Performance del Modelo':{'metricas':['accuracy','precision','recall','roc_auc'],'frecuencia':'Diaria','umbral_alerta':'Si accuracy cae >5% vs baseline','accion':'Email a equipo DS + revisar data drift'},'Data Drift':{'que_monitorear':['Distribución features (KS-test)','Distribución predicciones','Correlaciones entre features'],'frecuencia':'Diaria','umbral_alerta':'KS p-value < 0.05','accion':'Investigar causa, considerar reentrenamiento'},'Latencia API':{'metrica':'p95 latencia','frecuencia':'Tiempo real','umbral_alerta':'>2 segundos','accion':'Escalar recursos automáticamente'},'Volumen Predicciones':{'metrica':'Requests por minuto','frecuencia':'Tiempo real','umbral_alerta':'Caída >50% vs promedio 7 días','accion':'Revisar integraciones upstream'},'Errores':{'metrica':'Tasa de errores','frecuencia':'Tiempo real','umbral_alerta':'>1%','accion':'Pager a on-call engineer'}}print("=== PLAN DE MONITOREO ===\n")formonitor,configinmonitores.items():print(f"📊 {monitor}:")forkey,valueinconfig.items():print(f" {key}: {value}")print()# Reentrenamiento Automáticoreentrenamiento_config={'frecuencia':'Mensual (primer día del mes)','triggers_adicionales':['Data drift detectado (automático)','Performance cae >5% (automático)','Nueva feature disponible (manual)'],'proceso':['1. Recolectar datos último mes','2. Ejecutar pipeline CRISP-DM automatizado','3. Validar nuevo modelo vs actual en test set','4. Si mejora >2%, desplegar con canary (5%→100%)','5. Si no mejora, mantener modelo actual'],'aprobacion':'Automática si cumple criterios, sino manual'}print("🔄 REENTRENAMIENTO:")print(f" Frecuencia: {reentrenamiento_config['frecuencia']}")print(f" Triggers: {', '.join(reentrenamiento_config['triggers_adicionales'])}")
Tareas:
- Generar documentación completa
- Presentar resultados a stakeholders
# Documentación de Lecciones Aprendidaslecciones={'Éxitos':["Feature engineering RFM fue crítico (↑12% en AUC)","MLflow facilitó gestión de 47 experimentos","Colaboración domain expert evitó features irrelevantes","Despliegue canary previno incidentes"],'Desafíos':["Desbalanceo clases requirió técnicas sampling complejas","Integración CRM tomó 2x tiempo estimado (legacy system)","Definición 'churn' cambió mid-project (requerido rehacer Fase 2-3)"],'Mejoras Futuras':["Automatizar más pasos del pipeline (reduce tiempo 40%)","Implementar feature store centralizado","Contratar Data Engineer dedicado (cuellos botella en datos)","Establecer definiciones negocio ANTES de comenzar"],'Herramientas Recomendadas':["✅ MLflow: Esencial para tracking","✅ FastAPI: Deployment rápido y robusto","✅ Great Expectations: Validación calidad datos","⚠️ Airflow: Overkill para este proyecto, pero útil a futuro"]}# Guardar proyecto completoartifacts={'codigo':'GitHub repo: company/churn-prediction','datos':'S3: s3://company-ml/projects/churn-2024/','modelos':'MLflow registry: production/churn_model:v1.0','documentos':'Confluence: Projects/Churn Prediction 2024','presentaciones':'Sharepoint/Analytics/Churn_Final_Presentation.pptx'}print("=== PROYECTO COMPLETADO ===\n")print("📚 LECCIONES APRENDIDAS:")forcategoria,itemsinlecciones.items():print(f"\n{categoria}:")foriteminitems:print(f" - {item}")print("\n\n📦 ARTIFACTS GUARDADOS:")fortipo,ubicacioninartifacts.items():print(f" {tipo}: {ubicacion}")print("\n\n✅ PROYECTO CHURN PREDICTION FINALIZADO")print(" Fecha: Mayo 2024")print(" Estado: DESPLEGADO EN PRODUCCIÓN")print(" Next Review: Dentro de 3 meses")