将NA值替换为以其他列为条件的下一个或上一个非NA值

时间:2018-03-01 15:43:50

标签: r na zoo

以下是与我合作的示例数据集。

df<-data.frame(Loc=c(rev(seq(-4,5,1)),seq(-4,5,1)),
           Reg=c("A",rep(NA,8),"B",rep(NA,9),"C")) 

在这个例子中,我们有一串从+到 - 值的值,反之亦然(Loc)。我想要做的是填充这些NA值,其中B总是与Loc的负值相关联,但是,如果NA在A和B之间,则正值可以取值A,如果是NA不在B和C之间。

所需的输出应如下所示

df2<-data.frame(Loc=c(rev(seq(-4,5,1)),seq(-4,5,1)),
           Reg=c(rep("A",6),rep("B",8),rep("C",6)))

我已经查看了动物园软件包中的na.locf但是我不确定如何命令函数查找非NA值以获得所需的输出。

df$Reg2<-ifelse(df$Loc<=0,df$Reg2<-"B",na.locf(df$Reg,fromLast = F))

以上代码仅根据方向返回某些行的正确响应(即fromLast = T或F)

对此的任何帮助将不胜感激。

4 个答案:

答案 0 :(得分:3)

使用从ave符号生成的分组变量进行rleid分割。然后省略NA,在ave将为该组中的所有值复制的每个组中留下单个非NA。

library(data.table)

transform(df, Reg = ave(Reg, rleid(Loc >= 0), FUN = na.omit))

,并提供:

   Loc Reg
1    5   A
2    4   A
3    3   A
4    2   A
5    1   A
6    0   A
7   -1   B
8   -2   B
9   -3   B
10  -4   B
11  -4   B
12  -3   B
13  -2   B
14  -1   B
15   0   C
16   1   C
17   2   C
18   3   C
19   4   C
20   5   C

答案 1 :(得分:1)

这是一个data.table解决方案,可以复制OP的预期答案:

library(data.table)
result <- as.data.table(df)[, Reg := first(Reg[!is.na(Reg)]), by = rleid(Loc >= 0)][]
result
    Loc Reg
 1:   5   A
 2:   4   A
 3:   3   A
 4:   2   A
 5:   1   A
 6:   0   A
 7:  -1   B
 8:  -2   B
 9:  -3   B
10:  -4   B
11:  -4   B
12:  -3   B
13:  -2   B
14:  -1   B
15:   0   C
16:   1   C
17:   2   C
18:   3   C
19:   4   C
20:   5   C
identical(as.data.frame(result), df2)
[1] TRUE

请注意,此方法与G. Grothendiek's base R solution类似,因为它使用rleid(Loc >= 0)对数据进行分组,但不会调用transform()ave(),而是更新Reg通过引用,即不复制整个对象。

答案 2 :(得分:0)

以下是dplyr的快速解决方案:

df<-data.frame(Loc=c(rev(seq(-4,5,1)),seq(-4,5,1)),
           Reg=c("A",rep(NA,8),"B",rep(NA,9),"C")) 
c <- match("C",df$Reg)
a <- match("A",df$Reg)
df2 <- df %>%
  mutate(newReg=case_when(Loc < 0 ~ "B",
                      Loc >= 0 & abs(row_number()-c)<abs(row_number()-a)~ "C",
                      TRUE ~ "A"))

答案 3 :(得分:0)

注意:这很可怕,我怀疑这对于更多用例来说是可重现的...这可能更适合某种类型的dplyr::case_when函数,但我无法通过考虑这点。

lapply(2:nrow(df), function(i){
    this_row <- df[i, ]
    last_row <- i - 1 
    if(is.na(this_row[['Reg']])){
        if(this_row[['Loc']] < 0){
            df[i, 'Reg'] <<- "B"
        }else if(df[i - 1, 'Reg'] == "A"){
            df[i, 'Reg'] <<- "A"
        }else {
            df[i, "Reg"] <<- "C"
        }
    }
})



> df
   Loc Reg
1    5   A
2    4   A
3    3   A
4    2   A
5    1   A
6    0   A
7   -1   B
8   -2   B
9   -3   B
10  -4   B
11  -4   B
12  -3   B
13  -2   B
14  -1   B
15   0   C
16   1   C
17   2   C
18   3   C
19   4   C
20   5   C