forcats fct_inorder() in R: Order Levels by Appearance
The forcats fct_inorder() function reorders the levels of a factor so they follow the order values first appear in your data, not the alphabetical default.
fct_inorder(x) # levels follow first appearance fct_inorder(x, ordered = TRUE) # also make it an ordered factor fct_infreq(x) # order by frequency instead fct_inseq(factor(x)) # order by numeric level value fct_rev(fct_inorder(x)) # appearance order, reversed aes(fct_inorder(stage), users) # lock a ggplot axis to data order mutate(df, col = fct_inorder(col)) # apply inside a dplyr pipeline
Need explanation? Read on for examples and pitfalls.
What fct_inorder() does in one sentence
fct_inorder() reorders a factor's levels to match the order each value first appears in the data. It comes from the forcats package, part of the tidyverse. Where base R factor() sorts levels alphabetically, fct_inorder() keeps them in the sequence your data presents them, which matters whenever level order drives a plot or a table.
Syntax
The function takes a factor and an optional ordered flag. The full signature is short:
The arguments are:
f: a factor, or any vector that can be coerced to a factor (character, numeric, or logical). fct_inorder() converts it for you.ordered: a logical controlling whether the result is an ordered factor.NA(the default) keeps the input's existing ordered status.TRUEforces an ordered factor;FALSEforces an unordered one.
The function never changes the values or the number of rows. It only rewrites the levels attribute so the level vector is sorted by first appearance. Every observation keeps its original label, and the factor still has exactly the same categories.
fct_inorder() examples
Example 1 shows the core behavior against the base R default. Load forcats and compare a plain factor with an fct_inorder() factor.
base R sorts the levels alphabetically. fct_inorder() instead uses the order each value is first seen: Mar, then Jan, then Feb.
Example 2 is the most common real use case: fixing bar order in ggplot2. Bars and axis ticks follow factor level order, so an unordered factor often plots in the wrong sequence.
Without fct_inorder() the x-axis would read Paid, Signup, Trial, Visit, breaking the funnel story.
Example 3 creates an ordered factor in one step. Pass ordered = TRUE when the categories have a true ranking.
Example 4 applies fct_inorder() inside a dplyr pipeline. Use mutate() to convert a column in place so the rest of your workflow sees the corrected order.
fct_inorder() vs other forcats ordering functions
fct_inorder() is one of several forcats functions that reorder levels. Pick by what should drive the order.
| Function | Orders levels by | Use when |
|---|---|---|
| fct_inorder() | First appearance in the data | Rows already sit in a meaningful order |
| fct_infreq() | Frequency, most common first | Ranking categories by their count |
| fct_inseq() | Numeric value of the levels | Levels are numbers stored as a factor |
| fct_reorder() | A summary of another variable | Sorting bars by a measured value |
| fct_relevel() | A manual order you specify | You know the exact order you want |
The decision rule: if the row order itself carries the meaning, use fct_inorder(). If an external variable or a count should set the order, reach for fct_reorder() or fct_infreq() instead.
Common pitfalls
fct_inorder() does not sort numbers numerically. It treats every input as categorical and orders by appearance, so numeric values can end up out of sequence.
For a numeric sort, use fct_inseq(factor(v)), which returns 2, 10, 33.
fct_inorder() reflects the current row order. If you arrange() a data frame first, the levels follow the sorted rows, not the original file order. Call fct_inorder() before any sorting step if the raw order is what you want to preserve.
arrange(); fct_inorder() is purely about how the factor's categories are listed.Try it yourself
Try it: Given the vector c("North", "South", "East", "South", "West"), build a factor whose levels follow the order each region first appears. Save it to ex_regions.
Click to reveal solution
Explanation: fct_inorder() walks the vector once and records each new value as it appears. North is first, South second, East third, and West last, so the levels follow that exact sequence.
Related forcats functions
These forcats functions pair naturally with fct_inorder() for level management.
- fct_infreq(): order levels by how often each appears.
- fct_reorder(): order levels by a summary of another variable.
- fct_relevel(): move specific levels to a manual position.
- fct_rev(): reverse the current level order.
- Categorical Data in R: the full guide to factors.
See the forcats reference for the official documentation.
FAQ
What is the difference between fct_inorder() and fct_infreq()?
Both reorder factor levels, but they use different signals. fct_inorder() sorts levels by the order values first appear as you read down the data. fct_infreq() sorts levels by frequency, placing the most common category first. Use fct_inorder() when the row order is meaningful, such as a time sequence or a process funnel. Use fct_infreq() when you want a ranking by count, which is common for ordering bars in a frequency chart.
Does fct_inorder() change my data or just the levels?
It changes only the levels attribute of the factor. Every observation keeps its original value, and the number of rows is unchanged. fct_inorder() never sorts or drops rows. If you need the rows themselves in a new order, use dplyr::arrange() instead. Think of fct_inorder() as relabeling the legend, not rearranging the data.
Why is fct_inorder() useful for ggplot2?
ggplot2 draws categories in factor level order. With a plain character vector or an alphabetically sorted factor, bars and axis labels can appear in a sequence that hides the story. Wrapping the variable in fct_inorder() makes the chart follow the order rows appear in your data frame, so funnels, stages, and timelines plot correctly.
How do I reverse the order produced by fct_inorder()?
Wrap the result in fct_rev(). The call fct_rev(fct_inorder(x)) first sets levels by appearance, then reverses them. This is handy for horizontal bar charts, where ggplot2 plots the first level at the bottom and you often want the leading category on top.