|
| 1 | +rule bcftools_mpileup_all_sites: |
| 2 | + threads: 1 |
| 3 | + conda: "../envs/var_calling.yaml" |
| 4 | + params: |
| 5 | + min_mq = 0, |
| 6 | + min_bq = config["VC"]["MIN_QUALITY"], |
| 7 | + mpileup_extra = "--no-BAQ" |
| 8 | + input: |
| 9 | + bam = get_input_bam, |
| 10 | + reference = OUTDIR/"vaf"/"{sample}.reference.fasta", |
| 11 | + output: |
| 12 | + mpileup = temp(OUTDIR / "all_sites" / "{sample}.mpileup.vcf"), |
| 13 | + query = temp(OUTDIR / "all_sites" / "{sample}.query.tsv"), |
| 14 | + log: |
| 15 | + mpileup = LOGDIR / "bcftools_mpileup_all_sites" / "{sample}.mpileup.txt", |
| 16 | + query = LOGDIR / "bcftools_mpileup_all_sites" / "{sample}.query.txt", |
| 17 | + shell: |
| 18 | + "bcftools mpileup {params.mpileup_extra} -a AD,ADF,ADR --fasta-ref {input.reference:q} --threads {threads} -Q {params.min_bq} -q {params.min_mq} -Ov -o {output.mpileup:q} {input.bam:q} >{log.mpileup:q} 2>&1 && " |
| 19 | + "echo 'CHROM\tPOS\tREF\tALT\tDP\tAD\tADF\tADR' >{output.query:q} && " |
| 20 | + "bcftools query -f '%CHROM\t%POS\t%REF\t%ALT\t%DP\t[ %AD]\t[ %ADF]\t[ %ADR]\n' {output.mpileup:q} >>{output.query:q} 2>{log.query:q}" |
| 21 | + |
| 22 | + |
| 23 | +rule filter_mpileup_all_sites: |
| 24 | + threads: 1 |
| 25 | + params: |
| 26 | + min_total_AD = config["VC"]["MIN_DEPTH"], |
| 27 | + min_total_ADF = 0, |
| 28 | + min_total_ADR = 0, |
| 29 | + input: |
| 30 | + OUTDIR / "all_sites" / "{sample}.query.tsv", |
| 31 | + output: |
| 32 | + sites_pass = temp(OUTDIR / "all_sites" / "{sample}.filtered_sites.tsv"), |
| 33 | + sites_fail = temp(OUTDIR / "all_sites" / "{sample}.fail_sites.tsv"), |
| 34 | + log: |
| 35 | + LOGDIR / "filter_mpileup_all_sites" / "{sample}.txt" |
| 36 | + run: |
| 37 | + import pandas as pd |
| 38 | + df = pd.read_csv(input[0], sep="\t") |
| 39 | + df["SAMPLE"] = wildcards.sample |
| 40 | + df["REF_AD"] = df.AD.str.split(",").apply(lambda values: int(values[0])) |
| 41 | + df["TOTAL_AD"] = df.AD.str.split(",").apply(lambda values: sum(int(n) for n in values)) |
| 42 | + df["TOTAL_ADF"] = df.ADF.str.split(",").apply(lambda values: sum(int(n) for n in values)) |
| 43 | + df["TOTAL_ADR"] = df.ADR.str.split(",").apply(lambda values: sum(int(n) for n in values)) |
| 44 | + mask = ( |
| 45 | + (df.TOTAL_AD >= params.min_total_AD) & |
| 46 | + (df.TOTAL_ADF >= params.min_total_ADF) & |
| 47 | + (df.TOTAL_ADR >= params.min_total_ADR) |
| 48 | + ) |
| 49 | + df[mask].to_csv(output.sites_pass, sep="\t", index=False) |
| 50 | + df[~mask].to_csv(output.sites_fail, sep="\t", index=False) |
| 51 | + |
| 52 | + |
| 53 | +use rule concat_vcf_fields as merge_filtered_mpileup_all_sites with: |
| 54 | + input: |
| 55 | + expand(OUTDIR / "all_sites" / "{sample}.filtered_sites.tsv", sample=iter_samples()), |
| 56 | + output: |
| 57 | + OUTDIR / f"{OUTPUT_NAME}.filtered_sites.tsv", |
| 58 | + |
| 59 | + |
| 60 | +use rule concat_vcf_fields as merge_fail_mpileup_all_sites with: |
| 61 | + input: |
| 62 | + expand(OUTDIR / "all_sites" / "{sample}.fail_sites.tsv", sample=iter_samples()), |
| 63 | + output: |
| 64 | + OUTDIR / f"{OUTPUT_NAME}.fail_sites.tsv", |
| 65 | + |
| 66 | + |
| 67 | +rule fill_all_sites: |
| 68 | + conda: "../envs/renv.yaml" |
| 69 | + input: |
| 70 | + variants = OUTDIR/f"{OUTPUT_NAME}.variants.tsv", |
| 71 | + sites = OUTDIR / f"{OUTPUT_NAME}.filtered_sites.tsv", |
| 72 | + output: |
| 73 | + variants = OUTDIR/f"{OUTPUT_NAME}.variants.all_sites.tsv", |
| 74 | + log: |
| 75 | + LOGDIR / "fill_all_sites" / "log.txt" |
| 76 | + script: |
| 77 | + "../scripts/fill_all_sites.R" |
| 78 | + |
| 79 | + |
| 80 | +rule compile_fail_sites_vcf: |
| 81 | + params: |
| 82 | + filter_text = "mask", |
| 83 | + sub_text = "NA", |
| 84 | + exc_text = "site_qual", |
| 85 | + input: |
| 86 | + sites = OUTDIR / f"{OUTPUT_NAME}.fail_sites.tsv", |
| 87 | + output: |
| 88 | + sites = temp(OUTDIR / f"{OUTPUT_NAME}.fail_sites.vcf"), |
| 89 | + run: |
| 90 | + import pandas as pd |
| 91 | + HEADER = ["#CHROM", "POS", "ID", "REF", "ALT", "QUAL", "FILTER", "INFO"] |
| 92 | + sites = ( |
| 93 | + pd.read_table(input.sites, sep="\t") |
| 94 | + .drop_duplicates(subset=("CHROM", "POS", "REF")) |
| 95 | + .rename(columns={"CHROM": "#CHROM"}) |
| 96 | + ) |
| 97 | + sites["ID"] = "." |
| 98 | + sites["ALT"] = "." |
| 99 | + sites["QUAL"] = "." |
| 100 | + sites["FILTER"] = params.filter_text |
| 101 | + sites["INFO"] = f"SUB={params.sub_text};EXC={params.exc_text}" |
| 102 | + sites[HEADER].to_csv(output.sites, sep="\t", index=False) |
| 103 | + |
| 104 | + |
| 105 | +rule merge_mask_sites_vcf: |
| 106 | + input: |
| 107 | + lambda wildcards: select_problematic_vcf(), |
| 108 | + OUTDIR / f"{OUTPUT_NAME}.fail_sites.vcf", |
| 109 | + output: |
| 110 | + sites = temp(OUTDIR / "all_mask_sites.vcf"), |
| 111 | + run: |
| 112 | + import pandas as pd |
| 113 | + HEADER = ["#CHROM", "POS", "ID", "REF", "ALT", "QUAL", "FILTER", "INFO"] |
| 114 | + ( |
| 115 | + pd.concat( |
| 116 | + [pd.read_table(path, sep="\t", comment="#", names=HEADER, dtype={"POS": "int64"}) for path in input], |
| 117 | + axis="rows", |
| 118 | + ignore_index=True |
| 119 | + ) |
| 120 | + .drop_duplicates(subset=("#CHROM", "POS", "FILTER"), keep="first") |
| 121 | + .sort_values(by=["#CHROM", "POS"]) |
| 122 | + .to_csv(output.sites, sep="\t", index=False) |
| 123 | + ) |
0 commit comments