185 lines
6.5 KiB
Python
185 lines
6.5 KiB
Python
|
|
# -*- coding: utf-8 -*-
|
||
|
|
"""
|
||
|
|
EEG Data Quality Check - eeg_data0.mat
|
||
|
|
===================================
|
||
|
|
1. Time Domain Signal (Full Duration)
|
||
|
|
2. Amplitude Spectrum (FFT)
|
||
|
|
3. Power Spectral Density (Linear Scale)
|
||
|
|
4. Power Spectral Density (dB Scale)
|
||
|
|
"""
|
||
|
|
|
||
|
|
import numpy as np
|
||
|
|
import matplotlib.pyplot as plt
|
||
|
|
import mne
|
||
|
|
from scipy import signal
|
||
|
|
from scipy.io import loadmat
|
||
|
|
|
||
|
|
|
||
|
|
def load_and_preprocess(filepath):
|
||
|
|
"""Load .mat file (custom format) and basic preprocessing."""
|
||
|
|
mat_data = loadmat(filepath, simplify_cells=True)
|
||
|
|
eeg = mat_data['eeg']
|
||
|
|
|
||
|
|
# Extract data (shape: samples x channels)
|
||
|
|
data = eeg['data'].T # Transpose to (channels x samples)
|
||
|
|
sfreq = eeg['sample_rate']
|
||
|
|
|
||
|
|
# Get channel names (try multiple possible keys)
|
||
|
|
if 'chn' in eeg:
|
||
|
|
ch_names = list(eeg['chn'])
|
||
|
|
elif 'electrode_name' in eeg:
|
||
|
|
ch_names = list(eeg['electrode_name'])
|
||
|
|
else:
|
||
|
|
n_channels = data.shape[0]
|
||
|
|
ch_names = [f'Ch{i+1}' for i in range(n_channels)]
|
||
|
|
|
||
|
|
# Create MNE Info object
|
||
|
|
info = mne.create_info(ch_names=ch_names, sfreq=sfreq, ch_types='eeg')
|
||
|
|
raw = mne.io.RawArray(data, info)
|
||
|
|
|
||
|
|
raw.filter(l_freq=0.5, h_freq=10, fir_design='firwin', verbose=False)
|
||
|
|
return raw
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
filepath = r"D:\Ivey\Code_New_Proj\brainplot\plot64\eeg_data0511.mat"
|
||
|
|
output_path = r"D:\Ivey\Code_New_Proj\brainplot\plot64\eeg_quality_check_depression.png"
|
||
|
|
raw = load_and_preprocess(filepath)
|
||
|
|
|
||
|
|
# Print all channel names first
|
||
|
|
print(f"\nAvailable channels ({len(raw.ch_names)}):")
|
||
|
|
for i, ch in enumerate(raw.ch_names):
|
||
|
|
print(f" {i:3d}: {ch}")
|
||
|
|
|
||
|
|
select_channel = ['AIN5']
|
||
|
|
raw.pick(select_channel)
|
||
|
|
|
||
|
|
# Use all channels, full duration
|
||
|
|
ch_names = raw.ch_names
|
||
|
|
n_channels = len(ch_names)
|
||
|
|
data = raw.get_data()
|
||
|
|
sfreq = raw.info['sfreq']
|
||
|
|
n_samples = data.shape[1]
|
||
|
|
duration = n_samples / sfreq
|
||
|
|
|
||
|
|
print(f"Info: {n_channels} channels, {duration:.1f}s, {sfreq:.0f} Hz")
|
||
|
|
|
||
|
|
# Compute frequency domain data
|
||
|
|
n_fft = 2**int(np.ceil(np.log2(n_samples)))
|
||
|
|
freqs_fft = np.fft.rfftfreq(n_fft, 1 / sfreq)
|
||
|
|
fft_vals = np.fft.rfft(data, n=n_fft)
|
||
|
|
amplitude = np.abs(fft_vals) / n_fft * 2
|
||
|
|
|
||
|
|
freqs_psd, psd = signal.welch(data, fs=sfreq, nperseg=4096,
|
||
|
|
noverlap=2048, scaling='density')
|
||
|
|
|
||
|
|
# Frequency mask: 0.5-80 Hz
|
||
|
|
mask_fft = (freqs_fft >= 0.5) & (freqs_fft <= 80)
|
||
|
|
mask_psd = (freqs_psd >= 0.5) & (freqs_psd <= 80)
|
||
|
|
|
||
|
|
freq_fft = freqs_fft[mask_fft]
|
||
|
|
freq_psd = freqs_psd[mask_psd]
|
||
|
|
|
||
|
|
# Plot: 4 rows x 1 column
|
||
|
|
fig, axes = plt.subplots(4, 1, figsize=(16, 20))
|
||
|
|
fig.suptitle(f'EEG Data Quality Check — {", ".join(ch_names)}, '
|
||
|
|
f'Full Duration: {duration:.1f}s',
|
||
|
|
fontsize=16, fontweight='bold', y=0.995)
|
||
|
|
|
||
|
|
# Colormap for distinct channel
|
||
|
|
cmap = plt.cm.tab10 if n_channels <= 10 else plt.cm.tab20
|
||
|
|
colors = [cmap(i) for i in np.linspace(0, 1, n_channels)]
|
||
|
|
|
||
|
|
# ---- Row 1: Time Domain Signal ----
|
||
|
|
ax = axes[0]
|
||
|
|
offset = 0
|
||
|
|
step = max(100, np.std(data, axis=1).mean() * 1e6 * 4)
|
||
|
|
|
||
|
|
# Downsample for display
|
||
|
|
ds = max(1, n_samples // (int(duration) * 500))
|
||
|
|
t = np.arange(0, n_samples, ds) / sfreq
|
||
|
|
|
||
|
|
for i in range(n_channels):
|
||
|
|
sig = data[i, ::ds] * 1e6 + offset
|
||
|
|
ax.plot(t, sig, linewidth=0.5, alpha=0.9, color=colors[i], label=ch_names[i])
|
||
|
|
ax.text(t[0] - 0.5, offset, ch_names[i], fontsize=7, va='center', ha='right', color=colors[i])
|
||
|
|
offset += step
|
||
|
|
|
||
|
|
ax.set_xlim(0, duration)
|
||
|
|
ax.set_xlabel('Time (s)')
|
||
|
|
ax.set_ylabel('Amplitude (μV)')
|
||
|
|
ax.set_title('1. Time Domain Signal (Full Duration)', fontweight='bold')
|
||
|
|
ax.grid(True, alpha=0.3)
|
||
|
|
ax.legend(loc='upper right', fontsize=7, ncol=max(1, n_channels // 3), framealpha=0.8)
|
||
|
|
|
||
|
|
# ---- Row 2: Amplitude Spectrum (FFT) ----
|
||
|
|
ax = axes[1]
|
||
|
|
amp_data = amplitude[:, mask_fft] * 1e6 # (n_channels, n_freqs)
|
||
|
|
for i in range(n_channels):
|
||
|
|
ax.plot(freq_fft, amp_data[i], color=colors[i], linewidth=1.0, alpha=0.85, label=ch_names[i])
|
||
|
|
ax.axvline(50, color='red', linestyle='--', alpha=0.6, label='50 Hz Mains')
|
||
|
|
ax.set_xlim(0.5, 30)
|
||
|
|
ax.set_xlabel('Frequency (Hz)')
|
||
|
|
ax.set_ylabel('Amplitude (μV)')
|
||
|
|
ax.set_title('2. Amplitude Spectrum (FFT)', fontweight='bold')
|
||
|
|
ax.grid(True, alpha=0.3)
|
||
|
|
ax.legend(loc='upper right', fontsize=7, ncol=max(1, n_channels // 3), framealpha=0.8)
|
||
|
|
|
||
|
|
# ---- Row 3: PSD (Linear Scale) ----
|
||
|
|
ax = axes[2]
|
||
|
|
psd_data = psd[:, mask_psd] * 1e12 # (n_channels, n_freqs)
|
||
|
|
for i in range(n_channels):
|
||
|
|
ax.plot(freq_psd, psd_data[i], color=colors[i], linewidth=1.0, alpha=0.85, label=ch_names[i])
|
||
|
|
ax.axvline(50, color='red', linestyle='--', alpha=0.6, label='50 Hz Mains')
|
||
|
|
ax.set_xlim(0.5, 80)
|
||
|
|
ax.set_xlabel('Frequency (Hz)')
|
||
|
|
ax.set_ylabel('Power (μV²/Hz)')
|
||
|
|
ax.set_title('3. Power Spectral Density (Linear Scale)', fontweight='bold')
|
||
|
|
ax.grid(True, alpha=0.3)
|
||
|
|
ax.legend(loc='upper right', fontsize=7, ncol=max(1, n_channels // 3), framealpha=0.8)
|
||
|
|
|
||
|
|
# ---- Row 4: PSD (dB Scale) ----
|
||
|
|
ax = axes[3]
|
||
|
|
for i in range(n_channels):
|
||
|
|
psd_dbi = 10 * np.log10(psd_data[i] + 1e-20)
|
||
|
|
ax.plot(freq_psd, psd_dbi, color=colors[i], linewidth=1.0, alpha=0.85, label=ch_names[i])
|
||
|
|
ax.axvline(50, color='red', linestyle='--', alpha=0.6, label='50 Hz Mains')
|
||
|
|
ax.set_xlim(0.5, 80)
|
||
|
|
ax.set_xlabel('Frequency (Hz)')
|
||
|
|
ax.set_ylabel('Power (dB)')
|
||
|
|
ax.set_title('4. Power Spectral Density (dB Scale)', fontweight='bold')
|
||
|
|
ax.grid(True, alpha=0.3)
|
||
|
|
ax.legend(loc='upper right', fontsize=7, ncol=max(1, n_channels // 3), framealpha=0.8)
|
||
|
|
|
||
|
|
plt.tight_layout()
|
||
|
|
plt.subplots_adjust(top=0.97)
|
||
|
|
|
||
|
|
plt.savefig(output_path, dpi=150, bbox_inches='tight',
|
||
|
|
facecolor='white', edgecolor='none')
|
||
|
|
print(f"Figure saved to: {output_path}")
|
||
|
|
plt.show()
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
from scipy.io import loadmat
|
||
|
|
import numpy as np
|
||
|
|
|
||
|
|
mat = loadmat(r'D:\Ivey\Code_New_Proj\brainplot\plot64\eeg_data0511.mat', simplify_cells=True)
|
||
|
|
data = mat['eeg']['data'] # (samples, channels)
|
||
|
|
sfreq = 250
|
||
|
|
seg1 = data[0:int(10*sfreq), :] # 0-10s
|
||
|
|
seg2 = data[int(10*sfreq):int(20*sfreq), :] # 10-20s
|
||
|
|
|
||
|
|
print('Segment 1 (0-10s) shape:', seg1.shape)
|
||
|
|
print('Segment 2 (10-20s) shape:', seg2.shape)
|
||
|
|
print('Are they equal?', np.allclose(seg1, seg2))
|
||
|
|
print('Max difference:', np.max(np.abs(seg1 - seg2)))
|
||
|
|
print('Mean difference:', np.mean(np.abs(seg1 - seg2)))
|
||
|
|
|
||
|
|
# Check correlation
|
||
|
|
corr = np.corrcoef(seg1.flatten(), seg2.flatten())[0, 1]
|
||
|
|
print(f'Correlation: {corr:.4f}')
|