Add remove() in Shapes and Points - #8031
Conversation
remove , 'remove_selected and pop` in Shapes and Pointsremove , remove_selected and pop in Shapes and Points
|
The test fails appear legit, because the event is moved from remove_selected to remove. |
|
@TimMonko @psobolewskiPhD — thank you for pointing out the mistake . |
|
@rahul713rk There's some minor merge conflicts, do you think you can resolve them or would you rather one of the maintainers do so? Edit: also we will need some tests for the new methods. I think you can just copy and modify the existing test, e.g. for points: |
|
@jni you had thoughts on this previously, could you take a look at the implementation and see what you think? |
|
Moving to 0.6.3 so we have time to look at this, which unfortunately I don't have right now! Thanks @rahul713rk for the work you've done so far! |
|
Thanks @psobolewskiPhD , @TimMonko for your support and guidance , i have resolved the conflict and add test cases with name |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #8031 +/- ##
==========================================
+ Coverage 93.00% 93.06% +0.06%
==========================================
Files 702 702
Lines 63269 63321 +52
==========================================
+ Hits 58841 58930 +89
+ Misses 4428 4391 -37 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| def pop(self) -> None: | ||
| """Remove the last point from the layer.""" | ||
| visible_indices = self._indices_view.tolist() | ||
| if not visible_indices: | ||
| # If no points are visible, do nothing | ||
| show_warning('No points to remove.') | ||
| return | ||
| self.remove([visible_indices[-1]]) |
There was a problem hiding this comment.
Do we want pop to work on visible only? I think i lean towards just removing the last item regardless?
On the other hand, there may be some utility in removing from visible items only 🤔
Also is it worth making pop more like the Python List method, where it returns the item? that method also accepts an index for what it's worth...
|
Thanks for resolving the conflicts and your patience @rahul713rk
|
|
Yes, IMO we should aim to stick as close as possible to what pop means in other python contexts (like def pop(self, index=-1) -> npt.ArrayLike: # of shape (D,)And it shouldn't use |
|
Another option is to split off the |
shouldn't we return dictionary (for poped item only) containing instead of returning a list containing data of poped item
|
|
Uh, I was thinking to only return the data itself (coordinates), but I guess we could do like you said and return a dict.
Yeah good idea! @rahul713rk do you think you could split this |
|
thanks @brisvag , sure going to remove |
|
I pushed the requested change @brisvag from |
|
The windows test fails are unrelated, happening everywhere. Maybe from the windows runner changes. |
version, checking selected_data after removal
|
@brisvag OK, i think selection works correctly now. it was actually tricky, because when you remove items from the set, the indices of remaining items change! So you need to update the indices that are in selected_data. |
brisvag
left a comment
There was a problem hiding this comment.
Nice! Only thing is maybe to check for performance, but otherwise LGTM.
| for idx in self.selected_data: | ||
| # If the selected index was removed, skip it | ||
| if idx in indices: | ||
| continue | ||
| # Count how many indicies have been removed prior | ||
| shift = bisect.bisect_left(indices, idx) | ||
| new_selected.add(idx - shift) |
There was a problem hiding this comment.
Did you test with big arrays if this is slow?
There was a problem hiding this comment.
so what's the worst case scenario here, it's large number of points and then selecting all of them?
Should probably check for that case and short circuit the loop.
There was a problem hiding this comment.
But otherwise large number of points with most selected and some being deleted?
There was a problem hiding this comment.
Not sure, I guess it depends on what's slower: the loop or calling bisect_left on a big array?
There was a problem hiding this comment.
Ok so performance in the case of many selected points and removing some subset of points was not great.
The last two commits improve that case significantly, like 10X, by using sets better and using essentially the same approach as bisect, but numpy vectorized. (I got a little help from Gemini, testing it out.)
Removing selected shapes is horribly slow. I made an issue for that:
- Removing selected shapes is very slow #8264
It requires a batch method on ShapesList or something -- outside the scope of this PR.
There was a problem hiding this comment.
Note, it's technically not a regression, because on main you can't have some points selected and remove some other/overlaping set of points.
Removing all selected is not affected performance wise.
There was a problem hiding this comment.
Removing all selected shapes is now marginally faster, but still horrendously slow once you get beyond 100s of shapes.
There was a problem hiding this comment.
Nice, thanks for diving in!
| selected_not_removed = self.selected_data - set(indices) | ||
| if selected_not_removed: | ||
| indices_array = np.array(indices) | ||
| remaining_selected = np.array(list(selected_not_removed)) |
There was a problem hiding this comment.
using np.fromiter seems almost 2X faster on my machine than passing through a list intermediate.
There was a problem hiding this comment.
Yea using that is a bit faster. Like 10% or so. I will push the change when I get some free time.
|
Hm. I think we should add benchmarks for removing shapes and points and run on this PR. @brisvag @TimMonko @psobolewskiPhD Should I make a separate PR with benchmarks or expect contributor to do it here? |
|
I think it's best if you do it since yopu're most familiar; also I think @psobolewskiPhD mostly topok over this PR anyways :) |
|
IMO deep optimization is out of scope here? I made an issue for the slow shape removal. It's super slow on main too. |
|
I think it's more whether we are introducting a regression. But yeah, if you think it's good on that, we can do the benchmark separately IMO 👍 |
There is no regression, because prior to this PR you could only remove selected shapes/points and the code for that is unchanged. What this adds is removing by index, which means you can potentially have a set of selected shapes/points and remove some other set of points/shapes. This case requires shifting the indexes of the selected points, which I implemented. There may be optimizations possible, by using more numpy set functions? However, to be honest, I suspect that most people will either use Removing shapes is just slow period, with or without this pr: #8264 |
brisvag
left a comment
There was a problem hiding this comment.
Alright, at last I think we're good to go and get this merged :) Probably immediatly followed up by #8070, once conflicts are solved!
Thanks @rahul713rk, sorry again for taking long on this :)
remove and remove_selected in Shapes and Pointsremove() and remove_selected() in Shapes and Points
remove() and remove_selected() in Shapes and Pointsremove() in Shapes and Points
# References & Related Issues * Closes #8070 * Follow-up to: #7964 , #8031 --- # Description * Implemented a `pop` method for both the `Points` and `Shapes` layers, enabling removal and return of point/shape data at a specified index. * Added tests for `pop` method. --------- Co-authored-by: a <a> Co-authored-by: Peter Sobolewski <76622105+psobolewskiPhD@users.noreply.github.com> Co-authored-by: Lorenzo Gaifas <brisvag@gmail.com>
|
This pull request has been mentioned on Image.sc Forum. There might be relevant details there: |
References and relevant issues
Closes #7964
Description
This PR enhances shape and point layer functionality by introducing new methods and improving existing ones:
remove()method to both Shapes and Points layers for explicit item removalremove_selected()to use the newremove()internally, improving code clarity and modularityEDIT : Added tests for
removefor both Points and Shapes (test_removing_points,test_removing_shapes)and Remove
popmethod from this PR for follow-up issue #8070