Skip to Content
Course content

121: Factor Analysis in R

Click on the "Edit" button in the top corner of the screen to edit your slide content.

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_data extracting 2 factors.
  • Print the results using a cutoff of 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 uniqueness and explain why that variable is the "odd one out" in this latent structure.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.