forcats fct_unique() in R: List a Factor's Unique Levels
The forcats fct_unique() function returns each level of an R factor exactly once, in level order, giving you a clean factor of every value the variable can take.
fct_unique(f) # every level once, in level order fct_unique(factor(x)) # coerce a vector first fct_unique(ordered_f) # keeps the ordered factor class unique(f) # base R: observed values, appearance order levels(f) # level names as a character vector as.character(fct_unique(f)) # level names as plain text fct_unique(fct_drop(f)) # observed levels only
Need explanation? Read on for examples and pitfalls.
What fct_unique() does in one sentence
fct_unique() returns a factor containing each level of its input exactly once, ordered by the level definition. It comes from the forcats package, part of the tidyverse. For a factor, the set of possible unique values is the set of levels, so fct_unique() effectively hands back the levels as a factor object rather than as the plain character vector that levels() produces.
Syntax
The function takes a single argument: the factor to summarise. The signature could not be shorter:
The only argument is:
f: a factor, or any vector that fct_unique() can coerce to a factor. Character, numeric, and logical vectors are converted for you, with levels created in sorted order.
The result is always a factor. It has one element per level, listed in the order the levels are stored, and it keeps the ordered status of the input. If the input contains any NA values, a trailing NA element is appended to the result.
fct_unique() examples
Example 1 returns the levels of a factor, in level order. Load forcats and call fct_unique() on a small factor.
The five observations collapse to one entry per level. The order is S, M, L because that is the order passed to factor(), not the order the values first appear in the data.
Example 2 contrasts fct_unique() with base R unique(). The two functions answer different questions.
unique() returns the distinct values in the order they appear: M is first in the data, then S, then L. fct_unique() ignores appearance order and follows the level order instead.
Example 3 shows that fct_unique() keeps unused levels. A factor can carry levels with zero observations.
Levels B and D have no rows, yet fct_unique() still lists them. unique(grades) would return only A and C. This makes fct_unique() the right tool when you need the full set of allowed categories, such as building a complete axis for a chart.
Example 4 confirms that ordered factors keep their class. fct_unique() preserves the ordered attribute.
The < signs in the printed levels confirm the result is still an ordered factor. The unused level Med appears in its correct ranked position between Low and High.
fct_unique() vs other ways to list values
Several functions report the distinct values of a variable, and each returns a different shape. Pick by the output you need.
| Function | Returns | Use when |
|---|---|---|
| fct_unique() | A factor, one element per level | You want the levels as a factor, in order |
| unique() | Observed values, appearance order | You want only values present in the data |
| levels() | A character vector of level names | You need plain text, not a factor |
| fct_count() | A tibble of levels and counts | You also want how many rows each has |
| fct_drop() | A factor with unused levels removed | You want to discard empty levels |
The decision rule: reach for fct_unique() when you want every defined level as a factor object. Use unique() when only observed values matter, and levels() when you need a character vector to loop over or paste into labels.
.categories attribute of a pd.Categorical, which lists every category whether or not it occurs. pandas Series.unique() matches base R unique() instead, reporting only observed values.Common pitfalls
fct_unique() includes levels that never appear in the data. If you expect only observed values, this surprises you.
Call fct_drop() before fct_unique() to discard empty levels, or use unique() if appearance order is acceptable.
NA is a value, not a level, so levels() of the result is unchanged.fct_unique() returns a factor, not a character vector. Passing it where text is expected can cause subtle bugs.
Wrap the call in as.character(), or use levels() directly, when you need plain strings for labels or joins.
Try it yourself
Try it: Return every level of factor(c("red", "blue", "red"), levels = c("red", "green", "blue")) as a factor in level order. Save the result to ex_colors.
Click to reveal solution
Explanation: fct_unique() returns one element per level in level order, so green appears even though no observation uses it. The order follows the levels argument, not the data.
Related forcats functions
These forcats functions pair naturally with fct_unique() when working with factors.
- fct_count(): count how many rows fall in each level.
- fct_drop(): remove levels that have no observations.
- fct_inorder(): set level order to match first appearance.
- fct_relevel(): move chosen levels to a new position.
- Categorical Data in R: the full guide to factors.
See the forcats reference for the official documentation.
FAQ
What is the difference between fct_unique() and unique()?
Both return the distinct values of a variable, but they read different things. unique() walks the data and reports values in the order it first meets them, so it returns only values that actually appear. fct_unique() reads the factor's level attribute and returns every level once, in level order, including levels with no observations. Use fct_unique() for the full set of defined categories and unique() when only observed values matter.
Does fct_unique() return unused factor levels?
Yes. fct_unique() lists every level defined on the factor, whether or not any row uses it. This is intentional: the levels describe the complete set of categories the variable can take. If you want only the levels that appear in the data, call fct_drop() first to remove empty levels, then pass the result to fct_unique(), or use base R unique() instead.
How do I get factor levels as a character vector?
fct_unique() returns a factor, not text. To get plain character strings, either call levels(f) directly, which always returns a character vector, or wrap the result with as.character(fct_unique(f)). The levels() route is simpler and faster when you just need the names. Use as.character(fct_unique(f)) only when you specifically want the NA-aware ordering that fct_unique() applies.
Does fct_unique() handle NA values?
Yes. If the factor contains missing values, fct_unique() appends a single trailing NA to its result. The NA is a value in the returned factor, not a new level, so levels() of the result is unaffected. If you do not want the trailing NA, drop missing values from the factor before calling fct_unique(), or subset the result with !is.na().