Public
Edited
Oct 22, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// Import Hanken Grotesk font
html`<link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;700&display=swap" rel="stylesheet">`

Insert cell
Insert cell
Insert cell
participacion_corregido_2.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Insert cell
Insert cell
xColumnName = "año";
Insert cell
Insert cell
yColumnName = "participacion";
Insert cell
Insert cell
electionColumnName = "eleccion";
Insert cell
Insert cell
// Define the file attachment URL
logoUrl = await FileAttachment("Logo DecideChile (Gris).png").url();
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function plotCustomBarGraph(data) {
return Plot.plot({
width: 920, // width
height: 645, // height
x: {
type: "band",
padding: 0.2, // size of the bars
ticks: false, // no ticks in x axe
label: null,
},
y: { axis: null }, // Remove y-axis
style: {
fontFamily: "Hanken Grotesk", // Apply to the entire plot
},
marginTop: 15, // Add top margin to prevent text from getting cut off
marks: [
Plot.barY(data, { //bars
x: xColumnName,
y: yColumnName,
fill: (d, i) => i === data.length ? "#2150E0" : "#839FFE", // Change the last bar color A DATA LENGH SE LE TIENE QUE HACER UN -1
}),
Plot.ruleY([0], { stroke: 'grey' }), // add x-axis line with color grey
Plot.text(data, {
x: xColumnName,
y: d => d[yColumnName] - 0.1, // Adjust the y-position slightly above the bar
text: d => `${parseFloat(d[yColumnName]).toFixed(1).replace('.', ',')}%`, // add percentage above the bar
textAnchor: "middle", // Center the text horizontally
dy: -15, // Adjust vertical position to place above the bar
fill: "black", // Set text color
fontWeight: "regular", // Correctly set the font weight
fontSize: "16px",
}),
Plot.axisX({ //Creates the x axis, so the label size can change
label: null,
tickSize: 0,
fontSize: "16px", // Set the desired font size here
tickFormat: d => typeof d === 'string' ? d.split('-')[0] : d //the year is 2021-02, i dont need the month
}),
createTextInsideBars(data, xColumnName, yColumnName, electionColumnName), //text inside the bars, election name
//handleLastBarText(data, xColumnName, yColumnName, electionColumnName, 23), //if ppercentage is bellow threshold, the text of last bar is not plotted
createVerticalLineMarks(data, 4, "Voto voluntario, \n inscripción automática", 80), // create the vertical lines with extra information, the first number is the index where you want to position the line, the second number is the height of the line and text.
createVerticalLineMarks(data, 5, "Fin binominal", 70),
createVerticalLineMarks(data, 10, "Voto obligatorio, inscripción automática \n y cambio de local", 100),
//plotLastUpdatedNote(data, lastUpdateNote , 100)
],
});
}


Insert cell
Insert cell
Insert cell
function createVerticalLineMarks(data, position, text, yposition) {
return [
Plot.ruleX([data[position][xColumnName]], {
stroke: "#828282",
dx: 30, // the line is created at the center of the bar, so add a dx to move the line to the right
strokeDasharray: "5",
y1: 0, // Start position of the line (closer to the bottom)
y2: yposition // End position of the line (closer to the top)
}),
Plot.text([{
x: data[position][xColumnName],
y: yposition + 4, // Adjust this value to control the vertical position of the text
text: text
}], {
x: "x",
y: "y",
text: "text",
fill: "black",
dx: 28,
fontWeight: "regular",
fontFamily: "Hanken Grotesk",
fontSize: "16px",
textAnchor: "middle"
})
];
}

Insert cell
Insert cell
function createTextInsideBars(data, xColumnName, yColumnName, electionColumnName) {
return Plot.text(data.slice(0, data.length), { // Exclude the last bar
x: xColumnName,
y: 0,
text: d => splitText(`${d[electionColumnName]}`, 33), // Split text if too long, the number is the number of character to make a /n
textAnchor: 'start', // Align the text to the start of the bar
dy: -10, // Adjust horizontal position to add some padding from the start
fill: "white",
fontWeight: 600, // to get the semi bold
fontSize: "16px",
fontFamily: "Hanken Grotesk",
frameAnchor: "middle", // Center the text horizontally and vertically
rotate: -90, // Rotate the text
});
}

