Published
Edited
Jul 29, 2022
Importers
9 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
mtaDailyRidership = FileAttachment("MTA_Daily_Ridership_Data__Beginning_2020.csv").csv({typed: true})
Insert cell
Insert cell
Insert cell
database = DuckDBClient.of({ mtaDailyRidership })
Insert cell
Insert cell
database
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
database
SELECT make_date(array_slice("Date", 7, 11)::int,
array_slice("Date", 0, 2)::int,
array_slice("Date", 4, 5)::int) as "Date"
, array_slice("Date", 4, 5) as "Day"
, array_slice("Date", 0, 2) as "Month"
, array_slice("Date", 7, 11) as "Year"
, "Subways: Total Estimated Ridership" as metric
FROM mtaDailyRidership
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
function movingIQR(values, N) {
const means = new Array(values.length).fill(NaN);
let i = 0;
for (let n = values.length; i < n; ++i) {
// Filter values for just those that compute the moving average
// This includes filter those below the N threshold, which happens at the beginning and end of the dataset.
if(i >= N - 1) {
const meanObject = {};
// Then we run the mean of values that fall within the range
meanObject.avg = d3.mean(values.filter((e, f) => f > (i - N) && f <= i))
meanObject.min = d3.min(values.filter((e, f) => f > (i - N) && f <= i))
meanObject.max = d3.max(values.filter((e, f) => f > (i - N) && f <= i))
meanObject.first = d3.quantile(values.filter((e, f) => f > (i - N) && f <= i), .25)
meanObject.third = d3.quantile(values.filter((e, f) => f > (i - N) && f <= i), .75)

means[i] = meanObject
}
}
return means;
}
Insert cell
function movingAverage(values, N) {
let i = 0;
let sum = 0;
const means = new Float64Array(values.length).fill(NaN);
for (let n = Math.min(N - 1, values.length); i < n; ++i) {
sum += values[i];
}
for (let n = values.length; i < n; ++i) {
sum += values[i];
means[i] = sum / N;
sum -= values[i - N + 1];
}
return means;
}
Insert cell
subwayRidershipValues = mtaRidershipDB.map(d => +d["Subways: Total Estimated Ridership"])
Insert cell
movingAverages = movingAverage(subwayRidershipValues, 20)
Insert cell
movingIQRs = movingIQR(subwayRidershipValues, 20)
Insert cell
enhancedMTA = mtaDailyRidership.map((d,i) => {
return {
...d,
movingAvg: movingAverages[i],
movingIQR: movingIQRs[i]
}
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Plot.plot({
width,
marks: [
Plot.ruleY([0]),
Plot.lineY(seasonallyAdjustedMTA, {x: "Date", y: "metric", stroke: '#ccc', strokeWidth: .75}),
Plot.lineY(seasonallyAdjustedMTA, {x: "Date", y: "seasonality", stroke: '#4B74D5'})
],
marginLeft: 60
})
Insert cell
Insert cell
Insert cell
nycHurricanes = [
{name: 'Tropical Storm Fay', date: '07/10/2020'},
{name: 'Tropical Storm Isaias', date: '08/04/2020'},
{name: 'Hurricane Laura', date: '08/31/2020'},
{name: 'Hurricane Delta', date: '10/12/2020'},
{name: 'Hurricane Zeta', date: '10/28/2020'},
{name: 'Hurricane Eta', date: '11/13/2020'},
{name: 'Hurricane Elsa', date: '07/09/2021'},
{name: 'Tropical Storm Fred', date: '08/18/2021'},
{name: 'Hurricane Henri', date: '08/22/2021'},
{name: 'Hurricane Ida', date: '09/01/2021'},
{name: 'Hurricane Larry', date: '09/06/2021'},
{name: 'Tropical Storm Wanda', date: '10/26/2021'},
]
Insert cell
USholidays = [
{name: 'Thanksgiving', date: '11/25/2021'},
{name: 'Halloween', date: '10/31/2021'},
{name: 'Christmas', date: '12/25/2020'},
{name: 'Christmas Eve', date: '12/24/2020'},
{name: 'Christmas', date: '12/25/2021'},
{name: 'Christmas Eve', date: '12/24/2021'},
{name: 'Labor Day', date: '09/06/2021'},
{name: 'Labor Day', date: '09/07/2020'},
{name: 'New Years Eve', date: '12/31/2020'},
{name: 'New Years Day', date: '01/01/2021'},
{name: 'New Years Eve', date: '12/31/2021'},
{name: 'New Years Day', date: '01/01/2022'},
{name: 'Memorial Day', date: '05/25/2020'},
{name: 'Memorial Day', date: '05/31/2021'},
{name: 'Memorial Day', date: '05/30/2022'},
{name: 'July 4th', date: '07/04/2020'},
{name: 'July 4th', date: '07/04/2021'},
{name: 'July 4th', date: '07/04/2022'},
]
Insert cell
Plot.plot({
width,
marks: [
Plot.ruleY([0]),
Plot.dotX(nycHurricanes, {
x: d => parseDate(d.date),
y: 0,
r: 5,
fill: "#E8C655",
stroke: "white"
}),
Plot.dotX(USholidays, {
x: d => parseDate(d.date),
y: 0,
r: 5,
fill: "#BC2E2E",
stroke: "white"
}),
Plot.lineY(seasonallyAdjustedMTA, {x: 'Date', y: "metric", stroke: '#ccc', strokeWidth: .75}),
Plot.lineY(seasonallyAdjustedMTA, {x: 'Date', y: "seasonality", stroke: '#4B74D5'})
],
marginLeft: 60
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
ldnGoogleActivity = FileAttachment("google_activity_by_London_Borough.csv").csv({typed: true})
Insert cell
Insert cell
// These are the most populous boroughs according to the 2019 pop est. on Wikipedia: https://en.wikipedia.org/wiki/List_of_London_boroughs
populousLondon = ldnGoogleActivity.filter(d => d.area_name === "Barnet" || d.area_name === "Croydon" || d.area_name === "Newham" || d.area_name === "Ealing" || d.area_name === "Enfield")
Insert cell
// London data is d first, then m, y
parseLdnDate = d3.utcParse('%d/%m/%Y')
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more