Skip to content

Commit 5c040f2

Browse files
committed
Process sample variants in parallel
Also renames the REGION column to SAMPLE, keeping the intended use of REGION in a VCF, and GFF_FEATURE to GB_FEATURE, keeping the intended use of GFF_FEATURE as an output of iVar
1 parent 52e95ea commit 5c040f2

9 files changed

Lines changed: 98 additions & 84 deletions

File tree

workflow/rules/vaf.smk

Lines changed: 34 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ rule snps_to_ancestor:
4848
-m {params.ivar_depth} \
4949
-g {input.gff} \
5050
-r renamed_reference.fasta
51-
52-
sed 's/'$ref'/'{wildcards.sample}'/g' {wildcards.sample}.tsv | cat > {output.tsv}
5351
"""
5452

5553

@@ -69,39 +67,18 @@ rule annotation:
6967
"../scripts/report/get_annotation.py"
7068

7169

72-
rule format_tsv:
73-
threads:1
74-
shadow: "shallow"
75-
input:
76-
expand(OUTDIR/"{sample}.tsv", sample = iter_samples())
77-
output:
78-
tsv = OUTDIR/f"{OUTPUT_NAME}.tsv"
79-
log:
80-
LOGDIR / "format_tsv" / "log.txt"
81-
shell:
82-
"""
83-
path=`echo {input} | awk '{{print $1}}'`
84-
grep "^REGION" "$path" > header
85-
for tsv in {input}; do
86-
tail -n +2 "$tsv" >> body
87-
done
88-
cat header body > "{output.tsv}"
89-
rm header
90-
"""
91-
92-
9370
rule mask_tsv:
9471
threads: 1
9572
conda: "../envs/biopython.yaml"
9673
params:
9774
mask_class = ["mask"]
9875
input:
99-
tsv = OUTDIR/f"{OUTPUT_NAME}.tsv",
76+
tsv = OUTDIR/"{sample}.tsv",
10077
vcf = lambda wildcards: select_problematic_vcf()
10178
output:
102-
masked_tsv = temp(OUTDIR/f"{OUTPUT_NAME}.masked.tsv")
79+
masked_tsv = temp(OUTDIR/"{sample}.masked.tsv")
10380
log:
104-
LOGDIR / "mask_tsv" / "log.txt"
81+
LOGDIR / "mask_tsv" / "{sample}.log.txt"
10582
script:
10683
"../scripts/mask_tsv.py"
10784

@@ -114,12 +91,12 @@ rule filter_tsv:
11491
min_alt_rv = 2,
11592
min_alt_dp = 2,
11693
input:
117-
tsv = OUTDIR/f"{OUTPUT_NAME}.masked.tsv",
94+
tsv = OUTDIR/"{sample}.masked.tsv",
11895
annotation = OUTDIR/"annotation.csv"
11996
output:
120-
filtered_tsv = temp(OUTDIR/f"{OUTPUT_NAME}.masked.prefiltered.tsv")
97+
filtered_tsv = temp(OUTDIR/"{sample}.masked.prefiltered.tsv")
12198
log:
122-
LOGDIR / "filter_tsv" / "log.txt"
99+
LOGDIR / "filter_tsv" / "{sample}.log.txt"
123100
script:
124101
"../scripts/filter_tsv.R"
125102

@@ -130,11 +107,11 @@ rule tsv_to_vcf:
130107
params:
131108
ref_name = config["ALIGNMENT_REFERENCE"],
132109
input:
133-
tsv = OUTDIR/f"{OUTPUT_NAME}.masked.prefiltered.tsv",
110+
tsv = OUTDIR/"{sample}.masked.prefiltered.tsv",
134111
output:
135-
vcf = temp(OUTDIR/f"{OUTPUT_NAME}.vcf")
112+
vcf = temp(OUTDIR/"{sample}.vcf")
136113
log:
137-
LOGDIR / "tsv_to_vcf" / "log.txt"
114+
LOGDIR / "tsv_to_vcf" / "{sample}.log.txt"
138115
script:
139116
"../scripts/tsv_to_vcf.py"
140117

@@ -147,11 +124,11 @@ rule variants_effect:
147124
ref_name = config["ALIGNMENT_REFERENCE"],
148125
snpeff_data_dir = (BASE_PATH / "config" / "snpeff").resolve()
149126
input:
150-
vcf = OUTDIR/f"{OUTPUT_NAME}.vcf"
127+
vcf = OUTDIR/"{sample}.vcf"
151128
output:
152-
ann_vcf = temp(OUTDIR/f"{OUTPUT_NAME}.annotated.vcf")
129+
ann_vcf = OUTDIR/"{sample}.annotated.vcf"
153130
log:
154-
LOGDIR / "variants_effect" / "log.txt"
131+
LOGDIR / "variants_effect" / "{sample}.log.txt"
155132
retries: 2
156133
shell:
157134
"""
@@ -165,7 +142,7 @@ rule variants_effect:
165142
echo "Local database not found at '{params.snpeff_data_dir}', downloading from repository"
166143
fi
167144
168-
snpEff eff -dataDir {params.snpeff_data_dir} -noStats {params.ref_name} {input.vcf} > {output.ann_vcf}
145+
snpEff eff -dataDir {params.snpeff_data_dir} -noStats {params.ref_name} {input.vcf} >{output.ann_vcf}
169146
"""
170147

171148

@@ -180,11 +157,11 @@ rule extract_vcf_fields:
180157
],
181158
sep = ","
182159
input:
183-
vcf = OUTDIR/f"{OUTPUT_NAME}.annotated.vcf"
160+
vcf = OUTDIR/"{sample}.annotated.vcf"
184161
output:
185-
tsv = OUTDIR/f"{OUTPUT_NAME}.vcf_fields.tsv"
162+
tsv = OUTDIR/"{sample}.vcf_fields.tsv"
186163
log:
187-
LOGDIR / "tsv_to_vcf" / "log.txt"
164+
LOGDIR / "tsv_to_vcf" / "{sample}.log.txt"
188165
shell:
189166
'SnpSift extractFields -s {params.sep:q} {input.vcf:q} {params.extract_columns} >{output.tsv:q} 2>{log:q}'
190167

@@ -194,11 +171,11 @@ rule format_vcf_fields_longer:
194171
params:
195172
sep = ","
196173
input:
197-
tsv = OUTDIR/f"{OUTPUT_NAME}.vcf_fields.tsv"
174+
tsv = OUTDIR/"{sample}.vcf_fields.tsv"
198175
output:
199-
tsv = OUTDIR/f"{OUTPUT_NAME}.vcf_fields.longer.tsv"
176+
tsv = OUTDIR/"{sample}.vcf_fields.longer.tsv"
200177
log:
201-
LOGDIR / "format_vcf_fields_longer" / "log.txt"
178+
LOGDIR / "format_vcf_fields_longer" / "{sample}.log.txt"
202179
script:
203180
"../scripts/format_vcf_fields_longer.R"
204181

@@ -207,11 +184,22 @@ rule vcf_to_tsv:
207184
threads: 1
208185
conda: "../envs/renv.yaml"
209186
input:
210-
ann_vcf = OUTDIR/f"{OUTPUT_NAME}.annotated.vcf",
211-
pre_tsv = OUTDIR/f"{OUTPUT_NAME}.masked.prefiltered.tsv"
187+
ann_vcf = OUTDIR/"{sample}.annotated.vcf",
188+
pre_tsv = OUTDIR/"{sample}.masked.prefiltered.tsv"
212189
output:
213-
tsv = OUTDIR/f"{OUTPUT_NAME}.masked.filtered.tsv"
190+
tsv = OUTDIR/"{sample}.masked.filtered.tsv"
214191
log:
215-
LOGDIR / "vcf_to_tsv" / "log.txt"
192+
LOGDIR / "vcf_to_tsv" / "{sample}.log.txt"
216193
script:
217194
"../scripts/vcf_to_tsv.R"
195+
196+
197+
rule compile_variants:
198+
threads: 1
199+
input: expand(OUTDIR/"{sample}.masked.filtered.tsv", sample=iter_samples())
200+
output:
201+
tsv = OUTDIR/f"{OUTPUT_NAME}.masked.filtered.tsv"
202+
log:
203+
LOGDIR / "compile_variants" / "log.txt"
204+
script:
205+
"../scripts/compile_variants.R"
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env Rscript
2+
3+
# Write stdout and stderr to log file
4+
log <- file(snakemake@log[[1]], open = "wt")
5+
sink(log, type = "message")
6+
sink(log, type = "output")
7+
8+
library(tidyverse)
9+
library(logger)
10+
log_threshold(INFO)
11+
12+
log_info("Reading input partial variant tables and adding sample column")
13+
tables <- lapply(
14+
snakemake@input,
15+
function(path) {
16+
read_tsv(path) %>%
17+
mutate(SAMPLE = sub('\\.tsv$', '', path))
18+
}
19+
)
20+
21+
log_info("Binding and writing table")
22+
bind_rows(tables) %>%
23+
write_tsv(snakemake@output$tsv)

workflow/scripts/filter_tsv.R

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,8 @@ data <- distinct(data, pick(!GFF_FEATURE), .keep_all = TRUE)
4747
features <- read_csv(snakemake@input[["annotation"]])
4848

4949
data <- data %>%
50-
select(!GFF_FEATURE) %>%
5150
left_join(features) %>%
52-
rename(GFF_FEATURE = GEN)
51+
rename(GB_FEATURE = GEN)
5352

5453
log_info("Saving results")
5554
write_tsv(

workflow/scripts/report/NV_description.R

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ date_order <- metadata %>%
3838
unique()
3939

4040
empty_vcf <- tibble(
41-
REGION = date_order,
41+
SAMPLE = date_order,
42+
REGION = as.character(NA),
4243
variant = as.character(NA),
4344
ALT_FREQ = as.numeric(NA),
44-
GFF_FEATURE = as.character(NA),
45+
GB_FEATURE = as.character(NA),
4546
synonimous = as.character(NA),
4647
POS = as.numeric(NA),
4748
ALT = as.character(NA),
@@ -52,10 +53,11 @@ empty_vcf <- tibble(
5253
# Create SNP variable and select useful variables
5354
vcf <- vcf %>%
5455
dplyr::select(
55-
variant,
56+
SAMPLE,
5657
REGION,
58+
variant,
5759
ALT_FREQ,
58-
GFF_FEATURE,
60+
GB_FEATURE,
5961
synonimous,
6062
POS,
6163
ALT
@@ -91,7 +93,7 @@ vcf <- vcf %>%
9193
TRUE ~ "SNP"
9294
),
9395
Class = case_when(
94-
GFF_FEATURE == "Intergenic" ~ "Intergenic",
96+
GB_FEATURE == "Intergenic" ~ "Intergenic",
9597
TRUE ~ synonimous
9698
),
9799
POS = as.numeric(POS)
@@ -102,7 +104,7 @@ vcf <- vcf %>%
102104
NV_class == "INDEL" ~ str_length(ALT) - 1
103105
),
104106
indel_class = case_when(
105-
GFF_FEATURE == "Intergenic" ~ "Intergenic",
107+
GB_FEATURE == "Intergenic" ~ "Intergenic",
106108
NV_class == "INDEL" &
107109
indel_len %% 3 == 0 ~
108110
"In frame",
@@ -114,7 +116,7 @@ vcf <- vcf %>%
114116
ungroup() %>%
115117
mutate(
116118
group = case_when(
117-
GFF_FEATURE == "Intergenic" ~ "Intergenic",
119+
GB_FEATURE == "Intergenic" ~ "Intergenic",
118120
NV_class == "SNP" ~ Class,
119121
NV_class == "INDEL" ~ indel_class
120122
)
@@ -156,7 +158,7 @@ variants <- vcf %>%
156158
ggplot() +
157159
aes(
158160
x = POS,
159-
y = factor(REGION, date_order),
161+
y = factor(SAMPLE, date_order),
160162
shape = factor(NV_class, c("SNP", "INDEL")),
161163
color = group,
162164
alpha = ALT_FREQ
@@ -312,7 +314,7 @@ variants_spike <- vcf_spike %>%
312314
ggplot() +
313315
aes(
314316
x = POS,
315-
y = factor(REGION, date_order),
317+
y = factor(SAMPLE, date_order),
316318
shape = factor(NV_class, c("SNP", "INDEL")),
317319
color = group,
318320
alpha = ALT_FREQ
@@ -363,9 +365,9 @@ figur_SNP_table <- vcf_snp %>%
363365
filter(ALT_FREQ <= snakemake@params$max_alt_freq) %>%
364366
left_join(
365367
metadata,
366-
by = c("REGION" = "ID")
368+
by = c("SAMPLE" = "ID")
367369
) %>%
368-
group_by(REGION) %>%
370+
group_by(SAMPLE) %>%
369371
summarise(
370372
CollectionDate = min(as.Date(CollectionDate)),
371373
n = n_distinct(POS)
@@ -410,15 +412,15 @@ log_info("Saving plot tables")
410412
# Variants summary table
411413
vcf %>%
412414
select(
413-
REGION,
415+
SAMPLE,
414416
POS,
415417
variant,
416418
ALT_FREQ,
417419
NV_class,
418420
group
419421
) %>%
420422
rename(
421-
sample = REGION,
423+
sample = SAMPLE,
422424
Variant = variant,
423425
Class = group
424426
) %>%
@@ -444,18 +446,18 @@ window %>%
444446
# Heterozygous sites per sample table
445447
vcf_snp %>%
446448
filter(ALT_FREQ <= snakemake@params$max_alt_freq) %>%
447-
select(!GFF_FEATURE) %>%
449+
select(!GB_FEATURE) %>%
448450
left_join(
449451
metadata,
450-
by = c("REGION" = "ID")
452+
by = c("SAMPLE" = "ID")
451453
) %>%
452-
group_by(REGION) %>%
454+
group_by(SAMPLE) %>%
453455
summarise(
454456
CollectionDate = min(as.Date(CollectionDate)),
455457
n_PolymorphicSites = n_distinct(POS)
456458
) %>%
457459
ungroup() %>%
458-
rename(sample = REGION) %>%
460+
rename(sample = SAMPLE) %>%
459461
unique() %>%
460462
write.csv(snakemake@output[["table_3"]], row.names = FALSE)
461463

workflow/scripts/report/evo_plots.R

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ N_S_position <- read_delim(snakemake@input[["N_S"]])
2121
# Create SNP variable and select useful variables
2222
vcf <- vcf %>%
2323
dplyr::select(
24+
SAMPLE,
2425
variant,
2526
REGION,
2627
ALT_FREQ,
27-
GFF_FEATURE,
28+
GB_FEATURE,
2829
synonimous,
2930
POS
3031
)
@@ -37,14 +38,14 @@ metadata <- metadata %>%
3738
)
3839
) %>%
3940
select(ID, interval) %>%
40-
rename(REGION = ID)
41+
rename(SAMPLE = ID)
4142

4243
vcf <- left_join(vcf, metadata)
4344

4445
# PLOT
4546
log_info("Ploting dN and dS over time")
4647
plot_df <- vcf %>%
47-
group_by(REGION, synonimous) %>%
48+
group_by(SAMPLE, synonimous) %>%
4849
summarise(
4950
Freq = sum(ALT_FREQ, na.rm = TRUE)
5051
) %>%
@@ -67,7 +68,7 @@ plot_df <- vcf %>%
6768
values_to = "value",
6869
names_to = "d"
6970
) %>%
70-
left_join(unique(select(vcf, REGION, interval)))
71+
left_join(unique(select(vcf, SAMPLE, interval)))
7172

7273
plot <- plot_df %>%
7374
filter(d != "w") %>%
@@ -134,7 +135,7 @@ ggsave(
134135
# PLOT TABLES
135136
log_info("Saving plot table")
136137
vcf %>%
137-
group_by(REGION, synonimous) %>%
138+
group_by(SAMPLE, synonimous) %>%
138139
summarise(
139140
Freq = sum(ALT_FREQ, na.rm = TRUE)
140141
) %>%
@@ -148,9 +149,9 @@ vcf %>%
148149
ds = Yes / sum(N_S_position$S)
149150
) %>%
150151
ungroup() %>%
151-
left_join(unique(select(vcf, REGION, interval))) %>%
152+
left_join(unique(select(vcf, SAMPLE, interval))) %>%
152153
transmute(
153-
sample = REGION,
154+
sample = SAMPLE,
154155
DaysSinceFirst = interval,
155156
dN = dn,
156157
dS = ds,

0 commit comments

Comments
 (0)