Skip to content

Feature: utils.apply_peptide_batch_effects() - #49

Merged
idf-io merged 1 commit into
mainfrom
feature/apply-batch-effects
Oct 24, 2026
Merged

idf-io merged 1 commit into
mainfrom
feature/apply-batch-effects

Conversation

@idf-io

@idf-io idf-io commented Oct 24, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

pr.utils.apply_peptide_batch_effects() plants a peptide-level batch effect in a dataset.

Related issues

None

General checklist

  • Branch name and commit subjects follow the repo conventions
  • HISTORY.md updated under [Unreleased]
  • CI is green (flake8, pylint, pytest). Locally: flake8 and pylint (errors only) are clean and the full suite passes (705 tests); CI has not run yet

✨ Feature

What it adds:

pr.utils.apply_peptide_batch_effects() with flavor "subset_shift":

  • In each eligible protein, n_peptides peptides are drawn and shifted by the same log2fc in every sample of the affected batches. All other values are unchanged.
  • batches as a list names the affected batch labels; as an int it draws that many batches at random. At least one batch must stay unaffected.
  • Only proteins with more than n_peptides peptides are eligible, so every affected protein keeps at least one unshifted peptide. fraction sets the share of eligible proteins, randomly selected, that receive the effect.
  • log_space=False (default) multiplies by 2 ** log2fc; log_space=True adds log2fc and assumes log2-transformed .X. It warns if log_space does not match whether .X looks log-transformed.
  • Ground truth goes to .var["batch_effect"], and parameters and seed to .uns["batch_effect_simulation"].

A shift that is constant within a batch leaves every within-batch peptide correlation unchanged but separates the shifted peptides from their siblings across batches. Naive COPF can then call spurious proteoform groups, while batch-aware COPF is unaffected by construction.

Example usage:

import pandas as pd
import proteopy as pr

records = []
for log2fc in [0.0, -1.0, -2.0, -3.0]:
    for batch_aware in (False, True):
        simulator = pr.utils.proteoform_group_simulator(
            adata,
            n_iterations=5,
            n_mixed=500,
            n_unmixed=500,
            n_mixed_peptides=2,
            random_state=42,
        )
        for i, sim_adata in enumerate(simulator):
            if log2fc != 0:
                pr.utils.apply_peptide_batch_effects(
                    sim_adata,
                    batch_key="replicate",
                    batches=1,
                    log2fc=log2fc,
                    random_state=i,
                )
            pr.tl.infer_proteoform_groups(
                sim_adata,
                method="copf",
                batch_key="replicate" if batch_aware else None,
                min_score=0.1,
                min_adj_pval=0.1,
            )
            metrics, _ = pr.utils.proteoform_simulation_metrics(sim_adata)
            records.append(
                {"log2fc": log2fc, "batch_aware": batch_aware, **metrics}
            )

pd.DataFrame(records).groupby(["batch_aware", "log2fc"]).mean()

Pilot on the Schessner et al. (2023) DOM data (PXD034969, 36 samples, 3 replicates as batches), 2 iterations per arm, one random replicate shifted. These runs used an earlier, uncommitted version of the function (apply_batch_effects(), flavor "peptide_mix"). The draws and the shift in linear space are unchanged in this PR, but the numbers were not re-run after the renames:

log2 FC naive: detection rate naive: background rate batch-aware: detection rate batch-aware: background rate
none 0.70 0.09 0.73 0.10
−1 0.67 0.08 0.73 0.10
−2 0.67 0.20 0.73 0.10
−3 0.67 0.31 0.73 0.10

Naive COPF calls many more background proteins from log2 FC −2 on; batch-aware COPF is unchanged at every effect size.

  • Follows the AGENTS.md function and module conventions
  • Tests added for the new behavior (tests/utils/test_batch_effect_simulation.py, 40 tests)
  • Numpydoc docstring with an example
  • Added to the Sphinx API docs (docs/sphinx/source/api/utils.rst). The Sphinx build was not run locally
  • Relevant tutorial notebooks updated, or none affected (none affected; the analysis notebooks used for the pilot are not part of this PR)

🤖 Generated with Claude Code

Simulates peptide-specific batch effects to evaluate batch-aware
proteoform inference together with utils.proteoform_group_simulator()
and utils.proteoform_simulation_metrics().

- flavor="subset_shift": n_peptides peptides per protein are shifted
  by the same log2fc in every sample of the affected batches
- batches as list selects batch labels, as int draws them at random;
  at least one batch stays unaffected
- only proteins with more than n_peptides peptides are eligible;
  fraction limits the share of randomly selected proteins
- log_space=False multiplies by 2 ** log2fc, True adds log2fc
  (assumes log2 data); warns if log_space does not match .X
- ground truth in .var['batch_effect'], parameters and seed in
  .uns['batch_effect_simulation']

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-24T08:09:02.094632Z 27bcec6 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 27bcec6560

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +329 to +330
affected_batches = [
b.item() if isinstance(b, np.generic) else b for b in affected_batches

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve NumPy datetime batch labels before matching

When batches contains nanosecond-resolution np.datetime64 labels, .item() converts each label to an integer timestamp; the subsequent Series.isin() comparison against a datetime batch column then matches no observations. The function consequently flags peptides and records an affected batch in .uns while leaving every intensity unchanged. Build obs_mask from the original labels before converting values for serialization, or normalize datetime labels without changing their comparison semantics.

Useful? React with 👍 / 👎.

else:
X = np.array(Xsrc, dtype=float)

looks_log = is_log_transformed(adata)[0]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid densifying sparse input twice

For sparse .X, line 300 already retains a full dense float matrix, but is_log_transformed(adata) densifies the original sparse matrix again internally. Large peptide matrices therefore require roughly two simultaneous dense copies and can run out of memory despite sparse input being explicitly supported; run the log-scale heuristic on the already-created X array instead.

AGENTS.md reference: AGENTS.md:L71-L76

Useful? React with 👍 / 👎.

@idf-io
idf-io merged commit 84f7df2 into main Oct 24, 2026
7 checks passed
@idf-io
idf-io deleted the feature/apply-batch-effects branch October 24, 2026 18:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant