1616from adjustText import adjust_text
1717from loguru import logger
1818from matplotlib import pylab
19+ from matplotlib import pyplot as plt
1920from matplotlib .font_manager import FontProperties
2021from matplotlib .patches import PathPatch , Polygon
22+ from matplotlib .collections import PatchCollection
2123from matplotlib .path import Path
2224from matplotlib .textpath import TextPath
2325from matplotlib .transforms import Affine2D
24- from mpl_toolkits .axes_grid1 .inset_locator import inset_axes
2526from scipy .cluster .hierarchy import dendrogram , linkage
2627from 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