def load_acq(filename, chtrig=0): """ Populate object phys_input from acq extension files. Parameters ---------- filename : str path to the txt labchart file chtrig : int, optional index of trigger channel. Default is 0. Returns ------- BlueprintInput Note ---- chtrig is not a 0-based Python index - instead, it's human readable (i.e., 1-based). This is handy because, when initialising the class, a new channel corresponding to time is added at the beginning - that is already taken into account! See Also -------- physio_obj.BlueprintInput """ from bioread import read_file with warnings.catch_warnings(): warnings.filterwarnings('ignore', category=DeprecationWarning) data = read_file(filename).channels freq = [ data[0].samples_per_second, ] timeseries = [ data[0].time_index, ] units = [ 's', ] names = [ 'time', ] for k, ch in enumerate(data): LGR.info(f'{k:02d}. {ch}') timeseries.append(ch.data) freq.append(ch.samples_per_second) units.append(ch.units) names.append(ch.name) return BlueprintInput(timeseries, freq, names, units, chtrig)
def populate_phys_input(filename, chtrig): """ Populate object phys_input from acq files. Parameters ---------- filename: str path to the txt labchart file chtrig : int index of trigger channel Returns ------- BlueprintInput See Also -------- physio_obj.BlueprintInput """ with warnings.catch_warnings(): warnings.filterwarnings('ignore', category=DeprecationWarning) data = read_file(filename).channels freq = [ data[chtrig].samples_per_second, ] timeseries = [ data[chtrig].time_index, ] units = [ 's', ] names = [ 'time', ] for k, ch in enumerate(data): LGR.info(f'{k:02d}. {ch}') timeseries.append(ch.data) freq.append(ch.samples_per_second) units.append(ch.units) names.append(ch.name) return BlueprintInput(timeseries, freq, names, units)
def populate_phys_input(filename, chtrig): """ Populate object phys_input """ data = read_file(filename).channels freq = [data[chtrig].samples_per_second] * 2 timeseries = [data[chtrig].time_index, data[chtrig].data] units = ['s', data[chtrig].units] names = ['time', 'trigger'] for k, ch in enumerate(data): if k != chtrig: print(f'{k:02d}. {ch}') timeseries.append(ch.data) freq.append(ch.samples_per_second) units.append(ch.units) names.append(ch.name) return BlueprintInput(timeseries, freq, names, units)
def process_labchart(channel_list, chtrig, header=[]): """ Process labchart header and channel_list and make a physio_obj.BlueprintInput. Parameters ---------- channel_list: list list with channels only chtrig : int index of trigger channel, starting in 1 for human readability header: list list with that contains file header Returns ------- BlueprintInput Raises ------ ValueError If len(header) == 0 and therefore there is no header If sampling is not in ['hr', 'min', 's', 'ms', 'µs'] reference: https://www.adinstruments.com/support/knowledge-base/how-can-channel-titles-ranges-intervals-etc-text-file-be-imported-labchart See Also -------- physio_obj.BlueprintInput """ # get frequency # check header has some length if len(header) == 0: raise AttributeError('Files without header are not supported yet') interval = header[0][1].split(" ") # check the interval is in some of the correct labchart units if interval[-1] not in ['hr', 'min', 's', 'ms', 'µs']: raise AttributeError( f'Interval unit "{interval[-1]}" is not in a valid LabChart' 'time unit, this probably means your file is not in Labchart format' ) # check if interval is in seconds, if not change the units to seconds if interval[-1] != 's': LGR.warning('Interval is not in seconds. Converting its value.') if interval[-1] == 'hr': interval[0] = float(interval[0]) * 3600 interval[-1] = 's' elif interval[-1] == 'min': interval[0] = float(interval[0]) * 60 interval[-1] = 's' elif interval[-1] == 'ms': interval[0] = float(interval[0]) / 1000 interval[-1] = 's' elif interval[-1] == 'µs': interval[0] = float(interval[0]) / 1000000 interval[-1] = 's' else: interval[0] = float(interval[0]) # get units range_list = header[5][1:] orig_units = [] for item in range_list: orig_units.append(item.split(' ')[1]) units = [ 's', ] # get names orig_names = header[4][1:] orig_names_len = len(orig_names) names = [ 'time', ] # get channels # this transposes the channel_list from a list of samples x channels to # a list of channels x samples timeseries = list(map(list, zip(*channel_list))) freq = [1 / interval[0]] * len(timeseries) timeseries = [np.array(darray) for darray in timeseries] # check the file has a time channel if not create it and add it # As the "time" doesn't have a column header, if the number of header names # is less than the number of timesieries, then "time" is column 0... # ...otherwise, create the time channel if not (orig_names_len < len(timeseries)): duration = (timeseries[0].shape[0] + 1) * interval[0] t_ch = np.ogrid[0:duration:interval[0]][:-1] # create time channel timeseries = [ t_ch, ] + timeseries names = names + orig_names units = units + orig_units freq = [1 / interval[0]] * len(timeseries) freq = check_multifreq(timeseries, freq) return BlueprintInput(timeseries, freq, names, units, chtrig)
def process_acq(channel_list, chtrig, header=[]): """ Process AcqKnowledge header and channel_list to make a physio_obj.BlueprintInput. Parameters ---------- channel_list: list list with channels only chtrig : int index of trigger channel, starting in 1 for human readability header: list list with that contains file header Returns ------- BlueprintInput Raises ------ ValueError If len(header) == 0 and therefore there is no header If sampling is not in ['min', 'sec', 'µsec', 'msec','MHz', 'kHz', 'Hz'] reference: https://www.biopac.com/wp-content/uploads/acqknowledge_software_guide.pdf page 194 See Also -------- physio_obj.BlueprintInput """ # check header is not empty if len(header) == 0: raise AttributeError('Files without header are not supported yet') header.append(channel_list[0]) del channel_list[0] # delete sample size from channel list # this transposes the channel_list from a list of samples x channels to # a list of channels x samples timeseries = list(map(list, zip(*channel_list))) interval = header[1][0].split() # check the interval is in some of the correct AcqKnowledge units if interval[-1].split('/')[0] not in [ 'min', 'sec', 'µsec', 'msec', 'MHz', 'kHz', 'Hz' ]: raise AttributeError( f'Interval unit "{interval[-1]}" is not in a ' 'valid AcqKnowledge format time unit, this probably' 'means your file is not in min, sec, msec, µsec, Mhz, KHz or Hz') interval[-1] = interval[-1].split('/')[0] # Check if the header is in frequency or sampling interval if 'Hz' in interval[-1].split('/')[0]: print('frequency is given in the header, calculating sample Interval' ' and standarizing to Hz if needed') freq = float(interval[0]) freq_unit = interval[-1] if freq_unit == 'MHz': freq = freq * (1000000) elif freq_unit == 'kHz': freq = freq * 1000 interval[0] = 1 / freq freq = [freq] * (len(timeseries) + 1) else: # check if interval is in seconds, if not change the units to seconds and # calculate frequency if interval[-1].split('/')[0] != 'sec': LGR.warning('Interval is not in seconds. Converting its value.') if interval[-1].split('/')[0] == 'min': interval[0] = float(interval[0]) * 60 interval[-1] = 's' elif interval[-1].split('/')[0] == 'msec': interval[0] = float(interval[0]) / 1000 interval[-1] = 's' elif interval[-1].split('/')[0] == 'µsec': interval[0] = float(interval[0]) / 1000000 interval[-1] = 's' else: interval[0] = float(interval[0]) interval[-1] = 's' freq = [1 / interval[0]] * (len(timeseries) + 1) # get units and names orig_units = [] orig_names = [] # the for loop starts at index1 at 3 because that's the first line of the header # with channel name info and ends in 2 + twice the number of channels because # that should be the last channel name for index1 in range(3, 3 + len(header[-1]) * 2, 2): orig_names.append(header[index1][0]) # since units are in the line imediately after we get the units at the same time orig_units.append(header[index1 + 1][0]) # reorder channels names names = [ 'time', ] names = names + orig_names # reoder channels units units = [ 's', ] units = units + orig_units # get channels timeseries = [np.array(darray) for darray in timeseries] duration = (timeseries[0].shape[0] + 1) * interval[0] t_ch = np.ogrid[0:duration:interval[0]][:-1] # create time channel timeseries = [ t_ch, ] + timeseries freq = check_multifreq(timeseries, freq) return BlueprintInput(timeseries, freq, names, units, chtrig)
def generate_blueprint(channel_list, chtrig, interval, orig_units, orig_names): """ Standarize channel_list, chtrig interval orig_units and orig_names. Standarize channel_list, chtrig interval orig_units and orig_names in the correct units and format and generate a physio_obj.BlueprintInput object. Parameters ---------- channel_list : list of strings list with channels only chtrig : int index of trigger channel, starting in 1 for human readability interval : list of strings maximum sampling frequency or interval value and unit for the recording. Example: ["400", "Hz"] orig_units : list of strings contains original channels units orig_names : list of strings contains original channels name Returns ------- BlueprintInput Raises ------ AttributeError If sampling is not in ['min', 'sec', 'µsec', 'msec', 'MHz', 'kHz', 'Hz', 'hr', 'min', 's', 'ms', 'µs'] reference: https://www.adinstruments.com/support/knowledge-base/how-can-channel-titles-ranges-intervals-etc-text-file-be-imported-labchart https://www.biopac.com/wp-content/uploads/acqknowledge_software_guide.pdf page 194 See Also -------- physio_obj.BlueprintInput """ # this transposes the channel_list from a list of samples x channels to # a list of channels x samples timeseries = list(map(list, zip(*channel_list))) if interval[-1] not in [ 'min', 'sec', 'µsec', 'msec', 'MHz', 'kHz', 'Hz', 'hr', 'min', 's', 'ms', 'µs' ]: raise AttributeError( f'Interval unit "{interval[-1]}" is not in a ' 'valid frequency or time unit format, this probably ' 'means your file is not in min, sec, msec, µsec, hr, min, s, ms, µs, ' 'Mhz, KHz or Hz') # Check if the header is in frequency or sampling interval if 'Hz' in interval[-1]: LGR.info( 'Retrieving frequency from file header, calculating sample interval, ' 'and standarizing to Hz if needed') freq = float(interval[0]) freq_unit = interval[-1] if freq_unit == 'MHz': freq = freq * (1000000) elif freq_unit == 'kHz': freq = freq * 1000 interval[0] = 1 / freq freq = [freq] * len(timeseries) else: # check if interval is in seconds, if not change the units to seconds and # calculate frequency if interval[-1] not in ('s', 'sec'): LGR.warning('Sampling interval not expressed in seconds. ' 'Converting its value and unit.') if interval[-1] == 'min': interval[0] = float(interval[0]) * 60 elif interval[-1] == 'msec': interval[0] = float(interval[0]) / 1000 elif interval[-1] == 'µsec': interval[0] = float(interval[0]) / 1000000 elif interval[-1] == 'hr': interval[0] = float(interval[0]) * 3600 elif interval[-1] == 'ms': interval[0] = float(interval[0]) / 1000 elif interval[-1] == 'µs': interval[0] = float(interval[0]) / 1000000 else: interval[0] = float(interval[0]) # get frequency freq = [1 / interval[0]] * len(timeseries) # reorder channels names names = [ 'time', ] names = names + orig_names # reoder channels units units = [ 's', ] units = units + orig_units timeseries = list(map(list, zip(*channel_list))) freq = [1 / interval[0]] * len(timeseries) timeseries = [np.array(darray) for darray in timeseries] # Check if the file has a time channel, otherwise create it. # As the "time" doesn't have a column header, if the number of header names # is less than the number of timeseries, then "time" is column 0... # ...otherwise, create the time channel if not (len(orig_names) < len(timeseries)): duration = (timeseries[0].shape[0] + 1) * interval[0] t_ch = np.ogrid[0:duration:interval[0]][:-1] # create time channel timeseries = [ t_ch, ] + timeseries freq = [max(freq)] + freq freq = check_multifreq(timeseries, freq) return BlueprintInput(timeseries, freq, names, units, chtrig)
def load_mat(filename, chtrig=0): """ Populate object phys_input from MATLAB files. Parameters ---------- filename: str path to the txt labchart file chtrig : int index of trigger channel. !!! ATTENTION: IT'S MEANT TO REPRESENT AN INDEX STARTING FROM 1 !!! Returns ------- BlueprintInput Note ---- chtrig is not a 0-based Python index - instead, it's human readable (i.e., 1-based). This is handy because, when initialising the class, a new channel corresponding to time is added at the beginning - that is already taken into account! See Also -------- physio_obj.BlueprintInput """ # Load MATLAB file into dictionary. from pymatreader import read_mat mat_dict = read_mat(filename) if '__header__' in mat_dict: orig_names = list(mat_dict['labels']) orig_units = list(mat_dict['units']) interval = [mat_dict['isi'], mat_dict['isi_units']] channel_list = mat_dict['data'] return generate_blueprint(channel_list, chtrig, interval, orig_units, orig_names) else: # Convert data into 1d numpy array for easier indexing. data = np.squeeze(np.asarray(mat_dict['data'])) # Extract number of channels and tick rate. n_channels = len(mat_dict['titles']) t_freq = mat_dict['tickrate'] # Stores MATLAB data into lists. timeseries = [] freq = [ t_freq, ] units = [ 's', ] names = [ 'time', ] for ch in range(n_channels): units.append(mat_dict['unittext'][int(mat_dict['unittextmap'][ch] - 1)].strip()) names.append(mat_dict['titles'][ch].strip()) freq.append(mat_dict['samplerate'][ch]) idx_start = int(mat_dict['datastart'][ch]) idx_end = int(mat_dict['dataend'][ch]) timeseries.append(data[idx_start:idx_end]) # Calculate duration based on frequency and create time channel. interval = 1 / t_freq duration = (timeseries[0].shape[0] + 1) * interval t_ch = np.ogrid[0:duration:interval][:-1] timeseries = [ t_ch, ] + timeseries return BlueprintInput(timeseries, freq, names, units, chtrig)
def populate_phys_input(filename, chtrig): """ Populate object phys_input for now this works only with labchart files Input (Properties) ------------------ filename: str path to the txt labchart file chtrig : int index of trigger channel Output ------------------ BlueprintInput object for more see BlueprintInput docs """ header = [] channel_list = [] with open(filename, 'r') as f: header_l = 0 for line in f: line = line.rstrip('\n').split('\t') try: float(line[0]) except ValueError: header.append(line) continue line = [float(i) for i in line] channel_list.append(line) # get frequency interval = header[0][1].split(" ") if interval[-1] not in ['hr', 'min', 's', 'ms', 'µs']: raise AttributeError( f'Interval unit "{interval[-1]}" is not in a valid LabChart time unit, ' 'this probably means your file is not in labchart format') if interval[-1] != 's': print('Interval is not in seconds. Converting its value.') if interval[-1] == 'hr': interval[0] = float(interval)[0] * 3600 interval[-1] = 's' elif interval[-1] == 'min': interval[0] = float(interval[0]) * 60 interval[-1] = 's' elif interval[-1] == 'ms': interval[0] = float(interval[0]) / 1000 interval[-1] = 's' elif interval[-1] == 'µs': interval[0] = float(interval[0]) / 1000000 interval[-1] = 's' else: interval[0] = float(interval[0]) # get units range_list = header[5][1:] units = [] for item in range_list: units.append(item.split(' ')[1]) # get names orig_names = header[4][1:] names = ['time', 'trigger'] orig_names.pop(chtrig) names = names + orig_names # get channels timeseries = np.matrix(channel_list).T.tolist() freq = [1 / interval[0]] * len(timeseries) timeseries = [np.array(darray) for darray in timeseries] ordered_timeseries = [timeseries[0], timeseries[chtrig]] timeseries.pop(chtrig) timeseries.pop(0) ordered_timeseries = ordered_timeseries + timeseries return BlueprintInput(ordered_timeseries, freq, names, units)