Introduction
Model tuning and estimation has evolved from simple extrapolation to sophisticated probabilistic modeling frameworks. In contemporary data science, decision-makers require more than estimates, the need for clear statements about likelihood, risk, and uncertainty are critical to correct decision-making. However, despite the advances in predictive modeling, there is a persistent limitation of making quick and robust decisions from the estimated probabilities. The estimates are often not meaningfully interpreted. This is where the odds_summary function becomes strategically important. It converts probabilistic outputs into structured summaries that directly support:
• decision-making
• risk communication
• model validation
• reproducible research
In practical terms, it turns numbers into evidence.
The function is implemented as follows:
odds_summary(model)
where:
model An R object of estimates from models covered. For now only glm, multimon and polr models are covered.
Implement the function
library(Dyn4cast)
library(tidyverse)
Ordered Logistic Model
library(MASS)
options(contrasts = c("contr.treatment", "contr.poly"))
house.plr <- polr(Sat ~ Infl + Type + Cont, weights = Freq, data = housing)
modelsummary::datasummary_df(odds_summary(house.plr))
| Variables | Coefficient | Std Error | t value | p value | Coef Sig | Odds_ratio | % | Odds Sig | CI_lower | CI_upper |
|---|---|---|---|---|---|---|---|---|---|---|
| InflMedium | 0.57 | 0.10 | 5.41 | 0.00 | 0.566*** | 1.76 | 76.19 | 1.762*** | 1.44 | 2.16 |
| InflHigh | 1.29 | 0.13 | 10.14 | 0.00 | 1.289*** | 3.63 | 262.85 | 3.628*** | 2.83 | 4.66 |
| TypeApartment | -0.57 | 0.12 | -4.80 | 0.00 | -0.572*** | 0.56 | -43.58 | 0.564*** | 0.45 | 0.71 |
| TypeAtrium | -0.37 | 0.16 | -2.36 | 0.02 | -0.366* | 0.69 | -30.66 | 0.693* | 0.51 | 0.94 |
| TypeTerrace | -1.09 | 0.15 | -7.20 | 0.00 | -1.091*** | 0.34 | -66.41 | 0.336*** | 0.25 | 0.45 |
| ContHigh | 0.36 | 0.10 | 3.77 | 0.00 | 0.36*** | 1.43 | 43.37 | 1.434*** | 1.19 | 1.73 |
| Low|Medium | -0.50 | 0.12 | -3.97 | 0.00 | -0.496*** | 0.61 | -39.11 | 0.609*** | -0.74 | -0.25 |
| Medium|High | 0.69 | 0.13 | 5.50 | 0.00 | 0.691*** | 2.00 | 99.51 | 1.995*** | 0.44 | 0.94 |
glm models
counts <- c(18, 17, 15, 20, 10, 20, 25, 13, 12)
outcome <- gl(3, 1, 9)
treatment <- gl(3, 3)
ddc <- data.frame(treatment, outcome, counts) # showing data
glm.D93 <- glm(counts ~ ., data = ddc, family = poisson())
modelsummary::datasummary_df(odds_summary(glm.D93))
| Variables | Coefficient | Std Error | t value | p value | Coef Sig | Odds_ratio | % | Odds Sig | CI_lower | CI_upper |
|---|---|---|---|---|---|---|---|---|---|---|
| (Intercept) | 3.04 | 0.17 | 17.81 | 0.00 | 3.045*** | 21.00 | 2000.00 | 21*** | 14.82 | 28.98 |
| treatment2 | 0.00 | 0.20 | 0.00 | 1.00 | 0 | 1.00 | 0.00 | 1 | 0.67 | 1.48 |
| treatment3 | 0.00 | 0.20 | 0.00 | 1.00 | 0 | 1.00 | 0.00 | 1 | 0.67 | 1.48 |
| outcome2 | -0.45 | 0.20 | -2.25 | 0.02 | -0.454* | 0.63 | -36.51 | 0.635* | 0.42 | 0.94 |
| outcome3 | -0.29 | 0.19 | -1.52 | 0.13 | -0.293 | 0.75 | -25.40 | 0.746 | 0.51 | 1.09 |
anorex.1 <- glm(Postwt ~ Prewt + Treat + offset(Prewt),
family = gaussian, data = anorexia
)
modelsummary::datasummary_df(odds_summary(anorex.1))
| Variables | Coefficient | Std Error | t value | p value | Coef Sig | Odds_ratio | % | Odds Sig | CI_lower | CI_upper |
|---|---|---|---|---|---|---|---|---|---|---|
| (Intercept) | 49.77 | 13.39 | 3.72 | 0.00 | 49.771*** | 4.123994e+21 | 4.123994e+23 | 4.12399379732274e+21*** | 1.647835e+10 | 1.032101e+33 |
| Prewt | -0.57 | 0.16 | -3.51 | 0.00 | -0.566*** | 5.700000e-01 | -4.319000e+01 | 0.568*** | 4.100000e-01 | 7.800000e-01 |
| TreatCont | -4.10 | 1.89 | -2.16 | 0.03 | -4.097* | 2.000000e-02 | -9.834000e+01 | 0.017* | 0.000000e+00 | 6.800000e-01 |
| TreatFT | 4.56 | 2.13 | 2.14 | 0.04 | 4.563* | 9.588000e+01 | 9.487670e+03 | 95.877* | 1.460000e+00 | 6.274970e+03 |
clotting <- data.frame(
u = c(5, 10, 15, 20, 30, 40, 60, 80, 100),
lot1 = c(118, 58, 42, 35, 27, 25, 21, 19, 18),
lot2 = c(69, 35, 26, 21, 18, 16, 13, 12, 12)
)
lot1 <- glm(lot1 ~ log(u), data = clotting, family = Gamma)
modelsummary::datasummary_df(odds_summary(lot1))
| Variables | Coefficient | Std Error | t value | p value | Coef Sig | Odds_ratio | % | Odds Sig | CI_lower | CI_upper |
|---|---|---|---|---|---|---|---|---|---|---|
| (Intercept) | -0.02 | 0.00 | -17.85 | 0.00 | -0.017*** | 0.98 | -1.64 | 0.984*** | 0.98 | 0.99 |
| log(u) | 0.02 | 0.00 | 36.97 | 0.00 | 0.015*** | 1.02 | 1.55 | 1.015*** | 1.01 | 1.02 |
lot2 <- glm(lot2 ~ log(u), data = clotting, family = Gamma)
modelsummary::datasummary_df(odds_summary(lot2))
| Variables | Coefficient | Std Error | t value | p value | Coef Sig | Odds_ratio | % | Odds Sig | CI_lower | CI_upper |
|---|---|---|---|---|---|---|---|---|---|---|
| (Intercept) | -0.02 | 0.00 | -18.02 | 0.00 | -0.024*** | 0.98 | -2.36 | 0.976*** | 0.97 | 0.98 |
| log(u) | 0.02 | 0.00 | 40.92 | 0.00 | 0.024*** | 1.02 | 2.39 | 1.024*** | 1.02 | 1.03 |
x <- rnorm(100)
y <- rpois(100, exp(1 + x))
lm2 <- glm(y ~ x, family = quasi(variance = "mu", link = "log"))
modelsummary::datasummary_df(odds_summary(lm2))
| Variables | Coefficient | Std Error | t value | p value | Coef Sig | Odds_ratio | % | Odds Sig | CI_lower | CI_upper |
|---|---|---|---|---|---|---|---|---|---|---|
| (Intercept) | 0.92 | 0.06 | 14.45 | 0.00 | 0.915*** | 2.50 | 149.71 | 2.497*** | 2.20 | 2.82 |
| x | 1.05 | 0.04 | 29.75 | 0.00 | 1.053*** | 2.87 | 186.74 | 2.867*** | 2.67 | 3.07 |
lm3 <- glm(y ~ x, family = poisson)
modelsummary::datasummary_df(odds_summary(lm3))
| Variables | Coefficient | Std Error | t value | p value | Coef Sig | Odds_ratio | % | Odds Sig | CI_lower | CI_upper |
|---|---|---|---|---|---|---|---|---|---|---|
| (Intercept) | 0.92 | 0.07 | 13.56 | 0.00 | 0.915*** | 2.50 | 149.71 | 2.497*** | 2.18 | 2.84 |
| x | 1.05 | 0.04 | 27.92 | 0.00 | 1.053*** | 2.87 | 186.74 | 2.867*** | 2.66 | 3.09 |
mlogit models
library(mlogit)
data("Fishing", package = "mlogit")
Fish <- dfidx(Fishing, varying = 2:9, shape = "wide", choice = "mode")
## a pure "conditional" model
mml <- mlogit(mode ~ price + catch, data = Fish)
modelsummary::datasummary_df(odds_summary(mml))
| Variables | Coefficient | Std Error | t value | p value | Coef Sig | Odds_ratio | % | Odds Sig | CI_lower | CI_upper |
|---|---|---|---|---|---|---|---|---|---|---|
| (Intercept):boat | 0.87 | 0.11 | 7.64 | 0.00 | 0.871*** | 2.39 | 139.02 | 2.39*** | 1.91 | 2.99 |
| (Intercept):charter | 1.50 | 0.13 | 11.28 | 0.00 | 1.499*** | 4.48 | 347.67 | 4.477*** | 3.45 | 5.81 |
| (Intercept):pier | 0.31 | 0.11 | 2.68 | 0.01 | 0.307** | 1.36 | 35.94 | 1.359** | 1.09 | 1.70 |
| price | -0.02 | 0.00 | -14.54 | 0.00 | -0.025*** | 0.98 | -2.45 | 0.976*** | 0.97 | 0.98 |
| catch | 0.38 | 0.11 | 3.43 | 0.00 | 0.377*** | 1.46 | 45.82 | 1.458*** | 1.18 | 1.81 |
Multinomial Logistic model
For multinomial logistic regression, each of the measure is a data.frame, so the summary is a list of data frames. The summary is for each of the levels of the dependent variable.
library(nnet)
tinom <- multinom(Species ~ Petal.Length + Petal.Width + Sepal.Length
+ Sepal.Width, trace = FALSE, data = iris)
odds_summary(tinom)
$coefficient
Variables versicolor virginica
1 (Intercept) 18.690374 -23.836276
2 Petal.Length 14.244770 23.659779
3 Petal.Width -3.097684 15.135301
4 Sepal.Length -5.458424 -7.923634
5 Sepal.Width -8.707401 -15.370769
$t_value
versicolor virginica
1 0.53445109 -0.66644166
2 0.23665670 0.39128070
3 -0.06809815 0.32950063
4 -0.06072192 -0.08812701
5 -0.05544649 -0.09782845
$Odds_ratio
Variables versicolor virginica
1 (Intercept) 1.309563e+08 4.446690e-11
2 Petal.Length 1.536120e+06 1.885001e+10
3 Petal.Width 4.515366e-02 3.742635e+06
4 Sepal.Length 4.260265e-03 3.620841e-04
5 Sepal.Width 1.653575e-04 2.111348e-07
$Percent_odds
Variables versicolor virginica
1 (Intercept) 1.309563e+10 -1.000000e+02
2 Petal.Length 1.536119e+08 1.885001e+12
3 Petal.Width -9.548463e+01 3.742634e+08
4 Sepal.Length -9.957397e+01 -9.996379e+01
5 Sepal.Width -9.998346e+01 -9.999998e+01
$Coef_sig
versicolor virginica
1 18.69 -23.836
2 14.245 23.66
3 -3.098 15.135
4 -5.458 -7.924
5 -8.707 -15.371
$Odds_sig
versicolor virginica
1 130956302.531 0
2 1536119.713 18850009278.36
3 0.045 3742635.304
4 0.004 0
5 0 0
$p_value
versicolor virginica
1 0.5930295 0.5051288
2 0.8129231 0.6955898
3 0.9457075 0.7417773
4 0.9515807 0.9297757
5 0.9557828 0.9220685
$Confident_interval
Variables Lower versicolor Upper versicolor Lower virginica
1 (Intercept) -49.85184 87.23259 -93.93730
2 Petal.Length -103.72880 132.21834 -94.85441
3 Petal.Width -92.25354 86.05818 -74.89380
4 Sepal.Length -181.64381 170.72696 -184.14699
5 Sepal.Width -316.50312 299.08832 -323.31956
Upper virginica
1 46.26475
2 142.17397
3 105.16440
4 168.29972
5 292.57802
Conclusion
The odds_summary function represents a critical advancement in the practical interpretation of probabilistic estimates within the Dyn4cast environment. Its significance lies not in computation alone, but in converting statistical output into actionable knowledge. In econometric modelling systems, prediction without interpretation is incomplete. The odds_summary function closes that gap.
Attrition
cite this article as:
Nmadu J (2025). odds_summary: Turning Probabilistic Estimates into Clear, Decision-Ready Insights. https://www.jobnmadu.com/r-blog/.
To cite package 'Dyn4cast' in publications use:
Nmadu J (2025). _Dyn4cast: Dynamic Modeling and Machine Learning
Environment_. R package version 11.11.26,
<https://github.com/JobNmadu/Dyn4cast>.
A BibTeX entry for LaTeX users is
@Manual{,
title = {_Dyn4cast: Dynamic Modeling and Machine Learning Environment_},
author = {Job Nmadu},
year = {2025},
note = {R package version 11.11.26},
url = {https://github.com/JobNmadu/Dyn4cast},
}
Welcome to Data Science and Machine Learning!