I estou interessado em calcular a área sob a curva (AUC), ou estatística c, manualmente para um modelo de regressão logística binária.

Por exemplo, em o conjunto de dados de validação, tenho o valor verdadeiro para a variável dependente, retenção (1 = retido; 0 = não retido), bem como um estado de retenção previsto para cada observação gerada por minha análise de regressão usando um modelo que foi construído usando o treinamento definido (varia de 0 a 1).

Minha ideia inicial foi identificar o número “correto” de classificações do modelo e simplesmente dividir o número de observações “corretas” pelo número total de observações para calcular a estatística c. Por “correto”, se o verdadeiro status de retenção de uma observação = 1 e o status de retenção previsto for> 0,5, então essa é uma classificação “correta”. Além disso, se o verdadeiro status de retenção de uma observação = 0 e o status de retenção previsto for < 0,5, então essa também é uma classificação “correta”. Presumo que um “empate” ocorreria quando o valor previsto = 0,5, mas esse fenômeno não ocorre em meu conjunto de dados de validação. Por outro lado, as classificações “incorretas” seriam se o verdadeiro status de retenção de uma observação = 1 e o status de retenção previsto fosse < 0,5 ou se o verdadeiro status de retenção para um resultado = 0 e o status de retenção previsto é> 0,5. Estou ciente de TP, FP, FN, TN, mas não sei como calcular a estatística c dada esta informação.

Resposta

Eu recomendaria o & artigo de McNeil de 1982 de Hanley O significado e uso da área sob uma característica operacional do receptor (ROC ) curva .

Exemplo

Eles têm a seguinte tabela de status da doença e resultado do teste (correspondendo, por exemplo, ao risco estimado de um modelo logístico). O primeiro número à direita é o número de pacientes com estado de doença verdadeiro normal e o segundo número é o número de pacientes com estado de doença verdadeiro anormal:

(1) Definitivamente normal: 33/3
(2) Provavelmente normal: 6/2
(3) Questionável: 6/2
(4) Provavelmente anormal: 11/11
(5) Definitivamente anormal: 2/33

Portanto, há um total de 58 pacientes normais e 51 anormais. Vemos que quando o preditor é 1, Definitivamente normal, o paciente geralmente é normal (verdadeiro para 33 dos 36 pacientes), e quando é 5, Definitivamente anormal, os pacientes geralmente são anormais (verdadeiro para 33 dos 35 pacientes), então o preditor faz sentido. Mas como devemos julgar um paciente com pontuação 2, 3 ou 4? O que definimos nosso ponto de corte para julgar um paciente como anormal ou normal determina a sensibilidade e especificidade do teste resultante.

Sensibilidade e especificidade

Podemos calcular o estimado sensibilidade e especificidade para diferentes pontos de corte. (Vou escrever apenas sensibilidade e especificidade a partir de agora, deixando a natureza estimada dos valores estar implícita.)

Se escolhermos nosso corte para classificarmos todos os pacientes como anormais, não importa o que seus resultados de teste digam (ou seja, escolhemos o ponto de corte 1+), obteremos uma sensibilidade de 51/51 = 1. A especificidade será 0/58 = 0. Não parece tão bom.

Ok, então vamos escolher um corte menos rígido. Só classificamos os pacientes como anormais se eles tiverem um resultado de teste de 2 ou superior. Em seguida, perdemos 3 pacientes anormais e temos uma sensibilidade de 48/51 = 0,94. Mas temos uma especificidade muito maior, de 33/58 = 0,57.

Podemos agora continuar, escolhendo vários pontos de corte (3, 4, 5,> 5). (No último caso, não classificaremos quaisquer pacientes como anormais, mesmo se eles tiverem a pontuação de teste mais alta possível de 5.)

A curva ROC

Se fizermos isso para todos os pontos de corte possíveis e representarmos a sensibilidade em relação a 1 menos a especificidade, obteremos a curva ROC. Podemos usar o seguinte código R:

 # Data norm = rep(1:5, times=c(33,6,6,11,2)) abnorm = rep(1:5, times=c(3,2,2,11,33)) testres = c(abnorm,norm) truestat = c(rep(1,length(abnorm)), rep(0,length(norm))) # Summary table (Table I in the paper) ( tab=as.matrix(table(truestat, testres)) )  

