Decision Tree Statistical Analyses in R
Example R code linked to the prepared datasets
knitr::opts_chunk$set(
echo = TRUE,
warning = FALSE,
message = FALSE,
error = FALSE
)
data_dir <- file.path("..", "data", "examples")
read_example <- function(file) {
read.csv(file.path(data_dir, file), stringsAsFactors = FALSE)
}
has_pkg <- function(package) {
requireNamespace(package, quietly = TRUE)
}
optional_packages <- c(
"ARTool",
"bootnet",
"caret",
"e1071",
"lavaan",
"lme4",
"lmerTest",
"MASS",
"metafor",
"nnet",
"ordinal",
"performance",
"psych",
"qgraph",
"randomForest",
"rmdformats",
"rpart"
)
install_missing_packages <- function(packages = optional_packages) {
missing_packages <- packages[!vapply(packages, has_pkg, logical(1))]
if (length(missing_packages) == 0) {
message("All optional packages are already installed.")
return(invisible(character()))
}
install.packages(
missing_packages,
repos = "https://cloud.r-project.org"
)
invisible(missing_packages)
}Optional packages
Run this chunk once if you want to install only the packages that are missing on your computer.
Associations and relationships
Pearson correlation
Purpose: Use Pearson correlation to quantify the strength and direction of a linear association between two continuous variables.
##
## Pearson's product-moment correlation
##
## data: data$x and data$y
## t = 58.974, df = 10, p-value = 4.773e-14
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## 0.9947113 0.9996114
## sample estimates:
## cor
## 0.9985654
Spearman rank correlation
Purpose: Use Spearman correlation to assess whether two variables have a monotonic association based on ranks, especially when normality or linearity is not plausible.
data <- read_example("spearman.csv")
cor.test(data$stress_rank, data$wellbeing_rank, method = "spearman", exact = FALSE)##
## Spearman's rank correlation rho
##
## data: data$stress_rank and data$wellbeing_rank
## S = 571, p-value = 4.128e-12
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
## rho
## -0.9964974
Kendall rank correlation
Purpose: Use Kendall correlation to evaluate rank-order association, often with smaller samples or many tied ranks.
data <- read_example("kendall.csv")
cor.test(data$adherence_rank, data$wellbeing_rank, method = "kendall", exact = FALSE)##
## Kendall's rank correlation tau
##
## data: data$adherence_rank and data$wellbeing_rank
## z = 3.7029, p-value = 0.0002131
## alternative hypothesis: true tau is not equal to 0
## sample estimates:
## tau
## 0.8181818
Chi-square test of association
Purpose: Use this test to examine whether two categorical variables are statistically associated in a contingency table.
##
## improved not_improved
## A 8 4
## B 3 9
##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: tab
## X-squared = 2.6853, df = 1, p-value = 0.1013
Fisher exact test
Purpose: Use Fisher’s exact test to examine association between two categorical variables when sample sizes or expected cell counts are small.
##
## no yes
## A 2 3
## B 4 1
##
## Fisher's Exact Test for Count Data
##
## data: tab
## p-value = 0.5238
## alternative hypothesis: true odds ratio is not equal to 1
## 95 percent confidence interval:
## 0.002560407 4.586187647
## sample estimates:
## odds ratio
## 0.2033186
McNemar test
Purpose: Use McNemar’s test to compare paired binary responses, such as before-after classifications from the same participants.
##
## no yes
## no 2 6
## yes 2 4
##
## McNemar's Chi-squared test with continuity correction
##
## data: tab
## McNemar's chi-squared = 1.125, df = 1, p-value = 0.2888
Log-linear model
Purpose: Use a log-linear model to analyse associations among several categorical variables through their frequency counts.
data <- read_example("logLinearModel.csv")
tab <- xtabs(count ~ therapy + outcome + gender, data = data)
if (has_pkg("MASS")) {
fit <- MASS::loglm(~ therapy * outcome + gender, data = tab)
summary(fit)
} else {
message("Install MASS to run MASS::loglm().")
}## Formula:
## ~therapy * outcome + gender
## attr(,"variables")
## list(therapy, outcome, gender)
## attr(,"factors")
## therapy outcome gender therapy:outcome
## therapy 1 0 0 1
## outcome 0 1 0 1
## gender 0 0 1 0
## attr(,"term.labels")
## [1] "therapy" "outcome" "gender" "therapy:outcome"
## attr(,"order")
## [1] 1 1 1 2
## attr(,"intercept")
## [1] 1
## attr(,"response")
## [1] 0
## attr(,".Environment")
## <environment: R_GlobalEnv>
##
## Statistics:
## X^2 df P(> X^2)
## Likelihood Ratio 2.967460 5 0.7050026
## Pearson 2.956001 5 0.7067692
Path analysis / mediation
Purpose: Use path analysis or mediation to evaluate a theoretically specified indirect pathway between measured variables. This can estimate indirect effects, but causal interpretation still depends on design and assumptions.
data <- read_example("pathAnalysis.csv")
if (has_pkg("lavaan")) {
model <- '
mediator ~ a * predictor
outcome ~ b * mediator + c_prime * predictor
indirect := a * b
total := c_prime + indirect
'
fit <- lavaan::sem(model, data = data, se = "bootstrap", bootstrap = 5000)
summary(fit, standardized = TRUE, fit.measures = TRUE, rsquare = TRUE,ci = TRUE)
} else {
message("Install lavaan to run the mediation model.")
}## Length Class Mode
## 1 lavaan S4
## lhs op rhs label est se z pvalue ci.lower
## 1 mediator ~ predictor a 0.968 0.016 61.454 0.000 0.938
## 2 outcome ~ mediator b 0.867 0.220 3.935 0.000 0.332
## 3 outcome ~ predictor c_prime 0.147 0.210 0.701 0.483 -0.196
## 4 mediator ~~ mediator 0.603 0.180 3.351 0.001 0.232
## 5 outcome ~~ outcome 0.174 0.054 3.215 0.001 0.025
## 6 predictor ~~ predictor 139.500 0.000 NA NA 139.500
## 7 indirect := a*b indirect 0.840 0.218 3.855 0.000 0.316
## 8 total := c_prime+indirect total 0.987 0.015 63.732 0.000 0.962
## ci.upper std.lv std.all std.nox
## 1 1.001 0.968 0.998 0.084
## 2 1.221 0.867 0.851 0.851
## 3 0.647 0.147 0.149 0.013
## 4 0.933 0.603 0.005 0.005
## 5 0.228 0.174 0.001 0.001
## 6 139.500 139.500 1.000 139.500
## 7 1.196 0.840 0.849 0.072
## 8 1.024 0.987 0.998 0.084
#Extract regression path
pe <- lavaan::parameterEstimates(
fit,
standardized = TRUE,
ci = TRUE
)
pe[pe$op == "~", ]## lhs op rhs label est se z pvalue ci.lower ci.upper
## 1 mediator ~ predictor a 0.968 0.016 61.454 0.000 0.938 1.001
## 2 outcome ~ mediator b 0.867 0.220 3.935 0.000 0.332 1.221
## 3 outcome ~ predictor c_prime 0.147 0.210 0.701 0.483 -0.196 0.647
## std.lv std.all std.nox
## 1 0.968 0.998 0.084
## 2 0.867 0.851 0.851
## 3 0.147 0.149 0.013
## mediator outcome
## 0.995 0.999
Structural equation modeling
Purpose: Use SEM to test models that combine latent variables, measurement models, and structural relations among variables.
data <- read_example("structuralEquationModeling.csv")
if (has_pkg("lavaan")) {
model <- '
stress =~ stress1 + stress2 + stress3
recovery =~ sleep1 + sleep2 + sleep3
recovery ~ stress
performance ~ recovery + stress
'
fit <- lavaan::sem(model, data = data)
if (lavaan::lavInspect(fit, "converged")) {
print(summary(fit, standardized = TRUE, fit.measures = TRUE, rsquare = TRUE))
print(lavaan::parameterEstimates(fit, standardized = TRUE, ci = TRUE))
} else {
cat("This example SEM did not converge; its estimates should not be interpreted.\n")
}
} else {
message("Install lavaan to run SEM.")
}## This example SEM did not converge; its estimates should not be interpreted.
Confirmatory factor analysis
Purpose: Use confirmatory factor analysis to test whether observed indicators fit a measurement model defined before looking at the data. CFA is appropriate when the expected factors and item-factor assignments are theory-driven.
data <- read_example("confirmatoryFactorAnalysis.csv")
if (has_pkg("lavaan")) {
model <- '
stress =~ stress1 + stress2 + stress3
recovery =~ recovery1 + recovery2 + recovery3
confidence =~ conf1 + conf2 + conf3
'
fit <- lavaan::cfa(model, data = data)
summary(fit, standardized = TRUE, fit.measures = TRUE)
lavaan::fitMeasures(fit, c("cfi", "tli", "rmsea", "srmr"))
} else {
message("Install lavaan to run CFA.")
}## cfi tli rmsea srmr
## 0.968 0.952 0.158 0.024
Discriminant analysis
Purpose: Use discriminant analysis to classify cases into known groups using continuous predictors and to describe which predictors separate the groups.
data <- read_example("discriminantAnalysis.csv")
data$group <- factor(data$group)
if (has_pkg("MASS")) {
fit <- MASS::lda(group ~ anxiety + motivation + reaction_time, data = data)
fit
pred <- predict(fit)$class
table(Predicted = pred, Observed = data$group)
mean(pred == data$group)
} else {
message("Install MASS to run linear discriminant analysis.")
}## [1] 1
One-sample analyses
One-sample t-test
Purpose: Use a one-sample t-test to compare the mean of one continuous variable against a theoretical or reference value.
##
## One Sample t-test
##
## data: data$score
## t = 84.126, df = 13, p-value < 2.2e-16
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
## 51.98692 54.72736
## sample estimates:
## mean of x
## 53.35714
One-sample z-test
Purpose: Use a one-sample z-test to compare a sample mean against a reference mean when the population standard deviation is known.
data <- read_example("oneSampleZ.csv")
mu0 <- 50
sigma <- 15
x <- data$score
n <- length(x)
z <- (mean(x) - mu0) / (sigma / sqrt(n))
p <- 2 * pnorm(-abs(z))
c(
sample_mean = mean(x),
null_mean = mu0,
population_sd = sigma,
n = n,
z = z,
p = p
)## sample_mean null_mean population_sd n z
## 1.080667e+02 5.000000e+01 1.500000e+01 3.000000e+01 2.120295e+01
## p
## 8.969653e-100
One-sample Wilcoxon signed-rank test
Purpose: Use the one-sample Wilcoxon signed-rank test to compare a median or typical value against a reference value when a parametric t-test is not appropriate.
##
## Wilcoxon signed rank test with continuity correction
##
## data: data$rating
## V = 120, p-value = 0.000592
## alternative hypothesis: true location is not equal to 0
Two-group and paired analyses
Independent-samples t-test
Purpose: Use an independent-samples t-test to compare the means of two independent groups on a continuous outcome.
data <- read_example("independentT.csv")
data$group <- factor(data$group)
t.test(score ~ group, data = data, var.equal = FALSE)##
## Welch Two Sample t-test
##
## data: score by group
## t = -8.8299, df = 17.783, p-value = 6.452e-08
## alternative hypothesis: true difference in means between group control and group training is not equal to 0
## 95 percent confidence interval:
## -6.933593 -4.266407
## sample estimates:
## mean in group control mean in group training
## 19.7 25.3
Mann-Whitney U test
Purpose: Use the Mann-Whitney U test to compare two independent groups when the outcome is ordinal or when parametric assumptions are not suitable.
data <- read_example("mannWhitney.csv")
data$group <- factor(data$group)
wilcox.test(pain_score ~ group, data = data)##
## Wilcoxon rank sum test with continuity correction
##
## data: pain_score by group
## W = 1, p-value = 0.0002051
## alternative hypothesis: true location shift is not equal to 0
Paired-samples t-test
Purpose: Use a paired-samples t-test to compare two related measurements, such as pre-post scores from the same participants.
##
## Paired t-test
##
## data: data$baseline_score and data$followup_score
## t = -19.334, df = 47, p-value < 2.2e-16
## alternative hypothesis: true mean difference is not equal to 0
## 95 percent confidence interval:
## -5.660577 -4.593590
## sample estimates:
## mean difference
## -5.127083
##
## Shapiro-Wilk normality test
##
## data: data$difference
## W = 0.98296, p-value = 0.7051
Wilcoxon signed-rank test
Purpose: Use the Wilcoxon signed-rank test to compare two paired measurements when the differences are ordinal or non-normal.
##
## Wilcoxon signed rank test with continuity correction
##
## data: data$before and data$after
## V = 0, p-value = 0.002964
## alternative hypothesis: true location shift is not equal to 0
Variance tests
Chi-square test for one variance
Purpose: Use this test to compare a sample variance with a hypothesized population variance under normality assumptions.
data <- read_example("chiSquareVariance.csv")
n <- length(data$score)
s2 <- var(data$score)
sigma2_0 <- 100
chi2 <- (n - 1) * s2 / sigma2_0
p <- 2 * min(pchisq(chi2, df = n - 1), 1 - pchisq(chi2, df = n - 1))
c(
chi_square = chi2,
df = n - 1,
p = p
)## chi_square df p
## 21.10666667 11.00000000 0.06456529
F-test for two variances
Purpose: Use the F-test to compare whether two independent groups have equal variances.
data <- read_example("varianceFTest.csv")
data$group <- factor(data$group)
var.test(score ~ group, data = data)##
## F test to compare two variances
##
## data: score by group
## F = 0.059761, num df = 7, denom df = 7, p-value = 0.001418
## alternative hypothesis: true ratio of variances is not equal to 1
## 95 percent confidence interval:
## 0.01196431 0.29849903
## sample estimates:
## ratio of variances
## 0.05976065
ANOVA and nonparametric alternatives
One-way ANOVA
Purpose: Use one-way ANOVA to compare the means of three or more independent groups defined by one factor.
data <- read_example("anova.csv")
data$group <- factor(data$group)
fit <- aov(score ~ group, data = data)
summary(fit)## Df Sum Sq Mean Sq F value Pr(>F)
## group 2 265.33 132.67 85.9 6.1e-09 ***
## Residuals 15 23.17 1.54
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = score ~ group, data = data)
##
## $group
## diff lwr upr p adj
## low-high -9.333333 -11.197034 -7.469633 0.0000000
## medium-high -5.666667 -7.530367 -3.802966 0.0000029
## medium-low 3.666667 1.802966 5.530367 0.0003548
ANCOVA
Purpose: Use ANCOVA to compare group means on one continuous outcome while adjusting for one or more continuous covariates. It is useful when groups may differ on a baseline score or another variable that should be statistically controlled.
data <- read_example("ancova.csv")
data$group <- factor(data$group)
fit <- lm(post_anxiety ~ group + baseline_anxiety, data = data)
anova(fit)## Analysis of Variance Table
##
## Response: post_anxiety
## Df Sum Sq Mean Sq F value Pr(>F)
## group 2 1113.56 556.78 1262.4 < 2.2e-16 ***
## baseline_anxiety 1 365.22 365.22 828.1 < 2.2e-16 ***
## Residuals 32 14.11 0.44
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## Single term deletions
##
## Model:
## post_anxiety ~ group + baseline_anxiety
## Df Sum of Sq RSS AIC F value Pr(>F)
## <none> 14.11 -25.711
## group 2 1018.82 1032.94 124.839 1155.0 < 2.2e-16 ***
## baseline_anxiety 1 365.22 379.33 90.776 828.1 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
MANOVA
Purpose: Use MANOVA when you want to compare two or more related continuous dependent variables across groups at the same time. It first tests whether the groups differ on the combined outcome profile before considering any follow-up tests for individual outcomes.
data <- read_example("manova.csv")
data$group <- factor(data$group)
fit <- manova(
cbind(stress_score, wellbeing_score, performance_score) ~ group,
data = data
)
summary(fit, test = "Pillai")## Df Pillai approx F num Df den Df Pr(>F)
## group 2 0.47607 13.641 6 262 1.783e-13 ***
## Residuals 132
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## Df Wilks approx F num Df den Df Pr(>F)
## group 2 0.56426 14.354 6 260 3.935e-14 ***
## Residuals 132
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## Response stress_score :
## Df Sum Sq Mean Sq F value Pr(>F)
## group 2 721.9 360.97 13.091 6.507e-06 ***
## Residuals 132 3639.6 27.57
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Response wellbeing_score :
## Df Sum Sq Mean Sq F value Pr(>F)
## group 2 975.7 487.85 17.853 1.373e-07 ***
## Residuals 132 3607.0 27.33
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Response performance_score :
## Df Sum Sq Mean Sq F value Pr(>F)
## group 2 1716.7 858.35 34.958 6.555e-13 ***
## Residuals 132 3241.1 24.55
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
MANCOVA
Purpose: Use MANCOVA when groups are compared on two or more related continuous dependent variables while statistically controlling one or more continuous covariates. It is useful when baseline differences or participant characteristics should be adjusted before interpreting group differences.
data <- read_example("mancova.csv")
data$group <- factor(data$group)
fit <- manova(
cbind(stress_score, wellbeing_score, performance_score) ~ group + age_years + baseline_score,
data = data
)
summary(fit, test = "Pillai")## Df Pillai approx F num Df den Df Pr(>F)
## group 2 1.97801 899.69 6 60 < 2.2e-16 ***
## age_years 1 0.70799 23.44 3 29 6.654e-08 ***
## baseline_score 1 0.94963 182.25 3 29 < 2.2e-16 ***
## Residuals 31
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## Df Wilks approx F num Df den Df Pr(>F)
## group 2 0.000115 891.80 6 58 < 2.2e-16 ***
## age_years 1 0.292012 23.44 3 29 6.654e-08 ***
## baseline_score 1 0.050368 182.25 3 29 < 2.2e-16 ***
## Residuals 31
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## Response stress_score :
## Df Sum Sq Mean Sq F value Pr(>F)
## group 2 990.50 495.25 1034.028 < 2.2e-16 ***
## age_years 1 32.19 32.19 67.199 2.935e-09 ***
## baseline_score 1 239.47 239.47 499.981 < 2.2e-16 ***
## Residuals 31 14.85 0.48
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Response wellbeing_score :
## Df Sum Sq Mean Sq F value Pr(>F)
## group 2 1628.72 814.36 1565.61 < 2.2e-16 ***
## age_years 1 26.06 26.06 50.10 5.967e-08 ***
## baseline_score 1 222.98 222.98 428.68 < 2.2e-16 ***
## Residuals 31 16.12 0.52
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Response performance_score :
## Df Sum Sq Mean Sq F value Pr(>F)
## group 2 1602.89 801.44 1203.966 < 2.2e-16 ***
## age_years 1 27.58 27.58 41.435 3.561e-07 ***
## baseline_score 1 213.20 213.20 320.277 < 2.2e-16 ***
## Residuals 31 20.64 0.67
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Repeated-measures ANOVA
Purpose: Use repeated-measures ANOVA to compare means across three or more related conditions or time points from the same participants.
data <- read_example("repeatedAnova.csv")
long_data <- data.frame(
id = rep(data$id, each = 3),
condition = factor(rep(c("baseline", "week4", "week8"), times = nrow(data))),
score = c(t(data[, c("baseline", "week4", "week8")]))
)
fit <- aov(score ~ condition + Error(id / condition), data = long_data)
summary(fit)##
## Error: id
## Df Sum Sq Mean Sq F value Pr(>F)
## Residuals 1 7.127 7.127
##
## Error: id:condition
## Df Sum Sq Mean Sq
## condition 2 149.1 74.57
##
## Error: Within
## Df Sum Sq Mean Sq F value Pr(>F)
## condition 2 32.02 16.010 7.26 0.00342 **
## Residuals 24 52.92 2.205
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Two-way ANOVA
Purpose: Use two-way ANOVA to test the main and interaction effects of two or more independent factors on a continuous outcome.
data <- read_example("twoWayAnova.csv")
data$therapy <- factor(data$therapy)
data$age_group <- factor(data$age_group)
fit <- aov(stress_score ~ therapy * age_group, data = data)
summary(fit)## Df Sum Sq Mean Sq F value Pr(>F)
## therapy 1 469.2 469.2 79.120 1.29e-10 ***
## age_group 1 255.0 255.0 43.002 1.26e-07 ***
## therapy:age_group 1 4.2 4.2 0.712 0.404
## Residuals 36 213.5 5.9
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Two-way repeated-measures ANOVA
Purpose: Use two-way repeated-measures ANOVA to test main and interaction effects when the same participants are measured across combinations of repeated factors.
data <- read_example("twoWayRepeatedAnova.csv")
long_data <- data.frame(
participant = rep(data$participant, each = 6),
load = factor(rep(rep(c("low", "high"), each = 3), times = nrow(data))),
time = factor(rep(c("pre", "post", "followup"), times = 2 * nrow(data))),
score = c(t(data[, c("low_pre", "low_post", "low_followup", "high_pre", "high_post", "high_followup")]))
)
fit <- aov(score ~ load * time + Error(participant / (load * time)), data = long_data)
summary(fit)##
## Error: participant
## Df Sum Sq Mean Sq F value Pr(>F)
## Residuals 23 1008 43.82
##
## Error: participant:load
## Df Sum Sq Mean Sq F value Pr(>F)
## load 1 158.34 158.34 754.6 <2e-16 ***
## Residuals 23 4.83 0.21
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Error: participant:time
## Df Sum Sq Mean Sq F value Pr(>F)
## time 2 1265 632.5 3656 <2e-16 ***
## Residuals 46 8 0.2
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Error: participant:load:time
## Df Sum Sq Mean Sq F value Pr(>F)
## load:time 2 21.097 10.55 149.9 <2e-16 ***
## Residuals 46 3.236 0.07
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Kruskal-Wallis test
Purpose: Use the Kruskal-Wallis test to compare three or more independent groups when the outcome is ordinal or parametric assumptions are not suitable.
data <- read_example("kruskalWallis.csv")
data$group <- factor(data$group)
kruskal.test(rank_score ~ group, data = data)##
## Kruskal-Wallis rank sum test
##
## data: rank_score by group
## Kruskal-Wallis chi-squared = 15.028, df = 2, p-value = 0.0005455
Friedman test
Purpose: Use the Friedman test to compare three or more related conditions when repeated-measures ANOVA assumptions are not suitable.
data <- read_example("friedman.csv")
friedman.test(as.matrix(data[, c("condition_a", "condition_b", "condition_c")]))##
## Friedman rank sum test
##
## data: as.matrix(data[, c("condition_a", "condition_b", "condition_c")])
## Friedman chi-squared = 18.541, df = 2, p-value = 9.418e-05
Nonparametric two-way ANOVA via aligned rank transform
Purpose: Use aligned rank transform ANOVA to test factorial effects with nonparametric data while retaining an ANOVA-like interaction framework.
data <- read_example("nonparametricTwoWayAnova.csv")
data$therapy <- factor(data$therapy)
data$age_group <- factor(data$age_group)
if (has_pkg("ARTool")) {
fit <- ARTool::art(stress_rating ~ therapy * age_group, data = data)
anova(fit)
} else {
message("Install ARTool to run aligned rank transform ANOVA.")
}## Analysis of Variance of Aligned Rank Transformed Data
##
## Table Type: Anova Table (Type III tests)
## Model: No Repeated Measures (lm)
## Response: art(stress_rating)
##
## Df Df.res F value Pr(>F)
## 1 therapy 1 36 72.966 3.5045e-10 ***
## 2 age_group 1 36 15.429 0.00037187 ***
## 3 therapy:age_group 1 36 0.000 1.00000000
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Nonparametric two-way repeated-measures ANOVA via aligned rank transform
Purpose: Use aligned rank transform methods for repeated factorial designs when the outcome does not meet parametric assumptions.
data <- read_example("nonparametricTwoWayRepeatedAnova.csv")
long_data <- data.frame(
participant = factor(rep(data$participant, each = 6)),
load = factor(rep(rep(c("low", "high"), each = 3), times = nrow(data))),
time = factor(rep(c("pre", "post", "followup"), times = 2 * nrow(data))),
stress_rating = c(t(data[, c("low_pre", "low_post", "low_followup", "high_pre", "high_post", "high_followup")]))
)
if (has_pkg("ARTool")) {
fit <- ARTool::art(stress_rating ~ load * time + Error(participant), data = long_data)
anova(fit)
} else {
message("Install ARTool to run aligned rank transform ANOVA.")
}## Analysis of Variance of Aligned Rank Transformed Data
##
## Table Type: Repeated Measures Analysis of Variance Table (Type I)
## Model: Repeated Measures (aov)
## Response: art(stress_rating)
##
## Error Df Df.res F value Pr(>F)
## 1 load Withn 1 115 3.9364e+02 <2e-16 ***
## 2 time Withn 2 115 3.0906e+02 <2e-16 ***
## 3 load:time Withn 2 115 8.7071e-30 1
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Frequencies and distributions
Binomial test
Purpose: Use the binomial test to compare an observed number of successes in a two-level outcome with an expected probability.
data <- read_example("binomialTest.csv")
successes <- sum(data$response == "yes")
n <- length(data$response)
binom.test(successes, n, p = 0.50)##
## Exact binomial test
##
## data: successes and n
## number of successes = 14, number of trials = 20, p-value = 0.1153
## alternative hypothesis: true probability of success is not equal to 0.5
## 95 percent confidence interval:
## 0.4572108 0.8810684
## sample estimates:
## probability of success
## 0.7
Chi-square goodness-of-fit test
Purpose: Use the chi-square goodness-of-fit test to compare observed category frequencies with expected frequencies from a theoretical distribution.
##
## A B C
## 12 8 5
##
## Chi-squared test for given probabilities
##
## data: observed
## X-squared = 2.96, df = 2, p-value = 0.2276
Regression models
Regression models estimate associations and predictions. They do not, by themselves, provide evidence of causality.
Linear regression
Purpose: Use linear regression to predict a continuous outcome from one or more predictors and estimate their associations. Regression coefficients should not be interpreted as causal effects without a suitable design.
data <- read_example("linearRegression.csv")
fit <- lm(
race_time_min ~ training_volume_km_week + vo2max_ml_kg_min + age_years,
data = data
)
summary(fit)##
## Call:
## lm(formula = race_time_min ~ training_volume_km_week + vo2max_ml_kg_min +
## age_years, data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.5961 -0.3601 -0.1200 0.3805 1.8638
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 81.77108 8.00704 10.212 1.37e-10 ***
## training_volume_km_week -0.04576 0.06357 -0.720 0.4780
## vo2max_ml_kg_min -0.65184 0.19190 -3.397 0.0022 **
## age_years 0.11934 0.02371 5.033 3.08e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.7385 on 26 degrees of freedom
## Multiple R-squared: 0.9887, Adjusted R-squared: 0.9874
## F-statistic: 759.4 on 3 and 26 DF, p-value: < 2.2e-16
Binary logistic regression
Purpose: Use binary logistic regression to predict a two-category outcome and estimate effects as log-odds or odds ratios.
data <- read_example("logisticRegression.csv")
data$injury <- factor(data$injury, levels = c("no", "yes"))
fit <- glm(
injury ~ training_load_au + previous_injury + recovery_score + age_years,
data = data,
family = binomial
)
summary(fit)##
## Call:
## glm(formula = injury ~ training_load_au + previous_injury + recovery_score +
## age_years, family = binomial, data = data)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 9.293e+02 4.908e+06 0 1
## training_load_au -5.776e-01 4.081e+03 0 1
## previous_injuryyes -6.158e+00 1.336e+05 0 1
## recovery_score -8.640e+00 3.829e+04 0 1
## age_years -1.526e+00 4.149e+04 0 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 4.3860e+01 on 31 degrees of freedom
## Residual deviance: 4.5513e-10 on 27 degrees of freedom
## AIC: 10
##
## Number of Fisher Scoring iterations: 25
## (Intercept) training_load_au previous_injuryyes recovery_score
## Inf 0.5612239961 0.0021169728 0.0001769126
## age_years
## 0.2173359948
Multinomial logistic regression
Purpose: Use multinomial logistic regression to predict an outcome with more than two unordered categories.
data <- read_example("multinomialRegression.csv")
if (has_pkg("nnet")) {
data$treatment_outcome <- relevel(factor(data$treatment_outcome), ref = "completion")
fit <- nnet::multinom(
treatment_outcome ~ baseline_symptom_score + motivation_score + previous_therapy + age_years,
data = data
)
summary(fit)
} else {
message("Install nnet to run multinomial logistic regression.")
}## # weights: 18 (10 variable)
## initial value 49.437553
## iter 10 value 6.671487
## iter 20 value 0.022366
## iter 30 value 0.004528
## iter 40 value 0.002878
## iter 50 value 0.002641
## iter 60 value 0.001961
## iter 70 value 0.001823
## iter 80 value 0.001242
## iter 90 value 0.001146
## iter 100 value 0.000850
## final value 0.000850
## stopped after 100 iterations
## Call:
## nnet::multinom(formula = treatment_outcome ~ baseline_symptom_score +
## motivation_score + previous_therapy + age_years, data = data)
##
## Coefficients:
## (Intercept) baseline_symptom_score motivation_score previous_therapyyes
## dropout -33.98122 5.509793 -27.93061 -8.623852
## switch 19.85701 2.638611 -12.04692 -5.902525
## age_years
## dropout -4.558764
## switch -2.382015
##
## Std. Errors:
## (Intercept) baseline_symptom_score motivation_score previous_therapyyes
## dropout 84.82273 47.49466 255.3361 279.6206
## switch 67.97662 44.96074 222.8516 276.9262
## age_years
## dropout 91.49469
## switch 92.43814
##
## Residual Deviance: 0.001700612
## AIC: 20.0017
Linear mixed model
Purpose: Use a linear mixed model for continuous outcomes when observations are clustered or repeated, allowing random effects such as participant-level intercepts.
data <- read_example("linearMixedModel.csv")
data$participant <- factor(data$participant)
data$condition <- factor(data$condition)
data$team <- factor(data$team)
if (has_pkg("lme4") && has_pkg("lmerTest")) {
fit <- lmerTest::lmer(score ~ condition + week + (1 | participant), data = data)
summary(fit)
if (has_pkg("performance")) {
performance::r2_nakagawa(fit)
}
} else {
message("Install lme4 and lmerTest to run the linear mixed model.")
}## # R2 for Mixed Models
##
## Conditional R2: 0.821
## Marginal R2: 0.528
Generalized linear mixed model
Purpose: Use a generalized linear mixed model for non-continuous outcomes, such as binary responses, when observations are clustered or repeated.
data <- read_example("generalizedMixedModel.csv")
data$participant <- factor(data$participant)
data$condition <- factor(data$condition)
data$success_num <- ifelse(data$success %in% c("yes", "1", 1, "success"), 1, 0)
if (has_pkg("lme4")) {
fit <- lme4::glmer(
success_num ~ condition + week + (1 | participant),
data = data,
family = binomial
)
summary(fit)
exp(lme4::fixef(fit))
} else {
message("Install lme4 to run the generalized linear mixed model.")
}## (Intercept) conditionintervention week
## 1.034478e-05 2.013264e+00 2.478716e+02
Ordinal mixed model
Purpose: Use an ordinal mixed model for ordered categorical outcomes collected in clustered or repeated-measures designs.
data <- read_example("ordinalMixedModel.csv")
data$participant <- factor(data$participant)
data$condition <- factor(data$condition)
data$rating <- ordered(data$rating)
if (has_pkg("ordinal")) {
fit <- ordinal::clmm(rating ~ condition + day + (1 | participant), data = data)
summary(fit)
exp(coef(fit))
} else {
message("Install ordinal to run the ordinal mixed model.")
}## 1|2 2|3 3|4
## 3.095034e+00 6.828107e+06 1.059881e+15
## 4|5 conditionintervention day
## 2.264493e+21 4.017512e+04 9.238886e+04
Exploratory hypotheses
Factor analysis
Purpose: Use factor analysis to identify latent dimensions that explain patterns of correlations among several observed variables.
data <- read_example("factorAnalysis.csv")
items <- c(
"stress_item_1", "stress_item_2", "stress_item_3",
"mood_item_1", "mood_item_2", "mood_item_3",
"sleep_item_1", "sleep_item_2"
)
if (has_pkg("psych")) {
psych::fa.parallel(data[, items], fa = "fa")
fit <- psych::fa(data[, items], nfactors = 3, rotate = "oblimin")
print(fit$loadings, cutoff = .30)
} else {
fit <- factanal(data[, items], factors = 3, rotation = "promax")
print(fit)
}## Parallel analysis suggests that the number of factors = 1 and the number of components = NA
##
## Loadings:
## MR1 MR2 MR3
## stress_item_1 0.969
## stress_item_2 0.808
## stress_item_3 0.789
## mood_item_1 -0.969
## mood_item_2 -0.735
## mood_item_3 -0.492 -0.454
## sleep_item_1 1.069
## sleep_item_2 1.003
##
## MR1 MR2 MR3
## SS loadings 4.478 1.894 0.217
## Proportion Var 0.560 0.237 0.027
## Cumulative Var 0.560 0.797 0.824
Network analysis
Purpose: Use network analysis to explore direct relations among several variables while keeping each variable as a node. The resulting edges should be interpreted as conditional associations, not as evidence of causality.
data <- read_example("networkAnalysis.csv")
items <- c("stress", "sleep_quality", "rumination", "mood", "motivation", "performance")
cor_mat <- cor(data[, items])
round(cor_mat, 2)## stress sleep_quality rumination mood motivation performance
## stress 1.00 -0.98 0.95 -0.96 -0.96 -0.98
## sleep_quality -0.98 1.00 -0.96 0.98 0.96 0.98
## rumination 0.95 -0.96 1.00 -0.97 -0.95 -0.98
## mood -0.96 0.98 -0.97 1.00 0.95 0.98
## motivation -0.96 0.96 -0.95 0.95 1.00 0.98
## performance -0.98 0.98 -0.98 0.98 0.98 1.00
Cluster analysis
Purpose: Use cluster analysis to group people or objects into relatively homogeneous clusters based on similarity across variables.
data <- read_example("clusterAnalysis.csv")
variables <- c("training_load", "recovery_score", "sprint_time", "jump_height", "sleep_time")
scaled <- scale(data[, variables])
d <- dist(scaled, method = "euclidean")
fit <- hclust(d, method = "ward.D2")
plot(fit)## clusters
## 1 2 3
## 4 4 4
##
## 1 2 3
## 4 4 4
Multidimensional scaling
Purpose: Use multidimensional scaling to represent similarities or distances among objects in a low-dimensional visual space.
data <- read_example("multidimensionalScaling.csv")
variables <- c("calm", "joy", "anger", "sadness", "fear")
d <- dist(scale(data[, variables]))
fit <- cmdscale(d, k = 2, eig = TRUE)
plot(fit$points, xlab = "Dimension 1", ylab = "Dimension 2")
text(fit$points, labels = data$object, pos = 3)## initial value 3.393709
## final value 0.000000
## converged
## [1] 1.823673e-14
Meta-analysis
Odds ratio meta-analysis
Purpose: Use this meta-analysis when primary studies report binary events in two groups and the effect is summarized as an odds ratio.
data <- read_example("metaOddsRatio.csv")
if (has_pkg("metafor")) {
fit <- metafor::rma(
yi = log_odds_ratio,
vi = variance,
mods = ~ moderator_code,
data = data,
method = "REML"
)
summary(fit)
predict(fit, transf = exp)
metafor::forest(fit, slab = data$study_label)
} else {
message("Install metafor to run this meta-analysis.")
}Correlation coefficient meta-analysis
Purpose: Use this meta-analysis to synthesize correlations across studies, typically after transforming correlations to Fisher’s z scale.
data <- read_example("metaCorrelation.csv")
if (has_pkg("metafor")) {
es <- metafor::escalc(measure = "ZCOR", ri = correlation, ni = sample_size, data = data)
fit <- metafor::rma(yi, vi, mods = ~ moderator, data = es, method = "REML")
summary(fit)
predict(fit, transf = metafor::transf.ztor)
metafor::forest(fit, slab = data$study_label)
} else {
message("Install metafor to run this meta-analysis.")
}Mean difference meta-analysis
Purpose: Use this meta-analysis when studies report group means, standard deviations, and sample sizes for a continuous outcome.
data <- read_example("metaMeanDifferences.csv")
if (has_pkg("metafor")) {
es <- metafor::escalc(
measure = "SMD",
m1i = mean_treatment,
sd1i = sd_treatment,
n1i = n_treatment,
m2i = mean_control,
sd2i = sd_control,
n2i = n_control,
data = data
)
fit <- metafor::rma(yi, vi, mods = ~ moderator, data = es, method = "REML")
summary(fit)
metafor::forest(fit, slab = data$study_label)
} else {
message("Install metafor to run this meta-analysis.")
}Effect size meta-analysis
Purpose: Use this workflow when studies already provide an effect size with its variance or standard error.
data <- read_example("metaEffectSizes.csv")
if (has_pkg("metafor")) {
fit <- metafor::rma(
yi = effect_size,
vi = variance,
mods = ~ moderator,
data = data,
method = "REML"
)
summary(fit)
metafor::forest(fit, slab = data$study_label)
metafor::funnel(fit)
metafor::regtest(fit, model = "rma")
} else {
message("Install metafor to run this meta-analysis.")
}##
## Regression Test for Funnel Plot Asymmetry
##
## Model: mixed-effects meta-regression model
## Predictor: standard error
##
## Test for Funnel Plot Asymmetry: z = -0.2219, p = 0.8244
Fixed-effect meta-analysis
Purpose: Use a fixed-effect meta-analysis when the goal is to estimate one common effect assumed to be shared by all included studies.
data <- read_example("metaEffectSizes.csv")
if (has_pkg("metafor")) {
fit <- metafor::rma(
yi = effect_size,
sei = se,
data = data,
method = "FE"
)
summary(fit)
metafor::forest(fit, slab = data$study_label)
} else {
message("Install metafor to run this meta-analysis.")
}Random-effects meta-analysis
Purpose: Use a random-effects meta-analysis when true effects are expected to vary across studies and the goal is to estimate the mean of that distribution.
data <- read_example("metaEffectSizes.csv")
if (has_pkg("metafor")) {
fit <- metafor::rma(
yi = effect_size,
sei = se,
data = data,
method = "REML"
)
summary(fit)
predict(fit)
metafor::forest(fit, slab = data$study_label)
} else {
message("Install metafor to run this meta-analysis.")
}Subgroup meta-analysis
Purpose: Use subgroup meta-analysis to examine whether pooled effects differ across categorical study characteristics.
data <- read_example("metaEffectSizes.csv")
if (has_pkg("metafor")) {
fit <- metafor::rma(
yi = effect_size,
sei = se,
mods = ~ moderator,
data = data,
method = "REML"
)
summary(fit)
by(
data,
data$moderator,
function(d) summary(metafor::rma(yi = effect_size, sei = se, data = d, method = "REML"))
)
} else {
message("Install metafor to run this meta-analysis.")
}## data$moderator: psychology
##
## Random-Effects Model (k = 5; tau^2 estimator: REML)
##
## logLik deviance AIC BIC AICc
## 1.5074 -3.0148 0.9852 -0.2422 12.9852
##
## tau^2 (estimated amount of total heterogeneity): 0 (SE = 0.0242)
## tau (square root of estimated tau^2 value): 0
## I^2 (total heterogeneity / total variability): 0.00%
## H^2 (total variability / sampling variability): 1.00
##
## Test for Heterogeneity:
## Q(df = 4) = 2.9246, p-val = 0.5705
##
## Model Results:
##
## estimate se zval pval ci.lb ci.ub
## -0.5400 0.0830 -6.5050 <.0001 -0.7028 -0.3773 ***
##
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## ------------------------------------------------------------
## data$moderator: sport
##
## Random-Effects Model (k = 5; tau^2 estimator: REML)
##
## logLik deviance AIC BIC AICc
## -3.6788 7.3575 11.3575 10.1301 23.3575
##
## tau^2 (estimated amount of total heterogeneity): 0.3193 (SE = 0.2598)
## tau (square root of estimated tau^2 value): 0.5651
## I^2 (total heterogeneity / total variability): 87.01%
## H^2 (total variability / sampling variability): 7.70
##
## Test for Heterogeneity:
## Q(df = 4) = 30.4576, p-val < .0001
##
## Model Results:
##
## estimate se zval pval ci.lb ci.ub
## 0.1261 0.2711 0.4651 0.6418 -0.4053 0.6574
##
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Meta-regression
Purpose: Use meta-regression to examine whether study-level moderators explain variation in effect sizes across studies.
data <- read_example("metaEffectSizes.csv")
if (has_pkg("metafor")) {
fit <- metafor::rma(
yi = effect_size,
sei = se,
mods = ~ moderator,
data = data,
method = "REML"
)
summary(fit)
} else {
message("Install metafor to run meta-regression.")
}##
## Mixed-Effects Model (k = 10; tau^2 estimator: REML)
##
## logLik deviance AIC BIC AICc
## -4.7349 9.4698 15.4698 15.7082 21.4698
##
## tau^2 (estimated amount of residual heterogeneity): 0.1431 (SE = 0.0925)
## tau (square root of estimated tau^2 value): 0.3783
## I^2 (residual heterogeneity / unaccounted variability): 77.97%
## H^2 (unaccounted variability / sampling variability): 4.54
## R^2 (amount of heterogeneity accounted for): 44.47%
##
## Test for Residual Heterogeneity:
## QE(df = 8) = 33.3822, p-val < .0001
##
## Test of Moderators (coefficient 2):
## QM(df = 1) = 6.4230, p-val = 0.0113
##
## Model Results:
##
## estimate se zval pval ci.lb ci.ub
## intrcpt -0.5659 0.1895 -2.9859 0.0028 -0.9373 -0.1944 **
## moderatorsport 0.6901 0.2723 2.5344 0.0113 0.1564 1.2238 *
##
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Publication-bias diagnostics
Purpose: Use publication-bias diagnostics to inspect funnel plot asymmetry and possible small-study effects after fitting a meta-analysis model.
data <- read_example("metaEffectSizes.csv")
if (has_pkg("metafor")) {
fit <- metafor::rma(
yi = effect_size,
sei = se,
data = data,
method = "REML"
)
metafor::funnel(fit)
metafor::regtest(fit, model = "rma")
metafor::trimfill(fit)
} else {
message("Install metafor to run publication-bias diagnostics.")
}##
## Estimated number of missing studies on the left side: 0 (SE = 2.0440)
##
## Random-Effects Model (k = 10; tau^2 estimator: REML)
##
## tau^2 (estimated amount of total heterogeneity): 0.2577 (SE = 0.1414)
## tau (square root of estimated tau^2 value): 0.5076
## I^2 (total heterogeneity / total variability): 86.48%
## H^2 (total variability / sampling variability): 7.40
##
## Test for Heterogeneity:
## Q(df = 9) = 59.1993, p-val < .0001
##
## Model Results:
##
## estimate se zval pval ci.lb ci.ub
## -0.2279 0.1732 -1.3159 0.1882 -0.5674 0.1115
##
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Proportion meta-analysis
Purpose: Use proportion meta-analysis to synthesize event proportions or prevalence estimates across studies.
data <- read_example("metaProportion.csv")
if (has_pkg("metafor")) {
es <- metafor::escalc(
measure = "PLO",
xi = event_frequency,
ni = total_sample_size,
data = data
)
fit <- metafor::rma(yi, vi, mods = ~ moderator_code, data = es, method = "REML")
summary(fit)
predict(fit, transf = metafor::transf.ilogit)
metafor::forest(fit, slab = data$study_label)
} else {
message("Install metafor to run this meta-analysis.")
}Machine learning regression
Decision tree regression
Purpose: Use decision tree regression to predict a continuous outcome with transparent if-then decision rules.
data <- read_example("decisionTreeRegression.csv")
model_data <- subset(data, select = -client)
if (has_pkg("rpart")) {
set.seed(1)
train_id <- sample(seq_len(nrow(model_data)), size = floor(0.70 * nrow(model_data)))
fit <- rpart::rpart(
outcome_symptom_score ~ motivation_score + baseline_distress_score + sessions_attended,
data = model_data[train_id, ],
method = "anova",
control = rpart::rpart.control(cp = .01)
)
pred <- predict(fit, newdata = model_data[-train_id, ])
actual <- model_data$outcome_symptom_score[-train_id]
c(
RMSE = sqrt(mean((pred - actual)^2)),
MAE = mean(abs(pred - actual)),
R2 = cor(pred, actual)^2
)
plot(fit)
text(fit)
} else {
message("Install rpart to run decision tree regression.")
}Random forest regression
Purpose: Use random forest regression to improve continuous-outcome prediction by averaging many decision trees and estimating variable importance.
data <- read_example("randomForestRegression.csv")
model_data <- subset(data, select = -athlete)
if (has_pkg("randomForest")) {
set.seed(1)
train_id <- sample(seq_len(nrow(model_data)), size = floor(0.70 * nrow(model_data)))
fit <- randomForest::randomForest(
race_time_min ~ .,
data = model_data[train_id, ],
ntree = 500,
importance = TRUE
)
pred <- predict(fit, newdata = model_data[-train_id, ])
actual <- model_data$race_time_min[-train_id]
c(
RMSE = sqrt(mean((pred - actual)^2)),
MAE = mean(abs(pred - actual)),
R2 = cor(pred, actual)^2
)
randomForest::importance(fit)
} else {
message("Install randomForest to run random forest regression.")
}## %IncMSE IncNodePurity
## training_volume_km_week 13.841384 293.01803
## vo2max_ml_kg_min 12.687973 247.91246
## tempo_pace_min_km 11.619106 230.47331
## long_run_km 11.077403 231.25752
## recovery_score 11.268020 250.28043
## sleep_hours 9.712273 170.88730
## age_years 7.212518 26.74854
## injury_days_last_month 6.256555 60.74022
k-nearest neighbors regression
Purpose: Use k-nearest neighbors regression to predict a continuous outcome from the outcomes of similar cases in the predictor space.
data <- read_example("knnRegression.csv")
model_data <- subset(data, select = -participant)
if (has_pkg("caret")) {
set.seed(1)
ctrl <- caret::trainControl(method = "cv", number = 10)
fit <- caret::train(
wellbeing_score ~ sleep_hours + stress_score + physical_activity_min_week + social_support_score,
data = model_data,
method = "knn",
trControl = ctrl,
preProcess = c("center", "scale"),
tuneLength = 10
)
fit
} else {
message("Install caret to run k-nearest neighbors regression.")
}## k-Nearest Neighbors
##
## 60 samples
## 4 predictor
##
## Pre-processing: centered (4), scaled (4)
## Resampling: Cross-Validated (10 fold)
## Summary of sample sizes: 54, 52, 55, 54, 54, 56, ...
## Resampling results across tuning parameters:
##
## k RMSE Rsquared MAE
## 5 0.8780874 0.9953210 0.6747143
## 7 0.9991272 0.9947135 0.7866497
## 9 1.2284369 0.9930314 0.9449735
## 11 1.3550122 0.9917154 0.9799919
## 13 1.5656150 0.9897858 1.1271978
## 15 1.8735785 0.9853155 1.3204365
## 17 2.1412111 0.9812369 1.4230182
## 19 2.4874152 0.9787332 1.6676629
## 21 2.8636398 0.9726694 1.9309240
## 23 3.2583268 0.9672477 2.1707557
##
## RMSE was used to select the optimal model using the smallest value.
## The final value used for the model was k = 5.
Machine learning classification
Decision tree classifier
Purpose: Use a decision tree classifier to predict categorical group membership with interpretable decision rules.
data <- read_example("decisionTreeClassifier.csv")
data$class <- factor(data$class)
model_data <- subset(data, select = -participant)
if (has_pkg("rpart")) {
set.seed(1)
train_id <- sample(seq_len(nrow(model_data)), size = floor(0.70 * nrow(model_data)))
fit <- rpart::rpart(
class ~ .,
data = model_data[train_id, ],
method = "class"
)
pred <- predict(fit, newdata = model_data[-train_id, ], type = "class")
table(Predicted = pred, Observed = model_data$class[-train_id])
mean(pred == model_data$class[-train_id])
# plot(fit)
# text(fit)
} else {
message("Install rpart to run decision tree classification.")
}## [1] 0.4444444
Random forest classifier
Purpose: Use a random forest classifier to improve categorical prediction by combining many trees and evaluating variable importance.
data <- read_example("randomForestClassifier.csv")
data$class <- factor(data$class)
model_data <- subset(data, select = -athlete)
if (has_pkg("randomForest")) {
set.seed(1)
train_id <- sample(seq_len(nrow(model_data)), size = floor(0.70 * nrow(model_data)))
fit <- randomForest::randomForest(
class ~ .,
data = model_data[train_id, ],
ntree = 500,
importance = TRUE
)
pred <- predict(fit, newdata = model_data[-train_id, ])
table(Predicted = pred, Observed = model_data$class[-train_id])
mean(pred == model_data$class[-train_id])
randomForest::importance(fit)
} else {
message("Install randomForest to run random forest classification.")
}## high low moderate MeanDecreaseAccuracy
## training_load_au 12.321786 10.713224 9.102714 14.019030
## recovery_score 12.485727 11.785023 9.094083 15.010969
## sleep_hours 12.345697 10.550427 8.753778 13.743853
## previous_injury 3.567065 3.940552 -3.461392 3.238400
## soreness_score 12.862177 12.492907 9.138869 15.110068
## age_years 8.292441 7.039137 2.418129 9.188735
## MeanDecreaseGini
## training_load_au 2.5642061
## recovery_score 2.8597106
## sleep_hours 2.5171404
## previous_injury 0.3698107
## soreness_score 3.0796034
## age_years 1.2720049
k-nearest neighbors classifier
Purpose: Use k-nearest neighbors classification to assign cases to categories based on the classes of similar observations.
data <- read_example("knnClassifier.csv")
data$class <- factor(data$class)
model_data <- subset(data, select = -participant)
if (has_pkg("caret")) {
set.seed(1)
ctrl <- caret::trainControl(method = "cv", number = 10)
fit <- caret::train(
class ~ .,
data = model_data,
method = "knn",
trControl = ctrl,
preProcess = c("center", "scale"),
tuneLength = 10
)
fit
} else {
message("Install caret to run k-nearest neighbors classification.")
}## k-Nearest Neighbors
##
## 30 samples
## 5 predictor
## 3 classes: 'high_wellbeing', 'low_wellbeing', 'moderate_wellbeing'
##
## Pre-processing: centered (5), scaled (5)
## Resampling: Cross-Validated (10 fold)
## Summary of sample sizes: 27, 27, 27, 27, 27, 27, ...
## Resampling results across tuning parameters:
##
## k Accuracy Kappa
## 5 1.0000000 1.00
## 7 1.0000000 1.00
## 9 1.0000000 1.00
## 11 1.0000000 1.00
## 13 1.0000000 1.00
## 15 1.0000000 1.00
## 17 1.0000000 1.00
## 19 0.6333333 0.45
## 21 0.6333333 0.45
## 23 0.4666667 0.20
##
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was k = 17.
Naive Bayes classifier
Purpose: Use a Naive Bayes classifier as a fast probabilistic model for categorical prediction, especially as a baseline model.
data <- read_example("naiveBayes.csv")
data$class <- factor(data$class)
model_data <- subset(data, select = -student)
if (has_pkg("e1071")) {
set.seed(1)
train_id <- sample(seq_len(nrow(model_data)), size = floor(0.70 * nrow(model_data)))
fit <- e1071::naiveBayes(class ~ ., data = model_data[train_id, ])
pred <- predict(fit, newdata = model_data[-train_id, ])
table(Predicted = pred, Observed = model_data$class[-train_id])
mean(pred == model_data$class[-train_id])
} else {
message("Install e1071 to run naive Bayes classification.")
}## [1] 1
Principal component analysis
Principal component analysis
Purpose: Use PCA to reduce several correlated continuous variables into fewer components that summarize the main sources of variance.
data <- read_example("principalComponentAnalysis.csv")
variables <- c(
"attention_accuracy",
"working_memory",
"processing_speed",
"inhibition_score",
"cognitive_flexibility",
"verbal_memory",
"spatial_reasoning"
)
vars <- data[, variables]
fit <- prcomp(vars, center = TRUE, scale. = TRUE)
summary(fit)## Importance of components:
## PC1 PC2 PC3 PC4 PC5 PC6 PC7
## Standard deviation 2.6441 0.06736 0.05037 0.03283 0.01997 0.01451 3.081e-17
## Proportion of Variance 0.9988 0.00065 0.00036 0.00015 0.00006 0.00003 0.000e+00
## Cumulative Proportion 0.9988 0.99940 0.99976 0.99991 0.99997 1.00000 1.000e+00
## PC1 PC2 PC3 PC4
## attention_accuracy -0.3780990 0.16169560 0.319999090 -0.333217455
## working_memory -0.3777388 -0.70127074 -0.008562141 -0.319983152
## processing_speed -0.3780990 0.16169560 0.319999090 -0.333217455
## inhibition_score -0.3779550 -0.09117910 0.493009067 0.772979685
## cognitive_flexibility -0.3781527 0.01351191 -0.173274585 -0.066199878
## verbal_memory -0.3777806 0.64224748 -0.289534638 0.008492108
## spatial_reasoning -0.3779260 -0.18695021 -0.661978493 0.271276436
## PC5 PC6 PC7
## attention_accuracy 0.30522047 0.155893669 -7.071068e-01
## working_memory -0.49847680 0.120793591 1.830799e-16
## processing_speed 0.30522047 0.155893669 7.071068e-01
## inhibition_score -0.09091677 -0.003872043 2.082403e-16
## cognitive_flexibility 0.09445931 -0.901936194 2.016684e-16
## verbal_memory -0.57836888 0.162440800 1.785805e-16
## spatial_reasoning 0.46206366 0.311307457 -4.847109e-16