An analysis of 5,400,008 ride observations from the 2025 Cyclistic bike-share dataset reveals critical behavioral distinctions between annual members and casual riders. While members represent the majority of system usage, casual riders demonstrate high engagement through significantly longer trip durations and peak usage during weekend and summer periods. Converting these high-volume, long-duration casual riders into full annual members represents the single largest growth opportunity for recurring subscription revenue.
Annual members account for 64.5% of total trips, while casual riders make up 35.5%.
Casual riders average 19.91 minutes per trip, compared to 12.18 minutes for annual members, a 63.5% longer ride duration. Casual riders utilize bikes for leisure and exploration rather than short point-to-point utility commutes.
Member volume peaks on weekdays (Monday to Thursday at 69% to 72% share), driven by routine commuting. Casual rider volume ramps up toward the end of the week (37.1% on Friday) and peaks on weekends (47.3% on Saturday and 45.8% on Sunday).
On seasonal elasticity, Casual rider demand follows a bell-curve distribution, peaking during warm-weather months from June to August at 41.7% to 42.3% respectively. Conversely, annual member share spikes in winter 80% and above in December to February due to resilient, year-round utility riding.
The Cyclistic Bike-Share Case Study is a capstone project as a non requirement for the completion of the Coursera Google Data Analytics Professional Certificate. It simulates a real-world business scenario with the marketing analyst team at Cyclistic,” a fictional bike-share company based in Chicago. The director of marketing believes the company’s future success depends on maximizing the number of annual memberships. He therefore, wants to understand how casual riders and annual members use Cyclistic bikes differently. From the insights, a new marking stategy will be design to convert casual riders into annual membership.
Cyclistic’s financial analysts have determined that annual members are significantly more profitable than casual riders (users who buy single-ride or full-day passes). Rather than targeting all-new customers, the company’s growth strategy centers on converting casual riders into annual members.
The dataset used for this analysis is derived from Divvy-trip data, capturing 12 months of Cyclistic bike-share activity in 2025. The months were merged into one complete dataset. In total, the dataset includes 5,552,994 observations and 13 variables.
A set of four variables, consisting of ride length, day of the week, month, and hour was added to the original dataset making up 17 variables from 13 to extract insights across months, days of the week, and ride durations.
Out of Five million, Five Hundred and Fifty-Two Thousand, Nine Hundred and Ninety-Four (5,552,994) initial observations, One Hundred and Fifty-Two Thousand, Nine Hundred And Eighty-Six (152,986) were filtered out, leaving Five Million, Four Hundred Thousand and Eight (5,400,008) valid records for analysis. Trips lasting one minute or less were removed, as they typically represent false starts, accidental unlocks, or system tests. Similarly, rides exceeding 24 hours (1,440 minutes) were excluded, as these generally correspond to lost or stolen bikes or system errors. This data cleaning process removes anomalies and ensures data integrity for evidence-based decision-making. No distinct observations were found in this data maintaining the validity of this data for onward analysis. The Capstone Bike-Share Trip Data is ROCCC in Reliability, Originality,Comprehensiveness, Current and Cited for its integrity to provide insights for good decision making.
The only software used for this data cleaning, processing, visualization, and analysis was R, selected for its efficient handling of large data sets, advanced data cleaning and processing features, and high-quality, publication-ready data visualizations.
###### GOOGLE DATA ANALYTICS CAPSTONE CASE STUDY ######
########################################################
# loading the packages
library(haven) # for coverting factors as labels
library(tidyverse) # for data manipulation, cleaning, analysis...
library(ggplot2) # For data visualization
library(ggthemes) #for selecting themes
library(sjlabelled) # used for calling out labels of factor
library(labelled) # used for labeling data
library(readxl) # for reading excel data into R studio
library(readr) # for reading data into the R studio such as the csv.
library(glue)# its used for interpolation i.e; inserting variables directly into a text string.
library(patchwork) # for combining two or more plots using the operation signs.
library(lubridate) # for converting dates into years, months, hours and minutes.
library(janitor) # for data cleaning and exploration.
library(data.table) # fast data manipulation of a large data
library(hrbrthemes) # also for choosing themes just like ggthemes
library(psych) # for descriptive analysis in a a data such as mean, median...
library(patchwork) # For combining to or more chat plots
# getting a list of all my monthly file paths.
file_paths <- list.files(path = "C:/Users/HELLO/Desktop/Christian/One Million Coders_Coursera/Coursera_Capstone_Project/Divvy_TripData",
pattern = "*.csv",
full.names = TRUE)
# reading all the 12 data files and merging them into one data frame
trip_data_12 <- file_paths %>%
map_df(~ read_csv(.x))
trip_data_12 <- trip_data_12 %>%
distinct() %>% # this removes the distinct observations in the dataset.
# Add columns for ride length, day of the week, month and hour
mutate( # Calculate ride length in minutes
ride_length_mins = as.numeric(difftime(ended_at, started_at, units = "mins")),
# Extract structural time components
day_of_week = factor(
format(started_at,"%A"),
levels = c( "Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday") ),
month = lubridate::month(started_at, label = TRUE, abbr = TRUE),
hour = as.integer(format(started_at,"%H"))) %>%
# Filter out "false starts" (rides under 60 seconds) or system errors (negative times)
# 3. Remove data quality anomalies:
# Rides that lasted less than 1 minute (potentially accidental unlocks or system tests)
# Rides that lasted longer than 24 hours (usually stolen/lost bikes or system errors)
filter(ride_length_mins >= 1 , ride_length_mins <= 1440)The total ride volume breakdown across Five Million, Four Hundred Thousand and Eight (5,400,008) observations reveals a significant market share gap between Casual and Annual Membership user categories. Let us jump into the insights.
Annual Members account for 64.5% of all completed trips, establishing Annual subscriptions as the foundational baseline of Cyclistic’s daily operations.
Casual Riders represent 35.5% of overall trips, highlighting a massive, active user segment that engages with the service without an ongoing subscription commitment.
# Calculate total rides and percentage breakdown
total_rides <- trip_data_12 |>
count(member_casual) |>
mutate(
percentage = round(n / sum(n) * 100, 1),
label = paste0(member_casual, "\n", percentage, "%")
)
# Creating the donut plot
donut_plot_total_rides <- trip_data_12 |>
count(member_casual) |>
mutate(
percentage = round(n / sum(n) * 100, 1),
label = paste0(member_casual, "\n", percentage, "%")
) |>
ggplot(aes(x = 2, y = n, fill = member_casual)) +
geom_col(width = 1, colour = "white") +
coord_polar(theta = "y") +
xlim(0.5, 2.5) +
# Data labels inside the donut ring
geom_text(
aes(label = paste0(percentage, "%")),
position = position_stack(vjust = 0.5),
colour = "white",
size = 5,
fontface = "bold"
) +
# Center label indicating total aggregate rides
annotate(
"text",
x = 0.5,
y = 0,
label = paste0(
format(sum(total_rides$n), big.mark = ","),
"\nTotal Rides"
),
size = 5,
fontface = "bold"
) +
# Custom brand colors
scale_fill_manual(
values = c(
"casual" = "#10065A",
"member" = "#FF6109"
)
) +
labs(
title = "Total Cyclistic Ride distribution by Rider Type",
fill = "Rider type",
caption = "Source: Cyclistic Bike Share Data (2025)"
) +
theme_void() +
theme(
plot.title = element_text(
face = "bold",
hjust = 0.5, # Centered alignment over the plot
size = 16
),
plot.caption = element_text(
hjust = 1 # Right-aligned caption at the bottom
),
legend.position = "top"
)
# Render plot in document
donut_plot_total_ridesCasual riders spend significantly more time per trip (19.91 minutes) compared to annual members (12.18 minutes) representing a 63.5% longer average ride duration.
trip_data_12 %>%
# grouping the data by member_casual
group_by(member_casual) %>%
# calculating the average duration of the ride length in minutes.
summarise(avg_duration = mean(ride_length_mins)) %>%
# plotting the data by member_casual and everage duration.
ggplot(aes(member_casual,
avg_duration,
fill = member_casual)) +
geom_col(width = 0.6) +
geom_text( aes(label = paste0(round(avg_duration, 2), " min")),
vjust = -0.4, # it shows the position of the label
size = 5)+ # the font size of the label.
scale_fill_manual(values = c("casual" = "#10065A",
"member" = "#FF6109"))+
scale_y_continuous(limits = c(0, 25),
breaks = seq(0, 25, by = 5))+
labs(title = "Average Ride Duration by Rider Type in 2025",
subtitle = "Comparison of average ride trip duration",
caption = "Source: Cyclistic Bike Share Data (2025)",
x = "Rider Type",
y = "Duration in Minutes",
fill = "Rider type") +
theme_minimal(base_size = 12) +
theme(legend.position = "none", # change position of the legend.
plot.title = element_text(face = "bold"))Rider Intent Differences: Annual Members likely use the service for utility and point-to-point commuting (short, consistent, time-sensitive trips averaging ~12 minutes). Casual riders use the service more for leisure, exercise, or exploration, leading to longer single-use sessions, approximately (20 minutes).
Value Perception Gap: Casual riders are already heavy users in terms of duration per trip, meaning they derive high utility from the bikes. However, they likely pay single-ride or daily pass rates, which become expensive for longer, recurring leisure trips.
Nearly Equal Weekend Split. On Saturdays and Sundays, casual riders make up almost half of all active riders on the system.
Annual Members dominate ridership Monday through Thursday, hovering around 69% to 72% of total rides, peaking on Tuesday (71.8%) and Wednesday (71.7%)
Casual rider share ramps up significantly as the week progresses rising from 28.2% on Tuesday to 37.1% on Friday, before peaking on Saturday (47.3%) and Sunday (45.8%)
trip_data_12 |>
group_by(day_of_week, member_casual) |>
summarise(counts = n(),
.groups = "drop") |>
group_by(day_of_week) |>
mutate( percentage = round(counts / sum(counts) * 100, 1)) |>
ggplot(aes(x = day_of_week,
y = percentage,
fill = member_casual))+
geom_col(position = position_dodge(width = 0.8), width = 0.7)+
geom_text(aes(label = paste0(percentage, "%")),
position = position_dodge(width = 0.8),
vjust = -0.3,
size = 4,
fontface = "bold")+
scale_fill_manual(values = c("casual" = "#10065A",
"member" = "#FF6109"))+
scale_y_continuous(limits = c(0,80),
breaks = seq(0, 80, by = 20))+
labs(title = "Distribution of Casual and Member Riders by Day of the Week",
subtitle = "Percentage share of rider type by days of the week",
caption = "Source: Cyclistic Bike Share Data (2025)",
x = "Day of the week",
y = "Percentage of Rides",
fill = "Rider type")+
theme_minimal(base_size = 12) +
theme(legend.position = "top",
plot.title = element_text(face = "bold", hjust = 0),
axis.title = element_text(face = "bold"))There is a clear functional division between Work vs. Leisure. Annual Members primarily use Cyclistic for weekday commuting as their routine, reliable transit. Casual riders however utilizes the platform heavily for weekend leisure, entertainment, and recreation.
The Friday Pivot: Friday marks the transition point (37.1% casual), where casual usage begins ramping up ahead of the weekend.
High Conversion Opportunity Window: Saturdays and Sundays represent the highest concentration of casual riders actively engaging with the system.
Both Annual and Casual Members signals a seasonal trend over the months in 2025. While Casual riders follows a normal distribution curve, Annual members skewed their ride at the ends showing a U-shape or an inverse Bell curve.
On winter dominance, Member shares spikes dramatically during cold-weather months, reaching 82.8% in January, 81.9% in February, and 80.1% in December.
Casual participation builds steadily from March (28.4%) through May (35.9%), before tapering off through September (36.6%) and October (34.1%) to December (19.9%)trip_data_12 |>
group_by(month, member_casual) |>
summarise(counts = n(),
.groups = "drop") |>
group_by(month) |>
mutate( percentage = round(counts / sum(counts) * 100, 1)) |>
ggplot(aes(x = month,
y = percentage,
fill = member_casual))+
geom_col(position = position_dodge(width = 0.8), width = 0.7)+
geom_text(aes(label = paste0(percentage, "%")),
position = position_dodge(width = 0.8),
vjust = -0.3,
size = 2.5,
fontface = "bold")+
scale_fill_manual(values = c("casual" = "#10065A",
"member" = "#FF6109"))+
scale_y_continuous(limits = c(0,90),
breaks = seq(0, 90, by = 10))+
labs(title = "Distribution of Casual and Member Riders by Months",
subtitle = "Monthly ride activity by rider type",
caption = "Source: Cyclistic Bike Share Data (2025)",
x = "Months",
y = "Percentage of Rides",
fill = "Rider type")+
theme_minimal(base_size = 12) +
theme(legend.position = "top",
plot.title = element_text(face = "bold", hjust = 0),
axis.title = element_text(face = "bold"))Seasonality Drives Casual Adoption: Casual riders are weather-sensitive leisure users who heavily utilize the network during late spring, summer, and early autumn.
Resilient Year-Round Core: Annual members rely on the service as essential transportation, maintaining high usage relative to total volume even through harsh winter weather.
The Summer Conversion Window: June through August represents the prime window where casual volume is at its absolute highest, creating the largest target audience for conversion campaigns.
Lowering Acquisition Costs: These casual riders are already familiar with Cyclistic’s app, bike availability, and station network. Converting this existing user base requires significantly less marketing spend compared to acquiring entirely new customers from scratch. Below are the actionable recommendations:
Targeted Value Proposition: Identify the tipping point where single-pass, pay-as-you-go costs exceed the price of an annual membership. Positioning annual memberships as a direct cost-saving alternative for frequent casual riders provides a clear pathway to maximizing recurring revenue.
Tiered Member Benefits for Duration: Introduce annual member perks that cater to longer rides, such as extending the standard limit before extra time fees kick in (e.g., 45 free minutes for members vs. 30 minutes for single passes).
Targeted In-App Prompts: Trigger automated conversion messaging immediately after a casual rider completes a trip exceeding 15–20 minutes, showing them exactly how much money they would have saved on that specific trip as an annual member.
Launch Weekend-Only Membership Tiers: Introduce a tailored “Weekend Pass” or “Leisure Annual Pass” at a lower price point than a full commuting membership. This lowers the barrier to entry for users who do not require weekday commuting benefits.
Launch Seasonal/Summer Membership Promotions: Offer a discounted “Summer Pass” or seasonal annual tier starting in May to capture riders ahead of peak usage, with an option to roll into a discounted full-year membership in autumn.
Casual Riders are High-Value Leisure Users: Casual riders spend over 60% more time on bikes per trip than Annual members. They do not view Cyclistic purely as a transit tool, but as a primary medium for leisure, fitness, and weekend mobility.
Marketing Misalignment: Positioning Annual membership strictly as a weekday commute solution misses the core motivation of casual riders, who engage most heavily during summer weekends.
Prime Conversion Windows: The optimal time to engage casual riders is during the summer peak (June–August) and over weekend periods when casual volume is concentrated.
These insights must not be slept on. The data provides clear, empirical evidence that casual riders represent a high-value, untapped segment waiting to be converted. Sitting on these insights means leaving substantial recurring revenue on the table.