Rename only if field exists, otherwise ignore - r

One can rename a field:
mtcars %>% rename(bla = mpg)
But if the field doesn't exist, an error:
mtcars %>% rename(MPG = mpg, CYL = cyl, bla = uyhgfrtgf)
Error: Can't rename columns that don't exist.
x Column `uyhgfrtgf` doesn't exist
I looked at ?rename_if and it says this is now superseded by rename_with().
What's the 'right' way to attempt to rename fields but with a possibility they don't exist (e.g. in this case a ShinyApp with filter selectors).

Use any_of() with a named vector. any_of() doesn't error if a variable isn't found.
library(dplyr)
lookup <- c(MPG = "mpg", CYL = "cyl", bla = "uyhgfrtgf")
mtcars %>%
rename(any_of(lookup))
MPG CYL disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
...

I'm pretty sure you can do this with NSE but I can't handle this (yet?).
So you could define a lookup variable first:
lookup <- c("mpg" = "MPG", "cyl" = "CYL", "uyhgfrtgf" = "bla")
Attention: it's the other way round as in your rename function.
Now you could use
library(dplyr)
df %>%
rename_with(.fn = ~lookup[.x], .cols = intersect(names(.), names(lookup)))
which returns
MPG CYL disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2

We may use
library(dplyr)
lookup <-c("MPG" = "mpg", "CYL" = "cyl", "bla" = "uyhgfrtgf")
mtcars %>%
rename(!!! lookup[names(.) %in% lookup])
MPG CYL disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
...

We could use rename_with from dplyr package and wrap it in a ~case_when function:
library(dplyr)
mtcars %>%
rename_with(
~ case_when(
. == "mpg" ~ "MPG",
. == "cyl" ~ "CYL",
. == "bla" ~ "uyhgfrtgf",
TRUE ~ .))
MPG CYL disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2

You could use the following solution:
library(dplyr)
library(stringr)
names <- c("mpg", "cyl", "sdad")
reps <- c("MPG", "CYL", "BLA")
mtcars %>%
rename_with(~ str_replace_all(.x, "\\w+", reps[names(mtcars) %in% names]), matches(names))
MPG CYL disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1

Related

Re-order dataframe columns in R

I need to re-ordering the columns' position in a dataframe with 500 columns. In fact, I only want the last column to be moved between the third and the fourth columns.
Here is what I tried:
df[ ,c(1, 2, 3, ncol(df), 4:ncol(df)-1)]
But it gives me a vector of values which are the columns' number. Would you someone tell me what I expect wrong from this code?
The issue maybe related to the operator precedence - wrap the (ncol(df)-1) within bracket (assuming the original object is a data.frame)
library(data.table)
df <- df[ ,c(1, 2, 3, ncol(df), 4:(ncol(df)-1)), with = FALSE]
Or use setcolorder to update the original object
setcolorder(df, c(1, 2, 3, ncol(df), 4:(ncol(df)-1)))
NOTE: with = FALSE was added after the OP confirmed it is a data.table object
Or another option is select
library(dplyr)
df <- df %>%
select(1:3, last_col(), everything())
Or with relocate
df <- df %>%
relocate(last_col(), .before = 4)
-reproducible example testing
> data(mtcars)
> head(mtcars)[, c(1, 2, 3, ncol(mtcars), 4:(ncol(mtcars)-1))]
mpg cyl disp carb hp drat wt qsec vs am gear
Mazda RX4 21.0 6 160 4 110 3.90 2.620 16.46 0 1 4
Mazda RX4 Wag 21.0 6 160 4 110 3.90 2.875 17.02 0 1 4
Datsun 710 22.8 4 108 1 93 3.85 2.320 18.61 1 1 4
Hornet 4 Drive 21.4 6 258 1 110 3.08 3.215 19.44 1 0 3
Hornet Sportabout 18.7 8 360 2 175 3.15 3.440 17.02 0 0 3
Valiant 18.1 6 225 1 105 2.76 3.460 20.22 1 0 3
> head(mtcars) %>% select(1:3, last_col(), everything())
mpg cyl disp carb hp drat wt qsec vs am gear
Mazda RX4 21.0 6 160 4 110 3.90 2.620 16.46 0 1 4
Mazda RX4 Wag 21.0 6 160 4 110 3.90 2.875 17.02 0 1 4
Datsun 710 22.8 4 108 1 93 3.85 2.320 18.61 1 1 4
Hornet 4 Drive 21.4 6 258 1 110 3.08 3.215 19.44 1 0 3
Hornet Sportabout 18.7 8 360 2 175 3.15 3.440 17.02 0 0 3
Valiant 18.1 6 225 1 105 2.76 3.460 20.22 1 0 3
> ?relocate
> head(mtcars) %>% relocate(last_col(), .before = 4)
mpg cyl disp carb hp drat wt qsec vs am gear
Mazda RX4 21.0 6 160 4 110 3.90 2.620 16.46 0 1 4
Mazda RX4 Wag 21.0 6 160 4 110 3.90 2.875 17.02 0 1 4
Datsun 710 22.8 4 108 1 93 3.85 2.320 18.61 1 1 4
Hornet 4 Drive 21.4 6 258 1 110 3.08 3.215 19.44 1 0 3
Hornet Sportabout 18.7 8 360 2 175 3.15 3.440 17.02 0 0 3
Valiant 18.1 6 225 1 105 2.76 3.460 20.22 1 0 3

