ccStyle = ({fontSize: "13px", fontFamily: "Roboto, sans-serif",
color: "#6E7681", background: "transparent"})
ccRD = [6.0, 10.4, 10.5, 9.0, 7.3, 6.6, 6.9, 8.2, 7.1, 8.1,
7.9, 6.8, 7.4, 9.5, 8.1, 13.5, 9.9, 6.9, 11.1, 8.2,
8.0, 7.7, 7.4, 6.5, 9.5, 8.2, 6.9, 7.2, 8.2, 6.7]
ccSalary = [846, 457, 563, 1466, 1200, 2164, 746, 1611, 824, 824, 1310,
1007, 1367, 575, 1252, 1354, 2479, 1238, 927, 1253, 925,
1284, 1279, 1660, 860]
// Five of the twenty-six, all with SHORT names. The bar chart is the only
// plot with words on its y axis, so it used to set the shared left margin at
// 88 — which left the other four, and the box plot worst of all, with a band
// of empty space down their left side and the drawing pushed off centre.
// Picking short names lets every plot share a 44px margin instead.
ccPay = [["CBS", 60.25], ["Exxon", 34.90], ["AT&T", 21.00],
["Apple", 4.17], ["Tesco", 1.90]].map(([k, v]) => ({k, v}))
ccPairs = [1, 2, 3, 4, 5, 6, 7].map((x, i) => ({x, y: [2, 4, 8, 16, 32, 64, 128][i]}))
ccMonths = [23, 39, 36, 26, 36, 12, 26, 30, 42, 35, 36, 28]
.map((x, i) => ({month: i + 1, x}))
// One geometry for all five: identical margins mean the axes are the same
// length and the ticks the same size from plot to plot, so clicking between
// displays changes the picture and nothing else. marginLeft is set by the
// longest company name on the bar chart; the numeric plots spend that space
// on their y-axis label.
ccBase = ({
width: 400, height: 152,
marginTop: 10, marginRight: 16, marginBottom: 56, marginLeft: 66,
style: ccStyle
})
// labelOffset 38 against a 48 bottom margin: Plot's default sets the axis
// label directly under the tick text and the two touched.
// The two labelOffsets are not equal because they are not measured the same
// way: Plot pushes the x label DOWN from the axis and the y label LEFT from
// it, past tick text of different lengths. These two land both labels the
// same distance clear of their tick numbers, which is what reads as even.
ccAxX = (label) => ({label, labelAnchor: "center", labelOffset: 46, tickSize: 5})
ccAxY = (label, ticks, domain) => ({label, labelAnchor: "center",
labelOffset: 55, tickSize: 5,
ticks, domain, grid: true})
// Explicit tick values, not a count. The shared geometry leaves a 94px plot
// area and `ticks: n` is only a HINT — d3 rounds to a nice step, so both 4
// and 5 came back with six labels 15px apart under 13px type, all but
// touching, while leaving it automatic dropped to two. Naming the values is
// the only way to fix the pitch, and these datasets never change.
ccDemo = ({
"Bar or pie": () => Plot.plot({...ccBase,
x: ccAxX("Executive pay ($m)"),
y: {label: null, tickSize: 5},
marks: [
Plot.barX(ccPay, {y: "k", x: "v", fill: "#325D88",
sort: {y: "x", reverse: true}}),
Plot.ruleX([0], {stroke: "#B0B7C0"})
]}),
"Histogram": () => Plot.plot({...ccBase,
x: ccAxX("R&D spend (% of revenue)"),
y: ccAxY("Firms", [0, 4, 8]),
marks: [
Plot.rectY(ccRD, Plot.binX({y: "count"},
{x: d => d, thresholds: d3.range(6, 15), fill: "#325D88"})),
Plot.ruleY([0], {stroke: "#B0B7C0"})
]}),
"Scatter plot": () => Plot.plot({...ccBase,
x: ccAxX("X"), y: ccAxY("Y", [0, 40, 80, 120]),
marks: [Plot.dot(ccPairs, {x: "x", y: "y", fill: "#325D88", r: 3.4})]}),
"Time series": () => Plot.plot({...ccBase,
x: {...ccAxX("Month"), tickFormat: "d"}, // From zero: a demand axis that starts at 15 exaggerates every dip.
y: ccAxY("Demand", [0, 25, 50], [0, 50]),
marks: [Plot.line(ccMonths, {x: "month", y: "x",
stroke: "#325D88", strokeWidth: 2})]}),
"Box plot": () => Plot.plot({...ccBase,
x: {...ccAxX("Executive salary (£000s)"), grid: true},
y: {label: null, ticks: [], tickSize: 5},
marks: [Plot.boxX(ccSalary, {x: d => d, fill: "#CBD9E6", stroke: "#325D88"})]})
})