Different ways of displaying logistic regression. See Chapter 14 in Regression and Other Stories.


Load packages

library("rprojroot")
root<-has_file(".ROS-Examples-root")$make_fix_file()
library("rstanarm")
options(mc.cores = parallel::detectCores())
logit <- qlogis
invlogit <- plogis

Simulate fake data from logit model

n <- 50
a <- 2
b <- 3
x_mean <- -a/b
x_sd <- 4/b
x <- rnorm(n, x_mean, x_sd)
y <- rbinom(n, 1, invlogit(a + b*x))
fake_1 <- data.frame(x, y)
head(fake_1)
           x y
1 -0.6787458 0
2  0.3627305 1
3  0.2667119 1
4  0.2874112 1
5 -0.6349892 0
6  0.3316449 1

Fit the model and save the coefficient estimates

fit_1 <- stan_glm(y ~ x, family=binomial(link="logit"), data=fake_1, refresh=0)
a_hat <- coef(fit_1)[1]
b_hat <- coef(fit_1)[2]

Graph data and underying and fitted logistic curves

shifted <- function(a, delta=0.008) return(ifelse(a==0, delta, ifelse(a==1, 1 - delta, a)))
par(mar=c(3,3,2,1), mgp=c(1.5,.5,0), tck=-.01)
plot(x, shifted(y), ylim=c(0, 1), xlab="x", ylab="y", yaxs="i", pch=20)
curve(invlogit(a + b*x), add=TRUE, col="gray30")
curve(invlogit(a_hat + b_hat*x), add=TRUE, lty=2, col="gray30")
x0 <- (1.5 - a) / b
text(x0, invlogit(1.5), paste("   True curve,   \n   y = invlogit(", round(a, 1), " + ", round(b, 1), "x)   ", sep=""), cex=.9, col="gray30",
  adj=if (a_hat + b_hat*x0 > 1.5) 0 else 1)
x0 <- (-1.5 - a_hat) / b_hat
text(x0, invlogit(-1.5), paste("   Fitted curve,   \n   y = invlogit(", round(a_hat, 1), " + ", round(b_hat, 1), "x)   ", sep=""), cex=.9, col="gray30",
  adj=if (a + b*x0 > -1.5) 0 else 1)

Binned plot

K <- 5
bins <- as.numeric(cut(x, K))
x_bar <- rep(NA, K)
y_bar <- rep(NA, K)
for (k in 1:K){
  x_bar[k] <- mean(x[bins==k])
  y_bar[k] <- mean(y[bins==k])
}
par(mar=c(3,3,2,1), mgp=c(1.5,.5,0), tck=-.01)
plot(x, shifted(y), ylim=c(0, 1), xlab="x", ylab="y", yaxs="i", pch=20, cex=.8, main="Data and binned averages", cex.main=.9, col="gray50")
points(x_bar, shifted(y_bar, 0.02), pch=21, cex=1.5)

Logistic regression as function of two predictors

n <- 100
beta <- c(2, 3, 4) # arbitrary choice of true coefficients in the model
x1 <- rnorm(n, 0, 0.4) # somewhat arbitary choice of scale of data, set so there will be a good mix of 0's and 1's
x2 <- rnorm(n, -0.5, 0.4)
y <- rbinom(n, 1, invlogit(cbind(rep(1, n), x1, x2) %*% beta))
fake_2 <- data.frame(x1, x2, y)

Fit the model and save the coefficient estimates

fit_2 <- stan_glm(y ~ x1 + x2, family=binomial(link="logit"), data=fake_2, refresh=0)
beta_hat <- coef(fit_2)

Graph data and discrimination lines

