From f38e1ae4f9b0f215a040875b745595a7ba13eda7 Mon Sep 17 00:00:00 2001 From: James Kent Date: Tue, 9 Jan 2024 15:35:28 -0600 Subject: [PATCH] alternative way to sum across studies in a general way --- nimare/meta/cbma/base.py | 4 ++-- nimare/meta/cbma/mkda.py | 8 +++++--- nimare/meta/kernel.py | 40 +++++++++++++++++++++++++++------------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/nimare/meta/cbma/base.py b/nimare/meta/cbma/base.py index 96cde25b1..a6bd13913 100644 --- a/nimare/meta/cbma/base.py +++ b/nimare/meta/cbma/base.py @@ -205,7 +205,7 @@ def _compute_weights(self, ma_values): """ return None - def _collect_ma_maps(self, coords_key="coordinates", maps_key="ma_maps"): + def _collect_ma_maps(self, coords_key="coordinates", maps_key="ma_maps", return_type="sparse"): """Collect modeled activation maps from Estimator inputs. Parameters @@ -231,7 +231,7 @@ def _collect_ma_maps(self, coords_key="coordinates", maps_key="ma_maps"): ma_maps = self.kernel_transformer.transform( self.inputs_[coords_key], masker=self.masker, - return_type="sparse", + return_type=return_type, ) return ma_maps diff --git a/nimare/meta/cbma/mkda.py b/nimare/meta/cbma/mkda.py index 7272115db..e3d6b42bd 100644 --- a/nimare/meta/cbma/mkda.py +++ b/nimare/meta/cbma/mkda.py @@ -388,7 +388,7 @@ class MKDAChi2(PairwiseCBMAEstimator): def __init__( self, - kernel_transformer=MKDAKernel(sum_across_studies=True), + kernel_transformer=MKDAKernel(), prior=0.5, memory=Memory(location=None, verbose=0), memory_level=0, @@ -441,6 +441,7 @@ def _fit(self, dataset1, dataset2): n_selected_active_voxels = self._collect_ma_maps( maps_key="ma_maps1", coords_key="coordinates1", + return_type="summary_array", ) n_selected = self.dataset1.coordinates["id"].unique().shape[0] @@ -449,6 +450,7 @@ def _fit(self, dataset1, dataset2): n_unselected_active_voxels = self._collect_ma_maps( maps_key="ma_maps2", coords_key="coordinates2", + return_type="summary_array", ) n_unselected = self.dataset2.coordinates["id"].unique().shape[0] @@ -572,14 +574,14 @@ def _run_fwe_permutation(self, iter_xyz1, iter_xyz2, iter_df1, iter_df2, conn, v # Generate MA maps and calculate count variables for first dataset n_selected_active_voxels = self.kernel_transformer.transform( - iter_df1, self.masker, return_type="sparse" + iter_df1, self.masker, return_type="summary_array" ) n_selected = self.dataset1.coordinates["id"].unique().shape[0] # Generate MA maps and calculate count variables for second dataset n_unselected_active_voxels = self.kernel_transformer.transform( - iter_df2, self.masker, return_type="sparse" + iter_df2, self.masker, return_type="summary_array" ) n_unselected = self.dataset2.coordinates["id"].unique().shape[0] diff --git a/nimare/meta/kernel.py b/nimare/meta/kernel.py index 2b520b005..75ecbdc3a 100644 --- a/nimare/meta/kernel.py +++ b/nimare/meta/kernel.py @@ -96,7 +96,7 @@ def transform(self, dataset, masker=None, return_type="image"): Mask to apply to MA maps. Required if ``dataset`` is a DataFrame. If None (and ``dataset`` is a Dataset), the Dataset's masker attribute will be used. Default is None. - return_type : {'sparse', 'array', 'image'}, optional + return_type : {'sparse', 'array', 'image', 'summary_array'}, optional Whether to return a sparse matrix ('sparse'), a numpy array ('array'), or a list of niimgs ('image'). Default is 'image'. @@ -110,6 +110,8 @@ def transform(self, dataset, masker=None, return_type="image"): equal to `shape` of the images. If return_type is 'array', a 2D numpy array (C x V), where C is contrast and V is voxel. + If return_type is 'summary_array', a 1D numpy array (V,) containing + a summary measure for each voxel that has been combined across experiments. If return_type is 'image', a list of modeled activation images (one for each of the Contrasts in the input dataset). @@ -121,8 +123,10 @@ def transform(self, dataset, masker=None, return_type="image"): Name of the corresponding column in the Dataset.images DataFrame. If :meth:`_infer_names` is executed. """ - if return_type not in ("sparse", "array", "image"): - raise ValueError('Argument "return_type" must be "image", "array", or "sparse".') + if return_type not in ("sparse", "array", "image", "summary_array"): + raise ValueError( + 'Argument "return_type" must be "image", "array", "summary_array", "sparse".' + ) if isinstance(dataset, pd.DataFrame): assert ( @@ -169,9 +173,14 @@ def transform(self, dataset, masker=None, return_type="image"): mask_data = mask.get_fdata().astype(dtype) # Generate the MA maps - transformed_maps = self._cache(self._transform, func_memory_level=2)(mask, coordinates) + if return_type == "summary_array" or return_type == "sparse": + args = (mask, coordinates, return_type) + else: + args = (mask, coordinates) - if return_type == "sparse": + transformed_maps = self._cache(self._transform, func_memory_level=2)(*args) + + if return_type == "sparse" or return_type == "summary_array": return transformed_maps[0] imgs = [] @@ -344,10 +353,6 @@ def _generate_description(self): class KDAKernel(KernelTransformer): """Generate KDA modeled activation images from coordinates. - .. versionchanged:: 0.2.1 - - - Add new parameter ``sum_across_studies`` to sum across studies in KDA. - .. versionchanged:: 0.0.13 - Add new parameter ``memory`` to cache modeled activation (MA) maps. @@ -382,16 +387,25 @@ def __init__( value=1, memory=Memory(location=None, verbose=0), memory_level=0, - sum_across_studies=False, ): self.r = float(r) self.value = value - self.sum_across_studies = sum_across_studies super().__init__(memory=memory, memory_level=memory_level) - def _transform(self, mask, coordinates): + def _transform(self, mask, coordinates, return_type="sparse"): + """return type can either be sparse or summary_array""" + ijks = coordinates[["i", "j", "k"]].values exp_idx = coordinates["id"].values + if return_type == "sparse": + sum_across_studies = False + elif return_type == "summary_array": + sum_across_studies = True + else: + raise ValueError( + 'Argument "return_type" must be "sparse" or "summary_array".' + ) + transformed = compute_kda_ma( mask, ijks, @@ -399,7 +413,7 @@ def _transform(self, mask, coordinates): self.value, exp_idx, sum_overlap=self._sum_overlap, - sum_across_studies=self.sum_across_studies, + sum_across_studies=sum_across_studies, ) exp_ids = np.unique(exp_idx) return transformed, exp_ids