# EEG Microstates#

In continually recorded human EEG, the spatial distribution of electric potentials across the cortex changes over time. Interestingly, for brief time periods (60 - 120ms), quasi-stable states of activity (i.e., **microstates**), characterized by unique spatial configurations of electrical activity distribution, have been observed. NeuroKit can be used to easily analyze them.

```
# Load the NeuroKit package and other useful packages
import neurokit2 as nk
import mne
```

## EEG Preprocessing#

First, let’s download a raw eeg data in an MNE format and select teh EEG channels.

```
raw = nk.data("eeg_1min_200hz")
raw = raw.pick(["eeg"], verbose=False)
sampling_rate = raw.info["sfreq"] # Store the sampling rate
```

EEG recordings measure the difference in electric potential between each electrode and a reference electrode. This means that the ideal reference electrode is one which records **all** the interfering noise from the environment but doesn’t pick up any fluctuating signals related to brain activity. The idea behind re-referencing is to express the voltage at the EEG scalp channels with respect to another, new reference. This “virtual reference” can be any recorded channel, or the average of all the channels, as we will use in the current analysis.

Below, we will apply a band-pass filter and re-reference the signals to remove power line noise, slow drifts and other large artifacts from the raw input.

```
# Apply band-pass filter (1-35Hz) and re-reference the raw signal
eeg = raw.copy().filter(1, 35, verbose = False)
eeg = nk.eeg_rereference(eeg, 'average')
nk.signal_plot([raw.get_data()[0, 0:500], eeg.get_data()[0, 0:500]],
labels =["Raw", "Preprocessed"],
sampling_rate = eeg.info["sfreq"])
```

## Microstates Analysis#

### Minimal Example#

Microstates can be extracted and analyzed like that:

```
# Extract microstates
microstates = nk.microstates_segment(eeg, n_microstates=4)
# Visualize the extracted microstates
nk.microstates_plot(microstates, epoch = (0, 500))
```

This shows the aspect of the microstates and their sequence in the Global Field Power (GFP; see below). We can then proceed to a statistical analysis.

```
nk.microstates_static(microstates, sampling_rate=sampling_rate, show=True)
```

Microstate_0_Proportion | Microstate_1_Proportion | Microstate_2_Proportion | Microstate_3_Proportion | Microstate_0_LifetimeDistribution | Microstate_1_LifetimeDistribution | Microstate_2_LifetimeDistribution | Microstate_3_LifetimeDistribution | Microstate_0_DurationMean | Microstate_0_DurationMedian | Microstate_1_DurationMean | Microstate_1_DurationMedian | Microstate_2_DurationMean | Microstate_2_DurationMedian | Microstate_3_DurationMean | Microstate_3_DurationMedian | Microstate_Average_DurationMean | Microstate_Average_DurationMedian | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|

0 | 0.292417 | 0.180417 | 0.259167 | 0.268 | 790.5 | 507.5 | 711.5 | 709.5 | 0.020144 | 0.015 | 0.018102 | 0.01 | 0.019389 | 0.015 | 0.020303 | 0.015 | 0.019589 | 0.015 |

This shows computes static statistics, such as the prevalence of each microstates and the median duration time (also shown in the graph).

```
nk.microstates_dynamic(microstates, show=True)
```

Microstate_0_to_0 | Microstate_0_to_1 | Microstate_0_to_2 | Microstate_0_to_3 | Microstate_1_to_0 | Microstate_1_to_1 | Microstate_1_to_2 | Microstate_1_to_3 | Microstate_2_to_0 | Microstate_2_to_1 | Microstate_2_to_2 | Microstate_2_to_3 | Microstate_3_to_0 | Microstate_3_to_1 | Microstate_3_to_2 | Microstate_3_to_3 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|

