data.table setcolorder() in R: Reorder Columns by Reference
data.table setcolorder() reorders the columns of a data.table by reference, changing their position in place without copying the whole table. You pass a column order, and data.table rearranges the pointers, so it stays fast even on millions of rows.
setcolorder(DT, c("b","a","c")) # full explicit order
setcolorder(DT, "c") # move one column to front
setcolorder(DT, c(3, 1, 2)) # reorder by position
setcolorder(DT, "id", before = "name") # place before a column
setcolorder(DT, "ts", after = "id") # place after a column
setcolorder(DT, rev(names(DT))) # reverse every columnNeed explanation? Read on for examples and pitfalls.
What setcolorder() does
setcolorder() changes column position without moving data. It is one of data.table's set* functions, meaning it edits the table by reference. Instead of building a new data.table with columns in a different order, it rewrites the internal column list in place. No copy is made, and the call returns invisibly.
That reference behavior is the whole point. Reordering columns by selecting them, like DT[, .(c, a, b)], copies every value into a fresh table. On a wide or long dataset that copy is wasteful when all you want is a cosmetic change to column order. setcolorder() skips the copy entirely.
setcolorder() shuffles those pointers, so the cost is the same whether the table has 100 rows or 100 million. The data itself never moves.Syntax and arguments
The signature is compact but flexible. setcolorder() takes the table plus a description of the order you want.
Each argument controls a different part of the reorder:
x: the data.table (or data.frame) to modify in place.neworder: a character vector of column names, or an integer vector of positions, in the order you want. It may list only some columns.before: place thenewordercolumns immediately before this column.after: place thenewordercolumns immediately after this column.
If neworder lists only a subset of columns, the columns you left out keep their current relative order and are appended after the ones you named. You never lose a column by omitting it.
before and after arguments require data.table 1.15.0 or newer. On older versions, pass a full or partial neworder vector instead. Check your version with packageVersion("data.table").Examples by use case
Start by building a small data.table to reorder. Every example below runs against this same table.
Pass a full vector of names to set the exact order. This is the most explicit form: every column appears once, in the position you want.
Pass a single name to move one column to the front. Because neworder can be a subset, naming just "score" pulls it first and leaves the rest in place.
Use integer positions when names are long or generated. The vector c(4, 1, 2, 3) means "the 4th column first, then the 1st, 2nd, and 3rd".
Use before or after to place columns relative to another. This avoids spelling out the entire order when you only want to nudge one column.
setcolorder() vs other column-ordering approaches
setcolorder() wins whenever you only need a different order. The alternatives all build a copy, which matters on large data.
| Approach | Copies data? | Speed | Best when |
|---|---|---|---|
setcolorder(DT, ...) |
No | Fastest | Reordering a data.table by reference |
DT[, .(b, a, c)] |
Yes | Slower | You also subset or transform columns |
df[c("b", "a")] |
Yes | Slow | A plain data.frame, small data |
dplyr::relocate() |
Yes | Moderate | A tidyverse pipeline that returns a tibble |
The decision rule is simple. If the only change is column order and the object is a data.table, use setcolorder(). If you are already subsetting columns or chaining verbs, fold the reorder into that step instead.
setcolorder() also works on a plain data.frame. It still reorders by reference, so you get the same zero-copy benefit even without converting to a data.table first.Common pitfalls
The by-reference behavior surprises most newcomers. A second variable pointing at the same data.table is not a copy. Reordering through one name reorders the other.
To reorder an independent version, call copy() first, then setcolorder() on the copy.
A misspelled column name throws an error. setcolorder() validates neworder against the actual column names and stops if a name is missing.
Do not rely on the return value. setcolorder() returns the table invisibly, so writing DT <- setcolorder(DT, ...) works but is redundant and misleading. Call it for its side effect and use DT directly afterward.
Try it yourself
Try it: Reorder the columns of ex_dt so that grade comes first and the rest keep their order. Save nothing new, just modify ex_dt in place.
Click to reveal solution
Explanation: Passing the single name "grade" as neworder moves that column to the front. The columns you did not name, id and name, keep their original relative order and follow behind.
Related data.table functions
setcolorder() sits in a family of by-reference setters. Each one edits a different aspect of the table in place.
setnames(): rename columns without copying.setorder(): sort rows by one or more columns.setkey(): set a sort key for fast joins and lookups.setDT(): convert a data.frame to a data.table in place.set(): assign values into cells by reference.
FAQ
Does setcolorder() modify the original data.table?
Yes. setcolorder() reorders columns by reference, so the original object changes and no new table is returned for assignment. Any other variable bound to the same data.table sees the new order too. If you need to keep the original layout, make an explicit copy() of the table first and call setcolorder() on that copy instead.
Can I use setcolorder() on a data.frame?
Yes. setcolorder() accepts a plain data.frame and reorders its columns by reference, just as it does for a data.table. This is one of the few data.table functions that helps even if you have not converted your object. You do not need setDT() first when the only change you want is column order.
What happens if neworder lists only some columns?
The columns you name are placed in the order given, and every column you left out is appended afterward in its original relative order. Nothing is dropped. This makes it easy to move one or two columns to the front without typing the full column list.
Is setcolorder() faster than reordering with column selection?
Yes, especially on large data. Selecting columns with DT[, .(c, a, b)] copies every value into a new table, while setcolorder() only rearranges internal pointers. The cost of setcolorder() does not grow with the number of rows, so it is the right choice whenever reordering is the only change.
For the full argument reference, see the official data.table setcolorder documentation.