CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-wfdb

Python package for reading, writing, and processing physiologic signals and annotations in WFDB format.

Overview
Eval results
Files

format-conversion.mddocs/

Format Conversion

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.

Capabilities

EDF Conversion

Convert between WFDB and European Data Format (EDF/EDF+) files commonly used in sleep studies and clinical neurophysiology.

def read_edf(file_name: str, pn_dir: str = None, rd_time: bool = True,
             digital: bool = False, header_only: bool = False, 
             verbose: bool = False) -> Record:
    """
    Read EDF/EDF+ files and convert to WFDB Record format.
    
    Parameters:
    - file_name: str, EDF file name or path
    - pn_dir: str, directory containing the file
    - rd_time: bool, read time information from EDF+ 
    - digital: bool, return digital values instead of physical
    - header_only: bool, read header information only
    - verbose: bool, print detailed file information
    
    Returns:
    Record object containing the EDF data
    """

def wfdb_to_edf(record_name: str, pn_dir: str = None, sampfrom: int = 0,
                sampto: Union[int, str] = 'end', channels: List[int] = None,
                write_dir: str = '', edf_file: str = None, 
                file_type: str = 'EDF', digital: bool = False,
                header_only: bool = False) -> None:
    """
    Convert WFDB record to EDF format.
    
    Parameters:
    - record_name: str, WFDB record name
    - pn_dir: str, directory containing WFDB files
    - sampfrom: int, starting sample for conversion
    - sampto: int or 'end', ending sample for conversion
    - channels: list of int, specific channels to convert
    - write_dir: str, output directory for EDF file
    - edf_file: str, output EDF file name
    - file_type: str, 'EDF' or 'EDF+' format
    - digital: bool, write digital values instead of physical
    - header_only: bool, write header only
    """

def rdedfann(edf_file: str, ann_file: str = None, ann_ext: str = 'atr',
             pn_dir: str = None, encoding: str = 'latin-1') -> Annotation:
    """
    Read EDF+ annotations and convert to WFDB Annotation format.
    
    Parameters:
    - edf_file: str, EDF+ file containing annotations
    - ann_file: str, optional separate annotation file
    - ann_ext: str, annotation file extension
    - pn_dir: str, directory containing files
    - encoding: str, text encoding for annotation strings
    
    Returns:
    Annotation object containing the EDF+ annotations
    """

MATLAB Conversion

Convert WFDB records to MATLAB format for analysis in MATLAB/Octave environments.

def wfdb_to_mat(record_name: str, pn_dir: str = None, sampfrom: int = 0,
                sampto: Union[int, str] = 'end', channels: List[int] = None,
                write_dir: str = '') -> None:
    """
    Convert WFDB record to MATLAB (.mat) format.
    
    Parameters:
    - record_name: str, WFDB record name
    - pn_dir: str, directory containing WFDB files
    - sampfrom: int, starting sample for conversion
    - sampto: int or 'end', ending sample for conversion
    - channels: list of int, specific channels to convert
    - write_dir: str, output directory for MAT file
    
    The output .mat file contains:
    - val: signal values matrix (samples x channels)
    - tm: time vector
    - Fs: sampling frequency
    - units: cell array of signal units
    - signame: cell array of signal names
    """

CSV Conversion

Convert between WFDB format and comma-separated values (CSV) files for spreadsheet analysis.

def csv_to_wfdb(file_name: str, fs: float, units: List[str], sig_name: List[str],
                record_name: str = None, start_time: str = None,
                use_checksum: bool = True, write_dir: str = '') -> None:
    """
    Convert CSV file to WFDB format.
    
    Parameters:
    - file_name: str, input CSV file name
    - fs: float, sampling frequency in Hz
    - units: list of str, signal units for each column
    - sig_name: list of str, signal names for each column
    - record_name: str, output record name (default: CSV filename stem)
    - start_time: str, record start time in HH:MM:SS format
    - use_checksum: bool, calculate and store file checksums
    - write_dir: str, output directory for WFDB files
    
    CSV format requirements:
    - First row: optional column headers
    - Subsequent rows: numeric signal values (one sample per row)
    """

def csv2ann(file_name: str, fs: float, record_name: str) -> None:
    """
    Convert CSV annotation file to WFDB annotation format.
    
    Parameters:
    - file_name: str, input CSV annotation file name
    - fs: float, sampling frequency for time conversion
    - record_name: str, associated record name for annotations
    
    CSV annotation format:
    - Column 1: time in seconds or sample numbers
    - Column 2: annotation symbols
    - Column 3: (optional) auxiliary notes
    """

WAV Conversion

Convert between WFDB and WAV audio formats for acoustic physiological signals.

def wfdb_to_wav(record_name: str, pn_dir: str = None, sampfrom: int = 0,
                sampto: Union[int, str] = 'end', channels: List[int] = None,
                write_dir: str = '', write_frequency: int = 8000) -> None:
    """
    Convert WFDB record to WAV audio format.
    
    Parameters:
    - record_name: str, WFDB record name
    - pn_dir: str, directory containing WFDB files
    - sampfrom: int, starting sample for conversion
    - sampto: int or 'end', ending sample for conversion
    - channels: list of int, specific channels to convert
    - write_dir: str, output directory for WAV files
    - write_frequency: int, output sampling frequency for WAV
    
    Note: Signals are automatically scaled and converted to appropriate
    bit depth for audio playback.
    """

def read_wav(record_name: str, pn_dir: str = None, delete_file: bool = True,
             record_only: bool = False) -> Union[Record, Tuple[Record, str]]:
    """
    Read WAV file and convert to WFDB Record format.
    
    Parameters:
    - record_name: str, WAV file name (without .wav extension)
    - pn_dir: str, directory containing WAV file
    - delete_file: bool, delete temporary files after conversion
    - record_only: bool, return only Record object
    
    Returns:
    Record object, or (Record, file_path) tuple if record_only=False
    """

TFF Conversion

Read Text File Format (TFF) files used by some acquisition systems.

def rdtff(file_name: str, cut_end: bool = False) -> Record:
    """
    Read TFF (Text File Format) files and convert to WFDB Record.
    
    Parameters:
    - file_name: str, TFF file name or path
    - cut_end: bool, remove trailing zero values
    
    Returns:
    Record object containing the TFF data
    
    TFF format consists of:
    - Header lines with metadata
    - Data rows with tab-separated numeric values
    """

Usage Examples

EDF to WFDB Conversion

import wfdb
import os

# Read EDF file
edf_record = wfdb.io.convert.read_edf('sleep_study.edf', verbose=True)
print(f"EDF record: {edf_record.sig_name}")
print(f"Duration: {edf_record.sig_len / edf_record.fs:.1f} seconds")
print(f"Channels: {edf_record.n_sig}")

# Write as WFDB format
edf_record.wrsamp('sleep_study_wfdb')

# Convert WFDB back to EDF
wfdb.io.convert.wfdb_to_edf('sleep_study_wfdb', 
                           edf_file='converted_back.edf',
                           file_type='EDF+')

MATLAB Integration

import wfdb
import scipy.io

# Convert WFDB to MATLAB format
wfdb.io.convert.wfdb_to_mat('100', pn_dir='mitdb', 
                           sampfrom=0, sampto=3600)  # First 10 seconds

# Load in Python using scipy
mat_data = scipy.io.loadmat('100.mat')
print("MATLAB file contents:")
for key, value in mat_data.items():
    if not key.startswith('__'):
        print(f"  {key}: {type(value)} {getattr(value, 'shape', '')}")

# Access signal data
signals = mat_data['val']        # Signal values
time_vec = mat_data['tm'].flatten()  # Time vector
fs = mat_data['Fs'][0, 0]        # Sampling frequency
sig_names = [name[0] for name in mat_data['signame'].flatten()]

CSV Data Processing

import wfdb
import pandas as pd
import numpy as np

# Create sample CSV data
data = {
    'ECG_Lead_I': np.random.randn(1000),
    'ECG_Lead_II': np.random.randn(1000),
    'Respiration': np.random.randn(1000) * 0.1
}
df = pd.DataFrame(data)
df.to_csv('sample_signals.csv', index=False)

# Convert CSV to WFDB
wfdb.io.convert.csv_to_wfdb(
    'sample_signals.csv',
    fs=250,  # 250 Hz sampling
    units=['mV', 'mV', 'V'],
    sig_name=['ECG I', 'ECG II', 'Resp'],
    record_name='csv_converted'
)

# Read back as WFDB record
converted_record = wfdb.rdrecord('csv_converted')
print(f"Converted record: {converted_record.sig_name}")

# Create annotation CSV
ann_data = pd.DataFrame({
    'time': [1.0, 2.5, 4.2, 6.8],
    'symbol': ['N', 'V', 'N', 'N'],
    'note': ['Normal', 'PVC', 'Normal', 'Normal']
})
ann_data.to_csv('annotations.csv', index=False)

# Convert annotation CSV to WFDB
wfdb.io.convert.csv2ann('annotations.csv', fs=250, record_name='csv_converted')

WAV Audio Conversion

import wfdb
import soundfile as sf

# Read physiological signal
record = wfdb.rdrecord('100', pn_dir='mitdb', channels=[0])

# Convert to WAV for audio analysis
wfdb.io.convert.wfdb_to_wav('100', pn_dir='mitdb', 
                           channels=[0], 
                           write_frequency=8000)  # 8 kHz audio

# Read WAV file back
wav_record = wfdb.io.convert.read_wav('100')
print(f"WAV conversion: {wav_record.fs} Hz, {wav_record.sig_len} samples")

# Alternative: use soundfile for direct WAV handling
if os.path.exists('100.wav'):
    audio_data, sample_rate = sf.read('100.wav')
    print(f"Audio data shape: {audio_data.shape}")
    print(f"Sample rate: {sample_rate} Hz")

Batch Format Conversion

import wfdb
import os
from pathlib import Path

def convert_directory_to_matlab(input_dir, output_dir):
    """Convert all WFDB records in directory to MATLAB format."""
    
    # Find all .hea files (WFDB headers)
    header_files = list(Path(input_dir).glob('*.hea'))
    
    os.makedirs(output_dir, exist_ok=True)
    
    for hea_file in header_files:
        record_name = hea_file.stem
        print(f"Converting {record_name}...")
        
        try:
            # Convert to MATLAB
            wfdb.io.convert.wfdb_to_mat(
                record_name, 
                pn_dir=input_dir,
                write_dir=output_dir
            )
            print(f"  ✓ {record_name}.mat created")
            
        except Exception as e:
            print(f"  ✗ Error converting {record_name}: {e}")

# Example usage
# convert_directory_to_matlab('./wfdb_data', './matlab_data')

def batch_edf_to_wfdb(edf_directory, output_directory):
    """Convert multiple EDF files to WFDB format."""
    
    edf_files = list(Path(edf_directory).glob('*.edf'))
    os.makedirs(output_directory, exist_ok=True)
    
    for edf_file in edf_files:
        try:
            # Read EDF
            record = wfdb.io.convert.read_edf(str(edf_file))
            
            # Write as WFDB
            output_name = edf_file.stem
            record.wrsamp(output_name, write_dir=output_directory)
            
            print(f"Converted {edf_file.name} -> {output_name}")
            
        except Exception as e:
            print(f"Error converting {edf_file.name}: {e}")

# Example usage  
# batch_edf_to_wfdb('./edf_files', './wfdb_output')

Format Validation and Compatibility

import wfdb
import numpy as np

def validate_conversion_accuracy(original_record_name, pn_dir=None):
    """Test conversion accuracy by round-trip conversion."""
    
    # Read original WFDB record
    original = wfdb.rdrecord(original_record_name, pn_dir=pn_dir)
    
    # Convert to MATLAB and back
    wfdb.io.convert.wfdb_to_mat(original_record_name, pn_dir=pn_dir)
    
    # Note: Direct MAT to WFDB conversion not available
    # This demonstrates concept - actual implementation would require
    # manual MAT file reading and WFDB writing
    
    # Convert to CSV and back
    # First, save as CSV manually for this test
    import pandas as pd
    df = pd.DataFrame(original.p_signal, columns=original.sig_name)
    df.to_csv('temp_conversion.csv', index=False)
    
    # Convert CSV back to WFDB
    wfdb.io.convert.csv_to_wfdb(
        'temp_conversion.csv',
        fs=original.fs,
        units=original.units,
        sig_name=original.sig_name,
        record_name='temp_converted'
    )
    
    # Read converted record
    converted = wfdb.rdrecord('temp_converted')
    
    # Compare signals
    signal_diff = np.abs(original.p_signal - converted.p_signal)
    max_error = np.max(signal_diff)
    mean_error = np.mean(signal_diff)
    
    print(f"Conversion validation for {original_record_name}:")
    print(f"  Max error: {max_error:.6f}")
    print(f"  Mean error: {mean_error:.6f}")
    print(f"  Original shape: {original.p_signal.shape}")
    print(f"  Converted shape: {converted.p_signal.shape}")
    
    # Cleanup
    os.remove('temp_conversion.csv')
    for ext in ['.hea', '.dat']:
        if os.path.exists(f'temp_converted{ext}'):
            os.remove(f'temp_converted{ext}')
    
    return max_error < 1e-10  # Tolerance for floating point precision

# Example usage
# success = validate_conversion_accuracy('100', pn_dir='mitdb')

Cross-Platform Data Exchange

import wfdb
import json

def create_conversion_metadata(record_name, pn_dir=None):
    """Create metadata file for converted records."""
    
    # Read original record
    record = wfdb.rdrecord(record_name, pn_dir=pn_dir)
    
    # Create metadata dictionary
    metadata = {
        'original_format': 'WFDB',
        'record_name': record.record_name,
        'sampling_frequency': record.fs,
        'signal_length': record.sig_len,
        'n_signals': record.n_sig,
        'signal_names': record.sig_name,
        'units': record.units,
        'comments': record.comments,
        'conversion_timestamp': pd.Timestamp.now().isoformat(),
        'available_formats': []
    }
    
    # Convert to multiple formats
    formats_to_convert = ['matlab', 'csv', 'wav']
    
    for fmt in formats_to_convert:
        try:
            if fmt == 'matlab':
                wfdb.io.convert.wfdb_to_mat(record_name, pn_dir=pn_dir)
                metadata['available_formats'].append('matlab')
                
            elif fmt == 'csv':
                # Save signals as CSV
                df = pd.DataFrame(record.p_signal, columns=record.sig_name)
                df.to_csv(f'{record_name}_signals.csv', index=False)
                metadata['available_formats'].append('csv')
                
            elif fmt == 'wav':
                wfdb.io.convert.wfdb_to_wav(record_name, pn_dir=pn_dir)
                metadata['available_formats'].append('wav')
                
        except Exception as e:
            print(f"Could not convert to {fmt}: {e}")
    
    # Save metadata
    with open(f'{record_name}_metadata.json', 'w') as f:
        json.dump(metadata, f, indent=2)
    
    return metadata

# Example usage
# metadata = create_conversion_metadata('100', pn_dir='mitdb')
# print(f"Created conversions: {metadata['available_formats']}")

Install with Tessl CLI

npx tessl i tessl/pypi-wfdb

docs

format-conversion.md

index.md

io-operations.md

plotting.md

signal-processing.md

tile.json