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

问题描述 投票:1回答:4

下面是一个类似于我正在使用的示例数据集。

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和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)

任何有关这方面的帮助将非常感激。

r na zoo
4个回答
3
投票

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

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
投票

这是一个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,即不复制整个对象。


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"))

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
© www.soinside.com 2019 - 2024. All rights reserved.