将 dplyr::across 与两组变量一起使用

Posted

技术标签:

【中文标题】将 dplyr::across 与两组变量一起使用
【英文标题】:Using dplyr::across with two sets of variables将 dplyr::across 与两组变量一起使用
【发布时间】:2021-05-16 09:11:05
【相关技术】:@tags@
【问题描述】:

我有两组变量,例如变量a 和变量a_avail。我正在尝试根据a_avail 的值更改a 的值,我想知道这是否可以使用acrossglue 来完成。

这是我尝试过的。没有产生错误,但胶水似乎没有拾取.x_avail 的值,因为所有返回值都是 NA:

library(tidyverse)

df <- tibble(a = c(0, 1, 0, 0, 0),
       a_avail = c(1, 1, 1, 0, 0),
       b = c(1, 1, 1, 0, 0),
       b_avail = c(1, 0, 0, 1, 0))

df2 <- df %>% 
  mutate(across(.cols = c(a, b),
                .fns = ~case_when(
                  glue::glue(".x_avail") == 1 ~ .x,
                  glue::glue(".x_avail") == 0 ~ as.numeric(NA)
                ),
                .names = ".col_new"))

df2
#> # A tibble: 5 x 6
#>       a a_avail     b b_avail a_new b_new
#>   <dbl>   <dbl> <dbl>   <dbl> <dbl> <dbl>
#> 1     0       1     1       1    NA    NA
#> 2     1       1     1       0    NA    NA
#> 3     0       1     1       0    NA    NA
#> 4     0       0     0       1    NA    NA
#> 5     0       0     0       0    NA    NA

由reprex package (v0.3.0) 于 2021-02-12 创建

【问题讨论】:


【参考方案1】:

Ronak Shah 在他的 answer 和相关的 question 中提出了一种绝妙的方法,我将在下面复制。

其实两件事

mutate(across.. 内部使用列/变量名而不是值cur_column() 应与..x 相对应。 get() 也可以与 glue 一起使用,以便 R 将其识别为变量。

这样做

df %>% 
  mutate(across(.cols = c(a, b),
                .fns = ~case_when(
                  get(glue::glue("cur_column()_avail")) == 1 ~ .x,
                  get(glue::glue("cur_column()_avail")) == 0 ~ NA_real_
                ),
                .names = ".col_new"))

# A tibble: 5 x 6
      a a_avail     b b_avail a_new b_new
  <dbl>   <dbl> <dbl>   <dbl> <dbl> <dbl>
1     0       1     1       1     0     1
2     1       1     1       0     1    NA
3     0       1     1       0     0    NA
4     0       0     0       1    NA     0
5     0       0     0       0    NA    NA

【讨论】:

【参考方案2】:

我认为通过purrr 包可以轻松实现您想要的输出。在某种程度上,我们不使用across,而是使用map2函数,因为我们同时处理2个变量,并且为了我们的目的,我们希望逐行迭代它们:

library(dplyr)
library(purrr)

df <- tibble(a = c(0, 1, 0, 0, 0),
             a_avail = c(1, 1, 1, 0, 0),
             b = c(1, 1, 1, 0, 0),
             b_avail = c(1, 0, 0, 1, 0))


df %>%
  mutate(a_new = map2_dbl(a, a_avail, ~ ifelse(.y == 1, .x, NA)),
         b_new = map2_dbl(b, b_avail, ~ ifelse(.y == 1, .x, NA)))


# A tibble: 5 x 6
      a a_avail     b b_avail a_new b_new
  <dbl>   <dbl> <dbl>   <dbl> <dbl> <dbl>
1     0       1     1       1     0     1
2     1       1     1       0     1    NA
3     0       1     1       0     0    NA
4     0       0     0       1    NA     0
5     0       0     0       0    NA    NA

在这种情况下,最好仔细考虑哪个函数最能满足您的目的,并且最符合您给出的参数集,您想用它们做什么。由于我们正在处理逐行操作,因此我更愿意使用 purrr 包函数。

【讨论】:

【参考方案3】:

您遇到的主要问题是引用列本身,而不仅仅是将字符串(或glue 对象)与数字进行比较。您可能可以将一个 tidyeval 函数放在一起,但(可能)更简单的方法是将数据重塑为长格式,以便为原始值提供一列和可用性列,在新列中添加比较,然后重新调整.这也将进行缩放,因此您不必指定要执行此操作的所有列,或手动将原件与可用的准确匹配。

第一个技巧是使用某种方式标记原始列,以便您可以拆分,例如"a" 来自 "avail"。为此,将另一个字符串附加到只有单个字符的名称上。 (您可以使用不同的方法来选择列。)使用 ID 标记行 - 您可以稍后删除此列。第二个技巧是在数据透视函数中使用特殊的 ".value" 术语。

我建议逐步完成重塑步骤,看看它们是如何工作的,并根据需要进行调整。

library(dplyr)
library(tidyr)
df %>%
  rename_with(~paste(., "orig", sep = "_"), matches("^[a-z]$")) %>%
  tibble::rowid_to_column() %>%
  pivot_longer(-rowid, names_to = c("col", ".value"), names_sep = "_") %>%
  mutate(new = if_else(avail == 1, orig, NA_real_)) %>%
  pivot_wider(id_cols = rowid, names_from = col, values_from = orig:new, 
              names_glue = "col_.value")
#> # A tibble: 5 x 7
#>   rowid a_orig b_orig a_avail b_avail a_new b_new
#>   <int>  <dbl>  <dbl>   <dbl>   <dbl> <dbl> <dbl>
#> 1     1      0      1       1       1     0     1
#> 2     2      1      1       1       0     1    NA
#> 3     3      0      1       1       0     0    NA
#> 4     4      0      0       0       1    NA     0
#> 5     5      0      0       0       0    NA    NA

【讨论】:

【参考方案4】:

不是一个 tidyverse 解决方案,但这应该可以工作

library(tidyverse)

df <- tibble(a = c(0, 1, 0, 0, 0),
             a_avail = c(1, 1, 1, 0, 0),
             b = c(1, 1, 1, 0, 0),
             b_avail = c(1, 0, 0, 1, 0))


v1 <- list('a','b')
v2 <- list('a_avail','b_avail')


v3 <- as.data.frame(mapply(function(x,y)ifelse(df[[y]] == 0, NA,df[[x]]) , v1,v2, 
                           SIMPLIFY = TRUE))

names(v3) <- paste0(v1,"_new")

df3 <- cbind(df, v3)

【讨论】:

以上是关于将 dplyr::across 与两组变量一起使用的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server:按两列分组,并将第三列与两组的分叉相加

如何将ansible与两因素身份验证一起使用?

Oracle 的用户自定义聚合函数可以定义为与两列一起使用吗?

R语言工具变量与两阶段最小二乘法

Django:与模板变量一起返回模板

两组性别是不是有差异用spss卡方检验怎么做