Module 2: Neuroscience for EEG Workbook

Welcome to Module 2! This workbook bridges neuroscience fundamentals with EEG practice. You'll learn about brain anatomy, neural origins of EEG signals, electrode placement systems, and how to identify different brain rhythms. This knowledge is essential for understanding what your EEG recordings actually measure.
Learning Objectives:
• Understand functional neuroanatomy relevant to EEG
• Learn how neural activity generates EEG signals
• Master the 10-20 electrode placement system
• Identify EEG rhythms and their generators
• Recognize sleep stages in EEG recordings

Topic 1: Functional Neuroanatomy

1

Cortical Areas and Functions

Match the cortical areas with their primary functions and expected EEG activity:

1. Frontal Cortex
2. Parietal Cortex
3. Temporal Cortex
4. Occipital Cortex
5. Central Region
A. Visual processing, alpha rhythm
B. Motor control, mu rhythm
C. Executive function, frontal theta
D. Sensory integration, posterior alpha
E. Auditory processing, temporal theta
Match: 1-___, 2-___, 3-___, 4-___, 5-___

Complete the code to map brain regions to electrode positions:

# Define brain regions and their corresponding electrodes brain_regions = { 'frontal': ['Fp1', 'Fp2', ' ', ' ', 'F3', 'F4', 'F7', 'F8'], 'central': ['C3', 'C4', ' ', ' '], 'parietal': ['P3', 'P4', ' ', ' '], 'temporal': ['T3', 'T4', 'T5', ' '], 'occipital': ['O1', ' '] } # Function to identify brain region from electrode name def get_brain_region(electrode_name): for region, electrodes in brain_regions.items(): if electrode_name in electrodes: return return 'Unknown' # Hemisphere identification def get_hemisphere(electrode_name): if electrode_name[-1] in ['1', '3', '5', '7']: return ' ' elif electrode_name[-1] in ['2', '4', '6', '8']: return ' ' elif electrode_name[-1] == 'z': return ' '
2

Neural Basis of EEG Signals

Understanding how EEG signals are generated is crucial for interpretation.

Select all TRUE statements about EEG signal generation:

Complete the code to simulate how neural dipoles sum to create EEG:

import numpy as np def simulate_neural_dipoles(n_neurons, synchrony_level): """ Simulate how synchronized neural activity creates EEG signals Parameters: - n_neurons: number of neurons - synchrony_level: 0 (random) to 1 (perfectly synchronized) """ # Base frequency of neural oscillation (e.g., 10 Hz for alpha) base_freq = 10 duration = 1 # second fs = 1000 # sampling rate time = np.linspace(0, duration, fs) # Initialize summed activity eeg_signal = np.zeros(len(time)) for i in range(n_neurons): # Random phase offset (reduced by synchrony level) phase_offset = np.random.uniform(0, 2*np.pi) * (1 - ) # Random amplitude (represents dipole strength) amplitude = np.random.normal(1, 0.2) # Individual neuron's contribution neuron_signal = amplitude * np.sin(2 * np.pi * base_freq * time + ) # Add to total EEG eeg_signal += # Scale by number of neurons (spatial summation) eeg_signal = eeg_signal / np.sqrt( ) return time, eeg_signal # What happens to signal amplitude with more synchronized neurons? # Answer:

Topic 2: EEG Rhythms & Generators

3

Brain Wave Classification

Complete the comprehensive brain wave reference table:

Rhythm Frequency (Hz) Amplitude (μV) Location State/Function
Delta - 20-200 Widespread
Theta 4 - 8 Frontal midline Drowsiness, meditation
Alpha - 20-60 Relaxed wakefulness
Beta 13 - 30 5-30
Gamma - 100 <5 Widespread Cognitive processing
Mu 8 - 13 ~50 Motor rest

Special rhythm characteristics - select all correct statements:

4

Rhythm Detection Algorithm

Create a function to automatically detect and classify brain rhythms:

def detect_brain_rhythms(eeg_data, fs, channel_name): """ Detect dominant brain rhythm in EEG data """ from scipy import signal # Define rhythm bands rhythms = { 'delta': (0.5, 4), 'theta': (4, 8), 'alpha': (8, 13), 'beta': (13, 30), 'gamma': (30, 50) } # Special rhythms by location location_specific = { 'mu': {'freq': (8, 13), 'channels': ['C3', 'C4', ' ']}, 'occipital_alpha': {'freq': (8, 13), 'channels': ['O1', 'O2', ' ']}, 'frontal_theta': {'freq': (4, 8), 'channels': ['Fz', ' ']} } # Compute power spectral density freqs, psd = signal.welch(eeg_data, fs=fs, nperseg=fs*2) # Calculate power in each band band_powers = {} total_power = np.trapz(psd, freqs) for rhythm_name, (low_freq, high_freq) in rhythms.items(): # Find frequency indices idx = np.where((freqs >= low_freq) & (freqs <= ))[0] # Calculate absolute and relative power abs_power = np. (psd[idx], freqs[idx]) rel_power = (abs_power / ) * 100 band_powers[rhythm_name] = { 'absolute': abs_power, 'relative': rel_power } # Find dominant rhythm dominant = max(band_powers.items(), key=lambda x: x[1][' '])[0] # Check for location-specific rhythms if channel_name in location_specific['mu']['channels'] and dominant == 'alpha': # Could be mu rhythm - check for reactivity return 'mu (needs motor task to confirm)' return dominant, band_powers # Alpha reactivity test def test_alpha_reactivity(eeg_eyes_closed, eeg_eyes_open, fs): """Test alpha blocking with eye opening""" # Calculate alpha power for both conditions _, psd_closed = signal.welch(eeg_eyes_closed, fs=fs) _, psd_open = signal.welch(eeg_eyes_open, fs=fs) # Alpha band indices (8-13 Hz) freqs = np.fft.fftfreq(len(psd_closed), 1/fs) alpha_idx = np.where((freqs >= 8) & (freqs <= 13))[0] # Calculate alpha suppression alpha_closed = np.mean(psd_closed[alpha_idx]) alpha_open = np.mean(psd_open[alpha_idx]) suppression_ratio = ( - ) / alpha_closed * 100 return suppression_ratio # Positive value indicates suppression

Topic 3: 10-20 Electrode System

5

Electrode Placement System

Master the international 10-20 system for electrode placement:

Draw the 10-20 electrode positions on a head diagram
Include: Fp1/2, F3/4/7/8, C3/4, P3/4, O1/2, T3/4/5/6, Fz, Cz, Pz

Complete the code for 10-20 system calculations:

def calculate_electrode_positions(nasion_to_inion, ear_to_ear): """ Calculate electrode positions based on head measurements Parameters: - nasion_to_inion: distance from nasion to inion (cm) - ear_to_ear: distance between preauricular points (cm) """ # Standard 10-20 percentages positions = {} # Sagittal line (front to back) positions['Fpz'] = nasion_to_inion * # 10% from nasion positions['Fz'] = nasion_to_inion * # 30% from nasion positions['Cz'] = nasion_to_inion * 0.50 # 50% (vertex) positions['Pz'] = nasion_to_inion * # 70% from nasion positions['Oz'] = nasion_to_inion * 0.90 # 90% from nasion # Coronal line (ear to ear) positions['T3'] = ear_to_ear * 0.10 # 10% from left ear positions['C3'] = ear_to_ear * # 30% from left ear positions['Cz_check'] = ear_to_ear * 0.50 # Should match Cz positions['C4'] = ear_to_ear * # 70% from left ear positions['T4'] = ear_to_ear * 0.90 # 90% from left ear return positions # Extended 10-10 system def get_1010_positions(): """Get additional electrodes in 10-10 system""" additional_electrodes = { 'frontal': ['AF3', 'AF4', 'F1', 'F2', 'F5', 'F6'], 'central': ['FC1', 'FC2', 'FC5', 'FC6', 'CP1', 'CP2', 'CP5', 'CP6'], 'parietal': ['P1', 'P2', 'P5', 'P6'], 'temporal': ['FT9', 'FT10', 'TP9', 'TP10'], 'occipital': ['PO3', 'PO4', 'PO7', 'PO8'] } return additional_electrodes # Reference electrode selection reference_types = { 'monopolar': { 'linked_ears': ['A1+A2', 'Good for '], 'Cz': ['Cz', 'Common for studies'], 'average': ['AVG', 'Requires electrodes minimum'] }, 'bipolar': { 'longitudinal': ['Fp1-F3', 'F3-C3', 'C3-P3', 'P3-O1'], 'transverse': ['F7-F3', 'F3-Fz', 'Fz-F4', 'F4-F8'] } }

