bci_essentials.data_tank.data_tank

  1import numpy as np
  2from ..utils.logger import Logger  # Logger wrapper
  3
  4# Instantiate a logger for the module at the default level of logging.INFO
  5# Logs to bci_essentials.__module__) where __module__ is the name of the module
  6logger = Logger(name=__name__)
  7
  8
  9# Will eventually move somewhere else
 10class DataTank:
 11    """
 12    DataTank class is for storing raw EEG, markers, epochs, and rtesting state data
 13
 14    TODO: Add your desired flavour of save output from here.
 15    To be added:
 16    - MNE Raw
 17    - MNE Epochs
 18    - BIDS
 19    - XDF
 20    """
 21
 22    def __init__(self):
 23        """
 24        Initialize the DataTank.
 25        """
 26
 27        # Initialize np arrays to store the data
 28        self.__raw_eeg = np.zeros((0, 0))
 29        self.__raw_eeg_timestamps = np.zeros((0))
 30        self.__raw_marker_strings = np.zeros((0), dtype=str)
 31        self.__raw_marker_timestamps = np.zeros((0))
 32        # self.event_marker_strings = np.zeros((0), dtype=str)
 33        # self.event_marker_timestamps = np.zeros((0))
 34
 35        # Keep track of the latest timestamp
 36        self.latest_eeg_timestamp = 0
 37
 38        # Keep track of how many epochs have been sent, so it is possible to only send new ones
 39        self.epochs_sent = 0
 40        self.epochs = np.zeros((0, 0))
 41        self.labels = np.array([], dtype=str)
 42
 43    def set_source_data(
 44        self, headset_string, fsample, n_channels, ch_types, ch_units, channel_labels
 45    ):
 46        """
 47        Set the source data for the DataTank so that this metadata can be saved with the data.
 48
 49        Parameters
 50        ----------
 51
 52        headset_string : str
 53            The name of the headset used to collect the data.
 54        fsample : float
 55            The sampling frequency of the data.
 56        n_channels : int
 57            The number of channels in the data.
 58        ch_types : list of str
 59            The type of each channel.
 60        ch_units : list of str
 61            The units of each channel.
 62        channel_labels : list of str
 63            The labels of each channel.
 64
 65        Returns
 66        -------
 67        `None`
 68        """
 69        self.headset_string = headset_string
 70        self.fsample = fsample
 71        self.n_channels = n_channels
 72        self.ch_types = ch_types
 73        self.ch_units = ch_units
 74        self.channel_labels = channel_labels
 75
 76    def add_raw_eeg(self, new_raw_eeg, new_eeg_timestamps):
 77        """
 78        Add raw EEG data to the data tank.
 79
 80        Parameters
 81        ----------
 82        new_raw_eeg : np.array
 83            The new raw EEG data to add.
 84
 85        new_eeg_timestamps : np.array
 86            The timestamps of the new raw EEG data.
 87
 88        Returns
 89        -------
 90        `None`
 91        """
 92
 93        # If this is the first chunk of EEG, initialize the arrays
 94        if self.__raw_eeg.size == 0:
 95            self.__raw_eeg = new_raw_eeg
 96            self.__raw_eeg_timestamps = new_eeg_timestamps
 97        else:
 98            self.__raw_eeg = np.concatenate((self.__raw_eeg, new_raw_eeg), axis=1)
 99            self.__raw_eeg_timestamps = np.concatenate(
100                (self.__raw_eeg_timestamps, new_eeg_timestamps)
101            )
102
103        self.latest_eeg_timestamp = new_eeg_timestamps[-1]
104
105    def add_raw_markers(self, new_marker_strings, new_marker_timestamps):
106        """
107        Add raw markers to the data tank.
108
109        Parameters
110        ----------
111        new_marker_strings : np.array
112            The new marker strings to add.
113        new_marker_timestamps : np.array
114            The timestamps of the new marker strings.
115
116        Returns
117        -------
118        `None`
119        """
120
121        if self.__raw_marker_strings.size == 0:
122            self.__raw_marker_strings = new_marker_strings
123            self.__raw_marker_timestamps = new_marker_timestamps
124        else:
125            self.__raw_marker_strings = np.concatenate(
126                (self.__raw_marker_strings, new_marker_strings)
127            )
128            self.__raw_marker_timestamps = np.concatenate(
129                (self.__raw_marker_timestamps, new_marker_timestamps)
130            )
131
132    def get_raw_eeg(self):
133        """
134        Get the raw EEG data from the DataTank.
135
136        Returns
137        -------
138        np.array
139            The raw EEG data.
140        np.array
141            The timestamps of the raw EEG data.
142        """
143        # Get the EEG data between the start and end times
144        return self.__raw_eeg, self.__raw_eeg_timestamps
145
146    def get_raw_markers(self):
147        """
148        Get the raw markers from the DataTank.
149
150        Returns
151        -------
152        np.array
153            The raw marker strings.
154        np.array
155            The timestamps of the raw marker strings.
156        """
157        return self.__raw_marker_strings, self.__raw_marker_timestamps
158
159    def add_epochs(self, X, y):
160        """
161        Add epochs to the data tank.
162
163        Parameters
164        ----------
165        X : np.array
166            The new epochs to add. Shape is (n_epochs, n_channels, n_samples).
167        y : np.array
168            The labels of the epochs. Shape is (n_epochs).
169
170        Returns
171        -------
172        `None`
173
174        """
175        # Add new epochs to the data tank
176        if self.epochs.size == 0:
177            self.epochs = np.array(X)
178            self.labels = np.array(y)
179
180        else:
181            # Check the size of the new data
182            if X.shape[1:] != self.epochs.shape[1:]:
183                logger.warning("Epochs are not the same size, skipping this data.")
184            else:
185                self.epochs = np.concatenate((self.epochs, np.array(X)))
186                self.labels = np.concatenate((self.labels, np.array(y)))
187
188    def get_epochs(self, latest=False):
189        """
190        Get the epochs from the data tank.
191
192        Parameters
193        ----------
194        latest : bool
195            If `True`, only return the new data since the last call to this function.
196
197        Returns
198        -------
199        np.array
200            The epochs. Shape is (n_epochs, n_channels, n_samples).
201        np.array
202            The labels of the epochs. Shape is (n_epochs).
203
204        """
205        if self.epochs.size == 0:
206            logger.warning("Data tank contains no epochs, returning None.")
207            return [], []
208
209        if latest:
210            # Return only the new data
211            first_unsent = self.epochs_sent
212            self.epochs_sent = len(self.epochs)
213
214            return self.epochs[first_unsent:], self.labels[first_unsent:]
215        else:
216            # Return all
217            return self.epochs, self.labels
218
219    def add_resting_state_data(self, resting_state_data):
220        """
221        Add resting state data to the data tank.
222
223        Parameters
224        ----------
225        resting_state_data : dict
226            Dictionary containing resting state data.
227
228        Returns
229        -------
230        `None`
231
232        """
233        # Get the resting state data
234        self.__resting_state_data = resting_state_data
235
236    def get_resting_state_data(self):
237        """
238        Get the resting state data.
239
240        Returns
241        -------
242        dict
243            Dictionary containing resting state data.
244
245        """
246        return self.__resting_state_data
247
248    def save_epochs_as_npz(self, file_name: str):
249        """
250        Saves EEG trials and labels as a numpy file.
251
252        Parameters
253        ----------
254        file_name : str
255            The name of the file to save the EEG trials and labels to.
256
257        Returns
258        -------
259        `None`
260
261        """
262        # Check if file ends with .npz, if not add it
263        if file_name[-4:] != ".npz":
264            file_name += ".npz"
265
266        # Get the raw EEG trials and labels
267        X = self.epochs
268        y = self.labels
269
270        # Save the raw EEG trials and labels as a numpy file
271        np.savez(file_name, X=X, y=y)
class DataTank:
 11class DataTank:
 12    """
 13    DataTank class is for storing raw EEG, markers, epochs, and rtesting state data
 14
 15    TODO: Add your desired flavour of save output from here.
 16    To be added:
 17    - MNE Raw
 18    - MNE Epochs
 19    - BIDS
 20    - XDF
 21    """
 22
 23    def __init__(self):
 24        """
 25        Initialize the DataTank.
 26        """
 27
 28        # Initialize np arrays to store the data
 29        self.__raw_eeg = np.zeros((0, 0))
 30        self.__raw_eeg_timestamps = np.zeros((0))
 31        self.__raw_marker_strings = np.zeros((0), dtype=str)
 32        self.__raw_marker_timestamps = np.zeros((0))
 33        # self.event_marker_strings = np.zeros((0), dtype=str)
 34        # self.event_marker_timestamps = np.zeros((0))
 35
 36        # Keep track of the latest timestamp
 37        self.latest_eeg_timestamp = 0
 38
 39        # Keep track of how many epochs have been sent, so it is possible to only send new ones
 40        self.epochs_sent = 0
 41        self.epochs = np.zeros((0, 0))
 42        self.labels = np.array([], dtype=str)
 43
 44    def set_source_data(
 45        self, headset_string, fsample, n_channels, ch_types, ch_units, channel_labels
 46    ):
 47        """
 48        Set the source data for the DataTank so that this metadata can be saved with the data.
 49
 50        Parameters
 51        ----------
 52
 53        headset_string : str
 54            The name of the headset used to collect the data.
 55        fsample : float
 56            The sampling frequency of the data.
 57        n_channels : int
 58            The number of channels in the data.
 59        ch_types : list of str
 60            The type of each channel.
 61        ch_units : list of str
 62            The units of each channel.
 63        channel_labels : list of str
 64            The labels of each channel.
 65
 66        Returns
 67        -------
 68        `None`
 69        """
 70        self.headset_string = headset_string
 71        self.fsample = fsample
 72        self.n_channels = n_channels
 73        self.ch_types = ch_types
 74        self.ch_units = ch_units
 75        self.channel_labels = channel_labels
 76
 77    def add_raw_eeg(self, new_raw_eeg, new_eeg_timestamps):
 78        """
 79        Add raw EEG data to the data tank.
 80
 81        Parameters
 82        ----------
 83        new_raw_eeg : np.array
 84            The new raw EEG data to add.
 85
 86        new_eeg_timestamps : np.array
 87            The timestamps of the new raw EEG data.
 88
 89        Returns
 90        -------
 91        `None`
 92        """
 93
 94        # If this is the first chunk of EEG, initialize the arrays
 95        if self.__raw_eeg.size == 0:
 96            self.__raw_eeg = new_raw_eeg
 97            self.__raw_eeg_timestamps = new_eeg_timestamps
 98        else:
 99            self.__raw_eeg = np.concatenate((self.__raw_eeg, new_raw_eeg), axis=1)
