Page 1 of 1

Replicate an experiment in a cursor task

Posted: 27 Sep 2013, 11:32
by rubmoe
Hello everyone,

first of all thank you very much for the great work on BCI2000!
I have EEG Data that was recorded on a different system and would like to test them on BCI2000. The data were recorded from C3 C4 and Cz during right and left arm motor imagery. Each trial lasted 4 seconds, and the intertrial intervals varied randomly.
I already managed to run the data with the FilePlayback and move the cursor in 1 dimension. Now I would like to check the accuracy and therefore need to exactly rebuild the sequences of the experiment. Is it somehow possible to read out the StimulusCode information from my data and use them to replicate the experiment?

Thank you in advance,
rubmoe

Re: Replicate an experiment in a cursor task

Posted: 27 Sep 2013, 23:53
by boulay
What format are your original data in? To get it to work in FilePlayback, did you make a BCI2000.dat file and did you encode the states?
If not then that's probably your first step, and I believe that can be done with the Matlab tools. (BCI2000\tools\matlab)

As for evaluating "accuracy", do you want numbers or do you just want to see the cursor move to the target?

If your goal is to visualize the cursor moving then you can use the PythonApplication module and set the option to "Enslave Python states". This will require you to do a lot of preliminary work including writing a python application that has a moving cursor. The key is to make sure all the visual information and the transition between task phases depend on the states.

If your goal is to get accuracy metrics then you can try using the cmdline tools which perfectly replicate the signal processing as it's done online using the states in the file. Then, on your own, calculate "accuracy" based on the signal processing output and the TargetCode.

-Chad

Re: Replicate an experiment in a cursor task

Posted: 30 Sep 2013, 06:40
by rubmoe
Thanks for your response!

Yes, my data are in .mat format and I converted them to BCI2000.dat with the Matlab tools.
Actually seeing the cursor move would be nice, but since I have no experience in using Python I guess I better start with the cmdline tools.

I now ran the bci2000chain.mat, but I don't understand which variable the signal processing output is. Can you maybe help me with this step using one of the example files, e.g. eeg1_1.dat?

Thanks,
rubmoe

Re: Replicate an experiment in a cursor task

Posted: 30 Sep 2013, 11:49
by boulay
The output of bci2000chain is the output of all the designated filters. Note that some filters change the dimensions of the signal.

Using eeg1_1.dat as the example, running

Code: Select all

s = bci2000chain(filename, 'TransmissionFilter')
results in the following structure:

Code: Select all

s = 

             FileName: 'C:\Users\Chad\Desktop\tmp\eeg1_1.dat'
              DateStr: '2008-09-04 12:59:22'
              DateNum: 7.3366e+05
          FilterChain: {'TransmissionFilter'}
         ToolVersions: [1x1 struct]
           ShellInput: [1x175 char]
          ShellOutput: ''
            ChainTime: 0.7130
     ChainSpeedFactor: 172.6508
            Megabytes: 0.6741
                Parms: [1x1 struct]
               Blocks: 1231
      BlocksPerSecond: 10
      SecondsPerBlock: 0.1000
             Channels: 4
        ChannelLabels: {'1'  '2'  '3'  '4'}
             Elements: 16
        ElementLabels: {'1'  '2'  '3'  '4'  '5'  '6'  '7'  '8'  '9'  '10'  '11'  '12'  '13'  '14'  '15'  '16'}
        ElementValues: [0 0.0063 0.0125 0.0188 0.0250 0.0313 0.0375 0.0437 0.0500 0.0562 0.0625 0.0688 0.0750 0.0812 0.0875 0.0938]
          ElementUnit: 's'
          ElementRate: 160
                 Time: [1231x1 single]
             FullTime: [19696x1 single]
    FullElementValues: [19696x1 single]
               Signal: [19696x4 single]
               States: [1x1 struct]
Notice how s.Time has 1231 entries, or 1 per data block. s.FullTime has 19696 entries corresponding to 19696 samples. Note that 19696/1231 = 16, since there were 16 samples transmitted per block. At this stage in the processing, nothing has been done to the data so each sample is transmitted untouched.

Then try

Code: Select all

s = bci2000chain(filename, 'TransmissionFilter|SpatialFilter|ARFilter')
. Now s is:

Code: Select all

s = 

             FileName: 'C:\Users\Chad\Desktop\tmp\eeg1_1.dat'
              DateStr: '2008-09-04 12:59:22'
              DateNum: 7.3366e+05
          FilterChain: {'TransmissionFilter'  'SpatialFilter'  'ARFilter'}
         ToolVersions: [1x1 struct]
           ShellInput: [1x202 char]
          ShellOutput: ''
            ChainTime: 0.7660
     ChainSpeedFactor: 160.7050
            Megabytes: 0.4633
                Parms: [1x1 struct]
               Blocks: 1231
      BlocksPerSecond: 10
      SecondsPerBlock: 0.1000
             Channels: 4
        ChannelLabels: {'1'  '2'  '3'  '4'}
             Elements: 11
        ElementLabels: {'1'  '2'  '3'  '4'  '5'  '6'  '7'  '8'  '9'  '10'  '11'}
        ElementValues: [0 3 6 9 12 15 18 21 24 27 30]
          ElementUnit: 'Hz'
          ElementRate: 110
                 Time: [1231x1 single]
             FullTime: [1231x1 single]
    FullElementValues: [0 3 6 9 12 15 18 21 24 27 30]
               Signal: [1231x4x11 single]
               States: [1x1 struct]
