anywidget-vector 0.2.7__tar.gz → 0.2.8__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.
Files changed (50) hide show
  1. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/PKG-INFO +1 -1
  2. anywidget_vector-0.2.8/examples/fashion-mnist-parallel-coords-6d.py +245 -0
  3. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/pyproject.toml +1 -1
  4. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/ui/canvas.js +3 -0
  5. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/.github/workflows/ci.yml +0 -0
  6. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/.github/workflows/pypi.yml +0 -0
  7. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/.gitignore +0 -0
  8. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/.python-version +0 -0
  9. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/LICENSE +0 -0
  10. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/README.md +0 -0
  11. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/assets/demo.png +0 -0
  12. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/examples/demo.py +0 -0
  13. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/examples/test_add_numpy.py +0 -0
  14. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/examples/test_projection.py +0 -0
  15. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/__init__.py +0 -0
  16. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/__init__.py +0 -0
  17. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/chroma/__init__.py +0 -0
  18. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/chroma/client.py +0 -0
  19. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/chroma/converter.py +0 -0
  20. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/grafeo/__init__.py +0 -0
  21. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/grafeo/browser.js +0 -0
  22. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/grafeo/client.py +0 -0
  23. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/grafeo/converter.py +0 -0
  24. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/grafeo/embed.js +0 -0
  25. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/lancedb/__init__.py +0 -0
  26. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/lancedb/client.py +0 -0
  27. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/lancedb/converter.py +0 -0
  28. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/pinecone/__init__.py +0 -0
  29. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/pinecone/client.js +0 -0
  30. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/pinecone/converter.py +0 -0
  31. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/qdrant/__init__.py +0 -0
  32. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/qdrant/client.js +0 -0
  33. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/qdrant/converter.py +0 -0
  34. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/weaviate/__init__.py +0 -0
  35. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/weaviate/client.js +0 -0
  36. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/backends/weaviate/converter.py +0 -0
  37. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/demo.py +0 -0
  38. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/py.typed +0 -0
  39. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/static/icons.js +0 -0
  40. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/ui/__init__.py +0 -0
  41. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/ui/constants.js +0 -0
  42. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/ui/properties.js +0 -0
  43. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/ui/settings.js +0 -0
  44. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/ui/sidebar.js +0 -0
  45. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/ui/styles.css +0 -0
  46. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/ui/toolbar.js +0 -0
  47. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/src/anywidget_vector/widget.py +0 -0
  48. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/tests/__init__.py +0 -0
  49. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/tests/test_widget.py +0 -0
  50. {anywidget_vector-0.2.7 → anywidget_vector-0.2.8}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anywidget-vector
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: Interactive 3D vector visualization with query UI for vector databases
5
5
  Project-URL: Homepage, https://grafeo.dev/
6
6
  Project-URL: Repository, https://github.com/GrafeoDB/anywidget-vector
