Skip to content

Support multi-file analyses and improve logging#3

Open
wsnoble wants to merge 6 commits into
mainfrom
multi-file-support
Open

Support multi-file analyses and improve logging#3
wsnoble wants to merge 6 commits into
mainfrom
multi-file-support

Conversation

@wsnoble

@wsnoble wsnoble commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fix multi-file join bug: The join between Percolator and Casanovo results previously used scan number alone, which is not unique across files. Now joins on (file_stem, scan), extracting the file stem from Percolator's filename column and from the mztab MTD header's ms_run-to-location mapping.
  • Fix peptide stripping with decimal modifications: The old x.split('.') approach broke when modification masses contained decimal points (e.g. Q[0.984]), causing flanking residues to be included in the sequence. Fixed by using the x[2:-2] slice directly.
  • Add --fdr argument: Allows the Percolator FDR threshold used for calibration to be set at the command line (default: 0.01).
  • Fix imports: Changed bare from preprocessing import ... to relative from .preprocessing import ... so the package works correctly when invoked as python -m glissade.glissade.
  • Add if __name__ == '__main__' guard and write glissade_stats.json with matched/external peptide counts.
  • Improve logging: Report PSM counts from each input file, FDR pass rates, Casanovo/Percolator agreement rates, min-length filter retention, and unique-peptide collapse ratios at each step. All percentage prints guard against zero denominators.
  • Hoist import os to module scope and guard spectra_ref parsing against malformed entries.

Test plan

  • Run on a multi-file dataset and verify the joined PSM count is correct (previously scan-number collisions inflated or deflated the join)
  • Verify --fdr 0.1 gives more matched calibration peptides than the default --fdr 0.01
  • Check that glissade_stats.json is written with correct counts
  • Confirm log output shows per-step PSM counts and percentages

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a new --fdr option to let you control the database FDR threshold from the command line.
    • Added progress markers and a new summary report file with score counts for each run.
  • Bug Fixes

    • Improved multi-file data matching so results are joined using both file source and scan number.
    • Better handles .mztab inputs by skipping entries that can’t be parsed cleanly.
  • Chores

    • Switched to package-safe imports for more reliable execution.

wsnoble and others added 3 commits July 6, 2026 02:34
- Fix join to use (file_stem, scan) instead of scan alone, enabling
  correct multi-file analyses where scan numbers are not unique across
  files
- Parse ms_run->file mapping from mztab MTD header; extract file_stem
  from Percolator filename column
- Fix peptide stripping to handle bracket notation with decimal points
  (e.g. Q[0.984]) by using x[2:-2] slice instead of split('.')
- Add --fdr argument to control the Percolator FDR threshold used for
  calibration (default 0.01)
- Fix bare imports to relative imports so the package works correctly
  with python -m glissade.glissade
- Add if __name__ == '__main__' guard
- Write glissade_stats.json with matched/external counts
- Improve logging throughout: PSM counts, FDR pass rates, agreement
  rates, min-length filter retention, and unique peptide collapse ratios

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Hoist `import os` to module level (was conditionally scoped inside
  psms.txt branch, would cause UnboundLocalError in the mztab path)
- Guard `spectra_ref` parsing against malformed/missing patterns;
  helper functions return None/'' instead of raising AttributeError
- Guard all percentage logging against zero denominators in
  align_to_reference() and seperate_scores()
- Update read_data() docstring to reflect (file_stem, scan) join key

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Support multi-file analyses and improve logging
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@wsnoble, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2accdcba-999b-4b23-8f7b-03e42d14594a

📥 Commits

Reviewing files that changed from the base of the PR and between b798357 and 91f097f.

📒 Files selected for processing (2)
  • src/glissade/glissade.py
  • src/glissade/preprocessing.py
📝 Walkthrough

Walkthrough

Preprocessing's read_data() now joins on a derived file_stem plus scan to support multi-file analyses, parsing mzTab spectra_ref/ms_run mappings. align_to_reference() and seperate_scores() add printed count/percentage diagnostics. glissade.py adds a --fdr CLI option, uses n_bootstraps for bootstrapping, and writes glissade_stats.json.

Changes

Multi-file Join and FDR/Stats Reporting

Layer / File(s) Summary
Multi-file join via file_stem
src/glissade/preprocessing.py
read_data() derives file_stem from filenames and mzTab spectra_ref/ms_run mappings, drops unparseable rows, and joins on ['file_stem','scan'] instead of scan alone; imports reformatted.
Alignment and separation diagnostics
src/glissade/preprocessing.py
align_to_reference() and seperate_scores() print derived counts and percentages for FDR-passing PSMs, peptide agreement, and matched/external candidates before/after length filtering.
CLI FDR option and stats output
src/glissade/glissade.py
Switches to relative imports, adds a --fdr CLI argument passed to align_to_reference(), uses n_bootstraps for bootstrap runs, adds stage print statements, and writes glissade_stats.json with matched/external counts.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI as glissade.py main()
  participant Preprocessing as preprocessing.py
  participant FDR as FDR control
  participant Output as glissade_stats.json / peptide.tsv

  CLI->>Preprocessing: read_data(files)
  Preprocessing->>Preprocessing: derive file_stem, join on [file_stem, scan]
  Preprocessing-->>CLI: joined dataframe
  CLI->>Preprocessing: align_to_reference(df, database_fdr_threshold=args.fdr)
  Preprocessing->>Preprocessing: compute in_tide/agrees counts and percentages
  Preprocessing-->>CLI: aligned dataframe
  CLI->>Preprocessing: seperate_scores(df, min_length)
  Preprocessing->>Preprocessing: log matched/external counts before/after filtering
  Preprocessing-->>CLI: matched, external sets
  CLI->>FDR: run_procedure(n_boots=n_bootstraps)
  FDR-->>CLI: q-values
  CLI->>Output: write glissade_stats.json
  CLI->>Output: write_results() -> peptide.tsv
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: multi-file analysis support and expanded logging.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch multi-file-support

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI 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.

