skiftet_statistical 0.4.0 → 0.5.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +125 -1
- data/README.md +665 -4
- data/lib/skiftet_statistical/bandit/allocator.rb +23 -0
- data/lib/skiftet_statistical/bandit/decay.rb +3 -0
- data/lib/skiftet_statistical/bandit/draw.rb +16 -4
- data/lib/skiftet_statistical/bandit/posteriors.rb +29 -0
- data/lib/skiftet_statistical/bandit/prior.rb +16 -1
- data/lib/skiftet_statistical/bandit/ranking.rb +32 -0
- data/lib/skiftet_statistical/bandit/summary.rb +11 -0
- data/lib/skiftet_statistical/bandit/weighted_posterior.rb +7 -0
- data/lib/skiftet_statistical/bandit/win_hold.rb +9 -0
- data/lib/skiftet_statistical/beta_function.rb +261 -3
- data/lib/skiftet_statistical/gamma.rb +7 -0
- data/lib/skiftet_statistical/joint_beta.rb +6 -0
- data/lib/skiftet_statistical/shared_credit.rb +191 -0
- data/lib/skiftet_statistical/significance.rb +12 -1
- data/lib/skiftet_statistical/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d1c4747f6510fbb83eb77fe7fe5f6866f18205cf8818670de823e592683ecc4c
|
|
4
|
+
data.tar.gz: e7bd3286b6dbea7d1d490e9a60a18422ddf04767183cbbee17630af4261837f6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 26d379430ba2bfcc78c0d1f25e85076be8deba58b16f987cdc9d35dc80b563c72f2452b68c991b040d693775c6e9c16405093780f593c0dd1b65c086667db033
|
|
7
|
+
data.tar.gz: b7718a211b88ffc453154fd8bd1dad2f797841785f37eb173139cf1da2e336c2c0de72650cd07e5cb15432a605cd4055f59378a1356cc637820bb5dc9bc97d5f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,130 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [0.
|
|
3
|
+
## [0.5.0] - 2026-11-06
|
|
4
|
+
|
|
5
|
+
Three methods, a minor version by the README's Stability section: one that
|
|
6
|
+
lets an estimate whose arms hold a handful of visits be drawn without reading
|
|
7
|
+
one visit as certain, and two for intervals that always hold the figure they
|
|
8
|
+
stand beside. No answer moves: every 0.4.0 call answers as before, for every
|
|
9
|
+
input, key and seed, and every 0.4.0 spec and pin passes unedited. The
|
|
10
|
+
documentation and examples written since 0.4.0, which move no answer either
|
|
11
|
+
(the Stability and Interface contract sections, the last three items), ship in
|
|
12
|
+
this version too. Pure Ruby, no new dependency, Ruby >= 3.1 as before.
|
|
13
|
+
|
|
14
|
+
- `SharedCredit.with_prior(estimate, strength:)`: the estimate under a prior
|
|
15
|
+
pooled from its own credited arms, each arm's evidence topped up to
|
|
16
|
+
`strength` visits, for `LinearValue` to draw from. Why: the sandwich of an
|
|
17
|
+
arm of one visit, or of visits that all hold one value, is exactly 0, so
|
|
18
|
+
`LinearValue` drew that arm as a point, and one visit at 1 against three at
|
|
19
|
+
0, 1 and 0 read P(best) 0.994; at a strength of 20 it reads 0.620 (20 000
|
|
20
|
+
draws at one seed). The prior's mean per part is the credited arms' mean
|
|
21
|
+
weighted by their Kish's sizes; its per-visit covariance is theirs (the
|
|
22
|
+
sandwich times Kish's size) plus their spread around that mean. An arm of
|
|
23
|
+
Kish's size n below the strength k takes e = k - n visits of it: means
|
|
24
|
+
(e * m0 + n * r) / k, covariance block
|
|
25
|
+
(n / k)^2 * cov + (e * V0 + (e * n / k) * g * g^T) / k^2 (g the arm's
|
|
26
|
+
distance from m0), every covariance with another arm scaled by n / k. An arm
|
|
27
|
+
at or above the strength, and an arm with no credit, keeps its means and its
|
|
28
|
+
covariance block bit for bit; where every credited arm is at or above it the
|
|
29
|
+
answer holds the given means, covariance and rounding, so every seeded draw
|
|
30
|
+
and summary is the same. The answer keeps the arms, parts, weight sums and
|
|
31
|
+
Kish's sizes (the evidence's own); its rounding is the given one's or, where
|
|
32
|
+
larger, that of an entry it changed. Exact: plain arithmetic, no draw.
|
|
33
|
+
Refused, as an `ArgumentError`: a strength below 0 or not a finite number,
|
|
34
|
+
an estimate without its weight sums or Kish's sizes, an arm with means but a
|
|
35
|
+
Kish's size of 0, and an estimate whose figures rise past a Float's largest
|
|
36
|
+
value under the prior (figures so small that the prior's products fall below
|
|
37
|
+
Float::MIN read as 0; no estimate `estimate` answers holds them).
|
|
38
|
+
- `BetaFunction.shortest_interval(mass, alpha, beta)`: the shortest interval
|
|
39
|
+
holding `mass` of Beta(alpha, beta). With one peak inside (0, 1) it is the
|
|
40
|
+
highest-density interval, its ends of one density, so it holds the mode; a
|
|
41
|
+
density that falls from 0 (alpha <= 1 <= beta) gives [0, quantile(mass)],
|
|
42
|
+
one that rises to 1 gives [quantile(1 - mass), 1]; a flat density the
|
|
43
|
+
central interval; a U-shaped one the shorter one-sided interval. Why: under
|
|
44
|
+
the flat prior the mode of Beta(1 + c, 1 + n - c) is the observed share
|
|
45
|
+
c / n, and the equal-tailed interval leaves that share out at c = 0 and
|
|
46
|
+
c = n (0 of 1 visit reads 0.013 to 0.842 equal-tailed, 0.000 to 0.776
|
|
47
|
+
shortest), and out of fractional (weighted) counts just above 0; the
|
|
48
|
+
shortest interval holds it for every alpha, beta >= 1. By bisection on the
|
|
49
|
+
side of the peak nearer 0 (a peak above 1/2 is mirrored), no draw. Refused,
|
|
50
|
+
as an `ArgumentError`: a mass not strictly between 0 and 1, then a
|
|
51
|
+
parameter whose `to_f` is not a finite number above 0.
|
|
52
|
+
- `BetaFunction.mean_intervals(items, mass:)`: each item's interval of a mean
|
|
53
|
+
of non-negative values, `{ key => { visits:, mean:, standard_error: } }` in,
|
|
54
|
+
`{ key => [lower, upper] }` out, read as a share of its visits: with the
|
|
55
|
+
visits' second moment q = n * s^2 + m^2, one event is worth u = q / m, the
|
|
56
|
+
item holds k = n * m^2 / q events, and the interval is u times the shortest
|
|
57
|
+
interval of Beta(1 + k, 1 + n - k). So the mean always lies inside, the
|
|
58
|
+
interval is never below 0, its variance in the normal limit is s^2 exactly,
|
|
59
|
+
and a value of 0 or 1 per visit gives the share's own interval. An item with
|
|
60
|
+
a mean of 0 (no event, a standard error of 0) takes the set's value of one
|
|
61
|
+
event (summed squares over summed values of the items above 0) and k = 0,
|
|
62
|
+
so it still has an interval, from 0, which narrows as its visits grow; with
|
|
63
|
+
no item above 0 it has none. An item with a mean above 0 answers from its
|
|
64
|
+
own figures alone, the same in any set. nil for an item without data (no
|
|
65
|
+
visits, no mean or no standard error). Refused, as an `ArgumentError`: a
|
|
66
|
+
mass not strictly between 0 and 1, items that are not a Hash of Hashes, a
|
|
67
|
+
figure given beside the other two that is not a finite number, a negative
|
|
68
|
+
visit count, mean or standard error, and an item whose arithmetic leaves a Float's range (a
|
|
69
|
+
value of one event that is not a finite number above 0, as only extreme
|
|
70
|
+
figures give it, or visits past about 2**53 that all hold one value), named
|
|
71
|
+
in the refusal, rather than answered with an infinite or empty interval.
|
|
72
|
+
- `spec/shared_credit_prior_spec.rb`: the rule by hand and against a Rational
|
|
73
|
+
reference that reads the rows, from rows and from per-arm sums; the answer
|
|
74
|
+
unchanged, and every seeded draw and summary the same, where every arm is at
|
|
75
|
+
or above the strength; an arm at or above it drawing the same values beside a
|
|
76
|
+
topped-up one in an ordinary test; a table over visit counts and goals
|
|
77
|
+
(values of one part, of two parts at uncertain prices, and of amounts): at a
|
|
78
|
+
strength of 20 a version of one visit reads P(best) between 0.41 and 0.77
|
|
79
|
+
(0.98 or more, or 0.02 or less, in 12 of the table's 16 rows on the estimate
|
|
80
|
+
itself) and none of three visits or fewer reaches 0.95 (at most 0.90); two
|
|
81
|
+
versions of one true value read decided (a P(best) at or above 0.95) in 0 of
|
|
82
|
+
250 seeded datasets, against 158 on the estimate itself; the refusals.
|
|
83
|
+
- `spec/beta_function_interval_spec.rb`: the closed forms (Beta(1, b),
|
|
84
|
+
Beta(a, 1), the flat density, a symmetric peak), and over a table of
|
|
85
|
+
parameters and masses the mode held, the mass held, one density at both
|
|
86
|
+
ends and no wider than the equal-tailed interval or any interval of a
|
|
87
|
+
400-point grid; every count of 1 to 60 visits holds its share, where the
|
|
88
|
+
equal-tailed interval misses exactly at 0 and at n; fractional counts just
|
|
89
|
+
above 0; the U-shaped and monotone cases; refusals. For the means: a 0/1
|
|
90
|
+
value gives the share's interval, 200 seeded sets hold every mean and never
|
|
91
|
+
go below 0, the zero item's closed form and its narrowing, no interval in a
|
|
92
|
+
set of zeros, the normal limit, an item whose visits all hold one value, an
|
|
93
|
+
item answered alike alone and beside others, missing data, keys and field
|
|
94
|
+
names, refusals, and the items whose arithmetic leaves a Float's range.
|
|
95
|
+
- The README's Interface contract lists the three methods, each with what it
|
|
96
|
+
takes, answers, guarantees (each *exact*) and refuses, as its doc comment
|
|
97
|
+
states them, and `spec/interface_contract_spec.rb` indexes the examples
|
|
98
|
+
behind their 22 guarantees.
|
|
99
|
+
- Costs, measured as CPU time with YJIT on one development machine while other
|
|
100
|
+
work kept it busy (a load average near 30): `shortest_interval` 0.06 ms for
|
|
101
|
+
a one-sided interval and 0.7 to 2.0 ms with one peak inside (Beta(3, 341) to
|
|
102
|
+
Beta(40 001, 1 160 001)); `mean_intervals` of ten items 9.9 ms;
|
|
103
|
+
`with_prior` of four arms and two parts 0.04 ms.
|
|
104
|
+
- README: a Stability section (a change that moves a seeded answer for the same
|
|
105
|
+
inputs is a major or minor version, as its entry here states, never a patch)
|
|
106
|
+
and an Interface contract section: for each method an application calls,
|
|
107
|
+
what it takes, what it answers, what it guarantees (determinism, which inputs
|
|
108
|
+
move which outputs, accuracy) and what it refuses. The same contract is in
|
|
109
|
+
each method's doc comment.
|
|
110
|
+
- `spec/interface_contract_spec.rb` indexes, method by method, the examples
|
|
111
|
+
that back each stated guarantee, and fails when a cited example is gone, a
|
|
112
|
+
listed method is no longer public, or the README's list differs.
|
|
113
|
+
- New examples: `BetaFunction.regularized` and `.quantile` at parameters from
|
|
114
|
+
1 000 to 50 000 against an exact binomial reference, with the symmetry
|
|
115
|
+
identity, I_(1/2)(a, a) = 1/2, the quantile's inversion and the 2.5 % and
|
|
116
|
+
97.5 % quantiles at the skew-corrected normal approximation (accuracy now
|
|
117
|
+
stated: 4 n ln n machine epsilons, relative, n = alpha + beta);
|
|
118
|
+
`Posteriors#rank`: an added item absent from `may_lead` changes neither the
|
|
119
|
+
others' order, the leader nor the branch over 1 000 keys with `explore` and
|
|
120
|
+
`min_views` set, and a `min_views` below 0 reads as none; `Prior.pooled`
|
|
121
|
+
refuses a negative strength with or without a group; a posterior set is a
|
|
122
|
+
value no reader writes to; `summary` is exact and per item;
|
|
123
|
+
`Draw.from_weights`' refusals; `WeightedPosterior`'s reading of its counts by
|
|
124
|
+
`Integer()`; `JointBeta`'s refusal of a parameter not above 0;
|
|
125
|
+
`sample_ratio_p_value`'s `minimum_total` above its default.
|
|
126
|
+
|
|
127
|
+
## [0.4.0] - 2026-11-03
|
|
4
128
|
|
|
5
129
|
The generic bandit: one posterior set of items, ordered and picked by one rule
|
|
6
130
|
from a key, given uniforms or an RNG, with the odds of what that rule draws
|