Published
Edited
Jan 24, 2022
3 forks
Importers
19 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// Plot the total number of sales by brand
Plot.plot({
marks: [
// Draw vertical bars
Plot.barY(
data,
// Group data into categories along the x axis
Plot.groupX(
{ y: "count" }, // compute + display the count of observations in each group
{ x: "brand" } // group observations by the `brand`
)
)
],
y: {
label: "↑ Count of sales by brand"
},
marginLeft: 60
})
Insert cell
// Plot the total number of sales by brand
Plot.plot({
marks: [
// Draw vertical bars
Plot.barY(
data,
// Group data into categories along the x axis
Plot.groupX(
{ y: "count" }, // compute + display the count of observations in each group
{
x: "brand",
filter: (d) => d.brand !== "Google",
sort: { x: "y", reverse: true },
} // group observations by the `brand`
)
)
],
y: {
label: "↑ Count of sales by brand"
},
marginLeft: 60
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Inputs.table(data)
Insert cell
Insert cell
Insert cell
Insert cell
import {
createDemo,
intro,
dataDescription,
plotWidth,
welcomeBox,
download,
inputStyles,
deck
} from "@observablehq/cheatsheet-utilities"
Insert cell
import {toc} from "@nebrius/indented-toc"
Insert cell
Insert cell
// Purchase event data -- queried from this notebook using this query:
// https://observablehq.com/@observablehq/bigquery-e-commerce-public-data
/* select * from events
join items on events.item_id = items.id
where type = 'purchase' */
data = FileAttachment("purchase_data.csv").csv({ typed: true })
Insert cell
Insert cell
introParagraph = () => htl.html`<p style="margin-top:0px"><a href ="https://observablehq.com/@observablehq/plot?collection=@observablehq/plot" target="_blank">Observable Plot</a> offers a variety of ways to aggregate and augment your data at the moment you use it. These <a href="https://observablehq.com/@observablehq/plot-transforms?collection=@observablehq/plot" target="_blank">transform</a> functions are applied within your visualization code and range from simple filtering and sorting to more complex grouping, binning, and normalization techniques.</p>`
Insert cell
Insert cell
histX = ({
// Control panel (the code on the left): an array of controls, one for each line (including text)
controls: [
{ type: "text", value: "// Bin by date along x" },
{ type: "text", value: "Plot.rectY(data,", indent: 0 },
{ type: "text", value: "Plot.binX({", indent: 1 },

{
param: "reducer",
label: "y",
type: "select",
options: reducerOptions,
value: "count"
},
{ type: "text", value: "},{", indent: 1 },
{ param: "x", value: "date" },
{ param: "y", value: "price_in_usd" },
{
param: "fill",
options: ["blue", "brand", "red"],
type: "select"
},
{
param: "thresholds",
options: [10, 50, "d3.utcDay", "d3.utcWeek", "d3.utcMonth"],
type: "select"
},
{
param: "fillOpacity",
type: "range",
min: 0.1,
max: 1,
value: 0.5,
step: 0.05
},
{ type: "text", value: "}", indent: 1 },
{ type: "text", value: "})" }
],

// Function that returns a string of the plot code: input is key/value pairs of the controls
plot: (config) => `Plot.plot({
marks: [
Plot.rectY(
data,
Plot.binX(
{ y: "${config.reducer}" },
{
x: "date",
y: "price_in_usd",
fill: "${config.fill}",
fillOpacity: ${config.fillOpacity},
thresholds: ${config.thresholds}
}
)
)
],
marginLeft: 100,
width: ${plotWidth},
height: 300
})`
})
Insert cell
simplehist = ({
// Control panel (the code on the left): an array of controls, one for each line (including text)
controls: [
{ type: "text", value: "// Create a histogram" },
{ type: "text", value: "Plot.rectY(data,", indent: 0 },
{ type: "text", value: "Plot.binX({", indent: 1 },

{
param: "reducer",
label: "y",
value: "count"
},
{ type: "text", value: "},{", indent: 1 },
{ param: "x", value: "price_in_usd" },
{
param: "fill",
options: ["blue", "brand", "red"],
type: "select"
},
{
param: "thresholds",
type: "select",
options: [5, 10, 50]
},
{
param: "fillOpacity",
type: "range",
min: 0.1,
max: 1,
value: 0.5,
step: 0.05
},
{ type: "text", value: "}", indent: 1 },
{ type: "text", value: "})" }
],

// Function that returns a string of the plot code: input is key/value pairs of the controls
plot: (config) => `Plot.plot({
marks: [
Plot.rectY(
data,
Plot.binX(
{ y: "${config.reducer}" },
{
x: "price_in_usd",
fill: "${config.fill}",
fillOpacity: ${config.fillOpacity},
thresholds: ${config.thresholds}
}
)
)
],
marginLeft: 100,
width: ${plotWidth},
height: 300
})`
})
Insert cell
histY = ({
// Control panel (the code on the left): an array of controls, one for each line (including text)
controls: [
{ type: "text", value: "// Bin by date along y" },
{ type: "text", value: "Plot.rectX(data,", indent: 0 },
{ type: "text", value: "Plot.binY({", indent: 1 },

{
param: "reducer",
label: "x",
type: "select",
options: reducerOptions,
value: "count"
},
{ type: "text", value: "},{", indent: 1 },
{ param: "x", value: "price_in_usd" },
{ param: "y", value: "date" },
{
param: "fill",
options: ["blue", "brand", "red"],
type: "select",
value: "brand"
},
{
param: "thresholds",
options: [10, 50, "d3.utcDay", "d3.utcWeek", "d3.utcMonth"],
type: "select",
value: "d3.utcDay"
},
{
param: "fillOpacity",
type: "range",
min: 0.1,
max: 1,
value: 0.5,
step: 0.05
},
{ type: "text", value: "}", indent: 1 },
{ type: "text", value: "})" }
],

// Function that returns a string of the plot code: input is key/value pairs of the controls
plot: (config) => `Plot.plot({
marks: [
Plot.rectX(
data,
Plot.binY(
{ x: "${config.reducer}" },
{
x: "price_in_usd",
y: "date",
fill: "${config.fill}",
fillOpacity: ${config.fillOpacity},
thresholds: ${config.thresholds}
}
)
)
],
marginLeft: 100,
width: ${plotWidth},
y: { reverse: true },
color: {
legend: true
}
})`
})
Insert cell
select = ({
controls: [
{
type: "text",
value: `// Highlight max value
Plot.selectMaxY(
// Moving average
Plot.windowY({`
},
{ param: "reduce", value: "mean" },
{ param: "k", value: 7, type: "range", min: 1, max: 14, step: 1 },
{ type: "text", value: "// Sum by day", indent: 2 },
{ type: "text", value: "},Plot.binX({", indent: 2 },
{ param: "y", value: "sum" },
{ type: "text", value: "},{", indent: 2 },

{ param: "x", value: "date" },
{ param: "y", value: "price_in_usd" },
{
param: "textAnchor",
value: "middle",
type: "select",
options: ["start", "middle", "end"]
},
{ param: "dy", value: -5, type: "range", min: -15, max: 15, step: 1 },
{ type: "text", value: "})))" }
],
plot: (config) => {
const textFn = (d) => `Peak sales: ${d3.utcFormat("%b %d")(d.date)}`;
return `Plot.plot({
marks: [
Plot.line(
data,
Plot.windowY(
{ reduce: "mean", k: ${config.k}, anchor: "middle" },
Plot.binX(
{ y: "sum" },
{ x: "date", y: "price_in_usd", thresholds: d3.utcDay }
)
)
),
Plot.dot(
data,
Plot.selectMaxY(
Plot.windowY(
{ reduce: "mean", k: ${config.k}, anchor: "middle" },
Plot.binX(
{ y: "sum" },
{ x: "date", y: "price_in_usd", thresholds: d3.utcDay }
)
)
)
),
Plot.text(
data,
Plot.selectMaxY(
Plot.windowY(
{ reduce: "mean", k: ${config.k}, anchor: "middle" },
Plot.binX(
{ y: "sum", text: "first" },
{
x: "date",
y: "price_in_usd",
thresholds: d3.utcDay,
text: ${textFn},
textAnchor: "${config.textAnchor}",
dy: ${config.dy}
}
)
)
)
)
],
width: ${plotWidth}
})`;
}
})
Insert cell
windowTransform = ({
controls: [
{ type: "text", value: "// Sales (moving avg.)\nPlot.line(data, " },
{ type: "text", value: "Plot.windowY({", indent: 1 },
{
param: "reduce",
type: "select",
options: [
"sum",
"min",
"max",
"mean",
"median",
"mode",
"variance",
"deviation",
"difference",
"ratio"
],
value: "mean"
},
{
param: "k",
type: "range",
min: 1,
max: 28,
step: 1,
value: 7
},
{
param: "anchor",
type: "select",
options: ["start", "middle", "end"],
value: "middle"
},
{ type: "text", value: "}))" }
],

plot: (config) => `Plot.plot({
marks: [
Plot.line(
data,
Plot.windowY(
{ reduce: "${config.reduce}", k: ${config.k}, anchor: "${config.anchor}" },
Plot.binX(
{ y: "sum" },
{ x: "date", y: "price_in_usd", thresholds: d3.utcDay }
)
)
)
],
height: 100,
width: ${plotWidth}
})`
})
Insert cell
stack = ({
controls: [
// Offset
{ type: "text", value: "// Stack sales\nPlot.areaY(data, " },
{ type: "text", value: "Plot.binX({", indent: 1 },
{ param: "reducer", label: "y", value: "mean" },
{ type: "text", value: "}, {", indent: 2 },
{
param: "offset",
type: "select",
options: ["null", "silhouette", "wiggle", "expand"],
value: "null"
},
{
param: "order",
type: "select",
options: ["sum", "appearance", "inside-out", "z", "value", "null"],
value: "null"
},
{ param: "x", value: "date" },
{ param: "y", value: "price_in_usd" },
{ type: "text", value: "}})" }
],
plot: (config) => {
const offsetFormatted =
config.offset === "null" ? `null` : `"${config.offset}"`;
const orderFormatted =
config.order === "null" ? `null` : `"${config.order}"`;
return `Plot.plot({
marks: [
Plot.areaY(
data,
Plot.binX(
{ y: "mean" },
{
x: "date",
y: "price_in_usd",
thresholds: d3.utcWeek,
order: ${orderFormatted},
fill: "brand",
offset: ${offsetFormatted}
}
)
)
],
height: 100,
width: ${plotWidth},
color: { legend: true }
})`;
}
})
Insert cell
// Normalize example
normalize = ({
controls: [
{ type: "text", value: "// Normalize sales\nPlot.line(data, " },
{ type: "text", value: "Plot.normalizeY(", indent: 1 },
{ type: "text", value: "Plot.binX({", indent: 2 },
{
param: "basis",
type: "select",
options: ["first", "last", "mean", "median", "sum", "extent"],
value: "first"
},
{ param: "x", value: "date" },
{ param: "y", value: "price_in_usd" },
{ param: "stroke", value: "brand" },
{ type: "text", value: "})))" }
],
plot: (config) => `Plot.plot({
marks: [
Plot.line(
data,
Plot.normalizeY(
"${config.basis}",
Plot.binX(
{ y: "sum" },
{
x: "date",
y: "price_in_usd",
stroke: "brand",
sort: "date",
thresholds: d3.utcDay
}
)
)
)
],
y: {
label: "Price normalized by ${config.basis} value"
},
height: 200,
width: ${plotWidth},
color: { legend: true }
})`
})
Insert cell
group = {
const input = {
param: "reducer",
type: "select",
options: reducerOptions,
value: "count"
};

const groupX = {
controls: [
{ type: "text", value: "// Group along X" },
{ type: "text", value: "Plot.barY(data," },
{ type: "text", value: "Plot.groupX({", indent: 1 },
{ ...input, label: "y" },
{ type: "text", value: "}, {", indent: 2 },
{ param: "x", value: "brand" },
{ param: "y", value: "price_in_usd" },
{ type: "text", value: "}))" }
],
plot: (config) =>
`Plot.plot({
marks: [
Plot.barY(
data,
Plot.groupX({ y: "${config.reducer}" }, { x: "brand", y: "price_in_usd" })
)
],
height: 200,
marginLeft: 50,
width: ${plotWidth}
})`
};

const groupY = {
controls: [
{ type: "text", value: "// Group along Y" },
{ type: "text", value: "Plot.barX(data," },
{ type: "text", value: "Plot.groupY({", indent: 1 },
{ ...input, label: "x" },
{ type: "text", value: "}, {", indent: 2 },
{ param: "x", value: "price_in_usd" },
{ param: "y", value: "brand" },
{ type: "text", value: "}))" }
],
plot: (config) =>
`Plot.plot({
marks: [
Plot.barX(
data,
Plot.groupY({ x: "${config.reducer}" }, { x: "price_in_usd", y: "brand" })
)
],
height: 200,
marginLeft: 100,
width: ${plotWidth}
})`
};
return [groupX, groupY];
}
Insert cell
sortFilter = ({
controls: [
{ type: "text", value: "// Sort and filter data" },
{ type: "text", value: "Plot.barY(data," },
{ type: "text", value: "Plot.groupX({", indent: 1 },
{ param: "reducer", value: "count", label: "y" },
{ type: "text", value: "}, {", indent: 2 },
{ param: "x", value: "brand" },
{ param: "y", value: "price_in_usd" },
{
param: "sort",
type: "select",
options: [null, `{ x: "y" }`, `{ x: "y", reverse: true }`]
},
{
param: "filter",
type: "select",
options: [
null,
`(d) => d.brand !== "Google"`,
`(d) => d.brand === "YouTube"`
]
},
{ type: "text", value: "}))" }
],
plot: (config) =>
`Plot.plot({
marks: [
Plot.barY(
data,
Plot.groupX(
{ y: "${config.reducer}" },
{
x: "brand",
y: "price_in_usd",
sort: ${config.sort},
filter: ${config.filter}
}
)
)
],
height: 200,
marginLeft: 50,
width: ${plotWidth}
})`
})
Insert cell
map = ({
controls: [
{
type: "text",
value: `// Cumulative sales
Plot.line(
Plot.map({`
},
{ param: "reducer", label: "y", value: "cumsum" },
{ type: "text", value: "},{", indent: 2 },

{ param: "x", value: "date" },
{ param: "y", value: "price_in_usd" },
{ param: "stroke", value: "brand" },
{ type: "text", value: "}))" }
],
plot: (config) => {
return `Plot.plot({
marks: [
Plot.line(
data.sort((a, b) => a.date - b.date),
Plot.map(
{ y: "cumsum" },
{ x: "date", y: "price_in_usd", stroke: "brand" }
)
)
],
color: {
legend: true
},
marginLeft: 50,
y: {
label: "↑ Cumulative sales",
tickFormat: "$,.0s"
},
width: ${plotWidth}
})`;
}
})
Insert cell
reducerOptions = [
"count",
"sum",
"proportion",
"min",
"max",
"mean",
"median",
"variance",
"deviation",
"first",
"last"
]
Insert cell
Insert cell
<style>
.demo-wrapper {
margin-top:8px !important;
}
input, canvas, button {
vertical-align: bottom; /* unsure why this is needed to keep the radio buttons aligned */
}
</style>
Insert cell
inputStyles
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