Public
Edited
May 2
2 forks
Insert cell
Insert cell
Insert cell
{
const svg = d3.create('svg').attr('width',100).attr('height',100);
svg.append('circle')
.attr('cx',50)
.attr('cy',50)
.attr('r',25)
.attr('fill','green')
.on('mouseenter',function(){
d3.select(this).attr('fill','red');
})
.on('mouseleave',function(){
d3.select(this).attr('fill','green');
})

return svg.node();
}
Insert cell
{
const svg = d3.create('svg')
.attr('width', 100)
.attr('height', 100);
svg.append('circle')
.attr('cx', 50)
.attr('cy', 50)
.attr('r', 25)
.attr('fill', 'red')
// we can pass a function directly
.on('mouseenter', function() {
// this refers to the circle
// select it and change its attributes
d3.select(this)
.attr('r', 50)
.attr('fill', 'blue');
})
// or separate the function out and pass its name
.on('mouseleave', mouseLeave);
function mouseLeave() {
d3.select(this)
.attr('r', 25)
.attr('fill', 'red');
}
return svg.node();
}
Insert cell
Insert cell
swatches({ color: carColor })
Insert cell
Insert cell
Insert cell
clickCount
Insert cell
// usando viewof antes del nombre de la celda
viewof clickCount = {
const svg = d3.create('svg')
.attr('width', 100)
.attr('height', 100);

let count = 0;

svg.append('circle')
.attr('cx', 50)
.attr('cy', 50)
.attr('r', 25)
.attr('fill', 'green')
.on('click', function() {
count += 1;
// actualizamos la propiedad value con el nuevo conteo
svg.property('value', count);
// lanzamos un evento 'input' para avisar a Observable
// que hay un nuevo valor para clickCount
svg.dispatch('input');
});

// establecemos el valor inicial en cero y disparamos el evento
svg.property('value', 0).dispatch('input');

return svg.node();
}

Insert cell
Insert cell
Insert cell
Insert cell
{
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
svg.append("g").call(xAxis, x, 'horsehpower');
svg.append("g").call(yAxis, y, 'weight (lbs)');
svg.selectAll('circle')
// filter data to only contain selected car origins
.data(carData.filter(d => selections.get(d.origin)))
.join('circle')
.attr('cx', d => x(d.horsepower))
.attr('cy', d => y(d.weight))
.attr('fill', d => carColor(d.origin))
.attr('opacity', 1)
.attr('r', 3);
return svg.node();
}
Insert cell
{
const svg = d3.create('svg')
.attr('width', width + 80) // espacio para la leyenda
.attr('height', height);

// ejes
svg.append("g").call(xAxis, x, 'horsehpower');
svg.append("g").call(yAxis, y, 'weight (lbs)');

// dibujar los puntos
function drawPoints(data) {
svg.selectAll('circle')
.data(data)
.join('circle')
.attr('cx', d => x(d.horsepower))
.attr('cy', d => y(d.weight))
.attr('fill', d => carColor(d.origin))
.attr('r', 3);
}

drawPoints(carData); // dibujamos los puntos por primera vez

// dibujar la leyenda

const legend = svg.append('g')
.attr('transform', `translate(${width - margin.right},${margin.top})`);

const rows = legend.selectAll('g')
.data(origins)
.join('g')
.attr('transform', (d, i) => `translate(20, ${i * 20})`);

// agregar un cuadrado de color por cada categoría
rows.append('rect')
.attr('width', 15)
.attr('height', 15)
.attr('stroke-width', 2)
.attr('stroke', d => carColor(d))
.attr('fill', d => carColor(d))
.on('click', onclick); // activar filtro al hacer clic

// agregar texto al lado de cada cuadrado
rows.append('text')
.attr('font-size', 15)
.attr('x', 20)
.attr('y', 7.5)
.attr('font-family', 'sans-serif')
.attr('dominant-baseline', 'middle')
.text(d => d);

// llevar el control de qué orígenes están seleccionados
const selected = new Map(origins.map(d => [d, true]));

function onclick(event, d) {
const isSelected = selected.get(d);

// seleccionamos el cuadrado y cambiamos su color según estado
const square = d3.select(this);
square.attr('fill', d => isSelected ? 'white' : carColor(d));

// actualizamos el estado del origen clickeado
selected.set(d, !isSelected);

// volvemos a dibujar los puntos, filtrando según lo seleccionado
drawPoints(carData.filter(d => selected.get(d.origin)));
}

return svg.node();
}

