Source code for ipfx.dataset.labnotebook

import h5py
import math
from ipfx.string_utils import to_str

[docs]class LabNotebookReader(object): """ The Lab Notebook Reader class This class reads two sections: numeric data and text data """ def __init__(self): self.register_enabled_names()
[docs] def register_enabled_names(self): """ register_enabled_names: mapping of notebook keys to keys representing if that value is enabled move this to subclasses if/when key names diverge """ self.enabled = {} self.enabled["V-Clamp Holding Level"] = "V-Clamp Holding Enable" self.enabled["RsComp Bandwidth"] = "RsComp Enable" self.enabled["RsComp Correction"] = "RsComp Enable" self.enabled["RsComp Prediction"] = "RsComp Enable" self.enabled["Whole Cell Comp Cap"] = "Whole Cell Comp Enable" self.enabled["Whole Cell Comp Resist"] = "Whole Cell Comp Enable" self.enabled["I-Clamp Holding Level"] = "I-Clamp Holding Enable" self.enabled["Neut Cap Value"] = "Neut Cap Enable" self.enabled["Bridge Bal Value"] = "Bridge Bal Enable"
[docs] def get_numeric_value(self, name, data_col, sweep_col, enable_col, sweep_num, default_val): """ fetch numeric data val_number has 3 dimensions -- the first has a shape of (#fields * 9). there are many hundreds of elements in this dimension. they look to represent the full array of values (for each field for each multipatch) for a given point in time, and thus given sweep according to Thomas Braun (igor nwb dev), the first 8 pages are for headstage data, and the 9th is for headstage-independent data Parameters ---------- name: str data_col: int sweep_col: int enable_col: int sweep_num: int default_val: dict Returns ------- last non-empty entry in specified column for specified sweep number """ data = self.val_number return_val = default_val for sample in data: swp = sample[sweep_col][0] if math.isnan(swp): continue if int(swp) == sweep_num: if enable_col is not None and sample[enable_col][0] != 1.0: continue # 'enable' flag present and it's turned off val = sample[data_col][0] if not math.isnan(val): return_val = val return return_val
[docs] def get_text_value(self, name, data_col, sweep_col, enable_col, sweep_num, default_val): """ fetch text data Parameters ---------- name: str data_col: int sweep_col: int enable_col: int sweep_num: int default_val: dict Returns ------- last non-empty entry in specified column for specified sweep number """ data = self.val_text return_val = default_val for sample in data: swp = sample[sweep_col][0] if len(swp) == 0: continue if int(swp) == int(sweep_num): if enable_col is not None: print("Error: Enable flag not expected for text values") val = sample[data_col][0] if len(val) > 0: return_val = val return return_val
[docs] def get_value(self, name, sweep_num, default_val): """ looks for key in lab notebook and returns the value associated with the specified sweep, or the default value if no value is found (NaN and empty strings are considered to be non-values) name_number has 3 dimensions -- the first has shape (#fields * 9) and stores the key names. the second looks to store units for those keys. The third is numeric text but it's role isn't clear val_number has 3 dimensions -- the first has a shape of (#fields * 9). there are many hundreds of elements in this dimension. they look to represent the full array of values (for each field for each multipatch) for a given point in time, and thus given sweep Parameters ---------- name: str sweep_num: int default_val: dict Returns ------- values obtained from lab notebook """ numeric_fields = [to_str(c) for c in self.colname_number[0]] text_fields = [to_str(c) for c in self.colname_text[0]] if name in numeric_fields: sweep_idx = numeric_fields.index("SweepNum") enable_idx = None if name in self.enabled: enable_col = self.enabled[name] enable_idx = numeric_fields.index(enable_col) field_idx = numeric_fields.index(name) return self.get_numeric_value(name, field_idx, sweep_idx, enable_idx, sweep_num, default_val) elif name in text_fields: if "Sweep #" in text_fields: sweep_idx = text_fields.index("Sweep #") else: sweep_idx = text_fields.index("SweepNum") enable_idx = None if name in self.enabled: enable_col = self.enabled[name] enable_idx = text_fields.index(enable_col) field_idx = text_fields.index(name) return self.get_text_value(name, field_idx, sweep_idx, enable_idx, sweep_num, default_val) else: return default_val
[docs]class LabNotebookReaderIgorNwb(LabNotebookReader): """ LabNotebookReaderIgorNwb: Loads lab notebook data out of an Igor-generated NWB file. Module input is the name of the nwb file. Notebook data can be read through get_value() function """ def __init__(self, nwb_file): LabNotebookReader.__init__(self) h5 = h5py.File(nwb_file, "r") # check NWB version and whether labnotebook exist for k in h5["general/labnotebook"]: notebook = h5["general/labnotebook"][k] break # load column data into memory self.val_text = notebook["textualValues"][()] self.colname_text = notebook["textualKeys"][()] self.val_number = notebook["numericalValues"][()] self.colname_number = notebook["numericalKeys"][()] h5.close() self.register_enabled_names()
[docs] def get_textual_values(self): return self.val_text
[docs] def get_textual_keys(self): return self.colname_text
[docs] def get_numerical_values(self): return self.val_number
[docs] def get_numerical_keys(self): return self.colname_number