dplyr mutate和purrr map:使用数据掩码选择要映射的列

3
在dplyr mutate上下文中,我想通过purrr:map选择应用函数的列,并使用另一列的值。
让我们拿一个测试数据框架举例。
test <- data.frame(a = c(1,2), b = c(3,4), selector = c("a","b"))

我想要应用以下函数。
calc <- function(col)
{res <- col ^ 2
return(res)
}

我正在尝试类似这样的东西:

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

我的期望结果是:

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

但我得到

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

我知道.data[[var]]只应在函数编程的特殊上下文中使用,但即使我将其包装在函数或类似的东西中,也无法完成它。尝试使用tidy-selection会出现错误,选择助手只能在特殊的dplyr动词中使用,而不能在像purrr:map这样的函数中使用。

如何在dplyr中使用purrr map中的动态变量 提示我使用get()和匿名函数,但在这种情况下也不起作用。

5个回答

3
这是一种方法:
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

除了使用 .data,你也可以使用 cur_data(它会考虑到分组):

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

或者,使用 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

1
我不知道seq_along - 非常有趣! - Mhairi McNeill
1
非常感谢,我没有想到在函数中简单地对选择器进行子集操作,seq_along 对此非常好用! - taxecron

2
你可以使用 rowwise()get() 函数来选择变量:
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

如果选择器重复出现,使用group_by()会更有效率:
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

1

您也可以将函数更改为返回单个数字并使用purrr

calc <- function(col, id) {test[[col]][[id]]^2}

test %>% 
    mutate(
        quad = purrr::map2_dbl(selector, row_number(), calc)
    )
  a b selector quad
1 1 3        a    1
2 2 4        b   16

1
使用基础R:
test$quad <- calc(test[,test$selector][cbind(seq_len(nrow(test)), test$selector)])

(R版本3.5.3中,字符串在data.frame中被转换为因子)

0

虽然不完全符合您的要求,但另一种选择可能是重新组织数据,使计算更容易:

test %>% 
   pivot_longer(
       cols = c(a, b)
   ) %>% 
   filter(name == selector) %>% 
   mutate(quad = value**2)

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

您可以使用一个id列将结果与原始数据连接起来。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接