73. Generación de gráficos mediante programación

73.1 Introducción

Generar gráficos mediante programación consiste en transformar datos en elementos visuales: barras, puntos, líneas, sectores, etiquetas y ejes. Las funciones matemáticas permiten calcular tamaños, posiciones, colores y proporciones.

Un gráfico no es solo una imagen. Es una representación calculada a partir de datos y reglas de dibujo.

73.2 Datos de entrada

Todo gráfico comienza con datos. En programación suelen representarse como arreglos de números u objetos.

const ventas = [
  { mes: "Ene", total: 120 },
  { mes: "Feb", total: 180 },
  { mes: "Mar", total: 90 }
];

console.log(ventas);

La estructura elegida debe facilitar el cálculo de posiciones y medidas visuales.

73.3 Separar datos y dibujo

Una buena práctica es preparar primero los datos visuales y dibujarlos después. Así la lógica matemática no queda mezclada con la tecnología de dibujo.

function prepararDato(valor, indice) {
  return {
    etiqueta: `Dato ${indice + 1}`,
    valor,
    color: valor >= 50 ? "azul" : "gris"
  };
}

const datos = [40, 75, 60].map(prepararDato);
console.log(datos);

73.4 Función de escala

Una escala convierte un valor de datos en una medida gráfica. Por ejemplo, una venta puede convertirse en altura de barra.

function escalar(valor, maxDato, maxVisual) {
  return valor / maxDato * maxVisual;
}

const valor = 75;
const altoBarra = escalar(valor, 100, 300);

console.log("Alto de barra:", altoBarra);

73.5 Gráfico de barras

Para generar un gráfico de barras se calcula la posición horizontal, el ancho y la altura de cada barra.

function generarBarras(datos, anchoTotal, altoTotal) {
  const max = Math.max(...datos);
  const anchoBarra = anchoTotal / datos.length;

  return datos.map((valor, indice) => ({
    x: indice * anchoBarra,
    y: altoTotal - valor / max * altoTotal,
    ancho: anchoBarra * 0.8,
    alto: valor / max * altoTotal
  }));
}

console.log(generarBarras([10, 25, 40], 300, 200));

73.6 Etiquetas de barras

Las etiquetas se posicionan a partir de la geometría de cada barra. Conviene calcularlas con una función separada.

function etiquetaBarra(barra, texto) {
  return {
    texto,
    x: barra.x + barra.ancho / 2,
    y: barra.y - 8
  };
}

const barra = { x: 20, y: 60, ancho: 40, alto: 140 };
console.log(etiquetaBarra(barra, "120"));

73.7 Gráfico de líneas

Un gráfico de líneas transforma cada dato en un punto y luego une esos puntos en orden.

function generarPuntosLinea(datos, ancho, alto) {
  const max = Math.max(...datos);
  const pasoX = ancho / (datos.length - 1);

  return datos.map((valor, indice) => ({
    x: indice * pasoX,
    y: alto - valor / max * alto
  }));
}

console.log(generarPuntosLinea([5, 12, 8, 20], 300, 150));

73.8 Construir una línea SVG

En SVG, una polilínea puede representarse como una cadena de coordenadas. Una función puede generar esa cadena.

function puntosSVG(puntos) {
  return puntos.map(p => `${p.x},${p.y}`).join(" ");
}

const puntos = [
  { x: 0, y: 100 },
  { x: 50, y: 60 },
  { x: 100, y: 80 }
];

console.log(puntosSVG(puntos));

73.9 Gráfico de puntos

Un gráfico de dispersión ubica puntos según dos variables. Cada registro produce una coordenada x y una coordenada y.

function mapear(valor, minOrigen, maxOrigen, minDestino, maxDestino) {
  const proporcion = (valor - minOrigen) / (maxOrigen - minOrigen);
  return minDestino + proporcion * (maxDestino - minDestino);
}

function puntoDispersión(registro) {
  return {
    x: mapear(registro.edad, 0, 100, 0, 400),
    y: mapear(registro.ingreso, 0, 200000, 300, 0)
  };
}

console.log(puntoDispersión({ edad: 40, ingreso: 90000 }));

73.10 Gráfico circular

Un gráfico circular reparte un círculo según proporciones. Cada valor se convierte en un ángulo.

function sectores(datos) {
  const total = datos.reduce((suma, valor) => suma + valor, 0);
  let acumulado = 0;

  return datos.map(valor => {
    const inicio = acumulado / total * 2 * Math.PI;
    acumulado += valor;
    const fin = acumulado / total * 2 * Math.PI;
    return { valor, inicio, fin };
  });
}

console.log(sectores([30, 20, 50]));

73.11 Porcentajes para gráficos

Los porcentajes ayudan a interpretar partes de un total. Se calculan dividiendo cada valor por la suma total.

function porcentajes(datos) {
  const total = datos.reduce((suma, valor) => suma + valor, 0);
  return datos.map(valor => valor / total * 100);
}

console.log(porcentajes([15, 35, 50]).map(v => v.toFixed(1)));

73.12 Ejes automáticos

Para generar ejes, se calculan marcas de escala y se convierten a posiciones visuales.

function generarMarcas(maximo, cantidad) {
  const marcas = [];
  const paso = maximo / cantidad;

  for (let i = 0; i <= cantidad; i++) {
    marcas.push(i * paso);
  }

  return marcas;
}

console.log(generarMarcas(100, 5));

73.13 Posición de marcas

Una marca del eje se dibuja en una posición calculada según su valor y el tamaño del gráfico.

function posicionMarca(valor, maximo, alto) {
  return alto - valor / maximo * alto;
}

for (const marca of [0, 25, 50, 75, 100]) {
  console.log(marca, "=>", posicionMarca(marca, 100, 200));
}

73.14 Colores por categoría

Los colores pueden generarse a partir de categorías para mantener consistencia visual.

function colorCategoria(categoria) {
  const colores = {
    ventas: "#2f80ed",
    costos: "#eb5757",
    ganancias: "#27ae60"
  };

  return colores[categoria] || "#777777";
}

console.log(colorCategoria("ventas"));
console.log(colorCategoria("otro"));

73.15 Colores por intensidad

Un valor numérico puede transformarse en intensidad. Esto se usa en mapas de calor e indicadores.

function intensidad(valor, maximo) {
  const proporcion = Math.max(0, Math.min(1, valor / maximo));
  return Math.round(proporcion * 255);
}

console.log(intensidad(30, 100));
console.log(intensidad(100, 100));

73.16 Ordenar antes de graficar

Algunos gráficos son más legibles si los datos se ordenan antes de dibujarlos.

function ordenarPorValor(datos) {
  return [...datos].sort((a, b) => b.valor - a.valor);
}

const datos = [
  { nombre: "A", valor: 20 },
  { nombre: "B", valor: 50 },
  { nombre: "C", valor: 35 }
];

console.log(ordenarPorValor(datos));

73.17 Agrupar datos para graficar

Muchas veces hay que resumir registros antes de generar un gráfico.

function sumarPorCategoria(registros) {
  return registros.reduce((totales, registro) => {
    totales[registro.categoria] = (totales[registro.categoria] || 0) + registro.valor;
    return totales;
  }, {});
}

const registros = [
  { categoria: "A", valor: 10 },
  { categoria: "B", valor: 25 },
  { categoria: "A", valor: 15 }
];

console.log(sumarPorCategoria(registros));

73.18 Datos faltantes

Un gráfico debe decidir cómo tratar valores faltantes: quitarlos, reemplazarlos o mostrarlos como interrupciones.

function limpiarParaGrafico(datos) {
  return datos.filter(valor => valor !== null && valor !== undefined && Number.isFinite(valor));
}

console.log(limpiarParaGrafico([10, null, 20, NaN, 30]));

73.19 Formatear valores

Los números que aparecen en etiquetas y ejes deben ser comprensibles para el usuario.

function formatearNumero(valor) {
  if (valor >= 1000000) {
    return (valor / 1000000).toFixed(1) + "M";
  }

  if (valor >= 1000) {
    return (valor / 1000).toFixed(1) + "K";
  }

  return valor.toString();
}

console.log(formatearNumero(850));
console.log(formatearNumero(12500));
console.log(formatearNumero(2400000));

73.20 Generar SVG básico

Una función puede producir una cadena SVG simple a partir de datos calculados.

function rectSVG(rect) {
  return ``;
}

const rectangulo = { x: 10, y: 20, ancho: 80, alto: 120 };

console.log(rectSVG(rectangulo));

73.21 Preparar comandos para canvas

En canvas se dibuja con instrucciones. Aun así, se puede preparar una lista de comandos antes de ejecutarlos.

function comandosBarras(barras) {
  return barras.map(barra => ({
    tipo: "rect",
    x: barra.x,
    y: barra.y,
    ancho: barra.ancho,
    alto: barra.alto
  }));
}

const barras = [
  { x: 0, y: 50, ancho: 20, alto: 100 },
  { x: 30, y: 20, ancho: 20, alto: 130 }
];

console.log(comandosBarras(barras));

73.22 Aplicaciones en programación

La generación programática de gráficos se usa en:

  • Tableros de control y reportes.
  • Visualización de métricas de sistemas.
  • Gráficos estadísticos y financieros.
  • Herramientas educativas.
  • Simulaciones y monitoreo en tiempo real.
  • Exportación automática de imágenes o SVG.

73.23 Errores comunes

Al generar gráficos mediante programación conviene evitar estos problemas:

  • No separar cálculo de datos y dibujo final.
  • Usar escalas inconsistentes entre gráficos comparables.
  • No manejar datos faltantes o inválidos.
  • Mostrar etiquetas con demasiados decimales.
  • Elegir colores sin relación clara con los datos.
  • Olvidar que el eje vertical de pantalla crece hacia abajo.

73.24 Conclusión

Generar gráficos por programación es transformar datos en geometría. Cada barra, punto, línea o sector surge de funciones que calculan posiciones, tamaños, colores y etiquetas.

Cuando esas funciones están bien separadas y nombradas, el gráfico se vuelve más fácil de modificar, reutilizar y verificar.

Volver al índice