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

Add Fixed Effects Meta-Analysis with Hedges’ g #894

Merged
merged 4 commits into from
Aug 2, 2024

Conversation

JulioAPeraza
Copy link
Collaborator

@JulioAPeraza JulioAPeraza commented Jul 26, 2024

Closes None.

Changes proposed in this pull request:

Fixed Effects Meta-Analysis with Hedges’ g

  • Here, we use Hedge's g as the point estimate and the variance of bias-corrected Cohen's d as the variance estimate in a weighted least-squares regression model.

Summary by Sourcery

This pull request adds a new feature for performing Fixed Effects Meta-Analysis using Hedges' g, refactors some array initializations for clarity, updates documentation to include the new feature, and adds corresponding test cases.

  • New Features:
    • Introduced Fixed Effects Meta-Analysis using Hedges' g in the NiMARE library, providing a weighted least-squares estimate with bias-corrected Cohen's d variance.
  • Enhancements:
    • Refactored the initialization of multiple numpy arrays in the _fit method to use list comprehensions for cleaner code.
  • Documentation:
    • Added documentation for the new FixedEffectsHedges class, including parameters, notes, and warnings.
    • Updated the example script to demonstrate the usage of Fixed Effects Meta-Analysis with Hedges' g.
  • Tests:
    • Added new test cases for the FixedEffectsHedges estimator to ensure proper functionality.

@JulioAPeraza JulioAPeraza added enhancement New feature or request ibma Issues/PRs pertaining to image-based meta-analysis labels Jul 26, 2024
Copy link
Contributor

sourcery-ai bot commented Jul 26, 2024

Reviewer's Guide by Sourcery

This pull request introduces a new Fixed Effects Meta-Analysis using Hedges' g. The implementation includes a new estimator class FixedEffectsHedges in nimare/meta/ibma.py, along with necessary transformations in nimare/transforms.py. Example usage, reference, and tests have also been added to demonstrate and validate the new functionality.

File-Level Changes

Files Changes
nimare/meta/ibma.py
nimare/transforms.py
Implemented Fixed Effects Meta-Analysis with Hedges' g, including necessary transformations and a new estimator class.
examples/02_meta-analyses/02_plot_ibma.py
nimare/resources/references.bib
nimare/tests/test_meta_ibma.py
Added example usage, reference, and tests for the new FixedEffectsHedges meta-analysis.

Tips
  • Trigger a new Sourcery review by commenting @sourcery-ai review on the pull request.
  • Continue your discussion with Sourcery by replying directly to review comments.
  • You can change your review settings at any time by accessing your dashboard:
    • Enable or disable the Sourcery-generated pull request summary or reviewer's guide;
    • Change the review language;
  • You can always contact us if you have any questions or feedback.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @JulioAPeraza - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Consider adding benchmarks to assess the performance impact of the new FixedEffectsHedges method, especially for large datasets.
  • Explore opportunities for further optimization in the _fit_model method of FixedEffectsHedges, possibly through vectorization of calculations.
  • Add more detailed comments in the _fit_model method explaining the mathematical steps, to improve maintainability and verifiability of the implementation.
Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟡 Testing: 1 issue found
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment to tell me if it was helpful.

se_map = np.zeros(n_voxels, dtype=float)
tau2_map = np.zeros(n_voxels, dtype=float)

z_map, p_map, est_map, se_map, tau2_map = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider using np.zeros with shape parameter for array initialization

While the list comprehension is an improvement over the previous repetitive code, using np.zeros with a shape parameter (e.g., np.zeros((5, n_voxels), dtype=float)) could be more efficient and clearer in intent for numpy users.

