factor()
factor(x = character(), levels, labels = levels, exclude = NA, ordered = FALSE, nmax = NA) Factors are R’s primary data structure for representing categorical data. They store values as integers internally, with a mapping to human-readable level names, making them more memory-efficient than character vectors for repeated categories.
Syntax
factor(x = character(), levels, labels = levels, exclude = NA, ordered = FALSE, nmax = NA)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
x | character/numeric | , | Input vector to convert to a factor |
levels | character | unique(x) | Vector of unique values that will form the factor levels |
labels | character | levels | Labels for the levels (must be same length as levels) |
exclude | character | NA | Values to exclude from being valid levels |
ordered | logical | FALSE | If TRUE, creates an ordered factor for ordinal data |
nmax | numeric | NA | Upper bound on number of levels |
Examples
Creating a basic factor
# Create a factor from a character vector
colors <- c("red", "blue", "red", "green", "blue", "red")
color_factor <- factor(colors)
color_factor
# [1] red blue red green blue red
# Levels: blue green red
# Check the internal representation
as.integer(color_factor)
# [1] 3 1 3 2 1 3
By default, factor() derives levels from the unique values in the input, sorted alphabetically. You can supply a custom levels argument to enforce a specific order or include categories that might not appear in the current data. This matters for survey responses or experimental conditions where you know the full set of possible categories in advance and want empty levels represented in summaries and plots.
Specifying levels explicitly
# Ensure all expected levels appear, even if not in data
survey <- c("Yes", "No", "Yes", "No")
response <- factor(survey, levels = c("Yes", "No", "Maybe"))
response
# [1] Yes No Yes No
# Levels: Yes No Maybe
# Note: "Maybe" appears in levels but not in data
levels(response)
# [1] "Yes" "No" "Maybe"
Nominal factors treat all levels as unordered categories — correct for color names or country codes. For ordinal data where the order carries meaning, such as survey scales, education levels, or severity ratings, set ordered = TRUE. Ordered factors enable comparison operators (<, >) and are handled differently by some modeling functions that can exploit the known ordering in their parameterization.
Ordered factors for ordinal data
# Create an ordered factor for rating scale
ratings <- c("Low", "High", "Medium", "Low", "High")
ordered_ratings <- factor(ratings, levels = c("Low", "Medium", "High"), ordered = TRUE)
ordered_ratings
# [1] Low High Medium Low High
# Levels: Low < Medium < High
# Ordered factors can be compared
ordered_ratings[1] < ordered_ratings[2]
# [1] TRUE
The labels parameter decouples underlying data codes from their display names. This is useful when your data uses numeric codes or abbreviated strings and you want readable labels in plots and tables. The mapping must be one-to-one: length(labels) must equal length(levels), or R issues an error at construction time rather than silently misaligning the mapping.
Using labels instead of levels
# Rename levels with labels parameter
gender <- c(1, 2, 1, 1, 2)
gender_factor <- factor(gender, levels = c(1, 2), labels = c("Male", "Female"))
gender_factor
# [1] Male Female Male Male Female
# Levels: Male Female
Once you have a factor, the reference level — the first level in the ordering — controls how it behaves in statistical models and plots. relevel() shifts a chosen level to the front without dropping any data, preserving all observations while changing which category is the baseline for contrasts and comparisons in regression output.
Common patterns
Releveling factors
# Change the reference level
education <- c("Bachelor", "Master", "Bachelor", "PhD", "Master")
edu_factor <- factor(education)
# By default, alphabetical order determines reference
relevel(edu_factor, ref = "Master")
# [1] Bachelor Master Bachelor PhD Master
# Levels: Master Bachelor PhD
Checking how many levels a factor has and what they are is part of routine data inspection. nlevels() returns a single integer count, while levels() returns the character vector of level names. These functions are lightweight and safe — they do not modify the factor — so calling them in exploratory code has no side effects and costs negligible time even on large data.
Counting levels
# Get number of levels
colors <- c("red", "blue", "red", "green", "blue", "red")
color_factor <- factor(colors)
nlevels(color_factor)
# [1] 3
levels(color_factor)
# [1] "blue" "green" "red"
Subsetting a factor does not automatically remove levels that no longer appear in the data. If you filter rows and then call levels(), you may see categories with zero observations. This causes empty bars in plots and redundant factor levels in model summaries. droplevels() cleans up by removing any level with zero observations from the factor’s metadata, keeping the output tidy.
Dropping unused levels
# Subsetting may leave unused levels
colors <- c("red", "blue", "red", "green", "blue", "red")
color_factor <- factor(colors)
# Subset to only red values
red_only <- color_factor[color_factor == "red"]
# Unused levels still appear
levels(red_only)
# [1] "blue" "green" "red"
# Drop unused levels
red_only <- droplevels(red_only)
levels(red_only)
# [1] "red"
Factors integrate with data frames naturally. When you include a factor column during data.frame() construction, the column type is preserved. Inspecting the data frame with str() shows each factor column’s class and level count, confirming that R has recognized the categorical nature of the data rather than treating it as plain text. This integration is what makes factors useful end-to-end in analysis pipelines.
Factors in data frames
# Creating a data frame with factors
df <- data.frame(
name = c("Alice", "Bob", "Charlie"),
department = factor(c("Sales", "Engineering", "Sales"))
)
# Check structure
str(df)
# 'data.frame': 3 obs. of 2 variables:
# $ name : chr "Alice" "Bob" "Charlie"
# $ department: Factor w/ 2 levels "Sales","Engineering": 1 2 1
When to use factors
Use factors when a character variable represents a fixed set of categories and category order matters, for example, ordinal scales like "low", "medium", "high", or treatment groups in an experiment. Statistical models in R (lm(), glm(), etc.) treat factors differently from plain character vectors: they automatically create dummy variables for each level. If you pass a character column to a model, R converts it to a factor internally, making the conversion explicit in your data avoids surprises about which level becomes the reference category.
Use factors when you need a controlled level order for plots. ggplot2 plots character vectors in alphabetical order by default; converting to a factor with factor(x, levels = c(...)) lets you specify the display order directly.
Avoid factors for free-text columns, IDs, or any string where the set of values is not bounded. Manipulating factors with unexpected values (values not in the levels list) produces NA silently, which is a common source of data loss bugs. For general string operations, plain character vectors are safer.
Factors store categorical data as integers with a levels attribute. The integer codes are stored in memory while the character labels are stored once in levels. This makes factors memory-efficient for repeated categories. However, factors cannot store values not in their levels set, assigning a new value produces NA. Use forcats::fct_expand() to add new levels before assigning. Converting a numeric factor back to numbers requires as.numeric(as.character(f)), not as.numeric(f), which returns the integer codes.
Factor behavior in statistical functions
Statistical functions in R treat factor columns as categorical variables automatically. lm() encodes factors as dummy variables without explicit coding. table() counts occurrences of each level. tapply() splits by factor levels. This automatic handling is one of the main reasons to use factors rather than character vectors for categorical data that will be analyzed statistically.
The reference level, the level that does not get a dummy variable in regression, is the first level alphabetically by default. This affects coefficient interpretation: the coefficient for each other level is the difference from the reference level. Changing the reference level with relevel() changes which comparison is made explicit in the model output. Choosing a reference level that makes substantive sense, the control condition, the baseline category — produces more directly interpretable coefficients.
See also
- tibble::tibble()
- table()
- data.frame()
forcats::fct_relevel()reorders levels in a factor without dropping any, which is cleaner than reassigning withfactor(x, levels = new_order).