anywidget-vector 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anywidget_vector-0.1.0/.gitignore +33 -0
- anywidget_vector-0.1.0/.python-version +1 -0
- anywidget_vector-0.1.0/PKG-INFO +292 -0
- anywidget_vector-0.1.0/README.md +244 -0
- anywidget_vector-0.1.0/pyproject.toml +101 -0
- anywidget_vector-0.1.0/src/anywidget_vector/__init__.py +6 -0
- anywidget_vector-0.1.0/src/anywidget_vector/py.typed +0 -0
- anywidget_vector-0.1.0/src/anywidget_vector/widget.py +978 -0
- anywidget_vector-0.1.0/tests/__init__.py +0 -0
- anywidget_vector-0.1.0/tests/test_widget.py +177 -0
- anywidget_vector-0.1.0/uv.lock +2839 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
*.so
|
|
5
|
+
.Python
|
|
6
|
+
build/
|
|
7
|
+
develop-eggs/
|
|
8
|
+
dist/
|
|
9
|
+
downloads/
|
|
10
|
+
eggs/
|
|
11
|
+
.eggs/
|
|
12
|
+
lib/
|
|
13
|
+
lib64/
|
|
14
|
+
parts/
|
|
15
|
+
sdist/
|
|
16
|
+
var/
|
|
17
|
+
wheels/
|
|
18
|
+
*.cl*/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
.venv/
|
|
23
|
+
venv/
|
|
24
|
+
ENV/
|
|
25
|
+
.env
|
|
26
|
+
*.log
|
|
27
|
+
.coverage
|
|
28
|
+
htmlcov/
|
|
29
|
+
.pytest_cache/
|
|
30
|
+
.ruff_cache/
|
|
31
|
+
.mypy_cache/
|
|
32
|
+
.ty/
|
|
33
|
+
node_modules/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: anywidget-vector
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Interactive vector visualization for Python notebooks using anywidget
|
|
5
|
+
Project-URL: Homepage, https://grafeo.dev/
|
|
6
|
+
Project-URL: Repository, https://github.com/GrafeoDB/anywidget-vector
|
|
7
|
+
Author-email: "S.T. Grond" <widget@grafeo.dev>
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Keywords: anywidget,jupyter,marimo,vector,visualization
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Framework :: Jupyter
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Requires-Dist: anywidget>=0.9.21
|
|
22
|
+
Provides-Extra: all
|
|
23
|
+
Requires-Dist: chromadb>=0.4; extra == 'all'
|
|
24
|
+
Requires-Dist: numpy>=1.24; extra == 'all'
|
|
25
|
+
Requires-Dist: pandas>=2.0; extra == 'all'
|
|
26
|
+
Requires-Dist: qdrant-client>=1.0; extra == 'all'
|
|
27
|
+
Provides-Extra: chroma
|
|
28
|
+
Requires-Dist: chromadb>=0.4; extra == 'chroma'
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: marimo>=0.19.7; extra == 'dev'
|
|
31
|
+
Requires-Dist: prek>=0.3.1; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=9.0.2; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.14.14; extra == 'dev'
|
|
34
|
+
Requires-Dist: ty>=0.0.14; extra == 'dev'
|
|
35
|
+
Provides-Extra: lancedb
|
|
36
|
+
Requires-Dist: lancedb>=0.1; extra == 'lancedb'
|
|
37
|
+
Provides-Extra: numpy
|
|
38
|
+
Requires-Dist: numpy>=1.24; extra == 'numpy'
|
|
39
|
+
Provides-Extra: pandas
|
|
40
|
+
Requires-Dist: pandas>=2.0; extra == 'pandas'
|
|
41
|
+
Provides-Extra: pinecone
|
|
42
|
+
Requires-Dist: pinecone-client>=3.0; extra == 'pinecone'
|
|
43
|
+
Provides-Extra: qdrant
|
|
44
|
+
Requires-Dist: qdrant-client>=1.0; extra == 'qdrant'
|
|
45
|
+
Provides-Extra: weaviate
|
|
46
|
+
Requires-Dist: weaviate-client>=4.0; extra == 'weaviate'
|
|
47
|
+
Description-Content-Type: text/markdown
|
|
48
|
+
|
|
49
|
+
# anywidget-vector
|
|
50
|
+
|
|
51
|
+
Interactive 3D vector visualization for Python notebooks.
|
|
52
|
+
|
|
53
|
+
Works with Marimo, Jupyter, VS Code, Colab, anywhere [anywidget](https://anywidget.dev/) runs.
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
- **Universal** — One widget, every notebook environment
|
|
58
|
+
- **6D Visualization** — X, Y, Z position + Color, Shape, Size encoding
|
|
59
|
+
- **Backend-agnostic** — NumPy, pandas, Qdrant, Chroma, or raw dicts
|
|
60
|
+
- **Interactive** — Orbit, pan, zoom, click, hover, select
|
|
61
|
+
- **Customizable** — Color scales, shapes, sizes, themes
|
|
62
|
+
- **Performant** — Instanced rendering for large point clouds
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
uv add anywidget-vector
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Quick Start
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from anywidget_vector import VectorSpace
|
|
74
|
+
|
|
75
|
+
widget = VectorSpace(points=[
|
|
76
|
+
{"id": "a", "x": 0.5, "y": 0.3, "z": 0.8, "label": "Point A", "cluster": 0},
|
|
77
|
+
{"id": "b", "x": -0.2, "y": 0.7, "z": 0.1, "label": "Point B", "cluster": 1},
|
|
78
|
+
{"id": "c", "x": 0.1, "y": -0.4, "z": 0.6, "label": "Point C", "cluster": 0},
|
|
79
|
+
])
|
|
80
|
+
|
|
81
|
+
widget
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Data Sources
|
|
85
|
+
|
|
86
|
+
### Dictionary
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from anywidget_vector import VectorSpace
|
|
90
|
+
|
|
91
|
+
widget = VectorSpace.from_dict({
|
|
92
|
+
"points": [
|
|
93
|
+
{"id": "a", "x": 0, "y": 0, "z": 0},
|
|
94
|
+
{"id": "b", "x": 1, "y": 1, "z": 1},
|
|
95
|
+
]
|
|
96
|
+
})
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### NumPy Arrays
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
import numpy as np
|
|
103
|
+
from anywidget_vector import VectorSpace
|
|
104
|
+
|
|
105
|
+
positions = np.random.randn(100, 3)
|
|
106
|
+
widget = VectorSpace.from_numpy(positions)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### pandas DataFrame
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
import pandas as pd
|
|
113
|
+
from anywidget_vector import VectorSpace
|
|
114
|
+
|
|
115
|
+
df = pd.DataFrame({
|
|
116
|
+
"x": [0.1, 0.5, 0.9],
|
|
117
|
+
"y": [0.2, 0.6, 0.3],
|
|
118
|
+
"z": [0.3, 0.1, 0.7],
|
|
119
|
+
"cluster": ["A", "B", "A"],
|
|
120
|
+
"size": [0.5, 1.0, 0.8],
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
widget = VectorSpace.from_dataframe(
|
|
124
|
+
df,
|
|
125
|
+
color_col="cluster",
|
|
126
|
+
size_col="size",
|
|
127
|
+
)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### UMAP / t-SNE / PCA
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
import umap
|
|
134
|
+
from anywidget_vector import VectorSpace
|
|
135
|
+
|
|
136
|
+
# Reduce high-dimensional data to 3D
|
|
137
|
+
embedding = umap.UMAP(n_components=3).fit_transform(high_dim_data)
|
|
138
|
+
widget = VectorSpace.from_umap(embedding, labels=labels)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Qdrant
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from qdrant_client import QdrantClient
|
|
145
|
+
from anywidget_vector import VectorSpace
|
|
146
|
+
|
|
147
|
+
client = QdrantClient("localhost", port=6333)
|
|
148
|
+
widget = VectorSpace.from_qdrant(client, "my_collection", limit=5000)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### ChromaDB
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
import chromadb
|
|
155
|
+
from anywidget_vector import VectorSpace
|
|
156
|
+
|
|
157
|
+
client = chromadb.Client()
|
|
158
|
+
collection = client.get_collection("embeddings")
|
|
159
|
+
widget = VectorSpace.from_chroma(collection)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Visual Encoding
|
|
163
|
+
|
|
164
|
+
### 6 Dimensions
|
|
165
|
+
|
|
166
|
+
| Dimension | Visual Channel | Example |
|
|
167
|
+
|-----------|---------------|---------|
|
|
168
|
+
| X | Horizontal position | `x` coordinate |
|
|
169
|
+
| Y | Vertical position | `y` coordinate |
|
|
170
|
+
| Z | Depth position | `z` coordinate |
|
|
171
|
+
| Color | Hue/gradient | Cluster, score |
|
|
172
|
+
| Shape | Geometry | Category, type |
|
|
173
|
+
| Size | Scale | Importance, count |
|
|
174
|
+
|
|
175
|
+
### Color Scales
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
widget = VectorSpace(
|
|
179
|
+
points=data,
|
|
180
|
+
color_field="score", # Field to map
|
|
181
|
+
color_scale="viridis", # Scale: viridis, plasma, inferno, magma, cividis, turbo
|
|
182
|
+
color_domain=[0, 100], # Optional: explicit range
|
|
183
|
+
)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Shapes
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
widget = VectorSpace(
|
|
190
|
+
points=data,
|
|
191
|
+
shape_field="category",
|
|
192
|
+
shape_map={
|
|
193
|
+
"type_a": "sphere", # Available: sphere, cube, cone,
|
|
194
|
+
"type_b": "cube", # tetrahedron, octahedron, cylinder
|
|
195
|
+
"type_c": "cone",
|
|
196
|
+
}
|
|
197
|
+
)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Size
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
widget = VectorSpace(
|
|
204
|
+
points=data,
|
|
205
|
+
size_field="importance",
|
|
206
|
+
size_range=[0.02, 0.15], # Min/max point size
|
|
207
|
+
)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Interactivity
|
|
211
|
+
|
|
212
|
+
### Events
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
widget = VectorSpace(points=data)
|
|
216
|
+
|
|
217
|
+
@widget.on_click
|
|
218
|
+
def handle_click(point_id, point_data):
|
|
219
|
+
print(f"Clicked: {point_id}")
|
|
220
|
+
print(f"Data: {point_data}")
|
|
221
|
+
|
|
222
|
+
@widget.on_hover
|
|
223
|
+
def handle_hover(point_id, point_data):
|
|
224
|
+
if point_id:
|
|
225
|
+
print(f"Hovering: {point_id}")
|
|
226
|
+
|
|
227
|
+
@widget.on_selection
|
|
228
|
+
def handle_selection(point_ids, points_data):
|
|
229
|
+
print(f"Selected {len(point_ids)} points")
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Selection
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
widget.selected_points # Get current selection
|
|
236
|
+
widget.select(["a", "b"]) # Select points
|
|
237
|
+
widget.clear_selection() # Clear
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Camera
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
widget.camera_position # Get position [x, y, z]
|
|
244
|
+
widget.camera_target # Get target [x, y, z]
|
|
245
|
+
widget.reset_camera() # Reset to default
|
|
246
|
+
widget.focus_on(["a", "b"]) # Focus on specific points
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Options
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
widget = VectorSpace(
|
|
253
|
+
points=data,
|
|
254
|
+
width=1000,
|
|
255
|
+
height=700,
|
|
256
|
+
background="#1a1a2e", # Dark theme default
|
|
257
|
+
show_axes=True,
|
|
258
|
+
show_grid=True,
|
|
259
|
+
axis_labels={"x": "PC1", "y": "PC2", "z": "PC3"},
|
|
260
|
+
show_tooltip=True,
|
|
261
|
+
tooltip_fields=["label", "x", "y", "z", "cluster"],
|
|
262
|
+
selection_mode="click", # "click" or "multi"
|
|
263
|
+
use_instancing=True, # Performance: instanced rendering
|
|
264
|
+
)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Export
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
widget.to_json() # Export points as JSON string
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Environment Support
|
|
274
|
+
|
|
275
|
+
| Environment | Supported |
|
|
276
|
+
|-------------|-----------|
|
|
277
|
+
| Marimo | ✅ |
|
|
278
|
+
| JupyterLab | ✅ |
|
|
279
|
+
| Jupyter Notebook | ✅ |
|
|
280
|
+
| VS Code | ✅ |
|
|
281
|
+
| Google Colab | ✅ |
|
|
282
|
+
| Databricks | ✅ |
|
|
283
|
+
|
|
284
|
+
## Related
|
|
285
|
+
|
|
286
|
+
- [anywidget](https://anywidget.dev/) — Custom Jupyter widgets made easy
|
|
287
|
+
- [anywidget-graph](https://github.com/GrafeoDB/anywidget-graph) — Graph visualization widget
|
|
288
|
+
- [Three.js](https://threejs.org/) — 3D JavaScript library
|
|
289
|
+
|
|
290
|
+
## License
|
|
291
|
+
|
|
292
|
+
Apache-2.0
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# anywidget-vector
|
|
2
|
+
|
|
3
|
+
Interactive 3D vector visualization for Python notebooks.
|
|
4
|
+
|
|
5
|
+
Works with Marimo, Jupyter, VS Code, Colab, anywhere [anywidget](https://anywidget.dev/) runs.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Universal** — One widget, every notebook environment
|
|
10
|
+
- **6D Visualization** — X, Y, Z position + Color, Shape, Size encoding
|
|
11
|
+
- **Backend-agnostic** — NumPy, pandas, Qdrant, Chroma, or raw dicts
|
|
12
|
+
- **Interactive** — Orbit, pan, zoom, click, hover, select
|
|
13
|
+
- **Customizable** — Color scales, shapes, sizes, themes
|
|
14
|
+
- **Performant** — Instanced rendering for large point clouds
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv add anywidget-vector
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from anywidget_vector import VectorSpace
|
|
26
|
+
|
|
27
|
+
widget = VectorSpace(points=[
|
|
28
|
+
{"id": "a", "x": 0.5, "y": 0.3, "z": 0.8, "label": "Point A", "cluster": 0},
|
|
29
|
+
{"id": "b", "x": -0.2, "y": 0.7, "z": 0.1, "label": "Point B", "cluster": 1},
|
|
30
|
+
{"id": "c", "x": 0.1, "y": -0.4, "z": 0.6, "label": "Point C", "cluster": 0},
|
|
31
|
+
])
|
|
32
|
+
|
|
33
|
+
widget
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Data Sources
|
|
37
|
+
|
|
38
|
+
### Dictionary
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from anywidget_vector import VectorSpace
|
|
42
|
+
|
|
43
|
+
widget = VectorSpace.from_dict({
|
|
44
|
+
"points": [
|
|
45
|
+
{"id": "a", "x": 0, "y": 0, "z": 0},
|
|
46
|
+
{"id": "b", "x": 1, "y": 1, "z": 1},
|
|
47
|
+
]
|
|
48
|
+
})
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### NumPy Arrays
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import numpy as np
|
|
55
|
+
from anywidget_vector import VectorSpace
|
|
56
|
+
|
|
57
|
+
positions = np.random.randn(100, 3)
|
|
58
|
+
widget = VectorSpace.from_numpy(positions)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### pandas DataFrame
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
import pandas as pd
|
|
65
|
+
from anywidget_vector import VectorSpace
|
|
66
|
+
|
|
67
|
+
df = pd.DataFrame({
|
|
68
|
+
"x": [0.1, 0.5, 0.9],
|
|
69
|
+
"y": [0.2, 0.6, 0.3],
|
|
70
|
+
"z": [0.3, 0.1, 0.7],
|
|
71
|
+
"cluster": ["A", "B", "A"],
|
|
72
|
+
"size": [0.5, 1.0, 0.8],
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
widget = VectorSpace.from_dataframe(
|
|
76
|
+
df,
|
|
77
|
+
color_col="cluster",
|
|
78
|
+
size_col="size",
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### UMAP / t-SNE / PCA
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
import umap
|
|
86
|
+
from anywidget_vector import VectorSpace
|
|
87
|
+
|
|
88
|
+
# Reduce high-dimensional data to 3D
|
|
89
|
+
embedding = umap.UMAP(n_components=3).fit_transform(high_dim_data)
|
|
90
|
+
widget = VectorSpace.from_umap(embedding, labels=labels)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Qdrant
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from qdrant_client import QdrantClient
|
|
97
|
+
from anywidget_vector import VectorSpace
|
|
98
|
+
|
|
99
|
+
client = QdrantClient("localhost", port=6333)
|
|
100
|
+
widget = VectorSpace.from_qdrant(client, "my_collection", limit=5000)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### ChromaDB
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
import chromadb
|
|
107
|
+
from anywidget_vector import VectorSpace
|
|
108
|
+
|
|
109
|
+
client = chromadb.Client()
|
|
110
|
+
collection = client.get_collection("embeddings")
|
|
111
|
+
widget = VectorSpace.from_chroma(collection)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Visual Encoding
|
|
115
|
+
|
|
116
|
+
### 6 Dimensions
|
|
117
|
+
|
|
118
|
+
| Dimension | Visual Channel | Example |
|
|
119
|
+
|-----------|---------------|---------|
|
|
120
|
+
| X | Horizontal position | `x` coordinate |
|
|
121
|
+
| Y | Vertical position | `y` coordinate |
|
|
122
|
+
| Z | Depth position | `z` coordinate |
|
|
123
|
+
| Color | Hue/gradient | Cluster, score |
|
|
124
|
+
| Shape | Geometry | Category, type |
|
|
125
|
+
| Size | Scale | Importance, count |
|
|
126
|
+
|
|
127
|
+
### Color Scales
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
widget = VectorSpace(
|
|
131
|
+
points=data,
|
|
132
|
+
color_field="score", # Field to map
|
|
133
|
+
color_scale="viridis", # Scale: viridis, plasma, inferno, magma, cividis, turbo
|
|
134
|
+
color_domain=[0, 100], # Optional: explicit range
|
|
135
|
+
)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Shapes
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
widget = VectorSpace(
|
|
142
|
+
points=data,
|
|
143
|
+
shape_field="category",
|
|
144
|
+
shape_map={
|
|
145
|
+
"type_a": "sphere", # Available: sphere, cube, cone,
|
|
146
|
+
"type_b": "cube", # tetrahedron, octahedron, cylinder
|
|
147
|
+
"type_c": "cone",
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Size
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
widget = VectorSpace(
|
|
156
|
+
points=data,
|
|
157
|
+
size_field="importance",
|
|
158
|
+
size_range=[0.02, 0.15], # Min/max point size
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Interactivity
|
|
163
|
+
|
|
164
|
+
### Events
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
widget = VectorSpace(points=data)
|
|
168
|
+
|
|
169
|
+
@widget.on_click
|
|
170
|
+
def handle_click(point_id, point_data):
|
|
171
|
+
print(f"Clicked: {point_id}")
|
|
172
|
+
print(f"Data: {point_data}")
|
|
173
|
+
|
|
174
|
+
@widget.on_hover
|
|
175
|
+
def handle_hover(point_id, point_data):
|
|
176
|
+
if point_id:
|
|
177
|
+
print(f"Hovering: {point_id}")
|
|
178
|
+
|
|
179
|
+
@widget.on_selection
|
|
180
|
+
def handle_selection(point_ids, points_data):
|
|
181
|
+
print(f"Selected {len(point_ids)} points")
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Selection
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
widget.selected_points # Get current selection
|
|
188
|
+
widget.select(["a", "b"]) # Select points
|
|
189
|
+
widget.clear_selection() # Clear
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Camera
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
widget.camera_position # Get position [x, y, z]
|
|
196
|
+
widget.camera_target # Get target [x, y, z]
|
|
197
|
+
widget.reset_camera() # Reset to default
|
|
198
|
+
widget.focus_on(["a", "b"]) # Focus on specific points
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Options
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
widget = VectorSpace(
|
|
205
|
+
points=data,
|
|
206
|
+
width=1000,
|
|
207
|
+
height=700,
|
|
208
|
+
background="#1a1a2e", # Dark theme default
|
|
209
|
+
show_axes=True,
|
|
210
|
+
show_grid=True,
|
|
211
|
+
axis_labels={"x": "PC1", "y": "PC2", "z": "PC3"},
|
|
212
|
+
show_tooltip=True,
|
|
213
|
+
tooltip_fields=["label", "x", "y", "z", "cluster"],
|
|
214
|
+
selection_mode="click", # "click" or "multi"
|
|
215
|
+
use_instancing=True, # Performance: instanced rendering
|
|
216
|
+
)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Export
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
widget.to_json() # Export points as JSON string
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Environment Support
|
|
226
|
+
|
|
227
|
+
| Environment | Supported |
|
|
228
|
+
|-------------|-----------|
|
|
229
|
+
| Marimo | ✅ |
|
|
230
|
+
| JupyterLab | ✅ |
|
|
231
|
+
| Jupyter Notebook | ✅ |
|
|
232
|
+
| VS Code | ✅ |
|
|
233
|
+
| Google Colab | ✅ |
|
|
234
|
+
| Databricks | ✅ |
|
|
235
|
+
|
|
236
|
+
## Related
|
|
237
|
+
|
|
238
|
+
- [anywidget](https://anywidget.dev/) — Custom Jupyter widgets made easy
|
|
239
|
+
- [anywidget-graph](https://github.com/GrafeoDB/anywidget-graph) — Graph visualization widget
|
|
240
|
+
- [Three.js](https://threejs.org/) — 3D JavaScript library
|
|
241
|
+
|
|
242
|
+
## License
|
|
243
|
+
|
|
244
|
+
Apache-2.0
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "anywidget-vector"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Interactive vector visualization for Python notebooks using anywidget"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "Apache-2.0" }
|
|
7
|
+
requires-python = ">=3.12"
|
|
8
|
+
authors = [{ name = "S.T. Grond", email = "widget@grafeo.dev" }]
|
|
9
|
+
keywords = ["anywidget", "vector", "visualization", "jupyter", "marimo"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 3 - Alpha",
|
|
12
|
+
"Framework :: Jupyter",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Intended Audience :: Science/Research",
|
|
15
|
+
"License :: OSI Approved :: Apache Software License",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Programming Language :: Python :: 3.13",
|
|
19
|
+
"Programming Language :: Python :: 3.14",
|
|
20
|
+
"Topic :: Scientific/Engineering :: Visualization",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
dependencies = [
|
|
24
|
+
"anywidget>=0.9.21",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
dev = [
|
|
29
|
+
"prek>=0.3.1",
|
|
30
|
+
"pytest>=9.0.2",
|
|
31
|
+
"ruff>=0.14.14",
|
|
32
|
+
"ty>=0.0.14",
|
|
33
|
+
"marimo>=0.19.7",
|
|
34
|
+
]
|
|
35
|
+
pandas = ["pandas>=2.0"]
|
|
36
|
+
numpy = ["numpy>=1.24"]
|
|
37
|
+
qdrant = ["qdrant-client>=1.0"]
|
|
38
|
+
chroma = ["chromadb>=0.4"]
|
|
39
|
+
pinecone = ["pinecone-client>=3.0"]
|
|
40
|
+
weaviate = ["weaviate-client>=4.0"]
|
|
41
|
+
lancedb = ["lancedb>=0.1"]
|
|
42
|
+
all = [
|
|
43
|
+
"pandas>=2.0",
|
|
44
|
+
"numpy>=1.24",
|
|
45
|
+
"qdrant-client>=1.0",
|
|
46
|
+
"chromadb>=0.4",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[project.urls]
|
|
50
|
+
Homepage = "https://grafeo.dev/"
|
|
51
|
+
Repository = "https://github.com/GrafeoDB/anywidget-vector"
|
|
52
|
+
|
|
53
|
+
[build-system]
|
|
54
|
+
requires = ["hatchling"]
|
|
55
|
+
build-backend = "hatchling.build"
|
|
56
|
+
|
|
57
|
+
[tool.hatch.build.targets.wheel]
|
|
58
|
+
packages = ["src/anywidget_vector"]
|
|
59
|
+
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
line-length = 120
|
|
62
|
+
target-version = "py312"
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint]
|
|
65
|
+
select = [
|
|
66
|
+
"E",
|
|
67
|
+
"F",
|
|
68
|
+
"I",
|
|
69
|
+
"UP",
|
|
70
|
+
"FA",
|
|
71
|
+
"Q",
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
[tool.ruff.lint.per-file-ignores]
|
|
75
|
+
"src/anywidget_vector/widget.py" = ["E501"] # Long lines in embedded JS
|
|
76
|
+
|
|
77
|
+
[tool.ruff.lint.flake8-quotes]
|
|
78
|
+
inline-quotes = "double"
|
|
79
|
+
docstring-quotes = "double"
|
|
80
|
+
|
|
81
|
+
[tool.ty.environment]
|
|
82
|
+
python-version = "3.12"
|
|
83
|
+
|
|
84
|
+
[tool.pytest.ini_options]
|
|
85
|
+
testpaths = ["tests"]
|
|
86
|
+
pythonpath = ["src"]
|
|
87
|
+
addopts = "-v"
|
|
88
|
+
|
|
89
|
+
[tool.prek]
|
|
90
|
+
repos = [
|
|
91
|
+
{ repo = "ruff", hooks = [
|
|
92
|
+
{ id = "ruff-check", args = ["--fix", "--exit-non-zero-on-fix"] },
|
|
93
|
+
{ id = "ruff-format" },
|
|
94
|
+
]},
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
[tool.uv]
|
|
98
|
+
dev-dependencies = [
|
|
99
|
+
"pytest>=9.0.2",
|
|
100
|
+
"ruff>=0.14.14",
|
|
101
|
+
]
|
|
File without changes
|