Public
Edited
Jul 15, 2023
Insert cell
Insert cell
// load data
olympians = d3.csv("https://raw.githubusercontent.com/flother/rio2016/master/athletes.csv", d3.autoType)
viewof table = Inputs.table(olympians)


data = d3.csv("https://data.seattle.gov/api/views/76t5-zqzr/rows.csv")

import {lasers} from "@observablehq/faa-laser-reports-data"


Insert cell
{
// To group the athletes by sport
athletesBySport = d3.group(athletes, d => d.sport);
athletesBySport.get("Soccer");

// to compute the total earnings per sport for these athletes
d3.rollup(athletes, v => d3.sum(v, d => d.earnings), d => d.sport)

// the athlete earnings sorted by least to greatest
d3.groupSort(athletes, g => d3.median(g, d => d.earnings), d => d.earnings)

// d3.index creates a Map from key to unique value. Given several keys, it returns a nested Map
index = d3.index(athletes, d => d.name)

// Index is very powerful for joining two datasets
facts.map(({about: name, fact}) => ({fact, name, ...index.get(name)}))

// d3.merge flattens the specified iterable-of-iterables into a new array
// similar to the built-in array.concat and array.flat but can be used to flatten nested iterables as well as plain (untyped) arrays
d3.merge([[1], [2, 3]])

d3.count(flights, d => d.price)
d3.sum([1, 2, 3, -0.5])
d3.mean(olympic_athletes, d => d.height)
d3.median([0, 1, 2, 5])
d3.variance(olympic_athletes, d => d.height)
d3.deviation(olympic_athletes, d => d.height)
d3.quantile(olympic_athletes, 0.05, d => d.height)
d3.cumsum([1, 2, 3, 4])
d3.rank([23, 2, -1, 4])


// d3.bin groups data points into buckets of (usually) equal widths along a comparison axis.
values1 = distribution("Uniform") // 1000 values distributed with d3.randomUniform
draw_values(this || DOM.svg(width, 100), values1)
draw_buckets(bin1, values1)
}


Insert cell
{
randomDistributions = {
const r20 = d3.randomNormal(max * 0.3, 2),
r21 = d3.randomNormal(max * 0.75, 1),
r4 = d3.randomExponential(0.3);
return [
{ label: "Uniform", random: d3.randomUniform(0.5, max - 0.3) },
{ label: "Normal", random: d3.randomNormal(max / 2, (1.4 * max) / 18) },
{ label: "Two blobs", random: () => (Math.random() > 0.3 ? r20() : r21()) },
{ label: "Skewed right", random: d3.randomLogNormal() },
{ label: "Skewed left", random: () => 18 - r4() }
];
}
}
Insert cell
function draw_values(svg, values) {
// We sort the data for meaningful transitions.
const current = values.slice().sort();

const prev = (svg && svg.values) || current;

svg.values = current;

d3.select(svg)
.selectAll("circle")
.data(current)
.join("circle")
.attr("r", 2)
.attr("stroke", "#588")
.attr("fill", "lightblue")
.attr("fill-opacity", 0.3)
.attr("cx", (_, i) => x(prev[i]))
.attr("cy", (_, i) => y(i))
.transition()
.duration(500)
.attr("cx", x);

return svg;
}

Insert cell
function draw_buckets(bin, values) {
const svg = this || DOM.svg(width, 100);

const buckets = bin(values);

const binColor = d3
.scaleThreshold()
.domain(buckets.map(d => d.x0))
.range(colors);

d3.select(svg)
.selectAll("rect")
.data(buckets)
.join("rect")
.attr("y", d => 10)
.attr("height", 100 - 2 * 10)
.attr("x", d => (x(d.x0) + 1) | 0)
.attr("width", d => (x(d.x1) | 0) - (x(d.x0) | 0) - 2)
.attr("stroke", d => binColor(d.x0))
.attr("stroke-width", 1)
.attr("stroke-dasharray", d => (d.length === 0 ? "1 5" : null))
.attr("fill", "none");

draw_values(svg, values);

d3.select(svg)
.selectAll("circle")
.attr("fill", binColor)
.attr("stroke", binColor);

d3.select(svg)
.selectAll("text")
.data(buckets.filter(d => d.length > 0))
.join("text")
.attr("x", d => (x(d.x0) + 3) | 0)
.attr("y", 86)
.attr("fill", "black")
.attr("font-size", 9)
.text(d =>
x(d.x1) - x(d.x0) < 50
? d.length
: d.length > 1
? `${d.length} items`
: d.length === 1
? "1 item"
: "empty bin"
);

return svg;
}

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