1

Assume I have the following list:

list(c(1:5,NA,NA),NA,c(NA,6:10))

[[1]]
[1]  1  2  3  4  5 NA NA

[[2]]
[1] NA

[[3]]
[1] NA  6  7  8  9 10

I want to replace all NAs with 0:

[[1]]
[1] 1 2 3 4 5 0 0

[[2]]
[1] 0

[[3]]
[1]  0  6  7  8  9 10

I was originally thinking is.na would be involved, but couldn't get it to affect all list elements. I learned from the related question (Remove NA from list of lists), that using lapply would allow me to apply is.na to each element, but that post demonstrates how to remove (not replace) NA values.

How do I replace NA values from multiple list elements?

I've tried for loops and ifelse approaches, but everything I've tried is either slow, doesn't work or just plain clunky. There's got to be a simple way to do this with an apply function...

theforestecologist
  • 4,667
  • 5
  • 54
  • 91
  • 3
    `lapply(L1, function (x) ifelse(is.na(x), 0, x))` – janos Mar 16 '18 at 20:07
  • Btw, you might want to just store them in a single table, like `data.table::rbindlist(lapply(L, function(x) data.frame(x)), id=TRUE)` in which case normal methods will work.. – Frank Mar 16 '18 at 20:16
  • @janos thanks for the comment. See my [post below](https://stackoverflow.com/a/49329557/4581200) for performance comparison of `lapply` approaches. `replace()` is faster than `ifelse()`. – theforestecologist Mar 16 '18 at 21:17

6 Answers6

4

And there is!

Here's a simple lapply approach using the replace function:

L1 <-list(c(1:5,NA,NA),NA,c(NA,6:10))

lapply(L1, function(x) replace(x,is.na(x),0))

With the desired result:

[[1]]
[1] 1 2 3 4 5 0 0

[[2]]
[1] 0

[[3]]
[1]  0  6  7  8  9 10
theforestecologist
  • 4,667
  • 5
  • 54
  • 91
  • 2
    Or maybe `rapply` if the list is fancier: `rapply(L, function(x) replace(x, is.na(x), 0), classes = c("logical", "integer"), how="replace")` – Frank Mar 16 '18 at 20:13
3

There are multiple ways to do this:

using map from purrrr package.

lt <- list(c(1:5,NA,NA),NA,c(NA,6:10))
lt %>% 
    map(~replace(., is.na(.), 0))


#output

[[1]]
[1] 1 2 3 4 5 0 0

[[2]]
[1] 0

[[3]]
[1]  0  6  7  8  9 10
YOLO
  • 20,181
  • 5
  • 20
  • 40
1
kk<- list(c(1:5,NA,NA),NA,c(1,6:10))

lapply(kk, function(i) 
  { p<- which(is.na(i)==TRUE)
    i[p] <- 0
    i
  })

Edited upon Gregor's commment

lapply(kk, function(i) {i[is.na(i)] <- 0; i})
  • Thanks for the answer. This resembles my own early attempts. I was trying to find something a little bit easier to read/follow. – theforestecologist Mar 16 '18 at 20:15
  • 2
    You don't need `== TRUE` ever. And in this case the `which` is redundant. Just `i[is.na(i)] <- 0; i` is fine in the body of `function(i)`. – Gregor Thomas Mar 16 '18 at 20:35
1

I've decided to benchmark the various lapply approaches mentioned:

lapply(Lt, function(x) replace(x,is.na(x),0)) 
lapply(Lt, function(x) {x[is.na(x)] <- 0; x})
lapply(Lt, function(x) ifelse(is.na(x), 0, x)) 

Benchmarking code:

Lt <- lapply(1:10000, function(x)  sample(c(1:10000,rep(NA,1000))) )    ##Sample list

elapsed.time <- data.frame(
    m1 = mean(replicate(25,system.time(lapply(Lt, function(x) replace(x,is.na(x),0)))[3])),
    m2 = mean(replicate(25,system.time(lapply(Lt, function(x) {x[is.na(x)] <- 0; x}))[3])),
    m3 = mean(replicate(25,system.time(lapply(Lt, function(x) ifelse(is.na(x), 0, x)))[3]))
  )

Results:

Function                                          Average Elapsed Time
lapply(Lt, function(x) replace(x,is.na(x),0))     0.8684 
lapply(Lt, function(x) {x[is.na(x)] <- 0; x})     0.8936
lapply(Lt, function(x) ifelse(is.na(x), 0, x))    8.3176

The replace approach is fastest followed closely by the [] approach. The ifelse approach is 10x slower.

theforestecologist
  • 4,667
  • 5
  • 54
  • 91
0

This will deal with any list depth and structure:

x <-  eval(parse(text=gsub("NA","0",capture.output(dput(a)))))
# [[1]]
# [1] 1 2 3 4 5 0 0
# 
# [[2]]
# [1] 0
# 
# [[3]]
# [1]  0  6  7  8  9 10
moodymudskipper
  • 46,417
  • 11
  • 121
  • 167
-1

Try this:

lapply(enlist, function(x) { x[!is.na(x)]})

where:

enlist <- list(c(1:5,NA,NA),NA,c(NA,6:10))

This yields:

[[1]]
[1] 1 2 3 4 5

[[2]]
logical(0)

[[3]]
[1]  6  7  8  9 10
Sun Bee
  • 1,595
  • 15
  • 22