BinRule
Mojo struct 🡭
BinRule
@memory_only
struct BinRuleLet a rule choose how many bins a sample gets, instead of naming a count. Pass one to bin_edges(), shared_bin_edges(), or histogram()’s bins.
A fixed count is a guess that is wrong in both directions: ten bins oversmooth a five-thousand-point sample and turn a twelve-point one into a comb. Every rule below reads the sample and answers for you.
With n the sample size, ptp the sample’s max minus its min,
sigma its standard deviation and IQR its interquartile range,
each rule states a bin width, which is then turned into a count:
| Rule | Width | Reads |
|---|---|---|
SQRT |
ptp / sqrt(n) |
size only |
STURGES |
ptp / (log2(n) + 1) |
size only |
RICE |
ptp / (2 * cbrt(n)) |
size only |
SCOTT |
cbrt(24 * sqrt(pi) / n) * sigma |
spread |
FREEDMAN_DIACONIS |
2 * IQR / cbrt(n) |
spread, robustly |
AUTO |
min(max(fd, sqrt / 2), sturges) |
both |
The definitions and the width-to-count step are numpy.histogram’s,
so bin_edges(data, BinRule.SCOTT) and
numpy.histogram_bin_edges(data, bins="scott") give the same number
of bins over the same range. Matching an implementation rather than
a paper is deliberate: the papers state widths up to a constant and
say nothing about rounding a width back into a whole number of bins,
which is where two faithful readings of the same rule diverge.
Widths rather than counts throughout, again following numpy. The
three size-only rules are usually quoted as counts – ceil(sqrt(n))
bins, ceil(log2(n)) + 1 bins – and for those the two forms agree.
They stop agreeing for the two spread-based rules, which have no
count form at all, and a single representation means AUTO can
compare Freedman-Diaconis against Sturges without converting first.
Which to reach for:
AUTOunless you have a reason. It is Freedman-Diaconis with two guards, and it is what numpy’s ownbins="auto"does.SQRTis what most spreadsheets do. Cheap and unopinionated.STURGESassumes the data are roughly normal and undercounts badly past a few thousand points;RICEgrows faster and is the usual stand-in.SCOTTis optimal for normal data but follows the standard deviation, so one far outlier widens every bin.FREEDMAN_DIACONISuses the IQR instead, which outliers barely move – at the cost of collapsing to a single bin when more than half the sample is identical and the IQR is 0.
Aliases
SQRT = BinRule(Int(0)):ptp / sqrt(n)–ceil(sqrt(n))bins. Spreadsheet default.STURGES = BinRule(Int(1)):ptp / (log2(n) + 1)– assumes normality; undercounts past a few thousand points.RICE = BinRule(Int(2)):ptp / (2 * cbrt(n))– no normality assumption; grows faster than Sturges.SCOTT = BinRule(Int(3)):cbrt(24 * sqrt(pi) / n) * sigma– optimal for normal data, but one outlier widens every bin.FREEDMAN_DIACONIS = BinRule(Int(4)):2 * IQR / cbrt(n)– robust to outliers; collapses to one bin when the IQR is 0.AUTO = BinRule(Int(5)):min(max(fd, sqrt / 2), sturges)– the default choice.
Implemented traits
AnyType, Copyable, Deinitable, ImplicitlyCopyable, Movable
Methods
__init__
fn def __init__(out self, value: Int)Prefer the SQRT/STURGES/RICE/SCOTT/ FREEDMAN_DIACONIS/AUTO comptime constants over constructing one directly.
Args:
- value (
Int): 0 for SQRT, 1 for STURGES, 2 for RICE, 3 for SCOTT, 4 for FREEDMAN_DIACONIS, 5 for AUTO. - self (
Self)
Returns:
Self
__eq__
fn def __eq__(self, other: Self) -> BoolWhether both name the same rule.
Args:
- self (
Self) - other (
Self): The rule to compare against.
Returns:
Bool: True when they match.
__ne__
fn def __ne__(self, other: Self) -> BoolWhether the two name different rules.
Args:
- self (
Self) - other (
Self): The rule to compare against.
Returns:
Bool: True when they differ.
name
fn def name(self) -> StringThis rule’s constant name, for the error messages that have to say which rule produced an unusable bin count.
Args:
- self (
Self)
Returns:
String: “SQRT”, “STURGES”, “RICE”, “SCOTT”, “FREEDMAN_DIACONIS”,
“AUTO”, or “BinRule(