In the IT Approach computer tutorial, you have learnt how to use the MuMIn package for model selection.
Using the same roadkill example, use the IT approach to determine the best suite of models that explains the occurrence of roadkills in subsistence agricultural land.
We are interested in these habitat characteristics:
Distance to nearest water body
Distance to nearest non-fruiting tree
Distance to nearest shrub
Distance to nearest human dwelling
Distance to nearest fruit tree
Our analyses are also performed on the assumption that the variables have no interaction with each other.
# extract data from working directory and save it as a data frame.
sal<-read.csv("C:/Users/mishr/Desktop/Y4S1/ES3307/Assignment 4/Assignment 4 data/subsistence.csv")
head(sal)
## name kill habitat water tree shrub human fruit cable transectID
## 1 control 0 subsistence 40 20 20 60 10 NA 0
## 2 control 0 subsistence 30 10 15 30 20 NA 0
## 3 control 0 subsistence 30 10 5 140 20 NA 0
## 4 control 0 subsistence 25 15 10 190 10 NA 0
## 5 control 0 subsistence 20 20 15 180 10 NA 0
## 6 control 0 subsistence 30 20 15 120 10 NA 0
summary(sal)
## name kill habitat water tree
## Length :87 Min. :0.0000 Length :87 Min. : 5.00 Min. : 5.00
## N.unique : 5 1st Qu.:0.0000 N.unique : 1 1st Qu.: 5.00 1st Qu.: 5.00
## N.blank : 0 Median :1.0000 N.blank : 0 Median :10.00 Median :10.00
## Min.nchar: 4 Mean :0.5402 Min.nchar:11 Mean :14.25 Mean :11.44
## Max.nchar: 9 3rd Qu.:1.0000 Max.nchar:11 3rd Qu.:20.00 3rd Qu.:15.00
## Max. :1.0000 Max. :50.00 Max. :30.00
## shrub human fruit cable
## Min. : 5.00 Min. : 10.0 Min. : 5.000 Mode:logical
## 1st Qu.: 5.00 1st Qu.: 50.0 1st Qu.: 5.000 NAs :87
## Median :10.00 Median :100.0 Median :10.000
## Mean :11.67 Mean :130.1 Mean : 9.828
## 3rd Qu.:15.00 3rd Qu.:157.5 3rd Qu.:10.000
## Max. :30.00 Max. :580.0 Max. :50.000
## transectID
## Min. :0.000
## 1st Qu.:0.000
## Median :3.000
## Mean :1.586
## 3rd Qu.:3.000
## Max. :3.000
str(sal) # check data structure
## 'data.frame': 87 obs. of 10 variables:
## $ name : chr "control" "control" "control" "control" ...
## $ kill : int 0 0 0 0 0 0 0 0 0 0 ...
## $ habitat : chr "subsistence" "subsistence" "subsistence" "subsistence" ...
## $ water : int 40 30 30 25 20 30 10 20 5 10 ...
## $ tree : int 20 10 10 15 20 20 25 10 5 10 ...
## $ shrub : int 20 15 5 10 15 15 20 25 30 15 ...
## $ human : int 60 30 140 190 180 120 120 20 310 50 ...
## $ fruit : int 10 20 20 10 10 10 10 5 20 30 ...
## $ cable : logi NA NA NA NA NA NA ...
## $ transectID: int 0 0 0 0 0 0 0 0 0 0 ...
# several issues
# firstly, kill is binary either 0 (no kill) or 1 (kill)
# secondly, cable is NA for all so need to exclude from models
# not sure how big of a problem this is but there is only 2 values in transectID - 0 and 3
# missing? but stored as integer so should be okay?
# variables different from Swiss dataset example
################################## DECISIONS ####################################
# omit cable as it is not necessary and unlikely to give us relevant insights
# exclude transectID too as it is just a record metrics rather than an explanatory variable/predictor
# since our response variable KILL is binary we use glm with family=bionomial instead
# as it is not continuous and unlikely to be linear
# name also has no effect on model, its more of info? so we exclude
# write out possible models (referring from Tharka script 1)
library(car) # for Anova test
library(MuMIn) # for model averaging
# Set missing value handling
# options(na.action = "na.fail")
# MODEL
Q1 <- glm(
kill ~ water + tree + shrub + human + fruit,
family = binomial, na.action = "na.fail",
data = sal
)
mmQ1 <- dredge(Q1, rank = "AICc") # all possible models
mmQ1
## Global model call: glm(formula = kill ~ water + tree + shrub + human + fruit, family = binomial,
## data = sal, na.action = "na.fail")
## ---
## Model selection table
## (Intrc) fruit human shrub tree water df logLik AICc delta
## 16 3.9640 -0.2340 0.007149 -0.09202 -0.1346 5 -40.864 92.5 0.00
## 32 4.2160 -0.2255 0.007210 -0.09276 -0.1361 -0.02289 6 -40.679 94.4 1.94
## 12 2.9620 -0.2309 0.006524 -0.1379 4 -43.052 94.6 2.12
## 28 3.1790 -0.2237 0.006506 -0.1379 -0.02035 5 -42.879 96.5 4.03
## 8 2.6430 -0.2444 0.006595 -0.09817 4 -45.053 98.6 6.13
## 14 4.0530 -0.1931 -0.07290 -0.1099 4 -45.656 99.8 7.33
## 24 2.7710 -0.2371 0.006649 -0.09732 -0.01528 5 -44.957 100.7 8.19
## 10 3.1990 -0.1914 -0.1123 3 -47.190 100.7 8.20
## 4 1.4880 -0.2299 0.005798 3 -47.624 101.5 9.07
## 30 4.2360 -0.1838 -0.07157 -0.1114 -0.01930 5 -45.504 101.7 9.28
## 26 3.4190 -0.1821 -0.1132 -0.02113 4 -46.981 102.5 9.98
## 20 1.6710 -0.2229 0.005847 -0.01834 4 -47.459 103.4 10.94
## 6 2.9090 -0.2040 -0.07782 3 -49.071 104.4 11.96
## 2 1.9410 -0.1965 2 -50.851 105.8 13.38
## 22 2.9980 -0.1970 -0.07652 -0.01206 4 -49.002 106.5 14.02
## 31 2.7880 0.005995 -0.09172 -0.1324 -0.05648 5 -48.006 106.8 14.28
## 18 2.0950 -0.1881 -0.01660 3 -50.706 107.7 15.23
## 15 1.9900 0.005832 -0.08785 -0.1351 4 -49.907 108.3 15.83
## 27 1.7490 0.005282 -0.1293 -0.05454 4 -50.502 109.5 17.02
## 11 1.0190 0.005143 -0.1328 3 -52.421 111.1 18.66
## 29 3.0870 -0.07137 -0.1148 -0.05406 4 -51.790 112.1 19.60
## 25 2.1870 -0.1116 -0.05259 3 -53.565 113.4 20.95
## 13 2.3100 -0.06960 -0.1160 3 -53.763 113.8 21.35
## 23 1.2870 0.005258 -0.08570 -0.05509 4 -53.108 114.7 22.24
## 9 1.4660 -0.1145 2 -55.541 115.2 22.76
## 7 0.4986 0.004955 -0.08192 3 -55.332 117.0 24.49
## 19 0.3807 0.004417 -0.05371 3 -55.506 117.3 24.83
## 21 1.6860 -0.06750 -0.05185 3 -56.103 118.5 26.03
## 3 -0.3582 0.004182 2 -57.765 119.7 27.20
## 17 0.8962 -0.05168 2 -57.775 119.7 27.23
## 5 0.9397 -0.06663 2 -58.280 120.7 28.23
## 1 0.1613 1 -60.022 122.1 29.62
## weight
## 16 0.500
## 32 0.189
## 12 0.173
## 28 0.067
## 8 0.023
## 14 0.013
## 24 0.008
## 10 0.008
## 4 0.005
## 30 0.005
## 26 0.003
## 20 0.002
## 6 0.001
## 2 0.001
## 22 0.000
## 31 0.000
## 18 0.000
## 15 0.000
## 27 0.000
## 11 0.000
## 29 0.000
## 25 0.000
## 13 0.000
## 23 0.000
## 9 0.000
## 7 0.000
## 19 0.000
## 21 0.000
## 3 0.000
## 17 0.000
## 5 0.000
## 1 0.000
## Models ranked by AICc(x)
mmQ2_avg <- model.avg(mmQ1, subset = delta < 4)
summary(mmQ2_avg) # not very relevant for this question
##
## Call:
## model.avg(object = mmQ1, subset = delta < 4)
##
## Component model call:
## glm(formula = kill ~ <3 unique rhs>, family = binomial, data = sal,
## na.action = na.fail)
##
## Component models:
## df logLik AICc delta weight
## 1234 5 -40.86 92.47 0.00 0.58
## 12345 6 -40.68 94.41 1.94 0.22
## 124 4 -43.05 94.59 2.12 0.20
##
## Term codes:
## fruit human shrub tree water
## 1 2 3 4 5
##
## Model-averaged coefficients:
## (full average)
## Estimate Std. Error Adjusted SE z value Pr(>|z|)
## (Intercept) 3.818637 1.109965 1.124008 3.397 0.00068 ***
## fruit -0.231520 0.069638 0.070679 3.276 0.00105 **
## human 0.007037 0.002615 0.002654 2.652 0.00801 **
## shrub -0.073732 0.055804 0.056277 1.310 0.19014
## tree -0.135623 0.051748 0.052523 2.582 0.00982 **
## water -0.005031 0.020108 0.020345 0.247 0.80470
##
## (conditional average)
## Estimate Std. Error Adjusted SE z value Pr(>|z|)
## (Intercept) 3.818637 1.109965 1.124008 3.397 0.00068 ***
## fruit -0.231520 0.069638 0.070679 3.276 0.00105 **
## human 0.007037 0.002615 0.002654 2.652 0.00801 **
## shrub -0.092222 0.046795 0.047499 1.942 0.05219 .
## tree -0.135623 0.051748 0.052523 2.582 0.00982 **
## water -0.022893 0.037829 0.038403 0.596 0.55109
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# models included in the final candidate set
subset_mmQ1<-subset(mmQ1, delta < 4)
print(subset_mmQ1, abbrev.names = FALSE)
## Global model call: glm(formula = kill ~ water + tree + shrub + human + fruit, family = binomial,
## data = sal, na.action = "na.fail")
## ---
## Model selection table
## (Intercept) fruit human shrub tree water df logLik AICc delta
## 16 3.964 -0.2340 0.007149 -0.09202 -0.1346 5 -40.864 92.5 0.00
## 32 4.216 -0.2255 0.007210 -0.09276 -0.1361 -0.02289 6 -40.679 94.4 1.94
## 12 2.962 -0.2309 0.006524 -0.1379 4 -43.052 94.6 2.12
## weight
## 16 0.58
## 32 0.22
## 12 0.20
## Models ranked by AICc(x)
Based on the mmQ1 results where models with delta AICc < 4 are selected, the models included in the final candidate set are:
Model 16: glm(kill ~ fruit + human + shrub + tree, family = binomial, data = sal, na.action = “na.fail”)
Model 32: glm(kill ~ fruit + human + shrub + tree + water, family = binomial, data = sal, na.action = “na.fail”)
Model 12: glm(kill ~ fruit + human + tree, family = binomial, data = sal, na.action = “na.fail”)
The lower the AICc, the better the model is supported compared to the other models.
(Hint: Think of the most suitable way to show how the probability of roadkills changes with each variable.)
The best model from the candidate set is Model 16 which has the lowest AICc value (92.5) compared to the other 2 models, Model 32 (94.4) and Model 12 (94.6). Additionally, Model 16 has an Akaike weight of 0.58 (58%). This means that it is the best supported model among the candidate models. However, Model 16 does not include all explanatory variables it excludes water. This means that the inclusion of distance to water body in Model 32 may not have provided sufficient significance to be a justifiable parameter since AICc value of Model 16 is the lowest. On the other hand, by excluding both water and shrub in Model 12, the AICc value is not lowered, suggesting that the inclusion of shrub may be more justifiable. Hence, among the 3 top options, Model 16 is the best option.
# extract model 16 from the mmQ1 set
best_model <- get.models(mmQ1, subset = 1)[[1]] # top 1 from the subset which is Model 16
summary(best_model)
##
## Call:
## glm(formula = kill ~ fruit + human + shrub + tree + 1, family = binomial,
## data = sal, na.action = "na.fail")
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 3.964419 1.030820 3.846 0.000120 ***
## fruit -0.234031 0.069852 -3.350 0.000807 ***
## human 0.007149 0.002613 2.736 0.006217 **
## shrub -0.092020 0.046696 -1.971 0.048771 *
## tree -0.134638 0.051944 -2.592 0.009542 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 120.044 on 86 degrees of freedom
## Residual deviance: 81.727 on 82 degrees of freedom
## AIC: 91.727
##
## Number of Fisher Scoring iterations: 5
# + 1 is included in the model as it in the intercept by default there is a + 1 even if we do not explicitly write
# relevant libraries needed for plotting
library(ggeffects)
library(patchwork)
# model predicted line for the 4 explanatory variables
pred_fruit <- ggpredict(best_model, terms = "fruit")
pred_human <- ggpredict(best_model, terms = "human")
pred_shrub <- ggpredict(best_model, terms = "shrub")
pred_tree <- ggpredict(best_model, terms = "tree")
make_plot <- function(pred, variable, label, colour) {
ggplot() +
# Original observations
geom_jitter(
data = sal,
aes(x = .data[[variable]], y = kill),
height = 0.025,
width = 0,
alpha = 0.5,
colour = "black",
size = 2
) +
# 95% confidence interval
geom_ribbon(
data = pred,
aes(
x = x,
ymin = conf.low,
ymax = conf.high,
fill = "95% CI"
),
alpha = 0.25
) +
# Predicted probability line
geom_line(
data = pred,
aes(
x = x,
y = predicted,
colour = "Predicted probability"
),
linewidth = 1.2
) +
scale_colour_manual(
name = NULL,
values = c("Predicted probability" = colour)
) +
scale_fill_manual(
name = NULL,
values = c("95% CI" = colour)
) +
scale_y_continuous(
limits = c(0, 1),
breaks = seq(0, 1, 0.2)
) +
labs(
x = label,
y = "Predicted P(Roadkill)"
) +
guides(
colour = guide_legend(order = 1),
fill = guide_legend(order = 2)
) +
theme_classic() +
theme(
axis.title = element_text(size = 11),
axis.text = element_text(size = 10),
legend.position = "bottom",
legend.background = element_rect(
fill = "white",
colour = "black"
),
legend.box.background = element_rect(
colour = "black"
)
)
}
# 4 plots for the 4 explanatory variables
p1 <- make_plot(
pred_fruit, "fruit",
"Distance to nearest fruit tree",
"lightblue"
)
p2 <- make_plot(
pred_human, "human",
"Distance to nearest human dwelling",
"pink"
)
p3 <- make_plot(
pred_shrub, "shrub",
"Distance to nearest shrub",
"purple"
)
p4 <- make_plot(
pred_tree, "tree",
"Distance to nearest non-fruiting tree",
"lightgreen"
)
# combine all 4 plots into 1 figure for easier visualization
(p1 + p2) / (p3 + p4)
summary(best_model)
##
## Call:
## glm(formula = kill ~ fruit + human + shrub + tree + 1, family = binomial,
## data = sal, na.action = "na.fail")
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 3.964419 1.030820 3.846 0.000120 ***
## fruit -0.234031 0.069852 -3.350 0.000807 ***
## human 0.007149 0.002613 2.736 0.006217 **
## shrub -0.092020 0.046696 -1.971 0.048771 *
## tree -0.134638 0.051944 -2.592 0.009542 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 120.044 on 86 degrees of freedom
## Residual deviance: 81.727 on 82 degrees of freedom
## AIC: 91.727
##
## Number of Fisher Scoring iterations: 5
coef_table <- as.data.frame(
summary(best_model)$coefficients
)
coef_table <- tibble::rownames_to_column(
coef_table,
var = "Variable"
)
# Reorder columns
coef_table <- coef_table[, c(
"Variable", "Estimate", "Std. Error",
"z value", "Pr(>|z|)"
)]
# present in table
knitr::kable(
coef_table,
digits = 4,
caption = "Results for roadkill Model 16",
col.names = c(
"Variable", "Estimate", "Standard Error",
"z-value", "p-value"
)
)
| Variable | Estimate | Standard Error | z-value | p-value |
|---|---|---|---|---|
| (Intercept) | 3.9644 | 1.0308 | 3.8459 | 0.0001 |
| fruit | -0.2340 | 0.0699 | -3.3504 | 0.0008 |
| human | 0.0071 | 0.0026 | 2.7361 | 0.0062 |
| shrub | -0.0920 | 0.0467 | -1.9706 | 0.0488 |
| tree | -0.1346 | 0.0519 | -2.5920 | 0.0095 |
# report model fit statistics too
fit_table <- data.frame(
Statistic = c("Null deviance", "Residual deviance", "AIC"),
Value = c(120.044, 81.727, 91.727),
Degrees_of_freedom = c(86, 82, NA)
)
knitr::kable(
fit_table,
digits = 3,
caption = "Model fit statistics",
col.names = c("Statistic", "Value", "Degrees of freedom")
)
| Statistic | Value | Degrees of freedom |
|---|---|---|
| Null deviance | 120.044 | 86 |
| Residual deviance | 81.727 | 82 |
| AIC | 91.727 | NA |
From the results, all four explanatory variables had significant associations with occurrence of roadkill. The habitat characteristic or parameter, distance to the nearest fruit tree had a significant negative relationship with roadkill occurrence (β = -0.23 (2 d.p.), p < 0.001), suggesting that roadkill was more likely to occur closer to fruit trees. Similarly, distance to the nearest shrub (β = -0.10 (2 d.p.), p = 0.049 < 0.05) and non-fruiting tree (β = -0.14, p = 0.0010 < 0.01) also has a significant negative association with roadkill occurrence. However, the variable distance to the nearest human dwelling had a significant positive relationship with roadkill occurrence (β = 0.01, p = 0.006 < 0.01), implying that roadkill was more likely to occur further away from human dwellings.
In addition, by including this four parameters, the model becomes a better fit as there is a reduction in deviance from 120.04 (null model, df = 86) to 81.73 (residual model, df = 82), with a deviance difference of 120.04 - 81.73 = 38.32 (2 d.p.) (χ^2 = 38.32, p < 0.001).
Overall, these model results suggest that roadkill occurrence in subsistence agricultural land is associated with the surrounding habitat characteristics. Roadkill occurrence was associated with shorter distances to fruit trees, shrubs and non-fruiting trees in particular, but longer distances from human dwellings.
Plot the data to show the effect of DBH and SEX on the number of flowers in dioecious trees.
What does the plot tell you about the relationship between DBH and SEX?
# load assignment 1 data - dioecious tree
trees<-read.csv("C:/Users/mishr/Desktop/Y4S1/ES3307/Assignment 1/dioecious_tree.csv")
# check data structure
str(trees)
## 'data.frame': 50 obs. of 3 variables:
## $ SEX : int 2 1 2 1 2 1 2 2 1 1 ...
## $ DBH : int 174 171 212 187 227 274 86 161 285 182 ...
## $ FLOWERS: int 234 110 512 133 1107 624 8 333 637 225 ...
# DBH and SEX is integer
# since SEX is just 2 values (binary) FEMALE or MALE, need to change to factor
# 1 is MALE and 2 is FEMALE
trees$SEX <- factor(trees$SEX)
trees
## SEX DBH FLOWERS
## 1 2 174 234
## 2 1 171 110
## 3 2 212 512
## 4 1 187 133
## 5 2 227 1107
## 6 1 274 624
## 7 2 86 8
## 8 2 161 333
## 9 1 285 637
## 10 1 182 225
## 11 2 225 512
## 12 1 133 47
## 13 2 154 164
## 14 2 71 1
## 15 1 133 36
## 16 1 267 524
## 17 2 166 198
## 18 2 145 225
## 19 2 207 548
## 20 1 216 272
## 21 2 157 164
## 22 2 87 5
## 23 1 185 133
## 24 1 183 133
## 25 2 193 419
## 26 1 264 442
## 27 1 83 3
## 28 2 300 1482
## 29 1 242 333
## 30 2 316 1138
## 31 2 217 561
## 32 1 235 488
## 33 2 189 282
## 34 1 242 536
## 35 2 84 11
## 36 2 135 148
## 37 1 267 512
## 38 2 122 70
## 39 2 280 1107
## 40 2 295 941
## 41 2 139 89
## 42 2 165 181
## 43 2 81 8
## 44 1 190 173
## 45 2 68 1
## 46 2 314 1091
## 47 1 284 561
## 48 2 77 3
## 49 1 228 312
## 50 2 282 1398
summary(trees)
## SEX DBH FLOWERS
## 1:20 Min. : 68.0 Min. : 1.0
## 2:30 1st Qu.:140.5 1st Qu.: 115.8
## Median :188.0 Median : 253.0
## Mean :191.6 Mean : 383.5
## 3rd Qu.:242.0 3rd Qu.: 533.0
## Max. :316.0 Max. :1482.0
# From Assignment 1
# plot histogram #
ggplot(trees, aes(x = FLOWERS, fill = factor(SEX))) +
geom_histogram(
bins = 5,
color = "black"
) +
facet_wrap(~ factor(SEX)) +
labs(
x = "Sex",
y = "Number of Flowers",
title = "Distribution of Flowers by Sex"
) +
scale_fill_manual(
values = c("skyblue", "lightpink")
) +
theme_classic() +
theme(legend.position = "TRUE")
# plot data using boxplots #
ggplot(trees,
aes(x = factor(SEX), y = FLOWERS, fill = factor(SEX))) +
geom_boxplot() +
labs(
x = "Sex",
y = "Number of Flowers",
title = "Distribution of Flowers by Sex"
) +
scale_fill_manual(
values = c("skyblue", "lightpink")
) +
theme_classic() +
theme(legend.position = "TRUE")
# Plot raw data using scatter plot #
ggplot(trees,
aes(x = DBH, y = FLOWERS, colour = factor(SEX))) +
geom_point(size = 3) +
labs(
x = "Diameter at Breast Height (mm)",
y = "Number of Flowers",
title = "Plotting the effect of DBH and SEX on the number of flowers in dioecious trees",
colour = "Sex"
) +
scale_colour_manual(
values = c("skyblue", "lightpink"),
labels = c("Male", "Female")
) +
theme_classic()
From the scatter plot diagram, we can clearly observe that the range of number of flower for female dioecious trees is greater than that of the male and also more spread out in terms of DBH, ranging from less than 0 all the way to more than 300m. Whereas for male dioecious trees, the DBH range is slightly clustered starting from 130mm and ending before 300m. This suggests that there is an association or relationship present between SEX and DBH of diecious trees, indicating a potential interaction.
# with interaction
model_trees<-lm(FLOWERS ~ SEX*DBH, data=trees)
summary(model_trees)
##
## Call:
## lm(formula = FLOWERS ~ SEX * DBH, data = trees)
##
## Residuals:
## Min 1Q Median 3Q Max
## -212.09 -88.09 -46.77 91.64 403.24
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -440.7002 130.2774 -3.383 0.00147 **
## SEX2 -108.0718 146.7008 -0.737 0.46506
## DBH 3.5399 0.5935 5.964 3.28e-07 ***
## SEX2:DBH 1.9779 0.6885 2.873 0.00614 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 145.5 on 46 degrees of freedom
## Multiple R-squared: 0.8646, Adjusted R-squared: 0.8557
## F-statistic: 97.88 on 3 and 46 DF, p-value: < 2.2e-16
# without interaction, additive effect
model_trees_1 <-lm(FLOWERS ~ SEX + DBH, data = trees)
summary(model_trees_1)
##
## Call:
## lm(formula = FLOWERS ~ SEX + DBH, data = trees)
##
## Residuals:
## Min 1Q Median 3Q Max
## -206.31 -87.68 -34.16 63.06 443.80
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -753.0820 77.0812 -9.770 6.75e-13 ***
## SEX2 294.5838 46.5125 6.333 8.41e-08 ***
## DBH 5.0096 0.3232 15.499 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 156.3 on 47 degrees of freedom
## Multiple R-squared: 0.8403, Adjusted R-squared: 0.8335
## F-statistic: 123.6 on 2 and 47 DF, p-value: < 2.2e-16
anova(model_trees, model_trees_1)
## Analysis of Variance Table
##
## Model 1: FLOWERS ~ SEX * DBH
## Model 2: FLOWERS ~ SEX + DBH
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 46 973657
## 2 47 1148328 -1 -174671 8.2523 0.006138 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# from anova results
# run diagnostic plots
par(mfrow=c(2, 2)) # to set up a side by side layout
fit<-lm(FLOWERS ~ SEX*DBH, data=trees)# Run a regression analysis
plot(fit)
Based on the linear model FLOWERS ~ SEX*DBH, both tree sex and DBH have a significant interaction effect on the number of flowers (F1,46 = 8.25, p = 0.006). The interaction model is preferred over the additive model (FLOWERS ~ SEX + DBH), as it provides a significantly better fit and also statistically significant (F3,46 = 97.88, p < 0.001). This implies that there is strong evidence to support that the effect of DBH on number of flowers is different between male and female trees.
Interpretation of Diagnostic Plots
Residuals vs Fitted: The residuals show a slight curved pattern and not evenly distributed, suggesting possible non-linearity.
Q-Q Residuals: The points deviate from the qqnorm line at the tails, suggesting that the residuals may not be normally distributed.
Scale-Location: The residuals are not equally spread along the range of predictors, suggesting some heteroscedasticity.
Residuals vs Leverage: There is no significant outliers.
Based on the diagnostic plots in Question 1B, transform your y variable using an appropriate transformation and fit the linear model again.
Explain why you have chosen this transformation.
Plot the model predicted lines and confidence intervals with your original data. (Hint: Plot your predicted lines and confidence interval on the same scale as your original data.)
Run diagnostic plots to check your model fit.
Report and interpret your results.
# log transform number of flowers variable
model_log <- lm(log(FLOWERS) ~ SEX * DBH,
data = trees)
summary(model_log)
##
## Call:
## lm(formula = log(FLOWERS) ~ SEX * DBH, data = trees)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.1843 -0.3926 0.1704 0.6193 1.3565
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.704945 0.752965 0.936 0.354
## SEX2 -0.319963 0.847887 -0.377 0.708
## DBH 0.021599 0.003430 6.297 1.04e-07 ***
## SEX2:DBH 0.003743 0.003979 0.941 0.352
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.8409 on 46 degrees of freedom
## Multiple R-squared: 0.8133, Adjusted R-squared: 0.8011
## F-statistic: 66.78 on 3 and 46 DF, p-value: < 2.2e-16
# Compare models
model_log_add <- lm(log(FLOWERS) ~ SEX + DBH,
data = trees)
anova(model_log_add, model_log)
## Analysis of Variance Table
##
## Model 1: log(FLOWERS) ~ SEX + DBH
## Model 2: log(FLOWERS) ~ SEX * DBH
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 47 33.151
## 2 46 32.525 1 0.62552 0.8847 0.3518
library(ggplot2)
newdata <- expand.grid(
DBH = seq(min(trees$DBH),
max(trees$DBH),
length.out = 100),
SEX = levels(trees$SEX)
)
pred <- predict(model_log_add,
newdata = newdata,
interval = "confidence")
newdata$fit <- exp(pred[, "fit"])
newdata$lwr <- exp(pred[, "lwr"])
newdata$upr <- exp(pred[, "upr"])
# plot
ggplot(trees, aes(x = DBH, y = FLOWERS, colour = SEX)) +
geom_point(alpha = 0.7) +
geom_ribbon(
data = newdata,
aes(x = DBH, ymin = lwr, ymax = upr,
fill = SEX, group = SEX),
alpha = 0.2,
inherit.aes = FALSE
) +
geom_line(
data = newdata,
aes(x = DBH, y = fit,
colour = SEX, group = SEX),
linewidth = 1
) +
scale_colour_manual(
name = "Sex",
values = c("1" = "skyblue", "2" = "pink"),
labels = c("Male", "Female")
) +
scale_fill_manual(
name = "Sex",
values = c("1" = "skyblue", "2" = "pink"),
labels = c("Male", "Female")
) +
labs(
title = "Predicted Number of Flowers by DBH and Sex",
x = "DBH (mm)",
y = "Number of Flowers"
) +
theme_minimal()
# run diagnostic plot
par(mfrow=c(2, 2)) # to set up a side by side layout
plot(model_log_add)
From the diagnostic plots in 2B, we are going to log transform the number of flowers (y variable) to make it more normally distributed and overcome the heavily right skewed issue (see summary(trees)).
As such a log transformation, log(FLOWERS), was applied.
Based on the linear model log(FLOWERS) ~ SEX × DBH, the overall model is statistically significant (F3,46 = 66.78, p < 0.001). DBH has a significant positive effect on number of flowers (p < 0.001), while the interaction between SEX and DBH is not significant (F1,46= 0.885, p = 0.35 > 0.05). Therefore, the additive model (SEX + DBH) is preferred, as including the interaction term does not significantly improve the model fit. This suggests that flower production increases with DBH, but the relationship does not differ significantly between male and female trees.
Furthermore, the plot shows that the predicted number of flowers increases with DBH for both sexes, with females generally having higher predicted flower counts than males, although the confidence intervals widen at larger DBH values, indicating greater uncertainty.
Interpretation of Diagnostic Plots
Residuals vs Fitted: The residuals show a curved pattern suggesting likely non-linearity.
Normal Q-Q: Most points follow the diagonal line, although some deviations are observed at the lower tail. This suggests that the residuals are likely normally distributed.
Scale-Location: The residuals are not evenly spread across fitted values. This indicates possible heteroscedasticity.
Residuals vs Leverage: A few potentially influential observations, such as 14, 30 and 45. However, none appear to exceed the Cook’s distance line.
As the number of flowers is count data, we can instead fit a GLM to the data. Now, fit a GLM using a Poisson distribution to determine the effect of DBH and SEX on the number of flowers.
Plot the model predicted lines and confidence intervals with your original data. (Hint: Plot your predicted lines and confidence interval on the same scale as your original data.)
Run diagnostic plots to check your model fit.
Report and interpret your results.
What do you notice about the dispersion parameter in the summary() output?
glm_trees <- glm(FLOWERS ~ DBH*SEX, family = poisson, data = trees)
summary(glm_trees)
##
## Call:
## glm(formula = FLOWERS ~ DBH * SEX, family = poisson, data = trees)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 2.2567217 0.0798972 28.245 < 2e-16 ***
## DBH 0.0150589 0.0003198 47.087 < 2e-16 ***
## SEX2 0.9453136 0.0869851 10.868 < 2e-16 ***
## DBH:SEX2 -0.0018137 0.0003460 -5.242 1.59e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 17921.6 on 49 degrees of freedom
## Residual deviance: 2458.7 on 46 degrees of freedom
## AIC: 2811.6
##
## Number of Fisher Scoring iterations: 5
library(ggplot2)
# Create prediction data
newdata <- expand.grid(
DBH = seq(min(trees$DBH),
max(trees$DBH),
length.out = 100),
SEX = levels(trees$SEX)
)
# Predict on the link (log) scale
pred <- predict(
glm_trees,
newdata = newdata,
type = "link",
se.fit = TRUE
)
# Calculate confidence intervals on log scale
newdata$fit <- exp(pred$fit)
newdata$lwr <- exp(pred$fit - 1.96 * pred$se.fit)
newdata$upr <- exp(pred$fit + 1.96 * pred$se.fit)
# Plot
ggplot(trees, aes(x = DBH, y = FLOWERS, colour = SEX)) +
geom_point(alpha = 0.7) +
geom_ribbon(
data = newdata,
aes(x = DBH, ymin = lwr, ymax = upr,
fill = SEX, group = SEX),
alpha = 0.2,
inherit.aes = FALSE
) +
geom_line(
data = newdata,
aes(x = DBH, y = fit,
colour = SEX, group = SEX),
linewidth = 1
) +
scale_colour_manual(
name = "Sex",
values = c("1" = "skyblue", "2" = "pink"),
labels = c("Male", "Female")
) +
scale_fill_manual(
name = "Sex",
values = c("1" = "skyblue", "2" = "pink"),
labels = c("Male", "Female")
) +
labs(
title = "Poisson GLM: Predicted Number of Flowers",
x = "DBH (mm)",
y = "Number of Flowers"
) +
theme_minimal()
# run diagnostic plot (use DHARMa instead cos mixed model)
library(DHARMa)
# Simulate residuals
sim_res <- simulateResiduals(
fittedModel = glm_trees,
n = 1000
)
# Combine diagnostic plots into one figure
par(mfrow = c(2, 2), mar = c(4, 4, 3, 1))
plot(sim_res, quantreg = TRUE)
Based on the Poisson GLM (FLOWERS ~ DBH × SEX), DBH, SEX and their interaction all have significant effects on the number of flowers. DBH has a significant positive effect (p < 0.001), implying that the number of flowers increases with tree size. SEX also has a significant positive effect (p < 0.001), while the negative interaction suggests that the increase in the number of flowers with DBH is weaker for females. The plot shows that predicted flower values increase with DBH for both sexes, with females generally having higher predicted values than males.
However, the DHARMa diagnostic plots indicate significant deviations from the expected residual distribution and significant overdispersion (p < 0.001), suggesting that the Poisson model does not adequately fit the data. The dispersion parameter in the summary is fixed at 1, meaning the Poisson model assumes equal mean and variance, but the diagnostic results suggest this assumption is violated.
Now, fit a GLM with a Quasipoisson distribution. What has changed in your Quasipoisson model results?
Plot the model predicted lines and confidence intervals with your original data. (Hint: Plot your predicted lines and confidence interval on the same scale as your original data.)
Run diagnostic plots to check your model fit.
Report and interpret your results.
glm_quasi_trees <- glm(FLOWERS ~ DBH*SEX, family = quasipoisson, data = trees)
summary(glm_quasi_trees)
##
## Call:
## glm(formula = FLOWERS ~ DBH * SEX, family = quasipoisson, data = trees)
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.256722 0.569334 3.964 0.000255 ***
## DBH 0.015059 0.002279 6.608 3.54e-08 ***
## SEX2 0.945314 0.619840 1.525 0.134083
## DBH:SEX2 -0.001814 0.002466 -0.736 0.465714
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for quasipoisson family taken to be 50.77736)
##
## Null deviance: 17921.6 on 49 degrees of freedom
## Residual deviance: 2458.7 on 46 degrees of freedom
## AIC: NA
##
## Number of Fisher Scoring iterations: 5
library(ggplot2)
# Create prediction data
newdata <- expand.grid(
DBH = seq(min(trees$DBH),
max(trees$DBH),
length.out = 100),
SEX = levels(trees$SEX)
)
# Predictions on log scale
pred <- predict(
glm_quasi_trees,
newdata = newdata,
type = "link",
se.fit = TRUE
)
# Back-transform to original scale
newdata$fit <- exp(pred$fit)
newdata$lwr <- exp(
pred$fit - 1.96 * pred$se.fit
)
newdata$upr <- exp(
pred$fit + 1.96 * pred$se.fit
)
# Plot original data and predicted lines
ggplot(trees, aes(x = DBH, y = FLOWERS, colour = SEX)) +
geom_point(alpha = 0.7) +
geom_ribbon(
data = newdata,
aes(x = DBH, ymin = lwr, ymax = upr,
fill = SEX, group = SEX),
alpha = 0.2,
inherit.aes = FALSE
) +
geom_line(
data = newdata,
aes(x = DBH, y = fit,
colour = SEX, group = SEX),
linewidth = 1
) +
scale_colour_manual(
name = "Sex",
values = c("1" = "skyblue", "2" = "pink"),
labels = c("Male", "Female")
) +
scale_fill_manual(
name = "Sex",
values = c("1" = "skyblue", "2" = "pink"),
labels = c("Male", "Female")
) +
labs(
title = "Quasi-Poisson GLM: Predicted Number of Flowers",
x = "DBH (mm)",
y = "Number of Flowers"
) +
theme_minimal()
# run diagnostic plots
# Simulate residuals
par(mfrow = c(2, 2))
plot(glm_quasi_trees)
Based on the Quasi-Poisson GLM (FLOWERS ~ DBH × SEX), DBH has a significant positive effect on the number of flowers (p < 0.001), while SEX (p = 0.134 > 0.05) and the interaction between SEX and DBH (p = 0.466) are not significant. This suggests that the number of flower increases with DBH, but the relationship does not differ significantly between male and female trees. Compared to the Poisson model, the Quasi-Poisson model produces larger standard errors and p-values due to the adjustment for overdispersion. The dispersion parameter is estimated at 50.78 (2 d.p.), indicating substantial overdispersion, implying that the variance is much greater than the mean. Hence, the Quasi-Poisson model is more appropriate than the Poisson model for accounting for the overdispersion in the data.
Interpretation of Diagnostic Plots
Residuals vs Fitted: The residuals show a curved pattern suggesting likely non-linearity.
Normal Q-Q: Most points follow the diagonal line, although some deviations are observed at the upper tail. This suggests that the residuals are likely normally distributed.
Scale-Location: The residuals are not evenly spread across fitted values. This indicates possible heteroscedasticity.
Residuals vs Leverage: No signficiant outliers
I will select the Quasi-Poisson Model as the final model because the response variable is count data (number of flowers), and the data may exhibit overdispersion. This model is more appropriate than the Poisson model when the variance of the response variable is greater than its mean. As for the other model options, there are also not the most suitable because from the diagnostic plots, they violate the linear model rules such as normality and homoscedasticity.
What are the advantages and disadvantages associated with running a linear model or a generalised linear model?
| GLM | LM | |
|---|---|---|
| Advantages | Works well with non-linear and non normal data Variety of family structure including poisson and binomial Apt for count data |
Efficient method, faster to calculate Each regression coefficient shows exactly how much the response variable changes when a predictor (explanatory variable) changes by one unit |
| Disadvantages | GLMs assume each record is completely independent | Must be normally distributed and have homoscedasticity and be linear |