Repository: scipy/scipy

Issue:
ENH: stats.geometric.entropy: implement analytical formula 
The geometric distribution entropy is calculated numerically, and is sometimes incorrect.
```
ss.geom(0.0146).entropy()  # Should be: 5.219397961962304
/home/neil/.cache/pypoetry/virtualenvs/efax-ZssyUsLU-py3.11/lib/python3.11/site-packages/scipy/stats/_distn_infrastructure.py:3814: RuntimeWarning: expect(): sum did not converge
  warnings.warn('expect(): sum did not converge', RuntimeWarning)
Out[2]: 2.9609120114214393
```
It should be calculated analytically as:
```python
-np.log(p) - np.log1p(-p) * (1.0-p) / p
```


Discussion:
This is a good suggestion and the analytical formula looks like a straight forward improvement. Would you be interested in creating a PR?

We had many PRs related to entropy calculations lately, see e.g. https://github.com/scipy/scipy/pull/17857 or https://github.com/scipy/scipy/pull/17742, they could help you especially for the creation of test cases.
Thanks for reporting. I changed the title and label because the method is performing as expected, but an enhancement PR would be most welcome.

```python3
# no problem when shape parameter is less extreme
import numpy as np
from scipy import stats
res = stats.geom(0.146).entropy()
ref = -np.log(p) - np.log1p(-p) * (1.0-p) / p
np.testing.assert_allclose(res, ref)

# warns about failure to converge by design
res = stats.geom(0.0146).entropy()
```
It might be preferable for it to raise an error rather than emit a warning, but that is a more general issue.

I'm curious - can you share the context in which you encountered this shortcoming? I've been interested to see how `entropy` is used given all the work on it lately.
> I changed the title and label because the method is performing as expected

Thanks, that makes sense.

> I'm curious - can you share the context in which you encountered this shortcoming? I've been interested to see how entropy is used given all the work on it lately.

Sure!  I have my own statistical library for exponential family distribution, [efax](https://github.com/NeilGirdhar/efax/).  I [test it against scipy](https://github.com/NeilGirdhar/efax/blob/7dd2fb05c720d35a6bae6ebc641bfe41194b0d50/tests/test_match_scipy.py#L21).

> This is a good suggestion and the analytical formula looks like a straight forward improvement. Would you be interested in creating a PR?

Okay, I'll see if I find the time.

Resolving patch:
diff --git a/scipy/stats/_discrete_distns.py b/scipy/stats/_discrete_distns.py
--- a/scipy/stats/_discrete_distns.py
+++ b/scipy/stats/_discrete_distns.py
@@ -441,6 +441,9 @@ def _stats(self, p):
         g2 = np.polyval([1, -6, 6], p)/(1.0-p)
         return mu, var, g1, g2
 
+    def _entropy(self, p):
+        return -np.log(p) - np.log1p(-p) * (1.0-p) / p
+
 
 geom = geom_gen(a=1, name='geom', longname="A geometric")
 


Test patch:
diff --git a/scipy/stats/tests/test_distributions.py b/scipy/stats/tests/test_distributions.py
--- a/scipy/stats/tests/test_distributions.py
+++ b/scipy/stats/tests/test_distributions.py
@@ -999,6 +999,12 @@ def test_ppf_underflow(self):
         # this should not underflow
         assert_allclose(stats.geom.ppf(1e-20, 1e-20), 1.0, atol=1e-14)
 
+    def test_entropy_gh18226(self):
+        # gh-18226 reported that `geom.entropy` produced a warning and
+        # inaccurate output for small p. Check that this is resolved.
+        h = stats.geom(0.0146).entropy()
+        assert_allclose(h, 5.219397961962308, rtol=1e-15)
+
 
 class TestPlanck:
     def setup_method(self):