Orbit plots of general (information-theoretic) association between variables, for exploratory data analysis and feature selection.
Project description
arbital
arbital draws every variable in a dataset as an orbit around a target, so that the strength, shape, direction, redundancy, and usefulness of each association are readable from a single figure.
Why correlation fails
The first look at a new dataset is almost always a correlation matrix, and a correlation matrix can only see straight lines (or, for rank correlation, monotone curves). Three common structures defeat it entirely:
- Non-monotone relationships. A parabola or a sine wave has r ≈ ρ ≈ 0. A correlation screen ranks it with the noise and discards it.
- Interactions. If y = x₁·x₂, each parent is individually uncorrelated with y, since the other parent randomly flips the sign of its effect, yet y is strongly dependent on both.
- Pooled groups (Simpson's paradox). In the Palmer penguins data, bill depth versus body mass has r = −0.47 pooled across species, while the relationship is positive within every species. The correlation is not just weak; its sign is wrong.
Mutual information detects all three, but raw mutual information is expressed in nats, has no upper bound in common use, and is painful to read in bulk. So in practice it rarely replaces the correlation matrix as the first look.
What arbital adds
arbital (Association Radii By Information-Theoretic Association Learning) measures every variable against the target with both families of statistics and encodes the comparison geometrically:
| Element | Meaning |
|---|---|
| radius | total association with the target, logarithmic in mutual information (closer is stronger) |
| ghost marker + tether | where correlation alone would place the variable, and the association it misses |
| orbit eccentricity | the nonlinear share ν (a circle is a purely monotone relationship) |
| angle | associated variables huddle together; a tight cluster is a redundant group, a lone variable carries independent information |
| marker size | marginal gain under greedy mRMR feature selection by default (largest = strong and non-redundant); switch to total association or a constant with size="rinfo"/"uniform" |
| colour | direction of the relationship (signed Spearman ρ; neutral for nominal categories) |
The figure is interactive (Plotly), and every quantity in it is available as a plain table.
Five lines
import arbital
from arbital import datasets
cars = datasets.load_mpg() # bundled, loads offline
arbital.orbits(cars, target="mpg").to_html("mpg.html")
Weight, displacement, cylinders and horsepower huddle in one angular sector, one redundant
engine-size group, of which the selection keeps only weight (largest marker), while
model_year sits alone: weaker individually, but independent information.
Installation
pip install arbital # from PyPI (core: NumPy only)
pip install git+https://github.com/aaronbyrnephd/arbital # latest from GitHub
pip install "arbital[plotly,datasets]" # optional extras
For development: clone, pip install -e ".[test]", pytest tests/.
The core package depends only on NumPy. to_html() needs no plotly install (it emits Plotly
JSON plus a CDN script); the plotly extra is only for live Figure objects. Four example
datasets (mpg, penguins, titanic, tips) are bundled and load offline; with the datasets
extra, datasets.load("<name>") fetches any of seaborn's ~20 bundled datasets by name
(diamonds, iris, planets, taxis, flights, ...); datasets.available() lists them all.
Quick start
import arbital
from arbital import datasets
cars = datasets.load_mpg() # or datasets.load("<any seaborn dataset>")
space = arbital.orbits(cars, target="mpg")
space.to_html("mpg.html") # standalone interactive figure
space.table() # per-feature metrics as dicts
space.to_df() # the same as a pandas DataFrame
space.selection() # the greedy mRMR walkthrough, one dict per pick
# feature selection on its own, without a figure:
arbital.select_features(cars, target="mpg")
# don't know the target yet? let arbital suggest one:
arbital.select_target(cars) # index of the column most associated with the rest
# categorical target and fields, with bootstrap uncertainty arcs:
arbital.orbits(datasets.load_titanic(), target="survived",
selection=True, uncertainty=True)
Any 2-D NumPy array or object exposing .columns and array semantics (including a pandas
DataFrame) is accepted; string columns are detected as categorical and rows with missing
values are dropped.
Features
- One bounded scale for everything. Mutual information is converted to Linfoot's informational coefficient r_I ∈ [0, 1), directly comparable with |r| and |ρ|.
- Mixed data types. Continuous, categorical, and mixed pairs each get the appropriate mutual-information estimator, selected automatically.
- Honest feature selection, three ways to look at it. Greedy mRMR with r_I as both
relevance and redundancy; marginal gains are recorded at pick time. Run it standalone with
select_features(), get the full walkthrough (relevance, redundancy, gain per pick) fromOrbitSystem.selection(), or just number the markers in the figure withselection=True. - Chance-calibrated selection, without moving the plot. Finite-sample k-NN estimators
report small positive MI even for independent pairs; arbital measures that chance level
against shuffled copies of the target and draws it as a boundary circle to compare markers
against directly. Feature selection ranks on a separately chance-subtracted relevance, so
unassociated features score zero and cannot win a pick. r_info itself, and every
marker's position, is always the estimated value (calibrating both would double-count
chance against the same boundary). Pearson and Spearman get the same treatment
(
pearson_sig/spearman_sig) from the same permutation draws and the sameconfidence(default 0.95, one tunable parameter for every channel), a different, complementary question from whether r_info clears chance, not a differently-calibrated one.calibrate=Falseskips all of it. This is a heuristic screen, not a rigorous test: no multiple-comparison correction is applied across the features you screen at once, and the vignette's Assumptions and calibration caveats section lists every place this trades statistical rigor for a usable default. - Uncertainty on demand.
uncertainty=Truebootstraps a standard error for every r_info and draws it as a radial band through the marker; a diamond flags bands that cross the chance boundary (that association could be noise). - Meaningful angles. Three layouts:
"spread"(default; embedding gaps with a readable minimum separation),"embed"(raw embedding),"ordered"(even spacing). - Auto target.
target=Nonepicks the variable the rest of the data revolves around (select_target()is the same logic, usable on its own before you've decided what to plot). - Light by design. Pure-NumPy estimators you can read in one sitting; no compiled
extensions; figures as plain JSON. Rows are subsampled beyond
max_samples=2000because the k-NN estimator is O(n²); all estimators take a seed for reproducibility.
Documentation
- Usage vignette: the full tour, synthetic
ground truth, redundancy groups and the angle axis, transformed and lagged variables,
interactions, categorical data, Simpson's paradox, selection, uncertainty, and a glossary of
every quantity the hover/table/
to_df()report. Published on every push tomain; also downloadable from the repo. - API reference: every public function
and class, generated from the docstrings below with pdoc; the same
content
help(arbital.orbits)gives you at a prompt, browsable. - Tutorial notebook: hands-on version of the vignette.
- Repository layout:
src/arbital/measures.py(estimators),geometry.py(orbit mapping, layout, selection),plot.py(Plotly JSON),datasets.py(bundled data),tests/(property-based suite),demo/(vignette builder).
See CONTRIBUTING.md to get involved and RELEASING.md for the release process.
Theory
Mutual information I(x;y) is estimated per pair: Kraskov–Stögbauer–Grassberger k-nearest neighbour for continuous pairs, Ross's estimator for continuous–categorical, and a plug-in estimator for categorical pairs. It is placed on the correlation scale with Linfoot's
r_I = √(1 − e^(−2I)),
which equals |ρ| exactly for a bivariate Gaussian. The Gaussian statement is a calibration, not an assumption: r_I is a fixed monotone transform of mutual information, so it ranks variables identically to MI for any distribution, is zero under independence, approaches 1 as the relationship becomes deterministic, and (for continuous pairs) depends only on the copula, since mutual information there is exactly the negative entropy of the copula density (Joe, 1989). The radius is the residual uncertainty
d = √(1 − r_I²) = e^(−I),
so the radial axis is logarithmic in information: every factor of e closer to the centre is one more nat. The orbit's eccentricity is the nonlinear share
ν = 1 − (r_mono / r_I)², with r_mono = max(|r|, |ρ|),
the fraction of total dependence a monotone description misses (ν is a composite defined for this package, not a standard named statistic). The periastron of each orbit sits at d(r_I), the apastron at d(r_mono), so the ghost/tether is ν drawn to scale. Angles come from classical MDS on the feature–feature r_I matrix; marker sizes from greedy mRMR selection; uncertainty arcs from a nonparametric bootstrap.
References
- Pearson, K. (1895). Notes on regression and inheritance in the case of two parents. Proceedings of the Royal Society of London, 58, 240–242. (Pearson r)
- Spearman, C. (1904). The proof and measurement of association between two things. American Journal of Psychology, 15(1), 72–101. (Spearman ρ)
- Shannon, C. E. (1948). A mathematical theory of communication. Bell System Technical Journal, 27, 379–423. (mutual information)
- Torgerson, W. S. (1952). Multidimensional scaling: I. Theory and method. Psychometrika, 17(4), 401–419. (classical MDS for the angular layout)
- Linfoot, E. H. (1957). An informational measure of correlation. Information and Control, 1(1), 85–89. (the r_I scale)
- Efron, B. (1979). Bootstrap methods: another look at the jackknife. The Annals of Statistics, 7(1), 1–26. (uncertainty arcs)
- Joe, H. (1989). Relative entropy measures of multivariate dependence. Journal of the American Statistical Association, 84(405), 157–164. (r_I depends only on the copula)
- Kraskov, A., Stögbauer, H., & Grassberger, P. (2004). Estimating mutual information. Physical Review E, 69(6), 066138. (KSG estimator, continuous pairs)
- Peng, H., Long, F., & Ding, C. (2005). Feature selection based on mutual information: criteria of max-dependency, max-relevance, and min-redundancy. IEEE Transactions on Pattern Analysis and Machine Intelligence, 27(8), 1226–1238. (mRMR)
- Ross, B. C. (2014). Mutual information between discrete and continuous data sets. PLoS ONE, 9(2), e87357. (mixed continuous–categorical estimator)
License and citation
MIT, see LICENSE. If you use arbital in research, see CITATION.cff (GitHub's "Cite this
repository" button).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file arbital-0.1.2.tar.gz.
File metadata
- Download URL: arbital-0.1.2.tar.gz
- Upload date:
- Size: 75.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
822968b3af6702f500b7c3d67480220ba04f28e053df34189b7c46abf4a1c52a
|
|
| MD5 |
43ca4230839697c8356a889986a1e240
|
|
| BLAKE2b-256 |
b5e5cde60a810565af1bc33811d259adf6aef8a2ecb6b10ad8b9ae7f346a672d
|
Provenance
The following attestation bundles were made for arbital-0.1.2.tar.gz:
Publisher:
release.yml on aaronbyrnephd/arbital
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
arbital-0.1.2.tar.gz -
Subject digest:
822968b3af6702f500b7c3d67480220ba04f28e053df34189b7c46abf4a1c52a - Sigstore transparency entry: 2212184372
- Sigstore integration time:
-
Permalink:
aaronbyrnephd/arbital@cf9608d3d05ba350ba70337bdc02724ba4091d45 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/aaronbyrnephd
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@cf9608d3d05ba350ba70337bdc02724ba4091d45 -
Trigger Event:
push
-
Statement type:
File details
Details for the file arbital-0.1.2-py3-none-any.whl.
File metadata
- Download URL: arbital-0.1.2-py3-none-any.whl
- Upload date:
- Size: 58.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
058c5827abac48089c1c4fc0854fb55a34b9fd9b106b2028ee61ea11a3a96f59
|
|
| MD5 |
f00e5cd290df2261e7eb9a95bcc95b7d
|
|
| BLAKE2b-256 |
880b655e7727556fb257ac43b3fbe3eb42f09df953c28a97cde5b260fd1a1532
|
Provenance
The following attestation bundles were made for arbital-0.1.2-py3-none-any.whl:
Publisher:
release.yml on aaronbyrnephd/arbital
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
arbital-0.1.2-py3-none-any.whl -
Subject digest:
058c5827abac48089c1c4fc0854fb55a34b9fd9b106b2028ee61ea11a3a96f59 - Sigstore transparency entry: 2212184398
- Sigstore integration time:
-
Permalink:
aaronbyrnephd/arbital@cf9608d3d05ba350ba70337bdc02724ba4091d45 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/aaronbyrnephd
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@cf9608d3d05ba350ba70337bdc02724ba4091d45 -
Trigger Event:
push
-
Statement type: