Skip to content

Commit 17d3204

Browse files
authored
Merge pull request #364 from BiAPoL/fix-cluster-memorization
Fix cluster memorization
2 parents 577e351 + aaf8f98 commit 17d3204

File tree

4 files changed

+355
-38
lines changed

4 files changed

+355
-38
lines changed

notebooks/demo_new_plotter.ipynb

Lines changed: 243 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,7 @@
1818
"cell_type": "code",
1919
"execution_count": 2,
2020
"metadata": {},
21-
"outputs": [
22-
{
23-
"name": "stderr",
24-
"output_type": "stream",
25-
"text": [
26-
"napari.manifest -> 'napari-vedo-bridge' could not be imported: The name field in the manifest ('napari-clusters-plotter') must match the package name ('napari-vedo-bridge')\n"
27-
]
28-
}
29-
],
21+
"outputs": [],
3022
"source": [
3123
"viewer = napari.Viewer()"
3224
]
@@ -46,7 +38,7 @@
4638
{
4739
"data": {
4840
"text/plain": [
49-
"<Points layer 'points2' at 0x204f46b5c10>"
41+
"<Points layer 'points2' at 0x12998840130>"
5042
]
5143
},
5244
"execution_count": 3,
@@ -63,10 +55,10 @@
6355
"frame = np.arange(n_timeframes).repeat(n_samples//n_timeframes)\n",
6456
"# make some random points with random features\n",
6557
"points = np.random.random((n_samples, 4))\n",
66-
"points2 = np.random.random((n_samples, 4))\n",
58+
"points2 = np.random.random((n_samples-1, 4))\n",
6759
"\n",
6860
"points[:, 0] = frame\n",
69-
"points2[:, 0] = frame\n",
61+
"points2[:, 0] = frame[:-1]\n",
7062
"\n",
7163
"features = pd.DataFrame({\n",
7264
" 'frame': frame,\n",
@@ -76,13 +68,14 @@
7668
" 'feature4': np.random.normal(size=n_samples, loc=loc),})\n",
7769
"\n",
7870
"features2 = pd.DataFrame({\n",
79-
" 'frame': frame,\n",
80-
" 'feature2': np.random.normal(size=n_samples, loc=-loc),\n",
81-
" 'feature3': np.random.normal(size=n_samples, loc=-loc),\n",
82-
" 'feature4': np.random.normal(size=n_samples, loc=-loc),})\n",
71+
" 'frame': frame[:-1],\n",
72+
" 'feature2': np.random.normal(size=n_samples-1, loc=-loc),\n",
73+
" 'feature3': np.random.normal(size=n_samples-1, loc=-loc),\n",
74+
" 'feature4': np.random.normal(size=n_samples-1, loc=-loc),})\n",
8375
"\n",
8476
"layer = napari.layers.Points(points, features=features, size=0.1, blending='translucent_no_depth')\n",
8577
"layer2 = napari.layers.Points(points2, features=features2, size=0.1, translate=(0, 0, 2), blending='translucent_no_depth')\n",
78+
"viewer.layers.clear()\n",
8679
"viewer.add_layer(layer)\n",
8780
"viewer.add_layer(layer2)\n"
8881
]
@@ -105,7 +98,7 @@
10598
{
10699
"data": {
107100
"text/plain": [
108-
"<napari._qt.widgets.qt_viewer_dock_widget.QtViewerDockWidget at 0x2049456d3a0>"
101+
"<napari._qt.widgets.qt_viewer_dock_widget.QtViewerDockWidget at 0x1299a10a820>"
109102
]
110103
},
111104
"execution_count": 4,
@@ -118,6 +111,238 @@
118111
"viewer.window.add_dock_widget(plotter_widget, area='right')"
119112
]
120113
},
114+
{
115+
"cell_type": "code",
116+
"execution_count": 7,
117+
"metadata": {},
118+
"outputs": [
119+
{
120+
"data": {
121+
"text/html": [
122+
"<div>\n",
123+
"<style scoped>\n",
124+
" .dataframe tbody tr th:only-of-type {\n",
125+
" vertical-align: middle;\n",
126+
" }\n",
127+
"\n",
128+
" .dataframe tbody tr th {\n",
129+
" vertical-align: top;\n",
130+
" }\n",
131+
"\n",
132+
" .dataframe thead th {\n",
133+
" text-align: right;\n",
134+
" }\n",
135+
"</style>\n",
136+
"<table border=\"1\" class=\"dataframe\">\n",
137+
" <thead>\n",
138+
" <tr style=\"text-align: right;\">\n",
139+
" <th></th>\n",
140+
" <th>frame</th>\n",
141+
" <th>feature2</th>\n",
142+
" <th>feature3</th>\n",
143+
" <th>feature4</th>\n",
144+
" <th>MANUAL_CLUSTER_ID</th>\n",
145+
" </tr>\n",
146+
" </thead>\n",
147+
" <tbody>\n",
148+
" <tr>\n",
149+
" <th>0</th>\n",
150+
" <td>0</td>\n",
151+
" <td>-4.725088</td>\n",
152+
" <td>-4.325642</td>\n",
153+
" <td>-7.112442</td>\n",
154+
" <td>1</td>\n",
155+
" </tr>\n",
156+
" <tr>\n",
157+
" <th>1</th>\n",
158+
" <td>0</td>\n",
159+
" <td>-4.386041</td>\n",
160+
" <td>-5.335577</td>\n",
161+
" <td>-6.063437</td>\n",
162+
" <td>1</td>\n",
163+
" </tr>\n",
164+
" <tr>\n",
165+
" <th>2</th>\n",
166+
" <td>0</td>\n",
167+
" <td>-5.730792</td>\n",
168+
" <td>-4.642161</td>\n",
169+
" <td>-4.429953</td>\n",
170+
" <td>1</td>\n",
171+
" </tr>\n",
172+
" <tr>\n",
173+
" <th>3</th>\n",
174+
" <td>0</td>\n",
175+
" <td>-6.089492</td>\n",
176+
" <td>-2.991715</td>\n",
177+
" <td>-5.544447</td>\n",
178+
" <td>0</td>\n",
179+
" </tr>\n",
180+
" <tr>\n",
181+
" <th>4</th>\n",
182+
" <td>0</td>\n",
183+
" <td>-4.740159</td>\n",
184+
" <td>-5.189429</td>\n",
185+
" <td>-4.769790</td>\n",
186+
" <td>1</td>\n",
187+
" </tr>\n",
188+
" <tr>\n",
189+
" <th>...</th>\n",
190+
" <td>...</td>\n",
191+
" <td>...</td>\n",
192+
" <td>...</td>\n",
193+
" <td>...</td>\n",
194+
" <td>...</td>\n",
195+
" </tr>\n",
196+
" <tr>\n",
197+
" <th>94</th>\n",
198+
" <td>4</td>\n",
199+
" <td>-5.815555</td>\n",
200+
" <td>-4.737373</td>\n",
201+
" <td>-4.945831</td>\n",
202+
" <td>0</td>\n",
203+
" </tr>\n",
204+
" <tr>\n",
205+
" <th>95</th>\n",
206+
" <td>4</td>\n",
207+
" <td>-6.226173</td>\n",
208+
" <td>-3.972162</td>\n",
209+
" <td>-6.477352</td>\n",
210+
" <td>0</td>\n",
211+
" </tr>\n",
212+
" <tr>\n",
213+
" <th>96</th>\n",
214+
" <td>4</td>\n",
215+
" <td>-4.597238</td>\n",
216+
" <td>-3.308072</td>\n",
217+
" <td>-4.418548</td>\n",
218+
" <td>0</td>\n",
219+
" </tr>\n",
220+
" <tr>\n",
221+
" <th>97</th>\n",
222+
" <td>4</td>\n",
223+
" <td>-6.143753</td>\n",
224+
" <td>-5.130647</td>\n",
225+
" <td>-5.700410</td>\n",
226+
" <td>0</td>\n",
227+
" </tr>\n",
228+
" <tr>\n",
229+
" <th>98</th>\n",
230+
" <td>4</td>\n",
231+
" <td>-4.029651</td>\n",
232+
" <td>-5.172430</td>\n",
233+
" <td>-5.987246</td>\n",
234+
" <td>1</td>\n",
235+
" </tr>\n",
236+
" </tbody>\n",
237+
"</table>\n",
238+
"<p>99 rows × 5 columns</p>\n",
239+
"</div>"
240+
],
241+
"text/plain": [
242+
" frame feature2 feature3 feature4 MANUAL_CLUSTER_ID\n",
243+
"0 0 -4.725088 -4.325642 -7.112442 1\n",
244+
"1 0 -4.386041 -5.335577 -6.063437 1\n",
245+
"2 0 -5.730792 -4.642161 -4.429953 1\n",
246+
"3 0 -6.089492 -2.991715 -5.544447 0\n",
247+
"4 0 -4.740159 -5.189429 -4.769790 1\n",
248+
".. ... ... ... ... ...\n",
249+
"94 4 -5.815555 -4.737373 -4.945831 0\n",
250+
"95 4 -6.226173 -3.972162 -6.477352 0\n",
251+
"96 4 -4.597238 -3.308072 -4.418548 0\n",
252+
"97 4 -6.143753 -5.130647 -5.700410 0\n",
253+
"98 4 -4.029651 -5.172430 -5.987246 1\n",
254+
"\n",
255+
"[99 rows x 5 columns]"
256+
]
257+
},
258+
"execution_count": 7,
259+
"metadata": {},
260+
"output_type": "execute_result"
261+
}
262+
],
263+
"source": [
264+
"viewer.layers[-1].features"
265+
]
266+
},
267+
{
268+
"cell_type": "code",
269+
"execution_count": 6,
270+
"metadata": {},
271+
"outputs": [
272+
{
273+
"data": {
274+
"text/plain": [
275+
"(99, 4)"
276+
]
277+
},
278+
"execution_count": 6,
279+
"metadata": {},
280+
"output_type": "execute_result"
281+
}
282+
],
283+
"source": [
284+
"viewer.layers[1].data.shape"
285+
]
286+
},
287+
{
288+
"cell_type": "code",
289+
"execution_count": 6,
290+
"metadata": {},
291+
"outputs": [
292+
{
293+
"data": {
294+
"text/plain": [
295+
"array([1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0,\n",
296+
" 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0,\n",
297+
" 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0,\n",
298+
" 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1,\n",
299+
" 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0])"
300+
]
301+
},
302+
"execution_count": 6,
303+
"metadata": {},
304+
"output_type": "execute_result"
305+
}
306+
],
307+
"source": [
308+
"viewer.layers.selection.active = viewer.layers[-1]\n",
309+
"plotter_widget._selectors['x'].setCurrentText('feature3')\n",
310+
"plotter_widget.plotting_widget.active_artist.color_indices = np.random.randint(0, 2, n_samples)\n",
311+
"plotter_widget.plotting_widget.active_artist.color_indices"
312+
]
313+
},
314+
{
315+
"cell_type": "code",
316+
"execution_count": 7,
317+
"metadata": {},
318+
"outputs": [
319+
{
320+
"data": {
321+
"text/plain": [
322+
"array([1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0,\n",
323+
" 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0,\n",
324+
" 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0,\n",
325+
" 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1,\n",
326+
" 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0])"
327+
]
328+
},
329+
"execution_count": 7,
330+
"metadata": {},
331+
"output_type": "execute_result"
332+
}
333+
],
334+
"source": [
335+
"viewer.layers.selection.active = viewer.layers[0]\n",
336+
"plotter_widget.plotting_widget.active_artist.color_indices"
337+
]
338+
},
339+
{
340+
"cell_type": "code",
341+
"execution_count": 8,
342+
"metadata": {},
343+
"outputs": [],
344+
"source": []
345+
},
121346
{
122347
"cell_type": "code",
123348
"execution_count": 6,
@@ -377,7 +602,7 @@
377602
"name": "python",
378603
"nbconvert_exporter": "python",
379604
"pygments_lexer": "ipython3",
380-
"version": "3.9.19"
605+
"version": "3.9.18"
381606
}
382607
},
383608
"nbformat": 4,

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ dependencies = [
4848
"scikit-image",
4949
"superqt",
5050
"scipy",
51-
"biaplotter"
51+
"biaplotter@https://github.com/BiAPoL/biaplotter/archive/refs/heads/main.zip"
5252
]
5353

5454

src/napari_clusters_plotter/_new_plotter_widget.py

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,24 @@ def _replot(self):
152152
if self.x_axis == "" or self.y_axis == "":
153153
return
154154

155-
data_to_plot = self._get_data()
156-
self.plotting_widget.active_artist.data = data_to_plot
155+
# retrieve the data from the selected layers
156+
features = self._get_features()
157+
x_data = features[self.x_axis].values
158+
y_data = features[self.y_axis].values
159+
160+
# # if no hue is selected, set it to 0
161+
# if self.hue_axis == "None":
162+
# hue = np.zeros(len(features))
163+
# elif self.hue_axis != "":
164+
# hue = features[self.hue_axis].values
165+
166+
self.plotting_widget.active_artist.data = np.stack(
167+
[x_data, y_data], axis=1
168+
)
169+
if "MANUAL_CLUSTER_ID" in features.columns:
170+
self.plotting_widget.active_artist.color_indices = features[
171+
"MANUAL_CLUSTER_ID"
172+
].values
157173

158174
def _checkbox_status_changed(self):
159175
self._replot()
@@ -264,22 +280,6 @@ def n_selected_layers(self) -> int:
264280
"""
265281
return len(list(self.viewer.layers.selection))
266282

267-
def _get_data(self) -> np.ndarray:
268-
"""
269-
Get the data from the selected layers features.
270-
"""
271-
features = self._get_features()
272-
x_data = features[self.x_axis].values
273-
y_data = features[self.y_axis].values
274-
275-
# # if no hue is selected, set it to 0
276-
# if self.hue_axis == "None":
277-
# hue = np.zeros(len(features))
278-
# elif self.hue_axis != "":
279-
# hue = features[self.hue_axis].values
280-
281-
return np.stack([x_data, y_data], axis=1)
282-
283283
def _on_update_layer_selection(
284284
self, event: napari.utils.events.Event
285285
) -> None:
@@ -327,7 +327,11 @@ def _update_feature_selection(
327327
self._selectors[dim].clear()
328328

329329
for dim in ["x", "y", "hue"]:
330-
self._selectors[dim].addItems(sorted(self.common_columns))
330+
features_to_add = sorted(self.common_columns)
331+
if "MANUAL_CLUSTER_ID" in features_to_add:
332+
features_to_add.remove("MANUAL_CLUSTER_ID")
333+
334+
self._selectors[dim].addItems(features_to_add)
331335

332336
# it should always be possible to select no color
333337
self._selectors["hue"].addItem("None")
@@ -361,6 +365,11 @@ def _color_layer_by_cluster_id(self):
361365
].index
362366
_apply_layer_color(selected_layer, colors[layer_indices])
363367

368+
# store cluster indeces in the features table
369+
selected_layer.features["MANUAL_CLUSTER_ID"] = color_indices[
370+
layer_indices
371+
]
372+
364373
def _reset(self):
365374
"""
366375
Reset the selection in the current plotting widget.

0 commit comments

Comments
 (0)