Dataset integration

Dataset integration#

In this tutorial, we will integrate a differential centrifugation (DC) dataset with an organellar immunoprecipitation (OrgIP) dataset.

While DC is a more scalable technique, OrgIP provides significantly higher resolution. A practical approach to achieving high-resolution mapping is to strategically supplement DC data with targeted IP pulldowns for cellular compartments that are poorly resolved by centrifugation alone.

Although we merge two complete datasets in this example, real-world applications typically involve combining a DC dataset with only a small number of strategic IPs.

This integration process is streamlined using the grassp package.

import grassp as gr
import scanpy as sc
import anndata as ad
import numpy as np
/Users/mfrank/code/grassp/.venv-dev/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm

Loading Data#

# load DC dataset (hek cells)
dc = gr.ds.load_dataset("Frank_2026_HEK293")
dc
AnnData object with n_obs × n_vars = 8595 × 7
    obs: 'protein_name', 'gene_symbol', 'Protein IDs', 'Peptide counts (all)', 'Peptide counts (razor+unique)', 'Peptide counts (unique)', 'Protein names', 'Gene names', 'Number of proteins', 'Peptides', 'Razor + unique peptides', 'Unique peptides', 'Sequence coverage [%]', 'Unique + razor sequence coverage [%]', 'Unique sequence coverage [%]', 'Mol. weight [kDa]', 'Sequence length', 'Sequence lengths', 'Fraction average', 'Fraction 1', 'Fraction 2', 'Fraction 3', 'Q-value', 'Score', 'Intensity', 'iBAQ', 'MS/MS count', 'id', 'Peptide IDs', 'Peptide is razor', 'Mod. peptide IDs', 'Oxidation (M) site IDs', 'Oxidation (M) site positions', 'Length', 'Gene Names (primary)', 'marker_lilley', 'marker_christopher', 'marker_geladaki', 'marker_itzhak', 'marker_villaneuva', 'marker_hein2025', 'marker_hein2025_gt', 'n_samples_by_intensity', 'mean_intensity', 'log1p_mean_intensity', 'pct_dropout_by_intensity', 'total_intensity', 'log1p_total_intensity', 'n_imputed', 'leiden_0.5', 'leiden_0.8', 'leiden_1.0', 'leiden_2.0', 'leiden_3.0', 'leiden_5.0', 'leiden_8.0', 'leiden_12.0', 'leiden_15.0', 'leiden_15.0_CC_enrichment_consolidated', 'leiden_15.0_CC_enrichment_consolidated_Adjusted P-value Bonferroni', 'logEnrichmentPval', 'leiden_15.0_CC_enrichment_consolidated_filtered0.05', 'leiden_15.0_CC_enrichment_consolidated_filtered0.01', 'leiden_15.0_CC_enrichment_consolidated_filtered0.001', 'leiden_3.0_CC_enrichment', 'leiden_3.0_CC_enrichment_Adjusted P-value Bonferroni', 'leiden_15.0_CC_enrichment', 'leiden_15.0_CC_enrichment_Adjusted P-value Bonferroni', 'Topological domain', 'Transmembrane', 'Intramembrane', 'has_signal', 'has_transmem', 'has_intramem', 'has_topo_dom', 'mitocarta', 'mitocarta_evidence', 'mitocarta_subloc', 'mitocop', 'author_annotation', 'author_markers', 'unsupervised_annotation', 'harmonized_annotation_propagated', 'harmonized_annotation_propagated_probability'
    var: 'development_stage', 'tissue', 'sex', 'organism', 'disease', 'cell_line', 'cell_type', 'subcellular_enrichment', 'n_merged_samples', 'mean', 'std', 'enrichment_strategy', 'mass_spectrometry_method', 'cell_type_ontology_term_id', 'perturbation', 'disease_ontology_term_id', 'organism_ontology_term_id', 'sex_ontology_term_id', 'development_stage_ontology_term_id', 'tissue_ontology_term_id', 'cell_line_ontology_term_id', 'tissue_type', 'tissue_general_ontology_term_id', 'tissue_general', 'organ_ontology_term_id', 'organ', 'system_ontology_term_id', 'system'
    uns: 'RawInfo', 'harmonized_annotation_propagated_colors', 'has_intramem_colors', 'has_signal_colors', 'has_topo_dom_colors', 'has_transmem_colors', 'leiden_0.5', 'leiden_0.5_colors', 'leiden_0.8', 'leiden_0.8_colors', 'leiden_1.0', 'leiden_1.0_colors', 'leiden_12.0', 'leiden_12.0_colors', 'leiden_15.0', 'leiden_15.0_CC_enrichment_colors', 'leiden_15.0_CC_enrichment_consolidated_colors', 'leiden_15.0_CC_enrichment_consolidated_filtered0.001_colors', 'leiden_15.0_CC_enrichment_consolidated_filtered0.01_colors', 'leiden_15.0_CC_enrichment_consolidated_filtered0.05_colors', 'leiden_15.0_colors', 'leiden_2.0', 'leiden_2.0_colors', 'leiden_3.0', 'leiden_3.0_CC_enrichment_colors', 'leiden_3.0_colors', 'leiden_5.0', 'leiden_5.0_colors', 'leiden_8.0', 'leiden_8.0_colors', 'marker_christopher_colors', 'marker_geladaki_colors', 'marker_hein2025_colors', 'marker_hein2025_gt_colors', 'marker_itzhak_colors', 'marker_lilley_colors', 'marker_villaneuva_colors', 'mitocarta_colors', 'mitocop_colors', 'neighbors', 'pca', 'publication_authors', 'publication_date', 'publication_doi', 'publication_journal', 'publication_title', 'schema_reference', 'schema_version', 'serves_as_reference', 'title', 'umap', 'unsupervised_annotation_colors'
    obsm: 'X_pca', 'X_umap', 'X_umap3D', 'harmonized_annotation_propagated_one_hot_labels', 'harmonized_annotation_propagated_probabilities'
    varm: 'PCs'
    layers: 'MS_MS count', 'log_intensities', 'original_intensities', 'pvals', 'raw_intensities'
    obsp: 'connectivities', 'distances'
