29
votes

Is there a clever way to use the rename function in dplyr when in some instances the column to be renamed doesn't exist?

For example, I would like the following not to result in an error

mtcars%>%rename(miles_per_gallon=mpg,missing_varible=foo)

(results in this: Error: Unknown variables: foo.)

but rather the dataframe with all possible renaming done.

Currently I am explicitly checking that the specific column exists before renaming

Thanks

Iain

9
Why would you want to rename a column that doesn't exist? - DatamineR
Not all attributes (columns) exist for all objects (rows) that I am working with. This means if I subset the data in a particular way I can end up with a data frame that has fewer columns than I expect - hence the explicit checking - Iain
In fact, this is not an uncommon problem in code that is reused or shared, but accepts input from others. I've added one more solution @Iain - GGAnderson

9 Answers

44
votes

Perhaps not the intention of the designers, but you can utilize the scoped verb rename_all and the dplyr function recode, which takes one or more key value pairs like old_name = "New Name".

library(dplyr)

rename_all(iris, recode, Sepal.Length = "sepal_length", cyl = "cylinder")
#     sepal_length Sepal.Width Petal.Length Petal.Width    Species
# 1            5.1         3.5          1.4         0.2     setosa
# 2            4.9         3.0          1.4         0.2     setosa
# 3            4.7         3.2          1.3         0.2     setosa
# 4            4.6         3.1          1.5         0.2     setosa
# 5            5.0         3.6          1.4         0.2     setosa
# 6            5.4         3.9          1.7         0.4     setosa
# 7            4.6         3.4          1.4         0.3     setosa
# 8            5.0         3.4          1.5         0.2     setosa
# 9            4.4         2.9          1.4         0.2     setosa
21
votes

Sometimes it's okay to not do everything in dplyr. This may be one of those times. I would set up a vector that operates as a key:

namekey <- c(mpg="miles_per_gallon", cyl="cylinders", disp="displacement", hp="horse_power",
             drat="rear_axle_ratio", wt="weight", qsec="quarter_mile_time", vs="v_s",
             am="transmission", gear="number_of_gears", carb="number_of_carburetors",
             foo="missing_variable")

mtcars1 <- mtcars[,1:2]
mtcars1$foo <- rnorm(nrow(mtcars1))

names(mtcars1) <- namekey[names(mtcars1)]

head(mtcars1)
#                   miles_per_gallon cylinders missing_variable
# Mazda RX4                     21.0         6       -0.9901081
# Mazda RX4 Wag                 21.0         6        0.2338014
# Datsun 710                    22.8         4       -0.3077473
# Hornet 4 Drive                21.4         6        1.1200518
# Hornet Sportabout             18.7         8        0.7482842
# Valiant                       18.1         6        0.4206614

Once you have your key, it's just a single, easy-to-understand line of code that does the rename.

12
votes

The plyr package has a rename() function with a warn_missing parameter.

plyr::rename(
  mtcars, 
  replace      = c(mpg="miles_per_gallon", foo="missing_varible"),
  warn_missing = FALSE
)

If you use it, consider using requireNamespace() instead of library(), so its function names don't collide with dplyr's.

6
votes

You can use rename_at() to accomplish this:

mtcars %>%
  rename_at(vars(matches("^missing_var$")), function(x) "foo")

Doesn't return an error if "missing_var" isn't in your data.frame. If you want to replace multiple variables that may or may not exist, you can put the original and new names in a named vector/list, write a short little function to return the new variable name.

customRename <- function(orig_name) {
  renames <- c(
    foo = "OOF",
    bar = "RAB",
    fi = "IF")
  renames[[orig_name]]
}

mtcar %>%
  rename_at(vars(matches("^foo$|^bar$|^fi$")), customRename)
4
votes

One more solution that can safely operate within dplyr without throwing an error using conditional evaluation {}. This will apply the rename if "foo" exists, but continue execution with the original df in the absence of a column named "foo".

mtcars %>%
{if("foo" %in% names(.)) rename(., missing_varible=foo) else .} %>%
rename(miles_per_gallon=mpg) 
4
votes

They have now mentioned the use of rename_with

mtcars %>% rename_with( ~"missing_varible", "foo")

Please see the help for more details.

4
votes

To expand on Captain Tyler's mention of rename_with, if you have (potentially) more than one variable to rename, I find this solution using case_when works well and is easy to read:

iris %>%
    rename_with(
        ~ case_when(
            . == "Petal.Width" ~ "NewPetalWidthName",
            . == "NotActuallyThere" ~ "DoNothing",
            TRUE ~ .
        )
    ) %>%
    head()
3
votes

First, define a list with your column names you want to rename in a data frame df.

columnNamesToRename <-
    list(
      newColumnName1 = "oldColumnName1",
      newColumnName2 = "oldColumnName2"
    )

Next, remove the elements of the list, which have a column name as their name, but is not a column name of your data frame:

columnNamesToRename.WhichAreColumnNamesInTibble <-
columnNamesToRename[columnNamesToRename %in% intersect(
  unlist(columnNamesToRename),
  colnames(df)
)]

Then, you can rename the column names using the named list columnNamesToRename.WhichAreColumnNamesInTibble:

df.WithRenamedColumnNames <-
    df %>% dplyr::rename(!!!columnNamesToRename.WhichAreColumnNamesInTibble)
1
votes

Here is another way:

library(magrittr) # for `%>% `
library(dplyr) # for `rename_with`

mt <- mtcars[1:3, 1:4]
mt
#>                 mpg cyl disp  hp
#> Mazda RX4      21.0   6  160 110
#> Mazda RX4 Wag  21.0   6  160 110
#> Datsun 710     22.8   4  108  93

# c(old_column_name = "new_column_name")
column_recodes <- c(mpg = "miles_per_gallon", 
                    hp = "Horse Power",
                    missing_varible = "foo", 
                    `Another non-existent column name` = "bar")

mt %>% rename_with(~recode(., !!!column_recodes))
#>                miles_per_gallon cyl disp Horse Power
#> Mazda RX4                  21.0   6  160         110
#> Mazda RX4 Wag              21.0   6  160         110
#> Datsun 710                 22.8   4  108          93