From 76c4749e048b10a74b720e349dea4f277b9c658c Mon Sep 17 00:00:00 2001 From: Stefan Janssen Date: Wed, 13 Sep 2023 11:43:36 +0200 Subject: [PATCH 1/2] added two new RNA functions (+tests) to read a dot-plot matrix and to plot in our own style --- phylofiller/rna.py | 238 ++++++++++++++++++++++++ phylofiller/test/data/alidot.eps | 244 +++++++++++++++++++++++++ phylofiller/test/data/dot_1.ps | Bin 0 -> 15150 bytes phylofiller/test/data/dot_2.ps | Bin 0 -> 15182 bytes phylofiller/test/test_rna.py | 299 +++++++++++++++++++++++++++++++ 5 files changed, 781 insertions(+) create mode 100644 phylofiller/rna.py create mode 100644 phylofiller/test/data/alidot.eps create mode 100644 phylofiller/test/data/dot_1.ps create mode 100644 phylofiller/test/data/dot_2.ps create mode 100644 phylofiller/test/test_rna.py diff --git a/phylofiller/rna.py b/phylofiller/rna.py new file mode 100644 index 0000000..ee3695f --- /dev/null +++ b/phylofiller/rna.py @@ -0,0 +1,238 @@ +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt + + +def read_dotplot(fp_dotplot, omit_MEA=True): + """Read a dot postscript file from Vienna. + + Parameters + ---------- + fp_dotplot : str + Filepath to dot.ps file, procuded by e.g. RNAfold + omit_MEA : bool + If True, only report base pair probabilities, i.e. upper right + triangle but NO base pairs used in the Maximum Expected Accurracy + secondary structure, i.e. lower left triangle (= lbox) + + Returns + ------- + A tuple of a pandas.DataFrame and a nucleotide sequence as a str. + The dataframe has a two level index of type int to indicate start and + end positions of all read base pairs and one column calles "probabilities" + that holds the actual base pair probabilities, i.e. read data from the ps + file CONVERTED from sqrt(prob) to prob. + probability + start end + 1 17 0.177101 + 19 0.172224 + 21 0.277901 + 24 0.084927 + 38 0.212154 + ... ... + 38 46 0.194564 + 39 44 0.130276 + 40 45 0.347719 + 41 45 0.090910 + 42 46 0.103339 + If omit_MEA == False, the DataFrame holds an additional column "box" + flagging each probability as either ubox (uppler right triangle) or + lbox (lower left triangle = MEA). + """ + data = None + seq = [] + + recordseq = False + breakNext = False + peek = None + with open(fp_dotplot, 'r') as f: + for lnr, line in enumerate(f.readlines()): + if line.startswith(') } def'): + recordseq = False + if recordseq: + seq.append(line.strip().replace('\\', '')) + if line.startswith('/sequence { ('): + recordseq = True + if line.startswith('%start of base pair probability data'): + breakNext = True + continue + if breakNext: + peek = line + break + # rnafold and rnaalifold produce different data blocks: + # RNAfold: + # %start of base pair probability data + # 1 8 0.003375481 ubox + # 1 9 0.003326045 ubox + # + # RNAalifold: + # %start of base pair probability data + # 0.99 1.00 hsb 1 73 0.999728 ubox + # 0.60 1.00 hsb 1 73 0.399728 ubox + # + # we peek into the first data row and decide on the number of fields + # which program might be the source + fields = ['start', 'end', 'probability', 'box'] # default to RNAfold + if len(peek.strip().split()) > 4: + fields = ['field1', 'field2', 'hsb', 'start', 'end', 'probability', + 'box'] + + data = pd.read_csv(fp_dotplot, sep=" ", skiprows=lnr+1, header=None, + names=fields).iloc[:-3] + # convert sqrt(p(i,j)) back to p(i,j) + data['probability'] = data['probability'].apply(lambda x: np.sqrt(x)) + + for field in ['start', 'end']: + data[field] = data[field].astype(int) + for field in ['probability']: + data[field] = data[field].astype(float) + + if omit_MEA: + data = data[data['box'] != 'lbox'] + del data['box'] + + return data.set_index(['start', 'end']), ''.join(seq) + + +def dotPlot(bp_ref, seq_ref, bp_mut=None, seq_mut=None, plotsize=10, title="", + drawSequence=True, color_ref='darkgreen', color_mut='darkred', + color_both='yellow'): + """Produces an RNA 'dot-plot' to visualize base-pair probabilities. + + Parameters + ---------- + bp_ref : pandas.DataFrame + Base pair probabilities for a reference folding space. + The datatype is a pandas DataFrame with a two level index of type + int that address positions of the base pair and one column that + holds base pair probabilities, e.g. + + probability + start end + 1 17 0.177101 + 19 0.172224 + 21 0.277901 + 24 0.084927 + 38 0.212154 + ... ... + 38 46 0.194564 + 39 44 0.130276 + 40 45 0.347719 + 41 45 0.090910 + 42 46 0.103339 + seq_ref : str + The nucleotide sequence of the RNA molecule. + (It is used to determine the size of the dot-plot.) + bp_mut : pandas.DataFrame + Base pair probabilities of an alternative folding space, + e.g. a mutated RNA molecule. Datatype is the same as in + bp_ref. + seq_mut : str + The nucleotide sequence of a mutated RNA molecule. + plotsize : int + The relative size of the figure, which also depends on the + seq_ref length. + title : str + A title for the dot-plot. + drawSequence : bool + seq_ref and/or seq_mut won't we used as tick labels if set + to False. + color_ref : str + The color to plot base-pair probabilities for the reference + folding space. + color_mut : str + The color to plot base-pair probabilities for the mutated + folding space. + color_both : str + The color to plot base-pair probabilities that overlap between + the reference and the mutated folding space. + + Returns + ------- + plt.figure + """ + length = len(seq_ref) + plotsize = 0.15 * length + + fig, ax = plt.subplots(figsize=(plotsize, plotsize)) + + # grid at every 10th base + for pos in range(10, length, 10): + ax.axvline(x=pos, color='gray', linewidth=0.5, zorder=-5.0) + ax.axhline(y=pos, color='gray', linewidth=0.5, zorder=-5.0) + + # diagonal line + ax.plot([-0.5, length], [-0.5, length], color='black', linewidth=0.5) + + # plot overlay of two base pair probability sets + for ((start, end), probs) in pd.concat( + [bp_ref, bp_mut], axis=1).fillna(0).iterrows(): + bpprobA = probs.iloc[0] + bpprobB = 0 + if probs.shape[0] == 2: + bpprobB = probs.iloc[1] + + # scale basepair probability with square area -> take square root + # (same as in Vienna) + (sizeA, sizeB) = map(lambda x: x**2, (bpprobA, bpprobB)) + + def _getCoords(start, end, size): + return start - (size / 2) - 1, end - (size / 2) - 1 + size_larger, (x_larger, y_larger) = sizeA, _getCoords( + start, end, sizeA) + size_smaller, (x_smaller, y_smaller) = sizeB, _getCoords( + start, end, sizeB) + color = color_ref + if bpprobB > bpprobA: + size_larger, (x_larger, y_larger), \ + size_smaller, (x_smaller, y_smaller) = \ + size_smaller, (x_smaller, y_smaller), \ + size_larger, (x_larger, y_larger) + color = color_mut + ax.add_patch(plt.Rectangle((y_larger, x_larger), + size_larger, size_larger, color=color)) + if size_smaller > 0: + ax.add_patch(plt.Rectangle((y_smaller, x_smaller), + size_smaller, size_smaller, + color=color_both)) + + ax.set_xlim((-0.5, length - 0.5)) + ax.set_ylim((-0.5, length - 0.5)) + + if seq_mut is None: + seq_mut = seq_ref + top_x = list(map(lambda x: '\n'.join(x) if x[0] != x[1] else "\n" + x[0], + zip(seq_mut, seq_ref))) + bottom_x = list( + map(lambda x: '\n'.join(x) if x[0] != x[1] else x[0] + "\n", + zip(seq_ref, seq_mut))) + left_y = list(map(lambda x: ' '.join(x) if x[0] != x[1] else x[0], + zip(seq_mut, seq_ref))) + right_y = list(map(lambda x: ' '.join(x) if x[0] != x[1] else x[0], + zip(seq_ref, seq_mut))) + # sequence as tick labels at all four corners + if drawSequence: + ax.set_xticks(range(0, length), ) + ax.set_xticklabels(bottom_x, fontsize=6) + ax.tick_params(axis=u'both', which=u'both', length=0) + + ax.set_yticks(ax.get_xticks()) + ax.set_yticklabels(left_y, fontsize=6, ha="right") + + ax2 = ax.twiny() + ax2.set_xlim(ax.get_xlim()) + ax2.set_xticks(ax.get_xticks()) + ax2.set_xticklabels(top_x, fontsize=6) + ax2.tick_params(axis=u'both', which=u'both', length=0) + + ax3 = ax.twinx() + ax3.set_ylim(ax.get_ylim()) + ax3.set_yticks(ax.get_xticks()) + ax3.set_yticklabels(reversed(right_y), fontsize=6, ha='left') + ax3.tick_params(axis=u'both', which=u'both', length=0) + + ax.invert_yaxis() + + ax.set_title(title, fontsize=30) + + return fig diff --git a/phylofiller/test/data/alidot.eps b/phylofiller/test/data/alidot.eps new file mode 100644 index 0000000..83b19d8 --- /dev/null +++ b/phylofiller/test/data/alidot.eps @@ -0,0 +1,244 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: ViennaRNA-2.6.3 +%%CreationDate: Tue Sep 12 17:28:23 2023 +%%Title: RNA Dot Plot +%%BoundingBox: 66 211 518 662 +%%DocumentFonts: Helvetica +%%Pages: 1 +%%EndComments + +% Program options: --noLP + +% This file contains the square roots of probabilities in the form +% i j sqrt(p(i,j)) ubox + +/DPdict 100 dict def + +DPdict begin + +%%BeginProlog + +/logscale false def +/lpmin 1e-05 log def +/DataVisible [ true true true true] def +/DataTitles [ false false false false ] def +/min { 2 copy gt { exch } if pop } bind def +/max { 2 copy lt { exch } if pop } bind def +/box { %size x y box - draws box centered on x,y + 2 index 0.5 mul sub % x -= 0.5 + exch 2 index 0.5 mul sub exch % y -= 0.5 + 3 -1 roll dup rectfill +} bind def +/ubox { + logscale { + log dup add lpmin div 1 exch sub dup 0 lt { pop 0 } if + } if + 3 1 roll + exch len exch sub 1 add box +} bind def +/lbox { + 3 1 roll + len exch sub 1 add box +} bind def +/drawseq { % print sequence along all 4 sides +[ [0.7 -0.3 0 ] + [0.7 0.7 len add 0] + [-0.3 len sub -0.4 -90] + [-0.3 len sub 0.7 len add -90] +] { + gsave + aload pop rotate translate + 0 1 len 1 sub { + dup 0 moveto + sequence exch 1 getinterval + show + } for + grestore + } forall +} bind def +/drawgrid{ + gsave + 0.5 dup translate + 0.01 setlinewidth + len log 0.9 sub cvi 10 exch exp % grid spacing + dup 1 gt { + dup dup 20 div dup 2 array astore exch 40 div setdash + } { [0.3 0.7] 0.1 setdash } ifelse + 0 exch len { + dup dup + 0 moveto + len lineto + dup + len exch sub 0 exch moveto + len exch len exch sub lineto + stroke + } for + [] 0 setdash + 0.04 setlinewidth + % draw strand separators if required + currentdict /nicks known { + gsave + % draw lines in red color + 0 1 1 sethsbcolor + % draw with line thickness of 0.2 + 0.2 setlinewidth + nicks + { 1 sub + dup dup -1 moveto len 1 add lineto + len exch sub dup + -1 exch moveto len 1 add exch lineto + stroke + } forall + grestore + } if + % draw diagonal + 0 len moveto len 0 lineto stroke + grestore +} bind def +/drawTitle { + currentdict /DPtitle known { + % center title text + /Helvetica findfont 10 scalefont setfont + 360 705 moveto DPtitle dup stringwidth pop 2 div neg 0 rmoveto show + } if +} bind def +/prepareCoords { + 0 1 3 { + % check whether we want to display current data + dup DataVisible exch get + { + % check whether we've actually got some data + DataSource exch get dup currentdict exch known { + % data source s_j is present, so find length of array + currentdict exch get length + } { pop 0 } ifelse + } if + } for + exch dup 5 -1 roll add 4 -1 roll dup 5 1 roll 4 -1 roll add max + len add 3 add 700 exch div dup scale + exch 1 add exch 1 add translate +} bind def +/utri{ % i j prob utri + gsave + 0.5 dup translate + 1 min 2 div + 0.85 mul 0.15 add 0.95 0.33 + 3 1 roll % prepare hsb color + sethsbcolor + % now produce the coordinates for lines + exch 1 sub dup len exch sub dup 4 -1 roll dup 3 1 roll dup len exch sub + moveto lineto lineto closepath fill + grestore +} bind def + +%%EndProlog + +/DPtitle { + (alidot.ps) +} def + +/sequence { (\ +GGGCCCGUAGCACAGUGGA__AGAGCACAUGCCUUCCAACCA_GAUGUCCCGGGUUCGAAUCCAGCCGAGCCCA\ +) } def +/len { sequence length } bind def + +72 216 translate +72 6 mul len 1 add div dup scale +/Helvetica findfont 0.95 scalefont setfont + +drawseq +/hsb { +dup 0.3 mul 1 exch sub sethsbcolor +} bind def + + +%draw the grid +drawgrid + +%start of base pair probability data +0.99 1.00 hsb 1 73 0.999728 ubox +0.60 1.00 hsb 1 73 0.399728 ubox +0.32 1.00 hsb 1 73 0.9995 lbox +0.16 1.00 hsb 2 72 1.000000 ubox +0.16 1.00 hsb 2 72 1.0000 lbox +0.32 1.00 hsb 3 71 0.999999 ubox +0.32 1.00 hsb 3 71 1.0000 lbox +0.32 1.00 hsb 4 70 0.999999 ubox +0.32 1.00 hsb 4 70 1.0000 lbox +0.16 0.20 hsb 5 69 0.999625 ubox +0.16 0.20 hsb 5 69 0.9993 lbox +0.32 0.60 hsb 6 68 0.999951 ubox +0.32 0.60 hsb 6 68 0.9999 lbox +0.16 0.20 hsb 7 28 0.005574 ubox +0.16 1.00 hsb 7 67 0.999065 ubox +0.16 1.00 hsb 7 67 0.9981 lbox +0.00 1.00 hsb 8 14 0.003372 ubox +0.16 1.00 hsb 8 27 0.142834 ubox +0.16 0.20 hsb 8 45 0.003922 ubox +0.00 1.00 hsb 8 47 0.011281 ubox +0.16 0.20 hsb 8 66 0.064655 ubox +0.32 0.20 hsb 9 13 0.003015 ubox +0.16 0.60 hsb 9 16 0.001129 ubox +0.32 0.20 hsb 9 26 0.014167 ubox +0.16 0.20 hsb 9 46 0.007284 ubox +0.16 0.20 hsb 9 49 0.008486 ubox +0.00 0.60 hsb 10 16 0.002903 ubox +0.16 0.20 hsb 10 24 0.001556 ubox +0.16 1.00 hsb 10 26 0.999285 ubox +0.16 1.00 hsb 10 26 0.9986 lbox +0.00 0.20 hsb 10 28 0.011098 ubox +0.00 0.20 hsb 10 48 0.001017 ubox +0.16 1.00 hsb 10 49 0.001798 ubox +0.32 1.00 hsb 11 15 0.003132 ubox +0.32 1.00 hsb 11 23 0.001588 ubox +0.16 1.00 hsb 11 25 0.999858 ubox +0.16 1.00 hsb 11 25 0.9997 lbox +0.16 0.60 hsb 11 27 0.011981 ubox +0.16 1.00 hsb 11 47 0.001200 ubox +0.32 0.20 hsb 12 19 0.003318 ubox +0.32 0.60 hsb 12 24 0.999731 ubox +0.32 0.60 hsb 12 24 0.9995 lbox +0.16 0.20 hsb 12 26 0.011941 ubox +0.16 0.60 hsb 13 18 0.008608 ubox +0.32 1.00 hsb 13 23 0.999432 ubox +0.32 1.00 hsb 13 23 0.9989 lbox +0.16 1.00 hsb 13 25 0.011947 ubox +0.16 0.20 hsb 15 24 0.007699 ubox +0.32 1.00 hsb 15 26 0.006331 ubox +0.16 0.60 hsb 16 23 0.007765 ubox +0.16 0.60 hsb 16 25 0.006329 ubox +0.16 0.20 hsb 17 24 0.006435 ubox +0.16 1.00 hsb 23 49 0.009394 ubox +0.16 0.20 hsb 24 48 0.009126 ubox +0.00 0.20 hsb 25 48 0.002487 ubox +0.16 1.00 hsb 26 47 0.019164 ubox +0.16 0.20 hsb 27 46 0.067310 ubox +0.16 0.20 hsb 27 48 0.028508 ubox +0.00 0.20 hsb 28 44 0.975306 ubox +0.00 0.20 hsb 28 44 0.9512 lbox +0.32 0.20 hsb 28 45 0.024899 ubox +0.00 0.20 hsb 28 47 0.028905 ubox +0.16 0.20 hsb 29 43 0.997679 ubox +0.16 0.20 hsb 29 43 0.9954 lbox +0.32 1.00 hsb 30 42 0.999985 ubox +0.32 1.00 hsb 30 42 1.0000 lbox +0.32 1.00 hsb 31 41 0.999999 ubox +0.32 1.00 hsb 31 41 1.0000 lbox +0.32 1.00 hsb 32 40 0.999864 ubox +0.32 1.00 hsb 32 40 0.9997 lbox +0.16 0.20 hsb 49 66 0.004595 ubox +0.32 1.00 hsb 50 66 0.997558 ubox +0.32 1.00 hsb 50 66 0.9951 lbox +0.32 1.00 hsb 51 65 0.999993 ubox +0.32 1.00 hsb 51 65 1.0000 lbox +0.32 1.00 hsb 52 64 0.999997 ubox +0.32 1.00 hsb 52 64 1.0000 lbox +0.16 1.00 hsb 53 62 0.001558 ubox +0.00 1.00 hsb 53 63 0.999999 ubox +0.00 1.00 hsb 53 63 1.0000 lbox +0.16 0.20 hsb 54 61 0.003646 ubox +0.16 0.60 hsb 54 62 0.999342 ubox +0.16 0.60 hsb 54 62 0.9987 lbox +showpage +end +%%EOF diff --git a/phylofiller/test/data/dot_1.ps b/phylofiller/test/data/dot_1.ps new file mode 100644 index 0000000000000000000000000000000000000000..00e08442acdadeded8e3559f70a2260ef3b88b1c GIT binary patch literal 15150 zcmds;U2h!6v4-#WS9C6jNPxJS?++oluq0a!5cmj@l^aDchPy*@tmV#nXO^Un;QyYd zs=Mc7$w=mjjT48msNI^L>guYu-m2*ypZwwVo71n>?OA;qF9Y-S>o?!XhsDXsi+x@7 z?f#khdsA<>)!)AR>NHxW%g|qH+U?7#ub-Lk54Cwy?@Sn(Fn<Ca`=;M8 zf_u%&wl}XgZIASMd)TgTp$W`)}H9-#s(m)|>0PZ&nqT zURM`2BANf|?fOM~CF8oq;>5h(w-@{B%Cx&4qZlK??=PFqoHv`=tk6|8+s^ctwdsC3 zRQuZO+qUmadv137_N+QJ*R(`t1?)vyzdUZU{08F92}d|kl1cEW_{S1eZA^I zz-BRBn?%rj63s(I^qUpvebstxY-rZawF#{WVx%Yyj5ZQAVxzc(f^U9+yc#XIvZ zSmx$5SjO1>9s~8e$Yd%pEKsSA5RojHFNrxV@3}I)Q3t%YPQB==>sppCHdJec*1qi_ z3P4ki_QX;g+AW)2s>UF@5+%<0V)D9Yqh8-rzladp3Gw)IW3?k^cH7E7iE%Tn!LT{p0R z4RL+DGlW7^n{HRFU^wm&t(Z*`@r57xE`KATj%)kTRQrAP(Nt=-RhHPxOuw!=nfYhp zOvs4dGJnr6HBGK5_-Z(XLCzAfaz34%J1|q%>RMQvcQsboVRAv;Ef8=jk8jlLvw`9! zud3_!?S~-%Xnlvt;!C#}TnjMgu02uGFx* z-KX1T^`SE#w(X5yun(w2LIQ2XEG2f~pcQ1S7{QuQpjY>o-PtI0_uMr7rQS$2UJ#)PK>Ps#$M{{ z+g|h1cRQZ4zUl~TVDA^BGa$CWS*ny1X>x6AGS%$e zc*hjV)zhWy_JXzgMceMzoi0bf5xWe+^2>Vl!Q5Pem3nV(YI9RzW#2MIx7(1nZuMqO z*?)Uy;n*bNeP8cU*MqoFQ( zYF67MT<>6Fb%iqAVTjN^se1shjk(?cNgFxJKWX98<`VR|g}+{nN2%^4V|m-lhL)(+UTJ z2W_DyY%+7++m}~u-<*2~9^7}d{@eZwmf3%~_clk~^WXpDy>|>EWuY-&ZBHkIek<60 zJ4c=U`fIq2q%F(;cG%6mxB1<8@BbZkf0@DVgV~sa75xsNHzws!VYN0+i-G3xaC!DL z%?gJLdSBw}^pNAO)JjFVqtp*V&AsL3K~-mSHx5qpJBY)5G&%RIjnq9Z^lg$jTI1mb zcgAM3mdZQd@AEw`CD^w`AfB* zm;>8_vu!ka#E?f=>oe;siL`BiU6=d?6Kq(%ee-g<)fy{x3!eAUC)ErR{GfbcTea@57_dUqd zN~QNc$_KC$8;fIok&@vE30+d=k00p8f#ajRF$~k^`RT)mXAHGX3FG00397?o=5oyQ zt3H6*U58)GK1)0gHBfPc>wJy1&$yp=oqI<1+aT=LQUhd1*7?wkf`92v$7ByR*L%`) zg?d+g`&*dIE=SAba}Si(&G|=tde)r`2(RAoSbf-TWGnAud(h1(_H?RVzWtFCp>@j! z(jPe58TVLZ@8g|$+P6Cqnx`A6!G1c^VrM_^h2-_)i&N3@ z+Ei#kW@kB(tylJK?WrAG?#)WJ+}Nn2O3JoKb-3l!uG)Xp4UuYZ_sQ6PZ>~vUc4u7V zUG$#NlG^A!p`J>sA|ZQXWUqOMAZLDWYB@b&M@;5;-nN^%8dS+0{$x(>lkaM{7+G<$ z$(*&6@wJ#L$By(`fUM!14a0lcSz8N|Za9sgojma^KJ>Rls;S@!4N?MwG4Df^wa zS+sk;p78x9t!R*;{2lbkcB*XBaiH1t)#}4DHV!Mc5BW12qs#F&Ju%I)Ud|0OlRY|T zE2`O>)jxJC*~-O)v0BRZt4RFVO4T0gKB11?*H68@L*@$ zhXGEUri|@PKCvZ1Im*#KiRlkfltt$>E4MUB+Rcyn8T;)K{W-B4drR1u@Nvk@@|>nN{KL@-LgHdC2UbN$YOkka|t? zp>H1fb5fz{N9(p|DDK`rahW=^$vpr1)nC4wc5CjKaWQn*?-sWC%>lCj*35)%LoQD}p0e0_CID8^j)J;~AXkK6ZV5 z^+b_p-}SJsE7+AU9`GFe8TAV{u|1Ra_6_bO7vvQ>oqi$qa>Wml*H2WYAKc3BZI;a= z2(!AbnvIm2$9j{{oM_@7^Jv98b}X)YEPc+NwuN%@u>UUMMmm1_=$NOjpg{I(0CJ2fsOtLbuFW~X&MlRAgjI$(5%RyR- zG%B+!43p4Hqfn%I9z{hS=Yup+y?GQySsr+4tg|I)5R@VJ>Fa*H3m|&KFiPSe$|5gK zBAqRa<0MJqK`L+P1aVxXVHpqhCaIk*2{G8&5V0N0P?nYkMdlX4JCvD7(=^EApe(Ff zq-t0cW_g|^PHiMF5iu6uWC<$#*hmeFqcRV34y1Jvj8(&eG8NwpdSjU_jEX4EbM^t; zSloo(L@bL^x3tLcP7-6|EKQ?07#4w}85@OR8t|XjfQ?9lG6=(rT_C47(X~Z+k_4#_ zQ0&~0l=v3^>ri%BWx)yYjq^Bl3Szh5FpJAL;B62$)-5e9@~q_j5HHoqg1CrL?aYl0 zF%iS!GL6I3#S*D3I80;ADgqaZ*x02dL6Q^%&g->H!w-bAaL!2_OiPxQ#DkDmVr8;S z28W|m!N3czVHtQpa+)G0FjI&hc%MKQfm9(Kre%f|&V1Ed9)bNR8#PFPk_5$hk&k0-!V{me7-zGXCKN%M`L(4E(_x|+e@m9WR^Q}K&opigFM9P!zwJ& z!#E7mxNu`>_beXbhz!E3>R+UqnR%3k*^m`TC2mQQXF(MBTtTY#;`KC(<3X>bI2=YA zdz@v4RTB9i0=lx>cY-i4Q+ytaz{&vIEs*T9y{X#jV1k zB#Z+X&Bu+$MqP1MFgjpn9g9>okZMsJ7B1b8Dt^ID8sLawtgaZ#;wXw-2JnKp#aWp_ zvVIl0T`^Q2gVfwvhE(;YAQe&{^y-RXlCUfSpWKYqe8bc{!kyj*n->KypG7%1(p3}= z)j<))WZ@`P+F=b5Wa4Zn9C2Y*5$C*C>NHqXOJymk7hW1$pCynwz#gQEvn(RSL);3B zYN(J>h>wS{;xl3DB!PduRQVg1=dhG7LWM(h9_49HDi32FdBz$3aeknARvW?qHY@#D zq_${cK@xhZF5QYFXeWRmtlrY-+6tH;DC5$32dU}>q#4ZaY(VO;4y{K%V@qc*1u~;b zyJ*5i4m^~3inQ+t5UY+PBy6OHwL(T~n4_Z1XbPO$5L;diVnSDB&Su21rMfgr!f2u! z-0|vHk!57AkF<W_&LAnsa}Q)l)a7}a1h9(p2x7H_DJkL9 zHMbnsppbXMkC}@WqurD-X2bKtXq&$Q5T|kQx_#q8+reUdy(E2Eu&$Df;ufbQ^1&e~ zi)`?T>k$H>ku3353?-I2gCv*k!y8CB7vzWsi!k*Vfg5b#`M)dTGVlbB*eRjzMoH?G zQ1&FmD2c$2CnHzZ-o+Ba12{B4fs_`FAfz>d=_X=} z%#3s%m*dI?O%E*uoyefX)`B3VMljxqGhf6()mW}cR;}V#l7_tA=rRPcMOBys*@C56 z0}*TPg+lykIqG((f*11CQz?*gogv*Cpz=x(D;<*wG$g*bGTLS(%^~^fIZMP!4Ov0p z<$r*&mhfhQ&0!(~>^)8(J{3zKvBet*Z|Rf)ERrXKZp4~987iV2*jU|glpReqly1X` zp%`m-4qeBIpVDy*+#qlm{Ow5wvvVrMWVBYyv~RGxb1dQ^4APCbzu2In9@v8r0{mg5cLTwx1?00 zJe5#dN3m_6;*8PxD0Wmp5CTZnjV5|PBdDDW7A9T|my5^3jn*sJX#JWlz zqzbe+B|dk6J& zIx%3gVW5QPxlcfxtzlra#NKBbQ#*fWG+#q+ir5xx9wtdwpYtM?FrXx*J@q~TBsMRl zHYY<7MXX9>$_QY&$ctF<9mYunARMZQlC_)uDU3?bu_6|npq57&OtGs(Z1GJCM~moF zis0GhpjQ0iM-TiBzw#j9EkSG_Iq)~6fo^X%7*s0YNs(F{ zc-@F?E#^Uur(p*+AY~m3t@9x2yBiT(d?S+_bW%tj{@7SqwZp6AA&B~$m!K=>S0X`YJC;Vg$3`2cBa~Kq{ zhM0#S*-Y?@2ZOpC9?RJt@Vgl}M0JOeJmt|S+r{G$=fNVPOo0MT5{!26fgLGTE(|W^ zOZqZ??At#sgJ&|w&@-9zP2&Bz&^0`Bp=(LzLf1>og|0W43tg?B3ticr3FZBg9skqm OqTa4$OZ9)gS^N`WVbbsb literal 0 HcmV?d00001 diff --git a/phylofiller/test/data/dot_2.ps b/phylofiller/test/data/dot_2.ps new file mode 100644 index 0000000000000000000000000000000000000000..66d1a7c90e3f993d1b1203ac3eed5e189c240c4d GIT binary patch literal 15182 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%0AlI@Zt z!3hWt5lb0zOVYVE7~C%j+AC@xBW0ICHe_{LO2<}#pOM=I8*KuO0WqUh3mi}jQf}&u zW+*HPSuIFQT-Vq(a>w3}-NZnq5~!)9DW!6S_y(L^XcjqbOVqpeQAN(vJ_gJt;nh@V z($EECAEzK>U+H9@(NsXO3r!4?(q4LQ@YQWT$tWnH3X=*h0h6qxy^O#C!a55kIHw8S z1l(#WOG$wox>g~3^rvyAw~9-8o0J*I8)GVGrDI$ zHj*^~ZC0=(F)@){!NM8@eo_*f$b1rnFySN#Iey zR5U57_(*}G9)0Mk1R%X^aM0CINEMxuCxdJfJ~or)Cq8T70~g=4T4_Ne7r8ygrnJ;a zH3@OPq?x7ehu#AmP}xaBu`CD{F9h^)o|jHA_Iz-=IKfm|nMDhF^daU6&AJRu3&6qS zg*=}G*^pf}RCH%az*roq7HmrTnkFVUA?2JVPeb5Rh!dZ#2*CFhThmMa%8zXV+QY30h!F6BM-?xGBypuKs{kvOce2R_@lHdxAsOh&_}9BZIz=jh2RkHDvelc(pn%>^@g>@(y+@(TTk|?eSGSx)LE{j2RDw3}a#JtnHB!~gF zzztdKEl(jOh0*U4ZXdCZi7wy`z=3Vtk_DBnrdRa}#xg1r zVzUi*=)uc6r8oB6A*zys`br}{k#(0;Z5|v#XksUqBzUr9)gUAk7%D@_18N`>CoZMr z(LF1|E+IRNm3rBPKxQxAu=MBTTHubIX`a`XnSoOfMV$quInEh;c)eZU*U&HX<6im+ zkLq(;c<`Om!UN-+CP^oDI1m`K<$yCJ#Q`w{`2jJ+@|+lB^MDva@PHUXYEG2*Pp Date: Wed, 27 Sep 2023 23:10:57 +0200 Subject: [PATCH 2/2] type -> isinstance() --- phylofiller/test/test_rna.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phylofiller/test/test_rna.py b/phylofiller/test/test_rna.py index bfe3c12..f7ec339 100644 --- a/phylofiller/test/test_rna.py +++ b/phylofiller/test/test_rna.py @@ -15,10 +15,10 @@ def dpElements(fig): width_rect = [] height_rect = [] for c in fig.get_axes()[0]._children: - if type(c) == matplotlib.lines.Line2D: + if isinstance(c, matplotlib.lines.Line2D): xs_lines.extend(list(c._x)) ys_lines.extend(list(c._y)) - elif type(c) == matplotlib.patches.Rectangle: + elif isinstance(c, matplotlib.patches.Rectangle): xs_rect.append(c._x0) ys_rect.append(c._y0) width_rect.append(c._width)