Python library for reading and writing EDF+/BDF+ files used for storing biomedical signal data
—
Interface for creating and writing EDF, EDF+, BDF, and BDF+ files with comprehensive header configuration, signal data writing, and annotation support. The EdfWriter class provides Python-friendly methods with automatic resource management and validation.
Create new EDF files with specified channel count and file type, with automatic resource management through context managers.
class EdfWriter:
def __init__(self, file_name: str, n_channels: int, file_type: int = FILETYPE_EDFPLUS):
"""
Initialize EDF writer for new file.
Parameters:
- file_name: str, output file path
- n_channels: int, number of signal channels
- file_type: int, file format (FILETYPE_EDF, FILETYPE_EDFPLUS, FILETYPE_BDF, FILETYPE_BDFPLUS)
"""
def __enter__(self) -> EdfWriter:
"""Context manager entry."""
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit with automatic file closure."""
def close(self):
"""Close file and finalize writing."""
def update_header(self):
"""Update file header with current settings."""Usage example:
# Using context manager (recommended)
with pyedflib.EdfWriter('output.edf', 2, pyedflib.FILETYPE_EDFPLUS) as f:
# Configure and write data
f.setSignalHeaders(signal_headers)
f.writeSamples(data)
# Manual management
f = pyedflib.EdfWriter('output.edf', 2)
try:
f.setSignalHeaders(signal_headers)
f.writeSamples(data)
finally:
f.close()Set file-level and signal-level header information including patient data, recording details, and signal parameters.
def setHeader(self, fileHeader: Dict):
"""
Set file header from dictionary.
Parameters:
- fileHeader: dict, complete file header information
"""
def setSignalHeader(self, edfsignal: int, channel_info: Dict):
"""
Set header for specific signal channel.
Parameters:
- edfsignal: int, channel number (0-based)
- channel_info: dict, signal configuration
"""
def setSignalHeaders(self, signalHeaders: List[Dict]):
"""
Set headers for all signal channels.
Parameters:
- signalHeaders: List[dict], list of signal configurations
"""Usage example:
# Create signal headers
signal_headers = [
{
'label': 'EEG Fp1',
'dimension': 'uV',
'sample_frequency': 256,
'physical_min': -500.0,
'physical_max': 500.0,
'digital_min': -32768,
'digital_max': 32767,
'transducer': 'AgAgCl electrodes',
'prefilter': 'HP:0.1Hz LP:70Hz'
},
{
'label': 'EEG Fp2',
'dimension': 'uV',
'sample_frequency': 256,
'physical_min': -500.0,
'physical_max': 500.0,
'digital_min': -32768,
'digital_max': 32767,
'transducer': 'AgAgCl electrodes',
'prefilter': 'HP:0.1Hz LP:70Hz'
}
]
with pyedflib.EdfWriter('eeg.edf', 2) as f:
f.setSignalHeaders(signal_headers)Set patient demographics and identification information.
def setPatientName(self, patient_name: str):
"""Set patient name."""
def setPatientCode(self, patient_code: str):
"""Set patient identification code."""
def setPatientAdditional(self, patient_additional: str):
"""Set additional patient information."""
def setSex(self, sex: int):
"""
Set patient sex.
Parameters:
- sex: int, patient sex (0=female, 1=male)
"""
def setBirthdate(self, birthdate: Union[str, date]):
"""
Set patient birthdate.
Parameters:
- birthdate: str or date, birthdate in YYYY-MM-DD format or date object
"""Set recording session metadata and technical details.
def setTechnician(self, technician: str):
"""Set technician name."""
def setRecordingAdditional(self, recording_additional: str):
"""Set additional recording information."""
def setEquipment(self, equipment: str):
"""Set recording equipment information."""
def setAdmincode(self, admincode: str):
"""Set administration code."""
def setStartdatetime(self, recording_start_time: Union[datetime, str]):
"""
Set recording start date and time.
Parameters:
- recording_start_time: datetime or str, start time
"""
def setDatarecordDuration(self, record_duration: Union[float, int]):
"""
Set data record duration in seconds.
Parameters:
- record_duration: float or int, duration per data record
"""
def set_number_of_annotation_signals(self, number_of_annotations: int):
"""
Set number of annotation signals.
Parameters:
- number_of_annotations: int, annotation signal count
"""Configure individual signal channel properties including sampling rates, calibration, and metadata.
def setSamplefrequency(self, edfsignal: int, samplefrequency: Union[int, float]):
"""
Set sampling frequency for signal.
Parameters:
- edfsignal: int, channel number
- samplefrequency: int or float, sample rate in Hz
"""
def setPhysicalMaximum(self, edfsignal: int, physical_maximum: Union[int, float]):
"""
Set physical maximum value for signal.
Parameters:
- edfsignal: int, channel number
- physical_maximum: int or float, maximum physical value
"""
def setPhysicalMinimum(self, edfsignal: int, physical_minimum: Union[int, float]):
"""
Set physical minimum value for signal.
Parameters:
- edfsignal: int, channel number
- physical_minimum: int or float, minimum physical value
"""
def setDigitalMaximum(self, edfsignal: int, digital_maximum: int):
"""
Set digital maximum value for signal.
Parameters:
- edfsignal: int, channel number
- digital_maximum: int, maximum digital value
"""
def setDigitalMinimum(self, edfsignal: int, digital_minimum: int):
"""
Set digital minimum value for signal.
Parameters:
- edfsignal: int, channel number
- digital_minimum: int, minimum digital value
"""
def setLabel(self, edfsignal: int, label: str):
"""
Set signal label.
Parameters:
- edfsignal: int, channel number
- label: str, signal label/name
"""
def setPhysicalDimension(self, edfsignal: int, physical_dimension: str):
"""
Set physical dimension (units) for signal.
Parameters:
- edfsignal: int, channel number
- physical_dimension: str, units (e.g., 'uV', 'mV')
"""
def setTransducer(self, edfsignal: int, transducer: str):
"""
Set transducer information for signal.
Parameters:
- edfsignal: int, channel number
- transducer: str, transducer description
"""
def setPrefilter(self, edfsignal: int, prefilter: str):
"""
Set prefilter information for signal.
Parameters:
- edfsignal: int, channel number
- prefilter: str, prefilter description
"""Write signal data to the file with support for different data types and writing modes.
def writeSamples(self, data_list: Union[List[np.ndarray], np.ndarray], digital: bool = False):
"""
Write signal samples to file.
Parameters:
- data_list: List[numpy.ndarray] or numpy.ndarray, signal data for each channel
- digital: bool, whether data contains digital values (True) or physical values (False)
"""
def writePhysicalSamples(self, data: np.ndarray) -> int:
"""
Write physical samples for all channels.
Parameters:
- data: numpy.ndarray, physical sample values
Returns:
int: Number of samples written
"""
def writeDigitalSamples(self, data: np.ndarray) -> int:
"""
Write digital samples for all channels.
Parameters:
- data: numpy.ndarray, digital sample values
Returns:
int: Number of samples written
"""
def writeDigitalShortSamples(self, data: np.ndarray) -> int:
"""
Write digital short samples for all channels.
Parameters:
- data: numpy.ndarray, digital short sample values
Returns:
int: Number of samples written
"""
def blockWritePhysicalSamples(self, data: np.ndarray) -> int:
"""
Block write physical samples.
Parameters:
- data: numpy.ndarray, physical sample data
Returns:
int: Number of samples written
"""
def blockWriteDigitalSamples(self, data: np.ndarray) -> int:
"""
Block write digital samples.
Parameters:
- data: numpy.ndarray, digital sample data
Returns:
int: Number of samples written
"""
def blockWriteDigitalShortSamples(self, data: np.ndarray) -> int:
"""
Block write digital short samples.
Parameters:
- data: numpy.ndarray, digital short sample data
Returns:
int: Number of samples written
"""Usage example:
import numpy as np
# Generate sample data (1000 samples at 256 Hz for 2 channels)
data_ch1 = np.random.normal(0, 100, 1000) # EEG-like signal in uV
data_ch2 = np.random.normal(0, 100, 1000)
with pyedflib.EdfWriter('sample.edf', 2) as f:
f.setSignalHeaders(signal_headers)
# Write as list of arrays (recommended)
f.writeSamples([data_ch1, data_ch2])
# Or write as 2D array (channels x samples)
data_2d = np.array([data_ch1, data_ch2])
f.writeSamples(data_2d)Add annotations (events, markers) to EDF+ and BDF+ files with timing and description information.
def writeAnnotation(self, onset_in_seconds: Union[int, float],
duration_in_seconds: Union[int, float],
description: str, str_format: str = 'utf8'):
"""
Write annotation to file.
Parameters:
- onset_in_seconds: int or float, annotation start time in seconds
- duration_in_seconds: int or float, annotation duration in seconds
- description: str, annotation text description
- str_format: str, text encoding ('utf8' or 'latin1')
"""Usage example:
with pyedflib.EdfWriter('annotated.edf', 2, pyedflib.FILETYPE_EDFPLUS) as f:
f.setSignalHeaders(signal_headers)
f.writeSamples([data_ch1, data_ch2])
# Add annotations
f.writeAnnotation(10.0, 0.0, "Stimulus onset")
f.writeAnnotation(15.5, 2.0, "Patient movement")
f.writeAnnotation(30.0, 0.0, "End of trial")Helper methods for configuration and validation.
def get_smp_per_record(self, ch_idx: int) -> int:
"""
Get samples per record for channel.
Parameters:
- ch_idx: int, channel index
Returns:
int: Samples per data record
"""import pyedflib
import numpy as np
from datetime import datetime
# Create sample EEG data
n_channels = 4
n_samples = 2560 # 10 seconds at 256 Hz
sample_rate = 256
# Generate realistic EEG-like signals
channels = ['EEG Fp1', 'EEG Fp2', 'EEG C3', 'EEG C4']
data = []
for i in range(n_channels):
# Mix of sine waves with noise to simulate EEG
t = np.linspace(0, 10, n_samples)
signal = (np.sin(2*np.pi*10*t) * 50 + # 10 Hz alpha-like
np.sin(2*np.pi*4*t) * 30 + # 4 Hz theta-like
np.random.normal(0, 20, n_samples)) # noise
data.append(signal)
# Create signal headers
signal_headers = []
for i, label in enumerate(channels):
signal_headers.append({
'label': label,
'dimension': 'uV',
'sample_frequency': sample_rate,
'physical_min': -500.0,
'physical_max': 500.0,
'digital_min': -32768,
'digital_max': 32767,
'transducer': 'AgAgCl electrodes',
'prefilter': 'HP:0.1Hz LP:70Hz'
})
# Create file header
file_header = {
'technician': 'Dr. Smith',
'recording_additional': 'EEG sleep study',
'patientname': 'Patient001',
'patient_additional': 'Age 25',
'patientcode': 'P001',
'equipment': 'EEG System v2.1',
'admincode': 'ADMIN001',
'sex': 'M',
'startdate': datetime.now(),
'birthdate': '1998-01-15'
}
# Write EDF file
with pyedflib.EdfWriter('complete_example.edf', n_channels, pyedflib.FILETYPE_EDFPLUS) as f:
# Set headers
f.setHeader(file_header)
f.setSignalHeaders(signal_headers)
# Write data
f.writeSamples(data)
# Add annotations
f.writeAnnotation(2.0, 0.0, "Eyes closed")
f.writeAnnotation(5.0, 1.0, "Sleep spindle")
f.writeAnnotation(8.0, 0.0, "Eyes open")
print("EDF file created successfully!")# File type constants
FILETYPE_EDF: int # Standard EDF format
FILETYPE_EDFPLUS: int # EDF+ format with annotations
FILETYPE_BDF: int # BDF format (24-bit)
FILETYPE_BDFPLUS: int # BDF+ format with annotationsInstall with Tessl CLI
npx tessl i tessl/pypi-pyedflib