diff --git a/pylossless/assets/ll_default_config_adults.yaml b/pylossless/assets/ll_default_config_adults.yaml new file mode 100644 index 0000000..e7d1023 --- /dev/null +++ b/pylossless/assets/ll_default_config_adults.yaml @@ -0,0 +1,144 @@ +######################### General properties ######################### +#ref_loc_file: derivatives/pylossless/code/misc/standard_1020_ll_ref19.elc +#montage_info: [0.0, -16.0, 0.0, -0.02, 0.0, -1.58, 10.7, 11.5, 11.5] + +################## General info about the project #################### +project: + readme: "# Description of the dataset" + + # Montage use to make file BIDS compliant. + # Can be path to digitized montage OR a string of one of mne's built in + # standard montages as specified by mne.channels.get_builtin_montages(). + # Can be left empty if the input dataset is already in BIDS format. + bids_montage: GSN-HydroCel-129 + + # montage used while running the lossless pipeline. + # if empty, the pipeline will use the electrodes.tsv sidecar file, if created + # during the BIDS conversion. + # If specified, needs to be a string of one of mne's built in standard montages. + analysis_montage: "" + + set_montage_kwargs: {} + + coordsys: + EEGCoordinateSystem: Other + EEGCoordinateUnits: metres + + general_info: + authors: [Unspecified] + institution_Name: Unspecified + institution_address: Unspecified + dataset_doi: [] + funding: Unspecified + how_to_acknowledge: tba + license: "" + name: Unspecified + references_and_links: [] + + t_info: + EEG_placement_scheme: EGI 129 + cap_manufacturer: EGI + cap_manufacturers_model_name: Hydrocel 129 Channel + hardware_filters: n/a + manufacturer: Electrical Geodesics + manufacturers_model_name: NetAmps300 + power_line_frequency: 60 + software_filters: n/a + software_versions: NetStation V4.5 + +######################## Task break detection ######################## +# See arguments definition from mne.preprocessing.annotate_breaks +find_breaks: + +############################## epoching ############################## +epoching: + overlap: 0 + + # See arguments definition from mne.Epochs + epochs_args: + baseline: null + tmax: 1 + tmin: 0 + +########################### EEG filtering ############################ +# See arguments definition from mne.io.Raw.filter & mne.io.Raw.notch_filter +filtering: + filter_args: + h_freq: 100 + l_freq: 1 + notch_filter_args: + freqs: [60] + +############################## SLURM ################################# +# Options for running the pipeline on a cluster +# through SLURM +slurm_options: + account: def-emayada + job_name: pylossless + memory: 16g + mpi: false + num_tasks: 1 + program_options: [] + threads_per_task: [] + time_limit: 2h + +########################## Nearest neighbor ########################## +nearest_neighbors: + n_nbr_ch: 3 + n_nbr_epoch: 3 + +####################### Pipeline steps config ######################## +bridged_channels: + bridge_trim: 40 + bridge_z: 6 + +noisy_channels: + flag_crit: 0.2 + outlier_method: quantile + outliers_kwargs: + k: 6 + lower: 0.25 + upper: 0.75 + +uncorrelated_channels: + flag_crit: 0.2 + outlier_method: quantile + outliers_kwargs: + k: 6 + lower: 0.25 + upper: 0.75 + +noisy_epochs: + flag_crit: 0.2 + outlier_method: quantile + outliers_kwargs: + k: 6 + lower: 0.25 + upper: 0.75 + +uncorrelated_epochs: + flag_crit: 0.2 + outlier_method: quantile + outliers_kwargs: + k: 6 + lower: 0.25 + upper: 0.75 + +################################ ICA ################################# +ica: + noisy_ic_epochs: + flag_crit: 0.2 + outlier_method: quantile + outliers_kwargs: + k: 6 + lower: 0.25 + upper: 0.75 + + # See arguments definition from mne.preprocessing.ICA + ica_args: + run1: + method: fastica + run2: + method: infomax + fit_params: + extended: True diff --git a/pylossless/assets/ll_default_config.yaml b/pylossless/assets/ll_default_config_infants.yaml similarity index 87% rename from pylossless/assets/ll_default_config.yaml rename to pylossless/assets/ll_default_config_infants.yaml index 588b8a2..5ddf540 100644 --- a/pylossless/assets/ll_default_config.yaml +++ b/pylossless/assets/ll_default_config_infants.yaml @@ -1,16 +1,10 @@ ######################### General properties ######################### -aref_trim: 30 -order: 1 -out_path: derivatives/EEG-IP-L -ref_loc_file: derivatives/EEG-IP-L/code/misc/standard_1020_ll_ref19.elc -save_f_res: 1 -sd_t_pad: 1 -in_path: [] -montage_info: [0.0, -16.0, 0.0, -0.02, 0.0, -1.58, 10.7, 11.5, 11.5] +#ref_loc_file: derivatives/pylossless/code/misc/standard_1020_ll_ref19.elc +#montage_info: [0.0, -16.0, 0.0, -0.02, 0.0, -1.58, 10.7, 11.5, 11.5] ################## General info about the project #################### project: - readme: "# Q1K ACAR Dataset" + readme: "# Description of the dataset" # Montage use to make file BIDS compliant. # Can be path to digitized montage OR a string of one of mne's built in @@ -31,14 +25,14 @@ project: EEGCoordinateUnits: metres general_info: - authors: [Q1K Neuroimaging group] - institution_Name: McGill University - institution_address: 3775 Rue University, Montreal, QC + authors: [Unspecified] + institution_Name: Unspecified + institution_address: Unspecified dataset_doi: [] - funding: Azrieli Foundationt + funding: Unspecified how_to_acknowledge: tba license: "" - name: Q1K Mismatched Negativity + name: Unspecified references_and_links: [] t_info: diff --git a/pylossless/config/config.py b/pylossless/config/config.py index d9f9e7f..64d6830 100644 --- a/pylossless/config/config.py +++ b/pylossless/config/config.py @@ -15,7 +15,7 @@ class ConfigMixin(dict): """Base configuration file class for pipeline procedures.""" DEFAULT_CONFIG_PATH = ( - Path(__file__).parent.parent / "assets" / "ll_default_config.yaml" + Path(__file__).parent.parent / "assets" ) def read(self, file_name): @@ -51,11 +51,16 @@ def print(self): class Config(ConfigMixin): """Representation of configuration file for running the pipeline.""" - DEFAULT_CONFIG_PATH = ( - Path(__file__).parent.parent / "assets" / "ll_default_config.yaml" - ) + def load_default(self, kind="adults"): + """Get the default pylossless config file. - def load_default(self): - """Get the default pylossless config file.""" - self.read(Config.DEFAULT_CONFIG_PATH) + Parameters + ---------- + kind : str | pathlib.Path + Can be either 'adults' or 'infants'. Default to 'adults'. + """ + path = Config.DEFAULT_CONFIG_PATH / f"ll_default_config_{kind}.yaml" + if not path.exists(): + raise ValueError(f"No default configuration for kind '{kind}'.") + self.read(path) return self diff --git a/pylossless/conftest.py b/pylossless/conftest.py index a6ae964..abcdce1 100644 --- a/pylossless/conftest.py +++ b/pylossless/conftest.py @@ -18,7 +18,7 @@ def pipeline_fixture(): """Return a namedTuple containing MNE eyetracking raw data and events.""" raw, config, bids_path = load_openneuro_bids() - raw.crop(tmin=0, tmax=60) # take 60 seconds for speed + # raw.crop(tmin=0, tmax=60) # Too short for ICA to converge in some tests. annots = Annotations( onset=[1, 15], duration=[1, 1], description=["test_annot", "test_annot"] ) @@ -28,6 +28,7 @@ def pipeline_fixture(): config["find_breaks"]["min_break_duration"] = 9 config["find_breaks"]["t_start_after_previous"] = 1 config["find_breaks"]["t_stop_before_next"] = 0 + config["ica"]["ica_args"]["run1"]["max_iter"] = 5000 config.save("test_config.yaml") pipeline = ll.LosslessPipeline("test_config.yaml") not_in_1020 = ["EXG1", "EXG2", "EXG3", "EXG4", "EXG5", "EXG6", "EXG7", "EXG8"] diff --git a/pylossless/datasets/datasets.py b/pylossless/datasets/datasets.py index cbf6423..abd6393 100644 --- a/pylossless/datasets/datasets.py +++ b/pylossless/datasets/datasets.py @@ -33,7 +33,10 @@ def load_openneuro_bids(subject="pd6"): openneuro = ll.utils.import_optional_dependency("openneuro") config = ll.config.Config() - config.load_default() + # TODO: Tests were developed using this configuration. The adult version + # should probably be used instead, but this change will require fixing the + # tests accordingly. + config.load_default("infants") config["project"]["bids_montage"] = "" config["project"]["analysis_montage"] = "standard_1020" config["project"]["set_montage_kwargs"]["on_missing"] = "warn" diff --git a/pylossless/tests/test_simulated.py b/pylossless/tests/test_simulated.py index 1c8b6a0..a17e13f 100644 --- a/pylossless/tests/test_simulated.py +++ b/pylossless/tests/test_simulated.py @@ -11,7 +11,7 @@ # LOAD DEFAULT CONFIG config = ll.config.Config() -config.load_default() +config.load_default("infants") config["noisy_channels"]["outliers_kwargs"]["lower"] = 0.25 config["noisy_channels"]["outliers_kwargs"]["upper"] = 0.75 # short file, raise threshold so epochs w/ blinks dont cause flag diff --git a/pylossless/tests/test_utils.py b/pylossless/tests/test_utils.py index 5eb2952..2ac0cd8 100644 --- a/pylossless/tests/test_utils.py +++ b/pylossless/tests/test_utils.py @@ -5,14 +5,18 @@ def test_import_optional_dependency(): from pylossless.utils import check # Test the case where the package is not installed. - with pytest.raises(ImportError, match="Missing optional dependency 'astropy'."): - # choosing a package that will probably never be added to the requirements! - check.import_optional_dependency("astropy") + package = "sdgssfersfsdesdfsefsdfsdt" + with pytest.raises(ImportError, match=f"Missing optional dependency '{package}'."): + # Choosing a package that will probably never be added to the requirements! + # We also choose a name of a package that is likely not to exist at all + # to avoid the corresponding package is installed in the development + # environment of developers. + check.import_optional_dependency(package) # Test the case where the package is installed. mne = check.import_optional_dependency("mne", raise_error=False) assert mne is not None # Test the where case package is not installed but we don't want to raise an error. - astropy = check.import_optional_dependency("astropy", raise_error=False) - assert astropy is None + ret_val = check.import_optional_dependency(package, raise_error=False) + assert ret_val is None