R
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Data Structures
-
Section 4: Data Manipulation
-
Section 5: Visualization and Statistics
-
Section 6: Advanced R
-
Section 7: Practical Projects
-
Section 8: Interview Practice
-
Section 9: More Practice Exercises
-
Section 10: Shiny Apps in Depth
-
Section 11: More Data Wrangling
-
Section 12: Tidyverse Deep Dive
-
Section 13: Statistical Modeling Deep Dive
-
Section 14: Machine Learning in R
-
Section 15: R Visualization Deep Dive
-
Section 16: R Package Development Deep Dive
-
Section 17: R for Reproducible Research
-
Section 18: R and Databases
-
Section 19: R Performance Optimization
-
Section 20: Bioinformatics and Specialized R
-
Section 21: More Shiny Practice
-
Section 22: More Practice Exercises
-
Section 23: R Data Structures Deep Dive
-
Section 24: More Interview and Review
-
Section 25: R for Business Analytics
-
Section 26: R Text Mining and NLP
-
Section 27: R Spatial Data Analysis
-
Section 28: R Deep Learning
-
Section 29: Advanced Statistical Techniques
-
Section 30: R Object Systems Deep Dive
-
Section 31: R Environments and Metaprogramming
-
Section 32: R for Finance
-
Section 33: R for Clinical and Health Data
-
Section 34: More Shiny Advanced Practice
-
Section 35: R Data Cleaning Deep Dive
-
Section 36: R Reporting Automation
-
Section 37: More Practical Projects Round 2
-
Section 38: R Ecosystem and Career
-
Section 39: More Visualization Practice
-
Section 40: R for Experimentation
-
Section 41: R for Genomics and Bioinformatics Deep Dive
-
Section 42: R for Marketing Analytics
-
Section 43: R Data Import/Export Deep Dive
-
Section 44: R String Processing Deep Dive
-
Section 45: R for Actuarial and Insurance Analytics
-
Section 46: R Testing and Quality Assurance Deep Dive
-
Section 47: R Version Control and Collaboration
-
Section 48: R Advanced Functional Programming
-
Section 49: R for Supply Chain and Operations
-
Section 50: More Practice Exercises Round 3
-
Section 51: R Dashboards and BI Integration
-
Section 52: R Data Governance and Ethics
-
Section 53: More Modeling Practice
-
Section 54: R Final Capstone Projects
-
Section 55: R for Sports Analytics
-
Section 56: More Interview Practice Round 2
-
Section 57: R Networking and APIs
-
Section 58: R for Environmental Science
-
Section 59: R Notebook and Documentation Practices
-
Section 60: More Data Wrangling Mastery
-
Section 61: R for A/B Testing at Scale
-
Section 62: R Package Ecosystem Deep Dive
55: ANOVA and Post-Hoc Tests
Up until now, we've spent a lot of time comparing two groups using t-tests. But in the real world, you're rarely just comparing A and B. Usually, you've got A, B, C, and maybe a control group. If you try to run a dozen separate t-tests to compare every possible pair, you run into a massive statistical problem: the family-wise error rate. Basically, the more tests you run, the more likely you are to find a "significant" result just by pure chance. That's where ANOVA comes in.
Setting up our Plant Growth Experiment
Let's imagine we're testing three different types of fertilizer—let's call them 'NutriGrow', 'GreenLeaf', and 'BioBoost'—to see which one makes tomato plants grow the tallest. I've put together a small dataset to represent this. I'm keeping it simple so we can focus on the logic of the test rather than data cleaning.
# Creating the dataset
fertilizer <- c(rep("NutriGrow", 10), rep("GreenLeaf", 10), rep("BioBoost", 10))
height <- c(15, 16, 14, 17, 15, 16, 14, 15, 17, 16, # NutriGrow
20, 22, 19, 21, 20, 23, 18, 21, 20, 22, # GreenLeaf
16, 17, 15, 18, 16, 17, 15, 16, 18, 17) # BioBoost
plant_data <- data.frame(fertilizer, height)
Running the One-Way ANOVA
The One-Way ANOVA asks a very specific question: "Is there any statistically significant difference between the means of these groups?" It doesn't tell us which group is different, just that they aren't all the same. I'll use the aov() function, which is the standard way to handle this in R.
# This is where I usually jump straight in...
res_anova <- aov(height ~ fertilizer, data = plant_data)
summary(res_anova)
Wait, I just noticed something. If I had loaded this data from a CSV where the fertilizer column was just strings, R usually handles it, but if the column was coded as integers (1, 2, 3), aov() would treat it as a continuous variable—a regression—instead of a grouping factor. I always double-check my structure. Let's make sure fertilizer is explicitly a factor to avoid any weird behavior.
# Fixing the data type to be safe
plant_data$fertilizer <- as.factor(plant_data$fertilizer)
res_anova <- aov(height ~ fertilizer, data = plant_data)
summary(res_anova)
Looking at the output, if the p-value (Pr(>F)) is less than 0.05, we can reject the null hypothesis. In this case, the p-value is tiny, so we know at least one fertilizer is performing differently than the others. But we're still in the dark about which one is the winner.
Pinpointing the winner with Tukey's HSD
This is where the "Post-Hoc" part comes in. Since the ANOVA gave us a green light, we can now safely run a Tukey Honestly Significant Difference (HSD) test. This test compares all possible pairs while adjusting the p-values to account for the fact that we're doing multiple comparisons. It's the "safety net" that prevents us from claiming a discovery that's actually just noise.
# Performing the Tukey Post-Hoc test
tukey_result <- TukeyHSD(res_anova)
print(tukey_result)
The output gives us a comparison table. You'll see pairs like GreenLeaf-NutriGrow. If the p adj for that pair is below 0.05, that specific difference is significant. In our example, you'll likely see that GreenLeaf significantly outperforms both NutriGrow and BioBoost, while the difference between NutriGrow and BioBoost is negligible. Now we have an answer we can actually use.
📋 Practical Task
Analyzing Coffee Roast Caffeine Levels
You have been given a dataset containing caffeine levels (in mg) from three different roast levels of the same coffee bean: 'Light', 'Medium', and 'Dark'. Your goal is to determine if roast level significantly affects caffeine content.
Use the following data to start:
roast <- c(rep("Light", 8), rep("Medium", 8), rep("Dark", 8))
caffeine <- c(120, 125, 118, 122, 121, 124, 119, 123,
110, 115, 108, 112, 111, 114, 109, 113,
105, 110, 102, 108, 106, 109, 104, 107)
coffee_data <- data.frame(roast, caffeine)
Your task:
- Convert the
roastcolumn to a factor. - Perform a One-Way ANOVA to see if there is a significant difference in caffeine levels across the three roasts.
- If the ANOVA is significant, perform a Tukey HSD post-hoc test to determine exactly which roast levels differ from one another.
- Print the final Tukey results to the console.
There are no comments for now.