We’ll use data from http://www.tfl.gov.uk to analyse usage of the London
Bike Sharing scheme. This data has already been downloaded for you and
exists in a CSV (Comma Separated Values) file that you have
to read in to R.
There is no dropdown menu to read in your data to R, so instead we
use functions like read_csv to load data from file into R
objects.
#read the CSV file
bike <- read_csv(here::here("data", "london_bikes.csv")) %>%
mutate(weekend = if_else((wday == "Sat" | wday == "Sun"), TRUE, FALSE))Sometimes our data needs a bit of ‘cleaning’. For instance,
day_of_week is variable type character, or
chr. We should, however, treat it as a categorical, or
factor variable and relevel it, so Monday is the first
level of the factor (or first day of week), etc.
R is fairly sensitive with dates. When you read a CSV file, the date
may be in different formats. For instance, Christmas 2017 could be input
as 12-25-2017, 25.12.2017, 25 Dec 2017, Dec 25, 2017, etc. To be
consistent, we use lubridate’s ymd function, and force
variable Day to be a date in the format YYYY-MM-DD
Finally, we can turn season from 1, 2, 3, 4, to words
like Winter, Spring, etc.
We will be talking more about data wrangling later, but for now just execute the following piece of code.
# fix dates using lubridate, and generate new variables for year, month, month_name, day, and day_of _week
bike <- bike %>%
mutate(
year=year(date),
month = month(date),
month_name=month(date, label = TRUE),
day_of_week = wday(date, label = TRUE))
# generate new variable season_name to turn seasons from numbers to Winter, Spring, etc
bike <- bike %>%
mutate(
season_name = case_when(
month_name %in% c("Dec", "Jan", "Feb") ~ "Winter",
month_name %in% c("Mar", "Apr", "May") ~ "Spring",
month_name %in% c("Jun", "Jul", "Aug") ~ "Summer",
month_name %in% c("Sep", "Oct", "Nov") ~ "Autumn",
),
season_name = factor(season_name,
levels = c("Winter", "Spring", "Summer", "Autumn")))
#examine the structure of the datafame
skim(bike)| Name | bike |
| Number of rows | 5268 |
| Number of columns | 41 |
| _______________________ | |
| Column type frequency: | |
| character | 6 |
| factor | 3 |
| logical | 2 |
| numeric | 27 |
| POSIXct | 3 |
| ________________________ | |
| Group variables | None |
Variable type: character
| skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
|---|---|---|---|---|---|---|---|
| preciptype | 1411 | 0.73 | 4 | 9 | 0 | 3 | 0 |
| conditions | 0 | 1.00 | 4 | 28 | 0 | 11 | 0 |
| description | 0 | 1.00 | 26 | 85 | 0 | 78 | 0 |
| icon | 0 | 1.00 | 4 | 17 | 0 | 6 | 0 |
| wday | 0 | 1.00 | 3 | 3 | 0 | 7 | 0 |
| set | 0 | 1.00 | 5 | 5 | 0 | 1 | 0 |
Variable type: factor
| skim_variable | n_missing | complete_rate | ordered | n_unique | top_counts |
|---|---|---|---|---|---|
| month_name | 0 | 1 | TRUE | 12 | Aug: 465, Oct: 465, Dec: 464, Sep: 450 |
| day_of_week | 0 | 1 | TRUE | 7 | Sun: 753, Mon: 753, Fri: 753, Sat: 753 |
| season_name | 0 | 1 | FALSE | 4 | Aut: 1365, Sum: 1321, Win: 1294, Spr: 1288 |
Variable type: logical
| skim_variable | n_missing | complete_rate | mean | count |
|---|---|---|---|---|
| severerisk | 5268 | 0 | NaN | : |
| weekend | 0 | 1 | 0.29 | FAL: 3762, TRU: 1506 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| bikes_hired | 0 | 1.00 | 26327.60 | 9496.92 | 0.0 | 19746.75 | 26070.00 | 32745.00 | 73094.00 | ▂▇▅▁▁ |
| tempmax | 0 | 1.00 | 14.73 | 6.36 | -2.6 | 10.10 | 14.40 | 19.50 | 36.10 | ▁▇▇▃▁ |
| tempmin | 0 | 1.00 | 7.84 | 5.04 | -11.1 | 4.10 | 8.00 | 11.60 | 22.10 | ▁▃▇▇▁ |
| temp | 0 | 1.00 | 11.23 | 5.48 | -5.9 | 7.20 | 11.10 | 15.50 | 28.30 | ▁▅▇▅▁ |
| feelslikemax | 0 | 1.00 | 14.09 | 7.24 | -7.2 | 10.10 | 14.40 | 19.50 | 38.20 | ▁▅▇▃▁ |
| feelslikemin | 0 | 1.00 | 6.18 | 6.38 | -14.6 | 1.17 | 6.00 | 11.60 | 22.10 | ▁▃▇▇▂ |
| feelslike | 0 | 1.00 | 10.09 | 6.67 | -9.1 | 4.90 | 10.60 | 15.50 | 28.90 | ▁▆▇▇▁ |
| dew | 0 | 1.00 | 7.41 | 4.67 | -8.0 | 4.10 | 7.60 | 11.00 | 19.00 | ▁▃▇▇▂ |
| humidity | 0 | 1.00 | 79.28 | 10.40 | 39.6 | 72.20 | 80.30 | 87.30 | 99.90 | ▁▂▅▇▅ |
| precip | 0 | 1.00 | 1.64 | 4.73 | 0.0 | 0.00 | 0.18 | 1.34 | 168.20 | ▇▁▁▁▁ |
| precipprob | 0 | 1.00 | 73.04 | 44.38 | 0.0 | 0.00 | 100.00 | 100.00 | 100.00 | ▃▁▁▁▇ |
| precipcover | 0 | 1.00 | 8.96 | 11.60 | 0.0 | 0.00 | 8.33 | 12.50 | 100.00 | ▇▁▁▁▁ |
| snow | 0 | 1.00 | 0.02 | 0.25 | 0.0 | 0.00 | 0.00 | 0.00 | 8.40 | ▇▁▁▁▁ |
| snowdepth | 0 | 1.00 | 0.05 | 0.55 | 0.0 | 0.00 | 0.00 | 0.00 | 18.10 | ▇▁▁▁▁ |
| windgust | 1126 | 0.79 | 42.08 | 14.84 | 9.4 | 30.72 | 40.70 | 51.80 | 120.90 | ▅▇▃▁▁ |
| windspeed | 0 | 1.00 | 22.95 | 8.42 | 5.1 | 16.88 | 21.80 | 27.60 | 72.20 | ▅▇▂▁▁ |
| winddir | 0 | 1.00 | 196.97 | 92.33 | 0.1 | 127.75 | 221.60 | 259.00 | 359.80 | ▃▂▃▇▃ |
| sealevelpressure | 0 | 1.00 | 1014.90 | 10.52 | 966.6 | 1008.70 | 1015.70 | 1022.00 | 1047.80 | ▁▁▇▇▁ |
| cloudcover | 0 | 1.00 | 60.86 | 22.45 | 0.0 | 46.60 | 62.70 | 77.80 | 100.00 | ▁▃▇▇▅ |
| visibility | 0 | 1.00 | 22.65 | 10.62 | 0.2 | 15.20 | 22.00 | 28.90 | 62.50 | ▃▇▆▁▁ |
| solarradiation | 0 | 1.00 | 112.00 | 84.89 | 0.0 | 40.20 | 91.50 | 169.77 | 358.60 | ▇▅▃▂▁ |
| solarenergy | 0 | 1.00 | 9.66 | 7.34 | 0.0 | 3.50 | 7.90 | 14.60 | 31.00 | ▇▅▃▂▁ |
| uvindex | 0 | 1.00 | 4.26 | 2.55 | 0.0 | 2.00 | 4.00 | 6.00 | 10.00 | ▇▆▅▅▁ |
| moonphase | 0 | 1.00 | 0.48 | 0.29 | 0.0 | 0.25 | 0.50 | 0.75 | 0.98 | ▇▇▇▇▇ |
| month | 0 | 1.00 | 6.62 | 3.46 | 1.0 | 4.00 | 7.00 | 10.00 | 12.00 | ▇▅▅▅▇ |
| week | 0 | 1.00 | 27.01 | 15.08 | 1.0 | 14.00 | 27.00 | 40.00 | 53.00 | ▇▇▇▇▇ |
| year | 0 | 1.00 | 2017.28 | 4.17 | 2010.0 | 2014.00 | 2017.00 | 2021.00 | 2024.00 | ▆▇▇▇▇ |
Variable type: POSIXct
| skim_variable | n_missing | complete_rate | min | max | median | n_unique |
|---|---|---|---|---|---|---|
| date | 0 | 1 | 2010-07-30 00:00:00 | 2024-12-30 00:00:00 | 2017-10-14 12:00:00 | 5268 |
| sunrise | 0 | 1 | 2010-07-30 05:17:39 | 2024-12-30 08:06:26 | 2017-10-14 19:21:46 | 5268 |
| sunset | 0 | 1 | 2010-07-30 20:50:39 | 2024-12-30 16:00:26 | 2017-10-15 06:05:26 | 5268 |
Besides the number of bikes hired each day, we also have data on the weather for that day
Having loaded and cleaned our data, we can create summary statistics
using mosaic’s favstats. This is uni-variate analysis, so
in our mosaic syntax we will use
favstats(~ bikes_hired, data= bike). We also want to get an
overall, time-series plot of bikes over time; for the latter, we just
create a scatter plot of bikes_hired vs
Day.
| min | Q1 | median | Q3 | max | mean | sd | n | missing |
|---|---|---|---|---|---|---|---|---|
| 0 | 1.97e+04 | 2.61e+04 | 3.27e+04 | 7.31e+04 | 2.63e+04 | 9.5e+03 | 5268 | 0 |
While this analysis shows us overall statistics, what if we wanted to
get summary statistics by year, day_of_week,
month, or season? Mosaic’s syntax
Y ~ X alows us to facet our analysis of a variable Y by
another variable X using the syntax
favstats( Y ~ Z, data=...)
| year | min | Q1 | median | Q3 | max | mean | sd | n | missing |
|---|---|---|---|---|---|---|---|---|---|
| 2010 | 2.76e+03 | 9.3e+03 | 1.4e+04 | 1.87e+04 | 2.8e+04 | 1.41e+04 | 5.62e+03 | 155 | 0 |
| 2011 | 4.56e+03 | 1.63e+04 | 2.03e+04 | 2.37e+04 | 2.94e+04 | 1.96e+04 | 5.5e+03 | 365 | 0 |
| 2012 | 3.53e+03 | 1.93e+04 | 2.62e+04 | 3.25e+04 | 4.71e+04 | 2.6e+04 | 9.43e+03 | 366 | 0 |
| 2013 | 3.73e+03 | 1.76e+04 | 2.2e+04 | 2.74e+04 | 3.56e+04 | 2.2e+04 | 7.28e+03 | 365 | 0 |
| 2014 | 4.33e+03 | 2.05e+04 | 2.77e+04 | 3.44e+04 | 4.9e+04 | 2.75e+04 | 9.07e+03 | 365 | 0 |
| 2015 | 5.78e+03 | 2.21e+04 | 2.66e+04 | 3.29e+04 | 7.31e+04 | 2.7e+04 | 8.55e+03 | 365 | 0 |
| 2016 | 4.89e+03 | 2.24e+04 | 2.79e+04 | 3.51e+04 | 4.66e+04 | 2.82e+04 | 8.85e+03 | 366 | 0 |
| 2017 | 5.14e+03 | 2.41e+04 | 2.95e+04 | 3.45e+04 | 4.6e+04 | 2.86e+04 | 8.38e+03 | 365 | 0 |
| 2018 | 5.86e+03 | 2.18e+04 | 2.92e+04 | 3.77e+04 | 4.61e+04 | 2.9e+04 | 1.02e+04 | 365 | 0 |
| 2019 | 5.65e+03 | 2.42e+04 | 2.89e+04 | 3.45e+04 | 4.47e+04 | 2.86e+04 | 8.09e+03 | 365 | 0 |
| 2020 | 4.87e+03 | 2.01e+04 | 2.75e+04 | 3.65e+04 | 7.02e+04 | 2.85e+04 | 1.16e+04 | 366 | 0 |
| 2021 | 6.25e+03 | 2.17e+04 | 3.1e+04 | 3.82e+04 | 5.69e+04 | 3e+04 | 1.1e+04 | 365 | 0 |
| 2022 | 0 | 2.51e+04 | 3.14e+04 | 4e+04 | 6.7e+04 | 3.15e+04 | 1.03e+04 | 365 | 0 |
| 2023 | 6.72e+03 | 1.99e+04 | 2.37e+04 | 2.78e+04 | 3.53e+04 | 2.34e+04 | 6.03e+03 | 365 | 0 |
| 2024 | 7.48e+03 | 2.04e+04 | 2.48e+04 | 2.87e+04 | 3.52e+04 | 2.4e+04 | 6.14e+03 | 365 | 0 |
| day_of_week | min | Q1 | median | Q3 | max | mean | sd | n | missing |
|---|---|---|---|---|---|---|---|---|---|
| Sun | 0 | 1.41e+04 | 2.1e+04 | 2.97e+04 | 6.31e+04 | 2.22e+04 | 1.05e+04 | 753 | 0 |
| Mon | 3.97e+03 | 2.06e+04 | 2.57e+04 | 3.15e+04 | 6.7e+04 | 2.61e+04 | 8.28e+03 | 753 | 0 |
| Tue | 3.76e+03 | 2.25e+04 | 2.79e+04 | 3.44e+04 | 6.53e+04 | 2.82e+04 | 8.62e+03 | 752 | 0 |
| Wed | 4.33e+03 | 2.28e+04 | 2.8e+04 | 3.42e+04 | 5.44e+04 | 2.84e+04 | 8.52e+03 | 752 | 0 |
| Thu | 5.65e+03 | 2.27e+04 | 2.75e+04 | 3.44e+04 | 7.31e+04 | 2.83e+04 | 8.79e+03 | 752 | 0 |
| Fri | 5.4e+03 | 2.14e+04 | 2.65e+04 | 3.29e+04 | 6.7e+04 | 2.7e+04 | 8.54e+03 | 753 | 0 |
| Sat | 0 | 1.6e+04 | 2.25e+04 | 3.08e+04 | 7.02e+04 | 2.42e+04 | 1.11e+04 | 753 | 0 |
| month_name | min | Q1 | median | Q3 | max | mean | sd | n | missing |
|---|---|---|---|---|---|---|---|---|---|
| Jan | 3.73e+03 | 1.39e+04 | 1.9e+04 | 2.33e+04 | 3.8e+04 | 1.87e+04 | 5.99e+03 | 434 | 0 |
| Feb | 3.53e+03 | 1.6e+04 | 2.06e+04 | 2.46e+04 | 5.25e+04 | 2.03e+04 | 6.37e+03 | 396 | 0 |
| Mar | 5.06e+03 | 1.76e+04 | 2.31e+04 | 2.72e+04 | 5.66e+04 | 2.27e+04 | 7.59e+03 | 434 | 0 |
| Apr | 4.87e+03 | 2.1e+04 | 2.59e+04 | 3.08e+04 | 4.9e+04 | 2.6e+04 | 7.61e+03 | 420 | 0 |
| May | 1.07e+04 | 2.45e+04 | 2.99e+04 | 3.6e+04 | 7.02e+04 | 3.04e+04 | 8.44e+03 | 434 | 0 |
| Jun | 6.06e+03 | 2.77e+04 | 3.31e+04 | 3.92e+04 | 6.53e+04 | 3.34e+04 | 8.59e+03 | 420 | 0 |
| Jul | 5.56e+03 | 2.91e+04 | 3.58e+04 | 4.13e+04 | 7.31e+04 | 3.47e+04 | 8.35e+03 | 436 | 0 |
| Aug | 4.3e+03 | 2.56e+04 | 3.24e+04 | 3.83e+04 | 6.7e+04 | 3.14e+04 | 9.6e+03 | 465 | 0 |
| Sep | 0 | 2.49e+04 | 3.12e+04 | 3.61e+04 | 5.18e+04 | 3.04e+04 | 8.11e+03 | 450 | 0 |
| Oct | 7.07e+03 | 2.33e+04 | 2.8e+04 | 3.26e+04 | 4.79e+04 | 2.75e+04 | 6.77e+03 | 465 | 0 |
| Nov | 6.03e+03 | 1.88e+04 | 2.37e+04 | 2.79e+04 | 4.47e+04 | 2.33e+04 | 6.41e+03 | 450 | 0 |
| Dec | 2.76e+03 | 1.14e+04 | 1.67e+04 | 2.29e+04 | 3.91e+04 | 1.71e+04 | 6.91e+03 | 464 | 0 |
| season_name | min | Q1 | median | Q3 | max | mean | sd | n | missing |
|---|---|---|---|---|---|---|---|---|---|
| Winter | 2.76e+03 | 1.37e+04 | 1.88e+04 | 2.35e+04 | 5.25e+04 | 1.86e+04 | 6.58e+03 | 1294 | 0 |
| Spring | 4.87e+03 | 2.08e+04 | 2.61e+04 | 3.13e+04 | 7.02e+04 | 2.64e+04 | 8.51e+03 | 1288 | 0 |
| Summer | 4.3e+03 | 2.76e+04 | 3.37e+04 | 3.96e+04 | 7.31e+04 | 3.31e+04 | 8.98e+03 | 1321 | 0 |
| Autumn | 0 | 2.19e+04 | 2.72e+04 | 3.26e+04 | 5.18e+04 | 2.7e+04 | 7.69e+03 | 1365 | 0 |
While summary statistics allow us to quickly disover key metrics that represent the data, they are often not sufficient by themselves. Often, it is useful to represent the data graphically (or visually) to uncover information that is critical for decision making, such as trends, patterns and outliers.
In this section we will create a time-series scatter-plot that shows
the number of bikes hired on each day. We will use the
ggplot2 library.
Creating a basic plot is a two-step process. The first step is the
specify the data frame and the axes by using the syntax:
ggplot(data frame, aes(x=variable, y=variable). The second
step is to specify the plot that we want. In this case, we want a
scatter (point) plot using geom_point() after a
+ sign.
bike %>%
# Filter the bike dataset to include only data from January 1, 2014 onwards
filter(date >= lubridate::ymd("2014-01-01")) %>%
# Create a ggplot object
ggplot() +
# Map date to x-axis and bikes_hired to y-axis
aes(x = date, y = bikes_hired) +
# Add scatter points with 40% transparency to reduce overplotting
geom_point(alpha = 0.4) +
# Apply a clean black and white theme
theme_bw() +
# NULL doesn't add anything - often used as a placeholder for easy code modification
NULL# Histogram of bikes rented
bike %>%
# Filter the bike dataset to include only data from January 1, 2014 onwards
filter(date >= lubridate::ymd("2014-01-01")) %>%
ggplot()+
aes(x = bikes_hired)+
geom_histogram()+
theme_bw()+
NULL## `stat_bin()` using `bins = 30`. Pick better value `binwidth`.
# Histogram faceted by season_name
bike %>%
# Filter the bike dataset to include only data from January 1, 2014 onwards
filter(date >= lubridate::ymd("2014-01-01")) %>%
ggplot()+
aes(x = bikes_hired)+
geom_histogram()+
facet_wrap(~season_name)+
theme_bw()+
NULL## `stat_bin()` using `bins = 30`. Pick better value `binwidth`.
# Histogram faceted by month_name
bike %>%
# Filter the bike dataset to include only data from January 1, 2014 onwards
filter(date >= lubridate::ymd("2014-01-01")) %>%
ggplot()+
aes(x = bikes_hired)+
geom_histogram()+
facet_wrap(~month_name)+
theme_bw()+
NULL## `stat_bin()` using `bins = 30`. Pick better value `binwidth`.
# Histogram faceted by month_name in 4 rows
bike %>%
# Filter the bike dataset to include only data from January 1, 2014 onwards
filter(date >= lubridate::ymd("2014-01-01")) %>%
ggplot()+
aes(x = bikes_hired)+
geom_histogram()+
facet_wrap(~month_name, nrow = 4)+
theme_bw()+
NULL## `stat_bin()` using `bins = 30`. Pick better value `binwidth`.
# Density plot
bike %>%
# Filter the bike dataset to include only data from January 1, 2014 onwards
filter(date >= lubridate::ymd("2014-01-01")) %>%
ggplot()+
aes(x = bikes_hired)+
geom_density()+
theme_bw()+
NULL# Density plot filled by season_name
bike %>%
# Filter the bike dataset to include only data from January 1, 2014 onwards
filter(date >= lubridate::ymd("2014-01-01")) %>%
ggplot()+
aes(x = bikes_hired)+
geom_density(aes(fill=season_name), alpha = 0.3)+
theme_bw()+
NULL# Density plot filled by season_name, and faceted by season_name
bike %>%
# Filter the bike dataset to include only data from January 1, 2014 onwards
filter(date >= lubridate::ymd("2014-01-01")) %>%
ggplot()+
aes(x = bikes_hired)+
geom_histogram(aes(fill=season_name), alpha = 0.5)+
facet_wrap(~season_name, nrow = 4)+
theme_minimal()+
#remove legend to the right
theme(legend.position = "none")+
NULL## `stat_bin()` using `bins = 30`. Pick better value `binwidth`.
# Density plot filled by season_name, and faceted by month_name
bike %>%
# Filter the bike dataset to include only data from January 1, 2014 onwards
filter(date >= lubridate::ymd("2014-01-01")) %>%
ggplot()+
aes(x = bikes_hired)+
geom_density(aes(fill=season_name), alpha = 0.3)+
facet_wrap(~month_name, nrow = 4)+
theme_bw()+
theme(legend.position="none")+
NULL#Boxplot of bikes_hired by month_name
bike %>%
# Filter the bike dataset to include only data from January 1, 2014 onwards
filter(date >= lubridate::ymd("2014-01-01")) %>%
ggplot()+
aes(x=month_name, y= bikes_hired)+
geom_boxplot()+
theme_bw()+
NULL# bikes_hired vs. weather features
bike %>%
# Filter the bike dataset to include only data from January 1, 2014 onwards
filter(date >= lubridate::ymd("2014-01-01")) %>%
ggplot()+
aes(x=temp, y= bikes_hired)+
geom_point()+
geom_smooth(method = "lm", se=FALSE)+
theme_bw()+
NULL## `geom_smooth()` using formula = 'y ~ x'
bike %>%
# Filter the bike dataset to include only data from January 1, 2014 onwards
filter(date >= lubridate::ymd("2014-01-01")) %>%
ggplot()+
aes(x=temp, y= bikes_hired,
colour=season_name)+
geom_point(alpha = 0.1)+
geom_smooth(method = "lm", se=FALSE)+
theme_bw()+
NULL## `geom_smooth()` using formula = 'y ~ x'
###### bikes on humidity
bike %>%
# Filter the bike dataset to include only data from January 1, 2014 onwards
filter(date >= lubridate::ymd("2014-01-01")) %>%
ggplot()+
aes(x=humidity, y= bikes_hired,
colour=season_name)+
geom_point(alpha = 0.1)+
geom_smooth(method = "lm", se=FALSE)+
theme_bw()+
NULL## `geom_smooth()` using formula = 'y ~ x'
Besides the number of bikes rented out on a given day, we have also
downloaded weather data for London such as mean_temp,
humidity, pressure,
precipitation, etc. that measure weather conditions on a
single day. It may be the case that more bikes are rented out when it’s
warmer? Or how can we estimate the effect of rain on rentals?
Your task is to build a regression model that helps you explain the number of rentals per day.
Let us select a few of these numerical variables and create a scatterplot-correlation matrix
##
## Welch Two Sample t-test
##
## data: bikes_hired by weekend
## t = 13.929, df = 2300.2, p-value < 2.2e-16
## alternative hypothesis: true difference in means between group FALSE and group TRUE is not equal to 0
## 95 percent confidence interval:
## 3739.295 4964.730
## sample estimates:
## mean in group FALSE mean in group TRUE
## 27571.74 23219.72
bikes_hiredWe start the naive model where we just use the average to predict how many bikes we are going to rent out on a single day
| min | Q1 | median | Q3 | max | mean | sd | n | missing |
|---|---|---|---|---|---|---|---|---|
| 0 | 1.97e+04 | 2.61e+04 | 3.27e+04 | 7.31e+04 | 2.63e+04 | 9.5e+03 | 5268 | 0 |
# can you create a confidence interval for mean bikes_hired? What is the SE?
model0 <- lm(bikes_hired ~ 1, data= bike)
msummary(model0)## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 26327.6 130.8 201.2 <2e-16 ***
##
## Residual standard error: 9497 on 5267 degrees of freedom
# plot actual data vs predicted data
broom::augment(model0) |>
mutate(day=row_number()) |>
ggplot()+
aes(x=day, y = bikes_hired)+
geom_point(alpha = 0.2) +
geom_point(aes(y = .fitted),
shape = 1,
colour = "red", alpha = 0.2)+
theme_bw()What is the regression’s residual standard error? What is the intercept standard error?
bikes_hired on mean_temp# Define the model
model1 <- lm(bikes_hired ~ feelslikemax, data = bike)
# look at model estimated
msummary(model1)## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 14556.64 220.78 65.93 <2e-16 ***
## feelslikemax 835.44 13.94 59.94 <2e-16 ***
##
## Residual standard error: 7323 on 5266 degrees of freedom
## Multiple R-squared: 0.4056, Adjusted R-squared: 0.4054
## F-statistic: 3593 on 1 and 5266 DF, p-value: < 2.2e-16
# plot actual data vs predicted data
broom::augment(model1) |>
mutate(day=row_number()) |>
ggplot()+
aes(x=day, y = bikes_hired)+
geom_point(alpha = 0.2) +
geom_point(aes(y = .fitted),
shape = 1,
colour = "red", alpha = 0.2)+
theme_bw()mean_temp significant? Why?bikes_hired on mean_temp plus
weekend# Define the model
model2 <- lm(bikes_hired ~ feelslikemax + weekend, data = bike)
# look at model estimated
msummary(model2)## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 15800.66 221.76 71.25 <2e-16 ***
## feelslikemax 834.53 13.44 62.10 <2e-16 ***
## weekendTRUE -4306.90 215.28 -20.01 <2e-16 ***
##
## Residual standard error: 7060 on 5265 degrees of freedom
## Multiple R-squared: 0.4476, Adjusted R-squared: 0.4473
## F-statistic: 2133 on 2 and 5265 DF, p-value: < 2.2e-16
# plot actual data vs predicted data
broom::augment(model2) |>
mutate(day=row_number()) |>
ggplot()+
aes(x=day, y = bikes_hired)+
geom_point(alpha = 0.2) +
geom_point(aes(y = .fitted),
shape = 1,
colour = "red", alpha = 0.2)+
theme_bw()Fit a regression model called model2 with the
following explanatory variables: mean_temp and
weekend
mean_temp significant? Why?weekendTRUE?
What % of the variability of bikes_hired does your model explain?bikes_hired on mean_temp plus
wday# Define the model
model3 <- lm(bikes_hired ~ temp + wday, data = bike)
# look at model estimated
msummary(model3)## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 15310.17 335.80 45.594 < 2e-16 ***
## temp 1039.45 18.31 56.760 < 2e-16 ***
## wdayMon -880.36 375.40 -2.345 0.019057 *
## wdaySat -2731.60 375.40 -7.277 3.93e-13 ***
## wdaySun -4732.83 375.40 -12.607 < 2e-16 ***
## wdayThu 1236.42 375.53 3.293 0.000999 ***
## wdayTue 1205.90 375.52 3.211 0.001330 **
## wdayWed 1315.16 375.53 3.502 0.000465 ***
##
## Residual standard error: 7284 on 5260 degrees of freedom
## Multiple R-squared: 0.4125, Adjusted R-squared: 0.4117
## F-statistic: 527.6 on 7 and 5260 DF, p-value: < 2.2e-16
# plot actual data vs predicted data
broom::augment(model3) |>
mutate(day=row_number()) |>
ggplot()+
aes(x=day, y = bikes_hired)+
geom_point(alpha = 0.2) +
geom_point(aes(y = .fitted),
shape = 1,
colour = "red", alpha = 0.2)+
theme_bw()What is the meaning of the effect (slope) of, e.g.,
wdayMon? What % of the variability of bikes_hired does your
model explain?
model4 <- lm(bikes_hired ~ temp + wday + humidity + factor(month), data = bike)
# look at model estimated
msummary(model4)## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 38443.27 1064.54 36.113 < 2e-16 ***
## temp 537.96 31.27 17.205 < 2e-16 ***
## wdayMon -924.22 344.07 -2.686 0.007250 **
## wdaySat -2661.86 344.07 -7.736 1.22e-14 ***
## wdaySun -4821.70 344.08 -14.013 < 2e-16 ***
## wdayThu 1308.18 344.18 3.801 0.000146 ***
## wdayTue 1311.24 344.20 3.809 0.000141 ***
## wdayWed 1440.00 344.21 4.184 2.92e-05 ***
## humidity -256.42 11.00 -23.303 < 2e-16 ***
## factor(month)2 432.82 465.44 0.930 0.352456
## factor(month)3 1481.66 459.98 3.221 0.001284 **
## factor(month)4 2045.02 485.36 4.213 2.56e-05 ***
## factor(month)5 4500.81 517.00 8.706 < 2e-16 ***
## factor(month)6 5800.74 566.18 10.245 < 2e-16 ***
## factor(month)7 5428.74 603.25 8.999 < 2e-16 ***
## factor(month)8 2778.82 588.58 4.721 2.41e-06 ***
## factor(month)9 4373.23 543.67 8.044 1.07e-15 ***
## factor(month)10 4715.45 494.41 9.538 < 2e-16 ***
## factor(month)11 3504.31 459.80 7.621 2.96e-14 ***
## factor(month)12 -1582.60 447.96 -3.533 0.000415 ***
##
## Residual standard error: 6676 on 5248 degrees of freedom
## Multiple R-squared: 0.5076, Adjusted R-squared: 0.5059
## F-statistic: 284.8 on 19 and 5248 DF, p-value: < 2.2e-16
# plot actual data vs predicted data
broom::augment(model4) |>
mutate(day=row_number()) |>
ggplot()+
aes(x=day, y = bikes_hired)+
geom_point(alpha = 0.2) +
geom_point(aes(y = .fitted),
shape = 1,
colour = "red", alpha = 0.2)+
theme_bw()model5 <- lm(bikes_hired ~ feelslikemax + wday + humidity + factor(month) + precip, data = bike)
# look at model estimated
msummary(model5)## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 33695.66 1051.08 32.058 < 2e-16 ***
## feelslikemax 492.05 22.50 21.864 < 2e-16 ***
## wdayMon -906.85 331.82 -2.733 0.006299 **
## wdaySat -2670.15 331.83 -8.047 1.04e-15 ***
## wdaySun -4786.87 331.86 -14.424 < 2e-16 ***
## wdayThu 1308.28 331.97 3.941 8.22e-05 ***
## wdayTue 1324.82 331.99 3.991 6.68e-05 ***
## wdayWed 1487.40 331.97 4.481 7.60e-06 ***
## humidity -196.10 11.04 -17.767 < 2e-16 ***
## factor(month)2 666.31 449.57 1.482 0.138373
## factor(month)3 963.12 446.34 2.158 0.030988 *
## factor(month)4 1140.89 476.84 2.393 0.016763 *
## factor(month)5 3749.68 504.56 7.432 1.25e-13 ***
## factor(month)6 5156.49 543.10 9.495 < 2e-16 ***
## factor(month)7 4962.75 569.75 8.710 < 2e-16 ***
## factor(month)8 2271.48 557.27 4.076 4.65e-05 ***
## factor(month)9 3448.54 524.53 6.574 5.36e-11 ***
## factor(month)10 3938.39 478.80 8.226 2.43e-16 ***
## factor(month)11 3095.74 444.06 6.972 3.52e-12 ***
## factor(month)12 -1649.08 431.65 -3.820 0.000135 ***
## precip -279.62 19.33 -14.469 < 2e-16 ***
##
## Residual standard error: 6438 on 5247 degrees of freedom
## Multiple R-squared: 0.5421, Adjusted R-squared: 0.5404
## F-statistic: 310.7 on 20 and 5247 DF, p-value: < 2.2e-16
# plot actual data vs predicted data
broom::augment(model5) |>
mutate(day=row_number()) |>
ggplot()+
aes(x=day, y = bikes_hired)+
geom_point(alpha = 0.2) +
geom_point(aes(y = .fitted),
shape = 1,
colour = "red", alpha = 0.2)+
theme_bw()Our dataset has many more variables, so here are some ideas on how you can extend your analysis
bikes_hired?Let us say that your best model is model3. How do the
predictions of your model compare to the actual data?
# plot actual data vs predicted data
my_best_model <- broom::augment(model3) %>%
mutate(day=row_number())
# Plot fitted line and residuals
ggplot(my_best_model, aes(x=day, y = bikes_hired)) +
geom_point(alpha = 0.2) +
geom_point(aes(y = .fitted),
shape = 1,
colour = "red", alpha = 0.2)+
theme_bw()As you keep building your models, it makes sense to:
autoplot. You will always
have some deviation from normality, especially for very high values of
ncar::vif(model_x) to calculate the
Variance Inflation Factor (VIF) for your predictors and
determine whether you have colinear variables. A general guideline is
that a VIF larger than 10 is large, and your model may suffer from
collinearity. Remove the variable in question and run your model again
without it.## Warning: `fortify(<lm>)` was deprecated in ggplot2 3.6.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>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## 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>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## 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>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## feelslikemax weekend
## 1.000011 1.000011
## GVIF Df GVIF^(1/(2*Df))
## temp 1.000059 1 1.000029
## wday 1.000059 6 1.000005
## GVIF Df GVIF^(1/(2*Df))
## temp 3.470724 1 1.862988
## wday 1.001354 6 1.000113
## humidity 1.546931 1 1.243757
## factor(month) 3.958549 11 1.064537
Create a summary table, using huxtable (https://am01-sep23.netlify.app/example/modelling_side_by_side_tables/)
that shows which models you worked on, which predictors are significant,
the adjusted \(R^2\), and the Residual
Standard Error. If you want to add more models, just make sure you do
not forget the comma , after the last model, as shown
below
# produce summary table comparing models using huxtable::huxreg()
huxreg(model0, model1, model2, model3, model4,
statistics = c('#observations' = 'nobs',
'R squared' = 'r.squared',
'Adj. R Squared' = 'adj.r.squared',
'Residual SE' = 'sigma'),
bold_signif = 0.05
) %>%
set_caption('Comparison of models')| (1) | (2) | (3) | (4) | (5) | |
|---|---|---|---|---|---|
| (Intercept) | 26327.597 *** | 14556.638 *** | 15800.662 *** | 15310.174 *** | 38443.272 *** |
| (130.846) | (220.781) | (221.757) | (335.796) | (1064.536) | |
| feelslikemax | 835.436 *** | 834.529 *** | |||
| (13.938) | (13.438) | ||||
| weekendTRUE | -4306.903 *** | ||||
| (215.285) | |||||
| temp | 1039.450 *** | 537.960 *** | |||
| (18.313) | (31.267) | ||||
| wdayMon | -880.364 * | -924.224 ** | |||
| (375.400) | (344.065) | ||||
| wdaySat | -2731.599 *** | -2661.858 *** | |||
| (375.400) | (344.074) | ||||
| wdaySun | -4732.832 *** | -4821.699 *** | |||
| (375.400) | (344.076) | ||||
| wdayThu | 1236.424 *** | 1308.183 *** | |||
| (375.525) | (344.182) | ||||
| wdayTue | 1205.904 ** | 1311.241 *** | |||
| (375.524) | (344.204) | ||||
| wdayWed | 1315.157 *** | 1439.998 *** | |||
| (375.526) | (344.208) | ||||
| humidity | -256.423 *** | ||||
| (11.004) | |||||
| factor(month)2 | 432.823 | ||||
| (465.442) | |||||
| factor(month)3 | 1481.660 ** | ||||
| (459.975) | |||||
| factor(month)4 | 2045.016 *** | ||||
| (485.364) | |||||
| factor(month)5 | 4500.815 *** | ||||
| (517.000) | |||||
| factor(month)6 | 5800.743 *** | ||||
| (566.182) | |||||
| factor(month)7 | 5428.741 *** | ||||
| (603.254) | |||||
| factor(month)8 | 2778.824 *** | ||||
| (588.583) | |||||
| factor(month)9 | 4373.232 *** | ||||
| (543.674) | |||||
| factor(month)10 | 4715.447 *** | ||||
| (494.406) | |||||
| factor(month)11 | 3504.314 *** | ||||
| (459.798) | |||||
| factor(month)12 | -1582.596 *** | ||||
| (447.961) | |||||
| #observations | 5268 | 5268 | 5268 | 5268 | 5268 |
| R squared | 0.000 | 0.406 | 0.448 | 0.412 | 0.508 |
| Adj. R Squared | 0.000 | 0.405 | 0.447 | 0.412 | 0.506 |
| Residual SE | 9496.916 | 7322.808 | 7060.078 | 7284.098 | 6675.871 |
| *** p < 0.001; ** p < 0.01; * p < 0.05. | |||||