在根据列找到条件后按行填充NA

时间:2015-09-02 15:41:39

标签: r

我试图用R中的数据框解决一点困境。我有下一个具有此结构的数据框DF(我在最后部分添加dput()版本):

Key M1 M2 M3 M4 M5 M6 M7
001  1 NA NA  1 NA NA  1
002 NA NA  1 NA NA  1 NA
003 NA NA NA  1  1 NA  1
004 NA NA  1 NA NA NA  1
005 NA NA NA  1 NA NA  1
006  1 NA NA NA NA NA NA
007 NA NA  1 NA NA NA  1

此数据框包含ID列(Key)以及包含NA1的多个列。我希望在下一个模式中填充包含NA的每一行:在两个NA之后有两个1和之前的1以及最后一个NA },然后NA必须填充1。如果未找到模式,则行中的元素必须保持其原始形式。例如,在第一行中有两种模式:1 NA NA 11 NA NA 1,然后NA必须填充1。我想得到这样的结果:

Key M1 M2 M3 M4 M5 M6 M7
001  1  1  1  1  1  1  1
002 NA NA  1  1  1  1 NA
003 NA NA NA  1  1 NA  1
004 NA NA  1 NA NA NA  1
005 NA NA NA  1  1  1  1
006  1 NA NA NA NA NA NA
007 NA NA  1 NA NA NA  1

模式已填充1。我尝试使用na.locf()包中的zoo,但它会更改NAsDF的其余内容。 dput()的{​​{1}}版本是下一个:

DF

非常感谢你的帮助!

3 个答案:

答案 0 :(得分:4)

我会使用行程编码为每行找到两个NA:

i <- t(apply(is.na(df[-1]), 1, function(x){
    r <- rle(x)

    # 2 NAs in a row => lengths = 2 & value is is.na true 
    r$values = r$lengths == 2 & r$values 

    # don't modify first / last run of the series
    r$values[1] <- r$values[length(r$lengths)] <- FALSE 

    inverse.rle(r)
}))

df[-1][i] <- 1
df


#   Key M1 M2 M3 M4 M5 M6 M7
# 1 001  1  1  1  1  1  1  1
# 2 002 NA NA  1  1  1  1 NA
# 3 003 NA NA NA  1  1 NA  1
# 4 004 NA NA  1 NA NA NA  1
# 5 005 NA NA NA  1  1  1  1
# 6 006  1 NA NA NA NA NA NA
# 7 007 NA NA  1 NA NA NA  1

这样,您就不需要转换为角色并返回。

答案 1 :(得分:2)

mat <- as.matrix(df[-1])
mat[is.na(mat)] <- 0L
strings <- apply(mat, 1, function(x) paste(x, collapse=""))
pattern_matches <- str_locate_all(strings, "(?=(1001))")
strloc <- sapply(pattern_matches, function(x) c(x[,"start"]))
for(i in seq(nrow(mat))) mat[i,strloc[[i]]+rep(1:2,each=length(strloc[[i]]))] <- 1
is.na(mat) <- mat==0
as.data.frame(cbind(df[1],mat))
#   Key M1 M2 M3 M4 M5 M6 M7
# 1 001  1  1  1  1  1  1  1
# 2 002 NA NA  1  1  1  1 NA
# 3 003 NA NA NA  1  1 NA  1
# 4 004 NA NA  1 NA NA NA  1
# 5 005 NA NA NA  1  1  1  1
# 6 006  1 NA NA NA NA NA NA
# 7 007 NA NA  1 NA NA NA  1

我采用了与@ColonelBeauvel类似的方法,但使用了矩阵结构。首先将0分配给所有NA值。然后将每一行粘贴在一起。匹配模式&#34; 1001&#34;来自str_locate_all包的stringr。对于任何想知道,完整模式"(?=(1001))"的优点是,它允许在下一场比赛中使用一个匹配的一部分,称为&#34;非消费正则表达式&#34;。

然后我们找到每场比赛的开始并分配&#39; 1&#39;与它相邻的两个零。

答案 2 :(得分:1)

这是一种完成工作的方法,当然可以在不将NA转换为0的情况下进行改进,并使用gsub进行环视(但我不是正则表达式杀手!)。

m是您原来的data.frame

x = do.call(paste, c(data.frame(1*!is.na(m[-1])), sep=''))

y = gregexpr('(?=1001)', x, perl=T)

func = function(u,v)
{
    if(any(u!=-1))
        sapply(u, function(i) substr(v, start=i, stop=i+2) <<- '111')

    as.numeric(strsplit(v,'')[[1]])
}

df = cbind(list('key'=m[,1]), data.frame(do.call(rbind, Map(func, y, x))))
df[df==0] = NA

#  key X1 X2 X3 X4 X5 X6 X7
#1 001  1  1  1  1  1  1  1
#2 002 NA NA  1  1  1  1 NA
#3 003 NA NA NA  1  1 NA  1
#4 004 NA NA  1 NA NA NA  1
#5 005 NA NA NA  1  1  1  1
#6 006  1 NA NA NA NA NA NA
#7 007 NA NA  1 NA NA NA  1