Skill Tracer
Skills › Scientific Agent Skills › cellxgene-census › common_patterns.md

common_patterns.md

437 lines · skills/cellxgene-census/references/common_patterns.md at 5f2bc03

Open on GitHub
1# Common Query Patterns and Best Practices
2
3Reviewed against Census 1.18.0, TileDB-SOMA 2.3.0, and the 2025-11-08 LTS.
4Snippets share an open `census` context unless shown otherwise. Large cohort,
5model-training, and downstream analysis blocks are illustrative: first inspect
6metadata and size, restrict datasets/genes, and supply the named callbacks/model.
7API and sparse-statistics contracts were exercised on a small local SOMA fixture;
8a 10-cell/3-gene slice and metadata were also read from the public LTS.
9

Common Query Patterns and Best Practices

Reviewed against Census 1.18.0, TileDB-SOMA 2.3.0, and the 2025-11-08 LTS. Snippets share an open census context unless shown otherwise. Large cohort, model-training, and downstream analysis blocks are illustrative: first inspect metadata and size, restrict datasets/genes, and supply the named callbacks/model. API and sparse-statistics contracts were exercised on a small local SOMA fixture; a 10-cell/3-gene slice and metadata were also read from the public LTS.

Raw lines 1 to 10
11## Query Pattern Categories

Query Pattern Categories

Raw lines 11 to 12
13### 1. Exploratory Queries (Metadata Only)
15Use when exploring available data without loading expression matrices.
17**Pattern: Get unique cell types in a tissue**
18```python
19import cellxgene_census
21with cellxgene_census.open_soma(census_version="2025-11-08") as census:
22 cell_metadata = cellxgene_census.get_obs(
23 census,
24 "homo_sapiens",
25 value_filter="tissue_general == 'brain' and is_primary_data == True",
26 column_names=["cell_type"]
27 )
28 unique_cell_types = cell_metadata["cell_type"].unique()
29 print(f"Found {len(unique_cell_types)} unique cell types")
30```
32**Pattern: Count cells by condition**
33```python
34cell_metadata = cellxgene_census.get_obs(
35 census,
36 "homo_sapiens",
37 value_filter="disease != 'normal' and is_primary_data == True",
38 column_names=["disease", "tissue_general"]
40counts = cell_metadata.groupby(["disease", "tissue_general"]).size()
41```
43**Pattern: Explore dataset information**
44```python
45# Access datasets table
46datasets = census["census_info"]["datasets"].read().concat().to_pandas()
48# Title search is discovery only; disease labels are on obs, not datasets.
49candidates = datasets[datasets["dataset_title"].str.contains("COVID", case=False, na=False)]
50```

1. Exploratory Queries (Metadata Only)

Use when exploring available data without loading expression matrices.

Pattern: Get unique cell types in a tissue

import cellxgene_census

with cellxgene_census.open_soma(census_version="2025-11-08") as census:
    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)} unique cell types")

Pattern: Count cells by condition

cell_metadata = cellxgene_census.get_obs(
    census,
    "homo_sapiens",
    value_filter="disease != 'normal' and is_primary_data == True",
    column_names=["disease", "tissue_general"]
)
counts = cell_metadata.groupby(["disease", "tissue_general"]).size()

Pattern: Explore dataset information

# Access datasets table
datasets = census["census_info"]["datasets"].read().concat().to_pandas()

# Title search is discovery only; disease labels are on obs, not datasets.
candidates = datasets[datasets["dataset_title"].str.contains("COVID", case=False, na=False)]
Raw lines 13 to 51
52### 2. Small-to-Medium Queries (AnnData)
54Use `get_anndata()` when results fit in memory. There is no safe universal cell-count threshold; include gene count, sparsity, embeddings, and downstream copies in the estimate.
56**Pattern: Tissue-specific cell type query**
57```python
58adata = cellxgene_census.get_anndata(
59 census=census,
60 organism="Homo sapiens",
61 obs_value_filter="cell_type == 'B cell' and tissue_general == 'lung' and is_primary_data == True",
62 obs_column_names=["assay", "disease", "sex", "donor_id"],
64```
66**Pattern: Gene-specific query with multiple genes**
67```python
68marker_genes = ["CD4", "CD8A", "CD19", "FOXP3"]
70# First get gene IDs
71gene_metadata = cellxgene_census.get_var(
72 census, "homo_sapiens",
73 value_filter=f"feature_name in {marker_genes}",
74 column_names=["feature_id", "feature_name"]
76gene_ids = gene_metadata["feature_id"].tolist()
77if not gene_ids:
78 raise ValueError("No requested marker genes found")
79# Inspect duplicate symbols; retain feature_id rather than collapsing matches.
81# Query with gene filter
82adata = cellxgene_census.get_anndata(
83 census=census,
84 organism="Homo sapiens",
85 var_value_filter=f"feature_id in {gene_ids}",
86 obs_value_filter="cell_type == 'T cell' and is_primary_data == True",
88```
90**Pattern: Multi-tissue query**
91```python
92adata = cellxgene_census.get_anndata(
93 census=census,
94 organism="Homo sapiens",
95 obs_value_filter="tissue_general in ['lung', 'liver', 'kidney'] and is_primary_data == True",
96 obs_column_names=["cell_type", "tissue_general", "dataset_id"],
98```
100**Pattern: Disease-specific query**
101```python
102adata = cellxgene_census.get_anndata(
103 census=census,
104 organism="Homo sapiens",
105 obs_value_filter="disease == 'COVID-19' and tissue_general == 'lung' and is_primary_data == True",
107```
109**Pattern: Include compound disease labels**
111Schema 2.4.0 stores multiple disease labels/IDs in one string separated by ` || `.
112Use summary rows to discover the actual strings and token membership to choose
113whole values; substring matching can confuse distinct disease names. This
114includes co-diagnoses rather than only the exact single-label cohort.
116```python
117summary = census["census_info"]["summary_cell_counts"].read().concat().to_pandas()
118disease_values = summary.loc[
119 summary["organism"].eq("homo_sapiens") & summary["category"].eq("disease"),
120 "label",
121].unique()
122matching = [value for value in disease_values if "COVID-19" in value.split(" || ")]
123if not matching:
124 raise ValueError("Requested disease not present in this release")
125disease_filter = f"disease in {matching!r} and is_primary_data == True"
126# Add dataset/tissue restrictions after inspecting cohort size.
127```
129For precise ontology-based cohorts, inspect `disease_ontology_term_id` as well;
130matching one ontology term does not automatically include all descendants.

2. Small-to-Medium Queries (AnnData)

Use get_anndata() when results fit in memory. There is no safe universal cell-count threshold; include gene count, sparsity, embeddings, and downstream copies in the estimate.

Pattern: Tissue-specific cell type query

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",
    obs_column_names=["assay", "disease", "sex", "donor_id"],
)

Pattern: Gene-specific query with multiple genes

marker_genes = ["CD4", "CD8A", "CD19", "FOXP3"]

# First get gene IDs
gene_metadata = cellxgene_census.get_var(
    census, "homo_sapiens",
    value_filter=f"feature_name in {marker_genes}",
    column_names=["feature_id", "feature_name"]
)
gene_ids = gene_metadata["feature_id"].tolist()
if not gene_ids:
    raise ValueError("No requested marker genes found")
# Inspect duplicate symbols; retain feature_id rather than collapsing matches.

# Query with gene filter
adata = cellxgene_census.get_anndata(
    census=census,
    organism="Homo sapiens",
    var_value_filter=f"feature_id in {gene_ids}",
    obs_value_filter="cell_type == 'T cell' and is_primary_data == True",
)

Pattern: Multi-tissue query

adata = cellxgene_census.get_anndata(
    census=census,
    organism="Homo sapiens",
    obs_value_filter="tissue_general in ['lung', 'liver', 'kidney'] and is_primary_data == True",
    obs_column_names=["cell_type", "tissue_general", "dataset_id"],
)

Pattern: Disease-specific query

adata = cellxgene_census.get_anndata(
    census=census,
    organism="Homo sapiens",
    obs_value_filter="disease == 'COVID-19' and tissue_general == 'lung' and is_primary_data == True",
)

Pattern: Include compound disease labels

Schema 2.4.0 stores multiple disease labels/IDs in one string separated by || . Use summary rows to discover the actual strings and token membership to choose whole values; substring matching can confuse distinct disease names. This includes co-diagnoses rather than only the exact single-label cohort.

