使用列名而非位置来将列分配为purrr::pmap的参数

4
我试图使用purrr包的pmap函数循环遍历tibble的不同列。每一列包含某些模型参数(例如样本大小、系数等),我希望将它们用作我的函数的输入。每一行属于一个模拟模型。
因此,我想直接告诉pmap要引用哪些列作为参数 ..1, ..2等的名称,但我遇到了困难。
例如,以下代码将生成三个均匀分布,其中每一行定义分布的样本大小、最小和最大值。
set.seed(2022)
test <- tibble(n = c(10, 20, 30),
               min = c(0, 100, 500),
               max = c(100, 500, 1000))

test %>% pmap(..1 = n, ..2 = min, ..3 = max, ~runif(n = ..1, min = ..2, max = ..3) %>% round(digits = 0))

上面的代码运行正常。然而,似乎在pmap函数的第一部分明确指定..1 = n, ..2 = min等参数没有作用。相反,参数..1、..2和..3似乎是指数据框中实际列的位置。省略那个第一部分会产生相同的结果。
set.seed(2022)
test %>% pmap(~runif(n = ..1, min = ..2, max = ..3) %>% round(digits = 0))

如果数据框架有很多列或列的顺序发生变化,那么这将成为一个问题并容易出错。 例如,对于具有两个额外列的相同df(test2),上面的代码将抛出错误,因为第二列现已被替换为字符列,并且另外第一列和第三列也现在指代不同的内容。

test2 <- tibble(model = (1:3),
                type_dist= rep("uniform", 3),
                n = c(10, 20, 30),
                min = c(0, 100, 500),
                max = c(100, 500, 1000))
set.seed(2022)
test2 %>% pmap(..1 = n, ..2 = min, ..3 = max, ~runif(n = ..1, min = ..2, max = ..3) %>% round(digits = 0))

运行runif(n = ..1, min = ..2, max = ..3)时发生错误:参数无效

即使我尝试将..1、..2和..3明确分配给列名也没有用。 相反,我必须确保现在引用的是第三到第五列。

set.seed(2022)
test2 %>% pmap(~runif(n = ..3, min = ..4, max = ..5) %>% round(digits = 0))

使用多个数据帧或变换顺序的大量列时,很容易出现问题,比如混淆参数的顺序。因此我的问题是:是否可以通过列名而不是在数据框中的位置,显式地将 pmap 使用的 ..1, ..2, ..3, ... 参数分配给某一列?

可能重复:https://dev59.com/rFgR5IYBdhLWcg3wBZS3 - Maël
1
那么在你的情况下,类似于 args2 <- list(n = test$n, min = test$min, max = test$max) pmap(args2, ~with(list(...),runif(n, min, max))) 这样的代码是否合适? - Maël
3个回答

1

我认为你不需要使用 pmap 来实现你想要的功能。这样做是否能够达到你的目标?

library(tidyverse)

test2 %>%
   rowwise() %>% 
   group_map(
     function(.x, .y) {
       runif(n=.x$n, max=.x$max, min=.x$min) %>% 
       round(2)
     }
   ) %>%
   ungroup()
[[1]]
 [1]  77  33  60  49  67  53  36  62 100  60

[[2]]
 [1] 209 379 112 405 405 477 340 236 372 421 266 148 145 252 190 382 330 253 441 190

[[3]]
 [1] 931 717 878 551 650 682 802 916 946 665 870 865 580 937 511 704 659 900 759 689 542 642 799 863 794 538 903 860 589 524

1

如果您想在匿名函数内使用管道符号,可以直接传递点号,无需指定..1、..2、..3等参数。

set.seed(2022)
x = test %>% pmap(..1 = n, ..2 = min, ..3 = max, ~runif(n = ..1, min = ..2, max = ..3) %>% round(digits = 0))

set.seed(2022)
y = test %>%
  pmap(~runif(...) %>% round())

identical(x, y)
[1] TRUE

或者,如果您不介意走两步:

z <- test %>%
  pmap(runif) %>%
  map(round)
identical(x, z)
[1] TRUE

1

您只需要确保您的列与函数参数具有相同的名称。这意味着pmap有时难以与purrr风格的~函数一起使用(它们期望..1,..2等),并且通过使用\(args)function(args)来简化,其中您可以设置自己的参数:

test %>%
  pmap(
    \(n, min, max) round(runif(n, min, max))
  )

如果不需要四舍五入,那就更简单了:
test %>% pmap(runif)

如果您的数据框包含未用作参数的列,请添加...来吸收它们。(否则,您将会收到“未使用的参数”错误)。
test2 %>%
  pmap(
    \(n, min, max, ...) round(runif(n, min, max))
  )

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