Insert cell
Insert cell
cars
Insert cell
viewof cars = brushableScatterplot()
Insert cell
function brushableScatterplot() {
// configuración inicial

// valor por defecto cuando no hay selección (brush)
const initialValue = carData;

const svg = d3.create('svg')
.attr('width', width)
.attr('height', height)
.property('value', initialValue); // valor inicial asociado al SVG

// ejes
svg.append("g").call(xAxis, x, 'horsehpower');
svg.append("g").call(yAxis, y, 'weight (lbs)');

// dibujar los puntos
const radius = 3;

const dots = svg.selectAll('circle')
.data(carData)
.join('circle')
.attr('cx', d => x(d.horsepower))
.attr('cy', d => y(d.weight))
.attr('fill', d => carColor(d.origin))
.attr('opacity', 1)
.attr('r', radius);

// ********** lo nuevo empieza aquí **********

const brush = d3.brush()
// definimos el área donde se puede hacer brush
.extent([[margin.left, margin.top], [width - margin.right, height - margin.bottom]])
// manejamos los eventos de selección
.on('brush', onBrush)
.on('end', onEnd);

// agregamos la herramienta de selección al SVG
svg.append('g')
.call(brush);

function onBrush(event) {
// event.selection nos da las coordenadas de la caja de selección:
// [arribaIzquierda, abajoDerecha]
const [[x1, y1], [x2, y2]] = event.selection;

// esta función devuelve true si el punto está dentro de la caja de selección
function isBrushed(d) {
const cx = x(d.horsepower);
const cy = y(d.weight);
return cx >= x1 && cx <= x2 && cy >= y1 && cy <= y2;
}

// cambiamos el color de los puntos: grises los no seleccionados
dots.attr('fill', d => isBrushed(d) ? carColor(d.origin) : 'gray');

// actualizamos los datos seleccionados en la variable cars
svg.property('value', carData.filter(isBrushed)).dispatch('input');
}

function onEnd(event) {
// si se borra la selección (brush)
if (event.selection === null) {
// restauramos el color original de todos los puntos
dots.attr('fill', d => carColor(d.origin));
svg.property('value', initialValue).dispatch('input');
}
}

return svg.node();
}

Insert cell
Insert cell
columns = ['horsepower', 'weight', 'displacement', 'acceleration', 'mpg']
Insert cell
Insert cell
Insert cell
swatches({ color: carColor })
Insert cell
{
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);

// escalas

const xScale = d3.scaleLinear()
.domain(d3.extent(carData, d => d[xFeature])).nice()
.range([margin.left, width - margin.right]);

const yScale = d3.scaleLinear()
.domain(d3.extent(carData, d => d[yFeature])).nice()
.range([height - margin.bottom, margin.top])
// ejes
svg.append("g").call(xAxis, xScale, xFeature);
svg.append("g").call(yAxis, yScale, yFeature);

