
Session 3: Statistical modeling and machine learning
Department of Econometrics and Business Statistics
Food servers’ tips in restaurants may be influenced by many factors, including the nature of the restaurant, size of the party, and table locations in the restaurant. Restaurant managers need to know which factors matter when they assign tables to food servers. For the sake of staff morale, they usually want to avoid either the substance or the appearance of unfair treatment of the servers, for whom tips (at least in restaurants in the United States) are a major component of pay.
In one restaurant, a food server recorded the following data on all customers they served during an interval of two and a half months in early 1990. The restaurant, located in a suburban shopping mall, was part of a national chain and served a varied menu. In observance of local law the restaurant offered seating in a non-smoking section to patrons who requested it. Each record includes a day and time, and taken together, they show the server’s work schedule.
What is \(y\)? What is \(x\)?
Every person monitored their email for a week and recorded information about each email message; for example, whether it was spam, and what day of the week and time of day the email arrived. We want to use this information to build a spam filter, a classifier that will catch spam with high probability but will never classify good email as spam.
What is \(y\)? What is \(x\)?
A health insurance company collected the following information about households:
The health insurance company wants to provide a small range of products, containing different bundles of services and for different levels of cover, to market to customers.
What is \(y\)? What is \(x\)?
All (data-centric) models have a fitted values and residuals.
\[y = f(x_1, x_2, ..., x_p) + \varepsilon\]
where \(y\) is the observed response, \(x_1, ..., x_p\) are the observed values of \(p\) predictors and \(\varepsilon\) is the error. We conventionally use \(n\) to specfify the sample size.
Predictive accuracy
The primary purpose is to be able to predict \(\widehat{Y}\) for new data. And we’d like to do that well! That is, accurately.

Interpretability
Almost equally important is that we want to understand the relationship between \({\mathbf X}\) and \(Y\). The simpler model that is (almost) as accurate is the one we choose, always.
Person: Why did you predict 42 for this value?
Computer: Awkward silence
Parametric methods
Non-parametric methods


Black line is true boundary.
Grids (right) show boundaries for two different models.



If the model form is incorrect, the error (solid circles) may arise from wrong shape, and is thus reducible. Irreducible means that we have got the right model and mistakes (solid circles) are random noise.
Parametric models tend to be less flexible but non-parametric models can be flexible or less flexible depending on parameter settings.
Bias is the error that is introduced by modeling a complicated problem by a simpler problem.
Variance refers to how much your estimate would change if you had different training data. Its measuring how much your model depends on the data you have, to the neglect of future data.

When you impose too many assumptions with a parametric model, or use an inadequate non-parametric model, such as not letting an algorithm converge fully.

When the model closely captures the true shape, with a parametric model or a flexible model.

This fit will be virtually identical even if we had a different training sample.

Likely to get a very different model if a different training set is used.
When data are reused for multiple tasks, instead of carefully spent from the finite data budget, certain risks increase, such as the risk of accentuating bias or compounding effects from methodological errors. Julia Silge
[1] "A" "A" "B" "B" "B" "B" "B" "B" "B" "B" "B" "B"
[1] "B" "B" "A" "B" "B" "A" "B" "B"
[1] "B" "B" "B" "B"
Compute \(\widehat{y}\) from training data, \(\{(y_i, {\mathbf x}_i)\}_{i = 1}^n\). The error rate (fraction of misclassifications) to get the Training Error Rate
\[\text{Error rate} = \frac{1}{n}\sum_{i=1}^n I(y_i \ne \widehat{y}({\mathbf x}_i))\]
A better estimate of future accuracy is obtained using test data to get the Test Error Rate.
Training error will usually be smaller than test error. When it is much smaller, it indicates that the model is too well fitted to the training data to be accurate on future data (over-fitted).
| predicted | |||
1
|
0
|
||
| true |
1
|
a
|
b
|
0
|
c
|
d
|
|
Consider 1=positive (P), 0=negative (N).
adc (Type I error)b (Type II error)a/(a+b)d/(c+d)a+b)/(a+b+c+d)a+d)/(a+b+c+d)Two classes
# A tibble: 2 × 4
# Groups: y [2]
y bilby quokka cl_acc
<fct> <int> <int> <dbl>
1 bilby 9 3 0.75
2 quokka 5 10 0.667
More than two classes
# A tibble: 3 × 5
# Groups: y [3]
y bilby quokka numbat cl_err
<fct> <int> <int> <int> <dbl>
1 bilby 9 3 0 0.75
2 numbat 0 2 6 0.75
3 quokka 5 10 0 0.667
The balance of getting it right, without predicting everything as positive.

