rguides

dplyr::relocate

relocate(.data, ..., .before = NULL, .after = NULL)

relocate() changes the positions of columns in a data frame without changing their values. It uses tidy-select syntax, which means you can move columns by name, by type, or using any of the familiar helper functions like starts_with() or where(). The difference from select() is that relocate() doesn’t drop columns, it only changes their order.

Signature

relocate(.data, ..., .before = NULL, .after = NULL)

Parameters

.data

A data frame, tibble, or lazy data frame from dbplyr or dtplyr.

, one or more columns to move. You can name them directly, use tidyselect helpers, or rename on the fly with new_name = old_name.

.before

Move the selected columns immediately before this column (or columns). Accepts a tidy-select expression.

.after

Move the selected columns immediately after this column (or columns). Accepts a tidy-select expression.

Return value

A tibble with the same rows, columns, and attributes as the input, with only the column order changed. Groups are preserved.

Basic usage

When you pass a column name to relocate() with no positional arguments, the column moves to the front (leftmost position) of the data frame. All other columns shift right to make room while preserving their relative order:

library(dplyr)

df <- tibble(a = 1, b = 1, c = 1, d = "a", e = "a", f = "a")

# Move column f to the leftmost position
df |> relocate(f)
#> # A tibble: 1 × 6
#>   f     a     b     c     d     e
#>   <chr> <dbl> <dbl> <dbl> <chr> <chr>
#> 1 a     1     1     1     a     a

When you supply neither .before nor .after, the selected columns move to the leftmost position. This default is convenient when you want a key column to appear first for readability in printed output.

Moving relative to another column

The .after argument places the relocated columns immediately to the right of the target column. All columns that were originally after the target shift right to accommodate the moved columns. In the example below, column a is placed right after c:

df |> relocate(a, .after = c)
#> # A tibble: 1 × 6
#>   b     c     a     d     e     f
#>   <dbl> <dbl> <dbl> <chr> <chr> <chr>
#> 1 1     1     1     a     a     a

The .before argument works the same way but places the moved columns to the left of the target. Here, column f is inserted immediately before b, so f and b become adjacent while the columns originally between them shift right:

df |> relocate(f, .before = b)
#> # A tibble: 1 × 6
#>   a     f     b     c     d     e
#>   <dbl> <chr> <dbl> <dbl> <chr> <chr>
#> 1 1     a     1     1     a     a

You can only use one of .before or .after at a time — supplying both produces an error, since the intent would be ambiguous.

Using last_col()

The last_col() helper is the tidy-select way to reference the final column position, making it possible to move columns all the way to the right without knowing how many columns the data frame has. This is especially useful in programmatic code where the column count varies:

df |> relocate(a, .after = last_col())
#> # A tibble: 1 × 6
#>   b     c     d     e     f     a
#>   <dbl> <dbl> <chr> <chr> <chr> <dbl>
#> 1 1     1     a     a     a     1

Renaming during a move

relocate() borrows the new_name = old_name syntax from rename(), so you can both move a column and give it a new name in one step. The column appears at the new position with the new name, and the old name is removed from the column set:

df |> relocate(ff = f)
#> # A tibble: 1 × 6
#>   ff    a     b     c     d     e
#>   <chr> <dbl> <dbl> <dbl> <chr> <chr>
#> 1 a     1     1     1     a     a

Moving by type

Because relocate() uses tidy-select semantics, you can move columns based on their type using helpers like where(). Moving all character columns to the front groups text data together, while moving numeric columns to the end keeps the layout organized for statistical review:

# Move all character columns to the front
df |> relocate(where(is.character))
#> # A tibble: 1 × 6
#>   d     e     f     a     b     c
#>   <chr> <chr> <chr> <dbl> <dbl> <dbl>
#> 1 a     a     a     1     1     1

# Move all numeric columns to the end
df |> relocate(where(is.numeric), .after = last_col())
#> # A tibble: 1 × 6
#>   d     e     f     a     b     c
#>   <chr> <chr> <chr> <dbl> <dbl> <dbl>
#> 1 a     a     a     1     1     1

Moving multiple columns to a multiple-Column destination

When .before or .after resolves to multiple columns — for example, where(is.character) matching three columns — the moved columns are slotted in adjacent to all of them. Effectively, the moved columns become contiguous with the set of destination columns, which is a powerful pattern for grouping related columns together:

df2 <- tibble(a = 1, b = "a", c = 1, d = "a")

# Move numeric columns after all character columns
df2 |> relocate(where(is.numeric), .after = where(is.character))
#> # A tibble: 1 × 4
#>   b     d     a     c
#>   <chr> <chr> <dbl> <dbl>
#> 1 a     a     1     1

# Move character columns before all numeric columns
df2 |> relocate(where(is.character), .before = where(is.numeric))
#> # A tibble: 1 × 4
#>   a     c     b     d
#>   <dbl> <dbl> <chr> <chr>
#> 1 1     1     a     a

Using any_of() and other helpers

The any_of() helper silently ignores columns that don’t exist in the data, which makes it safe to use when you are not certain which columns will be present. Combined with other tidy-select helpers, relocate() becomes a concise way to normalize the column layout of data frames that arrive with unpredictable column sets:

df |> relocate(any_of(c("a", "e", "i", "o", "u")))
#> # A tibble: 1 × 6
#>   a     e     b     c     d     f
#>   <dbl> <chr> <dbl> <dbl> <chr> <chr>
#> 1 1     a     1     1     a     a

any_of() is useful when you’re not sure whether all the columns exist, it silently ignores missing ones.

relocate() vs select()

Both can reorder columns, but they differ in intent:

relocate()select()
Drops other columnsNoYes (unless you include everything())
Rename during reorderYesYes
Move by typeYes (via tidy-select)Yes
Best forMinor reorderingSelecting a subset + reordering

In practice, relocate() is cleaner when you want to adjust column order without thinking about every other column. select() is better when you’re explicitly choosing which columns to keep.

See also