From d03ea9adde06bdcdfdcc96a322ea4751ff22cdbc Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Mon, 7 Aug 2023 09:38:52 -0400 Subject: [PATCH 01/81] Get required metrics from decision tree. --- tedana/selection/component_selector.py | 104 ++++++++++++------------- tedana/selection/tedica.py | 8 +- tedana/workflows/tedana.py | 24 ++---- 3 files changed, 59 insertions(+), 77 deletions(-) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 0e82db931..33c46e758 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -224,23 +224,13 @@ def validate_tree(tree): class ComponentSelector: """Load and classify components based on a specified ``tree``.""" - def __init__(self, tree, component_table, cross_component_metrics={}, status_table=None): + def __init__(self, tree): """Initialize the class using the info specified in the json file ``tree``. Parameters ---------- tree : :obj:`str` The named tree or path to a JSON file that defines one. - component_table : (C x M) :obj:`pandas.DataFrame` - Component metric table. One row for each component, with a column for - each metric; the index should be the component number. - cross_component_metrics : :obj:`dict` - Metrics that are each a single value calculated across components. - Default is empty dictionary. - status_table : :obj:`pandas.DataFrame` - A table tracking the status of each component at each step. - Pass a status table if running additional steps on a decision - tree that was already executed. Default=None. Notes ----- @@ -274,58 +264,37 @@ def __init__(self, tree, component_table, cross_component_metrics={}, status_tab """ self.tree_name = tree - - self.__dict__.update(cross_component_metrics) - self.cross_component_metrics = cross_component_metrics - - # Construct an un-executed selector - self.component_table = component_table.copy() - - # To run a decision tree, each component needs to have an initial classification - # If the classification column doesn't exist, create it and label all components - # as unclassified - if "classification" not in self.component_table: - self.component_table["classification"] = "unclassified" - self.tree = load_config(self.tree_name) - tree_config = self.tree - - LGR.info("Performing component selection with " + tree_config["tree_id"]) - LGR.info(tree_config.get("info", "")) - RepLGR.info(tree_config.get("report", "")) - RefLGR.info(tree_config.get("refs", "")) - - self.tree["nodes"] = tree_config["nodes"] - self.necessary_metrics = set(tree_config["necessary_metrics"]) - self.intermediate_classifications = tree_config["intermediate_classifications"] - self.classification_tags = set(tree_config["classification_tags"]) - if "used_metrics" not in self.tree.keys(): - self.tree["used_metrics"] = set() - else: - self.tree["used_metrics"] = set(self.tree["used_metrics"]) - if status_table is None: - self.component_status_table = self.component_table[ - ["Component", "classification"] - ].copy() - self.component_status_table = self.component_status_table.rename( - columns={"classification": "initialized classification"} - ) - self.start_idx = 0 - else: - # Since a status table exists, we need to skip nodes up to the - # point where the last tree finished - self.start_idx = len(tree_config["nodes"]) - LGR.info(f"Start is {self.start_idx}") - self.component_status_table = status_table + LGR.info("Performing component selection with " + self.tree["tree_id"]) + LGR.info(self.tree.get("info", "")) + RepLGR.info(self.tree.get("report", "")) + RefLGR.info(self.tree.get("refs", "")) - def select(self): + self.necessary_metrics = set(self.tree["necessary_metrics"]) + self.classification_tags = set(self.tree["classification_tags"]) + self.tree["used_metrics"] = set(self.tree.get("used_metrics", [])) + + def select(self, component_table, cross_component_metrics={}, status_table=None): """Apply the decision tree to data. Using the validated tree in ``ComponentSelector`` to run the decision tree functions to calculate cross_component metrics and classify each component as accepted or rejected. + Parameters + ---------- + component_table : (C x M) :obj:`pandas.DataFrame` + Component metric table. One row for each component, with a column for + each metric; the index should be the component number. + cross_component_metrics : :obj:`dict` + Metrics that are each a single value calculated across components. + Default is empty dictionary. + status_table : :obj:`pandas.DataFrame` + A table tracking the status of each component at each step. + Pass a status table if running additional steps on a decision + tree that was already executed. Default=None. + Notes ------- The selection process uses previously calculated parameters stored in @@ -353,6 +322,33 @@ def select(self): - current_node_idx: The total number of nodes run in ``ComponentSelector`` """ + self.__dict__.update(cross_component_metrics) + self.cross_component_metrics = cross_component_metrics + + # Construct an un-executed selector + self.component_table = component_table.copy() + + # To run a decision tree, each component needs to have an initial classification + # If the classification column doesn't exist, create it and label all components + # as unclassified + if "classification" not in self.component_table: + self.component_table["classification"] = "unclassified" + + if status_table is None: + self.component_status_table = self.component_table[ + ["Component", "classification"] + ].copy() + self.component_status_table = self.component_status_table.rename( + columns={"classification": "initialized classification"} + ) + self.start_idx = 0 + else: + # Since a status table exists, we need to skip nodes up to the + # point where the last tree finished + self.start_idx = len(self.tree["nodes"]) + LGR.info(f"Start is {self.start_idx}") + self.component_status_table = status_table + if "classification_tags" not in self.component_table.columns: self.component_table["classification_tags"] = "" diff --git a/tedana/selection/tedica.py b/tedana/selection/tedica.py index 682bb5c61..52f1fa985 100644 --- a/tedana/selection/tedica.py +++ b/tedana/selection/tedica.py @@ -4,13 +4,12 @@ import logging from tedana.metrics import collect -from tedana.selection.component_selector import ComponentSelector LGR = logging.getLogger("GENERAL") RepLGR = logging.getLogger("REPORT") -def automatic_selection(component_table, n_echos, n_vols, tree="kundu", verbose=False): +def automatic_selection(component_table, n_echos, n_vols, selector): """Classify components based on component table and decision tree type. Parameters @@ -21,8 +20,6 @@ def automatic_selection(component_table, n_echos, n_vols, tree="kundu", verbose= The number of echoes in this dataset tree : :obj:`str` The type of tree to use for the ComponentSelector object. Default="kundu" - verbose : :obj:`bool` - More verbose logging output if True. Default=False Returns ------- @@ -69,8 +66,7 @@ def automatic_selection(component_table, n_echos, n_vols, tree="kundu", verbose= "n_echos": n_echos, "n_vols": n_vols, } - selector = ComponentSelector(tree, component_table, cross_component_metrics=xcomp) - selector.select() + selector.select(component_table, cross_component_metrics=xcomp) selector.metadata = collect.get_metadata(selector.component_table) return selector diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 4d443033d..9a4fcb795 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -29,6 +29,7 @@ utils, ) from tedana.bibtex import get_description_references +from tedana.selection.component_selector import ComponentSelector from tedana.stats import computefeats2 from tedana.workflows.parser_utils import check_tedpca_value, is_valid_file @@ -652,19 +653,8 @@ def tedana_workflow( # generated from dimensionally reduced data using full data (i.e., data # with thermal noise) LGR.info("Making second component selection guess from ICA results") - required_metrics = [ - "kappa", - "rho", - "countnoise", - "countsigFT2", - "countsigFS0", - "dice_FT2", - "dice_FS0", - "signal-noise_t", - "variance explained", - "normalized variance explained", - "d_table_score", - ] + ica_selector = ComponentSelector(tree) + necessary_metrics = ica_selector.necessary_metrics comptable = metrics.collect.generate_metrics( catd, data_oc, @@ -673,9 +663,9 @@ def tedana_workflow( tes, io_generator, "ICA", - metrics=required_metrics, + metrics=necessary_metrics, ) - ica_selector = selection.automatic_selection(comptable, n_echos, n_vols, tree=tree) + ica_selector = selection.automatic_selection(comptable, n_echos, n_vols, ica_selector) n_likely_bold_comps = ica_selector.n_likely_bold_comps if (n_restarts < maxrestart) and (n_likely_bold_comps == 0): LGR.warning("No BOLD components found. Re-attempting ICA.") @@ -696,7 +686,7 @@ def tedana_workflow( mixing_file = io_generator.get_name("ICA mixing tsv") mmix = pd.read_table(mixing_file).values - required_metrics = [ + necessary_metrics = [ "kappa", "rho", "countnoise", @@ -717,7 +707,7 @@ def tedana_workflow( tes, io_generator, "ICA", - metrics=required_metrics, + metrics=necessary_metrics, ) ica_selector = selection.automatic_selection( comptable, From c7577c702a256ffb9756b1c8b6cb052b80f5e223 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Mon, 7 Aug 2023 09:43:38 -0400 Subject: [PATCH 02/81] Continue changes. --- tedana/workflows/tedana.py | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 9a4fcb795..750f44345 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -686,19 +686,8 @@ def tedana_workflow( mixing_file = io_generator.get_name("ICA mixing tsv") mmix = pd.read_table(mixing_file).values - necessary_metrics = [ - "kappa", - "rho", - "countnoise", - "countsigFT2", - "countsigFS0", - "dice_FT2", - "dice_FS0", - "signal-noise_t", - "variance explained", - "normalized variance explained", - "d_table_score", - ] + ica_selector = ComponentSelector(tree) + necessary_metrics = ica_selector.necessary_metrics comptable = metrics.collect.generate_metrics( catd, data_oc, @@ -709,12 +698,7 @@ def tedana_workflow( "ICA", metrics=necessary_metrics, ) - ica_selector = selection.automatic_selection( - comptable, - n_echos, - n_vols, - tree=tree, - ) + ica_selector = selection.automatic_selection(comptable, n_echos, n_vols, ica_selector) # TODO The ICA mixing matrix should be written out after it is created # It is currently being writen after component selection is done From b866c1da021196d7c616ac81d5b1ab2606a664a8 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Mon, 7 Aug 2023 10:29:05 -0400 Subject: [PATCH 03/81] More updates. --- tedana/selection/component_selector.py | 112 ++++++++++++------------- tedana/selection/tedica.py | 10 +-- tedana/workflows/tedana.py | 14 +++- 3 files changed, 71 insertions(+), 65 deletions(-) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 33c46e758..617c5941c 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -236,31 +236,6 @@ def __init__(self, tree): ----- Initializing the ``ComponentSelector`` confirms tree is valid and loads all information in the tree json file into ``ComponentSelector``. - - Adds to the ``ComponentSelector``: - - - component_status_table: empty dataframe or contents of inputted status_table - - cross_component_metrics: empty dict or contents of inputed values - - used_metrics: empty set - - Any parameter that is used by a decision tree node function can be passed - as a parameter in the ``ComponentSelector`` initialization or can be - included in the json file that defines the decision tree. - If a parameter is set in the json file, that will take precedence. - As a style rule, a parameter that is the same regardless of the inputted data should be - defined in the decision tree json file. - A parameter that is dataset-specific should be passed through the initialization function. - Dataset-specific parameters that may need to be passed during initialization include: - - n_echos : :obj:`int` - Number of echos in multi-echo fMRI data. - Required for kundu and minimal trees - n_vols : :obj:`int` - Number of volumes (time points) in the fMRI data - Required for kundu tree - - An example initialization with these options would look like - ``selector = ComponentSelector(tree, comptable, n_echos=n_echos, n_vols=n_vols)`` """ self.tree_name = tree @@ -297,6 +272,31 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) Notes ------- + Adds to the ``ComponentSelector``: + + - component_status_table: empty dataframe or contents of inputted status_table + - cross_component_metrics: empty dict or contents of inputed values + - used_metrics: empty set + + Any parameter that is used by a decision tree node function can be passed + as a parameter in the ``ComponentSelector`` initialization or can be + included in the json file that defines the decision tree. + If a parameter is set in the json file, that will take precedence. + As a style rule, a parameter that is the same regardless of the inputted data should be + defined in the decision tree json file. + A parameter that is dataset-specific should be passed through the initialization function. + Dataset-specific parameters that may need to be passed during initialization include: + + n_echos : :obj:`int` + Number of echos in multi-echo fMRI data. + Required for kundu and minimal trees + n_vols : :obj:`int` + Number of volumes (time points) in the fMRI data + Required for kundu tree + + An example initialization with these options would look like + ``selector = selector.select(comptable, n_echos=n_echos, n_vols=n_vols)`` + The selection process uses previously calculated parameters stored in `component_table` for each ICA component such as Kappa (a T2* weighting metric), Rho (an S0 weighting metric), and variance explained. If a necessary metric @@ -323,44 +323,44 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) """ self.__dict__.update(cross_component_metrics) - self.cross_component_metrics = cross_component_metrics + self.cross_component_metrics_ = cross_component_metrics # Construct an un-executed selector - self.component_table = component_table.copy() + self.component_table_ = component_table.copy() # To run a decision tree, each component needs to have an initial classification # If the classification column doesn't exist, create it and label all components # as unclassified - if "classification" not in self.component_table: - self.component_table["classification"] = "unclassified" + if "classification" not in self.component_table_: + self.component_table_["classification"] = "unclassified" if status_table is None: - self.component_status_table = self.component_table[ + self.component_status_table_ = self.component_table_[ ["Component", "classification"] ].copy() - self.component_status_table = self.component_status_table.rename( + self.component_status_table_ = self.component_status_table_.rename( columns={"classification": "initialized classification"} ) - self.start_idx = 0 + self.start_idx_ = 0 else: # Since a status table exists, we need to skip nodes up to the # point where the last tree finished - self.start_idx = len(self.tree["nodes"]) - LGR.info(f"Start is {self.start_idx}") - self.component_status_table = status_table + self.start_idx_ = len(self.tree["nodes"]) + LGR.info(f"Start is {self.start_idx_}") + self.component_status_table_ = status_table - if "classification_tags" not in self.component_table.columns: - self.component_table["classification_tags"] = "" + if "classification_tags" not in self.component_table_.columns: + self.component_table_["classification_tags"] = "" # this will crash the program with an error message if not all # necessary_metrics are in the comptable confirm_metrics_exist( - self.component_table, self.necessary_metrics, function_name=self.tree_name + self.component_table_, self.necessary_metrics, function_name=self.tree_name ) # for each node in the decision tree for self.current_node_idx, node in enumerate( - self.tree["nodes"][self.start_idx :], start=self.start_idx + self.tree["nodes"][self.start_idx_ :], start=self.start_idx_ ): # parse the variables to use with the function fcn = getattr(selection_nodes, node["functionname"]) @@ -392,14 +392,14 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) ) # log the current counts for all classification labels - log_classification_counts(self.current_node_idx, self.component_table) + log_classification_counts(self.current_node_idx, self.component_table_) LGR.debug( f"Step {self.current_node_idx} Full outputs: " f"{self.tree['nodes'][self.current_node_idx]['outputs']}" ) # move decision columns to end - self.component_table = clean_dataframe(self.component_table) + self.component_table_ = clean_dataframe(self.component_table_) # warning anything called a necessary metric wasn't used and if # anything not called a necessary metric was used self.are_only_necessary_metrics_used() @@ -483,11 +483,11 @@ def are_all_components_accepted_or_rejected(self): classifications are either "accepted" or "rejected". If any other component classifications remain, log a warning """ - component_classifications = set(self.component_table["classification"].to_list()) + component_classifications = set(self.component_table_["classification"].to_list()) nonfinal_classifications = component_classifications.difference({"accepted", "rejected"}) if nonfinal_classifications: for nonfinal_class in nonfinal_classifications: - numcomp = asarray(self.component_table["classification"] == nonfinal_class).sum() + numcomp = asarray(self.component_table_["classification"] == nonfinal_class).sum() LGR.warning( f"{numcomp} components have a final classification of {nonfinal_class}. " "At the end of the selection process, all components are expected " @@ -495,14 +495,14 @@ def are_all_components_accepted_or_rejected(self): ) @property - def n_comps(self): + def n_comps_(self): """The number of components in the component table.""" - return len(self.component_table) + return len(self.component_table_) @property - def likely_bold_comps(self): + def likely_bold_comps_(self): """A boolean :obj:`pandas.Series` of components that are tagged "Likely BOLD".""" - likely_bold_comps = self.component_table["classification_tags"].copy() + likely_bold_comps = self.component_table_["classification_tags"].copy() for idx in range(len(likely_bold_comps)): if "Likely BOLD" in likely_bold_comps.loc[idx]: likely_bold_comps.loc[idx] = True @@ -511,24 +511,24 @@ def likely_bold_comps(self): return likely_bold_comps @property - def n_likely_bold_comps(self): + def n_likely_bold_comps_(self): """The number of components that are tagged "Likely BOLD".""" - return self.likely_bold_comps.sum() + return self.likely_bold_comps_.sum() @property - def accepted_comps(self): + def accepted_comps_(self): """A boolean :obj:`pandas.Series` of components that are accepted.""" - return self.component_table["classification"] == "accepted" + return self.component_table_["classification"] == "accepted" @property def n_accepted_comps(self): """The number of components that are accepted.""" - return self.accepted_comps.sum() + return self.accepted_comps_.sum() @property def rejected_comps(self): """A boolean :obj:`pandas.Series` of components that are rejected.""" - return self.component_table["classification"] == "rejected" + return self.component_table_["classification"] == "rejected" def to_files(self, io_generator): """Convert this selector into component files. @@ -538,10 +538,10 @@ def to_files(self, io_generator): io_generator : :obj:`tedana.io.OutputGenerator` The output generator to use for filename generation and saving. """ - io_generator.save_file(self.component_table, "ICA metrics tsv") + io_generator.save_file(self.component_table_, "ICA metrics tsv") io_generator.save_file( - self.cross_component_metrics, + self.cross_component_metrics_, "ICA cross component metrics json", ) - io_generator.save_file(self.component_status_table, "ICA status table tsv") + io_generator.save_file(self.component_status_table_, "ICA status table tsv") io_generator.save_file(self.tree, "ICA decision tree json") diff --git a/tedana/selection/tedica.py b/tedana/selection/tedica.py index 52f1fa985..34b402b55 100644 --- a/tedana/selection/tedica.py +++ b/tedana/selection/tedica.py @@ -9,7 +9,7 @@ RepLGR = logging.getLogger("REPORT") -def automatic_selection(component_table, n_echos, n_vols, selector): +def automatic_selection(component_table, selector, **kwargs): """Classify components based on component table and decision tree type. Parameters @@ -62,11 +62,7 @@ def automatic_selection(component_table, n_echos, n_vols, selector): ) component_table["classification_tags"] = "" - xcomp = { - "n_echos": n_echos, - "n_vols": n_vols, - } - selector.select(component_table, cross_component_metrics=xcomp) - selector.metadata = collect.get_metadata(selector.component_table) + selector.select(component_table, cross_component_metrics=kwargs) + selector.metadata_ = collect.get_metadata(selector.component_table) return selector diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 750f44345..6ebc8624d 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -665,7 +665,12 @@ def tedana_workflow( "ICA", metrics=necessary_metrics, ) - ica_selector = selection.automatic_selection(comptable, n_echos, n_vols, ica_selector) + ica_selector = selection.automatic_selection( + comptable, + ica_selector, + n_echos=n_echos, + n_vols=n_vols, + ) n_likely_bold_comps = ica_selector.n_likely_bold_comps if (n_restarts < maxrestart) and (n_likely_bold_comps == 0): LGR.warning("No BOLD components found. Re-attempting ICA.") @@ -698,7 +703,12 @@ def tedana_workflow( "ICA", metrics=necessary_metrics, ) - ica_selector = selection.automatic_selection(comptable, n_echos, n_vols, ica_selector) + ica_selector = selection.automatic_selection( + comptable, + ica_selector, + n_echos=n_echos, + n_vols=n_vols, + ) # TODO The ICA mixing matrix should be written out after it is created # It is currently being writen after component selection is done From a1cc4011a078b5751ced1d4f9022496b58c555ea Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Fri, 11 Aug 2023 14:25:55 -0400 Subject: [PATCH 04/81] Store necessary_metrics as a list. --- tedana/selection/component_selector.py | 11 +++---- tedana/selection/selection_utils.py | 40 +++++++++----------------- tedana/workflows/tedana.py | 6 ++-- 3 files changed, 21 insertions(+), 36 deletions(-) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 617c5941c..03d3c33ac 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -246,7 +246,7 @@ def __init__(self, tree): RepLGR.info(self.tree.get("report", "")) RefLGR.info(self.tree.get("refs", "")) - self.necessary_metrics = set(self.tree["necessary_metrics"]) + self.necessary_metrics = self.tree["necessary_metrics"] self.classification_tags = set(self.tree["classification_tags"]) self.tree["used_metrics"] = set(self.tree.get("used_metrics", [])) @@ -355,7 +355,9 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) # this will crash the program with an error message if not all # necessary_metrics are in the comptable confirm_metrics_exist( - self.component_table_, self.necessary_metrics, function_name=self.tree_name + self.component_table_, + self.necessary_metrics, + function_name=self.tree_name, ) # for each node in the decision tree @@ -463,9 +465,8 @@ def are_only_necessary_metrics_used(self): used and if any used_metrics weren't explicitly declared necessary. If either of these happen, a warning is added to the logger """ - necessary_metrics = self.necessary_metrics - not_declared = self.tree["used_metrics"] - necessary_metrics - not_used = necessary_metrics - self.tree["used_metrics"] + not_declared = self.tree["used_metrics"] - set(self.necessary_metrics) + not_used = set(self.necessary_metrics) - self.tree["used_metrics"] if len(not_declared) > 0: LGR.warning( f"Decision tree {self.tree_name} used the following metrics that were " diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index 52099d26b..b68da70e8 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -320,51 +320,37 @@ def clean_dataframe(component_table): def confirm_metrics_exist(component_table, necessary_metrics, function_name=None): - """ - Confirm that all metrics declared in necessary_metrics are - already included in comptable. + """Confirm that all metrics declared in necessary_metrics are already included in comptable. Parameters ---------- component_table : (C x M) :obj:`pandas.DataFrame` Component metric table. One row for each component, with a column for each metric. The index should be the component number. - necessary_metrics : :obj:`set` - A set of strings of metric names + necessary_metrics : :obj:`list` + A list of strings of metric names. function_name : :obj:`str` - Text identifying the function name that called this function - - Returns - ------- - metrics_exist : :obj:`bool` - True if all metrics in necessary_metrics are in component_table + Text identifying the function name that called this function. Raises ------ ValueError - If metrics_exist is False then raise an error and end the program + If ``metrics_exist`` is False then raise an error and end the program. Note ----- - This doesn't check if there are data in each metric's column, just that - the columns exist. Also, the string in `necessary_metrics` and the - column labels in component_table will only be matched if they're identical. + This doesn't check if there are data in each metric's column, just that the columns exist. + Also, the string in ``necessary_metrics`` and the column labels in ``component_table`` will + only be matched if they're identical. """ - missing_metrics = necessary_metrics - set(component_table.columns) - metrics_exist = len(missing_metrics) > 0 - if metrics_exist is True: - if function_name is None: - function_name = "unknown function" - - error_msg = ( - f"Necessary metrics for {function_name}: " - f"{necessary_metrics}. " + missing_metrics = set(necessary_metrics) - set(component_table.columns) + if missing_metrics: + function_name = function_name or "unknown function" + raise ValueError( + f"Necessary metrics for {function_name}: {necessary_metrics}. " f"Comptable metrics: {set(component_table.columns)}. " f"MISSING METRICS: {missing_metrics}." ) - raise ValueError(error_msg) - - return metrics_exist def log_decision_tree_step( diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 6ebc8624d..2f0f6d3f5 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -654,7 +654,6 @@ def tedana_workflow( # with thermal noise) LGR.info("Making second component selection guess from ICA results") ica_selector = ComponentSelector(tree) - necessary_metrics = ica_selector.necessary_metrics comptable = metrics.collect.generate_metrics( catd, data_oc, @@ -663,7 +662,7 @@ def tedana_workflow( tes, io_generator, "ICA", - metrics=necessary_metrics, + metrics=ica_selector.necessary_metrics, ) ica_selector = selection.automatic_selection( comptable, @@ -692,7 +691,6 @@ def tedana_workflow( mmix = pd.read_table(mixing_file).values ica_selector = ComponentSelector(tree) - necessary_metrics = ica_selector.necessary_metrics comptable = metrics.collect.generate_metrics( catd, data_oc, @@ -701,7 +699,7 @@ def tedana_workflow( tes, io_generator, "ICA", - metrics=necessary_metrics, + metrics=ica_selector.necessary_metrics, ) ica_selector = selection.automatic_selection( comptable, From adb83a9c8138aa88fd6bb0390012203acbd0029b Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Fri, 11 Aug 2023 14:38:51 -0400 Subject: [PATCH 05/81] Update selection_nodes.py --- tedana/selection/selection_nodes.py | 182 ++++++++++++++-------------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/tedana/selection/selection_nodes.py b/tedana/selection/selection_nodes.py index 4a91c22a0..2c0e1a432 100644 --- a/tedana/selection/selection_nodes.py +++ b/tedana/selection/selection_nodes.py @@ -137,7 +137,7 @@ def manual_classify( ) if clear_classification_tags: - selector.component_table["classification_tags"] = "" + selector.component_table_["classification_tags"] = "" LGR.info(function_name_idx + " component classification tags are cleared") selector.tree["nodes"][selector.current_node_idx]["outputs"] = outputs @@ -259,11 +259,11 @@ def identify_used_metric(val, isnum=False): """ orig_val = val if isinstance(val, str): - if val in selector.component_table.columns: + if val in selector.component_table_.columns: outputs["used_metrics"].update([val]) - elif val in selector.cross_component_metrics: + elif val in selector.cross_component_metrics_: outputs["used_cross_component_metrics"].update([val]) - val = selector.cross_component_metrics[val] + val = selector.cross_component_metrics_[val] # If decision tree is being run, then throw errors or messages # if a component doesn't exist. If this is just getting a list # of metrics to be used, then don't bring up warnings @@ -271,14 +271,14 @@ def identify_used_metric(val, isnum=False): if not comps2use: LGR.info( f"{function_name_idx}: {val} is neither a metric in " - "selector.component_table nor selector.cross_component_metrics, " + "selector.component_table_ nor selector.cross_component_metrics_, " f"but no components with {decide_comps} remain by this node " "so nothing happens" ) else: raise ValueError( - f"{val} is neither a metric in selector.component_table " - "nor selector.cross_component_metrics" + f"{val} is neither a metric in selector.component_table_ " + "nor selector.cross_component_metrics_" ) if isnum: if not isinstance(val, (int, float)): @@ -395,13 +395,13 @@ def operator_scale_descript(val_scale, val): RepLGR.info(log_extra_report) confirm_metrics_exist( - selector.component_table, outputs["used_metrics"], function_name=function_name_idx + selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) def parse_vals(val): """Get the metric values for the selected components or relevant constant""" if isinstance(val, str): - return selector.component_table.loc[comps2use, val].copy() + return selector.component_table_.loc[comps2use, val].copy() else: return val # should be a fixed number @@ -544,7 +544,7 @@ def dec_variance_lessthan_thresholds( comps2use = selectcomps2use(selector, decide_comps) confirm_metrics_exist( - selector.component_table, outputs["used_metrics"], function_name=function_name_idx + selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) if not comps2use: @@ -558,7 +558,7 @@ def dec_variance_lessthan_thresholds( if_false=outputs["n_false"], ) else: - variance = selector.component_table.loc[comps2use, var_metric] + variance = selector.component_table_.loc[comps2use, var_metric] decision_boolean = variance < single_comp_threshold # if all the low variance components sum above all_comp_threshold # keep removing the highest remaining variance component until @@ -612,7 +612,7 @@ def calc_median( %(selector)s %(decide_comps)s metric_name: :obj:`str` - The name of a column in selector.component_table. The median of + The name of a column in selector.component_table_. The median of the values in this column will be calculated median_label: :obj:`str` The median will be saved in "median_(median_label)" @@ -650,7 +650,7 @@ def calc_median( if only_used_metrics: return outputs["used_metrics"] - if label_name in selector.cross_component_metrics: + if label_name in selector.cross_component_metrics_: LGR.warning( f"{label_name} already calculated. Overwriting previous value in {function_name_idx}" ) @@ -668,7 +668,7 @@ def calc_median( comps2use = selectcomps2use(selector, decide_comps) confirm_metrics_exist( - selector.component_table, outputs["used_metrics"], function_name=function_name_idx + selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) if not comps2use: @@ -678,9 +678,9 @@ def calc_median( decide_comps=decide_comps, ) else: - outputs[label_name] = np.median(selector.component_table.loc[comps2use, metric_name]) + outputs[label_name] = np.median(selector.component_table_.loc[comps2use, metric_name]) - selector.cross_component_metrics[label_name] = outputs[label_name] + selector.cross_component_metrics_[label_name] = outputs[label_name] log_decision_tree_step(function_name_idx, comps2use, calc_outputs=outputs) @@ -750,7 +750,7 @@ def calc_kappa_elbow( function_name_idx = f"Step {selector.current_node_idx}: calc_kappa_elbow" - if ("kappa_elbow_kundu" in selector.cross_component_metrics) and ( + if ("kappa_elbow_kundu" in selector.cross_component_metrics_) and ( "kappa_elbow_kundu" in outputs["calc_cross_comp_metrics"] ): LGR.warning( @@ -758,7 +758,7 @@ def calc_kappa_elbow( f"Overwriting previous value in {function_name_idx}" ) - if "varex_upper_p" in selector.cross_component_metrics: + if "varex_upper_p" in selector.cross_component_metrics_: LGR.warning( f"varex_upper_p already calculated. Overwriting previous value in {function_name_idx}" ) @@ -776,7 +776,7 @@ def calc_kappa_elbow( comps2use = selectcomps2use(selector, decide_comps) confirm_metrics_exist( - selector.component_table, outputs["used_metrics"], function_name=function_name_idx + selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) if not comps2use: @@ -791,11 +791,11 @@ def calc_kappa_elbow( outputs["kappa_allcomps_elbow"], outputs["kappa_nonsig_elbow"], outputs["varex_upper_p"], - ) = kappa_elbow_kundu(selector.component_table, selector.n_echos, comps2use=comps2use) - selector.cross_component_metrics["kappa_elbow_kundu"] = outputs["kappa_elbow_kundu"] - selector.cross_component_metrics["kappa_allcomps_elbow"] = outputs["kappa_allcomps_elbow"] - selector.cross_component_metrics["kappa_nonsig_elbow"] = outputs["kappa_nonsig_elbow"] - selector.cross_component_metrics["varex_upper_p"] = outputs["varex_upper_p"] + ) = kappa_elbow_kundu(selector.component_table_, selector.n_echos, comps2use=comps2use) + selector.cross_component_metrics_["kappa_elbow_kundu"] = outputs["kappa_elbow_kundu"] + selector.cross_component_metrics_["kappa_allcomps_elbow"] = outputs["kappa_allcomps_elbow"] + selector.cross_component_metrics_["kappa_nonsig_elbow"] = outputs["kappa_nonsig_elbow"] + selector.cross_component_metrics_["varex_upper_p"] = outputs["varex_upper_p"] log_decision_tree_step(function_name_idx, comps2use, calc_outputs=outputs) @@ -880,7 +880,7 @@ def calc_rho_elbow( if only_used_metrics: return outputs["used_metrics"] - if (elbow_name in selector.cross_component_metrics) and ( + if (elbow_name in selector.cross_component_metrics_) and ( elbow_name in outputs["calc_cross_comp_metrics"] ): LGR.warning( @@ -901,7 +901,7 @@ def calc_rho_elbow( comps2use = selectcomps2use(selector, decide_comps) confirm_metrics_exist( - selector.component_table, outputs["used_metrics"], function_name=function_name_idx + selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) subset_comps2use = selectcomps2use(selector, subset_decide_comps) @@ -919,18 +919,18 @@ def calc_rho_elbow( outputs["rho_unclassified_elbow"], outputs["elbow_f05"], ) = rho_elbow_kundu_liberal( - selector.component_table, + selector.component_table_, selector.n_echos, rho_elbow_type=rho_elbow_type, comps2use=comps2use, subset_comps2use=subset_comps2use, ) - selector.cross_component_metrics[elbow_name] = outputs[elbow_name] - selector.cross_component_metrics["rho_allcomps_elbow"] = outputs["rho_allcomps_elbow"] - selector.cross_component_metrics["rho_unclassified_elbow"] = outputs[ + selector.cross_component_metrics_[elbow_name] = outputs[elbow_name] + selector.cross_component_metrics_["rho_allcomps_elbow"] = outputs["rho_allcomps_elbow"] + selector.cross_component_metrics_["rho_unclassified_elbow"] = outputs[ "rho_unclassified_elbow" ] - selector.cross_component_metrics["elbow_f05"] = outputs["elbow_f05"] + selector.cross_component_metrics_["elbow_f05"] = outputs["elbow_f05"] log_decision_tree_step(function_name_idx, comps2use, calc_outputs=outputs) @@ -1148,11 +1148,11 @@ def dec_reclassify_high_var_comps( comps2use = selectcomps2use(selector, decide_comps) - if "varex_upper_p" not in selector.cross_component_metrics: + if "varex_upper_p" not in selector.cross_component_metrics_: if not comps2use: LGR.info( f"{function_name_idx}: varex_upper_p is not in " - "selector.cross_component_metrics, but no components with " + "selector.cross_component_metrics_, but no components with " f"{decide_comps} remain by this node so nothing happens" ) else: @@ -1175,13 +1175,13 @@ def dec_reclassify_high_var_comps( else: keep_comps2use = comps2use.copy() for i_loop in range(3): - temp_comptable = selector.component_table.loc[keep_comps2use].sort_values( + temp_comptable = selector.component_table_.loc[keep_comps2use].sort_values( by=["variance explained"], ascending=False ) diff_vals = temp_comptable["variance explained"].diff(-1) diff_vals = diff_vals.fillna(0) keep_comps2use = temp_comptable.loc[ - diff_vals < selector.cross_component_metrics["varex_upper_p"] + diff_vals < selector.cross_component_metrics_["varex_upper_p"] ].index.values # Everything that should be kept as unclassified is False while the few # that are not in keep_comps2use should be True @@ -1241,7 +1241,7 @@ def calc_varex_thresh( num_highest_var_comps: :obj:`str` :obj:`int` percentile can be calculated on the num_highest_var_comps components with the lowest variance. Either input an integer directly or input a string that is - a parameter stored in selector.cross_component_metrics ("num_acc_guess" in + a parameter stored in selector.cross_component_metrics_ ("num_acc_guess" in original decision tree). Default=None %(log_extra_info)s %(log_extra_report)s @@ -1284,25 +1284,25 @@ def calc_varex_thresh( if only_used_metrics: return outputs["used_metrics"] - if varex_name in selector.cross_component_metrics: + if varex_name in selector.cross_component_metrics_: LGR.warning( f"{varex_name} already calculated. Overwriting previous value in {function_name_idx}" ) - if perc_name in selector.cross_component_metrics: + if perc_name in selector.cross_component_metrics_: LGR.warning( f"{perc_name} already calculated. Overwriting previous value in {function_name_idx}" ) comps2use = selectcomps2use(selector, decide_comps) confirm_metrics_exist( - selector.component_table, outputs["used_metrics"], function_name=function_name_idx + selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) if num_highest_var_comps is not None: if isinstance(num_highest_var_comps, str): - if num_highest_var_comps in selector.cross_component_metrics: - num_highest_var_comps = selector.cross_component_metrics[num_highest_var_comps] + if num_highest_var_comps in selector.cross_component_metrics_: + num_highest_var_comps = selector.cross_component_metrics_[num_highest_var_comps] elif not comps2use: # Note: It is possible the comps2use requested for this function # is not empty, but the comps2use requested to calculate @@ -1310,13 +1310,13 @@ def calc_varex_thresh( # used, that's unlikely, but worth a comment. LGR.info( f"{function_name_idx}: num_highest_var_comps ( {num_highest_var_comps}) " - "is not in selector.cross_component_metrics, but no components with " + "is not in selector.cross_component_metrics_, but no components with " f"{decide_comps} remain by this node so nothing happens" ) else: raise ValueError( f"{function_name_idx}: num_highest_var_comps ( {num_highest_var_comps}) " - "is not in selector.cross_component_metrics" + "is not in selector.cross_component_metrics_" ) if not isinstance(num_highest_var_comps, int) and comps2use: raise ValueError( @@ -1344,7 +1344,7 @@ def calc_varex_thresh( else: if num_highest_var_comps is None: outputs[varex_name] = scoreatpercentile( - selector.component_table.loc[comps2use, "variance explained"], percentile_thresh + selector.component_table_.loc[comps2use, "variance explained"], percentile_thresh ) else: # Using only the first num_highest_var_comps components sorted to include @@ -1363,13 +1363,13 @@ def calc_varex_thresh( num_highest_var_comps = len(comps2use) sorted_varex = np.flip( - np.sort((selector.component_table.loc[comps2use, "variance explained"]).to_numpy()) + np.sort((selector.component_table_.loc[comps2use, "variance explained"]).to_numpy()) ) outputs[varex_name] = scoreatpercentile( sorted_varex[:num_highest_var_comps], percentile_thresh ) - selector.cross_component_metrics[varex_name] = outputs[varex_name] + selector.cross_component_metrics_[varex_name] = outputs[varex_name] log_decision_tree_step(function_name_idx, comps2use, calc_outputs=outputs) @@ -1423,7 +1423,7 @@ def calc_extend_factor( function_name_idx = f"Step {selector.current_node_idx}: calc_extend_factor" - if "extend_factor" in selector.cross_component_metrics: + if "extend_factor" in selector.cross_component_metrics_: LGR.warning( f"extend_factor already calculated. Overwriting previous value in {function_name_idx}" ) @@ -1439,10 +1439,10 @@ def calc_extend_factor( RepLGR.info(log_extra_report) outputs["extend_factor"] = get_extend_factor( - n_vols=selector.cross_component_metrics["n_vols"], extend_factor=extend_factor + n_vols=selector.cross_component_metrics_["n_vols"], extend_factor=extend_factor ) - selector.cross_component_metrics["extend_factor"] = outputs["extend_factor"] + selector.cross_component_metrics_["extend_factor"] = outputs["extend_factor"] log_decision_tree_step(function_name_idx, -1, calc_outputs=outputs) @@ -1512,7 +1512,7 @@ def calc_max_good_meanmetricrank( if only_used_metrics: return outputs["used_metrics"] - if metric_name in selector.cross_component_metrics: + if metric_name in selector.cross_component_metrics_: LGR.warning( "max_good_meanmetricrank already calculated." f"Overwriting previous value in {function_name_idx}" @@ -1530,7 +1530,7 @@ def calc_max_good_meanmetricrank( comps2use = selectcomps2use(selector, decide_comps) confirm_metrics_exist( - selector.component_table, outputs["used_metrics"], function_name=function_name_idx + selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) if not comps2use: @@ -1541,15 +1541,15 @@ def calc_max_good_meanmetricrank( ) else: num_prov_accept = len(comps2use) - if "extend_factor" in selector.cross_component_metrics: - extend_factor = selector.cross_component_metrics["extend_factor"] + if "extend_factor" in selector.cross_component_metrics_: + extend_factor = selector.cross_component_metrics_["extend_factor"] outputs[metric_name] = extend_factor * num_prov_accept else: raise ValueError( f"extend_factor needs to be in cross_component_metrics for {function_name_idx}" ) - selector.cross_component_metrics[metric_name] = outputs[metric_name] + selector.cross_component_metrics_[metric_name] = outputs[metric_name] log_decision_tree_step(function_name_idx, comps2use, calc_outputs=outputs) @@ -1611,12 +1611,12 @@ def calc_varex_kappa_ratio( if only_used_metrics: return outputs["used_metrics"] - if "kappa_rate" in selector.cross_component_metrics: + if "kappa_rate" in selector.cross_component_metrics_: LGR.warning( f"kappa_rate already calculated. Overwriting previous value in {function_name_idx}" ) - if "varex kappa ratio" in selector.component_table: + if "varex kappa ratio" in selector.component_table_: raise ValueError( "'varex kappa ratio' is already a column in the component_table." f"Recalculating in {function_name_idx} can cause problems since these " @@ -1635,7 +1635,7 @@ def calc_varex_kappa_ratio( comps2use = selectcomps2use(selector, decide_comps) confirm_metrics_exist( - selector.component_table, outputs["used_metrics"], function_name=function_name_idx + selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) if not comps2use: @@ -1646,11 +1646,11 @@ def calc_varex_kappa_ratio( ) else: kappa_rate = ( - np.nanmax(selector.component_table.loc[comps2use, "kappa"]) - - np.nanmin(selector.component_table.loc[comps2use, "kappa"]) + np.nanmax(selector.component_table_.loc[comps2use, "kappa"]) + - np.nanmin(selector.component_table_.loc[comps2use, "kappa"]) ) / ( - np.nanmax(selector.component_table.loc[comps2use, "variance explained"]) - - np.nanmin(selector.component_table.loc[comps2use, "variance explained"]) + np.nanmax(selector.component_table_.loc[comps2use, "variance explained"]) + - np.nanmin(selector.component_table_.loc[comps2use, "variance explained"]) ) outputs["kappa_rate"] = kappa_rate LGR.debug( @@ -1659,17 +1659,17 @@ def calc_varex_kappa_ratio( ) # NOTE: kappa_rate is calculated on a subset of components while # "varex kappa ratio" is calculated for all compnents - selector.component_table["varex kappa ratio"] = ( + selector.component_table_["varex kappa ratio"] = ( kappa_rate - * selector.component_table["variance explained"] - / selector.component_table["kappa"] + * selector.component_table_["variance explained"] + / selector.component_table_["kappa"] ) # Unclear if necessary, but this may clean up a weird issue on passing # references in a data frame. # See longer comment in selection_utils.comptable_classification_changer - selector.component_table = selector.component_table.copy() + selector.component_table_ = selector.component_table_.copy() - selector.cross_component_metrics["kappa_rate"] = outputs["kappa_rate"] + selector.cross_component_metrics_["kappa_rate"] = outputs["kappa_rate"] log_decision_tree_step(function_name_idx, comps2use, calc_outputs=outputs) @@ -1753,18 +1753,18 @@ def calc_revised_meanmetricrank_guesses( if only_used_metrics: return outputs["used_metrics"] - if "num_acc_guess" in selector.cross_component_metrics: + if "num_acc_guess" in selector.cross_component_metrics_: LGR.warning( f"num_acc_guess already calculated. Overwriting previous value in {function_name_idx}" ) - if "conservative_guess" in selector.cross_component_metrics: + if "conservative_guess" in selector.cross_component_metrics_: LGR.warning( "conservative_guess already calculated. " f"Overwriting previous value in {function_name_idx}" ) - if "restrict_factor" in selector.cross_component_metrics: + if "restrict_factor" in selector.cross_component_metrics_: LGR.warning( "restrict_factor already calculated. " f"Overwriting previous value in {function_name_idx}" @@ -1772,7 +1772,7 @@ def calc_revised_meanmetricrank_guesses( if not isinstance(restrict_factor, (int, float)): raise ValueError(f"restrict_factor needs to be a number. It is: {restrict_factor}") - if f"d_table_score_node{selector.current_node_idx}" in selector.component_table: + if f"d_table_score_node{selector.current_node_idx}" in selector.component_table_: raise ValueError( f"d_table_score_node{selector.current_node_idx} is already a column" f"in the component_table. Recalculating in {function_name_idx} can " @@ -1781,15 +1781,15 @@ def calc_revised_meanmetricrank_guesses( comps2use = selectcomps2use(selector, decide_comps) confirm_metrics_exist( - selector.component_table, outputs["used_metrics"], function_name=function_name_idx + selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) for xcompmetric in outputs["used_cross_component_metrics"]: - if xcompmetric not in selector.cross_component_metrics: + if xcompmetric not in selector.cross_component_metrics_: if not comps2use: LGR.info( f"{function_name_idx}: {xcompmetric} is not in " - "selector.cross_component_metrics, but no components with " + "selector.cross_component_metrics_, but no components with " f"{decide_comps} remain by this node so nothing happens" ) else: @@ -1811,7 +1811,7 @@ def calc_revised_meanmetricrank_guesses( comps2use = selectcomps2use(selector, decide_comps) confirm_metrics_exist( - selector.component_table, outputs["used_metrics"], function_name=function_name_idx + selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) if not comps2use: @@ -1827,43 +1827,43 @@ def calc_revised_meanmetricrank_guesses( [ np.sum( ( - selector.component_table.loc[comps2use, "kappa"] - > selector.cross_component_metrics["kappa_elbow_kundu"] + selector.component_table_.loc[comps2use, "kappa"] + > selector.cross_component_metrics_["kappa_elbow_kundu"] ) & ( - selector.component_table.loc[comps2use, "rho"] - < selector.cross_component_metrics["rho_elbow_kundu"] + selector.component_table_.loc[comps2use, "rho"] + < selector.cross_component_metrics_["rho_elbow_kundu"] ) ), np.sum( - selector.component_table.loc[comps2use, "kappa"] - > selector.cross_component_metrics["kappa_elbow_kundu"] + selector.component_table_.loc[comps2use, "kappa"] + > selector.cross_component_metrics_["kappa_elbow_kundu"] ), ] ) ) outputs["conservative_guess"] = outputs["num_acc_guess"] / outputs["restrict_factor"] - tmp_kappa = selector.component_table.loc[comps2use, "kappa"].to_numpy() - tmp_dice_FT2 = selector.component_table.loc[comps2use, "dice_FT2"].to_numpy() - tmp_signal_m_noise_t = selector.component_table.loc[comps2use, "signal-noise_t"].to_numpy() - tmp_countnoise = selector.component_table.loc[comps2use, "countnoise"].to_numpy() - tmp_countsigFT2 = selector.component_table.loc[comps2use, "countsigFT2"].to_numpy() + tmp_kappa = selector.component_table_.loc[comps2use, "kappa"].to_numpy() + tmp_dice_FT2 = selector.component_table_.loc[comps2use, "dice_FT2"].to_numpy() + tmp_signal_m_noise_t = selector.component_table_.loc[comps2use, "signal-noise_t"].to_numpy() + tmp_countnoise = selector.component_table_.loc[comps2use, "countnoise"].to_numpy() + tmp_countsigFT2 = selector.component_table_.loc[comps2use, "countsigFT2"].to_numpy() tmp_d_table_score = generate_decision_table_score( tmp_kappa, tmp_dice_FT2, tmp_signal_m_noise_t, tmp_countnoise, tmp_countsigFT2 ) - selector.component_table[f"d_table_score_node{selector.current_node_idx}"] = np.NaN - selector.component_table.loc[ + selector.component_table_[f"d_table_score_node{selector.current_node_idx}"] = np.NaN + selector.component_table_.loc[ comps2use, f"d_table_score_node{selector.current_node_idx}" ] = tmp_d_table_score # Unclear if necessary, but this may clean up a weird issue on passing # references in a data frame. # See longer comment in selection_utils.comptable_classification_changer - selector.component_table = selector.component_table.copy() + selector.component_table_ = selector.component_table_.copy() - selector.cross_component_metrics["conservative_guess"] = outputs["conservative_guess"] - selector.cross_component_metrics["num_acc_guess"] = outputs["num_acc_guess"] - selector.cross_component_metrics["restrict_factor"] = outputs["restrict_factor"] + selector.cross_component_metrics_["conservative_guess"] = outputs["conservative_guess"] + selector.cross_component_metrics_["num_acc_guess"] = outputs["num_acc_guess"] + selector.cross_component_metrics_["restrict_factor"] = outputs["restrict_factor"] log_decision_tree_step(function_name_idx, comps2use, calc_outputs=outputs) From 610ea5a16be0e198d64620298168acb5eadd1701 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Fri, 11 Aug 2023 14:45:38 -0400 Subject: [PATCH 06/81] Update selection_utils.py --- tedana/selection/selection_utils.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index b68da70e8..929d4f3a2 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -38,9 +38,9 @@ def selectcomps2use(selector, decide_comps): A list of component indices with classifications included in decide_comps """ - if "classification" not in selector.component_table: + if "classification" not in selector.component_table_: raise ValueError( - "selector.component_table needs a 'classification' column to run selectcomp2suse" + "selector.component_table_ needs a 'classification' column to run selectcomp2suse" ) if type(decide_comps) in [str, int]: @@ -48,22 +48,22 @@ def selectcomps2use(selector, decide_comps): if (type(decide_comps) is list) and (decide_comps[0] == "all"): # All components with any string in the classification field # are set to True - comps2use = list(range(selector.component_table.shape[0])) + comps2use = list(range(selector.component_table_.shape[0])) elif (type(decide_comps) is list) and all(isinstance(elem, str) for elem in decide_comps): comps2use = [] for didx in range(len(decide_comps)): - newcomps2use = selector.component_table.index[ - selector.component_table["classification"] == decide_comps[didx] + newcomps2use = selector.component_table_.index[ + selector.component_table_["classification"] == decide_comps[didx] ].tolist() comps2use = list(set(comps2use + newcomps2use)) elif (type(decide_comps) is list) and all(type(elem) is int for elem in decide_comps): # decide_comps is already a string of indices - if len(selector.component_table) <= max(decide_comps): + if len(selector.component_table_) <= max(decide_comps): raise ValueError( "decide_comps for selectcomps2use is selecting for a component with index" f"{max(decide_comps)} (0 indexing) which is greater than the number " - f"of components: {len(selector.component_table)}" + f"of components: {len(selector.component_table_)}" ) elif min(decide_comps) < 0: raise ValueError( @@ -159,9 +159,9 @@ def change_comptable_classifications( dont_warn_reclassify=dont_warn_reclassify, ) - selector.component_status_table[ + selector.component_status_table_[ f"Node {selector.current_node_idx}" - ] = selector.component_table["classification"] + ] = selector.component_table_["classification"] n_true = decision_boolean.sum() n_false = np.logical_not(decision_boolean).sum() @@ -236,7 +236,7 @@ def comptable_classification_changer( changeidx = decision_boolean.index[np.asarray(decision_boolean) == boolstate] if not changeidx.empty: current_classifications = set( - selector.component_table.loc[changeidx, "classification"].tolist() + selector.component_table_.loc[changeidx, "classification"].tolist() ) if current_classifications.intersection({"accepted", "rejected"}): if not dont_warn_reclassify: @@ -250,7 +250,7 @@ def comptable_classification_changer( " changing away from accepted or rejected. Once a component is " "accepted or rejected, it shouldn't be reclassified" ) - selector.component_table.loc[changeidx, "classification"] = classify_if + selector.component_table_.loc[changeidx, "classification"] = classify_if # NOTE: CAUTION: extremely bizarre pandas behavior violates guarantee # that df['COLUMN'] matches the df as a a whole in this case. # We cannot replicate this consistently, but it seems to happen in some @@ -263,17 +263,17 @@ def comptable_classification_changer( # Comment line below to re-introduce original bug. For the kundu decision # tree it happens on node 6 which is the first time decide_comps is for # a subset of components - selector.component_table = selector.component_table.copy() + selector.component_table_ = selector.component_table_.copy() if tag_if is not None: # only run if a tag is provided for idx in changeidx: - tmpstr = selector.component_table.loc[idx, "classification_tags"] + tmpstr = selector.component_table_.loc[idx, "classification_tags"] if tmpstr == "" or isinstance(tmpstr, float): tmpset = set([tag_if]) else: tmpset = set(tmpstr.split(",")) tmpset.update([tag_if]) - selector.component_table.loc[idx, "classification_tags"] = ",".join( + selector.component_table_.loc[idx, "classification_tags"] = ",".join( str(s) for s in tmpset ) else: From 10f7b37bad52c891f06f3d55148808066d4c86e3 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Fri, 11 Aug 2023 15:11:29 -0400 Subject: [PATCH 07/81] Update across the package. --- docs/building_decision_trees.rst | 14 +- tedana/selection/component_selector.py | 22 +- tedana/selection/selection_nodes.py | 88 +++--- tedana/selection/selection_utils.py | 16 +- tedana/selection/tedica.py | 2 +- tedana/tests/test_component_selector.py | 6 +- tedana/tests/test_selection_nodes.py | 352 ++++++++++++------------ tedana/tests/test_selection_utils.py | 20 +- tedana/workflows/ica_reclassify.py | 2 +- 9 files changed, 261 insertions(+), 261 deletions(-) diff --git a/docs/building_decision_trees.rst b/docs/building_decision_trees.rst index dce2c6d31..1f5d82f73 100644 --- a/docs/building_decision_trees.rst +++ b/docs/building_decision_trees.rst @@ -51,7 +51,7 @@ The file key names are used below the full file names in the General outputs from component selection ======================================== -New columns in ``selector.component_table`` and the "ICA metrics tsv" file: +New columns in ``selector.component_table_`` and the "ICA metrics tsv" file: - classification: While the decision table is running, there may also be intermediate @@ -63,13 +63,13 @@ New columns in ``selector.component_table`` and the "ICA metrics tsv" file: or a comma separated list of tags. These tags may be useful parameters for visualizing and reviewing results -``selector.cross_component_metrics`` and "ICA cross component metrics json": +``selector.cross_component_metrics_`` and "ICA cross component metrics json": A dictionary of metrics that are each a single value calculated across components, for example, kappa and rho elbows. User or pre-defined scaling factors are also stored here. Any constant that is used in the component classification processes that isn't pre-defined in the decision tree file should be saved here. -``selector.component_status_table`` and "ICA status table tsv": +``selector.component_status_table_`` and "ICA status table tsv": A table where each column lists the classification status of each component after each node was run. Columns are only added for runs where component statuses can change. @@ -315,7 +315,7 @@ are good examples for how to meet these expectations. Create a dictionary called "outputs" that includes key fields that should be recorded. The following line should be at the end of each function to retain the output info: -``selector.nodes[selector.current_node_idx]["outputs"] = outputs`` +``selector.nodes[selector.current_node_idx_]["outputs"] = outputs`` Additional fields can be used to log function-specific information, but the following fields are common and may be used by other parts of the code: @@ -339,7 +339,7 @@ Before any data are touched in the function, there should be an call. This will be useful to gather all metrics a tree will use without requiring a specific dataset. -Existing functions define ``function_name_idx = f"Step {selector.current_node_idx}: [text of function_name]``. +Existing functions define ``function_name_idx = f"Step {selector.current_node_idx_}: [text of function_name]``. This is used in logging and is cleaner to initialize near the top of each function. Each function has code that creates a default node label in ``outputs["node_label"]``. @@ -386,7 +386,7 @@ also returns and should assign values to identified as true or false within each function. For calculation functions, the calculated values should be added as a value/key pair to -both ``selector.cross_component_metrics`` and ``outputs``. +both ``selector.cross_component_metrics_`` and ``outputs``. :func:`~tedana.selection.selection_utils.log_decision_tree_step` puts the relevant info from the function call into the program's output log. @@ -395,7 +395,7 @@ Every function should end with: .. code-block:: python - selector.nodes[selector.current_node_idx]["outputs"] = outputs + selector.nodes[selector.current_node_idx_]["outputs"] = outputs return selector functionname.__doc__ = (functionname.__doc__.format(**DECISION_DOCS)) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 03d3c33ac..9097b51f6 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -274,8 +274,8 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) ------- Adds to the ``ComponentSelector``: - - component_status_table: empty dataframe or contents of inputted status_table - - cross_component_metrics: empty dict or contents of inputed values + - component_status_table_: empty dataframe or contents of inputted status_table + - cross_component_metrics_: empty dict or contents of inputed values - used_metrics: empty set Any parameter that is used by a decision tree node function can be passed @@ -309,17 +309,17 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) When this is run, multiple elements in `ComponentSelector` will change including: - - component_table: ``classification`` column with ``accepted`` or ``rejected`` labels + - component_table_: ``classification`` column with ``accepted`` or ``rejected`` labels and ``classification_tags`` column with can hold multiple comma-separated labels explaining why a classification happened - - cross_component_metrics: Any values that were calculated based on the metric + - cross_component_metrics_: Any values that were calculated based on the metric values across components or by direct user input - component_status_table: Contains the classification statuses at each node in the decision tree - used_metrics: A list of metrics used in the selection process - nodes: The original tree definition with an added ``outputs`` key listing everything that changed in each node - - current_node_idx: The total number of nodes run in ``ComponentSelector`` + - current_node_idx_: The total number of nodes run in ``ComponentSelector`` """ self.__dict__.update(cross_component_metrics) @@ -361,7 +361,7 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) ) # for each node in the decision tree - for self.current_node_idx, node in enumerate( + for self.current_node_idx_, node in enumerate( self.tree["nodes"][self.start_idx_ :], start=self.start_idx_ ): # parse the variables to use with the function @@ -380,7 +380,7 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) all_params = {**params} LGR.debug( - f"Step {self.current_node_idx}: Running function {node['functionname']} " + f"Step {self.current_node_idx_}: Running function {node['functionname']} " f"with parameters: {all_params}" ) # run the decision node function @@ -390,14 +390,14 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) self = fcn(self, **params) self.tree["used_metrics"].update( - self.tree["nodes"][self.current_node_idx]["outputs"]["used_metrics"] + self.tree["nodes"][self.current_node_idx_]["outputs"]["used_metrics"] ) # log the current counts for all classification labels - log_classification_counts(self.current_node_idx, self.component_table_) + log_classification_counts(self.current_node_idx_, self.component_table_) LGR.debug( - f"Step {self.current_node_idx} Full outputs: " - f"{self.tree['nodes'][self.current_node_idx]['outputs']}" + f"Step {self.current_node_idx_} Full outputs: " + f"{self.tree['nodes'][self.current_node_idx_]['outputs']}" ) # move decision columns to end diff --git a/tedana/selection/selection_nodes.py b/tedana/selection/selection_nodes.py index 2c0e1a432..0a0ed225a 100644 --- a/tedana/selection/selection_nodes.py +++ b/tedana/selection/selection_nodes.py @@ -85,7 +85,7 @@ def manual_classify( """ # predefine all outputs that should be logged outputs = { - "decision_node_idx": selector.current_node_idx, + "decision_node_idx": selector.current_node_idx_, "used_metrics": set(), "node_label": None, "n_true": None, @@ -98,7 +98,7 @@ def manual_classify( if_true = new_classification if_false = "nochange" - function_name_idx = f"Step {selector.current_node_idx}: manual_classify" + function_name_idx = f"Step {selector.current_node_idx_}: manual_classify" if custom_node_label: outputs["node_label"] = custom_node_label else: @@ -140,7 +140,7 @@ def manual_classify( selector.component_table_["classification_tags"] = "" LGR.info(function_name_idx + " component classification tags are cleared") - selector.tree["nodes"][selector.current_node_idx]["outputs"] = outputs + selector.tree["nodes"][selector.current_node_idx_]["outputs"] = outputs return selector @@ -233,7 +233,7 @@ def dec_left_op_right( """ # predefine all outputs that should be logged outputs = { - "decision_node_idx": selector.current_node_idx, + "decision_node_idx": selector.current_node_idx_, "used_metrics": set(), "used_cross_component_metrics": set(), "node_label": None, @@ -241,7 +241,7 @@ def dec_left_op_right( "n_false": None, } - function_name_idx = f"Step {selector.current_node_idx}: left_op_right" + function_name_idx = f"Step {selector.current_node_idx_}: left_op_right" # Only select components if the decision tree is being run if not only_used_metrics: comps2use = selectcomps2use(selector, decide_comps) @@ -461,7 +461,7 @@ def parse_vals(val): if_false=if_false, ) - selector.tree["nodes"][selector.current_node_idx]["outputs"] = outputs + selector.tree["nodes"][selector.current_node_idx_]["outputs"] = outputs return selector @@ -518,7 +518,7 @@ def dec_variance_lessthan_thresholds( %(used_metrics)s """ outputs = { - "decision_node_idx": selector.current_node_idx, + "decision_node_idx": selector.current_node_idx_, "used_metrics": set([var_metric]), "node_label": None, "n_true": None, @@ -528,7 +528,7 @@ def dec_variance_lessthan_thresholds( if only_used_metrics: return outputs["used_metrics"] - function_name_idx = f"Step {selector.current_node_idx}: variance_lt_thresholds" + function_name_idx = f"Step {selector.current_node_idx_}: variance_lt_thresholds" if custom_node_label: outputs["node_label"] = custom_node_label else: @@ -590,7 +590,7 @@ def dec_variance_lessthan_thresholds( if_false=if_false, ) - selector.tree["nodes"][selector.current_node_idx]["outputs"] = outputs + selector.tree["nodes"][selector.current_node_idx_]["outputs"] = outputs return selector @@ -626,7 +626,7 @@ def calc_median( %(selector)s %(used_metrics)s """ - function_name_idx = f"Step {selector.current_node_idx}: calc_median" + function_name_idx = f"Step {selector.current_node_idx_}: calc_median" if not isinstance(median_label, str): raise ValueError( f"{function_name_idx}: median_label must be a string. It is: {median_label}" @@ -640,7 +640,7 @@ def calc_median( ) outputs = { - "decision_node_idx": selector.current_node_idx, + "decision_node_idx": selector.current_node_idx_, "node_label": None, label_name: None, "used_metrics": set([metric_name]), @@ -684,7 +684,7 @@ def calc_median( log_decision_tree_step(function_name_idx, comps2use, calc_outputs=outputs) - selector.tree["nodes"][selector.current_node_idx]["outputs"] = outputs + selector.tree["nodes"][selector.current_node_idx_]["outputs"] = outputs return selector @@ -729,7 +729,7 @@ def calc_kappa_elbow( are called """ outputs = { - "decision_node_idx": selector.current_node_idx, + "decision_node_idx": selector.current_node_idx_, "node_label": None, "n_echos": selector.n_echos, "used_metrics": set(["kappa"]), @@ -748,7 +748,7 @@ def calc_kappa_elbow( if only_used_metrics: return outputs["used_metrics"] - function_name_idx = f"Step {selector.current_node_idx}: calc_kappa_elbow" + function_name_idx = f"Step {selector.current_node_idx_}: calc_kappa_elbow" if ("kappa_elbow_kundu" in selector.cross_component_metrics_) and ( "kappa_elbow_kundu" in outputs["calc_cross_comp_metrics"] @@ -799,7 +799,7 @@ def calc_kappa_elbow( log_decision_tree_step(function_name_idx, comps2use, calc_outputs=outputs) - selector.tree["nodes"][selector.current_node_idx]["outputs"] = outputs + selector.tree["nodes"][selector.current_node_idx_]["outputs"] = outputs return selector @@ -848,7 +848,7 @@ def calc_rho_elbow( for a more detailed explanation of the difference between the kundu and liberal options. """ - function_name_idx = f"Step {selector.current_node_idx}: calc_rho_elbow" + function_name_idx = f"Step {selector.current_node_idx_}: calc_rho_elbow" if rho_elbow_type == "kundu".lower(): elbow_name = "rho_elbow_kundu" @@ -861,7 +861,7 @@ def calc_rho_elbow( ) outputs = { - "decision_node_idx": selector.current_node_idx, + "decision_node_idx": selector.current_node_idx_, "node_label": None, "n_echos": selector.n_echos, "calc_cross_comp_metrics": [ @@ -934,7 +934,7 @@ def calc_rho_elbow( log_decision_tree_step(function_name_idx, comps2use, calc_outputs=outputs) - selector.tree["nodes"][selector.current_node_idx]["outputs"] = outputs + selector.tree["nodes"][selector.current_node_idx_]["outputs"] = outputs return selector @@ -998,7 +998,7 @@ def dec_classification_doesnt_exist( """ # predefine all outputs that should be logged outputs = { - "decision_node_idx": selector.current_node_idx, + "decision_node_idx": selector.current_node_idx_, "used_metrics": set(), "used_cross_comp_metrics": set(), "node_label": None, @@ -1009,7 +1009,7 @@ def dec_classification_doesnt_exist( if only_used_metrics: return outputs["used_metrics"] - function_name_idx = f"Step {selector.current_node_idx}: classification_doesnt_exist" + function_name_idx = f"Step {selector.current_node_idx_}: classification_doesnt_exist" if custom_node_label: outputs["node_label"] = custom_node_label elif at_least_num_exist == 1: @@ -1066,7 +1066,7 @@ def dec_classification_doesnt_exist( if_false=if_false, ) - selector.tree["nodes"][selector.current_node_idx]["outputs"] = outputs + selector.tree["nodes"][selector.current_node_idx_]["outputs"] = outputs return selector @@ -1117,7 +1117,7 @@ def dec_reclassify_high_var_comps( """ # predefine all outputs that should be logged outputs = { - "decision_node_idx": selector.current_node_idx, + "decision_node_idx": selector.current_node_idx_, "used_metrics": set(["variance explained"]), "used_cross_comp_metrics": set(["varex_upper_p"]), "node_label": None, @@ -1128,7 +1128,7 @@ def dec_reclassify_high_var_comps( if only_used_metrics: return outputs["used_metrics"] - function_name_idx = f"Step {selector.current_node_idx}: reclassify_high_var_comps" + function_name_idx = f"Step {selector.current_node_idx_}: reclassify_high_var_comps" if custom_node_label: outputs["node_label"] = custom_node_label else: @@ -1205,7 +1205,7 @@ def dec_reclassify_high_var_comps( if_false=if_false, ) - selector.tree["nodes"][selector.current_node_idx]["outputs"] = outputs + selector.tree["nodes"][selector.current_node_idx_]["outputs"] = outputs return selector @@ -1253,7 +1253,7 @@ def calc_varex_thresh( %(selector)s %(used_metrics)s """ - function_name_idx = f"Step {selector.current_node_idx}: calc_varex_thresh" + function_name_idx = f"Step {selector.current_node_idx_}: calc_varex_thresh" thresh_label = thresh_label.lower() if thresh_label is None or thresh_label == "": varex_name = "varex_thresh" @@ -1263,7 +1263,7 @@ def calc_varex_thresh( perc_name = f"{thresh_label}_perc" outputs = { - "decision_node_idx": selector.current_node_idx, + "decision_node_idx": selector.current_node_idx_, "node_label": None, varex_name: None, "num_highest_var_comps": num_highest_var_comps, @@ -1373,7 +1373,7 @@ def calc_varex_thresh( log_decision_tree_step(function_name_idx, comps2use, calc_outputs=outputs) - selector.tree["nodes"][selector.current_node_idx]["outputs"] = outputs + selector.tree["nodes"][selector.current_node_idx_]["outputs"] = outputs return selector @@ -1412,7 +1412,7 @@ def calc_extend_factor( """ outputs = { "used_metrics": set(), - "decision_node_idx": selector.current_node_idx, + "decision_node_idx": selector.current_node_idx_, "node_label": None, "extend_factor": None, "calc_cross_comp_metrics": ["extend_factor"], @@ -1421,7 +1421,7 @@ def calc_extend_factor( if only_used_metrics: return outputs["used_metrics"] - function_name_idx = f"Step {selector.current_node_idx}: calc_extend_factor" + function_name_idx = f"Step {selector.current_node_idx_}: calc_extend_factor" if "extend_factor" in selector.cross_component_metrics_: LGR.warning( @@ -1446,7 +1446,7 @@ def calc_extend_factor( log_decision_tree_step(function_name_idx, -1, calc_outputs=outputs) - selector.tree["nodes"][selector.current_node_idx]["outputs"] = outputs + selector.tree["nodes"][selector.current_node_idx_]["outputs"] = outputs return selector @@ -1494,7 +1494,7 @@ def calc_max_good_meanmetricrank( earlier versions of this code. It might be worth consistently using the same term, but this note will hopefully suffice for now. """ - function_name_idx = f"Step {selector.current_node_idx}: calc_max_good_meanmetricrank" + function_name_idx = f"Step {selector.current_node_idx_}: calc_max_good_meanmetricrank" if (metric_suffix is not None) and (metric_suffix != "") and isinstance(metric_suffix, str): metric_name = f"max_good_meanmetricrank_{metric_suffix}" @@ -1502,7 +1502,7 @@ def calc_max_good_meanmetricrank( metric_name = "max_good_meanmetricrank" outputs = { - "decision_node_idx": selector.current_node_idx, + "decision_node_idx": selector.current_node_idx_, "node_label": None, metric_name: None, "used_metrics": set(), @@ -1553,7 +1553,7 @@ def calc_max_good_meanmetricrank( log_decision_tree_step(function_name_idx, comps2use, calc_outputs=outputs) - selector.tree["nodes"][selector.current_node_idx]["outputs"] = outputs + selector.tree["nodes"][selector.current_node_idx_]["outputs"] = outputs return selector @@ -1597,10 +1597,10 @@ def calc_varex_kappa_ratio( This metric sometimes causes issues with high magnitude BOLD responses such as the V1 response to a block-design flashing checkerboard """ - function_name_idx = f"Step {selector.current_node_idx}: calc_varex_kappa_ratio" + function_name_idx = f"Step {selector.current_node_idx_}: calc_varex_kappa_ratio" outputs = { - "decision_node_idx": selector.current_node_idx, + "decision_node_idx": selector.current_node_idx_, "node_label": None, "kappa_rate": None, "used_metrics": {"kappa", "variance explained"}, @@ -1673,7 +1673,7 @@ def calc_varex_kappa_ratio( log_decision_tree_step(function_name_idx, comps2use, calc_outputs=outputs) - selector.tree["nodes"][selector.current_node_idx]["outputs"] = outputs + selector.tree["nodes"][selector.current_node_idx_]["outputs"] = outputs return selector @@ -1729,10 +1729,10 @@ def calc_revised_meanmetricrank_guesses( accepted components calculated as the ratio of ``num_acc_guess`` to ``restrict_factor``. """ - function_name_idx = f"Step {selector.current_node_idx}: calc_revised_meanmetricrank_guesses" + function_name_idx = f"Step {selector.current_node_idx_}: calc_revised_meanmetricrank_guesses" outputs = { - "decision_node_idx": selector.current_node_idx, + "decision_node_idx": selector.current_node_idx_, "node_label": None, "num_acc_guess": None, "conservative_guess": None, @@ -1747,7 +1747,7 @@ def calc_revised_meanmetricrank_guesses( }, "used_cross_component_metrics": {"kappa_elbow_kundu", "rho_elbow_kundu"}, "calc_cross_comp_metrics": ["num_acc_guess", "conservative_guess", "restrict_factor"], - "added_component_table_metrics": [f"d_table_score_node{selector.current_node_idx}"], + "added_component_table_metrics": [f"d_table_score_node{selector.current_node_idx_}"], } if only_used_metrics: @@ -1772,9 +1772,9 @@ def calc_revised_meanmetricrank_guesses( if not isinstance(restrict_factor, (int, float)): raise ValueError(f"restrict_factor needs to be a number. It is: {restrict_factor}") - if f"d_table_score_node{selector.current_node_idx}" in selector.component_table_: + if f"d_table_score_node{selector.current_node_idx_}" in selector.component_table_: raise ValueError( - f"d_table_score_node{selector.current_node_idx} is already a column" + f"d_table_score_node{selector.current_node_idx_} is already a column" f"in the component_table. Recalculating in {function_name_idx} can " "cause problems since these are only calculated on a subset of components" ) @@ -1852,9 +1852,9 @@ def calc_revised_meanmetricrank_guesses( tmp_d_table_score = generate_decision_table_score( tmp_kappa, tmp_dice_FT2, tmp_signal_m_noise_t, tmp_countnoise, tmp_countsigFT2 ) - selector.component_table_[f"d_table_score_node{selector.current_node_idx}"] = np.NaN + selector.component_table_[f"d_table_score_node{selector.current_node_idx_}"] = np.NaN selector.component_table_.loc[ - comps2use, f"d_table_score_node{selector.current_node_idx}" + comps2use, f"d_table_score_node{selector.current_node_idx_}" ] = tmp_d_table_score # Unclear if necessary, but this may clean up a weird issue on passing # references in a data frame. @@ -1867,6 +1867,6 @@ def calc_revised_meanmetricrank_guesses( log_decision_tree_step(function_name_idx, comps2use, calc_outputs=outputs) - selector.tree["nodes"][selector.current_node_idx]["outputs"] = outputs + selector.tree["nodes"][selector.current_node_idx_]["outputs"] = outputs return selector diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index 929d4f3a2..934a40ecb 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -104,7 +104,7 @@ def change_comptable_classifications( ---------- selector : :obj:`tedana.selection.component_selector.ComponentSelector` The attributes used are component_table, component_status_table, and - current_node_idx + current_node_idx_ if_true, if_false : :obj:`str` If the condition in this step is true or false, give the component the label in this string. Options are 'accepted', 'rejected', @@ -129,7 +129,7 @@ def change_comptable_classifications( component_table["classifications"] will reflect any new classifications. component_status_table will have a new column titled - "Node current_node_idx" that is a copy of the updated classifications + "Node current_node_idx_" that is a copy of the updated classifications column. component_table["classification_tags"] will be updated to include any new tags. Each tag should appear only once in the string and tags will @@ -160,7 +160,7 @@ def change_comptable_classifications( ) selector.component_status_table_[ - f"Node {selector.current_node_idx}" + f"Node {selector.current_node_idx_}" ] = selector.component_table_["classification"] n_true = decision_boolean.sum() @@ -182,7 +182,7 @@ def comptable_classification_changer( ---------- selector : :obj:`tedana.selection.component_selector.ComponentSelector` The attributes used are component_table, component_status_table, and - current_node_idx + current_node_idx_ boolstate : :obj:`bool` Change classifications only for True or False components in decision_boolean based on this variable @@ -213,7 +213,7 @@ def comptable_classification_changer( component_table["classifications"] will reflect any new classifications. component_status_table will have a new column titled - "Node current_node_idx" that is a copy of the updated classifications + "Node current_node_idx_" that is a copy of the updated classifications column. component_table["classification_tags"] will be updated to include any new tags. Each tag should appear only once in the string and tags will @@ -246,7 +246,7 @@ def comptable_classification_changer( ("accepted" in current_classifications) and (classify_if != "accepted") ) or (("rejected" in current_classifications) and (classify_if != "rejected")): LGR.warning( - f"Step {selector.current_node_idx}: Some classifications are" + f"Step {selector.current_node_idx_}: Some classifications are" " changing away from accepted or rejected. Once a component is " "accepted or rejected, it shouldn't be reclassified" ) @@ -278,7 +278,7 @@ def comptable_classification_changer( ) else: LGR.info( - f"Step {selector.current_node_idx}: No components fit criterion " + f"Step {selector.current_node_idx_}: No components fit criterion " f"{boolstate} to change classification" ) return selector @@ -369,7 +369,7 @@ def log_decision_tree_step( ---------- function_name_idx : :obj:`str` The name of the function that should be logged. By convention, this - be "Step current_node_idx: function_name" + be "Step current_node_idx_: function_name" comps2use : :obj:`list[int]` or -1 A list of component indices that should be used by a function. Only used to report no components found if empty and report diff --git a/tedana/selection/tedica.py b/tedana/selection/tedica.py index 34b402b55..de346c39d 100644 --- a/tedana/selection/tedica.py +++ b/tedana/selection/tedica.py @@ -63,6 +63,6 @@ def automatic_selection(component_table, selector, **kwargs): component_table["classification_tags"] = "" selector.select(component_table, cross_component_metrics=kwargs) - selector.metadata_ = collect.get_metadata(selector.component_table) + selector.metadata_ = collect.get_metadata(selector.component_table_) return selector diff --git a/tedana/tests/test_component_selector.py b/tedana/tests/test_component_selector.py index 5f9a3da52..ebe7b525c 100644 --- a/tedana/tests/test_component_selector.py +++ b/tedana/tests/test_component_selector.py @@ -185,7 +185,7 @@ def test_minimal(): sample_comptable(), cross_component_metrics=xcomp.copy(), ) - selector.component_table = selector.component_table.drop(columns="classification_tags") + selector.component_table_ = selector.component_table_.drop(columns="classification_tags") selector.select() @@ -310,8 +310,8 @@ def test_are_all_components_accepted_or_rejected(): """Tests warnings are triggered in are_all_components_accepted_or_rejected""" selector = component_selector.ComponentSelector("minimal", sample_comptable()) - selector.component_table.loc[7, "classification"] = "intermediate1" - selector.component_table.loc[[1, 3, 5], "classification"] = "intermediate2" + selector.component_table_.loc[7, "classification"] = "intermediate1" + selector.component_table_.loc[[1, 3, 5], "classification"] = "intermediate2" selector.are_all_components_accepted_or_rejected() diff --git a/tedana/tests/test_selection_nodes.py b/tedana/tests/test_selection_nodes.py index 909af2b21..8b7e060dd 100644 --- a/tedana/tests/test_selection_nodes.py +++ b/tedana/tests/test_selection_nodes.py @@ -36,19 +36,19 @@ def test_manual_classify_smoke(): ) # There should be 4 selected components and component_status_table should # have a new column "Node 0" - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 4 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_false"] == 0 - assert f"Node {selector.current_node_idx}" in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 4 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_false"] == 0 + assert f"Node {selector.current_node_idx_}" in selector.component_status_table_ # No components with "NotALabel" classification so nothing selected and no # Node 1 column not created in component_status_table - selector.current_node_idx = 1 + selector.current_node_idx_ = 1 selector = selection_nodes.manual_classify(selector, "NotAClassification", new_classification) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 0 - assert f"Node {selector.current_node_idx}" not in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 0 + assert f"Node {selector.current_node_idx_}" not in selector.component_status_table_ # Changing components from "rejected" to "accepted" and suppressing warning - selector.current_node_idx = 2 + selector.current_node_idx_ = 2 selector = selection_nodes.manual_classify( selector, "rejected", @@ -59,8 +59,8 @@ def test_manual_classify_smoke(): tag="test tag", dont_warn_reclassify=True, ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 4 - assert f"Node {selector.current_node_idx}" in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 4 + assert f"Node {selector.current_node_idx_}" in selector.component_status_table_ def test_dec_left_op_right_succeeds(): @@ -98,13 +98,13 @@ def test_dec_left_op_right_succeeds(): ) # scales are set to make sure 3 components are true and 1 is false using # the sample component table - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 3 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_false"] == 1 - assert f"Node {selector.current_node_idx}" in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 3 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_false"] == 1 + assert f"Node {selector.current_node_idx_}" in selector.component_status_table_ # No components with "NotALabel" classification so nothing selected and no # Node 1 column is created in component_status_table - selector.current_node_idx = 1 + selector.current_node_idx_ = 1 selector = selection_nodes.dec_left_op_right( selector, "accepted", @@ -114,8 +114,8 @@ def test_dec_left_op_right_succeeds(): "kappa", "rho", ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 0 - assert f"Node {selector.current_node_idx}" not in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 0 + assert f"Node {selector.current_node_idx_}" not in selector.component_status_table_ # Re-initializing selector so that it has components classificated as # "provisional accept" again @@ -130,14 +130,14 @@ def test_dec_left_op_right_succeeds(): "kappa", "test_elbow", ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 3 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_false"] == 1 - assert f"Node {selector.current_node_idx}" in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 3 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_false"] == 1 + assert f"Node {selector.current_node_idx_}" in selector.component_status_table_ # right is a component_table_metric, left is a cross_component_metric # left also has a left_scale that's a cross component metric selector = sample_selector(options="provclass") - selector.cross_component_metrics["new_cc_metric"] = 1.02 + selector.cross_component_metrics_["new_cc_metric"] = 1.02 selector = selection_nodes.dec_left_op_right( selector, "accepted", @@ -148,9 +148,9 @@ def test_dec_left_op_right_succeeds(): "kappa", left_scale="new_cc_metric", ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 1 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_false"] == 3 - assert f"Node {selector.current_node_idx}" in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 1 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_false"] == 3 + assert f"Node {selector.current_node_idx_}" in selector.component_status_table_ # left component_table_metric, right is a constant integer value selector = sample_selector(options="provclass") @@ -163,9 +163,9 @@ def test_dec_left_op_right_succeeds(): "kappa", 21, ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 3 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_false"] == 1 - assert f"Node {selector.current_node_idx}" in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 3 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_false"] == 1 + assert f"Node {selector.current_node_idx_}" in selector.component_status_table_ # right component_table_metric, left is a constant float value selector = sample_selector(options="provclass") @@ -178,9 +178,9 @@ def test_dec_left_op_right_succeeds(): 21.0, "kappa", ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 1 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_false"] == 3 - assert f"Node {selector.current_node_idx}" in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 1 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_false"] == 3 + assert f"Node {selector.current_node_idx_}" in selector.component_status_table_ # Testing combination of two statements. kappa>21 AND rho<14 selector = sample_selector(options="provclass") @@ -196,9 +196,9 @@ def test_dec_left_op_right_succeeds(): op2="<", right2=14, ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 2 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_false"] == 2 - assert f"Node {selector.current_node_idx}" in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 2 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_false"] == 2 + assert f"Node {selector.current_node_idx_}" in selector.component_status_table_ # Testing combination of three statements. kappa>21 AND rho<14 AND 'variance explained'<5 selector = sample_selector(options="provclass") @@ -217,9 +217,9 @@ def test_dec_left_op_right_succeeds(): op3="<", right3=5, ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 1 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_false"] == 3 - assert f"Node {selector.current_node_idx}" in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 1 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_false"] == 3 + assert f"Node {selector.current_node_idx_}" in selector.component_status_table_ def test_dec_left_op_right_fails(): @@ -392,27 +392,27 @@ def test_dec_variance_lessthan_thresholds_smoke(): tag_if_true="test true tag", tag_if_false="test false tag", ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 1 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_false"] == 3 - assert f"Node {selector.current_node_idx}" in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 1 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_false"] == 3 + assert f"Node {selector.current_node_idx_}" in selector.component_status_table_ # No components with "NotALabel" classification so nothing selected and no # Node 1 column not created in component_status_table - selector.current_node_idx = 1 + selector.current_node_idx_ = 1 selector = selection_nodes.dec_variance_lessthan_thresholds( selector, "accepted", "rejected", "NotAClassification" ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 0 - assert f"Node {selector.current_node_idx}" not in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 0 + assert f"Node {selector.current_node_idx_}" not in selector.component_status_table_ # Running without specifying logging text generates internal text selector = sample_selector(options="provclass") selector = selection_nodes.dec_variance_lessthan_thresholds( selector, "accepted", "rejected", decide_comps ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_false"] == 4 - assert f"Node {selector.current_node_idx}" in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_false"] == 4 + assert f"Node {selector.current_node_idx_}" in selector.component_status_table_ def test_calc_kappa_elbow(): @@ -440,14 +440,14 @@ def test_calc_kappa_elbow(): "varex_upper_p", } output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["kappa_elbow_kundu"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["kappa_allcomps_elbow"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["kappa_nonsig_elbow"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["varex_upper_p"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_elbow_kundu"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_allcomps_elbow"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_nonsig_elbow"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["varex_upper_p"] > 0 # Using a subset of components for decide_comps. selector = selection_nodes.calc_kappa_elbow( @@ -464,14 +464,14 @@ def test_calc_kappa_elbow(): "varex_upper_p", } output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["kappa_elbow_kundu"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["kappa_allcomps_elbow"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["kappa_nonsig_elbow"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["varex_upper_p"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_elbow_kundu"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_allcomps_elbow"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_nonsig_elbow"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["varex_upper_p"] > 0 # No components with "NotALabel" classification so nothing selected selector = sample_selector() @@ -480,16 +480,16 @@ def test_calc_kappa_elbow(): # Outputs just the metrics used in this function selector = selection_nodes.calc_kappa_elbow(selector, decide_comps) assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["kappa_elbow_kundu"] is None + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_elbow_kundu"] is None ) assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["kappa_allcomps_elbow"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_allcomps_elbow"] is None ) assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["kappa_nonsig_elbow"] is None + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_nonsig_elbow"] is None ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["varex_upper_p"] is None + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["varex_upper_p"] is None def test_calc_rho_elbow(): @@ -517,16 +517,16 @@ def test_calc_rho_elbow(): "elbow_f05", } output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["rho_elbow_kundu"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["rho_allcomps_elbow"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["rho_elbow_kundu"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["rho_allcomps_elbow"] > 0 assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["rho_unclassified_elbow"] > 0 + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["rho_unclassified_elbow"] > 0 ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["elbow_f05"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["elbow_f05"] > 0 # Standard call to this function using rho_elbow_type="liberal" selector = selection_nodes.calc_rho_elbow( @@ -544,16 +544,16 @@ def test_calc_rho_elbow(): "elbow_f05", } output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["rho_elbow_liberal"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["rho_allcomps_elbow"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["rho_elbow_liberal"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["rho_allcomps_elbow"] > 0 assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["rho_unclassified_elbow"] > 0 + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["rho_unclassified_elbow"] > 0 ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["elbow_f05"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["elbow_f05"] > 0 # Using a subset of components for decide_comps. selector = selection_nodes.calc_rho_elbow( @@ -570,16 +570,16 @@ def test_calc_rho_elbow(): "elbow_f05", } output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["rho_elbow_kundu"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["rho_allcomps_elbow"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["rho_elbow_kundu"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["rho_allcomps_elbow"] > 0 assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["rho_unclassified_elbow"] > 0 + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["rho_unclassified_elbow"] > 0 ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["elbow_f05"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["elbow_f05"] > 0 with pytest.raises(ValueError): selection_nodes.calc_rho_elbow(selector, decide_comps, rho_elbow_type="perfect") @@ -590,15 +590,15 @@ def test_calc_rho_elbow(): # Outputs just the metrics used in this function selector = selection_nodes.calc_rho_elbow(selector, decide_comps) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["rho_elbow_kundu"] is None + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["rho_elbow_kundu"] is None assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["rho_allcomps_elbow"] is None + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["rho_allcomps_elbow"] is None ) assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["rho_unclassified_elbow"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["rho_unclassified_elbow"] is None ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["elbow_f05"] is None + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["elbow_f05"] is None def test_calc_median_smoke(): @@ -629,11 +629,11 @@ def test_calc_median_smoke(): ) calc_cross_comp_metrics = {"median_varex"} output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["median_varex"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["median_varex"] > 0 # repeating standard call and should make a warning because metric_varex already exists selector = selection_nodes.calc_median( @@ -641,7 +641,7 @@ def test_calc_median_smoke(): ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["median_varex"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["median_varex"] > 0 # Log without running if no components of decide_comps are in the component table selector = sample_selector() @@ -651,7 +651,7 @@ def test_calc_median_smoke(): metric_name="variance explained", median_label="varex", ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["median_varex"] is None + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["median_varex"] is None # Crashes because median_label is not a string with pytest.raises(ValueError): @@ -706,26 +706,26 @@ def test_dec_classification_doesnt_exist_smoke(): custom_node_label="custom label", tag="test true tag", ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 0 # Lists the number of components in decide_comps in n_false - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_false"] == 17 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_false"] == 17 # During normal execution, it will find provionally accepted components # and do nothing so another node isn't created - assert f"Node {selector.current_node_idx}" not in selector.component_status_table + assert f"Node {selector.current_node_idx_}" not in selector.component_status_table_ # No components with "NotALabel" classification so nothing selected and no # Node 1 column not created in component_status_table # Running without specifying logging text generates internal text - selector.current_node_idx = 1 + selector.current_node_idx_ = 1 selector = selection_nodes.dec_classification_doesnt_exist( selector, "accepted", "NotAClassification", class_comp_exists="provisional accept", ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 0 - assert f"Node {selector.current_node_idx}" not in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 0 + assert f"Node {selector.current_node_idx_}" not in selector.component_status_table_ # Other normal state is to change classifications when there are # no components with class_comp_exists. Since the component_table @@ -740,9 +740,9 @@ def test_dec_classification_doesnt_exist_smoke(): class_comp_exists="provisional reject", tag="test true tag", ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 17 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_false"] == 0 - assert f"Node {selector.current_node_idx}" in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 17 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_false"] == 0 + assert f"Node {selector.current_node_idx_}" in selector.component_status_table_ # Standard execution with at_least_num_exist=5 which should trigger the # components don't exist output @@ -758,10 +758,10 @@ def test_dec_classification_doesnt_exist_smoke(): custom_node_label="custom label", tag="test true tag", ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 17 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 17 # Lists the number of components in decide_comps in n_false - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_false"] == 0 - assert f"Node {selector.current_node_idx}" in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_false"] == 0 + assert f"Node {selector.current_node_idx_}" in selector.component_status_table_ def test_dec_reclassify_high_var_comps(): @@ -795,12 +795,12 @@ def test_dec_reclassify_high_var_comps(): "unclass_highvar", "NotAClassification", ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 0 - assert f"Node {selector.current_node_idx}" not in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 0 + assert f"Node {selector.current_node_idx_}" not in selector.component_status_table_ # Add varex_upper_p to cross component_metrics to run normal test selector = sample_selector(options="unclass") - selector.cross_component_metrics["varex_upper_p"] = 0.97 + selector.cross_component_metrics_["varex_upper_p"] = 0.97 # Standard execution where with all extra logging code and options changed from defaults selection_nodes.dec_reclassify_high_var_comps( @@ -813,18 +813,18 @@ def test_dec_reclassify_high_var_comps(): tag="test true tag", ) # Lists the number of components in decide_comps in n_true or n_false - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 3 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_false"] == 10 - assert f"Node {selector.current_node_idx}" in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 3 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_false"] == 10 + assert f"Node {selector.current_node_idx_}" in selector.component_status_table_ # No components with "NotALabel" classification so nothing selected and no # Node 1 column is created in component_status_table - selector.current_node_idx = 1 + selector.current_node_idx_ = 1 selector = selection_nodes.dec_reclassify_high_var_comps( selector, "unclass_highvar", "NotAClassification" ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["n_true"] == 0 - assert f"Node {selector.current_node_idx}" not in selector.component_status_table + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["n_true"] == 0 + assert f"Node {selector.current_node_idx_}" not in selector.component_status_table_ def test_calc_varex_thresh_smoke(): @@ -852,12 +852,12 @@ def test_calc_varex_thresh_smoke(): ) calc_cross_comp_metrics = {"varex_upper_thresh", "upper_perc"} output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["varex_upper_thresh"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["upper_perc"] == 90 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["varex_upper_thresh"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["upper_perc"] == 90 # Standard call , but thresh_label is "" selector = selection_nodes.calc_varex_thresh( @@ -871,12 +871,12 @@ def test_calc_varex_thresh_smoke(): ) calc_cross_comp_metrics = {"varex_thresh", "perc"} output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["varex_thresh"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["perc"] == 90 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["varex_thresh"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["perc"] == 90 # Standard call using num_highest_var_comps as an integer selector = selection_nodes.calc_varex_thresh( @@ -888,17 +888,17 @@ def test_calc_varex_thresh_smoke(): ) calc_cross_comp_metrics = {"varex_new_lower_thresh", "new_lower_perc"} output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["varex_new_lower_thresh"] > 0 + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["varex_new_lower_thresh"] > 0 ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["new_lower_perc"] == 25 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["new_lower_perc"] == 25 # Standard call using num_highest_var_comps as a value in cross_component_metrics - selector.cross_component_metrics["num_acc_guess"] = 10 + selector.cross_component_metrics_["num_acc_guess"] = 10 selector = selection_nodes.calc_varex_thresh( selector, decide_comps, @@ -908,14 +908,14 @@ def test_calc_varex_thresh_smoke(): ) calc_cross_comp_metrics = {"varex_new_lower_thresh", "new_lower_perc"} output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["varex_new_lower_thresh"] > 0 + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["varex_new_lower_thresh"] > 0 ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["new_lower_perc"] == 25 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["new_lower_perc"] == 25 # Raise error if num_highest_var_comps is a string, but not in cross_component_metrics with pytest.raises(ValueError): @@ -937,11 +937,11 @@ def test_calc_varex_thresh_smoke(): num_highest_var_comps="NotACrossCompMetric", ) assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["varex_new_lower_thresh"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["varex_new_lower_thresh"] is None ) # percentile_thresh doesn't depend on components and is assigned - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["new_lower_perc"] == 25 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["new_lower_perc"] == 25 # Raise error if num_highest_var_comps is not an integer with pytest.raises(ValueError): @@ -966,20 +966,20 @@ def test_calc_varex_thresh_smoke(): ) calc_cross_comp_metrics = {"varex_new_lower_thresh", "new_lower_perc"} output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["varex_new_lower_thresh"] > 0 + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["varex_new_lower_thresh"] > 0 ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["new_lower_perc"] == 25 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["new_lower_perc"] == 25 # Run warning logging code to see if any of the cross_component_metrics # already exists and would be over-written selector = sample_selector(options="provclass") - selector.cross_component_metrics["varex_upper_thresh"] = 1 - selector.cross_component_metrics["upper_perc"] = 1 + selector.cross_component_metrics_["varex_upper_thresh"] = 1 + selector.cross_component_metrics_["upper_perc"] = 1 decide_comps = "provisional accept" selector = selection_nodes.calc_varex_thresh( selector, @@ -991,8 +991,8 @@ def test_calc_varex_thresh_smoke(): custom_node_label="custom label", ) assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["varex_upper_thresh"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["upper_perc"] == 90 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["varex_upper_thresh"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["upper_perc"] == 90 # Raise error if percentile_thresh isn't a number selector = sample_selector(options="provclass") @@ -1014,10 +1014,10 @@ def test_calc_varex_thresh_smoke(): selector, decide_comps="NotAClassification", thresh_label="upper", percentile_thresh=90 ) assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["varex_upper_thresh"] is None + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["varex_upper_thresh"] is None ) # percentile_thresh doesn't depend on components and is assigned - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["upper_perc"] == 90 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["upper_perc"] == 90 def test_calc_extend_factor_smoke(): @@ -1038,27 +1038,27 @@ def test_calc_extend_factor_smoke(): ) calc_cross_comp_metrics = {"extend_factor"} output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["extend_factor"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["extend_factor"] > 0 # Run warning logging code for if any of the cross_component_metrics # already existed and would be over-written selector = sample_selector() - selector.cross_component_metrics["extend_factor"] = 1.0 + selector.cross_component_metrics_["extend_factor"] = 1.0 selector = selection_nodes.calc_extend_factor(selector) assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["extend_factor"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["extend_factor"] > 0 # Run with extend_factor defined as an input selector = sample_selector() selector = selection_nodes.calc_extend_factor(selector, extend_factor=1.2) assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["extend_factor"] == 1.2 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["extend_factor"] == 1.2 def test_calc_max_good_meanmetricrank_smoke(): @@ -1067,7 +1067,7 @@ def test_calc_max_good_meanmetricrank_smoke(): # Standard use of this function requires some components to be "provisional accept" selector = sample_selector("provclass") # This function requires "extend_factor" to already be defined - selector.cross_component_metrics["extend_factor"] = 2.0 + selector.cross_component_metrics_["extend_factor"] = 2.0 # Outputs just the metrics used in this function {""} used_metrics = selection_nodes.calc_max_good_meanmetricrank( @@ -1085,28 +1085,28 @@ def test_calc_max_good_meanmetricrank_smoke(): ) calc_cross_comp_metrics = {"max_good_meanmetricrank"} output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["max_good_meanmetricrank"] > 0 + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["max_good_meanmetricrank"] > 0 ) # Standard call to this function with a user defined metric_suffix selector = sample_selector("provclass") - selector.cross_component_metrics["extend_factor"] = 2.0 + selector.cross_component_metrics_["extend_factor"] = 2.0 selector = selection_nodes.calc_max_good_meanmetricrank( selector, "provisional accept", metric_suffix="testsfx" ) calc_cross_comp_metrics = {"max_good_meanmetricrank_testsfx"} output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"][ + selector.tree["nodes"][selector.current_node_idx_]["outputs"][ "max_good_meanmetricrank_testsfx" ] > 0 @@ -1115,17 +1115,17 @@ def test_calc_max_good_meanmetricrank_smoke(): # Run warning logging code for if any of the cross_component_metrics # already existed and would be over-written selector = sample_selector("provclass") - selector.cross_component_metrics["max_good_meanmetricrank"] = 10 - selector.cross_component_metrics["extend_factor"] = 2.0 + selector.cross_component_metrics_["max_good_meanmetricrank"] = 10 + selector.cross_component_metrics_["extend_factor"] = 2.0 selector = selection_nodes.calc_max_good_meanmetricrank(selector, "provisional accept") calc_cross_comp_metrics = {"max_good_meanmetricrank"} output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["max_good_meanmetricrank"] > 0 + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["max_good_meanmetricrank"] > 0 ) # Raise an error if "extend_factor" isn't pre-defined @@ -1135,11 +1135,11 @@ def test_calc_max_good_meanmetricrank_smoke(): # Log without running if no components of decide_comps are in the component table selector = sample_selector() - selector.cross_component_metrics["extend_factor"] = 2.0 + selector.cross_component_metrics_["extend_factor"] = 2.0 selector = selection_nodes.calc_max_good_meanmetricrank(selector, "NotAClassification") assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["max_good_meanmetricrank"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["max_good_meanmetricrank"] is None ) @@ -1166,29 +1166,29 @@ def test_calc_varex_kappa_ratio_smoke(): ) calc_cross_comp_metrics = {"kappa_rate"} output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["kappa_rate"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_rate"] > 0 # Run warning logging code for if any of the cross_component_metrics # already existed and would be over-written selector = sample_selector("provclass") - selector.cross_component_metrics["kappa_rate"] = 10 + selector.cross_component_metrics_["kappa_rate"] = 10 selector = selection_nodes.calc_varex_kappa_ratio(selector, "provisional accept") assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["kappa_rate"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_rate"] > 0 # Log without running if no components of decide_comps are in the component table selector = sample_selector() selector = selection_nodes.calc_varex_kappa_ratio(selector, "NotAClassification") - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["kappa_rate"] is None + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_rate"] is None # Raise error if "varex kappa ratio" is already in component_table selector = sample_selector("provclass") - selector.component_table["varex kappa ratio"] = 42 + selector.component_table_["varex kappa ratio"] = 42 with pytest.raises(ValueError): selector = selection_nodes.calc_varex_kappa_ratio(selector, "provisional accept") @@ -1198,8 +1198,8 @@ def test_calc_revised_meanmetricrank_guesses_smoke(): # Standard use of this function requires some components to be "provisional accept" selector = sample_selector("provclass") - selector.cross_component_metrics["kappa_elbow_kundu"] = 19.1 - selector.cross_component_metrics["rho_elbow_kundu"] = 15.2 + selector.cross_component_metrics_["kappa_elbow_kundu"] = 19.1 + selector.cross_component_metrics_["rho_elbow_kundu"] = 15.2 # Outputs just the metrics used in this function {""} used_metrics = selection_nodes.calc_revised_meanmetricrank_guesses( @@ -1226,46 +1226,46 @@ def test_calc_revised_meanmetricrank_guesses_smoke(): ) calc_cross_comp_metrics = {"num_acc_guess", "conservative_guess", "restrict_factor"} output_calc_cross_comp_metrics = set( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["calc_cross_comp_metrics"] + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["calc_cross_comp_metrics"] ) # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["num_acc_guess"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["conservative_guess"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["restrict_factor"] == 2 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["num_acc_guess"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["conservative_guess"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["restrict_factor"] == 2 # Run warning logging code for if any of the cross_component_metrics # already existed and would be over-written selector = sample_selector("provclass") - selector.cross_component_metrics["kappa_elbow_kundu"] = 19.1 - selector.cross_component_metrics["rho_elbow_kundu"] = 15.2 - selector.cross_component_metrics["num_acc_guess"] = 10 - selector.cross_component_metrics["conservative_guess"] = 10 - selector.cross_component_metrics["restrict_factor"] = 5 + selector.cross_component_metrics_["kappa_elbow_kundu"] = 19.1 + selector.cross_component_metrics_["rho_elbow_kundu"] = 15.2 + selector.cross_component_metrics_["num_acc_guess"] = 10 + selector.cross_component_metrics_["conservative_guess"] = 10 + selector.cross_component_metrics_["restrict_factor"] = 5 selector = selection_nodes.calc_revised_meanmetricrank_guesses( selector, ["provisional accept", "provisional reject", "unclassified"] ) assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["num_acc_guess"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["conservative_guess"] > 0 - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["restrict_factor"] == 2 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["num_acc_guess"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["conservative_guess"] > 0 + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["restrict_factor"] == 2 # Log without running if no components of decide_comps are in the component table selector = sample_selector() - selector.cross_component_metrics["kappa_elbow_kundu"] = 19.1 - selector.cross_component_metrics["rho_elbow_kundu"] = 15.2 + selector.cross_component_metrics_["kappa_elbow_kundu"] = 19.1 + selector.cross_component_metrics_["rho_elbow_kundu"] = 15.2 selector = selection_nodes.calc_revised_meanmetricrank_guesses(selector, "NotAClassification") - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["num_acc_guess"] is None + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["num_acc_guess"] is None assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["conservative_guess"] is None + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["conservative_guess"] is None ) # Raise error if "d_table_score_node0" is already in component_table selector = sample_selector("provclass") - selector.cross_component_metrics["kappa_elbow_kundu"] = 19.1 - selector.cross_component_metrics["rho_elbow_kundu"] = 15.2 - selector.component_table["d_table_score_node0"] = 42 + selector.cross_component_metrics_["kappa_elbow_kundu"] = 19.1 + selector.cross_component_metrics_["rho_elbow_kundu"] = 15.2 + selector.component_table_["d_table_score_node0"] = 42 with pytest.raises(ValueError): selector = selection_nodes.calc_revised_meanmetricrank_guesses( selector, ["provisional accept", "provisional reject", "unclassified"] @@ -1273,8 +1273,8 @@ def test_calc_revised_meanmetricrank_guesses_smoke(): # Raise error if restrict_factor isn't a number selector = sample_selector("provclass") - selector.cross_component_metrics["kappa_elbow_kundu"] = 19.1 - selector.cross_component_metrics["rho_elbow_kundu"] = 15.2 + selector.cross_component_metrics_["kappa_elbow_kundu"] = 19.1 + selector.cross_component_metrics_["rho_elbow_kundu"] = 15.2 with pytest.raises(ValueError): selector = selection_nodes.calc_revised_meanmetricrank_guesses( selector, @@ -1284,7 +1284,7 @@ def test_calc_revised_meanmetricrank_guesses_smoke(): # Raise error if kappa_elbow_kundu isn't in cross_component_metrics selector = sample_selector("provclass") - selector.cross_component_metrics["rho_elbow_kundu"] = 15.2 + selector.cross_component_metrics_["rho_elbow_kundu"] = 15.2 with pytest.raises(ValueError): selector = selection_nodes.calc_revised_meanmetricrank_guesses( selector, ["provisional accept", "provisional reject", "unclassified"] @@ -1293,12 +1293,12 @@ def test_calc_revised_meanmetricrank_guesses_smoke(): # Do not raise error if kappa_elbow_kundu isn't in cross_component_metrics # and there are no components in decide_comps selector = sample_selector("provclass") - selector.cross_component_metrics["rho_elbow_kundu"] = 15.2 + selector.cross_component_metrics_["rho_elbow_kundu"] = 15.2 selector = selection_nodes.calc_revised_meanmetricrank_guesses( selector, decide_comps="NoComponents" ) - assert selector.tree["nodes"][selector.current_node_idx]["outputs"]["num_acc_guess"] is None + assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["num_acc_guess"] is None assert ( - selector.tree["nodes"][selector.current_node_idx]["outputs"]["conservative_guess"] is None + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["conservative_guess"] is None ) diff --git a/tedana/tests/test_selection_utils.py b/tedana/tests/test_selection_utils.py index 2f25baea8..8849e6eab 100644 --- a/tedana/tests/test_selection_utils.py +++ b/tedana/tests/test_selection_utils.py @@ -55,7 +55,7 @@ def sample_selector(options=None): "test_elbow": 21, } selector = ComponentSelector(tree, component_table, cross_component_metrics=xcomp) - selector.current_node_idx = 0 + selector.current_node_idx_ = 0 return selector @@ -111,7 +111,7 @@ def test_selectcomps2use_fails(): with pytest.raises(ValueError): selection_utils.selectcomps2use(selector, decide_comps) - selector.component_table = selector.component_table.drop(columns="classification") + selector.component_table_ = selector.component_table_.drop(columns="classification") with pytest.raises(ValueError): selection_utils.selectcomps2use(selector, "all") @@ -127,13 +127,13 @@ def test_comptable_classification_changer_succeeds(): def validate_changes(expected_classification): # check every element that was supposed to change, did change changeidx = decision_boolean.index[np.asarray(decision_boolean) == boolstate] - new_vals = selector.component_table.loc[changeidx, "classification"] + new_vals = selector.component_table_.loc[changeidx, "classification"] for val in new_vals: assert val == expected_classification # Change if true selector = sample_selector(options="provclass") - decision_boolean = selector.component_table["classification"] == "provisional accept" + decision_boolean = selector.component_table_["classification"] == "provisional accept" boolstate = True selector = selection_utils.comptable_classification_changer( selector, boolstate, "accepted", decision_boolean, tag_if="testing_tag" @@ -142,7 +142,7 @@ def validate_changes(expected_classification): # Run nochange condition selector = sample_selector(options="provclass") - decision_boolean = selector.component_table["classification"] == "provisional accept" + decision_boolean = selector.component_table_["classification"] == "provisional accept" selector = selection_utils.comptable_classification_changer( selector, boolstate, "nochange", decision_boolean, tag_if="testing_tag" ) @@ -150,7 +150,7 @@ def validate_changes(expected_classification): # Change if false selector = sample_selector(options="provclass") - decision_boolean = selector.component_table["classification"] != "provisional accept" + decision_boolean = selector.component_table_["classification"] != "provisional accept" boolstate = False selector = selection_utils.comptable_classification_changer( selector, boolstate, "rejected", decision_boolean, tag_if="testing_tag1, testing_tag2" @@ -160,7 +160,7 @@ def validate_changes(expected_classification): # Change from accepted to rejected, which should output a warning # (test if the warning appears?) selector = sample_selector(options="provclass") - decision_boolean = selector.component_table["classification"] == "accepted" + decision_boolean = selector.component_table_["classification"] == "accepted" boolstate = True selector = selection_utils.comptable_classification_changer( selector, boolstate, "rejected", decision_boolean, tag_if="testing_tag" @@ -169,7 +169,7 @@ def validate_changes(expected_classification): # Change from rejected to accepted and suppress warning selector = sample_selector(options="provclass") - decision_boolean = selector.component_table["classification"] == "rejected" + decision_boolean = selector.component_table_["classification"] == "rejected" boolstate = True selector = selection_utils.comptable_classification_changer( selector, @@ -190,7 +190,7 @@ def test_change_comptable_classifications_succeeds(): # Given the rho values in the sample table, decision_boolean should have # 2 True and 2 False values comps2use = selection_utils.selectcomps2use(selector, "provisional accept") - rho = selector.component_table.loc[comps2use, "rho"] + rho = selector.component_table_.loc[comps2use, "rho"] decision_boolean = rho < 13.5 selector, n_true, n_false = selection_utils.change_comptable_classifications( @@ -206,7 +206,7 @@ def test_change_comptable_classifications_succeeds(): assert n_false == 2 # check every element that was supposed to change, did change changeidx = decision_boolean.index[np.asarray(decision_boolean) == True] # noqa: E712 - new_vals = selector.component_table.loc[changeidx, "classification"] + new_vals = selector.component_table_.loc[changeidx, "classification"] for val in new_vals: assert val == "accepted" diff --git a/tedana/workflows/ica_reclassify.py b/tedana/workflows/ica_reclassify.py index 89e958b47..83ce51fdc 100644 --- a/tedana/workflows/ica_reclassify.py +++ b/tedana/workflows/ica_reclassify.py @@ -391,7 +391,7 @@ def ica_reclassify_workflow( selector.add_manual(reject, "rejected") selector.select() - comptable = selector.component_table + comptable = selector.component_table_ # NOTE: most of these will be identical to previous, but this makes # things easier for programs which will view the data after running. From dd9cd25d0e063416465bb3e0484fbca3f4b2fe32 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Fri, 11 Aug 2023 15:16:44 -0400 Subject: [PATCH 08/81] Keep updating. --- tedana/selection/component_selector.py | 4 ++-- tedana/tests/test_component_selector.py | 10 +++++----- tedana/workflows/ica_reclassify.py | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 9097b51f6..67f59dbbb 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -522,12 +522,12 @@ def accepted_comps_(self): return self.component_table_["classification"] == "accepted" @property - def n_accepted_comps(self): + def n_accepted_comps_(self): """The number of components that are accepted.""" return self.accepted_comps_.sum() @property - def rejected_comps(self): + def rejected_comps_(self): """A boolean :obj:`pandas.Series` of components that are rejected.""" return self.component_table_["classification"] == "rejected" diff --git a/tedana/tests/test_component_selector.py b/tedana/tests/test_component_selector.py index ebe7b525c..5df63a960 100644 --- a/tedana/tests/test_component_selector.py +++ b/tedana/tests/test_component_selector.py @@ -320,11 +320,11 @@ def test_selector_properties_smoke(): selector = component_selector.ComponentSelector("minimal", sample_comptable()) - assert selector.n_comps == 21 + assert selector.n_comps_ == 21 - # Also runs selector.likely_bold_comps and should need to deal with sets in each field - assert selector.n_likely_bold_comps == 17 + # Also runs selector.likely_bold_comps_ and should need to deal with sets in each field + assert selector.n_likely_bold_comps_ == 17 - assert selector.n_accepted_comps == 17 + assert selector.n_accepted_comps_ == 17 - assert selector.rejected_comps.sum() == 4 + assert selector.rejected_comps_.sum() == 4 diff --git a/tedana/workflows/ica_reclassify.py b/tedana/workflows/ica_reclassify.py index 83ce51fdc..aca31f9dc 100644 --- a/tedana/workflows/ica_reclassify.py +++ b/tedana/workflows/ica_reclassify.py @@ -415,7 +415,7 @@ def ica_reclassify_workflow( # Save component selector and tree selector.to_files(io_generator) - if selector.n_accepted_comps == 0: + if selector.n_accepted_comps_ == 0: LGR.warning( "No accepted components remaining after manual classification! " "Please check data and results!" @@ -424,8 +424,8 @@ def ica_reclassify_workflow( mmix_orig = mmix.copy() # TODO: make this a function if tedort: - comps_accepted = selector.accepted_comps - comps_rejected = selector.rejected_comps + comps_accepted = selector.accepted_comps_ + comps_rejected = selector.rejected_comps_ acc_ts = mmix[:, comps_accepted] rej_ts = mmix[:, comps_rejected] betas = np.linalg.lstsq(acc_ts, rej_ts, rcond=None)[0] @@ -434,7 +434,7 @@ def ica_reclassify_workflow( mmix[:, comps_rejected] = resid comp_names = [ io.add_decomp_prefix(comp, prefix="ica", max_value=comptable.index.max()) - for comp in range(selector.n_comps) + for comp in range(selector.n_comps_) ] mixing_df = pd.DataFrame(data=mmix, columns=comp_names) io_generator.save_file(mixing_df, "ICA orthogonalized mixing tsv") From 1236509c52a79fb84803374e83fe927ec526048d Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Fri, 11 Aug 2023 15:20:55 -0400 Subject: [PATCH 09/81] Update tedana.py --- tedana/workflows/tedana.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 2f0f6d3f5..98d8861dd 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -653,7 +653,7 @@ def tedana_workflow( # generated from dimensionally reduced data using full data (i.e., data # with thermal noise) LGR.info("Making second component selection guess from ICA results") - ica_selector = ComponentSelector(tree) + selector = ComponentSelector(tree) comptable = metrics.collect.generate_metrics( catd, data_oc, @@ -662,15 +662,15 @@ def tedana_workflow( tes, io_generator, "ICA", - metrics=ica_selector.necessary_metrics, + metrics=selector.necessary_metrics, ) - ica_selector = selection.automatic_selection( + selector = selection.automatic_selection( comptable, - ica_selector, + selector, n_echos=n_echos, n_vols=n_vols, ) - n_likely_bold_comps = ica_selector.n_likely_bold_comps + n_likely_bold_comps = selector.n_likely_bold_comps_ if (n_restarts < maxrestart) and (n_likely_bold_comps == 0): LGR.warning("No BOLD components found. Re-attempting ICA.") elif n_likely_bold_comps == 0: @@ -690,7 +690,7 @@ def tedana_workflow( mixing_file = io_generator.get_name("ICA mixing tsv") mmix = pd.read_table(mixing_file).values - ica_selector = ComponentSelector(tree) + selector = ComponentSelector(tree) comptable = metrics.collect.generate_metrics( catd, data_oc, @@ -699,11 +699,11 @@ def tedana_workflow( tes, io_generator, "ICA", - metrics=ica_selector.necessary_metrics, + metrics=selector.necessary_metrics, ) - ica_selector = selection.automatic_selection( + selector = selection.automatic_selection( comptable, - ica_selector, + selector, n_echos=n_echos, n_vols=n_vols, ) @@ -723,7 +723,7 @@ def tedana_workflow( io_generator.save_file(betas_oc, "z-scored ICA components img") # Save component selector and tree - ica_selector.to_files(io_generator) + selector.to_files(io_generator) # Save metrics and metadata metric_metadata = metrics.collect.get_metadata(comptable) io_generator.save_file(metric_metadata, "ICA metrics json") @@ -740,16 +740,16 @@ def tedana_workflow( } io_generator.save_file(decomp_metadata, "ICA decomposition json") - if ica_selector.n_likely_bold_comps == 0: + if selector.n_likely_bold_comps_ == 0: LGR.warning("No BOLD components detected! Please check data and results!") # TODO: un-hack separate comptable - comptable = ica_selector.component_table + comptable = selector.component_table_ mmix_orig = mmix.copy() if tedort: - comps_accepted = ica_selector.accepted_comps - comps_rejected = ica_selector.rejected_comps + comps_accepted = selector.accepted_comps_ + comps_rejected = selector.rejected_comps_ acc_ts = mmix[:, comps_accepted] rej_ts = mmix[:, comps_rejected] betas = np.linalg.lstsq(acc_ts, rej_ts, rcond=None)[0] @@ -758,7 +758,7 @@ def tedana_workflow( mmix[:, comps_rejected] = resid comp_names = [ io.add_decomp_prefix(comp, prefix="ICA", max_value=comptable.index.max()) - for comp in range(ica_selector.n_comps) + for comp in range(selector.n_comps_) ] mixing_df = pd.DataFrame(data=mmix, columns=comp_names) From 2a7460c3cad7682c7c9f4d96d8ad54d0fb48f166 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Tue, 15 Aug 2023 10:24:54 -0400 Subject: [PATCH 10/81] Add extra metrics to list. --- tedana/workflows/tedana.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 98d8861dd..7e615f0db 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -654,6 +654,11 @@ def tedana_workflow( # with thermal noise) LGR.info("Making second component selection guess from ICA results") selector = ComponentSelector(tree) + necessary_metrics = selector.necessary_metrics + # The figures require some metrics that might not be used by the decision tree. + extra_metrics = ["variance explained", "normalized variance explained", "kappa", "rho"] + necessary_metrics = sorted(list(set(necessary_metrics + extra_metrics))) + comptable = metrics.collect.generate_metrics( catd, data_oc, @@ -662,7 +667,7 @@ def tedana_workflow( tes, io_generator, "ICA", - metrics=selector.necessary_metrics, + metrics=necessary_metrics, ) selector = selection.automatic_selection( comptable, @@ -691,6 +696,11 @@ def tedana_workflow( mmix = pd.read_table(mixing_file).values selector = ComponentSelector(tree) + necessary_metrics = selector.necessary_metrics + # The figures require some metrics that might not be used by the decision tree. + extra_metrics = ["variance explained", "normalized variance explained", "kappa", "rho"] + necessary_metrics = sorted(list(set(necessary_metrics + extra_metrics))) + comptable = metrics.collect.generate_metrics( catd, data_oc, @@ -699,7 +709,7 @@ def tedana_workflow( tes, io_generator, "ICA", - metrics=selector.necessary_metrics, + metrics=necessary_metrics, ) selector = selection.automatic_selection( comptable, From dd0f8d7f1e8189c78f287eaf50327e2a74cd2264 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Tue, 15 Aug 2023 10:39:37 -0400 Subject: [PATCH 11/81] Update ica_reclassify.py --- tedana/workflows/ica_reclassify.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tedana/workflows/ica_reclassify.py b/tedana/workflows/ica_reclassify.py index e3f1d9249..89179012e 100644 --- a/tedana/workflows/ica_reclassify.py +++ b/tedana/workflows/ica_reclassify.py @@ -323,7 +323,7 @@ def ica_reclassify_workflow( in_both.append(a) if len(in_both) != 0: - raise ValueError("The following components were both accepted and rejected: " f"{in_both}") + raise ValueError(f"The following components were both accepted and rejected: {in_both}") # boilerplate basename = "report" @@ -377,12 +377,7 @@ def ica_reclassify_workflow( ) # Make a new selector with the added files - selector = selection.component_selector.ComponentSelector( - previous_tree_fname, - comptable, - cross_component_metrics=xcomp, - status_table=status_table, - ) + selector = selection.component_selector.ComponentSelector(previous_tree_fname) if accept: selector.add_manual(accept, "accepted") @@ -390,7 +385,11 @@ def ica_reclassify_workflow( if reject: selector.add_manual(reject, "rejected") - selector.select() + selector.select( + comptable, + cross_component_metrics=xcomp, + status_table=status_table, + ) comptable = selector.component_table_ # NOTE: most of these will be identical to previous, but this makes From 9a7afa98da9a81b36973e588d0103f909f3c518b Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Wed, 7 Feb 2024 14:10:07 -0500 Subject: [PATCH 12/81] Draft metric-based regressor correlations. --- tedana/decomposition/pca.py | 15 +- tedana/metrics/collect.py | 30 +- tedana/metrics/external.py | 24 ++ tedana/resources/config/metrics.json | 7 +- .../decision_trees/minimal_external1.json | 247 +++++++++++++++ .../decision_trees/minimal_external2.json | 281 ++++++++++++++++++ tedana/tests/test_metrics.py | 15 +- tedana/workflows/tedana.py | 53 +++- 8 files changed, 642 insertions(+), 30 deletions(-) create mode 100644 tedana/metrics/external.py create mode 100644 tedana/resources/decision_trees/minimal_external1.json create mode 100644 tedana/resources/decision_trees/minimal_external2.json diff --git a/tedana/decomposition/pca.py b/tedana/decomposition/pca.py index f2e76fc19..173185966 100644 --- a/tedana/decomposition/pca.py +++ b/tedana/decomposition/pca.py @@ -349,13 +349,14 @@ def tedpca( "d_table_score", ] comptable = metrics.collect.generate_metrics( - data_cat, - data_oc, - comp_ts, - adaptive_mask, - tes, - io_generator, - "PCA", + data_cat=data_cat, + data_oc=data_oc, + mixing=comp_ts, + adaptive_mask=adaptive_mask, + tes=tes, + io_generator=io_generator, + label="PCA", + external_regressors=None, metrics=required_metrics, ) diff --git a/tedana/metrics/collect.py b/tedana/metrics/collect.py index e71e5ee4a..9db4555f5 100644 --- a/tedana/metrics/collect.py +++ b/tedana/metrics/collect.py @@ -7,7 +7,7 @@ import pandas as pd from tedana import io, utils -from tedana.metrics import dependence +from tedana.metrics import dependence, external from tedana.metrics._utils import dependency_resolver, determine_signs, flip_components from tedana.stats import getfbounds @@ -16,6 +16,7 @@ def generate_metrics( + *, data_cat, data_optcom, mixing, @@ -23,6 +24,7 @@ def generate_metrics( tes, io_generator, label, + external_regressors=None, metrics=None, ): """Fit TE-dependence and -independence models to components. @@ -47,6 +49,9 @@ def generate_metrics( The output generator object for this workflow label : str in ['ICA', 'PCA'] The label for this metric generation type + external_regressors : None or :obj:`pandas.DataFrame`, optional + External regressors (e.g., motion parameters, physiological noise) to correlate with + ICA components. If None, no external regressor metrics will be calculated. metrics : list List of metrics to return @@ -320,6 +325,15 @@ def generate_metrics( comptable["countsigFT2"], ) + # External regressor-based metrics + if "external correlation" in required_metrics: + external_regressor_names = external_regressors.columns.tolist() + LGR.info("Calculating external regressor correlations") + for col in external_regressor_names: + external_regressor_arr = external_regressors[col].values + corrs = external.correlate_regressor(external_regressor_arr, mixing) + comptable.loc[f"{col}_correlation", :] = corrs + # Write verbose metrics if needed if io_generator.verbose: write_betas = "map echo betas" in metric_maps @@ -374,6 +388,7 @@ def generate_metrics( "d_table_score", "kappa ratio", "d_table_score_scrub", + "external correlation", "classification", "rationale", ) @@ -594,6 +609,19 @@ def get_metadata(comptable): }, } + if any(col.endswith("_correlation") for col in comptable.columns): + external_correlations = [col for col in comptable.columns if col.endswith("_correlation")] + for col in external_correlations: + original_col = col.replace("_correlation", "") + metric_metadata[col] = { + "LongName": f"{original_col}-component correlation", + "Description": ( + "Correlation between the component time series and the external regressor " + f"{original_col}." + ), + "Units": "Pearson correlation coefficient", + } + # There are always components in the comptable, definitionally metric_metadata["Component"] = { "LongName": "Component identifier", diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py new file mode 100644 index 000000000..bcf8ef421 --- /dev/null +++ b/tedana/metrics/external.py @@ -0,0 +1,24 @@ +"""Metrics unrelated to TE-(in)dependence.""" + +import numpy as np + + +def correlate_regressor(external_regressor, mixing): + """Correlate external regressors with mixing components. + + Parameters + ---------- + external_regressor : array, shape (n_samples) + External regressor. The regressor will be correlated with each component's time series. + mixing : array, shape (n_samples, n_components) + Mixing matrix from ICA. + + Returns + ------- + corrs : array, shape (n_components) + Correlations between external regressor and mixing components. + """ + assert external_regressor.ndim == 1 + assert external_regressor.shape[0] == mixing.shape[0] + corrs = np.corrcoef(external_regressor, mixing.T)[0, 1:] + return corrs diff --git a/tedana/resources/config/metrics.json b/tedana/resources/config/metrics.json index 038578e2e..9e3862bd7 100644 --- a/tedana/resources/config/metrics.json +++ b/tedana/resources/config/metrics.json @@ -6,7 +6,8 @@ "adaptive_mask", "mask", "tes", - "ref_img" + "ref_img", + "external_regressors" ], "dependencies": { "kappa": [ @@ -115,6 +116,10 @@ "mask", "ref_img", "tes" + ], + "external correlation": [ + "mixing", + "external_regressors" ] } } diff --git a/tedana/resources/decision_trees/minimal_external1.json b/tedana/resources/decision_trees/minimal_external1.json new file mode 100644 index 000000000..64041db6f --- /dev/null +++ b/tedana/resources/decision_trees/minimal_external1.json @@ -0,0 +1,247 @@ +{ + "tree_id": "minimal_decision_tree_test1", + "info": "Proposed minimal decision tree with a single threshold for all external regressor correlation metrics.", + "report": "This is based on the minimal criteria of the original MEICA decision tree \\citep{kundu2013integrated} without the more aggressive noise removal steps \\citep{dupre2021te}.", + "necessary_metrics": [ + "kappa", + "rho", + "countsigFS0", + "countsigFT2", + "dice_FS0", + "dice_FT2", + "signal-noise_t", + "variance explained", + "*_correlation" + ], + "intermediate_classifications": [ + "provisionalaccept", + "provisionalreject" + ], + "classification_tags": [ + "Likely BOLD", + "Unlikely BOLD", + "Low variance" + ], + "nodes": [ + { + "functionname": "manual_classify", + "parameters": { + "new_classification": "unclassified", + "decide_comps": "all" + }, + "kwargs": { + "log_extra_report": "", + "clear_classification_tags": true, + "dont_warn_reclassify": true + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "rho", + "right": "kappa" + }, + "kwargs": { + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "countsigFS0", + "right": "countsigFT2" + }, + "kwargs": { + "left2": "countsigFT2", + "op2": ">", + "right2": 0, + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "calc_median", + "parameters": { + "decide_comps": "all", + "metric_name": "variance explained", + "median_label": "varex" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "dice_FS0", + "right": "dice_FT2" + }, + "kwargs": { + "left2": "variance explained", + "op2": ">", + "right2": "median_varex", + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": 0, + "right": "signal-noise_t" + }, + "kwargs": { + "left2": "variance explained", + "op2": ">", + "right2": "median_varex", + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "calc_kappa_elbow", + "parameters": { + "decide_comps": "all" + }, + "kwargs": { + "log_extra_info": "", + "log_extra_report": "" + }, + "_comment": "" + }, + { + "functionname": "calc_rho_elbow", + "parameters": { + "decide_comps": "all" + }, + "kwargs": { + "subset_decide_comps": "unclassified", + "rho_elbow_type": "liberal", + "log_extra_info": "", + "log_extra_report": "" + }, + "_comment": "" + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "provisionalaccept", + "if_false": "provisionalreject", + "decide_comps": "unclassified", + "op": ">=", + "left": "kappa", + "right": "kappa_elbow_kundu" + }, + "kwargs": { + "log_extra_report": "" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "accepted", + "if_false": "nochange", + "decide_comps": "provisionalaccept", + "op": ">", + "left": "kappa", + "right": "rho" + }, + "kwargs": { + "log_extra_info": "If kappa>elbow and kappa>2*rho accept even if rho>elbow", + "log_extra_report": "", + "right_scale": 2, + "tag_if_true": "Likely BOLD" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "provisionalreject", + "if_false": "nochange", + "decide_comps": [ + "provisionalreject", + "provisionalaccept" + ], + "op": ">", + "left": "rho", + "right": "rho_elbow_liberal" + }, + "kwargs": { + "log_extra_report": "" + } + }, + { + "functionname": "dec_variance_lessthan_thresholds", + "parameters": { + "if_true": "accepted", + "if_false": "nochange", + "decide_comps": "provisionalreject" + }, + "kwargs": { + "var_metric": "variance explained", + "log_extra_info": "", + "log_extra_report": "", + "single_comp_threshold": 0.1, + "all_comp_threshold": 1.0, + "tag_if_true": "Low variance" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "*_correlation", + "right": 0.2 + }, + "kwargs": { + "log_extra_info": "If any external regressor is correlated with the components > 0.2, then reject.", + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "manual_classify", + "parameters": { + "new_classification": "accepted", + "decide_comps": "provisionalaccept" + }, + "kwargs": { + "log_extra_info": "", + "log_extra_report": "", + "tag": "Likely BOLD" + } + }, + { + "functionname": "manual_classify", + "parameters": { + "new_classification": "rejected", + "decide_comps": [ + "provisionalreject", + "unclassified" + ] + }, + "kwargs": { + "log_extra_info": "", + "log_extra_report": "", + "tag": "Unlikely BOLD" + } + } + ] +} diff --git a/tedana/resources/decision_trees/minimal_external2.json b/tedana/resources/decision_trees/minimal_external2.json new file mode 100644 index 000000000..a6008ec31 --- /dev/null +++ b/tedana/resources/decision_trees/minimal_external2.json @@ -0,0 +1,281 @@ +{ + "tree_id": "minimal_decision_tree_test1", + "info": "Proposed minimal decision tree with a separate threshold for each external regressor correlation metric.", + "report": "This is based on the minimal criteria of the original MEICA decision tree \\citep{kundu2013integrated} without the more aggressive noise removal steps \\citep{dupre2021te}.", + "necessary_metrics": [ + "kappa", + "rho", + "countsigFS0", + "countsigFT2", + "dice_FS0", + "dice_FT2", + "signal-noise_t", + "variance explained", + "trans_x_correlation", + "trans_y_correlation", + "trans_z_correlation" + ], + "intermediate_classifications": [ + "provisionalaccept", + "provisionalreject" + ], + "classification_tags": [ + "Likely BOLD", + "Unlikely BOLD", + "Low variance" + ], + "nodes": [ + { + "functionname": "manual_classify", + "parameters": { + "new_classification": "unclassified", + "decide_comps": "all" + }, + "kwargs": { + "log_extra_report": "", + "clear_classification_tags": true, + "dont_warn_reclassify": true + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "rho", + "right": "kappa" + }, + "kwargs": { + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "countsigFS0", + "right": "countsigFT2" + }, + "kwargs": { + "left2": "countsigFT2", + "op2": ">", + "right2": 0, + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "calc_median", + "parameters": { + "decide_comps": "all", + "metric_name": "variance explained", + "median_label": "varex" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "dice_FS0", + "right": "dice_FT2" + }, + "kwargs": { + "left2": "variance explained", + "op2": ">", + "right2": "median_varex", + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": 0, + "right": "signal-noise_t" + }, + "kwargs": { + "left2": "variance explained", + "op2": ">", + "right2": "median_varex", + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "calc_kappa_elbow", + "parameters": { + "decide_comps": "all" + }, + "kwargs": { + "log_extra_info": "", + "log_extra_report": "" + }, + "_comment": "" + }, + { + "functionname": "calc_rho_elbow", + "parameters": { + "decide_comps": "all" + }, + "kwargs": { + "subset_decide_comps": "unclassified", + "rho_elbow_type": "liberal", + "log_extra_info": "", + "log_extra_report": "" + }, + "_comment": "" + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "provisionalaccept", + "if_false": "provisionalreject", + "decide_comps": "unclassified", + "op": ">=", + "left": "kappa", + "right": "kappa_elbow_kundu" + }, + "kwargs": { + "log_extra_report": "" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "accepted", + "if_false": "nochange", + "decide_comps": "provisionalaccept", + "op": ">", + "left": "kappa", + "right": "rho" + }, + "kwargs": { + "log_extra_info": "If kappa>elbow and kappa>2*rho accept even if rho>elbow", + "log_extra_report": "", + "right_scale": 2, + "tag_if_true": "Likely BOLD" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "provisionalreject", + "if_false": "nochange", + "decide_comps": [ + "provisionalreject", + "provisionalaccept" + ], + "op": ">", + "left": "rho", + "right": "rho_elbow_liberal" + }, + "kwargs": { + "log_extra_report": "" + } + }, + { + "functionname": "dec_variance_lessthan_thresholds", + "parameters": { + "if_true": "accepted", + "if_false": "nochange", + "decide_comps": "provisionalreject" + }, + "kwargs": { + "var_metric": "variance explained", + "log_extra_info": "", + "log_extra_report": "", + "single_comp_threshold": 0.1, + "all_comp_threshold": 1.0, + "tag_if_true": "Low variance" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "trans_x_correlation", + "right": 0.2 + }, + "kwargs": { + "log_extra_info": "If translational motion in the X direction is correlated with the components > 0.2, then reject.", + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "trans_y_correlation", + "right": 0.3 + }, + "kwargs": { + "log_extra_info": "If translational motion in the Y direction is correlated with the components > 0.3, then reject.", + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "trans_z_correlation", + "right": 0.4 + }, + "kwargs": { + "log_extra_info": "If translational motion in the Z direction is correlated with the components > 0.4, then reject.", + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "manual_classify", + "parameters": { + "new_classification": "accepted", + "decide_comps": "provisionalaccept" + }, + "kwargs": { + "log_extra_info": "", + "log_extra_report": "", + "tag": "Likely BOLD" + } + }, + { + "functionname": "manual_classify", + "parameters": { + "new_classification": "rejected", + "decide_comps": [ + "provisionalreject", + "unclassified" + ] + }, + "kwargs": { + "log_extra_info": "", + "log_extra_report": "", + "tag": "Unlikely BOLD" + } + } + ] +} diff --git a/tedana/tests/test_metrics.py b/tedana/tests/test_metrics.py index 826d186c1..3c61f8bcb 100644 --- a/tedana/tests/test_metrics.py +++ b/tedana/tests/test_metrics.py @@ -48,13 +48,14 @@ def test_smoke_generate_metrics(testdata1): "d_table_score", ] comptable = collect.generate_metrics( - testdata1["data_cat"], - testdata1["data_optcom"], - testdata1["mixing"], - testdata1["adaptive_mask"], - testdata1["tes"], - testdata1["generator"], - "ICA", + data_cat=testdata1["data_cat"], + data_optcom=testdata1["data_optcom"], + mixing=testdata1["mixing"], + adaptive_mask=testdata1["adaptive_mask"], + tes=testdata1["tes"], + io_generator=testdata1["generator"], + label="ICA", + external_regressors=None, metrics=metrics, ) assert isinstance(comptable, pd.DataFrame) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 710c72f13..beb947c85 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -163,6 +163,19 @@ def _get_parser(): ), default="kundu", ) + optional.add_argument( + "--external", + dest="external_regressors", + type=lambda x: is_valid_file(parser, x), + help=( + "File containing external regressors to be used in the decision tree. " + "The file must be a TSV file with the same number of rows as the number of volumes in " + "the input data. Each column in the file will be treated as a separate regressor. " + "The decision tree must contain a node for each of the columns in this file, " + "with the metric '_correlation'." + ), + default=None, + ) optional.add_argument( "--seed", dest="fixed_seed", @@ -322,6 +335,7 @@ def tedana_workflow( fittype="loglin", combmode="t2s", tree="kundu", + external_regressors=None, tedpca="aic", fixed_seed=42, maxit=500, @@ -381,6 +395,11 @@ def tedana_workflow( accepts and rejects some distinct components compared to kundu. Testing to better understand the effects of the differences is ongoing. Default is 'kundu'. + external_regressors : :obj:`str` or None, optional + File containing external regressors to be used in the decision tree. + The file must be a TSV file with the same number of rows as the number of volumes in + the input data. Each column in the file will be treated as a separate regressor. + Default is None. tedpca : {'mdl', 'aic', 'kic', 'kundu', 'kundu-stabilize', float, int}, optional Method with which to select components in TEDPCA. If a float is provided, then it is assumed to represent percentage of variance @@ -494,6 +513,10 @@ def tedana_workflow( # a float on [0, 1] or an int >= 1 tedpca = check_tedpca_value(tedpca, is_parser=False) + # Load external regressors if provided + if external_regressors: + external_regressors = pd.read_table(external_regressors) + # For z-catted files, make sure it's a list of size 1 if isinstance(data, str): data = [data] @@ -683,13 +706,14 @@ def tedana_workflow( "d_table_score", ] comptable = metrics.collect.generate_metrics( - catd, - data_oc, - mmix, - masksum_clf, - tes, - io_generator, - "ICA", + data_cat=catd, + data_optcom=data_oc, + mixing=mmix, + adaptive_mask=masksum_clf, + tes=tes, + io_generator=io_generator, + label="ICA", + external_regressors=external_regressors, metrics=required_metrics, ) ica_selector = selection.automatic_selection(comptable, n_echos, n_vols, tree=tree) @@ -727,13 +751,14 @@ def tedana_workflow( "d_table_score", ] comptable = metrics.collect.generate_metrics( - catd, - data_oc, - mmix, - masksum_clf, - tes, - io_generator, - "ICA", + data_cat=catd, + data_optcom=data_oc, + mixing=mmix, + adaptive_mask=masksum_clf, + tes=tes, + io_generator=io_generator, + label="ICA", + external_regressors=external_regressors, metrics=required_metrics, ) ica_selector = selection.automatic_selection( From 44cb0472acf6283f7451529bc72124d79cbd24cd Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Wed, 7 Feb 2024 14:14:03 -0500 Subject: [PATCH 13/81] Fix typo. --- tedana/decomposition/pca.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tedana/decomposition/pca.py b/tedana/decomposition/pca.py index 173185966..c8e3aa34b 100644 --- a/tedana/decomposition/pca.py +++ b/tedana/decomposition/pca.py @@ -350,7 +350,7 @@ def tedpca( ] comptable = metrics.collect.generate_metrics( data_cat=data_cat, - data_oc=data_oc, + data_optcom=data_oc, mixing=comp_ts, adaptive_mask=adaptive_mask, tes=tes, From 1b3681bb73bcdcc7d6d38feb441a670f70037ca2 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Thu, 8 Feb 2024 09:56:30 -0500 Subject: [PATCH 14/81] Work on trees. --- tedana/metrics/collect.py | 2 +- tedana/metrics/external.py | 4 ++-- .../decision_trees/minimal_external1.json | 4 ++-- .../decision_trees/minimal_external2.json | 6 +++--- tedana/selection/component_selector.py | 7 +++++-- tedana/selection/selection_utils.py | 18 ++++++++++++++---- tedana/workflows/tedana.py | 3 +++ 7 files changed, 30 insertions(+), 14 deletions(-) diff --git a/tedana/metrics/collect.py b/tedana/metrics/collect.py index 9db4555f5..d384d6ffa 100644 --- a/tedana/metrics/collect.py +++ b/tedana/metrics/collect.py @@ -332,7 +332,7 @@ def generate_metrics( for col in external_regressor_names: external_regressor_arr = external_regressors[col].values corrs = external.correlate_regressor(external_regressor_arr, mixing) - comptable.loc[f"{col}_correlation", :] = corrs + comptable[f"{col}_correlation"] = corrs # Write verbose metrics if needed if io_generator.verbose: diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index bcf8ef421..b2dc4bb1f 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -16,9 +16,9 @@ def correlate_regressor(external_regressor, mixing): Returns ------- corrs : array, shape (n_components) - Correlations between external regressor and mixing components. + Absolute correlations between external regressor and mixing components. """ assert external_regressor.ndim == 1 assert external_regressor.shape[0] == mixing.shape[0] - corrs = np.corrcoef(external_regressor, mixing.T)[0, 1:] + corrs = np.abs(np.corrcoef(external_regressor, mixing.T)[0, 1:]) return corrs diff --git a/tedana/resources/decision_trees/minimal_external1.json b/tedana/resources/decision_trees/minimal_external1.json index 64041db6f..08ecba528 100644 --- a/tedana/resources/decision_trees/minimal_external1.json +++ b/tedana/resources/decision_trees/minimal_external1.json @@ -11,7 +11,7 @@ "dice_FT2", "signal-noise_t", "variance explained", - "*_correlation" + "^.*_correlation$" ], "intermediate_classifications": [ "provisionalaccept", @@ -207,7 +207,7 @@ "if_false": "nochange", "decide_comps": "all", "op": ">", - "left": "*_correlation", + "left": "^.*_correlation$", "right": 0.2 }, "kwargs": { diff --git a/tedana/resources/decision_trees/minimal_external2.json b/tedana/resources/decision_trees/minimal_external2.json index a6008ec31..d66f5508f 100644 --- a/tedana/resources/decision_trees/minimal_external2.json +++ b/tedana/resources/decision_trees/minimal_external2.json @@ -210,7 +210,7 @@ "decide_comps": "all", "op": ">", "left": "trans_x_correlation", - "right": 0.2 + "right": 0.8 }, "kwargs": { "log_extra_info": "If translational motion in the X direction is correlated with the components > 0.2, then reject.", @@ -226,7 +226,7 @@ "decide_comps": "all", "op": ">", "left": "trans_y_correlation", - "right": 0.3 + "right": 0.8 }, "kwargs": { "log_extra_info": "If translational motion in the Y direction is correlated with the components > 0.3, then reject.", @@ -242,7 +242,7 @@ "decide_comps": "all", "op": ">", "left": "trans_z_correlation", - "right": 0.4 + "right": 0.8 }, "kwargs": { "log_extra_info": "If translational motion in the Z direction is correlated with the components > 0.4, then reject.", diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 623320b50..16a252230 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -348,12 +348,15 @@ def select(self): # this will crash the program with an error message if not all # necessary_metrics are in the comptable confirm_metrics_exist( - self.component_table, self.necessary_metrics, function_name=self.tree_name + self.component_table, + self.necessary_metrics, + function_name=self.tree_name, ) # for each node in the decision tree for self.current_node_idx, node in enumerate( - self.tree["nodes"][self.start_idx :], start=self.start_idx + self.tree["nodes"][self.start_idx :], + start=self.start_idx, ): # parse the variables to use with the function fcn = getattr(selection_nodes, node["functionname"]) diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index 4567926d8..f73a60bab 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -1,6 +1,7 @@ """Utility functions for tedana.selection.""" import logging +import re import numpy as np @@ -345,9 +346,18 @@ def confirm_metrics_exist(component_table, necessary_metrics, function_name=None the columns exist. Also, the string in `necessary_metrics` and the column labels in component_table will only be matched if they're identical. """ - missing_metrics = necessary_metrics - set(component_table.columns) - metrics_exist = len(missing_metrics) > 0 - if metrics_exist is True: + hardcoded_metrics = [metric for metric in necessary_metrics if not metric.startswith("^")] + regex_metrics = [metric for metric in necessary_metrics if metric.startswith("^")] + # Check that all hardcoded (literal string) metrics are accounted for. + missing_metrics = sorted(list(set(hardcoded_metrics) - set(component_table.columns))) + # Check that the regular expression-based metrics are accounted for. + found_metrics = component_table.columns.tolist() + for regex_metric in regex_metrics: + if not any(re.match(regex_metric, metric) for metric in found_metrics): + missing_metrics.append(regex_metric) + + metrics_are_missing = len(missing_metrics) > 0 + if metrics_are_missing: if function_name is None: function_name = "unknown function" @@ -359,7 +369,7 @@ def confirm_metrics_exist(component_table, necessary_metrics, function_name=None ) raise ValueError(error_msg) - return metrics_exist + return metrics_are_missing def log_decision_tree_step( diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index beb947c85..8913b262f 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -705,6 +705,9 @@ def tedana_workflow( "normalized variance explained", "d_table_score", ] + if external_regressors is not None: + required_metrics.append("external correlation") + comptable = metrics.collect.generate_metrics( data_cat=catd, data_optcom=data_oc, From 4c7a9ce04df2fd23f7fa82fefc97b62f93690003 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Thu, 8 Feb 2024 15:36:48 -0500 Subject: [PATCH 15/81] Expand regular expressions in trees. --- tedana/selection/component_selector.py | 6 ++- tedana/selection/selection_utils.py | 74 ++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 16a252230..4943249fa 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -11,6 +11,7 @@ from tedana.selection.selection_utils import ( clean_dataframe, confirm_metrics_exist, + expand_nodes, log_classification_counts, ) from tedana.utils import get_resource_path @@ -278,8 +279,9 @@ def __init__(self, tree, component_table, cross_component_metrics={}, status_tab if "classification" not in self.component_table: self.component_table["classification"] = "unclassified" - self.tree = load_config(self.tree_name) - tree_config = self.tree + tree_config = load_config(self.tree_name) + tree_config = expand_nodes(tree_config, component_table.columns.tolist()) + self.tree = tree_config LGR.info("Performing component selection with " + tree_config["tree_id"]) LGR.info(tree_config.get("info", "")) diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index f73a60bab..827b5555e 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -1,5 +1,6 @@ """Utility functions for tedana.selection.""" +import copy import logging import re @@ -316,6 +317,79 @@ def clean_dataframe(component_table): ################################################# +def expand_dict(d, node, metrics): + regex_found = False + out_nodes = [] + for k, v in d.items(): + if isinstance(v, str) and v.startswith("^"): + regex_found = True + replacements = [metric for metric in metrics if re.match(v, metric)] + if not replacements: + raise ValueError(f"No metrics matching regex '{v}' found.") + + for replacement in replacements: + mod_node = copy.deepcopy(node) + mod_node[k] = replacement + out_nodes.append(mod_node) + + return regex_found, out_nodes + + +def expand_node(node, metrics): + """Expand node definitions with regular expressions. + + Recursively expand a node so that any regular expressions are replaced with + any matching values in the 'metrics' list. + Regular expressions may be present as the value of any key in the + subdictionaries "parameters" or "kwargs". + + Parameters + ---------- + node : dict + A dictionary containing nested dictionaries called "parameters" and, + optionally, "kwargs". + Any of the values in the "parameters" or "kwargs" dictionaries may be + a regular expression, denoted by starting with "^". + metrics : list of str + List of metric names. + """ + + regex_found, out_nodes = expand_dict(node.get("parameters", {}), node, metrics) + if not regex_found: + regex_found, out_nodes = expand_dict(node.get("kwargs", {}), node, metrics) + + if not regex_found: + out_nodes = [copy.deepcopy(node)] + + real_out_nodes = [] + for out_node in out_nodes: + real_out_nodes += expand_node(out_node, metrics) + + return real_out_nodes + + +def expand_nodes(tree, metrics): + """Expand all nodes in a decision tree. + + Parameters + ---------- + tree : dict + A dictionary containing nested dictionaries called "parameters" and, + optionally, "kwargs". + Any of the values in the "parameters" or "kwargs" dictionaries may be + a regular expression, denoted by starting with "^". + metrics : list of str + List of metric names. + """ + expanded_tree = copy.deepcopy(tree) + expanded_tree["nodes"] = [] + for node in expanded_tree["nodes"]: + nodes = expand_node(node, metrics) + expanded_tree["nodes"] += nodes + + return expanded_tree + + def confirm_metrics_exist(component_table, necessary_metrics, function_name=None): """ Confirm that all metrics declared in necessary_metrics are included in comptable. From 4da6730df35827b03c26467621ee65bb93073ffb Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Thu, 8 Feb 2024 16:26:23 -0500 Subject: [PATCH 16/81] Fix up the expansion. --- .../decision_trees/minimal_external1.json | 6 ++-- .../decision_trees/minimal_external2.json | 6 ++-- tedana/selection/selection_utils.py | 32 ++++++++++++++++--- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/tedana/resources/decision_trees/minimal_external1.json b/tedana/resources/decision_trees/minimal_external1.json index 08ecba528..7f351ecf0 100644 --- a/tedana/resources/decision_trees/minimal_external1.json +++ b/tedana/resources/decision_trees/minimal_external1.json @@ -207,11 +207,11 @@ "if_false": "nochange", "decide_comps": "all", "op": ">", - "left": "^.*_correlation$", - "right": 0.2 + "left": "^trans_[x|y|z]_correlation$", + "right": 0.8 }, "kwargs": { - "log_extra_info": "If any external regressor is correlated with the components > 0.2, then reject.", + "log_extra_info": "If translational motion in any direction is correlated with the components > 0.8, then reject.", "log_extra_report": "", "tag_if_true": "Unlikely BOLD" } diff --git a/tedana/resources/decision_trees/minimal_external2.json b/tedana/resources/decision_trees/minimal_external2.json index d66f5508f..d6a5df986 100644 --- a/tedana/resources/decision_trees/minimal_external2.json +++ b/tedana/resources/decision_trees/minimal_external2.json @@ -213,7 +213,7 @@ "right": 0.8 }, "kwargs": { - "log_extra_info": "If translational motion in the X direction is correlated with the components > 0.2, then reject.", + "log_extra_info": "If translational motion in the X direction is correlated with the components > 0.8, then reject.", "log_extra_report": "", "tag_if_true": "Unlikely BOLD" } @@ -229,7 +229,7 @@ "right": 0.8 }, "kwargs": { - "log_extra_info": "If translational motion in the Y direction is correlated with the components > 0.3, then reject.", + "log_extra_info": "If translational motion in the Y direction is correlated with the components > 0.8, then reject.", "log_extra_report": "", "tag_if_true": "Unlikely BOLD" } @@ -245,7 +245,7 @@ "right": 0.8 }, "kwargs": { - "log_extra_info": "If translational motion in the Z direction is correlated with the components > 0.4, then reject.", + "log_extra_info": "If translational motion in the Z direction is correlated with the components > 0.8, then reject.", "log_extra_report": "", "tag_if_true": "Unlikely BOLD" } diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index 827b5555e..dbd5be9b5 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -317,10 +317,32 @@ def clean_dataframe(component_table): ################################################# -def expand_dict(d, node, metrics): +def expand_dict(node, field, metrics): + """Expand a dictionary with regular expressions. + + Parameters + ---------- + node : dict + A dictionary containing nested dictionaries called "parameters" and, + optionally, "kwargs". + Any of the values in the "parameters" or "kwargs" dictionaries may be + a regular expression, denoted by starting with "^". + field : :obj:`str` + The key in the ``node`` dictionary that should be expanded. + metrics : list of str + List of metric names to compare regular expressions against. + + Returns + ------- + regex_found : :obj:`bool` + True if any regular expressions were found in the input dictionary + out_nodes : :obj:`list[dict]` + A list of dictionaries with regular expressions replaced by all + matching values in the 'metrics' list. + """ regex_found = False out_nodes = [] - for k, v in d.items(): + for k, v in node.get(field, {}).items(): if isinstance(v, str) and v.startswith("^"): regex_found = True replacements = [metric for metric in metrics if re.match(v, metric)] @@ -354,9 +376,9 @@ def expand_node(node, metrics): List of metric names. """ - regex_found, out_nodes = expand_dict(node.get("parameters", {}), node, metrics) + regex_found, out_nodes = expand_dict(node, "parameters", metrics) if not regex_found: - regex_found, out_nodes = expand_dict(node.get("kwargs", {}), node, metrics) + regex_found, out_nodes = expand_dict(node, "kwargs", metrics) if not regex_found: out_nodes = [copy.deepcopy(node)] @@ -383,7 +405,7 @@ def expand_nodes(tree, metrics): """ expanded_tree = copy.deepcopy(tree) expanded_tree["nodes"] = [] - for node in expanded_tree["nodes"]: + for i_node, node in enumerate(tree["nodes"]): nodes = expand_node(node, metrics) expanded_tree["nodes"] += nodes From bed401e60343bb9ef7d5c09cd2ef024115dfea88 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Thu, 8 Feb 2024 16:44:18 -0500 Subject: [PATCH 17/81] Really fix it though. --- tedana/selection/selection_utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index dbd5be9b5..6815e8b85 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -350,8 +350,9 @@ def expand_dict(node, field, metrics): raise ValueError(f"No metrics matching regex '{v}' found.") for replacement in replacements: + LGR.warning(f"Replacing {v} with {replacement}") mod_node = copy.deepcopy(node) - mod_node[k] = replacement + mod_node[field][k] = replacement out_nodes.append(mod_node) return regex_found, out_nodes @@ -381,7 +382,9 @@ def expand_node(node, metrics): regex_found, out_nodes = expand_dict(node, "kwargs", metrics) if not regex_found: + # Stop early and just return the node if no regular expressions were found out_nodes = [copy.deepcopy(node)] + return out_nodes real_out_nodes = [] for out_node in out_nodes: @@ -405,7 +408,7 @@ def expand_nodes(tree, metrics): """ expanded_tree = copy.deepcopy(tree) expanded_tree["nodes"] = [] - for i_node, node in enumerate(tree["nodes"]): + for node in tree["nodes"]: nodes = expand_node(node, metrics) expanded_tree["nodes"] += nodes From 3a03d546890d691c8278f212ffff159f93bd1ae1 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Thu, 8 Feb 2024 16:46:39 -0500 Subject: [PATCH 18/81] Fix style issue. --- tedana/selection/selection_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index 6815e8b85..1d6e45e41 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -376,7 +376,6 @@ def expand_node(node, metrics): metrics : list of str List of metric names. """ - regex_found, out_nodes = expand_dict(node, "parameters", metrics) if not regex_found: regex_found, out_nodes = expand_dict(node, "kwargs", metrics) From 30da78325c4fc14cd4ce7e3f89db13e45f13420c Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Fri, 9 Feb 2024 12:57:30 -0500 Subject: [PATCH 19/81] Added external regress integration test --- tedana/tests/data/external_regress_3echo.tsv | 76 ++++++++++++++++++++ tedana/tests/test_integration.py | 46 +++++++++++- 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 tedana/tests/data/external_regress_3echo.tsv diff --git a/tedana/tests/data/external_regress_3echo.tsv b/tedana/tests/data/external_regress_3echo.tsv new file mode 100644 index 000000000..5d024b899 --- /dev/null +++ b/tedana/tests/data/external_regress_3echo.tsv @@ -0,0 +1,76 @@ +ICA43_noise_correlation ICA13_46_noise_correlation noise3_correlation noise4_correlation noise5_correlation noise6_correlation +0.549293545 0.250215878 -0.096477741 -1.80783834 0.776916736 0.065907176 +-0.396270561 -1.075620648 -0.600756113 0.711980905 -2.094919593 0.334240442 +0.067844834 -1.914881602 -1.970173156 1.12064652 0.012585975 -0.18755923 +-1.283188787 0.054245044 1.14112755 0.912449393 0.947398946 -0.34724064 +-0.388310083 -0.201996761 -0.814660276 -1.246250547 0.01160742 0.177643509 +-0.314573738 -0.361462669 0.999582139 3.166981439 -2.239632506 -1.033615113 +0.101103164 -0.936718776 -0.644072044 -1.141532825 -0.796738367 0.96054235 +0.165699577 -2.036858477 -0.10279295 1.536579074 0.319257456 1.715921565 +0.098109299 0.585901281 1.038573353 0.260447863 0.690166124 -0.161151162 +-0.781437441 1.818503746 -0.571917548 0.764737589 0.337003285 0.219660727 +-1.247637744 0.880593032 -0.524207554 -0.005374187 0.613769893 1.00302635 +-1.504675781 0.57410212 1.748392962 -0.534046731 0.350429392 -0.02658593 +0.112585463 0.262920242 0.772176539 -0.304480416 -0.544666476 -2.018629288 +-0.214374526 -0.293654406 -0.852332991 -0.573577571 1.715766493 -0.570580836 +-1.412780197 0.717905407 1.674133803 -0.311623303 0.612719298 -0.317575002 +-1.637689411 -0.596273649 -1.771748186 0.404446383 0.309263982 1.58636444 +1.713536945 -2.222086879 -0.804411903 -0.477431811 -0.320290214 -1.431562416 +0.056652762 -1.146727579 -0.420174664 -0.24501264 -0.414803497 -0.551047062 +0.081061898 -0.190862044 -0.509884094 -0.087111839 1.330026777 -1.629232182 +-0.05471883 -2.039102734 1.063635032 -0.84168961 -0.936894399 0.010683903 +0.00341481 -0.751533202 -0.637007659 0.971295125 1.1034672 2.277101592 +-0.117280066 0.090557218 2.285051289 -0.990407484 0.387670575 -1.243539605 +-0.911468035 -0.405366659 -0.86135508 1.858488511 -1.624474821 0.855144148 +-2.919106725 -0.365701943 0.780194847 0.348072422 -1.446574768 1.996965515 +0.281485374 0.003853161 -0.476169965 0.491866937 0.986765303 1.392489464 +-1.084777223 1.660531932 -0.407566919 -0.541167006 0.210902635 0.500097173 +-0.064159853 -1.207447601 -1.344501001 0.813093122 0.577649069 -0.588292507 +3.26877146 0.24295461 1.701957074 -1.700709039 -1.054462417 -1.157364526 +0.73738587 -1.546803807 -1.039000906 0.441813077 0.389006305 -0.701659285 +1.017508956 -3.145106952 -1.432274071 -0.387969456 1.814374372 2.721588712 +0.702045784 1.0541015 -0.041400816 0.501746728 -0.346236313 1.364195691 +-1.419460992 1.073143223 0.085531087 -2.08485422 0.549404508 -0.691495392 +1.821296986 -0.44159073 -0.075751365 0.531900783 -0.914148019 -0.579862985 +1.155084509 -0.099414478 -0.181823723 1.861443249 0.38605948 1.098281482 +-2.081874556 1.30194697 0.03780041 -1.091799124 0.81590004 0.561287461 +-2.447522949 1.621028133 2.158562029 0.644839134 0.235545151 -1.537146216 +2.349365117 0.822381838 1.101777472 -1.351665553 -0.596002084 0.844707117 +-1.216700364 1.569203014 -0.181325001 -0.187210652 0.618574805 -0.551658813 +1.078449117 1.663089223 -0.852101491 1.504903105 -0.722664474 0.691077035 +-0.753595892 -0.195027075 -0.359054932 -0.101472668 0.265806924 0.171246282 +-0.753382247 -0.440831457 -1.73580817 1.519237631 0.333876385 0.071902534 +1.440275154 -1.839893631 -0.885864835 0.820333353 -1.134197148 0.811506617 +-0.49603701 -1.293510593 0.445916421 0.689065776 -0.920792319 -0.38826739 +1.70718954 0.881044892 1.227161093 -0.237001451 -1.274266117 -0.640207348 +2.545373209 0.484761222 -0.569791348 0.359079485 0.343269482 0.773094428 +2.326650266 1.79716478 1.185504303 -1.756026474 0.627149195 1.108500718 +0.06892165 -0.000433049 -0.384900123 0.716903493 1.043301526 -1.899577465 +0.83595833 -0.849641986 -1.540499879 0.012335465 0.639580402 0.37445904 +-0.399846825 1.182312694 -0.967769768 -0.234147135 0.619768679 0.510283723 +-1.260081111 1.183503527 -0.765912971 -1.219338608 0.193656342 -1.449519314 +-0.605117081 -1.748286668 -0.979099019 0.384635208 0.578437602 0.056570781 +0.102038162 0.105405255 -0.560792398 -0.497943165 -1.025595788 -0.779721032 +0.448149577 -0.734202714 -0.41299561 -1.237303422 0.478066011 0.663720062 +0.450171003 0.002295622 -0.186952061 0.453439985 1.576647524 0.972136031 +-2.086453032 0.073190854 0.697622218 0.581972715 -0.656939108 0.271255055 +-1.944469003 -0.462552709 -0.673663485 -2.370164868 0.078187027 -0.965063082 +0.020457103 -0.688500083 0.061140466 1.239428774 -0.495915846 -1.111855594 +-1.502442927 1.189886753 0.952696685 0.566804848 0.172804195 -0.343963186 +-1.109334787 -1.363045959 1.451996437 0.522713225 1.061820565 -1.194576515 +0.620881989 0.631886329 -1.635025528 -2.079217436 0.894245916 -0.237997758 +0.015074957 -0.479485927 -0.54738094 -0.880379715 -1.230550351 0.57029003 +-0.712767625 -1.21814155 1.727036448 0.466334648 -0.327364513 0.309703656 +-1.224379717 0.715588731 0.113383657 0.779286 0.331314356 0.96101091 +0.995956228 0.191542938 0.49404847 -1.596474231 2.005607683 -1.425185272 +-1.399585534 -0.994363024 0.868392709 0.43096458 -0.71622594 -0.657267793 +1.150429535 -0.473643294 -1.210506332 -0.5088913 -0.763710142 -0.945984595 +1.999913996 -0.373711389 -1.116366225 -1.26229449 1.000404251 -0.191606881 +-1.208159173 0.962186501 0.114391665 0.423945443 -0.143560743 1.577180213 +-2.721222071 -0.968088703 2.186923001 -1.428910307 0.468760454 -1.450511079 +0.453155401 0.860313854 0.263699038 -0.540525262 0.043310305 0.305514675 +0.245875443 0.979793832 -0.410556081 -0.792951087 -1.118628988 -0.461609398 +1.15435361 1.067049756 0.931143726 -0.499062124 -1.084047235 0.705477191 +-0.105821873 1.845510017 0.822345061 -0.859892823 1.931309971 0.75920939 +-1.798901926 0.119045486 0.736093085 0.668628728 -1.539736171 0.187968738 +2.100677872 -0.01157544 0.460400408 -0.589231003 0.049676974 -0.663885275 diff --git a/tedana/tests/test_integration.py b/tedana/tests/test_integration.py index 59695a939..5a5124f59 100644 --- a/tedana/tests/test_integration.py +++ b/tedana/tests/test_integration.py @@ -133,7 +133,8 @@ def data_for_testing_info(test_dataset=str): def download_test_data(osf_id, test_data_path): """ - If current data is not already available, downloads tar.gz data + If current data is not already available, downloads tar.gz data. + stored at `https://osf.io/osf_id/download`. and unpacks into `out_path`. @@ -374,6 +375,49 @@ def test_integration_three_echo(skip_integration): check_integration_outputs(fn, out_dir) +def test_integration_three_echo_external_regressors(skip_integration): + """Integration test of tedana workflow. + + Three-echo test data & external regressors. + """ + + if skip_integration: + pytest.skip("Skipping three-echo with external regressors integration test") + + test_data_path, osf_id = data_for_testing_info("three-echo") + out_dir = os.path.abspath(os.path.join(test_data_path, "../../outputs/three-echo-externalreg")) + out_dir_manual = f"{out_dir}-rerun" + + if os.path.exists(out_dir): + shutil.rmtree(out_dir) + + if os.path.exists(out_dir_manual): + shutil.rmtree(out_dir_manual) + + # download data and run the test + # external_regress_3echo.tsv has 6 rows. Based on a local run on the 3 echo data: + # Col 1 is the time series for component 43 + similar stdev Gaussian Noise + # Col 2 is 0.4*comp46+0.5+comp13+Gaussian Noise + # Col 4-6 are Gaussian Noise + # While this integration test is not currently checking outputs, there should be + # three more rejected components compared to what was see in the 3 echo integration + # test without external regressors + download_test_data(osf_id, test_data_path) + tedana_cli.tedana_workflow( + data=f"{test_data_path}/three_echo_Cornell_zcat.nii.gz", + tes=[14.5, 38.5, 62.5], + out_dir=out_dir, + tree=resource_filename("tedana", "resources/decision_trees/minimal_external1.json"), + external_regressors=resource_filename("tedana", "tests/data/external_regress_3echo.tsv"), + low_mem=True, + tedpca="aic", + ) + + # compare the generated output files + fn = resource_filename("tedana", "tests/data/cornell_three_echo_outputs.txt") + check_integration_outputs(fn, out_dir) + + def test_integration_reclassify_insufficient_args(skip_integration): if skip_integration: pytest.skip("Skipping reclassify insufficient args") From 564886b87cd8ccea0f592834ecdca4f7015ce4cc Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Fri, 9 Feb 2024 14:42:32 -0500 Subject: [PATCH 20/81] Got intregration test with external regressors working --- .../decision_trees/minimal_external1.json | 2 +- .../decision_trees/minimal_external2.json | 6 +- tedana/tests/data/external_regress_3echo.tsv | 152 +++++++++--------- tedana/tests/test_integration.py | 17 +- 4 files changed, 90 insertions(+), 87 deletions(-) diff --git a/tedana/resources/decision_trees/minimal_external1.json b/tedana/resources/decision_trees/minimal_external1.json index 7f351ecf0..1385cd5e1 100644 --- a/tedana/resources/decision_trees/minimal_external1.json +++ b/tedana/resources/decision_trees/minimal_external1.json @@ -213,7 +213,7 @@ "kwargs": { "log_extra_info": "If translational motion in any direction is correlated with the components > 0.8, then reject.", "log_extra_report": "", - "tag_if_true": "Unlikely BOLD" + "tag_if_true": "corr to translational motion" } }, { diff --git a/tedana/resources/decision_trees/minimal_external2.json b/tedana/resources/decision_trees/minimal_external2.json index d6a5df986..62e01ca04 100644 --- a/tedana/resources/decision_trees/minimal_external2.json +++ b/tedana/resources/decision_trees/minimal_external2.json @@ -215,7 +215,7 @@ "kwargs": { "log_extra_info": "If translational motion in the X direction is correlated with the components > 0.8, then reject.", "log_extra_report": "", - "tag_if_true": "Unlikely BOLD" + "tag_if_true": "corr to X translational motion" } }, { @@ -231,7 +231,7 @@ "kwargs": { "log_extra_info": "If translational motion in the Y direction is correlated with the components > 0.8, then reject.", "log_extra_report": "", - "tag_if_true": "Unlikely BOLD" + "tag_if_true": "corr to Y translational motion" } }, { @@ -247,7 +247,7 @@ "kwargs": { "log_extra_info": "If translational motion in the Z direction is correlated with the components > 0.8, then reject.", "log_extra_report": "", - "tag_if_true": "Unlikely BOLD" + "tag_if_true": "corr to Z translational motion" } }, { diff --git a/tedana/tests/data/external_regress_3echo.tsv b/tedana/tests/data/external_regress_3echo.tsv index 5d024b899..d551e5ebd 100644 --- a/tedana/tests/data/external_regress_3echo.tsv +++ b/tedana/tests/data/external_regress_3echo.tsv @@ -1,76 +1,76 @@ -ICA43_noise_correlation ICA13_46_noise_correlation noise3_correlation noise4_correlation noise5_correlation noise6_correlation -0.549293545 0.250215878 -0.096477741 -1.80783834 0.776916736 0.065907176 --0.396270561 -1.075620648 -0.600756113 0.711980905 -2.094919593 0.334240442 -0.067844834 -1.914881602 -1.970173156 1.12064652 0.012585975 -0.18755923 --1.283188787 0.054245044 1.14112755 0.912449393 0.947398946 -0.34724064 --0.388310083 -0.201996761 -0.814660276 -1.246250547 0.01160742 0.177643509 --0.314573738 -0.361462669 0.999582139 3.166981439 -2.239632506 -1.033615113 -0.101103164 -0.936718776 -0.644072044 -1.141532825 -0.796738367 0.96054235 -0.165699577 -2.036858477 -0.10279295 1.536579074 0.319257456 1.715921565 -0.098109299 0.585901281 1.038573353 0.260447863 0.690166124 -0.161151162 --0.781437441 1.818503746 -0.571917548 0.764737589 0.337003285 0.219660727 --1.247637744 0.880593032 -0.524207554 -0.005374187 0.613769893 1.00302635 --1.504675781 0.57410212 1.748392962 -0.534046731 0.350429392 -0.02658593 -0.112585463 0.262920242 0.772176539 -0.304480416 -0.544666476 -2.018629288 --0.214374526 -0.293654406 -0.852332991 -0.573577571 1.715766493 -0.570580836 --1.412780197 0.717905407 1.674133803 -0.311623303 0.612719298 -0.317575002 --1.637689411 -0.596273649 -1.771748186 0.404446383 0.309263982 1.58636444 -1.713536945 -2.222086879 -0.804411903 -0.477431811 -0.320290214 -1.431562416 -0.056652762 -1.146727579 -0.420174664 -0.24501264 -0.414803497 -0.551047062 -0.081061898 -0.190862044 -0.509884094 -0.087111839 1.330026777 -1.629232182 --0.05471883 -2.039102734 1.063635032 -0.84168961 -0.936894399 0.010683903 -0.00341481 -0.751533202 -0.637007659 0.971295125 1.1034672 2.277101592 --0.117280066 0.090557218 2.285051289 -0.990407484 0.387670575 -1.243539605 --0.911468035 -0.405366659 -0.86135508 1.858488511 -1.624474821 0.855144148 --2.919106725 -0.365701943 0.780194847 0.348072422 -1.446574768 1.996965515 -0.281485374 0.003853161 -0.476169965 0.491866937 0.986765303 1.392489464 --1.084777223 1.660531932 -0.407566919 -0.541167006 0.210902635 0.500097173 --0.064159853 -1.207447601 -1.344501001 0.813093122 0.577649069 -0.588292507 -3.26877146 0.24295461 1.701957074 -1.700709039 -1.054462417 -1.157364526 -0.73738587 -1.546803807 -1.039000906 0.441813077 0.389006305 -0.701659285 -1.017508956 -3.145106952 -1.432274071 -0.387969456 1.814374372 2.721588712 -0.702045784 1.0541015 -0.041400816 0.501746728 -0.346236313 1.364195691 --1.419460992 1.073143223 0.085531087 -2.08485422 0.549404508 -0.691495392 -1.821296986 -0.44159073 -0.075751365 0.531900783 -0.914148019 -0.579862985 -1.155084509 -0.099414478 -0.181823723 1.861443249 0.38605948 1.098281482 --2.081874556 1.30194697 0.03780041 -1.091799124 0.81590004 0.561287461 --2.447522949 1.621028133 2.158562029 0.644839134 0.235545151 -1.537146216 -2.349365117 0.822381838 1.101777472 -1.351665553 -0.596002084 0.844707117 --1.216700364 1.569203014 -0.181325001 -0.187210652 0.618574805 -0.551658813 -1.078449117 1.663089223 -0.852101491 1.504903105 -0.722664474 0.691077035 --0.753595892 -0.195027075 -0.359054932 -0.101472668 0.265806924 0.171246282 --0.753382247 -0.440831457 -1.73580817 1.519237631 0.333876385 0.071902534 -1.440275154 -1.839893631 -0.885864835 0.820333353 -1.134197148 0.811506617 --0.49603701 -1.293510593 0.445916421 0.689065776 -0.920792319 -0.38826739 -1.70718954 0.881044892 1.227161093 -0.237001451 -1.274266117 -0.640207348 -2.545373209 0.484761222 -0.569791348 0.359079485 0.343269482 0.773094428 -2.326650266 1.79716478 1.185504303 -1.756026474 0.627149195 1.108500718 -0.06892165 -0.000433049 -0.384900123 0.716903493 1.043301526 -1.899577465 -0.83595833 -0.849641986 -1.540499879 0.012335465 0.639580402 0.37445904 --0.399846825 1.182312694 -0.967769768 -0.234147135 0.619768679 0.510283723 --1.260081111 1.183503527 -0.765912971 -1.219338608 0.193656342 -1.449519314 --0.605117081 -1.748286668 -0.979099019 0.384635208 0.578437602 0.056570781 -0.102038162 0.105405255 -0.560792398 -0.497943165 -1.025595788 -0.779721032 -0.448149577 -0.734202714 -0.41299561 -1.237303422 0.478066011 0.663720062 -0.450171003 0.002295622 -0.186952061 0.453439985 1.576647524 0.972136031 --2.086453032 0.073190854 0.697622218 0.581972715 -0.656939108 0.271255055 --1.944469003 -0.462552709 -0.673663485 -2.370164868 0.078187027 -0.965063082 -0.020457103 -0.688500083 0.061140466 1.239428774 -0.495915846 -1.111855594 --1.502442927 1.189886753 0.952696685 0.566804848 0.172804195 -0.343963186 --1.109334787 -1.363045959 1.451996437 0.522713225 1.061820565 -1.194576515 -0.620881989 0.631886329 -1.635025528 -2.079217436 0.894245916 -0.237997758 -0.015074957 -0.479485927 -0.54738094 -0.880379715 -1.230550351 0.57029003 --0.712767625 -1.21814155 1.727036448 0.466334648 -0.327364513 0.309703656 --1.224379717 0.715588731 0.113383657 0.779286 0.331314356 0.96101091 -0.995956228 0.191542938 0.49404847 -1.596474231 2.005607683 -1.425185272 --1.399585534 -0.994363024 0.868392709 0.43096458 -0.71622594 -0.657267793 -1.150429535 -0.473643294 -1.210506332 -0.5088913 -0.763710142 -0.945984595 -1.999913996 -0.373711389 -1.116366225 -1.26229449 1.000404251 -0.191606881 --1.208159173 0.962186501 0.114391665 0.423945443 -0.143560743 1.577180213 --2.721222071 -0.968088703 2.186923001 -1.428910307 0.468760454 -1.450511079 -0.453155401 0.860313854 0.263699038 -0.540525262 0.043310305 0.305514675 -0.245875443 0.979793832 -0.410556081 -0.792951087 -1.118628988 -0.461609398 -1.15435361 1.067049756 0.931143726 -0.499062124 -1.084047235 0.705477191 --0.105821873 1.845510017 0.822345061 -0.859892823 1.931309971 0.75920939 --1.798901926 0.119045486 0.736093085 0.668628728 -1.539736171 0.187968738 -2.100677872 -0.01157544 0.460400408 -0.589231003 0.049676974 -0.663885275 +trans_x trans_y trans_z +-0.329580266 -0.844918633 1.626381228 +-1.520650288 -2.13455262 -0.882532519 +-0.511890744 -0.711229882 0.276263262 +-2.06545787 -0.40815265 1.038928803 +-1.565465426 -0.289605838 0.551574072 +-0.583901238 -0.45316118 0.882519392 +0.225980497 -1.627540572 -0.68542407 +0.560677621 -1.489408639 -0.595520306 +0.003248201 -0.340159692 -0.879529088 +0.836161459 -1.092255501 -2.618392652 +1.586566953 0.271349643 -0.649296386 +0.667602572 1.549333738 0.076243832 +-0.01792122 2.740600234 0.074586135 +-0.659019247 2.298595672 0.17634643 +-1.094112767 -0.186225918 -2.127567341 +0.248649215 -0.618039127 -1.082778413 +-0.034109838 0.196157196 0.285049845 +-0.154408808 0.946079652 1.025632299 +0.764863858 0.933908359 -0.135353281 +0.445796508 1.917424429 0.650347848 +0.207513285 1.298735387 2.318083224 +0.381782634 0.081083807 -1.984367893 +-1.47473253 0.515676307 1.024256498 +-1.494898436 0.766171566 1.630635233 +-2.019896907 1.643560771 -0.311565305 +-2.178716752 0.973512277 2.252419221 +-1.232037427 -0.40191649 1.00829017 +0.731522293 0.40813286 1.025669565 +0.753719194 0.163556595 -1.064627798 +1.757683518 -0.537743502 -0.866195997 +1.550719352 0.11832591 -0.668387479 +2.288307277 0.791999772 0.75152785 +2.663681819 2.261180081 1.905593875 +2.268142799 0.717724473 -2.382603885 +-0.218049277 -0.508412721 -2.487404957 +-0.410793483 0.116533685 1.879092728 +-0.647691228 0.838129685 1.374778227 +-1.13985429 -0.495903004 1.056569633 +0.53999669 -0.487881558 0.581258434 +0.410905902 0.873257327 3.608291808 +-0.173062544 0.529483977 1.076381362 +0.590240338 -0.560105813 -0.33143028 +-0.330152172 -0.29496616 -1.27480681 +-0.770820846 -1.130215098 -0.533069878 +-0.997000751 -1.594080868 -2.206716323 +-0.078723051 -1.034723379 1.715724783 +0.317741611 -1.449732445 -0.829499847 +0.920629978 -3.107612041 -1.72016277 +0.904171949 -3.25810681 -1.338419095 +0.712432166 -2.702026568 -4.090712476 +1.213580415 -2.416744199 -1.644462247 +1.015276471 -0.468506664 0.826022926 +0.458644239 0.304782879 -0.286629392 +0.696387231 0.478940055 -0.661704616 +0.398048005 0.35203805 -0.836607472 +-0.433479435 0.130772773 -0.702912597 +-1.504705013 -0.117524037 -1.172268361 +-1.966296873 -1.284318477 -0.647627254 +-1.036674723 -0.437122887 0.788299251 +-0.696570694 1.035997759 0.315168026 +-1.056655551 2.054949636 1.733679189 +-1.253872449 0.940519962 -1.859117584 +-1.069287813 -0.5172584 -0.656338228 +0.073889742 1.015905744 3.017531937 +1.32888943 2.850501828 2.785235104 +0.831392436 2.255038285 0.082410724 +-0.670035292 0.826737793 0.321644476 +-0.478355817 0.188711887 -0.630649751 +0.999877364 -1.06986647 1.290972976 +0.069018404 0.516767176 1.573029961 +0.351758996 0.056295399 1.665144379 +0.858424569 -0.363527343 1.550740147 +1.334779099 -0.553036015 0.026277298 +0.365467586 -1.460953257 1.836000417 +-0.084225355 -0.90165276 0.41416722 diff --git a/tedana/tests/test_integration.py b/tedana/tests/test_integration.py index 5a5124f59..41beb7eb7 100644 --- a/tedana/tests/test_integration.py +++ b/tedana/tests/test_integration.py @@ -396,18 +396,21 @@ def test_integration_three_echo_external_regressors(skip_integration): # download data and run the test # external_regress_3echo.tsv has 6 rows. Based on a local run on the 3 echo data: - # Col 1 is the time series for component 43 + similar stdev Gaussian Noise - # Col 2 is 0.4*comp46+0.5+comp13+Gaussian Noise - # Col 4-6 are Gaussian Noise - # While this integration test is not currently checking outputs, there should be - # three more rejected components compared to what was see in the 3 echo integration - # test without external regressors + # Col 1 (trans_x_correlation) is the TS for ICA comp 59 + similar stdev Gaussian Noise + # Col 2 (trans_y_correlation) is 0.4*comp29+0.5+comp20+Gaussian Noise + # Col 3 (trans_z_correlation) is comp20+Gaussian Noise + # With the currently set up decision tree minimal_exteral 2, one component (my 59) + # should be rejected, but 20 and 29 aren't rejected because neither crosses the + # r>0.8 threshold. If trans_y and trans_Z were included in a single model then + # component 20 would have been rejected + # Note that the above is in comparision to the minimal decision tree + # but the integration test for 3 echoes uses the kundu tree download_test_data(osf_id, test_data_path) tedana_cli.tedana_workflow( data=f"{test_data_path}/three_echo_Cornell_zcat.nii.gz", tes=[14.5, 38.5, 62.5], out_dir=out_dir, - tree=resource_filename("tedana", "resources/decision_trees/minimal_external1.json"), + tree=resource_filename("tedana", "resources/decision_trees/minimal_external2.json"), external_regressors=resource_filename("tedana", "tests/data/external_regress_3echo.tsv"), low_mem=True, tedpca="aic", From 2d3c1da549a758ac8578fff2b56e16d81a9a1c4e Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Wed, 14 Feb 2024 19:05:18 -0500 Subject: [PATCH 21/81] Added F tests and options --- docs/api.rst | 1 + tedana/decomposition/pca.py | 1 + tedana/metrics/collect.py | 33 +- tedana/metrics/external.py | 24 - tedana/metrics/external_regressor_fits.py | 751 ++++++++++++++++++ tedana/resources/config/metrics.json | 5 +- .../decision_trees/minimal_external3.json | 251 ++++++ .../extern_regress_dicts/Fmodel.json | 6 + .../extern_regress_dicts/Mot12_CSF.json | 27 + .../extern_regress_dicts/corr_detrend.json | 6 + tedana/selection/selection_nodes.py | 12 +- tedana/selection/selection_utils.py | 6 +- tedana/selection/tedica.py | 5 +- .../data/external_regress_Ftest_3echo.csv | 76 ++ .../data/external_regress_Ftest_3echo.tsv | 76 ++ ...ho.tsv => external_regress_corr_3echo.tsv} | 0 tedana/tests/test_integration.py | 70 +- tedana/workflows/tedana.py | 41 +- 18 files changed, 1326 insertions(+), 65 deletions(-) delete mode 100644 tedana/metrics/external.py create mode 100644 tedana/metrics/external_regressor_fits.py create mode 100644 tedana/resources/decision_trees/minimal_external3.json create mode 100644 tedana/resources/extern_regress_dicts/Fmodel.json create mode 100644 tedana/resources/extern_regress_dicts/Mot12_CSF.json create mode 100644 tedana/resources/extern_regress_dicts/corr_detrend.json create mode 100644 tedana/tests/data/external_regress_Ftest_3echo.csv create mode 100644 tedana/tests/data/external_regress_Ftest_3echo.tsv rename tedana/tests/data/{external_regress_3echo.tsv => external_regress_corr_3echo.tsv} (100%) diff --git a/docs/api.rst b/docs/api.rst index c91211989..5eb019c3c 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -105,6 +105,7 @@ API tedana.metrics.collect tedana.metrics.dependence + tedana.metrics.external_regressor_fits .. _api_selection_ref: diff --git a/tedana/decomposition/pca.py b/tedana/decomposition/pca.py index c8e3aa34b..1b8d61139 100644 --- a/tedana/decomposition/pca.py +++ b/tedana/decomposition/pca.py @@ -348,6 +348,7 @@ def tedpca( "normalized variance explained", "d_table_score", ] + # Even if user inputted, don't fit external_regressors to PCA components comptable = metrics.collect.generate_metrics( data_cat=data_cat, data_optcom=data_oc, diff --git a/tedana/metrics/collect.py b/tedana/metrics/collect.py index d384d6ffa..e63bd7a1b 100644 --- a/tedana/metrics/collect.py +++ b/tedana/metrics/collect.py @@ -7,7 +7,7 @@ import pandas as pd from tedana import io, utils -from tedana.metrics import dependence, external +from tedana.metrics import dependence, external_regressor_fits from tedana.metrics._utils import dependency_resolver, determine_signs, flip_components from tedana.stats import getfbounds @@ -25,6 +25,7 @@ def generate_metrics( io_generator, label, external_regressors=None, + external_regressor_dict=None, metrics=None, ): """Fit TE-dependence and -independence models to components. @@ -52,6 +53,8 @@ def generate_metrics( external_regressors : None or :obj:`pandas.DataFrame`, optional External regressors (e.g., motion parameters, physiological noise) to correlate with ICA components. If None, no external regressor metrics will be calculated. + external_regressor_dict : :obj:`dict` + A dictionary for defining how to fit external regressors to component time series metrics : list List of metrics to return @@ -67,6 +70,15 @@ def generate_metrics( if metrics is None: metrics = ["map weight"] + + if external_regressors is not None: + if external_regressor_dict is None: + raise ValueError( + "If external_regressors is defined, then " + "external_regressor_dict also needs to be defined." + ) + metrics.append("external fit") + RepLGR.info(f"The following metrics were calculated: {', '.join(metrics)}.") if not (data_cat.shape[0] == data_optcom.shape[0] == adaptive_mask.shape[0]): @@ -326,13 +338,16 @@ def generate_metrics( ) # External regressor-based metrics - if "external correlation" in required_metrics: - external_regressor_names = external_regressors.columns.tolist() - LGR.info("Calculating external regressor correlations") - for col in external_regressor_names: - external_regressor_arr = external_regressors[col].values - corrs = external.correlate_regressor(external_regressor_arr, mixing) - comptable[f"{col}_correlation"] = corrs + if "external fit" in required_metrics: + # external_regressor_names = external_regressors.columns.tolist() + LGR.info("Calculating external regressor fits") + comptable = external_regressor_fits.fit_regressors( + comptable, external_regressors, external_regressor_dict, mixing + ) + # for col in external_regressor_names: + # external_regressor_arr = external_regressors[col].values + # corrs = external_regressors.correlate_regressor(external_regressor_arr, mixing) + # comptable[f"{col}_correlation"] = corrs # Write verbose metrics if needed if io_generator.verbose: @@ -388,7 +403,7 @@ def generate_metrics( "d_table_score", "kappa ratio", "d_table_score_scrub", - "external correlation", + "external fit", "classification", "rationale", ) diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py deleted file mode 100644 index b2dc4bb1f..000000000 --- a/tedana/metrics/external.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Metrics unrelated to TE-(in)dependence.""" - -import numpy as np - - -def correlate_regressor(external_regressor, mixing): - """Correlate external regressors with mixing components. - - Parameters - ---------- - external_regressor : array, shape (n_samples) - External regressor. The regressor will be correlated with each component's time series. - mixing : array, shape (n_samples, n_components) - Mixing matrix from ICA. - - Returns - ------- - corrs : array, shape (n_components) - Absolute correlations between external regressor and mixing components. - """ - assert external_regressor.ndim == 1 - assert external_regressor.shape[0] == mixing.shape[0] - corrs = np.abs(np.corrcoef(external_regressor, mixing.T)[0, 1:]) - return corrs diff --git a/tedana/metrics/external_regressor_fits.py b/tedana/metrics/external_regressor_fits.py new file mode 100644 index 000000000..4b77aa735 --- /dev/null +++ b/tedana/metrics/external_regressor_fits.py @@ -0,0 +1,751 @@ +"""Metrics unrelated to TE-(in)dependence.""" + +import logging +import os.path as op + +import numpy as np +import pandas as pd +from scipy import linalg, stats + +from tedana.io import load_json +from tedana.utils import get_resource_path + +LGR = logging.getLogger("GENERAL") +RepLGR = logging.getLogger("REPORT") + +DEFAULT_REGRESSOR_DICTS = ["Mot12_CSF", "Fmodel", "corr_detrend", "corr"] + + +class RegressError(Exception): + """Passes errors that are raised when `validate_extern_regress` fails.""" + + pass + + +def load_validate_external_regressors(external_regressors, external_regressor_dict, n_time): + """ + Load and validate external regressors and descriptors in dictionary. + + Parameters + ---------- + external_regressors: :obj:`str` + Path and name of tsv file that includes external regressor time series + external_regressor_dict: :obj:`str` + An included dictionary name or path to a JSON file that defines one. + This contains expected column labels for the regressors, + how the regressors can be grouped together, and what statistical + tests to use. + n_time: :obj:`int` + Number of timepoints in the fMRI time series + + Returns + ------- + external_regressors: :obj:`pandas.DataFrame` + Each column is a labelled regressor and the number of rows should + match the number of timepoints in the fMRI time series + external_regressor_dict : :obj:`dict` + A validated dictionary with info for fitting external regressors + to component time series + """ + LGR.info(f"Loading external regressor dictionary: {external_regressor_dict}") + external_regressor_dict = load_external_regressor_dict(external_regressor_dict) + try: + external_regressors = pd.read_table(external_regressors) + except FileNotFoundError: + raise ValueError(f"Cannot load tsv file with external regressors: {external_regressors}") + + validate_extern_regress(external_regressors, external_regressor_dict, n_time) + + return external_regressors, external_regressor_dict + + +def load_external_regressor_dict(external_regressor_dict): + """ + Load and validate external regressor dictionary. + + Parameters + ---------- + An included dictionary name or path to a JSON file that defines one. + This contains expected column labels for the regressors, + how the regressors can be grouped together, and what statistical + tests to use. + + Returns + ------- + external_regressor_dict : :obj:`dict` + A validated dictionary for fitting external regressors to component time series + """ + if external_regressor_dict in DEFAULT_REGRESSOR_DICTS: + fname = op.join( + get_resource_path(), "extern_regress_dicts", external_regressor_dict + ".json" + ) + else: + fname = external_regressor_dict + + try: + external_regressor_dict = load_json(fname) + except FileNotFoundError: + raise ValueError( + f"Cannot find external regressor dictionary {external_regressor_dict}. " + "Please check your path or use a " + f"default dictionary: ({DEFAULT_REGRESSOR_DICTS})." + ) + except IsADirectoryError: + raise ValueError( + f"{external_regressor_dict} is a directory. Please supply a JSON file or " + f"default dictionary: ({DEFAULT_REGRESSOR_DICTS})." + ) + + return external_regressor_dict + + +def validate_extern_regress(external_regressors, external_regressor_dict, n_time): + """ + Confirm that provided external regressor dictionary is valid and matches data. + + Checks if expected keys are in external_regressor_dict. + Checks if any regressor labels in the dictionary are specified in the + user-defined external_regressors + Checks if the number of time points in the external regressors matches + the number of time point in the fMRI data + + Parameters + ---------- + external_regressors : :obj:`pandas.DataFrame` + Each column is a labelled regressor and the number of rows should + match the number of timepoints in the fMRI time series + external_regressor_dict : :obj:`dict` + Information describing the external regressors and + method to use for fitting and statistical tests + n_time : :obj:`int` + The number of time point in the fMRI time series + + Raises + ------ + RegressorError if any validation test fails + """ + # err_msg is used to collect all errors in validation rather than require this to be run + # multiple times to see all validation errors. Will either collect errors and raise at the + # end of the function or raise errors that prevent the rest of the function from completing + err_msg = "" + + # Set the fields that should always be present + dict_expected_keys = set(["regess_ID", "info", "detrend", "calc_stats"]) + calc_stats_key_options = set(["f", "corr"]) + # defaults = {"selector", "decision_node_idx"} + # default_classifications = {"nochange", "accepted", "rejected", "unclassified"} + # default_decide_comps = {"all", "accepted", "rejected", "unclassified"} + + # Confirm that the required fields exist + missing_keys = dict_expected_keys - set(external_regressor_dict.keys()) + if missing_keys: + # If there are missing keys, this function may crash before the end. + # End function here with a clear error message rather than adding + # `if assert external_regressor_dict.get()` statements before every section + err_msg += f"External regressor dictionary missing required fields: {missing_keys}\n" + + if external_regressor_dict["calc_stats"].lower() not in calc_stats_key_options: + err_msg += ( + "calc_stats in external_regressor_dict is " + f"{external_regressor_dict['calc_stats']}. It must be one of the following " + f"{calc_stats_key_options}\n" + ) + + if (external_regressor_dict["calc_stats"].lower() != "f") and ( + "f_stats_partial_models" in set(external_regressor_dict.keys()) + ): + err_msg += ( + "External regressor dictionary cannot include" + "f_stats_partial_models if calc_stats is not F\n" + ) + + if "f_stats_partial_models" in set(external_regressor_dict.keys()): + dict_expected_keys.add("f_stats_partial_models") + dict_expected_keys.update(set(external_regressor_dict["f_stats_partial_models"])) + missing_partial_models = set(external_regressor_dict["f_stats_partial_models"]) - set( + external_regressor_dict.keys() + ) + if missing_partial_models: + raise RegressError( + f"{err_msg}" + "External regressor dictionary missing required fields for partial " + f"models defined in f_stats_partial_models: {missing_partial_models}" + ) + + # Warn if unused fields exist + unused_keys = set(external_regressor_dict.keys()) - set(dict_expected_keys) + if unused_keys: + LGR.warning( + "External regressor dictionary includes fields that " + f"are not used or logged {unused_keys}" + ) + + # Validating the information in external_regressor_dict works + # with the data in external_regressors + + # Currently column labels only need to be predefined for calc_stats==F + if "f_stats_partial_models" in set(external_regressor_dict.keys()): + regressor_names = set(external_regressors.columns) + expected_regressor_names = set() + for partial_models in external_regressor_dict["f_stats_partial_models"]: + tmp_names = set(external_regressor_dict[partial_models]) + if expected_regressor_names - tmp_names: + LGR.warning( + "External regressors used in more than one partial model: " + f"{expected_regressor_names - tmp_names}" + ) + expected_regressor_names.update(tmp_names) + if expected_regressor_names - regressor_names: + err_msg += ( + "Inputed regressors in external_regressors do not include all expected " + "regressors in partial models\n" + "Expected regressors not in inputted regressors: " + f"{expected_regressor_names - regressor_names}\n" + f"Inputted Regressors: {regressor_names}" + ) + if regressor_names - expected_regressor_names: + LGR.warning( + "Regressor labels in external_regressors are not all included in F " + "statistic partial models. Whether or not a regressor is in a partial " + "model, it will be included in the full F statistic model" + "Regressors not incldued in any partial model: " + f"{regressor_names - expected_regressor_names}" + ) + + if len(external_regressors.index) != n_time: + err_msg += ( + f"External Regressors have len(external_regressors.index) timepoints\n" + f"while fMRI data have {n_time} timepoints" + ) + + if err_msg: + raise RegressError(err_msg) + + +def fit_regressors(comptable, external_regressors, external_regressor_dict, mixing): + """ + Fit regressors to the mixing matrix. + + Uses correlation or F statistics in a linear model depending on the calc_stats + value in external_regressor_dict + + Parameters + ---------- + comptable : (C x X) :obj:`pandas.DataFrame` + Component metric table. One row for each component, with a column for + each metric. The index is the component number. + external_regressors : :obj:`pandas.DataFrame` + Each column is a labelled regressor and the number of rows should + match the number of timepoints in the fMRI time series + external_regressor_dict : :obj:`dict` + Information describing the external regressors and + method to use for fitting and statistical tests + mixing : (T x C) array_like + Mixing matrix for converting input data to component space, where `C` + is components and `T` is the same as in `data_cat` + + Returns + ------- + comptable : (C x X) :obj:`pandas.DataFrame` + Component metric table. Same as inputted, with additional columns + for metrics related to fitting the external regressors + """ + n_time = mixing.shape[0] + + # If the order of polynomial detrending is specified, then pass to make_detrend_regressors + # otherwise the function sets a detrending polynomial order + if external_regressor_dict["detrend"] is True: + detrend_regressors = make_detrend_regressors(n_time, polort=None) + elif ( + isinstance(external_regressor_dict["detrend"], int) + and external_regressor_dict["detrend"] > 0 + ): + detrend_regressors = make_detrend_regressors( + n_time, polort=external_regressor_dict["detrend"] + ) + else: + LGR.warning("External regressor fitting applied without detrending fMRI time series") + + if external_regressor_dict["calc_stats"].lower() == "corr": + if "detrend_regressors" not in locals(): + # set detrend regressors to None if it doesn't exist + detrend_regressors = None + comptable = correlate_regressors( + comptable, external_regressors, mixing, detrend_regressors=detrend_regressors + ) + elif external_regressor_dict["calc_stats"].lower() == "f": + # external_regressors = pd.concat([external_regressors, detrend_regressors]) + comptable = fit_mixing_to_regressors( + comptable, external_regressors, external_regressor_dict, mixing, detrend_regressors + ) + + return comptable + + +def make_detrend_regressors(n_time, polort=None): + """ + Create polynomial detrending regressors to use for removing slow drifts from data. + + Parameters + ---------- + n_time : :obj:`int` + The number of time point in the fMRI time series + polort : :obj:`int` or :obj:`NoneType` + The number of polynomial regressors to create (i.e. 3 is x^0, x^1, x^2) + If None, then this is set to 1+floor(n_time/150) + + Returns + ------- + detrend_regressors: (n_time x polort) :obj:`pandas.DataFrame` + Dataframe containing the detrending regressor time series + x^0 = 1. All other regressors are zscored so that they have + a mean of 0 and a stdev of 1. + Dataframe column labels are polort0 - polort{polort-1} + """ + if polort is None: + polort = int(1 + np.floor(n_time / 150)) + + # create polynomial detrending regressors -> each additive term leads + # to more points of transformation [curves] + detrend_regressors = np.zeros((n_time, polort)) + # create polynomial detrended to the power of 0 [1's], + # **1 [linear trend -> f(x) = a + bx], + # **2 [quadratic trend -> f(x) = a + bx + cx²], + # **3 [cubic trend -> f(x) = f(x) = a + bx + cx² + dx³], + # **4 [quintic trend -> f(x) = a + bx + cx² + dx³ + ex⁴] + for idx in range(polort): + # create a linear space with numbers in range [-1,1] because the mean = 0, + # and include the number of timepoints for each regressor + tmp = np.linspace(-1, 1, num=n_time) ** idx + if idx == 0: + detrend_regressors[:, idx] = tmp + detrend_labels = ["polort0"] + else: + # detrend the regressors by z-scoring the data (zero-mean-centered & stdev of 1) + detrend_regressors[:, idx] = stats.zscore(tmp) + # concatenate the polynomial power-detrended regressors within a matrix + detrend_labels.append(f"polort{idx}") + + # Vestigial code that was used to test whether outputs looked correct. + # if show_plot: + # plt.plot(detrend_regressors) # display the polynomial power-detrended regressors + # plt.show() + detrend_regressors = pd.DataFrame(data=detrend_regressors, columns=detrend_labels) + + return detrend_regressors + + +def fit_mixing_to_regressors( + comptable, + external_regressors, + external_regressor_dict, + mixing, + detrend_regressors, +): + """ + Compute Linear Model and calculate F statistics and P values for combinations of regressors. + + Equation: Y = XB + E + - Y = each ICA component (mixing) + - X = Design (Regressor) matrix (subsets of noise_regress_table) + - B = Weighting Factors (solving for B) + - E = errors (Y - Y_pred OR Y - XB) + + Parameters + ---------- + comptable : (C x X) :obj:`pandas.DataFrame` + Component metric table. One row for each component, with a column for + each metric. The index is the component number. + external_regressors : :obj:`pandas.DataFrame` + Each column is a labelled regressor and the number of rows should + match the number of timepoints in the fMRI time series + external_regressor_dict : :obj:`dict` + Information describing the external regressors and + method to use for fitting and statistical tests + mixing : (T x C) array_like + Mixing matrix for converting input data to component space, where `C` + is components and `T` is the same as in `data_cat` + detrend_regressors: (n_time x polort) :obj:`pandas.DataFrame` + Dataframe containing the detrending regressor time series + + Returns + ------- + comptable : (C x X) :obj:`pandas.DataFrame` + Component metric table. Same as inputted, with additional columns + for metrics related to fitting the external regressors. + There are new columns for F, R2, and p values for the full model + and all partial models. Names are "Fstat Full Model", "pval Full Model", + "R2stat Full Model" and "Full" is replaced by the partial model name + for each partial model + """ + LGR.info("Running fit_mixing_to_regressors") + LGR.info(f"ICA matrix has {mixing.shape[0]} time points and {mixing.shape[1]} components") + + # regressor_models is a dictionary of all the models that will be fit to the mixing matrix + # It will always include 'base' which is just the polort detrending regressors and + # 'full' which is all relevant regressors including the detrending regressors + # For F statistics, the other models need for tests are those that include everything + # EXCEPT the category of interest. For example, there will also be a field for "no Motion" + # which contains all regressors in the full model except those that model motion + regressor_models = build_fstat_regressor_models( + external_regressors, external_regressor_dict, detrend_regressors + ) + + # This is the test for the fit of the full model vs the polort detrending baseline + # The outputs will be what we use to decide which components to reject + betas_full, f_vals_tmp, p_vals_tmp, r2_vals_tmp = fit_model_with_stats( + y=mixing, regressor_models=regressor_models, base_label="base" + ) + + # TODO beta_full_model are the fits to all external regressors and might be useful to save + # TODO Also consider saving regressor_models or the detrending regressors + # betas_full_model = pd.DataFrame( + # data=betas_full.T, + # columns=np.concatenate( + # (np.array(detrend_regressors.columns), np.array(external_regressors.columns)) + # ), + # ) + f_vals = pd.DataFrame(data=f_vals_tmp, columns=["Fstat Full Model"]) + p_vals = pd.DataFrame(data=p_vals_tmp, columns=["pval Full Model"]) + r2_vals = pd.DataFrame(data=r2_vals_tmp, columns=["R2stat Full Model"]) + + # Test the fits between the full model and the full model excluding one category of regressor + if "f_stats_partial_models" in external_regressor_dict.keys(): + for pmodel in external_regressor_dict["f_stats_partial_models"]: + _, f_vals_tmp, p_vals_tmp, r2_vals_tmp = fit_model_with_stats( + mixing, regressor_models, f"no {pmodel}" + ) + f_vals[f"Fstat {pmodel} Model"] = f_vals_tmp + p_vals[f"pval {pmodel} Model"] = p_vals_tmp + r2_vals[f"R2stat {pmodel} Model"] = r2_vals_tmp + + # Add all F p and R2 statistics to comptable + comptable = pd.concat((comptable, f_vals, p_vals, r2_vals), axis=1) + + return comptable + + +def build_fstat_regressor_models(external_regressors, external_regressor_dict, detrend_regressors): + """ + Combine detrending all or subsets of external regressors to make models to fit and test. + + Parameters + ---------- + external_regressors : :obj:`pandas.DataFrame` + Each column is a labelled regressor and the number of rows should + match the number of timepoints in the fMRI time series + external_regressor_dict : :obj:`dict` + Information describing the external regressors and + method to use for fitting and statistical tests + detrend_regressors: (n_time x polort) :obj:`pandas.DataFrame` + Dataframe containing the detrending regressor time series + + Returns + ------- + regressor_models: :obj:`dict` + Each element in the dictionary is a numpy array defining the regressors in a + regressor model. The models that are always included are 'base' which is just the + detrending regressors, and 'full' which is all user-provided external regressors and + the detrending regressors. If there are partial models that are named in + external_regressor_dict["f_stats_partial_models"] then each of those will have a + dictionary element named "no" then model name and the regressors included will be + everything except the specified regressors. That is "no motion" will include all + regressors except the motion regressors. This is for the F test which compares + the variance explained with the full model to the variance explained if the + regressors-of-interest for the partial model are removed. + """ + # The category titles to group each regressor + if "f_stats_partial_models" in external_regressor_dict: + partial_models = external_regressor_dict["f_stats_partial_models"] + else: + partial_models = [] + + # All regressor labels from the data frame + regressor_labels = external_regressors.columns + + detrend_regressors_arr = detrend_regressors.to_numpy() + regressor_models = { + "base": detrend_regressors_arr, + "full": np.concatenate( + (detrend_regressors_arr, stats.zscore(external_regressors.to_numpy(), axis=0)), axis=1 + ), + } + + for pmodel in partial_models: + # For F statistics, the other models to test are those that include everything EXCEPT + # the category of interest + # That is "no motion" should contain the full model excluding motion regressors + keep_labels = set(regressor_labels) - set(external_regressor_dict[pmodel]) + no_pmodel = f"no {pmodel}" + regressor_models[no_pmodel] = detrend_regressors_arr + for keep_label in keep_labels: + regressor_models[no_pmodel] = np.concatenate( + ( + regressor_models[no_pmodel], + np.atleast_2d( + stats.zscore(external_regressors[keep_label].to_numpy(), axis=0) + ).T, + ), + axis=1, + ) + keep_labels.update(set(detrend_regressors.columns)) + LGR.info( + f"Size for External Regressor Partial Model '{no_pmodel}': " + f"{regressor_models[no_pmodel].shape}" + ) + LGR.info( + "Regressors In Partial Model (everything but regressors of interest) " + f"'{no_pmodel}': {keep_labels}" + ) + + LGR.info(f"Size for full Regressor Model: {regressor_models['full'].shape}") + LGR.info(f"Size for base Regressor Model: {regressor_models['base'].shape}") + + # vestigial codethat was used to check outputs and might be worth reviving + # if show_plot: + # fig = plt.figure(figsize=(10, 10)) + # ax = fig.add_subplot(3, 2, 1) + # ax.plot(detrend_regressors) + # plt.title("detrend") + # for idx, reg_cat in enumerate(regress_categories): + # if idx < 5: + # ax = fig.add_subplot(3, 2, idx + 2) + # ax.plot(stats.zscore(categorized_regressors[reg_cat].to_numpy(), axis=0)) + # plt.title(reg_cat) + # plt.savefig( + # f"{prefix}_ModelRegressors.jpeg", pil_kwargs={"quality": 20}, dpi="figure" + # ) # could also be saves as .eps + + return regressor_models + + +def fit_model(x, y, output_residual=False): + """ + Linear regression for a model y = betas * x + error. + + Parameters + ---------- + x : (R X T) :obj:`numpy.ndarray` + 2D array with the regressors for the specified model an time + y : (T X C) :obj:`numpy.ndarray` + Time by mixing matrix components for the time series for fitting + output_residual : :obj:`bool` + If true, then this just outputs the residual of the fit. + If false, then outputs beta fits, sse, and df + + Returns + ------- + residual : (T X C) :obj:`numpy.ndarray` + The residual time series for the fit (only if output_residual is True) + betas : (R X C) :obj:`numpy.ndarray` + The magnitude fits for the model (only if output_residual is False) + sse : (R X C) :obj:`numpy.ndarray` + The sum of square error for the model (only if output_residual is False) + df : (R X C) :obj:`numpy.ndarray` + The degrees of freeom for the model (only if output_residual is False) + (timepoints - number of regressors) + """ + betas, _, _, _ = linalg.lstsq(x, y) + # matrix-multiplication on the regressors with the betas -> to create a new 'estimated' + # component matrix = fitted regressors (least squares beta solution * regressors) + fitted_regressors = np.matmul(x, betas) + residual = y - fitted_regressors + if output_residual: + return residual + else: + # sum the differences between the actual ICA components and the 'estimated' + # component matrix (beta-fitted regressors) + sse = np.sum(np.square(residual), axis=0) + # calculate how many individual values [timepoints] are free to vary after + # the least-squares solution [beta] betw X & Y is calculated + df = y.shape[0] - betas.shape[0] + return betas, sse, df + + +def fit_model_with_stats(y, regressor_models, base_label): + """ + Fit full and partial models and calculate F stats, R2, and p values. + + Math from page 11-14 of https://afni.nimh.nih.gov/pub/dist/doc/manual/3dDeconvolve.pdf + Calculates Y=betas*X + error for the base and the full model + F = ((SSE_base-SSE_full)/(DF_base-DF_full)) / (SSE_full/DF_full) + DF = degrees of freedom + SSE = sum of squares error + + Parameters + ---------- + y : (T X C) :obj:`numpy.ndarray` + Time by mixing matrix components for the time series for fitting + regressor_models: :obj:`dict` + Each element in the dictionary is a numpy array defining the regressors in a + regressor model. Inclues 'full', 'base' and partial models. + base_label : :obj:`str` + The base model to compare the full model against. For F stat for the full + model, this should be 'base'. For partial models, this should be the name + of the partial model (i.e. "no motion"). + + Returns + ------- + betas_full : (C x R) :obj:`numpy.ndarray` + The beta fits for the full model (components x regressors) + f_vals : (C x M) :obj:`numpy.ndarray` + The F statistics for the fits to the full and partial models + p_vals : (C x M) :obj:`numpy.ndarray` + The p values for the fits to the full and partial models + r2_vals : (C x M) :obj:`numpy.ndarray` + The R2 statistics for the fits to the full and partial models + """ + betas_base, sse_base, df_base = fit_model(regressor_models[base_label], y) + betas_full, sse_full, df_full = fit_model(regressor_models["full"], y) + + # larger sample variance / smaller sample variance (F = (SSE1 – SSE2 / m) / SSE2 / n-k, + # where SSE = residual sum of squares, m = number of restrictions and k = number of + # independent variables) -> the 'm' restrictions in this case is the DOF range between + # the base - full model, the 'n-k' is the number of DOF (independent variables/timepoints) + # from the fully-fitted model + f_vals = np.divide((sse_base - sse_full) / (df_base - df_full), (sse_full / df_full)) + # cumulative distribution (FX(x) = P(X<=x), X = real-valued variable, x=probable variable + # within distribution) of the F-values + extra parameters to shape the range of the + # distribution values (range: start = DOF_base (unfitted) - + # DOF_Full (fitted with full model), end = DOF_Full) + p_vals = 1 - stats.f.cdf(f_vals, df_base - df_full, df_full) + # estimate proportion of variance (R2-squared fit) by SSE full [fully fitted] + # (sum of 1st errors) / SSE base [non-fitted] (sum of 2nd errors) ... and subtracting + # the error ratio from 1: R² = SSR (fitted model)/SST(total or model base) = + # Σ (Y_pred-Y_actual)**2 / Σ (Y_pred-Y_actual)**2 + r2_vals = 1 - np.divide(sse_full, sse_base) + print(y.shape) + + # Vestigial code for testing that might be worth reviving + # Plots the fits for the first 20 components + # if show_plot: + # plt.clf() + # fig = plt.figure(figsize=(20, 24)) + # for idx in range(30): + # # print('Outer bound index: ', idx) + + # if idx < Y.shape[1]: # num of components + # # print('Actual axis index: ', idx) + # ax = fig.add_subplot(5, 6, idx + 1) # this axis index starts from 1 + # plot_fit( + # ax, + # Y[:, idx], + # betas_full[:, idx], + # regressor_models["full"], + # betas_base=betas_base[:, idx], + # X_base=regressor_models[base_label], + # F_val=F_vals[idx], + # p_val=p_vals[idx], + # R2_val=R2_vals[idx], + # SSE_base=SSE_base[idx], + # SSE_full=SSE_full[idx], + # base_legend=base_label, + # ) + # base_save_label = base_label.replace(" ", "_") + # plt.savefig( + # f"{prefix}_ModelFits_{base_save_label}.jpeg", + # pil_kwargs={"quality": 20}, + # dpi="figure", + # ) # could also be saved as eps + + return betas_full, f_vals, p_vals, r2_vals + + +def correlate_regressors(comptable, external_regressors, mixing, detrend_regressors=None): + """Correlate external regressors with mixing components. + + Parameters + ---------- + comptable : (C x X) :obj:`pandas.DataFrame` + Component metric table. One row for each component, with a column for + each metric. The index is the component number. + external_regressors : :obj:`pandas.DataFrame` + Each column is a labelled regressor and the number of rows should + match the number of timepoints in the fMRI time series + mixing : (T x C) array_like + Mixing matrix for converting input data to component space, where `C` + is components and `T` is the same as in `data_cat` + detrend_regressors: (n_time x polort) :obj:`pandas.DataFrame` + Dataframe containing the detrending regressor time series + + Returns + ------- + comptable : (C x X) :obj:`pandas.DataFrame` + Component metric table. Same as inputted, with additional columns + for metrics related to fitting the external regressors. Each + external regressor has one metric column with the name of the + regressor followed by "_correlation_ + """ + if isinstance(detrend_regressors, pd.DataFrame): + # Detrend the mixing matrix before correlating to external regressors + mixing = fit_model(detrend_regressors.to_numpy(), mixing, output_residual=True) + + external_regressor_names = external_regressors.columns.tolist() + for col in external_regressor_names: + external_regressor_arr = external_regressors[col].values + if isinstance(detrend_regressors, pd.DataFrame): + external_regressor_arr = fit_model( + detrend_regressors.to_numpy(), external_regressor_arr, output_residual=True + ) + assert external_regressor_arr.ndim == 1 + assert external_regressor_arr.shape[0] == mixing.shape[0] + corrs = np.abs(np.corrcoef(external_regressor_arr, mixing.T)[0, 1:]) + comptable[f"{col}_correlation"] = corrs + + return comptable + + +# Vestigial code that was used for testing accuracy of some variables +# Might be worth reviving +# def plot_fit( +# ax, +# Y, +# betas_full, +# X_full, +# betas_base=None, +# X_base=None, +# F_val=None, +# p_val=None, +# R2_val=None, +# SSE_base=None, +# SSE_full=None, +# base_legend="base fit", +# ): +# """ +# plot_fit: Plot the component time series and the fits to the full and base models + +# INPUTS: +# ax: axis handle for the figure subplot +# Y: The ICA component time series to fit to +# betas_full: The full model fitting parameters +# X_full: The time series for the full model + +# Optional: +# betas_base, X_base=None: Model parameters and time series for base model +# (not plotted if absent) +# F_val, p_val, R2_val, SSE_base, SSE_full: Fit statistics to include with each plot +# base_legend: A description of what the base model is to include in the legent +# """ + +# ax.plot(Y, color="black") +# ax.plot( +# np.matmul(X_full, betas_full.T), color="red" +# ) # the 'red' plot is the matrix-multiplication product of the time series * +# if (type(betas_base) != "NoneType") and (type(X_base) != "NoneType"): +# ax.plot(np.matmul(X_base, betas_base.T), color="green") +# ax.text( +# 250, +# 2, +# f"F={np.around(F_val, decimals=4)}\np={np.around(p_val, decimals=4)} +# \nR2={np.around(R2_val, decimals=4)}\nSSE_base={np.around(SSE_base, +# decimals=4)}\nSSE_full={np.around(SSE_full, decimals=4)}", +# ) +# ax.legend(["ICA Component", "Full fit", f"{base_legend} fit"], loc="best") +# else: +# ax.text( +# 250, +# 2, +# f"F={np.around(F_val, decimals=4)}\np={np.around(p_val, decimals=4)}\nR2= +# {np.around(R2_val, decimals=4)}\nSSE_full={np.around(SSE_full, decimals=4)}", +# ) +# ax.legend(["ICA Component", "Full fit"], loc="best") diff --git a/tedana/resources/config/metrics.json b/tedana/resources/config/metrics.json index 9e3862bd7..7cd2ce22c 100644 --- a/tedana/resources/config/metrics.json +++ b/tedana/resources/config/metrics.json @@ -117,9 +117,10 @@ "ref_img", "tes" ], - "external correlation": [ + "external fit": [ "mixing", - "external_regressors" + "external_regressors", + "external_regressor_dict" ] } } diff --git a/tedana/resources/decision_trees/minimal_external3.json b/tedana/resources/decision_trees/minimal_external3.json new file mode 100644 index 000000000..ea58d3fa4 --- /dev/null +++ b/tedana/resources/decision_trees/minimal_external3.json @@ -0,0 +1,251 @@ +{ + "tree_id": "minimal_decision_tree_test3", + "info": "Proposed minimal decision tree that tests a full model of all external regressors.", + "report": "This is based on the minimal criteria of the original MEICA decision tree \\citep{kundu2013integrated} without the more aggressive noise removal steps \\citep{dupre2021te}.", + "necessary_metrics": [ + "kappa", + "rho", + "countsigFS0", + "countsigFT2", + "dice_FS0", + "dice_FT2", + "signal-noise_t", + "variance explained", + "pval Full Model", + "R2stat Full Model" + ], + "intermediate_classifications": [ + "provisionalaccept", + "provisionalreject" + ], + "classification_tags": [ + "Likely BOLD", + "Unlikely BOLD", + "Low variance" + ], + "nodes": [ + { + "functionname": "manual_classify", + "parameters": { + "new_classification": "unclassified", + "decide_comps": "all" + }, + "kwargs": { + "log_extra_report": "", + "clear_classification_tags": true, + "dont_warn_reclassify": true + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "rho", + "right": "kappa" + }, + "kwargs": { + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "countsigFS0", + "right": "countsigFT2" + }, + "kwargs": { + "left2": "countsigFT2", + "op2": ">", + "right2": 0, + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "calc_median", + "parameters": { + "decide_comps": "all", + "metric_name": "variance explained", + "median_label": "varex" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "dice_FS0", + "right": "dice_FT2" + }, + "kwargs": { + "left2": "variance explained", + "op2": ">", + "right2": "median_varex", + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": 0, + "right": "signal-noise_t" + }, + "kwargs": { + "left2": "variance explained", + "op2": ">", + "right2": "median_varex", + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD" + } + }, + { + "functionname": "calc_kappa_elbow", + "parameters": { + "decide_comps": "all" + }, + "kwargs": { + "log_extra_info": "", + "log_extra_report": "" + }, + "_comment": "" + }, + { + "functionname": "calc_rho_elbow", + "parameters": { + "decide_comps": "all" + }, + "kwargs": { + "subset_decide_comps": "unclassified", + "rho_elbow_type": "liberal", + "log_extra_info": "", + "log_extra_report": "" + }, + "_comment": "" + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "provisionalaccept", + "if_false": "provisionalreject", + "decide_comps": "unclassified", + "op": ">=", + "left": "kappa", + "right": "kappa_elbow_kundu" + }, + "kwargs": { + "log_extra_report": "" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "accepted", + "if_false": "nochange", + "decide_comps": "provisionalaccept", + "op": ">", + "left": "kappa", + "right": "rho" + }, + "kwargs": { + "log_extra_info": "If kappa>elbow and kappa>2*rho accept even if rho>elbow", + "log_extra_report": "", + "right_scale": 2, + "tag_if_true": "Likely BOLD" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "provisionalreject", + "if_false": "nochange", + "decide_comps": [ + "provisionalreject", + "provisionalaccept" + ], + "op": ">", + "left": "rho", + "right": "rho_elbow_liberal" + }, + "kwargs": { + "log_extra_report": "" + } + }, + { + "functionname": "dec_variance_lessthan_thresholds", + "parameters": { + "if_true": "accepted", + "if_false": "nochange", + "decide_comps": "provisionalreject" + }, + "kwargs": { + "var_metric": "variance explained", + "log_extra_info": "", + "log_extra_report": "", + "single_comp_threshold": 0.1, + "all_comp_threshold": 1.0, + "tag_if_true": "Low variance" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": "<", + "left": "pval Full Model", + "right": 0.05 + }, + "kwargs": { + "op2": ">", + "left2": "R2stat Full Model", + "right2": 0.5, + "log_extra_info": "If external regressors fit with p<0.05 and model R2>0.5 of the variance, then reject.", + "log_extra_report": "", + "tag_if_true": "external regressors" + } + }, + { + "functionname": "manual_classify", + "parameters": { + "new_classification": "accepted", + "decide_comps": "provisionalaccept" + }, + "kwargs": { + "log_extra_info": "", + "log_extra_report": "", + "tag": "Likely BOLD" + } + }, + { + "functionname": "manual_classify", + "parameters": { + "new_classification": "rejected", + "decide_comps": [ + "provisionalreject", + "unclassified" + ] + }, + "kwargs": { + "log_extra_info": "", + "log_extra_report": "", + "tag": "Unlikely BOLD" + } + } + ] +} diff --git a/tedana/resources/extern_regress_dicts/Fmodel.json b/tedana/resources/extern_regress_dicts/Fmodel.json new file mode 100644 index 000000000..469abe3cd --- /dev/null +++ b/tedana/resources/extern_regress_dicts/Fmodel.json @@ -0,0 +1,6 @@ +{ + "regess_ID": "Fmodel", + "info": "Fits all external regressors to a single model using an F statistic", + "detrend": true, + "calc_stats": "F" +} diff --git a/tedana/resources/extern_regress_dicts/Mot12_CSF.json b/tedana/resources/extern_regress_dicts/Mot12_CSF.json new file mode 100644 index 000000000..42304bb73 --- /dev/null +++ b/tedana/resources/extern_regress_dicts/Mot12_CSF.json @@ -0,0 +1,27 @@ +{ + "regess_ID": "Motion12_CSF", + "info": "Contains 6 columns for motion, 6 for first derivatives of motion, and 1 for a CSF ROI time series.", + "detrend": true, + "calc_stats": "F", + "f_stats_partial_models": [ + "Motion", + "CSF" + ], + "Motion": [ + "Mot_X", + "Mot_Y", + "Mot_Z", + "Mot_Pitch", + "Mot_Roll", + "Mot_Yaw", + "Mot_d1_X", + "Mot_d1_Y", + "Mot_d1_Z", + "Mot_d1_Pitch", + "Mot_d1_Roll", + "Mot_d1_Yaw" + ], + "CSF": [ + "CSF" + ] +} diff --git a/tedana/resources/extern_regress_dicts/corr_detrend.json b/tedana/resources/extern_regress_dicts/corr_detrend.json new file mode 100644 index 000000000..f4c364537 --- /dev/null +++ b/tedana/resources/extern_regress_dicts/corr_detrend.json @@ -0,0 +1,6 @@ +{ + "regess_ID": "corr_detrend", + "info": "Separately correlates every regressor after detrending regressors and fMRI data.", + "detrend": true, + "calc_stats": "corr" +} diff --git a/tedana/selection/selection_nodes.py b/tedana/selection/selection_nodes.py index fe93a242b..33a79df21 100644 --- a/tedana/selection/selection_nodes.py +++ b/tedana/selection/selection_nodes.py @@ -537,9 +537,9 @@ def dec_variance_lessthan_thresholds( if custom_node_label: outputs["node_label"] = custom_node_label else: - outputs["node_label"] = ( - f"{var_metric}<{single_comp_threshold}. All variance<{all_comp_threshold}" - ) + outputs[ + "node_label" + ] = f"{var_metric}<{single_comp_threshold}. All variance<{all_comp_threshold}" LGR.info(f"{function_name_idx}: {if_true} if {outputs['node_label']}, else {if_false}") if log_extra_info: @@ -1020,9 +1020,9 @@ def dec_classification_doesnt_exist( if custom_node_label: outputs["node_label"] = custom_node_label elif at_least_num_exist == 1: - outputs["node_label"] = ( - f"Change {decide_comps} to {new_classification} if {class_comp_exists} doesn't exist" - ) + outputs[ + "node_label" + ] = f"Change {decide_comps} to {new_classification} if {class_comp_exists} doesn't exist" else: outputs["node_label"] = ( f"Change {decide_comps} to {new_classification} if less than " diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index 1d6e45e41..20478f58e 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -160,9 +160,9 @@ def change_comptable_classifications( dont_warn_reclassify=dont_warn_reclassify, ) - selector.component_status_table[f"Node {selector.current_node_idx}"] = ( - selector.component_table["classification"] - ) + selector.component_status_table[ + f"Node {selector.current_node_idx}" + ] = selector.component_table["classification"] n_true = decision_boolean.sum() n_false = np.logical_not(decision_boolean).sum() diff --git a/tedana/selection/tedica.py b/tedana/selection/tedica.py index 75f60c96d..bc335354e 100644 --- a/tedana/selection/tedica.py +++ b/tedana/selection/tedica.py @@ -55,12 +55,11 @@ def automatic_selection(component_table, n_echos, n_vols, tree="kundu"): .. _FAQ: faq.html """ - LGR.info("Performing ICA component selection with Kundu decision tree v2.5") + LGR.info(f"Performing ICA component selection with tree: {tree}") RepLGR.info( "Next, component selection was performed to identify " "BOLD (TE-dependent), non-BOLD (TE-independent), and " - "uncertain (low-variance) components using the Kundu " - "decision tree (v2.5) \\citep{kundu2013integrated}." + "uncertain (low-variance) components." ) component_table["classification_tags"] = "" diff --git a/tedana/tests/data/external_regress_Ftest_3echo.csv b/tedana/tests/data/external_regress_Ftest_3echo.csv new file mode 100644 index 000000000..a0d504230 --- /dev/null +++ b/tedana/tests/data/external_regress_Ftest_3echo.csv @@ -0,0 +1,76 @@ +Mot_X,Mot_Y,Mot_Z,Mot_Pitch,Mot_Roll,Mot_Yaw,Mot_d1_X,Mot_d1_Y,Mot_d1_Z,Mot_d1_Pitch,Mot_d1_Roll,Mot_d1_Yaw,CSF +-0.329580266,-0.844918633,1.626381228,-0.218889288,0.634672027,0.603137649,0,0,0,0,0,0,-0.718687205 +-1.520650288,-2.13455262,-0.882532519,0.336875466,-1.592270822,-1.191402591,-1.191070022,-1.289633987,-2.508913747,0.555764753,-2.226942849,-1.794540239,-0.844111053 +-0.511890744,-0.711229882,0.276263262,0.188691918,0.099964095,-0.995174865,1.008759544,1.423322738,1.158795781,-0.148183548,1.692234917,0.196227726,-0.921595994 +-2.06545787,-0.40815265,1.038928803,0.226413424,-0.384996762,0.286311023,-1.553567126,0.303077232,0.762665541,0.037721506,-0.484960857,1.281485888,-0.638149754 +-1.565465426,-0.289605838,0.551574072,0.307316972,-0.272939181,-0.92834738,0.499992444,0.118546812,-0.487354731,0.080903548,0.112057581,-1.214658403,1.141942235 +-0.583901238,-0.45316118,0.882519392,-0.865762002,-0.537313024,0.517619453,0.981564188,-0.163555342,0.33094532,-1.173078975,-0.264373843,1.445966833,-1.385047823 +0.225980497,-1.627540572,-0.68542407,-0.700932399,-1.67754023,1.019518689,0.809881735,-1.174379392,-1.567943462,0.164829603,-1.140227206,0.501899236,-0.186120892 +0.560677621,-1.489408639,-0.595520306,0.685234401,-0.723554811,0.416786873,0.334697124,0.138131933,0.089903764,1.386166801,0.953985419,-0.602731816,-0.580953121 +0.003248201,-0.340159692,-0.879529088,0.602469892,-0.876641151,-0.884034813,-0.55742942,1.149248947,-0.284008782,-0.08276451,-0.15308634,-1.300821686,-1.692923237 +0.836161459,-1.092255501,-2.618392652,0.707002513,-0.763363892,0.276104352,0.832913258,-0.752095809,-1.738863564,0.104532622,0.113277259,1.160139164,-0.371763879 +1.586566953,0.271349643,-0.649296386,-0.881991034,-0.310818575,0.552978122,0.750405494,1.363605144,1.969096266,-1.588993548,0.452545317,0.276873771,0.933331204 +0.667602572,1.549333738,0.076243832,0.833106558,0.518018373,-0.620225079,-0.918964381,1.277984095,0.725540218,1.715097593,0.828836948,-1.173203202,-0.852116981 +-0.01792122,2.740600234,0.074586135,1.164085584,-0.240669175,-0.440358808,-0.685523792,1.191266496,-0.001657697,0.330979026,-0.758687548,0.179866271,-0.092616197 +-0.659019247,2.298595672,0.17634643,-0.101416476,-0.182581812,2.015890229,-0.641098027,-0.442004562,0.101760295,-1.26550206,0.058087363,2.456249037,1.414856771 +-1.094112767,-0.186225918,-2.127567341,-0.660534996,0.727968953,0.549065179,-0.43509352,-2.48482159,-2.303913771,-0.55911852,0.910550765,-1.46682505,-1.264667218 +0.248649215,-0.618039127,-1.082778413,-2.323754365,-0.275433162,-0.224507966,1.342761982,-0.431813209,1.044788928,-1.66321937,-1.003402116,-0.773573145,0.297574192 +-0.034109838,0.196157196,0.285049845,0.929700246,-1.903128963,-1.640111364,-0.282759053,0.814196323,1.367828258,3.253454611,-1.627695801,-1.415603398,-0.749200249 +-0.154408808,0.946079652,1.025632299,-1.504803984,1.86795795,0.461493517,-0.12029897,0.749922456,0.740582454,-2.43450423,3.771086913,2.101604881,0.024314549 +0.764863858,0.933908359,-0.135353281,-0.109277214,0.002639137,-1.643878764,0.919272666,-0.012171293,-1.16098558,1.39552677,-1.865318813,-2.105372281,-1.143075439 +0.445796508,1.917424429,0.650347848,1.98385538,0.655358612,1.874592447,-0.31906735,0.98351607,0.785701129,2.093132594,0.652719475,3.51847121,-1.758526555 +0.207513285,1.298735387,2.318083224,-0.535817525,2.16070642,-0.721054976,-0.238283223,-0.618689042,1.667735376,-2.519672905,1.505347808,-2.595647423,0.666876476 +0.381782634,0.081083807,-1.984367893,0.563118012,0.313116397,-0.168924986,0.174269349,-1.21765158,-4.302451117,1.098935537,-1.847590024,0.55212999,0.145183641 +-1.47473253,0.515676307,1.024256498,-0.163074269,0.924446522,-0.68895112,-1.856515164,0.4345925,3.008624391,-0.726192282,0.611330125,-0.520026134,-0.051313681 +-1.494898436,0.766171566,1.630635233,1.316319173,0.756997088,-0.541944145,-0.020165906,0.250495259,0.606378735,1.479393442,-0.167449434,0.147006975,0.176664517 +-2.019896907,1.643560771,-0.311565305,-1.536321826,0.680356602,0.758927506,-0.524998471,0.877389205,-1.942200538,-2.852640999,-0.076640486,1.300871651,-1.264980299 +-2.178716752,0.973512277,2.252419221,-0.98610174,-1.462675416,1.890594069,-0.158819845,-0.670048494,2.563984526,0.550220085,-2.143032018,1.131666563,-1.113211169 +-1.232037427,-0.40191649,1.00829017,1.358255513,0.106048498,-0.129832743,0.946679325,-1.375428767,-1.244129051,2.344357253,1.568723914,-2.020426812,0.154120413 +0.731522293,0.40813286,1.025669565,0.164802434,1.923608997,1.773241326,1.96355972,0.81004935,0.017379395,-1.193453079,1.817560499,1.903074069,-1.34580855 +0.753719194,0.163556595,-1.064627798,-0.934732068,0.685816646,0.525679007,0.022196901,-0.244576265,-2.090297363,-1.099534501,-1.237792351,-1.247562319,0.192821615 +1.757683518,-0.537743502,-0.866195997,1.491399948,-0.345322156,0.446364892,1.003964324,-0.701300097,0.198431801,2.426132015,-1.031138802,-0.079314115,1.960430091 +1.550719352,0.11832591,-0.668387479,0.223298561,-1.812651721,0.058823033,-0.206964166,0.656069412,0.197808518,-1.268101387,-1.467329565,-0.387541859,-0.600970527 +2.288307277,0.791999772,0.75152785,0.068312984,1.468953207,-0.859155411,0.737587925,0.673673862,1.419915329,-0.154985576,3.281604929,-0.917978445,-0.296907317 +2.663681819,2.261180081,1.905593875,-1.140429944,-1.722043668,0.364115037,0.375374542,1.469180309,1.154066025,-1.208742929,-3.190996875,1.223270448,-0.862172265 +2.268142799,0.717724473,-2.382603885,0.436210872,0.218979462,0.089319574,-0.39553902,-1.543455608,-4.28819776,1.576640816,1.94102313,-0.274795463,0.906547155 +-0.218049277,-0.508412721,-2.487404957,1.887320081,1.04722817,-0.395861337,-2.486192076,-1.226137194,-0.104801072,1.45110921,0.828248708,-0.485180912,-0.451624542 +-0.410793483,0.116533685,1.879092728,1.184222196,-0.031140809,-0.90091931,-0.192744206,0.624946406,4.366497685,-0.703097886,-1.078368979,-0.505057973,0.118224392 +-0.647691228,0.838129685,1.374778227,0.322847522,1.175235974,1.277024175,-0.236897745,0.721596,-0.504314501,-0.861374674,1.206376783,2.177943485,-0.449494687 +-1.13985429,-0.495903004,1.056569633,-1.587672129,0.39860901,-0.768585548,-0.492163062,-1.334032689,-0.318208594,-1.910519651,-0.776626964,-2.045609723,-0.222016948 +0.53999669,-0.487881558,0.581258434,-1.085920121,-0.501406499,0.115378063,1.67985098,0.008021446,-0.475311199,0.501752008,-0.900015509,0.883963611,-0.703432192 +0.410905902,0.873257327,3.608291808,0.653652794,-1.54826995,1.468348054,-0.129090788,1.361138885,3.027033374,1.739572915,-1.04686345,1.352969991,0.299902147 +-0.173062544,0.529483977,1.076381362,-0.503373399,-0.058733834,-1.45590019,-0.583968446,-0.34377335,-2.531910446,-1.157026193,1.489536116,-2.924248244,0.005305683 +0.590240338,-0.560105813,-0.33143028,-0.14587712,1.474596747,1.017308667,0.763302882,-1.08958979,-1.407811642,0.35749628,1.533330581,2.473208857,-1.883857873 +-0.330152172,-0.29496616,-1.27480681,0.993753616,0.247092802,-0.980747759,-0.92039251,0.265139653,-0.94337653,1.139630735,-1.227503945,-1.998056427,-1.361923392 +-0.770820846,-1.130215098,-0.533069878,1.339112763,1.354475058,-0.663609074,-0.440668674,-0.835248938,0.741736932,0.345359147,1.107382257,0.317138685,-0.854950385 +-0.997000751,-1.594080868,-2.206716323,0.551420017,-0.360200976,0.897818757,-0.226179905,-0.46386577,-1.673646445,-0.787692746,-1.714676034,1.561427831,-0.509180861 +-0.078723051,-1.034723379,1.715724783,2.451708059,0.070115822,-0.667778397,0.9182777,0.559357489,3.922441106,1.900288042,0.430316798,-1.565597154,0.134291091 +0.317741611,-1.449732445,-0.829499847,1.984395673,1.236600623,-0.310344732,0.396464662,-0.415009066,-2.54522463,-0.467312386,1.166484801,0.357433665,0.330435696 +0.920629978,-3.107612041,-1.72016277,-1.6235986,-0.680428817,-0.15105238,0.602888367,-1.657879596,-0.890662923,-3.607994273,-1.917029441,0.159292352,0.21447828 +0.904171949,-3.25810681,-1.338419095,-0.225635225,0.492152931,-1.184901646,-0.016458029,-0.150494769,0.381743675,1.397963375,1.172581749,-1.033849266,0.467948374 +0.712432166,-2.702026568,-4.090712476,0.325379006,-1.008512619,0.155445809,-0.191739783,0.556080242,-2.752293381,0.551014231,-1.50066555,1.340347455,-0.409919798 +1.213580415,-2.416744199,-1.644462247,0.887889538,-1.019416802,-0.194144049,0.501148249,0.285282369,2.446250229,0.562510532,-0.010904183,-0.349589858,-0.954443807 +1.015276471,-0.468506664,0.826022926,-0.704628983,-0.831670525,0.350992643,-0.198303944,1.948237535,2.470485173,-1.592518521,0.187746276,0.545136692,-1.28584816 +0.458644239,0.304782879,-0.286629392,-0.622614244,0.81723357,-0.620482414,-0.556632232,0.773289543,-1.112652318,0.082014739,1.648904096,-0.971475057,-1.250751609 +0.696387231,0.478940055,-0.661704616,-0.225347907,-1.187184555,-0.81881664,0.237742992,0.174157176,-0.375075224,0.397266338,-2.004418126,-0.198334226,-0.58673588 +0.398048005,0.35203805,-0.836607472,-1.114036436,-1.728872921,-0.244092905,-0.298339226,-0.126902005,-0.174902856,-0.888688529,-0.541688366,0.574723735,-0.251724316 +-0.433479435,0.130772773,-0.702912597,-0.711315522,0.831520689,-1.273732568,-0.83152744,-0.221265277,0.133694875,0.402720914,2.56039361,-1.029639663,2.040725153 +-1.504705013,-0.117524037,-1.172268361,-1.301597961,-1.43149785,-0.320764583,-1.071225578,-0.24829681,-0.469355764,-0.590282439,-2.263018539,0.952967985,-0.258655658 +-1.966296873,-1.284318477,-0.647627254,0.187287319,-1.026578293,0.466508325,-0.46159186,-1.16679444,0.524641107,1.48888528,0.404919557,0.787272908,2.114290456 +-1.036674723,-0.437122887,0.788299251,1.208653611,1.589038974,1.098106245,0.92962215,0.84719559,1.435926505,1.021366292,2.615617267,0.63159792,-0.93052932 +-0.696570694,1.035997759,0.315168026,-0.180901513,-0.784289145,0.252979402,0.340104029,1.473120646,-0.473131225,-1.389555124,-2.373328119,-0.845126843,0.177085093 +-1.056655551,2.054949636,1.733679189,-1.593982301,-2.245797478,0.326044934,-0.360084857,1.018951877,1.418511163,-1.413080788,-1.461508333,0.073065532,-1.242278489 +-1.253872449,0.940519962,-1.859117584,-0.393650127,-0.324318496,0.436127281,-0.197216898,-1.114429674,-3.592796773,1.200332173,1.921478982,0.110082347,0.278345459 +-1.069287813,-0.5172584,-0.656338228,0.143574863,-1.262613776,1.600586235,0.184584636,-1.457778362,1.202779356,0.537224991,-0.93829528,1.164458954,-1.920302265 +0.073889742,1.015905744,3.017531937,-0.558421237,0.29254061,-0.615549542,1.143177555,1.533164144,3.673870165,-0.7019961,1.555154386,-2.216135777,0.003952104 +1.32888943,2.850501828,2.785235104,-0.929137834,-0.021765497,-0.581322431,1.254999688,1.834596084,-0.232296833,-0.370716597,-0.314306107,0.034227111,-0.854938554 +0.831392436,2.255038285,0.082410724,0.365767705,0.860518504,-0.916585465,-0.497496994,-0.595463543,-2.70282438,1.294905539,0.882284001,-0.335263034,-0.264536802 +-0.670035292,0.826737793,0.321644476,1.449735904,-0.344614203,-1.344199808,-1.501427728,-1.428300492,0.239233752,1.083968199,-1.205132707,-0.427614343,-0.829211295 +-0.478355817,0.188711887,-0.630649751,-1.102278018,-1.374564234,0.697153667,0.191679475,-0.638025906,-0.952294227,-2.552013922,-1.029950031,2.041353475,-1.752282527 +0.999877364,-1.06986647,1.290972976,-0.92101604,0.906070467,0.416570393,1.478233181,-1.258578357,1.921622727,0.181261978,2.280634701,-0.280583274,0.64858987 +0.069018404,0.516767176,1.573029961,0.362136079,-1.202013587,-0.428893561,-0.93085896,1.586633646,0.282056985,1.283152119,-2.108084054,-0.845463954,-0.423093312 +0.351758996,0.056295399,1.665144379,-0.415044976,0.482451076,-1.053100619,0.282740592,-0.460471777,0.092114418,-0.777181055,1.684464663,-0.624207058,0.664860241 +0.858424569,-0.363527343,1.550740147,0.917563357,-0.68983397,-1.261316549,0.506665573,-0.419822742,-0.114404232,1.332608333,-1.172285046,-0.20821593,-0.097170847 +1.334779099,-0.553036015,0.026277298,-1.303973749,0.14799305,-1.047775225,0.47635453,-0.189508672,-1.524462849,-2.221537106,0.83782702,0.213541323,-0.273332909 +0.365467586,-1.460953257,1.836000417,0.076497972,-1.230053674,-0.597101686,-0.969311513,-0.907917242,1.809723119,1.38047172,-1.378046723,0.450673539,-0.026346724 +-0.084225355,-0.90165276,0.41416722,1.144524716,0.509152904,-1.158340165,-0.449692941,0.559300497,-1.421833197,1.068026744,1.739206578,-0.561238479,-1.19547545 diff --git a/tedana/tests/data/external_regress_Ftest_3echo.tsv b/tedana/tests/data/external_regress_Ftest_3echo.tsv new file mode 100644 index 000000000..ba9073eed --- /dev/null +++ b/tedana/tests/data/external_regress_Ftest_3echo.tsv @@ -0,0 +1,76 @@ +Mot_X Mot_Y Mot_Z Mot_Pitch Mot_Roll Mot_Yaw Mot_d1_X Mot_d1_Y Mot_d1_Z Mot_d1_Pitch Mot_d1_Roll Mot_d1_Yaw CSF +-0.329580266 -0.844918633 1.626381228 -0.218889288 0.634672027 0.603137649 0 0 0 0 0 0 -0.718687205 +-1.520650288 -2.13455262 -0.882532519 0.336875466 -1.592270822 -1.191402591 -1.191070022 -1.289633987 -2.508913747 0.555764753 -2.226942849 -1.794540239 -0.844111053 +-0.511890744 -0.711229882 0.276263262 0.188691918 0.099964095 -0.995174865 1.008759544 1.423322738 1.158795781 -0.148183548 1.692234917 0.196227726 -0.921595994 +-2.06545787 -0.40815265 1.038928803 0.226413424 -0.384996762 0.286311023 -1.553567126 0.303077232 0.762665541 0.037721506 -0.484960857 1.281485888 -0.638149754 +-1.565465426 -0.289605838 0.551574072 0.307316972 -0.272939181 -0.92834738 0.499992444 0.118546812 -0.487354731 0.080903548 0.112057581 -1.214658403 1.141942235 +-0.583901238 -0.45316118 0.882519392 -0.865762002 -0.537313024 0.517619453 0.981564188 -0.163555342 0.33094532 -1.173078975 -0.264373843 1.445966833 -1.385047823 +0.225980497 -1.627540572 -0.68542407 -0.700932399 -1.67754023 1.019518689 0.809881735 -1.174379392 -1.567943462 0.164829603 -1.140227206 0.501899236 -0.186120892 +0.560677621 -1.489408639 -0.595520306 0.685234401 -0.723554811 0.416786873 0.334697124 0.138131933 0.089903764 1.386166801 0.953985419 -0.602731816 -0.580953121 +0.003248201 -0.340159692 -0.879529088 0.602469892 -0.876641151 -0.884034813 -0.55742942 1.149248947 -0.284008782 -0.08276451 -0.15308634 -1.300821686 -1.692923237 +0.836161459 -1.092255501 -2.618392652 0.707002513 -0.763363892 0.276104352 0.832913258 -0.752095809 -1.738863564 0.104532622 0.113277259 1.160139164 -0.371763879 +1.586566953 0.271349643 -0.649296386 -0.881991034 -0.310818575 0.552978122 0.750405494 1.363605144 1.969096266 -1.588993548 0.452545317 0.276873771 0.933331204 +0.667602572 1.549333738 0.076243832 0.833106558 0.518018373 -0.620225079 -0.918964381 1.277984095 0.725540218 1.715097593 0.828836948 -1.173203202 -0.852116981 +-0.01792122 2.740600234 0.074586135 1.164085584 -0.240669175 -0.440358808 -0.685523792 1.191266496 -0.001657697 0.330979026 -0.758687548 0.179866271 -0.092616197 +-0.659019247 2.298595672 0.17634643 -0.101416476 -0.182581812 2.015890229 -0.641098027 -0.442004562 0.101760295 -1.26550206 0.058087363 2.456249037 1.414856771 +-1.094112767 -0.186225918 -2.127567341 -0.660534996 0.727968953 0.549065179 -0.43509352 -2.48482159 -2.303913771 -0.55911852 0.910550765 -1.46682505 -1.264667218 +0.248649215 -0.618039127 -1.082778413 -2.323754365 -0.275433162 -0.224507966 1.342761982 -0.431813209 1.044788928 -1.66321937 -1.003402116 -0.773573145 0.297574192 +-0.034109838 0.196157196 0.285049845 0.929700246 -1.903128963 -1.640111364 -0.282759053 0.814196323 1.367828258 3.253454611 -1.627695801 -1.415603398 -0.749200249 +-0.154408808 0.946079652 1.025632299 -1.504803984 1.86795795 0.461493517 -0.12029897 0.749922456 0.740582454 -2.43450423 3.771086913 2.101604881 0.024314549 +0.764863858 0.933908359 -0.135353281 -0.109277214 0.002639137 -1.643878764 0.919272666 -0.012171293 -1.16098558 1.39552677 -1.865318813 -2.105372281 -1.143075439 +0.445796508 1.917424429 0.650347848 1.98385538 0.655358612 1.874592447 -0.31906735 0.98351607 0.785701129 2.093132594 0.652719475 3.51847121 -1.758526555 +0.207513285 1.298735387 2.318083224 -0.535817525 2.16070642 -0.721054976 -0.238283223 -0.618689042 1.667735376 -2.519672905 1.505347808 -2.595647423 0.666876476 +0.381782634 0.081083807 -1.984367893 0.563118012 0.313116397 -0.168924986 0.174269349 -1.21765158 -4.302451117 1.098935537 -1.847590024 0.55212999 0.145183641 +-1.47473253 0.515676307 1.024256498 -0.163074269 0.924446522 -0.68895112 -1.856515164 0.4345925 3.008624391 -0.726192282 0.611330125 -0.520026134 -0.051313681 +-1.494898436 0.766171566 1.630635233 1.316319173 0.756997088 -0.541944145 -0.020165906 0.250495259 0.606378735 1.479393442 -0.167449434 0.147006975 0.176664517 +-2.019896907 1.643560771 -0.311565305 -1.536321826 0.680356602 0.758927506 -0.524998471 0.877389205 -1.942200538 -2.852640999 -0.076640486 1.300871651 -1.264980299 +-2.178716752 0.973512277 2.252419221 -0.98610174 -1.462675416 1.890594069 -0.158819845 -0.670048494 2.563984526 0.550220085 -2.143032018 1.131666563 -1.113211169 +-1.232037427 -0.40191649 1.00829017 1.358255513 0.106048498 -0.129832743 0.946679325 -1.375428767 -1.244129051 2.344357253 1.568723914 -2.020426812 0.154120413 +0.731522293 0.40813286 1.025669565 0.164802434 1.923608997 1.773241326 1.96355972 0.81004935 0.017379395 -1.193453079 1.817560499 1.903074069 -1.34580855 +0.753719194 0.163556595 -1.064627798 -0.934732068 0.685816646 0.525679007 0.022196901 -0.244576265 -2.090297363 -1.099534501 -1.237792351 -1.247562319 0.192821615 +1.757683518 -0.537743502 -0.866195997 1.491399948 -0.345322156 0.446364892 1.003964324 -0.701300097 0.198431801 2.426132015 -1.031138802 -0.079314115 1.960430091 +1.550719352 0.11832591 -0.668387479 0.223298561 -1.812651721 0.058823033 -0.206964166 0.656069412 0.197808518 -1.268101387 -1.467329565 -0.387541859 -0.600970527 +2.288307277 0.791999772 0.75152785 0.068312984 1.468953207 -0.859155411 0.737587925 0.673673862 1.419915329 -0.154985576 3.281604929 -0.917978445 -0.296907317 +2.663681819 2.261180081 1.905593875 -1.140429944 -1.722043668 0.364115037 0.375374542 1.469180309 1.154066025 -1.208742929 -3.190996875 1.223270448 -0.862172265 +2.268142799 0.717724473 -2.382603885 0.436210872 0.218979462 0.089319574 -0.39553902 -1.543455608 -4.28819776 1.576640816 1.94102313 -0.274795463 0.906547155 +-0.218049277 -0.508412721 -2.487404957 1.887320081 1.04722817 -0.395861337 -2.486192076 -1.226137194 -0.104801072 1.45110921 0.828248708 -0.485180912 -0.451624542 +-0.410793483 0.116533685 1.879092728 1.184222196 -0.031140809 -0.90091931 -0.192744206 0.624946406 4.366497685 -0.703097886 -1.078368979 -0.505057973 0.118224392 +-0.647691228 0.838129685 1.374778227 0.322847522 1.175235974 1.277024175 -0.236897745 0.721596 -0.504314501 -0.861374674 1.206376783 2.177943485 -0.449494687 +-1.13985429 -0.495903004 1.056569633 -1.587672129 0.39860901 -0.768585548 -0.492163062 -1.334032689 -0.318208594 -1.910519651 -0.776626964 -2.045609723 -0.222016948 +0.53999669 -0.487881558 0.581258434 -1.085920121 -0.501406499 0.115378063 1.67985098 0.008021446 -0.475311199 0.501752008 -0.900015509 0.883963611 -0.703432192 +0.410905902 0.873257327 3.608291808 0.653652794 -1.54826995 1.468348054 -0.129090788 1.361138885 3.027033374 1.739572915 -1.04686345 1.352969991 0.299902147 +-0.173062544 0.529483977 1.076381362 -0.503373399 -0.058733834 -1.45590019 -0.583968446 -0.34377335 -2.531910446 -1.157026193 1.489536116 -2.924248244 0.005305683 +0.590240338 -0.560105813 -0.33143028 -0.14587712 1.474596747 1.017308667 0.763302882 -1.08958979 -1.407811642 0.35749628 1.533330581 2.473208857 -1.883857873 +-0.330152172 -0.29496616 -1.27480681 0.993753616 0.247092802 -0.980747759 -0.92039251 0.265139653 -0.94337653 1.139630735 -1.227503945 -1.998056427 -1.361923392 +-0.770820846 -1.130215098 -0.533069878 1.339112763 1.354475058 -0.663609074 -0.440668674 -0.835248938 0.741736932 0.345359147 1.107382257 0.317138685 -0.854950385 +-0.997000751 -1.594080868 -2.206716323 0.551420017 -0.360200976 0.897818757 -0.226179905 -0.46386577 -1.673646445 -0.787692746 -1.714676034 1.561427831 -0.509180861 +-0.078723051 -1.034723379 1.715724783 2.451708059 0.070115822 -0.667778397 0.9182777 0.559357489 3.922441106 1.900288042 0.430316798 -1.565597154 0.134291091 +0.317741611 -1.449732445 -0.829499847 1.984395673 1.236600623 -0.310344732 0.396464662 -0.415009066 -2.54522463 -0.467312386 1.166484801 0.357433665 0.330435696 +0.920629978 -3.107612041 -1.72016277 -1.6235986 -0.680428817 -0.15105238 0.602888367 -1.657879596 -0.890662923 -3.607994273 -1.917029441 0.159292352 0.21447828 +0.904171949 -3.25810681 -1.338419095 -0.225635225 0.492152931 -1.184901646 -0.016458029 -0.150494769 0.381743675 1.397963375 1.172581749 -1.033849266 0.467948374 +0.712432166 -2.702026568 -4.090712476 0.325379006 -1.008512619 0.155445809 -0.191739783 0.556080242 -2.752293381 0.551014231 -1.50066555 1.340347455 -0.409919798 +1.213580415 -2.416744199 -1.644462247 0.887889538 -1.019416802 -0.194144049 0.501148249 0.285282369 2.446250229 0.562510532 -0.010904183 -0.349589858 -0.954443807 +1.015276471 -0.468506664 0.826022926 -0.704628983 -0.831670525 0.350992643 -0.198303944 1.948237535 2.470485173 -1.592518521 0.187746276 0.545136692 -1.28584816 +0.458644239 0.304782879 -0.286629392 -0.622614244 0.81723357 -0.620482414 -0.556632232 0.773289543 -1.112652318 0.082014739 1.648904096 -0.971475057 -1.250751609 +0.696387231 0.478940055 -0.661704616 -0.225347907 -1.187184555 -0.81881664 0.237742992 0.174157176 -0.375075224 0.397266338 -2.004418126 -0.198334226 -0.58673588 +0.398048005 0.35203805 -0.836607472 -1.114036436 -1.728872921 -0.244092905 -0.298339226 -0.126902005 -0.174902856 -0.888688529 -0.541688366 0.574723735 -0.251724316 +-0.433479435 0.130772773 -0.702912597 -0.711315522 0.831520689 -1.273732568 -0.83152744 -0.221265277 0.133694875 0.402720914 2.56039361 -1.029639663 2.040725153 +-1.504705013 -0.117524037 -1.172268361 -1.301597961 -1.43149785 -0.320764583 -1.071225578 -0.24829681 -0.469355764 -0.590282439 -2.263018539 0.952967985 -0.258655658 +-1.966296873 -1.284318477 -0.647627254 0.187287319 -1.026578293 0.466508325 -0.46159186 -1.16679444 0.524641107 1.48888528 0.404919557 0.787272908 2.114290456 +-1.036674723 -0.437122887 0.788299251 1.208653611 1.589038974 1.098106245 0.92962215 0.84719559 1.435926505 1.021366292 2.615617267 0.63159792 -0.93052932 +-0.696570694 1.035997759 0.315168026 -0.180901513 -0.784289145 0.252979402 0.340104029 1.473120646 -0.473131225 -1.389555124 -2.373328119 -0.845126843 0.177085093 +-1.056655551 2.054949636 1.733679189 -1.593982301 -2.245797478 0.326044934 -0.360084857 1.018951877 1.418511163 -1.413080788 -1.461508333 0.073065532 -1.242278489 +-1.253872449 0.940519962 -1.859117584 -0.393650127 -0.324318496 0.436127281 -0.197216898 -1.114429674 -3.592796773 1.200332173 1.921478982 0.110082347 0.278345459 +-1.069287813 -0.5172584 -0.656338228 0.143574863 -1.262613776 1.600586235 0.184584636 -1.457778362 1.202779356 0.537224991 -0.93829528 1.164458954 -1.920302265 +0.073889742 1.015905744 3.017531937 -0.558421237 0.29254061 -0.615549542 1.143177555 1.533164144 3.673870165 -0.7019961 1.555154386 -2.216135777 0.003952104 +1.32888943 2.850501828 2.785235104 -0.929137834 -0.021765497 -0.581322431 1.254999688 1.834596084 -0.232296833 -0.370716597 -0.314306107 0.034227111 -0.854938554 +0.831392436 2.255038285 0.082410724 0.365767705 0.860518504 -0.916585465 -0.497496994 -0.595463543 -2.70282438 1.294905539 0.882284001 -0.335263034 -0.264536802 +-0.670035292 0.826737793 0.321644476 1.449735904 -0.344614203 -1.344199808 -1.501427728 -1.428300492 0.239233752 1.083968199 -1.205132707 -0.427614343 -0.829211295 +-0.478355817 0.188711887 -0.630649751 -1.102278018 -1.374564234 0.697153667 0.191679475 -0.638025906 -0.952294227 -2.552013922 -1.029950031 2.041353475 -1.752282527 +0.999877364 -1.06986647 1.290972976 -0.92101604 0.906070467 0.416570393 1.478233181 -1.258578357 1.921622727 0.181261978 2.280634701 -0.280583274 0.64858987 +0.069018404 0.516767176 1.573029961 0.362136079 -1.202013587 -0.428893561 -0.93085896 1.586633646 0.282056985 1.283152119 -2.108084054 -0.845463954 -0.423093312 +0.351758996 0.056295399 1.665144379 -0.415044976 0.482451076 -1.053100619 0.282740592 -0.460471777 0.092114418 -0.777181055 1.684464663 -0.624207058 0.664860241 +0.858424569 -0.363527343 1.550740147 0.917563357 -0.68983397 -1.261316549 0.506665573 -0.419822742 -0.114404232 1.332608333 -1.172285046 -0.20821593 -0.097170847 +1.334779099 -0.553036015 0.026277298 -1.303973749 0.14799305 -1.047775225 0.47635453 -0.189508672 -1.524462849 -2.221537106 0.83782702 0.213541323 -0.273332909 +0.365467586 -1.460953257 1.836000417 0.076497972 -1.230053674 -0.597101686 -0.969311513 -0.907917242 1.809723119 1.38047172 -1.378046723 0.450673539 -0.026346724 +-0.084225355 -0.90165276 0.41416722 1.144524716 0.509152904 -1.158340165 -0.449692941 0.559300497 -1.421833197 1.068026744 1.739206578 -0.561238479 -1.19547545 diff --git a/tedana/tests/data/external_regress_3echo.tsv b/tedana/tests/data/external_regress_corr_3echo.tsv similarity index 100% rename from tedana/tests/data/external_regress_3echo.tsv rename to tedana/tests/data/external_regress_corr_3echo.tsv diff --git a/tedana/tests/test_integration.py b/tedana/tests/test_integration.py index 41beb7eb7..e65decb1e 100644 --- a/tedana/tests/test_integration.py +++ b/tedana/tests/test_integration.py @@ -375,17 +375,16 @@ def test_integration_three_echo(skip_integration): check_integration_outputs(fn, out_dir) -def test_integration_three_echo_external_regressors(skip_integration): - """Integration test of tedana workflow. - - Three-echo test data & external regressors. - """ +def test_integration_three_echo_external_regressors_corr(skip_integration): + """Integration test of tedana workflow with extern regress and correlation.""" if skip_integration: pytest.skip("Skipping three-echo with external regressors integration test") test_data_path, osf_id = data_for_testing_info("three-echo") - out_dir = os.path.abspath(os.path.join(test_data_path, "../../outputs/three-echo-externalreg")) + out_dir = os.path.abspath( + os.path.join(test_data_path, "../../outputs/three-echo-externalreg-corr") + ) out_dir_manual = f"{out_dir}-rerun" if os.path.exists(out_dir): @@ -395,7 +394,7 @@ def test_integration_three_echo_external_regressors(skip_integration): shutil.rmtree(out_dir_manual) # download data and run the test - # external_regress_3echo.tsv has 6 rows. Based on a local run on the 3 echo data: + # external_regress_corr_3echo.tsv has 6 rows. Based on a local run on the 3 echo data: # Col 1 (trans_x_correlation) is the TS for ICA comp 59 + similar stdev Gaussian Noise # Col 2 (trans_y_correlation) is 0.4*comp29+0.5+comp20+Gaussian Noise # Col 3 (trans_z_correlation) is comp20+Gaussian Noise @@ -411,7 +410,62 @@ def test_integration_three_echo_external_regressors(skip_integration): tes=[14.5, 38.5, 62.5], out_dir=out_dir, tree=resource_filename("tedana", "resources/decision_trees/minimal_external2.json"), - external_regressors=resource_filename("tedana", "tests/data/external_regress_3echo.tsv"), + external_regressors=resource_filename( + "tedana", "tests/data/external_regress_corr_3echo.tsv" + ), + external_regressor_dict="corr_detrend", + low_mem=True, + tedpca="aic", + ) + + # compare the generated output files + fn = resource_filename("tedana", "tests/data/cornell_three_echo_outputs.txt") + check_integration_outputs(fn, out_dir) + + +def test_integration_three_echo_external_regressors_fstat(skip_integration): + """Integration test of tedana workflow with extern regress and F stat.""" + + if skip_integration: + pytest.skip("Skipping three-echo with external regressors integration test") + + test_data_path, osf_id = data_for_testing_info("three-echo") + out_dir = os.path.abspath( + os.path.join(test_data_path, "../../outputs/three-echo-externalreg-Ftest") + ) + out_dir_manual = f"{out_dir}-rerun" + + if os.path.exists(out_dir): + shutil.rmtree(out_dir) + + if os.path.exists(out_dir_manual): + shutil.rmtree(out_dir_manual) + + # download data and run the test + # external_regress_Ftest_3echo.tsv has 13 rows. Based on a local run on the 3 echo data: + # Col 1 (trans_x_correlation) is the TS for ICA comp 59 + similar stdev Gaussian Noise + # Col 2 (trans_y_correlation) is 0.4*comp29+0.5+comp20+Gaussian Noise + # Col 3 (trans_z_correlation) is comp20+Gaussian Noise + # The above are the same as the test that uses corr for external regressors + # Col 4-6 are Gaussian noise representing pitch/roll/yaw + # Col 7-12 are the first derivative of col 1-6 + # With the currently set up decision tree minimal_exteral 2, one component (my 59) + # should be rejected, but 20 and 29 aren't rejected because neither crosses the + # r>0.8 threshold. If trans_y and trans_Z were included in a single model then + # component 20 would have been rejected + # Note that the above is in comparision to the minimal decision tree + # but the integration test for 3 echoes uses the kundu tree + download_test_data(osf_id, test_data_path) + tedana_cli.tedana_workflow( + data=f"{test_data_path}/three_echo_Cornell_zcat.nii.gz", + tes=[14.5, 38.5, 62.5], + out_dir=out_dir, + tree=resource_filename("tedana", "resources/decision_trees/minimal_external3.json"), + external_regressors=resource_filename( + "tedana", "tests/data/external_regress_Ftest_3echo.tsv" + ), + # external_regressor_dict="Mot12_CSF", + external_regressor_dict="Fmodel", low_mem=True, tedpca="aic", ) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 8913b262f..c3ad17aaa 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -168,11 +168,19 @@ def _get_parser(): dest="external_regressors", type=lambda x: is_valid_file(parser, x), help=( - "File containing external regressors to be used in the decision tree. " + "File containing external regressors to compare to ICA component be used in the " + "decision tree. For example, to identify components fit head motion time series." "The file must be a TSV file with the same number of rows as the number of volumes in " - "the input data. Each column in the file will be treated as a separate regressor. " - "The decision tree must contain a node for each of the columns in this file, " - "with the metric '_correlation'." + "the input data. Column labels and statistical tests are defined with external_labels." + ), + default=None, + ) + optional.add_argument( + "--external_labels", + dest="external_regressor_dict", + help=( + "Column labels for external regressors & statistical tests to compare be used in the " + "decision tree. Either the path and name of a json file or a packaged tree (Mot12_CSF)" ), default=None, ) @@ -336,6 +344,7 @@ def tedana_workflow( combmode="t2s", tree="kundu", external_regressors=None, + external_regressor_dict=None, tedpca="aic", fixed_seed=42, maxit=500, @@ -513,10 +522,6 @@ def tedana_workflow( # a float on [0, 1] or an int >= 1 tedpca = check_tedpca_value(tedpca, is_parser=False) - # Load external regressors if provided - if external_regressors: - external_regressors = pd.read_table(external_regressors) - # For z-catted files, make sure it's a list of size 1 if isinstance(data, str): data = [data] @@ -524,6 +529,23 @@ def tedana_workflow( LGR.info(f"Loading input data: {[f for f in data]}") catd, ref_img = io.load_data(data, n_echos=n_echos) + # Load external regressors if provided + # Decided to do the validation here so that, if there are issues, an error + # will be raised before PCA/ICA + if external_regressors: + if external_regressor_dict: + ( + external_regressors, + external_regressor_dict, + ) = metrics.external_regressor_fits.load_validate_external_regressors( + external_regressors, external_regressor_dict, catd.shape[2] + ) + else: + raise ValueError( + "If external_regressors is an input, then " + "external_regressor_dict also needs to be used." + ) + io_generator = io.OutputGenerator( ref_img, convention=convention, @@ -705,8 +727,6 @@ def tedana_workflow( "normalized variance explained", "d_table_score", ] - if external_regressors is not None: - required_metrics.append("external correlation") comptable = metrics.collect.generate_metrics( data_cat=catd, @@ -717,6 +737,7 @@ def tedana_workflow( io_generator=io_generator, label="ICA", external_regressors=external_regressors, + external_regressor_dict=external_regressor_dict, metrics=required_metrics, ) ica_selector = selection.automatic_selection(comptable, n_echos, n_vols, tree=tree) From a574f71077ccf0015941f6bf963d39c3ba7805c2 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Wed, 14 Feb 2024 19:12:24 -0500 Subject: [PATCH 22/81] added corr_no_detrend.json --- tedana/resources/extern_regress_dicts/corr_no_detrend.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tedana/resources/extern_regress_dicts/corr_no_detrend.json diff --git a/tedana/resources/extern_regress_dicts/corr_no_detrend.json b/tedana/resources/extern_regress_dicts/corr_no_detrend.json new file mode 100644 index 000000000..541058df7 --- /dev/null +++ b/tedana/resources/extern_regress_dicts/corr_no_detrend.json @@ -0,0 +1,6 @@ +{ + "regess_ID": "corr_detrend", + "info": "Separately correlates every regressor after detrending regressors and fMRI data.", + "detrend": false, + "calc_stats": "corr" +} From 7b0b3488b70bfd25d13f53879bd4b97c707269bd Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Fri, 16 Feb 2024 10:55:46 -0500 Subject: [PATCH 23/81] updated names and reporting --- tedana/decomposition/pca.py | 2 +- tedana/metrics/collect.py | 5 ++++- ...> demo_minimal_externalregressors_Fstat.json} | 4 ++-- ..._minimal_externalregressors_correlation.json} | 4 ++-- ...ternalregressors_correlation_with_regex.json} | 4 ++-- .../resources/extern_regress_dicts/Fmodel.json | 1 + .../extern_regress_dicts/corr_detrend.json | 1 + .../extern_regress_dicts/corr_no_detrend.json | 1 + tedana/selection/component_selector.py | 14 +++++++++++++- tedana/selection/tedica.py | 16 ++++++++++++---- tedana/tests/test_integration.py | 6 ++++-- tedana/tests/test_metrics.py | 2 +- tedana/workflows/tedana.py | 13 ++++++++++--- 13 files changed, 54 insertions(+), 19 deletions(-) rename tedana/resources/decision_trees/{minimal_external3.json => demo_minimal_externalregressors_Fstat.json} (97%) rename tedana/resources/decision_trees/{minimal_external2.json => demo_minimal_externalregressors_correlation.json} (97%) rename tedana/resources/decision_trees/{minimal_external1.json => demo_minimal_externalregressors_correlation_with_regex.json} (96%) diff --git a/tedana/decomposition/pca.py b/tedana/decomposition/pca.py index 1b8d61139..8ca51cf50 100644 --- a/tedana/decomposition/pca.py +++ b/tedana/decomposition/pca.py @@ -349,7 +349,7 @@ def tedpca( "d_table_score", ] # Even if user inputted, don't fit external_regressors to PCA components - comptable = metrics.collect.generate_metrics( + comptable, _ = metrics.collect.generate_metrics( data_cat=data_cat, data_optcom=data_oc, mixing=comp_ts, diff --git a/tedana/metrics/collect.py b/tedana/metrics/collect.py index e63bd7a1b..ad4ee0eb7 100644 --- a/tedana/metrics/collect.py +++ b/tedana/metrics/collect.py @@ -63,6 +63,9 @@ def generate_metrics( comptable : (C x X) :obj:`pandas.DataFrame` Component metric table. One row for each component, with a column for each metric. The index is the component number. + external_regressor_dict : :obj:`dict` + Information describing the external regressors and + method used for fitting and statistical tests (or None if none were inputed) """ # Load metric dependency tree from json file dependency_config = op.join(utils.get_resource_path(), "config", "metrics.json") @@ -411,7 +414,7 @@ def generate_metrics( other_columns = [col for col in comptable.columns if col not in preferred_order] comptable = comptable[first_columns + other_columns] - return comptable + return comptable, external_regressor_dict def get_metadata(comptable): diff --git a/tedana/resources/decision_trees/minimal_external3.json b/tedana/resources/decision_trees/demo_minimal_externalregressors_Fstat.json similarity index 97% rename from tedana/resources/decision_trees/minimal_external3.json rename to tedana/resources/decision_trees/demo_minimal_externalregressors_Fstat.json index ea58d3fa4..7a7939e61 100644 --- a/tedana/resources/decision_trees/minimal_external3.json +++ b/tedana/resources/decision_trees/demo_minimal_externalregressors_Fstat.json @@ -1,6 +1,6 @@ { - "tree_id": "minimal_decision_tree_test3", - "info": "Proposed minimal decision tree that tests a full model of all external regressors.", + "tree_id": "demo_minimal_externalregressors_Fstat", + "info": "Demonstration based on the minimal decision tree that uses an F stat on a model with multiple external regressors.", "report": "This is based on the minimal criteria of the original MEICA decision tree \\citep{kundu2013integrated} without the more aggressive noise removal steps \\citep{dupre2021te}.", "necessary_metrics": [ "kappa", diff --git a/tedana/resources/decision_trees/minimal_external2.json b/tedana/resources/decision_trees/demo_minimal_externalregressors_correlation.json similarity index 97% rename from tedana/resources/decision_trees/minimal_external2.json rename to tedana/resources/decision_trees/demo_minimal_externalregressors_correlation.json index 62e01ca04..c2c03f662 100644 --- a/tedana/resources/decision_trees/minimal_external2.json +++ b/tedana/resources/decision_trees/demo_minimal_externalregressors_correlation.json @@ -1,6 +1,6 @@ { - "tree_id": "minimal_decision_tree_test1", - "info": "Proposed minimal decision tree with a separate threshold for each external regressor correlation metric.", + "tree_id": "demo_minimal_externalregressors_correlation", + "info": "Demonstration based on the minimal decision tree that calculates a correlation for each external regressor and sets a threshold for each regressor in a separate node.", "report": "This is based on the minimal criteria of the original MEICA decision tree \\citep{kundu2013integrated} without the more aggressive noise removal steps \\citep{dupre2021te}.", "necessary_metrics": [ "kappa", diff --git a/tedana/resources/decision_trees/minimal_external1.json b/tedana/resources/decision_trees/demo_minimal_externalregressors_correlation_with_regex.json similarity index 96% rename from tedana/resources/decision_trees/minimal_external1.json rename to tedana/resources/decision_trees/demo_minimal_externalregressors_correlation_with_regex.json index 1385cd5e1..beb126ebf 100644 --- a/tedana/resources/decision_trees/minimal_external1.json +++ b/tedana/resources/decision_trees/demo_minimal_externalregressors_correlation_with_regex.json @@ -1,6 +1,6 @@ { - "tree_id": "minimal_decision_tree_test1", - "info": "Proposed minimal decision tree with a single threshold for all external regressor correlation metrics.", + "tree_id": "demo_minimal_externalregressors_correlation_with_regex", + "info": "Demonstration based on the minimal decision tree that calculates a correlation for each external regressors and sets a threshold for a single node for all of them using regex syntax.", "report": "This is based on the minimal criteria of the original MEICA decision tree \\citep{kundu2013integrated} without the more aggressive noise removal steps \\citep{dupre2021te}.", "necessary_metrics": [ "kappa", diff --git a/tedana/resources/extern_regress_dicts/Fmodel.json b/tedana/resources/extern_regress_dicts/Fmodel.json index 469abe3cd..9c0aebb60 100644 --- a/tedana/resources/extern_regress_dicts/Fmodel.json +++ b/tedana/resources/extern_regress_dicts/Fmodel.json @@ -1,6 +1,7 @@ { "regess_ID": "Fmodel", "info": "Fits all external regressors to a single model using an F statistic", + "report": "Unspecified external nuissance regressors were fit to components using a linear model were rejected if they fit.", "detrend": true, "calc_stats": "F" } diff --git a/tedana/resources/extern_regress_dicts/corr_detrend.json b/tedana/resources/extern_regress_dicts/corr_detrend.json index f4c364537..1142d1ba7 100644 --- a/tedana/resources/extern_regress_dicts/corr_detrend.json +++ b/tedana/resources/extern_regress_dicts/corr_detrend.json @@ -1,6 +1,7 @@ { "regess_ID": "corr_detrend", "info": "Separately correlates every regressor after detrending regressors and fMRI data.", + "report": "Unspecified external nuissance regressors were detrended and correlated to components and rejected if any individual regressor had a high correlation.", "detrend": true, "calc_stats": "corr" } diff --git a/tedana/resources/extern_regress_dicts/corr_no_detrend.json b/tedana/resources/extern_regress_dicts/corr_no_detrend.json index 541058df7..0fd6e1c83 100644 --- a/tedana/resources/extern_regress_dicts/corr_no_detrend.json +++ b/tedana/resources/extern_regress_dicts/corr_no_detrend.json @@ -1,6 +1,7 @@ { "regess_ID": "corr_detrend", "info": "Separately correlates every regressor after detrending regressors and fMRI data.", + "report": "Unspecified external nuissance regressors were correlated to components and rejected if any individual regressor had a high correlation.", "detrend": false, "calc_stats": "corr" } diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 4943249fa..8175bbf51 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -217,7 +217,14 @@ def validate_tree(tree): class ComponentSelector: """Load and classify components based on a specified ``tree``.""" - def __init__(self, tree, component_table, cross_component_metrics={}, status_table=None): + def __init__( + self, + tree, + component_table, + cross_component_metrics={}, + status_table=None, + external_regressor_dict=None, + ): """Initialize the class using the info specified in the json file ``tree``. Parameters @@ -234,6 +241,9 @@ def __init__(self, tree, component_table, cross_component_metrics={}, status_tab A table tracking the status of each component at each step. Pass a status table if running additional steps on a decision tree that was already executed. Default=None. + external_regressor_dict : :obj:`dict` + Information describing the external regressors and + method to use for fitting and statistical tests Notes ----- @@ -286,6 +296,8 @@ def __init__(self, tree, component_table, cross_component_metrics={}, status_tab LGR.info("Performing component selection with " + tree_config["tree_id"]) LGR.info(tree_config.get("info", "")) RepLGR.info(tree_config.get("report", "")) + if external_regressor_dict is not None: + RepLGR.info(external_regressor_dict["report"]) self.tree["nodes"] = tree_config["nodes"] self.necessary_metrics = set(tree_config["necessary_metrics"]) diff --git a/tedana/selection/tedica.py b/tedana/selection/tedica.py index bc335354e..d5e9d181f 100644 --- a/tedana/selection/tedica.py +++ b/tedana/selection/tedica.py @@ -9,7 +9,9 @@ RepLGR = logging.getLogger("REPORT") -def automatic_selection(component_table, n_echos, n_vols, tree="kundu"): +def automatic_selection( + component_table, n_echos, n_vols, tree="kundu", external_regressor_dict=None +): """Classify components based on component table and decision tree type. Parameters @@ -20,8 +22,9 @@ def automatic_selection(component_table, n_echos, n_vols, tree="kundu"): The number of echoes in this dataset tree : :obj:`str` The type of tree to use for the ComponentSelector object. Default="kundu" - verbose : :obj:`bool` - More verbose logging output if True. Default=False + external_regressor_dict : :obj:`dict` + Information describing the external regressors and + method to use for fitting and statistical tests Returns ------- @@ -67,7 +70,12 @@ def automatic_selection(component_table, n_echos, n_vols, tree="kundu"): "n_echos": n_echos, "n_vols": n_vols, } - selector = ComponentSelector(tree, component_table, cross_component_metrics=xcomp) + selector = ComponentSelector( + tree, + component_table, + cross_component_metrics=xcomp, + external_regressor_dict=external_regressor_dict, + ) selector.select() selector.metadata = collect.get_metadata(selector.component_table) diff --git a/tedana/tests/test_integration.py b/tedana/tests/test_integration.py index e65decb1e..2a8245ad9 100644 --- a/tedana/tests/test_integration.py +++ b/tedana/tests/test_integration.py @@ -405,11 +405,12 @@ def test_integration_three_echo_external_regressors_corr(skip_integration): # Note that the above is in comparision to the minimal decision tree # but the integration test for 3 echoes uses the kundu tree download_test_data(osf_id, test_data_path) + tree_name = "resources/decision_trees/demo_minimal_externalregressors_correlation.json" tedana_cli.tedana_workflow( data=f"{test_data_path}/three_echo_Cornell_zcat.nii.gz", tes=[14.5, 38.5, 62.5], out_dir=out_dir, - tree=resource_filename("tedana", "resources/decision_trees/minimal_external2.json"), + tree=resource_filename("tedana", tree_name), external_regressors=resource_filename( "tedana", "tests/data/external_regress_corr_3echo.tsv" ), @@ -456,11 +457,12 @@ def test_integration_three_echo_external_regressors_fstat(skip_integration): # Note that the above is in comparision to the minimal decision tree # but the integration test for 3 echoes uses the kundu tree download_test_data(osf_id, test_data_path) + tree_name = "resources/decision_trees/demo_minimal_externalregressors_Fstat.json" tedana_cli.tedana_workflow( data=f"{test_data_path}/three_echo_Cornell_zcat.nii.gz", tes=[14.5, 38.5, 62.5], out_dir=out_dir, - tree=resource_filename("tedana", "resources/decision_trees/minimal_external3.json"), + tree=resource_filename("tedana", tree_name), external_regressors=resource_filename( "tedana", "tests/data/external_regress_Ftest_3echo.tsv" ), diff --git a/tedana/tests/test_metrics.py b/tedana/tests/test_metrics.py index 3c61f8bcb..d9152bd3a 100644 --- a/tedana/tests/test_metrics.py +++ b/tedana/tests/test_metrics.py @@ -47,7 +47,7 @@ def test_smoke_generate_metrics(testdata1): "normalized variance explained", "d_table_score", ] - comptable = collect.generate_metrics( + comptable, _ = collect.generate_metrics( data_cat=testdata1["data_cat"], data_optcom=testdata1["data_optcom"], mixing=testdata1["mixing"], diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index c3ad17aaa..bf2aa6cc2 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -728,7 +728,7 @@ def tedana_workflow( "d_table_score", ] - comptable = metrics.collect.generate_metrics( + comptable, external_regressor_dict = metrics.collect.generate_metrics( data_cat=catd, data_optcom=data_oc, mixing=mmix, @@ -740,7 +740,13 @@ def tedana_workflow( external_regressor_dict=external_regressor_dict, metrics=required_metrics, ) - ica_selector = selection.automatic_selection(comptable, n_echos, n_vols, tree=tree) + ica_selector = selection.automatic_selection( + comptable, + n_echos, + n_vols, + tree=tree, + external_regressor_dict=external_regressor_dict, + ) n_likely_bold_comps = ica_selector.n_likely_bold_comps if (n_restarts < maxrestart) and (n_likely_bold_comps == 0): LGR.warning("No BOLD components found. Re-attempting ICA.") @@ -774,7 +780,7 @@ def tedana_workflow( "normalized variance explained", "d_table_score", ] - comptable = metrics.collect.generate_metrics( + comptable, external_regressor_dict = metrics.collect.generate_metrics( data_cat=catd, data_optcom=data_oc, mixing=mmix, @@ -783,6 +789,7 @@ def tedana_workflow( io_generator=io_generator, label="ICA", external_regressors=external_regressors, + external_regressor_dict=external_regressor_dict, metrics=required_metrics, ) ica_selector = selection.automatic_selection( From ab967aa044cf3946be343263433ccd5c89722093 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Fri, 16 Feb 2024 15:27:16 -0500 Subject: [PATCH 24/81] Run black. --- tedana/selection/selection_nodes.py | 8 ++++++-- tedana/selection/selection_utils.py | 6 +++--- tedana/tests/test_selection_nodes.py | 14 ++++++++++---- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/tedana/selection/selection_nodes.py b/tedana/selection/selection_nodes.py index b54a738f6..ae593d7d6 100644 --- a/tedana/selection/selection_nodes.py +++ b/tedana/selection/selection_nodes.py @@ -1370,7 +1370,9 @@ def calc_varex_thresh( num_highest_var_comps = len(comps2use) sorted_varex = np.flip( - np.sort((selector.component_table_.loc[comps2use, "variance explained"]).to_numpy()) + np.sort( + (selector.component_table_.loc[comps2use, "variance explained"]).to_numpy() + ) ) outputs[varex_name] = scoreatpercentile( sorted_varex[:num_highest_var_comps], percentile_thresh @@ -1853,7 +1855,9 @@ def calc_revised_meanmetricrank_guesses( tmp_kappa = selector.component_table_.loc[comps2use, "kappa"].to_numpy() tmp_dice_FT2 = selector.component_table_.loc[comps2use, "dice_FT2"].to_numpy() - tmp_signal_m_noise_t = selector.component_table_.loc[comps2use, "signal-noise_t"].to_numpy() + tmp_signal_m_noise_t = selector.component_table_.loc[ + comps2use, "signal-noise_t" + ].to_numpy() tmp_countnoise = selector.component_table_.loc[comps2use, "countnoise"].to_numpy() tmp_countsigFT2 = selector.component_table_.loc[comps2use, "countsigFT2"].to_numpy() tmp_d_table_score = generate_decision_table_score( diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index f1bb656b1..b3f3906f6 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -159,9 +159,9 @@ def change_comptable_classifications( dont_warn_reclassify=dont_warn_reclassify, ) - selector.component_status_table_[ - f"Node {selector.current_node_idx_}" - ] = selector.component_table_["classification"] + selector.component_status_table_[f"Node {selector.current_node_idx_}"] = ( + selector.component_table_["classification"] + ) n_true = decision_boolean.sum() n_false = np.logical_not(decision_boolean).sum() diff --git a/tedana/tests/test_selection_nodes.py b/tedana/tests/test_selection_nodes.py index 898b49e68..2887a52e5 100644 --- a/tedana/tests/test_selection_nodes.py +++ b/tedana/tests/test_selection_nodes.py @@ -446,7 +446,9 @@ def test_calc_kappa_elbow(): # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_elbow_kundu"] > 0 - assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_allcomps_elbow"] > 0 + assert ( + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_allcomps_elbow"] > 0 + ) assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_nonsig_elbow"] > 0 assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["varex_upper_p"] > 0 @@ -470,7 +472,9 @@ def test_calc_kappa_elbow(): # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_elbow_kundu"] > 0 - assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_allcomps_elbow"] > 0 + assert ( + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_allcomps_elbow"] > 0 + ) assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["kappa_nonsig_elbow"] > 0 assert selector.tree["nodes"][selector.current_node_idx_]["outputs"]["varex_upper_p"] > 0 @@ -1091,7 +1095,8 @@ def test_calc_max_good_meanmetricrank_smoke(): # Confirming the intended metrics are added to outputs and they have non-zero values assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 assert ( - selector.tree["nodes"][selector.current_node_idx_]["outputs"]["max_good_meanmetricrank"] > 0 + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["max_good_meanmetricrank"] + > 0 ) # Standard call to this function with a user defined metric_suffix @@ -1126,7 +1131,8 @@ def test_calc_max_good_meanmetricrank_smoke(): ) assert len(output_calc_cross_comp_metrics - calc_cross_comp_metrics) == 0 assert ( - selector.tree["nodes"][selector.current_node_idx_]["outputs"]["max_good_meanmetricrank"] > 0 + selector.tree["nodes"][selector.current_node_idx_]["outputs"]["max_good_meanmetricrank"] + > 0 ) # Raise an error if "extend_factor" isn't pre-defined From cc4118e76c051f69a80b598a4d84c76f15724d86 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Fri, 16 Feb 2024 15:30:15 -0500 Subject: [PATCH 25/81] Address style issues. --- tedana/selection/component_selector.py | 4 +--- tedana/selection/selection_nodes.py | 4 ++-- tedana/selection/selection_utils.py | 1 - 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 0392a7a2c..e91d8a9dd 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -229,7 +229,6 @@ def __init__(self, tree): Initializing the ``ComponentSelector`` confirms tree is valid and loads all information in the tree json file into ``ComponentSelector``. """ - self.tree_name = tree self.tree = load_config(self.tree_name) @@ -262,7 +261,7 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) tree that was already executed. Default=None. Notes - ------- + ----- Adds to the ``ComponentSelector``: - component_status_table_: empty dataframe or contents of inputted status_table @@ -312,7 +311,6 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) everything that changed in each node - current_node_idx_: The total number of nodes run in ``ComponentSelector`` """ - self.__dict__.update(cross_component_metrics) self.cross_component_metrics_ = cross_component_metrics diff --git a/tedana/selection/selection_nodes.py b/tedana/selection/selection_nodes.py index ae593d7d6..ab216291a 100644 --- a/tedana/selection/selection_nodes.py +++ b/tedana/selection/selection_nodes.py @@ -1854,12 +1854,12 @@ def calc_revised_meanmetricrank_guesses( outputs["conservative_guess"] = outputs["num_acc_guess"] / outputs["restrict_factor"] tmp_kappa = selector.component_table_.loc[comps2use, "kappa"].to_numpy() - tmp_dice_FT2 = selector.component_table_.loc[comps2use, "dice_FT2"].to_numpy() + tmp_dice_ft2 = selector.component_table_.loc[comps2use, "dice_FT2"].to_numpy() tmp_signal_m_noise_t = selector.component_table_.loc[ comps2use, "signal-noise_t" ].to_numpy() tmp_countnoise = selector.component_table_.loc[comps2use, "countnoise"].to_numpy() - tmp_countsigFT2 = selector.component_table_.loc[comps2use, "countsigFT2"].to_numpy() + tmp_countsig_ft2 = selector.component_table_.loc[comps2use, "countsigFT2"].to_numpy() tmp_d_table_score = generate_decision_table_score( tmp_kappa, tmp_dice_ft2, tmp_signal_m_noise_t, tmp_countnoise, tmp_countsig_ft2 ) diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index b3f3906f6..1d839628b 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -34,7 +34,6 @@ def selectcomps2use(selector, decide_comps): comps2use : :obj:`list[int]` A list of component indices with classifications included in decide_comps """ - if "classification" not in selector.component_table_: raise ValueError( "selector.component_table_ needs a 'classification' column to run selectcomp2suse" From cd38577a1f7cb128e6828f36560015d1902e5100 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Fri, 16 Feb 2024 15:48:49 -0500 Subject: [PATCH 26/81] Try fixing test bugs. --- tedana/selection/selection_nodes.py | 4 +-- tedana/tests/test_component_selector.py | 48 ++++++++----------------- tedana/tests/test_selection_utils.py | 3 +- 3 files changed, 19 insertions(+), 36 deletions(-) diff --git a/tedana/selection/selection_nodes.py b/tedana/selection/selection_nodes.py index ab216291a..9bceb21ca 100644 --- a/tedana/selection/selection_nodes.py +++ b/tedana/selection/selection_nodes.py @@ -523,7 +523,7 @@ def dec_variance_lessthan_thresholds( %(used_metrics)s """ outputs = { - "decision_node_idx": selector.current_node_idx, + "decision_node_idx": selector.current_node_idx_, "used_metrics": {var_metric}, "node_label": None, "n_true": None, @@ -1124,7 +1124,7 @@ def dec_reclassify_high_var_comps( """ # predefine all outputs that should be logged outputs = { - "decision_node_idx": selector.current_node_idx, + "decision_node_idx": selector.current_node_idx_, "used_metrics": {"variance explained"}, "used_cross_comp_metrics": {"varex_upper_p"}, "node_label": None, diff --git a/tedana/tests/test_component_selector.py b/tedana/tests/test_component_selector.py index 2e1e64624..d240d08dd 100644 --- a/tedana/tests/test_component_selector.py +++ b/tedana/tests/test_component_selector.py @@ -43,7 +43,6 @@ def dicts_to_test(treechoice): ------- tree : :ojb:`dict` A dict that can be input into component_selector.validate_tree """ - # valid_dict is a simple valid dictionary to test # It includes a few things that should trigger warnings, but not errors. valid_dict = { @@ -172,21 +171,13 @@ def test_minimal(): xcomp = { "n_echos": 3, } - selector = component_selector.ComponentSelector( - "minimal", - sample_comptable(), - cross_component_metrics=xcomp.copy(), - ) - selector.select() + selector = component_selector.ComponentSelector(tree="minimal") + selector.select(component_table=sample_comptable(), cross_component_metrics=xcomp.copy()) # rerun without classification_tags column initialized - selector = component_selector.ComponentSelector( - "minimal", - sample_comptable(), - cross_component_metrics=xcomp.copy(), - ) - selector.component_table_ = selector.component_table_.drop(columns="classification_tags") - selector.select() + selector = component_selector.ComponentSelector(tree="minimal") + temp_comptable = sample_comptable().drop(columns="classification_tags") + selector.select(component_table=temp_comptable, cross_component_metrics=xcomp.copy()) # validate_tree @@ -266,7 +257,7 @@ def test_validate_tree_fails(): def test_check_null_fails(): """Tests to trigger check_null missing parameter error.""" - selector = component_selector.ComponentSelector("minimal", sample_comptable()) + selector = component_selector.ComponentSelector(tree="minimal") selector.tree = dicts_to_test("null_value") params = selector.tree["nodes"][0]["parameters"] @@ -277,17 +268,7 @@ def test_check_null_fails(): def test_check_null_succeeds(): """Tests check_null finds empty parameter in self.""" - - # "left" is missing from the function definition in node - # but is found as an initialized cross component metric - xcomp = { - "left": 3, - } - selector = component_selector.ComponentSelector( - "minimal", - sample_comptable(), - cross_component_metrics=xcomp, - ) + selector = component_selector.ComponentSelector(tree="minimal") selector.tree = dicts_to_test("null_value") params = selector.tree["nodes"][0]["parameters"] @@ -297,28 +278,29 @@ def test_check_null_succeeds(): def test_are_only_necessary_metrics_used_warning(): """Tests a warning that wasn't triggered in other test workflows.""" - - selector = component_selector.ComponentSelector("minimal", sample_comptable()) + selector = component_selector.ComponentSelector(tree="minimal") + selector.select(component_table=sample_comptable()) # warning when an element of necessary_metrics was not in used_metrics selector.tree["used_metrics"] = {"A", "B", "C"} selector.necessary_metrics = {"B", "C", "D"} - selector.are_only_necessary_metrics_used() + assert selector.are_only_necessary_metrics_used() def test_are_all_components_accepted_or_rejected(): """Tests warnings are triggered in are_all_components_accepted_or_rejected.""" - - selector = component_selector.ComponentSelector("minimal", sample_comptable()) + selector = component_selector.ComponentSelector(tree="minimal") + selector.select(component_table=sample_comptable()) selector.component_table_.loc[7, "classification"] = "intermediate1" selector.component_table_.loc[[1, 3, 5], "classification"] = "intermediate2" - selector.are_all_components_accepted_or_rejected() + assert selector.are_all_components_accepted_or_rejected() def test_selector_properties_smoke(): """Tests to confirm properties match expected results.""" - selector = component_selector.ComponentSelector("minimal", sample_comptable()) + selector = component_selector.ComponentSelector(tree="minimal") + selector.select(component_table=sample_comptable()) assert selector.n_comps_ == 21 diff --git a/tedana/tests/test_selection_utils.py b/tedana/tests/test_selection_utils.py index 5afa97cf3..849204780 100644 --- a/tedana/tests/test_selection_utils.py +++ b/tedana/tests/test_selection_utils.py @@ -54,7 +54,8 @@ def sample_selector(options=None): "n_vols": 201, "test_elbow": 21, } - selector = ComponentSelector(tree, component_table, cross_component_metrics=xcomp) + selector = ComponentSelector(tree=tree) + selector.select(component_table=component_table, cross_component_metrics=xcomp) selector.current_node_idx_ = 0 return selector From c4de5be4c43316b0db77d2ddbaeb3ca04bf2ea17 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Fri, 16 Feb 2024 16:01:26 -0500 Subject: [PATCH 27/81] Update test_component_selector.py --- tedana/tests/test_component_selector.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tedana/tests/test_component_selector.py b/tedana/tests/test_component_selector.py index d240d08dd..ef7465426 100644 --- a/tedana/tests/test_component_selector.py +++ b/tedana/tests/test_component_selector.py @@ -279,21 +279,21 @@ def test_check_null_succeeds(): def test_are_only_necessary_metrics_used_warning(): """Tests a warning that wasn't triggered in other test workflows.""" selector = component_selector.ComponentSelector(tree="minimal") - selector.select(component_table=sample_comptable()) + # selector.select(component_table=sample_comptable()) # warning when an element of necessary_metrics was not in used_metrics selector.tree["used_metrics"] = {"A", "B", "C"} selector.necessary_metrics = {"B", "C", "D"} - assert selector.are_only_necessary_metrics_used() + selector.are_only_necessary_metrics_used() def test_are_all_components_accepted_or_rejected(): """Tests warnings are triggered in are_all_components_accepted_or_rejected.""" selector = component_selector.ComponentSelector(tree="minimal") - selector.select(component_table=sample_comptable()) + selector.select(component_table=sample_comptable(), cross_component_metrics={"n_echos": 3}) selector.component_table_.loc[7, "classification"] = "intermediate1" selector.component_table_.loc[[1, 3, 5], "classification"] = "intermediate2" - assert selector.are_all_components_accepted_or_rejected() + selector.are_all_components_accepted_or_rejected() def test_selector_properties_smoke(): From 79de1f38182e56427a694587bbbd695e1edda0c3 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Fri, 16 Feb 2024 16:15:21 -0500 Subject: [PATCH 28/81] Update component_selector.py --- tedana/selection/component_selector.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index e91d8a9dd..a0bae5bfa 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -311,6 +311,14 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) everything that changed in each node - current_node_idx_: The total number of nodes run in ``ComponentSelector`` """ + # this will crash the program with an error message if not all + # necessary_metrics are in the comptable + confirm_metrics_exist( + self.component_table_, + self.necessary_metrics, + function_name=self.tree_name, + ) + self.__dict__.update(cross_component_metrics) self.cross_component_metrics_ = cross_component_metrics @@ -341,14 +349,6 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) if "classification_tags" not in self.component_table_.columns: self.component_table_["classification_tags"] = "" - # this will crash the program with an error message if not all - # necessary_metrics are in the comptable - confirm_metrics_exist( - self.component_table_, - self.necessary_metrics, - function_name=self.tree_name, - ) - # for each node in the decision tree for self.current_node_idx_, node in enumerate( self.tree["nodes"][self.start_idx_ :], start=self.start_idx_ @@ -365,7 +365,7 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) kwargs = self.check_null(kwargs, node["functionname"]) all_params = {**params, **kwargs} else: - kwargs = None + kwargs = {} all_params = {**params} LGR.debug( @@ -373,10 +373,7 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) f"with parameters: {all_params}" ) # run the decision node function - if kwargs is not None: - self = fcn(self, **params, **kwargs) - else: - self = fcn(self, **params) + self = fcn(self, **params, **kwargs) self.tree["used_metrics"].update( self.tree["nodes"][self.current_node_idx_]["outputs"]["used_metrics"] From 41ab34037e82cbcbaaf990ad4c16cd0c769e7143 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Sun, 18 Feb 2024 08:51:48 -0500 Subject: [PATCH 29/81] Use component table directly in selectcomps2use. --- tedana/selection/selection_nodes.py | 30 ++++++++++++++-------------- tedana/selection/selection_utils.py | 24 ++++++++++------------ tedana/tests/test_selection_utils.py | 12 +++++------ 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/tedana/selection/selection_nodes.py b/tedana/selection/selection_nodes.py index 9bceb21ca..0c247ad4d 100644 --- a/tedana/selection/selection_nodes.py +++ b/tedana/selection/selection_nodes.py @@ -110,7 +110,7 @@ def manual_classify( if log_extra_report: RepLGR.info(log_extra_report) - comps2use = selectcomps2use(selector, decide_comps) + comps2use = selectcomps2use(selector.component_table_, decide_comps) if not comps2use: log_decision_tree_step(function_name_idx, comps2use, decide_comps=decide_comps) @@ -244,7 +244,7 @@ def dec_left_op_right( function_name_idx = f"Step {selector.current_node_idx_}: left_op_right" # Only select components if the decision tree is being run if not only_used_metrics: - comps2use = selectcomps2use(selector, decide_comps) + comps2use = selectcomps2use(selector.component_table_, decide_comps) def identify_used_metric(val, isnum=False): """ @@ -547,7 +547,7 @@ def dec_variance_lessthan_thresholds( if log_extra_report: RepLGR.info(log_extra_report) - comps2use = selectcomps2use(selector, decide_comps) + comps2use = selectcomps2use(selector.component_table_, decide_comps) confirm_metrics_exist( selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) @@ -671,7 +671,7 @@ def calc_median( if log_extra_report: RepLGR.info(log_extra_report) - comps2use = selectcomps2use(selector, decide_comps) + comps2use = selectcomps2use(selector.component_table_, decide_comps) confirm_metrics_exist( selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) @@ -779,7 +779,7 @@ def calc_kappa_elbow( if log_extra_report: RepLGR.info(log_extra_report) - comps2use = selectcomps2use(selector, decide_comps) + comps2use = selectcomps2use(selector.component_table_, decide_comps) confirm_metrics_exist( selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) @@ -904,12 +904,12 @@ def calc_rho_elbow( if log_extra_report: RepLGR.info(log_extra_report) - comps2use = selectcomps2use(selector, decide_comps) + comps2use = selectcomps2use(selector.component_table_, decide_comps) confirm_metrics_exist( selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) - subset_comps2use = selectcomps2use(selector, subset_decide_comps) + subset_comps2use = selectcomps2use(selector.component_table_, subset_decide_comps) if not comps2use: log_decision_tree_step( @@ -1038,9 +1038,9 @@ def dec_classification_doesnt_exist( if_true = new_classification if_false = "nochange" - comps2use = selectcomps2use(selector, decide_comps) + comps2use = selectcomps2use(selector.component_table_, decide_comps) - do_comps_exist = selectcomps2use(selector, class_comp_exists) + do_comps_exist = selectcomps2use(selector.component_table_, class_comp_exists) if (not comps2use) or (len(do_comps_exist) >= at_least_num_exist): outputs["n_true"] = 0 @@ -1153,7 +1153,7 @@ def dec_reclassify_high_var_comps( if_true = new_classification if_false = "nochange" - comps2use = selectcomps2use(selector, decide_comps) + comps2use = selectcomps2use(selector.component_table_, decide_comps) if "varex_upper_p" not in selector.cross_component_metrics_: if not comps2use: @@ -1301,7 +1301,7 @@ def calc_varex_thresh( f"{perc_name} already calculated. Overwriting previous value in {function_name_idx}" ) - comps2use = selectcomps2use(selector, decide_comps) + comps2use = selectcomps2use(selector.component_table_, decide_comps) confirm_metrics_exist( selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) @@ -1537,7 +1537,7 @@ def calc_max_good_meanmetricrank( if log_extra_report: RepLGR.info(log_extra_report) - comps2use = selectcomps2use(selector, decide_comps) + comps2use = selectcomps2use(selector.component_table_, decide_comps) confirm_metrics_exist( selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) @@ -1642,7 +1642,7 @@ def calc_varex_kappa_ratio( if log_extra_report: RepLGR.info(log_extra_report) - comps2use = selectcomps2use(selector, decide_comps) + comps2use = selectcomps2use(selector.component_table_, decide_comps) confirm_metrics_exist( selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) @@ -1788,7 +1788,7 @@ def calc_revised_meanmetricrank_guesses( "cause problems since these are only calculated on a subset of components" ) - comps2use = selectcomps2use(selector, decide_comps) + comps2use = selectcomps2use(selector.component_table_, decide_comps) confirm_metrics_exist( selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) @@ -1818,7 +1818,7 @@ def calc_revised_meanmetricrank_guesses( if log_extra_report: RepLGR.info(log_extra_report) - comps2use = selectcomps2use(selector, decide_comps) + comps2use = selectcomps2use(selector.component_table_, decide_comps) confirm_metrics_exist( selector.component_table_, outputs["used_metrics"], function_name=function_name_idx ) diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index 1d839628b..888c32c5b 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -14,12 +14,12 @@ ############################################################## -def selectcomps2use(selector, decide_comps): +def selectcomps2use(comptable, decide_comps): """Get a list of component numbers that fit the classification types in ``decide_comps``. Parameters ---------- - selector : :obj:`~tedana.selection.component_selector.ComponentSelector` + comptable : :obj:`~pandas.DataFrame` Only uses the component_table in this object decide_comps : :obj:`str` or :obj:`list[str]` or :obj:`list[int]` This is string or a list of strings describing what classifications @@ -34,33 +34,31 @@ def selectcomps2use(selector, decide_comps): comps2use : :obj:`list[int]` A list of component indices with classifications included in decide_comps """ - if "classification" not in selector.component_table_: - raise ValueError( - "selector.component_table_ needs a 'classification' column to run selectcomp2suse" - ) + if "classification" not in comptable: + raise ValueError("comptable needs a 'classification' column to run selectcomps2use") if isinstance(decide_comps, (str, int)): decide_comps = [decide_comps] if isinstance(decide_comps, list) and (decide_comps[0] == "all"): - # All components with any string in the classification field - # are set to True - comps2use = list(range(selector.component_table_.shape[0])) + # All components with any string in the classification field are set to True + comps2use = list(range(comptable.shape[0])) elif isinstance(decide_comps, list) and all(isinstance(elem, str) for elem in decide_comps): comps2use = [] for didx in range(len(decide_comps)): - newcomps2use = selector.component_table_.index[ - selector.component_table_["classification"] == decide_comps[didx] + newcomps2use = comptable.index[ + comptable["classification"] == decide_comps[didx] ].tolist() comps2use = list(set(comps2use + newcomps2use)) + elif isinstance(decide_comps, list) and all(isinstance(elem, int) for elem in decide_comps): # decide_comps is already a list of indices - if len(selector.component_table_) <= max(decide_comps): + if len(comptable) <= max(decide_comps): raise ValueError( "decide_comps for selectcomps2use is selecting for a component with index" f"{max(decide_comps)} (0 indexing) which is greater than the number " - f"of components: {len(selector.component_table_)}" + f"of components: {len(comptable)}" ) elif min(decide_comps) < 0: raise ValueError( diff --git a/tedana/tests/test_selection_utils.py b/tedana/tests/test_selection_utils.py index 849204780..9abc15a53 100644 --- a/tedana/tests/test_selection_utils.py +++ b/tedana/tests/test_selection_utils.py @@ -90,7 +90,7 @@ def test_selectcomps2use_succeeds(): decide_comps_lengths = [4, 17, 21, 21, 1, 3, 0] for idx, decide_comps in enumerate(decide_comps_options): - comps2use = selection_utils.selectcomps2use(selector, decide_comps) + comps2use = selection_utils.selectcomps2use(selector.component_table_, decide_comps) assert len(comps2use) == decide_comps_lengths[idx], ( f"selectcomps2use test should select {decide_comps_lengths[idx]} with " f"decide_comps={decide_comps}, but it selected {len(comps2use)}" @@ -111,11 +111,11 @@ def test_selectcomps2use_fails(): ] for decide_comps in decide_comps_options: with pytest.raises(ValueError): - selection_utils.selectcomps2use(selector, decide_comps) + selection_utils.selectcomps2use(selector.component_table_, decide_comps) selector.component_table_ = selector.component_table_.drop(columns="classification") with pytest.raises(ValueError): - selection_utils.selectcomps2use(selector, "all") + selection_utils.selectcomps2use(selector.component_table_, "all") def test_comptable_classification_changer_succeeds(): @@ -192,7 +192,7 @@ def test_change_comptable_classifications_succeeds(): # Given the rho values in the sample table, decision_boolean should have # 2 True and 2 False values - comps2use = selection_utils.selectcomps2use(selector, "provisional accept") + comps2use = selection_utils.selectcomps2use(selector.component_table_, "provisional accept") rho = selector.component_table_.loc[comps2use, "rho"] decision_boolean = rho < 13.5 @@ -255,7 +255,7 @@ def test_log_decision_tree_step_smoke(): selector = sample_selector() # Standard run for logging classification changes - comps2use = selection_utils.selectcomps2use(selector, "reject") + comps2use = selection_utils.selectcomps2use(selector.component_table_, "reject") selection_utils.log_decision_tree_step( "Step 0: test_function_name", comps2use, @@ -289,7 +289,7 @@ def test_log_decision_tree_step_smoke(): ) # Logging no components found with a specified classification - comps2use = selection_utils.selectcomps2use(selector, "NotALabel") + comps2use = selection_utils.selectcomps2use(selector.component_table_, "NotALabel") selection_utils.log_decision_tree_step( "Step 0: test_function_name", comps2use, From adf6f3ae39b8fa37e2bd6709aec6ec3fce576be2 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Tue, 20 Feb 2024 15:24:45 -0500 Subject: [PATCH 30/81] Fix. --- tedana/selection/component_selector.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index a0bae5bfa..80f02803d 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -311,6 +311,12 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) everything that changed in each node - current_node_idx_: The total number of nodes run in ``ComponentSelector`` """ + self.__dict__.update(cross_component_metrics) + self.cross_component_metrics_ = cross_component_metrics + + # Construct an un-executed selector + self.component_table_ = component_table.copy() + # this will crash the program with an error message if not all # necessary_metrics are in the comptable confirm_metrics_exist( @@ -319,12 +325,6 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) function_name=self.tree_name, ) - self.__dict__.update(cross_component_metrics) - self.cross_component_metrics_ = cross_component_metrics - - # Construct an un-executed selector - self.component_table_ = component_table.copy() - # To run a decision tree, each component needs to have an initial classification # If the classification column doesn't exist, create it and label all components # as unclassified From 11c03fb9c6741ad3c1215061c82afabc417da23e Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Wed, 21 Feb 2024 08:49:23 -0500 Subject: [PATCH 31/81] Include generated metrics in necessary metrics. --- tedana/selection/component_selector.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 80f02803d..d9f88ad03 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -453,8 +453,9 @@ def are_only_necessary_metrics_used(self): If either of these happen, a warning is added to the logger. """ - not_declared = self.tree["used_metrics"] - set(self.necessary_metrics) - not_used = set(self.necessary_metrics) - self.tree["used_metrics"] + necessary_metrics = self.necessary_metrics.union(self.tree["generated_metrics"]) + not_declared = self.tree["used_metrics"] - set(necessary_metrics) + not_used = set(necessary_metrics) - self.tree["used_metrics"] if len(not_declared) > 0: LGR.warning( f"Decision tree {self.tree_name} used the following metrics that were " From 9e22791b5d0444ce0625ef787bf3d48abe54fba7 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Wed, 21 Feb 2024 09:39:19 -0500 Subject: [PATCH 32/81] Update component_selector.py --- tedana/selection/component_selector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index d9f88ad03..1180856ae 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -453,7 +453,7 @@ def are_only_necessary_metrics_used(self): If either of these happen, a warning is added to the logger. """ - necessary_metrics = self.necessary_metrics.union(self.tree["generated_metrics"]) + necessary_metrics = set(self.necessary_metrics).union(set(self.tree["generated_metrics"])) not_declared = self.tree["used_metrics"] - set(necessary_metrics) not_used = set(necessary_metrics) - self.tree["used_metrics"] if len(not_declared) > 0: From 1d182fc7ecfc1f0a5b18c75a961207a8d8ce8561 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Mon, 26 Feb 2024 10:42:45 -0500 Subject: [PATCH 33/81] responding to feedback from tsalo --- .pre-commit-config.yaml | 2 +- tedana/metrics/collect.py | 18 +-- ...external_regressor_fits.py => external.py} | 114 +++++++++--------- tedana/resources/config/metrics.json | 2 +- .../Fmodel.json | 0 .../Mot12_CSF.json | 0 .../corr_detrend.json | 0 .../corr_no_detrend.json | 0 tedana/selection/component_selector.py | 8 +- tedana/selection/selection_nodes.py | 12 +- tedana/selection/selection_utils.py | 6 +- tedana/selection/tedica.py | 6 +- tedana/tests/test_integration.py | 6 +- tedana/workflows/tedana.py | 22 ++-- 14 files changed, 99 insertions(+), 97 deletions(-) rename tedana/metrics/{external_regressor_fits.py => external.py} (89%) rename tedana/resources/{extern_regress_dicts => external_regressor_configs}/Fmodel.json (100%) rename tedana/resources/{extern_regress_dicts => external_regressor_configs}/Mot12_CSF.json (100%) rename tedana/resources/{extern_regress_dicts => external_regressor_configs}/corr_detrend.json (100%) rename tedana/resources/{extern_regress_dicts => external_regressor_configs}/corr_no_detrend.json (100%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 465e701a1..4d5982044 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: - id: check-yaml - id: check-added-large-files - repo: https://github.com/psf/black-pre-commit-mirror - rev: 23.10.0 + rev: 24.2.0 hooks: - id: black files: ^tedana/ diff --git a/tedana/metrics/collect.py b/tedana/metrics/collect.py index ad4ee0eb7..53a2d7d6d 100644 --- a/tedana/metrics/collect.py +++ b/tedana/metrics/collect.py @@ -7,7 +7,7 @@ import pandas as pd from tedana import io, utils -from tedana.metrics import dependence, external_regressor_fits +from tedana.metrics import dependence, external from tedana.metrics._utils import dependency_resolver, determine_signs, flip_components from tedana.stats import getfbounds @@ -25,7 +25,7 @@ def generate_metrics( io_generator, label, external_regressors=None, - external_regressor_dict=None, + external_regressor_config=None, metrics=None, ): """Fit TE-dependence and -independence models to components. @@ -53,7 +53,7 @@ def generate_metrics( external_regressors : None or :obj:`pandas.DataFrame`, optional External regressors (e.g., motion parameters, physiological noise) to correlate with ICA components. If None, no external regressor metrics will be calculated. - external_regressor_dict : :obj:`dict` + external_regressor_config : :obj:`dict` A dictionary for defining how to fit external regressors to component time series metrics : list List of metrics to return @@ -63,7 +63,7 @@ def generate_metrics( comptable : (C x X) :obj:`pandas.DataFrame` Component metric table. One row for each component, with a column for each metric. The index is the component number. - external_regressor_dict : :obj:`dict` + external_regressor_config : :obj:`dict` Information describing the external regressors and method used for fitting and statistical tests (or None if none were inputed) """ @@ -75,10 +75,10 @@ def generate_metrics( metrics = ["map weight"] if external_regressors is not None: - if external_regressor_dict is None: + if external_regressor_config is None: raise ValueError( "If external_regressors is defined, then " - "external_regressor_dict also needs to be defined." + "external_regressor_config also needs to be defined." ) metrics.append("external fit") @@ -344,8 +344,8 @@ def generate_metrics( if "external fit" in required_metrics: # external_regressor_names = external_regressors.columns.tolist() LGR.info("Calculating external regressor fits") - comptable = external_regressor_fits.fit_regressors( - comptable, external_regressors, external_regressor_dict, mixing + comptable = external.fit_regressors( + comptable, external_regressors, external_regressor_config, mixing ) # for col in external_regressor_names: # external_regressor_arr = external_regressors[col].values @@ -414,7 +414,7 @@ def generate_metrics( other_columns = [col for col in comptable.columns if col not in preferred_order] comptable = comptable[first_columns + other_columns] - return comptable, external_regressor_dict + return comptable, external_regressor_config def get_metadata(comptable): diff --git a/tedana/metrics/external_regressor_fits.py b/tedana/metrics/external.py similarity index 89% rename from tedana/metrics/external_regressor_fits.py rename to tedana/metrics/external.py index 4b77aa735..051640c79 100644 --- a/tedana/metrics/external_regressor_fits.py +++ b/tedana/metrics/external.py @@ -22,7 +22,7 @@ class RegressError(Exception): pass -def load_validate_external_regressors(external_regressors, external_regressor_dict, n_time): +def load_validate_external_regressors(external_regressors, external_regressor_config, n_time): """ Load and validate external regressors and descriptors in dictionary. @@ -30,7 +30,7 @@ def load_validate_external_regressors(external_regressors, external_regressor_di ---------- external_regressors: :obj:`str` Path and name of tsv file that includes external regressor time series - external_regressor_dict: :obj:`str` + external_regressor_config: :obj:`str` An included dictionary name or path to a JSON file that defines one. This contains expected column labels for the regressors, how the regressors can be grouped together, and what statistical @@ -43,23 +43,23 @@ def load_validate_external_regressors(external_regressors, external_regressor_di external_regressors: :obj:`pandas.DataFrame` Each column is a labelled regressor and the number of rows should match the number of timepoints in the fMRI time series - external_regressor_dict : :obj:`dict` + external_regressor_config : :obj:`dict` A validated dictionary with info for fitting external regressors to component time series """ - LGR.info(f"Loading external regressor dictionary: {external_regressor_dict}") - external_regressor_dict = load_external_regressor_dict(external_regressor_dict) + LGR.info(f"Loading external regressor dictionary: {external_regressor_config}") + external_regressor_config = load_external_regressor_config(external_regressor_config) try: external_regressors = pd.read_table(external_regressors) except FileNotFoundError: raise ValueError(f"Cannot load tsv file with external regressors: {external_regressors}") - validate_extern_regress(external_regressors, external_regressor_dict, n_time) + validate_extern_regress(external_regressors, external_regressor_config, n_time) - return external_regressors, external_regressor_dict + return external_regressors, external_regressor_config -def load_external_regressor_dict(external_regressor_dict): +def load_external_regressor_config(external_regressor_config): """ Load and validate external regressor dictionary. @@ -72,38 +72,38 @@ def load_external_regressor_dict(external_regressor_dict): Returns ------- - external_regressor_dict : :obj:`dict` + external_regressor_config : :obj:`dict` A validated dictionary for fitting external regressors to component time series """ - if external_regressor_dict in DEFAULT_REGRESSOR_DICTS: + if external_regressor_config in DEFAULT_REGRESSOR_DICTS: fname = op.join( - get_resource_path(), "extern_regress_dicts", external_regressor_dict + ".json" + get_resource_path(), "external_regressor_configs", external_regressor_config + ".json" ) else: - fname = external_regressor_dict + fname = external_regressor_config try: - external_regressor_dict = load_json(fname) + external_regressor_config = load_json(fname) except FileNotFoundError: raise ValueError( - f"Cannot find external regressor dictionary {external_regressor_dict}. " + f"Cannot find external regressor dictionary {external_regressor_config}. " "Please check your path or use a " f"default dictionary: ({DEFAULT_REGRESSOR_DICTS})." ) except IsADirectoryError: raise ValueError( - f"{external_regressor_dict} is a directory. Please supply a JSON file or " + f"{external_regressor_config} is a directory. Please supply a JSON file or " f"default dictionary: ({DEFAULT_REGRESSOR_DICTS})." ) - return external_regressor_dict + return external_regressor_config -def validate_extern_regress(external_regressors, external_regressor_dict, n_time): +def validate_extern_regress(external_regressors, external_regressor_config, n_time): """ Confirm that provided external regressor dictionary is valid and matches data. - Checks if expected keys are in external_regressor_dict. + Checks if expected keys are in external_regressor_config. Checks if any regressor labels in the dictionary are specified in the user-defined external_regressors Checks if the number of time points in the external regressors matches @@ -114,7 +114,7 @@ def validate_extern_regress(external_regressors, external_regressor_dict, n_time external_regressors : :obj:`pandas.DataFrame` Each column is a labelled regressor and the number of rows should match the number of timepoints in the fMRI time series - external_regressor_dict : :obj:`dict` + external_regressor_config : :obj:`dict` Information describing the external regressors and method to use for fitting and statistical tests n_time : :obj:`int` @@ -137,33 +137,33 @@ def validate_extern_regress(external_regressors, external_regressor_dict, n_time # default_decide_comps = {"all", "accepted", "rejected", "unclassified"} # Confirm that the required fields exist - missing_keys = dict_expected_keys - set(external_regressor_dict.keys()) + missing_keys = dict_expected_keys - set(external_regressor_config.keys()) if missing_keys: # If there are missing keys, this function may crash before the end. # End function here with a clear error message rather than adding - # `if assert external_regressor_dict.get()` statements before every section + # `if assert external_regressor_config.get()` statements before every section err_msg += f"External regressor dictionary missing required fields: {missing_keys}\n" - if external_regressor_dict["calc_stats"].lower() not in calc_stats_key_options: + if external_regressor_config["calc_stats"].lower() not in calc_stats_key_options: err_msg += ( - "calc_stats in external_regressor_dict is " - f"{external_regressor_dict['calc_stats']}. It must be one of the following " + "calc_stats in external_regressor_config is " + f"{external_regressor_config['calc_stats']}. It must be one of the following " f"{calc_stats_key_options}\n" ) - if (external_regressor_dict["calc_stats"].lower() != "f") and ( - "f_stats_partial_models" in set(external_regressor_dict.keys()) + if (external_regressor_config["calc_stats"].lower() != "f") and ( + "f_stats_partial_models" in set(external_regressor_config.keys()) ): err_msg += ( "External regressor dictionary cannot include" "f_stats_partial_models if calc_stats is not F\n" ) - if "f_stats_partial_models" in set(external_regressor_dict.keys()): + if "f_stats_partial_models" in set(external_regressor_config.keys()): dict_expected_keys.add("f_stats_partial_models") - dict_expected_keys.update(set(external_regressor_dict["f_stats_partial_models"])) - missing_partial_models = set(external_regressor_dict["f_stats_partial_models"]) - set( - external_regressor_dict.keys() + dict_expected_keys.update(set(external_regressor_config["f_stats_partial_models"])) + missing_partial_models = set(external_regressor_config["f_stats_partial_models"]) - set( + external_regressor_config.keys() ) if missing_partial_models: raise RegressError( @@ -173,22 +173,22 @@ def validate_extern_regress(external_regressors, external_regressor_dict, n_time ) # Warn if unused fields exist - unused_keys = set(external_regressor_dict.keys()) - set(dict_expected_keys) + unused_keys = set(external_regressor_config.keys()) - set(dict_expected_keys) if unused_keys: LGR.warning( "External regressor dictionary includes fields that " f"are not used or logged {unused_keys}" ) - # Validating the information in external_regressor_dict works + # Validating the information in external_regressor_config works # with the data in external_regressors # Currently column labels only need to be predefined for calc_stats==F - if "f_stats_partial_models" in set(external_regressor_dict.keys()): + if "f_stats_partial_models" in set(external_regressor_config.keys()): regressor_names = set(external_regressors.columns) expected_regressor_names = set() - for partial_models in external_regressor_dict["f_stats_partial_models"]: - tmp_names = set(external_regressor_dict[partial_models]) + for partial_models in external_regressor_config["f_stats_partial_models"]: + tmp_names = set(external_regressor_config[partial_models]) if expected_regressor_names - tmp_names: LGR.warning( "External regressors used in more than one partial model: " @@ -222,12 +222,12 @@ def validate_extern_regress(external_regressors, external_regressor_dict, n_time raise RegressError(err_msg) -def fit_regressors(comptable, external_regressors, external_regressor_dict, mixing): +def fit_regressors(comptable, external_regressors, external_regressor_config, mixing): """ Fit regressors to the mixing matrix. Uses correlation or F statistics in a linear model depending on the calc_stats - value in external_regressor_dict + value in external_regressor_config Parameters ---------- @@ -237,7 +237,7 @@ def fit_regressors(comptable, external_regressors, external_regressor_dict, mixi external_regressors : :obj:`pandas.DataFrame` Each column is a labelled regressor and the number of rows should match the number of timepoints in the fMRI time series - external_regressor_dict : :obj:`dict` + external_regressor_config : :obj:`dict` Information describing the external regressors and method to use for fitting and statistical tests mixing : (T x C) array_like @@ -254,29 +254,29 @@ def fit_regressors(comptable, external_regressors, external_regressor_dict, mixi # If the order of polynomial detrending is specified, then pass to make_detrend_regressors # otherwise the function sets a detrending polynomial order - if external_regressor_dict["detrend"] is True: + if external_regressor_config["detrend"] is True: detrend_regressors = make_detrend_regressors(n_time, polort=None) elif ( - isinstance(external_regressor_dict["detrend"], int) - and external_regressor_dict["detrend"] > 0 + isinstance(external_regressor_config["detrend"], int) + and external_regressor_config["detrend"] > 0 ): detrend_regressors = make_detrend_regressors( - n_time, polort=external_regressor_dict["detrend"] + n_time, polort=external_regressor_config["detrend"] ) else: LGR.warning("External regressor fitting applied without detrending fMRI time series") - if external_regressor_dict["calc_stats"].lower() == "corr": + if external_regressor_config["calc_stats"].lower() == "corr": if "detrend_regressors" not in locals(): # set detrend regressors to None if it doesn't exist detrend_regressors = None comptable = correlate_regressors( comptable, external_regressors, mixing, detrend_regressors=detrend_regressors ) - elif external_regressor_dict["calc_stats"].lower() == "f": + elif external_regressor_config["calc_stats"].lower() == "f": # external_regressors = pd.concat([external_regressors, detrend_regressors]) comptable = fit_mixing_to_regressors( - comptable, external_regressors, external_regressor_dict, mixing, detrend_regressors + comptable, external_regressors, external_regressor_config, mixing, detrend_regressors ) return comptable @@ -338,7 +338,7 @@ def make_detrend_regressors(n_time, polort=None): def fit_mixing_to_regressors( comptable, external_regressors, - external_regressor_dict, + external_regressor_config, mixing, detrend_regressors, ): @@ -359,7 +359,7 @@ def fit_mixing_to_regressors( external_regressors : :obj:`pandas.DataFrame` Each column is a labelled regressor and the number of rows should match the number of timepoints in the fMRI time series - external_regressor_dict : :obj:`dict` + external_regressor_config : :obj:`dict` Information describing the external regressors and method to use for fitting and statistical tests mixing : (T x C) array_like @@ -388,7 +388,7 @@ def fit_mixing_to_regressors( # EXCEPT the category of interest. For example, there will also be a field for "no Motion" # which contains all regressors in the full model except those that model motion regressor_models = build_fstat_regressor_models( - external_regressors, external_regressor_dict, detrend_regressors + external_regressors, external_regressor_config, detrend_regressors ) # This is the test for the fit of the full model vs the polort detrending baseline @@ -410,8 +410,8 @@ def fit_mixing_to_regressors( r2_vals = pd.DataFrame(data=r2_vals_tmp, columns=["R2stat Full Model"]) # Test the fits between the full model and the full model excluding one category of regressor - if "f_stats_partial_models" in external_regressor_dict.keys(): - for pmodel in external_regressor_dict["f_stats_partial_models"]: + if "f_stats_partial_models" in external_regressor_config.keys(): + for pmodel in external_regressor_config["f_stats_partial_models"]: _, f_vals_tmp, p_vals_tmp, r2_vals_tmp = fit_model_with_stats( mixing, regressor_models, f"no {pmodel}" ) @@ -425,7 +425,9 @@ def fit_mixing_to_regressors( return comptable -def build_fstat_regressor_models(external_regressors, external_regressor_dict, detrend_regressors): +def build_fstat_regressor_models( + external_regressors, external_regressor_config, detrend_regressors +): """ Combine detrending all or subsets of external regressors to make models to fit and test. @@ -434,7 +436,7 @@ def build_fstat_regressor_models(external_regressors, external_regressor_dict, d external_regressors : :obj:`pandas.DataFrame` Each column is a labelled regressor and the number of rows should match the number of timepoints in the fMRI time series - external_regressor_dict : :obj:`dict` + external_regressor_config : :obj:`dict` Information describing the external regressors and method to use for fitting and statistical tests detrend_regressors: (n_time x polort) :obj:`pandas.DataFrame` @@ -447,7 +449,7 @@ def build_fstat_regressor_models(external_regressors, external_regressor_dict, d regressor model. The models that are always included are 'base' which is just the detrending regressors, and 'full' which is all user-provided external regressors and the detrending regressors. If there are partial models that are named in - external_regressor_dict["f_stats_partial_models"] then each of those will have a + external_regressor_config["f_stats_partial_models"] then each of those will have a dictionary element named "no" then model name and the regressors included will be everything except the specified regressors. That is "no motion" will include all regressors except the motion regressors. This is for the F test which compares @@ -455,8 +457,8 @@ def build_fstat_regressor_models(external_regressors, external_regressor_dict, d regressors-of-interest for the partial model are removed. """ # The category titles to group each regressor - if "f_stats_partial_models" in external_regressor_dict: - partial_models = external_regressor_dict["f_stats_partial_models"] + if "f_stats_partial_models" in external_regressor_config: + partial_models = external_regressor_config["f_stats_partial_models"] else: partial_models = [] @@ -475,7 +477,7 @@ def build_fstat_regressor_models(external_regressors, external_regressor_dict, d # For F statistics, the other models to test are those that include everything EXCEPT # the category of interest # That is "no motion" should contain the full model excluding motion regressors - keep_labels = set(regressor_labels) - set(external_regressor_dict[pmodel]) + keep_labels = set(regressor_labels) - set(external_regressor_config[pmodel]) no_pmodel = f"no {pmodel}" regressor_models[no_pmodel] = detrend_regressors_arr for keep_label in keep_labels: diff --git a/tedana/resources/config/metrics.json b/tedana/resources/config/metrics.json index 7cd2ce22c..6b9d50a1b 100644 --- a/tedana/resources/config/metrics.json +++ b/tedana/resources/config/metrics.json @@ -120,7 +120,7 @@ "external fit": [ "mixing", "external_regressors", - "external_regressor_dict" + "external_regressor_config" ] } } diff --git a/tedana/resources/extern_regress_dicts/Fmodel.json b/tedana/resources/external_regressor_configs/Fmodel.json similarity index 100% rename from tedana/resources/extern_regress_dicts/Fmodel.json rename to tedana/resources/external_regressor_configs/Fmodel.json diff --git a/tedana/resources/extern_regress_dicts/Mot12_CSF.json b/tedana/resources/external_regressor_configs/Mot12_CSF.json similarity index 100% rename from tedana/resources/extern_regress_dicts/Mot12_CSF.json rename to tedana/resources/external_regressor_configs/Mot12_CSF.json diff --git a/tedana/resources/extern_regress_dicts/corr_detrend.json b/tedana/resources/external_regressor_configs/corr_detrend.json similarity index 100% rename from tedana/resources/extern_regress_dicts/corr_detrend.json rename to tedana/resources/external_regressor_configs/corr_detrend.json diff --git a/tedana/resources/extern_regress_dicts/corr_no_detrend.json b/tedana/resources/external_regressor_configs/corr_no_detrend.json similarity index 100% rename from tedana/resources/extern_regress_dicts/corr_no_detrend.json rename to tedana/resources/external_regressor_configs/corr_no_detrend.json diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 8175bbf51..d5fc1a826 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -223,7 +223,7 @@ def __init__( component_table, cross_component_metrics={}, status_table=None, - external_regressor_dict=None, + external_regressor_config=None, ): """Initialize the class using the info specified in the json file ``tree``. @@ -241,7 +241,7 @@ def __init__( A table tracking the status of each component at each step. Pass a status table if running additional steps on a decision tree that was already executed. Default=None. - external_regressor_dict : :obj:`dict` + external_regressor_config : :obj:`dict` Information describing the external regressors and method to use for fitting and statistical tests @@ -296,8 +296,8 @@ def __init__( LGR.info("Performing component selection with " + tree_config["tree_id"]) LGR.info(tree_config.get("info", "")) RepLGR.info(tree_config.get("report", "")) - if external_regressor_dict is not None: - RepLGR.info(external_regressor_dict["report"]) + if external_regressor_config is not None: + RepLGR.info(external_regressor_config["report"]) self.tree["nodes"] = tree_config["nodes"] self.necessary_metrics = set(tree_config["necessary_metrics"]) diff --git a/tedana/selection/selection_nodes.py b/tedana/selection/selection_nodes.py index 33a79df21..fe93a242b 100644 --- a/tedana/selection/selection_nodes.py +++ b/tedana/selection/selection_nodes.py @@ -537,9 +537,9 @@ def dec_variance_lessthan_thresholds( if custom_node_label: outputs["node_label"] = custom_node_label else: - outputs[ - "node_label" - ] = f"{var_metric}<{single_comp_threshold}. All variance<{all_comp_threshold}" + outputs["node_label"] = ( + f"{var_metric}<{single_comp_threshold}. All variance<{all_comp_threshold}" + ) LGR.info(f"{function_name_idx}: {if_true} if {outputs['node_label']}, else {if_false}") if log_extra_info: @@ -1020,9 +1020,9 @@ def dec_classification_doesnt_exist( if custom_node_label: outputs["node_label"] = custom_node_label elif at_least_num_exist == 1: - outputs[ - "node_label" - ] = f"Change {decide_comps} to {new_classification} if {class_comp_exists} doesn't exist" + outputs["node_label"] = ( + f"Change {decide_comps} to {new_classification} if {class_comp_exists} doesn't exist" + ) else: outputs["node_label"] = ( f"Change {decide_comps} to {new_classification} if less than " diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index 20478f58e..1d6e45e41 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -160,9 +160,9 @@ def change_comptable_classifications( dont_warn_reclassify=dont_warn_reclassify, ) - selector.component_status_table[ - f"Node {selector.current_node_idx}" - ] = selector.component_table["classification"] + selector.component_status_table[f"Node {selector.current_node_idx}"] = ( + selector.component_table["classification"] + ) n_true = decision_boolean.sum() n_false = np.logical_not(decision_boolean).sum() diff --git a/tedana/selection/tedica.py b/tedana/selection/tedica.py index d5e9d181f..9a487fee7 100644 --- a/tedana/selection/tedica.py +++ b/tedana/selection/tedica.py @@ -10,7 +10,7 @@ def automatic_selection( - component_table, n_echos, n_vols, tree="kundu", external_regressor_dict=None + component_table, n_echos, n_vols, tree="kundu", external_regressor_config=None ): """Classify components based on component table and decision tree type. @@ -22,7 +22,7 @@ def automatic_selection( The number of echoes in this dataset tree : :obj:`str` The type of tree to use for the ComponentSelector object. Default="kundu" - external_regressor_dict : :obj:`dict` + external_regressor_config : :obj:`dict` Information describing the external regressors and method to use for fitting and statistical tests @@ -74,7 +74,7 @@ def automatic_selection( tree, component_table, cross_component_metrics=xcomp, - external_regressor_dict=external_regressor_dict, + external_regressor_config=external_regressor_config, ) selector.select() selector.metadata = collect.get_metadata(selector.component_table) diff --git a/tedana/tests/test_integration.py b/tedana/tests/test_integration.py index 2a8245ad9..1a28dd7e8 100644 --- a/tedana/tests/test_integration.py +++ b/tedana/tests/test_integration.py @@ -414,7 +414,7 @@ def test_integration_three_echo_external_regressors_corr(skip_integration): external_regressors=resource_filename( "tedana", "tests/data/external_regress_corr_3echo.tsv" ), - external_regressor_dict="corr_detrend", + external_regressor_config="corr_detrend", low_mem=True, tedpca="aic", ) @@ -466,8 +466,8 @@ def test_integration_three_echo_external_regressors_fstat(skip_integration): external_regressors=resource_filename( "tedana", "tests/data/external_regress_Ftest_3echo.tsv" ), - # external_regressor_dict="Mot12_CSF", - external_regressor_dict="Fmodel", + # external_regressor_config="Mot12_CSF", + external_regressor_config="Fmodel", low_mem=True, tedpca="aic", ) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index bf2aa6cc2..f0dde7915 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -177,7 +177,7 @@ def _get_parser(): ) optional.add_argument( "--external_labels", - dest="external_regressor_dict", + dest="external_regressor_config", help=( "Column labels for external regressors & statistical tests to compare be used in the " "decision tree. Either the path and name of a json file or a packaged tree (Mot12_CSF)" @@ -344,7 +344,7 @@ def tedana_workflow( combmode="t2s", tree="kundu", external_regressors=None, - external_regressor_dict=None, + external_regressor_config=None, tedpca="aic", fixed_seed=42, maxit=500, @@ -533,17 +533,17 @@ def tedana_workflow( # Decided to do the validation here so that, if there are issues, an error # will be raised before PCA/ICA if external_regressors: - if external_regressor_dict: + if external_regressor_config: ( external_regressors, - external_regressor_dict, + external_regressor_config, ) = metrics.external_regressor_fits.load_validate_external_regressors( - external_regressors, external_regressor_dict, catd.shape[2] + external_regressors, external_regressor_config, catd.shape[2] ) else: raise ValueError( "If external_regressors is an input, then " - "external_regressor_dict also needs to be used." + "external_regressor_config also needs to be used." ) io_generator = io.OutputGenerator( @@ -728,7 +728,7 @@ def tedana_workflow( "d_table_score", ] - comptable, external_regressor_dict = metrics.collect.generate_metrics( + comptable, external_regressor_config = metrics.collect.generate_metrics( data_cat=catd, data_optcom=data_oc, mixing=mmix, @@ -737,7 +737,7 @@ def tedana_workflow( io_generator=io_generator, label="ICA", external_regressors=external_regressors, - external_regressor_dict=external_regressor_dict, + external_regressor_config=external_regressor_config, metrics=required_metrics, ) ica_selector = selection.automatic_selection( @@ -745,7 +745,7 @@ def tedana_workflow( n_echos, n_vols, tree=tree, - external_regressor_dict=external_regressor_dict, + external_regressor_config=external_regressor_config, ) n_likely_bold_comps = ica_selector.n_likely_bold_comps if (n_restarts < maxrestart) and (n_likely_bold_comps == 0): @@ -780,7 +780,7 @@ def tedana_workflow( "normalized variance explained", "d_table_score", ] - comptable, external_regressor_dict = metrics.collect.generate_metrics( + comptable, external_regressor_config = metrics.collect.generate_metrics( data_cat=catd, data_optcom=data_oc, mixing=mmix, @@ -789,7 +789,7 @@ def tedana_workflow( io_generator=io_generator, label="ICA", external_regressors=external_regressors, - external_regressor_dict=external_regressor_dict, + external_regressor_config=external_regressor_config, metrics=required_metrics, ) ica_selector = selection.automatic_selection( From 0baccd9c99864a1e5da391c999f654cd01a0508c Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Mon, 26 Feb 2024 15:48:40 -0500 Subject: [PATCH 34/81] Update component_selector.py --- tedana/selection/component_selector.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index faaa8a14a..ad45d505b 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -461,7 +461,9 @@ def are_only_necessary_metrics_used(self): If either of these happen, a warning is added to the logger. """ - necessary_metrics = set(self.necessary_metrics).union(set(self.tree["generated_metrics"])) + necessary_metrics = set(self.necessary_metrics).union( + set(self.tree.get("generated_metrics", [])) + ) not_declared = self.tree["used_metrics"] - set(necessary_metrics) not_used = set(necessary_metrics) - self.tree["used_metrics"] if len(not_declared) > 0: From 4b751eb385208c2769138f3e344f6faf85920224 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Mon, 26 Feb 2024 16:02:57 -0500 Subject: [PATCH 35/81] Update test_component_selector.py --- tedana/tests/test_component_selector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tedana/tests/test_component_selector.py b/tedana/tests/test_component_selector.py index 3bfc307a3..fcd94a746 100644 --- a/tedana/tests/test_component_selector.py +++ b/tedana/tests/test_component_selector.py @@ -296,7 +296,7 @@ def test_selector_properties_smoke(): """Tests to confirm properties match expected results.""" selector = component_selector.ComponentSelector(tree="minimal") - selector.select(component_table=sample_comptable()) + selector.select(component_table=sample_comptable(), cross_component_metrics={"n_echos": 3}) assert selector.n_comps_ == 21 From b2b6067380971e3e1df2c85fe1e4a9f4fa1874c4 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Mon, 26 Feb 2024 17:20:39 -0500 Subject: [PATCH 36/81] fixed some testing failures --- tedana/selection/component_selector.py | 1 - tedana/selection/selection_nodes.py | 12 ++++++++---- tedana/selection/selection_utils.py | 20 ++++++++++---------- tedana/tests/test_selection_utils.py | 12 +++++++++++- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index ad45d505b..a57d2e9a0 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -319,7 +319,6 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) everything that changed in each node - current_node_idx_: The total number of nodes run in ``ComponentSelector`` """ - self.__dict__.update(cross_component_metrics) self.cross_component_metrics_ = cross_component_metrics # Construct an un-executed selector diff --git a/tedana/selection/selection_nodes.py b/tedana/selection/selection_nodes.py index e02598f1c..b4696ce2d 100644 --- a/tedana/selection/selection_nodes.py +++ b/tedana/selection/selection_nodes.py @@ -717,7 +717,7 @@ def calc_kappa_elbow( outputs = { "decision_node_idx": selector.current_node_idx_, "node_label": None, - "n_echos": selector.n_echos, + "n_echos": selector.cross_component_metrics_["n_echos"], "used_metrics": {"kappa"}, "calc_cross_comp_metrics": [ "kappa_elbow_kundu", @@ -775,7 +775,11 @@ def calc_kappa_elbow( outputs["kappa_allcomps_elbow"], outputs["kappa_nonsig_elbow"], outputs["varex_upper_p"], - ) = kappa_elbow_kundu(selector.component_table_, selector.n_echos, comps2use=comps2use) + ) = kappa_elbow_kundu( + selector.component_table_, + selector.cross_component_metrics_["n_echos"], + comps2use=comps2use, + ) selector.cross_component_metrics_["kappa_elbow_kundu"] = outputs["kappa_elbow_kundu"] selector.cross_component_metrics_["kappa_allcomps_elbow"] = outputs["kappa_allcomps_elbow"] selector.cross_component_metrics_["kappa_nonsig_elbow"] = outputs["kappa_nonsig_elbow"] @@ -845,7 +849,7 @@ def calc_rho_elbow( outputs = { "decision_node_idx": selector.current_node_idx_, "node_label": None, - "n_echos": selector.n_echos, + "n_echos": selector.cross_component_metrics_["n_echos"], "calc_cross_comp_metrics": [ elbow_name, "rho_allcomps_elbow", @@ -900,7 +904,7 @@ def calc_rho_elbow( outputs["elbow_f05"], ) = rho_elbow_kundu_liberal( selector.component_table_, - selector.n_echos, + selector.cross_component_metrics_["n_echos"], rho_elbow_type=rho_elbow_type, comps2use=comps2use, subset_comps2use=subset_comps2use, diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index 888c32c5b..3c0b90471 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -14,13 +14,13 @@ ############################################################## -def selectcomps2use(comptable, decide_comps): +def selectcomps2use(component_table, decide_comps): """Get a list of component numbers that fit the classification types in ``decide_comps``. Parameters ---------- - comptable : :obj:`~pandas.DataFrame` - Only uses the component_table in this object + component_table : :obj:`~pandas.DataFrame` + The component_table with metrics and labels for each ICA component decide_comps : :obj:`str` or :obj:`list[str]` or :obj:`list[int]` This is string or a list of strings describing what classifications of components to operate on, using default or intermediate_classification @@ -34,31 +34,31 @@ def selectcomps2use(comptable, decide_comps): comps2use : :obj:`list[int]` A list of component indices with classifications included in decide_comps """ - if "classification" not in comptable: - raise ValueError("comptable needs a 'classification' column to run selectcomps2use") + if "classification" not in component_table: + raise ValueError("component_table needs a 'classification' column to run selectcomps2use") if isinstance(decide_comps, (str, int)): decide_comps = [decide_comps] if isinstance(decide_comps, list) and (decide_comps[0] == "all"): # All components with any string in the classification field are set to True - comps2use = list(range(comptable.shape[0])) + comps2use = list(range(component_table.shape[0])) elif isinstance(decide_comps, list) and all(isinstance(elem, str) for elem in decide_comps): comps2use = [] for didx in range(len(decide_comps)): - newcomps2use = comptable.index[ - comptable["classification"] == decide_comps[didx] + newcomps2use = component_table.index[ + component_table["classification"] == decide_comps[didx] ].tolist() comps2use = list(set(comps2use + newcomps2use)) elif isinstance(decide_comps, list) and all(isinstance(elem, int) for elem in decide_comps): # decide_comps is already a list of indices - if len(comptable) <= max(decide_comps): + if len(component_table) <= max(decide_comps): raise ValueError( "decide_comps for selectcomps2use is selecting for a component with index" f"{max(decide_comps)} (0 indexing) which is greater than the number " - f"of components: {len(comptable)}" + f"of components: {len(component_table)}" ) elif min(decide_comps) < 0: raise ValueError( diff --git a/tedana/tests/test_selection_utils.py b/tedana/tests/test_selection_utils.py index 0425a54c3..465fb8bcc 100644 --- a/tedana/tests/test_selection_utils.py +++ b/tedana/tests/test_selection_utils.py @@ -55,7 +55,17 @@ def sample_selector(options=None): "test_elbow": 21, } selector = ComponentSelector(tree=tree) - selector.select(component_table=component_table, cross_component_metrics=xcomp) + + # Add an un-executed component table,cross component metrics, and status table + selector.component_table_ = component_table.copy() + selector.cross_component_metrics_ = xcomp + selector.component_status_table_ = selector.component_table_[ + ["Component", "classification"] + ].copy() + selector.component_status_table_ = selector.component_status_table_.rename( + columns={"classification": "initialized classification"} + ) + selector.current_node_idx_ = 0 return selector From 6c34a5ba8ab5763db445028826a5cbaa3d7a7eb3 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Mon, 26 Feb 2024 20:32:58 -0500 Subject: [PATCH 37/81] fixed test_check_null_succeeds --- tedana/selection/component_selector.py | 2 +- tedana/tests/test_component_selector.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index a57d2e9a0..a0a8b55ad 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -439,7 +439,7 @@ def check_null(self, params, fcn): for key, val in params.items(): if val is None: try: - params[key] = getattr(self, key) + params[key] = self.cross_component_metrics_[key] except AttributeError: raise ValueError( f"Parameter {key} is required in node {fcn}, but not defined. " diff --git a/tedana/tests/test_component_selector.py b/tedana/tests/test_component_selector.py index fcd94a746..e5cf0cd38 100644 --- a/tedana/tests/test_component_selector.py +++ b/tedana/tests/test_component_selector.py @@ -38,6 +38,7 @@ def dicts_to_test(treechoice): "missing_req_param": A missing required param in a decision node function "missing_function": An undefined decision node function "missing_key": A dict missing one of the required keys (report) + "null_value": A parameter in one node improperly has a null value Returns ------- @@ -267,6 +268,13 @@ def test_check_null_succeeds(): selector = component_selector.ComponentSelector(tree="minimal") selector.tree = dicts_to_test("null_value") + # "left" is missing from the function definition in node + # but is found as an initialized cross component metric + # so this should execute successfully + selector.cross_component_metrics_ = { + "left": 3, + } + params = selector.tree["nodes"][0]["parameters"] functionname = selector.tree["nodes"][0]["functionname"] selector.check_null(params, functionname) From 83911f5b68d4ab942bd732806b1972dd2e531940 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Mon, 26 Feb 2024 21:52:39 -0500 Subject: [PATCH 38/81] fixed ica_reclassify bug and selector_properties test --- tedana/selection/component_selector.py | 9 +++++++-- tedana/tests/test_component_selector.py | 5 ++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index a0a8b55ad..0dcc5fc97 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -348,8 +348,13 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) self.start_idx_ = 0 else: # Since a status table exists, we need to skip nodes up to the - # point where the last tree finished - self.start_idx_ = len(self.tree["nodes"]) + # point where the last tree finished. Notes that were executed + # have an output field. Identify the last node with an output field + tmp_idx = len(self.tree["nodes"]) - 1 + while ("outputs" not in self.tree["nodes"][tmp_idx]) and (tmp_idx > 0): + tmp_idx -= 1 + # start at the first node that does not have an output field + self.start_idx_ = tmp_idx + 1 LGR.info(f"Start is {self.start_idx_}") self.component_status_table_ = status_table diff --git a/tedana/tests/test_component_selector.py b/tedana/tests/test_component_selector.py index e5cf0cd38..f191df39f 100644 --- a/tedana/tests/test_component_selector.py +++ b/tedana/tests/test_component_selector.py @@ -303,8 +303,11 @@ def test_are_all_components_accepted_or_rejected(): def test_selector_properties_smoke(): """Tests to confirm properties match expected results.""" + # Runs on un-executed component table to smoke test three class + # functions that are used to count various types of component + # classifications in the component table selector = component_selector.ComponentSelector(tree="minimal") - selector.select(component_table=sample_comptable(), cross_component_metrics={"n_echos": 3}) + selector.component_table_ = sample_comptable() assert selector.n_comps_ == 21 From 24445e0f1edc49e834ad5b5f0ab8443a7ec7a3d0 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Tue, 27 Feb 2024 15:34:29 -0500 Subject: [PATCH 39/81] ComponentSelector initialized before loading data --- tedana/workflows/tedana.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 8079055d9..7a01277f8 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -499,6 +499,9 @@ def tedana_workflow( if isinstance(data, str): data = [data] + LGR.info("Initializing and validating component selection tree") + selector = ComponentSelector(tree) + LGR.info(f"Loading input data: {[f for f in data]}") catd, ref_img = io.load_data(data, n_echos=n_echos) @@ -630,8 +633,8 @@ def tedana_workflow( # optimally combine data data_oc = combine.make_optcom(catd, tes, masksum_denoise, t2s=t2s_full, combmode=combmode) - # regress out global signal unless explicitly not desired if "gsr" in gscontrol: + # regress out global signal catd, data_oc = gsc.gscontrol_raw(catd, data_oc, n_echos, io_generator) fout = io_generator.save_file(data_oc, "combined img") @@ -669,8 +672,6 @@ def tedana_workflow( # Estimate betas and compute selection metrics for mixing matrix # generated from dimensionally reduced data using full data (i.e., data # with thermal noise) - LGR.info("Making second component selection guess from ICA results") - selector = ComponentSelector(tree) necessary_metrics = selector.necessary_metrics # The figures require some metrics that might not be used by the decision tree. extra_metrics = ["variance explained", "normalized variance explained", "kappa", "rho"] @@ -686,6 +687,7 @@ def tedana_workflow( "ICA", metrics=necessary_metrics, ) + LGR.info("Selecting components from ICA results") selector = selection.automatic_selection( comptable, selector, @@ -704,6 +706,9 @@ def tedana_workflow( # If we're going to restart, temporarily allow force overwrite if keep_restarting: io_generator.overwrite = True + # Create a re-initialized selector object if rerunning + selector = ComponentSelector(tree) + RepLGR.disabled = True # Disable the report to avoid duplicate text RepLGR.disabled = False # Re-enable the report after the while loop is escaped io_generator.overwrite = overwrite # Re-enable original overwrite behavior From 51d285e27eb00bf6c95b91d371e9bdbb89cc0efc Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Tue, 27 Feb 2024 16:13:58 -0500 Subject: [PATCH 40/81] fixed docstrings --- docs/building_decision_trees.rst | 2 +- tedana/io.py | 2 +- tedana/selection/component_selector.py | 12 ++++++------ tedana/selection/selection_nodes.py | 2 +- tedana/selection/selection_utils.py | 26 +++++++++++++------------- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/building_decision_trees.rst b/docs/building_decision_trees.rst index 2f9ff0fb5..12a76311f 100644 --- a/docs/building_decision_trees.rst +++ b/docs/building_decision_trees.rst @@ -378,7 +378,7 @@ dataframe column that is True or False for the components in ``decide_comps`` ba the function's criteria. That column is an input to :func:`~tedana.selection.selection_utils.change_comptable_classifications`, which will update the component_table classifications, update the classification history -in component_status_table, and update the component classification_tags. Components not +in ``selector.component_status_table_``, and update the component classification_tags. Components not in ``decide_comps`` retain their existing classifications and tags. :func:`~tedana.selection.selection_utils.change_comptable_classifications` also returns and should assign values to diff --git a/tedana/io.py b/tedana/io.py index 94966793b..b73425554 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -643,7 +643,7 @@ def writeresults(ts, mask, comptable, mmix, io_generator): ========================================= =========================================== Filename Content ========================================= =========================================== - desc-denoised_bold.nii.gz Denoised time series. + desc-denoised_bold.nii.gz Denoised time series. desc-optcomAccepted_bold.nii.gz High-Kappa time series. (only with verbose) desc-optcomRejected_bold.nii.gz Low-Kappa time series. (only with verbose) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 0dcc5fc97..a29183416 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -272,8 +272,8 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) ----- Adds to the ``ComponentSelector``: - - component_status_table_: empty dataframe or contents of inputted status_table - - cross_component_metrics_: empty dict or contents of inputed values + - ``component_status_table_``: empty dataframe or contents of inputted status_table + - ``cross_component_metrics_``: empty dict or contents of inputed values - used_metrics: empty set Any parameter that is used by a decision tree node function can be passed @@ -307,17 +307,17 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) When this is run, multiple elements in `ComponentSelector` will change including: - - component_table_: ``classification`` column with ``accepted`` or ``rejected`` labels + - ``component_table_``: ``classification`` column with ``accepted`` or ``rejected`` labels and ``classification_tags`` column with can hold multiple comma-separated labels explaining why a classification happened - - cross_component_metrics_: Any values that were calculated based on the metric + - ``cross_component_metrics_``: Any values that were calculated based on the metric values across components or by direct user input - - component_status_table: Contains the classification statuses at each node in + - ``component_status_table_``: Contains the classification statuses at each node in the decision tree - used_metrics: A list of metrics used in the selection process - nodes: The original tree definition with an added ``outputs`` key listing everything that changed in each node - - current_node_idx_: The total number of nodes run in ``ComponentSelector`` + - ``current_node_idx_``: The total number of nodes run in ``ComponentSelector`` """ self.cross_component_metrics_ = cross_component_metrics diff --git a/tedana/selection/selection_nodes.py b/tedana/selection/selection_nodes.py index b4696ce2d..5fed5ddde 100644 --- a/tedana/selection/selection_nodes.py +++ b/tedana/selection/selection_nodes.py @@ -1218,7 +1218,7 @@ def calc_varex_thresh( num_highest_var_comps : :obj:`str` :obj:`int` percentile can be calculated on the num_highest_var_comps components with the lowest variance. Either input an integer directly or input a string that is - a parameter stored in selector.cross_component_metrics_ ("num_acc_guess" in + a parameter stored in ``selector.cross_component_metrics_`` ("num_acc_guess" in original decision tree). Default=None %(log_extra_info)s %(custom_node_label)s diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index 3c0b90471..c156b9181 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -100,8 +100,8 @@ def change_comptable_classifications( Parameters ---------- selector : :obj:`tedana.selection.component_selector.ComponentSelector` - The attributes used are component_table, component_status_table, and - current_node_idx_ + The attributes used are ``component_table_``, ``component_status_table_``, and + ``current_node_idx_`` if_true, if_false : :obj:`str` If the condition in this step is true or false, give the component the label in this string. Options are 'accepted', 'rejected', @@ -123,12 +123,12 @@ def change_comptable_classifications( Returns ------- selector : :obj:`tedana.selection.component_selector.ComponentSelector` - component_table["classifications"] will reflect any new + ``component_table_["classifications"]`` will reflect any new classifications. - component_status_table will have a new column titled - "Node current_node_idx_" that is a copy of the updated classifications + ``component_status_table_`` will have a new column titled + "Node ``current_node_idx_``" that is a copy of the updated classifications column. - component_table["classification_tags"] will be updated to include any + ``component_table_["classification_tags"]`` will be updated to include any new tags. Each tag should appear only once in the string and tags will be separated by commas. n_true, n_false : :obj:`int` @@ -178,8 +178,8 @@ def comptable_classification_changer( Parameters ---------- selector : :obj:`tedana.selection.component_selector.ComponentSelector` - The attributes used are component_table, component_status_table, and - current_node_idx_ + The attributes used are ``component_table_``, ``component_status_table_``, and + ``current_node_idx_`` boolstate : :obj:`bool` Change classifications only for True or False components in decision_boolean based on this variable @@ -207,12 +207,12 @@ def comptable_classification_changer( ------- selector : :obj:`tedana.selection.component_selector.ComponentSelector` Operates on the True OR False components depending on boolstate - component_table["classifications"] will reflect any new + ``component_table_["classifications"]`` will reflect any new classifications. - component_status_table will have a new column titled - "Node current_node_idx_" that is a copy of the updated classifications + ``component_status_table_`` will have a new column titled + "Node ``current_node_idx_``" that is a copy of the updated classifications column. - component_table["classification_tags"] will be updated to include any + component_table_["classification_tags"] will be updated to include any new tags. Each tag should appear only once in the string and tags will be separated by commas. @@ -363,7 +363,7 @@ def log_decision_tree_step( ---------- function_name_idx : :obj:`str` The name of the function that should be logged. By convention, this - be "Step current_node_idx_: function_name" + be "Step ``current_node_idx_``: function_name" comps2use : :obj:`list[int]` or -1 A list of component indices that should be used by a function. Only used to report no components found if empty and report From 3fe2443d4030d608cc8ef25ccf8ddb93435614de Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Tue, 27 Feb 2024 16:20:50 -0500 Subject: [PATCH 41/81] updated building decision tree docs --- docs/building_decision_trees.rst | 8 +++----- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/building_decision_trees.rst b/docs/building_decision_trees.rst index 12a76311f..59c6663a2 100644 --- a/docs/building_decision_trees.rst +++ b/docs/building_decision_trees.rst @@ -210,11 +210,9 @@ that is used to check whether results are plausible & can help avoid mistakes. - necessary_metrics A list of the necessary metrics in the component table that will be used - by the tree. If a metric doesn't exist then this will raise an error instead - of executing a tree. (Depending on future code development, this could - potentially be used to run ``tedana`` by specifying a decision tree and - metrics are calculated based on the contents of this field.) If a necessary - metric isn't used, there will be a warning. + by the tree. This field defines what metrics will be calculated on each ICA + comopnent. If a metric doesn't exist then this will raise an error instead + of executing a tree. If a necessary metric isn't used, there will be a warning. - generated_metrics An optional initial field. It lists metrics that are to be calculated as diff --git a/pyproject.toml b/pyproject.toml index cedc4b8b0..c3ccc6da3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ dependencies = [ "nibabel>=2.5.1,<=5.2.0", "nilearn>=0.7,<=0.10.3", "numpy>=1.16,<=1.26.4", - "pandas>=2.0,<=2.2.0", + "pandas>=2.0,<=2.2.1", "pybtex", "pybtex-apa-style", "scikit-learn>=0.21, <=1.4.1.post1", From 4e8ca2374a7b88c24751e7d6e83336c142375bad Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Mon, 4 Mar 2024 17:21:35 -0500 Subject: [PATCH 42/81] using external regressors and most tests passing --- tedana/metrics/_utils.py | 22 ++++ tedana/metrics/collect.py | 12 +- tedana/metrics/external.py | 110 +----------------- tedana/resources/config/metrics.json | 3 +- ...demo_minimal_externalregressors_Fstat.json | 7 ++ tedana/selection/component_selector.py | 69 +++++++++-- tedana/tests/test_component_selector.py | 2 - tedana/workflows/tedana.py | 56 +++++---- 8 files changed, 131 insertions(+), 150 deletions(-) diff --git a/tedana/metrics/_utils.py b/tedana/metrics/_utils.py index 8a9486a8f..70eaec7ba 100644 --- a/tedana/metrics/_utils.py +++ b/tedana/metrics/_utils.py @@ -8,6 +8,28 @@ LGR = logging.getLogger("GENERAL") +def add_external_dependencies(dependency_config, external_regressor_config): + """ + Add dependency information in external regressors are inputted. + + TODO Add a docstring + """ + # Add "external regressors" and an existing input + dependency_config["inputs"].append("external regressors") + + if external_regressor_config["calc_stats"].lower() == "f": + if "f_stats_partial_models" in set(external_regressor_config.keys()): + model_names = external_regressor_config["f_stats_partial_models"].append("Full") + else: + model_names = ["Full"] + for model_name in model_names: + for stat_type in ["Fstat", "R2stat", "pval"]: + dependency_config["dependencies"][f"{stat_type} {model_name} Model"] = [ + "external regressors" + ] + return dependency_config + + def dependency_resolver(dict_, requested_metrics, base_inputs): """Identify all necessary metrics based on a list of requested metrics. diff --git a/tedana/metrics/collect.py b/tedana/metrics/collect.py index 53a2d7d6d..ab066bff6 100644 --- a/tedana/metrics/collect.py +++ b/tedana/metrics/collect.py @@ -8,7 +8,12 @@ from tedana import io, utils from tedana.metrics import dependence, external -from tedana.metrics._utils import dependency_resolver, determine_signs, flip_components +from tedana.metrics._utils import ( + add_external_dependencies, + dependency_resolver, + determine_signs, + flip_components, +) from tedana.stats import getfbounds LGR = logging.getLogger("GENERAL") @@ -80,7 +85,8 @@ def generate_metrics( "If external_regressors is defined, then " "external_regressor_config also needs to be defined." ) - metrics.append("external fit") + dependency_config = add_external_dependencies(dependency_config, external_regressor_config) + dependency_config["inputs"].append("external regressors") RepLGR.info(f"The following metrics were calculated: {', '.join(metrics)}.") @@ -341,7 +347,7 @@ def generate_metrics( ) # External regressor-based metrics - if "external fit" in required_metrics: + if external_regressors is not None and external_regressor_config is not None: # external_regressor_names = external_regressors.columns.tolist() LGR.info("Calculating external regressor fits") comptable = external.fit_regressors( diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index 051640c79..e68178061 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -1,15 +1,11 @@ """Metrics unrelated to TE-(in)dependence.""" import logging -import os.path as op import numpy as np import pandas as pd from scipy import linalg, stats -from tedana.io import load_json -from tedana.utils import get_resource_path - LGR = logging.getLogger("GENERAL") RepLGR = logging.getLogger("REPORT") @@ -30,11 +26,9 @@ def load_validate_external_regressors(external_regressors, external_regressor_co ---------- external_regressors: :obj:`str` Path and name of tsv file that includes external regressor time series - external_regressor_config: :obj:`str` - An included dictionary name or path to a JSON file that defines one. - This contains expected column labels for the regressors, - how the regressors can be grouped together, and what statistical - tests to use. + external_regressor_config: :obj:`dict` + A validated dictionary with info for fitting external regressors + to component time series n_time: :obj:`int` Number of timepoints in the fMRI time series @@ -43,12 +37,7 @@ def load_validate_external_regressors(external_regressors, external_regressor_co external_regressors: :obj:`pandas.DataFrame` Each column is a labelled regressor and the number of rows should match the number of timepoints in the fMRI time series - external_regressor_config : :obj:`dict` - A validated dictionary with info for fitting external regressors - to component time series """ - LGR.info(f"Loading external regressor dictionary: {external_regressor_config}") - external_regressor_config = load_external_regressor_config(external_regressor_config) try: external_regressors = pd.read_table(external_regressors) except FileNotFoundError: @@ -56,47 +45,7 @@ def load_validate_external_regressors(external_regressors, external_regressor_co validate_extern_regress(external_regressors, external_regressor_config, n_time) - return external_regressors, external_regressor_config - - -def load_external_regressor_config(external_regressor_config): - """ - Load and validate external regressor dictionary. - - Parameters - ---------- - An included dictionary name or path to a JSON file that defines one. - This contains expected column labels for the regressors, - how the regressors can be grouped together, and what statistical - tests to use. - - Returns - ------- - external_regressor_config : :obj:`dict` - A validated dictionary for fitting external regressors to component time series - """ - if external_regressor_config in DEFAULT_REGRESSOR_DICTS: - fname = op.join( - get_resource_path(), "external_regressor_configs", external_regressor_config + ".json" - ) - else: - fname = external_regressor_config - - try: - external_regressor_config = load_json(fname) - except FileNotFoundError: - raise ValueError( - f"Cannot find external regressor dictionary {external_regressor_config}. " - "Please check your path or use a " - f"default dictionary: ({DEFAULT_REGRESSOR_DICTS})." - ) - except IsADirectoryError: - raise ValueError( - f"{external_regressor_config} is a directory. Please supply a JSON file or " - f"default dictionary: ({DEFAULT_REGRESSOR_DICTS})." - ) - - return external_regressor_config + return external_regressors def validate_extern_regress(external_regressors, external_regressor_config, n_time): @@ -129,57 +78,6 @@ def validate_extern_regress(external_regressors, external_regressor_config, n_ti # end of the function or raise errors that prevent the rest of the function from completing err_msg = "" - # Set the fields that should always be present - dict_expected_keys = set(["regess_ID", "info", "detrend", "calc_stats"]) - calc_stats_key_options = set(["f", "corr"]) - # defaults = {"selector", "decision_node_idx"} - # default_classifications = {"nochange", "accepted", "rejected", "unclassified"} - # default_decide_comps = {"all", "accepted", "rejected", "unclassified"} - - # Confirm that the required fields exist - missing_keys = dict_expected_keys - set(external_regressor_config.keys()) - if missing_keys: - # If there are missing keys, this function may crash before the end. - # End function here with a clear error message rather than adding - # `if assert external_regressor_config.get()` statements before every section - err_msg += f"External regressor dictionary missing required fields: {missing_keys}\n" - - if external_regressor_config["calc_stats"].lower() not in calc_stats_key_options: - err_msg += ( - "calc_stats in external_regressor_config is " - f"{external_regressor_config['calc_stats']}. It must be one of the following " - f"{calc_stats_key_options}\n" - ) - - if (external_regressor_config["calc_stats"].lower() != "f") and ( - "f_stats_partial_models" in set(external_regressor_config.keys()) - ): - err_msg += ( - "External regressor dictionary cannot include" - "f_stats_partial_models if calc_stats is not F\n" - ) - - if "f_stats_partial_models" in set(external_regressor_config.keys()): - dict_expected_keys.add("f_stats_partial_models") - dict_expected_keys.update(set(external_regressor_config["f_stats_partial_models"])) - missing_partial_models = set(external_regressor_config["f_stats_partial_models"]) - set( - external_regressor_config.keys() - ) - if missing_partial_models: - raise RegressError( - f"{err_msg}" - "External regressor dictionary missing required fields for partial " - f"models defined in f_stats_partial_models: {missing_partial_models}" - ) - - # Warn if unused fields exist - unused_keys = set(external_regressor_config.keys()) - set(dict_expected_keys) - if unused_keys: - LGR.warning( - "External regressor dictionary includes fields that " - f"are not used or logged {unused_keys}" - ) - # Validating the information in external_regressor_config works # with the data in external_regressors diff --git a/tedana/resources/config/metrics.json b/tedana/resources/config/metrics.json index 6b9d50a1b..2d1c6bc3d 100644 --- a/tedana/resources/config/metrics.json +++ b/tedana/resources/config/metrics.json @@ -6,8 +6,7 @@ "adaptive_mask", "mask", "tes", - "ref_img", - "external_regressors" + "ref_img" ], "dependencies": { "kappa": [ diff --git a/tedana/resources/decision_trees/demo_minimal_externalregressors_Fstat.json b/tedana/resources/decision_trees/demo_minimal_externalregressors_Fstat.json index 7a7939e61..a125a0b7f 100644 --- a/tedana/resources/decision_trees/demo_minimal_externalregressors_Fstat.json +++ b/tedana/resources/decision_trees/demo_minimal_externalregressors_Fstat.json @@ -23,6 +23,13 @@ "Unlikely BOLD", "Low variance" ], + "external_regressors": { + "regess_ID": "Fmodel", + "info": "Fits all external regressors to a single model using an F statistic", + "report": "Unspecified external nuissance regressors were fit to components using a linear model were rejected if they fit.", + "detrend": true, + "calc_stats": "F" + }, "nodes": [ { "functionname": "manual_classify", diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 260ab4e72..27d51c5ee 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -94,11 +94,17 @@ def validate_tree(tree): "intermediate_classifications", "classification_tags", "nodes", + "external_regressors", ] + tree_optional_keys = ["generated_metrics"] defaults = {"selector", "decision_node_idx"} default_classifications = {"nochange", "accepted", "rejected", "unclassified"} default_decide_comps = {"all", "accepted", "rejected", "unclassified"} + # If a tree doesn't include "external_regressors", instead of crashing, set to None + if "external_regressors" not in set(tree.keys()): + tree["external_regressors"] = None + # Confirm that the required fields exist missing_keys = set(tree_expected_keys) - set(tree.keys()) if missing_keys: @@ -109,12 +115,9 @@ def validate_tree(tree): # Warn if unused fields exist unused_keys = set(tree.keys()) - set(tree_expected_keys) - {"used_metrics"} - # Make sure some fields don't trigger a warning; hacky, sorry - ok_to_not_use = ( - "reconstruct_from", - "generated_metrics", - ) - for k in ok_to_not_use: + + # Separately check for optional, but used keys + for k in tree_optional_keys: if k in unused_keys: unused_keys.remove(k) if unused_keys: @@ -216,6 +219,57 @@ def validate_tree(tree): "tag that was not predefined" ) + # If there is an external_regressors field, validate it + if tree["external_regressors"] is not None: + external_regressor_config = tree["external_regressors"] + # Define the fields that should always be present + dict_expected_keys = set(["regess_ID", "info", "detrend", "calc_stats"]) + + # Right now, "f" is the only option, but this leaves open the possibility + # to have additional options + calc_stats_key_options = set("f") + + # Confirm that the required fields exist + missing_keys = dict_expected_keys - set(external_regressor_config.keys()) + if missing_keys: + err_msg += f"External regressor dictionary missing required fields: {missing_keys}\n" + + if external_regressor_config["calc_stats"].lower() not in calc_stats_key_options: + err_msg += ( + "calc_stats in external_regressor_config is " + f"{external_regressor_config['calc_stats']}. It must be one of the following: " + f"{calc_stats_key_options}\n" + ) + + if (external_regressor_config["calc_stats"].lower() != "f") and ( + "f_stats_partial_models" in set(external_regressor_config.keys()) + ): + err_msg += ( + "External regressor dictionary cannot include" + "f_stats_partial_models if calc_stats is not F\n" + ) + + if "f_stats_partial_models" in set(external_regressor_config.keys()): + dict_expected_keys.add("f_stats_partial_models") + dict_expected_keys.update(set(external_regressor_config["f_stats_partial_models"])) + missing_partial_models = set( + external_regressor_config["f_stats_partial_models"] + ) - set(external_regressor_config.keys()) + if missing_partial_models: + raise TreeError( + f"{err_msg}" + "External regressor dictionary missing required fields for partial " + f"models defined in f_stats_partial_models: {missing_partial_models}" + ) + + # Warn if unused fields exist + unused_keys = set(external_regressor_config.keys()) - set(dict_expected_keys) + if unused_keys: + LGR.warning( + "External regressor dictionary includes fields that " + f"are not used or logged {unused_keys}" + ) + if err_msg: raise TreeError("\n" + err_msg) @@ -240,7 +294,6 @@ def __init__(self, tree): """ self.tree_name = tree self.tree = load_config(self.tree_name) - self.tree = expand_nodes(self.tree, self.component_table_.columns.tolist()) LGR.info("Performing component selection with " + self.tree["tree_id"]) LGR.info(self.tree.get("info", "")) @@ -328,6 +381,8 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) # Construct an un-executed selector self.component_table_ = component_table.copy() + # Expand out metrics defined by regular expressions in the nodes + self.tree = expand_nodes(self.tree, self.component_table_.columns.tolist()) # this will crash the program with an error message if not all # necessary_metrics are in the comptable diff --git a/tedana/tests/test_component_selector.py b/tedana/tests/test_component_selector.py index f191df39f..91732f691 100644 --- a/tedana/tests/test_component_selector.py +++ b/tedana/tests/test_component_selector.py @@ -207,8 +207,6 @@ def test_validate_tree_succeeds(): # Test a few extra possabilities just using the minimal.json tree if "/minimal.json" in tree_name: - # Should remove/ignore the "reconstruct_from" key during validation - tree["reconstruct_from"] = "testinput" # Need to test handling of the tag_if_false kwarg somewhere tree["nodes"][1]["kwargs"]["tag_if_false"] = "testing tag" assert component_selector.validate_tree(tree) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 0e4bba98a..d2f9227ff 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -536,19 +536,15 @@ def tedana_workflow( # Load external regressors if provided # Decided to do the validation here so that, if there are issues, an error # will be raised before PCA/ICA - if external_regressors: - if external_regressor_config: - ( - external_regressors, - external_regressor_config, - ) = metrics.external.load_validate_external_regressors( - external_regressors, external_regressor_config, catd.shape[2] - ) - else: - raise ValueError( - "If external_regressors is an input, then " - "external_regressor_config also needs to be used." - ) + if "external_regressors" in set(selector.tree.keys()): + external_regressors = metrics.external.load_validate_external_regressors( + external_regressors, selector.tree["external_regressors"], catd.shape[2] + ) + else: + raise ValueError( + "If external_regressors is an input, then " + "external_regressor_config also needs to be used." + ) io_generator = io.OutputGenerator( ref_img, @@ -722,17 +718,17 @@ def tedana_workflow( extra_metrics = ["variance explained", "normalized variance explained", "kappa", "rho"] necessary_metrics = sorted(list(set(necessary_metrics + extra_metrics))) - comptable = metrics.collect.generate_metrics( - catd, - data_oc, - mmix, - masksum_clf, - tes, - io_generator, - "ICA", + comptable, _ = metrics.collect.generate_metrics( + data_cat=catd, + data_optcom=data_oc, + mixing=mmix, + adaptive_mask=masksum_clf, + tes=tes, + io_generator=io_generator, + label="ICA", metrics=necessary_metrics, external_regressors=external_regressors, - external_regressor_config=external_regressor_config, + external_regressor_config=selector.tree["external_regressors"], ) LGR.info("Selecting components from ICA results") selector = selection.automatic_selection( @@ -770,14 +766,14 @@ def tedana_workflow( extra_metrics = ["variance explained", "normalized variance explained", "kappa", "rho"] necessary_metrics = sorted(list(set(necessary_metrics + extra_metrics))) - comptable = metrics.collect.generate_metrics( - catd, - data_oc, - mmix, - masksum_clf, - tes, - io_generator, - "ICA", + comptable, _ = metrics.collect.generate_metrics( + data_cat=catd, + data_optcom=data_oc, + mixing=mmix, + adaptive_mask=masksum_clf, + tes=tes, + io_generator=io_generator, + label="ICA", metrics=necessary_metrics, external_regressors=external_regressors, external_regressor_config=external_regressor_config, From e2cf60ecaa91f8283452d2adbb9184f17c7ea64b Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Thu, 7 Mar 2024 14:57:24 -0500 Subject: [PATCH 43/81] removed corr added tasks --- tedana/metrics/external.py | 114 ++++--- ...inimal_externalregressors_correlation.json | 281 ------------------ ...rnalregressors_correlation_with_regex.json | 247 --------------- .../external_regressor_configs/Fmodel.json | 7 - .../corr_detrend.json | 7 - .../corr_no_detrend.json | 7 - tedana/tests/test_integration.py | 49 --- tedana/workflows/tedana.py | 10 +- 8 files changed, 54 insertions(+), 668 deletions(-) delete mode 100644 tedana/resources/decision_trees/demo_minimal_externalregressors_correlation.json delete mode 100644 tedana/resources/decision_trees/demo_minimal_externalregressors_correlation_with_regex.json delete mode 100644 tedana/resources/external_regressor_configs/Fmodel.json delete mode 100644 tedana/resources/external_regressor_configs/corr_detrend.json delete mode 100644 tedana/resources/external_regressor_configs/corr_no_detrend.json diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index e68178061..e3a79bbe2 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -164,18 +164,19 @@ def fit_regressors(comptable, external_regressors, external_regressor_config, mi else: LGR.warning("External regressor fitting applied without detrending fMRI time series") - if external_regressor_config["calc_stats"].lower() == "corr": - if "detrend_regressors" not in locals(): - # set detrend regressors to None if it doesn't exist - detrend_regressors = None - comptable = correlate_regressors( - comptable, external_regressors, mixing, detrend_regressors=detrend_regressors - ) - elif external_regressor_config["calc_stats"].lower() == "f": + if external_regressor_config["calc_stats"].lower() == "f": # external_regressors = pd.concat([external_regressors, detrend_regressors]) comptable = fit_mixing_to_regressors( comptable, external_regressors, external_regressor_config, mixing, detrend_regressors ) + else: + # This should already be valided by this point, but keeping the catch clause here + # since this would otherwise just return comptable with no changes, which would + # make a hard-to-track error + raise ValueError( + "calc_stats for external regressors in decision tree is " + f"{external_regressor_config['calc_stats'].lower()}, which is not valid." + ) return comptable @@ -300,13 +301,20 @@ def fit_mixing_to_regressors( # betas_full_model = pd.DataFrame( # data=betas_full.T, # columns=np.concatenate( - # (np.array(detrend_regressors.columns), np.array(external_regressors.columns)) + # (np.array(detrend_regressors.columns), np.array(exte rnal_regressors.columns)) # ), # ) f_vals = pd.DataFrame(data=f_vals_tmp, columns=["Fstat Full Model"]) p_vals = pd.DataFrame(data=p_vals_tmp, columns=["pval Full Model"]) r2_vals = pd.DataFrame(data=r2_vals_tmp, columns=["R2stat Full Model"]) + # Run a separate model if there are task regressors that might want to be kept + # TODO Need to edit this function so regressor_models can use something besides the full model + if "task keep" in regressor_models.keys(): + betas_task_keep, f_vals_tmp, p_vals_tmp, r2_vals_tmp = fit_model_with_stats( + y=mixing, regressor_models=regressor_models, base_label="base", full_label="task keep" + ) + # Test the fits between the full model and the full model excluding one category of regressor if "f_stats_partial_models" in external_regressor_config.keys(): for pmodel in external_regressor_config["f_stats_partial_models"]: @@ -362,14 +370,36 @@ def build_fstat_regressor_models( # All regressor labels from the data frame regressor_labels = external_regressors.columns - detrend_regressors_arr = detrend_regressors.to_numpy() - regressor_models = { - "base": detrend_regressors_arr, - "full": np.concatenate( + regressor_models = {"base": detrend_regressors_arr} + LGR.info(f"Size for base Regressor Model: {regressor_models['base'].shape}") + + if "task_keep_model" in external_regressor_config: + task_keep_model = external_regressor_config["task_keep_model"] + tmp_model_labels = {"full": set(regressor_labels) - set(task_keep_model)} + tmp_model_labels["task keep"] = set(task_keep_model) + for model_name in ["full", "task keep"]: + regressor_models[model_name] = detrend_regressors_arr + for keep_label in tmp_model_labels[model_name]: + regressor_models[model_name] = np.concatenate( + ( + regressor_models[model_name], + np.atleast_2d( + stats.zscore(external_regressors[keep_label].to_numpy(), axis=0) + ).T, + ), + axis=1, + ) + tmp_model_labels[model_name].update(set(detrend_regressors.columns)) + LGR.info(f"Size for {model_name} Model: {regressor_models[model_name].shape}") + LGR.info( + f"Regressors In {model_name} Model:'{model_name}': {tmp_model_labels[model_name]}" + ) + else: + regressor_models["full"] = np.concatenate( (detrend_regressors_arr, stats.zscore(external_regressors.to_numpy(), axis=0)), axis=1 - ), - } + ) + LGR.info(f"Size for full Regressor Model: {regressor_models['full'].shape}") for pmodel in partial_models: # For F statistics, the other models to test are those that include everything EXCEPT @@ -398,9 +428,6 @@ def build_fstat_regressor_models( f"'{no_pmodel}': {keep_labels}" ) - LGR.info(f"Size for full Regressor Model: {regressor_models['full'].shape}") - LGR.info(f"Size for base Regressor Model: {regressor_models['base'].shape}") - # vestigial codethat was used to check outputs and might be worth reviving # if show_plot: # fig = plt.figure(figsize=(10, 10)) @@ -462,7 +489,7 @@ def fit_model(x, y, output_residual=False): return betas, sse, df -def fit_model_with_stats(y, regressor_models, base_label): +def fit_model_with_stats(y, regressor_models, base_label, full_label="full"): """ Fit full and partial models and calculate F stats, R2, and p values. @@ -483,6 +510,9 @@ def fit_model_with_stats(y, regressor_models, base_label): The base model to compare the full model against. For F stat for the full model, this should be 'base'. For partial models, this should be the name of the partial model (i.e. "no motion"). + full_label : :obj:`str` + The full model to use. Default="full" but can also be "task_keep" if + tasks were inputted and want to compare results against task. Returns ------- @@ -496,7 +526,7 @@ def fit_model_with_stats(y, regressor_models, base_label): The R2 statistics for the fits to the full and partial models """ betas_base, sse_base, df_base = fit_model(regressor_models[base_label], y) - betas_full, sse_full, df_full = fit_model(regressor_models["full"], y) + betas_full, sse_full, df_full = fit_model(regressor_models[full_label], y) # larger sample variance / smaller sample variance (F = (SSE1 – SSE2 / m) / SSE2 / n-k, # where SSE = residual sum of squares, m = number of restrictions and k = number of @@ -551,50 +581,6 @@ def fit_model_with_stats(y, regressor_models, base_label): return betas_full, f_vals, p_vals, r2_vals -def correlate_regressors(comptable, external_regressors, mixing, detrend_regressors=None): - """Correlate external regressors with mixing components. - - Parameters - ---------- - comptable : (C x X) :obj:`pandas.DataFrame` - Component metric table. One row for each component, with a column for - each metric. The index is the component number. - external_regressors : :obj:`pandas.DataFrame` - Each column is a labelled regressor and the number of rows should - match the number of timepoints in the fMRI time series - mixing : (T x C) array_like - Mixing matrix for converting input data to component space, where `C` - is components and `T` is the same as in `data_cat` - detrend_regressors: (n_time x polort) :obj:`pandas.DataFrame` - Dataframe containing the detrending regressor time series - - Returns - ------- - comptable : (C x X) :obj:`pandas.DataFrame` - Component metric table. Same as inputted, with additional columns - for metrics related to fitting the external regressors. Each - external regressor has one metric column with the name of the - regressor followed by "_correlation_ - """ - if isinstance(detrend_regressors, pd.DataFrame): - # Detrend the mixing matrix before correlating to external regressors - mixing = fit_model(detrend_regressors.to_numpy(), mixing, output_residual=True) - - external_regressor_names = external_regressors.columns.tolist() - for col in external_regressor_names: - external_regressor_arr = external_regressors[col].values - if isinstance(detrend_regressors, pd.DataFrame): - external_regressor_arr = fit_model( - detrend_regressors.to_numpy(), external_regressor_arr, output_residual=True - ) - assert external_regressor_arr.ndim == 1 - assert external_regressor_arr.shape[0] == mixing.shape[0] - corrs = np.abs(np.corrcoef(external_regressor_arr, mixing.T)[0, 1:]) - comptable[f"{col}_correlation"] = corrs - - return comptable - - # Vestigial code that was used for testing accuracy of some variables # Might be worth reviving # def plot_fit( diff --git a/tedana/resources/decision_trees/demo_minimal_externalregressors_correlation.json b/tedana/resources/decision_trees/demo_minimal_externalregressors_correlation.json deleted file mode 100644 index c2c03f662..000000000 --- a/tedana/resources/decision_trees/demo_minimal_externalregressors_correlation.json +++ /dev/null @@ -1,281 +0,0 @@ -{ - "tree_id": "demo_minimal_externalregressors_correlation", - "info": "Demonstration based on the minimal decision tree that calculates a correlation for each external regressor and sets a threshold for each regressor in a separate node.", - "report": "This is based on the minimal criteria of the original MEICA decision tree \\citep{kundu2013integrated} without the more aggressive noise removal steps \\citep{dupre2021te}.", - "necessary_metrics": [ - "kappa", - "rho", - "countsigFS0", - "countsigFT2", - "dice_FS0", - "dice_FT2", - "signal-noise_t", - "variance explained", - "trans_x_correlation", - "trans_y_correlation", - "trans_z_correlation" - ], - "intermediate_classifications": [ - "provisionalaccept", - "provisionalreject" - ], - "classification_tags": [ - "Likely BOLD", - "Unlikely BOLD", - "Low variance" - ], - "nodes": [ - { - "functionname": "manual_classify", - "parameters": { - "new_classification": "unclassified", - "decide_comps": "all" - }, - "kwargs": { - "log_extra_report": "", - "clear_classification_tags": true, - "dont_warn_reclassify": true - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "rejected", - "if_false": "nochange", - "decide_comps": "all", - "op": ">", - "left": "rho", - "right": "kappa" - }, - "kwargs": { - "log_extra_report": "", - "tag_if_true": "Unlikely BOLD" - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "rejected", - "if_false": "nochange", - "decide_comps": "all", - "op": ">", - "left": "countsigFS0", - "right": "countsigFT2" - }, - "kwargs": { - "left2": "countsigFT2", - "op2": ">", - "right2": 0, - "log_extra_report": "", - "tag_if_true": "Unlikely BOLD" - } - }, - { - "functionname": "calc_median", - "parameters": { - "decide_comps": "all", - "metric_name": "variance explained", - "median_label": "varex" - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "rejected", - "if_false": "nochange", - "decide_comps": "all", - "op": ">", - "left": "dice_FS0", - "right": "dice_FT2" - }, - "kwargs": { - "left2": "variance explained", - "op2": ">", - "right2": "median_varex", - "log_extra_report": "", - "tag_if_true": "Unlikely BOLD" - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "rejected", - "if_false": "nochange", - "decide_comps": "all", - "op": ">", - "left": 0, - "right": "signal-noise_t" - }, - "kwargs": { - "left2": "variance explained", - "op2": ">", - "right2": "median_varex", - "log_extra_report": "", - "tag_if_true": "Unlikely BOLD" - } - }, - { - "functionname": "calc_kappa_elbow", - "parameters": { - "decide_comps": "all" - }, - "kwargs": { - "log_extra_info": "", - "log_extra_report": "" - }, - "_comment": "" - }, - { - "functionname": "calc_rho_elbow", - "parameters": { - "decide_comps": "all" - }, - "kwargs": { - "subset_decide_comps": "unclassified", - "rho_elbow_type": "liberal", - "log_extra_info": "", - "log_extra_report": "" - }, - "_comment": "" - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "provisionalaccept", - "if_false": "provisionalreject", - "decide_comps": "unclassified", - "op": ">=", - "left": "kappa", - "right": "kappa_elbow_kundu" - }, - "kwargs": { - "log_extra_report": "" - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "accepted", - "if_false": "nochange", - "decide_comps": "provisionalaccept", - "op": ">", - "left": "kappa", - "right": "rho" - }, - "kwargs": { - "log_extra_info": "If kappa>elbow and kappa>2*rho accept even if rho>elbow", - "log_extra_report": "", - "right_scale": 2, - "tag_if_true": "Likely BOLD" - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "provisionalreject", - "if_false": "nochange", - "decide_comps": [ - "provisionalreject", - "provisionalaccept" - ], - "op": ">", - "left": "rho", - "right": "rho_elbow_liberal" - }, - "kwargs": { - "log_extra_report": "" - } - }, - { - "functionname": "dec_variance_lessthan_thresholds", - "parameters": { - "if_true": "accepted", - "if_false": "nochange", - "decide_comps": "provisionalreject" - }, - "kwargs": { - "var_metric": "variance explained", - "log_extra_info": "", - "log_extra_report": "", - "single_comp_threshold": 0.1, - "all_comp_threshold": 1.0, - "tag_if_true": "Low variance" - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "rejected", - "if_false": "nochange", - "decide_comps": "all", - "op": ">", - "left": "trans_x_correlation", - "right": 0.8 - }, - "kwargs": { - "log_extra_info": "If translational motion in the X direction is correlated with the components > 0.8, then reject.", - "log_extra_report": "", - "tag_if_true": "corr to X translational motion" - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "rejected", - "if_false": "nochange", - "decide_comps": "all", - "op": ">", - "left": "trans_y_correlation", - "right": 0.8 - }, - "kwargs": { - "log_extra_info": "If translational motion in the Y direction is correlated with the components > 0.8, then reject.", - "log_extra_report": "", - "tag_if_true": "corr to Y translational motion" - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "rejected", - "if_false": "nochange", - "decide_comps": "all", - "op": ">", - "left": "trans_z_correlation", - "right": 0.8 - }, - "kwargs": { - "log_extra_info": "If translational motion in the Z direction is correlated with the components > 0.8, then reject.", - "log_extra_report": "", - "tag_if_true": "corr to Z translational motion" - } - }, - { - "functionname": "manual_classify", - "parameters": { - "new_classification": "accepted", - "decide_comps": "provisionalaccept" - }, - "kwargs": { - "log_extra_info": "", - "log_extra_report": "", - "tag": "Likely BOLD" - } - }, - { - "functionname": "manual_classify", - "parameters": { - "new_classification": "rejected", - "decide_comps": [ - "provisionalreject", - "unclassified" - ] - }, - "kwargs": { - "log_extra_info": "", - "log_extra_report": "", - "tag": "Unlikely BOLD" - } - } - ] -} diff --git a/tedana/resources/decision_trees/demo_minimal_externalregressors_correlation_with_regex.json b/tedana/resources/decision_trees/demo_minimal_externalregressors_correlation_with_regex.json deleted file mode 100644 index beb126ebf..000000000 --- a/tedana/resources/decision_trees/demo_minimal_externalregressors_correlation_with_regex.json +++ /dev/null @@ -1,247 +0,0 @@ -{ - "tree_id": "demo_minimal_externalregressors_correlation_with_regex", - "info": "Demonstration based on the minimal decision tree that calculates a correlation for each external regressors and sets a threshold for a single node for all of them using regex syntax.", - "report": "This is based on the minimal criteria of the original MEICA decision tree \\citep{kundu2013integrated} without the more aggressive noise removal steps \\citep{dupre2021te}.", - "necessary_metrics": [ - "kappa", - "rho", - "countsigFS0", - "countsigFT2", - "dice_FS0", - "dice_FT2", - "signal-noise_t", - "variance explained", - "^.*_correlation$" - ], - "intermediate_classifications": [ - "provisionalaccept", - "provisionalreject" - ], - "classification_tags": [ - "Likely BOLD", - "Unlikely BOLD", - "Low variance" - ], - "nodes": [ - { - "functionname": "manual_classify", - "parameters": { - "new_classification": "unclassified", - "decide_comps": "all" - }, - "kwargs": { - "log_extra_report": "", - "clear_classification_tags": true, - "dont_warn_reclassify": true - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "rejected", - "if_false": "nochange", - "decide_comps": "all", - "op": ">", - "left": "rho", - "right": "kappa" - }, - "kwargs": { - "log_extra_report": "", - "tag_if_true": "Unlikely BOLD" - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "rejected", - "if_false": "nochange", - "decide_comps": "all", - "op": ">", - "left": "countsigFS0", - "right": "countsigFT2" - }, - "kwargs": { - "left2": "countsigFT2", - "op2": ">", - "right2": 0, - "log_extra_report": "", - "tag_if_true": "Unlikely BOLD" - } - }, - { - "functionname": "calc_median", - "parameters": { - "decide_comps": "all", - "metric_name": "variance explained", - "median_label": "varex" - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "rejected", - "if_false": "nochange", - "decide_comps": "all", - "op": ">", - "left": "dice_FS0", - "right": "dice_FT2" - }, - "kwargs": { - "left2": "variance explained", - "op2": ">", - "right2": "median_varex", - "log_extra_report": "", - "tag_if_true": "Unlikely BOLD" - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "rejected", - "if_false": "nochange", - "decide_comps": "all", - "op": ">", - "left": 0, - "right": "signal-noise_t" - }, - "kwargs": { - "left2": "variance explained", - "op2": ">", - "right2": "median_varex", - "log_extra_report": "", - "tag_if_true": "Unlikely BOLD" - } - }, - { - "functionname": "calc_kappa_elbow", - "parameters": { - "decide_comps": "all" - }, - "kwargs": { - "log_extra_info": "", - "log_extra_report": "" - }, - "_comment": "" - }, - { - "functionname": "calc_rho_elbow", - "parameters": { - "decide_comps": "all" - }, - "kwargs": { - "subset_decide_comps": "unclassified", - "rho_elbow_type": "liberal", - "log_extra_info": "", - "log_extra_report": "" - }, - "_comment": "" - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "provisionalaccept", - "if_false": "provisionalreject", - "decide_comps": "unclassified", - "op": ">=", - "left": "kappa", - "right": "kappa_elbow_kundu" - }, - "kwargs": { - "log_extra_report": "" - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "accepted", - "if_false": "nochange", - "decide_comps": "provisionalaccept", - "op": ">", - "left": "kappa", - "right": "rho" - }, - "kwargs": { - "log_extra_info": "If kappa>elbow and kappa>2*rho accept even if rho>elbow", - "log_extra_report": "", - "right_scale": 2, - "tag_if_true": "Likely BOLD" - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "provisionalreject", - "if_false": "nochange", - "decide_comps": [ - "provisionalreject", - "provisionalaccept" - ], - "op": ">", - "left": "rho", - "right": "rho_elbow_liberal" - }, - "kwargs": { - "log_extra_report": "" - } - }, - { - "functionname": "dec_variance_lessthan_thresholds", - "parameters": { - "if_true": "accepted", - "if_false": "nochange", - "decide_comps": "provisionalreject" - }, - "kwargs": { - "var_metric": "variance explained", - "log_extra_info": "", - "log_extra_report": "", - "single_comp_threshold": 0.1, - "all_comp_threshold": 1.0, - "tag_if_true": "Low variance" - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "rejected", - "if_false": "nochange", - "decide_comps": "all", - "op": ">", - "left": "^trans_[x|y|z]_correlation$", - "right": 0.8 - }, - "kwargs": { - "log_extra_info": "If translational motion in any direction is correlated with the components > 0.8, then reject.", - "log_extra_report": "", - "tag_if_true": "corr to translational motion" - } - }, - { - "functionname": "manual_classify", - "parameters": { - "new_classification": "accepted", - "decide_comps": "provisionalaccept" - }, - "kwargs": { - "log_extra_info": "", - "log_extra_report": "", - "tag": "Likely BOLD" - } - }, - { - "functionname": "manual_classify", - "parameters": { - "new_classification": "rejected", - "decide_comps": [ - "provisionalreject", - "unclassified" - ] - }, - "kwargs": { - "log_extra_info": "", - "log_extra_report": "", - "tag": "Unlikely BOLD" - } - } - ] -} diff --git a/tedana/resources/external_regressor_configs/Fmodel.json b/tedana/resources/external_regressor_configs/Fmodel.json deleted file mode 100644 index 9c0aebb60..000000000 --- a/tedana/resources/external_regressor_configs/Fmodel.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "regess_ID": "Fmodel", - "info": "Fits all external regressors to a single model using an F statistic", - "report": "Unspecified external nuissance regressors were fit to components using a linear model were rejected if they fit.", - "detrend": true, - "calc_stats": "F" -} diff --git a/tedana/resources/external_regressor_configs/corr_detrend.json b/tedana/resources/external_regressor_configs/corr_detrend.json deleted file mode 100644 index 1142d1ba7..000000000 --- a/tedana/resources/external_regressor_configs/corr_detrend.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "regess_ID": "corr_detrend", - "info": "Separately correlates every regressor after detrending regressors and fMRI data.", - "report": "Unspecified external nuissance regressors were detrended and correlated to components and rejected if any individual regressor had a high correlation.", - "detrend": true, - "calc_stats": "corr" -} diff --git a/tedana/resources/external_regressor_configs/corr_no_detrend.json b/tedana/resources/external_regressor_configs/corr_no_detrend.json deleted file mode 100644 index 0fd6e1c83..000000000 --- a/tedana/resources/external_regressor_configs/corr_no_detrend.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "regess_ID": "corr_detrend", - "info": "Separately correlates every regressor after detrending regressors and fMRI data.", - "report": "Unspecified external nuissance regressors were correlated to components and rejected if any individual regressor had a high correlation.", - "detrend": false, - "calc_stats": "corr" -} diff --git a/tedana/tests/test_integration.py b/tedana/tests/test_integration.py index db1094d02..ba98e03f9 100644 --- a/tedana/tests/test_integration.py +++ b/tedana/tests/test_integration.py @@ -374,55 +374,6 @@ def test_integration_three_echo(skip_integration): check_integration_outputs(fn, out_dir) -def test_integration_three_echo_external_regressors_corr(skip_integration): - """Integration test of tedana workflow with extern regress and correlation.""" - - if skip_integration: - pytest.skip("Skipping three-echo with external regressors integration test") - - test_data_path, osf_id = data_for_testing_info("three-echo") - out_dir = os.path.abspath( - os.path.join(test_data_path, "../../outputs/three-echo-externalreg-corr") - ) - out_dir_manual = f"{out_dir}-rerun" - - if os.path.exists(out_dir): - shutil.rmtree(out_dir) - - if os.path.exists(out_dir_manual): - shutil.rmtree(out_dir_manual) - - # download data and run the test - # external_regress_corr_3echo.tsv has 6 rows. Based on a local run on the 3 echo data: - # Col 1 (trans_x_correlation) is the TS for ICA comp 59 + similar stdev Gaussian Noise - # Col 2 (trans_y_correlation) is 0.4*comp29+0.5+comp20+Gaussian Noise - # Col 3 (trans_z_correlation) is comp20+Gaussian Noise - # With the currently set up decision tree minimal_exteral 2, one component (my 59) - # should be rejected, but 20 and 29 aren't rejected because neither crosses the - # r>0.8 threshold. If trans_y and trans_Z were included in a single model then - # component 20 would have been rejected - # Note that the above is in comparision to the minimal decision tree - # but the integration test for 3 echoes uses the kundu tree - download_test_data(osf_id, test_data_path) - tree_name = "resources/decision_trees/demo_minimal_externalregressors_correlation.json" - tedana_cli.tedana_workflow( - data=f"{test_data_path}/three_echo_Cornell_zcat.nii.gz", - tes=[14.5, 38.5, 62.5], - out_dir=out_dir, - tree=resource_filename("tedana", tree_name), - external_regressors=resource_filename( - "tedana", "tests/data/external_regress_corr_3echo.tsv" - ), - external_regressor_config="corr_detrend", - low_mem=True, - tedpca="aic", - ) - - # compare the generated output files - fn = resource_filename("tedana", "tests/data/cornell_three_echo_outputs.txt") - check_integration_outputs(fn, out_dir) - - def test_integration_three_echo_external_regressors_fstat(skip_integration): """Integration test of tedana workflow with extern regress and F stat.""" diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index d2f9227ff..738561b2c 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -536,15 +536,13 @@ def tedana_workflow( # Load external regressors if provided # Decided to do the validation here so that, if there are issues, an error # will be raised before PCA/ICA - if "external_regressors" in set(selector.tree.keys()): + if ( + "external_regressors" in set(selector.tree.keys()) + and selector.tree["external_regressors"] is not None + ): external_regressors = metrics.external.load_validate_external_regressors( external_regressors, selector.tree["external_regressors"], catd.shape[2] ) - else: - raise ValueError( - "If external_regressors is an input, then " - "external_regressor_config also needs to be used." - ) io_generator = io.OutputGenerator( ref_img, From e1491b3b3adeb9c3cff3ff82b25e86c4e0e68c4b Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Thu, 7 Mar 2024 15:14:01 -0500 Subject: [PATCH 44/81] fit_model moved to stats --- tedana/metrics/external.py | 47 +++----------------------------------- tedana/stats.py | 45 +++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 45 deletions(-) diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index e3a79bbe2..b3ada8f4d 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -4,7 +4,9 @@ import numpy as np import pandas as pd -from scipy import linalg, stats +from scipy import stats + +from tedana.stats import fit_model LGR = logging.getLogger("GENERAL") RepLGR = logging.getLogger("REPORT") @@ -446,49 +448,6 @@ def build_fstat_regressor_models( return regressor_models -def fit_model(x, y, output_residual=False): - """ - Linear regression for a model y = betas * x + error. - - Parameters - ---------- - x : (R X T) :obj:`numpy.ndarray` - 2D array with the regressors for the specified model an time - y : (T X C) :obj:`numpy.ndarray` - Time by mixing matrix components for the time series for fitting - output_residual : :obj:`bool` - If true, then this just outputs the residual of the fit. - If false, then outputs beta fits, sse, and df - - Returns - ------- - residual : (T X C) :obj:`numpy.ndarray` - The residual time series for the fit (only if output_residual is True) - betas : (R X C) :obj:`numpy.ndarray` - The magnitude fits for the model (only if output_residual is False) - sse : (R X C) :obj:`numpy.ndarray` - The sum of square error for the model (only if output_residual is False) - df : (R X C) :obj:`numpy.ndarray` - The degrees of freeom for the model (only if output_residual is False) - (timepoints - number of regressors) - """ - betas, _, _, _ = linalg.lstsq(x, y) - # matrix-multiplication on the regressors with the betas -> to create a new 'estimated' - # component matrix = fitted regressors (least squares beta solution * regressors) - fitted_regressors = np.matmul(x, betas) - residual = y - fitted_regressors - if output_residual: - return residual - else: - # sum the differences between the actual ICA components and the 'estimated' - # component matrix (beta-fitted regressors) - sse = np.sum(np.square(residual), axis=0) - # calculate how many individual values [timepoints] are free to vary after - # the least-squares solution [beta] betw X & Y is calculated - df = y.shape[0] - betas.shape[0] - return betas, sse, df - - def fit_model_with_stats(y, regressor_models, base_label, full_label="full"): """ Fit full and partial models and calculate F stats, R2, and p values. diff --git a/tedana/stats.py b/tedana/stats.py index cb6991bbc..3a82f0841 100644 --- a/tedana/stats.py +++ b/tedana/stats.py @@ -3,7 +3,7 @@ import logging import numpy as np -from scipy import stats +from scipy import linalg, stats from tedana import utils @@ -223,3 +223,46 @@ def t_to_z(t_values, dof): if ret_float: out = out[0] return out + + +def fit_model(x, y, output_residual=False): + """ + Linear regression for a model y = betas * x + error. + + Parameters + ---------- + x : (R X T) :obj:`numpy.ndarray` + 2D array with the regressors for the specified model an time + y : (T X C) :obj:`numpy.ndarray` + Time by mixing matrix components for the time series for fitting + output_residual : :obj:`bool` + If true, then this just outputs the residual of the fit. + If false, then outputs beta fits, sse, and df + + Returns + ------- + residual : (T X C) :obj:`numpy.ndarray` + The residual time series for the fit (only if output_residual is True) + betas : (R X C) :obj:`numpy.ndarray` + The magnitude fits for the model (only if output_residual is False) + sse : (R X C) :obj:`numpy.ndarray` + The sum of square error for the model (only if output_residual is False) + df : (R X C) :obj:`numpy.ndarray` + The degrees of freeom for the model (only if output_residual is False) + (timepoints - number of regressors) + """ + betas, _, _, _ = linalg.lstsq(x, y) + # matrix-multiplication on the regressors with the betas -> to create a new 'estimated' + # component matrix = fitted regressors (least squares beta solution * regressors) + fitted_regressors = np.matmul(x, betas) + residual = y - fitted_regressors + if output_residual: + return residual + else: + # sum the differences between the actual ICA components and the 'estimated' + # component matrix (beta-fitted regressors) + sse = np.sum(np.square(residual), axis=0) + # calculate how many individual values [timepoints] are free to vary after + # the least-squares solution [beta] betw X & Y is calculated + df = y.shape[0] - betas.shape[0] + return betas, sse, df From 57e72a0af2851bc5785cc56a4f9cc9ef47938128 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Thu, 7 Mar 2024 16:36:22 -0500 Subject: [PATCH 45/81] removed and cleaned up external_regressors_config option --- tedana/resources/config/metrics.json | 5 ----- ...demo_minimal_externalregressors_Fstat.json | 2 +- tedana/selection/component_selector.py | 14 ++++++------- tedana/tests/test_integration.py | 2 -- tedana/workflows/tedana.py | 20 +++++-------------- 5 files changed, 13 insertions(+), 30 deletions(-) diff --git a/tedana/resources/config/metrics.json b/tedana/resources/config/metrics.json index 2d1c6bc3d..038578e2e 100644 --- a/tedana/resources/config/metrics.json +++ b/tedana/resources/config/metrics.json @@ -115,11 +115,6 @@ "mask", "ref_img", "tes" - ], - "external fit": [ - "mixing", - "external_regressors", - "external_regressor_config" ] } } diff --git a/tedana/resources/decision_trees/demo_minimal_externalregressors_Fstat.json b/tedana/resources/decision_trees/demo_minimal_externalregressors_Fstat.json index a125a0b7f..6aa7e8981 100644 --- a/tedana/resources/decision_trees/demo_minimal_externalregressors_Fstat.json +++ b/tedana/resources/decision_trees/demo_minimal_externalregressors_Fstat.json @@ -23,7 +23,7 @@ "Unlikely BOLD", "Low variance" ], - "external_regressors": { + "external_regressor_config": { "regess_ID": "Fmodel", "info": "Fits all external regressors to a single model using an F statistic", "report": "Unspecified external nuissance regressors were fit to components using a linear model were rejected if they fit.", diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 27d51c5ee..20e86e82e 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -94,16 +94,16 @@ def validate_tree(tree): "intermediate_classifications", "classification_tags", "nodes", - "external_regressors", + "external_regressor_config", ] tree_optional_keys = ["generated_metrics"] defaults = {"selector", "decision_node_idx"} default_classifications = {"nochange", "accepted", "rejected", "unclassified"} default_decide_comps = {"all", "accepted", "rejected", "unclassified"} - # If a tree doesn't include "external_regressors", instead of crashing, set to None - if "external_regressors" not in set(tree.keys()): - tree["external_regressors"] = None + # If a tree doesn't include "external_regressor_config", instead of crashing, set to None + if "external_regressor_config" not in set(tree.keys()): + tree["external_regressor_config"] = None # Confirm that the required fields exist missing_keys = set(tree_expected_keys) - set(tree.keys()) @@ -219,9 +219,9 @@ def validate_tree(tree): "tag that was not predefined" ) - # If there is an external_regressors field, validate it - if tree["external_regressors"] is not None: - external_regressor_config = tree["external_regressors"] + # If there is an external_regressor_config field, validate it + if tree["external_regressor_config"] is not None: + external_regressor_config = tree["external_regressor_config"] # Define the fields that should always be present dict_expected_keys = set(["regess_ID", "info", "detrend", "calc_stats"]) diff --git a/tedana/tests/test_integration.py b/tedana/tests/test_integration.py index ba98e03f9..d68741650 100644 --- a/tedana/tests/test_integration.py +++ b/tedana/tests/test_integration.py @@ -416,8 +416,6 @@ def test_integration_three_echo_external_regressors_fstat(skip_integration): external_regressors=resource_filename( "tedana", "tests/data/external_regress_Ftest_3echo.tsv" ), - # external_regressor_config="Mot12_CSF", - external_regressor_config="Fmodel", low_mem=True, tedpca="aic", ) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 738561b2c..1440d89c8 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -176,15 +176,6 @@ def _get_parser(): ), default=None, ) - optional.add_argument( - "--external_labels", - dest="external_regressor_config", - help=( - "Column labels for external regressors & statistical tests to compare be used in the " - "decision tree. Either the path and name of a json file or a packaged tree (Mot12_CSF)" - ), - default=None, - ) optional.add_argument( "--seed", dest="fixed_seed", @@ -345,7 +336,6 @@ def tedana_workflow( combmode="t2s", tree="kundu", external_regressors=None, - external_regressor_config=None, tedpca="aic", fixed_seed=42, maxit=500, @@ -537,11 +527,11 @@ def tedana_workflow( # Decided to do the validation here so that, if there are issues, an error # will be raised before PCA/ICA if ( - "external_regressors" in set(selector.tree.keys()) - and selector.tree["external_regressors"] is not None + "external_regressor_config" in set(selector.tree.keys()) + and selector.tree["external_regressor_config"] is not None ): external_regressors = metrics.external.load_validate_external_regressors( - external_regressors, selector.tree["external_regressors"], catd.shape[2] + external_regressors, selector.tree["external_regressor_config"], catd.shape[2] ) io_generator = io.OutputGenerator( @@ -726,7 +716,7 @@ def tedana_workflow( label="ICA", metrics=necessary_metrics, external_regressors=external_regressors, - external_regressor_config=selector.tree["external_regressors"], + external_regressor_config=selector.tree["external_regressor_config"], ) LGR.info("Selecting components from ICA results") selector = selection.automatic_selection( @@ -774,7 +764,7 @@ def tedana_workflow( label="ICA", metrics=necessary_metrics, external_regressors=external_regressors, - external_regressor_config=external_regressor_config, + external_regressor_config=selector.tree["external_regressor_config"], ) selector = selection.automatic_selection( comptable, From dfdf47eb764d854141fea9e0af28e66425a00407 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Mon, 11 Mar 2024 17:58:49 -0400 Subject: [PATCH 46/81] Added task regressors and some tests. Now alll in decision tree --- tedana/metrics/_utils.py | 7 +- tedana/metrics/external.py | 97 ++++-- ...xternal_regressors_motion_task_models.json | 284 ++++++++++++++++++ ...mal_external_regressors_single_model.json} | 2 +- .../data/external_regress_Ftest_3echo.csv | 76 ----- .../data/external_regress_Ftest_3echo.tsv | 152 +++++----- .../data/external_regress_corr_3echo.tsv | 76 ----- tedana/tests/test_external_metrics.py | 188 ++++++++++++ tedana/tests/test_integration.py | 53 +++- tedana/workflows/tedana.py | 6 +- 10 files changed, 680 insertions(+), 261 deletions(-) create mode 100644 tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json rename tedana/resources/decision_trees/{demo_minimal_externalregressors_Fstat.json => demo_minimal_external_regressors_single_model.json} (99%) delete mode 100644 tedana/tests/data/external_regress_Ftest_3echo.csv delete mode 100644 tedana/tests/data/external_regress_corr_3echo.tsv create mode 100644 tedana/tests/test_external_metrics.py diff --git a/tedana/metrics/_utils.py b/tedana/metrics/_utils.py index 70eaec7ba..d0b82f0e6 100644 --- a/tedana/metrics/_utils.py +++ b/tedana/metrics/_utils.py @@ -18,10 +18,11 @@ def add_external_dependencies(dependency_config, external_regressor_config): dependency_config["inputs"].append("external regressors") if external_regressor_config["calc_stats"].lower() == "f": + model_names = ["Full"] if "f_stats_partial_models" in set(external_regressor_config.keys()): - model_names = external_regressor_config["f_stats_partial_models"].append("Full") - else: - model_names = ["Full"] + model_names.append(external_regressor_config["f_stats_partial_models"]) + if "task_keep" in set(external_regressor_config.keys()): + model_names.append("task_keep") for model_name in model_names: for stat_type in ["Fstat", "R2stat", "pval"]: dependency_config["dependencies"][f"{stat_type} {model_name} Model"] = [ diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index b3ada8f4d..f47a23ba1 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -1,6 +1,7 @@ """Metrics unrelated to TE-(in)dependence.""" import logging +import re import numpy as np import pandas as pd @@ -29,7 +30,7 @@ def load_validate_external_regressors(external_regressors, external_regressor_co external_regressors: :obj:`str` Path and name of tsv file that includes external regressor time series external_regressor_config: :obj:`dict` - A validated dictionary with info for fitting external regressors + A dictionary with info for fitting external regressors to component time series n_time: :obj:`int` Number of timepoints in the fMRI time series @@ -39,15 +40,22 @@ def load_validate_external_regressors(external_regressors, external_regressor_co external_regressors: :obj:`pandas.DataFrame` Each column is a labelled regressor and the number of rows should match the number of timepoints in the fMRI time series + external_regressor_config: :obj:`dict` + A validated dictionary with info for fitting external regressors + to component time series. If regex patterns like '^mot_.*$' are used to define + regressor names, this is replaced with a list of the match column names used in + external_regressors """ try: external_regressors = pd.read_table(external_regressors) except FileNotFoundError: raise ValueError(f"Cannot load tsv file with external regressors: {external_regressors}") - validate_extern_regress(external_regressors, external_regressor_config, n_time) + external_regressor_config = validate_extern_regress( + external_regressors, external_regressor_config, n_time + ) - return external_regressors + return external_regressors, external_regressor_config def validate_extern_regress(external_regressors, external_regressor_config, n_time): @@ -71,6 +79,14 @@ def validate_extern_regress(external_regressors, external_regressor_config, n_ti n_time : :obj:`int` The number of time point in the fMRI time series + Returns + ------- + external_regressor_config: :obj:`dict` + A validated dictionary with info for fitting external regressors + to component time series. If regex patterns like '^mot_.*$' are used to define + regressor names, this is replaced with a list of the match column names used in + external_regressors + Raises ------ RegressorError if any validation test fails @@ -84,21 +100,51 @@ def validate_extern_regress(external_regressors, external_regressor_config, n_ti # with the data in external_regressors # Currently column labels only need to be predefined for calc_stats==F + # or if "task_keep" is used. column_label_specifications will include the + # names of the column labels for both f_stats_partial_models and task_keep + # if eithe ror both are defined. + column_label_specifications = set() if "f_stats_partial_models" in set(external_regressor_config.keys()): + column_label_specifications.update( + set(external_regressor_config["f_stats_partial_models"]) + ) + if "task_keep" in set(external_regressor_config.keys()): + column_label_specifications.update(["task_keep"]) + + if column_label_specifications: regressor_names = set(external_regressors.columns) expected_regressor_names = set() - for partial_models in external_regressor_config["f_stats_partial_models"]: - tmp_names = set(external_regressor_config[partial_models]) - if expected_regressor_names - tmp_names: + # for each column label, check if the label matches column names in external_regressors + for partial_models in column_label_specifications: + tmp_partial_model_names = set() + for tmp_name in external_regressor_config[partial_models]: + # If a label starts with ^ treat match the pattern to column names using regex + if tmp_name.startswith("^"): + tmp_replacements = [ + reg_name + for reg_name in regressor_names + if re.match(tmp_name, reg_name.lower()) + ] + if not tmp_replacements: + err_msg += ( + f"No external regressor labels matching regex '{tmp_name}' found." + ) + tmp_partial_model_names.update(set(tmp_replacements)) + else: + if not isinstance(tmp_name, list): + tmp_name = [tmp_name] + tmp_partial_model_names.update(tmp_name) + external_regressor_config[partial_models] = list(tmp_partial_model_names) + if expected_regressor_names.intersection(tmp_partial_model_names): LGR.warning( "External regressors used in more than one partial model: " - f"{expected_regressor_names - tmp_names}" + f"{expected_regressor_names.intersection(tmp_partial_model_names)}" ) - expected_regressor_names.update(tmp_names) + expected_regressor_names.update(tmp_partial_model_names) if expected_regressor_names - regressor_names: err_msg += ( "Inputed regressors in external_regressors do not include all expected " - "regressors in partial models\n" + "regressors in partial models or task\n" "Expected regressors not in inputted regressors: " f"{expected_regressor_names - regressor_names}\n" f"Inputted Regressors: {regressor_names}" @@ -106,20 +152,23 @@ def validate_extern_regress(external_regressors, external_regressor_config, n_ti if regressor_names - expected_regressor_names: LGR.warning( "Regressor labels in external_regressors are not all included in F " - "statistic partial models. Whether or not a regressor is in a partial " - "model, it will be included in the full F statistic model" - "Regressors not incldued in any partial model: " + "statistic partial models or task models. Regressor not in a partial " + "model or task_keep, it will be included in the full F statistic model " + "and treated like nuisance regressors to remove. " + "Regressors not included in any partial model: " f"{regressor_names - expected_regressor_names}" ) - if len(external_regressors.index) != n_time: - err_msg += ( - f"External Regressors have len(external_regressors.index) timepoints\n" - f"while fMRI data have {n_time} timepoints" - ) + if len(external_regressors.index) != n_time: + err_msg += ( + f"External Regressors have {len(external_regressors.index)} timepoints\n" + f"while fMRI data have {n_time} timepoints" + ) + + if err_msg: + raise RegressError(err_msg) - if err_msg: - raise RegressError(err_msg) + return external_regressor_config def fit_regressors(comptable, external_regressors, external_regressor_config, mixing): @@ -311,11 +360,13 @@ def fit_mixing_to_regressors( r2_vals = pd.DataFrame(data=r2_vals_tmp, columns=["R2stat Full Model"]) # Run a separate model if there are task regressors that might want to be kept - # TODO Need to edit this function so regressor_models can use something besides the full model if "task keep" in regressor_models.keys(): betas_task_keep, f_vals_tmp, p_vals_tmp, r2_vals_tmp = fit_model_with_stats( y=mixing, regressor_models=regressor_models, base_label="base", full_label="task keep" ) + f_vals["Fstat Task Model"] = f_vals_tmp + p_vals["pval Task Model"] = p_vals_tmp + r2_vals["R2stat Task Model"] = r2_vals_tmp # Test the fits between the full model and the full model excluding one category of regressor if "f_stats_partial_models" in external_regressor_config.keys(): @@ -376,8 +427,10 @@ def build_fstat_regressor_models( regressor_models = {"base": detrend_regressors_arr} LGR.info(f"Size for base Regressor Model: {regressor_models['base'].shape}") - if "task_keep_model" in external_regressor_config: - task_keep_model = external_regressor_config["task_keep_model"] + if "task_keep" in external_regressor_config: + task_keep_model = external_regressor_config["task_keep"] + # If there is a task_keep model, then the full model should exclude every + # regressor in task_keep tmp_model_labels = {"full": set(regressor_labels) - set(task_keep_model)} tmp_model_labels["task keep"] = set(task_keep_model) for model_name in ["full", "task keep"]: diff --git a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json new file mode 100644 index 000000000..8a38717db --- /dev/null +++ b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json @@ -0,0 +1,284 @@ +{ + "tree_id": "demo_minimal_external_regressors_motion_task_models", + "info": "Demonstration based on the minimal decision tree that uses partial F stats on a model with multiple external regressors divided by category and task regressors to bias towards keeping.", + "report": "This is based on the minimal criteria of the original MEICA decision tree \\citep{kundu2013integrated} without the more aggressive noise removal steps \\citep{dupre2021te}.", + "necessary_metrics": [ + "kappa", + "rho", + "countsigFS0", + "countsigFT2", + "dice_FS0", + "dice_FT2", + "signal-noise_t", + "variance explained", + "pval Full Model", + "R2stat Full Model" + ], + "intermediate_classifications": ["provisionalaccept", "provisionalreject"], + "classification_tags": ["Likely BOLD", "Unlikely BOLD", "Low variance"], + "external_regressor_config": { + "regess_ID": "Fmodel", + "info": "Fits all external regressors to a single model using an F statistic", + "report": "Unspecified external nuissance regressors were fit to components using a linear model were rejected if they fit.", + "detrend": true, + "calc_stats": "F", + "f_stats_partial_models": ["Motion", "CSF"], + "Motion": ["^mot_.*$"], + "CSF": ["^csf.*$"], + "task_keep": ["^task.*$"], + }, + "nodes": [ + { + "functionname": "manual_classify", + "parameters": {"new_classification": "unclassified", "decide_comps": "all"}, + "kwargs": { + "log_extra_report": "", + "clear_classification_tags": true, + "dont_warn_reclassify": true, + }, + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "rho", + "right": "kappa", + }, + "kwargs": {"log_extra_report": "", "tag_if_true": "Unlikely BOLD"}, + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "countsigFS0", + "right": "countsigFT2", + }, + "kwargs": { + "left2": "countsigFT2", + "op2": ">", + "right2": 0, + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD", + }, + }, + { + "functionname": "calc_median", + "parameters": { + "decide_comps": "all", + "metric_name": "variance explained", + "median_label": "varex", + }, + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "dice_FS0", + "right": "dice_FT2", + }, + "kwargs": { + "left2": "variance explained", + "op2": ">", + "right2": "median_varex", + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD", + }, + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": 0, + "right": "signal-noise_t", + }, + "kwargs": { + "left2": "variance explained", + "op2": ">", + "right2": "median_varex", + "log_extra_report": "", + "tag_if_true": "Unlikely BOLD", + }, + }, + { + "functionname": "calc_kappa_elbow", + "parameters": {"decide_comps": "all"}, + "kwargs": {"log_extra_info": "", "log_extra_report": ""}, + "_comment": "", + }, + { + "functionname": "calc_rho_elbow", + "parameters": {"decide_comps": "all"}, + "kwargs": { + "subset_decide_comps": "unclassified", + "rho_elbow_type": "liberal", + "log_extra_info": "", + "log_extra_report": "", + }, + "_comment": "", + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "provisionalaccept", + "if_false": "provisionalreject", + "decide_comps": "unclassified", + "op": ">=", + "left": "kappa", + "right": "kappa_elbow_kundu", + }, + "kwargs": {"log_extra_report": ""}, + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "accepted", + "if_false": "nochange", + "decide_comps": "provisionalaccept", + "op": ">", + "left": "kappa", + "right": "rho", + }, + "kwargs": { + "log_extra_info": "If kappa>elbow and kappa>2*rho accept even if rho>elbow", + "log_extra_report": "", + "right_scale": 2, + "tag_if_true": "Likely BOLD", + }, + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "provisionalreject", + "if_false": "nochange", + "decide_comps": ["provisionalreject", "provisionalaccept"], + "op": ">", + "left": "rho", + "right": "rho_elbow_liberal", + }, + "kwargs": {"tag_if_true": "Unlikely BOLD"}, + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "provisionalreject", + "if_false": "nochange", + "decide_comps": "all", + "op": "<", + "left": "pval Full Model", + "right": 0.05, + }, + "kwargs": { + "op2": ">", + "left2": "R2stat Full Model", + "right2": 0.5, + "log_extra_info": "If external regressors fit with p<0.05 and model R2>0.5 of the variance, then reject.", + "log_extra_report": "", + "tag_if_true": "external regressors", + }, + "_comment": "Provisionally rejecting components that fit to the external regressor noise model", + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "nochange", + "if_false": "nochange", + "decide_comps": "all", + "op": "<", + "left": "pval Full Model", + "right": 0.05, + }, + "kwargs": { + "op2": ">", + "left2": "R2stat Full Model", + "right2": 0.5, + "op3": "<", + "left3": "pval Motion Model", + "right3": 0.05, + "tag_if_true": "Fits motion external regressors", + }, + "_comment": "Identical to the one above, & not changing classifications, but tagging if fits to motion regressors", + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "nochange", + "if_false": "nochange", + "decide_comps": "all", + "op": "<", + "left": "pval Full Model", + "right": 0.05, + }, + "kwargs": { + "op2": ">", + "left2": "R2stat Full Model", + "right2": 0.5, + "op3": "<", + "left3": "pval CSF Model", + "right3": 0.05, + "tag_if_true": "Fits CSF external regressors", + }, + "_comment": "Identical to the one above, & not changing classifications, but tagging if fits to CSF regressors", + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "accept", + "if_false": "nochange", + "decide_comps": ["provisionalreject", "rejected"], + "op": "<", + "left": "pval Task Model", + "right": 0.05, + }, + "kwargs": { + "op2": ">", + "left2": "R2stat Task Model", + "right2": 0.5, + "op3": ">=", + "left3": "kappa", + "right3": "kappa_elbow_kundu", + "tag_if_true": "Fits task", + }, + "_comment": "Keep if it fits task regressors and contains T2* signal, as defined by kappa>elbow", + }, + { + "functionname": "dec_variance_lessthan_thresholds", + "parameters": { + "if_true": "accepted", + "if_false": "nochange", + "decide_comps": "provisionalreject", + }, + "kwargs": { + "var_metric": "variance explained", + "log_extra_info": "", + "log_extra_report": "", + "single_comp_threshold": 0.1, + "all_comp_threshold": 1.0, + "tag_if_true": "Low variance", + }, + }, + { + "functionname": "manual_classify", + "parameters": {"new_classification": "accepted", "decide_comps": "provisionalaccept"}, + "kwargs": {"log_extra_info": "", "log_extra_report": "", "tag": "Likely BOLD"}, + }, + { + "functionname": "manual_classify", + "parameters": { + "new_classification": "rejected", + "decide_comps": ["provisionalreject", "unclassified"], + }, + }, + ], +} diff --git a/tedana/resources/decision_trees/demo_minimal_externalregressors_Fstat.json b/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json similarity index 99% rename from tedana/resources/decision_trees/demo_minimal_externalregressors_Fstat.json rename to tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json index 6aa7e8981..3d3c00586 100644 --- a/tedana/resources/decision_trees/demo_minimal_externalregressors_Fstat.json +++ b/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json @@ -1,5 +1,5 @@ { - "tree_id": "demo_minimal_externalregressors_Fstat", + "tree_id": "demo_minimal_external_regressors_single_model", "info": "Demonstration based on the minimal decision tree that uses an F stat on a model with multiple external regressors.", "report": "This is based on the minimal criteria of the original MEICA decision tree \\citep{kundu2013integrated} without the more aggressive noise removal steps \\citep{dupre2021te}.", "necessary_metrics": [ diff --git a/tedana/tests/data/external_regress_Ftest_3echo.csv b/tedana/tests/data/external_regress_Ftest_3echo.csv deleted file mode 100644 index a0d504230..000000000 --- a/tedana/tests/data/external_regress_Ftest_3echo.csv +++ /dev/null @@ -1,76 +0,0 @@ -Mot_X,Mot_Y,Mot_Z,Mot_Pitch,Mot_Roll,Mot_Yaw,Mot_d1_X,Mot_d1_Y,Mot_d1_Z,Mot_d1_Pitch,Mot_d1_Roll,Mot_d1_Yaw,CSF --0.329580266,-0.844918633,1.626381228,-0.218889288,0.634672027,0.603137649,0,0,0,0,0,0,-0.718687205 --1.520650288,-2.13455262,-0.882532519,0.336875466,-1.592270822,-1.191402591,-1.191070022,-1.289633987,-2.508913747,0.555764753,-2.226942849,-1.794540239,-0.844111053 --0.511890744,-0.711229882,0.276263262,0.188691918,0.099964095,-0.995174865,1.008759544,1.423322738,1.158795781,-0.148183548,1.692234917,0.196227726,-0.921595994 --2.06545787,-0.40815265,1.038928803,0.226413424,-0.384996762,0.286311023,-1.553567126,0.303077232,0.762665541,0.037721506,-0.484960857,1.281485888,-0.638149754 --1.565465426,-0.289605838,0.551574072,0.307316972,-0.272939181,-0.92834738,0.499992444,0.118546812,-0.487354731,0.080903548,0.112057581,-1.214658403,1.141942235 --0.583901238,-0.45316118,0.882519392,-0.865762002,-0.537313024,0.517619453,0.981564188,-0.163555342,0.33094532,-1.173078975,-0.264373843,1.445966833,-1.385047823 -0.225980497,-1.627540572,-0.68542407,-0.700932399,-1.67754023,1.019518689,0.809881735,-1.174379392,-1.567943462,0.164829603,-1.140227206,0.501899236,-0.186120892 -0.560677621,-1.489408639,-0.595520306,0.685234401,-0.723554811,0.416786873,0.334697124,0.138131933,0.089903764,1.386166801,0.953985419,-0.602731816,-0.580953121 -0.003248201,-0.340159692,-0.879529088,0.602469892,-0.876641151,-0.884034813,-0.55742942,1.149248947,-0.284008782,-0.08276451,-0.15308634,-1.300821686,-1.692923237 -0.836161459,-1.092255501,-2.618392652,0.707002513,-0.763363892,0.276104352,0.832913258,-0.752095809,-1.738863564,0.104532622,0.113277259,1.160139164,-0.371763879 -1.586566953,0.271349643,-0.649296386,-0.881991034,-0.310818575,0.552978122,0.750405494,1.363605144,1.969096266,-1.588993548,0.452545317,0.276873771,0.933331204 -0.667602572,1.549333738,0.076243832,0.833106558,0.518018373,-0.620225079,-0.918964381,1.277984095,0.725540218,1.715097593,0.828836948,-1.173203202,-0.852116981 --0.01792122,2.740600234,0.074586135,1.164085584,-0.240669175,-0.440358808,-0.685523792,1.191266496,-0.001657697,0.330979026,-0.758687548,0.179866271,-0.092616197 --0.659019247,2.298595672,0.17634643,-0.101416476,-0.182581812,2.015890229,-0.641098027,-0.442004562,0.101760295,-1.26550206,0.058087363,2.456249037,1.414856771 --1.094112767,-0.186225918,-2.127567341,-0.660534996,0.727968953,0.549065179,-0.43509352,-2.48482159,-2.303913771,-0.55911852,0.910550765,-1.46682505,-1.264667218 -0.248649215,-0.618039127,-1.082778413,-2.323754365,-0.275433162,-0.224507966,1.342761982,-0.431813209,1.044788928,-1.66321937,-1.003402116,-0.773573145,0.297574192 --0.034109838,0.196157196,0.285049845,0.929700246,-1.903128963,-1.640111364,-0.282759053,0.814196323,1.367828258,3.253454611,-1.627695801,-1.415603398,-0.749200249 --0.154408808,0.946079652,1.025632299,-1.504803984,1.86795795,0.461493517,-0.12029897,0.749922456,0.740582454,-2.43450423,3.771086913,2.101604881,0.024314549 -0.764863858,0.933908359,-0.135353281,-0.109277214,0.002639137,-1.643878764,0.919272666,-0.012171293,-1.16098558,1.39552677,-1.865318813,-2.105372281,-1.143075439 -0.445796508,1.917424429,0.650347848,1.98385538,0.655358612,1.874592447,-0.31906735,0.98351607,0.785701129,2.093132594,0.652719475,3.51847121,-1.758526555 -0.207513285,1.298735387,2.318083224,-0.535817525,2.16070642,-0.721054976,-0.238283223,-0.618689042,1.667735376,-2.519672905,1.505347808,-2.595647423,0.666876476 -0.381782634,0.081083807,-1.984367893,0.563118012,0.313116397,-0.168924986,0.174269349,-1.21765158,-4.302451117,1.098935537,-1.847590024,0.55212999,0.145183641 --1.47473253,0.515676307,1.024256498,-0.163074269,0.924446522,-0.68895112,-1.856515164,0.4345925,3.008624391,-0.726192282,0.611330125,-0.520026134,-0.051313681 --1.494898436,0.766171566,1.630635233,1.316319173,0.756997088,-0.541944145,-0.020165906,0.250495259,0.606378735,1.479393442,-0.167449434,0.147006975,0.176664517 --2.019896907,1.643560771,-0.311565305,-1.536321826,0.680356602,0.758927506,-0.524998471,0.877389205,-1.942200538,-2.852640999,-0.076640486,1.300871651,-1.264980299 --2.178716752,0.973512277,2.252419221,-0.98610174,-1.462675416,1.890594069,-0.158819845,-0.670048494,2.563984526,0.550220085,-2.143032018,1.131666563,-1.113211169 --1.232037427,-0.40191649,1.00829017,1.358255513,0.106048498,-0.129832743,0.946679325,-1.375428767,-1.244129051,2.344357253,1.568723914,-2.020426812,0.154120413 -0.731522293,0.40813286,1.025669565,0.164802434,1.923608997,1.773241326,1.96355972,0.81004935,0.017379395,-1.193453079,1.817560499,1.903074069,-1.34580855 -0.753719194,0.163556595,-1.064627798,-0.934732068,0.685816646,0.525679007,0.022196901,-0.244576265,-2.090297363,-1.099534501,-1.237792351,-1.247562319,0.192821615 -1.757683518,-0.537743502,-0.866195997,1.491399948,-0.345322156,0.446364892,1.003964324,-0.701300097,0.198431801,2.426132015,-1.031138802,-0.079314115,1.960430091 -1.550719352,0.11832591,-0.668387479,0.223298561,-1.812651721,0.058823033,-0.206964166,0.656069412,0.197808518,-1.268101387,-1.467329565,-0.387541859,-0.600970527 -2.288307277,0.791999772,0.75152785,0.068312984,1.468953207,-0.859155411,0.737587925,0.673673862,1.419915329,-0.154985576,3.281604929,-0.917978445,-0.296907317 -2.663681819,2.261180081,1.905593875,-1.140429944,-1.722043668,0.364115037,0.375374542,1.469180309,1.154066025,-1.208742929,-3.190996875,1.223270448,-0.862172265 -2.268142799,0.717724473,-2.382603885,0.436210872,0.218979462,0.089319574,-0.39553902,-1.543455608,-4.28819776,1.576640816,1.94102313,-0.274795463,0.906547155 --0.218049277,-0.508412721,-2.487404957,1.887320081,1.04722817,-0.395861337,-2.486192076,-1.226137194,-0.104801072,1.45110921,0.828248708,-0.485180912,-0.451624542 --0.410793483,0.116533685,1.879092728,1.184222196,-0.031140809,-0.90091931,-0.192744206,0.624946406,4.366497685,-0.703097886,-1.078368979,-0.505057973,0.118224392 --0.647691228,0.838129685,1.374778227,0.322847522,1.175235974,1.277024175,-0.236897745,0.721596,-0.504314501,-0.861374674,1.206376783,2.177943485,-0.449494687 --1.13985429,-0.495903004,1.056569633,-1.587672129,0.39860901,-0.768585548,-0.492163062,-1.334032689,-0.318208594,-1.910519651,-0.776626964,-2.045609723,-0.222016948 -0.53999669,-0.487881558,0.581258434,-1.085920121,-0.501406499,0.115378063,1.67985098,0.008021446,-0.475311199,0.501752008,-0.900015509,0.883963611,-0.703432192 -0.410905902,0.873257327,3.608291808,0.653652794,-1.54826995,1.468348054,-0.129090788,1.361138885,3.027033374,1.739572915,-1.04686345,1.352969991,0.299902147 --0.173062544,0.529483977,1.076381362,-0.503373399,-0.058733834,-1.45590019,-0.583968446,-0.34377335,-2.531910446,-1.157026193,1.489536116,-2.924248244,0.005305683 -0.590240338,-0.560105813,-0.33143028,-0.14587712,1.474596747,1.017308667,0.763302882,-1.08958979,-1.407811642,0.35749628,1.533330581,2.473208857,-1.883857873 --0.330152172,-0.29496616,-1.27480681,0.993753616,0.247092802,-0.980747759,-0.92039251,0.265139653,-0.94337653,1.139630735,-1.227503945,-1.998056427,-1.361923392 --0.770820846,-1.130215098,-0.533069878,1.339112763,1.354475058,-0.663609074,-0.440668674,-0.835248938,0.741736932,0.345359147,1.107382257,0.317138685,-0.854950385 --0.997000751,-1.594080868,-2.206716323,0.551420017,-0.360200976,0.897818757,-0.226179905,-0.46386577,-1.673646445,-0.787692746,-1.714676034,1.561427831,-0.509180861 --0.078723051,-1.034723379,1.715724783,2.451708059,0.070115822,-0.667778397,0.9182777,0.559357489,3.922441106,1.900288042,0.430316798,-1.565597154,0.134291091 -0.317741611,-1.449732445,-0.829499847,1.984395673,1.236600623,-0.310344732,0.396464662,-0.415009066,-2.54522463,-0.467312386,1.166484801,0.357433665,0.330435696 -0.920629978,-3.107612041,-1.72016277,-1.6235986,-0.680428817,-0.15105238,0.602888367,-1.657879596,-0.890662923,-3.607994273,-1.917029441,0.159292352,0.21447828 -0.904171949,-3.25810681,-1.338419095,-0.225635225,0.492152931,-1.184901646,-0.016458029,-0.150494769,0.381743675,1.397963375,1.172581749,-1.033849266,0.467948374 -0.712432166,-2.702026568,-4.090712476,0.325379006,-1.008512619,0.155445809,-0.191739783,0.556080242,-2.752293381,0.551014231,-1.50066555,1.340347455,-0.409919798 -1.213580415,-2.416744199,-1.644462247,0.887889538,-1.019416802,-0.194144049,0.501148249,0.285282369,2.446250229,0.562510532,-0.010904183,-0.349589858,-0.954443807 -1.015276471,-0.468506664,0.826022926,-0.704628983,-0.831670525,0.350992643,-0.198303944,1.948237535,2.470485173,-1.592518521,0.187746276,0.545136692,-1.28584816 -0.458644239,0.304782879,-0.286629392,-0.622614244,0.81723357,-0.620482414,-0.556632232,0.773289543,-1.112652318,0.082014739,1.648904096,-0.971475057,-1.250751609 -0.696387231,0.478940055,-0.661704616,-0.225347907,-1.187184555,-0.81881664,0.237742992,0.174157176,-0.375075224,0.397266338,-2.004418126,-0.198334226,-0.58673588 -0.398048005,0.35203805,-0.836607472,-1.114036436,-1.728872921,-0.244092905,-0.298339226,-0.126902005,-0.174902856,-0.888688529,-0.541688366,0.574723735,-0.251724316 --0.433479435,0.130772773,-0.702912597,-0.711315522,0.831520689,-1.273732568,-0.83152744,-0.221265277,0.133694875,0.402720914,2.56039361,-1.029639663,2.040725153 --1.504705013,-0.117524037,-1.172268361,-1.301597961,-1.43149785,-0.320764583,-1.071225578,-0.24829681,-0.469355764,-0.590282439,-2.263018539,0.952967985,-0.258655658 --1.966296873,-1.284318477,-0.647627254,0.187287319,-1.026578293,0.466508325,-0.46159186,-1.16679444,0.524641107,1.48888528,0.404919557,0.787272908,2.114290456 --1.036674723,-0.437122887,0.788299251,1.208653611,1.589038974,1.098106245,0.92962215,0.84719559,1.435926505,1.021366292,2.615617267,0.63159792,-0.93052932 --0.696570694,1.035997759,0.315168026,-0.180901513,-0.784289145,0.252979402,0.340104029,1.473120646,-0.473131225,-1.389555124,-2.373328119,-0.845126843,0.177085093 --1.056655551,2.054949636,1.733679189,-1.593982301,-2.245797478,0.326044934,-0.360084857,1.018951877,1.418511163,-1.413080788,-1.461508333,0.073065532,-1.242278489 --1.253872449,0.940519962,-1.859117584,-0.393650127,-0.324318496,0.436127281,-0.197216898,-1.114429674,-3.592796773,1.200332173,1.921478982,0.110082347,0.278345459 --1.069287813,-0.5172584,-0.656338228,0.143574863,-1.262613776,1.600586235,0.184584636,-1.457778362,1.202779356,0.537224991,-0.93829528,1.164458954,-1.920302265 -0.073889742,1.015905744,3.017531937,-0.558421237,0.29254061,-0.615549542,1.143177555,1.533164144,3.673870165,-0.7019961,1.555154386,-2.216135777,0.003952104 -1.32888943,2.850501828,2.785235104,-0.929137834,-0.021765497,-0.581322431,1.254999688,1.834596084,-0.232296833,-0.370716597,-0.314306107,0.034227111,-0.854938554 -0.831392436,2.255038285,0.082410724,0.365767705,0.860518504,-0.916585465,-0.497496994,-0.595463543,-2.70282438,1.294905539,0.882284001,-0.335263034,-0.264536802 --0.670035292,0.826737793,0.321644476,1.449735904,-0.344614203,-1.344199808,-1.501427728,-1.428300492,0.239233752,1.083968199,-1.205132707,-0.427614343,-0.829211295 --0.478355817,0.188711887,-0.630649751,-1.102278018,-1.374564234,0.697153667,0.191679475,-0.638025906,-0.952294227,-2.552013922,-1.029950031,2.041353475,-1.752282527 -0.999877364,-1.06986647,1.290972976,-0.92101604,0.906070467,0.416570393,1.478233181,-1.258578357,1.921622727,0.181261978,2.280634701,-0.280583274,0.64858987 -0.069018404,0.516767176,1.573029961,0.362136079,-1.202013587,-0.428893561,-0.93085896,1.586633646,0.282056985,1.283152119,-2.108084054,-0.845463954,-0.423093312 -0.351758996,0.056295399,1.665144379,-0.415044976,0.482451076,-1.053100619,0.282740592,-0.460471777,0.092114418,-0.777181055,1.684464663,-0.624207058,0.664860241 -0.858424569,-0.363527343,1.550740147,0.917563357,-0.68983397,-1.261316549,0.506665573,-0.419822742,-0.114404232,1.332608333,-1.172285046,-0.20821593,-0.097170847 -1.334779099,-0.553036015,0.026277298,-1.303973749,0.14799305,-1.047775225,0.47635453,-0.189508672,-1.524462849,-2.221537106,0.83782702,0.213541323,-0.273332909 -0.365467586,-1.460953257,1.836000417,0.076497972,-1.230053674,-0.597101686,-0.969311513,-0.907917242,1.809723119,1.38047172,-1.378046723,0.450673539,-0.026346724 --0.084225355,-0.90165276,0.41416722,1.144524716,0.509152904,-1.158340165,-0.449692941,0.559300497,-1.421833197,1.068026744,1.739206578,-0.561238479,-1.19547545 diff --git a/tedana/tests/data/external_regress_Ftest_3echo.tsv b/tedana/tests/data/external_regress_Ftest_3echo.tsv index ba9073eed..296476b3d 100644 --- a/tedana/tests/data/external_regress_Ftest_3echo.tsv +++ b/tedana/tests/data/external_regress_Ftest_3echo.tsv @@ -1,76 +1,76 @@ -Mot_X Mot_Y Mot_Z Mot_Pitch Mot_Roll Mot_Yaw Mot_d1_X Mot_d1_Y Mot_d1_Z Mot_d1_Pitch Mot_d1_Roll Mot_d1_Yaw CSF --0.329580266 -0.844918633 1.626381228 -0.218889288 0.634672027 0.603137649 0 0 0 0 0 0 -0.718687205 --1.520650288 -2.13455262 -0.882532519 0.336875466 -1.592270822 -1.191402591 -1.191070022 -1.289633987 -2.508913747 0.555764753 -2.226942849 -1.794540239 -0.844111053 --0.511890744 -0.711229882 0.276263262 0.188691918 0.099964095 -0.995174865 1.008759544 1.423322738 1.158795781 -0.148183548 1.692234917 0.196227726 -0.921595994 --2.06545787 -0.40815265 1.038928803 0.226413424 -0.384996762 0.286311023 -1.553567126 0.303077232 0.762665541 0.037721506 -0.484960857 1.281485888 -0.638149754 --1.565465426 -0.289605838 0.551574072 0.307316972 -0.272939181 -0.92834738 0.499992444 0.118546812 -0.487354731 0.080903548 0.112057581 -1.214658403 1.141942235 --0.583901238 -0.45316118 0.882519392 -0.865762002 -0.537313024 0.517619453 0.981564188 -0.163555342 0.33094532 -1.173078975 -0.264373843 1.445966833 -1.385047823 -0.225980497 -1.627540572 -0.68542407 -0.700932399 -1.67754023 1.019518689 0.809881735 -1.174379392 -1.567943462 0.164829603 -1.140227206 0.501899236 -0.186120892 -0.560677621 -1.489408639 -0.595520306 0.685234401 -0.723554811 0.416786873 0.334697124 0.138131933 0.089903764 1.386166801 0.953985419 -0.602731816 -0.580953121 -0.003248201 -0.340159692 -0.879529088 0.602469892 -0.876641151 -0.884034813 -0.55742942 1.149248947 -0.284008782 -0.08276451 -0.15308634 -1.300821686 -1.692923237 -0.836161459 -1.092255501 -2.618392652 0.707002513 -0.763363892 0.276104352 0.832913258 -0.752095809 -1.738863564 0.104532622 0.113277259 1.160139164 -0.371763879 -1.586566953 0.271349643 -0.649296386 -0.881991034 -0.310818575 0.552978122 0.750405494 1.363605144 1.969096266 -1.588993548 0.452545317 0.276873771 0.933331204 -0.667602572 1.549333738 0.076243832 0.833106558 0.518018373 -0.620225079 -0.918964381 1.277984095 0.725540218 1.715097593 0.828836948 -1.173203202 -0.852116981 --0.01792122 2.740600234 0.074586135 1.164085584 -0.240669175 -0.440358808 -0.685523792 1.191266496 -0.001657697 0.330979026 -0.758687548 0.179866271 -0.092616197 --0.659019247 2.298595672 0.17634643 -0.101416476 -0.182581812 2.015890229 -0.641098027 -0.442004562 0.101760295 -1.26550206 0.058087363 2.456249037 1.414856771 --1.094112767 -0.186225918 -2.127567341 -0.660534996 0.727968953 0.549065179 -0.43509352 -2.48482159 -2.303913771 -0.55911852 0.910550765 -1.46682505 -1.264667218 -0.248649215 -0.618039127 -1.082778413 -2.323754365 -0.275433162 -0.224507966 1.342761982 -0.431813209 1.044788928 -1.66321937 -1.003402116 -0.773573145 0.297574192 --0.034109838 0.196157196 0.285049845 0.929700246 -1.903128963 -1.640111364 -0.282759053 0.814196323 1.367828258 3.253454611 -1.627695801 -1.415603398 -0.749200249 --0.154408808 0.946079652 1.025632299 -1.504803984 1.86795795 0.461493517 -0.12029897 0.749922456 0.740582454 -2.43450423 3.771086913 2.101604881 0.024314549 -0.764863858 0.933908359 -0.135353281 -0.109277214 0.002639137 -1.643878764 0.919272666 -0.012171293 -1.16098558 1.39552677 -1.865318813 -2.105372281 -1.143075439 -0.445796508 1.917424429 0.650347848 1.98385538 0.655358612 1.874592447 -0.31906735 0.98351607 0.785701129 2.093132594 0.652719475 3.51847121 -1.758526555 -0.207513285 1.298735387 2.318083224 -0.535817525 2.16070642 -0.721054976 -0.238283223 -0.618689042 1.667735376 -2.519672905 1.505347808 -2.595647423 0.666876476 -0.381782634 0.081083807 -1.984367893 0.563118012 0.313116397 -0.168924986 0.174269349 -1.21765158 -4.302451117 1.098935537 -1.847590024 0.55212999 0.145183641 --1.47473253 0.515676307 1.024256498 -0.163074269 0.924446522 -0.68895112 -1.856515164 0.4345925 3.008624391 -0.726192282 0.611330125 -0.520026134 -0.051313681 --1.494898436 0.766171566 1.630635233 1.316319173 0.756997088 -0.541944145 -0.020165906 0.250495259 0.606378735 1.479393442 -0.167449434 0.147006975 0.176664517 --2.019896907 1.643560771 -0.311565305 -1.536321826 0.680356602 0.758927506 -0.524998471 0.877389205 -1.942200538 -2.852640999 -0.076640486 1.300871651 -1.264980299 --2.178716752 0.973512277 2.252419221 -0.98610174 -1.462675416 1.890594069 -0.158819845 -0.670048494 2.563984526 0.550220085 -2.143032018 1.131666563 -1.113211169 --1.232037427 -0.40191649 1.00829017 1.358255513 0.106048498 -0.129832743 0.946679325 -1.375428767 -1.244129051 2.344357253 1.568723914 -2.020426812 0.154120413 -0.731522293 0.40813286 1.025669565 0.164802434 1.923608997 1.773241326 1.96355972 0.81004935 0.017379395 -1.193453079 1.817560499 1.903074069 -1.34580855 -0.753719194 0.163556595 -1.064627798 -0.934732068 0.685816646 0.525679007 0.022196901 -0.244576265 -2.090297363 -1.099534501 -1.237792351 -1.247562319 0.192821615 -1.757683518 -0.537743502 -0.866195997 1.491399948 -0.345322156 0.446364892 1.003964324 -0.701300097 0.198431801 2.426132015 -1.031138802 -0.079314115 1.960430091 -1.550719352 0.11832591 -0.668387479 0.223298561 -1.812651721 0.058823033 -0.206964166 0.656069412 0.197808518 -1.268101387 -1.467329565 -0.387541859 -0.600970527 -2.288307277 0.791999772 0.75152785 0.068312984 1.468953207 -0.859155411 0.737587925 0.673673862 1.419915329 -0.154985576 3.281604929 -0.917978445 -0.296907317 -2.663681819 2.261180081 1.905593875 -1.140429944 -1.722043668 0.364115037 0.375374542 1.469180309 1.154066025 -1.208742929 -3.190996875 1.223270448 -0.862172265 -2.268142799 0.717724473 -2.382603885 0.436210872 0.218979462 0.089319574 -0.39553902 -1.543455608 -4.28819776 1.576640816 1.94102313 -0.274795463 0.906547155 --0.218049277 -0.508412721 -2.487404957 1.887320081 1.04722817 -0.395861337 -2.486192076 -1.226137194 -0.104801072 1.45110921 0.828248708 -0.485180912 -0.451624542 --0.410793483 0.116533685 1.879092728 1.184222196 -0.031140809 -0.90091931 -0.192744206 0.624946406 4.366497685 -0.703097886 -1.078368979 -0.505057973 0.118224392 --0.647691228 0.838129685 1.374778227 0.322847522 1.175235974 1.277024175 -0.236897745 0.721596 -0.504314501 -0.861374674 1.206376783 2.177943485 -0.449494687 --1.13985429 -0.495903004 1.056569633 -1.587672129 0.39860901 -0.768585548 -0.492163062 -1.334032689 -0.318208594 -1.910519651 -0.776626964 -2.045609723 -0.222016948 -0.53999669 -0.487881558 0.581258434 -1.085920121 -0.501406499 0.115378063 1.67985098 0.008021446 -0.475311199 0.501752008 -0.900015509 0.883963611 -0.703432192 -0.410905902 0.873257327 3.608291808 0.653652794 -1.54826995 1.468348054 -0.129090788 1.361138885 3.027033374 1.739572915 -1.04686345 1.352969991 0.299902147 --0.173062544 0.529483977 1.076381362 -0.503373399 -0.058733834 -1.45590019 -0.583968446 -0.34377335 -2.531910446 -1.157026193 1.489536116 -2.924248244 0.005305683 -0.590240338 -0.560105813 -0.33143028 -0.14587712 1.474596747 1.017308667 0.763302882 -1.08958979 -1.407811642 0.35749628 1.533330581 2.473208857 -1.883857873 --0.330152172 -0.29496616 -1.27480681 0.993753616 0.247092802 -0.980747759 -0.92039251 0.265139653 -0.94337653 1.139630735 -1.227503945 -1.998056427 -1.361923392 --0.770820846 -1.130215098 -0.533069878 1.339112763 1.354475058 -0.663609074 -0.440668674 -0.835248938 0.741736932 0.345359147 1.107382257 0.317138685 -0.854950385 --0.997000751 -1.594080868 -2.206716323 0.551420017 -0.360200976 0.897818757 -0.226179905 -0.46386577 -1.673646445 -0.787692746 -1.714676034 1.561427831 -0.509180861 --0.078723051 -1.034723379 1.715724783 2.451708059 0.070115822 -0.667778397 0.9182777 0.559357489 3.922441106 1.900288042 0.430316798 -1.565597154 0.134291091 -0.317741611 -1.449732445 -0.829499847 1.984395673 1.236600623 -0.310344732 0.396464662 -0.415009066 -2.54522463 -0.467312386 1.166484801 0.357433665 0.330435696 -0.920629978 -3.107612041 -1.72016277 -1.6235986 -0.680428817 -0.15105238 0.602888367 -1.657879596 -0.890662923 -3.607994273 -1.917029441 0.159292352 0.21447828 -0.904171949 -3.25810681 -1.338419095 -0.225635225 0.492152931 -1.184901646 -0.016458029 -0.150494769 0.381743675 1.397963375 1.172581749 -1.033849266 0.467948374 -0.712432166 -2.702026568 -4.090712476 0.325379006 -1.008512619 0.155445809 -0.191739783 0.556080242 -2.752293381 0.551014231 -1.50066555 1.340347455 -0.409919798 -1.213580415 -2.416744199 -1.644462247 0.887889538 -1.019416802 -0.194144049 0.501148249 0.285282369 2.446250229 0.562510532 -0.010904183 -0.349589858 -0.954443807 -1.015276471 -0.468506664 0.826022926 -0.704628983 -0.831670525 0.350992643 -0.198303944 1.948237535 2.470485173 -1.592518521 0.187746276 0.545136692 -1.28584816 -0.458644239 0.304782879 -0.286629392 -0.622614244 0.81723357 -0.620482414 -0.556632232 0.773289543 -1.112652318 0.082014739 1.648904096 -0.971475057 -1.250751609 -0.696387231 0.478940055 -0.661704616 -0.225347907 -1.187184555 -0.81881664 0.237742992 0.174157176 -0.375075224 0.397266338 -2.004418126 -0.198334226 -0.58673588 -0.398048005 0.35203805 -0.836607472 -1.114036436 -1.728872921 -0.244092905 -0.298339226 -0.126902005 -0.174902856 -0.888688529 -0.541688366 0.574723735 -0.251724316 --0.433479435 0.130772773 -0.702912597 -0.711315522 0.831520689 -1.273732568 -0.83152744 -0.221265277 0.133694875 0.402720914 2.56039361 -1.029639663 2.040725153 --1.504705013 -0.117524037 -1.172268361 -1.301597961 -1.43149785 -0.320764583 -1.071225578 -0.24829681 -0.469355764 -0.590282439 -2.263018539 0.952967985 -0.258655658 --1.966296873 -1.284318477 -0.647627254 0.187287319 -1.026578293 0.466508325 -0.46159186 -1.16679444 0.524641107 1.48888528 0.404919557 0.787272908 2.114290456 --1.036674723 -0.437122887 0.788299251 1.208653611 1.589038974 1.098106245 0.92962215 0.84719559 1.435926505 1.021366292 2.615617267 0.63159792 -0.93052932 --0.696570694 1.035997759 0.315168026 -0.180901513 -0.784289145 0.252979402 0.340104029 1.473120646 -0.473131225 -1.389555124 -2.373328119 -0.845126843 0.177085093 --1.056655551 2.054949636 1.733679189 -1.593982301 -2.245797478 0.326044934 -0.360084857 1.018951877 1.418511163 -1.413080788 -1.461508333 0.073065532 -1.242278489 --1.253872449 0.940519962 -1.859117584 -0.393650127 -0.324318496 0.436127281 -0.197216898 -1.114429674 -3.592796773 1.200332173 1.921478982 0.110082347 0.278345459 --1.069287813 -0.5172584 -0.656338228 0.143574863 -1.262613776 1.600586235 0.184584636 -1.457778362 1.202779356 0.537224991 -0.93829528 1.164458954 -1.920302265 -0.073889742 1.015905744 3.017531937 -0.558421237 0.29254061 -0.615549542 1.143177555 1.533164144 3.673870165 -0.7019961 1.555154386 -2.216135777 0.003952104 -1.32888943 2.850501828 2.785235104 -0.929137834 -0.021765497 -0.581322431 1.254999688 1.834596084 -0.232296833 -0.370716597 -0.314306107 0.034227111 -0.854938554 -0.831392436 2.255038285 0.082410724 0.365767705 0.860518504 -0.916585465 -0.497496994 -0.595463543 -2.70282438 1.294905539 0.882284001 -0.335263034 -0.264536802 --0.670035292 0.826737793 0.321644476 1.449735904 -0.344614203 -1.344199808 -1.501427728 -1.428300492 0.239233752 1.083968199 -1.205132707 -0.427614343 -0.829211295 --0.478355817 0.188711887 -0.630649751 -1.102278018 -1.374564234 0.697153667 0.191679475 -0.638025906 -0.952294227 -2.552013922 -1.029950031 2.041353475 -1.752282527 -0.999877364 -1.06986647 1.290972976 -0.92101604 0.906070467 0.416570393 1.478233181 -1.258578357 1.921622727 0.181261978 2.280634701 -0.280583274 0.64858987 -0.069018404 0.516767176 1.573029961 0.362136079 -1.202013587 -0.428893561 -0.93085896 1.586633646 0.282056985 1.283152119 -2.108084054 -0.845463954 -0.423093312 -0.351758996 0.056295399 1.665144379 -0.415044976 0.482451076 -1.053100619 0.282740592 -0.460471777 0.092114418 -0.777181055 1.684464663 -0.624207058 0.664860241 -0.858424569 -0.363527343 1.550740147 0.917563357 -0.68983397 -1.261316549 0.506665573 -0.419822742 -0.114404232 1.332608333 -1.172285046 -0.20821593 -0.097170847 -1.334779099 -0.553036015 0.026277298 -1.303973749 0.14799305 -1.047775225 0.47635453 -0.189508672 -1.524462849 -2.221537106 0.83782702 0.213541323 -0.273332909 -0.365467586 -1.460953257 1.836000417 0.076497972 -1.230053674 -0.597101686 -0.969311513 -0.907917242 1.809723119 1.38047172 -1.378046723 0.450673539 -0.026346724 --0.084225355 -0.90165276 0.41416722 1.144524716 0.509152904 -1.158340165 -0.449692941 0.559300497 -1.421833197 1.068026744 1.739206578 -0.561238479 -1.19547545 +Mot_X Mot_Y Mot_Z Mot_Pitch Mot_Roll Mot_Yaw Mot_d1_X Mot_d1_Y Mot_d1_Z Mot_d1_Pitch Mot_d1_Roll Mot_d1_Yaw CSF Task +-0.329580266 -0.844918633 1.626381228 -0.218889288 0.634672027 0.603137649 0 0 0 0 0 0 -0.718687205 -1.352373215 +-1.520650288 -2.13455262 -0.882532519 0.336875466 -1.592270822 -1.191402591 -1.191070022 -1.289633987 -2.508913747 0.555764753 -2.226942849 -1.794540239 -0.844111053 -2.055595493 +-0.511890744 -0.711229882 0.276263262 0.188691918 0.099964095 -0.995174865 1.008759544 1.423322738 1.158795781 -0.148183548 1.692234917 0.196227726 -0.921595994 -1.301886348 +-2.06545787 -0.40815265 1.038928803 0.226413424 -0.384996762 0.286311023 -1.553567126 0.303077232 0.762665541 0.037721506 -0.484960857 1.281485888 -0.638149754 -1.255098086 +-1.565465426 -0.289605838 0.551574072 0.307316972 -0.272939181 -0.92834738 0.499992444 0.118546812 -0.487354731 0.080903548 0.112057581 -1.214658403 1.141942235 -1.790879267 +-0.583901238 -0.45316118 0.882519392 -0.865762002 -0.537313024 0.517619453 0.981564188 -0.163555342 0.33094532 -1.173078975 -0.264373843 1.445966833 -1.385047823 -0.08765588 +0.225980497 -1.627540572 -0.68542407 -0.700932399 -1.67754023 1.019518689 0.809881735 -1.174379392 -1.567943462 0.164829603 -1.140227206 0.501899236 -0.186120892 1.547906633 +0.560677621 -1.489408639 -0.595520306 0.685234401 -0.723554811 0.416786873 0.334697124 0.138131933 0.089903764 1.386166801 0.953985419 -0.602731816 -0.580953121 3.177369816 +0.003248201 -0.340159692 -0.879529088 0.602469892 -0.876641151 -0.884034813 -0.55742942 1.149248947 -0.284008782 -0.08276451 -0.15308634 -1.300821686 -1.692923237 2.847689428 +0.836161459 -1.092255501 -2.618392652 0.707002513 -0.763363892 0.276104352 0.832913258 -0.752095809 -1.738863564 0.104532622 0.113277259 1.160139164 -0.371763879 1.278999068 +1.586566953 0.271349643 -0.649296386 -0.881991034 -0.310818575 0.552978122 0.750405494 1.363605144 1.969096266 -1.588993548 0.452545317 0.276873771 0.933331204 0.582531024 +0.667602572 1.549333738 0.076243832 0.833106558 0.518018373 -0.620225079 -0.918964381 1.277984095 0.725540218 1.715097593 0.828836948 -1.173203202 -0.852116981 -1.764145893 +-0.01792122 2.740600234 0.074586135 1.164085584 -0.240669175 -0.440358808 -0.685523792 1.191266496 -0.001657697 0.330979026 -0.758687548 0.179866271 -0.092616197 -1.161527578 +-0.659019247 2.298595672 0.17634643 -0.101416476 -0.182581812 2.015890229 -0.641098027 -0.442004562 0.101760295 -1.26550206 0.058087363 2.456249037 1.414856771 -0.840842986 +-1.094112767 -0.186225918 -2.127567341 -0.660534996 0.727968953 0.549065179 -0.43509352 -2.48482159 -2.303913771 -0.55911852 0.910550765 -1.46682505 -1.264667218 0.987514587 +0.248649215 -0.618039127 -1.082778413 -2.323754365 -0.275433162 -0.224507966 1.342761982 -0.431813209 1.044788928 -1.66321937 -1.003402116 -0.773573145 0.297574192 2.174571557 +-0.034109838 0.196157196 0.285049845 0.929700246 -1.903128963 -1.640111364 -0.282759053 0.814196323 1.367828258 3.253454611 -1.627695801 -1.415603398 -0.749200249 2.032922444 +-0.154408808 0.946079652 1.025632299 -1.504803984 1.86795795 0.461493517 -0.12029897 0.749922456 0.740582454 -2.43450423 3.771086913 2.101604881 0.024314549 0.613790059 +0.764863858 0.933908359 -0.135353281 -0.109277214 0.002639137 -1.643878764 0.919272666 -0.012171293 -1.16098558 1.39552677 -1.865318813 -2.105372281 -1.143075439 -0.482525583 +0.445796508 1.917424429 0.650347848 1.98385538 0.655358612 1.874592447 -0.31906735 0.98351607 0.785701129 2.093132594 0.652719475 3.51847121 -1.758526555 -0.351681452 +0.207513285 1.298735387 2.318083224 -0.535817525 2.16070642 -0.721054976 -0.238283223 -0.618689042 1.667735376 -2.519672905 1.505347808 -2.595647423 0.666876476 -2.337411374 +0.381782634 0.081083807 -1.984367893 0.563118012 0.313116397 -0.168924986 0.174269349 -1.21765158 -4.302451117 1.098935537 -1.847590024 0.55212999 0.145183641 -1.673709355 +-1.47473253 0.515676307 1.024256498 -0.163074269 0.924446522 -0.68895112 -1.856515164 0.4345925 3.008624391 -0.726192282 0.611330125 -0.520026134 -0.051313681 -0.199835362 +-1.494898436 0.766171566 1.630635233 1.316319173 0.756997088 -0.541944145 -0.020165906 0.250495259 0.606378735 1.479393442 -0.167449434 0.147006975 0.176664517 2.199049334 +-2.019896907 1.643560771 -0.311565305 -1.536321826 0.680356602 0.758927506 -0.524998471 0.877389205 -1.942200538 -2.852640999 -0.076640486 1.300871651 -1.264980299 1.920445951 +-2.178716752 0.973512277 2.252419221 -0.98610174 -1.462675416 1.890594069 -0.158819845 -0.670048494 2.563984526 0.550220085 -2.143032018 1.131666563 -1.113211169 0.588423648 +-1.232037427 -0.40191649 1.00829017 1.358255513 0.106048498 -0.129832743 0.946679325 -1.375428767 -1.244129051 2.344357253 1.568723914 -2.020426812 0.154120413 -1.163257212 +0.731522293 0.40813286 1.025669565 0.164802434 1.923608997 1.773241326 1.96355972 0.81004935 0.017379395 -1.193453079 1.817560499 1.903074069 -1.34580855 -0.095732534 +0.753719194 0.163556595 -1.064627798 -0.934732068 0.685816646 0.525679007 0.022196901 -0.244576265 -2.090297363 -1.099534501 -1.237792351 -1.247562319 0.192821615 -1.148529666 +1.757683518 -0.537743502 -0.866195997 1.491399948 -0.345322156 0.446364892 1.003964324 -0.701300097 0.198431801 2.426132015 -1.031138802 -0.079314115 1.960430091 -0.725025316 +1.550719352 0.11832591 -0.668387479 0.223298561 -1.812651721 0.058823033 -0.206964166 0.656069412 0.197808518 -1.268101387 -1.467329565 -0.387541859 -0.600970527 1.338930898 +2.288307277 0.791999772 0.75152785 0.068312984 1.468953207 -0.859155411 0.737587925 0.673673862 1.419915329 -0.154985576 3.281604929 -0.917978445 -0.296907317 1.710280144 +2.663681819 2.261180081 1.905593875 -1.140429944 -1.722043668 0.364115037 0.375374542 1.469180309 1.154066025 -1.208742929 -3.190996875 1.223270448 -0.862172265 -1.10195542 +2.268142799 0.717724473 -2.382603885 0.436210872 0.218979462 0.089319574 -0.39553902 -1.543455608 -4.28819776 1.576640816 1.94102313 -0.274795463 0.906547155 -0.840393059 +-0.218049277 -0.508412721 -2.487404957 1.887320081 1.04722817 -0.395861337 -2.486192076 -1.226137194 -0.104801072 1.45110921 0.828248708 -0.485180912 -0.451624542 -1.114922613 +-0.410793483 0.116533685 1.879092728 1.184222196 -0.031140809 -0.90091931 -0.192744206 0.624946406 4.366497685 -0.703097886 -1.078368979 -0.505057973 0.118224392 -0.396853219 +-0.647691228 0.838129685 1.374778227 0.322847522 1.175235974 1.277024175 -0.236897745 0.721596 -0.504314501 -0.861374674 1.206376783 2.177943485 -0.449494687 -1.344892636 +-1.13985429 -0.495903004 1.056569633 -1.587672129 0.39860901 -0.768585548 -0.492163062 -1.334032689 -0.318208594 -1.910519651 -0.776626964 -2.045609723 -0.222016948 -0.176791983 +0.53999669 -0.487881558 0.581258434 -1.085920121 -0.501406499 0.115378063 1.67985098 0.008021446 -0.475311199 0.501752008 -0.900015509 0.883963611 -0.703432192 0.174003102 +0.410905902 0.873257327 3.608291808 0.653652794 -1.54826995 1.468348054 -0.129090788 1.361138885 3.027033374 1.739572915 -1.04686345 1.352969991 0.299902147 0.727958119 +-0.173062544 0.529483977 1.076381362 -0.503373399 -0.058733834 -1.45590019 -0.583968446 -0.34377335 -2.531910446 -1.157026193 1.489536116 -2.924248244 0.005305683 1.410121446 +0.590240338 -0.560105813 -0.33143028 -0.14587712 1.474596747 1.017308667 0.763302882 -1.08958979 -1.407811642 0.35749628 1.533330581 2.473208857 -1.883857873 0.197348647 +-0.330152172 -0.29496616 -1.27480681 0.993753616 0.247092802 -0.980747759 -0.92039251 0.265139653 -0.94337653 1.139630735 -1.227503945 -1.998056427 -1.361923392 -0.113442337 +-0.770820846 -1.130215098 -0.533069878 1.339112763 1.354475058 -0.663609074 -0.440668674 -0.835248938 0.741736932 0.345359147 1.107382257 0.317138685 -0.854950385 -0.767755066 +-0.997000751 -1.594080868 -2.206716323 0.551420017 -0.360200976 0.897818757 -0.226179905 -0.46386577 -1.673646445 -0.787692746 -1.714676034 1.561427831 -0.509180861 -1.183525054 +-0.078723051 -1.034723379 1.715724783 2.451708059 0.070115822 -0.667778397 0.9182777 0.559357489 3.922441106 1.900288042 0.430316798 -1.565597154 0.134291091 -0.655231001 +0.317741611 -1.449732445 -0.829499847 1.984395673 1.236600623 -0.310344732 0.396464662 -0.415009066 -2.54522463 -0.467312386 1.166484801 0.357433665 0.330435696 0.00329668 +0.920629978 -3.107612041 -1.72016277 -1.6235986 -0.680428817 -0.15105238 0.602888367 -1.657879596 -0.890662923 -3.607994273 -1.917029441 0.159292352 0.21447828 2.238926945 +0.904171949 -3.25810681 -1.338419095 -0.225635225 0.492152931 -1.184901646 -0.016458029 -0.150494769 0.381743675 1.397963375 1.172581749 -1.033849266 0.467948374 0.982016224 +0.712432166 -2.702026568 -4.090712476 0.325379006 -1.008512619 0.155445809 -0.191739783 0.556080242 -2.752293381 0.551014231 -1.50066555 1.340347455 -0.409919798 1.920065594 +1.213580415 -2.416744199 -1.644462247 0.887889538 -1.019416802 -0.194144049 0.501148249 0.285282369 2.446250229 0.562510532 -0.010904183 -0.349589858 -0.954443807 0.093335589 +1.015276471 -0.468506664 0.826022926 -0.704628983 -0.831670525 0.350992643 -0.198303944 1.948237535 2.470485173 -1.592518521 0.187746276 0.545136692 -1.28584816 -1.179097329 +0.458644239 0.304782879 -0.286629392 -0.622614244 0.81723357 -0.620482414 -0.556632232 0.773289543 -1.112652318 0.082014739 1.648904096 -0.971475057 -1.250751609 -1.045855877 +0.696387231 0.478940055 -0.661704616 -0.225347907 -1.187184555 -0.81881664 0.237742992 0.174157176 -0.375075224 0.397266338 -2.004418126 -0.198334226 -0.58673588 -1.436858073 +0.398048005 0.35203805 -0.836607472 -1.114036436 -1.728872921 -0.244092905 -0.298339226 -0.126902005 -0.174902856 -0.888688529 -0.541688366 0.574723735 -0.251724316 0.457782882 +-0.433479435 0.130772773 -0.702912597 -0.711315522 0.831520689 -1.273732568 -0.83152744 -0.221265277 0.133694875 0.402720914 2.56039361 -1.029639663 2.040725153 2.253329615 +-1.504705013 -0.117524037 -1.172268361 -1.301597961 -1.43149785 -0.320764583 -1.071225578 -0.24829681 -0.469355764 -0.590282439 -2.263018539 0.952967985 -0.258655658 1.83523334 +-1.966296873 -1.284318477 -0.647627254 0.187287319 -1.026578293 0.466508325 -0.46159186 -1.16679444 0.524641107 1.48888528 0.404919557 0.787272908 2.114290456 1.498179142 +-1.036674723 -0.437122887 0.788299251 1.208653611 1.589038974 1.098106245 0.92962215 0.84719559 1.435926505 1.021366292 2.615617267 0.63159792 -0.93052932 -0.4582012 +-0.696570694 1.035997759 0.315168026 -0.180901513 -0.784289145 0.252979402 0.340104029 1.473120646 -0.473131225 -1.389555124 -2.373328119 -0.845126843 0.177085093 0.658375887 +-1.056655551 2.054949636 1.733679189 -1.593982301 -2.245797478 0.326044934 -0.360084857 1.018951877 1.418511163 -1.413080788 -1.461508333 0.073065532 -1.242278489 -0.7184164 +-1.253872449 0.940519962 -1.859117584 -0.393650127 -0.324318496 0.436127281 -0.197216898 -1.114429674 -3.592796773 1.200332173 1.921478982 0.110082347 0.278345459 1.380498447 +-1.069287813 -0.5172584 -0.656338228 0.143574863 -1.262613776 1.600586235 0.184584636 -1.457778362 1.202779356 0.537224991 -0.93829528 1.164458954 -1.920302265 1.036520911 +0.073889742 1.015905744 3.017531937 -0.558421237 0.29254061 -0.615549542 1.143177555 1.533164144 3.673870165 -0.7019961 1.555154386 -2.216135777 0.003952104 1.353240573 +1.32888943 2.850501828 2.785235104 -0.929137834 -0.021765497 -0.581322431 1.254999688 1.834596084 -0.232296833 -0.370716597 -0.314306107 0.034227111 -0.854938554 -1.340041384 +0.831392436 2.255038285 0.082410724 0.365767705 0.860518504 -0.916585465 -0.497496994 -0.595463543 -2.70282438 1.294905539 0.882284001 -0.335263034 -0.264536802 -0.3039092 +-0.670035292 0.826737793 0.321644476 1.449735904 -0.344614203 -1.344199808 -1.501427728 -1.428300492 0.239233752 1.083968199 -1.205132707 -0.427614343 -0.829211295 -2.309967509 +-0.478355817 0.188711887 -0.630649751 -1.102278018 -1.374564234 0.697153667 0.191679475 -0.638025906 -0.952294227 -2.552013922 -1.029950031 2.041353475 -1.752282527 -0.569481283 +0.999877364 -1.06986647 1.290972976 -0.92101604 0.906070467 0.416570393 1.478233181 -1.258578357 1.921622727 0.181261978 2.280634701 -0.280583274 0.64858987 -0.552361383 +0.069018404 0.516767176 1.573029961 0.362136079 -1.202013587 -0.428893561 -0.93085896 1.586633646 0.282056985 1.283152119 -2.108084054 -0.845463954 -0.423093312 -1.774614671 +0.351758996 0.056295399 1.665144379 -0.415044976 0.482451076 -1.053100619 0.282740592 -0.460471777 0.092114418 -0.777181055 1.684464663 -0.624207058 0.664860241 -0.004888443 +0.858424569 -0.363527343 1.550740147 0.917563357 -0.68983397 -1.261316549 0.506665573 -0.419822742 -0.114404232 1.332608333 -1.172285046 -0.20821593 -0.097170847 2.424262862 +1.334779099 -0.553036015 0.026277298 -1.303973749 0.14799305 -1.047775225 0.47635453 -0.189508672 -1.524462849 -2.221537106 0.83782702 0.213541323 -0.273332909 1.294341793 +0.365467586 -1.460953257 1.836000417 0.076497972 -1.230053674 -0.597101686 -0.969311513 -0.907917242 1.809723119 1.38047172 -1.378046723 0.450673539 -0.026346724 0.770458972 +-0.084225355 -0.90165276 0.41416722 1.144524716 0.509152904 -1.158340165 -0.449692941 0.559300497 -1.421833197 1.068026744 1.739206578 -0.561238479 -1.19547545 -0.027621886 diff --git a/tedana/tests/data/external_regress_corr_3echo.tsv b/tedana/tests/data/external_regress_corr_3echo.tsv deleted file mode 100644 index d551e5ebd..000000000 --- a/tedana/tests/data/external_regress_corr_3echo.tsv +++ /dev/null @@ -1,76 +0,0 @@ -trans_x trans_y trans_z --0.329580266 -0.844918633 1.626381228 --1.520650288 -2.13455262 -0.882532519 --0.511890744 -0.711229882 0.276263262 --2.06545787 -0.40815265 1.038928803 --1.565465426 -0.289605838 0.551574072 --0.583901238 -0.45316118 0.882519392 -0.225980497 -1.627540572 -0.68542407 -0.560677621 -1.489408639 -0.595520306 -0.003248201 -0.340159692 -0.879529088 -0.836161459 -1.092255501 -2.618392652 -1.586566953 0.271349643 -0.649296386 -0.667602572 1.549333738 0.076243832 --0.01792122 2.740600234 0.074586135 --0.659019247 2.298595672 0.17634643 --1.094112767 -0.186225918 -2.127567341 -0.248649215 -0.618039127 -1.082778413 --0.034109838 0.196157196 0.285049845 --0.154408808 0.946079652 1.025632299 -0.764863858 0.933908359 -0.135353281 -0.445796508 1.917424429 0.650347848 -0.207513285 1.298735387 2.318083224 -0.381782634 0.081083807 -1.984367893 --1.47473253 0.515676307 1.024256498 --1.494898436 0.766171566 1.630635233 --2.019896907 1.643560771 -0.311565305 --2.178716752 0.973512277 2.252419221 --1.232037427 -0.40191649 1.00829017 -0.731522293 0.40813286 1.025669565 -0.753719194 0.163556595 -1.064627798 -1.757683518 -0.537743502 -0.866195997 -1.550719352 0.11832591 -0.668387479 -2.288307277 0.791999772 0.75152785 -2.663681819 2.261180081 1.905593875 -2.268142799 0.717724473 -2.382603885 --0.218049277 -0.508412721 -2.487404957 --0.410793483 0.116533685 1.879092728 --0.647691228 0.838129685 1.374778227 --1.13985429 -0.495903004 1.056569633 -0.53999669 -0.487881558 0.581258434 -0.410905902 0.873257327 3.608291808 --0.173062544 0.529483977 1.076381362 -0.590240338 -0.560105813 -0.33143028 --0.330152172 -0.29496616 -1.27480681 --0.770820846 -1.130215098 -0.533069878 --0.997000751 -1.594080868 -2.206716323 --0.078723051 -1.034723379 1.715724783 -0.317741611 -1.449732445 -0.829499847 -0.920629978 -3.107612041 -1.72016277 -0.904171949 -3.25810681 -1.338419095 -0.712432166 -2.702026568 -4.090712476 -1.213580415 -2.416744199 -1.644462247 -1.015276471 -0.468506664 0.826022926 -0.458644239 0.304782879 -0.286629392 -0.696387231 0.478940055 -0.661704616 -0.398048005 0.35203805 -0.836607472 --0.433479435 0.130772773 -0.702912597 --1.504705013 -0.117524037 -1.172268361 --1.966296873 -1.284318477 -0.647627254 --1.036674723 -0.437122887 0.788299251 --0.696570694 1.035997759 0.315168026 --1.056655551 2.054949636 1.733679189 --1.253872449 0.940519962 -1.859117584 --1.069287813 -0.5172584 -0.656338228 -0.073889742 1.015905744 3.017531937 -1.32888943 2.850501828 2.785235104 -0.831392436 2.255038285 0.082410724 --0.670035292 0.826737793 0.321644476 --0.478355817 0.188711887 -0.630649751 -0.999877364 -1.06986647 1.290972976 -0.069018404 0.516767176 1.573029961 -0.351758996 0.056295399 1.665144379 -0.858424569 -0.363527343 1.550740147 -1.334779099 -0.553036015 0.026277298 -0.365467586 -1.460953257 1.836000417 --0.084225355 -0.90165276 0.41416722 diff --git a/tedana/tests/test_external_metrics.py b/tedana/tests/test_external_metrics.py new file mode 100644 index 000000000..fbceb1107 --- /dev/null +++ b/tedana/tests/test_external_metrics.py @@ -0,0 +1,188 @@ +"""Tests for tedana.metrics.external.""" + +import logging +import os +import os.path as op + +import pandas as pd +import pytest + +from tedana.io import load_json +from tedana.metrics import external + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) +LGR = logging.getLogger("GENERAL") + +# ---------------------------------------------------------------------- +# Functions Used For Tests +# ---------------------------------------------------------------------- + + +def sample_external_regressors(regress_choice="valid"): + """ + Retrieves a sample external regressor dataframe. + + Parameters + ---------- + regress_choice : :obj:`str` How to keep or alter the external regressor data + Options are: + "valid": Column labels expected in demo_minimal_external_regressors_motion_task_models + The labels in the config file are lowercase and this file is capitalized, but it should + still be valid. + "no_mot_y_column": The column labeled "Mot_Pitch" is removed. + + Returns + ------- + external_regressors : :obj:`pandas.DataFrame` External regressor table + n_time : :obj:`int` Number of time points (rows) in external_regressors + """ + sample_fname = op.join(THIS_DIR, "data", "external_regress_Ftest_3echo.tsv") + + external_regressors = pd.read_csv(sample_fname, delimiter="\t") + + if regress_choice == "no_mot_y_column": + external_regressors = external_regressors.drop(columns="Mot_Y") + elif regress_choice != "valid": + raise ValueError(f"regress_choice is {regress_choice}, which is not a listed option") + + n_time = len(external_regressors) + + return external_regressors, n_time + + +def sample_external_regressor_config(config_choice="valid"): + """ + Retrieves a sample external regressor configuration dictionary. + + Parameters + ---------- + config_choice : :obj:`str` How to keep or alter the config file + Options are: + "valid": Config dictionary stored in demo_minimal_external_regressors_motion_task_models + "no_task": Removes "task_keep" info from config + "no_task_partial": Removes "task_keep" and everything with partial F stats + "csf_in_mot": Adds "CSF" to the list of motion regressor partial models + + Returns + ------- + external_regressor_config : :obj:`dict` External Regressor Dictionary + """ + + sample_fname = op.join( + THIS_DIR, + "../resources/decision_trees", + "demo_minimal_external_regressors_motion_task_models.json", + ) + tree = load_json(sample_fname) + external_regressor_config = tree["external_regressor_config"] + + if config_choice == "no_task": + external_regressor_config.pop("task_keep") + elif config_choice == "no_task_partial": + external_regressor_config.pop("task_keep") + external_regressor_config.pop("f_stats_partial_models") + external_regressor_config.pop("Motion") + external_regressor_config.pop("CSF") + elif config_choice == "csf_in_mot": + external_regressor_config["Motion"].append("CSF") + elif config_choice == "unmatched_regex": + external_regressor_config["Motion"] = ["^translation_.*$"] + elif config_choice != "valid": + raise ValueError(f"config_choice is {config_choice}, which is not a listed option") + + return external_regressor_config + + +# validate_extern_regress +# ----------- +def test_validate_extern_regress_succeeds(caplog): + """Test validate_extern_regress works as expected.""" + + external_regressors, n_time = sample_external_regressors() + external_regressor_config = sample_external_regressor_config() + external_regressor_config_expanded = external.validate_extern_regress( + external_regressors, external_regressor_config, n_time + ) + + # The regex patterns should have been replaced with the full names of the regressors + assert set(external_regressor_config_expanded["Motion"]) == set( + [ + "Mot_X", + "Mot_d1_Yaw", + "Mot_d1_Y", + "Mot_d1_Pitch", + "Mot_Z", + "Mot_d1_Z", + "Mot_d1_Roll", + "Mot_d1_X", + "Mot_Pitch", + "Mot_Y", + "Mot_Yaw", + "Mot_Roll", + ] + ) + assert external_regressor_config_expanded["CSF"] == ["CSF"] + assert external_regressor_config_expanded["task_keep"] == ["Task"] + assert "WARNING" not in caplog.text + + # Rerunning with explicit names for the above three categories instead of regex patterns + # Shouldn't change anything, but making sure it runs + caplog.clear() + external_regressor_config = external.validate_extern_regress( + external_regressors, external_regressor_config_expanded, n_time + ) + assert "WARNING" not in caplog.text + + # Removing all partial model and task_keep stuff to confirm it still runs + caplog.clear() + external_regressor_config = sample_external_regressor_config("no_task_partial") + external.validate_extern_regress(external_regressors, external_regressor_config, n_time) + assert caplog.text == "" + + # Removing "task_keep" from config to test if warning appears + caplog.clear() + external_regressor_config = sample_external_regressor_config("no_task") + external.validate_extern_regress(external_regressors, external_regressor_config, n_time) + assert "Regressor labels in external_regressors are not all included in F" in caplog.text + + # Removing "task_keep" from config to test if warning appears + caplog.clear() + external_regressor_config = sample_external_regressor_config("csf_in_mot") + external.validate_extern_regress(external_regressors, external_regressor_config, n_time) + assert "External regressors used in more than one partial model" in caplog.text + + +def test_validate_extern_regress_fails(): + """Test validate_extern_regress fails when expected.""" + + external_regressors, n_time = sample_external_regressors() + external_regressor_config = sample_external_regressor_config() + + # If there are a different number of time points in the fMRI data and external regressors + with pytest.raises( + external.RegressError, match=f"while fMRI data have {n_time - 1} timepoints" + ): + external.validate_extern_regress( + external_regressors, external_regressor_config, n_time - 1 + ) + + # If no external regressor labels match the regex label in config + external_regressor_config = sample_external_regressor_config("unmatched_regex") + with pytest.raises(external.RegressError, match="No external regressor labels matching regex"): + external.validate_extern_regress(external_regressors, external_regressor_config, n_time) + + # If a regressor expected in the config is not in external_regressors + # Run successfully to expand Motion labels in config and then create error + # when "Mot_Y" is in the config, but removed from external_regressros + external_regressor_config = sample_external_regressor_config() + external_regressor_config_expanded = external.validate_extern_regress( + external_regressors, external_regressor_config, n_time + ) + external_regressors, n_time = sample_external_regressors("no_mot_y_column") + with pytest.raises( + external.RegressError, + match="Inputed regressors in external_regressors do not include all expected", + ): + external.validate_extern_regress( + external_regressors, external_regressor_config_expanded, n_time + ) diff --git a/tedana/tests/test_integration.py b/tedana/tests/test_integration.py index d68741650..8636a2757 100644 --- a/tedana/tests/test_integration.py +++ b/tedana/tests/test_integration.py @@ -374,7 +374,7 @@ def test_integration_three_echo(skip_integration): check_integration_outputs(fn, out_dir) -def test_integration_three_echo_external_regressors_fstat(skip_integration): +def test_integration_three_echo_external_regressors_single_model(skip_integration): """Integration test of tedana workflow with extern regress and F stat.""" if skip_integration: @@ -384,13 +384,56 @@ def test_integration_three_echo_external_regressors_fstat(skip_integration): out_dir = os.path.abspath( os.path.join(test_data_path, "../../outputs/three-echo-externalreg-Ftest") ) - out_dir_manual = f"{out_dir}-rerun" if os.path.exists(out_dir): shutil.rmtree(out_dir) - if os.path.exists(out_dir_manual): - shutil.rmtree(out_dir_manual) + # download data and run the test + # external_regress_Ftest_3echo.tsv has 13 rows. Based on a local run on the 3 echo data: + # Col 1 (trans_x_correlation) is the TS for ICA comp 59 + similar stdev Gaussian Noise + # Col 2 (trans_y_correlation) is 0.4*comp29+0.5+comp20+Gaussian Noise + # Col 3 (trans_z_correlation) is comp20+Gaussian Noise + # The above are the same as the test that uses corr for external regressors + # Col 4-6 are Gaussian noise representing pitch/roll/yaw + # Col 7-12 are the first derivative of col 1-6 + # With the currently set up decision tree minimal_exteral 2, one component (my 59) + # should be rejected, but 20 and 29 aren't rejected because neither crosses the + # r>0.8 threshold. If trans_y and trans_Z were included in a single model then + # component 20 would have been rejected + # Note that the above is in comparision to the minimal decision tree + # but the integration test for 3 echoes uses the kundu tree + download_test_data(osf_id, test_data_path) + tree_name = "resources/decision_trees/demo_minimal_external_regressors_single_model.json" + tedana_cli.tedana_workflow( + data=f"{test_data_path}/three_echo_Cornell_zcat.nii.gz", + tes=[14.5, 38.5, 62.5], + out_dir=out_dir, + tree=resource_filename("tedana", tree_name), + external_regressors=resource_filename( + "tedana", "tests/data/external_regress_Ftest_3echo.tsv" + ), + low_mem=True, + tedpca="aic", + ) + + # compare the generated output files + fn = resource_filename("tedana", "tests/data/cornell_three_echo_outputs.txt") + check_integration_outputs(fn, out_dir) + + +def test_integration_three_echo_external_regressors_motion_task_models(skip_integration): + """Integration test of tedana workflow with extern regress and F stat.""" + + if skip_integration: + pytest.skip("Skipping three-echo with external regressors integration test") + + test_data_path, osf_id = data_for_testing_info("three-echo") + out_dir = os.path.abspath( + os.path.join(test_data_path, "../../outputs/three-echo-externalreg-Ftest-multimodels") + ) + + if os.path.exists(out_dir): + shutil.rmtree(out_dir) # download data and run the test # external_regress_Ftest_3echo.tsv has 13 rows. Based on a local run on the 3 echo data: @@ -407,7 +450,7 @@ def test_integration_three_echo_external_regressors_fstat(skip_integration): # Note that the above is in comparision to the minimal decision tree # but the integration test for 3 echoes uses the kundu tree download_test_data(osf_id, test_data_path) - tree_name = "resources/decision_trees/demo_minimal_externalregressors_Fstat.json" + tree_name = "resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json" tedana_cli.tedana_workflow( data=f"{test_data_path}/three_echo_Cornell_zcat.nii.gz", tes=[14.5, 38.5, 62.5], diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 1440d89c8..bddad6d02 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -530,8 +530,10 @@ def tedana_workflow( "external_regressor_config" in set(selector.tree.keys()) and selector.tree["external_regressor_config"] is not None ): - external_regressors = metrics.external.load_validate_external_regressors( - external_regressors, selector.tree["external_regressor_config"], catd.shape[2] + external_regressors, selector.tree["external_regressor_config"] = ( + metrics.external.load_validate_external_regressors( + external_regressors, selector.tree["external_regressor_config"], catd.shape[2] + ) ) io_generator = io.OutputGenerator( From 28e6295a596869c3be6f304337a8799e41a3844b Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Tue, 12 Mar 2024 11:27:53 -0400 Subject: [PATCH 47/81] cleaning up decision tree json files --- ...xternal_regressors_motion_task_models.json | 104 ++++++++---------- ...imal_external_regressors_single_model.json | 80 ++------------ 2 files changed, 57 insertions(+), 127 deletions(-) diff --git a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json index 8a38717db..3f2ad87e2 100644 --- a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json +++ b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json @@ -25,17 +25,13 @@ "f_stats_partial_models": ["Motion", "CSF"], "Motion": ["^mot_.*$"], "CSF": ["^csf.*$"], - "task_keep": ["^task.*$"], + "task_keep": ["^task.*$"] }, "nodes": [ { "functionname": "manual_classify", "parameters": {"new_classification": "unclassified", "decide_comps": "all"}, - "kwargs": { - "log_extra_report": "", - "clear_classification_tags": true, - "dont_warn_reclassify": true, - }, + "kwargs": {"clear_classification_tags": true, "dont_warn_reclassify": true} }, { "functionname": "dec_left_op_right", @@ -45,9 +41,9 @@ "decide_comps": "all", "op": ">", "left": "rho", - "right": "kappa", + "right": "kappa" }, - "kwargs": {"log_extra_report": "", "tag_if_true": "Unlikely BOLD"}, + "kwargs": {"tag_if_true": "Unlikely BOLD"} }, { "functionname": "dec_left_op_right", @@ -57,23 +53,22 @@ "decide_comps": "all", "op": ">", "left": "countsigFS0", - "right": "countsigFT2", + "right": "countsigFT2" }, "kwargs": { "left2": "countsigFT2", "op2": ">", "right2": 0, - "log_extra_report": "", - "tag_if_true": "Unlikely BOLD", - }, + "tag_if_true": "Unlikely BOLD" + } }, { "functionname": "calc_median", "parameters": { "decide_comps": "all", "metric_name": "variance explained", - "median_label": "varex", - }, + "median_label": "varex" + } }, { "functionname": "dec_left_op_right", @@ -83,15 +78,14 @@ "decide_comps": "all", "op": ">", "left": "dice_FS0", - "right": "dice_FT2", + "right": "dice_FT2" }, "kwargs": { "left2": "variance explained", "op2": ">", "right2": "median_varex", - "log_extra_report": "", - "tag_if_true": "Unlikely BOLD", - }, + "tag_if_true": "Unlikely BOLD" + } }, { "functionname": "dec_left_op_right", @@ -101,21 +95,19 @@ "decide_comps": "all", "op": ">", "left": 0, - "right": "signal-noise_t", + "right": "signal-noise_t" }, "kwargs": { "left2": "variance explained", "op2": ">", "right2": "median_varex", - "log_extra_report": "", - "tag_if_true": "Unlikely BOLD", - }, + "tag_if_true": "Unlikely BOLD" + } }, { "functionname": "calc_kappa_elbow", "parameters": {"decide_comps": "all"}, - "kwargs": {"log_extra_info": "", "log_extra_report": ""}, - "_comment": "", + "_comment": "" }, { "functionname": "calc_rho_elbow", @@ -123,10 +115,9 @@ "kwargs": { "subset_decide_comps": "unclassified", "rho_elbow_type": "liberal", - "log_extra_info": "", - "log_extra_report": "", + "log_extra_info": "" }, - "_comment": "", + "_comment": "" }, { "functionname": "dec_left_op_right", @@ -136,9 +127,8 @@ "decide_comps": "unclassified", "op": ">=", "left": "kappa", - "right": "kappa_elbow_kundu", - }, - "kwargs": {"log_extra_report": ""}, + "right": "kappa_elbow_kundu" + } }, { "functionname": "dec_left_op_right", @@ -148,14 +138,13 @@ "decide_comps": "provisionalaccept", "op": ">", "left": "kappa", - "right": "rho", + "right": "rho" }, "kwargs": { "log_extra_info": "If kappa>elbow and kappa>2*rho accept even if rho>elbow", - "log_extra_report": "", "right_scale": 2, - "tag_if_true": "Likely BOLD", - }, + "tag_if_true": "Likely BOLD" + } }, { "functionname": "dec_left_op_right", @@ -165,9 +154,9 @@ "decide_comps": ["provisionalreject", "provisionalaccept"], "op": ">", "left": "rho", - "right": "rho_elbow_liberal", + "right": "rho_elbow_liberal" }, - "kwargs": {"tag_if_true": "Unlikely BOLD"}, + "kwargs": {"tag_if_true": "Unlikely BOLD"} }, { "functionname": "dec_left_op_right", @@ -177,17 +166,16 @@ "decide_comps": "all", "op": "<", "left": "pval Full Model", - "right": 0.05, + "right": 0.05 }, "kwargs": { "op2": ">", "left2": "R2stat Full Model", "right2": 0.5, "log_extra_info": "If external regressors fit with p<0.05 and model R2>0.5 of the variance, then reject.", - "log_extra_report": "", - "tag_if_true": "external regressors", + "tag_if_true": "external regressors" }, - "_comment": "Provisionally rejecting components that fit to the external regressor noise model", + "_comment": "Provisionally rejecting components that fit to the external regressor noise model" }, { "functionname": "dec_left_op_right", @@ -197,7 +185,7 @@ "decide_comps": "all", "op": "<", "left": "pval Full Model", - "right": 0.05, + "right": 0.05 }, "kwargs": { "op2": ">", @@ -206,9 +194,9 @@ "op3": "<", "left3": "pval Motion Model", "right3": 0.05, - "tag_if_true": "Fits motion external regressors", + "tag_if_true": "Fits motion external regressors" }, - "_comment": "Identical to the one above, & not changing classifications, but tagging if fits to motion regressors", + "_comment": "Identical to the one above, & not changing classifications, but tagging if fits to motion regressors" }, { "functionname": "dec_left_op_right", @@ -218,7 +206,7 @@ "decide_comps": "all", "op": "<", "left": "pval Full Model", - "right": 0.05, + "right": 0.05 }, "kwargs": { "op2": ">", @@ -227,9 +215,9 @@ "op3": "<", "left3": "pval CSF Model", "right3": 0.05, - "tag_if_true": "Fits CSF external regressors", + "tag_if_true": "Fits CSF external regressors" }, - "_comment": "Identical to the one above, & not changing classifications, but tagging if fits to CSF regressors", + "_comment": "Identical to the one above, & not changing classifications, but tagging if fits to CSF regressors" }, { "functionname": "dec_left_op_right", @@ -239,7 +227,7 @@ "decide_comps": ["provisionalreject", "rejected"], "op": "<", "left": "pval Task Model", - "right": 0.05, + "right": 0.05 }, "kwargs": { "op2": ">", @@ -248,37 +236,35 @@ "op3": ">=", "left3": "kappa", "right3": "kappa_elbow_kundu", - "tag_if_true": "Fits task", + "tag_if_true": "Fits task" }, - "_comment": "Keep if it fits task regressors and contains T2* signal, as defined by kappa>elbow", + "_comment": "Keep if it fits task regressors and contains T2* signal, as defined by kappa>elbow" }, { "functionname": "dec_variance_lessthan_thresholds", "parameters": { "if_true": "accepted", "if_false": "nochange", - "decide_comps": "provisionalreject", + "decide_comps": "provisionalreject" }, "kwargs": { "var_metric": "variance explained", - "log_extra_info": "", - "log_extra_report": "", "single_comp_threshold": 0.1, "all_comp_threshold": 1.0, - "tag_if_true": "Low variance", - }, + "tag_if_true": "Low variance" + } }, { "functionname": "manual_classify", "parameters": {"new_classification": "accepted", "decide_comps": "provisionalaccept"}, - "kwargs": {"log_extra_info": "", "log_extra_report": "", "tag": "Likely BOLD"}, + "kwargs": {"tag": "Likely BOLD"} }, { "functionname": "manual_classify", "parameters": { "new_classification": "rejected", - "decide_comps": ["provisionalreject", "unclassified"], - }, - }, - ], + "decide_comps": ["provisionalreject", "unclassified"] + } + } + ] } diff --git a/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json b/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json index 3d3c00586..b9f856a53 100644 --- a/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json +++ b/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json @@ -14,15 +14,8 @@ "pval Full Model", "R2stat Full Model" ], - "intermediate_classifications": [ - "provisionalaccept", - "provisionalreject" - ], - "classification_tags": [ - "Likely BOLD", - "Unlikely BOLD", - "Low variance" - ], + "intermediate_classifications": ["provisionalaccept", "provisionalreject"], + "classification_tags": ["Likely BOLD", "Unlikely BOLD", "Low variance"], "external_regressor_config": { "regess_ID": "Fmodel", "info": "Fits all external regressors to a single model using an F statistic", @@ -33,10 +26,7 @@ "nodes": [ { "functionname": "manual_classify", - "parameters": { - "new_classification": "unclassified", - "decide_comps": "all" - }, + "parameters": {"new_classification": "unclassified", "decide_comps": "all"}, "kwargs": { "log_extra_report": "", "clear_classification_tags": true, @@ -53,10 +43,7 @@ "left": "rho", "right": "kappa" }, - "kwargs": { - "log_extra_report": "", - "tag_if_true": "Unlikely BOLD" - } + "kwargs": {"tag_if_true": "Unlikely BOLD"} }, { "functionname": "dec_left_op_right", @@ -72,7 +59,6 @@ "left2": "countsigFT2", "op2": ">", "right2": 0, - "log_extra_report": "", "tag_if_true": "Unlikely BOLD" } }, @@ -98,7 +84,6 @@ "left2": "variance explained", "op2": ">", "right2": "median_varex", - "log_extra_report": "", "tag_if_true": "Unlikely BOLD" } }, @@ -116,32 +101,18 @@ "left2": "variance explained", "op2": ">", "right2": "median_varex", - "log_extra_report": "", "tag_if_true": "Unlikely BOLD" } }, { "functionname": "calc_kappa_elbow", - "parameters": { - "decide_comps": "all" - }, - "kwargs": { - "log_extra_info": "", - "log_extra_report": "" - }, + "parameters": {"decide_comps": "all"}, "_comment": "" }, { "functionname": "calc_rho_elbow", - "parameters": { - "decide_comps": "all" - }, - "kwargs": { - "subset_decide_comps": "unclassified", - "rho_elbow_type": "liberal", - "log_extra_info": "", - "log_extra_report": "" - }, + "parameters": {"decide_comps": "all"}, + "kwargs": {"subset_decide_comps": "unclassified", "rho_elbow_type": "liberal"}, "_comment": "" }, { @@ -153,9 +124,6 @@ "op": ">=", "left": "kappa", "right": "kappa_elbow_kundu" - }, - "kwargs": { - "log_extra_report": "" } }, { @@ -170,7 +138,6 @@ }, "kwargs": { "log_extra_info": "If kappa>elbow and kappa>2*rho accept even if rho>elbow", - "log_extra_report": "", "right_scale": 2, "tag_if_true": "Likely BOLD" } @@ -180,16 +147,10 @@ "parameters": { "if_true": "provisionalreject", "if_false": "nochange", - "decide_comps": [ - "provisionalreject", - "provisionalaccept" - ], + "decide_comps": ["provisionalreject", "provisionalaccept"], "op": ">", "left": "rho", "right": "rho_elbow_liberal" - }, - "kwargs": { - "log_extra_report": "" } }, { @@ -201,8 +162,6 @@ }, "kwargs": { "var_metric": "variance explained", - "log_extra_info": "", - "log_extra_report": "", "single_comp_threshold": 0.1, "all_comp_threshold": 1.0, "tag_if_true": "Low variance" @@ -223,36 +182,21 @@ "left2": "R2stat Full Model", "right2": 0.5, "log_extra_info": "If external regressors fit with p<0.05 and model R2>0.5 of the variance, then reject.", - "log_extra_report": "", "tag_if_true": "external regressors" } }, { "functionname": "manual_classify", - "parameters": { - "new_classification": "accepted", - "decide_comps": "provisionalaccept" - }, - "kwargs": { - "log_extra_info": "", - "log_extra_report": "", - "tag": "Likely BOLD" - } + "parameters": {"new_classification": "accepted", "decide_comps": "provisionalaccept"}, + "kwargs": {"tag": "Likely BOLD"} }, { "functionname": "manual_classify", "parameters": { "new_classification": "rejected", - "decide_comps": [ - "provisionalreject", - "unclassified" - ] + "decide_comps": ["provisionalreject", "unclassified"] }, - "kwargs": { - "log_extra_info": "", - "log_extra_report": "", - "tag": "Unlikely BOLD" - } + "kwargs": {"tag": "Unlikely BOLD"} } ] } From 4562273489449c60b1e5df6d7a00d75cb545c579 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Tue, 9 Apr 2024 11:23:11 -0400 Subject: [PATCH 48/81] removed mot12_csf.json changed task to signal --- ...xternal_regressors_motion_task_models.json | 2 +- .../external_regressor_configs/Mot12_CSF.json | 27 ------------------- 2 files changed, 1 insertion(+), 28 deletions(-) delete mode 100644 tedana/resources/external_regressor_configs/Mot12_CSF.json diff --git a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json index 19fbf01fc..6d9951bc2 100644 --- a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json +++ b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json @@ -25,7 +25,7 @@ "f_stats_partial_models": ["Motion", "CSF"], "Motion": ["^mot_.*$"], "CSF": ["^csf.*$"], - "task_keep": ["^task.*$"] + "task_keep": ["^signal.*$"] }, "nodes": [ { diff --git a/tedana/resources/external_regressor_configs/Mot12_CSF.json b/tedana/resources/external_regressor_configs/Mot12_CSF.json deleted file mode 100644 index 985515dcd..000000000 --- a/tedana/resources/external_regressor_configs/Mot12_CSF.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "regress_ID": "Motion12_CSF", - "info": "Contains 6 columns for motion, 6 for first derivatives of motion, and 1 for a CSF ROI time series.", - "detrend": true, - "calc_stats": "F", - "f_stats_partial_models": [ - "Motion", - "CSF" - ], - "Motion": [ - "Mot_X", - "Mot_Y", - "Mot_Z", - "Mot_Pitch", - "Mot_Roll", - "Mot_Yaw", - "Mot_d1_X", - "Mot_d1_Y", - "Mot_d1_Z", - "Mot_d1_Pitch", - "Mot_d1_Roll", - "Mot_d1_Yaw" - ], - "CSF": [ - "CSF" - ] -} From 5404d6568a7a8a4f18b4a428a5b80aa423cd9dde Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Tue, 9 Apr 2024 14:05:35 -0400 Subject: [PATCH 49/81] fixed tests with task_keep signal --- tedana/tests/data/external_regress_Ftest_3echo.tsv | 2 +- tedana/tests/test_external_metrics.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tedana/tests/data/external_regress_Ftest_3echo.tsv b/tedana/tests/data/external_regress_Ftest_3echo.tsv index 296476b3d..d2609050f 100644 --- a/tedana/tests/data/external_regress_Ftest_3echo.tsv +++ b/tedana/tests/data/external_regress_Ftest_3echo.tsv @@ -1,4 +1,4 @@ -Mot_X Mot_Y Mot_Z Mot_Pitch Mot_Roll Mot_Yaw Mot_d1_X Mot_d1_Y Mot_d1_Z Mot_d1_Pitch Mot_d1_Roll Mot_d1_Yaw CSF Task +Mot_X Mot_Y Mot_Z Mot_Pitch Mot_Roll Mot_Yaw Mot_d1_X Mot_d1_Y Mot_d1_Z Mot_d1_Pitch Mot_d1_Roll Mot_d1_Yaw CSF Signal -0.329580266 -0.844918633 1.626381228 -0.218889288 0.634672027 0.603137649 0 0 0 0 0 0 -0.718687205 -1.352373215 -1.520650288 -2.13455262 -0.882532519 0.336875466 -1.592270822 -1.191402591 -1.191070022 -1.289633987 -2.508913747 0.555764753 -2.226942849 -1.794540239 -0.844111053 -2.055595493 -0.511890744 -0.711229882 0.276263262 0.188691918 0.099964095 -0.995174865 1.008759544 1.423322738 1.158795781 -0.148183548 1.692234917 0.196227726 -0.921595994 -1.301886348 diff --git a/tedana/tests/test_external_metrics.py b/tedana/tests/test_external_metrics.py index fbceb1107..34dc8573f 100644 --- a/tedana/tests/test_external_metrics.py +++ b/tedana/tests/test_external_metrics.py @@ -122,7 +122,7 @@ def test_validate_extern_regress_succeeds(caplog): ] ) assert external_regressor_config_expanded["CSF"] == ["CSF"] - assert external_regressor_config_expanded["task_keep"] == ["Task"] + assert external_regressor_config_expanded["task_keep"] == ["Signal"] assert "WARNING" not in caplog.text # Rerunning with explicit names for the above three categories instead of regex patterns From 3ff9c2e9fbac5a3aa27923a455cbbcc56c7947d0 Mon Sep 17 00:00:00 2001 From: Dan Handwerker <7406227+handwerkerd@users.noreply.github.com> Date: Thu, 2 May 2024 15:32:01 -0400 Subject: [PATCH 50/81] Update tedana/metrics/external.py Co-authored-by: Taylor Salo --- tedana/metrics/external.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index 4a225e392..3be08a956 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -263,7 +263,7 @@ def make_detrend_regressors(n_time, polort=None): # **1 [linear trend -> f(x) = a + bx], # **2 [quadratic trend -> f(x) = a + bx + cx²], # **3 [cubic trend -> f(x) = f(x) = a + bx + cx² + dx³], - # **4 [quintic trend -> f(x) = a + bx + cx² + dx³ + ex⁴] + # **4 [quartic trend -> f(x) = a + bx + cx² + dx³ + ex⁴] for idx in range(polort): # create a linear space with numbers in range [-1,1] because the mean = 0, # and include the number of timepoints for each regressor From f44f385e1b8a85b0a2e1046ef27749e7d0cd9cd2 Mon Sep 17 00:00:00 2001 From: Dan Handwerker <7406227+handwerkerd@users.noreply.github.com> Date: Thu, 2 May 2024 15:32:52 -0400 Subject: [PATCH 51/81] Update tedana/metrics/_utils.py Co-authored-by: Taylor Salo --- tedana/metrics/_utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tedana/metrics/_utils.py b/tedana/metrics/_utils.py index d0b82f0e6..b781acb1a 100644 --- a/tedana/metrics/_utils.py +++ b/tedana/metrics/_utils.py @@ -21,8 +21,10 @@ def add_external_dependencies(dependency_config, external_regressor_config): model_names = ["Full"] if "f_stats_partial_models" in set(external_regressor_config.keys()): model_names.append(external_regressor_config["f_stats_partial_models"]) + if "task_keep" in set(external_regressor_config.keys()): model_names.append("task_keep") + for model_name in model_names: for stat_type in ["Fstat", "R2stat", "pval"]: dependency_config["dependencies"][f"{stat_type} {model_name} Model"] = [ From 79de3d434be1510eb1678b4b446a5ac0c5c0b530 Mon Sep 17 00:00:00 2001 From: Dan Handwerker <7406227+handwerkerd@users.noreply.github.com> Date: Thu, 2 May 2024 15:33:53 -0400 Subject: [PATCH 52/81] Update tedana/metrics/collect.py Co-authored-by: Taylor Salo --- tedana/metrics/collect.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tedana/metrics/collect.py b/tedana/metrics/collect.py index 912030cc1..1d4a997fb 100644 --- a/tedana/metrics/collect.py +++ b/tedana/metrics/collect.py @@ -355,11 +355,6 @@ def generate_metrics( comptable = external.fit_regressors( comptable, external_regressors, external_regressor_config, mixing ) - # for col in external_regressor_names: - # external_regressor_arr = external_regressors[col].values - # corrs = external_regressors.correlate_regressor(external_regressor_arr, mixing) - # comptable[f"{col}_correlation"] = corrs - # Write verbose metrics if needed if io_generator.verbose: write_betas = "map echo betas" in metric_maps From fe09e68fd40aa4f81650b461d9b5439ecd2544e8 Mon Sep 17 00:00:00 2001 From: Dan Handwerker <7406227+handwerkerd@users.noreply.github.com> Date: Thu, 2 May 2024 15:34:48 -0400 Subject: [PATCH 53/81] Update tedana/metrics/external.py Co-authored-by: Taylor Salo --- tedana/metrics/external.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index 3be08a956..c3cb07a55 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -20,8 +20,7 @@ class RegressError(Exception): def load_validate_external_regressors(external_regressors, external_regressor_config, n_time): - """ - Load and validate external regressors and descriptors in dictionary. + """Load and validate external regressors and descriptors in dictionary. Parameters ---------- From 3b0a792a3505c6e29a554f788ccff610fe7b9db8 Mon Sep 17 00:00:00 2001 From: Dan Handwerker <7406227+handwerkerd@users.noreply.github.com> Date: Thu, 2 May 2024 15:36:06 -0400 Subject: [PATCH 54/81] Update tedana/metrics/external.py Co-authored-by: Taylor Salo --- tedana/metrics/external.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index c3cb07a55..2b27392c9 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -221,7 +221,7 @@ def fit_regressors(comptable, external_regressors, external_regressor_config, mi comptable, external_regressors, external_regressor_config, mixing, detrend_regressors ) else: - # This should already be valided by this point, but keeping the catch clause here + # This should already be validated by this point, but keeping the catch clause here # since this would otherwise just return comptable with no changes, which would # make a hard-to-track error raise ValueError( From 7043f10e5dfcb05c8b4289619d36e07694ac73f2 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Thu, 2 May 2024 16:32:48 -0400 Subject: [PATCH 55/81] Responding to review comments --- tedana/metrics/_utils.py | 15 +++++++++++++- tedana/metrics/collect.py | 13 ------------ tedana/metrics/external.py | 42 +++++++++++++++++++------------------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/tedana/metrics/_utils.py b/tedana/metrics/_utils.py index b781acb1a..a30d4c611 100644 --- a/tedana/metrics/_utils.py +++ b/tedana/metrics/_utils.py @@ -12,7 +12,20 @@ def add_external_dependencies(dependency_config, external_regressor_config): """ Add dependency information in external regressors are inputted. - TODO Add a docstring + Parameters + ---------- + dependency_config: :obj:`dict` + A dictionary stored in ./config/metrics.json with information on all the + internally defined metrics like kappa and rho + external_regressor_config: :obj:`dict` + A dictionary with info for fitting external regressors + to component time series + + Returns + ------- + dependency_config: :obj:`dict` + A dictionary with the internally defined regressors inputted with this parameter + and the information for fitting external regressors defined in external_regressor_config """ # Add "external regressors" and an existing input dependency_config["inputs"].append("external regressors") diff --git a/tedana/metrics/collect.py b/tedana/metrics/collect.py index 1d4a997fb..d71754886 100644 --- a/tedana/metrics/collect.py +++ b/tedana/metrics/collect.py @@ -630,19 +630,6 @@ def get_metadata(comptable): }, } - if any(col.endswith("_correlation") for col in comptable.columns): - external_correlations = [col for col in comptable.columns if col.endswith("_correlation")] - for col in external_correlations: - original_col = col.replace("_correlation", "") - metric_metadata[col] = { - "LongName": f"{original_col}-component correlation", - "Description": ( - "Correlation between the component time series and the external regressor " - f"{original_col}." - ), - "Units": "Pearson correlation coefficient", - } - # There are always components in the comptable, definitionally metric_metadata["Component"] = { "LongName": "Component identifier", diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index 2b27392c9..40f378b93 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -1,4 +1,4 @@ -"""Metrics unrelated to TE-(in)dependence.""" +"""Metrics based on correlations of component time series to external time series.""" import logging import re @@ -19,7 +19,7 @@ class RegressError(Exception): pass -def load_validate_external_regressors(external_regressors, external_regressor_config, n_time): +def load_validate_external_regressors(external_regressors, external_regressor_config, n_vols): """Load and validate external regressors and descriptors in dictionary. Parameters @@ -29,7 +29,7 @@ def load_validate_external_regressors(external_regressors, external_regressor_co external_regressor_config: :obj:`dict` A dictionary with info for fitting external regressors to component time series - n_time: :obj:`int` + n_vols: :obj:`int` Number of timepoints in the fMRI time series Returns @@ -49,13 +49,13 @@ def load_validate_external_regressors(external_regressors, external_regressor_co raise ValueError(f"Cannot load tsv file with external regressors: {external_regressors}") external_regressor_config = validate_extern_regress( - external_regressors, external_regressor_config, n_time + external_regressors, external_regressor_config, n_vols ) return external_regressors, external_regressor_config -def validate_extern_regress(external_regressors, external_regressor_config, n_time): +def validate_extern_regress(external_regressors, external_regressor_config, n_vols): """ Confirm that provided external regressor dictionary is valid and matches data. @@ -73,7 +73,7 @@ def validate_extern_regress(external_regressors, external_regressor_config, n_ti external_regressor_config : :obj:`dict` Information describing the external regressors and method to use for fitting and statistical tests - n_time : :obj:`int` + n_vols : :obj:`int` The number of time point in the fMRI time series Returns @@ -156,10 +156,10 @@ def validate_extern_regress(external_regressors, external_regressor_config, n_ti f"{regressor_names - expected_regressor_names}" ) - if len(external_regressors.index) != n_time: + if len(external_regressors.index) != n_vols: err_msg += ( f"External Regressors have {len(external_regressors.index)} timepoints\n" - f"while fMRI data have {n_time} timepoints" + f"while fMRI data have {n_vols} timepoints" ) if err_msg: @@ -196,24 +196,24 @@ def fit_regressors(comptable, external_regressors, external_regressor_config, mi Component metric table. Same as inputted, with additional columns for metrics related to fitting the external regressors """ - n_time = mixing.shape[0] + n_vols = mixing.shape[0] # If the order of polynomial detrending is specified, then pass to make_detrend_regressors # otherwise the function sets a detrending polynomial order if external_regressor_config["detrend"] is True: - detrend_regressors = make_detrend_regressors(n_time, polort=None) + detrend_regressors = make_detrend_regressors(n_vols, polort=None) elif ( isinstance(external_regressor_config["detrend"], int) and external_regressor_config["detrend"] > 0 ): detrend_regressors = make_detrend_regressors( - n_time, polort=external_regressor_config["detrend"] + n_vols, polort=external_regressor_config["detrend"] ) else: LGR.warning( "External regressor fitted without detrending fMRI time series. Only removing mean" ) - detrend_regressors = make_detrend_regressors(n_time, polort=0) + detrend_regressors = make_detrend_regressors(n_vols, polort=0) if external_regressor_config["calc_stats"].lower() == "f": # external_regressors = pd.concat([external_regressors, detrend_regressors]) @@ -232,32 +232,32 @@ def fit_regressors(comptable, external_regressors, external_regressor_config, mi return comptable -def make_detrend_regressors(n_time, polort=None): +def make_detrend_regressors(n_vols, polort=None): """ Create polynomial detrending regressors to use for removing slow drifts from data. Parameters ---------- - n_time : :obj:`int` + n_vols : :obj:`int` The number of time point in the fMRI time series polort : :obj:`int` or :obj:`NoneType` The number of polynomial regressors to create (i.e. 3 is x^0, x^1, x^2) - If None, then this is set to 1+floor(n_time/150) + If None, then this is set to 1+floor(n_vols/150) Returns ------- - detrend_regressors: (n_time x polort) :obj:`pandas.DataFrame` + detrend_regressors: (n_vols x polort) :obj:`pandas.DataFrame` Dataframe containing the detrending regressor time series x^0 = 1. All other regressors are zscored so that they have a mean of 0 and a stdev of 1. Dataframe column labels are polort0 - polort{polort-1} """ if polort is None: - polort = int(1 + np.floor(n_time / 150)) + polort = int(1 + np.floor(n_vols / 150)) # create polynomial detrending regressors -> each additive term leads # to more points of transformation [curves] - detrend_regressors = np.zeros((n_time, polort)) + detrend_regressors = np.zeros((n_vols, polort)) # create polynomial detrended to the power of 0 [1's], # **1 [linear trend -> f(x) = a + bx], # **2 [quadratic trend -> f(x) = a + bx + cx²], @@ -266,7 +266,7 @@ def make_detrend_regressors(n_time, polort=None): for idx in range(polort): # create a linear space with numbers in range [-1,1] because the mean = 0, # and include the number of timepoints for each regressor - tmp = np.linspace(-1, 1, num=n_time) ** idx + tmp = np.linspace(-1, 1, num=n_vols) ** idx if idx == 0: detrend_regressors[:, idx] = tmp detrend_labels = ["polort0"] @@ -315,7 +315,7 @@ def fit_mixing_to_regressors( mixing : (T x C) array_like Mixing matrix for converting input data to component space, where `C` is components and `T` is the same as in `data_cat` - detrend_regressors: (n_time x polort) :obj:`pandas.DataFrame` + detrend_regressors: (n_vols x polort) :obj:`pandas.DataFrame` Dataframe containing the detrending regressor time series Returns @@ -398,7 +398,7 @@ def build_fstat_regressor_models( external_regressor_config : :obj:`dict` Information describing the external regressors and method to use for fitting and statistical tests - detrend_regressors: (n_time x polort) :obj:`pandas.DataFrame` + detrend_regressors: (n_vols x polort) :obj:`pandas.DataFrame` Dataframe containing the detrending regressor time series Returns From 1ba5f2748247ff87248b9e0420338af276988b15 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Thu, 2 May 2024 16:35:16 -0400 Subject: [PATCH 56/81] reworded docstring --- tedana/metrics/external.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index 40f378b93..ae2f60604 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -1,4 +1,4 @@ -"""Metrics based on correlations of component time series to external time series.""" +"""Metrics based on fits of component time series to external time series.""" import logging import re From 33569ab2e41c07b9eaa63a2a6ea51081c5a29155 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Thu, 2 May 2024 17:08:01 -0400 Subject: [PATCH 57/81] Added type hints to external.py --- tedana/metrics/external.py | 143 +++++++------------------------------ 1 file changed, 27 insertions(+), 116 deletions(-) diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index ae2f60604..c03986941 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -2,6 +2,7 @@ import logging import re +from typing import Dict, Tuple import numpy as np import pandas as pd @@ -19,7 +20,9 @@ class RegressError(Exception): pass -def load_validate_external_regressors(external_regressors, external_regressor_config, n_vols): +def load_validate_external_regressors( + external_regressors: Dict, external_regressor_config: Dict, n_vols: int +) -> Dict: """Load and validate external regressors and descriptors in dictionary. Parameters @@ -55,7 +58,9 @@ def load_validate_external_regressors(external_regressors, external_regressor_co return external_regressors, external_regressor_config -def validate_extern_regress(external_regressors, external_regressor_config, n_vols): +def validate_extern_regress( + external_regressors: Dict, external_regressor_config: Dict, n_vols: int +) -> Dict: """ Confirm that provided external regressor dictionary is valid and matches data. @@ -168,7 +173,12 @@ def validate_extern_regress(external_regressors, external_regressor_config, n_vo return external_regressor_config -def fit_regressors(comptable, external_regressors, external_regressor_config, mixing): +def fit_regressors( + comptable: pd.DataFrame, + external_regressors: pd.DataFrame, + external_regressor_config: Dict, + mixing: np._typing.ArrayLike, +) -> pd.DataFrame: """ Fit regressors to the mixing matrix. @@ -232,7 +242,7 @@ def fit_regressors(comptable, external_regressors, external_regressor_config, mi return comptable -def make_detrend_regressors(n_vols, polort=None): +def make_detrend_regressors(n_vols: int, polort: int | None = None) -> pd.DataFrame: """ Create polynomial detrending regressors to use for removing slow drifts from data. @@ -286,12 +296,12 @@ def make_detrend_regressors(n_vols, polort=None): def fit_mixing_to_regressors( - comptable, - external_regressors, - external_regressor_config, - mixing, - detrend_regressors, -): + comptable: pd.DataFrame, + external_regressors: pd.DataFrame, + external_regressor_config: Dict, + mixing: np._typing.ArrayLike, + detrend_regressors: pd.DataFrame, +) -> pd.DataFrame: """ Compute Linear Model and calculate F statistics and P values for combinations of regressors. @@ -385,8 +395,10 @@ def fit_mixing_to_regressors( def build_fstat_regressor_models( - external_regressors, external_regressor_config, detrend_regressors -): + external_regressors: pd.DataFrame, + external_regressor_config: Dict, + detrend_regressors: pd.DataFrame, +) -> Dict: """ Combine detrending all or subsets of external regressors to make models to fit and test. @@ -483,25 +495,12 @@ def build_fstat_regressor_models( f"'{no_pmodel}': {keep_labels}" ) - # vestigial codethat was used to check outputs and might be worth reviving - # if show_plot: - # fig = plt.figure(figsize=(10, 10)) - # ax = fig.add_subplot(3, 2, 1) - # ax.plot(detrend_regressors) - # plt.title("detrend") - # for idx, reg_cat in enumerate(regress_categories): - # if idx < 5: - # ax = fig.add_subplot(3, 2, idx + 2) - # ax.plot(stats.zscore(categorized_regressors[reg_cat].to_numpy(), axis=0)) - # plt.title(reg_cat) - # plt.savefig( - # f"{prefix}_ModelRegressors.jpeg", pil_kwargs={"quality": 20}, dpi="figure" - # ) # could also be saves as .eps - return regressor_models -def fit_model_with_stats(y, regressor_models, base_label, full_label="full"): +def fit_model_with_stats( + y: np._typing.ArrayLike, regressor_models: Dict, base_label: str, full_label: str = "full" +) -> Tuple[np._typing.ArrayLike, np._typing.ArrayLike, np._typing.ArrayLike, np._typing.ArrayLike]: """ Fit full and partial models and calculate F stats, R2, and p values. @@ -558,92 +557,4 @@ def fit_model_with_stats(y, regressor_models, base_label, full_label="full"): r2_vals = 1 - np.divide(sse_full, sse_base) print(y.shape) - # Vestigial code for testing that might be worth reviving - # Plots the fits for the first 20 components - # if show_plot: - # plt.clf() - # fig = plt.figure(figsize=(20, 24)) - # for idx in range(30): - # # print('Outer bound index: ', idx) - - # if idx < Y.shape[1]: # num of components - # # print('Actual axis index: ', idx) - # ax = fig.add_subplot(5, 6, idx + 1) # this axis index starts from 1 - # plot_fit( - # ax, - # Y[:, idx], - # betas_full[:, idx], - # regressor_models["full"], - # betas_base=betas_base[:, idx], - # X_base=regressor_models[base_label], - # F_val=F_vals[idx], - # p_val=p_vals[idx], - # R2_val=R2_vals[idx], - # SSE_base=SSE_base[idx], - # SSE_full=SSE_full[idx], - # base_legend=base_label, - # ) - # base_save_label = base_label.replace(" ", "_") - # plt.savefig( - # f"{prefix}_ModelFits_{base_save_label}.jpeg", - # pil_kwargs={"quality": 20}, - # dpi="figure", - # ) # could also be saved as eps - return betas_full, f_vals, p_vals, r2_vals - - -# Vestigial code that was used for testing accuracy of some variables -# Might be worth reviving -# def plot_fit( -# ax, -# Y, -# betas_full, -# X_full, -# betas_base=None, -# X_base=None, -# F_val=None, -# p_val=None, -# R2_val=None, -# SSE_base=None, -# SSE_full=None, -# base_legend="base fit", -# ): -# """ -# plot_fit: Plot the component time series and the fits to the full and base models - -# INPUTS: -# ax: axis handle for the figure subplot -# Y: The ICA component time series to fit to -# betas_full: The full model fitting parameters -# X_full: The time series for the full model - -# Optional: -# betas_base, X_base=None: Model parameters and time series for base model -# (not plotted if absent) -# F_val, p_val, R2_val, SSE_base, SSE_full: Fit statistics to include with each plot -# base_legend: A description of what the base model is to include in the legent -# """ - -# ax.plot(Y, color="black") -# ax.plot( -# np.matmul(X_full, betas_full.T), color="red" -# ) # the 'red' plot is the matrix-multiplication product of the time series * -# if (type(betas_base) != "NoneType") and (type(X_base) != "NoneType"): -# ax.plot(np.matmul(X_base, betas_base.T), color="green") -# ax.text( -# 250, -# 2, -# f"F={np.around(F_val, decimals=4)}\np={np.around(p_val, decimals=4)} -# \nR2={np.around(R2_val, decimals=4)}\nSSE_base={np.around(SSE_base, -# decimals=4)}\nSSE_full={np.around(SSE_full, decimals=4)}", -# ) -# ax.legend(["ICA Component", "Full fit", f"{base_legend} fit"], loc="best") -# else: -# ax.text( -# 250, -# 2, -# f"F={np.around(F_val, decimals=4)}\np={np.around(p_val, decimals=4)}\nR2= -# {np.around(R2_val, decimals=4)}\nSSE_full={np.around(SSE_full, decimals=4)}", -# ) -# ax.legend(["ICA Component", "Full fit"], loc="best") From e81017a2cd6f552bb42b4e0f5f166edf086fec18 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Thu, 2 May 2024 22:22:47 -0400 Subject: [PATCH 58/81] fixed external.py type hints --- tedana/metrics/external.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index c03986941..5bd768637 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -5,6 +5,7 @@ from typing import Dict, Tuple import numpy as np +import numpy.typing as npt import pandas as pd from scipy import stats @@ -177,7 +178,7 @@ def fit_regressors( comptable: pd.DataFrame, external_regressors: pd.DataFrame, external_regressor_config: Dict, - mixing: np._typing.ArrayLike, + mixing: npt.NDArray, ) -> pd.DataFrame: """ Fit regressors to the mixing matrix. @@ -242,7 +243,7 @@ def fit_regressors( return comptable -def make_detrend_regressors(n_vols: int, polort: int | None = None) -> pd.DataFrame: +def make_detrend_regressors(n_vols: int, polort: int = None) -> pd.DataFrame: """ Create polynomial detrending regressors to use for removing slow drifts from data. @@ -299,7 +300,7 @@ def fit_mixing_to_regressors( comptable: pd.DataFrame, external_regressors: pd.DataFrame, external_regressor_config: Dict, - mixing: np._typing.ArrayLike, + mixing: npt.NDArray, detrend_regressors: pd.DataFrame, ) -> pd.DataFrame: """ @@ -499,8 +500,8 @@ def build_fstat_regressor_models( def fit_model_with_stats( - y: np._typing.ArrayLike, regressor_models: Dict, base_label: str, full_label: str = "full" -) -> Tuple[np._typing.ArrayLike, np._typing.ArrayLike, np._typing.ArrayLike, np._typing.ArrayLike]: + y: npt.NDArray, regressor_models: Dict, base_label: str, full_label: str = "full" +) -> Tuple[npt.NDArray, npt.NDArray, npt.NDArray, npt.NDArray]: """ Fit full and partial models and calculate F stats, R2, and p values. From 8625c9cdb9c116a7dce1672ca181b2c23f6e735c Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Thu, 2 May 2024 23:24:37 -0400 Subject: [PATCH 59/81] type hints to _utils collect and component_selector --- tedana/metrics/_utils.py | 17 ++++++++--- tedana/metrics/collect.py | 31 ++++++++++--------- tedana/selection/component_selector.py | 42 ++++++++++++++++++-------- 3 files changed, 58 insertions(+), 32 deletions(-) diff --git a/tedana/metrics/_utils.py b/tedana/metrics/_utils.py index a30d4c611..c40e52e52 100644 --- a/tedana/metrics/_utils.py +++ b/tedana/metrics/_utils.py @@ -1,14 +1,16 @@ """Miscellaneous utility functions for metric calculation.""" import logging +from typing import Dict, List import numpy as np +import numpy.typing as npt from scipy import stats LGR = logging.getLogger("GENERAL") -def add_external_dependencies(dependency_config, external_regressor_config): +def add_external_dependencies(dependency_config: Dict, external_regressor_config: Dict) -> Dict: """ Add dependency information in external regressors are inputted. @@ -46,7 +48,9 @@ def add_external_dependencies(dependency_config, external_regressor_config): return dependency_config -def dependency_resolver(dict_, requested_metrics, base_inputs): +def dependency_resolver( + dict_: Dict, requested_metrics: List[str], base_inputs: List[str] +) -> List[str]: """Identify all necessary metrics based on a list of requested metrics. This also determines which metrics each requested metric requires to be calculated, @@ -96,7 +100,7 @@ def dependency_resolver(dict_, requested_metrics, base_inputs): return required_metrics -def determine_signs(weights, axis=0): +def determine_signs(weights: npt.NDArray, axis: int = 0) -> npt.NDArray: """Determine component-wise optimal signs using voxel-wise parameter estimates. Parameters @@ -104,6 +108,9 @@ def determine_signs(weights, axis=0): weights : (S x C) array_like Parameter estimates for optimally combined data against the mixing matrix. + axis : int + The axis to calculate the weights over. + Default is 0 Returns ------- @@ -118,7 +125,7 @@ def determine_signs(weights, axis=0): return signs.astype(int) -def flip_components(*args, signs): +def flip_components(*args: npt.NDArray, signs: npt.NDArray) -> npt.NDArray: """Flip an arbitrary set of input arrays based on a set of signs. Parameters @@ -149,7 +156,7 @@ def flip_components(*args, signs): return [arg * signs for arg in args] -def check_mask(data, mask): +def check_mask(data: npt.NDArray, mask: npt.NDArray) -> None: """Check that no zero-variance voxels remain in masked data. Parameters diff --git a/tedana/metrics/collect.py b/tedana/metrics/collect.py index d71754886..b1d46ebd9 100644 --- a/tedana/metrics/collect.py +++ b/tedana/metrics/collect.py @@ -2,8 +2,10 @@ import logging import os.path as op +from typing import Dict, List, Tuple, Union import numpy as np +import numpy.typing as npt import pandas as pd from tedana import io, utils @@ -22,17 +24,17 @@ def generate_metrics( *, - data_cat, - data_optcom, - mixing, - adaptive_mask, - tes, - io_generator, - label, - external_regressors=None, - external_regressor_config=None, + data_cat: npt.NDArray, + data_optcom: npt.NDArray, + mixing: npt.NDArray, + adaptive_mask: npt.NDArray, + tes: Union[List[int], List[float], npt.NDArray], + io_generator: io.OutputGenerator, + label: str, + external_regressors: Union[pd.DataFrame, None] = None, + external_regressor_config: Union[Dict, None] = None, metrics=None, -): +) -> Tuple[pd.DataFrame, Dict]: """Fit TE-dependence and -independence models to components. Parameters @@ -420,7 +422,7 @@ def generate_metrics( return comptable, external_regressor_config -def get_metadata(comptable): +def get_metadata(comptable: pd.DataFrame) -> Dict: """Fill in metric metadata for a given comptable. Parameters @@ -430,9 +432,10 @@ def get_metadata(comptable): Returns ------- - A dict containing the metadata for each column in the comptable for - which we have a metadata description, plus the "Component" metadata - description (always). + metric_metadata : dict + The metadata for each column in the comptable for + which we have a metadata description, plus the "Component" metadata + description (always). """ metric_metadata = {} if "kappa" in comptable: diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 7760a56e4..ab26df213 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -3,7 +3,9 @@ import inspect import logging import os.path as op +from typing import Dict, List, Union +import pandas as pd from numpy import asarray from tedana.io import load_json @@ -33,7 +35,7 @@ class TreeError(Exception): pass -def load_config(tree): +def load_config(tree: str) -> Dict: """Load the json file with the decision tree and validate the fields in the decision tree. Parameters @@ -74,7 +76,7 @@ def load_config(tree): return validate_tree(dectree) -def validate_tree(tree): +def validate_tree(tree: Dict) -> Dict: """Confirm that provided `tree` is a valid decision tree. Parameters @@ -286,7 +288,7 @@ def validate_tree(tree): class ComponentSelector: """Load and classify components based on a specified ``tree``.""" - def __init__(self, tree): + def __init__(self, tree: str): """Initialize the class using the info specified in the json file ``tree``. Parameters @@ -310,7 +312,12 @@ def __init__(self, tree): self.classification_tags = set(self.tree["classification_tags"]) self.tree["used_metrics"] = set(self.tree.get("used_metrics", [])) - def select(self, component_table, cross_component_metrics={}, status_table=None): + def select( + self, + component_table: pd.DataFrame, + cross_component_metrics: Dict = {}, + status_table: Union[pd.DataFrame, None] = None, + ): """Apply the decision tree to data. Using the validated tree in ``ComponentSelector`` to run the decision @@ -503,7 +510,7 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) self.are_all_components_accepted_or_rejected() - def add_manual(self, indices, classification): + def add_manual(self, indices: List[int], classification: str): """Add nodes that will manually classify components. Parameters @@ -527,16 +534,25 @@ def add_manual(self, indices, classification): } ) - def check_null(self, params, fcn): + def check_null(self, params: Dict, fcn: str) -> Dict: """ Check that required parameters for selection node functions are attributes in the class. Error if any are undefined. + Parameters + ---------- + params : :obj:`dict` + The keys and values for the inputted parameters + fcn : :obj:`str` + The name of a component selection function in selection_nodes.py + Returns ------- params : :obj:`dict` - The keys and values for the inputted parameters + The keys and values for the inputted parameters. + If a parameter's value was defined in self.cross_component_metrics + then that value is also in params when returned """ for key, val in params.items(): if val is None: @@ -598,12 +614,12 @@ def are_all_components_accepted_or_rejected(self): ) @property - def n_comps_(self): + def n_comps_(self) -> int: """The number of components in the component table.""" return len(self.component_table_) @property - def likely_bold_comps_(self): + def likely_bold_comps_(self) -> int: """A boolean :obj:`pandas.Series` of components that are tagged "Likely BOLD".""" likely_bold_comps = self.component_table_["classification_tags"].copy() for idx in range(len(likely_bold_comps)): @@ -614,22 +630,22 @@ def likely_bold_comps_(self): return likely_bold_comps @property - def n_likely_bold_comps_(self): + def n_likely_bold_comps_(self) -> int: """The number of components that are tagged "Likely BOLD".""" return self.likely_bold_comps_.sum() @property - def accepted_comps_(self): + def accepted_comps_(self) -> pd.Series: """A boolean :obj:`pandas.Series` of components that are accepted.""" return self.component_table_["classification"] == "accepted" @property - def n_accepted_comps_(self): + def n_accepted_comps_(self) -> int: """The number of components that are accepted.""" return self.accepted_comps_.sum() @property - def rejected_comps_(self): + def rejected_comps_(self) -> pd.Series: """A boolean :obj:`pandas.Series` of components that are rejected.""" return self.component_table_["classification"] == "rejected" From 15e543f7df56bbb22a5d6b299ebe8eda9cc63f3a Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Fri, 3 May 2024 11:15:47 -0400 Subject: [PATCH 60/81] type hints and doc improvements in selection_utils --- tedana/metrics/external.py | 8 +- tedana/selection/selection_utils.py | 121 +++++++++++++++++----------- 2 files changed, 76 insertions(+), 53 deletions(-) diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index 5bd768637..9622312e7 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -2,7 +2,7 @@ import logging import re -from typing import Dict, Tuple +from typing import Dict, Tuple, Union import numpy as np import numpy.typing as npt @@ -22,8 +22,8 @@ class RegressError(Exception): def load_validate_external_regressors( - external_regressors: Dict, external_regressor_config: Dict, n_vols: int -) -> Dict: + external_regressors: str, external_regressor_config: Dict, n_vols: int +) -> Tuple[pd.DataFrame, Dict]: """Load and validate external regressors and descriptors in dictionary. Parameters @@ -243,7 +243,7 @@ def fit_regressors( return comptable -def make_detrend_regressors(n_vols: int, polort: int = None) -> pd.DataFrame: +def make_detrend_regressors(n_vols: int, polort: Union[int, None] = None) -> pd.DataFrame: """ Create polynomial detrending regressors to use for removing slow drifts from data. diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index fff33e1c9..c358564b4 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -3,9 +3,12 @@ import copy import logging import re +from typing import Dict, List, Tuple, Union import numpy as np +import pandas as pd +# from tedana.selection.component_selector import ComponentSelector from tedana.stats import getfbounds LGR = logging.getLogger("GENERAL") @@ -16,13 +19,13 @@ ############################################################## -def selectcomps2use(component_table, decide_comps): +def selectcomps2use( + component_table: pd.DataFrame, decide_comps: Union[str, List[str], List[int]] +) -> List[int]: """Get a list of component numbers that fit the classification types in ``decide_comps``. Parameters ---------- - component_table : :obj:`~pandas.DataFrame` - The component_table with metrics and labels for each ICA component component_table : :obj:`~pandas.DataFrame` The component_table with metrics and labels for each ICA component decide_comps : :obj:`str` or :obj:`list[str]` or :obj:`list[int]` @@ -87,14 +90,14 @@ def selectcomps2use(component_table, decide_comps): def change_comptable_classifications( - selector, - if_true, - if_false, - decision_boolean, - tag_if_true=None, - tag_if_false=None, - dont_warn_reclassify=False, -): + selector, # ComponentSelector + if_true: str, + if_false: str, + decision_boolean: pd.Series, + tag_if_true: str = None, + tag_if_false: str = None, + dont_warn_reclassify: bool = False, +): # -> Tuple[ComponentSelector, int, int] """ Change or don't change the component classification. @@ -170,13 +173,13 @@ def change_comptable_classifications( def comptable_classification_changer( - selector, - boolstate, - classify_if, - decision_boolean, - tag_if=None, - dont_warn_reclassify=False, -): + selector, # : ComponentSelector, + boolstate: bool, + classify_if: str, + decision_boolean: pd.Series, + tag_if: Union[str, None] = None, + dont_warn_reclassify: bool = False, +): # -> ComponentSelector """Implement the component classification changes from ``change_comptable_classifications``. Parameters @@ -285,7 +288,7 @@ def comptable_classification_changer( return selector -def clean_dataframe(component_table): +def clean_dataframe(component_table: pd.DataFrame) -> pd.DataFrame: """ Reorder columns in component table. @@ -317,7 +320,7 @@ def clean_dataframe(component_table): ################################################# -def expand_dict(node, field, metrics): +def expand_dict(node: Dict, field: str, metrics: List[str]) -> Tuple[bool, List[Dict]]: """Expand a dictionary with regular expressions. Parameters @@ -329,6 +332,7 @@ def expand_dict(node, field, metrics): a regular expression, denoted by starting with "^". field : :obj:`str` The key in the ``node`` dictionary that should be expanded. + Current keys that could include regular expressions are "parameters" and "kwargs" metrics : list of str List of metric names to compare regular expressions against. @@ -358,7 +362,7 @@ def expand_dict(node, field, metrics): return regex_found, out_nodes -def expand_node(node, metrics): +def expand_node(node: Dict, metrics: List[str]): """Expand node definitions with regular expressions. Recursively expand a node so that any regular expressions are replaced with @@ -368,13 +372,19 @@ def expand_node(node, metrics): Parameters ---------- - node : dict + node : :obj:`dict` A dictionary containing nested dictionaries called "parameters" and, optionally, "kwargs". Any of the values in the "parameters" or "kwargs" dictionaries may be a regular expression, denoted by starting with "^". - metrics : list of str - List of metric names. + metrics : :obj:`list[str]` + List of metric names to compare regular expressions against. + + Returns + ------- + out_nodes or expanded_out_nodes: :obj:`list[dict]` + A list of dictionaries with regular expressions replaced by all + matching values in the 'metrics' list. """ regex_found, out_nodes = expand_dict(node, "parameters", metrics) if not regex_found: @@ -385,25 +395,30 @@ def expand_node(node, metrics): out_nodes = [copy.deepcopy(node)] return out_nodes - real_out_nodes = [] + expanded_out_nodes = [] for out_node in out_nodes: - real_out_nodes += expand_node(out_node, metrics) + expanded_out_nodes += expand_node(out_node, metrics) - return real_out_nodes + return expanded_out_nodes -def expand_nodes(tree, metrics): - """Expand all nodes in a decision tree. +def expand_nodes(tree: Dict, metrics: List[str]) -> Dict: + """Expand regular expressions in all nodes of a decision tree. Parameters ---------- - tree : dict - A dictionary containing nested dictionaries called "parameters" and, - optionally, "kwargs". - Any of the values in the "parameters" or "kwargs" dictionaries may be - a regular expression, denoted by starting with "^". - metrics : list of str - List of metric names. + tree : :obj:`dict` + A dictionary containing the info needed to run the component selection decision tree. + tree["nodes"] is a list of dictionaries with each element is a sept in the decision tree. + Each node contains "parameters" and optionally, "kwargs" that are used in funciton calls. + "parameters" or "kwargs" may include a regular expression, denoted by starting with "^". + metrics : :obj:`list[str]` + List of metric names like "kappa" and "rho" to compare regular expressions against. + + Returns + ------- + tree : :obj:`dict` + The same decision tree dictionary that was inputted with all regular expressions expanded. """ expanded_tree = copy.deepcopy(tree) expanded_tree["nodes"] = [] @@ -414,7 +429,11 @@ def expand_nodes(tree, metrics): return expanded_tree -def confirm_metrics_exist(component_table, necessary_metrics, function_name=None): +def confirm_metrics_exist( + component_table: pd.DataFrame, + necessary_metrics: List[str], + function_name: Union[str, None] = None, +) -> Union[None, bool]: """Confirm that all metrics declared in necessary_metrics are already included in comptable. Parameters @@ -422,11 +441,16 @@ def confirm_metrics_exist(component_table, necessary_metrics, function_name=None component_table : (C x M) :obj:`pandas.DataFrame` Component metric table. One row for each component, with a column for each metric. The index should be the component number. - necessary_metrics : :obj:`list` + necessary_metrics : :obj:`list[str]` A list of strings of metric names. function_name : :obj:`str` Text identifying the function name that called this function. + Returns + ------- + metrics_are_missing : :obj:`bool` + If there are no metrics missing, this returns True. + Raises ------ ValueError @@ -465,15 +489,15 @@ def confirm_metrics_exist(component_table, necessary_metrics, function_name=None def log_decision_tree_step( - function_name_idx, - comps2use, - decide_comps=None, - n_true=None, - n_false=None, - if_true=None, - if_false=None, - calc_outputs=None, -): + function_name_idx: str, + comps2use: Union[List[int], int], + decide_comps: Union[str, List[str], List[int], None] = None, + n_true: Union[int, None] = None, + n_false: Union[int, None] = None, + if_true: Union[str, None] = None, + if_false: Union[str, None] = None, + calc_outputs: Union[Dict, None] = None, +) -> None: """Log text to add after every decision tree calculation. Parameters @@ -481,7 +505,6 @@ def log_decision_tree_step( function_name_idx : :obj:`str` The name of the function that should be logged. By convention, this be "Step ``current_node_idx_``: function_name" - be "Step ``current_node_idx_``: function_name" comps2use : :obj:`list[int]` or -1 A list of component indices that should be used by a function. Only used to report no components found if empty and report @@ -541,7 +564,7 @@ def log_decision_tree_step( ) -def log_classification_counts(decision_node_idx, component_table): +def log_classification_counts(decision_node_idx: int, component_table: pd.DataFrame) -> None: """Log the total counts for each component classification in component_table. Parameters From c26b86d97438a78f7bab7e3d99d9cfb594f8251f Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Mon, 6 May 2024 12:07:31 -0400 Subject: [PATCH 61/81] no expand_node recursion --- tedana/selection/selection_utils.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index c358564b4..976bf0e29 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -8,7 +8,6 @@ import numpy as np import pandas as pd -# from tedana.selection.component_selector import ComponentSelector from tedana.stats import getfbounds LGR = logging.getLogger("GENERAL") @@ -382,7 +381,7 @@ def expand_node(node: Dict, metrics: List[str]): Returns ------- - out_nodes or expanded_out_nodes: :obj:`list[dict]` + out_nodes : :obj:`list[dict]` A list of dictionaries with regular expressions replaced by all matching values in the 'metrics' list. """ @@ -393,13 +392,8 @@ def expand_node(node: Dict, metrics: List[str]): if not regex_found: # Stop early and just return the node if no regular expressions were found out_nodes = [copy.deepcopy(node)] - return out_nodes - - expanded_out_nodes = [] - for out_node in out_nodes: - expanded_out_nodes += expand_node(out_node, metrics) - return expanded_out_nodes + return out_nodes def expand_nodes(tree: Dict, metrics: List[str]) -> Dict: From 3ffc62d4d24d30b729016e508513adda1720369c Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Tue, 7 May 2024 12:23:16 -0400 Subject: [PATCH 62/81] removed expand_nodes expand_node expand_dict --- pyproject.toml | 2 +- tedana/selection/component_selector.py | 3 - tedana/selection/selection_utils.py | 107 +------------------------ 3 files changed, 2 insertions(+), 110 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f757350e7..d74a6c3aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ dependencies = [ "mapca>=0.0.4,<=0.0.5", "matplotlib", "nibabel>=2.5.1,<=5.2.1", - "nilearn>=0.7,<=0.10.4", + "nilearn>=0.10.3,<=0.10.4", "numpy>=1.16,<=1.26.4", "pandas>=2.0,<=2.2.2", "pybtex", diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index ab26df213..fc3f791c0 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -13,7 +13,6 @@ from tedana.selection.selection_utils import ( clean_dataframe, confirm_metrics_exist, - expand_nodes, log_classification_counts, ) from tedana.utils import get_resource_path @@ -395,8 +394,6 @@ def select( # Construct an un-executed selector self.component_table_ = component_table.copy() - # Expand out metrics defined by regular expressions in the nodes - self.tree = expand_nodes(self.tree, self.component_table_.columns.tolist()) # this will crash the program with an error message if not all # necessary_metrics are in the comptable diff --git a/tedana/selection/selection_utils.py b/tedana/selection/selection_utils.py index 976bf0e29..350d53009 100644 --- a/tedana/selection/selection_utils.py +++ b/tedana/selection/selection_utils.py @@ -1,9 +1,8 @@ """Utility functions for tedana.selection.""" -import copy import logging import re -from typing import Dict, List, Tuple, Union +from typing import Dict, List, Union import numpy as np import pandas as pd @@ -319,110 +318,6 @@ def clean_dataframe(component_table: pd.DataFrame) -> pd.DataFrame: ################################################# -def expand_dict(node: Dict, field: str, metrics: List[str]) -> Tuple[bool, List[Dict]]: - """Expand a dictionary with regular expressions. - - Parameters - ---------- - node : dict - A dictionary containing nested dictionaries called "parameters" and, - optionally, "kwargs". - Any of the values in the "parameters" or "kwargs" dictionaries may be - a regular expression, denoted by starting with "^". - field : :obj:`str` - The key in the ``node`` dictionary that should be expanded. - Current keys that could include regular expressions are "parameters" and "kwargs" - metrics : list of str - List of metric names to compare regular expressions against. - - Returns - ------- - regex_found : :obj:`bool` - True if any regular expressions were found in the input dictionary - out_nodes : :obj:`list[dict]` - A list of dictionaries with regular expressions replaced by all - matching values in the 'metrics' list. - """ - regex_found = False - out_nodes = [] - for k, v in node.get(field, {}).items(): - if isinstance(v, str) and v.startswith("^"): - regex_found = True - replacements = [metric for metric in metrics if re.match(v, metric)] - if not replacements: - raise ValueError(f"No metrics matching regex '{v}' found.") - - for replacement in replacements: - LGR.warning(f"Replacing {v} with {replacement}") - mod_node = copy.deepcopy(node) - mod_node[field][k] = replacement - out_nodes.append(mod_node) - - return regex_found, out_nodes - - -def expand_node(node: Dict, metrics: List[str]): - """Expand node definitions with regular expressions. - - Recursively expand a node so that any regular expressions are replaced with - any matching values in the 'metrics' list. - Regular expressions may be present as the value of any key in the - subdictionaries "parameters" or "kwargs". - - Parameters - ---------- - node : :obj:`dict` - A dictionary containing nested dictionaries called "parameters" and, - optionally, "kwargs". - Any of the values in the "parameters" or "kwargs" dictionaries may be - a regular expression, denoted by starting with "^". - metrics : :obj:`list[str]` - List of metric names to compare regular expressions against. - - Returns - ------- - out_nodes : :obj:`list[dict]` - A list of dictionaries with regular expressions replaced by all - matching values in the 'metrics' list. - """ - regex_found, out_nodes = expand_dict(node, "parameters", metrics) - if not regex_found: - regex_found, out_nodes = expand_dict(node, "kwargs", metrics) - - if not regex_found: - # Stop early and just return the node if no regular expressions were found - out_nodes = [copy.deepcopy(node)] - - return out_nodes - - -def expand_nodes(tree: Dict, metrics: List[str]) -> Dict: - """Expand regular expressions in all nodes of a decision tree. - - Parameters - ---------- - tree : :obj:`dict` - A dictionary containing the info needed to run the component selection decision tree. - tree["nodes"] is a list of dictionaries with each element is a sept in the decision tree. - Each node contains "parameters" and optionally, "kwargs" that are used in funciton calls. - "parameters" or "kwargs" may include a regular expression, denoted by starting with "^". - metrics : :obj:`list[str]` - List of metric names like "kappa" and "rho" to compare regular expressions against. - - Returns - ------- - tree : :obj:`dict` - The same decision tree dictionary that was inputted with all regular expressions expanded. - """ - expanded_tree = copy.deepcopy(tree) - expanded_tree["nodes"] = [] - for node in tree["nodes"]: - nodes = expand_node(node, metrics) - expanded_tree["nodes"] += nodes - - return expanded_tree - - def confirm_metrics_exist( component_table: pd.DataFrame, necessary_metrics: List[str], From f0f580192c40c9c78b5a61ddc5ea025171b4d16e Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Wed, 15 May 2024 14:23:15 -0400 Subject: [PATCH 63/81] docstring lines break on punctuation --- tedana/metrics/_utils.py | 7 ++- tedana/metrics/collect.py | 26 +++++------ tedana/metrics/external.py | 89 +++++++++++++++++++------------------- 3 files changed, 61 insertions(+), 61 deletions(-) diff --git a/tedana/metrics/_utils.py b/tedana/metrics/_utils.py index c40e52e52..2d0f878a4 100644 --- a/tedana/metrics/_utils.py +++ b/tedana/metrics/_utils.py @@ -17,11 +17,10 @@ def add_external_dependencies(dependency_config: Dict, external_regressor_config Parameters ---------- dependency_config: :obj:`dict` - A dictionary stored in ./config/metrics.json with information on all the - internally defined metrics like kappa and rho + A dictionary stored in ./config/metrics.json + with information on all the internally defined metrics like kappa and rho external_regressor_config: :obj:`dict` - A dictionary with info for fitting external regressors - to component time series + A dictionary with info for fitting external regressors to component time series Returns ------- diff --git a/tedana/metrics/collect.py b/tedana/metrics/collect.py index b1d46ebd9..284b60131 100644 --- a/tedana/metrics/collect.py +++ b/tedana/metrics/collect.py @@ -44,12 +44,12 @@ def generate_metrics( data_optcom : (S x T) array_like Optimally combined data mixing : (T x C) array_like - Mixing matrix for converting input data to component space, where `C` - is components and `T` is the same as in `data_cat` + Mixing matrix for converting input data to component space, + where `C` is components and `T` is the same as in `data_cat` adaptive_mask : (S) array_like Array where each value indicates the number of echoes with good signal - for that voxel. This mask may be thresholded; for example, with values - less than 3 set to 0. + for that voxel. + This mask may be thresholded; for example, with values less than 3 set to 0. For more information on thresholding, see `make_adaptive_mask`. tes : list List of echo times associated with `data_cat`, in milliseconds @@ -58,8 +58,9 @@ def generate_metrics( label : str in ['ICA', 'PCA'] The label for this metric generation type external_regressors : None or :obj:`pandas.DataFrame`, optional - External regressors (e.g., motion parameters, physiological noise) to correlate with - ICA components. If None, no external regressor metrics will be calculated. + External regressors (e.g., motion parameters, physiological noise) + to correlate with ICA components. + If None, no external regressor metrics will be calculated. external_regressor_config : :obj:`dict` A dictionary for defining how to fit external regressors to component time series metrics : list @@ -68,11 +69,11 @@ def generate_metrics( Returns ------- comptable : (C x X) :obj:`pandas.DataFrame` - Component metric table. One row for each component, with a column for - each metric. The index is the component number. + Component metric table. One row for each component, with a column for each metric. + The index is the component number. external_regressor_config : :obj:`dict` - Information describing the external regressors and - method used for fitting and statistical tests (or None if none were inputed) + Info describing the external regressors and method used for fitting and statistical tests + (or None if none were inputed) """ # Load metric dependency tree from json file dependency_config = op.join(utils.get_resource_path(), "config", "metrics.json") @@ -433,9 +434,8 @@ def get_metadata(comptable: pd.DataFrame) -> Dict: Returns ------- metric_metadata : dict - The metadata for each column in the comptable for - which we have a metadata description, plus the "Component" metadata - description (always). + The metadata for each column in the comptable for which we have a metadata description, + plus the "Component" metadata description (always). """ metric_metadata = {} if "kappa" in comptable: diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index 9622312e7..1d07ab0dd 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -31,8 +31,7 @@ def load_validate_external_regressors( external_regressors: :obj:`str` Path and name of tsv file that includes external regressor time series external_regressor_config: :obj:`dict` - A dictionary with info for fitting external regressors - to component time series + A dictionary with info for fitting external regressors to component time series n_vols: :obj:`int` Number of timepoints in the fMRI time series @@ -42,10 +41,9 @@ def load_validate_external_regressors( Each column is a labelled regressor and the number of rows should match the number of timepoints in the fMRI time series external_regressor_config: :obj:`dict` - A validated dictionary with info for fitting external regressors - to component time series. If regex patterns like '^mot_.*$' are used to define - regressor names, this is replaced with a list of the match column names used in - external_regressors + A validated dictionary with info for fitting external regressors to component time series. + If regex patterns like '^mot_.*$' are used to define regressor names, + this is replaced with a list of the match column names used in external_regressors """ try: external_regressors = pd.read_table(external_regressors) @@ -67,9 +65,9 @@ def validate_extern_regress( Checks if expected keys are in external_regressor_config. Checks if any regressor labels in the dictionary are specified in the - user-defined external_regressors + user-defined external_regressors. Checks if the number of time points in the external regressors matches - the number of time point in the fMRI data + the number of time point in the fMRI data. Parameters ---------- @@ -86,9 +84,9 @@ def validate_extern_regress( ------- external_regressor_config: :obj:`dict` A validated dictionary with info for fitting external regressors - to component time series. If regex patterns like '^mot_.*$' are used to define - regressor names, this is replaced with a list of the match column names used in - external_regressors + to component time series. + If regex patterns like '^mot_.*$' are used to define regressor names, + this is replaced with a list of the match column names used in external_regressors Raises ------ @@ -189,8 +187,8 @@ def fit_regressors( Parameters ---------- comptable : (C x X) :obj:`pandas.DataFrame` - Component metric table. One row for each component, with a column for - each metric. The index is the component number. + Component metric table. One row for each component, + with a column for each metric. The index is the component number. external_regressors : :obj:`pandas.DataFrame` Each column is a labelled regressor and the number of rows should match the number of timepoints in the fMRI time series @@ -198,14 +196,14 @@ def fit_regressors( Information describing the external regressors and method to use for fitting and statistical tests mixing : (T x C) array_like - Mixing matrix for converting input data to component space, where `C` - is components and `T` is the same as in `data_cat` + Mixing matrix for converting input data to component space, + where `C` is components and `T` is the same as in `data_cat` Returns ------- comptable : (C x X) :obj:`pandas.DataFrame` - Component metric table. Same as inputted, with additional columns - for metrics related to fitting the external regressors + Component metric table. + Same as inputted, with added columns for metrics related to the external regressor fits """ n_vols = mixing.shape[0] @@ -259,8 +257,8 @@ def make_detrend_regressors(n_vols: int, polort: Union[int, None] = None) -> pd. ------- detrend_regressors: (n_vols x polort) :obj:`pandas.DataFrame` Dataframe containing the detrending regressor time series - x^0 = 1. All other regressors are zscored so that they have - a mean of 0 and a stdev of 1. + x^0 = 1. + All other regressors are zscored so that they have a mean of 0 and a stdev of 1. Dataframe column labels are polort0 - polort{polort-1} """ if polort is None: @@ -315,8 +313,9 @@ def fit_mixing_to_regressors( Parameters ---------- comptable : (C x X) :obj:`pandas.DataFrame` - Component metric table. One row for each component, with a column for - each metric. The index is the component number. + Component metric table. + One row for each component, with a column for each metric. + The index is the component number. external_regressors : :obj:`pandas.DataFrame` Each column is a labelled regressor and the number of rows should match the number of timepoints in the fMRI time series @@ -324,20 +323,19 @@ def fit_mixing_to_regressors( Information describing the external regressors and method to use for fitting and statistical tests mixing : (T x C) array_like - Mixing matrix for converting input data to component space, where `C` - is components and `T` is the same as in `data_cat` + Mixing matrix for converting input data to component space, + where `C` is components and `T` is the same as in `data_cat` detrend_regressors: (n_vols x polort) :obj:`pandas.DataFrame` Dataframe containing the detrending regressor time series Returns ------- comptable : (C x X) :obj:`pandas.DataFrame` - Component metric table. Same as inputted, with additional columns - for metrics related to fitting the external regressors. - There are new columns for F, R2, and p values for the full model - and all partial models. Names are "Fstat Full Model", "pval Full Model", - "R2stat Full Model" and "Full" is replaced by the partial model name - for each partial model + Component metric table. + Same as inputted, with added columns for metrics related to external regressor fits. + New columns for F, R2, and p values for the full model and all partial models. + Names are "Fstat Full Model", "pval Full Model", "R2stat Full Model", + and "Full" is replaced by the partial model name for each partial model """ LGR.info("Running fit_mixing_to_regressors") LGR.info(f"ICA matrix has {mixing.shape[0]} time points and {mixing.shape[1]} components") @@ -418,15 +416,15 @@ def build_fstat_regressor_models( ------- regressor_models: :obj:`dict` Each element in the dictionary is a numpy array defining the regressors in a - regressor model. The models that are always included are 'base' which is just the - detrending regressors, and 'full' which is all user-provided external regressors and - the detrending regressors. If there are partial models that are named in - external_regressor_config["f_stats_partial_models"] then each of those will have a - dictionary element named "no" then model name and the regressors included will be - everything except the specified regressors. That is "no motion" will include all - regressors except the motion regressors. This is for the F test which compares - the variance explained with the full model to the variance explained if the - regressors-of-interest for the partial model are removed. + regressor model. + The models that are always included are 'base' which is just the detrending regressors, + and 'full' which is all user-provided external regressors and the detrending regressors. + If partial models are named in external_regressor_config["f_stats_partial_models"], + then each of those will have a dictionary element named "no" then model name and the + regressors included will be everything except the specified regressors. + That is "no motion" will include all regressors except the motion regressors. + This is for the F test which compares the variance explained with the full model to the + variance explained if the regressors-of-interest for the partial model are removed. """ # The category titles to group each regressor if "f_stats_partial_models" in external_regressor_config: @@ -517,14 +515,17 @@ def fit_model_with_stats( Time by mixing matrix components for the time series for fitting regressor_models: :obj:`dict` Each element in the dictionary is a numpy array defining the regressors in a - regressor model. Inclues 'full', 'base' and partial models. + regressor model. + Inclues 'full', 'base' and partial models. base_label : :obj:`str` - The base model to compare the full model against. For F stat for the full - model, this should be 'base'. For partial models, this should be the name - of the partial model (i.e. "no motion"). + The base model to compare the full model against. + For F stat for the full model, this should be 'base'. + For partial models, this should be the name of the partial model (i.e. "no motion"). full_label : :obj:`str` - The full model to use. Default="full" but can also be "task_keep" if - tasks were inputted and want to compare results against task. + The full model to use. + Default="full". + "full" is expected if the goal is to compare all nuissance regressors to a base model. + "task_keep" for the special case of fitting pre-defined task-locked regressors. Returns ------- From 325011e3c65cc6f0ea43d1c4d9b8e0367297e091 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Wed, 15 May 2024 17:10:47 -0400 Subject: [PATCH 64/81] updating external tests and docs --- tedana/metrics/external.py | 44 ++++++------ ...xternal_regressors_motion_task_models.json | 2 +- tedana/tests/test_external_metrics.py | 67 ++++++++++++++----- 3 files changed, 70 insertions(+), 43 deletions(-) diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index 1d07ab0dd..03e54d8cd 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -60,13 +60,15 @@ def load_validate_external_regressors( def validate_extern_regress( external_regressors: Dict, external_regressor_config: Dict, n_vols: int ) -> Dict: - """ - Confirm that provided external regressor dictionary is valid and matches data. - - Checks if expected keys are in external_regressor_config. - Checks if any regressor labels in the dictionary are specified in the - user-defined external_regressors. - Checks if the number of time points in the external regressors matches + """Confirm external regressor dictionary matches data and expands regular expressions. + + Most keys in external_regressor_config are valided in component_selector.validate_tree + which is run when a component selector object is initialized. + This function validates external_regressor_config against the dataset-specific + external_regressors time series. + If the config names specific column labels with the f_stats_partial_models key, + then this confirms those column labels are used in external_regressors + Also checks if the number of time points in the external regressors matches the number of time point in the fMRI data. Parameters @@ -78,7 +80,7 @@ def validate_extern_regress( Information describing the external regressors and method to use for fitting and statistical tests n_vols : :obj:`int` - The number of time point in the fMRI time series + The number of time points in the fMRI time series Returns ------- @@ -86,7 +88,7 @@ def validate_extern_regress( A validated dictionary with info for fitting external regressors to component time series. If regex patterns like '^mot_.*$' are used to define regressor names, - this is replaced with a list of the match column names used in external_regressors + this is replaced with a list of the matching column names used in external_regressors Raises ------ @@ -101,9 +103,9 @@ def validate_extern_regress( # with the data in external_regressors # Currently column labels only need to be predefined for calc_stats==F - # or if "task_keep" is used. column_label_specifications will include the - # names of the column labels for both f_stats_partial_models and task_keep - # if eithe ror both are defined. + # and there are f_stats_partial_models or if "task_keep" is used. + # column_label_specifications will include the names of the column labels for + # both f_stats_partial_models and task_keep if either or both are defined. column_label_specifications = set() if "f_stats_partial_models" in set(external_regressor_config.keys()): column_label_specifications.update( @@ -178,8 +180,7 @@ def fit_regressors( external_regressor_config: Dict, mixing: npt.NDArray, ) -> pd.DataFrame: - """ - Fit regressors to the mixing matrix. + """Fit regressors to the mixing matrix. Uses correlation or F statistics in a linear model depending on the calc_stats value in external_regressor_config @@ -242,8 +243,7 @@ def fit_regressors( def make_detrend_regressors(n_vols: int, polort: Union[int, None] = None) -> pd.DataFrame: - """ - Create polynomial detrending regressors to use for removing slow drifts from data. + """Create polynomial detrending regressors to use for removing slow drifts from data. Parameters ---------- @@ -256,8 +256,7 @@ def make_detrend_regressors(n_vols: int, polort: Union[int, None] = None) -> pd. Returns ------- detrend_regressors: (n_vols x polort) :obj:`pandas.DataFrame` - Dataframe containing the detrending regressor time series - x^0 = 1. + Dataframe containing the detrending regressor time series x^0 = 1. All other regressors are zscored so that they have a mean of 0 and a stdev of 1. Dataframe column labels are polort0 - polort{polort-1} """ @@ -301,8 +300,7 @@ def fit_mixing_to_regressors( mixing: npt.NDArray, detrend_regressors: pd.DataFrame, ) -> pd.DataFrame: - """ - Compute Linear Model and calculate F statistics and P values for combinations of regressors. + """Compute Linear Model and calculate F statistics and P values for combinations of regressors. Equation: Y = XB + E - Y = each ICA component (mixing) @@ -398,8 +396,7 @@ def build_fstat_regressor_models( external_regressor_config: Dict, detrend_regressors: pd.DataFrame, ) -> Dict: - """ - Combine detrending all or subsets of external regressors to make models to fit and test. + """Combine detrending all or subsets of external regressors to make models to fit and test. Parameters ---------- @@ -500,8 +497,7 @@ def build_fstat_regressor_models( def fit_model_with_stats( y: npt.NDArray, regressor_models: Dict, base_label: str, full_label: str = "full" ) -> Tuple[npt.NDArray, npt.NDArray, npt.NDArray, npt.NDArray]: - """ - Fit full and partial models and calculate F stats, R2, and p values. + """Fit full and partial models and calculate F stats, R2, and p values. Math from page 11-14 of https://afni.nimh.nih.gov/pub/dist/doc/manual/3dDeconvolve.pdf Calculates Y=betas*X + error for the base and the full model diff --git a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json index 6d9951bc2..de82c1e28 100644 --- a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json +++ b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json @@ -19,7 +19,7 @@ "external_regressor_config": { "regress_ID": "Fmodel", "info": "Fits all external regressors to a single model using an F statistic", - "report": "Unspecified external nuissance regressors were fit to components using a linear model were rejected if they fit.", + "report": "Unspecified external nuisance regressors that were fit to components using a linear model were rejected if they fit.", "detrend": true, "calc_stats": "F", "f_stats_partial_models": ["Motion", "CSF"], diff --git a/tedana/tests/test_external_metrics.py b/tedana/tests/test_external_metrics.py index 34dc8573f..104766066 100644 --- a/tedana/tests/test_external_metrics.py +++ b/tedana/tests/test_external_metrics.py @@ -34,7 +34,7 @@ def sample_external_regressors(regress_choice="valid"): Returns ------- external_regressors : :obj:`pandas.DataFrame` External regressor table - n_time : :obj:`int` Number of time points (rows) in external_regressors + n_vols : :obj:`int` Number of time points (rows) in external_regressors """ sample_fname = op.join(THIS_DIR, "data", "external_regress_Ftest_3echo.tsv") @@ -45,9 +45,9 @@ def sample_external_regressors(regress_choice="valid"): elif regress_choice != "valid": raise ValueError(f"regress_choice is {regress_choice}, which is not a listed option") - n_time = len(external_regressors) + n_vols = len(external_regressors) - return external_regressors, n_time + return external_regressors, n_vols def sample_external_regressor_config(config_choice="valid"): @@ -94,14 +94,14 @@ def sample_external_regressor_config(config_choice="valid"): # validate_extern_regress -# ----------- +# ----------------------- def test_validate_extern_regress_succeeds(caplog): """Test validate_extern_regress works as expected.""" - external_regressors, n_time = sample_external_regressors() + external_regressors, n_vols = sample_external_regressors() external_regressor_config = sample_external_regressor_config() external_regressor_config_expanded = external.validate_extern_regress( - external_regressors, external_regressor_config, n_time + external_regressors, external_regressor_config, n_vols ) # The regex patterns should have been replaced with the full names of the regressors @@ -129,60 +129,91 @@ def test_validate_extern_regress_succeeds(caplog): # Shouldn't change anything, but making sure it runs caplog.clear() external_regressor_config = external.validate_extern_regress( - external_regressors, external_regressor_config_expanded, n_time + external_regressors, external_regressor_config_expanded, n_vols ) assert "WARNING" not in caplog.text # Removing all partial model and task_keep stuff to confirm it still runs caplog.clear() external_regressor_config = sample_external_regressor_config("no_task_partial") - external.validate_extern_regress(external_regressors, external_regressor_config, n_time) + external.validate_extern_regress(external_regressors, external_regressor_config, n_vols) assert caplog.text == "" # Removing "task_keep" from config to test if warning appears caplog.clear() external_regressor_config = sample_external_regressor_config("no_task") - external.validate_extern_regress(external_regressors, external_regressor_config, n_time) + external.validate_extern_regress(external_regressors, external_regressor_config, n_vols) assert "Regressor labels in external_regressors are not all included in F" in caplog.text - # Removing "task_keep" from config to test if warning appears + # Add "CSF" to "Motion" partial model (also in "CSF" partial model) to test if warning appears caplog.clear() external_regressor_config = sample_external_regressor_config("csf_in_mot") - external.validate_extern_regress(external_regressors, external_regressor_config, n_time) + external.validate_extern_regress(external_regressors, external_regressor_config, n_vols) assert "External regressors used in more than one partial model" in caplog.text def test_validate_extern_regress_fails(): """Test validate_extern_regress fails when expected.""" - external_regressors, n_time = sample_external_regressors() + external_regressors, n_vols = sample_external_regressors() external_regressor_config = sample_external_regressor_config() # If there are a different number of time points in the fMRI data and external regressors with pytest.raises( - external.RegressError, match=f"while fMRI data have {n_time - 1} timepoints" + external.RegressError, match=f"while fMRI data have {n_vols - 1} timepoints" ): external.validate_extern_regress( - external_regressors, external_regressor_config, n_time - 1 + external_regressors, external_regressor_config, n_vols - 1 ) # If no external regressor labels match the regex label in config external_regressor_config = sample_external_regressor_config("unmatched_regex") with pytest.raises(external.RegressError, match="No external regressor labels matching regex"): - external.validate_extern_regress(external_regressors, external_regressor_config, n_time) + external.validate_extern_regress(external_regressors, external_regressor_config, n_vols) # If a regressor expected in the config is not in external_regressors # Run successfully to expand Motion labels in config and then create error # when "Mot_Y" is in the config, but removed from external_regressros external_regressor_config = sample_external_regressor_config() external_regressor_config_expanded = external.validate_extern_regress( - external_regressors, external_regressor_config, n_time + external_regressors, external_regressor_config, n_vols ) - external_regressors, n_time = sample_external_regressors("no_mot_y_column") + external_regressors, n_vols = sample_external_regressors("no_mot_y_column") with pytest.raises( external.RegressError, match="Inputed regressors in external_regressors do not include all expected", ): external.validate_extern_regress( - external_regressors, external_regressor_config_expanded, n_time + external_regressors, external_regressor_config_expanded, n_vols ) + + +# load_validate_external_regressors +# --------------------------------- + + +def test_load_validate_external_regressors_fails(): + """Test load_validate_external_regressors fails when not given a tsv file.""" + + external_regressors = "NotATSVFile.tsv" + external_regressor_config = sample_external_regressor_config("valid") + with pytest.raises( + ValueError, match=f"Cannot load tsv file with external regressors: {external_regressors}" + ): + external.load_validate_external_regressors( + external_regressors, external_regressor_config, 200 + ) + + +def test_load_validate_external_regressors_smoke(): + """Test load_validate_external_regressors succeeds.""" + + external_regressors = op.join(THIS_DIR, "data", "external_regress_Ftest_3echo.tsv") + n_vols = 75 + external_regressor_config = sample_external_regressor_config() + + # Not testing outputs because this is just calling validate_extern_regress and + # outputs are checked in those tests + external.load_validate_external_regressors( + external_regressors, external_regressor_config, n_vols + ) From a35b1fffc3908bc39a8986970b03be3991eb8999 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Mon, 20 May 2024 16:59:21 -0400 Subject: [PATCH 65/81] moved test data downloading to tests.utils.py and started test for fit_regressors --- tedana/tests/test_external_metrics.py | 51 +++++++++- tedana/tests/test_integration.py | 139 +------------------------ tedana/tests/utils.py | 141 +++++++++++++++++++++++++- 3 files changed, 194 insertions(+), 137 deletions(-) diff --git a/tedana/tests/test_external_metrics.py b/tedana/tests/test_external_metrics.py index 104766066..8f02bcbd3 100644 --- a/tedana/tests/test_external_metrics.py +++ b/tedana/tests/test_external_metrics.py @@ -1,7 +1,6 @@ """Tests for tedana.metrics.external.""" import logging -import os import os.path as op import pandas as pd @@ -9,8 +8,9 @@ from tedana.io import load_json from tedana.metrics import external +from tedana.tests.utils import data_for_testing_info, download_test_data -THIS_DIR = os.path.dirname(os.path.abspath(__file__)) +THIS_DIR = op.dirname(op.abspath(__file__)) LGR = logging.getLogger("GENERAL") # ---------------------------------------------------------------------- @@ -93,6 +93,31 @@ def sample_external_regressor_config(config_choice="valid"): return external_regressor_config +def sample_mixing_matrix(): + """Load and return the three-echo mixing matrix.""" + + test_data_path, osf_id = data_for_testing_info("three-echo") + download_test_data(osf_id, test_data_path) + + return pd.read_csv( + op.join( + THIS_DIR, + "../../.testing_data_cache/three-echo/TED.three-echo/desc_ICA_mixing_static.tsv", + ), + delimiter="\t", + ).to_numpy() + + +def sample_comptable(n_components): + """Create an empty component table.""" + + row_vals = [] + for ridx in range(n_components): + row_vals.append(f"ICA_{str(ridx).zfill(2)}") + + return pd.DataFrame(data={"Component": row_vals}) + + # validate_extern_regress # ----------------------- def test_validate_extern_regress_succeeds(caplog): @@ -217,3 +242,25 @@ def test_load_validate_external_regressors_smoke(): external.load_validate_external_regressors( external_regressors, external_regressor_config, n_vols ) + + +# fit_regressors +# -------------- + + +def test_fit_regressors_succeeds(): + """Test conditions fit_regressors should succeed.""" + + external_regressors, n_vols = sample_external_regressors() + external_regressor_config = sample_external_regressor_config() + external_regressor_config_expanded = external.validate_extern_regress( + external_regressors, external_regressor_config, n_vols + ) + mixing = sample_mixing_matrix() + + comptable = sample_comptable(mixing.shape[1]) + comptable = external.fit_regressors( + comptable, external_regressors, external_regressor_config_expanded, mixing + ) + + # TODO Add validation of output and tests of conditional statements diff --git a/tedana/tests/test_integration.py b/tedana/tests/test_integration.py index b2eea4f1b..5579e8d76 100644 --- a/tedana/tests/test_integration.py +++ b/tedana/tests/test_integration.py @@ -1,24 +1,19 @@ """Integration tests for "real" data.""" import glob -import json import logging import os import os.path as op import re import shutil import subprocess -import tarfile -from datetime import datetime -from gzip import GzipFile -from io import BytesIO import pandas as pd import pytest -import requests from pkg_resources import resource_filename from tedana.io import InputHarvester +from tedana.tests.utils import data_for_testing_info, download_test_data from tedana.workflows import t2smap as t2smap_cli from tedana.workflows import tedana as tedana_cli from tedana.workflows.ica_reclassify import ica_reclassify_workflow @@ -76,129 +71,6 @@ def check_integration_outputs(fname, outpath, n_logs=1): raise ValueError(msg) -def data_for_testing_info(test_dataset=str): - """ - Get the path and download link for each dataset used for testing. - - Also creates the base directories into which the data and output - directories are written - - Parameters - ---------- - test_dataset : str - References one of the datasets to download. It can be: - three-echo - three-echo-reclassify - four-echo - five-echo - - Returns - ------- - test_data_path : str - The path to the local directory where the data will be downloaded - osf_id : str - The ID for the OSF file. - Data download link would be https://osf.io/osf_id/download - Metadata download link would be https://osf.io/osf_id/metadata/?format=datacite-json - """ - - tedana_path = os.path.dirname(tedana_cli.__file__) - base_data_path = os.path.abspath(os.path.join(tedana_path, "../../.testing_data_cache")) - os.makedirs(base_data_path, exist_ok=True) - os.makedirs(os.path.join(base_data_path, "outputs"), exist_ok=True) - if test_dataset == "three-echo": - test_data_path = os.path.join(base_data_path, "three-echo/TED.three-echo") - osf_id = "rqhfc" - os.makedirs(os.path.join(base_data_path, "three-echo"), exist_ok=True) - os.makedirs(os.path.join(base_data_path, "outputs/three-echo"), exist_ok=True) - elif test_dataset == "three-echo-reclassify": - test_data_path = os.path.join(base_data_path, "reclassify") - osf_id = "f6g45" - os.makedirs(os.path.join(base_data_path, "outputs/reclassify"), exist_ok=True) - elif test_dataset == "four-echo": - test_data_path = os.path.join(base_data_path, "four-echo/TED.four-echo") - osf_id = "gnj73" - os.makedirs(os.path.join(base_data_path, "four-echo"), exist_ok=True) - os.makedirs(os.path.join(base_data_path, "outputs/four-echo"), exist_ok=True) - elif test_dataset == "five-echo": - test_data_path = os.path.join(base_data_path, "five-echo/TED.five-echo") - osf_id = "9c42e" - os.makedirs(os.path.join(base_data_path, "five-echo"), exist_ok=True) - os.makedirs(os.path.join(base_data_path, "outputs/five-echo"), exist_ok=True) - else: - raise ValueError(f"{test_dataset} is not a valid dataset string for data_for_testing_info") - - return test_data_path, osf_id - - -def download_test_data(osf_id, test_data_path): - """If current data is not already available, downloads tar.gz data. - - Data are stored at `https://osf.io/osf_id/download`. - It unpacks into `out_path`. - - Parameters - ---------- - osf_id : str - The ID for the OSF file. - out_path : str - Path to directory where OSF data should be extracted - """ - - try: - datainfo = requests.get(f"https://osf.io/{osf_id}/metadata/?format=datacite-json") - except Exception: - if len(os.listdir(test_data_path)) == 0: - raise ConnectionError( - f"Cannot access https://osf.io/{osf_id} and testing data " "are not yet downloaded" - ) - else: - TestLGR.warning( - f"Cannot access https://osf.io/{osf_id}. " - f"Using local copy of testing data in {test_data_path} " - "but cannot validate that local copy is up-to-date" - ) - return - datainfo.raise_for_status() - metadata = json.loads(datainfo.content) - # 'dates' is a list with all udpates to the file, the last item in the list - # is the most recent and the 'date' field in the list is the date of the last - # update. - osf_filedate = metadata["dates"][-1]["date"] - - # File the file with the most recent date for comparision with - # the lsst updated date for the osf file - if os.path.exists(test_data_path): - filelist = glob.glob(f"{test_data_path}/*") - most_recent_file = max(filelist, key=os.path.getctime) - if os.path.exists(most_recent_file): - local_filedate = os.path.getmtime(most_recent_file) - local_filedate_str = str(datetime.fromtimestamp(local_filedate).date()) - local_data_exists = True - else: - local_data_exists = False - else: - local_data_exists = False - if local_data_exists: - if local_filedate_str == osf_filedate: - TestLGR.info( - f"Downloaded and up-to-date data already in {test_data_path}. Not redownloading" - ) - return - else: - TestLGR.info( - f"Downloaded data in {test_data_path} was last modified on " - f"{local_filedate_str}. Data on https://osf.io/{osf_id} " - f" was last updated on {osf_filedate}. Deleting and redownloading" - ) - shutil.rmtree(test_data_path) - req = requests.get(f"https://osf.io/{osf_id}/download") - req.raise_for_status() - t = tarfile.open(fileobj=GzipFile(fileobj=BytesIO(req.content))) - os.makedirs(test_data_path, exist_ok=True) - t.extractall(test_data_path) - - def reclassify_raw() -> str: test_data_path, _ = data_for_testing_info("three-echo-reclassify") return os.path.join(test_data_path, "TED.three-echo") @@ -393,13 +265,12 @@ def test_integration_three_echo_external_regressors_single_model(skip_integratio # Col 1 (trans_x_correlation) is the TS for ICA comp 59 + similar stdev Gaussian Noise # Col 2 (trans_y_correlation) is 0.4*comp29+0.5+comp20+Gaussian Noise # Col 3 (trans_z_correlation) is comp20+Gaussian Noise - # The above are the same as the test that uses corr for external regressors # Col 4-6 are Gaussian noise representing pitch/roll/yaw # Col 7-12 are the first derivative of col 1-6 - # With the currently set up decision tree minimal_exteral 2, one component (my 59) - # should be rejected, but 20 and 29 aren't rejected because neither crosses the - # r>0.8 threshold. If trans_y and trans_Z were included in a single model then - # component 20 would have been rejected + # With the currently set up decision tree, + # Component 59 should be rejected because it is correlated to trans_x and, + # comp 20 should be rejected because of a signif fit to a combination of trans_y and trans_z. + # Component 29 is not rejected because the fit does not cross a r>0.8 threshold # Note that the above is in comparision to the minimal decision tree # but the integration test for 3 echoes uses the kundu tree download_test_data(osf_id, test_data_path) diff --git a/tedana/tests/utils.py b/tedana/tests/utils.py index 5b4a473e3..6147165be 100644 --- a/tedana/tests/utils.py +++ b/tedana/tests/utils.py @@ -1,6 +1,22 @@ """Utility functions for testing tedana.""" -from os.path import abspath, dirname, join, sep +import glob +import json +import logging +import shutil +import tarfile +from datetime import datetime +from gzip import GzipFile +from io import BytesIO +from os import listdir, makedirs +from os.path import abspath, dirname, exists, getctime, getmtime, join, sep + +import requests + +from tedana.workflows import tedana as tedana_cli + +# Added a testing logger to output whether or not testing data were downlaoded +TestLGR = logging.getLogger("TESTING") def get_test_data_path(): @@ -12,3 +28,126 @@ def get_test_data_path(): Based on function by Yaroslav Halchenko used in Neurosynth Python package. """ return abspath(join(dirname(__file__), "data") + sep) + + +def data_for_testing_info(test_dataset=str): + """ + Get the path and download link for each dataset used for testing. + + Also creates the base directories into which the data and output + directories are written + + Parameters + ---------- + test_dataset : str + References one of the datasets to download. It can be: + three-echo + three-echo-reclassify + four-echo + five-echo + + Returns + ------- + test_data_path : str + The path to the local directory where the data will be downloaded + osf_id : str + The ID for the OSF file. + Data download link would be https://osf.io/osf_id/download + Metadata download link would be https://osf.io/osf_id/metadata/?format=datacite-json + """ + + tedana_path = dirname(tedana_cli.__file__) + base_data_path = abspath(join(tedana_path, "../../.testing_data_cache")) + makedirs(base_data_path, exist_ok=True) + makedirs(join(base_data_path, "outputs"), exist_ok=True) + if test_dataset == "three-echo": + test_data_path = join(base_data_path, "three-echo/TED.three-echo") + osf_id = "rqhfc" + makedirs(join(base_data_path, "three-echo"), exist_ok=True) + makedirs(join(base_data_path, "outputs/three-echo"), exist_ok=True) + elif test_dataset == "three-echo-reclassify": + test_data_path = join(base_data_path, "reclassify") + osf_id = "f6g45" + makedirs(join(base_data_path, "outputs/reclassify"), exist_ok=True) + elif test_dataset == "four-echo": + test_data_path = join(base_data_path, "four-echo/TED.four-echo") + osf_id = "gnj73" + makedirs(join(base_data_path, "four-echo"), exist_ok=True) + makedirs(join(base_data_path, "outputs/four-echo"), exist_ok=True) + elif test_dataset == "five-echo": + test_data_path = join(base_data_path, "five-echo/TED.five-echo") + osf_id = "9c42e" + makedirs(join(base_data_path, "five-echo"), exist_ok=True) + makedirs(join(base_data_path, "outputs/five-echo"), exist_ok=True) + else: + raise ValueError(f"{test_dataset} is not a valid dataset string for data_for_testing_info") + + return test_data_path, osf_id + + +def download_test_data(osf_id, test_data_path): + """If current data is not already available, downloads tar.gz data. + + Data are stored at `https://osf.io/osf_id/download`. + It unpacks into `out_path`. + + Parameters + ---------- + osf_id : str + The ID for the OSF file. + out_path : str + Path to directory where OSF data should be extracted + """ + + try: + datainfo = requests.get(f"https://osf.io/{osf_id}/metadata/?format=datacite-json") + except Exception: + if len(listdir(test_data_path)) == 0: + raise ConnectionError( + f"Cannot access https://osf.io/{osf_id} and testing data " "are not yet downloaded" + ) + else: + TestLGR.warning( + f"Cannot access https://osf.io/{osf_id}. " + f"Using local copy of testing data in {test_data_path} " + "but cannot validate that local copy is up-to-date" + ) + return + datainfo.raise_for_status() + metadata = json.loads(datainfo.content) + # 'dates' is a list with all udpates to the file, the last item in the list + # is the most recent and the 'date' field in the list is the date of the last + # update. + osf_filedate = metadata["dates"][-1]["date"] + + # File the file with the most recent date for comparision with + # the lsst updated date for the osf file + if exists(test_data_path): + filelist = glob.glob(f"{test_data_path}/*") + most_recent_file = max(filelist, key=getctime) + if exists(most_recent_file): + local_filedate = getmtime(most_recent_file) + local_filedate_str = str(datetime.fromtimestamp(local_filedate).date()) + local_data_exists = True + else: + local_data_exists = False + else: + local_data_exists = False + if local_data_exists: + if local_filedate_str == osf_filedate: + TestLGR.info( + f"Downloaded and up-to-date data already in {test_data_path}. Not redownloading" + ) + return + else: + TestLGR.info( + f"Downloaded data in {test_data_path} was last modified on " + f"{local_filedate_str}. Data on https://osf.io/{osf_id} " + f" was last updated on {osf_filedate}. Deleting and redownloading" + ) + shutil.rmtree(test_data_path) + req = requests.get(f"https://osf.io/{osf_id}/download") + req.raise_for_status() + t = tarfile.open(fileobj=GzipFile(fileobj=BytesIO(req.content))) + makedirs(test_data_path, exist_ok=True) + t.extractall(test_data_path) From c54c4badc928ff20048910735e914b627274524d Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Tue, 21 May 2024 21:29:34 -0400 Subject: [PATCH 66/81] fixed bug where task regressors retained in partial models --- tedana/metrics/external.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index 03e54d8cd..e845bc60a 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -458,6 +458,8 @@ def build_fstat_regressor_models( LGR.info( f"Regressors In {model_name} Model:'{model_name}': {tmp_model_labels[model_name]}" ) + # Remove task_keep regressors from regressor_labels before calculating partial models + regressor_labels = set(regressor_labels) - set(task_keep_model) else: regressor_models["full"] = np.concatenate( (detrend_regressors_arr, stats.zscore(external_regressors.to_numpy(), axis=0)), axis=1 From 4223bf9a658c8fab3d2580aed6a5bea3a1dd1ba4 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Thu, 23 May 2024 12:34:19 -0400 Subject: [PATCH 67/81] matched testing external regressors to included mixing and fixed bugs --- tedana/metrics/external.py | 6 +- ...xternal_regressors_motion_task_models.json | 4 +- ...rnell_three_echo_preset_mixing_outputs.txt | 105 ++++++++++++ .../data/external_regress_Ftest_3echo.tsv | 150 +++++++++--------- tedana/tests/test_external_metrics.py | 24 +++ tedana/tests/test_integration.py | 21 +-- tedana/workflows/tedana.py | 7 +- 7 files changed, 222 insertions(+), 95 deletions(-) create mode 100644 tedana/tests/data/cornell_three_echo_preset_mixing_outputs.txt diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index e845bc60a..a44f75b6c 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -126,7 +126,7 @@ def validate_extern_regress( tmp_replacements = [ reg_name for reg_name in regressor_names - if re.match(tmp_name, reg_name.lower()) + if re.match(tmp_name, reg_name, re.IGNORECASE) ] if not tmp_replacements: err_msg += ( @@ -458,8 +458,8 @@ def build_fstat_regressor_models( LGR.info( f"Regressors In {model_name} Model:'{model_name}': {tmp_model_labels[model_name]}" ) - # Remove task_keep regressors from regressor_labels before calculating partial models - regressor_labels = set(regressor_labels) - set(task_keep_model) + # Remove task_keep regressors from regressor_labels before calculating partial models + regressor_labels = set(regressor_labels) - set(task_keep_model) else: regressor_models["full"] = np.concatenate( (detrend_regressors_arr, stats.zscore(external_regressors.to_numpy(), axis=0)), axis=1 diff --git a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json index de82c1e28..e2573c90d 100644 --- a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json +++ b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json @@ -222,7 +222,7 @@ { "functionname": "dec_left_op_right", "parameters": { - "if_true": "accept", + "if_true": "accepted", "if_false": "nochange", "decide_comps": ["provisionalreject", "rejected"], "op": "<", @@ -236,7 +236,7 @@ "op3": ">=", "left3": "kappa", "right3": "kappa_elbow_kundu", - "tag_if_true": "Fits task" + "tag_if_true": "fits task" }, "_comment": "Keep if it fits task regressors and contains T2* signal, as defined by kappa>elbow" }, diff --git a/tedana/tests/data/cornell_three_echo_preset_mixing_outputs.txt b/tedana/tests/data/cornell_three_echo_preset_mixing_outputs.txt new file mode 100644 index 000000000..1af72cda8 --- /dev/null +++ b/tedana/tests/data/cornell_three_echo_preset_mixing_outputs.txt @@ -0,0 +1,105 @@ +S0map.nii.gz +T2starmap.nii.gz +dataset_description.json +desc-ICAAccepted_components.nii.gz +desc-ICAAccepted_stat-z_components.nii.gz +desc-ICA_components.nii.gz +desc-ICA_decomposition.json +desc-tedana_metrics.json +desc-tedana_metrics.tsv +desc-tedana_registry.json +desc-ICACrossComponent_metrics.json +desc-ICA_status_table.tsv +desc-ICA_decision_tree.json +desc-ICA_mixing.tsv +desc_ICA_mixing_static.tsv +desc-ICA_stat-z_components.nii.gz +desc-adaptiveGoodSignal_mask.nii.gz +desc-denoised_bold.nii.gz +desc-optcom_bold.nii.gz +desc-confounds_timeseries.tsv +desc-rmse_statmap.nii.gz +figures +figures/adaptive_mask.svg +figures/carpet_optcom.svg +figures/carpet_denoised.svg +figures/carpet_accepted.svg +figures/carpet_rejected.svg +figures/comp_000.png +figures/comp_001.png +figures/comp_002.png +figures/comp_003.png +figures/comp_004.png +figures/comp_005.png +figures/comp_006.png +figures/comp_007.png +figures/comp_008.png +figures/comp_009.png +figures/comp_010.png +figures/comp_011.png +figures/comp_012.png +figures/comp_013.png +figures/comp_014.png +figures/comp_015.png +figures/comp_016.png +figures/comp_017.png +figures/comp_018.png +figures/comp_019.png +figures/comp_020.png +figures/comp_021.png +figures/comp_022.png +figures/comp_023.png +figures/comp_024.png +figures/comp_025.png +figures/comp_026.png +figures/comp_027.png +figures/comp_028.png +figures/comp_029.png +figures/comp_030.png +figures/comp_031.png +figures/comp_032.png +figures/comp_033.png +figures/comp_034.png +figures/comp_035.png +figures/comp_036.png +figures/comp_037.png +figures/comp_038.png +figures/comp_039.png +figures/comp_040.png +figures/comp_041.png +figures/comp_042.png +figures/comp_043.png +figures/comp_044.png +figures/comp_045.png +figures/comp_046.png +figures/comp_047.png +figures/comp_048.png +figures/comp_049.png +figures/comp_050.png +figures/comp_051.png +figures/comp_052.png +figures/comp_053.png +figures/comp_054.png +figures/comp_055.png +figures/comp_056.png +figures/comp_057.png +figures/comp_058.png +figures/comp_059.png +figures/comp_060.png +figures/comp_061.png +figures/comp_062.png +figures/comp_063.png +figures/comp_064.png +figures/comp_065.png +figures/comp_066.png +figures/comp_067.png +figures/comp_068.png +figures/rmse_brain.svg +figures/rmse_timeseries.svg +figures/s0_brain.svg +figures/s0_histogram.svg +figures/t2star_brain.svg +figures/t2star_histogram.svg +references.bib +report.txt +tedana_report.html diff --git a/tedana/tests/data/external_regress_Ftest_3echo.tsv b/tedana/tests/data/external_regress_Ftest_3echo.tsv index d2609050f..ee5ab71cc 100644 --- a/tedana/tests/data/external_regress_Ftest_3echo.tsv +++ b/tedana/tests/data/external_regress_Ftest_3echo.tsv @@ -1,76 +1,76 @@ Mot_X Mot_Y Mot_Z Mot_Pitch Mot_Roll Mot_Yaw Mot_d1_X Mot_d1_Y Mot_d1_Z Mot_d1_Pitch Mot_d1_Roll Mot_d1_Yaw CSF Signal --0.329580266 -0.844918633 1.626381228 -0.218889288 0.634672027 0.603137649 0 0 0 0 0 0 -0.718687205 -1.352373215 --1.520650288 -2.13455262 -0.882532519 0.336875466 -1.592270822 -1.191402591 -1.191070022 -1.289633987 -2.508913747 0.555764753 -2.226942849 -1.794540239 -0.844111053 -2.055595493 --0.511890744 -0.711229882 0.276263262 0.188691918 0.099964095 -0.995174865 1.008759544 1.423322738 1.158795781 -0.148183548 1.692234917 0.196227726 -0.921595994 -1.301886348 --2.06545787 -0.40815265 1.038928803 0.226413424 -0.384996762 0.286311023 -1.553567126 0.303077232 0.762665541 0.037721506 -0.484960857 1.281485888 -0.638149754 -1.255098086 --1.565465426 -0.289605838 0.551574072 0.307316972 -0.272939181 -0.92834738 0.499992444 0.118546812 -0.487354731 0.080903548 0.112057581 -1.214658403 1.141942235 -1.790879267 --0.583901238 -0.45316118 0.882519392 -0.865762002 -0.537313024 0.517619453 0.981564188 -0.163555342 0.33094532 -1.173078975 -0.264373843 1.445966833 -1.385047823 -0.08765588 -0.225980497 -1.627540572 -0.68542407 -0.700932399 -1.67754023 1.019518689 0.809881735 -1.174379392 -1.567943462 0.164829603 -1.140227206 0.501899236 -0.186120892 1.547906633 -0.560677621 -1.489408639 -0.595520306 0.685234401 -0.723554811 0.416786873 0.334697124 0.138131933 0.089903764 1.386166801 0.953985419 -0.602731816 -0.580953121 3.177369816 -0.003248201 -0.340159692 -0.879529088 0.602469892 -0.876641151 -0.884034813 -0.55742942 1.149248947 -0.284008782 -0.08276451 -0.15308634 -1.300821686 -1.692923237 2.847689428 -0.836161459 -1.092255501 -2.618392652 0.707002513 -0.763363892 0.276104352 0.832913258 -0.752095809 -1.738863564 0.104532622 0.113277259 1.160139164 -0.371763879 1.278999068 -1.586566953 0.271349643 -0.649296386 -0.881991034 -0.310818575 0.552978122 0.750405494 1.363605144 1.969096266 -1.588993548 0.452545317 0.276873771 0.933331204 0.582531024 -0.667602572 1.549333738 0.076243832 0.833106558 0.518018373 -0.620225079 -0.918964381 1.277984095 0.725540218 1.715097593 0.828836948 -1.173203202 -0.852116981 -1.764145893 --0.01792122 2.740600234 0.074586135 1.164085584 -0.240669175 -0.440358808 -0.685523792 1.191266496 -0.001657697 0.330979026 -0.758687548 0.179866271 -0.092616197 -1.161527578 --0.659019247 2.298595672 0.17634643 -0.101416476 -0.182581812 2.015890229 -0.641098027 -0.442004562 0.101760295 -1.26550206 0.058087363 2.456249037 1.414856771 -0.840842986 --1.094112767 -0.186225918 -2.127567341 -0.660534996 0.727968953 0.549065179 -0.43509352 -2.48482159 -2.303913771 -0.55911852 0.910550765 -1.46682505 -1.264667218 0.987514587 -0.248649215 -0.618039127 -1.082778413 -2.323754365 -0.275433162 -0.224507966 1.342761982 -0.431813209 1.044788928 -1.66321937 -1.003402116 -0.773573145 0.297574192 2.174571557 --0.034109838 0.196157196 0.285049845 0.929700246 -1.903128963 -1.640111364 -0.282759053 0.814196323 1.367828258 3.253454611 -1.627695801 -1.415603398 -0.749200249 2.032922444 --0.154408808 0.946079652 1.025632299 -1.504803984 1.86795795 0.461493517 -0.12029897 0.749922456 0.740582454 -2.43450423 3.771086913 2.101604881 0.024314549 0.613790059 -0.764863858 0.933908359 -0.135353281 -0.109277214 0.002639137 -1.643878764 0.919272666 -0.012171293 -1.16098558 1.39552677 -1.865318813 -2.105372281 -1.143075439 -0.482525583 -0.445796508 1.917424429 0.650347848 1.98385538 0.655358612 1.874592447 -0.31906735 0.98351607 0.785701129 2.093132594 0.652719475 3.51847121 -1.758526555 -0.351681452 -0.207513285 1.298735387 2.318083224 -0.535817525 2.16070642 -0.721054976 -0.238283223 -0.618689042 1.667735376 -2.519672905 1.505347808 -2.595647423 0.666876476 -2.337411374 -0.381782634 0.081083807 -1.984367893 0.563118012 0.313116397 -0.168924986 0.174269349 -1.21765158 -4.302451117 1.098935537 -1.847590024 0.55212999 0.145183641 -1.673709355 --1.47473253 0.515676307 1.024256498 -0.163074269 0.924446522 -0.68895112 -1.856515164 0.4345925 3.008624391 -0.726192282 0.611330125 -0.520026134 -0.051313681 -0.199835362 --1.494898436 0.766171566 1.630635233 1.316319173 0.756997088 -0.541944145 -0.020165906 0.250495259 0.606378735 1.479393442 -0.167449434 0.147006975 0.176664517 2.199049334 --2.019896907 1.643560771 -0.311565305 -1.536321826 0.680356602 0.758927506 -0.524998471 0.877389205 -1.942200538 -2.852640999 -0.076640486 1.300871651 -1.264980299 1.920445951 --2.178716752 0.973512277 2.252419221 -0.98610174 -1.462675416 1.890594069 -0.158819845 -0.670048494 2.563984526 0.550220085 -2.143032018 1.131666563 -1.113211169 0.588423648 --1.232037427 -0.40191649 1.00829017 1.358255513 0.106048498 -0.129832743 0.946679325 -1.375428767 -1.244129051 2.344357253 1.568723914 -2.020426812 0.154120413 -1.163257212 -0.731522293 0.40813286 1.025669565 0.164802434 1.923608997 1.773241326 1.96355972 0.81004935 0.017379395 -1.193453079 1.817560499 1.903074069 -1.34580855 -0.095732534 -0.753719194 0.163556595 -1.064627798 -0.934732068 0.685816646 0.525679007 0.022196901 -0.244576265 -2.090297363 -1.099534501 -1.237792351 -1.247562319 0.192821615 -1.148529666 -1.757683518 -0.537743502 -0.866195997 1.491399948 -0.345322156 0.446364892 1.003964324 -0.701300097 0.198431801 2.426132015 -1.031138802 -0.079314115 1.960430091 -0.725025316 -1.550719352 0.11832591 -0.668387479 0.223298561 -1.812651721 0.058823033 -0.206964166 0.656069412 0.197808518 -1.268101387 -1.467329565 -0.387541859 -0.600970527 1.338930898 -2.288307277 0.791999772 0.75152785 0.068312984 1.468953207 -0.859155411 0.737587925 0.673673862 1.419915329 -0.154985576 3.281604929 -0.917978445 -0.296907317 1.710280144 -2.663681819 2.261180081 1.905593875 -1.140429944 -1.722043668 0.364115037 0.375374542 1.469180309 1.154066025 -1.208742929 -3.190996875 1.223270448 -0.862172265 -1.10195542 -2.268142799 0.717724473 -2.382603885 0.436210872 0.218979462 0.089319574 -0.39553902 -1.543455608 -4.28819776 1.576640816 1.94102313 -0.274795463 0.906547155 -0.840393059 --0.218049277 -0.508412721 -2.487404957 1.887320081 1.04722817 -0.395861337 -2.486192076 -1.226137194 -0.104801072 1.45110921 0.828248708 -0.485180912 -0.451624542 -1.114922613 --0.410793483 0.116533685 1.879092728 1.184222196 -0.031140809 -0.90091931 -0.192744206 0.624946406 4.366497685 -0.703097886 -1.078368979 -0.505057973 0.118224392 -0.396853219 --0.647691228 0.838129685 1.374778227 0.322847522 1.175235974 1.277024175 -0.236897745 0.721596 -0.504314501 -0.861374674 1.206376783 2.177943485 -0.449494687 -1.344892636 --1.13985429 -0.495903004 1.056569633 -1.587672129 0.39860901 -0.768585548 -0.492163062 -1.334032689 -0.318208594 -1.910519651 -0.776626964 -2.045609723 -0.222016948 -0.176791983 -0.53999669 -0.487881558 0.581258434 -1.085920121 -0.501406499 0.115378063 1.67985098 0.008021446 -0.475311199 0.501752008 -0.900015509 0.883963611 -0.703432192 0.174003102 -0.410905902 0.873257327 3.608291808 0.653652794 -1.54826995 1.468348054 -0.129090788 1.361138885 3.027033374 1.739572915 -1.04686345 1.352969991 0.299902147 0.727958119 --0.173062544 0.529483977 1.076381362 -0.503373399 -0.058733834 -1.45590019 -0.583968446 -0.34377335 -2.531910446 -1.157026193 1.489536116 -2.924248244 0.005305683 1.410121446 -0.590240338 -0.560105813 -0.33143028 -0.14587712 1.474596747 1.017308667 0.763302882 -1.08958979 -1.407811642 0.35749628 1.533330581 2.473208857 -1.883857873 0.197348647 --0.330152172 -0.29496616 -1.27480681 0.993753616 0.247092802 -0.980747759 -0.92039251 0.265139653 -0.94337653 1.139630735 -1.227503945 -1.998056427 -1.361923392 -0.113442337 --0.770820846 -1.130215098 -0.533069878 1.339112763 1.354475058 -0.663609074 -0.440668674 -0.835248938 0.741736932 0.345359147 1.107382257 0.317138685 -0.854950385 -0.767755066 --0.997000751 -1.594080868 -2.206716323 0.551420017 -0.360200976 0.897818757 -0.226179905 -0.46386577 -1.673646445 -0.787692746 -1.714676034 1.561427831 -0.509180861 -1.183525054 --0.078723051 -1.034723379 1.715724783 2.451708059 0.070115822 -0.667778397 0.9182777 0.559357489 3.922441106 1.900288042 0.430316798 -1.565597154 0.134291091 -0.655231001 -0.317741611 -1.449732445 -0.829499847 1.984395673 1.236600623 -0.310344732 0.396464662 -0.415009066 -2.54522463 -0.467312386 1.166484801 0.357433665 0.330435696 0.00329668 -0.920629978 -3.107612041 -1.72016277 -1.6235986 -0.680428817 -0.15105238 0.602888367 -1.657879596 -0.890662923 -3.607994273 -1.917029441 0.159292352 0.21447828 2.238926945 -0.904171949 -3.25810681 -1.338419095 -0.225635225 0.492152931 -1.184901646 -0.016458029 -0.150494769 0.381743675 1.397963375 1.172581749 -1.033849266 0.467948374 0.982016224 -0.712432166 -2.702026568 -4.090712476 0.325379006 -1.008512619 0.155445809 -0.191739783 0.556080242 -2.752293381 0.551014231 -1.50066555 1.340347455 -0.409919798 1.920065594 -1.213580415 -2.416744199 -1.644462247 0.887889538 -1.019416802 -0.194144049 0.501148249 0.285282369 2.446250229 0.562510532 -0.010904183 -0.349589858 -0.954443807 0.093335589 -1.015276471 -0.468506664 0.826022926 -0.704628983 -0.831670525 0.350992643 -0.198303944 1.948237535 2.470485173 -1.592518521 0.187746276 0.545136692 -1.28584816 -1.179097329 -0.458644239 0.304782879 -0.286629392 -0.622614244 0.81723357 -0.620482414 -0.556632232 0.773289543 -1.112652318 0.082014739 1.648904096 -0.971475057 -1.250751609 -1.045855877 -0.696387231 0.478940055 -0.661704616 -0.225347907 -1.187184555 -0.81881664 0.237742992 0.174157176 -0.375075224 0.397266338 -2.004418126 -0.198334226 -0.58673588 -1.436858073 -0.398048005 0.35203805 -0.836607472 -1.114036436 -1.728872921 -0.244092905 -0.298339226 -0.126902005 -0.174902856 -0.888688529 -0.541688366 0.574723735 -0.251724316 0.457782882 --0.433479435 0.130772773 -0.702912597 -0.711315522 0.831520689 -1.273732568 -0.83152744 -0.221265277 0.133694875 0.402720914 2.56039361 -1.029639663 2.040725153 2.253329615 --1.504705013 -0.117524037 -1.172268361 -1.301597961 -1.43149785 -0.320764583 -1.071225578 -0.24829681 -0.469355764 -0.590282439 -2.263018539 0.952967985 -0.258655658 1.83523334 --1.966296873 -1.284318477 -0.647627254 0.187287319 -1.026578293 0.466508325 -0.46159186 -1.16679444 0.524641107 1.48888528 0.404919557 0.787272908 2.114290456 1.498179142 --1.036674723 -0.437122887 0.788299251 1.208653611 1.589038974 1.098106245 0.92962215 0.84719559 1.435926505 1.021366292 2.615617267 0.63159792 -0.93052932 -0.4582012 --0.696570694 1.035997759 0.315168026 -0.180901513 -0.784289145 0.252979402 0.340104029 1.473120646 -0.473131225 -1.389555124 -2.373328119 -0.845126843 0.177085093 0.658375887 --1.056655551 2.054949636 1.733679189 -1.593982301 -2.245797478 0.326044934 -0.360084857 1.018951877 1.418511163 -1.413080788 -1.461508333 0.073065532 -1.242278489 -0.7184164 --1.253872449 0.940519962 -1.859117584 -0.393650127 -0.324318496 0.436127281 -0.197216898 -1.114429674 -3.592796773 1.200332173 1.921478982 0.110082347 0.278345459 1.380498447 --1.069287813 -0.5172584 -0.656338228 0.143574863 -1.262613776 1.600586235 0.184584636 -1.457778362 1.202779356 0.537224991 -0.93829528 1.164458954 -1.920302265 1.036520911 -0.073889742 1.015905744 3.017531937 -0.558421237 0.29254061 -0.615549542 1.143177555 1.533164144 3.673870165 -0.7019961 1.555154386 -2.216135777 0.003952104 1.353240573 -1.32888943 2.850501828 2.785235104 -0.929137834 -0.021765497 -0.581322431 1.254999688 1.834596084 -0.232296833 -0.370716597 -0.314306107 0.034227111 -0.854938554 -1.340041384 -0.831392436 2.255038285 0.082410724 0.365767705 0.860518504 -0.916585465 -0.497496994 -0.595463543 -2.70282438 1.294905539 0.882284001 -0.335263034 -0.264536802 -0.3039092 --0.670035292 0.826737793 0.321644476 1.449735904 -0.344614203 -1.344199808 -1.501427728 -1.428300492 0.239233752 1.083968199 -1.205132707 -0.427614343 -0.829211295 -2.309967509 --0.478355817 0.188711887 -0.630649751 -1.102278018 -1.374564234 0.697153667 0.191679475 -0.638025906 -0.952294227 -2.552013922 -1.029950031 2.041353475 -1.752282527 -0.569481283 -0.999877364 -1.06986647 1.290972976 -0.92101604 0.906070467 0.416570393 1.478233181 -1.258578357 1.921622727 0.181261978 2.280634701 -0.280583274 0.64858987 -0.552361383 -0.069018404 0.516767176 1.573029961 0.362136079 -1.202013587 -0.428893561 -0.93085896 1.586633646 0.282056985 1.283152119 -2.108084054 -0.845463954 -0.423093312 -1.774614671 -0.351758996 0.056295399 1.665144379 -0.415044976 0.482451076 -1.053100619 0.282740592 -0.460471777 0.092114418 -0.777181055 1.684464663 -0.624207058 0.664860241 -0.004888443 -0.858424569 -0.363527343 1.550740147 0.917563357 -0.68983397 -1.261316549 0.506665573 -0.419822742 -0.114404232 1.332608333 -1.172285046 -0.20821593 -0.097170847 2.424262862 -1.334779099 -0.553036015 0.026277298 -1.303973749 0.14799305 -1.047775225 0.47635453 -0.189508672 -1.524462849 -2.221537106 0.83782702 0.213541323 -0.273332909 1.294341793 -0.365467586 -1.460953257 1.836000417 0.076497972 -1.230053674 -0.597101686 -0.969311513 -0.907917242 1.809723119 1.38047172 -1.378046723 0.450673539 -0.026346724 0.770458972 --0.084225355 -0.90165276 0.41416722 1.144524716 0.509152904 -1.158340165 -0.449692941 0.559300497 -1.421833197 1.068026744 1.739206578 -0.561238479 -1.19547545 -0.027621886 +-1.918448296 0.613840782 0.931540667 -0.6673914 -0.920974104 0.414696683 0 0 0 0 0 0 -0.682069874 -0.267104969 +-1.499312896 -1.436337287 -0.002877744 -1.4486174 1.920982078 -0.662135106 0.419135399 -1.049966602 1.371602444 -0.381774669 2.841956181 -1.076831788 1.299506949 -1.441873441 +-0.464347503 0.77742439 -2.018920101 -0.5813718 -0.08376844 -2.049602342 1.034965394 -0.639791483 -0.34571542 0.481431869 -2.004750518 -1.387467237 1.03956566 -0.012546791 +-0.882468232 0.613586639 -0.900634617 -1.8710858 0.832274633 -0.715980861 -0.418120729 -0.701576096 -1.5077406 -1.966556791 0.916043073 1.333621481 -2.117416134 -1.514608378 +-2.876432946 0.162624246 -2.693742538 0.40271992 -0.101695422 -2.322836044 -1.993964714 -0.986958583 1.393128708 1.22698911 -0.933970055 -1.606855183 0.165056144 -0.730488362 +-2.770599691 0.773069239 -0.594607386 0.87415002 -1.095591014 0.409168659 0.105833255 0.722646172 0.85406937 -0.028751384 -0.993895591 2.732004703 2.249547675 -0.118317264 +-3.205528009 -1.240282529 -0.231044327 1.2539739 0.868004428 0.741313722 -0.434928317 -0.361780621 -2.896983594 0.845013265 1.963595441 0.332145064 1.06534899 1.088560942 +-1.731140824 -0.585488802 -1.289487317 -1.4567711 -2.241535224 -1.303961556 1.474387185 -0.739533675 0.635886989 1.444834965 -3.109539652 -2.045275278 1.897657856 3.373106633 +-1.649385201 -1.010216314 0.409064517 1.82693469 0.072142733 1.011135469 0.081755623 1.713588814 0.760907196 1.438742606 2.313677958 2.315097025 0.475455108 2.886092128 +-0.356722413 -2.18462177 -1.960832678 1.34803872 0.038795691 0.586439931 1.292662788 -1.754630085 0.947755796 -2.51869842 -0.033347042 -0.424695538 -1.782949546 0.990997189 +-2.068139427 -2.368583778 -1.31756829 0.14838923 0.48600498 0.188669434 -1.711417014 -1.922701681 -2.548820211 -1.004376035 0.447209289 -0.397770497 0.319060478 -0.009543645 +0.715406929 -0.794055247 -0.659208429 -0.9088572 0.832244029 -0.298597633 2.783546356 3.433036569 3.681919631 0.171322901 0.346239049 -0.487267067 -2.360487189 -0.86352509 +-1.040454954 -1.460509829 0.730612891 0.48960456 1.662017221 1.097618703 -1.755861883 0.771015618 -0.57528371 -1.618451595 0.829773192 1.396216336 -0.075080291 -1.79176057 +0.641959638 -0.420512505 1.474108981 -0.4546902 0.644800249 0.785479669 1.682414591 -1.407900471 -0.134985528 -0.118394819 -1.017216972 -0.312139034 0.492524814 -1.707843326 +1.17282228 -1.440402159 -1.827017963 0.13785463 0.423777039 1.865866632 0.530862642 1.413588482 -3.183847724 1.162659639 -0.22102321 1.080386963 1.96694161 0.56742326 +0.019226195 -1.206922803 -1.019112095 1.89767797 0.03740602 -0.365314788 -1.153596085 -1.368131607 2.550710877 2.038738178 -0.386371019 -2.231181419 -0.506011411 2.15937525 +0.230716637 1.025871474 -0.210799297 2.72895514 -0.090854654 1.203749971 0.211490441 1.382776835 -1.693338606 -1.1407467 -0.128260675 1.569064758 -3.346663807 2.050447006 +0.407382846 1.273926231 1.842308698 0.2197399 -1.348759461 1.743040689 0.176666209 -0.27185966 1.602569217 -0.76463226 -1.257904807 0.539290719 -1.247235976 -0.433977049 +0.1612234 0.48067487 0.69887392 -1.0384601 -0.545209917 -0.538861847 -0.246159445 -0.795743419 -0.833587695 -0.113367872 0.803549544 -2.281902536 -2.528311547 -1.005952731 +-0.774435034 0.250436935 -1.302969683 -1.4253112 -0.31725089 0.997865351 -0.935658434 0.217343614 0.663050904 -2.285610786 0.227959027 1.536727197 -0.125139007 -1.532868153 +-2.21405771 -1.19855816 0.157180224 -1.2830147 0.915847837 -1.855905265 -1.439622676 -2.135317516 1.499162395 0.711683009 1.233098727 -2.853770615 -1.372953894 -0.723766052 +0.644602937 -0.593727894 1.116393898 -2.2446977 2.308808799 -0.738470514 2.858660646 -0.02026364 -3.100275341 0.653486815 1.392960962 1.11743475 0.219777736 -0.346882257 +-1.70817007 -0.569744613 0.856896127 2.2565618 -1.54181769 0.718533392 -2.352773007 0.345506953 1.767981229 1.488999395 -3.850626489 1.457003906 1.811748869 2.011863542 +-1.361399301 0.794794152 0.587718056 1.75394479 -0.337715782 -0.80254175 0.34677077 2.190844145 2.688794228 0.082987972 1.204101908 -1.521075142 -1.355637416 0.760877094 +-1.501020749 0.083640965 1.076805817 0.08823669 1.332131724 -1.603750955 -0.139621449 -1.359729639 -1.341695095 1.338693701 1.669847505 -0.801209205 -0.849180593 0.488569214 +0.48778649 0.710004314 0.835636131 -0.0622049 1.299686527 1.15627752 1.98880724 2.683677358 0.04576576 -0.440197601 -0.032445197 2.760028475 -0.611650727 0.011338428 +-0.442521714 -2.429135912 -0.344746589 -0.1539904 -0.361764912 2.15020488 -0.930308204 0.480707608 -1.904999213 -2.219798226 -1.661451439 0.99392736 -0.063962351 -0.36410246 +-1.293284679 -0.119698923 -2.140007228 -2.3016194 -0.092783134 0.846265045 -0.850762965 -2.56285129 -0.135733172 -0.07728598 0.268981778 -1.303939835 2.046688925 -0.08031042 +0.293540519 2.273792315 -0.922759343 -0.472177 0.371524998 -0.187843855 1.586825197 -1.795807531 0.65538016 -0.121451694 0.464308132 -1.0341089 0.913184123 -2.255100827 +-0.693487854 0.351463719 -0.162894519 0.08909674 0.702698899 -2.032021551 -0.987028372 2.557562924 -1.210489276 0.904088386 0.331173901 -1.844177697 1.386981796 -0.650041216 +0.173105971 0.46616017 0.566506326 0.55123039 -0.821761475 0.780445909 0.866593825 0.123577956 2.327322112 -0.048680501 -1.524460374 2.812467461 0.361548921 -0.515174586 +0.284124435 0.109629879 -0.256780429 0.92513744 -0.478904608 0.317718609 0.111018464 -1.192917786 -1.933131477 1.459177382 0.342856867 -0.4627273 0.152004422 -1.362814301 +-0.881234565 -2.388114332 1.356737359 0.98653926 -0.691392499 0.386870289 -1.165359 0.254732835 2.37031576 -0.124326207 -0.212487891 0.069151679 -2.490702928 -1.635385346 +-0.255444705 -2.054123065 -0.305918819 -0.0787619 0.063676012 0.039098394 0.62578986 -0.587539233 -1.981628457 -1.33038225 0.755068511 -0.347771894 1.310144787 -0.548120778 +0.780073222 -1.176335262 -1.008397871 -2.5274442 -0.133381314 -0.262708036 1.035517928 -0.953462691 2.52450507 -0.992425857 -0.197057326 -0.30180643 -0.699933248 -1.09400854 +1.096676395 2.630985961 1.101324011 0.72981317 -0.18497425 -1.860502604 0.316603173 1.339281552 -1.131021166 -0.395124649 -0.051592935 -1.597794569 1.066356413 -0.098655736 +-1.111129192 0.648260708 1.302645215 -2.8573443 -0.191034251 0.513984918 -2.207805588 1.048362324 -1.370405474 0.426137996 -0.006060001 2.374487522 0.404978742 -1.291616158 +0.924918217 3.136233551 -1.957241535 -0.0284925 1.565544896 -0.360526519 2.036047409 0.250771109 0.145908513 1.408397727 1.756579147 -0.874511437 -0.126338151 -0.038948249 +-0.469509665 1.138025998 2.606253963 -0.5611022 1.453765216 -0.840789872 -1.394427882 -0.656810745 0.193848406 -0.165074206 -0.11177968 -0.480263353 -0.211848963 0.738228755 +-0.456972678 3.643029733 2.451272614 1.63729865 1.483563223 -1.429801084 0.012536987 -0.116567016 -0.490289096 1.226139395 0.029798008 -0.589011213 1.481842619 1.457544972 +1.117822835 0.424896113 0.317851072 1.13970873 -0.515525219 0.831537695 1.574795513 -0.93531283 -0.051682394 1.459313684 -1.999088443 2.261338779 -1.387046241 1.360885272 +0.11902894 -0.263988644 0.992680446 -0.7257977 -1.60180806 -1.4286882 -0.998793895 1.380749638 2.61009726 -1.105881546 -1.086282841 -2.260225895 -4.090650432 1.074110525 +0.782962389 0.190310101 0.886954126 -0.327059 -0.440194623 -1.257077276 0.663933449 0.825127605 -0.900437185 -1.359456187 1.161613437 0.171610924 -2.605810353 -0.116600574 +-1.225328525 -0.01698158 0.25889854 -0.6122607 0.16691481 -1.412548542 -2.008290914 -2.091189114 0.37629829 -1.893617713 0.607109433 -0.155471266 0.801223562 -1.108415283 +2.004227441 1.490583611 1.351189959 -1.9813324 1.379480559 0.499874622 3.229555967 0.776274988 -0.551334681 1.771100233 1.212565749 1.912423164 3.117322611 -0.3030232 +-0.021224349 -0.546717557 0.42114983 -1.1798323 0.710741431 -0.478383763 -2.02545179 -0.711745628 1.618662502 -0.017105838 -0.668739128 -0.978258385 0.199583792 0.648559962 +-1.585894619 0.471012009 2.34339681 0.2217914 -1.300710729 -0.115940822 -1.56467027 0.13315835 -0.978953361 0.825405711 -2.01145216 0.36244294 1.919451105 0.709433343 +-0.322444366 -1.486051289 -0.695615769 1.60677983 1.006851176 0.229014053 1.263450253 0.762023344 -2.438725646 -0.300422879 2.307561905 0.344954875 1.9562584 0.784237939 +-1.23588898 0.223047682 -1.661200563 1.70669699 -0.430495205 0.688400238 -0.913444614 -0.144699562 0.711704777 1.625311402 -1.437346381 0.459386185 1.136081926 -0.862600046 +-0.113014634 -1.327742478 -2.356161038 -1.935502 0.524175949 -1.914071379 1.122874346 -1.028438305 -2.108397279 -1.732740987 0.954671153 -2.602471617 0.727916107 0.029407721 +1.896517135 -0.074610997 -1.792468809 -0.4276473 -1.126535717 -0.121119705 2.009531769 -0.376769575 1.833398649 -0.307528232 -1.650711665 1.792951674 -0.179938446 -2.194911615 +0.54927595 -1.469166384 -1.061423099 -1.7537277 0.081635502 0.188482433 -1.347241185 1.00375727 -1.605109539 -0.006448868 1.208171219 0.309602138 0.587593716 -1.401211947 +0.609241805 -1.600051424 -0.334116527 -1.8937495 -1.409227385 -3.090343971 0.059965855 2.191032891 -0.722138977 -1.440807059 -1.490862886 -3.278826404 0.204311075 0.023719982 +0.553297243 -1.082413327 -1.432198474 -0.8830019 -0.532461972 1.704602033 -0.055944562 -3.670809628 4.059332093 0.602669002 0.876765413 4.794946004 1.058054927 -0.499666887 +1.070313239 -1.987180682 0.597461687 -0.7079295 -0.644730406 1.487137198 0.517015996 2.814079678 -1.518649218 1.856274528 -0.112268434 -0.217464835 -0.04792865 -1.047042678 +1.178754327 -0.119901901 -0.454913138 1.28551571 0.247207569 -0.389487979 0.108441088 -1.137957841 1.495094438 0.662909128 0.891937975 -1.876625177 -0.368409886 0.146653233 +2.154376652 -3.508128379 0.842994286 1.65380175 1.660919467 -0.488961614 0.975622325 -0.263936276 -0.562174092 -0.279882361 1.413711898 -0.099473635 -1.463377013 2.687429287 +0.09128902 -0.783193205 0.617096023 0.68672227 -0.11425155 -0.599971624 -2.063087632 0.043172816 -1.287801089 0.335411313 -1.775171017 -0.11101001 -1.959369594 2.540784371 +2.311450852 -1.018120612 0.260947086 -0.0561641 -1.636916947 -0.816931299 2.220161831 0.300834316 0.767505849 -2.686875234 -1.522665397 -0.216959675 -2.275737467 0.040025881 +1.158741617 0.918316824 1.237957484 -0.3404328 -0.563502682 -1.573657474 -1.152709235 -2.340120734 -0.096825229 2.837647319 1.073414265 -0.756726175 1.591038481 -1.650004329 +1.479471242 0.973441967 -0.875383651 0.37398953 0.099716682 -0.330379693 0.320729625 2.528326789 0.60868415 -1.606010454 0.663219364 1.243277781 1.935694967 -0.667546755 +1.777648491 -1.097974442 1.53805825 -2.2046334 0.720276278 0.96057892 0.298177249 -0.10858881 0.430761715 0.126426695 0.620559596 1.290958612 1.714946716 0.490716727 +2.300528229 -0.019891598 -0.843192928 0.38002863 0.345593137 -1.927906889 0.522879738 -1.255608226 -3.36776509 -0.740799207 -0.374683141 -2.888485808 -0.104321121 0.189054556 +1.718219352 1.968615215 0.99258286 1.00367245 0.472089401 -1.151827276 -0.582308877 1.876953321 4.151726133 0.814804546 0.126496264 0.776079613 -1.886967124 0.88261297 +1.818796046 1.251516902 2.48112936 0.120162 -2.174404986 -0.288678607 0.100576694 -2.104495113 0.990463227 -0.11508072 -2.646494386 0.863148669 -1.843686685 -1.162003063 +2.650511053 0.666817523 2.116072146 -0.7304728 -0.289560275 0.757223995 0.831715007 1.332630432 -1.635287484 -0.835249062 1.88484471 1.045902602 -1.502048982 -0.81862897 +1.003358656 -0.553431082 0.253195231 0.29818877 -0.644888975 0.415375762 -1.647152397 -0.066198816 -1.68303734 -1.137958872 -0.3553287 -0.341848233 -0.209471432 -0.71773952 +-0.308505642 -1.009669814 -1.293291819 -0.5731582 0.223328279 -1.858455246 -1.311864297 0.095181926 -0.571466561 0.854871892 0.868217254 -2.273831008 1.524835998 -0.693049382 +2.580580394 1.431738472 -0.712151248 -1.8779663 -0.689204561 0.28543817 2.889086036 -0.20747373 0.812222922 -0.290684446 -0.91253284 2.143893416 1.199375477 -1.885283082 +1.278114657 0.200109843 2.617031393 -0.5918718 1.189929684 -1.246224619 -1.302465737 -0.902096937 1.316570806 0.226138509 1.879134246 -1.531662789 0.847608147 -0.843658546 +1.77544775 0.872762242 0.204838601 -1.1457108 0.511356667 -0.056703264 0.497333092 1.081594434 -3.06925756 0.580810673 -0.678573017 1.189521355 1.266513646 1.286820743 +2.216557752 0.736849721 0.321308305 0.48927743 -0.109493948 -0.438554941 0.441110002 0.598981665 1.452579029 1.709194708 -0.620850615 -0.381851677 2.911628578 2.000828511 +1.254647386 1.826695112 3.299852074 2.2897055 -0.324070512 -1.119656585 -0.961910366 -1.077788301 -1.502492458 -0.590872341 -0.214576565 -0.681101644 1.403046925 2.495912365 +-0.209384481 3.059836405 1.525797667 0.99017857 -0.829861324 1.067304931 -1.464031867 0.008085359 0.914983495 0.38404106 -0.505790812 2.186961516 1.291264032 -0.189273644 +1.671697742 2.588938477 -0.472348787 -1.4281572 0.57578738 -0.557224395 1.881082223 3.100744209 2.376132007 -3.233419839 1.405648703 -1.624529326 -1.465703731 -0.51309839 diff --git a/tedana/tests/test_external_metrics.py b/tedana/tests/test_external_metrics.py index 8f02bcbd3..a2326535a 100644 --- a/tedana/tests/test_external_metrics.py +++ b/tedana/tests/test_external_metrics.py @@ -35,6 +35,30 @@ def sample_external_regressors(regress_choice="valid"): ------- external_regressors : :obj:`pandas.DataFrame` External regressor table n_vols : :obj:`int` Number of time points (rows) in external_regressors + + Notes + ----- + The loaded external regressors are in ./tests/data/external_regress_Ftest_3echo.tsv + These are based on tedana being run with default parameters on the 3 echo data using + the mixing matrix downloaded with the three echo data + .testing_data_cache/three-echo/TED.three-echo/desc_ICA_mixing_static.tsv + For the external regressors: + Column 0 (Mot_X) is the time series for ICA component 8 + Gaussian noise + Column 1 (Mot_Y) is 0.6 * comp 18 + 0.4 * comp 29 + Gaussian Noise + Column 2 (Mot_Z) is 0.8 * comp 18 + 1.2 * Gaussian Noise + Column 3 (Mot_Pitch) is 0.9 * comp 30 + 0.1 * comp 61 + Gaussian Noise + Columns 4-5 are Gaussian noise + Columns 6-11 are the first derivatives of columns 0-5 + Column 12 (CSF) is comp 11 + Gaussian Noise + Column 13 (Signal) is comp 30 + Gaussian Noise + + The Gaussian Noise levels are set so that, with an R^2>0.5 threshold: + ICA Comp 8 rejected solely based on the fit to Mot_X + ICA Comp 31 looks strongly inversely correlated to Comp 8 and is also rejected + ICA Comp 18 rejected based on the combined fit to Mot_X and Mot_Y (signif Motion partial model) + ICA Comp 29 NOT rejected based only on the fit to Mot_Y + ICA Comp 11 rejected based on a fit to CSF (signif CSF partial model) + ICA Comp 30 accepted based on fit to task model even through also fits to Mot_Pitch """ sample_fname = op.join(THIS_DIR, "data", "external_regress_Ftest_3echo.tsv") diff --git a/tedana/tests/test_integration.py b/tedana/tests/test_integration.py index 5579e8d76..792ab8013 100644 --- a/tedana/tests/test_integration.py +++ b/tedana/tests/test_integration.py @@ -224,6 +224,8 @@ def test_integration_three_echo(skip_integration): ) # Test re-running, but use the CLI + # TODO Move this to a separate integration test, use the fixed desc_ICA_mixing_static.tsv that + # is distributed with the testing data, and test specific outputs for consistent values args = [ "-d", f"{test_data_path}/three_echo_Cornell_zcat.nii.gz", @@ -307,19 +309,9 @@ def test_integration_three_echo_external_regressors_motion_task_models(skip_inte shutil.rmtree(out_dir) # download data and run the test - # external_regress_Ftest_3echo.tsv has 13 rows. Based on a local run on the 3 echo data: - # Col 1 (trans_x_correlation) is the TS for ICA comp 59 + similar stdev Gaussian Noise - # Col 2 (trans_y_correlation) is 0.4*comp29+0.5+comp20+Gaussian Noise - # Col 3 (trans_z_correlation) is comp20+Gaussian Noise - # The above are the same as the test that uses corr for external regressors - # Col 4-6 are Gaussian noise representing pitch/roll/yaw - # Col 7-12 are the first derivative of col 1-6 - # With the currently set up decision tree minimal_exteral 2, one component (my 59) - # should be rejected, but 20 and 29 aren't rejected because neither crosses the - # r>0.8 threshold. If trans_y and trans_Z were included in a single model then - # component 20 would have been rejected - # Note that the above is in comparision to the minimal decision tree - # but the integration test for 3 echoes uses the kundu tree + # external_regress_Ftest_3echo.tsv has 12 columns for motion, 1 for CSF, and 1 for task signal + # The regressor values and expected fits with the data are detailed in: + # tests.test_external_metrics.sample_external_regressors download_test_data(osf_id, test_data_path) tree_name = "resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json" tedana_cli.tedana_workflow( @@ -330,12 +322,13 @@ def test_integration_three_echo_external_regressors_motion_task_models(skip_inte external_regressors=resource_filename( "tedana", "tests/data/external_regress_Ftest_3echo.tsv" ), + mixm=f"{test_data_path}/desc_ICA_mixing_static.tsv", low_mem=True, tedpca="aic", ) # compare the generated output files - fn = resource_filename("tedana", "tests/data/cornell_three_echo_outputs.txt") + fn = resource_filename("tedana", "tests/data/cornell_three_echo_preset_mixing_outputs.txt") check_integration_outputs(fn, out_dir) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 5cd10a7d0..b3e39022f 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -779,7 +779,12 @@ def tedana_workflow( if keep_restarting: io_generator.overwrite = True # Create a re-initialized selector object if rerunning + # Since external_regressor_config might have been expanded to remove + # regular expressions immediately after initialization, + # store and copy this key + tmp_external_regressor_config = selector.tree["external_regressor_config"] selector = ComponentSelector(tree) + selector.tree["external_regressor_config"] = tmp_external_regressor_config RepLGR.disabled = True # Disable the report to avoid duplicate text RepLGR.disabled = False # Re-enable the report after the while loop is escaped @@ -789,7 +794,7 @@ def tedana_workflow( mixing_file = io_generator.get_name("ICA mixing tsv") mmix = pd.read_table(mixing_file).values - selector = ComponentSelector(tree) + # selector = ComponentSelector(tree) necessary_metrics = selector.necessary_metrics # The figures require some metrics that might not be used by the decision tree. extra_metrics = ["variance explained", "normalized variance explained", "kappa", "rho"] From 5cdb8411886267208472448eff2003f8fe828538 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Thu, 23 May 2024 22:58:01 -0400 Subject: [PATCH 68/81] Made single function for detrending regressors --- tedana/gscontrol.py | 4 +- tedana/metrics/external.py | 73 ++++--------------- ...xternal_regressors_motion_task_models.json | 3 +- tedana/selection/component_selector.py | 11 ++- tedana/tests/test_utils.py | 28 +++++++ tedana/utils.py | 39 ++++++++++ 6 files changed, 94 insertions(+), 64 deletions(-) diff --git a/tedana/gscontrol.py b/tedana/gscontrol.py index 4ffddf631..a18e488c9 100644 --- a/tedana/gscontrol.py +++ b/tedana/gscontrol.py @@ -5,7 +5,6 @@ import numpy as np import pandas as pd from scipy import stats -from scipy.special import lpmv from tedana import utils @@ -64,8 +63,7 @@ def gscontrol_raw(catd, optcom, n_echos, io_generator, dtrank=4): ) # Legendre polynomial basis for denoising - bounds = np.linspace(-1, 1, optcom.shape[-1]) - legendre_arr = np.column_stack([lpmv(0, vv, bounds) for vv in range(dtrank)]) + legendre_arr = utils.create_legendre_polynomial_basis_set(optcom.shape[-1], dtrank=dtrank) # compute mean, std, mask local to this function # inefficient, but makes this function a bit more modular diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index a44f75b6c..b6696867e 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -2,13 +2,14 @@ import logging import re -from typing import Dict, Tuple, Union +from typing import Dict, Tuple import numpy as np import numpy.typing as npt import pandas as pd from scipy import stats +from tedana import utils from tedana.stats import fit_model LGR = logging.getLogger("GENERAL") @@ -208,25 +209,30 @@ def fit_regressors( """ n_vols = mixing.shape[0] - # If the order of polynomial detrending is specified, then pass to make_detrend_regressors - # otherwise the function sets a detrending polynomial order + # If the order of detrending regressors is specified, then pass to + # create_legendre_polynomial_basis_set + # otherwise the function sets an order for the Legendre polynomials if external_regressor_config["detrend"] is True: - detrend_regressors = make_detrend_regressors(n_vols, polort=None) + legendre_arr = utils.create_legendre_polynomial_basis_set(n_vols, dtrank=None) elif ( isinstance(external_regressor_config["detrend"], int) and external_regressor_config["detrend"] > 0 ): - detrend_regressors = make_detrend_regressors( - n_vols, polort=external_regressor_config["detrend"] + legendre_arr = utils.create_legendre_polynomial_basis_set( + n_vols, dtrank=external_regressor_config["detrend"] ) else: LGR.warning( "External regressor fitted without detrending fMRI time series. Only removing mean" ) - detrend_regressors = make_detrend_regressors(n_vols, polort=0) + legendre_arr = utils.create_legendre_polynomial_basis_set(n_vols, dtrank=0) + + detrend_labels = [] + for label_idx in range(legendre_arr.shape[1]): + detrend_labels.append(f"baseline {label_idx}") + detrend_regressors = pd.DataFrame(data=legendre_arr, columns=detrend_labels) if external_regressor_config["calc_stats"].lower() == "f": - # external_regressors = pd.concat([external_regressors, detrend_regressors]) comptable = fit_mixing_to_regressors( comptable, external_regressors, external_regressor_config, mixing, detrend_regressors ) @@ -242,57 +248,6 @@ def fit_regressors( return comptable -def make_detrend_regressors(n_vols: int, polort: Union[int, None] = None) -> pd.DataFrame: - """Create polynomial detrending regressors to use for removing slow drifts from data. - - Parameters - ---------- - n_vols : :obj:`int` - The number of time point in the fMRI time series - polort : :obj:`int` or :obj:`NoneType` - The number of polynomial regressors to create (i.e. 3 is x^0, x^1, x^2) - If None, then this is set to 1+floor(n_vols/150) - - Returns - ------- - detrend_regressors: (n_vols x polort) :obj:`pandas.DataFrame` - Dataframe containing the detrending regressor time series x^0 = 1. - All other regressors are zscored so that they have a mean of 0 and a stdev of 1. - Dataframe column labels are polort0 - polort{polort-1} - """ - if polort is None: - polort = int(1 + np.floor(n_vols / 150)) - - # create polynomial detrending regressors -> each additive term leads - # to more points of transformation [curves] - detrend_regressors = np.zeros((n_vols, polort)) - # create polynomial detrended to the power of 0 [1's], - # **1 [linear trend -> f(x) = a + bx], - # **2 [quadratic trend -> f(x) = a + bx + cx²], - # **3 [cubic trend -> f(x) = f(x) = a + bx + cx² + dx³], - # **4 [quartic trend -> f(x) = a + bx + cx² + dx³ + ex⁴] - for idx in range(polort): - # create a linear space with numbers in range [-1,1] because the mean = 0, - # and include the number of timepoints for each regressor - tmp = np.linspace(-1, 1, num=n_vols) ** idx - if idx == 0: - detrend_regressors[:, idx] = tmp - detrend_labels = ["polort0"] - else: - # detrend the regressors by z-scoring the data (zero-mean-centered & stdev of 1) - detrend_regressors[:, idx] = stats.zscore(tmp) - # concatenate the polynomial power-detrended regressors within a matrix - detrend_labels.append(f"polort{idx}") - - # Vestigial code that was used to test whether outputs looked correct. - # if show_plot: - # plt.plot(detrend_regressors) # display the polynomial power-detrended regressors - # plt.show() - detrend_regressors = pd.DataFrame(data=detrend_regressors, columns=detrend_labels) - - return detrend_regressors - - def fit_mixing_to_regressors( comptable: pd.DataFrame, external_regressors: pd.DataFrame, diff --git a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json index e2573c90d..30ed7e4cb 100644 --- a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json +++ b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json @@ -264,7 +264,8 @@ "parameters": { "new_classification": "rejected", "decide_comps": ["provisionalreject", "unclassified"] - } + }, + "kwargs": {"tag": "Unlikely BOLD"} } ] } diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index fc3f791c0..5b225ffff 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -25,7 +25,16 @@ # A user can run the desision tree either using one of these # names or by giving the full path to a tree in a different # location -DEFAULT_TREES = ["minimal", "meica", "tedana_orig"] +# Including the demo trees here so people do not need to type the full +# path, but these are not listed as options in the CLI help menu until +# they are actually validated as useful on some data +DEFAULT_TREES = [ + "minimal", + "meica", + "tedana_orig", + "demo_minimal_external_regressors_single_model", + "demo_minimal_external_regressors_motion_task_models", +] class TreeError(Exception): diff --git a/tedana/tests/test_utils.py b/tedana/tests/test_utils.py index b81bf62b3..20c3edd5e 100644 --- a/tedana/tests/test_utils.py +++ b/tedana/tests/test_utils.py @@ -323,6 +323,34 @@ def test_smoke_threshold_map(): assert utils.threshold_map(img, min_cluster_size, sided="bi") is not None +def test_create_legendre_polynomial_basis_set(): + """Checking that accurate Legendre polynomials are created.""" + + n_vols = 100 + legendre_arr = utils.create_legendre_polynomial_basis_set(n_vols, dtrank=6) + + # Testing first 6 orders to 6 decimal accuracy using + # explicit equations rathern than scipy's lpmv + legendre_rounded = np.round(legendre_arr, decimals=6) + bounds = np.linspace(-1, 1, n_vols) + # order 0 (all 1's) + assert (legendre_arr[:, 0] == 1).sum() == n_vols + # order 1 (y=x) + assert np.abs((legendre_rounded[:, 1] - np.round(bounds, decimals=6))).sum() == 0 + # order 2 (y = 0.5*(3*x^2 - 1)) + tmp_o2 = 0.5 * (3 * bounds**2 - 1) + assert np.abs((legendre_rounded[:, 2] - np.round(tmp_o2, decimals=6))).sum() == 0 + # order 3 (y = 0.5*(5*x^3 - 3*x)) + tmp_o3 = 0.5 * (5 * bounds**3 - 3 * bounds) + assert np.abs((legendre_rounded[:, 3] - np.round(tmp_o3, decimals=6))).sum() == 0 + # order 4 (y = 0.125*(35*x^4 - 30*x^2 + 3)) + tmp_o4 = 0.125 * (35 * bounds**4 - 30 * bounds**2 + 3) + assert np.abs((legendre_rounded[:, 4] - np.round(tmp_o4, decimals=6))).sum() == 0 + # order 5 (y = 0.125*(63*x^5 - 70*x^3 + 15x)) + tmp_o5 = 0.125 * (63 * bounds**5 - 70 * bounds**3 + 15 * bounds) + assert np.abs((legendre_rounded[:, 5] - np.round(tmp_o5, decimals=6))).sum() == 0 + + def test_sec2millisec(): """Ensure that sec2millisec returns 1000x the input values.""" assert utils.sec2millisec(5) == 5000 diff --git a/tedana/utils.py b/tedana/utils.py index 5235a63f7..9c2c0c67b 100644 --- a/tedana/utils.py +++ b/tedana/utils.py @@ -5,9 +5,11 @@ import platform import sys import warnings +from typing import Union import nibabel as nib import numpy as np +import numpy.typing as npt from bokeh import __version__ as bokeh_version from mapca import __version__ as mapca_version from matplotlib import __version__ as matplotlib_version @@ -18,6 +20,7 @@ from pandas import __version__ as pandas_version from scipy import __version__ as scipy_version from scipy import ndimage +from scipy.special import lpmv from sklearn import __version__ as sklearn_version from sklearn.utils import check_array from threadpoolctl import __version__ as threadpoolctl_version @@ -455,6 +458,42 @@ def threshold_map(img, min_cluster_size, threshold=None, mask=None, binarize=Tru return clust_thresholded +def create_legendre_polynomial_basis_set( + n_vols: int, dtrank: Union[int, None] = None +) -> npt.NDArray: + """ + Create Legendre polynomial basis set for detrending time series. + + Parameters + ---------- + n_vols : :obj:`int` + The number of time points in the fMRI time series + dtrank : :obj:`int`, optional + Specifies degree of Legendre polynomial basis function for estimating + spatial global signal. Default: None + If None, then this is set to 1+floor(n_vols/150) + + Returns + ------- + legendre_arr : (T X R) :obj:`np.ndarray` + A time by rank matrix of the first dtrank order Legendre polynomials. + These include: + Order 0: y = 1 + Order 1: y = x + Order 2: y = 0.5*(3*x^2 - 1) + Order 3: y = 0.5*(5*x^3 - 3*x) + Order 4: y = 0.125*(35*x^4 - 30*x^2 + 3) + Order 5: y = 0.125*(63*x^5 - 70*x^3 + 15x) + """ + if dtrank is None: + dtrank = int(1 + np.floor(n_vols / 150)) + + bounds = np.linspace(-1, 1, n_vols) + legendre_arr = np.column_stack([lpmv(0, vv, bounds) for vv in range(dtrank)]) + + return legendre_arr + + def sec2millisec(arr): """ Convert seconds to milliseconds. From 8d0959946e7a89fe0d1fac57fc2c945019f3aacd Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Fri, 24 May 2024 17:33:48 -0400 Subject: [PATCH 69/81] added tests for external fit_regressors and fix_mixing_to_regressors --- tedana/metrics/external.py | 11 +- tedana/tests/test_external_metrics.py | 169 +++++++++++++++++++++++++- 2 files changed, 175 insertions(+), 5 deletions(-) diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index b6696867e..d547cf0c2 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -214,6 +214,11 @@ def fit_regressors( # otherwise the function sets an order for the Legendre polynomials if external_regressor_config["detrend"] is True: legendre_arr = utils.create_legendre_polynomial_basis_set(n_vols, dtrank=None) + LGR.info( + "External regressors fit includes detrending with " + f"{legendre_arr.shape[1]} Legendre Polynomial regressors." + ) + elif ( isinstance(external_regressor_config["detrend"], int) and external_regressor_config["detrend"] > 0 @@ -221,11 +226,15 @@ def fit_regressors( legendre_arr = utils.create_legendre_polynomial_basis_set( n_vols, dtrank=external_regressor_config["detrend"] ) + LGR.info( + "External regressors fit includes detrending with " + f"{legendre_arr.shape[1]} Legendre Polynomial regressors." + ) else: LGR.warning( "External regressor fitted without detrending fMRI time series. Only removing mean" ) - legendre_arr = utils.create_legendre_polynomial_basis_set(n_vols, dtrank=0) + legendre_arr = utils.create_legendre_polynomial_basis_set(n_vols, dtrank=1) detrend_labels = [] for label_idx in range(legendre_arr.shape[1]): diff --git a/tedana/tests/test_external_metrics.py b/tedana/tests/test_external_metrics.py index a2326535a..070c07146 100644 --- a/tedana/tests/test_external_metrics.py +++ b/tedana/tests/test_external_metrics.py @@ -6,6 +6,7 @@ import pandas as pd import pytest +from tedana import utils from tedana.io import load_json from tedana.metrics import external from tedana.tests.utils import data_for_testing_info, download_test_data @@ -52,7 +53,8 @@ def sample_external_regressors(regress_choice="valid"): Column 12 (CSF) is comp 11 + Gaussian Noise Column 13 (Signal) is comp 30 + Gaussian Noise - The Gaussian Noise levels are set so that, with an R^2>0.5 threshold: + The base gaussian noise is mean=0, stdev=1. + The scaling weights for components and noise are set so that, with an R^2>0.5 threshold: ICA Comp 8 rejected solely based on the fit to Mot_X ICA Comp 31 looks strongly inversely correlated to Comp 8 and is also rejected ICA Comp 18 rejected based on the combined fit to Mot_X and Mot_Y (signif Motion partial model) @@ -272,9 +274,10 @@ def test_load_validate_external_regressors_smoke(): # -------------- -def test_fit_regressors_succeeds(): - """Test conditions fit_regressors should succeed.""" +def test_fit_regressors(caplog): + """Test conditions fit_regressors succeeds and fails.""" + caplog.set_level(logging.INFO) external_regressors, n_vols = sample_external_regressors() external_regressor_config = sample_external_regressor_config() external_regressor_config_expanded = external.validate_extern_regress( @@ -282,9 +285,167 @@ def test_fit_regressors_succeeds(): ) mixing = sample_mixing_matrix() + # Running with external_regressor_config["detrend"] is True, + # which results in 1 detrending regressor comptable = sample_comptable(mixing.shape[1]) comptable = external.fit_regressors( comptable, external_regressors, external_regressor_config_expanded, mixing ) - # TODO Add validation of output and tests of conditional statements + # Contents will be valided in fit_mixing_to_regressors so just checking column labels here + assert set(comptable.keys()) == { + "Component", + "Fstat Full Model", + "Fstat Task Model", + "Fstat Motion Model", + "Fstat CSF Model", + "pval Full Model", + "pval Task Model", + "pval Motion Model", + "pval CSF Model", + "R2stat Full Model", + "R2stat Task Model", + "R2stat Motion Model", + "R2stat CSF Model", + } + + assert ( + "External regressors fit includes detrending with 1 Legendre Polynomial regressors" + in caplog.text + ) + + caplog.clear() + # Running with external_regressor_config["detrend"]=3, which results in 3 detrending regressors + external_regressor_config["detrend"] = 3 + comptable = sample_comptable(mixing.shape[1]) + comptable = external.fit_regressors( + comptable, external_regressors, external_regressor_config_expanded, mixing + ) + assert ( + "External regressors fit includes detrending with 3 Legendre Polynomial regressors" + in caplog.text + ) + + caplog.clear() + # Running with external_regressor_config["detrend"]=0, + # which results in 1 detrend regressors (demeaning) + external_regressor_config["detrend"] = 0 + comptable = sample_comptable(mixing.shape[1]) + comptable = external.fit_regressors( + comptable, external_regressors, external_regressor_config_expanded, mixing + ) + assert ( + "External regressor fitted without detrending fMRI time series. Only removing mean" + in caplog.text + ) + + caplog.clear() + external_regressor_config["calc_stats"] = "Corr" + comptable = sample_comptable(mixing.shape[1]) + with pytest.raises( + ValueError, + match="calc_stats for external regressors in decision tree is corr, which is not valid.", + ): + comptable = external.fit_regressors( + comptable, external_regressors, external_regressor_config_expanded, mixing + ) + + +def test_fit_mixing_to_regressors(caplog): + """Test conditions fit_mixing_to_regressors succeeds and fails.""" + + caplog.set_level(logging.INFO) + external_regressors, n_vols = sample_external_regressors() + external_regressor_config = sample_external_regressor_config() + external_regressor_config_expanded = external.validate_extern_regress( + external_regressors, external_regressor_config, n_vols + ) + mixing = sample_mixing_matrix() + + # Creating detrend_regressors + legendre_arr = utils.create_legendre_polynomial_basis_set(n_vols, dtrank=None) + detrend_labels = [] + for label_idx in range(legendre_arr.shape[1]): + detrend_labels.append(f"baseline {label_idx}") + detrend_regressors = pd.DataFrame(data=legendre_arr, columns=detrend_labels) + + # Running with external_regressor_config["detrend"] is True, + # which results in 1 detrending regressor + comptable = sample_comptable(mixing.shape[1]) + + comptable = external.fit_mixing_to_regressors( + comptable, + external_regressors, + external_regressor_config_expanded, + mixing, + detrend_regressors, + ) + + # Since a fixed mixing matrix is used, the values should always be consistent + # Comparing just 3 rows and rounding to 6 decimal places to avoid testing failures + # due to differences in floating point precision between systems + output_rows_to_validate = comptable.iloc[[0, 11, 30]].round(decimals=6) + expected_results = pd.DataFrame( + columns=[ + "Component", + "Fstat Full Model", + "Fstat Task Model", + "Fstat Motion Model", + "Fstat CSF Model", + "pval Full Model", + "pval Task Model", + "pval Motion Model", + "pval CSF Model", + "R2stat Full Model", + "R2stat Task Model", + "R2stat Motion Model", + "R2stat CSF Model", + ] + ) + expected_results.loc[0] = [ + "ICA_00", + 0.5898043794795538, + 0.040242260292383224, + 0.6359651437299336, + 0.5882298391006501, + 0.8529159565446598, + 0.8415655022508225, + 0.8033066601119929, + 0.4460627598486151, + 0.1116607090996441, + 0.0005509601152330346, + 0.11119635498661495, + 0.009551010649882286, + ] + expected_results.loc[11] = [ + "ICA_11", + 5.050391950932562, + 0.3101483992796387, + 0.6191428219572478, + 37.021610927761515, + 5.897391126885587e-06, + 0.5792925973677331, + 0.8177727274388485, + 8.422777264538439e-08, + 0.518377055217612, + 0.004230633903377634, + 0.10857438156630017, + 0.377688252390028, + ] + expected_results.loc[30] = [ + "ICA_30", + 5.869398664558788, + 109.32951177196031, + 6.215675922255525, + 1.524970189426933, + 7.193855290354989e-07, + 3.3306690738754696e-16, + 5.303071232143353e-07, + 0.22160450819684074, + 0.5557244697248107, + 0.5996259777665551, + 0.5501080476751579, + 0.024389778752502478, + ] + + assert output_rows_to_validate.compare(expected_results.round(decimals=6)).empty From 440af0136af58b20a6410094782e3ca828c4673c Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Tue, 4 Jun 2024 14:43:22 -0400 Subject: [PATCH 70/81] Full tests in test_external_metrics.py --- tedana/metrics/external.py | 18 ++-- tedana/tests/test_external_metrics.py | 115 ++++++++++++++++++++++++-- 2 files changed, 119 insertions(+), 14 deletions(-) diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index d547cf0c2..426b6d5c5 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -397,7 +397,7 @@ def build_fstat_regressor_models( regressor_labels = external_regressors.columns detrend_regressors_arr = detrend_regressors.to_numpy() regressor_models = {"base": detrend_regressors_arr} - LGR.info(f"Size for base Regressor Model: {regressor_models['base'].shape}") + LGR.info(f"Size for base regressor model: {regressor_models['base'].shape}") if "task_keep" in external_regressor_config: task_keep_model = external_regressor_config["task_keep"] @@ -418,17 +418,21 @@ def build_fstat_regressor_models( axis=1, ) tmp_model_labels[model_name].update(set(detrend_regressors.columns)) - LGR.info(f"Size for {model_name} Model: {regressor_models[model_name].shape}") LGR.info( - f"Regressors In {model_name} Model:'{model_name}': {tmp_model_labels[model_name]}" + f"Size for {model_name} regressor model: {regressor_models[model_name].shape}" ) + LGR.info(f"Regressors in {model_name} model: {sorted(tmp_model_labels[model_name])}") # Remove task_keep regressors from regressor_labels before calculating partial models regressor_labels = set(regressor_labels) - set(task_keep_model) else: regressor_models["full"] = np.concatenate( (detrend_regressors_arr, stats.zscore(external_regressors.to_numpy(), axis=0)), axis=1 ) - LGR.info(f"Size for full Regressor Model: {regressor_models['full'].shape}") + LGR.info(f"Size for full regressor model: {regressor_models['full'].shape}") + LGR.info( + "Regressors in full model: " + f"{sorted(set(regressor_labels).union(set(detrend_regressors.columns)))}" + ) for pmodel in partial_models: # For F statistics, the other models to test are those that include everything EXCEPT @@ -449,12 +453,12 @@ def build_fstat_regressor_models( ) keep_labels.update(set(detrend_regressors.columns)) LGR.info( - f"Size for External Regressor Partial Model '{no_pmodel}': " + f"Size for external regressor partial model '{no_pmodel}': " f"{regressor_models[no_pmodel].shape}" ) LGR.info( - "Regressors In Partial Model (everything but regressors of interest) " - f"'{no_pmodel}': {keep_labels}" + "Regressors in partial model (everything but regressors of interest) " + f"'{no_pmodel}': {sorted(keep_labels)}" ) return regressor_models diff --git a/tedana/tests/test_external_metrics.py b/tedana/tests/test_external_metrics.py index 070c07146..99ba30fb5 100644 --- a/tedana/tests/test_external_metrics.py +++ b/tedana/tests/test_external_metrics.py @@ -135,7 +135,19 @@ def sample_mixing_matrix(): def sample_comptable(n_components): - """Create an empty component table.""" + """Create an empty component table. + + Parameters + ---------- + n_components : :obj:`int` + The number of components (rows) in the compponent table DataFrame + + Returns + ------- + component_table : :obj:`pd.DataFrame` + A component table with a single "Component" column with + "ICA_" number for each row + """ row_vals = [] for ridx in range(n_components): @@ -144,6 +156,30 @@ def sample_comptable(n_components): return pd.DataFrame(data={"Component": row_vals}) +def sample_detrend_regressors(n_vols, dtrank=None): + """ + Creates Legendre polynomial detrending regressors. + + Parameters + ---------- + n_vols: :obj:`int` + The number of volumes or time points for the regressors + dtrank : :obj:`int` or None + The rank (number) of detrending regressors to create + Automatically calculate if None (default) + + Returns + ------- + detrend_regressors : :obj:`pd.DataFrame` The specified detrending regressors + """ + + legendre_arr = utils.create_legendre_polynomial_basis_set(n_vols, dtrank) + detrend_labels = [] + for label_idx in range(legendre_arr.shape[1]): + detrend_labels.append(f"baseline {label_idx}") + return pd.DataFrame(data=legendre_arr, columns=detrend_labels) + + # validate_extern_regress # ----------------------- def test_validate_extern_regress_succeeds(caplog): @@ -351,9 +387,15 @@ def test_fit_regressors(caplog): ) +# fit_mixing_to_regressors +# -------------- + + def test_fit_mixing_to_regressors(caplog): """Test conditions fit_mixing_to_regressors succeeds and fails.""" + # Note: Outputs from fit_model_with_stats are also tested within this function + caplog.set_level(logging.INFO) external_regressors, n_vols = sample_external_regressors() external_regressor_config = sample_external_regressor_config() @@ -362,12 +404,7 @@ def test_fit_mixing_to_regressors(caplog): ) mixing = sample_mixing_matrix() - # Creating detrend_regressors - legendre_arr = utils.create_legendre_polynomial_basis_set(n_vols, dtrank=None) - detrend_labels = [] - for label_idx in range(legendre_arr.shape[1]): - detrend_labels.append(f"baseline {label_idx}") - detrend_regressors = pd.DataFrame(data=legendre_arr, columns=detrend_labels) + detrend_regressors = sample_detrend_regressors(n_vols, dtrank=None) # Running with external_regressor_config["detrend"] is True, # which results in 1 detrending regressor @@ -449,3 +486,67 @@ def test_fit_mixing_to_regressors(caplog): ] assert output_rows_to_validate.compare(expected_results.round(decimals=6)).empty + + +# build_fstat_regressor_models +# -------------- + + +def test_build_fstat_regressor_models(caplog): + """Test conditions build_fstat_regressor_models succeeds and fails.""" + + caplog.set_level(logging.INFO) + external_regressors, n_vols = sample_external_regressors() + external_regressor_config = sample_external_regressor_config() + external_regressor_config_expanded = external.validate_extern_regress( + external_regressors, external_regressor_config, n_vols + ) + + detrend_regressors = sample_detrend_regressors(n_vols, dtrank=3) + + # Running with f_stats_partial_models + regressor_models = external.build_fstat_regressor_models( + external_regressors, external_regressor_config_expanded, detrend_regressors + ) + + assert regressor_models["full"].shape == (n_vols, 16) + assert ( + "Regressors in full model: ['CSF', 'Mot_Pitch', 'Mot_Roll', 'Mot_X', 'Mot_Y', 'Mot_Yaw', " + "'Mot_Z', 'Mot_d1_Pitch', 'Mot_d1_Roll', 'Mot_d1_X', 'Mot_d1_Y', 'Mot_d1_Yaw', " + "'Mot_d1_Z', 'baseline 0', 'baseline 1', 'baseline 2']" + ) in caplog.text + assert regressor_models["task keep"].shape == (n_vols, 4) + assert ( + "Regressors in task keep model: ['Signal', 'baseline 0', 'baseline 1', 'baseline 2']" + in caplog.text + ) + + assert regressor_models["no CSF"].shape == (n_vols, 15) + assert ( + "Regressors in partial model (everything but regressors of interest) 'no CSF': " + "['Mot_Pitch', 'Mot_Roll', 'Mot_X', 'Mot_Y', 'Mot_Yaw', 'Mot_Z', 'Mot_d1_Pitch', " + "'Mot_d1_Roll', 'Mot_d1_X', 'Mot_d1_Y', 'Mot_d1_Yaw', 'Mot_d1_Z', " + "'baseline 0', 'baseline 1', 'baseline 2']" + ) in caplog.text + assert regressor_models["no Motion"].shape == (n_vols, 4) + assert ( + "Regressors in partial model (everything but regressors of interest) 'no Motion': " + "['CSF', 'baseline 0', 'baseline 1', 'baseline 2']" in caplog.text + ) + + # Rerunning with no "task_keep" model (creates full model slightly differently) + # Since the "Signal" column is still in exernal regressors, it would be included + # in the full model + external_regressor_config = sample_external_regressor_config("no_task") + external_regressor_config_expanded = external.validate_extern_regress( + external_regressors, external_regressor_config, n_vols + ) + regressor_models = external.build_fstat_regressor_models( + external_regressors, external_regressor_config_expanded, detrend_regressors + ) + assert regressor_models["full"].shape == (n_vols, 17) + assert ( + "Regressors in full model: ['CSF', 'Mot_Pitch', 'Mot_Roll', 'Mot_X', 'Mot_Y', 'Mot_Yaw', " + "'Mot_Z', 'Mot_d1_Pitch', 'Mot_d1_Roll', 'Mot_d1_X', 'Mot_d1_Y', 'Mot_d1_Yaw', " + "'Mot_d1_Z', 'Signal', 'baseline 0', 'baseline 1', 'baseline 2']" + ) in caplog.text From 5cb26b300ef405e7674b4e7cb2bb1952da8f0cd1 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Wed, 5 Jun 2024 17:23:49 -0400 Subject: [PATCH 71/81] adding tests --- tedana/metrics/external.py | 4 +--- tedana/stats.py | 6 +++--- tedana/tests/test_selection_utils.py | 17 +++++++++++++-- tedana/tests/test_stats.py | 32 +++++++++++++++++++++++++++- 4 files changed, 50 insertions(+), 9 deletions(-) diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index 426b6d5c5..f614731a1 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -135,9 +135,7 @@ def validate_extern_regress( ) tmp_partial_model_names.update(set(tmp_replacements)) else: - if not isinstance(tmp_name, list): - tmp_name = [tmp_name] - tmp_partial_model_names.update(tmp_name) + tmp_partial_model_names.add(tmp_name) external_regressor_config[partial_models] = list(tmp_partial_model_names) if expected_regressor_names.intersection(tmp_partial_model_names): LGR.warning( diff --git a/tedana/stats.py b/tedana/stats.py index 3a82f0841..ad9e63c0f 100644 --- a/tedana/stats.py +++ b/tedana/stats.py @@ -231,7 +231,7 @@ def fit_model(x, y, output_residual=False): Parameters ---------- - x : (R X T) :obj:`numpy.ndarray` + x : (T X R) :obj:`numpy.ndarray` 2D array with the regressors for the specified model an time y : (T X C) :obj:`numpy.ndarray` Time by mixing matrix components for the time series for fitting @@ -245,9 +245,9 @@ def fit_model(x, y, output_residual=False): The residual time series for the fit (only if output_residual is True) betas : (R X C) :obj:`numpy.ndarray` The magnitude fits for the model (only if output_residual is False) - sse : (R X C) :obj:`numpy.ndarray` + sse : (C) :obj:`numpy.ndarray` The sum of square error for the model (only if output_residual is False) - df : (R X C) :obj:`numpy.ndarray` + df : :obj:`int` The degrees of freeom for the model (only if output_residual is False) (timepoints - number of regressors) """ diff --git a/tedana/tests/test_selection_utils.py b/tedana/tests/test_selection_utils.py index 465fb8bcc..73ae84c30 100644 --- a/tedana/tests/test_selection_utils.py +++ b/tedana/tests/test_selection_utils.py @@ -241,8 +241,12 @@ def test_confirm_metrics_exist_succeeds(): # Testing for metrics that exist with 1 or 2 necessary metrics in a set # Returns True if an undefined metric exists so using "assert not" + # Testing if it can find a single metric assert not selection_utils.confirm_metrics_exist(component_table, {"kappa"}) + # Testing if it can find multiple metrics assert not selection_utils.confirm_metrics_exist(component_table, {"kappa", "rho"}) + # Testing if it can find metrics that use regular expressions + assert not selection_utils.confirm_metrics_exist(component_table, {"kappa", "^count.*$"}) def test_confirm_metrics_exist_fails(): @@ -251,13 +255,22 @@ def test_confirm_metrics_exist_fails(): component_table = sample_component_table(options="comptable") # Should fail with and error would have default or pre-defined file name - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="Necessary metrics for unknown function"): selection_utils.confirm_metrics_exist(component_table, {"kappa", "quack"}) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match=r"MISSING METRICS: \['quack'\]"): + selection_utils.confirm_metrics_exist( + component_table, + necessary_metrics={"kappa", "quack"}, + function_name="dec_left_op_right", + ) + with pytest.raises(ValueError, match="Necessary metrics for farm"): selection_utils.confirm_metrics_exist( component_table, {"kappa", "mooo"}, function_name="farm" ) + with pytest.raises(ValueError, match=r"MISSING METRICS: \['\^mount.\*\$'\]."): + selection_utils.confirm_metrics_exist(component_table, {"kappa", "^mount.*$"}) + def test_log_decision_tree_step_smoke(): """A smoke test for log_decision_tree_step.""" diff --git a/tedana/tests/test_stats.py b/tedana/tests/test_stats.py index 9d64f67ac..5214daa72 100644 --- a/tedana/tests/test_stats.py +++ b/tedana/tests/test_stats.py @@ -4,8 +4,9 @@ import numpy as np import pytest +from numpy.matlib import repmat -from tedana.stats import computefeats2, get_coeffs, getfbounds +from tedana.stats import computefeats2, fit_model, get_coeffs, getfbounds def test_break_computefeats2(): @@ -140,3 +141,32 @@ def test_smoke_getfbounds(): assert f05 is not None assert f025 is not None assert f01 is not None + + +def test_fit_model(): + """Tests for fit_model.""" + + # set up data where y = weights*x + residuals + r = 15 # number of regressors + t = 300 # number of time points + c = 50 # number of components + rng = np.random.default_rng(42) # using a fixed seed + x = rng.random(size=(t, r)) + weights = rng.random(size=(r, c)) + # Making the residuals sufficiently small for the fit to be precise to 4 decimals + residuals = rng.random(size=(t, c)) / 1000000 + y = np.empty((t, c)) + for cidx in range(c): + y[:, cidx] = (x * repmat(weights[:, cidx], t, 1)).sum(axis=1) + y = y + residuals + + # Fitting model and confirming outputs are the correct shape + # and beta fits match inputted weights to four decimal places + betas, sse, df = fit_model(x, y) + assert df == (t - r) + assert sse.shape == (c,) + assert (np.round(betas, decimals=4) == np.round(weights, decimals=4)).all() + + # Outputting the residual and checking it matches the inputted residual + fit_residuals = fit_model(x, y, output_residual=True) + assert (np.round(fit_residuals, decimals=4) == np.round(residuals, decimals=4)).all() From fdf6f03a3f1f97e97743b2d7914854980ce41b66 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Mon, 10 Jun 2024 20:34:49 -0400 Subject: [PATCH 72/81] fixed extern regress validation warnings and added tests --- tedana/metrics/_utils.py | 7 +- ...xternal_regressors_motion_task_models.json | 59 ++++++---- ...imal_external_regressors_single_model.json | 38 +++--- tedana/selection/component_selector.py | 35 +++--- tedana/tests/test_component_selector.py | 109 ++++++++++++++++-- 5 files changed, 182 insertions(+), 66 deletions(-) diff --git a/tedana/metrics/_utils.py b/tedana/metrics/_utils.py index 2d0f878a4..6e2da2d10 100644 --- a/tedana/metrics/_utils.py +++ b/tedana/metrics/_utils.py @@ -34,10 +34,13 @@ def add_external_dependencies(dependency_config: Dict, external_regressor_config if external_regressor_config["calc_stats"].lower() == "f": model_names = ["Full"] if "f_stats_partial_models" in set(external_regressor_config.keys()): - model_names.append(external_regressor_config["f_stats_partial_models"]) + if isinstance(external_regressor_config["f_stats_partial_models"], list): + model_names.extend(external_regressor_config["f_stats_partial_models"]) + else: # A single string + model_names.append(external_regressor_config["f_stats_partial_models"]) if "task_keep" in set(external_regressor_config.keys()): - model_names.append("task_keep") + model_names.append("Task") for model_name in model_names: for stat_type in ["Fstat", "R2stat", "pval"]: diff --git a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json index 30ed7e4cb..274da28bd 100644 --- a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json +++ b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json @@ -12,10 +12,22 @@ "signal-noise_t", "variance explained", "pval Full Model", - "R2stat Full Model" + "pval Task Model", + "pval Motion Model", + "pval CSF Model", + "R2stat Full Model", + "R2stat Task Model" ], "intermediate_classifications": ["provisionalaccept", "provisionalreject"], - "classification_tags": ["Likely BOLD", "Unlikely BOLD", "Low variance"], + "classification_tags": [ + "Likely BOLD", + "Unlikely BOLD", + "Low variance", + "External regressors", + "Fits motion external regressors", + "Fits CSF external regressors", + "Fits task" + ], "external_regressor_config": { "regress_ID": "Fmodel", "info": "Fits all external regressors to a single model using an F statistic", @@ -36,7 +48,7 @@ { "functionname": "dec_left_op_right", "parameters": { - "if_true": "rejected", + "if_true": "provisionalreject", "if_false": "nochange", "decide_comps": "all", "op": ">", @@ -48,7 +60,7 @@ { "functionname": "dec_left_op_right", "parameters": { - "if_true": "rejected", + "if_true": "provisionalreject", "if_false": "nochange", "decide_comps": "all", "op": ">", @@ -73,7 +85,7 @@ { "functionname": "dec_left_op_right", "parameters": { - "if_true": "rejected", + "if_true": "provisionalreject", "if_false": "nochange", "decide_comps": "all", "op": ">", @@ -90,7 +102,7 @@ { "functionname": "dec_left_op_right", "parameters": { - "if_true": "rejected", + "if_true": "provisionalreject", "if_false": "nochange", "decide_comps": "all", "op": ">", @@ -133,30 +145,33 @@ { "functionname": "dec_left_op_right", "parameters": { - "if_true": "accepted", + "if_true": "provisionalreject", "if_false": "nochange", - "decide_comps": "provisionalaccept", + "decide_comps": ["provisionalreject", "provisionalaccept"], "op": ">", - "left": "kappa", - "right": "rho" + "left": "rho", + "right": "rho_elbow_liberal" }, - "kwargs": { - "log_extra_info": "If kappa>elbow and kappa>2*rho accept even if rho>elbow", - "right_scale": 2, - "tag_if_true": "Likely BOLD" - } + "kwargs": {"tag_if_true": "Unlikely BOLD"} }, { "functionname": "dec_left_op_right", "parameters": { - "if_true": "provisionalreject", + "if_true": "provisionalaccept", "if_false": "nochange", - "decide_comps": ["provisionalreject", "provisionalaccept"], + "decide_comps": "provisionalreject", "op": ">", - "left": "rho", - "right": "rho_elbow_liberal" + "left": "kappa", + "right": "rho" }, - "kwargs": {"tag_if_true": "Unlikely BOLD"} + "kwargs": { + "log_extra_info": "If kappa>elbow and kappa>2*rho accept even if rho>elbow", + "right_scale": 2, + "op2": ">", + "left2": "kappa", + "right2": "kappa_elbow_kundu", + "tag_if_true": "Likely BOLD" + } }, { "functionname": "dec_left_op_right", @@ -173,7 +188,7 @@ "left2": "R2stat Full Model", "right2": 0.5, "log_extra_info": "If external regressors fit with p<0.05 and model R2>0.5 of the variance, then reject.", - "tag_if_true": "external regressors" + "tag_if_true": "External regressors" }, "_comment": "Provisionally rejecting components that fit to the external regressor noise model" }, @@ -236,7 +251,7 @@ "op3": ">=", "left3": "kappa", "right3": "kappa_elbow_kundu", - "tag_if_true": "fits task" + "tag_if_true": "Fits task" }, "_comment": "Keep if it fits task regressors and contains T2* signal, as defined by kappa>elbow" }, diff --git a/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json b/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json index a12223903..2733b71f5 100644 --- a/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json +++ b/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json @@ -15,7 +15,7 @@ "R2stat Full Model" ], "intermediate_classifications": ["provisionalaccept", "provisionalreject"], - "classification_tags": ["Likely BOLD", "Unlikely BOLD", "Low variance"], + "classification_tags": ["Likely BOLD", "Unlikely BOLD", "Low variance", "External regressors"], "external_regressor_config": { "regress_ID": "Fmodel", "info": "Fits all external regressors to a single model using an F statistic", @@ -27,10 +27,7 @@ { "functionname": "manual_classify", "parameters": {"new_classification": "unclassified", "decide_comps": "all"}, - "kwargs": { - "clear_classification_tags": true, - "dont_warn_reclassify": true - } + "kwargs": {"clear_classification_tags": true, "dont_warn_reclassify": true} }, { "functionname": "dec_left_op_right", @@ -128,7 +125,18 @@ { "functionname": "dec_left_op_right", "parameters": { - "if_true": "accepted", + "if_true": "provisionalreject", + "if_false": "nochange", + "decide_comps": ["provisionalreject", "provisionalaccept"], + "op": ">", + "left": "rho", + "right": "rho_elbow_liberal" + } + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "provisionalaccept", "if_false": "nochange", "decide_comps": "provisionalaccept", "op": ">", @@ -138,24 +146,16 @@ "kwargs": { "log_extra_info": "If kappa>elbow and kappa>2*rho accept even if rho>elbow", "right_scale": 2, + "op2": ">", + "left2": "kappa", + "right2": "kappa_elbow_kundu", "tag_if_true": "Likely BOLD" } }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "provisionalreject", - "if_false": "nochange", - "decide_comps": ["provisionalreject", "provisionalaccept"], - "op": ">", - "left": "rho", - "right": "rho_elbow_liberal" - } - }, { "functionname": "dec_variance_lessthan_thresholds", "parameters": { - "if_true": "accepted", + "if_true": "provisionalaccept", "if_false": "nochange", "decide_comps": "provisionalreject" }, @@ -181,7 +181,7 @@ "left2": "R2stat Full Model", "right2": 0.5, "log_extra_info": "If external regressors fit with p<0.05 and model R2>0.5 of the variance, then reject.", - "tag_if_true": "external regressors" + "tag_if_true": "External regressors" } }, { diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 5b225ffff..f4ee10c8d 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -240,8 +240,8 @@ def validate_tree(tree: Dict) -> Dict: if tree["external_regressor_config"] is not None: external_regressor_config = tree["external_regressor_config"] # Define the fields that should always be present - dict_expected_keys = set(["regress_ID", "info", "detrend", "calc_stats"]) - + dict_expected_keys = set(["regress_ID", "info", "report", "detrend", "calc_stats"]) + dict_optional_keys = set(["task_keep"]) # Right now, "f" is the only option, but this leaves open the possibility # to have additional options calc_stats_key_options = set("f") @@ -251,20 +251,21 @@ def validate_tree(tree: Dict) -> Dict: if missing_keys: err_msg += f"External regressor dictionary missing required fields: {missing_keys}\n" - if external_regressor_config["calc_stats"].lower() not in calc_stats_key_options: - err_msg += ( - "calc_stats in external_regressor_config is " - f"{external_regressor_config['calc_stats']}. It must be one of the following: " - f"{calc_stats_key_options}\n" - ) + if "calc_stats" in set(external_regressor_config.keys()): + if external_regressor_config["calc_stats"].lower() not in calc_stats_key_options: + err_msg += ( + "calc_stats in external_regressor_config is " + f"{external_regressor_config['calc_stats']}. It must be one of the following: " + f"{calc_stats_key_options}\n" + ) - if (external_regressor_config["calc_stats"].lower() != "f") and ( - "f_stats_partial_models" in set(external_regressor_config.keys()) - ): - err_msg += ( - "External regressor dictionary cannot include" - "f_stats_partial_models if calc_stats is not F\n" - ) + if (external_regressor_config["calc_stats"].lower() != "f") and ( + "f_stats_partial_models" in set(external_regressor_config.keys()) + ): + err_msg += ( + "External regressor dictionary cannot include " + "f_stats_partial_models if calc_stats is not F\n" + ) if "f_stats_partial_models" in set(external_regressor_config.keys()): dict_expected_keys.add("f_stats_partial_models") @@ -280,7 +281,9 @@ def validate_tree(tree: Dict) -> Dict: ) # Warn if unused fields exist - unused_keys = set(external_regressor_config.keys()) - set(dict_expected_keys) + unused_keys = ( + set(external_regressor_config.keys()) - set(dict_expected_keys) - dict_optional_keys + ) if unused_keys: LGR.warning( "External regressor dictionary includes fields that " diff --git a/tedana/tests/test_component_selector.py b/tedana/tests/test_component_selector.py index 9b45906cc..d37ff52e7 100644 --- a/tedana/tests/test_component_selector.py +++ b/tedana/tests/test_component_selector.py @@ -2,6 +2,7 @@ import glob import json +import logging import os import os.path as op @@ -12,6 +13,8 @@ from tedana.utils import get_resource_path THIS_DIR = os.path.dirname(os.path.abspath(__file__)) +LGR = logging.getLogger("GENERAL") + # ---------------------------------------------------------------------- # Functions Used For Tests @@ -40,6 +43,9 @@ def dicts_to_test(treechoice): "missing_function": An undefined decision node function "missing_key": A dict missing one of the required keys (report) "null_value": A parameter in one node improperly has a null value + "external_missing_key": external_regressors_config missing a required key + "external_invalid_calc_stats": external_regressors_config calc_stats is not "F" + "external_missing_partial_model": external regress names partial F model but not regressors Returns ------- @@ -56,6 +62,18 @@ def dicts_to_test(treechoice): "necessary_metrics": ["kappa", "rho"], "intermediate_classifications": ["random1"], "classification_tags": ["Random1"], + "external_regressor_config": { + "regress_ID": "Fmodel", + "info": "Info Text", + "report": "Report Text", + "detrend": True, + "calc_stats": "F", + "f_stats_partial_models": ["Motion", "CSF"], + "Motion": ["^mot_.*$"], + "CSF": ["^csf.*$"], + "task_keep": ["^signal.*$"], + "extra field": 42, + }, "nodes": [ { "functionname": "dec_left_op_right", @@ -134,6 +152,12 @@ def dicts_to_test(treechoice): tree.pop("report") elif treechoice == "null_value": tree["nodes"][0]["parameters"]["left"] = None + elif treechoice == "external_missing_key": + tree["external_regressor_config"].pop("calc_stats") + elif treechoice == "external_invalid_calc_stats": + tree["external_regressor_config"]["calc_stats"] = "corr" + elif treechoice == "external_missing_partial_model": + tree["external_regressor_config"].pop("Motion") else: raise Exception(f"{treechoice} is an invalid option for treechoice") @@ -223,12 +247,45 @@ def test_validate_tree_succeeds(): assert component_selector.validate_tree(tree) -def test_validate_tree_warnings(): +def test_validate_tree_warnings(caplog): """Test to make sure validate_tree triggers all warning conditions.""" + caplog.set_level(logging.WARNING) # A tree that raises all possible warnings in the validator should still be valid assert component_selector.validate_tree(dicts_to_test("valid")) + assert ( + r"Decision tree includes fields that are not used or logged {'unused_key'}" in caplog.text + ) + assert ( + r"{'random1'} in node 0 of the decision tree includes " + "a classification tag that was not predefined" + ) in caplog.text + assert ( + r"{'random2', 'nochange'} in node 1 of the decision tree includes a classification" + in caplog.text + ) + assert ( + r"{'random2notpredefined'} in node 1 of the decision tree " + "includes a classification tag that was not predefined" + ) in caplog.text + assert ( + r"{'random2notpredefined'} in node 2 of the decision tree includes " + "a classification label that was not predefined" + ) in caplog.text + assert ( + r"{'Random2_NotPredefined'} in node 2 of the decision tree " + "includes a classification tag that was not predefined" + ) in caplog.text + assert ( + r"Node 3 includes the 'log_extra_report' parameter. " + "This was removed from the code and will not be used." + ) in caplog.text + assert ( + "External regressor dictionary includes fields " + r"that are not used or logged {'extra field'}" + ) in caplog.text + def test_validate_tree_fails(): """Test to make sure validate_tree fails for invalid trees. @@ -238,27 +295,65 @@ def test_validate_tree_fails(): """ # An empty dict should not be valid - with pytest.raises(component_selector.TreeError): + with pytest.raises( + component_selector.TreeError, match="Decision tree missing required fields" + ): component_selector.validate_tree({}) # A tree that is missing a required key should not be valid - with pytest.raises(component_selector.TreeError): + with pytest.raises( + component_selector.TreeError, match=r"Decision tree missing required fields: {'report'}" + ): component_selector.validate_tree(dicts_to_test("missing_key")) # Calling a selection node function that does not exist should not be valid - with pytest.raises(component_selector.TreeError): + with pytest.raises( + component_selector.TreeError, + match=r"Node 0 has invalid functionname parameter: not_a_function", + ): component_selector.validate_tree(dicts_to_test("missing_function")) # Calling a function with an non-existent required parameter should not be valid - with pytest.raises(component_selector.TreeError): + with pytest.raises( + component_selector.TreeError, + match=r"Node 0 has additional, undefined required parameters: {'nonexistent_req_param'}", + ): component_selector.validate_tree(dicts_to_test("extra_req_param")) # Calling a function with an non-existent optional parameter should not be valid - with pytest.raises(component_selector.TreeError): + with pytest.raises( + component_selector.TreeError, match=r"Node 0 has additional, undefined optional parameters" + ): component_selector.validate_tree(dicts_to_test("extra_opt_param")) # Calling a function missing a required parameter should not be valid - with pytest.raises(component_selector.TreeError): + with pytest.raises( + component_selector.TreeError, match=r"Node 0 is missing required parameter" + ): component_selector.validate_tree(dicts_to_test("missing_req_param")) + with pytest.raises( + component_selector.TreeError, + match=r"External regressor dictionary missing required fields: {'calc_stats'}", + ): + component_selector.validate_tree(dicts_to_test("external_missing_key")) + + with pytest.raises( + component_selector.TreeError, + match=( + "External regressor dictionary cannot include " + "f_stats_partial_models if calc_stats is not F" + ), + ): + component_selector.validate_tree(dicts_to_test("external_invalid_calc_stats")) + + with pytest.raises( + component_selector.TreeError, + match=( + "External regressor dictionary missing required fields for partial models " + r"defined in f_stats_partial_models: {'Motion'}" + ), + ): + component_selector.validate_tree(dicts_to_test("external_missing_partial_model")) + def test_check_null_fails(): """Tests to trigger check_null missing parameter error.""" From 75b27f483c52d8e87744fb31dad940a5707cdb46 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Mon, 10 Jun 2024 22:45:55 -0400 Subject: [PATCH 73/81] sorting set values for test outputs --- tedana/selection/component_selector.py | 14 +++++++++----- tedana/tests/test_component_selector.py | 20 ++++++++------------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index f4ee10c8d..4e91fe96a 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -138,7 +138,9 @@ def validate_tree(tree: Dict) -> Dict: if k in unused_keys: unused_keys.remove(k) if unused_keys: - LGR.warning(f"Decision tree includes fields that are not used or logged {unused_keys}") + LGR.warning( + f"Decision tree includes fields that are not used or logged {sorted(unused_keys)}" + ) # Combine the default classifications with the user inputted classifications all_classifications = set(tree.get("intermediate_classifications")) | set( @@ -208,7 +210,9 @@ def validate_tree(tree: Dict) -> Dict: compclass = compclass | set(tmp_comp) nonstandard_labels = compclass.difference(all_classifications) if nonstandard_labels: - LGR.warning(f"{compclass} in node {i} of the decision tree includes a classification") + LGR.warning( + f"{sorted(compclass)} in node {i} of the decision tree includes a classification" + ) if "decide_comps" in node.get("parameters").keys(): tmp_comp = node["parameters"]["decide_comps"] if isinstance(tmp_comp, str): @@ -217,7 +221,7 @@ def validate_tree(tree: Dict) -> Dict: nonstandard_labels = compclass.difference(all_decide_comps) if nonstandard_labels: LGR.warning( - f"{compclass} in node {i} of the decision tree includes a classification " + f"{sorted(compclass)} in node {i} of the decision tree includes a classification " "label that was not predefined" ) @@ -232,7 +236,7 @@ def validate_tree(tree: Dict) -> Dict: undefined_classification_tags = tagset.difference(set(tree.get("classification_tags"))) if undefined_classification_tags: LGR.warning( - f"{tagset} in node {i} of the decision tree includes a classification " + f"{sorted(tagset)} in node {i} of the decision tree includes a classification " "tag that was not predefined" ) @@ -287,7 +291,7 @@ def validate_tree(tree: Dict) -> Dict: if unused_keys: LGR.warning( "External regressor dictionary includes fields that " - f"are not used or logged {unused_keys}" + f"are not used or logged {sorted(unused_keys)}" ) if err_msg: diff --git a/tedana/tests/test_component_selector.py b/tedana/tests/test_component_selector.py index d37ff52e7..3281cce16 100644 --- a/tedana/tests/test_component_selector.py +++ b/tedana/tests/test_component_selector.py @@ -250,40 +250,36 @@ def test_validate_tree_succeeds(): def test_validate_tree_warnings(caplog): """Test to make sure validate_tree triggers all warning conditions.""" - caplog.set_level(logging.WARNING) # A tree that raises all possible warnings in the validator should still be valid assert component_selector.validate_tree(dicts_to_test("valid")) assert ( - r"Decision tree includes fields that are not used or logged {'unused_key'}" in caplog.text + r"Decision tree includes fields that are not used or logged ['unused_key']" in caplog.text ) assert ( - r"{'random1'} in node 0 of the decision tree includes " + r"['random1'] in node 0 of the decision tree includes " "a classification tag that was not predefined" ) in caplog.text assert ( - r"{'random2', 'nochange'} in node 1 of the decision tree includes a classification" + r"['nochange', 'random2'] in node 1 of the decision tree includes a classification" in caplog.text ) assert ( - r"{'random2notpredefined'} in node 1 of the decision tree " + r"['random2notpredefined'] in node 1 of the decision tree " "includes a classification tag that was not predefined" ) in caplog.text assert ( - r"{'random2notpredefined'} in node 2 of the decision tree includes " + r"['random2notpredefined'] in node 2 of the decision tree includes " "a classification label that was not predefined" ) in caplog.text assert ( - r"{'Random2_NotPredefined'} in node 2 of the decision tree " + r"['Random2_NotPredefined'] in node 2 of the decision tree " "includes a classification tag that was not predefined" ) in caplog.text - assert ( - r"Node 3 includes the 'log_extra_report' parameter. " - "This was removed from the code and will not be used." - ) in caplog.text + assert (r"Node 3 includes the 'log_extra_report' parameter.") in caplog.text assert ( "External regressor dictionary includes fields " - r"that are not used or logged {'extra field'}" + r"that are not used or logged ['extra field']" ) in caplog.text From 1aee3a3f2347932f57985d8296a5bfec2dc5c867 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Tue, 11 Jun 2024 11:33:35 -0400 Subject: [PATCH 74/81] added to test_metrics --- tedana/metrics/collect.py | 4 +- tedana/tests/test_metrics.py | 80 ++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/tedana/metrics/collect.py b/tedana/metrics/collect.py index 284b60131..e4bf12d23 100644 --- a/tedana/metrics/collect.py +++ b/tedana/metrics/collect.py @@ -33,7 +33,7 @@ def generate_metrics( label: str, external_regressors: Union[pd.DataFrame, None] = None, external_regressor_config: Union[Dict, None] = None, - metrics=None, + metrics: Union[List[str], None] = None, ) -> Tuple[pd.DataFrame, Dict]: """Fit TE-dependence and -independence models to components. @@ -102,7 +102,7 @@ def generate_metrics( elif data_cat.shape[1] != len(tes): raise ValueError( f"Second dimension of data_cat ({data_cat.shape[1]}) does not match " - f"number of echoes provided (tes; {len(tes)})" + f"number of echoes provided ({tes}; {len(tes)})" ) elif not (data_cat.shape[2] == data_optcom.shape[1] == mixing.shape[0]): raise ValueError( diff --git a/tedana/tests/test_metrics.py b/tedana/tests/test_metrics.py index 79bc883e7..aa7f5a982 100644 --- a/tedana/tests/test_metrics.py +++ b/tedana/tests/test_metrics.py @@ -26,13 +26,18 @@ def testdata1(): data_optcom = np.mean(data_cat, axis=1) mixing = np.random.random((data_optcom.shape[1], 50)) io_generator = io.OutputGenerator(ref_img) + + # includes adaptive_mask_cut and mixing_cut which are used for ValueError tests + # for when dimensions do not align data_dict = { "data_cat": data_cat, "tes": tes, "data_optcom": data_optcom, "adaptive_mask": adaptive_mask, + "adaptive_mask_cut": np.delete(adaptive_mask, (0), axis=0), "generator": io_generator, "mixing": mixing, + "mixing_cut": np.delete(mixing, (0), axis=0), } return data_dict @@ -66,6 +71,81 @@ def test_smoke_generate_metrics(testdata1): assert isinstance(comptable, pd.DataFrame) +def test_generate_metrics_fails(testdata1): + """Testing error conditions for tedana.metrics.collect.generate_metrics.""" + + metrics = [ + "kappa", + "rho", + ] + + # missing external regressors + external_regress = pd.DataFrame(data={"col1": [1, 2], "col2": [3, 4]}) + with pytest.raises( + ValueError, + match=( + "If external_regressors is defined, then " + "external_regressor_config also needs to be defined." + ), + ): + comptable, _ = collect.generate_metrics( + data_cat=testdata1["data_cat"], + data_optcom=testdata1["data_optcom"], + mixing=testdata1["mixing"], + adaptive_mask=testdata1["adaptive_mask"], + tes=testdata1["tes"], + io_generator=testdata1["generator"], + label="ICA", + external_regressors=external_regress, + metrics=metrics, + ) + + with pytest.raises( + ValueError, + match=(r"First dimensions \(number of samples\) of data_cat"), + ): + comptable, _ = collect.generate_metrics( + data_cat=testdata1["data_cat"], + data_optcom=testdata1["data_optcom"], + mixing=testdata1["mixing"], + adaptive_mask=testdata1["adaptive_mask_cut"], + tes=testdata1["tes"], + io_generator=testdata1["generator"], + label="ICA", + metrics=metrics, + ) + + with pytest.raises( + ValueError, + match=("does not match number of echoes provided"), + ): + comptable, _ = collect.generate_metrics( + data_cat=testdata1["data_cat"], + data_optcom=testdata1["data_optcom"], + mixing=testdata1["mixing"], + adaptive_mask=testdata1["adaptive_mask"], + tes=testdata1["tes"][0:2], + io_generator=testdata1["generator"], + label="ICA", + metrics=metrics, + ) + + with pytest.raises( + ValueError, + match=("Number of volumes in data_cat"), + ): + comptable, _ = collect.generate_metrics( + data_cat=testdata1["data_cat"], + data_optcom=testdata1["data_optcom"], + mixing=testdata1["mixing_cut"], + adaptive_mask=testdata1["adaptive_mask"], + tes=testdata1["tes"], + io_generator=testdata1["generator"], + label="ICA", + metrics=metrics, + ) + + def test_smoke_calculate_weights(): """Smoke test for tedana.metrics.dependence.calculate_weights.""" n_voxels, n_volumes, n_components = 1000, 100, 50 From faed68f31d1aa1190a36fef05426611bd08c962b Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Tue, 11 Jun 2024 17:38:48 -0400 Subject: [PATCH 75/81] Added docs to building_decision_trees.rst --- docs/building_decision_trees.rst | 83 ++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/docs/building_decision_trees.rst b/docs/building_decision_trees.rst index 7b0e2e479..939863046 100644 --- a/docs/building_decision_trees.rst +++ b/docs/building_decision_trees.rst @@ -239,6 +239,89 @@ that is used to check whether results are plausible & can help avoid mistakes. none of the components include the "Likely BOLD" tag, then ICA will be repeated with a different seed and then the selection process will repeat. +External regressor configuration +================================ + +``external_regressor_config`` is an optional field. If this field is specified, then +additional metrics will be calculated that include F, R^2, and p values for the fit +of external regressors to each component time series. Users will need to specify the +external regressors using the ``--external`` option. +This functionality can be used to integrate non-multi-echo desision criteria, +such as correlations to head motion, CSF, or respiration time series. +These added metrics can then be used in decision tree steps just like any other metric. +Two demonstration trees that apply this functionality are in `resources/decision_trees`_. +demo_minimal_external_regressors_single_model.json demonstrates the simplest applications +of external regressors and demo_minimal_external_regressors_motion_task_models.json +highlights some added functionality. Both these trees are based on minimal.json. +While these might be good decision trees to use as is, +they are both called "demo" because they demonstrate what is possible, +but the utility of these specific decision trees have **not** been validated yet. + +``external_regressor_config`` must include the following sub-fields + +- "regress_ID" + A descriptive name for the external regressors that will be logged. + +- "info" + A brief description of the external regressors for info logging + +- "report" + A narrative description of how the external regressors are used + that will be used in report logging. + This should include any citations, which must be included in the + `references BibTeX file`_. + +- "detrend" + "true" or "false" to specify whether to include detrending regressors + when fitting the external regressors to the ICA component time series. + If "true" it will specify the number of detrending time regressors to + include based on the length of the time series. + If "false" it will just include an intercept regressor to remove the mean. + This can also be a integer that defines the number of regressors to include + +- "calc_stats" + The statistical test to use for fitting the external regressors to the + ICA component time series. Currently, the only valid option is "F" for + fitting using a linear model with an F statistic. + +With just the above fields, all regressors in the user provided file will be +fit to the ICA times series data and columns will be added to +``selector.component_table_`` that are called ``Fstat Full Model``, +``pval Full Model``, and ``R2stat Full Model``. The p value might be useful for +defining a statistically significant fit while R2stat might be useful for assessing +whether the regressors defined a meaningful amount of variance. That is, even if +head motion regressors significant correlated to an ICA component, if they only +model 1% of the total variance of that component, it might be detrimental to +reject that comopnent. + +There are several optional fields for ``external_regressor_config`` that require specifying +external regressors by name. + +- "f_stats_partial_models" + In addition to calculated an F statistic across all regressors, it is possible to define + partial F statistics. For example, if field is ``["Motion", "CSF"]``, then there will be + additional metrics called ``Fstat Motion Model``, ``pval Motion Model``, ``R2stat Motion Model``, + ``Fstat CSF Model``, ``pval CSF Model``, and ``R2stat CSF Model``. These fields will say whether + subsets of the external regressors significantly fit the data. This the demo decision tree, + this isn't used to decide whether to accept or reject any components, but it is used to add + informational tags. That is, a component that is rejected due to a fit to external regressors + can be tagged with "Fits motion external regressors". This specific field is a list of strings. + Each string needs to be the name of another field. So, if "Motion" included, there needs to be + another field called "Motion". That field will include a list of column names of + external regressors that should be part of that label. Regular expressions are also allowed + so that ``"Motion": ["^mot_.*$"]`` means that any external regressor column label that begins + with "mot_" is considered part of "Motion". Capitalization is ignored. + +- "task_keep" + This is a list of regressor names or regular expressions that model the task design. + Any regressor defined in "task_keep" will **not** be part of the Full F statistic model. + These will be separate fit to the component time series to generate metrics labelled + ``Fstat Task Model``, ``pval Task Model``, and ``R2stat Task Model``. + These can be used to accept metrics that would otherwise be rejected. For example, + in demo_minimal_external_regressors_motion_task_models.json, if a component fits + the task design and has substantical T2* signal (kappa>kappa elbow), accept the component + even if other metrics show the component also contains noise. + Nodes in the decision tree ========================== From 2d45383342441757d712dd7b541a5d86c474cca0 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Thu, 13 Jun 2024 22:15:46 -0400 Subject: [PATCH 76/81] Added motion task decision tree flow chart --- .gitignore | 7 + ...external_regressors_motion_task_models.pdf | Bin 0 -> 85993 bytes ...external_regressors_motion_task_models.png | Bin 0 -> 456616 bytes ...external_regressors_motion_task_models.tex | 122 ++++++++++++++++++ docs/included_decision_trees.rst | 31 ++++- ...xternal_regressors_motion_task_models.json | 28 ++-- 6 files changed, 170 insertions(+), 18 deletions(-) create mode 100644 docs/_static/decision_tree_demo_minimal_external_regressors_motion_task_models.pdf create mode 100644 docs/_static/decision_tree_demo_minimal_external_regressors_motion_task_models.png create mode 100644 docs/_static/decision_tree_demo_minimal_external_regressors_motion_task_models.tex diff --git a/.gitignore b/.gitignore index a52ae4872..540d22e67 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,13 @@ docs/generated/ .pytest_cache/ .testing_data_cache/ +# For decision tree .tex flow charts do not archive intermediate files +decision_tree*.aux +decision_tree*.fdb_latexmk +decision_tree*.fls +decision_tree*.log +decision_tree*.synctex.gz + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/docs/_static/decision_tree_demo_minimal_external_regressors_motion_task_models.pdf b/docs/_static/decision_tree_demo_minimal_external_regressors_motion_task_models.pdf new file mode 100644 index 0000000000000000000000000000000000000000..35227d87fb4e70b176245ccc0b9307fe44e05f04 GIT binary patch literal 85993 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±Ut|^KQrRyC(U&7j3+^vGH9{ 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

q{k~4kIIeW8lEZV5R(cD`?Rf4@v}WgmscK6- z;jU&Mbs8qQ7bsmJj%k7U_f+A96h}H!k`L7N@%7InXi1`5+fOZNkxSiA6${lf5PItM zv+5qEW|C>?uiUK(E#v3>Iu2O8X@<77o57z9qX)D|E{14swdgPI@`8dOlF29fyZ(KG@uMZ$r$$#G;Jr9j7*> zq3P09XXsojvY;z?veQpLP*Ci6oj`JI_!CQ<{Wr2&7H%v`uZ0l;%1QRx%fNf*M@1A? z&0jSRN@4fExL&hf&j);N2W*1;XeW-UHdk*s=Jn^?f`cmuc6^1?iJQL;9eUFP^Lwvs z%WM5`xjnm^R)&!2vnpE0Zdnd;-1^kJk4w$G%;)82?1WCtyXdt$pkZTGjwvPb3{`SK zt3#|zYT2~mZN%_-BaC4~j`vnk?N)eAOA!d%x@Xu?zA3h<^*Hq5(9mmf)Vl^*twLkN zwXjiwp&?fy785Wzf<$VrCWGnIf_QD$axIH9rPV6;YincW*IsGcgrzXO{l!38->i_i zYgYL|tT)rYA@DH~&YZr3Ta|f?INeIL6h04`j-4N_@-|n-_mVXB3Y%aujrwEiK@u%4 zi9Xfgaw2PJben{0Y(nsEW0bsM3Kh1{ESPHlP0Tp#qutoFWeRhC-=~+31@}{L7l!U8 z1wLyzxxYwl_-yru^Y@2$6MNs>t%+6$w9w*6-8{1Y^;C)|k*t9R`$t4G&3L@((dyXV zZtVtE(d^cK80Sh<-RCXq?}fU;X6^3!lkput%>~A~O~eOYVf{q+>-*zAFIn{c;+e-~ z2&QymYDznyTEu7D28F8@B246#euV1eD^Iuw6Qn8iuB~?6`VpZs3SBI^5O(z|4y~C% zQW+19jc@}j{K38h>LxPFR~zTDEKxz+((=QLd|cC-)w5U?vF)xt>5HWT6YX#^Lc?6gP|vQg>wih9%=rdBL1B67CxEsQjHI71vJoo#nmC0N!j@Rm3+-o zcCyX^rS*#)-v^SC2gBq0!2Ea86RcUwQ>@_VKy9jL8JjNgrPV^S@u~%EqPj+W;YtQ7 z5%x*Pxck%&1sq}eCjZ+6Y*z#yqjOQGx&4ZoQ|8nDY`EUI+JQm+0eH52^Z|4tc*$Ug z9pn9x44mGi>qxz|OkbYnxg~ELOHj$-I!Pi+G5DBv#>xmopK(i`oR+dM+tMoHnX3h~ z?_}(A?Z?-e8dhMPu%Eh97KU}tDQNf391k_yV&l}tU2wF?!#R)0gi?xLJl4KO$uQM^NP%~>%xXZ#j=6(1^x5yc z?rjUn>W#vwr+0J=1NWv6jk=-jks9q^_G-Bo`MA3dooJ_eGP>qGJqFpA-mPorM zH+Nr_L$SLfJ%*@s zUSj2Fwh1#|rp+4Yl;=K0n*v*1k>nj!~ zI>c(+Ey7Y4K0zO|Hn)43Roi_%lVYJyc!y|l2-QU0ckF0=R;il*+#}K?VN$f4WD_x9 zPO4R z><~S?(?($wIcq1-C=L6rtD2{D;2~Mmbq{AMr*C&`KC$WLSRYw0U{dm$gnoH+M_g-G z1|4VbRPu6(){T)_tY=M*!7Mb-=&Wbs==!OqeaS8R+q^^M=(i`JYCsBEhBNOGw8(R~VOGWlCr1PsPMp|kaL_v})@tr6T2@pf@C21Y|Jei5%zH|!tJwx-<- zybor5l{tt-1vHD}$r=cXmRl%oSJN~`wW^(RR}{c*PQ4p%PCZ*?Ld|(vlR=HL7->{w zLT?6CCojQV-x$`UjRo}dr=X*f>f!b6A(XUYE#8w*Cdq!RSSc%$2G7Os5O;bNHm^hZ zR%`)Nc+$PVspShUzvX*EVb2$y=ee$B(oOKod;6klv8avD8`z?xQ8BG${uvqT-l@r4 zj|O|3T9Ug(+xyG~mSwCa;XdWmrr$R%+`umt;>n!w@aB6xPz_BRwiCu zfu$+{{$_g`+JrtFXBps*HK%v$kg9EZa$!2H(Dfd^uqF|u$VLVmy`>wRvmI}99NdGLKr@XC!%4$mU7yKJT02O zLb7!{fV2%F0^`!q&n^dIv@mvKhP=`pyWEjlrq82nrA&~tGE`90zVan!8^%KfHz(2M zqchh%k@-RA%)Lyu?Vi}jm(TE&HeHBX$JIE#NdvF@@;N^vT}Q@;YUc=wg3rJbx@g1; zFSpArMjVQ_$`IF{K5Av#*c$aozIMnQw0ew=X8b{sK{PgD{ZX&MLrCDF3>+Kmsu$okdVB>UAWAtg^K zO=Kj3N?2fEW{&fgStt1;=;~AvW8ae|+A*Q0b(Ngo;$^NzQJR)x7k?Kx(mJo0RPO?A z(gTRj+(=+<4YGhr84sG{q}Arr6iS;*!mJ<>;S$%vKTdSp_@(yVzVRx@u7`K2<{p2h zy!?@?{-8l*f825U(b_R!0tVGktd&qu0lEY^i&8Ro3(n$ggO5z!m!$D3M(hOXKOl<4#oqpx{sQ>~2Kqq*%7kX~Zwnp`= zDzy(^P0Ir}o5$=IWYdg_0NEj{aG+3k>{qW7QX>q~K;x>@o|9PSmjsVkyf4tXy=_&V zelK~)5c-7(r~%0+3pLKWS=myMfeQnOlZ9>B_id#GJ~u07JZM6uo<0MthZg=(#5MjZ zTM8w`T;^?6TdurU%_S~l^5;&|b)9gY2UC-yqAhgDyLNl?=xULtKzDovL0F~Pn@<*x zU=tx=OMCZyegyH!(81W!)sa_?vp&LBK8CC^-n4I1(8(&?DKrqf_qDqYUVPZ)(|z2N zdliwk0SuJj?WT)I9LlG+8>M^gC2CbUm^S3;F#qIEWW4&RNP%S@Y6|~0OcZ-mMR2L{ z7$Ab@!d$s_S*eJ%&r08p?66YsgxP952LS5^Uzj1f2+^eJ@|N1Rj&(?|TNu25iGsIj zwYU*Kc%!nIX7StWfGWt9)jf%%5>67Z9!&s)SlGsVhGyLSjzs z)4}{iq4@;kPAIKo#iW>Eum|Zkj*!cj1eVzNxbu*1eKl_4m_fiLoy?03_&hI^2tQT; z`=L6zNp6~4pR;yDvTVX-tm^oi#!+X_$^MV^d+UIC1n+6F?`^hHw?28j!-~-wN<^=? zJ>S)paVMbjy2q*^zTnikZE(%vS$;!*tz@VZU(K%rDRewgWLY3eWJ@lL_u4qPr8d9I zdoyKl`JmRDiYZ_%ccdI5!M*M{DYqDCS4_SALA46SOqi{q>l|m?3)K^c^j|z_D%JNs zY)*kva`SIv@ACrVAWXr*9?-Nz$mHW!5uwn)DGh$cZ(CjhK5`;B?W3J3LVSI}^&nhT zZnWd5_%i@kI^zX4)Dh|Uw{N~XJ+F$0EHF>UZXcm7a)dfIA zxD~GFMWWx}C$D+#uT2Cb@mpSi;+&8hzEXwWZQld(97`>uqeX$YcE095OEKM-)a_4J zF5(({;L|(3dotu60UNnnsC74S^T1oXgfInKi!m-Ae4O!m{E%pPSLX|80A*DOsjVOB z(-<~^@^2sHeQK$^52pQe`!8#jEZ^6@Q1 zb5rN*BT~z~7wMFOb{V*TD8)QcCaFYED?{+9mP?1GQ@`YC6s?IUdon-75dZpuTJw$p z%A5PxVO`{bXKQR3aNHB&*6%HD!Hyz=)L`KS-BR>UD(#p#5yiEsANmY>2G&Dx%U=_t z+7|xRryNl)yYG?eJjeqE`=ie4lKYRpn8`+7dm1Tr^dw*iYxcT&dDUtt!`{?If*ye} zt6358dMXhyyJnA{&qd!(tqAdYv$J(U`79lxJ#2ca+xzq*fwx3FQDYlNjAOXCJ(nqn zz&C7{^T$8(gkagx{5{W7f?;uh0L+T<@ZJRLa9efCuzz04*Zp3yRR}zGySL|5`&5(_jP!#4@x>O^)&~MF{MQD zMvPHTdrkkO?%m<`x%{760J9ZotWslEGgQ`rW?}$>&c|u!@eG!cdkHzLN?jlj`Wf9O zRFC?iHS<s7_2u)I=|CS%D`oI&8^#SJ>(g5oj7>iPvhJUW{>ADX6%;p0qD8Vs~3( z@Q=Ug4(d&mmzKCUm#k42ff0d%fH*8s+V;z7QN`wsyqMMV4{y5fJYmiu^r=_QEx$ui ztDaA3+*4LD>E03csQcX*zB9u9nPaAjQahx`!#p!@SRkh6(2Uq5^-D5@gv&C-VVc1e zfn=*X<#xP5PRHg$xQWR&)vRXx#M<0z#gGj=r7~P1pLUx2 zaHC?pkDtiW8%wC^MHhi}aTs5kfaJjoinJT;%IRVgs}OcA6ui!yjzV82#g;cpT(}p? z{5pKqb+1o>OK6>(;&PAZzK+IoJGpr-wGy%wz1v6x={lNQ{uvJeB>aFDqqZ{7ss}Dm zv+U#uYkkknplzKXZH80W`U_u#@X;%~BYl zKO{UgR3ewXAklD~1{R~rz*n=j;3_MXh>)k?PG;5enJ{_ojhHJ8Ja=sfIdFSO!g&j- z5HEx^(7$X;(C4UrZdaLA6m@Y8dVAR?;??EMTM`y$hH(b5mg$G8A*X{fH` z6icC%a(3lFd6~pNbb;P8a{Dxa|K4QnP_WalNr)X>-yaB>Kisab|MOGiwKXaM>uw_< z!*n2Ny%n2e0fay}M=mr#B$d#D;g}{O`Z$K$e`iC0J)?&C)38tbJCWA&%~78!xPC^R z9AMwQCSa9d=%tS0(;KXd=}v8;e<^QL=f3N(l68UL`f3xt=}5&h3`$MGE|G2jLp#}e z`j3QR-~EVk4@;zVydQN@b>b(-%_eYH>s1(4jK?r={B%ivs`$hbr|_y@V_c1xXk}A*r&s6h zH$lx>JTiyQenIE4vr2I6h4J7d%t5{@!|~QbV(9{h0hY;5(@2f8;t{iC%(v(j#|D@u z$NL0UZK(=R?_1cZ#Aqu&{HdpY4ztcAL}xS*bc39zAT7#mz|9tQiJI97r>2C;W4PkWVB2FIjn8Cz^+ zGzq)n?cI@L4d}^>^u@=Lm;$Oj0~LC-K0-_Hx;V+N**6jS?Mh;*Kd9PSDK58lSPMCQ zaG?yHqgY-6ePsy;Xe(EunbKAFJ4qZ-0N~wSBa3=Zsv3!{IBhIyi`JQc;PiCM&^*ZO z?=jmZ=EpWpcf6(Kir~@!KoFpvzVM#TI_bicD(rFpDB+gZ&iCl+O6k`dwAxmxE3xzp zMQS{Za>2g)P4!bXivGYp6fnUv3HX7Mr)dyvl#-?rpzZ!X>?!w})N5Kh?f@LEbYa^l z{da0JMprKeu6gbB-sLREzhKHEqJoOqKrfxdDY}|SU2u-=3&!B&d+B-Ei0a=D#H_+9 zw>Z3@cdLz~!KeCX#_Zo3loo5?NW=t&dfvIV>LA1_H1R+8G?AWFk{}^~Rh*b-&Um$FuNX5WvT12gK z3EN&(zR0;3RNwyhL7@_!NW^<&F&ug9PX<55XY65lDJf>o_mIE--;M{}IX}fP_AoY4 z<-babxSc>vo6b>3zM9d1{Za!qsM^8HrQp9`z(hQ1UuF#{z?)e-(M?Ig&kFiu^(64` zf0H6nSR=O;8|0tS_UKFD@a_m@zpH;;KSDHm!>o|AS>X6eCX$1 zX+vmVt~{(*TCG`e0anizY_{mFF2s24uW?;}*`Z-}r>P4ts4lR3ZEr&{4wEw%^ zGC@F0jfD;!2a_Z7>)->JNO%D7&Ki$*VW#!w40-o@j)Tbbo_IrevkwMu28|4$UBJE> zpxV3zk3aB&VOnws5h*38>7O!eFn=Yt?s0>|Z zxUek*;)3J%lx|IY=l-Zj*-#GBPqg{CII0bSjFjR$h!2h$FAxw4uT>h4?yBCw2!9T+ zMzg@|)gsV?hlt7b;`EQX`k%T{9X_XGk6gC!FU%ASdW_zshFI?b(TsnRw4o@u2>7P> zBj{87=hsYCJDFind=WGQ=8@VYRE1dL-7tIet&DqdY>8HdIb&?>9i@Zp7Q~u<|ua8VY{N=N8|PmKz6HY2LfR zSr0Xh>59xTSh;4M!E+eAL*;)Icg0Zq5au=k@JN(|-4E^hb5K_C zfq50qcnfjy<5j*^@rR9_Jz#GVR$%qCfFo{!<<^-UG{zhwo&T-cE)nG=Z(8p)p*s3z zmFv^*nL(A_BGu=m8%WV1UDClXU!^lNv0wh~*D6u@R(Yn864;2_X!E=WBAgl-^om}Z zVE@;994;^tHXciAs!mipcLCZ*cS-bMDqtRv@0(B`#*S+wKWHX_Un?vky7GxF=^P36 zd;8fIMho67h#w0({FLMKJg{RH!CDAt1a{o?pid0S-ff62b}TC}3SG%;e~;yN=x zE$55Zxsj`V{|GPyolL^P<)9lQp2wx3D)Xt*^k;thP01g|6k)6el9e<)jZk*}DN>!F z|IOVf&1U+AgLR|P@~379O%5^7j3wLHN9auH9=wF$s^UMvRdhxJ5_*h5N41PM3bvYi zTq}NFZunLg#2j2M3iPK4f8=V83ATY+MH$dp`%8+DBi+L4Gh+k&E9co|=@wK@j6_BK zcIPqpEeG*d72NU$KxOXvRB;&_bgJuM2z-%7%2Bym$w;^Fmva5~yL8cU_sKKrWGc>L zBbqsZ7o<=wdx46J-GWZWdM5BQQ;FSv*<^H^Th0CMxDQ`%j0?3?0dRmR@RL^s!C#zq z$;)oBN1tcUInQo4&dNUH57ogj0DQ6snBsRyG7_^HqtY%iGKh_=bio(d`0OjxT=@f_ zuhoRUyX@`bhp0~oaz6znz8rr!tq$`PqxX@1Fd9e|Ud#wUXE&ki2_v;$t16jc?=xZB zRx)JILudCqPnk(EEY47Qw6hP(;IhBm|4#X>5_>3max_CRI;TT-_$>_^V1LFqa+`Gj za=+sW=W`lvzQMDNskfJcF0Hy(%7K&SXGHId`}mAANcSKEz@mG~RzG0R;2IqS#i`U9 z&}FRZuPLZ4_hmiv_`1tAkcHDEWGHI5rk8r#Is6eFmQ`qy2W=g+9$SPBJR=- z*~6vg=u30Bgt(AF;J`K2`-|!J$Loc@WT(*g{qCsL+hNgyd#Y09k9!_&Aoa)u(-(ck zw4xprgX0s=W>zP==B=kWQrz^pPy;uAAzyj~yi3s>u=;#Mr(4~t{jEQaFd6)wv}!Xl z5ViXPcG${&E!#ND`fKs;@31aY`SGv%82r^Bsd8J=?pgwZqC{6GNAorOs=ZWxtOH!Ar<_t#3S4z2k%BW=tpoZ|``EYw zZo>AO@H=(kgThlPsPEwI%vu&iW+ zC--Nfe(81MO!J_`nl&FDt{sfWbuWjpHJ3qMASc7CUDGuBbSu)0PQIQ>7o(F=fc&X^BrOWweGAfl ztXx!*mao|iamsLi3HLvlS1^e$shpW#8RXc-YnaBmO64Us7{>cj76G!YTOZNUav}8AIMt&!_561pX%IpS&Iqsuimczkmy?#lzeb+vH>R~YBY^h;E z{b53S{w4Y)jGkM$JEC{Rpq8Uk#VZao8IKqI##mgZi=Xw3^XF!3E=5IX$SlZYOkuOQ z$$U{Rxxj*0sdUkypW#OTxtB5uV4*IL$fI5}sq%5w7u0Vp7^Do0)oKZaSBs@OEov2K zZ*0zsQdlviaCo_C9X$(S)}=>l=O5A`DI1lgwEo$B?3FDSe&+=iu|JBQR5*8Z1WzdD zaC6G}o|%|D#v}{IQ;x>-fwZf{6na!5%5X@y%9FDwNMfh^qOFro-e?NBl*}i9(ardD z<0}*Kht#i@Y07Lp%%&71dUcDJ|4fU~N)yRp&7!U~&NcqnT$>)4i8_Z`pMvH^-0=?r zy}H_>Ss~U(4|qXIS7i%Oq-oG)?tc!6J5-(g?VMJ=F!}zceokY;DxLQN8qF%J8a)S2 z)Hzz$O+6dzk)$<<461`o<({Q^Eex0TjrJDOvfH z5^|wP0SHg@nc=+CrvToT)F&Y^?7#+_?IEiM9goS~9g{9pB=aVka^< z@ml>h^i}lRjQF@&Y}A>oR6kv+#LHzu@iI(;2gv>ZaY>{zLa~eQW6@h(N(O+GHvhTm z#P8-*e!);W+k6VcOGK6-EU|ep0+dqRZ;kSgX`A<>1)y*8OlUQC`* zYLtSiL|C(RF7GTvqoM-GROJfls-BNNa*9<*Se>}a|#+t4c{>aCbJTMZoieAUEZX!SC zE7^}za`^Jp9OX*r97;H-lV#b-GcSI4pL!!as3)j{#=Y2So-&H4=jPIGU4oH__ZVhR zjEh^Le3mj6wNd*R(Bt$PR(>8Yh_l{xKd{pdWZw$GUB9to4H!mvuJe&bPmx%4iu#iBIs|fmEkQ22u>C=UgJAN47u{eDWc3Ys0Q7C|*NMUYrWSMJ{zScY+ zv$22MU#Z2D!z2TqTJYH@%A^ab;1F z!?mw=wF4D`yKIkAp6C)#T-+QTR^6P=TN6$bNW0iTUXbImu)(lt9AqE1WwqMzn7^h+ zEo__AExOy2?}CU!ehnQ1DZzDPI%yJHqv0p|ILsePxBUDdl+HE~E@ zH;ajQT&~zcSX}hw5yiz|*4FSe=e*c{CwKS0|Aj#kS`osy->>N&+s*x!_fK zoc%J}%sk2aNu5={^{qD$y-kp`0GG-2L9k(7oLdYW_p z9oF9ua|p{{&p(grpTYj~-w$>W0{AHr;kkbe5(W$u5qK}xodJX|@~L#7V~#=i#%pEGKaSg68-U1SUW>nA)7#Lz2_j{YB^ z{%4$e@ZLGboTGpK{h$B-pX`Qz4HALf{{ImF6g4_RT*hby>H+;qaXlcv}!wU z|3|FA=5NT$$<_`aMTGbQ1F>+*zA_~z{F|?vtzBV*u(`RxVdZ|5t;nkN07ecs zKvMibzey3Ajo|A9=kSTe_nc|A$m^n`I_^tZ^H8XjUwFN?O!yWwF6*yyG-vK+VgH+Q zLFi}10G0z!cXbN~q9lKa!OX7)4%qWt?TW%f;DSTlah+Ld()h6tmgWe`RQ9BoPU8s5 z?B0Y>S&0x3Y2bFpMcA4n5OxN-<~3tDoafq?4fZbVCSr1w*vAzwmhaFAY$YB|Q$T1oX9RbrU zF?&?YfgcJbOn?^lhHFXlADs;M2W@<)SayLG5y%{KF(4$>vS!^WT6rZOY&r&xi(wg;$o(^UttQ83$((v6ow|s~SQ47T??OC=89b z-~v9oDG3cGhdcWJS?y3P$QvY5`WVcHytx+xpfO7X+-t)Zgd~Cfd@a81Hu|W_n=80i zIJiGp4Dd3ip)$vYmcdza(L$4N_6GW86~h%1v=p$1s=$|1f<8-`$#9ha-JO>&`LVe? z+!BBP;+a|7)g0uvmkH1Wwsg%?ofU^0UxxL@vTgtakJqx`!z)C=n37Z}5s@KM8-n!A z_LZea3upJA16FZbJ9Al1%JoGgPxb5@+8E_6Y!&lTb^ol^e>P{E6l`NR!Kcfm|Lc?p zm9T;QiuQuT2e$t{4B+!tG_1h&`$^SjD*4ajkuhk)`QczG_MP&79Z!UQA`Eahg~Voa z{vQX?zq4olb>2wR5WvATH~luZ<9{B0Hi;_O$^UN(f0yI`l2!0?AUZ%G`}QQzU{=c91iZRfm0@Ps=d@}DAA zr}j^Vm~;j=9L^DjPcHw}p)?6#t92pG9+Bg}zt~W!@kb9N1oQX5*We0cBal!3N7tea zfwf>to|AOOkJq7@s#EtlJ$$!pA3DLQ+1eC@#bf zz>*H_Im1OD7M&nF?O%~bGyyJE>sh7oOp=g5dIk{m3?1u8lxWyyQM70-=>EN zrJS3bhr5lDD~I^+WVDzw@_n`L=2QOL+(3@8+n@rH>4wjh{}Iez@krCGz==^Bsd?+a z%JTOE3sCdbgTxxIrvD#D668W_BDv829BJ6-khKEUi+aTQdR4PNZUV1defh-M@$YT4 ziqv_+*Z9DOE~DVf5r81oZV>EOB*Y-XM>sf#2(;Cj>;3w9I4bcJNx%E0@-PkocojO0 zO)q&v$=~3UBtnft8whpqLe+CrJ%QB8U*jtjZaPv-=@6K=Dg6eV@JUd5J1G^q4C+8? z&pc55?ODe!;*n47jVz6ny-O7Ha&LbQZJ7sF0mC$VcH<*AT+jCHyL~rMz}QL##ID8a z!+`ocki?WK-0!@)YqAE`i5ajL1Q5GJ{LgPklN88L;zM%f067gy>DVI^5E6sVy`7Bl zIz|G+1_4$-I9Gn@Bb+y&dk6p;Z85l>_0LkMUV{|~H!ctkllVeh2^{s#n7h}X3XDFX z-)CQPV`8>hSJC)OXuvVV-y_Fvny?k~n_Gc@#pMRdNTWPzk^Vm+-TDkI=k1~tc z%`}BU7A8^f-AyE&17ztfGhQgUZa)*ooCIyIb0pVulm8}=nnX#gR%EPBDBpl%?lPet zkh#Vf-%c?mM}I=8mzgP5=&=p%g62*}4`=4TVqul7<(4G2dTSAQ(Hq(@GU-vhU?V(D z2#0c>@upWT4<-X2wcuz0O>rn*b)BHXnNw#OeltXiR*>+7Mn8 zSAjA)PuGfeq2>z@kTA1JaFOKBU!xzMy_DYxk|u|gHGU-elCL%0e3wI~5rH~^?)EK6iAka9vta${ zktMSpursxW%u8qLPWHw)6>W!$4d-F+yIgy(USI(g5-hrbG~+mI7$Vd1YWc*i>JtzX z@WSX4m_;Ib_2`a1UyI>Paem6cy7{WzvxSmi1`^V;*G|F0+fqtGs`hsoI_1nm1Gpy} z{-sy&j3W`QC7j8Y7x1q=fw=NiH{UwL^k??V>q5tX5~bYjd?!II?2?c4P&HQUdkMj( zfe$i9BrnXT;<9e6+0Q4MCmBZywRA!kXdRak(?W4QmYJskOAs|(?2a9I;T-zgb?Q<# z92$vk5VKH(Mf?5K3$bk>dd~`6pDm}E@76?ZnCPdN0*MkCMJTGa`j8lxlL)?UZiqBH zK08Q<^pJ(#+e>d_@$}+M_8{6?!+)!Ghc22B1FmPsm8vJnZz-_yi%Ytn5q?zd&x_5N zh9LPZ;O3@v_=nihC0-aAuibQ5=c_514?W&0gPu*tbcMG0cV>gAXm=p5H!+S~rqwlFNhAU>2c$Ebg88Fpk|?8a0Ce%oN^imE}YilMywodte+jvpC=Y4 z40dZK)ewBx%s*%ZM}#?NvSEeI#0xR=z!F}wmir#Jet4E8vv)U=)Bhwb38%@95}^z& zg!rq_X@PcyOV2bM+Dn0skXEWIDor9us`rwaR-xP)3=Se%axbOUzlXvhVuF5}SEwWP zOQ9=<vUAahZkG)m755U?Suk+m+I+m7QwiS_woLi^ktB0lKT6ONu3!*bw z3Gw5_(@&>@L>{&oRf=4{fbk7rnxFS)DWocCB5G;pYli~&Hn%or-8f2EzY?7by}cfj zfzS0DRnhRY(<^yeS&{|9g4(X(#XNcqJhj3)Qr|Znl#|ID;vVpe+}32<4;e#(qSEKn z1Z+`Z!?)P1BHlxeU>0%s+uP}1TLNxzS)r(i)O#;jHi=xP@ASBT*e$wYK*yb%-0m10 zkLMEjr$qNNFngY?efs0gobiN&;K~dU?aHNfO34meT3=TJTZ%+nqAAq-gmfg{PBmMg z154qx+r~V}mByF&_YglNdB9?D+Vff#A#VHbEMrBf>fyOfGs|NbI@KP`jC>*aqQ8S} zy^YS4I83t|?yLsDRJvskLE6rbz}3SCr6_FHnbiGX2g)L9$Z0ut7q_Slm=bFucfKAi ztZav&W=yH3XB#nu2ylDzS094A|b z1$tQhOg`r%I!gvQS0BoZ$CPY9FO`f0b}1A?&!uAK5_=4 zVaz>>T78BrK}hROAu|f0a|iYzi>jecbgEB`-YgbB zOn0_HHDun3Zh1r2b?J5MsBpt$V3Vw?OxD?rc`7fsg%`dz{(JgEBDrCl_V(+^PzgPv zsb+_*pI?KUFd5LWJd`spN@`x7el(0^7<^?;UnRz!vi{8H&+OZTMb6cq?>cQNFj~t@ zOUk9?q#OBYB0BEFZIq|xquDxg)Sz6B@3d(s^95{dQ4Fux z;x%4b4Z%hSI(c`6ojDn>mAD{ZKsQsvnl>RkXdEbd*lCpQs^=|nj5lT%>6c^>^ibKy-#+=5`-G5rxC zfgX48IvG_GS^bq$y|hET3X&75B8JJ#MK}ET*+iogiZCXds<|tU5a7PcX`ZJKIPmWs zuq4CEyU1#_7_uN8mZmB0gEPZTT0?9tu33uL#l4e5D5x5LW=zvmAkOmM?;Ka8RLFtl zZ-A42^*#Rqq1-i7zVRjCK>f%{X?X^fT~#_mb~h(%Uxv%kzo%RY+Q{4>^U^DDbQ&T2nDyURkdpBR>_T3fyZ$0*NmLch@q#28lfW== zSfVN7nowWRqr*KyRycI(Jq(%A&{|iY1%_sF%!G=*!P=CR*yy<&hP8pd;r}cK$J2kJ zCbZLP{27@>t(h1rIyx55KBUrAdWFk z0?KDmb8>xOo^s}y1`o-0*PbA5UcC+Z_mSjA<06k6?dd?07m*a%|yiVYd9)mecn8&yPPR>F=YM zUsX*B89l$gzyBw-tmsNgT?}4;(^0JHFi)_PN0^ zfN(2uD)ab`{$^pZO?#2G;SF4L2DRCsc(NHJfM!OD!ND~g$~Ne`Ni~g@Y=V6`XQj?O z6bmm3b2g%|<0_vBd&DjE$JWbz8u|HLy@d`!-dPSl7^HOgv>iHAi47|aQOfkxMYxFx zJ&PaZxBN6^RGE1Lxx6BG^G!?jJ4H{XKk*30wG!?ri|(qj62;$7CRShirlNHQZ*YQ} zwYsIYLiZs2To}axyMW#Wy{41uH!7I@f_~;9dr&W8j#zAi%%@hykd)JR)kPQ$EY3x#L$avBQnGI~FZ`KA7Ks7<;%uaFcY#LU zPPa+%`M~a(M8=>0+*%Ky`dU7Y@pb-i5jx**Zs18w=)dRdwQl%;Wo`WH07-u6eC)+t<6!CT`l>g|AGxTfLT(h$Q!m z0tRCgytb_)KtYDFJF2C5S4oGUut!F=Eg4`@aibRg=#J~d4}7#JxX=0M?XRZjSs!Ea zq&*FZ>cW3dsUDh43?G(=#+_6eWNa!4a+e%wR$E{|*M(Vj@3k8`mvuV+8v1_i$ouh7 zyDYm@FsM(@jlrM|g-B;nDIy?1{KJ#x@E{DE#-SiWHQLt@g9AWk@UL*rl`h^)TNZQI zZ#joFPFA?MQgk$hLj4CYKG4NbMj=y4ID3ePK3p1SjlxVqIeb9o5UIVfltQmefe0+n(BQ-vuYE-?jRiw|1`fLnMw#XdW{qPKxxiBKNzlInPKgrRK7 zI>$DZ{m74K5CAsEKd?sMCilx^$W~TthoD{k zsO+M#&qRLhIX7Mz1hzWE@QV5ymv;9B5Hz6K0N~Vh{#VA!=&osuUXsZ|o=q$v z)j@unfgCo0k9&OE5FmdJu=qWyTecR_Uz!s61!ZcQEQaMzR1J5TLuHJ%>wo0dLo^A8 zk#yF=DF23=?&4aYoSwXZ^Rsfh2&LB^gepbfG+JP2J!g2hZ8^dAK3PG4l~0z_ ze30m|)@p;8FB)}^1_iOHq-)UpctO>urz380Q;Y4jC<`t8I1#ui1SG-W1(V3BV;ozoQ?w z`k~L6;OarhM7rU(^9{Ad(MN}ns|7MGMFx7QB}?vYFs9fwdc^c%i1R4OZhYD01(+*9 z=wh`pzAYAzb-{V@AEY%-7vO_atL3KnElYl~gyb(46jOZb<*Ubr1WeuEq9jKQDPony@UlT}GSjWPbsKlVWM zcrueJ--ck6a1d@V0^EpX#3WhX54Ykakzc<4unl5BTvi7!K-SEuFJu>Z^bo0pq2%WV zs8S5v$CeDVtyqJO(DhMFfxN)ZAz)Y{X;2ER1QuFWTUl$iwu!}@$ART+ z1)HMYwgH$sE*lV(rcFX&@5>Ps0%f}WAlmqg6Gk1|Md=Kj?qmQ$eo-p^3HYfz+m3!5 z(*O*MLA?zEV>}R$ReC~BJ7^n9Qf6a%`#&&V346u^IlBgFV_?;n-pt~SO;q+UU|WV8 zr4?MI6WU;%?efQ~*E$EiUHz*WegcJ+9rUZ_7c3IT7LXf z&dvltcQbTBmNGT#P_?%t^%InIR!*#F%(OkBo%VSRgWZRyTreEb#p^+TlClU@z7HTX znFw6{di-5qgn6jo^AF}}pIBROXGb3>BeW$Z#yj2z;VyfVu8$9`0}a=)Xh>s3X?}LN z?D!4+z{BtLxH&g_HVCdWb@+q*dvoTCTiI6W^wGvJaY3+WRIt+00mg} z8VO2#0nfHU)I|Q9l?lNzol{pN6MxAvSY5NCJ2uSi2;^%;#t?uX;?%4LZOHfZ;$Ifq`IxP0&-u-`U-w2$Ibbzeu z_O#Jpsqw^I*z(nDGq_!{Zr{OQp> zR{3UZ0ZU1LE}UC%7g)AxV2 zuP+IPFr*vuV6qQBUG6NF|LZxnLh06rhk1&?bHsiq@Ua5A*H*Vm%y$uT;>Y1sWnJ%n z{Q*J14=F6=noql5alA1Glw?*3aZy83{0>&bGLtmR`QYgDS2=^RG(2edRh}NUirp|9 zQxL&0Eqq zj51x}2Ei5-uYLg*2`F)LN>P2v;M+wlgOlppN&RsDMmCY-yUQ_ld7yF?v%dS16hqqT zO`?|<)hfaFPm67510O}#`*w*EenWQf)}wuDBj@cz&kF}o@>yp;$bT5>>TyPcb~*fI znmonrun#KLW;>cNvpFK~Kj5b+GPFt^U2<%k zKX(P6fn^Y@>j~j`^H_v+|DCgTrA6E~a3UX)E+)0Oiu`)`h^4&a4~WM<3nbnKVOuAI z)CT0sJy3<`EJYteO-%aa2a=^3HvK~rY%D=z)+tAIrh#IW4wA1H(_i(fj4q#64H)^JV-cV3`v(NOUgkXQ?%13$;^!xNIEdntN^z?bw?Vw`KJxYNZZd!} zRvhb!pRv6gC|g2=%(tp14bd6i?rGqd`X?UnfNXhN9W!z`5sJ(Scb?vm9p;&&S2Xzkn#@VpEO78wFxKnlt?@VV3nf!Pm7r(l4(U*VnX zliN`m&$sdIM02GYq#vfYsw66mmmedA*gxFXB$7}92llrTjTB;u04HkkM0I|D;|^o? z8lWKDJlBbEL_f%kPq-MBzbi8-6UUY28nAV3@_-M5wJV(-;r%PoS9Ab`aWjzikqya3 z0wDrV3c)#x{spy9aPtjb>-rx+`(UT%f{OWwee;cq-=M_qh#v#;cfxryR-N=ic=dM} zH!Ycfb@{uuLzyq%-7+o&7*DkQ5%-igfrEQAK5kz=N=se{$#+Qfy~GZ2g4-|gbvM}n z#X<*35~z>%-wVG@mH8;PI2IPi`JyG5YXL<5m*s@?6yL4@x>{^MMKz zD8p(44)MR79jgS0;}a!30o;m!e$hb_w>$Dzm~3q0Mo{E;mA+!feG?oD--o#AguRnA zedyTZ#yD(U9sO3Yumn^a;_J3(-H5R!p@bd{_o)MqkEX`WAtsv~L8?MA8&@4;@X}uK z=C?79Dllzdl_M-F?<5Y~&-!aF;+#LRD(}k7s3Tv1L*>00+bA9m<69(W3|O1tTo~4Z zQ41}}UPto7@$E%b!!YT|7%}43leaWUr8~hN_WgasLTwG!i$9MIu`sD^?ai+#Y&m4582lzP|n3+W<~<|@yvL7&+szf-sq1Gc>&W`wQ0A| zy{TVB*3^*;pju_`Pg+19Lk6QARX5^t+K>q6JwxB(3+%QsK^m!d{{b2RQm9sDECyL! zP^(V6H~~K^;qLH4c=^v>YflJ=d9@V2up~KC-7csT=u+74Sd=>0{N1==1gI>dIB(F? z8ThGfo*;+^Boq0vO5 z@_`5Kvrpsvxv>F471aQUXhflAeH{|XmV zaH=zO^w2p|iP)@0;6&#Ya@hoF62eq*0kHGJ9ka~>_f1AXmVNo=QlAC!-=Ovy0N>^7 z+|<>>RQSAu@zwr`uh{gRriyo?zSn@}>|#iVvxq|M?foF1)qhc#aSvBUS1R%r^QlgzYmi+!)Zy20Xro((;W|-_aD`E~EWuh{MaqPXkz67qCQG6MZGMsyU z6C>>wPaj<`*NVXCmT?N$O9!ZKqyoM5j|Y`pGNLq2r8ZK1RrxAZ)Sy@jAQ9OdFaOJ^ zD~h9k9V=h8$WDc@9E7l|dtfqhODEu%O@;00PnVMBP8MoIS8W_zk~14FHVN8^i;MGr!%Hmj(iAb+~|GF=K-$ zt=FDql3~L5UE~C4E+CnpXfU;Q6cS3&%wg12P+{&*@pt|~cJvW&AA_wsxZm6<<^RB! zu)RYeoa%EW?!R>df1v^3PX#NWfBye@UqP+qj`uiH4;jD=%^+G?Ksu6k^}FUH0QBqU z%4fU-wN~O$f6)tFh)AkPmb^n)jvyS884cT$ZviRbH&RX$*5$HyRJ3CS>dzo1+>^pl z4?3K;0D@09r&$fb6A^J>U^HkWLl8?U4kHH=fFE*i!OO#w05;7#Os4HWP`g=b5Z#7+ zwXlC@`CrI|FgcLZo;E4vg#Ux$d>;Z<;az{gM}XY;e{%%>5*Yxd>A-e))a3t6>;EJG z{x^Jdl?ueT_*1zYHSgkhV5ulCktf5K0n1F^O<0+20Dy!L|H+F%DX~yO#swIR*9Z~` z(27EDQlQHGOMXOApN{9A&Sf5L4loCrfa*vV@T2sOe(g#81V9~a;2!Eez)8|(UA_m9 z8S&qC^J&47@_@6=WF=SY-w{N{AOoV42P2i1>QMD*xp8-N>b6mlQ$7H5ngS=z6cAFG z99*c3lTsoB#E0$(Fu`>s;5R|SFLCqKB0mpj{@briNmRgcICBJU+l5Lo!F9oTl1#BS zldAbDPar_(?cls-i)11rlt((hnIimU!102Wf;^Bx-T52uXe|D=4DT&-m`+e z$rMIB2Y$QJs1qSIB5oirB3eOWxsenuB_i~*Cxry8;p1+PbM3E`C5Fj{_L$^f7M_r? z_eLS01`ZKKE+GAVGlSj>zidjRbsErf=v;?41A38&_pCVmFCAu_;!;=+ zSFKnZ#F1zh$&v8Yc9y!dB)yQqj}GM?fZs?rxt*^9z{*o0kZj~;vt*pkm3_pdQ&Vzo z{xjQe@ns$O?_t0&17TrkrQ>A)NeL}j?(%9b5Gas}V~#`RG}7;sg?xv z*0S#4di1J|pzR;JhXK6OfVSvI5MHO-#GX5p>p)rf4)9*x5Hxwq|9T$OReu5C+w~Wk zfamtR6UJ64X-qk9Ae%N!U%h6jUhA6@-Qn(UP;t@2 zeb+$qBw5xGc2@-5IDi18S?l_A*05!kkA$ZDzk*_akG@#Nqsp6&~M$KPme=`K=#9%fPN zI<{^P3~=#mZiP7|_GKj~GMXOk9~l2CPjSiZrl%S>J(_Rayl#b=-8)Y34%8JrwR(U> zZe`;Nv@H*Yp+QH`4JX(>&JhUnv`@faA`wuL84>mH)Ckuh!dU(@h^w&9Sb2h38{9O% zjdfs=#_0kD6I{tFQzvm5j zKWbL@+l|9Dt00v(@J`{`+U8m^*4wY(nqZHqQQtqfkJRr zF_9lwM|h@du!Z)_ZIJtUP=8XF%}iC{CZ1oMJD;-%(i^IlMS_co$iLFpQja{EZ=iDt z!n3BcWkYH_v{GWJ@x zox&%dUlHPU4%GIHFk>ai)*x$1J@#D3ccr8HGgaPMW7irsP$~eXNY)XD*k*J1i>Wo~ zy4YjSTvCtkj}^u8t!8U})?iNC`10DFUTKaMc$LZZ<1Fxo-rMo95F@BLAld>6d^lY?Z*3k z2o{Y_1JAEtDT3?;y+vc`t^OZ1pw?cksc(hk(TR;>C=LTDUQw9IVs<=R414l|i)e?d zj-M(tl`f=NOSRA1DrYN^QVU+jAfL`vP1!+u{;I^VKT1o5EX1Gh^yCWSW6gDk*N3ov z6YGWJwAJxDrf@B=uU6NHs*Tr~?UxE_OB7(L*BJaE$8D;Xp%q4Idg(>TvXJ=T#0Szd zKH&&I`T?&SvKou1Jg~S^Q0w%)z1CoaVIUe8XyWZt*@A|TuthLGt&M7PN`^;1Z@=0_ z1&ba65cp1Qob)X%s>7jB1QbyFfURqUZ!n)YyLXgGv!(3dBCbZXL-qDvbWze%3A1KU zK0sh!tB@taHt4MyQN1BQcO^4|J%;lI)JqQ_v%f3}>`!@?BK)w#S8D;!k%{}es{X!P z>?&II_EdQaz5cZkI4t)tkSa9JJNQe8z&L2J9gsOq`xqfi8pK1y35W$C_J}k@_#@k~ zkxGsaihe01eJ$w{pO_{GY52UQiFkVaW|EDDE0qrb4uba$r@OH4cMvz!f&g4PH;aVp ziTa9ODe<4RvsSM21{$B3nz`E&$#>S_JjNMk0;3zyDhDJ5n)M$FgAia7&&+U67e9Wp zoFXmw$7yHhyqv@f;k`_v`VkSKqV$g!^VI`peTh^hO$HIny|SbGFM{|$<$}3M#6C_G z-sk1}rNRAPg+v(PVVXP1;S+c2-vu1E>1KU|kpA~Vrlv%Af-=GE8|rTv7q@ud-?%nq ziF6qOYro{30XywBjW4p35Up8!3`o@^aV){gF`t-x0hL}M_e(e^_ObauQR;URS{OfV z%{MAz-d2kjCy3eTSeKv#g5%uqpm)SbJcD&Bq|!{q!Px>?^m~MA0Jr$oQ(tCW9dsOj z@}1O@BTZDxh!#A9zS4LMYGj`_MV*(47DwXU8A8pfeVae$4$Zicsz=%au(T=qpP%e5 zu#@Nx`Wkm)U|<@Ytd(_yS&ln(?Z5D@qx5M(S^860`BshB5>sJbwW`SWQ%MT22H$29 zLPWxfjZ#8ad!O6|N!$KtpSsY3b*4mgmu?e9x?AN;WH)Z+Out8pCl1yjvU59d>5~;e zKxl=EOIz|SvXm-c2PgyUi#~Es7zV_IHy}u3U*o*D*NXrUJrYo@eRb zTvNg1oshNtqWO%5kP^(O?T1ES^*i2N*)qf$a0;C8QNOU;xXL1P&neryQC)wKHLOaT zQt}&|&6Q5C+M|PN%__4qZs?|G>JqDc2s=)4PPxg839;P|M3{?4`x6hngjLU76|VY4os z{9-G+(l3}@xK!vliEJ5fg3{7aDOu9AD1jmmVr(A%CTMDCR;HGexX5!LyMF#B2iY86byx@+yy$|uh1`4u&)p~utTh&@f`)+c_otA7 z3ww@qLkUO1f=uccC+`%SMgt_{1(J@@nv-$|=3iY5PVyhVUX>g8<6C|h`ZzHc z8vfAMAq8LJ9bV*xPI~A^SQxiuy&1;m(^!tJKy5aVnw2ece>Ibr5j0hdo5_-?Zdd-~ z&irOWtV8;k@Jt)D1@+<$P&g>Nv-YcyDE`;aJ8EHrHTp>SU2A{0EA%I&Gidlf?;}bs zLRDc|@iZhPJT_vIR`k#(?|x2kGfUpH%Z~#aTNtVIHG=DJfRWY!W)D#jMS*%T;hciH z1I>u-DbjUohUX1&c z>X;`z7}Q{SXA?BHUU*2n?^ihtz5rx9C9q2BTdpB6ty1wDf$mh^@)%ri7p6B2&OYuT zz;-vd9~fqEkM1SE&2&YO>yz_tUyp-sh68YA=(&{5lq@c#cOn>ZFcWY5-MmuJ4*}{v zKsyh}EV==8!YZH`O#u0$OWO#6kxD>SCVsFnXu}+^#-@Nf%S)9BN<>K+U!nc7bJ!Y} z1sAwPc#+dQkU*lP6ftPGLA*2PC=vM`0GO#)ungiGiL2}pv=u02RUQAipWdADU^(&1 zU9R=y7MvwM6kr*=TNs252+r*LaH4@J$b{HyK>#1m56ry=vmNv+Ub`4v3!(*3{)7f; zC7Iuo10};1=|=5=VVPG}tDQz7MP#&v5am3Uv4T!)C7Tgq*ryZ# z5j`WaA0*Ylu1OK5zH?HL1bIN1x}>5bK32Rr@>M#S%bfPzY|Zc&X`&>60|04^7dU0X7MQw)SX-CCN@53; zKu?WWwJP!;$H?t;`_noq*xLFyLqe30VFP{;U+V*AGG=r{JfKgi=rrAn}c92 z|4e{g{7xA?N48LXT?;s+R^VbiPLsl>Z_X9pT1vkulqQ61|A_klp~piggqrSry8Q8C zJ)4VbaK%iLB?)7@TJ<~Kkqs~#Gniz2#zho@Zj(9%8l~BV0FlnaA@&hu)~!kOe|%W! zz&Ips=3pa;pl=RlO3@nXYXlL5DW$J+*6Tybk7jMZ=$-B^R9}N_lRE%i`UKIbUh?x9 z5sZ=6X=P|_^*I#)41caQ2DOo(B{up~`Ws+s!T6ClenN)`&@af8-ZRxUDFHiD%DG7o z>JUh?rC*i#5+H{f`EYnGk^p|!>jReJ218}^=>GjB^+KsC5FeUNXUL9$T$bF+z^PFZ zT;^T?y0ju8oV&l|?Rzn_1CrK8gJ0u`Q|@P%c#6;GNX1M8PsgO~UR@mUoy}Z9TGJYY zC7vZv!W9M-t$w{R$-RM!BbW)7e#U(OE~y#vC_p6=6_Hoo5Zf773IQi)n?z_{BcT3;h>1mgV`%Im zQohhQcOe`JcOnVetPu#hi1&ICx_?6FhZhw>b$)!|jO6Dk-L=uNnc)ge`;}mVKIhSB ze2X(!Teu}avlL$Qo%q(hUkjd#lSBD*=Z`E-Jr%R{+1t>t|J3-!_T6>Mv3wy{E;+s` z6$7Zjhylp>S08p}nSk|y>;9{%o{l;~iv-2ee=+^n!%U=iHbiVX9BfWq0m!HPHMN?k zMGle{nx$-F&JJJ52r_-1K*Uu`zr9n)n=Btbh#s2&E?+=Tfsgp87A?%%=1bs(`IvWL zcz8EjhoQ9xS(--{`6zlqYnO(>fKJjY^)35tp+PAy(hQ)M76+e5NCK1E7@1Wuj$WG0 z<*SK=`RIkaRN+RU%G)J!Wf-C_tGaaX_FVl$II^V=B~l3@`!G;AvJPUaVa#d#F2ly& znTnPn*AJF4w%(UC-AA>IR4A_7>jp8Bu*}xBrlxxC(|zg34C^|&_y0`TB9(gI@aC3e z6uO2VYfiM1>zY5X^owjoBhzVH@0GM5dn&P&P%g+@?|)Tvgm*ql@MP4jwm>i3-PRFP z@&2Y_C%zK8Cpe}6DJBDw2_@-+yJOz#;S#bmfhYlTT%4_-)4@ZUqFkAD{GRvvYp7K; zDe_^8m*9Kp=o(86qwPRKW2=y+nyW~d0hGzZazJUrS98+-VW?AVQ>&cSu z$i2cH08tJcBW)RCjbi<|mvs)C(YzmjC$Ri69!#I(+Lehdu*|4^5cO*lxge>IEjg3* zZ7<&Sdq+$P6F49S<3wb|-u?ufYRVFr=rtN!Nk{LULe1BJq_NYqx z-jhUG?WO3AL;$=n7qrMkfl}#o@UgBZ#VfV5i-^kPWLHH*>3#hq@0t3R|6meWN@)w# za$3FepX}=E9Ca%dI#`AKEyygBM`of z=1K*zn)_Nx)Jw*rlMn1?Vj%hnWRyh2-p}PQMA-=hfg1cUv_LC*b7Nj&fMhC{zLTWc>F~he^((w;V>I!#f z1I{ttp%LBcCRne$^I*B=mTm&IqCTpNRSWm7OOQxkp(IHfBE>1w`xKpMf2ZR(kba|m z|LbNfb|Pb&7v8+Uu%5dgUPe}TQY46NP-3s{qSPqxe`OxL;x{p&1gDj6)s|utuWp(dalfYZwyguzJqk2s)(n#Y& zc~Rkc>!A%mezKw>oq-<%8S^a5t>Tzf%QiiXc`7-QA~yo_zK(O@nPX*5E2@bl%HESn z>~eyIg-34HnfA@UGiJc4T+uN75s1Sxht-q&lGf_?z}xAJCr17PA!tgjGlB3tSH~#_ zcIFhcOoeNeYKfGcT9c7Q0(##U;el*#=~#k|fJj;e#^+r+f!E-$4VHY5c3)Lv`-gKE z*aH*`+_nlVDYB=C<^DZ1V9~vB&L}$Y^tGbeAp6ielgjJa#=y8rEfRud(E!;L`$(=_ z=>sa;FR4Ns-fOGi-1CujNJB9y>EHpWP`o+$)A%wru}hdVffB%UwZ(~5yu`SD$J5rn z4m^$R#RrJot+`Ll1|Ei}flbvq|9)#DZ~CQvIH=4^c^k~UK095dO3bQC;c=GLBB1mt zC#B!NG|ATZaP6Ci7*Bwjr>=lu(-LN2DF+ZxWO@)rZ%I`Ths1B9*9=7}eF)%yUPWw|lTYR-TrjpA?`eBeAu}B3u z8B`_kc*vLFmuex)M!U8g%`MOI*f%ks%CDR%=!$VaS*ydj#=4YF6L3{?h|zwzh!)Je zJNPj5Z8#~fA&H>dS;^^(7DAw4B^PmELx#t&J&joCdxoF9>P6#>7Cz0r_rq{?Y>Y7; zNS%V^*V=FNfH2@rVBiKhz1J=`&z_htMoECH9*_^25=^XT{lB4awLC^sM?O^5Zj%{B$VB^Nagzl{Q2;9A#yfS~%?d6{3mjApV znt(8&A(rs)5PC(J6?@M=ueGFRotcn6K^7Rs(9w?!=G7}v91{eKC?ipwrByWlVJc$9 z<^TIQ(rXKJN5`x^^={rAk>&|i#}WS53zpCeD?fDpeW4U+5pN|OO}bcOGz= z#QFny!PH2$Y#|$Jt7zWKttpe-1Lx75Q5S^=T!&PwU8qT~3Dh&Nw)f@iOrjQ}py>oZ z)WB~cc=Ble&WS0;+v8R=LCpN`FAPBL;6`h~zkO(8&S%BOS0_Xzhh`M~Q1XLO?#Vd& zLH}xyYnc#>L-Aeiq5l4#iSmV7NPZv$sY0j=A$esj3jaNb05D4OvPEKO&YaV{nuD0p zvD@Rsd?fz*d$=$vVc@DDL63jnB6%)KFLFNYC%}n>eOb1=(fgytlnN<9r-oQ*j80kCzCFzU1 z&LSu0l_C7sjKkER$9#vR&;CBn&npxD@5kcMvz znfUL=^w49j8B&(Nk5fR~%Kz0hLg+E)2r0wg#|5D6|9*TQdd$&F3g#V5Gjcs>`@bJM zfXCi~ZKM=;j}=t>#O3V%vn>Bd2gJh^RCpiA+pVRd7ym2Dn2?G{k`DI;vlfr4pdu!S zh_4=XxTO1cjcy_Dcj2&e9&Cm)7-BH<1ijC454T*O*qt0MdV(dsvKg2(092Ror}QD%nP3Sp`gv%QBOo8*7k8>rP6tLL#2~D=Hieo4SgQ5LK`IJTgPLGO>1(~y;Oj`f;OkEG z(aSwAQm4@FzV3Aic2#W#lO@>5Fb>ei`80)KmfZtW%fH)p2Z;O{c{m(wDv--kP-!3d zp`_D}=~VUl?(5k0;OkG!I-|kch;m-p%pVvMmzh7Dmpk4qhxacW(D%Qy3&RCpZ+h|* zU%lxG);7xDv5kV|$Ts~!3V!{ixL0;hHGS%GPxbnz#yc&5Yb)o~+QUoqh7NR23Ho*3 zeemnN?Aru)zuxi+w2q~@2Nr{}3v}-Mgvbm0`bIz_6rk|dhr$Ef3kcyEVCJwh^9~}| zk=o-t`|w<)pw54n^lD{(XqILKmL4Vs`i_s8aVu=McBuRV6PnupwTD?j>x8JceADf! z1>^5Qe)je^zpM1$Ifw*@5?d?M?R5CiE{^GIh1Hz@a*xy3Ac4CMWrE2h z;8O1Q2A_WpgzJ6IQuD@#UV`?xTRCdiyY)g80i6=2k8T56)iw<**B1PWilIGDs1Nt@ zPL+DggBSKT5^2DK*;0b#MxgPMV=6*=%gKum&H0`(m=g~^yKNpYf+H{~Vi`$7ahWv- zU#AT9AWccR2LsWq`PBEKU=HM<4d#vOmB3hpWUItWd-nxA=nI^R+w|ZIebAbxrU}IZ znd;Ou%7DJmatmbU{Fd}RGWbFinAl)sIfRAm8nngkwIN6ow@5%Q{Q6O%4PJ-o(A5U~5ui?;-3u`A0zi^{_yk^X zM`p~Q?c~I^0B!hcu0FhL;}mM+hO{&QwDA*61ObM|%hGLVch-0ext{AuV|TjT3y~H-Hq`DFzo%rn_OL zL0W)c6bSGuea*+9xed?Q^7k$8rm28(p782K1EvRW8onU?&q%mZO4I zIK>0vAvB^7-=8~~qjkBk!dL(P{2Wr}2&gqcuf(Nzm+^u32Els={44qv-pkC1rYT0y z$PovjfstyDgF!4Jg2^TK>S6X<4%Yh%I`EbNCdF|cvL-lqTEM}c-{8;3_y|nxzsHqi zi!~7FS^sDP&@Ja^AcG8>W$!`|{^un0{sqMW*`BOJij5B_*3-}jk{d_G?AIzu89 z_ynl5ho6mJf`8aRJMX?p6;3=tyQ&8D)!od}ViSNj@?iUdK*r!WK!muDtTa#+boMRp ztOZ(ZG9ZLMv)UV=WdM45jeFFJ5oi$gsFtP2P!p8i3ZR!FON$S{zx1GufL`7rsDTVl z*#FoyqT*nyaPl>P7~`TNn1(kJ1NU_)!45~FHN7*1DIo>1s)vA?f$SCp#>w7){}T6q zb#oEuh@PnXkcVv;h9IU2&oXNR!e$4At?y0)0b1;F z&Dn6bp})(57iu;#J~Mc00PRe$rQCw~!wXEsKbfU4Xf=_agPB0c(8r8Gc1wafF^G9d zNPrkvV7yuOdaLTsD!>IOnryiL9W(;e<9N|6*I)0Zj1!GZgh93#HFxEl;w=3`pkr_GpO_kl|$@ z!=;RO9Rpji03k^d^AqxwH|Q9of*u0xqW*-g^Zi{L51}@U$a&Fl_dwE1RZvK5JM$4t zL8lgxx&T@tL?Iw)YhUp?-^F+dfknR4K@@Qa?PH&Dja{GtLC2!n4X@2MH|iM%5TxcL z<`1J`U=e3wF{*cVS_U}%4q}1~pGmb)K;2w{mk#bwgGFSEh6g}f?CKNP4S$uohXgo6 zc5_3?P7n}-S+Imn;-GFWeg@rib1V^lBXxKfIf# z{3=ggz42my%iywBDDCQaBg3$K>gro>)Co{2O8gCXCkEoHMbC>2^~I}Q0*;YZ??|s( zTwtbh+w?q7;xR3`@;LM#wV9Xu_z$^dz~%9Q{Mt!jic-Cl^d+}VI_rgVG+d{QvWGt7 zBXhV)!G!31{tTne{5@AEsb;x;x|gk@(R7`?@2|QB4*$Fp?-oW|Y-e;HLek>~? zL^})3>~VViZ0hWKu|l_JuRf$VQ0?RKo7H{XyyDg5w5d}m8WSEi`S_Elepe~O)PqZ> zDE&s2UOtr)0QKS0CW-$d0z6^-OG3IP@<5>7(!7d;nD;D(vKvyVG=}oR-4V(3q=@$ocEeY{K9ag#?wX%f_UJu_2%e{CNk9zOxj8rIY|N_1Z8RKA-DU+yV34Ntq%Y7?*_f}y{b z95xD~9=__Y1EUB5`dah`i}3JQUfjsp1xke%(VYG0;nfWvZd6jAAE?aMh^x@aq>8>) z&!Y{D=F6In$n& zU2#}D8AjG1d!rJijw*rWcuXtZYw7dfj;(Wt)#wiBN7ckeo_7Z#&@O64<%V)VE5TrA z`FZcR+2Fzkc(X_W@@ec457DIdo6829OVbgx@3*X~10-^(v`IN11+DY6XP&u(8-sPQ z81f@gSEQ~*_`3C0fBLERN1x}Yr*!-_Jd@5l5jg%n#lO}EQ0s51)Pld6Ji*3oSEcNu z?!42LOQ=Hx9Q=51fJzIzkElne>Un!zUGyyYYLL`Nt0jMp^*h0?-D8`=W4ri`tErI# zfNdc`k14-Y)SYRxj_FncP&(If5WwjwJExFNbJ9+-{PoJePxH^I-TT$A*XxH%pM;Vt zRkPmx4(OYD|GF+TsqIbpiCGQ9Kr+X04GSTYs#>z+FRI!DqTmk#iUen%WF#BE2B)8( z=S`ihoKgTzM!5BSz3T*g;iI6gKWt;>N%Pmi$JH%r*3&(s-7!ZmjzYht{F8jau3Jaj zo4_6;n=;-@hw&TOSY+oL6xu_f8TT+SkpLVl@ka=e9(2b7Q$G8;#n?w}0DpQ&`ss8@ zQT*n|QX82Wl%6rn@L~^U<~M!?b31G|>-e$*ingMo*9v?;ja7&fpPzTSznA``McLAZ zzWH?e&`PWPwfNB{>)_XZ%5J4xPRle4E@%1Is(BU%lT-gbPA2|vH4(=%O7=mn4ja)wMQFV`SQt@bZ!56K$>@Pi1O|9*r=#=k5(;9!IpDM~xie7{#vliB!#GNa5N{u9&(3TzTy9T?;mY#gFRCWxu+=F%xnw9o zR5e-BZp|MxiObxFRkKWzS%s02?1fsa%C~4qALRX6g{MG?FVNT?u3PiEo|IY5H|WiO zv@w5@WA|AYZp9BVDND#7R)<}je&ydU2dOh007_gsayybcoN?|#Y zoR5Nj9oqGqNO9yvvo`zVEtS}D=E>ydgru;TwGTE^jCpNizQ?N0lzk`f$t!cVV_sRj{Ipo(aU6^vXy`s#zQ5AD zP4FnxtL1|AXxH1H_10pFp+f|(3JaU8m#8mVVr~ZQT@<#+!p&KVzo!i=&ELf0r*&^wAoBCmG= zWvbGWP)zk`zf=?fTQZ3=NG4-m7esv@gBocxFP!rYY!CskpP#hE4dg}wK_mp@x%@SM z>F%<)SNq=u737o?y$Bz8P8!YLP}aZ$>|-B2b4qeM?L|36h~QQ%$>!f*jVW{8qQ4xZ zINd>GRj;pl=P(^x9x{Hx99R?X?*A}~>*Kf~&Gupyp{h@px8r>z%Z;E%!}h-!t21Bn$02JQXK{-x9S0nVeH&F zyCU3z`t@;)B5lIL3jvp$nqb~J5RfhSViETb#XR41{77PTPc-eKEAn!?t5!IfHrwH` z*u+|If_w3&mqRpY1(b=#m{G{HVl4?`1RXH+ju>WV{r(^+KF2F{Wmc&J{H$Q1LT?l} z0^x>S8rP{l8K2{F-t~j5k;{lQ`l%c3OZ=M>As1wsj|hR%{WE6g6$zTX$A;WK={lz;z0 z3rTUxvm#>jH?5+I=&eS4@{*`yj-0vO$ptw~O8)ssmdlYX2u5yN&z1$;4-(zeIm}9e zD@Qqwo8lRZ40&jtn##mD%`-i=TM;BEN7D#KMhXozt zS<$32ZlEes6I(t4gHil;2j|jjE@3l{r)HkrI!(DL$KtVN8z0+eeN@q1W!kw6+{dC` zJqQhXq%#Ni4GY%(5OkJBvE$Qx&45tI8Qz)Gt(>f|6%<4NG~rp%%`mci$$_vlWzS+V zdjbiSscyv1{_0N}PWx8axy2?mlV(v_pLV6@`_+LhCGI4L^ho`+5c5_Y-TJbZl-to? zUy~J)z;taJKdc?l^efi{3%DUJ2O~b#2?%wJ16kdu_{Xj_ZvuIxC|gkRS>mv=tM~j-Q-2jmVz!yPV_9k0d;i;;Tir`* zjs0aIB0gkxA1fUt^@c=R*b8LOJjG=Kmu=68ZX#{Wu0+j@TGsp_dQm271phRKS$oGK zG(slt?K00@Ir|C^gEaN#aH_49J$K=*t4|BNrSDQ`W@%h4bA%VC`#0cyFVtXNQHJVg3Hm zrgAR`$%oB+v^u4moc`L*JZYM`Lg)P!caKQX&w%^w&o|@wbf(k4ULL&8qCSYd@6ht- z&k{)_(&n}eL*LH}N7Xhrnz`Wso1X$1krY4AN~qXGzG+`d>M9m=b*}h#r?Njw?(&?} zc=+-WWl?Y(o6YYQ4>jBRPiq}<6{cd6&mDYGBjkK8DO+P{Qu&eIYHaSLy%<*hOiazs z*BV6g-5Ptmz69qoecZj8RsCgwn@XoHNup6lO;(xSghJz7o}XL!X`-^UqRR22AY7_Z zLMFP2EV<_-Y+<^sil%kRtcMKM-bC}k?T{BHOaw8?_{;tNncnKU+;_8?Zw+^C%^aoy`%amX1()%ws9A> z$1cZJJwJf6qh3YxIa<`Z9GZrnEY^Bl*U?H7UbK5xGk`@osmtB>4qvkyIcmP0UIiv@ zw7eiOEdVu)W|DZq4k)qF53a6Hg`yc1-2mxz)X(3)w~C)7EO@*qKE7shJsnUkqKcnB z+$ip8a4B%0_&glVG;;WUV}Rmd+H$x1c&l1QNG{_I6YyOeDyGXgJuf!3HYaTAW^4BA zRf^vz<;r24<{m#;#20*(ewEbmFM(&)VB?+aIY7-=cI~k1cSE(%73OY1{n!F`VY= zu!U}(Msr*8fs4IU-w>h94UZ=b#?H@baXfnny34&!XH!Xe)fsEoz@gi;ahLLz@rZ zH_*j65%39_>?JYT`Nb|I6|UoAvVMj`FC9E;ZoFSz|7Grl4RJ4)NGgrKNCBIBD95YZ zHFUFHpWD`;w&_pD`~m@Pw6Jh}I%Kaix1Uy0sa&-=l5yhUS8l==;i;rw)^e3&wB9SU z1uxWijKR`7$z63yatj>VElx7g)gBo{V84b9&P$zEYBlNSP936J=D+(*E{{zdNDKRZ z5L(1irj#CQm(v?S^T(*|c$rt}k>a?0*K-zaUkT)(M7TE-c59PI`3EJv2|IxV4n^Pd z&-O;$4a8+Co}3Md{mA!tJujZ=QY5taR(00#h-&>1@1(%E5I)VTGZ1|rJUZA*q>V*_ z^UpJ(Z6n9+J^AQr1;z4sX59`V*%}vkvMroDwK4aWC+Y!z6v4iPb*X6aP5taevX?4WKJL zT52eeNya}o_z}msGw{hXVPn9Yb`UA7?;anrYIpRxW8+4f(}a5Q{3tiOUUwu_`SGOQ z8_#Qk$})i~r{AwLZZ5W(95xT40DMXOWIKt4_-XEw_K&CO%*wf}s)$PBybez%^h?c$ zjlJO7)!KmJ{EKT2FBTe}YJKAFO6TC~ED_lp)Ba};9&-%vA)XE{w=;ggge)Iso5GP* za5Li}a$7jF+7+cNKKhatK>w8QV^|R9*Duaj?{bBCRV!`1EnfH;nV{3_@K6xN>tO+xFv2GyWQy zFXKZ?>2kLAZT_v3iA3>v8C=0saDLlApGs`2#$h;!A+gLXWP%Tkci+r&4NC4V;eGk| zDS++MNs58o^ms9|UvOM>a$WjAhMjnusc6iBGsYx=NQRs7bzIcU%l1LIDb-hsl|k;s za#t52IfJq7@8Zz3?{CYI3$PM%nsF1Bmz=tv+gpXOXe6-d)KEfIVbrKdd7udC{uHPU zpmo}ry;z)ieLCc_5LlGT>o{2YI_@azT~l{Cz{P!2{OoTvQzffgvC|E(jgL15Gt`Ni zA_J}glX4G0*+T^gKhy8)VXLWqEjKIvG9LZt{jVXzm%p}3Hc$SzGUp{1VC!O^#P?=U zQBxL?`i%kg$b9dC2b{V(7Ze@7bkzkaCMt@CsOz%^9NtrAQt<7PX3f67MC7_R!A1p# zOJlmqU0d*KZ;6Mf=Jayc2^3%@cVO5}l`Sb{iwf%Sm>;at4Sxmq+RWBADNEz6XX}3# zKR6n(Q>hpnB0|i1oTAvDFc$8p({QG3!mp!8qVjXs(sirs`uY1+H>0V1z2?+ViiLI$ zfhsvhxM9GRQRxV)HVCnRK;aErehcA_spk6ztLoQ_q)6SWihg`03yQ|sXfBj_4OIC< zggOLN`H_@_OX3=*jpH2`t1>E7dLWWAHutWu^kngAYZi@4!&+;gWz?4-boIorI03r+ zAs(a;!YWjBKNWrvmm4cf>vJZE67;n2#c`Vz!Ndp-yvPm~U6*W_swQ;4$8pIfb6E)d zG8=*lM#8a|83B2+h3rGx61nf3DAi3^LT)uq8lX&YjE=1ku2ZXoNg>Dzv4(=H9jCVP z)C!YJHKy2+f1#4^?H@8SZCBmPW06m1Ewh>m1mT^D!iI623B%-BMG2W{xk$?Nxkk*b zuW*N*SrZyVN__x#vDC$M1Qm}_!9(}Ow?VC9sKP1#ewO8g(-B=;NSW=@vks+SK|C5? zcR@b;;F{MEORA*+8R?~p3qfMUv+)vD1{$3UCkv{K{xr7FmyR)rE2e=2@w1)vgFDv} zlvV8nb&92iVl0B^=U zM!N;$q?cT2s^LnIlw!0wOhw4A(XM}e;9xOZ6K!tT5z5X#hre~Ih=fJvZ*L)#~FS`sbDN=?Fu(RKHWQKf38sVN*$(1P%5K;^RGC3&34;v@2n)sGj$4#id0`Ktehc5Ksx}25E_`K9u@B{d}u%{+S24)-jSwmj%J3}n%6%(+)p&{G;d z+Vd6bq3JoEQd`KAj6ruin1ACk<;_37iswEX%Db=;6WXGDG_k|)visYpTjd&&dv?@y zPwTJ_sq(vj3#5z7$~0iBH3vUK6^h_HpNxI3U`tYy`1%yqMBQe1G!pQH%Eoy3rsX!R8Xxk{|4$!DQ6{h7yYml&J7reL znA_}704%^-4p_w`dw(p{s;A@&NBU&GCLH8HL*V!D6{ zX;Wf++l&3E{9S|_8YHJuZ1C7+&YG6BJ!4M&vviWBNg$Ux?!9W+B6Sq0pE>oChDc}S zIn1mxVI4Pt@kIEN8{ zcyRKOw`9@{WG+YDc0xdPwEUqwKw`-$h7==9$+?8|VCbTbe-7T+B!dKjsvmyD)5%~U zk{z`}gRlMemJM)B(7fO3cRW(w1S(RThrxP2=Sh`}lxg~GbYfWEyZN#trUGr@jI-^2 zPS;Ic;0?K_S;I?Dt!FGt*@d&m3yVFi2t5DuRx0<%C7DhpB<+=lIfcmf#mdoMs|3Q7 zQH|w?A#J1g>Cm%ljz0X7Mp3j=+a;2;!4*HhZeLOn6p^FP*vnA3T3CO@O5{3(d2foz z%2%@%M)euBe)=0jzAU?dIzqp*3hycsVx^&i9*5eRMD|9d>}*Ku){uAk0hGr-Z5
mO3~kGju1FHPymY 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%0DuU^Arhe)3#<9sB^32n2i+Q=UvJdU@%N>8 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%0AQ9YRv835-a5AES5^vE)`Cx-Dd90P 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 D_Wh!6K_z6K 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%0D@ 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 VA$P8x)rkQJJYD@<);T3K0RWxbXodg) literal 0 HcmV?d00001 diff --git a/docs/_static/decision_tree_demo_minimal_external_regressors_motion_task_models.tex b/docs/_static/decision_tree_demo_minimal_external_regressors_motion_task_models.tex new file mode 100644 index 000000000..364366df2 --- /dev/null +++ b/docs/_static/decision_tree_demo_minimal_external_regressors_motion_task_models.tex @@ -0,0 +1,122 @@ +\documentclass[border=2pt]{standalone} +\usepackage[utf8]{inputenc} % Required for inserting images +\usepackage{tikz} +\usepackage{helvet} +\usetikzlibrary{shapes.geometric, arrows} +\pagecolor{white} + +%-------------------------defining colorblind friendly colors +% Using pale color scheme in Figure 6 +% by Paul Tol https://personal.sron.nl/~pault/ +\definecolor{cbblue}{HTML}{BBCCEE} +\definecolor{cbcyan}{HTML}{CCEEFF} +\definecolor{cbgreen}{HTML}{CCDDAA} +\definecolor{cbyellow}{HTML}{EEEEBB} +\definecolor{cbred}{HTML}{FFCCCC} +\definecolor{cbgrey}{HTML}{DDDDDD} + +% -------------------------defining nodes +\tikzstyle{input} = [trapezium, trapezium left angle =80, trapezium right angle = 100, +minimum width= 3cm, minimum height=0.5cm, text centered, draw=black, fill=cbblue] +\tikzstyle{process} = [rectangle, minimum width = 3cm, minimum height = 1cm, +text centered, , text width=4cm,draw=black, fill=cbgrey] +\tikzstyle{decision} = [diamond, minimum width = 3cm, minimum height = 1cm, +text centered, , text width=3cm, draw=black, fill=cbcyan] +\tikzstyle{changeclass} = [rectangle, rounded corners, minimum width=3cm, minimum height=1cm, +text centered, draw = black, fill=cbyellow] +\tikzstyle{reject} = [trapezium, trapezium left angle =80, trapezium right angle = 100, +minimum width= 1cm, minimum height=0.5cm, text centered, draw=black, fill=cbred] +\tikzstyle{accept} = [trapezium, trapezium left angle =80, trapezium right angle = 100, +minimum width= 1cm, minimum height=0.5cm, text centered, draw=black, fill=cbgreen] + +% -------------------------defining connectors +\tikzstyle{arrow} = [thick,->, >=stealth] +\tikzstyle{line} = [thick,-,>=stealth] +\begin{document} + +% ------------------------- tikz image (flow chart) +\begin{tikzpicture}[node distance = 2cm] + +% ------------------------- nodes ------------------------- +% ----- node: 0 +\node(0)[input,label={90:\textbf{Demo Decision Tree with External Regressors, Motion, and Task Models}}, label={180:$node\ 0$}]{Set all components to unclassified}; +% ----- node: 1 +\node(1)[decision, below of=0,label={180:$node\ 1$}, yshift=-1.5cm]{$\rho$ $>$ $\kappa$}; +\node(rej1)[changeclass, right of=1, xshift=3cm, align=center]{Unlikely BOLD\\$\rightarrow$ Provisional reject}; +% ----- node: 2 +\node(2)[decision, below of=1,label={180:$node\ 2$}, label={[align=center] 315: voxel counts for signif fit\\of multi-echo data\\to $T_2$ or $S_0$ decay models}, yshift=-4.0cm]{$n \, FS_0 \, > \, n \, FT_2$ \& $n \,FT_2$ $>$ 0}; +\node(rej2)[changeclass, right of=2, xshift=3cm, align=center]{Unlikely BOLD\\$\rightarrow$ Provisional Reject}; +% ----- node: 3 +\node(3)[process, below of=2, label={180:$node\ 3$}, label={[align=center] 315: varex: variance explained\\by each component}, yshift=-2.0cm]{Calculate median(varex) across all components}; +% ----- node: 4 +\node(4)[decision, below of=3,label={180:$node\ 4$},label={[align=center] 315:DICE overlap between $T_2$ or $S_0$\\decay models and ICA component\\peak clusters}, yshift=-1.5cm]{dice $FS_0$ $>$ dice $FT_2$ \& varex $>$ median(varex) +}; +\node(rej4)[changeclass, right of=4, xshift=3cm, align=center]{Unlikely BOLD\\$\rightarrow$ Provisional Reject}; +% ----- node: 5 +\node(5)[decision, below of=4,label={180:$node\ 5$}, label={[align=center] 315: $t-statistic$ of $FT_2$ values\\in component peak clusters vs\\peak voxels outside of clusters}, yshift=-4.0cm]{ $0 \, >$ signal-noise \& varex $>$ median(varex)}; +\node(rej5)[changeclass, right of=5, xshift=3cm, align=center]{Unlikely BOLD\\$\rightarrow$ Provisional Reject}; +% ----- node: 6 +\node(6)[process, below of=5, label={180:$node\ 6$}, label={0: Uses all components}, yshift=-2.0cm]{Calculate $\kappa$ elbow}; +% ----- node: 7 +\node(7)[process, below of=6, label={180:$node\ 7$}, label={[align=center] 0: Uses all components and subset\\of unclassified components}]{Calculate $\rho$ elbow\\(liberal method)}; +% ----- node: 8 +\node(8)[decision, below of=7,label={180:$node\ 8$}, yshift=-1.5cm]{$\kappa \geq \kappa$ elbow\\$\rho$ $<$ $\rho$ elbow}; +\node(chrej8)[changeclass, below of=8, xshift=0cm, yshift=-2cm]{Provisional reject}; +\node(chacc8)[changeclass, right of=8, xshift=3cm, yshift=0cm]{Provisional accept}; +% ----- node: 9 +\node(9)[decision, below of=chrej8,label={180:$node\ 9$},label={20: Accept even if $\rho < \rho\ elbow$},yshift=-1.5cm]{$\kappa > 2\rho$\\$\kappa \geq \kappa$ elbow}; +\node(chrej9)[changeclass, below of=9, xshift=0cm, yshift=-2cm]{Provisional reject}; +\node(chacc9)[changeclass, right of=9, xshift=3cm, yshift=0cm]{Provisional accept}; +% ----- node: 10 +\node(10)[decision, below of=chacc9,label={150:$node\ 10$},label={[align=center] 310: Reject if\\fits external\\nuissance\\regressors},yshift=-2cm]{F test for\\Nuisance Regressors\\$p_{Full} \leq 0.05$\\$R^2_{Full} \geq 0.5$}; +\node(chrej10)[changeclass, below of=10, xshift=0cm, yshift=-2cm, align=center]{External regressors\\$\rightarrow$Provisional reject}; +% ----- node: 11 +\node(11)[decision, left of=chrej10,label={180:$node\ 11$},xshift=-3cm]{Partial F test for\\Motion Regressors\\$p_{Full} \leq 0.05$\\$R^2_{Full} \geq 0.5$\\$p_{Motion} \leq 0.05$}; +\node(chtag11)[changeclass, below of=11, xshift=0cm, yshift=-2cm, align=center]{Tag:\\Fits motion\\external regressors}; +% ----- node: 12 +\node(12)[decision, below of=chrej10,label={150:$node\ 12$},yshift=-2cm]{Partial F test for\\CSF Regressors\\$p_{Full} \leq 0.05$\\$R^2_{Full} \geq 0.5$\\$p_{CSF} \leq 0.05$}; +\node(chtag12)[changeclass, below of=12, xshift=0cm, yshift=-2cm, align=center]{Tag:\\Fits CSF\\external regressors}; +% ----- node: 13 +\node(prej13)[changeclass, below of=chtag11, xshift=0cm, yshift=-0.5cm]{Provisional reject}; +\node(13)[decision, below of=prej13,label={180:$node\ 13$},label={[align=center] 335: If fits task and\\contains T2*, accept\\even if other criteria\\would have rejected},yshift=-2cm]{F test for\\Task Regressors\\$p_{Task} \leq 0.05$\\$R^2_{Task} \geq 0.5$\\$\kappa \geq \kappa$ elbow}; +\node(chacc13)[accept, right of=13,xshift=3cm, align=center]{Fits task\\$\rightarrow$Accept}; +% ----- node: 14 +\node(14)[decision, below of=13,label={180:$node\ 14$},label={[align=left] 335: Will accept the lowest\\variance components until\\1\% of total variance is\\accepted this way}, yshift=-3.5cm]{$if$ component variance $<0.1$};%--check in kundu +\node(acc14)[accept, right of=14, xshift=2.5cm, align=center]{Low variance\\$\rightarrow$ Accept}; +% ----- node: 15 +\node(15)[accept, below of=14,label={180:$node\ 15$},yshift=-1.5cm, align=center]{Likely BOLD\\Change provisional accept\\$\rightarrow$Accept}; +% ----- node: 16 +\node(16)[reject, below of=15,label={180:$node\ 16$}, yshift=0cm, align=center]{Unlikely BOLD\\Change provisional reject\\$\rightarrow$Reject}; + +% ------------------------- connections ------------------------- +% draw[x](origin)--node[anchor=position]{text}(destination); +\draw[arrow](0)--(1); +\draw[arrow](1)--node[anchor=south, right=0] {no} (2); +\draw[arrow](1)--node[anchor=south] {yes} (rej1); +\draw[arrow](2)--node[anchor=south, right=0] {no} (3); +\draw[arrow](2)--node[anchor=south] {yes} (rej2); +\draw[arrow](3)--(4); +\draw[arrow](4)--node[anchor=south, right=0] {no} (5); +\draw[arrow](4)--node[anchor=south] {yes} (rej4); +\draw[arrow](5)--node[anchor=south, right=0] {no} (6); +\draw[arrow](5)--node[anchor=south] {yes} (rej5); +\draw[arrow](6)--(7); +\draw[arrow](7)--(8); +\draw[arrow](8)--node[anchor=south] {yes} (chacc8); +\draw[arrow](8)--node[anchor=south, right=0] {no} (chrej8); +\draw[arrow](chrej8)--(9); +\draw[arrow](9)--node[anchor=south] {yes} (chacc9); +\draw[arrow](9)--node[anchor=south, right=0] {no} (chrej9); +\draw[arrow](chacc9)--(10); +\draw[arrow](chrej9)--(10); +\draw[arrow](10)--node[anchor=south, right=0] {yes} (chrej10); +\draw[arrow](chrej10)--(11); +\draw[arrow](11)--node[anchor=south, right=0] {yes} (chtag11); +\draw[arrow](chrej10)--(12); +\draw[arrow](12)--node[anchor=south, right=0] {yes} (chtag12); +\draw[arrow](prej13)--(13); +\draw[arrow](13)--node[anchor=south] {yes} (chacc13); +\draw[arrow](13)--node[anchor=south, right=0] {no} (14); +\draw[arrow](14)--node[anchor=south] {yes} (acc14); +\end{tikzpicture} +\end{document} diff --git a/docs/included_decision_trees.rst b/docs/included_decision_trees.rst index c0d3f4e9a..42a2fb02b 100644 --- a/docs/included_decision_trees.rst +++ b/docs/included_decision_trees.rst @@ -2,7 +2,7 @@ Included Decision Trees ####################### -Three decision trees are currently distributed with ``tedana``. +Five decision trees are currently distributed with ``tedana``. ``meica`` is the decision tree that is based on MEICA version 2.5 and ``tedana_orig`` is very similar and has been included with ``tedana`` @@ -18,7 +18,18 @@ and comprehensible, but it has not yet be extensively validated and parts of the tree may change in response to additional tests on a wider range of data sets. -Flowcharts describing the steps in both trees are below. +With the addition of options to fit external regressors to components, +there are two demonstration decision trees that implement this new functionality. +While these might work well, since they have not yet been validated on data, they +are labeled ``demo``. +``decision_tree_demo_minimal_external_regressors_single_model`` +demonstrates fitting all nuissance regressors to a single model. +``decision_tree_demo_minimal_external_regressors_motion_task_models`` +demonstrates fitting nuissance regressors to a model, +partial tests and tagging for components that fit Motion or CSF regressors, +and retention of some components that fit task regressors. + +Flowcharts describing the steps in these trees are below. As documented more in :doc:`building_decision_trees`, the input to each tree is a table with metrics, like :math:`\kappa` or :math:`\rho`, for each component. Each step or node in the decision tree either calculates @@ -122,3 +133,19 @@ is rejected (node 13) `LaTeX file to generate the minimal decision tree flow chart`_ .. _LaTeX file to generate the minimal decision tree flow chart: _static/decision_tree_minimal.tex + +********************************************* +Demo minimal external regressors single model +********************************************* + +To be added + +**************************************************** +Demo minimal external regressors, motion task models +**************************************************** + +Add text + +.. image:: _static/decision_tree_demo_minimal_external_regressors_motion_task_models.png + :width: 400 + :alt: External Decision Tree With Motion and Task Models Flow Chart diff --git a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json index 274da28bd..57083a308 100644 --- a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json +++ b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json @@ -140,19 +140,14 @@ "op": ">=", "left": "kappa", "right": "kappa_elbow_kundu" - } - }, - { - "functionname": "dec_left_op_right", - "parameters": { - "if_true": "provisionalreject", - "if_false": "nochange", - "decide_comps": ["provisionalreject", "provisionalaccept"], - "op": ">", - "left": "rho", - "right": "rho_elbow_liberal" }, - "kwargs": {"tag_if_true": "Unlikely BOLD"} + "kwargs": { + "log_extra_info": "If kappa> kappa elbow and rho", "left2": "kappa", "right2": "kappa_elbow_kundu", - "tag_if_true": "Likely BOLD" + "tag_if_true": "Likely BOLD", + "tag_if_false": "Unlikely BOLD" } }, { @@ -197,7 +193,7 @@ "parameters": { "if_true": "nochange", "if_false": "nochange", - "decide_comps": "all", + "decide_comps": "provisionalreject", "op": "<", "left": "pval Full Model", "right": 0.05 @@ -218,7 +214,7 @@ "parameters": { "if_true": "nochange", "if_false": "nochange", - "decide_comps": "all", + "decide_comps": "provisionalreject", "op": "<", "left": "pval Full Model", "right": 0.05 @@ -239,7 +235,7 @@ "parameters": { "if_true": "accepted", "if_false": "nochange", - "decide_comps": ["provisionalreject", "rejected"], + "decide_comps": ["provisionalreject"], "op": "<", "left": "pval Task Model", "right": 0.05 From 2caab11da5b9f9525cd30f211fdd38cc085720a6 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Sun, 7 Jul 2024 21:59:14 -0400 Subject: [PATCH 77/81] made recommended change to external_regressor_config --- tedana/metrics/_utils.py | 42 +- tedana/metrics/collect.py | 14 +- tedana/metrics/external.py | 370 +++++++++--------- ...xternal_regressors_motion_task_models.json | 65 +-- ...imal_external_regressors_single_model.json | 25 +- tedana/selection/component_selector.py | 98 ++--- tedana/tests/test_component_selector.py | 78 ++-- tedana/tests/test_external_metrics.py | 200 +++++----- tedana/tests/test_metrics.py | 24 +- 9 files changed, 503 insertions(+), 413 deletions(-) diff --git a/tedana/metrics/_utils.py b/tedana/metrics/_utils.py index 6e2da2d10..4c28fb83c 100644 --- a/tedana/metrics/_utils.py +++ b/tedana/metrics/_utils.py @@ -10,17 +10,19 @@ LGR = logging.getLogger("GENERAL") -def add_external_dependencies(dependency_config: Dict, external_regressor_config: Dict) -> Dict: +def add_external_dependencies( + dependency_config: Dict, external_regressor_config: List[Dict] +) -> Dict: """ - Add dependency information in external regressors are inputted. + Add dependency information when external regressors are inputted. Parameters ---------- dependency_config: :obj:`dict` A dictionary stored in ./config/metrics.json with information on all the internally defined metrics like kappa and rho - external_regressor_config: :obj:`dict` - A dictionary with info for fitting external regressors to component time series + external_regressor_config: :obj:`list[dict]` + A list of dictionaries with info for fitting external regressors to component time series Returns ------- @@ -31,22 +33,22 @@ def add_external_dependencies(dependency_config: Dict, external_regressor_config # Add "external regressors" and an existing input dependency_config["inputs"].append("external regressors") - if external_regressor_config["calc_stats"].lower() == "f": - model_names = ["Full"] - if "f_stats_partial_models" in set(external_regressor_config.keys()): - if isinstance(external_regressor_config["f_stats_partial_models"], list): - model_names.extend(external_regressor_config["f_stats_partial_models"]) - else: # A single string - model_names.append(external_regressor_config["f_stats_partial_models"]) - - if "task_keep" in set(external_regressor_config.keys()): - model_names.append("Task") - - for model_name in model_names: - for stat_type in ["Fstat", "R2stat", "pval"]: - dependency_config["dependencies"][f"{stat_type} {model_name} Model"] = [ - "external regressors" - ] + for config_idx in range(len(external_regressor_config)): + model_names = [external_regressor_config[config_idx]["regress_ID"]] + if "partial_models" in set(external_regressor_config[config_idx].keys()): + partial_keys = external_regressor_config[config_idx]["partial_models"].keys() + for key_name in partial_keys: + model_names.append( + f"{external_regressor_config[config_idx]['regress_ID']} {key_name} partial" + ) + + # F is currently the only option so this only names metrics if "statistic"=="f" + if external_regressor_config[config_idx]["statistic"].lower() == "f": + for model_name in model_names: + for stat_type in ["Fstat", "R2stat", "pval"]: + dependency_config["dependencies"][f"{stat_type} {model_name} model"] = [ + "external regressors" + ] return dependency_config diff --git a/tedana/metrics/collect.py b/tedana/metrics/collect.py index e4bf12d23..e9c19a96b 100644 --- a/tedana/metrics/collect.py +++ b/tedana/metrics/collect.py @@ -32,7 +32,7 @@ def generate_metrics( io_generator: io.OutputGenerator, label: str, external_regressors: Union[pd.DataFrame, None] = None, - external_regressor_config: Union[Dict, None] = None, + external_regressor_config: Union[List[Dict], None] = None, metrics: Union[List[str], None] = None, ) -> Tuple[pd.DataFrame, Dict]: """Fit TE-dependence and -independence models to components. @@ -61,8 +61,8 @@ def generate_metrics( External regressors (e.g., motion parameters, physiological noise) to correlate with ICA components. If None, no external regressor metrics will be calculated. - external_regressor_config : :obj:`dict` - A dictionary for defining how to fit external regressors to component time series + external_regressor_config : :obj:`list[dic]t` + A list of dictionaries defining how to fit external regressors to component time series metrics : list List of metrics to return @@ -352,8 +352,12 @@ def generate_metrics( # External regressor-based metrics if external_regressors is not None and external_regressor_config is not None: # external_regressor_names = external_regressors.columns.tolist() - LGR.info(f"Calculating external regressor fits. {external_regressor_config['info']}") - RepLGR.info({external_regressor_config["report"]}) + for config_idx in range(len(external_regressor_config)): + LGR.info( + "Calculating external regressor fits. " + f"{external_regressor_config[config_idx]['info']}" + ) + RepLGR.info({external_regressor_config[config_idx]["report"]}) comptable = external.fit_regressors( comptable, external_regressors, external_regressor_config, mixing diff --git a/tedana/metrics/external.py b/tedana/metrics/external.py index f614731a1..0f89516a8 100644 --- a/tedana/metrics/external.py +++ b/tedana/metrics/external.py @@ -2,7 +2,7 @@ import logging import re -from typing import Dict, Tuple +from typing import Dict, List, Tuple import numpy as np import numpy.typing as npt @@ -59,8 +59,8 @@ def load_validate_external_regressors( def validate_extern_regress( - external_regressors: Dict, external_regressor_config: Dict, n_vols: int -) -> Dict: + external_regressors: pd.DataFrame, external_regressor_config: List[Dict], n_vols: int +) -> List[Dict]: """Confirm external regressor dictionary matches data and expands regular expressions. Most keys in external_regressor_config are valided in component_selector.validate_tree @@ -77,94 +77,123 @@ def validate_extern_regress( external_regressors : :obj:`pandas.DataFrame` Each column is a labelled regressor and the number of rows should match the number of timepoints in the fMRI time series - external_regressor_config : :obj:`dict` + external_regressor_config : :obj:`list[dict]` Information describing the external regressors and - method to use for fitting and statistical tests + method to use for fitting and statistical tests. + Each element in the list is a dict defining the regressors + and statistical models for a test. n_vols : :obj:`int` The number of time points in the fMRI time series Returns ------- - external_regressor_config: :obj:`dict` - A validated dictionary with info for fitting external regressors + external_regressor_config: :obj:`list[dict]` + A validated list of dictionaries with info for fitting external regressors to component time series. If regex patterns like '^mot_.*$' are used to define regressor names, - this is replaced with a list of the matching column names used in external_regressors + these are replaced with a list of the matching column names used in external_regressors Raises ------ RegressorError if any validation test fails """ # err_msg is used to collect all errors in validation rather than require this to be run - # multiple times to see all validation errors. Will either collect errors and raise at the - # end of the function or raise errors that prevent the rest of the function from completing + # multiple times to see all validation errors. + # Will either collect errors and raise at the end of the function + # or raise errors that prevent the rest of the function from completing err_msg = "" - # Validating the information in external_regressor_config works - # with the data in external_regressors - - # Currently column labels only need to be predefined for calc_stats==F - # and there are f_stats_partial_models or if "task_keep" is used. - # column_label_specifications will include the names of the column labels for - # both f_stats_partial_models and task_keep if either or both are defined. - column_label_specifications = set() - if "f_stats_partial_models" in set(external_regressor_config.keys()): - column_label_specifications.update( - set(external_regressor_config["f_stats_partial_models"]) - ) - if "task_keep" in set(external_regressor_config.keys()): - column_label_specifications.update(["task_keep"]) - - if column_label_specifications: - regressor_names = set(external_regressors.columns) - expected_regressor_names = set() - # for each column label, check if the label matches column names in external_regressors - for partial_models in column_label_specifications: - tmp_partial_model_names = set() - for tmp_name in external_regressor_config[partial_models]: - # If a label starts with ^ treat match the pattern to column names using regex - if tmp_name.startswith("^"): - tmp_replacements = [ - reg_name - for reg_name in regressor_names - if re.match(tmp_name, reg_name, re.IGNORECASE) - ] - if not tmp_replacements: - err_msg += ( - f"No external regressor labels matching regex '{tmp_name}' found." - ) - tmp_partial_model_names.update(set(tmp_replacements)) + external_regressor_names = set(external_regressors.columns) + + def expand_regress_regex(regressor_templates, external_regressor_names, err_msg): + """Match or regex expand regressor names from config.""" + expanded_regressor_names = set() + for tmp_regressor_name in regressor_templates: + # If a regressor name is a regular expression, use re to match and expand + # by comparing to regressor names in external_regressor_names. + if tmp_regressor_name.startswith("^"): + expanded_names = [ + reg_name + for reg_name in external_regressor_names + if re.match(tmp_regressor_name, reg_name, re.IGNORECASE) + ] + if not expanded_names: + err_msg += ( + "No external regressor labels matching " + f"regular expression '{tmp_regressor_name}' found.\n" + ) else: - tmp_partial_model_names.add(tmp_name) - external_regressor_config[partial_models] = list(tmp_partial_model_names) - if expected_regressor_names.intersection(tmp_partial_model_names): - LGR.warning( - "External regressors used in more than one partial model: " - f"{expected_regressor_names.intersection(tmp_partial_model_names)}" - ) - expected_regressor_names.update(tmp_partial_model_names) - if expected_regressor_names - regressor_names: - err_msg += ( - "Inputed regressors in external_regressors do not include all expected " - "regressors in partial models or task\n" - "Expected regressors not in inputted regressors: " - f"{expected_regressor_names - regressor_names}\n" - f"Inputted Regressors: {regressor_names}" - ) - if regressor_names - expected_regressor_names: + expanded_regressor_names.update(set(expanded_names)) + else: + # If a regressor name is a string, check if it is in external_regressor_names + if tmp_regressor_name in external_regressor_names: + expanded_regressor_names.add(tmp_regressor_name) + else: + err_msg += ( + f"No external regressor matching '{tmp_regressor_name}' was found.\n" + ) + return expanded_regressor_names, err_msg + + # Expanding the regressors used for each model + all_regressor_names = set() + for config_idx in range(len(external_regressor_config)): + expanded_regressor_names, err_msg = expand_regress_regex( + external_regressor_config[config_idx]["regressors"], external_regressor_names, err_msg + ) + reused_regressors = all_regressor_names.intersection(expanded_regressor_names) + if reused_regressors: LGR.warning( - "Regressor labels in external_regressors are not all included in F " - "statistic partial models or task models. Regressor not in a partial " - "model or task_keep, it will be included in the full F statistic model " - "and treated like nuisance regressors to remove. " - "Regressors not included in any partial model: " - f"{regressor_names - expected_regressor_names}" + f"{list(reused_regressors).sort()} used in " + "more than one external regressor model" ) + all_regressor_names.update(expanded_regressor_names) + external_regressor_config[config_idx]["regressors"] = sorted(expanded_regressor_names) + + extra_names = set(external_regressor_names) - all_regressor_names + if extra_names: + LGR.warning( + "User-provided external_regressors include columns not used in any " + f"external regressor model: {sorted(extra_names)}" + ) + + # If a model includes specifications for partial regressors, expand them + for config_idx in range(len(external_regressor_config)): + if "partial_models" in external_regressor_config[config_idx].keys(): + if not isinstance(external_regressor_config[config_idx]["partial_models"], type(None)): + part_model_regress_names = set() + for part_model in external_regressor_config[config_idx]["partial_models"].keys(): + expanded_regressor_names, err_msg = expand_regress_regex( + external_regressor_config[config_idx]["partial_models"][part_model], + external_regressor_names, + err_msg, + ) + reused_regressors = part_model_regress_names.intersection( + expanded_regressor_names + ) + if reused_regressors: + LGR.warning( + f"{sorted(reused_regressors)} used in " + "more than one partial regressor model for " + f"{external_regressor_config[config_idx]['regress_ID']}" + ) + part_model_regress_names.update(expanded_regressor_names) + external_regressor_config[config_idx]["partial_models"][part_model] = sorted( + expanded_regressor_names + ) + extra_names = part_model_regress_names - set( + external_regressor_config[config_idx]["regressors"] + ) + if extra_names: + err_msg += ( + f"Partial models in {external_regressor_config[config_idx]['regress_ID']} " + "include regressors that are excluded from its full model: " + f"{sorted(extra_names)}\n" + ) if len(external_regressors.index) != n_vols: err_msg += ( - f"External Regressors have {len(external_regressors.index)} timepoints\n" - f"while fMRI data have {n_vols} timepoints" + f"External Regressors have {len(external_regressors.index)} timepoints " + f"while fMRI data have {n_vols} timepoints\n" ) if err_msg: @@ -176,7 +205,7 @@ def validate_extern_regress( def fit_regressors( comptable: pd.DataFrame, external_regressors: pd.DataFrame, - external_regressor_config: Dict, + external_regressor_config: List[Dict], mixing: npt.NDArray, ) -> pd.DataFrame: """Fit regressors to the mixing matrix. @@ -189,10 +218,10 @@ def fit_regressors( comptable : (C x X) :obj:`pandas.DataFrame` Component metric table. One row for each component, with a column for each metric. The index is the component number. - external_regressors : :obj:`pandas.DataFrame` + external_regressors : (T x R) :obj:`pandas.DataFrame` Each column is a labelled regressor and the number of rows should match the number of timepoints in the fMRI time series - external_regressor_config : :obj:`dict` + external_regressor_config : :obj:`list[dict]` Information describing the external regressors and method to use for fitting and statistical tests mixing : (T x C) array_like @@ -207,50 +236,60 @@ def fit_regressors( """ n_vols = mixing.shape[0] - # If the order of detrending regressors is specified, then pass to - # create_legendre_polynomial_basis_set - # otherwise the function sets an order for the Legendre polynomials - if external_regressor_config["detrend"] is True: - legendre_arr = utils.create_legendre_polynomial_basis_set(n_vols, dtrank=None) - LGR.info( - "External regressors fit includes detrending with " - f"{legendre_arr.shape[1]} Legendre Polynomial regressors." - ) - - elif ( - isinstance(external_regressor_config["detrend"], int) - and external_regressor_config["detrend"] > 0 - ): - legendre_arr = utils.create_legendre_polynomial_basis_set( - n_vols, dtrank=external_regressor_config["detrend"] - ) - LGR.info( - "External regressors fit includes detrending with " - f"{legendre_arr.shape[1]} Legendre Polynomial regressors." - ) - else: - LGR.warning( - "External regressor fitted without detrending fMRI time series. Only removing mean" - ) - legendre_arr = utils.create_legendre_polynomial_basis_set(n_vols, dtrank=1) - - detrend_labels = [] - for label_idx in range(legendre_arr.shape[1]): - detrend_labels.append(f"baseline {label_idx}") - detrend_regressors = pd.DataFrame(data=legendre_arr, columns=detrend_labels) + # For every model (i.e. nuisance and task) in external_regressor_config + # setup and run fit_mixing_to_regressors to add columns to comptable + for config_idx in range(len(external_regressor_config)): + regress_id = external_regressor_config[config_idx]["regress_ID"] + # If the order of detrending regressors is specified, then pass to + # create_legendre_polynomial_basis_set + # otherwise the function sets an order for the Legendre polynomials + if external_regressor_config[config_idx]["detrend"] is True: + legendre_arr = utils.create_legendre_polynomial_basis_set(n_vols, dtrank=None) + LGR.info( + f"External regressors fit for {regress_id} includes detrending with " + f"{legendre_arr.shape[1]} Legendre Polynomial regressors." + ) - if external_regressor_config["calc_stats"].lower() == "f": - comptable = fit_mixing_to_regressors( - comptable, external_regressors, external_regressor_config, mixing, detrend_regressors - ) - else: - # This should already be validated by this point, but keeping the catch clause here - # since this would otherwise just return comptable with no changes, which would - # make a hard-to-track error - raise ValueError( - "calc_stats for external regressors in decision tree is " - f"{external_regressor_config['calc_stats'].lower()}, which is not valid." - ) + elif ( + isinstance(external_regressor_config[config_idx]["detrend"], int) + and external_regressor_config[config_idx]["detrend"] > 0 + ): + legendre_arr = utils.create_legendre_polynomial_basis_set( + n_vols, dtrank=external_regressor_config[config_idx]["detrend"] + ) + LGR.info( + f"External regressors fit for {regress_id} includes detrending with " + f"{legendre_arr.shape[1]} Legendre Polynomial regressors." + ) + else: + LGR.warning( + f"External regressor for {regress_id} fitted without detrending fMRI time series. " + "Only removing mean" + ) + legendre_arr = utils.create_legendre_polynomial_basis_set(n_vols, dtrank=1) + + detrend_labels = [] + for label_idx in range(legendre_arr.shape[1]): + detrend_labels.append(f"baseline {label_idx}") + detrend_regressors = pd.DataFrame(data=legendre_arr, columns=detrend_labels) + + if external_regressor_config[config_idx]["statistic"].lower() == "f": + comptable = fit_mixing_to_regressors( + comptable, + external_regressors, + external_regressor_config[config_idx], + mixing, + detrend_regressors, + ) + else: + # This should already be validated by this point, but keeping the catch clause here + # since this would otherwise just return comptable with no changes, which would + # make a hard-to-track error + raise ValueError( + f"statistic for {regress_id} external regressors in decision tree is " + f"{external_regressor_config[config_idx]['statistic'].lower()}, " + "which is not valid." + ) return comptable @@ -281,7 +320,9 @@ def fit_mixing_to_regressors( match the number of timepoints in the fMRI time series external_regressor_config : :obj:`dict` Information describing the external regressors and - method to use for fitting and statistical tests + method to use for fitting and statistical tests. + In other functions this is a list[dict] but here it is a + single dict which is one element in the list[dict] mixing : (T x C) array_like Mixing matrix for converting input data to component space, where `C` is components and `T` is the same as in `data_cat` @@ -297,7 +338,8 @@ def fit_mixing_to_regressors( Names are "Fstat Full Model", "pval Full Model", "R2stat Full Model", and "Full" is replaced by the partial model name for each partial model """ - LGR.info("Running fit_mixing_to_regressors") + regress_id = external_regressor_config["regress_ID"] + LGR.info(f"Running fit_mixing_to_regressors for {regress_id}") LGR.info(f"ICA matrix has {mixing.shape[0]} time points and {mixing.shape[1]} components") # regressor_models is a dictionary of all the models that will be fit to the mixing matrix @@ -324,28 +366,19 @@ def fit_mixing_to_regressors( # (np.array(detrend_regressors.columns), np.array(exte rnal_regressors.columns)) # ), # ) - f_vals = pd.DataFrame(data=f_vals_tmp, columns=["Fstat Full Model"]) - p_vals = pd.DataFrame(data=p_vals_tmp, columns=["pval Full Model"]) - r2_vals = pd.DataFrame(data=r2_vals_tmp, columns=["R2stat Full Model"]) - - # Run a separate model if there are task regressors that might want to be kept - if "task keep" in regressor_models.keys(): - betas_task_keep, f_vals_tmp, p_vals_tmp, r2_vals_tmp = fit_model_with_stats( - y=mixing, regressor_models=regressor_models, base_label="base", full_label="task keep" - ) - f_vals["Fstat Task Model"] = f_vals_tmp - p_vals["pval Task Model"] = p_vals_tmp - r2_vals["R2stat Task Model"] = r2_vals_tmp + f_vals = pd.DataFrame(data=f_vals_tmp, columns=[f"Fstat {regress_id} model"]) + p_vals = pd.DataFrame(data=p_vals_tmp, columns=[f"pval {regress_id} model"]) + r2_vals = pd.DataFrame(data=r2_vals_tmp, columns=[f"R2stat {regress_id} model"]) # Test the fits between the full model and the full model excluding one category of regressor - if "f_stats_partial_models" in external_regressor_config.keys(): - for pmodel in external_regressor_config["f_stats_partial_models"]: + if "partial_models" in external_regressor_config.keys(): + for pmodel in external_regressor_config["partial_models"].keys(): _, f_vals_tmp, p_vals_tmp, r2_vals_tmp = fit_model_with_stats( mixing, regressor_models, f"no {pmodel}" ) - f_vals[f"Fstat {pmodel} Model"] = f_vals_tmp - p_vals[f"pval {pmodel} Model"] = p_vals_tmp - r2_vals[f"R2stat {pmodel} Model"] = r2_vals_tmp + f_vals[f"Fstat {regress_id} {pmodel} partial model"] = f_vals_tmp + p_vals[f"pval {regress_id} {pmodel} partial model"] = p_vals_tmp + r2_vals[f"R2stat {regress_id} {pmodel} partial model"] = r2_vals_tmp # Add all F p and R2 statistics to comptable comptable = pd.concat((comptable, f_vals, p_vals, r2_vals), axis=1) @@ -367,7 +400,9 @@ def build_fstat_regressor_models( match the number of timepoints in the fMRI time series external_regressor_config : :obj:`dict` Information describing the external regressors and - method to use for fitting and statistical tests + method to use for fitting and statistical tests. + In other functions this is a list[dict] but here it is a + single dict which is one element in the list[dict] detrend_regressors: (n_vols x polort) :obj:`pandas.DataFrame` Dataframe containing the detrending regressor time series @@ -385,58 +420,43 @@ def build_fstat_regressor_models( This is for the F test which compares the variance explained with the full model to the variance explained if the regressors-of-interest for the partial model are removed. """ + regress_id = external_regressor_config["regress_ID"] # The category titles to group each regressor - if "f_stats_partial_models" in external_regressor_config: - partial_models = external_regressor_config["f_stats_partial_models"] + if "partial_models" in external_regressor_config: + partial_models = external_regressor_config["partial_models"].keys() else: partial_models = [] - # All regressor labels from the data frame - regressor_labels = external_regressors.columns + # All regressor labels for the full model + regressor_labels = set(external_regressor_config["regressors"]) + detrend_regressors_arr = detrend_regressors.to_numpy() regressor_models = {"base": detrend_regressors_arr} - LGR.info(f"Size for base regressor model: {regressor_models['base'].shape}") - - if "task_keep" in external_regressor_config: - task_keep_model = external_regressor_config["task_keep"] - # If there is a task_keep model, then the full model should exclude every - # regressor in task_keep - tmp_model_labels = {"full": set(regressor_labels) - set(task_keep_model)} - tmp_model_labels["task keep"] = set(task_keep_model) - for model_name in ["full", "task keep"]: - regressor_models[model_name] = detrend_regressors_arr - for keep_label in tmp_model_labels[model_name]: - regressor_models[model_name] = np.concatenate( - ( - regressor_models[model_name], - np.atleast_2d( - stats.zscore(external_regressors[keep_label].to_numpy(), axis=0) - ).T, - ), - axis=1, - ) - tmp_model_labels[model_name].update(set(detrend_regressors.columns)) - LGR.info( - f"Size for {model_name} regressor model: {regressor_models[model_name].shape}" - ) - LGR.info(f"Regressors in {model_name} model: {sorted(tmp_model_labels[model_name])}") - # Remove task_keep regressors from regressor_labels before calculating partial models - regressor_labels = set(regressor_labels) - set(task_keep_model) - else: + LGR.info(f"Size for base regressor model for {regress_id}: {regressor_models['base'].shape}") + + regressor_models["full"] = detrend_regressors_arr + for keep_label in regressor_labels: regressor_models["full"] = np.concatenate( - (detrend_regressors_arr, stats.zscore(external_regressors.to_numpy(), axis=0)), axis=1 - ) - LGR.info(f"Size for full regressor model: {regressor_models['full'].shape}") - LGR.info( - "Regressors in full model: " - f"{sorted(set(regressor_labels).union(set(detrend_regressors.columns)))}" + ( + regressor_models["full"], + np.atleast_2d(stats.zscore(external_regressors[keep_label].to_numpy(), axis=0)).T, + ), + axis=1, ) + regressor_labels.update(set(detrend_regressors.columns)) + # regressor_models["full"] = np.concatenate( + # (detrend_regressors_arr, stats.zscore(external_regressors.to_numpy(), axis=0)), axis=1 + # ) + LGR.info(f"Size for full regressor model for {regress_id}: {regressor_models['full'].shape}") + LGR.info(f"Regressors in full model for {regress_id}: {sorted(set(regressor_labels))}") for pmodel in partial_models: # For F statistics, the other models to test are those that include everything EXCEPT # the category of interest # That is "no motion" should contain the full model excluding motion regressors - keep_labels = set(regressor_labels) - set(external_regressor_config[pmodel]) + keep_labels = set(external_regressor_config["regressors"]) - set( + external_regressor_config["partial_models"][pmodel] + ) no_pmodel = f"no {pmodel}" regressor_models[no_pmodel] = detrend_regressors_arr for keep_label in keep_labels: @@ -451,7 +471,7 @@ def build_fstat_regressor_models( ) keep_labels.update(set(detrend_regressors.columns)) LGR.info( - f"Size for external regressor partial model '{no_pmodel}': " + f"Size of external regressor partial model '{no_pmodel}': " f"{regressor_models[no_pmodel].shape}" ) LGR.info( diff --git a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json index 57083a308..9619fbb51 100644 --- a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json +++ b/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json @@ -11,12 +11,12 @@ "dice_FT2", "signal-noise_t", "variance explained", - "pval Full Model", - "pval Task Model", - "pval Motion Model", - "pval CSF Model", - "R2stat Full Model", - "R2stat Task Model" + "pval nuisance model", + "pval task model", + "pval nuisance Motion partial model", + "pval nuisance CSF partial model", + "R2stat nuisance model", + "R2stat task model" ], "intermediate_classifications": ["provisionalaccept", "provisionalreject"], "classification_tags": [ @@ -28,17 +28,28 @@ "Fits CSF external regressors", "Fits task" ], - "external_regressor_config": { - "regress_ID": "Fmodel", - "info": "Fits all external regressors to a single model using an F statistic", - "report": "Unspecified external nuisance regressors that were fit to components using a linear model were rejected if they fit.", - "detrend": true, - "calc_stats": "F", - "f_stats_partial_models": ["Motion", "CSF"], - "Motion": ["^mot_.*$"], - "CSF": ["^csf.*$"], - "task_keep": ["^signal.*$"] - }, + "external_regressor_config": [ + { + "regress_ID": "nuisance", + "info": "Fits all external nuissance regressors to a single model using an F statistic", + "report": "External nuisance regressors that fit to components using a linear model were rejected.", + "detrend": true, + "statistic": "F", + "regressors": ["^(?!signal).*$"], + "partial_models": { + "Motion": ["^mot_.*$"], + "CSF": ["^csf.*$"] + } + }, + { + "regress_ID": "task", + "info": "Fits all task regressors to a single model using an F statistic", + "report": "Task regressors that fit to components using a linear model and have some T2* weighting were accepted even if they would have been rejected base on other criteriea.", + "detrend": true, + "statistic": "F", + "regressors": ["^signal.*$"] + } + ], "nodes": [ { "functionname": "manual_classify", @@ -176,12 +187,12 @@ "if_false": "nochange", "decide_comps": "all", "op": "<", - "left": "pval Full Model", + "left": "pval nuisance model", "right": 0.05 }, "kwargs": { "op2": ">", - "left2": "R2stat Full Model", + "left2": "R2stat nuisance model", "right2": 0.5, "log_extra_info": "If external regressors fit with p<0.05 and model R2>0.5 of the variance, then reject.", "tag_if_true": "External regressors" @@ -195,15 +206,15 @@ "if_false": "nochange", "decide_comps": "provisionalreject", "op": "<", - "left": "pval Full Model", + "left": "pval nuisance model", "right": 0.05 }, "kwargs": { "op2": ">", - "left2": "R2stat Full Model", + "left2": "R2stat nuisance model", "right2": 0.5, "op3": "<", - "left3": "pval Motion Model", + "left3": "pval nuisance Motion partial model", "right3": 0.05, "tag_if_true": "Fits motion external regressors" }, @@ -216,15 +227,15 @@ "if_false": "nochange", "decide_comps": "provisionalreject", "op": "<", - "left": "pval Full Model", + "left": "pval nuisance model", "right": 0.05 }, "kwargs": { "op2": ">", - "left2": "R2stat Full Model", + "left2": "R2stat nuisance model", "right2": 0.5, "op3": "<", - "left3": "pval CSF Model", + "left3": "pval nuisance CSF partial model", "right3": 0.05, "tag_if_true": "Fits CSF external regressors" }, @@ -237,12 +248,12 @@ "if_false": "nochange", "decide_comps": ["provisionalreject"], "op": "<", - "left": "pval Task Model", + "left": "pval task model", "right": 0.05 }, "kwargs": { "op2": ">", - "left2": "R2stat Task Model", + "left2": "R2stat task model", "right2": 0.5, "op3": ">=", "left3": "kappa", diff --git a/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json b/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json index 2733b71f5..1b7e1f0f0 100644 --- a/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json +++ b/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json @@ -11,18 +11,21 @@ "dice_FT2", "signal-noise_t", "variance explained", - "pval Full Model", - "R2stat Full Model" + "pval nuisance model", + "R2stat nuisance model" ], "intermediate_classifications": ["provisionalaccept", "provisionalreject"], "classification_tags": ["Likely BOLD", "Unlikely BOLD", "Low variance", "External regressors"], - "external_regressor_config": { - "regress_ID": "Fmodel", - "info": "Fits all external regressors to a single model using an F statistic", - "report": "Unspecified external nuissance regressors were fit to components using a linear model were rejected if they fit.", - "detrend": true, - "calc_stats": "F" - }, + "external_regressor_config": [ + { + "regress_ID": "nuisance", + "info": "Fits all external nuissance regressors to a single model using an F statistic", + "report": "External nuisance regressors that fit to components using a linear model were rejected.", + "detrend": true, + "statistic": "F", + "regressors": ["^.*$"] + } + ], "nodes": [ { "functionname": "manual_classify", @@ -173,12 +176,12 @@ "if_false": "nochange", "decide_comps": "all", "op": "<", - "left": "pval Full Model", + "left": "pval nuisance model", "right": 0.05 }, "kwargs": { "op2": ">", - "left2": "R2stat Full Model", + "left2": "R2stat nuisance model", "right2": 0.5, "log_extra_info": "If external regressors fit with p<0.05 and model R2>0.5 of the variance, then reject.", "tag_if_true": "External regressors" diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 4e91fe96a..1a0b1ef50 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -243,56 +243,60 @@ def validate_tree(tree: Dict) -> Dict: # If there is an external_regressor_config field, validate it if tree["external_regressor_config"] is not None: external_regressor_config = tree["external_regressor_config"] - # Define the fields that should always be present - dict_expected_keys = set(["regress_ID", "info", "report", "detrend", "calc_stats"]) - dict_optional_keys = set(["task_keep"]) - # Right now, "f" is the only option, but this leaves open the possibility - # to have additional options - calc_stats_key_options = set("f") - - # Confirm that the required fields exist - missing_keys = dict_expected_keys - set(external_regressor_config.keys()) - if missing_keys: - err_msg += f"External regressor dictionary missing required fields: {missing_keys}\n" - - if "calc_stats" in set(external_regressor_config.keys()): - if external_regressor_config["calc_stats"].lower() not in calc_stats_key_options: - err_msg += ( - "calc_stats in external_regressor_config is " - f"{external_regressor_config['calc_stats']}. It must be one of the following: " - f"{calc_stats_key_options}\n" - ) - - if (external_regressor_config["calc_stats"].lower() != "f") and ( - "f_stats_partial_models" in set(external_regressor_config.keys()) - ): - err_msg += ( - "External regressor dictionary cannot include " - "f_stats_partial_models if calc_stats is not F\n" + if isinstance(external_regressor_config, list): + # Define the fields that should always be present + dict_expected_keys = set( + ["regress_ID", "info", "report", "detrend", "statistic", "regressors"] + ) + dict_optional_keys = set(["partial_models"]) + # Right now, "f" is the only option, but this leaves open the possibility + # to have additional options + statistic_key_options = set("f") + + for config_idx in range(len(external_regressor_config)): + # Confirm that the required fields exist + missing_keys = dict_expected_keys - set( + external_regressor_config[config_idx].keys() ) + if missing_keys: + err_msg += ( + f"External regressor dictionary {config_idx} " + f"is missing required fields: {missing_keys}\n" + ) - if "f_stats_partial_models" in set(external_regressor_config.keys()): - dict_expected_keys.add("f_stats_partial_models") - dict_expected_keys.update(set(external_regressor_config["f_stats_partial_models"])) - missing_partial_models = set( - external_regressor_config["f_stats_partial_models"] - ) - set(external_regressor_config.keys()) - if missing_partial_models: - raise TreeError( - f"{err_msg}" - "External regressor dictionary missing required fields for partial " - f"models defined in f_stats_partial_models: {missing_partial_models}" + if "statistic" in set(external_regressor_config[config_idx].keys()): + if ( + external_regressor_config[config_idx]["statistic"].lower() + not in statistic_key_options + ): + err_msg += ( + "statistic in external_regressor_config 1 is " + f"{external_regressor_config[config_idx]['statistic']}. " + "It must be one of the following: " + f"{statistic_key_options}\n" + ) + + if (external_regressor_config[config_idx]["statistic"].lower() != "f") and ( + "partial_models" in set(external_regressor_config[config_idx].keys()) + ): + err_msg += ( + "External regressor dictionary cannot include " + "partial_models if statistic is not F\n" + ) + + # Warn if unused fields exist + unused_keys = ( + set(external_regressor_config[config_idx].keys()) + - dict_expected_keys + - dict_optional_keys ) - - # Warn if unused fields exist - unused_keys = ( - set(external_regressor_config.keys()) - set(dict_expected_keys) - dict_optional_keys - ) - if unused_keys: - LGR.warning( - "External regressor dictionary includes fields that " - f"are not used or logged {sorted(unused_keys)}" - ) + if unused_keys: + LGR.warning( + f"External regressor dictionary {config_idx} includes fields that " + f"are not used or logged {sorted(unused_keys)}" + ) + else: + err_msg += "External regressor dictional exists, but is not a list\n" if err_msg: raise TreeError("\n" + err_msg) diff --git a/tedana/tests/test_component_selector.py b/tedana/tests/test_component_selector.py index 3281cce16..36ef349e1 100644 --- a/tedana/tests/test_component_selector.py +++ b/tedana/tests/test_component_selector.py @@ -44,8 +44,7 @@ def dicts_to_test(treechoice): "missing_key": A dict missing one of the required keys (report) "null_value": A parameter in one node improperly has a null value "external_missing_key": external_regressors_config missing a required key - "external_invalid_calc_stats": external_regressors_config calc_stats is not "F" - "external_missing_partial_model": external regress names partial F model but not regressors + "external_invalid_statistic": external_regressors_config statistic is not "F" Returns ------- @@ -62,18 +61,36 @@ def dicts_to_test(treechoice): "necessary_metrics": ["kappa", "rho"], "intermediate_classifications": ["random1"], "classification_tags": ["Random1"], - "external_regressor_config": { - "regress_ID": "Fmodel", - "info": "Info Text", - "report": "Report Text", - "detrend": True, - "calc_stats": "F", - "f_stats_partial_models": ["Motion", "CSF"], - "Motion": ["^mot_.*$"], - "CSF": ["^csf.*$"], - "task_keep": ["^signal.*$"], - "extra field": 42, - }, + "external_regressor_config": [ + { + "regress_ID": "nuisance", + "info": ( + "Fits all external nuissance regressors to " + "a single model using an F statistic" + ), + "report": ( + "External nuisance regressors that fit to " + "components using a linear model were rejected." + ), + "detrend": True, + "statistic": "F", + "regressors": ["^(?!signal).*$"], + "partial_models": {"Motion": ["^mot_.*$"], "CSF": ["^csf.*$"]}, + }, + { + "regress_ID": "task", + "info": "Fits all task regressors to a single model using an F statistic", + "report": ( + "Task regressors that fit to components using a linear model and " + "have some T2* weighting were accepted even if they would have " + "been rejected base on other criteriea." + ), + "detrend": True, + "statistic": "F", + "regressors": ["^signal.*$"], + "extra field": 42, + }, + ], "nodes": [ { "functionname": "dec_left_op_right", @@ -153,11 +170,10 @@ def dicts_to_test(treechoice): elif treechoice == "null_value": tree["nodes"][0]["parameters"]["left"] = None elif treechoice == "external_missing_key": - tree["external_regressor_config"].pop("calc_stats") - elif treechoice == "external_invalid_calc_stats": - tree["external_regressor_config"]["calc_stats"] = "corr" - elif treechoice == "external_missing_partial_model": - tree["external_regressor_config"].pop("Motion") + tree["external_regressor_config"][1].pop("statistic") + elif treechoice == "external_invalid_statistic": + # Will also trigger statistic isn't F and partial models exist + tree["external_regressor_config"][0]["statistic"] = "corr" else: raise Exception(f"{treechoice} is an invalid option for treechoice") @@ -228,11 +244,11 @@ def test_validate_tree_succeeds(): Tested on all default trees in ./tedana/resources/decision_trees Note: If there is a tree in the default trees directory that is being developed and not yet valid, it's file name should - include 'invalid' as a prefix. + begin with 'X'. """ default_tree_names = glob.glob( - os.path.join(THIS_DIR, "../resources/decision_trees/[!invalid]*.json") + os.path.join(f"{THIS_DIR}/../resources/decision_trees/", "[!X]*.json") ) for tree_name in default_tree_names: @@ -278,7 +294,7 @@ def test_validate_tree_warnings(caplog): ) in caplog.text assert (r"Node 3 includes the 'log_extra_report' parameter.") in caplog.text assert ( - "External regressor dictionary includes fields " + "External regressor dictionary 1 includes fields " r"that are not used or logged ['extra field']" ) in caplog.text @@ -328,27 +344,19 @@ def test_validate_tree_fails(): with pytest.raises( component_selector.TreeError, - match=r"External regressor dictionary missing required fields: {'calc_stats'}", + match=r"External regressor dictionary 1 is missing required fields: {'statistic'}", ): component_selector.validate_tree(dicts_to_test("external_missing_key")) with pytest.raises( component_selector.TreeError, match=( - "External regressor dictionary cannot include " - "f_stats_partial_models if calc_stats is not F" - ), - ): - component_selector.validate_tree(dicts_to_test("external_invalid_calc_stats")) - - with pytest.raises( - component_selector.TreeError, - match=( - "External regressor dictionary missing required fields for partial models " - r"defined in f_stats_partial_models: {'Motion'}" + "statistic in external_regressor_config 1 is corr. It must be one of the following: " + "{'f'}\nExternal regressor dictionary cannot include partial_models " + "if statistic is not F" ), ): - component_selector.validate_tree(dicts_to_test("external_missing_partial_model")) + component_selector.validate_tree(dicts_to_test("external_invalid_statistic")) def test_check_null_fails(): diff --git a/tedana/tests/test_external_metrics.py b/tedana/tests/test_external_metrics.py index 99ba30fb5..e1d74f915 100644 --- a/tedana/tests/test_external_metrics.py +++ b/tedana/tests/test_external_metrics.py @@ -2,6 +2,7 @@ import logging import os.path as op +import re import pandas as pd import pytest @@ -85,9 +86,9 @@ def sample_external_regressor_config(config_choice="valid"): config_choice : :obj:`str` How to keep or alter the config file Options are: "valid": Config dictionary stored in demo_minimal_external_regressors_motion_task_models - "no_task": Removes "task_keep" info from config "no_task_partial": Removes "task_keep" and everything with partial F stats "csf_in_mot": Adds "CSF" to the list of motion regressor partial models + "signal_in_mot": Adds "Signal" to the list of motion regressor partial models Returns ------- @@ -102,17 +103,15 @@ def sample_external_regressor_config(config_choice="valid"): tree = load_json(sample_fname) external_regressor_config = tree["external_regressor_config"] - if config_choice == "no_task": - external_regressor_config.pop("task_keep") - elif config_choice == "no_task_partial": - external_regressor_config.pop("task_keep") - external_regressor_config.pop("f_stats_partial_models") - external_regressor_config.pop("Motion") - external_regressor_config.pop("CSF") + if config_choice == "no_task_partial": + external_regressor_config = [external_regressor_config[0]] + external_regressor_config[0].pop("partial_models") elif config_choice == "csf_in_mot": - external_regressor_config["Motion"].append("CSF") + external_regressor_config[0]["partial_models"]["Motion"].append("^csf.*$") + elif config_choice == "signal_in_mot": + external_regressor_config[0]["partial_models"]["Motion"].append("Signal") elif config_choice == "unmatched_regex": - external_regressor_config["Motion"] = ["^translation_.*$"] + external_regressor_config[0]["partial_models"]["Motion"] = ["^translation_.*$"] elif config_choice != "valid": raise ValueError(f"config_choice is {config_choice}, which is not a listed option") @@ -192,7 +191,7 @@ def test_validate_extern_regress_succeeds(caplog): ) # The regex patterns should have been replaced with the full names of the regressors - assert set(external_regressor_config_expanded["Motion"]) == set( + assert set(external_regressor_config_expanded[0]["partial_models"]["Motion"]) == set( [ "Mot_X", "Mot_d1_Yaw", @@ -208,35 +207,32 @@ def test_validate_extern_regress_succeeds(caplog): "Mot_Roll", ] ) - assert external_regressor_config_expanded["CSF"] == ["CSF"] - assert external_regressor_config_expanded["task_keep"] == ["Signal"] + assert external_regressor_config_expanded[0]["partial_models"]["CSF"] == ["CSF"] + assert external_regressor_config_expanded[1]["regressors"] == ["Signal"] assert "WARNING" not in caplog.text # Rerunning with explicit names for the above three categories instead of regex patterns # Shouldn't change anything, but making sure it runs caplog.clear() - external_regressor_config = external.validate_extern_regress( + external_regressor_config_expanded = external.validate_extern_regress( external_regressors, external_regressor_config_expanded, n_vols ) assert "WARNING" not in caplog.text - # Removing all partial model and task_keep stuff to confirm it still runs + # Removing all partial model and task_keep stuff to confirm it still runs, but with a warning caplog.clear() external_regressor_config = sample_external_regressor_config("no_task_partial") external.validate_extern_regress(external_regressors, external_regressor_config, n_vols) - assert caplog.text == "" - - # Removing "task_keep" from config to test if warning appears - caplog.clear() - external_regressor_config = sample_external_regressor_config("no_task") - external.validate_extern_regress(external_regressors, external_regressor_config, n_vols) - assert "Regressor labels in external_regressors are not all included in F" in caplog.text + assert ( + "User-provided external_regressors include columns not used " + "in any external regressor model: ['Signal']" + ) in caplog.text # Add "CSF" to "Motion" partial model (also in "CSF" partial model) to test if warning appears caplog.clear() external_regressor_config = sample_external_regressor_config("csf_in_mot") external.validate_extern_regress(external_regressors, external_regressor_config, n_vols) - assert "External regressors used in more than one partial model" in caplog.text + assert "['CSF'] used in more than one partial regressor model for nuisance" in caplog.text def test_validate_extern_regress_fails(): @@ -255,20 +251,46 @@ def test_validate_extern_regress_fails(): # If no external regressor labels match the regex label in config external_regressor_config = sample_external_regressor_config("unmatched_regex") - with pytest.raises(external.RegressError, match="No external regressor labels matching regex"): + with pytest.raises( + external.RegressError, + match=( + re.escape( + "No external regressor labels matching regular expression '^translation_.*$' found" + ) + ), + ): + external.validate_extern_regress(external_regressors, external_regressor_config, n_vols) + + # If Signal is in a partial model, but not "regressors" for the full model + external_regressor_config = sample_external_regressor_config("signal_in_mot") + with pytest.raises( + external.RegressError, + match=( + re.escape( + "Partial models in nuisance include regressors that " + "are excluded from its full model: ['Signal']" + ) + ), + ): external.validate_extern_regress(external_regressors, external_regressor_config, n_vols) # If a regressor expected in the config is not in external_regressors # Run successfully to expand Motion labels in config and then create error - # when "Mot_Y" is in the config, but removed from external_regressros + # when "Mot_Y" is in the config, but removed from external_regressors external_regressor_config = sample_external_regressor_config() + external_regressors, n_vols = sample_external_regressors() external_regressor_config_expanded = external.validate_extern_regress( external_regressors, external_regressor_config, n_vols ) external_regressors, n_vols = sample_external_regressors("no_mot_y_column") + # The same error message will appear twice. + # One for "regressor" and once for motion partial model with pytest.raises( external.RegressError, - match="Inputed regressors in external_regressors do not include all expected", + match=re.escape( + "No external regressor matching 'Mot_Y' was found.\n" + "No external regressor matching 'Mot_Y' was found." + ), ): external.validate_extern_regress( external_regressors, external_regressor_config_expanded, n_vols @@ -331,56 +353,59 @@ def test_fit_regressors(caplog): # Contents will be valided in fit_mixing_to_regressors so just checking column labels here assert set(comptable.keys()) == { "Component", - "Fstat Full Model", - "Fstat Task Model", - "Fstat Motion Model", - "Fstat CSF Model", - "pval Full Model", - "pval Task Model", - "pval Motion Model", - "pval CSF Model", - "R2stat Full Model", - "R2stat Task Model", - "R2stat Motion Model", - "R2stat CSF Model", + "Fstat nuisance model", + "Fstat task model", + "Fstat nuisance Motion partial model", + "Fstat nuisance CSF partial model", + "pval nuisance model", + "pval task model", + "pval nuisance Motion partial model", + "pval nuisance CSF partial model", + "R2stat nuisance model", + "R2stat task model", + "R2stat nuisance Motion partial model", + "R2stat nuisance CSF partial model", } assert ( - "External regressors fit includes detrending with 1 Legendre Polynomial regressors" - in caplog.text + "External regressors fit for nuisance includes detrending " + "with 1 Legendre Polynomial regressors" in caplog.text ) caplog.clear() # Running with external_regressor_config["detrend"]=3, which results in 3 detrending regressors - external_regressor_config["detrend"] = 3 + external_regressor_config[1]["detrend"] = 3 comptable = sample_comptable(mixing.shape[1]) comptable = external.fit_regressors( comptable, external_regressors, external_regressor_config_expanded, mixing ) assert ( - "External regressors fit includes detrending with 3 Legendre Polynomial regressors" - in caplog.text + "External regressors fit for task includes detrending " + "with 3 Legendre Polynomial regressors" in caplog.text ) caplog.clear() # Running with external_regressor_config["detrend"]=0, # which results in 1 detrend regressors (demeaning) - external_regressor_config["detrend"] = 0 + external_regressor_config[0]["detrend"] = 0 comptable = sample_comptable(mixing.shape[1]) comptable = external.fit_regressors( comptable, external_regressors, external_regressor_config_expanded, mixing ) assert ( - "External regressor fitted without detrending fMRI time series. Only removing mean" - in caplog.text + "External regressor for nuisance fitted without detrending fMRI time series. " + "Only removing mean" in caplog.text ) caplog.clear() - external_regressor_config["calc_stats"] = "Corr" + external_regressor_config[1]["statistic"] = "Corr" comptable = sample_comptable(mixing.shape[1]) with pytest.raises( ValueError, - match="calc_stats for external regressors in decision tree is corr, which is not valid.", + match=( + "statistic for task external regressors in decision tree is corr, " + "which is not valid." + ), ): comptable = external.fit_regressors( comptable, external_regressors, external_regressor_config_expanded, mixing @@ -410,13 +435,14 @@ def test_fit_mixing_to_regressors(caplog): # which results in 1 detrending regressor comptable = sample_comptable(mixing.shape[1]) - comptable = external.fit_mixing_to_regressors( - comptable, - external_regressors, - external_regressor_config_expanded, - mixing, - detrend_regressors, - ) + for config_idx in range(2): + comptable = external.fit_mixing_to_regressors( + comptable, + external_regressors, + external_regressor_config_expanded[config_idx], + mixing, + detrend_regressors, + ) # Since a fixed mixing matrix is used, the values should always be consistent # Comparing just 3 rows and rounding to 6 decimal places to avoid testing failures @@ -425,18 +451,18 @@ def test_fit_mixing_to_regressors(caplog): expected_results = pd.DataFrame( columns=[ "Component", - "Fstat Full Model", - "Fstat Task Model", - "Fstat Motion Model", - "Fstat CSF Model", - "pval Full Model", - "pval Task Model", - "pval Motion Model", - "pval CSF Model", - "R2stat Full Model", - "R2stat Task Model", - "R2stat Motion Model", - "R2stat CSF Model", + "Fstat nuisance model", + "Fstat task model", + "Fstat nuisance Motion partial model", + "Fstat nuisance CSF partial model", + "pval nuisance model", + "pval task model", + "pval nuisance Motion partial model", + "pval nuisance CSF partial model", + "R2stat nuisance model", + "R2stat task model", + "R2stat nuisance Motion partial model", + "R2stat nuisance CSF partial model", ] ) expected_results.loc[0] = [ @@ -485,7 +511,11 @@ def test_fit_mixing_to_regressors(caplog): 0.024389778752502478, ] - assert output_rows_to_validate.compare(expected_results.round(decimals=6)).empty + assert ( + (output_rows_to_validate.sort_index(axis=1)) + .compare(expected_results.sort_index(axis=1).round(decimals=6)) + .empty + ) # build_fstat_regressor_models @@ -504,23 +534,18 @@ def test_build_fstat_regressor_models(caplog): detrend_regressors = sample_detrend_regressors(n_vols, dtrank=3) - # Running with f_stats_partial_models + # Running nuisance with partial_models regressor_models = external.build_fstat_regressor_models( - external_regressors, external_regressor_config_expanded, detrend_regressors + external_regressors, external_regressor_config_expanded[0], detrend_regressors ) assert regressor_models["full"].shape == (n_vols, 16) assert ( - "Regressors in full model: ['CSF', 'Mot_Pitch', 'Mot_Roll', 'Mot_X', 'Mot_Y', 'Mot_Yaw', " + "Regressors in full model for nuisance: " + "['CSF', 'Mot_Pitch', 'Mot_Roll', 'Mot_X', 'Mot_Y', 'Mot_Yaw', " "'Mot_Z', 'Mot_d1_Pitch', 'Mot_d1_Roll', 'Mot_d1_X', 'Mot_d1_Y', 'Mot_d1_Yaw', " "'Mot_d1_Z', 'baseline 0', 'baseline 1', 'baseline 2']" ) in caplog.text - assert regressor_models["task keep"].shape == (n_vols, 4) - assert ( - "Regressors in task keep model: ['Signal', 'baseline 0', 'baseline 1', 'baseline 2']" - in caplog.text - ) - assert regressor_models["no CSF"].shape == (n_vols, 15) assert ( "Regressors in partial model (everything but regressors of interest) 'no CSF': " @@ -534,19 +559,14 @@ def test_build_fstat_regressor_models(caplog): "['CSF', 'baseline 0', 'baseline 1', 'baseline 2']" in caplog.text ) - # Rerunning with no "task_keep" model (creates full model slightly differently) - # Since the "Signal" column is still in exernal regressors, it would be included - # in the full model - external_regressor_config = sample_external_regressor_config("no_task") - external_regressor_config_expanded = external.validate_extern_regress( - external_regressors, external_regressor_config, n_vols - ) + # Running task regressor + caplog.clear() regressor_models = external.build_fstat_regressor_models( - external_regressors, external_regressor_config_expanded, detrend_regressors + external_regressors, external_regressor_config_expanded[1], detrend_regressors ) - assert regressor_models["full"].shape == (n_vols, 17) + + assert regressor_models["full"].shape == (n_vols, 4) assert ( - "Regressors in full model: ['CSF', 'Mot_Pitch', 'Mot_Roll', 'Mot_X', 'Mot_Y', 'Mot_Yaw', " - "'Mot_Z', 'Mot_d1_Pitch', 'Mot_d1_Roll', 'Mot_d1_X', 'Mot_d1_Y', 'Mot_d1_Yaw', " - "'Mot_d1_Z', 'Signal', 'baseline 0', 'baseline 1', 'baseline 2']" - ) in caplog.text + "Regressors in full model for task: ['Signal', 'baseline 0', 'baseline 1', 'baseline 2']" + in caplog.text + ) diff --git a/tedana/tests/test_metrics.py b/tedana/tests/test_metrics.py index aa7f5a982..377deea02 100644 --- a/tedana/tests/test_metrics.py +++ b/tedana/tests/test_metrics.py @@ -7,7 +7,11 @@ import pytest from tedana import io, utils -from tedana.metrics import collect, dependence +from tedana.metrics import collect, dependence, external +from tedana.tests.test_external_metrics import ( + sample_external_regressor_config, + sample_external_regressors, +) from tedana.tests.utils import get_test_data_path @@ -24,7 +28,7 @@ def testdata1(): methods=["dropout", "decay"], ) data_optcom = np.mean(data_cat, axis=1) - mixing = np.random.random((data_optcom.shape[1], 50)) + mixing = np.random.random((data_optcom.shape[1], 3)) io_generator = io.OutputGenerator(ref_img) # includes adaptive_mask_cut and mixing_cut which are used for ValueError tests @@ -57,6 +61,19 @@ def test_smoke_generate_metrics(testdata1): "normalized variance explained", "d_table_score", ] + + external_regressors, _ = sample_external_regressors() + # these data have 50 volumes so cut external_regressors to 50 vols for these tests + # This is just testing execution. Accuracy of values for external regressors are + # tested in test_external_metrics + n_vols = 5 + external_regressors = external_regressors.drop(labels=range(5, 75), axis=0) + + external_regressor_config = sample_external_regressor_config() + external_regressor_config_expanded = external.validate_extern_regress( + external_regressors, external_regressor_config, n_vols + ) + comptable, _ = collect.generate_metrics( data_cat=testdata1["data_cat"], data_optcom=testdata1["data_optcom"], @@ -65,7 +82,8 @@ def test_smoke_generate_metrics(testdata1): tes=testdata1["tes"], io_generator=testdata1["generator"], label="ICA", - external_regressors=None, + external_regressors=external_regressors, + external_regressor_config=external_regressor_config_expanded, metrics=metrics, ) assert isinstance(comptable, pd.DataFrame) From 1a0521ec2b6c6eef3915f83a00bba97d7b0de5d9 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Mon, 8 Jul 2024 15:30:29 -0400 Subject: [PATCH 78/81] Finished documentation and renamed demo decision trees --- ...xternal_regressors_motion_task_models.pdf} | Bin ...xternal_regressors_motion_task_models.png} | Bin ...xternal_regressors_motion_task_models.tex} | 0 ..._demo_external_regressors_single_model.pdf | Bin 0 -> 71144 bytes ..._demo_external_regressors_single_model.png | Bin 0 -> 328929 bytes ..._demo_external_regressors_single_model.tex | 109 ++++++++++++++++++ docs/building_decision_trees.rst | 107 +++++++++-------- docs/included_decision_trees.rst | 42 +++++-- ...ternal_regressors_motion_task_models.json} | 2 +- ...emo_external_regressors_single_model.json} | 33 +++--- tedana/selection/component_selector.py | 4 +- tedana/tests/test_external_metrics.py | 6 +- tedana/tests/test_integration.py | 4 +- 13 files changed, 226 insertions(+), 81 deletions(-) rename docs/_static/{decision_tree_demo_minimal_external_regressors_motion_task_models.pdf => decision_tree_demo_external_regressors_motion_task_models.pdf} (100%) rename docs/_static/{decision_tree_demo_minimal_external_regressors_motion_task_models.png => decision_tree_demo_external_regressors_motion_task_models.png} (100%) rename docs/_static/{decision_tree_demo_minimal_external_regressors_motion_task_models.tex => decision_tree_demo_external_regressors_motion_task_models.tex} (100%) create mode 100644 docs/_static/decision_tree_demo_external_regressors_single_model.pdf create mode 100644 docs/_static/decision_tree_demo_external_regressors_single_model.png create mode 100644 docs/_static/decision_tree_demo_external_regressors_single_model.tex rename tedana/resources/decision_trees/{demo_minimal_external_regressors_motion_task_models.json => demo_external_regressors_motion_task_models.json} (99%) rename tedana/resources/decision_trees/{demo_minimal_external_regressors_single_model.json => demo_external_regressors_single_model.json} (97%) diff --git a/docs/_static/decision_tree_demo_minimal_external_regressors_motion_task_models.pdf b/docs/_static/decision_tree_demo_external_regressors_motion_task_models.pdf similarity index 100% rename from docs/_static/decision_tree_demo_minimal_external_regressors_motion_task_models.pdf rename to docs/_static/decision_tree_demo_external_regressors_motion_task_models.pdf diff --git a/docs/_static/decision_tree_demo_minimal_external_regressors_motion_task_models.png b/docs/_static/decision_tree_demo_external_regressors_motion_task_models.png similarity index 100% rename from docs/_static/decision_tree_demo_minimal_external_regressors_motion_task_models.png rename to docs/_static/decision_tree_demo_external_regressors_motion_task_models.png diff --git a/docs/_static/decision_tree_demo_minimal_external_regressors_motion_task_models.tex b/docs/_static/decision_tree_demo_external_regressors_motion_task_models.tex similarity index 100% rename from docs/_static/decision_tree_demo_minimal_external_regressors_motion_task_models.tex rename to docs/_static/decision_tree_demo_external_regressors_motion_task_models.tex diff --git a/docs/_static/decision_tree_demo_external_regressors_single_model.pdf b/docs/_static/decision_tree_demo_external_regressors_single_model.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7aa4b3aab4efd5c2a50e8910b57cb6a432b2eec6 GIT binary patch literal 71144 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 vAGKgu*?j^NscE1fz>pYK|i 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%0A`ZPqH?ZQo*#e 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%0Ax+ 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, >=stealth] +\tikzstyle{line} = [thick,-,>=stealth] +\begin{document} + +% ------------------------- tikz image (flow chart) +\begin{tikzpicture}[node distance = 2cm] + +% ------------------------- nodes ------------------------- +% ----- node: 0 +\node(0)[input,label={90:\textbf{Demo Decision Tree. Single model with external regressors}}, label={180:$node\ 0$}]{Set all components to unclassified}; +% ----- node: 1 +\node(1)[decision, below of=0,label={180:$node\ 1$}, yshift=-1cm]{$\rho$ $>$ $\kappa$}; +\node(rej1)[reject, right of=1, xshift=2cm, align=center]{Unlikely BOLD\\$\rightarrow$ Reject}; +% ----- node: 2 +\node(2)[decision, below of=1,label={180:$node\ 2$}, label={[align=center] 315: voxel counts for signif fit\\of multi-echo data\\to $T_2$ or $S_0$ decay models}, yshift=-3.0cm]{$n \, FS_0 \, > \, n \, FT_2$ \& $n \,FT_2$ $>$ 0}; +\node(rej2)[reject, right of=2, xshift=2cm, align=center]{Unlikely BOLD\\$\rightarrow$ Reject}; +% ----- node: 3 +\node(3)[process, below of=2, label={180:$node\ 3$}, label={[align=center] 315: varex: variance explained\\by each component}, yshift=-1.5cm]{Calculate median(varex) across all components}; +% ----- node: 4 +\node(4)[decision, below of=3,label={180:$node\ 4$},label={[align=center] 315:DICE overlap between $T_2$ or $S_0$\\decay models and ICA component\\peak clusters}, yshift=-1.5cm]{dice $FS_0$ $>$ dice $FT_2$ \& varex $>$ median(varex) +}; +\node(rej4)[reject, right of=4, xshift=2.5cm, align=center]{Unlikely BOLD\\$\rightarrow$ Reject}; +% ----- node: 5 +\node(5)[decision, below of=4,label={180:$node\ 5$}, label={[align=center] 315: $t-statistic$ of $FT_2$ values\\in component peak clusters vs\\peak voxels outside of clusters}, yshift=-4.0cm]{ $0 \, >$ signal-noise \& varex $>$ median(varex)}; +\node(rej5)[reject, right of=5, xshift=2.5cm, align=center]{Unlikely BOLD\\$\rightarrow$ Reject}; +% ----- node: 6 +\node(6)[process, below of=5, label={180:$node\ 6$}, label={0: Uses all components}, yshift=-2.0cm]{Calculate $\kappa$ elbow}; +% ----- node: 7 +\node(7)[process, below of=6, label={180:$node\ 7$}, label={[align=center] 0: Uses all components and subset\\of unclassified components}]{Calculate $\rho$ elbow\\(liberal method)}; +% ----- node: 7 +\node(8)[decision, below of=7,label={180:$node\ 8$}, yshift=-1.5cm]{$\kappa \geq \kappa$ elbow}; +\node(chrej8)[changeclass, below of=8, yshift=-1.5cm]{Provisional reject}; +\node(chacc8)[changeclass, right of=8, xshift=3cm, yshift=0cm]{Provisional accept}; +% ----- node: 8 +\node(9)[decision, below of=chacc8,label={170:$node\ 9$}, yshift=-1.5cm]{ $\rho$ $>$ $\rho$ elbow}; +\node(chrej9)[changeclass, below of=9, yshift=-1.5cm]{Provisional reject}; +% ----- node: 9 +\node(10)[decision, left of=chrej9,label={180:$node\ 10$},label={235: Accept even if $\rho < \rho\ elbow$},xshift=-3.5cm]{$\kappa \geq \kappa$ elbow\\$\kappa > 2\rho$ }; +\node(chacc10)[changeclass, below of=10, xshift=0cm, yshift=-1.5cm, align=center]{Provisional accept}; +% ----- node: 10 +\node(11)[decision, below of=chacc10,label={180:$node\ 11$}, xshift=0cm, yshift=-2cm]{External regressor\\nuisance model\\$p<0.05$\\$R^2>0.5$};%--check in kundu +\node(chrej11)[changeclass, below of=11, xshift=0cm, yshift=-1.5cm, align=center]{Tag: External Regressors\\Provisional reject}; +% ----- node: 11 +\node(12)[decision, below of=chrej11,label={180:$node\ 11$},label={[align=left] 335: Will accept the lowest\\variance components until\\1\% of total variance is\\accepted this way}, yshift=-1.5cm]{$if$ component variance $<0.1$};%--check in kundu +\node(acc12)[accept, right of=12, xshift=3cm, align=center]{Low variance\\$\rightarrow$ Accept}; +% ----- node: 12 +\node(13)[accept, below of=12,label={180:$node\ 12$},yshift=-1.5cm, align=center]{Likely BOLD\\Change provisional accept\\$\rightarrow$Accept}; +% ----- node: 13 +\node(14)[reject, below of=13,label={180:$node\ 13$}, yshift=0cm, align=center]{Unlikely BOLD\\Change provisional reject\\$\rightarrow$Reject}; + + +% ------------------------- connections ------------------------- +\draw[arrow](0)--(1); +\draw[arrow](1)--node[anchor=south, right=0] {no} (2); +\draw[arrow](1)--node[anchor=south] {yes} (rej1); +\draw[arrow](2)--node[anchor=south, right=0] {no} (3); +\draw[arrow](2)--node[anchor=south] {yes} (rej2); +\draw[arrow](3)--(4); +\draw[arrow](4)--node[anchor=south, right=0] {no} (5); +\draw[arrow](4)--node[anchor=south] {yes} (rej4); +\draw[arrow](5)--node[anchor=south, right=0] {no} (6); +\draw[arrow](5)--node[anchor=south] {yes} (rej5); +\draw[arrow](6)--(7); +\draw[arrow](7)--(8); +\draw[arrow](8)--node[anchor=south] {yes} (chacc8); +\draw[arrow](8)--node[anchor=south, right=0] {no} (chrej8); +\draw[arrow](chacc8)--(9); +\draw[arrow](chrej8)--(9); +\draw[arrow](9)--node[anchor=south, right=0] {yes} (chrej9); +\draw[arrow](chrej9)--(10); +\draw[arrow](10)--node[anchor=south, right=0] {yes} (chacc10); +\draw[arrow](chacc10)--(11); +\draw[arrow](11)--node[anchor=south, right=0] {yes} (chrej11); +\draw[arrow](chrej11)--(12); +\draw[arrow](12)--node[anchor=south] {yes} (acc12); +\end{tikzpicture} +\end{document} diff --git a/docs/building_decision_trees.rst b/docs/building_decision_trees.rst index 939863046..5097d6457 100644 --- a/docs/building_decision_trees.rst +++ b/docs/building_decision_trees.rst @@ -243,27 +243,51 @@ External regressor configuration ================================ ``external_regressor_config`` is an optional field. If this field is specified, then -additional metrics will be calculated that include F, R^2, and p values for the fit -of external regressors to each component time series. Users will need to specify the +additional metrics will be calculated that include F, :math:`R^2`, and p values for the fit +of external regressors to each component time series. +The p value might be useful for defining a statistically significant fit, +while :math:`R^2` can assess whether the regressors model a meaningful amount of variance. +That is, even if nuisance regressors, like head motion, significantly fits an ICA component +time series, do not reject if they only model model 5% of the total variance of that component. + +Users will need to specify the external regressors using the ``--external`` option. -This functionality can be used to integrate non-multi-echo desision criteria, +``--external`` takes a TSV file where each column has a header label and is the length +of the fMRI time series. +This functionality can be used to integrate non-multi-echo decision criteria, such as correlations to head motion, CSF, or respiration time series. These added metrics can then be used in decision tree steps just like any other metric. Two demonstration trees that apply this functionality are in `resources/decision_trees`_. -demo_minimal_external_regressors_single_model.json demonstrates the simplest applications -of external regressors and demo_minimal_external_regressors_motion_task_models.json -highlights some added functionality. Both these trees are based on minimal.json. +``demo_external_regressors_single_model.json`` demonstrates the simplest application +of external regressors and ``demo_external_regressors_motion_task_models.json`` +highlights the full range of functionality. Both these trees are based on ``minimal.json``. While these might be good decision trees to use as is, they are both called "demo" because they demonstrate what is possible, -but the utility of these specific decision trees have **not** been validated yet. - -``external_regressor_config`` must include the following sub-fields +but the utility of these specific decision trees have **not yet** been validated. + +``external_regressor_config`` is a list of dictionaries. +Each dictionary defines statistical tests to apply to a group of +regressors specified in ``--external``. +``demo_external_regressors_single_model.json`` includes a single dictionary +that specifies fitting all supplied regressors to a single nuisance model that is +used to reject components. +``demo_external_regressors_motion_task_models`` includes one dictionary for +a nuisance model to reject components and one for a task model to retain more +task-correlated signal. For the nuisance model, columns with specific header names +are also fit to partial models for motion and CSF regressors that are used to label +why components were rejected. +(i.e. Component X was rejected because it fit to head motion regressors.) +Each dictionary in ``external_regressor_config`` must include the following sub-fields: - "regress_ID" A descriptive name for the external regressors that will be logged. + Will be used in the ``selector.component_table_`` labels describing the outputs of the + statistical tests. + For example, if this field is ``nuisance`` then component table column labels will include: + ``Fstat nuisance model``, ``R2stat nuisance model``, and ``pval nuisance model``. - "info" - A brief description of the external regressors for info logging + A brief description of the external regressors for info logging. - "report" A narrative description of how the external regressors are used @@ -277,50 +301,35 @@ but the utility of these specific decision trees have **not** been validated yet If "true" it will specify the number of detrending time regressors to include based on the length of the time series. If "false" it will just include an intercept regressor to remove the mean. - This can also be a integer that defines the number of regressors to include + This can also be a integer that defines the number of regressors to include. + Can also be an integer specifying the number of detrending regressors. -- "calc_stats" +- "statistic" The statistical test to use for fitting the external regressors to the ICA component time series. Currently, the only valid option is "F" for fitting using a linear model with an F statistic. -With just the above fields, all regressors in the user provided file will be -fit to the ICA times series data and columns will be added to -``selector.component_table_`` that are called ``Fstat Full Model``, -``pval Full Model``, and ``R2stat Full Model``. The p value might be useful for -defining a statistically significant fit while R2stat might be useful for assessing -whether the regressors defined a meaningful amount of variance. That is, even if -head motion regressors significant correlated to an ICA component, if they only -model 1% of the total variance of that component, it might be detrimental to -reject that comopnent. - -There are several optional fields for ``external_regressor_config`` that require specifying -external regressors by name. - -- "f_stats_partial_models" - In addition to calculated an F statistic across all regressors, it is possible to define - partial F statistics. For example, if field is ``["Motion", "CSF"]``, then there will be - additional metrics called ``Fstat Motion Model``, ``pval Motion Model``, ``R2stat Motion Model``, - ``Fstat CSF Model``, ``pval CSF Model``, and ``R2stat CSF Model``. These fields will say whether - subsets of the external regressors significantly fit the data. This the demo decision tree, - this isn't used to decide whether to accept or reject any components, but it is used to add - informational tags. That is, a component that is rejected due to a fit to external regressors - can be tagged with "Fits motion external regressors". This specific field is a list of strings. - Each string needs to be the name of another field. So, if "Motion" included, there needs to be - another field called "Motion". That field will include a list of column names of - external regressors that should be part of that label. Regular expressions are also allowed - so that ``"Motion": ["^mot_.*$"]`` means that any external regressor column label that begins - with "mot_" is considered part of "Motion". Capitalization is ignored. - -- "task_keep" - This is a list of regressor names or regular expressions that model the task design. - Any regressor defined in "task_keep" will **not** be part of the Full F statistic model. - These will be separate fit to the component time series to generate metrics labelled - ``Fstat Task Model``, ``pval Task Model``, and ``R2stat Task Model``. - These can be used to accept metrics that would otherwise be rejected. For example, - in demo_minimal_external_regressors_motion_task_models.json, if a component fits - the task design and has substantical T2* signal (kappa>kappa elbow), accept the component - even if other metrics show the component also contains noise. +- "regressors" + A list of strings or regular expressions to specify the columns in + the external regressor file to use in the model. Regular expressions begin with ``^`` + For example, ``["^.*$"]`` would mean use all regressors in the file, + while ``["^mot_.*$"]`` would mean use all regressors with labels beginging with ``mot_``. + ``["mot_x", "mot_y_", "mot_z"]`` would be use regressors with thos specific labels. + Capitalization is ignored. + Note: When tedana is run, regular expressions are replaced with the named regressors. + The outputted decision tree will specify what was used and might be useful for validation. + +An optional field is **"partial_models"**. +This is a dictionary where each element is a descriptor and columns specification similar to +``regressors``. For example, ``"partial_models": {"Motion": ["^mot_.*$"], "CSF": ["^csf.*$"]}`` +specified two partial motions for motion and CSF time series where the columns in +the external regressor tsv begin with either ``mot_`` or ``csf``. +When this field is used, statistics will be calculated for the full model with all regressors +and each specified partial model. This can be used to potentially reject components that fit +any combination of nuisance regressors and also note which components fit head motion regressors. +If this option is included, there would be added columns in ``selector.component_table_`` such as +``Fstat nuisance Motion partial model``, ``pval nuisance Motion partial model``, and +``R2stat nuisance Motion partial model`` Nodes in the decision tree diff --git a/docs/included_decision_trees.rst b/docs/included_decision_trees.rst index 42a2fb02b..607af8e87 100644 --- a/docs/included_decision_trees.rst +++ b/docs/included_decision_trees.rst @@ -22,9 +22,9 @@ With the addition of options to fit external regressors to components, there are two demonstration decision trees that implement this new functionality. While these might work well, since they have not yet been validated on data, they are labeled ``demo``. -``decision_tree_demo_minimal_external_regressors_single_model`` +``decision_tree_demo_external_regressors_single_model`` demonstrates fitting all nuissance regressors to a single model. -``decision_tree_demo_minimal_external_regressors_motion_task_models`` +``decision_tree_demo_external_regressors_motion_task_models`` demonstrates fitting nuissance regressors to a model, partial tests and tagging for components that fit Motion or CSF regressors, and retention of some components that fit task regressors. @@ -135,17 +135,43 @@ is rejected (node 13) .. _LaTeX file to generate the minimal decision tree flow chart: _static/decision_tree_minimal.tex ********************************************* -Demo minimal external regressors single model +Demo external regressors single model ********************************************* -To be added +This tree is similar to the minimal tree except there is an added node (node 11) +where components are rejected if they significant fit a model of external nuisance regressor +time series and the fit models a substantial amount of the total variance. +Unlike the minimal tree, components that would be accepted based on :math:`\kappa` & :math:`\rho` +criteria can be rejected based on a fit to external regressors. +This is called a "demo" tree because it is demonstrating how fits to external regressors can +be used. It might be a good decision tree to use, +but results have not yet been tested and validated. + +.. image:: _static/decision_tree_demo_external_regressors_single_model.png + :width: 400 + :alt: External Decision Tree With Motion and Task Models Flow Chart **************************************************** -Demo minimal external regressors, motion task models +Demo external regressors, motion task models **************************************************** -Add text - -.. image:: _static/decision_tree_demo_minimal_external_regressors_motion_task_models.png +This is based on the minimal tree, but multiple nodes were added to demonstrate how to use +external regressors for fits. +Unlike the minimal tree, components that would be accepted based on :math:`\kappa` & :math:`\rho` +criteria can be rejected based on a fit to external regressors. +Components are rejected if they significant fit a model of external nuisance regressor +time series and the fit models a substantial amount of the total variance (node 10). +For rejected components, if they also fit a partial model of motion external regressors (node 11), +or CSF external regressors (node 12), the outputs are also tagged to say they fit those groups +of regressors. +Additionally, if a rejected component fits the task design and has +:math:`\kappa` > :math:`\kappa` elbow, then it is accepted under the conservative assumption to +retain task fitting components with some :math:`T_2^*`` signal even if those components also +contain potentially rejection-worthy noise (node 13). +This is called a "demo" tree because it is demonstrating how fits to external regressors can +be used. It might be a good decision tree to use, +but results have not yet been tested and validated. + +.. image:: _static/decision_tree_demo_external_regressors_motion_task_models.png :width: 400 :alt: External Decision Tree With Motion and Task Models Flow Chart diff --git a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json b/tedana/resources/decision_trees/demo_external_regressors_motion_task_models.json similarity index 99% rename from tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json rename to tedana/resources/decision_trees/demo_external_regressors_motion_task_models.json index 9619fbb51..6d271dd68 100644 --- a/tedana/resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json +++ b/tedana/resources/decision_trees/demo_external_regressors_motion_task_models.json @@ -1,5 +1,5 @@ { - "tree_id": "demo_minimal_external_regressors_motion_task_models", + "tree_id": "demo_external_regressors_motion_task_models", "info": "Demonstration based on the minimal decision tree that uses partial F stats on a model with multiple external regressors divided by category and task regressors to bias towards keeping.", "report": "This is based on the minimal criteria of the original MEICA decision tree \\citep{kundu2013integrated} without the more aggressive noise removal steps \\citep{dupre2021te}.", "necessary_metrics": [ diff --git a/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json b/tedana/resources/decision_trees/demo_external_regressors_single_model.json similarity index 97% rename from tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json rename to tedana/resources/decision_trees/demo_external_regressors_single_model.json index 1b7e1f0f0..fb468a7f8 100644 --- a/tedana/resources/decision_trees/demo_minimal_external_regressors_single_model.json +++ b/tedana/resources/decision_trees/demo_external_regressors_single_model.json @@ -1,5 +1,5 @@ { - "tree_id": "demo_minimal_external_regressors_single_model", + "tree_id": "demo_external_regressors_single_model", "info": "Demonstration based on the minimal decision tree that uses an F stat on a model with multiple external regressors.", "report": "This is based on the minimal criteria of the original MEICA decision tree \\citep{kundu2013integrated} without the more aggressive noise removal steps \\citep{dupre2021te}.", "necessary_metrics": [ @@ -141,7 +141,7 @@ "parameters": { "if_true": "provisionalaccept", "if_false": "nochange", - "decide_comps": "provisionalaccept", + "decide_comps": "provisionalreject", "op": ">", "left": "kappa", "right": "rho" @@ -155,20 +155,6 @@ "tag_if_true": "Likely BOLD" } }, - { - "functionname": "dec_variance_lessthan_thresholds", - "parameters": { - "if_true": "provisionalaccept", - "if_false": "nochange", - "decide_comps": "provisionalreject" - }, - "kwargs": { - "var_metric": "variance explained", - "single_comp_threshold": 0.1, - "all_comp_threshold": 1.0, - "tag_if_true": "Low variance" - } - }, { "functionname": "dec_left_op_right", "parameters": { @@ -187,6 +173,21 @@ "tag_if_true": "External regressors" } }, + { + "functionname": "dec_variance_lessthan_thresholds", + "parameters": { + "if_true": "accepted", + "if_false": "nochange", + "decide_comps": "provisionalreject" + }, + "kwargs": { + "var_metric": "variance explained", + "single_comp_threshold": 0.1, + "all_comp_threshold": 1.0, + "tag_if_true": "Low variance" + } + }, + { "functionname": "manual_classify", "parameters": {"new_classification": "accepted", "decide_comps": "provisionalaccept"}, diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index 1a0b1ef50..e54845546 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -32,8 +32,8 @@ "minimal", "meica", "tedana_orig", - "demo_minimal_external_regressors_single_model", - "demo_minimal_external_regressors_motion_task_models", + "demo_external_regressors_single_model", + "demo_external_regressors_motion_task_models", ] diff --git a/tedana/tests/test_external_metrics.py b/tedana/tests/test_external_metrics.py index e1d74f915..848b281ae 100644 --- a/tedana/tests/test_external_metrics.py +++ b/tedana/tests/test_external_metrics.py @@ -28,7 +28,7 @@ def sample_external_regressors(regress_choice="valid"): ---------- regress_choice : :obj:`str` How to keep or alter the external regressor data Options are: - "valid": Column labels expected in demo_minimal_external_regressors_motion_task_models + "valid": Column labels expected in demo_external_regressors_motion_task_models The labels in the config file are lowercase and this file is capitalized, but it should still be valid. "no_mot_y_column": The column labeled "Mot_Pitch" is removed. @@ -85,7 +85,7 @@ def sample_external_regressor_config(config_choice="valid"): ---------- config_choice : :obj:`str` How to keep or alter the config file Options are: - "valid": Config dictionary stored in demo_minimal_external_regressors_motion_task_models + "valid": Config dictionary stored in demo_external_regressors_motion_task_models "no_task_partial": Removes "task_keep" and everything with partial F stats "csf_in_mot": Adds "CSF" to the list of motion regressor partial models "signal_in_mot": Adds "Signal" to the list of motion regressor partial models @@ -98,7 +98,7 @@ def sample_external_regressor_config(config_choice="valid"): sample_fname = op.join( THIS_DIR, "../resources/decision_trees", - "demo_minimal_external_regressors_motion_task_models.json", + "demo_external_regressors_motion_task_models.json", ) tree = load_json(sample_fname) external_regressor_config = tree["external_regressor_config"] diff --git a/tedana/tests/test_integration.py b/tedana/tests/test_integration.py index 792ab8013..142efc175 100644 --- a/tedana/tests/test_integration.py +++ b/tedana/tests/test_integration.py @@ -276,7 +276,7 @@ def test_integration_three_echo_external_regressors_single_model(skip_integratio # Note that the above is in comparision to the minimal decision tree # but the integration test for 3 echoes uses the kundu tree download_test_data(osf_id, test_data_path) - tree_name = "resources/decision_trees/demo_minimal_external_regressors_single_model.json" + tree_name = "resources/decision_trees/demo_external_regressors_single_model.json" tedana_cli.tedana_workflow( data=f"{test_data_path}/three_echo_Cornell_zcat.nii.gz", tes=[14.5, 38.5, 62.5], @@ -313,7 +313,7 @@ def test_integration_three_echo_external_regressors_motion_task_models(skip_inte # The regressor values and expected fits with the data are detailed in: # tests.test_external_metrics.sample_external_regressors download_test_data(osf_id, test_data_path) - tree_name = "resources/decision_trees/demo_minimal_external_regressors_motion_task_models.json" + tree_name = "resources/decision_trees/demo_external_regressors_motion_task_models.json" tedana_cli.tedana_workflow( data=f"{test_data_path}/three_echo_Cornell_zcat.nii.gz", tes=[14.5, 38.5, 62.5], From be89f1e80416e0a09f9d0d2ae8ff2b6743c06b54 Mon Sep 17 00:00:00 2001 From: handwerkerd Date: Tue, 23 Jul 2024 10:31:05 -0400 Subject: [PATCH 79/81] added link to example external regressors tsv file --- docs/building_decision_trees.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/building_decision_trees.rst b/docs/building_decision_trees.rst index 5097d6457..1d9155c3f 100644 --- a/docs/building_decision_trees.rst +++ b/docs/building_decision_trees.rst @@ -186,7 +186,7 @@ All functions are currently in :mod:`~tedana.selection.selection_nodes`. to interpret. .. _resources/decision_trees: https://github.com/ME-ICA/tedana/tree/main/tedana/resources/decision_trees - +.. _This is an example TSV file: https://github.com/ME-ICA/tedana/tree/main/tedana/tests/data/external_regress_Ftest_3echo.tsv General information fields ========================== @@ -260,7 +260,9 @@ These added metrics can then be used in decision tree steps just like any other Two demonstration trees that apply this functionality are in `resources/decision_trees`_. ``demo_external_regressors_single_model.json`` demonstrates the simplest application of external regressors and ``demo_external_regressors_motion_task_models.json`` -highlights the full range of functionality. Both these trees are based on ``minimal.json``. +highlights the full range of functionality. +`This is an example TSV file`_ with column labels that work with both of those trees. +Both these trees are based on ``minimal.json``. While these might be good decision trees to use as is, they are both called "demo" because they demonstrate what is possible, but the utility of these specific decision trees have **not yet** been validated. From 5334875383f3ebb99b656904ba08aedfdd6d5fa6 Mon Sep 17 00:00:00 2001 From: Dan Handwerker <7406227+handwerkerd@users.noreply.github.com> Date: Tue, 30 Jul 2024 15:03:12 -0400 Subject: [PATCH 80/81] Apply suggestions from code review Fixed nuissance typos Co-authored-by: Taylor Salo --- ...ision_tree_demo_external_regressors_motion_task_models.tex | 2 +- docs/included_decision_trees.rst | 4 ++-- .../demo_external_regressors_motion_task_models.json | 2 +- .../decision_trees/demo_external_regressors_single_model.json | 2 +- tedana/tests/test_component_selector.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/_static/decision_tree_demo_external_regressors_motion_task_models.tex b/docs/_static/decision_tree_demo_external_regressors_motion_task_models.tex index 364366df2..2bb2afaf4 100644 --- a/docs/_static/decision_tree_demo_external_regressors_motion_task_models.tex +++ b/docs/_static/decision_tree_demo_external_regressors_motion_task_models.tex @@ -68,7 +68,7 @@ \node(chrej9)[changeclass, below of=9, xshift=0cm, yshift=-2cm]{Provisional reject}; \node(chacc9)[changeclass, right of=9, xshift=3cm, yshift=0cm]{Provisional accept}; % ----- node: 10 -\node(10)[decision, below of=chacc9,label={150:$node\ 10$},label={[align=center] 310: Reject if\\fits external\\nuissance\\regressors},yshift=-2cm]{F test for\\Nuisance Regressors\\$p_{Full} \leq 0.05$\\$R^2_{Full} \geq 0.5$}; +\node(10)[decision, below of=chacc9,label={150:$node\ 10$},label={[align=center] 310: Reject if\\fits external\\nuisance\\regressors},yshift=-2cm]{F test for\\Nuisance Regressors\\$p_{Full} \leq 0.05$\\$R^2_{Full} \geq 0.5$}; \node(chrej10)[changeclass, below of=10, xshift=0cm, yshift=-2cm, align=center]{External regressors\\$\rightarrow$Provisional reject}; % ----- node: 11 \node(11)[decision, left of=chrej10,label={180:$node\ 11$},xshift=-3cm]{Partial F test for\\Motion Regressors\\$p_{Full} \leq 0.05$\\$R^2_{Full} \geq 0.5$\\$p_{Motion} \leq 0.05$}; diff --git a/docs/included_decision_trees.rst b/docs/included_decision_trees.rst index 607af8e87..837769892 100644 --- a/docs/included_decision_trees.rst +++ b/docs/included_decision_trees.rst @@ -23,9 +23,9 @@ there are two demonstration decision trees that implement this new functionality While these might work well, since they have not yet been validated on data, they are labeled ``demo``. ``decision_tree_demo_external_regressors_single_model`` -demonstrates fitting all nuissance regressors to a single model. +demonstrates fitting all nuisance regressors to a single model. ``decision_tree_demo_external_regressors_motion_task_models`` -demonstrates fitting nuissance regressors to a model, +demonstrates fitting nuisance regressors to a model, partial tests and tagging for components that fit Motion or CSF regressors, and retention of some components that fit task regressors. diff --git a/tedana/resources/decision_trees/demo_external_regressors_motion_task_models.json b/tedana/resources/decision_trees/demo_external_regressors_motion_task_models.json index 6d271dd68..87b548961 100644 --- a/tedana/resources/decision_trees/demo_external_regressors_motion_task_models.json +++ b/tedana/resources/decision_trees/demo_external_regressors_motion_task_models.json @@ -31,7 +31,7 @@ "external_regressor_config": [ { "regress_ID": "nuisance", - "info": "Fits all external nuissance regressors to a single model using an F statistic", + "info": "Fits all external nuisance regressors to a single model using an F statistic", "report": "External nuisance regressors that fit to components using a linear model were rejected.", "detrend": true, "statistic": "F", diff --git a/tedana/resources/decision_trees/demo_external_regressors_single_model.json b/tedana/resources/decision_trees/demo_external_regressors_single_model.json index fb468a7f8..1900ca2e5 100644 --- a/tedana/resources/decision_trees/demo_external_regressors_single_model.json +++ b/tedana/resources/decision_trees/demo_external_regressors_single_model.json @@ -19,7 +19,7 @@ "external_regressor_config": [ { "regress_ID": "nuisance", - "info": "Fits all external nuissance regressors to a single model using an F statistic", + "info": "Fits all external nuisance regressors to a single model using an F statistic", "report": "External nuisance regressors that fit to components using a linear model were rejected.", "detrend": true, "statistic": "F", diff --git a/tedana/tests/test_component_selector.py b/tedana/tests/test_component_selector.py index 36ef349e1..f536339e1 100644 --- a/tedana/tests/test_component_selector.py +++ b/tedana/tests/test_component_selector.py @@ -65,7 +65,7 @@ def dicts_to_test(treechoice): { "regress_ID": "nuisance", "info": ( - "Fits all external nuissance regressors to " + "Fits all external nuisance regressors to " "a single model using an F statistic" ), "report": ( From d6d3ccc302a2b6d2bd9f6edf7393323624dc9fc5 Mon Sep 17 00:00:00 2001 From: Neha Reddy Date: Fri, 2 Aug 2024 11:40:00 -0500 Subject: [PATCH 81/81] Minor documentation edits --- docs/building_decision_trees.rst | 20 ++++++++++---------- docs/included_decision_trees.rst | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/building_decision_trees.rst b/docs/building_decision_trees.rst index 1d9155c3f..1c31b6c2d 100644 --- a/docs/building_decision_trees.rst +++ b/docs/building_decision_trees.rst @@ -247,12 +247,12 @@ additional metrics will be calculated that include F, :math:`R^2`, and p values of external regressors to each component time series. The p value might be useful for defining a statistically significant fit, while :math:`R^2` can assess whether the regressors model a meaningful amount of variance. -That is, even if nuisance regressors, like head motion, significantly fits an ICA component -time series, do not reject if they only model model 5% of the total variance of that component. +For example, even if nuisance regressors, like head motion, significantly fit an ICA component +time series, do not reject if they only model 5% of the total variance of that component. Users will need to specify the external regressors using the ``--external`` option. -``--external`` takes a TSV file where each column has a header label and is the length +``--external`` takes a TSV file in which each column has a header label and is the length of the fMRI time series. This functionality can be used to integrate non-multi-echo decision criteria, such as correlations to head motion, CSF, or respiration time series. @@ -261,7 +261,7 @@ Two demonstration trees that apply this functionality are in `resources/decision ``demo_external_regressors_single_model.json`` demonstrates the simplest application of external regressors and ``demo_external_regressors_motion_task_models.json`` highlights the full range of functionality. -`This is an example TSV file`_ with column labels that work with both of those trees. +`This is an example TSV file`_ with column labels that work with both of these trees. Both these trees are based on ``minimal.json``. While these might be good decision trees to use as is, they are both called "demo" because they demonstrate what is possible, @@ -300,9 +300,9 @@ Each dictionary in ``external_regressor_config`` must include the following sub- - "detrend" "true" or "false" to specify whether to include detrending regressors when fitting the external regressors to the ICA component time series. - If "true" it will specify the number of detrending time regressors to + If "true", it will specify the number of detrending time regressors to include based on the length of the time series. - If "false" it will just include an intercept regressor to remove the mean. + If "false", it will just include an intercept regressor to remove the mean. This can also be a integer that defines the number of regressors to include. Can also be an integer specifying the number of detrending regressors. @@ -316,20 +316,20 @@ Each dictionary in ``external_regressor_config`` must include the following sub- the external regressor file to use in the model. Regular expressions begin with ``^`` For example, ``["^.*$"]`` would mean use all regressors in the file, while ``["^mot_.*$"]`` would mean use all regressors with labels beginging with ``mot_``. - ``["mot_x", "mot_y_", "mot_z"]`` would be use regressors with thos specific labels. + ``["mot_x", "mot_y_", "mot_z"]`` would be use regressors with those specific labels. Capitalization is ignored. Note: When tedana is run, regular expressions are replaced with the named regressors. The outputted decision tree will specify what was used and might be useful for validation. An optional field is **"partial_models"**. -This is a dictionary where each element is a descriptor and columns specification similar to +This is a dictionary where each element is a descriptor and column specification is similar to ``regressors``. For example, ``"partial_models": {"Motion": ["^mot_.*$"], "CSF": ["^csf.*$"]}`` -specified two partial motions for motion and CSF time series where the columns in +specifies two partial models for motion and CSF time series, where the columns in the external regressor tsv begin with either ``mot_`` or ``csf``. When this field is used, statistics will be calculated for the full model with all regressors and each specified partial model. This can be used to potentially reject components that fit any combination of nuisance regressors and also note which components fit head motion regressors. -If this option is included, there would be added columns in ``selector.component_table_`` such as +If this option is included, there would be added columns in ``selector.component_table_``, such as ``Fstat nuisance Motion partial model``, ``pval nuisance Motion partial model``, and ``R2stat nuisance Motion partial model`` diff --git a/docs/included_decision_trees.rst b/docs/included_decision_trees.rst index 837769892..af1d931ae 100644 --- a/docs/included_decision_trees.rst +++ b/docs/included_decision_trees.rst @@ -139,7 +139,7 @@ Demo external regressors single model ********************************************* This tree is similar to the minimal tree except there is an added node (node 11) -where components are rejected if they significant fit a model of external nuisance regressor +where components are rejected if they significantly fit a model of external nuisance regressor time series and the fit models a substantial amount of the total variance. Unlike the minimal tree, components that would be accepted based on :math:`\kappa` & :math:`\rho` criteria can be rejected based on a fit to external regressors. @@ -159,7 +159,7 @@ This is based on the minimal tree, but multiple nodes were added to demonstrate external regressors for fits. Unlike the minimal tree, components that would be accepted based on :math:`\kappa` & :math:`\rho` criteria can be rejected based on a fit to external regressors. -Components are rejected if they significant fit a model of external nuisance regressor +Components are rejected if they significantly fit a model of external nuisance regressor time series and the fit models a substantial amount of the total variance (node 10). For rejected components, if they also fit a partial model of motion external regressors (node 11), or CSF external regressors (node 12), the outputs are also tagged to say they fit those groups