100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Factors in R

Learn how R represents categorical data with factors — encoding fixed levels efficiently and controlling ordinal comparisons.

Data StructuresIntermediate8 min readJul 10, 2026
Analogies

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.

r
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 30

Ordered 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

Was this page helpful?

Topics covered

#Programming#RProgrammingStudyNotes#FactorsInR#Factors#Factor#Creating#Setting#StudyNotes#SkillVeris#ExamPrep