modist 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
modist/__init__.py ADDED
@@ -0,0 +1,21 @@
1
+ """modist - interactive distribution widgets for marimo.
2
+
3
+ Each widget renders a draggable density curve. The synced parameter traits
4
+ make ``mo.ui.anywidget(w).value`` a dict that splats directly into a
5
+ distribution constructor, e.g. ``pm.Normal.dist(**w.value)``.
6
+
7
+ Families
8
+ --------
9
+ - :class:`Normal` -- ``mu`` / ``sigma``
10
+ - :class:`Beta` -- ``alpha`` / ``beta`` (fixed [0, 1])
11
+ - :class:`Gamma` -- ``alpha`` / ``beta`` (shape / rate, edge at 0)
12
+ """
13
+
14
+ from ._base import DistMixin
15
+ from .beta import Beta
16
+ from .gamma import Gamma
17
+ from .normal import Normal
18
+
19
+ __all__ = ["Normal", "Beta", "Gamma", "DistMixin"]
20
+
21
+ __version__ = "0.1.0"
modist/_base.py ADDED
@@ -0,0 +1,60 @@
1
+ """Shared lazy adapters for modist widgets.
2
+
3
+ Each widget exposes a ``params`` dict of its canonical synced traits plus lazy
4
+ ``.scipy`` and ``.pymc`` attributes that construct a frozen scipy distribution
5
+ or a pymc distribution from those params. Imports happen only on first access.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any, Dict
11
+
12
+
13
+ class DistMixin:
14
+ """Provides ``params`` plus lazy ``.scipy`` / ``.pymc`` distribution adapters."""
15
+
16
+ # Family subclasses set these:
17
+ _param_names: tuple[str, ...] = ()
18
+ _dist_name: str = ""
19
+
20
+ @property
21
+ def params(self) -> Dict[str, float]:
22
+ """The canonical parameters of this distribution (the synced traits)."""
23
+ return {name: getattr(self, name) for name in self._param_names}
24
+
25
+ @property
26
+ def scipy(self) -> Any:
27
+ """A frozen ``scipy.stats`` distribution for the current params (lazy import)."""
28
+ from scipy import stats # type: ignore[import-not-found]
29
+
30
+ return self._make_scipy(stats)
31
+
32
+ @property
33
+ def pymc(self) -> Any:
34
+ """A ``pymc`` distribution object from the current params (lazy import)."""
35
+ import pymc as pm # type: ignore[import-not-found]
36
+
37
+ dist = getattr(pm, self._dist_name)
38
+ return dist.dist(**self.params)
39
+
40
+ def create_variable(self, name: str) -> Any:
41
+ """A symbolic pymc distribution whose parameters are named pytensor
42
+ scalars (``{name}_{param}``), ready for ``pm.compile`` with
43
+ ``pytensor.graph.traversal.explicit_graph_inputs``.
44
+
45
+ This is the compiled-input counterpart to :attr:`pymc`/:attr:`params`:
46
+ instead of baking the current values in, each parameter becomes a
47
+ ``pt.scalar(f"{name}_{param}")`` so the graph can be compiled once and
48
+ re-called with new values without rebuilding. E.g.
49
+
50
+ ``w_int.create_variable("intercept")`` gives ``pm.Normal.dist(
51
+ mu=pt.scalar("intercept_mu"), sigma=pt.scalar("intercept_sigma"))``.
52
+ """
53
+ import pymc as pm # type: ignore[import-not-found]
54
+ import pytensor.tensor as pt # type: ignore[import-not-found]
55
+
56
+ kwargs = {p: pt.scalar(f"{name}_{p}") for p in self._param_names}
57
+ return getattr(pm, self._dist_name).dist(**kwargs)
58
+
59
+ def _make_scipy(self, stats: Any) -> Any:
60
+ raise NotImplementedError
modist/beta.py ADDED
@@ -0,0 +1,41 @@
1
+ """Interactive Beta distribution widget on the fixed [0, 1] domain.
2
+
3
+ Drag the mean line to translate (at a fixed concentration) or either ``q25`` /
4
+ ``q75`` square to concentrate / spread out. Synced ``alpha`` / ``beta`` traits
5
+ make ``mo.ui.anywidget(...).value`` splat into ``pm.Beta.dist(**w.value)``.
6
+
7
+ Examples
8
+ --------
9
+ >>> import marimo as mo
10
+ >>> import modist as md
11
+ >>> w = mo.ui.anywidget(md.Beta(alpha=2, beta=2))
12
+ >>> w
13
+ >>> params = w.value # {'alpha': ..., 'beta': ...}
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from pathlib import Path
19
+
20
+ import anywidget
21
+ import traitlets
22
+
23
+ from ._base import DistMixin
24
+
25
+ _ESM = Path(__file__).parent / "static" / "beta.js"
26
+ _CSS = Path(__file__).parent / "styles.css"
27
+
28
+
29
+ class Beta(DistMixin, anywidget.AnyWidget):
30
+ """An interactive Beta distribution with draggable mean and concentration."""
31
+
32
+ _esm = _ESM
33
+ _css = _CSS
34
+ _param_names = ("alpha", "beta")
35
+ _dist_name = "Beta"
36
+
37
+ alpha = traitlets.Float(2.0).tag(sync=True)
38
+ beta = traitlets.Float(2.0).tag(sync=True)
39
+
40
+ def _make_scipy(self, stats):
41
+ return stats.beta(a=self.alpha, b=self.beta)
modist/gamma.py ADDED
@@ -0,0 +1,43 @@
1
+ """Interactive Gamma distribution widget, left edge pinned at 0.
2
+
3
+ Drag the mean line to translate (at a fixed shape) or either ``q25`` / ``q75``
4
+ square to reshape. ``alpha`` is the shape and ``beta`` the rate (pymc / stats
5
+ convention, not the scipy ``scale``). Synced traits make
6
+ ``mo.ui.anywidget(...).value`` splat into ``pm.Gamma.dist(**w.value)``.
7
+
8
+ Examples
9
+ --------
10
+ >>> import marimo as mo
11
+ >>> import modist as md
12
+ >>> w = mo.ui.anywidget(md.Gamma(alpha=2, beta=2))
13
+ >>> w
14
+ >>> params = w.value # {'alpha': ..., 'beta': ...}
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from pathlib import Path
20
+
21
+ import anywidget
22
+ import traitlets
23
+
24
+ from ._base import DistMixin
25
+
26
+ _ESM = Path(__file__).parent / "static" / "gamma.js"
27
+ _CSS = Path(__file__).parent / "styles.css"
28
+
29
+
30
+ class Gamma(DistMixin, anywidget.AnyWidget):
31
+ """An interactive Gamma distribution with draggable mean and shape."""
32
+
33
+ _esm = _ESM
34
+ _css = _CSS
35
+ _param_names = ("alpha", "beta")
36
+ _dist_name = "Gamma"
37
+
38
+ alpha = traitlets.Float(2.0).tag(sync=True)
39
+ beta = traitlets.Float(2.0).tag(sync=True)
40
+
41
+ def _make_scipy(self, stats):
42
+ # scipy gamma parametrizes by (shape, scale); here beta is the rate.
43
+ return stats.gamma(a=self.alpha, scale=1.0 / self.beta)
modist/normal.py ADDED
@@ -0,0 +1,42 @@
1
+ """Interactive Normal distribution widget.
2
+
3
+ A draggable Normal curve: drag the mean line to reposition, or either of the
4
+ ``\u00b11\u03c3`` squares to reshape the spread. The synced ``mu`` / ``sigma``
5
+ traits make ``mo.ui.anywidget(...).value`` splat directly into a distribution
6
+ constructor, e.g. ``pm.Normal.dist(**w.value)``.
7
+
8
+ Examples
9
+ --------
10
+ >>> import marimo as mo
11
+ >>> import modist as md
12
+ >>> w = mo.ui.anywidget(md.Normal(mu=0, sigma=1))
13
+ >>> w
14
+ >>> params = w.value # {'mu': ..., 'sigma': ...}
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from pathlib import Path
20
+
21
+ import anywidget
22
+ import traitlets
23
+
24
+ from ._base import DistMixin
25
+
26
+ _ESM = Path(__file__).parent / "static" / "normal.js"
27
+ _CSS = Path(__file__).parent / "styles.css"
28
+
29
+
30
+ class Normal(DistMixin, anywidget.AnyWidget):
31
+ """An interactive Normal distribution with draggable mean and spread."""
32
+
33
+ _esm = _ESM
34
+ _css = _CSS
35
+ _param_names = ("mu", "sigma")
36
+ _dist_name = "Normal"
37
+
38
+ mu = traitlets.Float(0.0).tag(sync=True)
39
+ sigma = traitlets.Float(1.0).tag(sync=True)
40
+
41
+ def _make_scipy(self, stats):
42
+ return stats.norm(loc=self.mu, scale=self.sigma)
modist/py.typed ADDED
File without changes