Public
Edited
Apr 7, 2023
Importers
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
distChart = {
const predicates = {
dist: (d) => true,
hour: (d) => true,
date: (d) => true,
delay: (d) => true
};

function getSpec(data) {
return {
height: height,
y: {
grid: true,
tickFormat: "~s"
},
x: {
label: "Distance (mi.) →",
domain: distScaleDomain
},
marks: [
Plot.rectY(
data,
Plot.binX(
{ y: "count" },
{
x: "distance"
}
)
),
Plot.ruleY([0])
]
};
}

let chart = Plot.plot(getSpec(data));

const x = chart.scale("x"),
[x1, x2] = x.range;
const y = chart.scale("y"),
[y1, y2] = y.range;

let domain = x.domain;

const wrapper = svg`<svg viewBox="${chart.getAttribute("viewBox")}">${chart}`;

const redraw = function () {
chart.replaceWith(
(chart = Plot.plot(
getSpec(
data.filter((d) => combinePredicates(...Object.values(predicates))(d))
)
))
);
};

const brush = d3
.brushX()
.extent([
[x1, y2],
[x2, y1]
])
.on("brush end", (event) => {
const { selection, sourceEvent } = event;
domain = selection && selection.map(x.invert);
if (sourceEvent) dispatch.call("distBrush", wrapper, domain);
});

d3.select(wrapper).call(brush);

dispatch.on("hourBrush.dist", function (domain) {
predicates.hour =
domain === null ? (d) => true : (d) => inDomain(hour(d), domain);
redraw();
});

dispatch.on("delayBrush.dist", function (domain) {
predicates.delay =
domain === null ? (d) => true : (d) => inDomain(delay(d), domain);
redraw();
});

dispatch.on("dateBrush.dist", function (domain) {
predicates.date =
domain === null ? (d) => true : (d) => inDomain(d.date, domain);
redraw();
});
return wrapper;
}
Insert cell
hourChart = {
const predicates = {
dist: (d) => true,
hour: (d) => true,
date: (d) => true,
delay: (d) => true
};

function getSpec(data) {
return {
height: height,
y: {
grid: true,
tickFormat: "~s"
},
x: {
label: "Hour of the Day →",
domain: hourScaleDomain
},
marks: [
Plot.rectY(
data,
Plot.binX(
{ y: "count" },
{
x: (d) => hour(d)
}
)
),
Plot.ruleY([0])
]
};
}

let chart = Plot.plot(getSpec(data));

const x = chart.scale("x"),
[x1, x2] = x.range;
const y = chart.scale("y"),
[y1, y2] = y.range;

let domain = x.domain;

const wrapper = svg`<svg viewBox="${chart.getAttribute("viewBox")}">${chart}`;

const redraw = function () {
chart.replaceWith(
(chart = Plot.plot(
getSpec(
data.filter((d) => combinePredicates(...Object.values(predicates))(d))
)
))
);
};

const brush = d3
.brushX()
.extent([
[x1, y2],
[x2, y1]
])
.on("brush end", (event) => {
const { selection, sourceEvent } = event;
domain = selection && selection.map(x.invert);
if (sourceEvent) dispatch.call("hourBrush", wrapper, domain);
});

d3.select(wrapper).call(brush);

dispatch.on("distBrush.hour", function (domain) {
predicates.dist =
domain === null ? (d) => true : (d) => inDomain(d.distance, domain);
redraw();
});

dispatch.on("delayBrush.hour", function (domain) {
predicates.delay =
domain === null ? (d) => true : (d) => inDomain(delay(d), domain);
redraw();
});

dispatch.on("dateBrush.hour", function (domain) {
predicates.date =
domain === null ? (d) => true : (d) => inDomain(d.date, domain);
redraw();
});

return wrapper;
}
Insert cell
delayChart = {
const predicates = {
dist: (d) => true,
hour: (d) => true,
date: (d) => true,
delay: (d) => true
};

function getSpec(data) {
return {
height: height,
y: {
grid: true,
tickFormat: "~s"
},
x: {
label: "Arrival delay (min) →",
domain: delayScaleDomain
},
marks: [
Plot.rectY(
data,
Plot.binX(
{ y: "count" },
{
x: (d) => delay(d),
thresholds: 80
}
)
),
Plot.ruleY([0])
]
};
}

let chart = Plot.plot(getSpec(data));

const x = chart.scale("x"),
[x1, x2] = x.range;
const y = chart.scale("y"),
[y1, y2] = y.range;

let domain = x.domain;

const wrapper = svg`<svg viewBox="${chart.getAttribute("viewBox")}">${chart}`;

const redraw = function () {
chart.replaceWith(
(chart = Plot.plot(
getSpec(
data.filter((d) => combinePredicates(...Object.values(predicates))(d))
)
))
);
};

const brush = d3
.brushX()
.extent([
[x1, y2],
[x2, y1]
])
.on("brush end", (event) => {
const { selection, sourceEvent } = event;
domain = selection && selection.map(x.invert);
if (sourceEvent) dispatch.call("delayBrush", wrapper, domain);
});

d3.select(wrapper).call(brush);

dispatch.on("distBrush.delay", function (domain) {
predicates.dist =
domain === null ? (d) => true : (d) => inDomain(d.distance, domain);
redraw();
});

dispatch.on("hourBrush.delay", function (domain) {
predicates.hour =
domain === null ? (d) => true : (d) => inDomain(hour(d), domain);
redraw();
});

dispatch.on("dateBrush.delay", function (domain) {
predicates.date =
domain === null ? (d) => true : (d) => inDomain(d.date, domain);
redraw();
});

return wrapper;
}
Insert cell
dateChart = {
const predicates = {
dist: (d) => true,
hour: (d) => true,
date: (d) => true,
delay: (d) => true
};

function getSpec(data) {
return {
height: height,
y: {
grid: true,
tickFormat: "~s"
},
x: {
label: "Date →",
type: "time",
domain: dateScaleDomain
},
marks: [
Plot.rectY(
data,
Plot.binX(
{ y: "count" },
{
x: "date",
thresholds: 80
}
)
),
Plot.ruleY([0])
]
};
}

let chart = Plot.plot(getSpec(data));

const x = chart.scale("x"),
[x1, x2] = x.range;
const y = chart.scale("y"),
[y1, y2] = y.range;

let domain = x.domain;

const wrapper = svg`<svg viewBox="${chart.getAttribute("viewBox")}">${chart}`;

const redraw = function () {
chart.replaceWith(
(chart = Plot.plot(
getSpec(
data.filter((d) => combinePredicates(...Object.values(predicates))(d))
)
))
);
};

const brush = d3
.brushX()
.extent([
[x1, y2],
[x2, y1]
])
.on("brush end", (event) => {
const { selection, sourceEvent } = event;
domain = selection && selection.map(x.invert);
if (sourceEvent) dispatch.call("dateBrush", wrapper, domain);
});

d3.select(wrapper).call(brush);

dispatch.on("distBrush.date", function (domain) {
predicates.dist =
domain === null ? (d) => true : (d) => inDomain(d.distance, domain);
redraw();
});

dispatch.on("hourBrush.date", function (domain) {
predicates.hour =
domain === null ? (d) => true : (d) => inDomain(hour(d), domain);
redraw();
});

dispatch.on("delayBrush.date", function (domain) {
predicates.delay =
domain === null ? (d) => true : (d) => inDomain(delay(d), domain);
redraw();
});

return wrapper;
}
Insert cell
Insert cell
height = 140
Insert cell
Insert cell
delay = function (d) {
return Math.max(-60, Math.min(149, d.delay));
}
Insert cell
hour = function (d) {
return d.date.getHours() + d.date.getMinutes() / 60;
}
Insert cell
parseDate = function (d) {
return new Date(
2001,
d.substring(0, 2) - 1,
d.substring(2, 4),
d.substring(4, 6),
d.substring(6, 8)
);
}
Insert cell
Insert cell
dispatch = d3.dispatch("distBrush", "delayBrush", "hourBrush", "dateBrush")
Insert cell
Insert cell
inDomain = function (d, domain) {
return domain === null ? true : d >= domain[0] && d <= domain[1];
}
Insert cell
function combinePredicates(...predicates) {
return function (value) {
return predicates.every((predicate) => predicate(value));
};
}
Insert cell
Insert cell
hourScaleDomain = d3.extent(data, (d) => hour(d))
Insert cell
distScaleDomain = d3.extent(data, (d) => d.distance)
Insert cell
delayScaleDomain = d3.extent(data, (d) => delay(d))
Insert cell
dateScaleDomain = d3.extent(data, (d) => d.date)
Insert cell
Insert cell
data = flights.map((d, i) => ({
...d,
date: parseDate(d.date)
}))
Insert cell
flights.map((d) => parseDate(d.date))
Insert cell
flights-3m.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

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