knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE)
# Install once if needed:
# install.packages(c("agricolae","gt","broom","corrplot","car"))
library(agricolae) # sweetpotato and corn datasets
library(gt) # nice tables
library(dplyr) # select() used with tidy() output
library(broom) # tidy() turns test/model output into a data frame
library(corrplot) # correlogram
library(car) # vif() for multicollinearity check
Module 6 uses sweetpotato (yield by
virus treatment, 12 plots).
Module 7 uses corn, a
different built-in agricolae dataset — a completely
randomized design comparing 4 corn-growing methods. It
has 34 plots and, importantly, two numeric variables recorded on
the same plots:
method — growing method (1 to 4)observation — yield per plotrx — a second numeric measurement taken on the same
plotHaving two numeric variables on the same units is what lets us do real (not simulated) correlation, regression, and paired testing in Module 7.
data(sweetpotato)
data(corn)
str(sweetpotato)
## 'data.frame': 12 obs. of 2 variables:
## $ virus: Factor w/ 4 levels "cc","fc","ff",..: 1 1 1 2 2 2 3 3 3 4 ...
## $ yield: num 28.5 21.7 23 14.9 10.6 13.1 41.8 39.2 28 38.2 ...
str(corn)
## 'data.frame': 34 obs. of 3 variables:
## $ method : int 1 1 1 1 1 1 1 1 1 2 ...
## $ observation: int 83 91 94 89 89 96 91 92 90 91 ...
## $ rx : num 11 23 28.5 17 17 31.5 23 26 19.5 23 ...
Descriptive statistics simply describe the data — count it, find its center, find its spread.
Definition. A frequency table shows how many times each category appears.
What it’s for. The first step in looking at any categorical variable — here, how many plots got each virus treatment.
Note: Works for any categorical variable.
freq <- table(sweetpotato$virus)
freq_df <- as.data.frame(freq)
names(freq_df) <- c("Virus", "Frequency")
gt(freq_df) |>
tab_header(title = "Frequency of Virus Treatments")
| Frequency of Virus Treatments | |
| Virus | Frequency |
|---|---|
| cc | 3 |
| fc | 3 |
| ff | 3 |
| oo | 3 |
Interpretation. Each row shows how many plots received that virus treatment. Each treatment appears 3 times here — a balanced design.
Definition. Turns each frequency into a percentage of the total.
What it’s for. Easier to compare groups on a common 0-100% scale.
virus_counts <- table(sweetpotato$virus)
pct_df <- data.frame(
Virus = names(virus_counts),
Percent = round(virus_counts / sum(virus_counts) * 100, 2)
)
gt(pct_df) |>
tab_header(title = "Percentage of Plots per Virus Treatment")
| Percentage of Plots per Virus Treatment | ||
| Virus | Percent.Var1 | Percent.Freq |
|---|---|---|
| cc | cc | 25 |
| fc | fc | 25 |
| ff | ff | 25 |
| oo | oo | 25 |
Interpretation. With 4 equal-sized groups, each should be about 25%.
Definition.
What it’s for. A quick first look at the spread of the data.
Note. Sensitive to outlier.
yield_min <- min(sweetpotato$yield)
yield_max <- max(sweetpotato$yield)
yield_range <- yield_max - yield_min
result1 <- data.frame(
Statistic = c("Minimum", "Maximum", "Range"),
Value = c(yield_min, yield_max, yield_range)
)
gt(result1) |>
tab_header(title = "Min, Max, and Range of Yield")
| Min, Max, and Range of Yield | |
| Statistic | Value |
|---|---|
| Minimum | 10.6 |
| Maximum | 41.8 |
| Range | 31.2 |
Interpretation. Range shows how far
apart the smallest and largest yields are. A large range means high
variability in the data.
Definition.
What it’s for. Finding one “typical” value to represent the data.
Note. Mean works best when data is not heavily skewed.
For the mode function, there is no native function for finding the mode. So we formulated a function for this example.
get_mode <- function(x) {
ux <- unique(x)
ux[which.max(tabulate(match(x, ux)))]
}
yield_mean <- mean(sweetpotato$yield)
yield_median <- median(sweetpotato$yield)
yield_mode <- get_mode(sweetpotato$yield)
result2 <- data.frame(
Statistic = c("Mean", "Median", "Mode"),
Value = c(yield_mean, yield_median, yield_mode)
)
gt(result2) |>
fmt_number(columns = Value, decimals = 2) |>
tab_header(title = "Central Tendency of Yield")
| Central Tendency of Yield | |
| Statistic | Value |
|---|---|
| Mean | 27.62 |
| Median | 28.25 |
| Mode | 28.50 |
By treatment group:
sweetpotato |>
group_by(virus) |>
summarise(mean= mean(yield, na.rm =TRUE),
median = median (yield, na.rm=TRUE)) |>
gt() |>
fmt_number(decimals = 2) |>
tab_header(title = "Mean and Median Yield by Virus Treatment")
| Mean and Median Yield by Virus Treatment | ||
| virus | mean | median |
|---|---|---|
| cc | 24.40 | 23.00 |
| fc | 12.87 | 13.10 |
| ff | 36.33 | 39.20 |
| oo | 36.90 | 38.20 |
Interpretation. If Mean and
Median are close, the data is fairly symmetric. A treatment
with a much higher mean than the others is producing more yield on
average.
Definition.
What it’s for. Tells you how spread out (consistent vs. erratic) the data is.
Note. Variance/SD assume numeric interval/ratio data. CV needs a meaningful non-zero mean.
yield <- sweetpotato$yield
result3 <- data.frame(
Statistic = c("Variance", "Standard Deviation", "CV (%)", "IQR"),
Value = c(var(yield), sd(yield), sd(yield) / mean(yield) * 100,
IQR(yield)
)
)
gt(result3) |>
fmt_number(columns = Value, decimals = 2) |>
tab_header(title = "Variability of Yield")
| Variability of Yield | |
| Statistic | Value |
|---|---|
| Variance | 122.74 |
| Standard Deviation | 11.08 |
| CV (%) | 40.10 |
| IQR | 18.45 |
By treatment group:
sweetpotato |>
group_by(virus) |>
summarise("standard deviation" = sd(yield, na.rm =TRUE)) |>
gt() |>
fmt_number(decimals = 2) |>
tab_header(title = "Standard Deviation of Yield by Virus Treatment")
| Standard Deviation of Yield by Virus Treatment | |
| virus | standard deviation |
|---|---|
| cc | 3.61 |
| fc | 2.16 |
| ff | 7.33 |
| oo | 4.30 |