100            self.__raw_eeg_timestamps = np.concatenate(
101                (self.__raw_eeg_timestamps, new_eeg_timestamps)
102            )
103
104        self.latest_eeg_timestamp = new_eeg_timestamps[-1]
105
106    def add_raw_markers(self, new_marker_strings, new_marker_timestamps):
107        """
108        Add raw markers to the data tank.
109
110        Parameters
111        ----------
112        new_marker_strings : np.array
113            The new marker strings to add.
114        new_marker_timestamps : np.array
115            The timestamps of the new marker strings.
116
117        Returns
118        -------
119        `None`
120        """
121
122        if self.__raw_marker_strings.size == 0:
123            self.__raw_marker_strings = new_marker_strings
124            self.__raw_marker_timestamps = new_marker_timestamps
125        else:
126            self.__raw_marker_strings = np.concatenate(
127                (self.__raw_marker_strings, new_marker_strings)
128            )
129            self.__raw_marker_timestamps = np.concatenate(
130                (self.__raw_marker_timestamps, new_marker_timestamps)
131            )
132
133    def get_raw_eeg(self):
134        """
135        Get the raw EEG data from the DataTank.
136
137        Returns
138        -------
139        np.array
140            The raw EEG data.
141        np.array
142            The timestamps of the raw EEG data.
143        """
144        # Get the EEG data between the start and end times
145        return self.__raw_eeg, self.__raw_eeg_timestamps
146
147    def get_raw_markers(self):
148        """
149        Get the raw markers from the DataTank.
150
151        Returns
152        -------
153        np.array
154            The raw marker strings.
155        np.array
156            The timestamps of the raw marker strings.
157        """
158        return self.__raw_marker_strings, self.__raw_marker_timestamps
159
160    def add_epochs(self, X, y):
161        """
162        Add epochs to the data tank.
163
164        Parameters
165        ----------
166        X : np.array
167            The new epochs to add. Shape is (n_epochs, n_channels, n_samples).
168        y : np.array
169            The labels of the epochs. Shape is (n_epochs).
170
171        Returns
172        -------
173        `None`
174
175        """
176        # Add new epochs to the data tank
177        if self.epochs.size == 0:
178            self.epochs = np.array(X)
179            self.labels = np.array(y)
180
181        else:
182            # Check the size of the new data
183            if X.shape[1:] != self.epochs.shape[1:]:
184                logger.warning("Epochs are not the same size, skipping this data.")
185            else:
186                self.epochs = np.concatenate((self.epochs, np.array(X)))
187                self.labels = np.concatenate((self.labels, np.array(y)))
188
189    def get_epochs(self, latest=False):
190        """
191        Get the epochs from the data tank.
192
193        Parameters
194        ----------
195        latest : bool
196            If `True`, only return the new data since the last call to this function.
197
198        Returns
199        -------
200        np.array
201            The epochs. Shape is (n_epochs, n_channels, n_samples).
202        np.array
203            The labels of the epochs. Shape is (n_epochs).
204
205        """
206        if self.epochs.size == 0:
207            logger.warning("Data tank contains no epochs, returning None.")
208            return [], []
209
210        if latest:
211            # Return only the new data
212            first_unsent = self.epochs_sent
213            self.epochs_sent = len(self.epochs)
214
215            return self.epochs[first_unsent:], self.labels[first_unsent:]
216        else:
217            # Return all
218            return self.epochs, self.labels
219
220    def add_resting_state_data(self, resting_state_data):
221        """
222        Add resting state data to the data tank.
223
224        Parameters
225        ----------
226        resting_state_data : dict
227            Dictionary containing resting state data.
228
229        Returns
230        -------
231        `None`
232
233        """
234        # Get the resting state data
235        self.__resting_state_data = resting_state_data
236
237    def get_resting_state_data(self):
238        """
239        Get the resting state data.
240
241        Returns
242        -------
243        dict
244            Dictionary containing resting state data.
245
246        """
247        return self.__resting_state_data
248
249    def save_epochs_as_npz(self, file_name: str):
250        """
251        Saves EEG trials and labels as a numpy file.
252
253        Parameters
254        ----------
255        file_name : str
256            The name of the file to save the EEG trials and labels to.
257
258        Returns
259        -------
260        `None`
261
262        """
263        # Check if file ends with .npz, if not add it
264        if file_name[-4:] != ".npz":
265            file_name += ".npz"
266
267        # Get the raw EEG trials and labels
268        X = self.epochs
269        y = self.labels
270
271        # Save the raw EEG trials and labels as a numpy file
272        np.savez(file_name, X=X, y=y)

