Repository: scipy/scipy

Issue:
Fisher's method for combining independent test results
Add Fisher's method (https://en.wikipedia.org/wiki/Fisher's_method) to combine the results from more than one independent tests.

Perhaps also Stouffer's Z-score method: https://en.wikipedia.org/wiki/Fisher's_method#Relation_to_Stouffer.27s_Z-score_method

Fisher's method for combining independent test results
Add Fisher's method (https://en.wikipedia.org/wiki/Fisher's_method) to combine the results from more than one independent tests.

Perhaps also Stouffer's Z-score method: https://en.wikipedia.org/wiki/Fisher's_method#Relation_to_Stouffer.27s_Z-score_method



Discussion:
Looks like that would be a good addition to scipy.stats - contributions welcome.

Looks like that would be a good addition to scipy.stats - contributions welcome.


Resolving patch:
diff --git a/scipy/stats/__init__.py b/scipy/stats/__init__.py
--- a/scipy/stats/__init__.py
+++ b/scipy/stats/__init__.py
@@ -265,6 +265,7 @@
    wilcoxon
    kruskal
    friedmanchisquare
+   combine_pvalues
 
 .. autosummary::
    :toctree: generated/
diff --git a/scipy/stats/stats.py b/scipy/stats/stats.py
--- a/scipy/stats/stats.py
+++ b/scipy/stats/stats.py
@@ -130,6 +130,7 @@
    wilcoxon
    kruskal
    friedmanchisquare
+   combine_pvalues
 
 Probability Calculations
 ------------------------
@@ -204,7 +205,7 @@
            'zprob', 'chisqprob', 'ksprob', 'fprob', 'betai',
            'f_value_wilks_lambda', 'f_value', 'f_value_multivariate',
            'ss', 'square_of_sums', 'fastsort', 'rankdata', 'nanmean',
-           'nanstd', 'nanmedian', ]
+           'nanstd', 'nanmedian', 'combine_pvalues', ]
 
 
 def _chk_asarray(a, axis):
@@ -4256,6 +4257,85 @@ def friedmanchisquare(*args):
     return chisq, chisqprob(chisq,k-1)
 
 
+def combine_pvalues(pvalues, method='fisher', weights=None):
+    """
+    Methods for combining the p-values of independent tests bearing upon the
+    same hypothesis.
+
+    Parameters
+    ----------
+    p: array_like, 1-D
+        Array of p-values assumed to come from independent tests.
+    method: str
+        Name of method to use to combine p-values. The following methods are
+        available:
+        - "fisher": Fisher's method (Fisher's combined probability test)
+        - "stouffer": Stouffer's Z-score method
+    weights: array_like, 1-D, optional
+        Optional array of weights used only for Stouffer's Z-score method.
+
+    Returns
+    -------
+    statistic: float
+        The statistic calculated by the specified method:
+        - "fisher": The chi-squared statistic
+        - "stouffer": The Z-score
+    pval: float
+        The combined p-value.
+
+    Notes
+    -----
+    Fisher's method (also known as Fisher's combined probability test) [1]_ uses
+    a chi-squared statistic to compute a combined p-value. The closely related
+    Stouffer's Z-score method [2]_ uses Z-scores rather than p-values. The
+    advantage of Stouffer's method is that it is straightforward to introduce
+    weights, which can make Stouffer's method more powerful than Fisher's
+    method when the p-values are from studies of different size [3]_ [4]_.
+
+    Fisher's method may be extended to combine p-values from dependent tests
+    [5]_. Extensions such as Brown's method and Kost's method are not currently
+    implemented.
+
+    References
+    ----------
+    .. [1] https://en.wikipedia.org/wiki/Fisher%27s_method
+    .. [2] http://en.wikipedia.org/wiki/Fisher's_method#Relation_to_Stouffer.27s_Z-score_method
+    .. [3] Whitlock, M. C. "Combining probability from independent tests: the
+           weighted Z-method is superior to Fisher's approach." Journal of
+           Evolutionary Biology 18, no. 5 (2005): 1368-1373.
+    .. [4] Zaykin, Dmitri V. "Optimally weighted Z-test is a powerful method
+           for combining probabilities in meta-analysis." Journal of
+           Evolutionary Biology 24, no. 8 (2011): 1836-1841.
+    .. [5] https://en.wikipedia.org/wiki/Extensions_of_Fisher%27s_method
+
+    """
+    pvalues = np.asarray(pvalues)
+    if pvalues.ndim != 1:
+        raise ValueError("pvalues is not 1-D")
+
+    if method == 'fisher':
+        Xsq = -2 * np.sum(np.log(pvalues))
+        pval = distributions.chi2.sf(Xsq, 2 * len(pvalues))
+        return (Xsq, pval)
+    elif method == 'stouffer':
+        if weights is None:
+            weights = np.ones_like(pvalues)
+        elif len(weights) != len(pvalues):
+            raise ValueError("pvalues and weights must be of the same size.")
+
+        weights = np.asarray(weights)
+        if weights.ndim != 1:
+            raise ValueError("weights is not 1-D")
+
+        Zi = distributions.norm.isf(pvalues)
+        Z = np.dot(weights, Zi) / np.linalg.norm(weights)
+        pval = distributions.norm.sf(Z)
+
+        return (Z, pval)
+    else:
+        raise ValueError(
+            "Invalid method '%s'. Options are 'fisher' or 'stouffer'", method)
+
 #####################################
 ####  PROBABILITY CALCULATIONS  ####
 #####################################


Test patch:
diff --git a/scipy/stats/tests/test_stats.py b/scipy/stats/tests/test_stats.py
--- a/scipy/stats/tests/test_stats.py
+++ b/scipy/stats/tests/test_stats.py
@@ -2826,5 +2826,30 @@ def test_three_groups(self):
         assert_approx_equal(p, stats.chisqprob(h, 2))
 
 
+class TestCombinePvalues(TestCase):
+
+    def test_fisher(self):
+        # Example taken from http://en.wikipedia.org/wiki/Fisher's_exact_test#Example
+        xsq, p = stats.combine_pvalues([.01, .2, .3], method='fisher')
+        assert_approx_equal(p, 0.02156, significant=4)
+
+    def test_stouffer(self):
+        Z, p = stats.combine_pvalues([.01, .2, .3], method='stouffer')
+        assert_approx_equal(p, 0.01651, significant=4)
+
+    def test_stouffer2(self):
+        Z, p = stats.combine_pvalues([.5, .5, .5], method='stouffer')
+        assert_approx_equal(p, 0.5, significant=4)
+
+    def test_weighted_stouffer(self):
+        Z, p = stats.combine_pvalues([.01, .2, .3], method='stouffer',
+                                     weights=np.ones(3))
+        assert_approx_equal(p, 0.01651, significant=4)
+
+    def test_weighted_stouffer2(self):
+        Z, p = stats.combine_pvalues([.01, .2, .3], method='stouffer',
+                                     weights=np.array((1, 4, 9)))
+        assert_approx_equal(p, 0.1464, significant=4)
+
 if __name__ == "__main__":
     run_module_suite()