# OrgIP dataset (hek cells)
orgip = gr.ds.load_dataset("Hein_2025_HEK293_WT")
# This object ships the untagged (WT) control pulldowns and unscaled log fold
# changes. Drop the controls and z-score each bait, so that the ~70 IP columns
# and the 7 DC fractions contribute on a comparable scale when we concatenate.
orgip = orgip[:, ~orgip.var_names.str.endswith("-WT")].copy()
sc.pp.scale(orgip)
orgip
AnnData object with n_obs × n_vars = 8477 × 71
    obs: 'protein_name', 'gene_symbol', 'Protein IDs', 'author_gene_symbol', 'Majority protein IDs', 'Gene names', 'Gene_name_canonical', 'author_annotation', 'author_markers', 'marker_lilley', 'marker_christopher', 'marker_geladaki', 'marker_itzhak', 'marker_villaneuva', 'marker_hein2025', 'marker_hein2025_gt', 'n_samples_by_intensity', 'mean_intensity', 'pct_dropout_by_intensity', 'total_intensity', 'unsupervised_annotation', 'harmonized_annotation_propagated', 'harmonized_annotation_propagated_probability'
    var: 'development_stage', 'tissue', 'sex', 'organism', 'disease', 'cell_line', 'cell_type', 'subcellular_enrichment', 'fraction', 'fraction_order', 'biological_replicate', 'n_proteins_by_intensity', 'total_intensity', 'pct_dropout_by_intensity', 'enrichment_strategy', 'mass_spectrometry_method', 'cell_type_ontology_term_id', 'perturbation', 'disease_ontology_term_id', 'organism_ontology_term_id', 'sex_ontology_term_id', 'development_stage_ontology_term_id', 'tissue_ontology_term_id', 'cell_line_ontology_term_id', 'tissue_type', 'tissue_general_ontology_term_id', 'tissue_general', 'organ_ontology_term_id', 'organ', 'system_ontology_term_id', 'system', 'mean', 'std'
    uns: 'author_markers_colors', 'harmonized_annotation_propagated_colors', 'marker_christopher_colors', 'marker_geladaki_colors', 'marker_hein2025_colors', 'marker_hein2025_gt_colors', 'marker_itzhak_colors', 'marker_lilley_colors', 'marker_villaneuva_colors', 'neighbors', 'publication_authors', 'publication_date', 'publication_doi', 'publication_journal', 'publication_title', 'schema_reference', 'schema_version', 'serves_as_reference', 'title', 'unsupervised_annotation_colors'
    obsm: 'X_pca', 'X_umap', 'X_umap3D', 'harmonized_annotation_propagated_one_hot_labels', 'harmonized_annotation_propagated_probabilities'
    layers: 'pvals'
    obsp: 'connectivities', 'distances'

Plotting individual maps#

Next, we use UMAP to visualize the spatial organization of the datasets. This helps us qualitatively assess how well the biological compartments are resolved before proceeding with downstream integration or analysis.

sc.pl.umap(orgip, color="marker_hein2025_gt")
sc.pl.umap(dc, color="marker_hein2025_gt")
../../_images/01c62020aaea71f7104ed9e4523d3667d66da91fd5a5f33e0b019ed3d4a63c81.png ../../_images/b36ed16dce999e4603ccc4cab0288e77b61d8df59bd9d35bda6fd3f7bdcfd5d7.png