Need predictive probabilities, probability of being each class.
The first thing to do with data is to look at them …. usually means tabulating and plotting the data in many different ways to see what’s going on. With the wide availability of computer packages and graphics nowadays there is no excuse for ducking the labour of this preliminary phase, and it may save some red faces later.
Crowder, M. J. & Hand, D. J. (1990) “Analysis of Repeated Measures”
Creating new variables to get better fits is a special skill! Sometimes automated by the method. All are transformations of the original variables. (See tidymodels steps.)
step_pca)step_log)step_ratio)step_ns)step_dummy)Compute and examine the usual diagnostics, some methods have more
Go beyond … Look at the data and the model together!
Training - plusses; Test - dots


We plot the model on the data to assess whether it fits or is a misfit!
Doing this in high dimensions is considered difficult!
So it is common to only plot the data-in-the-model-space.
NOTE, WE CAN PLOT THESE THINGS IN HIGH DIMENSIONS. But this is for another day.
w <- read_csv("data/sine-curve.csv") |>
mutate(cl = factor(cl))
w_rf <- randomForest(cl~x1+x2, data=w, ntree=200)
w_rf_pred <- predict(w_rf, w, type="prob") |>
as_tibble(.name_repair="unique")
w_rf_pred |>
bind_cols(w) |>
select(`A`, cl) |>
ggplot(aes(x=`A`, colour=cl, fill=cl)) +
geom_density(alpha=0.7) +
xlab("Prob class A") +
ggtitle("Random forest") +
theme(legend.position="none")
w_lda <- lda(cl~x1+x2, data=w)
w_lda_pred <- predict(w_lda, w, method="predictive")$posterior |>
as_tibble(.name_repair="unique")
w_lda_pred |>
bind_cols(w) |>
select(`A`, cl) |>
ggplot(aes(x=`A`, colour=cl, fill=cl)) +
geom_density(alpha=0.7) +
xlab("Prob class A") +
ggtitle("Linear DA") +
theme(legend.position="none")
Predictive probabilities are aspects of the model. It is useful to plot. What do we learn here?
But it doesn’t tell you why there is a difference.


Model is displayed, as a grid of predicted points in the original variable space. Data is overlaid, using text labels. What do you learn?
One model has a linear boundary, and the other has the highly non-linear boundary, which matches the class cluster better. Also …

