R - заполнить значения последовательно от последнего ненулевого значения - PullRequest
6 голосов
/ 19 апреля 2020

У меня есть столбец данных, который выглядит следующим образом:

       a
       <int>
 1     11127
 2     0
 3     0
 4     NA
 5     0
 6     0
 7     NA
 8     0
 9     11580
11     0
12     NA
13     0

Я хочу заполнить значения NA последовательно от последнего ненулевого значения, чтобы конечный результат выглядел так:

       a
       <int>
 1     11127
 2     0
 3     0
 4     11128
 5     0
 6     0
 7     11129
 8     0
 9     11580
11     0
12     11581
13     0

Есть ли способ dplyr (предпочтительно) или базовый R для этого? Я предпочел избегать для l oop, так как число моих строк довольно большое.

Спасибо.

Ответы [ 3 ]

6 голосов
/ 19 апреля 2020

Один вариант:

library(dplyr)

df %>%
  group_by(idx = cumsum(!(is.na(a) | a == 0)), is.na(a)) %>%
  mutate(rn = row_number()) %>%
  group_by(idx) %>%
  mutate(a = coalesce(a, first(a) + rn)) %>%
  ungroup() %>%
  select(a)

Вывод:

# A tibble: 12 x 1
       a
   <int>
 1 11127
 2     0
 3     0
 4 11128
 5     0
 6     0
 7 11129
 8     0
 9 11580
10     0
11 11581
12     0

Если скорость является проблемой, возможно, эквивалент data.table будет немного быстрее:

library(data.table)

setDT(df)[, rn := rowid(a), .(cumsum(!(is.na(a) | a == 0)), is.na(a))][
  , a := fcoalesce(a, first(a) + rn), by = cumsum(!(is.na(a) | a == 0))][
    , rn := NULL]

РЕДАКТИРОВАТЬ

Группировка IMO, а затем получение индекса строки для NA s не очень элегантно; гораздо лучше, чем вы можете видеть в других решениях (например, используя cumsum).

Используя fcoalesce, проблема может быть решена за один data.table шаг:

library(data.table)

setDT(df)[, a := fcoalesce(a, first(a) + cumsum(is.na(a))), by = cumsum(!(is.na(a) | a == 0))]
5 голосов
/ 19 апреля 2020

Одно решение с использованием dplyr может быть:

df %>%
 group_by(id = cumsum(!is.na(a) & a != 0)) %>%
 mutate(a = ifelse(is.na(a), first(a) + cumsum(is.na(a)), a))

       a    id
   <int> <int>
 1 11127     1
 2     0     1
 3     0     1
 4 11128     1
 5     0     1
 6     0     1
 7 11129     1
 8     0     1
 9 11580     2
10     0     2
11 11581     2
12     0     2
4 голосов
/ 19 апреля 2020

Базовый подход R с cumsum(logical) и ave.

nze <- df1$a != 0 & !is.na(df1$a)
ave(df1$a, cumsum(nze), FUN = function(x){
  na <- is.na(x)
  x[na] <- x[!na][1] + seq_along(which(na))
  x
})
# [1] 11127     0     0 11128     0     0 11129     0 11580     0 11581     0

Затем присвойте этот результат.

df1$a <- ave(df1$a, cumsum(nze), FUN = function(x){
  na <- is.na(x)
  x[na] <- x[!na][1] + seq_along(which(na))
  x
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...