MEA-seqX
Emery, Hu, Klütsch et al. (Amin Lab, DZNE) — a computational framework pairing
high-density microelectrode array (HD-MEA) electrophysiology with 10x Visium spatial transcriptomics
on the same mouse hippocampal slices, to relate gene expression to network-wide electrical activity
at cellular resolution. This page cross-checks the paper's 6-step pipeline against the vendored
source; the code browser lets you read the
actual implementation, and the runner executes
the authors' own official demo dataset (fetched from their Zenodo records) through the vendored code,
essentially unmodified — not a substitute built on other data.
Repo cloned
main
1545932 Update README.md
Runs the authors' own official demo dataset, unmodified vendored code
What it relates
Spatial transcriptomics
10x Visium H&E, mouse hippocampal slice
Electrophysiology
3Brain HD-MEA recording, same slice
Output
Per-region correlation, XGBoost cross-prediction, joint NMF patterns
What the runner actually uses
This is the paper's own demo, not a substitute. An earlier version of this
runner worked around missing data by building a stand-in LFP detector and a geometric reference-point
placeholder on top of unrelated real recordings from elsewhere in this project. That version is gone.
The README points to two Zenodo records; both are real, fetchable, and small enough to actually use
once the giant raw FASTQ/voltage files in those records (tens of GB, never read by any MEA-seqX script)
are skipped:
- Zenodo 10626259
— sample
L75027, confirmed via the record's own metadata to be Mouse_Hippocampus_SD,
the exact condition the repo's bundled data/SD/ files were built for. Only the processed
spaceranger outputs are fetched (~5MB): filtered_feature_bc_matrix.h5,
tissue_positions_list.csv, scalefactors_json.json, tissue_lowres_image.png.
- Zenodo 10620559
—
Phase_01_LFP_SD.bxr (~1.25GB), a real, genuine 3Brain BrainWave-processed recording —
not a substitute detector, not synthesized. This is the actual missing piece the earlier version
worked around.
Fetch with
measeqx/bin/fetch_zenodo_sd.sh, build the fixture with
measeqx/data/prepare_srt_fixture.py.
Steps 1 and 2 call the vendored methods directly.
MEASeqX_Project.gene_expression() and Multiscale_Spatial_Alignment() are
called exactly as the authors wrote them — same reference points (data/SD/SRT Reference
Points.csv / nEphys Reference Points.csv, the authors' own human-curated
landmarks), same cluster assignments (data/SD/Loupe Clusters.csv), same physical
calibration constants. No glue code, no placeholder code — except one real algorithmic correction,
disclosed in detail below, to the rotation step specifically.
A real bug in the rotation math, found by checking landmarks that should
match and didn't. The registration fits a 2-point rigid transform: one landmark
("Distal CA1") pins translation, a second ("Proximal CA3") is meant to pin rotation. A correctly-fit
2-point transform makes both anchors land closely — that's the whole point of using two. The
published code computed the rotation angle as the angle between the two landmarks' position
vectors from the coordinate origin, not the angle of the line connecting them — those only
coincide if the origin happens to sit in a comparable place relative to that line in both frames,
which it doesn't. Verified on the real SD data: with the original code, Proximal CA3 — one of only
two points the whole transform is fit from — landed 722µm from where it should, instead of near-zero.
Fixed by measuring each frame's landmark-to-landmark line direction against a fixed axis instead of
against the origin; same underlying angle function, corrected inputs. After the fix, Proximal CA3
lands at 0µm as it should, and the three landmarks never used to fit the transform (a genuine
held-out check) mostly improved too. Full numbers and the exact code diff are in
vendor/NOTICE.md.
Is this actually the same physical slice? Neither Zenodo record's metadata
carries an explicit shared sample/slice ID linking L75027 to
Phase_01_LFP_SD.bxr — they're linked only by the shared "SD" condition label. Checked the
paper itself (Emery et al. 2025) rather than assume either way: its Experimental Section states n-Ephys
recording and SRT processing are done sequentially on the same physical slice — "Immediately
following n-Ephys recordings (10 min) and optical imaging (2 min), slices were embedded in..." for SRT
— making cross-slice pairing structurally impossible by the authors' own protocol. The paper also
states elsewhere that using separate slices per modality "would be suboptimal and fundamentally
limited," producing only speculative, not measurable, relationships — i.e. the authors designed the
method specifically to rule this out. Combined with Zenodo shipping exactly one SRT sample and one
nEphys recording per condition (not a scattered multi-animal cohort), this is real, strong evidence for
a matched representative demo pair, though not a metadata-level guarantee. If the electrode-cluster
shape and the tissue photo's shape look different when you run it, that's the expected look of a
coarse 64×64 electrode-grid mask next to an organic tissue photo, not evidence the two datasets
are mismatched.
Six real bugs fixed to get this running on a modern stack. This is
2021-era code (Python 3.7, older numpy/matplotlib) that — going by what actually crashed on first
run — had apparently never been executed against real data since: np.histogram(...,
normed=False, ...) (removed from numpy), ChsGroups['Name'] never decoded from
bytes before a string concatenation, a hardcoded LfpForms = None that crashed the very
next line, np.int / plt.cm.get_cmap (both removed from their libraries),
and a unit-mismatch bug where the real-tissue-image overlay coordinates subtracted an unscaled pixel
offset from a scaled one — confirmed empirically when real spots plotted far outside the real tissue
photo instead of on it. All are one-line fixes, documented inline where they're patched and in
vendor/NOTICE.md — the underlying logic is untouched, and none of them
touch the actual registration math (verified separately, see the run page's disclosure).
What's still ours, not the paper's: the cluster-level correlation plot
on the run page. The paper's real Step 3 (
SRT_nEphys Network Activity Features.py,
~2950 lines) computes a richer set of features and isn't wired into this runner — the
browser is the way to read it. Everything
feeding our correlation is still real, real-matched data; it's just a lighter aggregation than the
paper's own.
Workflow — paper vs. code
1. Multiscale spatial alignment
Paper says
Automatic overlay of the SRT (10x Visium H&E) and nEphys (HD-MEA) slice images into a shared coordinate space, so each SRT spot can be matched to the nEphys electrode(s) beneath it despite the two being imaged on different microscopes at different resolutions.
Code does
Multiscale Spatial Alignment.py's MEASeqX_Project.read_related_files() loads spaceranger's filtered_feature_bc_matrix.h5 + scalefactors_json.json + tissue_positions_list.csv; a second method reads the .bxr file's 3BUserInfo/ChsGroups and 3BResults/3BInfo/MeaChs2ChIDsVector for electrode positions and cluster labels, then registers the two using hardcoded pixel-per-mm constants and reference-point CSVs.
2. SRT gene expression
Paper says
Preprocess, filter, normalize, and visualize expression for a supplied gene list across the aligned tissue clusters.
Code does
SRT Gene Expression.py wraps the h5 matrix in an AnnData object and calls scanpy.pp.normalize_total / scanpy.tl.dpt; plotting functions map normalized expression back onto the tissue clusters (DG, Hilus, CA3, CA1, EC, PC).
3. nEphys network activity features × gene expression
Paper says
Calculate network activity features (firing rate, LFP amplitude/duration, synchrony, frequency-band power) from the HD-MEA recording per electrode cluster, then correlate each against SRT gene expression in the same cluster.
Code does
SRT_nEphys Network Activity Features.py — the largest file in the repo (~2950 lines) — reads the .bxr recording, runs get_fft() and related feature extractors per cluster, then correlates results against the Step 2 expression values.
4. XGBoost prediction
Paper says
Use gradient-boosted trees to predict each nEphys network activity feature from SRT gene expression alone, testing whether transcription is predictive of function.
Code does
XGBoost Algorithm Prediction.py trains sklearn's GradientBoostingClassifier (despite the module's name, this is scikit-learn's GBM, not the xgboost package) per feature, with a train/test split via sklearn.model_selection.
Note: The paper and repo call this “XGBoost,” but the actual estimator imported is sklearn.ensemble.GradientBoostingClassifier — worth knowing if you go looking for the xgboost package itself in requirements.
5. nEphys network topological metrics × gene expression
Paper says
Build a functional connectivity graph over electrode clusters and compute standard network-topology metrics, again correlated against gene expression.
Code does
SRT_nEphys Network Topological Metrics.py builds a networkx graph from .bxr-derived cluster/channel groupings (get_Groupid_nEphys, get_Groupid_nEphys_hub) and computes degree/centrality/clustering metrics per cluster.
6. Non-negative matrix factorization
Paper says
Joint NMF across the combined SRT and nEphys feature matrices to identify shared spatiotemporal patterns that neither modality reveals alone.
Code does
Non‐Negative Matrix Factorization.py calls sklearn.decomposition.NMF over the concatenated feature set built by the earlier steps.