Week 9 Lab: Building interactive spatio-temporal visualizations

CS-GY 6313 - Information Visualization

Ryan Kim

New York University

2026-11-06

Week 9 Lab Overview

User Interface Graphics Library Notebook(s)
observablehq.com D3 Week 9 Lab Notebook

Today’s Lab Activities

Today we will be a exploring the following topics:

  1. Understanding Time and JavaScript/D3
  2. Creating new columns from timestamps
  3. Creating scatterplots with timescales
  4. d3.rollup() and lookup dictionaries
  5. Mapping geodata to NYC taxi trips

Group and Mini Projects: Reminders

Assignment Due Date Details
Data Analysis & Sketches Nov. 3 Data tables and visualization sketches
Mini-Project #1 Nov. 6

Group Project Milestone 2: Deliverables

For each of your domain questions:

  1. Transform the data to extract the information needed
  2. Create a data table showing the results (sample of the processed data)
  3. Sketch the visualization you plan to create

Sketches can be:

  • Hand-drawn (pen and paper, or tablet)
  • Created with drawing software (Figma, Excalidraw, etc.)
  • Generated with data viz tools (Tableau, Matplotlib, Vega-Lite)

Important: It’s okay (and expected!) to refine your questions at this stage based on what you discover in the data.

Timestamps and ISO-8601

  • Problem: Everyone has a different way to encode timestamps. You may see date types from 2020-01-15 to “January 15, 2020” to even crazy ones like 2020-01-15T10:30:00. How do you parse this consistently?
  • Solution: ISO-8601

ISO 8601 tackles this uncertainty by setting out an internationally agreed way to represent dates: YYYY-MM-DD

Therefore, the order of the elements used to express date and time in ISO 8601 is as follows: year, month, day, hour, minutes, seconds, and milliseconds.

For example, September 27, 2022 at 6 p.m. is represented as 2022-09-27 18:00:00.000.

JavaScript and D3 prefer to encode dates in this ISO-8601 format. However, you must consider certain caveats…

Timestamps, JavaScript, and D3

JavaScript: Date() object class

  • This object class expects your timestamp to already be in the ISO-8601 format.
  • this class gives you some nifty functions, such as using .getYear() or .getMonth() to extract the year and month of a timestamp.
  • You can look up the properties and functions of the Date() class here.
// Returns "2020-01-15T15:30Z"
new Date("2020-01-15T10:30:00");

// ERORR: Not ISO-8601 Format; this will return `null`
new Date("15-01-2020");

D3: d3.timeParse()

  • Unlike JavaScript’s Date() object class, D3 lets you define the expected format of your input string.
  • The output is generally the same: it’ll return a Date() object class in the ISO-8601 format.
// Returns "2020-01-15T15:30Z"
const parser1 = d3.timeParse("%Y-%m-%dT%H:%M:%S");
parser1("2020-01-15T10:30:00");

// Will return successfully: "2020-01-15T05:00Z"
const parser2 = d3.timeParse("%d-%m-%Y");
parser2("15-01-2020");


When working with D3 and need to work with time-series data, you are heavily encouraged to use d3.timeParse() to force timestamps into a Date() object with the ISO-8601 format. You’ll know if you’ve implemented it correctly if d3.timeParse() doesn’t return null.

d3.timeParse() and Time Formatting

  • If we want to work with d3.timeParse() then we need to know how to define something like "%d-%m-%Y" to begin with. How do we do that?
  • D3’s time format system uses the same kind of syntax that was first pioneered by C’s strtime function (Python also uses this too, actually). Read more about it here.
Directive Meaning Example
%Y 4-digit year 2025
%m Month number (01–12) 03
%b Abbreviated month name Jan, Feb
%d Day of month (01–31) 09

… and so on.

Lab Exercises #1 and #2: Time Formatting (10-15 mins)

In our Week 9 Lab Notebook, complete the code blocks under Parts 1C and 1D.

  1. In Part 1C, your goal is to return what parser(...) returns for each situation; your job is to modify parser() to fit the timestamp formats given to you.
  2. In Part 1D, your goal is to not only correctly configure parser(...) with the correct timestamp format, but also extract the listed date value.

Application: Why Do All This?

  • Primary Reason: Converting timestamps of varying formats into a stndardized ISO-8601.
  • Secondary Reason: Creating new columns from timestamp columns, for easier management.

Modifying Datasets in JavaScript: Reminders

JavaScript code Description
Array.map() Modifies each element without changing the order or number of items in the Array.
Array.filter() Removes elements of an Array that do not match some criteria, but doesn’t modify the elements.
Array.agg() Condenses large datasets into smaller data types, such as scalar values or smaller arrays.
[...old_data, /* new items */] Create a new Array by adopting values from an existing Array, then appending new items

Lab Exercises #3 and #4: Implementing Time-Series Charts (~20 mins)

In our Week 9 Lab Notebook, we’ve imported the “Daily Climate time series data” dataset, which features climate data from Delhi from 2013 to 2017. The data is imported under the variable climate_data.

  1. Using either Array.map or forEach(), create a new version of the climate data called climate_data_2 that features two new columns:
    • A new “year” column extracted from the existing “date” column, and
    • A new “day_month” column that keeps the day and month of the “date” but sets the year to some constant (e.g. 2020).
  2. Using climate_data_2, produce a simple scatter plot where:
    1. The chart is a 600px wide and 400px tall chart, with margins of 50px on all sides.
    2. Each point represents a single row.
    3. The X-axis represents “date”.
    4. The Y-axis represents “meantemp”.
    5. The color represents “year”.

NYC Taxi Data

We covered the NYC Taxi Open Dataset, which gives us a lot of juicy info about taxi pickups across many years in NYC. Let’s look at one specific subset of that data: the week of Thanksgiving in November, 2024.

  • yellow_tripdata_2024-11_thanksgiving.csv: The taxi data was originally formatted in the .parquet format, which JavaScript and D3 cannot read. We’ve pre-processed the dataset, removed some unnecessary columns, and converted it into a .csv for you.
  • taxi_zones_wgs84.json: The same data repository provides shapefiles for taxi regions in NYC. However, this geodata’s coordinates were localized to the New York State Plane coordinates (EPSG:2263). We converted all coordinates to EPSG:4326 (longitude/latitude) as well as transform it into GeoJSON for you.

d3.Rollup()

Our taxi_data dataset has the weekly taxi pickups and drop-offs for the week of Thanksgiving. Let’s say that we want to aggregate, for the entire week, the total number of pickups and drop-offs that occurred per location in our taxi zones.

To achieve this, we will have to use a special function from D3 called d3.rollup(). It’s a special function that lets you create a JavaScript Map that effectively acts as a dictionary. The general syntax is as follows:

d3.rollup(data,
         v => // the aggregated rows as an array
         d => d.key);

Lab Exercises #5 - #7: Plotting GeoJSON of NYC with Taxi Data (Remainder of the class)

In our Week 9 Lab Notebook, complete the code blocks to plot a geovisualization of NYC’s buroughs that depict the number of taxi pickups and dropoffs.

Goal #1: Use d3.rollup()

Use d3.rollup() to create pickup_lookup and dropoff_lookup. E.g. if you call pickup_lookup.get(QUERY VALUE), it should return the number of pickups in the QUERY_VALUE region.

Goal #2: Generating a <select> Dropdown

Using D3.js, create a dropdown that lets users select between “PickUp” and “DropOff”.

Goal #3: Dynamic Geomap With Zoom Interactivity

Using everything we’ve worked up to so far, plot a GeoJSON map that changes its coloration depending on what the user has selected using the dropdown.