R 中管道运算符后'curly'括号的正确语法



请原谅我问了一个简单的问题,也许我误解了花括号{}在R中的具体工作方式,但是我看到了一些奇怪的行为-可能是由于我自己的误解-并且想要接触到社区,这样我就可以更好地理解我的编程。我也不确定为什么我看到is.na调用返回一个不适当的结果。

我有几列数据,在一个或多个列中有许多na。在删除一列中包含na的行之后,我想检查数据以确保我知道还剩下多少行,并记录所有na都被删除了。我可以在3个单独的行中做到这一点,但我试图使用管道操作符来简化。

library(magrittr)
df <- data.frame(a=rnorm(10, 3, 5),   #create a quick data frame without any na values
b=rnorm(10, -3, 5))
df %>% head()        #works
df %>% count()       #works
df %>% sum(is.na())  #doesn't work - error
#Error in is.na() : 0 arguments passed to 'is.na' which requires 1
df %>% sum(is.na(.)) #returns random number (perhaps sum of all values) instead of zero??

可能是一个单独的问题,但是为什么第一个不能工作,为什么第二个不能计算'is '。na的论点吗?如果我在第三个参数周围加上花括号,它将返回正确的值:

df %>% {             #works, but why is this different?
sum(is.na(.))
}
#[1] 0

现在,当我试着计算所有3时,我不理解我看到的行为:

df %>% {             #doesn't work - error
head()
count()
sum(is.na())
}
# Error in checkHT(n, dx <- dim(x)) : 
#   argument "x" is missing, with no default
df %>% {             #returns appropriate na count of zero, but nothing else is evaluated
head(.)
count(.)
sum(is.na(.))
}
# [1] 0
df %>% {             #returns first and third result, but not count(.)
print(head(.))
count(.)
sum(is.na(.))
}
#    a           b
# 1  0.3555877  -7.29064483
# 2 -2.6278037   4.30943634
# 3  5.6163705 -10.31436769
# 4 -2.8920773  -4.83949384
# 5  9.0941861  -0.09287319
# 6  2.6118720 -11.86665105
# [1] 0
df %>% {             #returns all three like I want
print(head(.))
print(count(.))
sum(is.na(.))
}
#    a           b
# 1  0.3555877  -7.29064483
# 2 -2.6278037   4.30943634
# 3  5.6163705 -10.31436769
# 4 -2.8920773  -4.83949384
# 5  9.0941861  -0.09287319
# 6  2.6118720 -11.86665105
#   n
# 1 10
# [1] 0

感谢您对如何解释此行为的任何建议,以便我下次可以改进我的代码。

这源于大括号在磁力和基数r中的行为。

首先,为什么df %>% sum(is.na(.))返回一个出乎意料的大数字,而df %>% {sum(is.na(.))}则像您期望的那样工作?默认情况下,%>%将左侧传递给右侧函数的第一个参数。所以df %>% sum(is.na(.))等于sum(df, is.na(df)),这应该能让你明白为什么它会产生一个大的数。然而,根据magrittr文档,这种行为可以通过用大括号括住右侧来推翻。当rhs用大括号括起来时,lhs仅在显式添加.占位符的地方插入。所以df %>% {sum(is.na(.))}等于sum(is.na(df))

在其次,

df %>% {
print(head(.))
print(count(.))
sum(is.na(.))
}

为什么你必须在print()中包装head(.)count(.),而不是sum()?这是因为,根据R文档,用{包装的表达式返回"最后一个表达式计算的结果"。因此,返回并自动打印sum(is.na(.))的结果,但不返回前面表达式的结果,因此必须显式地为print()ed。

最后,您可能会对nakedpipe包感兴趣,它为在包含函数块的管道中使用管道增加了更多的灵活性。

%>%管道将左侧传递到右侧,因此可以这样考虑:

head(df)
# is the same as 
df %>% head()

然而,如果你传递多个东西,你可能会遇到一个问题:

head(df) 
count(df) 
# is not the same as 
df %>% head() %>% count()

在上面,R首先处理head,然后计算head(df)中的值,因此返回值为6。

这就是为什么你的管道没有返回你期望的结果。

此外,您的df %>% sum(is.na(.))返回0,因为它将所有内容评估为FALSE(因为没有NA值),当您将布尔值FALSE == 0TRUE == 1相加时

is.na(df)
#          a     b
# [1,] FALSE FALSE
# [2,] FALSE FALSE
# [3,] FALSE FALSE
# [4,] FALSE FALSE
# [5,] FALSE FALSE
# [6,] FALSE FALSE
# [7,] FALSE FALSE
# [8,] FALSE FALSE
# [9,] FALSE FALSE
# [10,] FALSE FALSE
# so 
sum(is.na(df))
# [1] 0

如果你把你想要的东西包装在函数中,并将所有东西存储在一个列表中,你可能是最有效的:

example_function <- function(x){
list(head(x), count(x), sum(is.na(x)))
}
example_function(df)
# [[1]]
# a          b
# 1  0.1976218  3.1204090
# 2  1.8491126 -1.2009309
# 3 10.7935416 -0.9961427
# 4  3.3525420 -2.4465864
# 5  3.6464387 -5.7792057
# 6 11.5753249  5.9345657
# 
# [[2]]
# n
# 1 10
# 
# [[3]]
# [1] 0

最新更新