44#-------------------------------------------------------------
55# Masking
66
7+
78def find_nested_events (samples , outer , inner ):
89 """ Returns indices of events in outer that contain events in inner
9-
10+
1011 This is helpful for dealing with EyeLink blink events. Each is embedded
1112 within a saccade event, and the EyeLink documentation states that data
1213 within saccades that contain blinks is unreliable. So we use this method
1314 to find those saccade events.
14-
15+
1516 Parameters
1617 ----------
1718 samples (cili Samples)
@@ -30,18 +31,21 @@ def find_nested_events(samples, outer, inner):
3031 post_onsets = onsets + inner .duration
3132 # convert to list of positional indices
3233 max_onset = samples .index [- 1 ]
33- last_idxs = post_onsets .apply (lambda x : max (0 , samples .index .searchsorted (x , side = "right" )- 1 ))
34+ last_idxs = post_onsets .apply (lambda x : max (
35+ 0 , samples .index .searchsorted (x , side = "right" ) - 1 ))
3436 # step back by one positional index to get pos. index of last samples of our events.
3537 # stupid fix - don't nudge the index back for events whose duration went beyond the samples
3638 end_safe_evs = post_onsets <= max_onset
3739 last_idxs [end_safe_evs ] = last_idxs [end_safe_evs ] - 1
3840 # get the time indices of the last samples of our events
3941 last_onsets = last_idxs .apply (lambda x : samples .index [x ])
40- idxs = outer .apply (has_overlapping_events , axis = 1 , args = [onsets , last_onsets ])
42+ idxs = outer .apply (has_overlapping_events , axis = 1 ,
43+ args = [onsets , last_onsets ])
4144 if len (idxs ) == 0 :
4245 return pd .DataFrame ()
4346 return outer [idxs ]
4447
48+
4549def has_overlapping_events (event , onsets , last_onsets ):
4650 """ Searches for onset/last_onset pairs overlapping with the event in 'event.'
4751
@@ -57,9 +61,11 @@ def has_overlapping_events(event, onsets, last_onsets):
5761 last_onsets (numpy array like)
5862 Last indices of the potentially intersecting events.
5963 """
60- matches = last_onsets [(onsets <= event .name + event .duration ) & (last_onsets >= event .name )]
64+ matches = last_onsets [(onsets <= event .name +
65+ event .duration ) & (last_onsets >= event .name )]
6166 return len (matches ) > 0
6267
68+
6369def get_eyelink_mask_events (samples , events , find_recovery = True ):
6470 """ Finds events from EyeLink data that contain untrustworthy data.
6571
@@ -79,11 +85,13 @@ def get_eyelink_mask_events(samples, events, find_recovery=True):
7985 the proper ends for blink events.
8086 """
8187 be = events .EBLINK .duration .to_frame ()
82- be = pd .concat ([be , find_nested_events (samples , events .ESACC .duration .to_frame (), be )])
88+ be = pd .concat ([be , find_nested_events (
89+ samples , events .ESACC .duration .to_frame (), be )])
8390 if find_recovery :
8491 adjust_eyelink_recov_idxs (samples , be )
8592 return be
8693
94+
8795def get_eyelink_mask_idxs (samples , events , find_recovery = True ):
8896 """ Calls get_eyelink_mask_events, finds indices from 'samples' within the returned events.
8997
@@ -93,6 +101,7 @@ def get_eyelink_mask_idxs(samples, events, find_recovery=True):
93101 bi = ev_row_idxs (samples , be )
94102 return bi
95103
104+
96105def mask_eyelink_blinks (samples , events , mask_fields = ["pup_l" ], find_recovery = True ):
97106 """ Sets the value of all untrustworthy data points to NaN.
98107
@@ -118,6 +127,7 @@ def mask_eyelink_blinks(samples, events, mask_fields=["pup_l"], find_recovery=Tr
118127 samps .loc [indices , mask_fields ] = float ('nan' )
119128 return samps
120129
130+
121131def mask_zeros (samples , mask_fields = ["pup_l" ]):
122132 """ Sets any 0 values in columns in mask_fields to NaN
123133
@@ -133,6 +143,7 @@ def mask_zeros(samples, mask_fields=["pup_l"]):
133143 samps [samps [f ] == 0 ] = float ("nan" )
134144 return samps
135145
146+
136147def interp_zeros (samples , interp_fields = ["pup_l" ]):
137148 """ Replace 0s in 'samples' with linearly interpolated data.
138149
@@ -151,6 +162,7 @@ def interp_zeros(samples, interp_fields=["pup_l"]):
151162 samps .fillna (method = "ffill" , inplace = True )
152163 return samps
153164
165+
154166def interp_eyelink_blinks (samples , events , find_recovery = True , interp_fields = ["pup_l" ]):
155167 """ Replaces the value of all untrustworthy data points linearly interpolated data.
156168
@@ -171,12 +183,14 @@ def interp_eyelink_blinks(samples, events, find_recovery=True, interp_fields=["p
171183 interp_fields (list of strings)
172184 The columns in which we should interpolate data.
173185 """
174- samps = mask_eyelink_blinks (samples , events , mask_fields = interp_fields , find_recovery = find_recovery )
186+ samps = mask_eyelink_blinks (
187+ samples , events , mask_fields = interp_fields , find_recovery = find_recovery )
175188 # inplace=True causes a crash, so for now...
176189 # fixed by #6284 ; will be in 0.14 release of pandas
177190 samps = samps .interpolate (method = "linear" , axis = 0 , inplace = False )
178191 return samps
179192
193+
180194def ev_row_idxs (samples , events ):
181195 """ Returns the indices in 'samples' contained in events from 'events.'
182196
@@ -190,11 +204,12 @@ def ev_row_idxs(samples, events):
190204 import numpy as np
191205 idxs = []
192206 for idx , dur in events .duration .items ():
193- idxs .extend (list (range (idx , int (idx + dur ))))
207+ idxs .extend (list (range (idx , int (idx + dur ))))
194208 idxs = np .unique (idxs )
195209 idxs = np .intersect1d (idxs , samples .index .tolist ())
196210 return idxs
197211
212+
198213def adjust_eyelink_recov_idxs (samples , events , z_thresh = .1 , window = 1000 , kernel_size = 100 ):
199214 """ Extends event endpoint until the z-scored derivative of 'field's timecourse drops below thresh
200215
@@ -225,35 +240,37 @@ def adjust_eyelink_recov_idxs(samples, events, z_thresh=.1, window=1000, kernel_
225240 # find a pupil size field to use
226241 p_fields = [f for f in samples .columns if f in PUP_FIELDS ]
227242 if len (p_fields ) == 0 :
228- return # if we can't find a pupil field, we won't make any adjustments
243+ return # if we can't find a pupil field, we won't make any adjustments
229244 field = p_fields [0 ]
230245 # use pandas to take rolling mean. pandas' kernel looks backwards, so we need to pull a reverse...
231246 dfs = np .gradient (samples [field ].values )
232247 reversed_dfs = dfs [::- 1 ]
233- reversed_dfs_ravg = np .array (pd .rolling_mean (pd .Series (reversed_dfs ),window = kernel_size , min_periods = 1 ))
248+ reversed_dfs_ravg = np .array (pd .rolling_mean (
249+ pd .Series (reversed_dfs ), window = kernel_size , min_periods = 1 ))
234250 dfs_ravg = reversed_dfs_ravg [::- 1 ]
235- dfs_ravg = np .abs ((dfs_ravg - np .mean (dfs_ravg ))/ np .std (dfs_ravg ))
251+ dfs_ravg = np .abs ((dfs_ravg - np .mean (dfs_ravg )) / np .std (dfs_ravg ))
236252 samp_count = len (samples )
237253 # search for drop beneath z_thresh after end index
238254 new_durs = []
239255 for idx , dur in events .duration .items ():
240256 try :
241- s_pos = samples .index .get_loc (idx + dur ) - 1
242- e_pos = samples .index [min (s_pos + window , samp_count - 1 )]
257+ s_pos = samples .index .get_loc (idx + dur ) - 1
258+ e_pos = samples .index [min (s_pos + window , samp_count - 1 )]
243259 except Exception as e :
244260 # can't do much about that
245261 s_pos = e_pos = 0
246262 if s_pos == e_pos :
247263 new_durs .append (dur )
248264 continue
249- e_dpos = np .argmax (dfs_ravg [s_pos :e_pos ] < z_thresh ) # 0 if not found
250- new_end = samples .index [min (s_pos + e_dpos , samp_count - 1 )]
265+ e_dpos = np .argmax (dfs_ravg [s_pos :e_pos ] < z_thresh ) # 0 if not found
266+ new_end = samples .index [min (s_pos + e_dpos , samp_count - 1 )]
251267 new_durs .append (new_end - idx )
252268 events .duration = new_durs
253269
254270#-------------------------------------------------------------
255271# Filters
256272
273+
257274def butterworth_series (samples , fields = ["pup_l" ], filt_order = 5 , cutoff_freq = .01 , inplace = False ):
258275 """ Applies a butterworth filter to the given fields
259276
@@ -267,6 +284,6 @@ def butterworth_series(samples, fields=["pup_l"], filt_order=5, cutoff_freq=.01,
267284 from numpy import array
268285 samps = samples if inplace else samples .copy (deep = True )
269286 B , A = signal .butter (filt_order , cutoff_freq , output = "BA" )
270- samps [fields ] = samps [fields ].apply (lambda x : signal .filtfilt (B ,A ,x ), axis = 0 )
287+ samps [fields ] = samps [fields ].apply (
288+ lambda x : signal .filtfilt (B , A , x ), axis = 0 )
271289 return samps
272-
0 commit comments