0
# Format Conversion
1
2
Utilities for converting between WFDB and other common biomedical data formats including EDF, MATLAB, CSV, WAV, and TFF. This module enables interoperability with various data acquisition systems and analysis tools used in biomedical research.
3
4
## Capabilities
5
6
### EDF Conversion
7
8
Convert between WFDB and European Data Format (EDF/EDF+) files commonly used in sleep studies and clinical neurophysiology.
9
10
```python { .api }
11
def read_edf(file_name: str, pn_dir: str = None, rd_time: bool = True,
12
digital: bool = False, header_only: bool = False,
13
verbose: bool = False) -> Record:
14
"""
15
Read EDF/EDF+ files and convert to WFDB Record format.
16
17
Parameters:
18
- file_name: str, EDF file name or path
19
- pn_dir: str, directory containing the file
20
- rd_time: bool, read time information from EDF+
21
- digital: bool, return digital values instead of physical
22
- header_only: bool, read header information only
23
- verbose: bool, print detailed file information
24
25
Returns:
26
Record object containing the EDF data
27
"""
28
29
def wfdb_to_edf(record_name: str, pn_dir: str = None, sampfrom: int = 0,
30
sampto: Union[int, str] = 'end', channels: List[int] = None,
31
write_dir: str = '', edf_file: str = None,
32
file_type: str = 'EDF', digital: bool = False,
33
header_only: bool = False) -> None:
34
"""
35
Convert WFDB record to EDF format.
36
37
Parameters:
38
- record_name: str, WFDB record name
39
- pn_dir: str, directory containing WFDB files
40
- sampfrom: int, starting sample for conversion
41
- sampto: int or 'end', ending sample for conversion
42
- channels: list of int, specific channels to convert
43
- write_dir: str, output directory for EDF file
44
- edf_file: str, output EDF file name
45
- file_type: str, 'EDF' or 'EDF+' format
46
- digital: bool, write digital values instead of physical
47
- header_only: bool, write header only
48
"""
49
50
def rdedfann(edf_file: str, ann_file: str = None, ann_ext: str = 'atr',
51
pn_dir: str = None, encoding: str = 'latin-1') -> Annotation:
52
"""
53
Read EDF+ annotations and convert to WFDB Annotation format.
54
55
Parameters:
56
- edf_file: str, EDF+ file containing annotations
57
- ann_file: str, optional separate annotation file
58
- ann_ext: str, annotation file extension
59
- pn_dir: str, directory containing files
60
- encoding: str, text encoding for annotation strings
61
62
Returns:
63
Annotation object containing the EDF+ annotations
64
"""
65
```
66
67
### MATLAB Conversion
68
69
Convert WFDB records to MATLAB format for analysis in MATLAB/Octave environments.
70
71
```python { .api }
72
def wfdb_to_mat(record_name: str, pn_dir: str = None, sampfrom: int = 0,
73
sampto: Union[int, str] = 'end', channels: List[int] = None,
74
write_dir: str = '') -> None:
75
"""
76
Convert WFDB record to MATLAB (.mat) format.
77
78
Parameters:
79
- record_name: str, WFDB record name
80
- pn_dir: str, directory containing WFDB files
81
- sampfrom: int, starting sample for conversion
82
- sampto: int or 'end', ending sample for conversion
83
- channels: list of int, specific channels to convert
84
- write_dir: str, output directory for MAT file
85
86
The output .mat file contains:
87
- val: signal values matrix (samples x channels)
88
- tm: time vector
89
- Fs: sampling frequency
90
- units: cell array of signal units
91
- signame: cell array of signal names
92
"""
93
```
94
95
### CSV Conversion
96
97
Convert between WFDB format and comma-separated values (CSV) files for spreadsheet analysis.
98
99
```python { .api }
100
def csv_to_wfdb(file_name: str, fs: float, units: List[str], sig_name: List[str],
101
record_name: str = None, start_time: str = None,
102
use_checksum: bool = True, write_dir: str = '') -> None:
103
"""
104
Convert CSV file to WFDB format.
105
106
Parameters:
107
- file_name: str, input CSV file name
108
- fs: float, sampling frequency in Hz
109
- units: list of str, signal units for each column
110
- sig_name: list of str, signal names for each column
111
- record_name: str, output record name (default: CSV filename stem)
112
- start_time: str, record start time in HH:MM:SS format
113
- use_checksum: bool, calculate and store file checksums
114
- write_dir: str, output directory for WFDB files
115
116
CSV format requirements:
117
- First row: optional column headers
118
- Subsequent rows: numeric signal values (one sample per row)
119
"""
120
121
def csv2ann(file_name: str, fs: float, record_name: str) -> None:
122
"""
123
Convert CSV annotation file to WFDB annotation format.
124
125
Parameters:
126
- file_name: str, input CSV annotation file name
127
- fs: float, sampling frequency for time conversion
128
- record_name: str, associated record name for annotations
129
130
CSV annotation format:
131
- Column 1: time in seconds or sample numbers
132
- Column 2: annotation symbols
133
- Column 3: (optional) auxiliary notes
134
"""
135
```
136
137
### WAV Conversion
138
139
Convert between WFDB and WAV audio formats for acoustic physiological signals.
140
141
```python { .api }
142
def wfdb_to_wav(record_name: str, pn_dir: str = None, sampfrom: int = 0,
143
sampto: Union[int, str] = 'end', channels: List[int] = None,
144
write_dir: str = '', write_frequency: int = 8000) -> None:
145
"""
146
Convert WFDB record to WAV audio format.
147
148
Parameters:
149
- record_name: str, WFDB record name
150
- pn_dir: str, directory containing WFDB files
151
- sampfrom: int, starting sample for conversion
152
- sampto: int or 'end', ending sample for conversion
153
- channels: list of int, specific channels to convert
154
- write_dir: str, output directory for WAV files
155
- write_frequency: int, output sampling frequency for WAV
156
157
Note: Signals are automatically scaled and converted to appropriate
158
bit depth for audio playback.
159
"""
160
161
def read_wav(record_name: str, pn_dir: str = None, delete_file: bool = True,
162
record_only: bool = False) -> Union[Record, Tuple[Record, str]]:
163
"""
164
Read WAV file and convert to WFDB Record format.
165
166
Parameters:
167
- record_name: str, WAV file name (without .wav extension)
168
- pn_dir: str, directory containing WAV file
169
- delete_file: bool, delete temporary files after conversion
170
- record_only: bool, return only Record object
171
172
Returns:
173
Record object, or (Record, file_path) tuple if record_only=False
174
"""
175
```
176
177
### TFF Conversion
178
179
Read Text File Format (TFF) files used by some acquisition systems.
180
181
```python { .api }
182
def rdtff(file_name: str, cut_end: bool = False) -> Record:
183
"""
184
Read TFF (Text File Format) files and convert to WFDB Record.
185
186
Parameters:
187
- file_name: str, TFF file name or path
188
- cut_end: bool, remove trailing zero values
189
190
Returns:
191
Record object containing the TFF data
192
193
TFF format consists of:
194
- Header lines with metadata
195
- Data rows with tab-separated numeric values
196
"""
197
```
198
199
## Usage Examples
200
201
### EDF to WFDB Conversion
202
203
```python
204
import wfdb
205
import os
206
207
# Read EDF file
208
edf_record = wfdb.io.convert.read_edf('sleep_study.edf', verbose=True)
209
print(f"EDF record: {edf_record.sig_name}")
210
print(f"Duration: {edf_record.sig_len / edf_record.fs:.1f} seconds")
211
print(f"Channels: {edf_record.n_sig}")
212
213
# Write as WFDB format
214
edf_record.wrsamp('sleep_study_wfdb')
215
216
# Convert WFDB back to EDF
217
wfdb.io.convert.wfdb_to_edf('sleep_study_wfdb',
218
edf_file='converted_back.edf',
219
file_type='EDF+')
220
```
221
222
### MATLAB Integration
223
224
```python
225
import wfdb
226
import scipy.io
227
228
# Convert WFDB to MATLAB format
229
wfdb.io.convert.wfdb_to_mat('100', pn_dir='mitdb',
230
sampfrom=0, sampto=3600) # First 10 seconds
231
232
# Load in Python using scipy
233
mat_data = scipy.io.loadmat('100.mat')
234
print("MATLAB file contents:")
235
for key, value in mat_data.items():
236
if not key.startswith('__'):
237
print(f" {key}: {type(value)} {getattr(value, 'shape', '')}")
238
239
# Access signal data
240
signals = mat_data['val'] # Signal values
241
time_vec = mat_data['tm'].flatten() # Time vector
242
fs = mat_data['Fs'][0, 0] # Sampling frequency
243
sig_names = [name[0] for name in mat_data['signame'].flatten()]
244
```
245
246
### CSV Data Processing
247
248
```python
249
import wfdb
250
import pandas as pd
251
import numpy as np
252
253
# Create sample CSV data
254
data = {
255
'ECG_Lead_I': np.random.randn(1000),
256
'ECG_Lead_II': np.random.randn(1000),
257
'Respiration': np.random.randn(1000) * 0.1
258
}
259
df = pd.DataFrame(data)
260
df.to_csv('sample_signals.csv', index=False)
261
262
# Convert CSV to WFDB
263
wfdb.io.convert.csv_to_wfdb(
264
'sample_signals.csv',
265
fs=250, # 250 Hz sampling
266
units=['mV', 'mV', 'V'],
267
sig_name=['ECG I', 'ECG II', 'Resp'],
268
record_name='csv_converted'
269
)
270
271
# Read back as WFDB record
272
converted_record = wfdb.rdrecord('csv_converted')
273
print(f"Converted record: {converted_record.sig_name}")
274
275
# Create annotation CSV
276
ann_data = pd.DataFrame({
277
'time': [1.0, 2.5, 4.2, 6.8],
278
'symbol': ['N', 'V', 'N', 'N'],
279
'note': ['Normal', 'PVC', 'Normal', 'Normal']
280
})
281
ann_data.to_csv('annotations.csv', index=False)
282
283
# Convert annotation CSV to WFDB
284
wfdb.io.convert.csv2ann('annotations.csv', fs=250, record_name='csv_converted')
285
```
286
287
### WAV Audio Conversion
288
289
```python
290
import wfdb
291
import soundfile as sf
292
293
# Read physiological signal
294
record = wfdb.rdrecord('100', pn_dir='mitdb', channels=[0])
295
296
# Convert to WAV for audio analysis
297
wfdb.io.convert.wfdb_to_wav('100', pn_dir='mitdb',
298
channels=[0],
299
write_frequency=8000) # 8 kHz audio
300
301
# Read WAV file back
302
wav_record = wfdb.io.convert.read_wav('100')
303
print(f"WAV conversion: {wav_record.fs} Hz, {wav_record.sig_len} samples")
304
305
# Alternative: use soundfile for direct WAV handling
306
if os.path.exists('100.wav'):
307
audio_data, sample_rate = sf.read('100.wav')
308
print(f"Audio data shape: {audio_data.shape}")
309
print(f"Sample rate: {sample_rate} Hz")
310
```
311
312
### Batch Format Conversion
313
314
```python
315
import wfdb
316
import os
317
from pathlib import Path
318
319
def convert_directory_to_matlab(input_dir, output_dir):
320
"""Convert all WFDB records in directory to MATLAB format."""
321
322
# Find all .hea files (WFDB headers)
323
header_files = list(Path(input_dir).glob('*.hea'))
324
325
os.makedirs(output_dir, exist_ok=True)
326
327
for hea_file in header_files:
328
record_name = hea_file.stem
329
print(f"Converting {record_name}...")
330
331
try:
332
# Convert to MATLAB
333
wfdb.io.convert.wfdb_to_mat(
334
record_name,
335
pn_dir=input_dir,
336
write_dir=output_dir
337
)
338
print(f" ✓ {record_name}.mat created")
339
340
except Exception as e:
341
print(f" ✗ Error converting {record_name}: {e}")
342
343
# Example usage
344
# convert_directory_to_matlab('./wfdb_data', './matlab_data')
345
346
def batch_edf_to_wfdb(edf_directory, output_directory):
347
"""Convert multiple EDF files to WFDB format."""
348
349
edf_files = list(Path(edf_directory).glob('*.edf'))
350
os.makedirs(output_directory, exist_ok=True)
351
352
for edf_file in edf_files:
353
try:
354
# Read EDF
355
record = wfdb.io.convert.read_edf(str(edf_file))
356
357
# Write as WFDB
358
output_name = edf_file.stem
359
record.wrsamp(output_name, write_dir=output_directory)
360
361
print(f"Converted {edf_file.name} -> {output_name}")
362
363
except Exception as e:
364
print(f"Error converting {edf_file.name}: {e}")
365
366
# Example usage
367
# batch_edf_to_wfdb('./edf_files', './wfdb_output')
368
```
369
370
### Format Validation and Compatibility
371
372
```python
373
import wfdb
374
import numpy as np
375
376
def validate_conversion_accuracy(original_record_name, pn_dir=None):
377
"""Test conversion accuracy by round-trip conversion."""
378
379
# Read original WFDB record
380
original = wfdb.rdrecord(original_record_name, pn_dir=pn_dir)
381
382
# Convert to MATLAB and back
383
wfdb.io.convert.wfdb_to_mat(original_record_name, pn_dir=pn_dir)
384
385
# Note: Direct MAT to WFDB conversion not available
386
# This demonstrates concept - actual implementation would require
387
# manual MAT file reading and WFDB writing
388
389
# Convert to CSV and back
390
# First, save as CSV manually for this test
391
import pandas as pd
392
df = pd.DataFrame(original.p_signal, columns=original.sig_name)
393
df.to_csv('temp_conversion.csv', index=False)
394
395
# Convert CSV back to WFDB
396
wfdb.io.convert.csv_to_wfdb(
397
'temp_conversion.csv',
398
fs=original.fs,
399
units=original.units,
400
sig_name=original.sig_name,
401
record_name='temp_converted'
402
)
403
404
# Read converted record
405
converted = wfdb.rdrecord('temp_converted')
406
407
# Compare signals
408
signal_diff = np.abs(original.p_signal - converted.p_signal)
409
max_error = np.max(signal_diff)
410
mean_error = np.mean(signal_diff)
411
412
print(f"Conversion validation for {original_record_name}:")
413
print(f" Max error: {max_error:.6f}")
414
print(f" Mean error: {mean_error:.6f}")
415
print(f" Original shape: {original.p_signal.shape}")
416
print(f" Converted shape: {converted.p_signal.shape}")
417
418
# Cleanup
419
os.remove('temp_conversion.csv')
420
for ext in ['.hea', '.dat']:
421
if os.path.exists(f'temp_converted{ext}'):
422
os.remove(f'temp_converted{ext}')
423
424
return max_error < 1e-10 # Tolerance for floating point precision
425
426
# Example usage
427
# success = validate_conversion_accuracy('100', pn_dir='mitdb')
428
```
429
430
### Cross-Platform Data Exchange
431
432
```python
433
import wfdb
434
import json
435
436
def create_conversion_metadata(record_name, pn_dir=None):
437
"""Create metadata file for converted records."""
438
439
# Read original record
440
record = wfdb.rdrecord(record_name, pn_dir=pn_dir)
441
442
# Create metadata dictionary
443
metadata = {
444
'original_format': 'WFDB',
445
'record_name': record.record_name,
446
'sampling_frequency': record.fs,
447
'signal_length': record.sig_len,
448
'n_signals': record.n_sig,
449
'signal_names': record.sig_name,
450
'units': record.units,
451
'comments': record.comments,
452
'conversion_timestamp': pd.Timestamp.now().isoformat(),
453
'available_formats': []
454
}
455
456
# Convert to multiple formats
457
formats_to_convert = ['matlab', 'csv', 'wav']
458
459
for fmt in formats_to_convert:
460
try:
461
if fmt == 'matlab':
462
wfdb.io.convert.wfdb_to_mat(record_name, pn_dir=pn_dir)
463
metadata['available_formats'].append('matlab')
464
465
elif fmt == 'csv':
466
# Save signals as CSV
467
df = pd.DataFrame(record.p_signal, columns=record.sig_name)
468
df.to_csv(f'{record_name}_signals.csv', index=False)
469
metadata['available_formats'].append('csv')
470
471
elif fmt == 'wav':
472
wfdb.io.convert.wfdb_to_wav(record_name, pn_dir=pn_dir)
473
metadata['available_formats'].append('wav')
474
475
except Exception as e:
476
print(f"Could not convert to {fmt}: {e}")
477
478
# Save metadata
479
with open(f'{record_name}_metadata.json', 'w') as f:
480
json.dump(metadata, f, indent=2)
481
482
return metadata
483
484
# Example usage
485
# metadata = create_conversion_metadata('100', pn_dir='mitdb')
486
# print(f"Created conversions: {metadata['available_formats']}")
487
```