- PVSM.RU - https://www.pvsm.ru -

Как взломать Генотек. Я навайбкодила свой ДНК-анализатор, и он работает

Как взломать Генотек. Я навайбкодила свой ДНК-анализатор, и он работает - 1

Дисклеймер: не читайте эту статью, если вы фанат традиционных ценностей и от заголовка уже хотите написать в комментах, что мне стоит изучить базу и отучиться в меде. Читайте эту статью только если вы прогрессивный, веселый и любите необычные проекты ♥ Статья носит исключительно научпоп-инженерно-юмористический характер. Не используйте самодельные скрипты для постановки диагнозов (хотя сходить на ПЦР-тест или к врачу после них никто не запрещает)!

Скажите, вы же тоже ожидали большего от интерпретаций коммерческих ДНК-тестов типа Genotek, 23andMe, Atlas? Ну вот и я не была в восторге от того, что я на 4.2% скандинавка, мне вреден арахис, и скорее всего я люблю кинзу.

Не будучи программисткой (но будучи профессиональным ипохондриком с неуемным любопытством), я почувствовала, что в бухгалтерии что-то напутали. Где глубокие выкладки? Где полигенные риски болезней? Где куча инфы, в которую можно закопаться?

Как взломать Генотек. Я навайбкодила свой ДНК-анализатор, и он работает - 2

Официальные лаборатории зажаты жесткими рамками регуляторов и юристов, поэтому они выдают клиентам лишь самую базовую «поп-генетику» без тысяч медицинских дисклеймеров. Но у нас на руках остаётся самое главное — сырой VCF-файл (Raw DNA Data).

В этой статье я расскажу, как я на диком азарте построила свой пайплайн анализа генома. Скриншоты вставила выше. Мы пройдем от пересборки координат и импутации 40 миллионов маркеров на суперкомпьютере TOPMed до укрощения памяти Node.js, чтобы скрипт не умер от 43 ГБ данных, и я покажу, как все устроено под капотом. Осторожно, много блоков кода!

Валидация и контроль качества: Чтобы не превратить анализ в «гадание на кофейной гуще», все полученные данные проходят строгую биоинформатическую валидацию (контроль качества импутации $R^2 ge 0.3$, вероятности генотипа $GP ge 0.90$, маркировка редких вариантов с $MAF < 0.01$). Алгоритмы фазирования и импутации были дополнительно валидированы кросс-анализом с геномом родственника через GEDmatch (IBD-сегменты на хромосоме 16 с перекрестной проверкой по 1408 фазово-информативным сайтам). Все клинические интерпретации строятся строго на аннотациях из свежих научных баз NCBI ClinVar (GRCh38) и PGS Catalog.

Пайплайн в двух словах

Чтобы понять масштабы задумки, вот как выглядит итоговая архитектура.

[Сырой VCF из лаборатории (~700k SNPs, hg19)]
                       │
                       ▼
            [CrossMap Liftover / Pure Python → hg38]
                       │
                       ▼
        [Подготовка VCF + Dummy Samples 0/0 + BGZFWriter (Python)]
                       │
                       ▼
     [TOPMed / Michigan Imputation Server (Eagle2 + Minimac4)]
                       │
                       ▼
   [Гигантский Imputed VCF (42.9 GB, ~40M+ SNPs)]
                       │
                       ▼
┌─────────────────────────────────────────────────────────┐
│     DNA Decoder Engine (TypeScript / Node.js)           │
│                                                         │
│ 1. gatherNeededRsIds() — сбор котировок из панелей      │
│ 2. loadVcfCombined()   — потоковый VCF-парсер (R²/GP)   │
│ 3. scanClinVar()       — матчинг с базами NCBI ClinVar  │
│ 4. computeAllPRS()     — вычисление полигенных рисков   │
└─────────────────────────────────────────────────────────┘
                       │
                       ▼
            [Интерактивный HTML + JSON Отчет]

Но обо всём по порядку!

Шаг 1. Великий Переезд: Liftover hg19 -> hg38

