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
121: Factor Analysis in R
Alright, let's dive into Factor Analysis. I've got this dataset of employee survey responses—let's call it burnout_survey. We asked employees to rate ten different things on a scale of 1 to 5, like "I feel exhausted," "I can't concentrate," "My boss is supportive," and "I feel valued."
Now, looking at ten separate columns is a headache. I suspect that these ten questions aren't actually measuring ten different things, but rather a few "latent" factors—maybe something like "Emotional Exhaustion" and "Workplace Support." Factor analysis is how we prove that hunch.
Trying to see the signal in the noise
First, I'll just look at the correlations. If my theory is right, questions that belong to the same factor should be highly correlated with each other.
# Creating a dummy dataset for our session
set.seed(123)
burnout_survey <- data.frame(
exhausted = rnorm(100, 3, 1),
insomnia = rnorm(100, 3, 1),
irritable = rnorm(100, 3, 1),
supported = rnorm(100, 3, 1),
valued = rnorm(100, 3, 1),
feedback = rnorm(100, 3, 1),
headache = rnorm(100, 3, 1),
cynical = rnorm(100, 3, 1),
equipment = rnorm(100, 3, 1),
culture = rnorm(100, 3, 1)
)
# Adding some artificial correlation to make the FA actually work
burnout_survey$insomnia <- burnout_survey$exhausted * 0.7 + rnorm(100)
burnout_survey$irritable <- burnout_survey$exhausted * 0.6 + rnorm(100)
burnout_survey$valued <- burnout_survey$supported * 0.8 + rnorm(100)
burnout_survey$feedback <- burnout_survey$supported * 0.7 + rnorm(100)
cor(burnout_survey)
The output is a giant wall of numbers. I can see that exhausted and insomnia have a high correlation, but staring at a 10x10 matrix is the fastest way to get a migraine. I need R to do the heavy lifting of grouping these for me.
The "Too Many Factors" Trap
I'll use the built-in factanal() function. The tricky part here is telling R how many factors to look for. I'll start by guessing. Maybe there are five factors? Let's see what happens.
# Attempting to extract 5 factors
fit_5 <- factanal(burnout_survey, factors = 5)
R just threw an error: "too few variables in each factor". Right, I forgot a fundamental rule of factor analysis: you can't extract almost as many factors as you have variables. You'd be leaving no room for the "common variance" that defines a factor in the first place. I need to be more conservative. I'll try two factors, since I suspect "Burnout" and "Support" are the main drivers.
fit_2 <- factanal(burnout_survey, factors = 2)
print(fit_2, cutoff = 0.3)
I added cutoff = 0.3 because factanal prints every single loading by default, and most of them are near zero. By cutting off the noise, the "loadings" (the correlation between the variable and the factor) become much clearer.
Making the results actually readable
Looking at the output, I see exhausted and insomnia loading heavily on Factor 1. But supported and valued are split awkwardly across both. This is a common issue. The initial mathematical extraction is purely about maximizing variance, not about making sense to a human.
This is where "rotation" comes in. I'll use a varimax rotation, which is the default in factanal(), but I want to make sure I understand why it's happening. Rotation basically pivots the axes of our factors to align more cleanly with the variables. It doesn't change the underlying data; it just changes the perspective to make the clusters obvious.
# Let's try again, explicitly ensuring we use rotation (though it's default)
fit_rotated <- factanal(burnout_survey, factors = 2, rotation = "varimax")
print(fit_rotated, cutoff = 0.4)
Now it's crystal clear. Factor 1 has high loadings for exhausted, insomnia, and irritable. Factor 2 has supported, valued, and feedback. The "noise" variables, like equipment, have low loadings on both, meaning they aren't part of these core latent themes.
Sanity checking the loadings
Before I call this "done," I need to look at the uniquenesses in the output. Uniqueness is essentially the variance that isn't explained by the factors. If a variable has a uniqueness of 0.9, it means our two factors are doing almost nothing to explain that variable. In our case, equipment has high uniqueness. That makes sense—having a bad chair (equipment) is a different problem than being emotionally burnt out or feeling unsupported by a manager.
We've successfully reduced ten variables down to two meaningful dimensions. Now we can actually use these factors to analyze the company's health without drowning in a sea of redundant survey questions.
📋 Practical Task
Analyzing the Dimensions of User Experience Survey
You have been handed a dataset representing a User Experience (UX) survey. Users rated the following features of a new app on a scale of 1-10: speed, responsiveness, load_time, visual_appeal, color_scheme, font_clarity, navigation_ease, and menu_logic.
Your goal is to use factanal() to determine if these 8 variables can be reduced to 2 latent factors (likely "Performance" and "Design").
# Provided Dataset
ux_data <- data.frame(
speed = c(8, 7, 9, 4, 5, 8, 9, 3),
responsiveness = c(7, 8, 8, 5, 4, 7, 8, 4),
load_time = c(9, 8, 9, 3, 4, 8, 9, 2),
visual_appeal = c(4, 5, 3, 8, 9, 4, 3, 9),
color_scheme = c(5, 4, 4, 7, 8, 5, 4, 8),
font_clarity = c(3, 4, 2, 9, 8, 3, 2, 9),
navigation_ease = c(7, 6, 8, 5, 6, 7, 8, 4),
menu_logic = c(8, 7, 7, 4, 5, 8, 7, 3)
)
Your Task:
- Run a factor analysis on
ux_dataextracting 2 factors. - Print the results using a
cutoffof 0.5 to hide weak loadings. - Identify which variables load onto Factor 1 and which load onto Factor 2.
- Find the variable with the highest
uniquenessand explain why that variable is the "odd one out" in this latent structure.
There are no comments for now.