O resultado é:

  testres truestat 1 2 3 4 5 0 33 6 6 11 2 1 3 2 2 11 33  

Podemos calcular várias estatísticas:

 ( tot=colSums(tab) ) # Number of patients w/ each test result ( truepos=unname(rev(cumsum(rev(tab[2,])))) ) # Number of true positives ( falsepos=unname(rev(cumsum(rev(tab[1,])))) ) # Number of false positives ( totpos=sum(tab[2,]) ) # The total number of positives (one number) ( totneg=sum(tab[1,]) ) # The total number of negatives (one number) (sens=truepos/totpos) # Sensitivity (fraction true positives) (omspec=falsepos/totneg) # 1 − specificity (false positives) sens=c(sens,0); omspec=c(omspec,0) # Numbers when we classify all as normal  

E usando isso, podemos traçar a curva ROC (estimada):

 plot(omspec, sens, type="b", xlim=c(0,1), ylim=c(0,1), lwd=2, xlab="1 − specificity", ylab="Sensitivity") # perhaps with xaxs="i" grid() abline(0,1, col="red", lty=2)  

Curva AUC

Calculando manualmente o AUC

Podemos calcular facilmente a área sob a curva ROC, usando a fórmula para a área de um trapézio:

 height = (sens[-1]+sens[-length(sens)])/2 width = -diff(omspec) # = diff(rev(omspec)) sum(height*width)  

O resultado é 0,8931711.

Uma medida de concordância

A AUC também pode ser vista como uma medida de concordância.Se tomarmos todos os pares possíveis de pacientes em que um é normal e o outro é anormal, podemos calcular a frequência com que é o anormal que tem o resultado de teste mais alto (de “aparência mais anormal”) (se eles têm o mesmo valor, contamos isso como meia vitória):

 o = outer(abnorm, norm, "-") mean((o>0) + .5*(o==0))  

A resposta é novamente 0,8931711, a área sob a curva ROC. Esse sempre será o caso.

Uma visão gráfica da concordância

Como apontado por Harrell em sua resposta, isso também tem uma interpretação gráfica. Vamos representar graficamente a pontuação do teste (estimativa de risco) no eixo y e o estado real da doença no eixo x (aqui com algum tremor, para mostrar pontos sobrepostos):

 plot(jitter(truestat,.2), jitter(testres,.8), las=1, xlab="True disease status", ylab="Test score")  

Gráfico de dispersão da pontuação de risco contra doença verdadeira status.

Vamos agora traçar uma linha entre cada ponto à esquerda (um paciente normal) e cada ponto à direita (um paciente anormal). A proporção de linhas com uma inclinação positiva (ou seja, a proporção de pares concordantes ) é o índice de concordância (linhas planas contam como ‘50% de concordância’).

É um pouco difícil visualizar as linhas reais para este exemplo, devido ao número de empates (pontuação de risco igual), mas com algum jittering e transparência podemos obter um gráfico razoável:

 d = cbind(x_norm=0, x_abnorm=1, expand.grid(y_norm=norm, y_abnorm=abnorm)) library(ggplot2) ggplot(d, aes(x=x_norm, xend=x_abnorm, y=y_norm, yend=y_abnorm)) + geom_segment(colour="#ff000006", position=position_jitter(width=0, height=.1)) + xlab("True disease status") + ylab("Test\nscore") + theme_light() + theme(axis.title.y=element_text(angle=0))  

Gráfico de dispersão da pontuação de risco em relação ao estado de doença real, com linhas entre todos os pares de observação possíveis.

Vemos que a maioria das linhas se inclina para cima, então o índice de concordância será alto. Também vemos a contribuição para o índice de cada tipo de par de observação. A maior parte vem de pacientes normais com uma pontuação de risco de 1 emparelhado com pacientes anormais com uma pontuação de risco de 5 (1-5 pares), mas muitos também vêm de 1-4 pares e 4-5 pares. E é muito fácil calcular o índice de concordância real com base na definição do declive:

 d = transform(d, slope=(y_norm-y_abnorm)/(x_norm-x_abnorm)) mean((d$slope > 0) + .5*(d$slope==0))  

