Skip to content

Repository files navigation

riesgo-contratos

tests python scikit-learn licencia

Modelo que predice si un contrato público peruano terminará siendo modificado —acumulando adicionales, reducciones o prórrogas— usando solo información disponible el día en que se firma.

Ver los resultados →

33 538 contratos · 27 features · partición temporal estricta · 2021-2025


El resultado

De los 100 contratos que el modelo marca como más riesgosos en 2025, 59 terminaron modificados. La tasa base es 18%, así que revisar 100 contratos al azar habría encontrado 18. El modelo concentra 3,3 veces más casos en la parte alta del ranking.

Contratos revisados Aciertos Mejor que al azar
los 50 de mayor riesgo 72% 4,0x
los 100 de mayor riesgo 59% 3,3x
los 250 de mayor riesgo 48% 2,7x
los 500 de mayor riesgo 49% 2,7x

Ese es el uso real: un equipo de control con capacidad para revisar cien expedientes necesita saber cuáles cien.


Lo que hace este proyecto distinto

1. Las features no ven el futuro

La regla es que una variable solo puede usar información disponible el día de la firma. El historial de cada entidad y de cada proveedor se calcula con ventanas expansivas en SQL:

RANGE BETWEEN UNBOUNDED PRECEDING AND INTERVAL 1 DAY PRECEDING

Y no con el habitual ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING. La diferencia importa: con ROWS, dos contratos firmados el mismo día se ven entre sí según el orden arbitrario de las filas. Con RANGE sobre la fecha, el corte es el día calendario y ningún contrato ve a otro del mismo día.

Quedan fuera del modelo, aunque están en el archivo del portal: los montos de adicionales y prórrogas (son el desenlace), la fecha de publicación en SEACE (ocurre después de firmar) y la fecha de fin actualizada (cambia justamente cuando hay prórroga).

Hay 10 tests dedicados solo a verificar esto.

2. La partición es temporal, no aleatoria

Entrenamiento 2021-2023, validación 2024, prueba 2025. Un split aleatorio habría dejado contratos de 2025 en entrenamiento y de 2021 en prueba, midiendo una habilidad que el modelo nunca va a necesitar: predecir el pasado con información del futuro.

El año de prueba no participó en ninguna decisión — ni en el entrenamiento ni en la elección del modelo — y se midió una sola vez.

3. Compite contra baselines que podrían ganarle

Modelo PR-AUC (validación) Top 100
gradient boosting regularizado 0,478 73%
gradient boosting 0,471 71%
regresión logística 0,452 66%
ordenar por monto, sin aprender 0,430 68%
predecir siempre la tasa base 0,223 27%

El baseline de monto no es de adorno: el monto por sí solo ya separa bastante (13% de modificación en el decil más bajo contra 55% en el más alto). El modelo le gana, pero por un margen moderado, y eso se reporta tal cual. Un README que solo muestre la métrica del modelo ganador esconde justamente lo que hay que saber.

4. El modelo está descalibrado, y el proyecto explica por qué

En el año de prueba el modelo sobreestima: en el decil más alto predice 63,6% y la realidad es 46,7%.

La causa no es el modelo, es el dato. Un contrato firmado en 2025 tuvo menos meses para acumular modificaciones que uno de 2024: es censura a la derecha. El archivo del portal no publica la fecha de cada modificación, así que no hay forma de acotar la ventana de observación. La comparación de las curvas de calibración entre 2024 y 2025 cuantifica la brecha en vez de dejarla como sospecha.

Consecuencia práctica: el orden que produce el modelo es válido; la probabilidad absoluta debe leerse como cota superior mientras los contratos no maduren.


Cómo correrlo

pip install -e ".[api,dev]"
riesgo-contratos todo

O por etapas:

riesgo-contratos datos && riesgo-contratos dataset && riesgo-contratos entrenar

El modelo entrenado se versiona en modelo/modelo.joblib (97 KB), así que la API funciona recién clonado el repositorio:

uvicorn riesgo_contratos.api:app --reload
curl -X POST localhost:8000/predecir -H "Content-Type: application/json" -d '{"monto_pen": 4500000, "objeto": "OBRA", "duracion_prevista": 540, "nivel_gobierno": "REGIONAL"}'

Qué mira el modelo

Importancia por permutación (cuánto empeora el PR-AUC al desordenar cada variable). Se usa esta y no la importancia interna del árbol, que premia a las variables con muchos valores distintos.

Las señales más fuertes son el monto, la duración prevista y el historial de modificación de la entidad. Tiene sentido: una obra grande y larga tiene más superficie para que aparezcan adicionales, y una entidad que modifica sistemáticamente sus contratos probablemente siga haciéndolo.


Supuestos y límites

  • Solo desde 2021. Hasta 2020 el portal publicaba también compras menores (mediana de S/ 7 500 frente a S/ 338 000 desde 2021). Mezclar ambos regímenes sería entrenar sobre dos poblaciones distintas y llamarlas una sola.
  • Tipo de cambio fijo para los contratos en dólares y euros: el portal publica la moneda pero no el tipo de cambio de la fecha de suscripción.
  • El objeto contractual se infiere del texto libre, porque el archivo de contratos no lo publica.
  • "Modificado" no significa "irregular". Un adicional puede responder a una necesidad legítima sobrevenida. Lo que el modelo señala es que lo licitado probablemente no era lo que se iba a ejecutar, que es una señal de calidad de la planificación, no una acusación.

Stack

Python 3.11+ · scikit-learn · DuckDB (features con window functions) · pandas · FastAPI (sirve el modelo) · pytest (27 tests) · ruff · GitHub Actions (CI + Pages)

Proyecto hermano

Los datos vienen del portal CONOSCE del OECE (ex-OSCE). El hallazgo que hace posible el target de este modelo —que el portal publica una fila por modificación y no una por contrato, sin ninguna columna que lo indique— salió de compras-peru, donde está el warehouse completo y el análisis de las compras públicas peruanas.

Licencia

MIT — ver LICENSE.

About

Modelo que predice si un contrato publico peruano terminara siendo modificado, con features construidas sin fuga temporal y particion temporal estricta

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages