What Is a Factor?
A factor is R's structure for categorical data: internally it stores a set of integer codes plus a table of 'levels' (the unique category labels), so a column like blood type or survey response is represented compactly and R knows in advance which values are valid. Factors matter for statistical modeling because functions like lm() and table() treat factor columns specially — generating dummy variables for regression or cross-tabulating counts — rather than treating them as arbitrary text.
Cricket analogy: A factor storing match result as levels 'Win', 'Loss', 'Draw' with underlying integer codes is like an official scoresheet using a fixed code (W/L/D) rather than free-text notes, ensuring only valid outcomes are recorded.
Creating Factors and Setting Levels
factor(x) automatically infers levels from the unique values in x and sorts them alphabetically by default; you can override this with factor(x, levels = c('Low','Medium','High')) to control the stored order explicitly, and factor(x, labels = c(...)) to rename the displayed labels while keeping the underlying data mapping. table(f) then cross-tabulates counts per level, showing zero counts for levels present in the factor's level set but absent from the actual data — a behavior a plain character vector's table() would not exhibit.
Cricket analogy: factor(results, levels=c('Loss','Draw','Win')) forcing a specific level order is like a scoreboard deliberately listing outcomes worst-to-best rather than alphabetically, so 'Win' always sorts last in a chart legend.
satisfaction <- factor(
c("Medium", "High", "Low", "High"),
levels = c("Low", "Medium", "High"),
ordered = TRUE
)
satisfaction > "Low" # TRUE TRUE FALSE TRUE
table(satisfaction) # shows counts for all 3 levels
# Common pitfall
codes <- factor(c("10", "20", "30"))
as.numeric(codes) # WRONG: 1 2 3
as.numeric(as.character(codes)) # RIGHT: 10 20 30Ordered Factors and Comparisons
Passing ordered = TRUE to factor() (or using the shortcut ordered()) creates an ordered factor where the sequence of levels carries meaning, enabling comparison operators: factor(x, levels = c('Low','Medium','High'), ordered = TRUE) lets you evaluate f > 'Low' meaningfully, returning TRUE for 'Medium' and 'High'. This distinguishes ordinal categorical data (survey ratings, education level, letter grades) from purely nominal factors (colors, city names) where '>' comparisons would be meaningless even if attempted.
Cricket analogy: An ordered factor for match importance with levels 'Group Stage' < 'Quarterfinal' < 'Semifinal' < 'Final' lets you meaningfully test if one match outranks another, unlike an unordered factor of team names where '>' would be meaningless.
If you set factor(x, levels = c('Low','Medium','High')) but x actually contains a value like 'low' (different case) or a typo like 'Med', that value silently becomes NA instead of raising an error — always cross-check unique(x) against your intended levels before creating the factor.
Common Pitfalls: Converting Factors
A common and costly mistake is calling as.numeric() directly on a factor: because factors store integer codes internally, as.numeric(factor(c('10','20','30'))) returns the codes 1, 2, 3 (the rank of each level alphabetically), not the numbers 10, 20, 30 you'd expect. The correct conversion path is as.numeric(as.character(f)), which first converts the factor back to its character labels and only then parses those strings as numbers, recovering the original values.
Cricket analogy: as.numeric(factor(c('10','20','30'))) wrongly returning 1,2,3 is like reading jersey numbers off a scoreboard's internal ranking slot instead of the actual number printed on the shirt — you get the rank in the list, not the real value.
After subsetting a factor (e.g., filtering a data frame to one category), the unused levels often remain in the factor's level set even though no rows use them; droplevels(f) removes these unused levels, which matters for functions like table() or ggplot2 that would otherwise still show empty categories.
- A factor stores categorical data as integer codes plus a table of level labels.
- factor(x, levels=, labels=) controls both the stored order and the displayed names.
- table(f) on a factor shows every defined level's count, including zeros for unused levels.
- ordered = TRUE enables meaningful <, >, <= comparisons based on level order.
- as.numeric(f) returns integer codes, not original values — use as.numeric(as.character(f)) to recover them.
- Values not matching any defined level become NA silently when a factor is created.
Practice what you learned
1. What does a factor store internally?
2. What does as.numeric(factor(c('10','20','30'))) return?
3. What must be set to TRUE to enable meaningful < and > comparisons on a factor?
4. What happens if a value in x doesn't match any level specified in factor(x, levels=...)?
5. What does droplevels() do?
Was this page helpful?
You May Also Like
Data Frames in R
Understand R's core tabular data structure — how data.frame combines vectors of equal length into rows and columns for real-world datasets.
Vectors in R
Learn how R's core data structure — the atomic vector — stores, indexes, and vectorizes operations over ordered collections of same-typed values.
Lists and Matrices in R
Compare R's two other core structures — the flexible, heterogeneous list and the strict, two-dimensional matrix — and learn when to use each.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics