# Fitting a dose response curve to 3 dose points

I have dose-response data sets, of 3 doses, 3 replicates for each dose.

I'd like to fit a curve for these data sets and I'm mainly interested in the slope of the curve since I want to be able and distinguish cases such as these two:

df.1 <- data.frame(dose = c(10,0.625,2.5,10,0.625,2.5,10,0.625,2.5),
response = c(61.408107781412,98.3181200924209,92.6050821600725,19.1515147744906,97.7336163702194,75.7946981922533,82.0473297811668,76.5281098812883,103.062728539761),
stringsAsFactors=F)


which when plotted: plot(log(df.1$dose),df.1$response), looks like:

df.2 <- data.frame(dose = c(10,0.625,2.5,10,0.625,2.5,10,0.625,2.5),
response = c(70.1978323700685,77.9487655135013,76.669278705336,27.3732511270187,127.028568421247,67.6604871443974,81.3663399111724,47.0441937203051,85.6920201048891),
stringsAsFactors = F)


which when plotted: plot(log(df.2$dose),df.2$response) looks like:

I tried using the LL.4 model implemented in R's drc package, using the drm function, however the results I get are a bit confusing.

For the first data set:

fit.1 <- drm(response~dose,data=df.1,fct=LL.4(names=c("slope","low","high","ED50")))


plot(fit.1) gives:

and:

> summary(fit.1)

Model fitted: Log-logistic (ED50 as parameter) (4 parms)

Parameter estimates:

Estimate Std. Error    t-value p-value
slope:(Intercept) 3.7577e+00 4.5689e+01 8.2247e-02  0.9376
low:(Intercept)   1.7206e+01 2.0485e+03 8.3995e-03  0.9936
high:(Intercept)  9.0872e+01 1.3778e+01 6.5955e+00  0.0012
ED50:(Intercept)  1.0025e+01 1.4769e+02 6.7877e-02  0.9485

Residual standard error:

23.4205 (5 degrees of freedom)


For the second data set:

fit.2 <- drm(response~dose,data=df.2,fct=LL.4(names=c("slope","low","high","ED50")))

plot(fit.2) gives:


and:

> summary(fit.2)

Model fitted: Log-logistic (ED50 as parameter) (4 parms)

Parameter estimates:

Estimate Std. Error t-value p-value
slope:(Intercept)  -1.0662         NA      NA      NA
low:(Intercept)    86.6585         NA      NA      NA
high:(Intercept)   32.1951         NA      NA      NA
ED50:(Intercept)   10.1519         NA      NA      NA

Residual standard error:

31.75223 (5 degrees of freedom)
Warning message:
In sqrt(diag(varMat)) : NaNs produced


I'd expect the slopes to have the same sign for both data sets but I guess given the low quality of my data a sigmoidal curve is not the way to go.

So my question is what is a reasonable model, preferably in R, to fit to such data to get an estimate for the slope.

A general comment first. You are trying to obtain an estimate from data that contain almost no information relevant to that estimate. There is almost no dose-response relationship in your data, perhaps because your drug does nothing, because the lowest dose is large enough to have the maximal effect, or because the highest dose is only just enough to start to produce a response. Fitting a sigmoid dose-response relationship to those data is entirely pointless.

If you really need to know about the slope (usually we care more about the ED50 and range than the slope), you will need to run another set of experiments that yield data with a wider range of doses. If the dose-response relationship is not obvious in the data then fitting a model will yield nonsense estimates.

Now, comments with respect to the process of dose-response curve fitting.

The models you fitted have parameters for the (mid-point) slope, for the upper plateau, for the lower plateau, and for the mid-point along the x-axis. Four parameters. Assuming your data are for a downwards-going response (i.e. your drug inhibits the response being measured) then you $might$ be able to assume that the upper plateau (the response values at doses of your drug too low to do anything) is at 100%, but you do not give enough information about the responses to know if that is appropriate. If you assume the upper plateau should be at 100 then you can substitute a fixed value of 100 for the upper plateau parameter. That reduces the amount of estimation that the curve fitting process has to achieve.

