Yigreen
Yigreen

Reputation: 27

replacing NA by row with value in a list

I have a table that looks kind of like this:

#   item  1   2   3   4   5   6   7   8
#1    1   2   4   6   NA  NA  NA  NA  NA
#2    2   1   4   5   6   NA  NA  NA  NA
#3    3   NA  NA  NA  NA  NA  NA  NA  NA
#4    4   1   2   6   NA  NA  NA  NA  NA
#5    5   2   3   4   6   7   8   NA  NA

and I have a list

list1<-11:13

I want to replace the NAs with the elements in the list by row and result should be like this:

#  item   1   2   3   4   5   6  7   8
#1    1   2   4   6  11  12  13  NA  NA
#2    2   1   4   5   6  11  12  13  NA
#3    3  11  12  13  NA  NA  NA  NA  NA
#4    4   1   2   6  11  12  13  NA  NA  
#5    5   2   3   4   6   7   8  11  12  

I tried

for(i in 1:5){
  res<-which(is.na(Mydata[i,]))
  Mydata[i,res]<-c(list1, rep(NA, 8))
}

It seems to work with the table in the example but gives many warning messages. And when I run it with a really large table it sometimes gives the wrong result. Can anyone tell me what is wrong my code? Or is there any better way to do this?

Upvotes: 1

Views: 113

Answers (2)

thelatemail
thelatemail

Reputation: 93813

You could do it all in one go using matrix indexing:

sel <- pmin(outer( 0:2, max.col(is.na(dat), "first"), `+`), ncol(dat))
dat[unique(cbind(c(col(sel)),c(sel)))] <- 11:13

#     item  1  2  3  4  5  6  7  8
#[1,]    1  2  4  6 11 12 13 NA NA
#[2,]    2  1  4  5  6 11 12 13 NA
#[3,]    3 11 12 13 NA NA NA NA NA
#[4,]    4  1  2  6 11 12 13 NA NA
#[5,]    5  2  3  4  6  7  8 11 12

Upvotes: 3

akrun
akrun

Reputation: 887028

We loop through the rows of 'Mydata' using apply with MARGIN=1, create the numeric index for elements that are NA ('i1'), check the minimum length of the NA elements and the list1 ('l1') and replace the elements based on the minimum number of elements.

t(apply(Mydata, 1, function(x) {
           i1 <- which(is.na(x))
           l1 <- min(length(i1), length(list1))
            replace(x, i1[seq(l1)], list1[seq(l1)])}))

#  item X1 X2 X3 X4 X5 X6 X7 X8
#1    1  2  4  6 11 12 13 NA NA
#2    2  1  4  5  6 11 12 13 NA
#3    3 11 12 13 NA NA NA NA NA
#4    4  1  2  6 11 12 13 NA NA
#5    5  2  3  4  6  7  8 11 12

Or as @RichardSciven mentioned, we can use na.omit with apply by looping over the rows

t(apply(df, 1, function(x) { 
        w <- na.omit(which(is.na(x))[1:3])
        x[w] <- list1[1:length(w)]
        x }))

Upvotes: 3

Related Questions