From 7e68898f43c6248465986d2b2891a6631cada19e Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Wed, 9 Sep 2015 14:31:53 -0400 Subject: [PATCH 01/19] Uses path to render file names in the method section. --- genipe/reporting/autoreport.py | 9 +-------- genipe/reporting/templates/main_template.tex | 3 +++ genipe/reporting/templates/parts/methods.tex | 6 +++++- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/genipe/reporting/autoreport.py b/genipe/reporting/autoreport.py index 1319dc1..25b60cc 100644 --- a/genipe/reporting/autoreport.py +++ b/genipe/reporting/autoreport.py @@ -165,15 +165,8 @@ def _generate_methods(templates, run_options, run_information): # The input files data_files = [ - "{}.{}".format(run_options.bfile, ext) for ext in ["bed", "bim", "fam"] + "{}.{}".format(run_options.bfile, ext) for ext in ("bed", "bim", "fam") ] - data_files = [ - format_tex(sanitize_tex(text), "texttt") for text in data_files - ] - - # Creating the iteration - data_files = itemize_template.render(iteration_type="itemize", - iteration_list=data_files) # The text for the different steps steps = [] diff --git a/genipe/reporting/templates/main_template.tex b/genipe/reporting/templates/main_template.tex index 3de3d5a..23ca075 100644 --- a/genipe/reporting/templates/main_template.tex +++ b/genipe/reporting/templates/main_template.tex @@ -35,6 +35,9 @@ % Ordinal numbers \usepackage{engord} +% To use the path command +\usepackage{url} + % The Report number... \newcommand{\ReportNumber}{\VAR{ report_number }} diff --git a/genipe/reporting/templates/parts/methods.tex b/genipe/reporting/templates/parts/methods.tex index efb3864..4b0836c 100644 --- a/genipe/reporting/templates/parts/methods.tex +++ b/genipe/reporting/templates/parts/methods.tex @@ -4,7 +4,11 @@ markers (including \VAR{ nb_special_markers } markers located on sexual or mitochondrial chromosomes): -\VAR{ data_files } +\begin{itemize} +\BLOCK{ for data_file in data_files } + \item \path{\VAR{ data_file }} +\BLOCK{ endfor } +\end{itemize} IMPUTE2's pre-phasing approach can work with phased haplotypes from SHAPEIT, a highly accurate phasing algorithm that can handle mixtures of unrelated From 07e2fda047c8fe711c02551adbede600e73e7386 Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Wed, 9 Sep 2015 14:38:17 -0400 Subject: [PATCH 02/19] Non-autosomal chromosomes... --- genipe/reporting/autoreport.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/genipe/reporting/autoreport.py b/genipe/reporting/autoreport.py index 25b60cc..81d54ef 100644 --- a/genipe/reporting/autoreport.py +++ b/genipe/reporting/autoreport.py @@ -196,12 +196,12 @@ def _generate_methods(templates, run_options, run_information): format_tex("C", "texttt") + "/" + format_tex("G", "texttt") + sanitize_tex( ", duplicated markers (same position), and markers located on " - "special chromosomes (sexual or mitochondrial chromosomes) were " - "excluded from the imputation. " + "sexual or mitochondrial chromosomes were excluded from the " + "imputation. " ) + to_add_1 + format_tex( sanitize_tex( "In total, {ambiguous} ambiguous, {duplicated} duplicated and " - "{special} special markers were excluded.".format( + "{special} non-autosomal markers were excluded.".format( ambiguous=run_information["nb_ambiguous"], duplicated=run_information["nb_duplicates"], special=run_information["nb_special_markers"], From 30b09a9d06fe4b75ff5aeca48628b529acc871fa Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Wed, 9 Sep 2015 14:56:21 -0400 Subject: [PATCH 03/19] Changed the status of the main module... --- genipe/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genipe/__init__.py b/genipe/__init__.py index 6b7e9e3..0fcede6 100644 --- a/genipe/__init__.py +++ b/genipe/__init__.py @@ -19,7 +19,7 @@ __license__ = "CC BY-NC 4.0" __maintainer__ = "Louis-Philippe Lemieux Perreault" __email__ = "louis-philippe.lemieux.perreault@statgen.org" -__status__ = "Development" +__status__ = "Production" chromosomes = range(1, 23) From 625b481aa3a9b2ec27465b69a053a7a1306ca9c6 Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Thu, 10 Sep 2015 08:23:03 -0400 Subject: [PATCH 04/19] Now possible to modify the background section from command line --- genipe/pipeline.py | 16 ++++++++++++---- genipe/reporting/autoreport.py | 14 ++++++++++++-- genipe/reporting/templates/parts/background.tex | 4 ---- 3 files changed, 24 insertions(+), 10 deletions(-) delete mode 100644 genipe/reporting/templates/parts/background.tex diff --git a/genipe/pipeline.py b/genipe/pipeline.py index 5db347e..eb665b0 100644 --- a/genipe/pipeline.py +++ b/genipe/pipeline.py @@ -958,9 +958,8 @@ def exclude_markers_before_phasing(prefix, db_name, options): is on the reverse strand and needs flipping. The information returned includes the initial number of markers and - samples, the number of ambiguous, duplicated, special (not on autosomal - chromosomes) markers, along with the number of markers to flip if the - reference was checked. + samples, the number of ambiguous, duplicated and non-autosomal markers, + along with the number of markers to flip if the reference was checked. """ # The ambiguous genotypes @@ -1044,7 +1043,7 @@ def exclude_markers_before_phasing(prefix, db_name, options): reference.close() # Logging - logging.info(" - {:,d} special markers".format(nb_special_markers)) + logging.info(" - {:,d} non-autosomal markers".format(nb_special_markers)) logging.info(" - {:,d} ambiguous markers removed".format(nb_ambiguous)) logging.info(" - {:,d} duplicated markers removed".format(nb_dup)) logging.info(" - {:,d} markers kept".format(nb_kept)) @@ -2430,6 +2429,15 @@ def parse_args(parser): default="Automatically generated by genipe", help="The report author. [%(default)s]", ) + group.add_argument( + "--report-background", + type=str, + metavar="BACKGROUND", + default="The aim of this project is to perform genome-wide imputation " + "using the study cohort.", + help="The report background section (can either be a string or a file " + "containing the background. [General background]", + ) return parser.parse_args() diff --git a/genipe/reporting/autoreport.py b/genipe/reporting/autoreport.py index 81d54ef..6471940 100644 --- a/genipe/reporting/autoreport.py +++ b/genipe/reporting/autoreport.py @@ -109,16 +109,26 @@ def _generate_background(templates, run_options, run_information): str: a string representation of the "background" section """ + # Some assertion + assert "report_background" in run_options + + # The background can either be a file or a string + background_content = run_options.report_background + if os.path.isfile(background_content): + with open(background_content, "r") as i_file: + background_content = " ".join( + line for line in i_file.read().splitlines() if line != "" + ) + # Loading the template section_template = templates.get_template("section_template.tex") - background = templates.get_template("parts/background.tex") # Returning the section return section_template.render( section_name="Background", section_type="section", section_label="sec:background", - section_content=background.render(**run_information), + section_content=sanitize_tex(background_content), ) diff --git a/genipe/reporting/templates/parts/background.tex b/genipe/reporting/templates/parts/background.tex deleted file mode 100644 index 51ff40d..0000000 --- a/genipe/reporting/templates/parts/background.tex +++ /dev/null @@ -1,4 +0,0 @@ - -The aim of this project is to perform genome-wide imputation using the study -cohort. - From 6bd4729a48d9de146dac667da59c6b25cfbedbcf Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Thu, 10 Sep 2015 11:00:42 -0400 Subject: [PATCH 05/19] Creation of a bar plot instead of a pie chart --- genipe/pipeline.py | 111 ++++++++---------- genipe/reporting/autoreport.py | 14 +-- .../reporting/templates/parts/frequencies.tex | 4 +- genipe/tests/test_main_pipeline.py | 12 +- 4 files changed, 63 insertions(+), 78 deletions(-) diff --git a/genipe/pipeline.py b/genipe/pipeline.py index eb665b0..06e8751 100644 --- a/genipe/pipeline.py +++ b/genipe/pipeline.py @@ -1675,8 +1675,8 @@ def gather_maf_stats(o_dir): | ``pct_maf_geq_01_lt_05`` | the percentage of markers with | | | 1% >= MAF < 5% | +--------------------------+----------------------------------------------+ - | ``frequency_pie`` | the name of the file containing the pie | - | | chart (if it was created) | + | ``frequency_barh`` | the name of the file containing the bar plot | + | | (if it was created) | +--------------------------+----------------------------------------------+ """ @@ -1774,76 +1774,61 @@ def gather_maf_stats(o_dir): else: logging.warning("There were no marker with MAF (something went wrong)") - # Generating a pie chart if matplotlib is installed - frequency_pie = "" + # Generating a bar plot if matplotlib is installed + frequency_barh = "" if (nb_marker_with_maf > 0) and HAS_MATPLOTLIB: # Creating a figure and axe - figure, axe = plt.subplots(1, 1, figsize=(6, 9)) + figure, axe = plt.subplots(1, 1, figsize=(15, 3)) # The colors colors = ["#0099CC", "#669900", "#FF8800"] - # The data for the pie chart - labels = [ - "{:.1f}%".format(nb_maf_lt_01 / nb_marker_with_maf * 100), - "{:.1f}%".format(nb_maf_geq_01_lt_05 / nb_marker_with_maf * 100), - "{:.1f}%".format(nb_maf_geq_05 / nb_marker_with_maf * 100), - ] + # The labels + ylabels = [r"$MAF < 1\%$", r"$1\% \leq MAF < 5\%$", r"$MAF \geq 5\%$"] + + # The data sizes = [nb_maf_lt_01, nb_maf_geq_01_lt_05, nb_maf_geq_05] - explode = (0.05, 0.05, 0.05) - wedges, texts = axe.pie(sizes, explode=explode, labels=labels, - colors=colors, startangle=90, - wedgeprops={"linewidth": 0}, - textprops={"fontsize": 12, "weight": "bold"}) - - # Changing the label parameters - for text in texts: - text.set_bbox({"boxstyle": "round", "fc": "#C0C0C0", - "ec": "#C0C0C0"}) - - # Shrink current axis by 50% - bbox = axe.get_position() - axe.set_position([bbox.x0, bbox.y0, bbox.width * 0.5, - bbox.height * 0.5]) - - # If no restriction was performed while imputing, there will be a high - # majority of ultra rare variants... We need to move the text box if - # they touch - # Getting the renderer and the bboxes - renderer = figure.canvas.get_renderer() - bbox_1 = texts[1].get_window_extent(renderer=renderer) - bbox_2 = texts[2].get_window_extent(renderer=renderer) - while bbox_1.overlaps(bbox_2): - # Moving the first label a bit - x, y = texts[1].get_position() - texts[1].set_x(x + 0.006) - bbox_1 = texts[1].get_window_extent(renderer=renderer) - - # Moving the second label (MAF >= 0.05) - x, y = texts[2].get_position() - texts[2].set_x(x - 0.036) - bbox_2 = texts[2].get_window_extent(renderer=renderer) - - # Adding a legend in the margin with custom patches - ultra_rare = mpatches.Patch(color=colors[0], linewidth=3) - rare = mpatches.Patch(color=colors[1], linewidth=3) - common = mpatches.Patch(color=colors[2], linewidth=3) - axe.legend( - [ultra_rare, rare, common], - [r"$MAF < 1\%$", r"$1\% \leq MAF < 5\%$", r"$MAF \geq 5\%$"], - bbox_to_anchor=(1.3, 1), - loc="upper left", - ncol=1, - frameon=False, - fontsize=21, - ) - # Setting the axis to equal size - axe.axis("equal") + # Plotting the horizontal bar plot + axe.barh(bottom=range(len(sizes)), width=sizes, color=colors, lw=0, + align="center") + + # Formatting the x tick labels + xticks = axe.get_xticks() + int_xticks = [int(xtick) for xtick in xticks] + if (xticks == int_xticks).all(): + # The tick labels are integer + axe.set_xticklabels(["{:,d}".format(tick) for tick in int_xticks]) + + # Formatting the y tick labels + axe.set_yticks([0, 1, 2]) + axe.set_yticklabels(ylabels, fontsize=18) + + # Adding the xlabel + axe.set_xlabel("Number of sites", weight="bold", fontsize=18) + + # Removing the y axis + axe.spines["left"].set_visible(False) + axe.spines["right"].set_visible(False) + axe.get_yaxis().set_tick_params(left="off", right="off") + + # Removing the x axis + axe.spines["top"].set_visible(False) + axe.get_xaxis().tick_bottom() + + # Adding the annotation + xmin, xmax = axe.get_xlim() + annotations = [ + "{:.1f}%".format(v / nb_marker_with_maf * 100) for v in sizes + ] + for i, annotation in enumerate(annotations): + axe.text((xmax - xmin) * 0.02, i, annotation, ha="left", + va="center", weight="bold", fontsize=14, + bbox=dict(boxstyle="round", fc="white", ec="white")) # Saving and closing the figure - frequency_pie = os.path.join(o_dir, "frequency_pie.pdf") - plt.savefig(frequency_pie, bbox_inches="tight", figure=figure) + frequency_barh = os.path.join(o_dir, "frequency_barh.pdf") + plt.savefig(frequency_barh, bbox_inches="tight", figure=figure) plt.close(figure) return { @@ -1859,7 +1844,7 @@ def gather_maf_stats(o_dir): "pct_maf_lt_05": "{:.1f}".format(pct_maf_lt_05), "pct_maf_lt_01": "{:.1f}".format(pct_maf_lt_01), "pct_maf_geq_01_lt_05": "{:.1f}".format(pct_maf_geq_01_lt_05), - "frequency_pie": frequency_pie, + "frequency_barh": frequency_barh, } diff --git a/genipe/reporting/autoreport.py b/genipe/reporting/autoreport.py index 6471940..15d4998 100644 --- a/genipe/reporting/autoreport.py +++ b/genipe/reporting/autoreport.py @@ -53,7 +53,7 @@ def generate_report(out_dir, run_opts, run_info): } # We want to copy the figures to the right place - figures = ["frequency_pie"] + figures = ["frequency_barh"] for figure in figures: assert figure in run_info, figure if run_info[figure] != "": @@ -297,7 +297,7 @@ def _generate_results(templates, run_options, run_information): "nb_maf_geq_05", "nb_maf_lt_05", "nb_maf_lt_01", "nb_maf_geq_01_lt_05", "pct_maf_geq_01", "pct_maf_geq_05", "pct_maf_lt_05", "pct_maf_lt_01", - "pct_maf_geq_01_lt_05", "frequency_pie"] + "pct_maf_geq_01_lt_05", "frequency_barh"] for required_variable in required_variables: assert required_variable in run_information, required_variable @@ -418,9 +418,9 @@ def _generate_results(templates, run_options, run_information): section_label="subsec:completion_rate", ) - # Do we have a frequency pie? + # Do we have a frequency bar plot? frequency_float = "" - if run_information["frequency_pie"] != "": + if run_information["frequency_barh"] != "": frequency_float = create_float( template=float_template, float_type="figure", @@ -431,11 +431,11 @@ def _generate_results(templates, run_options, run_information): "more.".format(run_information["rate_threshold"], run_information["prob_threshold"]) )), - label="fig:frequency_pie", + label="fig:frequency_barh", placement="H", content=graphics_template.render( - width=r"0.5\textwidth", - path=run_information["frequency_pie"], + width=r"0.9\textwidth", + path=run_information["frequency_barh"], ), ) run_information["frequency_float"] = frequency_float diff --git a/genipe/reporting/templates/parts/frequencies.tex b/genipe/reporting/templates/parts/frequencies.tex index 19335c6..dbc1497 100644 --- a/genipe/reporting/templates/parts/frequencies.tex +++ b/genipe/reporting/templates/parts/frequencies.tex @@ -5,8 +5,8 @@ allele frequency (MAF) $\geq 1\%$, \VAR{ nb_maf_geq_05 } (\VAR{ pct_maf_geq_05 }\%) variants with a MAF $\geq 5\%$, and \VAR{ nb_maf_lt_05 } (\VAR{ pct_maf_lt_05 }\%) variants with a MAF -$<5\%$.\BLOCK{ if frequency_pie == "" }\\\BLOCK{ else } - Figure~\ref{fig:frequency_pie} shows the proportions of ultra rare +$<5\%$.\BLOCK{ if frequency_barh == "" }\\\BLOCK{ else } + Figure~\ref{fig:frequency_barh} shows the proportions of ultra rare ($MAF<1\%$), rare ($1\%\leq MAF < 5\%$) and common ($MAF\geq 5\%$) variants.\\ \VAR{ frequency_float } diff --git a/genipe/tests/test_main_pipeline.py b/genipe/tests/test_main_pipeline.py index 5ea3bae..feb10d3 100644 --- a/genipe/tests/test_main_pipeline.py +++ b/genipe/tests/test_main_pipeline.py @@ -393,10 +393,10 @@ def test_gather_maf_stats(self): "marker_39", "marker_41", "marker_43", "marker_44"] # The PDF generated - frequency_pie = "" + frequency_barh = "" if HAS_MATPLOTLIB: - frequency_pie = os.path.join(self.output_dir.name, - "frequency_pie.pdf") + frequency_barh = os.path.join(self.output_dir.name, + "frequency_barh.pdf") # The expected results nb_sites = len(good_sites) expected_results = { @@ -412,7 +412,7 @@ def test_gather_maf_stats(self): "nb_maf_geq_01_lt_05": "12", "pct_maf_geq_01_lt_05": "{:.1f}".format(12 / nb_sites * 100), "nb_maf_nan": "0", - "frequency_pie": frequency_pie, + "frequency_barh": frequency_barh, } # Creating the files for the test @@ -460,9 +460,9 @@ def test_gather_maf_stats(self): # If matplotlib is installed, checking we have a figure (and not # otherwise) if HAS_MATPLOTLIB: - self.assertTrue(os.path.isfile(frequency_pie)) + self.assertTrue(os.path.isfile(frequency_barh)) else: - self.assertFalse(os.path.isfile(frequency_pie)) + self.assertFalse(os.path.isfile(frequency_barh)) # Testing an invalid entry changed_filename = filename_template.format(chrom=1, suffix="maf") From 7c724be4159eeafa240eae1a578a4b63f5f3336b Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Thu, 10 Sep 2015 11:02:12 -0400 Subject: [PATCH 06/19] Fixes #16: precision while testing MixedLM was decreased --- genipe/tests/test_imputed_stats.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/genipe/tests/test_imputed_stats.py b/genipe/tests/test_imputed_stats.py index 77f7ef4..fc41899 100644 --- a/genipe/tests/test_imputed_stats.py +++ b/genipe/tests/test_imputed_stats.py @@ -2189,7 +2189,7 @@ def test_fit_mixedlm(self): observed_z, observed_p, = observed # Comparing the results - self.assertAlmostEqual(expected_coef, observed_coef, places=10) + self.assertAlmostEqual(expected_coef, observed_coef, places=9) self.assertAlmostEqual(expected_se, observed_se, places=10) self.assertAlmostEqual(expected_min_ci, observed_min_ci, places=4) self.assertAlmostEqual(expected_max_ci, observed_max_ci, places=4) @@ -2226,7 +2226,7 @@ def test_fit_mixedlm(self): self.assertAlmostEqual(expected_se, observed_se, places=10) self.assertAlmostEqual(expected_min_ci, observed_min_ci, places=4) self.assertAlmostEqual(expected_max_ci, observed_max_ci, places=4) - self.assertAlmostEqual(expected_z, observed_z, places=8) + self.assertAlmostEqual(expected_z, observed_z, places=7) self.assertAlmostEqual(np.log10(expected_p), np.log10(observed_p), places=8) @@ -2319,7 +2319,7 @@ def test_fit_mixedlm_use_ml(self): observed_z, observed_p, = observed # Comparing the results - self.assertAlmostEqual(expected_coef, observed_coef, places=10) + self.assertAlmostEqual(expected_coef, observed_coef, places=9) self.assertAlmostEqual(expected_se, observed_se, places=10) self.assertAlmostEqual(expected_min_ci, observed_min_ci, places=5) self.assertAlmostEqual(expected_max_ci, observed_max_ci, places=5) @@ -2356,7 +2356,7 @@ def test_fit_mixedlm_use_ml(self): self.assertAlmostEqual(expected_se, observed_se, places=10) self.assertAlmostEqual(expected_min_ci, observed_min_ci, places=5) self.assertAlmostEqual(expected_max_ci, observed_max_ci, places=5) - self.assertAlmostEqual(expected_z, observed_z, places=8) + self.assertAlmostEqual(expected_z, observed_z, places=7) self.assertAlmostEqual(np.log10(expected_p), np.log10(observed_p), places=8) From ca96c73196bbdd20ecd4c0c1305625ad4e924864 Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Thu, 10 Sep 2015 11:09:53 -0400 Subject: [PATCH 07/19] Fixed the intersphinx mapping for Pandas --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 1e22ee7..a3b14bf 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -295,6 +295,6 @@ intersphinx_mapping = { 'https://docs.python.org/3.4': None, 'numpy': ('http://docs.scipy.org/doc/numpy/', None), - 'pandas': ('http://pandas.pydata.org/pandas-docs/dev', None), + 'pandas': ('http://pandas.pydata.org/pandas-docs/stable/', None), 'jinja2': ('http://jinja.pocoo.org/docs', None), } From b0603f29c375f5b2789e7d235da6951599401fba Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Thu, 10 Sep 2015 11:38:24 -0400 Subject: [PATCH 08/19] Updated documentation --- docs/_static/tutorial/report.pdf | Bin 296874 -> 302261 bytes docs/index.rst | 7 ++- docs/tutorials/tutorial_SKAT.rst | 87 +++++++++++++++++++++++++++ docs/tutorials/tutorial_cox.rst | 2 +- docs/tutorials/tutorial_extract.rst | 2 +- docs/tutorials/tutorial_genipe.rst | 10 ++- docs/tutorials/tutorial_linear.rst | 2 +- docs/tutorials/tutorial_logistic.rst | 2 +- docs/tutorials/tutorial_mixedlm.rst | 2 +- 9 files changed, 106 insertions(+), 8 deletions(-) diff --git a/docs/_static/tutorial/report.pdf b/docs/_static/tutorial/report.pdf index e241ed5880b796bf265e12745e6203ca051b4bdd..7ef257deeb75ad1b6fa02f0ff9b80222e809b61e 100644 GIT binary patch delta 55090 zcmZU)1yCH{8?_084?z+%!F_OoySux4aCaw-ySux)OK=Mo+${umw;+M#_y2acwrZ