The output of the curve fitting routine fit.2 contains a warning that you should not ignore. I don't know exactly what it means, but I would be surprised if it was not related to the curve fit routine failing to converge on a locally optimal solution.

Given that the curve fitting was done without information about the location of the lower plateau (I note that for fit.1 the estimated standard error on the estimate of the lower plateau is orders of magnitude greater than the estimate: a very bad sign), and given that the curve fitting routine probably failed to converge on a solution, it is inappropriate to place any value on the slope estimate.

• You're right. An LL.4 is too much to ask from these data. I edited my question accordingly.
– dan
Dec 11, 2016 at 2:05
• Given the shortcomings of your data, I would discourage you from attempting to make ANY inferences from model-fitting. Sorry, but your data are not up to the task. Statistics cannot extract information that is not present in the data. Dec 11, 2016 at 2:15

This may be reasonable - using loess (better than doing the 'easy' thing which is giving up and throwing away the data altogether..):

df.1$log2.dose <- log2(df.1$dose)
df.2$log2.dose <- log2(df.2$dose)

fit.1 <- loess(response ~ log2.dose,data=df.1)
fit.2 <- loess(response ~ log2.dose,data=df.2)


# plotting the results along with the slope for each interval:

require(ggplot2)
doses <- sort(unique(df.1\$log2.dose))
fit.1.slopes <- diff(predict(fit.1, newdata=doses))
pred.fit.1 <- predict(fit.1,newdata=seq(min(doses),max(doses),(max(doses)-min(doses))/100))
ann.x <- c(doses[1]+0.5*(doses[2]-doses[1]),doses[2]+0.5*(doses[3]-doses[2]))
ann.y <- c(median(pred.fit.1[1:49]),median(pred.fit.1[51:100]))+5

ggplot(df.1,aes(x=log2.dose,y=response))+stat_smooth(method="loess",formula=y~x,colour="black")+geom_point()+labs(x="log2(dose)",y="response")+
annotate("text",size=4,hjust=0,x=ann.x,y=ann.y,label=c(paste0("slope = ",signif(fit.1.slopes[1],2)),paste0("slope = ",signif(fit.1.slopes[2],2))))


fit.2.slopes <- diff(predict(fit.2, newdata=doses))
pred.fit.2 <- predict(fit.2,newdata=seq(min(doses),max(doses),(max(doses)-min(doses))/100))
ann.x <- c(doses[1]+0.5*(doses[2]-doses[1]),doses[2]+0.5*(doses[3]-doses[2]))
ann.y <- c(median(pred.fit.2[1:49]),median(pred.fit.2[51:100]))+5

ggplot(df.2,aes(x=log2.dose,y=response))+stat_smooth(method="loess",formula=y~x,colour="black")+geom_point()+labs(x="log2(dose)",y="response")+
annotate("text",size=4,hjust=0,x=ann.x,y=ann.y,label=c(paste0("slope = ",signif(fit.2.slopes[1],2)),paste0("slope = ",signif(fit.2.slopes[2],2))))


• No. The slopes that you determined from the loess function are not estimates of the dose-response curve slope! If the reason for wanting to know the slope is anything other than trivial, you will have to generate a more complete dataset. Dec 11, 2016 at 19:39
• Well, you wrote that the data don't adhere to dose-response properties
– dan
Dec 11, 2016 at 20:23
• What do you mean? Perhaps read my words again. Dec 11, 2016 at 20:26
• I agree with you that a dose-response curve is too much to ask from such noisy data, but getting some estimates for the slopes, be it for each interval, is reasonable. My answer doesn't claim to estimate dose-response.
– dan
Dec 11, 2016 at 20:30
• The important thing is what you are going to use the slope estimates for. If any inference is to be made from them then you must not do it! If you just want to describe the results then I strongly advise against it because no reader will gain an adequate understanding of the nature of your data from a bad slope estimate. The problem with your data is not noise, it is the range of doses tested. To define the slope of a dose response curve you need, at least a couple of doses above and below the mid-point. You don't have them. Three points will fix a circle, but not a sigmoid curve. Dec 11, 2016 at 20:40