在R向量中找到第一个非NA值的索引位置?

54

我有一个问题,向量的开头有很多NA,之后才是数据。然而我的数据特别之处在于,前n个非NA值可能不可靠,因此我想删除它们并用NA替换它们。

例如,如果我有一个长度为20的向量,并且非NA值从索引位置4开始:

> z
 [1]          NA          NA          NA -1.64801942 -0.57209233  0.65137286  0.13324344 -2.28339326
 [9]  1.29968050  0.10420776  0.54140323  0.64418164 -1.00949072 -1.16504423  1.33588892  1.63253646
[17]  2.41181291  0.38499825 -0.04869589  0.04798073

我想要删除前三个非NA值,因为我认为它们是不可靠的,以得到如下结果:

> z
 [1]          NA          NA          NA          NA          NA          NA  0.13324344 -2.28339326
 [9]  1.29968050  0.10420776  0.54140323  0.64418164 -1.00949072 -1.16504423  1.33588892  1.63253646
[17]  2.41181291  0.38499825 -0.04869589  0.04798073

当然,我需要一个通用的解决方案,而且我从不知道第一个非NA值是什么时候开始的。 我应该如何做?也就是说,我如何找到第一个非NA值的索引位置?

为了完整起见,我的数据实际上是排列在数据框中的,每一列都有很多这样的向量,并且每个向量的非NA起始位置可能不同。 此外,一旦数据开始,可能会出现零散的NA值,这阻止了我简单地计算它们的数量作为解决方案。


5
有没有一种有效的方法,在找到第一个结果后就停止搜索? - Alex Brown
6个回答

80

使用 is.nawhich 的组合来查找非 NA 值的索引位置。

NonNAindex <- which(!is.na(z))
firstNonNA <- min(NonNAindex)

# set the next 3 observations to NA
is.na(z) <- seq(firstNonNA, length.out=3)

哎呀,这是我的第二个猜测。想用 rle() 来装饰一下,但我更喜欢这个解决方案。 - Roman Luštrik
完美,谢谢。经过一些思考,我想到了 min((1:length(z))[!is.na(z)]),但当然这个想法更好。完美。 - Thomas Browne
6
firstNonNA <- NonNAindex[1] 更快吗?使用 [1]min() 会遇到问题吗? - Florian Jenn
1
@FlorianJenn:是的,对于更大的向量来说,这可能会更快。我暂时想不到使用它而不是“min”会有什么问题。 - Joshua Ulrich
对于那些只想删除所有NA值的人(与此问题略有不同):x <- c(NA, "B", "C", "D"); x[!is.na(x)] - carbocation

27

与 @Joshua 的想法类似,但使用 which.min()

## dummy data
set.seed(1)
dat <- runif(10)
dat[seq_len(sample(10, 1))] <- NA

## start of data
start <- which.min(is.na(dat))

这会给出:

> (start <- which.min(is.na(dat)))
[1] 4

使用此代码将start:(start+2)设置为NA

is.na(dat) <- seq(start, length.out = 3)

导致:

> dat
 [1]         NA         NA         NA         NA         NA
 [6]         NA 0.94467527 0.66079779 0.62911404 0.06178627

更加干净。谢谢,也感谢继续回答。 - Thomas Browne
3
+1,但我不确定“更清晰”的部分。虽然代码变短了,但对于那些不知道which.minTRUEFALSE强制转换为10的人来说,可能会更加不清晰。 - Joshua Ulrich
2
@Joshua同意,它还依赖于which.min返回任何并列最小值的第一个的行为。不确定是否应该接受更短的答案。 - Gavin Simpson
这个似乎在NAs后面跟着非NAs的情况下会有困难,然后你这里和那里都有NAs。返回的索引不适用。 Joshua详细阐述的解决方案如预期工作。 - Matteo Castagna
1
@MatteoCastagna 这对于OP的示例和Q有效,其中NA在向量的前面。正如我在评论中提到的那样,这依赖于which.min()的行为,这正是它在您描述的情况下失败的原因。 - Gavin Simpson

17

如果处理大量数据,Positionwhich 要快得多,因为它只在找到匹配项时才进行评估,而不是对整个向量进行评估。

x=c(rep(NA,3),1:1e8)
Position(function(x) !is.na(x), x)
# 4

我们可以通过以下方式将NA值分配给以下N个值(或向量的末尾,以先到者为准):

pos = Position(function(x)!is.na(x), x)
x[pos:min(pos+N-1, length(x))] <- NA

这在大数据上表现良好。 - stats-hb
1
不需要定义新的函数,你可以使用complete.cases - IceCreamToucan

2
我会按照以下方式进行操作。
# generate some data
tb <- runif(10)
tb[1:3] <- NA

# I convert vector to TRUE/FALSE based on whether it's NA or not
# rle function will tell you when something "changes" in the vector
# (in our case from TRUE to FALSE)
tb.rle <- rle(is.na(tb))

# this is where vector goes from all TRUE to (at least one) FALSE
# your first true number is one position ahead, so +1
tb.rle$lengths[1] 

# you can now subset your vector with the first non-NA value
# and do with it whatever you want. I assign it a fantastic 
# non-believable number
tb[tb.rle$lengths[1] + 1] <- 42

2

zoo包中的na.trim()函数可以帮助解决问题。

library(zoo)
dummy.data <- c(rep(NA, 5), seq(1:7), NA)
x <- length(dummy.data) - length(na.trim(dummy.data, sides = "left"))
dummy.data[(x+1):(x+3)] <- NA
dummy.data
[1] NA NA NA NA NA NA NA NA  4  5  6  7 NA

-2

你也可以直接使用replace()函数,我知道答案已经在那里了,但像replace()这样的函数对于这些事情来说太好用了。

例如:

A <- c(1,2,3,4,5,NA,58,NA,98,NA,NA,NA)
which(is.na(A))
A <- replace(A,1:3,NA)

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