Browse Source

Merge branch 'to_nix' of INT/multielectrode_grasp into master

sprenger 8 months ago
parent
commit
a6d508be09

+ 26 - 4
README.md

@@ -69,22 +69,44 @@ Download the latest release as a zip file by clicking on *Releases* on the main
 
 ## Repository structure
 
-### Directory datasets
-Contains the two data sets `i140703-001` and `l101210-001`. Original data files are provided in the Blackrock file format (.nev, .ns2, .ns5, .ns6, .ccf), e.g., `i140703-001.nev`, `i140703-001.ns6`,.... The files `i140703-001-03.nev` and `l101210-001-02.nev` contain offline spike sorted data for both datasets as opposed to the original recordings `i140703-001.nev` and `l101210-001.nev` which contain the same spikes, but unreliable sorting that should not be used. The files `i140703-001.odml` and `l101210-001.odml` contain extensive metadata describing the datasets in the odML format. The Excel files `i140703-001.xls` and `l101210-001.xls` contain the same information as in the odML for easy reading and browsing, however, they are not used by the loading routines. The odml.xsl is an XML schema that is required for viewing the odML files with a web browser.
+### Directory datasets_blackrock
+Contains the two original data sets `i140703-001` and `l101210-001`. Original data files are provided in the Blackrock file format (.nev, .ns2, .ns5, .ns6, .ccf), e.g., `i140703-001.nev`, `i140703-001.ns6`,.... The files `i140703-001-03.nev` and `l101210-001-02.nev` contain offline spike sorted data for both datasets as opposed to the original recordings `i140703-001.nev` and `l101210-001.nev` which contain the same spikes, but unreliable sorting that should not be used. The files `i140703-001.odml` and `l101210-001.odml` contain extensive metadata describing the datasets in the odML format. The Excel files `i140703-001.xls` and `l101210-001.xls` contain the same information as in the odML for easy reading and browsing, however, they are not used by the loading routines. The file `odml.xsl` is an XML schema that is required for viewing the odML files with a web browser. the file `example_blackrock.py` in the `code` subdirectory contains an example on using these nix files.These datasets can be 
+
+### Directory datasets_nix
+Contains a ready to use version data sets `i140703-001` and `l101210-001` in the Nix data format using a Neo structure. For practical purposes, we suggest using these files (instead of the original Blackrock files in `'datasets_blackrock`) for easier access to the data. Datasets can be loaded via the Neo command
+```
+import neo
+with neo.NixIO(nix_filename, mode='ro') as io:
+    block = io.read_block()
+```
+The resulting Neo objects are fully annotated and processed, similar to what the custom loading code in the `code` subdirectory will do. 
+
+In addition, `l101210-001.nix` will contain a downsampled version of the raw 30Khz data (i.e., the LFP) that is comparable to the online (hardware) downsampled ns2 LFP data that is supplied with `i140703-001`. Here, a 4th order, 250 Hz low pass Butterworth filter was applied using phase preservation and SOS, following a 30-fold downsampling. The structure of both files therefore becomes nearly identical. 
+
+Next to `l101210-001.nix` and `l101210-001.nix`, the directory also contains the files `l101210-001_no_raw.nix` and `l101210-001_no_raw.nix`. These files do not contain the raw electrode signals sampled at 30kHz, and are therefore considerably more light-weight in terms of file size.
+
+The code to produce the Nix files from the source files in the dataset directory is given in `convert_to_nix.py` in the `code` subdirectory. Also, the file `example_nix.py` in the `code` subdirectory contains an example on using these nix files.
 
 ### Directory datasets_matlab
 Contains the data and metadata output of the Python loading routines in the MATLAB .mat file format. These files are provided for convenience for MATLAB users, however, note that these files are not the original data files and contain a condensed, interpreted subset of the original data. Due to size restrictions of the MATLAB file format, the files `i140703-001_lfp-spikes.mat` and `l101210-001 _lfp-spikes.mat` contain only spikes and LFP data (for monkey N), while raw data is saved separately for each channel in correspondingly named files.
 
 ### Directory code
-Contains example code to help in loading and analyzing the data. The file `examply.py` is a Python script that acts as a tutorial for loading and plotting data. The scripts `data_overview_1.py` and `data_overview_2.py` reproduce the plots of the data found  in the publication. The files `neo_utils.py` and `odml_utils.py` contain useful utility routines to work with data and metadata. Finally, the file `example.m` contains a rudimentary MATLAB script demonstrating how to use the data provided in the .mat files.
+Contains example code to help in loading and analyzing the data based on the original data files contained in the `datasets_blackrock` folder. The file `example_blackrock.py` is a Python script that acts as a tutorial for loading and plotting data. Moreover, the file `example_nix.py` contains the same example using the nix files stored in the folder `datasets_nix`. The scripts `data_overview_1.py` and `data_overview_2.py` reproduce the plots of the data found  in the publication. The files `neo_utils.py` and `odml_utils.py` contain useful utility routines to work with data and metadata. Finally, the file `example_matlab.m` contains a rudimentary MATLAB script demonstrating how to use the data provided in the .mat files.
 
 To run the Python example code, download the release of this repository, and install the requirements in `code/requirements.txt`. Then, run the example via
 ```
    cd code
-   python example.py
+   python example_blackrock.py
+```
+or (the preferred option that does not require custom code in `code/reachgraspio`):
+```
+   cd code
+   python example_nix.py
 ```
 The script produces a figure saved in three different graphics file formats.
 
+Also, the file `convert_to_nix.py` contains code that produces the easy-to-use Nix files (in `datasets_nix`) from the original source data (in `datasets_blackrock`).
+
 ### Directory code/reachgraspio
 Contains the file `reachgraspio.py`, which contains the loading routine specific to the Reach-to-Grasp experiments in this repository. This loading routine merges the recorded data with metadata information from the odML files into a common Neo object. It is recommended that this loading routine is used in combination with the odML and Neo libraries (see below) to work on the data.
 

+ 15 - 0
code/Pipfile

@@ -0,0 +1,15 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+matplotlib = ">=3.0.3"
+neo = {version = ">=0.9.0,<0.11", extras = ["nixio"]}
+elephant = ">=0.9.0,<0.12"
+odml = ">=1.5.1,<1.6"
+
+[dev-packages]
+
+[requires]
+python_version = "3.11"

+ 644 - 0
code/Pipfile.lock

@@ -0,0 +1,644 @@
+{
+    "_meta": {
+        "hash": {
+            "sha256": "c0728df6a3f46314cad91ef093cb27ad4c67df59efecd856b65f07c41bf37cc6"
+        },
+        "pipfile-spec": 6,
+        "requires": {
+            "python_version": "3.11"
+        },
+        "sources": [
+            {
+                "name": "pypi",
+                "url": "https://pypi.org/simple",
+                "verify_ssl": true
+            }
+        ]
+    },
+    "default": {
+        "contourpy": {
+            "hashes": [
+                "sha256:052cc634bf903c604ef1a00a5aa093c54f81a2612faedaa43295809ffdde885e",
+                "sha256:084eaa568400cfaf7179b847ac871582199b1b44d5699198e9602ecbbb5f6104",
+                "sha256:0b6616375d7de55797d7a66ee7d087efe27f03d336c27cf1f32c02b8c1a5ac70",
+                "sha256:0b7b04ed0961647691cfe5d82115dd072af7ce8846d31a5fac6c142dcce8b882",
+                "sha256:143dde50520a9f90e4a2703f367cf8ec96a73042b72e68fcd184e1279962eb6f",
+                "sha256:17cfaf5ec9862bc93af1ec1f302457371c34e688fbd381f4035a06cd47324f48",
+                "sha256:181cbace49874f4358e2929aaf7ba84006acb76694102e88dd15af861996c16e",
+                "sha256:189ceb1525eb0655ab8487a9a9c41f42a73ba52d6789754788d1883fb06b2d8a",
+                "sha256:18a64814ae7bce73925131381603fff0116e2df25230dfc80d6d690aa6e20b37",
+                "sha256:1f0cbd657e9bde94cd0e33aa7df94fb73c1ab7799378d3b3f902eb8eb2e04a3a",
+                "sha256:1f795597073b09d631782e7245016a4323cf1cf0b4e06eef7ea6627e06a37ff2",
+                "sha256:25ae46595e22f93592d39a7eac3d638cda552c3e1160255258b695f7b58e5655",
+                "sha256:27bc79200c742f9746d7dd51a734ee326a292d77e7d94c8af6e08d1e6c15d545",
+                "sha256:2b836d22bd2c7bb2700348e4521b25e077255ebb6ab68e351ab5aa91ca27e027",
+                "sha256:30f511c05fab7f12e0b1b7730ebdc2ec8deedcfb505bc27eb570ff47c51a8f15",
+                "sha256:317267d915490d1e84577924bd61ba71bf8681a30e0d6c545f577363157e5e94",
+                "sha256:397b0ac8a12880412da3551a8cb5a187d3298a72802b45a3bd1805e204ad8439",
+                "sha256:438ba416d02f82b692e371858143970ed2eb6337d9cdbbede0d8ad9f3d7dd17d",
+                "sha256:53cc3a40635abedbec7f1bde60f8c189c49e84ac180c665f2cd7c162cc454baa",
+                "sha256:5d123a5bc63cd34c27ff9c7ac1cd978909e9c71da12e05be0231c608048bb2ae",
+                "sha256:62013a2cf68abc80dadfd2307299bfa8f5aa0dcaec5b2954caeb5fa094171103",
+                "sha256:89f06eff3ce2f4b3eb24c1055a26981bffe4e7264acd86f15b97e40530b794bc",
+                "sha256:90c81f22b4f572f8a2110b0b741bb64e5a6427e0a198b2cdc1fbaf85f352a3aa",
+                "sha256:911ff4fd53e26b019f898f32db0d4956c9d227d51338fb3b03ec72ff0084ee5f",
+                "sha256:9382a1c0bc46230fb881c36229bfa23d8c303b889b788b939365578d762b5c18",
+                "sha256:9f2931ed4741f98f74b410b16e5213f71dcccee67518970c42f64153ea9313b9",
+                "sha256:a67259c2b493b00e5a4d0f7bfae51fb4b3371395e47d079a4446e9b0f4d70e76",
+                "sha256:a698c6a7a432789e587168573a864a7ea374c6be8d4f31f9d87c001d5a843493",
+                "sha256:bc00bb4225d57bff7ebb634646c0ee2a1298402ec10a5fe7af79df9a51c1bfd9",
+                "sha256:bcb41692aa09aeb19c7c213411854402f29f6613845ad2453d30bf421fe68fed",
+                "sha256:d4f26b25b4f86087e7d75e63212756c38546e70f2a92d2be44f80114826e1cd4",
+                "sha256:d551f3a442655f3dcc1285723f9acd646ca5858834efeab4598d706206b09c9f",
+                "sha256:dffcc2ddec1782dd2f2ce1ef16f070861af4fb78c69862ce0aab801495dda6a3",
+                "sha256:e53046c3863828d21d531cc3b53786e6580eb1ba02477e8681009b6aa0870b21",
+                "sha256:e5cec36c5090e75a9ac9dbd0ff4a8cf7cecd60f1b6dc23a374c7d980a1cd710e",
+                "sha256:e7a117ce7df5a938fe035cad481b0189049e8d92433b4b33aa7fc609344aafa1",
+                "sha256:e94bef2580e25b5fdb183bf98a2faa2adc5b638736b2c0a4da98691da641316a",
+                "sha256:ed614aea8462735e7d70141374bd7650afd1c3f3cb0c2dbbcbe44e14331bf002",
+                "sha256:fb3b7d9e6243bfa1efb93ccfe64ec610d85cfe5aec2c25f97fbbd2e58b531256"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==1.1.0"
+        },
+        "cycler": {
+            "hashes": [
+                "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3",
+                "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"
+            ],
+            "markers": "python_version >= '3.6'",
+            "version": "==0.11.0"
+        },
+        "docopt": {
+            "hashes": [
+                "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"
+            ],
+            "version": "==0.6.2"
+        },
+        "elephant": {
+            "hashes": [
+                "sha256:08d3daa6792c6c3aadb431d23e17f7fb53b1bc5df1c022bf6b4552b1cb7c5fe1",
+                "sha256:0c6aa6e1cfdad87e187af5f31b6e1809d58d663fafd49c08b666a6c4864ca050",
+                "sha256:1315a9494704d586b7e7edabf4bda55ac8cb0aefa7134af9f43e079ce934baa0",
+                "sha256:3f69eaf0fb9f8fce0e00163914ffd9ccbba586851dc6b14123a943b4c850db3c",
+                "sha256:5f3b3937200b08a79801712738ef0ca7bf520b441880d0793dd052437dc52548",
+                "sha256:68a50fd587f8734282196c40c8e13899b0554993ed23260bd0e6087da8eb1e43",
+                "sha256:6cae476e00aaf4b8ef0ce0526159579a94ce26f1f987ee943354e7a95fa17548",
+                "sha256:7cf0826192ae007515a50d9275d3a337df99c0b7579e9b02ebf5e8aa0a00743a",
+                "sha256:912d1ebb29ea211d84211948ff7fb111604028043e7d894f7f70a14d0e52dafa",
+                "sha256:92bac510fbabdf82b1e935f4117d5f9c0f2d9798e2ddc6f270b2b1a0b0654553",
+                "sha256:a27ef5312d47bbe86b619aaa9ea7b4dc90a4c26607865ba1c03b98920c9890fc",
+                "sha256:e011a59c2a90e3d39b71a086cc4d1997f692cfdd0546ddac31b977209e6fabcd",
+                "sha256:f8759fff0bbb136ae4ffc8d1eacadeea8ba56610d705c3bf207de87ada3ba240"
+            ],
+            "index": "pypi",
+            "version": "==0.11.2"
+        },
+        "fonttools": {
+            "hashes": [
+                "sha256:1df1b6f4c7c4bc8201eb47f3b268adbf2539943aa43c400f84556557e3e109c0",
+                "sha256:2a22b2c425c698dcd5d6b0ff0b566e8e9663172118db6fd5f1941f9b8063da9b",
+                "sha256:33191f062549e6bb1a4782c22a04ebd37009c09360e2d6686ac5083774d06d95",
+                "sha256:38cdecd8f1fd4bf4daae7fed1b3170dfc1b523388d6664b2204b351820aa78a7",
+                "sha256:3ae64303ba670f8959fdaaa30ba0c2dabe75364fdec1caeee596c45d51ca3425",
+                "sha256:3d1f9471134affc1e3b1b806db6e3e2ad3fa99439e332f1881a474c825101096",
+                "sha256:4e3334d51f0e37e2c6056e67141b2adabc92613a968797e2571ca8a03bd64773",
+                "sha256:4edc795533421e98f60acee7d28fc8d941ff5ac10f44668c9c3635ad72ae9045",
+                "sha256:547ab36a799dded58a46fa647266c24d0ed43a66028cd1cd4370b246ad426cac",
+                "sha256:59eba8b2e749a1de85760da22333f3d17c42b66e03758855a12a2a542723c6e7",
+                "sha256:704bccd69b0abb6fab9f5e4d2b75896afa48b427caa2c7988792a2ffce35b441",
+                "sha256:73ef0bb5d60eb02ba4d3a7d23ada32184bd86007cb2de3657cfcb1175325fc83",
+                "sha256:7763316111df7b5165529f4183a334aa24c13cdb5375ffa1dc8ce309c8bf4e5c",
+                "sha256:849ec722bbf7d3501a0e879e57dec1fc54919d31bff3f690af30bb87970f9784",
+                "sha256:891cfc5a83b0307688f78b9bb446f03a7a1ad981690ac8362f50518bc6153975",
+                "sha256:952cb405f78734cf6466252fec42e206450d1a6715746013f64df9cbd4f896fa",
+                "sha256:a7bbb290d13c6dd718ec2c3db46fe6c5f6811e7ea1e07f145fd8468176398224",
+                "sha256:a9b3cc10dc9e0834b6665fd63ae0c6964c6bc3d7166e9bc84772e0edd09f9fa2",
+                "sha256:aaaef294d8e411f0ecb778a0aefd11bb5884c9b8333cc1011bdaf3b58ca4bd75",
+                "sha256:afce2aeb80be72b4da7dd114f10f04873ff512793d13ce0b19d12b2a4c44c0f0",
+                "sha256:b0938ebbeccf7c80bb9a15e31645cf831572c3a33d5cc69abe436e7000c61b14",
+                "sha256:b2d1ee95be42b80d1f002d1ee0a51d7a435ea90d36f1a5ae331be9962ee5a3f1",
+                "sha256:b927e5f466d99c03e6e20961946314b81d6e3490d95865ef88061144d9f62e38",
+                "sha256:bdd729744ae7ecd7f7311ad25d99da4999003dcfe43b436cf3c333d4e68de73d",
+                "sha256:c2071267deaa6d93cb16288613419679c77220543551cbe61da02c93d92df72f",
+                "sha256:cac73bbef7734e78c60949da11c4903ee5837168e58772371bd42a75872f4f82",
+                "sha256:da2c2964bdc827ba6b8a91dc6de792620be4da3922c4cf0599f36a488c07e2b2",
+                "sha256:e16a9449f21a93909c5be2f5ed5246420f2316e94195dbfccb5238aaa38f9751",
+                "sha256:e5c2b0a95a221838991e2f0e455dec1ca3a8cc9cd54febd68cc64d40fdb83669",
+                "sha256:ec453a45778524f925a8f20fd26a3326f398bfc55d534e37bab470c5e415caa1",
+                "sha256:edee0900cf0eedb29d17c7876102d6e5a91ee333882b1f5abc83e85b934cadb5",
+                "sha256:f14f3ccea4cc7dd1b277385adf3c3bf18f9860f87eab9c2fb650b0af16800f55",
+                "sha256:f240d9adf0583ac8fc1646afe7f4ac039022b6f8fa4f1575a2cfa53675360b69",
+                "sha256:f48602c0b3fd79cd83a34c40af565fe6db7ac9085c8823b552e6e751e3a5b8be"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==4.41.1"
+        },
+        "h5py": {
+            "hashes": [
+                "sha256:12aa556d540f11a2cae53ea7cfb94017353bd271fb3962e1296b342f6550d1b8",
+                "sha256:23e74b878bbe1653ab34ca49b83cac85529cd0b36b9d625516c5830cc5ca2eac",
+                "sha256:36408f8c62f50007d14e000f9f3acf77e103b9e932c114cbe52a3089e50ebf94",
+                "sha256:3f457089c5d524b7998e3649bc63240679b8fb0a3859ea53bbb06841f3d755f1",
+                "sha256:54f01202cdea754ab4227dd27014bdbd561a4bbe4b631424fd812f7c2ce9c6ac",
+                "sha256:551e358db05a874a0f827b22e95b30092f2303edc4b91bb62ad2f10e0236e1a0",
+                "sha256:64acceaf6aff92af091a4b83f6dee3cf8d3061f924a6bb3a33eb6c4658a8348b",
+                "sha256:6822a814b9d8b8363ff102f76ea8d026f0ca25850bb579d85376029ee3e73b93",
+                "sha256:78e44686334cbbf2dd21d9df15823bc38663f27a3061f6a032c68a3e30c47bf7",
+                "sha256:79bbca34696c6f9eeeb36a91776070c49a060b2879828e2c8fa6c58b8ed10dd1",
+                "sha256:804c7fb42a34c8ab3a3001901c977a5c24d2e9c586a0f3e7c0a389130b4276fc",
+                "sha256:8d9492391ff5c3c80ec30ae2fe82a3f0efd1e750833739c25b0d090e3be1b095",
+                "sha256:95f7a745efd0d56076999b52e8da5fad5d30823bac98b59c68ae75588d09991a",
+                "sha256:9da9e7e63376c32704e37ad4cea2dceae6964cee0d8515185b3ab9cbd6b947bc",
+                "sha256:a4e20897c88759cbcbd38fb45b507adc91af3e0f67722aa302d71f02dd44d286",
+                "sha256:a6284061f3214335e1eec883a6ee497dbe7a79f19e6a57fed2dd1f03acd5a8cb",
+                "sha256:d97409e17915798029e297a84124705c8080da901307ea58f29234e09b073ddc",
+                "sha256:dbf5225543ca35ce9f61c950b73899a82be7ba60d58340e76d0bd42bf659235a",
+                "sha256:e604db6521c1e367c6bd7fad239c847f53cc46646f2d2651372d05ae5e95f817",
+                "sha256:eb7bdd5e601dd1739698af383be03f3dad0465fe67184ebd5afca770f50df9d6",
+                "sha256:f68b41efd110ce9af1cbe6fa8af9f4dcbadace6db972d30828b911949e28fadd"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==3.9.0"
+        },
+        "isodate": {
+            "hashes": [
+                "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96",
+                "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9"
+            ],
+            "version": "==0.6.1"
+        },
+        "kiwisolver": {
+            "hashes": [
+                "sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b",
+                "sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166",
+                "sha256:1041feb4cda8708ce73bb4dcb9ce1ccf49d553bf87c3954bdfa46f0c3f77252c",
+                "sha256:10ee06759482c78bdb864f4109886dff7b8a56529bc1609d4f1112b93fe6423c",
+                "sha256:1d1573129aa0fd901076e2bfb4275a35f5b7aa60fbfb984499d661ec950320b0",
+                "sha256:283dffbf061a4ec60391d51e6155e372a1f7a4f5b15d59c8505339454f8989e4",
+                "sha256:28bc5b299f48150b5f822ce68624e445040595a4ac3d59251703779836eceff9",
+                "sha256:2a66fdfb34e05b705620dd567f5a03f239a088d5a3f321e7b6ac3239d22aa286",
+                "sha256:2e307eb9bd99801f82789b44bb45e9f541961831c7311521b13a6c85afc09767",
+                "sha256:2e407cb4bd5a13984a6c2c0fe1845e4e41e96f183e5e5cd4d77a857d9693494c",
+                "sha256:2f5e60fabb7343a836360c4f0919b8cd0d6dbf08ad2ca6b9cf90bf0c76a3c4f6",
+                "sha256:36dafec3d6d6088d34e2de6b85f9d8e2324eb734162fba59d2ba9ed7a2043d5b",
+                "sha256:3fe20f63c9ecee44560d0e7f116b3a747a5d7203376abeea292ab3152334d004",
+                "sha256:41dae968a94b1ef1897cb322b39360a0812661dba7c682aa45098eb8e193dbdf",
+                "sha256:4bd472dbe5e136f96a4b18f295d159d7f26fd399136f5b17b08c4e5f498cd494",
+                "sha256:4ea39b0ccc4f5d803e3337dd46bcce60b702be4d86fd0b3d7531ef10fd99a1ac",
+                "sha256:5853eb494c71e267912275e5586fe281444eb5e722de4e131cddf9d442615626",
+                "sha256:5bce61af018b0cb2055e0e72e7d65290d822d3feee430b7b8203d8a855e78766",
+                "sha256:6295ecd49304dcf3bfbfa45d9a081c96509e95f4b9d0eb7ee4ec0530c4a96514",
+                "sha256:62ac9cc684da4cf1778d07a89bf5f81b35834cb96ca523d3a7fb32509380cbf6",
+                "sha256:70e7c2e7b750585569564e2e5ca9845acfaa5da56ac46df68414f29fea97be9f",
+                "sha256:7577c1987baa3adc4b3c62c33bd1118c3ef5c8ddef36f0f2c950ae0b199e100d",
+                "sha256:75facbe9606748f43428fc91a43edb46c7ff68889b91fa31f53b58894503a191",
+                "sha256:787518a6789009c159453da4d6b683f468ef7a65bbde796bcea803ccf191058d",
+                "sha256:78d6601aed50c74e0ef02f4204da1816147a6d3fbdc8b3872d263338a9052c51",
+                "sha256:7c43e1e1206cd421cd92e6b3280d4385d41d7166b3ed577ac20444b6995a445f",
+                "sha256:81e38381b782cc7e1e46c4e14cd997ee6040768101aefc8fa3c24a4cc58e98f8",
+                "sha256:841293b17ad704d70c578f1f0013c890e219952169ce8a24ebc063eecf775454",
+                "sha256:872b8ca05c40d309ed13eb2e582cab0c5a05e81e987ab9c521bf05ad1d5cf5cb",
+                "sha256:877272cf6b4b7e94c9614f9b10140e198d2186363728ed0f701c6eee1baec1da",
+                "sha256:8c808594c88a025d4e322d5bb549282c93c8e1ba71b790f539567932722d7bd8",
+                "sha256:8ed58b8acf29798b036d347791141767ccf65eee7f26bde03a71c944449e53de",
+                "sha256:91672bacaa030f92fc2f43b620d7b337fd9a5af28b0d6ed3f77afc43c4a64b5a",
+                "sha256:968f44fdbf6dd757d12920d63b566eeb4d5b395fd2d00d29d7ef00a00582aac9",
+                "sha256:9f85003f5dfa867e86d53fac6f7e6f30c045673fa27b603c397753bebadc3008",
+                "sha256:a553dadda40fef6bfa1456dc4be49b113aa92c2a9a9e8711e955618cd69622e3",
+                "sha256:a68b62a02953b9841730db7797422f983935aeefceb1679f0fc85cbfbd311c32",
+                "sha256:abbe9fa13da955feb8202e215c4018f4bb57469b1b78c7a4c5c7b93001699938",
+                "sha256:ad881edc7ccb9d65b0224f4e4d05a1e85cf62d73aab798943df6d48ab0cd79a1",
+                "sha256:b1792d939ec70abe76f5054d3f36ed5656021dcad1322d1cc996d4e54165cef9",
+                "sha256:b428ef021242344340460fa4c9185d0b1f66fbdbfecc6c63eff4b7c29fad429d",
+                "sha256:b533558eae785e33e8c148a8d9921692a9fe5aa516efbdff8606e7d87b9d5824",
+                "sha256:ba59c92039ec0a66103b1d5fe588fa546373587a7d68f5c96f743c3396afc04b",
+                "sha256:bc8d3bd6c72b2dd9decf16ce70e20abcb3274ba01b4e1c96031e0c4067d1e7cd",
+                "sha256:bc9db8a3efb3e403e4ecc6cd9489ea2bac94244f80c78e27c31dcc00d2790ac2",
+                "sha256:bf7d9fce9bcc4752ca4a1b80aabd38f6d19009ea5cbda0e0856983cf6d0023f5",
+                "sha256:c2dbb44c3f7e6c4d3487b31037b1bdbf424d97687c1747ce4ff2895795c9bf69",
+                "sha256:c79ebe8f3676a4c6630fd3f777f3cfecf9289666c84e775a67d1d358578dc2e3",
+                "sha256:c97528e64cb9ebeff9701e7938653a9951922f2a38bd847787d4a8e498cc83ae",
+                "sha256:d0611a0a2a518464c05ddd5a3a1a0e856ccc10e67079bb17f265ad19ab3c7597",
+                "sha256:d06adcfa62a4431d404c31216f0f8ac97397d799cd53800e9d3efc2fbb3cf14e",
+                "sha256:d41997519fcba4a1e46eb4a2fe31bc12f0ff957b2b81bac28db24744f333e955",
+                "sha256:d5b61785a9ce44e5a4b880272baa7cf6c8f48a5180c3e81c59553ba0cb0821ca",
+                "sha256:da152d8cdcab0e56e4f45eb08b9aea6455845ec83172092f09b0e077ece2cf7a",
+                "sha256:da7e547706e69e45d95e116e6939488d62174e033b763ab1496b4c29b76fabea",
+                "sha256:db5283d90da4174865d520e7366801a93777201e91e79bacbac6e6927cbceede",
+                "sha256:db608a6757adabb32f1cfe6066e39b3706d8c3aa69bbc353a5b61edad36a5cb4",
+                "sha256:e0ea21f66820452a3f5d1655f8704a60d66ba1191359b96541eaf457710a5fc6",
+                "sha256:e7da3fec7408813a7cebc9e4ec55afed2d0fd65c4754bc376bf03498d4e92686",
+                "sha256:e92a513161077b53447160b9bd8f522edfbed4bd9759e4c18ab05d7ef7e49408",
+                "sha256:ecb1fa0db7bf4cff9dac752abb19505a233c7f16684c5826d1f11ebd9472b871",
+                "sha256:efda5fc8cc1c61e4f639b8067d118e742b812c930f708e6667a5ce0d13499e29",
+                "sha256:f0a1dbdb5ecbef0d34eb77e56fcb3e95bbd7e50835d9782a45df81cc46949750",
+                "sha256:f0a71d85ecdd570ded8ac3d1c0f480842f49a40beb423bb8014539a9f32a5897",
+                "sha256:f4f270de01dd3e129a72efad823da90cc4d6aafb64c410c9033aba70db9f1ff0",
+                "sha256:f6cb459eea32a4e2cf18ba5fcece2dbdf496384413bc1bae15583f19e567f3b2",
+                "sha256:f8ad8285b01b0d4695102546b342b493b3ccc6781fc28c8c6a1bb63e95d22f09",
+                "sha256:f9f39e2f049db33a908319cf46624a569b36983c7c78318e9726a4cb8923b26c"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==1.4.4"
+        },
+        "lxml": {
+            "hashes": [
+                "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3",
+                "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d",
+                "sha256:081d32421db5df44c41b7f08a334a090a545c54ba977e47fd7cc2deece78809a",
+                "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120",
+                "sha256:0bfd0767c5c1de2551a120673b72e5d4b628737cb05414f03c3277bf9bed3305",
+                "sha256:0c0850c8b02c298d3c7006b23e98249515ac57430e16a166873fc47a5d549287",
+                "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23",
+                "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52",
+                "sha256:1247694b26342a7bf47c02e513d32225ededd18045264d40758abeb3c838a51f",
+                "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4",
+                "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584",
+                "sha256:1509dd12b773c02acd154582088820893109f6ca27ef7291b003d0e81666109f",
+                "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693",
+                "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef",
+                "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5",
+                "sha256:23eed6d7b1a3336ad92d8e39d4bfe09073c31bfe502f20ca5116b2a334f8ec02",
+                "sha256:25f32acefac14ef7bd53e4218fe93b804ef6f6b92ffdb4322bb6d49d94cad2bc",
+                "sha256:2c74524e179f2ad6d2a4f7caf70e2d96639c0954c943ad601a9e146c76408ed7",
+                "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da",
+                "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a",
+                "sha256:3e9bdd30efde2b9ccfa9cb5768ba04fe71b018a25ea093379c857c9dad262c40",
+                "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8",
+                "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd",
+                "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601",
+                "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c",
+                "sha256:48d6ed886b343d11493129e019da91d4039826794a3e3027321c56d9e71505be",
+                "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2",
+                "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c",
+                "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129",
+                "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc",
+                "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2",
+                "sha256:4f1026bc732b6a7f96369f7bfe1a4f2290fb34dce00d8644bc3036fb351a4ca1",
+                "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7",
+                "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d",
+                "sha256:50baa9c1c47efcaef189f31e3d00d697c6d4afda5c3cde0302d063492ff9b477",
+                "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d",
+                "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e",
+                "sha256:56dc1f1ebccc656d1b3ed288f11e27172a01503fc016bcabdcbc0978b19352b7",
+                "sha256:578695735c5a3f51569810dfebd05dd6f888147a34f0f98d4bb27e92b76e05c2",
+                "sha256:57aba1bbdf450b726d58b2aea5fe47c7875f5afb2c4a23784ed78f19a0462574",
+                "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf",
+                "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b",
+                "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98",
+                "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12",
+                "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42",
+                "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35",
+                "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d",
+                "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce",
+                "sha256:704f61ba8c1283c71b16135caf697557f5ecf3e74d9e453233e4771d68a1f42d",
+                "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f",
+                "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db",
+                "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4",
+                "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694",
+                "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac",
+                "sha256:8df133a2ea5e74eef5e8fc6f19b9e085f758768a16e9877a60aec455ed2609b2",
+                "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7",
+                "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96",
+                "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d",
+                "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b",
+                "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a",
+                "sha256:9a92d3faef50658dd2c5470af249985782bf754c4e18e15afb67d3ab06233f13",
+                "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340",
+                "sha256:9e28c51fa0ce5674be9f560c6761c1b441631901993f76700b1b30ca6c8378d6",
+                "sha256:aca086dc5f9ef98c512bac8efea4483eb84abbf926eaeedf7b91479feb092458",
+                "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c",
+                "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c",
+                "sha256:b4e4bc18382088514ebde9328da057775055940a1f2e18f6ad2d78aa0f3ec5b9",
+                "sha256:b6420a005548ad52154c8ceab4a1290ff78d757f9e5cbc68f8c77089acd3c432",
+                "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991",
+                "sha256:bb3bb49c7a6ad9d981d734ef7c7193bc349ac338776a0360cc671eaee89bcf69",
+                "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf",
+                "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb",
+                "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b",
+                "sha256:c41bfca0bd3532d53d16fd34d20806d5c2b1ace22a2f2e4c0008570bf2c58833",
+                "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76",
+                "sha256:cdb650fc86227eba20de1a29d4b2c1bfe139dc75a0669270033cb2ea3d391b85",
+                "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e",
+                "sha256:d27be7405547d1f958b60837dc4c1007da90b8b23f54ba1f8b728c78fdb19d50",
+                "sha256:d37017287a7adb6ab77e1c5bee9bcf9660f90ff445042b790402a654d2ad81d8",
+                "sha256:d3ff32724f98fbbbfa9f49d82852b159e9784d6094983d9a8b7f2ddaebb063d4",
+                "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b",
+                "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5",
+                "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190",
+                "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7",
+                "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa",
+                "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0",
+                "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9",
+                "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0",
+                "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b",
+                "sha256:fc9b106a1bf918db68619fdcd6d5ad4f972fdd19c01d19bdb6bf63f3589a9ec5",
+                "sha256:fcdd00edfd0a3001e0181eab3e63bd5c74ad3e67152c84f93f13769a40e073a7",
+                "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"
+            ],
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+            "version": "==4.9.3"
+        },
+        "matplotlib": {
+            "hashes": [
+                "sha256:070f8dddd1f5939e60aacb8fa08f19551f4b0140fab16a3669d5cd6e9cb28fc8",
+                "sha256:0c3cca3e842b11b55b52c6fb8bd6a4088693829acbfcdb3e815fa9b7d5c92c1b",
+                "sha256:0f506a1776ee94f9e131af1ac6efa6e5bc7cb606a3e389b0ccb6e657f60bb676",
+                "sha256:12f01b92ecd518e0697da4d97d163b2b3aa55eb3eb4e2c98235b3396d7dad55f",
+                "sha256:152ee0b569a37630d8628534c628456b28686e085d51394da6b71ef84c4da201",
+                "sha256:1c308b255efb9b06b23874236ec0f10f026673ad6515f602027cc8ac7805352d",
+                "sha256:1cd120fca3407a225168238b790bd5c528f0fafde6172b140a2f3ab7a4ea63e9",
+                "sha256:20f844d6be031948148ba49605c8b96dfe7d3711d1b63592830d650622458c11",
+                "sha256:23fb1750934e5f0128f9423db27c474aa32534cec21f7b2153262b066a581fd1",
+                "sha256:2699f7e73a76d4c110f4f25be9d2496d6ab4f17345307738557d345f099e07de",
+                "sha256:26bede320d77e469fdf1bde212de0ec889169b04f7f1179b8930d66f82b30cbc",
+                "sha256:2ecb5be2b2815431c81dc115667e33da0f5a1bcf6143980d180d09a717c4a12e",
+                "sha256:2f8e4a49493add46ad4a8c92f63e19d548b2b6ebbed75c6b4c7f46f57d36cdd1",
+                "sha256:305e3da477dc8607336ba10bac96986d6308d614706cae2efe7d3ffa60465b24",
+                "sha256:30e1409b857aa8a747c5d4f85f63a79e479835f8dffc52992ac1f3f25837b544",
+                "sha256:318c89edde72ff95d8df67d82aca03861240512994a597a435a1011ba18dbc7f",
+                "sha256:35d74ebdb3f71f112b36c2629cf32323adfbf42679e2751252acd468f5001c07",
+                "sha256:50e0a55ec74bf2d7a0ebf50ac580a209582c2dd0f7ab51bc270f1b4a0027454e",
+                "sha256:5dea00b62d28654b71ca92463656d80646675628d0828e08a5f3b57e12869e13",
+                "sha256:60c521e21031632aa0d87ca5ba0c1c05f3daacadb34c093585a0be6780f698e4",
+                "sha256:6515e878f91894c2e4340d81f0911857998ccaf04dbc1bba781e3d89cbf70608",
+                "sha256:6d2ff3c984b8a569bc1383cd468fc06b70d7b59d5c2854ca39f1436ae8394117",
+                "sha256:71667eb2ccca4c3537d9414b1bc00554cb7f91527c17ee4ec38027201f8f1603",
+                "sha256:717157e61b3a71d3d26ad4e1770dc85156c9af435659a25ee6407dc866cb258d",
+                "sha256:71f7a8c6b124e904db550f5b9fe483d28b896d4135e45c4ea381ad3b8a0e3256",
+                "sha256:936bba394682049919dda062d33435b3be211dc3dcaa011e09634f060ec878b2",
+                "sha256:a1733b8e84e7e40a9853e505fe68cc54339f97273bdfe6f3ed980095f769ddc7",
+                "sha256:a2c1590b90aa7bd741b54c62b78de05d4186271e34e2377e0289d943b3522273",
+                "sha256:a7e28d6396563955f7af437894a36bf2b279462239a41028323e04b85179058b",
+                "sha256:a8035ba590658bae7562786c9cc6ea1a84aa49d3afab157e414c9e2ea74f496d",
+                "sha256:a8cdb91dddb04436bd2f098b8fdf4b81352e68cf4d2c6756fcc414791076569b",
+                "sha256:ac60daa1dc83e8821eed155796b0f7888b6b916cf61d620a4ddd8200ac70cd64",
+                "sha256:af4860132c8c05261a5f5f8467f1b269bf1c7c23902d75f2be57c4a7f2394b3e",
+                "sha256:bc221ffbc2150458b1cd71cdd9ddd5bb37962b036e41b8be258280b5b01da1dd",
+                "sha256:ce55289d5659b5b12b3db4dc9b7075b70cef5631e56530f14b2945e8836f2d20",
+                "sha256:d9881356dc48e58910c53af82b57183879129fa30492be69058c5b0d9fddf391",
+                "sha256:dbcf59334ff645e6a67cd5f78b4b2cdb76384cdf587fa0d2dc85f634a72e1a3e",
+                "sha256:ebf577c7a6744e9e1bd3fee45fc74a02710b214f94e2bde344912d85e0c9af7c",
+                "sha256:f081c03f413f59390a80b3e351cc2b2ea0205839714dbc364519bcf51f4b56ca",
+                "sha256:fdbb46fad4fb47443b5b8ac76904b2e7a66556844f33370861b4788db0f8816a",
+                "sha256:fdcd28360dbb6203fb5219b1a5658df226ac9bebc2542a9e8f457de959d713d0"
+            ],
+            "index": "pypi",
+            "version": "==3.7.2"
+        },
+        "neo": {
+            "extras": [
+                "nixio"
+            ],
+            "hashes": [
+                "sha256:2d4218b0826daeea880e155227060029ec38a00238ceb5f097138d9467c6399b",
+                "sha256:8ae6e79133461e2d50c9a8e592c636b784866d50544287caa5a3fc2bc9ef113f"
+            ],
+            "index": "pypi",
+            "version": "==0.10.2"
+        },
+        "nixio": {
+            "hashes": [
+                "sha256:0ba7a65148297bd43a5ddaf143c4faad1c999771aa1d7e690aa0a5e31f368608",
+                "sha256:5bc6258b0911738070f7008237d4cef348a994cce7f557adf624452f0f4287cb"
+            ],
+            "version": "==1.5.3"
+        },
+        "numpy": {
+            "hashes": [
+                "sha256:0d60fbae8e0019865fc4784745814cff1c421df5afee233db6d88ab4f14655a2",
+                "sha256:1a1329e26f46230bf77b02cc19e900db9b52f398d6722ca853349a782d4cff55",
+                "sha256:1b9735c27cea5d995496f46a8b1cd7b408b3f34b6d50459d9ac8fe3a20cc17bf",
+                "sha256:2792d23d62ec51e50ce4d4b7d73de8f67a2fd3ea710dcbc8563a51a03fb07b01",
+                "sha256:3e0746410e73384e70d286f93abf2520035250aad8c5714240b0492a7302fdca",
+                "sha256:4c3abc71e8b6edba80a01a52e66d83c5d14433cbcd26a40c329ec7ed09f37901",
+                "sha256:5883c06bb92f2e6c8181df7b39971a5fb436288db58b5a1c3967702d4278691d",
+                "sha256:5c97325a0ba6f9d041feb9390924614b60b99209a71a69c876f71052521d42a4",
+                "sha256:60e7f0f7f6d0eee8364b9a6304c2845b9c491ac706048c7e8cf47b83123b8dbf",
+                "sha256:76b4115d42a7dfc5d485d358728cdd8719be33cc5ec6ec08632a5d6fca2ed380",
+                "sha256:7dc869c0c75988e1c693d0e2d5b26034644399dd929bc049db55395b1379e044",
+                "sha256:834b386f2b8210dca38c71a6e0f4fd6922f7d3fcff935dbe3a570945acb1b545",
+                "sha256:8b77775f4b7df768967a7c8b3567e309f617dd5e99aeb886fa14dc1a0791141f",
+                "sha256:90319e4f002795ccfc9050110bbbaa16c944b1c37c0baeea43c5fb881693ae1f",
+                "sha256:b79e513d7aac42ae918db3ad1341a015488530d0bb2a6abcbdd10a3a829ccfd3",
+                "sha256:bb33d5a1cf360304754913a350edda36d5b8c5331a8237268c48f91253c3a364",
+                "sha256:bec1e7213c7cb00d67093247f8c4db156fd03075f49876957dca4711306d39c9",
+                "sha256:c5462d19336db4560041517dbb7759c21d181a67cb01b36ca109b2ae37d32418",
+                "sha256:c5652ea24d33585ea39eb6a6a15dac87a1206a692719ff45d53c5282e66d4a8f",
+                "sha256:d7806500e4f5bdd04095e849265e55de20d8cc4b661b038957354327f6d9b295",
+                "sha256:db3ccc4e37a6873045580d413fe79b68e47a681af8db2e046f1dacfa11f86eb3",
+                "sha256:dfe4a913e29b418d096e696ddd422d8a5d13ffba4ea91f9f60440a3b759b0187",
+                "sha256:eb942bfb6f84df5ce05dbf4b46673ffed0d3da59f13635ea9b926af3deb76926",
+                "sha256:f08f2e037bba04e707eebf4bc934f1972a315c883a9e0ebfa8a7756eabf9e357",
+                "sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760"
+            ],
+            "markers": "python_version >= '3.9'",
+            "version": "==1.25.2"
+        },
+        "odml": {
+            "hashes": [
+                "sha256:559818bd2be5aaf83491b0381ce69b205d47da429017fa7ea76553b8affaa0e0"
+            ],
+            "index": "pypi",
+            "version": "==1.5.3"
+        },
+        "packaging": {
+            "hashes": [
+                "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61",
+                "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==23.1"
+        },
+        "pathlib": {
+            "hashes": [
+                "sha256:6940718dfc3eff4258203ad5021090933e5c04707d5ca8cc9e73c94a7894ea9f",
+                "sha256:f35f95ab8b0f59e6d354090350b44a80a80635d22efdedfa84c7ad1cf0a74147"
+            ],
+            "version": "==1.0.1"
+        },
+        "pillow": {
+            "hashes": [
+                "sha256:00e65f5e822decd501e374b0650146063fbb30a7264b4d2744bdd7b913e0cab5",
+                "sha256:040586f7d37b34547153fa383f7f9aed68b738992380ac911447bb78f2abe530",
+                "sha256:0b6eb5502f45a60a3f411c63187db83a3d3107887ad0d036c13ce836f8a36f1d",
+                "sha256:1ce91b6ec08d866b14413d3f0bbdea7e24dfdc8e59f562bb77bc3fe60b6144ca",
+                "sha256:1f62406a884ae75fb2f818694469519fb685cc7eaff05d3451a9ebe55c646891",
+                "sha256:22c10cc517668d44b211717fd9775799ccec4124b9a7f7b3635fc5386e584992",
+                "sha256:3400aae60685b06bb96f99a21e1ada7bc7a413d5f49bce739828ecd9391bb8f7",
+                "sha256:349930d6e9c685c089284b013478d6f76e3a534e36ddfa912cde493f235372f3",
+                "sha256:368ab3dfb5f49e312231b6f27b8820c823652b7cd29cfbd34090565a015e99ba",
+                "sha256:38250a349b6b390ee6047a62c086d3817ac69022c127f8a5dc058c31ccef17f3",
+                "sha256:3a684105f7c32488f7153905a4e3015a3b6c7182e106fe3c37fbb5ef3e6994c3",
+                "sha256:3a82c40d706d9aa9734289740ce26460a11aeec2d9c79b7af87bb35f0073c12f",
+                "sha256:3b08d4cc24f471b2c8ca24ec060abf4bebc6b144cb89cba638c720546b1cf538",
+                "sha256:3ed64f9ca2f0a95411e88a4efbd7a29e5ce2cea36072c53dd9d26d9c76f753b3",
+                "sha256:3f07ea8d2f827d7d2a49ecf1639ec02d75ffd1b88dcc5b3a61bbb37a8759ad8d",
+                "sha256:520f2a520dc040512699f20fa1c363eed506e94248d71f85412b625026f6142c",
+                "sha256:5c6e3df6bdd396749bafd45314871b3d0af81ff935b2d188385e970052091017",
+                "sha256:608bfdee0d57cf297d32bcbb3c728dc1da0907519d1784962c5f0c68bb93e5a3",
+                "sha256:685ac03cc4ed5ebc15ad5c23bc555d68a87777586d970c2c3e216619a5476223",
+                "sha256:76de421f9c326da8f43d690110f0e79fe3ad1e54be811545d7d91898b4c8493e",
+                "sha256:76edb0a1fa2b4745fb0c99fb9fb98f8b180a1bbceb8be49b087e0b21867e77d3",
+                "sha256:7be600823e4c8631b74e4a0d38384c73f680e6105a7d3c6824fcf226c178c7e6",
+                "sha256:81ff539a12457809666fef6624684c008e00ff6bf455b4b89fd00a140eecd640",
+                "sha256:88af2003543cc40c80f6fca01411892ec52b11021b3dc22ec3bc9d5afd1c5334",
+                "sha256:8c11160913e3dd06c8ffdb5f233a4f254cb449f4dfc0f8f4549eda9e542c93d1",
+                "sha256:8f8182b523b2289f7c415f589118228d30ac8c355baa2f3194ced084dac2dbba",
+                "sha256:9211e7ad69d7c9401cfc0e23d49b69ca65ddd898976d660a2fa5904e3d7a9baa",
+                "sha256:92be919bbc9f7d09f7ae343c38f5bb21c973d2576c1d45600fce4b74bafa7ac0",
+                "sha256:9c82b5b3e043c7af0d95792d0d20ccf68f61a1fec6b3530e718b688422727396",
+                "sha256:9f7c16705f44e0504a3a2a14197c1f0b32a95731d251777dcb060aa83022cb2d",
+                "sha256:9fb218c8a12e51d7ead2a7c9e101a04982237d4855716af2e9499306728fb485",
+                "sha256:a74ba0c356aaa3bb8e3eb79606a87669e7ec6444be352870623025d75a14a2bf",
+                "sha256:b4f69b3700201b80bb82c3a97d5e9254084f6dd5fb5b16fc1a7b974260f89f43",
+                "sha256:bc2ec7c7b5d66b8ec9ce9f720dbb5fa4bace0f545acd34870eff4a369b44bf37",
+                "sha256:c189af0545965fa8d3b9613cfdb0cd37f9d71349e0f7750e1fd704648d475ed2",
+                "sha256:c1fbe7621c167ecaa38ad29643d77a9ce7311583761abf7836e1510c580bf3dd",
+                "sha256:c7cf14a27b0d6adfaebb3ae4153f1e516df54e47e42dcc073d7b3d76111a8d86",
+                "sha256:c9f72a021fbb792ce98306ffb0c348b3c9cb967dce0f12a49aa4c3d3fdefa967",
+                "sha256:cd25d2a9d2b36fcb318882481367956d2cf91329f6892fe5d385c346c0649629",
+                "sha256:ce543ed15570eedbb85df19b0a1a7314a9c8141a36ce089c0a894adbfccb4568",
+                "sha256:ce7b031a6fc11365970e6a5686d7ba8c63e4c1cf1ea143811acbb524295eabed",
+                "sha256:d35e3c8d9b1268cbf5d3670285feb3528f6680420eafe35cccc686b73c1e330f",
+                "sha256:d50b6aec14bc737742ca96e85d6d0a5f9bfbded018264b3b70ff9d8c33485551",
+                "sha256:d5d0dae4cfd56969d23d94dc8e89fb6a217be461c69090768227beb8ed28c0a3",
+                "sha256:d5db32e2a6ccbb3d34d87c87b432959e0db29755727afb37290e10f6e8e62614",
+                "sha256:d72e2ecc68a942e8cf9739619b7f408cc7b272b279b56b2c83c6123fcfa5cdff",
+                "sha256:d737a602fbd82afd892ca746392401b634e278cb65d55c4b7a8f48e9ef8d008d",
+                "sha256:d80cf684b541685fccdd84c485b31ce73fc5c9b5d7523bf1394ce134a60c6883",
+                "sha256:db24668940f82321e746773a4bc617bfac06ec831e5c88b643f91f122a785684",
+                "sha256:dbc02381779d412145331789b40cc7b11fdf449e5d94f6bc0b080db0a56ea3f0",
+                "sha256:dffe31a7f47b603318c609f378ebcd57f1554a3a6a8effbc59c3c69f804296de",
+                "sha256:edf4392b77bdc81f36e92d3a07a5cd072f90253197f4a52a55a8cec48a12483b",
+                "sha256:efe8c0681042536e0d06c11f48cebe759707c9e9abf880ee213541c5b46c5bf3",
+                "sha256:f31f9fdbfecb042d046f9d91270a0ba28368a723302786c0009ee9b9f1f60199",
+                "sha256:f88a0b92277de8e3ca715a0d79d68dc82807457dae3ab8699c758f07c20b3c51",
+                "sha256:faaf07ea35355b01a35cb442dd950d8f1bb5b040a7787791a535de13db15ed90"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==10.0.0"
+        },
+        "pyparsing": {
+            "hashes": [
+                "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb",
+                "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"
+            ],
+            "markers": "python_full_version >= '3.6.8'",
+            "version": "==3.0.9"
+        },
+        "python-dateutil": {
+            "hashes": [
+                "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
+                "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
+            ],
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+            "version": "==2.8.2"
+        },
+        "pyyaml": {
+            "hashes": [
+                "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc",
+                "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741",
+                "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206",
+                "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27",
+                "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595",
+                "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62",
+                "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98",
+                "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696",
+                "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d",
+                "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867",
+                "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47",
+                "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486",
+                "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6",
+                "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3",
+                "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007",
+                "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938",
+                "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c",
+                "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735",
+                "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d",
+                "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba",
+                "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8",
+                "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5",
+                "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd",
+                "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3",
+                "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0",
+                "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515",
+                "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c",
+                "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c",
+                "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924",
+                "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34",
+                "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43",
+                "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859",
+                "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673",
+                "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a",
+                "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab",
+                "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa",
+                "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c",
+                "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585",
+                "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d",
+                "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"
+            ],
+            "markers": "python_version >= '3.6'",
+            "version": "==6.0.1"
+        },
+        "quantities": {
+            "hashes": [
+                "sha256:b2edf67b8c2a28aa3bbe096f9fc3ec3ab83fc3192997373641cddab32bea2f72",
+                "sha256:efeafffc0c0364f891a9327239cd12496bccb55cd037a6d1bf44de706f722877"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==0.14.1"
+        },
+        "rdflib": {
+            "hashes": [
+                "sha256:0438920912a642c866a513de6fe8a0001bd86ef975057d6962c79ce4771687cd",
+                "sha256:9995eb8569428059b8c1affd26b25eac510d64f5043d9ce8c84e0d0036e995ae"
+            ],
+            "markers": "python_full_version >= '3.8.1' and python_full_version < '4.0.0'",
+            "version": "==7.0.0"
+        },
+        "scipy": {
+            "hashes": [
+                "sha256:08d957ca82d3535b3b9ba6c8ff355d78fe975271874e2af267cb5add5bd78625",
+                "sha256:249cfa465c379c9bb2c20123001e151ff5e29b351cbb7f9c91587260602c58d0",
+                "sha256:366a6a937110d80dca4f63b3f5b00cc89d36f678b2d124a01067b154e692bab1",
+                "sha256:39154437654260a52871dfde852adf1b93b1d1bc5dc0ffa70068f16ec0be2624",
+                "sha256:396fae3f8c12ad14c5f3eb40499fd06a6fef8393a6baa352a652ecd51e74e029",
+                "sha256:3b9963798df1d8a52db41a6fc0e6fa65b1c60e85d73da27ae8bb754de4792481",
+                "sha256:3e8eb42db36526b130dfbc417609498a6192381abc1975b91e3eb238e0b41c1a",
+                "sha256:512fdc18c65f76dadaca139348e525646d440220d8d05f6d21965b8d4466bccd",
+                "sha256:aec8c62fbe52914f9cf28d846cf0401dd80ab80788bbab909434eb336ed07c04",
+                "sha256:b41a0f322b4eb51b078cb3441e950ad661ede490c3aca66edef66f4b37ab1877",
+                "sha256:b4bb943010203465ac81efa392e4645265077b4d9e99b66cf3ed33ae12254173",
+                "sha256:b588311875c58d1acd4ef17c983b9f1ab5391755a47c3d70b6bd503a45bfaf71",
+                "sha256:ba94eeef3c9caa4cea7b402a35bb02a5714ee1ee77eb98aca1eed4543beb0f4c",
+                "sha256:be8c962a821957fdde8c4044efdab7a140c13294997a407eaee777acf63cbf0c",
+                "sha256:cce154372f0ebe88556ed06d7b196e9c2e0c13080ecb58d0f35062dc7cc28b47",
+                "sha256:d51565560565a0307ed06fa0ec4c6f21ff094947d4844d6068ed04400c72d0c3",
+                "sha256:e866514bc2d660608447b6ba95c8900d591f2865c07cca0aa4f7ff3c4ca70f30",
+                "sha256:fb5b492fa035334fd249f0973cc79ecad8b09c604b42a127a677b45a9a3d4289",
+                "sha256:ffb28e3fa31b9c376d0fb1f74c1f13911c8c154a760312fbee87a21eb21efe31"
+            ],
+            "markers": "python_version < '3.13' and python_version >= '3.9'",
+            "version": "==1.11.1"
+        },
+        "six": {
+            "hashes": [
+                "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
+                "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
+            ],
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+            "version": "==1.16.0"
+        },
+        "tqdm": {
+            "hashes": [
+                "sha256:1871fb68a86b8fb3b59ca4cdd3dcccbc7e6d613eeed31f4c332531977b89beb5",
+                "sha256:c4f53a17fe37e132815abceec022631be8ffe1b9381c2e6e30aa70edc99e9671"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==4.65.0"
+        }
+    },
+    "develop": {}
+}

+ 234 - 0
code/convert_to_nix.py

@@ -0,0 +1,234 @@
+"""
+This script loads a complete session in the blackrock format and converts it to
+a single nix file.
+
+For sessions without online recorded LFP (i.e., 1000Hz neural
+signal in the ns2 files), this LFP is generated offline by filtering and
+downsampling.
+
+Two versions of the nix files are saved: one containing the complete data, and
+a smaller one containing everything except the AnalogSignal object with the
+raw data records.
+"""
+import os
+
+import numpy as np
+import quantities as pq
+
+import neo
+from neo.test.tools import (assert_same_sub_schema,
+                            assert_same_annotations,
+                            assert_same_array_annotations)
+from elephant.signal_processing import butter
+
+from reachgraspio import reachgraspio
+
+##### INITIAL SETUP ############
+
+# Choose which session you want to convert into a nix file
+session = "i140703-001"
+session = "l101210-001"
+
+# Input data. i.e., original Blackrock files and odML
+dataset_dir = '../datasets_blackrock'
+session_path = f'{dataset_dir}/{session}'
+odml_dir = os.path.join('..', 'datasets_blackrock')
+
+# Output for the nix files
+nix_dataset_dir = '../datasets_nix'
+nix_session_path = f'{nix_dataset_dir}/{session}'
+
+
+##### LOAD BLACKROCK FILES ############
+session = reachgraspio.ReachGraspIO(
+    filename=session_path,
+    odml_directory=odml_dir,
+    verbose=False)
+
+block = session.read_block(lazy=False, load_waveforms=True)
+
+
+##### CREATE LFP IF MISSING ############
+# Here, we construct one offline filtered LFP from each ns5 (monkey L) or ns6
+# (monkey N) raw recording trace. For monkey N, this filtered LFP can be
+# compared to the LFPs in the ns2 file (note that monkey L contains only
+# behavioral signals in the ns2 file). Also, we assign telling names to each
+# Neo AnalogSignal, which is used for plotting later on in this script.
+
+# this factor was heuristically determined as an approximate shift introduced
+# by the online filtering. Here, the offline filtering does not introduce a
+# noticeable shift, so we set it to zero.
+time_shift_factor = 0*pq.ms
+
+# Identify neuronal signals and provide labels for plotting
+filtered_anasig = None
+raw_anasig = None
+for anasig in block.segments[0].analogsignals:
+    # skip non-neuronal signals
+    if not anasig.annotations['neural_signal']:
+        continue
+
+    # Identify nsx source of signals in this AnalogSignal object
+    if 'nsx' in anasig.annotations:
+        nsx = anasig.annotations['nsx']
+    else:
+        nsx = np.unique(anasig.array_annotations['nsx'])
+        assert len(nsx) == 1, 'Different nsx sources in AnalogSignal'
+        nsx = nsx[0]
+
+    if nsx == 2:
+        # AnalogSignal is LFP from ns2
+        filtered_anasig = anasig
+    elif nsx in [5, 6]:
+        # AnalogSignal is raw signal from ns5 or ns6
+        raw_anasig = anasig
+
+# Does the file contain a filtered signal, i.e., an LFP?
+if filtered_anasig is None:
+    print("Filtering raw time series to obtain LFP")
+
+    # Filtering must be done channel by channel for memory reasons (requires approx. 32 GB RAM)
+    for f in range(raw_anasig.shape[1]):
+
+        filtered_signal = butter(
+            raw_anasig[:, f],
+            highpass_freq=None,
+            lowpass_freq=250 * pq.Hz,
+            filter_function='sosfiltfilt',
+            order=4)
+
+        # Downsampling 30-fold here to get to 1000Hz from 30kHz
+        # Note: For filters that may introduce a time shift, here would be the
+        # place to correct for this shift using:
+        # [...] .time_shift(time_shift_factor)
+        downsampled_signal=filtered_signal.downsample(30)
+
+        # First run? Create a new Analogsignal
+        if f == 0:
+            offline_filtered_anasig = neo.AnalogSignal(
+                np.zeros((downsampled_signal.shape[0], raw_anasig.shape[1]), dtype=np.float32) *\
+                    downsampled_signal.units,
+                t_start=downsampled_signal.t_start,
+                sampling_rate=downsampled_signal.sampling_rate)
+
+            # add missing annotations (decision not to put nsx2, since this
+            # signal is not in the original files
+            offline_filtered_anasig.annotate(
+                neural_signal=True,
+                filter_shift_correction=time_shift_factor,
+                nsx=-1,
+                stream_id='',
+            )
+
+            # all array annotations of the raw signal also apply to the filtered
+            # signal
+            offline_filtered_anasig.array_annotate(
+                **raw_anasig.array_annotations)
+
+        offline_filtered_anasig[:, f] = downsampled_signal
+
+    if 'nsx' in anasig.annotations:
+        nsx = anasig.annotations['nsx']
+    else:
+        nsx = anasig.array_annotations["nsx"][0]
+
+    offline_filtered_anasig.name = f"NeuralTimeSeriesDownsampled"
+    offline_filtered_anasig.description = "Downsampled continuous neuronal " \
+            "recordings, where the downsampling was " \
+            "performed off-line during post-processing"
+
+    offline_filtered_group = neo.Group(
+        name="offline",
+        stream_id=''
+    )
+    offline_filtered_group.analogsignals.append(offline_filtered_anasig)
+
+    # Attach all offline filtered LFPs to the segment of data and the groups
+    block.segments[0].analogsignals.insert(0, offline_filtered_anasig)
+    block.groups.insert(0, offline_filtered_group)
+
+
+##### MISC FIXES ###################
+
+# For lilou, behavior channels were not set with nice names in the setup.
+# Here, we hard code these handwritten annotations to make both recordings
+# more similar
+block.segments[0].analogsignals[1].array_annotate(
+    channel_names=['GFpr1', 'GFside1', 'GFpr2', 'GFside2', 'LF', 'Displ'])
+
+# Unify group names of ns2 for Nikos session
+if session == "i140703-001":
+    block.groups[0].name = 'nsx2'
+    block.groups[1].name = 'nsx2'
+
+
+##### SAVE NIX FILE ###################
+nix_filename = nix_session_path + '.nix'
+if os.path.exists(nix_filename):
+    print('Nix file already exists and will not be overwritten.')
+else:
+    with neo.NixIO(nix_filename) as io:
+        print(f'Saving nix file at {nix_filename}')
+        io.write_block(block)
+
+
+##### VALIDATION OF FILE CONTENT ######
+with neo.NixIO(nix_filename, mode='ro') as io:
+    blocks = io.read_all_blocks()
+    assert len(blocks) == 1
+    block_new = blocks[0]
+
+    for seg_old, seg_new in zip(block.segments, block_new.segments):
+        for anasig_old, anasig_new in zip(seg_old.analogsignals, seg_new.analogsignals):
+            # ignoring differences in the file_origin attribute
+            anasig_old.file_origin = anasig_new.file_origin
+
+            assert_same_sub_schema(anasig_old, anasig_new)
+            assert_same_annotations(anasig_old, anasig_new)
+            assert_same_array_annotations(anasig_old, anasig_new)
+        print(f'AnalogSignals are equivalent.')
+        
+        for st_old, st_new in zip(seg_old.spiketrains, seg_new.spiketrains):
+            # ignoring differences in the file_origin attribute
+            st_old.file_origin = st_new.file_origin
+
+            assert_same_sub_schema(st_old, st_new)
+            assert_same_annotations(st_old, st_new)
+            assert_same_array_annotations(st_old, st_new)
+        print(f'Spiketrains are equivalent.')
+        
+        for ev_old, ev_new in zip(seg_old.events, seg_new.events):
+            # ignoring differences in the file_origin attribute
+            ev_old.file_origin = ev_new.file_origin
+
+            # ignore list-array type changes
+            if 'color_codes' in ev_old.annotations:
+                ev_old.annotations['color_codes'] = list(ev_old.annotations['color_codes'])
+
+            assert_same_sub_schema(ev_old, ev_new)
+            assert_same_annotations(ev_old, ev_new)
+            assert_same_array_annotations(ev_old, ev_new)
+        print(f'Events are equivalent.')
+        
+        for ep_old, ep_new in zip(seg_old.epochs, seg_new.epochs):
+            # ignoring differences in the file_origin attribute
+            ep_old.file_origin = ep_new.file_origin
+
+            assert_same_sub_schema(ep_old, ep_new)
+            assert_same_annotations(ep_old, ep_new)
+            assert_same_array_annotations(ep_old, ep_new)
+        print(f'Epochs are equivalent.')
+
+
+##### SAVE SMALL NIX FILE ###################
+# remove raw signal
+del block.segments[0].analogsignals[2]
+del block.groups[2]
+
+nix_filename = nix_session_path + '_no_raw.nix'
+if os.path.exists(nix_filename):
+    print('Nix file already exists and will not be overwritten.')
+else:
+    with neo.NixIO(nix_filename) as io:
+        print(f'Saving nix file at {nix_filename}')
+        io.write_block(block)

+ 1 - 1
code/data_overview_1.py

@@ -66,7 +66,7 @@ def get_monkey_datafile(monkey):
 
 
 # Enter your dataset directory here
-datasetdir = "../datasets/"
+datasetdir = "../datasets_blackrock/"
 
 trialtype_colors = {
     'SGHF': 'MediumBlue', 'SGLF': 'Turquoise',

+ 1 - 1
code/data_overview_2.py

@@ -63,7 +63,7 @@ def get_monkey_datafile(monkey):
 
 
 # Enter your dataset directory here
-datasetdir = os.path.join('..', 'datasets')
+datasetdir = os.path.join('..', 'datasets_blackrock')
 
 chosen_els = {'Lilou': range(3, 97, 7), 'Nikos2': range(1, 97, 7)}
 chosen_el = {

+ 9 - 7
code/example.py

@@ -2,7 +2,8 @@
 """
 Example code for loading and processing of a recording of the reach-
 to-grasp experiments conducted at the Institute de Neurosciences de la Timone
-by Thomas Brochier and Alexa Riehle.
+by Thomas Brochier and Alexa Riehle from the original Blackrock files and
+odML files, using the custom loading routine `ReachGraspIO`.
 
 Authors: Julia Sprenger, Lyuba Zehl, Michael Denker
 
@@ -57,12 +58,13 @@ from neo_utils import load_segment
 
 # Specify the path to the recording session to load, eg,
 # '/home/user/l101210-001'
-session_name = os.path.join('..', 'datasets', 'i140703-001')
-# session_name = os.path.join('..', 'datasets', 'l101210-001')
-odml_dir = os.path.join('..', 'datasets')
+session_name = "i140703-001"
+# session_name = "l101210-001"
+session_path = os.path.join('..', 'datasets_blackrock', session_name)
+odml_dir = os.path.join('..', 'datasets_blackrock')
 
 # Open the session for reading
-session = reachgraspio.ReachGraspIO(session_name, odml_directory=odml_dir)
+session = reachgraspio.ReachGraspIO(session_path, odml_directory=odml_dir)
 
 # Read a the complete dataset in lazy mode generating all neo objects,
 # but not loading data into memory.  The lazy neo structure will contain objects
@@ -283,6 +285,6 @@ plt.ylabel(amplitude_unit.name)
 plt.legend(loc=4, fontsize=10)
 
 # Save plot
-fname = 'example_plot'
+file_name = 'example_plot_from_blackrock_%s' % session_name
 for file_format in ['eps', 'png', 'pdf']:
-    fig.savefig(fname + '.%s' % file_format, dpi=400, format=file_format)
+    fig.savefig(file_name + '.%s' % file_format, dpi=400, format=file_format)

code/example.m → code/example_matlab.m


+ 240 - 0
code/example_nix.py

@@ -0,0 +1,240 @@
+# -*- coding: utf-8 -*-
+"""
+Example code for loading and processing of a recording of the reach-
+to-grasp experiments conducted at the Institute de Neurosciences de la Timone
+by Thomas Brochier and Alexa Riehle from the NIX files.
+
+Authors: Julia Sprenger, Lyuba Zehl, Michael Denker
+
+
+Copyright (c) 2017, Institute of Neuroscience and Medicine (INM-6),
+Forschungszentrum Juelich, Germany
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+* Neither the names of the copyright holders nor the names of the contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import os
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+import quantities as pq
+
+from neo import Block, Segment
+from neo.io import NixIO
+from neo.utils import cut_segment_by_epoch, add_epoch, get_events
+
+
+# =============================================================================
+# Load data
+#
+# As a first step, we load the data file into memory as a Neo object.
+# =============================================================================
+
+# Specify the path to the recording session to load, eg,
+# '/home/user/l101210-001'
+session_name = "i140703-001"
+# session_name = "l101210-001"
+session_path = os.path.join("..", "datasets_nix", session_name + ".nix")
+
+# Open the session for reading
+session = NixIO(session_path)
+
+# Channel ID to plot
+target_channel_id = 62
+
+
+# Read the complete dataset in lazy mode generating all neo objects,
+# but not loading data into memory.  The lazy neo structure will contain objects
+# to capture all recorded data types (time series at 1000Hz (ns2) and 30kHz (ns6)
+# scaled to units of voltage, sorted spike trains, spike waveforms and events)
+# of the recording session and return it as a Neo Block. The
+# time shift of the ns2 signal (LFP) induced by the online filter is
+# automatically corrected for by a heuristic factor stored in the metadata
+# (correct_filter_shifts=True).
+
+block = session.read_block()
+
+# Validate there is only a single Segment present in the block
+assert len(block.segments) == 1
+
+# loading data content of all data objects during the first 300 seconds
+# data_segment = load_segment(block.segments[0], time_range=(None, 300*pq.s))
+data_segment = block.segments[0]
+
+print("Session loaded.")
+
+
+# =============================================================================
+# Construct analysis epochs
+#
+# In this step we extract and cut the data into time segments (termed analysis
+# epochs) that we wish to analyze. We contrast these analysis epochs to the
+# behavioral trials that are defined by the experiment as occurrence of a Trial
+# Start (TS-ON) event in the experiment. Concretely, here our analysis epochs
+# are constructed as a cutout of 25ms of data around the TS-ON event of all
+# successful behavioral trials.
+# =============================================================================
+
+# Get Trial Start (TS-ON) events of all successful behavioral trials
+# (corresponds to performance code 255, which is accessed for convenience and
+# better legibility in the dictionary attribute performance_codes of the
+# ReachGraspIO class).
+#
+# To this end, we filter all event objects of the loaded data to match the name
+# "TrialEvents", which is the Event object containing all Events available (see
+# documentation of ReachGraspIO). From this Event object we extract only events
+# matching "TS-ON" and the desired trial performance code (which are
+# annotations of the Event object).
+start_events = get_events(
+    data_segment,
+    name='TrialEvents',
+    trial_event_labels='TS-ON',
+    performance_in_trial_str='correct_trial')
+print('Determined start events.')
+
+# Extract single Neo Event object containing all TS-ON triggers
+assert len(start_events) == 1
+start_event = start_events[0]
+
+
+# Construct analysis epochs from 10ms before the TS-ON of a successful
+# behavioral trial to 15ms after TS-ON. The name "analysis_epochs" is given to
+# the resulting Neo Epoch object. The object is not attached to the Neo
+# Segment. The parameter event2 of add_epoch() is left empty, since we are
+# cutting around a single event, as opposed to cutting between two events.
+
+pre = -10 * pq.ms
+post = 15 * pq.ms
+epoch = add_epoch(
+    data_segment,
+    event1=start_event, event2=None,
+    pre=pre, post=post,
+    attach_result=False,
+    name='analysis_epochs',
+    array_annotations=start_event.array_annotations)
+print('Added epoch.')
+
+
+# Create new segments of data cut according to the analysis epochs of the
+# 'analysis_epochs' Neo Epoch object. The time axes of all segments are aligned
+# such that each segment starts at time 0 (parameter reset_times); annotations
+# describing the analysis epoch are carried over to the segments. A new Neo
+# Block named "data_cut_to_analysis_epochs" is created to capture all cut
+# analysis epochs. For execution time reason, we are only considering the
+# first 10 epochs here.
+
+cut_trial_block = Block(name="data_cut_to_analysis_epochs")
+cut_trial_block.segments = cut_segment_by_epoch(
+    data_segment, epoch[:10], reset_time=True)
+print("Cut data.")
+
+
+# =============================================================================
+# Plot data
+# =============================================================================
+
+# Determine the first existing trial ID i from the Event object containing all
+# start events. Then, by calling the filter() function of the Neo Block
+# "data_cut_to_analysis_epochs" containing the data cut into the analysis
+# epochs, we ask to return all Segments annotated by the behavioral trial ID i.
+# In this case this call should return one matching analysis epoch around TS-ON
+# belonging to behavioral trial ID i. For monkey N, this is trial ID 1, for
+# monkey L this is trial ID 2 since trial ID 1 is not a correct trial.
+
+trial_id = int(np.min(start_event.array_annotations['trial_id']))
+trial_segments = cut_trial_block.filter(
+    targdict={"trial_id": trial_id}, objects=Segment)
+assert len(trial_segments) == 1
+trial_segment = trial_segments[0]
+
+# Create figure
+fig = plt.figure(facecolor='w')
+time_unit = pq.CompoundUnit('1./30000*s')
+amplitude_unit = pq.microvolt
+nsx_colors = {2: 'k', 5: 'r', 6: 'b'}
+
+# Loop through all AnalogSignal objects and plot the signal of the target channel
+# in a color corresponding to its sampling frequency (i.e., originating from the ns2/ns5 or ns2/ns6).
+for i, anasig in enumerate(trial_segment.analogsignals):
+    # only visualize neural data
+    if anasig.annotations['neural_signal']:
+        if 'nsx' in anasig.annotations:
+            nsx = anasig.annotations['nsx']
+        else:
+            nsx = anasig.array_annotations['nsx'][0]
+        channel_ids = np.asarray(anasig.array_annotations['channel_ids'], dtype=int)
+        target_channel_index = np.where(channel_ids == target_channel_id)[0]
+        target_signal = anasig[:, target_channel_index]
+        plt.plot(
+            target_signal.times.rescale(time_unit),
+            target_signal.squeeze().rescale(amplitude_unit),
+            label=target_signal.name,
+            color=nsx_colors[nsx if nsx>0 else 2]) # offline LFP: nsx==-1
+
+# Loop through all spike trains and plot the spike time, and overlapping the
+# wave form of the spike used for spike sorting stored separately in the nev
+# file.
+for st in trial_segment.spiketrains:
+    color = np.random.rand(3,)
+    if st.annotations['channel_id'] == target_channel_id:
+        for spike_id, spike in enumerate(st):
+            # Plot spike times
+            plt.axvline(
+                spike.rescale(time_unit).magnitude,
+                color=color,
+                label='Unit ID %i' % st.annotations['unit_id'])
+            # Plot waveforms
+            waveform = st.waveforms[spike_id, 0, :]
+            waveform_times = np.arange(len(waveform))*time_unit + spike
+            plt.plot(
+                waveform_times.rescale(time_unit).magnitude,
+                waveform.rescale(amplitude_unit),
+                '--',
+                linewidth=2,
+                color=color,
+                zorder=0)
+
+# Loop through all events
+for event in trial_segment.events:
+    if event.name == 'TrialEvents':
+        for ev_id, ev in enumerate(event):
+            plt.axvline(
+                ev.rescale(time_unit),
+                alpha=0.2,
+                linewidth=3,
+                linestyle='dashed',
+                label=f'event {event.array_annotations["trial_event_labels"][ev_id]}')
+
+# Finishing touches on the plot
+plt.autoscale(enable=True, axis='x', tight=True)
+plt.xlabel(time_unit.name)
+plt.ylabel(amplitude_unit.name)
+plt.legend(loc=4, fontsize=10)
+
+# Save plot
+file_name = 'example_plot_from_nix_%s' % session_name
+for file_format in ['eps', 'png', 'pdf']:
+    fig.savefig(file_name + '.%s' % file_format, dpi=400, format=file_format)

+ 64 - 7
code/reachgraspio/reachgraspio.py

@@ -556,7 +556,7 @@ class ReachGraspIO(BlackrockIO):
             times=pq.Quantity(event_time, 'ms').flatten(),
             labels=np.array(event_name),
             name='AnalogTrialEvents',
-            description='Events extracted from analog signals')
+            description='Events extracted from behavioural time series')
 
         performance_str = []
         for pit in performance_code:
@@ -788,7 +788,7 @@ class ReachGraspIO(BlackrockIO):
                 if chid not in unit_dict:
                     unit_dict[chid] = {}
                 if unit_id not in unit_dict[chid]:
-                    group = neo.Group(name='Unit {} on channel {}'.format(unit_id, chid),
+                    group = neo.Group(name='UnitGroup-ch{}#{}'.format(chid, unit_id),
                                       description='Group for neuronal data related to unit {} on '
                                                   'channel {}'.format(unit_id, chid),
                                       group_type='unit',
@@ -802,6 +802,10 @@ class ReachGraspIO(BlackrockIO):
 
                 unit_dict[chid][unit_id].add(st)
 
+                # create a consistent name and description for the spike train
+                st.name = 'SpikeTrain-ch{}#{}'.format(chid, unit_id)
+                st.description = 'SpikeTrain of unit {} on channel {}'.format(unit_id, chid)
+
         # if views are already created, link them to unit groups
         if view_dict:
             for chid, channel_dict in unit_dict.items():
@@ -889,6 +893,9 @@ class ReachGraspIO(BlackrockIO):
 
             if not any(neural_chids):
                 asig.annotate(neural_signal=False)
+                asig.name = "BehaviourTimeSeries"
+                asig.description = "Continuous behavioural time series recorded in the experiment, including " \
+                                    "object displacements and measurements of the gripforce sensors"
             elif all(neural_chids):
                 asig.annotate(neural_signal=True)
 
@@ -907,7 +914,7 @@ class ReachGraspIO(BlackrockIO):
                                                             props['LowPassFreq'].unit))
                 hi_pass_order = np.zeros_like(hi_pass_freq)
                 lo_pass_order = np.zeros_like(lo_pass_freq)
-                filter_type = np.empty((nchan), np.str)
+                filter_type = np.empty((nchan), str)
                 for chidx in range(nchan):
                     filter = 'Filter_ns%i' % nsx
                     sec = self.odmldoc['Cerebus']['NeuralSignalProcessor']['NeuralSignals'][filter]
@@ -929,6 +936,14 @@ class ReachGraspIO(BlackrockIO):
                     filter_type=filter_type
                 ))
 
+                if asig.sampling_rate == pq.Quantity(30000 * pq.Hz):
+                    asig.name = "NeuralTimeSeriesRaw"
+                    asig.description = "Continuous raw neuronal recordings sampled at high resolution"
+                if asig.sampling_rate == pq.Quantity(1000 * pq.Hz):
+                    asig.name = "NeuralTimeSeriesDownsampled"
+                    asig.description = "Downsampled continuous neuronal recordings, where the downsampling was " \
+                                       "performed on-line by the recording system"
+
                 self.__annotate_electrode_rejections(asig)
 
     def __annotate_electrode_rejections(self, obj):
@@ -971,13 +986,16 @@ class ReachGraspIO(BlackrockIO):
         if self.odmldoc:
 
             # updating array annotations of neuronal analogsignals
+            # and use same dtype for channel_id annotations as for
+            # channel_ids array_annotations
             for seg in block.segments:
                 for obj in seg.analogsignals:
                     if 'neural_signal' in obj.annotations and obj.annotations[
                        'neural_signal'] and 'channel_ids' in obj.array_annotations:
                         chids = np.asarray(obj.array_annotations['channel_ids'], dtype=int)
                         ca_ids, *coordinates = self.__convert_chids_and_coordinates(chids)
-                        obj.array_annotations.update(dict(connector_aligned_ids=ca_ids,
+                        obj.array_annotations.update(dict(channel_ids=chids,
+                                                          connector_aligned_ids=ca_ids,
                                                           coordinates_x=coordinates[0],
                                                           coordinates_y=coordinates[1]))
 
@@ -1232,7 +1250,8 @@ class ReachGraspIO(BlackrockIO):
                         of trial types presented to the subject during a
                         segment of the recording) present during the recording.
                         For a mapping of condition codes to trial types, see
-                        the condition_str attribute of the ReachGraspIO class.
+                        the condition_str attribute of the ReachGraspIO class
+                        or corresponding Block annotations.
                     subject_name (str):
                         Name of the recorded subject.
                     subject_gender (bool):
@@ -1280,13 +1299,41 @@ class ReachGraspIO(BlackrockIO):
                     rec_pauses (bool):
                         True if the session contains a recording pause (i.e.,
                         multiple segments).
+                    event_labels_str (dict):
+                        Provides a text label for each digital event code returned as
+                        events by the parent BlackrockIO. For example,
+                        event_labels_str['65296'] contains the string 'TS-ON'.
+                    event_labels_codes (dict):
+                        Reverse of `event_labels_str`: Provides a list of event codes
+                        related to a specific text label for a trial event. For example,
+                        event_labels_codes['TS-ON'] contains the list ['65296']. In
+                        addition to the detailed codes, for convenience the meta codes
+                        'CUE/GO', 'RW-ON', and 'SR' summarizing a set of digital events are
+                        defined for easier access.
+                    trial_const_sequence_str (dict):
+                        Dictionary contains the ordering of selected constant trial events
+                        for correct trials, e.g., as TS is the first trial event in a
+                        correct trial, trial_const_sequence_codes['TS'] is 0.
+                    trial_const_sequence_codes (dict):
+                        Reverse of trial_const_sequence_str: Dictionary contains the
+                        ordering of selected constant trial events for correct trials,
+                        e.g., trial_const_sequence_codes[0] is 'TS'.
+                    performance_str (dict):
+                        Text strings to help interpret the performance code of a trial. For
+                        example, correct trials have a performance code of 255, and thus
+                        performance_str[255] == 'correct_trial'
+                    performance_codes (dict):
+                        Reverse of performance_const_sequence_str. Returns the performance
+                        code of a given text string indicating trial performance. For
+                        example, performance_str['correct_trial'] == 255
 
                 Segment annotations:
                     condition (int):
                         Condition code (describing the set of trial types
                         presented to the subject) of this segment. For a
                         mapping of condition codes to trial types, see the
-                        condition_str attribute of the ReachGraspIO class.
+                        condition_str attribute of the ReachGraspIO class
+                        or corresponding Block annotations.
 
                 ChannelIndex annotations:
                     connector_aligned_id (int):
@@ -1501,7 +1548,8 @@ class ReachGraspIO(BlackrockIO):
                     performance_in_trial (list of int):
                         Performance code of the trial that the event belongs
                         to. Compare to the performance_codes and
-                        performance_str attributes of ReachGraspIO class.
+                        performance_str attributes of ReachGraspIO class
+                        or corresponding Block annotations.
                     trial_reject_XXX:
                         For different filter ranges XXX (defined in the odML
                         file), if True this variable indicates whether the
@@ -1561,6 +1609,15 @@ class ReachGraspIO(BlackrockIO):
             self.__annotate_channel_infos(bl)
             self.__annotate_units_with_odml(bl.groups)
 
+        # Add annotations for general performance codes and generic information
+        bl.annotate(performance_str=self.performance_str)
+        bl.annotate(performance_codes=self.performance_codes)
+        bl.annotate(condition_str=self.condition_str)
+        bl.annotate(event_labels_str=self.event_labels_str)
+        bl.annotate(event_labels_codes=self.event_labels_codes)
+        bl.annotate(trial_const_sequence_str=self.trial_const_sequence_str)
+        bl.annotate(trial_const_sequence_codes=self.trial_const_sequence_codes)
+
         return bl
 
     def read_segment(

+ 0 - 1
code/reachgraspio/test_reachgraspio.py

@@ -1 +0,0 @@
-../../.git/annex/objects/wv/P9/MD5-s6310--4e5f07cf4d415caa7b32517d24c50d62/MD5-s6310--4e5f07cf4d415caa7b32517d24c50d62

+ 144 - 0
code/reachgraspio/test_reachgraspio.py

@@ -0,0 +1,144 @@
+import sys
+from os.path import join, realpath, dirname, split
+
+sys.path.insert(0, realpath(join(dirname(__file__), '..')))
+import numpy as np
+
+import unittest
+import reachgraspio.reachgraspio as rg
+
+rio = rg.ReachGraspIO("../../datasets_blackrock/i140703-001",
+                      odml_directory="../../datasets_blackrock",
+                      nsx_to_load='all')
+block1 = rio.read_block(lazy=True)
+
+rio = rg.ReachGraspIO("../../datasets_blackrock/l101210-001",
+                      odml_directory="../../datasets_blackrock",
+                      nsx_to_load='all')
+block2 = rio.read_block(lazy=True)
+
+class RGIOTestCase(unittest.TestCase):
+    def setUp(self):
+        self.blocks = [block1, block2]
+
+    def test_group_units_present(self):
+        for block in self.blocks:
+            unit_groups = [g for g in block.groups if 'Unit' in g.name]
+            self.assertGreater(len(unit_groups), 0)
+            self.assertGreaterEqual(len(unit_groups), len(block.segments[0].spiketrains))
+
+    def test_channel_infos_present(self):
+        for block in self.blocks:
+            for seg in block.segments:
+                for anasig in seg.analogsignals:
+                    if anasig.annotations['neural_signal']:
+                        self.assertIn('connector_aligned_ids', anasig.array_annotations)
+                        self.assertIn('coordinates_x', anasig.array_annotations)
+                        self.assertIn('coordinates_y', anasig.array_annotations)
+
+    def test_group_unit_annotations(self):
+        for block in self.blocks:
+            for group in block.groups:
+                if 'Unit' in group.name:
+                    self.assertIn('unit_id', group.annotations)
+                    self.assertIn('connector_aligned_id', group.annotations)
+                    self.assertIn('sua', group.annotations)
+                    self.assertIn('mua', group.annotations)
+                    self.assertIn('noise', group.annotations)
+
+                    # To be investigated
+                    # if group.annotations['unit_id'] > 0:
+                    #     print(group.annotations)
+                    #     self.assertIn('spike_duration', group.annotations)
+                    #     self.assertIn('spike_amplitude', group.annotations)
+                    #     self.assertIn('spike_count', group.annotations)
+
+    def test_group_unit_linking(self):
+        for block in self.blocks:
+            # run this test only if neuronal signals are present
+            if not [a for seg in block.segments for a in seg.analogsignals if
+                    a.annotations['neural_signal']]:
+                return
+            for group in block.groups:
+                if 'Unit' in group.name:
+                    asig_annotations = group.channelviews[0].obj.array_annotations
+                    idx = group.channelviews[0].index
+                    self.assertEqual(group.annotations['channel_id'],
+                                     asig_annotations['channel_ids'][idx])
+
+    def test_consecutive_trial_ids(self):
+        for block in self.blocks:
+            for seg in block.segments:
+                for ev in seg.events:
+                    if 'trial_id' in ev.array_annotations:
+                        # exclude invalid trial_ids:
+                        trial_ids = ev.array_annotations['trial_id']
+                        valid_ids = trial_ids[trial_ids != -1]
+
+                        # Lilou session has one trial (197), where the object
+                        # release happens late and during the next trial, thus
+                        # disrupting incremental trial IDs
+                        if split(
+                                block.file_origin)[1]=='l101210-001':
+                            valid_ids = valid_ids[valid_ids != 197]
+
+                        self.assertTrue(all(np.diff(valid_ids) >= 0))
+
+    def test_data_annotations_present(self):
+        for block in self.blocks:
+            for seg in block.segments:
+                for anasig in seg.analogsignals:
+                    if anasig.annotations['neural_signal']:
+                        for key in ['channel_ids', 'channel_names',
+                                    'hi_pass_freq', 'lo_pass_freq',
+                                    'hi_pass_order', 'lo_pass_order', 'filter_type',
+                                    'electrode_reject_IFC', 'electrode_reject_LFC',
+                                    'electrode_reject_HFC', 'coordinates_x', 'coordinates_y']:
+                            self.assertIn(key, anasig.array_annotations)
+
+                for st in seg.spiketrains:
+                    for key in ['id', 'channel_id', 'unit_id', 'unit_tag',
+                                'electrode_reject_IFC', 'electrode_reject_LFC', 'electrode_reject_HFC',
+                                'sua', 'mua', 'noise', 'coordinate_x', 'coordinate_y', 'connector_aligned_id']:
+                        self.assertIn(key, st.annotations)
+
+    def test_event_annotations(self):
+        for block in self.blocks:
+            for seg in block.segments:
+                for ev in seg.events:
+                    if ev.name in ['DigitalTrialEvents', 'AnalogTrialEvents', 'TrialEvents']:
+                        for key in ['trial_event_labels', 'trial_timestamp_id', 'trial_id',
+                                    'belongs_to_trialtype', 'performance_in_trial',
+                                    'performance_in_trial_str', 'trial_reject_HFC', 'trial_reject_LFC',
+                                    'trial_reject_IFC']:
+                            self.assertIn(key, ev.array_annotations)
+
+                ev_names = [ev.name for ev in seg.events]
+                for key in ['AnalogTrialEvents', 'DigitalTrialEvents', 'TrialEvents']:
+                    self.assertIn(key, ev_names)
+
+    def test_block_annotation(self):
+        for block in self.blocks:
+            self.assertIn('conditions', block.annotations)
+            self.assertGreater(len(block.annotations['conditions']), 0)
+
+    def test_connector_aligned_ids_coordinates(self):
+        for block in self.blocks:
+            for seg in block.segments:
+                for anasig in seg.analogsignals:
+                    if not anasig.annotations['neural_signal']:
+                        continue  # only check annotations of neural signals
+                    for coords in ['coordinates_x', 'coordinates_y']:
+                        if coords in anasig.array_annotations:
+                            self.assertEqual(len(np.unique(anasig.array_annotations[coords])), 10)
+                        self.assertIn('connector_aligned_ids', anasig.array_annotations)
+
+
+def suite():
+    suite = unittest.makeSuite(RGIOTestCase, 'test')
+    return suite
+
+if __name__ == "__main__":
+    runner = unittest.TextTestRunner(verbosity=2)
+    runner.run(suite())
+

+ 1 - 7
code/requirements.txt

@@ -1,12 +1,6 @@
-numpy>=1.8.2, <=1.22.3
-quantities>=0.10.1, <=0.13.0
-scipy>=0.14.0, <=1.8.0
-six; python_version >= '3.3'
-enum34; python_version < '3.4'
-
 # Plotting module required for example scripts
 matplotlib>=3.0.3
 
-neo>=0.9.0, <0.11
+neo[nixio]>=0.9.0, <0.11
 elephant>=0.9.0, <0.12
 odml>=1.5.1, <1.6

datasets/LICENSE.txt → datasets_blackrock/LICENSE.txt


datasets/README.md → datasets_blackrock/README.md


datasets/i140703-001-03.nev → datasets_blackrock/i140703-001-03.nev


datasets/i140703-001.ccf → datasets_blackrock/i140703-001.ccf


datasets/i140703-001.nev → datasets_blackrock/i140703-001.nev


datasets/i140703-001.ns2 → datasets_blackrock/i140703-001.ns2


datasets/i140703-001.ns6 → datasets_blackrock/i140703-001.ns6


+ 64 - 64
datasets/i140703-001.odml

@@ -64627,28 +64627,32 @@
               <name>OT</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value></value>
+              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
+              <value>29242500</value>
             </property>
             <property>
               <definition>Time when target object is released</definition>
               <name>OR</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value></value>
+              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
+              <value>29277390</value>
             </property>
             <property>
               <definition>Onset time plateau phase of used FSR sensor signal</definition>
               <name>FSRplat-ON</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value></value>
+              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
+              <value>29245020</value>
             </property>
             <property>
               <definition>Offset time plateau phase of used FSR sensor signal</definition>
               <name>FSRplat-OFF</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value></value>
+              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
+              <value>29257320</value>
             </property>
             <name>GripForceSignals</name>
             <type>recording/task/trial/events</type>
@@ -64660,28 +64664,32 @@
               <name>DO</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value></value>
+              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
+              <value>29244030</value>
             </property>
             <property>
               <definition>Time when object moved back to its baseline</definition>
               <name>OBB</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value></value>
+              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
+              <value>29270730</value>
             </property>
             <property>
               <definition>Onset time plateau phase of used HE sensor signal; corresponds to the offline detected hold start (HS)</definition>
               <name>HEplat-ON</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value></value>
+              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
+              <value>29250330</value>
             </property>
             <property>
               <definition>Offset time plateau phase of used HE sensor signal</definition>
               <name>HEplat-OFF</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value></value>
+              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
+              <value>29261040</value>
             </property>
             <name>DisplacementSignal</name>
             <type>recording/task/trial/events</type>
@@ -64730,35 +64738,35 @@
             <name>RGT</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value></value>
+            <value>13047</value>
           </property>
           <property>
             <definition>Pull time (period between DO and HEplat-ON)</definition>
             <name>PT</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value></value>
+            <value>6300</value>
           </property>
           <property>
             <definition>Movement time (period between SR and HEplat-ON)</definition>
             <name>MT</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value></value>
+            <value>20877</value>
           </property>
           <property>
             <definition>Duration of plateau in FSR signal (period between FSRplat-ON and FSRplat-OFF)</definition>
             <name>FSRplat</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value></value>
+            <value>12300</value>
           </property>
           <property>
             <definition>Duration of plateau in HE signal (period between HEplat-ON and HEplat-OFF)</definition>
             <name>HEplat</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value></value>
+            <value>10710</value>
           </property>
           <property>
             <definition>Inter-trial-interval (period until next trial)</definition>
@@ -64933,7 +64941,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29242500</value>
+              <value>29422500</value>
             </property>
             <property>
               <definition>Time when target object is released</definition>
@@ -64941,7 +64949,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29277390</value>
+              <value>29460450</value>
             </property>
             <property>
               <definition>Onset time plateau phase of used FSR sensor signal</definition>
@@ -64949,7 +64957,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29245020</value>
+              <value>29425380</value>
             </property>
             <property>
               <definition>Offset time plateau phase of used FSR sensor signal</definition>
@@ -64957,7 +64965,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29257320</value>
+              <value>29442210</value>
             </property>
             <name>GripForceSignals</name>
             <type>recording/task/trial/events</type>
@@ -64970,7 +64978,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29244030</value>
+              <value>29423760</value>
             </property>
             <property>
               <definition>Time when object moved back to its baseline</definition>
@@ -64978,7 +64986,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29270730</value>
+              <value>29460450</value>
             </property>
             <property>
               <definition>Onset time plateau phase of used HE sensor signal; corresponds to the offline detected hold start (HS)</definition>
@@ -64986,7 +64994,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29250330</value>
+              <value>29427000</value>
             </property>
             <property>
               <definition>Offset time plateau phase of used HE sensor signal</definition>
@@ -64994,7 +65002,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29261040</value>
+              <value>29445960</value>
             </property>
             <name>DisplacementSignal</name>
             <type>recording/task/trial/events</type>
@@ -65043,21 +65051,21 @@
             <name>RGT</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value>-168213</value>
+            <value>13047</value>
           </property>
           <property>
             <definition>Pull time (period between DO and HEplat-ON)</definition>
             <name>PT</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value>2520</value>
+            <value>6300</value>
           </property>
           <property>
             <definition>Movement time (period between SR and HEplat-ON)</definition>
             <name>MT</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value>-165693</value>
+            <value>20877</value>
           </property>
           <property>
             <definition>Duration of plateau in FSR signal (period between FSRplat-ON and FSRplat-OFF)</definition>
@@ -65246,7 +65254,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29422500</value>
+              <value>29604870</value>
             </property>
             <property>
               <definition>Time when target object is released</definition>
@@ -65254,7 +65262,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29460450</value>
+              <value>29635680</value>
             </property>
             <property>
               <definition>Onset time plateau phase of used FSR sensor signal</definition>
@@ -65262,7 +65270,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29425380</value>
+              <value>29612100</value>
             </property>
             <property>
               <definition>Offset time plateau phase of used FSR sensor signal</definition>
@@ -65270,7 +65278,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29442210</value>
+              <value>29621070</value>
             </property>
             <name>GripForceSignals</name>
             <type>recording/task/trial/events</type>
@@ -65283,7 +65291,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29423760</value>
+              <value>29606250</value>
             </property>
             <property>
               <definition>Time when object moved back to its baseline</definition>
@@ -65291,7 +65299,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29460450</value>
+              <value>29638950</value>
             </property>
             <property>
               <definition>Onset time plateau phase of used HE sensor signal; corresponds to the offline detected hold start (HS)</definition>
@@ -65299,7 +65307,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29427000</value>
+              <value>29612070</value>
             </property>
             <property>
               <definition>Offset time plateau phase of used HE sensor signal</definition>
@@ -65307,7 +65315,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29445960</value>
+              <value>29623560</value>
             </property>
             <name>DisplacementSignal</name>
             <type>recording/task/trial/events</type>
@@ -65559,7 +65567,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29604870</value>
+              <value>29789100</value>
             </property>
             <property>
               <definition>Time when target object is released</definition>
@@ -65567,7 +65575,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29635680</value>
+              <value>29830680</value>
             </property>
             <property>
               <definition>Onset time plateau phase of used FSR sensor signal</definition>
@@ -65575,7 +65583,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29612100</value>
+              <value>29790750</value>
             </property>
             <property>
               <definition>Offset time plateau phase of used FSR sensor signal</definition>
@@ -65583,7 +65591,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29621070</value>
+              <value>29806080</value>
             </property>
             <name>GripForceSignals</name>
             <type>recording/task/trial/events</type>
@@ -65596,7 +65604,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29606250</value>
+              <value>29790030</value>
             </property>
             <property>
               <definition>Time when object moved back to its baseline</definition>
@@ -65604,7 +65612,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29638950</value>
+              <value>29829450</value>
             </property>
             <property>
               <definition>Onset time plateau phase of used HE sensor signal; corresponds to the offline detected hold start (HS)</definition>
@@ -65612,7 +65620,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29612070</value>
+              <value>29793930</value>
             </property>
             <property>
               <definition>Offset time plateau phase of used HE sensor signal</definition>
@@ -65620,7 +65628,7 @@
               <unit>1./30000*s</unit>
               <type>int</type>
               <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29623560</value>
+              <value>29809050</value>
             </property>
             <name>DisplacementSignal</name>
             <type>recording/task/trial/events</type>
@@ -65669,21 +65677,21 @@
             <name>RGT</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value>-170989</value>
+            <value>13080</value>
           </property>
           <property>
             <definition>Pull time (period between DO and HEplat-ON)</definition>
             <name>PT</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value>7230</value>
+            <value>5820</value>
           </property>
           <property>
             <definition>Movement time (period between SR and HEplat-ON)</definition>
             <name>MT</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value>-163759</value>
+            <value>20280</value>
           </property>
           <property>
             <definition>Duration of plateau in FSR signal (period between FSRplat-ON and FSRplat-OFF)</definition>
@@ -65871,32 +65879,28 @@
               <name>OT</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29789100</value>
+              <value></value>
             </property>
             <property>
               <definition>Time when target object is released</definition>
               <name>OR</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29830680</value>
+              <value></value>
             </property>
             <property>
               <definition>Onset time plateau phase of used FSR sensor signal</definition>
               <name>FSRplat-ON</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29790750</value>
+              <value></value>
             </property>
             <property>
               <definition>Offset time plateau phase of used FSR sensor signal</definition>
               <name>FSRplat-OFF</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29806080</value>
+              <value></value>
             </property>
             <name>GripForceSignals</name>
             <type>recording/task/trial/events</type>
@@ -65908,32 +65912,28 @@
               <name>DO</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29790030</value>
+              <value></value>
             </property>
             <property>
               <definition>Time when object moved back to its baseline</definition>
               <name>OBB</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29829450</value>
+              <value></value>
             </property>
             <property>
               <definition>Onset time plateau phase of used HE sensor signal; corresponds to the offline detected hold start (HS)</definition>
               <name>HEplat-ON</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29793930</value>
+              <value></value>
             </property>
             <property>
               <definition>Offset time plateau phase of used HE sensor signal</definition>
               <name>HEplat-OFF</name>
               <unit>1./30000*s</unit>
               <type>int</type>
-              <value_origin>fe_i140703-001_time_markers.mat</value_origin>
-              <value>29809050</value>
+              <value></value>
             </property>
             <name>DisplacementSignal</name>
             <type>recording/task/trial/events</type>
@@ -65982,35 +65982,35 @@
             <name>RGT</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value>-171005</value>
+            <value></value>
           </property>
           <property>
             <definition>Pull time (period between DO and HEplat-ON)</definition>
             <name>PT</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value>1650</value>
+            <value></value>
           </property>
           <property>
             <definition>Movement time (period between SR and HEplat-ON)</definition>
             <name>MT</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value>-169355</value>
+            <value></value>
           </property>
           <property>
             <definition>Duration of plateau in FSR signal (period between FSRplat-ON and FSRplat-OFF)</definition>
             <name>FSRplat</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value>15330</value>
+            <value></value>
           </property>
           <property>
             <definition>Duration of plateau in HE signal (period between HEplat-ON and HEplat-OFF)</definition>
             <name>HEplat</name>
             <unit>1./30000*s</unit>
             <type>int</type>
-            <value>15120</value>
+            <value></value>
           </property>
           <property>
             <definition>Inter-trial-interval (period until next trial)</definition>

BIN
datasets/i140703-001.xls


datasets/l101210-001-02.nev → datasets_blackrock/l101210-001-02.nev


datasets/l101210-001.ccf → datasets_blackrock/l101210-001.ccf


datasets/l101210-001.nev → datasets_blackrock/l101210-001.nev


datasets/l101210-001.ns2 → datasets_blackrock/l101210-001.ns2


datasets/l101210-001.ns5 → datasets_blackrock/l101210-001.ns5


datasets/l101210-001.odml → datasets_blackrock/l101210-001.odml


datasets/l101210-001.xls → datasets_blackrock/l101210-001.xls


+ 1 - 0
datasets_nix/i140703-001.nix

@@ -0,0 +1 @@
+/annex/objects/MD5-s12344369847--74b426b6b03b10bf415f2d37c68b1264

+ 1 - 0
datasets_nix/i140703-001_no_raw.nix

@@ -0,0 +1 @@
+/annex/objects/MD5-s779109880--5c5e443f3b53361a1bed3ec1bcad1b0b

+ 1 - 0
datasets_nix/l101210-001.nix

@@ -0,0 +1 @@
+/annex/objects/MD5-s9057265427--8c1520b2689942f1439ff75d973856cd

+ 1 - 0
datasets_nix/l101210-001_no_raw.nix

@@ -0,0 +1 @@
+/annex/objects/MD5-s882371719--f20162beefd2af1077325c445035a9d0