Skip to content

[BUG] incorrect masking of null array in stats.compare_images #196

@alicehodapp

Description

@alicehodapp

Issue summary

Hi!

It seems like the neuromaps.stats.comapre_images function uses the zero/nan mask that is based on the two input arrays on permutated data with different locations of zeros/nans.

Detailed issue description

Thank you very much for developing this super useful toolbox!

I used the neuromaps.alexander_bloch() function to create rotated data. I have a few nan values in my vertex vise data, so I wanted to see if and how neuromaps.stats.compare_images() is handling such a case. I might be missing something obvious, but it seems like the mask that is created based on zeros/nans in src and trg is then applied to the null distributions (if provided). At least when using the alexander_bloch() rotated output, the nans are also rotated and therefore the mask doesn't fit anymore.

I started fixing this and realized that a fix needs adjustments in multiple locations in the code. In case you want to adapt the masking approach in compare_images like I did (rather than e.g., changes in the alexander_bloch() function), I'd be happy to make a pull request.

This is the section of code in neuromaps.stats.compare_images() I am referring to:

def compare_images():
...
# drop NaNs (if nan_policy==`omit`) and zeros (if ignore_zero=True)
zeromask = np.zeros(len(srcdata), dtype=bool)
if ignore_zero:
    zeromask = np.logical_or(np.isclose(srcdata, 0),
                                np.isclose(trgdata, 0))
nanmask = np.logical_or(np.isnan(srcdata), np.isnan(trgdata))
if nan_policy == 'raise':
    if np.any(nanmask):
        raise ValueError('Inputs contain nan')
elif nan_policy == 'omit':
    mask = np.logical_and(np.logical_not(zeromask),
                            np.logical_not(nanmask))
elif nan_policy == 'propagate':
    mask = np.logical_not(zeromask)
srcdata, trgdata = srcdata[mask], trgdata[mask]

if metric in methods:
    if metric == 'spearmanr':
        srcdata = sstats.rankdata(srcdata)
        trgdata = sstats.rankdata(trgdata)
    metric = partial(efficient_pearsonr, return_pval=False)

if nulls is not None:
    n_perm = nulls.shape[-1]
    nulls = nulls[mask]
...

Steps to reproduce issue

I came across this bug in the context of running:


from neuromaps import stats, nulls

rotated = nulls.alexander_bloch(src, atlas='fsaverage', density='10k',
                                n_perm=100, seed=1234)
corr, pval = stats.compare_images(src, trg, nulls=rotated, ignore_zero=False, nan_policy='omit', metric="pearsonr")

Software version

3.9.6 (default, Nov 11 2024, 03:15:38)
[Clang 16.0.0 (clang-1600.0.26.6)]
0.0.5

Code of Conduct

  • I agree to follow the neuromaps Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions