Public
Edited
Dec 28, 2022
Insert cell
Insert cell
data = await d3
.json(
"https://raw.githubusercontent.com/chekos/my-recently-played-tracks/main/tracks.json"
)
.then((data) =>
data.map((d) => {
const playedAt = new Date(d.played_at);
const tzdate = Temporal.Instant.from(d.played_at).toZonedDateTimeISO(
"America/Los_Angeles"
); // date in localized timezone
const date = new Date(tzdate.epochMilliseconds); // Javascript Date object, stores date in browser's local timezone
const time = new Date(tzdate.epochMilliseconds % (24 * 60 * 60 * 1000)); // "Truncate" year, month, day
const hour = tzdate.hour;
const minute = tzdate.minute;
const dayOfMonth = tzdate.day;
const day = weekdays[tzdate.dayOfWeek + -1];
const weekend = tzdate.dayOfWeek > 5 ? "Weekend" : "Weekday";
const week = tzdate.weekOfYear;
const month = months[tzdate.month - 1];
const quarter =
tzdate.month < 4
? "Q1: Jan - Mar"
: tzdate.month < 7
? "Q2: Apr - Jun"
: tzdate.month < 10
? "Q3: Jul - Sep"
: "Q4: Oct - Dec";
const timeOfDay =
tzdate.hour < 5
? "Late night"
: tzdate.hour < 10
? "Early morning"
: tzdate.hour < 13
? "Morning"
: tzdate.hour < 17
? "Afternoon"
: tzdate.hour < 20
? "Late afternoon"
: "Evening";
const context = d.context;
const track = d.track;
const artistsNames = d.track.artists.map((v) => v.name);
return {
date,
time,
hour,
minute,
day,
weekend,
week,
month,
quarter,
dayOfMonth,
timeOfDay,
track,
playedAt,
context,
artistsNames
};
})
)
Insert cell
Insert cell
Plot.plot({
marks: [
Plot.tickX(data, {
x: "playedAt",
strokeOpacity: 0.1
})
]
})
Insert cell
Plot.plot({
marks: [
Plot.tickX(data, {
x: "time",
strokeOpacity: 0.1
})
]
})
Insert cell
Plot.plot({
marginLeft: 100,
y: { label: "", domain: weekdays, line: true },
x: { label: "", line: true, ticks: false },
color: { scheme: "purples" },
marks: [
Plot.cell(data, Plot.group({ fill: "count" }, { x: "hour", y: "day" })),
Plot.text(data, Plot.group({ text: "count" }, { x: "hour", y: "day" }))
]
})
Insert cell
Plot.plot({
facet: { data: data, y: "month", marginLeft: 100, marginRight: 100 },
fy: { label: "", domain: months.slice(7) },
marginLeft: 100,
y: { label: "", domain: weekdays },
x: { label: "" },
color: { scheme: "purples" },
marks: [
Plot.frame(),
Plot.cell(data, Plot.group({ fill: "count" }, { x: "hour", y: "day" })),
Plot.text(data, Plot.group({ text: "count" }, { x: "hour", y: "day" }))
]
})
Insert cell
Plot.plot({
y: { label: "", domain: months.slice(7), line: true },
x: { label: "Day of Month", line: true },
marginLeft: 80,
color: { scheme: "purples" },
marks: [
Plot.cell(
data,
Plot.group({ fill: "count" }, { x: "dayOfMonth", y: "month" })
),
Plot.text(
data,
Plot.group({ text: "count" }, { x: "dayOfMonth", y: "month" })
)
]
})
Insert cell
Plot.plot({
facet: { data: data, y: "day", marginLeft: 80 },
fy: { label: "", line: true, domain: domains.day },
x: { label: "Time of Day", line: true, domain: domains.timeOfDay },
marginLeft: 80,
color: { scheme: "purples" },
padding: 0.05,
marks: [
Plot.cell(data, Plot.groupX({ fill: "proportion" }, { x: "timeOfDay" })),
Plot.text(
data,
Plot.groupX({ text: "proportion-facet" }, { x: "timeOfDay" })
)
]
})
Insert cell
Plot.plot({
facet: { data: data, y: "timeOfDay", marginLeft: 100 },
fy: { domain: domains.timeOfDay, label: "" },
marks: [
Plot.dot(
data,
Plot.binX(
{ r: "count" },
{
x: "playedAt",
thresholds: d3.timeHour,
strokeOpacity: 0.4,
stroke: "timeOfDay"
}
)
)
]
})
Insert cell
Plot.tickX(data, { x: "playedAt", strokeOpacity: 0.1 }).plot({
facet: {
data: data,
x: "hour",
y: "day",
marginLeft: 100
},
fy: {
//domain: domains.month.slice(7),
domain: domains.day,
label: ""
},
width,
fx: { label: "" },
marks: [
Plot.frame(),
Plot.rect(
data,
Plot.groupZ({ fill: "count" }, { fill: "playedAt", fillOpacity: 0.6 })
)
],
color: {
scheme: "purples"
}
})
Insert cell
Insert cell
artists = data.flatMap((d) => d.artistsNames)
Insert cell
Insert cell
Plot.plot({
x: { domain: domains.hour },
color: { scheme: "purples" },
padding: 0,
marks: [
Plot.frame(),
Plot.cell(
filtered_data,
Plot.groupX({ fill: "count" }, { x: "hour", stroke: "black" })
),
Plot.text(filtered_data, Plot.groupX({ text: "count" }, { x: "hour" }))
]
})
Insert cell
Plot.plot({
marks: [
Plot.dot(
filtered_data,
Plot.binX(
{ r: "count" },
{
x: "playedAt",
thresholds: d3.timeHour,
stroke: "purple",
strokeOpacity: 0.3
}
)
)
]
})
Insert cell
Plot.plot({
x: { domain: domains.day, label: "" },
color: { scheme: "purples" },
padding: 0.05,
marks: [
Plot.cell(
filtered_data,
Plot.groupX(
{ fill: "count" },
{
x: "day"
}
)
),
Plot.text(
filtered_data,
Plot.groupX(
{ text: "proportion" },
{
x: "day"
}
)
)
]
})
Insert cell
Plot.tickX(filtered_data, { x: "playedAt", strokeOpacity: 0.3 }).plot({
facet: {
data: filtered_data,
x: "hour",
y: "day",
marginLeft: 80
},
fy: {
domain: domains.day,
label: ""
},
width,
fx: { label: "" },
marks: [
Plot.frame(),
Plot.rect(
filtered_data,
Plot.groupZ(
{ fill: "count" },
{ fill: "playedAt", fillOpacity: 0.6, thresholds: d3.timeHour }
)
)
],
color: {
scheme: "purples"
}
})
Insert cell
Plot.plot({
facet: { data: filtered_data, y: "timeOfDay", marginLeft: 100 },
fy: { domain: domains.timeOfDay, label: "" },
marks: [
Plot.dot(
filtered_data,
Plot.binX(
{ r: "count" },
{
x: "playedAt",
thresholds: d3.timeHour,
strokeOpacity: 0.4,
stroke: "timeOfDay"
}
)
)
]
})
Insert cell
Insert cell
weekdays = [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
]
Insert cell
months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
Insert cell
timeOfDay = [
"Late night",
"Early morning",
"Morning",
"Afternoon",
"Late afternoon",
"Evening"
]
Insert cell
hours = [...Array(24).keys()]
Insert cell
// Load the Temporal API using a Polyfill
Temporal = {
const TemporalLib = await require("@js-temporal/polyfill@0.3.0");
return TemporalLib.Temporal;
}
Insert cell
domains = ({
month: months,
day: weekdays,
timeOfDay: timeOfDay,
hour: hours
})
Insert cell
import {TimeChart, TimeAxis} from "@observablehq/timechart"
Insert cell
interval = d3.utcMinute.every(15) // sample frequency
Insert cell
stop = interval() // exclusive
Insert cell
start = interval.offset(stop, -width) // inclusive
Insert cell
TimeAxis({ interval, start, stop })
Insert cell
TimeChart(array_data, { interval, start, stop })
Insert cell
array_data = data.map((d) => d3.timeMinute.every(15)(d.time))
Insert cell
array_data.reduce((counts, item) => {
counts[item] = (counts[item] || 0) + 1;
return counts;
}, {})
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