Published unlisted
Edited
Nov 18, 2021
1 star
Insert cell
# Learning a Little D3

Insert cell
{
const svg = d3.create("svg");
svg.attr("height", 250);
svg.attr("width", 250);
yield svg.node();
svg
.selectAll("circle")
.data(myData)
.enter()
.append("circle")
.attr("cx", 50)
.attr("cy", 50)
.attr("r", 10)
.style("fill", "red")
.transition()
.delay((d, i) => i * 1000)
.duration(1000)
.attr("cx", (d, i) => d.x)
.attr("cy", (d, i) => d.y)
.style("fill", "blue");
}
Insert cell
{
const svg = d3.create("svg");
yield svg.node();
svg
.append("g")
.attr("id", "g1")
.selectAll("circle")
.data(myData)
.enter()
.append("circle")
.attr("cx", (d, i) => d.x)
.attr("cy", (d, i) => d.y)
.attr("r", 10)
.style("fill", "blue");
svg
.append("g")
.attr("id", "g2")
.selectAll("circle")
.data(myData)
.enter()
.append("circle")
.attr("cx", (d, i) => d.x)
.attr("cy", (d, i) => d.y)
.attr("r", 10)
.style("fill", "blue");
await Promises.delay(2500);
svg
.selectAll("g#g2")
.transition()
.duration(1500)
.attr("transform", "translate(30, 30)");
}
Insert cell
{
const svg = d3.create("svg").style("border", "1px solid black");
const sWidth = 300;
const sHeight = 300;
const padding = 30;
svg.attr("height", sHeight).attr("width", sWidth);
const xScale = d3
.scaleLinear()
.domain(d3.extent(myData, (d) => d.x))
.range([padding, sWidth - padding]);
const yScale = d3
.scaleLinear()
.domain(d3.extent(myData, (d) => d.y))
.range([sHeight - padding, padding]);
yield svg.node();
svg
.append("g")
.attr("id", "circleGroup")
.selectAll("circle")
.data(myData)
.enter()
.append("circle")
.attr("cx", (d, i) => xScale(d.x))
.attr("cy", (d, i) => yScale(d.y))
.attr("r", 10)
.style("fill", "blue");
svg
.append("g")
.call(d3.axisBottom(xScale))
.attr("transform", "translate(0, " + (sHeight - padding) + ")");
svg
.append("g")
.call(d3.axisLeft(yScale))
.attr("transform", "translate(" + padding + ",0)");
}
Insert cell
d3.extent(myData, (d) => d.x)
Insert cell
{
const scale = d3.scaleLinear().domain([0.0, 1.0]).range([100, 200]);
return scale(0.5);
}
Insert cell
{
const sWidth = 400;
const sHeight = 200;
const padding = 30;
const svg = d3.create("svg").attr("height", sHeight).attr("width", sWidth);
yield svg.node();
const xScale = d3
.scaleBand()
.domain(myData.map((d) => d.name))
.range([padding, sWidth - padding])
.padding(0.1);
const yScale = d3
.scaleLinear()
.domain(d3.extent(myData, (d) => d.y))
.range([sHeight - padding, padding]);
svg
.selectAll("rect")
.data(myData)
.enter()
.append("rect")
.attr("x", (d) => xScale(d.name))
.attr("y", (d) => yScale(d.y))
.attr("height", (d) => yScale(0) - yScale(d.y))
.attr("width", xScale.bandwidth())
.style("fill", "black");
svg
.append("g")
.attr("id", "bandScale")
.call(d3.axisBottom(xScale))
.attr("transform", "translate(0," + (sHeight - padding) + ")");
}
Insert cell
myData = [
{ x: 100, y: 50, name: "Harry Potter" },
{ x: 10, y: 35, name: "Hermione" },
{ x: 50, y: 10, name: "Tom Riddle" }
]
Insert cell
d3 = require("d3@6")
Insert cell
{
const svg = d3.create("svg").attr("width", 300).attr("height", 300);
yield svg.node();
var xScale = d3
.scaleLinear()
.range([20, 280])
.domain(d3.extent(dataset, (d) => d.x1));
var yScale = d3
.scaleLinear()
.range([280, 20])
.domain(d3.extent(dataset, (d) => d.y1));
svg
.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", (d) => xScale(d.x1))
.attr("cy", (d) => yScale(d.y1))
.attr("r", (d) => d.radius)
.style("fill", "black");
svg
.selectAll("circle")
.data(dataset)
.on("click", (event, d) => {
d3.select(event.target).transition().duration(3000).style("fill", "red");
});
}
Insert cell
d3.extent(near_earth, (d) => d.discovery_date)
Insert cell
["moid_au", "q_au_1", "q_au_2", "i_deg"].map((d) =>
d3.extent(near_earth, (d2) => parseFloat(d2[d]))
)
Insert cell
<div id="selection"></div>
Insert cell
{
function brushed(event) {
console.log(event.selection);
d3.select("#selection").text(event.selection);
}

const myWidth = 512;
const myHeight = 512;

const svg = d3.create("svg").attr("width", myWidth).attr("height", myHeight);
yield svg.node();

var brush = d3
.brush()
.extent([
[0.0, 0.0],
[512, 512]
])
.on("brush", brushed);
svg.append("g").attr("class", "brush").call(brush);
}
Insert cell
{
const svg = d3.create("svg").style("border", "1px solid black");
const sWidth = 300;
const sHeight = 300;
const padding = 30;
svg.attr("height", sHeight).attr("width", sWidth);
const xScale = d3
.scaleLog()
.domain(d3.extent(near_earth, (d) => parseFloat(d.q_au_1)))
.range([padding, sWidth - padding]);
const yScale = d3
.scaleLog()
.domain(d3.extent(near_earth, (d) => parseFloat(d.q_au_2)))
.range([sHeight - padding, padding]);
yield svg.node();
var myCircles = svg
.append("g")
.attr("id", "circleGroup")
.selectAll("circle")
.data(near_earth)
.enter()
.append("circle")
.attr("cx", (d, i) => xScale(parseFloat(d.q_au_1)))
.attr("cy", (d, i) => yScale(parseFloat(d.q_au_2)))
.attr("r", 5)
.style("fill", "blue");
svg
.append("g")
.call(d3.axisBottom(xScale))
.attr("transform", "translate(0, " + (sHeight - padding) + ")");
svg
.append("g")
.call(d3.axisLeft(yScale))
.attr("transform", "translate(" + padding + ",0)");
var brush = d3
.brush()
.extent([
[padding, padding],
[sWidth - padding, sHeight - padding]
])
.on("brush", (event) => {
const newBoundsX = [
xScale.invert(event.selection[0][0]),
xScale.invert(event.selection[1][0])
];
const newBoundsY = [
yScale.invert(event.selection[1][1]),
yScale.invert(event.selection[0][1])
];
myCircles.style("fill", "blue");
myCircles
.filter(
(d) =>
parseFloat(d.q_au_1) >= newBoundsX[0] &&
parseFloat(d.q_au_1) <= newBoundsX[1] &&
parseFloat(d.q_au_2) >= newBoundsY[0] &&
parseFloat(d.q_au_2) <= newBoundsY[1]
)
.transition()
.duration(0)
.style("fill", "black");
});
svg.append("g").attr("class", "brush").call(brush);
}
Insert cell
<style>
.baseCircle { fill: blue; }
.highlighted { fill: black; }
</style>
Insert cell
{
const svg = d3.create("svg").style("border", "1px solid black");
const sWidth = 300;
const sHeight = 300;
const padding = 30;
svg.attr("height", sHeight).attr("width", sWidth);
const xScale = d3
.scaleLog()
.domain(d3.extent(near_earth, (d) => parseFloat(d.q_au_1)))
.range([padding, sWidth - padding]);
const yScale = d3
.scaleLog()
.domain(d3.extent(near_earth, (d) => parseFloat(d.q_au_2)))
.range([sHeight - padding, padding]);
yield svg.node();
var myCircles = svg
.append("g")
.attr("id", "circleGroup")
.selectAll("circle")
.data(near_earth)
.enter()
.append("circle")
.attr("cx", (d, i) => xScale(parseFloat(d.q_au_1)))
.attr("cy", (d, i) => yScale(parseFloat(d.q_au_2)))
.classed("baseCircle", true)
.attr("r", 5);
svg
.append("g")
.call(d3.axisBottom(xScale))
.attr("transform", "translate(0, " + (sHeight - padding) + ")");
svg
.append("g")
.call(d3.axisLeft(yScale))
.attr("transform", "translate(" + padding + ",0)");
var brush = d3
.brush()
.extent([
[padding, padding],
[sWidth - padding, sHeight - padding]
])
.on("brush", (event) => {
const newBoundsX = [
xScale.invert(event.selection[0][0]),
xScale.invert(event.selection[1][0])
];
const newBoundsY = [
yScale.invert(event.selection[1][1]),
yScale.invert(event.selection[0][1])
];
myCircles.classed(
"highlighted",
(d) =>
parseFloat(d.q_au_1) >= newBoundsX[0] &&
parseFloat(d.q_au_1) <= newBoundsX[1] &&
parseFloat(d.q_au_2) >= newBoundsY[0] &&
parseFloat(d.q_au_2) <= newBoundsY[1]
);
});
svg.append("g").attr("class", "brush").call(brush);
}
Insert cell
{
const points = d3.range(256).map((d) => {
return { x: d / 16, y: Math.sin(d / 16) };
});
const myHeight = 300;
const myWidth = 300;
const padding = 30;
const svg = d3.create("svg").attr("height", myHeight).attr("width", myWidth);
var xScale = d3
.scaleLinear()
.domain(d3.extent(points, (d) => d.x))
.range([padding, myWidth - padding]);
var yScale = d3
.scaleLinear()
.domain(d3.extent(points, (d) => d.y))
.range([myHeight - padding, padding]);
var line = d3
.line()
.x((d) => xScale(d.x))
.y((d) => yScale(d.y));
svg
.selectAll("circle")
.data(points)
.enter()
.append("circle")
.attr("cx", (d) => xScale(d.x))
.attr("cy", (d) => yScale(d.y))
.style("fill", "black")
.attr("r", 1.5);
svg
.append("path")
.attr("d", line(points))
.style("fill", "none")
.style("stroke", "black");
yield svg.node();
}
Insert cell
{
const binnedValues = d3.bin().value((d) => parseFloat(d.moid_au))(near_earth);
const myWidth = 300;
const myHeight = 300;
const padding = 20;
const svg = d3.create("svg").attr("height", myHeight).attr("width", myWidth);
yield svg.node();
var xScale = d3
.scaleBand()
.range([padding, myWidth - padding])
.domain(d3.range(binnedValues.length))
.padding(0.15);
var yScale = d3
.scaleLinear()
.range([myHeight - padding, padding])
.domain([0, d3.max(binnedValues, (d) => d.length)]);
svg
.selectAll("rect")
.data(binnedValues)
.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", (d, i) => yScale(d.length))
.attr("width", xScale.bandwidth)
.attr("height", (d, i) => yScale(0) - yScale(d.length));
}
Insert cell
{
const amor = d3.group(near_earth, (d) => d.orbit_class).get("Amor");
const myWidth = 400;
const myHeight = 400;
const padding = 40;
const svg = d3.create("svg").attr("width", myWidth).attr("height", myHeight);
yield svg.node();
const maxQ1 = d3.max(amor, (d) => parseFloat(d.q_au_1));
const maxQ2 = d3.max(amor, (d) => parseFloat(d.q_au_2));
var xScale = d3
.scaleLinear()
.domain([-maxQ1, maxQ1])
.range([padding, myWidth - padding]);
var yScale = d3
.scaleLinear()
.domain([-maxQ2, maxQ2])
.range([myHeight - padding, padding]);
svg
.append("g")
.attr("class", "orbits")
.selectAll("ellipse")
.data(amor)
.enter()
.append("ellipse")
.attr("cx", myWidth / 2)
.attr("cy", myHeight / 2)
.attr("rx", (d) => xScale(parseFloat(d.q_au_1)) / 2)
.attr("ry", (d) => yScale(parseFloat(d.q_au_2)) / 2)
.style("fill", "none")
.style("stroke", "black")
.on("mouseover", (event, d) => {
console.log(d);
});
}
Insert cell
near_earth = d3.json("https://data.nasa.gov/resource/2vr3-k9wn.json")
Insert cell
Insert cell
dataset = [
{ x1: 1.0, y1: 3.0, x2: 3.5, y2: 2.0, radius: 10 },
{ x1: 2.5, y1: 2.0, x2: 0.75, y2: 3.5, radius: 5 },
{ x1: 3.1, y1: 0.6, x2: 4.1, y2: 3.4, radius: 25 }
]
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more