mne-python icon indicating copy to clipboard operation
mne-python copied to clipboard

Raw export edf file failed

Open BEGINRX opened this issue 9 months ago • 6 comments

Description of the problem

I tried to export the data to edf format file. It failed. The code is : raw = mne.io.read_raw_edf(r"\\NITD-NAS\bci-rw\sleep\sleep-edf-database-expanded-1.0.0\sleep-edf-database-expanded-1.0.0\sleep-cassette\SC4001E0-PSG.edf") raw.export(fname=r"E:\work\NITD\software_test\outputedf.edf", fmt="edf", physical_range="channelwise")

And the error is

Image

Steps to reproduce

raw = mne.io.read_raw_edf(r"\\NITD-NAS\bci-rw\sleep\sleep-edf-database-expanded-1.0.0\sleep-edf-database-expanded-1.0.0\sleep-cassette\SC4001E0-PSG.edf")
raw.export(fname=r"E:\work\NITD\software_test\outputedf.edf", fmt="edf", physical_range="channelwise")

Link to data

No response

Expected results

Get the edf file.

Actual results

Get the edf file.

Additional information

Error.

BEGINRX avatar Mar 20 '25 10:03 BEGINRX

Hello! 👋 Thanks for opening your first issue here! ❤️ We will try to get back to you soon. 🚴

welcome[bot] avatar Mar 20 '25 10:03 welcome[bot]

Hi @BEGINRX ,
I would like to work on this issue. Have you tried any other export formats, or does the error only happen with fmt="edf"? Also, could you share the exact error message to understand the problem better?

Thanks!

Saksham8540 avatar Mar 21 '25 06:03 Saksham8540

Hi. I have tried other formats like "brainvision" and "eeglab". The files were exported successfully. Yes, the error only happened in edf.

The error messages are only the ones listed above. There is no more information.

May I send you the raw file? Or you can download from the website https://physionet.org/content/sleep-edfx/1.0.0/sleep-cassette/#files-panel

Image

BEGINRX avatar Mar 21 '25 07:03 BEGINRX

To me this looks like a bug in the _round_float_to_8_characters method from edfio. For -2006585738.63211, which is the value of pmin in the provided example data, it returns -2006590000.0.

Script to reproduce:

#!/usr/bin/env python3

import mne
import math
import numpy as np
from typing import Callable


def _round_float_to_8_characters(
    value: float,
    round_func: Callable[[float], int],
) -> float:
    if isinstance(value, int) or value.is_integer():
        return value
    length = 8
    integer_part_length = str(value).find(".")
    if integer_part_length == length:
        return round_func(value)
    factor = 10 ** (length - 1 - integer_part_length)
    return round_func(value * factor) / factor


# https://physionet.org/files/sleep-edfx/1.0.0/sleep-cassette/SC4001E0-PSG.edf?download
raw = mne.io.read_raw_edf('SC4001E0-PSG.edf')

# relevant code from: _export_raw_edf_bdf()  
units = dict(
    eeg="uV", ecog="uV", seeg="uV", eog="uV", ecg="uV", emg="uV", bio="uV", dbs="uV"
)

ch_types = np.array(raw.get_channel_types())

ch_types_phys_max = dict()
ch_types_phys_min = dict()

for _type in np.unique(ch_types):
    _picks = [n for n, t in zip(raw.ch_names, ch_types) if t == _type]
    _data = raw.get_data(units=units, picks=_picks)
    ch_types_phys_max[_type] = _data.max()
    ch_types_phys_min[_type] = _data.min()

ch_type = ch_types[0]

pmin = ch_types_phys_min[ch_type]
pmax = ch_types_phys_max[ch_type]
if pmax == pmin:
    pmax = pmin + 1
prange = pmin, pmax


print("before _round_float_to_8_characters:", pmin)


# functions that are called by EdfSignal constructor
x = _round_float_to_8_characters(pmin, math.floor)
if float(x).is_integer():
    x = int(x)


print("after _round_float_to_8_characters:", x)


# trigger the error
raw.export("output.edf")

mistraube avatar Oct 26 '25 21:10 mistraube

Looking at the physical_range of the signals using edfio directly, it seems that -2006585738.63211 is wrong in the first place.

#!/usr/bin/env python3

import edfio

edf = edfio.read_edf("SC4001E0-PSG.edf")

for sig in edf.signals:
    print(sig.physical_dimension)
    print(sig.physical_range)
    print()

output:

uV
_FloatRange(min=-192.0, max=192.0)

uV
_FloatRange(min=-197.0, max=196.0)

uV
_FloatRange(min=-1009.0, max=1009.0)


_FloatRange(min=-2048.0, max=2047.0)

uV
_FloatRange(min=-5.0, max=5.0)

DegC
_FloatRange(min=34.0, max=40.0)


_FloatRange(min=-2047.0, max=2048.0)

mistraube avatar Oct 27 '25 07:10 mistraube

I think the issue is with the .edf file itself. The header does not specify an unit for the "Resp oro-nasal" channel and physical range min -2048, but the actual data min is -2006585738.63; but -2006585738.63 does not fit in the 16 bits for digital_min.

mistraube avatar Oct 27 '25 09:10 mistraube