From b8169b62ce5fc9ffc7e48ea0317331331ede2bf7 Mon Sep 17 00:00:00 2001 From: Thomas OBADIA <thomas.obadia@pasteur.fr> Date: Tue, 3 May 2022 15:02:31 +0200 Subject: [PATCH] Add support for Bioplex MFI outputs, including guessing the type of machine... --- ...e_3_BIOPLEX_MFI-WITH-BEAD-COUNTS_XLSX.xlsx | Bin 0 -> 27169 bytes .../Example_3_plate_layout_XLSX.xlsx | Bin 0 -> 10612 bytes ..._BIOPLEX_MFI-WITHOUT-BEAD-COUNTS_XLSX.xlsx | Bin 0 -> 19603 bytes .../Example_4_plate_layout_XLSX.xlsx | Bin 0 -> 10614 bytes FUNCTIONS.R | 625 ++++++++++-------- SHINY_APP.R | 4 +- 6 files changed, 357 insertions(+), 272 deletions(-) create mode 100644 Example data files/Example_3_BIOPLEX_MFI-WITH-BEAD-COUNTS_XLSX.xlsx create mode 100644 Example data files/Example_3_plate_layout_XLSX.xlsx create mode 100644 Example data files/Example_4_BIOPLEX_MFI-WITHOUT-BEAD-COUNTS_XLSX.xlsx create mode 100644 Example data files/Example_4_plate_layout_XLSX.xlsx diff --git a/Example data files/Example_3_BIOPLEX_MFI-WITH-BEAD-COUNTS_XLSX.xlsx b/Example data files/Example_3_BIOPLEX_MFI-WITH-BEAD-COUNTS_XLSX.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..6ef56a6d0c5f4e685b57c2bd8fdedb99cbf12ad6 GIT binary patch literal 27169 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 literal 0 HcmV?d00001 diff --git a/Example data files/Example_3_plate_layout_XLSX.xlsx b/Example data files/Example_3_plate_layout_XLSX.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..ee9bfeac6a710506c8f7b05ebc31005fdc5c3556 GIT binary patch literal 10612 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 literal 0 HcmV?d00001 diff --git a/Example data files/Example_4_BIOPLEX_MFI-WITHOUT-BEAD-COUNTS_XLSX.xlsx b/Example data files/Example_4_BIOPLEX_MFI-WITHOUT-BEAD-COUNTS_XLSX.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b21b3433f3f2fa83ab48f91a673f7d2f41596951 GIT binary patch literal 19603 zcmeFZ^;aFwmj!xpcXtTx?p%VqySux)6Fj(u-~{L5?h@SHU4naXdwjk#@6EiK`4495 zhgwyu>YnaCckep8devzqSqMl>05kv=0058ygt<ee9KZkoNGJdR0{{!IE9&UrX71o- zsOIft?yAq^Wp77X00~Z$2LS(^|9|Cwu?IkEBMyBmsGXW8U&XpKqVglk%V7!I2z4or zV19TdX;CyC;L*JLQ(Gvj*TVKvD%(xzQr!yRR<2TMiwN!6F+FQ5QWznW#8$do1K*@_ z#X|}~8nN{WF`Rk0NSBw;bB!=^EvqI>HeWuzP-1g7z_TCr-Y6W4R78FirR=2pQx5*L zd<q=t(*~ENb;mW~@d{O8#FyFB?C4NtW7nXl&U7VGXG(d9rNyjOYHfHXkv<un#mv*M zwZh{QB@wa)ftA{`ksz{%!=cu)V&0Hi20=5EZ?uiJu)+Uc_3|@1mBek!-m7YIH+<y} znMwGro~O7_=h<qrACGXG;d*<<F<ev`#W<>Cex~pM_wI#j?sw<NP<V?IJat5x!<umI z-T4jHH5}D?1F+e4j`o+(wG#XY?)t9=H_jE#OA2z_JP%KN)+!*~(-<eQiti2)G8pkp z)WvG~{yX7Q#TxQPc7ZU-cG@*E!TTrAK0Y7-O8-BaHmb9b|NX?C+~+95Cru4q%<Wv6 znf|H&kDmXFIr*O+9h4-m_?rbW{8H*QV)$WUBLP)J)>BxjolGqtNO~QmG4>Y)!A3VV zA*vdFD1>BSXTa;|+J->v$uQadE(a(E9h0BD$+I#%{n^<ahK|Z5O~M(p(~st{aJO)u zAt~cc<Ix#USJ6_MFFUeDE-`m4R*O2tq=^rQQA8My4a^AE8I;%FG<mE7TM$t_uMDqg z=FU4wp2`edN-I7_`WneAcQ%`WHSB6)xl-jfY)5wYgs-k@#cN$-lH<bn#oN%#uIE-P zvm4{hk5M6OSmg@``Xk$v<Pc@vU7$__$MsmYcRx4W?~388k&qaY(#_9^_1{SnESQph z`{|LkPm<sRV8OiXnE#C@o{lcICXSA_|3tI@mos3WA??%3|Ly`(k(cfNT+$KVBUn5$ zJ+RPL+?c<dsh^_2jx^A&QIW9+{M{m;Ytz?Slx2o+4EMgA81i`FMBatKxa*}ajYfy` z!LYmJhjN^_oq&KfIj<xRk%YoPKRY?CI7dMK>59=Rm^93ggUR#fk%kg4;#ct(vA?ae z;T$%TEbeSJuG2)LpO_hI*bjRCrg~Nx($L&Y`jgh;;G<P^?H)Qe#G7R177tEZ0LdFL zb2Lvz|JW;j4|~(`Y9a4EY1{kj8S#LyLYBCk6X+N2MjA0JNfJn#<a$t$(XM{E<0kSl zO5445!!Y*u(qeyPXkqK~pZ@QV!D;TRfPw%3ZlM7Hl+PKTkonJG0jaM!tnr}w8ybBC zZ@0@Sr(tPFs`PV%f{R(uoBa_7(TSWzqjZwuh%nzCh(;6V=9yPKkTRR|QKrVT$e6zp z+KA_Tt5xCVJqS%epTTTPnGjX@T8$$t6XiaglIKS5^31^S=5%*RlJ09y%&qek9n#89 zf&OsyJFBi*0W|+~rPXxZLNRoN>TQpmEo4d)df!5{xbcq~d^6E_*bHx!@J$DEGK;q5 zg#u?uRB}w7LPz?QRpLsfO$g0U@AXFE{8MZ)E*0sA^<WhhoRypW8E7u}4Z$lkUffrz z$2~GG<v{eGlX2zd#tjR)+(K3!6#jp{qTn4mia7Da_r4^USoA0V<rS!98)2@KQP3Kp z$3+Pwi2c>TS80aZVJJqB-6H<?#DzZf(Itf`xakeDaVn)iEZF^4g-`j<U+@wi=1qnl z`ur=>VH(7P!!i2V`yFgNz3Ybb)N&9Ca_)OdHq7&Zni@p+4YM>RF7)p!(&RNBN+L(d zO-g1pwe(I{D|o4@_bUZ~<HtxT=vvjjV-7;Wy{YZ^DPJmQ0%Q8Ln|#_ZB&w^I9&9VI zGD-3rQb{R!NCkD$-tu&Km=izpbZPvSal=oJCY@hhm#68I?^gAAMV&gb{QF<mdoe5! zIF-9FZ_(!xHv?lVtl0>uq<e<uPDx{Ty;-D_olz%oV#Je}sD8O!6LqpkYgjF}o`{1H zvzM?Izl^&<L~Qo`wxb?fy6p~Q&h2^l%Gr6|{xHAgZdkayrdjz&%j=gaU-pnqKlSE? zSz!7yd8M9KHENA1d2M~pOh!UyHWsnYYy$Ms;PwA);b9hPzzn|CVf;HFZ*HShs^V07 zPF>iI750fV`XYVqY2y41-cc&}VBP*Y_)p4@aT4n^%1iW>UzmB(sbpQJTvRhZP~MQ8 zok%KAeK1SJnxx*JzYq@n$lRfcoO8x3yk>zfzGnCt&-ADM$!uTsfF$zlqlKy+f8;rR zs2<eQ(ipspwpF29n;j&sOhhEoRXUQxdHUgg$NWDsh7kqB*Wr^ftY`oL=D#t<)ymk# z+)UNY#oEEr^`AA?Zh68XQwS4k+rOymcceNUCl}(NN5Xk@pbBTAc~V+fQ8dpSBv$Cy zHl6>Af{uDQkLB^$q46JL2L1ZyD;K+d#f8&0+od%2TE_fU(Fd(&McdPNrcON_5ft?o zEl&N4#YN_#N|elIM-3g6KELJ#+f#2`g?G1dl&KS~V`)#7%GR68)A$7=!Fxqj{}c7Q z(%e-1xCyn@dp{r0$s&5h#X7RBEME@gr2X3-s2~t)X%*zwsg0D$TyCu~XI%fYE8tzX z{l#5nZ<X4Sib?N<Xx?5@Z=7NgsHi6eb2>Q-s_$vDT}(IgKgRE-ct|Y{5MShW9}0cG zKfLg1XK(JQyy&a+W6`{#s@9})4P#9Irt2C;*x$-V;5q^(|7#s@KIX*BW*mbosq4}Q zc;UGHot_LcNKA<~AN^P2UMdF1#d4vQg=upB^&pibdxS*~vS?{a%ysh@j<MRP89ia{ zX&$bIP*NUk3Dgvqkh~W#HF!Uxo)ly;Of*Qm0Z3sfIN=0o8Psn{Xpm+5krajKJ>Cd@ zDLpCZf!dJ0GOWTiW8X+=iXesQ@Prd&6i`3MSE0`MAys9hn7+U!#h|q&=!u{fCZR)? znL+j*e_p{A%&i%K5kjl4#s3BN)4+d>M_T;51tx?eA=u(9VHdQ|QMu1wqeA-1z=hme zKnl}R!>YjLsq}1!upA&J#h`lsGl3!<6|$`GKi`8l!jS`P@!!03G~pT*In*;YGFTbf z0^Y)ZJ;molus8}RI{`7ZNwW4xT3BWa_*vN>Ayz8Iq?zAoc}yoU-B_IRCZO_Am0N=l z$%}`VV_h#o5I4mVXEJHn{4m4^9SfWQo<<c=_q<~2Oim1z@G!2$P8`%WQT4p4JX!2v zbG)iQls0Ibb%|1$zXS$WPqYX5Yk8)xNpzl>7ImX+SO=<dsvBy)l=8d1WX~R2O#8O} zSe_IctzJ@gsefs+FBhJme*2B^O3nAmv(!dB&JT@7u#eE%@~YLCKAU#aSySX_7<E4J zXv=>)DRft$^k*`*1xP}ob{V5sWV$w6d+0*7Gu&Qjirt2RlgnTK+q3Hm`yMh7ruwYv zBzJxr!82~^KKEDT=`zj-&_MNe)by6%e~M@%&mzd9p8^OT1^__%cYeBg+nN81m<|8L zGZsXDlA1u&zZat(dkk=PII^k`P))`{{6^K$jMO6TtQ3^4TxVO7D+;>>8<oEeOri>q zQBY{9I&F%Cd@UH3=zh<LN1~|~Rkf^|J<j=~jkDCnl}E!@YK5bW?^e9tRm``A8hPpt z5`x2;vv;^xX``BqF(%I&9EH>Jlr<*K_b%E++9Uo-y-P17b;ebQD%9H{HYOYU3YIu8 zaP~8AzAitmx^??v+Sr(4Y~WnNT677#v}xvBT4`)Cl|&^k^f5?rSIPk2-u;{jkbczm zhtgNZQ{m6IG)oGh06dkH1eSmlUt5Dn4zlp`R%VjPIURaS<ucDva~|jdgOGvHSwpp* z_`JSQ&79S0D@7=+FLE~x^CATMd2V@nmda{%n|nXuySUcd7t~ywk0xrl-4=*nDPYb! z6TOImnF9L0)gQza2-aj&I3|Tcj>D=}SVQ0GwlR@TUqm6w=#EZu*a+VieNB(ZqLXK! zW}&_@G?r~+5j8%rJroOIfjshpcQPwK_%?bjb@*Yj(@L;P2r5gj>WCZ>5u{4YMAo0u z?^^@DZ7fQin~`nUry6_J8(i(7qvNFk4Z5!%O&f@PAqgN3x+BEx{rf&v&S4*ciYIOa zGsS2yTfamf-1UMtq(UUYvs8q;6xm;U<MF_VzD^6D65+&ESSG=YmjlP}o^ANPvhn3B zDhn+{FSt>!za6vuKD_hQxX%72$>rt0?|aS{iI}@|>9_E!EWTIgVY072h<Oz+XUCCy zjQe&$9u216i5Gz?odNF8hzk})^67z%`uK02j(0(Py$_bAHeLj%I4T5sqm~S^mEhNn zm9(9i;*za9{%K;@+@ZNEd-O12XFQ2sGa1WXJD&_larA+TV>9n$%#ldno4#x8B$^YL zBI;ZQ!gM(&chEwuh$%fLTvNAH2qC@jWT45@KO*hF%Q${}Hrd|K5s^<tPyTP9|3}8T zTA7=>x&Hem;9niq|8EQMExtFpj|Efw0peZM%Qty33{g~-`%8=ZA%w~ED)d@<!UN`; zZ#|)T&%uGy)B#uKi$EEH1#%+8d>eS!zA27X&6V#eW4CZE&&fI_9HT&j@f=gvPY4AC znW&)zEi8oEFlCB2+}{EHE46WOWeI|RR-~i6Sw@~>2&O^hHug7$Fb&KKjh77j{+gkD zKo#xxvp9be0f%4RlTtTR-=ujHj06j$c!$N$Z-PvOPd)E*Z2gfTi|xn9Ztvm#Cl72K zvQpYn0RX1m008cP^T2;RdsfO?+$sxd_$B=v(U+~{ezKY%*b1BGQr{)l>R9$5al(=& z-m;T+&oc^0xH5RsVMK*LFGKg2<GG4;u*i9_O6C$)xXA#SDi>u3wwt{d7Z$SMTF|(5 zzRd729-_BFM<DlCx=wao00C(}2i}T~?nJ@x6{D1eh`Ml;=c0_P#dY<jSn+I+iy=h} zP1K$<MFx$u{EuTn#6|-I<m%{4%9N&XE1Dkz?-Lqo#YNJaFw_~F=M`X@bt3&Tn0-0G z6v(8hO|GI@JN4}jMGkWT9_vpfOVGp?@G2n<UzMtCOYnua+_x+Ho#Us(cSEV>+sm+g z>%tw<b!d;${o1snqdpnF<tyRkM{w7%B1_*y%YDUlKWN;t_Wq5c&WCh{Kdn7P9`*1X zAoiQ!$*yT0${CXjqQ9y=GkOQX<`s@gfaV~&6v)Xu`XY~uUT(tZMC(_GxRDkt7_Z=h z;o+eYO!@b<u@*wgf|x<~%@fAXvahE`1Be}^yXnK{*y7h$`!yCXeIB97PHb|!WbzW} znT8A37?Bf;CU9{+2opEA^tf?3g9^X<?|RgOL%fg*oh&gJM-=dSV|f>SemQvwSNALz zIf<cPb=Jx>$#a(ChY;+L;Svn1@(`C?FE=hF2ge-X;SxZ1k^3wbL{|?y*1WeE8+GV@ zl$Cw91t%A$!1gwt=ZdlSem_rIbb+O>Z-CkR?<W<n76G{v3f^q4CnGqJU)m#@unV&T zQJtETH%7_`y!&AqI74Jt)upQAPCnAfZ!B*~{zb!oRVmr0Ri`b9VTMIBC<$7gv_~8) zn_a`)D{ru|InX!s82cP-?2cb4y&1Lj#wPLJmV3elv82ny>a-Zr0-x`jw*o&*A5KZS z-(Tmxew=m(J{}qdjt9K%tbGl5due{WWY)gjL-|Nf?S8*c|9IWnxZDbOdAkgJy&Iay zy&(yFKW@I>{J?e05!-!kZh5ZGy_LV6%l){!RSbM*d@mSoUH6l3-J8&Hc<EX<`M1_X z*SgC;Eft3*Z$pR1;`M6_Ms8n2ht7X%1sXcEf42TKtS{;Q==xE)b?uXL9LL<fw@`N+ zm%XJtZl8Ox{6b{zGt_%oI?EpyAep^6^FmbZ7-%yza_QwiFh^wBy>|yx@YLJvVD&Cr z;%*++?d)t7H#oiegY8}Y@Y#>??`Rd@JY#lr@a|kSn?73f^K-rxKr}CivIiH3Gu+^C zGoPI#VjA0cc}PdOMBv@63*c{I;N#h;qj`MoIvcxnAw%lebx__s_b2=P0=>Dl^;Xl4 z;cuR)UN5=+Hv7ZxVXYQ~qn-*yRFVmLO}2>@TNpgOF(>rWfBA?w1y;+V@L#ue+}}pH zFtr#AyBdB?34BqSWHDUgcK(<yi0F<|i#nAHYVl=#DG}Z0k}XBP6Q-urP%kzWP|}sr zT!z3DXJUQ^s9+b(yP5$r%jeSPLl4wxNyei%4`SUm+0Ad(1-lVWM_p9UZmnZFQWVbz z)3^!9AY@PXD>`rA><aBs($qak5R2_nDY-V^RI074GgXv_HK(OJW6}huoq~!d2o{EN z?2IK9L}1d=)Qb(~>&w_RFOwTn6((qyn9acXAyO)X5QMAdF7;CA_MlZbVllatsBqpk zo_;hcfNpOOW5~$QTPo~!7%l>=HEh<3)da_?^L|;-KlOXIY%bp9pMmkU2K3w=lpD|9 zqmj;~Z(!Kr69mna_6e4=S8PR&n^zT2Garsx)=oZysK!QBhy(K?JI48|(x1DCL0zD) zAdx!tv4rc3beV&()nc{h-<s3PMX{ZcL{b9PS}TxYZjrwc?G(f*Z+3lP_bY=igczZ+ zojWVk?QyV+a<@}aXmYn>!LwsKrv^`kopW;)WPAXa+IPKK6B>-~gYAZ^DJ!2Y@pd#6 zF3E3BW%Y(pIxLox!sIF<B2~5Ex)Lc)8@x-PaVzGp)nCP!1pP`$_sJKdB{@qaHS9RI zK5I#|)3IJ+89UndgBiKi=q`oH)L=Xda$DDLyr0~?(5_r{Z=&T4Eeqh&wgGEL&Gd0h z%`hNK8liRxlEM{aQz8@|y`OM<W?-z>^bJwUFUr>bS$+&Yz4(k?<*FU)zG(R-uC7sD z0;ZzAxSI8xVuNsugc9jye%))!>C>ap@_Y9p-DW~dXLcN$W8y{Js$tt&?07ro^acd8 z>3Df4RUkW~{iSlJ|FjMFO=%Kp$zM1$tz0(bAW|h9<zVY;M-)tTpvumX!{di9$KFLc ze3A7UL;mbSS4doWxvp-KpfH20ozsSBY3YOwFTmW~0*J|Z?2{iMRiVG#Uk9JbIwfSz z7~NH4ZPRV2t+nt|VTOe}HixtV|Apn!&ymjq2)OYekA4he^t!Ac*LaHk^fw*OhP^6X z^dmf<cjrRa7MNLUD_IVDc1J4&BwwR$977pxp*4{HlFxcEs<39|3~}DfQLe;tj!xf4 z?{Ie>yKSOqMec>6qKXa=`jSCT-wh>*%@EW-$M{Wsv&irGtIqdJ#jkEM?kvcBWt>|r zfp82Kq<D#7(Q-sCFbN<H@;{Gs;~G`<(-K4&n6r+ja)}}@2Vp4lr91AFv;+h}4o3FY z;XMFM*Jgz#JAK_!|M5TOnPI&@Y`juWX$|?(-Bwqq)Hb9(!BVR;ctuooDq=Qu+7>i$ zrzg2f9#o~`t2gE1TSC1^mUfY0pL)67S0M8uRV`G0PN2JRWa0pzg`cC|Oj%U`ne5oh zy0<zEIUZZ8z-)3UmL6@>AC-=2{HZ3Y;*P0oIlM;|S~#vk6-qqy3ZN(}Od=*d#lK@w zE<_u9kxN{UI2>EI@E)cQ(;?z^q#)2gL!!{m9CLGp8MW1)2mXQ4LA)<B(QM7QDatHU z3#BWvQG&|0+tSgj1r$xH`O1{89SKEEK944{ZN~b1X$(@R|Ninb$b}^In&TNxUt25x z*E~|;`6gly4Wr`%rvSH(-~!B+@SSyIa`g>Ytml+R@?`j0O8Ew++q~V3h7En_H%TSn z2dQ=$(Ob`~4C6WM>|<S*rTD=h7nA}+jn#S(I6KCRNdEevmRAzr9qY!UT;pmegp_{8 zisvDFr}cX1!NBvYXqHgdC=BQ3+ia~vdGY-B(h|?ovG#SYW09NV+)+0IOU0ChXLFLs z-TK7QII&C_q@<kE5gFTIxSGZbpJJ_+HUD(WDyF9Vao2G+-E2b#*9-!Z=ZL3cd?Kn# zS9f{KIhIYcdTqq;W3SFpU@G&5YTNfUDm9q8>O9rzIL4}ZuFb@hjLs@xhCMRC>|7$6 zKSV7|&K4+SP}Y9huX8ir_L4EMg6Knb?Yu3crb+My?qFn||Kb55rMqBZ2{bh5Aj?!G z_QS~Z+nJS1?HE-~W!6`2%WaY`)7Eu5Y<PA)6LC-pEE&54N)@FogOOh9l>L!HQ}&NP z)wc-vhM`p1_7%d6fJl}ZWVqFfmB+CDZSdBS&QrLqV6oDwBUA70JxETK;b(TaM=Aup zVOuF(eKWevJ$4pH8&+40NXcD|Bl20)Jf{=j=x=a5AcHwpNmipPBQbZJ+i%h?i0_4& zeLB{QOX$JCG3Ds5D=y7$=+Il(EbArMG(%UIT6IN-+S9CTk;f!gk%c`lo3+yC(Sz)s zqsE>;l442y6^vraHd8*{D6C4^2fKMjKL|tGnBbbUvg(W(!1{AI)Zp-NrtBD6h5Ozb z$<EEx_Zn0>Uv?}!OGzbOuxXOoY}4V()zH9fuUa0iF+GxQd^?CtG3V`qk~g*v0p$h@ ztV3oG^GVQao>#z}>D1NJ)P3x0TX=2-SF_+-r%@>zGBP?i@!xmPtS2EID8XyeI>^Mo z3^-ObT|+PLy!m9#)aZmoLf^V7pwfQ>CSh6hDwSD_yk2Z-cK%oJskQ|pzp7Yw2!gp? zIFy!{pn#{NGjK^iCAeDL;7>Nx3jR~CrJ)^NiN0HEOt}_cz8j23rV8iJ5J93*Hj{He z9cWJdoS4QVmck>0E!88RT&jerggCEkQ?AlT{MX#Ow?nYQXm|9SzPF&2y>7Fe`$gJX z&&@iFKMb9H@?J0anB6VeVwaXDkI`|NO8T4!gYCuiiRUvsA5w3V&Q`FQ`J2TpN=<B0 zSrr*FCZc64l7=MwpSF!nRnU>&<!--)MJv+i6ig&##Ra%Xu{Fn+%TWol=hh7=cp_wI zuIg2e*>C(|bb*^g3~7wmJ=TT>2kmreUG%X`pV6|iEKk)?gTNF<$-ZIeLKqZJ0{D_6 zlHLNMOebE1Xuz)u0H;~_<zhjJ{nr(&Y~j8Q98=s9xrRV{*$_Llx18?bY5hs84d-Ed zZG^kpnQGyjE?2$wulB-8y-Zn#ilSq*L++)B&B@l~Gm97pEb<ra7mH2*Xv{Ee4q)Lp zzF;knXI@s+yjnNvk<96+4qGO>qfpWPad6myg~|?;RSz9xpJwx;oJwcajU1NjRLW1J zwQuE@kMu_k;)MaUdQ&#B*PEekjtq4DLIWujlHsJPM-H+m2}1?aA$D4^3h9<XD!^<U zE^Yf~`rK>zc=4FuL<hh$G=2Pr(FkSgKM;930s-X<>Q$vC$I-JW?wKm}<wwyoWR5s4 zQ(^YMu5l=uq!klRB{tE-kY<UO$lG*NN41zv4J;RD?d0Tf*cvaY0XFXIPC!qRzbrA! zWeHDE!%Hs!np4F!(E&Qid9MdMmSc3&Oci%AsO<2yfp1p=ya1y&E5*FE445E|*fnyY z;$C*3@c9s&f*Aop>y%K~GdvR{lWR7KWa#*zkJ=$b<ozf=<$Ui=UQgesbo(hCw_2^6 zXc=6yd>NjCqND2B)^V}naO^XaDVzz6NY9Oq|1nI$AR)J8g3d2OAB~3E1lpu!dPU#c z45U&4j|-8-#y^N%A?{5TP7t(Qd?zTRE5)jp&)G<BV<*C8RJ!BLQAuO!lcm=enoH^k zpJ725DqCVRQYQsSnjn##?i=L9X~RMvHQ+^}{i8CFMx`9(ra#Tzre1f7Ve%n_xteQP zf<$Sf1rJKiTkDXeJ7RsE##5u|`glipks%GIEJ@S5bhIc!rn1vsWP$S<%k8APG5qvW zXcuCU+}}4(%gzcFODg@?D-g0w$20s6L|4DDjj@I!%X&O+CssAM7N&mSF$<u;?y(r% zqA?ei_`TjCP%nHwX_3-qd!47H?T248rfyJiebLHnD>uqjWMc@G{ap9+Cx4KD$Glw! z)!cg^sg+x!w;8a_;R4L2aaRElCNY+CNOg+iPeyx}*)kbcHg^a;`^0Arpj)VFnGzc8 zh7rQL?Ja>)ZXGrL3_U894tGUrjG(+<HUu%?o5B6ijXx(T+BEn8Zf!LD;H9p=hVIT{ z!=M@v!2>L0ewfb-i%&mcjz2HFc7UbW*q|_g8hCdnHH(ca?VKM*9GI34(6!r&-6m>@ z#(a+TKJBVT+^v|xsI6=Y2oaWIbF&M5gB@i{Z}h4@3htvwrbH8Jby^Uqyed8@?nEHi zhF~igVnflkSQ{M;^@kQ}yt?S9s^#><zl(bB)7d2XN~76Y@u{isIZf&FE$ql1heTKl zrmYc<W4oiJEOUM%a$rK_U3r6GqO}--v9blK&lyD-&C%%3=XzlH@%3P3+--)u305gc zRko#Pp%2V)kwr}lbeig|ZtBOl_3~EKzgIrRwtHK1{qH-5U4=98GdLhwFBr|za4mT- z)HoOieyg(y<MVMAoEvy20298dx<nc#nn1`JEHPyox8UW_GO*@3cJirP=|~jC;GmB# zor%Hz;WNOP0QJS47iR~%^`!*)uLrT=eQS6aOl^xx)EMf`c=$z%HI_6{lsHZ#`D@{f zf0vWJfvA$Sk1~9?7?HofL?6>)m1OPLcMH=wa!S|&j_qolEl=mc9st9uyK{?1t)uCj zo51*))x=NbEsGHG;daPfSlrq+_V@Lacz7~$$EOTjGN_;?zSNRad#AUWK~b`lUC0ht z0nIFClVqr!7?8ltHOciCY`&}IwhgpF49Yj?4=oOL+Z~N&ecYm|rcbY8AaZD?<rGCY zd_RL7VcMr!GA~h`B`&N9^@B+$clmgAPMf`RLqQ+ma#IDPVBo$puWjIgKmR$v-g#?Y z8y33;Xl|9$|Bghezo$FnS;ICpe$odw!p3TVF0L8Hf9e}ed>L?O&{(5Vj;%3mUEkcm z$sz?ZcFTq|aTtLRGVZD+ub3%PS4^Pg4em#nzcsPT5RcQ7s*~`%uCmjusDF<woQEM2 zT9t|Y!EeqNUvENiGfaJLO{7Pe1G+r%JRVW$p8SkLi|=e|`xy=_SA|CLXX{`C%AKRP zm|)>BeP6hP@Hu_rKoxTdgE{J0UAlfI@~Iaxxt6q3;Z9_81CyyjZ|vMnRqiOafa16w zeuTN$b{K+eN`g{rYK2#=E%lq(_*CmJx%R0hob}-GI&q(Cl@uz4iIyAuGK9EO9O%2Y z4v1gvNwP*o6BkHdCm)3&{X|t2XFrXbiXg-zF*m3Wwrf`9Kv$cy&d?KFT59J!j@=)^ zS9#3Z+zv{2C{!DXl00pfG4N^VYyF|oQmFz;)7Ty<m^~PAfJXc7@GjFMaySYhWS`Fq z@ZO;Y_@pgbTb)O#WI$hJ3EPdh=0`f*>x0n&$=owZ1}8%%6-{%`vaL+1n^uDSu3c2_ zsH!S2%RP@fqQgMt+ygeQ>Qcl-;T%=zd;%9>Y)B4YmHMU{Noaow4+Djb*;nC8XE}&0 zN3~X2!9NJA{bVLU!KY(a`_|wDfO#<2L-$$*V%J~jrtAwgyFJfU8tF{sa=##()uShR z`e=oLLTw1GBPc4Tkqt0Wc|;S|kV(2cQ;BQ@L<cV!b{tQ*QW+KAuEla@5>nke2)ffJ zlAcg-<XeMzYo}2haClN1<=pR+H#R6|0W}@4JU&QCWziUci=OHoe=P?#b3E1|D?<NN zh(s#y)!Opc2+Vbc0;2>UgL~Tg&<d3!oWDYB_zKqA7ma2KN2{O&8vcPk@^pqO<NC7+ zpq0IALL#-V5WUZ~U$dLH@kSL7NJX)ZFYgb#(}$vNE#ru^;K#nN%Xcq+g%^<Ta18nB zVWiIEF<WhclJw$(OTmR;2KuTpXwgMX(FE^)YV43b7?Pu{V=Ksz6kCkgUT0r4n700> zsr8o+z}DoJ*&o4E#=VXH`G+|+{eA-qwIJxS-0S%FsM|tkrHWk>*5sD8l4PWOxch~m zHR%|=J79PrPXOF3V6~}4?tDlv_%H4R-V*&99P~759z8-5%F&?3@vC4B^oE&{FxTB~ z&|crfPdc~Mk8&B8pB3t@Sayq;aqk1WTyI}I$#~>DPSiIb%n~V-al{l8zqsBJk}Vv4 zTZWiNV=a+*ic;@1wrr|)I;4ncfRzc7Lb8wf(gkDajbbN$8t+aW!#b)eQ5Pk<<WO+z zlL}$m;mi734kKHmqZM2-v%-#i9!Ym+eJM-_3~&OJ0##0iXWbw#z)b4<Fy-+O_DQ{5 z3ExF5hOhxTFL@4jAw-z_w^k$;L^fvv7ByOqg>Cu=9pxAgDSWZbv>Nrh^jGtmmGoyq zb)%}t=*NCu0ILmK&%*DM5kR2}RwwGb8~GXrC3_6!Q)P^Ocq15J?QR7S1k~RjXe6`o z2qFE_B@gW(cXSa8UJn&>Cy7CoM($crIe*j_W-g#Oz9LJd`u-gPV{qhn--SgVQk!M% z%%z__@57MNK5o00!(*RfB1NMn#qLjKbLNqej36LrzV3&B(9+Zi7hW_U`bk`UBS^$q zuKDyClyLYo0#l6|%fJ<N#cfn~e8zwL0fE=4(Y6qmMU0cdnboEOgMV^T2dQibTgois z34_bpLv6oAawqgzUI&YCQIK1l%TM_y6}%lqfHZl_KMne*OQsreR)T+hOE~$n6}%si z;*X+sG%^lh&egWfQ%4L`TEgfUVRHkInXE7z&7i5HU;DGISGXFW$>q0KKNzdhI#~cA zb;t_-&{`hrNL*VNS@F&yQ61e`j<J=p%BS0Fp|!J}DS>k#;(*`5?ePpt;aguH0Ehtv z;RkWbwn7xa(7O`#D3T_w>xgh`t>5HL-8L6XF+8oN#+#K<IHb_taSZnqr?$<usTR*j ze5ILV(pxKYZ=^%5P!Le{!S~KIL8^F!7dTOlO(Ckvl!RE~q<9QE<x3~=?nWd9o?;DT zJLyNHvR57QdFTPoG&!mOC#!9>6hzN3Tnivtv&z}uW}w#Vnq<Ra!N1$;wVLj`8ME@w zl}7b-38^D0)0ZMn_`hvA*Y8PdH;f3ng%Wcn70*JoxzDv#O9tQI`q_|jX_JoMr=Tdn znang3<YRypDeHgwY!Kk^dRFzCNiO{mG8}<lmoFwta!JcIp$ua^yQxg|gY2L(6T?{O z{X_JJjT5lDDS{jc!1e_Ne^>vi*Hes|>@(GtcHw!(Lz6dybJ6-K@nS$*du?df5*(IJ z=|3Jk<v1d8L0YS)C{7HSDgIrPkEfbXR*M>$A@*ero{RqUxTUrMvps<TOScPd6;Ri4 z2NMLkW!xZNkw7862aU81S*Kl_U*mxmjG_@@=6WCC6C{@lnG?6W>D-ctXo_6Vh)A)w zbXFlswJp-6IMzXYQaqcTM{Or9=%9P`ZRJE~QrWdu2%GZ+JfUw|Q8DOw&S8YXY>H7^ z7-3t}dYX1Q_Ae?&@|)9m)|-Su%S0SV^~A9Nf=8*Qk}Dt5;~tPX8e5u;=K@?Gm9xG! z_&ugB^nK6VqiqaD(4#q*Htr=YdW5O{1yqaxzYQ?iN=!mg>50@2JmxRLY$Q8@aoHJ@ z>+g*}tx275o87^k(kcR|j41aC_5J<<{ysgs!Wqw>dZqpZu=7br6kQvSUK?YmoadVC zeV9^RBJ~GMWz!2Gqa9aC)EA^7Vp8_6_iz*ep5hvkpn;~?<=wWwDcuZ=nwInSRQScb zL1KVTubT_fw8ppIp{QJ}r<!+rjciiFOM@egH_ecR26%xxBhTi`pgNtqE*NX~c>yH~ z&FWpW{+r}GK&{E*G!kkI9ACXpqdgNK_05iYtx+#gkbR+6A3uk)M!%6y_JY)TR$KDV zVLRf~bVN|B#R6I1^+lM;LNKM4d_~VM6ClAFa$#M4KA`ov02z?&Fh%^i$Y~NzfkaWz zvj8UjEK6ACZgCwoX(}VBg}3Z3&Dx^@38`WaXqSFqZ9HCA=1dr&g~=Cm>FUWXAPyUI z`enKD5=$9<&d=ATdNYZ_TYi&98I%Z6HHJ_8Dt)uA5dF+2ZJc*@!H32Xa<6oWyp+Lw zZ}`=AJ%b8vp(%-a1~CLQ2OewLoKG6eh9YK_zL!&08oZ#-m4rx>{4=J&Ekh_8An;!{ zk<&6XU+0w0hV_9_pxb21heb00olr7HRqArIe*Q!~Y_)Hja|5G2%eqeu<7|s=t8G}1 z<3kbJ$*IMAoh;|$geCs;%*CAI21kXS#@+@qmhQcKuZ7Z%Ql5nUw`JGbeb!@NmL=va zy|i;2a6XHN7K4>*2f$Z#H|P-#%C)eb07i(O0Lo18t|=BJG0*RR?oaH-AoZ)?Q;|n1 z2-W0w^XjrrLnfk47G1Y~ql}cq$e7&&vJaL4krRTkS>!Y0e}O^THq<h1scM1HWja&F z5@ti_S~~65qAzAmdpzSRX_wKLgNTnXQm@ecC1~EM>E+TLYH8m3H5n?1%yn|SWITA< zUAo;c(TGn!)zAyQ>d68i>oFUEVemi#cN6h2iLD%Aox5Lp02BAB+rw1F!rvoEd9#sq z!RXh&2(3{%>&>&Dw@B=S3Jn6k?le{CsqJmV%xLMWg)HQJVopg`s+l}0_YC#pM$RsI zTc}VWf?SBu3fnZJN93C#X;U6Agcvl1ZI5}C<eRFUcG3F-fp}jXDhj()7FUw=nL@^T z`YcLQ`xD1nQ$^ONX@s|O;ZzgZ0b(z<Qi0lM<lz8la}-n<_5moktBZMDB_8KS&AZPy ziVqClZ*%mwK$w5;QtDH&8&5sDj$K=4d-@I&y)50#U*QS!YU*&)x0XBJaZb1OxtW)3 zO-t6V`Kc0|+eM4i16Ffvg_0+`2vLQTojqRiZGNNvLarI8K-#p<2XT}iiU8&IaRr}e zIv87d_gDZXRT9ciA{rhBLt<L{lu+)P%3m(OuACO4UxWW?FouLP7VazvH|Wi60F0E& zrIDXF*v4NR7;JgR4_5YN(r!6(T~e&;q!V2Gd53^xh%p*$7@U=+V7vD@{cuxtqUuJn zG3x6mdKH2OIo;LVOv^O|t&wwNe{x}vc`*n=I?LPziJV;hl{5rpP*EcshMZ_!k%m<x z`=<*kMF6#&&lFUl8v8pu>S5$Qd3SI_rOyy_7wm_{B_<sMFT<J!^W<u=jln^d4aQ;0 zfdP))Am(nFopTpL?;(fXg;~Pr?L0})4foe4X|rQ&t*o~nNR$jz=sjODnC$8)W%9of zv*!nWysrVwGE}ruB(`cA$J}hobToO3R{Lqq%a&B_T9+Uc=Ro2{sQW+n{1gj}8DK)V zsoCO;5@Y1-_bnkdU+?GO*sf1`>JT7}Ds~Zs@(p)WNduVK7*9Vp>n`4;H%JrwB#rZ> zRWMQ*PoX#vYUNw%#ARI9QrhSqy%rL{Y})%h6Edz7V4u`}*qQM9GEiwym60TPS5iQ| z6L$AT)s%Yl5z-gBeBPwJ2F}srNh^z3wgXHnt`SUVYr?<F0Sy;KUN_{Yc0qTkmg!Z( zfYxrrUhZ_ESX`waDNpWFy!O*KfrrAv_pcC|Z&vx06yDf?f>F#EMpS$9q)?cTA`>X^ z>E(7#V>2~=?wNOxU_~OH36ZpsspkSj6L2dj5O3#k%ljnXJ_`JN$)E$SE%ABV+P%QL zKV}|M3L8uJhhQ#>i9P_j&Hk?{qY&0Ud4tH?5N2IH;^lN}EP3C~VZoN;&g=v5uR(|m zHhVE-^Hrx&LkxApDJh6!8+1#b;1y{+`H2o%RNGb#Y&%-s@gY8b>Sq-%K=C83*-|nh zGP$|O^uo}BFkAUL3E+d&bJ}o`)7MTyuHs&FfI9>*nBVG!xoIp;GuXAL<yPn^>+eR_ zrJT|>Wt|4+>NfC!E`-LfRxpluM<9YF7m1?$2+DF}l6PMEi;smN^aA%RA<O2QTETxz zg}A>w3e<SGlTB5NV~m3_WN(&fXg=k&^~0FsEkadXV|vY(zwaI*FS&AZ-UBk;rs0Bh zbR_|Ac|Pz*__u$Bx5v4xFM<$)CNVV^^X}hbDUY8*!FmD+keA$NUV84FIy$!yjx?Tf zKi_&lR0V|U_S(=v<*3oRo*^&o;Z(SH3_N{p4G0q2d7mRs7y^5s_{nh!nP+jE(J0m2 zy6vO!!*U<KaE*r>+Z-C5fU^2zQj=2g-Do3I5v+WJu6pj%jk)+xlv?(`dXmA+MjGMV zgxg`P<}rK$kw`qZToY1(qd8$uv@IA3jA4@ua-44Y-`>y(gI{_bgcb&oFN2RfcKxdi zeywSZ4PicQ&~g7`u-XE*Xc>`Q3_pzVNcH%YKhT13M=qCn7iA{Pn$ZldKA!!0zzhX@ z=vLqjO3Ti8EnpgtAz*BRYnIu_e8;k^+9;7pM({6z)Hl8{Q53ttMV;>_>wZMPN$QoF zBA*nfm(@2*n63r2x$ZD^`R;m>AEX~hJpnrgGe|lK!`zH%zriT#9*^L$DbVGjJ;6y; z$l-WvS;mSd(+1jRMK*`$!#mMAY=pL!L%6qIdYUnU+pOC!Q|dtRfW#10QRx7(XBM35 zQK<z=LE5o$rsZ)uG3h99PqO>J;wLlUfUkeWryr{~rr5aLev=mkvr|hQ#kXZ^C!M2? z3(dPidEI6rHn(cG6+qE!@et7d>eWJNdffZgK?kFr{#d|}*k;6DlOtGCHw&E8hgGnN zl_zbi-7c}&dgb6NG(j2mAdF+LH<Toe3Z=q#pR~|YS!9>8yiUSN;vTt)+8r`-Cu|8` zNRxN_v4+!aS8C$fbM4vVUO9%-s67sTT-r&;|9O=nhR!yl`c70r^K1v4kmT|O({7k) zxb2|RFnn`KKx3+pUSP7<5oDpTk)>X9|0_;f5kI#k00?s1+dD4gP!i(1%T~;99n_ls z0!=YvVb=G49pU)#cz3z={%m@me&)NfSG;D(mCkh6G`etv|5VD&DdY*6_;XNn3T|S| zkCgVVr#G>`VB;AlaWVUkPoGOtENKBOTl0t)_FTplANN%J?Sy0B<n4qA{vcn>@X<YK zDGm}UW3`L~R`T;~U6bAk?G$Im^wKaj<7tPik+z^^UNLs?8?hA4qjr=>;xw~s>sU1D zUs*JF<xc#2iFVlcS*_LSi!2U<re})+rqf`GIi|qeC<*g0pPgG-2^Zz7@<KH<+zzE0 zqQ+>4ESJU#2|>D+TO;#=FU^htir9&`#|SY2uM8~6I;*o@X|ou{`PQ#&`BXx%^1p)d z!-NxFj`I<;E0g`L@UaHs?Uo4zL|-PBr@8hS#Q5jm#44eqBihL3{bU$%XlIs?R%IhJ z9G>P&cK-+imp1N?CV5hu1RNe;$t((_GOI2r8)8F)bGTUl#(vpLM>fMq7!E+8o{ycb z(n2i}$#>+L*(7YBj!7Vl?Z}Wl!wegpc{T@}-1cl$5aqP+t4%(#>!U@Ct^~ZWx)%&@ z-Jamj{?;t28s(BL(R7erOszQJycvltgVq?IAGXEPl{7KO!1BQa{zYQ`S><Ai3!{wm ztxZ|E=rB%XL(E5DUm(5YqWowxl4R~c{phElpgaBgpNDB1Ona-JhvfEqo)VWn531$* z#fttm&r@6P_b-=$X8~_}OSvDP2jxCK{&hfyBUTt5oE1^L8r=mTiH2sz=ZjM%Al#6_ z#luAz>*a6x?c)7K;GKk?vMZ627705CxE<gr!6Myix%BL)2#eyr<XJL~xx&TwWxRaT z_{X6A!{mEe9+h5}UIV0FY5#rU=;?sCiBS6>MAiG1x^Lt7+1$Ub{a;hqdT~&@6h057 z4wC+pY5K2J3pXosdvoT0)&Dw}ccwiNL%@yMjr05!(be;nb1$B9b9>4zd4s|%JB6^Z z=}cLJoh_+@00W$o_fCbTuqa)`5tuS71cB7~Hy)0n;h1EGdSaQp@_-`KPDaNADMs?W zv!vu9C&1^{?RvKJhmYhBgubLS!!D&8VAl8L9~5(Gfwnv}9Da&YDc{k|;T)b27+z)9 zasyn@>_=dSmLZ<R?DEq{q<Poqum@nn6I*&Lc!Ia6z7I+Dk>_F$5fX)N{=lX$QmMox zPkJ;6F;VjMXCos&LrRqi^eyX9++j@=LHbU!rCDA}nP0p2yG|rB>`3K;h-~%7IER&= zc-MFKfd(Bt!0L(e?GUG#8GA;9O6&kyTht-%gRNE0$WLp=_2#(OW|kB-RL8f-5TaCX z=<f7Qr@Lvk1k8#fB(=C{neQ~!A;B0)>3Jc)UGq|X4106hP`|#Fs60MwDNil1CY%JZ zZCf0nT9{T)?{`GDMTzC<QXZzE?(Zepa|5O3zqU$Z(k?CGOks^So~^4`W_?F-7@LTp z!ktO9Q_2ZQ6=y1{a#~-w4|Kj?un7W!z*Q)ucSi;g6{lhAT4HE;xBJ&9=U~wt!_ldX zTGVKfWYiz41%uy<$)lTC*a}nijiR&&RPMb@`bXrNUg`4|xJj9FfWGfn$F~vPz`(yp zPrnW8S{vzP9bhO`RWBCe4L{!Aa5fFQ->*)txsT_mulxGDU!TU(yFae^?rL_|v6#F3 zZ*Gp0akl*L4i)2xR@VrLeq517)m~NYm|jxF7zZQ2T!Veh?q?C{PZB~q@+iF+8wNMf zB|YhLuQ~5qBh(s%>JaF*U1=k^Zl#FJsFtXOS~vaff$Pb)UH)_I>dGlcZX@}&HL5*o zI??umNd^RXIAqV$+*MhLtug<D6pbEL%+ouo+FdL9ov5N+H1c2x$hIq26ZQ83wK_C% zj#AXch*E{ALDVF2tk0bE+v?54rhWDAQriWfpBcIF=Y6mQM3x;4j#)7&pQ-SQv2qf1 z#hC50xdiq~QdKcii1|d*-aeZ$e3Sjd7jcI#+zpSohx|gU-_MmK>vA;P8iUK82=rKv zRT6Mrl}j4#51>LO6HoLEmB#UlU*Q)6iV`Z-v9346HTAt&d})E&xA~bu9r<iNNmSeM zJ)@HZ$1S8L#(Y(vX}NJCik+YDwk&nE9aq)u`yj4wv!JWiv@!eGVe5?M7o$|7GZ_&5 z@TB@(mJyhFQn~FStxGyAz4^m+ihYfxqjQKCHcb@TR^b!A*oKTg&q0=&Pj(!#{5B`} z0m**M9PJX+5ld2gxAbW&thQyk5B7zhz7u_bWBrhk;U@6B3lh)I29NZ<`F;Xb*)~ML zI2;xp5oaSL66;-0EW1L-?jj{~Ig<+o0#ibx6C-&)c3>*1j{4Pel-Lu@6&fmD_xdG; zFO$anP%O9}?tz5C*2gMmy~imD<htms?rN~``0-M1F=rrHZ9Zw6ajP?UcYh~rlhNT9 zJW6$WbCjdlQG38nmU1x$HP9~N=?~d4^BsnzbWOR9y4413^2_5-&L0fxDddcM&QxEi z*;eDH5p_h(H+r(7<?htH*s1bN3nSd=v2v#?UleY=;$!C&EL%ZsKg$rHlAgS1^<qWS zWr~k<fR~$<^QQqvYM$EZ&@?M^xW;l81j_EX%7(}ym<E>L3|2C<X56%9lupv~btIe* z;92aku)GfLx5YaTB7|3k!z~C?4o$I28ZtV*SHVwaNcbePmt}w{nJ}}H#=EJmv^jg* zxLcQZiWf4He|M{oWtn9O!P#NppGbSr9&F2qm}gULHEAVqVLypQL9-SALfsNfMWs63 z&+qWmXan0!N6umUgmHxXd&vvBm3&?or$DvE15Y}~;nCAL4G*U-LM(+(FIiy}nl<r< zZ%RL-A!A@F62}Z-fjzxu5UQZg-cKQVl1yh*H~wKSwqA1U@C{t$90ii#9n~tQ_ws=) zA`rz2VP7XnY<wpK-yt_zp_UdiJxp9wP)w-H$pmX~W(`*2!IS5%(Wuo@NAxUjN*p;~ zjFLye53`&;&Zw&K8AW;$T*Z~2z4@n10zb+j?mHDXxJed9U7@Z2SQZb^oy@x>A4l-- zBURjp&ezcuIlL(_x>VURwIZW0c*}I`%=^bc^s+GKj<d;YcPRXhuCG^KYhY)9MO2*U zdbD?!-~nD&QGXrI9@lXfVmOt_etaHX?^v*a@eCI5aSG%qQ8`i)aZD$td7dR0PT?|& zSx?rEh284lrDpsHptXb9kXdF-!8h(kRSQ+gM{BAG^T5>@d#lkqai>GsHcC|1L!LW* z2GcX17Wc5O9$I3$*gIgH2t$jBkuw~}Y210<?b2KEbG}3)&3<Vd&C|B`unDth1kylu zD*T#0TjUEfsMNhg^4X}FAr!dkBu7GCU2sr3<%Vx`Y1W0cp)?QprCkb>D#KseOz@WX zMoMpLbU^EG)&+R5X>a=YACqGxD3i^KK1bL-bGJ}GlVi;sO_f|6om`oXot*ytD-Zvd z68kAjvXXoMNs$S^gt$izzi6~`91a)KP%dQTGwKbYJa1Go=fkFw4e!St$q8tPEuL(f zbD#)(KBShxUK(1Y0)DgVpx4f^U;r!eH(&2Ei*J9asAh;N)hhydD%D|#c`$wrK+W9d zhqWT5GTSj3!S8n%@t#L&pev@}SFs_{2}=nFKDgU+to!&|^^!5y+4b&3Zqt=kwbhel z5~c&+tE~_2dT??*O5%cwT8{Z_8$|eu^azOj{&G^;7R8E^WbR*-_QDG~V)VB{`NKjh zvSIk;L|1BHYjB7xj!K}uR+176A)T(#U=&~inS7^@MA0W4b&PtGaIlO4^xD-S%E)j| zR+&kxP5S+$mpewFA|%a(X(4}7F`KM@)~rDG_zBC5cqB|cE@Ws7RJim~{!orH$xdak z58<4t5y|i<-#*%|LhT&vf!pSVjpxJLLAq}P{Oisb9FNt?^jjXI*-|``Td^-#B<<PV zl)si34Y3EBl)>oQe%vx<A?yZY6P_8>*`%zA-mTsm;2I3g{Y4n|OSH&kYrD{vziJ+0 z`Zq!QUzMfZ0h*P{C$ud-)1J`&Gqg<|UCjRv+n=ER_wp@CS@Ab1X4sbeD+<yjV^Job z^rw6~8yTQnwDJ>eS&L-k2=aJ&nDwa8=eSPvMJ731PndjA$MXxWRjEKsq>%ulsR~nD zLj>ZCIfa9t{y+!K!?U>oV*WfGh>zNiHU>A&aN<(;Lgy4R9Jq&jOfF!Wj(IAv5_m`4 z>`G)t^9FM0>aoA~HU%Z~bPZzhQ2j7%feOD95|b?4hNyezFo!`U6otd0v1E;ceZS^o zNqxH&Dfyn2>K3u;$u~$;4|O@v+u7{G!X=NvnamVErFeCEvQt)pFSBqF4Kb?POUvDp z#q}g|;-udvIPlB3y(dY#`KEnS3N=R5;H<$=^XhT(n;Gr9{nd1ps3J7lqgunIlZpYA z=feQ{fpivyv0UGNairt%-*BpH-Iw>3H!kz9rE#?HlnTGiG!o#l(PgS6`Y}i^U2;FZ zzy6Q8cX>=bWdol;WWWLds6T!FnN9BWnH1}0?xJe$=Jro^?0@aEGMQLAoTW_J&Kcnj zbzA?@@O2LBW#X4wu?B&Pj@K1*l1h^ck>qtf&}ePH38klS6j>=Jyb-*J3izBk2L>Gy ziM>K}*lji2W=JK%8+<!NntFL1PwpT|8WN9Wr-)=N>=2J0x;l!8wD1@$jn?vtGndt! zTk7sxn?>O;%iGaqn!GP1+2Q#0I`@5YL%7F*_QA|Ly&J*i)}@FYv&gZ<AnS3kIvbSi z(;h_YargE*x0D<o=rbCseY5k`i{JXX8(qs!v!df8MZ9axr^tr;8V@7I2{JTL8qWNJ z6VrGP6>BgW6G71nOIy`=y;!}#ahh2*^DkZLWCzOGa)bYv^K@Jv1uos#DRM|Z$`*{% znZ}qyLE<;z6puNUxb69qG{;4Omso4!^l8L!=Rl6JX*<Le^>Dn0z5ZC=-t0!6y#V4P z+_ZL{7jp-tW#b!u@sihheC<7-GlSP<uhzGs=i_Kr%N)`BoRo6)_f0+e54@sZuku%~ ztcT;Uhgo1Ybq&0Z^Bh;xMV$RVo*;qP9|kK%OggoTX7Qf5HQiJE^SAR9vUtgqkl%t( zQNcxoU}3??NdKb~{QoSJ;|{RUfow=)WYPuhh{k@f5(65*a?DbIH>v@sN4_BIUk<Fo zkq&@C*NA!y2C~L8z*7!zABur)4*J;$2$TK;&yGSnDFIzG`ZjTdW<wsZW{gea=q8|V zWJQ>85jeg8w7wX=trcA>`gTBs)?-3&m*d+Uh;AVI?mC2lCxAm4(4c@Ch}dg~ZVLJc z2*Q*sE3hf(<00q<ptj)<+8G#LIx;Yzx9HGyqgIp1x{tUZ>4sIB=-SZ>H-rJc?qJsw YQ`7}`vjWo~0|O5bssWR6xF?7Q09djxVgLXD literal 0 HcmV?d00001 diff --git a/Example data files/Example_4_plate_layout_XLSX.xlsx b/Example data files/Example_4_plate_layout_XLSX.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..02b3c156612d7905d343205b67bda64ee790e038 GIT binary patch literal 10614 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%0AMHgkp^`U^pm1JXMJ+1NQOUhd=Gj#w$DNDp_dc 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 literal 0 HcmV?d00001 diff --git a/FUNCTIONS.R b/FUNCTIONS.R index 31371cb..e3bdc23 100644 --- a/FUNCTIONS.R +++ b/FUNCTIONS.R @@ -41,7 +41,7 @@ runRelativeAntibodyUnits = function(fname1, fname2, MFI_CSV, MFI_N_ANTIGENS, TEM ## converts the median fluorescence intensity (MFI) values into relative antibody units. ## ## ## ## Input: ## - ## - Luminex-200 or Magpix output file (required) ## + ## - Luminex-200 or Magpix output file or BioPlex raw MFI output file(required) ## ## - Plate layout with bleedcode information (optional) ## ## ## ## Output: ## @@ -56,7 +56,9 @@ runRelativeAntibodyUnits = function(fname1, fname2, MFI_CSV, MFI_N_ANTIGENS, TEM #### READ DATA ########################################################################################################## - # ## The first 41 rows are not relevant and are not imported into R. + ## The first rows are headers not relevant and are quickly discarded into R. + ## The number of rows to discard depends on platform used to generate the input. + if(MFI_CSV){ @@ -101,7 +103,7 @@ runRelativeAntibodyUnits = function(fname1, fname2, MFI_CSV, MFI_N_ANTIGENS, TEM dim(L) # MFI values as numeric - L[,-which(colnames(L) %in% c("Location","Sample","Total Events","TotalEvents"))] = lapply(L[,-which(colnames(L) %in% c("Location","Sample","Total Events"))], as.numeric) + L[,-which(colnames(L) %in% c("Location","Sample","Total Events","TotalEvents"))] = lapply(L[,-which(colnames(L) %in% c("Location","Sample","Total Events","TotalEvents"))], as.numeric) ## Load the counts to check for run quality control C = L_full[(count_row_number+1):(count_row_number+1+nrow(L)),1:(3+as.integer(MFI_N_ANTIGENS))] @@ -133,70 +135,138 @@ runRelativeAntibodyUnits = function(fname1, fname2, MFI_CSV, MFI_N_ANTIGENS, TEM # Read L_full <- as.data.frame(read_excel(fname1)) - # Identify rows - median_row_number <- which(L_full$xPONENT == "Median") - count_row_number <- which(L_full$xPONENT == "Count") - endcount_row_number <- which(L_full$xPONENT == "Avg Net MFI") - - # Load Excel file - L <- as.data.frame(read_excel(fname1, skip = median_row_number+1, col_types = "text")) + # Guess the platform from the header structure + # This should be improved in the future (made into a radio button instead ?) + PLATFORM <- ifelse(any(grepl("MAGPIX", colnames(L_full))), "Magpix", + ifelse(any(grepl("Reader Serial Number", L_full[, 1])), "Bioplex", "Unknown")) - ## Find all blank rows (i.e. rows that are all NA). - ## Then keep rows preceding the first blank row. - blank.row.number <- which(rowSums(is.na(L)) == length(names(L)))[1] - if(is.na(blank.row.number)){ - L = L - }else{ - L <- L[1:(blank.row.number-1),] + ## Magpix data input detected + if (PLATFORM == "Magpix") { + cat("Guessed MFI input file from a Magpix platform") + + # Identify rows + median_row_number <- which(L_full$xPONENT == "Median") + count_row_number <- which(L_full$xPONENT == "Count") + endcount_row_number <- which(L_full$xPONENT == "Avg Net MFI") + + # Load Excel file + L <- as.data.frame(read_excel(fname1, skip = median_row_number+1, col_types = "text")) + + ## Find all blank rows (i.e. rows that are all NA). + ## Then keep rows preceding the first blank row. + blank.row.number <- which(rowSums(is.na(L)) == length(names(L)))[1] + if(is.na(blank.row.number)){ + L = L + }else{ + L <- L[1:(blank.row.number-1),] + } + + ## Exclude column that corresponds to "Total events" + L <- L[, !(colnames(L) %in% c("Total Events","TotalEvents"))] + + # Antigen names clean-up + colnames(L) = gsub("\\..*", "", colnames(L)) + # Remove any values in (parentheses) + colnames(L) = gsub("\\s*\\([^\\)]+\\)","", colnames(L)) + # Remove spaces + colnames(L) = gsub(" ","", colnames(L)) + + dim(L) + + # Change "NaN" to 0s + L <- L %>% mutate_all(funs(gsub("NaN", 0, .))) + + # MFI values as numeric + L[,-which(colnames(L) %in% c("Location","Sample","Total Events","TotalEvents"))] = lapply(L[,-which(colnames(L) %in% c("Location","Sample","Total Events","TotalEvents"))], as.numeric) + + C <- as.data.frame(read_excel(fname1, skip = count_row_number+1, col_types = "text")) + + ## Find all blank rows (i.e. rows that are all NA). + ## Then keep rows preceding the first blank row. + blank.row.number <- which(rowSums(is.na(C)) == length(names(C)))[1] + if(is.na(blank.row.number)){ + C = C + }else{ + C <- C[1:(blank.row.number-1),] + } + + ## Exclude column that corresponds to "Total events" + C <- C[, !(colnames(C) %in% c("Total Events","TotalEvents"))] + + # Antigen names clean-up + colnames(C) = gsub("\\..*", "", colnames(C)) + # Remove any values in (parentheses) + colnames(C) = gsub("\\s*\\([^\\)]+\\)","", colnames(C)) + # Remove spaces + colnames(C) = gsub(" ","", colnames(C)) + dim(C) + + ## Save the MFI values for the blank sample(s) for run quality control + B <- L %>% filter(grepl(c("^B"), Sample, ignore.case = T)) + dim(B) + + ## Save the MFI values for the standards for run quality control + S <- L %>% filter(grepl("^S", Sample, ignore.case = T)) + dim(S) #there should be 10 rows } - ## Exclude column that corresponds to "Total events" - L <- L[, !(colnames(L) %in% c("Total Events","TotalEvents"))] - - # Antigen names clean-up - colnames(L) = gsub("\\..*", "", colnames(L)) - # Remove any values in (parentheses) - colnames(L) = gsub("\\s*\\([^\\)]+\\)","", colnames(L)) - # Remove spaces - colnames(L) = gsub(" ","", colnames(L)) - - dim(L) - - # Change "NaN" to 0s - L <- L %>% mutate_all(funs(gsub("NaN", 0, .))) - - # MFI values as numeric - L[,-which(colnames(L) %in% c("Location","Sample","Total Events","TotalEvents"))] = lapply(L[,-which(colnames(L) %in% c("Location","Sample","Total Events"))], as.numeric) - - C <- as.data.frame(read_excel(fname1, skip = count_row_number+1, col_types = "text")) - - ## Find all blank rows (i.e. rows that are all NA). - ## Then keep rows preceding the first blank row. - blank.row.number <- which(rowSums(is.na(C)) == length(names(C)))[1] - if(is.na(blank.row.number)){ - C = C - }else{ - C <- C[1:(blank.row.number-1),] + ## BioPlex data input detected + else if (PLATFORM == "Bioplex") { + cat("Guessed MFI input file from a BioPlex platform") + + # In BioPlex outputs, MFI and bead counts are in the same cell with format MFI (counts). + # Note that the bead counts may not be present, in that case only the MFI is reported. + + # Identify rows (-1 because we match the header, contrary as one row before in Magpix) + median_row_number <- which(L_full[, 1] == "Well") - 1 + + # Load again Excel file reading only after the 1st row of interest + L <- as.data.frame(read_excel(fname1, skip = median_row_number+1, col_types = "text")) %>% + dplyr::rename(Location = Well, + Sample = Type) %>% + # Pad the Location column to match the syntax used by Luminex outputs, + # so that future regex will work regardless + dplyr::mutate(Location = paste0(1:n(), "(1,", Location, ")")) + + # Remove columns that are not MFI readings or sample code / location + L <- L[, !(colnames(L) %in% c("Region", "Gate", "Total", "% Agg Beads", "Sampling Errors"))] + + # Antigen names clean-up + colnames(L) = gsub("\\..*", "", colnames(L)) + # Remove any values in (parentheses) + colnames(L) = gsub("\\s*\\([^\\)]+\\)","", colnames(L)) + # Remove spaces + colnames(L) = gsub(" ","", colnames(L)) + + dim(L) + + # Change "NaN" to 0s + L <- L %>% mutate_all(funs(gsub("NaN", 0, .))) + + # Bead counts, when available, will be extracted from the table + C <- L %>% + dplyr::mutate(dplyr::across(!c(Location, Sample), gsub, pattern = "^(\\d+(?:\\.\\d+)?)(\\s*\\((\\d+(?:\\.\\d+)?)\\))?$", replacement = "\\3")) %>% + # Set to NA in case bead counts were not available + dplyr::mutate(dplyr::across(!c(Location, Sample), gsub, pattern = "^$", replacement = NA)) + + dim(C) + + # Remove bead counts from L when they have been isolated in C + L <- L %>% + dplyr::mutate(dplyr::across(!c(Location, Sample), gsub, pattern = "^(\\d+(?:\\.\\d+)?)(\\s*\\((\\d+(?:\\.\\d+)?)\\))?$", replacement = "\\1")) + + ## Save the MFI values for the blank sample(s) for run quality control + B <- L %>% filter(grepl(c("^B"), Sample, ignore.case = T)) + dim(B) + + ## Save the MFI values for the standards for run quality control + S <- L %>% filter(grepl("^S", Sample, ignore.case = T)) + dim(S) #there should be 10 rows } - ## Exclude column that corresponds to "Total events" - C <- C[, !(colnames(C) %in% c("Total Events","TotalEvents"))] - - # Antigen names clean-up - colnames(C) = gsub("\\..*", "", colnames(C)) - # Remove any values in (parentheses) - colnames(C) = gsub("\\s*\\([^\\)]+\\)","", colnames(C)) - # Remove spaces - colnames(C) = gsub(" ","", colnames(C)) - dim(C) - - ## Save the MFI values for the blank sample(s) for run quality control - B <- L %>% filter(grepl(c("^B"), Sample, ignore.case = T)) - dim(B) - - ## Save the MFI values for the standards for run quality control - S <- L %>% filter(grepl("^S", Sample, ignore.case = T)) - dim(S) #there should be 10 rows + else { + stop("Execution halted: the platform used to generate the input could not be determined.") + } } @@ -214,9 +284,9 @@ runRelativeAntibodyUnits = function(fname1, fname2, MFI_CSV, MFI_N_ANTIGENS, TEM dplyr::filter(grepl("^\\d+$",count)) %>% dplyr::mutate(count = as.numeric(count), warning = case_when( - count<15~1, - count>=15~0 - )) %>% + count<15~1, + count>=15~0 + )) %>% dplyr::select(Location, warning) %>% dplyr::group_by(Location) %>% dplyr::summarise(sum = sum(warning)) %>% @@ -246,8 +316,8 @@ runRelativeAntibodyUnits = function(fname1, fname2, MFI_CSV, MFI_N_ANTIGENS, TEM ## Protein name list obtained after removing variable names: "Location" and "Sample" proteins <- names(L[, -c(1:2)]); proteins - ## Add new variable to data frame to indicate the first letter of sample type ("B", "C", "S", "U","N") - ## "B" = blank, "C"=control, "S"=standard (dilution of the pool), "U"=sample, "N"=negative control + ## Add new variable to data frame to indicate the first letter of sample type ("B", "C", "S", "U" or "X","N") + ## "B" = blank, "C"=control, "S"=standard (dilution of the pool), "U" or "X"=sample, "N"=negative control L$type.letter <- substr(L$Sample, start=1, stop=1) dilution <- c(1/50, 1/100, 1/200, 1/400, 1/800, 1/1600, 1/3200, 1/6400, 1/12800, 1/25600) dilution.scaled <- dilution*25600; dilution.scaled @@ -303,7 +373,7 @@ runRelativeAntibodyUnits = function(fname1, fname2, MFI_CSV, MFI_N_ANTIGENS, TEM for (r in 1:nrow(L)){ results <- NULL - if (L$type.letter[r]=="U"){ + if (L$type.letter[r] %in% c("U", "X")){ mfi.X <- as.numeric(L[r, i]) y <- log(mfi.X) @@ -367,7 +437,7 @@ runRelativeAntibodyUnits = function(fname1, fname2, MFI_CSV, MFI_N_ANTIGENS, TEM # Make all columns after 1st 4 numeric results.df.wide[,5:ncol(results.df.wide)] = lapply(results.df.wide[,5:ncol(results.df.wide)], as.character) results.df.wide[,5:ncol(results.df.wide)] = lapply(results.df.wide[,5:ncol(results.df.wide)], as.numeric) - + ########################################################################################################## #### OUTPUT @@ -428,7 +498,7 @@ getRelativeAntibodyUnits = function(RAW_MFI_FILE_NAME, RAW_MFI_FILE_PATH, cat("******************Running RAU function******************\n") - # Get Luminex-MagPix full file path + # Get Luminex-MagPix-BioPlex full file path fname1 = RAW_MFI_FILE_PATH # Extract working directly of this file @@ -461,46 +531,53 @@ getRelativeAntibodyUnits = function(RAW_MFI_FILE_NAME, RAW_MFI_FILE_PATH, bead_counts = results[[4]] MFI_RAU_results = results[[5]] model_results = results[[6]] + plot_stdcurve <- plot_counts <- plot_blank <- plots_model <- all_model_plots <- NULL ## Plot standard curve raw MFI - plot_stdcurve <- std_curve %>% - dplyr::select(-Location) %>% - dplyr::mutate(Sample = c("S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S10"), - Sample_dilution = factor(paste0(Sample,": ",dilution_plot),levels=paste0(Sample,": ",dilution_plot))) %>% - tidyr::pivot_longer(-c(Sample,dilution,dilution_plot,Sample_dilution), names_to = "protein", values_to = "MFI") %>% - # mutate(Sample = factor(Sample, c("S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S10"))) %>% - dplyr::mutate(MFI = as.numeric(MFI)) %>% - ggplot(aes(x = Sample_dilution, y = MFI, color = protein, group = protein)) + - geom_point() + - geom_line() + - scale_y_log10(breaks = c(0, 10, 100, 1000, 10000)) + - labs(x = "standard curve", - y = "log(MFI)") + - facet_wrap(~protein) + - theme_bw() + - theme(axis.text.x = element_text(angle = 90, hjust = 1)) + if (nrow(std_curve) > 0) { + plot_stdcurve <- std_curve %>% + dplyr::select(-Location) %>% + dplyr::mutate(Sample = c("S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S10"), + Sample_dilution = factor(paste0(Sample,": ",dilution_plot),levels=paste0(Sample,": ",dilution_plot))) %>% + tidyr::pivot_longer(-c(Sample,dilution,dilution_plot,Sample_dilution), names_to = "protein", values_to = "MFI") %>% + # mutate(Sample = factor(Sample, c("S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S10"))) %>% + dplyr::mutate(MFI = as.numeric(MFI)) %>% + ggplot(aes(x = Sample_dilution, y = MFI, color = protein, group = protein)) + + geom_point() + + geom_line() + + scale_y_log10(breaks = c(0, 10, 100, 1000, 10000)) + + labs(x = "standard curve", + y = "log(MFI)") + + facet_wrap(~protein) + + theme_bw() + + theme(axis.text.x = element_text(angle = 90, hjust = 1)) + } ## Plot plate counts - plot_counts <- bead_counts %>% - ggplot(mapping = aes(x = col, y = fct_rev(row), fill = colour), fill = summary)+ - geom_tile(aes(height = 0.90, width = 0.90)) + - scale_x_continuous(breaks = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))+ - scale_fill_manual(values = c("sufficient beads" = "#91bfdb", "repeat" = "#d73027"))+ - theme_linedraw()+ - labs(x = "columns", y = "rows", fill = "") + if (nrow(bead_counts) > 0) { + plot_counts <- bead_counts %>% + ggplot(mapping = aes(x = col, y = fct_rev(row), fill = colour), fill = summary)+ + geom_tile(aes(height = 0.90, width = 0.90)) + + scale_x_continuous(breaks = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))+ + scale_fill_manual(values = c("sufficient beads" = "#91bfdb", "repeat" = "#d73027"))+ + theme_linedraw()+ + labs(x = "columns", y = "rows", fill = "") + } ## Plot blank sample MFI for each protein - if there is more than one blank sample label as "Blank1", "Blank2" etc - plot_blank <- blank_MFI %>% - dplyr::select(-Location) %>% - dplyr::mutate(Sample = paste0(Sample,1:n())) %>% - tidyr::pivot_longer(-Sample, names_to = "protein", values_to = "MFI") %>% - ggplot(aes(x = factor(protein), y = as.numeric(MFI), fill = Sample)) + - geom_bar(stat = "identity", position = "dodge") + - geom_hline(yintercept = 50, linetype = "dashed", color = "grey") + - labs(x = "protein", - y = "MFI") + - theme_linedraw() + - theme(axis.text.x = element_text(angle = 90, hjust = 1)) + if (nrow(blank_MFI) > 0) { + plot_blank <- blank_MFI %>% + dplyr::select(-Location) %>% + dplyr::mutate(Sample = paste0(Sample,1:n())) %>% + tidyr::pivot_longer(-Sample, names_to = "protein", values_to = "MFI") %>% + ggplot(aes(x = factor(protein), y = as.numeric(MFI), fill = Sample)) + + geom_bar(stat = "identity", position = "dodge") + + geom_hline(yintercept = 50, linetype = "dashed", color = "grey") + + labs(x = "protein", + y = "MFI") + + theme_linedraw() + + theme(axis.text.x = element_text(angle = 90, hjust = 1)) + } ## Plot model curves plots_model <- lapply(seq_along(model_results), function(x){ @@ -526,32 +603,40 @@ getRelativeAntibodyUnits = function(RAW_MFI_FILE_NAME, RAW_MFI_FILE_PATH, # saveRDS(std_curve, file = paste0(EXP_DIR, "STD_CURVES.rds")) # Save PDF of std curve plots - ggsave(plot_stdcurve, - filename = paste0(EXP_DIR,"STD_CURVES_PLOT.pdf"), - height = 8, - width = 12, - units = "in") + if (!is.null(plot_stdcurve)) { + ggsave(plot_stdcurve, + filename = paste0(EXP_DIR,"STD_CURVES_PLOT.pdf"), + height = 8, + width = 12, + units = "in") + } # Save PDF of bead count plot - ggsave(plot_counts, - filename = paste0(EXP_DIR,"PLATE_BEADS_COUNT_PLOT.pdf"), - height = 8, - width = 12, - units = "in") + if(!is.null(plot_counts)) { + ggsave(plot_counts, + filename = paste0(EXP_DIR,"PLATE_BEADS_COUNT_PLOT.pdf"), + height = 8, + width = 12, + units = "in") + } # Save PDF ofblank sample QC plot - ggsave(plot_blank, - filename = paste0(EXP_DIR,"BLANK_SAMPLE_PLOT.pdf"), - height = 4, - width = 6, - units = "in") + if (!is.null(plot_blank)) { + ggsave(plot_blank, + filename = paste0(EXP_DIR,"BLANK_SAMPLE_PLOT.pdf"), + height = 4, + width = 6, + units = "in") + } # Save PDF of model plots - ggsave(all_model_plots, - filename = paste0(EXP_DIR,"MODEL_PLOTS.pdf"), - height = 8.27, - width = 11.69, - units = "in") + if (!is.null(all_model_plots)) { + ggsave(all_model_plots, + filename = paste0(EXP_DIR,"MODEL_PLOTS.pdf"), + height = 8.27, + width = 11.69, + units = "in") + } ## Write to file write.csv(results.df.wide, @@ -625,25 +710,25 @@ getAntigenNames = function(RAU_Dilution, ANTIGEN_FILE_PATH, ANTIGEN_CSV){ ################# Function 4: Random forest serology analysis getSeropositiveResults_RF = function(PATHWAY_1, - RAU_user_id_columns, RAU_user_RAU_columns, - RAU_RESULTS, - ANTIGEN_FILE_PATH, ANTIGEN_CSV, - CHECK_NAME, CHECK_ID, ID, DATE, - EXP_DIR, - MODEL_W16, - MODEL_W16_3_TARGETS, - MODEL_W16_EQUAL_TARGET, - MODEL_W16_HIGH_SP_TARGET, - MODEL_W16_HIGH_SE_TARGET, - MODEL_W16_OTHER_SE, - MODEL_W16_OTHER_SP, - MODEL_W47, - MODEL_W47_3_TARGETS, - MODEL_W47_EQUAL_TARGET, - MODEL_W47_HIGH_SP_TARGET, - MODEL_W47_HIGH_SE_TARGET, - MODEL_W47_OTHER_SE, - MODEL_W47_OTHER_SP){ + RAU_user_id_columns, RAU_user_RAU_columns, + RAU_RESULTS, + ANTIGEN_FILE_PATH, ANTIGEN_CSV, + CHECK_NAME, CHECK_ID, ID, DATE, + EXP_DIR, + MODEL_W16, + MODEL_W16_3_TARGETS, + MODEL_W16_EQUAL_TARGET, + MODEL_W16_HIGH_SP_TARGET, + MODEL_W16_HIGH_SE_TARGET, + MODEL_W16_OTHER_SE, + MODEL_W16_OTHER_SP, + MODEL_W47, + MODEL_W47_3_TARGETS, + MODEL_W47_EQUAL_TARGET, + MODEL_W47_HIGH_SP_TARGET, + MODEL_W47_HIGH_SE_TARGET, + MODEL_W47_OTHER_SE, + MODEL_W47_OTHER_SP){ ############################################################################################## ## ## @@ -1164,25 +1249,25 @@ getSeropositiveResults_RF = function(PATHWAY_1, ################# Function 5: SVM serology analysis getSeropositiveResults_SVM = function(PATHWAY_1, - RAU_user_id_columns, RAU_user_RAU_columns, - RAU_RESULTS, - ANTIGEN_FILE_PATH, ANTIGEN_CSV, - CHECK_NAME, CHECK_ID, ID, DATE, - EXP_DIR, - MODEL_W16, - MODEL_W16_3_TARGETS, - MODEL_W16_EQUAL_TARGET, - MODEL_W16_HIGH_SP_TARGET, - MODEL_W16_HIGH_SE_TARGET, - MODEL_W16_OTHER_SE, - MODEL_W16_OTHER_SP, - MODEL_W47, - MODEL_W47_3_TARGETS, - MODEL_W47_EQUAL_TARGET, - MODEL_W47_HIGH_SP_TARGET, - MODEL_W47_HIGH_SE_TARGET, - MODEL_W47_OTHER_SE, - MODEL_W47_OTHER_SP){ + RAU_user_id_columns, RAU_user_RAU_columns, + RAU_RESULTS, + ANTIGEN_FILE_PATH, ANTIGEN_CSV, + CHECK_NAME, CHECK_ID, ID, DATE, + EXP_DIR, + MODEL_W16, + MODEL_W16_3_TARGETS, + MODEL_W16_EQUAL_TARGET, + MODEL_W16_HIGH_SP_TARGET, + MODEL_W16_HIGH_SE_TARGET, + MODEL_W16_OTHER_SE, + MODEL_W16_OTHER_SP, + MODEL_W47, + MODEL_W47_3_TARGETS, + MODEL_W47_EQUAL_TARGET, + MODEL_W47_HIGH_SP_TARGET, + MODEL_W47_HIGH_SE_TARGET, + MODEL_W47_OTHER_SE, + MODEL_W47_OTHER_SP){ ############################################################################################## ## ## @@ -1370,30 +1455,30 @@ getSeropositiveResults_SVM = function(PATHWAY_1, # Create binary seropositivity value if(PATHWAY_1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:4], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_79SE_79SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative"), - SEROPOSITIVE_63SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative"), - SEROPOSITIVE_90SE_59SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_79SE_79SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative"), + SEROPOSITIVE_63SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative"), + SEROPOSITIVE_90SE_59SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) }else{ if(RAU_user_id_columns == 1){ col_name = colnames(RAU_RESULTS)[1] SVM_SEROPOS = as.data.frame(cbind(as.character(RAU_RESULTS[,colnames(RAU_RESULTS)[1]]), - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_79SE_79SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative"), - SEROPOSITIVE_63SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative"), - SEROPOSITIVE_90SE_59SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_79SE_79SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative"), + SEROPOSITIVE_63SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative"), + SEROPOSITIVE_90SE_59SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) colnames(SVM_SEROPOS) = c(col_name, colnames(SVM_SEROPOS[2:ncol(SVM_SEROPOS)])) } if(RAU_user_id_columns > 1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:as.integer(RAU_user_id_columns)], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_79SE_79SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative"), - SEROPOSITIVE_63SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative"), - SEROPOSITIVE_90SE_59SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_79SE_79SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative"), + SEROPOSITIVE_63SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative"), + SEROPOSITIVE_90SE_59SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) } } @@ -1405,30 +1490,30 @@ getSeropositiveResults_SVM = function(PATHWAY_1, # Create binary seropositivity value if(PATHWAY_1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:4], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_80SE_80SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative"), - SEROPOSITIVE_64SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative"), - SEROPOSITIVE_90SE_60SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_80SE_80SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative"), + SEROPOSITIVE_64SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative"), + SEROPOSITIVE_90SE_60SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) }else{ if(RAU_user_id_columns == 1){ col_name = colnames(RAU_RESULTS)[1] SVM_SEROPOS = as.data.frame(cbind(as.character(RAU_RESULTS[,colnames(RAU_RESULTS)[1]]), - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_80SE_80SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative"), - SEROPOSITIVE_64SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative"), - SEROPOSITIVE_90SE_60SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_80SE_80SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative"), + SEROPOSITIVE_64SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative"), + SEROPOSITIVE_90SE_60SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) colnames(SVM_SEROPOS) = c(col_name, colnames(SVM_SEROPOS[2:ncol(SVM_SEROPOS)])) } if(RAU_user_id_columns > 1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:as.integer(RAU_user_id_columns)], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_80SE_80SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative"), - SEROPOSITIVE_64SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative"), - SEROPOSITIVE_90SE_60SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_80SE_80SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative"), + SEROPOSITIVE_64SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative"), + SEROPOSITIVE_90SE_60SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) } } } @@ -1443,24 +1528,24 @@ getSeropositiveResults_SVM = function(PATHWAY_1, # Create binary seropositivity value if(PATHWAY_1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:4], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_79SE_79SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_79SE_79SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative")), stringsAsFactors = F) }else{ if(RAU_user_id_columns == 1){ col_name = colnames(RAU_RESULTS)[1] SVM_SEROPOS = as.data.frame(cbind(as.character(RAU_RESULTS[,colnames(RAU_RESULTS)[1]]), - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_79SE_79SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_79SE_79SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative")), stringsAsFactors = F) colnames(SVM_SEROPOS) = c(col_name, colnames(SVM_SEROPOS[2:ncol(SVM_SEROPOS)])) } if(RAU_user_id_columns > 1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:as.integer(RAU_user_id_columns)], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_79SE_79SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_79SE_79SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative")), stringsAsFactors = F) } } @@ -1472,24 +1557,24 @@ getSeropositiveResults_SVM = function(PATHWAY_1, # Create binary seropositivity value if(PATHWAY_1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:4], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_80SE_80SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_80SE_80SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative")), stringsAsFactors = F) }else{ if(RAU_user_id_columns == 1){ col_name = colnames(RAU_RESULTS)[1] SVM_SEROPOS = as.data.frame(cbind(as.character(RAU_RESULTS[,colnames(RAU_RESULTS)[1]]), - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_80SE_80SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_80SE_80SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative")), stringsAsFactors = F) colnames(SVM_SEROPOS) = c(col_name, colnames(SVM_SEROPOS[2:ncol(SVM_SEROPOS)])) } if(RAU_user_id_columns > 1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:as.integer(RAU_user_id_columns)], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_80SE_80SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_80SE_80SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_1, "Positive", "Negative")), stringsAsFactors = F) } } } @@ -1504,24 +1589,24 @@ getSeropositiveResults_SVM = function(PATHWAY_1, # Create binary seropositivity value if(PATHWAY_1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:4], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_63SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_63SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative")), stringsAsFactors = F) }else{ if(RAU_user_id_columns == 1){ col_name = colnames(RAU_RESULTS)[1] SVM_SEROPOS = as.data.frame(cbind(as.character(RAU_RESULTS[,colnames(RAU_RESULTS)[1]]), - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_63SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_63SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative")), stringsAsFactors = F) colnames(SVM_SEROPOS) = c(col_name, colnames(SVM_SEROPOS[2:ncol(SVM_SEROPOS)])) } if(RAU_user_id_columns > 1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:as.integer(RAU_user_id_columns)], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_63SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_63SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative")), stringsAsFactors = F) } } @@ -1533,24 +1618,24 @@ getSeropositiveResults_SVM = function(PATHWAY_1, # Create binary seropositivity value if(PATHWAY_1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:4], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_64SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_64SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative")), stringsAsFactors = F) }else{ if(RAU_user_id_columns == 1){ col_name = colnames(RAU_RESULTS)[1] SVM_SEROPOS = as.data.frame(cbind(as.character(RAU_RESULTS[,colnames(RAU_RESULTS)[1]]), - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_64SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_64SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative")), stringsAsFactors = F) colnames(SVM_SEROPOS) = c(col_name, colnames(SVM_SEROPOS[2:ncol(SVM_SEROPOS)])) } if(RAU_user_id_columns > 1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:as.integer(RAU_user_id_columns)], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_64SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_64SE_90SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_2, "Positive", "Negative")), stringsAsFactors = F) } } } @@ -1565,24 +1650,24 @@ getSeropositiveResults_SVM = function(PATHWAY_1, # Create binary seropositivity value if(PATHWAY_1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:4], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_90SE_59SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_90SE_59SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) }else{ if(RAU_user_id_columns == 1){ col_name = colnames(RAU_RESULTS)[1] SVM_SEROPOS = as.data.frame(cbind(as.character(RAU_RESULTS[,colnames(RAU_RESULTS)[1]]), - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_90SE_59SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_90SE_59SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) colnames(SVM_SEROPOS) = c(col_name, colnames(SVM_SEROPOS[2:ncol(SVM_SEROPOS)])) } if(RAU_user_id_columns > 1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:as.integer(RAU_user_id_columns)], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_90SE_59SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_90SE_59SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) } } @@ -1594,24 +1679,24 @@ getSeropositiveResults_SVM = function(PATHWAY_1, # Create binary seropositivity value if(PATHWAY_1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:4], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_90SE_60SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_90SE_60SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) }else{ if(RAU_user_id_columns == 1){ col_name = colnames(RAU_RESULTS)[1] SVM_SEROPOS = as.data.frame(cbind(as.character(RAU_RESULTS[,colnames(RAU_RESULTS)[1]]), - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_90SE_60SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_90SE_60SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) colnames(SVM_SEROPOS) = c(col_name, colnames(SVM_SEROPOS[2:ncol(SVM_SEROPOS)])) } if(RAU_user_id_columns > 1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:as.integer(RAU_user_id_columns)], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - SEROPOSITIVE_90SE_60SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + SEROPOSITIVE_90SE_60SP=ifelse(SVM_MODEL_VOTES <= CUTOFF_3, "Positive", "Negative")), stringsAsFactors = F) } } } @@ -1626,27 +1711,27 @@ getSeropositiveResults_SVM = function(PATHWAY_1, # Create binary seropositivity value if(PATHWAY_1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:4], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - assign(paste0("SEROPOSITIVE_",as.character(round(as.numeric(SE_Other)*100,digits = 0)), "SE_",as.character(round(as.numeric(SP_Other)*100,digits = 0)),"SP"),ifelse(SVM_MODEL_VOTES <= CUTOFF_4, "Positive", "Negative"))), - stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + assign(paste0("SEROPOSITIVE_",as.character(round(as.numeric(SE_Other)*100,digits = 0)), "SE_",as.character(round(as.numeric(SP_Other)*100,digits = 0)),"SP"),ifelse(SVM_MODEL_VOTES <= CUTOFF_4, "Positive", "Negative"))), + stringsAsFactors = F) }else{ if(RAU_user_id_columns == 1){ col_name = colnames(RAU_RESULTS)[1] SVM_SEROPOS = as.data.frame(cbind(as.character(RAU_RESULTS[,colnames(RAU_RESULTS)[1]]), - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - assign(paste0("SEROPOSITIVE_",as.character(round(as.numeric(SE_Other)*100,digits = 0)), "SE_",as.character(round(as.numeric(SP_Other)*100,digits = 0)),"SP"),ifelse(SVM_MODEL_VOTES <= CUTOFF_4, "Positive", "Negative"))), - stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + assign(paste0("SEROPOSITIVE_",as.character(round(as.numeric(SE_Other)*100,digits = 0)), "SE_",as.character(round(as.numeric(SP_Other)*100,digits = 0)),"SP"),ifelse(SVM_MODEL_VOTES <= CUTOFF_4, "Positive", "Negative"))), + stringsAsFactors = F) colnames(SVM_SEROPOS) = c(col_name, colnames(SVM_SEROPOS[2:ncol(SVM_SEROPOS)])) } if(RAU_user_id_columns > 1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:as.integer(RAU_user_id_columns)], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - assign(paste0("SEROPOSITIVE_",as.character(round(as.numeric(SE_Other)*100,digits = 0)), "SE_",as.character(round(as.numeric(SP_Other)*100,digits = 0)),"SP"),ifelse(SVM_MODEL_VOTES <= CUTOFF_4, "Positive", "Negative"))), - stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + assign(paste0("SEROPOSITIVE_",as.character(round(as.numeric(SE_Other)*100,digits = 0)), "SE_",as.character(round(as.numeric(SP_Other)*100,digits = 0)),"SP"),ifelse(SVM_MODEL_VOTES <= CUTOFF_4, "Positive", "Negative"))), + stringsAsFactors = F) } } } @@ -1657,27 +1742,27 @@ getSeropositiveResults_SVM = function(PATHWAY_1, # Create binary seropositivity value if(PATHWAY_1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:4], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - assign(paste0("SEROPOSITIVE_",as.character(round(as.numeric(SE_Other)*100,digits = 0)), "SE_",as.character(round(as.numeric(SP_Other)*100,digits = 0)),"SP"),ifelse(SVM_MODEL_VOTES <= CUTOFF_4, "Positive", "Negative"))), - stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + assign(paste0("SEROPOSITIVE_",as.character(round(as.numeric(SE_Other)*100,digits = 0)), "SE_",as.character(round(as.numeric(SP_Other)*100,digits = 0)),"SP"),ifelse(SVM_MODEL_VOTES <= CUTOFF_4, "Positive", "Negative"))), + stringsAsFactors = F) }else{ if(RAU_user_id_columns == 1){ col_name = colnames(RAU_RESULTS)[1] SVM_SEROPOS = as.data.frame(cbind(as.character(RAU_RESULTS[,colnames(RAU_RESULTS)[1]]), - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - assign(paste0("SEROPOSITIVE_",as.character(round(as.numeric(SE_Other)*100,digits = 0)), "SE_",as.character(round(as.numeric(SP_Other)*100,digits = 0)),"SP"),ifelse(SVM_MODEL_VOTES <= CUTOFF_4, "Positive", "Negative"))), - stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + assign(paste0("SEROPOSITIVE_",as.character(round(as.numeric(SE_Other)*100,digits = 0)), "SE_",as.character(round(as.numeric(SP_Other)*100,digits = 0)),"SP"),ifelse(SVM_MODEL_VOTES <= CUTOFF_4, "Positive", "Negative"))), + stringsAsFactors = F) colnames(SVM_SEROPOS) = c(col_name, colnames(SVM_SEROPOS[2:ncol(SVM_SEROPOS)])) } if(RAU_user_id_columns > 1){ SVM_SEROPOS = as.data.frame(cbind(RAU_RESULTS[,1:as.integer(RAU_user_id_columns)], - exp(RAU_Dilution_Subset), - SVM_MODEL_VOTES, - assign(paste0("SEROPOSITIVE_",as.character(round(as.numeric(SE_Other)*100,digits = 0)), "SE_",as.character(round(as.numeric(SP_Other)*100,digits = 0)),"SP"),ifelse(SVM_MODEL_VOTES <= CUTOFF_4, "Positive", "Negative"))), - stringsAsFactors = F) + exp(RAU_Dilution_Subset), + SVM_MODEL_VOTES, + assign(paste0("SEROPOSITIVE_",as.character(round(as.numeric(SE_Other)*100,digits = 0)), "SE_",as.character(round(as.numeric(SP_Other)*100,digits = 0)),"SP"),ifelse(SVM_MODEL_VOTES <= CUTOFF_4, "Positive", "Negative"))), + stringsAsFactors = F) } } } diff --git a/SHINY_APP.R b/SHINY_APP.R index 0a3a99c..53f5381 100644 --- a/SHINY_APP.R +++ b/SHINY_APP.R @@ -56,7 +56,7 @@ ui <- fluidPage( p("The Pv SeroTAT Tool has been developed by Ivo Mueller's research groups at the Institut Pasteur and Walter and Eliza Hall Institut of Medical Research. The tool is a patented diagnostic test based on validated serological markers of recent",tags$em("Plasmodium vivax")," infections and hypnozoite carriage. The tool uses machine learning classification algorithms (Random Forest (RF) and Support Vector Machine (SVM)) to predict sero-positivity with varying performance."), p("Validation of this tool using Random Forest can be found in the", a("Nature Medicine Publication", href="https://www.nature.com/articles/s41591-020-0841-4")), p("Please contact on how to cite this tool in publications."), - p(em("Follow steps 1 - 6 to process Luminex MFI or Relative Antibody Unit (RAU) data and run the diagnostic test.")), + p(em("Follow steps 1 - 6 to process Luminex/BioPlex MFI or Relative Antibody Unit (RAU) data and run the diagnostic test.")), hr()), tabPanel("Biomarkers of exposure", br(), @@ -216,7 +216,7 @@ ui <- fluidPage( fileInput("MFI_file", h5("4.1 Load raw MFI file (required)"), accept = c(".csv", ".xlsx")), - helpText("Note: a (.csv) or (.xlsx) file of raw outputs of the Luminex-MagPix machine. + helpText("Note: a (.csv) or (.xlsx) file of raw outputs of the Luminex-MagPix or BioPlex machine. If you encounter errors when loading a (.csv) file, convert it to (.xlsx) in Excel and load the (.xlsx) file instead.")), conditionalPanel("input.radio_data == 1", textInput("MFI_num_antigens", -- GitLab