Skip to content

Fxns: Initial reach & drop trials - #705

Open
vaevictori wants to merge 5 commits into
masterfrom
initial_reach
Open

Fxns: Initial reach & drop trials#705
vaevictori wants to merge 5 commits into
masterfrom
initial_reach

Conversation

@vaevictori

Copy link
Copy Markdown
Collaborator

Add functions to 1) identify trials that have gaps in data over a specified time threshold; and 2) identify the index where an initial reach ends using peak-finding on movement speed. Add human center-out test data for these functions.

@vaevictori
vaevictori requested a review from leoscholl August 19, 2025 20:38
Comment thread aopy/analysis/base.py
continue

# 1) Find peaks in speed and get their heights
peak_idx, peak_properties = find_peaks(speed, height=speed_threshold)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

i think you need to include distance=(0.4*samplerate) to be consistent with the paper. this will make sure the peaks are all +/- 200ms apart, eliminating the smaller peaks first.

you can also include prominence=prominence_fraction here so you don't have to separately compute it later.

Comment thread aopy/analysis/base.py
Comment on lines +2697 to +2706
# 2) Compute prominences for those peaks
prominences, left_bases, right_bases = peak_prominences(speed, peak_idx)

# 3) Keep peaks with prominence >= (prominence_fraction * peak_height)
peak_heights = peak_properties["peak_heights"]
keep = ( prominences >= (prominence_fraction * peak_heights) )
peak_idx = peak_idx[keep]
if peak_idx.size == 0:
split_indices.append(len(traj))
continue

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can be simplified by passing the prominence argument to find_peaks

Comment thread aopy/analysis/base.py
Comment on lines +2708 to +2728
# 4) Find troughs (local minima) in speed
trough_idx = find_peaks(-speed)[0] # peaks of -speed are minima of speed
if trough_idx.size == 0:
split_indices.append(len(traj))
continue

# 5) For each qualifying peak in chronological order, find the first trough after it.
peak_idx = np.sort(peak_idx)
no_initial_peak = True
for p in peak_idx:
# first trough strictly after the peak
after = trough_idx[trough_idx > p]
if after.size == 0:
continue
t = after[0]

# Check the "initial peak" criterion: the trough position is >= distance_threshold
if np.linalg.norm(trajectory[t]) >= distance_threshold:
split_indices.append(int(t))
no_initial_peak = False
break

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

i'm not sure i understand this part. what are you trying to do here? maybe this can be accomplished with distance parameter in find_peaks?

Comment thread aopy/postproc/base.py
Comment on lines +526 to +542
nans = np.any(np.isnan(trial), axis=1)

max_consecutive = 0
current_consecutive = 0

for nan in nans:

if nan:
current_consecutive += 1
max_consecutive = max(max_consecutive, current_consecutive)
else:
current_consecutive = 0

if (max_consecutive / samplerate) > max_gap:
drop.append(True)
else:
drop.append(False)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this looks good. it maybe slow if there are a lot of trials, i suggest using the existing function from utils if you add from .. import utils somewhere at the top, then:

Suggested change
nans = np.any(np.isnan(trial), axis=1)
max_consecutive = 0
current_consecutive = 0
for nan in nans:
if nan:
current_consecutive += 1
max_consecutive = max(max_consecutive, current_consecutive)
else:
current_consecutive = 0
if (max_consecutive / samplerate) > max_gap:
drop.append(True)
else:
drop.append(False)
max_consecutive = utils.max_repeated_nans(np.mean(trial, axis=1))
drop.append(max_consecutive / samplerate > max_gap)

Comment thread aopy/analysis/base.py

Args:
trajectories (list of numpy.ndarray): List of N trial trajectories, where each element
is an array of shape (T, D) with T time points and D spatial dimensions.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

we've been using nt and nch to describe time and spatial dimensions.

Comment thread aopy/analysis/base.py
Comment on lines +2567 to +2568
Based on method by Rouse et al., 2022
https://pmc.ncbi.nlm.nih.gov/articles/PMC9014981/

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

i think briefly describing the approach would be helpful as well as citing the paper you based it on

@leoscholl

Copy link
Copy Markdown
Collaborator

left some comments / suggestions! let me know if you have questions

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.

2 participants