From 04b7dbb09906e30acccc598445facbe3f0045ea4 Mon Sep 17 00:00:00 2001 From: JulioAPeraza Date: Thu, 9 Jun 2022 18:38:30 -0400 Subject: [PATCH 1/5] Check input array shapes when Dataset is initialized --- pymare/core.py | 6 +++++- pymare/utils.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/pymare/core.py b/pymare/core.py index 74c5b48..7fc7b60 100644 --- a/pymare/core.py +++ b/pymare/core.py @@ -5,7 +5,7 @@ import numpy as np import pandas as pd -from pymare.utils import _listify +from pymare.utils import _check_inputs_shape, _listify from .estimators import ( DerSimonianLaird, @@ -94,6 +94,10 @@ def __init__( self.X = X self.X_names = names + _check_inputs_shape(self.y, self.X, "y", "X", row=True) + _check_inputs_shape(self.y, self.v, "y", "v", row=True, column=True) + _check_inputs_shape(self.y, self.n, "y", "n", row=True, column=True) + def _get_predictors(self, X, names, add_intercept): if X is None and not add_intercept: raise ValueError( diff --git a/pymare/utils.py b/pymare/utils.py index 1e11cb7..b159a48 100644 --- a/pymare/utils.py +++ b/pymare/utils.py @@ -19,3 +19,39 @@ def _listify(obj): This provides a simple way to accept flexible arguments. """ return obj if isinstance(obj, (list, tuple, type(None), np.ndarray)) else [obj] + + +def _check_inputs_shape(param1, param2, param1_name, param2_name, row=False, column=False): + """Check whether 'param1' and 'param2' have the same shape. + + Parameters + ---------- + param1 : array + param2 : array + param1_name : str + param2_name : str + row : bool, default to False. + column : bool, default to False. + """ + if (param1 is not None) and (param2 is not None): + if row and not column: + shape1 = param1.shape[0] + shape2 = param2.shape[0] + message = "rows" + elif column and not row: + shape1 = param1.shape[1] + shape2 = param2.shape[1] + message = "columns" + elif row and column: + shape1 = param1.shape + shape2 = param2.shape + message = "rows and columns" + else: + raise ValueError("At least one of the two parameters (row or column) should be True.") + + if shape1 != shape2: + raise ValueError( + f"{param1_name} and {param2_name} should have the same number of {message}. " + f"You provided {param1_name} with shape {param1.shape} and {param2_name} " + f"with shape {param2.shape}." + ) From d3f9e9b27875ad4eda59e6e8a21f8e94431d1d0c Mon Sep 17 00:00:00 2001 From: JulioAPeraza Date: Thu, 9 Jun 2022 21:37:11 -0400 Subject: [PATCH 2/5] Add test for nimare.utils._check_inputs_shape --- pymare/tests/test_utils.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pymare/tests/test_utils.py b/pymare/tests/test_utils.py index 43a81bf..5e6bb97 100644 --- a/pymare/tests/test_utils.py +++ b/pymare/tests/test_utils.py @@ -1,6 +1,8 @@ """Tests for pymare.utils.""" import os.path as op +import numpy as np + from pymare import utils @@ -8,3 +10,20 @@ def test_get_resource_path(): """Test nimare.utils.get_resource_path.""" print(utils.get_resource_path()) assert op.isdir(utils.get_resource_path()) + + +def test_check_inputs_shape(): + """Test nimare.utils._check_inputs_shape.""" + n_rows = 5 + n_columns = 4 + n_pred = 3 + y = np.random.randint(1, 100, size=(n_rows, n_columns)) + v = np.random.randint(1, 100, size=(n_rows, n_columns)) + n = np.random.randint(1, 100, size=(n_rows, n_columns)) + X = np.random.randint(1, 100, size=(n_rows, n_pred)) + X_names = [f"X{x}" for x in range(n_pred)] + + utils._check_inputs_shape(y, X, "y", "X", row=True) + utils._check_inputs_shape(y, v, "y", "v", row=True, column=True) + utils._check_inputs_shape(y, n, "y", "n", row=True, column=True) + utils._check_inputs_shape(X, np.array(X_names)[None, :], "X", "X_names", column=True) From d73271af75f7f68364ef7642e0cf1fed89c3a586 Mon Sep 17 00:00:00 2001 From: JulioAPeraza Date: Thu, 9 Jun 2022 21:54:04 -0400 Subject: [PATCH 3/5] Cover two missing lines by the test --- pymare/tests/test_utils.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pymare/tests/test_utils.py b/pymare/tests/test_utils.py index 5e6bb97..495bd19 100644 --- a/pymare/tests/test_utils.py +++ b/pymare/tests/test_utils.py @@ -2,6 +2,7 @@ import os.path as op import numpy as np +import pytest from pymare import utils @@ -18,12 +19,19 @@ def test_check_inputs_shape(): n_columns = 4 n_pred = 3 y = np.random.randint(1, 100, size=(n_rows, n_columns)) - v = np.random.randint(1, 100, size=(n_rows, n_columns)) + v = np.random.randint(1, 100, size=(n_rows + 1, n_columns)) n = np.random.randint(1, 100, size=(n_rows, n_columns)) X = np.random.randint(1, 100, size=(n_rows, n_pred)) X_names = [f"X{x}" for x in range(n_pred)] utils._check_inputs_shape(y, X, "y", "X", row=True) - utils._check_inputs_shape(y, v, "y", "v", row=True, column=True) utils._check_inputs_shape(y, n, "y", "n", row=True, column=True) utils._check_inputs_shape(X, np.array(X_names)[None, :], "X", "X_names", column=True) + + # Raise error if the number of rows and columns of v don't match y + with pytest.raises(ValueError): + utils._check_inputs_shape(y, v, "y", "v", row=True, column=True) + + # Raise error if neither row or column is True + with pytest.raises(ValueError): + utils._check_inputs_shape(y, n, "y", "n") From cffebb4b75e474cdc4941f50e8fb8da85a1c619e Mon Sep 17 00:00:00 2001 From: JulioAPeraza Date: Fri, 10 Jun 2022 11:57:13 -0400 Subject: [PATCH 4/5] Add test when n or v is None --- .zenodo.json | 5 +++++ pymare/tests/test_utils.py | 3 +++ pymare/utils.py | 3 +++ 3 files changed, 11 insertions(+) diff --git a/.zenodo.json b/.zenodo.json index f524e90..00d9ae2 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -10,6 +10,11 @@ "affiliation": "Florida International University", "orcid": "0000-0001-9813-3167" }, + { + "name": "Peraza, Julio A.", + "affiliation": "Florida International University", + "orcid": "0000-0003-3816-5903" + }, { "name": "Nichols, Thomas E.", "affiliation": "Big Data Institute, University of Oxford", diff --git a/pymare/tests/test_utils.py b/pymare/tests/test_utils.py index 495bd19..474b8f8 100644 --- a/pymare/tests/test_utils.py +++ b/pymare/tests/test_utils.py @@ -35,3 +35,6 @@ def test_check_inputs_shape(): # Raise error if neither row or column is True with pytest.raises(ValueError): utils._check_inputs_shape(y, n, "y", "n") + + # Dataset may be initialized with n or v as None + utils._check_inputs_shape(y, None, "y", "n", row=True, column=True) diff --git a/pymare/utils.py b/pymare/utils.py index b159a48..dcb0011 100644 --- a/pymare/utils.py +++ b/pymare/utils.py @@ -55,3 +55,6 @@ def _check_inputs_shape(param1, param2, param1_name, param2_name, row=False, col f"You provided {param1_name} with shape {param1.shape} and {param2_name} " f"with shape {param2.shape}." ) + elif (param1 is None) or (param2 is None): + # If param1 or param2 is None, we don't need to check the shape + pass From 86a1eb58096304a737017d7fccb239bb50d9fceb Mon Sep 17 00:00:00 2001 From: JulioAPeraza Date: Fri, 10 Jun 2022 15:01:34 -0400 Subject: [PATCH 5/5] Remove extra clause --- pymare/utils.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pymare/utils.py b/pymare/utils.py index dcb0011..b159a48 100644 --- a/pymare/utils.py +++ b/pymare/utils.py @@ -55,6 +55,3 @@ def _check_inputs_shape(param1, param2, param1_name, param2_name, row=False, col f"You provided {param1_name} with shape {param1.shape} and {param2_name} " f"with shape {param2.shape}." ) - elif (param1 is None) or (param2 is None): - # If param1 or param2 is None, we don't need to check the shape - pass