0
# EDF File Writing
1
2
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.
3
4
## Capabilities
5
6
### File Creation and Management
7
8
Create new EDF files with specified channel count and file type, with automatic resource management through context managers.
9
10
```python { .api }
11
class EdfWriter:
12
def __init__(self, file_name: str, n_channels: int, file_type: int = FILETYPE_EDFPLUS):
13
"""
14
Initialize EDF writer for new file.
15
16
Parameters:
17
- file_name: str, output file path
18
- n_channels: int, number of signal channels
19
- file_type: int, file format (FILETYPE_EDF, FILETYPE_EDFPLUS, FILETYPE_BDF, FILETYPE_BDFPLUS)
20
"""
21
22
def __enter__(self) -> EdfWriter:
23
"""Context manager entry."""
24
25
def __exit__(self, exc_type, exc_val, exc_tb):
26
"""Context manager exit with automatic file closure."""
27
28
def close(self):
29
"""Close file and finalize writing."""
30
31
def update_header(self):
32
"""Update file header with current settings."""
33
```
34
35
Usage example:
36
37
```python
38
# Using context manager (recommended)
39
with pyedflib.EdfWriter('output.edf', 2, pyedflib.FILETYPE_EDFPLUS) as f:
40
# Configure and write data
41
f.setSignalHeaders(signal_headers)
42
f.writeSamples(data)
43
44
# Manual management
45
f = pyedflib.EdfWriter('output.edf', 2)
46
try:
47
f.setSignalHeaders(signal_headers)
48
f.writeSamples(data)
49
finally:
50
f.close()
51
```
52
53
### Header Configuration
54
55
Set file-level and signal-level header information including patient data, recording details, and signal parameters.
56
57
```python { .api }
58
def setHeader(self, fileHeader: Dict):
59
"""
60
Set file header from dictionary.
61
62
Parameters:
63
- fileHeader: dict, complete file header information
64
"""
65
66
def setSignalHeader(self, edfsignal: int, channel_info: Dict):
67
"""
68
Set header for specific signal channel.
69
70
Parameters:
71
- edfsignal: int, channel number (0-based)
72
- channel_info: dict, signal configuration
73
"""
74
75
def setSignalHeaders(self, signalHeaders: List[Dict]):
76
"""
77
Set headers for all signal channels.
78
79
Parameters:
80
- signalHeaders: List[dict], list of signal configurations
81
"""
82
```
83
84
Usage example:
85
86
```python
87
# Create signal headers
88
signal_headers = [
89
{
90
'label': 'EEG Fp1',
91
'dimension': 'uV',
92
'sample_frequency': 256,
93
'physical_min': -500.0,
94
'physical_max': 500.0,
95
'digital_min': -32768,
96
'digital_max': 32767,
97
'transducer': 'AgAgCl electrodes',
98
'prefilter': 'HP:0.1Hz LP:70Hz'
99
},
100
{
101
'label': 'EEG Fp2',
102
'dimension': 'uV',
103
'sample_frequency': 256,
104
'physical_min': -500.0,
105
'physical_max': 500.0,
106
'digital_min': -32768,
107
'digital_max': 32767,
108
'transducer': 'AgAgCl electrodes',
109
'prefilter': 'HP:0.1Hz LP:70Hz'
110
}
111
]
112
113
with pyedflib.EdfWriter('eeg.edf', 2) as f:
114
f.setSignalHeaders(signal_headers)
115
```
116
117
### Patient Information
118
119
Set patient demographics and identification information.
120
121
```python { .api }
122
def setPatientName(self, patient_name: str):
123
"""Set patient name."""
124
125
def setPatientCode(self, patient_code: str):
126
"""Set patient identification code."""
127
128
def setPatientAdditional(self, patient_additional: str):
129
"""Set additional patient information."""
130
131
def setSex(self, sex: int):
132
"""
133
Set patient sex.
134
135
Parameters:
136
- sex: int, patient sex (0=female, 1=male)
137
"""
138
139
def setBirthdate(self, birthdate: Union[str, date]):
140
"""
141
Set patient birthdate.
142
143
Parameters:
144
- birthdate: str or date, birthdate in YYYY-MM-DD format or date object
145
"""
146
```
147
148
### Recording Information
149
150
Set recording session metadata and technical details.
151
152
```python { .api }
153
def setTechnician(self, technician: str):
154
"""Set technician name."""
155
156
def setRecordingAdditional(self, recording_additional: str):
157
"""Set additional recording information."""
158
159
def setEquipment(self, equipment: str):
160
"""Set recording equipment information."""
161
162
def setAdmincode(self, admincode: str):
163
"""Set administration code."""
164
165
def setStartdatetime(self, recording_start_time: Union[datetime, str]):
166
"""
167
Set recording start date and time.
168
169
Parameters:
170
- recording_start_time: datetime or str, start time
171
"""
172
173
def setDatarecordDuration(self, record_duration: Union[float, int]):
174
"""
175
Set data record duration in seconds.
176
177
Parameters:
178
- record_duration: float or int, duration per data record
179
"""
180
181
def set_number_of_annotation_signals(self, number_of_annotations: int):
182
"""
183
Set number of annotation signals.
184
185
Parameters:
186
- number_of_annotations: int, annotation signal count
187
"""
188
```
189
190
### Signal Configuration
191
192
Configure individual signal channel properties including sampling rates, calibration, and metadata.
193
194
```python { .api }
195
def setSamplefrequency(self, edfsignal: int, samplefrequency: Union[int, float]):
196
"""
197
Set sampling frequency for signal.
198
199
Parameters:
200
- edfsignal: int, channel number
201
- samplefrequency: int or float, sample rate in Hz
202
"""
203
204
def setPhysicalMaximum(self, edfsignal: int, physical_maximum: Union[int, float]):
205
"""
206
Set physical maximum value for signal.
207
208
Parameters:
209
- edfsignal: int, channel number
210
- physical_maximum: int or float, maximum physical value
211
"""
212
213
def setPhysicalMinimum(self, edfsignal: int, physical_minimum: Union[int, float]):
214
"""
215
Set physical minimum value for signal.
216
217
Parameters:
218
- edfsignal: int, channel number
219
- physical_minimum: int or float, minimum physical value
220
"""
221
222
def setDigitalMaximum(self, edfsignal: int, digital_maximum: int):
223
"""
224
Set digital maximum value for signal.
225
226
Parameters:
227
- edfsignal: int, channel number
228
- digital_maximum: int, maximum digital value
229
"""
230
231
def setDigitalMinimum(self, edfsignal: int, digital_minimum: int):
232
"""
233
Set digital minimum value for signal.
234
235
Parameters:
236
- edfsignal: int, channel number
237
- digital_minimum: int, minimum digital value
238
"""
239
240
def setLabel(self, edfsignal: int, label: str):
241
"""
242
Set signal label.
243
244
Parameters:
245
- edfsignal: int, channel number
246
- label: str, signal label/name
247
"""
248
249
def setPhysicalDimension(self, edfsignal: int, physical_dimension: str):
250
"""
251
Set physical dimension (units) for signal.
252
253
Parameters:
254
- edfsignal: int, channel number
255
- physical_dimension: str, units (e.g., 'uV', 'mV')
256
"""
257
258
def setTransducer(self, edfsignal: int, transducer: str):
259
"""
260
Set transducer information for signal.
261
262
Parameters:
263
- edfsignal: int, channel number
264
- transducer: str, transducer description
265
"""
266
267
def setPrefilter(self, edfsignal: int, prefilter: str):
268
"""
269
Set prefilter information for signal.
270
271
Parameters:
272
- edfsignal: int, channel number
273
- prefilter: str, prefilter description
274
"""
275
```
276
277
### Data Writing
278
279
Write signal data to the file with support for different data types and writing modes.
280
281
```python { .api }
282
def writeSamples(self, data_list: Union[List[np.ndarray], np.ndarray], digital: bool = False):
283
"""
284
Write signal samples to file.
285
286
Parameters:
287
- data_list: List[numpy.ndarray] or numpy.ndarray, signal data for each channel
288
- digital: bool, whether data contains digital values (True) or physical values (False)
289
"""
290
291
def writePhysicalSamples(self, data: np.ndarray) -> int:
292
"""
293
Write physical samples for all channels.
294
295
Parameters:
296
- data: numpy.ndarray, physical sample values
297
298
Returns:
299
int: Number of samples written
300
"""
301
302
def writeDigitalSamples(self, data: np.ndarray) -> int:
303
"""
304
Write digital samples for all channels.
305
306
Parameters:
307
- data: numpy.ndarray, digital sample values
308
309
Returns:
310
int: Number of samples written
311
"""
312
313
def writeDigitalShortSamples(self, data: np.ndarray) -> int:
314
"""
315
Write digital short samples for all channels.
316
317
Parameters:
318
- data: numpy.ndarray, digital short sample values
319
320
Returns:
321
int: Number of samples written
322
"""
323
324
def blockWritePhysicalSamples(self, data: np.ndarray) -> int:
325
"""
326
Block write physical samples.
327
328
Parameters:
329
- data: numpy.ndarray, physical sample data
330
331
Returns:
332
int: Number of samples written
333
"""
334
335
def blockWriteDigitalSamples(self, data: np.ndarray) -> int:
336
"""
337
Block write digital samples.
338
339
Parameters:
340
- data: numpy.ndarray, digital sample data
341
342
Returns:
343
int: Number of samples written
344
"""
345
346
def blockWriteDigitalShortSamples(self, data: np.ndarray) -> int:
347
"""
348
Block write digital short samples.
349
350
Parameters:
351
- data: numpy.ndarray, digital short sample data
352
353
Returns:
354
int: Number of samples written
355
"""
356
```
357
358
Usage example:
359
360
```python
361
import numpy as np
362
363
# Generate sample data (1000 samples at 256 Hz for 2 channels)
364
data_ch1 = np.random.normal(0, 100, 1000) # EEG-like signal in uV
365
data_ch2 = np.random.normal(0, 100, 1000)
366
367
with pyedflib.EdfWriter('sample.edf', 2) as f:
368
f.setSignalHeaders(signal_headers)
369
370
# Write as list of arrays (recommended)
371
f.writeSamples([data_ch1, data_ch2])
372
373
# Or write as 2D array (channels x samples)
374
data_2d = np.array([data_ch1, data_ch2])
375
f.writeSamples(data_2d)
376
```
377
378
### Annotation Writing
379
380
Add annotations (events, markers) to EDF+ and BDF+ files with timing and description information.
381
382
```python { .api }
383
def writeAnnotation(self, onset_in_seconds: Union[int, float],
384
duration_in_seconds: Union[int, float],
385
description: str, str_format: str = 'utf8'):
386
"""
387
Write annotation to file.
388
389
Parameters:
390
- onset_in_seconds: int or float, annotation start time in seconds
391
- duration_in_seconds: int or float, annotation duration in seconds
392
- description: str, annotation text description
393
- str_format: str, text encoding ('utf8' or 'latin1')
394
"""
395
```
396
397
Usage example:
398
399
```python
400
with pyedflib.EdfWriter('annotated.edf', 2, pyedflib.FILETYPE_EDFPLUS) as f:
401
f.setSignalHeaders(signal_headers)
402
f.writeSamples([data_ch1, data_ch2])
403
404
# Add annotations
405
f.writeAnnotation(10.0, 0.0, "Stimulus onset")
406
f.writeAnnotation(15.5, 2.0, "Patient movement")
407
f.writeAnnotation(30.0, 0.0, "End of trial")
408
```
409
410
### Utility Methods
411
412
Helper methods for configuration and validation.
413
414
```python { .api }
415
def get_smp_per_record(self, ch_idx: int) -> int:
416
"""
417
Get samples per record for channel.
418
419
Parameters:
420
- ch_idx: int, channel index
421
422
Returns:
423
int: Samples per data record
424
"""
425
```
426
427
## Complete Writing Example
428
429
```python
430
import pyedflib
431
import numpy as np
432
from datetime import datetime
433
434
# Create sample EEG data
435
n_channels = 4
436
n_samples = 2560 # 10 seconds at 256 Hz
437
sample_rate = 256
438
439
# Generate realistic EEG-like signals
440
channels = ['EEG Fp1', 'EEG Fp2', 'EEG C3', 'EEG C4']
441
data = []
442
for i in range(n_channels):
443
# Mix of sine waves with noise to simulate EEG
444
t = np.linspace(0, 10, n_samples)
445
signal = (np.sin(2*np.pi*10*t) * 50 + # 10 Hz alpha-like
446
np.sin(2*np.pi*4*t) * 30 + # 4 Hz theta-like
447
np.random.normal(0, 20, n_samples)) # noise
448
data.append(signal)
449
450
# Create signal headers
451
signal_headers = []
452
for i, label in enumerate(channels):
453
signal_headers.append({
454
'label': label,
455
'dimension': 'uV',
456
'sample_frequency': sample_rate,
457
'physical_min': -500.0,
458
'physical_max': 500.0,
459
'digital_min': -32768,
460
'digital_max': 32767,
461
'transducer': 'AgAgCl electrodes',
462
'prefilter': 'HP:0.1Hz LP:70Hz'
463
})
464
465
# Create file header
466
file_header = {
467
'technician': 'Dr. Smith',
468
'recording_additional': 'EEG sleep study',
469
'patientname': 'Patient001',
470
'patient_additional': 'Age 25',
471
'patientcode': 'P001',
472
'equipment': 'EEG System v2.1',
473
'admincode': 'ADMIN001',
474
'sex': 'M',
475
'startdate': datetime.now(),
476
'birthdate': '1998-01-15'
477
}
478
479
# Write EDF file
480
with pyedflib.EdfWriter('complete_example.edf', n_channels, pyedflib.FILETYPE_EDFPLUS) as f:
481
# Set headers
482
f.setHeader(file_header)
483
f.setSignalHeaders(signal_headers)
484
485
# Write data
486
f.writeSamples(data)
487
488
# Add annotations
489
f.writeAnnotation(2.0, 0.0, "Eyes closed")
490
f.writeAnnotation(5.0, 1.0, "Sleep spindle")
491
f.writeAnnotation(8.0, 0.0, "Eyes open")
492
493
print("EDF file created successfully!")
494
```
495
496
## Constants
497
498
```python { .api }
499
# File type constants
500
FILETYPE_EDF: int # Standard EDF format
501
FILETYPE_EDFPLUS: int # EDF+ format with annotations
502
FILETYPE_BDF: int # BDF format (24-bit)
503
FILETYPE_BDFPLUS: int # BDF+ format with annotations
504
```