Public
Edited
Apr 30
Fork of Interacción
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
selections
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
margin = ({top: 10, right: 20, bottom: 50, left: 105})
Insert cell
width = 400 + margin.left + margin.right
Insert cell
height = 400 + margin.top + margin.bottom
Insert cell
carColor = d3.scaleOrdinal()
.domain(origins)
.range(d3.schemeCategory10);
Insert cell
x = d3.scaleLinear()
.domain(d3.extent(carData, d => d.horsepower)).nice()
.range([margin.left, width - margin.right])
Insert cell
y = d3.scaleLinear()
.domain(d3.extent(carData, d => d.weight)).nice()
.range([height - margin.bottom, margin.top])
Insert cell
xAxis = (g, scale, label) =>
g.attr('transform', `translate(0, ${height - margin.bottom})`)
// add axis
.call(d3.axisBottom(scale))
// remove baseline
.call(g => g.select('.domain').remove())
// add grid lines
// references https://observablehq.com/@d3/connected-scatterplot
.call(g => g.selectAll('.tick line')
.clone()
.attr('stroke', '#d3d3d3')
.attr('y1', -(height - margin.top - margin.bottom))
.attr('y2', 0))
// add label
.append('text')
.attr('x', margin.left + (width - margin.left - margin.right) / 2)
.attr('y', 40)
.attr('fill', 'black')
.attr('text-anchor', 'middle')
.text(label)
Insert cell
yAxis = (g, scale, label) =>
// add axis
g.attr('transform', `translate(${margin.left})`)
.call(d3.axisLeft(scale))
// remove baseline
.call(g => g.select('.domain').remove())
// add grid lines
// refernces https://observablehq.com/@d3/connected-scatterplot
.call(g => g.selectAll('.tick line')
.clone()
.attr('stroke', '#d3d3d3')
.attr('x1', 0)
.attr('x2', width - margin.left - margin.right))
// add label
.append('text')
.attr('x', -40)
.attr('y', margin.top + (height - margin.top - margin.bottom) / 2)
.attr('fill', 'black')
.attr('dominant-baseline', 'middle')
.text(label)
Insert cell
carData = (await datasets['cars.json']()).map(d => ({
horsepower: d['Horsepower'],
weight: d['Weight_in_lbs'],
origin: d['Origin'],
displacement: d['Displacement'],
acceleration: d['Acceleration'],
mpg: d['Miles_per_Gallon'],
name: d['Name']
})).filter(d => Object.values(d).every(d => d !== null))
Insert cell
origins = Array.from(new Set(carData.map(d => d.origin)))
Insert cell
Insert cell
d3 = require('d3@7')
Insert cell
import {legend, swatches} from "@d3/color-legend"
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