Match electrode naming conventions:

Letter Brain Region Numbers Hemisphere
F Odd (1,3,5,7)
P Even (2,4,6,8)
T z (as in Fz)
6

Montage Selection

Choose appropriate montages for different clinical and research applications:

Match the montage type with its best application:

1. Bipolar longitudinal
2. Referential (linked ears)
3. Average reference
4. Laplacian
5. Bipolar transverse
A. Source localization studies
B. Epilepsy focus detection
C. Sleep studies
D. High spatial resolution
E. Lateralization assessment
Match: 1-___, 2-___, 3-___, 4-___, 5-___
def create_montage(electrode_data, montage_type): """ Create different montages from monopolar recordings """ if montage_type == 'bipolar_longitudinal': # Define channel pairs pairs = [ ('Fp1', 'F3'), ('F3', 'C3'), ('C3', 'P3'), ('P3', 'O1'), ('Fp2', 'F4'), ('F4', 'C4'), ('C4', 'P4'), ('P4', 'O2'), ('Fp1', 'F7'), ('F7', 'T3'), ('T3', 'T5'), ('T5', 'O1'), ('Fp2', 'F8'), ('F8', 'T4'), ('T4', 'T6'), ('T6', 'O2') ] bipolar_data = {} for ch1, ch2 in pairs: # Bipolar = first electrode minus second bipolar_data[f'{ch1}-{ch2}'] = electrode_data[ch1] - electrode_data[ ] elif montage_type == 'average_reference': # Calculate average of all electrodes all_channels = list(electrode_data.keys()) avg_signal = np.mean([electrode_data[ch] for ch in all_channels], axis= ) avg_ref_data = {} for ch in all_channels: # Subtract average from each channel avg_ref_data[ch] = electrode_data[ch] - elif montage_type == 'laplacian': # Current source density (spatial filter) laplacian_data = {} # Define nearest neighbors for each electrode neighbors = { 'Cz': ['Fz', 'C3', 'C4', 'Pz'], 'C3': ['F3', 'Cz', 'T3', 'P3'], # ... more electrodes } for electrode, neighbor_list in neighbors.items(): if electrode in electrode_data: # Laplacian = electrode - mean of neighbors neighbor_mean = np.mean([electrode_data[n] for n in neighbor_list if n in electrode_data], axis=0) laplacian_data[electrode] = electrode_data[electrode] - return bipolar_data if montage_type.startswith('bipolar') else avg_ref_data

Topic 4: Sleep Stages & EEG

7

Sleep Stage Characteristics

Identify EEG features of different sleep stages:

Stage EEG Features Dominant Frequency Special Markers
Wake Low amplitude, mixed frequency Hz Alpha with eyes closed
N1 (Stage 1) 4-7 Hz Vertex sharp waves
N2 (Stage 2) Theta background Hz and
N3 (Stage 3/4) 0.5-2 Hz >75 μV delta waves
REM Low amplitude, mixed frequency

Select all TRUE statements about sleep EEG:

8

Basic Sleep Stage Identifier

Build a simple algorithm to identify sleep stage features:

