fill column with previous column if NA

Viewed 1046

I have a data frame like this

df <- data.frame(v1 = 10:14, v2 = c(NA, 1, NA, 3, 6), v3 = c(1, NA, NA, 9, 4))

  v1 v2 v3
1 10 NA  1
2 11  1 NA
3 12 NA NA
4 13  3  9
5 14  6  4

I now want to fill the NAs with the value of the previous column, so it looks like this:

  v1 v2 v3
1 10 10  1
2 11  1  1
3 12 12 12
4 13  3  9
5 14  6  4

I know how to do this manually, like this:

df$v2 <- ifelse(is.na(df$v2), df$v1, df$v2)

How can I automate this for a full data frame with many columns?

5 Answers

You can do this with fill from tidyr:

library(dplyr)
library(tidyr)

data.frame(t(df)) %>%
  fill(., names(.)) %>%
  t()

Result:

   v1 v2 v3
X1 10 10  1
X2 11  1  1
X3 12 12 12
X4 13  3  9
X5 14  6  4

Note:

Basically, I transposed df, filled every column downward, then transposed it back to the original orientation

for (i in 2:ncol(df))
  df[,i] = ifelse(is.na(df[,i]), df[,i-1],df[,i])

This will propagate values across streaks of NA columns. If you don't want this, just reverse the order of the indexes in the for loop declaration.

Another option using Reduce with ifelse:

df[] <- Reduce(function(x, y) ifelse(is.na(y), x, y), df, accumulate = TRUE)

df
#  v1 v2 v3
#1 10 10  1
#2 11  1  1
#3 12 12 12
#4 13  3  9
#5 14  6  4

You could use apply but note that the output will be a matrix

t(apply(df, 1, function(x){
    replace(x, is.na(x), x[cumsum(!is.na(x))][is.na(x)])
}))
#     v1 v2 v3
#[1,] 10 10  1
#[2,] 11  1  1
#[3,] 12 12 12
#[4,] 13  3  9
#[5,] 14  6  4

By using zoo na.locf

data.frame(t(apply(df,1,function(x) na.locf(x))))
  v1 v2 v3
1 10 10  1
2 11  1  1
3 12 12 12
4 13  3  9
5 14  6  4
Related