# fundamentos-de-scikit-learn
Fundamentos de Scikit-learn
Volver a la portada del cursoIniciando el Proyecto
Sección 6 · Proyecto Final
Iniciando el proyecto
El proyecto final es el modelo de entregas tardías de PyEnvíos, completo y listo para usarse. Antes de buscar hiperparámetros conviene hacer bien lo aburrido: entender el problema, mirar los datos, decidir con qué métrica se va a juzgar el modelo y tener un punto de partida medido. Esta guía acompaña al notebook iniciando-el-proyecto, que hace esos pasos con los datos del curso (generar_entregas(), semilla 42). El proyecto usará otra semilla, así que aquí importa el método más que las cifras.
1. El problema en una frase
PyEnvíos promete entregar en 60 minutos. Una entrega es tardía (tarde = 1) si tarda más. Se quiere predecir, al momento de recibir el pedido, si llegará tarde, para avisar al cliente o reasignar el repartidor. Esa frase define qué columnas se pueden usar: solo las que se conocen al recibir el pedido. minutos se conoce cuando la entrega ya terminó, y por eso nunca entra como columna.
2. Exploración rápida
El notebook revisa forma, tipos, faltantes y la tasa de tardías por grupo. Lo que debes encontrar:
- 2000 entregas y 11 columnas; 26.8 % llegan tarde.
- Faltantes: 101 en
peso_kgy 62 enzona. Habrá que imputar. valor_mxnva de 92 a 8435, en una escala muy distinta adistancia_km(0.7 a 33.9). Habrá que escalar para modelos basados en distancias.- Por transporte llega tarde el 45.2 % en bicicleta, el 28.1 % en camioneta y el 18.9 % en moto. Con lluvia, el 46.4 %; sin lluvia, el 21.7 %.
- En hora pico (13 a 15 h y 18 a 20 h), el 39.7 %; fuera de ella, el 17.1 %.
print(df.shape, df["tarde"].mean().round(3))
print(df.isna().sum()[lambda s: s > 0])
print(df.groupby("transporte")["tarde"].mean().round(3))
pico = df["hora"].isin([13, 14, 15, 18, 19, 20])
print(df.groupby(pico)["tarde"].mean().round(3))
Estas tasas no son el modelo, pero sirven para revisarlo después: si el modelo dijera que la bicicleta reduce el retraso, algo estaría mal.
3. Decidir la métrica antes de entrenar
Una tardía que no se avisa (falso negativo) molesta más al cliente que una falsa alarma. Por eso el proyecto se juzga con dos cifras: ROC AUC, para comparar modelos sin depender del umbral, y recall de la clase tarde, para elegir el umbral final. La exactitud se reporta, pero no decide: el modelo que siempre dice «a tiempo» ya tiene 0.732.
4. Partición y punto de partida
Se parte una sola vez, con test_size=0.25, stratify=y y random_state=42 (1500 y 500 entregas), y el test se guarda sin mirar hasta el final. Todas las comparaciones se hacen con validación cruzada sobre train:
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
metricas = ["accuracy", "recall", "roc_auc"]
for modelo in [DummyClassifier(), LogisticRegression(max_iter=1000)]:
p = Pipeline([("pre", pre), ("modelo", modelo)])
cv = cross_validate(p, X_tr, y_tr, cv=skf, scoring=metricas)
print(type(modelo).__name__,
[float(cv[f"test_{m}"].mean().round(3)) for m in metricas])
Con este código, el DummyClassifier da exactitud 0.732, recall 0 y AUC 0.5: es el piso. La regresión logística con pre da 0.880, 0.714 y 0.932. Ese es el número que cualquier modelo del proyecto tiene que superar para justificar su complejidad.
5. El plan del proyecto
Con la línea base medida, el resto del proyecto sigue este plan, que el enunciado detalla:
- Pipeline con
ColumnTransformer(imputar, escalar, one-hot). - Comparar al menos tres modelos con validación cruzada, por AUC.
- Buscar hiperparámetros del mejor candidato con
GridSearchCVoRandomizedSearchCV. - Elegir el umbral según el recall que pida el negocio.
- Evaluar una sola vez en test y guardar el modelo con
joblib.
Qué debes obtener
Un notebook que se ejecuta de principio a fin y deja definidos X_tr, X_te, y_tr, y_te, pre y la tabla de la línea base. Si tu logística no queda cerca de 0.93 de AUC en validación cruzada, revisa que minutos no esté en X (si estuviera, el AUC sería casi 1, y sería una trampa) y que el preprocesamiento esté dentro del pipeline.