Published
Edited
Jan 29, 2022
28 stars
Insert cell
Insert cell
Insert cell
{
const resources = [...new Set(gantt.map(d => d.Resource))];
const colorScheme = d3.schemeTableau10;
const plot = Plot.plot({
width: width,
height: gantt.length * 30,
marginLeft: 150,
x: {
axis: "top",
grid: true,
},
y: {
label: "",
domain: gantt.map(d => d.TaskName)
},
color: {
domain: resources,
range: colorScheme
},
marks: [
Plot.barX(gantt, {
y: "TaskName",
x1: "Start",
x2: "End",
fill: "Resource",
fillOpacity: 0.6,
rx: 6
}),
Plot.barX(gantt, {
y: "TaskName",
x1: "Start",
x2: "Current",
fill: "Resource",
fillOpacity: 1,
insetTop: 5,
insetBottom: 5,
rx: 6
})
]
})
return wrap(
Swatches({color: d3.scaleOrdinal(resources, colorScheme)}),
plot
);
}
Insert cell
gantt = {
const data = await FileAttachment("gantt.csv").csv({typed: true});
data.forEach(d => {
d.Start = d3.timeParse("%m/%d/%Y")(d.Start);
d.End = new Date(d.Start);
d.End.setDate(d.End.getDate() + d.Duration);
d.Current = new Date(d.Start);
d.Current.setDate(d.Current.getDate() + d.Duration * d.Progress);
});
return data;
}
Insert cell
Insert cell
bulletChart(bullet)
Insert cell
Insert cell
Insert cell
{
const data = bullet.map(d => ({...d, State: d.State, Revenue: d.Revenue * p / 100}));
return bulletChart(data);
}
Insert cell
color = ({
domain: ["Minimum", "Good", "Great", "Perfect"],
range: ["#fe7f2d", "#fcca46", "#a1c181", "#619b8a"]
})
Insert cell
segments = data => {
const max = d3.max(bullet.map(d => d.Great));
const thresholds = d => [d.Min, d.Good - d.Min, d.Great - d.Good, max - d.Great];
return data.flatMap(d => thresholds(d).map((v, i) => ({State: d.State, Name: color.domain[i], Threshold: v})));
}
Insert cell
bulletChart = data => {
const plot = Plot.plot({
width: width,
height: data.length * 50,
marginLeft: 50,
x: {
axis: "top",
tickFormat: "s"
},
color: color,
marks: [
Plot.barX(segments(data), Plot.stackX({
x: "Threshold",
y: "State",
fill: "Name"
})),
Plot.barX(data, {
x: "Revenue",
y: "State",
insetTop: 7.5,
insetBottom: 7.5,
fill: "#333",
fillOpacity: 0.8,
rx: 6
}),
Plot.tickX(data, {
x: "Target",
y: "State",
strokeWidth: 2
})
]
});
return wrap(
Swatches({color: d3.scaleOrdinal(color.domain, color.range)}),
plot
)
}
Insert cell
Insert cell
Insert cell
{
const data = treemap(mekko)
.each(d => {
d.x0 += margin.left;
d.x1 += margin.left;
d.y0 += margin.top;
d.y1 += margin.top;
})
.descendants()
.filter(d => d.height === 0);
const domain = [...new Set(data.map(d => d.data.territory))];
const colorScheme = d3.schemeTableau10;
const xs = [...new Set(data.flatMap(d => [d.x0, d.x1]))];
const lines = xs.map((x, i) => {
const f = data.find(d => d.x0 === x);
return {
x: x,
m: i < xs.length - 1 ? (x + xs[i + 1]) / 2 : null,
label: f ? f.data.year : null
}
});
const plot = Plot.plot({
width: width,
height: mekkoHeight + 30,
x: {
axis: null,
type: "identity"
},
y: {
axis: null,
type: "identity"
},
color: {
domain: domain,
range: colorScheme
},
marks: [
Plot.rect(data, {
x1: "x0", x2: "x1",
y1: "y0", y2: "y1",
fill: d => d.data.territory,
title: d => `${d.data.territory}\n${d.data.total}`
}),
Plot.ruleX(lines, {
x: d => d.x,
y1: 0,
y2: mekkoHeight,
stroke: "white",
strokeWidth: 2
}),
Plot.text(lines, {
x: "m",
y: mekkoHeight + 10,
fontWeight: "bold",
textAnchor: "middle",
text: "label"
}),
Plot.text(y.ticks(5), {
x: 10,
y: d => y(d),
fontWeight: "bold",
text: d => `${d}%`
})
]
});
return wrap(
Swatches({color: d3.scaleOrdinal(domain, colorScheme)}),
plot
);
}
Insert cell
y = d3.scaleLinear()
.domain([0, 100])
.range([mekkoHeight, margin.top]);
Insert cell
treemap = data => d3.treemap()
.round(true)
.tile(d3.treemapSliceDice)
.size([width - margin.left, mekkoHeight - margin.top])
(d3.hierarchy(d3.group(data, d => d.year, d => d.territory)).sum(d => d.total))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const data = treemap2(tdata).leaves();
const domain = [...new Set(data.map(d => d.data.territory))];
const colorScheme = d3.schemeTableau10;
const plot = Plot.plot({
width: width,
height: treemapHeight,
x: {
axis: null,
type: "identity"
},
y: {
axis: null,
type: "identity"
},
color: {
domain: domain,
range: colorScheme
},
marks: [
Plot.rect(data, {
x1: "x0", x2: "x1",
y1: "y0", y2: "y1",
fill: d => d.data.territory,
title: d => `${d.data.category} - ${d.data.territory}\n${d.data.amount}`
}),
Plot.text(data, {
x: "x0",
y: "y0",
dx: 2,
dy: 5,
ameAnchor: "top",
fill: "white",
textAnchor: "start",
text: d => {
const w = d.x1 - d.x0;
const l = d.data.category.length * 8;
return w > l ? d.data.category : ""
}
})
]
});
return wrap(
Swatches({color: d3.scaleOrdinal(domain, colorScheme)}),
plot
);
}
Insert cell
treemap2 = data => d3.treemap()
.tile(d3.treemapBinary)
.size([width, treemapHeight])
.padding(0.5)
.round(true)(
d3.hierarchy(d3.group(data, d => d.territory, d => d.category))
.sum(d => d.amount)
.sort((a, b) => b.amount - a.amount)
)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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