def detect_sleep_features(eeg_data, fs, stage='N2'): """ Detect characteristic features of different sleep stages """ if stage == 'N2': # Detect sleep spindles spindles = detect_spindles(eeg_data, fs) # Detect K-complexes k_complexes = detect_k_complexes(eeg_data, fs) return {'spindles': spindles, 'k_complexes': k_complexes} elif stage == 'N3': # Detect slow waves slow_waves = detect_slow_waves(eeg_data, fs) return {'slow_waves': slow_waves} def detect_spindles(eeg_data, fs): """ Detect sleep spindles (12-14 Hz, 0.5-2 sec duration) """ from scipy import signal # Bandpass filter for spindle frequency spindle_band = ( , ) # Hz nyquist = fs / 2 b, a = signal.butter(4, [spindle_band[0]/nyquist, spindle_band[1]/nyquist], 'band') # Filter the signal spindle_filtered = signal.filtfilt(b, a, eeg_data) # Calculate envelope using Hilbert transform analytic_signal = signal.hilbert(spindle_filtered) envelope = np.abs( ) # Threshold for spindle detection (e.g., 2 SD above mean) threshold = np.mean(envelope) + * np.std(envelope) # Find segments above threshold above_threshold = envelope > threshold # Identify spindle events (duration criteria) min_duration = * fs # 0.5 seconds max_duration = * fs # 2 seconds spindle_events = [] in_spindle = False start_idx = 0 for i, val in enumerate(above_threshold): if val and not in_spindle: in_spindle = True start_idx = i elif not val and in_spindle: in_spindle = False duration = i - start_idx if min_duration <= duration <= max_duration: spindle_events.append((start_idx/fs, i/fs)) # Convert to seconds return spindle_events def detect_k_complexes(eeg_data, fs): """ Detect K-complexes (sharp negative then positive wave) """ # Low-pass filter to enhance slow components cutoff = 4 # Hz b, a = signal.butter(4, cutoff/(fs/2), 'low') filtered = signal.filtfilt(b, a, eeg_data) # Find large amplitude deflections # K-complex: negative peak followed by positive within 0.5-1 sec # Calculate derivative to find sharp changes derivative = np.diff(filtered) # Find negative peaks neg_peaks, _ = signal.find_peaks(-filtered, height= , # Minimum amplitude (μV) distance=int(1.5*fs)) # Minimum 1.5 sec apart k_complexes = [] for neg_peak in neg_peaks: # Look for positive deflection within 0.5-1 second window_start = neg_peak window_end = min(neg_peak + int(1*fs), len(filtered)) window_data = filtered[window_start:window_end] if len(window_data) > 0: pos_peak = np.argmax(window_data) # Check if positive peak is significant if window_data[pos_peak] > : # μV threshold k_complexes.append(neg_peak/fs) # Convert to seconds return k_complexes

Project: Rhythm Detection Algorithm

9

Comprehensive Rhythm Analyzer

Build a complete system to detect and classify brain rhythms:

class EEGRhythmAnalyzer: def __init__(self, fs=256): self.fs = fs self.rhythms = { 'delta': {'range': (0.5, 4), 'amplitude': (20, 200)}, 'theta': {'range': (4, 8), 'amplitude': (10, 100)}, 'alpha': {'range': (8, 13), 'amplitude': (20, 60)}, 'beta': {'range': (13, 30), 'amplitude': (5, 30)}, 'gamma': {'range': (30, 100), 'amplitude': (1, 5)} } def analyze_channel(self, eeg_data, channel_name): """ Comprehensive rhythm analysis for a single channel """ results = { 'channel': channel_name, 'dominant_rhythm': None, 'rhythm_powers': {}, 'special_features': [] } # Calculate PSD freqs, psd = signal.welch(eeg_data, fs=self.fs, nperseg=self.fs*2) # Analyze each rhythm max_power = 0 dominant = None for rhythm_name, params in self.rhythms.items(): freq_range = params['range'] # Extract band band_idx = np.where((freqs >= freq_range[0]) & (freqs <= freq_range[1]))[0] # Calculate metrics band_power = np.trapz(psd[band_idx], freqs[band_idx]) peak_freq = freqs[band_idx[np.argmax(psd[band_idx])]] peak_amplitude = np.sqrt(np.max(psd[band_idx]) * 2) # Convert to amplitude results['rhythm_powers'][rhythm_name] = { 'power': band_power, 'peak_frequency': peak_freq, 'peak_amplitude': peak_amplitude, 'in_normal_range': params['amplitude'][0] <= peak_amplitude <= params['amplitude'][1] } if band_power > max_power: max_power = band_power dominant = rhythm_name results['dominant_rhythm'] = dominant # Check for special features based on location if channel_name in ['O1', 'O2'] and dominant == 'alpha': results['special_features'].append('Posterior dominant rhythm') elif channel_name in ['C3', 'C4'] and dominant == 'alpha': # Could be mu rhythm - check characteristics if self._check_mu_characteristics(eeg_data): results['special_features'].append('Possible mu rhythm') elif channel_name == 'Fz' and results['rhythm_powers']['theta']['power'] > \ results['rhythm_powers']['alpha']['power']: results['special_features'].append('Frontal midline theta') return results def _check_mu_characteristics(self, eeg_data): """ Check if alpha-range activity has mu rhythm characteristics """ # Mu rhythm has arch-shaped morphology # Check for non-sinusoidal shape using spectral analysis freqs, psd = signal.periodogram(eeg_data, fs=self.fs) # Find 10 Hz peak peak_10hz_idx = np.argmin(np.abs(freqs - 10)) # Check for harmonics (mu has strong 20 Hz harmonic) peak_20hz_idx = np.argmin(np.abs(freqs - 20)) harmonic_ratio = psd[peak_20hz_idx] / psd[peak_10hz_idx] # Mu rhythm typically has stronger harmonics than alpha return harmonic_ratio > # Threshold value def compare_conditions(self, eeg_rest, eeg_task, channel): """ Compare rhythms between two conditions (e.g., rest vs task) """ rest_analysis = self.analyze_channel(eeg_rest, channel) task_analysis = self.analyze_channel(eeg_task, channel) comparison = { 'channel': channel, 'changes': {} } for rhythm in self.rhythms.keys(): rest_power = rest_analysis['rhythm_powers'][rhythm]['power'] task_power = task_analysis['rhythm_powers'][rhythm]['power'] change_percent = ((task_power - rest_power) / rest_power) * 100 comparison['changes'][rhythm] = { 'rest_power': rest_power, 'task_power': task_power, 'change_percent': change_percent, 'significant': abs(change_percent) > # 20% threshold } # Identify reactivity patterns if channel in ['O1', 'O2'] and comparison['changes']['alpha']['change_percent'] < -30: comparison['reactivity'] = 'Alpha blocking detected' elif channel in ['C3', 'C4'] and comparison['changes']['alpha']['change_percent'] < -30: comparison['reactivity'] = 'Mu suppression detected' return comparison

