19 Receiver Operating Characteristic
Up until now, we have dealt with tests that are categorized as either positive or negative. However, many tests are quantitative rather than qualitative. For instance, the blood urea and nitrogen, serum cholesterol and serum protein among many others are measured on a continuous scale often ranging from 0 to infinity. Such tests pose different challenges as we often need a cut-off point to determine which range of values can be considered “normal” or “abnormal”. As we have learned above the sensitivity and specificity of a test are often used to define how good it is. For tests on a continuous scale the sensitivity and specificity change depending on the cut-off provided for the measure.
In this section, we use the lbw.csv data collected in a study conducted in a cohort of 350 newborns in Ghana. The identification of babies born with a weight of less than 2.5 kg is important because of the special needs they require. In many underdeveloped countries, however, the unavailability of a reliable weighing scale makes this a challenge. This has prompted the search for other surrogate measures easily available in rural areas for determining if a baby is a low birth weight (<2.5kgs). The study aimed to determine how well the length or chest circumference of a baby could be used as a surrogate indicator for low birth weight in newborns. If they turn out to be good tests they can easily be deployed in any rural area as the only instrument needed here would be a measuring tape. The variables collected include their study ID (sid), birth weight (bweight), sex (gender), chest circumference (chc) and length of baby (lgth).
First, we read the data after clearing the workspace
df_lbw <-
read_csv("./Data/lbw.csv") %>%
mutate(gender = factor(gender)) %>%
mutate(bwcat = ifelse(bweight < 2.5, 1, 0) %>%
factor(levels = c(0,1), labels = c("Normal","Low")))
And then summarize it
df_lbw %>% dfSummary(graph.col = F)
Data Frame Summary
df_lbw
Dimensions: 350 x 6
Duplicates: 0
---------------------------------------------------------------------------------------
No Variable Stats / Values Freqs (% of Valid) Valid Missing
---- ----------- --------------------------- --------------------- ---------- ---------
1 sid Mean (sd) : 175.5 (101.2) 350 distinct values 350 0
[numeric] min < med < max: (100.0%) (0.0%)
1 < 175.5 < 350
IQR (CV) : 174.5 (0.6)
2 bweight Mean (sd) : 2.9 (0.6) 33 distinct values 350 0
[numeric] min < med < max: (100.0%) (0.0%)
1 < 2.9 < 4.5
IQR (CV) : 0.7 (0.2)
3 gender 1. Female 166 (47.4%) 350 0
[factor] 2. Male 184 (52.6%) (100.0%) (0.0%)
4 chc Mean (sd) : 31.7 (3.2) 115 distinct values 350 0
[numeric] min < med < max: (100.0%) (0.0%)
20 < 32 < 42.1
IQR (CV) : 4 (0.1)
5 lgth Mean (sd) : 46.8 (4.3) 109 distinct values 350 0
[numeric] min < med < max: (100.0%) (0.0%)
28 < 47 < 60.3
IQR (CV) : 5.5 (0.1)
6 bwcat 1. Normal 268 (76.6%) 350 0
[factor] 2. Low 82 (23.4%) (100.0%) (0.0%)
---------------------------------------------------------------------------------------
Next, I introduce the pROC
package. The function roc()
from the package will
be used extensively in this section.
19.1 Sensitivity, specificity and cut-offs
As mentioned above when one has a gold standard which is bivariate (indicating
presence or absence) and a quantitative test, the sensitivity and specificity of
the test depends on the cut-off chosen. For our lbw.csv
data our gold standard
for a low birth weight baby is the birth weight categorised into low birth
weight (LBW) or normal birth weight (NBW). Two continuous measures, the chest
circumference and the length of the baby were used as our tests.
To illustrate the relationship between the various cut-offs with sensitivity and specificity we generate some arbitrary cut-offs.
cut.off <- c(28, 42, 44, 46, 47, 49, 50, 51, 61)
From these cut-offs, we generate the categories from the length of the babies and tabulate the resultant categorical variable.
df_lbw <-
df_lbw %>%
mutate(lgthcat = cut(lgth, br=cut.off, include.lowest=T))
And then count the various groups
df_lbw %>%
gtsummary::tbl_summary(
include = lgthcat,
digits = lgthcat ~ c(0,1)
) %>%
gtsummary::bold_labels()
Characteristic | N = 3501 |
---|---|
lgthcat | |
[28,42] | 49 (14.0%) |
(42,44] | 36 (10.3%) |
(44,46] | 59 (16.9%) |
(46,47] | 35 (10.0%) |
(47,49] | 67 (19.1%) |
(49,50] | 39 (11.1%) |
(50,51] | 25 (7.1%) |
(51,61] | 40 (11.4%) |
1 n (%) |
Next, we determine the sensitivities and specificities at the various cut-offs by
using the roc()
function from the pROC
package. Since this package requires
an ordered categorical variable we convert lgthcat
into an ordered factor
variable. And then go ahead to impute the relevant information into the roc()
function