Interactive 3D vector visualization with query UI for vector databases
Project description
anywidget-vector
Interactive 3D vector visualization for Python notebooks.
Works with Marimo, Jupyter, VS Code, Colab, anywhere anywidget runs.
Features
- Universal: One widget, every notebook environment
- 6D Visualization: X, Y, Z position + Color, Shape, Size encoding
- Backend-agnostic: NumPy, pandas, Qdrant, Chroma, Pinecone, Weaviate, LanceDB, or raw dicts
- Interactive: Orbit, pan, zoom, click, hover, box select
- Customizable: Color scales, shapes, sizes, themes
- Performant: Instanced rendering for large point clouds
Installation
uv add anywidget-vector
Quick Start
from anywidget_vector import VectorSpace
widget = VectorSpace(points=[
{"id": "a", "x": 0.5, "y": 0.3, "z": 0.8, "label": "Point A", "cluster": 0},
{"id": "b", "x": -0.2, "y": 0.7, "z": 0.1, "label": "Point B", "cluster": 1},
{"id": "c", "x": 0.1, "y": -0.4, "z": 0.6, "label": "Point C", "cluster": 0},
])
widget
Data Sources
Dictionary
widget = VectorSpace.from_dict({
"points": [
{"id": "a", "x": 0, "y": 0, "z": 0},
{"id": "b", "x": 1, "y": 1, "z": 1},
]
})
NumPy Arrays
import numpy as np
positions = np.random.randn(100, 3)
widget = VectorSpace.from_numpy(positions)
pandas DataFrame
import pandas as pd
df = pd.DataFrame({
"x": [0.1, 0.5, 0.9],
"y": [0.2, 0.6, 0.3],
"z": [0.3, 0.1, 0.7],
"cluster": ["A", "B", "A"],
"size": [0.5, 1.0, 0.8],
})
widget = VectorSpace.from_dataframe(
df,
color_col="cluster",
size_col="size",
)
UMAP / t-SNE / PCA
import umap
embedding = umap.UMAP(n_components=3).fit_transform(high_dim_data)
widget = VectorSpace.from_umap(embedding, labels=labels)
Qdrant
from qdrant_client import QdrantClient
client = QdrantClient("localhost", port=6333)
widget = VectorSpace.from_qdrant(client, "my_collection", limit=5000)
ChromaDB
import chromadb
client = chromadb.Client()
collection = client.get_collection("embeddings")
widget = VectorSpace.from_chroma(collection)
Pinecone
from pinecone import Pinecone
pc = Pinecone(api_key="...")
index = pc.Index("my-index")
widget = VectorSpace.from_pinecone(index, limit=5000)
Weaviate
import weaviate
client = weaviate.Client("http://localhost:8080")
widget = VectorSpace.from_weaviate(client, "Article", limit=5000)
LanceDB
import lancedb
db = lancedb.connect("~/.lancedb")
table = db.open_table("vectors")
widget = VectorSpace.from_lancedb(table, limit=5000)
Visual Encoding
6 Dimensions
| Dimension | Visual Channel | Example |
|---|---|---|
| X | Horizontal position | x coordinate |
| Y | Vertical position | y coordinate |
| Z | Depth position | z coordinate |
| Color | Hue/gradient | Cluster, score |
| Shape | Geometry | Category, type |
| Size | Scale | Importance, count |
Color Scales
widget = VectorSpace(
points=data,
color_field="score", # Field to map
color_scale="viridis", # Scale: viridis, plasma, inferno, magma, cividis, turbo
color_domain=[0, 100], # Optional: explicit range
)
Shapes
widget = VectorSpace(
points=data,
shape_field="category",
shape_map={
"type_a": "sphere", # Available: sphere, cube, cone,
"type_b": "cube", # tetrahedron, octahedron, cylinder
"type_c": "cone",
}
)
Size
widget = VectorSpace(
points=data,
size_field="importance",
size_range=[0.02, 0.15], # Min/max point size
)
Interactivity
Events
widget = VectorSpace(points=data)
@widget.on_click
def handle_click(point_id, point_data):
print(f"Clicked: {point_id}")
@widget.on_hover
def handle_hover(point_id, point_data):
if point_id:
print(f"Hovering: {point_id}")
@widget.on_selection
def handle_selection(point_ids, points_data):
print(f"Selected {len(point_ids)} points")
Selection
widget.selected_points # Current selection
widget.select(["a", "b"]) # Select points
widget.clear_selection() # Clear
widget.selection_mode = "box" # Switch to box-select mode
Camera
widget.camera_position # Get position [x, y, z]
widget.camera_target # Get target [x, y, z]
widget.reset_camera() # Reset to default
widget.focus_on(["a", "b"]) # Focus on specific points
Distance Metrics
Compute distances and visualize similarity relationships between points.
Supported Metrics
| Metric | Description |
|---|---|
euclidean |
Straight-line distance (L2 norm) |
cosine |
Angle-based distance (1 - cosine similarity) |
manhattan |
Sum of absolute differences (L1 norm) |
dot_product |
Negative dot product (higher = closer) |
Color by Distance
widget.color_by_distance("point_a")
widget.color_by_distance("point_a", metric="cosine")
Find Neighbors
neighbors = widget.find_neighbors("point_a", k=5)
# Returns: [("point_b", 0.1), ("point_c", 0.2), ...]
neighbors = widget.find_neighbors("point_a", threshold=0.5)
Show Connections
widget.show_neighbors("point_a", k=5)
widget.show_neighbors("point_a", threshold=0.3)
# Manual connection settings
widget = VectorSpace(
points=data,
show_connections=True,
k_neighbors=3,
distance_metric="cosine",
connection_color="#00ff00",
connection_opacity=0.5,
)
Compute Distances
distances = widget.compute_distances("point_a")
# Returns: {"point_b": 0.1, "point_c": 0.5, ...}
# Use high-dimensional vectors (not just x,y,z)
distances = widget.compute_distances(
"point_a",
metric="cosine",
vector_field="embedding"
)
Options
widget = VectorSpace(
points=data,
width=1000,
height=700,
background="#1a1a2e", # Dark theme default
show_axes=True,
show_grid=True,
axis_labels={"x": "PC1", "y": "PC2", "z": "PC3"},
show_tooltip=True,
tooltip_fields=["label", "x", "y", "z", "cluster"],
selection_mode="click", # "click", "multi", or "box"
use_instancing=True, # Performance: instanced rendering
)
Backends
Configure a backend for interactive querying:
widget.set_backend("chroma", client=collection)
widget.set_backend("lancedb", client=table)
widget.set_backend("grafeo", client=db)
Export
widget.to_json() # Points as JSON string
widget.to_html() # Self-contained HTML string
widget.to_html(title="My Vectors") # Custom title
widget.save_html("vectors.html") # Write HTML to file
Environment Support
| Environment | Supported |
|---|---|
| Marimo | Yes |
| JupyterLab | Yes |
| Jupyter Notebook | Yes |
| VS Code | Yes |
| Google Colab | Yes |
| Databricks | Yes |
Related
- anywidget, custom Jupyter widgets made easy
- anywidget-graph, graph visualization widget
- Three.js, 3D JavaScript library
License
Apache-2.0
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 anywidget_vector-0.3.0.tar.gz.
File metadata
- Download URL: anywidget_vector-0.3.0.tar.gz
- Upload date:
- Size: 242.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6fdd03e751a268126d5869fcdc5f12d9520067a4e7dc197ef331e361b54aaf60
|
|
| MD5 |
7884af668977bcebedfc52da102af799
|
|
| BLAKE2b-256 |
add29d93a820566062afb84d5247d7062318135605bf478d3c5e3615a9c39d14
|
Provenance
The following attestation bundles were made for anywidget_vector-0.3.0.tar.gz:
Publisher:
pypi.yml on GrafeoDB/anywidget-vector
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
anywidget_vector-0.3.0.tar.gz -
Subject digest:
6fdd03e751a268126d5869fcdc5f12d9520067a4e7dc197ef331e361b54aaf60 - Sigstore transparency entry: 1108516490
- Sigstore integration time:
-
Permalink:
GrafeoDB/anywidget-vector@03725bd3faee043d9e58460352ab1b4ded86c0d1 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/GrafeoDB
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@03725bd3faee043d9e58460352ab1b4ded86c0d1 -
Trigger Event:
release
-
Statement type:
File details
Details for the file anywidget_vector-0.3.0-py3-none-any.whl.
File metadata
- Download URL: anywidget_vector-0.3.0-py3-none-any.whl
- Upload date:
- Size: 65.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cddc8c9a42ee27da7e72d651616ab348cb8fb8ccd4875ee91e7c0140ec342d7b
|
|
| MD5 |
aa5729a82cb7b6a4e26624ab62c7e3a8
|
|
| BLAKE2b-256 |
aed74bc8737fb74d3b132d1d5facf6e4c46b5936fa2934a187b6c457c663fb7f
|
Provenance
The following attestation bundles were made for anywidget_vector-0.3.0-py3-none-any.whl:
Publisher:
pypi.yml on GrafeoDB/anywidget-vector
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
anywidget_vector-0.3.0-py3-none-any.whl -
Subject digest:
cddc8c9a42ee27da7e72d651616ab348cb8fb8ccd4875ee91e7c0140ec342d7b - Sigstore transparency entry: 1108516491
- Sigstore integration time:
-
Permalink:
GrafeoDB/anywidget-vector@03725bd3faee043d9e58460352ab1b4ded86c0d1 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/GrafeoDB
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@03725bd3faee043d9e58460352ab1b4ded86c0d1 -
Trigger Event:
release
-
Statement type: