alterlab-cellxgene
Query the CZ CELLxGENE Census (200M+ cells) programmatically via cellxgene-census and TileDB-SOMA, slicing expression by tissue, disease, or cell type and returning AnnData. Use when pulling reference single-cell RNA-seq data from the largest curated public atlas, running populat
Install
npx skills add https://github.com/AlterLab-IEU/AlterLab-Academic-Skills/tree/main/skills/bioinformatics/alterlab-cellxgene
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install alterlab-ieu-alterlab-academic-skills@llmmart
git clone https://github.com/AlterLab-IEU/AlterLab-Academic-Skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole alterlab-ieu/alterlab-academic-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
CZ CELLxGENE Census
Overview
The CZ CELLxGENE Census provides programmatic, versioned access to standardized single-cell genomics data from CZ CELLxGENE Discover. The 2025-11-08 LTS release holds 162,025,130 human and 46,299,127 mouse cells with standardized metadata (cell types, tissues, diseases, donors), raw gene expression matrices, pre-calculated embeddings, and integration with PyTorch, scanpy, and other analysis tools. Cell counts grow with each release — read them from census["census_info"]["summary"] rather than quoting a number in a methods section.
When to Use This Skill
Use this skill when:
- Querying single-cell expression data by cell type, tissue, or disease
- Exploring available single-cell datasets and metadata
- Training machine learning models on single-cell data
- Performing large-scale cross-dataset analyses
- Integrating Census data with scanpy or other analysis frameworks
- Computing statistics across millions of cells
- Accessing pre-calculated embeddings or model predictions
For analyzing your own dataset (not the reference atlas), use scanpy or scvi-tools instead.
Installation
uv pip install cellxgene-census
# For PyTorch ML workflows (loaders moved out of cellxgene-census):
uv pip install tiledbsoma-ml
Core Workflow
- Open the Census with a context manager; pin
census_versionfor reproducibility."stable"is the alias for the most recent LTS release (2025-11-08 as of 2026-09) and"latest"tracks the weekly build; LTS releases are kept available for at least five years. - Explore metadata first (
get_obs/ datasets summary) to understand what's available — always filteris_primary_data == Trueto avoid duplicate cells. - Estimate query size before loading expression. < 100k cells →
get_anndata()(in-memory); larger →axis_query()out-of-core iteration. - Query expression with
obs_value_filter(cells) andvar_value_filter(genes); select only theobs_column_namesyou need. - Downstream: hand the returned AnnData to scanpy, or stream batches into a PyTorch dataloader for ML.
Minimal skeleton:
import cellxgene_census
with cellxgene_census.open_soma(census_version="2025-11-08") as census: # pinned LTS
adata = cellxgene_census.get_anndata(
census=census,
organism="Homo sapiens",
obs_value_filter="cell_type == 'B cell' and tissue_general == 'lung' and is_primary_data == True",
)
Does NOT Trigger
| Scenario | Use Instead |
|---|---|
| Normalize/cluster/UMAP an AnnData you already have | alterlab-scanpy |
Concatenate or wrangle local .h5ad/zarr files |
alterlab-anndata |
| Train a deep generative model / batch-integrate your own data | alterlab-scvi-tools |
| Browse or download a specific GEO/ArrayExpress accession | alterlab-geo |
One-off gget cellxgene lookup from the CLI |
alterlab-gget |
Routing Guidance
- Small/medium query (fits in RAM) →
get_anndata(). Seereferences/querying_expression.md. - Query exceeds RAM →
axis_query()with chunked iteration and incremental stats. Seereferences/querying_expression.md. - Training ML models →
tiledbsoma_mlPyTorch dataloader /ExperimentDataset. Seereferences/ml_and_scanpy.md. - Standard scanpy analysis / multi-tissue integration → see
references/ml_and_scanpy.md. - Need full schema, all metadata fields, or filter-syntax details →
references/census_schema.md.
Reference Index
references/querying_expression.md— Opening the Census, exploring metadata, small/mediumget_anndata()queries, and large out-of-coreaxis_query()processing with incremental statistics.references/ml_and_scanpy.md—tiledbsoma_mlPyTorch dataloader /ExperimentDatasettrain-test splits, scanpy integration, multi-dataset/tissue integration (anndata.concat), and four worked use cases.references/best_practices_and_troubleshooting.md— Primary-data filtering, version pinning, query-size estimation,tissue_generalvstissue, presence matrices, the full obs/var metadata field list, and a troubleshooting guide.references/census_schema.md— Census data structure, all metadata fields, value-filter syntax/operators, SOMA object types, and data inclusion criteria.references/common_patterns.md— Extras beyond the core recipes: incremental (Welford) variance out-of-core, ontology-term filtering, batch-processing sweeps, and a common-pitfalls list.
Part of the AlterLab Academic Skills suite.
Files (alterlab-academic-skills)
-
evals
-
evals.json 4.8 KB
{ "skill": "alterlab-cellxgene", "evals": [ { "id": "query-marker-genes-by-celltype", "prompt": "From the CZ CELLxGENE Census, pull human T cells and B cells but only the expression of CD4, CD8A, and CD19, and return it as an AnnData I can work with. Make sure I'm not double-counting cells.", "expected_output": "Invokes alterlab-cellxgene: opens the census with cellxgene_census.open_soma(), calls get_anndata with organism='Homo sapiens', a var_value_filter on feature_name for the three genes, and an obs_value_filter on cell_type that includes is_primary_data == True to avoid duplicates.", "assertions": [ { "type": "should_trigger", "value": true }, { "type": "output_contains", "value": "is_primary_data" }, { "type": "behavior", "value": "Uses cellxgene_census.get_anndata with var_value_filter on feature_name and an obs_value_filter, and includes is_primary_data == True." } ] }, { "id": "explore-cell-types-in-tissue", "prompt": "Before I download anything, I just want to know what cell types are available in human brain in the Census and roughly how many cells of each. What's the lightweight way to check?", "expected_output": "Invokes alterlab-cellxgene: uses the explore-then-query pattern with cellxgene_census.get_obs (not get_anndata) on tissue_general == 'brain' and is_primary_data == True, selecting only column_names=['cell_type'] and doing value_counts to summarize without loading expression.", "assertions": [ { "type": "should_trigger", "value": true }, { "type": "output_contains", "value": "get_obs" }, { "type": "behavior", "value": "Queries metadata only via get_obs and value_counts before any expression download, filtering for primary data." } ] }, { "id": "out-of-core-large-query", "prompt": "I need mean expression of FOXP2, TBR1, and SATB2 across every primary brain cell in the Census, but that's way more cells than my 48 GB machine can hold. How do I compute it without loading everything into memory?", "expected_output": "Invokes alterlab-cellxgene: uses out-of-core processing via axis_query() with soma.AxisQuery obs/var filters, iterates query.X('raw').tables() in batches accumulating sum and count incrementally to compute the mean, rather than get_anndata.", "assertions": [ { "type": "should_trigger", "value": true }, { "type": "output_contains", "value": "axis_query" }, { "type": "behavior", "value": "Recommends out-of-core iteration over X tables/batches instead of loading the full query into an AnnData." } ] }, { "id": "reproducible-census-version", "prompt": "I'm writing the methods section of a paper that uses Census data. How do I make sure my exact same query is reproducible months from now when the Census has been updated?", "expected_output": "Invokes alterlab-cellxgene: advises pinning the release by passing census_version (e.g. open_soma(census_version='2025-11-08'), the current LTS build) so the analysis is reproducible across Census updates, and recording that version in the methods.", "assertions": [ { "type": "should_trigger", "value": true }, { "type": "output_contains", "value": "census_version" }, { "type": "behavior", "value": "Recommends explicitly pinning census_version for reproducibility rather than relying on the default 'stable' release." } ] }, { "id": "near-miss-scanpy", "prompt": "I already loaded a lung dataset into an AnnData object. Normalize it, find highly variable genes, run PCA and UMAP, and cluster it with Leiden so I can see the cell populations.", "expected_output": "Should NOT trigger alterlab-cellxgene. This is downstream single-cell analysis (normalize, HVG, PCA, UMAP, Leiden) on an already-loaded dataset, which is alterlab-scanpy territory. cellxgene-census is for pulling reference data from the public atlas and defers analysis of your own dataset to scanpy.", "assertions": [ { "type": "should_not_trigger", "value": true }, { "type": "output_contains", "value": "scanpy" } ] }, { "id": "near-miss-anndata", "prompt": "I have three local .h5ad files on disk and I want to concatenate them along the cells axis with an inner join on genes and a batch label. No public data involved.", "expected_output": "Should NOT trigger alterlab-cellxgene. Concatenating local .h5ad files with ad.concat (axis, join, label) is alterlab-anndata's data-format territory. cellxgene-census is specifically for querying the public CZ CELLxGENE Census, not wrangling local files.", "assertions": [ { "type": "should_not_trigger", "value": true }, { "type": "output_contains", "value": "anndata" } ] } ] }
-
-
references
-
best_practices_and_troubleshooting.md 4.3 KB
# Best Practices, Metadata Fields, and Troubleshooting ## Key Concepts and Best Practices ### Always Filter for Primary Data Unless analyzing duplicates, always include `is_primary_data == True` in queries to avoid counting cells multiple times: ```python obs_value_filter="cell_type == 'B cell' and is_primary_data == True" ``` ### Specify Census Version for Reproducibility Pin the Census version in anything you will report on, and record it in your methods: ```python census = cellxgene_census.open_soma(census_version="2025-11-08") # an LTS build ``` `"stable"` resolves to the most recent **LTS** release (2025-11-08 as of 2026-09) and `"latest"` to the weekly build, so both move under you. CZI commits to keeping LTS releases publicly available for at least five years; the LTS series so far is 2023-05-15, 2023-07-25, 2023-12-15, 2024-07-01, 2025-01-30 and 2025-11-08. Cell counts differ between them — read `census["census_info"]["summary"]` for the build you actually opened. ### Estimate Query Size Before Loading For large queries, first check the number of cells to avoid memory issues: ```python # Get cell count metadata = cellxgene_census.get_obs( census, "homo_sapiens", value_filter="tissue_general == 'brain' and is_primary_data == True", column_names=["soma_joinid"] ) n_cells = len(metadata) print(f"Query will return {n_cells:,} cells") # If too large (>100k), use out-of-core processing ``` ### Use tissue_general for Broader Groupings The `tissue_general` field provides coarser categories than `tissue`, useful for cross-tissue analyses: ```python # Broader grouping obs_value_filter="tissue_general == 'immune system'" # Specific tissue obs_value_filter="tissue == 'peripheral blood mononuclear cell'" ``` ### Select Only Needed Columns Minimize data transfer by specifying only required metadata columns: ```python obs_column_names=["cell_type", "tissue_general", "disease"] # Not all columns ``` ### Check Dataset Presence for Gene-Specific Queries When analyzing specific genes, verify which datasets measured them: ```python presence = cellxgene_census.get_presence_matrix( census, "homo_sapiens", var_value_filter="feature_name in ['CD4', 'CD8A']" ) ``` ### Two-Step Workflow: Explore Then Query First explore metadata to understand available data, then query expression: ```python # Step 1: Explore what's available metadata = cellxgene_census.get_obs( census, "homo_sapiens", value_filter="disease == 'COVID-19' and is_primary_data == True", column_names=["cell_type", "tissue_general"] ) print(metadata.value_counts()) # Step 2: Query based on findings adata = cellxgene_census.get_anndata( census=census, organism="Homo sapiens", obs_value_filter="disease == 'COVID-19' and cell_type == 'T cell' and is_primary_data == True", ) ``` ## Available Metadata Fields ### Cell Metadata (obs) Key fields for filtering: - `cell_type`, `cell_type_ontology_term_id` - `tissue`, `tissue_general`, `tissue_ontology_term_id` - `disease`, `disease_ontology_term_id` - `assay`, `assay_ontology_term_id` - `donor_id`, `sex`, `self_reported_ethnicity` - `development_stage`, `development_stage_ontology_term_id` - `dataset_id` - `is_primary_data` (Boolean: True = unique cell) ### Gene Metadata (var) - `feature_id` (Ensembl gene ID, e.g., "ENSG00000161798") - `feature_name` (Gene symbol, e.g., "FOXP2") - `feature_length` (Gene length in base pairs) ## Troubleshooting ### Query Returns Too Many Cells - Add more specific filters to reduce scope - Use `tissue` instead of `tissue_general` for finer granularity - Filter by specific `dataset_id` if known - Switch to out-of-core processing for large queries ### Memory Errors - Reduce query scope with more restrictive filters - Select fewer genes with `var_value_filter` - Use out-of-core processing with `axis_query()` - Process data in batches ### Duplicate Cells in Results - Always include `is_primary_data == True` in filters - Check if intentionally querying across multiple datasets ### Gene Not Found - Verify gene name spelling (case-sensitive) - Try Ensembl ID with `feature_id` instead of `feature_name` - Check dataset presence matrix to see if gene was measured - Some genes may have been filtered during Census construction ### Version Inconsistencies - Always specify `census_version` explicitly - Use same version across all analyses - Check release notes for version-specific changes -
census_schema.md 5.6 KB
# CZ CELLxGENE Census Data Schema Reference ## Overview The CZ CELLxGENE Census is a versioned collection of single-cell data built on the TileDB-SOMA framework. This reference documents the data structure, available metadata fields, and query syntax. ## High-Level Structure The Census is organized as a `SOMACollection` with two main components: ### 1. census_info Summary information including: - **summary**: Build date, cell counts, dataset statistics - **datasets**: All datasets from CELLxGENE Discover with metadata - **summary_cell_counts**: Cell counts stratified by metadata categories ### 2. census_data Organism-specific `SOMAExperiment` objects: - **"homo_sapiens"**: Human single-cell data - **"mus_musculus"**: Mouse single-cell data ## Data Structure Per Organism Each organism experiment contains: ### obs (Cell Metadata) Cell-level annotations stored as a `SOMADataFrame`. Access via: ```python census["census_data"]["homo_sapiens"].obs ``` ### ms["RNA"] (Measurement) RNA measurement data including: - **X**: Data matrices with layers: - `raw`: Raw count data - `normalized`: (if available) Normalized counts - **var**: Gene metadata - **feature_dataset_presence_matrix**: Sparse boolean array showing which genes were measured in each dataset ## Cell Metadata Fields (obs) ### Required/Core Fields **Identity & Dataset:** - `soma_joinid`: Unique integer identifier for joins - `dataset_id`: Source dataset identifier - `is_primary_data`: Boolean flag (True = unique cell, False = duplicate across datasets) **Cell Type:** - `cell_type`: Human-readable cell type name - `cell_type_ontology_term_id`: Standardized ontology term (e.g., "CL:0000236") **Tissue:** - `tissue`: Specific tissue name - `tissue_general`: Broader tissue category (useful for grouping) - `tissue_ontology_term_id`: Standardized ontology term **Assay:** - `assay`: Sequencing technology used - `assay_ontology_term_id`: Standardized ontology term **Disease:** - `disease`: Disease status or condition - `disease_ontology_term_id`: Standardized ontology term **Donor:** - `donor_id`: Unique donor identifier - `sex`: Biological sex (male, female, unknown) - `self_reported_ethnicity`: Ethnicity information - `development_stage`: Life stage (adult, child, embryonic, etc.) - `development_stage_ontology_term_id`: Standardized ontology term **Organism:** - `organism`: Scientific name (Homo sapiens, Mus musculus) - `organism_ontology_term_id`: Standardized ontology term **Technical:** - `suspension_type`: Sample preparation type (cell, nucleus, na) ## Gene Metadata Fields (var) Access via: ```python census["census_data"]["homo_sapiens"].ms["RNA"].var ``` **Available Fields:** - `soma_joinid`: Unique integer identifier for joins - `feature_id`: Ensembl gene ID (e.g., "ENSG00000161798") - `feature_name`: Gene symbol (e.g., "FOXP2") - `feature_length`: Gene length in base pairs ## Value Filter Syntax Queries use Python-like expressions for filtering. The syntax is processed by TileDB-SOMA. ### Comparison Operators - `==`: Equal to - `!=`: Not equal to - `<`, `>`, `<=`, `>=`: Numeric comparisons - `in`: Membership test (e.g., `feature_id in ['ENSG00000161798', 'ENSG00000188229']`) ### Logical Operators - `and`, `&`: Logical AND - `or`, `|`: Logical OR ### Examples **Single condition:** ```python value_filter="cell_type == 'B cell'" ``` **Multiple conditions with AND:** ```python value_filter="cell_type == 'B cell' and tissue_general == 'lung' and is_primary_data == True" ``` **Using IN for multiple values:** ```python value_filter="tissue in ['lung', 'liver', 'kidney']" ``` **Complex condition:** ```python value_filter="(cell_type == 'neuron' or cell_type == 'astrocyte') and disease != 'normal'" ``` **Filtering genes:** ```python var_value_filter="feature_name in ['CD4', 'CD8A', 'CD19']" ``` ## Data Inclusion Criteria The Census includes all data from CZ CELLxGENE Discover meeting: 1. **Species**: Human (*Homo sapiens*) or mouse (*Mus musculus*) 2. **Technology**: Approved sequencing technologies for RNA 3. **Count Type**: Raw counts only (no processed/normalized-only data) 4. **Metadata**: Standardized following CELLxGENE schema 5. **Both spatial and non-spatial data**: Includes traditional and spatial transcriptomics ## Important Data Characteristics ### Duplicate Cells Cells may appear across multiple datasets. Use `is_primary_data == True` to filter for unique cells in most analyses. ### Count Types The Census includes: - **Molecule counts**: From UMI-based methods - **Full-gene sequencing read counts**: From non-UMI methods These may need different normalization approaches. ### Versioning Census releases are versioned (e.g., "2025-11-08", "stable", "latest"). "stable" is the newest LTS build and "latest" the weekly one, so pin an explicit date for reproducible analysis: ```python census = cellxgene_census.open_soma(census_version="2025-11-08") ``` ## Dataset Presence Matrix Access which genes were measured in each dataset: ```python presence_matrix = census["census_data"]["homo_sapiens"].ms["RNA"]["feature_dataset_presence_matrix"] ``` This sparse boolean matrix helps understand: - Gene coverage across datasets - Which datasets to include for specific gene analyses - Technical batch effects related to gene coverage ## SOMA Object Types Core TileDB-SOMA objects used: - **DataFrame**: Tabular data (obs, var) - **SparseNDArray**: Sparse matrices (X layers, presence matrix) - **DenseNDArray**: Dense arrays (less common) - **Collection**: Container for related objects - **Experiment**: Top-level container for measurements - **SOMAScene**: Spatial transcriptomics scenes - **obs_spatial_presence**: Spatial data availability -
common_patterns.md 3.3 KB
# Additional Patterns and Pitfalls This file holds patterns that go beyond the core workflows. For the standard query recipes see `querying_expression.md` (open / explore / `get_anndata` / `axis_query`) and `ml_and_scanpy.md` (PyTorch dataloader, scanpy, multi-dataset concatenation). The best-practices checklist lives in `best_practices_and_troubleshooting.md`. ## Incremental Variance (Welford's Algorithm) When computing variance out-of-core over a query too large for RAM, accumulate it in one pass with Welford's online algorithm. Vectorize per batch rather than looping over individual values: ```python import numpy as np n = 0 mean = 0.0 M2 = 0.0 for batch in query.X("raw").tables(): values = batch["soma_data"].to_numpy() # Chan et al. parallel/batch update b_n = len(values) if b_n == 0: continue b_mean = values.mean() b_M2 = ((values - b_mean) ** 2).sum() delta = b_mean - mean new_n = n + b_n mean += delta * b_n / new_n M2 += b_M2 + delta**2 * n * b_n / new_n n = new_n variance = M2 / (n - 1) if n > 1 else float("nan") ``` Note: `X("raw")` returns only the *stored* (nonzero) entries, so a mean/variance computed this way is over observed nonzero values. To get statistics over the full dense matrix (including implicit zeros), divide sums by `n_cells * n_genes` from the query shape instead of by the number of nonzero entries. ## Filter by Ontology Term for Cross-Dataset Consistency Free-text labels (`cell_type == 'B cell'`) can vary subtly across datasets; ontology term IDs are canonical and more reliable for population-scale queries: ```python # Equivalent to 'B cell' but stable across all datasets obs_value_filter="cell_type_ontology_term_id == 'CL:0000236' and is_primary_data == True" ``` ## Batch Processing Across Conditions For systematic analyses sweeping one factor, loop the query and collect results. Pin `census_version` once and reuse the open handle: ```python tissues = ["lung", "liver", "kidney", "heart"] results = {} with cellxgene_census.open_soma(census_version="2025-11-08") as census: for tissue in tissues: adata = cellxgene_census.get_anndata( census=census, organism="Homo sapiens", obs_value_filter=f"tissue_general == '{tissue}' and is_primary_data == True", ) results[tissue] = analyze(adata) ``` ## Common Pitfalls to Avoid 1. **Forgetting `is_primary_data == True`** — silently counts duplicate cells that appear in multiple datasets. 2. **Loading before estimating** — run a `get_obs` count first; switch to `axis_query` out-of-core above ~100k cells. 3. **Skipping the context manager** — leak open SOMA/TileDB handles. 4. **Unpinned version** — results drift when the `stable` release rolls forward; pin `census_version`. 5. **Overly broad queries** — start focused, then widen. 6. **Ignoring the presence matrix** — a gene absent from a dataset reads as all-zero, not missing; check `get_presence_matrix` before interpreting zeros. 7. **Mixing count types** — UMI molecule counts and full-length read counts coexist in `raw` and may need different normalization. 8. **Using deprecated APIs** — ML loaders are now in `tiledbsoma_ml`, not `cellxgene_census.experimental.ml`; concatenate with `anndata.concat`, not `adata.concatenate`. -
ml_and_scanpy.md 5.6 KB
# Machine Learning, Scanpy, and Multi-Dataset Integration Code patterns for training PyTorch models on Census data, integrating with scanpy, and combining multiple datasets/tissues. ## Machine Learning with PyTorch The PyTorch integration now lives in the standalone **`tiledbsoma_ml`** package (`pip install tiledbsoma-ml`), NOT in `cellxgene_census.experimental.ml` — that prototype API has been superseded. Build an `ExperimentDataset` from an `axis_query`, then wrap it with `experiment_dataloader`: ```python import torch import tiledbsoma as soma from tiledbsoma_ml import ExperimentDataset, experiment_dataloader with cellxgene_census.open_soma(census_version="2025-11-08") as census: experiment = census["census_data"]["homo_sapiens"] with experiment.axis_query( measurement_name="RNA", obs_query=soma.AxisQuery( value_filter="tissue_general == 'liver' and is_primary_data == True" ), ) as query: dataset = ExperimentDataset( query, layer_name="raw", obs_column_names=["cell_type"], batch_size=128, shuffle=True, seed=42, ) dataloader = experiment_dataloader(dataset) # Each batch is a (X, obs) tuple: X is a NumPy array, obs a pandas DataFrame. for epoch in range(num_epochs): for X_batch, obs_batch in dataloader: X = torch.from_numpy(X_batch).float() labels = label_encoder.transform(obs_batch["cell_type"]) outputs = model(X) loss = criterion(outputs, torch.from_numpy(labels)) optimizer.zero_grad() loss.backward() optimizer.step() ``` **Train/test splitting** — call `random_split` on the dataset (positional fractions, not `split=[...]`), then wrap each split: ```python train_dataset, test_dataset = dataset.random_split(0.8, 0.2, seed=42) train_loader = experiment_dataloader(train_dataset) test_loader = experiment_dataloader(test_dataset) ``` ## Integration with Scanpy Seamlessly integrate Census data with scanpy workflows: ```python import scanpy as sc # Load data from Census adata = cellxgene_census.get_anndata( census=census, organism="Homo sapiens", obs_value_filter="cell_type == 'neuron' and tissue_general == 'cortex' and is_primary_data == True", ) # Standard scanpy workflow sc.pp.normalize_total(adata, target_sum=1e4) sc.pp.log1p(adata) sc.pp.highly_variable_genes(adata, n_top_genes=2000) # Dimensionality reduction sc.pp.pca(adata, n_comps=50) sc.pp.neighbors(adata) sc.tl.umap(adata) # Visualization sc.pl.umap(adata, color=["cell_type", "tissue", "disease"]) ``` ## Multi-Dataset Integration Prefer a single query with an `in` filter (Strategy 2) — it pulls a consistent gene set in one pass. Only query separately and concatenate when you need to tag or transform each slice differently: ```python import anndata as ad # Strategy 1: Query multiple tissues separately, then concatenate tissues = ["lung", "liver", "kidney"] adatas = [] for tissue in tissues: a = cellxgene_census.get_anndata( census=census, organism="Homo sapiens", obs_value_filter=f"tissue_general == '{tissue}' and is_primary_data == True", ) adatas.append(a) # Use anndata.concat (adata.concatenate() is deprecated). Inner join keeps # only genes shared across all slices. combined = ad.concat(adatas, join="inner", label="tissue", keys=tissues) # Strategy 2 (preferred): one query, multiple tissues adata = cellxgene_census.get_anndata( census=census, organism="Homo sapiens", obs_value_filter="tissue_general in ['lung', 'liver', 'kidney'] and is_primary_data == True", ) ``` ## Worked Use Cases ### Use Case 1: Explore Cell Types in a Tissue ```python with cellxgene_census.open_soma() as census: cells = cellxgene_census.get_obs( census, "homo_sapiens", value_filter="tissue_general == 'lung' and is_primary_data == True", column_names=["cell_type"] ) print(cells["cell_type"].value_counts()) ``` ### Use Case 2: Query Marker Gene Expression ```python with cellxgene_census.open_soma() as census: adata = cellxgene_census.get_anndata( census=census, organism="Homo sapiens", var_value_filter="feature_name in ['CD4', 'CD8A', 'CD19']", obs_value_filter="cell_type in ['T cell', 'B cell'] and is_primary_data == True", ) ``` ### Use Case 3: Train Cell Type Classifier ```python import tiledbsoma as soma from tiledbsoma_ml import ExperimentDataset, experiment_dataloader with cellxgene_census.open_soma(census_version="2025-11-08") as census: experiment = census["census_data"]["homo_sapiens"] with experiment.axis_query( measurement_name="RNA", obs_query=soma.AxisQuery( value_filter="tissue_general == 'blood' and is_primary_data == True" ), ) as query: dataset = ExperimentDataset( query, layer_name="raw", obs_column_names=["cell_type"], batch_size=128, shuffle=True, seed=42, ) dataloader = experiment_dataloader(dataset) for X_batch, obs_batch in dataloader: ... # Training logic ``` ### Use Case 4: Cross-Tissue Analysis ```python with cellxgene_census.open_soma() as census: adata = cellxgene_census.get_anndata( census=census, organism="Homo sapiens", obs_value_filter="cell_type == 'macrophage' and tissue_general in ['lung', 'liver', 'brain'] and is_primary_data == True", ) # Analyze macrophage differences across tissues sc.tl.rank_genes_groups(adata, groupby="tissue_general") ``` -
querying_expression.md 4.6 KB
# Querying Expression Data Code patterns for opening the Census, exploring metadata, and retrieving expression matrices at small/medium and large scales. ## Opening the Census Always use the context manager to ensure proper resource cleanup: ```python import cellxgene_census # Open latest stable version with cellxgene_census.open_soma() as census: summary = census["census_info"]["summary"].read().concat().to_pandas() # Open a specific version for reproducibility (preferred for published work) with cellxgene_census.open_soma(census_version="2025-11-08") as census: summary = census["census_info"]["summary"].read().concat().to_pandas() ``` **Key points:** - Use context manager (`with` statement) for automatic cleanup - Specify `census_version` for reproducible analyses - Default opens latest "stable" release ## Exploring Census Information Before querying expression data, explore available datasets and metadata. **Access summary information:** ```python # Get summary statistics summary = census["census_info"]["summary"].read().concat().to_pandas() print(f"Total cells: {summary['total_cell_count'][0]}") # Get all datasets datasets = census["census_info"]["datasets"].read().concat().to_pandas() # Filter datasets by criteria covid_datasets = datasets[datasets["disease"].str.contains("COVID", na=False)] ``` **Query cell metadata to understand available data:** ```python # Get unique cell types in a tissue cell_metadata = cellxgene_census.get_obs( census, "homo_sapiens", value_filter="tissue_general == 'brain' and is_primary_data == True", column_names=["cell_type"] ) unique_cell_types = cell_metadata["cell_type"].unique() print(f"Found {len(unique_cell_types)} cell types in brain") # Count cells by tissue tissue_counts = cell_metadata.groupby("tissue_general").size() ``` **Important:** Always filter for `is_primary_data == True` to avoid counting duplicate cells unless specifically analyzing duplicates. ## Small-to-Medium Scale Queries (`get_anndata`) For queries returning < 100k cells that fit in memory, use `get_anndata()`: ```python # Basic query with cell type and tissue filters adata = cellxgene_census.get_anndata( census=census, organism="Homo sapiens", # or "Mus musculus" obs_value_filter="cell_type == 'B cell' and tissue_general == 'lung' and is_primary_data == True", obs_column_names=["assay", "disease", "sex", "donor_id"], ) # Query specific genes with multiple filters adata = cellxgene_census.get_anndata( census=census, organism="Homo sapiens", var_value_filter="feature_name in ['CD4', 'CD8A', 'CD19', 'FOXP3']", obs_value_filter="cell_type == 'T cell' and disease == 'COVID-19' and is_primary_data == True", obs_column_names=["cell_type", "tissue_general", "donor_id"], ) ``` **Filter syntax:** - Use `obs_value_filter` for cell filtering - Use `var_value_filter` for gene filtering - Combine conditions with `and`, `or` - Use `in` for multiple values: `tissue in ['lung', 'liver']` - Select only needed columns with `obs_column_names` **Getting metadata separately:** ```python # Query cell metadata cell_metadata = cellxgene_census.get_obs( census, "homo_sapiens", value_filter="disease == 'COVID-19' and is_primary_data == True", column_names=["cell_type", "tissue_general", "donor_id"] ) # Query gene metadata gene_metadata = cellxgene_census.get_var( census, "homo_sapiens", value_filter="feature_name in ['CD4', 'CD8A']", column_names=["feature_id", "feature_name", "feature_length"] ) ``` ## Large-Scale Queries (Out-of-Core Processing) For queries exceeding available RAM, use `axis_query()` with iterative processing: ```python import tiledbsoma as soma # Create axis query query = census["census_data"]["homo_sapiens"].axis_query( measurement_name="RNA", obs_query=soma.AxisQuery( value_filter="tissue_general == 'brain' and is_primary_data == True" ), var_query=soma.AxisQuery( value_filter="feature_name in ['FOXP2', 'TBR1', 'SATB2']" ) ) # Iterate through expression matrix in chunks iterator = query.X("raw").tables() for batch in iterator: # batch is a pyarrow.Table with columns: # - soma_data: expression value # - soma_dim_0: cell (obs) coordinate # - soma_dim_1: gene (var) coordinate process_batch(batch) ``` **Computing incremental statistics:** ```python # Example: Calculate mean expression n_observations = 0 sum_values = 0.0 iterator = query.X("raw").tables() for batch in iterator: values = batch["soma_data"].to_numpy() n_observations += len(values) sum_values += values.sum() mean_expression = sum_values / n_observations ```
-
-
SKILL.md 5.4 KB
--- name: alterlab-cellxgene description: Query the CZ CELLxGENE Census (200M+ cells) programmatically via cellxgene-census and TileDB-SOMA, slicing expression by tissue, disease, or cell type and returning AnnData. Use when pulling reference single-cell RNA-seq data from the largest curated public atlas, running population-scale queries, or benchmarking your data against a reference — for analyzing your own dataset use scanpy or scvi-tools. Part of the AlterLab Academic Skills suite. license: MIT allowed-tools: Read Write Edit Bash(python:*) Bash(uv:*) compatibility: "Self-contained — runs under `uv run python` with `cellxgene-census` installed (1.18.0 as of 2026-09; it pulls in tiledbsoma). Reads are anonymous over the public S3 bucket, so no API key or account is required, but a network connection and several GB of scratch are." metadata: skill-author: AlterLab version: "1.1.0" last_updated: "2026-09-23" --- # CZ CELLxGENE Census ## Overview The CZ CELLxGENE Census provides programmatic, versioned access to standardized single-cell genomics data from CZ CELLxGENE Discover. The **2025-11-08 LTS release** holds **162,025,130 human and 46,299,127 mouse cells** with standardized metadata (cell types, tissues, diseases, donors), raw gene expression matrices, pre-calculated embeddings, and integration with PyTorch, scanpy, and other analysis tools. Cell counts grow with each release — read them from `census["census_info"]["summary"]` rather than quoting a number in a methods section. ## When to Use This Skill Use this skill when: - Querying single-cell expression data by cell type, tissue, or disease - Exploring available single-cell datasets and metadata - Training machine learning models on single-cell data - Performing large-scale cross-dataset analyses - Integrating Census data with scanpy or other analysis frameworks - Computing statistics across millions of cells - Accessing pre-calculated embeddings or model predictions For analyzing **your own** dataset (not the reference atlas), use scanpy or scvi-tools instead. ## Installation ```bash uv pip install cellxgene-census # For PyTorch ML workflows (loaders moved out of cellxgene-census): uv pip install tiledbsoma-ml ``` ## Core Workflow 1. **Open the Census** with a context manager; pin `census_version` for reproducibility. `"stable"` is the alias for the most recent LTS release (**2025-11-08** as of 2026-09) and `"latest"` tracks the weekly build; LTS releases are kept available for at least five years. 2. **Explore metadata first** (`get_obs` / datasets summary) to understand what's available — always filter `is_primary_data == True` to avoid duplicate cells. 3. **Estimate query size** before loading expression. < 100k cells → `get_anndata()` (in-memory); larger → `axis_query()` out-of-core iteration. 4. **Query expression** with `obs_value_filter` (cells) and `var_value_filter` (genes); select only the `obs_column_names` you need. 5. **Downstream**: hand the returned AnnData to scanpy, or stream batches into a PyTorch dataloader for ML. Minimal skeleton: ```python import cellxgene_census with cellxgene_census.open_soma(census_version="2025-11-08") as census: # pinned LTS adata = cellxgene_census.get_anndata( census=census, organism="Homo sapiens", obs_value_filter="cell_type == 'B cell' and tissue_general == 'lung' and is_primary_data == True", ) ``` ### Does NOT Trigger | Scenario | Use Instead | |----------|-------------| | Normalize/cluster/UMAP an AnnData you already have | `alterlab-scanpy` | | Concatenate or wrangle local `.h5ad`/zarr files | `alterlab-anndata` | | Train a deep generative model / batch-integrate your own data | `alterlab-scvi-tools` | | Browse or download a specific GEO/ArrayExpress accession | `alterlab-geo` | | One-off `gget cellxgene` lookup from the CLI | `alterlab-gget` | ## Routing Guidance - **Small/medium query (fits in RAM)** → `get_anndata()`. See `references/querying_expression.md`. - **Query exceeds RAM** → `axis_query()` with chunked iteration and incremental stats. See `references/querying_expression.md`. - **Training ML models** → `tiledbsoma_ml` PyTorch dataloader / `ExperimentDataset`. See `references/ml_and_scanpy.md`. - **Standard scanpy analysis / multi-tissue integration** → see `references/ml_and_scanpy.md`. - **Need full schema, all metadata fields, or filter-syntax details** → `references/census_schema.md`. ## Reference Index - **`references/querying_expression.md`** — Opening the Census, exploring metadata, small/medium `get_anndata()` queries, and large out-of-core `axis_query()` processing with incremental statistics. - **`references/ml_and_scanpy.md`** — `tiledbsoma_ml` PyTorch dataloader / `ExperimentDataset` train-test splits, scanpy integration, multi-dataset/tissue integration (`anndata.concat`), and four worked use cases. - **`references/best_practices_and_troubleshooting.md`** — Primary-data filtering, version pinning, query-size estimation, `tissue_general` vs `tissue`, presence matrices, the full obs/var metadata field list, and a troubleshooting guide. - **`references/census_schema.md`** — Census data structure, all metadata fields, value-filter syntax/operators, SOMA object types, and data inclusion criteria. - **`references/common_patterns.md`** — Extras beyond the core recipes: incremental (Welford) variance out-of-core, ontology-term filtering, batch-processing sweeps, and a common-pitfalls list. Part of the AlterLab Academic Skills suite.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.