#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
Тем не менее, приятно знать, почему ваш собственный код не работал, поэтому вот несколько вопросов, которые могут быть полезны для рассмотрения:
- Вам нужны факторные столбцы, а не символьные столбцы для
confusionMatrix
- Вы увеличивали количество строк
df
, но вам нужна одна итерация для каждого уникального возраста, а не для каждой строки в вашем фрейме данных. - Ваша строка для увеличения
x
происходит внеwhile
цикла, поэтомуx
никогда не увеличивается, и цикл никогда не завершается, поэтому консоль просто зависает. - Вы делаете
if(x = df$Age)
, но вам нужно==
проверить равенство. - Сравнивать в любом случае не имеет смысла
x
df$Age
, потомуx
что имеет длину 1 иdf$Age
является длинным вектором. - У вас есть ненужное повторение, выполняя
q$table
каждый раз. Вы можете просто сделатьq
равнымq$table
, чтобы сделать ваш код более читаемым и менее подверженным ошибкам. - Вы вызываете
matrix
в конце цикла, но вы нигде не сохраняете его, поэтому весь цикл фактически ничего не делает. - Вы пытаетесь
rbind
вызвать объект, вызванныйmatrix
в последней строке, который не существует - Отсутствие пробелов между математическими операторами, запятыми и переменными делает код менее читаемым и более сложным для отладки. Я говорю это не просто как стилистический момент; это основной источник ошибок, которые я часто вижу здесь, на SO.
Комментарии:
1. Большое спасибо за ваш подробный ответ и объяснение! Я не был знаком с
sapply
этим, поэтому я немного почитал об этом. И я добавляю этот комментарий для всех остальных в будущем, я также хотел иметь столбец, который был помеченAge
и перечислял возрасты в виде строк, поэтому я преобразовалfinal
в dataframe, а затем сделалsetDT(final, keep.rownames = "Age")
.