Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dplyr mutate and purrr map: use data masking to select columns for map

In a dplyr mutate context, I would like to select the column a function is applied to by purrr:map using the value of another column.

Let's take a test data frame

test <- data.frame(a = c(1,2), b = c(3,4), selector = c("a","b"))

I want to apply following function

calc <- function(col)
{res <- col ^ 2
return(res)
}

I am trying something like this:

test_2 <- test %>% mutate(quad = map(.data[[selector]], ~ calc(.x)))

My expected result would be:

  a b selector quad
1 1 3        a    1
2 2 4        b   16

but I get

Error in local_error_context(dots = dots, .index = i, mask = mask) : 
  promise already under evaluation: recursive default argument reference or earlier problems?

I know .data[[var]] is supposed to be used only in special context of function programming, but also if I wrap this in functions or similar I cannot get it done. Trying to use tidy-selection gives the error that selecting helpers can only be used in special dplyr verbs, not functions like purrr:map.

how to use dynamic variable in purrr map within dplyr hinted me to use get() and anonymous functions, but this also did not work in this context.

like image 786
taxecron Avatar asked Nov 02 '25 03:11

taxecron


2 Answers

Here's one way:

test %>% 
  mutate(quad = map(seq_along(selector), ~ calc(test[[selector[.x]]])[.x]))

#   a b selector quad
# 1 1 3        a    1
# 2 2 4        b   16

Instead of .data, you can also cur_data (which accounts for grouping):

test %>% 
  mutate(quad = map(seq(selector), ~ calc(cur_data()[[selector[.x]]])[.x]))

Or, with diag:

test %>% 
  mutate(quad = diag(as.matrix(calc(cur_data()[selector]))))

#  a b selector quad
#1 1 3        a    1
#2 2 4        b   16
like image 105
Maël Avatar answered Nov 03 '25 17:11

Maël


You can use rowwise() and get() the selector variable:

library(dplyr)

test %>%
  rowwise() %>%
  mutate(quad = calc(get(selector))) %>%
  ungroup()

# A tibble: 2 × 4
      a     b selector  quad
  <dbl> <dbl> <chr>    <dbl>
1     1     3 a            1
2     2     4 b           16

Or if the selector repeats, group_by() will be more efficient:

test <- data.frame(a = c(1,2,5), b = c(3,4,6), selector = c("a","b","a"))

test %>%
  group_by(selector) %>%
  mutate(quad = calc(get(selector[1]))) %>%
  ungroup()

# A tibble: 3 × 4
      a     b selector  quad
  <dbl> <dbl> <chr>    <dbl>
1     1     3 a            1
2     2     4 b           16
3     5     6 a           25
like image 40
Ritchie Sacramento Avatar answered Nov 03 '25 19:11

Ritchie Sacramento



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!