Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 34 additions & 34 deletions modules/local/fitness/templates/fitness_calculation.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ compute_nt_hamming <- function(merged.counts, wt.seq) {
merged.counts <- cbind("nt_ham" = rep(NA, nrow(merged.counts)), merged.counts)
for (i in 1:nrow(merged.counts)){
tmp.wt <- strsplit(as.character(wt.seq), "")[[1]]
tmp.mut <- strsplit(as.character(merged.counts$nt_seq[i]), "")[[1]]
tmp.mut <- strsplit(as.character(merged.counts\$nt_seq[i]), "")[[1]]
if(length(which(tmp.mut != tmp.wt)) == 0){
merged.counts$nt_ham[i] <- 0
merged.counts\$nt_ham[i] <- 0
rm(tmp.mut, tmp.wt)
next
}else{
merged.counts$nt_ham[i] <- length(which(tmp.mut != tmp.wt))
merged.counts\$nt_ham[i] <- length(which(tmp.mut != tmp.wt))
rm(tmp.mut, tmp.wt)
next
}
Expand All @@ -34,7 +34,7 @@ compute_nt_hamming <- function(merged.counts, wt.seq) {

# translate sequences and add aa_seq
add_aa_seq <- function(merged.counts) {
merged.counts <- cbind("aa_seq" = as.character(translate(DNAStringSet(merged.counts$nt_seq))), merged.counts)
merged.counts <- cbind("aa_seq" = as.character(translate(DNAStringSet(merged.counts\$nt_seq))), merged.counts)
merged.counts
}

Expand All @@ -43,13 +43,13 @@ compute_aa_hamming <- function(merged.counts, wt.seq.aa) {
merged.counts <- cbind("aa_ham" = rep(NA, nrow(merged.counts)), merged.counts)
for (i in 1:nrow(merged.counts)){
tmp.wt <- strsplit(as.character(wt.seq.aa), "")[[1]]
tmp.mut <- strsplit(as.character(merged.counts$aa_seq[i]), "")[[1]]
tmp.mut <- strsplit(as.character(merged.counts\$aa_seq[i]), "")[[1]]
if(length(which(tmp.mut != tmp.wt)) == 0){
merged.counts$aa_ham[i] <- 0
merged.counts\$aa_ham[i] <- 0
rm(tmp.mut, tmp.wt)
next
}else{
merged.counts$aa_ham[i] <- length(which(tmp.mut != tmp.wt))
merged.counts\$aa_ham[i] <- length(which(tmp.mut != tmp.wt))
rm(tmp.mut, tmp.wt)
next
}
Expand All @@ -63,14 +63,14 @@ name_mutations <- function(merged.counts, wt.seq.aa) {
"pos" = rep(NA, nrow(merged.counts)),
"mut_aa" = rep(NA, nrow(merged.counts)), merged.counts)
for (i in 1:nrow(merged.counts)){
if(merged.counts$aa_ham[i] == 0){
if(merged.counts\$aa_ham[i] == 0){
next
}else{
tmp.wt <- strsplit(as.character(wt.seq.aa), "")[[1]]
tmp.mut <- strsplit(as.character(merged.counts$aa_seq[i]), "")[[1]]
merged.counts$pos[i] <- which(tmp.mut != tmp.wt)
merged.counts$`wt_aa`[i] <- tmp.wt[merged.counts$pos[i]]
merged.counts$`mut_aa`[i] <- tmp.mut[merged.counts$pos[i]]
tmp.mut <- strsplit(as.character(merged.counts\$aa_seq[i]), "")[[1]]
merged.counts\$pos[i] <- which(tmp.mut != tmp.wt)
merged.counts\$'wt_aa'[i] <- tmp.wt[merged.counts\$pos[i]]
merged.counts\$'mut_aa'[i] <- tmp.mut[merged.counts\$pos[i]]
rm(tmp.mut, tmp.wt)
}
}
Expand All @@ -83,17 +83,17 @@ aggregate_by_aa <- function(merged.counts) {
merged.counts <- cbind(merged.counts,
"wt" = rep(NA, nrow(merged.counts)),
"stop" = rep(NA, nrow(merged.counts)))
merged.counts$wt[which(merged.counts$nt_ham == 0)] <- TRUE
merged.counts$stop[which(merged.counts$`mut_aa` == "*")] <- TRUE
merged.counts\$wt[which(merged.counts\$nt_ham == 0)] <- TRUE
merged.counts\$stop[which(merged.counts\$'mut_aa' == "*")] <- TRUE

## aggregate counts of variants which are identical on the aa (but not nt) level
## exception: wildtype ones
## thereby shrinking the matrix
uniq.aa.vars <- unique(merged.counts$aa_seq)
uniq.aa.vars <- uniq.aa.vars[-which(uniq.aa.vars == merged.counts$aa_seq[which(merged.counts$wt == TRUE)])]
uniq.aa.vars <- unique(merged.counts\$aa_seq)
uniq.aa.vars <- uniq.aa.vars[-which(uniq.aa.vars == merged.counts\$aa_seq[which(merged.counts\$wt == TRUE)])]
for(i in 1:length(uniq.aa.vars)){
tmp.aa_seq <- uniq.aa.vars[i]
hits <- which(as.character(merged.counts$aa_seq) == tmp.aa_seq)
hits <- which(as.character(merged.counts\$aa_seq) == tmp.aa_seq)
if(length(hits) == 1){
rm(tmp.aa_seq, hits)
next
Expand All @@ -115,21 +115,21 @@ density_peaks <- function(x, adjust = 1, ...) {

## obtain density distribution
d <- density(x, adjust = adjust, n = 5000, na.rm = T, ...)
y <- d$y
y <- d\$y
idx <- which(diff(sign(diff(y))) == -2) + 1
if (length(idx) == 0) return(numeric(0))

## order density peaks by height (y value); take top 2
idx <- idx[order(y[idx], decreasing = TRUE)]
peaks_x <- d$x[idx]
peaks_x <- d\$x[idx]
peaks_x[seq_len(min(2, length(peaks_x)))]

}

# 3. Raw fitness calculations ##
calc_raw_fitness <- function(merged.counts, exp.design) {
## how many fitness replicates are there
reps <- length(unique(exp.design$experiment_replicate))
reps <- length(unique(exp.design\$experiment_replicate))
for (i in 1:reps){
merged.counts <- cbind(merged.counts, rep(NA, nrow(merged.counts)))
colnames(merged.counts)[ncol(merged.counts)] <- paste0("raw_fitness_rep", i)
Expand All @@ -146,8 +146,8 @@ calc_raw_fitness <- function(merged.counts, exp.design) {
tmp.output.counts[which(tmp.output.counts == 0 & tmp.input.counts != 0)] <- 1

### take logs
tmp.wt.log.ratio <- log(tmp.output.counts[which(merged.counts$wt == TRUE)] /
tmp.input.counts[which(merged.counts$wt == TRUE)])
tmp.wt.log.ratio <- log(tmp.output.counts[which(merged.counts\$wt == TRUE)] /
tmp.input.counts[which(merged.counts\$wt == TRUE)])
tmp.fitness <- log(tmp.output.counts /
tmp.input.counts) - tmp.wt.log.ratio

Expand All @@ -174,40 +174,40 @@ rescale_and_summarize <- function(merged.counts, reps) {
colnames(merged.counts)[ncol(merged.counts)] <- paste0("rescaled_fitness_rep", i)

### fetch the key counts
tmp.wt.fitness <- merged.counts[which(merged.counts$aa_ham == 0),ncol(merged.counts) - reps]
tmp.stop.fitness <- merged.counts[which(merged.counts$stop == TRUE),ncol(merged.counts) - reps]
tmp.wt.fitness <- merged.counts[which(merged.counts\$aa_ham == 0),ncol(merged.counts) - reps]
tmp.stop.fitness <- merged.counts[which(merged.counts\$stop == TRUE),ncol(merged.counts) - reps]

### rescale
tmp.wt.fitness.med <- median(tmp.wt.fitness, na.rm = TRUE)
tmp.stop.fitness.med <- median(tmp.stop.fitness, na.rm = TRUE)
## if both WT and STOP mutants are available
if(!is.na(tmp.wt.fitness.med) & !is.na(tmp.stop.fitness.med)){
lm.rescale <- lm(c(0, -1) ~ c(tmp.wt.fitness.med, tmp.stop.fitness.med))
merged.counts[,ncol(merged.counts)] <- merged.counts[,ncol(merged.counts) - reps] * lm.rescale$coefficients[[2]] + lm.rescale$coefficients[[1]]
merged.counts[,ncol(merged.counts)] <- merged.counts[,ncol(merged.counts) - reps] * lm.rescale\$coefficients[[2]] + lm.rescale\$coefficients[[1]]
rm(tmp.wt.fitness, tmp.stop.fitness,
tmp.wt.fitness.med, tmp.stop.fitness.med, lm.rescale)

## if only WT mutants are available: lower peak determined by bimodal distribution fitting
}else if(!is.na(tmp.wt.fitness.med) & is.na(tmp.stop.fitness.med)){
tmp.peaks <- sort(density_peaks(x = merged.counts[,ncol(merged.counts) - reps]))
lm.rescale <- lm(c(0, -1) ~ c(tmp.wt.fitness.med, tmp.peaks[1]))
merged.counts[,ncol(merged.counts)] <- merged.counts[,ncol(merged.counts) - reps] * lm.rescale$coefficients[[2]] + lm.rescale$coefficients[[1]]
merged.counts[,ncol(merged.counts)] <- merged.counts[,ncol(merged.counts) - reps] * lm.rescale\$coefficients[[2]] + lm.rescale\$coefficients[[1]]
rm(tmp.wt.fitness, tmp.stop.fitness,
tmp.wt.fitness.med, tmp.stop.fitness.med, lm.rescale, tmp.peaks)

## if only STOP mutants are available: higher peak determined by bimodal distribution fitting
}else if(is.na(tmp.wt.fitness.med) & !is.na(tmp.stop.fitness.med)){
tmp.peaks <- sort(density_peaks(x = merged.counts[,ncol(merged.counts) - reps]))
lm.rescale <- lm(c(0, -1) ~ c(tmp.peaks[2], tmp.stop.fitness.med))
merged.counts[,ncol(merged.counts)] <- merged.counts[,ncol(merged.counts) - reps] * lm.rescale$coefficients[[2]] + lm.rescale$coefficients[[1]]
merged.counts[,ncol(merged.counts)] <- merged.counts[,ncol(merged.counts) - reps] * lm.rescale\$coefficients[[2]] + lm.rescale\$coefficients[[1]]
rm(tmp.wt.fitness, tmp.stop.fitness,
tmp.wt.fitness.med, tmp.stop.fitness.med, lm.rescale, tmp.peaks)

## if neither WT nor STOP mutants are available: both peak determined by bimodal distribution fitting
}else if(is.na(tmp.wt.fitness.med) & is.na(tmp.stop.fitness.med)){
tmp.peaks <- sort(density_peaks(x = merged.counts[,ncol(merged.counts) - reps]))
lm.rescale <- lm(c(0, -1) ~ c(tmp.peaks[2], tmp.peaks[1]))
merged.counts[,ncol(merged.counts)] <- merged.counts[,ncol(merged.counts) - reps] * lm.rescale$coefficients[[2]] + lm.rescale$coefficients[[1]]
merged.counts[,ncol(merged.counts)] <- merged.counts[,ncol(merged.counts) - reps] * lm.rescale\$coefficients[[2]] + lm.rescale\$coefficients[[1]]
rm(tmp.wt.fitness, tmp.stop.fitness,
tmp.wt.fitness.med, tmp.stop.fitness.med, lm.rescale, tmp.peaks)

Expand All @@ -221,15 +221,15 @@ rescale_and_summarize <- function(merged.counts, reps) {

if(reps == 1){

merged.counts$`mean fitness` <- merged.counts[,ncol(merged.counts) - 2]
merged.counts\$'mean fitness' <- merged.counts[,ncol(merged.counts) - 2]

}else if(reps > 1){

merged.counts$`mean fitness` <- apply(merged.counts[,c(ncol(merged.counts) - 1 - reps):c(ncol(merged.counts) - 2)],
merged.counts\$'mean fitness' <- apply(merged.counts[,c(ncol(merged.counts) - 1 - reps):c(ncol(merged.counts) - 2)],
1,
mean,
na.rm = TRUE)
merged.counts$`fitness sd` <- apply(merged.counts[,c(ncol(merged.counts) - 1 - reps):c(ncol(merged.counts) - 2)],
merged.counts\$'fitness sd' <- apply(merged.counts[,c(ncol(merged.counts) - 1 - reps):c(ncol(merged.counts) - 2)],
1,
sd,
na.rm = TRUE)
Expand Down Expand Up @@ -282,8 +282,8 @@ run_fitness_estimation <- function(counts_path,
## 3. Raw fitness calculations ##
#################################
fitness_res <- calc_raw_fitness(merged.counts, exp.design)
merged.counts <- fitness_res$merged.counts
reps <- fitness_res$reps
merged.counts <- fitness_res\$merged.counts
reps <- fitness_res\$reps

## 4. Fitness and error refinements ##
######################################
Expand Down Expand Up @@ -354,7 +354,7 @@ if (length(Biostrings_version) == 0) Biostrings_version <- "unknown"
f <- file("versions.yml", "w")
writeLines(
c(
'"${task.process}":',
'"\${task.process}":',
paste(' r-base:', r_version),
paste(' r-Biostrings:', Biostrings_version)
),
Expand Down
12 changes: 12 additions & 0 deletions tests/.nftignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.DS_Store
multiqc/multiqc_data/fastqc_top_overrepresented_sequences_table.txt
multiqc/multiqc_data/multiqc.parquet
multiqc/multiqc_data/multiqc.log
multiqc/multiqc_data/multiqc_data.json
multiqc/multiqc_data/multiqc_sources.txt
multiqc/multiqc_data/multiqc_software_versions.txt
multiqc/multiqc_data/llms-full.txt
multiqc/multiqc_plots/{svg,pdf,png}/*.{svg,pdf,png}
multiqc/multiqc_report.html
fastqc/*_fastqc.{html,zip}
pipeline_info/*.{html,json,txt,yml}
33 changes: 33 additions & 0 deletions tests/default.nf.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
nextflow_pipeline {

name "Test pipeline"
script "../main.nf"
tag "pipeline"

test("-profile test") {

when {
params {
outdir = "$outputDir"
}
}

then {
// stable_name: All files + folders in ${params.outdir}/ with a stable name
def stable_name = getAllFilesFromDir(params.outdir, relative: true, includeDir: true, ignore: ['pipeline_info/*.{html,json,txt}'])
// stable_path: All files in ${params.outdir}/ with stable content
def stable_path = getAllFilesFromDir(params.outdir, ignoreFile: 'tests/.nftignore')
assertAll(
{ assert workflow.success},
{ assert snapshot(
// pipeline versions.yml file for multiqc from which Nextflow version is removed because we test pipelines on multiple Nextflow versions
removeNextflowVersion("$outputDir/pipeline_info/nf_core_deepmutscan_software_mqc_versions.yml"),
// All stable path name, with a relative path
stable_name,
// All files with stable contents
stable_path
).match() }
)
}
}
}
14 changes: 14 additions & 0 deletions tests/nextflow.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
========================================================================================
Nextflow config file for running nf-test tests
========================================================================================
*/

// TODO nf-core: Specify any additional parameters here
// Or any resources requirements
params {
modules_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/'
pipelines_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/refs/heads/deepmutscan'
}

aws.client.anonymous = true // fixes S3 access issues on self-hosted runners
Loading