цикл в r studio с varlist

#r #for-loop #dplyr

#r #для цикла #dplyr

Вопрос:

Я работаю над базой данных панели с фирмами и годами. Я интерполирую значения для заполнения NA, если для интерполяции достаточно наблюдений ( um(!is.na(var))>1 требуется как минимум два наблюдения) и сохраняю предыдущие значения, если переменной недостаточно значений для интерполяции ( 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,2,3,3),
                   Date=c(1991,20000,1991,2000,1991,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, na_interpolation(var),var)) 
  } 
I get the error "Error in for (var in vars) { : invalid for() loop sequence"

  

Комментарии:

1. Я думаю, что ваш код может быть неправильным, нет объекта data_interp

2. это все еще были данные. Я пропустил строку кода data_interp <- data

3. теперь нет столбца ID

4. Где это na_interpolation определено?

Ответ №1:

Я не думаю, что вам нужен 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 может быть плохо: он удаляет class ( ifelse(TRUE,Sys.time(),Sys.time()) ) и не выполняет принудительное исполнение для возвращаемого значения type ( 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 это правильно, поскольку у вас не более 2 строк на ID , и ни одна из ваших данных не является 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