summary = census["census_info"]["summary_cell_counts"].read().concat().to_pandas()
disease_values = summary.loc[
    summary["organism"].eq("homo_sapiens") & summary["category"].eq("disease"),
    "label",
].unique()
matching = [value for value in disease_values if "COVID-19" in value.split(" || ")]
if not matching:
    raise ValueError("Requested disease not present in this release")
disease_filter = f"disease in {matching!r} and is_primary_data == True"
# Add dataset/tissue restrictions after inspecting cohort size.

For precise ontology-based cohorts, inspect disease_ontology_term_id as well; matching one ontology term does not automatically include all descendants.

Raw lines 52 to 131
132### 3. Large Queries (Out-of-Core Processing)
134Use `axis_query()` for queries that exceed available RAM.
136**Pattern: Iterative processing**
137```python
138import tiledbsoma as soma
140# Create query
141with census["census_data"]["homo_sapiens"].axis_query(
142 measurement_name="RNA",
143 obs_query=soma.AxisQuery(
144 value_filter="tissue_general == 'brain' and is_primary_data == True"
145 ),
146 var_query=soma.AxisQuery(
147 value_filter="feature_name in ['FOXP2', 'TBR1', 'SATB2']"
148 ),
149) as query:
150 # Iterate through X matrix in chunks
151 iterator = query.X("raw").tables()
152 for batch in iterator:
153 # Process batch (a pyarrow.Table); soma_dim_0 = cells, soma_dim_1 = genes
154 # batch has columns: soma_data, soma_dim_0, soma_dim_1
155 process_batch(batch)
156```
158**Pattern: Per-gene mean and variance including measured zeros**
160Restrict to datasets that measured every requested feature before computing the
161statistics. This example selects CD4 by its unique Ensembl ID. It includes sparse
162zeros, returns one row per gene, and rejects empty or one-cell selections rather
163than returning an undefined sample variance.
165```python
166from cellxgene_census.experimental.pp import mean_variance
168# Assumes census and soma are imported/open as above.
169genes = cellxgene_census.get_var(
170 census, "homo_sapiens", value_filter="feature_id == 'ENSG00000010610'",
171 column_names=["soma_joinid", "feature_id"],
173if len(genes) != 1:
174 raise ValueError("Expected one CD4 feature")
175datasets = census["census_info"]["datasets"].read().concat().to_pandas()
176presence = cellxgene_census.get_presence_matrix(census, "homo_sapiens")
177covered = presence[datasets["soma_joinid"].to_numpy(), :][:, genes["soma_joinid"].to_numpy()]
178eligible_ids = datasets.loc[covered.toarray().all(axis=1), "dataset_id"].tolist()
179if not eligible_ids:
180 raise ValueError("No datasets measured CD4")
182with census["census_data"]["homo_sapiens"].axis_query(
183 measurement_name="RNA",
184 obs_query=soma.AxisQuery(value_filter=(
185 f"dataset_id in {eligible_ids!r} and tissue_general == 'brain' "
186 "and is_primary_data == True"
187 )),
188 var_query=soma.AxisQuery(coords=(genes["soma_joinid"].to_numpy(),)),
189) as query:
190 if query.n_obs < 2:
191 raise ValueError("At least two selected cells are required")
192 stats = mean_variance(
193 query, layer="raw", axis=0, calculate_mean=True,
194 calculate_variance=True, ddof=1, nnz_only=False,
195 )
196```
198These describe raw count distributions, not normalized differential expression.
199For genes with different measurement coverage, either use a common measured
200subset or compute each gene with its own valid denominator. `nnz_only=True` is a
201different estimand: the distribution of explicitly stored entries.

3. Large Queries (Out-of-Core Processing)

Use axis_query() for queries that exceed available RAM.

Pattern: Iterative processing

import tiledbsoma as soma

# Create query
with 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']"
    ),
) as query:
    # Iterate through X matrix in chunks
    iterator = query.X("raw").tables()
    for batch in iterator:
        # Process batch (a pyarrow.Table); soma_dim_0 = cells, soma_dim_1 = genes
        # batch has columns: soma_data, soma_dim_0, soma_dim_1
        process_batch(batch)

Pattern: Per-gene mean and variance including measured zeros

Restrict to datasets that measured every requested feature before computing the statistics. This example selects CD4 by its unique Ensembl ID. It includes sparse zeros, returns one row per gene, and rejects empty or one-cell selections rather than returning an undefined sample variance.

from cellxgene_census.experimental.pp import mean_variance

# Assumes census and soma are imported/open as above.
genes = cellxgene_census.get_var(
    census, "homo_sapiens", value_filter="feature_id == 'ENSG00000010610'",
    column_names=["soma_joinid", "feature_id"],
)
if len(genes) != 1:
    raise ValueError("Expected one CD4 feature")
datasets = census["census_info"]["datasets"].read().concat().to_pandas()
presence = cellxgene_census.get_presence_matrix(census, "homo_sapiens")
covered = presence[datasets["soma_joinid"].to_numpy(), :][:, genes["soma_joinid"].to_numpy()]
eligible_ids = datasets.loc[covered.toarray().all(axis=1), "dataset_id"].tolist()
if not eligible_ids:
    raise ValueError("No datasets measured CD4")

with census["census_data"]["homo_sapiens"].axis_query(
    measurement_name="RNA",
    obs_query=soma.AxisQuery(value_filter=(
        f"dataset_id in {eligible_ids!r} and tissue_general == 'brain' "
        "and is_primary_data == True"
    )),
    var_query=soma.AxisQuery(coords=(genes["soma_joinid"].to_numpy(),)),
) as query:
    if query.n_obs < 2:
        raise ValueError("At least two selected cells are required")
    stats = mean_variance(
        query, layer="raw", axis=0, calculate_mean=True,
        calculate_variance=True, ddof=1, nnz_only=False,
    )

These describe raw count distributions, not normalized differential expression. For genes with different measurement coverage, either use a common measured subset or compute each gene with its own valid denominator. nnz_only=True is a different estimand: the distribution of explicitly stored entries.

Raw lines 132 to 202
203### 4. PyTorch Integration (Machine Learning)
205Use TileDB-SOMA-ML for training models. The former `cellxgene_census.experimental.ml` loaders are absent from 1.18.0.
207**Pattern: Create training dataloader**
208```python
209import tiledbsoma as soma
210from tiledbsoma_ml import ExperimentDataset, experiment_dataloader
212with cellxgene_census.open_soma(census_version="2025-11-08") as census:
213 experiment = census["census_data"]["homo_sapiens"]
214 with experiment.axis_query(
215 measurement_name="RNA",
216 obs_query=soma.AxisQuery(
217 value_filter="tissue_general == 'liver' and is_primary_data == True"
218 ),
219 ) as query:
220 dataset = ExperimentDataset(
221 query=query,
222 layer_name="raw",
223 obs_column_names=["cell_type"],
224 batch_size=128,
225 shuffle=True,
226 )
227 dataloader = experiment_dataloader(dataset)
229 for epoch in range(num_epochs):
230 dataset.set_epoch(epoch)
231 for X, obs in dataloader:
232 labels = obs["cell_type"]
233 # Train model...
234```
236**Pattern: Train/test split**
237```python
238# Split data
239train_dataset, test_dataset = dataset.random_split(0.8, 0.2, seed=42)
241# Create loaders
242train_loader = experiment_dataloader(train_dataset, num_workers=0)
243test_loader = experiment_dataloader(test_dataset, num_workers=0)
244```
246Set `batch_size` and `shuffle` on `ExperimentDataset`, not on the PyTorch `DataLoader`. On macOS, enable multiple workers only from an importable script under `if __name__ == "__main__":`. Keep iteration within the open Census/query context.
248A random cell split measures within-cohort interpolation; hold out donors or studies to test generalization. Keep one vocabulary fitted on the training data and define how unseen labels are handled. `X` is NumPy (or SciPy sparse), and `obs` is pandas, so convert inputs to tensors and string labels to stable integer IDs before computing a PyTorch loss.

4. PyTorch Integration (Machine Learning)

Use TileDB-SOMA-ML for training models. The former cellxgene_census.experimental.ml loaders are absent from 1.18.0.

Pattern: Create training dataloader

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=query,
            layer_name="raw",
            obs_column_names=["cell_type"],
            batch_size=128,
            shuffle=True,
        )
        dataloader = experiment_dataloader(dataset)

        for epoch in range(num_epochs):
            dataset.set_epoch(epoch)
            for X, obs in dataloader:
                labels = obs["cell_type"]
                # Train model...

Pattern: Train/test split

# Split data
train_dataset, test_dataset = dataset.random_split(0.8, 0.2, seed=42)

