Skip to content

Commit

Permalink
update examples and tutorials
Browse files Browse the repository at this point in the history
  • Loading branch information
Jordy Thielen committed Oct 1, 2024
1 parent e3d5e49 commit abdfc7d
Show file tree
Hide file tree
Showing 14 changed files with 239 additions and 69 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
- Added `find_neighbours` and `find_worst_neighbour` to `utilities`
- Added `optimize_subset_clustering` to `stimulus`
- Added `optimize_layout_incremental` to `stimulus`
- Added `stimplot` to `plotting`

### Changed
- Changed order of tutorials and examples

### Fixed

Expand Down
11 changes: 5 additions & 6 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@
]

sphinx_gallery_conf = {
'examples_dirs': ['../examples', '../tutorials'], # path to your example scripts
'gallery_dirs': ['examples', 'tutorials'], # path to where to save gallery generated output
'filename_pattern': "(/example_|/tutorial_|)",
'examples_dirs': ['../examples', '../tutorials'], # path to your example scripts
'gallery_dirs': ['examples', 'tutorials'], # path to where to save gallery generated output
'filename_pattern': "(/example_|/tutorial_|)",
'within_subsection_order': "FileNameSortKey",
}

autosummary_generate = True
Expand All @@ -46,15 +47,13 @@

numpydoc_show_class_members = False

exclude_patterns = ["_build", "_templates"]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ['_build', "_templates", 'Thumbs.db', '.DS_Store']

source_suffix = [".rst", ".md"]

Expand Down
18 changes: 6 additions & 12 deletions examples/rcca.py → examples/example_1_rcca.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
rCCA
=================
====
This script shows how to use rCCA from PyntBCI for decoding c-VEP trials. The rCCA method uses a template matching
classifier where templates are estimated using reconvolution and canonical correlation analysis (CCA).
Expand Down Expand Up @@ -90,16 +90,10 @@
plt.tight_layout()

# Visualize stimuli
Vup = V.repeat(20, axis=1) # upsample to better visualize the sharp edges
plt.figure(figsize=(15, 8))
plt.plot(np.arange(Vup.shape[1]) / (20 * fs), 2 * np.arange(n_classes) + Vup.T)
for i in range(1 + int(V.shape[1] / (fs / fr))):
plt.axvline(i / fr, c="k", alpha=0.1)
plt.yticks(2 * np.arange(n_classes), np.arange(n_classes))
plt.xlabel("time [s]")
plt.ylabel("code")
plt.title("Code time-series")
plt.tight_layout()
fig, ax = plt.subplots(1, 1, figsize=(15, 8))
pyntbci.plotting.stimplot(V, fs=fs, ax=ax, plotfs=False)
fig.tight_layout()
ax.set_title("Stimulus time-series")

# %%
# The event matrix
Expand Down Expand Up @@ -436,4 +430,4 @@
# Print accuracy
print(f"Average accuracy: {avg.mean():.2f}")

# plt.show()
plt.show()
16 changes: 5 additions & 11 deletions examples/ecca.py → examples/example_2_ecca.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
eCCA
=================
====
This script shows how to use eCCA from PyntBCI for decoding c-VEP trials. The eCCA method uses a template matching
classifier where templates are estimated using averaging and canonical correlation analysis (CCA).
Expand Down Expand Up @@ -90,16 +90,10 @@
plt.tight_layout()

# Visualize stimuli
Vup = V.repeat(20, axis=1) # upsample to better visualize the sharp edges
plt.figure(figsize=(15, 8))
plt.plot(np.arange(Vup.shape[1]) / (20 * fs), 2 * np.arange(n_classes) + Vup.T)
for i in range(1 + int(V.shape[1] / (fs / fr))):
plt.axvline(i / fr, c="k", alpha=0.1)
plt.yticks(2 * np.arange(n_classes), np.arange(n_classes))
plt.xlabel("time [s]")
plt.ylabel("code")
plt.title("Code time-series")
plt.tight_layout()
fig, ax = plt.subplots(1, 1, figsize=(15, 8))
pyntbci.plotting.stimplot(V, fs=fs, ax=ax, plotfs=False)
fig.tight_layout()
ax.set_title("Stimulus time-series")

# %%
# ERP CCA
Expand Down
16 changes: 5 additions & 11 deletions examples/etrca.py → examples/example_3_etrca.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
eTRCA
==================
=====
This script shows how to use eTRCA from PyntBCI for decoding c-VEP trials. The eTRCA method uses a template matching
classifier where templates are estimated using averaging and task-related component analysis (TRCA).
Expand Down Expand Up @@ -90,16 +90,10 @@
plt.tight_layout()

# Visualize stimuli
Vup = V.repeat(20, axis=1) # upsample to better visualize the sharp edges
plt.figure(figsize=(15, 8))
plt.plot(np.arange(Vup.shape[1]) / (20 * fs), 2 * np.arange(n_classes) + Vup.T)
for i in range(1 + int(V.shape[1] / (fs / fr))):
plt.axvline(i / fr, c="k", alpha=0.1)
plt.yticks(2 * np.arange(n_classes), np.arange(n_classes))
plt.xlabel("time [s]")
plt.ylabel("code")
plt.title("Code time-series")
plt.tight_layout()
fig, ax = plt.subplots(1, 1, figsize=(15, 8))
pyntbci.plotting.stimplot(V, fs=fs, ax=ax, plotfs=False)
fig.tight_layout()
ax.set_title("Stimulus time-series")

# %%
# ERP TRCA
Expand Down
2 changes: 1 addition & 1 deletion examples/fbrcca.py → examples/example_4_fbrcca.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
FBrCCA
===================
======
This script shows how to use FBrCCA from PyntBCI for decoding c-VEP trials. The FBrCCA method uses a template matching
classifier where templates are estimated using reconvolution and canonical correlation analysis (CCA). Additionally,
FBrCCA uses a filterbank (FB).
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
Epoch CCA and LDA
==========================
Bitwise Decoding
================
This script shows how to use CCA and LDA on epochs within a trial, using PyntBCI, for decoding c-VEP trials. Epochs are
defined as the windows of data within a trial, synchronized to the onset of individual flashes. For each of these epochs
the classifier determines whether a flash was presented or not. Integrating that information over time, allows the
Expand Down Expand Up @@ -95,16 +95,10 @@
plt.tight_layout()

# Visualize stimuli
Vup = V.repeat(20, axis=1) # upsample to better visualize the sharp edges
plt.figure(figsize=(15, 8))
plt.plot(np.arange(Vup.shape[1]) / (20 * fs), 2 * np.arange(n_classes) + Vup.T)
for i in range(1 + int(V.shape[1] / (fs / fr))):
plt.axvline(i / fr, c="k", alpha=0.1)
plt.yticks(2 * np.arange(n_classes), np.arange(n_classes))
plt.xlabel("time [s]")
plt.ylabel("code")
plt.title("Code time-series")
plt.tight_layout()
fig, ax = plt.subplots(1, 1, figsize=(15, 8))
pyntbci.plotting.stimplot(V, fs=fs, ax=ax, plotfs=False)
fig.tight_layout()
ax.set_title("Stimulus time-series")

