Skip to content

Commit

Permalink
chore: switch to click for argument parsing (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
holtgrewe authored Jan 2, 2023
1 parent 3550dfb commit 1348b84
Show file tree
Hide file tree
Showing 14 changed files with 292 additions and 284 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ mypy:
.PHONY: lint
lint: flake8 isort-check black-check mypy

.PHONY: pytest
pytest:
.PHONY: test
test:
pytest .
104 changes: 0 additions & 104 deletions chew/__main__.py

This file was deleted.

144 changes: 144 additions & 0 deletions chew/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import typing

import click

from chew import __version__, compare, fingerprint, plot_compare, plot_var_het, stats


@click.group()
@click.version_option(__version__)
@click.option("--verbose/--no-verbose", help="Enable verbose mode.", default=False)
@click.pass_context
def cli(ctx: click.Context, verbose: bool):
"""Main entry point for CLI via click."""
ctx.ensure_object(dict)
ctx.obj["verbose"] = verbose


@cli.command("fingerprint", help="Compute fingerprint to numpy .npz files.")
@click.option("--min-coverage", type=int, default=5, help="Minimal required coverage.")
@click.option("--reference", required=True, help="Path to reference FASTA file.")
@click.option(
"--output-fingerprint",
required=True,
help="Path to output .npz file (extension will be added automatically if necessary)",
)
@click.option(
"--output-aafs/--no-output-aafs",
default=True,
help="Write alternate allele fractions to .npz file.",
)
@click.option("--input-bam", required=True, help="Path to input BAM file.")
@click.option("--genome-release", required=False, default="GRCh37", help="Genome release used.")
@click.option("--max-sites", type=int, default=0, help="Maximal number of sites to consider.")
@click.option("--write-vcf/--no-write-vcf", default=False, help="Enable writing of call VCF.")
@click.pass_context
def cli_fingerprint(
ctx: click.Context,
min_coverage: int,
reference: str,
output_fingerprint: str,
output_aafs: bool,
input_bam: str,
genome_release: str,
max_sites: int,
write_vcf: bool,
):
config = fingerprint.Config(
verbosity=2 if ctx.obj["verbose"] else 1,
min_coverage=min_coverage,
reference=reference,
output_fingerprint=output_fingerprint,
output_aafs=output_aafs,
input_bam=input_bam,
genome_release=genome_release,
max_sites=max_sites,
write_vcf=write_vcf,
)
fingerprint.run(config)


@cli.command("compare", help="Perform fingeprint comparison.")
@click.option(
"--output-prefix", type=str, default="chew-comparison", help="Path to comparison file."
)
@click.option("--min-mask-ones", type=int, help="Minimal number of ones in mask.")
@click.option("--max-mask-ones", type=int, help="Maximal number of ones in mask.")
@click.argument("fingerprints", nargs=-1)
@click.pass_context
def cli_compare(
ctx: click.Context,
output_prefix: str,
min_mask_ones: typing.Optional[int],
max_mask_ones: typing.Optional[int],
fingerprints: typing.List[str],
):
config = compare.Config(
verbosity=2 if ctx.obj["verbose"] else 1,
output_prefix=output_prefix,
min_mask_ones=min_mask_ones,
max_mask_ones=max_mask_ones,
fingerprints=fingerprints,
)
compare.run(config)


@cli.command("stats", help="Compute statistics from fingerprint .npz files.")
@click.option("--output", default="chew-stats.txt", help="Path to output file.")
@click.argument("fingerprints", nargs=-1)
@click.pass_context
def cli_stats(
ctx: click.Context,
output: str,
fingerprints: typing.List[str],
):
config = stats.Config(
verbosity=2 if ctx.obj["verbose"] else 1,
output=output,
fingerprints=fingerprints,
)
stats.run(config)


@cli.command("plot-compare", help="Plot result of 'ngs-chew compare'.")
@click.option(
"--title", default="NGS Chew Comparison Plot", help="title to use for the output HTML file."
)
@click.argument("compare-out")
@click.argument("out_html")
@click.pass_context
def cli_plot_compare(
ctx: click.Context,
title: str,
compare_out: str,
out_html: str,
):
config = plot_compare.Config(
verbosity=2 if ctx.obj["verbose"] else 1,
compare_out=compare_out,
out_html=out_html,
title=title,
)
plot_compare.run(config)


@cli.command("plot-var-het", help="Plot var(het) metric from .npz files.")
@click.option(
"--title", default="NGS Chew var(het) Plot", help="title to use for the output HTML file."
)
@click.argument("stats_out")
@click.argument("out_html")
@click.pass_context
def cli_plot_var_het(
ctx: click.Context,
title: str,
stats_out: str,
out_html: str,
):
config = plot_var_het.Config(
verbosity=2 if ctx.obj["verbose"] else 1,
title=title,
out_html=out_html,
stats_out=stats_out,
)
plot_var_het.run(config)
38 changes: 28 additions & 10 deletions chew/compare.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
import math
import typing

import attrs
from logzero import logger
import numpy as np
from tqdm import tqdm


def load_fingerprint(path):
@attrs.frozen
class Config:
verbosity: int
output_prefix: str
min_mask_ones: typing.Optional[int]
max_mask_ones: typing.Optional[int]
fingerprints: typing.List[str]


def load_fingerprint(path, *, load_aafs: bool = False):
nparr = np.load(path)
return nparr["header"][3], nparr["fingerprint"]
if load_aafs:
return nparr["header"][3], nparr["fingerprint"], nparr.get("aafs")
else:
return nparr["header"][3], nparr["fingerprint"]


def load_fingerprint_with_aafs(path):
return load_fingerprint(path, load_aafs=True)


def relatedness(lhs, rhs):
Expand Down Expand Up @@ -35,18 +53,18 @@ def relatedness(lhs, rhs):
return n_ibs0, rel, np.count_nonzero(mask)


def run(args):
def run(config: Config):
logger.info("Loading fingerprints...")
fps = {
name: fingerprint for name, fingerprint in map(load_fingerprint, tqdm(args.fingerprints))
name: fingerprint for name, fingerprint in map(load_fingerprint, tqdm(config.fingerprints))
}
logger.info("Loaded %d fingerprints", len(fps))
if args.min_mask_ones or args.max_mask_ones:
if config.min_mask_ones or config.max_mask_ones:
fps = {
name: fp
for name, fp in fps.items()
if (not args.min_mask_ones or np.count_nonzero(fp[0]) >= args.min_mask_ones)
and (not args.max_mask_ones or np.count_nonzero(fp[0]) <= args.max_mask_ones)
if (not config.min_mask_ones or np.count_nonzero(fp[0]) >= config.min_mask_ones)
and (not config.max_mask_ones or np.count_nonzero(fp[0]) <= config.max_mask_ones)
}
logger.info("Filtered to %d fingerprints", len(fps))
header = ["name"] + list(fps)
Expand All @@ -66,12 +84,12 @@ def run(args):
rows_rel.append(row_rel)
rows_mask.append(row_mask)

with open("%s.relatedness.txt" % args.output_prefix, "wt") as outputf:
with open("%s.relatedness.txt" % config.output_prefix, "wt") as outputf:
for row in [header] + rows_rel:
print("\t".join(map(str, row)), file=outputf)
with open("%s.ibs0.txt" % args.output_prefix, "wt") as outputf:
with open("%s.ibs0.txt" % config.output_prefix, "wt") as outputf:
for row in [header] + rows_n_ibs0:
print("\t".join(map(str, row)), file=outputf)
with open("%s.mask.txt" % args.output_prefix, "wt") as outputf:
with open("%s.mask.txt" % config.output_prefix, "wt") as outputf:
for row in [header] + rows_mask:
print("\t".join(map(str, row)), file=outputf)
Loading

0 comments on commit 1348b84

Please sign in to comment.