填写多个三明治NA值

时间:2019-02-15 22:28:25

标签: r dplyr data.table

我有一个带有 some 的示例表,但不是所有需要替换的NA值。

> dat
   id message index
1   1    <NA>     1
2   1     foo     2
3   1     foo     3
4   1    <NA>     4
5   1     foo     5
6   1    <NA>     6
7   2    <NA>     1
8   2     baz     2
9   2    <NA>     3
10  2     baz     4
11  2     baz     5
12  2     baz     6
13  3     bar     1
14  3    <NA>     2
15  3    <NA>     3
16  3     bar     4
17  3    <NA>     5
18  3     bar     6
19  3    <NA>     7
20  3     qux     8

我的目标是使用消息的第一个外观(最小的index值)和消息的最后一个外观(使用最大的{{ 1}}值)按ID

有时,NA序列的长度仅为1,有时它们可​​能会非常长。无论如何,应该将“夹在中间的所有NA”填充在邮件之间。

上述不完整表的输出为:

index

此处使用 > output id message index 1 1 <NA> 1 2 1 foo 2 3 1 foo 3 4 1 foo 4 5 1 foo 5 6 1 <NA> 6 7 2 <NA> 1 8 2 baz 2 9 2 baz 3 10 2 baz 4 11 2 baz 5 12 2 baz 6 13 3 bar 1 14 3 bar 2 15 3 bar 3 16 3 bar 4 17 3 bar 5 18 3 bar 6 19 3 <NA> 7 20 3 qux 8 data.table的任何指导都将有所帮助,因为我什至不确定从哪里开始。

据我所知,它是通过唯一消息进行子集设置的,但是此方法并未考虑dplyr

id

数据:

#get distinct messages
messages = unique(dat$message)

#remove NA
messages = messages[!is.na(messages)]

#subset dat for each message
for (i in 1:length(messages)) {print(dat[dat$message == messages[i],]) }

5 个答案:

答案 0 :(得分:3)

向前和向后执行na.locf0,如果它们相同,则使用公共值;否则,请使用NA。分组是通过ave完成的。

library(zoo)

filler <- function(x) {
  forward <- na.locf0(x)
  backward <- na.locf0(x, fromLast = TRUE)
  ifelse(forward == backward, forward, NA)
}
transform(dat, message = ave(message, id, FUN = filler))

给予:

   id message index
1   1    <NA>     1
2   1     foo     2
3   1     foo     3
4   1     foo     4
5   1     foo     5
6   1    <NA>     6
7   2    <NA>     1
8   2     baz     2
9   2     baz     3
10  2     baz     4
11  2     baz     5
12  2     baz     6
13  3     bar     1
14  3     bar     2
15  3     bar     3
16  3     bar     4
17  3     bar     5
18  3     bar     6
19  3    <NA>     7
20  3     qux     8

答案 1 :(得分:1)

使用na.approx中的zoo的选项。

首先,我们从列message中提取不是NA的唯一元素,然后在dat$message中找到位置

x <- unique(na.omit(dat$message))
(y <- match(dat$message, x))
# [1] NA  1  1 NA  1 NA NA  2 NA  2  2  2  3 NA NA  3 NA  3 NA  4

library(zoo)
library(dplyr)
out <- do.call(coalesce, 
               lapply(seq_along(x), function(i) as.double(na.approx(match(y, i) * i, na.rm = FALSE))))
dat$new <- x[out]
dat
#    id message index  new
#1   1    <NA>     1 <NA>
#2   1     foo     2  foo
#3   1     foo     3  foo
#4   1    <NA>     4  foo
#5   1     foo     5  foo
#6   1    <NA>     6 <NA>
#7   2    <NA>     1 <NA>
#8   2     baz     2  baz
#9   2    <NA>     3  baz
#10  2     baz     4  baz
#11  2     baz     5  baz
#12  2     baz     6  baz
#13  3     bar     1  bar
#14  3    <NA>     2  bar
#15  3    <NA>     3  bar
#16  3     bar     4  bar
#17  3    <NA>     5  bar
#18  3     bar     6  bar
#19  3    <NA>     7 <NA>
#20  3     qux     8  qux

tl; dr

当我们打电话

match(y, 1) * 1
# [1] NA  1  1 NA  1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA

我们仅在1中有y个元素的地方获得元素。因此,当我们这样做

match(y, 2) * 2
# [1] NA NA NA NA NA NA NA  2 NA  2  2  2 NA NA NA NA NA NA NA NA

2的结果相同。

中的第一和第二个元素开始考虑12
x
# [1] "foo" "baz" "bar" "qux"

"foo""baz"

现在,对于每个match(y, i) * i,我们可以从na.approx调用zoo来填充介于两者之间的NAi将变成{{1} })。

seq_along(x)

我们对na.approx(match(y, 2) * 2, na.rm = FALSE) # [1] NA NA NA NA NA NA NA 2 2 2 2 2 NA NA NA NA NA NA NA NA 中的每个元素都执行相同的操作,即使用seq_along(x)1:4。结果是一个列表

lapply

