-
Notifications
You must be signed in to change notification settings - Fork 247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/new filter #1068
Feature/new filter #1068
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for looking into improving the filter functionality! This has been on our todo list for quite some time. Your approach is adding a number of new classes to neo. Intuitively this looks like an approach that might add a bit of overhead to filtering. Did you also run some performance tests with your new implementation? @samuelgarcia What do you think?
Co-authored-by: Julia Sprenger <julia.sprenger@rwth-aachen.de>
Co-authored-by: Julia Sprenger <julia.sprenger@rwth-aachen.de>
Co-authored-by: Julia Sprenger <julia.sprenger@rwth-aachen.de>
Thank you for your feedback and suggestions. I'm looking into it. |
Thanks for the PR, Oliver, and for reviewing the code, Julia. Regarding performance, there is actually another follow-up PR by @kloss-o in prep that would improve filtering speed quite a bit, but unrelated to this concrete implementation. @kloss-o , I think when you did some benchmarking the The idea behind the different Filter classes was, for one, that it would allow users to also define their own "custom" filters as a filter function, like (in pseudocode): class FilterGoodElectrodesSurroundedByGoodOnes(FilterCondition)
def test(electrode):
return is_good_quality[electrode] and np.all(is_good_quality[i] for i in neighbor_electrodes(electrode]) Secondly, that it would not require any new or ambiguous syntax (e.g., something like However, I think there are many other options. An alternative could be to revert to, e.g., simple filter functions, at the expense of being slightly more ambiguous. |
Co-authored-by: Julia Sprenger <julia.sprenger@rwth-aachen.de>
The following notebook is intended to show the application of the 'new filter' function in practice. Link to notebook GitHub gist: https://gist.github.com/Moritz-Alexander-Kern/afdfb707bde9052c6cd02ccddd2c4b9e |
Here some notes on the discussion of this PR during the neo core meeting today. |
Hi @kloss-o @Moritz-Alexander-Kern any news on this? Tell me if the notes from the last meeting are not detailed enough for you to continue on this topic. |
Review new filter
# Conflicts: # neo/core/container.py # neo/test/coretest/test_container.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is in good shape now and almost ready to merge. I have suggested some changes to filter class names, and I would also prefer it if all whitespace-only changes were reverted, to make it easier to see where the code has actually changed. The most important change, I think, is to keep the functions in their own module and do not import them into the top-level namespace, i.e. they should be used as
from neo.filters import LessThan
or
import neo.filters as nf
...nf.LessThan...
neo/core/__init__.py
Outdated
@@ -35,6 +37,11 @@ | |||
from neo.core.analogsignal import AnalogSignal | |||
from neo.core.irregularlysampledsignal import IrregularlySampledSignal | |||
|
|||
# Import FilterClasses | |||
from neo.core.filters import Equal, IsIn, IsNot,\ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the filters should be imported into the top-level namespace,
rather just from neo.core import filters
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, I used from neo.core import filters
neo/core/container.py
Outdated
@@ -18,27 +20,28 @@ def unique_objs(objs): | |||
using the "is" test. | |||
""" | |||
seen = set() | |||
return [obj for obj in objs | |||
if id(obj) not in seen and not seen.add(id(obj))] | |||
return [obj for obj in objs if id(obj) not in seen and not seen.add(id(obj))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please revert all white-space only changes, they make it harder to see the substantial changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
White-space only changes removed.
neo/core/container.py
Outdated
|
||
|
||
def filterdata(data, targdict=None, objects=None, **kwargs): | ||
""" | ||
Return a list of the objects in data matching *any* of the search terms | ||
Return a list of child objects matching *any* of the search terms |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
child of what? I think the original docstring is more understandable, perhaps put "objects in data
" to make it clear that we're referring to the variable named data
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reverted to:
Return a list of the objects in data matching *any* of the search terms
neo/core/container.py
Outdated
results.append(obj) | ||
|
||
# remove duplicates from results | ||
res = [] | ||
[res.append(result) for result in results if not any([result is elem for elem in res])] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks as though it will be slow - perhaps use a set
to eliminate duplicates?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried with:
res = list(set(results))
but I get:
> res = list(set(results))
E TypeError: unhashable type: 'SpikeTrain'
any ideas/help?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps use the Python id()
function?, e.g.,
unique_results = { id(res): res for res in results }.values()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect, thanks!
Used:
# remove duplicates from results
results = list({ id(res): res for res in results }.values())
neo/core/container.py
Outdated
@@ -188,13 +195,12 @@ class Container(BaseNeo): | |||
# Containers that are listed when pretty-printing | |||
_repr_pretty_containers = () | |||
|
|||
def __init__(self, name=None, description=None, file_origin=None, | |||
**annotations): | |||
def __init__(self, name=None, description=None, file_origin=None, **annotations): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment as above about whitespace-only changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed whitespace-only changes.
neo/core/filters.py
Outdated
raise NotImplementedError() | ||
|
||
|
||
class Equal(FilterCondition): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would call this Equals
or EqualTo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used Equals
neo/core/filters.py
Outdated
return x != self.control | ||
|
||
|
||
class LessThanEqual(FilterCondition): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LessThanOrEquals
or LessThanOrEqualTo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used LessThanOrEquals
neo/core/filters.py
Outdated
return x <= self.control | ||
|
||
|
||
class GreaterThanEqual(FilterCondition): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GreaterThanOrEquals
or GreaterThanOrEqualTo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used GreaterThanOrEquals
…, as no duplicate keys can exist for a dict, duplicates will be removed with this
Hey @apdavison and @JuliaSprenger , If you have any further suggestions or if there are any remaining issues, please don't hesitate to let me or @mdenker know. |
Until now it was not so easy to filter for multiple values with the filter function in neo. To solve this problem, I introduced "FilterConditions". These can be provided to the filter function as the value of a dictionary.
An example of the syntax:
seg.filter({“electrode_id”: FilterCondition) or seg.filter(electrode_id = FilterCondition)
In order to be able to filter for several electrode_ids, there are some objects that inherit from FilterCondition and provide different filtering options.
For example, seg.filter({"electrode_id": neo.FilterIsIn([0, 1, 5]) can be used to filter for all objects with electrode_id 0, 1 or 5.
List of all currently added FilterConditions:
-FilterEqual(x), returns id == x
-FilterIsNot(x), returns id != x
-FilterLessThan(x), returns id < x
-FilterLessThanEqual(x), returns id <= x
-FilterGreaterThan(x), returns id > x
-FilterGreaterThanEqual(x), returns id >= x
-FilterIsIn([x, y, z]), returns id == x or id == y or id == z
-FilterInRange(a, b), returns a <= id <= b
Additional FilterConditions can be added at any time and without much effort.