From 1478c51d8068c8f1ca9e6d112500da179da5dcb3 Mon Sep 17 00:00:00 2001 From: Faitero Date: Fri, 25 Sep 2020 13:54:37 +0200 Subject: [PATCH 01/26] add sha test and split rules --- iCount/snakemake/rules/demultiplex.smk | 0 iCount/snakemake/rules/map_reads.smk | 0 iCount/snakemake/rules/prepare_genome.smk | 0 iCount/snakemake/rules/qc.smk | 0 iCount/snakemake/rules/rules/qc.smk | 0 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 iCount/snakemake/rules/demultiplex.smk create mode 100644 iCount/snakemake/rules/map_reads.smk create mode 100644 iCount/snakemake/rules/prepare_genome.smk create mode 100644 iCount/snakemake/rules/qc.smk create mode 100644 iCount/snakemake/rules/rules/qc.smk diff --git a/iCount/snakemake/rules/demultiplex.smk b/iCount/snakemake/rules/demultiplex.smk new file mode 100644 index 0000000..e69de29 diff --git a/iCount/snakemake/rules/map_reads.smk b/iCount/snakemake/rules/map_reads.smk new file mode 100644 index 0000000..e69de29 diff --git a/iCount/snakemake/rules/prepare_genome.smk b/iCount/snakemake/rules/prepare_genome.smk new file mode 100644 index 0000000..e69de29 diff --git a/iCount/snakemake/rules/qc.smk b/iCount/snakemake/rules/qc.smk new file mode 100644 index 0000000..e69de29 diff --git a/iCount/snakemake/rules/rules/qc.smk b/iCount/snakemake/rules/rules/qc.smk new file mode 100644 index 0000000..e69de29 From 8e7f5c61a9e44867205c84e087877f91efb1892c Mon Sep 17 00:00:00 2001 From: Faitero Date: Fri, 25 Sep 2020 14:00:35 +0200 Subject: [PATCH 02/26] remove duplicate file --- iCount/snakemake/rules/rules/qc.smk | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 iCount/snakemake/rules/rules/qc.smk diff --git a/iCount/snakemake/rules/rules/qc.smk b/iCount/snakemake/rules/rules/qc.smk deleted file mode 100644 index e69de29..0000000 From bd6b7e04b39a319fbc7e3bc4b209fde5fd86a60c Mon Sep 17 00:00:00 2001 From: Faitero Date: Fri, 25 Sep 2020 14:05:48 +0200 Subject: [PATCH 03/26] merging --- iCount/examples/config_synthetic.yaml | 48 ++- iCount/snakemake/icount_snakemake.smk | 374 +++++--------------- iCount/snakemake/rules/bedgraph_UCSC.smk | 8 - iCount/snakemake/rules/common.smk | 68 +++- iCount/snakemake/rules/demultiplex.smk | 40 +++ iCount/snakemake/rules/map_reads.smk | 24 ++ iCount/snakemake/rules/prepare_genome.smk | 97 +++++ iCount/snakemake/rules/qc.smk | 60 ++++ iCount/snakemake/schemas/config.schema.yaml | 32 +- 9 files changed, 426 insertions(+), 325 deletions(-) diff --git a/iCount/examples/config_synthetic.yaml b/iCount/examples/config_synthetic.yaml index 1f079f3..2d52d2f 100755 --- a/iCount/examples/config_synthetic.yaml +++ b/iCount/examples/config_synthetic.yaml @@ -7,26 +7,22 @@ #=============================== Project ==============================# # Define name of the project. Output files will be saved under "project" folder. - ## Project directory name project: "synthetic" #=============================== Samples ==============================# # Define which samples to map and which species to map to. -# # Note that the path to the sample can be absolute or relative to the directory # where the Snakefile is run. - # Path or URL to sample sheet (tsv format, TAB separated columns: -# sample_name, method, protein, cells/tissue, condition, mapto, 5_barcode, -# 3_barcode, 3_adapter, linker, comments, group) +# sample_name, method, protein, cells/tissue, condition, mapto, 5_barcode, +# 3_barcode, 3_adapter, linker, comments, group) # If the column "group" is present (which is optional), # samples will be merged and output for the group calculated. - # Sample table input accepts tsv or txt #samples: "data/hnRNPC_reduced.tsv" samples: "data/samples_synthetic.txt" @@ -84,34 +80,36 @@ bedgraph_UCSC: true # takifugu_rubripes,tarsius_syrichta,tetraodon_nigroviridis,tupaia_belangeri,tursiops_truncatus,vicugna_pacos,xenopus_tropicalis,xiphophorus_maculatus # Update releases script and Icount annotation and genome doesn't accept "--releases" word - - # Current version of iCount is tested to work with human and mouse genomes only. - # Directory where all the genomes, STAR index and iCount segment files will be stored. genomes_path: "iCount_genomes" - # Annotation release on ensembl !!! Update to latest release release: 88 +# Processing the entire genome is computationally very expensive. For this reason, we are limiting the tutorial example +# to chromosomes 21 and MT. !!!!! Include validation +chromosomes: 21 MT +# Source of data. Only ENSEMBL or GENCODE are available (default: gencode)!!!!! Include validation!! gencode doesn't work +source: ensembl -# Alternatively, you can use you custom genome including acronym_name, fasta sequence (genome_fasta) and gtf file (annotation) paths +# Alternatively, you can use you custom genome including acronym_name, fasta sequence (genome_fasta) and gtf file +# (annotation) paths custom_genome: hg19: # Genomic fasta sequence genome_fasta: "custom/custom.fa.gz" # gtf file with transcripts annotation: "custom/custom.gtf.gz" - # Overhang for STAR index hg38: # Genomic fasta sequence genome_fasta: "custom/custom2.fa.gz" # gtf file with transcripts annotation: "custom/custom2.gtf.gz" -## Custom genome example annotation. Name directory, fasta sequence and annotation using the same acromyn ("hg19") as in the example +# Custom genome example annotation. Name directory, fasta sequence and annotation using the same acromyn ("hg19") +# as in the example: #custom_genome: # hg19: # # Genomic fasta sequence @@ -183,8 +181,8 @@ group_by: "start" # Report number of 'cDNA' or number of 'reads' (default: cDNA) quant: "cDNA" -# Reads on same position with random barcode differing less than -# ``mismatches`` are merged together, if their ratio is below ratio_th (default: 1) +# Reads on same position with random barcode differing less than ``mismatches`` +# are merged together, if their ratio is below ratio_th (default: 1) mismatches: 1 # Ignore hits with MAPQ < mapq_th (default: 0) @@ -251,3 +249,23 @@ imgfmt: "png" # Log directory to store invidual jobs output and error when run on a cluster logdir: "logs_cluster" + + +#===================== Workflow integrity check ==========================# + +# Create integrity and reproducibility test, boolean 'true' or 'false' (default: false). +create_integrity_test: false +# Check integrity and reproducibility test, boolean 'true' or 'false' (default: false). +integrity_test_check: true + + +### Project test directory name +#project: "integrity_test" +# +## Sample table input accepts tsv or txt +##samples: "data/hnRNPC_reduced.tsv" +#samples: "data/samples_synthetic.txt" +# +## Raw fastq file to demultiplex and analyse +##raw_fastq_file: "data/hnRNPC.fq.gz" +#raw_fastq_file: "data/merge_my_script.fq.gz" diff --git a/iCount/snakemake/icount_snakemake.smk b/iCount/snakemake/icount_snakemake.smk index a5525ec..271afa3 100644 --- a/iCount/snakemake/icount_snakemake.smk +++ b/iCount/snakemake/icount_snakemake.smk @@ -2,7 +2,7 @@ # iCount Snakemake workflow #==============================================================================# # Authors # Igor Ruiz de los Mozos, Charlotte Capitanchik, Tomaz Curk -# Last updated: November 2019 +# Last updated: September 2020 # Install Locally @@ -30,7 +30,7 @@ # Step two: To run locally use command: # snakemake -k -p --snakefile demultiplex_snakefile.smk --use-conda # snakemake -k -p --cores 4 --snakefile demultiplex_snakefile.smk --use-conda - +# snakemake -k -p --cores 4 --snakefile '/Users/mozosi/Dropbox (UCL-MN Team)/GitHub/iCount/iCount/snakemake/icount_snakemake.smk' --use-conda --configfile config_synthetic.yaml # dag workflow # snakemake --snakefile demultiplex_snakefile.smk --use-conda --dag 2> /dev/null | dot -T png > workflow_bysample.png # snakemake --snakefile demultiplex_snakefile.smk --use-conda --rulegraph 2> /dev/null | dot -T png > workflow.png @@ -89,48 +89,42 @@ import yaml from snakemake.utils import validate - - - #~~~~~~~~~~~~~~~~~~~~~* Import config file and samples annotation *~~~~~~~~~~~~~~~~~~~~# # Config file can be specified here or on the snakemake call (--configfile config_synthetic.yaml) # configfile:"config_synthetic.yaml" +# Note that the path to the configfile can be absolute or relative to the directory +# where the Snakefile is run. +# Validate config file validate(config, schema="schemas/config.schema.yaml") +# Import sample file and validate samples samples = pd.read_table(config["samples"]).set_index("barcode_5", drop=False) #samples = pd.read_table(config["samples"]) validate(samples, schema="schemas/samples.schema.yaml") - -# Move to common rules +# Validate adapter and samples integrity if len(samples["adapter_3"].unique().tolist()) > 1: sys.exit("iCount pipeline only accepts a unique 3' adapter") if len(samples.index) != len(samples["sample_name"].unique().tolist()): sys.exit("iCount pipeline only accepts a unique sample names") - # Merge 5'barcode and 3'barcode to create a table index (full barcode) cols = ['barcode_5', 'barcode_3'] samples["full_barcode"] = samples[cols].apply(lambda x: '_'.join(x.dropna()), axis=1) samples=samples.set_index(["full_barcode"], drop = False) -#~~~~~~~~~~~~~~~~~~~~~* Create log folder for cluster run *~~~~~~~~~~~~~~~~~~~~# -logdir = os.path.join(os.getcwd(), config["logdir"]) -os.makedirs(logdir, exist_ok=True) - - - -# PROJECT = config['project'] -# print("Procesing project:", PROJECT) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* Final outputs *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# -# Import set of rules +##### load rules ##### include: "rules/common.smk" -#include: "/Users/mozosi/Dropbox (UCL-MN Team)/GitHub/iCount/iCount/snakemake/rules/demultiplex.smk" +include: "rules/demultiplex.smk" +include: "rules/qc.smk" +include: "rules/prepare_genome.smk" +include: "rules/map_reads.smk" include: "rules/bedgraph_UCSC.smk" @@ -150,35 +144,27 @@ def all_input(wildcards): # final_output.extend( # expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.UCSC.bedgraph", project=config['project'], barcode=samples.index) # ) + if config["create_integrity_test"]: + final_output.extend(expand("{project}/shasum_files/demultiplex_shasum_file.txt", project=config['project'])) + final_output.extend(expand("{project}/shasum_files/qc_shasum_file.txt", project=config['project'])) + final_output.extend(expand("{project}/shasum_files/genome_shasum_file.txt", project=config['project'])) + if config["integrity_test_check"]: + final_output.extend(expand("{project}/shasum_files/demultiplex_shasum_check.txt", project=config['project'])) + final_output.extend(expand("{project}/shasum_files/qc_shasum_check.txt", project=config['project'])) + final_output.extend(expand("{project}/shasum_files/genome_shasum_check.txt", project=config['project'])) return final_output - +##### target rules ##### localrules: all rule all: input: - expand("{genomes_path}/{genome}/{genome}.fa.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), - expand("{genomes_path}/{genome}/{genome}.fa.gz.fai", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), - expand("{genomes_path}/{genome}/{genome}.gtf.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), - expand("{genomes_path}/{genome}/star_index/", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), - expand("{genomes_path}/{genome}/segment/{genome}_segment.gtf", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), - expand("{genomes_path}/{genome}/segment/landmarks.bed.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), - "demultiplexed/demux_nomatch5.fastq.gz", - expand("qc/fastqc/raw_fastq_file_fastqc.html"), - expand("qc/fastqc/raw_fastq_file_fastqc.zip"), - expand("{project}/qc/fastqc/{barcode}_fastqc.html", project=config['project'], barcode=samples.index), - expand("{project}/qc/fastqc/{barcode}_fastqc.zip", project=config['project'], barcode=samples.index,), - - expand("{project}/trimmed/demux_{barcode}_trimmed.fastq.gz", project=config['project'], barcode=samples.index), - expand("{project}/qc/fastqc/{barcode}_trimmed_fastqc.html", project=config['project'], barcode=samples.index), - expand("{project}/qc/fastqc/{barcode}_trimmed_fastqc.zip", project=config['project'], barcode=samples.index), - expand("{project}/mapped/{barcode}/Aligned.sortedByCoord.out.bam", project=config['project'], barcode=samples.index), expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", project=config['project'], barcode=samples.index), @@ -205,289 +191,99 @@ rule all: expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.bedgraph", project=config['project'], group=samples["group"].dropna().unique()), expand("{project}/groups/{group}/rnamaps/", project=config['project'], group=samples["group"].dropna().unique()), - expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.bed", project=config['project'], group=samples["group"].dropna().unique()), - expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.biotype.tab", project=config['project'], group=samples["group"].dropna().unique()), - expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.gene_id.tab", project=config['project'], group=samples["group"].dropna().unique()), - expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_gene.tsv", project=config['project'], group=samples["group"].dropna().unique()), + # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.bed", project=config['project'], group=samples["group"].dropna().unique()), + # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.biotype.tab", project=config['project'], group=samples["group"].dropna().unique()), + # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.gene_id.tab", project=config['project'], group=samples["group"].dropna().unique()), + # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_gene.tsv", project=config['project'], group=samples["group"].dropna().unique()), # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_type.tsv", project=config['project'], group=samples["group"].dropna().unique()), # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_subtype.tsv", project=config['project'], group=samples["group"].dropna().unique()), expand("{project}/groups/{group}/clusters/{group}.group.clusters.bed", project=config['project'], group=samples["group"].dropna().unique()), all_input - - -#==============================================================================# -# Demultiplex -#==============================================================================# -#### Check if one of the barcodes OR nomatch is not created/found - -rule demultiplex: - input: - fastq_file=config['raw_fastq_file'] - output: - expand("demultiplexed/demux_{barcode}.fastq.gz", barcode=samples.index), - # "{project}/demultiplexed/demux_{barcode}.fastq.gz", - "demultiplexed/demux_nomatch5.fastq.gz" - params: - adapter3=samples["adapter_3"].unique().tolist(), - all_5barcodes=samples["barcode_5"].tolist(), - # all_3barcodes=samples["barcode_3"].tolist(), - all_3barcodes = samples["barcode_3"].fillna(".").tolist(), - dir=directory("demultiplexed"), - barcode_mismatches=config['barcode_mismatches'], - minimum_length=config['minimum_length'], - min_adapter_overlap=config['min_adapter_overlap'], - shell: - """ - iCount demultiplex --mismatches {params.barcode_mismatches} --min_adapter_overlap {params.min_adapter_overlap} --minimum_length {params.minimum_length} {input.fastq_file} {params.adapter3} {params.all_5barcodes} --barcodes3 {params.all_3barcodes} --out_dir {params.dir} - """ - -# rule move_demultiplex: -# input: -# directory("demultiplexed/") -# output: -# directory("{project}/demultiplexed/".format(project=config['project'])) -# run: -# shutil.copytree(input, output) - -# -M {log.metrics} 2> {log.log} -# log: -# metrics = "{project}/metrics/demultiplex_metrics.txt", -# log = "{project}/logs/demultiplex_metrics.txt" - -#==============================================================================# -# Read quality trimming and QC -#==============================================================================# - -# shell("set -euo pipefail") - -rule fastqc_raw: - input: - config['raw_fastq_file'] - output: - html="qc/fastqc/raw_fastq_file_fastqc.html", - zip="qc/fastqc/raw_fastq_file_fastqc.zip" # the suffix _fastqc.zip is necessary for multiqc to find the file. If not using multiqc, you are free to choose an arbitrary filename - wrapper: - "0.38.0/bio/fastqc" - - -rule fastqc: - input: - "demultiplexed/demux_{barcode}.fastq.gz" - output: - html="{project}/qc/fastqc/{barcode}_fastqc.html", - zip="{project}/qc/fastqc/{barcode}_fastqc.zip" # the suffix _fastqc.zip is necessary for multiqc to find the file. If not using multiqc, you are free to choose an arbitrary filename - log: - "{project}/logs/fastqc/{barcode}_fastqc.txt" - wrapper: - "0.36.0/bio/fastqc" - - -# Include Temporal file -rule quality_trim: - input: - "demultiplexed/demux_{barcode}.fastq.gz" - output: - trimmed_reads="{project}/trimmed/demux_{barcode}_trimmed.fastq.gz", - metrics="{project}/metrics/{barcode}_trimmed.txt" - params: - qual_trim=config['qual_trim'], - minimum_length=config['minimum_length'], - adapter=samples["adapter_3"].unique().tolist(), - - overlap=config['overlap'], - untrimmed_output=config['untrimmed_output'], - error_rate=config['error_rate'], - log: - "{project}/logs/trimmed/{barcode}_trimmed.txt" - shell: - """ - iCount cutadapt --qual_trim {params.qual_trim} --untrimmed_output {params.untrimmed_output} --minimum_length {params.minimum_length} --file_log 2 --file_logpath {log} --results_file {output.metrics} --reads_trimmed {output.trimmed_reads} {input} {params.adapter} - """ - ## Unused parameters: --overlap {params.overlap} - - -rule fastqc_trimmed: - input: - "{project}/trimmed/demux_{barcode}_trimmed.fastq.gz" - output: - html="{project}/qc/fastqc/{barcode}_trimmed_fastqc.html", - zip="{project}/qc/fastqc/{barcode}_trimmed_fastqc.zip" # the suffix _fastqc.zip is necessary for multiqc to find the file. If not using multiqc, you are free to choose an arbitrary filename - log: - "{project}/logs/fastqc/{barcode}_trimmed_fastqc.log" - wrapper: - "0.36.0/bio/fastqc" - +# old rule all not used - remove +# expand("{genomes_path}/{genome}/{genome}.fa.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), +# expand("{genomes_path}/{genome}/{genome}.fa.gz.fai", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), +# expand("{genomes_path}/{genome}/{genome}.gtf.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), +# expand("{genomes_path}/{genome}/star_index/", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), +# expand("{genomes_path}/{genome}/segment/{genome}_segment.gtf", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), +# expand("{genomes_path}/{genome}/segment/landmarks.bed.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), +# +# expand("qc/fastqc/raw_fastq_file_fastqc.html"), +# expand("qc/fastqc/raw_fastq_file_fastqc.zip"), +# expand("{project}/qc/fastqc/{barcode}_fastqc.html", project=config['project'], barcode=samples.index), +# expand("{project}/qc/fastqc/{barcode}_fastqc.zip", project=config['project'], barcode=samples.index, ), +# +# expand("{project}/trimmed/demux_{barcode}_trimmed.fastq.gz", project=config['project'], barcode=samples.index), +# expand("{project}/qc/fastqc/{barcode}_trimmed_fastqc.html", project=config['project'], barcode=samples.index), +# expand("{project}/qc/fastqc/{barcode}_trimmed_fastqc.zip", project=config['project'], barcode=samples.index), #==============================================================================# -# Download annotation and index genome +# SHA Check #==============================================================================# -# if the genome is not in the config file will fail with a hint to include path to fasta file and annotation. This to validation of tabular sample file!! - -# genomes_path: "iCount_genomes" -# Missing input files(Using '~' in your paths is not allowed as such platform specific syntax is not resolved by Snakemake. In general, try sticking to relative paths for everything inside the working directory.) for rule all: - - -#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* Check for custom genomes *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# - -# Capture iCount available genomes -species_out = subprocess.Popen(["iCount species --source ensembl -r 88"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) -stdout,stderr = species_out.communicate() -available_genomes=stdout.decode('utf-8').rstrip() -available_genomes=re.split('available: ',str(available_genomes))[-1].split(',') - -all_genomes=samples["mapto"].unique() -custom_genomes=np.setdiff1d(all_genomes, available_genomes) -download_genomes=np.setdiff1d(all_genomes, custom_genomes) - - -# Custom genomes path from config file -def custom_fasta(wildcards): - return config['custom_genome'][wildcards]['genome_fasta'] - -def custom_gtf(wildcards): - return config['custom_genome'][wildcards]['annotation'] - - - - -# Funcion from icount (call it!!) -def decompress_to_tempfile(fname, context='misc'): - """ - Decompress files ending with .gz to a temporary file and return filename. - If file does nto end with .gz, juts return fname. - Parameters - ---------- - fname : str - Path to file to open. - context : str - Name of temporary subfolder where temporary file is created. - Returns - ------- - str - Path to decompressed file. - """ - if fname.endswith('.gz'): - tmp_dir = os.path.join(context) - if not os.path.exists(tmp_dir): - os.makedirs(tmp_dir) - - suffix = '_{:s}'.format(os.path.basename(fname)) - fout = tempfile.NamedTemporaryFile(suffix=suffix, dir=tmp_dir, delete=False) - fin = gzip.open(fname, 'r') - shutil.copyfileobj(fin, fout) - fin.close() - fout.close() - return fout.name - - return fname - - -# Tested with homo_sapiens, mus_musculus; and custom hg19, hg38, mm10, mm15. -rule download_genome: - output: - genome_fasta="{genomes_path}/{genome}/{genome}.fa.gz", - genome_index="{genomes_path}/{genome}/{genome}.fa.gz.fai", - gtf="{genomes_path}/{genome}/{genome}.gtf.gz", - params: - release=config['release'], - run: - GENOME=wildcards.genome - print ("Adquiring genome: %s \n" % (GENOME)) - - - if GENOME in download_genomes: - print ("Downloading iCount available genome:", GENOME) - print ("Downloading genomes could take some time depending on your conection") - shell("iCount genome --genome {output.genome_fasta} --source ensembl {wildcards.genome} {params.release} --chromosomes MT 19") # For testing include --chromosomes MT 19 - shell("iCount annotation --annotation {output.gtf} --source ensembl {wildcards.genome} {params.release}") +# print ("ALL_DEMULTIPLEXED", expand("demultiplexed/demux_{barcode}.fastq.gz", barcode=samples.index)) +# demultiplexed/demux_NNNNGTAACNNN_NNATT.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNAGG.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNTTA.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNTGC.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNCTG.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNCGT.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNGTC.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNGGA.fastq.gz demultiplexed/demux_NNNNCCGGANNN.fastq.gz demultiplexed/demux_NNNCTGCNN.fastq.gz +# cat demultiplexed/demux_NNNNGTAACNNN_NNATT.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNAGG.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNTTA.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNTGC.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNCTG.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNCGT.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNGTC.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNGGA.fastq.gz demultiplexed/demux_NNNNCCGGANNN.fastq.gz demultiplexed/demux_NNNCTGCNN.fastq.gz | md5 +# 3ef0e4c8a7628d7333df3cc315f498ce +# shasum demultiplexed/demux_NNNNGTAACNNN_NNATT.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNAGG.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNTTA.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNTGC.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNCTG.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNCGT.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNGTC.fastq.gz demultiplexed/demux_NNNNGTAACNNN_NNGGA.fastq.gz demultiplexed/demux_NNNNCCGGANNN.fastq.gz demultiplexed/demux_NNNCTGCNN.fastq.gz > demultiplex_md5.txt +# shasum -c demultiplex_md5.txt - elif GENOME in config['custom_genome'].keys(): - fasta_in = custom_fasta(GENOME) - gtf_in = custom_gtf(GENOME) - - # Move genome data - shutil.copy(fasta_in, output.genome_fasta) - shutil.copy(gtf_in, output.gtf) +# "qc/fastqc/raw_fastq_file_fastqc.html" +# - # Create fasta index - temp = decompress_to_tempfile(fasta_in) - pysam.faidx(temp) # pylint: disable=no-member - shutil.move(temp + '.fai', output.genome_index) +# "{project}/qc/fastqc/{barcode}_trimmed_fastqc.html" - else: - print ("Your genome %s in the annotation table %s is not in the iCount available genomes %s \n\n" % (GENOME, config["samples"], available_genomes)) - print ("Please, check misspelled genome or include custom genome %s fasta sequence and annotation GTF file in the config file:" % (GENOME)) - print (yaml.dump(config, default_flow_style=False)) -rule indexstar_genome: +rule shasum_create: input: - genome_fasta="{genomes_path}/{genome}/{genome}.fa.gz", - gtf="{genomes_path}/{genome}/{genome}.gtf.gz", - threads: - int(config['index_threads']) - params: - overhang=config['overhang'], + demultiplex=expand("demultiplexed/demux_{barcode}.fastq.gz", barcode=samples.index), + qc=["qc/fastqc/raw_fastq_file_fastqc.html", + expand("{project}/qc/fastqc/{barcode}_fastqc.html", project=config['project'], barcode=samples.index), + expand("{project}/qc/fastqc/{barcode}_trimmed_fastqc.html", project=config['project'], barcode=samples.index)], + genome=[expand("{genomes_path}/{genome}/{genome}.fa.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), + expand("{genomes_path}/{genome}/{genome}.fa.gz.fai", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), + expand("{genomes_path}/{genome}/{genome}.gtf.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), + expand("{genomes_path}/{genome}/segment/{genome}_segment.gtf", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), + expand("{genomes_path}/{genome}/segment/landmarks.bed.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path'])], + mapped_reads=expand("{project}/mapped/{barcode}/Aligned.sortedByCoord.out.bam", project=config['project'], barcode=samples.index), output: - directory("{genomes_path}/{genome}/star_index/"), + demultiplex_shasum_file="{project}/shasum_files/demultiplex_shasum_file.txt", + qc_shasum_file="{project}/shasum_files/qc_shasum_file.txt", + genome_shasum_file="{project}/shasum_files/genome_shasum_file.txt", + mapped_reads_shasum_file="{project}/shasum_files/mapped_reads_shasum_file.txt", shell: """ - iCount indexstar --overhang {params.overhang} --annotation {input.gtf} \ - --threads {threads} --genome_sasparsed 2 {input.genome_fasta} {output} + shasum {input.demultiplex} > {output.demultiplex_shasum_file} + shasum {input.qc} > {output.qc_shasum_file} + shasum {input.genome} > {output.genome_shasum_file} + shasum {input.mapped_reads} > {output.mapped_reads_shasum_file} """ - -rule segment: +rule shasum_check: input: - gtf="{genomes_path}/{genome}/{genome}.gtf.gz", - genome_fai="{genomes_path}/{genome}/{genome}.fa.gz.fai" + demultiplex_shasum_file="{project}/shasum_files/demultiplex_shasum_file.txt", + qc_shasum_file="{project}/shasum_files/qc_shasum_file.txt", + genome_shasum_file="{project}/shasum_files/genome_shasum_file.txt", + mapped_reads_shasum_file="{project}/shasum_files/mapped_reads_shasum_file.txt", output: - segment="{genomes_path}/{genome}/segment/{genome}_segment.gtf", - landmarks="{genomes_path}/{genome}/segment/landmarks.bed.gz", + demultiplex_shasum_check = "{project}/shasum_files/demultiplex_shasum_check.txt", + qc_shasum_check="{project}/shasum_files/qc_shasum_check.txt", + genome_shasum_check="{project}/shasum_files/genome_shasum_check.txt", + mapped_reads_shasum_check="{project}/shasum_files/mapped_reads_shasum_check.txt", shell: """ - iCount segment {input.gtf} {output.segment} {input.genome_fai} + echo "Checking test files integrity and reproducibility." + echo "--------------------------------------------------" + shasum --check {input.demultiplex_shasum_file} 2>&1 | tee {output.demultiplex_shasum_check} + shasum --check {input.qc_shasum_file} 2>&1 | tee {output.qc_shasum_check} + shasum --check {input.genome_shasum_file} 2>&1 | tee {output.genome_shasum_check} + shasum --check {input.mapped_reads_shasum_file} 2>&1 | tee {output.mapped_reads_shasum_check} """ -#==============================================================================# -# Map reads -#==============================================================================# - - -def get_gtf_path(wildcards): - return ("{0}/{1}/{1}.gtf.gz".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) -def get_star_index_path(wildcards): - return ("{0}/{1}/star_index/".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) - -def get_segment_path(wildcards): - return ("{0}/{1}/segment/{1}_segment.gtf".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) - -def get_templates_dir(wildcards): - return ("{0}/{1}/segment/".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) - - -rule map_reads: - input: - trimmed_reads="{project}/trimmed/demux_{barcode}_trimmed.fastq.gz", - gtf = get_gtf_path, - output: - "{project}/mapped/{barcode}/Aligned.sortedByCoord.out.bam" - params: - star_index = directory(get_star_index_path), - outdir=directory("{project}/mapped/{barcode}/"), - multimax=config['multimax'], - log: - "{project}/logs/mapstar/{barcode}_mapstar.log" - shell: - """ - iCount mapstar --annotation {input.gtf} --multimax {params.multimax} \ - {input.trimmed_reads} {params.star_index} {params.outdir} - """ #==============================================================================# diff --git a/iCount/snakemake/rules/bedgraph_UCSC.smk b/iCount/snakemake/rules/bedgraph_UCSC.smk index 23a0195..fdd52a4 100644 --- a/iCount/snakemake/rules/bedgraph_UCSC.smk +++ b/iCount/snakemake/rules/bedgraph_UCSC.smk @@ -2,14 +2,6 @@ # Create crosslinks bedgraphs to import on UCSC genome browser #==============================================================================# -def bedgraph_header(wildcards): - # db=\"{mapto}\" removed - # return ("{project}_{sample_name}_{protein}_{method}_{mapto}".format(project=config['project'], sample_name=samples.loc[wildcards.barcode, "sample_name"], mapto=samples.loc[wildcards.barcode, "mapto"], method=samples.loc[wildcards.barcode, "method"], protein=samples.loc[wildcards.barcode, "protein"], cells_tissue=samples.loc[wildcards.barcode, "cells_tissue"], condition=samples.loc[wildcards.barcode, "condition"],)) - return ("track type=bedGraph name=\"{project}_{sample_name}_{protein}_{method}_{mapto}_unique.xl.bedgraph.bed\" description=\"{project}_{sample_name}_{protein}_{method}_{mapto}\" " - "color=\"120,101,172\" mapped_to=\"{mapto}\" altColor=\"200,120,59\" lib_id=\"{project}\" maxHeightPixels=\"100:50:0\" visibility=\"full\" " - "tissue=\"{cells_tissue}\" protein=\"{protein}\" species=\"{mapto}\" condition=\"{condition}\" res_type=\"T\" priority=\"20\" \n".format(project=config['project'], sample_name=samples.loc[wildcards.barcode, "sample_name"], mapto=samples.loc[wildcards.barcode, "mapto"], method=samples.loc[wildcards.barcode, "method"], protein=samples.loc[wildcards.barcode, "protein"], cells_tissue=samples.loc[wildcards.barcode, "cells_tissue"], condition=samples.loc[wildcards.barcode, "condition"],)) - - rule bedgraphUCSC: input: xlsites="{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", diff --git a/iCount/snakemake/rules/common.smk b/iCount/snakemake/rules/common.smk index 883b37e..2de7355 100644 --- a/iCount/snakemake/rules/common.smk +++ b/iCount/snakemake/rules/common.smk @@ -1,15 +1,77 @@ +# Create log folder for cluster run +logdir = os.path.join(os.getcwd(), config["logdir"]) +os.makedirs(logdir, exist_ok=True) +# Print project +print("Procesing project:", config['project'], "\n") +# this container defines the underlying OS for each job when using the workflow +# with --use-conda --use-singularity +container: "docker://continuumio/miniconda3" +##### Helper functions ##### +def get_genome(wildcards): + return ("{0}".format(samples.loc[wildcards.barcode, "mapto"])) +# Custom genomes path from config file +def custom_fasta(wildcards): + return config['custom_genome'][wildcards]['genome_fasta'] +def custom_gtf(wildcards): + return config['custom_genome'][wildcards]['annotation'] -##### Helper functions ##### +# Function from icount (call it!!) +def decompress_to_tempfile(fname, context='misc'): + """ + Decompress files ending with .gz to a temporary file and return filename. + If file does nto end with .gz, juts return fname. + Parameters + ---------- + fname : str + Path to file to open. + context : str + Name of temporary subfolder where temporary file is created. + Returns + ------- + str + Path to decompressed file. + """ + if fname.endswith('.gz'): + tmp_dir = os.path.join(context) + if not os.path.exists(tmp_dir): + os.makedirs(tmp_dir) + suffix = '_{:s}'.format(os.path.basename(fname)) + fout = tempfile.NamedTemporaryFile(suffix=suffix, dir=tmp_dir, delete=False) + fin = gzip.open(fname, 'r') + shutil.copyfileobj(fin, fout) + fin.close() + fout.close() + return fout.name -def get_genome(wildcards): - return ("{0}".format(samples.loc[wildcards.barcode, "mapto"])) + return fname + +def bedgraph_header(wildcards): + # db=\"{mapto}\" removed + # return ("{project}_{sample_name}_{protein}_{method}_{mapto}".format(project=config['project'], sample_name=samples.loc[wildcards.barcode, "sample_name"], mapto=samples.loc[wildcards.barcode, "mapto"], method=samples.loc[wildcards.barcode, "method"], protein=samples.loc[wildcards.barcode, "protein"], cells_tissue=samples.loc[wildcards.barcode, "cells_tissue"], condition=samples.loc[wildcards.barcode, "condition"],)) + return ("track type=bedGraph name=\"{project}_{sample_name}_{protein}_{method}_{mapto}_unique.xl.bedgraph.bed\" description=\"{project}_{sample_name}_{protein}_{method}_{mapto}\" " + "color=\"120,101,172\" mapped_to=\"{mapto}\" altColor=\"200,120,59\" lib_id=\"{project}\" maxHeightPixels=\"100:50:0\" visibility=\"full\" " + "tissue=\"{cells_tissue}\" protein=\"{protein}\" species=\"{mapto}\" condition=\"{condition}\" res_type=\"T\" priority=\"20\" \n".format(project=config['project'], sample_name=samples.loc[wildcards.barcode, "sample_name"], mapto=samples.loc[wildcards.barcode, "mapto"], method=samples.loc[wildcards.barcode, "method"], protein=samples.loc[wildcards.barcode, "protein"], cells_tissue=samples.loc[wildcards.barcode, "cells_tissue"], condition=samples.loc[wildcards.barcode, "condition"],)) + + + +def get_gtf_path(wildcards): + return ("{0}/{1}/{1}.gtf.gz".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) + +def get_star_index_path(wildcards): + return ("{0}/{1}/star_index/".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) + +def get_segment_path(wildcards): + return ("{0}/{1}/segment/{1}_segment.gtf".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) + +def get_templates_dir(wildcards): + return ("{0}/{1}/segment/".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) diff --git a/iCount/snakemake/rules/demultiplex.smk b/iCount/snakemake/rules/demultiplex.smk index e69de29..9f5e14c 100644 --- a/iCount/snakemake/rules/demultiplex.smk +++ b/iCount/snakemake/rules/demultiplex.smk @@ -0,0 +1,40 @@ +#==============================================================================# +# Demultiplex +#==============================================================================# +#### Check if one of the barcodes OR nomatch is not created/found + +rule demultiplex: + input: + fastq_file=config['raw_fastq_file'] + output: + expand("demultiplexed/demux_{barcode}.fastq.gz", barcode=samples.index), + "demultiplexed/demux_nomatch5.fastq.gz" + params: + adapter3=samples["adapter_3"].unique().tolist(), + all_5barcodes=samples["barcode_5"].tolist(), + # all_3barcodes=samples["barcode_3"].tolist(), + all_3barcodes = samples["barcode_3"].fillna(".").tolist(), + dir=directory("demultiplexed"), + barcode_mismatches=config['barcode_mismatches'], + minimum_length=config['minimum_length'], + min_adapter_overlap=config['min_adapter_overlap'], + shell: + """ + iCount demultiplex --mismatches {params.barcode_mismatches} --min_adapter_overlap {params.min_adapter_overlap} --minimum_length {params.minimum_length} {input.fastq_file} {params.adapter3} {params.all_5barcodes} --barcodes3 {params.all_3barcodes} --out_dir {params.dir} + """ + + + + +# rule move_demultiplex: +# input: +# directory("demultiplexed/") +# output: +# directory("{project}/demultiplexed/".format(project=config['project'])) +# run: +# shutil.copytree(input, output) + +# -M {log.metrics} 2> {log.log} +# log: +# metrics = "{project}/metrics/demultiplex_metrics.txt", +# log = "{project}/logs/demultiplex_metrics.txt" \ No newline at end of file diff --git a/iCount/snakemake/rules/map_reads.smk b/iCount/snakemake/rules/map_reads.smk index e69de29..40a6ba3 100644 --- a/iCount/snakemake/rules/map_reads.smk +++ b/iCount/snakemake/rules/map_reads.smk @@ -0,0 +1,24 @@ +#==============================================================================# +# Map reads +#==============================================================================# + + + +rule map_reads: + input: + trimmed_reads="{project}/trimmed/demux_{barcode}_trimmed.fastq.gz", + gtf = get_gtf_path, + output: + "{project}/mapped/{barcode}/Aligned.sortedByCoord.out.bam" + params: + star_index = directory(get_star_index_path), + outdir=directory("{project}/mapped/{barcode}/"), + multimax=config['multimax'], + log: + "{project}/logs/mapstar/{barcode}_mapstar.log" + shell: + """ + iCount mapstar --annotation {input.gtf} --multimax {params.multimax} \ + {input.trimmed_reads} {params.star_index} {params.outdir} + """ + diff --git a/iCount/snakemake/rules/prepare_genome.smk b/iCount/snakemake/rules/prepare_genome.smk index e69de29..c4d8bc6 100644 --- a/iCount/snakemake/rules/prepare_genome.smk +++ b/iCount/snakemake/rules/prepare_genome.smk @@ -0,0 +1,97 @@ + +#==============================================================================# +# Download annotation and index genome +#==============================================================================# +# if the genome is not in the config file will fail with a hint to include path to fasta file and annotation. + +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* Check for custom genomes *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# + +# Capture iCount available genomes +# --chromosomes 21 MT +# -r 88 +args = ["iCount species --source ensembl -r 88"] +# if config["release"]: +# args.extend(config['release']) +# +# print("RELEASE and download:", args) + + +species_out = subprocess.Popen(list(args), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) +# species_out = subprocess.Popen(["iCount species --source ensembl -r 88"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) +stdout,stderr = species_out.communicate() +available_genomes=stdout.decode('utf-8').rstrip() +available_genomes=re.split('available: ',str(available_genomes))[-1].split(',') + +all_genomes=samples["mapto"].unique() +custom_genomes=np.setdiff1d(all_genomes, available_genomes) +download_genomes=np.setdiff1d(all_genomes, custom_genomes) + +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* Download ensembl genomes *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +# Tested with homo_sapiens and mus_musculus; custom hg19, hg38, mm10 and mm15. +rule download_genome: + output: + genome_fasta="{genomes_path}/{genome}/{genome}.fa.gz", + genome_index="{genomes_path}/{genome}/{genome}.fa.gz.fai", + gtf="{genomes_path}/{genome}/{genome}.gtf.gz", + params: + release=config['release'], + chromosomes=config['chromosomes'], + source=config['source'], + run: + GENOME=wildcards.genome + print ("Adquiring genome: %s \n" % (GENOME)) + + if GENOME in download_genomes: + print ("Downloading iCount available genome:", GENOME) + print ("Downloading genomes could take some time depending on your conection") + shell("iCount genome --genome {output.genome_fasta} --chromosomes {params.chromosomes} --source {params.source} {wildcards.genome} {params.release}") # For testing include --chromosomes MT 19 + shell("iCount annotation --annotation {output.gtf} --source {params.source} {wildcards.genome} {params.release}") + + elif GENOME in config['custom_genome'].keys(): + fasta_in = custom_fasta(GENOME) + gtf_in = custom_gtf(GENOME) + + # Move genome data + shutil.copy(fasta_in, output.genome_fasta) + shutil.copy(gtf_in, output.gtf) + + # Create fasta index + temp = decompress_to_tempfile(fasta_in) + pysam.faidx(temp) # pylint: disable=no-member + shutil.move(temp + '.fai', output.genome_index) + + else: + print ("Your genome %s in the annotation table %s is not in the iCount available genomes %s \n\n" % (GENOME, config["samples"], available_genomes)) + print ("Please, check misspelled genome or include custom genome %s fasta sequence and annotation GTF file in the config file:" % (GENOME)) + print (yaml.dump(config, default_flow_style=False)) + +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* STAR genome index *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +rule indexstar_genome: + input: + genome_fasta="{genomes_path}/{genome}/{genome}.fa.gz", + gtf="{genomes_path}/{genome}/{genome}.gtf.gz", + threads: + int(config['index_threads']) + params: + overhang=config['overhang'], + output: + directory("{genomes_path}/{genome}/star_index/"), + shell: + """ + iCount indexstar --overhang {params.overhang} --annotation {input.gtf} \ + --threads {threads} --genome_sasparsed 2 {input.genome_fasta} {output} + """ + +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* iCount genome segment *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +rule segment: + input: + gtf="{genomes_path}/{genome}/{genome}.gtf.gz", + genome_fai="{genomes_path}/{genome}/{genome}.fa.gz.fai" + output: + segment="{genomes_path}/{genome}/segment/{genome}_segment.gtf", + landmarks="{genomes_path}/{genome}/segment/landmarks.bed.gz", + shell: + """ + iCount segment {input.gtf} {output.segment} {input.genome_fai} + """ + diff --git a/iCount/snakemake/rules/qc.smk b/iCount/snakemake/rules/qc.smk index e69de29..c1c5366 100644 --- a/iCount/snakemake/rules/qc.smk +++ b/iCount/snakemake/rules/qc.smk @@ -0,0 +1,60 @@ + +#==============================================================================# +# Read quality trimming and QC +#==============================================================================# + +rule fastqc_raw: + input: + config['raw_fastq_file'] + output: + html="qc/fastqc/raw_fastq_file_fastqc.html", + zip="qc/fastqc/raw_fastq_file_fastqc.zip" # the suffix _fastqc.zip is necessary for multiqc to find the file. If not using multiqc, you are free to choose an arbitrary filename + wrapper: + "0.38.0/bio/fastqc" + + +rule fastqc: + input: + "demultiplexed/demux_{barcode}.fastq.gz" + output: + html="{project}/qc/fastqc/{barcode}_fastqc.html", + zip="{project}/qc/fastqc/{barcode}_fastqc.zip" # the suffix _fastqc.zip is necessary for multiqc to find the file. If not using multiqc, you are free to choose an arbitrary filename + log: + "{project}/logs/fastqc/{barcode}_fastqc.txt" + wrapper: + "0.36.0/bio/fastqc" + + +# Trimm reads +rule quality_trim: + input: + "demultiplexed/demux_{barcode}.fastq.gz" + output: + trimmed_reads="{project}/trimmed/demux_{barcode}_trimmed.fastq.gz", + metrics="{project}/metrics/{barcode}_trimmed.txt" + params: + qual_trim=config['qual_trim'], + minimum_length=config['minimum_length'], + adapter=samples["adapter_3"].unique().tolist(), + overlap=config['overlap'], + untrimmed_output=config['untrimmed_output'], + error_rate=config['error_rate'], + log: + "{project}/logs/trimmed/{barcode}_trimmed.txt" + shell: + """ + iCount cutadapt --qual_trim {params.qual_trim} --untrimmed_output {params.untrimmed_output} --minimum_length {params.minimum_length} --file_log 2 --file_logpath {log} --results_file {output.metrics} --reads_trimmed {output.trimmed_reads} {input} {params.adapter} + """ + ## Unused parameters: --overlap {params.overlap} + +rule fastqc_trimmed: + input: + "{project}/trimmed/demux_{barcode}_trimmed.fastq.gz" + output: + html="{project}/qc/fastqc/{barcode}_trimmed_fastqc.html", + zip="{project}/qc/fastqc/{barcode}_trimmed_fastqc.zip" # the suffix _fastqc.zip is necessary for multiqc to find the file. If not using multiqc, you are free to choose an arbitrary filename + log: + "{project}/logs/fastqc/{barcode}_trimmed_fastqc.log" + wrapper: + "0.36.0/bio/fastqc" + diff --git a/iCount/snakemake/schemas/config.schema.yaml b/iCount/snakemake/schemas/config.schema.yaml index 963369d..620d4fa 100644 --- a/iCount/snakemake/schemas/config.schema.yaml +++ b/iCount/snakemake/schemas/config.schema.yaml @@ -13,12 +13,15 @@ properties: description: "Raw fastq file to demultiplex and analyse" completeness_output: type: string - description: "If minimum is present only crosslinks, significant crosslinks and clusters will be calculated. On the other hand if marked as complete all the crosslinks and significant crosslinks will be annotated (default)." + description: "If minimum is present only crosslinks, significant crosslinks and clusters will be calculated. + On the other hand if marked as complete all the crosslinks and significant crosslinks will be annotated (default)." enum: ["minimum", "complete"] default: "minimum" grouped_completeness_output: type: string - description: "If minimum is present only merged crosslinks, significant crosslinks and clusters will be calculated. On the other hand if marked as complete all merged the crosslinks and significant crosslinks will be annotated (default)." + description: "If minimum is present only merged crosslinks, significant crosslinks and clusters will be calculated. + On the other hand if marked as complete all merged the crosslinks and significant crosslinks will be annotated + (default)." enum": ["minimum", "complete"] default: "complete" bedgraph_UCSC: @@ -37,7 +40,8 @@ properties: default: 88 custom_genome: type: object - description: "Alternatively, you can use you custom genome including acronym_name, fasta sequence (genome_fasta) and gtf file (annotation) paths" + description: "Alternatively, you can use you custom genome including acronym_name, fasta sequence (genome_fasta) + and gtf file (annotation) paths" minProperties: 1 additionalProperties: true barcode_mismatches: @@ -73,7 +77,8 @@ properties: description: "Write reads that do not contain any adapter to this file path (default: None)" error_rate: type: ["null", "number"] - description: "Maximum allowed error rate (no. of errors divided by the length of the matching region) (default: None)" + description: "Maximum allowed error rate (no. of errors divided by the length of the matching region) + (default: None)" overhang: type: integer description: "Overhang for STAR index. Length of the donor/acceptor sequence on each side of the junctions" @@ -81,12 +86,14 @@ properties: default: 100 mismatches: type: integer - description: "Maximum number of mismatches per pair, large number switches off this filter max number of mismatches per pair relative to read length" + description: "Maximum number of mismatches per pair, large number switches off this filter max number of mismatches + per pair relative to read length" minimum: 0 default: 2 star_multimax: type: integer - description: "Maximum number of loci the read is allowed to map to. Alignments (all ofthem) will be output only if the read maps to no more loci than this value." + description: "Maximum number of loci the read is allowed to map to. Alignments (all ofthem) will be output only if + the read maps to no more loci than this value." minimum: 0 default: 10 index_threads: @@ -106,7 +113,8 @@ properties: default: "cDNA" mismatches: type: integer - description: "Reads on same position with random barcode differing less than mismatches are merged together, if their ratio is below ratio_th (default: 1)" + description: "Reads on same position with random barcode differing less than mismatches are merged together, if + their ratio is below ratio_th (default: 1)" minimum: 0 default: 1 mapq_th: @@ -126,17 +134,21 @@ properties: default: 4 ratio_th: type: number - description: "Ratio between the number of reads supporting a randomer versus the number of reads supporting the most frequent randomer. All randomers above this threshold are accepted as unique. Remaining are merge with the rest, allowing for the specified number of mismatches (default: 0.1)" + description: "Ratio between the number of reads supporting a randomer versus the number of reads supporting the + most frequent randomer. All randomers above this threshold are accepted as unique. Remaining are merge with the + rest, allowing for the specified number of mismatches (default: 0.1)" minimum: 0 default: 0.1 max_barcodes: type: integer - description: "Skip merging similar barcodes if number of distinct barcodes at position is higher that this (default: 10000)" + description: "Skip merging similar barcodes if number of distinct barcodes at position is higher that this + (default: 10000)" minimum: 0 default: 10000 distance: type: integer - description: "Merge adjacent peaks into clusters and sum cross-links within clusters. Distance between two peaks to merge into same cluster (default: 20)" + description: "Merge adjacent peaks into clusters and sum cross-links within clusters. Distance between two peaks + to merge into same cluster (default: 20)" minimum: 0 default: 20 slop: From 44efa20e5073df2bad589270cc8a985555f46206 Mon Sep 17 00:00:00 2001 From: Faitero Date: Fri, 9 Oct 2020 14:17:07 +0200 Subject: [PATCH 04/26] finish sha test and split rules --- iCount/examples/config_synthetic.yaml | 20 +- iCount/snakemake/icount_snakemake.smk | 582 ++++--------------------- iCount/snakemake/rules/clusters.smk | 25 ++ iCount/snakemake/rules/common.smk | 128 +++++- iCount/snakemake/rules/group.smk | 201 +++++++++ iCount/snakemake/rules/sig_xlsites.smk | 79 ++++ iCount/snakemake/rules/xlsites.smk | 119 +++++ 7 files changed, 648 insertions(+), 506 deletions(-) create mode 100644 iCount/snakemake/rules/clusters.smk create mode 100644 iCount/snakemake/rules/group.smk create mode 100644 iCount/snakemake/rules/sig_xlsites.smk create mode 100644 iCount/snakemake/rules/xlsites.smk diff --git a/iCount/examples/config_synthetic.yaml b/iCount/examples/config_synthetic.yaml index 2d52d2f..8b21566 100755 --- a/iCount/examples/config_synthetic.yaml +++ b/iCount/examples/config_synthetic.yaml @@ -39,21 +39,19 @@ raw_fastq_file: "data/merge_my_script.fq.gz" # Set the desidred output of the pipeline. ## Completeness output -# If "minimum" is present only crosslinks, significant crosslinks and clusters -# will be calculated. -# On the other hand if marked as "complete" all the crosslinks and significant -# crosslinks will be annotated (default). -completeness_output: "minimum" +# If "minimum" is set only crosslinks, significant crosslinks and clusters will be calculated. +# On the other hand if marked as "complete" all the crosslinks and significant crosslinks will be annotated +# and bedgraph output file will be generated (default). +completeness_output: "complete" ## Grouped replicates completeness output -# If "minimum" is present only merged crosslinks, significant crosslinks and -# clusters will be calculated. -# On the other hand if marked as "complete" all merged the crosslinks and -# significant crosslinks will be annotated (default). -grouped_completeness_output: "minimum" +# If "minimum" is set only merged crosslinks, significant crosslinks and clusters will be calculated. +# On the other hand if marked as "complete" all merged the crosslinks, significant crosslinks will be annotated +# and bedgraph output file will be generated (default). +group_completeness_output: "minimum" # Transform crosslink 6bed to UCSC bedgraph (false or true; default false) -bedgraph_UCSC: true +bedgraph_UCSC: false #============================= Parameters =============================# diff --git a/iCount/snakemake/icount_snakemake.smk b/iCount/snakemake/icount_snakemake.smk index 271afa3..96d0cb4 100644 --- a/iCount/snakemake/icount_snakemake.smk +++ b/iCount/snakemake/icount_snakemake.smk @@ -88,7 +88,6 @@ import yaml # Validate config file!!! from snakemake.utils import validate - #~~~~~~~~~~~~~~~~~~~~~* Import config file and samples annotation *~~~~~~~~~~~~~~~~~~~~# # Config file can be specified here or on the snakemake call (--configfile config_synthetic.yaml) # configfile:"config_synthetic.yaml" @@ -116,6 +115,10 @@ samples["full_barcode"] = samples[cols].apply(lambda x: '_'.join(x.dropna()), ax samples=samples.set_index(["full_barcode"], drop = False) +# Print project +print("Procesing project:", config['project'], "\n") + + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* Final outputs *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# @@ -126,35 +129,10 @@ include: "rules/qc.smk" include: "rules/prepare_genome.smk" include: "rules/map_reads.smk" include: "rules/bedgraph_UCSC.smk" - - - -def all_input(wildcards): - """ - Function defining all requested inputs for the rule all. - """ - final_output = [] - - if config["bedgraph_UCSC"]: - final_output.extend( - expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.UCSC.bedgraph", project=config['project'], barcode=samples.index) - ) - - # if config["completeness_output"] == "minimum": - # final_output.extend( - # expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.UCSC.bedgraph", project=config['project'], barcode=samples.index) - # ) - if config["create_integrity_test"]: - final_output.extend(expand("{project}/shasum_files/demultiplex_shasum_file.txt", project=config['project'])) - final_output.extend(expand("{project}/shasum_files/qc_shasum_file.txt", project=config['project'])) - final_output.extend(expand("{project}/shasum_files/genome_shasum_file.txt", project=config['project'])) - - if config["integrity_test_check"]: - final_output.extend(expand("{project}/shasum_files/demultiplex_shasum_check.txt", project=config['project'])) - final_output.extend(expand("{project}/shasum_files/qc_shasum_check.txt", project=config['project'])) - final_output.extend(expand("{project}/shasum_files/genome_shasum_check.txt", project=config['project'])) - - return final_output +include: "rules/xlsites.smk" +include: "rules/sig_xlsites.smk" +include: "rules/clusters.smk" +include: "rules/group.smk" ##### target rules ##### @@ -165,43 +143,12 @@ rule all: input: "demultiplexed/demux_nomatch5.fastq.gz", - expand("{project}/mapped/{barcode}/Aligned.sortedByCoord.out.bam", project=config['project'], barcode=samples.index), - - expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", project=config['project'], barcode=samples.index), - expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.summary_gene.tsv", project=config['project'], barcode=samples.index), - expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.annotated_sites_biotype.tab", project=config['project'], barcode=samples.index), - expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.annotated_sites_gene_id.tab", project=config['project'], barcode=samples.index), - expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.bedgraph", project=config['project'], barcode=samples.index), - #expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.UCSC.bedgraph", project=config['project'], barcode=samples.index), - expand("{project}/xlsites/{barcode}/rnamaps/", project=config['project'], barcode=samples.index), - - expand("{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.bed", project=config['project'], barcode=samples.index), - expand("{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.summary_gene.tsv", project=config['project'], barcode=samples.index), - expand("{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.annotated.biotype.tab", project=config['project'], barcode=samples.index), - expand("{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.annotated.gene_id.tab", project=config['project'], barcode=samples.index), - - expand("{project}/clusters/{barcode}/{barcode}.clusters.bed", project=config['project'], barcode=samples.index), - - expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", project=config['project'], group=samples["group"].dropna().unique()), - expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.annotated_group_biotype.tab", project=config['project'], group=samples["group"].dropna().unique()), - expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.annotated_group_gene_id.tab", project=config['project'], group=samples["group"].dropna().unique()), - expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_gene.tsv", project=config['project'], group=samples["group"].dropna().unique()), - # expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_type.tsv", project=config['project'], group=samples["group"].dropna().unique()), - # expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_subtype.tsv", project=config['project'], group=samples["group"].dropna().unique()), - expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.bedgraph", project=config['project'], group=samples["group"].dropna().unique()), - expand("{project}/groups/{group}/rnamaps/", project=config['project'], group=samples["group"].dropna().unique()), - - # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.bed", project=config['project'], group=samples["group"].dropna().unique()), - # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.biotype.tab", project=config['project'], group=samples["group"].dropna().unique()), - # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.gene_id.tab", project=config['project'], group=samples["group"].dropna().unique()), - # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_gene.tsv", project=config['project'], group=samples["group"].dropna().unique()), - # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_type.tsv", project=config['project'], group=samples["group"].dropna().unique()), - # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_subtype.tsv", project=config['project'], group=samples["group"].dropna().unique()), - - expand("{project}/groups/{group}/clusters/{group}.group.clusters.bed", project=config['project'], group=samples["group"].dropna().unique()), - all_input + all_input, + all_xlsites, + all_group # old rule all not used - remove +# "demultiplexed/demux_nomatch5.fastq.gz", # expand("{genomes_path}/{genome}/{genome}.fa.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), # expand("{genomes_path}/{genome}/{genome}.fa.gz.fai", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), # expand("{genomes_path}/{genome}/{genome}.gtf.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), @@ -217,6 +164,69 @@ rule all: # expand("{project}/trimmed/demux_{barcode}_trimmed.fastq.gz", project=config['project'], barcode=samples.index), # expand("{project}/qc/fastqc/{barcode}_trimmed_fastqc.html", project=config['project'], barcode=samples.index), # expand("{project}/qc/fastqc/{barcode}_trimmed_fastqc.zip", project=config['project'], barcode=samples.index), +# expand("{project}/mapped/{barcode}/Aligned.sortedByCoord.out.bam", project=config['project'], barcode=samples.index), + +# expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", project=config['project'], barcode=samples.index), +# expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.summary_gene.tsv", project=config['project'], +# barcode=samples.index), +# expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.annotated_sites_biotype.tab", project=config['project'], +# barcode=samples.index), +# expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.annotated_sites_gene_id.tab", project=config['project'], +# barcode=samples.index), +# expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.bedgraph", project=config['project'], barcode=samples.index), +# # expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.UCSC.bedgraph", project=config['project'], barcode=samples.index), +# expand("{project}/xlsites/{barcode}/rnamaps/", project=config['project'], barcode=samples.index), +# +# expand("{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.bed", project=config['project'], barcode=samples.index), +# expand("{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.summary_gene.tsv", project=config['project'], +# barcode=samples.index), +# expand("{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.annotated.biotype.tab", project=config['project'], +# barcode=samples.index), +# expand("{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.annotated.gene_id.tab", project=config['project'], +# barcode=samples.index), +# +# expand("{project}/clusters/{barcode}/{barcode}.clusters.bed", project=config['project'], barcode=samples.index), +# expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", project=config['project'], +# group=samples["group"].dropna().unique()), +# expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.annotated_group_biotype.tab", +# project=config['project'], group=samples["group"].dropna().unique()), +# expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.annotated_group_gene_id.tab", +# project=config['project'], group=samples["group"].dropna().unique()), +# expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_gene.tsv", project=config['project'], +# group=samples["group"].dropna().unique()), +# # expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_type.tsv", project=config['project'], group=samples["group"].dropna().unique()), +# # expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_subtype.tsv", project=config['project'], group=samples["group"].dropna().unique()), +# expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.bedgraph", project=config['project'], +# group=samples["group"].dropna().unique()), +# expand("{project}/groups/{group}/rnamaps/", project=config['project'], group=samples["group"].dropna().unique()), +# +# # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.bed", project=config['project'], group=samples["group"].dropna().unique()), +# # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.biotype.tab", project=config['project'], group=samples["group"].dropna().unique()), +# # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.gene_id.tab", project=config['project'], group=samples["group"].dropna().unique()), +# # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_gene.tsv", project=config['project'], group=samples["group"].dropna().unique()), +# # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_type.tsv", project=config['project'], group=samples["group"].dropna().unique()), +# # expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_subtype.tsv", project=config['project'], group=samples["group"].dropna().unique()), +# +# expand("{project}/groups/{group}/clusters/{group}.group.clusters.bed", project=config['project'], +# group=samples["group"].dropna().unique()), + + +#------------------- +# expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", project=config['project'], group=samples["group"].dropna().unique()), +# expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.annotated_group_biotype.tab", project=config['project'], group=samples["group"].dropna().unique()), +# expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.annotated_group_gene_id.tab", project=config['project'], group=samples["group"].dropna().unique()), +# expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_gene.tsv", project=config['project'], group=samples["group"].dropna().unique()), +# # expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_type.tsv", project=config['project'], group=samples["group"].dropna().unique()), +# # expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_subtype.tsv", project=config['project'], group=samples["group"].dropna().unique()), +# expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.bedgraph", project=config['project'], group=samples["group"].dropna().unique()), +# expand("{project}/groups/{group}/rnamaps/", project=config['project'], group=samples["group"].dropna().unique()), + +# expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.bed", project=config['project'], group=samples["group"].dropna().unique()), +# expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.biotype.tab", project=config['project'], group=samples["group"].dropna().unique()), +# expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.gene_id.tab", project=config['project'], group=samples["group"].dropna().unique()), +# expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_gene.tsv", project=config['project'], group=samples["group"].dropna().unique()), +# expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_type.tsv", project=config['project'], group=samples["group"].dropna().unique()), +# expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_subtype.tsv", project=config['project'], group=samples["group"].dropna().unique()), #==============================================================================# # SHA Check @@ -236,7 +246,7 @@ rule all: # "{project}/qc/fastqc/{barcode}_trimmed_fastqc.html" - +#xlsites_output=list(xlsites_output) rule shasum_create: input: demultiplex=expand("demultiplexed/demux_{barcode}.fastq.gz", barcode=samples.index), @@ -249,30 +259,42 @@ rule shasum_create: expand("{genomes_path}/{genome}/segment/{genome}_segment.gtf", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), expand("{genomes_path}/{genome}/segment/landmarks.bed.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path'])], mapped_reads=expand("{project}/mapped/{barcode}/Aligned.sortedByCoord.out.bam", project=config['project'], barcode=samples.index), + xlsites=all_xlsites, + group=all_group, + output: demultiplex_shasum_file="{project}/shasum_files/demultiplex_shasum_file.txt", qc_shasum_file="{project}/shasum_files/qc_shasum_file.txt", genome_shasum_file="{project}/shasum_files/genome_shasum_file.txt", mapped_reads_shasum_file="{project}/shasum_files/mapped_reads_shasum_file.txt", + xlsites_shasum_file="{project}/shasum_files/xlsites_shasum_file.txt", + group_shasum_file="{project}/shasum_files/group_shasum_file.txt", shell: """ shasum {input.demultiplex} > {output.demultiplex_shasum_file} shasum {input.qc} > {output.qc_shasum_file} shasum {input.genome} > {output.genome_shasum_file} shasum {input.mapped_reads} > {output.mapped_reads_shasum_file} + shasum {input.xlsites} > {output.xlsites_shasum_file} + shasum {input.group} > {output.group_shasum_file} """ + rule shasum_check: input: demultiplex_shasum_file="{project}/shasum_files/demultiplex_shasum_file.txt", qc_shasum_file="{project}/shasum_files/qc_shasum_file.txt", genome_shasum_file="{project}/shasum_files/genome_shasum_file.txt", mapped_reads_shasum_file="{project}/shasum_files/mapped_reads_shasum_file.txt", + xlsites_shasum_file="{project}/shasum_files/xlsites_shasum_file.txt", + group_shasum_file="{project}/shasum_files/group_shasum_file.txt", output: demultiplex_shasum_check = "{project}/shasum_files/demultiplex_shasum_check.txt", qc_shasum_check="{project}/shasum_files/qc_shasum_check.txt", genome_shasum_check="{project}/shasum_files/genome_shasum_check.txt", mapped_reads_shasum_check="{project}/shasum_files/mapped_reads_shasum_check.txt", + xlsites_shasum_check="{project}/shasum_files/xlsites_shasum_check.txt", + group_shasum_check="{project}/shasum_files/group_shasum_check.txt", shell: """ echo "Checking test files integrity and reproducibility." @@ -281,439 +303,15 @@ rule shasum_check: shasum --check {input.qc_shasum_file} 2>&1 | tee {output.qc_shasum_check} shasum --check {input.genome_shasum_file} 2>&1 | tee {output.genome_shasum_check} shasum --check {input.mapped_reads_shasum_file} 2>&1 | tee {output.mapped_reads_shasum_check} + shasum --check {input.xlsites_shasum_file} 2>&1 | tee {output.xlsites_shasum_check} + shasum --check {input.group_shasum_file} 2>&1 | tee {output.group_shasum_check} """ -#==============================================================================# -# Create crosslink sites, annotate and obtain summaries -#==============================================================================# - - -rule xlsites: - input: - "{project}/mapped/{barcode}/Aligned.sortedByCoord.out.bam" - output: - unique_bed="{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", - multimapped_bed="{project}/xlsites/{barcode}/{barcode}.multimapped.xl.bed", - skipped_bam="{project}/xlsites/{barcode}/{barcode}.skipped.xl.bam", - benchmark: - "{project}/benchmarks/{barcode}.xlsites.benchmark.tab" - # repeat("benchmarks/{barcode}.xlsites.benchmark.tab", 3) - params: - group_by=config['group_by'], - quant = config['quant'], - mismatches = config['mismatches'], - mapq_th = config['mapq_th'], - multimax = config['multimax'], - gap_th = config['gap_th'], - ratio_th = config['ratio_th'], - max_barcodes = config['max_barcodes'], - log: - "{project}/logs/xlsites/{barcode}.xlsites.log" - shell: - """ - iCount xlsites --group_by {params.group_by} --quant {params.quant} -mis {params.mismatches} --mapq_th {params.mapq_th} \ - --multimax {params.multimax} --gap_th {params.gap_th} --ratio_th {params.ratio_th} --max_barcodes {params.max_barcodes} \ - {input} {output.unique_bed} {output.multimapped_bed} {output.skipped_bam} -M {log} - - """ - -def bedgraph_description(wildcards): - return ("{project}_{sample_name}_{protein}_{method}_{mapto}".format(project=config['project'], sample_name=samples.loc[wildcards.barcode, "sample_name"], mapto=samples.loc[wildcards.barcode, "mapto"], method=samples.loc[wildcards.barcode, "method"], protein=samples.loc[wildcards.barcode, "protein"], cells_tissue=samples.loc[wildcards.barcode, "cells_tissue"], condition=samples.loc[wildcards.barcode, "condition"],)) - - - -rule bedgraph: - input: - xlsites="{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", - output: - bedgraph="{project}/xlsites/{barcode}/{barcode}.unique.xl.bedgraph", - params: - name=bedgraph_description, - description=bedgraph_description, - visibility="full", - priority="20", - color="120,101,172", - alt_color="200,120,59", - max_height_pixels="100:50:0", - run: - shell("iCount bedgraph --name \"{params.name}.unique.xl.bedgraph\" --description \"{params.description}\" --visibility \"{params.visibility}\" --priority \"{params.priority}\" --color \"{params.color}\" --alt_color \"{params.alt_color}\" --max_height_pixels \"{params.max_height_pixels}\" {input.xlsites} {output.bedgraph}") - - -rule annotate_xlsites: - input: - xlsites = "{project}/xlsites/{barcode}/{barcode}.unique.xl.bed" - output: - biotype="{project}/xlsites/{barcode}/{barcode}.unique.xl.annotated_sites_biotype.tab", - gene_id="{project}/xlsites/{barcode}/{barcode}.unique.xl.annotated_sites_gene_id.tab", - params: - templates_dir = get_templates_dir, - segment = get_segment_path, - #out_dir = "{project}/annotated/", - shell: - """ - iCount annotate --subtype biotype {params.segment} {input.xlsites} {output.biotype} - iCount annotate --subtype gene_id {params.segment} {input.xlsites} {output.gene_id} - """ - -rule summary: - input: - xlsites="{project}/xlsites/{barcode}/{barcode}.unique.xl.bed" - output: - gene="{project}/xlsites/{barcode}/{barcode}.unique.xl.summary_gene.tsv", - type="{project}/xlsites/{barcode}/{barcode}.unique.xl.summary_type.tsv", - subtype="{project}/xlsites/{barcode}/{barcode}.unique.xl.summary_subtype.tsv" - params: - templates_dir=get_templates_dir, - segment = get_segment_path, - out_dir="{project}/xlsites/{barcode}/", - rename_gene="{project}/xlsites/{barcode}/summary_gene.tsv", - rename_type="{project}/xlsites/{barcode}/summary_type.tsv", - rename_subtype="{project}/xlsites/{barcode}/summary_subtype.tsv", - shell: - """ - iCount summary --templates_dir {params.templates_dir} {params.segment} {input.xlsites} {params.out_dir} - mv {params.rename_gene} {output.gene} - mv {params.rename_type} {output.type} - mv {params.rename_subtype} {output.subtype} - """ - - -def get_xlsites_landmark_path(wildcards): - return ("{0}/{1}/segment/landmarks.bed.gz".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) - - -rule RNAmaps: - input: - xlsites="{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", - landmarks=get_xlsites_landmark_path, - output: - directory("{project}/xlsites/{barcode}/rnamaps/") - params: - plot_type=config['plot_type'], - top_n=config['top_n'], - smoothing=config['smoothing'], - nbins=config['nbins'], - binsize=config['binsize'], - colormap=config['colormap'], - imgfmt=config['imgfmt'], - shell: - """ - iCount rnamaps {input.xlsites} {input.landmarks} --outdir {output} --top_n {params.top_n} \ - --smoothing {params.smoothing} --colormap {params.colormap} --imgfmt {params.imgfmt} --nbins {params.nbins} - # --binsize {params.binsize} - """ - -#==============================================================================# -# Create significant crosslink sites, annotate and obtain summaries -#==============================================================================# - -rule sig_xlsites: - input: - xlsites="{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", - segment_file=get_segment_path - output: - sigxls="{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.bed", - scores="{project}/sig_xlsites/{barcode}/{barcode}.scores.tsv" - benchmark: - "{project}/benchmarks/{barcode}.sig_xlsites.benchmark.txt" - shell: - """ - iCount peaks {input.segment_file} {input.xlsites} {output.sigxls} --scores {output.scores} - """ - - -def is_empty(fname): - print(fname + " file is empty.") - return os.stat(str(fname)).st_size == 0 - - -# Create a new empty file. -def createNewFile(fname): - file_object = open(fname, 'w') - # file_object.write('File is created.') - print(fname + " has been created. ") -rule annotate_sig_xlsites: - input: - sig_xlsites = "{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.bed" - output: - biotype="{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.annotated.biotype.tab", - gene_id="{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.annotated.gene_id.tab" - params: - templates_dir = get_templates_dir, - segment = get_segment_path, - #out_dir = "{project}/sig_xlsites/{barcode}/", - run: - if is_empty(input.sig_xlsites): - print ("File", input.sig_xlsites, "is empty. Creating empty output files:", output.biotype, output.gene_id, \ - " to continue snakemake pipeline") - createNewFile(output.biotype) - createNewFile(output.gene_id) - else: - shell("iCount annotate --subtype biotype {params.segment} {input.sig_xlsites} {output.biotype}") - shell("iCount annotate --subtype gene_id {params.segment} {input.sig_xlsites} {output.gene_id}") - - -rule summary_sig: - input: - sig_xlsites = "{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.bed" - output: - gene = "{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.summary_gene.tsv", - type = "{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.summary_type.tsv", - subtype = "{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.summary_subtype.tsv", - params: - templates_dir = get_templates_dir, - segment = get_segment_path, - out_dir = "{project}/sig_xlsites/{barcode}/", - rename_gene = "{project}/sig_xlsites/{barcode}/summary_gene.tsv", - rename_type = "{project}/sig_xlsites/{barcode}/summary_type.tsv", - rename_subtype = "{project}/sig_xlsites/{barcode}/summary_subtype.tsv", - run: - if is_empty(input.sig_xlsites): - print ("File", input.sig_xlsites, "is empty. Creating empty output file:", output.gene, - " to continue snakemake pipeline") - createNewFile(output.gene) - createNewFile(output.type) - createNewFile(output.subtype) - else: - shell("iCount summary --templates_dir {params.templates_dir} {params.segment} {input.sig_xlsites} {params.out_dir}") - shell("mv {params.rename_gene} {output.gene}") - shell("mv {params.rename_type} {output.type}") - shell("mv {params.rename_subtype} {output.subtype}") - - -#==============================================================================# -# Create clusters -#==============================================================================# - -rule clusters: - input: - xlsites="{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", - sig_xlsites="{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.bed", - output: - clusters="{project}/clusters/{barcode}/{barcode}.clusters.bed", - benchmark: - "{project}/benchmarks/{barcode}.clusters.benchmark.txt" - params: - distance=config['distance'], - slop=config['slop'], - run: - if is_empty(input.xlsites) or is_empty(input.sig_xlsites): - print ("File", input.sig_xlsites, "is empty. Creating empty output file:", output.clusters, \ - " to continue snakemake pipeline") - createNewFile(output.clusters) - else: - shell("iCount clusters --dist {params.distance} --slop {params.slop} {input.xlsites} {input.sig_xlsites} \ - {output.clusters}") - - - - -#==============================================================================# -# Group analysis -#==============================================================================# - -def files2group(wildcards): - barcode_group = samples.loc[samples['group'] == wildcards.group, "full_barcode"].values[0:].tolist() - xlsites_list=[] - for barcode in barcode_group: - xlsites_list.append("{project}/xlsites/{barcode}/{barcode}.unique.xl.bed".format(project=config['project'], barcode=barcode)) - return (xlsites_list) - - -rule group: - input: - files2group, - output: - group="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", - benchmark: - "{project}/benchmarks/{group}.group.unique.xl.benchmark.txt" - shell: - """ - iCount group {output.group} {input} - """ - -def bedgraph_group_description(wildcards): - return ("{project}_group_{group}_{protein}_{method}_{mapto}".format(project=config['project'], group=wildcards.group, mapto=samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0], method=samples.loc[samples['group'] == wildcards.group, "method"].unique()[0], protein=samples.loc[samples['group'] == wildcards.group, "protein"].unique()[0], cells_tissue=samples.loc[samples['group'] == wildcards.group, "cells_tissue"].unique()[0], condition=samples.loc[samples['group'] == wildcards.group, "condition"].unique()[0])) - - - -rule group_bedgraph: - input: - group_xlsites="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", - output: - group_bedgraph="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bedgraph", - params: - name=bedgraph_group_description, - description=bedgraph_group_description, - visibility="full", - priority="20", - color="120,101,172", - alt_color="200,120,59", - max_height_pixels="100:50:0", - run: - shell("iCount bedgraph --name \"{params.name}.group.unique.xl.bedgraph\" --description \"{params.description}\" --visibility \"{params.visibility}\" --priority \"{params.priority}\" --color \"{params.color}\" --alt_color \"{params.alt_color}\" --max_height_pixels \"{params.max_height_pixels}\" {input.group_xlsites} {output.group_bedgraph}") - - - -def get_group_segment_path(wildcards): - return ("{0}/{1}/segment/{1}_segment.gtf".format(config['genomes_path'], samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0])) - -def get_group_templates_dir(wildcards): - return ("{0}/{1}/segment/".format(config['genomes_path'], samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0])) - -rule annotate_group_xlsites: - input: - group_xlsites="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed" - output: - group_biotype="{project}/groups/{group}/xlsites/{group}.group.unique.xl.annotated_group_biotype.tab", - group_gene_id="{project}/groups/{group}/xlsites/{group}.group.unique.xl.annotated_group_gene_id.tab", - params: - templates_dir = get_group_templates_dir, - segment = get_group_segment_path, - #out_dir = "{project}/annotated/", - shell: - """ - iCount annotate --subtype biotype {params.segment} {input.group_xlsites} {output.group_biotype} - iCount annotate --subtype gene_id {params.segment} {input.group_xlsites} {output.group_gene_id} - """ - -rule summary_group: - input: - group_xlsites="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed" - output: - group_gene="{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_gene.tsv", - group_type="{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_type.tsv", - group_subtype="{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_subtype.tsv" - params: - templates_dir=get_group_templates_dir, - segment = get_group_segment_path, - out_dir="{project}/groups/{group}/xlsites/", - group_rename_gene="{project}/groups/{group}/xlsites/summary_gene.tsv", - group_rename_type="{project}/groups/{group}/xlsites/summary_type.tsv", - group_rename_subtype="{project}/groups/{group}/xlsites/summary_subtype.tsv", - shell: - """ - iCount summary --templates_dir {params.templates_dir} {params.segment} {input.group_xlsites} {params.out_dir} - mv {params.group_rename_gene} {output.group_gene} - mv {params.group_rename_type} {output.group_type} - mv {params.group_rename_subtype} {output.group_subtype} - """ - -def get_group_segment_path(wildcards): - return ("{0}/{1}/segment/{1}_segment.gtf".format(config['genomes_path'], samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0])) - -rule group_sig_xlsites: - input: - group_xlsites = "{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", - segment_file=get_group_segment_path, - output: - group_sigxls="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.bed", - group_scores="{project}/groups/{group}/sig_xlsites/{group}.group.scores.tsv" - benchmark: - "{project}/benchmarks/{group}.group.sig_xlsites.benchmark.txt" - shell: - """ - iCount peaks {input.segment_file} {input.group_xlsites} {output.group_sigxls} --scores {output.group_scores} - """ - -rule annotate_group_sig_xlsites: - input: - group_sig_xlsites="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.bed" - output: - group_biotype="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.biotype.tab", - group_gene_id="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.gene_id.tab" - params: - templates_dir = get_group_templates_dir, - segment = get_group_segment_path, - #out_dir = "{project}/sig_xlsites/{barcode}/", - run: - if is_empty(input.group_sig_xlsites): - print ("File", input.group_sig_xlsites, "is empty. Creating empty output files:", output.group_biotype, \ - output.group_gene_id, " to continue snakemake pipeline") - createNewFile(output.group_biotype) - createNewFile(output.group_gene_id) - else: - shell("iCount annotate --subtype biotype {params.segment} {input.group_sig_xlsites} {output.group_biotype}") - shell("iCount annotate --subtype gene_id {params.segment} {input.group_sig_xlsites} {output.group_gene_id}") - - -rule summary_group_sig: - input: - group_sig_xlsites="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.bed" - output: - group_gene="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_gene.tsv", - group_type="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_type.tsv", - group_subtype="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_subtype.tsv", - params: - templates_dir = get_group_templates_dir, - segment = get_group_segment_path, - out_dir = "{project}/groups/{group}/sig_xlsites/", - group_rename_gene = "{project}/groups/{group}/sig_xlsites/summary_gene.tsv", - group_rename_type = "{project}/groups/{group}/sig_xlsites/summary_type.tsv", - group_rename_subtype = "{project}/groups/{group}/sig_xlsites/summary_subtype.tsv", - run: - if is_empty(input.group_sig_xlsites): - print ("File", input.group_sig_xlsites, "is empty. Creating empty output file:", output.group_gene, \ - output.group_type , output.group_subtype, " to continue snakemake pipeline") - createNewFile(output.group_gene) - createNewFile(output.group_type) - createNewFile(output.group_subtype) - else: - shell("iCount summary --templates_dir {params.templates_dir} {params.segment} {input.sig_xlsites} {params.out_dir}") - shell("mv {params.group_rename_gene} {output.group_gene}") - shell("mv {params.group_rename_type} {output.group_type}") - shell("mv {params.group_rename_subtype} {output.group_subtype}") - - -def get_group_landmark_path(wildcards): - return ("{0}/{1}/segment/landmarks.bed.gz".format(config['genomes_path'], samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0])) - -rule group_clusters: - input: - group_xlsites="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", - group_sigxls="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.bed", - output: - group_clusters="{project}/groups/{group}/clusters/{group}.group.clusters.bed", - benchmark: - "{project}/benchmarks/{group}.group.clusters.benchmark.txt" - params: - distance=config['distance'], - slop=config['slop'], - run: - if is_empty(input.group_xlsites) or is_empty(input.group_sigxls): - print ("File", input.group_sigxls, "is empty. Creating empty output file:", output.group_clusters, \ - " to continue snakemake pipeline") - createNewFile(output.group_clusters) - else: - shell("iCount clusters --dist {params.distance} --slop {params.slop} {input.group_xlsites} \ - {input.group_sigxls} {output.group_clusters}") - - -rule RNAmaps_group: - input: - group_xlsites="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", - landmarks=get_group_landmark_path, - output: - directory("{project}/groups/{group}/rnamaps/") - params: - plot_type=config['plot_type'], - top_n=config['top_n'], - smoothing=config['smoothing'], - nbins=config['nbins'], - binsize=config['binsize'], - colormap=config['colormap'], - imgfmt=config['imgfmt'], - shell: - """ - iCount rnamaps {input.group_xlsites} {input.landmarks} --outdir {output} --top_n {params.top_n} --smoothing \ - {params.smoothing} --colormap {params.colormap} --imgfmt {params.imgfmt} --nbins {params.nbins} - # --binsize {params.binsize} - """ #==============================================================================# diff --git a/iCount/snakemake/rules/clusters.smk b/iCount/snakemake/rules/clusters.smk new file mode 100644 index 0000000..0788b00 --- /dev/null +++ b/iCount/snakemake/rules/clusters.smk @@ -0,0 +1,25 @@ +#==============================================================================# +# Create clusters +#==============================================================================# + +rule clusters: + input: + xlsites="{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", + sig_xlsites="{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.bed", + output: + clusters="{project}/clusters/{barcode}/{barcode}.clusters.bed", + benchmark: + "{project}/benchmarks/{barcode}.clusters.benchmark.txt" + params: + distance=config['distance'], + slop=config['slop'], + run: + if is_empty(input.xlsites) or is_empty(input.sig_xlsites): + print ("File", input.sig_xlsites, "is empty. Creating empty output file:", output.clusters, \ + " to continue snakemake pipeline") + createNewFile(output.clusters) + else: + shell("iCount clusters --dist {params.distance} --slop {params.slop} {input.xlsites} {input.sig_xlsites} \ + {output.clusters}") + + diff --git a/iCount/snakemake/rules/common.smk b/iCount/snakemake/rules/common.smk index 2de7355..9d4f73c 100644 --- a/iCount/snakemake/rules/common.smk +++ b/iCount/snakemake/rules/common.smk @@ -2,14 +2,136 @@ logdir = os.path.join(os.getcwd(), config["logdir"]) os.makedirs(logdir, exist_ok=True) -# Print project -print("Procesing project:", config['project'], "\n") - # this container defines the underlying OS for each job when using the workflow # with --use-conda --use-singularity container: "docker://continuumio/miniconda3" + +##### Completeness Options ##### +def all_xlsites(wilcards): + xlsites_output = list() + if config["completeness_output"] == "minimum": + # xlsites_output.extend(expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", project=config['project'], barcode=samples.index)) + # xlsites_output.extend(expand("{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.bed", project=config['project'], barcode=samples.index)) + xlsites_output.extend(expand("{project}/clusters/{barcode}/{barcode}.clusters.bed", project=config['project'], + barcode=samples.index)) + elif config["completeness_output"] == "complete": + # xlsites_output.extend(expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", project=config['project'], barcode=samples.index)) + xlsites_output.extend( + expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.summary_gene.tsv", project=config['project'], + barcode=samples.index)) + xlsites_output.extend(expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.annotated_sites_biotype.tab", + project=config['project'], barcode=samples.index)) + xlsites_output.extend(expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.annotated_sites_gene_id.tab", + project=config['project'], barcode=samples.index)) + xlsites_output.extend( + expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.bedgraph", project=config['project'], + barcode=samples.index)) + xlsites_output.extend( + expand("{project}/xlsites/{barcode}/rnamaps/", project=config['project'], barcode=samples.index)) + # xlsites_output.extend(expand("{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.bed", project=config['project'], barcode=samples.index)) + xlsites_output.extend( + expand("{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.summary_gene.tsv", project=config['project'], + barcode=samples.index)) + xlsites_output.extend(expand("{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.annotated.biotype.tab", + project=config['project'], barcode=samples.index)) + xlsites_output.extend(expand("{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.annotated.gene_id.tab", + project=config['project'], barcode=samples.index)) + xlsites_output.extend(expand("{project}/clusters/{barcode}/{barcode}.clusters.bed", project=config['project'], + barcode=samples.index)) + else: + sys.exit("completeness_output option in configfile needs to be set as minimum or complete ") + + return xlsites_output + + +def all_group(wilcards): + group_output = list() + if config["group_completeness_output"] == "minimum": + group_output.extend( + expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", project=config['project'], + group=samples["group"].dropna().unique())) + group_output.extend( + expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.bed", project=config['project'], + group=samples["group"].dropna().unique())) + group_output.extend( + expand("{project}/groups/{group}/clusters/{group}.group.clusters.bed", project=config['project'], + group=samples["group"].dropna().unique()), ) + elif config["group_completeness_output"] == "complete": + group_output.extend( + expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", project=config['project'], + group=samples["group"].dropna().unique())) + group_output.extend( + expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.annotated_group_biotype.tab", + project=config['project'], group=samples["group"].dropna().unique())) + group_output.extend( + expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.annotated_group_gene_id.tab", + project=config['project'], group=samples["group"].dropna().unique())) + group_output.extend(expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_gene.tsv", + project=config['project'], group=samples["group"].dropna().unique())) + # group_output.extend(expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_type.tsv", project=config['project'], group=samples["group"].dropna().unique())) + # group_output.extend(expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_subtype.tsv", project=config['project'], group=samples["group"].dropna().unique())) + group_output.extend( + expand("{project}/groups/{group}/xlsites/{group}.group.unique.xl.bedgraph", project=config['project'], + group=samples["group"].dropna().unique()), ) + group_output.extend(expand("{project}/groups/{group}/rnamaps/", project=config['project'], + group=samples["group"].dropna().unique())) + + group_output.extend( + expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.bed", project=config['project'], + group=samples["group"].dropna().unique())) + group_output.extend(expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.biotype.tab", + project=config['project'], group=samples["group"].dropna().unique())) + group_output.extend(expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.gene_id.tab", + project=config['project'], group=samples["group"].dropna().unique())) + group_output.extend(expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_gene.tsv", + project=config['project'], group=samples["group"].dropna().unique())) + group_output.extend(expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_type.tsv", + project=config['project'], group=samples["group"].dropna().unique())) + group_output.extend(expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_type.tsv", + project=config['project'], group=samples["group"].dropna().unique())) + group_output.extend(expand("{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_subtype.tsv", + project=config['project'], group=samples["group"].dropna().unique())) + + group_output.extend( + expand("{project}/groups/{group}/clusters/{group}.group.clusters.bed", project=config['project'], + group=samples["group"].dropna().unique()), ) + + else: + sys.exit("group_completeness_output option in configfile needs to be set as minimum or complete ") + + return group_output + +def all_input(wildcards): + """ + Function defining all requested inputs for the rule all. + """ + final_output = [] + + if config["bedgraph_UCSC"]: + final_output.extend(expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.UCSC.bedgraph", project=config['project'], barcode=samples.index)) + + if config["create_integrity_test"]: + final_output.extend(expand("{project}/shasum_files/demultiplex_shasum_file.txt", project=config['project'])) + final_output.extend(expand("{project}/shasum_files/qc_shasum_file.txt", project=config['project'])) + final_output.extend(expand("{project}/shasum_files/genome_shasum_file.txt", project=config['project'])) + final_output.extend(expand("{project}/shasum_files/mapped_reads_shasum_file.txt", project=config['project'])) + final_output.extend(expand("{project}/shasum_files/xlsites_shasum_file.txt", project=config['project'])) + final_output.extend(expand("{project}/shasum_files/group_shasum_file.txt", project=config['project'])) + + if config["integrity_test_check"]: + final_output.extend(expand("{project}/shasum_files/demultiplex_shasum_check.txt", project=config['project'])) + final_output.extend(expand("{project}/shasum_files/qc_shasum_check.txt", project=config['project'])) + final_output.extend(expand("{project}/shasum_files/genome_shasum_check.txt", project=config['project'])) + final_output.extend(expand("{project}/shasum_files/mapped_reads_shasum_check.txt", project=config['project'])) + final_output.extend(expand("{project}/shasum_files/xlsites_shasum_check.txt", project=config['project'])) + final_output.extend(expand("{project}/shasum_files/group_shasum_check.txt", project=config['project'])) + + print("ALL_OUTPUT_PRINT@@@@@@@@@@@@@@@@", final_output) + return final_output + + ##### Helper functions ##### def get_genome(wildcards): diff --git a/iCount/snakemake/rules/group.smk b/iCount/snakemake/rules/group.smk new file mode 100644 index 0000000..70dd445 --- /dev/null +++ b/iCount/snakemake/rules/group.smk @@ -0,0 +1,201 @@ +#==============================================================================# +# Group analysis +#==============================================================================# + +def files2group(wildcards): + barcode_group = samples.loc[samples['group'] == wildcards.group, "full_barcode"].values[0:].tolist() + xlsites_list=[] + for barcode in barcode_group: + xlsites_list.append("{project}/xlsites/{barcode}/{barcode}.unique.xl.bed".format(project=config['project'], barcode=barcode)) + return (xlsites_list) + + +rule group: + input: + files2group, + output: + group="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", + benchmark: + "{project}/benchmarks/{group}.group.unique.xl.benchmark.txt" + shell: + """ + iCount group {output.group} {input} + """ + +def bedgraph_group_description(wildcards): + return ("{project}_group_{group}_{protein}_{method}_{mapto}".format(project=config['project'], group=wildcards.group, mapto=samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0], method=samples.loc[samples['group'] == wildcards.group, "method"].unique()[0], protein=samples.loc[samples['group'] == wildcards.group, "protein"].unique()[0], cells_tissue=samples.loc[samples['group'] == wildcards.group, "cells_tissue"].unique()[0], condition=samples.loc[samples['group'] == wildcards.group, "condition"].unique()[0])) + + + +rule group_bedgraph: + input: + group_xlsites="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", + output: + group_bedgraph="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bedgraph", + params: + name=bedgraph_group_description, + description=bedgraph_group_description, + visibility="full", + priority="20", + color="120,101,172", + alt_color="200,120,59", + max_height_pixels="100:50:0", + run: + shell("iCount bedgraph --name \"{params.name}.group.unique.xl.bedgraph\" --description \"{params.description}\" --visibility \"{params.visibility}\" --priority \"{params.priority}\" --color \"{params.color}\" --alt_color \"{params.alt_color}\" --max_height_pixels \"{params.max_height_pixels}\" {input.group_xlsites} {output.group_bedgraph}") + + + +def get_group_segment_path(wildcards): + return ("{0}/{1}/segment/{1}_segment.gtf".format(config['genomes_path'], samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0])) + +def get_group_templates_dir(wildcards): + return ("{0}/{1}/segment/".format(config['genomes_path'], samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0])) + +rule annotate_group_xlsites: + input: + group_xlsites="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed" + output: + group_biotype="{project}/groups/{group}/xlsites/{group}.group.unique.xl.annotated_group_biotype.tab", + group_gene_id="{project}/groups/{group}/xlsites/{group}.group.unique.xl.annotated_group_gene_id.tab", + params: + templates_dir = get_group_templates_dir, + segment = get_group_segment_path, + #out_dir = "{project}/annotated/", + shell: + """ + iCount annotate --subtype biotype {params.segment} {input.group_xlsites} {output.group_biotype} + iCount annotate --subtype gene_id {params.segment} {input.group_xlsites} {output.group_gene_id} + """ + +rule summary_group: + input: + group_xlsites="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed" + output: + group_gene="{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_gene.tsv", + group_type="{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_type.tsv", + group_subtype="{project}/groups/{group}/xlsites/{group}.group.unique.xl.summary_subtype.tsv" + params: + templates_dir=get_group_templates_dir, + segment = get_group_segment_path, + out_dir="{project}/groups/{group}/xlsites/", + group_rename_gene="{project}/groups/{group}/xlsites/summary_gene.tsv", + group_rename_type="{project}/groups/{group}/xlsites/summary_type.tsv", + group_rename_subtype="{project}/groups/{group}/xlsites/summary_subtype.tsv", + shell: + """ + iCount summary --templates_dir {params.templates_dir} {params.segment} {input.group_xlsites} {params.out_dir} + mv {params.group_rename_gene} {output.group_gene} + mv {params.group_rename_type} {output.group_type} + mv {params.group_rename_subtype} {output.group_subtype} + """ + +def get_group_segment_path(wildcards): + return ("{0}/{1}/segment/{1}_segment.gtf".format(config['genomes_path'], samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0])) + +rule group_sig_xlsites: + input: + group_xlsites = "{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", + segment_file=get_group_segment_path, + output: + group_sigxls="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.bed", + group_scores="{project}/groups/{group}/sig_xlsites/{group}.group.scores.tsv" + benchmark: + "{project}/benchmarks/{group}.group.sig_xlsites.benchmark.txt" + shell: + """ + iCount peaks {input.segment_file} {input.group_xlsites} {output.group_sigxls} --scores {output.group_scores} + """ + +rule annotate_group_sig_xlsites: + input: + group_sig_xlsites="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.bed" + output: + group_biotype="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.biotype.tab", + group_gene_id="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.annotated.gene_id.tab" + params: + templates_dir = get_group_templates_dir, + segment = get_group_segment_path, + #out_dir = "{project}/sig_xlsites/{barcode}/", + run: + if is_empty(input.group_sig_xlsites): + print ("File", input.group_sig_xlsites, "is empty. Creating empty output files:", output.group_biotype, \ + output.group_gene_id, " to continue snakemake pipeline") + createNewFile(output.group_biotype) + createNewFile(output.group_gene_id) + else: + shell("iCount annotate --subtype biotype {params.segment} {input.group_sig_xlsites} {output.group_biotype}") + shell("iCount annotate --subtype gene_id {params.segment} {input.group_sig_xlsites} {output.group_gene_id}") + + +rule summary_group_sig: + input: + group_sig_xlsites="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.bed" + output: + group_gene="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_gene.tsv", + group_type="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_type.tsv", + group_subtype="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.summary_subtype.tsv", + params: + templates_dir = get_group_templates_dir, + segment = get_group_segment_path, + out_dir = "{project}/groups/{group}/sig_xlsites/", + group_rename_gene = "{project}/groups/{group}/sig_xlsites/summary_gene.tsv", + group_rename_type = "{project}/groups/{group}/sig_xlsites/summary_type.tsv", + group_rename_subtype = "{project}/groups/{group}/sig_xlsites/summary_subtype.tsv", + run: + if is_empty(input.group_sig_xlsites): + print ("File", input.group_sig_xlsites, "is empty. Creating empty output file:", output.group_gene, \ + output.group_type , output.group_subtype, " to continue snakemake pipeline") + createNewFile(output.group_gene) + createNewFile(output.group_type) + createNewFile(output.group_subtype) + else: + shell("iCount summary --templates_dir {params.templates_dir} {params.segment} {input.sig_xlsites} {params.out_dir}") + shell("mv {params.group_rename_gene} {output.group_gene}") + shell("mv {params.group_rename_type} {output.group_type}") + shell("mv {params.group_rename_subtype} {output.group_subtype}") + + +def get_group_landmark_path(wildcards): + return ("{0}/{1}/segment/landmarks.bed.gz".format(config['genomes_path'], samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0])) + +rule group_clusters: + input: + group_xlsites="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", + group_sigxls="{project}/groups/{group}/sig_xlsites/{group}.group.sig_sites.bed", + output: + group_clusters="{project}/groups/{group}/clusters/{group}.group.clusters.bed", + benchmark: + "{project}/benchmarks/{group}.group.clusters.benchmark.txt" + params: + distance=config['distance'], + slop=config['slop'], + run: + if is_empty(input.group_xlsites) or is_empty(input.group_sigxls): + print ("File", input.group_sigxls, "is empty. Creating empty output file:", output.group_clusters, \ + " to continue snakemake pipeline") + createNewFile(output.group_clusters) + else: + shell("iCount clusters --dist {params.distance} --slop {params.slop} {input.group_xlsites} \ + {input.group_sigxls} {output.group_clusters}") + + +rule RNAmaps_group: + input: + group_xlsites="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", + landmarks=get_group_landmark_path, + output: + directory("{project}/groups/{group}/rnamaps/") + params: + plot_type=config['plot_type'], + top_n=config['top_n'], + smoothing=config['smoothing'], + nbins=config['nbins'], + binsize=config['binsize'], + colormap=config['colormap'], + imgfmt=config['imgfmt'], + shell: + """ + iCount rnamaps {input.group_xlsites} {input.landmarks} --outdir {output} --top_n {params.top_n} --smoothing \ + {params.smoothing} --colormap {params.colormap} --imgfmt {params.imgfmt} --nbins {params.nbins} + # --binsize {params.binsize} + """ diff --git a/iCount/snakemake/rules/sig_xlsites.smk b/iCount/snakemake/rules/sig_xlsites.smk new file mode 100644 index 0000000..7b1dab7 --- /dev/null +++ b/iCount/snakemake/rules/sig_xlsites.smk @@ -0,0 +1,79 @@ +#==============================================================================# +# Create significant crosslink sites, annotate and obtain summaries +#==============================================================================# + +rule sig_xlsites: + input: + xlsites="{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", + segment_file=get_segment_path + output: + sigxls="{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.bed", + scores="{project}/sig_xlsites/{barcode}/{barcode}.scores.tsv" + benchmark: + "{project}/benchmarks/{barcode}.sig_xlsites.benchmark.txt" + shell: + """ + iCount peaks {input.segment_file} {input.xlsites} {output.sigxls} --scores {output.scores} + """ + + +def is_empty(fname): + print(fname + " file is empty.") + return os.stat(str(fname)).st_size == 0 + + +# Create a new empty file. +def createNewFile(fname): + file_object = open(fname, 'w') + # file_object.write('File is created.') + print(fname + " has been created. ") + + +rule annotate_sig_xlsites: + input: + sig_xlsites = "{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.bed" + output: + biotype="{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.annotated.biotype.tab", + gene_id="{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.annotated.gene_id.tab" + params: + templates_dir = get_templates_dir, + segment = get_segment_path, + #out_dir = "{project}/sig_xlsites/{barcode}/", + run: + if is_empty(input.sig_xlsites): + print ("File", input.sig_xlsites, "is empty. Creating empty output files:", output.biotype, output.gene_id, \ + " to continue snakemake pipeline") + createNewFile(output.biotype) + createNewFile(output.gene_id) + else: + shell("iCount annotate --subtype biotype {params.segment} {input.sig_xlsites} {output.biotype}") + shell("iCount annotate --subtype gene_id {params.segment} {input.sig_xlsites} {output.gene_id}") + + +rule summary_sig: + input: + sig_xlsites = "{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.bed" + output: + gene = "{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.summary_gene.tsv", + type = "{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.summary_type.tsv", + subtype = "{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.summary_subtype.tsv", + params: + templates_dir = get_templates_dir, + segment = get_segment_path, + out_dir = "{project}/sig_xlsites/{barcode}/", + rename_gene = "{project}/sig_xlsites/{barcode}/summary_gene.tsv", + rename_type = "{project}/sig_xlsites/{barcode}/summary_type.tsv", + rename_subtype = "{project}/sig_xlsites/{barcode}/summary_subtype.tsv", + run: + if is_empty(input.sig_xlsites): + print ("File", input.sig_xlsites, "is empty. Creating empty output file:", output.gene, + " to continue snakemake pipeline") + createNewFile(output.gene) + createNewFile(output.type) + createNewFile(output.subtype) + else: + shell("iCount summary --templates_dir {params.templates_dir} {params.segment} {input.sig_xlsites} {params.out_dir}") + shell("mv {params.rename_gene} {output.gene}") + shell("mv {params.rename_type} {output.type}") + shell("mv {params.rename_subtype} {output.subtype}") + diff --git a/iCount/snakemake/rules/xlsites.smk b/iCount/snakemake/rules/xlsites.smk new file mode 100644 index 0000000..5c2595a --- /dev/null +++ b/iCount/snakemake/rules/xlsites.smk @@ -0,0 +1,119 @@ +#==============================================================================# +# Create crosslink sites, annotate and obtain summaries +#==============================================================================# + + +rule xlsites: + input: + "{project}/mapped/{barcode}/Aligned.sortedByCoord.out.bam" + output: + unique_bed="{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", + multimapped_bed="{project}/xlsites/{barcode}/{barcode}.multimapped.xl.bed", + skipped_bam="{project}/xlsites/{barcode}/{barcode}.skipped.xl.bam", + benchmark: + "{project}/benchmarks/{barcode}.xlsites.benchmark.tab" + # repeat("benchmarks/{barcode}.xlsites.benchmark.tab", 3) + params: + group_by=config['group_by'], + quant = config['quant'], + mismatches = config['mismatches'], + mapq_th = config['mapq_th'], + multimax = config['multimax'], + gap_th = config['gap_th'], + ratio_th = config['ratio_th'], + max_barcodes = config['max_barcodes'], + log: + "{project}/logs/xlsites/{barcode}.xlsites.log" + shell: + """ + iCount xlsites --group_by {params.group_by} --quant {params.quant} -mis {params.mismatches} --mapq_th {params.mapq_th} \ + --multimax {params.multimax} --gap_th {params.gap_th} --ratio_th {params.ratio_th} --max_barcodes {params.max_barcodes} \ + {input} {output.unique_bed} {output.multimapped_bed} {output.skipped_bam} -M {log} + + """ + +def bedgraph_description(wildcards): + return ("{project}_{sample_name}_{protein}_{method}_{mapto}".format(project=config['project'], sample_name=samples.loc[wildcards.barcode, "sample_name"], mapto=samples.loc[wildcards.barcode, "mapto"], method=samples.loc[wildcards.barcode, "method"], protein=samples.loc[wildcards.barcode, "protein"], cells_tissue=samples.loc[wildcards.barcode, "cells_tissue"], condition=samples.loc[wildcards.barcode, "condition"],)) + + + +rule bedgraph: + input: + xlsites="{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", + output: + bedgraph="{project}/xlsites/{barcode}/{barcode}.unique.xl.bedgraph", + params: + name=bedgraph_description, + description=bedgraph_description, + visibility="full", + priority="20", + color="120,101,172", + alt_color="200,120,59", + max_height_pixels="100:50:0", + run: + shell("iCount bedgraph --name \"{params.name}.unique.xl.bedgraph\" --description \"{params.description}\" --visibility \"{params.visibility}\" --priority \"{params.priority}\" --color \"{params.color}\" --alt_color \"{params.alt_color}\" --max_height_pixels \"{params.max_height_pixels}\" {input.xlsites} {output.bedgraph}") + + +rule annotate_xlsites: + input: + xlsites = "{project}/xlsites/{barcode}/{barcode}.unique.xl.bed" + output: + biotype="{project}/xlsites/{barcode}/{barcode}.unique.xl.annotated_sites_biotype.tab", + gene_id="{project}/xlsites/{barcode}/{barcode}.unique.xl.annotated_sites_gene_id.tab", + params: + templates_dir = get_templates_dir, + segment = get_segment_path, + #out_dir = "{project}/annotated/", + shell: + """ + iCount annotate --subtype biotype {params.segment} {input.xlsites} {output.biotype} + iCount annotate --subtype gene_id {params.segment} {input.xlsites} {output.gene_id} + """ + +rule summary: + input: + xlsites="{project}/xlsites/{barcode}/{barcode}.unique.xl.bed" + output: + gene="{project}/xlsites/{barcode}/{barcode}.unique.xl.summary_gene.tsv", + type="{project}/xlsites/{barcode}/{barcode}.unique.xl.summary_type.tsv", + subtype="{project}/xlsites/{barcode}/{barcode}.unique.xl.summary_subtype.tsv" + params: + templates_dir=get_templates_dir, + segment = get_segment_path, + out_dir="{project}/xlsites/{barcode}/", + rename_gene="{project}/xlsites/{barcode}/summary_gene.tsv", + rename_type="{project}/xlsites/{barcode}/summary_type.tsv", + rename_subtype="{project}/xlsites/{barcode}/summary_subtype.tsv", + shell: + """ + iCount summary --templates_dir {params.templates_dir} {params.segment} {input.xlsites} {params.out_dir} + mv {params.rename_gene} {output.gene} + mv {params.rename_type} {output.type} + mv {params.rename_subtype} {output.subtype} + """ + + +def get_xlsites_landmark_path(wildcards): + return ("{0}/{1}/segment/landmarks.bed.gz".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) + + +rule RNAmaps: + input: + xlsites="{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", + landmarks=get_xlsites_landmark_path, + output: + directory("{project}/xlsites/{barcode}/rnamaps/") + params: + plot_type=config['plot_type'], + top_n=config['top_n'], + smoothing=config['smoothing'], + nbins=config['nbins'], + binsize=config['binsize'], + colormap=config['colormap'], + imgfmt=config['imgfmt'], + shell: + """ + iCount rnamaps {input.xlsites} {input.landmarks} --outdir {output} --top_n {params.top_n} \ + --smoothing {params.smoothing} --colormap {params.colormap} --imgfmt {params.imgfmt} --nbins {params.nbins} + # --binsize {params.binsize} + """ \ No newline at end of file From 8f65c2d2c9a8cd28ae9c918d7813d5c8ed59f43c Mon Sep 17 00:00:00 2001 From: Faitero Date: Tue, 13 Oct 2020 14:06:12 +0200 Subject: [PATCH 05/26] finish sha test and split rules --- iCount/examples/config_synthetic.yaml | 15 ++++++++------- iCount/snakemake/icount_snakemake.smk | 4 ---- iCount/snakemake/rules/common.smk | 1 - 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/iCount/examples/config_synthetic.yaml b/iCount/examples/config_synthetic.yaml index 8b21566..f8ed887 100755 --- a/iCount/examples/config_synthetic.yaml +++ b/iCount/examples/config_synthetic.yaml @@ -60,8 +60,8 @@ bedgraph_UCSC: false #~~~~~~~~~~~~~~~~ iCount genomes ~~~~~~~~~~~~~~~~# -## iCount genomes will be automatically downloaded from NCBI if any of the following -# accepted species terms are used. There are 87 species available: +## iCount genomes will be automatically downloaded from NCBI if any of the following accepted species terms are used. +# There are 87 species available: # ailuropoda_melanoleuca,anas_platyrhynchos,ancestral_alleles,anolis_carolinensis,astyanax_mexicanus, # bos_taurus,caenorhabditis_elegans,callithrix_jacchus,canis_familiaris,cavia_porcellus,chlorocebus_sabaeus, # choloepus_hoffmanni,ciona_intestinalis,ciona_savignyi,danio_rerio,dasypus_novemcinctus,dipodomys_ordii, @@ -72,10 +72,11 @@ bedgraph_UCSC: false # mus_musculus_aj,mus_musculus_akrj,mus_musculus_balbcj,mus_musculus_c3hhej,mus_musculus_c57bl6nj,mus_musculus_casteij, # mus_musculus_cbaj,mus_musculus_dba2j,mus_musculus_fvbnj,mus_musculus_lpj,mus_musculus_nodshiltj,mus_musculus_nzohlltj, # mus_musculus_pwkphj,mus_musculus_wsbeij,mus_spretus_spreteij,mustela_putorius_furo,myotis_lucifugus,nomascus_leucogenys, -# ochotona_princeps,oreochromis_niloticus,ornithorhynchus_anatinus,oryctolagus_cuniculus,oryzias_latipes,otolemur_garnettii, -# ovis_aries,pan_troglodytes,papio_anubis,pelodiscus_sinensis,petromyzon_marinus,poecilia_formosa,pongo_abelii,procavia_capensis, -# pteropus_vampyrus,rattus_norvegicus,saccharomyces_cerevisiae,sarcophilus_harrisii,sorex_araneus,sus_scrofa,taeniopygia_guttata, -# takifugu_rubripes,tarsius_syrichta,tetraodon_nigroviridis,tupaia_belangeri,tursiops_truncatus,vicugna_pacos,xenopus_tropicalis,xiphophorus_maculatus +# ochotona_princeps,oreochromis_niloticus,ornithorhynchus_anatinus,oryctolagus_cuniculus,oryzias_latipes, +# ovis_aries,pan_troglodytes,papio_anubis,pelodiscus_sinensis,petromyzon_marinus,poecilia_formosa,pongo_abelii, +# procavia_capensis,pteropus_vampyrus,rattus_norvegicus,saccharomyces_cerevisiae,sarcophilus_harrisii,sorex_araneus, +# sus_scrofa,taeniopygia_guttata,takifugu_rubripes,tarsius_syrichta,tetraodon_nigroviridis,tupaia_belangeri, +# otolemur_garnettii, tursiops_truncatus,vicugna_pacos,xenopus_tropicalis,xiphophorus_maculatus # Update releases script and Icount annotation and genome doesn't accept "--releases" word # Current version of iCount is tested to work with human and mouse genomes only. @@ -243,7 +244,7 @@ colormap: "Greys" imgfmt: "png" -#======================== Computing Cluster =============================# +#======================== Computing Cluster =============================# # Log directory to store invidual jobs output and error when run on a cluster logdir: "logs_cluster" diff --git a/iCount/snakemake/icount_snakemake.smk b/iCount/snakemake/icount_snakemake.smk index 96d0cb4..a58303d 100644 --- a/iCount/snakemake/icount_snakemake.smk +++ b/iCount/snakemake/icount_snakemake.smk @@ -310,10 +310,6 @@ rule shasum_check: - - - - #==============================================================================# # Kmers #==============================================================================# diff --git a/iCount/snakemake/rules/common.smk b/iCount/snakemake/rules/common.smk index 9d4f73c..6c56e11 100644 --- a/iCount/snakemake/rules/common.smk +++ b/iCount/snakemake/rules/common.smk @@ -128,7 +128,6 @@ def all_input(wildcards): final_output.extend(expand("{project}/shasum_files/xlsites_shasum_check.txt", project=config['project'])) final_output.extend(expand("{project}/shasum_files/group_shasum_check.txt", project=config['project'])) - print("ALL_OUTPUT_PRINT@@@@@@@@@@@@@@@@", final_output) return final_output From 4243d4a99fbc41c7e4dca2d3b2dad7a0f8b71e23 Mon Sep 17 00:00:00 2001 From: Faitero Date: Thu, 29 Oct 2020 18:11:42 +0100 Subject: [PATCH 06/26] Solve sigxlsites issues and add new synthetic reads --- .../merge_chr20_2652426_2664336_GTAAC.fq.gz | Bin 0 -> 115523 bytes .../examples/data/samples_synthetic_chr20.txt | 4 + iCount/snakemake/icount_snakemake.smk | 41 ++- iCount/snakemake/rules/common.smk | 8 +- iCount/snakemake/rules/demultiplex.smk | 19 +- iCount/snakemake/rules/map_reads.smk | 6 +- iCount/snakemake/rules/prepare_genome.smk | 2 +- iCount/snakemake/rules/qc.smk | 34 +-- iCount/snakemake/rules/sig_xlsites.smk | 18 +- iCount/snakemake/rules/xlsites.smk | 4 +- iCount/snakemake/schemas/config.schema.yaml | 40 +++ .../synthetic_reads/synthetic_iCLIP_reads.py | 238 ++++++++++++++++++ 12 files changed, 378 insertions(+), 36 deletions(-) create mode 100644 iCount/examples/data/merge_chr20_2652426_2664336_GTAAC.fq.gz create mode 100644 iCount/examples/data/samples_synthetic_chr20.txt create mode 100644 iCount/tests/functional/synthetic_reads/synthetic_iCLIP_reads.py diff --git a/iCount/examples/data/merge_chr20_2652426_2664336_GTAAC.fq.gz b/iCount/examples/data/merge_chr20_2652426_2664336_GTAAC.fq.gz new file mode 100644 index 0000000000000000000000000000000000000000..0e347b8719408b0b78ff1795d881b888d87b3414 GIT binary patch literal 115523 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