A resposta é novamente 0,8931711, ou seja, a AUC.

O teste de Wilcoxon-Mann-Whitney

Há uma conexão estreita entre a medida de concordância e o teste de Wilcoxon-Mann-Whitney teste. Na verdade, o último testa se a probabilidade de concordância (ou seja, que é o paciente anormal em um par aleatório normal-anormal que terá o resultado de teste mais “anormal”) é exatamente 0,5. E sua estatística de teste é apenas uma transformação simples da probabilidade de concordância estimada:

 > ( wi = wilcox.test(abnorm,norm) ) Wilcoxon rank sum test with continuity correction data: abnorm and norm W = 2642, p-value = 1.944e-13 alternative hypothesis: true location shift is not equal to 0  

A estatística de teste (W = 2642) conta o número de pares concordantes. Se dividirmos pelo número de pares possíveis, obtemos um número familiar:

 w = wi$statistic w/(length(abnorm)*length(norm))  

Sim, é 0,8931711, a área sob a curva ROC.

Maneiras mais fáceis de calcular a AUC (em R)

Mas vamos tornar a vida mais fácil para nós mesmos. Existem vários pacotes que calculam o AUC para nós automaticamente.

O pacote Epi

O pacote Epi cria uma bela curva ROC com vários estatísticas (incluindo a AUC) incorporadas:

 library(Epi) ROC(testres, truestat) # also try adding plot="sp"  

Curva ROC do pacote Epi

O pacote pROC

Eu também gosto do pacote pROC, pois pode suavizar a estimativa ROC (e calcular uma estimativa AUC com base no ROC suavizado):

Curva ROC (não suavizada e suavizada) do pacote pROC

(A linha vermelha é o ROC original e a linha preta é o ROC suavizado. Observe também a proporção padrão de 1: 1. Faz sentido usar isso, já que a sensibilidade e a especificidade têm um valor 0-1 intervalo.)

O AUC estimado do ROC suavizado é 0,9107, semelhante, mas ligeiramente maior do que, o AUC do ROC não suavizado (se você olhar um na figura, você pode ver facilmente por que é maior). (Embora realmente tenhamos poucos valores de resultado de teste distintos possíveis para calcular um AUC suave).

O pacote rms

Pacote rms de Harrell pode calcular várias estatísticas de concordância relacionadas usando a função rcorr.cens(). O C Index em sua saída é o AUC:

 > library(rms) > rcorr.cens(testres,truestat)[1] C Index 0.8931711  

O pacote caTools

Finalmente, temos o pacote caTools e sua função colAUC(). Ele tem algumas vantagens sobre outros pacotes (principalmente velocidade e capacidade de trabalhar com dados multidimensionais – consulte ?colAUC) que podem às vezes ser úteis.Mas é claro que dá a mesma resposta que calculamos continuamente:

 library(caTools) colAUC(testres, truestat, plotROC=TRUE) [,1] 0 vs. 1 0.8931711  

Curva ROC do pacote caTools

Palavras finais

Muitas pessoas parecem pensar que o AUC nos diz o quão bom um teste é. E algumas pessoas pensam que AUC é a probabilidade de que o teste classifique corretamente um paciente. É não . Como você pode ver no exemplo e nos cálculos acima, o AUC nos diz algo sobre uma família de testes, um teste para cada corte possível.

E o AUC é calculado com base em cortes que ninguém usaria na prática. Por que devemos nos preocupar com a sensibilidade e especificidade dos valores de corte “sem sentido”? Ainda assim, é nisso que a AUC é (parcialmente) baseada. (Claro, se o AUC for muito próximo a 1, quase todos os testes possíveis terão grande poder discriminatório e todos ficaríamos muito felizes.)

O aleatório normal – a interpretação de pares anormais da AUC é boa (e pode ser estendida, por exemplo, para modelos de sobrevivência, onde vemos se é a pessoa com o maior risco (relativo) que morre mais cedo). Mas ninguém o usaria na prática. É um caso raro onde alguém sabe que tem uma saudável e uma doente, não sabe qual pessoa é a doente e deve decidir qual deles tratar. (Em qualquer caso, a decisão é fácil; trate aquele com o risco estimado mais alto.)

