From 3689e1c994a3c421cc45b402d18f677e8afd64e1 Mon Sep 17 00:00:00 2001 From: Haibao Tang Date: Fri, 9 Aug 2024 12:00:42 -0700 Subject: [PATCH 01/13] Add --chrprefix --- jcvi/formats/bed.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jcvi/formats/bed.py b/jcvi/formats/bed.py index eb6886f5..f949db02 100755 --- a/jcvi/formats/bed.py +++ b/jcvi/formats/bed.py @@ -593,6 +593,7 @@ def format(args): Re-format BED file, e.g. switch sequence ids. """ p = OptionParser(format.__doc__) + p.add_argument("--chrprefix", help="Add prefix to seqid") p.add_argument("--prefix", help="Add prefix to name column (4th)") p.add_argument("--switch", help="Switch seqids based on two-column file") p.set_outfile() @@ -604,11 +605,14 @@ def format(args): (bedfile,) = args switch = DictFile(opts.switch, delimiter="\t") if opts.switch else None prefix = opts.prefix + chrprefix = opts.chrprefix bed = Bed(bedfile) with must_open(opts.outfile, "w") as fw: for b in bed: if prefix: b.accn = prefix + b.accn + if chrprefix: + b.seqid = chrprefix + b.seqid if switch and b.seqid in switch: b.seqid = switch[b.seqid] print(b, file=fw) From 8cd8f9f29a632994b8205e8496d02ad5c26af6aa Mon Sep 17 00:00:00 2001 From: Haibao Tang Date: Fri, 9 Aug 2024 12:57:36 -0700 Subject: [PATCH 02/13] update landscape --- jcvi/graphics/landscape.py | 48 +++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/jcvi/graphics/landscape.py b/jcvi/graphics/landscape.py index 0b9f34cf..3f1b7ec1 100644 --- a/jcvi/graphics/landscape.py +++ b/jcvi/graphics/landscape.py @@ -280,6 +280,7 @@ def draw_depth( logscale: bool = False, title: Optional[str] = None, subtitle: Optional[str] = None, + median_line: bool = True, ): """Draw depth plot on the given axes, using data from bed @@ -302,6 +303,7 @@ def draw_depth( ends = {} label_positions = [] start = 0 + end = 0 for seqid in seqids: if seqid not in sizes: continue @@ -345,14 +347,15 @@ def draw_depth( seqid_end = ends[seqid] seqid_median = np.median(values) medians[seqid] = seqid_median - ax.plot( - (seqid_start, seqid_end), - (seqid_median, seqid_median), - "-", - lw=4, - color=c, - alpha=0.5, - ) + if median_line: + ax.plot( + (seqid_start, seqid_end), + (seqid_median, seqid_median), + "-", + lw=4, + color=c, + alpha=0.5, + ) # Vertical lines for all the breaks for pos in starts.values(): @@ -364,22 +367,30 @@ def draw_depth( median_depth_y = 0.88 chr_label_y = 0.08 + rotation = 20 if len(label_positions) > 10 else 0 for seqid, position in label_positions: xpos = 0.1 + position * 0.8 / xsize c = chrinfo[seqid].color if seqid in chrinfo else defaultcolor newseqid = chrinfo[seqid].new_name if seqid in chrinfo else seqid - root.text( - xpos, chr_label_y, newseqid, color=c, ha="center", va="center", rotation=20 - ) - seqid_median = medians[seqid] root.text( xpos, - median_depth_y, - str(int(seqid_median)), + chr_label_y, + newseqid, color=c, ha="center", va="center", + rotation=rotation, ) + seqid_median = medians[seqid] + if median_line: + root.text( + xpos, + median_depth_y, + str(int(seqid_median)), + color=c, + ha="center", + va="center", + ) # Add an arrow to the right of the plot, indicating these are median depths root.text( @@ -432,6 +443,7 @@ def draw_multi_depth( titleinfo_file: str, maxdepth: int, logscale: bool, + median_line: bool = True, ): """ Draw multiple depth plots on the same canvas. @@ -463,6 +475,7 @@ def draw_multi_depth( logscale=logscale, title=title, subtitle=subtitle, + median_line=median_line, ) ypos -= yinterval @@ -505,6 +518,12 @@ def depth(args): p.add_argument( "--logscale", default=False, action="store_true", help="Use log-scale on depth" ) + p.add_argument( + "--no-median-line", + default=False, + action="store_true", + help="Do not plot median depth line", + ) opts, args, iopts = p.set_image_options(args, style="dark", figsize="14x4") if len(args) < 1: @@ -535,6 +554,7 @@ def depth(args): opts.titleinfo, opts.maxdepth, opts.logscale, + median_line=not opts.no_median_line, ) if npanels > 1: From 212eb41a63e0348c4c35e9db33ebc0da855d5976 Mon Sep 17 00:00:00 2001 From: Haibao Tang Date: Fri, 9 Aug 2024 13:02:31 -0700 Subject: [PATCH 03/13] update landscape --- jcvi/graphics/landscape.py | 41 ++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/jcvi/graphics/landscape.py b/jcvi/graphics/landscape.py index 3f1b7ec1..e3000e38 100644 --- a/jcvi/graphics/landscape.py +++ b/jcvi/graphics/landscape.py @@ -281,6 +281,7 @@ def draw_depth( title: Optional[str] = None, subtitle: Optional[str] = None, median_line: bool = True, + draw_seqids: bool = True, ): """Draw depth plot on the given axes, using data from bed @@ -372,15 +373,16 @@ def draw_depth( xpos = 0.1 + position * 0.8 / xsize c = chrinfo[seqid].color if seqid in chrinfo else defaultcolor newseqid = chrinfo[seqid].new_name if seqid in chrinfo else seqid - root.text( - xpos, - chr_label_y, - newseqid, - color=c, - ha="center", - va="center", - rotation=rotation, - ) + if draw_seqids: + root.text( + xpos, + chr_label_y, + newseqid, + color=c, + ha="center", + va="center", + rotation=rotation, + ) seqid_median = medians[seqid] if median_line: root.text( @@ -393,13 +395,14 @@ def draw_depth( ) # Add an arrow to the right of the plot, indicating these are median depths - root.text( - 0.91, - 0.88, - r"$\leftarrow$median", - color="lightslategray", - va="center", - ) + if median_line: + root.text( + 0.91, + 0.88, + r"$\leftarrow$median", + color="lightslategray", + va="center", + ) if title: root.text( @@ -453,7 +456,9 @@ def draw_multi_depth( npanels = len(bedfiles) yinterval = 1.0 / npanels ypos = 1 - yinterval - for bedfile, panel_root, panel_ax in zip(bedfiles, panel_roots, panel_axes): + for i, (bedfile, panel_root, panel_ax) in enumerate( + zip(bedfiles, panel_roots, panel_axes) + ): pf = op.basename(bedfile).split(".", 1)[0] bed = Bed(bedfile) @@ -466,6 +471,7 @@ def draw_multi_depth( subtitle = title.subtitle title = title.title + draw_seqids = i in (0, npanels - 1) draw_depth( panel_root, panel_ax, @@ -476,6 +482,7 @@ def draw_multi_depth( title=title, subtitle=subtitle, median_line=median_line, + draw_seqids=draw_seqids, ) ypos -= yinterval From abb59d1c8b7be76e4f0b2da18b21f9bde7624b29 Mon Sep 17 00:00:00 2001 From: Haibao Tang Date: Fri, 9 Aug 2024 13:06:16 -0700 Subject: [PATCH 04/13] use lightgray instead --- jcvi/graphics/landscape.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcvi/graphics/landscape.py b/jcvi/graphics/landscape.py index e3000e38..688d06e6 100644 --- a/jcvi/graphics/landscape.py +++ b/jcvi/graphics/landscape.py @@ -463,7 +463,7 @@ def draw_multi_depth( bed = Bed(bedfile) if ypos > 0.001: - root.plot((0.02, 0.98), (ypos, ypos), "-", lw=2, color="lightslategray") + root.plot((0.02, 0.98), (ypos, ypos), "-", lw=2, color="lightgray") title = titleinfo.get(bedfile, pf.split("_", 1)[0]) subtitle = None From f4f372df3538b350077fe2e14c85bcb53d4ba7e8 Mon Sep 17 00:00:00 2001 From: Haibao Tang Date: Fri, 9 Aug 2024 13:41:33 -0700 Subject: [PATCH 05/13] add calculate-coverage --- jcvi/graphics/landscape.py | 32 +++++++++++++++++++++++++++++++- jcvi/utils/cbook.py | 3 ++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/jcvi/graphics/landscape.py b/jcvi/graphics/landscape.py index 688d06e6..005dc6b0 100644 --- a/jcvi/graphics/landscape.py +++ b/jcvi/graphics/landscape.py @@ -20,7 +20,7 @@ from ..formats.base import BaseFile, DictFile, LineFile, must_open from ..formats.bed import Bed, bins, get_nbins from ..formats.sizes import Sizes -from ..utils.cbook import autoscale, human_size +from ..utils.cbook import autoscale, human_size, percentage from .base import ( CirclePolygon, @@ -53,6 +53,9 @@ "Exons": "Genes (exons)", } +# Consider a depth of 5 as minimum covered depth +MIN_COVERED_DEPTH = 5 + class BinLine: def __init__(self, row): @@ -282,6 +285,7 @@ def draw_depth( subtitle: Optional[str] = None, median_line: bool = True, draw_seqids: bool = True, + calculate_coverage: bool = False, ): """Draw depth plot on the given axes, using data from bed @@ -318,6 +322,8 @@ def draw_depth( # Extract plotting data data = [] data_by_seqid = defaultdict(list) + total_bp = 0 + covered_bp = 0 for b in bed: seqid = b.seqid if seqid not in starts: @@ -328,6 +334,10 @@ def draw_depth( c = chrinfo[seqid].color if seqid in chrinfo else "k" data.append((x, y, c)) data_by_seqid[seqid].append(y) + if y >= MIN_COVERED_DEPTH: + covered_bp += b.end - b.start + total_bp += b.end - b.start + logger.debug("Coverage: %s", percentage(covered_bp, total_bp)) x, y, c = zip(*data) ax.scatter( @@ -424,6 +434,17 @@ def draw_depth( va="center", size=15, ) + if calculate_coverage: + cov_pct = percentage(covered_bp, total_bp, mode=None) + root.text( + 0.95, + 0.25, + f"Coverage: {cov_pct}", + color="darkslategray", + ha="center", + va="center", + size=15, + ) ax.set_xticks([]) ax.set_xlim(0, xsize) @@ -447,6 +468,7 @@ def draw_multi_depth( maxdepth: int, logscale: bool, median_line: bool = True, + calculate_coverage: bool = False, ): """ Draw multiple depth plots on the same canvas. @@ -483,6 +505,7 @@ def draw_multi_depth( subtitle=subtitle, median_line=median_line, draw_seqids=draw_seqids, + calculate_coverage=calculate_coverage, ) ypos -= yinterval @@ -531,6 +554,12 @@ def depth(args): action="store_true", help="Do not plot median depth line", ) + p.add_argument( + "--calculate-coverage", + default=False, + action="store_true", + help="Calculate genome coverage", + ) opts, args, iopts = p.set_image_options(args, style="dark", figsize="14x4") if len(args) < 1: @@ -562,6 +591,7 @@ def depth(args): opts.maxdepth, opts.logscale, median_line=not opts.no_median_line, + calculate_coverage=opts.calculate_coverage, ) if npanels > 1: diff --git a/jcvi/utils/cbook.py b/jcvi/utils/cbook.py index e11b1591..20c7c2c5 100644 --- a/jcvi/utils/cbook.py +++ b/jcvi/utils/cbook.py @@ -8,6 +8,7 @@ import sys from collections import defaultdict +from typing import Optional from ..apps.base import logger @@ -181,7 +182,7 @@ def enumerate_reversed(sequence): yield index, sequence[index] -def percentage(a, b, precision=1, mode=0): +def percentage(a, b, precision=1, mode: Optional[int] = 0): """ >>> percentage(100, 200) '100 of 200 (50.0%)' From aae4ac01027bd4d46fc1ff614cfb8720376c4051 Mon Sep 17 00:00:00 2001 From: Haibao Tang Date: Fri, 9 Aug 2024 13:43:21 -0700 Subject: [PATCH 06/13] simplify --- jcvi/graphics/landscape.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcvi/graphics/landscape.py b/jcvi/graphics/landscape.py index 005dc6b0..d61c73ef 100644 --- a/jcvi/graphics/landscape.py +++ b/jcvi/graphics/landscape.py @@ -337,7 +337,7 @@ def draw_depth( if y >= MIN_COVERED_DEPTH: covered_bp += b.end - b.start total_bp += b.end - b.start - logger.debug("Coverage: %s", percentage(covered_bp, total_bp)) + logger.debug("cov: %s", percentage(covered_bp, total_bp)) x, y, c = zip(*data) ax.scatter( From ea3ed8578e5249b5dfd1a055c7ab737c25facd93 Mon Sep 17 00:00:00 2001 From: Haibao Tang Date: Fri, 9 Aug 2024 13:47:11 -0700 Subject: [PATCH 07/13] make sure we print % --- jcvi/graphics/landscape.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jcvi/graphics/landscape.py b/jcvi/graphics/landscape.py index d61c73ef..9d49a50e 100644 --- a/jcvi/graphics/landscape.py +++ b/jcvi/graphics/landscape.py @@ -337,7 +337,7 @@ def draw_depth( if y >= MIN_COVERED_DEPTH: covered_bp += b.end - b.start total_bp += b.end - b.start - logger.debug("cov: %s", percentage(covered_bp, total_bp)) + logger.debug("cov: %s", percentage(covered_bp, total_bp, precision=0)) x, y, c = zip(*data) ax.scatter( @@ -435,11 +435,11 @@ def draw_depth( size=15, ) if calculate_coverage: - cov_pct = percentage(covered_bp, total_bp, mode=None) + cov_pct = percentage(covered_bp, total_bp, precision=0, mode=None) root.text( 0.95, 0.25, - f"Coverage: {cov_pct}", + latex(f"cov: {cov_pct}"), color="darkslategray", ha="center", va="center", From fc2f2a6978c187addde4a2a7389d194c71b2cd62 Mon Sep 17 00:00:00 2001 From: Haibao Tang Date: Fri, 9 Aug 2024 16:37:16 -0700 Subject: [PATCH 08/13] allow output file name to be specified --- jcvi/graphics/landscape.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/jcvi/graphics/landscape.py b/jcvi/graphics/landscape.py index 9d49a50e..8b39063f 100644 --- a/jcvi/graphics/landscape.py +++ b/jcvi/graphics/landscape.py @@ -560,6 +560,7 @@ def depth(args): action="store_true", help="Calculate genome coverage", ) + p.set_outfile("depth.pdf") opts, args, iopts = p.set_image_options(args, style="dark", figsize="14x4") if len(args) < 1: @@ -594,10 +595,7 @@ def depth(args): calculate_coverage=opts.calculate_coverage, ) - if npanels > 1: - pf = op.commonprefix(bedfiles) - pf = pf or "depth" - image_name = pf + "." + iopts.format + image_name = opts.outfile savefig(image_name, dpi=iopts.dpi, iopts=iopts) From 3c85f7e97c2692bf066d99d2c07f3db2b104dc0c Mon Sep 17 00:00:00 2001 From: Haibao Tang Date: Fri, 9 Aug 2024 20:41:18 -0700 Subject: [PATCH 09/13] Add tests for depth() --- jcvi/graphics/landscape.py | 1 + .../data/VAR0_srtd.wgs.regions.bed.gz | Bin 0 -> 19654 bytes .../data/VAR1_srtd.wgs.regions.bed.gz | Bin 0 -> 19738 bytes .../data/VAR2_srtd.wgs.regions.bed.gz | Bin 0 -> 19773 bytes tests/graphics/data/chrinfo.txt | 12 +++++++++++ tests/graphics/data/titleinfo.txt | 3 +++ tests/graphics/test_landscape.py | 20 +++++++++++++++++- 7 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 tests/graphics/data/VAR0_srtd.wgs.regions.bed.gz create mode 100644 tests/graphics/data/VAR1_srtd.wgs.regions.bed.gz create mode 100644 tests/graphics/data/VAR2_srtd.wgs.regions.bed.gz create mode 100644 tests/graphics/data/chrinfo.txt create mode 100644 tests/graphics/data/titleinfo.txt diff --git a/jcvi/graphics/landscape.py b/jcvi/graphics/landscape.py index 8b39063f..9da2b496 100644 --- a/jcvi/graphics/landscape.py +++ b/jcvi/graphics/landscape.py @@ -597,6 +597,7 @@ def depth(args): image_name = opts.outfile savefig(image_name, dpi=iopts.dpi, iopts=iopts) + return image_name def add_window_options(p): diff --git a/tests/graphics/data/VAR0_srtd.wgs.regions.bed.gz b/tests/graphics/data/VAR0_srtd.wgs.regions.bed.gz new file mode 100644 index 0000000000000000000000000000000000000000..4a65373b5baec97f8e484ff95ce48d3c03116866 GIT binary patch literal 19654 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 literal 0 HcmV?d00001 diff --git a/tests/graphics/data/VAR1_srtd.wgs.regions.bed.gz b/tests/graphics/data/VAR1_srtd.wgs.regions.bed.gz new file mode 100644 index 0000000000000000000000000000000000000000..db5f09afb7dd330da4751d49408d5f7c3a9ba800 GIT binary patch literal 19738 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 jGO0~o9FkVv&z=LotR>_fs@wo= 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 Date: Sat, 10 Aug 2024 10:14:35 -0700 Subject: [PATCH 10/13] Add roi --- jcvi/graphics/landscape.py | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/jcvi/graphics/landscape.py b/jcvi/graphics/landscape.py index 9da2b496..7c25aaeb 100644 --- a/jcvi/graphics/landscape.py +++ b/jcvi/graphics/landscape.py @@ -11,9 +11,10 @@ import sys from collections import Counter, OrderedDict, defaultdict -from typing import List, Optional +from typing import Dict, List, Tuple, Optional import numpy as np +import seaborn as sns from ..algorithms.matrix import moving_sum from ..apps.base import ActionDispatcher, OptionParser, logger @@ -203,8 +204,6 @@ def mosdepth(args): Plot depth vs. coverage per chromosome. Inspired by mosdepth plot. See also: https://github.com/brentp/mosdepth """ - import seaborn as sns - sns.set_style("darkgrid") p = OptionParser(mosdepth.__doc__) @@ -286,6 +285,7 @@ def draw_depth( median_line: bool = True, draw_seqids: bool = True, calculate_coverage: bool = False, + roi: Optional[List[Tuple[str, int]]] = None, ): """Draw depth plot on the given axes, using data from bed @@ -404,6 +404,14 @@ def draw_depth( va="center", ) + # Plot the regions of interest + if roi: + for chrom, pos in roi: + if chrom not in starts: + continue + x = starts[chrom] + pos + ax.plot((x, x), (0, maxdepth), "-", lw=2, color="gray") + # Add an arrow to the right of the plot, indicating these are median depths if median_line: root.text( @@ -458,6 +466,21 @@ def draw_depth( normalize_axes(root) +def read_roi(roi_file: str) -> Dict[str, List[str]]: + """ + Read the regions of interest file, and return a dict of filename => regions. + """ + roi = defaultdict(list) + with open(roi_file, encoding="utf-8") as fp: + for row in fp: + filename, region = row.strip().split(",") + chrom, start_end = region.split(":", 1) + start, end = start_end.split("-") + region = (chrom, (int(start) + int(end)) // 2) + roi[filename].append(region) + return roi + + def draw_multi_depth( root, panel_roots, @@ -469,6 +492,7 @@ def draw_multi_depth( logscale: bool, median_line: bool = True, calculate_coverage: bool = False, + roi: Optional[str] = None, ): """ Draw multiple depth plots on the same canvas. @@ -478,6 +502,7 @@ def draw_multi_depth( npanels = len(bedfiles) yinterval = 1.0 / npanels ypos = 1 - yinterval + roi = read_roi(roi) if roi else {} for i, (bedfile, panel_root, panel_ax) in enumerate( zip(bedfiles, panel_roots, panel_axes) ): @@ -506,6 +531,7 @@ def draw_multi_depth( median_line=median_line, draw_seqids=draw_seqids, calculate_coverage=calculate_coverage, + roi=roi.get(bedfile), ) ypos -= yinterval @@ -560,6 +586,10 @@ def depth(args): action="store_true", help="Calculate genome coverage", ) + p.add_argument( + "--roi", + help="File that contains regions of interest, format: filename, chr:start-end", + ) p.set_outfile("depth.pdf") opts, args, iopts = p.set_image_options(args, style="dark", figsize="14x4") @@ -593,6 +623,7 @@ def depth(args): opts.logscale, median_line=not opts.no_median_line, calculate_coverage=opts.calculate_coverage, + roi=opts.roi, ) image_name = opts.outfile From 4f3387674bff7dc45c3bc18d8f092c4f5ff12944 Mon Sep 17 00:00:00 2001 From: Haibao Tang Date: Sat, 10 Aug 2024 10:24:49 -0700 Subject: [PATCH 11/13] update --- jcvi/graphics/landscape.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jcvi/graphics/landscape.py b/jcvi/graphics/landscape.py index 7c25aaeb..62cef0bd 100644 --- a/jcvi/graphics/landscape.py +++ b/jcvi/graphics/landscape.py @@ -473,11 +473,12 @@ def read_roi(roi_file: str) -> Dict[str, List[str]]: roi = defaultdict(list) with open(roi_file, encoding="utf-8") as fp: for row in fp: - filename, region = row.strip().split(",") + filename, region = row.strip().split(",")[:2] chrom, start_end = region.split(":", 1) start, end = start_end.split("-") region = (chrom, (int(start) + int(end)) // 2) roi[filename].append(region) + logger.info("Read %d regions of interest", len(roi)) return roi From 56e205cd5bb77c2e058dbe1609df140ee1410bc5 Mon Sep 17 00:00:00 2001 From: Haibao Tang Date: Sat, 10 Aug 2024 10:37:24 -0700 Subject: [PATCH 12/13] special case --- jcvi/graphics/landscape.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/jcvi/graphics/landscape.py b/jcvi/graphics/landscape.py index 62cef0bd..12287ec8 100644 --- a/jcvi/graphics/landscape.py +++ b/jcvi/graphics/landscape.py @@ -406,11 +406,13 @@ def draw_depth( # Plot the regions of interest if roi: - for chrom, pos in roi: + for chrom, pos, name in roi: if chrom not in starts: continue x = starts[chrom] + pos - ax.plot((x, x), (0, maxdepth), "-", lw=2, color="gray") + # TODO: Remove this special case + color = "tomato" if name == "II" else "gray" + ax.plot((x, x), (0, maxdepth), "-", lw=2, color=color) # Add an arrow to the right of the plot, indicating these are median depths if median_line: @@ -473,10 +475,10 @@ def read_roi(roi_file: str) -> Dict[str, List[str]]: roi = defaultdict(list) with open(roi_file, encoding="utf-8") as fp: for row in fp: - filename, region = row.strip().split(",")[:2] + filename, region, name = row.strip().split(",")[:3] chrom, start_end = region.split(":", 1) start, end = start_end.split("-") - region = (chrom, (int(start) + int(end)) // 2) + region = (chrom, (int(start) + int(end)) // 2, name) roi[filename].append(region) logger.info("Read %d regions of interest", len(roi)) return roi From c5ba92b63969f3d05fd49b8b5c750634fe5e9a89 Mon Sep 17 00:00:00 2001 From: Haibao Tang Date: Sat, 10 Aug 2024 13:52:06 -0700 Subject: [PATCH 13/13] minor coloring --- jcvi/graphics/landscape.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcvi/graphics/landscape.py b/jcvi/graphics/landscape.py index 12287ec8..ad09c86d 100644 --- a/jcvi/graphics/landscape.py +++ b/jcvi/graphics/landscape.py @@ -411,7 +411,7 @@ def draw_depth( continue x = starts[chrom] + pos # TODO: Remove this special case - color = "tomato" if name == "II" else "gray" + color = {"II": "tomato", "low qual": "g"}.get(name, "gray") ax.plot((x, x), (0, maxdepth), "-", lw=2, color=color) # Add an arrow to the right of the plot, indicating these are median depths