P***a`sw5_ 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

Rip&p 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 z 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 Y!EoX70)S=gzgp1ze^Ah9lybQR05WYu3jhEB literal 0 HcmV?d00001 diff --git a/iCount/examples/data/samples_synthetic_chr20.txt b/iCount/examples/data/samples_synthetic_chr20.txt new file mode 100644 index 0000000..e6d752e --- /dev/null +++ b/iCount/examples/data/samples_synthetic_chr20.txt @@ -0,0 +1,4 @@ +sample_name method protein cells_tissue condition mapto barcode_5 barcode_3 adapter_3 linker comments group New +synthetic_1 iCLIP synthetic HEK293 synthetic_1 homo_sapiens NNNNGTAACNNN NNATT AGATCGGAAGAGCGGTTCAG L3-NNATT L3-NNATT WildType +synthetic_2 iCLIP synthetic HEK293 synthetic_2 homo_sapiens NNNNGTAACNNN NNAGG AGATCGGAAGAGCGGTTCAG L3-NNAGG L3-NNAGG WildType +synthetic_3 iCLIP synthetic HEK293 synthetic_3 homo_sapiens NNNNGTAACNNN NNTTA AGATCGGAAGAGCGGTTCAG L3-NNTTA L3-NNTTA WildType \ No newline at end of file diff --git a/iCount/snakemake/icount_snakemake.smk b/iCount/snakemake/icount_snakemake.smk index a58303d..d04a949 100644 --- a/iCount/snakemake/icount_snakemake.smk +++ b/iCount/snakemake/icount_snakemake.smk @@ -19,7 +19,7 @@ # Step one: Activate conda environment with Snakemake, iCount and dependencies installed # Create new environment # conda env create --name iCount_pipeline2 --file envs/environment_iCount.yaml -# conda activate iCount_pipeline2 +# conda activate iCount_pipeline3 # pip install ./iCount/ # Check the install # iCount @@ -106,17 +106,19 @@ validate(samples, schema="schemas/samples.schema.yaml") if len(samples["adapter_3"].unique().tolist()) > 1: sys.exit("iCount pipeline only accepts a unique 3' adapter") -if len(samples.index) != len(samples["sample_name"].unique().tolist()): +elif len(samples.index) != len(samples["sample_name"].unique().tolist()): sys.exit("iCount pipeline only accepts a unique sample names") # Merge 5'barcode and 3'barcode to create a table index (full barcode) -cols = ['barcode_5', 'barcode_3'] -samples["full_barcode"] = samples[cols].apply(lambda x: '_'.join(x.dropna()), axis=1) -samples=samples.set_index(["full_barcode"], drop = False) - +else: + cols = ['barcode_5', 'barcode_3'] + samples["full_barcode"] = samples[cols].apply(lambda x: '_'.join(x.dropna()), axis=1) + samples=samples.set_index(["full_barcode"], drop = False) # Print project print("Procesing project:", config['project'], "\n") +# Print Sample annotation +print ("Sample annotation:", samples, "\n"), @@ -141,12 +143,31 @@ localrules: all rule all: input: + expand("demultiplexed/demux_{barcode}.fastq.gz", barcode=samples.index), "demultiplexed/demux_nomatch5.fastq.gz", + expand("{project}/qc/fastqc/raw_fastq_file_fastqc.html", project=config['project']), + expand("{project}/qc/fastqc/raw_fastq_file_fastqc.zip", project=config['project']), + + expand("{project}/qc/fastqc/{barcode}_fastqc.html", project=config['project'], barcode=samples.index), + expand("{project}/qc/fastqc/{barcode}_fastqc.zip", project=config['project'], barcode=samples.index), + + #expand("{project}/trimmed/demux_{barcode}_trimmed.fastq.gz", project=config['project'], barcode=samples.index), + expand("{project}/qc/fastqc/{barcode}_qtrimmed_fastqc.html", project=config['project'], barcode=samples.index), + expand("{project}/qc/fastqc/{barcode}_qtrimmed_trimmed_fastqc.zip", project=config['project'], barcode=samples.index), all_input, all_xlsites, all_group + + + + + +#expand("demultiplexed/demux_{barcode}.fastq.gz", barcode=samples.index), + + +# expand("{project}/trimmed/demux_{barcode}_untrimmed.fastq.gz", project=config['project'], barcode=samples.index), # old rule all not used - remove # "demultiplexed/demux_nomatch5.fastq.gz", # expand("{genomes_path}/{genome}/{genome}.fa.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), @@ -162,8 +183,8 @@ rule all: # expand("{project}/qc/fastqc/{barcode}_fastqc.zip", project=config['project'], barcode=samples.index, ), # # expand("{project}/trimmed/demux_{barcode}_trimmed.fastq.gz", project=config['project'], barcode=samples.index), -# expand("{project}/qc/fastqc/{barcode}_trimmed_fastqc.html", project=config['project'], barcode=samples.index), -# expand("{project}/qc/fastqc/{barcode}_trimmed_fastqc.zip", project=config['project'], barcode=samples.index), +# expand("{project}/qc/fastqc/{barcode}_qtrimmed_fastqc.html", project=config['project'], barcode=samples.index), +# expand("{project}/qc/fastqc/{barcode}_qtrimmed_trimmed_fastqc.zip", project=config['project'], barcode=samples.index), # expand("{project}/mapped/{barcode}/Aligned.sortedByCoord.out.bam", project=config['project'], barcode=samples.index), # expand("{project}/xlsites/{barcode}/{barcode}.unique.xl.bed", project=config['project'], barcode=samples.index), @@ -250,9 +271,9 @@ rule all: rule shasum_create: input: demultiplex=expand("demultiplexed/demux_{barcode}.fastq.gz", barcode=samples.index), - qc=["qc/fastqc/raw_fastq_file_fastqc.html", + qc=["{project}/qc/fastqc/raw_fastq_file_fastqc.html", expand("{project}/qc/fastqc/{barcode}_fastqc.html", project=config['project'], barcode=samples.index), - expand("{project}/qc/fastqc/{barcode}_trimmed_fastqc.html", project=config['project'], barcode=samples.index)], + expand("{project}/qc/fastqc/{barcode}_qtrimmed_fastqc.html", project=config['project'], barcode=samples.index)], genome=[expand("{genomes_path}/{genome}/{genome}.fa.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), expand("{genomes_path}/{genome}/{genome}.fa.gz.fai", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), expand("{genomes_path}/{genome}/{genome}.gtf.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), diff --git a/iCount/snakemake/rules/common.smk b/iCount/snakemake/rules/common.smk index 6c56e11..ea8a5e6 100644 --- a/iCount/snakemake/rules/common.smk +++ b/iCount/snakemake/rules/common.smk @@ -188,10 +188,16 @@ def get_gtf_path(wildcards): return ("{0}/{1}/{1}.gtf.gz".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) def get_star_index_path(wildcards): + return ("{0}/{1}/star_index/SAindex".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) + +def get_star_index_folder(wildcards): return ("{0}/{1}/star_index/".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) def get_segment_path(wildcards): - return ("{0}/{1}/segment/{1}_segment.gtf".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) + return ("{0}/{1}/segment/homo_sapiens_segment.gtf".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) + +def get_segment_regions(wildcards): + return ("{0}/{1}/segment/regions.gtf.gz".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) def get_templates_dir(wildcards): return ("{0}/{1}/segment/".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) diff --git a/iCount/snakemake/rules/demultiplex.smk b/iCount/snakemake/rules/demultiplex.smk index 9f5e14c..4aeb2fa 100644 --- a/iCount/snakemake/rules/demultiplex.smk +++ b/iCount/snakemake/rules/demultiplex.smk @@ -3,11 +3,24 @@ #==============================================================================# #### Check if one of the barcodes OR nomatch is not created/found +# def demux_out(wildcards): +# demux_output = list() +# #expand("demultiplexed/demux_{0}.fastq.gz".format(samples.loc[ "full_barcode"]))) +# demux_output.extend(expand("demultiplexed/demux_{barcode}.fastq.gz", barcode=samples.index)) +# demux_output.extend("demultiplexed/demux_nomatch5.fastq.gz") +# print ("demux_output", demux_output) +# return (demux_output) +# +# # expand("demultiplexed/demux_{barcode}.fastq.gz", barcode=samples.index), \ +# # "demultiplexed/demux_nomatch5.fastq.gz" +# + + rule demultiplex: input: - fastq_file=config['raw_fastq_file'] + fastq_file=config['raw_fastq_file'], output: - expand("demultiplexed/demux_{barcode}.fastq.gz", barcode=samples.index), + #expand("demultiplexed/demux_{barcode}.fastq.gz", barcode=samples.index), "demultiplexed/demux_nomatch5.fastq.gz" params: adapter3=samples["adapter_3"].unique().tolist(), @@ -26,6 +39,8 @@ rule demultiplex: + + # rule move_demultiplex: # input: # directory("demultiplexed/") diff --git a/iCount/snakemake/rules/map_reads.smk b/iCount/snakemake/rules/map_reads.smk index 40a6ba3..8797bdc 100644 --- a/iCount/snakemake/rules/map_reads.smk +++ b/iCount/snakemake/rules/map_reads.smk @@ -6,12 +6,12 @@ rule map_reads: input: - trimmed_reads="{project}/trimmed/demux_{barcode}_trimmed.fastq.gz", + trimmed_reads="{project}/trimmed/demux_{barcode}_qtrimmed.fastq.gz", gtf = get_gtf_path, + star_index = get_star_index_folder, output: "{project}/mapped/{barcode}/Aligned.sortedByCoord.out.bam" params: - star_index = directory(get_star_index_path), outdir=directory("{project}/mapped/{barcode}/"), multimax=config['multimax'], log: @@ -19,6 +19,6 @@ rule map_reads: shell: """ iCount mapstar --annotation {input.gtf} --multimax {params.multimax} \ - {input.trimmed_reads} {params.star_index} {params.outdir} + {input.trimmed_reads} {input.star_index} {params.outdir} """ diff --git a/iCount/snakemake/rules/prepare_genome.smk b/iCount/snakemake/rules/prepare_genome.smk index c4d8bc6..4f71f4f 100644 --- a/iCount/snakemake/rules/prepare_genome.smk +++ b/iCount/snakemake/rules/prepare_genome.smk @@ -44,7 +44,7 @@ rule download_genome: if GENOME in download_genomes: print ("Downloading iCount available genome:", GENOME) print ("Downloading genomes could take some time depending on your conection") - shell("iCount genome --genome {output.genome_fasta} --chromosomes {params.chromosomes} --source {params.source} {wildcards.genome} {params.release}") # For testing include --chromosomes MT 19 + shell("iCount genome --genome {output.genome_fasta} --chromosomes {params.chromosomes} --source {params.source} {wildcards.genome} {params.release} --chromosomes 20") # For testing include --chromosomes MT 19 shell("iCount annotation --annotation {output.gtf} --source {params.source} {wildcards.genome} {params.release}") elif GENOME in config['custom_genome'].keys(): diff --git a/iCount/snakemake/rules/qc.smk b/iCount/snakemake/rules/qc.smk index c1c5366..9fc50f3 100644 --- a/iCount/snakemake/rules/qc.smk +++ b/iCount/snakemake/rules/qc.smk @@ -3,16 +3,17 @@ # Read quality trimming and QC #==============================================================================# +# Check quality of raw reads rule fastqc_raw: input: config['raw_fastq_file'] output: - html="qc/fastqc/raw_fastq_file_fastqc.html", - zip="qc/fastqc/raw_fastq_file_fastqc.zip" # the suffix _fastqc.zip is necessary for multiqc to find the file. If not using multiqc, you are free to choose an arbitrary filename + html="{project}/qc/fastqc/raw_fastq_file_fastqc.html", + zip="{project}/qc/fastqc/raw_fastq_file_fastqc.zip" # the suffix _fastqc.zip is necessary for multiqc to find the file. If not using multiqc, you are free to choose an arbitrary filename wrapper: "0.38.0/bio/fastqc" - +# Check quality of demultiplexed reads rule fastqc: input: "demultiplexed/demux_{barcode}.fastq.gz" @@ -22,7 +23,9 @@ rule fastqc: log: "{project}/logs/fastqc/{barcode}_fastqc.txt" wrapper: - "0.36.0/bio/fastqc" + "0.38.0/bio/fastqc" + + # Trimm reads @@ -30,31 +33,34 @@ rule quality_trim: input: "demultiplexed/demux_{barcode}.fastq.gz" output: - trimmed_reads="{project}/trimmed/demux_{barcode}_trimmed.fastq.gz", - metrics="{project}/metrics/{barcode}_trimmed.txt" + qtrimmed_output = "{project}/trimmed/demux_{barcode}_qtrimmed.fastq.gz", + metrics="{project}/metrics/{barcode}_qtrimmed.txt", params: + trimmed_reads = "{project}/trimmed/demux_{barcode}_trimmed.fastq.gz", + untrimmed_reads = "{project}/trimmed/demux_{barcode}_untrimmed.fastq.gz", qual_trim=config['qual_trim'], minimum_length=config['minimum_length'], adapter=samples["adapter_3"].unique().tolist(), overlap=config['overlap'], - untrimmed_output=config['untrimmed_output'], error_rate=config['error_rate'], log: - "{project}/logs/trimmed/{barcode}_trimmed.txt" + "{project}/logs/trimmed/{barcode}_qtrimmed.txt" shell: """ - iCount cutadapt --qual_trim {params.qual_trim} --untrimmed_output {params.untrimmed_output} --minimum_length {params.minimum_length} --file_log 2 --file_logpath {log} --results_file {output.metrics} --reads_trimmed {output.trimmed_reads} {input} {params.adapter} + iCount cutadapt --qual_trim {params.qual_trim} --untrimmed_output {params.untrimmed_reads} --minimum_length {params.minimum_length} --file_log 2 --file_logpath {log} --results_file {output.metrics} --reads_trimmed {params.trimmed_reads} {input} {params.adapter} + cat {params.trimmed_reads} {params.untrimmed_reads} > {output.qtrimmed_output} + #rm {params.trimmed_reads} """ ## Unused parameters: --overlap {params.overlap} rule fastqc_trimmed: input: - "{project}/trimmed/demux_{barcode}_trimmed.fastq.gz" + "{project}/trimmed/demux_{barcode}_qtrimmed.fastq.gz" output: - html="{project}/qc/fastqc/{barcode}_trimmed_fastqc.html", - zip="{project}/qc/fastqc/{barcode}_trimmed_fastqc.zip" # the suffix _fastqc.zip is necessary for multiqc to find the file. If not using multiqc, you are free to choose an arbitrary filename + html="{project}/qc/fastqc/{barcode}_qtrimmed_fastqc.html", + zip="{project}/qc/fastqc/{barcode}_qtrimmed_trimmed_fastqc.zip" # the suffix _fastqc.zip is necessary for multiqc to find the file. If not using multiqc, you are free to choose an arbitrary filename log: - "{project}/logs/fastqc/{barcode}_trimmed_fastqc.log" + "{project}/logs/fastqc/{barcode}_qtrimmed_fastqc.log" wrapper: - "0.36.0/bio/fastqc" + "0.38.0/bio/fastqc" diff --git a/iCount/snakemake/rules/sig_xlsites.smk b/iCount/snakemake/rules/sig_xlsites.smk index 7b1dab7..a4f6a53 100644 --- a/iCount/snakemake/rules/sig_xlsites.smk +++ b/iCount/snakemake/rules/sig_xlsites.smk @@ -9,13 +9,25 @@ rule sig_xlsites: output: sigxls="{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.bed", scores="{project}/sig_xlsites/{barcode}/{barcode}.scores.tsv" + params: + features=config['features'], + group_by=config['group_by_sig_xl'], + merge_features=config['merge_features'], + half_window=config['half_window'], + fdr=config['fdr'], + perms=config['perms'], + rnd_seed=config['rnd_seed'], + results_file="{project}/sig_xlsites/{barcode}/{barcode}.sig_sites_metrics.txt", + log: + file_logpath="{project}/logs/sig_xlsites/{barcode}.xlsites.log" benchmark: "{project}/benchmarks/{barcode}.sig_xlsites.benchmark.txt" shell: """ - iCount peaks {input.segment_file} {input.xlsites} {output.sigxls} --scores {output.scores} + iCount peaks {input.segment_file} {input.xlsites} {output.sigxls} --scores {output.scores} --features {params.features} --group_by {params.group_by} --half_window {params.half_window} --fdr {params.fdr} --perms {params.perms} --rnd_seed {params.rnd_seed} --file_logpath {log.file_logpath} --results_file {params.results_file} """ +# iCount peaks iCount_genomes/homo_sapiens/segment/regions.gtf.gz synthetic_chr20/xlsites/NNNNGTAACNNN_NNAGG/NNNNGTAACNNN_NNAGG.unique.xl.bed synthetic_chr20/sig_xlsites/NNNNGTAACNNN_NNAGG/NNNNGTAACNNN_NNAGG.sig_sites.bed --scores synthetic_chr20/sig_xlsites/NNNNGTAACNNN_NNAGG/NNNNGTAACNNN_NNAGG.scores.tsv --features gene --group_by gene_id --half_window 3 --fdr 0.05 --perms 100 --rnd_seed 42 --file_logpath synthetic_chr20/logs/sig_xlsites/NNNNGTAACNNN_NNAGG.xlsites.log --results_file synthetic_chr20/sig_xlsites/NNNNGTAACNNN_NNAGG/NNNNGTAACNNN_NNAGG.sig_sites_metrics.txt def is_empty(fname): print(fname + " file is empty.") @@ -37,7 +49,7 @@ rule annotate_sig_xlsites: gene_id="{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.annotated.gene_id.tab" params: templates_dir = get_templates_dir, - segment = get_segment_path, + segment = get_segment_regions, #out_dir = "{project}/sig_xlsites/{barcode}/", run: if is_empty(input.sig_xlsites): @@ -59,7 +71,7 @@ rule summary_sig: subtype = "{project}/sig_xlsites/{barcode}/{barcode}.sig_sites.summary_subtype.tsv", params: templates_dir = get_templates_dir, - segment = get_segment_path, + segment = get_segment_regions, out_dir = "{project}/sig_xlsites/{barcode}/", rename_gene = "{project}/sig_xlsites/{barcode}/summary_gene.tsv", rename_type = "{project}/sig_xlsites/{barcode}/summary_type.tsv", diff --git a/iCount/snakemake/rules/xlsites.smk b/iCount/snakemake/rules/xlsites.smk index 5c2595a..eeb56f6 100644 --- a/iCount/snakemake/rules/xlsites.smk +++ b/iCount/snakemake/rules/xlsites.smk @@ -62,7 +62,7 @@ rule annotate_xlsites: gene_id="{project}/xlsites/{barcode}/{barcode}.unique.xl.annotated_sites_gene_id.tab", params: templates_dir = get_templates_dir, - segment = get_segment_path, + segment = get_segment_regions, #out_dir = "{project}/annotated/", shell: """ @@ -79,7 +79,7 @@ rule summary: subtype="{project}/xlsites/{barcode}/{barcode}.unique.xl.summary_subtype.tsv" params: templates_dir=get_templates_dir, - segment = get_segment_path, + segment = get_segment_regions, out_dir="{project}/xlsites/{barcode}/", rename_gene="{project}/xlsites/{barcode}/summary_gene.tsv", rename_type="{project}/xlsites/{barcode}/summary_type.tsv", diff --git a/iCount/snakemake/schemas/config.schema.yaml b/iCount/snakemake/schemas/config.schema.yaml index 620d4fa..76c76ce 100644 --- a/iCount/snakemake/schemas/config.schema.yaml +++ b/iCount/snakemake/schemas/config.schema.yaml @@ -145,6 +145,46 @@ properties: (default: 10000)" minimum: 0 default: 10000 + features: + type: string + description: "Features from annotation to consider: gene, CDS, intron, UTR3, UTR5, ncRNA or intergenic. + (default: gene)" + enum: ["gene", "CDS", "intron", "UTR3", "UTR5", "ncRNA", "intergenic"] + default: "gene" + group_by_sig_xl: + type: string + description: "Attribute by which cross-link positions are grouped (default: gene_id)" + enum: ["gene_id", "transcript_id"] + default: "gene_id" + merge_features: + type: boolean + description: "Treat all features as one when grouping. Has no effect when only one feature is given in features + parameter (false or true; default false)" + default: false + half_window: + type: integer + description: "Half-window size (default: 3)" + minimum: 0 + maximum: 1000 + default: 3 + fdr: + type: number + description: "FDR threshold (default: 0.05)" + minimum: 0.0001 + maximum: 1 + default: 0.05 + perms: + type: integer + description: "Number of permutations when calculating random distribution (default: 100)" + minimum: 10 + maximum: 1000 + default: 100 + rnd_seed: + type: integer + description: "Seed for random generator (default: 42)" + minimum: 1 + maximum: 1000 + default: 42 distance: type: integer description: "Merge adjacent peaks into clusters and sum cross-links within clusters. Distance between two peaks diff --git a/iCount/tests/functional/synthetic_reads/synthetic_iCLIP_reads.py b/iCount/tests/functional/synthetic_reads/synthetic_iCLIP_reads.py new file mode 100644 index 0000000..ff12dd4 --- /dev/null +++ b/iCount/tests/functional/synthetic_reads/synthetic_iCLIP_reads.py @@ -0,0 +1,238 @@ +#!/usr/bin/python2.7 + +import random +import os +import gzip + +### Script to generate synthetic reads to test demultiplexing on iMaps +# +# python synthetic_iCLIP_reads.py fastq_file_path.fq +# +# +# +# +# + +''' +## Get fasta +# From the chr 20 I'll get synthetic reads from position chr20_3470487_4016368 +# chr20_2652426_2664336.bed (11kb 2 genes both orientations lots of snora snords) +bedtools getfasta -fi homo_sapiens_chr20.fa -bed chr20_2652426_2664336.bed > chr20_2652426_2664336.fasta +/Users/mozosi/Programs/art_bin_MountRainier/art_illumina -ss HS20 --noALN -i "/Users/mozosi/Dropbox (UCL-MN Team)/GitHub/iCount/iCount/tests/functional/synthetic_reads/chr20_2652426_2664336.fasta" -l 100 -f 3 -o "/Users/mozosi/Desktop/synthetic_reads/chr20_2652426_2664336_1" --rndSeed 111 +/Users/mozosi/Programs/art_bin_MountRainier/art_illumina -ss HS20 --noALN -i "/Users/mozosi/Dropbox (UCL-MN Team)/GitHub/iCount/iCount/tests/functional/synthetic_reads/chr20_2652426_2664336.fasta" -l 100 -f 3 -o "/Users/mozosi/Desktop/synthetic_reads/chr20_2652426_2664336_2" --rndSeed 222 +/Users/mozosi/Programs/art_bin_MountRainier/art_illumina -ss HS20 --noALN -i "/Users/mozosi/Dropbox (UCL-MN Team)/GitHub/iCount/iCount/tests/functional/synthetic_reads/chr20_2652426_2664336.fasta" -l 100 -f 3 -o "/Users/mozosi/Desktop/synthetic_reads/chr20_2652426_2664336_3" --rndSeed 333 + +# Include some reads in snord86 chr20_2656093_2656195 (TTA) +bedtools getfasta -fi homo_sapiens_chr20.fa -bed chr20_2656093_2656195.bed > chr20_2656093_2656195.fasta +/Users/mozosi/Programs/art_bin_MountRainier/art_illumina -ss HS20 --noALN -i "/Users/mozosi/Desktop/synthetic_reads/chr20_2656093_2656195.fasta" -l 100 -f 20 -o "/Users/mozosi/Desktop/synthetic_reads/chr20_2656093_2656195" --rndSeed 111 + +# intron-exon reads in NOP56 chr20_2656398_2656507 (AGG) +bedtools getfasta -fi homo_sapiens_chr20.fa -bed chr20_2656398_2656507.bed > chr20_2656398_2656507.fasta +/Users/mozosi/Programs/art_bin_MountRainier/art_illumina -ss HS20 --noALN -i "/Users/mozosi/Desktop/synthetic_reads/chr20_2656398_2656507.fasta" -l 100 -f 100 -o "/Users/mozosi/Desktop/synthetic_reads/chr20_2656398_2656507" --rndSeed 111 + + +## Merge +cat chr20_2652426_2664336_1_NNNN_GTAAC_NNN_NN_ATT.fq chr20_2652426_2664336_2_NNNN_GTAAC_NNN_NN_AGG.fq chr20_2652426_2664336_3_NNNN_GTAAC_NNN_NN_TTA.fq chr20_2656093_2656195_NNNN_GTAAC_NNN_NN_TTA.fq chr20_2656398_2656507_NNNN_GTAAC_NNN_NN_TTA.fq chr20_2656398_2656507_NNNN_GTAAC_NNN_NN_AGG.fq > merge_chr20_2652426_2664336_GTAAC.fq +cat merge_chr20_2652426_2664336_GTAAC.fq | wc -l +gzip merge_chr20_2652426_2664336_GTAAC.fq + +HiSeq 2000 (100bp) + + + + ====================ART==================== + ART_Illumina (2008-2016) + Q Version 2.5.8 (June 6, 2016) + Contact: Weichun Huang + ------------------------------------------- + +Warning: your simulation will not output any ALN or SAM file with your parameter settings! + Single-end Simulation + +Total CPU time used: 849.236 + +The random seed for the run: 666 + +Parameters used during run + Read Length: 100 + Genome masking 'N' cutoff frequency: 1 in 100 + Fold Coverage: 1X + Profile Type: Combined + ID Tag: + +Quality Profile(s) + First Read: HiSeq 2000 Length 100 R1 (built-in profile) + +Output files + + FASTQ Sequence File: + single_dat.fq + + +sed -n 1,10p single_dat.fq > synthetic_iCLIP_reads_10000.fq + +NNNNGTAACNNN NNATT +NNNNGTAACNNN NNAGG +NNNNGTAACNNN NNTTA +NNNNGTAACNNN NNTGC +NNNNGTAACNNN NNCTG +NNNNGTAACNNN NNCGT +NNNNGTAACNNN NNGTC +NNNNGTAACNNN NNGGA +''' + + +### Imputs +# fastq file +fastq_file_path="chr20_2656398_2656507.fq" +fastq_file_name = os.path.basename(fastq_file_path) +fastq_file_name = fastq_file_name.replace(".fq", "") + + +# Synthetic 5' barcode +barcode5 = "GTAAC" +number_random_upstream = 4 +number_random_downstream = 3 + +# Synthetic 5' barcode +barcode3 = "AGG" +number_random_barcode3 = 2 + +# Synthetic 3' Illumina adapter +adapter3 = "AGATCGGAAGAGCGGTTCAG" +adapter3_qual = "FFFFFFFFFFFFFFFFFFFF" + + +# Output name including the 5'barcode and UMI lenght +print (fastq_file_name) +number_N_upstream = 'N' * number_random_upstream +number_N_downstream = 'N' * number_random_downstream +number_N_barcode3 = 'N' * number_random_barcode3 +modified_fastq_file_out="%s_%s_%s_%s_%s_%s.fq" % (fastq_file_name, number_N_upstream, barcode5, number_N_downstream, number_N_barcode3, barcode3) + +print (modified_fastq_file_out) + + + + +# fastq parser class +class ParseFastQ(object): + """Returns a read-by-read fastQ parser analogous to file.readline()""" + + def __init__(self, filePath, headerSymbols=['@', '+']): + """Returns a read-by-read fastQ parser analogous to file.readline(). + Exmpl: parser.next() + -OR- + Its an iterator so you can do: + for rec in parser: + ... do something with rec ... + + rec is tuple: (seqHeader,seqStr,qualHeader,qualStr) + """ + if filePath.endswith('.gz'): + self._file = gzip.open(filePath) + else: + self._file = open(filePath, 'rU') + self._currentLineNumber = 0 + self._hdSyms = headerSymbols + + def __iter__(self): + return self + + def next(self): + return self.__next__() + + def __next__(self): + """Reads in next element, parses, and does minimal verification. + Returns: tuple: (seqHeader,seqStr,qualHeader,qualStr)""" + # ++++ Get Next Four Lines ++++ + elemList = [] + for i in range(4): + line = self._file.readline() + self._currentLineNumber += 1 ## increment file position + if line: + elemList.append(line.strip('\n')) + else: + elemList.append(None) + + # ++++ Check Lines For Expected Form ++++ + trues = [bool(x) for x in elemList].count(True) + nones = elemList.count(None) + # -- Check for acceptable end of file -- + if nones == 4: + raise StopIteration + # -- Make sure we got 4 full lines of data -- + assert trues == 4, \ + "** ERROR: It looks like I encountered a premature EOF or empty line.\n\ + Please check FastQ file near line number %s (plus or minus ~4 lines) and try again**" % ( + self._currentLineNumber) + # -- Make sure we are in the correct "register" -- + assert elemList[0].startswith(self._hdSyms[0]), \ + "** ERROR: The 1st line in fastq element does not start with '%s'.\n\ + Please check FastQ file near line number %s (plus or minus ~4 lines) and try again**" % ( + self._hdSyms[0], self._currentLineNumber) + assert elemList[2].startswith(self._hdSyms[1]), \ + "** ERROR: The 3rd line in fastq element does not start with '%s'.\n\ + Please check FastQ file near line number %s (plus or minus ~4 lines) and try again**" % ( + self._hdSyms[1], self._currentLineNumber) + # -- Make sure the seq line and qual line have equal lengths -- + assert len(elemList[1]) == len(elemList[3]), "** ERROR: The length of Sequence data and Quality data of the last record aren't equal.\n\ + Please check FastQ file near line number %s (plus or minus ~4 lines) and try again**" % ( + self._currentLineNumber) + + # ++++ Return fatsQ data as tuple ++++ + return tuple(elemList) + + + + + + + +def modify_read(fastq_file_path, barcode5): + + parser = ParseFastQ(fastq_file_path) + counter_reads = 0 + + for record in parser: + + # Initialise counter of total reads + counter_reads += 1 + + # Define each element on a fastq file + header = record[0] + seq = record[1] + header2 = record[2] + qual = record[3] + + + random_upstream = ''.join(random.choice('ACTG') for _ in range(number_random_upstream)) + random_downstream = ''.join(random.choice('ACTG') for _ in range(number_random_downstream)) + + len_barcode5 = len(barcode5) + number_random_upstream + number_random_downstream + + barcode5_qual = 'F' * len_barcode5 ## All the lenght including randomer + + len_barcode3 = len(barcode3) + number_random_barcode3 + random_barcode3 = ''.join(random.choice('ACTG') for _ in range(number_random_barcode3)) + barcode3_qual = 'F' * len_barcode3 + + # print ("Original read") + # print (seq) + # print qual + # print "\n" + + seq = random_upstream + barcode5 + random_downstream + seq + random_barcode3 + barcode3 + adapter3 + qual = barcode5_qual + qual + barcode3_qual + adapter3_qual + + # print "Modified read" + # print seq + # print qual + # print "\n" + + f = open(modified_fastq_file_out, 'a+') + f.write("%s\n%s\n%s\n%s\n" % (header, seq, header2, qual)) + f.close() + + +modify_read(fastq_file_path, barcode5) \ No newline at end of file From a0144cc658c0327a9daf294891f5fe512fd1e9f6 Mon Sep 17 00:00:00 2001 From: Faitero Date: Wed, 11 Nov 2020 09:45:19 +0100 Subject: [PATCH 07/26] Solve sigxlsites issues and add new synthetic reads --- iCount/examples/config_synthetic.yaml | 34 +++++++++++++++++++++++---- iCount/snakemake/icount_snakemake.smk | 14 ++++++++--- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/iCount/examples/config_synthetic.yaml b/iCount/examples/config_synthetic.yaml index f8ed887..3c40b87 100755 --- a/iCount/examples/config_synthetic.yaml +++ b/iCount/examples/config_synthetic.yaml @@ -8,7 +8,7 @@ # Define name of the project. Output files will be saved under "project" folder. ## Project directory name -project: "synthetic" +project: "synthetic_chr20" #=============================== Samples ==============================# @@ -25,14 +25,15 @@ project: "synthetic" # Sample table input accepts tsv or txt #samples: "data/hnRNPC_reduced.tsv" -samples: "data/samples_synthetic.txt" +samples: "data/samples_synthetic_chr20.txt" #======================== Raw sequencing data =============================# # Raw fastq file to demultiplex and analyse #raw_fastq_file: "data/hnRNPC.fq.gz" -raw_fastq_file: "data/merge_my_script.fq.gz" +raw_fastq_file: "data/merge_chr20_2652426_2664336_GTAAC.fq.gz" +#raw_fastq_file: "data/merge_my_script.fq.gz" #============================ Desired output ===========================# @@ -150,7 +151,7 @@ minimum_length: 17 # adapter to be found (default: None) overlap: # Write reads that do not contain any adapter to this file path (default: None) -untrimmed_output: +# untrimmed_output: # Maximum allowed error rate (no. of errors divided by the length # of the matching region) (default: None) error_rate: 0.1 @@ -204,7 +205,7 @@ max_barcodes: 10000 #~~~~~~~~~~~~~~~~ iCount significant cross links ~~~~~~~~~~~~~~~~# # Peaks (significant crosslinks low FDR) configuration # Find positions with high density of cross-linked sites. -# +# More info on "iCount peaks --help" or check manual # There are two typical variants of this analysis, depending on the parameters: # #* Gene-wise analysis, where: @@ -216,6 +217,29 @@ max_barcodes: 10000 # * group_by = transcript_id # [--merge_features] [--half_window] [--fdr] +# Features from annotation to consider: gene, CDS, intron, UTR3, UTR5, ncRNA or intergenic. If None, ['gene'] is used. +# Sometimes, it is advised to use ['gene', 'intergenic'] (default: gene) +features: "gene" +# Attribute by which cross-link positions are grouped (default: gene_id) +group_by_sig_xl: "gene_id" +# Treat all features as one when grouping. Has no effect when only one feature is given in features parameter +# (default: False) +merge_features: false +# Half-window size (default: 3) +half_window: 3 +# FDR threshold (default: 0.05) +fdr: 0.05 +# Number of permutations when calculating random distribution (default: 100) +perms: 100 +# Seed for random generator (default: 42) +rnd_seed: 42 +# -prog, --report_progress +# Report analysis progress (default: False) +# -S , --stdout_log Threshold value (0-50) for logging to stdout. If 0, logging to stdout if turned OFF. +# -F , --file_log Threshold value (0-50) for logging to file. If 0, logging to file if turned OFF. +# -P , --file_logpath Path to log file. +# -M , --results_file File into which to store Metrics. +# #~~~~~~~~~~~~~~~~ iCount cluster of cross links ~~~~~~~~~~~~~~~~# # Merge adjacent peaks into clusters and sum cross-links within clusters. diff --git a/iCount/snakemake/icount_snakemake.smk b/iCount/snakemake/icount_snakemake.smk index d04a949..2ba7028 100644 --- a/iCount/snakemake/icount_snakemake.smk +++ b/iCount/snakemake/icount_snakemake.smk @@ -2,7 +2,7 @@ # iCount Snakemake workflow #==============================================================================# # Authors # Igor Ruiz de los Mozos, Charlotte Capitanchik, Tomaz Curk -# Last updated: September 2020 +# Last updated: November 2020 # Install Locally @@ -14,11 +14,19 @@ # TMP_ROOT = os.environ.get('ICOUNT_TMP_ROOT', 'a directory you have access to') # TMP_ROOT = os.environ.get('ICOUNT_TMP_ROOT', '/Users/mozosi/Programs/temp_iCount') -# Check the install # Step one: Activate conda environment with Snakemake, iCount and dependencies installed # Create new environment -# conda env create --name iCount_pipeline2 --file envs/environment_iCount.yaml +conda create -c conda-forge -c bioconda -n iCount_pipeline3 +conda activate iCount_pipeline3 +conda install -c conda-forge mamba +conda install --yes --file snakemake/envs/environment_iCount.yaml +pip install ./iCount/ +# Check the install +iCount + + +# conda env create --name iCount_pipeline2 --file snakemake/envs/environment_iCount.yaml # conda activate iCount_pipeline3 # pip install ./iCount/ # Check the install From b517dcdb09fd1b0c1269a4ff841695a4f013adac Mon Sep 17 00:00:00 2001 From: Faitero Date: Fri, 13 Nov 2020 10:22:19 +0100 Subject: [PATCH 08/26] Prepare genome poitining only to homo_sapiens --- iCount/examples/config_synthetic.yaml | 4 +++- iCount/examples/data/samples_synthetic_chr20.txt | 6 +++--- iCount/snakemake/icount_snakemake.smk | 16 ++++++++-------- iCount/snakemake/rules/common.smk | 2 +- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/iCount/examples/config_synthetic.yaml b/iCount/examples/config_synthetic.yaml index 3c40b87..4f2fa0e 100755 --- a/iCount/examples/config_synthetic.yaml +++ b/iCount/examples/config_synthetic.yaml @@ -107,7 +107,9 @@ custom_genome: genome_fasta: "custom/custom2.fa.gz" # gtf file with transcripts annotation: "custom/custom2.gtf.gz" - +# Arabidopsis_thaliana: +# genome_fasta: "custom/Arabidopsis_thaliana/Arabidopsis_thaliana.TAIR10.dna_sm.toplevel.fa.gz" +# annotation: "custom/Arabidopsis_thaliana/Arabidopsis_thaliana_mod.gtf" # Custom genome example annotation. Name directory, fasta sequence and annotation using the same acromyn ("hg19") # as in the example: #custom_genome: diff --git a/iCount/examples/data/samples_synthetic_chr20.txt b/iCount/examples/data/samples_synthetic_chr20.txt index e6d752e..4d6c529 100644 --- a/iCount/examples/data/samples_synthetic_chr20.txt +++ b/iCount/examples/data/samples_synthetic_chr20.txt @@ -1,4 +1,4 @@ sample_name method protein cells_tissue condition mapto barcode_5 barcode_3 adapter_3 linker comments group New -synthetic_1 iCLIP synthetic HEK293 synthetic_1 homo_sapiens NNNNGTAACNNN NNATT AGATCGGAAGAGCGGTTCAG L3-NNATT L3-NNATT WildType -synthetic_2 iCLIP synthetic HEK293 synthetic_2 homo_sapiens NNNNGTAACNNN NNAGG AGATCGGAAGAGCGGTTCAG L3-NNAGG L3-NNAGG WildType -synthetic_3 iCLIP synthetic HEK293 synthetic_3 homo_sapiens NNNNGTAACNNN NNTTA AGATCGGAAGAGCGGTTCAG L3-NNTTA L3-NNTTA WildType \ No newline at end of file +synthetic_1 iCLIP synthetic HEK293 synthetic_1 homo_sapiens NNNNGTAACNNN NNATT AGATCGGAAGAGCGGTTCAG L3-NNATT L3-NNATT WildType +synthetic_2 iCLIP synthetic HEK293 synthetic_2 homo_sapiens NNNNGTAACNNN NNAGG AGATCGGAAGAGCGGTTCAG L3-NNAGG L3-NNAGG WildType +synthetic_3 iCLIP synthetic HEK293 synthetic_3 homo_sapiens NNNNGTAACNNN NNTTA AGATCGGAAGAGCGGTTCAG L3-NNTTA L3-NNTTA WildType \ No newline at end of file diff --git a/iCount/snakemake/icount_snakemake.smk b/iCount/snakemake/icount_snakemake.smk index 2ba7028..8123e85 100644 --- a/iCount/snakemake/icount_snakemake.smk +++ b/iCount/snakemake/icount_snakemake.smk @@ -16,14 +16,14 @@ # Step one: Activate conda environment with Snakemake, iCount and dependencies installed -# Create new environment -conda create -c conda-forge -c bioconda -n iCount_pipeline3 -conda activate iCount_pipeline3 -conda install -c conda-forge mamba -conda install --yes --file snakemake/envs/environment_iCount.yaml -pip install ./iCount/ -# Check the install -iCount +# # Create new environment +# conda create -c conda-forge -c bioconda -n iCount_pipeline3 +# conda activate iCount_pipeline3 +# conda install -c conda-forge mamba +# conda install --yes --file snakemake/envs/environment_iCount.yaml +# pip install ./iCount/ +# # Check the install +# iCount # conda env create --name iCount_pipeline2 --file snakemake/envs/environment_iCount.yaml diff --git a/iCount/snakemake/rules/common.smk b/iCount/snakemake/rules/common.smk index ea8a5e6..57199b0 100644 --- a/iCount/snakemake/rules/common.smk +++ b/iCount/snakemake/rules/common.smk @@ -194,7 +194,7 @@ def get_star_index_folder(wildcards): return ("{0}/{1}/star_index/".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) def get_segment_path(wildcards): - return ("{0}/{1}/segment/homo_sapiens_segment.gtf".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) + return ("{0}/{1}/segment/{1}_segment.gtf".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) def get_segment_regions(wildcards): return ("{0}/{1}/segment/regions.gtf.gz".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) From f028e64fe1b563c051e3c4c06995115e2a78ea10 Mon Sep 17 00:00:00 2001 From: Gregor Rot Date: Mon, 16 Nov 2020 11:39:53 +0100 Subject: [PATCH 09/26] Initial version that installs conda requirements for iCount --- Dockerfile | 105 ++++++++++++++++++----------------------------------- 1 file changed, 35 insertions(+), 70 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7baedf8..7f89c31 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,96 +1,61 @@ -FROM ubuntu:16.04 +FROM continuumio/anaconda3 MAINTAINER Tomaz Curk +# suppress prompt for tzdata +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Europe/Ljubljana + # thanks to https://github.com/bschiffthaler/ngs/blob/master/base/Dockerfile # and https://github.com/AveraSD/ngs-docker-star/blob/master/Dockerfile -RUN useradd -m -d /home/icuser icuser - -# update system -RUN sed -Ei 's/^# deb-src /deb-src /' /etc/apt/sources.list -RUN apt-get update && apt-get upgrade -y && \ - apt-get install -y \ - build-essential \ - gfortran \ - libatlas-base-dev \ - wget \ - g++ \ - make \ - binutils \ - python3 \ - python3-pip \ - python3-setuptools \ - python-virtualenv \ - python-pip \ - pandoc \ - git && \ - apt-get build-dep -y python3-matplotlib - -RUN apt-get autoclean -y && \ - apt-get autoremove -y +RUN conda update -n base -c defaults conda -y +RUN conda update -n base -c defaults conda +RUN conda config --add channels defaults +RUN conda config --add channels bioconda +RUN conda config --add channels conda-forge -################# ### samtools -RUN apt-get install -y \ - zlib1g-dev \ - liblzma-dev \ - libbz2-dev \ - samtools - +RUN conda install -c bioconda -y "samtools>=1.10" -################# ### bedtools, need at least version 2.26, where merge command reports strand -# RUN apt-get install -y bedtools -WORKDIR /tmp/bedtools -RUN wget https://github.com/arq5x/bedtools2/releases/download/v2.27.1/bedtools-2.27.1.tar.gz -RUN tar -zxvf bedtools-2.27.1.tar.gz -WORKDIR /tmp/bedtools/bedtools2 -RUN make -RUN make install -WORKDIR /tmp -RUN rm -rfv bedtools +RUN conda install -c bioconda -y bedtools -################# ### RNA-star -WORKDIR /tmp/STAR -RUN wget https://github.com/alexdobin/STAR/archive/2.6.1a.tar.gz -RUN tar -xvzf 2.6.1a.tar.gz -WORKDIR /tmp/STAR/STAR-2.6.1a/source -RUN make STAR -RUN mkdir -p /home/icuser/bin && cp STAR /home/icuser/bin -WORKDIR /tmp -RUN rm -rfv STAR - +RUN conda install -c bioconda -y star ################# #### iCount -RUN chown -R icuser.icuser /home/icuser +################# + +#RUN useradd -m -d /home/icuser icuser + +#RUN chown -R icuser.icuser /home/icuser -USER icuser -WORKDIR /home/icuser -RUN virtualenv -p python3 /home/icuser/.icountenv +#USER icuser +#WORKDIR /home/icuser +#RUN virtualenv -p python3 /home/icuser/.icountenv -USER root +#USER root # to speed-up building of Docker images -RUN /home/icuser/.icountenv/bin/pip install numpy pandas pysam pybedtools numpydoc matplotlib +#RUN /home/icuser/.icountenv/bin/pip install numpy pandas pysam pybedtools numpydoc matplotlib -ADD . /home/icuser/iCount_src -RUN chown -R icuser.icuser /home/icuser +#ADD . /home/icuser/iCount_src +#RUN chown -R icuser.icuser /home/icuser -USER icuser -WORKDIR /home/icuser/iCount_src +#USER icuser +#WORKDIR /home/icuser/iCount_src -RUN ../.icountenv/bin/pip install -e .[docs,test] +#RUN ../.icountenv/bin/pip install -e .[docs,test] -USER root -RUN echo "source /home/icuser/.icountenv/bin/activate" >> /etc/bash.bashrc -USER icuser +#USER root +#RUN echo "source /home/icuser/.icountenv/bin/activate" >> /etc/bash.bashrc +#USER icuser -RUN mkdir /home/icuser/storage +#RUN mkdir /home/icuser/storage -ENV PATH /home/icuser/bin:$PATH +#ENV PATH /home/icuser/bin:$PATH -WORKDIR /home/icuser +#WORKDIR /home/icuser -CMD ["/bin/bash"] +#CMD ["/bin/bash"] From b1291475272ef40a1f126e7d7ba18f935cd0e540 Mon Sep 17 00:00:00 2001 From: Gregor Rot Date: Wed, 18 Nov 2020 10:52:16 +0100 Subject: [PATCH 10/26] Renamed and moved environment_iCount.yaml yo conda_iCount.yaml (moved to root folder). Changed requirement of python 3.6 to 3.8, removed samtools requirement of 1.9. --- Dockerfile | 46 +++++++++---------- ...vironment_iCount.yaml => conda_iCount.yaml | 6 +-- 2 files changed, 25 insertions(+), 27 deletions(-) rename iCount/snakemake/envs/environment_iCount.yaml => conda_iCount.yaml (88%) diff --git a/Dockerfile b/Dockerfile index 7f89c31..c999c00 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,38 +24,36 @@ RUN conda install -c bioconda -y bedtools ### RNA-star RUN conda install -c bioconda -y star -################# -#### iCount -################# +### numpy -#RUN useradd -m -d /home/icuser icuser - -#RUN chown -R icuser.icuser /home/icuser - -#USER icuser -#WORKDIR /home/icuser -#RUN virtualenv -p python3 /home/icuser/.icountenv #USER root # to speed-up building of Docker images #RUN /home/icuser/.icountenv/bin/pip install numpy pandas pysam pybedtools numpydoc matplotlib -#ADD . /home/icuser/iCount_src -#RUN chown -R icuser.icuser /home/icuser - -#USER icuser -#WORKDIR /home/icuser/iCount_src - -#RUN ../.icountenv/bin/pip install -e .[docs,test] +################# +#### iCount +################# -#USER root -#RUN echo "source /home/icuser/.icountenv/bin/activate" >> /etc/bash.bashrc -#USER icuser +RUN useradd -m -d /home/icuser icuser +RUN chown -R icuser.icuser /home/icuser -#RUN mkdir /home/icuser/storage +USER icuser +WORKDIR /home/icuser +RUN mkdir /home/icuser/storage +RUN git clone https://github.com/tomazc/iCount.git --branch snakemake -#ENV PATH /home/icuser/bin:$PATH +RUN conda create -c conda-forge -c bioconda -n iCount_pipeline3 -y +RUN conda init bash +RUN exec bash # restart shell +RUN conda activate iCount_pipeline3 +RUN conda install -c conda-forge mamba -y +RUN conda env update --file iCount/iCount/snakemake/envs/environment_iCount.yaml # needs ~ 4 GB RAM, otherwise killed -#WORKDIR /home/icuser +USER root +RUN echo "conda activate iCount_pipeline3" >> /etc/bash.bashrc -#CMD ["/bin/bash"] +USER icuser +ENV PATH /home/icuser/bin:$PATH +WORKDIR /home/icuser +CMD ["/bin/bash"] diff --git a/iCount/snakemake/envs/environment_iCount.yaml b/conda_iCount.yaml similarity index 88% rename from iCount/snakemake/envs/environment_iCount.yaml rename to conda_iCount.yaml index 32727b7..d1d00be 100644 --- a/iCount/snakemake/envs/environment_iCount.yaml +++ b/conda_iCount.yaml @@ -4,11 +4,11 @@ channels: dependencies: - snakemake =5.5.4 - - python =3.6 + - python =3.8 - jinja2 =2.10.1 - networkx =2.3 - bcftools =1.9 - - samtools =1.9 + - samtools - bwa =0.7.17 - pysam = 0.14 - cutadapt =2.4 @@ -25,4 +25,4 @@ dependencies: - matplotlib =3.1.0 - docutils = 0.15.2 - sphinx_rtd_theme =0.4.3 - - sphinx-releases =1.6.1 \ No newline at end of file + - sphinx-releases =1.6.1 From 012c96de6911e65afe481c9350d81f7425d868fb Mon Sep 17 00:00:00 2001 From: Gregor Rot Date: Wed, 18 Nov 2020 16:05:56 +0100 Subject: [PATCH 11/26] Conda yaml dev --- Dockerfile | 34 ++++++++++++---------------------- conda_iCount.yaml | 42 +++++++++++++++++++++--------------------- 2 files changed, 33 insertions(+), 43 deletions(-) diff --git a/Dockerfile b/Dockerfile index c999c00..15320a0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,28 +8,23 @@ ENV TZ=Europe/Ljubljana # thanks to https://github.com/bschiffthaler/ngs/blob/master/base/Dockerfile # and https://github.com/AveraSD/ngs-docker-star/blob/master/Dockerfile +SHELL ["/bin/bash", "-c"] + RUN conda update -n base -c defaults conda -y +RUN conda install -c conda-forge mamba -y -RUN conda update -n base -c defaults conda RUN conda config --add channels defaults RUN conda config --add channels bioconda RUN conda config --add channels conda-forge ### samtools -RUN conda install -c bioconda -y "samtools>=1.10" +#RUN conda install -c bioconda -y "samtools>=1.10" ### bedtools, need at least version 2.26, where merge command reports strand -RUN conda install -c bioconda -y bedtools +# RUN conda install -c bioconda -y bedtools ### RNA-star -RUN conda install -c bioconda -y star - -### numpy - - -#USER root -# to speed-up building of Docker images -#RUN /home/icuser/.icountenv/bin/pip install numpy pandas pysam pybedtools numpydoc matplotlib +# RUN conda install -c bioconda -y star ################# #### iCount @@ -45,15 +40,10 @@ RUN git clone https://github.com/tomazc/iCount.git --branch snakemake RUN conda create -c conda-forge -c bioconda -n iCount_pipeline3 -y RUN conda init bash -RUN exec bash # restart shell -RUN conda activate iCount_pipeline3 -RUN conda install -c conda-forge mamba -y -RUN conda env update --file iCount/iCount/snakemake/envs/environment_iCount.yaml # needs ~ 4 GB RAM, otherwise killed - -USER root -RUN echo "conda activate iCount_pipeline3" >> /etc/bash.bashrc +RUN echo "conda activate iCount_pipeline3" >> ~/.bashrc +#RUN conda env update --file iCount/conda_iCount.yaml # needs ~ 4 GB RAM, otherwise killed +#RUN pip install ./iCount -USER icuser -ENV PATH /home/icuser/bin:$PATH -WORKDIR /home/icuser -CMD ["/bin/bash"] +#ENV PATH /home/icuser/bin:$PATH +#WORKDIR /home/icuser +#CMD ["/bin/bash"] diff --git a/conda_iCount.yaml b/conda_iCount.yaml index d1d00be..c0b5610 100644 --- a/conda_iCount.yaml +++ b/conda_iCount.yaml @@ -1,28 +1,28 @@ channels: + - defaults - bioconda - conda-forge dependencies: - - snakemake =5.5.4 - - python =3.8 - - jinja2 =2.10.1 - - networkx =2.3 - - bcftools =1.9 - - samtools - - bwa =0.7.17 - - pysam = 0.14 - - cutadapt =2.4 - - bedtools =2.28 - - STAR =2.7.2a + - python = 3.8.6 + - snakemake = 5.5.4 + - jinja2 = 2.10.1 + - networkx = 2.3 + - bcftools = 1.9 + - samtools = 1.10 + - bwa = 0.7.17 + - pysam = 0.16.0 + - cutadapt = 2.4 + - bedtools = 2.28 + - STAR = 2.7.2a - trim-galore = 0.6.4 - - pip =19.2.3 - - pybedtools =0.8.0 - - numpy =1.17.1 - - pandas =0.25.1 - - pybedtools =0.8.0 - - numpydoc =0.9.1 - - sphinx =2.2.0 - - matplotlib =3.1.0 + - pip = 20.2.4 + - pybedtools = 0.8.0 + - numpy = 1.17.1 + - pandas = 0.25.1 + - pybedtools = 0.8.0 + - numpydoc = 0.9.1 + - sphinx = 2.2.0 + - matplotlib = 3.1.0 - docutils = 0.15.2 - - sphinx_rtd_theme =0.4.3 - - sphinx-releases =1.6.1 + - sphinx-releases = 1.6.1 From 383ff28b296adb3e0afe67fedf079c8d09fd7ee3 Mon Sep 17 00:00:00 2001 From: Gregor Rot Date: Thu, 19 Nov 2020 09:28:13 +0100 Subject: [PATCH 12/26] Updated several package versions to be compatible with python 3.8.6. Now iCount installs in conda via pip. --- Dockerfile | 22 +++++++++++++++------- conda_iCount.yaml | 35 +++++++++++++++++------------------ 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/Dockerfile b/Dockerfile index 15320a0..5eb6024 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,9 @@ ENV TZ=Europe/Ljubljana # thanks to https://github.com/bschiffthaler/ngs/blob/master/base/Dockerfile # and https://github.com/AveraSD/ngs-docker-star/blob/master/Dockerfile -SHELL ["/bin/bash", "-c"] +SHELL ["/bin/bash", "--login", "-c"] + +RUN apt-get install -y vim RUN conda update -n base -c defaults conda -y RUN conda install -c conda-forge mamba -y @@ -36,14 +38,20 @@ RUN chown -R icuser.icuser /home/icuser USER icuser WORKDIR /home/icuser RUN mkdir /home/icuser/storage -RUN git clone https://github.com/tomazc/iCount.git --branch snakemake +#RUN git clone https://github.com/tomazc/iCount.git --branch snakemake +COPY . /home/icuser/iCount RUN conda create -c conda-forge -c bioconda -n iCount_pipeline3 -y RUN conda init bash RUN echo "conda activate iCount_pipeline3" >> ~/.bashrc -#RUN conda env update --file iCount/conda_iCount.yaml # needs ~ 4 GB RAM, otherwise killed -#RUN pip install ./iCount -#ENV PATH /home/icuser/bin:$PATH -#WORKDIR /home/icuser -#CMD ["/bin/bash"] +SHELL ["conda", "run", "-n", "iCount_pipeline3", "/bin/bash", "-c"] + +### needs ~ 4 GB RAM, otherwise killed +RUN conda env update --file iCount/conda_iCount.yaml + +RUN pip install ./iCount + +ENV PATH /home/icuser/bin:$PATH +WORKDIR /home/icuser +CMD ["/bin/bash"] diff --git a/conda_iCount.yaml b/conda_iCount.yaml index c0b5610..c976fd7 100644 --- a/conda_iCount.yaml +++ b/conda_iCount.yaml @@ -5,24 +5,23 @@ channels: dependencies: - python = 3.8.6 - - snakemake = 5.5.4 - - jinja2 = 2.10.1 - - networkx = 2.3 - - bcftools = 1.9 - - samtools = 1.10 + - snakemake = 5.28.0 + - jinja2 = 2.11.2 + - networkx = 2.5 + - bcftools = 1.11 + - samtools = 1.11 - bwa = 0.7.17 - - pysam = 0.16.0 - - cutadapt = 2.4 - - bedtools = 2.28 - - STAR = 2.7.2a - - trim-galore = 0.6.4 + - pysam = 0.16.0.1 + - cutadapt = 3.0 + - bedtools = 2.29.2 + - STAR = 2.7.6a + - trim-galore = 0.6.6 - pip = 20.2.4 - - pybedtools = 0.8.0 - - numpy = 1.17.1 - - pandas = 0.25.1 - - pybedtools = 0.8.0 - - numpydoc = 0.9.1 - - sphinx = 2.2.0 - - matplotlib = 3.1.0 - - docutils = 0.15.2 + - pybedtools = 0.8.1 + - numpy = 1.19.4 + - pandas = 1.1.4 + - numpydoc = 1.1.0 + - sphinx = 3.3.1 + - matplotlib = 3.3.3 + - docutils = 0.16 - sphinx-releases = 1.6.1 From 7686aafbbc110a04cfcb0a3bd624b2393123642c Mon Sep 17 00:00:00 2001 From: Gregor Rot Date: Fri, 20 Nov 2020 10:59:04 +0100 Subject: [PATCH 13/26] Fixed "git clone" of iCount snakemake branch --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5eb6024..0a716f6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,8 +38,8 @@ RUN chown -R icuser.icuser /home/icuser USER icuser WORKDIR /home/icuser RUN mkdir /home/icuser/storage -#RUN git clone https://github.com/tomazc/iCount.git --branch snakemake -COPY . /home/icuser/iCount +RUN git clone https://github.com/tomazc/iCount.git --branch snakemake +#COPY . /home/icuser/iCount RUN conda create -c conda-forge -c bioconda -n iCount_pipeline3 -y RUN conda init bash From 888ab000917497929f84a95cf3a1253ec38b0be4 Mon Sep 17 00:00:00 2001 From: Gregor Rot Date: Fri, 20 Nov 2020 16:30:53 +0100 Subject: [PATCH 14/26] Updated docker file to install iCount with conda --- Dockerfile | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0a716f6..f772cb6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,5 @@ -FROM continuumio/anaconda3 +FROM continuumio/anaconda3:2020.07 + MAINTAINER Tomaz Curk # suppress prompt for tzdata @@ -8,9 +9,10 @@ ENV TZ=Europe/Ljubljana # thanks to https://github.com/bschiffthaler/ngs/blob/master/base/Dockerfile # and https://github.com/AveraSD/ngs-docker-star/blob/master/Dockerfile -SHELL ["/bin/bash", "--login", "-c"] - RUN apt-get install -y vim +RUN apt-get install -y nano + +SHELL ["/bin/bash", "--login", "-c"] RUN conda update -n base -c defaults conda -y RUN conda install -c conda-forge mamba -y @@ -19,27 +21,17 @@ RUN conda config --add channels defaults RUN conda config --add channels bioconda RUN conda config --add channels conda-forge -### samtools -#RUN conda install -c bioconda -y "samtools>=1.10" - -### bedtools, need at least version 2.26, where merge command reports strand -# RUN conda install -c bioconda -y bedtools - -### RNA-star -# RUN conda install -c bioconda -y star - ################# #### iCount ################# RUN useradd -m -d /home/icuser icuser +ADD . /home/icuser/iCount RUN chown -R icuser.icuser /home/icuser USER icuser WORKDIR /home/icuser RUN mkdir /home/icuser/storage -RUN git clone https://github.com/tomazc/iCount.git --branch snakemake -#COPY . /home/icuser/iCount RUN conda create -c conda-forge -c bioconda -n iCount_pipeline3 -y RUN conda init bash From a74a568406dffb556a4da7288d60b4272de43cec Mon Sep 17 00:00:00 2001 From: Faitero Date: Thu, 4 Feb 2021 15:01:11 +0100 Subject: [PATCH 15/26] Prepare genome individual chromosomes --- iCount/snakemake/rules/common.smk | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/iCount/snakemake/rules/common.smk b/iCount/snakemake/rules/common.smk index 57199b0..7e10da2 100644 --- a/iCount/snakemake/rules/common.smk +++ b/iCount/snakemake/rules/common.smk @@ -143,6 +143,12 @@ def custom_fasta(wildcards): def custom_gtf(wildcards): return config['custom_genome'][wildcards]['annotation'] +def download_chromosome(genome_fasta, source, genome, release, chromosomes): + if (config['chromosomes'] != "None"): + return ("iCount genome --genome {0} --chromosomes {4} --source {1} {2} {3}".format(genome_fasta, source, genome, release, chromosomes)) + else: + return ("iCount genome --genome {0} --source {1} {2} {3}".format(genome_fasta, source, genome, release, chromosomes)) + # Function from icount (call it!!) def decompress_to_tempfile(fname, context='misc'): @@ -202,3 +208,37 @@ def get_segment_regions(wildcards): def get_templates_dir(wildcards): return ("{0}/{1}/segment/".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) + +# Sig xlsites helper functions + +def is_empty(fname): + print(fname + " file is empty.") + return os.stat(str(fname)).st_size == 0 + +# Create a new empty file. +def createNewFile(fname): + file_object = open(fname, 'w') + # file_object.write('File is created.') + print(fname + " has been created. ") + + +# Group helper functions + +def files2group(wildcards): + barcode_group = samples.loc[samples['group'] == wildcards.group, "full_barcode"].values[0:].tolist() + xlsites_list=[] + for barcode in barcode_group: + xlsites_list.append("{project}/xlsites/{barcode}/{barcode}.unique.xl.bed".format(project=config['project'], barcode=barcode)) + return (xlsites_list) + +def bedgraph_group_description(wildcards): + return ("{project}_group_{group}_{protein}_{method}_{mapto}".format(project=config['project'], group=wildcards.group, mapto=samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0], method=samples.loc[samples['group'] == wildcards.group, "method"].unique()[0], protein=samples.loc[samples['group'] == wildcards.group, "protein"].unique()[0], cells_tissue=samples.loc[samples['group'] == wildcards.group, "cells_tissue"].unique()[0], condition=samples.loc[samples['group'] == wildcards.group, "condition"].unique()[0])) + +def get_group_segment_path(wildcards): + return ("{0}/{1}/segment/{1}_segment.gtf".format(config['genomes_path'], samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0])) + +def get_group_templates_dir(wildcards): + return ("{0}/{1}/segment/".format(config['genomes_path'], samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0])) + +def get_group_landmark_path(wildcards): + return ("{0}/{1}/segment/landmarks.bed.gz".format(config['genomes_path'], samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0])) From 12104ba6f39ad200cd50f6aec58026eff2f136f5 Mon Sep 17 00:00:00 2001 From: Faitero Date: Thu, 4 Feb 2021 15:21:58 +0100 Subject: [PATCH 16/26] Prepare genome individual chromosomes --- iCount/snakemake/rules/prepare_genome.smk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iCount/snakemake/rules/prepare_genome.smk b/iCount/snakemake/rules/prepare_genome.smk index 4f71f4f..f91d1c6 100644 --- a/iCount/snakemake/rules/prepare_genome.smk +++ b/iCount/snakemake/rules/prepare_genome.smk @@ -7,7 +7,7 @@ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* Check for custom genomes *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# # Capture iCount available genomes -# --chromosomes 21 MT +# --chromosomes 21 MT Include Chromosome from config file!! # -r 88 args = ["iCount species --source ensembl -r 88"] # if config["release"]: @@ -44,7 +44,7 @@ rule download_genome: if GENOME in download_genomes: print ("Downloading iCount available genome:", GENOME) print ("Downloading genomes could take some time depending on your conection") - shell("iCount genome --genome {output.genome_fasta} --chromosomes {params.chromosomes} --source {params.source} {wildcards.genome} {params.release} --chromosomes 20") # For testing include --chromosomes MT 19 + shell (download_chromosome(output.genome_fasta, params.source, GENOME, params.release, params.chromosomes)) shell("iCount annotation --annotation {output.gtf} --source {params.source} {wildcards.genome} {params.release}") elif GENOME in config['custom_genome'].keys(): From 59728505c28408a4815d3cb923da566a49ca2aa1 Mon Sep 17 00:00:00 2001 From: Faitero Date: Fri, 5 Feb 2021 10:20:48 +0100 Subject: [PATCH 17/26] Prepare genome --- iCount/examples/config_synthetic.yaml | 24 ++++++++++--------- iCount/snakemake/icount_snakemake.smk | 25 +++++++++++++++----- iCount/snakemake/rules/group.smk | 26 +-------------------- iCount/snakemake/rules/map_reads.smk | 1 - iCount/snakemake/rules/sig_xlsites.smk | 11 --------- iCount/snakemake/schemas/config.schema.yaml | 9 +++++++ 6 files changed, 42 insertions(+), 54 deletions(-) diff --git a/iCount/examples/config_synthetic.yaml b/iCount/examples/config_synthetic.yaml index 4f2fa0e..8e840a4 100755 --- a/iCount/examples/config_synthetic.yaml +++ b/iCount/examples/config_synthetic.yaml @@ -40,7 +40,7 @@ raw_fastq_file: "data/merge_chr20_2652426_2664336_GTAAC.fq.gz" # Set the desidred output of the pipeline. ## Completeness output -# If "minimum" is set only crosslinks, significant crosslinks and clusters will be calculated. +# If "minimum" is set, only crosslinks, significant crosslinks and clusters will be calculated. # On the other hand if marked as "complete" all the crosslinks and significant crosslinks will be annotated # and bedgraph output file will be generated (default). completeness_output: "complete" @@ -88,11 +88,12 @@ genomes_path: "iCount_genomes" # Annotation release on ensembl !!! Update to latest release release: 88 -# Processing the entire genome is computationally very expensive. For this reason, we are limiting the tutorial example -# to chromosomes 21 and MT. !!!!! Include validation -chromosomes: 21 MT -# Source of data. Only ENSEMBL or GENCODE are available (default: gencode)!!!!! Include validation!! gencode doesn't work -source: ensembl +# If given, do not download the whole genome, but listed chromosomes only. (default: None)" +# chromosomes: "20, MT" +chromosomes: "None" + +# Source of data. Only ensembl or gencode are available (default: ensembl) !! gencode doesn't work +source: "ensembl" # Alternatively, you can use you custom genome including acronym_name, fasta sequence (genome_fasta) and gtf file # (annotation) paths @@ -107,9 +108,7 @@ custom_genome: genome_fasta: "custom/custom2.fa.gz" # gtf file with transcripts annotation: "custom/custom2.gtf.gz" -# Arabidopsis_thaliana: -# genome_fasta: "custom/Arabidopsis_thaliana/Arabidopsis_thaliana.TAIR10.dna_sm.toplevel.fa.gz" -# annotation: "custom/Arabidopsis_thaliana/Arabidopsis_thaliana_mod.gtf" + # Custom genome example annotation. Name directory, fasta sequence and annotation using the same acromyn ("hg19") # as in the example: #custom_genome: @@ -279,10 +278,13 @@ logdir: "logs_cluster" #===================== Workflow integrity check ==========================# # Create integrity and reproducibility test, boolean 'true' or 'false' (default: false). -create_integrity_test: false +create_integrity_test: true # Check integrity and reproducibility test, boolean 'true' or 'false' (default: false). -integrity_test_check: true +integrity_test_check: false +# Processing the entire genome is computationally very expensive. For this reason, we are limiting the tutorial example +# to chromosome 20 (default: 20). +chromosomes: 20 ### Project test directory name #project: "integrity_test" diff --git a/iCount/snakemake/icount_snakemake.smk b/iCount/snakemake/icount_snakemake.smk index 8123e85..c3d10c7 100644 --- a/iCount/snakemake/icount_snakemake.smk +++ b/iCount/snakemake/icount_snakemake.smk @@ -2,7 +2,7 @@ # iCount Snakemake workflow #==============================================================================# # Authors # Igor Ruiz de los Mozos, Charlotte Capitanchik, Tomaz Curk -# Last updated: November 2020 +# Last updated: January 2021 # Install Locally @@ -32,13 +32,19 @@ # Check the install # iCount +# Install Locally with docker +#================ +# docker build -t icount . +# docker run --user icuser -ti icount bash --login + + # Run Locally #================ # Step two: To run locally use command: # snakemake -k -p --snakefile demultiplex_snakefile.smk --use-conda # snakemake -k -p --cores 4 --snakefile demultiplex_snakefile.smk --use-conda -# snakemake -k -p --cores 4 --snakefile '/Users/mozosi/Dropbox (UCL-MN Team)/GitHub/iCount/iCount/snakemake/icount_snakemake.smk' --use-conda --configfile config_synthetic.yaml +# snakemake -k -p --cores 4 --snakefile '/Users/mozosi/Dropbox (UCL-MN Team)/GitHub/iCount_pre_docker/iCount/snakemake/icount_snakemake.smk' --use-conda --configfile config_synthetic.yaml # dag workflow # snakemake --snakefile demultiplex_snakefile.smk --use-conda --dag 2> /dev/null | dot -T png > workflow_bysample.png # snakemake --snakefile demultiplex_snakefile.smk --use-conda --rulegraph 2> /dev/null | dot -T png > workflow.png @@ -63,6 +69,13 @@ # cd /camp/lab/ulej/working/Igor/Programs # pip install ./iCount/ +# Install CEITEC +#================ +# module add anaconda3-2019.10 +# git clone https://github.com/tomazc/iCount.git --branch snakemake +# conda env create -n iCount_pipeline2 --file ~/Programs/iCount/conda_iCount.yaml +# source activate iCount_pipeline2 + # Run Cluster #================ # To run in a cluster use command: @@ -123,10 +136,10 @@ else: samples["full_barcode"] = samples[cols].apply(lambda x: '_'.join(x.dropna()), axis=1) samples=samples.set_index(["full_barcode"], drop = False) -# Print project -print("Procesing project:", config['project'], "\n") -# Print Sample annotation -print ("Sample annotation:", samples, "\n"), +# # Print project +# print("Procesing project:", config['project'], "\n") +# # Print Sample annotation +# print ("Sample annotation:", samples, "\n"), diff --git a/iCount/snakemake/rules/group.smk b/iCount/snakemake/rules/group.smk index 70dd445..aabc6dd 100644 --- a/iCount/snakemake/rules/group.smk +++ b/iCount/snakemake/rules/group.smk @@ -2,13 +2,6 @@ # Group analysis #==============================================================================# -def files2group(wildcards): - barcode_group = samples.loc[samples['group'] == wildcards.group, "full_barcode"].values[0:].tolist() - xlsites_list=[] - for barcode in barcode_group: - xlsites_list.append("{project}/xlsites/{barcode}/{barcode}.unique.xl.bed".format(project=config['project'], barcode=barcode)) - return (xlsites_list) - rule group: input: @@ -22,11 +15,6 @@ rule group: iCount group {output.group} {input} """ -def bedgraph_group_description(wildcards): - return ("{project}_group_{group}_{protein}_{method}_{mapto}".format(project=config['project'], group=wildcards.group, mapto=samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0], method=samples.loc[samples['group'] == wildcards.group, "method"].unique()[0], protein=samples.loc[samples['group'] == wildcards.group, "protein"].unique()[0], cells_tissue=samples.loc[samples['group'] == wildcards.group, "cells_tissue"].unique()[0], condition=samples.loc[samples['group'] == wildcards.group, "condition"].unique()[0])) - - - rule group_bedgraph: input: group_xlsites="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", @@ -45,12 +33,6 @@ rule group_bedgraph: -def get_group_segment_path(wildcards): - return ("{0}/{1}/segment/{1}_segment.gtf".format(config['genomes_path'], samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0])) - -def get_group_templates_dir(wildcards): - return ("{0}/{1}/segment/".format(config['genomes_path'], samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0])) - rule annotate_group_xlsites: input: group_xlsites="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed" @@ -89,9 +71,6 @@ rule summary_group: mv {params.group_rename_subtype} {output.group_subtype} """ -def get_group_segment_path(wildcards): - return ("{0}/{1}/segment/{1}_segment.gtf".format(config['genomes_path'], samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0])) - rule group_sig_xlsites: input: group_xlsites = "{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", @@ -149,15 +128,12 @@ rule summary_group_sig: createNewFile(output.group_type) createNewFile(output.group_subtype) else: - shell("iCount summary --templates_dir {params.templates_dir} {params.segment} {input.sig_xlsites} {params.out_dir}") + shell("iCount summary --templates_dir {params.templates_dir} {params.segment} {input.group_sig_xlsites} {params.out_dir}") shell("mv {params.group_rename_gene} {output.group_gene}") shell("mv {params.group_rename_type} {output.group_type}") shell("mv {params.group_rename_subtype} {output.group_subtype}") -def get_group_landmark_path(wildcards): - return ("{0}/{1}/segment/landmarks.bed.gz".format(config['genomes_path'], samples.loc[samples['group'] == wildcards.group, "mapto"].unique()[0])) - rule group_clusters: input: group_xlsites="{project}/groups/{group}/xlsites/{group}.group.unique.xl.bed", diff --git a/iCount/snakemake/rules/map_reads.smk b/iCount/snakemake/rules/map_reads.smk index 8797bdc..6258775 100644 --- a/iCount/snakemake/rules/map_reads.smk +++ b/iCount/snakemake/rules/map_reads.smk @@ -3,7 +3,6 @@ #==============================================================================# - rule map_reads: input: trimmed_reads="{project}/trimmed/demux_{barcode}_qtrimmed.fastq.gz", diff --git a/iCount/snakemake/rules/sig_xlsites.smk b/iCount/snakemake/rules/sig_xlsites.smk index a4f6a53..0a288fd 100644 --- a/iCount/snakemake/rules/sig_xlsites.smk +++ b/iCount/snakemake/rules/sig_xlsites.smk @@ -29,17 +29,6 @@ rule sig_xlsites: # iCount peaks iCount_genomes/homo_sapiens/segment/regions.gtf.gz synthetic_chr20/xlsites/NNNNGTAACNNN_NNAGG/NNNNGTAACNNN_NNAGG.unique.xl.bed synthetic_chr20/sig_xlsites/NNNNGTAACNNN_NNAGG/NNNNGTAACNNN_NNAGG.sig_sites.bed --scores synthetic_chr20/sig_xlsites/NNNNGTAACNNN_NNAGG/NNNNGTAACNNN_NNAGG.scores.tsv --features gene --group_by gene_id --half_window 3 --fdr 0.05 --perms 100 --rnd_seed 42 --file_logpath synthetic_chr20/logs/sig_xlsites/NNNNGTAACNNN_NNAGG.xlsites.log --results_file synthetic_chr20/sig_xlsites/NNNNGTAACNNN_NNAGG/NNNNGTAACNNN_NNAGG.sig_sites_metrics.txt -def is_empty(fname): - print(fname + " file is empty.") - return os.stat(str(fname)).st_size == 0 - - -# Create a new empty file. -def createNewFile(fname): - file_object = open(fname, 'w') - # file_object.write('File is created.') - print(fname + " has been created. ") - rule annotate_sig_xlsites: input: diff --git a/iCount/snakemake/schemas/config.schema.yaml b/iCount/snakemake/schemas/config.schema.yaml index 76c76ce..4d2b615 100644 --- a/iCount/snakemake/schemas/config.schema.yaml +++ b/iCount/snakemake/schemas/config.schema.yaml @@ -38,6 +38,15 @@ properties: minimum: 59 maximum: 88 default: 88 + chromosomes: + type: ["null", "integer", "string"] + description: "If given, do not download the whole genome, but listed chromosomes only. (default: None)" + default: "None" + source: + type: string + description: "Source of data. Only ensembl or gencode are available (default: ensembl)" + enum: ["ensembl", "gencode"] + default: "ensembl" custom_genome: type: object description: "Alternatively, you can use you custom genome including acronym_name, fasta sequence (genome_fasta) From 8cc585355340e601fb91a16ff64771460d2401f3 Mon Sep 17 00:00:00 2001 From: Faitero Date: Fri, 5 Feb 2021 13:40:54 +0100 Subject: [PATCH 18/26] Fix demultiplex bug --- iCount/snakemake/icount_snakemake.smk | 2 +- iCount/snakemake/rules/demultiplex.smk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/iCount/snakemake/icount_snakemake.smk b/iCount/snakemake/icount_snakemake.smk index c3d10c7..989eaf8 100644 --- a/iCount/snakemake/icount_snakemake.smk +++ b/iCount/snakemake/icount_snakemake.smk @@ -128,7 +128,7 @@ if len(samples["adapter_3"].unique().tolist()) > 1: sys.exit("iCount pipeline only accepts a unique 3' adapter") elif len(samples.index) != len(samples["sample_name"].unique().tolist()): - sys.exit("iCount pipeline only accepts a unique sample names") + sys.exit("iCount pipeline only accepts unique sample names") # Merge 5'barcode and 3'barcode to create a table index (full barcode) else: diff --git a/iCount/snakemake/rules/demultiplex.smk b/iCount/snakemake/rules/demultiplex.smk index 4aeb2fa..a888b90 100644 --- a/iCount/snakemake/rules/demultiplex.smk +++ b/iCount/snakemake/rules/demultiplex.smk @@ -20,7 +20,7 @@ rule demultiplex: input: fastq_file=config['raw_fastq_file'], output: - #expand("demultiplexed/demux_{barcode}.fastq.gz", barcode=samples.index), + expand("demultiplexed/demux_{barcode}.fastq.gz", barcode=samples.index), "demultiplexed/demux_nomatch5.fastq.gz" params: adapter3=samples["adapter_3"].unique().tolist(), From 802116964fbbbd21b67ad6a9f923448ada375600 Mon Sep 17 00:00:00 2001 From: Gregor Rot Date: Fri, 12 Feb 2021 16:53:39 +0100 Subject: [PATCH 19/26] STAR folder mkdir when generating genome index --- iCount/snakemake/rules/prepare_genome.smk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iCount/snakemake/rules/prepare_genome.smk b/iCount/snakemake/rules/prepare_genome.smk index f91d1c6..a01827b 100644 --- a/iCount/snakemake/rules/prepare_genome.smk +++ b/iCount/snakemake/rules/prepare_genome.smk @@ -78,6 +78,7 @@ rule indexstar_genome: directory("{genomes_path}/{genome}/star_index/"), shell: """ + mkdir {output} iCount indexstar --overhang {params.overhang} --annotation {input.gtf} \ --threads {threads} --genome_sasparsed 2 {input.genome_fasta} {output} """ @@ -92,6 +93,5 @@ rule segment: landmarks="{genomes_path}/{genome}/segment/landmarks.bed.gz", shell: """ - iCount segment {input.gtf} {output.segment} {input.genome_fai} + iCount segment {input.gtf} {output.segment} {input.genome_fai} """ - From fc06ad13895dd497d87318569d8c1e17b3d25c32 Mon Sep 17 00:00:00 2001 From: Faitero Date: Tue, 16 Feb 2021 11:11:45 +0100 Subject: [PATCH 20/26] solve create STAR index directory --- iCount/snakemake/icount_snakemake.smk | 4 ++++ iCount/snakemake/rules/prepare_genome.smk | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/iCount/snakemake/icount_snakemake.smk b/iCount/snakemake/icount_snakemake.smk index 989eaf8..d6dbfdc 100644 --- a/iCount/snakemake/icount_snakemake.smk +++ b/iCount/snakemake/icount_snakemake.smk @@ -48,6 +48,8 @@ # dag workflow # snakemake --snakefile demultiplex_snakefile.smk --use-conda --dag 2> /dev/null | dot -T png > workflow_bysample.png # snakemake --snakefile demultiplex_snakefile.smk --use-conda --rulegraph 2> /dev/null | dot -T png > workflow.png +# snakemake -k -p --unlock --cores 4 --snakefile '/Users/mozosi/Dropbox (UCL-MN Team)/GitHub/iCount_pre_docker/iCount/snakemake/icount_snakemake.smk' --use-conda --configfile config_synthetic.yaml +# config_Blazek.yaml # Install Cluster #================ @@ -169,6 +171,8 @@ rule all: expand("{project}/qc/fastqc/raw_fastq_file_fastqc.html", project=config['project']), expand("{project}/qc/fastqc/raw_fastq_file_fastqc.zip", project=config['project']), + expand("{genomes_path}/{genome}/star_index/", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), + expand("{project}/qc/fastqc/{barcode}_fastqc.html", project=config['project'], barcode=samples.index), expand("{project}/qc/fastqc/{barcode}_fastqc.zip", project=config['project'], barcode=samples.index), diff --git a/iCount/snakemake/rules/prepare_genome.smk b/iCount/snakemake/rules/prepare_genome.smk index a01827b..22137c3 100644 --- a/iCount/snakemake/rules/prepare_genome.smk +++ b/iCount/snakemake/rules/prepare_genome.smk @@ -78,7 +78,7 @@ rule indexstar_genome: directory("{genomes_path}/{genome}/star_index/"), shell: """ - mkdir {output} + # mkdir {output} iCount indexstar --overhang {params.overhang} --annotation {input.gtf} \ --threads {threads} --genome_sasparsed 2 {input.genome_fasta} {output} """ From c06e92747e03713c450997ce98f7b8e2855f9b3f Mon Sep 17 00:00:00 2001 From: Faitero Date: Tue, 16 Feb 2021 11:54:56 +0100 Subject: [PATCH 21/26] Prepare genome mkdir STAR index directory --- iCount/snakemake/rules/prepare_genome.smk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iCount/snakemake/rules/prepare_genome.smk b/iCount/snakemake/rules/prepare_genome.smk index 22137c3..a01827b 100644 --- a/iCount/snakemake/rules/prepare_genome.smk +++ b/iCount/snakemake/rules/prepare_genome.smk @@ -78,7 +78,7 @@ rule indexstar_genome: directory("{genomes_path}/{genome}/star_index/"), shell: """ - # mkdir {output} + mkdir {output} iCount indexstar --overhang {params.overhang} --annotation {input.gtf} \ --threads {threads} --genome_sasparsed 2 {input.genome_fasta} {output} """ From 85726327a1bd89412c9d666f57c4c6a1a207285e Mon Sep 17 00:00:00 2001 From: Faitero Date: Tue, 16 Feb 2021 12:48:10 +0100 Subject: [PATCH 22/26] More on missing output directories --- iCount/snakemake/icount_snakemake.smk | 4 ++-- iCount/snakemake/rules/map_reads.smk | 10 ++++++---- iCount/snakemake/rules/prepare_genome.smk | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/iCount/snakemake/icount_snakemake.smk b/iCount/snakemake/icount_snakemake.smk index d6dbfdc..a94d63e 100644 --- a/iCount/snakemake/icount_snakemake.smk +++ b/iCount/snakemake/icount_snakemake.smk @@ -143,7 +143,7 @@ else: # # Print Sample annotation # print ("Sample annotation:", samples, "\n"), - +# workdir: "path/to/workdir" #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* Final outputs *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# @@ -171,7 +171,7 @@ rule all: expand("{project}/qc/fastqc/raw_fastq_file_fastqc.html", project=config['project']), expand("{project}/qc/fastqc/raw_fastq_file_fastqc.zip", project=config['project']), - expand("{genomes_path}/{genome}/star_index/", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), + #expand("{genomes_path}/{genome}/star_index/", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), expand("{project}/qc/fastqc/{barcode}_fastqc.html", project=config['project'], barcode=samples.index), expand("{project}/qc/fastqc/{barcode}_fastqc.zip", project=config['project'], barcode=samples.index), diff --git a/iCount/snakemake/rules/map_reads.smk b/iCount/snakemake/rules/map_reads.smk index 6258775..f3dce6d 100644 --- a/iCount/snakemake/rules/map_reads.smk +++ b/iCount/snakemake/rules/map_reads.smk @@ -7,17 +7,19 @@ rule map_reads: input: trimmed_reads="{project}/trimmed/demux_{barcode}_qtrimmed.fastq.gz", gtf = get_gtf_path, - star_index = get_star_index_folder, + #star_index = get_star_index_folder, + star_index = rules.indexstar_genome.output output: - "{project}/mapped/{barcode}/Aligned.sortedByCoord.out.bam" + outdir=directory("{project}/mapped/{barcode}"), + aligned="{project}/mapped/{barcode}/Aligned.sortedByCoord.out.bam" params: - outdir=directory("{project}/mapped/{barcode}/"), + #outdir=directory("{project}/mapped/{barcode}/"), multimax=config['multimax'], log: "{project}/logs/mapstar/{barcode}_mapstar.log" shell: """ iCount mapstar --annotation {input.gtf} --multimax {params.multimax} \ - {input.trimmed_reads} {input.star_index} {params.outdir} + {input.trimmed_reads} {input.star_index} {output.outdir} """ diff --git a/iCount/snakemake/rules/prepare_genome.smk b/iCount/snakemake/rules/prepare_genome.smk index a01827b..af29253 100644 --- a/iCount/snakemake/rules/prepare_genome.smk +++ b/iCount/snakemake/rules/prepare_genome.smk @@ -75,10 +75,10 @@ rule indexstar_genome: params: overhang=config['overhang'], output: - directory("{genomes_path}/{genome}/star_index/"), + directory("{genomes_path}/{genome}/star_index"), shell: """ - mkdir {output} + # mkdir {output} iCount indexstar --overhang {params.overhang} --annotation {input.gtf} \ --threads {threads} --genome_sasparsed 2 {input.genome_fasta} {output} """ From b23110bfb754a35d6273776555c0ccc74747ba48 Mon Sep 17 00:00:00 2001 From: Faitero Date: Tue, 16 Feb 2021 13:34:30 +0100 Subject: [PATCH 23/26] Solving missing directories on map_reads and prepare_genome rules --- iCount/snakemake/icount_snakemake.smk | 3 ++- iCount/snakemake/rules/common.smk | 4 ++-- iCount/snakemake/rules/map_reads.smk | 11 ++++++----- iCount/snakemake/rules/prepare_genome.smk | 5 +++-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/iCount/snakemake/icount_snakemake.smk b/iCount/snakemake/icount_snakemake.smk index a94d63e..217a350 100644 --- a/iCount/snakemake/icount_snakemake.smk +++ b/iCount/snakemake/icount_snakemake.smk @@ -171,10 +171,11 @@ rule all: expand("{project}/qc/fastqc/raw_fastq_file_fastqc.html", project=config['project']), expand("{project}/qc/fastqc/raw_fastq_file_fastqc.zip", project=config['project']), - #expand("{genomes_path}/{genome}/star_index/", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), + # expand("{genomes_path}/{genome}/star_index", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), expand("{project}/qc/fastqc/{barcode}_fastqc.html", project=config['project'], barcode=samples.index), expand("{project}/qc/fastqc/{barcode}_fastqc.zip", project=config['project'], barcode=samples.index), + expand("{genomes_path}/{genome}/segment/regions.gtf.gz", genome=samples["mapto"].unique(), genomes_path=config['genomes_path']), #expand("{project}/trimmed/demux_{barcode}_trimmed.fastq.gz", project=config['project'], barcode=samples.index), diff --git a/iCount/snakemake/rules/common.smk b/iCount/snakemake/rules/common.smk index 7e10da2..d3ed330 100644 --- a/iCount/snakemake/rules/common.smk +++ b/iCount/snakemake/rules/common.smk @@ -193,8 +193,8 @@ def bedgraph_header(wildcards): def get_gtf_path(wildcards): return ("{0}/{1}/{1}.gtf.gz".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) -def get_star_index_path(wildcards): - return ("{0}/{1}/star_index/SAindex".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) +# def get_star_index_path(wildcards): +# return ("{0}/{1}/star_index/SAindex".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) def get_star_index_folder(wildcards): return ("{0}/{1}/star_index/".format(config['genomes_path'], samples.loc[wildcards.barcode, "mapto"])) diff --git a/iCount/snakemake/rules/map_reads.smk b/iCount/snakemake/rules/map_reads.smk index f3dce6d..7cf83a4 100644 --- a/iCount/snakemake/rules/map_reads.smk +++ b/iCount/snakemake/rules/map_reads.smk @@ -7,19 +7,20 @@ rule map_reads: input: trimmed_reads="{project}/trimmed/demux_{barcode}_qtrimmed.fastq.gz", gtf = get_gtf_path, - #star_index = get_star_index_folder, - star_index = rules.indexstar_genome.output + star_index = get_star_index_folder, + # star_index = rules.indexstar_genome.output output: - outdir=directory("{project}/mapped/{barcode}"), + # outdir=directory("{project}/mapped/{barcode}"), aligned="{project}/mapped/{barcode}/Aligned.sortedByCoord.out.bam" params: - #outdir=directory("{project}/mapped/{barcode}/"), + outdir=directory("{project}/mapped/{barcode}/"), multimax=config['multimax'], log: "{project}/logs/mapstar/{barcode}_mapstar.log" shell: """ + mkdir -p {params.outdir} iCount mapstar --annotation {input.gtf} --multimax {params.multimax} \ - {input.trimmed_reads} {input.star_index} {output.outdir} + {input.trimmed_reads} {input.star_index} {params.outdir} """ diff --git a/iCount/snakemake/rules/prepare_genome.smk b/iCount/snakemake/rules/prepare_genome.smk index af29253..a738939 100644 --- a/iCount/snakemake/rules/prepare_genome.smk +++ b/iCount/snakemake/rules/prepare_genome.smk @@ -75,10 +75,10 @@ rule indexstar_genome: params: overhang=config['overhang'], output: - directory("{genomes_path}/{genome}/star_index"), + directory("{genomes_path}/{genome}/star_index/"), shell: """ - # mkdir {output} + mkdir -p {output} iCount indexstar --overhang {params.overhang} --annotation {input.gtf} \ --threads {threads} --genome_sasparsed 2 {input.genome_fasta} {output} """ @@ -91,6 +91,7 @@ rule segment: output: segment="{genomes_path}/{genome}/segment/{genome}_segment.gtf", landmarks="{genomes_path}/{genome}/segment/landmarks.bed.gz", + regions="{genomes_path}/{genome}/segment/regions.gtf.gz", shell: """ iCount segment {input.gtf} {output.segment} {input.genome_fai} From a95907dc70e68a9b3cd016ab4c8dd48b8ec85f10 Mon Sep 17 00:00:00 2001 From: Faitero Date: Tue, 16 Feb 2021 16:28:54 +0100 Subject: [PATCH 24/26] Prepare genome --- iCount/snakemake/rules/map_reads.smk | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/iCount/snakemake/rules/map_reads.smk b/iCount/snakemake/rules/map_reads.smk index 7cf83a4..d74b653 100644 --- a/iCount/snakemake/rules/map_reads.smk +++ b/iCount/snakemake/rules/map_reads.smk @@ -3,6 +3,7 @@ #==============================================================================# + rule map_reads: input: trimmed_reads="{project}/trimmed/demux_{barcode}_qtrimmed.fastq.gz", @@ -13,13 +14,13 @@ rule map_reads: # outdir=directory("{project}/mapped/{barcode}"), aligned="{project}/mapped/{barcode}/Aligned.sortedByCoord.out.bam" params: - outdir=directory("{project}/mapped/{barcode}/"), + outdir="{project}/mapped/{barcode}/", multimax=config['multimax'], log: "{project}/logs/mapstar/{barcode}_mapstar.log" shell: """ - mkdir -p {params.outdir} + mkdir -p {params.outdir} iCount mapstar --annotation {input.gtf} --multimax {params.multimax} \ {input.trimmed_reads} {input.star_index} {params.outdir} """ From 729d92bb6a9b1f5dd6985c789442b2c3d70c1d2e Mon Sep 17 00:00:00 2001 From: Gregor Rot Date: Tue, 16 Feb 2021 17:05:14 +0100 Subject: [PATCH 25/26] Updated snakemake to 5.32.2 version --- conda_iCount.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda_iCount.yaml b/conda_iCount.yaml index c976fd7..858dc4c 100644 --- a/conda_iCount.yaml +++ b/conda_iCount.yaml @@ -5,7 +5,7 @@ channels: dependencies: - python = 3.8.6 - - snakemake = 5.28.0 + - snakemake = 5.32.2 - jinja2 = 2.11.2 - networkx = 2.5 - bcftools = 1.11 From 538f69c54b60374954b084a16c7c7b1d00c01df3 Mon Sep 17 00:00:00 2001 From: Faitero Date: Tue, 16 Feb 2021 18:00:06 +0100 Subject: [PATCH 26/26] Real iCLIP data subset --- iCount/examples/config_real_CLIP_subset.yaml | 301 ++++++++++++++++++ .../examples/data/real_CLIP_subset.fastq.gz | Bin 0 -> 105487 bytes .../data/samples_real_CLIP_subset.txt | 7 + iCount/snakemake/icount_snakemake.smk | 3 + 4 files changed, 311 insertions(+) create mode 100755 iCount/examples/config_real_CLIP_subset.yaml create mode 100755 iCount/examples/data/real_CLIP_subset.fastq.gz create mode 100644 iCount/examples/data/samples_real_CLIP_subset.txt diff --git a/iCount/examples/config_real_CLIP_subset.yaml b/iCount/examples/config_real_CLIP_subset.yaml new file mode 100755 index 0000000..ad817c0 --- /dev/null +++ b/iCount/examples/config_real_CLIP_subset.yaml @@ -0,0 +1,301 @@ + +#==============================================================================# +# iCount snakemake Configuration file +# Generated at (system time) +#==============================================================================# + +#=============================== Project ==============================# +# Define name of the project. Output files will be saved under "project" folder. + +## Project directory name +project: "project_real_CLIP_subset" + + +#=============================== Samples ==============================# +# Define which samples to map and which species to map to. +# Note that the path to the sample can be absolute or relative to the directory +# where the Snakefile is run. + +# Path or URL to sample sheet (tsv format, TAB separated columns: +# sample_name, method, protein, cells/tissue, condition, mapto, 5_barcode, +# 3_barcode, 3_adapter, linker, comments, group) + +# If the column "group" is present (which is optional), +# samples will be merged and output for the group calculated. + +# Sample table input accepts tsv or txt +#samples: "data/hnRNPC_reduced.tsv" +samples: "data/samples_real_CLIP_subset.txt" + + +#======================== Raw sequencing data =============================# + +# Raw fastq file to demultiplex and analyse +#raw_fastq_file: "data/hnRNPC.fq.gz" +# All CDK11 data +#raw_fastq_file: "data/CDK11_20150611_Bz1.fastq.gz" +# Subset of CDK11 +raw_fastq_file: "data/real_CLIP_subset.fastq.gz" +#raw_fastq_file: "data/merge_my_script.fq.gz" + + +#============================ Desired output ===========================# +# Set the desidred output of the pipeline. + +## Completeness output +# If "minimum" is set, only crosslinks, significant crosslinks and clusters will be calculated. +# On the other hand if marked as "complete" all the crosslinks and significant crosslinks will be annotated +# and bedgraph output file will be generated (default). +completeness_output: "minimum" + +## Grouped replicates completeness output +# If "minimum" is set only merged crosslinks, significant crosslinks and clusters will be calculated. +# On the other hand if marked as "complete" all merged the crosslinks, significant crosslinks will be annotated +# and bedgraph output file will be generated (default). +group_completeness_output: "minimum" + +# Transform crosslink 6bed to UCSC bedgraph (false or true; default false) +bedgraph_UCSC: false + + +#============================= Parameters =============================# +# Set the parameters for each step. +# These are set to defaults that are widely applicable to iCLIP analysis. + + +#~~~~~~~~~~~~~~~~ iCount genomes ~~~~~~~~~~~~~~~~# +## iCount genomes will be automatically downloaded from NCBI if any of the following accepted species terms are used. +# There are 87 species available: +# ailuropoda_melanoleuca,anas_platyrhynchos,ancestral_alleles,anolis_carolinensis,astyanax_mexicanus, +# bos_taurus,caenorhabditis_elegans,callithrix_jacchus,canis_familiaris,cavia_porcellus,chlorocebus_sabaeus, +# choloepus_hoffmanni,ciona_intestinalis,ciona_savignyi,danio_rerio,dasypus_novemcinctus,dipodomys_ordii, +# drosophila_melanogaster,echinops_telfairi,equus_caballus,erinaceus_europaeus,fasta,felis_catus, +# ficedula_albicollis,gadus_morhua,gallus_gallus,gasterosteus_aculeatus,gorilla_gorilla,homo_sapiens, +# ictidomys_tridecemlineatus,latimeria_chalumnae,lepisosteus_oculatus,loxodonta_africana,macaca_mulatta, +# macropus_eugenii,meleagris_gallopavo,microcebus_murinus,monodelphis_domestica,mus_musculus,mus_musculus_129s1svimj, +# mus_musculus_aj,mus_musculus_akrj,mus_musculus_balbcj,mus_musculus_c3hhej,mus_musculus_c57bl6nj,mus_musculus_casteij, +# mus_musculus_cbaj,mus_musculus_dba2j,mus_musculus_fvbnj,mus_musculus_lpj,mus_musculus_nodshiltj,mus_musculus_nzohlltj, +# mus_musculus_pwkphj,mus_musculus_wsbeij,mus_spretus_spreteij,mustela_putorius_furo,myotis_lucifugus,nomascus_leucogenys, +# ochotona_princeps,oreochromis_niloticus,ornithorhynchus_anatinus,oryctolagus_cuniculus,oryzias_latipes, +# ovis_aries,pan_troglodytes,papio_anubis,pelodiscus_sinensis,petromyzon_marinus,poecilia_formosa,pongo_abelii, +# procavia_capensis,pteropus_vampyrus,rattus_norvegicus,saccharomyces_cerevisiae,sarcophilus_harrisii,sorex_araneus, +# sus_scrofa,taeniopygia_guttata,takifugu_rubripes,tarsius_syrichta,tetraodon_nigroviridis,tupaia_belangeri, +# otolemur_garnettii, tursiops_truncatus,vicugna_pacos,xenopus_tropicalis,xiphophorus_maculatus + +# Update releases script and Icount annotation and genome doesn't accept "--releases" word +# Current version of iCount is tested to work with human and mouse genomes only. + +# Directory where all the genomes, STAR index and iCount segment files will be stored. +genomes_path: "iCount_genomes" + +# Annotation release on ensembl !!! Update to latest release +release: 88 + +# If given, do not download the whole genome, but listed chromosomes only. (default: None)" +# chromosomes: "20, MT" +chromosomes: "None" + +# Source of data. Only ensembl or gencode are available (default: ensembl) !! gencode doesn't work +source: "ensembl" + +# Alternatively, you can use you custom genome including acronym_name, fasta sequence (genome_fasta) and gtf file +# (annotation) paths +custom_genome: + hg19: + # Genomic fasta sequence + genome_fasta: "custom/custom.fa.gz" + # gtf file with transcripts + annotation: "custom/custom.gtf.gz" + hg38: + # Genomic fasta sequence + genome_fasta: "custom/custom2.fa.gz" + # gtf file with transcripts + annotation: "custom/custom2.gtf.gz" + +# Custom genome example annotation. Name directory, fasta sequence and annotation using the same acromyn ("hg19") +# as in the example: +#custom_genome: +# hg19: +# # Genomic fasta sequence +# genome_fasta: "iCount_genomes/hg19/hg19.fa.gz" +# # gtf file with transcripts +# annotation: "iCount_genomes/hg19/hg19.gtf.gz" + + +#~~~~~~~~~~~~~~~~ iCount demultiplex ~~~~~~~~~~~~~~~~# +# Split FASTQ file into separate files, one for each sample barcode. +# +# Saved FASTQ files contain sequences where sample barcode, random +# barcode, and adapter sequences are removed. Random barcode is moved into +# the header line, since it is needed in later steps (removing PCR duplicates +# and counting number of cross-link events). + +# Optional arguments: +# List of 3-prime end barcodes order as the 5' barcodes (default: None) +# barcodes3 [ ...] +# Number of tolerated mismatches when comparing barcodes (default: 1) +barcode_mismatches: 0 +# Minimum length of trimmed sequence to keep (default: 15) +demultiplex_minimum_length: 17 +# Minimum length of adapter on 3' end if demultiplexing also on +# 3' barcodes (default: 7) +min_adapter_overlap: 7 + + + +#~~~~~~~~~~~~~~~~ iCount reads quality trimm ~~~~~~~~~~~~~~~~# +# Remove adapter sequences and check quality from reads in FASTQ file. + +# Optional arguments: +# Trim low-quality bases before adapter removal (default: None) +qual_trim: 20 +# Discard trimmed reads that are shorter than `minimum_length` (default: None) +minimum_length: 17 +# Required ``overlap`` overlap between read and adapter for an +# adapter to be found (default: None) +overlap: +# Write reads that do not contain any adapter to this file path (default: None) +# untrimmed_output: +# Maximum allowed error rate (no. of errors divided by the length +# of the matching region) (default: None) +error_rate: 0.1 + + + +#~~~~~~~~~~~~~~~~ iCount STAR aligner ~~~~~~~~~~~~~~~~# +# Map reads to genome with STAR. + +# Optional arguments: +# Overhang for STAR index +overhang: 100 +# Number of allowed mismatches in STAR mapping (default: 2) !!!Check iMAps!!! +mismatches: 2 +# Number of allowed multiple hits (default: 10) +star_multimax: 10 +# Number of threads that STAR can use for generating index (default: 1) +index_threads: 8 + + + +#~~~~~~~~~~~~~~~~ iCount cross links ~~~~~~~~~~~~~~~~# +# Calculate cross links. Groupby should be selected in base of the CLIP technique + +# Assign score of a read to either 'start', 'middle' or 'end' nucleotide (default: start) +group_by: "start" +# Report number of 'cDNA' or number of 'reads' (default: cDNA) +quant: "cDNA" + +# Reads on same position with random barcode differing less than ``mismatches`` +# are merged together, if their ratio is below ratio_th (default: 1) + +mismatches: 1 +# Ignore hits with MAPQ < mapq_th (default: 0) +mapq_th: 0 +# Ignore reads, mapped to more than ``multimax`` places (default: 50) +multimax: 10 +# Reads with gaps less than gap_th are treated as if they have no gap (default: 4) +gap_th: 4 + +# Ratio between the number of reads supporting a randomer versus the +# number of reads supporting the most frequent randomer. All randomers +# above this threshold are accepted as unique. Remaining are merge +# with the rest, allowing for the specified number of mismatches (default: 0.1) +ratio_th: 0.1 +# Skip merging similar barcodes if number of distinct barcodes at +# position is higher that this (default: 10000) +max_barcodes: 10000 + + +#~~~~~~~~~~~~~~~~ iCount significant cross links ~~~~~~~~~~~~~~~~# +# Peaks (significant crosslinks low FDR) configuration +# Find positions with high density of cross-linked sites. +# More info on "iCount peaks --help" or check manual +# There are two typical variants of this analysis, depending on the parameters: +# +#* Gene-wise analysis, where: +# * features = gene +# * group_by = gene_id +# +#* Transcript-wise analysis where: +# * features = CDS, intron, UTR3, UTR5, ncRNA, intergenic +# * group_by = transcript_id +# [--merge_features] [--half_window] [--fdr] + +# Features from annotation to consider: gene, CDS, intron, UTR3, UTR5, ncRNA or intergenic. If None, ['gene'] is used. +# Sometimes, it is advised to use ['gene', 'intergenic'] (default: gene) +features: "gene" +# Attribute by which cross-link positions are grouped (default: gene_id) +group_by_sig_xl: "gene_id" +# Treat all features as one when grouping. Has no effect when only one feature is given in features parameter +# (default: False) +merge_features: false +# Half-window size (default: 3) +half_window: 3 +# FDR threshold (default: 0.05) +fdr: 0.05 +# Number of permutations when calculating random distribution (default: 100) +perms: 100 +# Seed for random generator (default: 42) +rnd_seed: 42 +# -prog, --report_progress +# Report analysis progress (default: False) +# -S , --stdout_log Threshold value (0-50) for logging to stdout. If 0, logging to stdout if turned OFF. +# -F , --file_log Threshold value (0-50) for logging to file. If 0, logging to file if turned OFF. +# -P , --file_logpath Path to log file. +# -M , --results_file File into which to store Metrics. +# + +#~~~~~~~~~~~~~~~~ iCount cluster of cross links ~~~~~~~~~~~~~~~~# +# Merge adjacent peaks into clusters and sum cross-links within clusters. +# Distance between two peaks to merge into same cluster (default: 20) +distance: 20 +# Distance between site and cluster to assign site to cluster (default: 3) +slop: 3 + + +#~~~~~~~~~~~~~~~~ iCount rnamaps generation ~~~~~~~~~~~~~~~~# +# Perform RNA-maps analysis. + +# What kind of plot to make. Choices are distribution, heatmaps and combined (default: combined) +plot_type: "combined" +# Plot heatmap for top_n best covered landmarks (default: 100) +top_n: 1000 +# Smoothing half-window. Average smoothing is used (default: 1) +smoothing: 1 +# Number of bins. Either nbins or binsize can be defined, but not both (default: 50) +nbins: 50 +# Bin size. Either nbins or binsize can be defined, but not both (default: None) +binsize: 0 +# Colormap to use. Any matplotlib colormap can be used (default: Greys) +colormap: "Greys" +# Output image format (default: png) +imgfmt: "png" + + +#======================== Computing Cluster =============================# + +# Log directory to store invidual jobs output and error when run on a cluster +logdir: "logs_cluster" + + +#===================== Workflow integrity check ==========================# + +# Create integrity and reproducibility test, boolean 'true' or 'false' (default: false). +create_integrity_test: true +# Check integrity and reproducibility test, boolean 'true' or 'false' (default: false). +integrity_test_check: false + +# Processing the entire genome is computationally very expensive. For this reason, we are limiting the tutorial example +# to chromosome 20 (default: 20). +chromosomes: 20 + +### Project test directory name +#project: "integrity_test" +# +## Sample table input accepts tsv or txt +##samples: "data/hnRNPC_reduced.tsv" +#samples: "data/samples_synthetic.txt" +# +## Raw fastq file to demultiplex and analyse +##raw_fastq_file: "data/hnRNPC.fq.gz" +#raw_fastq_file: "data/merge_my_script.fq.gz" diff --git a/iCount/examples/data/real_CLIP_subset.fastq.gz b/iCount/examples/data/real_CLIP_subset.fastq.gz new file mode 100755 index 0000000000000000000000000000000000000000..81b5ee8759113bcb271d027894fb6f8ee9c5450b GIT binary patch literal 105487 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

2L|hA)GFIGjt#u;|fh=oK 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 T{{R30|NjF34Mw$5t(OM?9os7; literal 0 HcmV?d00001 diff --git a/iCount/examples/data/samples_real_CLIP_subset.txt b/iCount/examples/data/samples_real_CLIP_subset.txt new file mode 100644 index 0000000..a01e37d --- /dev/null +++ b/iCount/examples/data/samples_real_CLIP_subset.txt @@ -0,0 +1,7 @@ +sample_name method protein cells_tissue condition mapto barcode_5 barcode_3 adapter_3 linker comments group New +CDK11b_nFLAG_1 iCLIP CDK11b 293Flp nFLAG homo_sapiens NNNTGGCNN AGATCGGAAGAGCGGTTCAG nFLAG +CDK11b_1del226_nFLAG_1 iCLIP CDK11b 293Flp 1del226 homo_sapiens NNNGGCGNN AGATCGGAAGAGCGGTTCAG 1del226 +CDK11b_nFLAG_noUV iCLIP CDK11b 293Flp noUV homo_sapiens NNNGGTTNN AGATCGGAAGAGCGGTTCAG noUV +CDK11b_nFLAG_2 iCLIP CDK11b 293Flp nFLAG homo_sapiens NNNAATANN AGATCGGAAGAGCGGTTCAG nFLAG +CDK11b_1del226_nFLAG_2 iCLIP CDK11b 293Flp 1del226 homo_sapiens NNNCCACNN AGATCGGAAGAGCGGTTCAG 1del226 +CDK11b_nFLAG_noAb iCLIP CDK11b 293Flp noAb homo_sapiens NNNTTGTNN AGATCGGAAGAGCGGTTCAG noAb \ No newline at end of file diff --git a/iCount/snakemake/icount_snakemake.smk b/iCount/snakemake/icount_snakemake.smk index 217a350..1950716 100644 --- a/iCount/snakemake/icount_snakemake.smk +++ b/iCount/snakemake/icount_snakemake.smk @@ -4,6 +4,9 @@ # Authors # Igor Ruiz de los Mozos, Charlotte Capitanchik, Tomaz Curk # Last updated: January 2021 +# Docker +# docker build -t icountsrc . +# docker run -i -t icountsrc # Install Locally #================