# %%
# Epoch decoding
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
Optimize stimulus subset and layout
===================
Stimulus optimization
=====================
This script shows how to optimize the stimulus presentation by means of selecting the optimal subset of stimuli from a
set of candidate stimuli and how to select an optimal layout to allocate them to a stimulus grid. The methods to
optimize such subset and layout were developed and evaluated in [1]_.
Expand Down
54 changes: 50 additions & 4 deletions pyntbci/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ def eventplot(
Parameters
----------
S: NDArray
The stimulus time-series of shape (samples)
The stimulus time-series of shape (n_samples)
E: NDArray
The event time-series of shape (events, samples)
The event time-series of shape (n_events, n_samples)
fs: int
The sampling rate in Hz
ax: Axes (default: None)
Expand Down Expand Up @@ -68,14 +68,60 @@ def eventplot(
ax.axvline(i / fs, c="k", alpha=0.1)

# Markup
ax.set_xlabel("time [sec]")
ax.set_xlabel("time [s]")
if events is None:
ax.set_ylabel("events")
else:
ax.set_ylabel("")
ax.set_yticks(-1.5 * np.arange(0, 1 + n_events) + 0.5, ("stimulus",) + events)


def stimplot(
S: NDArray,
fs: int,
ax: Axes = None,
upsample: int = 20,
plotfs: bool = True,
) -> None:
"""
Plot the stimulus time-series.
Parameters
----------
S: NDArray
The stimulus time-series of shape (n_stimuli, n_samples)
fs: int
The sampling rate in Hz
ax: Axes (default: None)
The axis to plot in. If None, a new figure will be opened.
upsample: int (default: 20)
A scalar value to upsample the stimulus and event time-series with for improved visualization
plotfs: bool (default: True)
Whether to plot vertical gridlines at the original sampling rate fs
"""
# Make figure
if ax is None:
fig = plt.figure()
ax = fig.add_subplot(111)

# Up-sample to better visualize the sharp edges
S = S.repeat(upsample, axis=1)
n_classes, n_samples = S.shape

# Visualize stimuli
ax.plot(np.arange(n_samples) / (upsample * fs), 2 * np.arange(n_classes) + S.T)

# Add sample rate
if plotfs:
for i in range(1 + int(n_samples / upsample)):
ax.axvline(i / fs, c="k", alpha=0.1)

# Label axes
ax.set_yticks(2 * np.arange(n_classes), np.arange(n_classes))
ax.set_xlabel("time [s]")
ax.set_ylabel("stimulus")


def topoplot(
z: NDArray,
locfile: str,
Expand All @@ -90,7 +136,7 @@ def topoplot(
Parameters
----------
z: NDArray
A vector of electrode values, e.g. a spatial filter/patterns.
A vector of electrode values, e.g. a spatial filter/patterns of shape (n_channels).
locfile: str
A .loc file with electrode position information.
cbar: bool (default: False)
Expand Down
30 changes: 30 additions & 0 deletions pyntbci/stimulus.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,3 +523,33 @@ def optimize_subset_clustering(
subset = np.where(np.logical_not(removed))[0]

return subset


def shift(
stimulus: NDArray,
stride: int = 1,
) -> NDArray:
"""
Shift a code to create multiple.
Parameters
----------
stimulus: NDArray
The stimulus to shift of shape (1, n_bits).
stride: int (default: 1)
The number of bits to shift.
Returns
-------
stimulus: NDArray
The set of stimuli with shifted versions of the original of shape (n_classes, n_bits).
"""
if stimulus.ndim == 1:
stimulus = stimulus[np.newaxis, :]
assert stimulus.shape[0] == 1, "The stimulus matrix must have only one stimulus."
tmp = stimulus
stimulus = np.zeros((int(stimulus.shape[1] / stride), stimulus.shape[1]))
stimulus[0, :] = tmp
for i in range(1, int(stimulus.shape[1] / stride)):
stimulus[i, :] = np.roll(stimulus[i-1, :], stride)
return stimulus
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,10 @@
plt.tight_layout()

# Visualize stimuli
Vup = V.repeat(20, axis=1) # upsample to better visualize the sharp edges
plt.figure(figsize=(15, 8))
plt.plot(np.arange(Vup.shape[1]) / (20 * fs), 2 * np.arange(n_classes) + Vup.T)
for i in range(1 + int(V.shape[1] / (fs / fr))):
plt.axvline(i / fr, c="k", alpha=0.1)
plt.yticks(2 * np.arange(n_classes), np.arange(n_classes))
plt.xlabel("time [s]")
plt.ylabel("code")
plt.title("Code time-series")
plt.tight_layout()
fig, ax = plt.subplots(1, 1, figsize=(15, 8))
pyntbci.plotting.stimplot(V, fs=fs, ax=ax, plotfs=False)
fig.tight_layout()
ax.set_title("Stimulus time-series")

# %%
# The event matrix
Expand Down
Loading

0 comments on commit abdfc7d

Please sign in to comment.