A unified, high-performance C++ toolkit for processing GWAS summary statistics.
GWAStoolkit integrates common GWAS summary-statistic processing steps into one flag-driven command-line tool. There are no subcommands: the program decides what to run from the flags you provide.
- Fast rsID annotation from dbSNP text, gzipped text, or PLINK
.bim. - CHR/POS lookup with allele-aware matching: GWAS
A1/A2can match dbSNPREF/ALTin either order. - OR to beta/SE conversion.
- Sample-size filling from fixed case/control, per-row case/control columns, or total-N.
- Optional beta/SE standardization through
--std-effect. - Final format conversion to
gwas,cojo,popcorn, ormrmega. - Two-phase QC with per-reason counts for operation inputs and final output values.
- Optional duplicate SNP removal by smallest P-value.
- Quiet default logging, with
--verbosefor column mappings, scan progress, and debug examples.
Operations are selected only by flags:
--or COLconverts OR to beta/SE.--case --control,--case-col --control-col, or numeric--Nfills sample size.--std-effectstandardizes beta/SE using binary effective N or continuous total N.--dbsnp FILEannotates/fills rsID.--formatwrites the final output in a supported downstream format.
The input is loaded once. When several flags are used together, GWAStoolkit evaluates each flag in a fixed order and writes the result once:
Initial QC -> OR -> sample size/effective N -> standardized effect
-> rsID -> Final QC -> duplicate removal -> final write
Disabled steps are skipped. Initial QC validates only the source fields consumed by enabled operations. Final QC validates operation results and the fields required by the selected output format. For example, CHR and POS are required only when --dbsnp is used, while a direct COJO conversion requires every output SNP value to be non-missing. Beta/SE may be absent if --or generates them before final format conversion.
Dependencies:
- g++ with C++17 support
- zlib
Build:
make clean
makeThis creates:
./GWAStoolkitShow help:
./GWAStoolkit --helpOR to beta/SE, fixed total sample size, rsID annotation, and COJO output in one call:
./GWAStoolkit \
--gwas-summary gwas_or.txt.gz \
--out final.cojo.rsid.txt.gz \
--or OR \
--case 20000 \
--control 30000 \
--dbsnp GRCH37.dbSNP157.txt \
--dbchr CHROM --dbpos POS --dbA1 REF --dbA2 ALT --dbrsid ID \
--chr CHR --pos POS \
--SNP SNP --A1 A1 --A2 A2 \
--freq freq --beta beta --se se --pval P \
--format cojo \
--threads 4The same style works for every workflow: add the flags for the operations you want, and GWAStoolkit will execute them in the correct order.
This section follows the real execution order used internally by GWAStoolkit.
./GWAStoolkit \
--gwas-summary gwas_or.txt.gz \
--out gwas.beta.txt.gz \
--or OR \
--SNP SNP --A1 A1 --A2 A2 \
--freq freq --pval P --N N \
--format gwasIf a valid SE is unavailable, GWAStoolkit can estimate SE from a valid P-value. If neither source is usable, SE is written as NA for gwas output; formats that require SE remove that row during QC.
Fixed case/control counts:
./GWAStoolkit \
--gwas-summary gwas.txt.gz \
--out gwas.N.txt.gz \
--case 20000 \
--control 30000 \
--format gwasPer-row case/control columns:
./GWAStoolkit \
--gwas-summary gwas.txt.gz \
--out gwas.N.txt.gz \
--case-col n_case \
--control-col n_control \
--format gwasBinary trait with fixed case/control counts:
./GWAStoolkit \
--gwas-summary gwas.txt.gz \
--out final.std.cojo.txt.gz \
--case 20000 \
--control 30000 \
--std-effect \
--SNP SNP --A1 A1 --A2 A2 \
--freq freq --beta beta --se se --pval P \
--format cojoContinuous trait with fixed total N:
./GWAStoolkit \
--gwas-summary gwas.txt.gz \
--out final.std.cojo.txt.gz \
--N 50000 \
--std-effect \
--SNP SNP --A1 A1 --A2 A2 \
--freq freq --beta beta --se se --pval P \
--format cojoContinuous trait with an existing total-N column:
./GWAStoolkit \
--gwas-summary gwas.txt.gz \
--out final.std.cojo.txt.gz \
--N N \
--std-effect \
--SNP SNP --A1 A1 --A2 A2 \
--freq freq --beta beta --se se --pval P \
--format cojo./GWAStoolkit \
--gwas-summary gwas.txt.gz \
--out gwas.rsid.txt.gz \
--dbsnp GRCH37.dbSNP157.txt \
--dbchr CHROM --dbpos POS --dbA1 REF --dbA2 ALT --dbrsid ID \
--chr CHR --pos POS \
--A1 A1 --A2 A2 \
--format gwasrsID matching uses:
GWAS CHR/POS == dbSNP CHR/POS
and
{GWAS A1, GWAS A2} == {dbSNP REF, dbSNP ALT}
Allele matching is case-insensitive and order-insensitive. No strand-complement matching is applied.
./GWAStoolkit \
--gwas-summary gwas.txt.gz \
--out gwas.cojo.txt.gz \
--SNP SNP --A1 A1 --A2 A2 \
--freq freq --beta beta --se se --pval P --N N \
--format cojoIf P-value is available and SE is absent, GWAStoolkit estimates SE from the two-sided P-value:
If neither a valid SE nor a valid P-value is available, SE is written as NA for gwas output. A final format that requires SE removes the row and reports it in QC logs.
Without --std-effect, case/control flags write total sample size:
With --std-effect, binary traits use effective sample size:
For per-row case/control columns:
GWAStoolkit standardizes beta/SE using:
For binary traits:
For continuous traits:
GWAStoolkit can read dbSNP as a tab-delimited text file with columns such as:
CHROM POS ID REF ALT
It also supports PLINK .bim / .bim.gz, interpreted as:
CHR RSID CM POS A1 A2
Example for GRCh37 dbSNP 157:
- Split multi-allelic sites into biallelic
wget https://ftp.ncbi.nih.gov/snp/latest_release/VCF/GCF_000001405.25.gz
gunzip -c GCF_000001405.25.gz | bgzip -c > GCF_000001405.25.bgz
tabix -p vcf GCF_000001405.25.bgz
bcftools norm -m -any --thread 10 -Oz -o GRCH37.dbsnp157.vcf.gz GCF_000001405.25.bgz- Rename chromosome names (optional, recommended)
cat > chrmap.txt <<EOF
NC_000001.10 1
NC_000002.11 2
NC_000003.11 3
NC_000004.11 4
NC_000005.9 5
NC_000006.11 6
NC_000007.13 7
NC_000008.10 8
NC_000009.11 9
NC_000010.10 10
NC_000011.9 11
NC_000012.11 12
NC_000013.10 13
NC_000014.8 14
NC_000015.9 15
NC_000016.9 16
NC_000017.10 17
NC_000018.9 18
NC_000019.9 19
NC_000020.10 20
NC_000021.8 21
NC_000022.10 22
NC_000023.10 X
NC_000024.9 Y
EOF
bcftools annotate --rename-chrs chrmap.txt GRCH37.dbsnp157.vcf.gz --thread 10 -Oz -o GRCH37.dbsnp157.chr.vcf.gz
bcftools index GRCH37.dbsnp157.chr.vcf.gz- Extract chromosomes 1 to X, then export the needed columns:
bcftools view -r 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,X \
GRCH37.dbsnp157.vcf.gz \
--thread 10 -Oz \
-o GRCH37.dbsnp157.1toXchr.vcf.gz
(
echo -e "CHROM\tPOS\tID\tREF\tALT"
bcftools query -f '%CHROM\t%POS\t%ID\t%REF\t%ALT\n' GRCH37.dbsnp157.1toXchr.vcf.gz
) > GRCH37.dbSNP157.txtThen use:
--dbsnp GRCH37.dbSNP157.txt \
--dbchr CHROM --dbpos POS --dbA1 REF --dbA2 ALT --dbrsid IDGWAStoolkit canonicalizes common chromosome names internally. Typical accepted forms include:
| Input form | Internal meaning |
|---|---|
1, 01, chr1, CHR1, NC_000001.11 |
chromosome 1 |
X, chrX, 23, NC_000023.11 |
chromosome X |
Y, chrY, 24, NC_000024.11 |
chromosome Y |
M, MT, chrM, NC_012920.1 |
mitochondrial chromosome |
Build consistency is still required: GRCh37 GWAS should be matched against GRCh37 dbSNP, and GRCh38 GWAS against GRCh38 dbSNP.
| Flag | Meaning |
|---|---|
--gwas-summary FILE |
Input GWAS summary statistics. Text and gzipped input are supported. |
--out FILE |
Output file. Text and gzipped output are supported. |
| Flag | Meaning | Default |
|---|---|---|
--SNP COL |
SNP identifier column | SNP |
--chr COL --pos COL |
GWAS chromosome/position columns. Required only with --dbsnp. |
CHR / POS |
--A1 COL --A2 COL |
GWAS allele columns | A1 / A2 |
--freq COL |
Allele frequency column | freq |
--beta COL |
Beta column to read/write | b |
--se COL |
Standard error column to read/write | se |
--pval COL |
P-value column | p |
--N COL_OR_VALUE, --n COL_OR_VALUE |
Sample-size column name or fixed numeric total N | N |
| Flag | Meaning |
|---|---|
--or COL |
Convert odds ratio in COL to beta/SE. |
--case INT --control INT |
Use fixed case/control counts. Without --std-effect, writes total N. With --std-effect, uses binary effective N. |
--case-col COL --control-col COL |
Use per-row case/control columns. |
--std-effect |
Standardize beta/SE using sample size. |
--dbsnp FILE |
Annotate/fill rsID from dbSNP text/gz or PLINK .bim. |
--dbchr COL --dbpos COL |
dbSNP chromosome/position columns. |
--dbA1 COL --dbA2 COL |
dbSNP allele columns, usually REF and ALT. |
--dbrsid COL |
dbSNP rsID column, usually ID. |
--format gwas|cojo|popcorn|mrmega |
Final output format. |
| Flag | Meaning | Default |
|---|---|---|
--maf VALUE |
MAF threshold. Must be between 0 and 0.5. | 0.01 |
--remove-dup-snp |
Keep one row per SNP, using the smallest P-value when available. | off |
--threads N, --thread-num N |
Number of OpenMP threads. | 1 |
--log FILE |
Write log output to file. | none |
--verbose |
Print column mappings, scan progress, and rsID matching details. | off |
--keep-temp |
Accepted for compatibility; the unified workflow creates no intermediate files. | off |
For final non-gwas output, the processed GWAS table must contain the columns required by that format. These columns may already exist in the input or be generated by earlier flags in the same call.
| Format | Required columns |
|---|---|
cojo |
SNP, A1, A2, freq, beta, se, p, N |
popcorn |
SNP, A1, A2, freq, beta, se, N |
mrmega |
SNP, A1, A2, freq, beta, se, p, N |
If required fields are missing, add the relevant operation flags first. For example, use --or to create beta/SE from OR, and use --case --control, --case-col --control-col, or numeric --N to create N.
Example COJO output:
SNP A1 A2 freq b se p N
rs1000 A G 0.37 0.145 0.035 1e-5 50000
rs2000 T C 0.42 -0.080 0.025 2e-3 50000
Use --verbose when debugging column names, dbSNP scans, or low rsID match rates:
./GWAStoolkit ... --verbose --log run.logEvery run reports the exact pipeline order and separate Initial QC and Final QC summaries. Each summary lists the number removed for every triggered reason, such as invalid OR source, failed rsID annotation, missing SNP, invalid beta/SE/P/N, or MAF filtering. Operation logs separately report generated, matched, failed, and unavailable counts. Verbose mode additionally prints column mappings and dbSNP scan/match details.
Initial QC is source-aware: a generated field replaces the corresponding raw input for validation. For example, numeric --N is allowed to replace an invalid input N column, and --or is allowed to replace an invalid input beta. Final QC then validates the generated values and the selected target format. Rows that fail only statistical Final QC are not written to the rsID .unmatch file; that file is reserved for invalid annotation inputs and annotation failures.
- Check genome build consistency: GRCh37 vs GRCh38.
- Confirm
--chr,--pos,--A1, and--A2point to the same columns used in your reference matching script. - Confirm dbSNP columns with
--dbchr,--dbpos,--dbA1,--dbA2, and--dbrsid. - Use
--verboseand inspectrsID allele mismatch examplelines. - Make sure multi-allelic sites were split with
bcftools norm -m -any.
COJO requires an N column. Use one of:
--N N
--N 50000
--case 20000 --control 30000
--case-col n_case --control-col n_controlFor continuous traits, use total N through --N. Do not use case/control unless the trait is binary.
For binary traits, use case/control counts and --std-effect so GWAStoolkit can compute effective N.
