# Frequency domain plot no different after MNE low-pass and high-pass filtering

**URL:** https://mne.discourse.group/t/frequency-domain-plot-no-different-after-mne-low-pass-and-high-pass-filtering/5800
**Category:** Support & Discussions
**Created:** [October 24, 2022, 12:12pm UTC](https://mne.discourse.group/t/frequency-domain-plot-no-different-after-mne-low-pass-and-high-pass-filtering/5800 "2022-10-24T12:12:17Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![oskarhibbert](https://yyz2.discourse-cdn.com/free1/user_avatar/mne.discourse.group/oskarhibbert/32/1999_2.png) [@oskarhibbert](https://mne.discourse.group/u/oskarhibbert)
#### Post date: [October 24, 2022, 12:12pm UTC](https://mne.discourse.group/t/frequency-domain-plot-no-different-after-mne-low-pass-and-high-pass-filtering/5800/1 "2022-10-24T12:12:17Z")

</div>

Hi! After importing my EEG recording using:

```python
raw = mne.io.read_raw_edf(filepath, preload=True).filter(l_freq=1, h_freq=40)
# ... lots of processing or 'raw'
# EEG = 
              FP1-F7 F7-T7 ... FT9-FT10 FT10-T8
0 1.016440e-14 -3.388132e-15 ... -1.270549e-14 -2.752857e-15
1 -2.580525e+01 -1.245503e+01 ... 6.489457e+00 8.350538e+00
2 -4.289616e+01 -2.009946e+01 ... 1.073307e+01 1.373020e+01
3 -4.793277e+01 -2.150957e+01 ... 1.196083e+01 1.504048e+01
4 -4.432927e+01 -1.919635e+01 ... 1.115241e+01 1.352766e+01
             ... ... ... ... ...
921595 6.392553e+00 -2.020362e+01 ... 8.076414e+00 7.673939e+00
921596 5.983158e+00 -1.690656e+01 ... 2.825419e+00 7.266273e+00
921597 4.482021e+00 -1.176326e+01 ... -4.654217e-01 6.606688e+00
921598 2.356530e+00 -5.916955e+00 ... -1.160807e+00 4.144133e+00
921599 5.293956e-15 2.964615e-15 ... -2.329341e-15 -2.541099e-15

[921600 rows x 22 columns]

```

And applying SciPy’s Fourier transform functions to plot the frequency domain of my recording:

```python
N = len(EEG[['FP1-F7']]) #921600
f = 256 #256 Hz

xf = fftfreq(N, 1/f)
yf = fft(EEG[['FP1-F7']])

plt.plot(xf, np.abs(yf))
plt.xlabel("Frequency")
plt.ylabel("Amplitude")
plt.title("Frequency domain (Fourier transform) of single signal")
plt.show()

```

I still receive a frequency domain plot ranging from -128 to +128 Hz with considerable amplitudes across all frequencies, e.g. a large spike at 60 Hz still ([Imgur: The magic of the Internet](https://imgur.com/a/uG7zl7J)).

**Shouldn’t the high-pass, low-pass filtering filter out the frequencies outside the range specified (in my case: 1 to 40Hz) as per the filtering applied, more or less flattening the amplitudes outside of that range?** I.e., in my plot, should the amplitudes before 0 and after 40 Hz be almost flat?

MNE version and OS information:

- MNE version: 1.2.1
- operating system: macOS 12.6

---

<div class="post-metadata">

### Author: ![mscheltienne](https://yyz2.discourse-cdn.com/free1/user_avatar/mne.discourse.group/mscheltienne/32/827_2.png) [@mscheltienne](https://mne.discourse.group/u/mscheltienne)
#### Post date: [October 25, 2022, 10:01am UTC](https://mne.discourse.group/t/frequency-domain-plot-no-different-after-mne-low-pass-and-high-pass-filtering/5800/2 "2022-10-25T10:01:52Z")

</div>

Hello,

It looks weird, but it’s difficult to tell what has been going wrong from those code snippets.  
Can you do:

```python
raw = mne.io.read_raw_edf(filepath, preload=True)
raw.plot_psd()
raw.filter(1., 40.)
raw.plot_psd()

```

And share both plots?

Mathieu

---

<div class="post-metadata">

### Author: ![oskarhibbert](https://yyz2.discourse-cdn.com/free1/user_avatar/mne.discourse.group/oskarhibbert/32/1999_2.png) [@oskarhibbert](https://mne.discourse.group/u/oskarhibbert)
#### Post date: [October 25, 2022, 2:31pm UTC](https://mne.discourse.group/t/frequency-domain-plot-no-different-after-mne-low-pass-and-high-pass-filtering/5800/3 "2022-10-25T14:31:59Z")

</div>

Hi Mathieu! Thank you sincerely for your reply. You’re right! The filter is applied as can be seen in the following two graphs (without and with filtering resp.):

 ![without](https://global.discourse-cdn.com/free1/uploads/mne/original/2X/3/3c590d05c262a209acea0d9aa28e6da20acf8428.jpeg)  
 ![with](https://global.discourse-cdn.com/free1/uploads/mne/original/2X/8/8342f1f5c72131ed5295c47fa1310ab185cadee9.png)

Indeed after applying the filter I checked the EEG data-frame I am creating, and the values do change.

So I must be fundamentally mistaken how Fourier Transform works (in Python).

If I were to plot the frequency domain of one of the EEG channels after filtering, shouldn’t the plot reflect this change? I.e., shouldn’t the power in the red boxed be close to zero in the following plot?

 ![U4DXumn](https://global.discourse-cdn.com/free1/uploads/mne/original/2X/f/f098cbecabe95a2c73467c3a973ac03bfec3845c.png)

My code:

```python
raw = mne.io.read_raw_edf(filepath, verbose='Critical', preload=True)
raw.filter(1., 40.)
    
raw_ch_names = raw.info['ch_names']
raw_data = raw.get_data().T
eeg = pd.DataFrame(data=raw_data*10**6, columns=raw_ch_names)
eeg = eeg.loc[:, ~eeg.head(10).T.duplicated()] 

def fourier_transform(df):
    
    N = len(df)
    f = 256

    xf = fftfreq(N, 1/f)
    yf = fft(df)
    
    return xf, yf

xf, yf = fourier_transform(eeg[['FP1-F7']])

plt.plot(xf, np.abs(yf))
plt.xlabel("Frequency")
plt.ylabel("Amplitude")
plt.title("Frequency domain (Fourier transform) of single signal")
plt.show()

```

---

<div class="post-metadata">

### Author: ![mscheltienne](https://yyz2.discourse-cdn.com/free1/user_avatar/mne.discourse.group/mscheltienne/32/827_2.png) [@mscheltienne](https://mne.discourse.group/u/mscheltienne)
#### Post date: [October 25, 2022, 2:49pm UTC](https://mne.discourse.group/t/frequency-domain-plot-no-different-after-mne-low-pass-and-high-pass-filtering/5800/4 "2022-10-25T14:49:56Z")

</div>

From your code, I don’t see anything that looks wrong. But I am not super fluent with `pandas`, I might be missing something.

Here is a short example of the EEG channels of a sample MEG recording you can play with. I’ve used the FFT from `numpy` directly:

```python
import numpy as np
from matplotlib import pyplot as plt
from mne.io import read_raw_fif
from mne.datasets import sample

#%% load raw data and drop MEG channels
directory = sample.data_path() / "MEG" / "sample"
raw = read_raw_fif(directory / "sample_audvis_raw.fif", preload=True)
raw.pick_types(eeg=True, exclude="bads")

#%% high-pass to avoid the DC-offset
raw.filter(1., None)

#%% before filter
data = raw.get_data()
winsize = data.shape[-1]
frequencies = np.fft.rfftfreq(winsize, 1 / raw.info["sfreq"])
fftval = np.abs(np.fft.rfft(data, axis=1))
# average across channels
fftval = np.average(fftval, axis=0)
# plots
fig, ax = plt.subplots(2, 1)
raw.plot_psd(ax=ax[0])
ax[1].plot(frequencies, fftval)
ax[0].set_title("PSD")
ax[1].set_title("abs(FFT)")

#%% after filter
raw.filter(1., 40.)
data = raw.get_data()
winsize = data.shape[-1]
frequencies = np.fft.rfftfreq(winsize, 1 / raw.info["sfreq"])
fftval = np.abs(np.fft.rfft(data, axis=1))
# average across channels
fftval = np.average(fftval, axis=0)
# plots
fig, ax = plt.subplots(2, 1)
raw.plot_psd(ax=ax[0])
ax[1].plot(frequencies, fftval)
ax[0].set_title("PSD")
ax[1].set_title("abs(FFT)")

```

As you can see, the filter effect is very clear.

 ![Screenshot 2022-10-25 at 16.49.20](https://global.discourse-cdn.com/free1/uploads/mne/original/2X/4/4da4b80bd5a559a2c33c4b99bcaaba8c38743294.jpeg)

---

<div class="post-metadata">

### Author: ![oskarhibbert](https://yyz2.discourse-cdn.com/free1/user_avatar/mne.discourse.group/oskarhibbert/32/1999_2.png) [@oskarhibbert](https://mne.discourse.group/u/oskarhibbert)
#### Post date: [October 27, 2022, 7:55am UTC](https://mne.discourse.group/t/frequency-domain-plot-no-different-after-mne-low-pass-and-high-pass-filtering/5800/5 "2022-10-27T07:55:22Z")

</div>

You’re right! This worked for me! Thank you so much.

Side-note, do you have any idea what those two large spikes might be in my data? Some noise presumably? I hate to take more of your time so please ignore if you don’t feel like / have time for responding!

 ![Screenshot 2022-10-27 at 09.53.36](https://global.discourse-cdn.com/free1/uploads/mne/original/2X/2/251e822651dfee3fcbae707b5176534d35c29d20.jpeg)

---

<div class="post-metadata">

### Author: ![mscheltienne](https://yyz2.discourse-cdn.com/free1/user_avatar/mne.discourse.group/mscheltienne/32/827_2.png) [@mscheltienne](https://mne.discourse.group/u/mscheltienne)
#### Post date: [October 27, 2022, 8:16am UTC](https://mne.discourse.group/t/frequency-domain-plot-no-different-after-mne-low-pass-and-high-pass-filtering/5800/6 "2022-10-27T08:16:16Z")

</div>

No idea, but you should investigate. Try to look at `raw.plot()` and to find this activity in the temporal domain, try to figure out if it is a constant background noise or if it just happens at specific timings/intervals.

I’m going to guess an ICA should be able to isolate it well.

---

<div class="post-metadata">

### Author: ![drammock](https://yyz2.discourse-cdn.com/free1/user_avatar/mne.discourse.group/drammock/32/4_2.png) [@drammock](https://mne.discourse.group/u/drammock)
#### Post date: [October 27, 2022, 3:06pm UTC](https://mne.discourse.group/t/frequency-domain-plot-no-different-after-mne-low-pass-and-high-pass-filtering/5800/7 "2022-10-27T15:06:57Z")

</div>

> [@oskarhibbert](#):
>
> ```python
> raw_ch_names = raw.info['ch_names']
> raw_data = raw.get_data().T
> eeg = pd.DataFrame(data=raw_data*10**6, columns=raw_ch_names)
> eeg = eeg.loc[:, ~eeg.head(10).T.duplicated()] 
> 
> ```

_ASIDE:_ Just an FYI to make your life easier: there is a `raw.to_data_frame()` method that handles column names automatically, scales EEG from V to uV, lets you subselect specific channels or time ranges, etc. [mne.io.Raw — MNE 1.6.0 documentation](https://mne.tools/stable/generated/mne.io.Raw.html#mne.io.Raw.to_data_frame)
