In Lab 3, I explored Citi Bike ridership patterns using a partial January 2025 dataset, focusing on differences between classic and electric bike use, popular starting stations, and ride duration. Those visualizations showed differences among stations but did not show where the stations were located. This report extends that work by mapping Citi Bike member rides originating at stations across New York City. The interactive map uses circle size to represent the number of member rides starting at each station, highlights the ten busiest stations in this dataset, and provides a breakdown of classic and electric bike trips when a marker is clicked. The goal is to add geographic context to the station-level patterns identified in Lab 3.
Scope: This report uses only
202501-citibike-tripdata_1.csv, the same partial
January 2025 file analyzed in Lab 3. Counts are not totals for the
full month, and a starting station is not necessarily a rider’s home or
final destination.
The data come from Citi Bike System Data. Each record represents one trip. I filtered to member rides with usable starting station identifiers, names, and coordinates, then summarized trips by starting station ID. For each station, I calculated total rides, classic-bike rides, electric-bike rides, and median recorded starting coordinates. Summarizing the trip records produces a much smaller dataset that can be displayed efficiently in an interactive map.
library(here)
library(tidyverse)
library(leaflet)
library(scales)
library(htmltools)
# Use the same partial January file as Lab 3.
janCitiBike <- read_csv(
here("data", "202501-citibike-tripdata_1.csv"),
show_col_types = FALSE
)
# Keep member trips with usable station information and coordinates.
memberTrips <- janCitiBike %>%
filter(
member_casual == "member",
!is.na(start_station_id),
!is.na(start_station_name),
start_station_name != "",
!is.na(start_lat),
!is.na(start_lng),
between(start_lat, 40, 41.5),
between(start_lng, -75, -72)
)
# Aggregate individual rides into one record per starting station.
stationSummary <- memberTrips %>%
group_by(start_station_id) %>%
summarise(
station_name = first(start_station_name),
latitude = median(start_lat),
longitude = median(start_lng),
total_rides = n(),
classic_rides = sum(rideable_type == "classic_bike", na.rm = TRUE),
electric_rides = sum(rideable_type == "electric_bike", na.rm = TRUE),
.groups = "drop"
) %>%
arrange(desc(total_rides)) %>%
mutate(
rank = row_number(),
station_group = if_else(rank <= 10, "Top 10 stations", "Other stations")
)
# Confirm the number of mapped stations and rides.
tibble(
mapped_stations = nrow(stationSummary),
mapped_member_rides = sum(stationSummary$total_rides),
excluded_member_rides = sum(janCitiBike$member_casual == "member", na.rm = TRUE) -
sum(stationSummary$total_rides)
)
## # A tibble: 1 Ă— 3
## mapped_stations mapped_member_rides excluded_member_rides
## <int> <int> <int>
## 1 2144 896946 360
The table below identifies the ten busiest member starting stations in this partial January file. These are the stations highlighted on the map.
stationSummary %>%
slice_head(n = 10) %>%
transmute(
Station = station_name,
`Total member rides` = comma(total_rides),
`Classic bike rides` = comma(classic_rides),
`Electric bike rides` = comma(electric_rides)
) %>%
knitr::kable(caption = "Ten busiest member starting stations in the analyzed partial January 2025 file.")
| Station | Total member rides | Classic bike rides | Electric bike rides |
|---|---|---|---|
| W 21 St & 6 Ave | 3,929 | 1,476 | 2,453 |
| Lafayette St & E 8 St | 3,168 | 1,225 | 1,943 |
| Pier 61 at Chelsea Piers | 3,145 | 1,061 | 2,084 |
| W 31 St & 7 Ave | 3,105 | 958 | 2,147 |
| 11 Ave & W 41 St | 3,074 | 736 | 2,338 |
| E 33 St & 1 Ave | 2,855 | 928 | 1,927 |
| 9 Ave & W 33 St | 2,758 | 841 | 1,917 |
| Ave A & E 14 St | 2,727 | 1,320 | 1,407 |
| University Pl & E 14 St | 2,715 | 836 | 1,879 |
| Broadway & E 14 St | 2,706 | 949 | 1,757 |
I created the map using the leaflet package. Each circle
marks a starting station, with larger circles representing more member
rides. Orange circles highlight the ten busiest
stations in this dataset; blue circles represent the
other stations. Hover over a circle to see its station name and click to
view the ride counts by bike type. The Esri WorldTopoMap basemap
provides context for roads and neighborhoods. The basemap requires an
internet connection and is subject to the provider’s access
policies.
# Prepare HTML popups and hover labels.
stationMap <- stationSummary %>%
mutate(
safe_name = htmlEscape(station_name),
popup_info = paste0(
"<b>", safe_name, "</b>",
"<br>Total member rides: ", comma(total_rides),
"<br>Classic bike rides: ", comma(classic_rides),
"<br>Electric bike rides: ", comma(electric_rides)
),
hover_label = paste0(station_name, ": ", comma(total_rides), " rides"),
marker_color = if_else(rank <= 10, "#D95F02", "#2166AC"),
# Square-root scaling keeps very busy stations from dominating the map.
marker_radius = pmax(3, pmin(13, sqrt(total_rides) / 7))
)
citibikeMap <- leaflet(stationMap, width = "100%", height = 620) %>%
addProviderTiles(providers$Esri.WorldTopoMap) %>%
setView(lng = -73.98, lat = 40.73, zoom = 11) %>%
addCircleMarkers(
lng = ~longitude,
lat = ~latitude,
radius = ~marker_radius,
color = ~marker_color,
fillColor = ~marker_color,
fillOpacity = 0.65,
opacity = 0.85,
weight = 1,
popup = ~popup_info,
label = ~hover_label,
labelOptions = labelOptions(direction = "auto")
) %>%
addLegend(
position = "bottomright",
colors = c("#D95F02", "#2166AC"),
labels = c("10 busiest stations", "Other stations"),
title = "Station category",
opacity = 0.9
)
citibikeMap
Figure 1. Interactive map of Citi Bike member ride origins at starting stations in a partial January 2025 trip file. Circle size represents the number of recorded member trips starting at each station; orange indicates the ten highest-count stations in this file. Select a marker for the classic/electric bike breakdown. Source: Citi Bike System Data; basemap: Esri WorldTopoMap.
The ten busiest starting stations in this dataset are concentrated in Manhattan, particularly around Chelsea, Midtown, and Union Square. W 21 St & 6 Ave had the highest number of member rides (3,929), followed by Lafayette St & E 8 St (3,168). This suggests that ridership activity is not evenly distributed across the mapped stations, although the partial dataset and differences in station capacity limit broader conclusions. The interactive map adds a geographic dimension to the station rankings from Lab 3. Rather than viewing the busiest stations only as bars in a chart, readers can locate them within New York City and compare their activity with nearby stations. The marker sizes communicate differences in the number of member trips, while the popups make it possible to inspect how classic and electric bike use contributes to each station’s total.
The map is exploratory rather than a measure of neighborhood demand or access. Stations differ in capacity and surroundings, and this dataset records trip origins rather than the number of unique riders. The analysis also uses only one portion of January 2025, so patterns should not be generalized to the entire month or year. A future extension could compare different months or normalize ride counts by station capacity to examine geographic variation more fairly.
Citi Bike. (2025). Citi Bike system data. https://citibikenyc.com/system-data
Esri. WorldTopoMap basemap (accessed through the Leaflet providers interface).