@@ -0,0 +1,245 @@
1
+ # /// script
2
+ # requires-python = ">=3.12"
3
+ # dependencies = [
4
+ # "marimo",
5
+ # "polars==1.38.1",
6
+ # "numpy==2.4.2",
7
+ # "scikit-learn==1.8.0",
8
+ # "wigglystuff==0.2.37",
9
+ # "matplotlib==3.10.8",
10
+ # "pandas==3.0.1",
11
+ # "anywidget-vector==0.2.7",
12
+ # ]
13
+ # ///
14
+
15
+ import marimo
16
+
17
+ __generated_with = "0.20.2"
18
+ app = marimo.App(width="full")
19
+
20
+
21
+ @app.cell
22
+ def _():
23
+ import marimo as mo
24
+ import matplotlib.pyplot as plt
25
+ import numpy as np
26
+ import polars as pl
27
+ from sklearn.datasets import fetch_openml
28
+ from sklearn.decomposition import PCA
29
+ from wigglystuff import ParallelCoordinates
30
+
31
+ from anywidget_vector import VectorSpace
32
+
33
+ return PCA, ParallelCoordinates, VectorSpace, fetch_openml, mo, np, pl, plt
34
+
35
+
36
+ @app.cell(hide_code=True)
37
+ def _(mo):
38
+ mo.md(r"""
39
+ # Fashion MNIST: Parallel Coordinates + 3D Vector View
40
+
41
+ Brush the parallel coordinates axes to filter. The 3D scatter updates in real time.
42
+ Use the dropdowns to map PCA dimensions to visual channels.
43
+ """)
44
+ return
45
+
46
+
47
+ @app.cell
48
+ def _(fetch_openml, np):
49
+ mnist = fetch_openml("Fashion-MNIST", version=1, as_frame=False, parser="auto")
50
+ images = mnist.data.astype(np.float32)
51
+ labels = mnist.target.astype(int)
52
+
53
+ label_names = {
54
+ 0: "T-shirt/top",
55
+ 1: "Trouser",
56
+ 2: "Pullover",
57
+ 3: "Dress",
58
+ 4: "Coat",
59
+ 5: "Sandal",
60
+ 6: "Shirt",
61
+ 7: "Sneaker",
62
+ 8: "Bag",
63
+ 9: "Ankle boot",
64
+ }
65
+ LABEL_COLORS = {
66
+ "T-shirt/top": "#6366f1",
67
+ "Trouser": "#f59e0b",
68
+ "Pullover": "#10b981",
69
+ "Dress": "#ef4444",
70
+ "Coat": "#8b5cf6",
71
+ "Sandal": "#06b6d4",
72
+ "Shirt": "#f97316",
73
+ "Sneaker": "#84cc16",
74
+ "Bag": "#ec4899",
75
+ "Ankle boot": "#14b8a6",
76
+ }
77
+ SHAPE_NAMES = ["sphere", "cube", "cone", "octahedron", "cylinder", "tetrahedron"]
78
+ return LABEL_COLORS, SHAPE_NAMES, images, label_names, labels
79
+
80
+
81
+ @app.cell
82
+ def _(mo):
83
+ n_samples_slider = mo.ui.slider(start=500, stop=5000, step=500, value=2000, label="Samples")
84
+ n_components_slider = mo.ui.slider(start=3, stop=15, step=1, value=8, label="PCA dims")
85
+ mo.hstack([n_samples_slider, n_components_slider])
86
+ return n_components_slider, n_samples_slider
87
+
88
+
89
+ @app.cell
90
+ def _(
91
+ PCA,
92
+ images,
93
+ label_names,
94
+ labels,
95
+ n_components_slider,
96
+ n_samples_slider,
97
+ np,
98
+ pl,
99
+ ):
100
+ _rng = np.random.default_rng(42)
101
+ idx = _rng.choice(len(images), size=n_samples_slider.value, replace=False)
102
+
103
+ _pca = PCA(n_components=n_components_slider.value)
104
+ components = _pca.fit_transform(images[idx])
105
+
106
+ df = pl.DataFrame({f"PC{i + 1}": components[:, i] for i in range(n_components_slider.value)}).with_columns(
107
+ pl.Series("label", [label_names[labels[i]] for i in idx])
108
+ )
109
+ return components, df, idx
110
+
111
+
112
+ @app.cell
113
+ def _(ParallelCoordinates, df, mo):
114
+ widget = mo.ui.anywidget(ParallelCoordinates(df, color_by="label"))
115
+ widget
116
+ return (widget,)
117
+
118
+
119
+ @app.cell
120
+ def _(mo, n_components_slider):
121
+ pcs = [f"PC{i + 1}" for i in range(n_components_slider.value)]
122
+ x_dim = mo.ui.dropdown(options=pcs, value="PC1", label="X")
123
+ y_dim = mo.ui.dropdown(options=pcs, value="PC2", label="Y")
124
+ z_dim = mo.ui.dropdown(options=pcs, value="PC3", label="Z")
125
+ size_dim = mo.ui.dropdown(options=["none", *pcs], value="none", label="Size")
126
+ color_dim = mo.ui.dropdown(options=["label", *pcs], value="label", label="Color")
127
+ shape_dim = mo.ui.dropdown(options=["label", "none"], value="none", label="Shape")
128
+ mo.hstack([x_dim, y_dim, z_dim, size_dim, color_dim, shape_dim], gap=0.5)
129
+ return color_dim, shape_dim, size_dim, x_dim, y_dim, z_dim
130
+
131
+
132
+ @app.cell
133
+ def _(
134
+ LABEL_COLORS,
135
+ SHAPE_NAMES,
136
+ VectorSpace,
137
+ color_dim,
138
+ components,
139
+ idx,
140
+ label_names,
141
+ labels,
142
+ mo,
143
+ shape_dim,
144
+ size_dim,
145
+ x_dim,
146
+ y_dim,
147
+ z_dim,
148
+ ):
149
+ def _pc(name):
150
+ return int(name[2:]) - 1
151
+
152
+ _xi, _yi, _zi = _pc(x_dim.value), _pc(y_dim.value), _pc(z_dim.value)
153
+ _color_pc = None if color_dim.value == "label" else _pc(color_dim.value)
154
+ _size_pc = None if size_dim.value == "none" else _pc(size_dim.value)
155
+ _unique_labels = sorted(set(label_names.values()))
156
+ _shape_map = {_n: SHAPE_NAMES[_j % len(SHAPE_NAMES)] for _j, _n in enumerate(_unique_labels)}
157
+
158
+ vs_points = []
159
+ for _i in range(len(components)):
160
+ _name = label_names[labels[idx[_i]]]
161
+ _p = {
162
+ "id": f"p_{_i}",
163
+ "x": float(components[_i, _xi]),
164
+ "y": float(components[_i, _yi]),
165
+ "z": float(components[_i, _zi]),
166
+ "label": _name,
167
+ }
168
+ if _color_pc is None:
169
+ _p["color"] = LABEL_COLORS[_name]
170
+ else:
171
+ _p["color_val"] = float(components[_i, _color_pc])
172
+ if _size_pc is not None:
173
+ _p["size_val"] = float(components[_i, _size_pc])
174
+ if shape_dim.value == "label":
175
+ _p["shape_cat"] = _name
176
+ vs_points.append(_p)
177
+
178
+ _vs_kwargs = {
179
+ "axis_labels": {"x": x_dim.value, "y": y_dim.value, "z": z_dim.value},
180
+ }
181
+ if _color_pc is not None:
182
+ _vs_kwargs["color_field"] = "color_val"
183
+ _vs_kwargs["color_scale"] = "viridis"
184
+ if _size_pc is not None:
185
+ _vs_kwargs["size_field"] = "size_val"
186
+ _vs_kwargs["size_range"] = [0.01, 0.06]
187
+ if shape_dim.value == "label":
188
+ _vs_kwargs["shape_field"] = "shape_cat"
189
+ _vs_kwargs["shape_map"] = _shape_map
190
+
191
+ vs_widget = VectorSpace(
192
+ points=vs_points,
193
+ width=1200,
194
+ height=500,
195
+ dark_mode=False,
196
+ background="#fafafa",
197
+ show_toolbar=True,
198
+ show_settings=True,
199
+ show_properties=False,
200
+ **_vs_kwargs,
201
+ )
202
+ vs = mo.ui.anywidget(vs_widget)
203
+ vs
204
+ return vs, vs_points, vs_widget
205
+
206
+
207
+ @app.cell
208
+ def _(LABEL_COLORS, color_dim, mo, vs_points, vs_widget, widget):
209
+ _filtered = set(widget.widget.filtered_indices)
210
+ _dim_color = "#d1d5db"
211
+ _updated = []
212
+ for _i, _p in enumerate(vs_points):
213
+ _new_p = dict(_p)
214
+ if _i in _filtered:
215
+ if color_dim.value == "label":
216
+ _new_p["color"] = LABEL_COLORS[_p["label"]]
217
+ else:
218
+ _new_p.pop("color", None)
219
+ else:
220
+ _new_p["color"] = _dim_color
221
+ _updated.append(_new_p)
222
+ vs_widget.points = _updated
223
+ mo.md(f"**{len(_filtered)}** / {len(vs_points)} points selected")
224
+ return
225
+
226
+
227
+ @app.cell
228
+ def _(idx, images, label_names, labels, np, plt, widget):
229
+ _filtered = widget.widget.filtered_indices
230
+ _sample_idx = np.array(_filtered[:10]) if len(_filtered) >= 10 else np.array(_filtered)
231
+
232
+ _fig, _axes = plt.subplots(1, len(_sample_idx), figsize=(2 * len(_sample_idx), 2))
233
+ if len(_sample_idx) == 1:
234
+ _axes = [_axes]
235
+ for _ax, _si in zip(_axes, _sample_idx, strict=False):
236
+ _ax.imshow(images[idx[_si]].reshape(28, 28), cmap="gray")
237
+ _ax.set_title(label_names[labels[idx[_si]]], fontsize=9)
238
+ _ax.axis("off")
239
+ plt.tight_layout()
240
+ _fig
241
+ return
242
+
243
+
244
+ if __name__ == "__main__":
245
+ app.run()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "anywidget-vector"
3
- version = "0.2.7"
3
+ version = "0.2.8"
4
4
  description = "Interactive 3D vector visualization with query UI for vector databases"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -461,6 +461,9 @@ export function createCanvas(model, container, callbacks) {
461
461
  const distance = size / (2 * Math.tan(Math.PI * camera.fov / 360));
462
462
  controls.target.copy(center);
463
463
  camera.position.copy(center.clone().add(new THREE.Vector3(0, 0, distance * 1.2)));
464
+ camera.near = Math.max(0.01, distance * 0.001);
465
+ camera.far = Math.max(1000, distance * 10);
466
+ camera.updateProjectionMatrix();
464
467
  controls.update();
465
468
  }
466
469