Curly curly - How to access the variable name [duplicate]

This question already has answers here:
In R, how to get an object's name after it is sent to a function?
(4 answers)
Closed 1 year ago.
I am trying to create a function which summarises a grouped dataset and then adds a column to identify which variable is being summarised (ID column).
I am not sure how to add the ID column using the curly curly appraoch.
my_fun <- function(dat, var_name){
dat %>%
mutate(id_column = names({{var_name}}))
}
my_fun(mtcars, cyl)
What I want is for the variable name, in this case cyl, to be recycled.
Just, deparse/subsitute at the start
my_fun <- function(dat, var_name){
nm1 <- deparse(substitute(var_name))
dat %>%
mutate(id_column = nm1)
}
-testing
my_fun(mtcars, cyl)
mpg cyl disp hp drat wt qsec vs am gear carb id_column
Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4 cyl
Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4 cyl
Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1 cyl
Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1 cyl
Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2 cyl
Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1 cyl
Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4 cyl
Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2 cyl
Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2 cyl
Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4 cyl
...
In the tidyverse, it may also be done directly from a symbol i.e. use ensym to convert to symbol and then evaluate (!!) to get the value or convert to string with as_string
my_fun <- function(dat, var_name){
var_name <- rlang::ensym(var_name)
dat %>%
mutate(id_column = rlang::as_string(var_name), val_column = !! var_name)
}
-testing
my_fun(head(mtcars), cyl)
mpg cyl disp hp drat wt qsec vs am gear carb id_column val_column
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 cyl 6
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 cyl 6
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 cyl 4
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 cyl 6
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 cyl 8
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 cyl 6

Rename colums with tidyverse using environment variables

I want to rename() some variables in my data programmatically, so I can to it via map at some point.
I'm looking for the equivalent of,
library(tidyverse)
mtcars %>% rename(
"MPG" = "mpg"
)
but using environment variables instead. I tried !!sym() by doing the following,
library(tidyverse)
new_name <- "MPG"
old_name <- "mpg"
mtcars %>% rename(
!!sym(new_name) = !!sym(old_name)
)
However, I get the error Error: unexpected ')' in ")". I am not sure what I am missing here!
We could use setNames and evaluate (!!!)
head(mtcars %>%
rename(!!! setNames(old_name, new_name)))
-output
MPG cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 6 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
You can use {{}} -
library(dplyr)
new_name <- "MPG"
old_name <- "mpg"
mtcars %>% rename({{new_name}} := {{old_name}}) %>% head
# MPG cyl disp hp drat wt qsec vs am gear carb
#Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
#Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
#Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
#Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
#Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
#Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1

Assigning function argument to column name

I'm looking to add the input of a function to the column name created in the function.
new_col = function(dat_, period_) {
dat_ %>% mutate(paste0('mpg',period_) = mpg + period_
}
mtcars %>% newcol(20)
In this function, mpg20 should be created where it's values are equal to mpg + 20.
You can use :
library(dplyr)
new_col = function(dat_, period_) {
dat_ %>% mutate(!!paste0('mpg',period_) := mpg + period_)
}
mtcars %>% new_col(20)
# mpg cyl disp hp drat wt qsec vs am gear carb mpg20
#Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4 41.0
#Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4 41.0
#Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1 42.8
#Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1 41.4
#Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2 38.7
#Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1 38.1
#...
#...
!! and := combination is used when we have a character variable at the LHS.

how to define the name of a new object with a string?

I would like to define a string
string<- "modelName"
That could be used to name an object later. Something like
paste0(string) <- mtcars
cat(string) <- mtcars
print(string) <- mtcars
get(string) <- mtcars
The needed result is the dataset called "modelName". None of the examples above work, obviously.
Question:
How can create one create an object which name is defined by the sourced string?
As #Spacedman notes this is not generally the way things are done but you can use assign
string<- "modelName"
assign(string, mtcars)
> head(modelName)
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
In general it may be perferable to use sometthing like a list:
x <- list()
x[[string]] <- mtcars
> head(x$modelName)
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1

Resources