Skip to content

Commit

Permalink
Update with latest changes from develop (#246)
Browse files Browse the repository at this point in the history
- Updating doc examples
- Update plot conditions method
- Fix github action builds
  • Loading branch information
oreHGA authored Mar 7, 2024
1 parent a7dbf2a commit 1257c3a
Show file tree
Hide file tree
Showing 28 changed files with 291 additions and 484 deletions.
26 changes: 26 additions & 0 deletions .devcontainer/cpu/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "EEG-ExPy-CPU",
"image": "mcr.microsoft.com/devcontainers/python:3.8",

"customizations": {
"vscode": {
"extensions": [
"ms-python.python"
],
"settings": {
"python.pythonPath": "/usr/local/bin/python"
}
}
},

"forwardPorts": [
8000,
8888,
5000,
6000
],
// print the python version:
"postCreateCommand": "python --version && pip install -r requirements.txt && pip install -e . && echo 'Dependencies installed'",
"appPort": 8000

}
13 changes: 7 additions & 6 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:

jobs:
build:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Set up Python
Expand All @@ -17,17 +17,18 @@ jobs:
python-version: 3.8
- name: Install dependencies
run: |
make install-deps-apt
python -m pip install --upgrade pip wheel
python -m pip install attrdict
# Install wxPython wheels since they are distribution-specific and therefore not on PyPI
# See: https://wxpython.org/pages/downloads/index.html
pip install -U -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-20.04 wxPython
make install-deps-wxpython
- name: Build project
run: |
make build
pip install .
- name: Build docs
run: |
cd doc && make html
make docs
- name: Deploy Docs
uses: peaceiris/actions-gh-pages@v3
if: github.ref == 'refs/heads/master' # TODO: Deploy seperate develop-version of docs?
Expand Down
72 changes: 28 additions & 44 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,60 +8,53 @@ on:

jobs:
test:
name: ${{ matrix.os }}, py-${{ matrix.python_version }}
name: test (${{ matrix.os }}, py-${{ matrix.python_version }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
python_version: [3.7]
os: ['ubuntu-22.04', windows-latest, macOS-latest]
python_version: ['3.8']
include:
- os: ubuntu-latest
python_version: 3.8
# Experimental: Python 3.9
# Works fine, commented out because mostly covered (at least installing/building deps) by the typecheck job
# See issue: https://github.com/NeuroTechX/eeg-notebooks/issues/50
#- os: ubuntu-latest
# python_version: 3.9

# Check 3.10 for future-proofing
- os: ubuntu-22.04
python_version: '3.10'

steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v1
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python_version }}

# Not needed if pywinhook is installed from wheels
#- name: Install swig
# if: "startsWith(runner.os, 'windows')"
# run: |
# (New-Object System.Net.WebClient).DownloadFile("http://prdownloads.sourceforge.net/swig/swigwin-4.0.1.zip","swigwin-4.0.1.zip");
# Expand-Archive .\swigwin-4.0.1.zip .;
# echo "$((Get-Item .).FullName)/swigwin-4.0.1" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

