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
## 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…
## 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"
## 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"
# 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."
# 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")
The visual and statistical analysis of the daily bike rental demand provided the following insights:
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.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.