Skip to main content
import neuroencoder as ne
ne.explore(embeddings)
Launches Apple’s open-source Embedding Atlas inline in Jupyter. Defaults: time coloring, side panels hidden — a clean canvas you can pan and zoom.
Apple Embedding Atlas: EEGBCI motor imagery embeddings
Each tight cluster is one recording; the time gradient inside each cluster shows the trajectory of brain state during that recording.

Try it yourself

Reproduce the figure above on a public EEG dataset:
import mne, torch, neuroencoder as ne
from neuroencoder import MRL
from mne.datasets import eegbci
mne.set_log_level("ERROR")

model = MRL.from_pretrained()

all_emb, all_files = [], []
for s in [1, 2, 3, 4, 5]:
    for r in [1, 2, 4, 6, 8, 10]:
        f = eegbci.load_data(subjects=s, runs=r, verbose=False)[0]
        raw = mne.io.read_raw_edf(f, preload=True, verbose=False)
        eegbci.standardize(raw)
        # Sliding window: 30s epochs, 1s hop
        images = ne.preprocess(
            raw.get_data(),
            sfreq=raw.info["sfreq"],
            channel_names=raw.ch_names,
            stride_seconds=1.0,
        )
        emb = model.predict(images, dim=192)
        all_emb.append(emb)
        all_files.extend([f.split("/")[-1]] * len(emb))

ne.explore(torch.cat(all_emb), filename=all_files, epoch_seconds=1.0)

Defaults

Coloringtime (auto-set)
Side charts panelhidden
Bottom data tablehidden
Themelight
ProjectionUMAP
Pass show_charts=True and / or show_table=True to re-enable the panels.

Standalone server (no Jupyter)

ne.serve(embeddings, filename=filenames, port=5055)
Launches a localhost web app with the same defaults as ne.explore() — light theme, panels hidden, time coloring already applied. Opens in your default browser. Blocks until Ctrl-C.

With filenames

Tag points with their source recording:
ne.explore(embeddings, filename=["recording_A.edf"] * 50 + ["recording_B.edf"] * 50)

Sliding-window vs non-overlapping

For dense temporal visualizations, pass stride_seconds to ne.preprocess. Smaller strides give many more points per recording, revealing smooth trajectories.
# Default - non-overlapping 30s epochs
ne.preprocess(data, sfreq=256, channel_names=ch_names)

# Sliding 30s windows hopping every 1 second
ne.preprocess(data, sfreq=256, channel_names=ch_names, stride_seconds=1.0)

Projection methods

ne.explore(embeddings, method="umap")    # default
ne.explore(embeddings, method="tsne")
ne.explore(embeddings, method="pca")

Standalone server

For very large datasets, save to parquet and serve via the embedding-atlas CLI:
embedding-atlas embeddings.parquet --x x --y y --neighbors neighbors

Static plot

ne.plot(embeddings)                  # by time (default, viridis)
ne.plot(embeddings, color="cluster") # DBSCAN auto-clustering
ne.plot(embeddings, color=labels)    # custom values