- name: Install APT dependencies
if: "startsWith(runner.os, 'Linux')"
run: |
# update archive links
sudo apt-get update
# xvfb is a dependency to create a virtual display
# libgtk-3-dev is a requirement for wxPython
# freeglut3-dev is a requirement for a wxPython dependency
sudo apt-get -y install xvfb libgtk-3-dev freeglut3-dev
make install-deps-apt
- name: Upgrade pip
run: |
python -m pip install --upgrade pip wheel
- name: Install Linux dependencies
if: "startsWith(runner.os, 'Linux')"
run: |
python -m pip install --upgrade pip wheel
# Install wxPython wheels since they are distribution-specific and therefore not on PyPI
# See: https://wxpython.org/pages/downloads/index.html
pip install -U -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-18.04 wxPython
pip install .
- name: Install MacOS/Windows dependencies
make install-deps-wxpython
- name: Install dependencies
run: |
python -m pip install --upgrade pip wheel
pip install .
pip install -U psychtoolbox # JG_ADD
# EB: needed?
make build
- name: Run eegnb install test
shell: bash
run: |
Expand All @@ -78,14 +71,15 @@ jobs:
Xvfb :0 -screen 0 1024x768x24 -ac +extension GLX +render -noreset &> xvfb.log &
export DISPLAY=:0
fi
pytest
make test
typecheck:
name: typecheck (${{ matrix.os }}, py-${{ matrix.python_version }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
os: ['ubuntu-22.04']
python_version: [3.9]

steps:
Expand All @@ -97,27 +91,17 @@ jobs:
- name: Install APT dependencies
if: "startsWith(runner.os, 'Linux')"
run: |
# update archive links
sudo apt-get update
# xvfb is a dependency to create a virtual display
# libgtk-3-dev is a requirement for wxPython
# freeglut3-dev is a requirement for a wxPython dependency
sudo apt-get -y install xvfb libgtk-3-dev freeglut3-dev
make install-deps-apt
- name: Upgrade pip
run: |
python -m pip install --upgrade pip wheel
- name: Install Linux dependencies
if: "startsWith(runner.os, 'Linux')"
run: |
python -m pip install --upgrade pip wheel
# Install wxPython wheels since they are distribution-specific and therefore not on PyPI
# See: https://wxpython.org/pages/downloads/index.html
pip install -U -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-18.04 wxPython
pip install .
- name: Install MacOS/Windows dependencies
make install-deps-wxpython
- name: Install dependencies
run: |
python -m pip install --upgrade pip wheel
pip install .
make build
- name: Typecheck
run: |
# Exclude visual_cueing due to errors
python -m mypy --exclude 'examples/visual_cueing'
make typecheck
40 changes: 40 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
build:
# Use pep517 to install pygatt==4.0.5(deprecated setuptools/egg installer) on macos
pip install --use-pep517 .

test:
pytest

typecheck:
# Exclude visual_cueing due to errors
python -m mypy --exclude 'examples/visual_cueing'

docs:
cd doc && make html

clean:
cd doc && make clean

install-deps-apt:
sudo apt-get update # update archive links

# xvfb is a dependency to create a virtual display
# libgtk-3-dev is a requirement for wxPython
# freeglut3-dev is a requirement for a wxPython dependency
# portaudio19-dev *might* be required to import psychopy on Ubuntu
# pulseaudio *might* be required to actually run the tests (on PsychoPy import)
# libpulse-dev required to build pocketsphinx (speech recognition dependency of psychopy)
# libsdl2-dev required by psychopy
# libnotify4 is so we can have the libnotify.so module used in wxPython working
sudo apt-get -y install xvfb libgtk-3-dev freeglut3-dev portaudio19-dev libpulse-dev pulseaudio libsdl2-dev libnotify4

# configure dynamic links
sudo ldconfig

UPDATED_LIBPATH=$(sudo find / -name libnotify.so)
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$UPDATED_LIBPATH

install-deps-wxpython:
# Install wxPython wheels since they are distribution-specific and therefore not on PyPI
# See: https://wxpython.org/pages/downloads/index.html
pip install -U -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-22.04 wxPython
2 changes: 1 addition & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ def setup(app):
'backreferences_dir': 'generated', # Where to drop linking files between examples & API
'doc_module': ('eeg-notebooks',),
'reference_url': {'eeg-notebooksS': None},
'remove_conffig_comments': True,
'remove_config_comments': True,
}
"""
79 changes: 41 additions & 38 deletions eegnb/analysis/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
from copy import deepcopy
import math
import logging
import sys
from collections import OrderedDict
from glob import glob
from typing import Union, List, Dict
from collections import Iterable
from typing import Union, List
from time import sleep, time
from numpy.core.fromnumeric import std
import keyboard
import os

Expand All @@ -20,6 +19,8 @@
from mne.channels import make_standard_montage
from mne.filter import create_filter
from matplotlib import pyplot as plt
from matplotlib import lines as mlines
from scipy import stats
from scipy.signal import lfilter, lfilter_zi

from eegnb import _get_recording_dir
Expand Down Expand Up @@ -221,7 +222,8 @@ def plot_conditions(
ylim=(-6, 6),
diff_waveform=(1, 2),
channel_count=4,
channel_order=None):
channel_order=None,
):
"""Plot ERP conditions.
Args:
epochs (mne.epochs): EEG epochs
Expand All @@ -247,20 +249,19 @@ def plot_conditions(
"""

if channel_order:
channel_order = np.array(channel_order)
channel_order = np.array(channel_order)
else:
channel_order = np.array(range(channel_count))

channel_order = np.array(range(channel_count))
channel_names = np.array(epochs.ch_names)[channel_order]

if isinstance(conditions, dict):
conditions = OrderedDict(conditions)

if palette is None:
palette = sns.color_palette("hls", len(conditions) + 1)

X = epochs.get_data() * 1e6

X = X[:,channel_order]
dfX = epochs.to_data_frame()
dfX[channel_names] *= 1e6

times = epochs.times
y = pd.Series(epochs.events[:, -1])
Expand All @@ -275,43 +276,45 @@ def plot_conditions(
plot_axes.append(axes[axis_x, axis_y])
axes = plot_axes

for ch in range(channel_count):
for cond, color in zip(conditions.values(), palette):
sns.tsplot(
X[y.isin(cond), ch],
time=times,
for ch,ch_name in enumerate(channel_names):
for cond,cond_name, color in zip(conditions.values(),conditions.keys(), palette):
dfXc = dfX[dfX.condition.isin(conditions[cond_name])]
sns.lineplot(
data=dfXc,
x="time",
y=ch_name,
color=color,
n_boot=n_boot,
ci=ci,
ax=axes[ch],
errorbar=('ci',ci)
)
axes[ch].set(xlabel='Time (s)', ylabel='Amplitude (uV)', title=epochs.ch_names[channel_order[ch]])

if diff_waveform:
diff = np.nanmean(X[y == diff_waveform[1], ch], axis=0) - np.nanmean(
X[y == diff_waveform[0], ch], axis=0
)
dfXc1 = dfX[dfX.condition.isin(conditions[diff_waveform[1]])]
dfXc2 = dfX[dfX.condition.isin(conditions[diff_waveform[0]])]
dfXc1_mn = dfXc1.set_index(['time', 'epoch'])[ch_name].unstack('epoch').mean(axis=1)
dfXc2_mn = dfXc2.set_index(['time', 'epoch'])[ch_name].unstack('epoch').mean(axis=1)
diff = (dfXc1_mn - dfXc2_mn).values
axes[ch].plot(times, diff, color="k", lw=1)

axes[ch].set_title(epochs.ch_names[channel_order[ch]])
axes[ch].set_title(ch_name)
axes[ch].set_ylim(ylim)
axes[ch].axvline(
x=0, ymin=ylim[0], ymax=ylim[1], color="k", lw=1, label="_nolegend_"
)

axes[0].set_xlabel("Time (s)")
axes[0].set_ylabel("Amplitude (uV)")
axes[-1].set_xlabel("Time (s)")
axes[1].set_ylabel("Amplitude (uV)")

legs = []
for cond,cond_name,color in zip(conditions.values(),conditions.keys(), palette):
lh = mlines.Line2D([], [], color=color, marker='', ls='-', label=cond_name)
legs.append(lh)
if diff_waveform:
legend = ["{} - {}".format(diff_waveform[1], diff_waveform[0])] + list(
conditions.keys()
)
else:
legend = conditions.keys()
axes[-1].legend(
legend, bbox_to_anchor=(1.05, 1), loc="upper left", borderaxespad=0.0
)
lh = mlines.Line2D([], [], color="k", marker='', ls='-',
label = "{} - {}".format(diff_waveform[1], diff_waveform[0]))
legs.append(lh)

axes[-1].legend(handles=legs,
bbox_to_anchor=(1.05, 1), loc="upper left", borderaxespad=0.0)
sns.despine()
plt.tight_layout()

Expand All @@ -331,6 +334,9 @@ def plot_highlight_regions(
Args:
x (array_like): x coordinates
y (array_like): y values of same shape as `x`
Keyword Args:
hue (array_like): values to be plotted as hue based on `hue_thresh`.
Must be of the same shape as `x` and `y`.
Keyword Args:
hue (array_like): values to be plotted as hue based on `hue_thresh`.
Must be of the same shape as `x` and `y`.
Expand Down Expand Up @@ -456,12 +462,9 @@ def check_report(eeg: EEG, n_times: int=60, pause_time=5, thres_std_low=None, th
Usage:
------
from eegnb.devices.eeg import EEG
from eegnb.analysis.utils import check_report
eeg = EEG(device='museS')
check_report(eeg)
standard deviation for a quality recording.
The thres_std_low & thres_std_high values are the
lower and upper bound of accepted
thresholds = {
standard deviation for a quality recording.
thresholds = {
Expand Down
Loading

0 comments on commit 1257c3a

Please sign in to comment.