Коммерческие чипы обычно выдают геномные координаты в сборке GRCh37 (hg19), принятой аж в 2009 году. Это как если бы вы искали квартиру в современной Москве по карте 1980 года.

Свежие научные базы, например, NCBI ClinVar (главный мировой реестр клинических мутаций) и каталог полигенных рисков PGS Catalog — работают в сборке GRCh38 (hg38). Если попытаться натравить координаты hg19 на базы hg38, вы обнаружите у себя 500 смертельных мутаций просто потому, что геномные координаты сдвинулись на пару тысяч пар оснований! Поэтому перед началом всего процесса мы переезжаем.

Обычно для переноса координат используется консольная утилита CrossMap и специальный файл цепочек конвертации (hg19ToHg38.over.chain.gz):

Но если вы работаете на Windows без подтянутого C-окружения samtools или HTSlib, CrossMap может закапризничать. Поэтому для гибкости я написала собственный лифтовер на чистом Python, который разбирает бинарные цепочки смещений UCSC .over.chain.gz:

# scripts/liftover_vcf.py — Pure-Python конвертация координат hg19 → hg38
import gzip, collections

def load_chain(chain_gz: str) -> dict:
    """Парсит hg19ToHg38.over.chain.gz и строит карту дельт для сопоставления позиций"""
    chains = collections.defaultdict(list)
    with gzip.open(chain_gz, 'rt') as fh:
        t_chrom = t_pos = q_pos = 0
        for line in fh:
            if line.startswith('chain'):
                parts = line.split()
                # chain score tName tSize tStrand tStart tEnd qName qSize qStrand qStart qEnd id
                t_chrom, t_pos, q_pos = parts[2], int(parts[5]), int(parts[10])
            elif t_chrom and line.strip():
                size = int(line.split()[0])
                delta = q_pos - t_pos  # Смещение hg38 относительно hg19
                chains[t_chrom].append((t_pos, t_pos + size, delta))
                t_pos += size
    return chains

Посмотрите, как меняются координаты одной и той же точки генома (например, знаменитого SNP реакции на стресс rs1360780 в гене FKBP5):

# Исходный VCF от Генотека (hg19 / GRCh37):

chr16  53825834  rs1360780  C  T  .  .  .

# После лифтовера (hg38 / GRCh38):

chr16  53791924  rs1360780  C  T  .  .  .

На этом этапе мы привели координаты в норму, но столкнулись со следующей проблемой: сырой файл из лаборатории содержит всего около 700 000 генотипированных точек (SNPs). В масштабах человеческого генома (3 миллиарда пар оснований) — это смешные 0.02%. Самые интересные редкие мутации или полигенные риски просто не попали на чип, ну и чёрт с ними.

Шаг 2. А что нас вообще интересует?