Suggested change
z_map, p_map, est_map, se_map, tau2_map = [
import numpy as np
z_map, p_map, est_map, se_map, tau2_map = np.zeros((5, n_voxels), dtype=float)

@@ -860,3 +860,48 @@ def z_to_t(z_values, dof):
t_values = np.zeros(z_values.shape)
t_values[z_values != 0] = t_values_nonzero
return t_values


def t_to_d(t_values, sample_sizes):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Add input validation and document assumptions for t_to_d and d_to_g functions

Consider adding input validation to check if the input arrays have the same shape and if the sample sizes are positive. Also, it might be worth adding a note about the assumptions these transformations make, such as equal sample sizes across groups for the t_to_d function.

Suggested change
def t_to_d(t_values, sample_sizes):
def t_to_d(t_values, sample_sizes):
if not isinstance(t_values, (list, np.ndarray)) or not isinstance(sample_sizes, (list, np.ndarray)):
raise TypeError("t_values and sample_sizes must be lists or numpy arrays.")
if len(t_values) != len(sample_sizes):
raise ValueError("t_values and sample_sizes must have the same length.")
if any(s <= 0 for s in sample_sizes):
raise ValueError("All sample sizes must be positive.")

Comment on lines +123 to +129
pytest.param(
ibma.FixedEffectsHedges,
{"tau2": 0},
None,
{},
("z", "p", "est", "se", "dof"),
id="FixedEffectsHedges",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Add edge case tests for FixedEffectsHedges

Consider adding tests for edge cases such as when t_maps contain NaN values, when sample_sizes are zero or negative, and when tau2 is set to a non-zero value. This will ensure the robustness of the FixedEffectsHedges implementation.

###############################################################################
# Fixed Effects Meta-Analysis with Hedges’ g
# -----------------------------------------------------------------------------
from nimare.meta.ibma import FixedEffectsHedges
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider refactoring to reduce redundancy and improve code organization.

The new code introduces unnecessary complexity and redundancy, violating the DRY (Don't Repeat Yourself) principle. The same block of code is repeated twice, making it harder to maintain. Additionally, the new functionality (Fixed Effects Meta-Analysis with Hedges’ g) is mixed with the original code, disrupting the flow and making it harder to read.

Here's a refactored version that addresses these issues:

from nimare.meta.ibma import FixedEffectsHedges

def plot_results(results):
    plot_stat_map(
        results.get_map("z"),
        cut_coords=[0, 0, -8],
        draw_cross=False,
        cmap="RdBu_r",
        symmetric_cbar=True,
    )
    print("Description:")
    pprint(results.description_)
    print("References:")
    pprint(results.bibtex_)

# Original functionality
plot_results(results)

# Fixed Effects Meta-Analysis with Hedges’ g
meta = FixedEffectsHedges(tau2=0)
hedges_results = meta.fit(dset)
plot_results(hedges_results)

This version reduces redundancy, maintains separation of concerns, and improves code organization, making it easier to understand and maintain.

Copy link

codecov bot commented Jul 26, 2024

Codecov Report

Attention: Patch coverage is 96.66667% with 2 lines in your changes missing coverage. Please review.

Project coverage is 88.23%. Comparing base (0c48c2f) to head (c2afbec).
Report is 11 commits behind head on main.

Files with missing lines Patch % Lines
nimare/meta/ibma.py 98.07% 1 Missing ⚠️
nimare/transforms.py 87.50% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #894      +/-   ##
==========================================
+ Coverage   88.20%   88.23%   +0.03%     
==========================================
  Files          48       48              
  Lines        6352     6386      +34     
==========================================
+ Hits         5603     5635      +32     
- Misses        749      751       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Member

@jdkent jdkent left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! We will circle back to getting Thomas Nichol's feedback.

Comment on lines +1681 to +1682
# tau2 is a float, not a map, so it can't go into the results dictionary
tables = {"level-estimator": pd.DataFrame(columns=["tau2"], data=[self.tau2])}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is fine for now since it's how weightedleastsquares returns results, but it is awkward to return a single value in a pandas dataframe versus it just being an attribute of the object.

@jdkent
Copy link
Member

jdkent commented Jul 31, 2024

@nicholst: If you have time, we have a couple items we could use an extra pair of eyes to affirm the equations were implemented correctly:

  1. T-statistic to Cohen's d

https://github.com/neurostuff/NiMARE/pull/894/files#diff-c4b7e710d859ad7044f5ace1b14be8c78884c8bd7e43eb34a53561cd90756022R865-R881

  1. the transformation from Cohen's d to Hedge's g

https://github.com/neurostuff/NiMARE/pull/894/files#diff-c4b7e710d859ad7044f5ace1b14be8c78884c8bd7e43eb34a53561cd90756022R884-R907

  1. Using the Hedge's g estimate and variance maps as input into the weighted least squares estimator.

https://github.com/neurostuff/NiMARE/pull/894/files#diff-9f375829adc9282d13ce7317d6a413f6466e9f04a59349edf705cb26b74636c4R1629-R1635

@jdkent jdkent merged commit 1dd2a34 into neurostuff:main Aug 2, 2024
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request ibma Issues/PRs pertaining to image-based meta-analysis
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants