Introduction
This project is the final challenge for the Google Data Analytics Expert Certificate program.In this project, I will follow the steps of the data analysis process: ask, prepare, process, analyze, and share.
Cyclistic is a bike-share company in Chicago aiming to boost annual memberships. By analyzing bike usage patterns, the goal is to identify differences between casual riders and annual members.
Scenario
I am a junior data analyst working in the marketing analyst team at Cyclistic, a bike-share company in Chicago.
A bike-share program that features more than 5,800 bicycles and 600 docking stations. Since then, the program has grown to a fleet of 5,824 bicycles that are geotracked and locked into a network of 692 stations across Chicago
The majority of riders opt for traditional bikes; about 8% of riders use the assistive options. Cyclistic users are more likely to ride for leisure, but about 30% use the bikes to commute to work each day.
Step 1: Ask Business objective The business objective is to identify trends and patterns in rider behavior, understand how casual and annual members use the service, and uncover opportunities to convert casual riders into members. The goal is to use these insights to develop targeted marketing strategies that drive membership growth.
Business task As an analyst my take is to do following:
Analyze Cyclistic’s historical bike trip data. Identify trends and patterns in how casual riders and annual members use Cyclistic bikes. Understand behavior differences between the two user groups. Identify opportunities to convert casual riders into annual members. Use insights from data analysis to inform Cyclistic’s marketing strategies. Goal: Leverage findings to create targeted campaigns that drive casual riders to become members.
Stakeholders The Stakeholders in this case study include:
Lily Moreno: Director of Marketing, responsible for campaigns to promote Cyclistic.
Cyclistic Marketing Analytics Team: Data analysts responsible for gathering and analyzing data to inform marketing strategies.
Cyclistic Executive Team: Decision-makers who will approve the proposed marketing strategies.
Casual Riders: Non-members who use Cyclistic bikes for single rides or full-day passes.
Annual Members: Regular subscribers who use Cyclistic bikes and represent higher long-term value.
Step 2: Prepare
Data Location: Cyclistic’s historical trip data.
Data Sources: Cyclistic’s past trip data records.
Data Ownership: Currently unidentified.
Data License: Provided by Motivate International Inc. under its license.
Data Structure: Monthly CSV files covering the past year (January to December 2022), with each file containing 13 columns related to trip information.
ROCCC Data Validation:
Reliable: Unknown.
Original: Unverified.
Comprehensive: Includes trip duration, user type, gender, and station names, suitable for analyzing user trends.
Current: Not outdated, covering data from 2013 to 2022.
Cited: Unclear.
Licensing, Privacy, and Security: The dataset has been anonymized, limiting the ability to track repeat riders or identify Chicago residents.
Use Data to answer Business Questions
Analyze trip duration, frequency, ride times, station usage, and bike types to identify patterns and differences between annual members and casual riders. Use these insights to inform marketing strategies.
Problems with Data
The data from 12 trips exists as separate files, so we must merge them into a single aggregate file for easier analysis. Additionally, I removed unnecessary variables, corrected erroneous data, and eliminated any duplicates.
Step 3: Process What tools are you choosing and why?
We need to work with large amounts of data so tools like Excel won’t be able to handle it, that’s why I chose R.
Review of Data
Verifying column names across all 12 original files. Checking for duplicate records. Identifying other data anomalies.
Review Findings:
Some records have a start time greater than the end time. No duplicates were found.
After the initial review, all 12 files were merged into a single data frame. The final dataset contains 5,732,913 rows and 15 columns of character and numeric data..
library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.4.2
## Warning: package 'ggplot2' was built under R version 4.4.2
## Warning: package 'tidyr' was built under R version 4.4.2
## Warning: package 'dplyr' was built under R version 4.4.2
## Warning: package 'lubridate' was built under R version 4.4.2
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.5.1 ✔ tibble 3.2.1
## ✔ lubridate 1.9.4 ✔ tidyr 1.3.1
## ✔ purrr 1.0.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(lubridate)
library(janitor)
## Warning: package 'janitor' was built under R version 4.4.2
##
## Attaching package: 'janitor'
## The following objects are masked from 'package:stats':
##
## chisq.test, fisher.test
library(dplyr)
library(ggplot2)
#Import and View Data_trips
X202201_tripdata <- read_csv("C:/Users/USER/Desktop/R files/Data/202201-tripdata.csv")
## Rows: 103770 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): ride_id, rideable_type, start_station_name, start_station_id, end_...
## dbl (4): start_lat, start_lng, end_lat, end_lng
## dttm (2): started_at, ended_at
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
X202202_tripdata <- read_csv("C:/Users/USER/Desktop/R files/Data/202202-tripdata.csv")
## Rows: 115609 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): ride_id, rideable_type, start_station_name, start_station_id, end_...
## dbl (4): start_lat, start_lng, end_lat, end_lng
## dttm (2): started_at, ended_at
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
X202203_tripdata <- read_csv("C:/Users/USER/Desktop/R files/Data/202203-tripdata.csv")
## Rows: 284042 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): ride_id, rideable_type, start_station_name, start_station_id, end_...
## dbl (4): start_lat, start_lng, end_lat, end_lng
## dttm (2): started_at, ended_at
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
X202204_tripdata <- read_csv("C:/Users/USER/Desktop/R files/Data/202204-tripdata.csv")
## Rows: 371249 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): ride_id, rideable_type, start_station_name, start_station_id, end_...
## dbl (4): start_lat, start_lng, end_lat, end_lng
## dttm (2): started_at, ended_at
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
X202205_tripdata <- read_csv("C:/Users/USER/Desktop/R files/Data/202205-tripdata.csv")
## Rows: 634858 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): ride_id, rideable_type, start_station_name, start_station_id, end_...
## dbl (4): start_lat, start_lng, end_lat, end_lng
## dttm (2): started_at, ended_at
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
X202206_tripdata <- read_csv("C:/Users/USER/Desktop/R files/Data/202206-tripdata.csv")
## Rows: 769204 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): ride_id, rideable_type, start_station_name, start_station_id, end_...
## dbl (4): start_lat, start_lng, end_lat, end_lng
## dttm (2): started_at, ended_at
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
X202207_tripdata <- read_csv("C:/Users/USER/Desktop/R files/Data/202207-tripdata.csv")
## Rows: 823488 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): ride_id, rideable_type, start_station_name, start_station_id, end_...
## dbl (4): start_lat, start_lng, end_lat, end_lng
## dttm (2): started_at, ended_at
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
X202208_tripdata <- read_csv("C:/Users/USER/Desktop/R files/Data/202208-tripdata.csv")
## Rows: 785932 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): ride_id, rideable_type, start_station_name, start_station_id, end_...
## dbl (4): start_lat, start_lng, end_lat, end_lng
## dttm (2): started_at, ended_at
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
X202209_tripdata <- read_csv("C:/Users/USER/Desktop/R files/Data/202209-tripdata.csv")
## Rows: 701339 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): ride_id, rideable_type, start_station_name, start_station_id, end_...
## dbl (4): start_lat, start_lng, end_lat, end_lng
## dttm (2): started_at, ended_at
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
X202210_tripdata <- read_csv("C:/Users/USER/Desktop/R files/Data/202210-tripdata.csv")
## Rows: 558685 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): ride_id, rideable_type, start_station_name, start_station_id, end_...
## dbl (4): start_lat, start_lng, end_lat, end_lng
## dttm (2): started_at, ended_at
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
X202211_tripdata <- read_csv("C:/Users/USER/Desktop/R files/Data/202211-tripdata.csv")
## Rows: 337735 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): ride_id, rideable_type, start_station_name, start_station_id, end_...
## dbl (4): start_lat, start_lng, end_lat, end_lng
## dttm (2): started_at, ended_at
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
X2021122_tripdata <- read_csv("C:/Users/USER/Desktop/R files/Data/2021122-tripdata.csv")
## Rows: 247540 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): ride_id, rideable_type, start_station_name, start_station_id, end_...
## dbl (4): start_lat, start_lng, end_lat, end_lng
## dttm (2): started_at, ended_at
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Data Validation
Combine Data for Easy workflow
data_trips <- bind_rows(X202201_tripdata,X202202_tripdata,X202203_tripdata,X202204_tripdata,X202205_tripdata,X202206_tripdata,X202207_tripdata,X202208_tripdata,X202209_tripdata,X202210_tripdata,X202211_tripdata,X2021122_tripdata)
Check new data after combine
View(data_trips)
colnames(data_trips)
## [1] "ride_id" "rideable_type" "started_at"
## [4] "ended_at" "start_station_name" "start_station_id"
## [7] "end_station_name" "end_station_id" "start_lat"
## [10] "start_lng" "end_lat" "end_lng"
## [13] "member_casual"
nrow(data_trips)
## [1] 5733451
dim(data_trips)
## [1] 5733451 13
head(data_trips)
## # A tibble: 6 × 13
## ride_id rideable_type started_at ended_at
## <chr> <chr> <dttm> <dttm>
## 1 C2F7DD78E82EC875 electric_bike 2022-01-13 11:59:47 2022-01-13 12:02:44
## 2 A6CF8980A652D272 electric_bike 2022-01-10 08:41:56 2022-01-10 08:46:17
## 3 BD0F91DFF741C66D classic_bike 2022-01-25 04:53:40 2022-01-25 04:58:01
## 4 CBB80ED419105406 classic_bike 2022-01-04 00:18:04 2022-01-04 00:33:00
## 5 DDC963BFDDA51EEA classic_bike 2022-01-20 01:31:10 2022-01-20 01:37:12
## 6 A39C6F6CC0586C0B classic_bike 2022-01-11 18:48:09 2022-01-11 18:51:31
## # ℹ 9 more variables: start_station_name <chr>, start_station_id <chr>,
## # end_station_name <chr>, end_station_id <chr>, start_lat <dbl>,
## # start_lng <dbl>, end_lat <dbl>, end_lng <dbl>, member_casual <chr>
tail(data_trips)
## # A tibble: 6 × 13
## ride_id rideable_type started_at ended_at
## <chr> <chr> <dttm> <dttm>
## 1 92BBAB97D1683D69 electric_bike 2021-12-24 15:42:09 2021-12-24 19:29:35
## 2 847431F3D5353AB7 electric_bike 2021-12-12 13:36:55 2021-12-12 13:56:08
## 3 CF407BBC3B9FAD63 electric_bike 2021-12-06 19:37:50 2021-12-06 19:44:51
## 4 60BB69EBF5440E92 electric_bike 2021-12-02 08:57:04 2021-12-02 09:05:21
## 5 C414F654A28635B8 electric_bike 2021-12-13 09:00:26 2021-12-13 09:14:39
## 6 37AC57E34B2E7E97 classic_bike 2021-12-13 08:45:32 2021-12-13 08:49:09
## # ℹ 9 more variables: start_station_name <chr>, start_station_id <chr>,
## # end_station_name <chr>, end_station_id <chr>, start_lat <dbl>,
## # start_lng <dbl>, end_lat <dbl>, end_lng <dbl>, member_casual <chr>
str(data_trips)
## spc_tbl_ [5,733,451 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ ride_id : chr [1:5733451] "C2F7DD78E82EC875" "A6CF8980A652D272" "BD0F91DFF741C66D" "CBB80ED419105406" ...
## $ rideable_type : chr [1:5733451] "electric_bike" "electric_bike" "classic_bike" "classic_bike" ...
## $ started_at : POSIXct[1:5733451], format: "2022-01-13 11:59:47" "2022-01-10 08:41:56" ...
## $ ended_at : POSIXct[1:5733451], format: "2022-01-13 12:02:44" "2022-01-10 08:46:17" ...
## $ start_station_name: chr [1:5733451] "Glenwood Ave & Touhy Ave" "Glenwood Ave & Touhy Ave" "Sheffield Ave & Fullerton Ave" "Clark St & Bryn Mawr Ave" ...
## $ start_station_id : chr [1:5733451] "525" "525" "TA1306000016" "KA1504000151" ...
## $ end_station_name : chr [1:5733451] "Clark St & Touhy Ave" "Clark St & Touhy Ave" "Greenview Ave & Fullerton Ave" "Paulina St & Montrose Ave" ...
## $ end_station_id : chr [1:5733451] "RP-007" "RP-007" "TA1307000001" "TA1309000021" ...
## $ start_lat : num [1:5733451] 42 42 41.9 42 41.9 ...
## $ start_lng : num [1:5733451] -87.7 -87.7 -87.7 -87.7 -87.6 ...
## $ end_lat : num [1:5733451] 42 42 41.9 42 41.9 ...
## $ end_lng : num [1:5733451] -87.7 -87.7 -87.7 -87.7 -87.6 ...
## $ member_casual : chr [1:5733451] "casual" "casual" "member" "casual" ...
## - attr(*, "spec")=
## .. cols(
## .. ride_id = col_character(),
## .. rideable_type = col_character(),
## .. started_at = col_datetime(format = ""),
## .. ended_at = col_datetime(format = ""),
## .. start_station_name = col_character(),
## .. start_station_id = col_character(),
## .. end_station_name = col_character(),
## .. end_station_id = col_character(),
## .. start_lat = col_double(),
## .. start_lng = col_double(),
## .. end_lat = col_double(),
## .. end_lng = col_double(),
## .. member_casual = col_character()
## .. )
## - attr(*, "problems")=<externalptr>
summary(data_trips)
## ride_id rideable_type started_at
## Length:5733451 Length:5733451 Min. :2021-12-01 00:00:01.00
## Class :character Class :character 1st Qu.:2022-05-17 12:04:44.50
## Mode :character Mode :character Median :2022-07-13 22:04:44.00
## Mean :2022-07-06 05:55:33.92
## 3rd Qu.:2022-09-07 17:55:40.00
## Max. :2022-11-30 23:56:11.00
##
## ended_at start_station_name start_station_id
## Min. :2021-12-01 00:02:40.00 Length:5733451 Length:5733451
## 1st Qu.:2022-05-17 12:27:04.00 Class :character Class :character
## Median :2022-07-13 22:22:06.00 Mode :character Mode :character
## Mean :2022-07-06 06:14:59.07
## 3rd Qu.:2022-09-07 18:11:41.00
## Max. :2022-12-01 11:45:53.00
##
## end_station_name end_station_id start_lat start_lng
## Length:5733451 Length:5733451 Min. :41.64 Min. :-87.84
## Class :character Class :character 1st Qu.:41.88 1st Qu.:-87.66
## Mode :character Mode :character Median :41.90 Median :-87.64
## Mean :41.90 Mean :-87.65
## 3rd Qu.:41.93 3rd Qu.:-87.63
## Max. :45.64 Max. :-73.80
##
## end_lat end_lng member_casual
## Min. : 0.00 Min. :-88.14 Length:5733451
## 1st Qu.:41.88 1st Qu.:-87.66 Class :character
## Median :41.90 Median :-87.64 Mode :character
## Mean :41.90 Mean :-87.65
## 3rd Qu.:41.93 3rd Qu.:-87.63
## Max. :42.37 Max. : 0.00
## NA's :5874 NA's :5874
“Filter Out Invalid Rides: Started At After Ended At”
data_trips <- data_trips %>%
filter(started_at < ended_at)
data_trips <- distinct(data_trips)
“Split Date into Components: Date, Day, Month, Year”
data_trips$date <- as.Date(data_trips$started_at) #The default format is yyyy-mm-dd
data_trips$month <- format(as.Date(data_trips$date), "%m")
data_trips$day <- format(as.Date(data_trips$date), "%d")
data_trips$year <- format(as.Date(data_trips$date), "%Y")
data_trips$day_of_week <- format(as.Date(data_trips$date), "%A")
“Include Ride Length Column”
data_trips$ride_length <- difftime(data_trips$ended_at, data_trips$started_at)
“Examine Column Structure”
str(data_trips)
## tibble [5,732,913 × 19] (S3: tbl_df/tbl/data.frame)
## $ ride_id : chr [1:5732913] "C2F7DD78E82EC875" "A6CF8980A652D272" "BD0F91DFF741C66D" "CBB80ED419105406" ...
## $ rideable_type : chr [1:5732913] "electric_bike" "electric_bike" "classic_bike" "classic_bike" ...
## $ started_at : POSIXct[1:5732913], format: "2022-01-13 11:59:47" "2022-01-10 08:41:56" ...
## $ ended_at : POSIXct[1:5732913], format: "2022-01-13 12:02:44" "2022-01-10 08:46:17" ...
## $ start_station_name: chr [1:5732913] "Glenwood Ave & Touhy Ave" "Glenwood Ave & Touhy Ave" "Sheffield Ave & Fullerton Ave" "Clark St & Bryn Mawr Ave" ...
## $ start_station_id : chr [1:5732913] "525" "525" "TA1306000016" "KA1504000151" ...
## $ end_station_name : chr [1:5732913] "Clark St & Touhy Ave" "Clark St & Touhy Ave" "Greenview Ave & Fullerton Ave" "Paulina St & Montrose Ave" ...
## $ end_station_id : chr [1:5732913] "RP-007" "RP-007" "TA1307000001" "TA1309000021" ...
## $ start_lat : num [1:5732913] 42 42 41.9 42 41.9 ...
## $ start_lng : num [1:5732913] -87.7 -87.7 -87.7 -87.7 -87.6 ...
## $ end_lat : num [1:5732913] 42 42 41.9 42 41.9 ...
## $ end_lng : num [1:5732913] -87.7 -87.7 -87.7 -87.7 -87.6 ...
## $ member_casual : chr [1:5732913] "casual" "casual" "member" "casual" ...
## $ date : Date[1:5732913], format: "2022-01-13" "2022-01-10" ...
## $ month : chr [1:5732913] "01" "01" "01" "01" ...
## $ day : chr [1:5732913] "13" "10" "25" "04" ...
## $ year : chr [1:5732913] "2022" "2022" "2022" "2022" ...
## $ day_of_week : chr [1:5732913] "Thursday" "Monday" "Tuesday" "Tuesday" ...
## $ ride_length : 'difftime' num [1:5732913] 177 261 261 896 ...
## ..- attr(*, "units")= chr "secs"
“Transform ‘ride_length’ from Factor to Numeric”
data_trips$ride_length <- as.numeric(as.character(data_trips$ride_length))
“Cleanse Data”
clean_data_trips <- data_trips[!data_trips$ride_length<0,]
Save the clean data
write.csv(data_trips, file = "cleaned_data_trips.csv", row.names = FALSE)
Step 4: Analyze Descriptive Analysis Summary Statistics of ride length (in seconds) for both casual and member riders:
summary(clean_data_trips$ride_length)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1 350 618 1165 1110 2483235
Compare members and casual users Compare of mean, median, max and min
aggregate(clean_data_trips$ride_length ~ clean_data_trips$member_casual, FUN = mean)
## clean_data_trips$member_casual clean_data_trips$ride_length
## 1 casual 1746.6957
## 2 member 762.5879
aggregate(clean_data_trips$ride_length ~ clean_data_trips$member_casual, FUN = median)
## clean_data_trips$member_casual clean_data_trips$ride_length
## 1 casual 783
## 2 member 530
aggregate(clean_data_trips$ride_length ~ clean_data_trips$member_casual, FUN = max)
## clean_data_trips$member_casual clean_data_trips$ride_length
## 1 casual 2483235
## 2 member 93594
aggregate(clean_data_trips$ride_length ~ clean_data_trips$member_casual, FUN = min)
## clean_data_trips$member_casual clean_data_trips$ride_length
## 1 casual 1
## 2 member 1
Average duration per rider type sorted by day of the week
aggregate(clean_data_trips$ride_length ~ clean_data_trips$member_casual + clean_data_trips$day_of_week, FUN = mean)
## clean_data_trips$member_casual clean_data_trips$day_of_week
## 1 casual Friday
## 2 member Friday
## 3 casual Monday
## 4 member Monday
## 5 casual Saturday
## 6 member Saturday
## 7 casual Sunday
## 8 member Sunday
## 9 casual Thursday
## 10 member Thursday
## 11 casual Tuesday
## 12 member Tuesday
## 13 casual Wednesday
## 14 member Wednesday
## clean_data_trips$ride_length
## 1 1667.4773
## 2 750.0528
## 3 1754.0790
## 4 736.4578
## 5 1952.2200
## 6 849.0936
## 7 2045.2010
## 8 842.9611
## 9 1534.5322
## 10 738.0395
## 11 1558.7622
## 12 728.9139
## 13 1482.1735
## 14 724.0015
summary(clean_data_trips)
## ride_id rideable_type started_at
## Length:5732913 Length:5732913 Min. :2021-12-01 00:00:01.00
## Class :character Class :character 1st Qu.:2022-05-17 12:02:48.00
## Mode :character Mode :character Median :2022-07-13 22:02:28.00
## Mean :2022-07-06 05:53:17.42
## 3rd Qu.:2022-09-07 17:54:49.00
## Max. :2022-11-30 23:56:11.00
##
## ended_at start_station_name start_station_id
## Min. :2021-12-01 00:02:40.00 Length:5732913 Length:5732913
## 1st Qu.:2022-05-17 12:24:31.00 Class :character Class :character
## Median :2022-07-13 22:19:43.00 Mode :character Mode :character
## Mean :2022-07-06 06:12:42.82
## 3rd Qu.:2022-09-07 18:10:35.00
## Max. :2022-12-01 11:45:53.00
##
## end_station_name end_station_id start_lat start_lng
## Length:5732913 Length:5732913 Min. :41.64 Min. :-87.84
## Class :character Class :character 1st Qu.:41.88 1st Qu.:-87.66
## Mode :character Mode :character Median :41.90 Median :-87.64
## Mean :41.90 Mean :-87.65
## 3rd Qu.:41.93 3rd Qu.:-87.63
## Max. :45.64 Max. :-73.80
##
## end_lat end_lng member_casual date
## Min. : 0.00 Min. :-88.14 Length:5732913 Min. :2021-12-01
## 1st Qu.:41.88 1st Qu.:-87.66 Class :character 1st Qu.:2022-05-17
## Median :41.90 Median :-87.64 Mode :character Median :2022-07-13
## Mean :41.90 Mean :-87.65 Mean :2022-07-05
## 3rd Qu.:41.93 3rd Qu.:-87.63 3rd Qu.:2022-09-07
## Max. :42.37 Max. : 0.00 Max. :2022-11-30
## NA's :5874 NA's :5874
## month day year day_of_week
## Length:5732913 Length:5732913 Length:5732913 Length:5732913
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## ride_length
## Min. : 1
## 1st Qu.: 350
## Median : 618
## Mean : 1165
## 3rd Qu.: 1110
## Max. :2483235
##
Step 5: Share Visualizations Plot: Number of Rides by Rider Type
clean_data_trips %>%
group_by(member_casual, day_of_week) %>%
summarise(number_of_ride = n(),
avgerage_duration = mean(ride_length))%>%
arrange(member_casual, day_of_week) %>%
ggplot(aes(x = day_of_week, y = number_of_ride, fill = member_casual)) +
geom_col(position = "dodge") +
labs(title = "Number of Rides: Casual vs. Member Riders")
## `summarise()` has grouped output by 'member_casual'. You can override using the
## `.groups` argument.
Day by Rider Type
clean_data_trips %>%
group_by(member_casual, day_of_week) %>%
summarise(number_of_ride = n()
,average_duration = mean(ride_length)) %>%
arrange(member_casual,day_of_week) %>%
ggplot(aes(x = day_of_week, y = average_duration, fill = member_casual)) +
geom_col(position = "dodge") +
labs(title = "Average Duration: Casual vs. Member Riders")
## `summarise()` has grouped output by 'member_casual'. You can override using the
## `.groups` argument.
Step 6: Act
Our analysis provided key insights for converting casual riders into members through data-driven strategies.
First, Casual riders are more active on weekends. For example, 60% of casual rides occur on Saturdays and Sundays. Offer membership promotions during these peak days.
Casual riders average 45 minutes per ride, 30% longer than members (35 minutes). Create membership plans with perks for longer rides, like free extra minutes after 40-minute rides.
Next, Offer weekend-only membership discounts to casual riders, who make 70% of their rides during the weekend.
Focus on improving weekday commute benefits for members, especially in peak months like July and August.
Finally, casual riders predominantly use dock bikes, highlighting the need to optimize dock bike availability to avoid shortages or excesses.