How is slope calculated in a calibration plot?

I am using logistic regression with white cell count and temperature as predictors and hospital admission>3 days as the outcome of interest. I'm using the rms package in R to assess calibration (curve generated by val.prob) and having some difficulty interpreting the output. My specific questions are, how is slope calculated and how is the intercept calculated? The smooth line logistic calibration I believe is generated by locally estimated scatterplot smoothing (loess). And qualitatively I believe this is a poorly calibrated model. However, I don't understand how to interpret the slope or intercept given that the slope differs along the curve. Any help would be appreciated.

It transforms your predicted probabilities to log odds ratios (or logit) and then uses that as a dependent variable to fit a logistic regression. If your prediction can clearly separate the labels, you would get an intercept of 0 and slope 1..

If we check the vignette of the function:

Given a set of predicted probabilities p or predicted log odds logit, and a vector of binary outcomes y that were not used in developing the predictions p or logit, val.prob computes the following indexes and ... chi- square with 2 d.f. for testing unreliability (H0: intercept=0, slope=1), its P-value, ..., Intercept, and Slope

Of course, bear in mind this is one of many test, and we can use the example from the vignette below, and the slope and intercept values are great because it's simulated:

library(rms)

set.seed(1)
n <- 200
x1 <- runif(n)
x2 <- runif(n)
x3 <- runif(n)
logit <- 2*(x1-.5)
P <- 1/(1+exp(-logit))
y <- ifelse(runif(n)<=P, 1, 0)
d <- data.frame(x1,x2,x3,y)
f <- lrm(y ~ x1 + x2 + x3, subset=1:100)
pred.logit <- predict(f, d[101:200,])
phat <- 1/(1+exp(-pred.logit))

res = val.prob(phat, y[101:200], m=20, cex=.5)

res[c("Intercept","Slope")]
Intercept      Slope
0.05228721 0.95651781


It's the same as doing:

glm(y[101:200] ~ log(phat/(1-phat)),family="binomial")

Call:  glm(formula = y[101:200] ~ log(phat/(1 - phat)), family = "binomial")

Coefficients:
(Intercept)  log(phat/(1 - phat))
0.05229               0.95652

• "It transforms your predicted probabilities to log odds ratios (or logit) and then uses that as a dependent variable to fit a logistic regression." I'm trying to follow this - is it not the other way around - the predicted probabilities is the independent variable and the observed outcome the dependent variable? Commented May 6, 2022 at 13:04

Although the answer by StupidWolf leads to the correct numbers, it is a special case because the calibration slope is very close to 1.

The calibration slope does not come from the model with the logit as the only predictor. Rather, it uses a model like y ~ 1, offset = logit(p), where p is the predicted probability. Thus, it sets the logit(p) coefficient to 1. I realized this when I tried to calculate the intercept and slope for a model following the same steps but it did not work.

Call:  glm(formula = outcome ~ linear_pred,
family = binomial,
data = df)

Coefficients:
(Intercept)        linear_pred
0.3398             0.6917


I then used CalibrationCurves::valProbggplot() to check the results. Although the slope was the same, I got an intercept = 0.43, not 0.34.

Note that the slope was further from 1 this time.

Then I used the linear predictor (logit) as an offset as suggested here and here (supplementary material) and ran:

Call:  glm(formula = outcome ~ 1,
family = binomial,
data = df,
offset = linear_pred)

Coefficients:
(Intercept)
0.43


Getting the same intercept as in the valProbggplot() output.

Bottom line: the calibration intercept and calibration slope do not come from the same model. As the calibration slope gets closer to one, however, the intercept from the model with the linear predictor as the only independent variable approximates the actual calibration intercept. However, the true intercept comes from the model using the linear predictor as an offset.