0 | 0.751781 | 0.07153 | 0.092334 | 0.084355 | 0.106747 | 0.724122 | 0.082717 | 0.086414 | 0.10418 | 0.054662 | 0.742122 | 0.099035 | 0.098259 | 0.055037 | 0.092973 | 0.753731 |

Finally, one can compute complexity features of the sequence of microstates, such as the entropy.

```
nk.microstates_complexity(microstates, show=True)
```

Microstates_Entropy_Shannon | |
---|---|

0 | 1.978443 |

## Options and features#

### Global Field Power (GFP)#

Under the hood, `microstates_segment()`

starts by computing the GFP.

The Global Field Power (GFP) is a reference-independent measure of potential field strength. It is thought to quantify the integrated electrical activity of the brain and is mathematically defined as the standard deviation of all electrodes at a given time.

The GFP time series periodically shows peaks, where the EEG topographies are most clearly defined (i.e., signal-to-noise ratio is maximized at GFP peaks). As such, GFP peak samples are often used to extract microstates.

This can be visualized manually:

```
gfp = nk.eeg_gfp(eeg)
peaks = nk.microstates_peaks(eeg, gfp=gfp)
# Plot the peaks in the first 200 data points
nk.events_plot(events = peaks[peaks < 200], signal = gfp[0:200])
```

By default, the microstates clustering algorithm is **trained** on the EEG activity at these peaks, and then applied to back-predict the state at all data points. However, this behaviour can be changed. For instance, one can decide to train the algorithm directly on all data points.

```
microstates_all = nk.microstates_segment(eeg, n_microstates=4, train="all")
nk.microstates_plot(microstates_all, epoch = (0, 500))
```

### How many microstates?#

Most of the clustering algorithms used in microstates analysis require the number of clusters to extract to be specified beforehand.

However, one can attempt at statistically estimating the optimal number of microstates. A variety of indices of fit can be used.

```
# Note: we cropped the data as this function takes some time to compute
n_optimal, scores = nk.microstates_findnumber(eeg.crop(0, 5), n_max=8, show=True)
print("Optimal number of microstates: ", n_optimal)
```

```
[........................................] 0/7
```

```
[█████...................................] 1/7
```

```
[███████████.............................] 2/7
```

```
[█████████████████.......................] 3/7
```

```
[██████████████████████..................] 4/7
```

```
[████████████████████████████............] 5/7
```

```
[██████████████████████████████████......] 6/7
```

```
[████████████████████████████████████████] 7/7
```

```
```

```
Optimal number of microstates: 5.0
```

### Microstates clustering algorithms#

Several different clustering algorithms can be used to segment your EEG recordings into microstates. These algorithms mainly differ in how they define cluster membership and the cost functionals to be optimized (Xu & Tian, 2015). The method to use hence depends on your data and the underlying assumptions of the methods (e.g., some methods ignore polarity). There is no one true method that gives the best results but you can refer to Poulsen et al., 2018 if you would like a more detailed review of the different clustering methods.

In the example below, we will compare the two most commonly applied clustering algorithms - the *K-means* and *modified K-means*. Other methods that can be applied using `nk.microstates_segment`

include `kmedoids, pca, ica, aahc`

.

```
# Extract microstates
microstates_kmeans = nk.microstates_segment(eeg, n_microstates=4, method="kmeans")
microstates_kmod = nk.microstates_segment(eeg, n_microstates=4, method="kmod")
# Global Explained Variance
gev_kmeans = microstates_kmeans['GEV']
gev_kmod = microstates_kmod['GEV']
print( f' Using conventional Kmeans, GEV = {gev_kmeans*100:.2f}%')
print( f' Using modified Kmeans, GEV = {gev_kmod*100:.2f}%')
# Visualize the extracted microstates
nk.microstates_plot(microstates_kmeans, epoch = (150, 450))
nk.microstates_plot(microstates_kmod, epoch = (150, 450))
```

```
Using conventional Kmeans, GEV = 61.46%
Using modified Kmeans, GEV = 72.23%
```