localFunctions.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. '''
  2. Local functions for convert2nwb
  3. '''
  4. import numpy as np
  5. import h5py
  6. from scipy import sparse
  7. from pynwb.core import VectorData
  8. def electrodeLocations(areas, endCh, nCh):
  9. '''
  10. electrodeLocationVec = electrodeLocations(areas, endCh, nCh)
  11. Function assigns brain area to a channel
  12. '''
  13. electrodeLocationVec = []
  14. for iCh in range(nCh):
  15. areaInd = np.nonzero(endCh >= iCh)[0].min()
  16. electrodeLocationVec.append(areas[areaInd])
  17. return electrodeLocationVec
  18. def createElectrodeTable(nwb, input):
  19. '''
  20. (tbl, columnLabels, columnDescription) = createElectrodeTable(nwb, input)
  21. Function creates an electrode table with the following columns:
  22. channel_id: A unnique probe channel ID formed by combining session ID,
  23. probe reference number, and channel number relative to the
  24. tip of the probe.
  25. channel_local_index: Channel index relative to the tip of the probe.
  26. Channel indices are only unique within a probe.
  27. x: Channel AP brain surface coordinate (probe inisertion location mm).
  28. y: Channel ML brain surface coordinate (probe inisertion location mm).
  29. z: Channel location relative to the tip of the probe in mm.
  30. imp: Channel impedance.
  31. location: Channel brain area location.
  32. filtering: Type of LFP filtering applied.
  33. group: Channel electrode group (e.g., shank 1). NWB documentation on
  34. ElectrodeGroup datatype is provided in the following links:
  35. https://nwb-schema.readthedocs.io/en/latest/format.html#electrodegroup
  36. https://nwb-schema.readthedocs.io/en/latest/format.html#sec-electrodegroup-src
  37. channel_label
  38. probe_label.
  39. The rows of the table correspond to individual recording channels.
  40. Input: nwb - a nwb object.
  41. input - a dictionary with the following keys:
  42. iElectrode: electrode reference number.
  43. electrodeDescription: a cell array (n probes) with probe
  44. desciptions.
  45. electrodeManufacturer: a cell array of electrode manufacturers.
  46. nShanks: a cell array of number of shanks.
  47. nChannelsPerShank: a cell array of electrode number of
  48. recording channels per shank.
  49. electrodeLocation: a cell array (n channels) of channel brain
  50. area locations.
  51. electrodeCoordinates: a cell array (n probes) with recording
  52. channel coordinate arrays (n channels by
  53. 3).
  54. sessionID: a string with the session ID.
  55. electrodeLabel: a cell array (n probes) with probe labels.
  56. Output: tbl - a numpy array object with rows and columns as described
  57. above.
  58. columnLabels - a list of column labels of the output table.
  59. columnDescription - a list of descriptions of data in the columns.
  60. '''
  61. # Parse input
  62. iEl = input["iElectrode"]
  63. nSh = input["nShanks"]
  64. nCh = input["nChannelsPerShank"]
  65. # Create a table with given column labels
  66. columnLabels = ['channel_id', 'channel_local_index', 'x', 'y', 'z', 'imp', 'location', 'filtering', 'group', 'channel_label', 'probe_label']
  67. columnDescription = [
  68. 'A unnique probe channel ID formed by combining session ID, probe reference number, and channel number relative to the tip of the probe.',
  69. 'Channel index relative to the tip of the probe. Channel indices are only unique within a probe.',
  70. 'Channel AP brain surface coordinate (probe inisertion location mm).',
  71. 'Channel ML brain surface coordinate (probe inisertion location mm).',
  72. 'Channel location relative to the tip of the probe in mm.',
  73. 'Channel impedance.',
  74. 'Channel brain area location.',
  75. 'Type of LFP filtering applied.',
  76. 'Channel electrode group (e.g., shank 1).',
  77. 'Channel_label.',
  78. 'Probe_label.'
  79. ]
  80. tbl = np.empty((0, len(columnLabels)))
  81. # Register the probe device
  82. device = nwb.create_device(
  83. name = 'probe' + str(iEl+1),
  84. description = input["electrodeDescription"][iEl],
  85. manufacturer = input["electrodeManufacturer"][iEl])
  86. for iShank in range(nSh[iEl]):
  87. # Register a shank electrode group (only one because this is a single shank probe)
  88. electrode_group = nwb.create_electrode_group(
  89. name = 'probe' + str(iEl+1),
  90. description = 'electrode group for probe' + str(iEl+1),
  91. location = input["electrodeLocation"][iEl][-1],
  92. device = device,
  93. position = input["electrodeCoordinates"][iEl][-1])
  94. # Populate the electrode table
  95. for iCh in range(nCh[iEl]):
  96. if iCh < 10-1:
  97. channelID = str(input["sessionID"] + str(iEl+1) + '00' + str(iCh+1))
  98. elif iCh < 99-1:
  99. channelID = str(input["sessionID"] + str(iEl+1) + '0' + str(iCh+1))
  100. else:
  101. channelID = str(input["sessionID"] + str(iEl+1) + str(iCh+1))
  102. channel_label = 'probe' + str(iEl+1) + 'shank' + str(iShank+1) + 'elec' + str(iCh+1)
  103. tbl = np.append(tbl, np.matrix([
  104. channelID, iCh+1, input["electrodeCoordinates"][iEl][iCh][0], input["electrodeCoordinates"][iEl][iCh][1], input["electrodeCoordinates"][iEl][iCh][2],
  105. np.nan, input["electrodeLocation"][iEl][iCh], 'unknown', electrode_group, channel_label, input["electrodeLabel"][iEl]]), axis=0)
  106. return np.array(tbl), columnLabels, columnDescription
  107. def array2list(tbl, columnLabels, columnDescription):
  108. # Function definition
  109. columnList = []
  110. if tbl.any():
  111. tblColumns=tbl.transpose().tolist()
  112. for iCol in range(len(columnLabels)):
  113. columnList.append(VectorData(
  114. name=columnLabels[iCol],
  115. data=tblColumns[iCol],
  116. description=columnDescription[iCol]
  117. ))
  118. return columnList
  119. def concatenateMat(mat1, mat2, method='vertical'):
  120. '''
  121. concatenatedMat = concatenateMat(mat1, mat2, method)
  122. Function concatenates two 2-D numpy arrays by appending the
  123. smaller matrix with trailing zeros or NaNs.
  124. Input: mat1.
  125. mat2.
  126. method - concatenate either vertically ('vertical') or
  127. horizontally ('horizontal'). This input is optional and
  128. the default method is vertical. In case when trailing
  129. NaNs are needed rather than zeros, corresponding methods
  130. are 'verticalnan' and 'horizontalnan', respectively.
  131. Output: concatenatedMat - a concatenated numpy array.
  132. '''
  133. if mat1.any() and mat2.any():
  134. if method == 'vertical':
  135. diff = mat1.shape[1] - mat2.shape[1]
  136. if diff > 0:
  137. trailingMat = np.zeros([mat2.shape[0], abs(diff)])
  138. mat2 = np.append(mat2, trailingMat, axis=1)
  139. elif diff < 0:
  140. trailingMat = np.zeros([mat1.shape[0], abs(diff)])
  141. mat1 = np.append(mat1, trailingMat, axis=1)
  142. concatenatedMat = np.append(mat1, mat2, axis=0)
  143. elif method == 'horizontal':
  144. diff = mat1.shape[0] - mat2.shape[0]
  145. if diff > 0:
  146. trailingMat = np.zeros([abs(diff), mat2.shape[1]])
  147. mat2 = np.append(mat2, trailingMat, axis=0)
  148. elif diff < 0:
  149. trailingMat = np.zeros([abs(diff), mat1.shape[1]])
  150. mat1 = np.append(mat1, trailingMat, axis=0)
  151. concatenatedMat = np.append(mat1, mat2, axis=1)
  152. elif method == 'verticalnan':
  153. diff = mat1.shape[1] - mat2.shape[1]
  154. if diff > 0:
  155. trailingMat = np.nan([mat2.shape[0], abs(diff)])
  156. mat2 = np.append(mat2, trailingMat, axis=1)
  157. elif diff < 0:
  158. trailingMat = np.nan([mat1.shape[0], abs(diff)])
  159. mat1 = np.append(mat1, trailingMat, axis=1)
  160. concatenatedMat = np.append(mat1, mat2, axis=0)
  161. elif method == 'horizontalnan':
  162. diff = mat1.shape[0] - mat2.shape[0]
  163. if diff > 0:
  164. trailingMat = np.nan([abs(diff), mat2.shape[1]])
  165. mat2 = np.append(mat2, trailingMat, axis=0)
  166. elif diff < 0:
  167. trailingMat = np.nan([abs(diff), mat1.shape[1]])
  168. mat1 = np.append(mat1, trailingMat, axis=0)
  169. concatenatedMat = np.append(mat1, mat2, axis=1)
  170. elif mat1.any() and not mat2.any():
  171. concatenatedMat = mat1
  172. elif not mat1.any() and mat2.any():
  173. concatenatedMat = mat2
  174. else:
  175. concatenatedMat = np.array()
  176. return concatenatedMat
  177. def getSpikes(animalDerivedDataFile, animalID, sessionID, electrodeTbl):
  178. '''
  179. (spikes, metadataTbl, derivedData, columnLabels, columnDescription) = getSpikes(animalDerivedDataFile, animalID, sessionID, electrodeTbl)
  180. Function loads Neuronexus spiking data from a MAT file with a custom data
  181. structure. Input:
  182. animalDerivedDataFile - a string with derived data file name or already
  183. loaded data.
  184. animalID - an animal ID string.
  185. sessionID - a session of interest ID string.
  186. electrodeTbl - a numpy array with electrode information generated by
  187. the function createElectrodeTable.
  188. Output: spikes - a 1-by-n list of numpy arrays (n units) with unit spike
  189. times in seconds.
  190. metadataTbl - a numpy array with rows corresponding to
  191. individual clusters (units) and columns to:
  192. cluster_id: a unique cluster ID formed by combining session
  193. ID, probe reference number, and unit cluster ID.
  194. local_cluster_id: a unit cluster ID. This is only unique
  195. within the probe.
  196. type - activity type: single unit (unit) or multi-unit (mua).
  197. peak_channel_index: recording channel with the highest unit peak
  198. index relative to the tip of the probe.
  199. peak_channel_id: a corresponding unnique probe channel ID formed by
  200. combining session ID, probe reference number, and
  201. channel number relative to the tip of the probe.
  202. local_peak_channel_id: a corresponding channel index relative to the
  203. tip of the probe. Channel indices are only
  204. unique within a probe.
  205. rel_horz_position: relative horizontal position in um.
  206. rel_vert_position: probe tip-relative vertical position in um.
  207. isi_violations: interspike interval violations, a cluster
  208. quality measure.
  209. isolation_distance: cluster isolation distance, a cluster
  210. quality measure.
  211. area: unit brain area location.
  212. probe_id: probe label.
  213. electrode_group: channel electrode group (e.g., shank 1). NWB
  214. documentation on ElectrodeGroup datatype is
  215. provided in the following links:
  216. https://nwb-schema.readthedocs.io/en/latest/format.html#electrodegroup
  217. https://nwb-schema.readthedocs.io/en/latest/format.html#sec-electrodegroup-src
  218. derivedData - animal data loaded from the MAT derived data file.
  219. columnLabels - a list of column labels of the output table.
  220. columnDescription - a list of descriptions of data in the columns.
  221. '''
  222. # Column labels of the metadata table
  223. columnLabels = ['cluster_id', 'local_cluster_id', 'type', 'peak_channel_index', 'peak_channel_id', 'local_peak_channel_id',
  224. 'rel_horz_pos', 'rel_vert_pos', 'isi_violations', 'isolation_distance', 'area', 'probe_id', 'electrode_group']
  225. columnDescription = [
  226. 'A unique cluster ID formed by combining session ID, probe reference number, and unit cluster ID.',
  227. 'A unit cluster ID. This is only unique within the probe.',
  228. 'Single unit (unit) or multi-unit (mua).',
  229. 'A Recording channel with the highest unit peak index relative to the tip of the probe.',
  230. 'A corresponding unnique probe channel ID formed by combining session ID, probe reference number, and channel number relative to the tip of the probe.',
  231. 'A corresponding channel index relative to the tip of the probe. Channel indices are only unique within a probe.',
  232. 'Relative horizontal position in um.',
  233. 'Probe tip-relative vertical position in um.',
  234. 'Interspike interval violations, a cluster quality measure.',
  235. 'Cluster isolation distance, a cluster quality measure.',
  236. 'Unit brain area location.',
  237. 'Probe label.',
  238. 'Channel electrode group (e.g., shank 1).'
  239. ]
  240. # Data series names with different brain areas
  241. if isinstance(animalDerivedDataFile, str):
  242. derivedData = h5py.File(animalDerivedDataFile,'r')
  243. else:
  244. derivedData = animalDerivedDataFile
  245. dataSeriesNames = []
  246. for iSeries in range(11):
  247. dataSeriesNames.append(animalID + '_s' + sessionID + str(iSeries+1))
  248. dataSeriesNames.append(animalID + '_s' + sessionID)
  249. # Load data
  250. spikes = []; metadataTbl = np.array([])
  251. for iSeries in range(len(dataSeriesNames)):
  252. metadata = np.array([])
  253. seriesDerivedData = np.array(derivedData.get('dataStruct/seriesData/' + dataSeriesNames[iSeries]))
  254. if len(seriesDerivedData.shape):
  255. srData = np.array(derivedData.get('dataStruct/seriesData/' + dataSeriesNames[iSeries] + '/conf/samplingParams/srData'))
  256. # Series spike array
  257. spikesSeries_data = np.array(derivedData.get('dataStruct/seriesData/' + dataSeriesNames[iSeries] + '/popData/spkDB/data'))
  258. if not (spikesSeries_data == None).all():
  259. spikesSeries_ir = np.array(derivedData.get('dataStruct/seriesData/' + dataSeriesNames[iSeries] + '/popData/spkDB/ir'))
  260. spikesSeries_jc = np.array(derivedData.get('dataStruct/seriesData/' + dataSeriesNames[iSeries] + '/popData/spkDB/jc'))
  261. spikesSeries = sparse.csc_matrix((spikesSeries_data, spikesSeries_ir, spikesSeries_jc)).toarray()
  262. else:
  263. spikesSeries = np.array([])
  264. nRows = spikesSeries.shape[0]
  265. if nRows:
  266. # Unit metadata: [local_unit_id type local_probe_channel horizontal_position vertical_position ...
  267. # isi_violations isolation_distance anterior_posterior_ccf_coordinate ...
  268. # dorsal_ventral_ccf_coordinate left_right_ccf_coordinate]
  269. metadata = concatenateMat(metadata, np.transpose(np.array(derivedData.get('dataStruct/seriesData/' + dataSeriesNames[iSeries] + '/popData/muaMetadata'))))
  270. # Spike times
  271. if nRows != len(metadata): # means that h5py did not load the sparse matrix correctly. There are redundant rows
  272. spikesSeries = spikesSeries[:metadata.shape[0],:]
  273. nRows = spikesSeries.shape[0]
  274. nColumns = spikesSeries.shape[1]
  275. timeVector = np.linspace(1,nColumns,nColumns)/srData
  276. for iUnit in range(nRows):
  277. spikes.append(timeVector[0][spikesSeries[iUnit].astype(np.bool)])
  278. # Unit metadata: [metadata area]
  279. if iSeries+1 == 1:
  280. areas = ['S1'] * nRows
  281. elif iSeries+1 == 2:
  282. areas = ['VB'] * nRows
  283. elif iSeries+1 == 3:
  284. areas = ['Po'] * nRows
  285. elif iSeries+1 == 4:
  286. areas = ['LP'] * nRows
  287. elif iSeries+1 == 5:
  288. areas = ['DG'] * nRows
  289. elif iSeries+1 == 6:
  290. areas = ['CA1'] * nRows
  291. elif iSeries+1 == 7:
  292. areas = ['RSC'] * nRows
  293. elif iSeries+1 == 8:
  294. areas = ['VB'] * nRows
  295. elif iSeries+1 == 9:
  296. areas = ['LP'] * nRows
  297. elif iSeries+1 == 10:
  298. areas = ['LGN'] * nRows
  299. elif iSeries+1 == 11:
  300. areas = ['CA3'] * nRows
  301. elif iSeries+1 == 12:
  302. areas = ['VB'] * nRows
  303. metadata = concatenateMat(metadata.astype(object), np.matrix(areas).transpose().astype(object), 'horizontal')
  304. # Unit metadata: correct unit type
  305. units = np.array(derivedData.get('dataStruct/seriesData/' + dataSeriesNames[iSeries] + '/shankData/shank1/units'))
  306. muas = np.array(derivedData.get('dataStruct/seriesData/' + dataSeriesNames[iSeries] + '/popData/spkDB_units'))
  307. unitInds = np.isin(muas[0], units[0])
  308. unitTypes = np.array(['mua'] * len(muas[0]))
  309. unitTypes[np.array(unitInds)] = 'unit'
  310. metadata = metadata.astype(object)
  311. metadata[:,1] = np.matrix(unitTypes).transpose()
  312. # Unit metadata: [metadata probe_id]
  313. if iSeries+1 == 1:
  314. probeLabel = ['probe1'] * nRows
  315. elif iSeries+1 == 2:
  316. probeLabel = ['probe2'] * nRows
  317. elif iSeries+1 == 3:
  318. probeLabel = ['probe2'] * nRows
  319. elif iSeries+1 == 4:
  320. probeLabel = ['probe2'] * nRows
  321. elif iSeries+1 == 5:
  322. probeLabel = ['probe2'] * nRows
  323. elif iSeries+1 == 6:
  324. probeLabel = ['probe2'] * nRows
  325. elif iSeries+1 == 7:
  326. probeLabel = ['probe2'] * nRows
  327. elif iSeries+1 == 8:
  328. probeLabel = ['probe1'] * nRows
  329. elif iSeries+1 == 9:
  330. probeLabel = ['probe1'] * nRows
  331. elif iSeries+1 == 10:
  332. probeLabel = ['probe1'] * nRows
  333. elif iSeries+1 == 11:
  334. probeLabel = ['probe1'] * nRows
  335. elif iSeries+1 == 12:
  336. probeLabel = ['probe1'] * nRows
  337. metadata = concatenateMat(metadata.astype(object), np.matrix(probeLabel).transpose().astype(object), 'horizontal')
  338. # Unit metadata: [unit_id metadata]
  339. unitIDs = []
  340. for iUnit in range(nRows):
  341. if metadata[iUnit, -1] == 'probe1':
  342. unitID = str(sessionID) + '1'
  343. else:
  344. unitID = str(sessionID) + '2'
  345. if metadata[iUnit, 0] < 9:
  346. unitID = unitID + '000' + str(metadata[iUnit, 0])
  347. elif metadata[iUnit, 0] < 99:
  348. unitID = unitID + '00' + str(metadata[iUnit, 0])
  349. elif metadata[iUnit, 0] < 999:
  350. unitID = unitID + '0' + str(metadata[iUnit, 0])
  351. else:
  352. unitID = unitID + str(metadata[iUnit, 0])
  353. unitIDs.append(unitID)
  354. metadata = concatenateMat(np.matrix(unitIDs).transpose().astype(object), metadata, 'horizontal')
  355. # Unit metadata: [metadata[:,:3] probe_channel_index probe_channel_id metadata[:,3:] electrode_group]
  356. channelIndices = []
  357. channelIDs = []
  358. electrodeGroups = []
  359. for iUnit in range(nRows):
  360. ind = np.logical_and(np.isin(np.array(electrodeTbl[1].data), np.array(metadata[iUnit,3])), \
  361. np.isin(electrodeTbl[-1].data, metadata[iUnit,-1])) # Find channel ID on a particular probe and get its index and ID
  362. channelIndices.append(np.where(ind)[0]+1)
  363. channelIDs.append(electrodeTbl[0].data[ind[0]])
  364. electrodeGroups.append(electrodeTbl[8].data[ind[0]])
  365. metadataInit = concatenateMat(metadata[:,:3], np.matrix(channelIndices).astype(object), 'horizontal')
  366. metadataInit = concatenateMat(metadataInit, np.matrix(channelIDs).transpose().astype(object), 'horizontal')
  367. metadataInit = concatenateMat(metadataInit, metadata[:,3:], 'horizontal')
  368. metadata = concatenateMat(metadataInit, np.matrix(electrodeGroups).transpose().astype(object), 'horizontal')
  369. metadataTbl = concatenateMat(metadataTbl, metadata)
  370. return spikes, metadataTbl, derivedData, columnLabels, columnDescription
  371. def reshapeWaveforms(waveforms, iEl, metadata):
  372. '''
  373. waveformsMean = reshapeWaveforms(waveforms, iEl, metadata)
  374. Function extracts relevant waveform information.
  375. Input: waveforms - a strucuture loaded from the waveforms MAT file.
  376. Relevant fields are waveforms (described above),
  377. maxWaveforms (same as waveforms but excluding all
  378. channels except for the maximum amplitude one), and
  379. cluIDs (unit cluster IDs corresponding to the
  380. dimension one in waveforms and maxWaveforms).
  381. iEl - probe reference number.
  382. metadata - a numpy array unit table produced by the function
  383. getSpikes.
  384. nCh - number of recording channels with waveforms for the same
  385. unit.
  386. Output: waveformsMean - waveforms.maxWaveforms converted into a numpy array.
  387. Rows correspond to units. MUAs are NaNs.
  388. '''
  389. # Load data fields
  390. if len(waveforms):
  391. maxWaveforms = np.array(waveforms.get('maxWaveforms')).transpose()
  392. cluIDs = np.array(waveforms.get('cluIDs')).transpose()
  393. else:
  394. maxWaveforms = []
  395. cluIDs = []
  396. # Load waveforms
  397. metadataInds = np.where(np.array(np.isin(metadata[:,11], 'probe' + str(iEl))))[0]
  398. metadata = metadata[metadataInds]
  399. waveformsMean = []
  400. if len(maxWaveforms) and len(maxWaveforms.shape):
  401. nWaveformSamples = maxWaveforms.shape[1]
  402. else:
  403. nWaveformSamples = 200
  404. for iUnit in range(metadata.shape[0]):
  405. row = np.where(np.isin(cluIDs, metadata[iUnit,1]))[0]
  406. if row.size:
  407. waveformsMean.append(maxWaveforms[row])
  408. else:
  409. waveformsMean.append(np.full([1,nWaveformSamples], np.nan))
  410. return waveformsMean
  411. def parsePeriod(acceptablePeriod, derivedData):
  412. '''
  413. acceptablePeriod = parsePeriod(acceptablePeriod, derivedData)
  414. Function resolves acceptable behavioural data periods. In cases when
  415. there are multiple periods, each of them needs to be loaded separately.
  416. Input: acceptablePeriod - behavioural data period loaded from a MAT
  417. file. It should be a numpy array.
  418. derivedData - passively loaded data from a MAT file.
  419. Output: acceptablePeriod - a numpy array of acceptable behavioural data
  420. periods.
  421. '''
  422. if len(acceptablePeriod.shape):
  423. if isinstance(acceptablePeriod[0], np.ndarray) and isinstance(acceptablePeriod[0][0], h5py.h5r.Reference):
  424. acceptablePeriodNew = []
  425. for iPeriod in range(len(acceptablePeriod)):
  426. data_of_interest_reference = acceptablePeriod[iPeriod, 0]
  427. acceptablePeriodNew.append(np.array(derivedData[data_of_interest_reference]))
  428. acceptablePeriod = np.matrix(np.array(acceptablePeriodNew).squeeze())
  429. else:
  430. acceptablePeriod = np.matrix(acceptablePeriod)
  431. return acceptablePeriod
  432. def markQualitySamples(acceptablePeriod, videoFrameTimes):
  433. '''
  434. acceptableSamples = markQualitySamples(acceptablePeriod, videoFrameTimes)
  435. Function marks acceptable behavioural samples given the sample times and
  436. the range of acceptable time periods.
  437. Input: acceptablePeriod - a numpy array of row vectors marking the
  438. beginning and end of acceptable time periods.
  439. videoFrameTimes - a numpy array (a row vector) with sample times.
  440. Ouptut: acceptableSamples - a logical vector marking acceptable samples
  441. by ones.
  442. '''
  443. if not len(acceptablePeriod) or not len(videoFrameTimes):
  444. acceptableSamples = []
  445. else:
  446. acceptableSamples = np.full((1, videoFrameTimes.shape[1]), False)
  447. for iPeriod in range(acceptablePeriod.shape[0]):
  448. acceptableSamples[np.logical_and(videoFrameTimes >= acceptablePeriod[iPeriod][0,0],
  449. videoFrameTimes <= acceptablePeriod[iPeriod][0,1])] = True
  450. return acceptableSamples+0