Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Analyzes cfDNA fragment size distributions and fragmentomics features using FinaleToolkit or Griffin. Extracts nucleosome positioning patterns, fragment ratios, and DELFI-style fragmentation profiles for cancer detection. Use when leveraging fragment patterns for tumor detection or tissue-of-origin analysis.
.claude/skills/bio-fragment-analysis/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | — | — |
| case-11 | ✗→✓ | ▲ Improved | — | — |
| case-02 | ✗→✓ | ▲ Improved | — | — |
| case-18 | ✗→✓ | ▲ Improved | — | — |
| case-05 | ✗→✓ | ▲ Improved | — | — |
Reference examples tested with: numpy 1.26+, pandas 2.2+, pysam 0.22+
Before using code patterns, verify installed versions match. If versions differ:
pip show <package> then help(module.function) to check signaturesIf code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt the example to match the actual API rather than retrying.
"Analyze cfDNA fragment patterns for cancer detection" → Extract fragmentomics features (size distributions, nucleosome positioning, DELFI profiles) from cfDNA for tumor detection and tissue-of-origin analysis.
FinaleToolkit or Griffin for fragment feature extractionpysam for custom fragmentomics analysisAnalyze cfDNA fragmentomics for cancer detection and characterization.
| Tool | Description | Use Case | |------|-------------|----------| | FinaleToolkit | DELFI-style patterns, MIT license | General fragmentomics | | Griffin | Nucleosome profiling | Tissue deconvolution |
Note: DELFI is a commercial company, NOT software. Use FinaleToolkit (MIT license) which replicates DELFI patterns and is 50x faster.
pythonimport pysam import numpy as np import pandas as pd def calculate_fragment_metrics(bam_path): ''' Calculate cfDNA fragment metrics. Key ratios for cancer detection: - Short (100-150 bp) vs Long (151-220 bp) - ctDNA tends to be shorter than normal cfDNA ''' bam = pysam.AlignmentFile(bam_path, 'rb') sizes = [] for read in bam.fetch(): if read.is_proper_pair and not read.is_secondary and read.template_length > 0: sizes.append(read.template_length) bam.close() sizes = np.array(sizes) # DELFI-style ratios short = np.sum((sizes >= 100) & (sizes <= 150)) long = np.sum((sizes >= 151) & (sizes <= 220)) metrics = { 'total_fragments': len(sizes), 'median_size': np.median(sizes), 'mean_size': np.mean(sizes), 'short_fragments': short, 'long_fragments': long, 'short_long_ratio': short / long if long > 0 else np.nan, # Mononucleosome peak 'mono_peak_fraction': np.sum((sizes >= 150) & (sizes <= 180)) / len(sizes) } return metrics
pythonimport finaletoolkit as ft import pandas as pd def run_finaletoolkit(bam_path, output_prefix): ''' Run FinaleToolkit for DELFI-style fragmentomics. FinaleToolkit 0.7.1+ required. ''' # Extract fragment sizes fragments = ft.read_fragments(bam_path) # Calculate genome-wide fragmentation profile # 5Mb bins as in DELFI profile = ft.calculate_fragmentation_profile( fragments, bin_size=5_000_000, short_range=(100, 150), long_range=(151, 220) ) profile.to_csv(f'{output_prefix}_frag_profile.csv') # Calculate coverage-corrected ratios ratios = ft.calculate_short_long_ratios( fragments, bin_size=5_000_000, gc_correct=True ) return profile, ratios
pythonimport subprocess def run_griffin(bam_path, sites_bed, output_dir): ''' Run Griffin for nucleosome positioning analysis. Griffin 0.2.0+ required. ''' # Griffin analyzes nucleosome accessibility around regulatory sites subprocess.run([ 'griffin', '--bam', bam_path, '--sites', sites_bed, # TSS, CTCF, etc. '--output', output_dir, '--window', '2000', # bp around site '--fragment_length', '120-180' ], check=True)
Goal: Generate a genome-wide map of short-to-long fragment ratios across fixed-size bins, replicating the DELFI approach for cancer detection from cfDNA fragmentomics.
Approach: Iterate over proper-pair fragments in each genomic bin, classify each as short (100-150 bp) or long (151-220 bp), and compute the short/long ratio per bin as the fragmentation feature vector.
pythonimport pysam import numpy as np def calculate_binned_profile(bam_path, bin_size=5_000_000, chromosomes=None): ''' Calculate fragment profiles in genomic bins. Similar to DELFI approach. ''' if chromosomes is None: chromosomes = [f'chr{i}' for i in range(1, 23)] bam = pysam.AlignmentFile(bam_path, 'rb') profiles = {} for chrom in chromosomes: try: chrom_len = bam.get_reference_length(chrom) except Exception: continue n_bins = (chrom_len // bin_size) + 1 short_counts = np.zeros(n_bins) long_counts = np.zeros(n_bins) for read in bam.fetch(chrom): if not read.is_proper_pair or read.is_secondary: continue if read.template_length <= 0: continue bin_idx = read.reference_start // bin_size if bin_idx >= n_bins: continue size = read.template_length if 100 <= size <= 150: short_counts[bin_idx] += 1 elif 151 <= size <= 220: long_counts[bin_idx] += 1 # Calculate ratio per bin with np.errstate(divide='ignore', invalid='ignore'): ratios = short_counts / long_counts ratios[~np.isfinite(ratios)] = np.nan profiles[chrom] = { 'short': short_counts, 'long': long_counts, 'ratio': ratios } bam.close() return profiles
| Pattern | Interpretation | |---------|----------------| | Higher short/long ratio | Possible tumor signal | | Altered nucleosome positioning | Epigenetic changes | | Tissue-specific patterns | Tissue of origin | | Modal peak shift | cfDNA quality issue or biology |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 20 counted toward the lift figure. The other 2 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +23 percentage points is the difference between those two pass rates over the 20 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.