Skip to main content

Filter and normalize

Remove low-quality cells and normalize an SDO table before clustering or differential expression.

After you load a table from a Spatial Data Object (SDO), check cell quality and normalize features before downstream analysis. The multiomics Python package runs both steps under ebsd.pp, with defaults chosen per modality.

Before you begin​

  • The multiomics Python package is installed.
  • You have an in-memory AnnData table:
import elembio_spatialdata_analysis as ebsd
from elembio_spatialdata_analysis.load import Loader

loader = Loader("/path/to/RunID.zarr.zip")
adata = loader.load_tables(table="ThreePrimeUntargeted", lazy=False)

This page uses the 3′ transcriptome table (ThreePrimeUntargeted) as the example. The same calls apply to other tables. Pass the matching target_type string.

Tabletarget_typeFilterNormalize
3′ transcriptome"ThreePrimeUntargeted"YesYes
Imaging (CellPaint and protein IF)"Imaging"YesNo single default. Scaling depends on your plate design. See Imaging tables.
OPS and specialized panels"OPS"Yes (cell area only)Not applicable. OPS is an identity readout.

Compute QC metrics​

Compute per-cell QC metrics first when you want to plot distributions before you filter. The call writes adata.obsm["qc_metrics"] in place and does not drop cells.

ebsd.pp.compute_qc_metrics(adata, "ThreePrimeUntargeted")
ebsd.vis.plot_qc_per_well(adata, metric="total_counts")

Filter cells​

To preview filtering without changing the loaded table, pass inplace=False and review the report:

filtered = ebsd.pp.filter_cells(adata, "ThreePrimeUntargeted", inplace=False)
ebsd.pp.generate_filter_report(filtered)

generate_filter_report returns a table of pass and fail counts per filter. It separates cells that fail only one filter from cells that fail several.

Default thresholds for the 3′ transcriptome table:

FilterParameterDefault
Low counts, per batchmin_quant_countBottom 2.5%
High counts, per batchmax_quant_countTop 1%
Small cells, per wellmin_quant_areaBottom 1%
Large cells, per wellmax_quant_areaTop 1%
Mitochondrial fractionmax_pct_mt0.10
Spatial artifactsfilter_spatial_artifactsOff

Quantile thresholds are fractions (for example, min_quant_count=0.05 drops the bottom 5%). Pass None to turn a filter off. Other modalities use different defaults. For example, OPS tables skip count filtering and filter on cell area only.

Note:

The defaults are a starting point, not a universal setting. Appropriate thresholds depend on cell type, assay, and run quality. Review the QC plots and the filter report for your run, then adjust thresholds before you filter in place.

When the thresholds look right, filter the working table in place:

ebsd.pp.filter_cells(adata, "ThreePrimeUntargeted")

To filter several tables from the same run together, use ebsd.pp.filter_modalities. It applies each modality's defaults and reports how each cell fares across tables.

Filter genes​

For the 3′ transcriptome, remove genes detected in fewer than 10 cells in every well:

ebsd.pp.filter_genes(adata, "ThreePrimeUntargeted")

Other modalities are targeted panels, so filter_genes does nothing for them unless you pass min_cells_per_gene or min_counts_per_gene.

Normalize​

ebsd.pp.normalize(adata, "ThreePrimeUntargeted")

The normalized, log-transformed matrix is written to adata.X, and the raw counts are kept in adata.layers["raw"]. Do not apply log1p again. The table is ready for Scanpy steps such as highly variable gene selection, PCA, and clustering.

Imaging tables​

Imaging features are fluorescence intensities and morphology measurements, not counts, so ebsd.pp.normalize does not apply to them. Transform and scale them with a z-score instead, for example an arcsinh transform followed by a robust z-score. The reference group for the z-score depends on what you are comparing:

What you are comparingReference group
Groups mixed within each well (for example, guides in a pooled screen)Each well
Conditions that differ between wells, with shared control wellsControl wells
Conditions that differ between wells, with no shared controlAll cells

Scaling each well on its own removes real differences between conditions that differ between wells. The imaging-pipeline skill in the multiomics skills covers how to choose a reference, which features to exclude, and how to handle edge cases.

Next steps​

See also​