Este proyecto integra los conceptos del curso en un caso práctico: modelar un sistema mediante funciones. El objetivo es pasar de un problema real a un conjunto de variables, reglas, simulaciones y resultados interpretables.
El ejemplo será un modelo simple de crecimiento de usuarios de una aplicación. Aunque el caso es pequeño, la estructura sirve para muchos otros problemas: ventas, inventario, energía, población, tráfico o rendimiento.
Antes de programar, hay que expresar qué se quiere responder. En este proyecto preguntamos: ¿cómo evoluciona la cantidad de usuarios si cada mes entran usuarios nuevos y una parte abandona la aplicación?
usuariosSiguientes = usuariosActuales + altas - bajas
Esta relación básica será el punto de partida del modelo.
Un modelo necesita variables claras. En este caso usaremos usuarios activos, tasa de crecimiento, tasa de abandono y tiempo.
const modelo = {
usuariosIniciales: 1000,
tasaCrecimiento: 0.12,
tasaAbandono: 0.04,
meses: 6
};
console.log(modelo);
Las tasas están expresadas como proporciones: 0.12 representa 12%.
Las altas representan usuarios nuevos. En un modelo simple, pueden calcularse como un porcentaje de los usuarios actuales.
function calcularAltas(usuarios, tasaCrecimiento) {
return usuarios * tasaCrecimiento;
}
console.log(calcularAltas(1000, 0.12));
Las bajas representan usuarios que abandonan. También pueden modelarse como un porcentaje de los usuarios activos.
function calcularBajas(usuarios, tasaAbandono) {
return usuarios * tasaAbandono;
}
console.log(calcularBajas(1000, 0.04));
La función principal del modelo calcula el estado del próximo mes a partir del estado actual.
function siguienteMes(usuarios, tasaCrecimiento, tasaAbandono) {
const altas = usuarios * tasaCrecimiento;
const bajas = usuarios * tasaAbandono;
return usuarios + altas - bajas;
}
console.log(siguienteMes(1000, 0.12, 0.04));
Para observar la evolución, se repite la función de actualización durante varios pasos.
function siguienteMes(usuarios, tasaCrecimiento, tasaAbandono) {
return usuarios + usuarios * tasaCrecimiento - usuarios * tasaAbandono;
}
function simularUsuarios(iniciales, meses, tasaCrecimiento, tasaAbandono) {
const historial = [];
let usuarios = iniciales;
for (let mes = 0; mes <= meses; mes++) {
historial.push({ mes, usuarios });
usuarios = siguienteMes(usuarios, tasaCrecimiento, tasaAbandono);
}
return historial;
}
console.log(simularUsuarios(1000, 6, 0.12, 0.04));
El modelo puede producir decimales, pero los usuarios suelen representarse como cantidades enteras. Una función de formato mejora la lectura.
function formatearHistorial(historial) {
return historial.map(registro => ({
mes: registro.mes,
usuarios: Math.round(registro.usuarios)
}));
}
const historial = [
{ mes: 0, usuarios: 1000 },
{ mes: 1, usuarios: 1080.4 }
];
console.log(formatearHistorial(historial));
Después de simular, se puede calcular cuánto cambió el sistema entre el inicio y el final.
function crecimientoTotal(historial) {
const primero = historial[0].usuarios;
const ultimo = historial[historial.length - 1].usuarios;
return {
diferencia: ultimo - primero,
porcentaje: (ultimo - primero) / primero * 100
};
}
const historial = [
{ mes: 0, usuarios: 1000 },
{ mes: 6, usuarios: 1586.87 }
];
console.log(crecimientoTotal(historial));
Un modelo es más útil cuando permite probar escenarios. Por ejemplo, se pueden comparar tasas de crecimiento distintas.
function simularFinal(iniciales, meses, crecimiento, abandono) {
let usuarios = iniciales;
for (let mes = 1; mes <= meses; mes++) {
usuarios = usuarios + usuarios * crecimiento - usuarios * abandono;
}
return usuarios;
}
const escenarios = [
{ nombre: "conservador", crecimiento: 0.06, abandono: 0.04 },
{ nombre: "base", crecimiento: 0.12, abandono: 0.04 },
{ nombre: "optimista", crecimiento: 0.18, abandono: 0.03 }
];
for (const escenario of escenarios) {
console.log(escenario.nombre, Math.round(simularFinal(1000, 12, escenario.crecimiento, escenario.abandono)));
}
El análisis de sensibilidad muestra cuánto cambia el resultado cuando se modifica una variable.
function simularFinal(iniciales, meses, crecimiento, abandono) {
let usuarios = iniciales;
for (let mes = 1; mes <= meses; mes++) {
usuarios *= 1 + crecimiento - abandono;
}
return usuarios;
}
for (const abandono of [0.02, 0.04, 0.06, 0.08]) {
const final = simularFinal(1000, 12, 0.12, abandono);
console.log("Abandono:", abandono, "usuarios:", Math.round(final));
}
En muchos sistemas, el crecimiento no puede continuar indefinidamente. Se puede agregar una capacidad máxima para representar un mercado limitado.
function siguienteMesLimitado(usuarios, crecimiento, abandono, capacidad) {
const factorLimite = 1 - usuarios / capacidad;
const altas = usuarios * crecimiento * factorLimite;
const bajas = usuarios * abandono;
return usuarios + altas - bajas;
}
console.log(siguienteMesLimitado(9000, 0.20, 0.03, 10000));
Antes de simular, conviene validar que los parámetros tengan sentido.
function parametrosValidos(modelo) {
return modelo.usuariosIniciales >= 0 &&
modelo.meses >= 0 &&
modelo.tasaCrecimiento >= 0 &&
modelo.tasaAbandono >= 0 &&
modelo.tasaAbandono <= 1;
}
console.log(parametrosValidos({
usuariosIniciales: 1000,
meses: 12,
tasaCrecimiento: 0.1,
tasaAbandono: 0.05
}));
El historial de la simulación puede transformarse en puntos para un gráfico de líneas.
function prepararPuntos(historial, ancho, alto) {
const maxUsuarios = Math.max(...historial.map(r => r.usuarios));
const maxMes = Math.max(...historial.map(r => r.mes));
return historial.map(registro => ({
x: registro.mes / maxMes * ancho,
y: alto - registro.usuarios / maxUsuarios * alto
}));
}
const historial = [
{ mes: 0, usuarios: 1000 },
{ mes: 1, usuarios: 1080 },
{ mes: 2, usuarios: 1166 }
];
console.log(prepararPuntos(historial, 400, 250));
Una función puede transformar resultados numéricos en una tabla lista para mostrar.
function tablaResumen(historial) {
return historial.map(registro => ({
mes: registro.mes,
usuarios: Math.round(registro.usuarios).toLocaleString("es-AR")
}));
}
const historial = [
{ mes: 0, usuarios: 1000 },
{ mes: 1, usuarios: 1080 },
{ mes: 2, usuarios: 1166.4 }
];
console.log(tablaResumen(historial));
El resultado de un modelo necesita interpretación. Una función puede convertir métricas en una conclusión breve.
function interpretarCrecimiento(porcentaje) {
if (porcentaje > 50) {
return "crecimiento alto";
}
if (porcentaje > 10) {
return "crecimiento moderado";
}
if (porcentaje >= 0) {
return "crecimiento bajo";
}
return "pérdida de usuarios";
}
console.log(interpretarCrecimiento(58));
console.log(interpretarCrecimiento(-4));
Si se tienen datos históricos, se puede estimar una tasa promedio observando el cambio entre períodos.
function tasasMensuales(historial) {
const tasas = [];
for (let i = 1; i < historial.length; i++) {
const anterior = historial[i - 1];
const actual = historial[i];
tasas.push((actual - anterior) / anterior);
}
return tasas;
}
console.log(tasasMensuales([1000, 1080, 1134, 1240]));
Para saber si el modelo se aproxima a la realidad, se comparan valores simulados con valores reales.
function errorAbsolutoMedio(reales, simulados) {
let total = 0;
for (let i = 0; i < reales.length; i++) {
total += Math.abs(reales[i] - simulados[i]);
}
return total / reales.length;
}
const reales = [1000, 1100, 1210];
const simulados = [1000, 1080, 1166];
console.log(errorAbsolutoMedio(reales, simulados));
Un modelo inicial suele ser simple. Luego puede mejorarse incorporando campañas, estacionalidad, límites o cambios en las tasas.
function crecimientoConCampania(mes, crecimientoBase) {
if (mes === 3 || mes === 9) {
return crecimientoBase + 0.08;
}
return crecimientoBase;
}
for (let mes = 1; mes <= 12; mes++) {
console.log("Mes", mes, "crecimiento:", crecimientoConCampania(mes, 0.10));
}
La estacionalidad representa patrones que se repiten en el tiempo. Puede modelarse con una función periódica.
function factorEstacional(mes) {
return 1 + 0.1 * Math.sin(2 * Math.PI * mes / 12);
}
for (let mes = 1; mes <= 12; mes++) {
console.log("Mes", mes, factorEstacional(mes).toFixed(3));
}
El siguiente ejemplo reúne validación, simulación y resumen en un flujo pequeño y completo.
function validar(modelo) {
return modelo.usuariosIniciales >= 0 && modelo.meses >= 0;
}
function actualizar(usuarios, modelo) {
return usuarios + usuarios * modelo.crecimiento - usuarios * modelo.abandono;
}
function simular(modelo) {
if (!validar(modelo)) {
return [];
}
const historial = [];
let usuarios = modelo.usuariosIniciales;
for (let mes = 0; mes <= modelo.meses; mes++) {
historial.push({ mes, usuarios });
usuarios = actualizar(usuarios, modelo);
}
return historial;
}
function resumir(historial) {
const inicio = historial[0].usuarios;
const fin = historial[historial.length - 1].usuarios;
return {
inicio: Math.round(inicio),
fin: Math.round(fin),
crecimiento: ((fin - inicio) / inicio * 100).toFixed(2) + "%"
};
}
const historial = simular({
usuariosIniciales: 1000,
meses: 12,
crecimiento: 0.12,
abandono: 0.04
});
console.log(resumir(historial));
El proyecto puede ampliarse de muchas formas:
Al construir un proyecto de modelado conviene evitar estos problemas:
Un proyecto de modelado con funciones transforma una situación en un sistema calculable. Para lograrlo se definen variables, se escriben reglas, se simula el comportamiento y se interpretan los resultados.
Este enfoque resume el propósito del curso: usar funciones matemáticas y programación para representar problemas, analizarlos y tomar decisiones con mayor claridad.