R: Интерполировать и экстраполировать значения в фрейме данных, сопоставляя столбец другого фрейма данных

У меня есть два фрейма данных:

df1 <- data.frame(levels = c(1, 3, 5, 7, 9), 
                  values = c(2.2, 5.3, 7.9, 5.4, 8.7))

df2 <- data.frame(levels = c(1, 4, 8, 12)) # other columns not necessary

Я хочу, чтобы значения df1$ интерполировались в уровни df2$ на основе чисел в уровнях df1$. Таким образом, есть некоторая интерполяция, а также экстраполяция на уровень 12 во втором кадре данных.


person B.Quaink    schedule 13.04.2021    source источник
comment
Функция approx поможет с интерполяцией между точками, но как бы вы хотели, чтобы экстраполяция работала за пределами диапазона df1?   -  person Jon Spring    schedule 13.04.2021
comment
возможно Hmisc::approxExtrap   -  person r2evans    schedule 13.04.2021
comment
Это похоже на однострочное решение, но из документации о том, как это настроить, это не очень очевидно.   -  person B.Quaink    schedule 13.04.2021


Ответы (2)


Возможно, сделать complete на основе union из levels обоих наборов данных, а затем использовать na.approx (из zoo) с rule = 2 (для экстраполяции)

library(dplyr)
library(tidyr)
library(zoo)
df1 <- df1 %>% 
    complete(levels = union(levels, df2$levels)) %>%
    mutate(values = na.approx(values, maxgap = Inf, rule = 2))

-выход

df1
# A tibble: 8 x 2
#  levels values
#   <dbl>  <dbl>
#1      1   2.2 
#2      3   5.3 
#3      4   6.6 
#4      5   7.9 
#5      7   5.4 
#6      8   7.05
#7      9   8.7 
#8     12   8.7 
person akrun    schedule 13.04.2021
comment
Ошибка: Столбцы соединения должны присутствовать в данных. x Проблема с levels. Это показывает - person B.Quaink; 13.04.2021
comment
@B.Quaink Я проверил данные вашего примера. Оба имеют столбец levels. Не удалось получить указанную вами ошибку. Я обновил вывод, который я получаю - person akrun; 13.04.2021
comment
Да теперь работает! Любой способ сохранить вывод? Я не могу изменить df1 до %›% на новый, и он не обновит исходный df1 этим. - person B.Quaink; 13.04.2021
comment
@B.Quaink, вы можете назначить df1 <- df1 %>% . или в конце сделать -> df1 - person akrun; 13.04.2021
comment
Круто, спасибо за помощь :) - person B.Quaink; 13.04.2021

Я уверен, что это можно сжать, это какой-то код, который я написал давным-давно, который обрабатывает необходимость экстраполяции в начале/хвосте упорядоченного вектора:

# Function to interpolate / extrapolate: l_estimate => function()
l_estimate <- function(vec){
  # Function to perform-linear interpolation and return vector: 
  # .l_interp_vec => function()
  .l_interp_vec <- function(vec){
    interped_values <- 
      approx(x = vec, method = "linear", ties = "constant", n = length(vec))$y
    return(ifelse(is.na(vec), interped_values[is.na(vec)], vec))
  }
  
  # Store a vector denoting the indices of the vector that are NA: 
  # na_idx => integer vector
  na_idx <- is.na(vec)
  
  # Store a scalar of min row where x isn't NA: min_non_na => integer vector
  min_non_na <- min(which(!(na_idx)))
  
  # Store a scalar of max row where x isn't NA: max_non_na => integer vector
  max_non_na <- max(which(!(na_idx)))
  
  # Store scalar of the number of rows needed to impute prior 
  # to first NA value: ru_lower => integer vector
  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_upper => integer vector
  ru_upper <- ifelse(
    max_non_na == length(vec), 
    length(vec) - 1, 
    (length(vec) - (max_non_na + 1))
  )
  
  # Store a vector of the ramp to function: ramp_up => numeric vector 
  ramp_up <- as.numeric(
    cumsum(rep(vec[min_non_na]/(min_non_na), ru_lower))
  )
  
  # Apply the interpolation function on vector: y => numeric vector
  y <- as.numeric(.l_interp_vec(as.numeric(vec[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:
  if(length(ramp_up) >= 1 & max_non_na != length(vec)){
    # Create a vector interpolations if there are 
    # multiple NA values after the last value: lower_l_int => numeric vector
    lower_l_int <- as.numeric(
      cumsum(rep(mean(diff(c(ramp_up, y))), ru_upper+1)) + 
        as.numeric(vec[max_non_na])
      )
    
    # Store the linear interpolations in  a vector: z => numeric vector
    z <- as.numeric(c(ramp_up, y, lower_l_int))
  
  }else if(length(ramp_up) > 1 & max_non_na == length(vec)){
    
    # Store the linear interpolations in  a vector: z => numeric
    z <- as.numeric(c(ramp_up, y))
    
  }else if(min_non_na == 1 & max_non_na != length(vec)){
    
    # Create a vector interpolations if there are 
    # multiple NA values after the last value: lower_l_int => numeric vector
    lower_l_int <- as.numeric(
      cumsum(rep(mean(diff(c(ramp_up, y))), ru_upper+1)) +
        as.numeric(vec[max_non_na])
      )
    
    # Store the linear interpolations in  a vector: z => numeric vector
    z <- as.numeric(c(y, lower_l_int))
    
  }else{
    # Store the linear interpolations in  a vector: z => numeric vector
    z <- as.numeric(y)
    
  }
  # Interpolate between points in x, return new x:
  return(as.numeric(ifelse(is.na(vec), z, vec)))
}

# Apply the function on ordered data: data.frame => stdout(console)
transform(full_df[order(full_df$levels),],
     values = l_estimate(values)
)
person hello_friend    schedule 14.04.2021