About Data Analysis Report

This RMarkdown file contains the report of the data analysis done for the project on forecasting daily bike rental demand using time series models in R. It contains analysis such as data exploration, summary statistics and building the time series models. The final report was completed on Sat Feb 7 18:09:44 2026.

Data Description:

This dataset contains the daily count of rental bike transactions between years 2011 and 2012 in Capital bikeshare system with the corresponding weather and seasonal information.

Data Source: https://archive.ics.uci.edu/ml/datasets/bike+sharing+dataset

Relevant Paper:

Fanaee-T, Hadi, and Gama, Joao, ‘Event labeling combining ensemble detectors and background knowledge’, Progress in Artificial Intelligence (2013): pp. 1-15, Springer Berlin Heidelberg

Task One: Load and explore the data

Load data and install packages

## Import required packages
# Check if packages are installed, if not, install them (you only need to do this once)
if (!require("pacman")) install.packages("pacman")
## Loading required package: pacman
pacman::p_load(tidyverse, timetk, lubridate, forecast, tseries, ggthemes)

# Load the data
# We will use the 'day.csv' file. 
# If you don't have it locally, this downloads it temporarily
temp <- tempfile()
download.file("https://archive.ics.uci.edu/ml/machine-learning-databases/00275/Bike-Sharing-Dataset.zip", temp)
unzip(temp, "day.csv")
bike_data <- read_csv("day.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.
# Clean and format the data
bike_data_clean <- bike_data %>%
  select(dteday, cnt, temp, hum, windspeed) %>%
  mutate(dteday = as.Date(dteday)) %>%
  rename(date = dteday, 
         demand = cnt)

# Preview the data
glimpse(bike_data_clean)
## Rows: 731
## Columns: 5
## $ date      <date> 2011-01-01, 2011-01-02, 2011-01-03, 2011-01-04, 2011-01-05,…
## $ demand    <dbl> 985, 801, 1349, 1562, 1600, 1606, 1510, 959, 822, 1321, 1263…
## $ temp      <dbl> 0.3441670, 0.3634780, 0.1963640, 0.2000000, 0.2269570, 0.204…
## $ hum       <dbl> 0.805833, 0.696087, 0.437273, 0.590435, 0.436957, 0.518261, …
## $ windspeed <dbl> 0.1604460, 0.2485390, 0.2483090, 0.1602960, 0.1869000, 0.089…

Describe and explore the data

Task Two: Create interactive time series plots

## Read about the timetk package
# ?timetk

# Plot the daily bike rental demand
bike_data_clean %>%
  plot_time_series(
    .date_var = date,
    .value = demand,
    .interactive = TRUE,
    .plotly_slider = TRUE,
    .title = "Daily Bike Rental Demand (2011-2012)"
  )
## 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 timetk package.
##   Please report the issue at
##   <https://github.com/business-science/timetk/issues>.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Ignoring unknown labels:
## • colour : "Legend"

Task Three: Smooth time series data

## Read about the timetk package
# ?timetk

# Plot the daily bike rental demand
bike_data_clean %>%
  plot_time_series(
    .date_var = date,
    .value = demand,
    .interactive = TRUE,
    .plotly_slider = TRUE,
    .title = "Daily Bike Rental Demand (2011-2012)"
  )
## Ignoring unknown labels:
## • colour : "Legend"

Task Four: Decompse and access the stationarity of time series data

# 1. Decomposition (STL Method) to see Trend, Seasonality, and Remainder
bike_data_clean %>%
  plot_stl_diagnostics(
    .date_var = date,
    .value = demand,
    .frequency = "auto", .trend = "auto",
    .feature_set = c("observed", "season", "trend", "remainder"),
    .interactive = TRUE
  )
## frequency = 7 observations per 1 week
## trend = 92 observations per 3 months
# 2. Assess Stationarity using Augmented Dickey-Fuller Test
# Null Hypothesis (H0): Data is non-stationary
# Alternative Hypothesis (H1): Data is stationary

adf_result <- adf.test(bike_data_clean$demand)

print(adf_result)
## 
##  Augmented Dickey-Fuller Test
## 
## data:  bike_data_clean$demand
## Dickey-Fuller = -1.6351, Lag order = 9, p-value = 0.7327
## alternative hypothesis: stationary
# Interpretation logic
if(adf_result$p.value < 0.05) {
  print("Result: The time series is likely Stationary (Reject H0)")
} else {
  print("Result: The time series is likely Non-Stationary (Fail to reject H0). Differencing may be required.")
}
## [1] "Result: The time series is likely Non-Stationary (Fail to reject H0). Differencing may be required."

Task Five: Fit and forecast time series data using ARIMA models

# Load the rsample library for data splitting functions
library(rsample) 

# Split data into training (first 80%) and testing (last 20%)
splits <- time_series_split(bike_data_clean, assess = "3 months", cumulative = TRUE)
## Using date_var: date
train_data <- training(splits)
test_data  <- testing(splits)

# Fit an Auto-ARIMA model
# auto.arima() automatically finds the best p,d,q parameters
model_arima <- auto.arima(train_data$demand, seasonal = TRUE)

# Check model summary
summary(model_arima)
## Series: train_data$demand 
## ARIMA(0,1,3) with drift 
## 
## Coefficients:
##           ma1      ma2      ma3   drift
##       -0.6106  -0.1868  -0.0673  9.3169
## s.e.   0.0396   0.0466   0.0398  4.6225
## 
## sigma^2 = 730568:  log likelihood = -5227.22
## AIC=10464.44   AICc=10464.53   BIC=10486.74
## 
## Training set error measures:
##                     ME     RMSE      MAE       MPE     MAPE      MASE
## Training set 0.4753752 851.3924 607.1892 -7.564197 20.56696 0.8683823
##                      ACF1
## Training set -0.001701445
# Forecast for the length of the test set
forecast_arima <- forecast(model_arima, h = nrow(test_data))

# Visualize the forecast against actual data
autoplot(forecast_arima) +
  autolayer(ts(test_data$demand, start = length(train_data$demand) + 1), series = "Actual Data") +
  theme_minimal() +
  labs(title = "ARIMA Model Forecast vs Actual Demand", y = "Bike Rentals", x = "Time Index")

Task Six: Findings and Conclusions

1. Findings

The visual and statistical analysis of the daily bike rental demand provided the following insights:

  • Trend Analysis: The interactive time series plots revealed a clear positive trend in bike rentals from 2011 to 2012. The moving average smoothing (Task Three) helped isolate this growth, showing that demand consistently increased year-over-year, likely due to the growing popularity of the bikeshare system.
  • Seasonality: The decomposition analysis (Task Four) confirmed strong annual seasonality. Demand peaks during the warmer months (Spring/Summer) and drops significantly during the Winter. This cyclical pattern accounts for a large portion of the variance in the data.
  • Model Performance: The auto.arima() function successfully fitted a Seasonal ARIMA model. The forecast plot in Task Five shows that the model captures the expected seasonal downturn for the winter months while maintaining the overall upward trend.

2. Conclusions

  • Predictability: Daily bike rental demand is highly predictable using historical data because the seasonal patterns are consistent and the growth trend is stable.
  • Key Drivers: While this univariate analysis focused only on past demand, the strong seasonality suggests that external factors—specifically weather (temperature, precipitation) and working days—are likely the primary drivers of demand.
  • Limitations: The ARIMA model assumes historical patterns will repeat. It may not accurately forecast demand anomalies caused by unexpected events (e.g., extreme weather events or station closures) without including them as external regressors.

3. Recommendations for Future Analysis

To improve the accuracy of the “Data Analysis Laboratory” forecasts, future iterations of this project should consider: 1. Dynamic Regression (ARIMAX): Including the temp (temperature) and hum (humidity) columns from the dataset as external regressors would likely reduce forecast error significantly. 2. Holiday Effects: Flagging holidays explicitly could help the model account for demand drops on non-working days.