Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] concordant z values #125

Merged
merged 4 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions pymare/estimators/combination.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,15 @@ def fit(self, z, *args, **kwargs):
p1 = ose.p_value(z, *args, **kwargs)
p2 = ose.p_value(-z, *args, **kwargs)
p = np.minimum(1, 2 * np.minimum(p1, p2))
z_calc = ss.norm.isf(p)
z_calc[p2 < p1] *= -1
else:
if self.mode == "undirected":
z = np.abs(z)
p = self.p_value(z, *args, **kwargs)
self.params_ = {"p": p}
z_calc = ss.norm.isf(p)

self.params_ = {"p": p, "z": z_calc}
return self

def summary(self):
Expand All @@ -60,7 +64,9 @@ def summary(self):
"This {} instance hasn't been fitted yet. Please "
"call fit() before summary().".format(name)
)
return CombinationTestResults(self, self.dataset_, p=self.params_["p"])
return CombinationTestResults(
self, self.dataset_, z=self.params_["z"], p=self.params_["p"]
)


class StoufferCombinationTest(CombinationTest):
Expand Down
23 changes: 8 additions & 15 deletions pymare/tests/test_combination_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import numpy as np
import pytest
import scipy.stats as ss

from pymare import Dataset
from pymare.estimators import FisherCombinationTest, StoufferCombinationTest
Expand All @@ -16,22 +15,21 @@
(StoufferCombinationTest, _z1, "concordant", [4.55204117]),
(StoufferCombinationTest, _z2, "directed", [4.69574275, -4.16803071]),
(StoufferCombinationTest, _z2, "undirected", [4.87462819, 4.16803071]),
(StoufferCombinationTest, _z2, "concordant", [4.55204117, 4.00717817]),
(StoufferCombinationTest, _z2, "concordant", [4.55204117, -4.00717817]),
(FisherCombinationTest, _z1, "directed", [5.22413541]),
(FisherCombinationTest, _z1, "undirected", [5.27449962]),
(FisherCombinationTest, _z1, "concordant", [5.09434911]),
(FisherCombinationTest, _z2, "directed", [5.22413541, -3.30626405]),
(FisherCombinationTest, _z2, "undirected", [5.27449962, 4.27572965]),
(FisherCombinationTest, _z2, "concordant", [5.09434911, 4.11869468]),
(FisherCombinationTest, _z2, "concordant", [5.09434911, -4.11869468]),
]


@pytest.mark.parametrize("Cls,data,mode,expected", _params)
def test_combination_test(Cls, data, mode, expected):
"""Test CombinationTest Estimators with numpy data."""
results = Cls(mode).fit(data).params_
z = ss.norm.isf(results["p"])
assert np.allclose(z, expected, atol=1e-5)
assert np.allclose(results["z"], expected, atol=1e-5)


@pytest.mark.parametrize("Cls,data,mode,expected", _params)
Expand All @@ -40,8 +38,7 @@ def test_combination_test_from_dataset(Cls, data, mode, expected):
dset = Dataset(y=data)
est = Cls(mode).fit_dataset(dset)
results = est.summary()
z = ss.norm.isf(results.p)
assert np.allclose(z, expected, atol=1e-5)
assert np.allclose(results.z, expected, atol=1e-5)


def test_stouffer_adjusted():
Expand All @@ -61,10 +58,9 @@ def test_stouffer_adjusted():
groups = np.tile(np.array([0, 0, 1, 2, 2, 2]), (data.shape[1], 1)).T

results = StoufferCombinationTest("directed").fit(z=data, w=weights, g=groups).params_
z = ss.norm.isf(results["p"])

z_expected = np.array([5.00088912, 3.70356943, 4.05465924, 5.4633001, 5.18927878])
assert np.allclose(z, z_expected, atol=1e-5)
assert np.allclose(results["z"], z_expected, atol=1e-5)

# Test with weights and no groups. Limiting cases.
# Limiting case 1: all correlations are one.
Expand All @@ -74,22 +70,20 @@ def test_stouffer_adjusted():
groups_l1 = np.tile(np.array([0, 0, 0, 0, 0]), (data_l1.shape[1], 1)).T

results_l1 = StoufferCombinationTest("directed").fit(z=data_l1, g=groups_l1).params_
z_l1 = ss.norm.isf(results_l1["p"])

sigma_l1 = n_maps_l1 * (n_maps_l1 - 1) # Expected inflation term
z_expected_l1 = n_maps_l1 * common_sample / np.sqrt(n_maps_l1 + sigma_l1)
assert np.allclose(z_l1, z_expected_l1, atol=1e-5)
assert np.allclose(results_l1["z"], z_expected_l1, atol=1e-5)

# Test with correlation matrix and groups.
data_corr = data - data.mean(0)
corr = np.corrcoef(data_corr, rowvar=True)
results_corr = (
StoufferCombinationTest("directed").fit(z=data, w=weights, g=groups, corr=corr).params_
)
z_corr = ss.norm.isf(results_corr["p"])

z_corr_expected = np.array([5.00088912, 3.70356943, 4.05465924, 5.4633001, 5.18927878])
assert np.allclose(z_corr, z_corr_expected, atol=1e-5)
assert np.allclose(results_corr["z"], z_corr_expected, atol=1e-5)

# Test with no correlation matrix and groups, but only one feature.
with pytest.raises(ValueError):
Expand All @@ -101,6 +95,5 @@ def test_stouffer_adjusted():

# Test with correlation matrix and no groups.
results1 = StoufferCombinationTest("directed").fit(z=_z1, corr=corr).params_
z1 = ss.norm.isf(results1["p"])

assert np.allclose(z1, [4.69574], atol=1e-5)
assert np.allclose(results1["z"], [4.69574], atol=1e-5)
Loading