Data looks uninteresting: weak linear relationships between response and predictors.
x <- lm(tip ~ total_bill, data = tips)
tips.reg <- data.frame(tips, .resid = residuals(x),
.fitted = fitted(x))
library(ggplot2)
ggplot(lineup(null_lm(tip ~ total_bill,
method = 'rotate'),
tips.reg)) +
geom_point(aes(x = total_bill,
y = .resid)) +
facet_wrap(~ .sample) +
theme(axis.text = element_blank(),
axis.title = element_blank())
Techniques for assessing behaviour with new samples, and measuring the variability of models and predictions.
The package broom gets model results into a tidy format at different levels. (And broom.mixed does this for mixed models.)
broom::glance (values such as AIC, BIC, model fit, …)broom::tidy (estimate, confidence interval, significance level, …)broom::augment (fitted values, residuals, predictions, influence, …)# A tibble: 1 × 12
r.squared adj.r.squared sigma statistic p.value df
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 0.220 0.218 1.00 93.8 5.52e-104 6
# ℹ 6 more variables: logLik <dbl>, AIC <dbl>, BIC <dbl>,
# deviance <dbl>, df.residual <int>, nobs <int>
# A tibble: 7 × 5
term estimate std.error statistic p.value
<chr> <dbl> <dbl> <dbl> <dbl>
1 (Intercept) -0.0125 0.0224 -0.555 5.79e- 1
2 x1 1.03 0.126 8.17 5.59e-16
3 x2 0.460 0.268 1.72 8.58e- 2
4 x3 0.622 0.232 2.69 7.29e- 3
5 x4 0.965 0.820 1.18 2.39e- 1
6 x5 0.968 0.192 5.05 4.81e- 7
7 x6 0.930 0.117 7.94 3.40e-15
tidymodelstidymodels is a set of R packages that make it easier to build, tune, and evaluate statistical and machine learning models — all using the same consistent, tidyverse-style syntax. It provides a framework for the modeling workflow in R:
recipes)parsnip)rsample)tune)yardstick, workflows, broom)Source: A predictive modeling case study
About the data: The hotel bookings data from Antonio, Almeida, and Nunes (2019) to predict which hotel stays included children and/or babies, based on the other characteristics of the stays such as which hotel the guests stay at, how much they pay, etc.
Model: We will build a model to predict which actual hotel stays included children and/or babies, and which did not. Our outcome variable children is a factor variable with two levels.
Rows: 50,000
Columns: 23
$ hotel <fct> City_Hotel, City_Ho…
$ lead_time <dbl> 217, 2, 95, 143, 13…
$ stays_in_weekend_nights <dbl> 1, 0, 2, 2, 1, 2, 0…
$ stays_in_week_nights <dbl> 3, 1, 5, 6, 4, 2, 2…
$ adults <dbl> 2, 2, 2, 2, 2, 2, 2…
$ children <fct> none, none, none, n…
$ meal <fct> BB, BB, BB, HB, HB,…
$ country <fct> DEU, PRT, GBR, ROU,…
$ market_segment <fct> Offline_TA/TO, Dire…
$ distribution_channel <fct> TA/TO, Direct, TA/T…
$ is_repeated_guest <dbl> 0, 0, 0, 0, 0, 0, 0…
$ previous_cancellations <dbl> 0, 0, 0, 0, 0, 0, 0…
$ previous_bookings_not_canceled <dbl> 0, 0, 0, 0, 0, 0, 0…
$ reserved_room_type <fct> A, D, A, A, F, A, C…
$ assigned_room_type <fct> A, K, A, A, F, A, C…
$ booking_changes <dbl> 0, 0, 2, 0, 0, 0, 0…
$ deposit_type <fct> No_Deposit, No_Depo…
$ days_in_waiting_list <dbl> 0, 0, 0, 0, 0, 0, 0…
$ customer_type <fct> Transient-Party, Tr…
$ average_daily_rate <dbl> 81, 170, 8, 81, 158…
$ required_car_parking_spaces <fct> none, none, none, n…
$ total_of_special_requests <dbl> 1, 3, 2, 1, 4, 1, 1…
$ arrival_date <date> 2016-09-01, 2017-0…
Data splitting and re-sampling
Reserve 25% of the stays to the test set. The outcome variable children is pretty imbalanced so use a stratified random sample.
# A tibble: 2 × 3
children n prop
<fct> <int> <dbl>
1 children 3046 0.0812
2 none 34454 0.919
# A tibble: 2 × 3
children n prop
<fct> <int> <dbl>
1 children 992 0.0794
2 none 11508 0.921
Data splitting and re-sampling
The “other” set is further divided into training and validation sets. These two are used to build the model.
The test set is only used when the final model is decided.
IDA
p1 <- ggplot(hotel_other, aes(x=children, y=lead_time)) +
geom_boxplot()
p2 <- ggplot(hotel_other, aes(x=children, y=stays_in_weekend_nights)) +
geom_boxplot()
p3 <- ggplot(hotel_other, aes(x=children, y=average_daily_rate)) +
geom_boxplot()
p4 <- ggplot(hotel_other,
aes(x=reserved_room_type,
fill=children)) +
geom_bar(position="fill")
p1 + p2 + p3 + p4 + plot_layout(ncol=2)
Model setup
Since the outcome variable is categorical, logistic regression is a reasonable choice. This fits an S-curve which essentially models proportion of classes (children v none) for different values of the predictors. Using glmnet allows for feature selection while training the model.
holidays <- c("AllSouls", "AshWednesday", "ChristmasEve", "Easter",
"ChristmasDay", "GoodFriday", "NewYearsDay", "PalmSunday")
lr_recipe <-
recipe(children ~ ., data = hotel_other) |>
step_date(arrival_date) |>
step_holiday(arrival_date, holidays = holidays) |>
step_rm(arrival_date) |>
step_dummy(all_nominal_predictors()) |>
step_zv(all_predictors()) |>
step_normalize(all_predictors())# A tibble: 5 × 1
penalty
<dbl>
1 0.0001
2 0.000127
3 0.000161
4 0.000204
5 0.000259
# A tibble: 5 × 1
penalty
<dbl>
1 0.0386
2 0.0489
3 0.0621
4 0.0788
5 0.1
Model training
Better performance for smaller penalty, means that all predictors have some importance for the model fit.
Alternative model
Random forest models are generalist fits. It combines the predictions from many trees, each one fitted with a random selection of variables (features) and a random sample of observations.
Because each tree splits on single variables, it is also robust to missing values by using the complete data efficiently. It also handles the predictors as factors easily.
# A tibble: 5 × 8
mtry min_n .metric .estimator mean n std_err .config
<int> <int> <chr> <chr> <dbl> <int> <dbl> <chr>
1 9 3 roc_auc binary 0.928 1 NA Prepro…
2 4 5 roc_auc binary 0.928 1 NA Prepro…
3 8 11 roc_auc binary 0.926 1 NA Prepro…
4 5 19 roc_auc binary 0.926 1 NA Prepro…
5 14 8 roc_auc binary 0.924 1 NA Prepro…
# A tibble: 1 × 3
mtry min_n .config
<int> <int> <chr>
1 9 3 Preprocessor1_Model09
# A tibble: 187,500 × 8
.pred_children .pred_none id .row mtry min_n
<dbl> <dbl> <chr> <int> <int> <int>
1 0.118 0.882 validation 2 1 24
2 0.224 0.776 validation 6 1 24
3 0.0703 0.930 validation 7 1 24
4 0.0364 0.964 validation 10 1 24
5 0.0698 0.930 validation 12 1 24
6 0.0531 0.947 validation 13 1 24
7 0.0584 0.942 validation 16 1 24
8 0.0500 0.950 validation 22 1 24
9 0.0575 0.942 validation 27 1 24
10 0.0721 0.928 validation 35 1 24
# ℹ 187,490 more rows
# ℹ 2 more variables: children <fct>, .config <chr>
Choose the best model
For the same false positive rate (predicting the observation to the target class, incorrectly), the random forest model has a higher true positive rate (correctly predicting the target class) than the logistic regression model.
Most important variables are average_daily_room_rate, reserved_room_type, assigned_room_type, lead_time.
Diagnostics and summaries
hotel_test_pred <- tibble(hotel_test) |>
mutate(
pchild_cl = last_rf_fit$.predictions[[1]]$.pred_class,
pchild_pc = last_rf_fit$.predictions[[1]]$.pred_children,
pchild_pn = last_rf_fit$.predictions[[1]]$.pred_none)
hotel_test_pred |>
select(pchild_pc, pchild_pn, pchild_cl) |>
ggplot(aes(x=pchild_pc,
fill=pchild_cl,
colour=pchild_cl)) +
geom_density()
hotel_test_pred <- hotel_test_pred |>
mutate(error = if_else(children != pchild_cl,
"yes", "no"))
ggplot(hotel_test_pred, aes(x=children,
y=average_daily_rate,
colour = children)) +
geom_boxplot() +
scale_x_discrete("", labels = c("C", "N")) +
facet_grid(error~reserved_room_type) +
theme(legend.position = "none")
Tidy models makes it easy to compare and contrast fits, and to follow all the steps in pre-processing and fitting. Many case studies and examples to follow on the web site.
More work is needed to check assumptions, and to summarise the fit well.
Principal component analysis (PCA) produces a low-dimensional representation of a dataset. It finds a sequence of linear combinations of the variables that have maximal variance, and are mutually uncorrelated. It is an unsupervised learning method.
Use it, when:
Make sure you use standardised variables!
hotels data:
Rows: 50,000
Columns: 23
$ hotel <fct> City_Hotel, City_Ho…
$ lead_time <dbl> 217, 2, 95, 143, 13…
$ stays_in_weekend_nights <dbl> 1, 0, 2, 2, 1, 2, 0…
$ stays_in_week_nights <dbl> 3, 1, 5, 6, 4, 2, 2…
$ adults <dbl> 2, 2, 2, 2, 2, 2, 2…
$ children <fct> none, none, none, n…
$ meal <fct> BB, BB, BB, HB, HB,…
$ country <fct> DEU, PRT, GBR, ROU,…
$ market_segment <fct> Offline_TA/TO, Dire…
$ distribution_channel <fct> TA/TO, Direct, TA/T…
$ is_repeated_guest <dbl> 0, 0, 0, 0, 0, 0, 0…
$ previous_cancellations <dbl> 0, 0, 0, 0, 0, 0, 0…
$ previous_bookings_not_canceled <dbl> 0, 0, 0, 0, 0, 0, 0…
$ reserved_room_type <fct> A, D, A, A, F, A, C…
$ assigned_room_type <fct> A, K, A, A, F, A, C…
$ booking_changes <dbl> 0, 0, 2, 0, 0, 0, 0…
$ deposit_type <fct> No_Deposit, No_Depo…
$ days_in_waiting_list <dbl> 0, 0, 0, 0, 0, 0, 0…
$ customer_type <fct> Transient-Party, Tr…
$ average_daily_rate <dbl> 81, 170, 8, 81, 158…
$ required_car_parking_spaces <fct> none, none, none, n…
$ total_of_special_requests <dbl> 1, 3, 2, 1, 4, 1, 1…
$ arrival_date <date> 2016-09-01, 2017-0…
Usable variables: lead_time, stays_XXX, adults, is_repeated_guest, previous_XXX, booking_changes, days_in_waiting_list, average_daily_rate, total_of_special_requests.
Common methods:
and many variations.
Clustering is one of the hardest analytical methods because there is no known truth. Deciding on a solution requires external knowledge and understanding of geometry.
Here we will use \(k\)-means, and examine solutions for \(k=2,3,4,5\) clusters. We will use the lionfish R package, that is mostly written in python for highly reactive interactive graphics.
Clustering tends to break up data according to it’s shape.






library(lionfish)
data("risk")
colnames(risk) <- c("Rec", "Hea", "Car", "Fin", "Saf", "Soc")
risk_d <- apply(risk, 2, function(x) (x-mean(x))/sd(x))
# Two clusters
nc <- 3
set.seed(1145)
r_km <- kmeans(risk_d, centers=nc,
iter.max = 500, nstart = 5)
r_km_d <- risk |>
as_tibble() |>
mutate(cl = factor(r_km$cluster))
r_km_d |>
pivot_longer(Rec:Soc, names_to = "var", values_to = "val") |>
ggplot(aes(x=val, fill=cl)) +
geom_bar() +
facet_wrap(~var, ncol=3, scales="free_y") +
scale_fill_manual(values = c("#377EB8", "#FF7F00", "#4DAF4A")) +
xlab("") + ylab("") +
theme_minimal() +
theme(legend.position = "none",
axis.text = element_blank())
All the activities contribute to the segmentation into three clusters.
Unsupervised learning provides a way to organise observations or variables into smaller sets. It can help to make big noisy data more digestible - if done well.
tsibble: R temporal object
The tsibble package provides a data infrastructure for tidy temporal data with wrangling tools. Adapting the tidy data principles, tsibble is a data- and model-oriented object. In tsibble:
The Melbourne pedestrian sensor data has a regular period. Counts are provided for every hour, at numerous locations.
# A tsibble: 66,037 x 5 [1h] <Australia/Melbourne>
# Key: Sensor [4]
Sensor Date_Time Date Time Count
<chr> <dttm> <date> <int> <int>
1 Birrarun… 2015-01-01 00:00:00 2015-01-01 0 1630
2 Birrarun… 2015-01-01 01:00:00 2015-01-01 1 826
3 Birrarun… 2015-01-01 02:00:00 2015-01-01 2 567
4 Birrarun… 2015-01-01 03:00:00 2015-01-01 3 264
5 Birrarun… 2015-01-01 04:00:00 2015-01-01 4 139
6 Birrarun… 2015-01-01 05:00:00 2015-01-01 5 77
7 Birrarun… 2015-01-01 06:00:00 2015-01-01 6 44
8 Birrarun… 2015-01-01 07:00:00 2015-01-01 7 56
9 Birrarun… 2015-01-01 08:00:00 2015-01-01 8 113
10 Birrarun… 2015-01-01 09:00:00 2015-01-01 9 166
# ℹ 66,027 more rows
In contrast, the US flights data, below, is irregular.
# A tsibble: 336,776 x 20 [!] <UTC>
# Key: origin, dest, carrier, tailnum [52,807]
year month day dep_time sched_dep_time dep_delay
<int> <int> <int> <int> <int> <dbl>
1 2013 1 30 2224 2000 144
2 2013 2 17 2012 2010 2
3 2013 2 26 2356 2000 236
4 2013 3 13 1958 2005 -7
5 2013 5 16 2214 2000 134
6 2013 5 30 2045 2000 45
7 2013 9 11 2254 2159 55
8 2013 9 12 NA 2159 NA
9 2013 9 8 2156 2159 -3
10 2013 1 26 1614 1620 -6
# ℹ 336,766 more rows
# ℹ 14 more variables: arr_time <int>,
# sched_arr_time <int>, arr_delay <dbl>,
# carrier <chr>, flight <int>, tailnum <chr>,
# origin <chr>, dest <chr>, air_time <dbl>,
# distance <dbl>, hour <dbl>, minute <dbl>,
# time_hour <dttm>, dt <dttm>
Is pedestrian traffic really regular?
Wrangling prior to analysing temporal data includes:
For the airlines data, you can aggregate by multiple quantities, eg number of arrivals, departures, average hourly arrival delay and departure delays.
The US flights data already has some temporal components created, so aggregating by these is easy. Here is departure delay.
flights_mth <- flights_ts |>
as_tibble() |>
group_by(month, origin) |>
summarise(dep_delay = mean(dep_delay, na.rm=TRUE)) |>
as_tsibble(key=origin, index=month)
ggplot(flights_mth, aes(x=month, y=dep_delay, colour=origin)) +
geom_point() +
geom_smooth(se=F) +
scale_x_continuous("", breaks = seq(1, 12, 1),
labels=c("J","F","M","A","M","J",
"J","A","S","O","N","D")) +
scale_y_continuous("av dep delay (mins)", limits=c(0, 25)) +
theme(aspect.ratio = 0.5)
Aggregate by month, but examine arrival delays.
flights_mth_arr <- flights_ts |>
as_tibble() |>
group_by(month, origin) |>
summarise(arr_delay = mean(arr_delay, na.rm=TRUE)) |>
as_tsibble(key=origin, index=month)
ggplot(flights_mth_arr, aes(x=month, y=arr_delay, colour=origin)) +
geom_point() +
geom_smooth(se=F) +
scale_x_continuous("", breaks = seq(1, 12, 1),
labels=c("J","F","M","A","M","J",
"J","A","S","O","N","D")) +
scale_y_continuous("av arr delay (mins)", limits=c(0, 25)) +
theme(aspect.ratio = 0.5)
Week day vs weekend would be expected to have different patterns of delay, but this is not provided.
flights_wk <- flights_ts |>
as_tibble() |>
mutate(wday = wday(dt, label=TRUE, week_start = 1)) |>
group_by(wday, origin) |>
summarise(dep_delay = mean(dep_delay, na.rm=TRUE)) |>
mutate(weekend = ifelse(wday %in% c("Sat", "Sun"), "yes", "no")) |>
as_tsibble(key=origin, index=wday)
ggplot(flights_wk, aes(x=wday, y=dep_delay, fill=weekend)) +
geom_col() +
facet_wrap(~origin, ncol=1, scales="free_y") +
xlab("") +
ylab("av dep delay (mins)") +
theme(aspect.ratio = 0.5, legend.position = "none")
Be careful of times!
flights_airtm <- flights |>
mutate(dep_min = dep_time %% 100,
dep_hr = dep_time %/% 100,
arr_min = arr_time %% 100,
arr_hr = arr_time %/% 100) |>
mutate(dep_dt = ymd_hm(paste(paste(year, month, day, sep="-"),
paste(dep_hr, dep_min, sep=":")))) |>
mutate(arr_dt = ymd_hm(paste(paste(year, month, day, sep="-"),
paste(arr_hr, arr_min, sep=":")))) |>
mutate(air_time2 = as.numeric(difftime(arr_dt, dep_dt)))
fp <- flights_airtm |>
sample_n(3000) |>
ggplot(aes(x=air_time, y=air_time2, label = paste(origin, dest))) +
geom_abline(intercept=0, slope=1) +
geom_point()
ggplotly(fp, width=500, height=500)Why is this not what we expect?
# A tsibble: 6 x 3 [1Y]
# Key: fruit [2]
year fruit kilo
<dbl> <chr> <int>
1 2011 cherry 2
2 2012 cherry 7
3 2013 cherry 1
4 2010 kiwi 6
5 2011 kiwi 5
6 2013 kiwi 8
# A tsibble: 6 x 3 [1Y]
# Key: fruit [2]
year fruit kilo
<dbl> <chr> <int>
1 2011 cherry 2
2 2012 cherry 7
3 2013 cherry 1
4 2010 kiwi 6
5 2011 kiwi 5
6 2013 kiwi 8
# A tsibble: 6 x 3 [1Y]
# Key: fruit [2]
year fruit kilo
<dbl> <chr> <int>
1 2011 cherry 2
2 2012 cherry 7
3 2013 cherry 1
4 2010 kiwi 6
5 2011 kiwi 5
6 2013 kiwi 8
# A tsibble: 6 x 3 [1Y]
# Key: fruit [2]
year fruit kilo
<dbl> <chr> <int>
1 2011 cherry 2
2 2012 cherry 7
3 2013 cherry 1
4 2010 kiwi 6
5 2011 kiwi 5
6 2013 kiwi 8
We have already seen na_ma() function, that imputes using a moving average. Alternatively, na_interpolation() uses the previous and next values to impute.
ped_QV <- pedestrian |>
mutate(year = year(Date),
month = month(Date)) |>
dplyr::filter(Sensor == "QV Market-Elizabeth St (West)",
year == 2016, month %in% c(2:5))
ggplot(ped_QV) +
geom_line(aes(x=Time, y=Count)) +
facet_calendar(~Date) +
theme(axis.title = element_blank(),
axis.text.y = element_blank(),
aspect.ratio = 0.5) # A tibble: 4 × 2
Sensor .gaps
<chr> <lgl>
1 Birrarung Marr TRUE
2 Bourke Street Mall (North) TRUE
3 QV Market-Elizabeth St (West) TRUE
4 Southern Cross Station TRUE
# A tibble: 3 × 4
Sensor .from .to .n
<chr> <dttm> <dttm> <int>
1 QV Mar… 2015-04-05 02:00:00 2015-04-05 02:00:00 1
2 QV Mar… 2015-12-31 00:00:00 2015-12-31 23:00:00 24
3 QV Mar… 2016-04-03 02:00:00 2016-04-03 02:00:00 1
There is a block of missing values on the last day of 2015. The other two missings correspond to an hour in April, when the clocks change from summer to regular time.
# Which days are holidays
hol <- holiday_aus(2015:2016, state = "VIC")
ped_qvm_filled <- ped_qvm_filled |>
mutate(hol = is.weekend(Date)) |>
mutate(hol = if_else(Date %in% hol, TRUE, hol)) |>
mutate(Time = factor(Time))
# Fit linear model to inpute missings
ped_qvm_lm <- lm(Count~Time*hol, data=ped_qvm_filled)
ped_qvm_filled <- ped_qvm_filled |>
mutate(pCount = predict(ped_qvm_lm, ped_qvm_filled))
# Select subset and plot results
ped_qvm_sub <- ped_qvm_filled |>
filter(Date > ymd("2015-12-17"), Date < ymd("2016-01-01"))
ggplot(ped_qvm_sub) +
geom_line(aes(x=Date_Time, y=Count)) +
geom_line(data=filter(ped_qvm_sub, is.na(Count)),
aes(x=Date_Time,
y=pCount),
colour="seagreen3") +
scale_x_datetime("", date_breaks = "1 day",
date_labels = "%a %d") +
theme(aspect.ratio = 0.15)The resources at Forecasting: Principles and Practice (3e) explains how to effectively do forecasting.
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.