Introduction
Cyclistic has grown as a leading bike-share program in Chicago, serving thousands of users with diverse needs through its network of more than 5,800 bicycles and 600 docking stations. To continue its growth, Cyclistic aims to convert casual riders into annual members. This report provides an in-depth analysis of how annual members and casual riders use Cyclistic bikes differently and offers data-driven recommendations to maximize membership conversions.
This case study follows the data analysis process: Ask, Prepare, Process, Analyze, Share, and Act.
The primary business objective is to answer the following question: How do annual members and casual riders use Cyclistic bikes differently? This analysis will guide marketing strategies to convert casual riders into annual members by identifying behavioral patterns and actionable insights.
Key Stakeholders:
Lily Moreno: Director of Marketing, responsible for campaign development.
Cyclistic Marketing Analytics Team: Analysts (including you) collecting and analyzing data to support the marketing strategy.
Cyclistic Executive Team: Decision-makers who will approve the recommended marketing strategy based on data insights.
Desired Outcome:
Identify behavioral patterns between casual riders and annual members.
Generate actionable insights supported by data visualizations.
Provide strategic recommendations to increase membership conversions.
Data Source Overview:
The data used for this analysis comes from Cyclistic's publicly available historical trip datasets, provided by Motivate International Inc. under a public data license. These datasets represent actual bike-share usage patterns over the past 12 months. The data can be accessed and downloaded from the Cyclistic data repository provided in the case study.
Data Structure and Organization:
File Format: The data is available as CSV files. Each file represents a month's worth of trip data, making up a full year's data when combined.
Data Granularity: Each row in the dataset represents a single bike trip. The columns provide details such as:
ride_id: Unique identifier for each trip.
rideable_type: Type of bike used (standard, electric, or assistive).
started_at & ended_at: Timestamps for trip start and end.
start_station_name & end_station_name: Station details.
member_casual: User type (either member for annual subscribers or casual for single-use and day-pass riders).
Data Integrity and Quality Check (ROCCC Framework):
To ensure the data is reliable for our analysis, we evaluate it against the ROCCC framework:
Reliability: The data originates from the official Cyclistic system managed by Motivate International Inc., a reputable bike-share operator.
Originality: The data is directly sourced from Cyclistic's bike system, ensuring first-hand accuracy.
Comprehensive: The dataset includes essential variables for behavior analysis, such as ride duration, start and end locations, and user type.
Current: The dataset covers the most recent 12 months, making it relevant for current trends and decision-making.
Cited: The data source is clearly cited and publicly accessible under a public license.
Potential Data Quality Issues:
Missing Values: Some rows may have missing station names or coordinates.
Inconsistent Labels: The member_casual column may have legacy labels (Subscriber instead of member).
Outliers: Negative or zero ride durations may appear due to system tests or data errors.
Data Cleaning Requirement:
Convert data types where needed.
Standardize member_casual values.
Remove rows with negative ride durations.
Data Validation Steps Taken:
Inspected the first few rows using head() to ensure structural consistency.
Checked for missing values using sum(is.na()).
Verified data types with str().
Confirmed integrity by comparing the total rows across all monthly datasets.
Data Relevance for Business Task:
This dataset provides the necessary information to analyze user behavior patterns for both casual and annual members. It enables us to:
Identify ride duration patterns.
Examine usage frequency and timing.
Compare bike usage trends across membership types.
Objective:
This section outlines the steps taken to prepare and clean the Cyclistic bike-share dataset, ensuring data quality and readiness for analysis. The goal is to process the data effectively to identify trends in how annual members and casual riders use the service differently.
R Programming Language: Chosen for its powerful data manipulation packages and statistical capabilities.
Packages Used:
tidyverse: For data wrangling and visualization.
conflicted: To handle conflicts between functions from different packages.
dplyr: Core part of tidyverse used for data transformation.
Action:
Imported quarterly datasets using read_csv().
Datasets used: Divvy_Trips_2019_Q1.csv and Divvy_Trips_2020_Q1.csv.
Code Used:
R
Copy code
q1_2019 <- read_csv("Divvy_Trips_2019_Q1.csv")
q1_2020 <- read_csv("Divvy_Trips_2020_Q1.csv")
Actions Taken:
Checked Column Consistency: colnames() was used to compare column names for consistency across datasets.
Renamed Columns: Legacy column names were renamed to match the latest schema.
Converted Data Types: The ride_id and rideable_type columns were converted to character for consistent data types across datasets.
Merged Dataframes: The datasets were combined using bind_rows().
Code Used:
R
Copy code
colnames(q1_2019)
colnames(q1_2020)
q1_2019 <- rename(q1_2019,
ride_id = trip_id,
rideable_type = bikeid,
started_at = start_time,
ended_at = end_time,
start_station_name = from_station_name,
start_station_id = from_station_id,
end_station_name = to_station_name,
end_station_id = to_station_id,
member_casual = usertype)
q1_2019 <- mutate(q1_2019,
ride_id = as.character(ride_id),
rideable_type = as.character(rideable_type))
all_trips <- bind_rows(q1_2019, q1_2020)
Removed Unnecessary Columns:
Columns dropped due to irrelevance and inconsistencies (start_lat, start_lng, end_lat, end_lng, birthyear, gender, and tripduration).
all_trips <- all_trips %>%
select(-c(start_lat, start_lng, end_lat, end_lng, birthyear, gender, "tripduration"))
Standardized Membership Labels:
The member_casual column contained inconsistent labels (Subscriber and Customer).
Replaced with the consistent labels: "member" and "casual".
all_trips <- all_trips %>%
mutate(member_casual = recode(member_casual,
"Subscriber" = "member",
"Customer" = "casual"))
Generated Date-Based Columns for Aggregation:
Extracted the day, month, and year to enable time-based analysis.
all_trips$date <- as.Date(all_trips$started_at)
all_trips$month <- format(as.Date(all_trips$date), "%m")
all_trips$day <- format(as.Date(all_trips$date), "%d")
all_trips$year <- format(as.Date(all_trips$date), "%Y")
all_trips$day_of_week <- format(as.Date(all_trips$date), "%A")
Calculated Ride Duration (ride_length):
Ride duration was computed as the difference between ended_at and started_at.
all_trips$ride_length <- difftime(all_trips$ended_at, all_trips$started_at)
all_trips$ride_length <- as.numeric(as.character(all_trips$ride_length))
Negative Values Removal:
Rides with negative durations were removed, as they indicated test rides or quality control.
Removed Quality Control Entries:
Removed rows where start_station_name == "HQ QR" which was identified as a test station.
all_trips_v2 <- all_trips[!(all_trips$start_station_name == "HQ QR" | all_trips$ride_length < 0),]
Checked Column Consistency: colnames() and str()
NA Handling: NA values were checked but not explicitly mentioned in the code. Consider using:
sum(is.na(all_trips))
Column Names Verified: colnames(all_trips)
Data Types Confirmed: str(all_trips)
Corrected Factor Levels for Days of the Week:
all_trips_v2$day_of_week <- ordered(all_trips_v2$day_of_week, levels=c("Sunday","Monday","Tuesday","Wednesday", "Thursday" "Friday", "Saturday"))
This analysis aims to explore the key behavioral differences between annual members and casual riders using Cyclistic bikes. By examining ride frequency, duration, timing, and station usage patterns, we uncover insights into how these two groups use Cyclistic services. These findings directly address the business question: How do annual members and casual riders use Cyclistic bikes differently?
To understand central tendencies, variability, and outliers in ride length for both user types.
Code used:
mean(all_trips_v2$ride_length)
median(all_trips_v2$ride_length)
max(all_trips_v2$ride_length)
min(all_trips_v2$ride_length)
summary(all_trips_v2$ride_length)
mean(all_trips_v2$ride_length)
[1] 1189.459
> median(all_trips_v2$ride_length)
[1] 539
> max(all_trips_v2$ride_length)
[1] 10632022
> min(all_trips_v2$ride_length)
[1] 1
> summary(all_trips_v2$ride_length)
Min. 1st Qu. Median Mean 3rd Qu. Max.
1 331 539 1189 912 10632022
Key Findings:
Outliers: Extreme values were identified using max() and min() functions, suggesting potential non-standard rides.
Data Consistency: No missing values were identified during summary inspections.
Objective:
Compare how casual riders and annual members use Cyclistic bikes differently by aggregating ride duration data.
Actions Taken:
Calculated the mean, median, maximum, and minimum ride_length for each user group.
Grouped data by both user type (member_casual) and day of the week.
Code Used:
# Summary stats by user type
aggregate(all_trips_v2$ride_length ~ all_trips_v2$member_casual, FUN = mean)
member_casual ride_length
1 casual 5372.7839
2 member 795.2523
aggregate(all_trips_v2$ride_length ~ all_trips_v2$member_casual, FUN = median)
member_casual ride_length
1 casual 1393
2 member 508
Key Insights:
Casual Riders: Longer average ride durations, indicating more recreational use.
Annual Members: Shorter, more consistent ride durations, suggesting frequent but shorter trips, likely for commuting.
To Identify how ride behavior varies by the day of the week for both casual riders and annual members.
Aggregated the data to calculate the average ride duration for each combination of member_casual and day_of_week.
Code used:
aggregate(all_trips_v2$ride_length ~ all_trips_v2$member_casual + all_trips_v2$day_of_week, FUN = mean)
Output- Table:
member_casual day_of_week ride_length
1 casual Sunday 5061.3044
2 member Sunday 972.9383
3 casual Monday 4752.0504
4 member Monday 822.3112
5 casual Tuesday 4561.8039
6 member Tuesday 769.4416
7 casual Wednesday 4480.3724
8 member Wednesday 711.9838
9 casual Thursday 8451.6669
10 member Thursday 707.2093
11 casual Friday 6090.7373
12 member Friday 796.7338
13 casual Saturday 4950.7708
14 member Saturday 974.0730
Key Insights:
Casual Riders: High usage on weekends, indicating recreational use.
Annual Members: Consistent usage throughout the workweek, aligning with commuter behavior.
4.5 Ridership Trends Over Time
Analyze ride frequency and average duration trends across the week to assess usage patterns more deeply.
Actions Taken:
Created a grouped summary using dplyr.
Visualized the distribution using ggplot2.
Code used:
all_trips_v2 %>%
mutate(weekday = wday(started_at, label = TRUE)) %>%
group_by(member_casual, weekday) %>%
summarise(number_of_rides = n(),
average_duration = mean(ride_length)) %>%
arrange(member_casual, weekday)
Output Table:
# A tibble: 14 × 4
# Groups: member_casual [2]
member_casual weekday number_of_rides average_duration
<chr> <ord> <int> <dbl>
1 casual Sun 18652 5061.
2 casual Mon 5591 4752.
3 casual Tue 7311 4562.
4 casual Wed 7690 4480.
5 casual Thu 7147 8452.
6 casual Fri 8013 6091.
7 casual Sat 13473 4951.
8 member Sun 60197 973.
9 member Mon 110430 822.
10 member Tue 127974 769.
11 member Wed 121902 712.
12 member Thu 125228 707.
13 member Fri 115168 797.
14 member Sat 59413 974.
>
4.5.1 Visualization- Number of Rides by Rider Type
Created a bar plot comparing casual and member usage by weekday.
Code Used:
all_trips_v2 %>%
mutate(weekday = wday(started_at, label = TRUE)) %>%
group_by(member_casual, weekday) %>%
summarise(number_of_rides = n(),
average_duration = mean(ride_length)) %>%
ggplot(aes(x = weekday, y = number_of_rides, fill = member_casual)) +
geom_col(position = "dodge") +
labs(title = "Number of Rides by Rider Type",
x = "Day of the Week",
y = "Number of Rides") +
theme_minimal()
The bar plot displays the total number of rides for casual riders and annual members across the days of the week. Each bar is split by rider type, with distinct colors representing casual and member riders.
Key Insights
Casual Riders:
Exhibit a significant preference for weekends (Saturday and Sunday), with ride numbers peaking on these days.
Weekday usage is relatively low, indicating that casual riders may primarily use the service for leisure or recreational purposes.
Annual Members:
Maintain a consistent pattern of usage across all weekdays, with slightly higher activity from Monday to Friday.
This indicates that annual members might rely on Cyclistic bikes for daily commuting or routine travel needs.
Strategic Implications:
Marketing campaigns aimed at converting casual riders into members should emphasize the benefits of weekday rides, such as convenience for daily commuting.
Promotions or incentives can target casual riders during weekends to encourage frequent use, potentially transitioning them into memberships.
Highlighting cost savings for frequent riders and accessibility features for weekday commutes could resonate with casual riders.
4.5.2 Analysis: Average Ride Duration by Rider Type
Objective:
The goal of this analysis is to compare the average duration of bike rides for casual riders and annual members across the days of the week.
Code Used:
all_trips_v2 %>%
mutate(weekday = wday(started_at, label = TRUE)) %>%
group_by(member_casual, weekday) %>%
summarise(number_of_rides = n(),
average_duration = mean(ride_length)) %>%
ggplot(aes(x = weekday, y = average_duration, fill = member_casual)) +
geom_col(position = "dodge") +
labs(title = "Average Ride Duration by Rider Type",
x = "Day of the Week",
y = "Average Duration (Seconds)") +
theme_minimal()
Key Insights :
Significant Difference in Ride Duration:
Casual riders consistently have much longer average ride durations compared to members across all days of the week.
This suggests that casual riders may use the bikes more for leisure or exploratory purposes, taking extended rides.
Consistency for Members:
Annual members show stable and significantly shorter average ride durations throughout the week, averaging around 500-600 seconds (8-10 minutes).
This consistency indicates that members likely use the service for commuting or other short, practical trips.
Identify the frequency of rides across time intervals (e.g., hourly, daily, monthly trends).
Time of Day Analysis:
Aggregate the number of rides taken by hour of the day (hour extracted from started_at).
all_trips_v2 %>%
mutate(hour = lubridate::hour(started_at)) %>%
group_by(member_casual, hour) %>%
summarise(number_of_rides = n()) %>%
ggplot(aes(x = hour, y = number_of_rides, color = member_casual)) +
geom_line(size = 1.2) +
labs(title = "Ride Frequency by Hour", x = "Hour of the Day", y = "Number of Rides")
4.6.1 Visualization: Line Chart
4.6.2 Heatmap of Rides (Time vs. Day)
Understand peak riding hours for both groups.
Use a heatmap showing the count of rides by hour and day of the week.
all_trips_v2 %>%
mutate(hour = lubridate::hour(started_at)) %>%
group_by(day_of_week, hour, member_casual) %>%
summarise(number_of_rides = n()) %>%
ggplot(aes(x = hour, y = day_of_week, fill = number_of_rides)) +
geom_tile() +
facet_wrap(~ member_casual) +
labs(title = "Heatmap of Rides by Hour and Day", fill = "Number of Rides")
Key Findings:
Annual Members:
Primarily use bikes for weekday commuting, with activity peaking during morning (7–9 AM) and evening rush hours (4–6 PM).
Their usage is concentrated on Monday through Friday, with minimal activity on weekends.
Casual Riders:
Ride for leisure or recreational purposes, with higher activity during weekends.
Their usage peaks during midday to early evening hours (10 AM–6 PM), showing more relaxed and flexible timing.
Count the frequency of trips from each station.
Identify the top 10 stations for casual riders and annual members.
all_trips_v2 %>%
group_by(member_casual, start_station_name) %>%
summarise(number_of_rides = n()) %>%
arrange(desc(number_of_rides)) %>%
slice(1:10)
# A tibble: 20 × 3
# Groups: member_casual [2]
member_casual start_station_name number_of_rides
<chr> <chr> <int>
1 casual Streeter Dr & Grand Ave 2749
2 casual Lake Shore Dr & Monroe St 2732
3 casual Shedd Aquarium 1832
4 casual Millennium Park 1406
5 casual Michigan Ave & Oak St 1017
6 casual Michigan Ave & Washington St 839
7 casual Dusable Harbor 832
8 casual Adler Planetarium 827
9 casual Theater on the Lake 795
10 casual Lake Shore Dr & North Blvd 606
11 member Canal St & Adams St 13799
12 member Clinton St & Washington Blvd 13434
13 member Clinton St & Madison St 12891
14 member Kingsbury St & Kinzie St 8720
15 member Columbus Dr & Randolph St 8515
16 member Canal St & Madison St 7957
17 member Franklin St & Monroe St 7010
18 member Michigan Ave & Washington St 6686
19 member Larrabee St & Kingsbury St 6467
20 member Clinton St & Lake St 6439
Strategic Implications:
For Casual Riders:
The high concentration of rides starting near tourist and leisure spots highlights the opportunity for marketing campaigns promoting memberships at these locations. Targeted offers or advertisements placed at these stations could encourage conversion.
For Members:
The data validates that members use bikes as a reliable part of their daily commute. Cyclistic could explore partnership opportunities with businesses in these areas to increase visibility and value for members.
Strategic Implications:
Cyclistic might want to examine whether stations with high casual rider usage experience capacity issues during peak times and allocate resources accordingly.
Implementing digital campaigns or promotions (e.g., discounted annual memberships) specifically at popular casual rider stations could influence conversions.
Compare the busiest times for these stations by user type to optimize station operations and scheduling.
Target Casual Riders with Weekend-to-Weekday Transition Incentives
Insight from Analysis: Casual riders primarily use Cyclistic bikes on weekends for leisure, while annual members use bikes consistently throughout the week, likely for commuting.
Recommendation: Launch a marketing campaign that incentivizes casual riders to use bikes during weekdays. For example, offer discounted weekday rides or a "Weekday Challenge" where casual riders earn rewards for using bikes on weekdays. Highlight the convenience and cost savings of annual memberships for frequent weekday use.
Strategic Impact: This could encourage casual riders to adopt biking as a regular commuting option, increasing the likelihood of converting them to annual members.
Enhance Station Capacity and Visibility at Popular Casual Rider Locations
Insight from Analysis: Casual riders tend to start their rides at popular tourist and leisure spots (e.g., Streeter Dr & Grand Ave, Lake Shore Dr & Monroe St).
Recommendation: Increase bike availability and docking capacity at these high-traffic stations, especially during peak weekend hours. Additionally, place targeted advertisements or promotional materials at these stations to promote annual memberships.
Strategic Impact: Improved station capacity will enhance user experience, while targeted promotions can directly influence casual riders to consider annual memberships.
Leverage Digital Media to Highlight Cost Savings and Convenience for Commuters
Insight from Analysis: Annual members have shorter, more consistent ride durations, suggesting they use bikes for commuting. Casual riders, on the other hand, take longer rides, primarily on weekends.
Recommendation: Use digital media (social media, email campaigns, and in-app notifications) to emphasize the cost savings and convenience of annual memberships for daily commuters. Create content that compares the cost of casual rides versus annual memberships, showcasing the financial benefits of membership.
Strategic Impact: This approach can appeal to casual riders who may not realize the long-term savings of an annual membership, especially if they start using bikes more frequently during weekdays.
The analysis clearly differentiates the behavioral patterns of annual members and casual riders. These findings provide actionable insights to guide Cyclistic’s marketing and operational strategies. By focusing on tailored promotions, resource optimization, and commute-centric enhancements, Cyclistic can achieve its goal of increasing membership conversions and enhancing customer satisfaction.