Public
Edited
Feb 3, 2023
1 fork
Insert cell
Insert cell
chart_Ecuador = BeeswarmChart(datos_ecuador, {
x: (d) => d.renta_rel * 1,
xLabel: "Más renta →",
xLabel_0: "← Menos renta",
width: 540,
height: 260
})
Insert cell
chart_Colombia = BeeswarmChart(datos_colombia, {
x: (d) => d.renta_rel * 1,
xLabel: "Más renta →",
xLabel_0: "← Menos renta",
width: 540,
height: 260
})
Insert cell
chart_Argentina = BeeswarmChart(datos_argentina, {
x: (d) => d.renta_rel * 1,
xLabel: "Más renta →",
xLabel_0: "← Menos renta",
width: 540,
height: 260
})
Insert cell
chart_Venezuela = BeeswarmChart(datos_venezuela, {
x: (d) => d.renta_rel * 1,
xLabel: "Más renta →",
xLabel_0: "← Menos renta",
width: 540,
height: 260
})
Insert cell
Insert cell
datos_colombia = FileAttachment("bee_colombia (1).csv").csv({ typed: true })
Insert cell
datos_argentina = FileAttachment("bee_argentina (1).csv").csv({ typed: true })
Insert cell
datos_venezuela = FileAttachment("bee_venezuela (1).csv").csv({ typed: true })
Insert cell
// Copyright 2021 Observable, Inc.
// Released under the ISC license.
// https://observablehq.com/@d3/mirrored-beeswarm
function BeeswarmChart(
data,
{
value = (d) => d, // convience alias for x
label, // convenience alias for xLabel
domain, // convenience alias for xDomain
x = value, // given d in data, returns the quantitative x value
title = null, // given d in data, returns the title
radius = 1, // (fixed) radius of the circles
padding = 1.5, // (fixed) padding between the circles
marginTop = 10, // top margin, in pixels
marginRight = 20, // right margin, in pixels
marginBottom = 35, // bottom margin, in pixels
marginLeft = 20, // left margin, in pixels
width = 540, // outer width, in pixels
height, // outer height, in pixels
xLabel, // a label for the x-axis
xLabel_0,
xDomain = domain, // [xmin, xmax]
xRange = [marginLeft, width - marginRight] // [left, right]
} = {}
) {
// Compute values.
const X = d3.map(data, x);
const T = title == null ? null : d3.map(data, title);

// Compute which data points are considered defined.
const I = d3.range(X.length).filter((i) => !isNaN(X[i]));

// Compute default domains.
if (xDomain === undefined) xDomain = d3.extent(X);

// Construct scales and axes.
const xScale = d3.scaleLinear(xDomain, xRange);
const xAxis = d3
.axisBottom(xScale)
.tickSizeOuter(0)
.tickFormat(function (d) {
return formatNumberES(d, 1);
});

// Compute the y-positions.
const Y = dodge(
I.map((i) => xScale(X[i])),
radius * 2 + padding
);

// Compute the default height;
if (height === undefined)
height =
(d3.max(Y, Math.round) + radius + padding) * 2 + marginTop + marginBottom;

// Given an array of x-values and a separation radius, returns an array of y-values.
function dodge(X, radius) {
const Y = new Float64Array(X.length);
const radius2 = radius ** 2;
const epsilon = 1e-3;
let head = null,
tail = null;

// Returns true if circle ⟨x,y⟩ intersects with any circle in the queue.
function intersects(x, y) {
let a = head;
while (a) {
const ai = a.index;
if (radius2 - epsilon > (X[ai] - x) ** 2 + (Y[ai] - y) ** 2)
return true;
a = a.next;
}
return false;
}

// Place each circle sequentially.
for (const bi of d3.range(X.length).sort((i, j) => X[i] - X[j])) {
// Remove circles from the queue that can’t intersect the new circle b.
while (head && X[head.index] < X[bi] - radius2) head = head.next;

// Choose the minimum non-intersecting tangent.
if (intersects(X[bi], (Y[bi] = 0))) {
let a = head;
Y[bi] = Infinity;
do {
const ai = a.index;
let y1 = Y[ai] + Math.sqrt(radius2 - (X[ai] - X[bi]) ** 2);
let y2 = Y[ai] - Math.sqrt(radius2 - (X[ai] - X[bi]) ** 2);
if (Math.abs(y1) < Math.abs(Y[bi]) && !intersects(X[bi], y1))
Y[bi] = y1;
if (Math.abs(y2) < Math.abs(Y[bi]) && !intersects(X[bi], y2))
Y[bi] = y2;
a = a.next;
} while (a);
}

// Add b to the queue.
const b = { index: bi, next: null };
if (head === null) head = tail = b;
else tail = tail.next = b;
}

return Y;
}

const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; height: intrinsic;");

svg
.append("g")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(xAxis)
.attr("fill", "#000000")
.attr("opacity", 0.6)
.call((g) =>
g
.append("text")
.attr("x", width - 20)
.attr("y", marginBottom)
.attr("fill", "currentColor")
.attr("text-anchor", "end")
.text(xLabel)
)
.call((g) =>
g
.append("text")
.attr("x", 20)
.attr("y", marginBottom)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text(xLabel_0)
);

const dot = svg
.append("g")
.selectAll("circle")
.data(I)
.join("circle")
.attr("cx", (i) => xScale(X[i]))
.attr("cy", (i) => (marginTop + height - marginBottom) / 2 + Y[i])
.attr("r", radius);

if (T) dot.append("title").text((i) => T[i]);

return svg.node();
}
Insert cell
/*
* Funcion para devolver un numero formateado con separadores de miles
* y decimales en formato español
* @param {int|float|string} n - numero valido en formato entero, float o string
* @param {int} d - numero de decimales
*/
formatNumberES = (n, d = 1) => {
n = new Intl.NumberFormat("es-ES").format(parseFloat(n).toFixed(d));
if (d > 0) {
// Obtenemos la cantidad de decimales que tiene el numero
const decimals = n.indexOf(",") > -1 ? n.length - 1 - n.indexOf(",") : 0;

// añadimos los ceros necesios al numero
n = decimals == 0 ? n + "," + "0".repeat(d) : n + "0".repeat(d - decimals);
}
return n;
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more