75. Proyecto integrador de modelado con funciones

75.1 Introducción

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.

75.2 Definir el problema

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.

75.3 Variables 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%.

75.4 Función de altas

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));

75.5 Función de bajas

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));

75.6 Función de actualización

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));

75.7 Simulación de varios meses

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));

75.8 Redondear resultados

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));

75.9 Calcular crecimiento total

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));

75.10 Comparar escenarios

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)));
}

75.11 Sensibilidad del modelo

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));
}

75.12 Agregar límite de mercado

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));

75.13 Validar parámetros

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
}));

75.14 Preparar datos para gráfico

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));

75.15 Generar una tabla resumen

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));

75.16 Interpretar resultados

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));

75.17 Ajustar el modelo con datos reales

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]));

75.18 Medir error del modelo

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));

75.19 Mejorar el modelo

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));
}

75.20 Modelo con estacionalidad

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));
}

75.21 Proyecto completo en funciones

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));

75.22 Ideas para extender el proyecto

El proyecto puede ampliarse de muchas formas:

  • Agregar costos de adquisición de usuarios.
  • Modelar ingresos por usuario.
  • Comparar estrategias de crecimiento.
  • Incorporar campañas con fechas específicas.
  • Mostrar gráficos de evolución.
  • Guardar escenarios y comparar resultados.

75.23 Errores comunes

Al construir un proyecto de modelado conviene evitar estos problemas:

  • Empezar a programar sin definir la pregunta.
  • Mezclar unidades, como porcentajes y proporciones.
  • No validar parámetros del modelo.
  • Confundir simulación con predicción exacta.
  • No comparar el modelo con datos reales cuando existen.
  • Hacer una función enorme en lugar de dividir responsabilidades.

75.24 Conclusión

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.

Volver al índice