Двойной цикл для заполнения фрейма данных — как исправить «недопустимую функцию при сложном назначении»

#r #loops #dataframe #for-loop

#r #циклы #фрейм данных #для цикла

Вопрос:

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

 results 2 (612 obs. 281 variables)
ID Q1000_p2000_2016 Q1893_p2039_2016 .... Q1000_p2000_2017 Q1893_p2039_2017
1      392               381                    422              351
2      432               293                    398              310
.       .                 .                      .                .
.       .                 .                      .                .
  

там, где есть 140 вопросов за 2016 год и 140 за 2017 год, каждый год вопросы имеют одно и то же имя, но в конце каждого имени переменной есть "_2016" или "_2017" , чтобы различать периоды времени.

и другой фрейм данных:

 absdiff (0 obs. 141 variables)
ID Q1000_p2000 Q1893_p2039 .... 
  

Я хочу присвоить значение в absdiff, взяв абсолютную разницу в два года, для каждого вопроса для каждого ID .

В моем состоянии я проверяю, совпадает ли номер вопроса за 2016 год (или первые несколько символов имени переменной) с номером вопроса за 2017 год в результатах2.

Если это верно, я хочу присвоить абсолютную разницу двух ответов соответствующей переменной / номеру вопроса в absdiff

Я использовал

 for (q in 2:141){
  if (substr(colnames(results2[q]),1,12) == substr(colnames(results2[q 140]),1,12)){
    for (j in 1:nrow(results2)){absdiff$substr(colnames(results2[q]),1,11) <- abs(results2[j,q] - results2[j,(q 140)])}
  }
  else 
    print("ERROR")
}
  

но я получаю это сообщение об ошибке:

Ошибка в absdiff $substr(colnames(results2[q]), 1, 11) <- abs(results2[j, : недопустимая функция при сложном назначении

Какая проблема вызывает это сообщение об ошибке? Как мне это исправить?

Ради репликации все это можно упростить до:

 ID <- c(1,2)
Q1000_p2000_2016 <- c(392,432)
Q1893_p2039_2016 <- c(381,293)
Q1000_p2000_2017 <- c(422,398)
Q1893_p2039_2017 <- c(351,310)    

results2 <- as.data.frame(cbind(ID, Q1000_p2000_2016, Q1893_p2039_2016 ,Q1000_p2000_2017, Q1893_p2039_2017 ))

absdiff <- results2[FALSE,1:3]

    for (q in 2:3){
  if (substr(colnames(results2[q]),1,12) == substr(colnames(results2[q 2]),1,12)){
    for (j in 1:nrow(results2)){absdiff$substr(colnames(results2[q]),1,11) <- abs(results2[j,q] - results2[j,(q 2)])}
  }
  else 
    print("ERROR")
}
  

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

1. Вам действительно стоит прочитать статью «Аккуратные данные» Хэдли Уикхема (google it).

Ответ №1:

Не используйте циклы, а просто векторизируйте. Получите столбцы 2016, столбцы 2017 и затем вычтите:

 col2016<-grep("_2016$",names(results2),value=TRUE)
col2017<-grep("_2017$",names(results2),value=TRUE)
absdiff<-results2[,col2017]-results2[,col2016]
#  Q1000_p2000_2017 Q1893_p2039_2017
#1               30              -30
#2              -34               17
  

Чтобы сохранить ID столбец, просто добавьте его после:

 absdiff$ID<-results2$ID
  

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

1. Есть ли способ сохранить ID переменную с помощью этого метода?

Ответ №2:

Краткие замечания по вашему коду для дальнейшего кодирования: Причина ошибки здесь такова: absdiff$substr(colnames(results2[q]),1,11) поскольку вы не можете использовать знак доллара с функцией только потому, что она возвращает строку, вы можете, однако, использовать разделительные скобки, подобные этой absdiff[substr(colnames(results2[q]),1,11)] .

Еще одна проблема с кодом заключается в том, что он absdiff изначально пуст, при вызове results2[FALSE,1:3] вы получаете имена столбцов, но не строки (если вы хотите удалить все строки FALSE ), что означает, что вы не сможете присвоить значения новому столбцу.

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

 library(dplyr)
library(reshape2)
new_resutls  <- results2 %>% 
  reshape2::melt(id.vars='ID') %>% 
  dplyr::mutate(question = substr(variable, 1, 11), 
                year = substr(variable, 13, 16))
new_resutls

#   ID         variable value    question year
# 1  1 Q1000_p2000_2016   392 Q1000_p2000 2016
# 2  2 Q1000_p2000_2016   432 Q1000_p2000 2016
# 3  1 Q1893_p2039_2016   381 Q1893_p2039 2016
# 4  2 Q1893_p2039_2016   293 Q1893_p2039 2016
# 5  1 Q1000_p2000_2017   422 Q1000_p2000 2017
# 6  2 Q1000_p2000_2017   398 Q1000_p2000 2017
# 7  1 Q1893_p2039_2017   351 Q1893_p2039 2017
# 8  2 Q1893_p2039_2017   310 Q1893_p2039 2017
  

Затем ваша проблема может быть решена следующим образом:

 new_resutls %>% 
  dplyr::group_by(ID, question) %>% 
  dplyr::summarise(absdiff = abs(sum(value*c(1, -1))))

#      ID question    absdiff
#   <dbl> <chr>         <dbl>
# 1     1 Q1000_p2000      30
# 2     1 Q1893_p2039      30
# 3     2 Q1000_p2000      34
# 4     2 Q1893_p2039      17