在varlist上的r studio上循环

问题描述

我正在与公司和年份建立一个面板数据库。如果有足够的观测值要进行插值(um(!is.na(var))>1至少需要两个观测值),我将对这些值进行插值以填充NA,如果变量没有足够的值进行插值(ifelse(sum(!is.na(var))>1,na_interpolation(var),var)),我将保留先前的值,以免失去价值。


set.seed(1)
data <- data.frame(Output_manufacturing = runif(6),Output_agriculture   = runif(6),ID=c(1,1,2,3,3),Date=c(1991,20000,1991,2000,2000))

vars <- ls(data,pattern="Output_*")

for (var in vars) {
    data<- data %>%
      group_by(ID) %>% 
      mutate(!!sym(paste0(var,"_interpolated")) := ifelse(sum(!is.na(var))>1,var)) 
  } 
I get the error "Error in for (var in vars) { : invalid for() loop sequence"

解决方法

我认为您不需要for循环。我将使用mtcars进行演示,并对na_interpolation的功能做一个幼稚的假设(这实际上与我的观点相切):

library(dplyr)
mt <- select(mtcars,cyl,disp,hp,drat) %>%
  group_by(cyl) %>%
  slice(1:3) %>%
  ungroup()
mt$disp[c(1,7:8)] <- mt$hp[c(2:3)] <- NA
mt
# # A tibble: 9 x 4
#     cyl  disp    hp  drat
#   <dbl> <dbl> <dbl> <dbl>
# 1     4   NA     93  3.85
# 2     4  147.    NA  3.69
# 3     4  141.    NA  3.92
# 4     6  160    110  3.9 
# 5     6  160    110  3.9 
# 6     6  258    110  3.08
# 7     8   NA    175  3.15
# 8     8   NA    245  3.21
# 9     8  276.   180  3.07

na_interpolation <- function(x) mean(x,na.rm = TRUE)

现在是真实的作品:

vars <- c("disp","hp")
mt %>%
  group_by(cyl) %>%
  mutate_at(vars,list(
    interpolated = ~ if (sum(is.na(.)) > 1) na_interpolation(.) else .
  )) %>%
  ungroup()
# # A tibble: 9 x 6
#     cyl  disp    hp  drat disp_interpolated hp_interpolated
#   <dbl> <dbl> <dbl> <dbl>             <dbl>           <dbl>
# 1     4   NA     93  3.85               NA               93
# 2     4  147.    NA  3.69              147.              93
# 3     4  141.    NA  3.92              141.              93
# 4     6  160    110  3.9               160              110
# 5     6  160    110  3.9               160              110
# 6     6  258    110  3.08              258              110
# 7     8   NA    175  3.15              276.             175
# 8     8   NA    245  3.21              276.             245
# 9     8  276.   180  3.07              276.             180

观点:具有摘要统计信息的ifelse不是正确的工具,出于以下几个原因:

  1. base::ifelse 可能很糟糕:它会丢弃类(ifelse(TRUE,Sys.time(),Sys.time())),并且不会对返回值类型(ifelse(TRUE,1L,"2"))进行强制执行;都是动机dplyr::if_else(和data.table::fifelse);
  2. 声明性:如果您确信条件始终为1,请使用if / else进行声明;如果您看到有关the condition has length > 1的警告,则您的假设不正确,这意味着关于事物状态的其他一些假设可能不正确; (也许不会在这里发生,但这是一种很好的防御性编程实践)。

这表明您的代码(没有for循环)可能是:

vars <- grep("^Output_",names(data),value = TRUE)
data %>%
  group_by(ID) %>%
  mutate_at(vars,list(
    interpolated = ~ if (sum(is.na(.)) > 1) na_interpolation(.) else .
  )) %>%
  ungroup()
# A tibble: 6 x 6
#   Output_manufacturing Output_agriculture    ID  Date Output_manufacturing_interpolated Output_agriculture_interpolated
#                  <dbl>              <dbl> <dbl> <dbl>                             <dbl>                           <dbl>
# 1                0.266             0.945      1  1991                             0.266                          0.945 
# 2                0.372             0.661      1 20000                             0.372                          0.661 
# 3                0.573             0.629      2  1991                             0.573                          0.629 
# 4                0.908             0.0618     2  2000                             0.908                          0.0618
# 5                0.202             0.206      3  1991                             0.202                          0.206 
# 6                0.898             0.177      3  2000                             0.898                          0.177 

(由此尚不清楚na_interpolation是否会做正确的事,因为每个ID的行数不超过2行,并且您的数据都不是NA,但我认为这无关紧要。代码仍然可以正常工作。)


已更新,以反映mutate_at取代了mutate(across(...),...)

data %>%
  group_by(ID) %>%
  mutate(across(vars,list(
    interpolated = ~ if (sum(is.na(.)) > 1) na_interpolate(.) else . 
  ))) %>%
  ungroup()
# A tibble: 6 x 6
#   Output_manufacturing Output_agriculture    ID  Date Output_manufacturing_interpolated Output_agriculture_interpolated
#                  <dbl>              <dbl> <dbl> <dbl>                             <dbl>                           <dbl>
# 1                0.266             0.945      1  1991                             0.266                          0.945 
# 2                0.372             0.661      1 20000                             0.372                          0.661 
# 3                0.573             0.629      2  1991                             0.573                          0.629 
# 4                0.908             0.0618     2  2000                             0.908                          0.0618
# 5                0.202             0.206      3  1991                             0.202                          0.206 
# 6                0.898             0.177      3  2000                             0.898                          0.177