"To deal with hyper-planes in a 14 dimensional space, visualize a 3D space and say 'fourteen' very loudly. Everyone does it." - Geoff Hinton
HyperTools is designed to facilitate dimensionality reduction-based visual explorations of high-dimensional data. The basic pipeline is to feed in a high-dimensional dataset (or a series of high-dimensional datasets) and, in a single function call, reduce the dimensionality of the dataset(s) and create a plot. The package is built atop many familiar friends, including matplotlib, scikit-learn and seaborn. Our package was featured in 2017 on Kaggle's now-retired "No Free Hunch" blog (archived copy). For a general overview, you may find this talk useful (given as part of the MIND Summer School at Dartmouth).
HyperTools 1.0 modernizes the toolbox while keeping the familiar API:
-
Interactive plotting (optional):
hyp.plot(..., backend='plotly')renders interactive figures. With the defaultbackend='auto', HyperTools automatically uses plotly on Google Colab and Kaggle (where interactive figures work best and plotly is preinstalled) and matplotlib everywhere else — existing workflows are unchanged. The two backends produce visually matched output: identical colors, line/marker styles and sizes, format strings, and the signature cube/square framing. -
Multicolored lines: passing continuous values (or a per-observation matrix) as
huetogether with a line format colors each trajectory continuously along its length, on both backends. -
hyp.apply_model: a unified stack → fit-once → unstack core for applying any scikit-learn style model (or pipeline of models) across one or more datasets, withreturn_model=Truefor reuse on held-out data. -
Mixture-model ("soft") clustering:
clusterandplotsupportGaussianMixture,BayesianGaussianMixture,LatentDirichletAllocation, andNMF.hyp.clusterreturns per-observation membership proportions, andhyp.plotcolors each observation by blending component colors according to its mixture weights. -
Richer coloring: the
hueargument now accepts categorical labels, continuous values, or entire matrices (e.g. mixture proportions or model weights), which are mapped to colors via the newmat2colorshelper (from hypertools.plot.colors import mat2colors). -
Nested-list input:
hyp.plot([[a, b], [c]])colors datasets by their outermost grouping and renders more deeply nested datasets with thinner, fainter lines. -
Hull surfaces (optional):
hyp.plot(..., surface=True)overlays a smooth, lit surface over each dataset's convex hull — a filled outline in 2D, or a shaded, Taubin-smoothed "blob" in 3D. A dict of scalar options (e.g.surface={'alpha': 0.6}) customizes all surfaces at once; a list of bools/dicts (e.g.surface=[{'alpha': 0.2}, {'alpha': 0.8}]) controls alpha/color/lighting/smoothing per dataset. -
Morph animation:
hyp.plot(datasets, animate='morph')treats each dataset as a point cloud and morphs smoothly between them (Hungarian- matched, smoothstep-eased), holding on each one along the way;rotationsaccepts a per-segment list for independent camera control over each hold and transition, andsurface=Truecomposes with it to morph a lit hull instead of raw points. Seeexamples/plot_shape_morph.pyandexamples/animate_surface_morph.py. -
More animation styles, and 2-D animation: in addition to
True/'parallel',animate='spin'keeps all the data drawn and spins the camera,'serial'reveals each dataset one at a time in list order, and'window'(hyp.plot(traj, animate='window', focused=2)) slides a fixed- length, fully-opaque window along each trajectory. Every style except'spin'(which is inherently a 3-D camera move) now also works forndims=2data, using a fixed (non-rotating) viewport. -
hyp.Pipeline: a scikit-learn-stylePipelinechainsmanip/normalize/reduce/align/clusterstages, fit once and reused on new data without refitting --hyp.plot(A, ..., return_model=True)returns a bundle whose'pipeline'entry can be passed back in ashyp.plot(B, pipeline=bundle['pipeline'])to apply the exact same fitted transformation to a structurally-identical datasetB. -
hyp.manipand manip chaining:hyp.manip(data, model='ZScore')applies a manipulation (Normalize,ZScore,Smooth,Resample) to each dataset.SmoothandResamplerun independently per dataset (kernels never cross dataset boundaries);ZScoreandNormalizealso transform each dataset separately but fit one shared set of statistics across all datasets in a list (likenormalize='across'). Alistof specs chains several manipulations as aPipeline, andhyp.plot(data, manip=[...])runs the chain at the canonicalmanipstage -- first, beforenormalize/reduce/align/cluster-- e.g.hyp.plot(data, manip=[{'model': 'Smooth', 'kwargs': {'kernel_width': 5}}, 'ZScore'], reduce='PCA'). -
Autoencoder reducers (optional):
hyp.reduce/hyp.plot(..., reduce= 'Autoencoder')supports six torch-backed autoencoder reducers (Autoencoder,DeepAutoencoder,SparseAutoencoder,ConvolutionalAutoencoder,SequenceAutoencoder,VariationalAutoencoder), gated behindpip install "hypertools[torch]". -
gensim text vectorizers/semantic models (optional):
hyp.plot(texts, vectorizer='Word2Vec', semantic=None, reduce='PCA')(andhyp.tools.text2mat) addWord2Vec/Doc2Vec/FastTextvectorizers andLdaModel/LsiModel/HdpModelsemantic models, gated behindpip install "hypertools[gensim]"-- passsemantic=Nonewith an embedding vectorizer likeWord2Vecsince the default LDA semantic stage rejects negative embedding values. Note that non-default vectorizers train on the defaultcorpus='wiki'corpus the first time, which can take a couple of minutes even for tiny inputs; passcorpus=a list of your own documents to train on those instead. -
LSL streaming (optional):
hyp.io.lsl_stream(type='EEG')resolves a live Lab Streaming Layer stream and wraps it forhyp.plot(..., stream_init=200, stream_chunk=20), gated behindpip install "hypertools[lsl]". -
hyp.predict/hyp.impute:hyp.predict(data, model='Kalman', t=10)forecaststnew rows continuing each dataset.Kalman,GaussianProcess,AutoRegressor, andARIMAwork with the base install (pykalman/statsmodels are core deps);Laplaceneedspip install "hypertools[predict]"(skaters) andChronosneedspip install "hypertools[predict-hf]".hyp.impute(data, model='PPCA')fills missing (NaN) values in place; theKalmanimputer also works with the base install. Both takereturn_model=Truefor reuse. -
Kaggle loader:
hyp.load('kaggle/uciml/iris')downloads a public Kaggle dataset anonymously viakagglehub, gated behindpip install "hypertools[kaggle]". -
Density shading (optional):
hyp.plot(..., density=True)overlays a subtle KDE "glow" behind the data (a 2D heatmap or 3D volumetric cloud) showing where each dataset's points concentrate; off by default. -
Colorbars:
hyp.plot(..., colorbar=True)draws a colorbar matching whatever color mapping is already in use — a continuous gradient for a numerichue, or a segmented, labeled bar for discrete groups/clusters. -
MultiIndex DataFrames: a DataFrame with a row
MultiIndexis expanded automatically into one leaf trace per index combination plus a thicker, more opaque mean trace per level of grouping, colored by the top-level index value. -
Per-dataset animation trails:
chemtrails/precog/bullettimeeach accept a list of bools (one per dataset) so different datasets in the same animation can show different trail styles, in addition to a single bool applied to all of them. -
More
hyp.loadsources: in addition to the built-in example datasets and local files,hyp.loadnow resolves Hugging Face dataset ids, Google Sheets/Drive links, Dropbox links, and arbitrary URLs, plus more local file formats (.npy/.npz,.csv/.tsv/.txt,.json,.parquet,.mat,.xlsx/.xls). -
Faster and cleaner:
import hypertoolsis ~3.5x faster (heavy dependencies load lazily); plotting no longer mutates global matplotlib settings; the unreliable result cache was removed; HDBSCAN now comes from scikit-learn (no extra dependency); packaging follows current standards (pyproject.toml, Python 3.10–3.13). -
Retired legacy arguments: the long-deprecated
group(usehue),model/model_params(usereduce), andalign(method=...)/align=True(usealign='hyper') arguments were removed and now raise errors instead of being silently accepted.cluster'sndims=is no longer a standalone reduction step: it is only forwarded to thereduce=stage, and a warning fires if it is passed withoutreduce=. Legacy data: geo files saved by hypertools ≥0.8 (pickle-format) still load — the internal unpickle-only shim reads them and returns their raw data, with retired arguments translated or skipped with a warning on replay. Older pre-0.8deepdish/HDF5-format geos cannot be read directly under HyperTools' required NumPy 2 (thedeepdishreader is unmaintained and imports only undernumpy<2);hyp.loaddetects them and raises a message explaining the one-time out-of-process conversion:# in a throwaway environment, convert an old .geo to a modern format once python -m venv /tmp/dd && /tmp/dd/bin/pip install "numpy<2" deepdish /tmp/dd/bin/python -c "import deepdish as dd, numpy as np; \ d = dd.io.load('old.geo'); np.savez('old_converted.npz', \ **{'data': np.asarray(d['data'], dtype=object)})" # then load old_converted.npz with hypertools as usual
Check the repo of Jupyter notebooks from the HyperTools paper (note: those notebooks predate the 1.0 API described below). For up-to-date, runnable examples covering every 1.0 feature, see the example gallery in the docs.
To install the latest stable version run:
pip install hypertools
To install the latest unstable version directly from GitHub, run:
pip install -U git+https://github.com/ContextLab/hypertools.git
Or alternatively, clone the repository to your local machine:
git clone https://github.com/ContextLab/hypertools.git
Then, navigate to the folder and type:
pip install -e .
(These instructions assume that you have pip installed on your system)
- python>=3.10
- scikit-learn>=1.4.0
- pandas>=2.2.0
- seaborn>=0.13.0
- matplotlib>=3.8.0
- scipy>=1.13.0
- numpy>=2.0.0
- umap-learn>=0.5.5, numba>=0.59
- pydata-wrangler>=0.5.1 (data-wrangling core)
- pykalman>=0.11, statsmodels>=0.14 (Kalman/ARIMA forecasting and imputation)
- requests, dill, ipympl
- ffmpeg (for saving animations)
All Python dependencies are declared in pyproject.toml and installed
automatically by pip. The base install covers all core functionality
(plotting, dimensionality reduction, alignment, clustering, normalization,
and Kalman/ARIMA forecasting + imputation) and therefore pulls in the
full scientific stack (NumPy, SciPy, pandas, scikit-learn, matplotlib,
seaborn, UMAP/Numba, statsmodels, pykalman, ipympl, pydata-wrangler); it is
not a minimal footprint. Heavier optional model families are separated into
extras that add features on request (mix and match, e.g.
pip install "hypertools[interactive,torch]"):
interactive-- plotly + kaleido, forhyp.plot(..., backend='plotly'). Note: kaleido 1.x renders static images (PNG/PDF, and the frames of saved plotly animations) through a headless Chrome/Chromium, which it does not install. If image/GIF/video export from the plotly backend fails with a browser/Chromium error, install Chrome or Chromium (or runplotly_get_chrome). Interactive/HTML plotly output needs no browser.text-- transformer/sentence-transformers text embeddings (via datawrangler'shfextra)predict-- the skatersLaplaceensemble forecaster forhyp.predict(Kalman,GaussianProcess,AutoRegressor, andARIMAalready work with the base install)predict-hf-- the Hugging FaceChronosforecaster forhyp.predictio--.xlsxsupport forhyp.loaddensity3d-- smooth 3-Ddensity=Trueiso-surfaces (scikit-image)torch-- the six autoencoder reducers (reduce='Autoencoder'and variants)kaggle--hyp.load('kaggle/<owner>/<dataset>')lsl--hyp.io.lsl_stream(...)(Lab Streaming Layer input)gensim--Word2Vec/Doc2Vec/FastTextvectorizers andLdaModel/LsiModel/HdpModelsemantic modelsdev-- test/development dependencies (pip install -e ".[dev]")
Check out our readthedocs page for further documentation, complete API details, and additional examples.
We wrote a short JMLR paper about HyperTools, which you can read here, or you can check out a (longer) preprint here. We also have a repository with example notebooks from the paper here.
Please cite as:
Heusser AC, Ziman K, Owen LLW, Manning JR (2018) HyperTools: A Python toolbox for gaining geometric insights into high-dimensional data. Journal of Machine Learning Research, 18(152): 1--6.
Here is a bibtex formatted reference:
@ARTICLE{heusser2018hypertools,
author = {Andrew C. Heusser and Kirsten Ziman and Lucy L. W. Owen and Jeremy R. Manning},
title = {HyperTools: a Python Toolbox for Gaining Geometric Insights into High-Dimensional Data},
journal = {Journal of Machine Learning Research},
year = {2018},
volume = {18},
number = {152},
pages = {1-6},
url = {http://jmlr.org/papers/v18/17-434.html}
}If you'd like to contribute, please first read our Code of Conduct.
For specific information on how to contribute to the project, please see our Contributing page.
CI runs on every push via GitHub Actions (badge at the top of this page).
To test HyperTools locally, install pytest (pip install -e ".[dev]") and run pytest in the HyperTools folder.
See here for more examples.
import numpy as np
import hypertools as hyp
# two random-walk "datasets" (rows = observations, columns = features)
walk = lambda seed: np.cumsum(np.random.default_rng(seed).standard_normal((300, 10)), axis=0)
list_of_arrays = [walk(1), walk(2)]
list_of_labels = ['A'] * 300 + ['B'] * 300 # one label per observation
hyp.plot(list_of_arrays, animate=True, hue=list_of_labels)import numpy as np
import hypertools as hyp
# rotated, noisy views of one shared trajectory
rng = np.random.default_rng(0)
base = np.cumsum(rng.standard_normal((300, 3)), axis=0)
list_of_arrays = [base @ np.linalg.qr(rng.standard_normal((3, 3)))[0]
+ 0.05 * rng.standard_normal(base.shape) for _ in range(3)]
hyp.plot(list_of_arrays, align='hyper')Soft ("mixture-model") clustering, new in 1.0 -- each point's color blends its component memberships:
import numpy as np
import hypertools as hyp
# three overlapping point clouds
rng = np.random.default_rng(0)
array = np.vstack([rng.standard_normal((100, 3)) + offset
for offset in ([0, 0, 0], [4, 0, 0], [0, 4, 0])])
hyp.plot(array, 'o', cluster='GaussianMixture', n_clusters=3)New in 1.0: overlay a smooth, lit surface over each dataset's convex hull:
import numpy as np
import hypertools as hyp
rng = np.random.default_rng(0)
blob_a = rng.standard_normal((100, 3))
blob_b = rng.standard_normal((100, 3)) + [4, 0, 0]
hyp.plot([blob_a, blob_b], '.', surface=True)import numpy as np
import hypertools as hyp
rng = np.random.default_rng(0)
list_of_arrays = [np.cumsum(rng.standard_normal((200, 20)), axis=0)
for _ in range(3)]
hyp.describe(list_of_arrays, reduce='PCA', max_dims=14)






