【问题标题】:Interpolating NA's by group using dplyr on multiple columns在多列上使用 dplyr 按组内插 NA
【发布时间】:2018-12-22 04:37:47
【问题描述】:

我有一个这样的数据框:

> head(df1)
  iso year var1 var2 var3
1 XXX 2005  165   29 2151
2 XXX 2006  160   21 2139
3 XXX 2007   NA   NA   NA
4 XXX 2008  184    9 3640
5 XXX 2009   NA   NA   NA
6 YYY 2005  206  461 8049 

我想通过前后携带最外面的非NA来替换基于它周围年份的NA的间歇年份和范围开始和结束的年份NA观察。

我为一列执行此操作的代码是:

df1 %>% 
 group_by(iso) %>%
 mutate(var1 = na.approx(var1, na.rm = FALSE, rule = 1)) %>%
 mutate(var1 = na.locf(var1, na.rm = FALSE)) %>%
 mutate(var1 = na.locf(var1, na.rm = FALSE, fromLast = TRUE))

这行得通,所以现在我想一次性对所有列执行此操作(超过 3 个,并且它们没有像我的示例中那样编号)。这是我从this 问题的答案拼凑而成的。我省略了对na.locf 的两次调用。

columnnames <- c("var1, "var2", "var3")
df1 %>%
 group_by(iso) %>%
 mutate_at(.vars = vars(columnnames), .funs = funs(na.approx(., na.rm = FALSE, rule = 1)))

这会给我一个错误和警告:

在 approx(x[!na], y[!na], xout, ...) 中出现错误: 需要至少两个非 NA 值进行插值 另外:警告信息: 在 xy.coords(x, y, setLab = FALSE) 中:强制引入的 NAs

我想我理解了这个错误,但是当我在var1 上使用第一段代码时我没有得到它。我不遵守的警告。如何将代码应用于数据框中的所有列?我还尝试将evertything 放在一个循环中,循环columnnames,但这也不起作用(它可能不是解决此问题的最佳方法)。

【问题讨论】:

    标签: r dplyr na zoo


    【解决方案1】:

    我们可以使用mutate_at。关键是在vars 参数中指定正确的列,它使用与select 函数相同的规则。因此,在这种情况下,vars(starts_with("var")) 也将起作用。

    library(dplyr)
    library(zoo)
    
    df1 %>% 
      group_by(iso) %>%
      mutate_at(vars(-iso, -year), funs(na.approx(., na.rm = FALSE, rule = 1))) %>%
      mutate_at(vars(-iso, -year), funs(na.locf(., na.rm = FALSE))) %>%
      mutate_at(vars(-iso, -year), funs(na.locf(., na.rm = FALSE, fromLast = TRUE)))
    # # A tibble: 6 x 5
    # # Groups:   iso [2]
    #   iso    year  var1  var2  var3
    #   <chr> <int> <dbl> <dbl> <dbl>
    # 1 XXX    2005   165    29 2151 
    # 2 XXX    2006   160    21 2139 
    # 3 XXX    2007   172    15 2890.
    # 4 XXX    2008   184     9 3640 
    # 5 XXX    2009   184     9 3640 
    # 6 YYY    2005   206   461 8049 
    

    数据

    df1 <- read.table(text = "  iso year var1 var2 var3
    1 XXX 2005  165   29 2151
    2 XXX 2006  160   21 2139
    3 XXX 2007   NA   NA   NA
    4 XXX 2008  184    9 3640
    5 XXX 2009   NA   NA   NA
    6 YYY 2005  206  461 8049 ",
                     header = TRUE, stringsAsFactors = FALSE)
    

    【讨论】:

    • 为什么你使用了三个不同的mutate_at?能否添加一些 cmets 以便更好地理解?
    【解决方案2】:

    您可以使用mutate_at 重写您的代码,以便一次性完成转换:

    library(dplyr)
    library(zoo)
    
    
    df %>% 
      group_by(iso) %>%
      mutate_at(vars(starts_with("var")), 
                funs(na.locf(na.locf(na.approx(., na.rm = FALSE, rule = 1),na.rm=FALSE),
                                                                  fromLast=TRUE)))
    
    
    # # A tibble: 6 x 5
    # # Groups: iso [2]
    # iso    year  var1   var2  var3
    # <chr> <int> <dbl>  <dbl> <dbl>
    # 1 XXX    2005   165  29.0   2151
    # 2 XXX    2006   160  21.0   2139
    # 3 XXX    2007   172  15.0   2890
    # 4 XXX    2008   184   9.00  3640
    # 5 XXX    2009   184   9.00  3640
    # 6 YYY    2005   206 461     8049
    # 
    

    数据:

    df <- read.table(text=
    "iso year var1 var2 var3
    1 XXX 2005  165   29 2151
    2 XXX 2006  160   21 2139
    3 XXX 2007   NA   NA   NA
    4 XXX 2008  184    9 3640
    5 XXX 2009   NA   NA   NA
    6 YYY 2005  206  461 8049",
    header = TRUE, stringsAsFactors = FALSE)
    

    【讨论】:

    • @Moody_Mudskipper 没问题。实际上,这让我有机会纠正mutate_at:-)
    • 如果我不清楚,我很抱歉,但我的实际列名不是var1var2 等,所以我不能像你建议的那样引用它们。使用 @www 的 var 引用和您的函数嵌套使其工作。谢谢!
    【解决方案3】:

    这是一个基本解决方案:

    ave(df,df$iso, FUN =function(y){
      if(nrow(y) > 1) y[3:5] <- lapply(y[3:5], function(x) approx(y$year,x,y$year,rule=2)$y)
      y
    })
    
    #   iso year var1 var2   var3
    # 1 XXX 2005  165   29 2151.0
    # 2 XXX 2006  160   21 2139.0
    # 3 XXX 2007  172   15 2889.5
    # 4 XXX 2008  184    9 3640.0
    # 5 XXX 2009  184    9 3640.0
    # 6 YYY 2005  206  461 8049.0
    

    【讨论】:

      【解决方案4】:

      na.approxmethod = "constant"(与na.locf 相同)和rule = 2 一起使用(意味着将最接近的值扩展到前导和尾随NA)。如果您希望对 NA 进行线性插值,请删除 method="constant" 参数。

      df1 %>%
        group_by(iso) %>%
        mutate_at(vars(-iso), funs(na.approx(., method = "constant", rule = 2))) %>%
        ungroup
      

      给予:

      # A tibble: 6 x 5
        iso    year  var1  var2  var3
        <fct> <dbl> <dbl> <dbl> <dbl>
      1 XXX    2005   165    29  2151
      2 XXX    2006   160    21  2139
      3 XXX    2007   160    21  2139
      4 XXX    2008   184     9  3640
      5 XXX    2009   184     9  3640
      6 YYY    2005   206   461  8049
      

      注意

      df1 的可重现形式是:

      df1 <- 
      structure(list(iso = structure(c(1L, 1L, 1L, 1L, 1L, 2L), .Label = c("XXX", 
      "YYY"), class = "factor"), year = c(2005L, 2006L, 2007L, 2008L, 
      2009L, 2005L), var1 = c(165L, 160L, NA, 184L, NA, 206L), var2 = c(29L, 
      21L, NA, 9L, NA, 461L), var3 = c(2151L, 2139L, NA, 3640L, NA, 
      8049L)), class = "data.frame", row.names = c("1", "2", "3", "4", 
      "5", "6"))
      

      【讨论】:

      • 因此,如果我理解正确,这会将 2007 年的数据插入 XXX 作为 2006 年和 2008 年的平均值,但将 2008 年的数据应用于 2009 年?
      • 没有。正如method="constant" 的答案中所解释的,它的工作方式类似于na.locf。它用最近的先前非 NA 填充 NA,就像 na.locf 一样,但它也用第一个非 NA 填充领先的 NA。我已将输出添加到答案中。查看?na.approxmethodrule 参数查看?approx
      • 好的,谢谢。这不是我在这里寻找的,但很高兴知道。
      • 该问题使用na.locf 表明该功能是您想要使用的。但是,如果您想要对周围点进行线性插值,请删除 method = "constant" 参数,因为 na.approx 的默认值是采用线性插值。
      猜你喜欢
      • 1970-01-01
      • 2016-08-02
      • 2016-04-01
      • 1970-01-01
      • 2017-02-23
      相关资源
      最近更新 更多