# Create loaders
train_loader = experiment_dataloader(train_dataset, num_workers=0)
test_loader = experiment_dataloader(test_dataset, num_workers=0)

Set batch_size and shuffle on ExperimentDataset, not on the PyTorch DataLoader. On macOS, enable multiple workers only from an importable script under if __name__ == "__main__":. Keep iteration within the open Census/query context.

A random cell split measures within-cohort interpolation; hold out donors or studies to test generalization. Keep one vocabulary fitted on the training data and define how unseen labels are handled. X is NumPy (or SciPy sparse), and obs is pandas, so convert inputs to tensors and string labels to stable integer IDs before computing a PyTorch loss.

Raw lines 203 to 249
250### 5. Spatial Census Data
252Use the `cellxgene-census[spatial]` extra and query the `census_spatial_sequencing` collection for Visium or Slide-seq V2 data.
254```python
255import tiledbsoma as soma
257with cellxgene_census.open_soma(census_version="2025-11-08") as census:
258 spatial_experiment = census["census_spatial_sequencing"]["homo_sapiens"]
259 with spatial_experiment.axis_query(
260 measurement_name="RNA",
261 obs_query=soma.AxisQuery(
262 value_filter="dataset_id == '4cceac62-9513-42a4-90e5-2878dbb0192c'"
263 ),
264 ) as query:
265 sdata = query.to_spatialdata(X_name="raw")
266```

5. Spatial Census Data

Use the cellxgene-census[spatial] extra and query the census_spatial_sequencing collection for Visium or Slide-seq V2 data.

import tiledbsoma as soma

with cellxgene_census.open_soma(census_version="2025-11-08") as census:
    spatial_experiment = census["census_spatial_sequencing"]["homo_sapiens"]
    with spatial_experiment.axis_query(
        measurement_name="RNA",
        obs_query=soma.AxisQuery(
            value_filter="dataset_id == '4cceac62-9513-42a4-90e5-2878dbb0192c'"
        ),
    ) as query:
        sdata = query.to_spatialdata(X_name="raw")
Raw lines 250 to 267
268### 6. Integration Workflows
270**Pattern: Scanpy integration**
271```python
272import scanpy as sc
274# Load data
275adata = cellxgene_census.get_anndata(
276 census=census,
277 organism="Homo sapiens",
278 obs_value_filter="cell_type == 'neuron' and is_primary_data == True",
281# Standard scanpy workflow
282sc.pp.normalize_total(adata, target_sum=1e4)
283sc.pp.log1p(adata)
284sc.pp.highly_variable_genes(adata)
285sc.pp.pca(adata)
286sc.pp.neighbors(adata)
287sc.tl.umap(adata)
288sc.pl.umap(adata, color=["cell_type", "tissue_general"])
289```
291**Pattern: Multi-dataset integration**
292```python
293# Query multiple datasets separately
294datasets_to_integrate = ["dataset_id_1", "dataset_id_2", "dataset_id_3"]
296adatas = []
297for dataset_id in datasets_to_integrate:
298 adata = cellxgene_census.get_anndata(
299 census=census,
300 organism="Homo sapiens",
301 obs_value_filter=f"dataset_id == '{dataset_id}' and is_primary_data == True",
302 )
303 adatas.append(adata)
305# Scanpy's wrapper expects ONE AnnData with contiguous batches and X_pca.
306# Install scanorama separately for this illustrative integration workflow.
307import anndata as ad
308import scanpy as sc
309import scanpy.external as sce
310for item in adatas:
311 item.var_names = item.var["feature_id"].astype(str)
312combined = ad.concat(adatas, label="batch", keys=datasets_to_integrate, join="inner")
313combined.layers["counts"] = combined.X.copy()
314sc.pp.normalize_total(combined, target_sum=1e4)
315sc.pp.log1p(combined)
316sc.pp.highly_variable_genes(combined, n_top_genes=2000, batch_key="batch")
317sc.pp.pca(combined)
318sce.pp.scanorama_integrate(combined, key="batch")
319sc.pp.neighbors(combined, use_rep="X_scanorama")
320```

6. Integration Workflows

Pattern: Scanpy integration

import scanpy as sc