(此处需要{lapply(seq_along(x), function(i) as.double(na.approx(match(y, i) * i, na.rm = FALSE))) #[[1]] # [1] NA 1 1 1 1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA # #[[2]] # [1] NA NA NA NA NA NA NA 2 2 2 2 2 NA NA NA NA NA NA NA NA # #[[3]] # [1] NA NA NA NA NA NA NA NA NA NA NA NA 3 3 3 3 3 3 NA NA # #[[4]] # [1] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA 4 ,因为否则as.double会抱怨“参数4必须为double类型,而不是整数”

我们快到了。接下来需要做的是找到每个位置的第一个非缺失值,这是coalesce中的coalesce起作用的地方,结果是

dplyr

我们可以使用此向量从out <- do.call(coalesce, lapply(seq_along(x), function(i) as.integer(na.approx(match(y, i) * i, na.rm = FALSE)))) out # [1] NA 1 1 1 1 NA NA 2 2 2 2 2 3 3 3 3 3 3 NA 4 中提取所需的值

x

希望这会有所帮助。

答案 2 :(得分:0)

这是一种无需分组以填充值的方法,如果填充不正确,则用NA代替。

tidyr::fill默认情况下会使用先前的值填充缺失的值,因此它将过度填充某些值。不幸的是,它不尊重分组,因此我们必须使用if_else条件来解决其错误。

首先,我们捕获原始的缺失值位置,并为每个indexid计算最大值和最小值message。填充后,我们加入这些index边界。如果没有匹配项,则id已更改;如果存在匹配项,则为正确的替换项,或者index在边界之外。因此,对于这些情况,我们将检查具有原始缺失值的位置,并在满足条件的情况下用NA替换。

编辑:这可能在其他输入上被破坏,试图修复

library(tidyverse)
dat <- structure(list(id = c(1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3), message = c(NA, "foo", "foo", NA, "foo", NA, NA, "baz", NA, "baz", "baz", "baz", "bar", NA, NA, "bar", NA, "bar", NA, "qux"), index = c(1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8)), row.names = c(NA, -20L), class = "data.frame")

indices <- dat %>%
  group_by(id, message) %>%
  summarise(min = min(index), max = max(index)) %>%
  drop_na

dat %>%
  mutate(orig_na = is.na(message)) %>%
  fill(message) %>%
  left_join(indices, by = c("id", "message")) %>% 
  mutate(
    message = if_else(
      condition = orig_na &
        (index < min | index > max | is.na(min)),
      true = NA_character_,
      false = message
    )
  )
#>    id message index orig_na min max
#> 1   1    <NA>     1    TRUE  NA  NA
#> 2   1     foo     2   FALSE   2   5
#> 3   1     foo     3   FALSE   2   5
#> 4   1     foo     4    TRUE   2   5
#> 5   1     foo     5   FALSE   2   5
#> 6   1    <NA>     6    TRUE   2   5
#> 7   2    <NA>     1    TRUE  NA  NA
#> 8   2     baz     2   FALSE   2   6
#> 9   2     baz     3    TRUE   2   6
#> 10  2     baz     4   FALSE   2   6
#> 11  2     baz     5   FALSE   2   6
#> 12  2     baz     6   FALSE   2   6
#> 13  3     bar     1   FALSE   1   6
#> 14  3     bar     2    TRUE   1   6
#> 15  3     bar     3    TRUE   1   6
#> 16  3     bar     4   FALSE   1   6
#> 17  3     bar     5    TRUE   1   6
#> 18  3     bar     6   FALSE   1   6
#> 19  3    <NA>     7    TRUE   1   6
#> 20  3     qux     8   FALSE   8   8

reprex package(v0.2.1)于2019-02-15创建

答案 3 :(得分:0)

另一个使用case_when的tidyverse解决方案。编辑以避免在系列结束后填充。

library(dplyr)

dfr <- data.frame(
  index =  c(1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8),
  message = c(NA, "foo", "foo", NA, "foo", NA, NA, "baz", NA, "baz", "baz", "baz", "bar", NA, NA, "bar", NA, "bar", NA, "qux"),
  id =  c(1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3)
)

dfrFilled <- dfr %>% 
  group_by(id) %>% 
  mutate(
    endSeries = max( # identify end of series
      index[message == na.omit(message)[1]],
      na.rm = T
      ),
    filledValues = case_when(
      min(index) == index ~ message,
      max(index) == index ~ message,
      index < endSeries ~ na.omit(message)[1], # fill if index is before end of series.
      TRUE ~ message
    )
  )

答案 4 :(得分:0)

如果您同时填写了两种方法并检查了是否可行,只要您考虑了分组和索引:

tidyverse:

library(tidyverse)

dat %>%
  arrange(id, index) %>%
  mutate(msg_down = fill(group_by(., id), message, .direction = 'down')$message,
         msg_up   = fill(group_by(., id), message, .direction = 'up')$message,
         message = case_when(!is.na(message) ~ message,
                             msg_down == msg_up ~ msg_down,
                             TRUE ~ NA_character_)) %>%
  select(-msg_down, -msg_up)

   id message index
1   1    <NA>     1
2   1     foo     2
3   1     foo     3
4   1     foo     4
5   1     foo     5
6   1    <NA>     6
7   2    <NA>     1
8   2     baz     2
9   2     baz     3
10  2     baz     4
11  2     baz     5
12  2     baz     6
13  3     bar     1
14  3     bar     2
15  3     bar     3
16  3     bar     4
17  3     bar     5
18  3     bar     6
19  3    <NA>     7
20  3     qux     8

data.table

library(data.table)
library(zoo)

setDT(dat)[order(index),
           message := ifelse(na.locf(message, na.rm = FALSE) == na.locf(message, na.rm = FALSE, fromLast = TRUE),
                             na.locf(message, na.rm = FALSE),
                             NA),
           by = "id"][]

    id message index
 1:  1    <NA>     1
 2:  1     foo     2
 3:  1     foo     3
 4:  1     foo     4
 5:  1     foo     5
 6:  1    <NA>     6
 7:  2    <NA>     1
 8:  2     baz     2
 9:  2     baz     3
10:  2     baz     4
11:  2     baz     5
12:  2     baz     6
13:  3     bar     1
14:  3     bar     2
15:  3     bar     3
16:  3     bar     4
17:  3     bar     5
18:  3     bar     6
19:  3    <NA>     7
20:  3     qux     8