odds_summary: Turning Probabilistic Estimates into Clear, Decision-Ready Insights
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
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!
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.