afquery 0.1.4__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- afquery-0.2.0/.dockerignore +11 -0
- {afquery-0.1.4 → afquery-0.2.0}/.github/workflows/release.yml +55 -1
- {afquery-0.1.4 → afquery-0.2.0}/.gitignore +1 -0
- afquery-0.2.0/Dockerfile +6 -0
- afquery-0.2.0/PKG-INFO +142 -0
- afquery-0.2.0/README.md +115 -0
- {afquery-0.1.4 → afquery-0.2.0}/docs/advanced/benchmarking.md +66 -4
- afquery-0.2.0/docs/advanced/debugging-results.md +138 -0
- afquery-0.2.0/docs/advanced/filter-pass-tracking.md +100 -0
- afquery-0.2.0/docs/advanced/multi-cohort-strategies.md +160 -0
- {afquery-0.1.4 → afquery-0.2.0}/docs/advanced/performance.md +49 -7
- afquery-0.2.0/docs/advanced/pipeline-integration.md +184 -0
- afquery-0.2.0/docs/advanced/ploidy-and-sex-chroms.md +149 -0
- afquery-0.2.0/docs/faq.md +233 -0
- afquery-0.2.0/docs/getting-started/concepts.md +215 -0
- {afquery-0.1.4 → afquery-0.2.0}/docs/getting-started/installation.md +22 -16
- afquery-0.2.0/docs/getting-started/motivation.md +140 -0
- afquery-0.2.0/docs/getting-started/preprocessing.md +35 -0
- afquery-0.2.0/docs/getting-started/quickstart.md +121 -0
- afquery-0.2.0/docs/getting-started/understanding-output.md +147 -0
- {afquery-0.1.4 → afquery-0.2.0}/docs/guides/annotate-vcf.md +9 -29
- {afquery-0.1.4 → afquery-0.2.0}/docs/guides/create-database.md +7 -11
- afquery-0.2.0/docs/guides/dump-export.md +133 -0
- {afquery-0.1.4 → afquery-0.2.0}/docs/guides/manifest-format.md +28 -31
- afquery-0.2.0/docs/guides/query.md +196 -0
- {afquery-0.1.4 → afquery-0.2.0}/docs/guides/sample-filtering.md +46 -8
- {afquery-0.1.4 → afquery-0.2.0}/docs/guides/update-database.md +74 -11
- afquery-0.2.0/docs/index.md +75 -0
- {afquery-0.1.4 → afquery-0.2.0}/docs/reference/cli.md +19 -12
- {afquery-0.1.4 → afquery-0.2.0}/docs/reference/data-model.md +8 -19
- afquery-0.2.0/docs/reference/glossary.md +65 -0
- {afquery-0.1.4 → afquery-0.2.0}/docs/reference/python-api.md +116 -4
- {afquery-0.1.4 → afquery-0.2.0}/docs/troubleshooting.md +22 -2
- afquery-0.2.0/docs/use-cases/acmg-use-cases.md +226 -0
- afquery-0.2.0/docs/use-cases/clinical-prioritization.md +119 -0
- afquery-0.2.0/docs/use-cases/cohort-stratification.md +113 -0
- afquery-0.2.0/docs/use-cases/population-specific-af.md +79 -0
- afquery-0.2.0/docs/use-cases/pseudo-controls.md +131 -0
- afquery-0.2.0/docs/use-cases/sex-specific-af.md +111 -0
- afquery-0.2.0/docs/use-cases/technology-integration.md +166 -0
- afquery-0.2.0/mkdocs.yml +99 -0
- afquery-0.2.0/plans/issue-01-remove-publication-section.md +27 -0
- afquery-0.2.0/plans/issue-02-drop-schema-v1.md +52 -0
- afquery-0.2.0/plans/issue-03-query-syntax-chrom-pos.md +83 -0
- afquery-0.2.0/plans/issue-04-wes-bed-validation.md +62 -0
- afquery-0.2.0/plans/issue-05-update-sample-metadata.md +79 -0
- afquery-0.2.0/plans/issue-06-multi-chromosome-query.md +55 -0
- afquery-0.2.0/plans/issue-07-doc-formatting-fixes.md +71 -0
- afquery-0.2.0/plans/issue-08-index-feature-list.md +49 -0
- afquery-0.2.0/plans/issue-09-doc-content-improvements.md +97 -0
- afquery-0.2.0/src/afquery/__init__.py +5 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/_version.py +2 -2
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/annotate.py +7 -1
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/cli.py +154 -41
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/constants.py +1 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/database.py +73 -1
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/dump.py +15 -21
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/models.py +4 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/preprocess/update.py +210 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/query.py +236 -58
- afquery-0.2.0/tests/test_batch.py +435 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_benchmark.py +2 -2
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_cli.py +21 -21
- afquery-0.2.0/tests/test_cli_docs_consistency.py +87 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_dump.py +71 -1
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_sample_filter.py +6 -6
- afquery-0.2.0/tests/test_update_metadata.py +436 -0
- afquery-0.2.0/tests/test_warnings.py +100 -0
- afquery-0.1.4/PKG-INFO +0 -371
- afquery-0.1.4/README.md +0 -344
- afquery-0.1.4/docs/advanced/filter-pass-tracking.md +0 -111
- afquery-0.1.4/docs/advanced/ploidy-and-sex-chroms.md +0 -100
- afquery-0.1.4/docs/faq.md +0 -129
- afquery-0.1.4/docs/getting-started/concepts.md +0 -110
- afquery-0.1.4/docs/getting-started/quickstart.md +0 -145
- afquery-0.1.4/docs/guides/dump-export.md +0 -152
- afquery-0.1.4/docs/guides/query.md +0 -161
- afquery-0.1.4/docs/index.md +0 -94
- afquery-0.1.4/docs/webhook_schema.md +0 -106
- afquery-0.1.4/mkdocs.yml +0 -74
- afquery-0.1.4/src/afquery/__init__.py +0 -5
- afquery-0.1.4/tests/test_batch.py +0 -196
- {afquery-0.1.4 → afquery-0.2.0}/.github/workflows/ci.yml +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/.github/workflows/docs.yml +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/LICENSE +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/pyproject.toml +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/recipes/afquery/meta.yaml +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/resources/normalize_vcf.sh +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/benchmark.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/bitmaps.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/capture.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/cli.pyr +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/ploidy.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/preprocess/__init__.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/preprocess/build.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/preprocess/compact.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/preprocess/ingest.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/preprocess/manifest.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/preprocess/regions.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/src/afquery/preprocess/synth.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/conftest.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/annotate_input.vcf +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/annotate_multi_bucket.vcf +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/annotate_multi_chrom.vcf +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/beds/wes_kit_a.bed +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/beds/wes_kit_b.bed +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/expected_results.json +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/manifest.tsv +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/vcfs/S00.vcf +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/vcfs/S01.vcf +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/vcfs/S02.vcf +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/vcfs/S03.vcf +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/vcfs/S04.vcf +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/vcfs/S05.vcf +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/vcfs/S06.vcf +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/vcfs/S07.vcf +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/vcfs/S08.vcf +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/data/vcfs/S09.vcf +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_annotate.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_bitmaps.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_capture.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_compact.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_constants.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_haploid_stats.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_info.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_pass_filter.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_ploidy.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_preprocess.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_query.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_synth.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_synthetic_stats.py +0 -0
- {afquery-0.1.4 → afquery-0.2.0}/tests/test_update.py +0 -0
|
@@ -73,7 +73,61 @@ jobs:
|
|
|
73
73
|
files: dist/*
|
|
74
74
|
generate_release_notes: true
|
|
75
75
|
|
|
76
|
-
# ── 5.
|
|
76
|
+
# ── 5. Docker image → ghcr.io (final releases only) ─────────────────
|
|
77
|
+
docker:
|
|
78
|
+
needs: publish-pypi
|
|
79
|
+
runs-on: ubuntu-latest
|
|
80
|
+
if: ${{ !contains(github.ref_name, 'rc') }} # skip RC tags
|
|
81
|
+
permissions:
|
|
82
|
+
packages: write
|
|
83
|
+
steps:
|
|
84
|
+
- uses: actions/checkout@v4
|
|
85
|
+
|
|
86
|
+
- name: Extract version
|
|
87
|
+
id: version
|
|
88
|
+
run: echo "version=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT
|
|
89
|
+
|
|
90
|
+
- name: Set up QEMU
|
|
91
|
+
uses: docker/setup-qemu-action@v3
|
|
92
|
+
|
|
93
|
+
- name: Set up Docker Buildx
|
|
94
|
+
uses: docker/setup-buildx-action@v3
|
|
95
|
+
|
|
96
|
+
- name: Login to GHCR
|
|
97
|
+
uses: docker/login-action@v3
|
|
98
|
+
with:
|
|
99
|
+
registry: ghcr.io
|
|
100
|
+
username: ${{ github.actor }}
|
|
101
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
102
|
+
|
|
103
|
+
- name: Wait for PyPI propagation
|
|
104
|
+
run: |
|
|
105
|
+
VERSION=${{ steps.version.outputs.version }}
|
|
106
|
+
echo "Waiting for afquery==${VERSION} on PyPI..."
|
|
107
|
+
for i in $(seq 1 12); do
|
|
108
|
+
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
|
|
109
|
+
"https://pypi.org/pypi/afquery/${VERSION}/json")
|
|
110
|
+
if [ "$STATUS" = "200" ]; then
|
|
111
|
+
echo "Package available."
|
|
112
|
+
exit 0
|
|
113
|
+
fi
|
|
114
|
+
echo "Attempt ${i}/12: HTTP ${STATUS} — retrying in 30 s..."
|
|
115
|
+
sleep 30
|
|
116
|
+
done
|
|
117
|
+
echo "Not available after 6 min, proceeding anyway."
|
|
118
|
+
|
|
119
|
+
- name: Build and push
|
|
120
|
+
uses: docker/build-push-action@v6
|
|
121
|
+
with:
|
|
122
|
+
context: .
|
|
123
|
+
push: true
|
|
124
|
+
platforms: linux/amd64,linux/arm64
|
|
125
|
+
build-args: VERSION=${{ steps.version.outputs.version }}
|
|
126
|
+
tags: |
|
|
127
|
+
ghcr.io/${{ github.repository }}:${{ steps.version.outputs.version }}
|
|
128
|
+
ghcr.io/${{ github.repository }}:latest
|
|
129
|
+
|
|
130
|
+
# ── 6. Bioconda PR (final releases only — no "rc" in tag) ────────────
|
|
77
131
|
bioconda-pr:
|
|
78
132
|
needs: publish-pypi
|
|
79
133
|
runs-on: ubuntu-latest
|
afquery-0.2.0/Dockerfile
ADDED
afquery-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: afquery
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Genomic allele frequency query engine with bitmap-encoded genotypes
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Requires-Dist: click>=8.1
|
|
13
|
+
Requires-Dist: cyvcf2>=0.30
|
|
14
|
+
Requires-Dist: duckdb>=0.10
|
|
15
|
+
Requires-Dist: pyarrow>=14.0
|
|
16
|
+
Requires-Dist: pyranges<0.2,>=0.1.2
|
|
17
|
+
Requires-Dist: pyroaring>=0.4.8
|
|
18
|
+
Requires-Dist: tqdm>=4.60
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
22
|
+
Provides-Extra: docs
|
|
23
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
24
|
+
Requires-Dist: mkdocstrings[python]>=0.25; extra == 'docs'
|
|
25
|
+
Requires-Dist: pymdown-extensions>=10.7; extra == 'docs'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# AFQuery
|
|
29
|
+
|
|
30
|
+
[](https://github.com/dlopez-bioinfo/afquery/actions/workflows/ci.yml)
|
|
31
|
+
[](https://codecov.io/gh/dlopez-bioinfo/afquery)
|
|
32
|
+
[](https://dlopez-bioinfo.github.io/afquery/)
|
|
33
|
+
<br>
|
|
34
|
+
[](https://pypi.org/project/afquery/)
|
|
35
|
+
[](https://anaconda.org/bioconda/afquery)
|
|
36
|
+
[](https://github.com/dlopez-bioinfo/afquery/pkgs/container/afquery)
|
|
37
|
+
[](https://pypi.org/project/afquery/)
|
|
38
|
+
[](https://github.com/dlopez-bioinfo/afquery/blob/master/LICENSE)
|
|
39
|
+
|
|
40
|
+
AFQuery enables fast allele frequency queries on user-defined subsets of local genomic cohorts, without rescanning VCFs.
|
|
41
|
+
|
|
42
|
+
AFQuery is a bitmap-indexed engine that efficiently recomputes AC/AN/AF for dynamically defined subcohorts (e.g., by phenotype, sex, or sequencing technology), a common requirement in ACMG/AMP variant classification. It stores per-variant genotype data as Roaring Bitmaps in Parquet files and resolves sample filters into bitmaps that can be intersected in microseconds, enabling sub-100 ms queries on large cohorts. The system accounts for ploidy in sex chromosomes, adjusts AN based on sequencing technology, supports incremental updates, and runs locally using a file-based setup (Parquet + SQLite) without requiring server or cloud infrastructure.
|
|
43
|
+
|
|
44
|
+
[Full Documentation→](https://dlopez-bioinfo.github.io/afquery/)
|
|
45
|
+
|
|
46
|
+
## When to use AFQuery
|
|
47
|
+
|
|
48
|
+
- You need allele frequencies for phenotype or user-defined subcohorts
|
|
49
|
+
- You work with mixed sequencing technologies or capture kits versions (WGS, WES, targeted panels)
|
|
50
|
+
- You require fast, repeated queries without rescanning VCFs
|
|
51
|
+
- You want a local, reproducible workflow without cloud or cluster dependencies
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
- **Dynamic subcohort queries (<100 ms)** — bitmap intersections at query time; no VCF re-scan required
|
|
56
|
+
- **Technology-aware** — avoids bias when mixing WGS, WES, and panels using different BED capture indexes
|
|
57
|
+
- **Ploidy-aware** — correct handling of sex chromosomes (PAR/non-PAR, chrX, chrY)
|
|
58
|
+
- **ACMG-compatible allele counting** — AC/AN/AF computed per standard definitions
|
|
59
|
+
- **Flexible metadata filtering** — arbitrary labels (ICD-10, HPO, custom fields) with inclusion/exclusion rules
|
|
60
|
+
- **Incremental updates** — add or remove samples and update metadata without rebuilding the database
|
|
61
|
+
- **VCF annotation** — annotate variants using subcohort-specific frequencies
|
|
62
|
+
- **FILTER/call quality tracking** — failed calls (FILTER!=PASS) tracked per variant and reported as N_FAIL
|
|
63
|
+
- **Batch and region queries** — query a single locus, a genomic region, or a list of variants from a file
|
|
64
|
+
- **Bulk CSV export** — export all variant frequencies with optional disaggregation by sex, technology, or phenotype
|
|
65
|
+
- **Audit changelog** — all database operations logged with timestamps and operator notes
|
|
66
|
+
- **Database validation** — integrity checks with scripted exit codes
|
|
67
|
+
- **Portable and serverless** — file-based system, no infrastructure required
|
|
68
|
+
|
|
69
|
+
## Performance
|
|
70
|
+
|
|
71
|
+
- Query latency: <100 ms (tested up to 50,000 samples)
|
|
72
|
+
- Storage: ~2 bytes/sample/variant
|
|
73
|
+
- Scales to millions of variants per chromosome
|
|
74
|
+
|
|
75
|
+
## Comparison with Alternative Tools
|
|
76
|
+
|
|
77
|
+
| | AFQuery | bcftools | GATK GenomicsDB | Hail |
|
|
78
|
+
|---|---|---|---|---|
|
|
79
|
+
| Technology-aware AN | **Yes** | No | No | No |
|
|
80
|
+
| Metadata filtering | **Arbitrary labels** | No | No | Custom code |
|
|
81
|
+
| Ploidy-aware sex chromosomes | **Yes** | Manual | No | Manual |
|
|
82
|
+
| Dynamic subcohort queries | **Yes** | No | Limited | Requires code |
|
|
83
|
+
| FILTER/call quality tracking | **Per variant** | Manual | No | Manual |
|
|
84
|
+
| Incremental updates | **Yes** | No | **Yes** | No |
|
|
85
|
+
| Infrastructure required | **None** | **None** | Java/server | Spark cluster |
|
|
86
|
+
| Query latency (50K samples) | **<100 ms** | ~5 min | <1 min | 1–2 min |
|
|
87
|
+
|
|
88
|
+
## Algorithm Overview
|
|
89
|
+
|
|
90
|
+
AFQuery pre-indexes per-variant genotype data as [Roaring Bitmaps](https://roaringbitmap.org/) stored in Parquet files. Each variant row holds three bitmaps: heterozygous carriers, homozygous alt carriers, and samples with FILTER!=PASS. Sample metadata (sex, phenotype, technology) is pre-serialized as bitmaps in SQLite.
|
|
91
|
+
|
|
92
|
+
At query time, the requested sample filter is resolved to a single candidate bitmap via bitmap intersections and differences — taking microseconds regardless of cohort size. For each variant, the candidate bitmap is intersected with the genotype bitmaps to compute AC/AN/AF. AN accounts for WES capture regions (via BED-indexed interval trees) and for ploidy on sex chromosomes (males are haploid on non-PAR chrX and chrY).
|
|
93
|
+
|
|
94
|
+
## Input Requirements
|
|
95
|
+
|
|
96
|
+
- VCF files: normalized and consistent with the selected genome build (GRCh37 or GRCh38)
|
|
97
|
+
- Sample metadata: must include sex, sequencing technology, and any fields used for filtering (e.g., phenotype)
|
|
98
|
+
- BED files (optional): define capture regions for each sequencing technology
|
|
99
|
+
|
|
100
|
+
## Quick Start
|
|
101
|
+
|
|
102
|
+
Example workflow from raw VCFs to query, export, and annotation:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pip install afquery
|
|
106
|
+
# Docker: see Installation docs for docker pull / run usage
|
|
107
|
+
|
|
108
|
+
# Build the database
|
|
109
|
+
afquery create-db --manifest samples.tsv --output-dir ./db/ --genome-build GRCh38
|
|
110
|
+
|
|
111
|
+
# Inspect the database
|
|
112
|
+
afquery info --db ./db/
|
|
113
|
+
|
|
114
|
+
# Query a single position, filtered to a phenotype
|
|
115
|
+
afquery query --db ./db/ --locus chr1:925952 --phenotype E11.9 --sex female
|
|
116
|
+
|
|
117
|
+
# Query a genomic region
|
|
118
|
+
afquery query --db ./db/ --region chr1:900000-1000000
|
|
119
|
+
|
|
120
|
+
# Export BRCA1 variant frequencies to CSV
|
|
121
|
+
afquery dump --db ./db/ --output all_variants.csv --chrom chr17 --start 43044292 --end 43170327
|
|
122
|
+
|
|
123
|
+
# Annotate a VCF with cohort frequencies
|
|
124
|
+
afquery annotate --db ./db/ --input patient.vcf --output annotated.vcf --threads 12
|
|
125
|
+
|
|
126
|
+
# Add new samples to an existing database
|
|
127
|
+
afquery update-db --db ./db/ --add-samples new_samples.tsv
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Documentation
|
|
131
|
+
|
|
132
|
+
- [Full Documentation](https://dlopez-bioinfo.github.io/afquery/)
|
|
133
|
+
- [Getting Started](https://dlopez-bioinfo.github.io/afquery/getting-started/)
|
|
134
|
+
- [Why local allele frequencies?](https://dlopez-bioinfo.github.io/afquery/getting-started/motivation/)
|
|
135
|
+
- [CLI Reference](https://dlopez-bioinfo.github.io/afquery/reference/cli/)
|
|
136
|
+
|
|
137
|
+
## Citation
|
|
138
|
+
|
|
139
|
+
If you use AFQuery, please cite:
|
|
140
|
+
|
|
141
|
+
> AFQuery: fast, metadata-aware allele frequency queries on local genomic cohorts.
|
|
142
|
+
> *(manuscript in preparation)*
|
afquery-0.2.0/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# AFQuery
|
|
2
|
+
|
|
3
|
+
[](https://github.com/dlopez-bioinfo/afquery/actions/workflows/ci.yml)
|
|
4
|
+
[](https://codecov.io/gh/dlopez-bioinfo/afquery)
|
|
5
|
+
[](https://dlopez-bioinfo.github.io/afquery/)
|
|
6
|
+
<br>
|
|
7
|
+
[](https://pypi.org/project/afquery/)
|
|
8
|
+
[](https://anaconda.org/bioconda/afquery)
|
|
9
|
+
[](https://github.com/dlopez-bioinfo/afquery/pkgs/container/afquery)
|
|
10
|
+
[](https://pypi.org/project/afquery/)
|
|
11
|
+
[](https://github.com/dlopez-bioinfo/afquery/blob/master/LICENSE)
|
|
12
|
+
|
|
13
|
+
AFQuery enables fast allele frequency queries on user-defined subsets of local genomic cohorts, without rescanning VCFs.
|
|
14
|
+
|
|
15
|
+
AFQuery is a bitmap-indexed engine that efficiently recomputes AC/AN/AF for dynamically defined subcohorts (e.g., by phenotype, sex, or sequencing technology), a common requirement in ACMG/AMP variant classification. It stores per-variant genotype data as Roaring Bitmaps in Parquet files and resolves sample filters into bitmaps that can be intersected in microseconds, enabling sub-100 ms queries on large cohorts. The system accounts for ploidy in sex chromosomes, adjusts AN based on sequencing technology, supports incremental updates, and runs locally using a file-based setup (Parquet + SQLite) without requiring server or cloud infrastructure.
|
|
16
|
+
|
|
17
|
+
[Full Documentation→](https://dlopez-bioinfo.github.io/afquery/)
|
|
18
|
+
|
|
19
|
+
## When to use AFQuery
|
|
20
|
+
|
|
21
|
+
- You need allele frequencies for phenotype or user-defined subcohorts
|
|
22
|
+
- You work with mixed sequencing technologies or capture kits versions (WGS, WES, targeted panels)
|
|
23
|
+
- You require fast, repeated queries without rescanning VCFs
|
|
24
|
+
- You want a local, reproducible workflow without cloud or cluster dependencies
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- **Dynamic subcohort queries (<100 ms)** — bitmap intersections at query time; no VCF re-scan required
|
|
29
|
+
- **Technology-aware** — avoids bias when mixing WGS, WES, and panels using different BED capture indexes
|
|
30
|
+
- **Ploidy-aware** — correct handling of sex chromosomes (PAR/non-PAR, chrX, chrY)
|
|
31
|
+
- **ACMG-compatible allele counting** — AC/AN/AF computed per standard definitions
|
|
32
|
+
- **Flexible metadata filtering** — arbitrary labels (ICD-10, HPO, custom fields) with inclusion/exclusion rules
|
|
33
|
+
- **Incremental updates** — add or remove samples and update metadata without rebuilding the database
|
|
34
|
+
- **VCF annotation** — annotate variants using subcohort-specific frequencies
|
|
35
|
+
- **FILTER/call quality tracking** — failed calls (FILTER!=PASS) tracked per variant and reported as N_FAIL
|
|
36
|
+
- **Batch and region queries** — query a single locus, a genomic region, or a list of variants from a file
|
|
37
|
+
- **Bulk CSV export** — export all variant frequencies with optional disaggregation by sex, technology, or phenotype
|
|
38
|
+
- **Audit changelog** — all database operations logged with timestamps and operator notes
|
|
39
|
+
- **Database validation** — integrity checks with scripted exit codes
|
|
40
|
+
- **Portable and serverless** — file-based system, no infrastructure required
|
|
41
|
+
|
|
42
|
+
## Performance
|
|
43
|
+
|
|
44
|
+
- Query latency: <100 ms (tested up to 50,000 samples)
|
|
45
|
+
- Storage: ~2 bytes/sample/variant
|
|
46
|
+
- Scales to millions of variants per chromosome
|
|
47
|
+
|
|
48
|
+
## Comparison with Alternative Tools
|
|
49
|
+
|
|
50
|
+
| | AFQuery | bcftools | GATK GenomicsDB | Hail |
|
|
51
|
+
|---|---|---|---|---|
|
|
52
|
+
| Technology-aware AN | **Yes** | No | No | No |
|
|
53
|
+
| Metadata filtering | **Arbitrary labels** | No | No | Custom code |
|
|
54
|
+
| Ploidy-aware sex chromosomes | **Yes** | Manual | No | Manual |
|
|
55
|
+
| Dynamic subcohort queries | **Yes** | No | Limited | Requires code |
|
|
56
|
+
| FILTER/call quality tracking | **Per variant** | Manual | No | Manual |
|
|
57
|
+
| Incremental updates | **Yes** | No | **Yes** | No |
|
|
58
|
+
| Infrastructure required | **None** | **None** | Java/server | Spark cluster |
|
|
59
|
+
| Query latency (50K samples) | **<100 ms** | ~5 min | <1 min | 1–2 min |
|
|
60
|
+
|
|
61
|
+
## Algorithm Overview
|
|
62
|
+
|
|
63
|
+
AFQuery pre-indexes per-variant genotype data as [Roaring Bitmaps](https://roaringbitmap.org/) stored in Parquet files. Each variant row holds three bitmaps: heterozygous carriers, homozygous alt carriers, and samples with FILTER!=PASS. Sample metadata (sex, phenotype, technology) is pre-serialized as bitmaps in SQLite.
|
|
64
|
+
|
|
65
|
+
At query time, the requested sample filter is resolved to a single candidate bitmap via bitmap intersections and differences — taking microseconds regardless of cohort size. For each variant, the candidate bitmap is intersected with the genotype bitmaps to compute AC/AN/AF. AN accounts for WES capture regions (via BED-indexed interval trees) and for ploidy on sex chromosomes (males are haploid on non-PAR chrX and chrY).
|
|
66
|
+
|
|
67
|
+
## Input Requirements
|
|
68
|
+
|
|
69
|
+
- VCF files: normalized and consistent with the selected genome build (GRCh37 or GRCh38)
|
|
70
|
+
- Sample metadata: must include sex, sequencing technology, and any fields used for filtering (e.g., phenotype)
|
|
71
|
+
- BED files (optional): define capture regions for each sequencing technology
|
|
72
|
+
|
|
73
|
+
## Quick Start
|
|
74
|
+
|
|
75
|
+
Example workflow from raw VCFs to query, export, and annotation:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pip install afquery
|
|
79
|
+
# Docker: see Installation docs for docker pull / run usage
|
|
80
|
+
|
|
81
|
+
# Build the database
|
|
82
|
+
afquery create-db --manifest samples.tsv --output-dir ./db/ --genome-build GRCh38
|
|
83
|
+
|
|
84
|
+
# Inspect the database
|
|
85
|
+
afquery info --db ./db/
|
|
86
|
+
|
|
87
|
+
# Query a single position, filtered to a phenotype
|
|
88
|
+
afquery query --db ./db/ --locus chr1:925952 --phenotype E11.9 --sex female
|
|
89
|
+
|
|
90
|
+
# Query a genomic region
|
|
91
|
+
afquery query --db ./db/ --region chr1:900000-1000000
|
|
92
|
+
|
|
93
|
+
# Export BRCA1 variant frequencies to CSV
|
|
94
|
+
afquery dump --db ./db/ --output all_variants.csv --chrom chr17 --start 43044292 --end 43170327
|
|
95
|
+
|
|
96
|
+
# Annotate a VCF with cohort frequencies
|
|
97
|
+
afquery annotate --db ./db/ --input patient.vcf --output annotated.vcf --threads 12
|
|
98
|
+
|
|
99
|
+
# Add new samples to an existing database
|
|
100
|
+
afquery update-db --db ./db/ --add-samples new_samples.tsv
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Documentation
|
|
104
|
+
|
|
105
|
+
- [Full Documentation](https://dlopez-bioinfo.github.io/afquery/)
|
|
106
|
+
- [Getting Started](https://dlopez-bioinfo.github.io/afquery/getting-started/)
|
|
107
|
+
- [Why local allele frequencies?](https://dlopez-bioinfo.github.io/afquery/getting-started/motivation/)
|
|
108
|
+
- [CLI Reference](https://dlopez-bioinfo.github.io/afquery/reference/cli/)
|
|
109
|
+
|
|
110
|
+
## Citation
|
|
111
|
+
|
|
112
|
+
If you use AFQuery, please cite:
|
|
113
|
+
|
|
114
|
+
> AFQuery: fast, metadata-aware allele frequency queries on local genomic cohorts.
|
|
115
|
+
> *(manuscript in preparation)*
|
|
@@ -23,14 +23,14 @@ This generates 1,000 synthetic samples and 10,000 variants per chromosome, build
|
|
|
23
23
|
| `--n-samples` | `1000` | Number of synthetic samples |
|
|
24
24
|
| `--n-variants` | `10000` | Number of variants per chromosome |
|
|
25
25
|
| `--output` | `benchmark_report.json` | Output path for JSON report |
|
|
26
|
-
| `--db
|
|
26
|
+
| `--db` | None | Run against an existing database instead of synthetic data |
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
30
30
|
## Benchmark Against a Real Database
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
afquery benchmark --db
|
|
33
|
+
afquery benchmark --db ./my_db/ --output my_db_benchmark.json
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
This uses your actual Parquet files and sample metadata, giving realistic performance numbers for your specific cohort size and variant density.
|
|
@@ -90,11 +90,11 @@ Run the benchmark after major updates to detect regressions:
|
|
|
90
90
|
|
|
91
91
|
```bash
|
|
92
92
|
# Before update
|
|
93
|
-
afquery benchmark --db
|
|
93
|
+
afquery benchmark --db ./db/ --output before.json
|
|
94
94
|
|
|
95
95
|
# After adding 500 samples
|
|
96
96
|
afquery update-db --db ./db/ --add-samples new_batch.tsv
|
|
97
|
-
afquery benchmark --db
|
|
97
|
+
afquery benchmark --db ./db/ --output after.json
|
|
98
98
|
|
|
99
99
|
# Compare
|
|
100
100
|
python3 -c "
|
|
@@ -106,3 +106,65 @@ for k in before:
|
|
|
106
106
|
print(f'{k}: {before[k]:.1f} → {after[k]:.1f} ms ({delta:+.1f})')
|
|
107
107
|
"
|
|
108
108
|
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Comparison with Alternative Approaches
|
|
113
|
+
|
|
114
|
+
### Feature Comparison
|
|
115
|
+
|
|
116
|
+
| Feature | AFQuery | bcftools stats | VCFtools --freq | GATK GenomicsDB | Hail |
|
|
117
|
+
|---------|---------|---------------|----------------|----------------|------|
|
|
118
|
+
| **Query latency** | <100 ms | Minutes (VCF scan) | Minutes (VCF scan) | Seconds | Seconds–minutes |
|
|
119
|
+
| **Dynamic subcohort queries** | Yes | No | No | Partial | Yes (programmatic) |
|
|
120
|
+
| **Metadata filtering** | Arbitrary labels | No | No | No | User-defined |
|
|
121
|
+
| **Sex-stratified AF** | Yes (auto ploidy) | Manual | Manual | No | Manual |
|
|
122
|
+
| **Technology-aware AN** | Yes (BED capture) | No | No | No | No |
|
|
123
|
+
| **Incremental updates** | Yes (no rebuild) | N/A | N/A | Yes (import) | Rebuild |
|
|
124
|
+
| **Infrastructure required** | None (file-based) | None | None | Java/server | Spark cluster |
|
|
125
|
+
| **Input format** | Single-sample VCFs | Any VCF | VCF | gVCF | VCF/BGEN |
|
|
126
|
+
| **Output format** | JSON/TSV/VCF annotation | Stats text | Freq file | Merged gVCF | Table/VCF |
|
|
127
|
+
| **Max cohort size (tested)** | 50,000 | 100,000+ | 100,000+ | 100,000+ | 1,000,000+ |
|
|
128
|
+
| **Bitmap compression** | Yes (Roaring) | No | No | Yes (GenomicsDB) | No |
|
|
129
|
+
| **VCF annotation** | Yes | No | No | No | Yes |
|
|
130
|
+
| **Python API** | Yes | No | No | No | Yes |
|
|
131
|
+
|
|
132
|
+
### Query Latency Summary
|
|
133
|
+
|
|
134
|
+
Brief summary of query latency comparison:
|
|
135
|
+
|
|
136
|
+
| Tool | 50K samples, chr1, point query |
|
|
137
|
+
|------|-------------------------------|
|
|
138
|
+
| AFQuery (cold) | <100 ms |
|
|
139
|
+
| AFQuery (warm) | ~10 ms |
|
|
140
|
+
| bcftools stats (VCF scan) | ~5 minutes |
|
|
141
|
+
| VCFtools --freq (VCF scan) | ~5 minutes |
|
|
142
|
+
|
|
143
|
+
AFQuery achieves this speed advantage because queries access only the bitmaps for a single 1-Mbp bucket (one Parquet file) rather than scanning the entire dataset.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Regression Testing
|
|
148
|
+
|
|
149
|
+
Run the benchmark after each major update to detect performance regressions:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# Save baseline before changes
|
|
153
|
+
afquery benchmark --db ./db/ --output baseline.json
|
|
154
|
+
|
|
155
|
+
# After update
|
|
156
|
+
afquery benchmark --db ./db/ --output after_update.json
|
|
157
|
+
|
|
158
|
+
# Compare
|
|
159
|
+
python3 - <<'EOF'
|
|
160
|
+
import json
|
|
161
|
+
b = json.load(open('baseline.json'))['queries']
|
|
162
|
+
a = json.load(open('after_update.json'))['queries']
|
|
163
|
+
for k in b:
|
|
164
|
+
pct = (a[k] - b[k]) / b[k] * 100
|
|
165
|
+
flag = " ⚠" if pct > 20 else ""
|
|
166
|
+
print(f"{k}: {b[k]:.1f} → {a[k]:.1f} ms ({pct:+.1f}%){flag}")
|
|
167
|
+
EOF
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
A regression of >20% on `point_query_cold_ms` warrants investigation.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Debugging Results
|
|
2
|
+
|
|
3
|
+
When AFQuery returns unexpected results — AN=0, surprising AF values, or missing variants — use this diagnostic checklist to identify the root cause.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Diagnostic Checklist
|
|
8
|
+
|
|
9
|
+
### 1. Unexpected AN=0
|
|
10
|
+
|
|
11
|
+
AN=0 means no eligible samples at the queried position. Work through these checks in order:
|
|
12
|
+
|
|
13
|
+
| Check | Command | What to look for |
|
|
14
|
+
|-------|---------|-----------------|
|
|
15
|
+
| Chromosome normalization | `afquery query --db ./db/ --chrom 1 ...` vs `--chrom chr1` | Database may use `chr1` while you're querying `1` (or vice versa). Check `manifest.json` for `genome_build`. |
|
|
16
|
+
| Position exists in database | `afquery query --db ./db/ --locus chr1:12345678` | If no result at all, the variant was not observed in any sample during ingestion. |
|
|
17
|
+
| BED coverage (WES) | `afquery info --db ./db/` | If all eligible samples are WES and the position is outside capture regions, AN=0 is correct. |
|
|
18
|
+
| Sample filter too restrictive | Remove `--phenotype` and `--sex` filters | Query with no filters first. If AN>0 without filters, the filter is excluding all samples. |
|
|
19
|
+
| Technology filter | Remove `--tech` filter | Check if any samples match the requested technology. |
|
|
20
|
+
|
|
21
|
+
### 2. Unexpected AF Value
|
|
22
|
+
|
|
23
|
+
| Symptom | Likely Cause | Resolution |
|
|
24
|
+
|---------|-------------|------------|
|
|
25
|
+
| AF higher than expected | Cohort enriched for disease with this variant | Compare with `--phenotype ^disease_code` to get control AF |
|
|
26
|
+
| AF lower than expected | Many WES samples without coverage at this position → diluted AN | Filter by `--tech wgs` to check WGS-only AF |
|
|
27
|
+
| AF=1.0 | All eligible samples carry the variant | Check if AN is very small (e.g., AN=2 means just 1 sample) |
|
|
28
|
+
| AF differs from gnomAD | Expected — local cohort AF ≠ global population AF | This is by design; see [Why Cohort-Specific AF Matters](../getting-started/concepts.md#why-cohort-specific-af-matters) |
|
|
29
|
+
|
|
30
|
+
### 3. Missing Variants
|
|
31
|
+
|
|
32
|
+
A variant you expect to find is not in the database:
|
|
33
|
+
|
|
34
|
+
| Check | Details |
|
|
35
|
+
|-------|---------|
|
|
36
|
+
| Was it in the source VCFs? | AFQuery only stores variants present in ingested VCFs |
|
|
37
|
+
| Was it FILTER=PASS? | Default ingestion skips non-PASS variants. Check with `afquery info` for `pass_only_filter` |
|
|
38
|
+
| Multiallelic sites | AFQuery stores each ALT separately. Query the specific ALT allele, not just position |
|
|
39
|
+
| Chromosome naming | Ensure consistent `chr` prefix usage |
|
|
40
|
+
|
|
41
|
+
### 4. Unexpected N_FAIL > 0
|
|
42
|
+
|
|
43
|
+
`N_FAIL > 0` means some eligible samples had the alt allele called but with `FILTER≠PASS`. These samples are excluded from AC/AN. This is usually benign (1–2 samples), but a high N_FAIL warrants investigation:
|
|
44
|
+
|
|
45
|
+
| N_FAIL relative to n_eligible | Likely cause |
|
|
46
|
+
|---|---|
|
|
47
|
+
| 1–2 samples | Isolated low-quality calls — not concerning |
|
|
48
|
+
| > 5% of n_eligible | Systematic sequencing artifact at this site |
|
|
49
|
+
| All eligible samples | Site-wide QC failure — AF=0 but variant is present in source VCFs |
|
|
50
|
+
|
|
51
|
+
To inspect a site with high N_FAIL, query with `--format json` to see all fields:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
afquery query --db ./db/ --locus chr1:12345678 --format json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
If N_FAIL is consistently high across many sites, check the variant calling pipeline and FILTER field settings in your VCFs.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Diagnostic Commands
|
|
62
|
+
|
|
63
|
+
### Database Info
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
afquery info --db ./db/
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Shows: sample count, technology list, schema version, genome build, PASS-only status.
|
|
70
|
+
|
|
71
|
+
### Check Database Integrity
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
afquery check --db ./db/
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Validates: manifest consistency, Parquet file integrity, capture index presence.
|
|
78
|
+
|
|
79
|
+
### Query with Full Output
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Point query with all fields visible
|
|
83
|
+
afquery query --db ./db/ --locus chr1:12345678 --format json
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
JSON format shows all fields including N_HET, N_HOM_ALT, N_HOM_REF, and N_FAIL — useful for understanding the composition of the result.
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
### Direct Metadata Inspection
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
import sqlite3
|
|
93
|
+
|
|
94
|
+
conn = sqlite3.connect("./db/metadata.sqlite")
|
|
95
|
+
|
|
96
|
+
# List all phenotype codes and sample counts
|
|
97
|
+
cursor = conn.execute("""
|
|
98
|
+
SELECT code, COUNT(*) as n_samples
|
|
99
|
+
FROM sample_phenotypes
|
|
100
|
+
GROUP BY code
|
|
101
|
+
ORDER BY n_samples DESC
|
|
102
|
+
""")
|
|
103
|
+
for row in cursor:
|
|
104
|
+
print(f"{row[0]:30s} {row[1]} samples")
|
|
105
|
+
|
|
106
|
+
# List technologies
|
|
107
|
+
cursor = conn.execute("""
|
|
108
|
+
SELECT technology, COUNT(*) as n_samples
|
|
109
|
+
FROM samples
|
|
110
|
+
GROUP BY technology
|
|
111
|
+
""")
|
|
112
|
+
for row in cursor:
|
|
113
|
+
print(f"{row[0]:20s} {row[1]} samples")
|
|
114
|
+
|
|
115
|
+
conn.close()
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Common Root Causes
|
|
121
|
+
|
|
122
|
+
| Symptom | Root Cause | Fix |
|
|
123
|
+
|---------|-----------|-----|
|
|
124
|
+
| AN=0 for all queries | Wrong `--db` path or empty database | Verify path; run `afquery info` |
|
|
125
|
+
| AN=0 for specific region | WES-only cohort, position outside capture | Check BED file coverage |
|
|
126
|
+
| AN much lower than sample count | Mixed WGS/WES, position outside WES capture | Filter by `--tech wgs` to isolate |
|
|
127
|
+
| AF=None in output | AN=0 (division by zero) | See AN=0 diagnosis above |
|
|
128
|
+
| Different AF between `query` and `annotate` | Different default filters or phenotype context | Ensure same `--phenotype`, `--sex`, `--tech` flags |
|
|
129
|
+
| N_FAIL high at a site | Systematic QC failure in source VCFs at this position | Inspect site with `--format json`; check VCF FILTER annotations |
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Related Pages
|
|
134
|
+
|
|
135
|
+
- [Understanding Output](../getting-started/understanding-output.md) — what each field means
|
|
136
|
+
- [FAQ](../faq.md) — common questions and answers
|
|
137
|
+
- [Troubleshooting](../troubleshooting.md) — error messages and solutions
|
|
138
|
+
- [FILTER=PASS Tracking](filter-pass-tracking.md) — how PASS filtering works
|