Skip to content

Commit 5ee48a5

Browse files
committed
refactor and run on all plugins
1 parent 3ff04fe commit 5ee48a5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+3422
-86
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ _site/
22
.jekyll-cache/
33
_config-claire.yml
44
Gemfile.lock
5+
code/plugins/github/

code/SIFT.py

-37
This file was deleted.

code/plugins/README_plugin.py

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import os
2+
import sys
3+
import shutil
4+
5+
# open a text file ending with .md and append a paragraph to it
6+
def reformat_plugin(dirpath, plugin_name):
7+
plugins_dir = '/Users/dtyoung/Documents/EEGLAB/sccn.github.io/plugins'
8+
index_file = os.path.join(plugins_dir, '{plugin_name}.md'.format(plugin_name=plugin_name))
9+
shutil.copyfile(os.path.join(dirpath, 'README.md'), index_file)
10+
with open(index_file) as f:
11+
text = f.read()
12+
append_text = '''---
13+
layout: default
14+
title: {plugin_name}
15+
long_title: {plugin_name}
16+
parent: Plugins
17+
---
18+
'''.format(plugin_name=plugin_name)
19+
text = append_text + text
20+
with open(index_file, 'w') as out:
21+
out.write(text)
22+
23+
24+
# main
25+
def main():
26+
if len(sys.argv) != 3:
27+
print('Usage: python README_plugin.py <plugin_dir_path> <plugin_name>')
28+
sys.exit(1)
29+
dirpath = sys.argv[1]
30+
plugin_name = sys.argv[2]
31+
reformat_plugin(dirpath, plugin_name)
32+
33+
if __name__ == "__main__":
34+
main()

code/plugins/Wiki_plugin.py

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import os
2+
import sys
3+
import shutil
4+
5+
# open a text file ending with .md and append a paragraph to it
6+
# Usage: python test.py <filename>.md
7+
def append_to_file(filepath, filename, parent, output_file):
8+
with open(filepath) as f:
9+
text = f.read()
10+
append_text = '''---
11+
layout: default
12+
title: {filename}
13+
long_title: {filename}
14+
parent: {parent}
15+
grand_parent: Plugins
16+
---
17+
'''.format(filename=filename, parent=parent)
18+
text = append_text + text
19+
with open(output_file, 'w') as out:
20+
out.write(text)
21+
22+
def reformat_plugin_dir(plugin_input_dir, plugin_name):
23+
# plugins_output_dir = '/Users/dtyoung/Documents/EEGLAB/sccn.github.io/plugins'
24+
plugin_output_dir = os.path.join('/Users/dtyoung/Documents/EEGLAB/sccn.github.io/plugins', plugin_name)
25+
if not os.path.exists(plugin_output_dir):
26+
os.makedirs(plugin_output_dir)
27+
# copy image directory from input to output dir
28+
if os.path.exists(os.path.join(plugin_input_dir, 'images')):
29+
shutil.copytree(os.path.join(plugin_input_dir, 'images'), os.path.join(plugin_output_dir, 'images'))
30+
31+
index_file = os.path.join(plugin_output_dir, 'index.md')
32+
shutil.copyfile(os.path.join(plugin_input_dir, 'Home.md'), index_file)
33+
with open(index_file) as f:
34+
text = f.read()
35+
append_text = '''---
36+
layout: default
37+
title: {plugin_name}
38+
long_title: {plugin_name}
39+
parent: Plugins
40+
categories: plugins
41+
has_children: true
42+
---
43+
'''.format(plugin_name=plugin_name)
44+
text = append_text + text
45+
with open(index_file, 'w') as out:
46+
out.write(text)
47+
48+
for root, dirs, files in os.walk(plugin_input_dir):
49+
for file in files:
50+
if file.endswith('.md') and not file.startswith('index') and not file.startswith('Home'):
51+
append_to_file(os.path.join(plugin_input_dir, file), file.strip('.md'), plugin_name, os.path.join(plugin_output_dir, file))
52+
53+
# main
54+
def main():
55+
if len(sys.argv) != 3:
56+
print('Usage: python test.py <plugin_dir_path> <plugin_name>')
57+
sys.exit(1)
58+
dirpath = sys.argv[1]
59+
plugin_name = sys.argv[2]
60+
reformat_plugin_dir(dirpath, plugin_name)
61+
62+
if __name__ == "__main__":
63+
main()

