scatter3d-anywidget 0.1.2__py3-none-any.whl
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.
- scatter3d/__init__.py +3 -0
- scatter3d/scatter3d.py +1011 -0
- scatter3d/static/scatter3d.js +22647 -0
- scatter3d/static/scatter3d.js.map +1 -0
- scatter3d/widget_test.py +48 -0
- scatter3d_anywidget-0.1.2.dist-info/METADATA +145 -0
- scatter3d_anywidget-0.1.2.dist-info/RECORD +8 -0
- scatter3d_anywidget-0.1.2.dist-info/WHEEL +4 -0
scatter3d/widget_test.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import marimo
|
|
2
|
+
|
|
3
|
+
__generated_with = "0.19.2"
|
|
4
|
+
app = marimo.App(width="medium")
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@app.cell
|
|
8
|
+
def _():
|
|
9
|
+
import random
|
|
10
|
+
from scatter3d import Scatter3dWidget, Category, LabelListErrorResponse
|
|
11
|
+
import marimo
|
|
12
|
+
import numpy as np
|
|
13
|
+
import pandas
|
|
14
|
+
|
|
15
|
+
num_points = 100
|
|
16
|
+
|
|
17
|
+
point_ids = [f"id_{i}" for i in range(1, num_points + 1)]
|
|
18
|
+
points = np.random.randn(num_points, 3)
|
|
19
|
+
species_list = ["species1", "species2", "species3"]
|
|
20
|
+
species = random.choices(species_list, k=num_points)
|
|
21
|
+
species = Category(pandas.Series(species, name="species"))
|
|
22
|
+
|
|
23
|
+
w = Scatter3dWidget(xyz=points, category=species, point_ids=point_ids)
|
|
24
|
+
ui = marimo.ui.anywidget(w)
|
|
25
|
+
ui
|
|
26
|
+
return species, ui, w
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@app.cell
|
|
30
|
+
def _(species, ui, w):
|
|
31
|
+
ui.lasso_result_t
|
|
32
|
+
print(species.values.value_counts())
|
|
33
|
+
print(species.num_unassigned)
|
|
34
|
+
print(species.values)
|
|
35
|
+
print(w.point_ids)
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@app.cell
|
|
40
|
+
def _(w):
|
|
41
|
+
import inspect
|
|
42
|
+
print(type(w))
|
|
43
|
+
print(inspect.getsource(type(w)._on_lasso_request_t))
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
app.run()
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: scatter3d-anywidget
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: 3D scatter widget with lasso selection
|
|
5
|
+
License: MIT
|
|
6
|
+
Classifier: Development Status :: 3 - Alpha
|
|
7
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
12
|
+
Requires-Dist: anywidget>=0.9.21
|
|
13
|
+
Requires-Dist: narwhals>=2.14.0
|
|
14
|
+
Requires-Dist: numpy>=2.3.5
|
|
15
|
+
Requires-Python: >=3.13
|
|
16
|
+
Project-URL: Homepage, https://github.com/JoseBlanca/any_scatter3d
|
|
17
|
+
Project-URL: Issues, https://github.com/JoseBlanca/any_scatter3d/issues
|
|
18
|
+
Project-URL: Repository, https://github.com/JoseBlanca/any_scatter3d
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# scatter3d-anywidget
|
|
22
|
+
|
|
23
|
+
**Interactive 3D scatter plots for Python notebooks, with lasso-based selection and categorical annotation.**
|
|
24
|
+
|
|
25
|
+
`scatter3d-anywidget` provides a high-performance, WebGL-based 3D scatter plot widget built on top of **[anywidget](https://anywidget.dev/)**.
|
|
26
|
+
It is designed for exploratory data analysis workflows where users need to **interactively select, assign, and modify categories** on point clouds.
|
|
27
|
+
|
|
28
|
+

|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
* **3D scatter visualization** (WebGL / Three.js) (up to tens of thousands of points)
|
|
33
|
+
* **Lasso selection** with *add* and *remove* operations
|
|
34
|
+
* **Categorical annotation** backed by pandas or Polars Series thanks to [narwhals](https://narwhals-dev.github.io/narwhals/)
|
|
35
|
+
* **Bidirectional sync** between Python state and frontend
|
|
36
|
+
* Designed for **anywidget**, works well with **marimo** and Jupyter
|
|
37
|
+
|
|
38
|
+
## Project status
|
|
39
|
+
|
|
40
|
+
⚠️ **Alpha status**, this is alpha software that we are using in our research.
|
|
41
|
+
|
|
42
|
+
This project is under active development.
|
|
43
|
+
APIs, traitlets, and frontend behavior may change without notice.
|
|
44
|
+
|
|
45
|
+
Contributions and feedback are welcome.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install scatter3d-anywidget
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+

|
|
55
|
+
|
|
56
|
+
## Requirements
|
|
57
|
+
|
|
58
|
+
- Python ≥ 3.13
|
|
59
|
+
- Jupyter or marimo
|
|
60
|
+
- WebGL-capable browser
|
|
61
|
+
|
|
62
|
+
## Basic usage
|
|
63
|
+
|
|
64
|
+
Below is a minimal example showing how to:
|
|
65
|
+
|
|
66
|
+
* Create a 3D scatter plot
|
|
67
|
+
* Attach a categorical variable
|
|
68
|
+
* Modify category labels programmatically
|
|
69
|
+
* Inspect lasso selections from Python
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
import random
|
|
73
|
+
import numpy as np
|
|
74
|
+
import pandas as pd
|
|
75
|
+
import marimo
|
|
76
|
+
|
|
77
|
+
from scatter3d import Scatter3dWidget, Category, LabelListErrorResponse
|
|
78
|
+
|
|
79
|
+
num_points = 10_000
|
|
80
|
+
|
|
81
|
+
# Generate random 3D points
|
|
82
|
+
points = np.random.randn(num_points, 3)
|
|
83
|
+
|
|
84
|
+
# Create a categorical variable
|
|
85
|
+
species_list = ["species1", "species2", "species3"]
|
|
86
|
+
species = random.choices(species_list, k=num_points)
|
|
87
|
+
species = Category(pd.Series(species, name="species"))
|
|
88
|
+
|
|
89
|
+
# Create the widget
|
|
90
|
+
w = Scatter3dWidget(xyz=points, category=species)
|
|
91
|
+
w.point_size = 0.15
|
|
92
|
+
|
|
93
|
+
# Modify allowed labels
|
|
94
|
+
species.set_label_list(
|
|
95
|
+
["species1"],
|
|
96
|
+
on_missing_labels=LabelListErrorResponse.SET_MISSING,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
species.set_label_list(["species1", "species4"])
|
|
100
|
+
|
|
101
|
+
# Display in marimo / Jupyter
|
|
102
|
+
ui = marimo.ui.anywidget(w)
|
|
103
|
+
ui
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
After interacting with the plot (e.g. lasso selection), you can inspect results from Python:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
# Result of the last lasso operation
|
|
110
|
+
ui.lasso_result_t
|
|
111
|
+
|
|
112
|
+
# Category statistics
|
|
113
|
+
print(species.values.value_counts())
|
|
114
|
+
print(species.num_unassigned)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Interactive operation
|
|
118
|
+
|
|
119
|
+
The widget is fully interactive and designed for exploratory annotation workflows. Users can rotate, pan, and zoom the 3D point cloud directly with the mouse. A lasso tool allows drawing a free-form polygon in screen space to select subsets of points. Once a lasso selection is completed, the selection can be applied to the active category using add or remove operations, updating both the visual state and the underlying Python Category object. All interactions are bidirectionally synchronized: changes made via the UI are immediately reflected in Python, and programmatic updates from Python are propagated back to the frontend. This enables tight interactive loops where visual inspection, manual selection, and scripted analysis coexist seamlessly.
|
|
120
|
+
|
|
121
|
+
## Concepts
|
|
122
|
+
|
|
123
|
+
### `Scatter3dWidget`
|
|
124
|
+
|
|
125
|
+
The main widget. It owns the point cloud, rendering state, and interaction logic.
|
|
126
|
+
|
|
127
|
+
### `Category`
|
|
128
|
+
|
|
129
|
+
A wrapper around a categorical vector (pandas or Polars Series) that:
|
|
130
|
+
|
|
131
|
+
* Encodes categories efficiently
|
|
132
|
+
* Tracks unassigned values
|
|
133
|
+
* `Category` objects are **stateful**: updating them updates the widget, and vice versa.
|
|
134
|
+
|
|
135
|
+
### Lasso interaction
|
|
136
|
+
|
|
137
|
+
The lasso tool allows:
|
|
138
|
+
|
|
139
|
+
* Selecting points in screen space
|
|
140
|
+
* Adding or removing points from a category
|
|
141
|
+
* Reading back selection results in Python
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
scatter3d/__init__.py,sha256=nVTx6roiwzTe5lpGoMHw7pDZovHdhbxcVOgg4J3trT4,142
|
|
2
|
+
scatter3d/scatter3d.py,sha256=HPAjrZWAEAaOPBuQnN1Lh2PzFyPajNPLrHi60EM5_Yg,36334
|
|
3
|
+
scatter3d/static/scatter3d.js,sha256=TpMbhUKtVOHg8Aydh00Ti0N8iTs90TeAsMSxLhvip-0,889726
|
|
4
|
+
scatter3d/static/scatter3d.js.map,sha256=gd3ISXRZrHl_rQ9W5AZqyGU12T-dggRkFnytpp-ZrpQ,3013931
|
|
5
|
+
scatter3d/widget_test.py,sha256=sWxewIrhcNwssyd6TJyG1ulXciPX7D0QRLxVxml6VhQ,1060
|
|
6
|
+
scatter3d_anywidget-0.1.2.dist-info/WHEEL,sha256=eycQt0QpYmJMLKpE3X9iDk8R04v2ZF0x82ogq-zP6bQ,79
|
|
7
|
+
scatter3d_anywidget-0.1.2.dist-info/METADATA,sha256=0bURpON3pHQkuhAPm6JpjgM7skv6i25-yM5s3FsS-no,4697
|
|
8
|
+
scatter3d_anywidget-0.1.2.dist-info/RECORD,,
|