def test_dataselection(self): dummy = AnalogData(data=self.data, trialdefinition=self.trl, samplerate=self.samplerate) trialSelections = [ "all", # enforce below selections in all trials of `dummy` [3, 1] # minimally unordered ] chanSelections = [ ["channel03", "channel01", "channel01", "channel02"], # string selection w/repetition + unordered [4, 2, 2, 5, 5], # repetition + unorderd range(5, 8), # narrow range slice(-2, None) # negative-start slice ] toiSelections = [ "all", # non-type-conform string [0.6], # single inexact match [-0.2, 0.6, 0.9, 1.1, 1.3, 1.6, 1.8, 2.2, 2.45, 3.] # unordered, inexact, repetions ] toilimSelections = [ [0.5, 1.5], # regular range [1.5, 2.0], # minimal range (just two-time points) [1.0, np.inf] # unbounded from above ] timeSelections = list(zip(["toi"] * len(toiSelections), toiSelections)) \ + list(zip(["toilim"] * len(toilimSelections), toilimSelections)) idx = [slice(None)] * len(dummy.dimord) timeIdx = dummy.dimord.index("time") chanIdx = dummy.dimord.index("channel") for trialSel in trialSelections: for chanSel in chanSelections: for timeSel in timeSelections: kwdict = {} kwdict["trials"] = trialSel kwdict["channels"] = chanSel kwdict[timeSel[0]] = timeSel[1] cfg = StructDict(kwdict) # data selection via class-method + `Selector` instance for indexing selected = dummy.selectdata(**kwdict) selector = Selector(dummy, kwdict) idx[chanIdx] = selector.channel for tk, trialno in enumerate(selector.trials): idx[timeIdx] = selector.time[tk] assert np.array_equal( selected.trials[tk].squeeze(), dummy.trials[trialno][idx[0], :][:, idx[1]].squeeze()) cfg.data = dummy cfg.out = AnalogData(dimord=AnalogData._defaultDimord) # data selection via package function and `cfg`: ensure equality selectdata(cfg) assert np.array_equal(cfg.out.channel, selected.channel) assert np.array_equal(cfg.out.data, selected.data)
def test_dataselection(self): dummy = EventData(data=self.data, trialdefinition=self.trl, samplerate=2.0) # selections are chosen so that result is not empty trialSelections = [ "all", # enforce below selections in all trials of `dummy` [3, 1] # minimally unordered ] eventidSelections = [ [0, 0, 1], # preserve repetition, don't convert to slice range(0, 2), # narrow range slice(-2, None) # negative-start slice ] toiSelections = [ "all", # non-type-conform string [-0.2, 0.6, 0.9, 1.1, 1.3, 1.6, 1.8, 2.2, 2.45, 3.] # unordered, inexact, repetions ] toilimSelections = [ [0.5, 3.5], # regular range [0.0, np.inf] # unbounded from above ] timeSelections = list(zip(["toi"] * len(toiSelections), toiSelections)) \ + list(zip(["toilim"] * len(toilimSelections), toilimSelections)) eventidIdx = dummy.dimord.index("eventid") for trialSel in trialSelections: for eventidSel in eventidSelections: for timeSel in timeSelections: kwdict = {} kwdict["trials"] = trialSel kwdict["eventids"] = eventidSel kwdict[timeSel[0]] = timeSel[1] cfg = StructDict(kwdict) # data selection via class-method + `Selector` instance for indexing selected = dummy.selectdata(**kwdict) selector = Selector(dummy, kwdict) tk = 0 for trialno in selector.trials: if selector.time[tk]: assert np.array_equal( dummy.trials[trialno][selector.time[tk], :], selected.trials[tk]) tk += 1 assert np.array_equal( selected.eventid, dummy.eventid[np.unique( selected.data[:, eventidIdx]).astype(np.intp)]) cfg.data = dummy cfg.out = EventData(dimord=EventData._defaultDimord) # data selection via package function and `cfg`: ensure equality selectdata(cfg) assert np.array_equal(cfg.out.eventid, selected.eventid) assert np.array_equal(cfg.out.data, selected.data)
def test_validcallstyles(self): # data positional fname, = group_objects(self.data) assert fname == self.data.filename # data as keyword fname, = group_objects(data=self.data) assert fname == self.data.filename # data in cfg cfg = StructDict() cfg.data = self.data fname, = group_objects(cfg) assert fname == self.data.filename # 1. data positional, 2. cfg positional cfg = StructDict() cfg.groupbychan = None fname, = group_objects(self.data, cfg) assert fname == self.data.filename # 1. cfg positional, 2. data positional fname, = group_objects(cfg, self.data) assert fname == self.data.filename # data positional, cfg as keyword fname, = group_objects(self.data, cfg=cfg) assert fname == self.data.filename # cfg positional, data as keyword fname, = group_objects(cfg, data=self.data) assert fname == self.data.filename # both keywords fname, = group_objects(cfg=cfg, data=self.data) assert fname == self.data.filename
def test_dataselection(self): dummy = SpikeData(data=self.data, trialdefinition=self.trl, samplerate=2.0) # selections are chosen so that result is not empty trialSelections = [ "all", # enforce below selections in all trials of `dummy` [3, 1] # minimally unordered ] chanSelections = [ ["channel03", "channel01", "channel01", "channel02"], # string selection w/repetition + unordered [4, 2, 2, 5, 5], # repetition + unorderd range(5, 8), # narrow range slice(-5, None) # negative-start slice ] toiSelections = [ "all", # non-type-conform string [-0.2, 0.6, 0.9, 1.1, 1.3, 1.6, 1.8, 2.2, 2.45, 3.] # unordered, inexact, repetions ] toilimSelections = [ [0.5, 3.5], # regular range [1.0, np.inf] # unbounded from above ] unitSelections = [ ["unit1", "unit1", "unit2", "unit3"], # preserve repetition [0, 0, 2, 3], # preserve repetition, don't convert to slice range(1, 4), # narrow range slice(-2, None) # negative-start slice ] timeSelections = list(zip(["toi"] * len(toiSelections), toiSelections)) \ + list(zip(["toilim"] * len(toilimSelections), toilimSelections)) chanIdx = dummy.dimord.index("channel") unitIdx = dummy.dimord.index("unit") chanArr = np.arange(dummy.channel.size) for trialSel in trialSelections: for chanSel in chanSelections: for unitSel in unitSelections: for timeSel in timeSelections: kwdict = {} kwdict["trials"] = trialSel kwdict["channels"] = chanSel kwdict["units"] = unitSel kwdict[timeSel[0]] = timeSel[1] cfg = StructDict(kwdict) # data selection via class-method + `Selector` instance for indexing selected = dummy.selectdata(**kwdict) selector = Selector(dummy, kwdict) tk = 0 for trialno in selector.trials: if selector.time[tk]: assert np.array_equal( dummy.trials[trialno][ selector.time[tk], :], selected.trials[tk]) tk += 1 assert set(selected.data[:, chanIdx]).issubset( chanArr[selector.channel]) assert set(selected.channel) == set( dummy.channel[selector.channel]) assert np.array_equal( selected.unit, dummy.unit[np.unique(selected.data[:, unitIdx])]) cfg.data = dummy cfg.out = SpikeData(dimord=SpikeData._defaultDimord) # data selection via package function and `cfg`: ensure equality selectdata(cfg) assert np.array_equal(cfg.out.channel, selected.channel) assert np.array_equal(cfg.out.unit, selected.unit) assert np.array_equal(cfg.out.data, selected.data)
def test_invalidcallstyles(self): # expected error messages errmsg1 = "expected Syncopy data object(s) provided either via " +\ "`cfg`/keyword or positional arguments, not both" errmsg2 = "expected Syncopy data object(s) provided either via `cfg` " +\ "or as keyword argument, not both" errmsg3 = "expected either 'data' or 'dataset' in `cfg`/keywords, not both" # ensure things break reliably for 'data' as well as 'dataset' for key in ["data", "dataset"]: # data + cfg w/data cfg = StructDict() cfg[key] = self.data with pytest.raises(SPYValueError) as exc: group_objects(self.data, cfg) assert errmsg1 in str(exc.value) # data as positional + kwarg with pytest.raises(SPYValueError) as exc: group_objects(self.data, data=self.data) assert errmsg1 in str(exc.value) with pytest.raises(SPYValueError) as exc: group_objects(self.data, dataset=self.data) assert errmsg1 in str(exc.value) # cfg w/data + kwarg + positional with pytest.raises(SPYValueError) as exc: group_objects(self.data, cfg, data=self.data) assert errmsg1 in str(exc.value) with pytest.raises(SPYValueError) as exc: group_objects(self.data, cfg, dataset=self.data) assert errmsg1 in str(exc.value) # cfg w/data + kwarg with pytest.raises(SPYValueError) as exc: group_objects(cfg, data=self.data) assert errmsg2 in str(exc.value) with pytest.raises(SPYValueError) as exc: group_objects(cfg, dataset=self.data) assert errmsg2 in str(exc.value) # cfg (no data) but double-whammied cfg = StructDict() cfg.groupbychan = None with pytest.raises(SPYValueError)as exc: group_objects(self.data, cfg, cfg=cfg) assert "expected `cfg` either as positional or keyword argument, not both" in str(exc.value) # keyword set via cfg and kwarg with pytest.raises(SPYValueError) as exc: group_objects(self.data, cfg, groupbychan="invalid") assert "'non-default value for groupbychan'; expected no keyword arguments" in str(exc.value) # both data and dataset in cfg/keywords cfg = StructDict() cfg.data = self.data cfg.dataset = self.data with pytest.raises(SPYValueError)as exc: group_objects(cfg) assert errmsg3 in str(exc.value) with pytest.raises(SPYValueError)as exc: group_objects(data=self.data, dataset=self.data) assert errmsg3 in str(exc.value) # data/dataset do not contain Syncopy object with pytest.raises(SPYError)as exc: group_objects(data="invalid") assert "`data` must be Syncopy data object(s)!" in str(exc.value) # cfg is not dict/StructDict with pytest.raises(SPYTypeError)as exc: group_objects(cfg="invalid") assert "Wrong type of cfg: expected dictionary-like" in str(exc.value) # no data input whatsoever with pytest.raises(SPYError)as exc: group_objects("invalid") assert "missing mandatory argument: `data`" in str(exc.value)
def test_varargin(self): # data positional allFnames = group_objects(*self.dataObjs) assert allFnames == [obj.filename for obj in self.dataObjs] # data in cfg cfg = StructDict() cfg.data = self.dataObjs fnameList = group_objects(cfg) assert allFnames == fnameList # group objects by single-letter "channels" in various ways for letter in ["L", "E", "I", "A"]: letterIdx = string.ascii_uppercase.index(letter) nOccurences = letterIdx + 1 # data positional + keyword to get "reference" groupList = group_objects(*self.dataObjs, groupbychan=letter) assert len(groupList) == nOccurences # 1. data positional, 2. cfg positional cfg = StructDict() cfg.groupbychan = letter fnameList = group_objects(*self.dataObjs, cfg) assert groupList == fnameList # 1. cfg positional, 2. data positional fnameList = group_objects(cfg, *self.dataObjs) assert groupList == fnameList # data positional, cfg as keyword fnameList = group_objects(*self.dataObjs, cfg=cfg) assert groupList == fnameList # cfg w/data + keyword cfg = StructDict() cfg.dataset = self.dataObjs cfg.groupbychan = letter fnameList = group_objects(cfg) assert groupList == fnameList # data positional + select keyword fnameList = group_objects(*self.dataObjs[:letterIdx + 1], select={"channels": [letter]}) assert groupList == fnameList # data positional + cfg w/select cfg = StructDict() cfg.select = {"channels": [letter]} fnameList = group_objects(*self.dataObjs[:letterIdx + 1], cfg) assert groupList == fnameList # cfg w/data + select cfg = StructDict() cfg.data = self.dataObjs[:letterIdx + 1] cfg.select = {"channels": [letter]} fnameList = group_objects(cfg) assert groupList == fnameList # invalid selection with pytest.raises(SPYValueError) as exc: group_objects(*self.dataObjs, select={"channels": ["Z"]}) assert "expected list/array of channel existing names or indices" in str(exc.value) # data does not only contain Syncopy objects cfg = StructDict() cfg.data = self.dataObjs + ["invalid"] with pytest.raises(SPYError)as exc: group_objects(cfg) assert "`data` must be Syncopy data object(s)!" in str(exc.value)
def wrapper_cfg(*args, **kwargs): # First, parse positional arguments for dict-type inputs (`k` counts the # no. of dicts provided) and convert tuple of positional args to list cfg = None k = 0 args = list(args) for argidx, arg in enumerate(args): if isinstance(arg, dict): cfgidx = argidx k += 1 # If a dict was found, assume it's a `cfg` dict and extract it from # the positional argument list; if more than one dict was found, abort if k == 1: cfg = args.pop(cfgidx) elif k > 1: raise SPYValueError( legal="single `cfg` input", varname="cfg", actual="{0:d} `cfg` objects in input arguments".format(k)) # Now parse provided keywords for `cfg` entry - if `cfg` was already # provided as positional argument, abort if kwargs.get("cfg") is not None: if cfg: lgl = "`cfg` either as positional or keyword argument, not both" raise SPYValueError(legal=lgl, varname="cfg") cfg = kwargs.pop("cfg") # If `cfg` was detected either in positional or keyword arguments, process it if cfg: # If `cfg` is not dict-like, abort (`StructDict` is a `dict` child) if not isinstance(cfg, dict): raise SPYTypeError(cfg, varname="cfg", expected="dictionary-like") # IMPORTANT: create a copy of `cfg` using `StructDict` constructor to # not manipulate `cfg` in user's namespace! cfg = StructDict(cfg) # FIXME # If a method is called using `cfg`, non-default values for # keyword arguments must *only* to be provided via `cfg` defaults = get_defaults(func) for key, value in kwargs.items(): if defaults.get(key, value) != value: raise SPYValueError( legal="no keyword arguments", varname=key, actual="non-default value for {}".format(key)) # Translate any existing "yes" and "no" fields to `True` and `False` for key in cfg.keys(): if str(cfg[key]) == "yes": cfg[key] = True elif str(cfg[key]) == "no": cfg[key] = False # No explicit `cfg`: rename `kwargs` to `cfg` to consolidate processing below; # IMPORTANT: this does *not* create a copy of `kwargs`, thus the `pop`-ing # below actually manipulates `kwargs` as well - crucial for the `kwargs.get("data")` # error checking! else: cfg = kwargs # If `cfg` contains keys 'data' or 'dataset' extract corresponding # entry and make it a positional argument (abort if both 'data' # and 'dataset' are present) data = cfg.pop("data", None) if cfg.get("dataset"): if data: lgl = "either 'data' or 'dataset' in `cfg`/keywords, not both" raise SPYValueError(legal=lgl, varname="cfg") data = cfg.pop("dataset") # If `cfg` did not contain `data`, look into `kwargs` if data is None: data = kwargs.pop("data", None) if kwargs.get("dataset"): if data: lgl = "either `data` or `dataset` keyword, not both" raise SPYValueError(legal=lgl, varname="data/dataset") data = kwargs.pop("dataset") # If Syncopy data object(s) were provided convert single objects to one-element # lists, ensure positional args do *not* contain add'l objects; ensure keyword # args (besides `cfg`) do *not* contain add'l objects; ensure `data` exclusively # contains Syncopy data objects. Finally, rename remaining positional arguments if data: if not isinstance(data, (tuple, list)): data = [data] if any([ isinstance(arg, spy.datatype.base_data.BaseData) for arg in args ]): lgl = "Syncopy data object(s) provided either via `cfg`/keyword or " +\ "positional arguments, not both" raise SPYValueError(legal=lgl, varname="cfg/data") if kwargs.get("data") or kwargs.get("dataset"): lgl = "Syncopy data object(s) provided either via `cfg` or as " +\ "keyword argument, not both" raise SPYValueError(legal=lgl, varname="cfg.data") if any([ not isinstance(obj, spy.datatype.base_data.BaseData) for obj in data ]): raise SPYError("`data` must be Syncopy data object(s)!") posargs = args # If `data` was not provided via `cfg` or as kw-arg, parse positional arguments if data is None: data = [] posargs = [] while args: arg = args.pop(0) if isinstance(arg, spy.datatype.base_data.BaseData): data.append(arg) else: posargs.append(arg) # Call function with unfolded `data` + modified positional/keyword args return func(*data, *posargs, **cfg)
def test_dataselection(self): dummy = SpectralData( data=self.data, trialdefinition=self.trl, samplerate=self.samplerate, taper=["TestTaper_0{}".format(k) for k in range(1, self.nt + 1)]) trialSelections = [ "all", # enforce below selections in all trials of `dummy` [3, 1] # minimally unordered ] chanSelections = [ ["channel03", "channel01", "channel01", "channel02"], # string selection w/repetition + unordered [4, 2, 2, 5, 5], # repetition + unorderd range(5, 8), # narrow range slice(-2, None) # negative-start slice ] toiSelections = [ "all", # non-type-conform string [0.6], # single inexact match [-0.2, 0.6, 0.9, 1.1, 1.3, 1.6, 1.8, 2.2, 2.45, 3.] # unordered, inexact, repetions ] toilimSelections = [ [0.5, 1.5], # regular range [1.5, 2.0], # minimal range (just two-time points) [1.0, np.inf] # unbounded from above ] foiSelections = [ "all", # non-type-conform string [2.6], # single inexact match [1.1, 1.9, 2.1, 3.9, 9.2, 11.8, 12.9, 5.1, 13.8] # unordered, inexact, repetions ] foilimSelections = [ [2, 11], # regular range [1, 2.0], # minimal range (just two-time points) [1.0, np.inf] # unbounded from above ] taperSelections = [ ["TestTaper_03", "TestTaper_01", "TestTaper_01", "TestTaper_02"], # string selection w/repetition + unordered [0, 1, 1, 2, 3], # preserve repetition, don't convert to slice range(2, 5), # narrow range slice(0, 5, 2), # slice w/non-unitary step-size ] timeSelections = list(zip(["toi"] * len(toiSelections), toiSelections)) \ + list(zip(["toilim"] * len(toilimSelections), toilimSelections)) freqSelections = list(zip(["foi"] * len(foiSelections), foiSelections)) \ + list(zip(["foilim"] * len(foilimSelections), foilimSelections)) idx = [slice(None)] * len(dummy.dimord) timeIdx = dummy.dimord.index("time") chanIdx = dummy.dimord.index("channel") freqIdx = dummy.dimord.index("freq") taperIdx = dummy.dimord.index("taper") for trialSel in trialSelections: for chanSel in chanSelections: for timeSel in timeSelections: for freqSel in freqSelections: for taperSel in taperSelections: kwdict = {} kwdict["trials"] = trialSel kwdict["channels"] = chanSel kwdict[timeSel[0]] = timeSel[1] kwdict[freqSel[0]] = freqSel[1] kwdict["tapers"] = taperSel cfg = StructDict(kwdict) # data selection via class-method + `Selector` instance for indexing selected = dummy.selectdata(**kwdict) selector = Selector(dummy, kwdict) idx[chanIdx] = selector.channel idx[freqIdx] = selector.freq idx[taperIdx] = selector.taper for tk, trialno in enumerate(selector.trials): idx[timeIdx] = selector.time[tk] indexed = dummy.trials[trialno][ idx[0], ...][:, idx[1], ...][:, :, idx[2], :][..., idx[3]] assert np.array_equal( selected.trials[tk].squeeze(), indexed.squeeze()) cfg.data = dummy cfg.out = SpectralData( dimord=SpectralData._defaultDimord) # data selection via package function and `cfg`: ensure equality selectdata(cfg) assert np.array_equal(cfg.out.channel, selected.channel) assert np.array_equal(cfg.out.freq, selected.freq) assert np.array_equal(cfg.out.taper, selected.taper) assert np.array_equal(cfg.out.data, selected.data)