Limit of detection is the sensitivity of the assay — how low a concentration can the test detect?

To test this, the lab did a dilution series over a range of concentrations. When the concentration is low enough, the fusion won’t be detected.

Using the data from the dilution series, we used 4 models to predict the limit of detection.

- Linear regression using all data
- Linear regression using just the concentrations where data was observed
- Logit
- Probit

1. Linear regression using all data

My data frame “chan” contains deltaCq values and the Percentage_Fusion.

First do the linear regression:

chan.lm <- lm ( deltaCq ~ log2 ( Final_Percentage_Fusion ), data=chan)
summary (chan.lm)

with a prediction interval:

PI <- predict (chan.lm, newdata = new.dat, interval='prediction', level=0.90)

The fusion dilution where the top prediction limit crosses the delta Ct cut-off will be estimated and chosen to be the model's estimate of the C95.

My delta Ct cutoff is 5 (determined from the Accuracy study).

`cutoff <- 5`

Our linear model on the upper prediction interval is :

upper.lm <- lm (PI[,"upr"] ~ log2(conc) )

y = slope * x + intercept and hence x = (y- intercept)/slope. In our case x is the concentration, and it's what we want. y is delta Ct and we want to know what concentration is predicted to have a delta Ct of 5 (our cutoff).

`slope <- upper.lm$coefficients[2]`

`intercept <- upper.lm$coefficients[1]`

C95 = (cutoff - intercept)/slope

conc_LoD = 2^C95

conc_LoD is our limit of detection.

2. Linear regression using just the concentrations where data was observed

To do a linear regression on just some of the data, identify which concentrations to remove.

remove_because_partial_or_no_data <- c(0.0488, 0.0977, 0.1953, 0.3906, 0.781, 1.563)

Remove these concentrations from the data frame:

chan <- chan[!(chan$Final_Percentage_Fusion %in% remove_because_partial_or_no_data),]

and then follow the same steps as in #1.

3. Logit model

First, make a column of whether the PCR product is called as a positive or not. This column is called "CalledPositive".

chan["CalledPositive"] <- 1

If no signal is detected or deltaCq is > 5, then the fusion is not called and we have to assign CalledPositive as false or "0".

chan[is.na(chan$deltaCq), "CalledPositive"] <- 0

chan[!is.na(chan$deltaCq) & chan$deltaCq > 5, "CalledPositive"] <- 0

Run the logit analysis

mylogit <- glm (CalledPositive ~ log2Conc , family = binomial(link = "logit"),
data = chan)

To find the sensitivity at 95% based on the logit model, I manually feed in ranges of values until I get something close to 95%.

For example, by inspecting the graph I know that my LoD is somewhere between 0 and 2 so I set

xlower <- 0

xupper <- 2

and then I make a series of points:

temp.data <- data.frame(log2Conc = seq(from = xlower, to = xupper, length.out = 10))

And I input temp.data to get predicted sensitivities:

predicted.data.logit <- predict(mylogit, temp.data, type = "response", se.fit=TRUE)

I then look at predicted.data.logit to see which concentration gives me close to 95%. This can take several iterations until I'm very close to 0.95

4. Probit model

We make use of the "CalledPositive" column defined from the section 3 logit model.

myprobit <- glm(CalledPositive ~ log2Conc , family = binomial(link = "probit"),
data = chan)

To find the concentration with sensitivity of 95%, do the same steps as described in 3.Logit model, but use predicted.data.probit instead.

predicted.data.probit <- predict(myprobit, temp.data, type = "response", se.fit=TRUE)

You can find my complete linear regression R scripts and probit and logit scripts on github.

## Leave a Reply