Skip to content

prepro

Butterworth filters for EMG preprocessing.

All filters work along axis=0, so data has shape (samples, chs) or (samples,).

bandpass_filter

bandpass_filter(data: ndarray, fs: Optional[int] = 2048, cutoff: Optional[list] = None, order: Optional[int] = 4, filtfilt: Optional[bool] = True) -> np.ndarray

Apply a Butterworth bandpass filter.

Parameters:

Name Type Description Default
data ndarray

Input data with shape (samples, chs).

required
fs int

Sampling frequency in Hz. Default is 2048.

2048
cutoff list

Low and high cutoff frequencies in Hz. Default is None, which uses [20, 500].

None
order int

Filter order. Default is 4.

4
filtfilt bool

If True, filter forward and backward (zero phase, doubled effective order). If False, filter forward only. Default is True.

True

Returns:

Type Description
ndarray

np.ndarray: Filtered data, same shape as the input.

Source code in emg_tools/prepro.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def bandpass_filter(
    data: np.ndarray,
    fs: Optional[int] = 2048,
    cutoff: Optional[list] = None,
    order: Optional[int] = 4,
    filtfilt: Optional[bool] = True,
    ) -> np.ndarray:

    """Apply a Butterworth bandpass filter.

    Args:
        data (np.ndarray): Input data with shape (samples, chs).
        fs (int, optional): Sampling frequency in Hz. Default is 2048.
        cutoff (list, optional): Low and high cutoff frequencies in Hz.
            Default is None, which uses [20, 500].
        order (int, optional): Filter order. Default is 4.
        filtfilt (bool, optional): If True, filter forward and backward (zero phase,
            doubled effective order). If False, filter forward only. Default is True.

    Returns:
        np.ndarray: Filtered data, same shape as the input.
    """
    # Define cutoff frequencies
    if cutoff is None:
        cutoff = [20, 500]

    # Define filter
    sos = signal.butter(order, cutoff, btype='band', fs=fs, output='sos')

    # Apply filter
    if filtfilt:
        out = signal.sosfiltfilt(sos, data, axis=0)
    else:
        out = signal.sosfilt(sos, data, axis=0)

    return out

highpass_filter

highpass_filter(data: ndarray, fs: Optional[int] = 2048, cutoff: Optional[float] = 20, order: Optional[int] = 2, filtfilt: Optional[bool] = True) -> np.ndarray

Apply a Butterworth highpass filter.

Parameters:

Name Type Description Default
data ndarray

Input data with shape (samples, chs).

required
fs int

Sampling frequency in Hz. Default is 2048.

2048
cutoff float

Cutoff frequency in Hz. Default is 20.

20
order int

Filter order. Default is 2.

2
filtfilt bool

If True, filter forward and backward (zero phase, doubled effective order). If False, filter forward only. Default is True.

True

Returns:

Type Description
ndarray

np.ndarray: Filtered data, same shape as the input.

Source code in emg_tools/prepro.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def highpass_filter(
    data: np.ndarray,
    fs: Optional[int] = 2048,
    cutoff: Optional[float] = 20,
    order: Optional[int] = 2,
    filtfilt: Optional[bool] = True,
    ) -> np.ndarray:

    """Apply a Butterworth highpass filter.

    Args:
        data (np.ndarray): Input data with shape (samples, chs).
        fs (int, optional): Sampling frequency in Hz. Default is 2048.
        cutoff (float, optional): Cutoff frequency in Hz. Default is 20.
        order (int, optional): Filter order. Default is 2.
        filtfilt (bool, optional): If True, filter forward and backward (zero phase,
            doubled effective order). If False, filter forward only. Default is True.

    Returns:
        np.ndarray: Filtered data, same shape as the input.
    """
    # Define filter
    sos = signal.butter(order, cutoff, btype='high', fs=fs, output='sos')

    # Apply filter
    if filtfilt:
        out = signal.sosfiltfilt(sos, data, axis=0)
    else:
        out = signal.sosfilt(sos, data, axis=0)

    return out

lowpass_filter

lowpass_filter(data: ndarray, fs: Optional[int] = 2048, cutoff: Optional[float] = 500, order: Optional[int] = 2, filtfilt: Optional[bool] = True) -> np.ndarray

Apply a Butterworth lowpass filter.

Parameters:

Name Type Description Default
data ndarray

Input data with shape (samples, chs).

required
fs int

Sampling frequency in Hz. Default is 2048.

2048
cutoff float

Cutoff frequency in Hz. Default is 500.

500
order int

Filter order. Default is 2.

2
filtfilt bool

If True, filter forward and backward (zero phase, doubled effective order). If False, filter forward only. Default is True.

True

Returns:

Type Description
ndarray

np.ndarray: Filtered data, same shape as the input.

Source code in emg_tools/prepro.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
def lowpass_filter(
    data: np.ndarray,
    fs: Optional[int] = 2048,
    cutoff: Optional[float] = 500,
    order: Optional[int] = 2,
    filtfilt: Optional[bool] = True,
    ) -> np.ndarray:

    """Apply a Butterworth lowpass filter.

    Args:
        data (np.ndarray): Input data with shape (samples, chs).
        fs (int, optional): Sampling frequency in Hz. Default is 2048.
        cutoff (float, optional): Cutoff frequency in Hz. Default is 500.
        order (int, optional): Filter order. Default is 2.
        filtfilt (bool, optional): If True, filter forward and backward (zero phase,
            doubled effective order). If False, filter forward only. Default is True.

    Returns:
        np.ndarray: Filtered data, same shape as the input.
    """
    # Define filter
    sos = signal.butter(order, cutoff, btype='low', fs=fs, output='sos')

    # Apply filter
    if filtfilt:
        out = signal.sosfiltfilt(sos, data, axis=0)
    else:
        out = signal.sosfilt(sos, data, axis=0)

    return out

remove_powerline

remove_powerline(data: ndarray, fs: Optional[int] = 2048, cutoff: Optional[float] = 50, width: Optional[float] = 1, order: Optional[int] = 2, filtfilt: Optional[bool] = True) -> np.ndarray

Remove powerline noise with a Butterworth bandstop filter.

Parameters:

Name Type Description Default
data ndarray

Input data with shape (samples, chs).

required
fs int

Sampling frequency in Hz. Default is 2048.

2048
cutoff float

Powerline frequency in Hz. Default is 50.

50
width float

Width of the stop band in Hz, centred on cutoff. Default is 1.

1
order int

Filter order. Default is 2.

2
filtfilt bool

If True, filter forward and backward (zero phase, doubled effective order). If False, filter forward only. Default is True.

True

Returns:

Type Description
ndarray

np.ndarray: Filtered data, same shape as the input.

Source code in emg_tools/prepro.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
def remove_powerline(
    data: np.ndarray,
    fs: Optional[int] = 2048,
    cutoff: Optional[float] = 50,
    width: Optional[float] = 1,
    order: Optional[int] = 2,
    filtfilt: Optional[bool] = True,
    ) -> np.ndarray:

    """Remove powerline noise with a Butterworth bandstop filter.

    Args:
        data (np.ndarray): Input data with shape (samples, chs).
        fs (int, optional): Sampling frequency in Hz. Default is 2048.
        cutoff (float, optional): Powerline frequency in Hz. Default is 50.
        width (float, optional): Width of the stop band in Hz, centred on cutoff.
            Default is 1.
        order (int, optional): Filter order. Default is 2.
        filtfilt (bool, optional): If True, filter forward and backward (zero phase,
            doubled effective order). If False, filter forward only. Default is True.

    Returns:
        np.ndarray: Filtered data, same shape as the input.
    """

    # Build cutoff
    cutoff = [cutoff - width/2, cutoff + width/2]

    # Define filter
    sos = signal.butter(order, cutoff, btype='bandstop', fs=fs, output='sos')

    # Apply filter
    if filtfilt:
        out = signal.sosfiltfilt(sos, data, axis=0)
    else:
        out = signal.sosfilt(sos, data, axis=0)

    return out