Integration#

To integrate the two datasets, we must identify a common gene or protein identifier to facilitate matching. While UniProt IDs or gene names are typical candidates, direct merging via UniProt IDs is challenging in this case because one dataset was searched against Swiss-Prot and the other against TrEMBL. To ensure consistency, we will first collapse all entries based on their gene names.

# aggregate proteins in the DC dataset
dc = dc[dc.obs["Gene names"].notna()]
dc_agg = gr.pp.aggregate_proteins(dc, grouping_columns="Gene names", agg_func=np.mean)
dc_agg.obs.head()
protein_name gene_symbol Protein IDs Peptide counts (all) Peptide counts (razor+unique) Peptide counts (unique) Protein names Gene names Number of proteins Peptides ... mitocarta mitocarta_evidence mitocarta_subloc mitocop author_annotation author_markers unsupervised_annotation harmonized_annotation_propagated harmonized_annotation_propagated_probability n_merged_proteins
AAAS Aladin (Adracalin) AAAS Q9NRG9;Q9NRG9-2 24;20 24;20 24;20 Aladin AAAS 2 24 ... NaN NaN NaN NaN Cell membrane Cell membrane Nucleus membrane NaN 0.566277 1
AACS Acetoacetyl-CoA synthetase (EC 6.2.1.16) (Acyl... AACS Q86V21;Q86V21-2;Q86V21-3 28;21;15 28;21;15 28;21;15 Acetoacetyl-CoA synthetase AACS 3 28 ... NaN NaN NaN NaN unknown unknown Cytoplasm Cytoplasm 1.0 1
AADAT Kynurenine/alpha-aminoadipate aminotransferase... AADAT Q8N5Z0;Q8N5Z0-2 9;8 9;8 9;8 Kynurenine/alpha-aminoadipate aminotransferase... AADAT 2 9 ... True targetP signal, Rickettsial homolog, mito prot... Matrix True Mitochondrion Mitochondrion Mitochondrion Mitochondrion 1.0 1
AAED1 Peroxiredoxin-like 2C (AhpC/TSA antioxidant en... PRXL2C Q7RTV5 4 4 4 Thioredoxin-like protein AAED1 AAED1 1 4 ... NaN NaN NaN NaN Golgi apparatus Golgi apparatus Golgi apparatus NaN 0.516824 1
AAGAB Alpha- and gamma-adaptin-binding protein p34 AAGAB Q6PD74;Q6PD74-2 10;6 10;6 10;6 Alpha- and gamma-adaptin-binding protein p34 AAGAB 2 10 ... NaN NaN NaN NaN Cytoplasm Cytoplasm Cytoplasm Cytoplasm 0.918249 1

5 rows × 85 columns

# aggregate proteins in the OrgIP dataset
orgip_agg = gr.pp.aggregate_proteins(
    orgip, grouping_columns="Gene_name_canonical", agg_func=np.mean
)
orgip_agg.obs_names = orgip_agg.obs_names.str.upper()
orgip_agg.obs.head()
protein_name gene_symbol Protein IDs author_gene_symbol Majority protein IDs Gene names Gene_name_canonical author_annotation author_markers marker_lilley ... marker_hein2025 marker_hein2025_gt n_samples_by_intensity mean_intensity pct_dropout_by_intensity total_intensity unsupervised_annotation harmonized_annotation_propagated harmonized_annotation_propagated_probability n_merged_proteins
A0A2R8Y3M9[P] Group XIIA secretory phospholipase A2 (EC 3.1.... NaN A0A2R8Y3M9 A0A2R8Y3M9[p] A0A2R8Y3M9 NaN A0A2R8Y3M9[p] unclassified NaN NaN ... NaN NaN 83 0.016135 1.190476 1.355319 Centrosome NaN 0.385025 1
A0A3B3ITR4[P] Krev interaction trapped protein 1 (Leucine-ri... NaN A0A3B3ITR4;A0A0C4DG23;C9J718;C9JF32;C9JBN7;C9J... A0A3B3ITR4[p] A0A3B3ITR4 NaN A0A3B3ITR4[p] recycling_endosome NaN NaN ... NaN NaN 84 -0.65914 0.0 -55.367732 Cell membrane Cell membrane 1.0 1
A0A5C2GRJ2[P] IG c1334_heavy_IGHV3-73_IGHD6-6_IGHJ4 NaN A0A5C2GRJ2 A0A5C2GRJ2[p] A0A5C2GRJ2 NaN A0A5C2GRJ2[p] unclassified NaN NaN ... NaN NaN 82 -0.390949 2.380952 -32.839735 Nucleus Nucleus 0.750001 1
A0A7D5Y1P9[P] E1A binding protein p300 NaN A0A7D5Y1P9 A0A7D5Y1P9[p] A0A7D5Y1P9 NaN A0A7D5Y1P9[p] unclassified NaN NaN ... NaN NaN 84 0.00639 0.0 0.536731 Nucleus Nucleus 0.740299 1
A0A384MDX9[P] Far upstream element-binding protein 1 NaN B4E0X8;A0A384MDX9 A0A384MDX9[p];B4E0X8[p] B4E0X8;A0A384MDX9 NaN A0A384MDX9[p] nucleus NaN NaN ... NaN NaN 84 0.007124 0.0 0.598452 Nucleus Nucleus 0.800081 1

5 rows × 24 columns

# combine the two datasets, keep only the common proteins,
combined = ad.concat(
    [dc_agg, orgip_agg],
    axis=1,  # This specifies a horizontal concatenation. We are keeping the proteins as rows and concatenating the experimental fractions or pulldowns from both datasets as columns.
    join="inner",
    merge="first",
    keys=["dc", "orgip"],
    label="dataset",
)
combined
AnnData object with n_obs × n_vars = 6997 × 78
    obs: 'protein_name', 'gene_symbol', 'Protein IDs', 'Peptide counts (all)', 'Peptide counts (razor+unique)', 'Peptide counts (unique)', 'Protein names', 'Gene names', 'Number of proteins', 'Peptides', 'Razor + unique peptides', 'Unique peptides', 'Sequence coverage [%]', 'Unique + razor sequence coverage [%]', 'Unique sequence coverage [%]', 'Mol. weight [kDa]', 'Sequence length', 'Sequence lengths', 'Fraction average', 'Fraction 1', 'Fraction 2', 'Fraction 3', 'Q-value', 'Score', 'Intensity', 'iBAQ', 'MS/MS count', 'id', 'Peptide IDs', 'Peptide is razor', 'Mod. peptide IDs', 'Oxidation (M) site IDs', 'Oxidation (M) site positions', 'Length', 'Gene Names (primary)', 'marker_lilley', 'marker_christopher', 'marker_geladaki', 'marker_itzhak', 'marker_villaneuva', 'marker_hein2025', 'marker_hein2025_gt', 'n_samples_by_intensity', 'mean_intensity', 'log1p_mean_intensity', 'pct_dropout_by_intensity', 'total_intensity', 'log1p_total_intensity', 'n_imputed', 'leiden_0.5', 'leiden_0.8', 'leiden_1.0', 'leiden_2.0', 'leiden_3.0', 'leiden_5.0', 'leiden_8.0', 'leiden_12.0', 'leiden_15.0', 'leiden_15.0_CC_enrichment_consolidated', 'leiden_15.0_CC_enrichment_consolidated_Adjusted P-value Bonferroni', 'logEnrichmentPval', 'leiden_15.0_CC_enrichment_consolidated_filtered0.05', 'leiden_15.0_CC_enrichment_consolidated_filtered0.01', 'leiden_15.0_CC_enrichment_consolidated_filtered0.001', 'leiden_3.0_CC_enrichment', 'leiden_3.0_CC_enrichment_Adjusted P-value Bonferroni', 'leiden_15.0_CC_enrichment', 'leiden_15.0_CC_enrichment_Adjusted P-value Bonferroni', 'Topological domain', 'Transmembrane', 'Intramembrane', 'has_signal', 'has_transmem', 'has_intramem', 'has_topo_dom', 'mitocarta', 'mitocarta_evidence', 'mitocarta_subloc', 'mitocop', 'author_annotation', 'author_markers', 'unsupervised_annotation', 'harmonized_annotation_propagated', 'harmonized_annotation_propagated_probability', 'n_merged_proteins', 'author_gene_symbol', 'Majority protein IDs', 'Gene_name_canonical'
    var: 'development_stage', 'tissue', 'sex', 'organism', 'disease', 'cell_line', 'cell_type', 'subcellular_enrichment', 'mean', 'std', 'enrichment_strategy', 'mass_spectrometry_method', 'cell_type_ontology_term_id', 'perturbation', 'disease_ontology_term_id', 'organism_ontology_term_id', 'sex_ontology_term_id', 'development_stage_ontology_term_id', 'tissue_ontology_term_id', 'cell_line_ontology_term_id', 'tissue_type', 'tissue_general_ontology_term_id', 'tissue_general', 'organ_ontology_term_id', 'organ', 'system_ontology_term_id', 'system', 'dataset'
    layers: 'pvals'
# compute UMAP embeddings
sc.pp.neighbors(combined, use_rep="X")
sc.tl.umap(combined, min_dist=0.1)
# plot UMAP of the integrated dataset
sc.pl.umap(combined, color="marker_hein2025_gt")
../../_images/afcf94df9c45d4c16a91b14460168353b87cff50177f48673344844d39559a4a.png