Создание нескольких матриц путаницы в R с использованием циклов

#r #loops #while-loop #confusion-matrix

#r #циклы #while-цикл #путаница-матрица

Вопрос:

Я пытаюсь создать несколько матриц путаницы из одного фрейма данных, при этом каждая матрица генерируется на основе другого условия в фрейме данных.

Итак, для приведенного ниже фрейма данных мне нужна матрица путаницы, когда Value = 1 , Value = 2 , Value =3

   observed predicted Value
       1      1      1
       0      1      1
       1      0      2
       0      0      2
       1      1      3
       0      0      3
 

и посмотрите на результаты, подобные:

 Value  Sensitivity  Specificity  PPV  NPV
1        .96            .71      .84  .95
2        .89            .63      .30  .45     
3        .88            .95      .28  .80
 

Это то, что я попробовал на воспроизводимом примере. Я пытаюсь написать цикл, который просматривает каждую строку, определяет Age = 1 , а затем извлекает значения из прогнозируемых и наблюдаемых столбцов, чтобы сгенерировать матрицу путаницы. Затем я вручную извлек значения из матрицы путаницы, чтобы записать sen, spec, ppv и npv и попытался объединить все матрицы вместе. И затем цикл начинается снова с Age = 2 .

 data(scat)
df<-scat %>% transmute(observed=ifelse(Site=="YOLA","case", "control"), predicted=ifelse(Location=="edge","case", "control"),Age)

x<-1 #evaluate at ages 1 through 5
for (i in dim(df)[1]) { #for every row in df
  while(x<6) { #loop stops at Age=5
    if(x=df$Age) {
      q<-confusionMatrix(data = df$predicted, reference = df$observed, positive = "case")
      sensitivity = q$table[1,1]/(q$table[1,1] q$table[2,1])
      specificity = q$table[2,2]/(q$table[2,2] q$table[1,2])
      ppv = q$table[1,1]/(q$table[1,1] q$table[1,2])
      npv = q$table[2,2]/(q$table[2,2] q$table[2,1])
      matrix(c(sensitivity, specificity, ppv, npv),ncol=4,byrow=TRUE)
    }
  }
  x <- x   1 #confusion matrix at next Age value
}

final<- rbind(matrix) #combine all the matrices together
 

Однако этот цикл полностью нефункциональен. Я не уверен, где ошибка.

Ответ №1:

Ваш код может быть упрощен, и желаемый результат достигается следующим образом:

 library(caret)
library(dplyr)

data(scat)

df <- scat %>% 
  transmute(observed = factor(ifelse(Site == "YOLA","case", "control")), 
            predicted = factor(ifelse(Location == "edge","case", "control")),
            Age)

final <- t(sapply(sort(unique(df$Age)), function(i) { 
  
  q <- confusionMatrix(data      = df$predicted[df$Age == i],
                       reference = df$observed[df$Age == i], 
                       positive  = "case")$table
  
  c(sensitivity = q[1, 1] / (q[1, 1]   q[2, 1]),
    specificity = q[2, 2] / (q[2, 2]   q[1, 2]),
    ppv         = q[1, 1] / (q[1, 1]   q[1, 2]),
    npv         = q[2, 2] / (q[2, 2]   q[2, 1]))
}))
 

В результате

 final
#>      sensitivity specificity        ppv       npv
#> [1,]         0.0   0.5625000 0.00000000 0.8181818
#> [2,]         0.0   1.0000000        NaN 0.8000000
#> [3,]         0.2   0.5882353 0.06666667 0.8333333
#> [4,]         0.0   0.6923077 0.00000000 0.6923077
#> [5,]         0.5   0.6400000 0.25000000 0.8421053
 

Тем не менее, приятно знать, почему ваш собственный код не работал, поэтому вот несколько вопросов, которые могут быть полезны для рассмотрения:

  1. Вам нужны факторные столбцы, а не символьные столбцы для confusionMatrix
  2. Вы увеличивали количество строк df , но вам нужна одна итерация для каждого уникального возраста, а не для каждой строки в вашем фрейме данных.
  3. Ваша строка для увеличения x происходит вне while цикла, поэтому x никогда не увеличивается, и цикл никогда не завершается, поэтому консоль просто зависает.
  4. Вы делаете if(x = df$Age) , но вам нужно == проверить равенство.
  5. Сравнивать в любом случае не имеет смысла x df$Age , потому x что имеет длину 1 и df$Age является длинным вектором.
  6. У вас есть ненужное повторение, выполняя q$table каждый раз. Вы можете просто сделать q равным q$table , чтобы сделать ваш код более читаемым и менее подверженным ошибкам.
  7. Вы вызываете matrix в конце цикла, но вы нигде не сохраняете его, поэтому весь цикл фактически ничего не делает.
  8. Вы пытаетесь rbind вызвать объект, вызванный matrix в последней строке, который не существует
  9. Отсутствие пробелов между математическими операторами, запятыми и переменными делает код менее читаемым и более сложным для отладки. Я говорю это не просто как стилистический момент; это основной источник ошибок, которые я часто вижу здесь, на SO.

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

1. Большое спасибо за ваш подробный ответ и объяснение! Я не был знаком с sapply этим, поэтому я немного почитал об этом. И я добавляю этот комментарий для всех остальных в будущем, я также хотел иметь столбец, который был помечен Age и перечислял возрасты в виде строк, поэтому я преобразовал final в dataframe, а затем сделал setDT(final, keep.rownames = "Age") .