DataTank class is for storing raw EEG, markers, epochs, and rtesting state data

TODO: Add your desired flavour of save output from here. To be added:

  • MNE Raw
  • MNE Epochs
  • BIDS
  • XDF
DataTank()
23    def __init__(self):
24        """
25        Initialize the DataTank.
26        """
27
28        # Initialize np arrays to store the data
29        self.__raw_eeg = np.zeros((0, 0))
30        self.__raw_eeg_timestamps = np.zeros((0))
31        self.__raw_marker_strings = np.zeros((0), dtype=str)
32        self.__raw_marker_timestamps = np.zeros((0))
33        # self.event_marker_strings = np.zeros((0), dtype=str)
34        # self.event_marker_timestamps = np.zeros((0))
35
36        # Keep track of the latest timestamp
37        self.latest_eeg_timestamp = 0
38
39        # Keep track of how many epochs have been sent, so it is possible to only send new ones
40        self.epochs_sent = 0
41        self.epochs = np.zeros((0, 0))
42        self.labels = np.array([], dtype=str)

Initialize the DataTank.

latest_eeg_timestamp
epochs_sent
epochs
labels
def set_source_data( self, headset_string, fsample, n_channels, ch_types, ch_units, channel_labels):
44    def set_source_data(
45        self, headset_string, fsample, n_channels, ch_types, ch_units, channel_labels
46    ):
47        """
48        Set the source data for the DataTank so that this metadata can be saved with the data.
49
50        Parameters
51        ----------
52
53        headset_string : str
54            The name of the headset used to collect the data.
55        fsample : float
56            The sampling frequency of the data.
57        n_channels : int
58            The number of channels in the data.
59        ch_types : list of str
60            The type of each channel.
61        ch_units : list of str
62            The units of each channel.
63        channel_labels : list of str
64            The labels of each channel.
65
66        Returns
67        -------
68        `None`
69        """
70        self.headset_string = headset_string
71        self.fsample = fsample
72        self.n_channels = n_channels
73        self.ch_types = ch_types
74        self.ch_units = ch_units
75        self.channel_labels = channel_labels

Set the source data for the DataTank so that this metadata can be saved with the data.

Parameters
  • headset_string (str): The name of the headset used to collect the data.
  • fsample (float): The sampling frequency of the data.
  • n_channels (int): The number of channels in the data.
  • ch_types (list of str): The type of each channel.
  • ch_units (list of str): The units of each channel.
  • channel_labels (list of str): The labels of each channel.
Returns
  • None
def add_raw_eeg(self, new_raw_eeg, new_eeg_timestamps):
 77    def add_raw_eeg(self, new_raw_eeg, new_eeg_timestamps):
 78        """
 79        Add raw EEG data to the data tank.
 80
 81        Parameters
 82        ----------
 83        new_raw_eeg : np.array
 84            The new raw EEG data to add.
 85
 86        new_eeg_timestamps : np.array
 87            The timestamps of the new raw EEG data.
 88
 89        Returns
 90        -------
 91        `None`
 92        """
 93
 94        # If this is the first chunk of EEG, initialize the arrays
 95        if self.__raw_eeg.size == 0:
 96            self.__raw_eeg = new_raw_eeg
 97            self.__raw_eeg_timestamps = new_eeg_timestamps
 98        else:
 99            self.__raw_eeg = np.concatenate((self.__raw_eeg, new_raw_eeg), axis=1)