pu6q=X6Evr{?xgapW*uOe~2XA=L4T*i7(? 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 CDNR$FFsW+3 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 T1RM=AIW;*lGzujpMNdWwSlHic diff --git a/docs/index.rst b/docs/index.rst index f881d7f..3ab9ca8 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -55,9 +55,10 @@ Usage [--probability FLOAT] [--completion FLOAT] [--info FLOAT] [--report-number NB] [--report-title TITLE] [--report-author AUTHOR] + [--report-background BACKGROUND] Execute the genome-wide imputation pipeline. This script is part of the - 'genipe' package, version 1.2.1. + 'genipe' package, version 1.2.2. optional arguments: -h, --help show this help message and exit @@ -126,6 +127,10 @@ Usage imputation] --report-author AUTHOR The report author. [Automatically generated by genipe] + --report-background BACKGROUND + The report background section (can either be a string + or a file containing the background. [General + background] About diff --git a/docs/tutorials/tutorial_SKAT.rst b/docs/tutorials/tutorial_SKAT.rst index a7daf8e..7837346 100644 --- a/docs/tutorials/tutorial_SKAT.rst +++ b/docs/tutorials/tutorial_SKAT.rst @@ -9,6 +9,7 @@ Quick navigation 3. :ref:`skat-tut-3` 4. :ref:`skat-tut-4` 5. :ref:`skat-tut-5` +6. :ref:`skat-tut-6` SKAT analysis tutorial ----------------------- @@ -130,6 +131,7 @@ thresholds. Only the variants in this file will be considered by the ``imputed-stats`` tool. + .. _skat-tut-2: Creating a SNP sets file @@ -189,6 +191,7 @@ variants. If you want to use custom weights based on something la deleteriousness prediction, you need to generate the weights manually and provide them in the SNP set file as shown here. + .. _skat-tut-3: Format for the phenotypes file @@ -220,6 +223,7 @@ file, as well as covariates. The covariates are included in the analysis using the ``--covar`` argument and the variable type of the outcome is given using the ``--outcome-type`` argument which can be set to `discrete` or `continuous`. + .. _skat-tut-4: Running the script @@ -282,6 +286,7 @@ The line by line explanation of this command is as follows: Also note that the SKAT-O algorithm can be used by using the ``--skat-o`` flag. + .. _skat-tut-5: Results @@ -314,3 +319,85 @@ If you have any questions or problems, don't hesitate to contact us through our We are also very open to suggestions and improvements. If you have developed a new interesting feature, please send us a push request from Github. + + +.. _skat-tut-6: + +Usage +^^^^^^ + +The following command will display the documentation for the SKAT analysis in +the console: + +.. code-block:: console + + $ imputed-stats skat --help + usage: imputed-stats skat [-h] [-v] [--debug] --impute2 FILE --sample FILE + --pheno FILE [--extract-sites FILE] [--out FILE] + [--nb-process INT] [--nb-lines INT] [--chrx] + [--gender-column NAME] [--scale INT] [--prob FLOAT] + [--maf FLOAT] [--covar NAME] [--categorical NAME] + [--missing-value NAME] [--sample-column NAME] + [--interaction NAME] --snp-sets FILE --outcome-type + {continuous,discrete} [--skat-o] --pheno-name NAME + + Uses the SKAT R package to analyze user defined gene sets. This script is part + of the 'genipe' package, version 1.2.2). + + optional arguments: + -h, --help show this help message and exit + -v, --version show program's version number and exit + --debug set the logging level to debug + + Input Files: + --impute2 FILE The output from IMPUTE2. + --sample FILE The sample file (the order should be the same as in + the IMPUTE2 files). + --pheno FILE The file containing phenotypes and co variables. + --extract-sites FILE A list of sites to extract for analysis (optional). + + Output Options: + --out FILE The prefix for the output files. [imputed_stats] + + General Options: + --nb-process INT The number of process to use. [1] + --nb-lines INT The number of line to read at a time. [1000] + --chrx The analysis is performed for the non pseudo-autosomal + region of the chromosome X (male dosage will be + divided by 2 to get values [0, 0.5] instead of [0, 1]) + (males are coded as 1 and option '--gender-column' + should be used). + --gender-column NAME The name of the gender column (use to exclude samples + with unknown gender (i.e. not 1, male, or 2, female). + If gender not available, use 'None'. [Gender] + + Dosage Options: + --scale INT Scale dosage so that values are in [0, n] (possible + values are 1 (no scaling) or 2). [2] + --prob FLOAT The minimal probability for which a genotype should be + considered. [>=0.9] + --maf FLOAT Minor allele frequency threshold for which marker will + be skipped. [<0.01] + + Phenotype Options: + --covar NAME The co variable names (in the phenotype file), + separated by coma. + --categorical NAME The name of the variables that are categorical (note + that the gender is always categorical). The variables + are separated by coma. + --missing-value NAME The missing value in the phenotype file. + --sample-column NAME The name of the sample ID column (in the phenotype + file). [sample_id] + --interaction NAME Add an interaction between the genotype and this + variable. + + SKAT Options: + --snp-sets FILE A file indicating a snp_set and an optional weight for + every variant. + --outcome-type {continuous,discrete} + The variable type for the outcome. This will be passed + to SKAT. + --skat-o By default, the regular SKAT is used. Setting this + flag will use the SKAT-O algorithm instead. + --pheno-name NAME The phenotype. + diff --git a/docs/tutorials/tutorial_cox.rst b/docs/tutorials/tutorial_cox.rst index 3f97acb..b5981bb 100644 --- a/docs/tutorials/tutorial_cox.rst +++ b/docs/tutorials/tutorial_cox.rst @@ -233,7 +233,7 @@ in the console: NAME Performs a survival regression on imputed data using Cox's proportional hazard - model. This script is part of the 'genipe' package, version 1.2.1). + model. This script is part of the 'genipe' package, version 1.2.2). optional arguments: -h, --help show this help message and exit diff --git a/docs/tutorials/tutorial_extract.rst b/docs/tutorials/tutorial_extract.rst index b1ccb2d..12ca2f7 100644 --- a/docs/tutorials/tutorial_extract.rst +++ b/docs/tutorials/tutorial_extract.rst @@ -239,7 +239,7 @@ analysis in the console: [--rate FLOAT] [--info FLOAT] Extract imputed markers located in a specific genomic region. This script is - part of the 'genipe' package, version 1.2.1). + part of the 'genipe' package, version 1.2.2). optional arguments: -h, --help show this help message and exit diff --git a/docs/tutorials/tutorial_genipe.rst b/docs/tutorials/tutorial_genipe.rst index ebd4130..3e5a7ce 100644 --- a/docs/tutorials/tutorial_genipe.rst +++ b/docs/tutorials/tutorial_genipe.rst @@ -352,8 +352,8 @@ genome-wide imputation of the *HapMap* CEU dataset. ``--impute2-bin`` and/or the ``--plink-bin`` options if the SHAPEIT, IMPUTE2 and/or the Plink binaries are in the ``PATH`` variable. -The following table describes the option used by :py:mod:`genipe` in the -previous command (see the :ref:`genipe-usage` section for a full list): +The following table describes the options **used by** :py:mod:`genipe` **in the +previous command** (see the :ref:`genipe-usage` section for a full list): .. table:: @@ -411,6 +411,12 @@ previous command (see the :ref:`genipe-usage` section for a full list): subsequent steps). +.. note:: + + Four options will modify the report content: ``--report-number``, + ``--report-title``, ``--report-author`` and ``--report-background``. + + .. _genipe-tut-compile-report: Compiling the report diff --git a/docs/tutorials/tutorial_linear.rst b/docs/tutorials/tutorial_linear.rst index 5d11a6e..fe49a81 100644 --- a/docs/tutorials/tutorial_linear.rst +++ b/docs/tutorials/tutorial_linear.rst @@ -245,7 +245,7 @@ analysis in the console: --pheno-name NAME Performs a linear regression (ordinary least squares) on imputed data. This - script is part of the 'genipe' package, version 1.2.1). + script is part of the 'genipe' package, version 1.2.2). optional arguments: -h, --help show this help message and exit diff --git a/docs/tutorials/tutorial_logistic.rst b/docs/tutorials/tutorial_logistic.rst index 2e3d572..19ce34c 100644 --- a/docs/tutorials/tutorial_logistic.rst +++ b/docs/tutorials/tutorial_logistic.rst @@ -236,7 +236,7 @@ regression analysis in the console: --pheno-name NAME Performs a logistic regression on imputed data using a GLM with a binomial - distribution. This script is part of the 'genipe' package, version 1.2.1). + distribution. This script is part of the 'genipe' package, version 1.2.2). optional arguments: -h, --help show this help message and exit diff --git a/docs/tutorials/tutorial_mixedlm.rst b/docs/tutorials/tutorial_mixedlm.rst index 3fc78af..036ecd8 100644 --- a/docs/tutorials/tutorial_mixedlm.rst +++ b/docs/tutorials/tutorial_mixedlm.rst @@ -255,7 +255,7 @@ effects analysis in the console: Performs a linear mixed effects regression on imputed data using a random intercept for each group. This script is part of the 'genipe' package, version - 1.2.1). + 1.2.2). optional arguments: -h, --help show this help message and exit From 6082ee8d7bc03880720d2be764dffcd1695da01d Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Thu, 10 Sep 2015 12:52:39 -0400 Subject: [PATCH 09/19] Now possible to only index impute2 files --- genipe/tools/impute2_extractor.py | 60 ++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/genipe/tools/impute2_extractor.py b/genipe/tools/impute2_extractor.py index 2a2ccc9..412311d 100644 --- a/genipe/tools/impute2_extractor.py +++ b/genipe/tools/impute2_extractor.py @@ -6,6 +6,7 @@ # http://creativecommons.org/licenses/by-nc/4.0/ or send a letter to Creative # Commons, PO Box 1866, Mountain View, CA 94042, USA. + import os import re import sys @@ -49,24 +50,35 @@ def main(args=None): # Parsing the options args = parse_args(parser, args) - # Getting the output directory (dirname of the output prefix + # Getting the output directory (dirname of the output prefix) out_dir = os.path.dirname(args.out) + # The logging handlers + handlers = [logging.StreamHandler()] + if not args.index_only: + log_file = args.out + ".log" + logging_fh = logging.FileHandler(log_file, mode="w") + handlers.append(logging_fh) + # Adding the logging capability - log_file = args.out + ".log" - logging_fh = logging.FileHandler(log_file, mode="w") logging.basicConfig( format="[%(asctime)s %(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S", level=logging.DEBUG if args.debug else logging.INFO, - handlers=[logging.StreamHandler(), logging_fh] + handlers=handlers, ) - logging.info("Logging everything into '{}'".format(log_file)) + + # First log + if not args.index_only: + logging.info("Logging everything into '{}'".format(log_file)) logging.info("Program arguments: '{}'".format(" ".join(sys.argv[1:]))) # Checking the options check_args(args) + if args.index_only: + return index_files(args.impute2) + # Gathering what needs to be extracted to_extract = gather_extraction( i_filenames=args.impute2, @@ -105,6 +117,27 @@ def main(args=None): logging_fh.close() +def index_files(i_filenames): + """Indexes the impute2 files. + + Args: + i_filenames (list): the list of input file names + + This function uses the :py:func:`genipe.formats.index.get_index` to create + the index file if it's missing. + + Note + ---- + We won't catch the :py:class:`genipe.error.ProgramError` exception if + it's raised, since the message will be relevant to the user. + + """ + # For each input file + for i_filename in i_filenames: + get_index(i_filename, cols=[0, 1, 2], names=["chrom", "name", "pos"], + sep=" ") + + def extract_markers(i_filenames, to_extract, out_prefix, out_format, prob_t): """Extracts according to names. @@ -376,12 +409,20 @@ def check_args(args): If there is a problem, a :py:class:`genipe.error.ProgramError` is raised. + Note + ---- + Noting is checked (apart from the impute2 files) if indexation is asked + (``--index`` option). + """ # Checking that the impute2 files exists for filename in args.impute2: if not os.path.isfile(filename): raise ProgramError("{}: no such file".format(filename)) + if args.index_only: + return True + # Is there something to extract? if not args.genomic and not args.maf and not args.rate and not args.info: if args.extract is None: @@ -501,6 +542,15 @@ def parse_args(parser, args=None): help="The output from IMPUTE2.", ) + # Indexation options + group = parser.add_argument_group("Indexation Options") + group.add_argument( + "--index", + dest="index_only", + action="store_true", + help="Only perform the indexation.", + ) + # The output files group = parser.add_argument_group("Output Options") group.add_argument( From 24bf94efd207e7ee31efab341988f8b601d250f6 Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Thu, 10 Sep 2015 13:36:18 -0400 Subject: [PATCH 10/19] Now extracts companion files for impute2 --- genipe/tools/impute2_extractor.py | 58 +++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/genipe/tools/impute2_extractor.py b/genipe/tools/impute2_extractor.py index 412311d..5c65b94 100644 --- a/genipe/tools/impute2_extractor.py +++ b/genipe/tools/impute2_extractor.py @@ -202,6 +202,14 @@ def extract_markers(i_filenames, to_extract, out_prefix, out_format, prob_t): # Keeping track of what has been extracted all_extracted |= extracted + # Extracting the companion files (if impute2 and files are present) + if "impute2" in o_files: + extract_companion_files( + i_prefix=get_file_prefix(i_filename), + to_extract=names, + o_prefix=out_prefix, + ) + # Closing the files for o_file in o_files.values(): o_file.close() @@ -211,6 +219,56 @@ def extract_markers(i_filenames, to_extract, out_prefix, out_format, prob_t): "completed".format(len(all_extracted))) +def extract_companion_files(i_prefix, o_prefix, to_extract): + """Extract markers from companion files (if they exists). + + Args: + i_prefix (str): the prefix of the input file + o_prefix (str): the prefix of the output file + to_extract (set): the set of markers to extract + + """ + file_info = [ + dict(suffix=".alleles", header=True, name="name"), + dict(suffix=".completion_rates", header=True, name="name"), + dict(suffix=".good_sites", header=False, index=0), + dict(suffix=".impute2_info", header=True, name="name"), + dict(suffix=".imputed_sites", header=False, index=0), + dict(suffix=".maf", header=True, name="name"), + dict(suffix=".map", header=False, index=1), + ] + + for info in file_info: + # The name of the input file + i_fn = i_prefix + info["suffix"] + + if not os.path.isfile(i_fn): + # The file doesn't exist, so we continue + continue + + # The name of the output file + o_fn = o_prefix + info["suffix"] + + # If the file doesn't have a header, we just read line per line + if not info["header"]: + with open(i_fn, "r") as i_file, open(o_fn, "w") as o_file: + for line in i_file: + row = line.rstrip("\r\n").split(info.get("sep", "\t")) + if row[info["index"]] in to_extract: + o_file.write(line) + + else: + # We use pandas to speed up the analysis + data = pd.read_csv(i_fn, sep=info.get("sep", "\t")) + + # Extracting + data[data["name"].isin(to_extract)].to_csv( + o_fn, + sep=info.get("sep", "\t"), + index=False, + ) + + def print_data(o_files, prob_t, *, line=None, row=None): """Prints an impute2 line. From 480fb55d847e2ff4d248ef9d72b30ca41234b4c6 Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Thu, 10 Sep 2015 14:04:57 -0400 Subject: [PATCH 11/19] Doesn't use DataFrame anymore --- genipe/tools/impute2_extractor.py | 35 ++++++++++++++++--------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/genipe/tools/impute2_extractor.py b/genipe/tools/impute2_extractor.py index 5c65b94..8bab379 100644 --- a/genipe/tools/impute2_extractor.py +++ b/genipe/tools/impute2_extractor.py @@ -250,23 +250,24 @@ def extract_companion_files(i_prefix, o_prefix, to_extract): o_fn = o_prefix + info["suffix"] # If the file doesn't have a header, we just read line per line - if not info["header"]: - with open(i_fn, "r") as i_file, open(o_fn, "w") as o_file: - for line in i_file: - row = line.rstrip("\r\n").split(info.get("sep", "\t")) - if row[info["index"]] in to_extract: - o_file.write(line) - - else: - # We use pandas to speed up the analysis - data = pd.read_csv(i_fn, sep=info.get("sep", "\t")) - - # Extracting - data[data["name"].isin(to_extract)].to_csv( - o_fn, - sep=info.get("sep", "\t"), - index=False, - ) + header = None + with open(i_fn, "r") as i_file, open(o_fn, "w") as o_file: + for i, line in enumerate(i_file): + row = line.rstrip("\r\n").split(info.get("sep", "\t")) + if info["header"] and i == 0: + header = {name: i for i, name in enumerate(row)} + if info["name"] not in header: + raise ProgramError("{}: missing column {}".format( + i_fn, + info["name"], + )) + + info["index"] = header[info["name"]] + o_file.write(line) + continue + + if row[info["index"]] in to_extract: + o_file.write(line) def print_data(o_files, prob_t, *, line=None, row=None): From 290349e6ab0da7c629b386ac69c03546dc8b2e06 Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Thu, 10 Sep 2015 14:05:53 -0400 Subject: [PATCH 12/19] Tested the extraction of the info file --- genipe/tests/test_impute2_extractor.py | 274 +++++++++++++++++++++++++ 1 file changed, 274 insertions(+) diff --git a/genipe/tests/test_impute2_extractor.py b/genipe/tests/test_impute2_extractor.py index bd09a86..b23d659 100644 --- a/genipe/tests/test_impute2_extractor.py +++ b/genipe/tests/test_impute2_extractor.py @@ -177,6 +177,23 @@ def test_extract(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (impute2_info) + info_fn = template_name.format(ext="impute2_info") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "chr\tname\tposition\ta0\ta1\texp_freq_a1\tinfo\tcertainty\t" + "type\tinfo_type0\tconcord_type0\tr2_type0\n" + "1\trs23456\t3214569\tT\tC\t0.082\t0.362\t0.866\t0\t-1\t-1\t-1\n" + "1\trs23457_2\t3214572\tT\tTC\t0.084\t0.203\t0.854\t0\t-1\t-1\t" + "-1\n" + "1\t1:4214570_1\t4214570\tT\tTC\t0.174\t0.589\t0.831\t0\t-1\t-1\t" + "-1\n" + ) + observed = None + with open(info_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic(self): """Tests the extraction by genomic location.""" # Executing the script @@ -228,6 +245,25 @@ def test_genomic(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (impute2_info) + info_fn = template_name.format(ext="impute2_info") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "chr\tname\tposition\ta0\ta1\texp_freq_a1\tinfo\tcertainty\t" + "type\tinfo_type0\tconcord_type0\tr2_type0\n" + "1\trs23457\t3214570\tT\tTC\t0.126\t0.299\t0.832\t0\t-1\t-1\t-1\n" + "1\trs23457_1\t3214571\tT\tTC\t0.060\t0.300\t0.909\t0\t-1\t-1\t" + "-1\n" + "1\trs23457_2\t3214572\tT\tTC\t0.084\t0.203\t0.854\t0\t-1\t-1\t" + "-1\n" + "1\t1:3214573\t3214573\tT\tTC\t0.371\t0.339\t0.619\t0\t-1\t-1\t" + "-1\n" + ) + observed = None + with open(info_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_maf(self): """Tests the extraction by maf.""" # Executing the script @@ -282,6 +318,28 @@ def test_maf(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (impute2_info) + info_fn = template_name.format(ext="impute2_info") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "chr\tname\tposition\ta0\ta1\texp_freq_a1\tinfo\tcertainty\t" + "type\tinfo_type0\tconcord_type0\tr2_type0\n" + "1\trs23456\t3214569\tT\tC\t0.082\t0.362\t0.866\t0\t-1\t-1\t" + "-1\n" + "1\trs23457\t3214570\tT\tTC\t0.126\t0.299\t0.832\t0\t-1\t-1\t" + "-1\n" + "1\trs23457_1\t3214571\tT\tTC\t0.060\t0.300\t0.909\t0\t-1\t" + "-1\t-1\n" + "1\trs23457_2\t3214572\tT\tTC\t0.084\t0.203\t0.854\t0\t-1\t" + "-1\t-1\n" + "1\t1:3214573\t3214573\tT\tTC\t0.371\t0.339\t0.619\t0\t-1\t" + "-1\t-1\n" + ) + observed = None + with open(info_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_rate(self): """Tests the extraction by completion rate.""" # Executing the script @@ -339,6 +397,30 @@ def test_rate(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (impute2_info) + info_fn = template_name.format(ext="impute2_info") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "chr\tname\tposition\ta0\ta1\texp_freq_a1\tinfo\tcertainty\t" + "type\tinfo_type0\tconcord_type0\tr2_type0\n" + "1\trs12345\t1231415\tA\tG\t0.006\t0.359\t0.987\t0\t-1\t-1\t" + "-1\n" + "1\trs23456\t3214569\tT\tC\t0.082\t0.362\t0.866\t0\t-1\t-1\t" + "-1\n" + "1\trs23457\t3214570\tT\tTC\t0.126\t0.299\t0.832\t0\t-1\t-1\t" + "-1\n" + "1\trs23457_1\t3214571\tT\tTC\t0.060\t0.300\t0.909\t0\t-1\t" + "-1\t-1\n" + "1\trs23457_2\t3214572\tT\tTC\t0.084\t0.203\t0.854\t0\t-1\t" + "-1\t-1\n" + "1\t1:3214573\t3214573\tT\tTC\t0.371\t0.339\t0.619\t0\t-1\t" + "-1\t-1\n" + ) + observed = None + with open(info_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_info(self): """Tests the extraction by information value.""" # Executing the script @@ -394,6 +476,28 @@ def test_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (impute2_info) + info_fn = template_name.format(ext="impute2_info") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "chr\tname\tposition\ta0\ta1\texp_freq_a1\tinfo\tcertainty\t" + "type\tinfo_type0\tconcord_type0\tr2_type0\n" + "1\trs12345\t1231415\tA\tG\t0.006\t0.359\t0.987\t0\t-1\t-1\t" + "-1\n" + "1\trs23456\t3214569\tT\tC\t0.082\t0.362\t0.866\t0\t-1\t-1\t" + "-1\n" + "1\trs23457_1\t3214571\tT\tTC\t0.060\t0.300\t0.909\t0\t-1\t" + "-1\t-1\n" + "1\t1:3214573\t3214573\tT\tTC\t0.371\t0.339\t0.619\t0\t-1\t" + "-1\t-1\n" + "1\t1:4214570_1\t4214570\tT\tTC\t0.174\t0.589\t0.831\t0\t-1\t" + "-1\t-1\n" + ) + observed = None + with open(info_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_maf(self): """Tests the extraction by genomic location and maf.""" # Executing the script @@ -437,6 +541,20 @@ def test_genomic_maf(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (impute2_info) + info_fn = template_name.format(ext="impute2_info") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "chr\tname\tposition\ta0\ta1\texp_freq_a1\tinfo\tcertainty\t" + "type\tinfo_type0\tconcord_type0\tr2_type0\n" + "1\trs23456\t3214569\tT\tC\t0.082\t0.362\t0.866\t0\t-1\t-1\t" + "-1\n" + ) + observed = None + with open(info_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_rate(self): """Tests the extraction by genomic location and completion rate.""" # Executing the script @@ -480,6 +598,20 @@ def test_genomic_rate(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (impute2_info) + info_fn = template_name.format(ext="impute2_info") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "chr\tname\tposition\ta0\ta1\texp_freq_a1\tinfo\tcertainty\t" + "type\tinfo_type0\tconcord_type0\tr2_type0\n" + "1\trs12345\t1231415\tA\tG\t0.006\t0.359\t0.987\t0\t-1\t-1\t" + "-1\n" + ) + observed = None + with open(info_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_info(self): """Tests the extraction by genomic location and information value.""" # Executing the script @@ -535,6 +667,28 @@ def test_genomic_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (impute2_info) + info_fn = template_name.format(ext="impute2_info") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "chr\tname\tposition\ta0\ta1\texp_freq_a1\tinfo\tcertainty\t" + "type\tinfo_type0\tconcord_type0\tr2_type0\n" + "1\trs12345\t1231415\tA\tG\t0.006\t0.359\t0.987\t0\t-1\t-1\t" + "-1\n" + "1\trs23456\t3214569\tT\tC\t0.082\t0.362\t0.866\t0\t-1\t-1\t" + "-1\n" + "1\trs23457\t3214570\tT\tTC\t0.126\t0.299\t0.832\t0\t-1\t-1\t" + "-1\n" + "1\trs23457_1\t3214571\tT\tTC\t0.060\t0.300\t0.909\t0\t-1\t" + "-1\t-1\n" + "1\t1:3214573\t3214573\tT\tTC\t0.371\t0.339\t0.619\t0\t-1\t" + "-1\t-1\n" + ) + observed = None + with open(info_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_maf_rate(self): """Tests the extraction by maf and completion rate.""" # Executing the script @@ -590,6 +744,28 @@ def test_maf_rate(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (impute2_info) + info_fn = template_name.format(ext="impute2_info") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "chr\tname\tposition\ta0\ta1\texp_freq_a1\tinfo\tcertainty\t" + "type\tinfo_type0\tconcord_type0\tr2_type0\n" + "1\trs23456\t3214569\tT\tC\t0.082\t0.362\t0.866\t0\t-1\t-1\t" + "-1\n" + "1\trs23457\t3214570\tT\tTC\t0.126\t0.299\t0.832\t0\t-1\t-1\t" + "-1\n" + "1\trs23457_1\t3214571\tT\tTC\t0.060\t0.300\t0.909\t0\t-1\t" + "-1\t-1\n" + "1\trs23457_2\t3214572\tT\tTC\t0.084\t0.203\t0.854\t0\t-1\t" + "-1\t-1\n" + "1\t1:3214573\t3214573\tT\tTC\t0.371\t0.339\t0.619\t0\t-1\t" + "-1\t-1\n" + ) + observed = None + with open(info_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_maf_info(self): """Tests the extraction by maf and information value.""" # Executing the script @@ -639,6 +815,24 @@ def test_maf_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (impute2_info) + info_fn = template_name.format(ext="impute2_info") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "chr\tname\tposition\ta0\ta1\texp_freq_a1\tinfo\tcertainty\t" + "type\tinfo_type0\tconcord_type0\tr2_type0\n" + "1\trs23456\t3214569\tT\tC\t0.082\t0.362\t0.866\t0\t-1\t-1\t" + "-1\n" + "1\trs23457_1\t3214571\tT\tTC\t0.060\t0.300\t0.909\t0\t-1\t" + "-1\t-1\n" + "1\t1:3214573\t3214573\tT\tTC\t0.371\t0.339\t0.619\t0\t-1\t" + "-1\t-1\n" + ) + observed = None + with open(info_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_rate_info(self): """Tests the extraction by completion rate and information value.""" # Executing the script @@ -685,6 +879,22 @@ def test_rate_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (impute2_info) + info_fn = template_name.format(ext="impute2_info") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "chr\tname\tposition\ta0\ta1\texp_freq_a1\tinfo\tcertainty\t" + "type\tinfo_type0\tconcord_type0\tr2_type0\n" + "1\trs12345\t1231415\tA\tG\t0.006\t0.359\t0.987\t0\t-1\t-1\t" + "-1\n" + "1\trs23456\t3214569\tT\tC\t0.082\t0.362\t0.866\t0\t-1\t-1\t" + "-1\n" + ) + observed = None + with open(info_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_maf_rate(self): """Tests the extraction by genomic location, maf and rate.""" # Executing the script @@ -741,6 +951,28 @@ def test_genomic_maf_rate(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (impute2_info) + info_fn = template_name.format(ext="impute2_info") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "chr\tname\tposition\ta0\ta1\texp_freq_a1\tinfo\tcertainty\t" + "type\tinfo_type0\tconcord_type0\tr2_type0\n" + "1\trs23456\t3214569\tT\tC\t0.082\t0.362\t0.866\t0\t-1\t-1\t" + "-1\n" + "1\trs23457\t3214570\tT\tTC\t0.126\t0.299\t0.832\t0\t-1\t-1\t" + "-1\n" + "1\trs23457_1\t3214571\tT\tTC\t0.060\t0.300\t0.909\t0\t-1\t" + "-1\t-1\n" + "1\trs23457_2\t3214572\tT\tTC\t0.084\t0.203\t0.854\t0\t-1\t" + "-1\t-1\n" + "1\t1:3214573\t3214573\tT\tTC\t0.371\t0.339\t0.619\t0\t-1\t" + "-1\t-1\n" + ) + observed = None + with open(info_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_maf_info(self): """Tests the extraction by genomic location, maf and information.""" # Executing the script @@ -785,6 +1017,20 @@ def test_genomic_maf_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (impute2_info) + info_fn = template_name.format(ext="impute2_info") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "chr\tname\tposition\ta0\ta1\texp_freq_a1\tinfo\tcertainty\t" + "type\tinfo_type0\tconcord_type0\tr2_type0\n" + "1\trs23456\t3214569\tT\tC\t0.082\t0.362\t0.866\t0\t-1\t-1\t" + "-1\n" + ) + observed = None + with open(info_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_maf_rate_info(self): """Tests the extraction by maf, completion rate and information.""" # Executing the script @@ -829,6 +1075,20 @@ def test_maf_rate_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (impute2_info) + info_fn = template_name.format(ext="impute2_info") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "chr\tname\tposition\ta0\ta1\texp_freq_a1\tinfo\tcertainty\t" + "type\tinfo_type0\tconcord_type0\tr2_type0\n" + "1\trs12345\t1231415\tA\tG\t0.006\t0.359\t0.987\t0\t-1\t-1\t" + "-1\n" + ) + observed = None + with open(info_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_maf_rate_info(self): """Tests the extraction by genomic location, maf, rate and info.""" # Executing the script @@ -873,3 +1133,17 @@ def test_genomic_maf_rate_info(self): with open(template_name.format(ext="calls"), "r") as i_file: observed = i_file.read() self.assertEqual(expected, observed) + + # Checks companion files (impute2_info) + info_fn = template_name.format(ext="impute2_info") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "chr\tname\tposition\ta0\ta1\texp_freq_a1\tinfo\tcertainty\t" + "type\tinfo_type0\tconcord_type0\tr2_type0\n" + "1\trs23456\t3214569\tT\tC\t0.082\t0.362\t0.866\t0\t-1\t-1\t" + "-1\n" + ) + observed = None + with open(info_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) From c684a43802a624d11d556630d9cc1922475e672e Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Thu, 10 Sep 2015 20:23:10 -0400 Subject: [PATCH 13/19] Check the extraction of the 'map' companion file --- genipe/tests/test_impute2_extractor.py | 198 +++++++++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/genipe/tests/test_impute2_extractor.py b/genipe/tests/test_impute2_extractor.py index b23d659..087e458 100644 --- a/genipe/tests/test_impute2_extractor.py +++ b/genipe/tests/test_impute2_extractor.py @@ -194,6 +194,19 @@ def test_extract(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (map) + map_fn = template_name.format(ext="map") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "1\trs23456\t0\t3214569\n" + "1\trs23457_2\t0\t3214572\n" + "1\t1:4214570_1\t0\t4214570\n" + ) + observed = None + with open(map_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic(self): """Tests the extraction by genomic location.""" # Executing the script @@ -264,6 +277,20 @@ def test_genomic(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (map) + map_fn = template_name.format(ext="map") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "1\trs23457\t0\t3214570\n" + "1\trs23457_1\t0\t3214571\n" + "1\trs23457_2\t0\t3214572\n" + "1\t1:3214573\t0\t3214573\n" + ) + observed = None + with open(map_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_maf(self): """Tests the extraction by maf.""" # Executing the script @@ -340,6 +367,21 @@ def test_maf(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (map) + map_fn = template_name.format(ext="map") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "1\trs23456\t0\t3214569\n" + "1\trs23457\t0\t3214570\n" + "1\trs23457_1\t0\t3214571\n" + "1\trs23457_2\t0\t3214572\n" + "1\t1:3214573\t0\t3214573\n" + ) + observed = None + with open(map_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_rate(self): """Tests the extraction by completion rate.""" # Executing the script @@ -421,6 +463,22 @@ def test_rate(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (map) + map_fn = template_name.format(ext="map") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "1\trs12345\t0\t1231415\n" + "1\trs23456\t0\t3214569\n" + "1\trs23457\t0\t3214570\n" + "1\trs23457_1\t0\t3214571\n" + "1\trs23457_2\t0\t3214572\n" + "1\t1:3214573\t0\t3214573\n" + ) + observed = None + with open(map_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_info(self): """Tests the extraction by information value.""" # Executing the script @@ -498,6 +556,21 @@ def test_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (map) + map_fn = template_name.format(ext="map") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "1\trs12345\t0\t1231415\n" + "1\trs23456\t0\t3214569\n" + "1\trs23457_1\t0\t3214571\n" + "1\t1:3214573\t0\t3214573\n" + "1\t1:4214570_1\t0\t4214570\n" + ) + observed = None + with open(map_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_maf(self): """Tests the extraction by genomic location and maf.""" # Executing the script @@ -555,6 +628,17 @@ def test_genomic_maf(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (map) + map_fn = template_name.format(ext="map") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "1\trs23456\t0\t3214569\n" + ) + observed = None + with open(map_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_rate(self): """Tests the extraction by genomic location and completion rate.""" # Executing the script @@ -612,6 +696,17 @@ def test_genomic_rate(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (map) + map_fn = template_name.format(ext="map") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "1\trs12345\t0\t1231415\n" + ) + observed = None + with open(map_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_info(self): """Tests the extraction by genomic location and information value.""" # Executing the script @@ -689,6 +784,21 @@ def test_genomic_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (map) + map_fn = template_name.format(ext="map") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "1\trs12345\t0\t1231415\n" + "1\trs23456\t0\t3214569\n" + "1\trs23457\t0\t3214570\n" + "1\trs23457_1\t0\t3214571\n" + "1\t1:3214573\t0\t3214573\n" + ) + observed = None + with open(map_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_maf_rate(self): """Tests the extraction by maf and completion rate.""" # Executing the script @@ -766,6 +876,21 @@ def test_maf_rate(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (map) + map_fn = template_name.format(ext="map") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "1\trs23456\t0\t3214569\n" + "1\trs23457\t0\t3214570\n" + "1\trs23457_1\t0\t3214571\n" + "1\trs23457_2\t0\t3214572\n" + "1\t1:3214573\t0\t3214573\n" + ) + observed = None + with open(map_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_maf_info(self): """Tests the extraction by maf and information value.""" # Executing the script @@ -833,6 +958,19 @@ def test_maf_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (map) + map_fn = template_name.format(ext="map") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "1\trs23456\t0\t3214569\n" + "1\trs23457_1\t0\t3214571\n" + "1\t1:3214573\t0\t3214573\n" + ) + observed = None + with open(map_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_rate_info(self): """Tests the extraction by completion rate and information value.""" # Executing the script @@ -895,6 +1033,18 @@ def test_rate_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (map) + map_fn = template_name.format(ext="map") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "1\trs12345\t0\t1231415\n" + "1\trs23456\t0\t3214569\n" + ) + observed = None + with open(map_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_maf_rate(self): """Tests the extraction by genomic location, maf and rate.""" # Executing the script @@ -973,6 +1123,21 @@ def test_genomic_maf_rate(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (map) + map_fn = template_name.format(ext="map") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "1\trs23456\t0\t3214569\n" + "1\trs23457\t0\t3214570\n" + "1\trs23457_1\t0\t3214571\n" + "1\trs23457_2\t0\t3214572\n" + "1\t1:3214573\t0\t3214573\n" + ) + observed = None + with open(map_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_maf_info(self): """Tests the extraction by genomic location, maf and information.""" # Executing the script @@ -1031,6 +1196,17 @@ def test_genomic_maf_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (map) + map_fn = template_name.format(ext="map") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "1\trs23456\t0\t3214569\n" + ) + observed = None + with open(map_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_maf_rate_info(self): """Tests the extraction by maf, completion rate and information.""" # Executing the script @@ -1089,6 +1265,17 @@ def test_maf_rate_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion files (map) + map_fn = template_name.format(ext="map") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "1\trs12345\t0\t1231415\n" + ) + observed = None + with open(map_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_maf_rate_info(self): """Tests the extraction by genomic location, maf, rate and info.""" # Executing the script @@ -1147,3 +1334,14 @@ def test_genomic_maf_rate_info(self): with open(info_fn, "r") as i_file: observed = i_file.read() self.assertEqual(expected, observed) + + # Checks companion files (map) + map_fn = template_name.format(ext="map") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "1\trs23456\t0\t3214569\n" + ) + observed = None + with open(map_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) From 293cb3d121253f47367796bb7d61846cf368a104 Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Thu, 10 Sep 2015 20:58:02 -0400 Subject: [PATCH 14/19] Check the extraction of the 'maf' companion file --- genipe/tests/test_impute2_extractor.py | 213 +++++++++++++++++++++++++ 1 file changed, 213 insertions(+) diff --git a/genipe/tests/test_impute2_extractor.py b/genipe/tests/test_impute2_extractor.py index 087e458..ab1ffe8 100644 --- a/genipe/tests/test_impute2_extractor.py +++ b/genipe/tests/test_impute2_extractor.py @@ -207,6 +207,20 @@ def test_extract(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion file (maf) + maf_fn = template_name.format(ext="maf") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "name\tmajor\tminor\tmaf\n" + "rs23456\tT\tC\t{}\n" + "rs23457_2\tTC\tT\t{}\n" + "1:4214570_1\tT\tTC\t{}\n" + ).format(0.5, 1/4, "NA") + observed = None + with open(maf_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic(self): """Tests the extraction by genomic location.""" # Executing the script @@ -291,6 +305,21 @@ def test_genomic(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion file (map) + maf_fn = template_name.format(ext="maf") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "name\tmajor\tminor\tmaf\n" + "rs23457\tTC\tT\t{}\n" + "rs23457_1\tTC\tT\t{}\n" + "rs23457_2\tTC\tT\t{}\n" + "1:3214573\tTC\tT\t{}\n" + ).format(1/4, 1/4, 1/4, 1/4) + observed = None + with open(maf_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_maf(self): """Tests the extraction by maf.""" # Executing the script @@ -382,6 +411,22 @@ def test_maf(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion file (map) + maf_fn = template_name.format(ext="maf") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "name\tmajor\tminor\tmaf\n" + "rs23456\tT\tC\t{}\n" + "rs23457\tTC\tT\t{}\n" + "rs23457_1\tTC\tT\t{}\n" + "rs23457_2\tTC\tT\t{}\n" + "1:3214573\tTC\tT\t{}\n" + ).format(0.5, 1/4, 1/4, 1/4, 1/4) + observed = None + with open(maf_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_rate(self): """Tests the extraction by completion rate.""" # Executing the script @@ -479,6 +524,23 @@ def test_rate(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion file (map) + maf_fn = template_name.format(ext="maf") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "name\tmajor\tminor\tmaf\n" + "rs12345\tA\tG\t{}\n" + "rs23456\tT\tC\t{}\n" + "rs23457\tTC\tT\t{}\n" + "rs23457_1\tTC\tT\t{}\n" + "rs23457_2\tTC\tT\t{}\n" + "1:3214573\tTC\tT\t{}\n" + ).format(1/6, 0.5, 1/4, 1/4, 1/4, 1/4) + observed = None + with open(maf_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_info(self): """Tests the extraction by information value.""" # Executing the script @@ -571,6 +633,22 @@ def test_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion file (map) + maf_fn = template_name.format(ext="maf") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "name\tmajor\tminor\tmaf\n" + "rs12345\tA\tG\t{}\n" + "rs23456\tT\tC\t{}\n" + "rs23457_1\tTC\tT\t{}\n" + "1:3214573\tTC\tT\t{}\n" + "1:4214570_1\tT\tTC\t{}\n" + ).format(1/6, 0.5, 1/4, 1/4, "NA") + observed = None + with open(maf_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_maf(self): """Tests the extraction by genomic location and maf.""" # Executing the script @@ -639,6 +717,18 @@ def test_genomic_maf(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion file (map) + maf_fn = template_name.format(ext="maf") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "name\tmajor\tminor\tmaf\n" + "rs23456\tT\tC\t{}\n" + ).format(0.5) + observed = None + with open(maf_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_rate(self): """Tests the extraction by genomic location and completion rate.""" # Executing the script @@ -707,6 +797,18 @@ def test_genomic_rate(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion file (map) + maf_fn = template_name.format(ext="maf") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "name\tmajor\tminor\tmaf\n" + "rs12345\tA\tG\t{}\n" + ).format(1/6) + observed = None + with open(maf_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_info(self): """Tests the extraction by genomic location and information value.""" # Executing the script @@ -799,6 +901,22 @@ def test_genomic_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion file (map) + maf_fn = template_name.format(ext="maf") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "name\tmajor\tminor\tmaf\n" + "rs12345\tA\tG\t{}\n" + "rs23456\tT\tC\t{}\n" + "rs23457\tTC\tT\t{}\n" + "rs23457_1\tTC\tT\t{}\n" + "1:3214573\tTC\tT\t{}\n" + ).format(1/6, 0.5, 1/4, 1/4, 1/4) + observed = None + with open(maf_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_maf_rate(self): """Tests the extraction by maf and completion rate.""" # Executing the script @@ -891,6 +1009,22 @@ def test_maf_rate(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion file (map) + maf_fn = template_name.format(ext="maf") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "name\tmajor\tminor\tmaf\n" + "rs23456\tT\tC\t{}\n" + "rs23457\tTC\tT\t{}\n" + "rs23457_1\tTC\tT\t{}\n" + "rs23457_2\tTC\tT\t{}\n" + "1:3214573\tTC\tT\t{}\n" + ).format(0.5, 1/4, 1/4, 1/4, 1/4) + observed = None + with open(maf_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_maf_info(self): """Tests the extraction by maf and information value.""" # Executing the script @@ -971,6 +1105,20 @@ def test_maf_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion file (map) + maf_fn = template_name.format(ext="maf") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "name\tmajor\tminor\tmaf\n" + "rs23456\tT\tC\t{}\n" + "rs23457_1\tTC\tT\t{}\n" + "1:3214573\tTC\tT\t{}\n" + ).format(0.5, 1/4, 1/4) + observed = None + with open(maf_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_rate_info(self): """Tests the extraction by completion rate and information value.""" # Executing the script @@ -1045,6 +1193,19 @@ def test_rate_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion file (map) + maf_fn = template_name.format(ext="maf") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "name\tmajor\tminor\tmaf\n" + "rs12345\tA\tG\t{}\n" + "rs23456\tT\tC\t{}\n" + ).format(1/6, 0.5) + observed = None + with open(maf_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_maf_rate(self): """Tests the extraction by genomic location, maf and rate.""" # Executing the script @@ -1138,6 +1299,22 @@ def test_genomic_maf_rate(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion file (map) + maf_fn = template_name.format(ext="maf") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "name\tmajor\tminor\tmaf\n" + "rs23456\tT\tC\t{}\n" + "rs23457\tTC\tT\t{}\n" + "rs23457_1\tTC\tT\t{}\n" + "rs23457_2\tTC\tT\t{}\n" + "1:3214573\tTC\tT\t{}\n" + ).format(0.5, 1/4, 1/4, 1/4, 1/4) + observed = None + with open(maf_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_maf_info(self): """Tests the extraction by genomic location, maf and information.""" # Executing the script @@ -1207,6 +1384,18 @@ def test_genomic_maf_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion file (map) + maf_fn = template_name.format(ext="maf") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "name\tmajor\tminor\tmaf\n" + "rs23456\tT\tC\t{}\n" + ).format(0.5) + observed = None + with open(maf_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_maf_rate_info(self): """Tests the extraction by maf, completion rate and information.""" # Executing the script @@ -1276,6 +1465,18 @@ def test_maf_rate_info(self): observed = i_file.read() self.assertEqual(expected, observed) + # Checks companion file (map) + maf_fn = template_name.format(ext="maf") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "name\tmajor\tminor\tmaf\n" + "rs12345\tA\tG\t{}\n" + ).format(1/6) + observed = None + with open(maf_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) + def test_genomic_maf_rate_info(self): """Tests the extraction by genomic location, maf, rate and info.""" # Executing the script @@ -1345,3 +1546,15 @@ def test_genomic_maf_rate_info(self): with open(map_fn, "r") as i_file: observed = i_file.read() self.assertEqual(expected, observed) + + # Checks companion file (map) + maf_fn = template_name.format(ext="maf") + self.assertTrue(os.path.isfile(info_fn)) + expected = ( + "name\tmajor\tminor\tmaf\n" + "rs23456\tT\tC\t{}\n" + ).format(0.5) + observed = None + with open(maf_fn, "r") as i_file: + observed = i_file.read() + self.assertEqual(expected, observed) From f6854ff3f881eaa08e727cdfe44e480335618279 Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Thu, 10 Sep 2015 21:00:54 -0400 Subject: [PATCH 15/19] Added a note about the --index option --- docs/tutorials/tutorial_extract.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/tutorials/tutorial_extract.rst b/docs/tutorials/tutorial_extract.rst index 12ca2f7..d4f573e 100644 --- a/docs/tutorials/tutorial_extract.rst +++ b/docs/tutorials/tutorial_extract.rst @@ -277,3 +277,8 @@ analysis in the console: the specified threshold. Can be use in combination with '--maf', '--rate' and '--genomic'. +.. note:: + + When using the ``--index`` option, only the indexation (of files without an + index) will be performed. + From b3d0aefa77b4b640b13a4059dbcf9fd8683c8a5b Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Fri, 11 Sep 2015 09:50:49 -0400 Subject: [PATCH 16/19] Added information about the companion files in the documentation --- docs/tutorials/tutorial_extract.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/tutorials/tutorial_extract.rst b/docs/tutorials/tutorial_extract.rst index d4f573e..ab7cd5a 100644 --- a/docs/tutorials/tutorial_extract.rst +++ b/docs/tutorials/tutorial_extract.rst @@ -172,6 +172,12 @@ The following example shows two lines of the ``.impute2`` file. 22 rs7289830 16058758 C A 0 0 1 0 0 1 0 1 0 ... 22 rs6423472 16087621 A G 0 1 0 1 0 0 0 1 0 ... +.. note:: + + When extracting using the ``impute2`` format, all the existing companion + files (``.maf``, ``.map``, etc.) will also be extracted and included in the + same directory (using the same output prefix). + ``.dosage`` file """"""""""""""""" From ca31dd1ca2aeff35c6a483a7e9a6b514792c57f6 Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Fri, 11 Sep 2015 09:57:33 -0400 Subject: [PATCH 17/19] Forgot to copy the sample file --- genipe/tools/impute2_extractor.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/genipe/tools/impute2_extractor.py b/genipe/tools/impute2_extractor.py index 8bab379..a1b81c9 100644 --- a/genipe/tools/impute2_extractor.py +++ b/genipe/tools/impute2_extractor.py @@ -10,6 +10,7 @@ import os import re import sys +import shutil import logging import argparse from collections import namedtuple @@ -269,6 +270,12 @@ def extract_companion_files(i_prefix, o_prefix, to_extract): if row[info["index"]] in to_extract: o_file.write(line) + # We need to copy the sample file + sample_fn = i_prefix + ".sample" + if os.path.isfile(sample_fn): + o_fn = o_prefix + ".sample" + shutil.copyfile(sample_fn, o_fn) + def print_data(o_files, prob_t, *, line=None, row=None): """Prints an impute2 line. From 1d3221f5001e08da0eb80449d971d761536e51cc Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Fri, 11 Sep 2015 10:07:11 -0400 Subject: [PATCH 18/19] Check python version prior to installation --- setup.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 65e3a1d..44eba76 100644 --- a/setup.py +++ b/setup.py @@ -13,13 +13,23 @@ import os +import sys from setuptools import setup MAJOR = 1 MINOR = 2 MICRO = 1 -VERSION = "{}.{}.{}".format(MAJOR, MINOR, MICRO) +VERSION = "{0}.{1}.{2}".format(MAJOR, MINOR, MICRO) + + +def check_python_version(): + """Checks the python version, exits if < 3.3.""" + python_major, python_minor = sys.version_info[:2] + + if python_major != 3 or python_minor < 3: + sys.stderr.write("genipe requires python 3 (version 3.3 or higher)\n") + sys.exit(1) def write_version_file(fn=None): @@ -40,6 +50,9 @@ def write_version_file(fn=None): def setup_package(): + # Checking the python version prior to installation + check_python_version() + # Saving the version into a file write_version_file() @@ -66,7 +79,7 @@ def setup_package(): "imputed-stats=genipe.tools.imputed_stats:main", ], }, - install_requires=["numpy >= 1.9.2", "jinja2 >= 2.7.3", + install_requires=["numpy >= 1.9.2", "Jinja2 >= 2.7.3", "pandas >= 0.15.2", "setuptools >= 12.0.5"], packages=["genipe", "genipe.task", "genipe.db", "genipe.tools", "genipe.formats", "genipe.reporting", "genipe.config", From 08cb92778c8e6d6e0fefa69f9c624492aeffa049 Mon Sep 17 00:00:00 2001 From: Louis-Philippe Lemieux Perreault Date: Fri, 11 Sep 2015 10:07:56 -0400 Subject: [PATCH 19/19] Preparation for next release --- README.mkd | 13 +++++++++---- setup.py | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.mkd b/README.mkd index 95cd909..941b7ed 100644 --- a/README.mkd +++ b/README.mkd @@ -4,7 +4,7 @@ # genipe - A Python module to perform genome-wide imputation analysis -*Version 1.2.1* +*Version 1.2.2* The `genipe` module (standing for **GEN**ome-wide **I**mputation **P**ipelin**E**) includes a script (named `genipe-launcher`) that @@ -40,7 +40,7 @@ The tool requires a standard [Python](http://python.org/) 3 installation with the following modules: * `numpy` version 1.8.2 and latest -* `jinja2` version 2.7.3 and latest +* `Jinja2` version 2.7.3 and latest * `pandas` version 0.15.2 and latest * `setuptools` version 12.0.5 and latest @@ -103,9 +103,10 @@ usage: genipe-launcher [-h] [-v] [--debug] [--thread THREAD] --bfile PREFIX [--probability FLOAT] [--completion FLOAT] [--info FLOAT] [--report-number NB] [--report-title TITLE] [--report-author AUTHOR] + [--report-background BACKGROUND] Execute the genome-wide imputation pipeline. This script is part of the -'genipe' package, version 1.2.1. +'genipe' package, version 1.2.2. optional arguments: -h, --help show this help message and exit @@ -174,6 +175,10 @@ Automatic Report Options: imputation] --report-author AUTHOR The report author. [Automatically generated by genipe] + --report-background BACKGROUND + The report background section (can either be a string + or a file containing the background. [General + background] ``` @@ -212,7 +217,7 @@ usage: imputed-stats [-h] [-v] {cox,linear,logistic,mixedlm,skat} ... Performs statistical analysis on imputed data (either SKAT analysis, or linear, logistic or survival regression). This script is part of the 'genipe' -package, version 1.2.1). +package, version 1.2.2). optional arguments: -h, --help show this help message and exit diff --git a/setup.py b/setup.py index 44eba76..50ade53 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ MAJOR = 1 MINOR = 2 -MICRO = 1 +MICRO = 2 VERSION = "{0}.{1}.{2}".format(MAJOR, MINOR, MICRO)