Metadata-Version: 2.5
Name: milume
Version: 1.3.3
Summary: Milume: a thinking surface for spatial omics. A tactile, reactive Jupyter (anywidget) surface for forming intuition about spatial omics data.
Project-URL: Homepage, https://github.com/ckmah/milume
Project-URL: Repository, https://github.com/ckmah/milume
Project-URL: Issues, https://github.com/ckmah/milume/issues
Author: ckmah
License: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Requires-Python: >=3.11
Requires-Dist: anndata>=0.10
Requires-Dist: anywidget>=0.9.2
Requires-Dist: geopandas>=1.1.4
Requires-Dist: leidenalg>=0.12.0
Requires-Dist: matplotlib>=3.0
Requires-Dist: numpy>=1.24
Requires-Dist: pandas>=2.0
Requires-Dist: polars>=1.0
Requires-Dist: scipy>=1.11
Requires-Dist: shapely>=2.1.2
Requires-Dist: spatialdata>=0.2
Requires-Dist: traitlets>=5.0
Provides-Extra: demo
Requires-Dist: huggingface-hub>=0.30; extra == 'demo'
Requires-Dist: igraph>=0.11; extra == 'demo'
Requires-Dist: marimo>=0.25.1; extra == 'demo'
Requires-Dist: scanpy>=1.10; extra == 'demo'
Requires-Dist: spatialdata-io; (python_version >= '3.12') and extra == 'demo'
Provides-Extra: test
Requires-Dist: geopandas>=1.1.4; extra == 'test'
Requires-Dist: pandas>=2.0; extra == 'test'
Requires-Dist: pytest>=8.3.3; extra == 'test'
Requires-Dist: shapely>=2.1.2; extra == 'test'
Requires-Dist: spatialdata>=0.2; extra == 'test'
Description-Content-Type: text/markdown

<p align="center">
  <img src="https://raw.githubusercontent.com/ckmah/milume/main/assets/logo/milume-mark.svg" width="128" alt="Milume logo" />
</p>

# milume

**A thinking surface for spatial omics.**

Layer images, cells, and transcripts in one reactive notebook widget.

You have a spatial dataset and a hunch. milume turns a notebook cell into a tactile,
reactive surface where you can look at your tissue, select what catches your eye, and
carry that selection straight into code. Cells, landmarks and measurements stay in
plain `AnnData` / `obs_names` / geometry, so what you see on the surface is what you
analyze next.

One call opens it:

```python
import milume

w = milume.peek(adata, color="cell_type")
```

Draw selections and landmarks, color by category or gene, expand neighborhoods, and
read everything back in Python. The name comes from *mille* (thousand, as in
mille-feuille, layers) + *lume* (light).

