unquote string as variable in pipe

I want to remove duplicate rows from a dataframe, for specific columns only. That can be obtained with distinct:

data <- tibble(a = c(1, 1, 2, 2), b = c(3, 3, 3, 4), z = c(5,4,5,5)) filtered_data <- data %>% distinct(a, b, .keep_all = T) dim(filtered_data) # [1] 3 3 

This is (almost) what I need. Yet, my problem is that the columnnames I need to use with distinct will change. So I have a string gen that contains the names of the columns I want to use for with the distinct function. They need to get unquoted to be usefull in the pipe. I found suggestions to use as.name() or eval(parse()). This however gives me a different result:

gen <- c("a", "b") filtered_data <- data %>% distinct(eval(parse(text = gen)), .keep_all = T) dim(filtered_data) # [1] 2 4 

The eval seems to do something funny with the amount of times the data is filtered. (and, adds an extra column. I could live with that, though...) So, how to obtain a similar result, as if I had used a,b, but by using a variable instead?

additional information I actually obtain gen by reading the columnnames of a dataframe: gen <- colnames(data)[1:2]. The solution suggested by @gymbrane would be perfect, if I had a way to transform the gen to c(a, b). The whole point is to avoid hardcoding the columnames. I tried things like gen <- noquotes(gen), which does not give an error in the rm_dup_rows function suggested below, but it does give a different result, giving the same sort of repeated filtering as I started with...

fixed I think I got it working. It might be unelegant, and I'm not sure if every step is necessary for the result, but it seems to work by combining the function provided by @gymbrane below with ensym and quos in a forloop while adding to a list in GlobalEnv (edit: GlobalEnv isn't necessary):

unquote_string <- function(string) { out <- list() i <- 1 for (s in string) { t <- ensym(s) out[i] <-dplyr::quos(!!t) i <- i+1 } return(out) } gen_quo <- unquote_string(gen) filtered_data <- rm_dup_rows(data, gen_quo) dim(filtered_data) # [1] 3 3 
3

1 Answer

How about creating a function and using quosures . Perhaps something like this is what you are looking for...

rm_dup_rows <- function(data, ...){ vars = dplyr::quos(...) data %>% distinct(!!! vars, .keep_all = T) } 

I believe this returns what you are asking for

rm_dup_rows(data = data, a, b) # A tibble: 3 x 3 a b z <dbl> <dbl> <dbl> 1 3 5 2 3 5 2 4 5 rm_dup_rows(data, b, z) # A tibble: 3 x 3 a b z <dbl> <dbl> <dbl> 1 3 5 1 3 4 2 4 5 

Additional

You could modify rm_dup_rows just slightly and construct and your vector with quos. Something like this...

rm_dup_rows <- function(data, vars){ data %>% distinct(!!! vars, .keep_all = T) } # quos your column name vector gen <- quos(a,z) rm_dup_rows(data, gen) # A tibble: 3 x 3 a b z <dbl> <dbl> <dbl> 1 3 5 1 3 4 2 3 5 
4

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

You Might Also Like