Insert cell
function handleLastBarText(data, xColumnName, yColumnName, electionColumnName, threshold) {
const lastIndex = data.length - 1;
const lastValue = data[lastIndex][yColumnName];

if (lastValue >= threshold) {
return Plot.text([data[lastIndex]], {
x: xColumnName,
y: 0, // Position inside the bar
text: d => splitText(`${d[electionColumnName]}`, 35),
textAnchor: 'start', // Align the text to the start of the bar
dy: -10, // Adjust horizontal position to add some padding from the start
fill: "white",
fontWeight: 600, // to get the semi bold
fontSize: "16px",
fontFamily: "Hanken Grotesk",
frameAnchor: "middle", // Center the text horizontally and vertically
rotate: -90, // Rotate the text
});
}
return
}
Insert cell
Insert cell
function plotLastUpdatedNote(data, noteText, yposition) {
return [
Plot.text([{
x: data[0][xColumnName],
y: yposition + 4, // Adjust this value to control the vertical position of the text
text: noteText
}], {
x: "x",
y: "y",
text: "text",
fill: "black",
dx: -8,
fontWeight: "regular",
fontFamily: "JetBrains Mono",
fontSize: "12px",
textAnchor: "start"
}),
Plot.dot([{x: data[0][xColumnName], y: yposition + 4}], {
x: "x",
y: "y",
r: 3, // Radius of the dot
fill: "red",
stroke: "none",
dx : -18
})
];
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function createMainContainer(){
const containerWidth = "1040"; // Ancho del contenedor
const containerHeight = "940"; // Alto del contenedor
const container = document.createElement("div");
container.style.backgroundColor = 'white'//"#f5f5f5"; //color de fondo del contenedor principal
container.style.position = "relative"; // Establecer posición relativa para posicionar elementos hijos
container.style.width = containerWidth + "px"; // Establecer el ancho del contenedor
container.style.height = containerHeight + "px";
return container;
};

Insert cell
Insert cell
function createElementsContainers() {
// Crear contenedores para los elementos de la página
const titleContainer = document.createElement("div"); // Contenedor para el título
const subtitleContainer = document.createElement("div"); // Contenedor para el subtítulo
const plotContainer = document.createElement("div"); // Contenedor para la gráfica
const leyendContainer = document.createElement("div"); // Contenedor para la leyenda
const sourceContainer = document.createElement("div"); // Contenedor para la fuente de información
const logoContainer = document.createElement("a"); // Contenedor para el logo (enlace interactivo)

// Establecer estilo para el contenedor del título
titleContainer.style.position = "absolute"; // Posición absoluta
titleContainer.style.height = "50px"; // Altura del contenedor
titleContainer.style.width = "100%"; // Ancho del contenedor
titleContainer.style.left = "60px"; // Posicionamiento desde el borde izquierdo
titleContainer.style.top = "30px"; // Posicionamiento desde el borde superior

// Establecer estilo para el contenedor del subtítulo
subtitleContainer.style.position = "absolute"; // Posición absoluta
subtitleContainer.style.height = "24px"; // Altura del contenedor
subtitleContainer.style.width = "100%"; // Ancho del contenedor
subtitleContainer.style.left = "61px"; // Posicionamiento desde el borde izquierdo
subtitleContainer.style.top = "90px"; // Posicionamiento desde el borde superior

// Establecer estilo para el contenedor de la gráfica
plotContainer.style.position = "absolute"; // Posición absoluta
plotContainer.style.top = "160px"; // Posicionamiento desde el borde superior
plotContainer.style.left = "60px"; // Posicionamiento desde el borde izquierdo
plotContainer.style.height = "655px"; // Altura del contenedor
plotContainer.style.width = "920px"; // Ancho del contenedor
// Establecer estilo para el contenedor de la leyenda
leyendContainer.style.position = "absolute"; // Posición absoluta
leyendContainer.style.bottom = "0px"; // Posicionamiento desde el borde inferior
leyendContainer.style.left = "0px"; // Posicionamiento desde el borde izquierdo
leyendContainer.style.height = "100px"; // Altura del contenedor
leyendContainer.style.width = "1040px"; // Ancho del contenedor
// Establecer estilo para el contenedor de la fuente
sourceContainer.style.position = "absolute"; // Posición absoluta
sourceContainer.style.width = "646px"; // Ancho del contenedor
sourceContainer.style.left = "60px"; // Posicionamiento desde el borde izquierdo
sourceContainer.style.top = "24px"; // Posicionamiento desde el borde superior
// Establecer estilo para el contenedor del logo
logoContainer.style.position = "absolute"; // Posición absoluta
logoContainer.style.top = "30px"; // Posicionamiento desde el borde superior
logoContainer.style.right = "60px"; // Posicionamiento desde el borde derecho
// Devolver los contenedores en un array
return [plotContainer, leyendContainer, sourceContainer, logoContainer, titleContainer, subtitleContainer];
}

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function fillSource(sourceContainer, text) {
sourceContainer.innerText = text;
sourceContainer.style.fontSize = "13px"; // Tamaño de fuente del texto
sourceContainer.style.color = "#9B9B9B"; // Color del texto (puedes cambiarlo según tus necesidades)
sourceContainer.style.fontFamily = 'JetBrains Mono'; // Aplica la fuente Source Sans Pro

return sourceContainer;
};
Insert cell
Insert cell
function fillPlot(plotContainer, data, plotFunctionName) {
plotContainer.appendChild(plotFunctionName(data));
return plotContainer;
};
Insert cell
function fillLeyend(leyendContainer, sourceContainer, logoContainer) {
// Add a custom class to leyendContainer
leyendContainer.classList.add('leyend-container');

// Append sourceContainer and logoContainer to the leyendContainer
leyendContainer.appendChild(sourceContainer);
leyendContainer.appendChild(logoContainer);

return leyendContainer;
}

Insert cell
function fillHeader(titleContainer, subtitleContainer, title, subtitle) {
titleContainer.innerText = title;
titleContainer.style.fontSize = "44px"; // Tamaño de fuente del texto
titleContainer.style.color = "#171B21"; // Color del texto (puedes cambiarlo según tus necesidades)
titleContainer.style.fontFamily = 'Hanken Grotesk'; // Aplica la fuente Source Sans Pro
titleContainer.style.fontWeight = '500'; // Use font weight 500 for Medium
subtitleContainer.innerText = subtitle;
subtitleContainer.style.fontSize = "30px"; // Tamaño de fuente del texto
subtitleContainer.style.color = "#4F4F4F"; // Color del texto (puedes cambiarlo según tus necesidades)
subtitleContainer.style.fontFamily = 'Hanken Grotesk'; // Aplica la fuente Source Sans Pro
return [titleContainer, subtitleContainer]
}
Insert cell
Insert cell
Insert cell
function createFigure(data, title, subtitle, source, functionName) {
const mainContainer = createMainContainer();
let [plotContainer, leyendContainer, sourceContainer ,logoContainer, titleContainer, subtitleContainer] = createElementsContainers();
[titleContainer,subtitleContainer] = fillHeader(titleContainer, subtitleContainer, title, subtitle)
sourceContainer = fillSource(sourceContainer, source);
logoContainer = fillLogo(logoContainer);
mainContainer.appendChild(titleContainer);
mainContainer.appendChild(subtitleContainer);
mainContainer.appendChild(fillPlot(plotContainer, data, functionName));

mainContainer.appendChild(fillLeyend(leyendContainer, sourceContainer, logoContainer));

return mainContainer;
}

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
slide16_912 = FileAttachment("Slide 16_9 - 12.png").image()
Insert cell
Insert cell
// Example usage with the provided data
dataExample = [
{Género: "Mujeres", Edad: ">54 años", Votación: "2da Vuelta Presidencial 2021", Porcentaje: 46},
{Género: "Mujeres", Edad: ">54 años", Votación: "Plebiscito de Salida 2022", Porcentaje: 80},
{Género: "Mujeres", Edad: ">54 años", Votación: "Consejo Constitucional 2023", Porcentaje: 77},
{Género: "Mujeres", Edad: ">54 años", Votación: "Plebiscito de Salida 2023", Porcentaje: 81},
{Género: "Mujeres", Edad: "34 a 54 años", Votación: "2da Vuelta Presidencial 2021", Porcentaje: 69},
{Género: "Mujeres", Edad: "34 a 54 años", Votación: "Plebiscito de Salida 2022", Porcentaje: 89},
{Género: "Mujeres", Edad: "34 a 54 años", Votación: "Consejo Constitucional 2023", Porcentaje: 91},
{Género: "Mujeres", Edad: "34 a 54 años", Votación: "Plebiscito de Salida 2023", Porcentaje: 91},
{Género: "Mujeres", Edad: "<34 años", Votación: "2da Vuelta Presidencial 2021", Porcentaje: 67},
{Género: "Mujeres", Edad: "<34 años", Votación: "Plebiscito de Salida 2022", Porcentaje: 93},
{Género: "Mujeres", Edad: "<34 años", Votación: "Consejo Constitucional 2023", Porcentaje: 91},
{Género: "Mujeres", Edad: "<34 años", Votación: "Plebiscito de Salida 2023", Porcentaje: 94},
{Género: "Hombres", Edad: ">54 años", Votación: "2da Vuelta Presidencial 2021", Porcentaje: 46},
{Género: "Hombres", Edad: ">54 años", Votación: "Plebiscito de Salida 2022", Porcentaje: 80},
{Género: "Hombres", Edad: ">54 años", Votación: "Consejo Constitucional 2023", Porcentaje: 77},
{Género: "Hombres", Edad: ">54 años", Votación: "Plebiscito de Salida 2023", Porcentaje: 81},
{Género: "Hombres", Edad: "34 a 54 años", Votación: "2da Vuelta Presidencial 2021", Porcentaje: 69},
{Género: "Hombres", Edad: "34 a 54 años", Votación: "Plebiscito de Salida 2022", Porcentaje: 89},
{Género: "Hombres", Edad: "34 a 54 años", Votación: "Consejo Constitucional 2023", Porcentaje: 91},
{Género: "Hombres", Edad: "34 a 54 años", Votación: "Plebiscito de Salida 2023", Porcentaje: 91},
{Género: "Hombres", Edad: "<34 años", Votación: "2da Vuelta Presidencial 2021", Porcentaje: 67},
{Género: "Hombres", Edad: "<34 años", Votación: "Plebiscito de Salida 2022", Porcentaje: 93},
{Género: "Hombres", Edad: "<34 años", Votación: "Consejo Constitucional 2023", Porcentaje: 91},
{Género: "Hombres", Edad: "<34 años", Votación: "Plebiscito de Salida 2023", Porcentaje: 94}
];
Insert cell
Insert cell
function plotBarGenreDistribution(data) {
return Plot.plot({
label: null, //eliminate label of "Género" and "Edad"
width: 900, // width
height: 580, // height
marginTop: 100, //add top space for text
x: { grid: true , domain : [0,100]}, //stablish that x axis must be from 0 to 100.
y: {
label: null,
axis: null,
domain: ["2da Vuelta Presidencial 2021", "Plebiscito de Salida 2022", "Consejo Constitucional 2023", "Plebiscito de Salida 2023"], //order of bars
},
fy: {
label: null, // Custom label for fy (Edad)
grid: false,
padding: 0.2, //separation between graphs in y axis
domain : [">54 años", "34 a 54 años", "<34 años"] //order of the graphs top to bottom
},
fx: {
label: null,
grid: true,
padding: 0.2, //separation between graphs in x axis
},
color: {
domain: ["2da Vuelta Presidencial 2021", "Plebiscito de Salida 2022", "Consejo Constitucional 2023", "Plebiscito de Salida 2023"],
range: ["#BFCBF6", "#88A0F7", "#4E65C7", "#1D3479"], // Custom colors
legend: true,
legendColumns: 2,
style: { // Center legend and adjust size
display: "grid",
gridTemplateColumns: "repeat(2, auto)", // Two columns for the legend
justifyContent: "center",
alignItems: "center", // Ensure items are centered
fontSize: "20px", // Change the legend font size
fontFamily: "Hanken Grotesk",
marginBottom: "10px", // Adjust margin to prevent unwanted spacing
whiteSpace: "nowrap" // Prevent text from wrapping to the next line
}
},
facet: {
data: data,
x: "Género", //what is the x value to graph the different figures
y: "Edad", //etarian group to graph to to bottom
label: null
},
marks: [
Plot.axisFy({ dy:-80, dx: 10, anchor: "left", fontSize: 20, fontWeight : "bold", textAnchor: "start"}), //add title of Etarian Group on left graphs
Plot.axisFy({ dy:-80, dx: -372, anchor: "right", fontSize: 20, fontWeight : "bold", textAnchor: "start"}), //add title of Etarian Group on right graphs
Plot.axisFx({ dy: -50, fontSize: 24, fontWeight : "bold"}), //add title "Mujeres" "Hombres" to graph
Plot.barX(data, {
x: "Porcentaje",
y: "Votación",
fill: "Votación",
insetTop: 2.5, insetBottom: 2.5
}),
Plot.text(data, { //add percentage value to bars
x: "Porcentaje",
y: "Votación",
text: d => `${parseFloat(d["Porcentaje"])}%`,
textAnchor: "middle",
dx: 25, // Adjust the position of the text
fontSize: 16
}),
Plot.axisX({
label: null, // Custom label for the entire x-axis
tickValues: [0, 20, 40, 60, 80, 100], // Set specific tick values
tickFormat: d => `${d}%`, // Custom format for the tick labels
tickSize: 5, // Size of the ticks
tickPadding: 14, // Increase padding be1ween tick and tick label
fontSize: "16px", // Set the desired font size here
textAnchor : "middle",
fontFamily: "Hanken Grotesk",
tickColor: "#A1A1A1"
}),
],
style: {
fontFamily: "Hanken Grotesk" // Change font of the graph
}
});
}

Insert cell
Insert cell
createFigure(dataExample, "Tasa de participación", "Género y rango etario", "Fuente: Análisis de Unholster utilizando modelos propios y los resultados electorales proporcionados por el Servel.", plotBarGenreDistribution);
Insert cell
Insert cell
// Example usage with the provided data
dataExampleNulos = [
{Género: "Mujeres", Edad: ">54 años", Porcentaje: 5.6},
{Género: "Mujeres", Edad: "34 a 54 años", Porcentaje: 4.6},
{Género: "Mujeres", Edad: "<34 años", Porcentaje: 4.5},
{Género: "Hombres", Edad: ">54 años", Porcentaje: 6.3},
{Género: "Hombres", Edad: "34 a 54 años", Porcentaje: 4.8},
{Género: "Hombres", Edad: "<34 años", Porcentaje: 5.6}
];
Insert cell
function plotBarGenreNules(data) {
return Plot.plot({
label: null,
width: 900, // width
height: 630, // height
marginTop: 100,
x: { grid: true },
y: {
label: null,
axis: null,
},
fy: {
label: null, // Custom label for fy (Edad)
grid: false,
padding: 0.2,
domain : [">54 años", "34 a 54 años", "<34 años"]
},
fx: {
label: null, // Custom label for fx (Género)
grid: true,
padding: 0.2,
},
facet: {
data: data,
x: "Género",
y: "Edad",
label: null
},
marks: [
Plot.axisFy({ dy:-80, dx: 10, anchor: "left", fontSize: 20, fontWeight : "bold", textAnchor: "start"}),
Plot.axisFy({ dy:-80, dx: -372, anchor: "right", fontSize: 20, fontWeight : "bold", textAnchor: "start"}),
Plot.axisFx({ dy: -50, fontSize: 24, fontWeight : "bold"}),
Plot.barX(data, {
x: "Porcentaje",
insetTop: 2.5, insetBottom: 2.5,
fill: "#C5C5C5"
}),
Plot.text(data, {
x: "Porcentaje",
text: d => `${parseFloat(d["Porcentaje"])}%`,
textAnchor: "middle",
dx: 25, // Adjust the position of the text
fontSize: 13
}),
Plot.axisX({
label: null, // Custom label for the entire x-axis // Set specific tick values
tickFormat: d => `${d}%`, // Custom format for the tick labels
tickSize: 5, // Size of the ticks
tickPadding: 14, // Increase padding be1ween tick and tick label
fontSize: "16px", // Set the desired font size here
textAnchor : "middle",
fontFamily: "Hanken Grotesk",
tickColor: "#A1A1A1"
}),
],
style: {
fontFamily: "Hanken Grotesk" // Change font of the graph
}
});
}

Insert cell
createFigure(dataExampleNulos, "Nulos y blancos", "Género y rango etario", "Fuente: Análisis de Unholster utilizando modelos propios y los resultados electorales proporcionados por el Servel.", plotBarGenreNules);
Insert cell
Insert cell
// Example usage with the provided data
dataExampleBar = [
{Candidat: "Karla Rubilar", votos: 18155, partido: 'IND - RN', comuna: 'Puente Alto'},
{Candidat: "Daniel Reyes", votos: 13426, partido: 'IND - CHV', comuna: 'La Florida'},
{Candidat: "Felipe Alessandri", votos: 8938, partido: 'RN', comuna: 'Lo Barnechea'},
{Candidat: "Carlos Ward", votos: 6736, partido: 'UDI', comuna: 'Lo Barnechea'},
{Candidat: "Nicolas Hurtado", votos: 6547, partido: 'PC', comuna: 'La Florida'},
{Candidat: "Miguel Concha", votos: 6277, partido: 'RD', comuna: 'Peñalolen'},
{Candidat: "Luis Escanilla", votos: 5133, partido: 'PS', comuna: 'Puente Alto'}
];
Insert cell
function formatMiles(num) {
// Convertir el número en string y luego invertirlo
let str = num.toString().split('').reverse().join('');
// Insertar un punto cada tres dígitos
let result = '';
for (let i = 0; i < str.length; i++) {
if (i > 0 && i % 3 === 0) {
result += '.';
}
result += str[i];
}
// Invertir nuevamente para devolver el formato original
return result.split('').reverse().join('');
}

Insert cell
function plotCustomBarChart(data) {
// Ordenar los datos de mayor a menor según los votos
data.sort((a, b) => b.votos - a.votos);

return Plot.plot({
width: 980, // Ancho del gráfico
height: 650, // Altura del gráfico
x: {
label: null, // Sin etiqueta en el eje X
grid: false, // Eliminar las líneas de cuadrícula
tickFormat: formatMiles, // Aplicar separador de miles usando la función formatMiles
domain: [0, Math.max(...data.map(d => d.votos)) + 2000], // Añadir un margen al final del eje X
axis: false // Ocultar el eje X
},
y: {
label: null, // Sin etiqueta en el eje Y
domain: data.map(d => d.Candidat), // Usar los nombres de los candidatos como etiquetas
axis: false, // No mostrar el eje Y
},
marks: [
Plot.barX(data, {
x: "votos", // Tamaño de las barras según los votos
y: "Candidat", // Nombres de los candidatos en el eje Y
fill: d => d.color || "#4C6DE2", // Colores personalizados para cada barra
stroke: "white", // Añadir borde blanco a las barras para separarlas mejor
textAnchor: "start",
}),
Plot.text(data, {
x: 5, // Posicionar el texto al inicio de la barra, un poco desplazado
y: "Candidat",
// Mostrar el nombre del candidato y el partido en la primera línea, comuna en la segunda línea
text: d => `${d.Candidat} (${d.partido})\n\n${d.comuna}`,
dx: 5, // Ajustar la posición del texto a la derecha
fontSize: 14, // Ajustar el tamaño del texto
fill: "white", // Color del texto
textAnchor: "start",
fontWeight: "bold",
dy: 4, // Ajustar verticalmente el texto
}),
Plot.text(data, {
x: d => d.votos + 50, // Colocar el número de votos fuera de la barra
y: "Candidat",
// Usar la función formatMiles para mostrar los números con separador de miles
text: d => formatMiles(d.votos),
dx: 5, // Ajustar la posición del texto a la derecha del final de la barra
fontSize: 16, // Tamaño del texto
fill: "black", // Color del texto
textAnchor: "start",
dy: 4, // Ajustar verticalmente el texto
})
],
style: {
fontFamily: "Hanken Grotesk", // Apply to the entire plot
fontSize: "14px" // Cambiar el tamaño de fuente general
}
});
}

Insert cell
createFigure(dataExampleBar, "Más votados", "Primarias 2024", "Fuente: Análisis de Unholster utilizando modelos propios y los resultados electorales proporcionados por el Servel.", plotCustomBarChart);
Insert cell
Insert cell
Insert cell
createFigure(dataParticipacion, "Titulo", "subtitulo", "Fuente: Este gráfico se ha elaborado con datos proporcionados por el Servicio Electoral.", plotBarGraph);
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