Skip to content

Commit 94855af

Browse files
authored
Add FDR and Bonferroni correction functions (#87)
* Add FDR and Bonferroni functions. * Add references. * Fix directive.
1 parent fc69b3c commit 94855af

File tree

4 files changed

+136
-0
lines changed

4 files changed

+136
-0
lines changed

docs/api.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,5 @@ API
113113
stats.ensure_2d
114114
stats.q_profile
115115
stats.q_gen
116+
stats.bonferroni
117+
stats.fdr

docs/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@
168168
"sklearn": ("https://scikit-learn.org/stable", (None, "./_intersphinx/sklearn-objects.inv")),
169169
"matplotlib": ("https://matplotlib.org/", (None, "https://matplotlib.org/objects.inv")),
170170
"pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None),
171+
"statsmodels": ("http://www.statsmodels.org/stable/", None),
171172
}
172173

173174
sphinx_gallery_conf = {

docs/references.bib

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,38 @@
1+
@article{benjamini1995controlling,
2+
title={Controlling the false discovery rate: a practical and powerful approach to multiple testing},
3+
author={Benjamini, Yoav and Hochberg, Yosef},
4+
journal={Journal of the Royal statistical society: series B (Methodological)},
5+
volume={57},
6+
number={1},
7+
pages={289--300},
8+
year={1995},
9+
publisher={Wiley Online Library},
10+
url={https://doi.org/10.1111/j.2517-6161.1995.tb02031.x},
11+
doi={10.1111/j.2517-6161.1995.tb02031.x}
12+
}
13+
14+
@article{benjamini2001control,
15+
author={Yoav Benjamini and Daniel Yekutieli},
16+
title={The control of the false discovery rate in multiple testing under dependency},
17+
volume={29},
18+
journal={The Annals of Statistics},
19+
number={4},
20+
publisher={Institute of Mathematical Statistics},
21+
pages={1165 -- 1188},
22+
year={2001},
23+
doi={10.1214/aos/1013699998},
24+
url={https://doi.org/10.1214/aos/1013699998}
25+
}
26+
27+
@article{bonferroni1936teoria,
28+
title={Teoria statistica delle classi e calcolo delle probabilita},
29+
author={Bonferroni, Carlo},
30+
journal={Pubblicazioni del R Istituto Superiore di Scienze Economiche e Commericiali di Firenze},
31+
volume={8},
32+
pages={3--62},
33+
year={1936}
34+
}
35+
136
@article{brockwell2001comparison,
237
title={A comparison of statistical methods for meta-analysis},
338
author={Brockwell, Sarah E and Gordon, Ian R},
@@ -88,6 +123,17 @@ @article{sangnawakij2019meta
88123
doi={10.1177/0962280217718867}
89124
}
90125

126+
@article{shaffer1995multiple,
127+
title={Multiple hypothesis testing},
128+
author={Shaffer, Juliet Popper},
129+
journal={Annual review of psychology},
130+
volume={46},
131+
number={1},
132+
pages={561--584},
133+
year={1995},
134+
publisher={Annual Reviews 4139 El Camino Way, PO Box 10139, Palo Alto, CA 94303-0139, USA}
135+
}
136+
91137
@article{stouffer1949american,
92138
title={The american soldier: Adjustment during army life.(studies in social psychology in world war ii), vol. 1},
93139
author={Stouffer, Samuel A and Suchman, Edward A and DeVinney, Leland C and Star, Shirley A and Williams Jr, Robin M},

pymare/stats.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,90 @@ def q_gen(y, v, X, tau2):
146146
beta = weighted_least_squares(y, v, X, tau2)
147147
w = 1.0 / (v + tau2)
148148
return (w * (y - X.dot(beta)) ** 2).sum(0)
149+
150+
151+
def bonferroni(p_values):
152+
"""Perform Bonferroni correction on p values.
153+
154+
This correction is based on the one described in :footcite:t:`bonferroni1936teoria` and
155+
:footcite:t:`shaffer1995multiple`.
156+
157+
.. versionadded:: 0.0.4
158+
159+
Parameters
160+
----------
161+
p_values : :obj:`numpy.ndarray`
162+
Uncorrected p values.
163+
164+
Returns
165+
-------
166+
p_corr : :obj:`numpy.ndarray`
167+
Corrected p values.
168+
169+
References
170+
----------
171+
.. footbibliography::
172+
"""
173+
p_corr = p_values * p_values.size
174+
p_corr[p_corr > 1] = 1
175+
return p_corr
176+
177+
178+
def fdr(p_values, q=0.05, method="bh"):
179+
"""Perform FDR correction on p values.
180+
181+
.. versionadded:: 0.0.4
182+
183+
Parameters
184+
----------
185+
p_values : :obj:`numpy.ndarray`
186+
Array of p values.
187+
q : :obj:`float`, optional
188+
Alpha value. Default is 0.05.
189+
method : {"bh", "by"}, optional
190+
Method to use for correction.
191+
Either "bh" (Benjamini-Hochberg :footcite:p:`benjamini1995controlling`) or
192+
"by" (Benjamini-Yekutieli :footcite:p:`benjamini2001control`).
193+
Default is "bh".
194+
195+
Returns
196+
-------
197+
p_adjusted : :obj:`numpy.ndarray`
198+
Array of adjusted p values.
199+
200+
Notes
201+
-----
202+
This function is adapted from ``statsmodels``, which is licensed under a BSD-3 license.
203+
204+
References
205+
----------
206+
.. footbibliography::
207+
208+
See Also
209+
--------
210+
statsmodels.stats.multitest.fdrcorrection
211+
"""
212+
sort_idx = np.argsort(p_values)
213+
revert_idx = np.argsort(sort_idx)
214+
p_sorted = p_values[sort_idx]
215+
216+
n_tests = p_values.size
217+
218+
# empirical cumulative density function
219+
ecdf = np.linspace(0, 1, n_tests + 1)[1:]
220+
if method == "by":
221+
# NOTE: I don't know what cm stands for
222+
cm = np.sum(1 / np.arange(1, n_tests + 1))
223+
ecdffactor = ecdf / cm
224+
else:
225+
ecdffactor = ecdf
226+
227+
p_adjusted = p_sorted / ecdffactor
228+
p_adjusted = np.minimum.accumulate(p_adjusted[::-1])[::-1]
229+
# NOTE: Why not this?
230+
# p_adjusted = np.maximum.accumulate(p_adjusted)
231+
232+
p_adjusted[p_adjusted > 1] = 1
233+
p_adjusted = p_adjusted[revert_idx]
234+
235+
return p_adjusted

0 commit comments

Comments
 (0)