diff --git a/.gitignore b/.gitignore index 7a456211..da011f09 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,9 @@ tests/*.tsv* # exclude report from check_my_code check_my_code_report.txt +# jupyter notebook checkpoints +.ipynb_checkpoints +*/.ipynb_checkpoints/* diff --git a/README.md b/README.md index bc0995c3..b28970f4 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ sub-090/ses-003/sub-090_ses-003_task-auditoryTask_run-023_events_date-2020072915 - [Installation](./docs/installation.md) - [How to use it](./docs/usage.md) +- [Jupyter notebooks](./notebooks/README.md) - [Functions description](./docs/functions_description.md) ## 3. Contributing diff --git a/binder/apt.txt b/binder/apt.txt new file mode 100644 index 00000000..1d1ab49a --- /dev/null +++ b/binder/apt.txt @@ -0,0 +1,2 @@ +octave +liboctave-dev diff --git a/binder/environment.yml b/binder/environment.yml new file mode 100644 index 00000000..19c10f83 --- /dev/null +++ b/binder/environment.yml @@ -0,0 +1,2 @@ +dependencies: +- octave_kernel diff --git a/binder/postBuild b/binder/postBuild new file mode 100644 index 00000000..e0ce1aac --- /dev/null +++ b/binder/postBuild @@ -0,0 +1,4 @@ +cd ${HOME} +cd lib/JSONio; mkoctfile --mex jsonread.c jsmn.c -DJSMN_PARENT_LINKS; cd .. +sudo apt-get install tree + diff --git a/notebooks/README.md b/notebooks/README.md new file mode 100644 index 00000000..9ec8cd6d --- /dev/null +++ b/notebooks/README.md @@ -0,0 +1,11 @@ +# README + +1. Make sure that you have Octave installed. +2. If you have Conda/Jupyter/pip installed, go to step 4. +3. Download the [Anaconda Installer](https://www.anaconda.com/products/individual) and install it. +4. Install [Octave kernel](https://pypi.org/project/octave-kernel/): + ``` + pip install octave_kernel + ``` +5. Run `jupyter notebook` in your terminal. `Octave` should appear on the list + for creating a new notebook. diff --git a/notebooks/basic_usage.ipynb b/notebooks/basic_usage.ipynb new file mode 100644 index 00000000..b4e16532 --- /dev/null +++ b/notebooks/basic_usage.ipynb @@ -0,0 +1,457 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "% add the relevant functions from the src folder\n", + "addpath(genpath(fullfile(pwd, '..', 'src')))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setting the parameters about our experiment\n", + "\n", + "We need to define the configuration structure where we put all our information." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "% define the folder where the data will be saved\n", + "cfg.dir.output = fullfile(pwd, 'output');\n", + "\n", + "% define the name of the task\n", + "cfg.task.name = 'test task';\n", + "\n", + "% can use the userInputs function to collect subject info\n", + "% cfg = userInputs;\n", + "\n", + "% Or we can add those information manually directly\n", + "cfg.subject.subjectNb = 1;\n", + "cfg.subject.runNb = 1;\n", + "\n", + "% if set to 'mri' then the data will be saved in the `func` folder\n", + "cfg.testingDevice = 'mri';\n", + "\n", + "% to keep things quiet\n", + "cfg.verbose = false;\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If set to `eeg` then the data will be saved in the `eeg` folder\n", + "\n", + "```\n", + "cfg.testingDevice = 'eeg';\n", + "```\n", + "\n", + "By default we assume you are running things on a behavioral PC with no eyetracker.\n", + "\n", + "```\n", + "cfg.eyeTracker = false;\n", + "cfg.testingDevice = 'pc';\n", + "```\n", + "\n", + "If the testing device is set to `pc` then the data will be saved in the `beh` folder" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "% We can then initialize all the other fields. The ones we have already filled in will not be overwritten\n", + "cfg = checkCFG(cfg)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating BIDS paths and filenames" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "% create the filenames: this include a step to check that all the information is there: createFilename calls \n", + "% checkCFG before running\n", + "[cfg] = createFilename(cfg);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Output directory for this subject\n", + "\n", + "Everything is put in a `source` folder to distinguish it from the `raw` that is usually reserved for the final 'true' BIDS data.\n", + "\n", + "You can see that even though it was not specified a session folder and label is added to the folder structure and the filename." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "cfg.dir.outputSubject" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### filename for events.tsv files\n", + "\n", + "You will note that we are converting the name of the task to a `camelCase`.\n", + "\n", + "`test task` --> `testTask`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cfg.fileName.events" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The fullpath for this file would be:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fullfile(...\n", + " cfg.dir.outputSubject, ...\n", + " cfg.fileName.modality, ...\n", + " cfg.fileName.events)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Saving data to tsv file" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Setting things up" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "clear cfg\n", + "\n", + "cfg.dir.output = fullfile(pwd, 'output');\n", + "\n", + "cfg.task.name = 'test task';\n", + "\n", + "cfg.subject.subjectNb = 1;\n", + "cfg.subject.runNb = 1;\n", + "\n", + "cfg.testingDevice = 'mri';\n", + "\n", + "cfg.verbose = false;\n", + "\n", + "% Create the filenames\n", + "[cfg] = createFilename(cfg);\n", + "\n", + "% We can define what extra columns we want in our tsv file beyond the \n", + "% BIDS holy trinity ('onset', 'duration', 'trial_type')\n", + "\n", + "% Say we want to keep track of the type of target that what presented during a trial and of its position\n", + "logFile.extraColumns = {'target_position', 'target_type'};\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Preparing your data dictionnary\n", + "\n", + "We can then initialize our logfile." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "logFile = saveEventsFile('init', cfg, logFile);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This will initialize a structure for each extra column." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "logFile.extraColumns.target_position.bids" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can then add information about each extraccolumns so they are saved in the JSON data dictionary that will go with the `.tsv` file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "logFile.extraColumns.target_position.bids.Description = 'Position of the target on the screen';\n", + "logFile.extraColumns.target_position.bids.Levels = {'left', 'center', 'right'};\n", + "\n", + "logFile.extraColumns.target_position.bids" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Adding data during the experiment\n", + "\n", + "A couple of things to do before you start the main loop of your experiment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "% create the output directory\n", + "mkdir(fullfile(cfg.dir.outputSubject, cfg.fileName.modality));\n", + "% open the file\n", + "logFile = saveEventsFile('open', cfg, logFile);\n", + "\n", + "% This prints the header of the tsv file.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Calling `tree` from the linux command line to see that we got what we want" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "!tree" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2 things to note.\n", + "\n", + "- A `_date-YYYYMMDDHHMM` suffix is appended to the file name. This is NOT accepted by the BIDS filenaming convention. This was introduced to prevent overwriting files by mistake in case some things were re-run. When you convert the source data into raw data, this date suffix should be removed (the `removeDataSuffix` is there for that).\n", + "\n", + "- A json file that stores your data dictionnary was created.\n", + "\n", + "Let's inpsect its content." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!cat output/source/sub-001/ses-001/func/sub-001_ses-001_task-testTask_run-001_events_date-*.json" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "% The information about 2 events that we want to save\n", + "% NOTE : If the user DOES NOT provide `onset`, `trial_type`, this events will be skipped.\n", + "\n", + "% Trial one\n", + "logFile(1,1).onset = 2;\n", + "logFile(1,1).trial_type = 'motion_up';\n", + "logFile(1,1).duration = 1;\n", + "logFile(1,1).target_position = 'left';\n", + "logFile(1,1).target_type = 'audio';\n", + "\n", + "% Trial two\n", + "logFile(2,1).onset = 6;\n", + "logFile(2,1).trial_type = 'static';\n", + "logFile(2,1).duration = 1.2;\n", + "logFile(2,1).target_position = 'right';\n", + "logFile(2,1).target_type = 'visual';\n", + "\n", + "% add those 2 events to the events.tsv file\n", + "\n", + "logFile = saveEventsFile('save', cfg, logFile);\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The content of the `logfile` structure is emptied after every save so saving again will only throw a warning." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "logFile = saveEventsFile('save', cfg, logFile);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Any missing information will be replaced by `n/a`.\n", + "\n", + "And will throw a warning. They can be quite verbose on octave." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "% New trial\n", + "logFile(1,1).onset = 10;\n", + "logFile(1,1).trial_type = 'motion_down';\n", + "logFile(1,1).duration = 1;\n", + "logFile(1,1).target_position = [];\n", + "logFile(1,1).target_type = 'audio';\n", + "\n", + "logFile = saveEventsFile('save', cfg, logFile);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can then close the file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "% close the file\n", + "cfg.verbose = true; % set verbose in true if you want to know where things are\n", + "saveEventsFile('close', cfg, logFile);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Inspecting the output\n", + "\n", + "The `bids-matlab` toolbox comes as part of CPP_BIDS as a submodule so you can easily load the content of this tsv file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "output = bids.util.tsvread( ...\n", + " fullfile(...\n", + " cfg.dir.outputSubject, ...\n", + " cfg.fileName.modality, ...\n", + " cfg.fileName.events))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Octave", + "language": "octave", + "name": "octave" + }, + "language_info": { + "file_extension": ".m", + "help_links": [ + { + "text": "GNU Octave", + "url": "https://www.gnu.org/software/octave/support.html" + }, + { + "text": "Octave Kernel", + "url": "https://github.com/Calysto/octave_kernel" + }, + { + "text": "MetaKernel Magics", + "url": "https://metakernel.readthedocs.io/en/latest/source/README.html" + } + ], + "mimetype": "text/x-octave", + "name": "octave", + "version": "4.2.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/notebooks/creating_BIDS_dataset.ipynb b/notebooks/creating_BIDS_dataset.ipynb new file mode 100644 index 00000000..061a5c0e --- /dev/null +++ b/notebooks/creating_BIDS_dataset.ipynb @@ -0,0 +1,344 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Creatring BIDS dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating a dataset description" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "addpath(genpath(fullfile(pwd, '..', 'src')));\n", + "\n", + "cfg.dir.output = fullfile(pwd, 'output');\n", + "\n", + "cfg.task.name = 'test task';\n", + "\n", + "cfg.subject.subjectNb = 1;\n", + "cfg.subject.runNb = 1;\n", + "\n", + "cfg.testingDevice = 'mri';\n", + "\n", + "cfg.eyetracker.do = 1;\n", + "\n", + "cfg.verbose = false;" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cfg.bids.datasetDescription.Name = 'my new bids data set';\n", + "cfg.bids.datasetDescription.BIDSVersion = '1.3';\n", + "cfg.bids.datasetDescription.License = 'CC-BY';\n", + "cfg.bids.datasetDescription.Authors = {'John Wick', 'Napoléon Bonaparte'};\n", + "cfg.bids.datasetDescription.Acknowledgements = '';\n", + "cfg.bids.datasetDescription.HowToAcknowledge = '';\n", + "cfg.bids.datasetDescription.Funding = {''};\n", + "cfg.bids.datasetDescription.ReferencesAndLinks = {''};\n", + "cfg.bids.datasetDescription.DatasetDOI = '';\n", + "\n", + "[cfg] = createFilename(cfg);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mkdir(fullfile(cfg.dir.outputSubject, cfg.fileName.modality));\n", + "\n", + "createDatasetDescription(cfg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!tree" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!cat output/source/dataset_description.json" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## fMRI" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### fMRI specific name suffixes\n", + "\n", + "You can specify some extra information for you fMRI runs as some of this information can be important for filenaming or to add to some JSON files." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cfg.mri.repetitionTime = 2.1;\n", + "cfg.mri.contrastEnhancement = [];\n", + "cfg.mri.phaseEncodingDirection = [];\n", + "cfg.mri.reconstruction = 'fast';\n", + "cfg.mri.echo = '1';\n", + "cfg.mri.acquisition = 'slow';" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "[cfg] = createFilename(cfg);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cfg.fileName.events" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Saving a `_bold.json` file\n", + "\n", + "There is bare minimum functionality to create a `_bold.json` file for fMRI runs: the information in there wil be that required to pass a BIDS validation but no more. You should REALLY get most of the information for `bold.json` from your favorite DICOM to Nifti converter\n", + "\n", + "This function is there because it can help put in JSON format some of the information that would NOT be available to a converter (usually information about your task)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "createBoldJson(cfg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!tree" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!cat output/source/sub-001/ses-001/func/sub-001*bold_date-*.json" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Adding extra info to your `_bold.json`\n", + "\n", + "One thing this function CAN do though is help you save some of the information about your experiment in a json format rather than a `.mat` format. The former being a simple text file you can easily open on any computer.\n", + "\n", + "For example if you have defined some other parameters in another structure." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%% Timing info\n", + "\n", + "% Time between blocs in secs\n", + "parameters.timing.IBI = 1.8 * 3; % 8;\n", + "% Time between events in secs\n", + "parameters.timing.ISI = 0.1;\n", + "% Number of seconds before the motion stimuli are presented\n", + "parameters.timing.onsetDelay = .1;\n", + "% Number of seconds after the end all the stimuli before ending the run\n", + "parameters.timing.endDelay = .1;\n", + "\n", + "parameters.timing.eventDuration = 0.8; % second\n", + "\n", + "%% Visual Stimulation\n", + "\n", + "% Speed in visual angles / second\n", + "parameters.dot.speed = 30;\n", + "% Coherence Level (0-1)\n", + "parameters.dot.coherence = 1;\n", + "% Number of dots per visual angle square.\n", + "parameters.dot.density = .1;\n", + "% Dot life time in seconds\n", + "parameters.dot.lifeTime = 10;\n", + "% proportion of dots killed per frame\n", + "parameters.dot.proportionKilledPerFrame = 0;\n", + "% Dot Size (dot width) in visual angles.\n", + "parameters.dot.size = 1;" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "createBoldJson(cfg, parameters)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!cat output/source/sub-001/ses-001/func/sub-001*bold_date-*.json" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Converting your data set from source to raw\n", + "\n", + "Before we do that, let's adding some actual data to the dataset." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "logFile.extraColumns = {'target_position', 'target_type'};\n", + "\n", + "logFile = saveEventsFile('open', cfg, logFile);\n", + "\n", + "logFile(1,1).onset = 2;\n", + "logFile(1,1).trial_type = 'motion_up';\n", + "logFile(1,1).duration = 1;\n", + "logFile(1,1).target_position = 'left';\n", + "logFile(1,1).target_type = 'audio';\n", + "\n", + "logFile(2,1).onset = 6;\n", + "logFile(2,1).trial_type = 'static';\n", + "logFile(2,1).duration = 1.2;\n", + "logFile(2,1).target_position = 'right';\n", + "logFile(2,1).target_type = 'visual';\n", + "\n", + "logFile = saveEventsFile('save', cfg, logFile);\n", + "\n", + "saveEventsFile('close', cfg, logFile);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "!tree" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The very basic conversion here mostly entails:\n", + "- adding empty README and CHANGES files in the `source` folder\n", + "- copying the content of the `source` folder into a `raw` folder\n", + "- removing the date suffixes\n", + "- compressing `_stim.tsv` into `_stim.tsv.gz`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "convertSourceToRaw(cfg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!tree" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Octave", + "language": "octave", + "name": "octave" + }, + "language_info": { + "file_extension": ".m", + "help_links": [ + { + "text": "GNU Octave", + "url": "https://www.gnu.org/software/octave/support.html" + }, + { + "text": "Octave Kernel", + "url": "https://github.com/Calysto/octave_kernel" + }, + { + "text": "MetaKernel Magics", + "url": "https://metakernel.readthedocs.io/en/latest/source/README.html" + } + ], + "mimetype": "text/x-octave", + "name": "octave", + "version": "4.2.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/notebooks/more_on_saving.ipynb b/notebooks/more_on_saving.ipynb new file mode 100644 index 00000000..c5842027 --- /dev/null +++ b/notebooks/more_on_saving.ipynb @@ -0,0 +1,207 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Saving arrays in the tsv file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "% add the relevant functions from the src folder\n", + "addpath(genpath(fullfile(pwd, '..', 'src')));\n", + "\n", + "cfg.dir.output = fullfile(pwd, 'output');\n", + "\n", + "cfg.task.name = 'test task';\n", + "\n", + "cfg.subject.subjectNb = 1;\n", + "cfg.subject.runNb = 1;\n", + "\n", + "cfg.testingDevice = 'mri';\n", + "\n", + "cfg.verbose = false;\n", + "\n", + "[cfg] = createFilename(cfg);\n", + "\n", + "mkdir(fullfile(cfg.dir.outputSubject, cfg.fileName.modality));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Saving arrays into the tsv file is possible as long as you specify the length of the variables you expect for each response field." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "logFile.extraColumns.target_position.length = 2;\n", + "logFile.extraColumns.target_type.length = 1;" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that it is also possible to directly open the file: things will be intialised automatically." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "logFile = saveEventsFile('open', cfg, logFile);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "% Trial one\n", + "logFile(1,1).onset = 2;\n", + "logFile(1,1).trial_type = 'motion_up';\n", + "logFile(1,1).duration = 1;\n", + "logFile(1,1).target_position = [2, 1];\n", + "logFile(1,1).target_type = 'visual';\n", + "\n", + "logFile = saveEventsFile('save', cfg, logFile);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Missing data in an array will be NaN padded." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "% Trial two\n", + "logFile(1,1).onset = 6;\n", + "logFile(1,1).trial_type = 'static';\n", + "logFile(1,1).duration = 1.2;\n", + "logFile(1,1).target_position = [3];\n", + "logFile(1,1).target_type = 'audio';\n", + "\n", + "logFile = saveEventsFile('save', cfg, logFile);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And extra information will dropped from arrays. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "% Trial three\n", + "logFile(1,1).onset = 6;\n", + "logFile(1,1).trial_type = 'static';\n", + "logFile(1,1).duration = 1.2;\n", + "logFile(1,1).target_position = [3, 4, 5];\n", + "logFile(1,1).target_type = 'touch';\n", + "\n", + "logFile = saveEventsFile('save', cfg, logFile);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "% close the file\n", + "cfg.verbose = true;\n", + "saveEventsFile('close', cfg, logFile);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In case you have many columns with only one value and a couple of columns with arrays.\n", + "\n", + "You would then do things this way" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "% define the extra columns: they will be added to the tsv files in the order the user input them\n", + "logFile.extraColumns = {'speed', 'is_fixation', };\n", + "\n", + "% initialize the logFile variable\n", + "[logFile] = saveEventsFile('init', cfg, logFile);\n", + "\n", + "% set the real length we really want\n", + "logFile.extraColumns.Speed.length = 12;\n", + "\n", + "% open the file\n", + "logFile = saveEventsFile('open', cfg, logFile);\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Saving `_stim.tsv` files" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Octave", + "language": "octave", + "name": "octave" + }, + "language_info": { + "file_extension": ".m", + "help_links": [ + { + "text": "GNU Octave", + "url": "https://www.gnu.org/software/octave/support.html" + }, + { + "text": "Octave Kernel", + "url": "https://github.com/Calysto/octave_kernel" + }, + { + "text": "MetaKernel Magics", + "url": "https://metakernel.readthedocs.io/en/latest/source/README.html" + } + ], + "mimetype": "text/x-octave", + "name": "octave", + "version": "4.2.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/src/subfun/returnHeaderName.m b/src/subfun/returnHeaderName.m index 86b2e144..e78865f1 100644 --- a/src/subfun/returnHeaderName.m +++ b/src/subfun/returnHeaderName.m @@ -7,5 +7,5 @@ else headerName = sprintf('%s_%02.0f', columnName, iCol); end - + end