par(mar=c(3,3,2,1), mgp=c(1.5,.5,0), tck=-.01)
plot(x1, x2, bty="l", type="n", main="Data and 10%, 50%, 90% discrimination lines\nfrom fitted logistic regression", cex.main=.9) 
points(x1[y==0], x2[y==0], pch=20)  # dots
points(x1[y==1], x2[y==1], pch=21) # circles
abline(-beta[1]/beta[3], -beta[2]/beta[3])
abline((logit(0.9) - beta[1])/beta[3], -beta[2]/beta[3], lty=2)
abline((logit(0.1) - beta[1])/beta[3], -beta[2]/beta[3],  lty=2)

LS0tCnRpdGxlOiAiUmVncmVzc2lvbiBhbmQgT3RoZXIgU3RvcmllczogTG9naXN0aWMgcmVncmVzc2lvbiBncmFwaHMiCmF1dGhvcjogIkFuZHJldyBHZWxtYW4sIEplbm5pZmVyIEhpbGwsIEFraSBWZWh0YXJpIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCkpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCkRpZmZlcmVudCB3YXlzIG9mIGRpc3BsYXlpbmcgbG9naXN0aWMgcmVncmVzc2lvbi4gU2VlIENoYXB0ZXIgMTQgaW4KUmVncmVzc2lvbiBhbmQgT3RoZXIgU3Rvcmllcy4KCi0tLS0tLS0tLS0tLS0KCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjb21tZW50PU5BKQojIHN3aXRjaCB0aGlzIHRvIFRSVUUgdG8gc2F2ZSBmaWd1cmVzIGluIHNlcGFyYXRlIGZpbGVzCnNhdmVmaWdzIDwtIEZBTFNFCmBgYAoKIyMjIyBMb2FkIHBhY2thZ2VzCgpgYGB7ciB9CmxpYnJhcnkoInJwcm9qcm9vdCIpCnJvb3Q8LWhhc19maWxlKCIuUk9TLUV4YW1wbGVzLXJvb3QiKSRtYWtlX2ZpeF9maWxlKCkKbGlicmFyeSgicnN0YW5hcm0iKQpvcHRpb25zKG1jLmNvcmVzID0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCkpCmxvZ2l0IDwtIHFsb2dpcwppbnZsb2dpdCA8LSBwbG9naXMKYGBgCgojIyMjIFNpbXVsYXRlIGZha2UgZGF0YSBmcm9tIGxvZ2l0IG1vZGVsCgpgYGB7ciB9Cm4gPC0gNTAKYSA8LSAyCmIgPC0gMwp4X21lYW4gPC0gLWEvYgp4X3NkIDwtIDQvYgp4IDwtIHJub3JtKG4sIHhfbWVhbiwgeF9zZCkKeSA8LSByYmlub20obiwgMSwgaW52bG9naXQoYSArIGIqeCkpCmZha2VfMSA8LSBkYXRhLmZyYW1lKHgsIHkpCmhlYWQoZmFrZV8xKQpgYGAKCiMjIyMgRml0IHRoZSBtb2RlbCBhbmQgc2F2ZSB0aGUgY29lZmZpY2llbnQgZXN0aW1hdGVzCgpgYGB7ciB9CmZpdF8xIDwtIHN0YW5fZ2xtKHkgfiB4LCBmYW1pbHk9Ymlub21pYWwobGluaz0ibG9naXQiKSwgZGF0YT1mYWtlXzEsIHJlZnJlc2g9MCkKYV9oYXQgPC0gY29lZihmaXRfMSlbMV0KYl9oYXQgPC0gY29lZihmaXRfMSlbMl0KYGBgCgojIyMjIEdyYXBoIGRhdGEgYW5kIHVuZGVyeWluZyBhbmQgZml0dGVkIGxvZ2lzdGljIGN1cnZlcwoKYGBge3IgfQpzaGlmdGVkIDwtIGZ1bmN0aW9uKGEsIGRlbHRhPTAuMDA4KSByZXR1cm4oaWZlbHNlKGE9PTAsIGRlbHRhLCBpZmVsc2UoYT09MSwgMSAtIGRlbHRhLCBhKSkpCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJMb2dpdEdyYXBocy9maWdzIiwibG9naXRncmFwaDFhLnBkZiIpLCBoZWlnaHQ9NC41LCB3aWR0aD02KQpgYGAKYGBge3IgfQpwYXIobWFyPWMoMywzLDIsMSksIG1ncD1jKDEuNSwuNSwwKSwgdGNrPS0uMDEpCnBsb3QoeCwgc2hpZnRlZCh5KSwgeWxpbT1jKDAsIDEpLCB4bGFiPSJ4IiwgeWxhYj0ieSIsIHlheHM9ImkiLCBwY2g9MjApCmN1cnZlKGludmxvZ2l0KGEgKyBiKngpLCBhZGQ9VFJVRSwgY29sPSJncmF5MzAiKQpjdXJ2ZShpbnZsb2dpdChhX2hhdCArIGJfaGF0KngpLCBhZGQ9VFJVRSwgbHR5PTIsIGNvbD0iZ3JheTMwIikKeDAgPC0gKDEuNSAtIGEpIC8gYgp0ZXh0KHgwLCBpbnZsb2dpdCgxLjUpLCBwYXN0ZSgiICAgVHJ1ZSBjdXJ2ZSwgICBcbiAgIHkgPSBpbnZsb2dpdCgiLCByb3VuZChhLCAxKSwgIiArICIsIHJvdW5kKGIsIDEpLCAieCkgICAiLCBzZXA9IiIpLCBjZXg9LjksIGNvbD0iZ3JheTMwIiwKICBhZGo9aWYgKGFfaGF0ICsgYl9oYXQqeDAgPiAxLjUpIDAgZWxzZSAxKQp4MCA8LSAoLTEuNSAtIGFfaGF0KSAvIGJfaGF0CnRleHQoeDAsIGludmxvZ2l0KC0xLjUpLCBwYXN0ZSgiICAgRml0dGVkIGN1cnZlLCAgIFxuICAgeSA9IGludmxvZ2l0KCIsIHJvdW5kKGFfaGF0LCAxKSwgIiArICIsIHJvdW5kKGJfaGF0LCAxKSwgIngpICAgIiwgc2VwPSIiKSwgY2V4PS45LCBjb2w9ImdyYXkzMCIsCiAgYWRqPWlmIChhICsgYip4MCA+IC0xLjUpIDAgZWxzZSAxKQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBkZXYub2ZmKCkKYGBgCgojIyMjIEJpbm5lZCBwbG90CgpgYGB7ciB9CksgPC0gNQpiaW5zIDwtIGFzLm51bWVyaWMoY3V0KHgsIEspKQp4X2JhciA8LSByZXAoTkEsIEspCnlfYmFyIDwtIHJlcChOQSwgSykKZm9yIChrIGluIDE6Syl7CiAgeF9iYXJba10gPC0gbWVhbih4W2JpbnM9PWtdKQogIHlfYmFyW2tdIDwtIG1lYW4oeVtiaW5zPT1rXSkKfQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBwZGYocm9vdCgiTG9naXRHcmFwaHMvZmlncyIsImxvZ2l0Z3JhcGgxYi5wZGYiKSwgaGVpZ2h0PTQuNSwgd2lkdGg9NikKYGBgCmBgYHtyIH0KcGFyKG1hcj1jKDMsMywyLDEpLCBtZ3A9YygxLjUsLjUsMCksIHRjaz0tLjAxKQpwbG90KHgsIHNoaWZ0ZWQoeSksIHlsaW09YygwLCAxKSwgeGxhYj0ieCIsIHlsYWI9InkiLCB5YXhzPSJpIiwgcGNoPTIwLCBjZXg9LjgsIG1haW49IkRhdGEgYW5kIGJpbm5lZCBhdmVyYWdlcyIsIGNleC5tYWluPS45LCBjb2w9ImdyYXk1MCIpCnBvaW50cyh4X2Jhciwgc2hpZnRlZCh5X2JhciwgMC4wMiksIHBjaD0yMSwgY2V4PTEuNSkKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgZGV2Lm9mZigpCmBgYAoKIyMjIyBMb2dpc3RpYyByZWdyZXNzaW9uIGFzIGZ1bmN0aW9uIG9mIHR3byBwcmVkaWN0b3JzCgpgYGB7ciB9Cm4gPC0gMTAwCmJldGEgPC0gYygyLCAzLCA0KSAjIGFyYml0cmFyeSBjaG9pY2Ugb2YgdHJ1ZSBjb2VmZmljaWVudHMgaW4gdGhlIG1vZGVsCngxIDwtIHJub3JtKG4sIDAsIDAuNCkgIyBzb21ld2hhdCBhcmJpdGFyeSBjaG9pY2Ugb2Ygc2NhbGUgb2YgZGF0YSwgc2V0IHNvIHRoZXJlIHdpbGwgYmUgYSBnb29kIG1peCBvZiAwJ3MgYW5kIDEncwp4MiA8LSBybm9ybShuLCAtMC41LCAwLjQpCnkgPC0gcmJpbm9tKG4sIDEsIGludmxvZ2l0KGNiaW5kKHJlcCgxLCBuKSwgeDEsIHgyKSAlKiUgYmV0YSkpCmZha2VfMiA8LSBkYXRhLmZyYW1lKHgxLCB4MiwgeSkKYGBgCgojIyMjIEZpdCB0aGUgbW9kZWwgYW5kIHNhdmUgdGhlIGNvZWZmaWNpZW50IGVzdGltYXRlcwoKYGBge3IgfQpmaXRfMiA8LSBzdGFuX2dsbSh5IH4geDEgKyB4MiwgZmFtaWx5PWJpbm9taWFsKGxpbms9ImxvZ2l0IiksIGRhdGE9ZmFrZV8yLCByZWZyZXNoPTApCmJldGFfaGF0IDwtIGNvZWYoZml0XzIpCmBgYAoKIyMjIyBHcmFwaCBkYXRhIGFuZCBkaXNjcmltaW5hdGlvbiBsaW5lcwoKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBwZGYocm9vdCgiTG9naXRHcmFwaHMvZmlncyIsImxvZ2l0Z3JhcGgyLnBkZiIpLCBoZWlnaHQ9NSwgd2lkdGg9NikKYGBgCmBgYHtyIH0KcGFyKG1hcj1jKDMsMywyLDEpLCBtZ3A9YygxLjUsLjUsMCksIHRjaz0tLjAxKQpwbG90KHgxLCB4MiwgYnR5PSJsIiwgdHlwZT0ibiIsIG1haW49IkRhdGEgYW5kIDEwJSwgNTAlLCA5MCUgZGlzY3JpbWluYXRpb24gbGluZXNcbmZyb20gZml0dGVkIGxvZ2lzdGljIHJlZ3Jlc3Npb24iLCBjZXgubWFpbj0uOSkgCnBvaW50cyh4MVt5PT0wXSwgeDJbeT09MF0sIHBjaD0yMCkgICMgZG90cwpwb2ludHMoeDFbeT09MV0sIHgyW3k9PTFdLCBwY2g9MjEpICMgY2lyY2xlcwphYmxpbmUoLWJldGFbMV0vYmV0YVszXSwgLWJldGFbMl0vYmV0YVszXSkKYWJsaW5lKChsb2dpdCgwLjkpIC0gYmV0YVsxXSkvYmV0YVszXSwgLWJldGFbMl0vYmV0YVszXSwgbHR5PTIpCmFibGluZSgobG9naXQoMC4xKSAtIGJldGFbMV0pL2JldGFbM10sIC1iZXRhWzJdL2JldGFbM10sICBsdHk9MikKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgZGV2Lm9mZigpCmBgYAoK