typestar

sk_churn_model.py en Python

Un modelo de punta a punta: dividir, preprocesar por tipo de columna, grid search, reporte.

import numpy as np
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import RandomForestClassifier
from sklearn.impute import SimpleImputer
from sklearn.metrics import classification_report, roc_auc_score
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler

NUMERICAS = [0, 1, 2]
CATEGORICAS = [3]


def crear_datos(filas=600, semilla=0):
    """Antigüedad, gasto mensual, llamadas a soporte y un plan."""
    rng = np.random.default_rng(semilla)
    antiguedad = rng.integers(1, 72, size=filas)
    gasto = rng.normal(70, 25, size=filas).clip(10, None)
    llamadas = rng.poisson(1.2, size=filas)
    plan = rng.choice(["basico", "plus", "pro"], size=filas)

    riesgo = (0.03 * llamadas + 0.02 * (gasto / 50) - 0.02 * (antiguedad / 12)
            + (plan == "basico") * 0.15)
    abandono = (rng.random(filas) < riesgo.clip(0.02, 0.9)).astype(int)

    X = np.empty((filas, 4), dtype=object)
    X[:, 0], X[:, 1], X[:, 2], X[:, 3] = antiguedad, gasto, llamadas, plan
    return X, abandono


def armar_pipeline():
    numerico = Pipeline([
        ("impute", SimpleImputer(strategy="median")),
        ("scale", StandardScaler()),
    ])
    preparador = ColumnTransformer([
        ("numeric", numerico, NUMERICAS),
        ("categorical", OneHotEncoder(handle_unknown="ignore"), CATEGORICAS),
    ])
    return Pipeline([
        ("prepare", preparador),
        ("model", RandomForestClassifier(random_state=0)),
    ])


def main():
    X, y = crear_datos()
    X_entreno, X_prueba, y_entreno, y_prueba = train_test_split(
        X, y, test_size=0.25, random_state=0, stratify=y)

    malla = {
        "model__n_estimators": [100, 300],
        "model__max_depth": [4, 8, None],
        "model__min_samples_leaf": [1, 5],
    }
    busqueda = GridSearchCV(armar_pipeline(), malla, cv=4, scoring="roc_auc",
                          n_jobs=1)
    busqueda.fit(X_entreno, y_entreno)

    print("mejores parámetros")
    for clave, valor in sorted(busqueda.best_params_.items()):
        print(f"  {clave:28} {valor}")
    print(f"auc valid. cruzada  {busqueda.best_score_:.3f}")

    mejor = busqueda.best_estimator_
    probabilidades = mejor.predict_proba(X_prueba)[:, 1]
    print(f"auc datos apartados {roc_auc_score(y_prueba, probabilidades):.3f}")
    print(classification_report(y_prueba, mejor.predict(X_prueba), digits=3))

    nombres = mejor.named_steps["prepare"].get_feature_names_out()
    pesos = mejor.named_steps["model"].feature_importances_
    orden = np.argsort(pesos)[::-1]
    print("lo que importó")
    for i in orden[:5]:
        print(f"  {nombres[i]:28} {pesos[i]:.3f}")


if __name__ == "__main__":
    main()

Cómo funciona

  1. El ColumnTransformer mantiene separados los caminos numérico y categórico.
  2. GridSearchCV valida en cruzado cada combinación dentro del pipeline.
  3. El conjunto reservado se evalúa una vez, al final, y nunca se ajusta contra él.

Palabras clave y builtins usados aquí

El intento, en números

Líneas
79
Caracteres a escribir
2503
Tokens
666
Ritmo de tres estrellas
115 tpm

Al ritmo de tres estrellas de 115 tokens por minuto, este intento toma unos 347 segundos.

Escribe este fragmento

Paso 1 de 1 en Bis; paso 25 de 25 en Machine learning con scikit-learn.

← Anterior