Skip to content

Add mode method for Discrete distributions #660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 1, 2025

Conversation

aleicazatti
Copy link
Collaborator

@aleicazatti aleicazatti commented Mar 26, 2025

Description

  • This PR adds find_discrete_mode in internal.optimization. It can be used in the remaining discrete distributions which don't have an analytical or simple solution for the mode. It evaluates the PMF over the distribution's support and returns the value with the highest probability.
  • Applied this helper to the mode() method of ZeroInflatedBinomial, ZeroInflatedNegativeBinomial and ZeroInflatedPoisson.

Checklist

  • Code style is correct (follows ruff and black guidelines)

@codecov-commenter
Copy link

codecov-commenter commented Mar 26, 2025

Codecov Report

Attention: Patch coverage is 53.84615% with 6 lines in your changes missing coverage. Please review.

Project coverage is 49.44%. Comparing base (f25da81) to head (2545d41).
Report is 115 commits behind head on main.

Files with missing lines Patch % Lines
preliz/internal/optimization.py 25.00% 3 Missing ⚠️
preliz/distributions/zi_binomial.py 66.66% 1 Missing ⚠️
preliz/distributions/zi_negativebinomial.py 66.66% 1 Missing ⚠️
preliz/distributions/zi_poisson.py 66.66% 1 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (f25da81) and HEAD (2545d41). Click for more details.

HEAD has 2 uploads less than BASE
Flag BASE (f25da81) HEAD (2545d41)
3 1
Additional details and impacted files
@@             Coverage Diff             @@
##             main     #660       +/-   ##
===========================================
- Coverage   82.23%   49.44%   -32.79%     
===========================================
  Files         101      107        +6     
  Lines        8020     8808      +788     
===========================================
- Hits         6595     4355     -2240     
- Misses       1425     4453     +3028     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment on lines 106 to 125
binomial_raw_mode = Binomial(self.n, self.p).mode()
binomial_mode = (
min(binomial_raw_mode)
if isinstance(binomial_raw_mode, tuple)
else int(binomial_raw_mode)
)
if self.psi == 0:
return 0
elif self.psi == 1:
return binomial_mode
else:
prob_zero = (1 - self.psi) + self.psi * (1 - self.p) ** self.n
prob_binomial_mode = self.psi * np.exp(
gammaln(self.n + 1)
- gammaln(binomial_mode + 1)
- gammaln(self.n - binomial_mode + 1)
+ binomial_mode * np.log(self.p)
+ (self.n - binomial_mode) * np.log1p(-self.p)
)
return 0 if prob_zero > prob_binomial_mode else binomial_mode
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would write this as

Suggested change
binomial_raw_mode = Binomial(self.n, self.p).mode()
binomial_mode = (
min(binomial_raw_mode)
if isinstance(binomial_raw_mode, tuple)
else int(binomial_raw_mode)
)
if self.psi == 0:
return 0
elif self.psi == 1:
return binomial_mode
else:
prob_zero = (1 - self.psi) + self.psi * (1 - self.p) ** self.n
prob_binomial_mode = self.psi * np.exp(
gammaln(self.n + 1)
- gammaln(binomial_mode + 1)
- gammaln(self.n - binomial_mode + 1)
+ binomial_mode * np.log(self.p)
+ (self.n - binomial_mode) * np.log1p(-self.p)
)
return 0 if prob_zero > prob_binomial_mode else binomial_mode
if self.psi == 0:
return 0
else:
bin_dist = Binomial(self.n, self.p)
binomial_raw_mode = bin_dist.mode()
binomial_mode = (
min(binomial_raw_mode)
if isinstance(binomial_raw_mode, tuple)
else int(binomial_raw_mode)
)
if self.psi == 1:
return binomial_mode
else:
prob_zero = (1 - self.psi) + self.psi * (1 - self.p) ** self.n
prob_binomial_mode = self.psi * bin_dist.pdf(binomial_mode)
return 0 if prob_zero > prob_binomial_mode else binomial_mode

But then I realized I was confused about how we have implemented ZIB. When we talked, I had the wrong idea that we have explicitly impleted as a binomial + a zero_inflation. So may we should use the pmf method you suggested like this:

    x_vals = self.xvals("full")
    pmf_vals = self.pdf(x_vals)
    return x_vals[np.argmax(pmf_vals)].astype(int)

This should be implemented as a general method for all the missing discrete variables (unless we have a more direct way for them).

@aleicazatti aleicazatti changed the title Add mode to ZIB Add mode method for Discrete distributions Mar 28, 2025
@aloctavodia
Copy link
Contributor

Could you add this to all the discrete distributions without a mode method? We can later replace some of them with a specific method if available or easy-to-implemet

@aloctavodia aloctavodia merged commit 84d428f into arviz-devs:main Apr 1, 2025
4 checks passed
@aleicazatti aleicazatti deleted the zibinomialmode branch April 4, 2025 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants