Skip to main content

A simple plugin that computes Local Moran's I spatial-autocorrelation maps of 2-D images in napari.

Project description

napari-morans-i

License BSD-3 PyPI Python Version tests codecov napari hub npe2 Copier

A napari plugin that computes Local Moran's I spatial-autocorrelation maps of 2-D images, with a permutation-test–based significance map and a categorical cluster map (HH / LL / HL / LH / NS).

It is a Python port of the MATLAB reference implementation moran_local.m used in:

Dávid Cs. et al., "An image segmentation method based on the spatial correlation coefficient of Local Moran’s I - identification of A-type potassium channel clusters in the thalamus", eLife (2024). https://doi.org/10.7554/eLife.89361.1)

This code was entirely created using Claude Opus 4.7 (2026-05-06). Prompts have been archived in the prompts.txt file.


Demo

Demo of the plugin

Features

  • Layer chooser — operates on any 2-D Image layer in the current viewer.
  • Configurable Moran order — controls the size of the Gaussian neighbourhood kernel (2·order + 1)².
  • Four standard significance levels — 0.05, 0.01, 0.001, 0.0001.
  • Permutation test with user-defined number of repeats (default n = 200).
  • Threaded execution via napari.qt.threading.thread_worker, with a live progress bar and a Cancel button — the viewer stays responsive throughout.
  • Three output layers added back to the viewer for inspection:
    Suffix Type Meaning
    _LocalI Image Local Moran's I per pixel (continuous)
    _Significance Image Binary mask: pixels with p ≤ sig_level
    _Clusters Labels Categorical cluster code (see legend below)

Cluster legend

Code Class Meaning
0 NS Not significant
1 HH High-value pixel surrounded by high-value neighbours
2 LL Low-value pixel surrounded by low-value neighbours
3 LH Low-value pixel surrounded by high-value neighbours
4 HL High-value pixel surrounded by low-value neighbours

These codes match the original MATLAB source one-for-one.


Usage

  1. Launch napari and open or drag-drop an image.
  2. Open Plugins → Moran's I Analysis.
  3. (Optional) Try the bundled demo via File → Open Sample → napari-morans-i: Moran's I sample (blocks).
  4. Choose the input image layer, the Moran order, the significance level, and the number of permutations.
  5. Click Calculate. Progress is reported live; Cancel stops the permutation test cleanly.
  6. Inspect the three new layers added to the viewer.


This napari plugin was generated with copier using the napari-plugin-template (None).

Installation

You can install napari-morans-i via pip:

pip install napari-morans-i

If napari is not already installed, you can install napari-morans-i with napari and Qt via:

pip install "napari-morans-i[all]"

To install latest development version:

pip install git+https://github.com/lukasjarzembowski/napari-morans-i.git

Algorithm — step-by-step

The implementation lives in napari_morans_i/_core.py and reproduces the MATLAB script step-for-step. Each step has a corresponding pure function so it is independently testable.

1. Z-normalisation (z_normalize)

Convert the input image X into z-scores using the sample standard deviation (ddof=1), to match MATLAB's default std:

z = (X − mean(X)) / std(X, ddof=1)

If the image is constant (std = 0) we short-circuit to an all-zeros array.

2. Gaussian neighbourhood weights (gaussian_weight_matrix)

Build a (2·order + 1) × (2·order + 1) Gaussian kernel W with σ = (order + 1) / 1.7 and the centre cell zeroed, exactly as in the MATLAB reference. Zeroing the centre means each pixel never contributes to its own neighbourhood average.

3. Lagged values (local_morans_i)

Two zero-padded 2-D convolutions (scipy.signal.convolve2d, mode='same', boundary='fill'):

WZ  = conv2(z,         W, 'same')   # weighted sum of neighbours
nS  = conv2(ones_like(z), W, 'same')   # normaliser, edge-correction
lagged = WZ / nS                    # mean of standardised neighbours
local_i = z * lagged                # Local Moran's I per pixel

The nS normaliser corrects for kernel weight that "falls off the edge" of the image, which is why we use zero-padding rather than a periodic boundary.

4. Global Moran's I (global_morans_i)

The global statistic is the OLS slope of lagged regressed on z — i.e. cov(z, lagged) / var(z). Computed with numpy.polyfit. Returns 0.0 when z has zero variance.

5. Permutation test (_permutation_pass, inside morans_compute)

For each repetition 1…n_repeats:

  1. Shuffle z to obtain z_perm.
  2. Recompute the permuted lagged map and local_i_perm.
  3. Sign-aware tally: for each pixel, increment a counter when local_i_perm lies in the same tail (positive vs. negative) as the observed local_i_obs and has equal-or-greater magnitude.

After n_repeats permutations, the pseudo p-value per pixel is (count + 1) / (n_repeats + 1) — the standard +1 correction that keeps p strictly positive. Sign-aware tallying matches the MATLAB reference and is not equivalent to a two-sided absolute-value test.

6. Cluster classification (classify_clusters)

Threshold the p-values at the user-chosen significance level, then classify each significant pixel by the signs of z and lagged:

z lagged label code
≥ 0 ≥ 0 HH 1
< 0 < 0 LL 2
< 0 ≥ 0 LH 3
≥ 0 < 0 HL 4

Non-significant pixels get code 0 (NS).

Threading model

The whole pipeline is exposed as a generator (morans_compute) that yields integer progress percentages in [0, 100]. The widget wraps it in @napari.qt.threading.thread_worker:

@thread_worker
def _run():
    result = yield from morans_compute(image, order=, sig_level=, n_repeats=)
    return result

yield from automatically forwards every progress integer to the worker's yielded Qt-signal (which drives the progress bar) and forwards the final return value to the returned signal (which adds the output layers).

Cancel calls worker.quit(), which stops the generator at its next yield, leaving the viewer responsive at all times.


Citation

If you use this plugin in academic work, please cite both the original paper

Dávid Cs. et al. (2024). eLife, doi:10.7554/eLife.89361.1

and napari (Sofroniew et al., 2022, doi:10.5281/zenodo.3555620).

Contributing

Contributions are very welcome. Tests can be run with tox, please ensure the coverage at least stays the same before you submit a pull request.

License

Distributed under the terms of the BSD-3 license, "napari-morans-i" is free and open source software

Issues

If you encounter any problems, please file an issue along with a detailed description.

Project details


Release history Release notifications | RSS feed

This version

1.0

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

napari_morans_i-1.0.tar.gz (5.6 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

napari_morans_i-1.0-py3-none-any.whl (29.7 kB view details)

Uploaded Python 3

File details

Details for the file napari_morans_i-1.0.tar.gz.

File metadata

  • Download URL: napari_morans_i-1.0.tar.gz
  • Upload date:
  • Size: 5.6 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for napari_morans_i-1.0.tar.gz
Algorithm Hash digest
SHA256 93f05336a5bd12d83d5b331be4028759a1082e9aaf0bdcc6c9a390cb10df049d
MD5 8c89a82148bac26b97b76b881286d6a5
BLAKE2b-256 362abf99e1b7c60c3b67c658e7519ca747a9af87e33813793adc8a8b2d2dcd87

See more details on using hashes here.

Provenance

The following attestation bundles were made for napari_morans_i-1.0.tar.gz:

Publisher: test_and_deploy.yml on lukasjarzembowski/napari-morans-i

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file napari_morans_i-1.0-py3-none-any.whl.

File metadata

  • Download URL: napari_morans_i-1.0-py3-none-any.whl
  • Upload date:
  • Size: 29.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for napari_morans_i-1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 eeb49f7fb39b7a5865c9786aa8550468aff15a90e1fd123160863b64ceb98203
MD5 301b36e0cf83ae429b29095a7186b5d4
BLAKE2b-256 c7ad05df216c4661e3e378776641262c3d379462ea99827896b969c3caad568f

See more details on using hashes here.

Provenance

The following attestation bundles were made for napari_morans_i-1.0-py3-none-any.whl:

Publisher: test_and_deploy.yml on lukasjarzembowski/napari-morans-i

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page