28

I have the following tibble:

library(tidyverse)
df <- structure(list(Sepal.Length = c(5.1, 4.9, 4.7, 4.6, 5), Sepal.Width = c(3.5, 
3, 3.2, 3.1, 3.6), Petal.Length = c(1.4, 1.4, 1.3, 1.5, 1.4)), .Names = c("Sepal.Length", 
"Sepal.Width", "Petal.Length"), row.names = c(NA, 5L), class = c("tbl_df", 
"tbl", "data.frame"))

That looks like this:

> df
# A tibble: 5 × 3
  Sepal.Length Sepal.Width Petal.Length
*        <dbl>       <dbl>        <dbl>
1          5.1         3.5          1.4
2          4.9         3.0          1.4
3          4.7         3.2          1.3
4          4.6         3.1          1.5
5          5.0         3.6          1.4

What I want to do is to replace Sepal.Length and Petal.Length with appended string to_app <- ".xxx" resulting in:

  Sepal.Length.xxx Sepal.Width Petal.Length.xxx
          5.1         3.5          1.4
          4.9         3.0          1.4
          4.7         3.2          1.3
          4.6         3.1          1.5
          5.0         3.6          1.4

I tried this with error:

df %>% rename(paste(Sepal.Length,to_app,sep="") = Petal.Length,paste(Sepal.Width,to_app,sep="") = Petal.Length)

Error: unexpected '=' in "df %>% rename(paste(Sepal.Length,to_app,sep="") ="

UseR10085
  • 7,120
  • 3
  • 24
  • 54
neversaint
  • 60,904
  • 137
  • 310
  • 477
  • 2
    Just another R construction of doing this, using `match` - `vars <- c("Sepal.Length", "Sepal.Width"); names(df)[match(vars, names(df))] <- paste0(vars, to_app)` – thelatemail Nov 17 '17 at 03:18

6 Answers6

29

You can use the rename_at() function for this (starting in dplyr_0.7.0) or rename_with() (starting in dplyr_1.0.0).

For example, you can pass the variables you want to rename as strings. In your example, the paste0 function can be used to append on the appropriate suffix to each column.

cols = c("Sepal.Length", "Petal.Length")
to_app = ".xxx"

For dplyr_1.0.0, use rename_with(). The function argument comes before the column argument. You can use the tidy-select function all_of() (or others) to choose columns.

rename_with(df, .fn = ~paste0(., to_app), .cols = all_of(cols) )

# A tibble: 5 x 3
  Sepal.Length.xxx Sepal.Width Petal.Length.xxx
*            <dbl>       <dbl>            <dbl>
1              5.1         3.5              1.4
2              4.9         3                1.4
3              4.7         3.2              1.3
4              4.6         3.1              1.5
5              5           3.6              1.4

Earlier versions of dplyr use rename_at(). This has been superseded in version 1.0.0, which means there are new functions to use as above but this particular function is not going away.

rename_at(df, cols, list( ~paste0(., to_app) ) )

# A tibble: 5 x 3
  Sepal.Length.xxx Sepal.Width Petal.Length.xxx
*            <dbl>       <dbl>            <dbl>
1              5.1         3.5              1.4
2              4.9         3.0              1.4
3              4.7         3.2              1.3
4              4.6         3.1              1.5
5              5.0         3.6              1.4

You can also use the select helper functions to choose variables for renaming, such as contains.

rename_at(df, vars( contains("Length") ), list( ~paste0(., ".xxx") ) )

# A tibble: 5 x 3
  Sepal.Length.xxx Sepal.Width Petal.Length.xxx
*            <dbl>       <dbl>            <dbl>
1              5.1         3.5              1.4
2              4.9         3.0              1.4
3              4.7         3.2              1.3
4              4.6         3.1              1.5
5              5.0         3.6              1.4

This list() coding replaces the previous funs() coding starting in dplyr_0.7.0. Previously, e.g., funs( paste0(., to_app) )

aosmith
  • 34,856
  • 9
  • 84
  • 118
  • 1
    Just updating for dplyr 0.8, rename_at(df, cols, list(~paste0(., to_app) ) ) – Luis Jun 15 '19 at 14:35
  • 2
    dplyr 1.0.0 (and using stringr::str_c), rename_with( .data = df, .fn = ~ str_c(., to_app), .cols = contains("Length") ). Other rename_x functions have been superseded. – Whalen Jul 14 '20 at 13:30
25

EDIT: these days, I'd recommend using dplyr::rename_with, as per @aosmith's answer. Write a function that takes your old column names as input and returns your new column names as output, and you're done :)

I'm a little late to the party on this, but after staring at the programming vignette for a long time, I found the relevant example in the Different input and output variable .

In my simpler use case, I just needed to rename a column to the value of a string:

> df1 = data_frame(index = 1:5, value = c(10, 20, 30, 40, 50))
> df1
# A tibble: 5 x 2
  index value
  <int> <dbl>
1     1    10
2     2    20
3     3    30
4     4    40
5     5    50

> newname = 'blau'
> newname2 = 'wheee'

> df1 %>% rename(!!newname := value, !!newname2 := index)
# A tibble: 5 x 2
  wheee  blau
  <int> <dbl>
1     1    10
2     2    20
3     3    30
4     4    40
5     5    50

So if you're happy to do that manually, you can just:

df %>%
  rename(!!paste("Sepal.Length", "xxx", sep = ".") := Sepal.Length)

If, however, you need something that automatically appends ".xxx" to whatever column name is supplied to it, I recommend you have a close look at that section. It's still a bit over my head, unfortunately, but I can see that it's doable >_>

jimjamslam
  • 1,988
  • 1
  • 18
  • 32
13

If you want to use dplyr's rename function, it would be best to create a named vector / list and call it using the .dots argument in the standard evaluation version:

cols <- c("Sepal.Length", "Petal.Length")
to_app <- ".xxx"
cols <- setNames(cols, paste0(cols, to_app))

df %>% rename_(.dots = cols)

## A tibble: 5 × 3
#  Sepal.Length.xxx Sepal.Width Petal.Length.xxx
#*            <dbl>       <dbl>            <dbl>
#1              5.1         3.5              1.4
#2              4.9         3.0              1.4
#3              4.7         3.2              1.3
#4              4.6         3.1              1.5
#5              5.0         3.6              1.4

Note however, that this approach is subject to change with the next version 0.6.0 of dplyr (see e.g. http://blog.rstudio.org/2017/04/13/dplyr-0-6-0-coming-soon/ and http://dplyr.tidyverse.org/articles/programming.html).

talat
  • 68,970
  • 21
  • 126
  • 157
4
df %>% setNames(paste0(names(.), to.app))

# A tibble: 5 × 3
  Sepal.Length.xxx Sepal.Width.xxx Petal.Length.xxx
*            <dbl>           <dbl>            <dbl>
1              5.1             3.5              1.4
2              4.9             3.0              1.4
3              4.7             3.2              1.3
4              4.6             3.1              1.5
5              5.0             3.6              1.4

EDIT:

Apologies for not reading properly. Here's a solution with data.table package.

var <- names(df)[c(1,3)]
df %>% setnames(., var, paste0(var, to.app))
df

# A tibble: 5 × 3
  Sepal.Length.xxx Sepal.Width Petal.Length.xxx
*            <dbl>       <dbl>            <dbl>
1              5.1         3.5              1.4
2              4.9         3.0              1.4
3              4.7         3.2              1.3
4              4.6         3.1              1.5
5              5.0         3.6              1.4
Adam Quek
  • 6,973
  • 1
  • 17
  • 23
2

Assuming the aim is to rename all columns that contain "Length":

colnames(df) <- ifelse(grepl("Length", colnames(df)), 
                       paste0(colnames(df), to_app), 
                       colnames(df))
neilfws
  • 32,751
  • 5
  • 50
  • 63
2

The best I can do in the development version of dplyr (to be released on May 11th):

cols <- c("Sepal.Length", "Petal.Length")
to_app <- ".xxx"
ns <- paste0(cols, to_app)

rename(df, 
       !!ns[1] := !!as.name(cols[1]), 
       !!ns[2] := !!as.name(cols[2]))

To do this fully programatically, one needs to use quos instead:

xx <- do.call(quos, setNames(map(cols, as.name), ns))
rename(df, !!!xx)

Both give:

# A tibble: 5 × 3
  Sepal.Length.xxx Sepal.Width Petal.Length.xxx
*            <dbl>       <dbl>            <dbl>
1              5.1         3.5              1.4
2              4.9         3.0              1.4
3              4.7         3.2              1.3
4              4.6         3.1              1.5
5              5.0         3.6              1.4

One liner:

rename(df, !!!do.call(quos, setNames(map(cols, as.name), paste0(cols, to_app))))
Axeman
  • 32,068
  • 8
  • 81
  • 94
  • 1
    The question then is, how would this be done without calling each column separately? I think it might be something with `!!!` instead of `!!` – talat Apr 19 '17 at 08:23
  • Yes, I've added _a_ solution for that, but not sure if there is a way to avoid `do.call` and/or `setNames` there. – Axeman Apr 19 '17 at 08:25
  • 2
    Good to see this example, but I hope that will not be the final version of doing since imho, it's too complicated for a rather simple column renaming – talat Apr 19 '17 at 08:31
  • I agree! Some things have become nicer, but I don't readily see a convenient way of doing this with `tidyeval`/`rlang`. I wouldn't say it's terrible though. Maybe @hadley can give us his take on this. – Axeman Apr 19 '17 at 08:36
  • 6
    [tongue-in-cheek] and to think that Hadley once argued that in data.table one needs to learn ["*cryptic shorcuts*"](http://stackoverflow.com/questions/21435339/data-table-vs-dplyr-can-one-do-something-well-the-other-cant-or-does-poorly#comment32357862_21435339)... Not to mention that that `:=` thingi looks somewhat familiar from some cryptic package out there which I fail to recall its name.... – David Arenburg Apr 19 '17 at 08:55
  • @docendodiscimus For what it's worth, I don't think the final version will change much since package maintainers have already be notified of the upcoming release. – Axeman Apr 19 '17 at 11:33