Skip to content
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

Change of default behavior for MAXIMA_THRESHOLD_WINDOW #49

Merged
merged 4 commits into from
Jul 18, 2024

Conversation

cosimolupo
Copy link
Contributor

  • MAXIMA_THRESHOLD_WINDOW has now a default value equal to 'None', corresponding to set it equal to the entire signal duration. Also accepts float values.
  • Little fix involving plot_minima function in minima.py script, now plotting the interpolated position of minima when required.

@mdenker
Copy link
Member

mdenker commented Feb 22, 2024

@cosimolupo Please look through again and update.

@@ -200,7 +203,7 @@ def plot_minima(asig, event, channel, maxima_threshold_window,
plot_minima(asig=time_slice(asig, args.plot_tstart, args.plot_tstop),
event=time_slice(transition_event, args.plot_tstart, args.plot_tstop),
channel=int(channel),
maxima_threshold_window = args.maxima_threshold_window,
maxima_threshold_window = min(args.maxima_threshold_window, args.plot_tstop-args.plot_tstart),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is redundant with the changes in lines 186-188

Comment on lines 187 to 188
if args.maxima_threshold_window is None or args.maxima_threshold_window > asig.t_stop - asig.t_start:
args.maxima_threshold_window = asig.t_stop - asig.t_start
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

args.maxima_threshold_window is a float value for a time period in seconds, whereas asig.t_stop and asig.t_start are quantity values. So, before comparing them or assigning one to the other, the quantity values need to be rescaled to seconds and converted to floats.

@cosimolupo cosimolupo force-pushed the maxima_threshold_window_PR branch from 961e393 to 56cf073 Compare March 19, 2024 11:01
@cosimolupo
Copy link
Contributor Author

@mdenker, I re-aligned this branch to the current HEAD of NeuralEnsemble/cobrawap, and then added back all the changes, updating them according to our recent discussions and to @rgutzen's comments above.

@cosimolupo cosimolupo requested a review from gulpgiulia March 19, 2024 11:15
@gulpgiulia
Copy link
Collaborator

I went through the proposed changes and checked for consistency and coherence; done from my side

Copy link
Collaborator

@gulpgiulia gulpgiulia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went through the proposed changes and checked for consistency and coherence; done from my side

@cosimolupo cosimolupo self-assigned this Mar 20, 2024
@cosimolupo cosimolupo added this to the v0.2.0 milestone Mar 21, 2024
@mdenker
Copy link
Member

mdenker commented May 22, 2024

After final look and review, merge together with version number increase #62 and #63

@@ -64,9 +64,11 @@ NUM_INTERPOLATION_POINTS: 0
# minimum distance between two peaks (s)
MIN_PEAK_DISTANCE: 0.28
# amplitude fraction to set the threshold detecting local maxima
MAXIMA_THRESHOLD_FRACTION: .5
# range of allowed values is [0,1], default value is 0.5
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comments shouldn't describe default values, except they have special significance.
You can just remove this line, since the range is already implied by 'fraction'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed with a new commit.

Comment on lines -59 to +81
def moving_threshold(signal, window, fraction):
# compute a dynamic threshold function through a sliding window
# on the signal array
strides = np.lib.stride_tricks.sliding_window_view(signal, window)
threshold_func = np.min(strides, axis=1) + fraction*np.ptp(strides, axis=1)
def moving_threshold(signal, maxima_threshold_window, maxima_threshold_fraction):
threshold_signal = []

sampling_rate = signal.sampling_rate.rescale('Hz').magnitude
duration = float(signal.t_stop.rescale('s').magnitude - signal.t_start.rescale('s').magnitude)
if maxima_threshold_window is None or maxima_threshold_window > duration:
maxima_threshold_window = duration
window_frame = int(maxima_threshold_window*sampling_rate)

for channel, channel_signal in enumerate(signal.T):
if np.isnan(channel_signal).any():
threshold_func = np.full(np.shape(signal)[0], np.nan)
else:
# compute a dynamic threshold function through a sliding window
# on the signal array
strides = np.lib.stride_tricks.sliding_window_view(channel_signal, window_frame)
threshold_func = np.min(strides, axis=1) + maxima_threshold_fraction*np.ptp(strides, axis=1)
# add elements at the beginning
threshold_func = np.append(np.ones(window_frame//2)*threshold_func[0], threshold_func)
threshold_func = np.append(threshold_func, np.ones(len(channel_signal)-len(threshold_func))*threshold_func[-1])
threshold_signal.append(threshold_func)

threshold_signal = neo.AnalogSignal(np.array(threshold_signal).T, units=signal.units, sampling_rate=signal.sampling_rate)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why create a separate threshold analogsignal here? It seems it requires more code and more RAM

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the previous version of the code, the threshold was computed channel-wise in the detect_minima function, and then again in plot_minima function for each channel to be plotted. The new implementation does it only once for each channel.
Also, for a given channel to be visualized, the signal to be plotted was trimmed as [plot_tstart, plot_tstop] before passing it to plot_minima function. This implied that the moving threshold had to be computed on the signal after the trimming, with minima found and plotted consequently. This was different from what done in detect_minima function, where moving thresholds and minima were computed on the whole signal, with no trimming. This means that minima reported in plots could have been different from minima actually detected for a certain channel. Thanks to @mdenker for pointing this out during one of our discussions.
So, we decided to coherently compute moving threshold on the whole signal for each channel, only once, and then use such threshold_signal for coming minima detection and visualization. On one side, this certainly implies a larger RAM usage, but ensures a more coherent and robust computation of the moving thresholds. At the same time, larger number of code lines in moving_threshold function are almost balanced by code lines now useless and hence removed elsewhere in the code.

@mdenker
Copy link
Member

mdenker commented Jul 3, 2024

@rgutzen Please recheck the latest changes.

Copy link
Collaborator

@rgutzen rgutzen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving these changes, but we need to keep in mind that this involves a duplication of the signal (i.e. temporarily doubling the RAM demands), and if this poses an issue in the future we can come back to this block to find another solution.

@mdenker mdenker merged commit 491aed3 into NeuralEnsemble:master Jul 18, 2024
@cosimolupo cosimolupo deleted the maxima_threshold_window_PR branch August 14, 2024 15:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants