Add multinomial distribution functions - #145
Open
nehill197 wants to merge 9 commits into
Open
Conversation
Adds ran_multinom(), log_lik_multinom(), dev_multinom(), and res_multinom(). Data are expected in long format (one row per category per trial, identified by a `group` argument) so that residuals work one-per-observation with embr's posterior_predictive_check(), and so that a signed residual is always well defined per cell without needing an "extended sign" for a multi-category row. dev_multinom()/log_lik_multinom() rely on the identity that a multinomial is equivalent to independent Poissons conditional on the trial total, letting each category's contribution be computed from dev_pois()/log_lik_pois() alone. dev_multinom() is verified against glmnet's multinomial deviance() and log_lik_multinom() against dmultinom().
ave()'s non-standard FUN lookup fails when log_lik_multinom() is evaluated inside a Bayesian model-fitting framework's derived-quantity expressions; replaced with an equivalent table()-based computation. Also reject `group` values mapping to a single row in chk_multinom_group() (shared by ran_multinom()/log_lik_multinom(), and so reached by res_multinom(simulate = TRUE)): a singleton group can't represent a real multinomial trial, and silently produces a degenerate result (rmultinom() just returns `size`) rather than erroring. This is the shape you get if the function ends up evaluated once per row instead of once for the whole data vector.
Note in the shared group param docs that each group must contain at least 2 rows, matching the check already enforced in chk_multinom_group(). Also drop an unneeded implementation aside from res_multinom()'s docs, and add a simulate-many-and-check-mean/sd test for res_multinom(), matching the pattern used for every other distribution's res_*() function.
Reject groups whose row count differs from the mode across the call (a trial silently missing a row for ordinary multinomial logistic regression), and move the NA-group check into each calling function so it runs before group is used to split the data.
…d res_multinom(simulate = FALSE) Trim verbose internal comments and roxygen prose added for the multinomial functions, and move NA-propagation notes to the size/prob docs where they actually apply. dev_multinom() now takes group and validates size/prob against it, and res_multinom() validates group even when simulate = FALSE, so a prob vector that doesn't sum to 1 can no longer silently produce a wrong result on either path.
The most common cause is size/prob/group being passed one row at a time rather than as full vectors, so point at that instead of "group lost rows".
…ctions dev_multinom()/log_lik_multinom()/res_multinom() now validate size/prob/ group lengths via chk_compatible_lengths() before recycling with rep_len(), matching ran_multinom() and skewnorm()/skewlnorm() -- previously a non-recyclable length mismatch (e.g. prob shorter than x) was silently mis-recycled by rep_len() instead of erroring, producing a wrong or misleading result. chk_multinom_group()'s prob-sum check no longer skips validation entirely whenever any prob in a group is NA; it now checks the known values don't already exceed 1, mirroring the size check's existing NA-tolerant logic. Also share one group-index split (multinom_split()) across chk_multinom_group()/multinom_row_na()/ran_multinom() instead of recomputing it up to three times per call, and add direct tests for the two internal helpers.
Use expect_no_error() instead of expect_null() where the intent is "this call succeeds," not "this call returns NULL." Add a regression test for chk_multinom_group()'s documented tie-break rule (favours the smaller row count on an exact tie).
nehill197
marked this pull request as ready for review
August 10, 2026 18:04
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
dev_multinom(),log_lik_multinom(),ran_multinom(), andres_multinom()for long-format multinomial data (one row per category per trial), using agroupargument to identify trial membership.groupconsistently across all four functions: noNA, at least 2 rows per trial, matchingsizeandprobsumming to 1 within a trial, and a consistent row count across groups (catches the common mistake of anew_exprbeing evaluated row-by-row instead of vectorized, and rows lost from a trial).Test plan
devtools::test()— 3252/3252 passingdevtools::check()— 0 errors, 0 warnings, 0 notes