Portanto, acho que estudar a curva ROC real será mais útil do que apenas olhar para a medida sumária da AUC. E se você usar o ROC junto com (estimativas dos) custos de falsos positivos e falsos negativos, junto com as taxas básicas do que você está estudando, você pode chegar a algum lugar.

Observe também que a AUC mede apenas discriminação , não calibração. Ou seja, mede se você pode discriminar entre duas pessoas (uma doente e uma saudável), com base na pontuação de risco. Para isso, ele considera apenas os valores de risco relativo (ou classificações, se preferir, conforme a interpretação do teste Wilcoxon-Mann-Whitney), não os absolutos, que você deveria ter interesse. Por exemplo, se você dividir cada estimativa de risco de seu modelo logístico por 2, obterá exatamente o mesmo AUC (e ROC).

Ao avaliar um modelo de risco, calibração também é muito importante. Para examinar isso, você observará todos os pacientes com uma pontuação de risco de cerca de, por exemplo, 0,7, e verá se aproximadamente 70% deles realmente estavam doentes. Faça isso para cada pontuação de risco possível (possivelmente usando algum tipo de suavização / regressão local). Trace os resultados e você obterá uma medida gráfica de calibração .

Se tiver um modelo com ambos boa calibração e boa discriminação, você comece a ter um bom modelo. 🙂

Comentários

  • Obrigado, @Karl Ove Hufthammer, esta é a resposta mais completa que já recebi. Agradeço especialmente sua seção ” Palavras finais “. Excelente trabalho! Obrigado novamente!
  • Muito obrigado por esta resposta detalhada. Estou trabalhando com um conjunto de dados onde Epi :: ROC () v2.2.6 está convencido de que a AUC é 1,62 (não, não é um estudo mentalista), mas de acordo com o ROC, acredito muito mais no 0,56 que o código acima resulta in.
  • Acho que há um pequeno erro em sens=c(sens,0); omspec=c(omspec,0), que deveria ‘ ser sens=c(0, sens); omspec=c(0, omspec)? Ele é plotado corretamente com o 0 inicial, mas não da maneira que está atualmente na resposta.
  • Não, a definição atual é, AFAICS, correto, @steveb, e resulta em um gráfico correto. Acho que o que talvez seja confuso é que a curva ROC é desenhada da direita para a esquerda (ou seja, do canto superior direito para o inferior esquerdo), não da esquerda para a certo , como a maioria dos enredos. Isso é apenas o resultado de como eu defini as variáveis; poderia muito bem tê-lo plotado da esquerda para a direita (revertendo sens e omspec antes de plotar).

Resposta

Dê uma olhada nesta pergunta: Compreendendo a curva ROC

Veja como construir uma curva ROC (a partir dessa pergunta):

Desenhando a curva ROC

dado um conjunto de dados processado por seu classificador de classificação

  • exemplos de teste de classificação em pontuação decrescente
  • começam em $ (0, 0) $
  • para cada exemplo $ x $ (no ordem decrescente)
    • se $ x $ for positivo, mova $ 1 / \ text {pos} $ para cima
    • se $ x $ for negativo, mova $ 1 / \ text {neg} $ para a direita

onde $ \ text {pos} $ e $ \ text {neg} $ são as frações dos exemplos positivos e negativos, respectivamente.

Você pode usar esta ideia para calcular manualmente AUC ROC usando o seguinte algoritmo:

auc = 0.0 height = 0.0 for each training example x_i, y_i if y_i = 1.0: height = height + tpr else auc = auc + height * fpr return auc 

Esta bela imagem animada em GIF deve ilustrar isso clarificador de processo

construindo a curva