code/plugins/update_plugins.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import os
2+
import subprocess
3+
4+
def run_command(command):
5+
result = subprocess.run(command, shell=True, capture_output=False, text=True)
6+
if result.returncode != 0:
7+
print(f"Command failed: {command}\nError: {result.stderr}")
8+
return result
9+
10+
def update_repo(repo, script, type='readme'):
11+
current_dir = os.getcwd()
12+
repo_path = os.path.join(current_dir, 'github', repo)
13+
14+
if os.path.exists(repo_path):
15+
os.chdir(repo_path)
16+
run_command('git pull')
17+
else:
18+
if type == "wiki":
19+
run_command(f'git clone https://github.com/sccn/{repo}.wiki.git {repo_path}')
20+
else:
21+
run_command(f'git clone https://github.com/sccn/{repo}.git {repo_path}')
22+
23+
os.chdir(current_dir)
24+
run_command(f'python {script} {repo_path} {repo}')
25+
26+
if __name__ == "__main__":
27+
# if 'github' not in current directory, create it
28+
if not os.path.exists('github'):
29+
os.makedirs('github')
30+
31+
wiki_plugins = ['SIFT', 'get_chanlocs', 'NFT', 'PACT', 'nsgportal', 'clean_rawdata']
32+
for plugin in wiki_plugins:
33+
update_repo(plugin, "Wiki_plugin.py", 'wiki')
34+
readme_plugins = ['roiconnect', 'EEG-BIDS', 'trimOutlier', 'groupSIFT', 'nwbio', 'ICLabel', 'dipfit', 'eegstats', 'PowPowCAT', 'PACTools', 'zapline-plus', 'amica', 'fMRIb', 'relica', 'std_dipoleDensity', 'imat', 'viewprops', 'cleanline', 'ARfitStudio ', 'NIMA', 'firfilt']
35+
# for plugin in readme_plugins:
36+
# update_repo(plugin, "README_plugin.py")

code/plugins/update_plugins.sh

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
current_dir=$(pwd)
2+
3+
repo="SIFT"
4+
if cd ../../$repo; then git pull; else git clone https://github.com/sccn/$repo.wiki.git ../../$repo; fi
5+
python Wiki_plugin.py ../../$repo $repo
6+
cd $current_dir
7+
8+
repo="EEG-BIDS"
9+
if cd ../../$repo; then git pull; else git clone https://github.com/sccn/$repo.git ../../$repo; fi
10+
python README_plugin.py ../../$repo $repo
11+
cd $current_dir
12+
13+
repo="roiconnect"
14+
if cd ../../$repo; then git pull; else git clone https://github.com/sccn/$repo.git ../../$repo; fi
15+
python README_plugin.py ../../$repo $repo
16+
cd $current_dir

code/update_plugins.sh

-3
This file was deleted.