# Load data
adata = cellxgene_census.get_anndata(
    census=census,
    organism="Homo sapiens",
    obs_value_filter="cell_type == 'neuron' 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)
sc.pp.pca(adata)
sc.pp.neighbors(adata)
sc.tl.umap(adata)
sc.pl.umap(adata, color=["cell_type", "tissue_general"])

Pattern: Multi-dataset integration

# Query multiple datasets separately
datasets_to_integrate = ["dataset_id_1", "dataset_id_2", "dataset_id_3"]

adatas = []
for dataset_id in datasets_to_integrate:
    adata = cellxgene_census.get_anndata(
        census=census,
        organism="Homo sapiens",
        obs_value_filter=f"dataset_id == '{dataset_id}' and is_primary_data == True",
    )
    adatas.append(adata)

# Scanpy's wrapper expects ONE AnnData with contiguous batches and X_pca.
# Install scanorama separately for this illustrative integration workflow.
import anndata as ad
import scanpy as sc
import scanpy.external as sce
for item in adatas:
    item.var_names = item.var["feature_id"].astype(str)
combined = ad.concat(adatas, label="batch", keys=datasets_to_integrate, join="inner")
combined.layers["counts"] = combined.X.copy()
sc.pp.normalize_total(combined, target_sum=1e4)
sc.pp.log1p(combined)
sc.pp.highly_variable_genes(combined, n_top_genes=2000, batch_key="batch")
sc.pp.pca(combined)
sce.pp.scanorama_integrate(combined, key="batch")
sc.pp.neighbors(combined, use_rep="X_scanorama")
Raw lines 268 to 321
322## Best Practices

Best Practices

Raw lines 322 to 323
324### 1. Always Filter for Primary Data
325Unless specifically analyzing duplicates, always include `is_primary_data == True`:
326```python
327obs_value_filter="cell_type == 'B cell' and is_primary_data == True"
328```

1. Always Filter for Primary Data

Unless specifically analyzing duplicates, always include is_primary_data == True:

obs_value_filter="cell_type == 'B cell' and is_primary_data == True"
Raw lines 324 to 329
330### 2. Specify Census Version
331For reproducible analysis, always specify the Census version:
332```python
333census = cellxgene_census.open_soma(census_version="2025-11-08")
334```

2. Specify Census Version

For reproducible analysis, always specify the Census version:

census = cellxgene_census.open_soma(census_version="2025-11-08")
Raw lines 330 to 335
336### 3. Use Context Manager
337Always use the context manager to ensure proper cleanup:
338```python
339with cellxgene_census.open_soma(census_version="2025-11-08") as census:
340 print(list(census["census_data"]))
341```

3. Use Context Manager

Always use the context manager to ensure proper cleanup:

with cellxgene_census.open_soma(census_version="2025-11-08") as census:
    print(list(census["census_data"]))
Raw lines 336 to 342
343### 4. Select Only Needed Columns
344Minimize data transfer by selecting only required metadata columns:
345```python
346obs_column_names=["cell_type", "tissue_general", "disease"] # Not all columns
347```

4. Select Only Needed Columns

Minimize data transfer by selecting only required metadata columns:

obs_column_names=["cell_type", "tissue_general", "disease"]  # Not all columns
Raw lines 343 to 348
349### 5. Check Dataset Presence for Gene Queries
350When analyzing specific genes, check which datasets measured them:
351```python
352genes = cellxgene_census.get_var(
353 census, "homo_sapiens",
354 value_filter="feature_name in ['CD4', 'CD8A']",
355 column_names=["soma_joinid", "feature_id", "feature_name"],
357presence = cellxgene_census.get_presence_matrix(census, "homo_sapiens")
358# Columns use Census join IDs, not positions in the filtered gene table.
359gene_presence = presence[:, genes["soma_joinid"].to_numpy()]
360```

5. Check Dataset Presence for Gene Queries

When analyzing specific genes, check which datasets measured them:

genes = cellxgene_census.get_var(
    census, "homo_sapiens",
    value_filter="feature_name in ['CD4', 'CD8A']",
    column_names=["soma_joinid", "feature_id", "feature_name"],
)
presence = cellxgene_census.get_presence_matrix(census, "homo_sapiens")
# Columns use Census join IDs, not positions in the filtered gene table.
gene_presence = presence[:, genes["soma_joinid"].to_numpy()]
Raw lines 349 to 361
362### 6. Use tissue_general for Broader Queries
363`tissue_general` provides coarser groupings than `tissue`, useful for cross-tissue analyses:
364```python
365# Better for broad queries
366obs_value_filter="tissue_general == 'immune system'"
368# Use specific tissue when needed
369obs_value_filter="tissue == 'venous blood'"
370```

6. Use tissue_general for Broader Queries

tissue_general provides coarser groupings than tissue, useful for cross-tissue analyses:

# Better for broad queries
obs_value_filter="tissue_general == 'immune system'"

# Use specific tissue when needed
obs_value_filter="tissue == 'venous blood'"
Raw lines 362 to 371
372### 7. Combine Metadata Exploration with Expression Queries
373First explore metadata to understand available data, then query expression:
374```python
375# Step 1: Explore
376metadata = cellxgene_census.get_obs(
377 census, "homo_sapiens",
378 value_filter="disease == 'COVID-19'",
379 column_names=["cell_type", "tissue_general"]
381print(metadata.value_counts())
383# Step 2: Query based on findings
384adata = cellxgene_census.get_anndata(
385 census=census,
386 organism="Homo sapiens",
387 obs_value_filter="disease == 'COVID-19' and cell_type == 'T cell' and is_primary_data == True",
389```

7. Combine Metadata Exploration with Expression Queries

First explore metadata to understand available data, then query expression:

# Step 1: Explore
metadata = cellxgene_census.get_obs(
    census, "homo_sapiens",
    value_filter="disease == 'COVID-19'",
    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",
)
Raw lines 372 to 390
391### 8. Memory Management for Large Queries
392For large queries, check estimated size before loading:
393```python
394# Get cell count first
395metadata = cellxgene_census.get_obs(
396 census, "homo_sapiens",
397 value_filter="tissue_general == 'brain' and is_primary_data == True",
398 column_names=["soma_joinid"]
400n_cells = len(metadata)
401print(f"Query will return {n_cells} cells")
403# If too large, use out-of-core processing or further filtering
404```

8. Memory Management for Large Queries

For large queries, check estimated size before loading:

# Get cell count first
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, use out-of-core processing or further filtering
Raw lines 391 to 405
406### 9. Leverage Ontology Terms for Consistency
407When possible, use ontology term IDs instead of free text:
408```python
409# More reliable than cell_type == 'B cell' across datasets
410obs_value_filter="cell_type_ontology_term_id == 'CL:0000236'"
411```

9. Leverage Ontology Terms for Consistency

When possible, use ontology term IDs instead of free text:

# More reliable than cell_type == 'B cell' across datasets
obs_value_filter="cell_type_ontology_term_id == 'CL:0000236'"
Raw lines 406 to 412
413### 10. Batch Processing Pattern
414For systematic analyses across multiple conditions:
415```python
416tissues = ["lung", "liver", "kidney", "heart"]
417results = {}
419for tissue in tissues:
420 adata = cellxgene_census.get_anndata(
421 census=census,
422 organism="Homo sapiens",
423 obs_value_filter=f"tissue_general == '{tissue}' and is_primary_data == True",
424 )
425 # Perform analysis
426 results[tissue] = analyze(adata)
427```

10. Batch Processing Pattern

For systematic analyses across multiple conditions:

tissues = ["lung", "liver", "kidney", "heart"]
results = {}

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",
    )
    # Perform analysis
    results[tissue] = analyze(adata)
Raw lines 413 to 428
429## Common Pitfalls to Avoid
4311. **Not filtering for is_primary_data**: Leads to counting duplicate cells
4322. **Loading too much data**: Use metadata queries to estimate size first
4333. **Not using context manager**: Can cause resource leaks
4344. **Inconsistent versioning**: Results not reproducible without specifying version
4355. **Overly broad queries**: Start with focused queries, expand as needed
4366. **Ignoring dataset presence**: Some genes not measured in all datasets
4377. **Wrong count normalization**: Be aware of UMI vs read count differences

Common Pitfalls to Avoid

  1. Not filtering for is_primary_data: Leads to counting duplicate cells
  2. Loading too much data: Use metadata queries to estimate size first
  3. Not using context manager: Can cause resource leaks
  4. Inconsistent versioning: Results not reproducible without specifying version
  5. Overly broad queries: Start with focused queries, expand as needed
  6. Ignoring dataset presence: Some genes not measured in all datasets
  7. Wrong count normalization: Be aware of UMI vs read count differences
Raw lines 429 to 437