The spectral transformation (ARFilter) transformed the data from 16 samples per block to 11 frequency amplitude-values per block.

If that doesn't answer your question then please be more specific.

Re: Replicate an experiment in a cursor task

Posted: 30 Sep 2013, 12:56
by mellinger
Hi,

there are many issues with replaying recorded signals in the BCI2000 online system.
If you want to do that for demonstration purposes, it's fine.

For offline data analysis of existing data, use analysis tools such as FieldTrip, or EEGlab. BCI2000 is not a data analysis tool. Rather, it is a system for interactive online feedback of real-time analyzed brain signals. In the online case, restrictions apply which do not exist in offline data analysis (causality, limited amount of past data for fast reaction, etc.). As a result, most methods for offline analysis will be much stronger than BCI2000 when it comes to detecting patterns in your data. Also, all methods available in BCI2000 are available in offline toolboxes as well, so you will be able to predict the possibility of online feedback from using a toolbox.

For more information about playing back existing data files in BCI2000, also see the notes in the FAQ:
http://www.bci2000.org/wiki/index.php/U ... orded_Data

Regards,
Juergen

Re: Replicate an experiment in a cursor task

Posted: 30 Sep 2013, 15:34
by rubmoe
Thanks again.

I am currently trying to understand all the different steps of a BCI session. Therefore I wanted to use recorded data to see how different filters etc affect the outcome. I think I now understood the idea of the bci2000chain.
But I still don't understand how to get the classifier output. I thought, in the end I'd have the classifier output for each time bin that I then could compare to the StimulusCode in order to determine the accuracy. Or am I wrong here?

Re: Replicate an experiment in a cursor task

Posted: 02 Oct 2013, 10:37
by rubmoe
I now tried to add more filters to the chain like in the flowchart here: http://www.bci2000.org/wiki/index.php/U ... n_Overview

This works with the chain TransmissionFilter|SpatialFilter|ARFilter|LinearClassifier|LPFilter|ExpressionFilter. But whenever I want to add the Normalizer I get the error message: Variable "TargetCode" does not exist.

However, when I leave the Normalizer out and add the ConditionalIntegrator it runs. But I still have problems with interpreting the results. I get a 1231x2 Matrix and assume that the first column represents the movement in the x-direction and the second column the y-axis. It says, the result of the ConditionalIntegrator is an output equivalent to cursor position on the feedback screen. But the values are between 0 and 420....

Re: Replicate an experiment in a cursor task

Posted: 02 Oct 2013, 21:29
by boulay
You can use BCI2000\tools\BCI2000Viewer\BCI2000Viewer.exe to look at your data file, including all of its states. You'll notice that none of the sample files have a TargetCode state.

The Normalizer default parameters indicate that the data buffer(s) are filled when TargetCode==X. This will not work as long as there is no TargetCode state.

So, you can try making a new data file that has the TargetCode state, or you can passing new Normalizer parameters to BCI2000chain so that it does not use TargetCode but instead uses another state. The latter is easier, IMO.

Re: Replicate an experiment in a cursor task

Posted: 03 Oct 2013, 07:30
by rubmoe
Ok, thanks.

Can you also answer my question concerning the ConditionalIntegrator's output? Does the minium value simply represent the very left and the maximum value the very right position of the cursor?

And is it also possible to classify the data continuously with the LinearClassifier?

Re: Replicate an experiment in a cursor task

Posted: 03 Oct 2013, 10:23
by boulay
rubmoe wrote:Can you also answer my question concerning the ConditionalIntegrator's output? Does the minium value simply represent the very left and the maximum value the very right position of the cursor?
I've never used it, I'm sorry. The default CursorTask has the cursor starting at the middle of the left edge. It moves left to right at a constant rate and the y-speed depends on the feature value (i.e., the output of the Normalizer). The trial ends when the cursor hits the right wall. I'm not sure what ConditionalIntegrator does. If it is similar, then one of your values (x dimension) should be constantly increasing then reset at the beginning of a trial.
rubmoe wrote:And is it also possible to classify the data continuously instead of segment-wise?
What do you mean by 'continuous' and what do you mean by 'segment'?

If by continuous you mean 'sample-by-sample', then yes it is possible but it is not useful to do offline and it is impractical to do online.

Online: Data are typically retrieved from the hardware as 'blocks' of samples. I assume this is what you meant by 'segment'. Please correct me if I'm wrong. (Note: The gtec simulink blocksets use 'frame' instead of 'block'.) The maximum rate at which you can retrieve the data, and therefore update the feedback, is the block rate. You typically define the block rate by setting the hardware sample rate and the number of samples per block. The online system must complete the signal processing pipeline and feedback within the time it takes to acquire one block. Otherwise the data will be collected faster than it is processed. It is up to the researcher to set the block rate to something high enough that the feedback seems smooth and continuous (~30 Hz) but not so high that the signal processing can't keep up.

