# milume Notebook widgets for spatial omics that stay in sync with Python. - Install: `pip install milume` - Documentation: https://ckmah.github.io/milume/docs/ - Repository: https://github.com/ckmah/milume - Landmarks demo (molab): https://molab.marimo.io/github/ckmah/milume/blob/main/demos/landmarks.py LandmarksWidget draws selections and landmarks on tissue coordinates in AnnData or SpatialData. --- # https://ckmah.github.io/milume/docs/api/landmarks.md # LandmarksWidget API Generated from `milume` sources. For narrative guides, see [Quickstart](../quickstart.md). ## `peek()` ```python def peek(data, **kwargs) -> LandmarksWidget ``` Open a Milume surface on AnnData or SpatialData. Main notebook entry point for interactive landmarks. Constructs `LandmarksWidget` with the same keyword arguments. #### Parameters - **data, adata** — AnnData with spatial coordinates, or SpatialData (volume inferred). `milume.peek` names this argument `data`; `LandmarksWidget` names the same argument `adata`. - **spatial_key** — `obsm` key for x/y coordinates (default `"spatial"`). - **color** — `obs` column or `var_names` gene for point colour. When omitted, the first detected categorical column is used. - **genes** — Genes to pack for view-only colouring. `None` (default) packs every `var_name`; pass a name or list to restrict the eager catalog. - **table, image, labels** — SpatialData-only overrides for the inferred table, multiscale image, and labels layer. - **contrast_limits** — SpatialData-only image contrast `(min, max)` for the Inspect cube. #### Notes The widget keeps a reference to `adata` (no `obs.copy()`, no full-`X` densify at construct). Chrome follows the notebook cell width; height starts at 720px and is resizable. Marker radius comes from median nearest-neighbor distance. Neighbourhood expand runs in the browser from coordinates; no `obsp` graphs are required. UI chrome (mode, neighbourhoods, raster controls) stays in the browser and is not part of the notebook API. Raster bins and probe scores are built client-side from the eager gene, embedding, and category packs. #### Returns - **LandmarksWidget** — Widget handle `w`. Synced traitlets include `w.selections` and `w.landmarks`; join to `adata` with `get_obs_names` / `assign_obs_mask`. #### Examples ```python import milume w = milume.peek(adata, color="cell_type") w = milume.peek(sdata) # table, labels, image inferred ``` --- ## Constructor parameters #### Parameters - **data, adata** — AnnData with spatial coordinates, or SpatialData (volume inferred). `milume.peek` names this argument `data`; `LandmarksWidget` names the same argument `adata`. - **spatial_key** — `obsm` key for x/y coordinates (default `"spatial"`). - **color** — `obs` column or `var_names` gene for point colour. When omitted, the first detected categorical column is used. - **genes** — Genes to pack for view-only colouring. `None` (default) packs every `var_name`; pass a name or list to restrict the eager catalog. - **table, image, labels** — SpatialData-only overrides for the inferred table, multiscale image, and labels layer. - **contrast_limits** — SpatialData-only image contrast `(min, max)` for the Inspect cube. #### Notes The widget keeps a reference to `adata` (no `obs.copy()`, no full-`X` densify at construct). Chrome follows the notebook cell width; height starts at 720px and is resizable. Marker radius comes from median nearest-neighbor distance. Neighbourhood expand runs in the browser from coordinates; no `obsp` graphs are required. UI chrome (mode, neighbourhoods, raster controls) stays in the browser and is not part of the notebook API. Raster bins and probe scores are built client-side from the eager gene, embedding, and category packs. ## Synced traitlets These traitlets sync between Python and the browser. UI-only chrome (modes, sliders, undoredo) is not listed. ### Selection & landmarks
| Name | Type | Default | Meaning | | --- | --- | --- | --- | | `selections` | List[Dict] | `[]` | Region dicts; join to adata via `get_obs_names` / `assign_obs_mask`. | | `landmarks` | List[Dict] | `[]` | Committed landmark annotations (round-trip with GeoDataFrame helpers). | | `selected_kind` | str | `""` | Active item kind (`""` when none). | | `selected_index` | int | `-1` | Index of active selection or landmark (`-1` when none). | | `selected_type_indices` | List[Int] | `[]` | Legend indices for multi type focus when kind is `type`. |
### Inspect (SpatialData)
| Name | Type | Default | Meaning | | --- | --- | --- | --- | | `inspect_cx` | float | `None` | Inspect cube centre X (µm); `None` until placed. | | `inspect_cy` | float | `None` | Inspect cube centre Y (µm); `None` until placed. | | `inspect_size_um` | float | `300.0` | Inspect window side length (µm; 300 µm square). | | `volume` | dict | — | SpatialData Inspect cube config (Python-set). | | `volume_cut` | List[Float] | `[]` | µm cut box; browser writes on slider release. |
### View & coloring
| Name | Type | Default | Meaning | | --- | --- | --- | --- | | `active_category` | str | `""` | Category column mirrored from colour chrome. | | `active_genes` | List[Unicode] | `[]` | View-only gene picks mirrored from chrome. |
### Advanced synced fields Usually driven from notebook methods (`set_render_mode`, `set_raster_basis`, …) or browser chrome; documented for debugging and extensions. #### Raster & render mode
| Name | Type | Default | Meaning | | --- | --- | --- | --- | | `render_mode` | str | `"points"` | points \| raster | | `raster_bin_size` | float | `0.0` | Synced widget state. | | `raster_window_radius` | float | `0.0` | Synced widget state. | | `raster_basis` | str | `"composition"` | genes \| embedding \| composition | | `raster_embedding_key` | str | `""` | Synced widget state. | | `raster_embedding_keys` | List[Unicode] | `[]` | Synced widget state. | | `raster_embedding_dims` | List[Int] | `[]` | Synced widget state. | | `raster_obs_key` | str | `""` | Synced widget state. | | `raster_gene_mode` | str | `"active"` | Synced widget state. | | `raster_origin_x` | float | `0.0` | Synced widget state. | | `raster_origin_y` | float | `0.0` | Synced widget state. | | `raster_n_cols` | int | `0` | Synced widget state. | | `raster_n_rows` | int | `0` | Synced widget state. | | `raster_n_bins` | int | `0` | Synced widget state. | | `raster_bin_rows` | str | `""` | base64 int32 | | `raster_bin_cols` | str | `""` | Synced widget state. | | `raster_bin_counts` | str | `""` | Synced widget state. | | `raster_features` | str | `""` | base64 float32 row-major | | `raster_feature_dim` | int | `0` | Synced widget state. | | `raster_feature_labels` | List[Unicode] | `[]` | Synced widget state. | | `raster_query_bin` | int | `-1` | -1 = none (pin) | | `raster_similarity_enabled` | bool | `False` | Synced widget state. | | `raster_threshold` | float | `0.0` | Synced widget state. | | `raster_status` | str | `""` | Synced widget state. | | `volume` | dict | — | SpatialData Inspect cube config (Python-set). | | `volume_label_ids` | str | `""` | base64 int32, table order | | `volume_cut` | List[Float] | `[]` | µm cut box; browser writes on slider release. |
## GeoDataFrame interchange Convert `w.landmarks` to a durable GeoPandas table (and back). Control vertices live in a JSON `vertices` column; `geometry` is a GIS proxy. The widget does not own SpatialData — assign `sdata['landmarks'] = gdf` in the notebook if needed. Requires optional `geopandas` and `shapely`. ### `landmarks_to_geodataframe()` ```python def landmarks_to_geodataframe(landmarks: list[dict] | None) ``` Convert widget landmark dicts to a GeoDataFrame. #### Parameters - **landmarks** — Landmark dicts from `w.landmarks`, or `None` / `[]` for an empty frame with contract columns (`id`, `type`, `vertices`, …). #### Returns - **geopandas.GeoDataFrame** — Proxy `geometry` plus JSON `vertices` as the round-trip source of truth. Callers assign into SpatialData themselves (e.g. `sdata["landmarks"] = gdf`). #### Examples ```python import milume w = milume.peek(adata) gdf = milume.landmarks_to_geodataframe(w.landmarks) len(gdf) ``` 0 ### `geodataframe_to_landmarks()` ```python def geodataframe_to_landmarks(gdf) ``` Convert a landmarks GeoDataFrame back to widget dicts. #### Parameters - **gdf** — GeoDataFrame produced by `landmarks_to_geodataframe`, or `None` / zero rows. #### Returns - **list[dict]** — Widget landmark dicts suitable for `w.landmarks = ...`. Empty input yields `[]`. #### Examples ```python import milume w = milume.peek(adata) w.landmarks = milume.geodataframe_to_landmarks( ``` ... milume.landmarks_to_geodataframe(w.landmarks) ... ) ## Methods Notebook helpers on the widget handle returned by `milume.peek`. ### `set_points()` ```python def set_points(x: Any, y: Any, *, color: Any = None, color_map: dict[str, str] | None = None, legend_title: str | None = None, continuous_range: tuple[str, str] | None = None) -> None ``` Replace scatter points/colors. ### `set_expression()` ```python def set_expression(expr: Any) -> None ``` Replace the eager gene catalog/matrix from a table (view-only). Packs all columns immediately for the browser gene picker. ### `set_render_mode()` ```python def set_render_mode(mode: str) -> None ``` Switch canvas between point scatter and spatial raster bins. ### `set_raster_basis()` ```python def set_raster_basis(*, genes: str | None = None, embedding: str | None = None, composition: str | None = None) -> None ``` Select bin aggregation basis (genes / embedding / composition). Pathway / `obs` score columns are deferred (plan slice A); use `raster_obs_key` later via the same mean-into-bin path. Bin feature matrices are built in the browser; this only updates traits. ### `clear_raster_query()` ```python def clear_raster_query() -> None ``` Clear the pinned similarity query bin. ### `set_color()` ```python def set_color(color: Any, *, color_map: dict[str, str] | None = None, legend_title: str | None = None, continuous_range: tuple[str, str] | None = None) -> None ``` Update point colors without changing x/y. ### `category_colors()` ```python def category_colors(column: str | None = None) -> dict[str, str] ``` Label -> hex colour of a categorical column, as the map draws it. Defaults to the active category. Use it to colour other views (plots, the cube) the same way as the points. ### `clear_selections()` ```python def clear_selections() -> None ``` Remove every selection from the widget. ### `clear_landmarks()` ```python def clear_landmarks() -> None ``` Remove every landmark from the widget. ### `clear()` ```python def clear() -> None ``` Remove all selections and landmarks. ### `select_type()` ```python def select_type(index: int) -> None ``` Focus one category group in the active set (replaces a multi-select union). The browser may set `selected_type_indices` to several groups; this helper is the notebook path for single-group focus without fighting front-end sync. ### `get_focus_obs_names()` ```python def get_focus_obs_names(adata: Any, *, spatial_key: str = 'spatial') -> 'np.ndarray' ``` `obs_names` for the current type or selection focus (multi-type union). ### `get_obs_names()` ```python def get_obs_names(adata: Any = None, selection_id: str | None = 'all', *, spatial_key: str = 'spatial') -> 'np.ndarray' ``` `obs_names` of cells inside `selection_id` (durable join key). Uses `selection_mask` (geometry or stored `point_indices`). Neighborhood expand is client-side; promote freezes membership into `point_indices` before syncing. With no `adata` the widget's own coordinates and names are used, so the result cannot depend on which `spatial_key` you pass. ### `assign_obs_mask()` ```python def assign_obs_mask(adata: Any, key: str, selection_id: str | None = 'all', *, spatial_key: str = 'spatial') -> None ``` Write a boolean column on `adata.obs` for the current selection. # https://ckmah.github.io/milume/docs/index.md # milume **Explore spatial biology in your notebook.** Layer images, cells, and transcripts in 3D, in one reactive widget. ```python import milume w = milume.peek(adata, color="cell_type") ``` ## Agents - [llms.txt](https://ckmah.github.io/milume/llms.txt) — page index - [llms-full.txt](https://ckmah.github.io/milume/llms-full.txt) — bundled markdown - Markdown mirrors: append `.md` to doc paths (for example `install.md`) # https://ckmah.github.io/milume/docs/install.md # Install ## pip ```bash pip install milume ``` Requires Python 3.11+. ## From source ```bash uv sync --extra demo --group dev ``` Use the `demo` extra for marimo notebooks and spatial-omics helpers used by `demos/`. ## Quickstart Format data as `AnnData` with coordinates in `obsm["spatial"]`, then open the widget: ```python import milume w = milume.peek(adata, color="cell_type") w # display in the notebook ``` Full walkthrough: [Quickstart](quickstart.md). ## Next - [Quickstart](quickstart.md) - [LandmarksWidget API](api/landmarks/) - [Open Landmarks demo](https://molab.marimo.io/github/ckmah/milume/blob/main/demos/landmarks.py) # https://ckmah.github.io/milume/docs/quickstart.md # Quickstart LandmarksWidget is the interactive surface for tissue coordinates in `AnnData` or `SpatialData`. Open it with `milume.peek`, draw a selection, then read hits back into the notebook with synced traitlets. ![LandmarksWidget](assets/landmarks_widget_dark.png) ## Open the widget Coordinates must live in `obsm["spatial"]` (or the table’s spatial key on SpatialData): ```python import milume w = milume.peek(adata, color="cell_type") w = milume.peek(sdata, color="cell_type") # table, labels, image inferred ``` ## Use selections downstream `w.selections` updates on every edit in the browser. After you draw a region, join to observations by name (not row index): ```python import pandas as pd sel_id = w.selections[-1]["id"] hits = w.get_obs_names(adata, sel_id) adata.obs["in_lasso"] = adata.obs_names.isin(hits) # or write a boolean column in one call w.assign_obs_mask(adata, "in_lasso", selection_id=sel_id) summary = pd.DataFrame({"cell_type": adata.obs.loc[hits, "cell_type"].value_counts()}) ``` Landmarks (`w.landmarks`) are durable geometric annotations; selections are analysis regions. See the [API reference](api/landmarks/) for traitlets and methods. # https://ckmah.github.io/milume/docs/tools/color-layers.md # Color & layers
The color panel switches from category to genes to embed, and the map recolors each time.
Switch the color panel between category, genes and embed; the map recolors to match. {: .ml-caption } ```python import milume w = milume.peek(adata, color="cell_type", genes=["GeneA", "GeneB", "GeneC"]) w.active_category, w.active_genes ``` ## Color tabs The right panel has three pill tabs. They have no icons, so the table names each tab.
| | Tab | What it colors by | |---|---|---| | 1 | category | An `obs` category column. Expand it () to toggle single subcategories. | | 2 | genes | Expression of the picked genes, with ridgeline densities and a blend legend. Pick genes in the field under the legend. | | 3 | embed | An embedding (for example `X_pca`) mapped to RGB, with an RGB-cube legend. Choose the embedding from the dropdown. |
### genes
The genes tab with three genes picked: ridgeline densities per gene, a blend legend, the gene chips, and Shared scale and log scale switches. The genes tab with three genes picked: ridgeline densities per gene, a blend legend, the gene chips, and Shared scale and log scale switches.
### embed
The embed tab showing an RGB-cube legend and the embedding dropdown, with cells colored by their embedding. The embed tab showing an RGB-cube legend and the embedding dropdown, with cells colored by their embedding.
## Points ⇄ Raster
The map flips from individual points to square raster bins and back.
| | Control | What it does | |---|---|---| | | points/raster | Flip between drawing each cell as a point and aggregating cells into raster bins (). The icon shows the current mode. |
## Layers list The left panel lists every selection and landmark.
The layers list with one selection row and one landmark row; amber callouts mark the two rows and their Layer actions buttons. The layers list with one selection row and one landmark row; amber callouts mark the two rows and their Layer actions buttons.
| | Control | What it does | |---|---|---| | 1 | Selections | Click a row to make that selection active. | | 2 | Landmarks | Click a row to select the landmark and open its context toolbar. | | | Layer actions | Hide / Show, Rename, Delete. | | | Collapse left panel ([) | Hide the list; see [Navigate](navigate.md). |
## Python ```python w.set_render_mode("raster") # or "points" w.category_colors("cell_type") # {label: hex}, as the map draws it ``` See [Methods](../api/landmarks.md#methods) for `set_color`, `set_raster_basis` and the rest. ## Related
Inspect in 3D →Cube orbit and slice Landmarks →Durable annotations Probe →Raster similarity
# https://ckmah.github.io/milume/docs/tools/index.md # Tools & modes Every tool and mode in the milume widget, with its icon and keyboard shortcut.
The widget with six numbered amber callouts: the top toolbar (navigate, selection and landmark tools, Inspect, Probe) and the right color panel. Measure has no on-canvas control; it runs from Python. The widget with six numbered amber callouts: the top toolbar (navigate, selection and landmark tools, Inspect, Probe) and the right color panel. Measure has no on-canvas control; it runs from Python.
1 Navigate VSelect, pan, zoom, minimap, rulers, full screen. 2 Select regions LLasso, Square, Circle and Shape regions you read back in Python. 3 Landmarks 1–4Points, lines, splines and shapes with buffers. 4 Color & layersColor by category, gene or embedding; points or raster; the layers list. 5 Inspect in 3D IA 3D cube of image, labels and transcripts you can slice and orbit. 6 Probe PHover or pin a bin to see raster similarity. MeasureDistances, composition and enrichment from Python.
Start with `milume.peek(...)`: see the [Quickstart](../quickstart.md). # https://ckmah.github.io/milume/docs/tools/inspect-3d.md # Inspect in 3D
A 3D cube of the tissue volume, with the image and cell labels layered on, swings about 112° and back as the pointer drags the camera.
Place a cube window on the map, then orbit and slice image, labels and transcripts together. {: .ml-caption } ```python import milume w = milume.peek(sdata, image="mosaic", labels="cells") # press I, then click the tissue w.volume_cut # cut-plane state after you slide the cross-section ``` ## Entering Inspect Press I or click Inspect, then click the tissue to place a cube window. The top pill shows the window size, Save window and Exit Inspect (Esc).
The Inspect cube open: the top pill shows Exit, Inspect with the window size in µm, Save window, and full screen; the bottom toolbar has camera presets and layer toggles. The Inspect cube open: the top pill shows Exit, Inspect with the window size in µm, Save window, and full screen; the bottom toolbar has camera presets and layer toggles.
## Inspect toolbar
The Inspect toolbar under the cube. Amber callouts: 1 camera Top / Iso / Side, 2 Move and Reset view, 3 Image, 4 Labels, 5 Points (Cross-section sits to their right). The Inspect toolbar under the cube. Amber callouts: 1 camera Top / Iso / Side, 2 Move and Reset view, 3 Image, 4 Labels, 5 Points (Cross-section sits to their right).
| | Control | What it does | |---|---|---| | 1 | Top / Iso / Side | Snap the camera to a top, oblique or side view. | | 2 | Move | Drag to pan the cube window on the map. | | | Reset view | Return the cube camera to its default. | | 3 | Image | Show or hide the image layer. Right-click for palette, contrast, alpha, gamma and projection. | | 4 | Labels | Show or hide the labels layer. Right-click for projection and fill. Cell outlines stay on. | | 5 | Points | Show or hide the points layer. | | | Cross-section | Slide a cut plane through the cube. |
## Cross-section
The pointer drags the cut slider: a cross-section plane sweeps through the cube and back; cell outlines stay visible on the cut.
## Python ```python w.inspect_cx, w.inspect_cy, w.inspect_size_um # window center and size w.volume_cut # cut-plane state ``` See [Synced traitlets](../api/landmarks.md#synced-traitlets) for the volume fields, and pass `image=` / `labels=` to `milume.peek` in [Constructor parameters](../api/landmarks.md#constructor-parameters). ## Related
Navigate →Pan, zoom, minimap Color & layers →What colors the cells Probe →Raster similarity
# https://ckmah.github.io/milume/docs/tools/landmarks.md # Landmarks
A spline landmark is drawn point by point, a buffer is widened around it, then the landmark is deleted.
Click to place landmark vertices, press Enter to finish, then widen a buffer around the line. {: .ml-caption } ```python import milume w = milume.peek(adata) # press 3, click points, Enter w.landmarks # list of landmark dicts ``` Landmarks are durable geometric annotations (points, lines, splines, shapes). Selections are analysis regions; see [Select regions](select-regions.md). ## Draw tools The four landmark tools share one toolbar button. Click it to arm the last-used tool, or right-click it to pick another.
| | Control | What it does | |---|---|---| | | Point (1) | Click once to place a point. | | | Line (2) | Click vertices of a polyline; press Enter to finish. | | | Spline (3) | Click control points of a smooth curve; press Enter to finish. | | | Shape (4) | Click vertices of a closed shape; press Enter to finish. | | | Node (N) | Drag, insert or delete vertices of the selected landmark. |
## Context toolbar Click a landmark with Select (V) to open its toolbar at the bottom of the map.
A spline landmark with a wide buffer and its context toolbar. Amber callouts: 1 Color, 2 Tension, 3 the Buffer width slider, 4 Make selection from buffer, 5 Convert type. A spline landmark with a wide buffer and its context toolbar. Amber callouts: 1 Color, 2 Tension, 3 the Buffer width slider, 4 Make selection from buffer, 5 Convert type.
| | Control | What it does | |---|---|---| | | Line style | Toggle solid or dashed. | | 1 | Color | Six swatches plus a custom color. | | 2 | Tension | How tightly a spline follows its control points. | | 3 | Buffer | Signed buffer width: negative is left or in, positive is right or out. Both sides makes it symmetric; Reset buffer sets it to 0. | | 4 | Make selection from buffer | Turn the cells inside the buffer into a selection. | | | Reverse | Flip the direction of a line or spline. | | 5 | Convert type | Convert between Line, Spline and Shape. |
## Right-click menu
Right-click menu on a spline landmark with Hide, Rename, Duplicate and Delete. Right-click menu on a spline landmark with Hide, Rename, Duplicate and Delete.
| | Control | What it does | |---|---|---| | | Hide / Show | Toggle the landmark's visibility. | | | Rename | Give the landmark a new name. | | | Duplicate | Copy the landmark. | | | Delete (⌫) | Remove the landmark. | | | Insert node | In Node mode, add a vertex where you right-clicked. | | | Delete node (⌫) | In Node mode, remove the active vertex. |
## Python `w.landmarks` updates on every edit. Convert to and from a GeoDataFrame (needs `geopandas`): ```python from milume import landmarks_to_geodataframe, geodataframe_to_landmarks gdf = landmarks_to_geodataframe(w.landmarks) w.landmarks = geodataframe_to_landmarks(gdf) ``` See [GeoDataFrame interchange](../api/landmarks.md#geodataframe-interchange) in the API reference. ## Related
Select regions →Lasso, square, circle Navigate →Pan, zoom, minimap Measure →Distances and composition
# https://ckmah.github.io/milume/docs/tools/measure.md # Measure Quantify landmarks and selections in Python. Every function returns a tidy `pandas.DataFrame` with one row per cell (or per group).
Left: the widget with one spline landmark and a wide buffer. Right: a histogram of each cell's distance to that landmark. Left: the widget with one spline landmark and a wide buffer. Right: a histogram of each cell's distance to that landmark.
## Setup Draw a landmark (for example a spline, key 3) and a selection, then build a GeoDataFrame from the widget (needs `geopandas`): ```python import milume from milume import landmarks_to_geodataframe w = milume.peek(adata, color="cell_type") gdf = landmarks_to_geodataframe(w.landmarks) sel = w.get_obs_names(adata, w.selections[-1]["id"]) ``` `obs_key` names the `obs` column to group cells by; every output has a `group` column with its values. When `obsm["spatial"]` has x, y and z, the outputs also carry `z` and `z_bin` (1 µm bins by default, `z_bin_size=None` turns them off). ## distances `distances(adata, landmarks, *, obs_key, spatial_key="spatial", obs_names=None, z_bin_size=1.0)` Per-cell distance to each landmark's geometry. A line or spline with a buffer only measures cells inside the buffer. ```python from milume import distances d = distances(adata, gdf, obs_key="cell_type") d[["obs_name", "landmark_id", "group", "distance"]] ```
Histogram of cell counts against distance to the landmark. Histogram of cell counts against distance to the landmark.
## composition `composition(adata, landmarks, *, obs_key, spatial_key="spatial", obs_names=None, z_bin_size=1.0)` Counts and proportions of each group inside a shape or a line's buffer. ```python from milume import composition c = composition(adata, gdf, obs_key="cell_type") c.groupby("group")["count"].sum() ```
Horizontal bars of cell counts per group inside the landmark buffer. Horizontal bars of cell counts per group inside the landmark buffer.
## along_positions `along_positions(adata, landmarks, *, obs_key, spatial_key="spatial", obs_names=None, radius=None, z_bin_size=1.0)` Projects cells onto a line or spline as a normalized arc position `s` from 0 (start) to 1 (end). ```python from milume import along_positions s = along_positions(adata, gdf, obs_key="cell_type") s[["obs_name", "group", "s", "distance"]] ```
Histogram of cell counts against position along the landmark, from 0 to 1. Histogram of cell counts against position along the landmark, from 0 to 1.
## enrichment `enrichment(adata, obs_names, *, obs_key, background=None)` Group composition of a cell subset (such as a selection) against a background, all cells by default. ```python from milume import enrichment e = enrichment(adata, sel, obs_key="cell_type") e[["group", "count", "proportion", "background_proportion", "log2_enrichment"]] ```
Horizontal bars of log2 enrichment per group; groups above zero are amber, below zero teal. Horizontal bars of log2 enrichment per group; groups above zero are amber, below zero teal.
## nearest_distances `nearest_distances(adata, seeds, *, obs_key, spatial_key="spatial", obs_names=None)` Distance from every cell to its nearest seed cell, in XY and in XYZ. Needs x, y, z in `obsm["spatial"]`. ```python from milume import nearest_distances n = nearest_distances(adata, sel, obs_key="cell_type") n[["obs_name", "group", "seed", "distance_xy", "distance_xyz"]] ```
Histogram of cell counts against XY distance to the nearest selected cell. Histogram of cell counts against XY distance to the nearest selected cell.
## write_obs `write_obs(adata, df, column, value_col, *, obs_name_col="obs_name")` Writes a per-cell measurement back into `adata.obs`, aligned by `obs_name`. Cells missing from `df` stay NaN. ```python from milume import write_obs write_obs(adata, d, "dist_to_landmark", "distance") adata.obs["dist_to_landmark"].describe() ``` The new column works like any other `obs` column, for example in `scanpy` plots or as input to the next `peek`. ## Related
Landmarks →Durable annotations Select regions →Lasso, square, circle API reference →Traitlets and methods
# https://ckmah.github.io/milume/docs/tools/navigate.md # Navigate
The map zooms in on the cells, pans across them, then returns to the full view.
Scroll to zoom, drag to pan, and press 0 to fit the whole tissue back in view. {: .ml-caption } ```python import milume w = milume.peek(sdata) # press H to pan, = / - to zoom, 0 to reset ``` ## Controls
| | Control | What it does | |---|---|---| | | Select (V) | Default tool. Click a selection or landmark to open its context toolbar. | | | Move (H) | Drag anywhere to pan. Hold Space to pan with any tool. | | | Zoom in (=) | Zoom in one step. The scroll wheel also zooms. | | | Zoom out (-) | Zoom out one step. | | | Reset view (0) | Fit all cells in view. | | | Canvas rulers | Show coordinate rulers along the top and left edges. | | | Full screen (F) | Fill the screen with the widget; Exit full screen returns. | | | Collapse left / right panel ([ / ]) | Hide a side panel; Show left panel brings it back. | | | Minimap | Drag the viewport box to pan; scroll on it to zoom. |
### Minimap
The minimap viewport box is dragged up and to the left, and the map pans with it.
### Canvas rulers
Canvas rulers switched on: coordinate ticks along the top and left edges of the map, with the rulers toggle highlighted in the top-right toolbar. Canvas rulers switched on: coordinate ticks along the top and left edges of the map, with the rulers toggle highlighted in the top-right toolbar.
## Keyboard shortcuts | Action | Keys | |---|---| | Select | V | | Move | H | | Pan while held (any tool) | Space | | Zoom in / out | = / - | | Reset view | 0 | | Full screen | F | | Collapse left / right panel | [ / ] | | Deselect, cancel a draft | Esc | ## Python The active tool and the rulers are synced traitlets, so you can set them from a cell: ```python w.mode = "move" # same as pressing H w.show_rulers = True ``` See [Synced traitlets](../api/landmarks.md#synced-traitlets) for the full list. ## Related
Select regions →Lasso, square, circle Landmarks →Durable annotations Color & layers →What colors the cells
# https://ckmah.github.io/milume/docs/tools/probe.md # Probe
The probe ring moves in a figure-eight over the raster bins, and the bins recolor by similarity to the bin under the ring.
Hover a bin with Probe on; similar bins light up. Click to pin the query. {: .ml-caption } Probe compares raster bins, so switch the map to raster first ( points/raster; see [Color & layers](color-layers.md)). ```python import milume w = milume.peek(adata, genes=["GeneA", "GeneB", "GeneC"]) w.set_render_mode("raster") # then press P and hover ``` ## Controls
| | Control | What it does | |---|---|---| | | Probe (P) | Hover a bin to color every bin by its similarity to it; click to pin that bin. | | | Similarity legend | Color scale from 0 (dissimilar) to 1 (identical), shown in the right panel while Probe is on. | | | Esc | Clear the probe. |
With the genes tab active, similarity uses the picked genes; pick at least one gene first, or hovering does nothing. ## Pinned query
A pinned probe ring on the raster, with the Similarity legend from 0 to 1 in the right panel under the gene densities. A pinned probe ring on the raster, with the Similarity legend from 0 to 1 in the right panel under the gene densities.
## Python ```python w.raster_similarity_enabled = True # similarity coloring without the Probe tool w.raster_threshold = 0.5 # similarity cutoff for the map w.raster_query_bin # pinned bin index, -1 when none w.clear_raster_query() ``` See [Synced traitlets](../api/landmarks.md#synced-traitlets) and [Methods](../api/landmarks.md#methods). ## Related
Color & layers →Points or raster Navigate →Pan, zoom, minimap Inspect in 3D →Cube orbit and slice
# https://ckmah.github.io/milume/docs/tools/select-regions.md # Select regions
A lasso is drawn around a group of cells; the cells inside stay bright, a selection row appears in the layers list, then the selection is removed.
Draw a lasso, square, circle or shape around cells, then read the selection back in Python. {: .ml-caption } ```python import milume w = milume.peek(adata) # press L, then drag around some cells sel_id = w.selections[-1]["id"] ``` ## Controls The four selection tools share one toolbar button. Click it to arm the last-used tool, or right-click it to pick another.
| | Control | What it does | |---|---|---| | | Lasso (L) | Drag a freehand outline. | | | Square (R) | Drag a rectangle. | | | Circle (O) | Drag an ellipse. | | | Shape (G) | Click vertices of a polygon; press Enter to finish. | | | Shape menu (right-click) | Choose which of the four tools the button arms. | | | Layer actions | Rename, hide or delete the selection from the layers list. |
The selection tool button right-clicked, showing a menu with Lasso, Square, Circle and Shape, each with its icon. The selection tool button right-clicked, showing a menu with Lasso, Square, Circle and Shape, each with its icon.
### Square and circle
A square selection is dragged out over the cells, held, then removed.
A circle selection is dragged out over the cells, held, then removed.
### While a selection is active The cells inside stay at full color and the right panel counts only them. A small toolbar at the bottom of the map offers Radius neighborhood and k-NN neighborhood around the selection, with Make selection to keep that neighborhood as a new selection. Press ⌫ or Delete to remove the active selection.
A square selection with k-NN neighborhood on; the k slider moves up and back and the ring of neighboring cells grows and shrinks.
## Python `w.selections` updates on every edit. Join hits to observations by name: ```python hits = w.get_obs_names(adata, sel_id) # obs names inside w.assign_obs_mask(adata, "in_region", selection_id=sel_id) w.clear_selections() ``` See [Methods](../api/landmarks.md#methods) for the full signatures. ## Related
Navigate →Pan, zoom, minimap Landmarks →Durable annotations Measure →Distances and composition