plugins/ARfitStudio.md

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
---
2+
layout: default
3+
title: ARfitStudio
4+
long_title: ARfitStudio
5+
parent: Plugins
6+
---
7+
What is ARfitStudio
8+
-------------------
9+
10+
First of all, ARfit is a collection of Matlab modules developed by Tapio
11+
Schneider and Arnold Neumaier for estimating parameters of multivariate
12+
autoregressive (AR) models, diagnostic checking of fitted AR models, and
13+
analyzing eigenmodes of fitted AR models. This plugin, ARfitStudio, uses
14+
it so that one can interactively clean TMS-induced short-burst (5-10 ms)
15+
artifacts and so on. The nature of the artifact does not matter (could
16+
be spikes by artifact, could be zeros, NaNs, etc), as long as the
17+
artifact duration is short and they are correctly marked. It has
18+
strength on the following points.
19+
20+
- To perform quick (e.g. smart epoching & integration to continuous
21+
data using mcolon(); before and after correction comparison with one
22+
click, etc) and intuitive (e.g. training, correction, and blending
23+
windows overlaid on grand-median ERPs) correction of spiky
24+
artifacts.
25+
- Immediately usable after importing the continuous data; it is the
26+
very first stage of the preprocess pipeline.
27+
28+
This plugin is dependent on ARfit and mcolon. ARfit can be installed by
29+
installing SIFT from EEGLAB plugin manager. To use mcolon(), users
30+
should compile the mex file.
31+
32+
<http://www.mathworks.com/matlabcentral/fileexchange/174-arfit>
33+
<http://www.mathworks.com/matlabcentral/fileexchange/29854-multiple-colon/content/mcolonFolder/mcolon.m>
34+
35+
Why ARfitStudio
36+
---------------
37+
38+
If there are spike artifacts with high amplitude, we cannot filter the
39+
data (because spikes spread in the time domain). In certain situation,
40+
it is more important to make them harmless for the sake of data process,
41+
rather than recovering the underlying signals (the latter is often
42+
impossible). This plugin provides a solution for it.
43+
44+
How it works
45+
------------
46+
47+
It learns autoregressive model from the training period (green), which
48+
is the past of the spike, to make a prediction to replace problematic
49+
spike data points (red). For smooth connection, one can also set
50+
optional blending period (light magenta) during which predicted signal
51+
and original signals are gradually mixed with a linear slope. Thus,
52+
*this plugin replaces the bad data points using the past information*
53+
(therefore temporal interpolation). It does NOT recover the signal under
54+
the noise. One can select multiple markers for correction. In the main
55+
plot, grand-median ERPs for all the channels are shown. To save the
56+
result, just save the EEG dataset (do not check 'restore the last data'
57+
for this!)
58+
59+
Screenshots
60+
-----------
61+
62+
![300px\|Figure 1. interpolateSpike:
63+
GUI](images/Screenshot3_interpolatespike.png)
64+
65+
![600px\|Figure 2. interpolateSpike: Before
66+
correction](images/Screenshot2_interpolatespike.png)
67+
68+
![600px\|Figure 3. interpolateSpike: After
69+
correction](images/Screenshot1_interpolatespike.png)
70+
71+
Data by courtesy of Michael Borich
72+
73+
If you want to use ICA for the final analysis
74+
---------------------------------------------
75+
76+
Because this ARfit-based interpolation is performed for the same time
77+
points across channels, ICA cannot decompose this period. As a result,
78+
you'll see strange spikes in IC activations (i.e., EEG.icaact) during
79+
the correction windows. In the plots below, I show 1) ERP of IC
80+
activation for 33ch with ARfitStudio applied on channels (left), 2) the
81+
same but ARfitStudio applied on ICs (right). The noise was originally
82+
present within -/+10 ms relative to latency zero.
83+
84+
![Onchannels.png](images/Onchannels.png)
85+
![Onics.png](images/Onics.png)
86+
87+
Performing ARfit-interpolation for ICA properly is complicated.
88+
89+
1. Import data.
90+
2. Perform ARfitStudio on channels.
91+
3. Preprocess the data (high-pass filter, CleanLine plugin,
92+
clean_rawdata plugin, average referencing, AMICA plugin, DIPFIT
93+
plugin, fitTwoDipoles plugin). Note that you need to exclude the
94+
interpolated data from ICA process (those have no multivariate
95+
property across channels, so not decomposable).
96+
4. Perform ARfitStudio on IC activations.
97+
5. Backproject interpolated IC activation to reconstruct channel data.
98+
99+
Note that the ARfitStudio is used twice, and the first application is
100+
purposed only for various filters, not even for ICA. In this way, you
101+
can obtain both clean channel signals and IC activations. For detail,
102+
see 'batchDemoForICA.m' included in the download package. I recommend
103+
you take a look because there are several cumbersome steps you have to
104+
go through.
105+
106+
Note for batch users (03/11/2019 updated)
107+
-----------------------------------------
108+
109+
If one wants to run the process as a batch,
110+
111+
1. Use EEGLAB function pop_epoch() to crop out peri-event data points.
112+
Note all the negative latencies will be used as a learning period
113+
for ARfit.
114+
2. Use arfit2interpolate(). Note that the last input
115+
'last_n_pointsToBlend' does NOT specify the additional length to
116+
the data points to the main interpolation window (as the GUI
117+
operation indicates), but it actually specifies the last data points
118+
of the main interpolation window. For example, outputData =
119+
arfit2interpolate(EEG.data, \[80 95\], 5) means, \[80:90\] for 100%
120+
interpolation, and \[91:95\] is blended with \[83% 67% 50% 33% 17%\]
121+
interpolated data blended with \[17% 33% 50% 67% 83%\] of the
122+
original data, respectively.
123+
3. Use putBackEpoch2Continuous() to re-construct the corrected, epoched
124+
data to the continuous data.
125+
126+
Reference
127+
---------
128+
129+
A. Neumaier and T. Schneider, 2001: Estimation of parameters and
130+
eigenmodes of multivariate autoregressive models. ACM Trans. Math.
131+
Softw., 27, 27-57.
132+
133+
T. Schneider and A. Neumaier, 2001: Algorithm 808: ARfit – A Matlab
134+
package for the estimation of parameters and eigenmodes of multivariate
135+
autoregressive models. ACM Trans. Math. Softw., 27, 58-65.
136+
137+
Authors: Makoto Miyakoshi and Tim Mullen. SCCN, INC, UCSD

0 commit comments

Comments
 (0)