From c67ab27ef8fdebaca2ebf41be2e2b21eacc3cf5b Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Thu, 16 May 2019 13:04:10 +0200 Subject: [PATCH 01/14] Add option to load only specific streams --- pyxdf/pyxdf.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/pyxdf/pyxdf.py b/pyxdf/pyxdf.py index 4dee842..3ed3bdf 100644 --- a/pyxdf/pyxdf.py +++ b/pyxdf/pyxdf.py @@ -67,6 +67,7 @@ def __init__(self, xml): def load_xdf(filename, + load_only=None, on_chunk=None, synchronize_clocks=True, handle_clock_resets=True, @@ -94,6 +95,9 @@ def load_xdf(filename, Args: filename : name of the file to import (*.xdf or *.xdfz) + load_only : One or more stream IDs to load. If None, all streams are + loaded (default: None). + synchronize_clocks : Whether to enable clock synchronization based on ClockOffset chunks. (default: true) @@ -179,14 +183,18 @@ def load_xdf(filename, Examples: load the streams contained in a given XDF file - >>> streams, fileheader = load_xdf('C:\Recordings\myrecording.xdf') + >>> streams, fileheader = load_xdf('myrecording.xdf') """ logger.info('Importing XDF file %s...' % filename) if not os.path.exists(filename): raise Exception('file %s does not exist.' % filename) - # dict of returned streams, in order of apparance, indexed by stream id + # load_only contains the streams to load + if isinstance(load_only, int): + load_only = [load_only] # put single stream id into list + + # dict of returned streams, in order of appearance, indexed by stream id streams = OrderedDict() # dict of per-stream temporary data (StreamData), indexed by stream id temp = {} @@ -207,10 +215,8 @@ def load_xdf(filename, if f.read(4) != b'XDF:': raise Exception('not a valid XDF file: %s' % filename) - # for each chunk... - StreamId = None + # for each chunk while True: - # noinspection PyBroadException try: # read [NumLengthBytes], [Length] @@ -231,9 +237,16 @@ def load_xdf(filename, if tag in [2, 3, 4, 6]: StreamId = struct.unpack(' Date: Thu, 16 May 2019 13:07:57 +0200 Subject: [PATCH 02/14] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f43f7e3..2c5fd54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## [1.15.2] - 2019-06-07 ### Added - Store unique stream ID inside the `["info"]["stream_id"]` dict value ([#19](https://github.com/xdf-modules/xdf-Python/pull/19) by [Clemens Brunner](https://github.com/cbrnr)). +- Add option to load only specific streams ([#24](https://github.com/xdf-modules/xdf-Python/pull/24) by [Clemens Brunner](https://github.com/cbrnr)). ## [1.15.1] - 2019-04-26 ### Added From 9f7c7ffdd7a0a13b5bc4aede365fee917a973545 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Wed, 22 May 2019 08:51:04 +0200 Subject: [PATCH 03/14] Add more details and examples in load_only description --- pyxdf/pyxdf.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pyxdf/pyxdf.py b/pyxdf/pyxdf.py index 3ed3bdf..8c1f1d5 100644 --- a/pyxdf/pyxdf.py +++ b/pyxdf/pyxdf.py @@ -95,8 +95,11 @@ def load_xdf(filename, Args: filename : name of the file to import (*.xdf or *.xdfz) - load_only : One or more stream IDs to load. If None, all streams are - loaded (default: None). + load_only : One or more stream IDs to load. This can be an integer or a + list of integers. For example, load_only=5 loads only the stream with + the stream ID 5, whereas load_only=[2, 4, 5] loads only the streams + with stream IDs 2, 4, and 5. If None, all streams are loaded + (default: None). synchronize_clocks : Whether to enable clock synchronization based on ClockOffset chunks. (default: true) From 3fc43bc83965e6a093d3342348c7a8e70d387d09 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Fri, 24 May 2019 08:52:48 +0200 Subject: [PATCH 04/14] Rename load_only -> stream_ids --- pyxdf/pyxdf.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pyxdf/pyxdf.py b/pyxdf/pyxdf.py index 8c1f1d5..2e2c6fa 100644 --- a/pyxdf/pyxdf.py +++ b/pyxdf/pyxdf.py @@ -67,7 +67,7 @@ def __init__(self, xml): def load_xdf(filename, - load_only=None, + stream_ids=None, on_chunk=None, synchronize_clocks=True, handle_clock_resets=True, @@ -95,9 +95,9 @@ def load_xdf(filename, Args: filename : name of the file to import (*.xdf or *.xdfz) - load_only : One or more stream IDs to load. This can be an integer or a - list of integers. For example, load_only=5 loads only the stream with - the stream ID 5, whereas load_only=[2, 4, 5] loads only the streams + stream_ids : One or more stream IDs to load. This can be an integer or + a list of integers. For example, load_only=5 loads only the stream + with stream ID 5, whereas load_only=[2, 4, 5] loads only the streams with stream IDs 2, 4, and 5. If None, all streams are loaded (default: None). @@ -194,8 +194,8 @@ def load_xdf(filename, raise Exception('file %s does not exist.' % filename) # load_only contains the streams to load - if isinstance(load_only, int): - load_only = [load_only] # put single stream id into list + if isinstance(stream_ids, int): + stream_ids = [stream_ids] # put single stream id into list # dict of returned streams, in order of appearance, indexed by stream id streams = OrderedDict() @@ -245,8 +245,8 @@ def load_xdf(filename, logger.debug(log_str) - if StreamId is not None and load_only is not None: - if StreamId not in load_only: + if StreamId is not None and stream_ids is not None: + if StreamId not in stream_ids: f.read(chunklen - 2 - 4) # skip remaining chunk contents continue From c0b634c8ba92ea8dfe72aa11e5a3b3cf83a070ae Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Fri, 24 May 2019 09:03:30 +0200 Subject: [PATCH 05/14] Add stream_info function to extract information on streams contained in file --- pyxdf/__init__.py | 2 +- pyxdf/pyxdf.py | 89 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/pyxdf/__init__.py b/pyxdf/__init__.py index ad41151..154ed51 100644 --- a/pyxdf/__init__.py +++ b/pyxdf/__init__.py @@ -8,5 +8,5 @@ except DistributionNotFound: # package is not installed __version__ = None -from .pyxdf import load_xdf +from .pyxdf import load_xdf, stream_info diff --git a/pyxdf/pyxdf.py b/pyxdf/pyxdf.py index 2e2c6fa..48fbba6 100644 --- a/pyxdf/pyxdf.py +++ b/pyxdf/pyxdf.py @@ -600,3 +600,92 @@ def _robust_fit(A, y, rho=1, iters=1000): z = rho / (1 + rho) * d + 1 / (1 + rho) * tmp * d u = d - z return x + + +def stream_info(fname): + return parse_chunks(parse_xdf(fname)) + + +def parse_xdf(fname): + """Parse and return chunks contained in an XDF file. + + Parameters + ---------- + fname : str + Name of the XDF file. + + Returns + ------- + chunks : list + List of all chunks contained in the XDF file. + """ + chunks = [] + with open(fname, "rb") as f: + if f.read(4) != b"XDF:": # magic code + raise ValueError(f"Invalid XDF file {fname}.") + for chunk in _read_chunks(f): + chunks.append(chunk) + return chunks + + +def parse_chunks(chunks): + """Parse chunks and extract information on individual streams.""" + streams = [] + for chunk in chunks: + if chunk["tag"] == 2: # stream header chunk + streams.append(dict(stream_id=chunk["stream_id"], + name=chunk.get("name"), # optional + type=chunk.get("type"), # optional + source_id=chunk.get("source_id"), # optional + created_at=chunk.get("created_at"), # optional + uid=chunk.get("uid"), # optional + session_id=chunk.get("session_id"), # optional + hostname=chunk.get("hostname"), # optional + channel_count=int(chunk["channel_count"]), + channel_format=chunk["channel_format"], + nominal_srate=int(chunk["nominal_srate"]))) + return streams + + +def _read_chunks(f): + """Read and yield XDF chunks. + + Parameters + ---------- + f : file handle + File handle of XDF file. + + + Yields + ------ + chunk : dict + XDF chunk. + """ + while True: + chunk = dict() + try: + nbytes = struct.unpack("B", f.read(1))[0] + except struct.error: + return # reached EOF + if nbytes == 1: + chunk["nbytes"] = struct.unpack("B", f.read(1))[0] + elif nbytes == 4: + chunk["nbytes"] = struct.unpack(" Date: Fri, 24 May 2019 14:20:52 +0200 Subject: [PATCH 06/14] Rename stream_info -> resolve_streams, add docstring --- pyxdf/__init__.py | 2 +- pyxdf/pyxdf.py | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/pyxdf/__init__.py b/pyxdf/__init__.py index 154ed51..ba6e8cd 100644 --- a/pyxdf/__init__.py +++ b/pyxdf/__init__.py @@ -8,5 +8,5 @@ except DistributionNotFound: # package is not installed __version__ = None -from .pyxdf import load_xdf, stream_info +from .pyxdf import load_xdf, resolve_streams diff --git a/pyxdf/pyxdf.py b/pyxdf/pyxdf.py index 48fbba6..cc637e3 100644 --- a/pyxdf/pyxdf.py +++ b/pyxdf/pyxdf.py @@ -602,7 +602,19 @@ def _robust_fit(A, y, rho=1, iters=1000): return x -def stream_info(fname): +def resolve_streams(fname): + """Resolve streams in given XDF file. + + Parameters + ---------- + fname : str + Name of the XDF file. + + Returns + ------- + streams : list of dicts + List of dicts containing information on each stream. + """ return parse_chunks(parse_xdf(fname)) From 4b2e24898b32e9ae34f4a03838def1f8f86cf432 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Fri, 24 May 2019 14:39:01 +0200 Subject: [PATCH 07/14] Add match_streaminfo function --- pyxdf/__init__.py | 2 +- pyxdf/pyxdf.py | 31 ++++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/pyxdf/__init__.py b/pyxdf/__init__.py index ba6e8cd..58003e4 100644 --- a/pyxdf/__init__.py +++ b/pyxdf/__init__.py @@ -8,5 +8,5 @@ except DistributionNotFound: # package is not installed __version__ = None -from .pyxdf import load_xdf, resolve_streams +from .pyxdf import load_xdf, resolve_streams, match_streaminfos diff --git a/pyxdf/pyxdf.py b/pyxdf/pyxdf.py index cc637e3..3d12c22 100644 --- a/pyxdf/pyxdf.py +++ b/pyxdf/pyxdf.py @@ -602,6 +602,35 @@ def _robust_fit(A, y, rho=1, iters=1000): return x +def match_streaminfos(stream_infos, parameters): + """Find stream IDs matching specified criteria. + + Parameters + ---------- + stream_infos : list of dicts + List of dicts containing information on each stream. This information + can be obtained using the function resolve_streams. + parameters : list of dicts + List of dicts containing key/values that should be present in streams. + Examples: [{"name": "Keyboard"}] matches all streams with a "name" + field equal to "Keyboard". + [{"name": "Keyboard"}, {"type": "EEG"}] matches all streams + with a "name" field equal to "Keyboard" and all streams with + a "type" field equal to "EEG". + """ + matches = [] + for request in parameters: + for info in stream_infos: + for key in request.keys(): + match = info[key] == request[key] + if not match: + break + if match: + matches.append(info['stream_id']) + + return list(set(matches)) # return unique values + + def resolve_streams(fname): """Resolve streams in given XDF file. @@ -612,7 +641,7 @@ def resolve_streams(fname): Returns ------- - streams : list of dicts + stream_infos : list of dicts List of dicts containing information on each stream. """ return parse_chunks(parse_xdf(fname)) From 1dfd317b4357c07e99ec4d2529e7697301cfe14e Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Tue, 18 Jun 2019 12:18:26 +0200 Subject: [PATCH 08/14] Factor out opening of XDF files and use _read_varlen_int --- pyxdf/pyxdf.py | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/pyxdf/pyxdf.py b/pyxdf/pyxdf.py index 3d12c22..e38c213 100644 --- a/pyxdf/pyxdf.py +++ b/pyxdf/pyxdf.py @@ -206,18 +206,7 @@ def load_xdf(filename, # number of bytes in the file for fault tolerance filesize = os.path.getsize(filename) - # read file contents ([SomeText] below refers to items in the XDF Spec) - filename = Path(filename) # convert to pathlib object - if filename.suffix == '.xdfz' or filename.suffixes == ['.xdf', '.gz']: - f_open = gzip.open - else: - f_open = open - - with f_open(filename, 'rb') as f: - # read [MagicCode] - if f.read(4) != b'XDF:': - raise Exception('not a valid XDF file: %s' % filename) - + with open_xdf(filename) as f: # for each chunk while True: # noinspection PyBroadException @@ -352,6 +341,18 @@ def load_xdf(filename, return streams, fileheader +def open_xdf(filename): + """Open XDF file for reading.""" + filename = Path(filename) # convert to pathlib object + if filename.suffix == '.xdfz' or filename.suffixes == ['.xdf', '.gz']: + f = gzip.open(filename, 'rb') + else: + f = open(filename, 'rb') + if f.read(4) != b'XDF:': # magic bytes + raise IOError('Invalid XDF file {}'.format(filename)) + return f + + def _read_chunk3(f, s): # read [NumSampleBytes], [NumSamples] nsamples = _read_varlen_int(f) @@ -403,6 +404,8 @@ def _read_varlen_int(f): return struct.unpack(' Date: Wed, 19 Jun 2019 12:26:40 +0200 Subject: [PATCH 09/14] Allow list of dicts as stream_ids argument --- pyxdf/__init__.py | 4 ++-- pyxdf/pyxdf.py | 29 +++++++++++++++++++++++------ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/pyxdf/__init__.py b/pyxdf/__init__.py index 58003e4..51968a7 100644 --- a/pyxdf/__init__.py +++ b/pyxdf/__init__.py @@ -1,12 +1,12 @@ # Authors: Christian Kothe & the Intheon pyxdf team +# Clemens Brunner # # License: BSD (2-clause) from pkg_resources import get_distribution, DistributionNotFound try: __version__ = get_distribution(__name__).version -except DistributionNotFound: - # package is not installed +except DistributionNotFound: # package is not installed __version__ = None from .pyxdf import load_xdf, resolve_streams, match_streaminfos diff --git a/pyxdf/pyxdf.py b/pyxdf/pyxdf.py index e38c213..847ec68 100644 --- a/pyxdf/pyxdf.py +++ b/pyxdf/pyxdf.py @@ -95,11 +95,18 @@ def load_xdf(filename, Args: filename : name of the file to import (*.xdf or *.xdfz) - stream_ids : One or more stream IDs to load. This can be an integer or - a list of integers. For example, load_only=5 loads only the stream - with stream ID 5, whereas load_only=[2, 4, 5] loads only the streams - with stream IDs 2, 4, and 5. If None, all streams are loaded - (default: None). + stream_ids : One or more stream IDs to load. This can be an integer, a + list of integers or a list of dicts. + For example, load_only=5 loads only the stream with stream ID 5, + whereas load_only=[2, 4, 5] loads only the streams with stream IDs 2, + 4, and 5. If load_only=[{'type': 'EEG'}] (a list of dicts), only + streams matching the query will be loaded. Entries within a dict must + all match a stream, for example + load_only=[{'type': 'EEG', 'name': 'TestAMP'}] matches a stream with + both type 'EEG' *and* name 'TestAMP'. + If load_only=[{'type': 'EEG'}, {'name': 'TestAMP'}], streams matching + either the type *or* the name will be loaded. + If None, all streams are loaded (default: None). synchronize_clocks : Whether to enable clock synchronization based on ClockOffset chunks. (default: true) @@ -193,9 +200,19 @@ def load_xdf(filename, if not os.path.exists(filename): raise Exception('file %s does not exist.' % filename) - # load_only contains the streams to load + # if stream_ids is an int or a list of int, load only streams associated + # with the corresponding stream IDs + # if stream_ids is a list of dicts, use this to query and load streams + # associated with these properties if isinstance(stream_ids, int): stream_ids = [stream_ids] # put single stream id into list + elif all([isinstance(elem, dict) for elem in stream_ids]): # list of dicts + stream_ids = match_streaminfos(resolve_streams(filename), stream_ids) + if not stream_ids: # no streams found + raise ValueError("No matching streams found.") + elif not all([isinstance(elem, int) for elem in stream_ids]): # list of ints + raise ValueError("Argument 'stream_ids' must be an int, a list of ints" + " or a list of dicts.") # dict of returned streams, in order of appearance, indexed by stream id streams = OrderedDict() From ed6a146b27bf494cd3b0b4246bff917d249ed6b2 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Tue, 25 Jun 2019 12:51:35 +0200 Subject: [PATCH 10/14] Update docstring --- pyxdf/pyxdf.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pyxdf/pyxdf.py b/pyxdf/pyxdf.py index 847ec68..9e907bb 100644 --- a/pyxdf/pyxdf.py +++ b/pyxdf/pyxdf.py @@ -97,16 +97,16 @@ def load_xdf(filename, stream_ids : One or more stream IDs to load. This can be an integer, a list of integers or a list of dicts. - For example, load_only=5 loads only the stream with stream ID 5, - whereas load_only=[2, 4, 5] loads only the streams with stream IDs 2, - 4, and 5. If load_only=[{'type': 'EEG'}] (a list of dicts), only - streams matching the query will be loaded. Entries within a dict must - all match a stream, for example - load_only=[{'type': 'EEG', 'name': 'TestAMP'}] matches a stream with - both type 'EEG' *and* name 'TestAMP'. - If load_only=[{'type': 'EEG'}, {'name': 'TestAMP'}], streams matching - either the type *or* the name will be loaded. - If None, all streams are loaded (default: None). + For example, stream_ids=5 loads only the stream with stream ID 5, + whereas stream_ids=[2, 4, 5] loads only the streams with stream IDs + 2, 4, and 5. If stream_ids=[{'type': 'EEG'}] (a list of dicts), only + streams matching the query will be loaded (in this example, all + streams of type 'EEG'). Entries within a dict must all match a + stream, for example stream_ids=[{'type': 'EEG', 'name': 'TestAMP'}] + matches a stream with both type 'EEG' *and* name 'TestAMP'. If + stream_ids=[{'type': 'EEG'}, {'name': 'TestAMP'}], streams matching + either the type *or* the name will be loaded. If None, all streams + are loaded (default: None). synchronize_clocks : Whether to enable clock synchronization based on ClockOffset chunks. (default: true) From 945183cebaf35bf1c8261ec8a4d3fa2e7cdd1a28 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Fri, 28 Jun 2019 09:08:44 +0200 Subject: [PATCH 11/14] Rename stream_ids parameter -> select_streams --- pyxdf/pyxdf.py | 57 +++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/pyxdf/pyxdf.py b/pyxdf/pyxdf.py index 9e907bb..b31aca8 100644 --- a/pyxdf/pyxdf.py +++ b/pyxdf/pyxdf.py @@ -67,7 +67,7 @@ def __init__(self, xml): def load_xdf(filename, - stream_ids=None, + select_streams=None, on_chunk=None, synchronize_clocks=True, handle_clock_resets=True, @@ -97,16 +97,16 @@ def load_xdf(filename, stream_ids : One or more stream IDs to load. This can be an integer, a list of integers or a list of dicts. - For example, stream_ids=5 loads only the stream with stream ID 5, - whereas stream_ids=[2, 4, 5] loads only the streams with stream IDs - 2, 4, and 5. If stream_ids=[{'type': 'EEG'}] (a list of dicts), only - streams matching the query will be loaded (in this example, all - streams of type 'EEG'). Entries within a dict must all match a - stream, for example stream_ids=[{'type': 'EEG', 'name': 'TestAMP'}] + For example, select_streams=5 loads only the stream with stream ID 5, + whereas select_streams=[2, 4, 5] loads only the streams with stream + IDs 2, 4, and 5. If select_streams=[{'type': 'EEG'}] (a list of + dicts), only streams matching the query will be loaded (in this + example, all streams of type 'EEG'). Entries within a dict must all + match a stream, select_streams=[{'type': 'EEG', 'name': 'TestAMP'}] matches a stream with both type 'EEG' *and* name 'TestAMP'. If - stream_ids=[{'type': 'EEG'}, {'name': 'TestAMP'}], streams matching - either the type *or* the name will be loaded. If None, all streams - are loaded (default: None). + select_streams=[{'type': 'EEG'}, {'name': 'TestAMP'}], streams + matching either the type *or* the name will be loaded. If None, all + streams are loaded (default: None). synchronize_clocks : Whether to enable clock synchronization based on ClockOffset chunks. (default: true) @@ -169,10 +169,10 @@ def load_xdf(filename, streams : list of dicts, one for each stream; the dicts have the following content: ['time_series'] entry: contains the stream's time series - [#Channels x #Samples] this matrix is of the type declared in - ['info']['channel_format'] - ['time_stamps'] entry: contains the time stamps for each sample - (synced across streams) + [#Channels x #Samples] this matrix is of the type declared + in ['info']['channel_format'] + ['time_stamps'] entry: contains the time stamps for each + sample (synced across streams) ['info'] field: contains the meta-data of the stream (all values are strings) @@ -200,19 +200,20 @@ def load_xdf(filename, if not os.path.exists(filename): raise Exception('file %s does not exist.' % filename) - # if stream_ids is an int or a list of int, load only streams associated - # with the corresponding stream IDs - # if stream_ids is a list of dicts, use this to query and load streams + # if select_streams is an int or a list of int, load only streams + # associated with the corresponding stream IDs + # if select_streams is a list of dicts, use this to query and load streams # associated with these properties - if isinstance(stream_ids, int): - stream_ids = [stream_ids] # put single stream id into list - elif all([isinstance(elem, dict) for elem in stream_ids]): # list of dicts - stream_ids = match_streaminfos(resolve_streams(filename), stream_ids) - if not stream_ids: # no streams found + if isinstance(select_streams, int): + select_streams = [select_streams] + elif all([isinstance(elem, dict) for elem in select_streams]): + select_streams = match_streaminfos(resolve_streams(filename), + select_streams) + if not select_streams: # no streams found raise ValueError("No matching streams found.") - elif not all([isinstance(elem, int) for elem in stream_ids]): # list of ints - raise ValueError("Argument 'stream_ids' must be an int, a list of ints" - " or a list of dicts.") + elif not all([isinstance(elem, int) for elem in select_streams]): + raise ValueError("Argument 'select_streams' must be an int, a list of " + "ints or a list of dicts.") # dict of returned streams, in order of appearance, indexed by stream id streams = OrderedDict() @@ -251,8 +252,8 @@ def load_xdf(filename, logger.debug(log_str) - if StreamId is not None and stream_ids is not None: - if StreamId not in stream_ids: + if StreamId is not None and select_streams is not None: + if StreamId not in select_streams: f.read(chunklen - 2 - 4) # skip remaining chunk contents continue @@ -282,7 +283,7 @@ def load_xdf(filename, # optionally send through the on_chunk function if on_chunk is not None: values, stamps, streams[StreamId] = on_chunk(values, stamps, - streams[StreamId], StreamId) + streams[StreamId], StreamId) # append to the time series... temp[StreamId].time_series.append(values) temp[StreamId].time_stamps.append(stamps) From a21b2b1cc94c331433f854900168b8c972114483 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Tue, 2 Jul 2019 10:26:58 +0200 Subject: [PATCH 12/14] Correctly handle default case of select_streams=None --- pyxdf/pyxdf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyxdf/pyxdf.py b/pyxdf/pyxdf.py index b31aca8..58a210f 100644 --- a/pyxdf/pyxdf.py +++ b/pyxdf/pyxdf.py @@ -204,7 +204,9 @@ def load_xdf(filename, # associated with the corresponding stream IDs # if select_streams is a list of dicts, use this to query and load streams # associated with these properties - if isinstance(select_streams, int): + if select_streams is None: + pass + elif isinstance(select_streams, int): select_streams = [select_streams] elif all([isinstance(elem, dict) for elem in select_streams]): select_streams = match_streaminfos(resolve_streams(filename), From 2850f040fb3a1d096a8ed0c972247f5b937fecf9 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Tue, 2 Jul 2019 10:36:13 +0200 Subject: [PATCH 13/14] Fix arg name in docstring --- pyxdf/pyxdf.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pyxdf/pyxdf.py b/pyxdf/pyxdf.py index 58a210f..12a50b7 100644 --- a/pyxdf/pyxdf.py +++ b/pyxdf/pyxdf.py @@ -95,15 +95,15 @@ def load_xdf(filename, Args: filename : name of the file to import (*.xdf or *.xdfz) - stream_ids : One or more stream IDs to load. This can be an integer, a - list of integers or a list of dicts. - For example, select_streams=5 loads only the stream with stream ID 5, - whereas select_streams=[2, 4, 5] loads only the streams with stream - IDs 2, 4, and 5. If select_streams=[{'type': 'EEG'}] (a list of - dicts), only streams matching the query will be loaded (in this - example, all streams of type 'EEG'). Entries within a dict must all - match a stream, select_streams=[{'type': 'EEG', 'name': 'TestAMP'}] - matches a stream with both type 'EEG' *and* name 'TestAMP'. If + select_streams : One or more stream IDs to load. This can be an integer + integer, a list of integers or a list of dicts. For example, + select_streams=5 loads only the stream with stream ID 5, whereas + select_streams=[2, 4, 5] loads only the streams with stream IDs 2, 4, + and 5. If select_streams=[{'type': 'EEG'}] (a list of dicts), only + streams matching the query will be loaded (in this example, all + streams of type 'EEG'). Entries within a dict must all match a + stream, select_streams=[{'type': 'EEG', 'name': 'TestAMP'}] matches a + stream with both type 'EEG' *and* name 'TestAMP'. If select_streams=[{'type': 'EEG'}, {'name': 'TestAMP'}], streams matching either the type *or* the name will be loaded. If None, all streams are loaded (default: None). From fde44db77b7a83e0e6bc4253d04b3ee98e07c3b6 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Mon, 8 Jul 2019 13:43:35 +0200 Subject: [PATCH 14/14] Rephrase select_streams doc --- pyxdf/pyxdf.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/pyxdf/pyxdf.py b/pyxdf/pyxdf.py index 12a50b7..6a730cb 100644 --- a/pyxdf/pyxdf.py +++ b/pyxdf/pyxdf.py @@ -95,18 +95,19 @@ def load_xdf(filename, Args: filename : name of the file to import (*.xdf or *.xdfz) - select_streams : One or more stream IDs to load. This can be an integer - integer, a list of integers or a list of dicts. For example, - select_streams=5 loads only the stream with stream ID 5, whereas - select_streams=[2, 4, 5] loads only the streams with stream IDs 2, 4, - and 5. If select_streams=[{'type': 'EEG'}] (a list of dicts), only - streams matching the query will be loaded (in this example, all - streams of type 'EEG'). Entries within a dict must all match a - stream, select_streams=[{'type': 'EEG', 'name': 'TestAMP'}] matches a - stream with both type 'EEG' *and* name 'TestAMP'. If - select_streams=[{'type': 'EEG'}, {'name': 'TestAMP'}], streams - matching either the type *or* the name will be loaded. If None, all - streams are loaded (default: None). + select_streams : int | list[int] | list[dict] | None + One or more stream IDs to load. Accepted values are: + - int or list[int]: load only specified stream IDs, e.g. + select_streams=5 loads only the stream with stream ID 5, whereas + select_streams=[2, 4] loads only streams with stream IDs 2 and 4. + - list[dict]: load only streams matching the query, e.g. + select_streams=[{'type': 'EEG'}] loads all streams of type 'EEG'. + Entries within a dict must all match a stream, e.g. + select_streams=[{'type': 'EEG', 'name': 'TestAMP'}] matches streams + with both type 'EEG' *and* name 'TestAMP'. If + select_streams=[{'type': 'EEG'}, {'name': 'TestAMP'}], streams + matching either the type *or* the name will be loaded. + - None: load all streams (default). synchronize_clocks : Whether to enable clock synchronization based on ClockOffset chunks. (default: true)