Skip to content

Commit 40b3e31

Browse files
authored
Merge pull request #1421 from PeterNSteinmetz/speedUpSectionScan
Put back numpy approach for gap detections
2 parents e803ef4 + 810968d commit 40b3e31

File tree

2 files changed

+49
-48
lines changed

2 files changed

+49
-48
lines changed

neo/rawio/neuralynxrawio/ncssections.py

Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,13 @@ class NcsSection:
5151

5252
_RECORD_SIZE = 512 # nb sample per signal record
5353

54-
def __init__(self, sb, st, eb, et, ns):
55-
self.startRec = sb # index of starting record
56-
self.startTime = st # starttime of first record
57-
self.endRec = eb # index of last record (inclusive)
58-
self.endTime = et # end time of last record, that is, the end time of the last
54+
def __init__(self, startRec, startTime, endRec, endTime, n_samples):
55+
self.startRec = startRec # index of starting record
56+
self.startTime = startTime # starttime of first record
57+
self.endRec = endRec # index of last record (inclusive)
58+
self.endTime = endTime # end time of last record, that is, the end time of the last
5959
# sampling period contained in the last record of the section
60-
self.n_samples = ns # number of samples in record which are valid
60+
self.n_samples = n_samples # number of samples in record which are valid
6161

6262
def __eq__(self, other):
6363
return (
@@ -159,33 +159,26 @@ def _parseGivenActualFrequency(ncsMemMap, ncsSects, chanNum, reqFreq, blkOnePred
159159
-------
160160
NcsSections object with block locations marked
161161
"""
162-
startBlockPredTime = blkOnePredTime
163-
blk_len = 0
164-
curBlock = ncsSects.sects[0]
165-
for recn in range(1, ncsMemMap.shape[0]):
166-
timestamp = ncsMemMap["timestamp"][recn]
167-
channel_id = ncsMemMap["channel_id"][recn]
168-
sample_rate = ncsMemMap["sample_rate"][recn]
169-
nb_valid = ncsMemMap["nb_valid"][recn]
170-
171-
if channel_id != chanNum or sample_rate != reqFreq:
172-
raise IOError("Channel number or sampling frequency changed in " + "records within file")
173-
predTime = NcsSectionsFactory.calc_sample_time(ncsSects.sampFreqUsed, startBlockPredTime, blk_len)
174-
nValidSamps = nb_valid
175-
if timestamp != predTime:
176-
curBlock.endRec = recn - 1
177-
curBlock.endTime = predTime
178-
curBlock.n_samples = blk_len
179-
curBlock = NcsSection(recn, timestamp, -1, -1, -1)
180-
ncsSects.sects.append(curBlock)
181-
startBlockPredTime = NcsSectionsFactory.calc_sample_time(ncsSects.sampFreqUsed, timestamp, nValidSamps)
182-
blk_len = 0
183-
else:
184-
blk_len += nValidSamps
185-
186-
curBlock.endRec = ncsMemMap.shape[0] - 1
187-
endTime = NcsSectionsFactory.calc_sample_time(ncsSects.sampFreqUsed, startBlockPredTime, blk_len)
188-
curBlock.endTime = endTime
162+
163+
# New code numpy vector based (speedup X50)
164+
delta = (ncsMemMap["timestamp"][1:] - ncsMemMap["timestamp"][:-1]).astype(np.int64)
165+
delta_prediction = ((ncsMemMap["nb_valid"][:-1] / ncsSects.sampFreqUsed) * 1e6).astype(np.int64)
166+
gap_inds = np.flatnonzero((delta - delta_prediction) != 0)
167+
gap_inds += 1
168+
sections_limits = [ 0 ] + gap_inds.tolist() + [len(ncsMemMap)]
169+
170+
for i in range(len(gap_inds) + 1):
171+
start = sections_limits[i]
172+
stop = sections_limits[i + 1]
173+
ncsSects.sects.append(
174+
NcsSection(
175+
startRec=start,
176+
startTime=ncsMemMap["timestamp"][start],
177+
endRec=stop-1,
178+
endTime=ncsMemMap["timestamp"][stop-1] + np.uint64(ncsMemMap["nb_valid"][stop-1] / ncsSects.sampFreqUsed * 1e6),
179+
n_samples=np.sum(ncsMemMap["nb_valid"][start:stop])
180+
)
181+
)
189182

190183
return ncsSects
191184

@@ -219,9 +212,9 @@ def _buildGivenActualFrequency(ncsMemMap, actualSampFreq, reqFreq):
219212
raise IOError("Sampling frequency in first record doesn't agree with header.")
220213
chanNum = ncsMemMap["channel_id"][0]
221214

222-
secs = NcsSections()
223-
secs.sampFreqUsed = actualSampFreq
224-
secs.microsPerSampUsed = NcsSectionsFactory.get_micros_per_samp_for_freq(actualSampFreq)
215+
ncsSects = NcsSections()
216+
ncsSects.sampFreqUsed = actualSampFreq
217+
ncsSects.microsPerSampUsed = NcsSectionsFactory.get_micros_per_samp_for_freq(actualSampFreq)
225218

226219
# check if file is one block of records, which is often the case, and avoid full parse
227220
lastBlkI = ncsMemMap.shape[0] - 1
@@ -241,15 +234,17 @@ def _buildGivenActualFrequency(ncsMemMap, actualSampFreq, reqFreq):
241234
n_samples = NcsSection._RECORD_SIZE * lastBlkI
242235
curBlock = NcsSection(0, ts0, lastBlkI, lastBlkEndTime, n_samples)
243236

244-
secs.sects.append(curBlock)
245-
return secs
237+
ncsSects.sects.append(curBlock)
238+
return ncsSects
246239

247-
# otherwise need to scan looking for breaks
240+
248241
else:
242+
# otherwise need to scan looking for data gaps
249243
blkOnePredTime = NcsSectionsFactory.calc_sample_time(actualSampFreq, ts0, nb0)
250-
curBlock = NcsSection(0, ts0, -1, -1, -1)
251-
secs.sects.append(curBlock)
252-
return NcsSectionsFactory._parseGivenActualFrequency(ncsMemMap, secs, chanNum, reqFreq, blkOnePredTime)
244+
# curBlock = NcsSection(0, ts0, -1, -1, -1)
245+
# ncsSects.sects.append(curBlock)
246+
ncsSects = NcsSectionsFactory._parseGivenActualFrequency(ncsMemMap, ncsSects, chanNum, reqFreq, blkOnePredTime)
247+
return ncsSects
253248

254249
@staticmethod
255250
def _parseForMaxGap(ncsMemMap, ncsSects, maxGapLen):

neo/rawio/neuralynxrawio/nlxheader.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,11 @@ def readTimeDate(self, txt_header):
291291
self["recording_opened"] = datetime.datetime.strptime(
292292
dt1["date"] + " " + dt1["time"], hpd["datetimeformat"])
293293
except:
294-
self["recording_opened"] = datetime.datetime.strptime(
295-
dt1["date"] + " " + dt1["time"], hpd["datetime2format"])
294+
try:
295+
self["recording_opened"] = datetime.datetime.strptime(
296+
dt1["date"] + " " + dt1["time"], hpd["datetime2format"])
297+
except:
298+
self["recording_opened"] = None
296299

297300

298301
# close time, if available
@@ -304,10 +307,13 @@ def readTimeDate(self, txt_header):
304307
+ f"version {av}. Please contact developers."
305308
)
306309
else:
307-
dt2 = sr.groupdict()
308-
self["recording_closed"] = datetime.datetime.strptime(
309-
dt2["date"] + " " + dt2["time"], hpd["datetimeformat"]
310-
)
310+
try:
311+
dt2 = sr.groupdict()
312+
self["recording_closed"] = datetime.datetime.strptime(
313+
dt2["date"] + " " + dt2["time"], hpd["datetimeformat"]
314+
)
315+
except:
316+
self["recording_closed"] = None
311317

312318
def type_of_recording(self):
313319
"""

0 commit comments

Comments
 (0)