Application questions:

  1. What threshold would you use for the mu rhythm harmonic ratio?
    • 0.1
    • 0.3
    • 0.5
    • 0.8
  2. What percentage change would indicate significant rhythm modulation?
  3. How would you validate mu rhythm vs posterior alpha?
10

Clinical Application

Apply your knowledge to interpret EEG patterns:

Match these EEG findings with their clinical significance:

EEG Finding Clinical Significance
Absent posterior alpha rhythm ____________________
Excessive frontal theta in adults ____________________
High amplitude beta (>30 μV) ____________________
Focal delta activity ____________________
Mu rhythm non-reactive to movement ____________________

Options: Drowsiness/attention issues, Medication effect (benzodiazepines), Structural lesion, Cortical dysfunction, Normal variant

Design a protocol to test alpha reactivity:

Include: electrode placement, instructions to participant, analysis method, interpretation criteria

Answer Key

Check your answers after completing all exercises.

Exercise 1: Match: 1-C, 2-D, 3-E, 4-A, 5-B
Code: 'Fz', 'F8', 'Cz', 'C4', 'Pz', 'P4', 'T6', 'O2', region, 'left', 'right', 'midline'
Exercise 2: True statements: 1, 3, 5, 6
Code: synchrony_level, phase_offset, neuron_signal, n_neurons, increases
Exercise 3: Delta: 0.5, 4, Deep sleep; Theta: 10-100; Alpha: 8, 13, Occipital; Beta: Frontal, Active thinking; Gamma: 30; Mu: Central
True statements: 1, 2, 4, 5
Exercise 4: 'Cz', 'Pz', 'Fp1', high_freq, trapz, total_power, relative, alpha_closed, alpha_open
Exercise 5: 0.10, 0.30, 0.70, 0.30, 0.70, clinical, ERP, 19
Table: Frontal/Left, Parietal/Right, Temporal/Midline
Exercise 6: Match: 1-B, 2-C, 3-A, 4-D, 5-E
Code: ch2, 0, avg_signal, neighbor_mean
Exercise 7: Wake: 8-13; N1: Theta replaces alpha; N2: 4-8, Sleep spindles, K-complexes; N3: High amplitude delta; REM: Mixed frequency, Rapid eye movements
True statements: 1, 3, 4, 5
Exercise 8: 12, 14, analytic_signal, 2, 0.5, 2, 50-75, 25-50
Exercise 9: 0.3, 20
Q1: B (0.3), Q2: 20-30%, Q3: Motor task suppression test
Exercise 10: Clinical significance: Cortical dysfunction, Drowsiness, Medication effect, Structural lesion, Normal variant

Congratulations! 🎉

You've completed Module 2 on Neuroscience for EEG! You now understand the neural basis of EEG signals, electrode placement systems, brain rhythms, and their clinical significance.

Next step: Move on to Module 3 - EEG Data Collection