123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575 |
- # coding=utf-8
- '''
- Reach-to-grasp IO module
- This module provides an IO to load data recorded in the context of the reach-
- to-grasp experiments conducted by Thomas Brochier and Alexa Riehle at the
- Institute de Neurosciences de la Timone. The IO is based on the BlackrockIO of
- the Neo library, which is used in the background to load the primary data, and
- utilized the odML library to load metadata information. Specifically, this IO
- annotates the Neo object returned by BlackrockIO with semantic information,
- e.g., interpretation of digital event codes, and key-value pairs found in the
- corresponding odML file are attached to relevant Neo objects as annotations.
- Authors: Julia Sprenger, Lyuba Zehl, Michael Denker
- Copyright (c) 2017, Institute of Neuroscience and Medicine (INM-6),
- Forschungszentrum Juelich, Germany
- All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * Neither the names of the copyright holders nor the names of the contributors
- may be used to endorse or promote products derived from this software without
- specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- '''
- import glob
- import os
- import re
- import numpy as np
- import odml.tools
- import quantities as pq
- import neo
- # Using old version of IO as long as available - should be deleted at some point
- try:
- from neo.io import OldBlackrockIO as BlackrockIO
- except ImportError:
- from neo.io.blackrockio import BlackrockIO
- class ReachGraspIO(BlackrockIO):
- """
- Derived class from Neo's BlackrockIO to load recordings obtained from the
- reach-to-grasp experiments.
- Args:
- filename (string):
- File name (without extension) of the set of Blackrock files to
- associate with. Any .nsX or .nev, .sif, or .ccf extensions are
- ignored when parsing this parameter. Note: unless the parameter
- nev_override is given, this IO will load the nev file containing
- the most recent spike sorted data of all nev files found in the
- same directory as filename. The spike sorting version is attached
- to filename by a postfix '-XX', where XX is the version, e.g.,
- l101010-001-02 for spike sorting version 2 of file l101010-001. If
- an odML file is specified, the version must be listed in the odML
- entries at
- "/PreProcessing/OfflineSpikeSorting/Sortings"
- and relates to the section
- "/PreProcessing/OfflineSpikeSorting/Sorting-XX".
- If no odML is present, no information on the spike sorting (e.g.,
- if a unit is SUA or MUA) is provided by this IO.
- odml_directory (string):
- Alternative directory where the odML file is stored. If None, the
- directory is assumed to be the same as the .nev and .nsX data
- files. Default: None.
- nsx_override (string):
- File name of the .nsX files (without extension). If None,
- filename is used.
- Default: None.
- nev_override (string):
- File name of the .nev file (without extension). If None, the
- current spike-sorted version filename is used (see parameter
- filename above). Default: None.
- sif_override (string):
- File name of the .sif file (without extension). If None,
- filename is used.
- Default: None.
- ccf_override (string):
- File name of the .ccf file (without extension). If None,
- filename is used.
- Default: None.
- odml_override (string):
- File name of the .odml file (without extension). If None,
- filename is used.
- Default: None.
- verbose (boolean):
- If True, the class will output additional diagnostic
- information on stdout.
- Default: False
- Returns:
- -
- Attributes:
- condition_str (dict):
- Dictionary containing a list of string codes reflecting the trial
- types that occur in recordings in a certain condition code
- (dictionary keys). For example, for condition 1 (all grip first
- conditions), condition_str[1] contains the list
- ['SGHF', 'SGLF', 'PGHF', 'PGLF'].
- Possible conditions:
- 0:[]
- No trials, or condition not conclusive from file
- 4 types (two_cues_task):
- 1: all grip-first trial types with two different cues
- 2: all force-first trial types with two different cues
- 2 types (two_cues_task):
- 11: grip-first, but only LF types
- 12: grip-first, but only HF types
- 13: grip-first, but only SG types
- 14: grip-first, but only PG types
- 2 types (two_cues_task):
- 21: force-first, but only LF types
- 22: force-first, but only HF types
- 23: force-first, but only SG types
- 24: force-first, but only PG types
- 1 type (two_cues_task):
- 131: grip-first, but only SGLF type
- 132: grip-first, but only SGHF type
- 141: grip-first, but only PGLF type
- 142: grip-first, but only PGHF type
- 213: force-first, but only LFSG type
- 214: force-first, but only LFPG type
- 223: force-first, but only HFSG type
- 224: force-first, but only HFPG type
- 1 type (one_cue_task):
- 133: SGSG, only grip info, force unknown
- 144: PGPG, only grip info, force unknown
- 211: LFLF, only force info, grip unknown
- 222: HFHF, only force info, grip unknown
- event_labels_str (dict):
- Provides a text label for each digital event code returned as
- events by the parent BlackrockIO. For example,
- event_labels_str['65296'] contains the string 'TS-ON'.
- event_labels_codes (dict):
- Reverse of `event_labels_str`: Provides a list of event codes
- related to a specific text label for a trial event. For example,
- event_labels_codes['TS-ON'] contains the list ['65296']. In
- addition to the detailed codes, for convenience the meta codes
- 'CUE/GO', 'RW-ON', and 'SR' summarizing a set of digital events are
- defined for easier access.
- trial_const_sequence_str (dict):
- Dictionary contains the ordering of selected constant trial events
- for correct trials, e.g., as TS is the first trial event in a
- correct trial, trial_const_sequence_codes['TS'] is 0.
- trial_const_sequence_codes (dict):
- Reverse of trial_const_sequence_str: Dictionary contains the
- ordering of selected constant trial events for correct trials,
- e.g., trial_const_sequence_codes[0] is 'TS'.
- performance_str (dict):
- Text strings to help interpret the performance code of a trial. For
- example, correct trials have a performance code of 255, and thus
- performance_str[255] == 'correct_trial'
- performance_codes (dict):
- Reverse of performance_const_sequence_str. Returns the performance
- code of a given text string indicating trial performance. For
- example, performance_str['correct_trial'] == 255
- """
- # Create a dictionary of conditions (i.e., the trial types presented in a
- # given recording session)
- condition_str = {
- 0: [],
- 1: ['SGHF', 'SGLF', 'PGHF', 'PGLF'],
- 2: ['HFSG', 'HFPG', 'LFSG', 'LFPG'],
- 11: ['SGLF', 'PGLF'],
- 12: ['SGHF', 'PGHF'],
- 13: ['SGHF', 'SGLF'],
- 14: ['PGHF', 'PGLF'],
- 21: ['LFSG', 'LFPG'],
- 22: ['HFSG', 'HFPG'],
- 23: ['HFSG', 'LFSG'],
- 24: ['HFPG', 'LFPG'],
- 131: ['SGLF'],
- 132: ['SGHF'],
- 133: ['SGSG'],
- 141: ['PGLF'],
- 142: ['PGHF'],
- 144: ['PGPG'],
- 211: ['LFLF'],
- 213: ['LFSG'],
- 214: ['LFPG'],
- 222: ['HFHF'],
- 223: ['HFSG'],
- 224: ['HFPG']}
- ###########################################################################
- # event labels, the corresponding first 8 digits of their binary
- # representation and their meaning
- #
- # R L T T L L L L
- # w E a r E E E E
- # P D S S D D D D in
- # u c w t b t t b mo-
- # l r l r nk-
- # label:| ^ ^ ^ ^ ^ ^ ^ ^ | status of devices: | trial event label:| ey
- # 65280 < 0 0 0 0 0 0 0 0 > TS-OFF > TS-OFF/STOP > L,T
- # 65296 < 0 0 0 1 0 0 0 0 > TS-ON > TS-ON > all
- # 65312 < 0 0 1 0 0 0 0 0 > TaSw > STOP > all
- # 65344 < 0 1 0 0 0 0 0 0 > LEDc (+TS-OFF) > WS-ON/CUE-OFF > L,T
- # 65349 < 0 1 0 0 0 1 0 1 > LEDc|rt|rb (+TS-OFF) > PG-ON (CUE/GO-ON) > L,T
- # 65350 < 0 1 0 0 0 1 1 0 > LEDc|tl|tr (+TS-OFF) > HF-ON (CUE/GO-ON) > L,T
- # 65353 < 0 1 0 0 1 0 0 1 > LEDc|bl|br (+TS-OFF) > LF-ON (CUE/GO-ON) > L,T
- # 65354 < 0 1 0 0 1 0 1 0 > LEDc|lb|lt (+TS-OFF) > SG-ON (CUE/GO-ON) > L,T
- # 65359 < 0 1 0 0 1 1 1 1 > LEDall > ERROR-FLASH-ON > L,T
- # 65360 < 0 1 0 1 0 0 0 0 > LEDc (+TS-ON) > WS-ON/CUE-OFF > N
- # 65365 < 0 1 0 1 0 1 0 1 > LEDc|rt|rb (+TS-ON) > PG-ON (CUE/GO-ON) > N
- # 65366 < 0 1 0 1 0 1 1 0 > LEDc|tl|tr (+TS-ON) > HF-ON (CUE/GO-ON) > N
- # 65369 < 0 1 0 1 1 0 0 1 > LEDc|bl|br (+TS-ON) > LF-ON (CUE/GO-ON) > N
- # 65370 < 0 1 0 1 1 0 1 0 > LEDc|lb|lt (+TS-ON) > SG-ON (CUE/GO-ON) > N
- # 65376 < 0 1 1 0 0 0 0 0 > LEDc+TaSw > GO-OFF/RW-OFF > all
- # 65381 < 0 1 1 0 0 1 0 1 > TaSw (+LEDc|rt|rb) > SR (+PG) > all
- # 65382 < 0 1 1 0 0 1 1 0 > TaSw (+LEDc|tl|tr) > SR (+HF) > all
- # 65383 < 0 1 1 0 0 1 1 1 > TaSw (+LEDc|rt|rb|tl) > SR (+PGHF/HFPG) >
- # 65385 < 0 1 1 0 1 0 0 1 > TaSw (+LEDc|bl|br) > SR (+LF) > all
- # 65386 < 0 1 1 0 1 0 1 0 > TaSw (+LEDc|lb|lt) > SR (+SG) > all
- # 65387 < 0 1 1 0 1 0 1 1 > TaSw (+LEDc|lb|lt|br) > SR (+SGLF/LGSG) >
- # 65389 < 0 1 1 0 1 1 0 1 > TaSw (+LEDc|rt|rb|bl) > SR (+PGLF/LFPG) >
- # 65390 < 0 1 1 0 1 1 1 0 > TaSw (+LEDc|lb|lt|tr) > SR (+SGHF/HFSG) >
- # 65391 < 0 1 1 0 1 1 1 1 > LEDall (+TaSw) > ERROR-FLASH-ON > L,T
- # 65440 < 1 0 1 0 0 0 0 0 > RwPu (+TaSw) > RW-ON (noLEDs) > N
- # 65504 < 1 1 1 0 0 0 0 0 > RwPu (+LEDc) > RW-ON (-CONF) > L,T
- # 65509 < 1 1 1 0 0 1 0 1 > RwPu (+LEDcr) > RW-ON (+CONF-PG) > all
- # 65510 < 1 1 1 0 0 1 1 0 > RwPu (+LEDct) > RW-ON (+CONF-HF) > N?
- # 65513 < 1 1 1 0 1 0 0 1 > RwPu (+LEDcb) > RW-ON (+CONF-LF) > N?
- # 65514 < 1 1 1 0 1 0 1 0 > RwPu (+LEDcl) > RW-ON (+CONF-SG) > all
- # ^ ^ ^ ^ ^ ^ ^ ^
- # label binary code
- #
- # ABBREVIATIONS:
- # c (central), l (left), t (top), b (bottom), r (right),
- # HF (high force, LEDt), LF (low force, LEDb), SG (side grip, LEDl),
- # PG (precision grip, LEDr), RwPu (reward pump), TaSw (table switch),
- # TS (trial start), SR (switch release), WS (warning signal), RW (reward),
- # L (Lilou), T (Tanya t+a), N (Nikos n+i)
- ###########################################################################
- # Create dictionaries for event labels
- event_labels_str = {
- '65280': 'TS-OFF/STOP',
- '65296': 'TS-ON',
- '65312': 'STOP',
- '65344': 'WS-ON/CUE-OFF',
- '65349': 'PG-ON',
- '65350': 'HF-ON',
- '65353': 'LF-ON',
- '65354': 'SG-ON',
- '65359': 'ERROR-FLASH-ON',
- '65360': 'WS-ON/CUE-OFF',
- '65365': 'PG-ON',
- '65366': 'HF-ON',
- '65369': 'LF-ON',
- '65370': 'SG-ON',
- '65376': 'GO/RW-OFF',
- '65381': 'SR (+PG)',
- '65382': 'SR (+HF)',
- '65383': 'SR (+PGHF/HFPG)',
- '65385': 'SR (+LF)',
- '65386': 'SR (+SG)',
- '65387': 'SR (+SGLF/LFSG)',
- '65389': 'SR (+PGLF/LFPG)',
- '65390': 'SR (+SGHF/HFSG)',
- '65391': 'ERROR-FLASH-ON',
- '65440': 'RW-ON (noLEDs)',
- '65504': 'RW-ON (-CONF)',
- '65509': 'RW-ON (+CONF-PG)',
- '65510': 'RW-ON (+CONF-HF)',
- '65513': 'RW-ON (+CONF-LF)',
- '65514': 'RW-ON (+CONF-SG)'}
- event_labels_codes = dict(
- [(k, []) for k in np.unique(list(event_labels_str.values()))]) #inefficient in Python2
- for k in list(event_labels_codes):
- for l, v in event_labels_str.items(): #inefficient in Python2
- if v == k:
- event_labels_codes[k].append(l)
- # additional summaries
- event_labels_codes['CUE/GO'] = \
- event_labels_codes['SG-ON'] + \
- event_labels_codes['PG-ON'] + \
- event_labels_codes['LF-ON'] + \
- event_labels_codes['HF-ON']
- event_labels_codes['RW-ON'] = \
- event_labels_codes['RW-ON (+CONF-PG)'] + \
- event_labels_codes['RW-ON (+CONF-HF)'] + \
- event_labels_codes['RW-ON (+CONF-LF)'] + \
- event_labels_codes['RW-ON (+CONF-SG)'] + \
- event_labels_codes['RW-ON (-CONF)'] + \
- event_labels_codes['RW-ON (noLEDs)']
- event_labels_codes['SR'] = \
- event_labels_codes['SR (+PG)'] + \
- event_labels_codes['SR (+HF)'] + \
- event_labels_codes['SR (+LF)'] + \
- event_labels_codes['SR (+SG)'] + \
- event_labels_codes['SR (+PGHF/HFPG)'] + \
- event_labels_codes['SR (+SGHF/HFSG)'] + \
- event_labels_codes['SR (+PGLF/LFPG)'] + \
- event_labels_codes['SR (+SGLF/LFSG)']
- del k, l, v
- # Create dictionaries for constant trial sequences (in all monkeys)
- # (bit position (value) set if trial event (key) occurred)
- trial_const_sequence_codes = {
- 'TS-ON': 0,
- 'WS-ON': 1,
- 'CUE-ON': 2,
- 'CUE-OFF': 3,
- 'GO-ON': 4,
- 'SR': 5,
- 'RW-ON': 6,
- 'STOP': 7}
- trial_const_sequence_str = dict(
- (v, k) for k, v in trial_const_sequence_codes.items()) #inefficient in Python2
- # Create dictionaries for trial performances
- # (resulting decimal number from binary number created from trial_sequence)
- performance_codes = {
- 'incomplete_trial': 0,
- 'error<SR-ON': 159,
- 'error<WS': 161,
- 'error<CUE-ON': 163,
- 'error<CUE-OFF': 167,
- 'error<GO-ON': 175,
- 'grip_error': 191,
- 'correct_trial': 255}
- performance_str = dict((v, k) for k, v in performance_codes.items()) #inefficient in Python2
- def __init__(
- self, filename, odml_directory=None,
- nsx_override=None, nev_override=None,
- sif_override=None, ccf_override=None, odml_filename=None,
- verbose=False):
- """
- Constructor
- """
- # Remember choice whether to print diagnostic messages or not
- self._verbose = verbose
- # Remove known extensions from input filename
- for ext in self.extensions:
- filename = re.sub(os.path.extsep + ext + '$', '', filename)
- if nev_override:
- # check if sorting postfix is appended to nev_override name
- if nev_override[-3] == '-':
- sorting_postfix = nev_override[-2:]
- else:
- sorting_postfix = None
- sorting_version = nev_override
- else:
- # find most recent spike sorting version
- nev_versions = [re.sub(
- os.path.extsep + 'nev$', '', p) for p in glob.glob(
- filename + '*.nev')]
- nev_versions = [p.replace(filename, '') for p in nev_versions]
- if len(nev_versions):
- sorting_postfix = sorted(nev_versions)[-1]
- else:
- sorting_postfix = ''
- sorting_version = filename + sorting_postfix
- # Initialize file
- BlackrockIO.__init__(
- self, filename, nsx_override=nsx_override,
- nev_override=sorting_version, sif_override=sif_override,
- ccf_override=ccf_override, verbose=verbose)
- # if no odML directory is specified, use same directory as main files
- if not odml_directory:
- odml_directory = os.path.dirname(self.filename)[:-1]
- # remove extensions from odml override
- filen = os.path.split(self.filename)[-1]
- if odml_filename:
- self._filenames['odml'] = ''.join(
- [odml_directory, os.path.sep, odml_filename])
- else:
- self._filenames['odml'] = ''.join(
- [odml_directory, os.path.sep, filen])
- file2check = ''.join([self._filenames['odml'], os.path.extsep, 'odml'])
- if os.path.exists(file2check):
- self._avail_files['odml'] = True
- self.odmldoc = odml.tools.xmlparser.load(file2check)
- else:
- self._avail_files['odml'] = False
- self.odmldoc = None
- # If we did not specify an explicit sorting version, and there is an
- # odML, then make sure the detected sorting version matches the odML
- if self.odmldoc:
- if self.odmldoc.sections['PreProcessing'].sections[
- 'OfflineSpikeSorting'].properties[
- 'Sortings'].value.data != sorting_postfix:
- self._print_verbose(
- "Attempting to utilize the most recent "
- "sorting version in file %s, but the sorting version "
- "specified in odML is %s" % (
- sorting_version,
- self.odmldoc.sections['PreProcessing'].sections[
- 'OfflineSpikeSorting'].properties['Sortings']))
- self._load_spikesorting_info = False
- else:
- self._load_spikesorting_info = True
- else:
- self._load_spikesorting_info = False
- def __is_set(self, flag, pos):
- """
- Checks if bit is set at the given position for flag. If flag is an
- array, an array will be returned.
- """
- return flag & (1 << pos) > 0
- def __set_bit(self, flag, pos):
- """
- Returns the given flag with an additional bit set at the given
- position. for flag. If flag is an array, an array will be returned.
- """
- return flag | (1 << pos)
- def __add_rejection_to_event(self, event):
- """
- Given an event with annotation trial_id, adds information on whether to
- reject the trial or not.
- """
- if self.odmldoc:
- # Get rejection bands
- sec = self.odmldoc['PreProcessing']
- bands = sec.properties['LFPBands'].value
- for band in bands:
- sec = self.odmldoc['PreProcessing'][band.data]
- if type(sec.properties['RejTrials'].value) is list:
- rej_trials = [int(_.data) for _ in sec.properties[
- 'RejTrials'].value]
- rej_index = np.in1d(
- event.annotations['trial_id'],
- rej_trials)
- elif sec.properties['RejTrials'].value.data == -1:
- rej_index = np.zeros(
- (len(event.annotations['trial_id'])), dtype=bool)
- elif sec.properties['RejTrials'].value.data >= 0:
- rej_index = np.in1d(
- event.annotations['trial_id'],
- [sec.properties['RejTrials'].value])
- else:
- raise ValueError(
- "Invalid entry %s in odML for rejected trials in LFP "
- " band %s." %
- (sec.properties['RejTrials'].value.data, band.data))
- event.annotate(
- **{str('trial_reject_' + band.data): list(rej_index)})
- def __extract_task_condition(self, trialtypes):
- """
- Extracts task condition from trialtypes.
- """
- occurring_trtys = np.unique(trialtypes).tolist()
- # reduce occurring_trtys to actual trialtypes
- # (remove all not identifiable trialtypes (incomplete/error trial))
- if 'NONE' in occurring_trtys:
- occurring_trtys.remove('NONE')
- # (remove all trialtypes where only the CUE was detected (error trial))
- if 'SG' in occurring_trtys:
- occurring_trtys.remove('SG')
- if 'PG' in occurring_trtys:
- occurring_trtys.remove('PG')
- if 'LF' in occurring_trtys:
- occurring_trtys.remove('LF')
- if 'HF' in occurring_trtys:
- occurring_trtys.remove('HF')
- # first set to unidentified task condition
- task_condition = 0
- if len(occurring_trtys) > 0:
- for cnd, trtys in self.condition_str.items(): #inefficient in Python2
- if set(trtys) == set(occurring_trtys):
- # replace with detected task condition
- task_condition = cnd
- return task_condition
- def __extract_analog_events_from_odml(self, t_start, t_stop):
- event_name = []
- event_time = []
- trial_id = []
- trial_timestamp_id = []
- performance_code = []
- trial_type = []
- # Look for all Trial Sections
- sec = self.odmldoc['Recording']['TaskSettings']
- ff = lambda x: x.name.startswith('Trial_')
- tr_secs = sec.itersections(filter_func=ff)
- for trial_sec in tr_secs:
- for signalname in ['GripForceSignals', 'DisplacementSignal']:
- for analog_events in trial_sec[
- 'AnalogEvents'][signalname].properties:
- time = analog_events.value.data * \
- pq.CompoundUnit(analog_events.value.unit)
- if time >= t_start and time < t_stop:
- event_name.append(analog_events.name)
- event_time.append(time)
- trial_id.append(
- trial_sec.properties['TrialID'].value.data)
- trial_timestamp_id.append(
- trial_sec.properties[
- 'TrialTimestampID'].value.data)
- performance_code.append(
- trial_sec.properties['PerformanceCode'].value.data)
- trial_type.append(
- trial_sec.properties['TrialType'].value.data)
- # Create event object with analog events
- analog_events = neo.Event(
- times=pq.Quantity(
- [_.magnitude for _ in event_time],
- units=event_time[0].units).rescale('ms'),
- labels=np.array(event_name),
- name='AnalogTrialEvents',
- description='Events extracted from analog signals')
- analog_events.annotate(
- trial_id=trial_id,
- trial_timestamp_id=trial_timestamp_id,
- performance_in_trial=performance_code,
- belongs_to_trialtype=trial_type,
- trial_event_labels=event_name)
- return analog_events
- def __annotate_dig_trial_events(self, events):
- """
- Modifies events of digital input port to trial events of the
- reach-to-grasp project.
- """
- # Modifiy name and description
- events.name = "DigitalTrialEvents"
- events.description = "Trial " + events.description.lower()
- # Uncomment for event and trial sequence debugging
- # for ev in events.labels:
- # if ev in list(self.event_labels_str):
- # print ev, self.event_labels_str[ev]
- # else:
- # print ev
- # Extract beginning of first complete trial
- tson_label = self.event_labels_codes['TS-ON'][0]
- if tson_label in events.labels:
- first_TSon_idx = list(events.labels).index(tson_label)
- else:
- first_TSon_idx = len(events.labels)
- # Extract end of last complete trial
- stop_label = self.event_labels_codes['STOP'][0]
- if stop_label in events.labels:
- last_WSoff_idx = len(events.labels) - \
- list(events.labels[::-1]).index(stop_label) - 1
- else:
- last_WSoff_idx = -1
- # Annotate events with modified labels, trial ids, and trial types
- trial_event_labels = []
- trial_ID = []
- trial_timestamp_ID = []
- trialtypes = {-1: 'NONE'}
- trialsequence = {-1: 0}
- for i, l in enumerate(events.labels):
- if i < first_TSon_idx or i > last_WSoff_idx:
- trial_event_labels.append('NONE')
- trial_ID.append(-1)
- trial_timestamp_ID.append(-1)
- else:
- # interpretation of TS-ON
- if self.event_labels_str[l] == 'TS-ON':
- if i > 0:
- prev_ev = events.labels[i - 1]
- if self.event_labels_str[prev_ev] in \
- ['STOP', 'TS-OFF/STOP']:
- timestamp_id = int(events.times[i].rescale(
- self._BlackrockIO__nev_params(
- 'event_unit')).item())
- trial_timestamp_ID.append(timestamp_id)
- trial_event_labels.append('TS-ON')
- trialsequence[timestamp_id] = self.__set_bit(
- 0, self.trial_const_sequence_codes['TS-ON'])
- else:
- timestamp_id = trial_timestamp_ID[-1]
- trial_timestamp_ID.append(timestamp_id)
- trial_event_labels.append('TS-ON-ERROR')
- else:
- timestamp_id = int(events.times[i].rescale(
- self._BlackrockIO__nev_params(
- 'event_unit')).item())
- trial_timestamp_ID.append(timestamp_id)
- trial_event_labels.append('TS-ON')
- trialsequence[timestamp_id] = self.__set_bit(
- 0, self.trial_const_sequence_codes['TS-ON'])
- # Identify trial ID if odML exists
- ID = -1
- if self.odmldoc:
- sec = self.odmldoc['Recording']['TaskSettings']
- ff = lambda x: x.name.startswith('Trial_')
- tr_secs = sec.itersections(filter_func=ff)
- for trial_sec in tr_secs:
- if trial_sec.properties[
- 'TrialTimestampID'].value.data == \
- timestamp_id:
- ID = trial_sec.properties['TrialID'].value.data
- trial_ID.append(ID)
- # interpretation of GO/RW-OFF
- elif self.event_labels_str[l] == 'GO/RW-OFF':
- trial_timestamp_ID.append(timestamp_id)
- trial_ID.append(ID)
- trial_event_labels.append('GO/RW-OFF')
- # interpretation of ERROR-FLASH-ON
- elif l in self.event_labels_codes['ERROR-FLASH-ON']:
- trial_timestamp_ID.append(timestamp_id)
- trial_ID.append(ID)
- trial_event_labels.append('ERROR-FLASH-ON')
- # Error-Flash hides too early activation of SR
- # SR is set to 1 here to match perf codes between monkeys
- trialsequence[timestamp_id] = self.__set_bit(
- trialsequence[timestamp_id],
- self.trial_const_sequence_codes['SR'])
- # TS-OFF/STOP
- elif self.event_labels_str[l] == 'TS-OFF/STOP':
- trial_timestamp_ID.append(timestamp_id)
- trial_ID.append(ID)
- prev_ev = events.labels[i - 1]
- if self.event_labels_str[prev_ev] == 'TS-ON':
- trial_event_labels.append('TS-OFF')
- elif prev_ev in self.event_labels_codes['ERROR-FLASH-ON']:
- trial_event_labels.append('STOP')
- trialsequence[timestamp_id] = self.__set_bit(
- trialsequence[timestamp_id],
- self.trial_const_sequence_codes['STOP'])
- else:
- trial_event_labels.append('STOP')
- trialsequence[timestamp_id] = self.__set_bit(
- trialsequence[timestamp_id],
- self.trial_const_sequence_codes['STOP'])
- # interpretation of WS-ON/CUE-OFF
- elif self.event_labels_str[l] == 'WS-ON/CUE-OFF':
- trial_timestamp_ID.append(timestamp_id)
- trial_ID.append(ID)
- prev_ev = events.labels[i - 1]
- if self.event_labels_str[prev_ev] in \
- ['TS-ON', 'TS-OFF/STOP']:
- trial_event_labels.append('WS-ON')
- trialsequence[timestamp_id] = self.__set_bit(
- trialsequence[timestamp_id],
- self.trial_const_sequence_codes['WS-ON'])
- elif (prev_ev in self.event_labels_codes['CUE/GO'] or
- prev_ev in self.event_labels_codes['GO/RW-OFF']):
- trial_event_labels.append('CUE-OFF')
- trialsequence[timestamp_id] = self.__set_bit(
- trialsequence[timestamp_id],
- self.trial_const_sequence_codes['CUE-OFF'])
- else:
- raise ValueError("Unknown trial event sequence.")
- # interpretation of CUE and GO events and trialtype detection
- elif l in self.event_labels_codes['CUE/GO']:
- trial_timestamp_ID.append(timestamp_id)
- trial_ID.append(ID)
- prprev_ev = events.labels[i - 2]
- if self.event_labels_str[prprev_ev] in \
- ['TS-ON', 'TS-OFF/STOP']:
- trial_event_labels.append('CUE-ON')
- trialsequence[timestamp_id] = self.__set_bit(
- trialsequence[timestamp_id],
- self.trial_const_sequence_codes['CUE-ON'])
- trialtypes[timestamp_id] = self.event_labels_str[l][:2]
- elif prprev_ev in self.event_labels_codes['CUE/GO']:
- trial_event_labels.append('GO-ON')
- trialsequence[timestamp_id] = self.__set_bit(
- trialsequence[timestamp_id],
- self.trial_const_sequence_codes['GO-ON'])
- trialtypes[timestamp_id] += \
- self.event_labels_str[l][:2]
- else:
- raise ValueError("Unknown trial event sequence.")
- # interpretation of WS-OFF
- elif self.event_labels_str[l] == 'STOP':
- trial_timestamp_ID.append(timestamp_id)
- trial_ID.append(ID)
- prev_ev = self.event_labels_str[events.labels[i - 1]]
- if prev_ev == 'ERROR-FLASH-ON':
- trial_event_labels.append('ERROR-FLASH-OFF')
- else:
- trial_event_labels.append('STOP')
- trialsequence[timestamp_id] = self.__set_bit(
- trialsequence[timestamp_id],
- self.trial_const_sequence_codes['STOP'])
- # interpretation of SR events
- elif l in self.event_labels_codes['SR']:
- trial_timestamp_ID.append(timestamp_id)
- trial_ID.append(ID)
- prev_ev = events.labels[i - 1]
- if prev_ev in self.event_labels_codes['SR']:
- trial_event_labels.append('SR-REP')
- elif prev_ev in self.event_labels_codes['RW-ON']:
- trial_event_labels.append('RW-OFF')
- else:
- trial_event_labels.append('SR')
- trialsequence[timestamp_id] = self.__set_bit(
- trialsequence[timestamp_id],
- self.trial_const_sequence_codes['SR'])
- # interpretation of RW events
- elif l in self.event_labels_codes['RW-ON']:
- trial_timestamp_ID.append(timestamp_id)
- trial_ID.append(ID)
- prev_ev = events.labels[i - 1]
- if prev_ev in self.event_labels_codes['RW-ON']:
- trial_event_labels.append('RW-ON-REP')
- else:
- trial_event_labels.append('RW-ON')
- trialsequence[timestamp_id] = self.__set_bit(
- trialsequence[timestamp_id],
- self.trial_const_sequence_codes['RW-ON'])
- else:
- raise ValueError("Unknown event label.")
- # add modified trial_event_labels to annotations
- events.annotate(trial_event_labels=trial_event_labels)
- # add trial timestamp IDs
- events.annotate(trial_timestamp_id=trial_timestamp_ID)
- # add trial IDs
- events.annotate(trial_id=trial_ID)
- # add modified belongs_to_trialtype to annotations
- for tid in trial_timestamp_ID:
- if tid not in list(trialtypes):
- trialtypes[tid] = 'NONE'
- belongs_to_trialtype = [
- trialtypes[tid] for tid in trial_timestamp_ID]
- events.annotate(belongs_to_trialtype=belongs_to_trialtype)
- # add modified trial_performance_codes to annotations
- performance_in_trial = [
- trialsequence[tid] for tid in trial_timestamp_ID]
- events.annotate(performance_in_trial=performance_in_trial)
- def __annotate_units_with_odml(self, units):
- """
- Annotates units with metadata from odml file.
- """
- # Can the spike sorting info from the odML be matched with the odML?
- if not self._load_spikesorting_info:
- return
- for un in units:
- an_dict = dict(
- sua=False,
- mua=False,
- noise=False)
- try:
- sec = self.odmldoc['UtahArray']['Array'][
- 'Electrode_%03d' % un.annotations['channel_id']][
- 'OfflineSpikeSorting']
- except KeyError:
- return
- suaids = [v.data for v in sec.properties['SUAIDs'].values]
- muaid = sec.properties['MUAID'].value.data
- noiseids = [v.data for v in sec.properties['NoiseIDs'].values]
- if un.annotations['unit_id'] in suaids:
- an_dict['sua'] = True
- elif un.annotations['unit_id'] in noiseids:
- an_dict['noise'] = True
- elif un.annotations['unit_id'] == muaid:
- an_dict['mua'] = True
- else:
- raise ValueError(
- "Unit %i is not registered for channel %i in odML file."
- % (un.annotations['unit_id'],
- un.annotations['channel_id']))
- if ('Unit_%02i' % un.annotations['unit_id']) in sec.sections:
- unit_sec = sec['Unit_%02i' % un.annotations['unit_id']]
- if an_dict['sua']:
- an_dict['SNR'] = unit_sec.properties['SNR'].value.data
- an_dict['spike_duration'] = unit_sec.properties[
- 'SpikeDuration'].value.data
- an_dict['spike_amplitude'] = unit_sec.properties[
- 'SpikeAmplitude'].value.data
- an_dict['spike_count'] = unit_sec.properties[
- 'SpikeCount'].value.data
- # Annotate Unit and all children for convenience
- un.annotate(**an_dict)
- for st in un.spiketrains:
- st.annotate(**an_dict)
- def __annotate_spiketrains_with_odml(self, sts):
- """
- Annotates spiketrains with metadata from odml file.
- """
- def __annotate_analogsignals_with_odml(self, asig):
- """
- Annotates analogsignals with metadata from odml file.
- """
- if self.odmldoc and asig.annotations['channel_id'] in range(1, 129):
- # Annotate filter settings from odML
- sec = self.odmldoc[
- 'Cerebus']['NeuralSignalProcessor']['NeuralSignals'][
- 'Filter_ns%i' % asig.annotations['nsx']]
- asig.annotate(
- filter_hi_pass_freq=pq.Quantity(
- sec.properties['HighPassFreq'].value.data,
- sec.properties['HighPassFreq'].value.unit),
- filter_lo_pass_freq=pq.Quantity(
- sec.properties['LowPassFreq'].value.data,
- sec.properties['LowPassFreq'].value.unit),
- filter_hi_pass_order=sec.properties[
- 'HighPassOrder'].value.data,
- filter_lo_pass_order=sec.properties[
- 'LowPassOrder'].value.data,
- filter_type=sec.properties[
- 'Type'].value.data)
- def __annotate_channelindex_with_odml(self, chidx):
- """
- Annotates channelindex with metadata from odml file.
- """
- if self.odmldoc:
- # Get rejection bands
- sec = self.odmldoc['PreProcessing']
- bands = sec.properties['LFPBands'].value
- if hasattr(bands, '__iter__'):
- for band in bands:
- sec = self.odmldoc['PreProcessing'][band.data]
- if type(sec.properties['RejElectrodes'].value) is list:
- rej_electrodes = [int(_.data) for _ in sec.properties[
- 'RejElectrodes'].value]
- rej = chidx.channel_ids[0] in rej_electrodes
- elif sec.properties['RejElectrodes'].value.data == -1:
- rej = False
- elif sec.properties['RejElectrodes'].value.data >= 0:
- rej_electrodes = sec.properties[
- 'RejElectrodes'].value.data
- rej = (chidx.channel_ids[0] == rej_electrodes)
- else:
- raise ValueError(
- "Invalid entry %s in odML for rejected electrodes "
- "in LFP band %s." % (
- sec.properties['RejElectrodes'].value.data,
- band.data))
- rej_dict = {str('electrode_reject_' + band.data): rej}
- # Annotate ChannelIndex and all children for convenience
- chidx.annotate(**rej_dict)
- for asig in chidx.analogsignals:
- asig.annotate(**rej_dict)
- for unit in chidx.units:
- unit.annotate(**rej_dict)
- for st in unit.spiketrains:
- st.annotate(**rej_dict)
- # Annotate connector aligned ID to channel
- if chidx.channel_ids[0] in \
- chidx.block.annotations['avail_electrode_ids']:
- ca_dict = {
- 'connector_aligned_id': chidx.block.annotations[
- 'avail_electrode_ids'].index(chidx.channel_ids[0])+1}
- chidx.coordinates = pq.Quantity(np.array([
- np.mod(ca_dict['connector_aligned_id']-1, 10)*.4,
- (ca_dict['connector_aligned_id']-1)/10*.4]),
- units=pq.mm)
- chidx.annotate(**ca_dict)
- for asig in chidx.analogsignals:
- asig.annotate(**ca_dict)
- for unit in chidx.units:
- unit.annotate(**ca_dict)
- for st in unit.spiketrains:
- st.annotate(**ca_dict)
- def __annotate_block_with_odml(self, bl):
- """
- Annotates block with metadata from odml file.
- """
- sec = self.odmldoc['Project']
- bl.annotate(
- project_name=sec.properties['Name'].value.data,
- project_type=sec.properties['Type'].value.data,
- project_subtype=sec.properties['Subtype'].value.data)
- sec = self.odmldoc['Project']['TaskDesigns']
- bl.annotate(
- taskdesigns=[v.data for v in sec.properties['UsedDesign'].values])
- sec = self.odmldoc['Subject']
- bl.annotate(
- subject_name=sec.properties['GivenName'].value.data,
- subject_gender=sec.properties['Gender'].value.data,
- subject_activehand=sec.properties['ActiveHand'].value.data,
- subject_birthday=sec.properties['Birthday'].value.data)
- sec = self.odmldoc['Setup']
- bl.annotate(setup_location=sec.properties['Location'].value.data)
- sec = self.odmldoc['UtahArray']
- bl.annotate(array_serialnum=sec.properties['SerialNo'].value.data)
- sec = self.odmldoc['UtahArray']['Connector']
- bl.annotate(connector_type=sec.properties['Style'].value.data)
- sec = self.odmldoc['UtahArray']['Array']
- bl.annotate(arraygrids_tot_num=sec.properties['GridCount'].value.data)
- sec = self.odmldoc['UtahArray']['Array']['Grid_01']
- bl.annotate(
- electrodes_tot_num=sec.properties['ElectrodeCount'].value.data,
- electrodes_pitch=pq.Quantity(
- sec.properties['ElectrodePitch'].value.data,
- units=sec.properties['ElectrodePitch'].value.unit),
- arraygrid_row_num=sec.properties['GridRows'].value.data,
- arraygrid_col_num=sec.properties['GridColumns'].value.data)
- secs = self.odmldoc['UtahArray']['Array'].sections
- bl.annotate(
- avail_electrode_ids=[])
- for i in range(1, 101):
- elidx = [s.properties['ID'].value.data for s in secs if
- s.name.startswith('Electrode') and
- s.properties['ConnectorAlignedID'].value.data == i]
- if len(elidx) == 0:
- bl.annotations['avail_electrode_ids'].append(-1)
- elif len(elidx) == 1:
- bl.annotations['avail_electrode_ids'].append(elidx[0])
- else:
- raise ValueError("Electrode IDs in odML file are corrupt. "
- "ID %i occurs %i times" % (i, len(elidx)))
- # TODO: add list of behavioral channels
- # bl.annotate(avail_behavsig_indexes=[])
- def __correct_filter_shifts(self, asig):
- if self.odmldoc and asig.annotations['channel_id'] in range(1, 129):
- # Get and correct for shifts
- sec = self.odmldoc[
- 'Cerebus']['NeuralSignalProcessor']['NeuralSignals'][
- 'Filter_ns%i' % asig.annotations['nsx']]
- shift = pq.Quantity(
- sec.properties['EstimatedShift'].value.data,
- sec.properties['EstimatedShift'].value.unit)
- asig.t_start = asig.t_start - shift
- # Annotate shift
- asig.annotate(filter_shift_correction=shift)
- def __merge_digital_analog_events(self, events):
- """
- Merge the two event arrays AnalogTrialEvents and DigitalTrialEvents
- into one common event array TrialEvents.
- """
- event_name = []
- event_time = None
- trial_id = []
- trial_timestamp_id = []
- performance_code = []
- trial_type = []
- for event in events:
- if event.name in ['AnalogTrialEvents', 'DigitalTrialEvents']:
- # Extract event times
- if event_time is None:
- event_time = event.times.magnitude
- event_units = event.times.units
- else:
- event_time = np.concatenate((
- event_time,
- event.times.rescale(event_units).magnitude))
- # Transfer annotations
- trial_id.extend(
- event.annotations['trial_id'])
- trial_timestamp_id.extend(
- event.annotations['trial_timestamp_id'])
- performance_code.extend(
- event.annotations['performance_in_trial'])
- trial_type.extend(
- event.annotations['belongs_to_trialtype'])
- event_name.extend(
- event.annotations['trial_event_labels'])
- # Sort time stamps and save sort order
- sort_idx = np.argsort(event_time)
- event_time = event_time[sort_idx]
- # Create event object with analog events
- merged_event = neo.Event(
- times=pq.Quantity(event_time, units=event_units),
- labels=np.array([event_name[_] for _ in sort_idx]),
- name='TrialEvents',
- description='All trial events (digital and analog)')
- merged_event.annotate(
- trial_id=[trial_id[_] for _ in sort_idx],
- trial_timestamp_id=[trial_timestamp_id[_] for _ in sort_idx],
- performance_in_trial=[performance_code[_] for _ in sort_idx],
- belongs_to_trialtype=[trial_type[_] for _ in sort_idx],
- trial_event_labels=[event_name[_] for _ in sort_idx])
- return merged_event
- def read_block(
- self, index=None, name=None, description=None, nsx_to_load='none',
- n_starts=None, n_stops=None, channels=range(1, 97), units='none',
- load_waveforms=False, load_events=False, scaling='raw',
- correct_filter_shifts=True, lazy=False, cascade=True):
- """
- Reads file contents as a Neo Block.
- The Block contains one Segment for each entry in zip(n_starts,
- n_stops). If these parameters are not specified, the default is
- to store all data in one Segment.
- The Block contains one ChannelIndex per channel.
- Args:
- index (None, int):
- If not None, index of block is set to user input.
- name (None, str):
- If None, name is set to default, otherwise it is set to user
- input.
- description (None, str):
- If None, description is set to default, otherwise it is set to
- user input.
- nsx_to_load (int, list, str):
- ID(s) of nsx file(s) from which to load data, e.g., if set to
- 5 only data from the ns5 file are loaded. If 'none' or empty
- list, no nsx files and therefore no analog signals are loaded.
- If 'all', data from all available nsx are loaded.
- n_starts (None, Quantity, list):
- Start times for data in each segment. Number of entries must be
- equal to length of n_stops. If None, intrinsic recording start
- times of files set are used.
- n_stops (None, Quantity, list):
- Stop times for data in each segment. Number of entries must be
- equal to length of n_starts. If None, intrinsic recording stop
- times of files set are used.
- channels (int, list, str):
- Channel id(s) from which to load data. If 'none' or empty list,
- no channels and therefore no analog signal or spiketrains are
- loaded. If 'all', all available channels are loaded. By
- default, all neural channels (1-96) are loaded.
- units (int, list, str, dict):
- ID(s) of unit(s) to load. If 'none' or empty list, no units and
- therefore no spiketrains are loaded. If 'all', all available
- units are loaded. If dict, the above can be specified
- individually for each channel (keys), e.g. {1: 5, 2: 'all'}
- loads unit 5 from channel 1 and all units from channel 2.
- load_waveforms (boolean):
- If True, waveforms are attached to all loaded spiketrains.
- load_events (boolean):
- If True, all recorded events are loaded.
- scaling (str):
- Determines whether time series of individual
- electrodes/channels are returned as AnalogSignals containing
- raw integer samples ('raw'), or scaled to arrays of floats
- representing voltage ('voltage'). Note that for file
- specification 2.1 and lower, the option 'voltage' requires a
- nev file to be present.
- correct_filter_shifts (bool):
- If True, shifts of the online-filtered neural signals (e.g.,
- ns2, channels 1-128) are corrected by time-shifting the signal
- by a heuristically determined estimate stored in the metadata,
- in the property EstimatedShift, under the path
- /Cerebus/NeuralSignalProcessor/NeuralSignals/Filter_nsX/
- lazy (bool):
- If True, only the shape of the data is loaded.
- cascade (bool or "lazy"):
- If True, only the block without children is returned.
- Returns:
- Block (neo.segment.Block):
- Block linking to all loaded Neo objects.
- Block annotations:
- avail_file_set (list of str):
- List of file extensions of the files found to be
- associated to the project, and which are used in
- loading the data, e.g., ccf, odml, nev, ns2,...
- avail_nsx (list of int):
- List of integers specifying the .nsX files available,
- e.g., [2, 5] indicates that an ns2 and and ns5 file are
- available.
- avail_nev (bool):
- True if a .nev file is available.
- avail_ccf (bool):
- True if a .ccf file is available.
- avail_sif (bool):
- True if a .sif file is available.
- nb_segments (int):
- Number of segments created after merging recording
- times specified by user with the intrinsic ones of the
- file set.
- project_name (str):
- Identifier for the project/experiment.
- project_type (str):
- Identifier for the type of project/experiment.
- project_subtype (str):
- Identifier of the subtype of the project/experiment.
- taskdesigns (list of str):
- List of strings identifying the task designed presented
- during the recording. The standard task reach-to-grasp
- is denoted by the string "TwoCues".
- conditions (list of int):
- List of condition codes (each code describing the set
- of trial types presented to the subject during a
- segment of the recording) present during the recording.
- For a mapping of condition codes to trial types, see
- the condition_str attribute of the ReachGraspIO class.
- subject_name (str):
- Name of the recorded subject.
- subject_gender (bool):
- 'male' or 'female'.
- subject_birthday (datetime):
- Birthday of the recorded subject.
- subject_activehand (str):
- Handedness of the subject.
- setup_location (str):
- Physical location of the recording setup.
- avail_electrode_ids (list of int):
- List of length 100 of electrode channel IDs (Blackrock
- IDs) ordered corresponding to the connector-aligned
- linear electrode IDs. The connector-aligned IDs start
- at 1 in the bottom left corner, and increase from left
- to right, and from bottom to top assuming the array is
- placed in front of the observer pins facing down,
- connector extruding to the right:
- 91 92 ... 99 100 \
- 81 82 ... 89 90 \
- ... ... --- Connector Wires
- 11 12 ... 19 20 /
- 1 2 ... 9 10 /
- Thus,
- avail_electrode_ids[k-1]
- is the Blackrock channel ID corresponding to connector-
- aligned ID k. Unconnected/unavailable channels are
- marked by -1.
- arraygrids_tot_num (int):
- Number of Utah arrays (not necessarily all connected).
- electrodes_tot_num (int):
- Number of electrodes of the Utah array (not necessarily
- all connected).
- electrodes_pitch (float):
- Distance in micrometers between neighboring electrodes
- in one row/column.
- array_serial_num (str):
- Serial number of the recording array.
- array_grid_col_num, array_grid_row_num (int):
- Number of columns / rows of the array.
- connector_type (str):
- Type of connector used for recording.
- rec_pauses (bool):
- True if the session contains a recording pause (i.e.,
- multiple segments).
- Segment annotations:
- condition (int):
- Condition code (describing the set of trial types
- presented to the subject) of this segment. For a
- mapping of condition codes to trial types, see the
- condition_str attribute of the ReachGraspIO class.
- ChannelIndex annotations:
- connector_aligned_id (int):
- Connector-aligned channel ID from which the spikes were
- loaded. This is a channel ID between 1 and 100 that is
- related to the location of an electrode on the Utah
- array and thus common across different arrays
- (independent of the Blackrock channel ID). The ID
- considers a top-view of the array with the connector
- wires extruding to the right. Electrodes are then
- numbered from bottom left to top right:
- 91 92 ... 99 100 \
- 81 82 ... 89 90 \
- ... ... --- Connector Wires
- 11 12 ... 19 20 /
- 1 2 ... 9 10 /
- Note: The Blackrock IDs are given in the 'channel_ids'
- property of the ChannelIndex object.
- waveform_size (Quantitiy):
- Length of time used to save spike waveforms (in units
- of 1/30000 s).
- nev_hi_freq_corner (Quantitiy),
- nev_lo_freq_corner (Quantitiy),
- nev_hi_freq_order (int), nev_lo_freq_order (int),
- nev_hi_freq_type (str), nev_lo_freq_type (str),
- nev_hi_threshold, nev_lo_threshold,
- nev_energy_threshold (Quantity):
- Indicates parameters of spike detection.
- nev_dig_factor (int):
- Digitization factor in microvolts of the nev file, used
- to convert raw samples to volt.
- connector_ID, connector_pinID (int):
- ID of connector and pin on the connector where the
- channel was recorded from.
- nb_sorted_units (int):
- Number of sorted units on this channel (noise, mua and
- sua).
- electrode_reject_XXX (bool):
- For different filter ranges XXX (as defined in the odML
- file), if this variable is True it indicates whether
- the spikes were recorded on an electrode that should be
- rejected based on preprocessing analysis for removing
- electrodes due to noise/artefacts in the respective
- frequency range.
- Unit annotations:
- coordinates (Quantity):
- Contains the x and y coordinate of the electrode in mm
- (spacing: 0.4mm). The coordinates use the same
- representation as the connector_aligned_id with the
- origin located at the bottom left electrode. Thus,
- e.g., connector aligned ID 14 is at coordinates:
- (1.2 mm, 0.4 mm)
- unit_id (int):
- ID of the unit.
- channel_id (int):
- Channel ID (Blackrock ID) from which the unit was
- loaded (equiv. to the single list entry in the
- attribute channel_ids of ChannelIndex parent).
- connector_aligned_id (int):
- Connector-aligned channel ID from which the unit was
- loaded. This is a channel ID between 1 and 100 that is
- related to the location of an electrode on the Utah
- array and thus common across different arrays
- (independent of the Blackrock channel ID). The ID
- considers a top-view of the array with the connector
- wires extruding to the right. Electrodes are then
- numbered from bottom left to top right:
- 91 92 ... 99 100 \
- 81 82 ... 89 90 \
- ... ... --- Connector Wires
- 11 12 ... 19 20 /
- 1 2 ... 9 10 /
- electrode_reject_XXX (bool):
- For different filter ranges XXX (as defined in the odML
- file), if this variable is True it indicates whether
- the spikes were recorded on an electrode that should be
- rejected based on preprocessing analysis for removing
- electrodes due to noise/artefacts in the respective
- frequency range.
- noise, mua, sua (bool):
- True, if the unit is classified as a noise unit, i.e.,
- not considered neural activity (noise), a multi-unit
- (mua), or a single unit (sua).
- SNR (float):
- Signal to noise ratio of SUA/MUA waveforms. A higher
- value indicates that the unit could be better
- distinguished in the spike detection and spike sorting
- procedure.
- spike_duration (float):
- Approximate duration of the spikes of SUAs/MUAs in
- microseconds.
- spike_amplitude (float):
- Maximum amplitude of the spike waveform.
- spike_count (int):
- Number of spikes sorted into this unit.
- AnalogSignal annotations:
- nsx (int):
- nsX file the signal was loaded from, e.g., 5 indicates
- the .ns5 file.
- channel_id (int):
- Channel ID (Blackrock ID) from which the signal was
- loaded.
- connector_aligned_id (int):
- Connector-aligned channel ID from which the signal was
- loaded. This is a channel ID between 1 and 100 that is
- related to the location of an electrode on the Utah
- array and thus common across different arrays
- (independent of the Blackrock channel ID). The ID
- considers a top-view of the array with the connector
- wires extruding to the right. Electrodes are then
- numbered from bottom left to top right:
- 91 92 ... 99 100 \
- 81 82 ... 89 90 \
- ... ... --- Connector Wires
- 11 12 ... 19 20 /
- 1 2 ... 9 10 /
- electrode_reject_XXX (bool):
- For different filter ranges XXX (as defined in the odML
- file), if this variable is True it indicates whether
- the spikes were recorded on an electrode that should be
- rejected based on preprocessing analysis for removing
- electrodes due to noise/artefacts in the respective
- frequency range.
- filter_shift_correction (Quantity):
- If the parameter correct_filter_shift is True, and a
- shift estimate was found in the odML, this annotation
- indicates the amount of time by which the signal was
- shifted. I.e., adding this number to t_start will
- result in the uncorrected, originally recorded time
- axis.
- Spiketrain annotations:
- unit_id (int):
- ID of the unit from which the spikes were recorded.
- channel_id (int):
- Channel ID (Blackrock ID) from which the spikes were
- loaded.
- connector_aligned_id (int):
- Connector-aligned channel ID from which the spikes were
- loaded. This is a channel ID between 1 and 100 that is
- related to the location of an electrode on the Utah
- array and thus common across different arrays
- (independent of the Blackrock channel ID). The ID
- considers a top-view of the array with the connector
- wires extruding to the right. Electrodes are then
- numbered from bottom left to top right:
- 91 92 ... 99 100 \
- 81 82 ... 89 90 \
- ... ... --- Connector Wires
- 11 12 ... 19 20 /
- 1 2 ... 9 10 /
- electrode_reject_XXX (bool):
- For different filter ranges XXX (as defined in the odML
- file), if this variable is True it indicates whether
- the spikes were recorded on an electrode that should be
- rejected based on preprocessing analysis for removing
- electrodes due to noise/artefacts in the respective
- frequency range.
- noise, mua, sua (bool):
- True, if the unit is classified as a noise unit, i.e.,
- not considered neural activity (noise), a multi-unit
- (mua), or a single unit (sua).
- SNR (float):
- Signal to noise ratio of SUA/MUA waveforms. A higher
- value indicates that the unit could be better
- distinguished in the spike detection and spike sorting
- procedure.
- spike_duration (float):
- Approximate duration of the spikes of SUAs/MUAs in
- microseconds.
- spike_amplitude (float):
- Maximum amplitude of the spike waveform.
- spike_count (int):
- Number of spikes sorted into this unit.
- Event annotations:
- The resulting Block contains three Event objects with the
- following names:
- "DigitalTrialEvents' contains all digitally recorded events
- returned by BlackrockIO, annotated with semantic labels
- in accordance with the reach-to-grasp experiment (e.g.,
- 'TS-ON').
- 'AnalogTrialEvents' contains events extracted from the
- analog behavioral signals during preprocessing and
- stored in the odML (e.g., 'OT').
- 'TrialEvents' contains all events of DigitalTrialEvents and
- AnalogTrialEvents merged into a single Neo object.
- Each annotation is a list containing one entry per time
- point stored in the event.
- trial_event_labels (list of str):
- Name identifying the name of the event, e.g., 'TS-ON'.
- trial_id (list of int):
- Trial ID the event belongs to.
- trial_timestamp_id (list of int):
- Timestamp-based trial ID (equivalent to the time of TS-
- ON of a trial) the event belongs to.
- belongs_to_trialtype (str):
- String identifying the trial type (e.g., SGHF) the
- trial belongs to.
- performance_in_trial (list of int):
- Performance code of the trial that the event belongs
- to. Compare to the performance_codes and
- performance_str attributes of ReachGraspIO class.
- trial_reject_XXX:
- For different filter ranges XXX (defined in the odML
- file), if True this variable indicates whether the
- trial was rejected based on preprocessing analysis.
- """
- if not name:
- name = 'Reachgrasp Recording Data Block'
- if not description:
- description = \
- "Block of reach-to-grasp project data from Blackrock file set."
- # Load neo block
- bl = BlackrockIO.read_block(
- self, index=index, name=name, description=description,
- nsx_to_load=nsx_to_load, n_starts=n_starts, n_stops=n_stops,
- channels=channels, units=units, load_waveforms=load_waveforms,
- load_events=load_events, scaling=scaling, lazy=lazy,
- cascade=cascade)
- bl.annotate(conditions=[])
- for seg in bl.segments:
- if load_events and not lazy:
- if 'condition' in list(seg.annotations):
- bl.annotations['conditions'].append(
- seg.annotations['condition'])
- if self.odmldoc:
- self.__annotate_block_with_odml(bl)
- for chidx in bl.channel_indexes:
- self.__annotate_channelindex_with_odml(chidx)
- self.__annotate_units_with_odml(chidx.units)
- return bl
- def read_segment(
- self, n_start, n_stop, name=None, description=None, index=None,
- nsx_to_load='none', channels=range(1, 97), units='none',
- load_waveforms=False, load_events=False, scaling='raw',
- correct_filter_shifts=True, lazy=False, cascade=True):
- """
- Reads file contents as a Neo Block.
- The Block contains one Segment for each entry in zip(n_starts,
- n_stops). If these parameters are not specified, the default is
- to store all data in one Segment.
- The Block contains one ChannelIndex per channel.
- Args:
- n_start (Quantity):
- Start time of maximum time range of signals contained in this
- segment.
- n_stop (Quantity):
- Stop time of maximum time range of signals contained in this
- segment.
- name (None, string):
- If None, name is set to default, otherwise it is set to user
- input.
- description (None, string):
- If None, description is set to default, otherwise it is set to
- user input.
- index (None, int):
- If not None, index of segment is set to user index.
- nsx_to_load (int, list, str):
- ID(s) of nsx file(s) from which to load data, e.g., if set to
- 5 only data from the ns5 file are loaded. If 'none' or empty
- list, no nsx files and therefore no analog signals are loaded.
- If 'all', data from all available nsx are loaded.
- channels (int, list, str):
- Channel id(s) from which to load data. If 'none' or empty list,
- no channels and therefore no analog signal or spiketrains are
- loaded. If 'all', all available channels are loaded. By
- default, all neural channels (1-96) are loaded.
- units (int, list, str, dict):
- ID(s) of unit(s) to load. If 'none' or empty list, no units and
- therefore no spiketrains are loaded. If 'all', all available
- units are loaded. If dict, the above can be specified
- individually for each channel (keys), e.g. {1: 5, 2: 'all'}
- loads unit 5 from channel 1 and all units from channel 2.
- load_waveforms (boolean):
- If True, waveforms are attached to all loaded spiketrains.
- load_events (boolean):
- If True, all recorded events are loaded.
- scaling (str):
- Determines whether time series of individual
- electrodes/channels are returned as AnalogSignals containing
- raw integer samples ('raw'), or scaled to arrays of floats
- representing voltage ('voltage'). Note that for file
- specification 2.1 and lower, the option 'voltage' requires a
- nev file to be present.
- correct_filter_shifts (bool):
- If True, shifts of the online-filtered neural signals (e.g.,
- ns2, channels 1-128) are corrected by time-shifting the signal
- by a heuristically determined estimate stored in the metadata,
- in the property EstimatedShift, under the path
- /Cerebus/NeuralSignalProcessor/NeuralSignals/Filter_nsX/
- lazy (boolean):
- If True, only the shape of the data is loaded.
- cascade (boolean):
- If True, only the segment without children is returned.
- Returns:
- Segment (neo.segment.Segment):
- Segment linking to all loaded Neo objects. See documentation of
- read_block() for a full list of annotations per Neo object.
- """
- # Load neo block
- seg = BlackrockIO.read_segment(
- self, n_start, n_stop, name=name, description=description,
- index=index, nsx_to_load=nsx_to_load, channels=channels,
- units=units, load_waveforms=load_waveforms,
- load_events=load_events, scaling=scaling, lazy=lazy,
- cascade=cascade)
- for asig in seg.analogsignals:
- self.__annotate_analogsignals_with_odml(asig)
- if correct_filter_shifts:
- self.__correct_filter_shifts(asig)
- if load_events and not lazy:
- for ev in seg.events:
- # Modify digital trial events to include semantic event
- # informations
- if ev.name == 'digital_input_port':
- self.__annotate_dig_trial_events(ev)
- self.__add_rejection_to_event(ev)
- cnd = self.__extract_task_condition(
- ev.annotations['belongs_to_trialtype'])
- seg.annotate(condition=cnd)
- # If digital trial events exist, extract analog events from odML
- # and create one common event array
- if len(seg.events) > 0 and self.odmldoc:
- analog_event = self.__extract_analog_events_from_odml(
- seg.t_start, seg.t_stop)
- self.__add_rejection_to_event(analog_event)
- seg.events.append(analog_event)
- merged_event = self.__merge_digital_analog_events(
- seg.events)
- self.__add_rejection_to_event(merged_event)
- seg.events.append(merged_event)
- return seg
- if __name__ == '__main__':
- pass
|