Skip to content

Commit 82e0a7c

Browse files
committed
using patches to speedup rendering
1 parent 0ef100b commit 82e0a7c

1 file changed

Lines changed: 68 additions & 74 deletions

File tree

trackplot/plot_func.py

Lines changed: 68 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
from adjustText import adjust_text
1717
from loguru import logger
1818
from matplotlib import pylab
19+
from matplotlib import pyplot as plt
1920
from matplotlib.font_manager import FontProperties
2021
from matplotlib.patches import PathPatch, Polygon
22+
from matplotlib.collections import PatchCollection
2123
from matplotlib.path import Path
2224
from matplotlib.textpath import TextPath
2325
from matplotlib.transforms import Affine2D
24-
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
2526
from scipy.cluster.hierarchy import dendrogram, linkage
2627
from scipy.stats import gaussian_kde, zscore
2728

@@ -427,6 +428,7 @@ def plot_annotation(
427428
@2018.12.26
428429
Maybe I'm too stupid for this, using 30% of total length of x axis as the gap between text with axis
429430
"""
431+
patches, arrows = [], {}
430432
for transcript in data:
431433
strand = transcript.strand
432434
# @2018.12.20 add transcript id, based on fixed coordinates
@@ -450,15 +452,12 @@ def plot_annotation(
450452
for ind, exon in enumerate(transcript.exons):
451453
s, e, strand = region.relative(
452454
exon.start), region.relative(exon.end), exon.strand
453-
x = [
454-
graph_coords[s], graph_coords[e],
455-
graph_coords[e], graph_coords[s]
456-
]
457-
y = [
458-
y_loc - exon_width / 2, y_loc - exon_width / 2,
459-
y_loc + exon_width / 2, y_loc + exon_width / 2
460-
]
461-
ax.fill(x, y, color, lw=.5, zorder=20)
455+
456+
patches.append(plt.Rectangle(
457+
(graph_coords[s], y_loc - exon_width / 2),
458+
width=graph_coords[e]-graph_coords[s], height=exon_width,
459+
lw=.5, zorder=20,rasterized=raster, color=color
460+
))
462461

463462
if show_exon_id and exon.name:
464463
y_loc_offset = 0.1 if ind % 2 == 0 else - 0.2
@@ -490,12 +489,10 @@ def plot_annotation(
490489

491490
for i in range(narrows):
492491
loc = float(i) * length / narrows + graph_coords[region.relative(transcript.start)]
493-
if strand == '+':
494-
x = [loc - spread, loc, loc - spread]
495-
else:
496-
x = [loc + spread, loc, loc + spread]
497-
y = [y_loc - exon_width / 5, y_loc, y_loc + exon_width / 5]
498-
ax.plot(x, y, lw=.5, color=color, rasterized=raster)
492+
493+
arrows[(loc - spread if strand == "+" else loc + spread, y_loc - exon_width / 5)] = Path.MOVETO
494+
arrows[(loc, y_loc)] = Path.LINETO
495+
arrows[(loc - spread if strand == "+" else loc + spread, y_loc + exon_width / 5)] = Path.LINETO
499496

500497
y_loc += 1 # if transcript.transcript else .5
501498
if plot_domain and obj.domain and transcript.transcript_id in obj.domain.pep:
@@ -516,15 +513,11 @@ def plot_annotation(
516513
s = 0 if s < 0 else s
517514
e = len(region) - 1 if e > len(region) else e
518515

519-
x = [
520-
graph_coords[s], graph_coords[e],
521-
graph_coords[e], graph_coords[s]
522-
]
523-
y = [
524-
y_loc - exon_width / 4, y_loc - exon_width / 4,
525-
y_loc + exon_width / 4, y_loc + exon_width / 4
526-
]
527-
ax.fill(x, y, color, lw=.5, zorder=20, rasterized=raster)
516+
patches.append(plt.Rectangle(
517+
(graph_coords[s], y_loc - exon_width / 2),
518+
width=graph_coords[e] - graph_coords[s], height=exon_width,
519+
lw=.5, zorder=20,rasterized=raster, color=color
520+
))
528521

529522
# @2022.05.13
530523
intron_relative_s = region.relative(
@@ -540,11 +533,12 @@ def plot_annotation(
540533
if intron_relative_e <= 0:
541534
continue
542535

543-
intron_sites = [graph_coords[intron_relative_s],
544-
graph_coords[intron_relative_e]]
545536
if len(sub_exon) != 1:
546-
ax.plot(intron_sites, [y_loc, y_loc], color=color, lw=0.2, rasterized=raster)
547-
537+
patches.append(plt.Rectangle(
538+
(graph_coords[intron_relative_s], y_loc),
539+
width=graph_coords[intron_relative_e] - graph_coords[intron_relative_s],
540+
height=0, color=color, lw=0.2, rasterized=raster
541+
))
548542
if show_id:
549543
ax.text(x=-1, y=y_loc - 0.125, s=f"{sub_current_domain.gene}|{transcript.transcript_id}",
550544
fontsize=font_size / 2, ha="right")
@@ -571,16 +565,11 @@ def plot_annotation(
571565
s = 0 if s < 0 else s
572566
e = len(region) - 1 if e > len(region) else e
573567

574-
x = [
575-
graph_coords[s], graph_coords[e],
576-
graph_coords[e], graph_coords[s]
577-
]
578-
y = [
579-
y_loc - exon_width / 4, y_loc - exon_width / 4,
580-
y_loc + exon_width / 4, y_loc + exon_width / 4
581-
]
582-
ax.fill(x, y, color, lw=.5,
583-
zorder=20, rasterized=raster)
568+
patches.append(plt.Rectangle(
569+
(graph_coords[s], y_loc - exon_width / 2),
570+
width=graph_coords[e] - graph_coords[s], height=exon_width,
571+
lw=.5, zorder=20, rasterized=raster, color=color
572+
))
584573

585574
# @2022.05.13
586575
intron_relative_s = region.relative(
@@ -596,17 +585,21 @@ def plot_annotation(
596585
if intron_relative_e <= 0:
597586
continue
598587

599-
intron_sites = [graph_coords[intron_relative_s],
600-
graph_coords[intron_relative_e]]
601588
if len(sub_exon) != 1:
602-
ax.plot(intron_sites, [y_loc, y_loc],
603-
color=color, lw=0.2, rasterized=raster)
589+
patches.append(plt.Rectangle(
590+
(graph_coords[intron_relative_s], y_loc),
591+
width=graph_coords[intron_relative_e]-graph_coords[intron_relative_s],
592+
height=0, color=color, lw=0.2, rasterized=raster
593+
))
604594

605595
ax.text(x=-1, y=y_loc - 0.125, s=f"{sub_current_domain.gene}|{base_name}",
606596
fontsize=font_size / 2, ha="right")
607597

608598
y_loc += 1
609599

600+
ax.add_collection(PatchCollection(patches, match_original=True))
601+
ax.add_patch(PathPatch(Path(list(arrows.keys()), list(arrows.values())), lw=.5, color=color, rasterized=raster))
602+
610603
# @2022.05.13 Set y lim using y_loc value.
611604
ax.set_xlim(min(graph_coords), max(graph_coords))
612605
ax.set_ylim(-.5, y_loc + .5)
@@ -1208,6 +1201,7 @@ def plot_igv_like(
12081201
y_label = obj.label
12091202
y_loc = 0.5
12101203

1204+
patches, scatters_x, scatters_y = [], [], []
12111205
# Add this to skip zero coverage regions.
12121206
if obj.meta is not None:
12131207

@@ -1228,17 +1222,12 @@ def plot_igv_like(
12281222
s = 0 if s < 0 else s
12291223
e = len(region) - 1 if e >= len(region) else e
12301224

1231-
x = [
1232-
graph_coords[s], graph_coords[e],
1233-
graph_coords[e], graph_coords[s]
1234-
]
1235-
y = [
1236-
y_loc - exon_width, y_loc - exon_width,
1237-
y_loc + exon_width, y_loc + exon_width
1238-
]
1225+
patches.append(plt.Rectangle((graph_coords[s], y_loc - exon_width),
1226+
width=graph_coords[e] - graph_coords[s],
1227+
height=exon_width * 2,
1228+
facecolor='k' if not exon_color else exon_color,
1229+
lw=.5, zorder=20))
12391230

1240-
ax.fill(x, y, 'k' if not exon_color else exon_color,
1241-
lw=.5, zorder=20)
12421231
add_plot = add_plot | True
12431232

12441233
for intron in c_data.introns:
@@ -1253,9 +1242,12 @@ def plot_igv_like(
12531242
graph_coords[s],
12541243
graph_coords[e]
12551244
]
1256-
ax.plot(intron_sites, [y_loc, y_loc],
1257-
color="#4d4d4d" if not intron_color else intron_color,
1258-
lw=0.2, rasterized=raster)
1245+
patches.append(plt.Rectangle(
1246+
(graph_coords[s], y_loc),
1247+
width=graph_coords[e]-graph_coords[s], height=0,
1248+
color="#4d4d4d" if not intron_color else intron_color,
1249+
lw=.2, rasterized=raster
1250+
))
12591251

12601252
for feature in c_data.features:
12611253
if feature.start == -1:
@@ -1275,31 +1267,33 @@ def plot_igv_like(
12751267
s = 0 if s < 0 else s
12761268
e = len(region) - 1 if e >= len(region) else e
12771269

1278-
x = [
1279-
graph_coords[s], graph_coords[e],
1280-
graph_coords[e], graph_coords[s]
1281-
]
12821270
width_ratio = 0.75 if not is_site else 1.5
12831271

1284-
y = [
1285-
y_loc - exon_width * width_ratio, y_loc - exon_width * width_ratio,
1286-
y_loc + exon_width * width_ratio, y_loc + exon_width * width_ratio
1287-
]
1288-
12891272
if is_site:
1290-
ax.fill(x, y, 'b' if not feature_color else feature_color, lw=.2, zorder=20)
1291-
ax.scatter(graph_coords[s],
1292-
y_loc + exon_width * width_ratio,
1293-
c='b' if not feature_color else feature_color,
1294-
s=.5,
1295-
linewidths=(0,),
1296-
rasterized=raster
1297-
)
1273+
patches.append(plt.Rectangle((graph_coords[s], y_loc - exon_width * width_ratio),
1274+
width=graph_coords[e] - graph_coords[s],
1275+
height=exon_width * 2,
1276+
facecolor='blue' if not feature_color else feature_color,
1277+
lw=.2, zorder=20, rasterized = raster))
1278+
1279+
scatters_x.append(graph_coords[s])
1280+
scatters_y.append(y_loc + exon_width * width_ratio)
12981281
else:
1299-
ax.fill(x, y, 'r' if not feature_color else feature_color, lw=.2, zorder=20)
1282+
patches.append(plt.Rectangle((graph_coords[s], y_loc - exon_width * width_ratio),
1283+
width=graph_coords[e] - graph_coords[s],
1284+
height=exon_width * 2,
1285+
facecolor='red' if not feature_color else feature_color,
1286+
lw=.2, zorder=20, rasterized = raster))
13001287
if add_plot:
13011288
y_loc += 1
13021289

1290+
# using patches and draw scatter in bath to reduce time
1291+
ax.add_collection(PatchCollection(patches, match_original=True))
1292+
ax.scatter(
1293+
scatters_x, scatters_y, rasterized=raster,
1294+
color='b' if not feature_color else feature_color, s=.5, linewidths=(0,)
1295+
)
1296+
13031297
set_y_ticks(
13041298
ax, label=y_label, theme=theme,
13051299
graph_coords=graph_coords,

0 commit comments

Comments
 (0)