在 mutate_if 调用中提取列名
Extract column name in mutate_if call
我想提取函数调用中的列名 mutate_if
。有了这个,然后我想查找一个值
不同 table 并用查找值填充缺失值。我尝试使用 quosure
语法,但它不起作用。
是否可以直接提取列名?
示例数据
df <- structure(list(x = 1:10,
y = c(1L, 2L, 3L, NA, 1L, 2L, 3L, NA, 1L, 2L),
z = c(NA, 2L, 3L, NA, NA, 2L, 3L, NA, NA, 2L),
a = c("a", "b", "c", "d", "e", "a", "b", "c", "d", "e")),
.Names = c("x", "y", "z", "a"),
row.names = c(NA, -10L),
class = c("tbl_df", "tbl", "data.frame"))
df_lookup <- tibble(x = 0L, y = 5L, z = 8L)
不工作
以某种方式直接提取名称是行不通的。
df %>%
mutate_if(is.numeric, funs({
x <- .
x <- enquo(x)
lookup_value <- df_lookup %>% pull(quo_name(x))
x <- ifelse(is.na(x), lookup_value, x)
return(x)
}))
有了一个额外的功能,我可以提取名称,但是替换就不再起作用了。
custom_mutate <- function(v) {
v <- enquo(v)
lookup_value <- df_lookup %>% pull(quo_name(v))
# ifelse(is.na((!!v)), lookup_value, (!!v))
}
df %>%
mutate_if(is.numeric, funs(custom_mutate(v = .)))
有效
如果我将 df
作为附加参数添加到我的自定义函数中,它就可以工作,但是有没有办法不使用它?感觉不对,而不是 dplyr
的本意...如果我错了请纠正我 ;)
除此之外,我必须使用 UQE
而不是 !!
并且正如它在 Programming with dplyr:
中所说
UQE() is for expert use only
custom_mutate2 <- function(v, df) {
v <- enquo(v)
lookup_value <- df_lookup %>% pull(quo_name(v))
df %>%
mutate(UQE(v) := ifelse(is.na((!!v)), lookup_value, (!!v))) %>%
pull(!!v)
}
df %>%
mutate_if(is.numeric, funs(custom_mutate2(v = ., df = df)))
预期输出
# A tibble: 10 x 4
# x y z a
# <int> <int> <int> <chr>
# 1 1 1 8 a
# 2 2 2 2 b
# 3 3 3 3 c
# 4 4 5 8 d
# 5 5 1 8 e
# 6 6 2 2 a
# 7 7 3 3 b
# 8 8 5 8 c
# 9 9 1 8 d
# 10 10 2 2 e
您必须使用 quo
而不是 enquo
#enquo(.) :
<quosure: empty>
~function (expr)
{
enexpr(expr)
}
...
#quo(.) :
<quosure: frame>
~x
<quosure: frame>
~y
<quosure: frame>
~z
以你为例:
mutate_if(df, is.numeric, funs({
lookup_value <- df_lookup %>% pull(quo_name(quo(.)))
ifelse(is.na(.), lookup_value, .)
}))
# A tibble: 10 x 4
x y z a
<int> <int> <int> <chr>
1 1 1 8 a
2 2 2 2 b
3 3 3 3 c
4 4 5 8 d
5 5 1 8 e
6 6 2 2 a
7 7 3 3 b
8 8 5 8 c
9 9 1 8 d
10 10 2 2 e
是绝对正确的(你需要使用 quo
)但是,因为我的第一个想法也是使用 enquo
我已经研究了为什么你必须使用quo
改为:
如果我们查看 mutate_if
的源代码,我们可以看到它是如何构造的:
dplyr:::mutate_if
#> function (.tbl, .predicate, .funs, ...)
#> {
#> funs <- manip_if(.tbl, .predicate, .funs, enquo(.funs), caller_env(),
#> ...)
#> mutate(.tbl, !(!(!funs)))
#> }
#> <environment: namespace:dplyr>
通过略微修改覆盖 dplyr
中的 mutate_if
函数,我可以插入对 print()
的调用,允许我查看正在传递的 funs
对象至 mutate
:
mutate_if <- function (.tbl, .predicate, .funs, ...)
{
funs <- dplyr:::manip_if(.tbl, .predicate, .funs, enquo(.funs), caller_env(),
...)
print(funs)
}
然后,运行 您的代码将使用这个修改后的 mutate_if
函数::
df <- structure(list(x = 1:10,
y = c(1L, 2L, 3L, NA, 1L, 2L, 3L, NA, 1L, 2L),
z = c(NA, 2L, 3L, NA, NA, 2L, 3L, NA, NA, 2L),
a = c("a", "b", "c", "d", "e", "a", "b", "c", "d", "e")),
.Names = c("x", "y", "z", "a"),
row.names = c(NA, -10L),
class = c("tbl_df", "tbl", "data.frame"))
df_lookup <- tibble(x = 0L, y = 5L, z = 8L)
df %>%
mutate_if(is.numeric, funs({
x <- .
x <- enquo(x)
lookup_value <- df_lookup %>% pull(quo_name(x))
x <- ifelse(is.na(x), lookup_value, x)
return(x)
}))
#> $x
#> <quosure>
#> expr: ^{
#> x <- x
#> x <- enquo(x)
#> lookup_value <- df_lookup %>% pull(quo_name(x))
#> x <- ifelse(is.na(x), lookup_value, x)
#> return(x)
#> }
#> env: 0000000007FBBFA0
#>
#> $y
#> <quosure>
#> expr: ^{
#> x <- y
#> x <- enquo(x)
#> lookup_value <- df_lookup %>% pull(quo_name(x))
#> x <- ifelse(is.na(x), lookup_value, x)
#> return(x)
#> }
#> env: 0000000007FBBFA0
#>
#> $z
#> <quosure>
#> expr: ^{
#> x <- z
#> x <- enquo(x)
#> lookup_value <- df_lookup %>% pull(quo_name(x))
#> x <- ifelse(is.na(x), lookup_value, x)
#> return(x)
#> }
#> env: 0000000007FBBFA0
现在,我们可以看到传递给 mutate 调用的函数列表已经用列名替换了 .
变量。这意味着,在语句中,有一个名为 x
、y
或 z
的变量,其值来自 df
.
想象一下简单的情况,我们有:
library(rlang)
x <- 1:10
quo(x)
#> <quosure>
#> expr: ^x
#> env: 0000000007615318
enquo(x)
#> <quosure>
#> expr: ^<int: 1L, 2L, 3L, 4L, 5L, ...>
#> env: empty
由此,希望您可以推断出为什么要使用 quo
而不是 enquo
。您在列名之后,这是变量的名称 - 由 quo
.
提供给您
因此,使用 quo
而不是 enquo
并且不首先将其分配给变量:
mutate_if(df, is.numeric, funs({
lookup_value <- df_lookup %>% pull(quo_name(quo(.)))
ifelse(is.na(.), lookup_value, .)
}))
我想提取函数调用中的列名 mutate_if
。有了这个,然后我想查找一个值
不同 table 并用查找值填充缺失值。我尝试使用 quosure
语法,但它不起作用。
是否可以直接提取列名?
示例数据
df <- structure(list(x = 1:10,
y = c(1L, 2L, 3L, NA, 1L, 2L, 3L, NA, 1L, 2L),
z = c(NA, 2L, 3L, NA, NA, 2L, 3L, NA, NA, 2L),
a = c("a", "b", "c", "d", "e", "a", "b", "c", "d", "e")),
.Names = c("x", "y", "z", "a"),
row.names = c(NA, -10L),
class = c("tbl_df", "tbl", "data.frame"))
df_lookup <- tibble(x = 0L, y = 5L, z = 8L)
不工作
以某种方式直接提取名称是行不通的。
df %>%
mutate_if(is.numeric, funs({
x <- .
x <- enquo(x)
lookup_value <- df_lookup %>% pull(quo_name(x))
x <- ifelse(is.na(x), lookup_value, x)
return(x)
}))
有了一个额外的功能,我可以提取名称,但是替换就不再起作用了。
custom_mutate <- function(v) {
v <- enquo(v)
lookup_value <- df_lookup %>% pull(quo_name(v))
# ifelse(is.na((!!v)), lookup_value, (!!v))
}
df %>%
mutate_if(is.numeric, funs(custom_mutate(v = .)))
有效
如果我将 df
作为附加参数添加到我的自定义函数中,它就可以工作,但是有没有办法不使用它?感觉不对,而不是 dplyr
的本意...如果我错了请纠正我 ;)
除此之外,我必须使用 UQE
而不是 !!
并且正如它在 Programming with dplyr:
UQE() is for expert use only
custom_mutate2 <- function(v, df) {
v <- enquo(v)
lookup_value <- df_lookup %>% pull(quo_name(v))
df %>%
mutate(UQE(v) := ifelse(is.na((!!v)), lookup_value, (!!v))) %>%
pull(!!v)
}
df %>%
mutate_if(is.numeric, funs(custom_mutate2(v = ., df = df)))
预期输出
# A tibble: 10 x 4
# x y z a
# <int> <int> <int> <chr>
# 1 1 1 8 a
# 2 2 2 2 b
# 3 3 3 3 c
# 4 4 5 8 d
# 5 5 1 8 e
# 6 6 2 2 a
# 7 7 3 3 b
# 8 8 5 8 c
# 9 9 1 8 d
# 10 10 2 2 e
您必须使用 quo
而不是 enquo
#enquo(.) :
<quosure: empty>
~function (expr)
{
enexpr(expr)
}
...
#quo(.) :
<quosure: frame>
~x
<quosure: frame>
~y
<quosure: frame>
~z
以你为例:
mutate_if(df, is.numeric, funs({
lookup_value <- df_lookup %>% pull(quo_name(quo(.)))
ifelse(is.na(.), lookup_value, .)
}))
# A tibble: 10 x 4
x y z a
<int> <int> <int> <chr>
1 1 1 8 a
2 2 2 2 b
3 3 3 3 c
4 4 5 8 d
5 5 1 8 e
6 6 2 2 a
7 7 3 3 b
8 8 5 8 c
9 9 1 8 d
10 10 2 2 e
quo
)但是,因为我的第一个想法也是使用 enquo
我已经研究了为什么你必须使用quo
改为:
如果我们查看 mutate_if
的源代码,我们可以看到它是如何构造的:
dplyr:::mutate_if
#> function (.tbl, .predicate, .funs, ...)
#> {
#> funs <- manip_if(.tbl, .predicate, .funs, enquo(.funs), caller_env(),
#> ...)
#> mutate(.tbl, !(!(!funs)))
#> }
#> <environment: namespace:dplyr>
通过略微修改覆盖 dplyr
中的 mutate_if
函数,我可以插入对 print()
的调用,允许我查看正在传递的 funs
对象至 mutate
:
mutate_if <- function (.tbl, .predicate, .funs, ...)
{
funs <- dplyr:::manip_if(.tbl, .predicate, .funs, enquo(.funs), caller_env(),
...)
print(funs)
}
然后,运行 您的代码将使用这个修改后的 mutate_if
函数::
df <- structure(list(x = 1:10,
y = c(1L, 2L, 3L, NA, 1L, 2L, 3L, NA, 1L, 2L),
z = c(NA, 2L, 3L, NA, NA, 2L, 3L, NA, NA, 2L),
a = c("a", "b", "c", "d", "e", "a", "b", "c", "d", "e")),
.Names = c("x", "y", "z", "a"),
row.names = c(NA, -10L),
class = c("tbl_df", "tbl", "data.frame"))
df_lookup <- tibble(x = 0L, y = 5L, z = 8L)
df %>%
mutate_if(is.numeric, funs({
x <- .
x <- enquo(x)
lookup_value <- df_lookup %>% pull(quo_name(x))
x <- ifelse(is.na(x), lookup_value, x)
return(x)
}))
#> $x
#> <quosure>
#> expr: ^{
#> x <- x
#> x <- enquo(x)
#> lookup_value <- df_lookup %>% pull(quo_name(x))
#> x <- ifelse(is.na(x), lookup_value, x)
#> return(x)
#> }
#> env: 0000000007FBBFA0
#>
#> $y
#> <quosure>
#> expr: ^{
#> x <- y
#> x <- enquo(x)
#> lookup_value <- df_lookup %>% pull(quo_name(x))
#> x <- ifelse(is.na(x), lookup_value, x)
#> return(x)
#> }
#> env: 0000000007FBBFA0
#>
#> $z
#> <quosure>
#> expr: ^{
#> x <- z
#> x <- enquo(x)
#> lookup_value <- df_lookup %>% pull(quo_name(x))
#> x <- ifelse(is.na(x), lookup_value, x)
#> return(x)
#> }
#> env: 0000000007FBBFA0
现在,我们可以看到传递给 mutate 调用的函数列表已经用列名替换了 .
变量。这意味着,在语句中,有一个名为 x
、y
或 z
的变量,其值来自 df
.
想象一下简单的情况,我们有:
library(rlang)
x <- 1:10
quo(x)
#> <quosure>
#> expr: ^x
#> env: 0000000007615318
enquo(x)
#> <quosure>
#> expr: ^<int: 1L, 2L, 3L, 4L, 5L, ...>
#> env: empty
由此,希望您可以推断出为什么要使用 quo
而不是 enquo
。您在列名之后,这是变量的名称 - 由 quo
.
因此,使用 quo
而不是 enquo
并且不首先将其分配给变量:
mutate_if(df, is.numeric, funs({
lookup_value <- df_lookup %>% pull(quo_name(quo(.)))
ifelse(is.na(.), lookup_value, .)
}))