This is the report on Citi Bike NYC a bike sharing system for New York City (USA). We examine the factors associated with the recent behaviours of Citi Bike users answering the following research question.
This approach will enable the Citi Bike executives to gain valuable insights into the factors associated with the recent behaviors of Citi Bike users, facilitating informed decision-making and strategic planning.
## ride_id started_at ended_at
## 1 159C90AB46D5631B 2023-04-01 17:24:06 2023-04-01 17:46:10
## 2 4349D44670885CED 2023-04-13 15:09:12 2023-04-13 15:16:11
## 3 55EB4E8AF45439D2 2023-04-28 15:17:00 2023-04-28 15:26:09
## 4 3133FC58AA938D08 2023-04-23 16:18:49 2023-04-23 17:10:50
## 5 BF2DD897668DA1AC 2023-04-21 15:42:08 2023-04-21 15:47:04
## 6 FF558338220E455B 2023-04-21 22:21:17 2023-04-21 22:26:53
## start_station_name start_station_id end_station_name
## 1 Hoyt St & Warren St 4330.09 Washington St & Laight St
## 2 E 25 St & 2 Ave 6046.02 E 47 St & 2 Ave
## 3 3 Ave & E 62 St 6762.04 W 55 St & 6 Ave
## 4 Washington Pl & 6 Ave 5838.09 E 56 St & 3 Ave
## 5 3 Ave & E 62 St 6762.04 W 55 St & 6 Ave
## 6 Evergreen Ave & Noll St 4873.08 Stockholm St & Wilson Ave
## end_station_id start_lat start_lng end_lat end_lng member_casual
## 1 5509.02 40.68429 -73.98894 40.72231 -74.01060 member
## 2 6498.10 40.73913 -73.97974 40.75323 -73.97033 member
## 3 6809.09 40.76313 -73.96527 40.76319 -73.97843 member
## 4 6691.11 40.73224 -74.00026 40.75935 -73.96760 member
## 5 6809.09 40.76311 -73.96581 40.76319 -73.97843 member
## 6 4824.03 40.70106 -73.93318 40.69930 -73.92304 member
## day_period classic_bike electric_bike docked_bike
## 1 afternoon 22.07 NA NA
## 2 afternoon NA 6.98 NA
## 3 afternoon NA 9.15 NA
## 4 afternoon 52.02 NA NA
## 5 afternoon 4.93 NA NA
## 6 night NA 5.60 NA
# Combine bike type columns into a single column
citi_bike_data_bike <- citi_bike_data %>%
mutate(bike_type = case_when(
!is.na(classic_bike) ~ "classic",
!is.na(electric_bike) ~ "electric",
!is.na(docked_bike) ~ "docked"
)) %>%
# Remove the individual bike type columns
select(-classic_bike, -electric_bike, -docked_bike)
# Check the modification
head(citi_bike_data_bike)
## ride_id started_at ended_at
## 1 159C90AB46D5631B 2023-04-01 17:24:06 2023-04-01 17:46:10
## 2 4349D44670885CED 2023-04-13 15:09:12 2023-04-13 15:16:11
## 3 55EB4E8AF45439D2 2023-04-28 15:17:00 2023-04-28 15:26:09
## 4 3133FC58AA938D08 2023-04-23 16:18:49 2023-04-23 17:10:50
## 5 BF2DD897668DA1AC 2023-04-21 15:42:08 2023-04-21 15:47:04
## 6 FF558338220E455B 2023-04-21 22:21:17 2023-04-21 22:26:53
## start_station_name start_station_id end_station_name
## 1 Hoyt St & Warren St 4330.09 Washington St & Laight St
## 2 E 25 St & 2 Ave 6046.02 E 47 St & 2 Ave
## 3 3 Ave & E 62 St 6762.04 W 55 St & 6 Ave
## 4 Washington Pl & 6 Ave 5838.09 E 56 St & 3 Ave
## 5 3 Ave & E 62 St 6762.04 W 55 St & 6 Ave
## 6 Evergreen Ave & Noll St 4873.08 Stockholm St & Wilson Ave
## end_station_id start_lat start_lng end_lat end_lng member_casual
## 1 5509.02 40.68429 -73.98894 40.72231 -74.01060 member
## 2 6498.10 40.73913 -73.97974 40.75323 -73.97033 member
## 3 6809.09 40.76313 -73.96527 40.76319 -73.97843 member
## 4 6691.11 40.73224 -74.00026 40.75935 -73.96760 member
## 5 6809.09 40.76311 -73.96581 40.76319 -73.97843 member
## 6 4824.03 40.70106 -73.93318 40.69930 -73.92304 member
## day_period bike_type
## 1 afternoon classic
## 2 afternoon electric
## 3 afternoon electric
## 4 afternoon classic
## 5 afternoon classic
## 6 night electric
# Convert the 'started_at' and 'ended_at' columns to POSIXct format
citi_bike_data_bike$started_at <- as.POSIXct(citi_bike_data_bike$started_at, format = "%Y-%m-%d %H:%M:%S")
citi_bike_data_bike$ended_at <- as.POSIXct(citi_bike_data_bike$ended_at, format = "%Y-%m-%d %H:%M:%S")
# Calculate the ride duration in seconds
citi_bike_data_bike <- citi_bike_data_bike %>%
mutate(ride_duration = as.numeric(difftime(ended_at, started_at, units = "secs")))
head(citi_bike_data_bike)
## ride_id started_at ended_at
## 1 159C90AB46D5631B 2023-04-01 17:24:06 2023-04-01 17:46:10
## 2 4349D44670885CED 2023-04-13 15:09:12 2023-04-13 15:16:11
## 3 55EB4E8AF45439D2 2023-04-28 15:17:00 2023-04-28 15:26:09
## 4 3133FC58AA938D08 2023-04-23 16:18:49 2023-04-23 17:10:50
## 5 BF2DD897668DA1AC 2023-04-21 15:42:08 2023-04-21 15:47:04
## 6 FF558338220E455B 2023-04-21 22:21:17 2023-04-21 22:26:53
## start_station_name start_station_id end_station_name
## 1 Hoyt St & Warren St 4330.09 Washington St & Laight St
## 2 E 25 St & 2 Ave 6046.02 E 47 St & 2 Ave
## 3 3 Ave & E 62 St 6762.04 W 55 St & 6 Ave
## 4 Washington Pl & 6 Ave 5838.09 E 56 St & 3 Ave
## 5 3 Ave & E 62 St 6762.04 W 55 St & 6 Ave
## 6 Evergreen Ave & Noll St 4873.08 Stockholm St & Wilson Ave
## end_station_id start_lat start_lng end_lat end_lng member_casual
## 1 5509.02 40.68429 -73.98894 40.72231 -74.01060 member
## 2 6498.10 40.73913 -73.97974 40.75323 -73.97033 member
## 3 6809.09 40.76313 -73.96527 40.76319 -73.97843 member
## 4 6691.11 40.73224 -74.00026 40.75935 -73.96760 member
## 5 6809.09 40.76311 -73.96581 40.76319 -73.97843 member
## 6 4824.03 40.70106 -73.93318 40.69930 -73.92304 member
## day_period bike_type ride_duration
## 1 afternoon classic 1324
## 2 afternoon electric 419
## 3 afternoon electric 549
## 4 afternoon classic 3121
## 5 afternoon classic 296
## 6 night electric 336
# Remove the specified variables
citi_bike_data_bike<-citi_bike_data_bike %>%
select(-started_at, -ended_at, -start_station_id, -end_station_id, -start_lat, -start_lng, -end_lat, -end_lng)
# Set seed for reproducibility
student_id <- 67816
set.seed(student_id)
# Randomly sample 50,000 rows
citi_bike_data_bike_sample <- citi_bike_data_bike %>%
sample_n(50000)
# Save the sampled dataset
write.csv(citi_bike_data_bike_sample, "Citi-Bike-NYC-Sample.csv", row.names = FALSE)
# Display the first 6 rows of the sampled dataset
head(citi_bike_data_bike_sample)
## ride_id start_station_name end_station_name
## 1 77C141FD968C2DBA 23 Rd & 31 St 23 Rd & 31 St
## 2 79D065D3303940F4 MacDougal St & Washington Sq MacDougal St & Washington Sq
## 3 A58DF6EBEBBB1E3C Mercer St & Bleecker St St Marks Pl & 1 Ave
## 4 93D264A51E33171F 1 Ave & E 68 St E 77 St & 1 Ave
## 5 800E80CC8D9F67E6 W 21 St & 6 Ave 2 Ave & E 31 St
## 6 CDA6779FA9EC06D9 48 St & Skillman Ave 45 Ave & 21 St
## member_casual day_period bike_type ride_duration
## 1 member afternoon classic 0
## 2 member night classic 51
## 3 member night classic 336
## 4 member afternoon classic 170
## 5 member afternoon classic 868
## 6 member morning classic 716
After importing the dataset into R, we performed the following data cleaning steps:
Created a new column bike_type by combining classic_bike, electric_bike, and docked_bike into a single categorical variable.
Created a new column ride_duration to represent the duration of each trip in minutes.
Removed irrelevant columns such as started_at, ended_at, station_ids and location coordinates.
Randomly selected a sample of 50,000 rows from the cleaned dataset for analysis, ensuring reproducibility by setting a seed number.
# Function to produce a table of summary statistics
summary_stats <- function(data) {
stats <- data %>%
summarize(
minimum = min(ride_duration, na.rm = TRUE),
maximum = max(ride_duration, na.rm = TRUE),
mean = mean(ride_duration, na.rm = TRUE),
sd = sd(ride_duration, na.rm = TRUE),
median = median(ride_duration, na.rm = TRUE),
IQR = IQR(ride_duration, na.rm = TRUE)
)
return(stats)
}
# Generate summary statistics for ride durations
ride_duration_stats <- summary_stats(citi_bike_data_bike_sample)
ride_duration_stats
## minimum maximum mean sd median IQR
## 1 -1 313643 921.2392 3315.769 553 708# Parameters for Uniform distribution
min_duration <- ride_duration_stats$minimum
max_duration <- ride_duration_stats$maximum
# Generate random sample from Uniform distribution
uniform_sample <- runif(50000, min = min_duration, max = max_duration)
# Parameters for Normal distribution
mean_duration <- ride_duration_stats$mean
sd_duration <- ride_duration_stats$sd
# Generate random sample from Normal distribution
normal_sample <- rnorm(50000, mean = mean_duration, sd = sd_duration)
# Plot histograms of ride durations, Uniform sample, and Normal sample
par(mfrow = c(1, 3)) # Arrange plots in a single row
# Histogram of ride durations
hist(citi_bike_data_bike_sample$ride_duration, breaks = 50, main = "Ride Durations", xlab = "Duration (mins)", col = "blue", probability = TRUE)
# Histogram of Uniform sample
hist(uniform_sample, breaks = 50, main = "Uniform Distribution", xlab = "Duration (mins)", col = "green", probability = TRUE)
# Histogram of Normal sample
hist(normal_sample, breaks = 50, main = "Normal Distribution", xlab = "Duration (mins)", col = "red", probability = TRUE)
par(mfrow = c(1, 1)) # Reset plot arrangement
# Creating a histogram of trip durations
histogram_plot <- ggplot(citi_bike_data_bike_sample, aes(x = ride_duration)) +
geom_histogram(binwidth = 100, fill = "skyblue", color = "black") +
labs(title = "Trip Durations", x = "Duration (seconds)", y = "Frequency") +
theme_minimal()
# Displaying the histogram
print(histogram_plot)
Similarities:
Differences:
The ride duration histogram is highly skewed to the left, with many short rides and a few longer rides.
The uniform sample histogram is flat, showing an equal frequency of all values across the range, reflecting the uniform distribution.
Similarities:
Differences:
The ride duration histogram is highly skewed to the left, indicating that most rides are short, with fewer longer rides.
The normal sample histogram is symmetrical and bell-shaped, indicating a normal distribution with most values clustered around the mean and fewer values at the extremes.
The normal distribution would be more suitable to approximate the trip durations since it shows more variation in the frequency of different durations, indicating that certain durations are more common than others just as the Ride durations have more frequency in short durations.
# Calculate ride count by time of day and user type
time_of_day_summary <- citi_bike_data_bike %>%
group_by(member_casual, day_period) %>%
summarize(ride_count = n(), .groups = 'drop')
time_of_day_summary
## # A tibble: 6 × 3
## member_casual day_period ride_count
## <chr> <chr> <int>
## 1 casual afternoon 267759
## 2 casual morning 97037
## 3 casual night 165661
## 4 member afternoon 970674
## 5 member morning 621389
## 6 member night 731830
# Bar plot of ride count by time of day and user type
ggplot(time_of_day_summary, aes(x = day_period, y = ride_count, fill = member_casual)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Ride Count by Time of Day and User Type", x = "Time of Day", y = "Ride Count") +
theme_minimal() +
scale_fill_manual(values = c("member" = "blue", "casual" = "red"))
The bar plot shows that the peak periods for Citi Bike usage are in the afternoon. Members exhibit higher ride counts, indicating that they primarily use the service for commuting. Both casual and members use bikes in the afternoon most, followed by the night and lastly in the morning. This difference in peak usage periods between members and casual users highlights the importance of ensuring bike availability during peak commuting times to cater to the needs of regular users. Members exhibit significantly higher ride counts across all periods, with the afternoon being the peak usage period. Casual users also have their peak usage in the afternoon, followed by night and morning. This indicates that while members use the service more frequently overall, both groups tend to prefer afternoon rides, possibly due to leisure activities or post-work travel.
# Calculate summary statistics for ride duration by bike type
ride_duration_summary <- citi_bike_data_bike %>%
group_by(bike_type) %>%
summarize(
count = n(),
mean_duration = mean(ride_duration, na.rm = TRUE),
median_duration = median(ride_duration, na.rm = TRUE),
sd_duration = sd(ride_duration, na.rm = TRUE),
IQR_duration = IQR(ride_duration, na.rm = TRUE),
.groups = 'drop'
)
ride_duration_summary
## # A tibble: 3 × 6
## bike_type count mean_duration median_duration sd_duration IQR_duration
## <chr> <int> <dbl> <dbl> <dbl> <dbl>
## 1 classic 2381013 893. 552 2771. 712
## 2 docked 15758 9802. 1461 69280. 1315
## 3 electric 457579 892. 537 3213. 658
# Box plot of ride duration by bike type
# theme_minimal() +
# scale_fill_manual(values = c("classic_bike" = "green", "electric_bike" = "yellow", "docked_bike" = "purple"))
The summary statistics and box plot reveal that electric bikes tend
to have shorter ride durations. Additionaly the most popular bike being
used is the clasic bikes with a count of 2381013.
The mean ride duration for electric bikes and classic bikes is
significantly lower, which could be attributed to their faster speed,
allowing users to reach their destinations more quickly. Docked bikes,
which are typically rented for short periods, have a median ride
duration that is much higher, indicating a skew in the data.
Studying the behavior of Citi Bike users, one can find a lot of interesting patterns in trip time and peak usage. On average, both members and casual users tend to use the service more intensively during the afternoon, but members use it much more often than casual ones. Another important factor that greatly affects ride duration is the type of bike, where e-bikes are used for tiny, short distances, whereas classic bikes are used for longer rides. In addition, these results can be combined into knowledge with which to best manage Citi Bike NYC operations, deal with fluctuating demand in bicycle flows for peak hours, and service the diverse pool of users who have varying preferences. With the knowledge of patterns of user behavior, Citi Bike NYC will actually optimize the satisfaction of users coupled with increasing operational effectiveness required for the growth of the bike-sharing system in New York City.