resumematch-api / stats /bayesian.py
ayushgupta7777's picture
Deploy ResumeMatch Lab API (engine + 9,014-job corpus)
3a41bf2
Raw
History Blame Contribute Delete
1.53 kB
"""Bayesian Beta-Binomial model for 'fraction of jobs where B beats A'.
Each job is a Bernoulli trial: B scores higher than A or it does not. With a
uniform Beta(1,1) prior the posterior is conjugate, so the credible interval and
P(p > 0.5) are available in closed form.
"""
from __future__ import annotations
from dataclasses import dataclass
import numpy as np
from scipy import stats
@dataclass
class BayesResult:
k: int # jobs where B > A
n: int
post_a: float
post_b: float
mean: float # posterior mean of p = P(B beats A on a job)
ci_low: float
ci_high: float
prob_b_beats_a: float # P(p > 0.5 | data)
def posterior_pdf(self, grid: int = 256) -> tuple[np.ndarray, np.ndarray]:
xs = np.linspace(0.0, 1.0, grid)
return xs, stats.beta.pdf(xs, self.post_a, self.post_b)
def beta_binomial(
deltas: np.ndarray,
prior_a: float = 1.0,
prior_b: float = 1.0,
ci: float = 0.95,
) -> BayesResult:
d = np.asarray(deltas, dtype=float)
n = int(len(d))
k = int(np.sum(d > 0))
post_a = prior_a + k
post_b = prior_b + (n - k)
lo = float(stats.beta.ppf((1 - ci) / 2, post_a, post_b))
hi = float(stats.beta.ppf(1 - (1 - ci) / 2, post_a, post_b))
prob_b = float(stats.beta.sf(0.5, post_a, post_b)) # P(p > 0.5)
return BayesResult(
k=k,
n=n,
post_a=post_a,
post_b=post_b,
mean=float(post_a / (post_a + post_b)),
ci_low=lo,
ci_high=hi,
prob_b_beats_a=prob_b,
)