#Let's make up a data
set.seed(1234)
mydat <- data.frame(
won=as.factor(sample(c(0, 1), 250, replace=TRUE)), #wining probability
bid=runif(250, min=0, max=1000) #uniform distribution data
)
head(mydat)
#Let's fit the Logistic Regression using glm function:
mod1 <- glm(won~bid, data=mydat, family=binomial(link="logit"))
summary(mod1)
Call:
glm(formula = won ~ bid, family = binomial(link = "logit"), data = mydat)
Deviance Residuals:
Min 1Q Median 3Q Max
-1.202 -1.169 -1.141 1.184 1.214
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 0.0582872 0.2629218 0.222 0.825
bid -0.0001440 0.0004468 -0.322 0.747
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 346.56 on 249 degrees of freedom
Residual deviance: 346.45 on 248 degrees of freedom
AIC: 350.45
Number of Fisher Scoring iterations: 3
A logistic regression model models the relationship between a binary response variable and, in this case, one continuous predictor. The result is a logit-transormed probability as a linear relation to the predictor. The coefficients from mod1
are given in logged odds (which are difficult to interpet), according to: \[logit(p) = \log(\frac{p}{1-p}) = \beta_0 + \beta_1x_1\]
To convert logged odds to probabilities, we can translate the above to: \[p = \frac{\exp(\beta_0 + \beta_1x_1)}{(1+\exp(\beta_0+\beta_1x_1))}\]
Let’s use this information to set up the plot. First, you need a range of the predictor variable:
plotdat <- data.frame(bid=(0:1000))
head(plotdat)
Then using predict
, you can obtain predictions based on your model:
preddat <- predict(mod1, newdata=plotdat, se.fit=TRUE)
str(preddat)
List of 3
$ fit : Named num [1:1001] 0.0583 0.0581 0.058 0.0579 0.0577 ...
..- attr(*, "names")= chr [1:1001] "1" "2" "3" "4" ...
$ se.fit : Named num [1:1001] 0.263 0.263 0.262 0.262 0.261 ...
..- attr(*, "names")= chr [1:1001] "1" "2" "3" "4" ...
$ residual.scale: num 1
head(data.frame(preddat))
By specifying se.fit=TRUE
, you also get the standard error associated with each fitted value. The resulting data.frame
is a matrix with the following components:
- the fitted predictions (
fit
),
- the estimated standard errors (
se.fit
), and
- a scalar giving the square root of the dispersion used to compute the standard errors (
residual.scale
).
In the case of a binomial logit, the value will be 1 (which you can see by entering preddat$residual.scale
in R
). If you want to see an example of what you’ve calculated so far, you can type head(data.frame(preddat))
.
The next step is to set up the plot. We can set up a blank plotting area with the parameters first:
with(mydat, plot(bid, won, type="n",
ylim=c(0, 1), ylab="Probability of winning", xlab="Bid"))

Now you can see where it is important to know how to calculate the fitted probabilities. You can draw the line corresponding to the fitted probabilities following the second formula above. Using the preddat data.frame
you can convert the fitted values to probabilities and use that to plot a line against the values of your predictor variable.
with(mydat, plot(bid, won, type="n",
ylim=c(0, 1), ylab="Probability of winning", xlab="Bid"))
with(preddat, lines(0:1000, exp(fit)/(1+exp(fit)), col="blue"))

Finally, answer the question, the confidence intervals can be added to the plot by calculating the probability for the fitted values +/-
1.96 times the standard error:
with(mydat, plot(bid, won, type="n",
ylim=c(0, 1), ylab="Probability of winning", xlab="Bid"))
with(preddat, lines(0:1000, exp(fit)/(1+exp(fit)), col="blue"))
with(preddat, lines(0:1000, exp(fit+1.96*se.fit)/(1+exp(fit+1.96*se.fit)), lty=2))
with(preddat, lines(0:1000, exp(fit-1.96*se.fit)/(1+exp(fit-1.96*se.fit)), lty=2))