Comentários

  • Obrigado @Alexey Grigorev, este é um ótimo visual e provavelmente será útil no futuro! +1
  • Poderia explicar um pouco sobre ” frações de exemplos positivos e negativos “, você quer dizer o menor valor unitário de dois eixos?
  • @Allan Ruin: pos aqui significa o número de dados positivos. Digamos que você tenha 20 pontos de dados, em que 11 pontos são 1. Portanto, ao desenhar o gráfico, temos um retângulo 11×9 (altura x largura). Alexey Grigorev dimensionou, mas deixe como está ‘ se desejar. Agora, basta mover 1 no gráfico a cada etapa.

Resposta

A postagem de Karl tem muitos de informações excelentes. Mas ainda não vi nos últimos 20 anos um exemplo de curva ROC que mudou o pensamento de qualquer pessoa para uma boa direção. O único valor de uma curva ROC na minha humilde opinião é que sua área equivale a uma probabilidade de concordância muito útil. A própria curva ROC tenta o leitor a usar pontos de corte, o que é uma má prática estatística.

Quanto ao cálculo manual do $ c $ -index, faça um gráfico com $ Y = 0,1 $ no $ eixo x $ e o preditor contínuo ou probabilidade prevista de que $ Y = 1 $ no eixo $ y $. Se você conectar todos os pontos com $ Y = 0 $ com todos os pontos com $ Y = 1 $, a proporção das linhas que têm uma inclinação positiva é a probabilidade de concordância.

Quaisquer medidas que tenham um denominador de $ n $ nesta configuração são regras de pontuação de precisão impróprias e devem ser evitadas. Isso inclui proporção classificada corretamente, sensibilidade e especificidade.

Para a função R Hmisc pacote rcorr.cens, imprima a resultado inteiro para ver mais informações, especialmente um erro padrão.

Comentários

  • Obrigado, @Frank Harell, agradeço sua perspectiva. Eu simplesmente uso a estatística c como uma probabilidade de concordância, já que não ‘ gosto de pontos de corte. Obrigado mais uma vez!

Resposta

Aqui está uma alternativa à forma natural de calcular AUC simplesmente usando o trapezoidal regra para obter a área sob a curva ROC.

A AUC é igual à probabilidade de que uma observação positiva amostrada aleatoriamente tenha uma probabilidade prevista (de ser positiva) maior do que uma observação negativa amostrada aleatoriamente. Você pode usar isso para calcular a AUC facilmente em qualquer linguagem de programação, passando por todas as combinações de pares de observações positivas e negativas. Você também pode amostrar observações aleatoriamente se o tamanho da amostra for muito grande. Se você deseja calcular a AUC usando papel e caneta, esta pode não ser a melhor abordagem, a menos que você tenha uma amostra muito pequena / muito tempo. Por exemplo, em R:

n <- 100L x1 <- rnorm(n, 2.0, 0.5) x2 <- rnorm(n, -1.0, 2) y <- rbinom(n, 1L, plogis(-0.4 + 0.5 * x1 + 0.1 * x2)) mod <- glm(y ~ x1 + x2, "binomial") probs <- predict(mod, type = "response") combinations <- expand.grid(positiveProbs = probs[y == 1L], negativeProbs = probs[y == 0L]) mean(combinations$positiveProbs > combinations$negativeProbs) [1] 0.628723 

Podemos verificar usando o pacote pROC:

library(pROC) auc(y, probs) Area under the curve: 0.6287 

Usando amostragem aleatória:

mean(sample(probs[y == 1L], 100000L, TRUE) > sample(probs[y == 0L], 100000L, TRUE)) [1] 0.62896 

Resposta

  1. Você tem valor verdadeiro para as observações.
  2. Calcule a probabilidade posterior e, em seguida, classifique as observações por esta probabilidade.
  3. Assumindo a probabilidade de corte de $ P $ e o número de observações $ N $:
    $$ \ frac {\ text {Soma das classificações verdadeiras} -0,5PN (PN + 1)} { PN (N-PN)} $$

Comentários

  • @ user73455 … 1) Sim, tenho o valor verdadeiro para observações. 2) A probabilidade posterior é sinônimo de probabilidades previstas para cada uma das observações? 3) Compreendido; entretanto, o que é ” Soma das classificações verdadeiras ” e como se calcula esse valor? Talvez um exemplo o ajude a explicar essa resposta mais detalhadamente. Obrigado!

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *