Public
Edited
Oct 31, 2023
1 star
Insert cell
Insert cell
Insert cell
svg = {
const svg = d3
.create("svg")
.attr("viewBox", [
0,
0,
width + margin.left + margin.right,
height + margin.top + margin.bottom
]);


return svg.node();
}
Insert cell
height = 450 - margin.top - margin.bottom
Insert cell
width = 850 - margin.left - margin.right
Insert cell
margin = ({ top: 30, right: 0, bottom: 80, left: 50 })
Insert cell
tweetData = FileAttachment("tweet.json").json()
Insert cell
function processTweets(tweets) {
const counts = new Map();

tweets.forEach((tweetObject) => {
const tweet = tweetObject.tweet;
const date = new Date(tweet.created_at);
const dayOfWeek = date.toLocaleString("en-US", { weekday: "long" });
const hourOfDay = date.getHours();
const month = date.toLocaleString("en-US", { month: "long" });

const key = `${dayOfWeek}-${hourOfDay}-${month}`;
counts.set(key, (counts.get(key) || 0) + 1);
});

return Array.from(counts).map(([key, count]) => {
const [dayOfWeek, hourOfDay, month] = key.split("-");
return { dayOfWeek, hourOfDay, month, count };
});
}
Insert cell
processedData = processTweets(await tweetData)
Insert cell
d3 = require("d3@6")
Insert cell
scales = {
const hours = [...new Set(processedData.map((d) => +d.hourOfDay))];
hours.sort((a, b) => a - b);

const xScale = d3
.scaleBand()
.range([margin.left, width - margin.right])
.domain(hours)
.padding(0.05);

const yScale = d3
.scaleBand()
.range([height - margin.bottom, margin.top])
.domain([...new Set(processedData.map((d) => d.dayOfWeek))].reverse())
.padding(0.05);

const colorScale = d3
.scaleSequential(d3.interpolateInferno)
.domain([0, d3.max(processedData, (d) => d.count)]);

return { xScale, yScale, colorScale };
}
Insert cell
axes = {
const svgElement = d3.select(svg);

console.log(svgElement);

const { xScale, yScale } = scales;

const xAxis = d3.axisBottom(xScale).tickSizeOuter(0);
const yAxis = d3.axisLeft(yScale).tickSizeOuter(0);

svgElement
.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(xAxis);

svgElement
.append("g")
.attr("transform", `translate(${margin.left},0)`)
.call(yAxis);
}
Insert cell
drawHeatmap = {
const svgElement = d3.select(svg);

svgElement
.selectAll("rect")
.data(processedData, (d) => d.dayOfWeek + ":" + d.hourOfDay)
.join("rect")
.attr("x", (d) => scales.xScale(d.hourOfDay))
.attr("y", (d) => scales.yScale(d.dayOfWeek))
.attr("width", scales.xScale.bandwidth())
.attr("height", scales.yScale.bandwidth())
.attr("fill", (d) => scales.colorScale(d.count));
}

Insert cell
sortedMonths = {
const monthOrder = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
];

function sortMonths(months) {
return months.sort((a, b) => monthOrder.indexOf(a) - monthOrder.indexOf(b));
}

let months = [...new Set(processedData.map((d) => d.month))];
return sortMonths(months);
}
Insert cell
animateHeatmap = {
let currentMonthIndex = 0;

function update(month) {
console.log(`Updating heatmap for month: ${month}`);
const monthData = processedData.filter((d) => d.month === month);
console.log(`Data for month:`, monthData);

const svgElement = d3.select(svg);

svgElement
.selectAll("rect")
.data(monthData, (d) => d.dayOfWeek + ":" + d.hourOfDay)
.join(
(enter) => enter.append("rect"),
(update) => update,
(exit) => exit.remove()
)
.transition()
.duration(500)
.attr("fill", (d) => scales.colorScale(d.count))
.attr("x", (d) => scales.xScale(d.hourOfDay))
.attr("y", (d) => scales.yScale(d.dayOfWeek))
.attr("width", scales.xScale.bandwidth())
.attr("height", scales.yScale.bandwidth());

svgElement
.selectAll("text.month-display")
.data([month])
.join(
(enter) =>
enter
.append("text")
.attr("class", "month-display")
.attr("x", width / 2)
.attr("y", height + margin.bottom - 10)
.attr("dy", "1em")
.style("text-anchor", "middle")
.text((d) => `Month: ${d}`),
(update) => update.text((d) => `Month: ${d}`)
);
}

const interval = d3.interval(() => {
update(sortedMonths[currentMonthIndex]);
currentMonthIndex = (currentMonthIndex + 1) % sortedMonths.length;
}, 3000); // Update every 3 seconds

return () => interval.stop();
}
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