- offline GTF now picks MANE>appris1>canonical>basic>protein_coding (318 vs 3018 rows at VAF>10%) - online Ensembl REST likewise picks best (mane_select/canonical) - --all-transcripts restores previous all-transcripts behavior
ffpe_damage
Методологически корректная оценка дезаминирования в FFPE-образцах (формалин-фиксированная парафинизированная ткань) по BAM.
Инструмент не изобретает универсальный «FFPE damage score». Он выдаёт измеряемые показатели:
- все 12 типов mismatch;
- нормализованную частоту
C>TиG>Aна каждой позиции read; - отдельно R1/R2;
- отдельно forward/reverse alignment;
- расстояния от 5′- и 3′-конца read;
- профиль частот (не просто количество mismatch);
- BED-треки для IGV;
- CSV/TSV для сравнения образцов между собой.
Классификация low / moderate / high назначается только при сравнении с валидированными контрольными образцами, приготовленными тем же протоколом (ffpe_compare.py). Абсолютных порогов типа «>5% = плохо» нет — у них нет валидированного основания.
Что считаем
Для каждой позиции i в read:
C>T_i = #(C→T на позиции i) / #(C на позиции i)
G>A_i = #(G→A на позиции i) / #(G на позиции i)
Знаменатель — все высококачественные наблюдения соответствующего reference-основания на этой позиции, включая совпадения (matches). Это принципиально: только так частота сопоставима между позициями и образцами.
Фильтрация: убираются unmapped/secondary/supplementary, MAPQ < --mapq, основание с BQ < --baseq, дупликаты (кроме --include-duplicates). Read-позиция отсчитывается как от 5′-конца (position_5prime), так и от 3′-конца (position_3prime).
Установка
cd ~/Projects/ffpe_damage
python3 -m venv --system-site-packages .venv
.venv/bin/pip install pysam==0.24.0 # плюс pandas, matplotlib, scipy
Требования на входные данные:
- BAM отсортирован и проиндексирован (
.bai); - референсная FASTA проиндексирована (
.fai).
1. Анализ образца
.venv/bin/python ffpe_damage_v2.py \
--bam sample.bam \
--reference hg38.fa \
--outdir FFPE_sample
Строгий вариант:
.venv/bin/python ffpe_damage_v2.py \
--bam sample.bam --reference hg38.fa --outdir FFPE_sample \
--mapq 30 --baseq 20 --min-depth 30 --min-alt-count 5
Выходные файлы
| Файл | Содержимое |
|---|---|
sample_summary.csv |
число прочитанных/использованных ридов, usable bases, глобальная частота C>T/G>A, пороги фильтрации |
substitution_summary.csv |
все 12 типов замен: count, знаменатель, частота |
substitution_all12.png |
бар-чарт частот всех 12 типов (C>T и G>A выделены) |
normalized_damage_by_read_position.csv |
полный профиль: R1/R2 × позиция × reference-base × замена |
normalized_damage_by_read_position_3prime.csv |
то же, по расстоянию от 3′-конца |
CtoT_GtoA_normalized_profile.csv |
ключевая таблица «позиция в read vs C>T% / G>A%» |
CtoT_GtoA_normalized_profile_3prime.csv |
то же по 3′-концу |
strand_damage_profile.csv |
C>T/G>A по цепи выравнивания (+/-) |
strand_damage_profile_3prime.csv |
то же по 3′-концу |
end_enrichment.csv |
C>T/G>A % в первых/последних 1,3,5,10,20 основаниях, по R1/R2 |
candidate_CtoT.bed, candidate_GtoA.bed |
позиции для IGV; имя = ref>alt;depth;alt;VAF |
candidate_variants.csv |
контекст каждой позиции: VAF, средняя позиция в read, strand bias, BQ, раскладка R1/R2 |
clean_variants.csv / ffpe_suspect_variants.csv |
разделение на чистые и подозрительные |
CtoT_R1.png … GtoA_R2_3prime.png |
графики частоты по позиции в read (5′ и 3′) |
Как читать результат
- Классическая FFPE-сигнатура: пик
C>Tу 5′-конца R1 иG>Aу 3′-конца R2 — две цепи одного и того же дезаминирования цитозина. Проверяйте профили R1/R2 и по strand. - Если C>T/G>A повышены равномерно по всей длине read (без пика у концов) — это не классическое end-associated повреждение, а другой артефакт/ошибки.
- Отличить настоящий вариант от дезаминирования (
candidate_variants.csv): настоящий вариант имеет ожидаемый VAF, распределён по read (средняя позиция ~ середина), встречается на обеих цепях (strand bias не значим) и не привязан к концам. Дезаминирование концентрируется у концов read и на одной цепи.
2. Классификация относительно контролей
.venv/bin/python ffpe_compare.py \
--controls sampleA_out sampleB_out sampleC_out \
--query query_sample_out \
--out compare_out \
--window 5 --k 2
--controls принимает несколько папок с выходами шага 1 (в них лежит end_enrichment.csv) либо один файл-манифест с путями по одному в строке.
Метрики (окно W от конца read):
C_to_T_R1_5prime_W— C>T в первых W основаниях R1 (forward);G_to_A_R2_3prime_W— G>A в последних W основаниях R2 (reverse);combined— среднее двух.
Классификация против среднего M и SD S контролей, z = (query − M)/S:
high : z > k
moderate : -k ≤ z ≤ k
low : z < -k
Выход: classification.csv, comparison_table.csv, control_vs_query.png.
Зачем сравнивать только с контролями? FFPE-повреждение зависит от протокола (UDG-обработка, end repair, длина фрагментов, панель). Одно и то же значение частоты может быть нормой для одного протокола и тяжёлым повреждением для другого. Поэтому сравнивать стоит только образцы, приготовленные одинаково, а классификация всегда относительная.
3. Синтетические данные для проверки
.venv/bin/python make_test_data.py --outdir test_data/damaged --damage-rate 0.5
.venv/bin/python make_test_data.py --outdir test_data/clean --damage-rate 0.0
.venv/bin/python ffpe_damage_v2.py \
--bam test_data/damaged/sample.sorted.bam \
--reference test_data/damaged/synthetic_reference.fa \
--outdir test_data/damaged/out
Встроенный ground truth:
- damaged: C>T у 5′-конца forward-ридов (R1), G>A у 3′-конца reverse-ридов (R2), плюс один «настоящий» гетерозиготный SNP без read-position/strand bias — его инструмент не должен классифицировать как дезаминирование;
- clean: только фоновая ошибка секвенирования.
Ожидаемые результаты на damaged:
- R1, 5′-профиль: C>T ~48% на позициях 1–5, ~0 дальше;
- R2, 3′-профиль: G>A ~50% в последних 5 основаниях, ~0 дальше;
- SNP на
chr1:2504: VAF ~50%, середина read, обе цепи, без strand bias.
Оговорки
- Это QC/исследовательский инструмент, а не валидированный клинический assay.
- C>T/G>A в обычном alignment нельзя автоматически интерпретировать как химическое дезаминирование при поиске соматических вариантов: нужны read-position/strand/base-quality, а в идеале — matched normal и сопоставление с variant calls.
- Сравнивать нужно только образцы одного протокола подготовки библиотеки.