在“+”运算符中跳过NA值是否可能?

16

我想在R中计算一个方程。我不想使用sum函数,因为它只返回一个值。我需要得到完整的数值向量。

x = 1:10
y = c(21:29,NA)
x+y
 [1] 22 24 26 28 30 32 34 36 38 NA

x = 1:10
y = c(21:30)
x+y
 [1] 22 24 26 28 30 32 34 36 38 40

我不想要:

sum(x,y, na.rm = TRUE)
[1] 280

不返回向量的内容。

这只是一个玩具示例,但我有一个更复杂的方程,使用多个长度为84647元素的向量。

这里是另一个我所指的例子:

x = 1:10
y = c(21:29,NA)
z = 11:20
a = c(NA,NA,NA,30:36)
5 +2*(x+y-50)/(x+y+z+a) 
 [1]       NA       NA       NA 4.388889 4.473684 4.550000 4.619048 4.681818 4.739130       NA

3
获取连接向量的按行求和。rowSums(cbind(x,y), na.rm = T) - M--
好的,那么您可以将它放在数据框格式中。不可能直接将它们用作向量吗?apply(cbind(x,y), 1, function(x) sum(x, na.rm = T)) - M. Beausoleil
2
cbind 创建的是矩阵,而不是数据框。 rowSums 已经被优化过了,因此比 apply(..., 1, sum, na.rm = T) 快得多。 - Gregor Thomas
在复杂表达式中跳过NA值并不需要使用自定义的 +。你可以简单地在最终结果向量中跳过NA值,例如 res<-res[!is.na(res)],这也避免了在不同数量的NA值的情况下对不同长度的向量求和的风险。如果你想用零来替换NA值,那就是另一回事了... - digEmAll
5个回答

19

1) %+% 定义自定义 + 运算符:

`%+%` <- function(x, y)  mapply(sum, x, y, MoreArgs = list(na.rm = TRUE))
5 + 2 * (x %+% y - 50) / (x %+% y %+% z %+% a)

提供:

[1] 3.303030 3.555556 3.769231 4.388889 4.473684 4.550000 4.619048 4.681818
[9] 4.739130 3.787879

以下是一些简单的示例:

1 %+% 2
## [1] 3

NA %+% 2
## [1] 2

2 %+% NA
## [1] 2

NA %+% NA
## [1] 0

2) na2zero 另一个可能性是定义一个将NA映射到0的函数,如下所示:

na2zero <- function(x) ifelse(is.na(x), 0, x)

X <- na2zero(x)
Y <- na2zero(y)
Z <- na2zero(z)
A <- na2zero(a)

5 + 2 * (X + Y - 50) / (X + Y + Z + A)

提供:

[1] 3.303030 3.555556 3.769231 4.388889 4.473684 4.550000 4.619048 4.681818
[9] 4.739130 3.787879

3) 合并以上内容 将(1)与(2)中的想法相结合的一种变化形式是:

X <- x %+% 0
Y <- y %+% 0
Z <- z %+% 0
A <- a %+% 0

5 + 2 * (X + Y - 50) / (X + Y + Z + A)

4) numeric0类 我们可以定义一个自定义类"numeric0",带有自己的+运算符:

as.numeric0 <- function(x) structure(x, class = "numeric0")
`+.numeric0` <- `%+%`

X <- as.numeric0(x)
Y <- as.numeric0(y)
Z <- as.numeric0(z)
A <- as.numeric0(a)

5 + 2 * (X + Y - 50) / (X + Y + Z + A)

注意: 所使用的输入是问题中提供的,即:

x = 1:10
y = c(21:29,NA)
z = 11:20
a = c(NA,NA,NA,30:36)

我正在尝试找到使用mapply并获得四个单独向量(X,Y,Z,A)的方法。出于好奇,这可能吗?(附言:对于编辑很抱歉,我看到了一个错别字) - M--
1
你可以这样做 attach(lapply(list(X = x, Y = y, Z = z, A = a), na2zero)) 或者 with(lapply(...), ...在 X、Y、Z、A 中的表达式...) - G. Grothendieck

16

使用rowSums

为了解释我的评论,您可以将向量连接起来,然后在结果矩阵上应用计算。这是您在问题末尾提供的示例的解决方案;

5 + 2 * (rowSums(cbind(x,y), na.rm = T)-50)/(rowSums(cbind(x,y,z,a), na.rm = T))

#  [1] 3.303030 3.555556 3.769231 4.388889 4.473684 4.550000 4.619048 4.681818 
#  [9] 4.739130 3.787879

替换 NA:

我在这里看到了一些解决方案,其思路是在向量中替换 NA。我认为这也会很有帮助:

y[is.na(y)] <- 0 #indexing NA values and replacing with zero

7
您可以使用ifelse()函数。
x = 1:10
y = c(21:29,NA)
x+y

[1] 22 24 26 28 30 32 34 36 38 NA

x + ifelse(is.na(y), 0, y)

[1] 22 24 26 28 30 32 34 36 38 10

7

数据

x = 1:10
y = c(21:29,NA)
x+y
# [1] 22 24 26 28 30 32 34 36 38 NA

1

foo1 = function(...){
    return(rowSums(cbind(...), na.rm = TRUE))
}
foo1(x, y)
# [1] 22 24 26 28 30 32 34 36 38 10

2

foo2 = function(...){
    Reduce('+', lapply(list(...), function(x) replace(x, is.na(x), 0)))
}
foo2(x, y)
# [1] 22 24 26 28 30 32 34 36 38 10

4
无需使用 data.frame,可以使用 cbind() 来保持矩阵形式并避免额外的转换(rowSums 只会将其转换回矩阵)。 - Gregor Thomas

5

仅供娱乐:

x=1:10
y=c(21:29, NA)

"[<-"(x, is.na(x), 0) + "[<-"(y, is.na(y), 0)
# [1] 22 24 26 28 30 32 34 36 38 10

这再次说明了R中的一切都是函数(也表明当需要时,R解释器足够聪明,可以将字符串转换为函数)。

语法更加简洁:

na.zero <- function(x)
{
    "[<-"(x, is.na(x), 0)
}
na.zero(x) + na.zero(y)
# [1] 22 24 26 28 30 32 34 36 38 10

更为广泛适用的版本:
na.replace <- function(x, value)
{
    "[<-"(x, is.na(x), value)
}
na.replace(x, 1) * na.replace(x, 1)
# [1]   1   4   9  16  25  36  49  64  81 100

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