Рассчитайте cumsum(), игнорируя значения NA

Рассмотрим следующий именованный вектор x,

( x <- setNames(c(1, 2, 0, NA, 4, NA, NA, 6), letters[1:8]) )
# a  b  c  d  e  f  g  h 
# 1  2  0 NA  4 NA NA  6 

Я хотел бы рассчитать совокупную сумму x игнорируя NA ценности. Многие функции R имеют аргумент na.rm который удаляет NA элементы до расчетов. cumsum() не один из них, что делает эту операцию немного сложнее.

Я могу сделать это таким образом.

y <- setNames(numeric(length(x)), names(x))
z <- cumsum(na.omit(x))
y[names(y) %in% names(z)] <- z
y[!names(y) %in% names(z)] <- x[is.na(x)]
y
# a  b  c  d  e  f  g  h 
# 1  3  3 NA  7 NA NA 13 

Но это кажется чрезмерным и делает много новых заданий / копий. Я уверен, что есть лучший способ.

Какие есть лучшие методы, чтобы вернуть накопленную сумму, эффективно игнорируя NA ценности?

5 ответов

Решение

Вы хотите что-то вроде этого:

x2 <- x
x2[!is.na(x)] <- cumsum(x2[!is.na(x)])

x2

[edit] В качестве альтернативы, как предложено в комментарии выше, вы можете изменить NA на 0 -

miss <- is.na(x)
x[miss] <- 0
cs <- cumsum(x)
cs[miss] <- NA
# cs is the requested cumsum

Вы можете сделать это в одной строке с:

cumsum(ifelse(is.na(x), 0, x)) + x*0
#  a  b  c  d  e  f  g  h 
#  1  3  3 NA  7 NA NA 13

Или, аналогично:

library(dplyr)
cumsum(coalesce(x, 0)) + x*0
#  a  b  c  d  e  f  g  h 
#  1  3  3 NA  7 NA NA 13 

Это старый вопрос, но tidyr дает новое решение. Основано на идее замены NA с нуля.

require(tidyr)

cumsum(replace_na(x, 0))

 a  b  c  d  e  f  g  h 
 1  3  3  3  7  7  7 13 

Вот функция, которую я придумал из ответов на этот вопрос. Думал, что поделюсь этим, так как, кажется, до сих пор хорошо работает. Он рассчитывает совокупный FUNC из x игнорируя NA, FUNC может быть любым из sum(), prod(), min(), или же max(), а также x числовой вектор

cumSkipNA <- function(x, FUNC)
{
    d <- deparse(substitute(FUNC))
    funs <- c("max", "min", "prod", "sum")
    stopifnot(is.vector(x), is.numeric(x), d %in% funs)
    FUNC <- match.fun(paste0("cum", d))
    x[!is.na(x)] <- FUNC(x[!is.na(x)])
    x
}

set.seed(1)
x <- sample(15, 10, TRUE)
x[c(2,7,5)] <- NA
x
# [1]  4 NA  9 14 NA 14 NA 10 10  1
cumSkipNA(x, sum)
# [1]  4 NA 13 27 NA 41 NA 51 61 62
cumSkipNA(x, prod)
# [1]      4     NA     36    504     NA   7056     NA
# [8]  70560 705600 705600
cumSkipNA(x, min)
# [1]  4 NA  4  4 NA  4 NA  4  4  1
cumSkipNA(x, max)
# [1]  4 NA  9 14 NA 14 NA 14 14 14 

Определенно ничего нового, но, возможно, кому-то пригодится.

Другой вариант — использованиеcollapseпакет сfcumsumфункционировать следующим образом:

      ( x <- setNames(c(1, 2, 0, NA, 4, NA, NA, 6), letters[1:8]) )
#>  a  b  c  d  e  f  g  h 
#>  1  2  0 NA  4 NA NA  6
library(collapse)
fcumsum(x)
#>  a  b  c  d  e  f  g  h 
#>  1  3  3 NA  7 NA NA 13

Создано 24 августа 2022 г. с репрексом v2.0.2

Другие вопросы по тегам