Public
Edited
May 2, 2023
Insert cell
companyBlacklist = new Set([
"601df123c52e1859c97ab4f0",
"60c781d99d434f59270b2f20",
"60e5ba56f31b9403db9d1cda",
"60956febcfe34d771a18a518",
"60f58c20f0f4551ea30b6989",
"617d7ceffb0a1614953fad1c",
"621c885a01238480e322364e",
"623c71d80ba059a2ee247337",
"613527ffea4ba3328b3cd10c",
"62a84e5f33960c7dc92b812e",
"618c3fa2a9bc837a3093c853",
"61781584fb0a1614953f56f7",
"62ed9475de16143f3220c067",
"62d2366e0fb23645461c4937",
"6317651f0aa2c6e2064cf936",
"61f8095d38970e6fb5f340c7"
])
Insert cell
Insert cell
Plot.plot(
(() => {
const n = 3; // number of facet columns
const keys = Array.from(d3.union(filteredBoosts.map((d) => d.org)));
const index = new Map(keys.map((key, i) => [key, i]));
// const fx = (key) => index.get(key) % n;
// const fy = (key) => Math.floor(index.get(key) / n);
const fx = (key) =>
["large", "medium", "small"].indexOf(
companyData.find((c) => c.orgId === key).size
);
const fy = (key) => companyData.find((c) => c.orgId === key).yf;
const ftext = (key) =>
txtMap[companyData.find((c) => c.orgId === key).size];
return {
height: 1800,
width: 800,
axis: null,
y: { insetTop: 10 },
fx: { padding: 0.03 },
marks: [
Plot.barY(
filteredBoosts,
Plot.normalizeY("extent", {
x: "invoiceMonth",
y: "boostedRevenueUsd",
fx: (d) => fx(d.org),
fy: (d) => fy(d.org)
})
),
Plot.text(keys, {
fx,
fy,
frameAnchor: "top-left",
dx: 6,
dy: 6,
text: ftext
}),
Plot.frame()
]
};
})()
)
Insert cell
txtMap = ({
large: "Enterprise",
medium: "SMB",
small: "Small"
})
Insert cell
companyData = d3
.groups(filteredCompanies, (d) => d.size)
.reduce((acc, [size, cs]) => {
acc.push(
...cs.map((c, i) => ({
yf: i,
size,
...c
}))
);
return acc;
}, [])
Insert cell
filteredBoosts = boosts.filter((d) => filteredCompanyIds.has(d.org))
Insert cell
filteredCompanyIds = new Set(filteredCompanies.map((d) => d.orgId))
Insert cell
filteredCompanies = companies.filter(
(d) =>
d.totalBoosted > 1000 && data.length >= 3 && !companyBlacklist.has(d.orgId)
)
Insert cell
companies = d3
.groups(boosts, (d) => d.org)
.map(([id, data]) => {
const totalBoosted = data.reduce((a, s) => a + s.boostedRevenueUsd, 0);
const boostPerMonth = totalBoosted / data.length;
let size = "small";
if (boostPerMonth > 4000) size = "medium";
if (boostPerMonth > 10000) size = "large";
return {
orgId: id,
totalBoosted,
size,
data,
boostPerMonth: totalBoosted / data.length
};
})
Insert cell
selectedBoosts = boosts.filter((d) => selectedOrgs.includes(d.org))
Insert cell
boosts = data
.map((d) => {
const m = { ...d };
delete m.orgId;
return {
...m,
org: d.orgId.$oid
};
})
.sort((a, b) => d3.ascending(a.invoiceMonth, b.invoiceMonth))
.filter((d) => d.boostedRevenueUsd > 200)
Insert cell
data = FileAttachment("materialized_boosted_revenue_usd.json").json()
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