Natus/Nicolet/Nervus .e → EDF+
An open-source Python converter for Natus/Nicolet/Nervus .e EEG recordings to standard EDF+ format. It runs without vendor DLLs or MATLAB and can convert individual recordings or folders from the command line. Legacy .eeg support is experimental; see Limitations.
Note: Some reverse-engineered event labels are currently in Norwegian.
Quick Start
Install the latest release from PyPI. To run it once without installing it permanently:
uvx nicolet-e2edf --help
uvx nicolet-e2edf --in /path/to/recording.e --out ./edf_output
To install it as a persistent uv tool:
uv tool install nicolet-e2edf
nicolet-e2edf --in /path/to/recording.e --out ./edf_output
Or install it with pip:
python -m pip install nicolet-e2edf
nicolet-e2edf --in /path/to/recording.e --out ./edf_output
The input can also be a folder containing .e/.eeg files:
nicolet-e2edf --in ./my_eeg_folder --out ./edf_output
Install from source
For development, clone the repository and let uv create the project environment:
git clone https://github.com/haukurtg/e2edfconverter.git
cd e2edfconverter
uv sync
uv run nicolet-e2edf --help
Optional faster reads (0.4.0)
Conversion got faster in 0.4.0 without changing the output bytes. There is also an experimental reader that merges adjacent disk reads into bigger ones (capped at 8 MiB per read). It produces identical output but is off by default for now; turn it on if you want the extra speed:
NICOLET_E2EDF_COALESCE_READS=1 nicolet-e2edf \
--in /path/to/recording.e --out ./edf_output
or from Python: read_nervus_data(path, header, coalesce_reads=True).
Interactive Mode
For a guided experience with menus and progress bars:
uvx --with rich nicolet-e2edf --ui
For a persistent installation, use python -m pip install "nicolet-e2edf[tui]"
or uv tool install "nicolet-e2edf[tui]", then run nicolet-e2edf --ui.
CLI Options
| Option | Description |
|---|---|
--in |
Input .e/.eeg file or folder |
--out |
Output directory for EDF files |
--glob |
Filter pattern when input is a folder (e.g. recording_*) |
--json-sidecar |
Also emit a .json with metadata (channels, events, etc.) |
--split-by-segment |
Output one EDF per segment if the recording contains multiple segments |
--vendor-style |
Suppress system events to better match vendor EDF exports |
--resample-to |
Resample to a specific rate (Hz) (requires scipy) |
--lowcut |
High-pass filter cutoff in Hz (requires scipy) |
--highcut |
Low-pass filter cutoff in Hz (requires scipy) |
--notch |
Notch filter for powerline noise, e.g. 50 or 60 Hz (requires scipy) |
--ui |
Launch interactive terminal UI (requires rich) |
--verbose |
Show detailed logging |
Filtering example:
# Install the optional filtering dependency once
python -m pip install "nicolet-e2edf[filter]"
# Clinical defaults: 0.5–35 Hz bandpass + 50 Hz notch
nicolet-e2edf \
--in ./data --out ./edf_output \
--lowcut 0.5 --highcut 35 --notch 50
Vendor-style comparison example:
# Match vendor-style exports (split per segment + suppress system events)
nicolet-e2edf \
--in /path/to/recording.e --out ./edf_output \
--split-by-segment --vendor-style --json-sidecar
Viewing the Results
In a source checkout, the bundled viewer script shows your EDF in a double-banana montage:
uv run --isolated --with mne python inspect_edf.py ./edf_output/recording.edf
Note: When using the interactive TUI (--ui), the viewer is automatically launched with MNE in an isolated environment if needed. No manual installation required!
Options: --lowcut, --highcut, --notch, --snapshot out.png (for headless systems).
Filtering during conversion (--lowcut, --highcut, --notch) is lossy. In most cases, keep exports unfiltered and only use conversion-time filtering when you intentionally want a preprocessed output for direct downstream use (for example, an ML pipeline).
Limitations
- Mixed sampling rates: default exports only dominant-rate channels; use
--resample-toto include all "on" channels. - When
--resample-tois used, channels are resampled to the requested integer EDF rate. - Events are written as EDF+ annotations
- EVENTTYPEINFOGUID labels are reverse-engineered; unknown GUIDs may be exported as UNKNOWN.
.eegsupport is currently not reliable; we need a larger.eegdataset to implement and validate it properly.- Some
.erecordings store only numeric channel IDs (e.g.,1..64). The numeric-channel fix and montage-recovery strategy (fromv0.2.5) are mainly aimed at recovering channel names in atypical multi-channel EEG setups (32,64,128, etc.) using source montage derivations, fixed DERIVATION tables, and hidden montage catalogs. - The CLI supports folder input, but processes files serially. For large cohorts, it is usually more efficient to call the CLI from a small batch wrapper that runs multiple workers and tracks progress/errors.
Contributing
Contributions are welcome! If you're working on the EDF writer or want to understand the file format:
- EDF+ Specification: A copy of the full EDF+ specification is included at
docs/EDF+ specification.pdf. The official spec is also available at edfplus.info. - Tests: Run
uv run pytestto verify EDF+ compliance. We use PyEDFlib as a strict validator.
Profiling And Regression Checks
Two helper scripts are included for speed work that must not change output:
tools/profile_conversion_stages.py- Runs an in-process conversion profile for one or more
.efiles. - Breaks runtime into rough stages such as header read, waveform read, EDF write, and JSON write.
- Runs an in-process conversion profile for one or more
tools/validate_regression_equivalence.py- Re-converts a regression corpus and compares the result against a known-good baseline.
- Checks EDF byte equality plus exact equality of sidecar
channels,events, andannotations(ignoring only the expectededf_fileoutput path field).
Recommended workflow for performance changes:
- Profile on a small representative local corpus.
- Make the optimization.
- Run
uv run pytest. - Run the regression-equivalence validator before merging.
Acknowledgements
The MATLAB implementation of the Nervus/Nicolet file format in the FieldTrip toolbox provided the foundation for this Python port. Additional GUID, event and channel-ID handling was developed through reverse engineering.
Development was assisted by various coding models used through Cursor.
License
GPL-3.0 — see LICENSE.
This project adapts logic from the FieldTrip toolbox (GPL-3.0).
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nicolet_e2edf-0.4.1.tar.gz.
File metadata
- Download URL: nicolet_e2edf-0.4.1.tar.gz
- Upload date:
- Size: 127.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ffb3f38a60231c568a56df27e27d99165847b636de1ddcbad4aa5754621b0a61
|
|
| MD5 |
25d765504ccc85a5c88edb239f7b175b
|
|
| BLAKE2b-256 |
0c961731451aa3b5034526ed3914b97d28f7d77177aab8aa36ab8c1f7f195d85
|
Provenance
The following attestation bundles were made for nicolet_e2edf-0.4.1.tar.gz:
Publisher:
publish-pypi.yml on haukurtg/e2edfconverter
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nicolet_e2edf-0.4.1.tar.gz -
Subject digest:
ffb3f38a60231c568a56df27e27d99165847b636de1ddcbad4aa5754621b0a61 - Sigstore transparency entry: 2498609271
- Sigstore integration time:
-
Permalink:
haukurtg/e2edfconverter@56e19e570b2ffbe47bbce91a4bd2c2d84c929a2c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/haukurtg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@56e19e570b2ffbe47bbce91a4bd2c2d84c929a2c -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file nicolet_e2edf-0.4.1-py3-none-any.whl.
File metadata
- Download URL: nicolet_e2edf-0.4.1-py3-none-any.whl
- Upload date:
- Size: 123.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
093fa9891d4dc0f2d1b17b1d3ac168aad6daeaede42a0a263d464e28a2974667
|
|
| MD5 |
04ea2c59e771ae7a017ed82691316023
|
|
| BLAKE2b-256 |
ba3eba068e83229b940dae0a110754ce5d6c77d9c3736b2370334aae26aa2c9f
|
Provenance
The following attestation bundles were made for nicolet_e2edf-0.4.1-py3-none-any.whl:
Publisher:
publish-pypi.yml on haukurtg/e2edfconverter
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nicolet_e2edf-0.4.1-py3-none-any.whl -
Subject digest:
093fa9891d4dc0f2d1b17b1d3ac168aad6daeaede42a0a263d464e28a2974667 - Sigstore transparency entry: 2498609282
- Sigstore integration time:
-
Permalink:
haukurtg/e2edfconverter@56e19e570b2ffbe47bbce91a4bd2c2d84c929a2c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/haukurtg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@56e19e570b2ffbe47bbce91a4bd2c2d84c929a2c -
Trigger Event:
workflow_dispatch
-
Statement type: