插值NA值

时间:2011-08-25 10:29:44

标签: r interpolation

我有两组与时间无关的样本。我想合并它们并计算缺失值 在我没有两者价值的时代。简化示例:

A <- cbind(time=c(10, 20, 30, 40, 50, 60, 70, 80, 90, 100),
           Avalue=c(1, 2, 3, 2, 1, 2, 3, 2, 1, 2))
B <- cbind(time=c(15, 30, 45, 60), Bvalue=c(100, 200, 300, 400))
C <- merge(A,B, all=TRUE)

   time Avalue Bvalue
1    10      1     NA
2    15     NA    100
3    20      2     NA
4    30      3    200
5    40      2     NA
6    45     NA    300
7    50      1     NA
8    60      2    400
9    70      3     NA
10   80      2     NA
11   90      1     NA
12  100      2     NA

通过假设每个样本之间的线性变化,可以计算缺失的NA值。 直觉上很容易看出时间15和45的A值应该是1.5。但是对B的正确计算 例如,在时间20将是

100 +(20 - 15)*(200 - 100)/(30 - 15)

等于133.33333。 第一个括号是估计时间和最后一个可用样本之间的时间。 第二个括号是最近的样本之间的差异。 第三个括号是最近样本之间的时间。

如何使用R来计算NA值?

3 个答案:

答案 0 :(得分:14)

使用zoo包:

library(zoo)
Cz <- zoo(C)
index(Cz) <- Cz[,1]
Cz_approx <- na.approx(Cz)

答案 1 :(得分:3)

以统计方式执行此操作且仍然获得有效置信区间的正确方法是使用多重插补。请参阅鲁宾的经典book,并且有一个很好的R package for this (mi)

答案 2 :(得分:0)

一个丑陋且可能效率低下的Base R解决方案:

# Data provided:
A <- cbind(time=c(10, 20, 30, 40, 50, 60, 70, 80, 90, 100),
           Avalue=c(1, 2, 3, 2, 1, 2, 3, 2, 1, 2))
B <- cbind(time=c(15, 30, 45, 60), Bvalue=c(100, 200, 300, 400))
C <- merge(A,B, all=TRUE)

# Scalar valued at the minimum time difference: -> min_time_diff

min_time_diff <- min(diff(C$time))

# Adjust frequency of the series to hold all steps in range: -> df

df <- merge(C, 
            data.frame(time = seq(min_time_diff, 
                                 max(C$time), 
                                 by = min_time_diff)),
           by = "time",
           all = TRUE)



# Linear interpolation function handling ties,
# returns interpolated vector the same length 
# a the input vector: -> vector

l_interp_vec <- function(na_vec){

  approx(x = na_vec,

         method = "linear",

         ties = "constant",

         n = length(na_vec))$y

}

# Applied to a dataframe, replacing NA values
# in each of the numeric vectors, 
# with interpolated values. 
# input is dataframe: -> dataframe()

interped_df <- data.frame(lapply(df, function(x){

      if(is.numeric(x)){

        # Store a scalar of min row where x isn't NA: -> min_non_na

        min_non_na <- min(which(!(is.na(x))))

        # Store a scalar of max row where x isn't NA: -> max_non_na

        max_non_na <- max(which(!(is.na(x))))

        # Store scalar of the number of rows needed to impute prior 
        # to first NA value: -> ru_lower

        ru_lower <- ifelse(min_non_na > 1, min_non_na - 1, min_non_na)

        # Store scalar of the number of rows needed to impute after
        # the last non-NA value: -> ru_lower

        ru_upper <- ifelse(max_non_na == length(x), 

                           length(x) - 1, 

                           (length(x) - (max_non_na + 1)))

        # Store a vector of the ramp to function: -> l_ramp_up: 

        ramp_up <- as.numeric(
          cumsum(rep(x[min_non_na]/(min_non_na), ru_lower))
          )

        # Apply the interpolation function on vector "x": -> y

        y <- as.numeric(l_interp_vec(as.numeric(x[min_non_na:max_non_na])))

        # Create a vector that combines the ramp_up vector 
        # and y if the first NA is at row 1: -> z

        if(length(ramp_up) > 1 & max_non_na != length(x)){

          # Create a vector interpolations if there are 
          # multiple NA values after the last value: -> lower_l_int

          lower_l_int <- as.numeric(cumsum(rep(mean(diff(c(ramp_up, y))),
                                               ru_upper+1)) +
                                  as.numeric(x[max_non_na]))

          # Store the linear interpolations in  a vector: -> z

          z <- as.numeric(c(ramp_up, y, lower_l_int))

        }else if(length(ramp_up) > 1 & max_non_na == length(x)){

          # Store the linear interpolations in  a vector: -> z

          z <- as.numeric(c(ramp_up, y))

        }else if(min_non_na == 1 & max_non_na != length(x)){

          # Create a vector interpolations if there are 
          # multiple NA values after the last value: -> lower_l_int

          lower_l_int <- as.numeric(cumsum(rep(mean(diff(c(ramp_up, y))),
                                               ru_upper+1)) +
                                  as.numeric(x[max_non_na]))


          # Store the linear interpolations in  a vector: -> z

          z <- as.numeric(c(y, lower_l_int))

        }else{

          # Store the linear interpolations in  a vector: -> z

          z <- as.numeric(y)

        }

        # Interpolate between points in x, return new x:

        return(as.numeric(ifelse(is.na(x), z, x)))

      }else{

        x

      }

    }

  )

)

# Subset interped df to only contain 
# the time values in C, store a data frame: -> int_df_subset

int_df_subset <- interped_df[interped_df$time %in% C$time,]