100            self.__raw_eeg_timestamps = np.concatenate(
101                (self.__raw_eeg_timestamps, new_eeg_timestamps)
102            )
103
104        self.latest_eeg_timestamp = new_eeg_timestamps[-1]

Add raw EEG data to the data tank.

Parameters
  • new_raw_eeg (np.array): The new raw EEG data to add.
  • new_eeg_timestamps (np.array): The timestamps of the new raw EEG data.
Returns
  • None
def add_raw_markers(self, new_marker_strings, new_marker_timestamps):
106    def add_raw_markers(self, new_marker_strings, new_marker_timestamps):
107        """
108        Add raw markers to the data tank.
109
110        Parameters
111        ----------
112        new_marker_strings : np.array
113            The new marker strings to add.
114        new_marker_timestamps : np.array
115            The timestamps of the new marker strings.
116
117        Returns
118        -------
119        `None`
120        """
121
122        if self.__raw_marker_strings.size == 0:
123            self.__raw_marker_strings = new_marker_strings
124            self.__raw_marker_timestamps = new_marker_timestamps
125        else:
126            self.__raw_marker_strings = np.concatenate(
127                (self.__raw_marker_strings, new_marker_strings)
128            )
129            self.__raw_marker_timestamps = np.concatenate(
130                (self.__raw_marker_timestamps, new_marker_timestamps)
131            )

Add raw markers to the data tank.

Parameters
  • new_marker_strings (np.array): The new marker strings to add.
  • new_marker_timestamps (np.array): The timestamps of the new marker strings.
Returns
  • None
def get_raw_eeg(self):
133    def get_raw_eeg(self):
134        """
135        Get the raw EEG data from the DataTank.
136
137        Returns
138        -------
139        np.array
140            The raw EEG data.
141        np.array
142            The timestamps of the raw EEG data.
143        """
144        # Get the EEG data between the start and end times
145        return self.__raw_eeg, self.__raw_eeg_timestamps

Get the raw EEG data from the DataTank.

Returns
  • np.array: The raw EEG data.
  • np.array: The timestamps of the raw EEG data.
def get_raw_markers(self):
147    def get_raw_markers(self):
148        """
149        Get the raw markers from the DataTank.
150
151        Returns
152        -------
153        np.array
154            The raw marker strings.
155        np.array
156            The timestamps of the raw marker strings.
157        """
158        return self.__raw_marker_strings, self.__raw_marker_timestamps

Get the raw markers from the DataTank.

Returns
  • np.array: The raw marker strings.
  • np.array: The timestamps of the raw marker strings.
def add_epochs(self, X, y):
160    def add_epochs(self, X, y):
161        """
162        Add epochs to the data tank.
163
164        Parameters
165        ----------
166        X : np.array
167            The new epochs to add. Shape is (n_epochs, n_channels, n_samples).
168        y : np.array
169            The labels of the epochs. Shape is (n_epochs).
170
171        Returns
172        -------
173        `None`
174
175        """
176        # Add new epochs to the data tank
177        if self.epochs.size == 0:
178            self.epochs = np.array(X)
179            self.labels = np.array(y)
180
181        else:
182            # Check the size of the new data
183            if X.shape[1:] != self.epochs.shape[1:]:
184                logger.warning("Epochs are not the same size, skipping this data.")
185            else:
186                self.epochs = np.concatenate((self.epochs, np.array(X)))
187                self.labels = np.concatenate((self.labels, np.array(y)))

Add epochs to the data tank.

Parameters
  • X (np.array): The new epochs to add. Shape is (n_epochs, n_channels, n_samples).
  • y (np.array): The labels of the epochs. Shape is (n_epochs).
Returns
  • None
def get_epochs(self, latest=False):
189    def get_epochs(self, latest=False):
190        """
191        Get the epochs from the data tank.
192
193        Parameters
194        ----------
195        latest : bool
196            If `True`, only return the new data since the last call to this function.
197
198        Returns
199        -------
200        np.array
201            The epochs. Shape is (n_epochs, n_channels, n_samples).
202        np.array
203            The labels of the epochs. Shape is (n_epochs).
204
205        """
206        if self.epochs.size == 0:
207            logger.warning("Data tank contains no epochs, returning None.")
208            return [], []
209
210        if latest:
211            # Return only the new data
212            first_unsent = self.epochs_sent
213            self.epochs_sent = len(self.epochs)
214
215            return self.epochs[first_unsent:], self.labels[first_unsent:]
216        else:
217            # Return all
218            return self.epochs, self.labels

Get the epochs from the data tank.

Parameters
  • latest (bool): If True, only return the new data since the last call to this function.
Returns
  • np.array: The epochs. Shape is (n_epochs, n_channels, n_samples).
  • np.array: The labels of the epochs. Shape is (n_epochs).
def add_resting_state_data(self, resting_state_data):
220    def add_resting_state_data(self, resting_state_data):
221        """
222        Add resting state data to the data tank.
223
224        Parameters
225        ----------
226        resting_state_data : dict
227            Dictionary containing resting state data.
228
229        Returns
230        -------
231        `None`
232
233        """
234        # Get the resting state data
235        self.__resting_state_data = resting_state_data

Add resting state data to the data tank.

Parameters
  • resting_state_data (dict): Dictionary containing resting state data.
Returns
  • None
def get_resting_state_data(self):
237    def get_resting_state_data(self):
238        """
239        Get the resting state data.
240
241        Returns
242        -------
243        dict
244            Dictionary containing resting state data.
245
246        """
247        return self.__resting_state_data

Get the resting state data.

Returns
  • dict: Dictionary containing resting state data.
def save_epochs_as_npz(self, file_name: str):
249    def save_epochs_as_npz(self, file_name: str):
250        """
251        Saves EEG trials and labels as a numpy file.
252
253        Parameters
254        ----------
255        file_name : str
256            The name of the file to save the EEG trials and labels to.
257
258        Returns
259        -------
260        `None`
261
262        """
263        # Check if file ends with .npz, if not add it
264        if file_name[-4:] != ".npz":
265            file_name += ".npz"
266
267        # Get the raw EEG trials and labels
268        X = self.epochs
269        y = self.labels
270
271        # Save the raw EEG trials and labels as a numpy file
272        np.savez(file_name, X=X, y=y)

Saves EEG trials and labels as a numpy file.

Parameters
  • file_name (str): The name of the file to save the EEG trials and labels to.
Returns
  • None