使用 dplyr::across 和两组变量
Using dplyr::across with two sets of variables
我有两组变量,例如变量a
和变量a_avail
。我正在尝试根据 a_avail
的值更改 a
的值,并且想知道是否可以使用 across
和 glue
.
来完成此操作
这是我试过的。没有产生错误,但是胶水似乎没有获取 .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 年 2 月 12 日创建
不是一个 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)
您遇到的主要问题是引用列本身,而不仅仅是将字符串(或 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
我认为您可以通过 purrr
包轻松实现您想要的输出。在某种程度上,我们使用 map2
函数而不是使用 across
函数,因为我们同时处理 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
包函数。
Ronak Shah 在他的 to a related 中提出了一个很棒的方法,我在下面复制了它。
实际上是两件事
- 在
mutate(across..
中使用 column/variable 名称而不是值 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
我有两组变量,例如变量a
和变量a_avail
。我正在尝试根据 a_avail
的值更改 a
的值,并且想知道是否可以使用 across
和 glue
.
这是我试过的。没有产生错误,但是胶水似乎没有获取 .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 年 2 月 12 日创建不是一个 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)
您遇到的主要问题是引用列本身,而不仅仅是将字符串(或 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
我认为您可以通过 purrr
包轻松实现您想要的输出。在某种程度上,我们使用 map2
函数而不是使用 across
函数,因为我们同时处理 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
包函数。
Ronak Shah 在他的
实际上是两件事
- 在
mutate(across..
中使用 column/variable 名称而不是值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