Offline: EEG signal used in most BCIs don't modulate so quickly that you need millisecond resolution in your analysis. Also, a larger time resolution can often cause problems such as over-fitting models or loss of statistical power. It's up to the researcher to find a compromise between having a high enough time resolution in their signal processing to detect changes in their signal of interest and having a low enough time resolution to maximize analytical power.

Re: Replicate an experiment in a cursor task

Posted: 03 Oct 2013, 13:27
by rubmoe
thanks again for your help. but maybe i have to go back one step....

what I actually wanted to do is the following: i have some recorded EEG data with MI of the left and the right arm performed for 4 seconds each. now i'm trying to find a way to replay those signals and in the end determine the accuracy of the bci2000. So for each sample when the subject performed a MI I would like to see if the BCI would classify it correctly or not.

Isn't it the right way to do this with the bci2000chain? Or if it is, what's the next step after the linearClassifier? I just don't know how to interpret the output....

Re: Replicate an experiment in a cursor task

Posted: 03 Oct 2013, 22:27
by boulay
rubmoe wrote:in the end determine the accuracy of the bci2000.
rubmoe wrote:So for each sample [...] I would like to see if the BCI would classify it correctly or not.
These are two different things, but I think the confusion just stems from unfamiliarity with the terminology.

A 'sample' is the data from all your channels at a single time-point. Maybe you are collecting 256 samples per second.
A 'block' is a set of adjacent samples (maybe 32 samples) that are transmitted in aggregate from the hardware to the software. fs=256; blocksize=32; blockrate=256/32=8;
A 'trial' is a repeated experimental manipulation. In your case this is a 4 second period. You have two trial types or trial conditions. Do you have a rest period in between left MI and right MI periods? If so, the rest period is usually included in the definition of a 'trial' at the beginning, as sometimes the data from the resting period is used to normalize the following MI data.

Using the terminology above, my understanding is that you are most interested in classifying trials as left or right, then you will compare this classification result with the true trial condition to determine your accuracy. Is that correct?

Forgetting BCI2000 for a second, the most basic way you could do this is to calculate the FFT of the 4 seconds of data from each trial for a single channel (say C3), extract the amplitude at ~10Hz (aka mu rhythm), then regress C3 mu amplitude against true trial class (left=-1; right=+1). Using the coefficients determined by the regression, you can then calculate an expected class value for each trial and classify anything <0 as 'left' and anything >=0 as 'right'. Actually, you'd probably want to do a leave-one-out cross-validation, but let's keep this simple for now.

Instead of doing a single FFT from the 4 seconds, you can do 25 FFTs using 1-second windows with 87.5% overlap. You can use all 25 mu-rhythm amplitude values in a multivariate regression or you could simply average those values for each trial and use the per-trial sum in a univariate regression as above.

What happens online is closer to the following: treat each of the 25 values (* previous ~3 trials = 75 values) as independent measures and include them all in the univariate regression. Use the coefficients from the univariate regression to calculate a class-value for each new window of data. Sum all class-values within a trial. The sign of the summed class values is your trial class.

I'll leave it to you to determine how each Filter works, but the outputs are as follows:
TransmissionFilter: selected channel(s) data; 1 block (n_transmitted_channels x samples_per_block)
SpatialFilter: Re-referenced channel(s) data block. e.g., CAR, Laplacian, ICA (n_reref_chans x samples_per_block)
SpectralEstimation: Amplitude (or power) at each frequency for each channel calculated from the last WindowLength-s of data. (n_reref_chans x n_freqs)
LinearClassifier: N linear combination(s) of channel*frequency values, where N is the number of dimensions your BCI will use (typically 1). e.g., average of power from 8-12 Hz at C3 - average of power from 8-12 Hz at C4 = 1 output per block.
LPFilter: a smoothed version of the above.
ExpressionFilter: Can be anything. I like to take -10*log10(power) to convert my power (uV^2) to dB and have activation (usually a decrease in mu) be up.
Normalizer: A z-scored (sorta) version of the above, where the data used to calculate the mean and variance are the last BufferLength-s of values. You would describe two buffers, one for left MI trials and one for right MI trials, and these buffers are then combined when calculating the mean and variance. Each new value has this mean (NormalizerOffsets) subtracted and is scaled down by the variance (NormalizerGains).

Thus the output of the Normalizer has a mean of 0 and a variance of 1. Since each trial class is equally represented in the buffers, the mean of the buffers is assumed to represent something like a midpoint between the data from each class and thus the Normalizer output is >=0 for one class and <0 for another class. You simply sum the outputs of the Normalizer for a given trial and the sign of the result is that trial's class.

*Slight edits for clarity.

Re: Replicate an experiment in a cursor task

Posted: 04 Oct 2013, 10:20
by rubmoe
Thank you very much for the explanations. I'll try and see how far I get with this