// circulos
const dots = svg.selectAll('circle')
.data(carData)
.join('circle')
.attr('cx', d => xScale(d[xFeature]))
.attr('cy', d => yScale(d[yFeature]))
.attr('fill', d => carColor(d.origin))
.attr('opacity', 1)
.attr('r', 3);
return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
carsScatterplot = {
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
const xScale = d3.scaleLinear()
.range([margin.left, width - margin.right]);

const yScale = d3.scaleLinear()
.range([height - margin.bottom, margin.top]);

// ejes
const axesGroup = svg.append('g');
let xAxisGroup = axesGroup.append('g');
let yAxisGroup = axesGroup.append('g');

const dots = svg.selectAll('circle')
.data(carData)
.join('circle')
.attr('fill', d => carColor(d.origin))
.attr('r', 3);

// Update
function update(xFeat, yFeat) {
// transition

const t = svg.transition()
.duration(1500);

// update the scales

xScale.domain(d3.extent(carData, d => d[xFeat])).nice();
yScale.domain(d3.extent(carData, d => d[yFeat])).nice();


// fade-out the old axis
xAxisGroup.transition(t)
.attr('opacity', 0)
.remove();

// add the new axis
xAxisGroup = axesGroup.append('g')
// make it invisible
.attr('opacity', 0)
.call(xAxis, xScale, xFeat);

// fade-in the new axis
xAxisGroup.transition(t)
.attr('opacity', 1);

// y axis

// fade-out the old axis
yAxisGroup.transition(t)
.attr('opacity', 0)
.remove();

// add the new axis
yAxisGroup = axesGroup.append('g')
// make it invisible
.attr('opacity', 0)
.call(yAxis, yScale, yFeat);

// fade-in the new axis
yAxisGroup.transition(t)
.attr('opacity', 1);

// update the circles

dots.transition(t)
.attr('cx', d => xScale(d[xFeat]))
.attr('cy', d => yScale(d[yFeat]));
}

// add the update function to the svg.node() object
// so that we can call it outside of this cell
return Object.assign(svg.node(), { update });
}
Insert cell
carsScatterplot.update(xFeat, yFeat)
Insert cell
Insert cell
{
const scatter = brushableScatterplot();
const bar = barChart();

// update the bar chart when the scatterplot
// selection changes
d3.select(scatter).on('input', () => {
bar.update(scatter.value);
});

// intial state of bar chart
bar.update(scatter.value);

// use HTML to place the two charts next to each other
return html`<div style="display: flex">${scatter}${bar}</div>`;
}
Insert cell
function barChart() {
// set up
const margin = {top: 10, right: 20, bottom: 50, left: 50};
const width = 300 + margin.left + margin.right;
const height = 200 + margin.top + margin.bottom;

const svg = d3.create('svg')
.attr('width', width)
.attr('height', height + margin.top + margin.bottom);
// create scales
const x = d3.scaleLinear()
.range([margin.left, width - margin.right]);
const y = d3.scaleBand()
.domain(carColor.domain())
.range([margin.top, height - margin.bottom])
.padding(0.2);
// create and add axes
const xAxis = d3.axisBottom(x).tickSizeOuter(0);
const xAxisGroup = svg.append("g")
.attr("transform", `translate(0, ${height - margin.bottom})`);
xAxisGroup.append("text")
.attr('x', margin.left + (width - margin.left - margin.right) / 2)
.attr("y", 40)
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("Count");
const yAxis = d3.axisLeft(y);
const yAxisGroup = svg.append("g")
.attr("transform", `translate(${margin.left})`)
.call(yAxis)
// remove baseline from the axis
.call(g => g.select(".domain").remove());
let barsGroup = svg.append("g");

function update(data) {
// get the number of cars for each origin
const originCounts = d3.rollup(
data,
group => group.length,
d => d.origin
);

// update x scale
x.domain([0, d3.max(originCounts.values())]).nice()

// update x axis

const t = svg.transition()
.ease(d3.easeLinear)
.duration(200);

xAxisGroup
.transition(t)
.call(xAxis);
// draw bars
barsGroup.selectAll("rect")
.data(originCounts, ([origin, count]) => origin)
.join("rect")
.attr("fill", ([origin, count]) => carColor(origin))
.attr("height", y.bandwidth())
.attr("x", x(0))
.attr("y", ([origin, count]) => y(origin))
.transition(t)
.attr("width", ([origin, count]) => x(count) - x(0))
}
return Object.assign(svg.node(), { update });;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
datasets = require('vega-datasets@1')
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