[![Open in molab](https://marimo.io/molab-shield.svg)](https://molab.marimo.io/github/ckmah/milume/blob/main/demos/landmarks.py)

## Contents

- [Install](#install)
- [Alongside other viewers](#alongside-other-viewers)
- [API](#api)

## Install

```bash
pip install milume
```

With [uv](https://docs.astral.sh/uv/):

```bash
uv add milume
```

Contributors: see [AGENTS.md](AGENTS.md) for dev setup.

### Migrate from spatial-rx

Note: Milume was formerly `spatial-rx`. The API is unchanged; only the names moved:

```bash
pip uninstall spatial-rx
pip install milume
```

```python
import milume   # was: import spatial_rx
```

## Alongside other viewers

Milume complements other viewers. Use `milume` to form intuition: layer images, cells,
and transcripts in a notebook, select what stands out, and carry the selection straight
into your analysis code. Use heavier viewers when you need to inspect in depth.
Landmarks and selections round-trip as plain geometry and `obs_names`, so you can hand
the same regions to any viewer.

## API

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="assets/landmarks_widget_dark.png" />
  <source media="(prefers-color-scheme: light)" srcset="assets/landmarks_widget_light.png" />
  <img alt="Milume LandmarksWidget on Stellaromics colon A2 with clustered cells and drawn landmarks" src="assets/landmarks_widget_light.png" />
</picture>

`milume.peek(data, **kwargs)` opens the surface on your data and returns a widget `w`
you read results from. Full widget guide and generated API reference:
[docs site](https://ckmah.github.io/milume/docs/).

```python
import milume

w = milume.peek(adata, color="cell_type")   # AnnData with obsm["spatial"]
w = milume.peek(sdata, color="cell_type")   # SpatialData on disk: adds the 3D cube
```

| Argument | Meaning |
| --- | --- |
| `data` | `AnnData` with `obsm["spatial"]`, or a `SpatialData` |
| `color` | `obs` column to color cells by |
| `genes` | gene(s) to expose for coloring; limits the catalog on large matrices |
| `spatial_key` | `obsm` key holding coordinates (default `"spatial"`) |
| `table`, `image`, `labels` | override what is inferred from a `SpatialData` |
| `contrast_limits` | display range for the 3D image (default: its 1st to 99.5th percentile, from the coarsest pyramid level) |

`milume.peek` works on `AnnData` and on-disk `SpatialData` (including Pyxa-backed
mosaics). For **Xenium**, **Atera**, **CosMx**, and other readers, see
[Other platforms](#other-platforms) below and the
[LandmarksWidget guide](https://ckmah.github.io/milume/docs/landmarks/) on the docs site.

### Other platforms

Read with [spatialdata-io](https://github.com/scverse/spatialdata-io) as usual. **Xenium** and **Atera** work with defaults (`obsm["spatial"]` is the cell position in global coordinates). **CosMx** puts FOV-local pixels in `obsm["spatial"]`; use global coordinates instead:

```python
import spatialdata_io as sio
import milume

w = milume.peek(sio.cosmx(path), spatial_key="global")
```

- From a SpatialData, the widget finds the table, the labels element it annotates, a 3D image on the same grid, and their µm frame (override with `table=`, `image=`, `labels=`; `contrast_limits=` for the image).
- Press **I** (Inspect): the top toolbar turns into a dashed Inspect pill, hovering shows a live coarse preview of the tissue under the cursor, and a click places a 300 µm window; on release a full-resolution cube of it takes over the plot area, cells drawn as outlines colored like the map, with your landmarks on top. Shift+drag in the cube (or the **Move** tool) slides the window across the tissue; a plain drag orbits. The bottom bar holds the camera, **Adjust** (image and label display) and **Cross-section** (cuts). Esc closes the cube; Esc again, or ×, leaves Inspect. The side panels' peek tabs stay over the cube, so you can focus a category or Selection to color it.
- **Save window** on the Inspect pill adds the window as an **inspect selection**, listed in the Selections panel with a thumbnail and named by its main category and size (e.g. `Epithelial · 214`); hover a row for its cell count and category mix, click it to reopen the window. Read the inspected cells back with `w.get_obs_names(adata, "<inspect id>")` or `w.selections`.
- Hold **Space** to pan in any tool.

Read results back in Python:

| Need | Call |
| --- | --- |
| Cells in a selection | `w.get_obs_names(adata, selection_id)`, `w.assign_obs_mask(adata, key, selection_id)` |
| Landmarks as geometry | `landmarks_to_geodataframe(w.landmarks)` |
| Measure against landmarks | `distances`, `composition`, `along_positions` (+ `write_obs`) |
| Compare a subset with the tissue | `enrichment(adata, obs_names, obs_key=...)` |
| XY vs 3D neighbors | `nearest_distances(adata, seeds, obs_key=...)` (needs `obsm["spatial"]` x, y, z) |
| Map colors for other plots | `w.category_colors("cell_type")` |
| Inspect window and cube cut | `w.inspect_cx`, `w.inspect_cy`, `w.inspect_size_um`; `w.volume_cut` = x0, x1, y0, y1, z0, z1 µm (intersect X/Y with the window for the shown box) |

Large expression matrices are sent sparse; pass `genes=` to limit the gene
catalog.
