Skip to main content

Visual Explanations via Region Annotation (VERA)

BSD 3-Clause License

VERA is a Python library for generating static visual explanations of arbitrary two-dimensional point embeddings, usually produced by t-SNE, UMAP, or other dimensionality reduction techniques. The goal of VERA is to quickly and automatically generate explanations that give users a high-level overview of the embedding space without users having to load and interact with sometimes clunky interactive tools.

Why VERA?

Two-dimensional embeddings are widely used in exploratory data analysis to visualize and understand complex data. While tools like scatter plots help users spot clusters or patterns, interpreting what these structures actually mean is still a largely manual process.

VERA automates this process. By identifying informative features and characteristic regions in the embedding, VERA produces multiple small, static visualizations that summarize the main structural patterns -- allowing users to quickly understand and communicate key insights.

VERA is particularly useful in workflows where:

  • The original features are at least partially human-interpretable (e.g., tabular data).

  • Users seek a quick overview of structure without manual exploration.

  • There is a need to generate publication-ready summaries of embeddings.

Limitations

  • As a general purpose embedding explanation tool, VERA requires at least some human-interpretable features in order to generate embeddings (although embeddings can be generated from arbitrary feature sets). While VERA could be extended to text, image, and video data with domain-specific adaptations, this is currently not supported.

  • Descriptive explanations generate annotations that include many features. If your data set contains a large number of features, the annotations may also become very long, rendering the visualization unusable. In this case, we suggest limiting explanations to a subset of features.

Installation

vera is published as vera-explain on PyPI and conda-forge. Install it with pip

pip install vera-explain

or with conda

conda install -c conda-forge vera-explain

The conda-forge package requires Python 3.11 or newer; the PyPI package also supports Python 3.10.

A hello world example

Getting started with vera is very simple. First, we'll load up some data using scikit-learn.

from sklearn import datasets

iris = datasets.load_iris()
x = iris["data"]

Next, we have to generate an embedding of the data. We'll use openTSNE here, but any embedding method will do.

import openTSNE

embedding = openTSNE.TSNE().fit(x)

Then, we'll import and run the following commands to explain the embedding.

import vera

region_annotations = vera.an.generate_region_annotations(x, embedding)
contrastive_explanations = vera.explain.contrastive(region_annotations)
descriptive_explanations = vera.explain.descriptive(region_annotations)

vera.pl.plot_annotations(contrastive_explanations)
vera.pl.plot_annotations(descriptive_explanations)

Binary and presence features

Before regions are extracted, every column is expanded into indicator variables: continuous columns are discretized into bins, categorical and boolean columns are one-hot encoded. For a binary feature -- a gene that is either expressed or not, a flag that is either set or not -- this yields two indicators, gene is True and gene is False, and the negative one is annotated as a region of its own. A region labelled "gene is absent" is rarely something you want on a plot.

Name such columns in indicator_columns and they are used as they are, each described by its positive case alone. For a table of nothing but indicators -- a gene presence matrix, say -- pass "all":

presence = expression > 0

region_annotations = vera.an.generate_region_annotations(
    presence, embedding, indicator_columns="all"
)

In a mixed table, list the columns to be read this way; everything else is discretized or one-hot encoded as before:

region_annotations = vera.an.generate_region_annotations(
    features, embedding, indicator_columns=["CD3", "CD4"]
)

Indicator columns have to be of boolean dtype. Nothing is converted on the way in: a 0/1 column is rejected rather than read as a flag -- cast it with .astype(bool) if that is what it means. A column with no positive samples is dropped.

Missing values are supported through pandas' nullable boolean dtype:

import pandas as pd

presence = (expression > 0).astype("boolean")
presence[expression.isna()] = pd.NA

A sample with no measurement is not flagged, and it is not unflagged either. It takes no part in shaping the variable's region, it is never reported as a member, and it is left out of the rates the variable is scored on rather than counted against it. Where several variables are annotated together, a sample belongs to the group if every variable flags it, and is excluded as soon as one does not -- whether or not the others measured it.

Continuous and categorical columns are treated the same way: a missing value falls in no bin and in no category, and says so in each of them.

The column name is what appears on the plot, so a CD3 column is annotated CD3. To annotate a column with something else, pass a mapping instead of a list:

region_annotations = vera.an.generate_region_annotations(
    features, embedding, indicator_columns={"CD3": "CD3 expressed"}
)

Each indicator forms a group of one, so filter_uninformative judges these variables on whether their region says anything: an indicator is dropped when its rule matches, or its region contains, at least uninformative_max_sample_coverage (default 0.95) of the data.

Being a group of one also means there is nothing for vera.explain.contrastive to set against anything else, and it returns no panels for a table of nothing but indicators. Reach for vera.explain.descriptive, or vera.pl.plot_regions to see the regions one at a time.

For an annotation that a label cannot express -- a threshold, a range, one of several categories -- build the variable yourself and use it as the column name. A column named by a Variable is passed through the expansion step unchanged, and its rule becomes the annotation:

import pandas as pd
from vera.rules import IntervalRule
from vera.variables import ContinuousVariable, IndicatorVariable

base = ContinuousVariable("CD3", values=expression["CD3"].values)
rule = IntervalRule(lower=2.5, value_name="CD3")
above_threshold = IndicatorVariable(base, rule, (base.values > 2.5).astype(float))

features = pd.DataFrame({above_threshold: above_threshold.values})
region_annotations = vera.an.generate_region_annotations(features, embedding)

Citation

If you make use of vera for your work we would appreciate it if you would cite the paper:

\article{Policar2024
  title={VERA: Generating Visual Explanations of Two-Dimensional Embeddings via Region Annotation}, 
  author={Pavlin G. Poličar and Blaž Zupan},
  year={2024},
  eprint={2406.04808},
  archivePrefix={arXiv},
  primaryClass={cs.LG}
}

Metadata

Release files for vera-explain 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vera-explain 1.2.0
File Size Uploaded Provenance Actions
vera_explain-1.2.0.tar.gz 104.8 kB With provenance Details for vera_explain-1.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for vera-explain 1.2.0
File Interpreter ABI Platform Actions
vera_explain-1.2.0-py3-none-any.whl Python 3 none any Details for vera_explain-1.2.0-py3-none-any.whl

Total release size: 174.7 kB

Release files / vera_explain-1.2.0.tar.gz

Download URL vera_explain-1.2.0.tar.gz
Size 104.8 kB
Tags Source
SHA-256 checksum
How to use checksums
e28ceb5e1040470bf0f40d7612b656bb249a766479ebce737ae6e073af8ba50e
BLAKE2b-256 checksum
How to use checksums
065a5ed1e318febb657c2630ab47cb7221f7dffb20036539b35b28887801df7c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Release files / vera_explain-1.2.0-py3-none-any.whl

Download URL vera_explain-1.2.0-py3-none-any.whl
Size 69.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d9e6cb7646d2d12f9d3f0c092f9a5708fae2b02074da5a14002dfa7d442ba502
BLAKE2b-256 checksum
How to use checksums
c29245a9f11935475d85ab70a1af80acbec02604ee710be27b6182e85c796dcd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Security for vera-explain 1.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for vera_explain-1.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page