Skip to content

Commit 6e64232

Browse files
committed
RF: PEP8
1 parent 75c5133 commit 6e64232

File tree

8 files changed

+307
-167
lines changed

8 files changed

+307
-167
lines changed

cili/cleanup.py

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
#-------------------------------------------------------------
55
# Masking
66

7+
78
def 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+
4549
def 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+
6369
def 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+
8795
def 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+
96105
def 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+
121131
def 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+
136147
def 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+
154166
def 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+
180194
def 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+
198213
def 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+
257274
def 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-

cili/extract.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
TIME_UNITS = 'time'
77
SAMP_UNITS = 'samples'
88

9+
910
def extract_event_ranges(samples, events_dataframe, start_offset=0,
1011
end_offset=0, round_indices=True, borrow_attributes=[]):
1112
""" Extracts ranges from samples based on event timing.
@@ -52,7 +53,8 @@ def extract_event_ranges(samples, events_dataframe, start_offset=0,
5253
r_times.columns = ['last_onset']
5354
# sanity check - make sure no events start before the data, or end afterwards
5455
if any(r_times.index < samples.index[0]):
55-
raise ValueError("at least one event range starts before the first sample")
56+
raise ValueError(
57+
"at least one event range starts before the first sample")
5658
if any(r_times.index > samples.index[-1]):
5759
raise ValueError("at least one event range ends after the last sample")
5860

@@ -65,14 +67,14 @@ def extract_event_ranges(samples, events_dataframe, start_offset=0,
6567
# we're going to make a df with a hierarchical index.
6668
samples['orig_idx'] = samples.index
6769
midx = pd.MultiIndex.from_product([list(range(len(e_starts))), list(range(r_len))],
68-
names=['event', 'onset'])
70+
names=['event', 'onset'])
6971
# get all of the samples!
7072
# idxs = []
7173
df = pd.DataFrame()
7274
idx = 0
7375
for stime, etime in r_times.itertuples():
7476
# get the start time... add the number of indices that you want...
75-
s_idx = np.where(samples.index > stime)[0][0]-1
77+
s_idx = np.where(samples.index > stime)[0][0] - 1
7678
e_idx = s_idx + r_len - 1
7779
stime = samples.index[s_idx]
7880
etime = samples.index[e_idx]
@@ -85,6 +87,7 @@ def extract_event_ranges(samples, events_dataframe, start_offset=0,
8587
df.index = midx
8688
return df
8789

90+
8891
def extract_events(samples, events, offset=0, duration=0,
8992
units='samples', borrow_attributes=[]):
9093
""" Extracts ranges from samples based on event timing and sample count.
@@ -136,39 +139,45 @@ def extract_events(samples, events, offset=0, duration=0,
136139
if units == TIME_UNITS:
137140
# get the indices for the first event (minus the first index), then use
138141
# the length of the first event as a template for all events
139-
r_times = e_starts+offset
142+
r_times = e_starts + offset
140143
ev_idxs = np.logical_and(samples.index <= r_times.iloc[0] + duration,
141144
samples.index > r_times.iloc[0])
142145
r_dur = len(np.where(ev_idxs)[0]) + 1
143-
r_idxs = [np.where(samples.index > rt)[0][0]-1 for rt in r_times]
146+
r_idxs = [np.where(samples.index > rt)[0][0] - 1 for rt in r_times]
144147
# sanity check - make sure no events start before the data, or end afterwards
145148
if any(r_times < samples.index[0]):
146-
raise ValueError("at least one event range starts before the first sample")
149+
raise ValueError(
150+
"at least one event range starts before the first sample")
147151
if any(r_times > samples.index[-1]):
148-
raise ValueError("at least one event range ends after the last sample")
152+
raise ValueError(
153+
"at least one event range ends after the last sample")
149154
elif units == SAMP_UNITS:
150155
# just find the indexes of the event starts, and offset by sample count
151-
r_idxs = np.array([np.where(samples.index > et)[0][0]-1+offset for et in e_starts])
156+
r_idxs = np.array([np.where(samples.index > et)[0]
157+
[0] - 1 + offset for et in e_starts])
152158
r_dur = duration
153159
if any(r_idxs < 0):
154-
raise ValueError("at least one event range starts before the first sample")
160+
raise ValueError(
161+
"at least one event range starts before the first sample")
155162
if any(r_idxs >= len(samples)):
156-
raise ValueError("at least one event range ends after the last sample")
163+
raise ValueError(
164+
"at least one event range ends after the last sample")
157165
else:
158166
raise ValueError("Not a valid unit!")
159167

160168
# make a hierarchical index
161169
samples['orig_idx'] = samples.index
162170
midx = pd.MultiIndex.from_product([list(range(len(e_starts))), list(range(r_dur))],
163-
names=['event', 'onset'])
171+
names=['event', 'onset'])
164172
# get the samples
165173
df = pd.DataFrame()
166174
idx = 0
167175
for s_idx in r_idxs:
168176
# get the start time... add the number of indices that you want...
169-
e_idx = s_idx + r_dur-1 # pandas.loc indexing is inclusive
177+
e_idx = s_idx + r_dur - 1 # pandas.loc indexing is inclusive
170178
# this deepcopy is heavy handed... but gets around some early pandas bugs
171-
new_df = deepcopy(samples.loc[samples.index[s_idx] : samples.index[e_idx]])
179+
new_df = deepcopy(
180+
samples.loc[samples.index[s_idx]: samples.index[e_idx]])
172181
for ba in borrow_attributes:
173182
new_df[ba] = events.iloc[idx].get(ba, float('nan'))
174183
df = pd.concat([df, new_df])

cili/models.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
import pandas.io.pytables as pt
33
from pandas.compat import u_safe as u, string_types, isidentifier
44

5+
56
class SaveMixin(object):
67
""" Bakes in some save settings for NDFrame subclasses
7-
8+
89
You can still use the pandas methods, but for quick saving and loading
910
this mixin provides some setting you might want to reuse.
1011
"""
12+
1113
def __init__(self, *args, **kwargs):
1214
super(SaveMixin, self).__init__(*args, **kwargs)
1315

@@ -28,14 +30,17 @@ def load_saved(cls, save_path):
2830
def from_pd_obj(cls, pd_obj):
2931
return cls(pd_obj._data.copy()).__finalize__(pd_obj)
3032

33+
3134
class Samples(SaveMixin, pd.DataFrame):
3235
"""Pandas DataFrame subclas for representing eye tracking timeseries data.
3336
3437
Indexes may be hierarchical.
3538
"""
39+
3640
def __init__(self, *args, **kwargs):
3741
super(Samples, self).__init__(*args, **kwargs)
38-
42+
43+
3944
class Events(object):
4045
"""Pandas Panel-like object that gives you access to DataFrames via standard accessors.
4146
@@ -48,6 +53,7 @@ class Events(object):
4853
4954
Right now, the best way way to make one of these is to use Events.from_dict().
5055
"""
56+
5157
def __init__(self, *args, **kwargs):
5258
super(Events, self).__init__(*args, **kwargs)
5359
self.dframes = {}
@@ -62,14 +68,14 @@ def save(self, save_path):
6268
def load_saved(cls, save_path):
6369
obj = cls()
6470
s = pt.HDFStore(save_path)
65-
obj.dframes = dict([(k[1:],s[k]) for k in list(s.keys())])
71+
obj.dframes = dict([(k[1:], s[k]) for k in list(s.keys())])
6672
s.close()
6773
return obj
6874

6975
@classmethod
7076
def from_dict(cls, the_d):
7177
""" Returns an Events instance containing the given DataFrames
72-
78+
7379
Parameters
7480
----------
7581
the_d (dict)
@@ -115,7 +121,7 @@ def from_list_of_dicts(cls, events_list):
115121
def _local_dir(self):
116122
""" add the string-like attributes from the info_axis """
117123
return [c for c in list(self.dframes.keys())
118-
if isinstance(c, string_types) and isidentifier(c)]
124+
if isinstance(c, string_types) and isidentifier(c)]
119125

120126
def __dir__(self):
121127
"""
@@ -137,6 +143,7 @@ def __getattr__(self, name):
137143
raise AttributeError("'%s' object has no attribute '%s'" %
138144
(type(self).__name__, name))
139145

146+
140147
def initialize_hdf5():
141-
pt._TYPE_MAP.update({Events:u('wide'), Samples:u('frame'),})
142-
pt._AXES_MAP.update({Events:[1, 2], Samples:[0],})
148+
pt._TYPE_MAP.update({Events: u('wide'), Samples: u('frame'), })
149+
pt._AXES_MAP.update({Events: [1, 2], Samples: [0], })

0 commit comments

Comments
 (0)