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.
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.
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);
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);
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));
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"));
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));
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));
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 }));
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]));
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)));
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));
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));
}
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"));
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));
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));
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));
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]));
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));
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));
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));
La generación programática de gráficos se usa en:
Al generar gráficos mediante programación conviene evitar estos problemas:
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.