From 500a80fcea37b9890f919f9a7069f7bbdf940f56 Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Wed, 12 Apr 2023 17:17:34 +0400 Subject: [PATCH 01/19] Updated pre_imputation_check to not require snp ids in only rs or id:chr:ref:alt form --- scripts/client_background_test.py | 2 +- scripts/pre_imputation_check.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/client_background_test.py b/scripts/client_background_test.py index 2592e342..c7cc8579 100644 --- a/scripts/client_background_test.py +++ b/scripts/client_background_test.py @@ -53,7 +53,7 @@ def extract_cb_matches_from_local(input150_path: str, input18_path: str, local_m input150_samples_path = 'workdir/input150.samples' input18_samples_path = 'workdir/input18.samples' local_matches_path = 'workdir/relatives_ibis168.tsv' - cb_matches_path = 'workdir/relatives_cb_input18_150.tsv' + cb_matches_path = 'workdir/relatives_cb_input18_150_2.tsv' local_cb_matches_path = 'workdir/relatives_18_150_2.tsv' diff --git a/scripts/pre_imputation_check.py b/scripts/pre_imputation_check.py index c89a6c26..05b61aa9 100644 --- a/scripts/pre_imputation_check.py +++ b/scripts/pre_imputation_check.py @@ -69,7 +69,8 @@ def pre_imputation_check(params, reference): for i in open(fn_new): items = i.split() # id: (ref, alt) - a1_a2[items[1]] = (items[-2], items[-1]) + # %CHROM:%POS:%REF:%FIRST_ALT + a1_a2[f'{items[0]}:{items[3]}:{items[-2]}:{items[-1]}'] = (items[-2], items[-1]) # files to update bim chr_path = prefix + '.chr' From 7b309048a7c27dc1ff3854d54f886d4aab903319 Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Thu, 13 Apr 2023 12:56:38 +0400 Subject: [PATCH 02/19] Temp fixes in pre_imputation_check --- rules/relatives_ibis.smk | 2 +- scripts/client_background_test.py | 2 +- scripts/pre_imputation_check.py | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/rules/relatives_ibis.smk b/rules/relatives_ibis.smk index 2270085b..dc6ec512 100644 --- a/rules/relatives_ibis.smk +++ b/rules/relatives_ibis.smk @@ -96,7 +96,7 @@ rule ersa: TEMPFILE=ersa/temp_relatives.tsv rm -f $TEMPFILE rm -f {output} - + echo "ersa --avuncular-adj -ci -a {params.a} --dmax 14 -t {params.t} -l {params.l} {params.r} -th {params.th}" for input_file in $FILES; do ersa --avuncular-adj -ci -a {params.a} --dmax 14 -t {params.t} -l {params.l} \ {params.r} -th {params.th} $input_file -o $TEMPFILE |& tee {log} diff --git a/scripts/client_background_test.py b/scripts/client_background_test.py index c7cc8579..6cd2bb41 100644 --- a/scripts/client_background_test.py +++ b/scripts/client_background_test.py @@ -52,7 +52,7 @@ def extract_cb_matches_from_local(input150_path: str, input18_path: str, local_m if __name__ == '__main__': input150_samples_path = 'workdir/input150.samples' input18_samples_path = 'workdir/input18.samples' - local_matches_path = 'workdir/relatives_ibis168.tsv' + local_matches_path = 'workdir/relatives_ibis168_3.tsv' cb_matches_path = 'workdir/relatives_cb_input18_150_2.tsv' local_cb_matches_path = 'workdir/relatives_18_150_2.tsv' diff --git a/scripts/pre_imputation_check.py b/scripts/pre_imputation_check.py index 05b61aa9..f23dacdc 100644 --- a/scripts/pre_imputation_check.py +++ b/scripts/pre_imputation_check.py @@ -120,6 +120,8 @@ def pre_imputation_check(params, reference): elif matching == 3: flip_file.write(id_ + "\n") flip_swap += 1 + else: + print(id_) logging.info("Exclude: {} Keep: {}".format(exclude, in_ref - exclude)) logging.info("Total flip: {}.".format(strand_flip)) From a229f413c287ae47fd6425b0cc6886938f6ff534 Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Fri, 14 Apr 2023 11:29:36 +0400 Subject: [PATCH 03/19] Added back annotate id bcftools rule to standard preprocessing --- rules/filter.smk | 16 ++++++++++++++-- scripts/pre_imputation_check.py | 4 +--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/rules/filter.smk b/rules/filter.smk index b703dbe4..e7fb1961 100644 --- a/rules/filter.smk +++ b/rules/filter.smk @@ -1,6 +1,18 @@ +rule annotate_snp_ids: + input: + vcf = 'vcf/{batch}_merged_lifted.vcf.gz' + output: + vcf = 'vcf/{batch}_merged_annotated.vcf.gz' + conda: + 'bcftools' + shell: + ''' + bcftools annotate --set-id "%CHROM:%POS:%REF:%FIRST_ALT" {input.vcf} -O z -o {output.vcf} + ''' + rule select_bad_samples: input: - vcf='vcf/{batch}_merged_lifted.vcf.gz' + vcf='vcf/{batch}_merged_annotated.vcf.gz' output: bad_samples='vcf/{batch}_lifted_vcf.badsamples', report='results/{batch}_bad_samples_report.tsv', @@ -25,7 +37,7 @@ rule select_bad_samples: rule plink_filter: input: - vcf='vcf/{batch}_merged_lifted.vcf.gz', + vcf='vcf/{batch}_merged_annotated.vcf.gz', bad_samples=rules.select_bad_samples.output.bad_samples output: bed = temp('plink/{batch}_merged_filter.bed'), diff --git a/scripts/pre_imputation_check.py b/scripts/pre_imputation_check.py index f23dacdc..334495df 100644 --- a/scripts/pre_imputation_check.py +++ b/scripts/pre_imputation_check.py @@ -70,7 +70,7 @@ def pre_imputation_check(params, reference): items = i.split() # id: (ref, alt) # %CHROM:%POS:%REF:%FIRST_ALT - a1_a2[f'{items[0]}:{items[3]}:{items[-2]}:{items[-1]}'] = (items[-2], items[-1]) + a1_a2[items[1]] = (items[-2], items[-1]) # files to update bim chr_path = prefix + '.chr' @@ -120,8 +120,6 @@ def pre_imputation_check(params, reference): elif matching == 3: flip_file.write(id_ + "\n") flip_swap += 1 - else: - print(id_) logging.info("Exclude: {} Keep: {}".format(exclude, in_ref - exclude)) logging.info("Total flip: {}.".format(strand_flip)) From 99c9cbb0a542fddc4374c5aaa6e90ab614162a02 Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Tue, 18 Apr 2023 12:14:02 +0400 Subject: [PATCH 04/19] eagle phasing should work --- rules/phasing.smk | 27 +++++++++++++++++++++------ rules/preprocessing.smk | 2 +- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/rules/phasing.smk b/rules/phasing.smk index 184eeca3..d28c424b 100644 --- a/rules/phasing.smk +++ b/rules/phasing.smk @@ -1,8 +1,23 @@ +rule index_for_eagle: + input: + bcf='vcf/{batch}_merged_mapped_sorted.bcf.gz' + output: + idx='vcf/{batch}_merged_mapped_sorted.bcf.gz.tbi' + log: + 'logs/vcf/{batch}_merged_mapped_sorted.bcf.gz.tbi.log' + conda: + 'bcftools' + shell: + ''' + bcftools index -f -t {input.bcf} |& tee {log} + ''' + rule phase: input: - vcf='vcf/{batch}_merged_mapped_sorted.vcf.gz', + vcf='vcf/{batch}_merged_mapped_sorted.bcf.gz', + idx='vcf/{batch}_merged_mapped_sorted.bcf.gz.tbi', vcfRef=REF_VCF - output: temp('phase/{batch}_chr{chrom}.phased.vcf.gz') + output: temp('phase/{batch}_chr{chrom}.phased.bcf.gz') log: 'logs/phase/{batch}_eagle-{chrom}.log' benchmark: @@ -13,20 +28,20 @@ rule phase: --vcfTarget {input.vcf} \ --geneticMapFile {GENETIC_MAP} \ --chrom {wildcards.chrom} \ - --vcfOutFormat z \ + --vcfOutFormat b \ --pbwtIters 2 \ --Kpbwt 20000 \ --outPrefix phase/{wildcards.batch}_chr{wildcards.chrom}.phased |& tee {log} ''' -phase = ['chr{i}.phased.vcf.gz'.format(i=chr) for chr in CHROMOSOMES] +phase = ['chr{i}.phased.bcf.gz'.format(i=chr) for chr in CHROMOSOMES] phase_batch = [ 'phase/{batch}_' + line for line in phase] rule merge_phased: input: phase_batch output: - 'phase/{batch}_merged_phased.vcf.gz' + 'phase/{batch}_merged_phased.bcf.gz' params: list='vcf/{batch}_phased.merge.list', mode=config['mode'] @@ -55,7 +70,7 @@ rule merge_phased: # check if there is a background data and merge it if [ -f "background/{wildcards.batch}_merged_imputed.vcf.gz" && {params.mode} = "client" ]; then mv {output} {output}.client - bcftools merge --force-samples background/{wildcards.batch}_merged_imputed.vcf.gz {output}.client -O z -o {output} |& tee -a {log} + bcftools merge --force-samples background/{wildcards.batch}_merged_imputed.vcf.gz {output}.client -O b -o {output} |& tee -a {log} bcftools index -f {output} |& tee -a {log} fi ''' diff --git a/rules/preprocessing.smk b/rules/preprocessing.smk index 1a21ff8c..63282174 100644 --- a/rules/preprocessing.smk +++ b/rules/preprocessing.smk @@ -306,7 +306,7 @@ if not flow == 'rapid': else: rule create_samples_list: input: - bcf_input = 'phase/batch1_merged_phased.bcf.gz' + bcf = 'phase/batch1_merged_phased.bcf.gz' output: fam='preprocessed/data.fam' conda: From 92973a63c52535139173c0e869136670e1665cbf Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Tue, 18 Apr 2023 12:14:24 +0400 Subject: [PATCH 05/19] slightly decreased number of simulated relatives for simbig pipeline --- workflows/simbig/params/relatives_big.def | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/workflows/simbig/params/relatives_big.def b/workflows/simbig/params/relatives_big.def index 6a8595d7..cc180d61 100644 --- a/workflows/simbig/params/relatives_big.def +++ b/workflows/simbig/params/relatives_big.def @@ -4,6 +4,6 @@ def first 1 8 3 16 2 4 32 2 5 32 2 -6 64 2 -7 128 2 -8 192 16 +6 32 2 +7 64 2 +8 64 16 From c87aa0639060b58f23b1f2ffa70df7bcbd78c050 Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Tue, 18 Apr 2023 12:15:04 +0400 Subject: [PATCH 06/19] Added downloading eagle back to the Dockerfile --- containers/snakemake/Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/containers/snakemake/Dockerfile b/containers/snakemake/Dockerfile index 91ab5f80..d283462b 100644 --- a/containers/snakemake/Dockerfile +++ b/containers/snakemake/Dockerfile @@ -29,10 +29,6 @@ ENV PATH "$PATH:/opt/Minimac3Executable/bin" # Install Minimac4 RUN apt-get install -y minimac4 -# Install Eagle -# RUN wget "https://data.broadinstitute.org/alkesgroup/Eagle/downloads/dev/eagle_v2.4.1" -O /usr/bin/eagle -# RUN chmod a+x /usr/bin/eagle - # Install Germline # conda version of germline has a problem with non-zero error code # https://github.com/gusevlab/germline/issues/8 @@ -48,6 +44,10 @@ RUN apt-get install -y make g++ git && \ ENV PATH "$PATH:/opt/germline/bin" WORKDIR / +# Install Eagle +RUN wget "https://data.broadinstitute.org/alkesgroup/Eagle/downloads/dev/eagle_v2.4.1" -O /usr/bin/eagle +RUN chmod a+x /usr/bin/eagle + # Workaround of NonWritableError when conda tries to create environments for the first time # funnel launches docker containers with --read-only and snakemake cannot create conda envs # because it has to do something with urls.txt From 2242d1e76512ba54b85648e8b98f0e30d94debe9 Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Fri, 21 Apr 2023 16:19:12 +0400 Subject: [PATCH 07/19] script to select only modern samples from aadr dataset --- scripts/filter_aadr.py | 61 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 scripts/filter_aadr.py diff --git a/scripts/filter_aadr.py b/scripts/filter_aadr.py new file mode 100644 index 00000000..40e86b45 --- /dev/null +++ b/scripts/filter_aadr.py @@ -0,0 +1,61 @@ +import numpy +import pandas +from utils.bcftools import bcftools_view + + +''' +Index(['Genetic ID', 'Master ID', 'Skeletal code', 'Skeletal element', + 'Year data from this individual was first published [for a present-day individuals we give the data of the data reported here; missing GreenScience 2010 (Vi33.15, Vi33.26), Olalde2018 (I2657), RasmussenNature2010 (Australian)]', + 'Publication', + 'Method for Determining Date; unless otherwise specified, calibrations use 95.4% intervals from OxCal v4.4.2 Bronk Ramsey (2009); r5; Atmospheric data from Reimer et al (2020)', + 'Date mean in BP in years before 1950 CE [OxCal mu for a direct radiocarbon date, and average of range for a contextual date]', + 'Date standard deviation in BP [OxCal sigma for a direct radiocarbon date, and standard deviation of the uniform distribution between the two bounds for a contextual date]', + 'Full Date One of two formats. (Format 1) 95.4% CI calibrated radiocarbon age (Conventional Radiocarbon Age BP, Lab number) e.g. 2624-2350 calBCE (3990±40 BP, Ua-35016). (Format 2) Archaeological context range, e.g. 2500-1700 BCE', + 'Age at Death from physical anthropology', 'Group ID', 'Locality', + 'Political Entity', 'Lat.', 'Long.', 'Pulldown Strategy', 'Data source', + 'No. Libraries', + '1240k coverage (taken from original pulldown where possible)', + 'SNPs hit on autosomal targets (Computed using easystats on 1240k snpset)', + 'SNPs hit on autosomal targets (Computed using easystats on HO snpset)', + 'Molecular Sex', 'Family ID and position within family', + 'Y haplogroup (manual curation in terminal mutation format)', + 'Y haplogroup (manual curation in ISOGG format)', + 'mtDNA coverage (merged data)', 'mtDNA haplogroup if >2x or published', + 'mtDNA match to consensus if >2x (merged data)', + 'Damage rate in first nucleotide on sequences overlapping 1240k targets (merged data)', + 'Sex ratio [Y/(Y+X) counts] (merged data)', + 'Library type (minus=no.damage.correction, half=damage.retained.at.last.position, plus=damage.fully.corrected, ds=double.stranded.library.preparation, ss=single.stranded.library.preparation)', + 'Libraries', 'ASSESSMENT', + 'ASSESSMENT WARNINGS (Xcontam interval is listed if lower bound is >0.005, "QUESTIONABLE" if lower bound is 0.01-0.02, "QUESTIONABLE_CRITICAL" or "FAIL" if lower bound is >0.02) (mtcontam confidence interval is listed if coverage >2 and upper bound is <0.'], + dtype='object') +''' + + +if __name__ == '__main__': + cols = [ + 'genetic_id', 'master_id', 'skeletal_code', 'skeletal_element', + 'year_data_from_this_individual_was_first_published', + 'publication', + 'method_for_determining_date', + 'date_mean', + 'date_sd', + 'full_date', + 'age_at_death_from_physical_anthropology', 'group_id', 'locality', + 'political_entity', 'lat', 'long', 'pulldown_strategy', 'data_source', + 'no_libraries', + 'coverage', 'snps_hit_autosomal_1240k', 'snps_hit_autosomal_ho', + 'sex', 'family_id', 'y_haplogroup_terminal', 'y_haplogroup_isogg', 'mt_coverage', 'mt_haplogroup', 'mt_dna_match', 'damage_rate', 'sex_ratio', 'library_type', 'libraries', 'assessment', 'assessment_warnings' + ] + anno = pandas.read_table('workdir/v54.1.p1_1240K_public.tsv', header=0, names=cols, index_col='genetic_id') + present = anno[anno['full_date'] == 'present'] + # group by publication and print counts + print(present.groupby('publication').size()) + + # plot hist of snps hit on autosomal targets + present.loc[:, 'snps_hit_autosomal_1240k'] = present['snps_hit_autosomal_1240k'].astype(float) + (present['snps_hit_autosomal_1240k']/present['snps_hit_autosomal_1240k'].max()).hist(bins=20).get_figure().savefig('workdir/snps_hit_autosomal_1240k.png') + present.loc[:, 'coverage'] = present['coverage'].apply(lambda x: float(x) if x != '..' else numpy.nan).astype(float) + present.reset_index()['genetic_id'].to_csv('workdir/present.txt', index=False, header=None) + print(f'{len(present)} individuals are modern') + + bcftools_view('workdir/aadr.vcf.gz', 'workdir/aadr.present.vcf.gz', 'workdir/present.txt') \ No newline at end of file From 8c457f20d62aa5a532473e4d3a5c9a70cc2dc23d Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Fri, 21 Apr 2023 16:20:00 +0400 Subject: [PATCH 08/19] passing zero parameters values to select_bad_samples will now bypass relevant filters --- scripts/select_bad_samples.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/select_bad_samples.py b/scripts/select_bad_samples.py index dc54eb61..f37d80be 100644 --- a/scripts/select_bad_samples.py +++ b/scripts/select_bad_samples.py @@ -108,9 +108,9 @@ def get_stats(vcf_input, samples_path, psc_path=False, stats_path=''): bad_missing_samples_mask = (psc.missing_share >= missing_samples / 100) | (psc.nMissing + psc.nNonMissing == 0) - bad_alt_hom_samples_mask = (psc.alt_hom_share <= alt_hom_samples / 100) | (psc.nNonMissing == 0) + bad_alt_hom_samples_mask = (psc.alt_hom_share < alt_hom_samples / 100) | (psc.nNonMissing == 0) - bad_het_samples_mask = (psc.het_samples_share <= het_samples / 100) | (psc.nNonMissing == 0) + bad_het_samples_mask = (psc.het_samples_share < het_samples / 100) | (psc.nNonMissing == 0) psc.loc[bad_het_samples_mask, 'exclusion_reason'] = f'Sample has <= {het_samples}% heterozygous SNPs' psc.loc[bad_alt_hom_samples_mask, 'exclusion_reason'] = f'Sample has <= {alt_hom_samples}% homozygous alternative SNPs' From 640f1fad429188a6cb0476ebcb659de6de726b36 Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Mon, 15 May 2023 15:02:54 +0400 Subject: [PATCH 09/19] simbig workflow tries to generate --sim-samples-number samples --- launcher.py | 17 +++++- workflows/simbig/Snakefile | 116 ++++++++++++++++++++++++++++--------- 2 files changed, 104 insertions(+), 29 deletions(-) diff --git a/launcher.py b/launcher.py index 2fc260dc..e55319d7 100644 --- a/launcher.py +++ b/launcher.py @@ -328,6 +328,20 @@ def get_parser_args(): type=int, help='Random seed for Ped-sim pedigree simulation. The default value is randomly generated.') + parser.add_argument( + '--sim_samples_number', + default=1000, + type=int, + help='Number of samples to simulate in Ped-sim pedigree simulation using simbig workflow.' + ) + + parser.add_argument( + '--background', + default='1kg', + type=str, + help='Founders for simulation. The default value is 1kg. In this case, it will use 1000 genomes founders' + ) + args = parser.parse_args() valid_commands = [ @@ -493,8 +507,9 @@ def copy_input(input_dir, working_dir, samples_file): config_dict['alt_hom_samples'] = args.alt_hom_samples config_dict['het_samples'] = args.het_samples config_dict['iqr_alpha'] = args.iqr_alpha - + config_dict['sim_samples_number'] = args.sim_samples_number config_dict['seed'] = args.seed + config_dict['background'] = args.background if args.weight_mask: config_dict['weight_mask'] = os.path.join(args.directory, args.weight_mask) diff --git a/workflows/simbig/Snakefile b/workflows/simbig/Snakefile index d52904c0..5486faa7 100644 --- a/workflows/simbig/Snakefile +++ b/workflows/simbig/Snakefile @@ -12,6 +12,8 @@ PEDSIM_MAP = join(REF_DIR, config["reference"]["pedsim_map"]["file"]) CHROMOSOMES = [str(i) for i in range(1, 23)] DEF_FILE = config['sim_params_file'] SIM_SAMPLES_FILE = config['sim_samples_file'] +SIM_SAMPLES_NUMBER = config['sim_samples_number'] + names = [] def generate_code(): @@ -24,11 +26,16 @@ def generate_code(): return code -def get_num_runs(def_file): +def get_num_founders(samples_file): with open(SIM_SAMPLES_FILE, "r") as f: lines = f.readlines() samples = [line.rstrip() for line in lines] total_founders = len(samples) + return total_founders + + +def get_num_runs(def_file): + total_founders = get_num_founders(SIM_SAMPLES_FILE) with open(f"{def_file}", "r") as f: lines = f.readlines() @@ -69,39 +76,91 @@ def prepare_folders(num_runs, def_file): f.write("\n".join(l for l in lines if l != '\n')) -NUM_RUNS, NUM_FOUNDERS = get_num_runs(DEF_FILE) -prepare_folders(NUM_RUNS, DEF_FILE) +def generate_def_file(def_file: str): + # we have number of founders fixed (14), + # number of total simulated samples fixed (SIM_SAMPLES_NUMBER), + # and number of founders in the background (num_founders) fixed + # variables are number of runs (num_runs) and number of branches in each generation + max_number_of_samples_per_run = 3000 + num_founders_per_run = 14 + num_founders = get_num_founders(SIM_SAMPLES_FILE) + num_runs = int(num_founders / num_founders_per_run) + if max_number_of_samples_per_run * num_runs < SIM_SAMPLES_NUMBER: + raise ValueError(f'Number of samples in sim-samples-file is too small for the total number of samples requested. Max number of samples per run is {max_number_of_samples_per_run}, number of runs is {num_runs}, total number of samples is {SIM_SAMPLES_NUMBER}') + + # SIM_SAMPLES_NUMBER = num_samples_per_run * num_runs + # num_samples_per_run = num_founders_per_run + \sum_{i=1}^{num_generations} num_branches_per_generation * num_siblings_per_branch + # we should make sure that num_branches_per_generation * num_siblings_per_branch is as small as possible + # for a relatives_big.def equation is 2 * (2x) + 3 * (2*2x) + 1 * (4*2x) + 1 * (4*16x) = num_samples_per_run + # 4x + 12x + 8x + 64x = num_samples_per_run + # 88x = num_samples_per_run + # x = num_samples_per_run / 88 + num_samples_per_run = int(SIM_SAMPLES_NUMBER / num_runs) + siblings_scaling_factor = num_samples_per_run / 88 + siblings = [1, 1, 2, 2, 2, 4, 4] + branches = [2, 2, 2, 2, 2, 2, 16] + siblings = [int(s * siblings_scaling_factor) for s in siblings] + siblings[-1] += 2 + actual_samples_per_run = sum([s*b for s, b in zip(siblings, branches)]) + num_founders_per_run + print(f'Actual number of samples per run is {actual_samples_per_run}') + + with open(f'{def_file}.adjusted', 'w') as f: + f.write(f'def first 1 8\n') + f.write(f'1 1 1\n') + for i, (s, b) in enumerate(zip(siblings, branches)): + f.write(f'{i+2} {s} {b}\n') + print(f'for generation {i+2} we have {s} siblings and {b} branches') + + return f'{def_file}.adjusted' + + +adjusted_def_file = generate_def_file(DEF_FILE) +NUM_RUNS, NUM_FOUNDERS = get_num_runs(adjusted_def_file) +prepare_folders(NUM_RUNS, adjusted_def_file) + rule all: input: "generated.vcf.gz", "generated.vcf.gz.csi" - -rule merge_background: - input: - data=expand(PHASED_VCF, chrom=CHROMOSOMES) - output: - "pedsim/phased/background.bcf.gz" - params: - list="pedsim/phased/phased.merge.list" - conda: - "bcftools" - shell: - """ - # for now just skip empty files - true > {params.list} && \ - for i in {input.data}; do - if [ -s $i ] - then - echo $i >> {params.list} - else - continue - fi - done - bcftools concat -f {params.list} -O b -o {output} - """ - +if config['background'] == '1kg': + + rule merge_background: + input: + data=expand(PHASED_VCF, chrom=CHROMOSOMES) + output: + "pedsim/phased/background.bcf.gz" + params: + list="pedsim/phased/phased.merge.list" + conda: + "bcftools" + shell: + """ + # for now just skip empty files + true > {params.list} && \ + for i in {input.data}; do + if [ -s $i ] + then + echo $i >> {params.list} + else + continue + fi + done + bcftools concat -f {params.list} -O b -o {output} + """ +else: + rule convert_backgrond: + input: + data=config['background'] + output: + "pedsim/phased/background.bcf.gz" + conda: + "bcftools" + shell: + """ + bcftools convert {input.data} -O b -o {output} + """ rule split_chip: input: @@ -119,6 +178,7 @@ rule split_chip: script: "../../scripts/split_chip.py" + rule simulate: input: _map=PEDSIM_MAP, From 9af598b893aab7f0abaf47a8064cd2e90180d77d Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Mon, 15 May 2023 15:21:17 +0400 Subject: [PATCH 10/19] WIP! per-chromosome ibis processing --- rules/relatives_ibis.smk | 41 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/rules/relatives_ibis.smk b/rules/relatives_ibis.smk index dc6ec512..6913a7bc 100644 --- a/rules/relatives_ibis.smk +++ b/rules/relatives_ibis.smk @@ -2,15 +2,36 @@ import os import sys -rule ibis: +rule split_for_ibis: input: bed = "preprocessed/data.bed", fam = "preprocessed/data.fam", bim = "preprocessed/data_mapped.bim" + conda: + "plink" + output: + bed = "ibis/{chr}.bed", + fam = "ibis/{chr}.fam", + bim = "ibis/{chr}.bim" + threads: 1 + params: + bfile = "preprocessed/data", + chr = lambda wildcards: wildcards.chr + shell: + """ + plink --bfile {params.bfile} --chr {params.chr} --make-bed --out ibis/{params.chr} --threads {threads} + """ + + +rule ibis: + input: + bed = "ibis/{chr}.bed", + fam = "ibis/{chr}.fam", + bim = "ibis/{chr}.bim" conda: "ibis" output: - ibd = "ibis/merged_ibis.seg" + ibd = "ibis/merged_ibis_{chr}.seg" log: "logs/ibis/run_ibis.log" benchmark: @@ -18,10 +39,22 @@ rule ibis: threads: workflow.cores params: mL = config['ibis_seg_len'], - mT = config['ibis_min_snp'] + mT = config['ibis_min_snp'], + chr = lambda wildcards: wildcards.chr + shell: + """ + ibis {input.bed} {input.bim} {input.fam} -t {threads} -mt {params.mT} -mL {params.mL} -ibd2 -mL2 3 -hbd -f ibis/merged_ibis_{params.chr} |& tee -a {log} + """ + + +rule merge_ibis_segments: + input: + expand("ibis/merged_ibis_{chr}.seg", chr=CHROMOSOMES) + output: + "ibis/merged_ibis.seg" shell: """ - ibis {input.bed} {input.bim} {input.fam} -t {threads} -mt {params.mT} -mL {params.mL} -ibd2 -mL2 3 -hbd -f ibis/merged_ibis |& tee -a {log} + cat {input} >> {output} """ From ac603c68793e526688e6a39b0e012ec938dc3e0f Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Mon, 15 May 2023 16:17:49 +0400 Subject: [PATCH 11/19] ersa matching is done in parallel --- rules/relatives_ibis.smk | 42 ++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/rules/relatives_ibis.smk b/rules/relatives_ibis.smk index 6913a7bc..0b6d1e50 100644 --- a/rules/relatives_ibis.smk +++ b/rules/relatives_ibis.smk @@ -33,9 +33,9 @@ rule ibis: output: ibd = "ibis/merged_ibis_{chr}.seg" log: - "logs/ibis/run_ibis.log" + "logs/ibis/run_ibis_{chr}.log" benchmark: - "benchmarks/ibis/run_ibis.txt" + "benchmarks/ibis/run_ibis_{chr}.txt" threads: workflow.cores params: mL = config['ibis_seg_len'], @@ -107,15 +107,15 @@ def aggregate_input(wildcards): rule ersa: input: - ibd = aggregate_input + ibd = "ibd/{id}.tsv" output: - "ersa/relatives.tsv" + relatives="ersa/relatives_{id}.tsv" conda: "ersa" log: - "logs/ersa/ersa.log" + "logs/ersa/ersa_{id}.log" benchmark: - "benchmarks/ersa/ersa.txt" + "benchmarks/ersa/ersa_{id}.txt" params: l = config['zero_seg_count'], th = config['zero_seg_len'], @@ -124,20 +124,24 @@ rule ersa: r = '--nomask ' + '-r ' + str(config['ersa_r']) if config.get('weight_mask') else '' shell: """ - touch {output} - FILES="{input.ibd}" - TEMPFILE=ersa/temp_relatives.tsv - rm -f $TEMPFILE - rm -f {output} - echo "ersa --avuncular-adj -ci -a {params.a} --dmax 14 -t {params.t} -l {params.l} {params.r} -th {params.th}" - for input_file in $FILES; do - ersa --avuncular-adj -ci -a {params.a} --dmax 14 -t {params.t} -l {params.l} \ - {params.r} -th {params.th} $input_file -o $TEMPFILE |& tee {log} + ersa --avuncular-adj -ci -a {params.a} --dmax 14 -t {params.t} -l {params.l} \ + {params.r} -th {params.th} {input.ibd} -o {output.relatives} |& tee {log} + """ + +rule merge_ersa: + input: + expand("ersa/relatives_{id}.tsv", id=glob_wildcards("ibd/{id}.tsv").id) + output: + "ersa/relatives.tsv" + shell: + """ + FILES="{input}" + for input_file in $FILES; do if [[ "$input_file" == "${{FILES[0]}}" ]]; then - cat $TEMPFILE >> {output} + cat $input_file >> {output} else - sed 1d $TEMPFILE >> {output} + sed 1d $input_file >> {output} fi done """ @@ -145,8 +149,8 @@ rule ersa: rule postprocess_ersa: input: - ibd=rules.ibis.output['ibd'], - ersa=rules.ersa.output[0] + ibd=rules.merge_ibis_segments.output[0], + ersa=rules.merge_ersa.output[0] params: ibis = True output: "results/relatives.tsv" From b5856d3940498d267ab4855e0697382b1c698c97 Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Fri, 23 Jun 2023 18:27:04 +0400 Subject: [PATCH 12/19] grape launches several ibis processes in parallel for different chromosomes --- rules/relatives_ibis.smk | 75 ++++++++++++++++++++---------- scripts/postprocess_ersa.py | 2 +- scripts/transform_ibis_segments.py | 4 +- 3 files changed, 53 insertions(+), 28 deletions(-) diff --git a/rules/relatives_ibis.smk b/rules/relatives_ibis.smk index 0b6d1e50..3781bdd8 100644 --- a/rules/relatives_ibis.smk +++ b/rules/relatives_ibis.smk @@ -10,51 +10,69 @@ rule split_for_ibis: conda: "plink" output: - bed = "ibis/{chr}.bed", - fam = "ibis/{chr}.fam", - bim = "ibis/{chr}.bim" + bed = "ibis_data/{chrom}.bed", + fam = "ibis_data/{chrom}.fam", + bim = "ibis_data/{chrom}.bim" threads: 1 params: bfile = "preprocessed/data", - chr = lambda wildcards: wildcards.chr + chr = lambda wildcards: wildcards.chrom shell: """ - plink --bfile {params.bfile} --chr {params.chr} --make-bed --out ibis/{params.chr} --threads {threads} + plink --bfile {params.bfile} --chr {params.chr} --make-bed --out ibis_data/{params.chr} --threads {threads} """ +rule ibis_chrom_mapping: + input: + bim = "ibis_data/{chrom}.bim" + params: + genetic_map_GRCh37=GENETIC_MAP_GRCH37 + conda: + 'ibis' + output: + 'ibis_mapped/mapped_{chrom}.bim' + log: + 'logs/ibis/run_ibis_mapping_chr{chrom}.log' + benchmark: + 'benchmarks/ibis/run_ibis_mapping_chr{chrom}.txt' + shell: + ''' + (add-map-plink.pl -cm {input.bim} {params.genetic_map_GRCh37}> {output}) |& tee -a {log} + ''' + rule ibis: input: - bed = "ibis/{chr}.bed", - fam = "ibis/{chr}.fam", - bim = "ibis/{chr}.bim" + bed = "ibis_data/{chrom}.bed", + fam = "ibis_data/{chrom}.fam", + bim = "ibis_mapped/mapped_{chrom}.bim" conda: "ibis" output: - ibd = "ibis/merged_ibis_{chr}.seg" + ibd = "ibis/merged_ibis_{chrom}.seg.gz" log: - "logs/ibis/run_ibis_{chr}.log" + "logs/ibis/run_ibis_{chrom}.log" benchmark: - "benchmarks/ibis/run_ibis_{chr}.txt" + "benchmarks/ibis/run_ibis_{chrom}.txt" threads: workflow.cores params: mL = config['ibis_seg_len'], mT = config['ibis_min_snp'], - chr = lambda wildcards: wildcards.chr + chr = lambda wildcards: wildcards.chrom shell: """ - ibis {input.bed} {input.bim} {input.fam} -t {threads} -mt {params.mT} -mL {params.mL} -ibd2 -mL2 3 -hbd -f ibis/merged_ibis_{params.chr} |& tee -a {log} + ibis {input.bed} {input.bim} {input.fam} -t {threads} -mt {params.mT} -mL {params.mL} -ibd2 -mL2 3 -hbd -gzip -f ibis/merged_ibis_{params.chr} |& tee -a {log} """ rule merge_ibis_segments: input: - expand("ibis/merged_ibis_{chr}.seg", chr=CHROMOSOMES) + expand("ibis/merged_ibis_{chrom}.seg.gz", chrom=CHROMOSOMES) output: - "ibis/merged_ibis.seg" + ibd=temp("ibis/merged_ibis.seg") shell: """ - cat {input} >> {output} + zcat {input} >> {output.ibd} """ @@ -82,7 +100,7 @@ if config.get('weight_mask'): """ ibd_segments_file = rules.ibis_segments_weighing.output.ibd else: - ibd_segments_file = rules.ibis.output.ibd + ibd_segments_file = rules.merge_ibis_segments.output.ibd checkpoint transform_ibis_segments: @@ -99,16 +117,16 @@ checkpoint transform_ibis_segments: "../scripts/transform_ibis_segments.py" -def aggregate_input(wildcards): - checkpoints.transform_ibis_segments.get() - ids = glob_wildcards(f"ibd/{{id}}.tsv").id - return expand(f"ibd/{{id}}.tsv", id=ids) +def tis_output(wildcards): + checkpoints.transform_ibis_segments.get(id=wildcards.id) + return "ibd/{id}.tsv.gz" rule ersa: input: - ibd = "ibd/{id}.tsv" + ibd = tis_output output: + ibd = temp('temp_ibd/{id}.tsv'), relatives="ersa/relatives_{id}.tsv" conda: "ersa" @@ -124,14 +142,21 @@ rule ersa: r = '--nomask ' + '-r ' + str(config['ersa_r']) if config.get('weight_mask') else '' shell: """ + zcat {input.ibd} > {output.ibd} ersa --avuncular-adj -ci -a {params.a} --dmax 14 -t {params.t} -l {params.l} \ - {params.r} -th {params.th} {input.ibd} -o {output.relatives} |& tee {log} + {params.r} -th {params.th} {output.ibd} -o {output.relatives} |& tee {log} """ +def aggregate_input(wildcards): + checkpoints.transform_ibis_segments.get() + ids = glob_wildcards(f"ibd/{{id}}.tsv.gz").id + return expand(f"ersa/relatives_{{id}}.tsv", id=ids) + + rule merge_ersa: input: - expand("ersa/relatives_{id}.tsv", id=glob_wildcards("ibd/{id}.tsv").id) + aggregate_input output: "ersa/relatives.tsv" shell: @@ -149,7 +174,7 @@ rule merge_ersa: rule postprocess_ersa: input: - ibd=rules.merge_ibis_segments.output[0], + ibd=rules.merge_ibis_segments.output.ibd, ersa=rules.merge_ersa.output[0] params: ibis = True diff --git a/scripts/postprocess_ersa.py b/scripts/postprocess_ersa.py index 22366d92..3e0c7793 100644 --- a/scripts/postprocess_ersa.py +++ b/scripts/postprocess_ersa.py @@ -107,7 +107,7 @@ def get_new_col_names(old_names): ersa_path = snakemake.input['ersa'] output_path = snakemake.output[0] - with open(ersa_path, 'r') as ersa_file, open(ibd_path, 'r') as ibd_file: + with open(ersa_path, 'r') as ersa_file, open(ibd_path, 'rb') as ibd_file: if len(ersa_file.readlines(5000)) < 2 or len(ibd_file.readlines(5000)) < 1: print("ersa postprocess input is empty") open(output_path, "w").close() # create empty output to avoid error diff --git a/scripts/transform_ibis_segments.py b/scripts/transform_ibis_segments.py index 0541b225..94980c4a 100644 --- a/scripts/transform_ibis_segments.py +++ b/scripts/transform_ibis_segments.py @@ -23,9 +23,9 @@ def process_chunk_with_hash(data: pandas.DataFrame, denominator: int, dest_dir: 'ibd21', 'ibd22']] for bucket_id, group in to_write.groupby('bucket_id'): - dest_file = os.path.join(dest_dir, f'{bucket_id}.tsv') + dest_file = os.path.join(dest_dir, f'{bucket_id}.tsv.gz') group.drop('bucket_id', inplace=True, axis='columns') - group.to_csv(dest_file, index=False, header=None, sep='\t', mode='a') + group.to_csv(dest_file, index=False, header=None, sep='\t', mode='a', compression='gzip') def split_by_id(input_ibd: str, samples_count: int, dest_dir: str): From e1dbd820fbb7f83461c3a57467af983080ca2c73 Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Mon, 26 Jun 2023 12:48:13 +0400 Subject: [PATCH 13/19] Generation of new simulation samples using simple mix of alternative allele counts --- scripts/simulate_dummy_samples.py | 104 ++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 scripts/simulate_dummy_samples.py diff --git a/scripts/simulate_dummy_samples.py b/scripts/simulate_dummy_samples.py new file mode 100644 index 00000000..35c69038 --- /dev/null +++ b/scripts/simulate_dummy_samples.py @@ -0,0 +1,104 @@ +import logging +from collections import namedtuple +import allel +import numpy +import tqdm +import gzip + + +def write_vcf_file(file_name, headers, calldata, variant_names, sample_names, sample_genotypes): + with gzip.open(file_name, 'wt') as vcf_file: + # Writing metadata headers (e.g., file format, reference, etc.) + for header in headers: + vcf_file.write("##" + header + "\n") + + # Writing column header + columns = ['#CHROM', 'POS', 'ID', 'REF', 'ALT', 'QUAL', 'FILTER', 'INFO', 'FORMAT'] + columns.extend(sample_names) + vcf_file.write("\t".join(columns) + "\n") + + # Writing variant data + variant_count = len(variant_names) + for tqdm_idx in tqdm.tqdm(range(variant_count)): + var_name, calldata_item, sample_genotype = variant_names[tqdm_idx], calldata[tqdm_idx], sample_genotypes[tqdm_idx, :] + chrom, pos, id, ref, alt = var_name + qual, filter, format = calldata_item + info = 'AF=0.5' + data_line = [chrom, pos, id, ref, alt, qual, filter, info, format] + data_line.extend([f'{sg[0]}|{sg[1]}' for sg in sample_genotype]) # add the sample genotype data here + vcf_file.write("\t".join(map(str, data_line)) + "\n") + +''' +# Example usage: +# You can create your input data as per your requirement +headers = ["fileformat=VCFv4.2", "reference=file:///path_to_reference_genome"] +calldata = [(None, None, "GT"), (None, None, "GT")] # Example calldata, replace with actual data +variant_names = [("chr1", "12345", ".", "A", "C"), ("chr1", "12346", ".", "G", "T")] +variant_info = ["NS=3;DP=14;AF=0.5;DB;H2", "NS=3;DP=11;AF=0.017"] +sample_names = ["sample1", "sample2"] +sample_genotypes = [["0/1", "0/0"], ["1/1", "0/1"]] + +# Writing to file +write_vcf_file("output.vcf", headers, calldata, variant_names, variant_info, sample_names, sample_genotypes) +''' + +if __name__ == '__main__': + + try: + snakemake + except NameError: + Snakemake = namedtuple('Snakemake', ['input', 'output', 'params', 'log']) + snakemake = Snakemake( + input={'background': 'test_data/vcf/background_20.vcf.gz'}, + output=['test_data/vcf/augmentated_background_20.vcf.gz'], + params={'samples_count': 100}, + log=['test_data/merge_king_ersa.log'] + ) + + logging.basicConfig(filename=snakemake.log[0], level=logging.DEBUG, format='%(levelname)s:%(asctime)s %(message)s') + + # We read background.vcf.gz file + + vcf = allel.read_vcf(snakemake.input['background']) + print(f'We read vcf with {len(vcf["samples"])} samples from {snakemake.input["background"]}') + print(f'vcf has {vcf.keys()} fields') + # the first dimension corresponds to the variants genotyped + # the second dimension corresponds to the samples genotyped + # the third dimension corresponds to the ploidy of the samples. + gt = vcf['calldata/GT'] + samples = vcf['samples'] + # We sample pair of samples from the file + indices = numpy.arange(gt.shape[0]) # number of variants + new_samples = [] + new_gt = [] + for i in range(0, len(samples), 2): + for j in range(1, len(samples), 2): + sample_gt_i = gt[:, i, :] + sample_gg_j = gt[:, j, :] + # We generate a mix of these two samples + change_indices = numpy.random.choice(indices, size=gt.shape[1] // 2, replace=False) + sample_gt_i[change_indices, :] = sample_gg_j[change_indices, :] + new_samples.append(samples[i] + '_' + samples[j]) + new_gt.append(numpy.expand_dims(sample_gt_i, axis=1)) + if len(new_samples) >= snakemake.params['samples_count']: + break + + print(f'generated {len(new_samples)} samples') + new_samples = numpy.array(new_samples) + new_gt = numpy.concatenate(new_gt, axis=1) + + headers = ["fileformat=VCFv4.2", "reference=file:///path_to_reference_genome"] + # qual, filter, format + calldata = [(qual, fp, 'GT') for qual, fp in zip(vcf['variants/QUAL'], vcf['variants/FILTER_PASS'])] + variants = [(chrom, pos, _id, ref, alt) for chrom, pos, _id, ref, alt in zip(vcf['variants/CHROM'], + vcf['variants/POS'], + vcf['variants/ID'], + vcf['variants/REF'], + vcf['variants/ALT'])] + + samples = numpy.concatenate([vcf['samples'], new_samples], axis=0) + gt_to_write = numpy.concatenate([gt, new_gt], axis=1) + write_vcf_file(snakemake.output[0], headers, calldata, variants, samples, gt_to_write) + + test_read_vcf = allel.read_vcf(snakemake.output[0]) + \ No newline at end of file From 25508f85f4db41459e95f7552e1e8e077c475714 Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Tue, 27 Jun 2023 11:27:47 +0400 Subject: [PATCH 14/19] Simple background augmentation in simbig workflow --- scripts/simulate_dummy_samples.py | 26 ++++++++++++++++------- workflows/pedsim/Snakefile | 17 ++++++++++++++- workflows/simbig/Snakefile | 19 ++++++++++++++++- workflows/simbig/params/relatives_big.def | 2 +- 4 files changed, 53 insertions(+), 11 deletions(-) diff --git a/scripts/simulate_dummy_samples.py b/scripts/simulate_dummy_samples.py index 35c69038..777201e4 100644 --- a/scripts/simulate_dummy_samples.py +++ b/scripts/simulate_dummy_samples.py @@ -2,7 +2,6 @@ from collections import namedtuple import allel import numpy -import tqdm import gzip @@ -10,7 +9,7 @@ def write_vcf_file(file_name, headers, calldata, variant_names, sample_names, sa with gzip.open(file_name, 'wt') as vcf_file: # Writing metadata headers (e.g., file format, reference, etc.) for header in headers: - vcf_file.write("##" + header + "\n") + vcf_file.write(header + "\n") # Writing column header columns = ['#CHROM', 'POS', 'ID', 'REF', 'ALT', 'QUAL', 'FILTER', 'INFO', 'FORMAT'] @@ -19,7 +18,8 @@ def write_vcf_file(file_name, headers, calldata, variant_names, sample_names, sa # Writing variant data variant_count = len(variant_names) - for tqdm_idx in tqdm.tqdm(range(variant_count)): + # for tqdm_idx in tqdm.tqdm(range(variant_count)): + for tqdm_idx in range(variant_count): var_name, calldata_item, sample_genotype = variant_names[tqdm_idx], calldata[tqdm_idx], sample_genotypes[tqdm_idx, :] chrom, pos, id, ref, alt = var_name qual, filter, format = calldata_item @@ -51,7 +51,7 @@ def write_vcf_file(file_name, headers, calldata, variant_names, sample_names, sa snakemake = Snakemake( input={'background': 'test_data/vcf/background_20.vcf.gz'}, output=['test_data/vcf/augmentated_background_20.vcf.gz'], - params={'samples_count': 100}, + params={'sample_count': 100}, log=['test_data/merge_king_ersa.log'] ) @@ -72,6 +72,8 @@ def write_vcf_file(file_name, headers, calldata, variant_names, sample_names, sa new_samples = [] new_gt = [] for i in range(0, len(samples), 2): + if len(new_samples) >= snakemake.params['sample_count']: + break for j in range(1, len(samples), 2): sample_gt_i = gt[:, i, :] sample_gg_j = gt[:, j, :] @@ -80,17 +82,25 @@ def write_vcf_file(file_name, headers, calldata, variant_names, sample_names, sa sample_gt_i[change_indices, :] = sample_gg_j[change_indices, :] new_samples.append(samples[i] + '_' + samples[j]) new_gt.append(numpy.expand_dims(sample_gt_i, axis=1)) - if len(new_samples) >= snakemake.params['samples_count']: + if len(new_samples) >= snakemake.params['sample_count']: break print(f'generated {len(new_samples)} samples') new_samples = numpy.array(new_samples) new_gt = numpy.concatenate(new_gt, axis=1) - headers = ["fileformat=VCFv4.2", "reference=file:///path_to_reference_genome"] + # headers = ["fileformat=VCFv4.2", "reference=file:///path_to_reference_genome"] + with gzip.open(snakemake.input['background'], 'rt') as vcf_file: + headers = [] + for i, line in enumerate(vcf_file): + if line.startswith('##'): + headers.append(line.strip()) + if i > 2 and not line.startswith('##'): + break # qual, filter, format - calldata = [(qual, fp, 'GT') for qual, fp in zip(vcf['variants/QUAL'], vcf['variants/FILTER_PASS'])] - variants = [(chrom, pos, _id, ref, alt) for chrom, pos, _id, ref, alt in zip(vcf['variants/CHROM'], + calldata = [(qual, 'PASS', 'GT') for qual, fp in zip(vcf['variants/QUAL'], vcf['variants/FILTER_PASS'])] + print(f'calldata {calldata[:10]}') + variants = [(chrom, pos, _id, ref, alt[0]) for chrom, pos, _id, ref, alt in zip(vcf['variants/CHROM'], vcf['variants/POS'], vcf['variants/ID'], vcf['variants/REF'], diff --git a/workflows/pedsim/Snakefile b/workflows/pedsim/Snakefile index 9dce5a58..521f309a 100644 --- a/workflows/pedsim/Snakefile +++ b/workflows/pedsim/Snakefile @@ -72,9 +72,24 @@ rule merge_background: """ +rule augment_background: + input: + background=rules.merge_background.output[0], + output: + 'pedsim/phased/background.augmented.vcf.gz' + params: + sample_count=100 + log: + "logs/augment_background.log" + conda: + "vcf_to_ped" + script: + "../../scripts/simulate_dummy_samples.py" + + rule simulate: input: - bg=rules.merge_background.output, + bg=rules.augment_background.output, _map=PEDSIM_MAP, _def=config['sim_params_file'], intf='params/nu_p_campbell.tsv' diff --git a/workflows/simbig/Snakefile b/workflows/simbig/Snakefile index 5486faa7..a4227bd2 100644 --- a/workflows/simbig/Snakefile +++ b/workflows/simbig/Snakefile @@ -162,6 +162,7 @@ else: bcftools convert {input.data} -O b -o {output} """ + rule split_chip: input: back = "pedsim/phased/background.bcf.gz", @@ -179,11 +180,27 @@ rule split_chip: "../../scripts/split_chip.py" +rule augment_background: + input: + background="background/segment{runs}.vcf.gz", + output: + "background/segment{runs}.augmented.vcf.gz" + params: + sample_count=1000 + log: + "logs/augment_background.log" + conda: + "vcf_to_ped" + script: + "../../scripts/simulate_dummy_samples.py" + + + rule simulate: input: _map=PEDSIM_MAP, intf='params/nu_p_campbell.tsv', - seg = "background/segment{runs}.vcf.gz" + seg = "background/segment{runs}.augmented.vcf.gz" output: temp('gen{runs}/data{runs}.vcf.gz'), temp('gen{runs}/data{runs}.seg') diff --git a/workflows/simbig/params/relatives_big.def b/workflows/simbig/params/relatives_big.def index cc180d61..b8ffa6c9 100644 --- a/workflows/simbig/params/relatives_big.def +++ b/workflows/simbig/params/relatives_big.def @@ -6,4 +6,4 @@ def first 1 8 5 32 2 6 32 2 7 64 2 -8 64 16 +8 64 2 From d143813f1981dd062da7f958c61e0dfddf269d58 Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Thu, 29 Jun 2023 11:57:28 +0400 Subject: [PATCH 15/19] Optional simulation background augmentation --- launcher.py | 8 ++++ workflows/pedsim/Snakefile | 42 ++++++++++++------- workflows/pedsim/ceph14.tsv | 14 +++++++ .../pedsim/params/relatives_16_founders.def | 9 ++++ workflows/simbig/Snakefile | 13 +++--- 5 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 workflows/pedsim/ceph14.tsv create mode 100644 workflows/pedsim/params/relatives_16_founders.def diff --git a/launcher.py b/launcher.py index e55319d7..55812d17 100644 --- a/launcher.py +++ b/launcher.py @@ -342,6 +342,13 @@ def get_parser_args(): help='Founders for simulation. The default value is 1kg. In this case, it will use 1000 genomes founders' ) + parser.add_argument( + '--augment-background', + default=0, + type=int, + help='How many samples to add to the background. The default value is 0. In this case, it will not add any samples to the background' + ) + args = parser.parse_args() valid_commands = [ @@ -510,6 +517,7 @@ def copy_input(input_dir, working_dir, samples_file): config_dict['sim_samples_number'] = args.sim_samples_number config_dict['seed'] = args.seed config_dict['background'] = args.background + config_dict['augment_background'] = args.augment_background if args.weight_mask: config_dict['weight_mask'] = os.path.join(args.directory, args.weight_mask) diff --git a/workflows/pedsim/Snakefile b/workflows/pedsim/Snakefile index 521f309a..234def90 100644 --- a/workflows/pedsim/Snakefile +++ b/workflows/pedsim/Snakefile @@ -28,6 +28,7 @@ need_phase = config['phase'] need_imputation = config['impute'] need_remove_imputation = config['remove_imputation'] simulation_seed = config['seed'] +augment_background = config['augment_background'] _IDEAL_LARGE_MEM_GB = 20 @@ -71,25 +72,35 @@ rule merge_background: bcftools view --force-samples --samples-file {input.eu} -O z -o {output} """ - -rule augment_background: - input: - background=rules.merge_background.output[0], - output: - 'pedsim/phased/background.augmented.vcf.gz' - params: - sample_count=100 - log: - "logs/augment_background.log" - conda: - "vcf_to_ped" - script: - "../../scripts/simulate_dummy_samples.py" +if augment_background: + rule augment_background: + input: + background=rules.merge_background.output[0], + output: + 'pedsim/phased/background.augmented.vcf.gz' + params: + sample_count=100 + log: + "logs/augment_background.log" + conda: + "vcf_to_ped" + script: + "../../scripts/simulate_dummy_samples.py" +else: + rule link_background: + input: + background=rules.merge_background.output[0] + output: + 'pedsim/phased/background.augmented.vcf.gz' + shell: + """ + ln -s {input.background} {output} + """ rule simulate: input: - bg=rules.augment_background.output, + bg=rules.merge_background.output[0], _map=PEDSIM_MAP, _def=config['sim_params_file'], intf='params/nu_p_campbell.tsv' @@ -105,6 +116,7 @@ rule simulate: shell: """ pedsim -d {input._def} -m {input._map} -i {input.bg} --keep_phase \ + --err_hom_rate 0.05 --err_rate 0.001 --miss_rate 0.0 \ -o {params.prefix} --intf {input.intf} --fam --seed {params.seed} """ diff --git a/workflows/pedsim/ceph14.tsv b/workflows/pedsim/ceph14.tsv new file mode 100644 index 00000000..2cfe6f57 --- /dev/null +++ b/workflows/pedsim/ceph14.tsv @@ -0,0 +1,14 @@ +NA06984 +NA12347 +NA12348 +NA06986 +NA07037 +NA07051 +NA12341 +NA12342 +NA12144 +NA06994 +NA07000 +NA07056 +NA12058 +NA07347 diff --git a/workflows/pedsim/params/relatives_16_founders.def b/workflows/pedsim/params/relatives_16_founders.def new file mode 100644 index 00000000..a91799fd --- /dev/null +++ b/workflows/pedsim/params/relatives_16_founders.def @@ -0,0 +1,9 @@ +def first 1 8 +1 1 1 +2 1 2 +3 1 2 +4 1 2 +5 1 2 +6 1 2 +7 1 2 +8 1 4 \ No newline at end of file diff --git a/workflows/simbig/Snakefile b/workflows/simbig/Snakefile index a4227bd2..7e70f5ee 100644 --- a/workflows/simbig/Snakefile +++ b/workflows/simbig/Snakefile @@ -83,8 +83,9 @@ def generate_def_file(def_file: str): # variables are number of runs (num_runs) and number of branches in each generation max_number_of_samples_per_run = 3000 num_founders_per_run = 14 - num_founders = get_num_founders(SIM_SAMPLES_FILE) + num_true_founders = get_num_founders(SIM_SAMPLES_FILE) num_runs = int(num_founders / num_founders_per_run) + num_founders = num_true_founders + num_founders_per_run * num_runs * 2 if max_number_of_samples_per_run * num_runs < SIM_SAMPLES_NUMBER: raise ValueError(f'Number of samples in sim-samples-file is too small for the total number of samples requested. Max number of samples per run is {max_number_of_samples_per_run}, number of runs is {num_runs}, total number of samples is {SIM_SAMPLES_NUMBER}') @@ -98,7 +99,7 @@ def generate_def_file(def_file: str): num_samples_per_run = int(SIM_SAMPLES_NUMBER / num_runs) siblings_scaling_factor = num_samples_per_run / 88 siblings = [1, 1, 2, 2, 2, 4, 4] - branches = [2, 2, 2, 2, 2, 2, 16] + branches = [2, 2, 2, 2, 2, 2, 2] siblings = [int(s * siblings_scaling_factor) for s in siblings] siblings[-1] += 2 actual_samples_per_run = sum([s*b for s, b in zip(siblings, branches)]) + num_founders_per_run @@ -107,7 +108,9 @@ def generate_def_file(def_file: str): with open(f'{def_file}.adjusted', 'w') as f: f.write(f'def first 1 8\n') f.write(f'1 1 1\n') + for i, (s, b) in enumerate(zip(siblings, branches)): + # def file format is generation, samples to print for each branch, number of branches f.write(f'{i+2} {s} {b}\n') print(f'for generation {i+2} we have {s} siblings and {b} branches') @@ -186,16 +189,15 @@ rule augment_background: output: "background/segment{runs}.augmented.vcf.gz" params: - sample_count=1000 + sample_count=28 log: - "logs/augment_background.log" + "logs/augment_background/segment{runs}.log" conda: "vcf_to_ped" script: "../../scripts/simulate_dummy_samples.py" - rule simulate: input: _map=PEDSIM_MAP, @@ -210,6 +212,7 @@ rule simulate: """ pedsim -d gen{wildcards.runs}/params/relatives_big.def \ -m {input._map} -i {input.seg} \ + --err_hom_rate 0.05 --err_rate 0.01 --miss_rate 0.0 \ -o gen{wildcards.runs}/data{wildcards.runs} \ --intf {input.intf} --keep_phase """ From 13bf44ed41f35ba3692ddd7622e2eea21e3ea53a Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Tue, 18 Jul 2023 11:11:41 +0400 Subject: [PATCH 16/19] Configurable simbig background augmentation --- analysis/GRAPE Article Visualisations.ipynb | 28 +++++++---- launcher.py | 4 +- scripts/evaluate.py | 6 +-- scripts/simulate_dummy_samples.py | 11 +++-- scripts/split_chip.py | 2 +- workflows/simbig/Snakefile | 54 ++++++++++++--------- workflows/simbig/params/relatives_big.def | 10 ++-- 7 files changed, 69 insertions(+), 46 deletions(-) diff --git a/analysis/GRAPE Article Visualisations.ipynb b/analysis/GRAPE Article Visualisations.ipynb index 6b69c19c..d9033eb6 100644 --- a/analysis/GRAPE Article Visualisations.ipynb +++ b/analysis/GRAPE Article Visualisations.ipynb @@ -392,6 +392,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "id": "eca1ef71", "metadata": {}, @@ -1009,6 +1010,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "id": "faed9661", "metadata": {}, @@ -1018,21 +1020,26 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": 2, "id": "84fe6040", "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ - "
" + "
" ] }, - "metadata": { - "needs_background": "light" - }, + "metadata": {}, "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "finished\n" + ] } ], "source": [ @@ -1047,7 +1054,8 @@ "samples = [100, 500, 1000, 2500, 10000, 100000]\n", "ibis = [12, 28, 1*60 + 7, 5*60 + 36, 22*60 + 14, 22*3600 + 3*60 + 15]\n", "ibis_king = [17, 39, 1*60 + 37, 8*60 + 59, 31*60 + 25, 33*3600 + 32*60 + 49]\n", - "\n", + "# rapid = [0, 0, 6*60+3, 0, 0, 0]\n", + "# 0:06:03\n", "def seconds_to_string(seconds):\n", " if seconds < 60:\n", " return strftime(\"%-Ss\", gmtime(seconds))\n", @@ -1082,10 +1090,12 @@ "plt.grid(color='lightgray', linestyle='--', linewidth=0.5)\n", "plt.tight_layout()\n", "plt.savefig('performance.pdf')\n", - "plt.show()" + "plt.show()\n", + "print(f'finished')" ] }, { + "attachments": {}, "cell_type": "markdown", "id": "f4da7de9", "metadata": {}, @@ -1275,7 +1285,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.9.16" }, "vscode": { "interpreter": { diff --git a/launcher.py b/launcher.py index 55812d17..891cf1db 100644 --- a/launcher.py +++ b/launcher.py @@ -329,7 +329,7 @@ def get_parser_args(): help='Random seed for Ped-sim pedigree simulation. The default value is randomly generated.') parser.add_argument( - '--sim_samples_number', + '--sim-samples-number', default=1000, type=int, help='Number of samples to simulate in Ped-sim pedigree simulation using simbig workflow.' @@ -531,7 +531,7 @@ def copy_input(input_dir, working_dir, samples_file): workdir=args.directory if args.command != 'reference' else args.ref_directory, cores=args.cores, unlock=args.unlock, - printshellcmds=True, + printshellcmds=False, dryrun=(not args.real_run), targets=args.target, stats=args.stat_file, diff --git a/scripts/evaluate.py b/scripts/evaluate.py index af4efcdb..58c41e26 100644 --- a/scripts/evaluate.py +++ b/scripts/evaluate.py @@ -17,9 +17,9 @@ def compare(total, correct, plot_name=None, cutoff=1): ds = [] cs = [] for i in sorted(total): - if i >= cutoff: + if i >= cutoff and i <= 12: c = correct.get(i, 0) - cs.append((c / total[i]) * 100) + cs.append(int((c / total[i]) * 100)) ds.append(i) df = pd.DataFrame(columns=['Degree of Relatedness', '% of true degree in predicted interval']) df.iloc[:, 0] = ds @@ -155,7 +155,7 @@ def get_interval_precision_recall_metrics(kinship, inferred, clients, source): false_positives[pred_degree] += 1 data = {'True Degree': [], 'Precision': [], 'Recall': []} - for degree in range(1, 14): + for degree in range(1, 12): tp, fp, fn = true_positives[degree], false_positives[degree], false_negatives[degree] if tp + fp > 0: precision = tp / (tp + fp) diff --git a/scripts/simulate_dummy_samples.py b/scripts/simulate_dummy_samples.py index 777201e4..68808b7b 100644 --- a/scripts/simulate_dummy_samples.py +++ b/scripts/simulate_dummy_samples.py @@ -10,6 +10,7 @@ def write_vcf_file(file_name, headers, calldata, variant_names, sample_names, sa # Writing metadata headers (e.g., file format, reference, etc.) for header in headers: vcf_file.write(header + "\n") + # Writing column header columns = ['#CHROM', 'POS', 'ID', 'REF', 'ALT', 'QUAL', 'FILTER', 'INFO', 'FORMAT'] @@ -60,8 +61,8 @@ def write_vcf_file(file_name, headers, calldata, variant_names, sample_names, sa # We read background.vcf.gz file vcf = allel.read_vcf(snakemake.input['background']) - print(f'We read vcf with {len(vcf["samples"])} samples from {snakemake.input["background"]}') print(f'vcf has {vcf.keys()} fields') + print(f'We read vcf with {len(vcf["samples"])} samples from {snakemake.input["background"]}') # the first dimension corresponds to the variants genotyped # the second dimension corresponds to the samples genotyped # the third dimension corresponds to the ploidy of the samples. @@ -71,10 +72,13 @@ def write_vcf_file(file_name, headers, calldata, variant_names, sample_names, sa indices = numpy.arange(gt.shape[0]) # number of variants new_samples = [] new_gt = [] - for i in range(0, len(samples), 2): + # ugly hack because bcftools somehow cannot divide multiallelic sites to biallelic ones + # and we need data to be biallelic only + gt[gt >= 2] = 0 + for i in range(0, len(samples)): if len(new_samples) >= snakemake.params['sample_count']: break - for j in range(1, len(samples), 2): + for j in range(i + 1, len(samples)): sample_gt_i = gt[:, i, :] sample_gg_j = gt[:, j, :] # We generate a mix of these two samples @@ -97,6 +101,7 @@ def write_vcf_file(file_name, headers, calldata, variant_names, sample_names, sa headers.append(line.strip()) if i > 2 and not line.startswith('##'): break + headers.append('##INFO=') # qual, filter, format calldata = [(qual, 'PASS', 'GT') for qual, fp in zip(vcf['variants/QUAL'], vcf['variants/FILTER_PASS'])] print(f'calldata {calldata[:10]}') diff --git a/scripts/split_chip.py b/scripts/split_chip.py index 638e0b11..f7ef7889 100644 --- a/scripts/split_chip.py +++ b/scripts/split_chip.py @@ -9,7 +9,7 @@ def split_chip(): lines = f.readlines() samples_list = [line.rstrip() for line in lines] - for i in range(0, (num_runs)*(num_founders), num_founders): + for i in range(0, num_runs*num_founders, num_founders): sample = samples_list[i:i + num_founders] with open("sample.txt", "w") as s: s.write("\n".join(sample)) diff --git a/workflows/simbig/Snakefile b/workflows/simbig/Snakefile index 7e70f5ee..bc3ba94d 100644 --- a/workflows/simbig/Snakefile +++ b/workflows/simbig/Snakefile @@ -13,7 +13,7 @@ CHROMOSOMES = [str(i) for i in range(1, 23)] DEF_FILE = config['sim_params_file'] SIM_SAMPLES_FILE = config['sim_samples_file'] SIM_SAMPLES_NUMBER = config['sim_samples_number'] - +AUGMENT_SAMPLES_NUMBER = config['augment_background'] names = [] def generate_code(): @@ -73,35 +73,43 @@ def prepare_folders(num_runs, def_file): first[1] = name lines[0] = " ".join(first) with open(f"gen{i}/params/relatives_big.def", "w") as f: - f.write("\n".join(l for l in lines if l != '\n')) + f.write("\n".join(l.strip() for l in lines if l != '\n')) def generate_def_file(def_file: str): - # we have number of founders fixed (14), + # we have number of founders fixed (14) + (14*3) from background augmentation, # number of total simulated samples fixed (SIM_SAMPLES_NUMBER), # and number of founders in the background (num_founders) fixed # variables are number of runs (num_runs) and number of branches in each generation max_number_of_samples_per_run = 3000 - num_founders_per_run = 14 + num_founders_per_run = 14 + AUGMENT_SAMPLES_NUMBER num_true_founders = get_num_founders(SIM_SAMPLES_FILE) - num_runs = int(num_founders / num_founders_per_run) - num_founders = num_true_founders + num_founders_per_run * num_runs * 2 + num_runs = int(num_true_founders / 14) + num_founders = num_true_founders + AUGMENT_SAMPLES_NUMBER * num_runs + if max_number_of_samples_per_run * num_runs < SIM_SAMPLES_NUMBER: - raise ValueError(f'Number of samples in sim-samples-file is too small for the total number of samples requested. Max number of samples per run is {max_number_of_samples_per_run}, number of runs is {num_runs}, total number of samples is {SIM_SAMPLES_NUMBER}') + raise ValueError(f'''Number of samples in sim-samples-file is too small + for the total number of samples requested. + Max number of samples per run is {max_number_of_samples_per_run}, + number of runs is {num_runs}, total number of samples is {SIM_SAMPLES_NUMBER}''') # SIM_SAMPLES_NUMBER = num_samples_per_run * num_runs # num_samples_per_run = num_founders_per_run + \sum_{i=1}^{num_generations} num_branches_per_generation * num_siblings_per_branch # we should make sure that num_branches_per_generation * num_siblings_per_branch is as small as possible - # for a relatives_big.def equation is 2 * (2x) + 3 * (2*2x) + 1 * (4*2x) + 1 * (4*16x) = num_samples_per_run - # 4x + 12x + 8x + 64x = num_samples_per_run - # 88x = num_samples_per_run - # x = num_samples_per_run / 88 + # nfpr + 2 + 1*4 + 2*16 + 2*16 + 2*16 + 4*24 + ??? = num_samples_per_run + # nfpr + 198x = num_samples_per_run + # x = (num_samples_per_run - nfpr) / 198 num_samples_per_run = int(SIM_SAMPLES_NUMBER / num_runs) - siblings_scaling_factor = num_samples_per_run / 88 + siblings_scaling_factor = num_samples_per_run / 198 + print(f'Number of runs is {num_runs}, number of founders is {num_founders}, number of true founders is {num_true_founders}') + print(f'Number of samples to simulate is {SIM_SAMPLES_NUMBER}, number of samples per run is {num_samples_per_run}') + print(f'Number of founders per run is {num_founders_per_run}') + print(f'Siblings scaling factor is {siblings_scaling_factor}') + siblings = [1, 1, 2, 2, 2, 4, 4] - branches = [2, 2, 2, 2, 2, 2, 2] - siblings = [int(s * siblings_scaling_factor) for s in siblings] - siblings[-1] += 2 + branches = [2, 4, 16, 16, 16, 24, 2] + siblings = [max(1, int(s * siblings_scaling_factor)) for s in siblings] + # siblings[-1] += 2 actual_samples_per_run = sum([s*b for s, b in zip(siblings, branches)]) + num_founders_per_run print(f'Actual number of samples per run is {actual_samples_per_run}') @@ -114,11 +122,11 @@ def generate_def_file(def_file: str): f.write(f'{i+2} {s} {b}\n') print(f'for generation {i+2} we have {s} siblings and {b} branches') - return f'{def_file}.adjusted' + return f'{def_file}.adjusted', num_runs, num_founders -adjusted_def_file = generate_def_file(DEF_FILE) -NUM_RUNS, NUM_FOUNDERS = get_num_runs(adjusted_def_file) +adjusted_def_file, NUM_RUNS, NUM_FOUNDERS = generate_def_file(DEF_FILE) +# NUM_RUNS, NUM_FOUNDERS = get_num_runs(adjusted_def_file) prepare_folders(NUM_RUNS, adjusted_def_file) @@ -153,7 +161,7 @@ if config['background'] == '1kg': bcftools concat -f {params.list} -O b -o {output} """ else: - rule convert_backgrond: + rule convert_background: input: data=config['background'] output: @@ -174,7 +182,7 @@ rule split_chip: params: # def_file = expand("gen{runs}/params/relatives_big.def", runs = list(range(NUM_RUNS))), num_runs = str(NUM_RUNS), - num_founders = str(NUM_FOUNDERS) + num_founders = 14 output: expand("background/segment{runs}.vcf.gz", runs = list(range(NUM_RUNS))) conda: @@ -189,7 +197,7 @@ rule augment_background: output: "background/segment{runs}.augmented.vcf.gz" params: - sample_count=28 + sample_count=AUGMENT_SAMPLES_NUMBER log: "logs/augment_background/segment{runs}.log" conda: @@ -214,7 +222,7 @@ rule simulate: -m {input._map} -i {input.seg} \ --err_hom_rate 0.05 --err_rate 0.01 --miss_rate 0.0 \ -o gen{wildcards.runs}/data{wildcards.runs} \ - --intf {input.intf} --keep_phase + --intf {input.intf} --retain_extra -1 --keep_phase """ @@ -227,7 +235,7 @@ rule convert: "bcftools" shell: """ - bcftools convert gen{wildcards.runs}/data{wildcards.runs}.vcf.gz -O b -o gen{wildcards.runs}/data4merge{wildcards.runs}.bcf.gz + bcftools norm -m-any {input} | bcftools convert -O b -o gen{wildcards.runs}/data4merge{wildcards.runs}.bcf.gz """ diff --git a/workflows/simbig/params/relatives_big.def b/workflows/simbig/params/relatives_big.def index b8ffa6c9..74893618 100644 --- a/workflows/simbig/params/relatives_big.def +++ b/workflows/simbig/params/relatives_big.def @@ -1,9 +1,9 @@ def first 1 8 1 1 1 2 16 2 -3 16 2 -4 32 2 -5 32 2 -6 32 2 -7 64 2 +3 16 4 +4 32 8 +5 32 8 +6 32 16 +7 64 16 8 64 2 From 6e1784dd55f34049523dca448c63b61b0da0d487 Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Fri, 28 Jul 2023 13:43:30 +0400 Subject: [PATCH 17/19] Fixed polars, pydor and other dependencies, removed conda envs --- containers/snakemake/Dockerfile | 11 ++++++-- envs/big_env.yaml | 46 ++++++++++++++++++++++++++++++ launcher.py | 2 +- rules/filter.smk | 4 --- rules/preprocessing.smk | 30 +++----------------- rules/relatives_ibis.smk | 13 --------- rules/relatives_rapid.smk | 32 ++++++++++----------- scripts/evaluate.py | 14 --------- scripts/interpolate.py | 4 --- scripts/postprocess_ersa.py | 47 ++++++++++++------------------- scripts/select_bad_samples.py | 2 +- scripts/simulate_dummy_samples.py | 2 +- workflows/pedsim/Snakefile | 11 -------- 13 files changed, 95 insertions(+), 123 deletions(-) create mode 100644 envs/big_env.yaml diff --git a/containers/snakemake/Dockerfile b/containers/snakemake/Dockerfile index d283462b..8d7c2537 100644 --- a/containers/snakemake/Dockerfile +++ b/containers/snakemake/Dockerfile @@ -14,10 +14,15 @@ ENV SHELL /bin/bash RUN apt-get update && apt-get install -y wget bzip2 gnupg2 git libgomp1 && \ wget -nv https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh && \ bash Miniconda3-latest-Linux-x86_64.sh -b -p /opt/conda && \ - rm Miniconda3-latest-Linux-x86_64.sh && \ - conda install -c conda-forge mamba + rm Miniconda3-latest-Linux-x86_64.sh + # conda install -n base --override-channels -c conda-forge mamba==1.4.5 'python_abi=*=*cp*' -RUN for e in envs/*; do mamba env create -f $e ; done && \ +# RUN for e in envs/*; do mamba env create -f $e ; done && \ +# conda clean --all -y + +RUN conda install -n base conda-libmamba-solver && \ + conda config --set solver libmamba && \ + conda env create -f envs/big_env.yaml && \ conda clean --all -y # Intall Minimac3 diff --git a/envs/big_env.yaml b/envs/big_env.yaml new file mode 100644 index 00000000..33156b21 --- /dev/null +++ b/envs/big_env.yaml @@ -0,0 +1,46 @@ +name: snakemake +channels: + - conda-forge + - bioconda + - biocore + - b3bg + - alex_genxt + - defaults +dependencies: + - python>=3.9 + - matplotlib + - pandas + - numpy + - seaborn + - biom-format + - scikit-bio + - docutils + - mmh3 + - bcftools + - samtools + - plink + - unzip + - wget + - openssl + - parallel + - ibis + - openjdk + - picard-slim + - ped-sim + - networkx + - scipy + - statsmodels + - rapid-ibd + - scikit-allel + - scikit-learn + - snakemake + - pip + - pip: + - polars + - pydot + - "--editable=git+https://github.com/alex-medvedev-msc/ersa.git#egg=ersa" + # - pip: + # - "--editable=git+https://github.com/Jahysama/snakemake.git#egg=snakemake" + #Fork of a snakemake is used here because of not working conda envs inside python scripts + #Please check out https://github.com/snakemake/snakemake/pull/1812 for more info + diff --git a/launcher.py b/launcher.py index 891cf1db..db76dd17 100644 --- a/launcher.py +++ b/launcher.py @@ -531,7 +531,7 @@ def copy_input(input_dir, working_dir, samples_file): workdir=args.directory if args.command != 'reference' else args.ref_directory, cores=args.cores, unlock=args.unlock, - printshellcmds=False, + printshellcmds=True, dryrun=(not args.real_run), targets=args.target, stats=args.stat_file, diff --git a/rules/filter.smk b/rules/filter.smk index e7fb1961..1b001632 100644 --- a/rules/filter.smk +++ b/rules/filter.smk @@ -90,8 +90,6 @@ rule plink_clean_up: params: input = 'plink/{batch}_merged_filter', out = 'plink/{batch}_merged_mapped' - conda: - 'plink' log: 'logs/plink/{batch}_plink_clean_up.log' benchmark: @@ -128,8 +126,6 @@ rule prepare_vcf: params: input = 'plink/{batch}_merged_mapped', vcf = 'vcf/{batch}_merged_mapped_sorted.vcf.gz' - conda: - 'bcf_plink' log: plink='logs/plink/{batch}_prepare_vcf.log', vcf='logs/vcf/{batch}_prepare_vcf.log' diff --git a/rules/preprocessing.smk b/rules/preprocessing.smk index 63282174..da400e63 100644 --- a/rules/preprocessing.smk +++ b/rules/preprocessing.smk @@ -14,8 +14,6 @@ if NUM_BATCHES > 1: temp(expand('vcf/batch{i}.txt',i=BATCHES)) params: num_batches=NUM_BATCHES - conda: - 'bcftools' shell: ''' bcftools query --list-samples input.vcf.gz >> vcf/samples.txt @@ -37,8 +35,6 @@ if NUM_BATCHES > 1: samples='vcf/{batch}.txt' output: vcf='vcf/{batch}.vcf.gz' - conda: - 'bcftools' shell: ''' bcftools view -S {input.samples} {input.vcf} -O z -o {output.vcf} --force-samples @@ -81,8 +77,6 @@ if assembly == 'hg38': vcf='vcf/{batch}_imputation_removed.vcf.gz' output: vcf=temp('vcf/{batch}_merged_lifted.vcf.gz') - conda: - 'liftover' log: 'logs/liftover/liftover{batch}.log' params: @@ -91,7 +85,7 @@ if assembly == 'hg38': mem_mb=_mem_gb_for_ram_hungry_jobs() * 1024 shell: ''' - JAVA_OPTS="-Xmx{params.mem_gb}g" picard LiftoverVcf WARN_ON_MISSING_CONTIG=true MAX_RECORDS_IN_RAM=5000 I={input.vcf} O={output.vcf} CHAIN={LIFT_CHAIN} REJECT=vcf/chr{wildcards.batch}_rejected.vcf.gz R={GRCH37_FASTA} |& tee -a {log} + JAVA_OPTS="-Xmx{params.mem_gb}g" picard -Xmx12g LiftoverVcf WARN_ON_MISSING_CONTIG=true MAX_RECORDS_IN_RAM=5000 I={input.vcf} O={output.vcf} CHAIN={LIFT_CHAIN} REJECT=vcf/chr{wildcards.batch}_rejected.vcf.gz R={GRCH37_FASTA} |& tee -a {log} ''' else: rule copy_liftover: @@ -110,11 +104,11 @@ if flow == 'rapid' or flow == 'germline-king': vcf = 'vcf/{batch}_imputation_removed.vcf.gz' output: bcf = 'vcf/{batch}_merged_mapped_sorted.bcf.gz' - conda: - 'bcftools' shell: ''' - bcftools annotate --set-id "%CHROM:%POS:%REF:%FIRST_ALT" {input.vcf} | bcftools view --min-af 0.05 -O b -o {output.bcf} + bcftools annotate --set-id "%CHROM:%POS:%REF:%FIRST_ALT" {input.vcf} | \ + bcftools view -t ^8:10428647-13469693,21:16344186-19375168,10:44555093-53240188,22:16051881-25095451,2:85304243-99558013,1:118434520-153401108,15:20060673-25145260,17:77186666-78417478,15:27115823-30295750,17:59518083-64970531,2:132695025-141442636,16:19393068-24031556,2:192352906-198110229 | \ + bcftools view --min-af 0.05 --types snps -O b -o {output.bcf} ''' else: include: '../rules/filter.smk' @@ -149,8 +143,6 @@ else: bcf=bcf_input output: vcf=vcf_output - conda: - 'bcftools' shell: ''' bcftools view {input.bcf} -O z -o {output.vcf} @@ -168,8 +160,6 @@ if not flow == 'rapid': bim='preprocessed/{batch}_data.bim' params: out='preprocessed/{batch}_data' - conda: - 'plink' log: 'logs/plink/convert_mapped_to_plink{batch}.log' benchmark: @@ -199,8 +189,6 @@ if not flow == 'rapid': bim='preprocessed/data.bim' threads: workflow.cores - conda: - 'plink' shell: ''' rm files_list.txt || true @@ -222,8 +210,6 @@ if not flow == 'rapid': batches_vcf='preprocessed/{batch}_data.vcf.gz' output: batches_vcf_index=temp('preprocessed/{batch}_data.vcf.gz.csi') - conda: - 'bcftools' shell: ''' bcftools index -f {input.batches_vcf} @@ -248,8 +234,6 @@ if not flow == 'rapid': params: batches_vcf=expand('batch{s}_data.vcf.gz',s=BATCHES), vcf='data.vcf.gz' - conda: - 'bcftools' shell: ''' rm complete_vcf_list.txt || true @@ -274,8 +258,6 @@ if not flow == 'rapid': bim='preprocessed/data.bim' params: out='preprocessed/data' - conda: - 'plink' log: 'logs/plink/convert_mapped_to_plink_batch1.log' benchmark: @@ -291,8 +273,6 @@ if not flow == 'rapid': bim='preprocessed/data.bim' params: genetic_map_GRCh37=expand(GENETIC_MAP_GRCH37,chrom=CHROMOSOMES) - conda: - 'ibis' output: 'preprocessed/data_mapped.bim' log: @@ -309,8 +289,6 @@ else: bcf = 'phase/batch1_merged_phased.bcf.gz' output: fam='preprocessed/data.fam' - conda: - 'bcftools' shell: ''' bcftools query --list-samples {input.bcf} >> {output.fam} diff --git a/rules/relatives_ibis.smk b/rules/relatives_ibis.smk index 3781bdd8..65a0784d 100644 --- a/rules/relatives_ibis.smk +++ b/rules/relatives_ibis.smk @@ -7,8 +7,6 @@ rule split_for_ibis: bed = "preprocessed/data.bed", fam = "preprocessed/data.fam", bim = "preprocessed/data_mapped.bim" - conda: - "plink" output: bed = "ibis_data/{chrom}.bed", fam = "ibis_data/{chrom}.fam", @@ -28,8 +26,6 @@ rule ibis_chrom_mapping: bim = "ibis_data/{chrom}.bim" params: genetic_map_GRCh37=GENETIC_MAP_GRCH37 - conda: - 'ibis' output: 'ibis_mapped/mapped_{chrom}.bim' log: @@ -46,8 +42,6 @@ rule ibis: bed = "ibis_data/{chrom}.bed", fam = "ibis_data/{chrom}.fam", bim = "ibis_mapped/mapped_{chrom}.bim" - conda: - "ibis" output: ibd = "ibis/merged_ibis_{chrom}.seg.gz" log: @@ -85,8 +79,6 @@ if config.get('weight_mask'): input: ibd = rules.ibis.output.ibd, script = os.path.join(SNAKEFILE_FOLDER, '../weight/apply_weight_mask.py') - conda: - 'weight-mask' output: ibd = os.path.join(WEIGHTED_IBD_SEGMENTS_FOLDER, 'ibis_weighted.seg'), params: @@ -111,8 +103,6 @@ checkpoint transform_ibis_segments: bucket_dir = directory("ibd") log: "logs/ibis/transform_ibis_segments.log" - conda: - "evaluation" script: "../scripts/transform_ibis_segments.py" @@ -128,8 +118,6 @@ rule ersa: output: ibd = temp('temp_ibd/{id}.tsv'), relatives="ersa/relatives_{id}.tsv" - conda: - "ersa" log: "logs/ersa/ersa_{id}.log" benchmark: @@ -179,6 +167,5 @@ rule postprocess_ersa: params: ibis = True output: "results/relatives.tsv" - conda: "evaluation" log: "logs/merge/postprocess-ersa.log" script: "../scripts/postprocess_ersa.py" diff --git a/rules/relatives_rapid.smk b/rules/relatives_rapid.smk index ee5e899a..7672023c 100644 --- a/rules/relatives_rapid.smk +++ b/rules/relatives_rapid.smk @@ -7,8 +7,8 @@ rule index_for_split: vcf = "preprocessed/data.vcf.gz" output: vcf = "preprocessed/data.vcf.gz.csi" - conda: - "bcftools" + # conda: + # "snakemake" log: "logs/vcf/index_for_split.log" benchmark: @@ -25,8 +25,8 @@ rule index_and_split: vcf_index = rules.index_for_split.output['vcf'] output: vcf = "vcf/for_rapid_{chrom}.vcf.gz" - conda: - "bcftools" + # conda: + # "bcftools" log: "logs/vcf/index_and_split-{chrom}.log" benchmark: @@ -40,8 +40,8 @@ rule index_and_split: rule estimate_rapid_params: input: vcf = rules.index_and_split.output['vcf'] - conda: - "rapid_params" + # conda: + # "rapid_params" log: "logs/rapid/estimate_rapid_params-{chrom}.log" output: @@ -60,8 +60,8 @@ rule interpolate: vcf = rules.index_and_split.output['vcf'], cmmap=CMMAP output: "cm/chr{chrom}.cm.g" - conda: - "interpolation" + # conda: + # "interpolation" log: "logs/cm/interpolate-{chrom}.log" script: @@ -89,12 +89,12 @@ rule rapid: num_runs = config['rapid_num_runs'], num_success = config['rapid_num_success'], min_cm_len=config['rapid_seg_len'], - window_size=3, + window_size=10, output_folder='rapid' output: seg = "rapid/chr{chrom}/results.max.gz" - conda: - "rapid" + # conda: + # "rapid" shell: """ rapidibd -i {input.vcf} -g {input.g} -d {params.min_cm_len} -o {params.output_folder}/chr{wildcards.chrom} \ @@ -123,8 +123,8 @@ checkpoint transform_rapid_segments: bucket_dir = directory("ibd") log: "logs/ibis/transform_rapid_segments.log" - conda: - "evaluation" + # conda: + # "evaluation" script: "../scripts/transform_rapid_segments.py" @@ -140,8 +140,8 @@ rule ersa: ibd = aggregate_input output: "ersa/relatives.tsv" - conda: - "ersa" + # conda: + # "ersa" log: "logs/ersa/ersa.log" benchmark: @@ -180,6 +180,6 @@ rule postprocess_ersa: params: ibis = False output: "results/relatives.tsv" - conda: "evaluation" + # conda: "evaluation" log: "logs/merge/postprocess-ersa.log" script: "../scripts/postprocess_ersa.py" diff --git a/scripts/evaluate.py b/scripts/evaluate.py index 58c41e26..e74b6aed 100644 --- a/scripts/evaluate.py +++ b/scripts/evaluate.py @@ -7,7 +7,6 @@ import logging import math import networkx as nx -import pydot from networkx.drawing.nx_pydot import graphviz_layout from utils.relatives import get_kinship, read_pedigree, relatives_to_graph @@ -105,16 +104,6 @@ def kinship_to_dataframe(kinship: nx.Graph): return data.set_index(['id1', 'id2']) -def draw_pedigree(pedigree: nx.DiGraph, pedigree_plot_name: str): - - plt.figure(figsize=(20, 20)) - first1 = pedigree.subgraph([n for n in pedigree.nodes if n.startswith('first1')]) - pos = graphviz_layout(first1, prog="dot") - nx.draw(first1, pos, with_labels=True) - plt.savefig(pedigree_plot_name) - plt.close() - - def get_interval_precision_recall_metrics(kinship, inferred, clients, source): iterator = itertools.combinations(clients, 2) @@ -190,8 +179,6 @@ def interval_evaluate( po_fs_plot_name=None ): iids, pedigree = read_pedigree(fn=fam) - if pedigree_plot_name is not None: - draw_pedigree(pedigree, pedigree_plot_name) kinship = get_kinship(pedigree) inferred, clients = relatives_to_graph(result, only_client) confusion_matrix = {} @@ -362,5 +349,4 @@ def draw_po_fs(merged, plot_name): snakemake.output['metrics'], only_client=True, source=source, - pedigree_plot_name=snakemake.output['pedigree_plot'], po_fs_plot_name=po_fs_plot_name) diff --git a/scripts/interpolate.py b/scripts/interpolate.py index bf90ea30..2bd5d4b8 100644 --- a/scripts/interpolate.py +++ b/scripts/interpolate.py @@ -25,9 +25,6 @@ def interpolate(vcf_path, cm_path, output_path, chrom): else: new_cms.append(cms[i]) - print(f'cms of len {len(cms)} after:') - - print(new_cms[-10:]) with open(output_path, 'w') as w_map: for i, cm in enumerate(new_cms): if i > 0 and cm <= new_cms[i-1]: @@ -42,5 +39,4 @@ def interpolate(vcf_path, cm_path, output_path, chrom): chrom = snakemake.wildcards['chrom'] output_path = snakemake.output[0] - print(vcf_path) interpolate(vcf_path, cm_path, output_path, chrom) \ No newline at end of file diff --git a/scripts/postprocess_ersa.py b/scripts/postprocess_ersa.py index 3e0c7793..38529ad1 100644 --- a/scripts/postprocess_ersa.py +++ b/scripts/postprocess_ersa.py @@ -30,7 +30,7 @@ def get_new_col_names(old_names): 'genetic_end_pos', 'genetic_seg_length', 'marker_count', 'error_count', 'error_density'] - data = pl.scan_csv(ibd_path, has_header=False, with_column_names=get_new_col_names, sep='\t', null_values="NA").lazy() + data = pl.scan_csv(ibd_path, has_header=False, with_column_names=get_new_col_names, separator='\t', null_values="NA").lazy() data = data.with_columns([ pl.col('id1').str.replace(':', '_'), pl.col('id2').str.replace(':', '_') @@ -58,7 +58,7 @@ def get_new_col_names(old_names): 'ersa_degree', 'ersa_lower_bound', 'ersa_upper_bound', 'seg_count', 'total_seg_len'] - data = pl.scan_csv(ersa_path, has_header=True, sep='\t', null_values="NA", + data = pl.scan_csv(ersa_path, has_header=True, separator='\t', null_values="NA", with_column_names=get_new_col_names, dtypes={'ersa_degree': str, 'ersa_lower_bound': str, 'ersa_upper_bound': str, 'seg_count': str, 'total_seg_len': str}) @@ -121,7 +121,7 @@ def get_new_col_names(old_names): ('total_seg_len_ibd2', pl.Float64), ('seg_count_ibd2', pl.Int32) ] - ibd = pl.DataFrame(data=[], columns=_columns).lazy() + ibd = pl.DataFrame(data=[], schema=_columns).lazy() ersa = read_ersa(ersa_path) @@ -129,57 +129,46 @@ def get_new_col_names(old_names): relatives = ibd.join(ersa, how='outer', on=['id1', 'id2']) # No left_index / right_index input params # It is impossible to have more than 50% of ibd2 segments unless you are monozygotic twins or duplicates. - GENOME_CM_LEN = 3440 + GENOME_CM_LEN = 3400 DUPLICATES_THRESHOLD = 1750 FS_TOTAL_THRESHOLD = 2100 FS_IBD2_THRESHOLD = 450 dup_mask = pl.col('total_seg_len_ibd2') > DUPLICATES_THRESHOLD fs_mask = (pl.col('total_seg_len') > FS_TOTAL_THRESHOLD) & (pl.col('total_seg_len_ibd2') > FS_IBD2_THRESHOLD) & ( dup_mask.is_not()) - po_mask = (pl.col('total_seg_len') / GENOME_CM_LEN > 0.8) & (fs_mask.is_not()) & (dup_mask.is_not()) + po_mask = (pl.col('total_seg_len') / GENOME_CM_LEN > 0.77) & (fs_mask.is_not()) & (dup_mask.is_not()) relatives = relatives.with_columns([ pl.when(dup_mask) .then(0) + .when(fs_mask) + .then(2) + .when(po_mask) + .then(1) + #.when((pl.col('ersa_degree') == 1)) # when ersa_degree is 1 but PO mask does not tell us that it is PO + #.then(2) .otherwise(pl.col('ersa_degree')) .alias('final_degree'), pl.when(dup_mask) .then('MZ/Dup') - .otherwise(pl.col('ersa_degree')) - .alias('relation'), - - pl.when(fs_mask) - .then(2) - .otherwise(pl.col('ersa_degree')) - .alias('final_degree'), - - pl.when(fs_mask) + .when(fs_mask) .then('FS') - .otherwise(pl.col('ersa_degree')) - .alias('relation'), - - pl.when(po_mask) - .then(1) - .otherwise(pl.col('ersa_degree')) - .alias('final_degree'), - - pl.when(po_mask) + .when(po_mask) .then('PO') + #.when((pl.col('ersa_degree') == 1)) # when ersa_degree is 1 but PO mask does not tell us that it is PO + #.then('FS') .otherwise(pl.col('ersa_degree')) .alias('relation'), - pl.when((po_mask.is_not()) & (pl.col('ersa_degree') == 1)) - .then(2) - .otherwise(pl.col('ersa_degree')) - .alias('final_degree'), - pl.col('total_seg_len_ibd2') .fill_null(pl.lit(0)), pl.col('seg_count_ibd2') .fill_null(pl.lit(0)) ]) + + ''' IBIS and ERSA do not distinguish IBD1 and IBD2 segments shared_genome_proportion is a proportion of identical alleles @@ -200,4 +189,4 @@ def get_new_col_names(old_names): f'{len(relatives.filter(fs_mask))} full siblings and ' f'{len(relatives.filter(po_mask))} parent-offspring relationships') logging.info(f'final degree not null: {len(relatives["final_degree"]) - relatives["final_degree"].null_count()}') - relatives.write_csv(output_path, sep='\t') + relatives.write_csv(output_path, separator='\t') diff --git a/scripts/select_bad_samples.py b/scripts/select_bad_samples.py index f37d80be..893b26e4 100644 --- a/scripts/select_bad_samples.py +++ b/scripts/select_bad_samples.py @@ -117,7 +117,7 @@ def get_stats(vcf_input, samples_path, psc_path=False, stats_path=''): psc.loc[bad_missing_samples_mask, 'exclusion_reason'] = f'Sample has >= {missing_samples}% missing SNPs' bad_samples = psc.loc[(bad_alt_hom_samples_mask | bad_het_samples_mask | bad_missing_samples_mask), ['sample_id', 'missing_share', 'alt_hom_share', 'het_samples_share', 'exclusion_reason']] - psc = psc.append(outliers[['sample_id', 'missing_share', 'alt_hom_share', 'het_samples_share', 'exclusion_reason']], ignore_index=True) + psc = pandas.concat([psc, outliers[['sample_id', 'missing_share', 'alt_hom_share', 'het_samples_share', 'exclusion_reason']]], ignore_index=True) samples_only = bad_samples.loc[:, ['sample_id']].copy() # if input vcf file has iids in form fid_iid we split it, else we just assign fid equal to iid diff --git a/scripts/simulate_dummy_samples.py b/scripts/simulate_dummy_samples.py index 68808b7b..c97ee0c0 100644 --- a/scripts/simulate_dummy_samples.py +++ b/scripts/simulate_dummy_samples.py @@ -84,7 +84,7 @@ def write_vcf_file(file_name, headers, calldata, variant_names, sample_names, sa # We generate a mix of these two samples change_indices = numpy.random.choice(indices, size=gt.shape[1] // 2, replace=False) sample_gt_i[change_indices, :] = sample_gg_j[change_indices, :] - new_samples.append(samples[i] + '_' + samples[j]) + new_samples.append(samples[i].replace('_', '-') + '_' + samples[j].replace('_', '-')) new_gt.append(numpy.expand_dims(sample_gt_i, axis=1)) if len(new_samples) >= snakemake.params['sample_count']: break diff --git a/workflows/pedsim/Snakefile b/workflows/pedsim/Snakefile index 234def90..dc0c3161 100644 --- a/workflows/pedsim/Snakefile +++ b/workflows/pedsim/Snakefile @@ -54,8 +54,6 @@ rule merge_background: 'pedsim/phased/background.vcf.gz' params: list='pedsim/phased/phased.merge.list' - conda: - 'bcftools' shell: """ # for now just skip empty files @@ -82,8 +80,6 @@ if augment_background: sample_count=100 log: "logs/augment_background.log" - conda: - "vcf_to_ped" script: "../../scripts/simulate_dummy_samples.py" else: @@ -111,8 +107,6 @@ rule simulate: params: prefix='pedsim/simulated/data', seed=simulation_seed - conda: - 'ped-sim' shell: """ pedsim -d {input._def} -m {input._map} -i {input.bg} --keep_phase \ @@ -129,8 +123,6 @@ rule postprocess: kin='pedsim/simulated/reheaded_data.kinship', vcf='input.vcf.gz', fam='pedsim/simulated/reheaded_data.fam' - conda: - 'postprocess' script: '../../scripts/postprocess.py' @@ -155,7 +147,6 @@ rule evaluate_accuracy: pr = 'results/precision_recall.png', conf_matrix = 'results/confusion_matrix.png', updated_rel = 'results/updated_relatives.tsv', - pedigree_plot = 'results/pedigree_plot.png', metrics = 'results/metrics.tsv' params: inferred = 'results/inferred.png', @@ -164,7 +155,5 @@ rule evaluate_accuracy: po_fs_plot='results/po_fs_plot.png' if flow == 'ibis' else None log: 'logs/evaluation/accuracy.log' - conda: - 'evaluation' script: '../../scripts/evaluate.py' From c29c655ea2a70519e62d2e2d3c62ac4670b05d98 Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Fri, 28 Jul 2023 13:59:42 +0400 Subject: [PATCH 18/19] Removed 3 more custom conda envs from snakemake rules --- rules/filter.smk | 6 ------ 1 file changed, 6 deletions(-) diff --git a/rules/filter.smk b/rules/filter.smk index 1b001632..b146659f 100644 --- a/rules/filter.smk +++ b/rules/filter.smk @@ -3,8 +3,6 @@ rule annotate_snp_ids: vcf = 'vcf/{batch}_merged_lifted.vcf.gz' output: vcf = 'vcf/{batch}_merged_annotated.vcf.gz' - conda: - 'bcftools' shell: ''' bcftools annotate --set-id "%CHROM:%POS:%REF:%FIRST_ALT" {input.vcf} -O z -o {output.vcf} @@ -29,8 +27,6 @@ rule select_bad_samples: psc='stats/{batch}_lifted_vcf.psc', keep_samples='stats/{batch}_keep_samples.list', - conda: - 'evaluation' script: '../scripts/select_bad_samples.py' @@ -43,8 +39,6 @@ rule plink_filter: bed = temp('plink/{batch}_merged_filter.bed'), bim = temp('plink/{batch}_merged_filter.bim'), fam = temp('plink/{batch}_merged_filter.fam') - conda: - 'plink' params: input = '{batch}_merged', out = '{batch}_merged_filter', From a997db9d99124153d7a9aacd59c87668270bce83 Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Fri, 28 Jul 2023 15:09:24 +0400 Subject: [PATCH 19/19] Fixed ibis_king workflow --- envs/big_env.yaml | 1 + rules/relatives_ibis_king.smk | 62 +++++++++++++++++------------------ scripts/merge_king_ersa.py | 6 +++- 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/envs/big_env.yaml b/envs/big_env.yaml index 33156b21..b82b8f23 100644 --- a/envs/big_env.yaml +++ b/envs/big_env.yaml @@ -33,6 +33,7 @@ dependencies: - rapid-ibd - scikit-allel - scikit-learn + - king - snakemake - pip - pip: diff --git a/rules/relatives_ibis_king.smk b/rules/relatives_ibis_king.smk index 8c1f9783..91a89526 100644 --- a/rules/relatives_ibis_king.smk +++ b/rules/relatives_ibis_king.smk @@ -14,8 +14,6 @@ rule run_king: out="king/data", kin="king/data" threads: workflow.cores - conda: - "king" log: "logs/king/run_king.log" benchmark: @@ -47,8 +45,6 @@ rule ibis: bed="preprocessed/data.bed", fam="preprocessed/data.fam", bim="preprocessed/data_mapped.bim" - conda: - "ibis" output: ibd = "ibis/merged_ibis.seg" log: @@ -74,8 +70,6 @@ if config.get('weight_mask'): input: ibd = rules.ibis.output.ibd, script = os.path.join(SNAKEFILE_FOLDER, '../weight/apply_weight_mask.py') - conda: - 'weight-mask' output: ibd = os.path.join(WEIGHTED_IBD_SEGMENTS_FOLDER, 'ibis_weighted.seg'), params: @@ -100,29 +94,25 @@ checkpoint transform_ibis_segments: bucket_dir = directory("ibd") log: "logs/ibis/transform_ibis_segments.log" - conda: - "evaluation" script: "../scripts/transform_ibis_segments.py" -def aggregate_input(wildcards): - checkpoints.transform_ibis_segments.get() - ids = glob_wildcards(f"ibd/{{id}}.tsv").id - return expand(f"ibd/{{id}}.tsv", id=ids) +def tis_output(wildcards): + checkpoints.transform_ibis_segments.get(id=wildcards.id) + return "ibd/{id}.tsv.gz" rule ersa: input: - ibd = aggregate_input + ibd = tis_output output: - "ersa/relatives.tsv" - conda: - "ersa" + ibd = temp('temp_ibd/{id}.tsv'), + relatives="ersa/relatives_{id}.tsv" log: - "logs/ersa/ersa.log" + "logs/ersa/ersa_{id}.log" benchmark: - "benchmarks/ersa/ersa.txt" + "benchmarks/ersa/ersa_{id}.txt" params: l = config['zero_seg_count'], th = config['zero_seg_len'], @@ -131,20 +121,31 @@ rule ersa: r = '--nomask ' + '-r ' + str(config['ersa_r']) if config.get('weight_mask') else '' shell: """ - touch {output} - FILES="{input.ibd}" - TEMPFILE=ersa/temp_relatives.tsv - rm -f $TEMPFILE - rm -f {output} + zcat {input.ibd} > {output.ibd} + ersa --avuncular-adj -ci -a {params.a} --dmax 14 -t {params.t} -l {params.l} \ + {params.r} -th {params.th} {output.ibd} -o {output.relatives} |& tee {log} + """ - for input_file in $FILES; do - ersa --avuncular-adj -ci -a {params.a} --dmax 14 -t {params.t} -l {params.l} \ - {params.r} -th {params.th} $input_file -o $TEMPFILE |& tee {log} +def aggregate_input(wildcards): + checkpoints.transform_ibis_segments.get() + ids = glob_wildcards(f"ibd/{{id}}.tsv.gz").id + return expand(f"ersa/relatives_{{id}}.tsv", id=ids) + + +rule merge_ersa: + input: + aggregate_input + output: + "ersa/relatives.tsv" + shell: + """ + FILES="{input}" + for input_file in $FILES; do if [[ "$input_file" == "${{FILES[0]}}" ]]; then - cat $TEMPFILE >> {output} + cat $input_file >> {output} else - sed 1d $TEMPFILE >> {output} + sed 1d $input_file >> {output} fi done """ @@ -156,8 +157,6 @@ rule split_map: output: expand("cm/chr{chrom}.cm.map", chrom=CHROMOSOMES) params: cm_dir='cm' - conda: - "evaluation" script: "../scripts/split_map.py" @@ -165,13 +164,12 @@ rule merge_king_ersa: input: king=rules.run_king.output['king'], king_segments=rules.run_king.output['segments'], - ersa=rules.ersa.output[0], + ersa=rules.merge_ersa.output[0], kinship=rules.run_king.output['kinship'], kinship0=rules.run_king.output['kinship0'], cm=expand("cm/chr{chrom}.cm.map", chrom=CHROMOSOMES) params: cm_dir='cm' output: "results/relatives.tsv" - conda: "evaluation" log: "logs/merge/merge-king-ersa.log" script: "../scripts/merge_king_ersa.py" diff --git a/scripts/merge_king_ersa.py b/scripts/merge_king_ersa.py index 209a9ed9..44082bc1 100644 --- a/scripts/merge_king_ersa.py +++ b/scripts/merge_king_ersa.py @@ -135,12 +135,16 @@ def read_king_segments_chunked(king_segments_path, map_dir): segments = interpolate_all(segments, map_dir) data = pandas.DataFrame(columns=['id1', 'id2', 'total_seg_len_king', 'seg_count_king']) logging.info(f'loaded and interpolated segments from chunk {i} for {len(segments)} pairs') + rows = [] for key, segs in segments.items(): row = {'id1': key[0], 'id2': key[1], 'total_seg_len_king': sum([s.cm_len for s in segs]), 'seg_count_king': len(segs)} - data = data.append(row, ignore_index=True) + rows.append(row) + + rows_frame = pandas.DataFrame.from_records(rows, columns=['id1', 'id2', 'total_seg_len_king', 'seg_count_king']) + data = pandas.concat([data, rows_frame], ignore_index=True) _sort_ids(data) data = data.set_index(['id1', 'id2'])