Published
Edited
Sep 2, 2022
Insert cell
Insert cell
Insert cell
Insert cell
DOM.download(() => serialize(chart), undefined, "Save as SVG")
Insert cell
key = legend({
color,
title: "Daily views on the the QAnon Wikipedia page",
tickFormat: "0,f"
})
Insert cell
chart = {
const svg = d3
.create("svg")
.attr("viewBox", [0, 0, width, height * years.length])
.attr("font-family", "sans-serif")
.attr("font-size", 10);

const year = svg
.selectAll("g")
.data(years)
.join("g")
.attr("transform", function (d, i) {
if (type == "calendar") {
return `translate(40.5,${height * i + cellSize * 1.5})`;
} else {
return `translate(40.5,${height * i + cellSize * 8})`;
}
})
.attr("id", (d) => d[0]);

year
.append("text")
.attr("x", -5)
.attr("y", -5)
.attr("font-weight", "bold")
.attr("text-anchor", "end")
.text(([key]) => key);

year
.append("g")
.attr("text-anchor", "end")
.selectAll("text")
.data(
(weekday === "weekday" ? d3.range(2, 7) : d3.range(7)).map(
(i) => new Date(1995, 0, i)
)
)
.join("text")
.attr("x", -5)
.attr("y", (d) => (countDay(d) + 0.5) * cellSize)
.attr("dy", "0.31em")
.text(type === "calendar" ? formatDay : "");

year
.append("g")
.selectAll("rect")
.data(
weekday === "weekday"
? ([, values]) =>
values.filter((d) => ![0, 6].includes(d.date.getUTCDay()))
: ([, values]) => values
)
.enter()
.append("rect")
.attr("width", function (d, i) {
if (type == "calendar") {
return cellSize - 1;
} else {
return 1.7;
}
})
.attr("height", function (d) {
if (type == "calendar") {
return cellSize - 1;
} else {
return d.value * (125 / max);
}
})
.attr("x", function (d, i) {
if (type == "calendar") {
return timeWeek.count(d3.utcYear(d.date), d.date) * cellSize + 0.5;
} else {
return i * 2.2;
}
})
.attr("y", function (d) {
if (type == "calendar") {
return countDay(d.date) * cellSize + 0.5;
} else {
return d.value * -(125 / max);
}
})
// .attr("fill", "#eee")
.attr("fill", (d) => color(d.value));

const month = year
.append("g")
.selectAll("g")
.data(([, values]) =>
d3.utcMonths(d3.utcMonth(values[0].date), values[values.length - 1].date)
)
.join("g");

month
.filter((d, i) => i)
.append("path")
.attr("fill", "none")
.attr("stroke", "#fff")
.attr("stroke-width", 3)
.attr("d", pathMonth);

month
.append("text")
.attr(
"x",
(d) => timeWeek.count(d3.utcYear(d), timeWeek.ceil(d)) * cellSize + 2
)
.attr("y", -5)
.text(formatMonth);

const day_2018 = svg
.append("rect")
.attr("width", cellSize - 1)
.attr("height", cellSize - 1)
.attr("x", -100)
.attr("stroke", "#000")
.attr("stroke-width", 2)
.attr("fill", "none");

const day_2019 = svg
.append("rect")
.attr("width", cellSize - 1)
.attr("height", cellSize - 1)
.attr("x", -100)
.attr("stroke", "#000")
.attr("stroke-width", 2)
.attr("fill", "none");

const day_2020 = svg
.append("rect")
.attr("width", cellSize - 1)
.attr("height", cellSize - 1)
.attr("x", -100)
.attr("stroke", "#000")
.attr("stroke-width", 2)
.attr("fill", "none");

function getDays(d) {
var date = d.date,
value = d.value;
console.log(date, value);
if (type === "calendar") {
day_2018.attr(
"x",
40.5 + (timeWeek.count(d3.utcYear(date), date) * cellSize + 0.5)
);
day_2018.attr(
"y",
height * 2 + cellSize * 1.5 + countDay(date) * cellSize + 0.5
);

day_2019.attr(
"x",
40.5 + (timeWeek.count(d3.utcYear(date), date) * cellSize + 0.5)
);
day_2019.attr(
"y",
height * 1 + cellSize * 1.5 + countDay(date) * cellSize + 0.5
);

day_2020.attr(
"x",
40.5 + (timeWeek.count(d3.utcYear(date), date) * cellSize + 0.5)
);
day_2020.attr(
"y",
height * 0 + cellSize * 1.5 + countDay(date) * cellSize + 0.5
);
} else {
day_2018.attr("x", -100);
day_2019.attr("x", -100);
day_2020.attr("x", -100);
}
}
return svg.node();
}
Insert cell
color = {
const max = d3.quantile(
data.map((d) => Math.abs(d.value)).sort(d3.ascending),
0.9975
);
const min = d3.min(data, (d) => d.value);
return d3.scaleSequentialSqrt(d3.interpolateOranges).domain([min, max]);
}
Insert cell
min = d3.min(data, (d) => d.value)
Insert cell
max = d3.max(data, (d) => d.value)
Insert cell
nu_color = d3
.scaleQuantize()
.domain(naturalbreaks)
.range(["#7AE6E9", "#00D4D8", "#39BCC0", "#279CA2", "#147D83", "#005F66"])
Insert cell
colorInterpolator = d3.interpolate("#7FF6F6", "#005F66")
Insert cell
naturalbreaks = simple.ckmeans(values, 5).map((v) => v.pop())
Insert cell
values = _.uniqBy(data, (d) => d.value).map((d) => d.value)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
data = {
var arr = [];
wikidata.forEach(function (d) {
arr.push({ date: d.date, value: d.views });
});
return arr;
}
Insert cell
viewsData = {
var arr = [];
wikidata.forEach(function (d) {
arr.push({ value: d.views });
});
return arr;
}
Insert cell
wikidata = pageviewsPerArticle(article)
Insert cell
viewof article = Inputs.text({
submit: true,
label: "Wikipedia Slug",
value: "Roe_v._Wade"
})
Insert cell
pageviewsPerArticle = (
article,
{
start = "20170101",
end = date2ts(new Date()),
access = "all-access",
project = "en.wikipedia",
granularity = "daily",
agent = "user"
} = {}
) => {
return fetch(
`https://wikimedia.org/api/rest_v1/metrics/pageviews/per-article/${project}/${access}/${agent}/${encodeURIComponent(
article
)}/${granularity}/${start}/${end}`
)
.then((res) => res.json())
.then((data) => {
if (!data.items) return data;
data.items.forEach((d) => (d.date = ts2date(d.timestamp)));
return data.items;
});
}
Insert cell
date2ts = (date) =>
(
date.getUTCFullYear() * 1e6 +
(date.getUTCMonth() + 1) * 1e4 +
date.getUTCDate() * 1e2 +
date.getUTCHours()
).toString()
Insert cell
ts2date = (ts) =>
new Date(
Date.UTC(
+ts.slice(0, 4),
+ts.slice(4, 6) - 1,
+ts.slice(6, 8),
+ts.slice(8, 10)
)
)
Insert cell
Insert cell
Insert cell
Insert cell
serialize = {
const xmlns = "http://www.w3.org/2000/xmlns/";
const xlinkns = "http://www.w3.org/1999/xlink";
const svgns = "http://www.w3.org/2000/svg";
return function serialize(svg) {
svg = svg.cloneNode(true);
const fragment = window.location.href + "#";
const walker = document.createTreeWalker(svg, NodeFilter.SHOW_ELEMENT);
while (walker.nextNode()) {
for (const attr of walker.currentNode.attributes) {
if (attr.value.includes(fragment)) {
attr.value = attr.value.replace(fragment, "#");
}
}
}
svg.setAttributeNS(xmlns, "xmlns", svgns);
svg.setAttributeNS(xmlns, "xmlns:xlink", xlinkns);
const serializer = new window.XMLSerializer();
const string = serializer.serializeToString(svg);
return new Blob([string], { type: "image/svg+xml" });
};
}
Insert cell
simple = require("simple-statistics@7/dist/simple-statistics.min.js")
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