Pull request overview

This PR improves Glissade’s ability to run multi-file analyses by fixing the Percolator↔Casanovo join key (now (file_stem, scan) instead of scan alone) and adds more user-facing instrumentation and CLI configurability to make calibration/FDR behavior easier to understand and control.

Changes:

  • Join Percolator and mzTab results on (file_stem, scan) by extracting file identity from Percolator filename and mzTab ms_run[..]-location headers.
  • Fix peptide stripping when modification masses contain decimal points by avoiding split('.') and using x[2:-2].
  • Add --fdr CLI flag, add __main__ guard, emit glissade_stats.json, and expand step-by-step logging.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/glissade/preprocessing.py Adds multi-file join keys, safer mzTab header parsing, and more per-step logging around FDR/agreement/filtering.
src/glissade/glissade.py Switches to relative imports, adds --fdr, writes stats JSON, adds __main__ entrypoint guard, and improves console logging.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +49 to +51
def _parse_scan(ref):
m = re.search(r'scan=(\d+)', ref)
return int(m.group(1)) if m else None

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in b798357 — both helpers now coerce ref to str before applying regex, handling NaN/None safely.

Comment on lines +52 to +54
def _parse_stem(ref):
m = re.match(r'(ms_run\[\d+\])', ref)
return run_map.get(m.group(1), '') if m else ''

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in b798357 — same coercion added to _parse_stem.

Comment on lines +55 to +58
dn_scans = [_parse_scan(x) for x in denovo_df['spectra_ref']]
dn_stems = [_parse_stem(x) for x in denovo_df['spectra_ref']]
denovo_df['scan'] = dn_scans
denovo_df['file_stem'] = dn_stems

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in b798357 — rows where scan parsing returned None are now dropped with dropna() and the column is cast to int before the join.

Comment thread src/glissade/glissade.py Outdated
- Guard _parse_scan/_parse_stem against NaN/None spectra_ref values by
  coercing to str before applying regex
- Drop rows where scan parsing failed and cast scan column to int so the
  join key is well-typed and matches are not silently lost
- Pass n_bootstraps CLI argument through to run_procedure instead of
  hard-coding 250

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@wsnoble

wsnoble commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/glissade/preprocessing.py`:
- Around line 40-48: Initialize skiprows before the loop in the preprocessing
logic so it is always defined even if denovo_file is empty or malformed, then
keep using it when calling pd.read_csv. Update the code around run_map and the
loop that parses MTD/PSH lines in preprocessing.py to set a safe default for
skiprows before enumerate(f_in), so the denovo_df read path does not raise a
NameError.
- Around line 32-33: Guard the new file_stem and scan join in preprocessing by
validating that db_df contains filename before using it, and by normalizing both
sides to the same basename consistently. In preprocessing.py, update the logic
around the file_stem derivation and the join against the mzTab scan data so
missing filename values raise a clear error or fall back safely instead of
causing a KeyError or silent row loss. Also add logging in the code path that
builds or merges on file_stem to report unmatched stems when no overlap is
found.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3f60dde3-2989-462e-ac79-cd71dfde773e

📥 Commits

Reviewing files that changed from the base of the PR and between eaaeac9 and b798357.

📒 Files selected for processing (2)
  • src/glissade/glissade.py
  • src/glissade/preprocessing.py

Comment thread src/glissade/preprocessing.py
Comment thread src/glissade/preprocessing.py
- Guard db_df['filename'] access with a clear ValueError if the column
  is absent, preventing a silent KeyError on non-standard psms.txt files
- Initialize skiprows=0 before the mzTab header loop to prevent
  NameError if the file is empty or malformed
- Log any file stems present in only one of the two inputs after the
  join, making stem-mismatch problems visible

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment on lines +63 to +68
denovo_df['file_stem'] = dn_stems
n_before = len(denovo_df)
denovo_df = denovo_df.dropna(subset=['scan'])
denovo_df['scan'] = denovo_df['scan'].astype(int)
if len(denovo_df) < n_before:
print(f" Dropped {n_before - len(denovo_df)} Casanovo rows with unparseable spectra_ref")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 91f097f — rows with an empty file_stem are now dropped alongside rows with unparseable scan numbers, so they no longer appear in the unmatched-stems warning.

Comment thread src/glissade/glissade.py
Comment on lines +173 to +174
print(f'--- Running FDR control on {len(external_scores)} external peptides using {len(matched_scores)} matched scores ---')
fdrs, peps, scores = run_procedure(matched_scores, external_scores, external_peps, n_boots=n_bootstraps)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 91f097f — added explicit ValueError guards before calling run_procedure() when either matched_scores or external_scores is empty, with actionable messages.

- Drop Casanovo rows with empty file_stem (unmatched ms_run entries)
  alongside rows with unparseable scan numbers, preventing them from
  polluting the unmatched-stems warning with empty-string entries
- Guard run_procedure() against empty matched_scores or external_scores
  with clear ValueError messages rather than a cryptic crash inside the
  FDR procedure

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

2 participants