Data 110 Project 2

Author

Evan Xing

Red Capital Bikeshare bicycles at the Eastern Market Metro station in Washington, D.C.

Capital Bikeshare station outside Eastern Market Metro, Washington, D.C.

Source/Credit: Ben Schumin, 2010, Wikimedia Commons, CC BY-SA 3.0. (https://commons.wikimedia.org/wiki/File:Capital_Bikeshare_station_outside_Eastern_Market_Metro.jpg).

Data 110 Project 2: Bikesharing in DC

Introduction

The dataset consists of bikesharing data collected in DC from Capital Bikeshare, a bike sharing company, between 2011-2012. The original dataset has 731 observations and 16 variables. It was compiled by Hadi Fanaee-T and made available on UCI machine learning repository. The variables in the dataset include: Instant, Deteday, Season, yr, mnth, holiday, weekday, workingday, weathersit, temp, atemp, hum, windspeed, casual, registered, and cnt. The variables in the scope of my project’s analysis include only: temp - the normalized temperature(0-1), weekday from whole numbers 0-6, each representing the day of the week, workingday as 0 pr 1 with 0 being a non-working day and 1 being a working day, normalized windspeed(0-1), hum - normalized humidity(0-1), cnt- number of daily bike rentals, season - the four seasons, yr- year as 2011 or 2012, weathersit- daily weather conditions, and dteday - Calendar Date.

The questions that are being explored consist of: “What factors are associated with Daily Bike Sharing Rental demand?”, “How does daily bike sharing rentals change over time, and how do weather conditions appear in that pattern?”, “How does daily bike sharing rental demand change between seasons?”

This topic “Bike Sharing in DC” matters to me because it gives me an opportunity to analyze and visualize a real world problem that is relevant to contemporary daily life, such as transportation.

Background Research

Weather has consistently been linked to bike-sharing demand. Bean, Pojani, and Corcoran (2021) compared bike share use across 40 cities and found that weather factors, especially precipitation and temperature, were important predictors of use. Their study also found that ridership often increases with temperature up to a point before declining. This preliminary research supports examining temperature, humidity, wind, and weather conditions together as predictors in bike-sharing demand.

Load Libraries and Dataset

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.2.1     ✔ readr     2.2.0
✔ forcats   1.0.1     ✔ stringr   1.6.0
✔ ggplot2   4.0.3     ✔ tibble    3.3.1
✔ lubridate 1.9.5     ✔ tidyr     1.3.2
✔ purrr     1.2.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(plotly)
Warning: package 'plotly' was built under R version 4.6.1

Attaching package: 'plotly'

The following object is masked from 'package:ggplot2':

    last_plot

The following object is masked from 'package:stats':

    filter

The following object is masked from 'package:graphics':

    layout
library(ggfortify)
Warning: package 'ggfortify' was built under R version 4.6.1
Dc_BikeSharing <- read_csv("Bike_Sharing_Daily_2011_2012.csv")
Rows: 731 Columns: 16
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
dbl  (15): instant, season, yr, mnth, holiday, weekday, workingday, weathers...
date  (1): dteday

ℹ 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.
head(Dc_BikeSharing)
# A tibble: 6 × 16
  instant dteday     season    yr  mnth holiday weekday workingday weathersit
    <dbl> <date>      <dbl> <dbl> <dbl>   <dbl>   <dbl>      <dbl>      <dbl>
1       1 2011-01-01      1     0     1       0       6          0          2
2       2 2011-01-02      1     0     1       0       0          0          2
3       3 2011-01-03      1     0     1       0       1          1          1
4       4 2011-01-04      1     0     1       0       2          1          1
5       5 2011-01-05      1     0     1       0       3          1          1
6       6 2011-01-06      1     0     1       0       4          1          1
# ℹ 7 more variables: temp <dbl>, atemp <dbl>, hum <dbl>, windspeed <dbl>,
#   casual <dbl>, registered <dbl>, cnt <dbl>
dim(Dc_BikeSharing)
[1] 731  16
names(Dc_BikeSharing)
 [1] "instant"    "dteday"     "season"     "yr"         "mnth"      
 [6] "holiday"    "weekday"    "workingday" "weathersit" "temp"      
[11] "atemp"      "hum"        "windspeed"  "casual"     "registered"
[16] "cnt"       

Clean Dataset

cleaned_bike <- Dc_BikeSharing |>select(dteday, season, yr, temp, hum, windspeed, cnt, workingday, weathersit) |> filter(!is.na(temp), !is.na(dteday), !is.na(season), !is.na(yr),!is.na(temp),!is.na(hum), !is.na(windspeed), !is.na(cnt),!is.na(workingday),!is.na(weathersit)) |> mutate(temperature = temp, date = dteday, year = yr, dailyrentals = cnt, weathercondition = weathersit) |>  select(-temp, -dteday, -yr, -cnt, -weathersit) |> mutate( season = factor( season, levels = c(1, 2, 3, 4), labels = c("Spring", "Summer", "Fall", "Winter")), year = factor(year, levels = c(0, 1), labels = c("2011", "2012")), workingday = factor(workingday, levels = c(0, 1), labels = c("No", "Yes")),weathercondition = factor(weathercondition, levels = c(1, 2, 3, 4), labels = c("Clear","Mist/Cloudy","Light Rain/Snow","Heavy Rain/Snow")))

Using dplyr functions this dataset cleaning selects relevant variables for cleaning using select(); uses !is.na() and filter() to remove Na/Missing values from key variables;uses mutate() to mutate the variable names to more readable version; uses select() with - to remove original variable columns with unclear names; and uses mutate to convert categorical variable columns into factor arrays so that they are identified as categorical variables in R Analysis.

Quick Summary Statistics

overall_summary <- cleaned_bike |>
  summarise(
    days = n(),
    mean_daily_rentals = mean(dailyrentals),
    median_daily_rentals = median(dailyrentals),
    minimum_daily_rentals = min(dailyrentals),
    maximum_daily_rentals = max(dailyrentals)
  )
overall_summary
# A tibble: 1 × 5
   days mean_daily_rentals median_daily_rentals minimum_daily_rentals
  <int>              <dbl>                <dbl>                 <dbl>
1   731              4504.                 4548                    22
# ℹ 1 more variable: maximum_daily_rentals <dbl>

Question 1: What Factors are associated with daily bike sharing rental demand?

To answer this question, I first looked into the background research conducted and determine the factors relevant to daily bike sharing demand including temperature, humidity, wind speed, year, season, working-day status, and weather conditions. I used a multiple linear regression because the outcome variable, total daily rentals, was not binary and rather a quantitative continuous one.

Multiple Linear Regression Model w/ some basic assumption checks

bike_model <- lm(dailyrentals ~ temperature + hum + windspeed + workingday + year + season + weathercondition, data = cleaned_bike)

summary(bike_model)

Call:
lm(formula = dailyrentals ~ temperature + hum + windspeed + workingday + 
    year + season + weathercondition, data = cleaned_bike)

Residuals:
    Min      1Q  Median      3Q     Max 
-3676.4  -385.3    87.1   477.8  3412.4 

Coefficients:
                                Estimate Std. Error t value Pr(>|t|)    
(Intercept)                      1534.08     227.74   6.736 3.33e-11 ***
temperature                      5085.76     309.03  16.457  < 2e-16 ***
hum                             -1328.76     297.10  -4.472 8.98e-06 ***
windspeed                       -2800.74     431.01  -6.498 1.52e-10 ***
workingdayYes                     174.63      66.20   2.638  0.00852 ** 
year2012                         2012.87      62.18  32.373  < 2e-16 ***
seasonSummer                     1152.32     114.25  10.086  < 2e-16 ***
seasonFall                        862.41     150.90   5.715 1.61e-08 ***
seasonWinter                     1550.02      97.33  15.925  < 2e-16 ***
weatherconditionMist/Cloudy      -423.95      81.79  -5.183 2.84e-07 ***
weatherconditionLight Rain/Snow -1900.85     208.68  -9.109  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 827.8 on 720 degrees of freedom
Multiple R-squared:  0.8199,    Adjusted R-squared:  0.8174 
F-statistic: 327.8 on 10 and 720 DF,  p-value: < 2.2e-16

The adjusted R^2 is .8174. This means approximately 81.7% of the variation in daily bike-sharing rentals is explained by the predictors included in the multiple linear regression model. Predicted Daily Rentals = 1534.1 + 5085.8(Temperature) - 1328.8(Humidity) - 2800.7(Windspeed) + 174.6(Working Day) + 2012.9(Year 2012) + 1152.3(Summer) + 862.4(Fall) + 1550.0(Winter) - 423.9(Mist/Cloudy) - 1900.8(Light Rain/Snow). The final MLR model equations describes that for every 1.0 increase in temperature the daily rentals increase by 5085.8 holding other predictors constant. As for the other quantitiative predictors, -1328.8 to daily rentals for each +1.0 increase in humidity and -2800.7 to daily rentals for each +1.0 increase in windspeed. As for the qualitative predictors, holding all other predictors constant, the presence of the year 2012 increases daily rentals by 2012.9, +174.6 if it is a working day, etc. With P values for each predictor at:

Temperature - 6.77 × 10⁻⁵² Humidity - 8.98 × 10⁻⁶ Windspeed - 1.52 × 10⁻¹⁰ Working Day: Yes vs No - 0.00852 Year: 2012 vs 2011 - 1.35 × 10⁻¹⁴² Summer vs Spring - 1.83 × 10⁻²² Fall vs Spring - 1.61 × 10⁻⁸ Winter vs Spring - 3.83 × 10⁻⁴⁹ Mist/Cloudy vs Clear - 2.84 × 10⁻⁷ Light Rain/Snow vs Clear - 8.10 × 10⁻¹⁹

All extremely significant below a significance level of .05.

Spring, clear conditions, and the year 2011 were reference conditions for season, weathercondition, and year. So those conditions do not effect predicted daily rentals.

Assumption checks

autoplot(
  bike_model,
  which = 1:4,
  nrow = 2,
  ncol = 2
)
Warning: `fortify(<lm>)` was deprecated in ggplot2 4.0.0.
ℹ Please use `broom::augment(<lm>)` instead.
ℹ The deprecated feature was likely used in the ggfortify package.
  Please report the issue at <https://github.com/sinhrks/ggfortify/issues>.
Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
ℹ Please use tidy evaluation idioms with `aes()`.
ℹ See also `vignette("ggplot2-in-packages")` for more information.
ℹ The deprecated feature was likely used in the ggfortify package.
  Please report the issue at <https://github.com/sinhrks/ggfortify/issues>.
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
ℹ The deprecated feature was likely used in the ggfortify package.
  Please report the issue at <https://github.com/sinhrks/ggfortify/issues>.

The Residuals vs Fitted plot shows some curvature and increasing residual spread at higher fitted values, suggesting possible non-linearity and unequal variance. The Normal Q-Q plot has deviations from normality in the tails, while the Scale-Location plot also has greater residual variation at higher predicted rental levels. Observation 668 appears to be the most influential observation based on Cook’s distance, although its Cook’s distance is well below 1. The independence of the plot may be violated if consecutive days of bike renting are dependent on each other. But, Therefore, the model remains useful for examining general associations between the predictors and daily bike-sharing demand in daily rentals. The results should still be interpreted cautiously.

Uses ggfortify to run assumptions checks.

Question 2: How does daily bike-rental demand change between seasons?

Final Plot 1: A Box Plot

ggplot(cleaned_bike, aes(x = season, y = dailyrentals,fill = season)) +
  geom_boxplot() + scale_fill_manual(values = c("Winter" = "darkblue","Spring" = "cyan","Summer" = "yellow","Fall" = "brown")) +
  annotate("text", x = 3, y = 9500, label = "Fall Peak", size = 3.5) +
  labs(title = "Daily Capital Bikeshare Rentals by Season",
  subtitle = "Distribution of daily rentals across seasons, 2011–2012",
  x = "Season",
  y = "Total Daily Rentals",
  fill = "Season",
  caption = "Source: Capital Bikeshare / UCI Bike Sharing Dataset"
  ) + theme_classic() +
  theme( legend.position = "right")

The box plot clearly shows a fall peak occurring over the seasons 2011-2012 for daily capital bikeshare rentals. Sets a theme using theme_classic(), sets the legend to the right using theme(). Has appropriate plot labels using labs() and uses annotate() to give an identifying text to the fall peak. ggplot and geom_boxplot sets the foundations for this boxplot by identifying the dataset, x and y variables, the variable(Season) to be colored, and the exact plot to use. Also, scale_fill_manual allows me to set the colors for each outcome of season.

Question 3: How does daily bike sharing rentals change over time, and how do weather conditions appear in that pattern?

Plot 2:

# color palette set
weather_colors <- c("Clear" = "blue" , "Mist/Cloudy" = "purple", "Light Rain/Snow" = "orange")

# day with the highest daily bikesharing rentals
peak_day <- cleaned_bike |> slice_max(dailyrentals, n = 1, with_ties = FALSE)

plot2 <- ggplot(cleaned_bike, aes( x = date, y = dailyrentals, text = paste0( "Date: ", date, "<br>Daily Rentals: ", dailyrentals, "<br>Weather: ", weathercondition, "<br>Season: ", season,"<br>Working Day: ", workingday))) + geom_line( color = "grey", linewidth = 0.4, alpha = 0.7) + 
  geom_point(aes(color = weathercondition), size = 1.8, alpha = 0.8) + scale_color_manual(values = weather_colors) + 
  annotate("text",x = peak_day$date, y = peak_day$dailyrentals, label = "Peak Daily Ridership", vjust = -1,size = 3.5) +
  labs( title = "Capital Bikeshare Rentals Changed Over 2011–2012", subtitle = "Daily rentals colored by weather condition", x = "Date", y = "Total Daily Rentals", color = "Weather   Condition", caption = "Source: Capital Bikeshare / UCI Bike Sharing Dataset") + 
  theme_minimal() + theme(legend.position = "bottom") 

## plot2 made interactive
ggplotly(plot2, tooltip = "text" )

Based on this plot, it seems 2011 had less overall daily bikesharing rentals compared to that with 2012’s. Also there seems to be a characteristic yearly seasonal trend curve with peaks toward late spring, summer, and early fall before troughs during the late fall, winter, and early spring. Light Rain/Snow seem to decrease expected daily bikesharing rentals relative to the other weather conditions. The day being a working day did not have a visually noticeable effect on daily bikesharing rentals. The peak bikesharing rentals of 8714 occurred in 2012-09-15(early fall) in clear weather. Overall, this plot is good to identify obvious trends and facts about daily bikesharing over time and over multiple different weather conditions. For a more accurate analysis, statistical tests should be used.

Plotly was used to make the plot interactive with zoom controls and text tooltips over the individual observations. The interactive plot allows one to observe individual observations in a large scatter plot more accurately. ggplot sets the text that will be displayed in plotly using paste0 which concatenates the text without automatically adding spaces, the dataset used, and the x and y variables. geom_point plots the x and y variables values as points with the size and alpha corresponding to their opacity and size. It also tells the plot to color in weathercondition for each point. Annotate uses the observation with the highest daily rentals found by slice_max and stored in peak day to label that point accordingly in the plot. Labs labels the plot appropriately. Theme_minimal gives the plot a minimal theme and theme sets the legend positon to the bottom.

Conclusion

Overall, this analysis shows that daily Capital Bikeshare rental demand is associated with several weather, seasonal, and time-related variables. The multiple linear regression model explained approximately 81.7% of the variation in daily bike rentals, using the adjusted R² of 0.8174. This R² value represents a very strong model. Temperature had a positive association with daily rentals. Whereas, humidity and windspeed had negative associations. Year, season, working-day status, and weather conditions were also statistically significant predictors in the model. The seasonal boxplot showed that fall had the highest median daily rentals, while the interactive plot showed an overall increase in bike rentals from 2011 to 2012 along with a repeating seasonal pattern. Clear weather was generally associated with higher ridership, while light rain or snow was associated with lower ridership. However, the regression diagnostic plots showed some non-linearity, unequal variance, and deviations from normality, so the results should be interpreted cautiously. Future analysis with multiple linear regression should use adjustments that correct these model assumption concerns. Overall, the findings suggest that both weather conditions and time-related factors are useful for understanding patterns in daily bike-sharing demand.

References

Bean, R., Pojani, D., & Corcoran, J. (2021). How does weather affect bikeshare use? A comparative analysis of forty cities across climate zones. Journal of Transport Geography, 95, 103155. https://doi.org/10.1016/j.jtrangeo.2021.103155

Capital Bikeshare. (n.d.). System data. https://capitalbikeshare.com/system-data

Fanaee-T, H. (2013). Bike Sharing [Dataset]. UCI Machine Learning Repository. https://doi.org/10.24432/C5W894

Schumin, B. (2010). Capital Bikeshare station outside Eastern Market Metro [Photograph]. Wikimedia Commons. https://commons.wikimedia.org/wiki/File:Capital_Bikeshare_station_outside_Eastern_Market_Metro.jpg