This guide covers how to get data into Latent Scope: supported file formats, how columns are detected, working with text vs. image columns, downloading datasets from HuggingFace, and importing embeddings you already have. It addresses issue #60.
Every dataset lives in a directory under LATENT_SCOPE_DATA. Ingesting writes a
normalized input.parquet plus a meta.json describing the columns; every later
step reads from there.
export LATENT_SCOPE_DATA=~/latent-scope-data # where datasets are storedls-ingest accepts CSV, Parquet, JSON, JSONL, and XLSX — or a directory
of images (see §4):
ls-ingest mydataset --path /path/to/data.csv --text_column textmydataset— the dataset id (its directory name underLATENT_SCOPE_DATA).--path— the source file. Format is inferred from the extension (.csv,.parquet,.json,.jsonl,.xlsx); a directory is ingested as an image dataset. If omitted,ls-ingestlooks forinput.csvinside the dataset directory.--text_column— which column to embed by default. If omitted, the UI lets you pick later; you can always embed a different column withls-embed.
Ingest normalizes the data to input.parquet and writes meta.json with
per-column metadata (type, unique-value counts, categories, numeric/date
extents, and image flags).
import latentscope as ls
import pandas as pd
ls.init("~/latent-scope-data")
df = pd.read_csv("data.csv")
ls.ingest("mydataset", df, text_column="text")During ingest each column is typed and recorded in meta.json:
| Detected type | How | Used for |
|---|---|---|
string |
text columns | embedding input, labels, filtering |
number |
numeric columns | color-by, filtering (min/max extent stored) |
date |
parseable dates | filtering (min/max extent stored) |
image (binary) |
cells that decode as images (PIL) — HF {"bytes":…, "path":…} dicts or raw bytes |
image embedding + sprite atlas |
image (url) |
string column where every value is an http… URL ending in png/jpg/jpeg/webp/svg/gif |
image embedding + thumbnails |
String columns with ≤100 distinct values also store their categories and counts, which powers the categorical filters in Explore.
Tip: an image column is never auto-selected as the default text column.
Pick a small, fast embedding model to start (runs on CPU):
ls-embed mydataset text transformers-BAAI___bge-small-en-v1.5List available models with ls-list-models. Providers include
sentence-transformers/HuggingFace (transformers-…), OpenAI, Cohere, Voyage,
Mistral, Together, and any OpenAI-compatible endpoint. After embedding, the
embedding metadata records token statistics (total / mean / min / max tokens
per document) when a local tokenizer is available — surfaced in the Setup UI
(issue #77).
Null, empty, and NaN cells in the text column are handled gracefully (replaced with a single space so row alignment is preserved) rather than crashing the run.
From a folder of images — pass a directory to ls-ingest and it builds the
dataset for you: each file's bytes land in an image column, with filename
(the default text column), date (file mtime), and size_kb alongside for
labeling and color-by. Non-recursive; picks up png/jpg/jpeg/webp/gif:
ls-ingest myshots --path ~/DesktopFrom a table — an image column is detected from raw bytes / HF-style
{"bytes": …} dicts or http… URLs (see §2). A column of local file paths
is treated as plain strings — read the bytes into the frame yourself, or use
the folder form above.
Image columns are embedded with an image/multimodal model (e.g. CLIP):
ls-embed mydataset image transformers-openai___clip-vit-base-patch32Images that fail to decode are replaced with a 1×1 black placeholder so a few bad rows don't break the run. After you create a scope, generate the sprite atlas so the map can show representative images as you zoom in:
ls-sprite-atlas mydataset scopes-001 image --resolutions 64,128,256In Explore, image datasets default to the heatmap and transition to the image grid (then individual points) as you zoom — no separate toggle. See the Images step in Setup to plan resolutions and preview size estimates.
ColBERT models store one vector per token and search with MaxSim, which is great
for fine-grained retrieval. They use the colbert- prefix:
ls-embed mydataset text colbert-answerdotai___answerai-colbert-small-v1Per-token vectors are stored fp16 in LanceDB; the Explore search box uses
late-interaction search automatically for these embeddings. A complete,
CPU-friendly, runnable example lives in
examples/colbert_quickstart/ — run
bash examples/colbert_quickstart/run.sh to ingest a tiny topical dataset, embed
it, and verify MaxSim search returns on-topic results.
Many ready-made Latent Scope datasets are published on HuggingFace:
ls-download-dataset enjalot/ls-datavis-misunderstood datavis-misunderstood \
~/latent-scope-data/datavis-misunderstoodThis pulls the scope, embeddings, umaps, clusters, and metadata so you can open the dataset in Explore immediately without re-running the pipeline.
If your input file already contains an embedding column (a list/array per row), import it directly instead of recomputing:
# ls-embed-importer <dataset_id> <embedding_column> <model_id> <text_column>
ls-embed-importer mydataset my_vectors my-precomputed-model textThis reads the column from input.parquet, stores it as an embedding set in
LanceDB, and records metadata so UMAP/cluster/scope can use it like any other
embedding.
Latent Scope is designed for interactive exploration, not billion-row warehousing (issue #33). Practical guidance:
- Sweet spot: up to a few hundred thousand rows. The published demos are in the 50k–100k range and stay snappy in the browser. Datasets up to ~500k are workable; beyond that, expect slower UMAP/cluster steps and a heavier map.
- Embedding is the main time cost. On CPU, use a small model
(
transformers-BAAI___bge-small-en-v1.5) and start with a sample of your data. A GPU dramatically speeds up both embedding and (optionally) UMAP/clustering — see gpu-acceleration.md. - ColBERT and image datasets are heavier. ColBERT stores one vector per token (fp16 in LanceDB), so multi-vector datasets use far more disk than dense ones; image atlases add tiled sprite pyramids. Budget disk accordingly and keep these datasets smaller while iterating.
- Everything is flat files. Each step writes to your dataset directory, so you can inspect sizes as you go and delete intermediate runs you don't need.
To work with a large source, sample it down at ingest time (e.g. slice the
DataFrame before ls.ingest(...)) and scale up once the pipeline settings look
right.
Once data is ingested and embedded:
ls-umap mydataset embedding-001 25 0.1
ls-cluster mydataset umap-001 5 3 0.0 --method hdbscan
ls-scope mydataset embedding-001 umap-001 cluster-001 default "My scope" "description"
ls-serve $LATENT_SCOPE_DATA # open http://localhost:5001 to explore- clustering.md — clustering methods (EVoC / HDBSCAN / KMeans /
GMM), the
--cluster_oninput choice, and named experiment runs. - gpu-acceleration.md — optional cuML GPU acceleration and
the
LATENT_SCOPE_DEVICEcontrol. - exploring.md — color-by, the Compare page's shared selection, the experiment gallery, and the curation (post-1.0) status.
See CLAUDE.md for the agent quickstart and
DEVELOPMENT.md for the developer setup.