Не все интересные фенотипы (от цвета глаз и чувствительности к боли до особенностей характера и реакции на кофе) есть в медицинском репозитории ClinVar. Для этого я создала декларативную плагинную JSON-архитектуру. Каждый раздел исследований — это самостоятельный .panel.json файл в папке panels/. Движок на TypeScript при старте просто сканирует папку panels/*.panel.json и на лету подхватывает новые знания без пересборки кода.

Всего я собрала 24 тематические панели знаний:

Клинический блок (ACMG): mandatory (ACMG SF 3.2 — 81 критический ген), cancer_early, cardio, carrier_screening (носительство моногенных патологий), inborn_errors_metabolism (врожденные ошибки метаболизма).

Психика, Нейромедиаторы и Личность: psych (депрессия, БАР, СДВГ), personality (реакция на стресс FKBP5, ГАМК-система GABRA2, дофаминовый метаболизм CDH13/TH, окситоцин OXTR), neurodegeneration (Альцгеймер, Паркинсон).

Иммунология и Воспаление: ankylosing_spondylitis (болезнь Бехтерева), autoimmune, innate_immunity (врожденный антивирусный иммунитет CCR5, OAS1, TLR5), allergy_skin.

Фенотип, Биохакинг и Суперспособности: phenotype (пигментация, вкус кинзы OR6A2, соматотип «Яблоко»), supertraits (редкие эволюционные мутации), sleep_circadian (совы/жаворонки CLOCK/PER3), athletics (тип мышечных волокон ACTN3), vitamins, pharmacogenomics (метаболизм лекарств CYP-ферментами).

Данные для панелей я собирала вручную из статей PubMed, GWAS Catalog, SNPedia и базы NCBI dbSNP. Каждая панель содержит список целевых генов (targetGenes), ключевые слова (keywords) и массив прямых GWAS-маркеров (directSnps) с описанием эффектов каждого аллеля.

// panels/personality.panel.json — Фрагмент описания GWAS-маркера
{
  "rsId": "rs53576",
  "gene": "OXTR",
  "name": "Окситоциновый рецептор (Эмпатия и социальность)",
  "riskAllele": "G",
  "description": "Аллель G ассоциирован с более высокой эмпатией и эмоциональной отзывчивостью."
}

Шаг 3. Колдовство импутации и 24 гомункула

Что делать, если лаборатория прочла 0.02% меня? Нужно «дорисовать» остальные 99.98% с помощью математики и статистики. Этот процесс называется геномная импутация.

Геномы людей передаются блоками (сцепленное наследование). Если мы знаем 700k опорных точек, статистические алгоритмы могут с вероятностью >99% предсказать соседние миллионы мутаций, сравнив наш геном с эталонной панелью TOPMed (более 180 000 полностью отсеквенированных геномов высокого покрытия).

Грабли #1: Требования TOPMed, хак 0/0 и свой BGZFWriter

Сервер импутации TOPMed (на базе Мичиганского университета) — бесплатный и невероятно мощный, но у него есть жесткий контроль качества (QC).

1. Проблема группового: Софт фазирования Eagle2 не хочет работать с одиночным файлом одного человека, ему нужна группа образцов! В целом, объяснимо. На 13.08.2026 TOPMed совсем поверил в себя, постоянно ремонтируется, ставит техно-энтузиастов в конец очереди и вообще начал банить личные gmail аккаунты.

2. Проблема Call Rate (Коварная ошибка для тех, кто считает себя умнее машины): Если добавить фейковые генотипы как ./. (missing data), сервер высчитает суммарный Call Rate файла как 1/24 ≈ 4.1% и с треском отклонит файл с ошибкой QC. Опять же, когда я делала свою импутацию — узнала об этом сразу же. На 13.08.2026 я, когда делала импутацию родственнику, узнавала обо всех ошибках через несколько суток.

3. Решение: Генотипы фейковых образцов-пустышек (dummy1..dummy23) нужно принудительно выставить в 0/0 (гомозигота по референсу). Тогда Call Rate становится 100%, и сервер принимает файл. В конце концов мы умнее машин?

Кроме того, серверам импутации нужны файлы в формате BGZF (.vcf.gz). Чтобы не возиться с компиляцией bgzip под Windows, я написала свой упаковщик прямо на встроенных модулях Python zlib и struct:

# scripts/prepare_topmed_input.py — Хак с dummy-образцами и BGZF-упаковщиком
import zlib, struct, os, collections

# Чистый Python BGZF-упаковщик (создает честные .vcf.gz блоки)
def bgzf_block(data: bytes) -> bytes:
    comp  = zlib.compress(data, 6)[2:-4]       # DEFLATE без zlib-обвязки
    bsize = len(comp) + 26 - 1                  # Полный размер BGZF блока
    hdr   = bytes([0x1f, 0x8b, 8, 4]) + struct.pack('<I', 0) + bytes([0, 255])
    hdr  += struct.pack('<H', 6) + bytes([0x42, 0x43]) + struct.pack('<H', 2)
    hdr  += struct.pack('<H', bsize)             # Записываем BSIZE
    crc   = zlib.crc32(data) & 0xFFFFFFFF
    return hdr + comp + struct.pack('<II', crc, len(data) & 0xFFFFFFFF)

# Подготовка колонок заголовка VCF: 1 реальный sample + 23 пустышки
dummy_names  = [f"dummy{i+1}" for i in range(23)]
# ВАЖНО: 0/0 = hom-ref (не missing!), что поднимает Call Rate до 100%
dummy_suffix = "t" + "t".join(["0/0"] * 23)

# Модификация строк VCF
line_out = f"{row_data}t{real_gt}{dummy_suffix}n"

Загружаем файлы на сервер TOPMed, ждем несколько часов в очереди суперкомпьютера... и скачиваем обратно 42.9 Гигабайта сжатых данных. Теперь у нас на руках под 40 миллионов полиморфизмов.

Защита от галлюцинаций статистики (R², GP, MAF)

Импутация — это вероятностный процесс. Статистика может нафантазировать вариант, которого на самом деле нет. Чтобы не ловить инфаркт от ложноположительных мутаций, в коде парсера заложены жесткие фильтры:

// core/loadVariants.ts — Фильтрация импутированных вариантов по качеству
export const QC = {
  MIN_R2:   0.3,  // Минимальный коэффициент R² (INFO Score). Всё, что ниже — жестко отсеивается
  MIN_GP:   0.9,  // Минимальная вероятность генотипа (Genotype Probability)
  RARE_MAF: 0.01, // Редкие импутированные варианты (MAF < 1%) требуют плашки с предупреждением
};

// Внутри потокового чтения VCF:
const info = parseInfo(infoStr);
const r2   = parseFloat(info['R2'] ?? info['INFO_SCORE'] ?? '');

// 1. Отбрасываем недостоверные статистические варианты (R² < 0.3)
if (!isNaN(r2) && r2 < QC.MIN_R2) {
  skippedR2++;
  return; // Вариант мгновенно выкидывается прямо в потоке!
}

1. R² (INFO Score) — качество импутации. Если R² < 0.3, вариант безжалостно отбрасывается (так отсеялось более 400 млн сомнительных маркеров по всем хромосомам).

2. GP (Genotype Probability) — вероятность генотипа. Если максимальная вероятность генотипа < 0.90, вариант помечается как недостоверный.

3. MAF (Minor Allele Frequency) — если редкий вариант с частотой меньше 1% (MAF < 0.01) очутился в результатах импутации, аналитический отчет помечает его ОГРОМНЫМ КРАСНЫМ БАННЕРОМ с требованием обязательной перепроверки ПЦР-тестом в реальной лаборатории.

Шаг 4. Великая битва с Node.js и Памятью V8 (Heap Out of Memory)

Итак, что мы имеем:

- clinvar_hg38.vcf.gz — база ClinVar на 190 МБ в сжатом виде (миллионы строк).

- my_data_imputed_hg38.vcf — 42.9 ГБ импутированного генома.

- Набор из 24 пользовательских панелей генов (*.panel.json) и база полигенных рисков pgs.catalog.json.

Навайбкодив всё это безумие на TypeScript, я радостно запустила npm run analyze... и через 15 секунд получила классический привет от движка V8:

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

Или еще более веселый RangeError: Map maximum size exceeded. Попытка прочитать 40 миллионов строк в обычный JS-объект или Map убивает память быстрее, чем Chrome с сотней открытых вкладок.

Как мы это победили? (Оптимизация архитектуры)

1. Двухпроходная схема с Lean RS-Pre-pass (gatherNeededRsIds)

Зачем хранить в памяти миллионы нейтральных SNP, если нас интересуют только варианты из наших 24 медицинских панелей и базы PGS Catalog?

Перед чтением гигантского VCF мы делаем быстрый проход и собираем Set только нужных нам RS-идентификаторов:

// core/computePRS.ts — Сбор требуемых RS-ID перед загрузкой VCF
export async function gatherNeededRsIds(
  pgsCatalogPath: string, 
  panels: Panel[]
): Promise<Set<string>> {
  const neededRs = new Set<string>();

  // 1. Собираем RS-ID из всех 24 фенотипических панелей
  for (const panel of panels) {
    panel.directSnps?.forEach(snp => neededRs.add(snp.rsId.toLowerCase()));
  }

  // 2. Предзагружаем веса из PGS Catalog и добавляем их RS-ID
  const catalog: CatalogEntry[] = JSON.parse(fs.readFileSync(pgsCatalogPath, 'utf8'));
  for (const entry of catalog) {
    const weights = await getOrDownloadPgsWeights(entry.pgsId);
    weights.rows.forEach(r => neededRs.add(r.rsId));
  }

  console.log(`[Pre-pass] Собран целевой Set из ${neededRs.size.toLocaleString()} RS-ID.`);
  return neededRs;
}

2. Потоковое чтение

Забудьте про fs.readFileSync или fs.promises.readFile. Только readline и поток fs.createReadStream.

Мы читаем 42.9 ГБ файл по одной строке, мгновенно фильтруем по neededRs и фильтрам качества R², парсим только нужные генотипы и сразу отдаем их сборщику мусора V8:

// core/loadVariants.ts — Потоковый парсинг VCF файла с выводом прогресса
export function loadVcfCombined(
  vcfPath: string, 
  neededRs: Set<string>
): Promise<{ myMap: Map<string, MyVariant>; fullIdx: Map<string, RsRecord> }> {
  return new Promise((resolve, reject) => {
    const myMap   = new Map<string, MyVariant>();
    const fullIdx = new Map<string, RsRecord>();
    let linesProcessed = 0;

    const rl = readline.createInterface({
      input:     fs.createReadStream(vcfPath, { encoding: 'utf8' }),
      crlfDelay: Infinity,
    });

    rl.on('line', (line: string) => {
      linesProcessed++;
      if (linesProcessed % 10000000 === 0) {
        process.stdout.write(`r      ... обработано ${Math.round(linesProcessed / 1000000)}M строк`);
      }

      if (!line || line.startsWith('#')) return;

      const cols = line.split('t');
      const [chromRaw, pos, rsIdRaw, ref, altField, , , infoStr] = cols;
      const chrom = normalizeChrom(chromRaw);
      const rsId  = rsIdRaw !== '.' ? rsIdRaw.toLowerCase() : '';

      // Фильтр по предзагруженному Set: отбрасываем 99.9% лишних строк за O(1)
      if (rsId && !neededRs.has(rsId)) return;

      // Мгновенный парсинг нужной строки и сохранение в карту...
    });

    rl.on('close', () => resolve({ myMap, fullIdx }));
  });
}

3. Выделение RAM

Запускаем Node.js с флагом увеличенного куска памяти:

node --max-old-space-size=16384 -r ts-node/register analyze.ts

Итог: Чтение 42.9 ГБ VCF теперь проходит за пару минут, а потребление оперативной памяти не превышает 2.5 ГБ, что особенно актуально для моего ноутбука времен развала Римской империи.

Шаг 5. Движок сопоставления: ClinVar, Direct SNPs и PRS

Наш анализатор запускает 3 параллельных трека обработки данных для каждого полиморфизма:

                  ┌────────────────────────────────────────┐
                  │         Генетический Вариант           │
                  └───────────────────┬────────────────────┘
                                      │
        ┌─────────────────────────────┼─────────────────────────────┐
        ▼                             ▼                             ▼
┌───────────────┐             ┌───────────────┐             ┌───────────────┐
│   Track A     │             │    Track B    │             │    Track C    │
│ ClinVar Scan  │             │   Direct SNPs │             │   PGS Catalog │
├───────────────┤             ├───────────────┤             ├───────────────┤
│ Сравнение с   │             │ Поиск GWAS-   │             │ Расчет скора  │
│ базами NCBI   │             │ эффектов в    │             │   N           │
│ по CHROM:POS  │             │ JSON-панелях  │             │  ∑ βi × Gi    │
│ + REF/ALT     │             │ по rsID       │             │  i=1          │
└───────────────┘             └───────────────┘             └───────────────┘

1. Track A: ClinVar & Защита Сборки + Инделы (не инцелы)

ClinVar содержит клинику: Pathogenic, Likely pathogenic, Benign.

Важнейший момент — проверка сборки на лету (Assembly Guard) и точное сопоставление по ключу Chrom:Pos:REF:ALT, чтобы вставки и делеции (Indels) сопоставлялись идеально:

// core/scanClinVar.ts — Однопроходный парсер ClinVar с проверкой сборки
const fileStream = fs.createReadStream(clinvarPath);
const gunzip     = zlib.createGunzip(); // Декомпрессия потока на лету

const rl = readline.createInterface({
  input: fileStream.pipe(gunzip),
  crlfDelay: Infinity,
});

rl.on('line', (line: string) => {
  // 1. Assembly Guard: защищаемся от сдвига координат (hg19 vs hg38)
  if (line.startsWith('##') && !assemblyChecked) {
    if (line.toLowerCase().includes('assembly') && line.includes('GRCh37')) {
      throw new Error('КРИТИЧЕСКАЯ ОШИБКА: ClinVar файл должен быть в сборке GRCh38!');
    }
  }

  // 2. Матчинг по точному ключу геномной позиции и аллелей (подходит для Indels!)
  const key   = `${chrom}:${pos}:${ref}:${alt}`;
  const myVar = myMap.get(key);

  if (myVar) {
    // Совпадение найдено! Проверяем клинику в ClinVar (CLNSIG / CLNDN)
  }
});

2. Track B: Direct SNPs (GWAS панелей)

Возвращаемся к моим кастомным панелям.

Код поиска считывает генотип пользователя и определяет дозировку аллеля риска:

// core/scanDirectSnps.ts — Оценка прямого SNP из панели
const record = fullIdx.get(snp.rsId.toLowerCase());
if (record) {
  // Дозировка аллеля риска (0, 1 или 2 копии)
  const dosage = calculateDosage(record, snp.riskAllele);
  if (dosage > 0) {
    hits.push({
      gene: snp.gene,
      rsId: snp.rsId,
      dosage,
      description: snp.description
    });
  }
}

3. Track C: Polygenic Risk Scores (PRS)

Многие сложные состояния (ожирение, аутоиммунка, тревожные расстройства) не определяются одним геном. Это результат работы сотен мелких мутаций.

Для расчета PRS мы берем взвешенную сумму аллелей риска из PGS Catalog и нормализуем через EAF (Effect Allele Frequency):

PRS = ∑ (weight × dosage)

// core/computePRS.ts — Расчет скора полигенного риска по базам PGS Catalog
export function computePrsScore(
  weights: ParsedPgsRow[], 
  fullIdx: Map<string, RsRecord>
): PrsResult {
  let rawScore = 0;
  let snpsMatched = 0;

  for (const row of weights) {
    const record = fullIdx.get(row.rsId);
    if (!record) continue;

    // Вычисляем количество копий аллеля эффекта (dosage = 0, 1 или 2)
    const dosage = calculateDosage(record, row.effectAllele);
    rawScore += row.weight * dosage;
    snpsMatched++;
  }

  // Переводим сырой скор в популяционный z-score и процентиль
  const zScore     = (rawScore - popMean) / popSd;
  const percentile = normalCdf(zScore) * 100;

  return { rawScore, zScore, percentile, snpsMatched };
}

Шаг 6. Прозрение

И вот, код отработал. report.html сгенерирован (более 19 000 находок, сгруппированных по категориям). С трепетом в руках я открыла результат в браузере.

Чтобы репорт выглядел не просто сухим списком JSON, генератор отчетов на TypeScript автоматически добавляет плашки предупреждения для редких вариантов и форматирует клинические категории:

// reporter/htmlReport.ts — Генерация предупреждающих баннеров в HTML-отчете
if (hit.isImputed && hit.maf && hit.maf < QC.RARE_MAF) {
  hitNotice = `
    <div class="alert alert-warning">
      <strong>⚠️ ВНИМАНИЕ:</strong> Вариант получен статистической импутацией (MAF = ${(hit.maf * 100).toFixed(2)}%). 
      Перед принятием медицинских решений обязательно перепроверьте его в лаборатории (ПЦР / Сэнгер)!
    </div>`;
}

Ниже приведены реальные обезличенные выдержки из полученного отчета.

1. Жизнеугрожающие заболевания (ACMG SF / mandatory.panel.json)

Самый волнующий раздел: гены рака груди/яичников (BRCA1/2), синдром Линча, гипертрофическая кардиомиопатия (TTN, RYR2).

- Результат: 1212 хитов, и ВСЕ 1212 — Benign (доброкачественные полиморфизмы). Ни одной патогенной мутации! Можно выдохнуть с облегчением.

2. Болезнь Бехтерева (Ankylosing Spondylitis)

Всю жизнь меня пугали неясными болями и риском аутоиммунки.

- **HLA-B27:** ОТРИЦАТЕЛЬНЫЙ (классический анкилозирующий спондилоартрит исключен!).

- Но есть нюанс (как в анекдоте): Панель выявила высокое полигенное накопление HLA-B27-негативного риска: 5 прямых GWAS-гомозигот (ERAP2, RUNX3, ANTXR2, STAT3, IL6). То есть классической болезни нет, но иммунная ось IL-6/STAT3 любит иногда воспаляться. Об этом я уже написала отдельный огромный пост.

3. Личность, Нейромедиаторы и Психика (psych & personality)

Вот тут вайбкодинг оправдал себя на все 100%!

- FKBP5 (rs1360780): Гомозиготный рисковый гаплотип реакции на стресс (5× Likely risk allele). Это генетическая склонность к гипереактивности гипоталамо-гипофизарно-надпочечниковой оси. Перевод: на сильный стресс мой организм реагирует как боевая тревога на атомной станции.

- GABRA2 (rs279871): 3× гомозиготный маркер изменения чувствительности ГАМК-рецепторов. Объясняет, почему алкоголь расслабляет меня сильнее, чем других (и почему с ним надо быть осторожной). Люди, склонные к аддикциям — у вас такое тоже наверняка есть!

- OXTR (rs53576): Носительница аллеля G — нормальная окситоциновая рецепция, высокая эмпатия и эмоциональная восприимчивость.

- Кофеин и Дофамин: Плотная комбинация полиморфизмов в CDH13, NTRK2 и TH (тирозингидроксилаза) даёт медленный метаболизм дофамина в префронтальной коре.

4. Другие панели

- Ожирение и Аппетит: Высокая полигенная нагрузка по FTO (rs9939609) и рецептору лептина LEPR (rs1137101), но при этом плотный защитный гаплотип термогенеза в генах UCP1-3 (бурый жир активно сжигает калории в тепло).

- Нейродегенерация: Профиль отличный. Никаких аллелей APOE e4 (главный фактор риска Альцгеймера), огромные защитные/нейтральные гаплоблоки в NOTCH3 и PSEN2.

Не обиделась, а сделала выводы

1. Импутация — это магия. Бесплатный сервер TOPMed позволяет превратить дешевенький коммерческий тест от Генотека или 23andMe в исследовательскую дату стоимостью в тысячи долларов.

2. Фильтры — ваше спасение. Без жесткой проверки R² ≥ 0.3 и контроля MAF вы рискуете найти у себя мутации марсианина и потратить кучу нервов.

3. Познание себя через код — это безумно увлекательно. Когда ты видишь строчку кода на TypeScript, которая коррелирует с тем, как ты реагируешь на стресс, кофе или физические нагрузки — это дает невероятное чувство связи технологий и собственной биологии. Комплекс Бога, ты тут???

Если у вас пылится VCF-файл от Генотека, 23andMe или Atlas — не оставляйте его без дела :)

Хиханьки, хаханьки, инженерные авантюры и больше биоинформатики (включая большие статьи про болезнь Бехтерева и мой генетический кабинет редкостей) у меня в тг-канале [1]!

Автор: ishtna

Источник [2]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/genom/456549

Ссылки в тексте:

[1] тг-канале: https://t.me/gcodegspot

[2] Источник: https://habr.com/ru/articles/1069884/?utm_campaign=1069884&utm_source=habrahabr&utm_medium=rss