|
| 1 | +# binjamin |
| 2 | + |
| 3 | +**Bin width estimation — every major method in one place.** |
| 4 | + |
| 5 | +```python |
| 6 | +import binjamin as bj |
| 7 | + |
| 8 | +# Estimate bin width |
| 9 | +bj.auto(intervals) # good default — max(FD, Sturges) |
| 10 | +bj.freedman_diaconis(intervals) # robust, no distributional assumption |
| 11 | + |
| 12 | +# Bin data and get the artifact |
| 13 | +edges, counts = bj.bin(data) |
| 14 | +edges, counts = bj.bin(data, method='bayesian_blocks') |
| 15 | +``` |
| 16 | + |
| 17 | +## Install |
| 18 | + |
| 19 | +```bash |
| 20 | +pip install binjamin |
| 21 | +``` |
| 22 | + |
| 23 | +## Methods |
| 24 | + |
| 25 | +### Scalar — return a single bin width |
| 26 | + |
| 27 | +| Method | Formula | When to use | |
| 28 | +|---|---|---| |
| 29 | +| `auto` | max(FD, Sturges) | Good general default | |
| 30 | +| `freedman_diaconis` | 2·IQR·n^(-1/3) | Unknown or skewed distribution, outliers present | |
| 31 | +| `scott` | 3.5·σ·n^(-1/3) | Near-normal data, few outliers | |
| 32 | +| `sturges` | (max−min)/(1+log₂n) | Small, near-normal datasets | |
| 33 | +| `rice` | (max−min)/(2·n^(1/3)) | No assumption, simple alternative to FD | |
| 34 | +| `sqrt` | (max−min)/√n | Quick exploratory work | |
| 35 | +| `doane` | Sturges + skewness correction | Skewed or multimodal distributions | |
| 36 | +| `stone` | Cross-validation | Unknown distribution, accuracy over speed | |
| 37 | +| `knuth` | Maximum likelihood | Uniform bins, optimal posterior | |
| 38 | +| `gcd_interval` | GCD of intervals | Integer sequences, regularly-sampled data | |
| 39 | + |
| 40 | +### Variable-width — returns bin edges |
| 41 | + |
| 42 | +| Method | When to use | |
| 43 | +|---|---| |
| 44 | +| `bayesian_blocks` | Non-stationary event data; density varies across the domain | |
| 45 | + |
| 46 | +### Binning artifact |
| 47 | + |
| 48 | +| Function | Returns | |
| 49 | +|---|---| |
| 50 | +| `bin(data, method='auto')` | `(edges, counts)` — edges and observation counts per bin | |
| 51 | + |
| 52 | +## Usage |
| 53 | + |
| 54 | +All scalar methods take a 1-D array-like and return a `float`: |
| 55 | + |
| 56 | +```python |
| 57 | +import numpy as np |
| 58 | +import binjamin as bj |
| 59 | + |
| 60 | +intervals = np.diff(np.sort(event_times)) # inter-event intervals |
| 61 | + |
| 62 | +bj.freedman_diaconis(intervals) # → float |
| 63 | +bj.scott(intervals) # → float |
| 64 | +bj.knuth(intervals) # → float |
| 65 | +``` |
| 66 | + |
| 67 | +`bayesian_blocks` takes event positions (not intervals) and returns edges: |
| 68 | + |
| 69 | +```python |
| 70 | +edges = bj.bayesian_blocks(event_times, p0=0.05) |
| 71 | +# edges: array of variable-width bin boundaries |
| 72 | +# p0: false-positive rate for new change points (lower = fewer blocks) |
| 73 | +``` |
| 74 | + |
| 75 | +`gcd_interval` takes integer-valued positions: |
| 76 | + |
| 77 | +```python |
| 78 | +bj.gcd_interval([0, 60, 120, 180, 300]) # → 60.0 |
| 79 | +``` |
| 80 | + |
| 81 | +## Choosing a method |
| 82 | + |
| 83 | +**Start with `auto`.** If the result looks wrong: |
| 84 | + |
| 85 | +- Heavy tails or outliers → `freedman_diaconis` |
| 86 | +- Known near-normal distribution → `scott` |
| 87 | +- Skewed data → `doane` |
| 88 | +- Event rate changes over time → `bayesian_blocks` |
| 89 | +- Integer sequence with known regular spacing → `gcd_interval` |
| 90 | +- Need provably optimal bins, willing to wait → `knuth` or `stone` |
| 91 | + |
| 92 | +## License |
| 93 | + |
| 94 | +[MIT](LICENSE) |
0 commit comments