LS0tCnRpdGxlOiAiUGxvdHRpbmcgY29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBmcm9tIGEgbG9naXN0aWMgcmVncmVzc2lvbiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCgpgYGB7cn0KI0xldCdzIG1ha2UgdXAgYSBkYXRhCnNldC5zZWVkKDEyMzQpCm15ZGF0IDwtIGRhdGEuZnJhbWUoCiAgICB3b249YXMuZmFjdG9yKHNhbXBsZShjKDAsIDEpLCAyNTAsIHJlcGxhY2U9VFJVRSkpLCAgI3dpbmluZyBwcm9iYWJpbGl0eQogICAgYmlkPXJ1bmlmKDI1MCwgbWluPTAsIG1heD0xMDAwKSAjdW5pZm9ybSBkaXN0cmlidXRpb24gZGF0YQopCmhlYWQobXlkYXQpCgojTGV0J3MgZml0IHRoZSBMb2dpc3RpYyBSZWdyZXNzaW9uIHVzaW5nIGdsbSBmdW5jdGlvbjoKbW9kMSA8LSBnbG0od29ufmJpZCwgZGF0YT1teWRhdCwgZmFtaWx5PWJpbm9taWFsKGxpbms9ImxvZ2l0IikpCnN1bW1hcnkobW9kMSkKCmBgYAoKQSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIG1vZGVscyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYSBiaW5hcnkgcmVzcG9uc2UgdmFyaWFibGUgYW5kLCBpbiB0aGlzIGNhc2UsIG9uZSBjb250aW51b3VzIHByZWRpY3Rvci4gVGhlIHJlc3VsdCBpcyBhIGxvZ2l0LXRyYW5zb3JtZWQgcHJvYmFiaWxpdHkgYXMgYSBsaW5lYXIgcmVsYXRpb24gdG8gdGhlIHByZWRpY3Rvci4gVGhlIGNvZWZmaWNpZW50cyBmcm9tIGBtb2QxYCBhcmUgZ2l2ZW4gaW4gbG9nZ2VkIG9kZHMgKHdoaWNoIGFyZSBkaWZmaWN1bHQgdG8gaW50ZXJwZXQpLCBhY2NvcmRpbmcgdG86CiQkbG9naXQocCkgPSBcbG9nKFxmcmFje3B9ezEtcH0pID0gXGJldGFfMCArIFxiZXRhXzF4XzEkJAoKVG8gY29udmVydCBsb2dnZWQgb2RkcyB0byBwcm9iYWJpbGl0aWVzLCB3ZSBjYW4gdHJhbnNsYXRlIHRoZSBhYm92ZSB0bzoKJCRwID0gXGZyYWN7XGV4cChcYmV0YV8wICsgXGJldGFfMXhfMSl9eygxK1xleHAoXGJldGFfMCtcYmV0YV8xeF8xKSl9JCQKCkxldCdzIHVzZSB0aGlzIGluZm9ybWF0aW9uIHRvIHNldCB1cCB0aGUgcGxvdC4gRmlyc3QsIHlvdSBuZWVkIGEgcmFuZ2Ugb2YgdGhlIHByZWRpY3RvciB2YXJpYWJsZToKYGBge3J9CnBsb3RkYXQgPC0gZGF0YS5mcmFtZShiaWQ9KDA6MTAwMCkpCmhlYWQocGxvdGRhdCkKYGBgCgpUaGVuIHVzaW5nIGBwcmVkaWN0YCwgeW91IGNhbiBvYnRhaW4gcHJlZGljdGlvbnMgYmFzZWQgb24geW91ciBtb2RlbDoKCmBgYHtyfQpwcmVkZGF0IDwtIHByZWRpY3QobW9kMSwgbmV3ZGF0YT1wbG90ZGF0LCBzZS5maXQ9VFJVRSkKc3RyKHByZWRkYXQpCmhlYWQoZGF0YS5mcmFtZShwcmVkZGF0KSkKYGBgCgoKQnkgc3BlY2lmeWluZyBgc2UuZml0PVRSVUVgLCB5b3UgYWxzbyBnZXQgdGhlICoqc3RhbmRhcmQgZXJyb3IqKiBhc3NvY2lhdGVkIHdpdGggZWFjaCBmaXR0ZWQgdmFsdWUuIFRoZSByZXN1bHRpbmcgYGRhdGEuZnJhbWVgIGlzIGEgbWF0cml4IHdpdGggdGhlIGZvbGxvd2luZyBjb21wb25lbnRzOiAKCiogdGhlIGZpdHRlZCBwcmVkaWN0aW9ucyAoYGZpdGApLCAKKiB0aGUgZXN0aW1hdGVkIHN0YW5kYXJkIGVycm9ycyAoYHNlLmZpdGApLCBhbmQgCiogYSBzY2FsYXIgZ2l2aW5nIHRoZSAqKnNxdWFyZSByb290Kiogb2YgdGhlIGRpc3BlcnNpb24gdXNlZCB0byBjb21wdXRlIHRoZSBzdGFuZGFyZCBlcnJvcnMgKGByZXNpZHVhbC5zY2FsZWApLiAKCkluIHRoZSBjYXNlIG9mIGEgYmlub21pYWwgbG9naXQsIHRoZSB2YWx1ZSB3aWxsIGJlIDEgKHdoaWNoIHlvdSBjYW4gc2VlIGJ5IGVudGVyaW5nIGBwcmVkZGF0JHJlc2lkdWFsLnNjYWxlYCBpbiBgUmApLiBJZiB5b3Ugd2FudCB0byBzZWUgYW4gZXhhbXBsZSBvZiB3aGF0IHlvdSd2ZSBjYWxjdWxhdGVkIHNvIGZhciwgeW91IGNhbiB0eXBlIGBoZWFkKGRhdGEuZnJhbWUocHJlZGRhdCkpYC4KClRoZSBuZXh0IHN0ZXAgaXMgdG8gc2V0IHVwIHRoZSBwbG90LiBXZSBjYW4gc2V0IHVwIGEgYmxhbmsgcGxvdHRpbmcgYXJlYSB3aXRoIHRoZSBwYXJhbWV0ZXJzIGZpcnN0OgoKYGBge3J9CndpdGgobXlkYXQsIHBsb3QoYmlkLCB3b24sIHR5cGU9Im4iLCAKICAgIHlsaW09YygwLCAxKSwgeWxhYj0iUHJvYmFiaWxpdHkgb2Ygd2lubmluZyIsIHhsYWI9IkJpZCIpKQpgYGAKCgpOb3cgeW91IGNhbiBzZWUgd2hlcmUgaXQgaXMgaW1wb3J0YW50IHRvIGtub3cgaG93IHRvIGNhbGN1bGF0ZSB0aGUgZml0dGVkIHByb2JhYmlsaXRpZXMuIFlvdSBjYW4gZHJhdyB0aGUgbGluZSBjb3JyZXNwb25kaW5nIHRvIHRoZSBmaXR0ZWQgcHJvYmFiaWxpdGllcyBmb2xsb3dpbmcgdGhlIHNlY29uZCBmb3JtdWxhIGFib3ZlLiBVc2luZyB0aGUgYHByZWRkYXQgZGF0YS5mcmFtZWAgeW91IGNhbiBjb252ZXJ0IHRoZSBmaXR0ZWQgdmFsdWVzIHRvIHByb2JhYmlsaXRpZXMgYW5kIHVzZSB0aGF0IHRvIHBsb3QgYSBsaW5lIGFnYWluc3QgdGhlIHZhbHVlcyBvZiB5b3VyIHByZWRpY3RvciB2YXJpYWJsZS4KCmBgYHtyfQp3aXRoKG15ZGF0LCBwbG90KGJpZCwgd29uLCB0eXBlPSJuIiwgCiAgICB5bGltPWMoMCwgMSksIHlsYWI9IlByb2JhYmlsaXR5IG9mIHdpbm5pbmciLCB4bGFiPSJCaWQiKSkKd2l0aChwcmVkZGF0LCBsaW5lcygwOjEwMDAsIGV4cChmaXQpLygxK2V4cChmaXQpKSwgY29sPSJibHVlIikpCmBgYAoKRmluYWxseSwgYW5zd2VyIHRoZSBxdWVzdGlvbiwgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGNhbiBiZSBhZGRlZCB0byB0aGUgcGxvdCBieSBjYWxjdWxhdGluZyB0aGUgcHJvYmFiaWxpdHkgZm9yIHRoZSBmaXR0ZWQgdmFsdWVzIGArLy1gIDEuOTYgdGltZXMgdGhlIHN0YW5kYXJkIGVycm9yOgoKYGBge3J9CndpdGgobXlkYXQsIHBsb3QoYmlkLCB3b24sIHR5cGU9Im4iLCAKICAgIHlsaW09YygwLCAxKSwgeWxhYj0iUHJvYmFiaWxpdHkgb2Ygd2lubmluZyIsIHhsYWI9IkJpZCIpKQp3aXRoKHByZWRkYXQsIGxpbmVzKDA6MTAwMCwgZXhwKGZpdCkvKDErZXhwKGZpdCkpLCBjb2w9ImJsdWUiKSkKd2l0aChwcmVkZGF0LCBsaW5lcygwOjEwMDAsIGV4cChmaXQrMS45NipzZS5maXQpLygxK2V4cChmaXQrMS45NipzZS5maXQpKSwgbHR5PTIpKQp3aXRoKHByZWRkYXQsIGxpbmVzKDA6MTAwMCwgZXhwKGZpdC0xLjk2KnNlLmZpdCkvKDErZXhwKGZpdC0xLjk2KnNlLmZpdCkpLCBsdHk9MikpCmBgYAoK