Repository: nilearn/nilearn

Issue:
SimpleRegressionResults.__init__()
The documentation of `SimpleRegressionResults.__init__()` has been copied "as is" from `RegressionResults.__init__()` and is obviously wrong:

#### [SimpleRegressionResults](https://github.com/nilearn/nilearn/blob/e91e02d/nilearn/glm/regression.py#L466-L471)
```python
class SimpleRegressionResults(LikelihoodModelResults):
    [...]
        """See LikelihoodModelResults constructor.

        The only difference is that the whitened Y and residual values
        are stored for a regression model.
        """
```
#### [RegressionResults](https://github.com/nilearn/nilearn/blob/e91e02d/nilearn/glm/regression.py#L340-L347)
```python
class RegressionResults(LikelihoodModelResults):
    [...]
        """See LikelihoodModelResults constructor.

        The only difference is that the whitened Y and residual values
        are stored for a regression model.

        """
```

See discussion in https://github.com/nilearn/nilearn/pull/2952#issuecomment-919751239

### Affected pages

https://nilearn.github.io/modules/generated/nilearn.glm.SimpleRegressionResults.html#nilearn.glm.SimpleRegressionResults.__init__



Discussion:

Resolving patch:
diff --git a/nilearn/glm/_base.py b/nilearn/glm/_base.py
--- a/nilearn/glm/_base.py
+++ b/nilearn/glm/_base.py
@@ -8,6 +8,8 @@ class BaseGLM(BaseEstimator, TransformerMixin, CacheMixin):
     """Implement a base class \
     for the :term:`General Linear Model<GLM>`."""
 
+    # @auto_attr store the value as an object attribute after initial call
+    # better performance than @property
     @auto_attr
     def residuals(self):
         """Transform voxelwise residuals to the same shape \
diff --git a/nilearn/glm/model.py b/nilearn/glm/model.py
--- a/nilearn/glm/model.py
+++ b/nilearn/glm/model.py
@@ -86,6 +86,8 @@ def __init__(
         # put this as a parameter of LikelihoodModel
         self.df_residuals = self.df_total - self.df_model
 
+    # @auto_attr store the value as an object attribute after initial call
+    # better performance than @property
     @auto_attr
     def logL(self):
         """Return the maximized log-likelihood."""
diff --git a/nilearn/glm/regression.py b/nilearn/glm/regression.py
--- a/nilearn/glm/regression.py
+++ b/nilearn/glm/regression.py
@@ -17,7 +17,6 @@
 
 __docformat__ = "restructuredtext en"
 
-
 import numpy as np
 import scipy.linalg as spl
 from nibabel.onetime import auto_attr
@@ -309,6 +308,8 @@ def __init__(
         self.whitened_residuals = whitened_residuals
         self.whitened_design = model.whitened_design
 
+    # @auto_attr store the value as an object attribute after initial call
+    # better performance than @property
     @auto_attr
     def residuals(self):
         """Residuals from the fit."""
@@ -391,15 +392,19 @@ def __init__(self, results):
         # put this as a parameter of LikelihoodModel
         self.df_residuals = self.df_total - self.df_model
 
-    def logL(self, Y):
+    def logL(self):
         """Return the maximized log-likelihood."""
-        raise ValueError("can not use this method for simple results")
+        raise NotImplementedError(
+            "logL not implemented for "
+            "SimpleRegressionsResults. "
+            "Use RegressionResults"
+        )
 
-    def residuals(self, Y):
+    def residuals(self, Y, X):
         """Residuals from the fit."""
-        return Y - self.predicted
+        return Y - self.predicted(X)
 
-    def normalized_residuals(self, Y):
+    def normalized_residuals(self, Y, X):
         """Residuals, normalized to have unit length.
 
         See :footcite:`Montgomery2006` and :footcite:`Davidson2004`.
@@ -419,14 +424,11 @@ def normalized_residuals(self, Y):
         .. footbibliography::
 
         """
-        return self.residuals(Y) * positive_reciprocal(
+        return self.residuals(Y, X) * positive_reciprocal(
             np.sqrt(self.dispersion)
         )
 
-    @auto_attr
-    def predicted(self):
+    def predicted(self, X):
         """Return linear predictor values from a design matrix."""
         beta = self.theta
-        # the LikelihoodModelResults has parameters named 'theta'
-        X = self.model.design
         return np.dot(X, beta)


Test patch:
diff --git a/nilearn/glm/tests/test_regression.py b/nilearn/glm/tests/test_regression.py
--- a/nilearn/glm/tests/test_regression.py
+++ b/nilearn/glm/tests/test_regression.py
@@ -1,9 +1,13 @@
 """Test functions for models.regression"""
 
 import pytest
-from numpy.testing import assert_almost_equal, assert_array_almost_equal
+from numpy.testing import (
+    assert_almost_equal,
+    assert_array_almost_equal,
+    assert_array_equal,
+)
 
-from nilearn.glm import ARModel, OLSModel
+from nilearn.glm import ARModel, OLSModel, SimpleRegressionResults
 
 
 @pytest.fixture()
@@ -69,3 +73,15 @@ def test_AR_degenerate(X, Y):
     model = ARModel(design=X, rho=0.9)
     results = model.fit(Y)
     assert results.df_residuals == 31
+
+
+def test_simple_results(X, Y):
+    model = OLSModel(X)
+    results = model.fit(Y)
+
+    simple_results = SimpleRegressionResults(results)
+    assert_array_equal(results.predicted, simple_results.predicted(X))
+    assert_array_equal(results.residuals, simple_results.residuals(Y, X))
+    assert_array_equal(
+        results.normalized_residuals, simple_results.normalized_residuals(Y, X)
+    )