В базе R
:
vec <- scan(text="1 2 NA 3 4 NA NA NA NA NA NA NA 5 NA NA NA NA 6 NA NA NA NA NA 7", what=numeric())
diff_vec <- diff(is.na(c(0,vec)))
# [1] 0 0 1 -1 0 1 0 0 0 0 0 0 -1 1 0 0 0
# [18] -1 1 0 0 0 0 -1
data.frame(start= which(diff_vec==1)-1,
end= which(diff_vec==-1))
# start end
# 1 2 4
# 2 5 13
# 3 13 18
# 4 18 24
0
в c(0,vec)
помогает нам быть уверенными, что мы не начинаем с NA
Другойрешение с data.table::rleid
и tapply
:
library(data.table)
do.call(rbind,
tapply(seq_along(vec)[is.na(vec)],rleid(vec)[is.na(vec)],
function(x) data.frame(start=min(x)-1,end=max(x)+1)))
# start end
# 3 2 4
# 6 5 13
# 8 13 18
# 10 18 24
Здесь я использую rleid
для создания групп и seq_along
для создания индексов, и среди этих групп я беру * min
и max
index.
Базовое решение с использованием rle
:
vec[is.na(vec)] <- Inf
rle_ <- rle(vec)
cumsum_ <- cumsum(rle_$lengths)
infs_ <- which(rle_$value == Inf)
data.frame(start = c(0,cumsum_)[infs_], end = cumsum_[infs_]+1)
# start end
# 1 2 4
# 2 5 13
# 3 13 18
# 4 18 24
base::rle
дает специальную обработку NAs
, тогда как data.table::rleid
нет, поэтому я использовалInf
, делает его несколько менее устойчивым.