Written and maintained by CASRAI Editorial Board
Last updated
To order by in R, use order() from base R inside the row index of a data frame: df[order(df$score), ]. order() returns the row positions that would sort the data, so you apply it to the rows yourself. The tidyverse offers arrange() in dplyr, and data.table offers setorder(). They look interchangeable, but they treat missing values and character columns differently, and that difference can change the order of your results.
What ordering is used for in research
Researchers order data frames to rank results (the largest effect sizes first), to inspect extremes and possible data-entry errors, to put time-stamped records in sequence before computing change scores, and to make an exported table read in a stable, documented order. Because the order of rows can affect later steps such as first-observation selection or de-duplication, state how you sorted in your analysis script. For layout of the data itself, see tidy data rules for structuring research data.
Install or access
order() is part of base R, so it needs no installation. Base R is distributed by the R Project through CRAN. The other two tools are add-on packages, installed once and loaded in each session:
install.packages("dplyr") # provides arrange() and desc()
install.packages("data.table") # provides setorder()
library(dplyr)
library(data.table)
If you are new to the environment, how to use RStudio covers the editor, and data frames in R covers the object being sorted.
Order by one or more columns
The three approaches below sort the built-in mtcars data by number of cylinders, then by displacement within each cylinder count.
- Base R. Pass the sort keys to
order()and use the result as the row index (the comma after it matters, because it leaves the columns untouched):mtcars[order(mtcars$cyl, mtcars$disp), ] - dplyr. Name the columns directly, with no
$:arrange(mtcars, cyl, disp) - data.table.
setorder()reorders the table by reference, meaning in place, without making a copy:dt <- as.data.table(mtcars) setorder(dt, cyl, disp)
When the first key has tied values, the second key breaks the tie, then the third, and so on, as documented for order(). If ties remain after all keys, the original row order is kept, because the sort is stable (every method except "quick").
Order in R from largest to smallest (descending)
Each tool has its own switch for descending order:
order(x, decreasing = TRUE)reverses a single key. For numbers,order(-x)does the same, but the minus sign works only on numeric keys.arrange(df, desc(x))uses dplyr’sdesc().setorderv(dt, "x", order = -1)sets the direction through theorderargument, which takes only1(ascending) or-1(descending).
Ascending on one column, descending on another
The R manual’s own example for mixed directions is dd[order(x, -y, z), ]: ascending on x, descending on y, ascending on z. The same idea in dplyr is arrange(df, x, desc(y), z). A character column cannot take a minus sign. If you need a single decreasing vector with one value per key, the manual states that only method = "radix" accepts a vector, for example order(df$g, df$x, decreasing = c(FALSE, TRUE), method = "radix").
How missing values are sorted
The three tools do not put NA in the same place by default. This is the detail most tutorials leave out, and it matters when a column has gaps. For ways to deal with the gaps themselves, see omitting rows with NA in R.
| Tool | Default position of NA | How to change it |
|---|---|---|
order() (base R) |
Last (na.last = TRUE) |
na.last = FALSE puts them first; na.last = NA removes them |
arrange() (dplyr) |
Always at the end for local data, even inside desc() |
No argument; they stay at the end |
setorder() (data.table) |
First (na.last = FALSE) |
na.last = TRUE (only TRUE or FALSE are accepted) |
The practical consequence: moving the same script from setorder() to arrange() moves the missing rows from the top of the table to the bottom, even though the sort keys are identical.
Why character columns can sort differently
For character vectors, the R manual says that except for method = "radix", the sort order depends on the collating sequence of the locale in use. In other words, base order() on text can give different results on two computers with different language settings. The radix method is not locale-dependent.
dplyr and data.table take a different route. arrange() uses the “C” locale by default (its .locale argument is NULL), and its documentation notes that the C locale is not the same as English locales such as “en”. data.table states that it always reorders in the C locale, which puts uppercase letters before lowercase ones. To get session-locale ordering with data.table, subset with base order(): dt[base::order(name)]. With dplyr, set .locale, for example arrange(df, name, .locale = "en").
For a reproducible analysis, this means a sort on names, site codes or sample IDs that mix upper and lower case is worth pinning down explicitly, rather than relying on whichever tool you used first.
Order by group
On a grouped data frame, arrange() ignores the grouping unless you ask for it. dplyr’s reference shows both behaviours on mtcars grouped by cyl:
by_cyl <- mtcars |> group_by(cyl)
by_cyl |> arrange(desc(wt)) # ignores groups
by_cyl |> arrange(desc(wt), .by_group = TRUE) # sorts by group first
With .by_group = TRUE, rows are sorted by the grouping variable first and then by the keys you give. The pipe operator used here is explained in the pipe in R. To sort on several columns that share a name pattern, dplyr’s documentation shows arrange(across(starts_with("Sepal"), desc)) on the iris data.
order() vs sort() vs arrange()
The manual page for order() points to sort, sort.list, rank and xtfrm as related functions. In everyday use the difference is what each returns:
| Function | What it returns | Works on |
|---|---|---|
order(x) |
The positions that would sort x (an index vector) |
One or more vectors, so it can drive a data frame sort |
sort(x) |
The sorted values themselves | A single vector |
arrange(df, x) |
A data frame with its rows reordered | A data frame (dplyr) |
setorder(dt, x) |
The same table, reordered in place | A data.table |
Output and how to read it
Called on its own, order(x) prints integers, not values. If the first number is 4, then the fourth element of x is the smallest. Using that vector as df[order(df$x), ] then returns the rows in that sequence. Row names (and the row numbers shown at the left) keep their original labels after sorting, so a sorted table can show row names out of numeric sequence; this is expected. Check the first and last few rows with head() and tail(), and see summary statistics in R to confirm the range of the sorted column.
Troubleshooting
- The data frame did not change.
order()andarrange()return a new object. Assign the result, for exampledf <- df[order(df$x), ]. Onlysetorder()modifies its input in place. - You get columns back instead of rows. The comma is missing. Use
df[order(df$x), ], with the comma after the order call. - Sorting a whole data frame with
do.call(order, df). The manual showsdd[do.call(order, dd), ]to sort on all columns, and warns to remove the column names first (unname), because column names can clash with argument names such asdecreasing. - Uppercase sorted before lowercase (or the reverse). You are seeing the locale difference described above.
- Plotting a line out of order. A line chart joins points in row order, so sort on the x variable first. Plotting a line in R uses
order()for this.
Free alternatives (table)
All three tools are free and open source. They suit different habits:
| Approach | Package | Direction syntax | Modifies in place |
|---|---|---|---|
df[order(...), ] |
base R | decreasing = TRUE or a minus sign |
No |
arrange() |
dplyr | desc() |
No |
setorder() |
data.table | order = -1 in setorderv() |
Yes, by reference |
FAQ
What does order() do in R?
It returns the positions that would put its arguments into ascending order (descending with decreasing = TRUE). Used as a row index, it sorts a data frame.
How do I order a data frame by a column in R?
Use df[order(df$col), ] in base R, or arrange(df, col) with dplyr.
How do I order by several columns?
List them in priority order: order(df$a, df$b) or arrange(df, a, b). Later columns only break ties in earlier ones.
Where do NA values go?
Last in order() and arrange() by default, first in setorder().
Sources (official docs)
- R Documentation, order: Ordering Permutation (base R manual, R-devel), checked 2026-10-29.
- tidyverse, dplyr reference: arrange(), checked 2026-10-29.
- data.table, reference: setorder, checked 2026-10-29.







