GGally/0000755000176200001440000000000014064014052011425 5ustar liggesusersGGally/NAMESPACE0000644000176200001440000000775514063646352012677 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method("+",gg) S3method("[",ggmatrix) S3method("[",glyphplot) S3method("[<-",ggmatrix) S3method(grid.draw,ggmatrix) S3method(print,ggmatrix) S3method(print,glyphplot) S3method(print,legend_guide_box) S3method(str,ggmatrix) export("%>%") export(StatCross) export(StatGGallyCount) export(StatProp) export(StatWeightedMean) export(add_ref_boxes) export(add_ref_lines) export(add_to_ggmatrix) export(brew_colors) export(broomify) export(eval_data_col) export(fn_switch) export(geom_stripped_cols) export(geom_stripped_rows) export(getPlot) export(ggally_autopoint) export(ggally_autopointDiag) export(ggally_barDiag) export(ggally_blank) export(ggally_blankDiag) export(ggally_box) export(ggally_box_no_facet) export(ggally_colbar) export(ggally_cor) export(ggally_cor_v1_5) export(ggally_count) export(ggally_countDiag) export(ggally_cross) export(ggally_crosstable) export(ggally_density) export(ggally_densityDiag) export(ggally_denstrip) export(ggally_diagAxis) export(ggally_dot) export(ggally_dot_and_box) export(ggally_dot_no_facet) export(ggally_facetbar) export(ggally_facetdensity) export(ggally_facetdensitystrip) export(ggally_facethist) export(ggally_na) export(ggally_naDiag) export(ggally_nostic_cooksd) export(ggally_nostic_hat) export(ggally_nostic_resid) export(ggally_nostic_se_fit) export(ggally_nostic_sigma) export(ggally_nostic_std_resid) export(ggally_points) export(ggally_ratio) export(ggally_rowbar) export(ggally_smooth) export(ggally_smooth_lm) export(ggally_smooth_loess) export(ggally_statistic) export(ggally_summarise_by) export(ggally_table) export(ggally_tableDiag) export(ggally_text) export(ggally_trends) export(ggbivariate) export(ggcoef) export(ggcoef_compare) export(ggcoef_model) export(ggcoef_multinom) export(ggcoef_plot) export(ggcorr) export(ggduo) export(ggfacet) export(gglegend) export(ggmatrix) export(ggmatrix_gtable) export(ggmatrix_location) export(ggmatrix_progress) export(ggnet) export(ggnet2) export(ggnetworkmap) export(ggnostic) export(ggpairs) export(ggparcoord) export(ggscatmat) export(ggsurv) export(ggtable) export(ggts) export(glyphplot) export(glyphs) export(grab_legend) export(is.glyphplot) export(is_character_column) export(is_horizontal) export(lowertriangle) export(mapping_color_to_fill) export(mapping_string) export(mapping_swap_x_y) export(max1) export(mean0) export(min0) export(model_beta_label) export(model_beta_variables) export(model_response_variables) export(print_if_interactive) export(putPlot) export(range01) export(remove_color_unless_equal) export(rescale01) export(rescale11) export(scatmat) export(signif_stars) export(stat_cross) export(stat_ggally_count) export(stat_prop) export(stat_weighted_mean) export(uppertriangle) export(v1_ggmatrix_theme) export(vig_ggally) export(weighted_mean_sd) export(weighted_median_iqr) export(wrap) export(wrap_fn_with_param_arg) export(wrap_fn_with_params) export(wrapp) import(RColorBrewer) import(ggplot2) import(plyr) import(utils) importFrom(dplyr,"%>%") importFrom(grDevices,colorRampPalette) importFrom(grDevices,gray.colors) importFrom(grid,gpar) importFrom(grid,grid.draw) importFrom(grid,grid.layout) importFrom(grid,grid.newpage) importFrom(grid,grid.rect) importFrom(grid,grid.text) importFrom(grid,popViewport) importFrom(grid,pushViewport) importFrom(grid,seekViewport) importFrom(grid,upViewport) importFrom(grid,viewport) importFrom(gtable,gtable_filter) importFrom(lifecycle,deprecate_soft) importFrom(reshape,melt) importFrom(reshape,melt.data.frame) importFrom(reshape,melt.default) importFrom(rlang,"%||%") importFrom(stats,anova) importFrom(stats,complete.cases) importFrom(stats,cor) importFrom(stats,lm) importFrom(stats,mad) importFrom(stats,median) importFrom(stats,na.omit) importFrom(stats,pf) importFrom(stats,qnorm) importFrom(stats,quantile) importFrom(stats,sd) importFrom(stats,spline) importFrom(stats,symnum) importFrom(stats,terms) importFrom(stats,time) importFrom(utils,capture.output) importFrom(utils,head) importFrom(utils,installed.packages) importFrom(utils,str) GGally/README.md0000644000176200001440000000305713777103031012717 0ustar liggesusers# [GGally](http://ggobi.github.io/ggally/): Extension to [ggplot2](https://ggplot2.tidyverse.org/) [![R build status](https://github.com/ggobi/ggally/workflows/R-CMD-check/badge.svg)](https://github.com/ggobi/ggally/actions) [![codecov.io](https://codecov.io/github/ggobi/ggally/coverage.svg?branch=master)](https://codecov.io/github/ggobi/ggally?branch=master) [![CRAN_Status_Badge](http://www.r-pkg.org/badges/version/GGally)](https://cran.r-project.org/package=GGally) [![](http://cranlogs.r-pkg.org/badges/GGally)](https://cran.r-project.org/package=GGally) [![DOI](https://zenodo.org/badge/22529/ggobi/ggally.svg)](https://zenodo.org/badge/latestdoi/22529/ggobi/ggally) [![RStudio community](https://img.shields.io/badge/community-GGally-blue?style=social&logo=rstudio&logoColor=75AADB)](https://community.rstudio.com/tags/c/general/17/ggally) [`ggplot2`](https://ggplot2.tidyverse.org/) is a plotting system for R based on the grammar of graphics. [`GGally`](https://ggobi.github.io/ggally/) extends ggplot2 by adding several functions to reduce the complexity of combining geoms with transformed data. Some of these functions include a pairwise plot matrix, a scatterplot plot matrix, a parallel coordinates plot, a survival plot, and several functions to plot networks. ## Installation To install this package from GitHub or [CRAN](https://cran.r-project.org/package=GGally), do the following from the R console: ```r # Github library(devtools) install_github("ggobi/ggally") ``` ```r # CRAN install.packages("GGally") ``` GGally/data/0000755000176200001440000000000013663637143012356 5ustar liggesusersGGally/data/happy.rda0000644000176200001440000042646013663637143014203 0ustar liggesusers7zXZi"6!Xh])TW"nRʟ[^ nt9 >O=QEĕ .jzrOR *F ogװh;RX vÉD6t[bIDRm( ŋ +H(Ĥhj>9" > l$@  'n:t(VPiBl'CrOY2ESP p9sn~%&Eh6&$%NM q"j|?-%(8&Ӳ;o~;g_Qφ_}B5/@DKnKsZMBWjO9N# }-r)S_z-;Y|"0˟~OJ`mH .V ̽[gUZ:PvwT.ZyO/Xdqo뻙#/KxAG{ T&.]+s{X ;jMSs#ΦLօ̉e; /o/ *>S Njejavg6k;Vl@t/dH d։3B"w1= ˵֬jsQ%L[3ʨqE$,B(\h0㵌N%Ia=mtUT>\<")QC^7حzɱ{ab1Nhutu[ ^E(f6亄 `Uf+a{V .&XYCgIkyT3Gݘ  $wy)w-kJIoDς]v{֭GYzpZ%2j1MdIX qet@%?1i?z Ԥ,NG ~rPs}pOEu蘨ZdSCyo%nKڰ|*ѨG1J4./zBkGFs~ h1Kf^cؑh~&'~/ā/+GimG)8UHM)k F/& 2tXoD"v|Ҋ$[Н>=w'<Hɍ$SN+a "pPkBZe28P_=C凒Hg}D{o2<P3)opuާ} 9 ;nPu:QnG"Z'JN?,(hPfʃna;"^aI xjrRr6iph>^ᡯ"fp*Kd'ܣɋVݍ-U#'6ĤVt.?`P:Fst`[? ϻ#ŷa .[ʀvq"SPE;^ѹPMĦ~4`'qD,HW(~!/[OBJQ=:Ue;>Ax'z&XRhkw6bL )1HabzHo.ORc^XZQrl !]T^'!ڌޢVFB5 `㘰e&_l%̪&r2y:4|I :wy&tytzijGn;+<3;Ե$DT(-|p=t?kt|I"h\Ih긖a}Z+SR6KRQ󷁭 QWz0d֐C@@Xu:.F q`p|SY]ba W!crI_A [r`*)VQD~)t1ь@m pNN)B^%lP5˓`D$Yx(1 6*/ZA_C9@"t2hkH0X"rx@"ރ>07m[ԩcw@oxjijuL;]azm#Q\ gzmy :^J3Wwx7xQ}[\k KS5b $HJ=3 r'$@@\e]p$9YN`iFv2Oܯ)>0r+&{;|+kXH#']?^*mJpe986FSG>{S6#E\#6 WG+^=w1r 6)Ի*N ߭`;0?1[BֈƂfJ#SMA9/Œ5=NJ qx>)o uj9\B6բ`|l}߬X6?J8Dag>:cb:%lye&SU zQt*VzfC}$WzèJ2u?)Uj| `S-.T،ܱ:gUH{, <%zpFMUs[\T$D-)f;z홼\^v>zLqnPO90 w׸K fX;PrnW^^=uϳ2Yn rMThA=S+YWK{RL` SZ?(4d:کk(GH1"iƐfo1xN&}hz|*۪ҏ%*-aW!%̪%X<-?{r=ҕn5 r>^ח Q󾻑3ZNFy$H>-;ױ.PKb&DCz}kFP5߀>C 3Si0UkSL,*Q5 rŊ(KAmb0`i>/<`/[׶Hoݒ,L2(+OH qbM=z+1wJXJ0B>B樣Np:~0X1ޙ'Œ/$ay]K]S=+"eͿȺ614Hi9.ԱO\*U[r,'dx7Vy- ~G(Ɣ`!'Ed"}#HCPʩq9&ir;=0 eG %}vRPsg*_ xZgGf#dw&vYZ'Sf_ȁ ~PCfUȜ_~ZX1la1srR}s4߾6h恎SLL,=E:? <%BJ׍ɑp(}iS:9*5P݉u6:J(z](.`@'Lz) $}jeU6AV6 [v9y- rMc>Ϗ}z)9!Ѩ蕔?mP:N|bf@Ӡ3zz9)TV*OfFJ/ asfH0XL2~xL~ .[.?+AJAAvV,u袻w1جlߓE]R7!ۑuc;ń\Ҳ:0Jux$Q#4'~Eح+%'T*;v}ɌҢZ31$W"։!P2vňр @%4XAͱZd.;" WbDGMNykD0F)mc\ǙmC49 q?* 9T޼ؑnL:*.0_)nz#Bt|l0Oy>?1Ǎ3,4lx%ڢ q< *OX4ܷ#v(b`SAۍnxX} WgR1ec+:p7>Ab?i<:OP:ƁDBTAX)B?߭Siz9o&[# =RQG}^˜YP7 ܝ#-SȦ{?%TIA+7,M%C ]SQ jBy+^LjV^:tO u 0r*Z0/AEB~LF.uiV9mqCUH-wcpjLYFp{P~ׯ ȁA^c< D`vr 5)uh)FuV6&-X'zV9m.)ɫaΑ[jB:M٠}Wh㽳uP(7/;5_̘m&6Ԩ1 O5hcK&kzW>n&ŰK s7H,%iZ+H(WH66)$~d09DJV?qVw`kB]Eõ {_ƺF2-un8"RtƣicRaMb(MfG'im[j 7 Á -HԖp88EOQ{O`HX]`ќkGR30Pw=\fa2P~ bmJ^.(owY}\S{Zß;޲b*]8B,&T}T!<򩆠[fB>'-'ƧSv"jjvShw|_+L#wPb6{mvʉA&ƞw ;#^stp_R\6 w=?|Y%LL_ysP/g6k$>ua<ھ#4iaN-[ʷhe-(-T_ Ț3뗁/ߜ 璖tyx|L77 ?`E1}d=JfJ3YU`E,?3\MR> t5ZƎ~~LbH-"鄷o84I;9X} ?|xI/$I]WKy+N-:Ty3+Dmy6 wVTlC 3cUr6tQ2pۉY]O˅/Qz[2QE6ӄͬwxCxx@Gx.W-S2&6^7 g(1I*D*SPKCPN_ho4LC?IJÍVKW=` >Nc _dj`z˳ODY (UEQuF||˱1 q-GI #1ATlO <4mx)}c^|wQ/^ys\bC0x[MiYVڰOLV n0͹ dܣG0cziү?9ɮ5&gf|0}q- T_TAHE7wFvՆBnGx35W w6LYѪGVVxKǴ\ϡū[},nJkZf `M,+|22κ6HH`]y%#5n 08dirh.ӛ ʚnMW ekbu5$낲.-d4Lyua&4Ot?&%w!;g:C/8 &Typ!0 `irD])6Uv&}NYKsWya[|"xq!2xZI?yJjc.L41N\ߔ‘?*,y M }:%c(}H!?fw!,[p=\"\Qp_.G]o4 J ?mBihQ^}mN_(ݍ1+))K)>|"i u3 і~klο:ڝ~yǺ]dL¥;W2&q[ԡ]E%t!Sçp,Mawag!n`e#B<|b5*!!t, \MفxSڅ ԙGj]E0PlCEG*en B1(G_ۛX"ٻ>#[\!5unbD? (-k O> )<\n<&? W&ĕoo A㦞>@W"'y(c2P%"TNy++ybwE &x' Lo*Bet7 ?& ynu ximIzrxb'|hU;R^*Io aYL>.;x ɷۿIlEP%Գ<9 X*;c0 DG=R&z9$D~^Ģ%eҡ@/CpTg[Yk$i1E{-]_.2-Y۪m匞R VrJ| S|ZKoԍZM zԨx1+a 6;gzC'+'jKp`qƨI+x%c!INk`Cm\fk{X,UN {SO\LVYe 6 ug^Gn@ `)g;ӇlI}FVQ7n!!;\ЏqutCQ3ЕT\Dx(>18lg3e.|[6K*:5ndtŻKeQ쥞$Z^*0M"u8?z|ŲOs oBWX SemPrh)vgҨwf™O4#5 ޗQQ29CS R-1E`yP."_)j~TdXfl1AZi7 czb빗fYdEh~<쒚e[dB52 ToxnG2kstmlU`~_pq&leL?Kʹ1ڱoF=kE_oVy 㟸vg ߎmunV)J@RϵdQ,$FxyNoް` ]3dqNf2,b/ ,5 p>rвƩOQ#OU[Fy3 ծ\E/iѴ~7ނ{{$x !b8DZЧzي8Y/1붯u Lm6uw#t$KBC1d~:>(Lvua{!sTEyNh=.$ D7Dcx qP?60>4@i&e9 ߱u{%YAvVZVDjSu |^sc,!8(G+:Vثװ77OR&:DjzOOkw3YbEL䵥 M#5+B.( wəH}WklRW9^)-Ua}X3j)0\?|dGrqjA^ڬzjn lD5lŔ1wgm6Q70_`4n|r90v D~><*" e&uen[DŽK+h*QDkOPzj'$oq );O04i2jG佒q9 ˈNb\Nգ^r暂v L:*sn =u7$@t&^\s?ܲQoDo&ow|>*ΥR~k @قJ  u>Ã%̉1p jeڠKfO>$jpӵ_qrY#P%M3vrT2LU~\_h πOY$pWI) wp#g%XZ "%e=&'UiɸqxPʤcZԛ)F!'mZm=8yNNH!Vd1gVwxﲒAڔ!Շ1O #&pjAR>[ yw%N`f tBt#I 7' (4 IkmH,n{4>IgbSm@:dϴ! ?~x+e#X vQG%tBP+b׫(Yo'/>ʍ5B\r1W{!3E~3cQ,COV\jd I]iuYCeʇ6k hQdi(Lކ[~, GLPM\yN׳YI(F|>~oV=[R;ѾT&'Lǹ#軆dvD`(Nh~5B/#;3͋1q*xQ70Mh4I. kYk#;ƹE5B7 bvnoX:4o?՝9g0`$ZRBcņi]C@R}|4;z"LVS4XìuNJDv*i;@_/3E]49_7SĐ9S˺ jc*a' j!qM݀/:!w4(]CpK3=~O=QM/s 'Cu!3LTx62c1R]0L f4FO.' ErHH݂ZNjWW>n* Su˸e~v$%N!hK%!p 9`oQm&K6_&1:Czb")f(TɩSQ eA\< ]?aw.Q\jm?R,*aӳl V7BP\n.)׬C'#Ų _0GKbFbesӤryD4kYjcLLdIgVm~wEDNSg}<5@{]Mea7qRxZㅯRR7>|ݿrҗh$srwDm')v- c75ذ#TԴva&?),8 _Ⱥ, dT'Q #zqa C H ~_摦J6D.}zl{U2>DziecҬߊVJGc,iƆ $d`7RtdE<ϗI-InF܅PU1TݐUE5lC9(U<#[pH$3 :vdv.k51χL$X &N>u 2==D1dz:v+V$Zm`l>[fcWrkv`C6-,;^t3*""/: 0΀ 7E 6|]P.vPWXOU,OkH/B5Nha\_O%USq_6tZ> 6BNwXK Uu9%HA;lz_"(0kີQz08_FkƉb{6xg5Zѧy&FwA2KV9JMFx??2J9n$LeIދ fzئ päxC]ƙUkttJ^as RQBR}.ukHB?2Ӊхt"k x"9-?]HW:&D=j0Z >NQDhS sFPl(p7Լ7&W\1'f^reZ6Giu R gg^ ,rJ^YH=q8Ug]M['&]QmI`PutaKy^rj7N+Zi*GVn\6J+̺& o0@(~E\iS "<*lվpl/$~ Rr(ɡB86t"Qnd?S?<0Ÿ43,6X.&mNj^m?{Sn9_C)s87%!DsuI\`r0$\M Itu`VPt?@+v?\e gLW() ȥ[ _)}o8ÂrxYAEW\6rUC,Y0I8iё@1A䂘!a!I}DkKiyi>Zsa & wң'[FHN 'G*UduXP$J"W ; ?7GТPr"=F$`&je_ISGrN:h=j]V6ihS+bW #D 5Zq܀Hqy*^f8%Id^,η>E=yJ3k+Lyl޷Dlڜo6#WL2w>SWaV I{%wJ{3,fzx"o/2m{G^u/0w 9fa:{ UoZgf(;_2? \ߗ+ ?;WUѪv1Z0ZcOg#(cd6 ֲ(!a0wF1lBqJRoS6 ƒs[ۭۙ들_r|ufxDf\AQ*8;jرz=:I;Zn2:fOnn@Mw*S2}[{R97-[ap7)S~}ْq΄oab(>yƖ:ͺ]r!-Kbqu_Vm{qOi\F8Ƥ$5F(X\*WX>H{EzDӏy;ܕ2hcVѩ L+w;h2 H@~qqA5#iI$/QC-LmK2;zN+jH,jNOTM5qE),+R r! el%؞] 0V-s3Of'"cjǝmEZH]8̡G$P pjT'l;нWlSp1 ̓FL !gpns_҂= |L9,5}V#O^9Qϱ*w)rRB3cKHj@\tu_=î3Z"ߖdAlJ+>49%R_7>.0I 6@>Z[j/bo. ǧw/Nm8 :&!~V3VM2 y`))hE,xCl鵜V ډp(wP|EUE6b76"ILz۞c߆=*D=M//k*C!"턘^ͿVE)I l=o$,uXY$A5W;dD fc0cN(U_jZ:{nN WzҌ amW^+ׁ;v=,[eSixtvkZp^ x rWNi+Օr5c7{uo7y#sZ'i~( ]nۈH{v$?! β/q c~f]2n>2Z Ru}0}G/q祂ma) 47j짐!xAﲃ ٷT]ŷ{iԔEqu&!lj@i=;YuHo|P`9xd8 "]/u>H&i ZkJ>s>U q 97Aڰj /5IyKH>G=hVyjjs{^nr݉dyND 1ULOjg <6pWT+@̅$zt >I]\Jz$(vJ<~  5JBU+81jyVs%mqk`5dF.HZ(Ӣhu/3Dѹ^5!x7͵` di`Gb73AҏjPnՕhiFb;SJ=>t(v^i&9.yЏV+Z{r0qW}*fS&iRxr( S<; 2+o%@cI,G`byOɓی:Vo)ju!X~Jl0rMБ`1߼Bjq2[5K6"ntw!>w w7dž==C^qݷZzևԨ 13Zm62:/uG{O\yDi&42zI%ǛI.1$RB@;w/){d,O$PkCvThHJ]&GVc;vS8G༼=|B$T۲?wL;ltضFTTz2_L_UtGU4lZ섧g9*ChdC?_~5 $4yQPupa[ae <|[NHa?~ΫM~cVKY'%!*ki<;8t_J_M??uKK& Sz݋v͎yDprĊp|$[yx.BNoe¥Ost.Np8O~c2v<J>W=t{QsZG=еd,J7 RʛvPAv44ĉ)gy*KTI>c[Y{Y"scvzf;$IK?6e 4Ņ#&_IX2VC KV~J, AQ[tʻm*UKu-CL5#a \ ]0q|&+0A@ LCzm>ԩ8{^h&{_va`4]VnR#8m1t;!XѷҀy>FbE.N0xHږ^;קޝn1}! i᷾ͷ$ZЃӆU;F0AYAMkORLQV=zĨӣSr&h"39JُrsT~ W1ܳ@! }'7\7@ Rks]6׈(nR)$Dԗp,x޳b?1XlЋ Rb1Q5Y,?m+%r(y9.ZmN,B/lj8wcH@(|>ݍ||+슄1ϯ͡m= EGW4)L yyXyAn)p4btee*ѓEuPϳ_lt229~,@?<YO'X<W\ⵆDܜ-_IAL>~?Ms k89%w{p3tE ^G+Уm6 fl5Ըnaf~r)uad߹L=)%jV6r+JO}0Z ٍyYM%}Fc;6! -ګ/'`jYQEbt|^o q(jAΚvJ 4}GLA+UaŠѵ ܳGS;e_6KotssCN1>up$@M@Tۚ]H@`=t.$#0 #,<rX9$ƌ}N-0ݺL/}iQ ȘbP&og0̜d$?W/E1IGcLm6^CO!^<9dOvw_#5E_Kڭ mV5Pv:UY<_,f(nƉ}>uӀ2mgp~plIQopJ)?>p1xjSX |+Dt\nLhCO )!WH//( ϟ G"I>Wq]}B(ͣcX.5'dNJ"G^YqD$ 'ſ֦I_14f5WPqltz6FR#]J=b2SM2]sR9+93W̔%jԠ՝({2uA3c҄A8 7{pO1*Y`}M8y|:9LT_<W/eX?{2X3GunJE89h[ƕ-|p`0xlU0_!AyL`f|r&ŏK<}AZW1Ԋs@":\{EʀN&vG3I^\l&阊yS1zq!?rl>W$rqIh:@[l 蝗y"%E&r<g9hs}'CԐ  vfXKOmmr^t+ jw͒^ێjA}17s,[ƓmwFMü+hFQey)]{vU8wnwhlW/aʹkA{E,@^gt[{B%œa2A)vŲtv$A# ] !"5q(A㭦O2'e-W_a(!wdzu<$AРsCtzEɯ.?љ~/!p7P@Nhr$M(@8U~YwV//VdۮKAn :fd`:)i~If؇JRYj~%3/)jca{5Q^Ȱ6/{~j^ Ci{NJ0B3';v縦oO Ӂ.돧L')C>悽&sgiK 緾%O="X DRqI{:_ =TVɺ::%G==^mKN8 cd~/?S5yNq Ĝ8d-?ϽCVd\"K/J^J$WMH$q?SCh gخ6'is^칉,a KlgńML2o䙫Rj,d<+tRax:kyCe*Jqy\P lx+JFY[ΐ8srZ~K#3N&qS=FUUZsRUx.3)G\fiZ\1#_g,A M4d#-.4<ë}p\JTմԧX}85[T W{:+F=ٛe c'WzyмUNמY;]c~[Bf,44]`99F[YqCeւ D'2nT䟻xk?(}) Ʋ( DwbQRfy!L8P8p k@'~1m6L `l a8%ҷe_n̗6qٔYezy5HwD;K~^*z $H춨UKXt{N=r?tH#xdEk+5#9HӱQѦrEYŧEƾYI|4+,`^CηG1񳗔SlŌ-6RW8) NcKHhjq:ya+z3LtBsR1RY'UZ_$8ans d٧^DkbH*Nx6(l} wȽlEsX)qr7'6a k_s%َ.q2Jۮʄ{FvxzU'kaKŻsV/ Z g`3%)%&HJE*.6AM֩V4#hm/9ww^diJڵٝT/CBb*[743i5xžci|4QzЛiߧ*ޓ̘~<3wr]C=u-^ϩ&, b{qQS޻C;LBe( vAg xTEW;1Q\?13N AǖpfSuθ;!"8Gdr& k \1?=ُ!vCl78u0U$9 9*))Gck4" hU`.1YdFWER5F5Dt<.fIsx"9tu 1w+:w}s+Q~$[k 3u0)F8}YY;=^-; BAQG8v''l2kt,Ssr\LM ;yHLY|CoUncr}ծ E`V/&=4IC,#o ykL+vU;[JGJW,FWŤettm 0N̛ oNG¸sM޹vrx/& j(XΖQ.m^,^`^+ |YH{f!Œ0 e#׃N=׃Lhfv%XYZH8;Mlur͚/!>pM",/cNS78OBeޢtS.ȍxKªB)(\^ :8gAK$e{mőd#F&f@ ʲ(Dac,)VeĶ߹1ߩ[ ~ jsJDКrqn}O\2NQ"wH 4[j/BYЈ7Dȷ 0&lmte4pKقnHyg*rbmW,@iCG;s`%kA+2Y G,&64p#0M^ u? ^GWCF)΁>$ 2=䠰r?\-^tnmab خ5% Eȵ`#^om31챻>u;]? -=caȖkbԶ?_CEF&IkUT٥ (sNH$5,I<w:1{DӉǕ73.Cht*/We%jk>?; >n.To5RR!혒ͩ>YX  mKqؐ,> ;J&Q2KۼT퍰UYdO'!ǼI8q)BP៱`ٸDO V)\4іՓNJ3ޝ wFf]r`wjz)pl3vm aYqnqfF:Kmh X2f:8(h_`i/RoYX8Dϊb"ѢQ6"Qi#d A.DFʬ[ϸ?ÔQ3 W?@!녜?hȒT|L--6wu9j9ɕ&ݒV?RX%f0u(D1$ps!P\k.tg`KJBGz+NZaprJ~N@0mgI.CY5NӐ\IʡEX=#6q=Ko2eד*˥ 袻?kOwJ##ge+6lw"ĺ!Rda>1uIMe'-s {-),x b .;f, ),[iG / &0`̥oVpcß:u#xL{@ 0XJ[dOOr7fXW2UHsY]BYzA5KA!Ǥm@+r=,7훥vN}"bKO(gDzjBhq מ&٫0ukt["V,f@Nhĺp6@ЃprS/%=?וO/ޕϞ7KyKm9=eRJC.D6W|q.(0,@,`Pe{mS$xz˒`.dGGK?e|x ] cNen!//J/.X+˄eUA'*m`1- "Y 6cDdOc2`7-9wTy,3bgnS{ 3I,+o{T6%ST/ї }?@@ޘuTs *Up)m= #?G'3ͲHN~\\dbڡij]w#'Vz̙qfKúJ(d˨޿ʼnkFiWwt =K57IGR^?X9҆t gD̆j-bd67itOےQc$W[ذ^6L\N0;[|+=×f.쯨b7\د<\=Ӈ.!~cJNn:c P1UeҐd}y$I20$}?c9;1YB!cwF.G--S&'Xk2鬥DgLӖaZ5o(:01?e#TH@ SZn$ =foVҖI5^.Pˆƣl)-9qFD8 p@ C\v \qd4/^׬ON  ˉmD.| fT s]>umFMjGUVO wO7[v(R/}*uIL*L冶B[@;s-LԮA_",#NJ]nP/0l=Y"@^mf<7L}~ۇT  *bej뎘vCSnE+luIڇXs=yOn!t- Y%H`3E9 4يph6OF1 ,@EMCݙ4MDTt;@xd9xg"@.&x= >a 7H U$*WCq4jyF5GQ+H/R^ʳ|۲D0̵c 8HDk!W^@4zB2JKn(PSV">UP?AAf]I7i"6z -?#?7e;?+ȳ_u8Q5?=2*ظs|[LF=癑-B0{oAԃXĉTr}UzV2XBL<(^A?8SGͽPz]9"r@wBwk` ,M)nm^c,"N_CɾAk-{SuxMОJg{l8I Ç#r dDx$Н"B4t6 &L7i@qu|RyՉ`IAC A"Fh4T/o\[0؜$V$MT (YOq0WЁA_:׺Yc<7SMgЩ/ت"[ɪ_ KN;=n^vnƧ葟'S-DB >{j?;.)J 'E;Օΐ[-'ޞm 0*|A)4B.^߶uܚqzr|F at{ /I:Nwj. j4m@t6}.X ie U-.(o>牏Όmy E&ڃ ig~=36Xr7@N%&8W<4#-|*qZËDz*pS_@ѱ6PYzQ?aMSҟp*O`P(7o vcMmAQ}% 6WXmYq} QBK6Gmb]WØ8"O٢GFvsPvǼA/@k'03e埖uޕO00[,ڗukh-!!m 5"_3XFzj@:\!`Ɵ+;Gұ?KBu( R5ZOl_D5JyBpc59oXOH2u s((U${ LrG,3Wq+T0\ߓ?n!Ե>vݬT *Y ,d?6+r1Ili{:-9@e6w7y"ttrק3}!nwA]jأ#"2) r^<t:`SD&L:n٬U[$8ĘezJ8J lVyBMy*I^E.T g:i6=Vy;+1}T,x < 8HU7I^;fQ9e<'7̕q+:*mk: [糧Eq V>,_%T.sz($3(y1f|8 I[`">BeV^x{j3,/bT~fHG@7L…$6_C1 r_M9m<;%ܪ~(OÝ1^1GzWȠ]7 !UwbSOZdаHԤF\'#x m^>6j"=Iԥ|@HKiq-2"CP-g֠N׀&5'f!J&] <дT^knΡ# -aa+_"$"6ޜQM8(B<қžĭ䄪ȾNcDּ^{WlB5kaGJI&UZ<0 "iw')` IBxt3#sjُY&f2"۫YL%HPrjyw⨍d?tЃۅM6q<-^E|X?Qݣm4"̣ ]fbQ5YXW(;M ?}Z8)`W(}$*۩j֤`D 90ƻQo|@j]͟G\%%˗D㴽u%F,w'Je 5dh;C% _'(S)'g5 Fl~NLeVI32ci@ŝo_P(h_j/ bܵ b>:SQTx/ס}5qTBHQgY/zzк6IS{mledJ;"" M&"C:T.UE#9N~%IiD`VU/iH_K'W‡/SzD\eMC Ȅ\ܞR$Jp bvQ0Yx$rQ.+6SByE~!7`)ĥE>"4n1gS~zre9 rm@o.㸸sJ% ER*R|K:A}jP|Q 4yvwq ƀٸqWYP9Z9>x #EL {fWGLQWqfʏ'g@v [98FU UYjYnXE\}Q6NQ0gDp4/mfPU(dJUf  ҝ3DKȬf7;<J?RYv,9( k ɩLBIKt60AIԑ{>+ᬣ= [p S 5(NBIzFW.=ټŤYmدX5Prӧ8RȔ&r-1m]&q"8>g{sdh :stᢝr(4vo"`ۯMOGX^ew.F{ c3t %fȮu-y5ӂ&lFeS!U2ѿ_x' ʹ.LZ dT5#d:ͻ7S9j 1%sW&Qa}OUQŽ煺X7 tc몝@fR4w3/sO VHݍF)}Li8 UgaoC[؞-[ ,H`UiBA52qF_/GpeN݁"cTtJpHZVu@YUaF(9k)|:N "'r]lGMwQmϒ*9>M" @ᗋݏsGdeS%IUi}Oc"Hx+ckxPue2HiRQTe/JX*AʡpЍ{tr5fiؓ|_wk_R?ro2kakm q<0/#EUF/['S9!X}*I2"+):bp$Zo.,Q ֪VlM9VAFnVP2f%V/U1e}66'ujX7 W}^fy'W7VUURt;љ* z$x2qbb?hplȻpUXIpM.0{+F@1bВ Z#ԓ:Ø?F`6\Ü%=H6[(Ɣt9b6Bl|iQ m Q8kkkL8A4 gpuAu$wmIe; 4,#6kgg{}%}GWgVv+ @ɦWya] {~b['N} $@JGVw"Y7p')Ey(PxVh#*siDT(c`O-7\6]Ir,g9~^q[CwDJ|'tVZ Ɋ hJus˩4c?"9 GϮ*ƫVi YZkޚvd`lʌR u+ѳ$0)ULT:u@Xz@LnI]hyxz)LԣP& d{zl"w{p@2k 'pSxWO A>+Y8Z3 -_ҰCJ|gd3lvSӎԱ@[|zO R.ð_qOoʻ0b:"AN6Wm:u@Ҡ7ufhWگݡ?>&u+_`[/+eݗ2ehdH/OZ%A-'SJ d7}C" :r%;O /$ŅF/ZJiE)X\EgU,&Io8쪧 vKI =)WSZcWU`0] SA5u,Ea^D]IWXIcy$6YfKj!¦jZa]2jɞ L9^Ir&|K['=dqkZH 6 eSyq_ƕonސ[Tm_9{}؇_M&k'Ү3*݁)ֵ% 5&Vw-6J1{{t 9E5\!&x}Wļ]. ^HPˊm'P| TSwqtUI5f+5 hhw5^/+؜'@"y@=SU}z߯U )5: 7ִ .^pj5CiTn(pMӮv5jD%-kbkd\R‹3i8Ac|MOrT|Y^u.fwC-X+* tEM$:&"Q&s8kwwRG{ >8h MW7X s~Tiŷrߨŏuċ?X (uM8ۣ8ioL6vCpu Iq{P`MFCX:T 5;pMr?ƭBnJ(y:(UJ_W@44 E[f<5","M? \v)X|d1ShS1;ݞ59i9EcQusM)VB7QRہ7௝yWR>q?B=Y % (d{vڔMGD J3Aw|{&ei}lQ~GN9`Žg#'S!y75GoBb2U<8w$:iI Cx΂K)?"n̤y!f0nJz:c.9PhkxO7;3Yy Y_!(zCc$iMx4kQa}к^ΪC6yE,%( WAK}Nbm/,Khl3u&@"7S=09ú/II _Cbņ@-PX9ͧG ν8 KF쟾rۯӥ6ޣMvHrvPצzXãvتl,9̟*^7JtTٚdP8B7Pf+ٜC\ Bg:J=Bδ ԃdPPGXMgQUj7&tN/YH[K=VnW4bLn~6#Ydl6HŜE2<%_DD3TD0\"gψr1yr:{v"W)UA~YϏ-7ϘV&j$?6H/Nۈ > a\8v1w?N3 r!1~8+ ^XlNbߺCMO“> vL"*:aŠMG~\]=U leItA92f{U @T?Bxl UI :*T ki%Y{ѴiQW{ D CUD\4;wXD{Ҍ3CrQHP"O,RoY['=· 6|xI0Kb ēUItϓk'”J 8:u8yͱD 86Oqx̲h@#?yC^d Z-J[fKKwF AY ]_뤞]=|,rZʶ+pIr[TIzx1!J2ݧn  ȋ >_μ%1#t:I/yPEbV-30pG0G>.3ϖua(6Pܥ8cD^;kɆ#KeZ=vӪf?liWQǧYn |Ih[>j*Jl!`8]>1G@ k*o/R F\z9j_dW1Ȕݨ;>@M 1:){RJPV7p.Wxo8v<:/n_F:cp%a]uM;ר7[X\qj(Uϸj Te"1:st-=@=0RP@ N.`Ҽa(Uk HwDA/h=P>eVU]=,MmXvw.AupHU8Mr5Vݲ 5`c:(U Ka3]R.H- :FUjy6q*4E쇪d%ϖx|OweMB,(I- 'uLLAPᤤߣf !HXgaއdyu }GRR\EV*? 漿r{}F?er%~,h7Ò.]=i0ƤI<ʼn[EYh!A%[a5/zhMF6.eg9\rV e" 478qQ9E>sA9 e4vB;Y/)``d%_}1>m \? [-8H?](>{Ie(rQMfn)R]b8pQɦ&ohF7;nrJX4"3ܩxUX}sl"\LC)6} 2TFHsl{]#rw%eupެ| 78ҡIcX A|]Z}ed "ۢ%'WṆH~t2 MH3s9Z, ? h>Y| I:8(i/{Sƿp ;hf/RZiTJ<ՉJw&sG1Jq@0_ha":pkd }˜&-LE o ,Zȭ7$(_ys`c\yd &TYl` 2=9rm]aOG vJ,,_ 8Ij&vU=51~`$\FQla3mq2D&Lg/[= 9Q$#h^[j*z|2##c[#Ro13YΨ"*R-XeAMY lGR g\ OG^T[J ۡ;v^qK=7iNgP\o $c.LFwu AGa-RzwA# t6r;Y#)h')5KA[K9b\")H)ϫ-SK $S=Ax6Ȝ0ES@2R)>}?9- (.g$VîA \3/G=,`;0[{v dTbzT?ZXz< e1XHe,0 VUoed:/QLu{[S批Q/|v݆E,=2㿡_qj *)-c#Af> `)CtnCW }".;5XcO8ڌ!%hI7h-T1 EEm)챙uGÚOK,wczfJL5'1?/yiV7)Mi;D{;&Pfj>M7JLVzoF#wJv6*?~R#o%/K|Rבٖaq{wcSZ!& ~(ed-dŜ[lU FP./g)^í+Sgsε.K>ŨMŇ} *qEXViB!2Rj.Pӫa:&=Q/O!2anhf8;zbǡ9YuG&փ鵀Oe{}&qX3 н#.P؍6ӉRz]S14PJV r@߼ h tߐY=||-.;hI-#xG";r!%mwl\dJ2[go\vP$i(!HUf=5{vG[tϙ^.@vXsYk$+\5QgA^Il@cwLLCBe噼/lhy΁)EeO -TW;M:  Dd _$[u@R^ㄔ;m!.R`fZW۶@#%h[I@|m"#h;e FwW:0E|El VT:c nǃ(hpZzko/:v ,Qc3˭L2ej~-}>:2;ZC14uDʨ.O0xTO6E:!YtME&Yk\7V%J%! (`dE>N؄ ^C/SJgZ\,m|p.PL z8yR NqݧX{B$F;!H<SLZX9;sJBBd4vgKz3p0!rZþ5 CS^zř?΃9ʋGzYp(O|fy| jQ|RU4{IuQo lCcg̃Zj2+륓S q^` v̒$" O}Zo#C%[%b#3v &8[䣡hsS Clug5ٱwi:չgn-VIS6 |XARrJqͶ?&r3.mwkUʋDz^/j)@-<BP[ ХN% 'u1~# j_cBi=x<TocAv7 Q; !RFN4)MW'cy*3aϩ1_>`s2 jFÞ<`1HsXl4Cta=U0![\fyz+^$[BE.XhfQ Tq6KX.Ru95T&u.5;&FdЯXl1b;ʌ " {fRLߡZBcnS8/ļpHKZH83YW~uЈ4REdܫYmW@M t|>zuKTqZu?CDcgWnTTة#ʛkpX HD8bgx&`x ;նDӑ [kl(+o2 .4Mt?iQ 3~2lÝ`3rt{#ڇjXZ9E ])}H&7[]B _+$\bߎyE3@9΁-"% "q'W#HwhuyLy!e'QW:"98kx)%DٰTCȺG\oG#)fX;,\  C:e~]HZ\/EP82ѷuɻyxh,W"uLx?x]"O!{YFkI¯:&?$AbGyÔ4.k=.?/*_?]GðX|CgJᄋTTajxbY|y|3!;+?XVHtddJa%.N%ުn+ UZ_81•}^|o,890FLW21y}wP\>Ub('*[OpmcKƞ]2RAayEbYp# H՘4⼀oOcXgOqy._Ż@>OjUc(h/2*M9;&,[Dy|4ۧ@zHFcX`t SJ1I<3 #J#Dwl#Bt$ʺPDc2>)^w95K0!- T"= 5[qhI\[zd#sZ$ꦎe/V>-Utuzvo[?cr_mh;YIj{;'W*~ir9S(@ҠnP_P3BUZ %+EMxqfR cU?/|V FS4d~n2*Qgj=\ E齵S!ꏻxh"s \1N|߯[Gi}6cm5b&)v2 L '&uECQb4ԤbCcsUud:4`:n232)(m Ǎl@sISt]y$d>60:pO% g\J5=-T$ 78i5 ;zqBK!m) vkj'tۑ6hEf‘nVr4<@}' KAfje6{{ӜEH;9c,>4jc39AŦ"ws+ip kDXީ%+`åj+:%׬m.&8R} ӓ%З׆_0se24 iɵlf<-)-@Fk(h&kml[zÄFHW A@~F2,45F_{#)}4^-a"h0έ[!s&%΁ob;. u`&S=Gc䧀0 XC2Z'/Q [aH$@G*$ǰV6Ęt `9'V5#<+MC P]"uPt^N8l\.ijq8x7HI㷋\wR/($kV&2j%I[Ծta6ht1!~u vx!_\t(:p(G3^u\sy=(f8>BRg`䩓ӑڦ\,``Ε @= wnQF)lk E~ pn'fNܰ '*^Å-TbȫjuDvH /TJ@Qc7k"U>3%8?nLWy*׮&i6~FӴ0ڣRP ˜҆VO5D0$h_ַ)_ kW[gV21_'u TY%ǠgdR $ F!=fYأɺKATkH{6طw1#p}T%4 p2'T&$DDKS2)cO\l*嬇$S-:e ^j?AD0zTɨbih͑i 4 vdSL,./scB?1 jRMaJU*nnWn.vi>v(eT8 ,g4YYܿhݠCoL.k@N1WA(oI!=|Ԉ`AU~b\bQB80 YhuH҂7WC205 g 9/Fdp֊Zۋa:ڦr,L'qJr|]u/ H/$bUc1Z飇<]I⫊1"03 f#ޡH(m wW-0$( &rnj6.bnEsE&^e8hFk5"@IFp0([H.`WDm` ƫ/O|c1\Yk !N2"T7^50fdP|Nh^tA5ȤQśak xJtW~9Z.idV* DJ=>wv+d \m}RhĂyl/PM܄N߽CJ*k_AbAOaA};3N(x]4pJx²N&ZwUFlbƱ"8KC r[{g2euEfGS.x 5k89Au)t0u0!׸ht3+[Ӳ0Apx-hsӄ"OrŽӣ4?h4)nrrlEl,T d$f^oOnpRzg\mMu-?TX)Hs2=;GvA/W۲-Ohpv,2ǩiiiTp'!2bL(Tc f ߛVbm7uAhZm0'6 3Wjk}ۨ I(~QP G6>-MY_<3uU~OCNδU.vesD$gZ3rBd4vR@E*ʠ+)ٕ\')wy ΐ\cf ˠ'zh`-lݍz{p`fUO$e0v($Xo8,p\?9%J]5ly{lx#zLDjX.[+yuv64V0&o9U-52`I H^rNTW$vj|)^ 5hϰ6tLj7ZZ^cCQ$XyoS!CӶQ"Am1FwBߧ髚>>cXI n_'6e JUtMĶC^Z;C a6ӄ)1# (1@#|?bJH[Qah7 `߶<$pSATt=t|!FI _phr 4]h|2Ha"EdC Vݽ*a5fjU5ɋi`E.yAhq YGCwubT*@O=X mGr6O2@әlrTDK@*+GhO_(z ӽ)1Ʒӫ^+*[1xy=̾"cԣ)P淬A)} 5Mh]V$`P>jl,mxevڹmݢY >O9ꚣpvF[o(ZHsxazsIz_^/ʱQc%@NN|hr s)<>#Trcm 8Za$ [>-}<<99@!gWha5]O@lꓵg׆c6~_Pj6NH./Xظ&z~d;?e=ae[PEJ5$OENWå鼃"*2,!Cc~lBft YQIn;Ƈeǹ7\Cop%<ʧvͬ0Imjٓd͟g G7?5 Vs};IQ&ѣH֑%PtL[|(I!KFsI +9(pja¡ogUDPLxJψOdD8mzjΜ ǗFU^vI'r}r{ H$d; ]AT IB|3Aa`Ikn'|nă3+Yưۨ8u3(}ߘTosƚ:b?ԹƸ(LL6i{x}d/u|LЫMQm D$mUN0%g蔿{9T>w)23Lٜ&azi:[2n6|nbr[|CXǿ>7׉oۋ(LW?r֡fv,F3Oh9ϵ.$OE"YNZ>q2JN^u2$lp*Jw),9#Qp< TL+M4!Ȍ] 3F.) s0ee[{'FĦAx.3\4aYOy2lVHE*򻓽Zhb2#8ohP0bO7 ^]^ȉC[ÉxWg!L^tvHυ z A.AF(jg~r!|O ${7$e}.C䏋}9j|2@5((Eo/i-]x:?)yȹ!"$,m#}LMc;JLwb*~H/@Xj|/+ EWF } Zܢၺ x"0%N]KS"uuV!%cxO7[0YdQ,Ξ5Q g[sqD(>;epdfh0C4W("r4"TnƸ5Lٸ&T堪u&]G\?7rH#(0-o LW>w&II)˸HbZeaWqD6jS*>,*PQ2C"q@.U%+NvK jՠ6Z=lk> ""{k/U4^5q,@}[3j;ar,Kh Q|iXQ|?Bnr>W[z|ɪϑne*a;vkj4_p %t75̐\]AZLKu{ŴX x/;qjʸSER=[Q\'3-[_FOoY_THcM`Yr$Ljˏ5ijᱵ4`8^,2ZLo`Ϊ'`O#Eux)ʟ` ګdgLɐ=]J}v+C:~df1]$gT_`+;w ,d] +c'蹯v jUm9H$FhݺwyQU2ke@BOx݇{~ }NxLiϨm_2#5͔::ڴ±+ˈz/(F.01,"dr= DQ2%ծ/aK9go04/ra?4ɩ fGip7M`=he3Bh@`7Fg8id Dh9M]{eWRahX(~/Bz\?^,'_A½̶D}>K\WPWO3phA Pg8PҦmzfRdlqb#;hQk䳞Rb5PDĀk_ū rPh`WQ熟gg"~0QtPFq. ˖SPe8q/ s!M8v Gf gHΔ9 ĻϮ2 e@hPP3M_e5D`!rA<=ߟzk2!-xG-ToL+!S6-}ld&k[LJ;dTؿ5m3}2(3xgv /3b*.60 &6$]@ +CV:` c#mS*>T+bA{1H0M5he z AfDvtuE=mCpkX|^gfKʳJ\e&A7߰K!o וwQIۢ1Y  *6e>Er֢@HȂz<0JЈ~:ORD}-&k \G֚dNRp͌MWNsR_E=-|BS7bz3/G8ÃA]̾ TIR64}塺-;5;Ά~h(=Ұ L*ym?AdeɈz($HHy:M^| 0gԻ$2׆xK'^-?W-Sm+=Y!wa^9s=|leڝ[u2R5Xg*sWj6AG*{X%HVv Ȯ"З?#Frq*s 1 ź4%*vhg?,vCB#ٔH6+W[?Vdg 1z壙MQTҞF8PMZ'?gYW0;w;{~Eo?lttF :+i[)8Рe5>:Q:T9/Xh=۟`~qhv?W }L B%_Cfn9OZ,< RDE3d[D}d>Q% ^"ഴB/+%9^Sb(a0 Uۉ⠏ouV #Ԕk8hlYB(l|3E/_}[ $ ,] Ӡԝ[v]94:pv\xL6r7N! \0mNQҌ` t[ή.Ag eE?ƽYt>E_(Ҙ'SBsCNܤUm[Ρ=bd*,qxNSxunŵwXsS%|FC`Y(zւ~6&fSz@Yg{i5zU_Ip?ބ3МF!:/#7 {=VI6$LCZ0 p1/.Kr8 o FqT_)3ysc=u;BIdT) nʒ{dOTW{\>:t12mЙ-8c Qls a1*1{P].]-αQ~uI r7˵FX \lx1RU ;*cwb+ vn=$!) Y^]0P(ӡ?{5 X}EZGh[j}v :6qKt47F~3/vy'gl%br &ԯυUnb"?tM; JA@tS9+6DzHqȒzȧU g8]sl#qwR|ύ'Lz% tڮC=4%#Bipo( YZ!҈Ӣ'.G(`n3]ʬ}Y32`3ʨܖC`@Ǯ6& /U]$.y/Ab!LNN11 _WWo?-ZPe|Q[BNZ?28F*^VBr5]9nF7:nlc $dNfQ[0]yD=y#gWYeCc:UGjܺt-˩3~æތ.?8ؾ@Eto z3' _񺏁<⮒jj.}+~e`!8E-0EIrTh;)D>')oʪoG+1A)H3jjju0 M斥 '*7\\'\IŖnqtU"m((dlR UKzAKY7u% hkW%RwuOh hir1 þYrBulڮ*_PioyBze@SrRwZ|4I+@!cҌJlֵ"* !Tr9z'Ek",߯饅/i`_2 kyb,IDMŕ]  "۴nond "|Ӧd#n_Y9kg$4|DHz~?0;q"܋ˍ4|w!Tm֪pQ^I-̐]7"JQJ&`-͕Dbόw?wL!x(jȌZi{Ycy"Ԍ`-/@[^#^a~tg!q<%mVBZ).uA^w~jD >@uʇrH6%:(AmO0=3;]_O5ˠpWઘ4K8{I%-W$}Xƹ߫8=3$ Hx`nMq x]|vk 0&bj, sq*<2C &<-3[(C؇KeMm퐌kK-3B$? (Ze?QD>^fjKA.1R[e _g.M%{~|iJR{04_̖{mCmpS)C%"Eqx;vU& "f190U{G &[Yojq;ͤd.+iP&],6cu +SU" ۶0-5V":*BŸZYيo ~Tm+I<\%M +22)c_5jv>ɽxϒ|%;QÍn]HIYY I[]-BKΰSq`Gk&B 9Y wU>"a+fA,.5XQq<#)a!@_fHlqQoqNw _jO Nh.rNU|*r2#[ 9GQpTK>u}i#5Ju@bٍI5 _ J* 4vt cs@tFƫ/)l@"f\!ht%VňGvL&(NzsCVDV1 ͋-vG-DŽ2hP!_Ony^uh`Gl"M:X<+. $:-dXjBG (Z%y}%s9OhY%*s0QNrXj`W0܋r4ssٱ!zləh1:LIzGC~FAawVN43Z۞BBj}&_&Aё~O :j@ .!50Zx Jx u VOމBe$5+G.ҦCȒh%pg5XyyT_x@ot r lI;eS܂"uGpU aٱ_`e7 HXG]w >/Nz7!HksIY x./10rp0;kb=BXq8M* b:[Je 䢏 l|pt'lg&oy"U\o#{3SXyv Oװ~rx]AԟfFC N%a&)Y&4A,SnJAM\ 1+W&1Zm3kH?iT;cat\N8p@?1B+2#FA64N6 A>~ ѹ|Q'3IO C _`rRft&x^^.W97Y2d^QNCѻ|q+%8@i_=ױg&Q)i_;gKp≮iDkjek*Xi^Ji}$ID4fH͖Qva .~r"ޘ#0fwC4S?79{G#Tc )y%c$[MTh=Dxέ8Ŝ}.63BһդVH:W0&h S{+bTL}iJIJf~݃ڠ4ZDГp^Ԩ03ܓSQ$ʕ}fOZzǽٯ_5}m:)X'`|Q(B'ϑ3Zͻ4_ޢněnufHQt0;Jj0ԛ (ErB>Fl^BQF' vm"Ψauf;%p퓬yՆCA hf=QjDS؉\zߴ' l{&mE3χ=xb#΋'5>%)' @d#PG?5p:c^v5LԂN?brϸCyWru/wqke%G5(wwN5C[sřBaaS - 9"BJ nlqS]`K w;)R]7l.nRhUCSLSId g=rz8{IdLjU7˳OV:.0Lˆ"rR 9Lk>݇$0Kf(]S'+6/g_1} lg6}V]ʧ 1Ym (aNcKd\BٝjgeK8@gJ} 3QOȖTPp$5k BXz1W6Jb@-8-|/KCu\kh~e!0k7_V(_vꎴeӶQ6t٭-F8DV4q #I,'%gOB :G|~6y:rFHjo~+G*M# fմf7NEf<-K9KyaDb} ,hCxvײlHX WqfS*{}dpxРN$gm%d~65B3cl';%34W^P=jRz$`K2K:ƛle/`2u_KxabD0K2s{-F5xO'9L0PMǥ5kMZ[Ⱦ{#y-쀮;"Iw7tV$䏳KUNF{2z̝i^wt6_su}O<8&E˓|?ڣ29֩w7U5V*1ӯ~.Ra2Bͼ{` ]YY{P}C2C)˼OKGS-{Bf^Lq58 P'.8F\tƠlOo&O28[fPn@BqM@X!Pl>Crmp嶫HM,;kk[ 4$ͼ) l%ao <#P=UO?)5©<~x0I sB݈7*U{N5)k]fK/5o>|D6m:q?%}fA-9}Tw`,llh\h'IpjD %FḫQ{xGquD3{6h]ooAlҏLW.? x\RuF6S`i/圕 Q)=Ј;[="d4c{鰎;Ns4ߚ:ŋ\)A<%rc5%ySKm"Y6B Zh&f~[.@P{w(3{>%˸b?2+K_ѯ)H#gKw~]VSLbۖ0DMx\kyoJlԑACOț'jg 07;f0}!4ƽ9ȸL³e-_ p{Ey<7(oZ}aDL{PC5uPp_0q+K50L sWw%Dtrʯ:i{s/"Ȳ,8[(QKaՃ5CpU،N3WbߛTKXHA@_40} 13'D*)|m|9Uf5ѸJOe^ z()r12Mo1gĻߨsu*pHfv94q}4uN6 fkŠϽi3oqK O&x=4"rp۾ы >Ի@&4%b:n58!Bκ·!HԊ. Y]ETıE 7GQ4Gt෹?9_UF&"z}gc~g)^?s|[sw}c LCp?r:tI$+/kEBj;A(8W+OH1 Sh dJϷnz7vlڊ bOn("4RF14i+ʂAMRA߅P孢Nܴ7lDiӾkHPo ܛ&8lv3&VC>N+x`jDL`,F B G-VmBx*(7`62m SGh`2^߲Z,SƋW'?A>0JRƣPF{<*Y.A\'f79U}[~_cR#.Y#iPNm甦D\厃l% tFq:]~tӆ%HP֧i:7ƛܞo7#{1Z23}_nM(Z“DkJp͘^= /_O1S8ꋎ\;6AUkw49i~]A+02Gd7Fd h&n8A(6ОӀ~Ik"}J4?tNBԩ$ 6՗mO}NjܚZE?v5&V/oXx[3Am\̨W{zF/Qe׳ Cg" Ug{Ś*%Xsed1#F0x/X_WGY_zu Ex{$lt摔XlyV*.bifG?KnSgKl M"QTB0<$G#NCjxuQIY ,wSDa`h%B=,NE@{ sU{s!Ef<~G^_o}A- w7 &\ tϨ&OwkvPj jha}~Bőn̠9ʴ_zܤ* nuh0&K$6Eqm!eXE9ܥw#s}W}qtקg³Es e|k&ϼD})"#h{˘w P:,6ec"~E` \=+F-:? .1&ϢRK3Vm2f_U 2:J[5vӾ|S<-3n71ʰ:Q<- ?"|~!&8nÅkŶEc)0j{UzŃjsѣH)dq#zn2,rj"?.,x#_S2/kƣ4gW4YӠxs8S޼1}G2IѹF]C{h7fp of+"^z,  kw9**vfȎ&BøèBGx+:P.GRvk mտUQi[fh"  ZPWF;rmj<ԺozHIa$yYlJQ*ڎ2ëŇE Xo$5NSirdEMBU> 7ujH<5OL:aGd.{t@ FVLGF , pe6/5usJN{kR@Z,ƨIK t'4- )jHyvx ] d ʰ|*Fw!tetYua A!l-w,<}:FƏxtf£WW_nZ/yZvvZ #O]峅tZ"3W˴xs+ Ȱ$(rx%l8@EkiK½FrVZgҤsf 1 -YNSK'"ltoėVdh*-k0 G/Op9`(|/%h(V8wC7=HܒXM9FOf X8ؚUy%"` rUIfMG04&揘YT_q/6g |3DQI8>)<҃6o%=b$2}u=p|5+3мQt>v ];„8/'V )g4'R!<+^Ufr1j  ]x_i@ 6x;PYT(O oePn}i:vZ;T۰)!_ wy/>x݇HT"c%WX]"78w/q٥s7ALp.JIVn$^>/.5ň"JuPhA`l_̑n`no;{s!c}b t 5inDWC:̥PO]*#_pß}Kҧ,2 [Q7?)h1Ŗ[DŽ׼pȀ/+.ǮB;EG5Duπ薘X j\Zyjq $K /MH.sQ~8FjWuW*Be<~X8 @hBia6"տ:а;/38)Rz +}2$@ )Q"t9yCk> _C4Y INO7̪GISTvm e cP~C3=tc]H8Ocǡ=`pSޮ;Kwv# br-t(D, E&E[e)&n%fLZĈ| 8/&Ag ~\x`e9S|WaxD|5N8r&CLa͛8ފ*L1Qب[@*Ey-p}|ybdJ@.$B;ZSCS^Ig:+гh) B{",y+ʨ/)na:Mճ8S^GƈK!v룍7Q\PA.do?i=/n5zS cy2Ny0HW~̶h%)0Wɨ-8Sm;[LOOJZѽqx^dpmTх܎\[| 9Ruyx{QX4g$N;=iVY[k%b xv?{ ,gF3S?.W%íG)f6+731m0E',rrD}nkMZuH?}dvĆ'gqi<+"[7ʹ5.ѰXu^瞋9x|qw`=\ כJ.eC !5 -sS/W6#!?nkbAE&9/go ]j /ش;U"p!CjQŚuiu7CDqPo".[6#b6lqm|k''f.3]g4?53՟(qMN*zX}2vnx_Xep.qwsN>VgdÕG51Qi=Jld;Dъd1Җ X 9Bǖ;Ѷ/wnىE¦̟n#=[9~eUN*ށfLZe8ޏ-(.q#Ώ쥳 -sqxl1*d>Qy0DP[K]oag!g%wޓ&7{(#S]qvypÅɦ$n181,ډE $ k_d^5pT\j'^"dTwwp_CL>m"kEwҒivdVv&Fֶwʍ{T1=NBfj( bJ5 n E_rCM5Ɨi]Qh UZLPU L*2_NCEHlPrȫrlcF<`+V~F =yy|=}E\g ҳgpd'зۡ,ZSn A+*V}"#Y)펱;S!$ҭYFf GZDPn~+i 螽-m?]m^3^c,JvcJ}c-ѧq~9 eEl SԨ6 I53C:$AhQlC .٣R@#ʃC ry戴4 #F fmw$Ii4xދ_hz]_t}{v>w&[lNʨ 3d~ϤX @gA,15s):-t`g.(cMgų@l rΆqBHY8Cv*josPI8 8Lٲ謱T7nUvՀh˅'Ӛ>ʮ6x #oEk=7fo>[:Z0sS +]^Gb:(m`n"  M*:a:nׯ nvQ$U}_?NLc޶Ok 4 ftqw7AI.>ڵkJxHIs6Q* -Xy4nK4ց৽4iTǒބ[iMU8jHa̔P8$9Lֻ`3}JF츯Nq/U3;5LKa+I뤙$J54AXaSt~M"N j&Һ];h,=qiCc^2Jc)d~b1(Zx:9;r!YAppI JU1(ӳۡKn)cEd ަyX路 m IS SԔg/D) ʄՇ=, B4P6hkuy9E U8*tipcbbNЦs}Z]0ɷcc6mr'߽o[AU$Od$1] 4ıU2hiS@.t1ͭqwȊƳMF!底jZj/Bu)"!;˃87oJ֗<*RSkD_0~@+.Ѝb ~Ʋ!"EZ@a~ ҟY,z"v +z:tk&/h\NQ80~B sC=cY= 0J|B ub QJ!NygK¦EKkL?|Jpu9<o֮/ƣ )PmPsC护 T>{R Or[''r#UGwk:0 LԲo7nRhƕDk_Kb/}3t9al  _x,5x\gB♯Iz`.Q}t,cՖ5rӸ/X(vAj Gn|ZJsh=6؊0/WpFփMv'4)bi˛Xd(bOȦ-a1kd? c|!@"<*'kmpks0Wn SUYz;j4{v1^lЖEl*OxɊ6se؟ĥ䏱P᠗1-7ћ6A RmG=7uDIͥ4kO3 AyhjjNlDԷ{H" g'Oτx ",D"[G%@Kt}n B6;:CtUIYF׾,\ UM ÝߠkO; ћ[?焮Oz{/?4?B1elFp^T(vPa3GWlCy1 =Oy"Kj62 v)3Em*|Ⲏ_hTE2cq;4<vZzd+`E=;9 }L!hx, g_/=rvnocInjMJfm@H$[;VҹFw?!|#΍{FrkS:xFM nJmvj @kz6LUY$_ƣi/ӹfi%G'd+XӬ"E5SmFSϷB\,IYΪ7wV.r!S]fEwׯ3YLaV/{}Tg}tw2E^M ̶֗ӁQ.7=Qh* iDm,QId)eƢR*a]?0h7M39ʡlm)Jm  :l D4P OHITd=!G9|b!xԏ3tL?lg]"S.ѭxuO6W:}(_IO?= &97]\`S|5h"NhX)HM$]ݿRy΅hz<꫶ڭ7yA 62Ke\W'\ +Ÿ?fgBu*}w{Ժ6-'PPؔ4;W *:W;'npm֦b:I"˿ 4yݣ=hాSpc*V!ARoQVsWAUB+s |2D3*0'emˏ weE~?Z2Y\&GR:\ mH!- ξ&6)>k=!mdֽ#9Gy5V:ہZ[&JG*_#zEss;׮]q;L%# :5&rp"֍g՛R.M=g}wŮXl--EOKN0j>A®24ssĉ\B)[[ ChRL4Fvn :]ԓdMY6"@Uu[䥦Btk&ٜguQ!94̨2R` /Tx1R*?L6+x4I,,R@k{ #%vqA ^PZ-3|x̜uC~2bTxgК3`nJ׃ԁ͈xL}gqIoGUPGpwvu^At(=Q׻O (G2=5;ǀr $v2Xx.hny4l,>cf^M#iR-C2 R ";5Q&'x)VK.0tvk:UT]ye5_)x3-Jokٽ#g>5"[SC7NZe \xpuu3sv#2xUv%#-̗lRu~ }:^qr6f$voАHA!e=K8'Àe!~dkKK+r0x&([wS_pثP %TL~L1tm豂Ix*i@,wƘ8Z.jk_{.k=y>"wKٶ=ᇸY'`^`~m^a1VhH< +֔QsD(/kCg1\.^;%$@U'*hF{kTD1?|i !gxfqq7ɎPZ{B#,VA+ Oifkv3TS+̠4̡(ϝws}6Os;zemJ.KueSej9eK$ ϗG{p}!ٓvED\ړo]Wӯs= ׏~+AhB8\7jx%SG w2~.n&| a´'烤!EC[öſkpx?hժeMzf#NM,6jqh/QK`o*<$5#`*3,W7rR.Փ,{k{o´xSo)"# &^Kbף)RWG95ҁT"’K8h6ltL`$y422BbnHF1{j|m2ɩr&n U?4A|ڦƍr-\*bVܠ7+ae14 *ԖeD7|5\z0=e b۾eK|W%xݍ)C݉m z'!$奾hߒllR{ڿUCL')`Wx0ѳqe\քs=`z/tO<]Uq'=q~mߨ# p.z; B @{BoBdWO~kE.! ȸ^椲fVmT:W3pe[xPݬނ|pE0yzڛW-qy;^5i3 ~ VR?^l.deixFHu^%dBWQjKsX!٬dZrbbZbP4 JC|•x( 4ꈘQ {SǪgϜ $|e"hEO.Xtb43ʏ|YΩ,enOD3BEkUU%' 'nmέG^Nֹpqr 0^0c.utUalФe8 $T.8t?Ye¿LpNygḜ SDU*Ĝz5_c'ٝF_h|B@<7ŐoՊeĹɹݜ0k.x)I_îE 񴛊H.Jw !N7[&հ[2OAO,HffA<,TQ8],xjVT=EO\"F>dgٮWUJuy5L^ |V'jf>”Lz6y._`$$W\0hOΘK/3S$38 "c|6,[5BqBWrE!eGC_~*m,ȞȁnxJ<,iY,>G4a &`Ԡ.A(YfTBN.Ͽ X] dM!< %&tC9lXx~(yध!ާx‚̥멮1 iSnK:28ޙ+9k4"C44)i@2p?vpKUL,Oc`4Ѧ#O:dY=R/YɀTK&ۏk>SơX9d&WIG4̋]&M_N^:B=s(;MnE00VRE [Ϻ_\")X5$Aaˣ_YwCB֒Q}g S!Oxzzk{0/!D5p/L"( a0o7PNk$;K9$YpɉL{hl5ɡz4j-7醘R=Fvb qoЮGݠ+DBB9~v|.O;\+[ M?SYxeIJ e|-A*wldI;D^]= 'H5]a1Ƞ52(:͕^|_j" )ڣRAJ7`KϠA13:5V[?/xLBw|[c/c͋wB̅/ ^c?bA3Y:oXTLX]surk_6 sȕ`g*'5@Pb?ҕ+Hpŝh5y 凤} jy]Fܽ ?(&` _qѷIfH-ݘwʽ}df Ĕ'6a ƈ~NI=yí@Q{ ?1Q+oN 3\ȂK % ? GqnC](%THB4;v3[)A-Whի7n%QԡgMFP5x;1㏰i6>y=ig_d .h4"N.X,u㥠EdKTE#0wOoWnˬ/`qpvd+\I'Dz$ e MЌv[.2i3cD5M~Tx6N"Ÿ;_ , , [5,;~9 Nۿ~yD8e:̌ wrրN0Ńi dz9~rTyaJuv yp5;~ {̈M$e}<- e׾Sاq'[ѨTϜ{ N9AFx'/q^dhD(vB^?OOÕ`M|_AX0gl IWHG:]WF@76N~ 6 7ovgALfmrDBⓛ f0b1WL574pI6JiiaQdn5B YlBmj|훗7+ś* מlhq2)F2`}+Zd!3+aSV¯h`VY*pJ D0w'&K+Wf {L=ix SgD|E"]bD*>$Y-",NhXF {9k:EyP>g%Ҩb?AKAڵXJ5 &X=śNT$20y z2àbBƽI3Ȝ'N@ g^w*!cȂbT'Iű3'C.L|{f y=j2db2";q%ѷJ^F(њI/wzf:mgL<} / 5` t]g(^1ߏU{)_Zr!~TODI9L"=>$I)wvu9rtw+dXZ<&t-w%GyWjZq[PtvӪoBsxeJɆޖ;8hs*s$HQMZW| }udE.h=60Ob5\UW)IJj墔Κ.C_@ae30b8ŽM 0Tivh/&+" ;踜qTy8ow-Mn ZUB<9(U3 N~*8bwDɲr'.ſXSRY rw!mBDK&l5Cp^HlN,ń.| ʷ'r HS㴈9 eKdRvuE|/a`]ݸ qHgzͥkLsͧ~Zh‡'\N|9UP d)O9$B"Z嬩ը%|Uytc4\kxvr)z'ᥘ-نŰSc7 sy~g .ݕ= ko#+zr,JAi:;Q? J@9j ̏\Y #^V@z O o ܦMÔʓ?H>bi9U7.6Vv8QbдyߴtY^mqY^lɇ!xv6\73-`L}泼șɛT}˩x'O)"{1 Y{4*ΐg̝rZl|P3T$|y\8 &T ͱXAR~րt ̹..l& ] *" U""Y*j_: 1mB7qX֦ ȱPO 5+ntrf+ f_X)S ݔ}o1mGWp_l _NӮZ]=[$0;ڤvƇ0x9st{|Tf Gh$Tm_+gdJܿPtoCRjTSacX;2MjabM _ e+CȂ>s9J6oz'Drט ę$$[eӤr?ajw>4 Rd/~\ IC[p ;Nh}pV@|Sݝ>Ү0E"sJ5|t#!W)к!4K<`~L,op~{=N}StD9&BI{`q`] P0+X-MQ_9GUi-0!>|Q >%rTu\JI!ŞHzPM "hoG'4O!:j.|4ԕ"no|t _q]}Hbqf}|^(h4ۊseGtq7* ̠D7 rxf竰 NS|?) q1I3PSQ=E1H)(%M7]Ɋ:}55/ |m,F$ec\5k^j4uV0 ˪-Q7>b\+vK9%g]OB3|gJ&ĵ6"^րe$h kS.+Kذ(ޯjNWc;xN'd_f5D5uwY5IMUtRO<~Fy 86ވد4&YCB@Xs;& m`fWQtXjry G %n/HZ̠TNgI3Gvr%.޴3Z)ڄHexT0]1DoCѕI92lX;r5~!d7L휥IOi?;ʴǑzpHwwQ]fǯuuO}^x ,v0eC9q&xJc`3LT 0LߵvhށyV"o;oc~ǠCkK[`__sǾl`mY `u e;ӄ49i~RF mE 48b$$U,⶚,alhhZc?>K>ݣe@Jb`_ff65۔uh[:QhhK}s P,; Iؓg- vgUG5A ˮ_'} WݵXN"z dH 19)'YC.j"W8Kc&nU"hz'ʠmϟrqTy+x (PY  Юx.ʹI N.jpD}y}FzWgMn@ BLHF{iPMCߓ/cgno^, x:0ʲm[ jDįahQ(y`UrfUԐ>Hs=P1}0-gh;^ }zS7b>Fٮ3SxƩ 0a?mRC/ᇱ'rlسNĬK?.q ,gkE<]bF|55f{N0jf Ή2s43~6F,ґ~TlyY/e0)Kp߄5Q ٌ$]-ƍXTS%9@fgS)~hV'*fU滦κ|^K;] U.}"Wa>HY xyRSDTz`G;c 2F/;06b) |e'中rCL7L6'<^@‹ A4J[mCZ^.ZdH[+G# q.Dq]eu] Lig%Woxu-\:^#g!JԒQ~ !?DG1(W?k 8c Q1v֞ / . "<7Gho'!ޠ}|YI"E`QW7aWo<^!r=Զ"$-wn*~:gɹj#O0ck U8ޖj>u%[+2wG>qUvlt4!ߝ*Hkem` D}ۢK\P "}=JLS9 IcED3C+h\`1cgʺQ"4𗟜NXZnknܽS0Նf5KblĪ$\ۚmL.?PQ5ߦkF:ge"=J"Yt^w *Υ @? S: nL袁p) ұ>%K9iòCǮR8fn>Bz_ ':>s~OB(FP-ʆ]3WZyZdiьv#]Y Vp33_#r {dsbӯ#[{Y 1vp6.+䫔F,e\^'k N/P@CR1u%q* dНnȖ4ckh>Gp/սs뭵^4^*& 2T7U24U hq%XÄ5Wؗ#2/]Wc~ׇ)̤Lk֨RKfJ/'mDxR0'эjou0XJwҭ=êJw$[v1YXڢ!)}dJ2Bx^^S25NEftgUZEIO̵zN8I BɎekeX $7)E⯖]rvp ˍ3?eʠغ䡯xQ*]xŻp4,"FN80&eA {,}TPUkk}e%`žSĨA6P>TRqO6<jQ9J}.\#ԳХhXرF0zP5ǿV?I5iGqq )RK7ڎ<JI ZK G:M\ HLuz U#&E@x0>t]t6Ųޝ{nz$)2RpL?'"OrZ|i!xFGk??k$; 颩lGĀ8rRQfE[1(Y'Guz;=k!?zt}BHM ^c$g |*"jD‹ך0`2!!-`>v9ɘ0M1Kʏ&? I3qkjK6^:%:0(C s~*|bƏH/zH= xəy#+N |:pa߇5ώO kk&plbu$ 6R Fe-McrVݡߝ NI"eC r+ 8[~>T<9hl'!8@@9,ڛh11l]R iëv54;NGwrE񍵗© y_n'C^(:xST% yQN:BڣZF;nps{u SRKLUVɓdF ޕͨY|={d&UbJuͿ_Wi" 7]>oIs\$CNu( XxVe>5usi!֠D_mg3 n+0hIC8i|hcʮya8ͨ\jk~.mh."ޛEQ0}rs/`ɦ^t$#&VQm}Ұ̵7;f|KQ[!貍pY-7:w)o#:!zX}Q(.*K"^zJJ3v~n#~oa+MΊyt`Xc_@e5KLcSʝ qG7i2M݃g94#?݋'pȪbq@詛Wͳ)y5\IO:X -%lE9ƂZH1N]Z0A Y*zL9BS#k2U)W|53?5ͶkAg9!]aRWفr2 b2~.7$u%UR0gC@ 3W@UwCED_] X<}H*:bQ/׬itA9g/ wb15Mcjg& '7Vi}5 *U[Hծ6yjWAh$\lI7hI8wj'D><nMEDkQڥE0Yx^lãEm*r4 XORljuI YD]~p☦v#S۲X=/ьG~A׋lÎ "'lAxp},הZM8W|A5I2f8OXmu3zƅbIƣ#Pk@N|׶A?drDv~>pخE)si:Vrj4mg;k3ɨ缰 e"5LmԜu4l.B)z qqyrق^E`ͯ"{#,@A|^z ;k{!Uvi6 gss247wOYsHYza8)!ڐE}=vT%!H̍.͌cb_ھўAϣ(W4y8?jU5rZ]tDz~9e/d{6oJB{͛*JZhAt{v6dv6"hF=TZp~(ZK] 8\&Cǧ}!=CǨ_W(6 k+>}H/͙QZD{|pOQx[t^.na{ g_ ҰrIT:nT0!xb? SV=pg4A )>M %i0=t|Se` ]-IY (TqўWş|y0",y#* niT1=X0yߛ7a]ARoB|q -++{ѽphWʔ823.`2ygrRʂ:K:\*I Aj8b~[c? -!Lb0VIH[0Xcv 3]Q]BL>:ў^} @}8/|wܜ"KqT}N3toQʬ<1*U3}Aĉ)hI&a0l߇5I¦Wu>;x!~y}ޑd"ݙ"JU):x)pf2G"|4C>!OhPP`Ty/ *zmd v {/'>SI$$].&"R]SN[9B#Vzг4 F*̝0+:qvI,6^dʱ wrd=bh{Ccu-G' 6vEüMoROr} U /*$ը3$)M1Of@-┾ŷ[J$)+ebA*&җbf#O;s` _0 7]^=qyl^"Vv6*_`A* .cb7mGȔ/T㬻5XS윍[_y$@{MȁPRįqPC;rtBxT%]x .-Gnkbwc V7!rGv|tHߛ$ظl_&)~]Sp܎ƒlCt؜R“S0Q|5M{ n`#с!BL|;!V4 y|PȜbW5ci h% vzV?$mjED6KX_g/LW hِ/_k߆/+{ v*Q}75AbDC9 MQ =#7r^^JvH3Xpcx*~%L ;rDZu hbUiGr΂̡7Q XLհJ3܄k?Ů~+#}'*I h^ eWnB~*BǝdH" A|LZ3YMs/qnNȰ3h#d#7[1y0QRO5u9.&~4#ןn4qJ ,sR䑝!4/ISfEȈr|-p B/Y} |)7;ybk;tvx(#iOo&-*uxwY)XFN՞vn,$NXH 0HtS<$]+TM>¡PD]yTÈ3ե"+?'o&MK}yNP(elr&n3*eU:uྉN{pP9UˆOk}%\L0^^A ǔzɏ%o{  &c6VZli&r1W" 7*==L0U hƭp`)ՀҚW,LRNgτĎ{M'\h>1vt(E=A+ѓ!w%RD fFC=hŔnink\bmDIJz;uC)~8Xqg,p IÂv6RjJThaˑ-xLϺL*9'™~0ԒX¿, rӃ B52{U5fA'rWL=f"d'tw76myhuHYeXAo݀-E b_]Ք;'KGǀ=jPy~L_o'E|s(=gە`!r4Ϻ>GX+TZj,h1sZ"[˨2ű#EāFxXJKyk{e,Ez+Jl$W꧝bCw{?,@໵):WELhCr pf<T[&tW˶K$D'gB -svyj>pb5}?!7΂LXA x 휹SXAݜ rp*8:J`-v6}uC$!z*HпXnHhiDD,h2ScNb. VHdD'Z~OIJ^"Yo}R* (i`gq^oT]mNo=u2+.`FC8xVF=J ?ޱƠ:Ӳ/wP}w/,$. b#≻ (u} vGqv-Pb.)}ϟX"O?q1?PMOTFvX(\36nS^3ub^liLJa'MM} ?{JqְA:f+,F4"r[qW߯aLȜ.] }m6,>s< 94eɟPRI!rKOy87;97cgHgq\z?avj2GzpZGxyxY=!fiQJ]4fgu(ͺ6 >]Sf^n:3ҨL_l^SU\'N,`34WPyDII9:9Rk=bsyLS=hy WC~ ؚYOxy:W.ѽS⿶: B4>R?/q%]>al?.`K%D'~#S#=ag''?Hn5i6Z4뷝MDVly!wTI}pS9.:Y~,7t{d56>Dw)쿑+@F2Ny{ 4F7 Aߌ)2VQ+XGWQbd0ZV$Y![A x7Y2QjHDؘzŗdѵ(SY:-7?#Swm m #Qng`AM bnXQkrei1 WKL|zUPuplaDKF$WG0@S@9|O )%( diʎ+u#-ϙ! v6"pa\Mr'IԖN݂ZX;`)WV2㲮7Ēe {ݘglv|rc{+g:S!?Y Ո]"dMZ e5ڵoMemsapłV2t%[>7\(W(Sj3~M,anAasfb"0ѥR:쒬 lBW,Uwf:s\G{D=C=WEfNd<$]Q 4͉):Tط gre^Qy44_} `]ժz9:C.jGUKBꊫ)* I j6\XZBߴtr-32]VYi7 9\ MdXkl¹< *}_b62Дdզk+Qt2|/Juڷj pMif`n}0,CWh Ge:jb;JS3c&+sIV@Tl4z2рFc5@//U%._c6I-T2S{j |Լ-N}57&[Kk3ڐj$!LH֍{ ֶE#ayXuOwm;쉌3^l"0^#?91t֛d~d-=@AP72 u"qKbO83 31aIB=cZ,፨1L :QLjZp+$0PsjHfWaA||`M1R%K(:3v8 f}q|Ҽ(Rҟ f+vk%} FT9lj #Kpf@:ڔ;|ɥ[ b'KąØ5Hcrp= |Üa F]׍!ρ1G3<`wdm]-ׁh v<,6T5zV9Tri%` uu 1ùl2&vL)" 까:y貆*P-6`Ƒ+_q`$?p=հz[F̣LLgtK4mh(F7Ql_>^؅J)R :Qi~܊ ~w<ƱdM<DwU?Q %` tRE|P4RMϪ&͡gC zt"Y$#D~4>F7+ʦX| H ] *' SͮEb }5>dr=Hd_׊ Z?ujgu.R.^7r6 _=-Uv v 4k{j'෪&ÆQ:_IK2vzZ'WoS*g5'M/# f<kO- A,nyRe&双aR+'b%HhJ M]F>OMgQ^sx C"t7FJTYؾ`GPCD<*-nkۢF}@r\9sc=!{l;̈́Z"r|Kj%rhvf== 4;#Ҧe͔SPFqFN[sS;(3t%tiOHVoZ.wsa8&,,0b`q1ir*fCqn (bwox-PH2+DCWg ;1y8hΉO/mM9RHQ7wǚhLQ@\o|T0T|t0WiN-K,;dc!F*ںCgc `RT  :2͡[z*zD֑ M$ *3N*kUg\ \6̍28^smt Сa;)&-9Pz; m>4ߌm'3C/Pycq}ݳD,+ys]B!OR$>fOli8`U!J7< ulqJG5䩋2+QW[ZFBkKKh˨vgM:Ea߹xmPJd pgk1Lu!bI_v?V6':v<|U@?KEϜe?D*NQ@HV+edR j%- oT5*I5#C$ExFhdESQp:܃shL*C({h@v>|5ckӬzf9\rFFNC-hwRweå<|8<Twr^l3a؝mBP+GJύkVTDEŎ9A+)Q,ޟL Ukg5vU8xVb4&𧺟 c\ _֜#'XsWT~=pK喻 HxgDMя >L'CI@57MK[D OKH<԰zÒ"O0[_ީ@RC#n4~ai"5-##3CL]u B1?A? u"`N'T$q$_>~$V)8x زҮ]Uh] 5l1=\6|4}47x?Cr5~%3 ٚY;΀DyўHyhܦyE<'(= )U@Ax*I:f7!îZ}}UB}H\ {BVŨ:7cܥ-kac0g ##C׺o+GHRd1U iJQ|guq?(YQ-"(d*tԋ&GS}\$z_7%׭i2.UHkJ7F30h:ң lV JzM͸~>>HYٔ/7ɜC8XFq\RHP-ײh |)ˢ8L%S =Aڀq>\$BFˡs;pxvE^SL|-?ޤ_]3\ig"S#U 눢1_E>!?\\Q0ĥZI KJ&dL. lPL;0Rg&2'b(yxS"EW%gִ =z`E*L 㾁;fY4v< %g8&HQGpf*LN,&͢\ szbl4-k1٥*6Y’1T ʔ/\AYSQ cRن5mh qA(ڎ<[]4J[ǘ`JY!%pDp+cυ}h&F8v{j`NRRemjO'ِL p!im ,yLL\q=*@ K'#$MșPF(tdZ2 Jt@ptl秄g5?x=HFKUVX?+\9hV'nzDL{₡jRG^n+.(ZҢ&N5Xx ĕɮi- *h @Mz}*0,*tu84kˁ`U0Lci$nsA%Q۪[yKd pFFp4#wM7_b=d اndGT {sp>7600M C#8ţw,0Iu3xOŖɐ@`_ pUIRm؟3ycR'0s +:S-q2=8P{Z+D>ҋ3~ze)rQĤ],։J_eh +OYqfF8J|󘪮⋄GXsMpOAuAOy@C M6ϗ@ܰ+_eبq.> w`O_pY/uFj {?ciҀCZT_kǂpۜ~?2sH" *؅C2v5ChfHt,CPb= ҢCuK6?}RTbNxJ>9D!1і`mGg6$; go;ua5c@j„YkIXم٢_&{XL< ,LKl/zM@ 9'|>=mL{ec$Əykd< P2â\8 VK: d&Z>;~jñ#v\Ct齏sQ[+ֵ? 2lrДШ3DFhctV`_X>z#w`筳c ͉=WH,5@W用!'ZwϞS'|G@s'?F1 e4̏$= ^5$/&7''u 4$]ab~Hȥ# iX> q%$2Jk4y>&! 9sOpGqPXL?Tlow|'[@܌L,2p>\23W@~ ׿i:uTw3MHY<>Pz)NPx?xMٍ^8U?Xy_7U+jDb|s'lC[ʂ?Os-6ĈeqlyM3VEKXD (ˋ$BUzplL+dN8<K;V:M&r1[o~ ` nI~%MKA*it|&ς^~^e6P"ފ1Nm@@F`MZ`)5g>l&dބ> Gc[ikHa.&]+L>;}y?p-y]\=aI_ qᡨ2f8OYr3@J .mtO蒊4'Ce@J!!ƁS5G@~s6#']ǁGucvB)e}l,uY+=cf1|0CZAw]f#TG/zRJ2 f"lh(UVar<b`9] o  cKHS2I4Bw\>ԅD͆ 埲w kBt!녈Ӄ(9k>oyU]rU6r U暉 rhu)H<W Np7z)M+(j /( 1eHL=_|XN\Go&F%VGcc! ߰&8A#{&9$NBw It;5hNh<LOf7%ݻړ<=l%\B0fLYI94EPǟ{h$~8%KZ^Ŗ/ً0\-Y0"h_AA\II3dS+f4;NozweFXܜ:xMMtՉeYG4壴ͣˉ7n#8GHZPa>wRqt*Ȯez4xFDj !ds7R; W_E!漉 s'$p&;~DU+!UiiVg *&+Nu1 K7xOMX+x>#RY!zѬ"99Poh5:*AW@e#nyU\}3f]$ip"M9C{T"捯g&/`Dd;|xt 8o' _1~OH/a{j;#0T16|K:IQh58/8**!/#gcS\ʬX9,8D{ #}j[ڛ@yI4Г5о!򣁗LS(RDWI2Ҁ/G!`xVUdIhtfib.2e1"XH~o1GZ<j{^r hfyGJ0¢1{]YVt0䅂Bd$A{M:F"6A)6ӿ:ъU/q)y9zS%w2{2'Mn<y+1&e|W"n3Qx)ɹslQh㯖ndڍޯ bD< UOr|uwM۳ cmN8r«ORlz@-6JN.zi?q{vYxW/&8]8lpP(M(N!2?vd=9ؕ3R%TkE%p5#bSim}t@Qri,U1{\:v׸s9Ex<+:ܠz gl,敓\xxq ԛg.H#u`j)./5văXVnuN>'+ nr/+}&7$ϧHr}!~$ 16ʸ^}-qx2&{%ʇh! OBv&CZS? /&ŶcdR&˙k]7PzwSKluĴܿy?RT}^maU]&W`x%Q [^@͓ġsQ?"mK@aAtb"[BU#ߏ!T7󾟆:]O R T 7LxBJ(g s-,O;pqVU@9E4sӦ/.34nw.%'v/0!:gWt? aW67.G s%L۠ 4  Eh}fŻ(V-HI u]6}r"nLK 9~zkp1_ڦ̑SGf3c8f anL[x+TDB ]-;pk:el*IU?@ܰ:(]lPbkmNv@SJtR,_Ql7 ^tyDg IMÉ])*үvbi84u߾3yu{g5} tF~Ta gjȳ3`bV|+ =OGigoV~֫e{Isa]ZH= g!8:f0wf챃0rXt,㤵['8j#m.mlSbT4RFlٝ KP Ɯ9.8 0ɴVSATiW=-p*Dyz“j,QJ6)^%ϋg8bN| ge WF=| m=;"Mo&Ir'kyK+IqjU7J%'䡳vu$gn4~>؁dBapvR҄?qR#@Wgp}1I(`Ja'xIFtc> &Rtqr ~gNǪ~ΛO+wD&c_L K2JpL >8+,) >Tۤ"ڰsB8al>g<@߉ㄈ2~2 EFQ+04NI] ݒ{W1ѡ g~Ŭmbv.C^|;(/k55V MQ=@AghK7.LJ|.V2N!S-s[i܅,d֣u Dj_8jFc=Ԑ7?&qgHf8ee\&8Y3 3[ 7s\@'XRc,e"b9sJl%ʠJЧgqE=¢_ >1*wW\M`'ތfI:f'6~: 1|?<4@yab@eICk:jaj}캝Y|rs1<4@[oK UYDgxZG27ectߟÉ8~!Dqc+:Fxw aAf|^V^"lGMՒm4PfՒ̉3WWfG„a;9EaǞqF$;"ӧfK>S 6~A| & ցT@Dg@+hJ(#>!:|~V]k[qNxK"LPL}54ZHR5Ȯov)>D;u:5>Ne sتJY Ds= WʚóoSG 0C Ms ] C>N$13waf-)-Q]"FvZ)hhzq/bHpQap}D@2~; &=},ǙɴjWsaJRA\X5}E2̇pę!ƑFnWL]Fk 8u;e?H%O> a$/_ C+6)MFYJX}`tXtptI2488=V́pR,wzJn#vCiZRDBYRWQ>VF#i;nC}Zɲ{pt+J=(ΣB+XHsЈ kW*sP{tS_qLJ%/)p|Yɶe9x&+`4OU5ھ]R}-q#95dGBذPVz!&sK%+Cdq穟b\՞p4_8{Y鯕pE{ 0f³ѷ2GG a:S5+YʂDq5:'ܰP6GYFJoH|mxtZ5pB8-??276a0 )|s^H@foq+'yuͨyBQZ-_uwڏ~hXvm|{}E9"UI,FQFr> Ж+.{6劥bTd%ޤėkU1alL wð MdKʍ98 z$|ƚ'5/D7&G18Jܒ,Ԛ gĭAΤ]|ӳSl+!r/ɷH;+ jkX<1Z3PMmNȜƙe/ جbI{p bTpwE4Ccډ/+l$ кHoyrO䊜vjBQIk3Mzj%Ay`K7!c"m7 CXq۴,;&ܓ).l7x h!qDIøtY!gթ$|_#N6Q5t_':VmVn,G`%ǩ19KMҀ}hIתּq *~*tÓ0HNƷoXu㚂.I6IH~>Eo@qY {PA%l"j{ͮrFˑ (8h :GɲU(>I*^db}]sgbTfIO' ص DU{{M Z6VhRI &ea~أY;:l)ÿxiq2YZ1yG3@2y+oP|U\M9$û!&h'#{wR36XB}zqchu$=Y )M9Z&\Bv'Lmdž ЇDNKv  B@Sܻ?M113%#c B*/6]㱭 _ _.(Ym.)jxL K#~d`}Ϻ+p_޳(hv,fu3 1%D@k!v}×dzˡ.U09g,Qhu@6~'t?J -+⋑~EVk DpI\T T+*B*^\DX5eua D+>t؊ߢsq|9kf@+ʨFѐȠDfjƣv>FjļmH47,*ʁx> =r!;퐼4jb9y,ύcSpZJbF /@g r ,B}?n[jKY{\$rJrޕ{yƔ1%}C ccS穈 )7Ca.?u31" a%%2Wq[nLd<ЕD|-GrJ`1?][rDƖ1eS-ie@.{doLB7$2z "M)`3sA0.#t0(⺩;FY.d.Jʬpt@r:Uw 뽄t^sqy烧qxCQJ}k J$3F.ލKw=%I-DOǒꊬ5 M p+wѲ~ χ2YĔ'#xLsV@PewVrCu4&?6 Q*k&Ԯ̟[KOI赽nn=oo p)ݒ-_ )2'8 D1)N@rW>s)죺#fa ?eXnmiskژK9Y2N-sQESP3}4ЕINkr[Ԇ㆗WB<7xiݠ4Yt}]Fq+\')E%0rگ{!fU, ]wz㽶-)[qʿ p/"kl;柽 a̙[) dgKEOj +=5oW#4[]'xT(ZjmsNuF;AzDw2(9qرj$VPۻy`Y^Vd|%bŴ.CRk{^De@ꅩAFBi@U=IEtG$[!҈xY'#:M gڃ߿ G"#%Xe VT=j T[A) X[59t b`)m &]0_w ́=,鍒җrM,Ds8_?pFqиD9aޞכ,fG.7ҙ?B':3??X='|(-t;:%Ft-3WAj8gpʮL(,;bw9X'"QBu8JV@ ?[ G^9{ -pmRSkA a4K [Ehm ]>CքzbF?X$v{,ͩo6sdRm x1@&FT䧏c,"m~ʽ(i gaw6\ #P1$OhG9+i4ܑu gc`M07RO izz= A8q)rE}qKFgoGۖh{.5p'M *!66~ pW@m'<&z"blێ?՝hu{L&@*d )܆+`Q]g:L {|3f8Yk&%x ԲvXhY8RWP:-!pVs>2w*w[f6tyEPWYP̙OR8۰0AzZ)Isj'Ce.ʼnZ = SCcC- 4vo^ 4YBDB^JjQKT ͛ޒ$֗3flN]TE-QӺ|?oyMsLs 6{h«*%99`-TXvGL$ԑ|K } u+Clr !b2J={v! xn|4A@ʭ@>sbt7>sC Yv"yb/J{ɋ.>8)o-Uj ֜,o3٭%oJgL`]Z釷{\@gw ;'U޷V{*ywv^g!{ .׌/Gi?%Rsل^o RP-HdB)&,y3 Wx] }5ʄ }>ί(T4k ( ^]R`mHv /QLo1,0)>޾*7vz9+@@YrjbjFLf5)tc!:Yc]kS~ 96NxJ=c#QcXzys ol_&H{"x]D Qa@va;.\O)N繁SOxEcCqřbrٌt89f-X ޒKԜAtkaE޷7=Xy4Wܿ!NJkŚ?nJ(F05;JZ"JṼ3)q!g=!|=~$kڛ-71ٷBwۆK#'2 h8 32\ qCl^@W4΋@ҙ~OWO5VUavYyo@_"(#yG-{V?<.C|Ulwg>Ͻ 9Ǹ{9I*Q{1Χ7I_:I*)@2 ¬2s+^W7!; {QARcЮT#8gޞ')0 vP!z\37u'ChxzL#_^ԍTHa O<1NPa$+B)izӮ~Kjanj51L<\zqHh}*R\wYWn@a^Τ jĺ$6Myv]q)dPB&Ym]K|T Cv-@7r.q.ԹR')CV3qfKQm'uϗͅn4ƛun*0CfxOZѳ|UԿmC:5%l ^Qg@ m#"+8 {?g6| :zhc`]8E1}D``+l8K~ݤ,H xR_*sV2ę5Z݉)C@V}OΏ_zõ@c${N7A ]c^j;l&'?܅^ JK<-4HFf!7sn 1%>ͤD+W̫;xl}|Iѫ]%{q+ JF""XIxmjбʂ*TO ^HL&[{QMQpu6pS~^onЭ!u ۣ}o]n '@nNuk&ynz|Z(4BiažV+CX S (A*^ R>1 "`yt^^)hD!<"G'V6ZsD>=3F B7^[>_ 2 d7O2 ޠIGa}K@! !g@TI?cQE*D:hNZ.8KgiD^2d<<}k"j76/RaA*<9&G gdV\Z(Mf 3X>sz"e͝tԃjX`?l&k*'O R:%ĩkd1m6Mj;x?auvᩱ|Ytx<Ē 4^ xٱeH]5-3Aw&,:fl{ˊAn,>^u-}ۑhZm pxTP\PH5*8 Yy =Uۯ/ퟧpdvy7H=@Ti_ 1ϯ/V2/M*9gNͺ6ʐ(d_s=yʧ~z3&3^ a-*"50Y}[ip|+9[yvS'Ô,0^`r\q'E SO^C:E&rSٴnL>7;g#|-78+1&$tc, ҊfNFXNGڂF9#W$I\.zܴ5+G:4yg yb=m'G* 4;eA[^>'OQ^c '~5P.6vTck3[Pk,* +] 5[XRˆ@;2ٍ#ql\=)Ѵz>W(d$rQ(@椹0\]&|iedfy3S]MEM@1=dٹa$?TU| = Fy;hh@|0NTVkY# VcndۤKR;E(99Z"4}-nWsdn{?|jvyp}w0Z8P6}K 㡕!y(}Y҇t ͸Ќ橖qA'ÃM$=Cƨ)q63_k) %O6j]~M1ygɚF/"z ktXhqBFv0!xӷ3Z_ВNhY[xz= e! e&wx_f C5md.։Iۅ#a mqK=D5D^Q)WGZgzV{ޅ_)UC C䨍UgĭMvr f#,rSq3Vnq(MԑN5~1fz4=mcw1*s~ZC#tGp~$⪷4DCGEeh-edNHCKHeyU"l-ӭUς? 7HM-+% Y ]H6t[i$GLCܱ2y 4#UQ]D8/>eKP)/1ٻBJ,YETTId쾫,rӛ&8.WxUk퉹[T_Ñ W}R"waG6HX4˭Xe}ŝ08bPm'( l.L.`^^woxerY@:6Vt qbGagdK2$XO1r I9SEo.sh_msG) l@ʗ 9,=7- M\t07D Ӟf~vA.Mƫ_"EdE&Ga!:ԣ,!y4$trx;H;KM Q NRr#9= FÛi읎{ ک5[x<Xc6;qZXtZ׭%G`Oܿ[/MMs[tAo5 c[Pd)*-5=}4Q]pCn/e6i!Z[5_qJcTQU hG%j>4ߋ>_:^Ø a1X'3 v>wU,yPΤP{1@J^sʿ+4SJõ M1`=lUHܳXҝ̅5, ѐ\&zъQjM<}䦑R̬Q-*KG.iBB^]s`r|xS^OBxcg"܍qi^2d^57(xv,"MAaS3z Tu7d*׋"[Aw&dČPdab4٭@jJG}J%G~W$ ᳂ vs`Ą3 $+M' Uw h`ffOXq[4;fڞdva  4:XWro=`ɞ9MJ iaҤ+ -ch:Oan"IJ3[>SaJr,zeIUeݛ &eu7tů>>MDihdžDSU,7~ ~;e׽mmuSO#`P-_ gİ͎x7fGYZ/nW?Gp{1;\9 qFmS|c+nxD[vjKQ:'U֑+:d49EXs͖ih2uRi*w{VU Io$pʰ{hUe|tn/R=$oڑ[:5j!o"\aNy9?:yC#X60NÄtHy!L@G1|D Z~qQf b(6VMr+ةjޞ]B%Ezͭz Ť8KP_.eOt9SaE"Y,zzdu@V"[rWXKĖ^9%+\{S9ŋtmK8*VD5g4׉{a6H*8vplb]LW6Q 88'rkּ.,[ 7#hL'Cgr'|PG)*ԆPO=Z|? \`}IYt7ϔj?pê6,y2l. qs^Mz@#آOMқYTQu:Wz_>dU9قD/QPB0As?dx%,"yhHP٨4mMSE6c_b Ñ*h k{9Z7rVVk9^w B8R|@Mv<Ba:s898f}4,p# r4ea9pWHggtv , YyVl:u3RmR86Y܈(jϠ>)EF.Mi?lxW _ -ǔ߯~ɳ|%`,N%ׄH-K'Fvdd ɔ3Yi?VL =^|ۨ䐮 H`gINj5E/~ 7v?mUk0)1JÀj\jwֱa#QPIsX& Tφăo  0U@(Io8_E\O7x%#Z_7zwhȟ@lYcYmkA gT%J"Ov.nJ.6*D墀|ul Zp$:O4 E&/@./WT-Q읫Kar UrʵlVJJgRIS9vWk0vbzR,;- ynݾBܖ2I=a51hR\nd2{hZ~lC;ԏ.K.dJG&_Ӥ+VPgmk.tlIEru͔LCpx.8tӐ%^oԓQ`3GX\a_ә~C]W״"Cw]Â00åfᶏ>X2aIRʜ@`JfI "ET=B)t!~@NPd2.BR$O<;3c3v+W>e@_G GsNvH;jN6~WNDR qy/v1R0؆)raLNcmS.uzSd$etE嵒s]7ɿtMe 2|nypCC R)ԱS[&s'b4د`^87|A`tvj|v_w5OU=v!3ô0torF&c2'P,¸ӪŎf`ElJjQHL f;U@5v6*=3 ;Z21K [(U`|B럓A >9ՠH;{KzxebI4sn{j;o`2~4 U'i ˓T][۶Y{oYD"fݖ`žwg5ŁW{^̰-QCfªCvR9w-lCA*Dyƍl6-0YΖs'pg K[~ʮbr $H{qGW]9^k mb,XXvKvwnxܘ:KO8\2H57M@'q7NAzrL@Xϕ^6M=9~=V- t|ҭ$Qzh03 O]Q~K3Yѧ˿jkW&=Ɛ/UxG, Uq3O֡2ʺ^6ɲ\>i!i=Pm`8b{1Y9M?>R3dW5kd\9|42PLl?uB/X5pC,LIҢS$ãUD*<DWq%DGi,7Ѝ!֭m~Ɣgb02x<@Y/˿ފ.Jd=.̲q9^vBbv;ĕ+/6" bٰ-J 8MVKJ &8ua0}ot*8jm Ȟ.=אfJ~/y~AOs\ϖ+ Oe(͚؊jd^糃a8k_!n&ȘMChu|T.g98DmtYh7z9q r,'^1x4ډ'3\llcf~B9V~|fBp'JUC,ù+2l= "4 \GHia8vi>t, [pi;$=T[崙Ѯdž̨NRd&jIw-ĦDVT.Ӭ=}|1_iCqWͲQ=CYr$n||N1s{^fG4WurР- aB`լ\H"@i+ pI!,^I{io)2FI^|Ԅ3bffزXVfv(Yuâ.OU Z5%+GϮ'&ObfhsG{I96<|`lGחy0Ԩ5`kzƟZqGE5uk 5$ZyS.#ӕ |CetW C UҢ/qΨ{HWɞpsXRn p/M0 {3k=S['&}e4b( s꣫ڿ^+Ν8/xr]EŊ>M8?팎~\Р8:pPF-V0Q-axX>cᖥ+RӆU:SJcF)-2M F*2`v ]Vhjdz95lbV?/Y?A( н x8d۩\Y #߁[sP~j{bԉi y 0$#4tX3T'uk<&1)\ФtXkke@MgoDD,HjDj2q]q<.:fHÜ۱ug X 0&Ůo ՒNrupR1wY0h?QOӞxz2Gi^f'[*GsDϦϵںg*sZTilܸX5&JwtZWnuQE(Z~q] .,j`;V_osMɍCTjϽ-c&y\vBy>%={}є ^EJv`)IukeZUй)xH1p1pJkRحuw*jEe|% Qfk.aOgPso)Ԯ5gEEX΀[vFFy~h9pXY>\:@\kMk=r{߻l5s/<&1d4y!1N Tml3/R,pV#1l X@Q:?}ͽ/,X8/}v\|35Z, f^ڨL7H}F{^9KՎ vMUR]Q,g.C0%X^~Fѷ azwBb'B?іݛzFYyGT}t"uS,C\D1GiEG\}WZhW&g:m+{Xנ+g`*fdFVtv$3?b <0Aτ] Dcg_ 6? Ew%1fG,$su>8%"d%m0b7BUU5T+7^K"4 VDSA.>6Oά"^Bʅ E _¹f13MN*}aG С<\וU6fHjJ q#Wd ;LfVwxSX8rO׆ ]I8O')8kQ^ Mc-d~jDCFkM(ٕN.O0OUG~Ѳݳύ PBgiZQ(n!RHkWeUȀZtL ((byȩ6ύAN諾"i_[ZӄX^靸!;HIdIt7lK1cyAg*W;"W87W !&_[l_AQۀ>Q3*AeL G /IۢCߘkQHieG +0C[#t)b_id=6Wr`ݝc3S,B.hʘNZc[+[{Aw'S#? HAUtXClJ<@惋zʪPxrWuq ;eLVqH&kgIp6ګg6 IU'h*"  \i}d^z;֞#4L`pU59wz|{fLeAul1^4#I< JK i룅ji7m?QL6-؅=Su0 ŀw +:D7%Ds778$蓌}""_qq(}w 18w6Sl S0^I.Iu`'A\4@澡6ϐс}U3gg=yT[U'5\!0Y^{3 ܎8 uRCX)d` +>:\tɘ% ~GlHC"=b ` 񞥫1U s@t+QƤH-z)i_Gƿ2O{amsK(ЌgS5\}uع2QA4)ޒWhP*=zI82a8'}:5,/"%7T4M $ m p;B%D\' @zP^SDY5СFz a٠veBdj$_ ؎s;Hn h_bB,1lb62LD}p4&7MElW$?&aZ* 26kI,:wL[8>7%Mnߥ:'Z5|(4N5ca 6aAt|v!D|wCbsS4Jr Skh@ baЪ `n3 4&BbZ׌ %Sdb8C7h[amҴ4F34fAӨ Is֭^y3O '{+c(viyFFb>q<jTkǩ:h۷ 3jRBRQ,`9k)8W΋l1:nd DVZ٠% )"1N1SP&f N?Nܤ8A|,I  ʥVtBgW#M3Hאkw!ʼZPH8hG]Ѣ1}eC>;器iz)ni~=44݃rm 8C=^!ӄdxNU!-S3. R1o-^t(j,ohԎ/_\C~AyO@ иtw'qwsՔ_`1Gd {iE`YeGu<;̱-~]}b%e1&i.Ql3ȔvL婶ie4~Q(6,'¾N SbN18(v9ǹWF 9lHsA-!^xk@k~N;!d MYY_Q%2H~6(Ų/!ԛg2/o9ml+nVM-lM('.7Pأ(IN&x$A6+f@ˆÔZzaAltЮ0N.tx0\[(#v&*G9fVV04JәFXCUuD+< 5A/R<1vsÅWDAj,oj'XZP#uC$JLpuZLj"'ٹ|A0̿2ۺ SC L7 RBVw=ۻU ] V*;}$J A8d H.jou'7%[D贞pr~!%_'6,^^y`-`,|Tna$j6ީ=MYam0GE|#ܻ ;szLc1@ I4M# ۢ!!Fk͑[Jmm M|y}Wö-DJ@ᮆXbd6Q>M%-43ṣY?_܍ zfC޵_U:8d{x Y7GmL\7+$NCZYA'mi|0s S_]DaBMډma4Cg<2W!5S  ʴscRD_+!Sw2@k6L/_@QD&MLM Xu GfAYFh#΢qPlp;ĘXgQ|G@/On\JOZH9_7WJܛS։9S4|pgj\YAq#ˏ! Ã*C"f\Q.=dQӨ}XXG[$a2yLQWQ;Ͻ&%jA}DsբuUt lOK&!M1>#(^j6cFun4x\c+֕a ;֐nx__YDQ ^-ot!8hK`IZpgDڌYX^=>x (_ ςـ?R#d%AmČdG/4fZQ|vB"nq y V̘ފ/WYy+Ws0H|{rb<#Sd-q[ee4l3\iip"PG`Z/J+L $#ݢN51+S5> CF yĮ-f%Ϝxf'k}\lCH)lҕ7%R{! nuIDthCH{B+euw!:<bɗ P)zS(Cz6~6D'2S:Nyv/ϗmY52b"2Z{Շ^Tdۨ溟|hؕR~Nj4W^uE<6%UnEg Ms\\Fz""`^sw⑬/dOBC4J8Vp"gft>4(|_w*Y8rfC4xA ݴg~}MMnr#e5ю>Qj;D v=ُK1I* H|ϱ# Vy?W9b*$"b1З6jKs{XR̍h"p\&R30~ng?OE))6):YMOp[qaYI$j‘V?c PZz%VA [l%򠎔E@YPGt͡A>O*l~<+P!9'p3G~$@]C3u{bV^BCAxVĜ[I2%چ\(J!m|W=$uJ֩.ynN|`j $%˳?K`[|7A]skN;D>5WI1Zr]NTE М!L,tme,MLu#6JlO%)b'j ` /P';n8sa Nʮ˷ģݓÜ>Ěq]Z25 iВk#_,uQ-ti-";>Q,[A+޼¸nbBLn}AC0T@B9O'@H.h96ưfhͯ }Y͊Α6ަ^~HEw!"S@qK%'#86M7.e=rno H{AXPpoxj\R;hPe^ٶGp-1<~^״ -}k5]5|ʄ]lkHTN)7)<8OvmKM=çX37AK m[p\1@+i/EWLcñҪ ‹% ͔kd^Q:8Ud%ĘŴFz}.F.*t:! Ig/ȫHx;6>\2hz]QƼsnPJ\vDDL?g@qN/czaUz75[9# TLd0 Ʒzua [W$+|5)紵qRӡfsC/sAB;lgz/TXq(\&&BkkLS)OIp\[;!Ns:M^X1]66G×Ui#yو_@Ax*0 'i6]. }1HF)Ai k.}=5#jF^iHZἄMLo@ A5N )u^T\ !Y"FGə]" \{"%>̺Ewt̖} go>f-MuY|]m*:pQ%&Fΰ'\(Xm8{jvˤU1і[bip ȵq+Ɔeičs|2 },,N0GzI66vuǗ|ܨ ClhB[M/۵%,~&!Z0>#r IVUHΞGo)EuFtmO,Har5x7/,f)_Pej|_^[W u4.zՁm,ÜFz~F)Gu.룖΃6;IL8Sv'qk/T j'-X˳]`r]}ޅiGi7 y^ b{_k\y*'tf2=NJbj[$pug_pl$^9Dr/0Ii۠:;/۹{̏K7'<^3",ˬ#mg`| }`N+V;.\Z|㲑NIK Zp=Fgx"zw%V̐(mdB=<*[֚ Xw{So`p/ G.'"3Lй8d/\.J WmIۡs"&NaǠ",M0W {Ŝw u8.^[ >T sJ+̡d뚋~$ل Ouw|Fj ۯ*+.D/!9{wc1>K Poq~&*`)'A z (CvB5 'A3IǺ. s}`_Go'!'fTBInþ&C ^`R'֠Y\w[Kq˚jܖyI[j#&,Ԛ !DFቺiZh'72Ҕ<#;tIʀ!q˘4pspA5qD=D qE?Ł͸*…F#"{GnM:iJ<)k[ IgmfpVR;Ϛ0/iå>0D~"= u cD'3u*Iia`?PWo WSHG#|]qɔkS[/ݠ,6鏆M,R6@Z+3\ĉZ yWLd@^\L'hr _u˽8at`5TylFms|dkP ZZ"$&uu&D^{;e),A",^!ۯ:[v|5d_g&=(Uj@KDzw^5ʘ &R.̚a]ثuWEMWXG"&"0`AQb+k~cKY.u0:.=T}|GJs~1s[\/Ք3 @L8`yY*1z$IDV9bW&勼)堏JFM ۂPCRst"`ecd}/4}RqWJ1I@v%ugMޝ'0P\m񂹙6Y<5&^9\JyY"%a!+\Opr"Ϊ]sazr&',8x i[Qv1u`= -(n@ՠ.升_#n '&•R@a#shB܌8V_8߇o8eY`9y8~R [Y.XQ5pTMe(y-_K%JFjöDQg֞l۲u)vCq{Mjqwۍ8/-py<ɐ'ͫ<*կV_L]lH74 ]MluֆK9KdkmmoS"ڱ݋6}QꁫIv[}E"}tWIBjm]^+[,}I/lNWglG_m}fZ[h̒K+y{WzMj"l E$9MY˥tUr\"]){Q]٭V.(_dEyϥmbp yW$~Wh6{nG~n}V"/Q-5/mrI )>C*-/mk=KuS$Tf-b6Ӣ66WnO?(cMz,*sH]TV:hۖǶw%[]"BG|mHҽ"y󯨽H]hKzo:٤*f.ف$dEllV^MvlEF44}NӱھH74*ok[Z}jlSNy4s[+Ey}-j:j7.ٻ^6m+NȥGniu.El6gcciS/6ehmMlֶ|mAjwvm/X{kQ;jً]juG{^%^6:`cEusi"PצltMkPt)fWi4QK<ظk}+/W; \6_іVl̶~bc><]UQI/mt9֟V?EmZEz"OQ<>mȦVg6u!i}jnkG[}.2ٴIeW/갈v?[njjEuىNke)[*?ΊtNS6һؖKy&E2M;K:iCGk:)͆t\+$m;ڇɧI6y]׶m"w6e[y[&1Q+(zj[GEugSFŎBdNκև MNlmN]H'$RۖH\mw诫.J]ٕ&KMMw/Ս2--kku+-_d!6rj陔޶n:-޶^\mWk2O'ـ.EvT6-mjmEuPD[_{.j;Hʩ٪}ꮤ'Ecζ|}ԿT'e,ѱկzlH|jhzS{ڝå-lA[6d]lfZ+gnIMڪZȪّ+Z9tC"]̵mduǢx.2%t "3;T-T&}SzW/M껨mloޚjOF+qm%XDV4MՐ? lmGп";e[/Evٟo辔FluۖަbuhKv\T]sz/ylmKK=<6٥&>K4S2Jn؝MѲ 5H&mLk2]T>i%VQYlF/\lmeIZkuUTEmVZ[JmSQ2i`.%DQ[锦gEi.JkSW^kYך.Jm٦nAֶn}G6:-+HN:î~B㥵M{ٴqnl۴VEelޤvѦ޶NB&Y՟Mld 4\F56Ӿ1^¶ewrK.aM<_-Mlܧ^U"ymuN Etzd_uS[6e)_yER;DQۦt5Y6d+}V'>m`#K]i.+w{N?uӷ 7~5oy~y3|xlA#Oy37Vk˵ZG1QP=w(hYP$ 3=zy߹:-/؉ޞe3=[#j8hoϼr)amE^\=ygklHlh$|U>vZ7^~.oY=[G"okE+TO^g浙\;޹mcMwc|#'m6gov1>bM_}qg۝a6Rf˞+y:ʗ!%ԿtE:7ع/6浩r1E6&لTgEmG#_qlsEjK#KLiaI ťRl#E1IEY^I>E/8+OFɏswZ,c^]!R;6E{Ӧb:+{Q]KsYlcE:7D{*'OWh񌤯E6R$,'<jsʋ_ڞQ9|+D+hDjٖ{| ,{/pMҩ"ayzyßՃTE$}.uWS n}ƗJiy_8'HG+S^\h;oZh~[̶,1l^MJ<`iS;<؃lO$Z;Heӻ?JR1L88O~FKzu\$Ms{h1狴;uS4xڒyx2h;LiT_r]>+o| y2K,yyuZ4ŔE${M^<$GCWUTwEYje5wIRKv+}|/佗gIFMsy~.vV^<'HWyEWwx,CQ?,RlsykRVz-;ZZI8Hj3۴R?s[،{i>GX>9?zEؔQ<]L6-UyͣEuw&1#k~wEe)z|6֔\|و6E]ƇJ^]6m7E}$ \T;++ȓf#.˟j:smr@Gjw)Kg[|R\Gl&o]Pҿ"YOسU/չ_x̟w7nE}fy~(WoE}[5//ΤK7{s? Fߦ/>˧;ϨU{Pl;Wr?.VNZyJWʛg;C!TfIVE^l>C-.C۹>g;X^96m<̋Kl#,V^[6/ҧ0^^nl+|gvQ=Z/o<]9fZ|E$)O%m+ٔ%~IZRvv(%EQ?\5*'Az{^FQ}(> + $)/o?OOFkɦ~[@ѸHGjKvˣ]fE$l~X-T'x_t.H6s)+-ooև}Ie*䷩Ow]>^ldڙw>Wwy>+n1a^Fˊ| Glc^{p[ڭ5Z;[~hFQ9$[Βɓ׏dR[-yHh\'Etm2vB<ˣAx9IżEҟT6cs,E ;P'nSR,mH<]+5OyX޻(ԏتTEs)IVT兮SjgZ?W4YE>iJP^hvW^EڷH:-sVxd94tξw>ۼ_&oOFG۫+˫< lȶ[u^Ciye۳=nKyNdGRHW~iyC?ɓAkemmEvvֻ&yDQ" cGhLy,*BI.7/NɓYNS^6^ΝqrAM f;MM4[/ҵ۝?s_Y't-֒.7E;"=l /o^Gv^]I82_<|d{Es;տ4z g[+H1 ׫vQI}_<6c2iV|Θ?Oϸ.۔Ӷ>a*I_MslQjc.$_) yi=?ˋwɖ[߼Z~.h6W6PV]f{sEms\n7Tm?G?G?GQ=g7aGgnᅢ}Z飒+R_{b&1`o,|o3t/!n<ϥZAr{fS,҃LÙ\ȷ{|!=obsVO/|]~e|8%/'ܼ|L3:(7y\awq~0&Ǒ~Ğ{.?ӟLO1ǑzB#3S!,[ 3#tRxutN8=^o]2p=B#3nOSrMX}wG\?e߁c sk:Sr9CfHP^y=p򿏴\?g!tHF{=K1=~.ϔh#3#vq4Gt- /y!AgەGrBܓ=sϩbE!3pzrc^!ד׿鷧{AM?usvsDnva^=J8<<9 W\FOgW7 exy?e%H#[ k|y>#۟I΁<g)d3b|1>OƟS{k{<=UyM/kzz~ya>I;13~ܮq%ؗۇg=o'nG-i·\^Ǔ=~Y~A?n rr0~R!ϙx/=3 5C ,ȳ`af^A=61 _gr9`>~r{'͗3qؼHX:>ρY?Y>i/cGA1?0w:^#Wu6g2><͈=EN1q<@=M3yS >G\nn/-u&?wȉwzoƟ8yďs3g:_^/g1ÒC=)R\ yq G8>r!~)ӛfw?92,|FC3>bG7sGg>O~W|99Mq68~y=ž32x|z:p8>%8_g.#`#Zq*_7xd9t]Cg&c\Fw3-|<ag=~FOfzɍ|| itGpI?q-chD/yx/C-0WS9렌Mځ^yx;"O[eLȾKZ =#>ΖA/rA#tR@)}z{8__gf0g\|3ڇKA9cG~1?}\RxmIGxSǿSÅxuחc~=;>3:|] -_:g+Jq Ƴh7w;}q.훑ڑW^=Z럥uViP?8Hi^cTqzno<<>^X~o_sxv8Ka^rݸyGZ{ xȞ%f{qoZoSu2͏糗!wI\xd Ɓ:Д y/W۴yDS/??t3qzFΑyw~ǛK?X:o~zw;3< 㒌V^~^Rq*orf~4O7sZ/q4.00/y9}קYzro~/?Űgd痥G=p~܎y=hp33s=Ӽ_CO8?>|gBrGi Υ<.p%Kb!F3? ?9H9.O2=yb!i)8tё)y=s}di)ob|}Hbߑ+)K$?=Hѕ_!\Szw>i?rs=}XF)=C~~tq01%`ry#X>.W[=[OǹS<bAwǻ|crfKg~z9R)vqk=}aGRWay#t4~eG$-yC>^x{ qy>QߵqK?0X: z||lߵ+HFK(cA!׃4/r !afŞ|icD߸3<д4xF~]?S;ځSnb|0ҿy[ sbWj~N/GTx y}zsE=hؾ_Dƅ4SN1O8tdzJ~Cק>OCҽRpIR<;Txe59k_q g% y8VFKvޞ_1H' h /ws3#+|/l1$WR?)&4_(Yyp>36x.62_#/Xz>ANΗ_Z<ܟ#^ֹ f߅~&Ls~O ׏8f~|j$/g^6㖃?άw5/8?vmd]s8{/m|~y糐7/7WUqqm8=n߬~A}O ,.b߹@`xύgc?N3>dT?=0f%9OiĸɯJq:?'ܞy<7s_N@>i]8_KamX-O`?q9=|i{b߹\B9Gk{#_8' .|ǻR?̟ݔC9i,Fͼy#Ybݖ]Kv>C.>eH#qTNk}g3v==O ڛif߹8- ui;;/A\vbߵOks&Kgn7?-?>Ɵs\qsHGnW/~}ױ1~샏y\bty<绘8Cz 7x{N1>Z_X\N1,š_=/3yھz\_y?̿sRIzٸu^I+FH0m0q%|>O=K?{f!=G!^8ֻ|_i{S{q>__yJx ;ܿw}#.a/|G=RN>/_/h<|؟I7f,_ɫC'czp87OH#|<-UgEz27y>?a&fZ dەпkj} sb |V.t|#?NO/%p~L?F4i| kWE.p/}>Xd!gA.Q}st<~b_rs[0Lx Sy?A&hS<үIøJ:?? |ş|,QR9G>1ߥqN\73@ 2>lH=q8lĎyޛ ߧY~<֧l|?I..T?Ji<] =w |{z%Fʧ[cq;O)_ zc|Ǹpladޏ~ea^oD/|>D:w>Mg%ŋiOvn'_3m,ҹv ϧ@˖H컴/E?rJM<sxg[R:=[x `RۗÈL.;a!_T~2-!GVg3>Ҽ{?ϋ;i_0<=i[gg9mx?C<WΣ-t<|67)fד7i&3?:KJ>`߈z*Ւ8#ܿy=s"t.d緡ftPw(~׃f~rIV?.?grd^cџ2xzswuCv +Gܞ|_Z;W裰O02~~<[]dzr~~sG}޼=k^Ӭ\=S,S?mqރ#N'~B[yY)FƉ/œwv#K1MO횏/61:ܮ!{+p$0ߪK{_al$9'02k y=_0~#bGi\zߋ~-AϠW4)~e#ݔ /;$Xfr|Dzaďrs#&}%|<2U,Ⱥ6 '{Qo~W#xFsq({nw&_f7cY>_7Y9x<=3,͋M ssnl~%[8^Z s}Ezx fry֌>_74肟tϛrHO|+y81η0ctNh=s=`78c@v?Fi aҾN/FD9&Yet7 r%=iO\oCH㧬{6  #7cArQ${[X>q{Fy$?#=MV>pz{G-7ip}9G~VƬrM')Nc<~dR|7b\x#^u4k#\,ߖǀLx?b܎w=IN/OqKx!܃' (e<@|#O&0*g47B{qҼ~xESxNp.WKq(m]Jҹi|93aAqZO\ٴoOK{/{ig)v8'=o~Gڅ>Wy='w1̃s|w}s\~N)WR<}YA?8>y>+nL^1Κft4$|^$;Ǎ|~fR;~&Ǒ,=x"񪴿{v9~";>=_qz¸!@OO֓S|5r{bW+cq?u SK!3giv Xzi^' aj}cj}ƍ<)}#ӌLGG).YH7m_? 'gΏۏ4KO3~$GO/ͳ,H&Y|V>yI=?(GhOlO;jދ# Kn&ف8N8Xy\[+j-|iy:~aؾx^,sd?Ϝ>קX|+Ğ{~ۑKgn׼?29[>2Ͱ4c3_O}]<>R>61ү>"q|>b~ b"Ά4_ա>&wV_#\Neۈ\let0} =¼xz<">o>7-FSbُ^~19oxz{WQ!rMr"9Ǽݐ)ΫLHqqn'3qu8{vq{~Gcrd7tNCc|Noq"oHq~7o$\Y>n,DžH\,T||IϦسob|9Gq ;=/ vH~<> o->y{4?z-_<4{gnL %O*Kduo?O'O#8%#oyz}>< \/2!ߏ,KOI0;o||X:6x~w{_g]|!MLN)nɁ<#"R80J|>.)~ޞ-$ma?N,_t;B:_iOYM^B>C/FVsٹ~RjW)=3ԳNO'^$Zۯi!π,o P5m=0?k";x>aOu@S+ }uO72OҮ-Yȼv#x~$Ŀ}Dy X~Ob/>]~nܧ),^<82./ד)ǵft 7ϷlbY9FR}k&[ _#UFR|<@8ӓqyBI ux.'ysg7#z2}TLN#9PCa,q>< חtN< mQy>GZ/sVk()C%׸wy뒧.lu+g{Sp,[u$Oluv/qߪǦWhM,9ʢzͿM;]|BJmIQv{7ݮbс^ky\5-٥F?{팃fguG]vc')[K k%Wo[eVM}˥wW5M"WSu]~,zݞcKo\1)tuoX'[ǡD.wQ6XX>Jb'u-= ƥ~lKqŖ雯Jä=0 T%:] nSMDz工_%_娚|ƽ\qh/_^ܡty~@UvU1VOJW>_>|rM\U7 بbNquR^/Rl{[zfUnybbٛmz)@PU#v;ՕĒ׎9]o _6 bPu9%u +ן],Ƶ>'r/ߨ0. ߥvܼua@UJ=}kĢщWcoEjn(l-_9'寊?5>r]wQ\Mb͏6Jr(w9's(KO͏>r%z,ƭ| hj%Ǹmjv/4p/Ϯ;h_z.[7&x'x{tM+_rVUXN,-$WUVVݾM'V=ͧ*yӻW hJ_6߲*O;_tڳm}`7Z8SWcѩZ^׾׍*WlĢW7-M/q;rkϾ³/W^|uirIծ|]ozJ_cSrp<>auS=H7J}&vnǥUё^J|lU?ʭcYrE7BNK)zS[|*WSUݦ`@8__9\Imhls([ʥC'հTM*d+ħz*[>nl\Uӕһڟ+ߺ5.iKao;-olM\v\޸)`RmkqÀ k3a K¯j9 s?trTotBsc@Y ޵0n9l唠zES_ǦֻD/6Q0\J9ڍ|ꧩV^t)n޼̸`@%ږOW]EUW,UY6ot}ʎο|CL ߺ-^nIβo~dcvM[7P5ߺt hZ;kvf[%+MΪSPg7^]lyKO}-F&x<0`\y1;ǿQCLzwƱxWs[~u1q0,? oZy|o[ZM)GYZqU V>QM՛?6J}֍9ۺU0n9Ety˖*%>UƀiϾrhz}ɡzДz ޕ1)CC*9nbub ۦ (CK7E {;}\M/[|'viM4 ~?@˒C?Q%V4ݫrr\o&4ŀ(K^tgW:VݮeJh*Pyw-aKPU0n9ϻzƕ~Y$|oz}7|#6iowƀ嘔ccb@rԍw ["w\'Sr4EIy06n0v>Ā49mjJז_{_z]tb^lleǡ|ʒ/+] .4=Zˢ?.ewM ׉eot){u˱Q1*>|t 򎋜&wkJy94~7:^1`R?~0ѳz1i9"oZ}qF)g98k6rKw5 h*qvl? [w.^UhPEκ1`\6FǀRe+o~VcW_w9nb@S-G,:Mŀq_w9J,ƀ]M߉\€Iaui,9ҳ|뒣iJ,oŀq;n0n:PĪ[|Ve?MvGojy4Ԟ'8.lulUm˯by}C_V;H7 `Z ꖧr͗CtBۏCKuﮎeuY|vUn\$6 B˥w^WbgSn m/$>Wŧi^--=I)UOUrvg>n9B-FoZSY|-ϸaUKr5UΦ+R nŀh:;?NbqgYUUt~OtJ5&NqsB)}Ki_?nOR:_zMŶKlW%Oj|{}5c1,]__6cFP9Co4 (+};֍ui/+l8_/\F,& B,y*I|Q䪺cщU/ZrUݾZPVuU0n9&xƀ娪\q{rIr%oYtǡr׭)rۯ]?um -GۦڦwӕmNp>Tt˒n7 Uڳzߵ\ L&}R@K7ѫz.Ole9aNiWk%ϸڑ&ݖ+kqÀX`KU\l+Ua,~ƥ]CPU}M?<+'-bӋeGZ:)_s(4Mk ^Uk^TͿb{*lz}rqK}ǒ;6|\OlFVf? \ZHb_l{]+򩻜c۵1[/m>_MPy/]O˖חkv|lj=+>P]4ծm\o吠q_U_MvS|b~vu mG R|*>uC\rPMiX9+vۧS>nRu,|%O1) vmJ)[>~rHrҋ]n[ݕ~,}ˡ)*}nioh8=M?utCoeOv\W{%,ɣwmGus*{ДrƖ+T*Ui}Il#oo1lUiJϦӋM_#o}jr/HrJ˖'VWl-W,y49ʲ/l:B ͿPM7ǦvMKG{?[NW㦧UTUphZמcA{wMK߮^ǖ+66neJ5};b[rUU+'˒W~|MX~ĶR:ޖ^ɸa볩Дv[Xʒ3V5CS/uߨzrJ|owecKוl4=rs]/\WzV?O4:u˱QZi|lJ^r嚾nվWbvzۿH!ѱ?MM`@V.eɣwcWY~i_Mi7NǕ>TP9O׷)9}WzKڦ|NW{n[N|u䭊-q[\uv zjJ9Cɩ}/}/[U۩F/{ոxlRz-bPSH|$UlEgW+_WǸ=[hu Op1TO'վ nreoU4$_ݗ/ltZ>)6Ʒi._Jzr1n9b?7!_U]/tc/K^`=[V Q._KS1ChϾck8^\Ģ[~4Opeb+w*/O|ĿPWض&w1i*T||BqhM)CU˧b~%_SL(qgbߗ^Ԟǥ|s}m -MXޖ^lgПSu=/[_r&|O]"]Te˳Ѱ[TeUGtR߶|֎=6EO^?P9|[__lMkG-_lzuc@r4UWt]c-,9|%ϮbOrJn<]]U|c\ɕ%|n*dMzZGM8Puc@r*0ifc@Sߗn;Pwm۷>׿>< Q?Ql [ ^JS<4 ca@rLO=nxOMׁ&77gD_c}uG=-׋J'x'n Nn,u1'x'xX隂M n ݍVO cWŀ`? [aS;=\Mgc`cr0n9&x=-OO+-Ǹ`@r4&k`@rlTyZ>O;Ż>7^i-o [`@rL9%[ osU|'xꖧlN0< [ k?  [I9&xWƀ N vu;z U0M)GUY<tBw0)t& &P -ŀĀPMÀ嘔owE w30Y[\SPwVg[1lLwr3rL҇ n-P5ߍZ=s1`\IC6bW,:MÀ]&zZ/-ǮlҍE/T>-/L$S<>7 ҭ-O'Tz, [ n)nxP'x^9W&?M׫*g;/lNp0nu1<PrUua+/oT [ / s;,*o&vl W<.P<< yn P\<6P:-׋) ޘI?7娺yR_q0n9&x'0.t}i<<~CDZGU%{-kX_uZUѓ隊9T^B[}FPgޗ~Sہ<+_+>TוNl4>p{E*]j9CʭK}shr(+mZKgN,, ZOZ٦˾\9[.-$~3nYIJrp[>b4G$po~4>eV^[>kBW%y49w_v}4>?UMY}jU϶]hϒ|l p勥'eozlˇHC?~+P[|泭kz}.]qCk }OJ[JW~ŲOO׎c񕰭ǒl>D7~W{-O^U$HϡwכFzK]rխRz^ĪOWz*[\󭏪ZP9x:|u}H]|sz#_Z._9HP.wT]UU^/oHJHz˵rhhtҗvҞ}_r'eP{׷=t5{J5}[nItZV=ʧq#嫵|Uc|֏|598Y_g/V;ֿ+P -h$b_MmW>o:g~,K/6ҳm~ICPɩk,= J,Bm$_(Xz!sri}ȕ^|l߈-$>V}h|cҗK*W(]n_RǟtlWm?H}-꿖zvGKJ/ (+}rۡnxrmFVneGOZmWΕ^KZ'oYʗCl bWy\Ŷ[9|eu,cul:e黭\w[%/ԋoP[?tcˡjՓG49$_eQ7eG,~l=-R:R>W|Um吠iJ.t}~j"k~[$}oT-/?ϖұM!^h˯,y˪|}p(8p}V?$4RzRK4ڍ-ߦ_:7EǽM)GqCSK|]il֏jl֓kvʖ~So;۲%#_9noW蹾=kKOm5lYvXZOtC7%:S[:b7W:C]6VrKtw[Z:[yC7pZ履\eUϾbۉ-P~~-l[N-]tcK\--4eZ,4/黯V9_:U3Ԟ5UV;hmi+.=7W~޴}H~KGgK7TeӉ^|a\Ue=|ۦ$'rz[]_ymY'-ǮZGJ Wc' WV{K\kr|˶pU>S(KBz>mWUoMv{6}YKPcIW6_~WMznk\~7%wP҅ک״wW}bԇ|{N.X.՟\wC5{\rߪ.FOn/||\ht]\[5yq8T_b+ێrիP{U#Œ۵\VN)v Gz?n5Vbϱl?$P^ie,\Ko+DG;oUzQX5:|Bg_>e;h'BpCYK_{~VBkw 궣6+} 'W[Ҟ]ˣ工־Ǫ?_UEOh{H|c+|e/<-|wnrpͷ}0XVnuU]~l~bՃ-]=ʥ}/[/|Mn=,.=kn[y*F?6C]\?n=UP~GKCHve+Og(_v T=?P֮tbr'A]]]e[ÿǶgrJ/Ʒ.VXzc+/ݕ]ϮҴv工cΖPVk?(+}rUΜo5Eo|B _k|cstM{mW-/n?;o+*M^MΪ[CWRvzUUn_$(K\ǦAkg)_JZ|v}ZbOo [qkZ;It]it|׍5*_S곝x߮UPOOp}vޕnxp;?orlt [qǀh*-Ǹa@rLxa@rLpPU'Wnx?ׅ'ln9%")L`@rm\M\һҳǡz:hO9~'xߺฝby^r ` BḝYmZ=ot [q`? t_Y|_5wU [ .ƀCjzQ1\}djQCZև}wm Ol𥳐;V<>P*ߤ>SW娫YJϡr4]?S{~OpX@r s0`"Ǯ%O 4a@rLpPn8?қz1n9&x}0>|S0[._wu97<11n9&)Bg= Onyq=`bbFW7lt{ht} 0~@+Ǯu˱b@r)?\vvƖ˕^Y0`R?<CY)_%UƷۃ/ O [Px:|]{_7~m1 V;h&x1uӟ .s[n9bvvvU1n96^/7?U_&x}0n9bv796 -Ǹbn'\ P<<11`\;-GS1 4-_~q;œ͡nj[| ]nS=v}Xt&x'8nxO'Z *Qj\踝Ie#`\iW+@rLĀ߫]ǿ]vÀq_7}W9"O/ny'-ǤnP\PB7ct [ sK?3 0ouB>Q}l++h~ ͕mjCA_ε\HzjΗN\}V|'|>4{WRzWXK_V? "ѓmBY|c-_[%)^w\Ӈӕ|%W\ z?[[d B6Ptw|$>RŊ4~3_(ۮ4;~_I!ߖk\+q+qFϕ-__?'4zϾ׮q׵bwlPMW:կjWBK3U<ela/HqUUqU~2vfO[bCY~7},g]59tW{}*/]?b[rپM?տB4#ŦlJ'}!TXa/WӅrIrv!ͯs/K_ZRl[?W;#[Bۂ꿭CK~V.gWk\꣯'vs'AoHX^Vybc~(Vi|Cgl}jrQB!v׮lyxVMB[,?Ċ'] ZgV}黦[bEhs|ܵ4+~BPIqFOzJ4>X9-To񪫿gC%_|EM.)?ڥ/zCr,/]尥ٱ/=IN?^ض7ߵvb/uXܕ^5zYÚ腂oWu#'sGW`Hm˥ޕfweC-+/}[:rAη-G]ӻڣj+^[t{iD' 4}3*Glھ^rp7)}hb_-o|aҳOG]7ܾğ>ʫC_[v=`ˇہ|t\lWzqD/X'6[Y4k]teqh?j[Zβվ}1^ӰKY z]ۿmm[o?|M}z-9mꧭmXv7}Un )f__J,msk / kz[_e_AG4pmX 8)~\~]jxP/]!taKGJ_A,?pm߲!wqo՟ԟw|cL@M,]J/q&FO/Ah.5{_;sc bͶb?W+N_ߒlJBKB[KW;ӠʖTߕm-/?Zf7nq+`]덷VR{oV>+]5N.;>m|BxMP~V>}h#!+Zd[O*icّǎ cw[;qwBWub OCY<ٲUW:'irW6KBO]qNU494ke_^BxMK+m/v5$SY[o]K/Wz}rXcף/-lw[ܗ,ǥto B{t<}hBGvnm˷x^C \m~_R:-_C|?pX@کroktP? R[bkgk߱[[=OHB\.MnFKt\!45_h WqU5|mc7tyBи5׎]#V$zv-tUՓw[MꇵP?+]}HӮ_K[ovf_#4pXt${>iS+N'=k%r֋Tp*g,Bzrm{k﹖Ӗ&DW+򹶫/hqV<_v#۵>P}'IC |Y~>?㛞YzoK\̖&,`%?[>RXB׎%Z|*Kt %~ҳ{t\3Wb=Wվy^q-_h+JZ5>}}hBh|&Gk֯ߔ޻ڛ/V!!mO7HrIJS_ w[~|]?);.Y|ߧ՚>JK߷I[ytrI!ʆ{_~o(~ggDzǕW?mMlK]+=Wteų_t_i/nC#4|U\5njˊbGR~)- 4^rM*^$Zi>q>Ev|'FזzW|Wl5*WYX*9-}[8UnOGtx|P?ŊbImC^>_Wvˡ)o?ǮW \? ͕-_=ۿM7}vM}6KCW |/N,RvQV??m_[w]7o!_?_IJ#W3Wt~bC؛oﹽ걯?+7_?W[/bգk$$?+Ϸ/=k]|k|\1VeK߷^_x~WZ=/K_Bd<BVYc euXڣ,lS=ԟKtb~Fbi~ї^,O\Ǧ/'lKrhW4z]Uw>C)4տX5N_K|8֋i^umoqWz'cmmOjl-Ċm] z`[߶QAU~;vIt%qr?VyߤzY5=|U]qBh } }w8͗,K |ٗk>"~ԸBk߮,=u[^ŶX/K{-MkIv_Cϟmݵs~Q$v4+ePTv?i>>/tmHOl4cl9lK]kP=v3︶]!>ik{ǒ5+-ke~HJNj r? eo|W?c_WtAg|kA8Un[]zqXq@-P;:?eOt'W}t[Gir[qZ]qjhPK-k|я--)-=1CY`ߋW{T-m~vAZ'7՟5H3Um2ĊbZCT^cwR۾<{׵)/JZѵzcىƒ7dg[\O(-}~*4XF,o^K+퇥||䗤~=T|5^ Տ/ԮxOAkƊKNe/߫N//)vtIO].Nzv~6_h?ڿoz_=]!߶\v C[ 7Uϡqw(Pzea͎T׶\TqZl?\+](/-}U tk 4z\|Ֆm{ ?}h\ne95%OYo>_};)/vV_?һ-]%;,+k^K_cXO|Plb^߲15X[eMYۿ*g>H\(_ O߸'6vanbO{IC2V|G\Ym4tTh?`ηlkH8=eŵeAhiK7Odkjbً&sp/ [zhnF57>t5>eo C_?${mzjOg|P⣽E,pmٶ}csJqteǁwW?!ֿK~PV{B,zg l˶i|w_ۯJgkZ~v 4pWTux8/T\Gqr]PV{ݶo4}-_mgU.C8Կ 5|t4z\w]F5-cׯHrCitR+o?+OӯX ]ˊ+]s[ؖe\X#v#/qb_,KGX b (6HmOo|\fh_|tq+_'~0_rG|Uϕ+=W.U?iƁ~M4ŵ|@h\3|_[?i cYGI\[\77_'cꭤF_*/eS49\W-wg=VXl4ϱWbŽ2hrڱX]UXFO~HE7~~/]ݟG~8?9v=ڿxUJKA/ԯHrjri4~!@l$4Yz/]׸@k]hwǡfϡm> ʊ7bGRHr[~P?/wviu\&[~_l>ͯWPcۯht膖[ۯh}g.=o+G%O7{_zz+WBfl{=rX}Ӈink~黯u-k=_K׵mW>]lttk~-_hz_hvj]]gޖD'Trqlmķt'CP}՟Kۿƪ_o,KϮ`Wʒ#b}Z|6v ַ\ri]Dzؘ/_𥳐~ڕmlcVeGr[_\UX@ǪOʯDzgWb)זn՘šlT-O]>tʶwINWRzDbۈ7_YEH_}pB=~B M/WNpv5|R~ 3ree'qWnzU/`i|yzZ>Ybl>u=-mJϮ}KOl2CO֣lǶz.NC![imҷ4(bӫ_I\۶7FZ|vSZ~Wt˶rt=X!TcKv[>qt|trڦzk78hU՗m5Petb/}m:L[>}Pp~IrnSU%I߹ _>ʣs!KӲI}z^]U<0oX![gpUN[bW\w_|ƒG[Ē뉯5_(?V5%+_I+__ycc嫿=Rvgk?~1{~e7^zU{oN[%<}KϤtR>_9lXlm[ګD?6X~j*_WzmP95ֿz}=Ww_4ls;pU덫g[M3|c7y:Y{Ͽ&OsO{lW>|eaI?KUrC&+Wzҏlk(rB-m:ggٕfWUhߵtU첮o1vMZP>vm eQEO-[֟Z~;r֯]+]P|MomIW\|$zZ/]qM_*mq>|cWb۹>vIr/ˡ,^KVOC锕/=HbSH~`k:_9R=ir/Nrs([n_ڂ݆FOǿTկp(l/5:ltH.sh]b9Oѯ>9ֻ*Wh}ƶr5U~WCU.rO[+Vn;eU_)3=W\xֿ&iz!K~$VP{y۞\ϖ>.˶9H!ԮJ/6߲sR>_y%>4u姥y}֏&+rkt|NJo^zP߉ePv9cӑqK?5;3wWy\;tbp]e)K6U+KrbOK\s fK'KǖOgK_z[_˞e;~Y^VPrIuwY!>v}%O] .[U=KmĶálҹ֓rŮSJ/ՃUP:C5n|=vimMWw;ڗq_7T/+M^'Nl_+UOl>\R쏃+|UktRitIJ/+}[j+W,}uMjk|֗F/v;NUS]|{v"z` :9o6Kr4(o(P:M+ m[ytmϖ~Utm 뚿,}(%9lMg+]{jߪ3Vj]__}qS{*K$z~'Vlp=me6Y߭|eb}}F;4.;]q@=5:||m:_.O=bٍځCTUuهDbw,Ρ(m9MDG'V-Kײ)/x:Yk˧*?R~~C Tte2}N6Ǣ~vWVb?5}Uu}W׺n4=qSWQM^]z脶+}I.陷+vr٦;ծ9Zz]{Km+mzkG骲{꛿WI\X _i–$.Uv{%+]UFC^u{Uo~kjvlKjniՇXzMZʶ{NGw-gh:M.{WZ7jzn+NcK*}MKnFCGӗo94~PÿKlH~.OH)*qig\>KXR4=eޗUX>-/=KGc7KnKnNG/|\ĮW*O_zl{OUv?IC}\W~'/]|tb׋-tO}oߛs{vW˖5CY\vkhw9߈U~_?5};.7ڮ(ѫKs,U^)#}SUCeK_>Q(RP?b[>ػ-]WRnPvyzuCGӁ^Vs(-Xrp:JJ'}#[9}&GY|қtmۉV޵zIϱ7NʖO߮^vIמ.UX|Nl;m/뵩۫^w _N_{v.+=>T-P!,}-!OV\uw -?kд难j-_[yx:c,Z/Rzrh\s^|=_~Um~ \]{kz!V}j~ėoUvmKוYl[߱/V8}]W\=ץ(AU~FgޗYY꟯\B+kk{P94]qʢ뛎]l*'oK'+P~_6]ߚR>.rv-[߫rǒ3\AlUU -O(HկHr~һ]UU7^ƀzm۾wڳkGUǕ~IJ䰕6-_|\VoFWJoK?/V{O;}ӕ]?ў[5R~\l+?/~}Kr֍mҹo٦ڵ/Oۻ'ɡ74??!ݹ~'[v%:Xj+gzJo{k/KҞbٳ&+?_8>u1@WSJ_V=p~UyS1T].^G/ `_uكR~}|jt}KlSIgc~bWlTĕNY_+<ʪXiX~n,͡l;ҠrǢJ'u#vKb*&lhb} ?)ibٯ/=|RHcm~.K?k}VNbEz7,K}UGKו@WZ>~!߹;+6߲۝C'8e?_>t%Ntˮݕ-vޏ7IX\Ė[[;6]v[u]髮;-͏ӕ=r9z'qAm?=mqP/U՛PM7}ёC?ʮ-=-[:}+-)՟ئS۾ҕB`vm>_uKUq(۟i~_eס|\!J.?Az/}n,ZR:^zOeMmKہ/_/4?"ӾltMqתڗמms{)\$wv lʗDzXRXb/[6=O'ߕ^]^Nqsegl>}֯neɧu_zn(v]Kmz [`Fsq|?nϗ+?Գ&lZMp>-G;/4eQݍVOuz4 hZ}V-a@SM v"D_[\Mv7n<^P"WpM_5v'ٚ7AJbhPm鸦-g]W٦k*wR_m .oyCVq7@SLwwЇVk{<\'lt{/h~>o qU-9Tg˿jw :(ߦ?nxƀڹle<9vo 7?[]ꖣlbӫ$<"ox{Zyut}ͯjyv [3`@r.O;}qb@SMp>Q1? [qŀ;nYr5 s[ -WYvNS4E񸗧n ["O +nl~vekNUب0վCq!H?eﮊ0n9&8ꖣl]׍M " [a>'I!uՊ[@SLp1-OW)TUoZ=5M .cR )n-GrjMnoLTvݔ]] cŀ1)rT-OU)n4E"a@ZG5?>W?ݖ|ϲqUnW>rꇫ>j߫jǪ,z|Bqͯ.ASJ`k-gUWwqj9zCP?+/nrI$OW=wu[RP9BCWmǶo-(g(?^KArĦkϕ>'S,]mƕǶz| \ҳo-n Uٲ͕Nhz \ߕ^,Œ*皾nmױhiV|bKi+/^(&Vimq?rza^+WʖGXv-i[f*,Tp;5|끃k{hCUUMl#􎿷{Yz`K/ S>HK~_wWƇ4r7~%pmWNUXXǴt%4|[mll_m> |/P{lrK~$jc멯軖6m{j8py8eg ?ˏh/|4$WS\۱l}mͲ0W_ȕ/rjkW]WS[b鋫},q?.XtR>v g ˎ4seKlZ}>6߲\G?#!bڎ=&_ơl9c-ksb+_޻B҉]eWzUP\LG[ڳM/a_t|eYΧiv嫥˿}!҇M_H`۞Uc_R>z*$O#ͯ.7OGP GסZ_We7gW:=# 巕#/᫇em~Ir&^v'S}W_u痠mZe jUFו+=W+]Y>%RGlwl%/vbU)FO*T?e%ɫGcK~O͕prktlk [>[P}+?)e#vy-oe/?Wylkw[5-F+[o/8W]I/cUw,K՞\Ɩ/^g赖҉m5Tb[$ZݘkؖGNvKi,eK,>hj~-FmCYvScK'X/Nۯ+1jZ:-k{kסS~B7}U*ߕ-UN+_[T]_jK߶WOc_]zhco?Y+_ҹ+[bXTUc[yx:|a7՞P~~[KүJ/}yz{,` MgWsXb r)Wj/]/+!?ʩF7V})o;Fv W!#=ʥGsO4y[vP[.Wz<\_lH!6W++P,'-]W{ Vrirjc=gW}оZ:[:ϵ|5})4{[y4=m/vyBs!cҳ+Z4^'8bٝߖ~/+G;}WxFzW1,I#K>_z ]7B˫AYrj_; \b bѪC8ڧjj?Ԏ5-,{ Õ+z,;tR~)D'h)AU~Է49%:~Soҳ>lirr~\?~tb񓰫?eW0ϡ7nUv.>e|ڳ=>QI6>0rgv=?*viroN^}&˗kդݻ/wͳKk;^,?I)%Knx˒}[yd_YJm^wdr/<8Yy'_5i+I?NzGҷ>I3Nd?{ƛN'cCμ`:ou/N7y!o͒չ>k!Y;o~$]-|a=&~wu/ۃEk]'H6G|G'sk?!wJ|-o~/?NΛ0~}2p{NA'ɶ3'SK>dS_[^,k;~awoz0YZڠiGOg~;ݳ̞{OKʽS2׿#tnk-dyJ[8!I[?|m2NzGsL_=G&]|[y>i6}t.sOI{P{p+MM?c~o&Y|{ӓoh 1I/8ƯXH:J_,}}7Lzzv;2s{19gp(iw~;%nޕ,]s7WS{c2{߾Z2uo,nz>y̕z澞t_uO7sſ{甤>z󛯛xtZ??n|w /ްrKzǞ0h7&_qp&xK}'_q7IzWϒ]}cß1Po%sNRnߥz~c~=w\'\*IJۿѧ<}tn|6Y\.,^^IF);M:'~]7g!Yz5vMޱo{twy/^2Y~d/gpջ햬=uO=sCCk~w7raܯmhWO9I~ ?e}Utg>Uj򈤻Kמ>|Ӂrs_8r|f!G7{A\ϑt~1lsˁ8|cy{M:AM4doe`';U yI/6t=-)&_!?1/{oygvS_m؞fO,8H?WK:_$C懟o<35Y^4`}`؞,CqS^/}^Z+¤;0d;8.|Szuud./"/NWNo:4ӓϞi'+ }w_zL_V:hSoTk,e~t0O~.+~sՓHﻟ;upGۧ>:YGn?x/l],~ݬ]WUwhdB+;%+o_Aqn'?Pߩd5R_Rwjg])ㆎ(YOd{&Y󇆔ttgIV~AC~}h*KX1Whߟ{%CKס{8fwK?_=.qw7ɏ-|7/9Y W1 #kry߫4y݅G.\)N3ڤ.%s?H2Oxnrg~,Oץ\=i=t{;w2}|T*(eǿs@rt?2/6t&K;߽Gw=qAwqГ K#;\~ l6fj(&ӷ#m%^ڭ冊2dq❕S27+p7t2S_dh&m/g& Ã~j%+Ow &+{!o󡤳 ",?P=v~xSr[eɃI_xujG=A $oQ6..l}.ppMKnA`xdۍ/AlEi|ǬKVxu8įd1'{@jKL?eٻZ.Ko!=at?a,Xz]#׹璿^ i7z?' zdɯ ۓf'K%sWw~wFAtys3~}>wb?h-Y=r.Nǁ3=iSv'~c?^o0.~ǽ?7}>l{?~{M:0JVh j#?xq$7m_Hǯ=J@NtHN /NQ? O{ñO_t 31~]=~,^J_ڒg w9o}[SµS,Kʧ,D{'iM|3S?;ݕs4.'_Ap*[^Ё?|dl{ˆ }}Ƞ??/l?wqڏKd4x|0_?iH7К?3⇥o ֣^k0>ɶse~u~gg6}f>,=v܋ڳ+c4pWŇ|>֗Jzs%[ yC(A^!9k>9C;3tlonf|m~jftSs۟qސ_yOyu~]O/װ`pd{:OW_:wp`?o}V֯6ӵ /Q?˿Vߥ ,ޘۆs [`◜ӿ%wNtCzo~LsO#޵)^9|8dWIz?hB?噩z0Hchn%dOqS`dqOnM[1p3YJ5I?IQe?lح+L^>'YG|AK6?u^I/vIqg ;df>F4.[x->}im{~'1pvߦE}aOTðl\}g0~$ɋÁAroǓ]Ha&<7\v9?Υaoi% Gĉ{&/t/xsҿ̳n#y ;tugu&i?vڙGܤ{Q_ϽO8νdž O~J{Nwbki<}o){q^3p'{ƁC>$f{:.L.`NtԿua$K_5-oi܀|՘^z'P]n5<ɽtm#/+xiQ4 hsd̃8}t>LZ/H_=|ڶT٫^9?n-o1ݙqAQd /Mqܽ(]IRJft@8{Dx;OD;F[:nF'd1Sv~PK?~c`Տ^QۥpOeqlvoNſv@<@=n vߤ;0Tr-_gCǷ?B۔gs>sk nB]f/Yi??so[|'3ϥyj[ ><_M'f<7in+ɯ G=s 'ړ5pМⰭz_t~QǕP?1-ax?Лi=^8tK7MBqfy {'yn0\xtpc3^դ rҶgo p>>=̣jh~{ddoɍ۩>0.|ҩf|Ob~zؙГwtg&6?{e_}uIk ߕe/?Ge~x,..Nf󬽅G'R=WIP<7d̃tΡq|w+ܾ䯰΀yOר鿒7}eNoJvaqO/=Iimuo78Y1<|75۽h>m(]<l>g7G~,c;]ެ7|EF5+v]\2t֟Qm̠a7%lx.{X9&dg0^BCzs7Ʃ|/N|k6fN(>}^#xEp2nF%GмңS7JJv4OZOVߺ^Mǃݷ{d67~P oͿv[{Sx~v:ߠN{C}[S?· >|Î+o{߶@~F4^0+7'Ϝ9+^Sp?_~00 ,jlbVץuBӺ܉Χsߒ<>,:Շþݒo –=-YYuc)~_r4c_:p<>-Gh<;.WdǽKSyct|?џb# ~iceT{Kg<3Χ,[g:'_P;d{?ob+tuC3Yװn˷ot=ҸM3oZ?ź{?t1//l~1aȵͼh\&E:3i}9 wǤ2fS>w,o[o㷠81]Z? 23q[]2<垖кaw_mPtiINkToZA\7h<񚗥ᰏ姐]ZXFT{cÅDs$o658qRSð󦏤paa}5xzK-^F,Y -䛿WB==?uoY Ѹ+,}me彩?_īxcmk1o6sEÀ1{۷\|c8ͧ_I~54ҙ㯟;03']]pZICndgg-~ >h?i{jodIL//kи7W"K&^.!fqoH0_7U'~j?Կ{JzO``8C1ZƯ-Ji9O@GL><}qZ>y4|x?"YH(u>ǩB}vB*WIߥ~h'ͼҥ8L<jlNg~@X/x:ww^H .#~mߣu#;vNK%3쳘Kcz<=xgnA:GP{#>E܊sg u3|%=7`2KfOݾ;x&{S\ӹS{ƾwUN@Yo_~w0~ٯgCt/8`бWO}sx-K?vn؁|$w/I_a]Φɏ990G`=[.omY1Xj^?z+?*m쳚@xoyx{Ϧ#h:&#ߥ*9X^/ d:'0ӻ-iǬֹٯzcWY:~x}$+;hsR?׸7ۮB\oxoyuvd_4X9A}tnJ01ӽ}=I9ƹz_ilŤ%xaNVGF03GSߓ.i?A Oh}kHӺr&.6qO/Si\춏Ka (Z'`?%tC4?t7tWx`ww| b~-i} s}wyV?>tBԿiܼDZץ/ /|0ad4/ >@So8scO</Hu._[~}.%oSqb3-^/̝Fcacŷ/~ixM{zwhE?̇aƓ+_}C>N?';lqm'=`` Eލ1 /aOIo_E 瑼_2¼$޺pO+ϧ}zyDz~sCbs:|,{R?zSyZkkOt:Qny5_|-ց.AvWg^&@^o`4>>{:ϥzeڬ[\>mx,aϤ{{R4s<4c}<b|m/NBq#U,7@q>,ߖ+wtY;~m1NǾNgibٴNuMŖ^LɞϠx]X1"?qts3sNČ߯sw|I7a_~`f<þC7p\}x uҿ ??;Gxs0c%;co}~l_u7O"6v?C|Q ^}3]i~&3󛘷DrmYdפWQ}Loq.Wk ^J;QO8߂yީoۭ/Gˌ_Zỹ,_:3wex^~yLyĿ8v^MNj|Mo8ww}mjhgK"~þӇ-Ǔo;-Ay:?nOlqYJQujGa|߹ 3t/O]}ژ^Jz}75=:(X\: ?y zKsa?ɯO^i91s_ +.doh%x/'%!̼fyo%˟v f9oi -Qf/ѿ?ɖ_17ѹʥ+|Q$?X>Νi>qIf]73D#~?ǸQ{oeW/5@f?F<1/z\#m7>֛;w(O~m:Q;fϴn֏֎8 q89^l/:"~xw.}װ2K;dF:|yC_~'m5i疣W-̾~Ez.q\_ݐb}w^Z$7a_N^i(zR:Bxi^irp+3[ys~|}lN OifF>MYΕNJ˱zW[̿}vG?MV6Y~9ke|΃n}hpi~jui5/#< G Gyí˴Kb-͹a^6SCO9d<7 &Xg4s{4?Y0}s?M~#i뱽?'sƯ@NW_`I~q.}wù|0>_>.]浻 nxj_r#9s"Qλ`O>Q!>Ÿ x<_uW$}7S]Ocʷޘ{^:,~|' 攋i4s8Ƿ%:;,(8a ]i^vO:xbZ϶|x,kZz;7~4?goCQyiZSdq_9 fHEER<هt5;e<33yx=֜~ȃt31|!ؿzU1˃iFg~s}8?<G<ѹ/鼿Y[:h0[K)r;"9^i\߾Ĺĥ}@)7sΦxWw8Q0DZO~CO\ݷ\+s.#\Oc?=*p`sAϡCǂW:5~@y#m f⭾B|GVOyܕO>Mk8}}RӹxJ[̸oy *< +0;7%:O=:}9?Ѿ|t_:U6yguvx[ m;\FyP|uO/3k)~$Zw6瑳}4l<vrIf>aLq-1a 犳s_ =?\~g_=~]O노Os2+n/ G$W6`!l{͋#N/^L%e 5y΃:u]ҳsIacvyܵ)]y'ӓ/đW{9꿰\C`H?ڬN`7X7=I?gh/{olѼwO֮FL}נyOw~Jv}^F_SH:]MgqEm2+'993)j6ҾyGXZ<'7{a l_uNsƯO_ݾ3u  %s> Ύfm2s>S<{ޏ旱v2;6ߘqkLL qo7C&}&nΏuB7݇8ry|oG-mcyV>pc汰/?#XO{//.]L'-Yw:>e\Y{iܻuMeOslfzm:/3txuo܏h;ǸO=O)cSg&Np*It~K_S8gsΣA4s9X/{5ӱ|s:גga2f+a}tᶘGdV>J&֞OY1u`%Ĺ6?y2ŁXc"浰 d4߱;;קi8o߿ߘs">dip94?Cv_7`OIȈ +I`9D|::_7GQĽX_=a+Ϣs_%}Ѿ{a 1u/Ks/.:+su XZ{y J﹁1c[m%s Cpf^s%q~tR48n4>ylcz[._s|k:;ҹo+lǩ޽z>7>z~ 7i QO']]|9d ~ޜ7Y>h] 30s;]-C+ou ~vù!ӯm{v\z\Hv{/V3񉙇þT9 {Ͼq i|S~3s?ڑ~WtwNYz|w?Zw8^Ϋh벘_:^1Ń_̓ɏnp_ބɑ4`/8s0O:W~v:Z!8?8!,Mp/ϝL랈>6͡{v {` ?qnsGT^#>oR,:An}g+}fj6iob!urtU`ݽй";K^v4.9=pbͳODSf"ѹ+#Y=8>$gO9~BJ{3Z[wͯug̼ןC:·OjߢjCG+-8]º<桺̾C?{F wO漁Y_&>X] Iϻ{>?)aL鴾r;oh杍>oG~o}8~֕i<?ko 6>f{3z'_ӬS;5w-Wpwuc>11?>{&}{G8 d] U~mfWHݻ<1]ZS?OG痳yH{ `zW9{y7+B6_;sovJ~}uC(asa^ <+u.a8ܝMvΜC}cn惴pAH a39fk}}7^'h )X>sm=T@@v {+1~:^1=oJ2{zٟukѼ҇sw=~Kg}tƜ+h]/w6',&;Ϲcψ}S`fwξ;!΁yгFs_5 yM|`Ƶ 4 u},g#{Pv"}ɉ{z/g8υy/g8ϰz{{_>rgݫsX_ufb{u2tnyBz dn}4.{7fGv="BgO6\ľ|b2wgϴyҸ4}gx Xg^bjXsnv:6fA} ϧeV3[CqGq܍.wx`:iG/f i7? p}H87a|ѿ/g|hn[~T_c #G:S݀իQ\0;W{mf p^ uq"yy 3N:ǎ+GWH~ ح]DzޣtWu鏿 Y#~}CaFt~oC| ѯ_ }Ƹq9ӱc_ȟB5Xǘ{=3ϧqW;'=\}=p&Ʈ7a?_ <.ց{鞃}-@m1̼Uǎ}12I~@xE}WRm],pڱgg3w,[{'?Ǽ<փ|?yyy[g86p'_l_b*tN}ִc̲{=ټi~)=/ute5[:ڭwUpX{-t}=6<s* uDw䇶_Cpڡnz6w`ƅrO@y5}{:iglI-/ӿA+'هzWq'!FxfsS'$s= gGٔYv/os{Z9` "si\Ǖ1P:4@|0߆qSw_}EC-Wi-h.OztܳiƁR?kw7ݏٻ'&}<8GuS0TC m3q?yh xg_mmB?s%pTw%\-]G=jOtε㾞W~g~C*/^.K0ϸ<[<[@A3}8r`k4or:W '= O<ywh_{̹hqV:ǘ q0);^EC?YYyy-sNi*o={$7 {m50WpK}2?uhft ~#wb%ozoENߕs'xG>Hi?3ݖbʿi|ԟJwZoiGw /ܐW8g̾:Yg[ttqmz_yy[<wgc~qN6N2۸w]t aw[`[o}/| ~ޏ(>[}1+y~X "@}~1o:t?}k^tN z?2fspqc{+ 0kh~OSpos>dfKtm4pnlW?\8ܣۧ 5qߊ ΀po܇XlSs]+gR@u=d(O~O8 08z ýȹkˁ8eb}$Hg뤙fpO~wqǒc3뭘Q} ;?u+a\^G};>t s98z'١ٯ|1`!q/i=Ip[b+>照[s=I4!nHqa;d'}śS<'RsG~t^J/#O73k^8`QwhAG-!.G}s>ɾ.7??؏7X;=x8?yh+]pNi;fH7F81X5ŝ؏c~͸~tɴ_F|}ߘ_A\+c_;G8{˓.{@?cBCgg( Ojz6kOGa}Na_-gƹ͕;#zKFf>Xp~ 7.Mqe{b;^+s6 l qU8߿ܸW{i]"Όs8<{cgLO{A`he˛۫ݘ~o~ ϥ{30nMk!st:bõ87Wy>))zwg=>Qsoc6/ÈO~C;Jpdb>Ѭo>ލ~~4/ySaos W2f>/O C܋qO{G?;Qy>=9ےSCKS{hͱ~d7 / a3}#{O;08秿}]mm;brnw?O݆GZy\Zcqsc> ^uB~hџG}km7g_Hp~!fEʜ=oOrk<4_ƍ&}a\>/2̽;=~f}g !1wAۚݵ|KDs?ٗ{:KqX]A0i{>޵iK̽l~myy!?,_]b7Ɠ736?~|pwS{>9٫S|{|9'a YGĺ?λ쯺<_cof^hi2~b_oI齐M43o{ͺ݅s7{q1ѿDdþaq=Lch~wGz=J 2ܛW.7Lq^Ls '';&ROl(7߁!u-/Y_fv9~xy^fڜa-ۿΫ3ů+?>C͉/O ;=BD]ZgC_Xsrf>sߏĺdw=f3ѝ9t4Mcq#~>/3st/L6_e[Fǂ?bP6m) DZs l^ aDJ?<.:/Lq3~}i'~{a0O50dv~wo;˸SfܫCkZzEO-~B;'}w'#{پso ~aہϲqK~zPc?S~+`}X+3X9nY|oF+R<:v3i}?PtFs_pc@2E7~ _>W:l֗{]%=&c=g~yjol'#nC (5B+[oI~}ݷݖ{90.o˜+]ߟ~{&͇_ʳޟvkk>97W;Kqнlh}~ ݥO#oZsӡߡ8:msN,~wo q~taЧ(EP،{\q?`M#<<1yc 2ssм`6`v ;~c]V:+4 ~$5z[Hp_O?}:-$p1m/N?p?6 ӝҥs>wp/9[*ދ?9Ƒ}t,ʁHw~"gcφaګϥz=1tN_}k?lZ׍sy6'kDgG1co.}S~us2d//C<0ny`g]R:ߙO,_qΆ󇈇>CC3Dӂ;!?E~m2[fփǝ}O3S4/I|aMm}>q}i_;֣ FgѾy~Kt?j᫏3O[@e8+pj\<'Sk|y=.Os1>vh_G$?s*HI>P}n[ƽد}qSߦO<b~{ð`&+Xo>c߉uf̛`k1ψ0C}W} 1#~dsdo4S~ٷemq?7wOkz>ppg8_y-Ǧz1K\a)`B8va܁qڣ} ?t#]ЃI5=ac?Gufz]:ܾ9/=%X/F8K8smNycW~+ #澑 鞞oS?{NtNg~s܇~g?;~akSڟ1yS-nY7s>E{==0 L0߈yly l2zߏ9<9_q?}~qgHNv9~ܗvvsNGeQw"f9xw{NYb?Nt'?^{=q>c b^S<=~pu~Ay-up9Ͻ⮥^+1/>tOh]p ;e֍e~t'h:/{LfH}`ܧ13N}h7B of\GqMd~v|~>WKg^A머ro:.q?gbC}o慱_؏ {|?nc\ž(9GH=a_ߐs.e~'WD+858gտaw'G?{99/>9>w7=i/O0qK>7 }dB:{yޢ]?+ݧwY1/={SsƜɜg̟zm!?N6sE>h{̽ط}i3popq_?Y3l=}s~jA0~ K~[Ӹ Hx2GޓօѢuǽǽ~O{^V.,SA~dIt_%'#h켝uLw}NycK4~:Beɉ N=)s/#Λǽ8ς}Q /u{kлX})K㡵w^'e0>X{߇"d'}]X30[NX}۾h^]h^ eJ8y+cqvi|^:s/^oi?߲~VDߓEy!'b?4i7Ѻrܟ34W}~8{ po~O'>`ge{f^ؽGnM9z9ľ;ib]m4K[Jb(}s%h܀>~~CȬ{1uUy `z)o^C%eL[a40Sܓq"3;i~{petp/n+c~ }Jo{@3?e1aKYv^K}7}Y|ko0K/_@ع͆pxcgh~6g'-?kG]+z}FXOXwN>c*q~NS|f*}?~S%KiGgDW ? '^|Lʅ?k~f<|Onp\}g('Ƚ9p;FHCk?*k#+Ol=3k#'>eQnΤ;= FrS#'9y\Pvٞ1NRƺT9YgNIyǰßwo}qn>\'zXqG9cK'@N8|~:ZEU)s'ߖ"W|=@<0ÄOyJ4˪ +{O}Jc.6w{'g}3KEsgrsz >s78w|i䡴GoEЅe~ ;z n|q{SA]M"yS|gwH sҡ/)|Ï 9gd/]}5|wBA4?_mfS+s{dwsB;_۹'[׺WoSPy{կ$k_܀E'uPTy@M;$?[}PQyYO ɤ/YꦛY@{p΂} A|i|fwt+I|&*>@dsIL6ē uNQ}Sg/*o&~p z`yJ:z&'YɃ=t>_ǗBАZ_Y9+,~%Ofނ/lm/ze辡 ]7dGCwj}h"*wlB?g^~;!|4xuz3C9@tް7 >s/ =Gܓ5ې'@?RutCN>b|C)ƕsx{X)Ob߰w_W>]-g> |!?'7xRpxAo%޳uo9˾S[YpwEѫ0ӟWIUm]{1XN e~?7iЫGoXN4~ \v+c'}@a͙){}#W>+sH >O/H) b8!/7{Y0:4>'w21xcG=2y3;%&zf;vy^az y~_z7KC8ߜ~-x%U/+sS5o4ݯP>p3xExb5)z|ϰgs~mϡvtɬKDМBY4Ԟr%Ű灯tQ1:ε4ֹEWsC=WѼ7֨欧ŸSA>^re g#~!:[_ '6:FPΑP}^ Fu+ŏ oz2΅v:ESI<\jN989VU&?W oK,>#{)MCvXKӣM>?9ߵSJN z*qij]szCyџl=_He9={\ 8sG_L۸w : 8ȇN^%z*(}h>؛C5O@2r)Y>5Cٳ?Z΅B3]%7,?>=peisα,pٯT]ѹQg)lyӓ+(׺~eȩo)Γ-N^/\1[;y$ryoc-zVsKQ|{}Ŕ/yUs/g(έH6:=ݏ^WzQǖ؏#X)yϑC*y r_)>,?Z~_83]!]zj{_<$']V@!9_cvp_!5S[\rSu¯ʹCP \yeoJ^S9%gw.sٕL=0Ws/N~ xttSY:[fCO>ttV{9<6%ۤSGFYZG57s=[hNΎ||ƿןM|DzO1DA#'|Oc\Wk%גּ#F?G眤EK꟧*{I~@xJ{}ku‡W+{˹G%gQ+ήQupd~Fu)T-eu ʹ柯_"+|"xIT;VyHD17֪ wf'ǫVSؿ?\@~PgXF ܮ =rYw>aq7BB|?![&??i{^GnW·N3^7lӭxPs9$ n̫yܨEtv柝DB?͒K//yޟ_>38|}FOC? ^WQXLu9qӫ=KWtqH{.\?sjTg)[r#UoM5ϴn> ^'|909_7s8+8e9p$Ehя$hލ?zN;RSm 䐐獏$|#/R%9SxO88u꧋GE8A39tOG5t.'s |O- #>긭t.'A׃09z̓"|c_c ^S+{TN a#G<$8Bҕ_,8Hs?Z䋑N=5RYi._zg3ԓW +3}.^ws# rvy$;H=r\ǿ*_,'WG'.Ѣ?X}OY6(u h,>ķ᭲,y;|K?0Sd'W |]Poy~F?[a3@zZ+\Hp詵\_۴_~zy5<3D ZqO>@|.VVOq듏 Vgoɷ~M*? _% >d7=cO.3kx|[s'eyE>Oнx&/(I'K+j*xw65krWV>A^ZuT[yt)6&\T$wz"/4CsּO%zOHK[9Asnr_S{M3tO }5{!例|cϻ{h_&;|1'|7:E Ɵs>1auI|ჶv{~-"yȕ|T{N|uדx/ٿ+G!G9yLV 碯~dEuɗW*/9ybnIgٶ[S _ Z~Exy;֗tWwNQ:HL.=#_|m$4}oڿn5yK|z= |::f|фOk5y|啛s>=G>C`qv|, /%x zm/b.%o({X)UD9=1[BUW[~0x+qɫ=ӹH'rЭG> ~_Ӻ=gKk.yP̚ҵeZwC{iooJTSV9kCDzwDz\io,/Rs< ;k`NA^8Ȣy:t5SFN3{=$/sxb/(S>/E~4si'=V6XL?g)`ݧϱy{D|7M/A׮9g%w N]з'G^}W9瓟/܈| _?"zcVXVxaw|p\*IG'c$J%<$|ygٜCha6y&ݳTQ'Lo< }t|?}{՜K<='G]ᗪh_' Rz~سȝ=.Ss(bp|]Zxz"S?I_?tQ?$-l.V+y sK Cy{VϕdB{=6"r>bgIߏ/ 09r|ifkt@Ѵv>?O|͘t'滞Vd^C?'Yy΍&׾ N-O>J/5Χʹ{rB)<878Ne=+9i'h LT,&Sh-$2pL<=PmCn28XZ3?Ocݳ/#%}7r~_7gz}.pO}S*>B#Ÿ9e&?yHzWHA.xMPkGr]H./~G{q}{>/&Qn9l9XȒP-|58LuK|O#<9b?<g],Yy׎yYYA!wdRDCO^5ø9! 1` sb{jOoѼC0○Jl>猯 29{9ז^PO;~\x^z j>GڃDN&LΕ/qry ᙂq^>!ߥ<r: &ܚ5WyK/|א焾kgW[H[;XQ|1wD62tJʨ{"ή\G$}\si- J&3=sW+|я>BpÑ'7M&Ϝ^ rk O?>XjQF'iڇ\ (xAYOG,;¿3|Aρ\~pK>C!pnZs3yãuԜE~wtEٷ#%S//9O%)%A{@~u G-@ B{.#?|{1:EzTK?^|%t:GK>4W{s7DOֹ &[_?(%?]%zZ|ǔSƜJ;g 7뉞5spE_'gg#׃=?A?{%jH2WGF{8[I=wu| }Ѣ>ǜ>9+m̤>_{,{|lɞ|ON42Xz;؏lP~OF_ɉF^G6伄o/WA|(|{Ƚ}7ރ_S^33\7#x{`MI\|yasڧ&ܤEs:'-;do8zu[WZ~_N7)U |w=HoV_Sg?vG}9~G|Pt_i]:b13?yM U/iΧSXO~ =u;Z?_Woa5d?ބu+sYJf|4O?^>_$P(ד.;~^z߻o}{;ɏVnǞ8Rz}pt[X{/J>~ۅd WO@/|D {uyh?f?s֍WF>z~xcrעO<w:'์c䞡DGM/MW L{ z.G:&fIK@=^mɹW V}vٽs|{aݷY,#M>sz j !a|+?(=u!_ygV,"'rw'C[yj_yIY7|AF7ksp-=C9 9T }$|~ s}ÂW ?_c=x_}]E/5K_e7,+ ̫%4o~4nNO53KRѫo^Zt?74.;¹7#^-=շDnM+:dH } Մ'g*os?מFO^8'[ ;?ý} Hg_w+ll[+?ѓpqw7;G%08_GŜ'޿pGzJ8^my۴l7E>{f{ oJO7YGsyfa3AD~ry*ퟵǓ[Ou z96oA 88<8pJ>EtG.}F]=qo]]S붞8]x29p_wt[芲 _rSşz٩c}/rhm y{2r~Bp΍+},@ H7Ott v1\d)7?8@A߂3~3x!벓>WsTn)-ރg¿ 7Ρףs/c֡j|%\D~,ǣH'!o ],sӅ/\]RmwΟʹ?-{Xc6^/-i}r߅WrNsrϕ#;#ۿ|K7LeG7zپl_%)zrׅ.]͗C/@9|r/7C_ ~_:+{jo<_!L>FCGEzݤ>M|TNu/y(З,܆ҟ o_wzx[6+6gsqލQ/;Wx }'s\|!b*-SD>cx4»%$=(jG頳=gδq4p{KG.:sj\/]efآf)EBWhVKBd?!:Kr"7<6lry=9zW`a?ެ\Sx+e^Ϝ?/+xHik8$?7d^b'\>}]=yӘ׺3_?}ȿBo1~>̓g$=ɒJYӅ/+OM6.IN`__6.<WڧCEq(adNG9ХA~s~>a¥1"|o<ϑ-IoP[ojϬ}"߉^616+~g~xo.2=M=܃i=MlF.{?tx[t魳9w/=|/߽t``4h/Hw'J/icل?;,9[L{Zz1N&&=t!{ei|^#' d[s8~X@of v@aN^_DW_޺ދŸyҿT$#^V_s&4wc!8r;8'$%a%.|q#/>t 'ScM|oГ[],dxsE^ 磣#I:8w8k3wޛäS#rѻn29 fKvyfCAKA:uk>S:iy_rO$idDS{;OsY3Bey8g.OY'n$$Ü3A>9jp7^=-; s*'CwLj4{dc͝Ylyp{i-e?|??|HFɻ^jN9{>{4ƼhyvtFܲSyN}Nxq;1O= xaL1tːs Ӝֽg)O}f_(:׽7ԏzq F9'^OnJy 3S>{_z=g{VlԟWjm s9Y:tg݃o+|'Ͻ2N źp<J\yNaOB9ͷom=Co}LYF~6Lxu됝{BCc5Os}EBK\9B{f/<}:YK<[w^=ޫ!o5(ߠkF+rˮ-)V sxr] Y'~bpl6pN+ѯB 1&? &~q|/.=t 0O G̢<|яe|3F>qx{">ީbooCy՜uEGo)BN^9EgK {' țWH*ߞW24lo[F>zkȯR~@ƍ\Bwqޯ}98Je;}}G/ o_/O 9y&2mq;C G迄`}Z1icgۗN˹߹[7\% [J-W4ӎ\_,y|U;Fs}tc7{dgjWF?7B S^(H3g7-sO5tnޔiy<4ǹפ{Ǖ˓Pya#7_)>=dAW)0cJX]|y< _E>u.ώB<&J9^)?uuv:,<7Odpz['*{pgv}74w;ҏN<_ܟ\GqӐe=F?M:T#Ow˟+ԇrj?8)z%_G3YV{SfQOw'^ҷoS _aK6Exm:?of=|o{\s|2pVq[GK7A :1}t#uͥW?+s~/ [/  '_Tq9v'?`/}2-N?>#!9`sW'(;W:NtW -kzOȣ 琞y빗}9#/=sߡs^ ycT55~3\W ̇t1)~#5K v~e?㟫x_:&^l5J7k/2z"+O?ץ/# }?OewH-y ڇ^F?|foua~X'/ +g(!3yDGN}ӝO%Mj珞=bqͥJÞϝfd'xV<+<ڇ>xJs((9 〇rҧT㦐_jOC93~&OA^sC,ys0ɥw@3|_w%'e^U>лDA?ҁa)Ї2CΗs1 65Gx̑=< X9zr`Iyop`}[__KB}mCK's$:g犇|Amb{iOy>^уi_?~=4YMCqVۿ= },*ű[CN켋=Ʒn?AE`SLGL׿oFRLWk/_~1KOӳƿ:#uVd}IG#Iٺ*yϝ4SOj.|yQ7ܴWyF?CYw~|r o =Drk'@Ls&3y}?8üNE+| Fetw}fXWzW^?;Ǒ}y?.*ٞ+k__@|<7~מO}cJGt? O)Nyޣks+w =MoEAxά#@t'gz/Ǥcn>3O7N~9G'9LziK}_ zSpAO%Od{=o91m3CpC|KiWd~I;A{oL>;zeyi|恘oGG\}Ps@e_pNg|@.0zG&'ǿ<8?9w?%籱W:*-= :;zQ??`?R6"ۨ]/znλE~0]>Sz!7^FW?!} #o_hrGл?jO?8<_A:lIK??"%_iuJ-Yg|r3~}-oʞnЛU /1[w4Xf&5Ht)GĜ7|m#M&_|et`䜆>پ`rwб$^) /B~wqN|ަ[2wksLuGͧo-":+# :r ~O9Qrϒ.8kpuf]Ӽ:|=ytg}rΩC=6>D<~2_!m78} :J^-t?#<ոc5o ?sNd|Kn ſ.δHx\r#80yO;KXpѫ_>'yAg๚sɦ>#:#:/=&%~8Gz8\c/{=}֑K3Bd.:oP{|B''J8B8z~{yO)nvG߁7 x]|ż몇z%};7H>@ymZ܃w/3t Q=+փ2]z|||q>}Isǽz#9ג2Wp/Oqp Ml#Gϗ0/GC~~Sl{r3{}g+ssD?&wLxN8@3MhX;ބgSUwы_UGmP{9඼ޘ_I:r㛿,pD}V~+FԾK-܍ɔƽspp|gҗI}uO_z'cGu}vWQFs]m[Fϐ7OY=.ri+3#ì<ƑtNq9WMC'Q~ zͳ|ω= zcK+߰_2 zz?𧤯jNwݽ^˦:t&}ұ5N"7i~xtם~ESzQ3/6ĽuŇ4Ԝ^OcEcp%z6n|WJ:r'/ "|t K/X7g|K҅}~ } '&'h y /|JxB(]>FJbuav~o4C }.vW/` ) ?-&S3oaAoV3u)_ =#|3U%!9OSaN!z?9~~ұگ9_ƹPK™9?_ =8yΧO7zmue= ͆ND/.k/b~b? wď垞d[}]{?{Yc;3?TҼs>P3[ ~{?:[SxQ W"|jl~}gyH|e'~#+p3nË7abӯ>JnI}gtn2_OQuI헵g9'~OkC4P*hDgƕ9~n}? ?C玎/#J$;xo# .eA 4=ۃU5nVtWBA9% r-6KGC^Jt ô?odNaa︘_E1b_鋨/_K5w_桁埏8;61C'=gת{=;x yc3TroLsW+?pCչ9w Guzق+/ .ao.ӣ}d]᪵[eS˲[ M\w6Bϗ^-(DãgJ5׋ >/YrKVugr94Gopg" 80e HK/?V%Ĺs ]U?(}papl4roΫAH/ :>\{I~G .0}͗Ck>}ik[ÿ||h:[K]:ztzbn92M<tNV$Y᛻KP0RF{كяћ'|HVws08UxZ3~#?cޅ7tO(|"gV{!1?;79ךcZ ?LS#9ȑr_/kgO69ַk,eOH{Np' |{07a眜srP%47Ч1uzhލ|[u/Nt|Oy6/7'zc9b+t^-v/Ę!g^{7:V(ϥyrK}C3i>anUƛJW1|޻lZ9wp=0~y /_y07SF9{u:55 ⿷Ϳާ^~N{8+m(g!ީY@w3>Gs5G̼9rn-{?dS^䛶; Gt%I2_b 騛_.~w@z:#)aLs`<OvuUgυM|Qz<O"?E.|) }$7Rz'ɇ'n=GnC@ok?AUYV%ʿ{#y_}fįHugŷν@ >SwſSNIhd} 5'E>F^까މQ'g?xpfy'xF|gZFi}nG͋S_yHDuAS=fv3@Mb9Wkz>o9~ p?"X_{D }Bs}{ yuҍvmxXm~xgFU yI!5ZJ{O^#cfd^E<"rs)%+U|ͥ_ݍ#Z7~"{G|=9uYJ~!(o\G/Du]_~ =${q_%?7 zQONre ]*榚#m7O&/޹>t+q9-u@{>?r{A_sa<+9tt`·J/<8&yo!ޛo4?8¾W fĢ>_/LY}jOG-KpAk7ģ֏WRxC~+WLJ8%d#+ >ǾozxI0鍞/FSSz|. [tv'ұG=_εD5_y9u4f:W[5?2V-o ᜎ^$Uu8~upu;{QJQ6߈GfA'9?8ޟ1 c=%?:ky9n\gH_s|tr!'U/ +.ן۽EﶵW Lue:$̋3#WlgPc{s7uSut7T\\y&ѳ/b==x_+^?\+tϟT.M"OŽhఁ.Vz"/$ge^P={Oeߕ,PtCUj'3ƾϤ9Hj_|}*k>ƒ۝i,<0K:ٟK ]эT7;z벳y(׺ǹ٤:/Q744_\>'91NO_6x&9g1tm"BܑnF's?ra4߫O.Ϗdm o O WtO-/ }Xyޥo6e?_OWN4=y;khNU 6Dٜ{4NQn N~ƿ\[9MRGN:<spg#]ɡoBxx>OO.p#qaQ6~nڭɁ#?&{G _̮}2-iO[ \%ԜH}s6 {io|W ܳ ECK:Ar| {s4?߾ss 2?ȗO@=ӈt 73~%ᴑD'rɝnc{1=^OZG)OC:^O)ADOB*O^7[ystvutO\俏/|&xamLz'3Eu igo^d`c[y׺L1؟MFqەD9c|9< z^'_^3H8iwVUc*Y$ps5+ 9q\9׶K->;z 9?KC>y݋3CN_)?G|9"rh? {o,)n n?j'G>:LW2h K='|jtz'DψϧڌzoKyzc''ЛgSk6^|9 !&lC5[a<߃y#~o6=ҟL.LI! d)J<W7Ƈ^9UzwūSF.}U[o:lfO [s/rt" V\C=Oyl?3{\AE#kN?a> N)l0%{ZO/1cW~ze-=| G\ _zpl;ck N]~yCl$})qz7˯^KNl#g{{yWD7N]z+~}n\:)o'rݿ6pVΡ?/;>(~=_sǔ~R"pfws}yjt^!eKecҕ_T ѣW'KV٥ϻW;Γ'˪3jtU9sWfņ_~: _y9ׂ7_ 5yaXGs5t)9qNϷ%ܒ5лZJw<ͫG |3V~NQh껊/Cķp:{摽px{?7E .=e>s&D6΅YSz5ޫ7ѥ΅lC? s[kޒK^ϏN7rQ;>{͍:GŜB-s"{0K䨱ĞD~|58Խ;x}18 qWx"yEK_-[TI-yd}D:<=-Q#}"{)}=!;+7]cu7f%{x}%4I78"4=3WM|)CS=?Dnyp|^K!Kf &^^8M냽?No [1yM[\_r<@?N}>+Lz(Si5ų_?}nd$ҁUq^*i&?2xHc|,0?EzG-"tJ{<|ӫON>Sz-q~ңG>?&p;kZ=Iٗ=YHG}wy~6W.'r7kS{H'oϾ{ԻS =1V.#omi\=gztTy7ʫҏ27Ug/<r5?Cķ:E=sISgO*εNv^qmK !:+'<6zS?bNzHW#;ei ?߷x sߤ Yu.DߑsЋes~IgWN S)I+]Ϸ/䣒O Nμ=9 7͚c;7}yֳC &G|#so斿>!z}?nWs'{WzO~=Ɏ~˴8Gs _ěoO{(Q#: ?r+iN,fzT{;+K>MǷH} ?ݤk'V}3rc}B̅W~JqsTZ)O6Cde&h>y Ŝ\\oj R.}Wk~2V78rx];!=U"T\ҕ׾_}C.~ssjȷt}6| 9ߡzrMO;KZk.K?jGGe#xY/ VSQ+zJz_Z^Tg dg]=gS^"[N#A/n2YS{%bOn9_"iUu]%\>Ά*oJ .=GೆOsŸo|\qrߒ]~%@?{ugI^}O>pЏc9 )(LgK(;Qz9͵ԣAWN^2z%BwI4̿'Ss6AOFW xnL[ŞK yLə_\zYv{\}{ GsC/ ^e;/}tͫJ'b-`On]p=)?y/] /0g0շ'EKrtנwOqqck(d mlyw~4eDP}Y48=f܋õ;'}5zod3\΋+)}C~_zZ9 '+bѳ绱yߋ(Vې_& ",tRsxNF.{K{H1W76XA ~JrUBcu ݯe*< ҸP3B~!W콑7AF{#o8=S+]ܮsGV[z7I o'ID iQR{hq_'ג|oxEA g'o}<=Yi%/Jj+})͛37Ny?JYgRybבc~$‡?3O{N[d!XQ_rYs^^!r4"SϜ{ywՓ؟XE~ocunVVSCEBM}Xyܿn][2uos7>X]zsdMLj~&?. w⟣_s< )4'3)O} zY4<w;O)\E{&/ZG ُ 9oid঵դ7̿ݻ;/>׸n ‹ˢWa/$װ9|{W$K׏/3K_=,ҋ3L?㡳vo9xose7Ko;|{otP[vnzqsn|ON9}{2?ҷWЏ:]9Kr^&qQ;+!XONWɽޮ9Nw3K(=3?7.q)/RW5xgwckYHMz.|~l7[7oh:]FΫZo ޖdp(lܗ;?@;s4x/#mwØw:^Ŝ,ٲG6#Ѕߧ?7=TzPzq]hrk&*lJn~?o.}3 .-KdtD{@Xc ό\_>Lp~~a4EOuCѫvi|\C'Gsո_Hi$z1rVW>>8u*+Bo۝DN(t 93E8H.;}/{F>?z_{oȞ.J}y}'˟Hb~'BO |%.+^/5ßA{Nڻ8酎<$j![I|)GsECG^Yr73?I7x}߳SPrkʱܑ+u7o/ޗe.{~9|)u%/InG^sN&xk9f4|6:w `{'5_wgM81㠗\a:~>r|S|yB=~6'γ}X'4g?i="{kpG?_wN~.k_>" EƑ7}m絨F-sW){is S\\]`_\,}tg}za}L=?YMzO _5*:ct U_C-zҚ_/z *yOCf^upt;Z ߧu^Ms5<g OoJ?7S)pIeľdrz9y[8ػwTKW9~{x~Or:++׈:GO)Ν?}ᓻ5huzi#ΊrozmYzKFNT9(%ӒF.|Gw?rU{O?%;syW810_Ïg{Gߏ>y_¾`y?^f٣n=jv#i_x_|ݪ \ܸWeo%Kwrr |&L4'wŖ~ԟw'_-T8{t̃)99ROνtEAOS({Mc}I9wx|忍Vt+7(un_RPe6?^]ßһ\sukq{_iFWE3=C^Cƭ7>k\ G>ω^޼~n|\q1ӫB6}9# zdF9K:W/ރ{$<묇/ٝx8?J8=?/51It7N48bv]U7c}'l;/}i)[8dIk7w_>zϤ\DN[S9EO\Ysz:rw#y |wWY_GKI?$xbTw^>Fptԓ>~.7-?־g{:on۵HNzd0(W9_&}?y/E,6{ ?O9#~{EY鲃7V)pnqB^,i;eNrMr>I~iM|Hxѿ>Ƚ {m7>dц\޷s?{x\?i\`EK^+_]\|%_\qf{]cS/\>'ȝ'pga~k9-Ko)\ l_}"Q%W=mdHNTO#sCx?pwrgz3 oIwyTByη~K=C׆t׵WB H6~tNwv^h͘-C[~FnZy0ri-,kiHJֳٺffGM9uG^2Jx*t17i\ܲ׌+S\dN%<%E~{n#GKӹ؞Tܿ%$GQY@i|oNEYFL'+_|V='y죰Ο4DoLϡw󴺂xE.}NU5ϸ飧'n# ,uO_"s9=&7ԟ^t99U|/yЇ9wGXs+^ni*nO\xr ύ>z.珯Who.>r*1y.NTx{zp1{ptP:N=K7j^ |: m=n#o~XOYME&􇅿`3Z/=} =}F0#g@ul{O"ރt-JFv(t5 Ck\[x,[5WYwkߋ|u^3zީ9rm/H8%:!9 bF_ٲҕx$>'.+'A_8~ ~|r(CF8`ui=ˎ=WGz7d# *:"𯹯uGc+=y0[r{<;)'b>jN^]dnl(~s6@/9)Kݳ=\SOwl٧GA;1^yҁ=n<hCENsȹMx3>GΥK4nD#}: J}M/W؛~fDsrO`O)?{@x/ 0zR~Dzȡ7;g5n0rdSݪ?&#~|7K]lnFkM?!|ޓޭk# [x?jt9xWW/V>vDI6Sl}7+>濤j}[T/9msh%>8< x^8S,Ig M~N—ȓ@@>{/sxGwz |MkNC=O3$L('x>xAK{/#1ssHTo)OQ:#ùnDG>/Лjq; -~3U#5n\*/{s1W[/jhPo1s9':~wZK9><{c/;sŇgmyXruйRH>9owh^^Qs#?[ړ:SӈzVM;DcF;?k _79Z7Xo~gBH +ٺ;פw]Rj #o{CͷmwV^<} g\*yӜ#tTtϢ_8 }rV6i?ГG\\0p87| |nǗ^9r[t[uߓ/%ㄜ/ŌǖuJ6NN<ܞx;T3GW.Ƚ~'Ga?ru;8U? Ld EJEv򚻬Do~htj ]Lwٜi]wo;fg/ΩV^SilطGM{zyTOsa\=ܧzq~K3s{9|~|O9gȧ9I}{x^[|Ԟ\y 䕣$*hO1^3A@;=Γ'w h'.i~{g g:sV&ǽ 5u.gFJnyN> '7c3l~)&=)Oo^?ys zLsEϯs0z38oݳ oSpmr%>W躪EKyQ"Ux5y)~<|t$Օ+:9D?z4³t9J{B{ K >(g AܘZ?G;վB/sw=ͫҧ {Od> dռ]OX1{V7u^"ݯKxүE~:W /$> 'U} =w9?|:sEFvy3z&U:'=?‡i?Be[O#~}Lr<?'Gs..+ŗۉou~>v;a咄yQM|1z<ۇ}]q+^̝%OR9ʎuy|} _L~y~Ћ_#=9E䬡#op:{.z{E#9ەr3ܼ)QzM%^?v[4/dDwGg^[{ߑ9{9}S{JY5Q_Pz3*˔rOP?SEb:&%rޙŗU2W}noSO虳3W\^k{4Z3/~Q\[n]hz?8_;/_0{XZC*1#G~ͥ{/Z'8"΃v6-1nux! "7:[\ڷh^~ISG[68sƝs]=Rn)?<ȁ#-'GޘlZ#USwZ\ܭޯ|٧32[1gLWM7oz۳W4@:,޺FԞF`!7EV<&@?.Nb}E-+sUE$~J~G*s :!򃆗>*K9y ,~wp՞^Q-x,{'}̝?'iOO|s|1 WBr#KrBpo; ĺ(t4ўS,? ٜoZ6Nl5y=FFy핔>sOE_~bztF'F'lrȡ3VۡNˡS?\:O|V*G!h[ RrqѼ[{@YڳGfNԞoMwxc^M=>"Kqosr(>GgQxae|4䷌Ω=|0|1%?s{k*X{i/[9?=K+rn-}j7KaBޑp1r"9ȗȡwœFN#3|*ly^^"y@yG D8(?&gAQ/ ߄oC!O<ѡoefO#F/RN%8\2 {d9%{:U5[-{M+o=~C2@;~W ~jq/0~[F]Q{hf7}bş'2y7]=уcMs֔/ʹ玫[7~MtXBNGKg5rtΕ Sn*os^jR<sERЧ36+{"9&[3#}:_>_s}fsA;ϩuB7A/Ԛ >q:xvɹ_9}-oJ%o}N&`IXx߸'Oy1^Ww~©~?g?|P"Yz]V.a: A^c^J{frDwQ0RY3ϒi=cy:\>œyG@>L&p|׌c91 sCd/w1UߑN(r|{PBVr/x5{=w轅ὫLchc?F >n(NcmSIt58g_IwxF~~bGIwF^"9cѻ]22y'o+? !\;_]f.\aCr}1Ǒ]M3&>=r4Hz̫҃ Gޔik(ڤ|?ɧrtMѳEΤv}[N8:`rًB`8s-|*#=-Vso&sr" -?e !+d翰wލ߻ХL} 9U[G>ATk7?|ѻp ρ4F/o~䰱һpƨmN/<~.&%+Ζt8gsH<8N ޗ"?uz>'#aomEsA,9ɝ.瘞p1oλA"7<|޿aF9~|2{UxG?̇Wxtx*zVṺOsDޢuNc>݌Wtc#;p/"Λymr>_\zG~ 6S.C0/þZnzͧ%kƇ?om(^\?]zIIscгvwռXYP:Ց nONY; WJ|~:wQ/9S.GCOE3 shrߒ=Gh!~9RGk#[F\YA̅䚠gE6t8dיſŹI;6vuu~,s敺c7ߋQ慨I9_QGw^9DƯ=p_G.>ySA.2Q1cu#ב!G~i+ς|SR8{c J?О[yBDOWyUs`O|;oٿ=Õ?$&=+:0[972?k@vsZ{X>י˫*gG?xl9Kqo[Ƞs/ O{j#O!/sy=9^ʽAW9p)ۇ7 ȑ GޜzM }0<>]]C=5p+sG.{{yo!__S! z୒GO-B2nEq[ LoQzȇ,|FE?go.yI# 7ziOEZ_^=gvU%onK5{G_+IN}t\.'O't#V/~9#T}B9"|kF{Jӊ=,F2V)͟gUOѹZmF~|*|!9U("ynK~Z]X|_ gGzCj[~/oo072W 3Oza88_+KbR}WN:yyIa}pUgFOEbs+(s $TγťxW'}(О~0^)fz7wΑ=)Ms5W﨟yt;7yO~{a'oީs7Kޛwƫ=Ukdu˝{'O;Lݜ_ίG/՗5åWcט__on|{\$rEswvgunm>AK,|#8 :x/pp#ćSY!;/ >|=sAFO-=]Bg%}rUEugπ9~:7zj߂^7 B"/|=OlNr22+j<7c^&= 9{?OG:ޏZ3JO?&=)? |$p$r5/U!zQz2'X>䷢ G%}ʟ(tlU&'=@19f_>&ӯ*}^x7WTBrCc%7%z{#xGhnl/߆8r1O+tJt+'%Y'w1ocopw W^SDum\냷f hoDs]}@9C=4xHWYzZ_EP>(lpGT}:{_~τ{ 2OSC"=< [V=7(xQCyޒ*s@ZGi/L]g^웈jXwP 9IyT~|t3ͷoZP󦷢?v)q'<нׅAC:Px~|rXWoׄO5bk]\IgWY{9 N/ gM79{>Ea.t3[kk%yl%|O߼,VÒͽǚC\U{2:]|09l/\nぞ y4ɝB99)vpq'޾ȭqyuͫ%| ?Ar DOuvϏ޸6۾}^uN0R_JSS*=֣ggM9P9W;9sI(ȋ2__πB?:Iqk(c':Zt|_ms=Aɾӏ=1rO·y^Ɵ{^2c>gz͍G>2g/ȿߵo\{؝]}"UyX__7ǝ}rx+HO_qx]Z}{W:+K;ym^y+.s}Wn;>a={PF瘨iȞPOq]ue= #=\rk?IPku8S\90__%J~_z6znڙrL:c<oL.s A!z0u<{es}b97=ݹ+-ۗ'T_)-sma݇wyT~IYTLJӾKC_ #U(=pIIzH>p~ 5rbnϒDzC7^ל_i#|EK~#ke5s8W^357~ x9ǡ"?]_GSq'7 z@=aoO{=sϜ~/)ߪ3kEt{{ d+\3WK=O%N}s_&ʧҸ8҇\g ^D槭SJ/Yrx#ܼUy?N|%8wRz  +W,G ^f/!̤츓'qyOiP\N7[S{,\O`}~~\Q aWo$J>Es;'iOziRotJ\,;җ'7yc'S!=wgDb?ߍ+sMF^9K5?<3E ~L~Mc'y{|G"zdE_>"y覜ED}S:_׾2zZ+!'r.K}c-'Eϲ?J~_t{?9'^"aGAsENӹ?ɭ[U{&!WK!p,t ëKɾ7cψ r㉾3'yo(~ .o% qEY Sż>kKO~ >"Bw\ /'K}:)ƛ[]mT>$y=\ǝ6V9s2]wf#.q'K_y; ?7Y k Bj[@;sW,>%>a]Jܒ~b?Y:eIη֏^?r+Ί%,,EoX GdC[_^@ o=C߮ݪ2"~,+~5Y񟥾|MtW{ <.- }m79171EEs%m vKϯTŸm@>KmsClzt̝kt]=8w^59)en$-*ܬƯ!6>IWw Է2?38}f܎rOgғASKaF{ʑe?OzIw⿂dk޹7|6Ҿwti䣠"= q̃A5=="=/MgWӜG~xvpyÚ wؘ'}z<&2ԳJ_#pBmASj%u :W%['/{{{nAWܥ*[&G_/a?(.*X:=rS^dr·=>F{o oNpS"߽*p33Gfg\/zF\b f .XG4N(qw1J{:pѝ#;ϜRyu ׃uCoGFhGF{13|.9'i*,|؟z=ٟ<= 3j 4w^<;qg܎{}o6_%wppFN4[ o  9(qƼ2-P<  }y <WX倐{M>ي[u_}+]q2g4tG&?S\UpNԞUz.Zۺ/:p 9s%__:o:@K|9+}ݺY$-SmN 9z*g|#q=A{Agj/>/~xԗ6Q9ҷw0RkoF?BzSIB< zפ:C2zso_ =x:ԾJV{3ޠ_-?t={ =a5VlPrS4ҸʹpャREck.Xt+݇FGmBIOR|X"YLGK_#˲sq1עa.w)?x}Soҳ. 'xEO~/K|xS5ݛ}=y8\_D`!ՇSOz?V^fkzﳿ|!WW%ľ9ZX YE7.󶿿2㤗vr~ΝlU66{lsrG W=K/@/ákj? :_(9܉c}AoR]su[lQrw3q.zT:ٻEW=z6ͫ7J]!tѫG;{J|~o/S}#<F炷WL(9ů/z{^_A}n./b 珽,(;RpΠÅ?ܫP_ggZWvGrq+IXFzD.9d~B)rg#!9+O9CgK}>CxsK~71ʳ"1S#ɯ"!GGݻCu]^}"94^=UMtF??/%?S\"yy]5 󍼇G7yo7W6dZ^tּ|VAE_\zr/ ȡuᣦG^G>O{ڏ\ %1z 4oxRM9W[g'` A}&Có GFd_LC3i_w8|B_^GsSf“?ńSe/$9ȃ1g,b7ˊ)rEt^%tDD%BDž=+ѻ>p5t}̿['g:wZOY/3wNc>\SA_|UL {|WCE.|zC`KC'^W{ncp>e6>ӉԻ̞>z>rrz*ίpX2x#kj!W!c_g RyO{1f~>糽bOQ^=dfvo,^{LљпH>sH#J| rlg{n䬡$Qx1gTy7j`BrսCpm;Oş4_N|kSψeNn)<|O)ټS[r"܇\ &?!~Q§h5[ނ<SS8vhd_fx@\O;X]zb5n$< = BGoxf!OL:Ϩ=Gx}z ^|E%,'zɗ }-b ܪy r(OЧΝ~tos#7aZ_ ~>j<_pyӣSL!>^zWB >z%+ZNǒRLbyÜ7?Via/H'g\xtR>O~,::7)Y~>Kt3_J>7پOxNk@?nQS KCEnږ~,Y6)}_WȊ:'j h!\%}P{y3w z"-OtrU^M5w0΋rهW4T /Aw{{ <3ճM'7>%ZY^sOQ\ r0.? t8 Wwo+oEJcۅ5&_sɣ͞Zܳ[>ϖ?)+>L!|UTԮpe ?zkjw׆ކ ydK|sng/owڛ'\dgNR7<{i|o0N R>Vgk$־t#(%x6ㆭӴOCk:u3Y=ޓѕᷯ^Mry{>|{KGm~" G{}z HboIO ͉6ӻD=M}UNgP} n:bSB<{gbJ6cJAnGwP Vg%U?W ל/q/^7ޛ{ov8C9w\^ >캦p5ύ':_3HV|3ުx0z&8wGVϛ=*rsFr{?%?٬!?9zѽQo=w >4[kz9K3 '=y<ݷ g_8t~<a5sc|~|7b ?_3iSqg~mJ^|$zӕcsuwK4n0|e>)5{ bYm*W40 4ru[}~\}*<B_m=ֻqr y?爽q(\;[U7:V )A=c^3ܓ[!V) @McK?|y:|z6ҕK]P|n,᳕Kf/#rk<_7sK+|sCHZgC7PCtؽ-t۫.ޜ|ĸW ||dz >]nZ8rӭ)]K7*;t!D?Ugy~ //|Q+^9(R}2E[==Z"<:׷璿p+y~aSks5srӧ6v"C7;^%gyJ͙GgF|<>* r9ɧAOE|݋o%<7?_q"ĽbEɟCnrTNj`]Dy 鄕]]Uɷ]0%w-k_Y0?Bn;W~ɍ-cNn.@l'ԋ* t*>_X~bzR?ut@,#^l-=MN[2'+gv֜§?޻;xG}lrNϷ{޵ DG9okq~:pt^ܞ}O~&zL~^d'MvWw.|]'~-2K6xz?Kߣ'%'~8N Fۧ8 vo8y;f?bO~;|+HCa o?oצ~ynA=1GnY{iҿA-8tS/ {AgF>#_&Zpæ9)|' u+/r'N^Mw'\ dO往{G";tM{=çM|ͭSWu4,.yk+4I?){}R{gS*_<1gЯy'hOP%>2Gܿc$xRzgkO.=ro:Շu/zϼ@p]w66kV< _/Y0ϓ瀏{Pܡ9Yy=3*_Ob1ݯ'g|n~=5սg|xi<Ŝ-'W>=~rOܯ~>aVkI'Fc'}.Srf/SX#[x3y_gtK(>*}#n\OxkB>jrWe >d+|+ao{ZP9~Yw_!"dȭNHA9/Uq䒢G*Zz]朡/_9C>bAyj^׿wK$5ܽz/[vأ"VsbXMs*8ЙwjCa?*Mg:ڢʷ$T{+zj{7qNw=O-H׽F߳|o>aoKǜ^E(vxO>|6:)D{үyn^Eu;ß'Z!ūۨz!IBwҘ~7wDΤȡ=#Boavkkg*8o|糧Zս3W' u7EG~z5k'"SEz`oNO<:~"J_fx6z/<οס|{O#'}@9^־'}ћnzѣjzby^pZ 0o@p{؃o'Ya]4N~ ]:#SG>|쳇^-@NO|B=zdy%gpn997K 'W]չgko\/^(;gSA'ar:OKy#~{>ʑ Ff>aӌ߼*= X3(}_gM%0@~&#7s`'?g׹R:,Y[*;&W{QBC_e~utfP&"礻YY/YZrڜQFG灾%VaNp?S]u5Ɠ}F[}&ρ\jxgxԷw^󬲓K4V/}/u{zK>!jS/&ǭ%/پCzџ=;GUT6dA))œ-MPV{/ z|zߏyr/e*{*w3q_W@Eu|t/J~?7sG*<=]-oB#0xTϟw σtJtezCr;C^rg5 ՝F0x}}vlG8ϼh[ ,cKjK9z-'Wy\)GiYF_NZG߳uPb`zr.O@"~)7>OIЛ_|=& 9S*&gy֞FDqΰKޛB{Wc^ϼ Vrtw^=^xi9DA }ctkgo(_9|K>5eNϤ^_I>?+8o  ?Hy|ʛeI_nyp1% ܀G',o4Fv η:\Ê_T=х©u>q/G%=:Lٛg1K3{:8-lm( ==cWY4ߓKָQTNB>lx|.gWZA~B`oRރ/$]/e{}c= U7K"9@?|o[ Ir:q:_o^pazUec}ySt=ȹJ>BSYwMm K}GyӷΕn9mb|\GEތq4<_;sl|6Pt9_25"W:FFuirs|?~G/|e"gH^\?瘐+n&NQgy_g9q u<7Ӌܭ[/,mhͲqǧs9{O[c^>+OH2d<%3KTzE:-{W|'@r48Rc }Ow.sը߯kͿY%cy8p07|[wɍb$￾𫙗82.|XQG(#-M;HG7 =7ЇC.czi;k׈ws.q?w-?ntQ/>UxqI4֯ߤg&F7Z׌'@r%LrF<|=q^#| rw|qÈz8?r5NT 7}g~vtg<\t{/;C]_Y{LexP9`O zdgk.G#%7sxWIopΉ{)Oz1 nz 9Εڈ1$j稟 K;9w ky~ ޡ(엝oqe1cuLj.7}X6 3ٗ"V2 |? }79GDBOEi#oG/P{U>pBǬ-ߟ<Ew_|uF cjp9s/i|~}p;>;dƟaeRNVz<4~ Ѓ㗈Ź85'ԗ3c>kzxZCiܯqW?k~u'6qwpJ7t|'W oHQ.:Bc|G7rޯ5O.Ћ:xI[2ss gso7}8|bǷ1s͍À |~>ё? }T0h|*3 οY hNBϘ~]+}>ɝKh}07gм?) /fs*:8R{[8_qVܪ A>kpzABE~ zBֽ:KZ r:y߲.+g7?*7FG[-_c~I+.j^>dx`>Q[oZ+ ay ߋ~uW\{?~Hd߉FKg)j '`GkzOs5<'oB] CPSG߈sGs:wϕewлX?[{sjrWD E^ZEonnn/%_qk73:XS96ޫʹ?Ῡ0=S<499Y?3DuS#e>O}be=//^ru~39پ5o?io}ퟫZ3A/ :Cqe=KTeikͭ>@Ag?y)=?_$8xWw{h>hΣ1W/{aC$z y̾}ч//~^ y8q{D;: 7?QKdAF>_6Fv&_2/~>k%]y0у;esLST>A6|諍\L{ߓk^ApFU(x\!Aϟ \Yl?>lzOYGi̥ΞS=έEO"'=%tr3ZQI/B/jhE 'Ïݙ["b`*|ł_ЯD%)%<ƸW0UonmS:zb-0CGI>b8?plF[7{]s;ɫw G_GIQ+{o'ΥߠOFf|,u\}Oe !~~K0^3ZQiFO*oispӧM~6z+oc? }_dXvu5= !˙[Z7_N!nKn(~^rK?)gJ9a#_n,f|Wv̳cG DIE~#<%av{fH{cz'j'(k =hozE՗[?뛷s%{T8KՂW(V?8=9SM3_K{<@C:I:Ko#Zp+:+w=# DK:sW8-8N нѼA>P< xbc!-7p?{#Љ[[OE4_C G?g!T^8]?7M/)|KzܼM~޽յ%r|oGMސk< t*ϱiK_hW'5AZ]]Lo}{pp}# _{盓:[}R燏 O? Qz7h<NQλȝ^}W^:d-|₏r2O"|kUoBy^ȍ|VLƃ+qg^G<WͼwԺ/p1:#' _ ~zIw}konQ,X<0q^+ry:D[_[ 4HZ3A#{+@E}?yO+2;_wxB6jЯ̼%y\_u^}}Ot|3]O+u#'&dww-?\.}kOWyO% r!O2Y%w?33+9 t,#]\ʝv]iL6/yR+;7r9/$_^m-.\] hmR?FiЫ[1[9s>NNr:=T4'ol2=? uVt?xeQ+Crނ[|oANV+$ߛ= 8'=+ê,<hS.:czk/9ew y>rQ^gW~;D_ gsɡ_i.=i1*H^%WŞ\ɹLx k [ne( 64wW\Seɉώ~&6>\FV^1>!SO<ν ;E>}14.=={Hfo#|$xzv뇋G8\k'OɡǗz#ܸCO\yVWcYg{Cg9|Oņヷ>_1pF,Gz<#/; B|J>OoywcZzN9-ѧ` oz7:\0r' ]zГfh>?s3:OG^׹@{Prr;'<'<=%49BoK)n Cm}tI}N#:GƿaiVֹsO?&gO's/t_h<{gǼĹYC< }4̕ï;A9x*#gּb=~Ƚg|0<} _wJ}t U)ݷ4QMx?~L|<=h΋$׃CrUV^',"}#ޟ<{ѭOsylf•gPs8ɏ__ڽѾc:xgjm|w;w)G7k t>ȽBןVsgկ#/QLHox) xHvɍn|ȃϫ9`okO̦<'ז{rqrZ#~b7&pWy,>_zvr=ɹnw>|Ӕx\ ݇{<0| 6Tx ~_Qsݛsش'S|@䃓~,=KCJvsӃ>/Y&s%)_4ɓ1NN'j[͝tyN+HʱOf>S>?z~8wgD/&r|j w߽蘙G*2ޕ̣,r7:/n/*/|5V揑__`( 3'{xB97yJٓҽ~}wGc5VR}|bΩʽfB2>4:n'HqȑHn:#7|9btHNP>3gw lzK_Dx4=p@tI  >~%r rg\ϼ޽tq OnM6<|iseLVPh^7齥sSa/F? Oo{|"zo{|(zÜG 9n[?ڲϣw~N=v:~ 7ߡHPȄYό[XyՆʾMh~Mc~|O9[=J~n rɳϦ5TO :|}?җgM5瀼rs몿uoL2t {ctϣ sz)G*{be } yȡBFom9OFs9C!^)|o膙/_ks<|Pcr痜zNb;r"'ύtSl&:y!XmgbgiW<'p1'wH~^qxxátkUvs_|CwzLN>P9IxR-pv y;4BoJ<9>믪hIW98OON*]*©x^7:g2wT\42O\86 ;/.]kmgOym^Cn j0nLl >?=z* G 2֓:ӣ/+z޼w\\` GCvs Jpl۶mTlIETضm۶mcO;:gAQ^^It+JWiNl6u;G2|6<=[^^M>BB!c7g>3Tm/8c}t䇓ڼB8urrл~HF>߳֕'K z:}ޠ(>|<$N腮 OBN' ~ozTbF8>_:*njSps 5]3[9m_^WGJSzώ<ܘce?^a eM'>GZYԣH+gVl{t| GooEO qE9n+\ܾxw|߃?*}yizݏ.ѯNd_ه@YKJO ]}n{EcϤ(F#S_ $~bde/;=MEe Лr&7k/s^1#/1|Ћ߹#1־sx }_ { {_ja sx]j޻Occa֣K^^S#b4F y q):G>̽o)7v4rwO z0+F/YO59PໜKēO_V͝х+oC#/v^}ģ ݆r:$XI=rC/{M7oIXz8Rrg!o >;;?1\}Iﺵ TV83+"zg ױ:Y~xgjӸ {y3qqN_Տ5Si$ށ|(}17½ɶ{,t fm̕ՊetG]?eQ٣Ͽ3>!)+_9Ö杭#G7\\&oje'C(1{] ңv_%ѱ_o2 nAGCou4&j,vsϵϺ86t 6~/~ʸ6{Epq*C{iL,|lO &J9qpc^#<(u,򵀷oG-YK>9|$HR^]?ݵ^@o<ד@j1:!4=`GW+ܑOu/2zvJm'}EM<|H^ft=7?BP9z bs.â+7]G']'χ6uZS>?sTsPڣ+vj;i|e@0/=B5s{{SgNT>6寈Vy\z ܳK`1 /y7{pv?>1Y!g>\MEӓĞB<=1rۏI=rZF9j뇬;acψ=/"O>@Ǩ6^ayw~ckY+YI^EΜYkCDuv{:yS7c8 ʗ^{9,!f9 ։k/?tmEmg}ĻbH{8s.0j&+GT6N-Y_>dxD _D~8O*{Vw^FGi:c4̳mNgo#p' />&] 7kI\F;/u/ | Oԟꍍ9>l~U{ =/6~^}=C^~$p e#LK)i|J.9xxooú7~/80 uork*Xx'ؼK7-#t{%f+=*W,Yߧ>cjND10./w1GɿW@''ay+.~optQ`#ﲎpb|]WI/'c Q{QQ{?[yy@ CB>(}UѺV>7[|O̽h?a?,ݪy'\9q͛ij]=[w {Awh\e ɿM/,媀{\8kDSKrΣ4?_nGetZ]gfҼ0=T}^?B788" wo)N7x|!!}*KJURQ?D~mO{T6衳ybeyӄWWn~YIF 21 ^\9C|>f <)|f:Gݝ~3$FO/uu+ho-(<<5| Cߕ-c}TS]tÜڛ~ge}ț.j{tO/x''܏|Hzٳ8t>BAO8VJA{|1W 6h=ĖZ=r-IHj">i/yٻ-?>}ykG{_r[mɕk I P~`s %oW _u|%W_eNcݼ::O!Usi2ދ{ݕ5|?{ fBv㵭1Go" 1=Sչy%ޢ~uYFاg<[h}tNwq~'_-M?=Q菱?$9$腺εuP;O,NQN'znJIo7/!=9Wi^j瓽>xWcdU~6khNol$ }*[ȉwJG39g7*6w58i]սP`_#')ɹ+Ks3W?WW:ɧ7pk[l/=4_b3>ѳ>}U*G|+9&{.ҕ 57?b]slߟ¹{<#Lmb0nz&^?pZ<9"w"!9ٸNg+\W>"|e昇ہs_de!:WSx>N!Es\Is+}|Jݸƥǘ-K/gOp򁯔.m=7.iH]9S*GpdR~ͷAmyEE)~8gIsW1y9b{Myt픿Y=Pխ/7/F1~HCs54k;(&Wy2E^_f 9]s2ésS#wDGZSd3oa^/&EM?;G9 ~HzR~m ?Bo,>QtK[pLI 9LC̿{p.$hk\kxɹy`5`϶Y?لo6fs\֟\S{Aw;q?X2+9[?4 =u!]ġO}m~ "׏|=_3>\/YyW<18fߺne1ϯߑ߲/w6ϑ~:1/;u>@?2OfKo za>y؛9?DTrb:{*^ؼ0y -hcsUQ9|^XE_<Ǎ{[iC$}}B %Hj}gE.KEgZ͹cǯ>opeFVٝQȖϤ9ފ<{>y_1/f9~Ӊ;6 .n{wpQg?K6Bs5_6/\Q'w{~!G}|LM3>5xu[rף7YS8 8285{}~>e0 }9;2>ا~+.|k WGϯwQ?znr}=ͥ+V3xYҨun+𾢥7aCO49c$%w_.w&z& ̕׀o2Wi`M}V[e^s?}9etuл0jᾊϋp\=M~Kiò4\ y{b.w> zbKQ8M"NMx~s9'ɠd?M+:M`cjhn(9xiirHej,_y:iOv/1z:C[K;j:~9iMA1tscsH8eçJ}cs"#d9&},gtϰR.^uny{eA њWw_cHIђ/R 6O]_=֏D]O@qlQYcy`ps]1F`9r[ 1Mv^t'8)ɚ:g~dB89ΓY\[kL&D0z#tZHŽIK潣Guz>HrW# )ݷ}|t1ng߬^?k o>'}-U˅seP7OIýD*Lذw9>='U ߕ}?ƹQZ齳 ނ._9'+.YK>c/è,-Lx<G͜O~Ff@o=A/r!Gi_}Oމ/Z|V=[y:3CsA#sTB:v 9ە|'S7Ccyt+sW{<{>LE|~qF䔦˫<7F!~% ^޿Gv0u J84߃DSzd+,7~1.rEQ(*~?)1dqcSķst$gƼ"oݮssr~%|Pҵ?}| rz -ۓX~>uƾBW<:8eg;>Q{E7:x2声7旾󤹳k'%MW^[ù]`[{> Θ<$?!s1&pt hǼ_j_ 2[ע:*ka˖ sns+17M}s/幌5G7>DM @'XB:?= =; 'opk=96K43鹄U*:qU@;S{ß;qGg^\s<\ i;2^Ls o:\-Ke E}VkE4,𛃯'ڻ^!W/BYr:鼩Mx:r{󹏪y>tv>8|wvo#>[-ry}.oJ{O#ޑ:~`O:9L)iӲqxp޺MgKLD_KO;YpFWbU嬑 ~F/}Q|Hxs]>;?rO<={}=_/#>m97; ~COZow=/n;X߻u7:/q߭Mcn3;aǠs{dN=y+EUPz+Cg+a{-_Dz_O8YBf{Wԃ\)x+J7E$<}䨖֒5[!2h 9-tF5C/q~ig=2~3딷~8r(g /]<>_[IoKTtQ_kjgϬk\{"E>~vަὩm"wz+ot ҡ/_nb[Ρkc[[˟ .jySj}vxiq[ 잆eͷLxpA ^_w@/&谛8w'>'N7t55MuȑCsgu.bvDo \0~<0|!=̢֗ ==ԿN~ca|ϑ>$֭WO^clyZI|:&\~p1euN HOOoTKއy)p^xspb|V^Pqcۿ'ץ57=  } 97s.Aw.}y+u#e@-#g5W^=tFseW(|]Wit^Gٹ9qA!_9.9͹oɯ,mt^yW)2h}cO\s1D|=5wK{Fy~S_FG$Ϙw~o. s*0*;:z_$#o _ƹl[ARs蟳ޝl}/Ã+o@ a4Uyu]ѻW}JM4~>rQИC{q9ycAY-I }폾jpOr,o?=M{vo w'K`~@_M"}EIYtDw23slϩګ:W#m`)#= :+z;c]5Tvʐ?4?_s~ xk;E&:|ϱɘ'6s'2}pxdr]x1I}#ZOw|KS ^&tO"?yO)Vüu+}_ޏ`1ǹQk}hͧ+cY?ޒhz jsC^zQz7h>y1sSm!~?|gUoT^k_wa<8s:zsWtQ^缵rD{1O \74-'W:͹1%optjDїTpqڮ XGBAwKf/Jqo#>_=k:bùU3q3 _tsw/N2o?KH N~ηdGplrt|s͡wlr-_@ʼ9S8 3Kg:c|ΏΡE=B:[ܧW:/^8E}=>sG弑gЗ/P{\OI!_݆pnQ܆™Y>$g> kn{Z 2W7u_wU'8`οj gI-莜ϹE} /ID%*X,?UG\\wSGg{OzVr?ev[{/r[ݛ󵻹us#g] ܃9La _>A|ٱ?yN1h5;A'9kތ z,r~sOy9ڻ9GK㹪s\n|PTDxrc[=ߔoH Yz'GgE^/)=Ju??t8[}S멿*:T'7|ݣW1_>|N*_ leЙ ϥ&"A.vl]'w֟#MAp>pcCnۑqNeqO}?d6.;'؋^uN&!~pzksoS෱{bSҋ3]y[SvOA yAWVNV>g6 |]6V^yE4~D{\w-@9@6z}kof-N> xB_?NTniȱ}ᒠwύlss?wC!<:Puȏf.o28~㌥}f|-詳Ukk^$0xEzh<~ޖg=J˒\JOsx!W>C 䓠!_~>tGn>И]s|CNr_̜>#}oJ?^Jv/y"k+p &ynqKC@?|7sQrc+EͷG2ƙOk.Kx@2mi|ۿݞXS6m7uu?MoRnZw}+=?\¹P̗bcז/ѹd'Ycۺ\0=;[okRt_컺~k};!yGh͓uӼn!ƹιO%.Y B߉Yy<ܓ (P5{3GJ2b79}_ޞJ~l?Uڗ'K."䛰ǡgʇI3}NB?WΗ;O.sK2tv~ϗ\Ƒ7Vos'+(t1:+iyr9/&y7?ǼW/o??='uɟuh7%;>gt=e( =:0O W=6!9D+辢̹ܧ/^y8A\t| <ՆY}pzFL+)܋A~8h{'|o;y;'T>G/>tm?\ړJK N>XeӰ ^쨛py ɓڿ@n\=v j*E:>O׏~wt!(KjܪtK{|ӓc!1=Ť'靃o}\q yݜ ;N.AIsn}:W}Y:YN,sAW#{e/;ihyTݥv_>%ůxZƎ#sFMC6 ڊ/'^ZA:qcL&myDJ%y33p5T"<}pmrJQI+:u+3F.y:s)Ϥqk<>:O{+J _C,zSwrJ*JW]Uzxۑl%x{{L.;:9|d+(%~n"鵷GQic?uBG>q/QO*x_wnKu\pe N&{BCNKrx7,g{yW|OZG{9">Qaϡo%N>;>1G+'0rϰ+Ɯ!ok7?tJrW?9SLYOqϒ5_KZa˟=0_6u}ໂGbꞨ>Pt;FfBIke߳=d,|rH왂 W99=1پA;NvΝuA!;V+ϴs(AkGΚ:}#_zmJY~:d\}&zҐIk UK !}1Y:zP:w Co En,=FّMG;/{03ab t7$3_ܯ~Ar!2{Ln^ʆÅǯ~' |-3>V 4voUQ;ύWsϋ\|'uW7V"y/`>{~.u~smȍI4f=9ӣsfN'SrpHpc Hyw=A>!~Dt1Oy9c-mO6a?},[e54__l>:t gz3rEؿi^ [؟^S.~|-O4)]Cґ lB5ʱs=qxC(wuCyN(lvo1?40_͇lC~\>ps#wr͟7wR"'%:F>>L)w^*tfߙ2~'b{KvV;Cg{WN1b^z|gϕ_tQ&yM_i!0]yosI?ކcuo}Rƥ]^瑞%DԼ2g|{S%~mr kK'2]J)Ega#'-2xN߷¹نWi8\sGv}]tﱾd>C}~_9= ^ ~tϵ/ZM |&{tӦ7*פwrCå/½<ܗ-zګw=̟МEa̯s'y^01_¹z%s_zN؃c~+ '%߷;R9 g}:C=CxB{O~wJosqkKƍkE,ދD3:/~> gGwM(<~"ƹBucK}.NbJ?cs?F5gOsgu^DEFuaGL. 8xO/ODF~Zy]Kvx yq|^}a.m+;8s=n|an$/\FxcNa6~Iy\ԞS 簯Y+\Y5_ 1`5B$ńK8񜗕//^S'rOf/Zy_^1QЃW*yW;Ig2ϵ 9+!8g ά>I1}{>82qOݤ<_n{1xJ|YYA_=zGs#@ zOZYyqoެ)T?#pИ#k,zސrlcOsZ>Y.<QUwچ_p/z4{;O~",R_i/O2:â+EL-x$!{$_\>qi\%zLIKG#|mzt\p{UyG=E}Ϋ\Ms\;D߰aDO$'B7G1.gl3C$J{zBGGy/CPk<6YL}E|t瑾296ZxC7r/d¯g~wރ7z##/;D>gPR3CގlJz"= K/߼Ssq2 uƞi'_47 >-/T;_轨^}4>7")^֞~VH|;5V?zt_3=99bAYn"^b:XQϿuQW_][x7x$xSk{ip;p'|Ew+ޡx{uF gGܑ¿'/.~}6s`pq5zOoW~)As~UstAՑ' <=|j w<3ty;sO?}; ]11=x7Gҙp?ߎ:kܻk?J{CTRz^ҍ#Ǘ>:ϟK}ϲͤgC׎_s]Qs& J:/ot9J0yO̷w(}j׍f(Ob޽!c$gcm6ֽ|H(i']%K;7|6Kث"Ĺk_c|YOcO[r˓[BjCT{ oб;7Gf}F3=ԭDa$>^\zPiINk.pbЩEny%ЫW>SHZG<ǗmdS|RLKҘz".m }{.Om[ʐK~g_-~x_$(ݼGp4Ii| ?msG=Osޡl}s`^:c<(O{Gr#ɓIƷpzMmA{W} {ɪKo}l?BT>Co) [+1E|64Wk;@䖏 y݅'g&AXISzؿ@N_9coƏZy;}?{ן^%Oj O!7k7vя8OVO_<猅+!$ie=_P>T"im{૘+Kh/_- ߵ=>Fd#x>+8Ugsܼ[~x#ڸ\C/__#;+{1U `o|CipG{}З{[>y4y)cI_:jU{G+$3A$ j)boEzw3/D/8d0^Ob7ȟJoOAceH<8[O]It97}@WMLg̻y1>-sgcbO~Ln@k\il/|lI}g S>'r%ZXSquii~η^֛=kd篭h}+,̃^?9{._Ч$ώ؏o5^T'l:ܼc䓋.i_g.,ύ$-y"Oۢ|v*T:eW~Ş`|>=#FOtmwF޷QKK|{=k}k(>%z攨<+(S9CANwU 'GVz͓@,~xrz^ȟw<Wj-^jI9yπ>c~sslx&g'd<Н"sテo V8Bo$3⽍sϖ syC=aAKqYOtkg *n8O9+8O1JA1o|YӾ朘[sy8Lb#ٱf4GO!/A{@t(~9M9Ĕ|y ?>ΰ󛞓rE>G ߎ>Y͟;/ ODoN}[d+'?3YΑl"*ב׼E9Cis~67ȩ!~Y!1OҟN.m58wM!>92yp0{C^S/.9|דR!'2dѓ}pYί<1|={|z$tL,F'6Cw]c蓸Яql,.(+8O9DZu/-Ykr곪_EI|A薺s(=S~:?Lf85/>aҸZ3n}rWb~()s"^9߄{};ǡ~Q0זހfʅ'~wZϩg \9Qz,jKy<'?u&oz8Gسki#>ǭ͖ST~Yy]1~bCt:ƹȗ>{>lt?u>G_˂'=؊ԗJNXyys}(9䷣EJ.3aӫuxT=Gbw[K:0Kߕ6QԺxɒ|m|1|8<vk.}&wPu>tNWCx%<59t:AϋkOĿvԧ8c4@Gx!!W&Lox` #y⹦p'g>@l{Kݗj#{!n_j?={<:9zDD;+֐Odtr.{ V}s|N ;\J䇡{?|3#%^||+#re fş1 gNjxt3IޏثzC@W+\4;j9wōS斯 ^?f{:y絢{f/W'\:wހ6K!',@4~|z3 ,^sO_:c59/?\z ȗC}{9NF NS {]CwoHwY7C=zdup{oǹ8iE[ކY}Ee[3 sO>{>gs\:/p5蟥o1Oq~\t̋g!| ~y/}窵}>W S@>|7?gٛyw1jOo#f_dsCώ\m>?Jw. }䗣G_u0? Ow`V9:߆9?~c Yc?{z۹Ϳb:T9T%! v+~|Xg> UλE?K Kr?^zmuouxC=!7vk?m|9cˎX_a ?z׉a|ti܏bs=r/[C?:zsܮ' {!r2|ŧN.V_σi} tS~8yˈ;/6ا!r?sϝsmγAt.W~?!_/[:SMJ+{g/;Ng*#mbo*<:O5&B䛡ǯFVzts1'97ϝ>Е_{/ߧg\Y&?vea=& SmݽXQ|Vt+c[rO|~΋zc}}:;Orw_>?Z>ZIYw#ϋQm;+>>2\Ss=3boY/Z^мe6ieO#7Ksp<|MxF!j87qU?XVy)[sOuW/9@~tx:(>ѷH7L.*6 zɚw}^orwd&O?Oӕw~_)zS03j q#-X>us{_˘*ْqub~.=&_>I7t?̕Ƒk.ߘ/<r"^ʹm_s9O`hV<"ϱ_y"CC^Psx&'@~t1ghea':V9vZG -fuLu5- jg9{jk"1ydG"_?/t'1W3&2Mŷҧ<>gZyH=?<{y6VJw~\C£5d7O?-r镠<>p@zuSC'WJANkʿȿ8">>/Ny#,~EWGq=z.aDn=8̹䮂g*?IC :p-OPϽ!ݰw.W:V|'Go |%ϋo?*A oayֻKY~TyIّr*O'{l-r_ed܏̜=*>^ !4:i_N!ѯ<װ~ZS}:\p֍N~.]".PohzK߉פiţFDU{3Oʯ <䞐my! =s p2t#Ã>=X:؇G,4s_DDg,||.;z*ᥥqu_7/ VB yQw{oE8$>\o؛cs[?׌{ȋJ/Fϙx?^C>+D;>]>Ş7x{z\g'UVV^FAȼ/ n[M[u.=%>'{紮?WrڃDnQ̹2wr?缧^ p9u>l̠+:&W?ܣ8{N_V=rхX+n޹rQ9{L,w]/*^X1g^ݘmވ{ y%̫ܳUCNv9/Ͻ9tN\p.1xwŞ^]Jq]ͱ屗s="Vo NWKg w_L2(>Nzɧ/:BO=OO b|k7fg-WsjCƑϰ}ج3(#< ~+ԷǾ  ^'Q3!0qc l9 {mQ㙇 _7<:r}/=Byw~ğK{}tB0o\w +v2St(䑑s%vx 9x/z su>i>/ܳd}:CӚ>)~~JK7{Lus`1x{?чhqB][Ͻ;B|>|WQ9ʞ.Dy_ռE~XАso1#A=CZwl?r2᫵MǼs{]~,Ͽ?D?8p|a::9c3r\{[i =p$1'>8B~^rM8O m~s_{ ֘A|&/{m~}S|{ЏYT|)8E>ʨSFw YjS:?Ip/u̗<]N nT1{t缙],nһ}罖9 \ -NW~m"o`9M}Gz͇kSyʹn8+^ۼ{ s%[?Xz~>ޠ=+=UyR|_U~WI{;[LsGC?g9AN yLKt_W^QayvQl2gB >tKv_m5ӛhFYJtk&oAiYtk>z\DGxyrJ}7˜V;@]Qr?$xѐ6ԯ>הNxGv~'[a)i78D9ܣ'c},mCEߚC fB^7=is>GuSFǁ)E>d1尥+(~# La"b7s>I xڽɁ'IO,}W_8rosN_>MT}zUS%ǂ>1;}Kϲr>W|`x| T8o}=|5rb^ПpӛVݿ-En}397[QCuM2OˣKI_gnf~m\9ǿܮDlp^š*Ϭs528o˗_t3C{nnh}>oX9lbtOuF=#xZ׏]8pm3W%o(^=1= ' %Kh?--{sgnp @kG̟?NOκGW=g|/l]ssq'$wS4:.(tѿ\YN@zN<}tɬ'5ΑŎ<koAx^[9d&ќcKӽm^oC|`XG͸%y{T:ȃG\Sxl8|{t?L?QvW}Y=}eOn+9Lyt\K/4}u\<|eC(]!@?JT^s/oߡJaړ遣ρ`AnK2PV~t}BI</G-OH9n골<rG98[A?dS10{Ԭ?8OO>5?@^U&z /~ ~?{GM샱N 9N.*='4mƎ! sjO!&:?ۺ$ػ{k'8GsdG~-g#??`wW%O>>nC[c7}e6_$6ydEMwC>Jj~{%m4szSF7sc=/P4|oܣ8Q9E9+[X6參t>T8N}\spF2F!aeL6=c4Kzg~ɍF^B7}Qo]qc5n\I9qKkɇmޏ> _L{[˻W9#]ԟ< <6Ii)ծ*cqΥbѽJx_qjDoߘ<veisR 3SC tͳ?K'>'gɅ_~޼9Ssqܳ|E Ðc@08vk;:ף=*99ioyG9|2\گ[2׃o7oRXd./ONWazk&Oofįxs~%[e3c?lҞ{{W'|xx؏F_\q='¿+UHuskn]W7ݬM6=ף+}|xb>}I/2S0Ǟܯtɱi䂱#\a~WA?@gcΔs}Yv~}^/x]Ӡͻp|IySɅ7t9+ͦi'_+gyrYG״Fɜnu~9"^ ݽ/zNf'"E{/ޗf ژK_ &ݞs%'(_9_ݢߗAs _4FE颲O7):tt' 4|; 7xa{ $`Q+g\Cx{=&g7{ {,PgT[ɹ<{(:{}ֱvnٙG8Q9E8uOç;yCB{ )й.}"{|3rPyycNu\K.EII:?zϦ)^|HNc3«Ld|fen{m;ZkWz ?7>ʝE{U9NS>j|SiC=&u_Hړz<r飠g{sϲ:_,[@W`_Cpޡ/j}phu?9Bo>fGIp0ѳ^?GE%vp܇p}>*G?t\C~=yŴoy%='SrpʄG?~ybOG3ߋ}*[+Ovu ?D_]>\ܲWiF_W]:B|\J+y@|Nl$)'[. F}>cޒ|シ<[o|8ihOg:ϰ׳ nh\4G}iBoZ[Rs)nz~Φ^)ߍq1?a[=a9<=?ߌ<}Ai{OC t"'䩦H/8{^??~܉9|:/Wn Fw}"a~ak^F@:s9sLWhE^R8"yl1v?'wGပ/ [~8ՄcEE?:Oz3sunټfF _|=0f[Jޓp;㠇9t&S+znޙ{\+xc~n{)9}?g|cݓ|zx ymb^u̧)KGqOP|* [1~jy!7~ηguZrO#^>S\<>Z+j)&.~]{Ew2}l?ƒU9عIxz#{E {q綳|7L <9*4psyR9[_VWnQk!z;CK,^Ks@^w ͱ~:jYxP=wwo:iWVr'=Êf{i2^O9|P3d~>6لYj=u|[ד+pxo+*\9} 2>e΅}X'L}o{1;&s5s:A:t[ јN}(1_x= /zD^個ml"?s,l1?źn1|.?'> \F(MݗA+=O[O(ODb[ N*䵤3}`ωs&17(_W_QT>|%(f?-wg=(V毯\kN;;1<\|gۇ_{6s 6n+**M*ɯt{`wW NBx8|Gwco֠+:'ΕN.i/||믚 u՛݃}5>y*?OIo#Ǐ_}?}䊀Oqkͷ}<tC{@^1z{[TYZxBB t7w{daߩ3SϤub~%}BA~UxWlIy//ދW}Ӌ?3>l%ZϗMCusd4΍ڪSy%/~1IC̽|)9/3) #{ɅrwVVA*< 3]Q)| DGOp{gm#^sX[e9o7QGks%)79o0OuD/O~a֝TvQt [\$}?+wN'O-/ӹT&a-(>A z+{97?ǛmxUE=ʼbs ??{a9#>9~'gNg@ys>ȉwW9j[_m)=;1zg/@ou1UO7'ǐPz҅47ѷ>-w㼈9z/V.fdAz*}j>T9;<q>]%=\}B-{ |Ϲr{8?OOKu ρG!瞽;f"g)cspX:$ #ܣ} {:z,rtro'o%F 8i䇌_:_痡ˍ ^Eѧ=}k =Hآ{Λ@בnз^o?OWb_ž%V ZvxWg_0Nv/ޓ\}.c՚C3+;Ow-ZtS<}EyszCN;G}?o2[{_J?e~~щDCTP-8+d%;1ޟ{ȿ!۪\ހd9]9vq,)(t3dOC .>=͢sHO=L=7=+^CW{ޘLwIs'GS6t3ɉ:'?}q/r¡3oֺ fw46 >~'}?@Zs_{U>=zØ?s)qe;1U<)|eÜһG{yrk=<?W"}Cçna٫xKhM'N1?&wL.. _sT5EG/x>OϡW\l٥V}15.#qN<}| k>I('slr ϫή7~ZrūD^}o .}"9{|݇<*ׯh%{;=/IDϑ#]ԋļH{ru@}ɟ'Ӏk=g'3_r>I53Fs Y .!(d wQ^s!˝`|=D>vW̧^*>nߣOy?[+;\=y̽GF}49qz}XiSz0Ε']Qrjy_kWuzQG+]\Q3 :0g_>:g/֣ܿ;\!~Z=@|:G=Aob[>.twN=7h&qy#z(&7O >6rx7oj1'>g'pcksIO:KtWԞ~zZgȧ~ܡk6tZcȷFM qtsG|9\EW* :_>QrJ#*5?S)}zfEKז^zֽ9\Qڃz7K\u/47DXE8Y' K9K5J+<1:2g%ŧ/~}ϑ<^y\7{'xh=|~ʡCk_G 931g#cb爽,EbnzeW]_%932%߫sYKkN=+%y%:{5|5ug'}l>{=G½dr>ƘKs)|sދ~h)>sop?gك$r- vu\nO`k/{*ʹEs]ȗz3EƼiD10: u Z ދ\lG=8@>Ti/.>㟻#>o!Kc~%+k]9Z3n1#6*^|xIts8Qtx7ȗ7#p?ɯ/lSRn Lip#/ݭ%Wʎ5D/\O\e~8Iqr! ."9h $W?ÿo ЁNwӻw\A9jsseoay+gB9CEm Zz@\9#Ls~ﻑ'9O{RwҢ`/xR>h3_CwEGOڐ'9H¯C>H&Q3<6W1ySG0_O{wJWQs,:|k;\s Z -3ѹO_\ݩ$'<\O7䤶A?_ܸdW=u|f`:r bsxVt{@ob{pzMd?jNz\>%۪q?O;m}wqᇈ$`K斡s 󡟬uK27hϣiwCR{Iae^wZHOܛ/rv;?gEs7noD/}/ǽ[kN2=܁/x*SKz'Z/K'C~ WB z\t?_E|2YL{fw&xlZɃd\lu^8+3ϽPymDx/ϹzrKS/;\[97{RyR7O|w?coyg.={sGފ^+?}TwrʹIim~sѽפX3p%/2}뎺>eLy~~Џ7bOj='C@mMu?s&ד' kzc. y"7@oB0-K[n )UJ dvz?(Wҍ _?Hx]&ruT} N?IGf{Ɖ=}Yo3h?};&+<HͭS.{AxM% WN%;Gt 䟤/~[|[O}=98L*_]sm9G/{ aS'\ss|C|HYR zbecDc~9t7,W }e@uvK/ש-sA:**w {|?^s|::ᐧTAP0cW9 l?xma׽AD2]a>i-$'?ǟN>UL'+tOvC=@{_n*8_ >=O?%g|\IxzF#wWVN,yW;a|$LJ4o^#*iЗٝNAÃӧ Ou*n7|Ğ3݋c%w&1szt|_1BOO#&bBu:۽ާbГϣT}w]Fy=i%,;G@܏1s:/G\]Q"tZ1lͷЏoD[vi/1=)|1[gXF86~rr3 'K ٻ_O1Nۚ,/!C=09;ëgk>GE}M]澢=pz(݋m=Gy_}锚;3ǓZzs+ϝs"C^r/1lMRQ^1|R[xDםQ:;to?<3kp>վn y54o>~+ W*=>9WQ7~IÄߍy!,-/]cuoZ18W=&ύC@niMV`@?1pp𬪹(4Ew? =| }yQ8MbWwQ&/^dgyJ2Ű)߄xLsy ,y͋[t]7nA3|4>9EmQGﴵ7gQ{L>0|v{cT3a^{7 {G >g Wľ?|X[{E'Ӑ<']嗸y=31(5r[ ʡ#?]@ % _ј 5g=ļ<=]jYݻ/=zbOC}w|鱦GysAbD|X֖ON4BZ @'X˸Y/$joh~s\0|D2{4yAYZCi|%+' F-5o쳱C|҅2旑;9u{5ot>s PzO)~arS%] {IrukBs!#*Q}3/w.O~=8j$8ϑ|KN߈o7ȯDW-̓CxqpOz!y(7 _o|5 ϻ7z;KKO|[|~ _{>\|^j7x; jDK~<6p!魝?An |{"_ (s ?'|J&ɩdn?Qok]hGbs,9Mb.s#_]'9YD/8 RzOQ"_Lr[ws;kM#O>iS%=#{#WV=g]N|ϙsܛ?պՍ8%|oNJCsF>Buu'_y&wvW-=5U= EȭO'Y~+?6ҹZ]sq>̜[{^z`/pi;+Eti]ձe3Oos#[kL_ jmCx!~ym#ߟ\Ytӂ9շGw(&P]_d׃{>7m.y;>n]=b73d8Mbsm :wOI8[O߽t#'5E~:Gyz~K~#3o9} No=S?ؾHz4wK_ |m옧 R='o2{++_2\߸=9cbIDK~4ػ9sL{~ѸE{[{:Wxr_|;Bw^r.pts/y%'-yϫ/|;Cg\=k~{OG'+\lK@/ _9ˀ\l>@:ZK>O;Yl=:tDȳl(7HU.Fzٷj-To:>9<9« 3X[3^Ƀ":G}]y]쑥ǥ!8J}N GeyWdEtf3X?>3ss>s4﫰oGb?D9|sWjR_+ʯ]^>_1w?R/U} 72?۟uUO$z#nнL49}=@'J^wN\gz/߉8o8E__sn|DžsKk2Ϋss"'=bW#׋+#iO&{{sBf'7{GOOe]Q5߶envˊS8c.ӧ+?/W|tgG?׻Yt]D]}ɰ;X> EWA.:5#|C/.?ppX1oQ~-A;EDݫ~ \9M{*eh-Ճa9C_01+G.+_#j?X{2{ׅwEx$+71?sTk 鋨zU3r=8c/9ۢYS—ֹLO [sB.:z2# 7y|+[9_켄/ |9u5. `i 5Fuťז\O'R,\wtgMb{*Zν?HO9|99&:|Zu$ âB4_ˠ`b?9 ˎonXUoqtQ|?覚Y/h@GXe{ Kc?y37g+}uy> #2빯{tܿt\f^%|wk=)n 5"w/ KA5Sйc䇡@.ܡX^B{_}@ҝoR;NKo_Ϻr,kz7z2&׍|¼{q|]rR_k@P>F+7zn?^/[M93o]''[?H>}R y.}5OܵOl-?K#97빯yt7W89aὈ>8丐F`}urkgos+}bq1۩=+aSe=^ =L #" &87;Wc?zSG ̯9"Y_K뻯DO-V1{uG $zރdҕ7~ԞTLk>0zMJ:oϺI x O>ʸIEKצ˦? KΖ<71weЋ>9}x/x>ѱ4oԽ# +Z`71G2Ο|_p+`bO'=Cj}'ymؿC@ 9@LS^弨犼TjW1'J^[gG&>?su)H"9Uw~g׮gqZri?/ >o foQ?ُ~Xѭ֧]>u֝ςm= |{s3m񵁷g=m^VmC\#{G_V|NC|twMwЧAXz:c_KvtQLb3gu>C'+֦sZL@}eYœܗ}5?p3zK#/^p󿧻kAϙ<4j zj^#[gι1x7*<45g<:+n%셜ڧ%Ov K<#uKGD^12z$Ȟۑ_-r孋{E/JymǢך&{W> wub/CsxjE'Eѽd'/>pl/#ߍ<83WױڹQ凗. ~U=##W7_0xg)?Suk _Z@s6ENOq%Z\KÅWC68B8~{r[=5 -s8:ھ3 YS=c|m=q~ޘII91WT9g ~ȯDޣs||)I]A'礠{]~N|.#cdVѾOG%~QM$Q7Ͻ,|5yȱ_yXQ]ti~ܓ|6:G7u̻Pzwy)||Oz)Q_EBW^1m ʡ,'[7|3͉t_c~ 8~=|Sco x)~bڭB}~|~rt;^-ωޭt2 <|w=@ 4yu6 zpz) w*эI7s~򸳓펔 }~9a'G5\=[mpNwٸ8'>|󷹥\xO!I??L-ӕƎKۋ/G>Jkuߏᾯ"O.}ID~'JKu?ŸӋ-:<肋ES@= 9iEڨ&W\|MO I\;thW=L^J7۲]z7\/;Yy9y/\oyБgߪ_/zψz[A7#u>U]Ǟ&3G9E9˓ N9JvvYtzK\r}1-n~~lxHkr-ϦdK o,]ckoqx]4w-szLGD.8'ѧzv$ׇ\ACj/'G/x:twrz֯!Sk~cs8'?$R|u>+OO~6rSE?D :|x[Y>U2Ý;9_P6n昫PN>[pd ™97ܮg ^9}7L2?9/8?/+?7yS]֣v Hg1ݺywƿB^389Eg\&{RӞyn{y'ӓqLs?|]Iq >=9i*H.e yDDr|Їq?p8ZdL&-q-ɇxdGBO+CuråӴÓ2V.~ DGVCGy.ɡ$G1ۀ')ߐ9*QH6Ϥ»7_i<<}]I:?}_ӌ :}9W9' XkGn%~y{w1\: z6sΟ81:3gz#/pq'~,U~^ErGqOۘx'SH#qx|N51U_MD/ۚПq1{W%(zOLN3jO φ^:BǺĞ'+!/&AɗF{3pKL9QFa g|?~E\ѻZ|*L LJ>R=ye?ssW'x|t&77Zikj{KGݟ~]z>=| 4"|q`INK?y|^178h>ܽyK?*_{ޘ7uw>wO}ioe o {Gx"<:Q:Z Hޝy\.<XΏ9}<>W!$<>J}'A}}s#}z蘳CN _|QS_ȉ3.PBy'ɃDW~H C t?k3|U?)WyB'0w3伍G>_VohNG|%9cUjqw/Z.> FsXv͏ yw|45^r ć?rpy:럻@qt%&}Cl2E%e%w_-=yOJϝV?7>6N{O\)5Yzì=?HE{E_&JxP5qůty|O|U9t?Ӽz6vNO5k{=9\{zߖzo8Old{%̽kח "K4{3@yH%Ms_{\Vc]Wϕ,:*̗Nн#57ݯԼRM yfHWq{*RDt.SHOy]H\̵p ?ϟƝ\9~{l~na+?ܛƁ#^uZUYԗ?0x+_B]yЧqHEcoO%;:Q٤ ZqrS򩋊}4sϪoMS Z_VrmUZkԸ9kG|=L5tL85Wt֘Fz~UK<{ Z6s67]S`ݏ5ޥ8hfh,Fwj=׋'z5WrΏs ɺsGhn=?AS.{l~w1ڃUsuX*O:g֞ν ;H]@xퟵ2tܿqK'GՙIj:#!5Xz8 pz43&'Kߛ\H8ϊ{w1[?~HG4?z_jU<lArxu>_s1tH3 7 &W_~Oϓޒs{*7񍞏چg{I/~t\=LM䘳˭C]]|" :g ڍ·n zl"|%GwyE|Y!-=9hHOz\lr:M.=>se[6֜ž޴DG ޠ~<.R@ɄnR>ITkޞIo}nw] \*s;NC] :0F$_ˮ%_ȺK+|mZ[]֞}ddϦpWg~z:9gl~'|vfCzdckknݦnss5w:̵w[rVk?{%WxUF^[/*= - SڷsNsҸ_SA.k*7~~0ON!>yчKVySgwk*ֻy9΍WHltqx;/ks ֟\Bx8sMZ5O4vw~kud_98;dpwTeu}m/8ܦ7.^ο9dja}ݮ ޟ.={i9R>Ӵep~~vӞ/io3c4$o)9Y[:O½Y_'Jͬ]m@/v!^Օu> O:G3{[' ׏ρ_@9g wg>k!XZ9fJN0toqpmFnx7Å3$%v۾T߳{W+NdwV\YSEO\{ {{,7@ نs;o9Ux1n_ |9BVʳ<[b?!Wuttog5]_:yŇuV?̓i]lf98;s1Oa~Ȏ24ZzoaH6\y{(޾~]'[wBy?ɶ=[MsN]'^ڗ+}%_*'8e`s6'Ә>EyΕ̈́f[S#>=]^[V8Rw~(hh`h|'==$x|e7x^f|/E|G CCNLr[SGG|_pƳI:)xT:NӾ߷5j{>|p|(򕥋wWan.{KNc͔3\P?NCg^i|5;-ndKWGHM{="ݛ7y sHUGxϡhN* s[C>yi_(}%{jIӳ W5_)4曻gÃ%_oN{ʹܧ=?=X|Pܧn5|=ݣאw xitU[Gi!=?[|l>m|f+.sd: ]&|lc6>#8n{ZO#TrNt|ҸX:?JSEx |Q>^ӟޛL*0CiCFI0WUownƕ3=r9*kA ;J^EE2sܛ,P2x(g;7R "ʑOt' wUI>A}^{pZfp>4W{t_$s+ϡpIY8!8my܊rؚ[J4j5&}&G;5qVc[G|xg)hO_96?7K7D^'[e|^d++K t 't$ ;gu?645Jtp֯)^Ehc5r*tn>\y_%+IvS/*gkcrݢzn(vҞ9]K_ s_dgn]#<̇oE<):Xzp_ l'/Y=e{0E|w?yP}7u?u&W=iMP>yuCu4vυlқ(;0>w johUk&K?Y-t`+I/szx]t2N*;̽g*%}K=}\yryщF*%P)zQ k.?-׾v?:[ﻯƼA{Һ7+g,*uΌkk/>/>!s9i,{)Qs&|Kg-Yd(g6/s?MR1`g]讻5GS˧>{иymtn";tp?hOK?:K1~{5;h{wŬc_ԗXr:Z&z`gl5_6SArAmz}r acE1姵9Ժ&p%ޖ90V Y:T{ұKF?[ϯ;L~C]rtkٔ=esC[qyy5IUL'\}D>w:9kצ4Ÿ8FtS ,x󼃓l֟&n狹 dΨ\h=;_[vɭ\ܹ_'v_K^^M$W{?XCquo8[[}V֖<[V9[q8G"3{gEgonN">uD-Hkkb?ݓ}WmryO/CMֿWKGu\:zOH[\Gok*7 \}OH~^\sro[_{=WPV=uSJo z{'s7UT9])bhԮv.YfOOgo /(ry.|Askv,\}kbˏϮ1> ͹\uHgqoew)?$~?}= xf>K~ϭ?7d̜|J~^#ηz~џnܯ< fzljmy&6g|/ɝ[n$sa:3IO~\A~#_=ZIix5'wtiϪVSyR~8Y.-(w;cWH/n hsƅG=*4.HTš˻G _=mW46u?yw-| [tns.^_9Pҙ/qo죁.q^ %)hn>T:qEtnw$ ҍIOT=׍޿/tϑ## ڞy߯:go<}67tSʁL?T#yF}yY)'xJ>z/G2|SJ/w}Wam(},{C8hn,^(YV}ϫg +|m76V̟tlt\wF"AzÕغI{Nk1ǼJ_ׁe_sN޴ެ)!4V>} 9Ew2FtNQhz褻ُ~s-j; =} kqG{m/ ^ _hV 7K迂7=~W=G=oo+\#d7i.A/8=8g۠CW Y8Zrio/^ZKg={ߏ]n$i|~<ڧ-lk1?6PkyBsy5;NxjiY<r/+q}}x}pEtammM ~yأ;:y3GQڹ99ko4M93n_%]_=Gn3?|3tx|Gh"loP}=^.(w{g ̞мlZ t(FR?ig[Ux}t׶3>BHPOb*c_8ļ)G~̏ym&nq_W>N, ur-E'90A')-NtG?6fU/=~il[pV)8f&e-^V6ܙ7+(y>:y櫚#1W`Z?$]Eoߡ{m%\'ΫRe͟e6j~~WE9wZ{Xm_ Atl;{|\s!+΋{:={zGJv|Js=SAѮKW}e$k~eJR=vń9\~s6 -ky%Mۜ~K@[3l-wdrCbE'Vϔtqdw=[y_ ׶>xeG7̹^}{-t%ţ5W**}zr%DtꥊV+o螮l>OjxFoWҦʷiO½6qF_"z?ҷfX6^wq#aھ?nG{>>*Qi^ꞤMmQZ<<~n=>{< yԫܧ|v8ҽL5rθ}ཱུ#+f?~1{g:΢b;G X>^zt xg4ϯ2}@EeuCNR߈z\kX/Tkt?a~)N*⛫\#=+]Gy2y:Cn1փ }.I(Wvy\>֚}[~^>uv=zͳ.s>[<{{/5^㼥h'4&^ĺwuґdbܧ粣~#yWeG(ǰ7K|#;-x^s&pƍG={#γ+GCYkN3W.hDʣIƕ~@g[y@U.^|ֳ~o2ݵ?I7הo=7t'\Gvpkf4ކK8kΪ^\'Ӿh̩e^4G8:e񥿇ξ/r ط'xM}C${O{Iw[>+|6d Zo^T{}S"G.K/$Ϡ15ʖʿml]יƒ;iN-ls?@1 >._E_+ߚ7t.9\/Y~ K5raL,/YX} Q?M|~?Y|9W߼qGxw>R~?ISؗb~wF;[~s<.4W8)ōֳ㷂|.Kn6{Wa~N9*;{:=}˸1;9.,>{BEz dr7Jw2k8rWO |9lU:Wѩm̦s2xAxds^]W @9sjL-(-|BwB-µ󙔟O.u.C7.qw}si7/<{Jrˋi@~iM?sy0e O'4QZ?'=_? N=ؓ'_6לH~}iEWp>!|䳶7ۙMڡyF{ZIS+g~7 |Q!H#'W+չHg/^B"oP8 ~ĥ7sﭗ+cu8o%s%Oo"Zsu={۲|}!O~h:7k՟s~n%$:`?'s`u 2ށ:7>ސ;gAZ_̤|+bEjq$;G9?a^ ;ErKx΋MC?c0L1y+w?rt~y~d빟]>֭w'WTkڏt.):z sżL& L-*yIAqmOKovjgq{XG⧞ {2yG'u\G=ΏN\1w >8ÄgHt&W=H8^Ov1uo{?<68cw+%#{P6SG|Ot}o:w`WJvվW|e~9kho+ ~rȫk?=[5D~zix[1yx_mA{=C;/HWsw^TViu:g9xp}gS%#RFyt5LRg=ިթto/@!M}k䙒[/uH/~>r|Gk~s%|~8W׫1t ~~0} |7= rx ]+fzt6O֗>y2[KVRi,/}S+'w/R>|䷣h|0jO5eڏo,1kͻx^R|Iz ~9xkr7p1zgo~H|/[Uug>z gI?uUcvr >Nzϩ]9b/3o>^s}O1ίQǭ5_]Z+}0Ӌ0^??tߵ)tか3LV{\k{#=G7S=}k٭-[\#7<'eاk ǻ8L8s9+DtS8핥os/|[zceOMYƐ ~~>bJ?f-{^ou̓YwN_qȌн=O%N< ;Wm碠W\$3z' Oz14Gkޠ{YosE?Y}'='Z9qkKŋo||WqNw~1[D.ɝ z\#O $zV|߳"z{LE4s_ǾD Wόc;hq%G^|ZkY鞨yCiY{+>R}C);?3R}oA+K%XxM~(t/"߳z;|$ndI(˞xjwl[kYN}l<9ʔN >q |3k$LL%E$zmt υ\bYK|v|Bb7H>25>0'ȟ}a{qk O$Nj <zzp#/z"{zx< 3'RJk)%5ש(_=r,`V7ٺU' Nҁf}c1=6C.S{'sˏXرw^,<7}:k'jk[.;kma?øG~]Ð/>ɼr7e^\'6rr/FNȰ|o@?$yB\rpڮkoE8?:rF^Q8mCWOt#ZS~ArѽW^=CūϡtF~4^V9PY~Db`>z?ֹ n?3<[E\NS' p_t^+u3wǁҫZ'=y^O~yl!yr*t->r*<ɦNOx^+ ^6ys2eqM-1O}y5|R+I4|FuN:|k16ٸ]á7s.~\egCLcG+6P-c>wMT<1ޢ\}H:A}m e7ېH'[>{GI@|>_3pvλl;Yc4O:@Os~J"Z_{S.y*lЇ'C+7g|I'zJkǵݷ8q^ܬ>t_S_y/+w-Yv:*|Yo|!"} |o/dy=g?' k'OX~#/`UOk}e'C/arL}'_y/4/k?|vAݲ r6MdȷWh=83휢{;i/o}#]@S:tcpGP^)sMys1~e}MQQ$}>>wz]|:8%ybʟGM ??C U<{ :Qr(ыkOGBtpOO./&;Gi.3~) \boG͵_DO^d!ߣCх[Z=D+r>[$G9zt΁3㣇ol>+Ac%}J\Ҙ/QL)}g\6W9uk_Mr=v>9ݽ I6.@w*xͨ>t#H5w;}?\ҧo/[:bf52#u>?ypjt23;կ^.cp>厓H9"үK9:׌9kT踽k! >Ӗz #p9 }ũe`xG$o ,ޜ,%xlnқEuc{У;OœNx|zެwc'Ut $qgFyA9G7n;R"ú]ڗ12j#/sL/c[ȗ,c?}SCe0s9O2:tm9;-x3_6ԞGN I}e_#?>e]l™ws/u<,)> =(3es?3o\ ra!Njt{%_L$.䨶L)?oKKH]Ȝ]_ݽR}1F;( ܞ<]r'kβ91o5z'\ֹ~_jW"_zkfJYS;{4KC!yTzNN8G~t23Yty/s/f+]ɺ|3儁n5JP-  z3@g{YFn~z^}K/a oSǽȟCJ{A7aj[Ŝ_< }vfT>8~|JJY_2~t+Ci.YSV_}V*r#Od1i+}}~ْ΀s?9X|PzX3O?1<ԟqkٯW?ͽk>9 ܭߐ~>) ƅ ~* 9w.a~[z/~[ud ozgȕH{~(ͫÓU#]sW\{]KO#Zj|;Ios_N [h/YNޫħɕ%W{l F9ܓ/`nA^=}sӉ+uo+ L>PH+M-)țEH x{]9V;묜e0pK,_Gud=[=%˧R=4½_#ź@z w}qI/AYkq%)6]L<}{ĿK,Tycl#P. s 'G+^zGy}oD_?tFß9AXȅaJoƐpIpZt&ݟ_|ұ˜ΣO%?~/尻g}}nmqчASkR+?"rFӪ"Y|/"g7 >?},=sOݫ̯:+7ޯS+6^,ʻ}+|W_&, }Lp~U_aIW>gXKgcwIcgO[{x{_ďDQ}eBݗDߕGș@8Z훕Gw |kxGvs\z +N ~ȩLzypzWKWVO8tܒؓwk}'8st\0O N/("Yra}8;]{6s'zx҇sJ@*_Ի]y4-\^]z1s[=";x"Iq '*[yYH:ɷ ܗ2 o?r>ސMI)I3OgU znG=s9->pbe=I]f\{F5(GZ_|R19w3RыX 2SC3fen' gpމX_A%~O~xwf(#Fw߉~^>}#+bce_Zky(eN~mٷS'rԋd҅_24]Jx Kվ#}:OK>J7:N;/Bz|߽ms~:K>5~=K:~FLJί2ϑiOn!\`Q͟+"gxgϐKC={\w彛z8*7j/ 1r.1yk-|븒ZF>Op,p[GU;h.Ϡ/Н)ZYM=]s4PWJ\ ޗ7| ޢ"CEp.Ur479z`4f1s֟ҟϹS/m+7d{N$}ݹ<ܥ{9|)܅|r 9"/۟-ueA=lm|I+?Sq?͎y \Wͪy]T[-=V~J˗~x/fP~Dqx @ ֖n NaSG|L=5oG}zARyO$w<_e͞W>c>1{:_=Ƀ4/,$=U:zMԾ^,|\W`hNo7=r箋s4rtC[ _J24Fr*3{Q&/<x|i-s8u{T_X_~_߯qo"|8Fܙ_;@=Ϡ>!eEȗOt7%r7>0lGY+/~#]?<%r/(~DӏN _q}1ȿDN1#g+[~|:GVw^G!}!kۿF~b>-^z~iuO.dU}nC97 okpx &~Щ-׆?D'螮d(veN z5oNyI7;_vz~*soi1ΨlQ*J>X<ј7W72&}g^}g9JoF=9X}^''bvWi x~|ywc&?x66c^".\x]YA^zQȼQ{~@c#pp5gѣF~3k. Cg2e/=ɟOι:_9=7*3:$9P;)s{@Ky[9#y ~ Qyh }i7'ʞ.ptc#!8uzrp{Kk=. g.֛4__ݼunJ_7:-p0n58g#?6n75@ټ=N7zǗgޅ_GJn#O"pZz_uB_ ivFʆז8z2͵=R|A4h{[9 wp䬹gq^G7tC-XO='#>M=X~t՟4?t^^>yYGE\ +9̹;!7Y{~ /S̤sH{σsxxzlˇoq NxY|Ag\́??2O~-9Ž9o?Κ?j =âzrVQ m{7YX1s;ɋ'?F_һCs`G)2^  6%KN=z@xbKDs /2zo{^:fw8Us3?{.fC cgo&x٬#uĕP(~nW/>zoU9} .E9_Wb|} w_8gx#z|#'s9 >5rM$8d1mzX7fzџޣzߑUN^;yͿ8z|~_s`b箑I>iXwO=5NodB [/Ks*1{JI䍘o`(!oANg_*NX{'17Xz/nΧ G< |!9OƋKZ U>zF Ύ~xW{ ׀4%sS8go`'K9|KA޶:ȷ)vNy!>?17EG OY<X/>RNT9AylBGM|/΂'Ejr?ֽ-<ܳ.[Syw>wOGN :3}9/$W ]4PW{KB*葛]g=sgO>]?3}J>~g|^/~^ӜCq6 |+:ε>? s_~#XZI->7=OXe(?ތ%xdf>_ rȇ |g6=.Pd%vW>~s ʽPC`_ ]NBGћ߼/zC fj=ۄCDy 5OW{]|O':y1qΫD^s]_ǗAg^"5yR/ClkNE_S o#zх{O{Js+cJ{!e|A }t>M{Gl!(7/J,x+ſ0c]&s|t+:K/''eA\E S 5퇏y\0&ܣS¶: 3ɂ'\N1ψ3D{J;L1~xho)<=<)=s (|sSK=W+ަW<߄pώl)]̄<ΐ.؏6TSHU?ß۾[{yŋT7zĖ ۲בZc| <|rl)<Ѻ]zQn'xjIn^|B7LtwE^'}y/?_|Ϗ.|߯7K^}G53#gͺй2{q|> 9χVQyTg^ۼ(:Ǖ _4.r+礏{LaQ@8wv.DJDr#}Cn5xkq^Jwq>b@yvidfA0aqO>Ii'̇5O=k̪}lg~?y'ѥp^,{ 9g>lr9;`y ?&٥!7,Y /~~U $_ +lYJdp{B9 CCJ|Nn,!ϕ_UGZ}[N6{]i-~|9vapZt/RГMVt_#GyHq[kv~<W'>F{N_D A)}+ɏ#u6A}]Xq?%sypoSo V׾ Zewl3pc{:{k.hE[7nD9K-̹+aɅ? ~ֿ=Cv/3ߛKrxz + xa7g>r/}{ȹAC($%}. ϱ}4 _{5kv#g짼ŚY>*zԳ^\)G>J'o{awז}}}UNeiN=,q_==:y3גVc{U%}ԝrЋ&ʟׄKyy}ROx|~t*b~I.|U@Ջ XTnNO1sxcsObp CG0CCI[< }E+<Ϟ^_679%,$cZ#ި)o&OqϭH^q|95.wo{i(]4wǖ:OQ@wA<óGy7<4.#c\U,&>`@K+v1!ynm ܡӹzWUď\g D>GO1!^oGg{+q֞:g9ȑH6[H֠{^"=y,RJ3֖oXB8V87^Xdε_4OC>?6:/(G*di?o3 :/u}w{`e@o-u1߁F~~ ѩ7S#қeSHg>Lzo}ckHy;t|臫/H79RFF|lQ9ԞI::h>& ~[f.uFޮ}1Qx'ir򧄓#OQR'.1o;G=͏; ~>|`tM+WQ{mf87/;xO5Oy%N>q_*dDΥxB|bqlIʭܤs`)f\.>ԋc vV>A ֠%%]w^J*g<B>r?1,q?^X}G~)k5o^]G?Sz|)讃τ{s[7qπD>+iq ‹ԯV :"X߇}W.^N ~~X<~t֯XEC[z){2O-]dkk?KzHr|͡C_Xq)HoMGύUk@_yUO1R#>>-ȫC'0/1cO4'rN,sWdUw]uqhp'~8r tR2d3+w Mɼo ~ޮg.:"FşsFY:? |#9Ҽ81(zCr?( ]=OMx'%<>?t=~w?:؊o54ް^sB:緵{]UNenDF*5C~rȍoJstN_TJ\/@} >Wѥc1]sx0yR[鼨mwO4r+η~HgyA*|)I{ݻf] x,P8|h) ieeOžjc/M[O^?Lf%7鍫/>]7{"z͵yC>z|G*J?!F ?辧:t>zI{ʩ|?~6?"~ murW͏ob'=<'JB?8R{T^)!}IrУ}o9|c⼕%{n~oxǫ?6?_sݛ`~rz GŸpv?# qQ֯;?/$ g/|n^ )CZo}@,~E>sE賬} .k\{174p>~{fbͿ_BN oPN?tNjK6^G|c>O;q.!byros=i'8TpcJVwl K}k{|i2IN_|$[j$N]"/6ݫ+*G =foύBŗN[WAd|'/%R[4 -'0/o3duXV^r4WuO|7~ w$P+Oaїiu|ons/ 5#ϖ?}{?psbﰏ;oA>sr>ڣ n^@7a5TG:C`eπO$y71GX7^/s ?]ykm--~⡹&.[.Mt֌?C?(%HiJ{>}&= <5G?KwB8&[𹜷qҭ6MTw]V=t6?}2zD _P#8~ơ;"T}'Z:UH'o>vzJ% <6n^2:ng0gHDžDwG;}Vq?{O˝fzO +s'MAtOoGt&w&p/{e}}}765k'Gj~UG:8yi:}_fNܽ6=Z1*yX{qOKk윩s=&";tnyQf]ġ+N1}D59:(.^ʡGsٗ0apu|y5wq {4=/A O̼>rȧ!=rUӾcK'{y,wN=>1l ?v9GJG^~c+G_9AWpqp|NQ߂/&>ߍ ?)v}~Ǭ\jzoм5Gx=-kS?DoԿ\䃏._g~!9Jsk? \=N VyiEȿZ{`N5mCy.sB$ \rSgmơ-gWʟL VeS&>_[sH_1⹰VS s5Ep?i/+ߩsv=GvLR?gۇ,|UTݦ\yDbXo܌b?  ]SKہck+r/DMCw!c#_ \%NvOzr]7?\INf\~8QieGџsr&(v^yJ?ȥOE>`hn=hpz{t/s xd3>oLmw۝IGA>"}u~Ч>^wAS|Rۊ3tP;]|{FVkťO ).^ 8FֹAO:prGVuGq76_==&g=%Yzĕ'/~4z/'tVs.qKrS"cӸ};#<Wz8ߋwIOn,w#CG9>=>9`oi<^r.iWezK_d0/'/{B5w.^};V[$|jQ꿤3υ~jrUChOUt>ug??9.oy'5:{tctxu儏{vϙd~L)SWψ%pk_odpi^f+=FQ~|oJ<_`6m*~%7ҙC>s$ϊ?{w܍˒Sso{c6~lxǷ\UIO8e#>gFz.oyc?^k6 >}c3WzQzon28ߣW:' :Brc{Xedh?%o$_7hݫƼX7tVۇ ?{ޏ ?1XBdr^O>p{\\!_q J>6Ogw|%^0z'gLawM[EN=:o9U'sN_ӿN{Ҙd[a.N?_ӸZx9xK%o܃=UяC?c+ã\,_ `cCW<Źy7ǿG~ }X-Jܧ_xz' 'g2F~tj";9)~s !J|膊k4'tn#[Enu?=k9跳:/W>S>k]sMr; =`kuu<:7zwx<x M)>H<]'򞥳ȿBZ䅢^"s97Qs8="uwъ-}Ufaaܨ.g转 ^Q]dϷP9&oZq |~㫠-4z*&>Ωjnfo:X>y@sUC#W19ne7o|r9f~mKS?}e}V'Jy_:Af_: .??(=;Zj6]A9 6RWOHw8AB݉:Kʇ0|k$G|!r?L'igCzk鄓-+k]cee2> +n^z=sC{7}Oo}<9^87{zW.BF|{)&KO>FFCDk&o3*BK3WoM/jM2@ PG8c|Oʖu>=mL1Gg=O(>xܒkMes\ЯD44C91CFyOȷw}\|o7 _yaQ,^{z5Wir5ɡ\x<8TMͥ3G^OtY> S?C#g꾇9ѸRxpW:=v$#0\P8 *:7zG\!g& FgIpPc<ޒ9{ww|c`?B@W'}aB{Ue$e~_mg%kRs=)?PK顓˗9sBLU_^\Wm>>aG^ z;7ޟ~OSY,\Oˠg\\'Wަԓ4܃y|Dݓݛs3tǴ eq\PC9&W}fuI''΃ήOك)SYH:[trћJۺOǓ@NrmogﰯK_c?l+i_C8pM%_>y[I"Y&/r-'y<mQ7W^u7z__1||kpGG_SBFo2YSzΙuJ^yȗW//}m5qn΃³fjO!=vUσ|jxI9ΧJsT>XCvwg79rpON^ɹ_}E%K}exKɒņJW|"?og.v+[G}O02$S/=cl>+p͵[S}s'GO/7)* ބS}#ފ._|tKk7)xO1>or؋%u>id])|pɾzۗ)ǂgtG~H}9vȗ\{5 e/zT˄+n9:gR@T!%Wq|:# Vsndt? ];O%],,o~py0/|k9Ɨb_yo_9J]p/t:^^}}eGD9F 9R ;}2.k]ӥxs'&r}F|VwǼ-{:ۊ!~nG.3}ij=ΒUΧ_s~DW}~ge`wD9-3J{ӑJy=eru_WYh~t[ Sx}{k*gvK'sGƾ#> }$:E,x[tDŽMg_{1~;SzCZzi[O/y,pft^۹E٧^_Sd Ϫ|p'xz]d@rȣh= p&ϵvйǹD">}Y!?!<5s+>CW㿗\t}(}`[Gi>%.? ųN>0sqqU缱tݑK{\_~=yl+8 }q=9TJ7}ӋrӉ\4Ř ^`_@9w_%s}.']O9֭ jÓ_N41OXQ{b#ԗ.ސ쉟ʟXU̯`05'Lf$ktk>_k\tѿ~0j~c0&y䣱_F_uߕ?ɟT}ꯦ\֟牿,y^zhCV>W=чÞ-R?(3aNGKM3v^qu# e"R] ;ѣ z+W>C޺|t6}5О~p@B\XAy(_ۧb/1cGOş˧SNDc{wst:+ =Ok*/O=_e/y{7_ rlLNQ哐 %t<ɼ|*:bz^wG!{UxG5Co19y Vm ʗCwL+?t_/o󝬡\P{_Gr> G|0P̍EEc䪻7;tvaSMEs'w7VQ>u7S*K]|IA C":y~)W>_DGGA'/G1sot/w"IsϠ8JEӧsU䂴n gOytD)<8$c[_:|S[:'G^#w |7Ì0%pt?kkH_ԥ^"N{+OTU7n?>f| ϋo3{@s ?}J=Wu Oczdo Z_F5)?߻T*ttN9:_ހ}4Sf9i ;sweg[ˇi_Tfy0r|Q_sgnݤWy#!6Q !ϝ*K]P9ukk.>| 9Rޏbڧ;֧5 .c=nlyp|XA-}"d酆]~)i.<ff 'SK(}Z9ΖޛxIrd p/_mon_@z ֭޹dW(?9BW[W{?U0ѷ2s_}ss_ranjBݛ0*9`#$뽪9Lt?zƈ~_p:j_džy};Y^,J(:{bڥKp>h߆I@z4y| #z]r_-~a|IFX:D2zX$7ɯ^9Op?WFK;o͇^x<|G>ʷ] l?E/z=ꇅ}_̥g|qiC~\:gt^BNKg[d-]C[9g|j׭p&s'$W\IWU@W'AeW;_:OsrM V9G8}|XCO N<'˫XE k\^~oocјt=y75&g1}蟡9,]Qp_}E2џ5 ]҇uY{Nxrˬ=ȱK."t޻ ͦ,VGUpbSbG9=1ՄE%'oK񇞇=o=؛?q/>wr5r}Ga>…34d ݯ33g"3${.|W딼= 8\9Ǒ<|^0y0gRS_ㄓFNM?1wnQgIcΡn_{{k+'G<>sͻFd7zmeg_wUws'9שd9wCmsVg1:W/AW3akrp۞$]]SW02`d')Oc H_ԯD 9[{=7}!|yvt\B/]gKͽɍd&ҹ__P{oK}1sE1}?՟bo&CA'o1=Q7򋓝=?dinHn3|7J 9TtS.^ʩ=Royw_ 7AMr?+o^9?Q ;Ar_9ِ*[>=-[Ya^m{fZ'sEݘCx2o/&/_ib Ї^G9[ig4EQ[z"RHpJ<;=yͤo)V8r5&,7<d Kg/rЍ7^N:J|W[d?z3gSZ:i ^Nvs̹Ԙ5y+,K_L: .J׹>_=[BsIt~>vέv_<|GϷsWWCLwy?/3\➾:7~1G*}?k-[wK&rρtuvѩ^R+ p9)7?IyהcfB@x}| }}#}^ponn܆<{D ?,-Bг_a)䝐cL brh+ͶEWS@gCڹ?=F>&4\kgtN߇}fz_g! rJ{Ϯ!es{ ȋpV?/_9Cs~<$WΪs/ =5ӻyڞÚCOXs*,^S͡!_3͛/_#&9/}( yN"O-Nx>y 1G3;ç~݅8:}89knX."՛lZ[o9sx_zs)x<='׋&ܲ$pnͯGKk~S<' ƣ-9jBC·f(LυN~Ϳoˎ"dz~BGl?CS0|o/𹫖:_9\8{OO-/osA=9cCXᛏnܮ= |2{mMN5N\l)"~dͳO 2N^Ob5?7YMt=y/aGl9"}6ycaKw<^gq]?oڧp>G]Wyg{IS}5sanܨ=kc\#^?26@d}†Ϥ4@'>/BxYxyrط'̨s{N&#f8/-V9o> Klxka\=٦K'~2r;^HKzSgϧ\+=#V~^_s9>wzy>9g?] 9'͋^>g/VBjP_UɺI&}A;qKa%?>)2_D )]rr>1׃k_N~ryVnpޟuDs黂GW~_427Q1vU?s/S}2$֞~8[%15^~}G룇 Y :G3|C$JqO\+ufy/ߑ[7~y=b_jk855jz[Nyxw_ ~J?bY[y!-;s>}  >wVLj_|&RFwq挑W¯_{Wяor#UO~N=/ɩ$y^s kO_|LQ{^n Qe+<}l78 >9uig70 9c+H5|s׻]9>9_&zW{f:/'5r~QoI?p:x&s{3E.CeJ&pZ; _Bcqn.dZu۟*=f64kwƹ:֡ҷ5y'0h>jqq/P1ZSȂER=Е Kޫr{Μㆿ#|?_h?>|~GgԸQ̣#ߖ#yqpsOUi.9kgA^;ԛF^req~݊|ǵ?fρ=Tc=r53{k} Ǿ79C:'O}|>|co}7$G=w EiO-rJyށ<%trǨ>A=[}FGH5:}$Q/K[~E7,~eIx_b<8~M"!xW2˩;~~9| W ߕu"'[̟>R,?<9?T`g[ye3(;J f<'{{t٧O׹lAņAN9:K~L^t$ԼE/='WAp}}ˋﮞ<G.] tWߏgQ^%GUn(o"}NxPrp 6ѫKu/}=kٽ8ϕ@']ԓ]}OBh]@׮_ W3nz`Z<~qsf/HWD/gMɻ')~tι5~sL} e!e1n=}͗Fub^]⇢ݹٵߑJg;jϢ9*uD k(9|]rn9{Vs|?̟o8NF~r m, '~n/_s3}|$_LCߤݕǜ>lϳxMCO8~ C/foG֒RI_qpnz_GΗWkmbƆ~AY9_8g ?uze@8`7BCt [)VY̋k)z>~N?pi{5GD8~7TǛ(0;O9ͱ?LuQd^P^^|҉(/)|;{;W{&}' L΍>>@H@q#ﯦxkr\GǔA\ɝIU~?|諚0ݗd8-y9yEX&K.+̧ޭȷ۹ON%:MZsyD:|_ɔ+[GѡSB?GvBzGi ϣoqٙ?c .D&?gq\5yk{wt/kD_}鯴Ws])itCJ7[Vz [O<%wܠK;6J~!q[X?ݭ){7/r.#kmC0~)ޝ{ 96-;ȱI?U |ovǜg>zh_G9om.^r|sDGKNq h}o.gZ?~Ov`t-9/ϫy^jdON_gg yEN rEoIx{:ʽ(W.0B8NAiCOqix<|j/N??лYcy_9RG8?]EqȌy};L0ƭ9u~KW/ [9{J'7{ghLJLNK˽bQTxdO!*RmQ[\X|Er;;o\S|/a4{>o+?:;{X_;𻜧ꋗ;e#"wfoRxN[ sy+ /`䞢/7rkGoL~uD]})r':]w?}(Z'|`Yt7voWH=>ixhrwIM?Gcו9A,-ߪ?;x <<{e72?}fÍī0ϣ鬚gmdz{1kQKi}_&jIֵ94%{ty{^|"M zuZyx+>LjrO|Jϭs'VNE.zu RQ. :΋xNF1x䍃w_ Gi~%}̛.Nac>'!é> g%~jOl~)|7Ż3GoC&{f7 -G8y W^wgE y՝})IsQi"It{:Zp3hjo|?z?FނG֜'S˟{W Q9CrG?C^f[H޼ėkZ_I7d|Ktzτk@ωmh>k}<-<ڐ缾NGɼ>=p$Iw"1FiN~UWg_ /ҍp7y hoϭ7[\C/4j GBr(=ZK߉~!wK9J4'-f*[?*56-s Bd)~_o»ƾCBwN=ٕ>/9~+مg2?Fw(z9x_ϵ Vu 7<$eY>=$8QpSr:P8|js6{ɺ镯~?}X}qͽOY$3ỉ1^؇ЕoX\Pw~ϟ^(/wpac}=q6.Dap tϻsMiv_Bi6?KO5Ϻ"7Q>zmҧ~;(]93%~C{tIK~ϵX۸QI5?{&t@LSIptUS ߎy>P;9(KGMN0NGd @G=k'WXcܧ}>U}ۈa}nc鷗>=<nș & F8W=2ee-ucK =cȪI7 %W?9䧇=t|ŦqNw.ly7gj__O$I=Z%35RFDZuB:|<;m;o+§|BsyqT|x]b7腻k=ޗ/lkrCF D< zғO<9kW{eFs% JL*7L^"b9箘M?wkWδGT%\z͸i5߇nX_gKr`rr. }wr4!$?}5Ѵ)psD[;P`a?{p%#}c$W\򆪋 k._O{7=8gx/xCoF99<ɲgxomK|Us\ |ɕ|3t8OO')r9o@G'fcܦ{Tr ߯,O<?'ȃ\] 7x=I1N53啢 KItֱOR~.yQ,nŹY ;S\{sɓ }q9[ŽwB]g<)6b~I)#tr^8ypd[y@zs7 ސ~ȉ\sxTmy+cc@w}UzjrȑFqxrrS546m<1-%G;ZXs_efw//.bzt;Z7uά9i*sU\_+w)% ^Csuqtue+4!4| Ɲ'ӽDJטjk;׹ete'{=MǯxYݛJsk'5z)y~<]N}䝓k}[PS|:w9Pd‡L.%~\[Xrɽ8tG7y7pηڿCpsٿ;{+wypr&d[蕘K^ɣ,n,=Oh/Ǥ}7;?q~LA?˿ӾREq#Z+2*:m;ӷD_aWv>v^׷~xAD >npKzmx?&;t8~u7beѻWܠ^08Ss]yd]$ywHh$:|5,cS9O<\b'>WBZ/e勵q֡eo"'ۜF:[r:@#?⃍xc#,QN|kɿ/<9sĩ?S|fȃ<".m5ulM~g "_OzT-]zosQ]`1JO۾UWi=;%oo>:zbݟt+Ctْ|^վN~9ug{_Bg㟻Y',gKjbcV?\=krޏ_Z>Ip멍J_6[_2CrVNpyS=Bdĭijvbto}j-\pzA=ӎl`kʏIHr-(a{ޒbFW)ki<|Ta[=;_ݞNJk^+W}uCwQO=*19 NJ8OjC<(}p{E.R~+1~; OY7䬹=Qg^/ dgrzn9E?e B|jZO;Zq7)=ttBJs}_fg~__,{:zo#2g-h< \=ߗ?'SCzm[oc&G\A:iG|=[k)|lf8УKHHgC˄[Ϋ^T* op?97>y&{ }E=@o?,ͦ9ݞSkfNh\#:].z'ɭ5w="=#a5_RVx=R%KnuwY#Ӈ\P>g_ ==+{ ?ƒЧ>鼯ʛȹoŤH6~yW>]as>syKw9}X#ϼ'o3KNǖws <~ͼi_/$>־;8kEOƹU}M>q䬧rNF*9Y{O{^~Ʃt_N{K]~ 񪗊EQ;~srmew>kϐ4G|}ʓGJoF}GW'QsI:z/<`o{Ĺwg& s+򾓟w] 5go } IǾMrpc\OsN;'F^]K i?k>V~_EmNO%:|VpIx{5? 7?%:k ׍_cyNOQ̥υ~|eu Зm{fuߕ}} uƏs;뜣;?6^R89gDOx J ׭8Ko.^># 3s.|m{ObW%akҝugs>r*5Ƀ¡+O:?/h9oILǹZ|8zTrMCʿG#wxbLQ{ +J }s}ͮg#wG<}IƺW[ n9efb?J^ܟ#9{/t"\<7fЫ>MZfsDMCe=#su,_Xut_ V;(|y+<_=rzc%grDO)=M`Z_Gzѳů_, z;臷O A;7+un4Wޗ|?O6>~?VEZSSzO:kg)VR~#}[k->?اMoi^y(ս5W3"Wst{^[/u[?Ʌ}l;|Z͜ޢ #yns<zt~Cx]"G$<xHr~t;=!< r+9:3t b?ҫVR7ӧ@UӖO<34tyɺ{|ȝS6s[ՅcK8Ig~[E)r@>Tz{~%GAGH >7Y{$g, =oBrC*~{z=f[,Pٵ#S󽏎# K5lqQo@o7~kKh|Zg,X?p0=&z!h?-1|-ok*^~]ԓ/7'Ή}'2ʋ#=d;O#}4.#/Kb:N{'# p}êҥ<$w>Q+}c~p#p oiTr}cN%\|t>>FtҖϰs N/~+w{Fovr/VVaKaSҏ]\c7FJ:KlecG-@z~?\|u_1#(p}ty `xw>|pv95nRCtJ};sK}z0岷C m>wB4y/9e偣7ħL,!K{,ncM݊[O/@c{N~A%1.x) k ߳Suew:g[RrY_-Gr"w<͵Y?OߜZ}YG_W^GK޿+gByC}Sr5Z}$"Ftq _B-E9-׋3JoXRGΣ_UksGdKWdfrNs|R[g,tOj;Zk?;?tt޳ π>Z[z9(f?\l"}g5݌3+x|b_/OzA+WS\>`{_\A -[$>rYO?3"/0`2utؚO9aO^XD 's<̙س+w𜃧öo~J:cy%tٞ˗zSz+@Z6rɓ9tU×\:$$^R \ʷE]~nz<'K~G~鵗?Ϯx$N\{k9=13'~0?{j/-/oğ_~E471oғ^G4ϫDJЏ]:E9E ?ܡy/eןVc5;oZ={AŇ/sYue7[6 ;{~gm0KnA訜KMP̵Oټt?D#ޫ|=A7bzsmClflEiP~̝3R~Jt18B9+ҽ\G궞CUyk~9Z? I{̡=1 ˡ_7�W?IK{Nό_W:usM>ե~[)@q#tΕy}|Gm*u1{Og4߃ Co 7CiSe.~~vp{H<1>Ci0'c3'Na~9:3k/?rVN|k}%/;?O}Qya)Y3+.9boz"yd|a$g)?HXW.q>1Յj?)NYW{M6tE䯶_s?qh_^qQJ#0ϵ}"tڟY>K~3zA~/=<ztt ᄃ9 Ecc'NuFۢ^xmzBo\۽-CqNs\WO6_Y23{=Tzjcxzʙar%vD.?Vgt??K}t9},W~EH.OVr'GyFG=;kgԹ8/y&9/DXP‘ׯtu^GV}0i\hLw\=1.C/F}ɑQxb==_:9!F{IONvzY en9 \^vz:G}KZMw=~,e#7{eK|GoϏ]-y1וWnaDwfͳKߟ;{wʼ=yȜ۟RAWsߐo pK+Ʒ_-Қп1/ Gost僟әWov_S>ɥҍAO[|+y_G/׿["y+A =>=+/1?[v-=}-E6"Y@~%0fxbO?$~ pTO>^r/}O:7|h8ҿ9eV۩=CpCtΓJ\{pI[e=GByɥK"g kJ~?MV"_>}r4=K{4DNC J1=L%}Ū:gʗl8޾7;Cq|F$">DG靉o;O=C藴:rܷ5v/;[ }|LM{\uQh =v8 y-MVOlg1GܣDWY^%|bއb$G=ty7J9y䂓M{ZKJ?9K s'~֢#o _O)ޭʅ߁^#=:r#5 }Ñ>߼yd]=i1 _Id=ƺ5|஭u eϫ/H}|So|RZBuNjyI AAs ]}*ro_\ Z!?e <][ co5''(~=x}r5G{uG4jr|~k_9DyJy,&jV0T8}g!{KK/9[1~\/(>|[|%ƹ%֡\o^$9%2O@0~$=!e~'t99sF{%}\g*~xOȯGgoa>xt՞lS *[ioh'|L|1%>LxtXC4r~xJsn1W˳0w֏NRG᳣[+\F_g|uZI]7xv+W([fd1EKxI:<&􆭃s} ;rݍ#F{g uOÏ?0FwLO̸<|r / MнK}kw{/Se-Ul~>r/",۾෥ x,??4lt9=%9@N?={y??>T!Ic*/i7g3/&w')O7 x2'Tg螝QtrHoה'}8_|~47k]%̽MHy8+7^4qɻo'{w=kH]\&oesaڿ:ݓCsSj!HڣJZ:7 =^~-z9G_6M3o=|tpU>kʿA?iXfwW>_gDωTS%'|oF|z5ݯi}sq&(]=P/@O>yn $_/ћ\C_X|% JUs2`xzcjɗ6PoIXYg9J _ߣ+z=.wcSe[Xmƃgt.=OioE!mnW?y|}/Sre':3|O泘w^X2FNN6fސݭses[B7}lUEqg~8aX?q_>![xsl/V1|~fj!|I -ޖ_ Jz;o|ȏ\>,_cIszM7;t>9?&{r/gt1?|(LXρ>L#g>%? k^F޾N>wF8ib/{X6y>Ʉ~}?C7i**tӝSX'6{s2 Z_>,Q~(s+Mcs9.)7ON/y }7If+æd}sfKg0y@8v1xjKgޫ*ΓWaC-/܁"rzKcC/}8<^]xfI?|B^3l_w)'oρ@߇=@9is5q$_^'wOsttWsύzg ?Ym8G q9׳w"W¾yAЇ6v |]5{4W=J>S?9*ry6ڸ$'?_;1"7{ҧP=>G!s;,szKu$d^fn#׊jp|g7gr ?k_ym}KJO1sK}7|{ _߼L/5I-3oo@sEu.F8$7! utv~^߲_\r/*{YɽGGT"]}8{Ph~Wc^'ǥ<}|:)=}ov@A\#A.6$\osW̷|G͇P|: /ْM}=ʏQ߳۸UsݳKl}|)t6$}v􏑧|hKhQ3~>c-JeirV/v\ZGs9U]7窇}1 E߁NJ%N~>ᇜ_ċDo/=GOypw:jޓ~5GoAW>En:/}*z}V1>pl:w:XUzFA'&zLpZ|3ܳ䙎l,|mtwۓQrRǟK&M>+u_ :=#s['Ŝ9OMi_!lj>\rۀ[q[}>R\'_jޚp~ x[>/MȆnoh=.k79 #Oo|;w5>|LR~K^>3 _W5{w p,"׃53ݧͧEB<>JG _Y?9z&<\'{__]˽۝Ysu*S MąK?B^ g+h:z87{ koRj3C,^^_iH7|K[Mp3L/Sc2|<TxRtf_\eή\[9hQ%3lyO;]8Xwsd+INQѾ3s͗:YK_n}g/_n-(>a~S2gs{Yy riݰ7Tד]5$qj3xϵxOw~yp!CO~vtKC=1y|/\9gZ<=ΑY+K$+x=n\.X:4p!zٻjOossH]0y2p_m&9^c7_\%@}L :N.?)Boa*7;! K yGɻ> itO?r=%潫/yt Ob$\1zkǙ58zyD+^l|=_+g|1K;5Wor 3K;]`{ޙ-t =s#<{=Y>}?a:AT^uʾm}OJ_7_NnWV]M},Ay1< xc =T?y>po9=l%~y~__WlBNS:NCϝ|th {d诜{@yퟟRs?"'|]>Ź߯lsHLq9|qK$'%|>d7"Lr׾p?W7|O?h9 S|PշT\5G9}J4sFVtcW4/W^}.b󎍣y{&;F=sҒ?A7?HM> m\3$g7NxY>@O~g քIGzn{y`ń{:4{\{Y{,襶?|{?}J@R@MN$c2|s7CN)=5T]}bןOkO‡JgԹ_8qgv)b;su`^jazxڕ18zOG#]s^J9?y=Ta O|b^57t%ܫ쟑{t?:'xBU֟._:wү9l]bu5+r_'yr)9ny>ʚƓK _~q?t]Pf7HE$>3Lj*x=X& koI1k=:la=.2zCk=5:2ǹy^0i^`>>C}q2`rK[h5j#w$6ѥykȕg~| \q;GKO^~,/FD~GKl>Wr"O>=| j>%s_s{J譽gT X}Q:rYCl?n+o|'/e'=)Ξ<7sB݇Չ%?J/S,lϣڼ%ګ+[{O6޹o\탯ޅȋ 8}{.6z!VFzaxt3G>1mMΛ\ SusqnqzR!_1ҳs@ʼ+>yzyEgD_@9oЭo#?81FT=v%þO'@ofDv|2oo=zC7r 'w\7?|qOsŸ99-տsvi!1m: 8Y4Ϡo䩙u tt!ɎI.>y^}U"G{;P}Gf>3remMi?/yemr0wY~w{ڟJA^}2m[\Oy٣o5/z/%k9qrK4u:|nt΋S =Gikߣyrcɽ%Ϸrv+1 ջ;\%bX^|.}'͗;p՜k586{)uK'&u^?kj?oس7Z\}\t{b>.aU ̉䐴~y#Wx_ӟ?hRGAO6=]xj|jѯ^]rΕ\=Lrr`<׭5{@2ߥm&/9t|7ē$'R1ӽ~\@7:·[9}d^7gRO9;oy <=Ůړ9{S! }rm'뒍_չOg }_/YLsYo̳K\7Sڂ= {lt1m Ut2n)EAML~^t=Z(н_B;yiwӾPH@g.v.me91iʕ q^{u/P~]#\`55nI=oWSS[ λ=-u"%]S:y{_/3rk"Gcxde%_zr@ zebzۯO"YB(gkGǔ|-x:I/9C/8a/巠*fiYj_l~p ;H>yqu.72 ??|1vp9GuKΉ?69WO~=Isbzoter<|٧蓤=Yv\/Qs ;O\?{rx/ <f߽ ~^W+rOquN9+r{\E8kLty\+{A%E+^#^Ÿ5,=xGȽs>|r~V#~e<bĽƝ <>-Ly>*R\w}w2WS}GZ}^o`rЍˠg;G,pҥ>#ֹU*—ҟZmOS~4&v=X+]!G|O{#=sӇnB;DH)Α[t%>YO93˷2><1\Ĺ46]#]P^yK`ǂw/j-}~"'{Kḯ;.4rHSEO2!]÷y}Cz:8Lo?AMg =ڍ)8O|E\Hy}r𾭞{Iܧ#3!هYL{2/x[{7=,?[s}Wn\v)0;s&{9O!Y#LxYx~W-Q$\ܐKn-}!ѩ4(|أ8/tгM}l)J\/~JWX{{g*>M| ё0qOC>,]\_I`"}ukQ8h.Sxb3cIOMӽ? z.fqN wO? 7 ;otC9*2WLƍ~n_37WuG~z-Kr?Y/Le|"9sr^s1@}?6Oqrljknzd.|>҃оs?$MqBf_s+zʏ֥9#[BO7ȭ-+4rCtp_%,;x&߬Wsc{@@wOf<8S{~!zݤ*n\ޟK=Dz6 Go@ЩWޡ&9Ese97zӜ_7rt/t{D.x» ܘ=6x=|~Bj<3pb,ݤ5|-|E|aX{|m+Ǒم %}܏뻟~DQ|[~qz꫗hnk#E'^~~-$\}9 G>4?N-Gy0t=sH־L?7:t*|IrV?0_>4P87O~ιҳ״tō%ܞyrOp.s?~%Ϩ{^Idj gX ҏB^øk<C/94M4γu/W7l쥹 <> i_Ym{G?9T0{A|os'yMG5D󏊫GQoο?@ F;y-'[.|@+\p~7-^[=Q=8r^*nO #uo+&? vߓ}.\~ʚFo7}Rп>.{C9y-!G5Tax-p^KOvomO?ztzᛛF^P/nkj7ɟ..ϥK^C4GUf{Q=HhħC:H|S+өNN|,<9uSW~l9~gz`_QFߔO{:}itsNDZgEOSj/ǂ:B㒿CN:57pN6z=ğv ]U |;y6՞ -+3K=D<^ɘ>,\=U2ϥt(t.7Ni?Fy(!&u:#/C'Ay%.>׽_k}7ܒRgo[OrRm4gvx1^ Pyӫ½D>n~㫿G#_^I7'I\*> O<$K䀡csߐsFGurN:C7φ9\*!=kk)1/' &=ٚsP:8Q5i`}z;}B}n+{M۳\?=ɷ^m7Sʹ8޺&Hw}8ܜ5,z3j  eϹ9^ ?W_p^' ? 峧_G Ҹ]"D^t{&t Oim/|/zGFoşN-?TΏ6/^_K>swS8|qq^ CgI.;il$3{^ σ~ޔ\itQUқTY]:%rzs:N;j|>wHÅ/zomr#ͪܳ؇}ogy~Vڇf| keH`rN&~ܵdk/|ƣ# Y~Kk1+딮#;|x{G9;쭩^z1~oϰqWԽ;:O+9@s>8>U>_Zk/'|Z' 6pNO<9\jJ{s6\ܹbE2.+8\8.|#GyD_{g"7#7yd{mqvSN8Ru)}oxjCڧ; w_L6|>凐kutw B%)3FK}0«W|^m< #U[̠+e 8}Ni׺c0tYvo3 ދ;ߒs|#\_a4UwG<;hl-|q94w ={8/{@q/1wYA=xި39 9&_pȕWkN0|ݡ}[#=YS{EMFg漵3YwM|y?,Xgg|P\'zRΫ5*Η>rpW5 nH6^]_|#7W@Nprz%'Ky>~]3w$7{L1/ޔrtQq(|pz7ג/CqkMouynsWKXW(^?5k6Uo r\YύKֹsOw9ù \vx˸%sp.ZS𣾧5`~{CϚ'@/O-sBXgU$t4D/˺o_yTpܭe?]. >|AgOoО_[YY}<{O)~s\ zE`cT_1pD.uܷ34{k_o_iOts/co'\ -Cg7VEzv:OG+s.>1#Ϳ.yi eϔ{(=JY'O:;rͶ>Grv ~u>{zkKP9SzV"OvO9"gWD觳 }v] =YT*{8%(_XW.\6}zrN<+C-=AKz<½t{J޿W|{s<ӟQ/}]ۛNϖ @>4e'Y4υ^9tvӞ1zunΛotk=#+-~ MVvt5srbOY zjQKUdz?]>=YxzIcC!zQ*'|s3HWl+csE y^yp߳б%pZ}ǜɣ46u_ Oݢע [麒E{V ?WGzX8WyOΖ{Ͻ1Ò9^!s_O^y8r;EޞsѽT_> O{;C=?|n<'#vtߗO?J^>4_PWa˘;ZWe{,>G籿T{KCU[\|H_In8F6?? <<`Gkө,oE~Kmr^>}xRZ.]L{G+fi.)}¥K'9\3]@8BƓۓs|h;>s(t< fN"Ѕ_u@W<'uSCYWʎO;oF?Q|SS\6pZ 9uΓaB~> #7Qy`-FvUW`Zg-msɯN=4=K{QgкI<'>E>#: {0f=%t>*~V|/=ᯢ:_5cw3[SWy[v>v9?V} J'>}̥bG N5tޯ֋1S#[ ϓ(!rnO~^P{E|T\]RZ=sbb{\x"w: D:YM)rom; QtGR\ϓ~Uq[G>זr_ݯїv|O ~}9l*6վr!Gųh^N:|N,[g  g=O#=S 7K3܀.,{K~[|6(Urx_G#8/~r:o'픋 nAևOxP5:|{K\o8k=?ƍ'f鿠w9"Y_,sxǵ#|4}3JK)977kO^C99rWؚ~ OFvҾFa>jsH=W/krֹ_#4_gqߑ7@0z1_IC:+Do9^Vݖ.6ޔ܊uғ5Zx䗑Rp~} #ޚFIao^O=8)|(C }/hnW^mޝQzxL]M !9*3}_KW^nmsΙS*")f' K=ND>M4o"B8ZENesx>| E"%===G*(Nyg3E/yC_E2@~>МC\ s~XȽ>%ZN{ޟh\"MG+O$C+S~u^z [XH[ hN Z&͋܂a^a/D?C\)/^.K ?%rnOwPzo !e f:غhn%yeSߝuYg }ڧɆJ7 = {HBwRZI?ZqE> {:p"})4ׂo!䪸/+&'Pz{kf߯4󡏁!8go/>>'!W{$:TΓ>solf48e焮ȃBS}U:5qOE9搿Jvϭ9=wߤ:0O}/nj.|اxywCEw I}E>!7y} GQ!53(ye֗tN}SW~tݯc>ߠs| < _Bq_z#gS~xMޝLn)8Oq_0o>#h"[Z|qW>W4\.YY;zspj.} 9 ~7o,.z^/EREsc*to6ZGEMvx=O&6Jdc{Ua=?b_yD䤣"w#z󣂯#?Lã47l~y^nr`B`28|+1[:b<6E0y7rς33EZ4u-s+r8G7?&>ykBg({M~=W !z+ZκwSş9AEߝ%},ez04_^'O~o,#4OU4'j9}kL?Ǣ]5>ɜIo}=|ps<yȦI?:rWN׷'OUg*J*x*UуFljMr5tZ[[f]<|U];yP{:hATnyl.7Bs!'%䁾\O(,Jso՞^4phVH>gu#$[ZZt|!oX/Yu~Xo{mo:T#ҕw}Aa/_=rmM|٢_əPZFS<=ߴ jo{gNx\q]q+?Ƿ'y^o׸xQ?-9?tu+{LC>pv1}zz>[~zs-(C^}>=zH=T'1,|Ir>S'Wsors>\V()j}}˯Ꙥ ޿[q;/dCǷ*yPzB{t!w:ަlIyd%|5o]ύAzyr{xW%?;Y‡xw~0xm<_g^Y$3mO1`| yg/'ߌmpK#4/T<%< t(< ~\Kz5#Zh~Aus7𞄎5ɉ8|aOz}ɿͼ͞(*=Tax7YLЛstKqEx}emGIot /|_:TrD;Cl}>F{s賣 .[3+^sm}.u2߆<^Jz,_)mRqop?LSYѹ?ԞQ{zN*e'X}ϧų8 >~@orrDŽ<HԷ6Er>0O59Q{E6ˣs3y/tl}1/uKOXXxPex+pM*tzUζȾzׇ)s%G"[ WOczeD{o6yo>ͽ+[aQ]<4\*~:zK,|}+Ҩ-?>' ~B6z-z^Ğd?dwQZ՘>ZG{1=KNc~{0x{=.܀y 49"v`.32G+ςp S|irWA\%s=,9}n<;XOh94!'>+CpXr8пUs@_1r]iݥWrC/{9Yw_ OKNW*;r[>sɛl|`tP8orQ9W#$_t=c ~7:7.΋s jkL2bЛ0WG{󓛛wçJ5>%ﭫ+/랑مKO'yu~h=c92+`>zXU9#Rs~J&_FsW9t3Q9MUS㎶">v_3/Zx|?ʥ%Fu!Yr5y)?#Q7hn :\-^h`?g#?s(Ia׽^ĥ#_[0_/jczK9Ol=tnNE/moo.q[Oמ1riIsa/d$\CzH s]U: KrNBޑ֕s 8[i|stأkb[W=sHǻa <]K{s ~>əuYS$4 {4_Z8){$|~ŸuoZU&bgc=*l}S |Z!VGxuɹw6} 7tsF#_sz;_>O4~sY_D=yk^4Cw|*2Nsc+OH/?EA.~^EHun'}=TT {[\7+O-t1OөC!ɓ +J'^<~Isifv91RY.޿lKBϼ{O𩅾$έyă2Ǣ.#tT~sQsssS:/j?>Sz[ukk/1rwJ~~8]M{[L_S-3¯½ori-=':k x0?+ \y7x t=Mi6 ^ @rL7MҞ \O P8okDg(.5l\l{ս&]# |2=Wt#m7 s!'xew?'u{D=3#~ar~ |C.<;P2xxۣ)ʫB?> ݩGtt1,>m\::ۅ4>Ys ykB;9I!`1ɛB֐<-4|Z 䃠BW$_K^=OgH,kc[x;/zC9W89z\y ~l|27b> EY<}aߦGx6:_+sC2Vc`ފ|?z7F $]=`hn\|9s=>_g%BMp&z1҇M+_a1n//A}f)n=Q{-t#T9I_!z$k'ȟQҾ'琠KWVf^7U }E}C_E? % =/ ~NOI7}V.~ʸ:-r3ku^|4Vz|ǍT}ma9DsL[\ͤ<֏AN|O`>rANw.CtG@ZՊpcYʞ}D4r 7ý`|x!GYσuRϛVOuPsWb}t{G } yμ] 8{bR=Ag4^G$?Gȳ;msWgv:gKxt ygsr]C(~ י+۱?+w޻wVָ}|Zz}{ ;hoy|zHɅ`?~s>Yd ~" Зd&xrD*Ǖ<|kkWb?M(|[p`,=wU;O>{o-\+;#_1+C_R=&==֣K9 &<"r^"_/?o7K@ 9S׽J>zp=џ bߌz^X>?+&ɓGߕ>?s+Boui͟ŧK>r r98 IQ{ =[ǜ/d,9ȏ!,M0p ЧT}%wxc8N>Z¹Ue y[0K?n)^y +<m5I6R=S#6'o!~ +"qhn;DteJD0M2{-HR ,ŃGfX{_h_f /jx9K)oO zt<'`<Í{k]$a6Qz_?u3)hnC>OWκAr2Y>OW^^V?\+AW^wJZ?G)8 jFZG 3 W܀ut7foG0~1!Hg~+{KT9ErNf^gQ4ܩ֔/?L~_jܜ/`Njz|tcHז y*K{n:7Ӊ_'RC߼{\s[_rC‚O~iM' <7R{o8K'ͺ[ʝqA/ 9W{&629?>3KDDұO3m]/;~7HzO΃n(pK<<8;Ek/9;)^9}ٻϚuC.wEYu1ӫxJGGa~%zy ׇo=?:V_swZSڗ}q{o{_QV|\k}(3B/I5s;xr4_,X-\*t#ho)]9t_~WI.Z|S[6W1zؐ'~w{۫cE >nr*OJ*F 3G2x8fB#|F枾%<<+Wâ+wߒEMUTK=]}He)'_oolOϪoBVd_u3Wv:HP?!.߮\t0mgLo~4~>GʫJ3mO#uZ)Q  ^ Y{|=<\N9M^L;l}r;?"w|JS{*Xql*=S?$cn(E~d =ПO^RK7;vMT[$'>Ec:|NKGWK&O5#>*K2λ.|0k-yd{'Ar[p-睡Dw=܅Y_{ݓc}1Sn񝵏3tDk!Y | /YG{ ?~ў]Ct? X?O eg M4p;v}c{\(|}C_+G9&t;{QѓXZ#ЇoCq_ 5@F;|T%}scja%Ms1O~R|CP_;'=n|,bQߢ='K]PA0N=#e//x}/o) ~tOZY[- ;I:܃:X 5|>37&OGGk\3Z+>c,#\qu J~+|0ė)W2_bO% 5#81d -ӷ1B,~pr^Oq4? 5='ZkϢ9˽l$荅U__(l#v8E!7}9\T%c(xu$o&9:o %_r{ѵn?9ΏZEOj#ms<6nnoNIT7s\쫇 Ppnk3螲ySOyl=88'̞ER -Ln]u}| QtߧԿ}{>^e|06`qc#qUz+ΓΝw{)~Aww-nD+W?^tͯy;ZAyGs$}{ d!ǎFnLck+9Ozt+`a)N0~5|zM-+KHWy&puT;`ph6| =;BG}䞓!|.iԾX>={%%a-! }g/錑|y/CO=\5Ȼln~{FE^9 >B^<{ͥ'}sCcfUn8O^)DUoY6mmgfA>xk͢y&7ݨo]gWxC{ya0K]zt7#YX܁>Kh?]NlUqwV^:^_E.ecS…Z/)xa]H U{C|BVuiOltj r{?U _pt1yΝ*='6nnf[Cg>$}[_9X=i >^U  h!?\ ёV?p~;}ց;4~::gw:5O_裞^~͸kc*n&=7y!uo@ ~tOùּRse>\pL&9!GRi%9PɡzHINwE 98*ݨ=Muo$?5\ yyaJ.[i38}!p'z:#禄^? ',;Uy5fgŹ>|7s_|,̥|4C3/̖PNzt?ʺ4?ﱂ@uF詢[.3zk$=FKb./+<:H8?.xp =d߈|7jrgB]W"^>YᏺgHy}%HjAIs@\G:"ɦz_;F,\xt߼OEwߒŽ B:y,͐3nΓƅij= ֟α4ߎN[{x>N.!Ah8{%^ }Rhitworn{˯c|_{9۟1LO ސ90g#=Hg ~M88 rmE?(2R8)>~J)oΣ2G<{|J߁7:ACὫ=z)]kdO8̵*x r)b³s_Jan}o|z?ay@U}G2Gt~٠'/~'yH䗅z=#;s0Iy#,~/흌X- 9|j[>EM>WlSxMM\rj]A÷9-WF'bniz{~_%\=>jk r]ss9/s{UDNe m>!.RBO{ ~P>k>+|4V^W"R{m~{\tL)^:cҞ^]W~drw-P~>B7r+ᾨ\-%c,t2ɵ|KXZx,sm*z[pンCwO{52rҷ-ͯ==RQ_7=WzӾ܋+=7? .='=gK'sma=OܒKBޜRzrg95s++G3齥^u{wN}R &]ۀogw+~roT{ý? >nXM%= {㓃g;mg:ϱمQeAN?;$Yߢ3߈;+vm0n}-,O53_^~WHJ6T {gL~ s's@45K%->4xack{OYy{J_ pg]X97>휒K[_Г32O4RAiy<"4xt=VQ7B}AW!yVVտO>z跉Zv-6.0ߵ;4ԜB=vɝ,)<1J߄ː^/[Bgs{݋:|!wtrU;B?ܓzJ#ux>_ =TopԱ:_%Wgs9}/;ݡ=!,jzOGK_<3wO8aqooB/I]z>w!w3Vcd*އyo2g᧩mM~г~xX 耪 :s9Z=t[C>OGTOio= ׁ_Nox]S?po7n\S:?Ы2wN+=/0Id\ݏ o:`2} ޗqpa E_I^2< e9UwM+*?uZGoWztBƿG}}e3n)zQ5x _}tMփ&zt!o-o|#sCvK|z0woɼވ!ZW2sҋ^~F_N:]uVzzcK 'CZGx@9sk/oSU~s-図wjΗNwk],̝ánL?t,NJ| rkؗ_5gfKOs}Klȯ}\TryV o{]'RzBz򓝇r|ǀYNI{JZDzvr9=xOF%gcε>rȅFFgjrDUX=ڛ™XaKhYR%)sJ+Z#yZπO =zwt.#E=j/7#@f:ri|!71{HvÄ4WL7'!oVz%v\|䝒#M;-Q]ឹ8ɉϿy?'O2@MUɁ?[_ߑZ\9ewg5PD3M7d\bW#TО4ƺ絯>;wrhgϘg|qt7mS }r% !w ?.:䁠!g& KI2z6΋_?}_ y .>/=gPthM;WA9Suy43(RcЏ,}1Yy|_=pj+x)x{?}I=!ԗsq{Lo~߼PXS'3{ ^<_kLWOD=PN_d T5eݕķ}:{]sq圫}<>7ppptS=Dx+|>nWɟCJE?/O(yܢz"⑯~rG}!V9#NͣgC-K\~zIl"ܲ +o9s܁\`H'Yζ0n(>G{L2\-/\ʜ ( yj3F z}c3'6K#-+rWц3'x5 b ==u HxE8[҇_[ooX+/@~\l={t~K ?@>"GWd:~OsҺBI=st[9/Z˩z~$i$_;>OHjCjGZ|r_f3E4^~Jny{_Rzm>r:G%](~r+).cfD.w.z}U^kW}}uWf3|x(tX~}@/I]J m ܘz-MPx*Cзórϑi6U>Lz9Ԝ) n/侀EH.}&xl1zC˜Ii$6 _s=K'o ^vl>ḟx/'+e<潨l&|'w-W|^y.Q.]g]ι{k'K񴼟? 9sskfB=3B<̟S}(sN{hO~ֹ/$]|B<ց'Bݨ#???ǎwȋ_ۡG9I-:Se}7eb 7 }|yo{9|܁Sh\@DĹs0w#uP[[:sX0.}Y|ݙ~wVEscx/;ԵSn$zzc!_g,p9j^FoiS3~DC'Pqt{gGuFgJo*T߷5-<|kgwHm=oі |9t6Tl>ipS<=~0!xtoyys~Y%烧uoOn)$>5zkHo{n~gmҖxe~JpEOK>.~$P{[~vtVs჏^ѹ.@#7OC *Ei;/Fuu{/3notK 'f|eK_ސϺБ/).6нyg֯FONsas }sX}G鱲; Kk^wtP[wa-yzE8xO%zyi%O\ :l9Q7C~[E[?7[B}Uy+~7Ru zr/>.#ڛ L%}r琂=`8`rTx }&r}yo'\@t5E=)Fܻ 1 z9z=oKonzޕmt/{㲔լ(b0N$:'S<] o{&;~%cOx/нc 6܋;XYc+gtd73o`E2=ΙN# IuQG\#뽛Z;.g|eL/5dž^] ӴoT|;6sCI  /C .7>eߎVG.>h{_s !t=,!/ q5/,e:a{LxZ_-(;B?|#`'tG>NBoswD<~ѥcVPY0wļ%>guۆ_n_hCV3oTDc'O_Ҿh!:_Ԇ?W!y㽌q.ܗs0@^NkܼGПI:9e=yxm 90'@|l gt?.]Ą@G&B{4}>.5O6 1nlߊskw1Om0HzGZP~wr>wOj]Bȡ9ӹk ,=PŃw{tՙo;Z`Yk\;ѱ3Gog^+w/9]5]tAr>~$Bcv[ٲ]쭉+;wKs$LIz"C_]{oz?d kg~:[ψ{5NPMy9gGhz_ʛv9Ke|L-:x;]ɹk '| ܳԸLgv!1yMEX?c?E_=Z៯Rٛ*Xa= =9+ћ~9=\T#}j)CE`4oW&_#_D 䕰/$cU-A|y;&qu3oҵE+:sX砗=}9'IG?E)[O y^wv>~a}+ N^8u_GKͼ .MVk_Hx~!ץ|a J.u~ӧVU`е9_jּza;W:m_Vɟf܉yGk>r_gj6:z 1nzѧ~n!=o_)朠r/-6skJ)ۆ>)G[\俣s!=}&+؃q{A ~iu1?3&뜩=su==_\6m0"7Χå}G HG>Otp t9zPi};911%}u\|{V[/X/}s.:"R_e%:gդG\L\#?b~ 1zh/=77q?qU76<||c/Bn.OE3{t_yU }G~-1אs6Z:] 3^|p/9: +G= vo~*<[7o y? |?7|?|/ko|}]tqGKh}>w>'0M.)}XKV^%+}kU_S}SsY3KλΛs؇#]1cˀWM9{vС~X-;YQut/s~) .+gtrm\O>6Ι|Y-;c`RKz>pH<7O_垫=/s#ο3OW=I#jGܪ\q](=+O5םLp>Cԣ\!\uss_m&=w}hGԾEi.:CÛ凾YB|ߒF:J934;'MFOi=y4KKL^9=77dO{TYwx|^̡E8,ڏm/!zBunJ䤄\諥A˹XjNI6~/=ߥ".yZ{4=U1?*"z+_КZoo<-+sL=GpH'k>9H? r/F$9;4]?^9qss>~4~.bo>C27ѷI;S}u/k&4E7?ϧuz 9r/?6 \g$<D~ ^UPGh!>1_-oygz+z_1z^*E?xyWPB.W)1W3aYcSfC?ug\ ݞAx/:XC/9WL砛5^?V>~~d=6zWaO>WOuϝ:4>Ӛ51{>ە{  {2H.49.3'^>leK/<|Gqn>;d NZXH}<5x-!@t!~UE-$8J8'ywϾ7E |_%9 {8G}#?T̻E }!96o'}f [~6(ugiN˧tMOwּ {vGؼ8 >JھΎoc![ؓNJǨl|2|ɝ?̝k^'n+ƓM+?Z-r7:KJN5n#\_4_zqG*7^fr3Yszs֑? yǃo9:bYQ*x?篰w%t9=ǒb={o6dzj*{cs Osr+ &/ ]Q ~5_qNL[->_w\\[VO߅{PO1Cゎ6sͧb2ց/๤.=>5AkhrσHBKk e=rFͩ^~?~~@`e5}*7r;2Vo9`LV9ߪUkI$ Ϥ%֙NC|?)679$^=MƇ+;9&Ȁ7.-(=k5c~Y|[_˷zoW.C<'h~ǏDzmtя .W}?'D\ҒO+Yd^IޱNV³r.ׇ/6_9[ݿ7O?#'=V{|ŞI:/ya^[39irNeu}þ?_xtYssM?K/[I/tK:Jsc&<#"\·F~>%]C6??ނGx~зŧyYb5 \nv?I,GO~y4&o?+:țqF{j7Wi{Tuwr:8f>ƴ| eO_h)Cמtue=)%Ul: =hm% 4}•2SsowZ̉Q}O_} 92YI;=IYҥ~>tTw|o G/9:Or[Z0~z'kx[Ws1 cϮ=ݥ Ź?W{#~.{Q伄x)[4Xp0oՇ=_!| O.f}!A\/[OJgg7i}ktÕ-ϑlfR_=,y-AK>{+'ʽo'^yQVxhN?҅ ~@"M'uqs%NQn~x0u5׏!y2 \|>:H=@8K~Zy C^I9<|H9 4{'}ǵro 7([#$;^??*It=ǿ{^qzw>ڙSxN9xwII.StG̩K:]'91wB_c<r@a>{WܮC[GL79X:_\Oxu9gj>ϳ'^ϯoJ_VizrQs%>y}R>H3I¯)*οtdUn𣤳RT g-|347>|LӗN OQ'τ#뛭ojzE<m/z@ͥW =~:z-ɣF32I }K/R9' A~Wq~az <#]rVBnJʍBZ;RI[Sg qOg̼tSٷ}Qt>JOq|cYMt~3Do/stM/s>Hzzj^aK:y'kO%ߛ|TrSŷs>?gz{)9?*xoN#  DTs%:/8'ӡTzѾWt/Y\kQ}K]v"ݟ= \zt#KH6{3.|*Oν =IzSo*y?mѝ ߹ 0|XSK.snճƽ~OqVA >-}*)Mo,#=;W Fz|L!q͔;ܟ$}WeGzƆ뷣겚S;}NuN <^iDS2.s9Q>{AyOAG gbF_䬢v\d{+,{"}֙f_Zjo~R[-J}zwHz!"tأ^Izx psy1E})s[#'xnȏӹM)>F uݏ>x2_pC:㙕>U;a|Uv-)YQkf{jޣ@|4Ljx -] og^IO˓_L{b@_ ޜGyU!ٽ!ɹ=n!ǜLҎrW Yn+{D6y8r=O`b||ZKO2zo/|'<8 ܴIǼ|W h%Noq0E 9{~I>|ǧwՋ\*gv?fC.B'Dtb<:}&Q97[8T>C7ş @l*:y9ퟨy'է.]Y+Y7r]J}A+H$$=GB sâ*]MMs*KߍOsڷ%;izN\!rz`F_9z>;UG~zLWNr rNO';'I SrUe]k>~ x./0^=䷰N՞~v_K| NM:; K9ɋxCsgz4ϐn`?g&)~Os-A?dzA=j&G$X_Drx44SO.ڔW(!ƙyϬokн76+zk.!pvsuޕ pUYx x2B~lOV7~zÜ>Am?"Cٺڣ8lnKC^/0#::r/B_uq@^]~]{/&/*<>gGqq˕o?t+|G@ {G0yvT.ry+U=S}Y0Okn__zlxv\7{&U+䅢:|+bDW|et]8%~s9w5ѫJn ^ 0=?$goyrKO;A_/W}>̵Ow#Z§fl~=t|s G+>o.}Pa…s & eJAy (g?>̋OOB?\N8^9 gKyG=G:J{hi˹ε \`^D砻?0Y>78O{I~@3Gྈn/n̬.:}nbO^mr}7"F:r!W<9^-^B8a}~!~=/}^xT|>Ӿ.|-u:W&H:AGx "?xU2oy 2|UXSm yOt];'&QOUu<4䫔W>}8)N`S׃@?HN'<-|3>krg_;鏙s灋dz#==ws $чosR藱^lUxyT:_os9Gt<ߙhsFBO `-w>Rxe1?ѯ {nu|7g ߐzСw>@ 0VSE+>WsIG̼}> +G'^Z;cn?e|@=A7 n}o.;Q<1CI8||-Nv0zˤsϔ ! x>߄^|"^Ezz-7 49oيw:wp&^pdG(|i3_BNj|`^羁S.B ?2|vBnsg\{#ގsO/ؓ۬=!JͿk嚹%yYOZH_ӳa^:{W}+lj7+|̍9'ۧpf7 5:_!JnU.>la,}7Q~i߀l_\KâɓM~)szRC5tSIp&wO :P9yF>e{_{Lm'_hߊ3{(RU7"aZ{'NPa%~-*z>gsrBϐ_5)x<+xgz;&NM_O-(*;&7sOP>4}Vyœ׷_2׋;*E':G7ӹOиY-rW^?O>YPHs5UhA/H/*{m;skG gbOVuq\!)!vȭweͽE_s?~shMԚ?  9x~aOyΜ/nk-5t?bՒ^+& QiBCNNG9yQAR9Ó͇=c_p>0Kso\FlNq?{co\ tOdߝR _g3)?jdҕ{Ni˒Kt')$$ْa'6s=OJ=zoڃЁ֧V.؛_-~kބNK'G%yOߩJjj=JrFk{zxW^s9~ o8X=1n{#ߧ}͡n'48qxZ9[z2[ڋqfpdߊ=~c|`?rJj+'2Ƚ {~ SrLӞR9a1gkOLVO t.9e7k> EGɟ\f\m Sυs7"\ mq[I>7|iKO|M?𩞓Wݸi?*5r˧y$sAf4haa.=`b.&AMzA'W ={7tS?98cKMw >{^|63g[JRQ36zŝ #V~9c|0W#gi?}*[}⾧Ǡ~ržy7=?SG=26!sO9yb?qHKFsht /+:ػ?X.#|Ccu.q]v=E&w)t!ڋəGR/߆__*}n]KNpI'<._96x$d>}~& wz߽'g;?iYq$R g~7ӿ!~,G>͜h)zTh >-zZzV8Ns[U_*{9MNC }'5.\¥m '!oaٯ .6F{+`.O4\r)ԟNh oԞn{UOt|{ =ED~h'ճj}*;<,sk|S/?=ՑSڣ s.|K8 3';b^מ4/i׉=^ ޒk^ <qE0s-szV{ ~1ޗ/!,?SjС~AI*?Og@I>N}.6D~Srb]-B~?y04*X@yqT|/r'PyKVB.?/'><>M8Agc9g֒@|u*t= / N1]&l#CPGi^C{s%%š_ }J׶1~~6J}o~^S@xNz4љtRA 7?ֽ߾Z{hrFz׋glWT ; S)DžRtw }y%zO Uɕ6yc\-5غзܼ9ăOÿwZ9]з&?(~ \|*x {A画Cw/T}{ϭV[_߿q־^Iɥ&*n/ܳY5E'8ob c{O) DL%4OܹFk^>5Z~\נSe}^QN|t`'Oj>,>xH}ncOкK4yeo]<1+9Wyp20`rQOsL93X9sׇ 9d%g8nޅ|eN߾?; Ƈ9yE:Kt"^/復{iEN-z/pCDMwN7 TJ"y{c k 7sx/9_Ek)9=W<1=i.S|ptNuw~ 9,;\Bl}{\rk`&o]+ztbwzO]W: r3zUX~&z &bo GI?S:a~>! +Jo p9/+uq_&xzz% zki,/y c=|҂ˇ!~Eg+htS+:e=䂐[CFr…GŸ\џW'{,ek'D 9 O:ӹK,tW๭Wo<r8٣,jNvSU~|ɟڛOqED'lɳ/OWepio8}Gs֎v]qE;$3{l;"<|۾%>'_9E?#<y䩥?m_kny[ -i4}_ܠsq:˷K`|۽_!w|Utn1s܂ɧDG٘^tGS'~tIC| {蜧^=?ܫĸڎs^rNnټsz|&ݤE+Xߝ,D~s/=F.]|Knޛ皧k[#e<{ns +{{Y8,#kk:x SG:>qi1[{ϲ^>u_gPDs3Sͺ /K9Ջ֘B5iطoC_d_ESsmM9$.^<ۚK7YF&{[xQ{y>d7n풧{We/}3꼪zoÒ>zעq‡ ?VOeIʀ[?{n6Ǿ NNY[ O'\ `9>'Λ><(7aL z[eK{^i<z.ٽ?Dzs}kr"ʻ~8ań'p&4n{TK|w|yu&'NN--z%qS>~LyW$'VK8{PB𵃇E{k.@c܄p9L2O-/@'xvqЭֆ+%yHssH8zOX_\L~%|ֿt [g&\gU՟wqed<_9\)vN+-^aBG{^>\8絒ows;ot K]hܤb\p"1>|^$i N[7(ڇGx tz{[8?½i~ x㿜oD~gr S|'tsߕW:^;>}4/V`g klNJ;r /j>?>z?lOsvZaO1oA^|ůV=}VƉ[8,0zzt2gOԹ~֥3\נ}7WUܯᅜ_l,(?arhѽǢPhI|ي DݧÄ[U6RN|e:M|e/n ܋.yA8L^ϖ #{zv#$Ӫww r>x+fshϽ?Vputyj/mnld_b{߻xwZJ_9f=+Z$_y짭z'DŽ{Χź?+}YEc1zjd+/x_H:Er c鉩o\TS?VG߸3=g\sj7I3FOW:f5V~$>$)ד^iz҇ϐ.%]Gv9yTKNSGw_s&pe_W|9JFy)Wj܃+=@ %3'FݢtcӝyރlS=))z猾Wt>ie#ϥ_=Fukt2 v7u15>o1Q:Kr*+o$hHy ّK<8Xv#Bmѽ|+#yH2{$~tcʥK;H9)87r_GsS}.^+J㼃| ':1/򼆏^t! &r)=·Bـ+_T+85ޔܥ_:o: l>rӸ  5я.9tFlP~?GLN9 sAm鵙œ|ezCW)}SϺs߈w]$s#:Zd7ҧ./gs3/By&g oi7f/'_>zCrn* }@w>\H#>$p!z1W?q3+俔{9ǽn?&z͍\oVM_cԼz^k^jB G aEķp*g-=P|Jݎ'ܣozGj߸>пf~?|uλ[{?+zAlBo4~XOz\dzۋoW `Zo$'|1|~Erwˈ9^ 2UzA5ռ>V.+enͬ)<ү# 3B/HUtB.9S;rܪCkj@PD~&&zN,r%j+| zaƷ#ed 2ttiB7eE[ע土dewoũdK^sm俭b݈sȇ?{ϸ=DkntȽ,6ɯJqapH^{x9~,?;@z#zoWhoB?n  0ূм/~ У<%5?G4=c}H+amYu~=Ѓ@>|j \+ȝ8"7 37b OWIK&|9]xygY|ѻrHVد'==]Wc 4}JgU޻7WZn컁er{YqrCn?a\ ?oz 趆JL>yyk웺@x ZSphUIo0rs'|/G~fsɵ }sF?ezl?s+.;Ҟ}_': ɚʫ|KWM}j:E xsAy7Fk]ɫ$9zP*4~O HO`:Z|XErkGogPGt <^15wkj+uhzЧvڥʍonyt7w:\.4-88Zc7=})IM0>-=s~4=a9BF='!6֟G4y&=Zʹrۦ8]oC:9N$7G_h \笃$sG{>:{6ఞ+~ |_y~#p$-ޘO.uaeǿ~@xuK{lRnVTǹ!9C½[J^W6|dыnA?g{~߳K+ "\7H^X7'UD󟽏g<Q.1.w9K\Y3~;E= /5yGy6l㊼dC_uj0uTKKnWn< sXөW/o'qԼG3 zzy#|  OPMҏC‡J7x^]o~6>Fz֥{r[n 3>,M?7 {4w@Qا>owi+{=94Fw;=\m9w+t2Q?GyMV׽x!puS_߆OoLo`3>wٟFXO"g>%:/>ToJm=?N =|?7zӎmO GoN"FBkWg+><г`e9#58#zz_AdP F\y1A4 R\h{^Gy0ݝ^*rj& ..<= / >?ys SUien~Obo"TokCsBnh_mk%;z\x1tU6=ZC<NIƋM[%kRoOܿt_yT :C_atLJb]&9̿+_uuIod{yƫu_[\>@u3<Nd;HLyky_c}kkУVsyy-a8W< 9xw>z_mdRCG$K]?<ok9)kj?EE[I ƯB^^ЕM6.ܮA3 2:9=GG|uHi,}>*{s?<-|4s0=Nބ|#썣ydG5_VQʨWcm*z'Wn)|3q} ~!sxu>cA/|[иbmg{ƒCzݠ}3>9pXc&=nѷңLF+5~7uG~x>9_@)t;BTOљg<Օ|ߥ-9]{;lzxZKxc3'*ܟ8jW7묥|O¾7[[oWضȋ.!Y{)YUx{%] 1C|4|IXׇqz+k)^r<UQ|537@9JP>vgyߐ_G@Zmsȕڿȷȿt%ka)#}Xst{e4.9kz?%g\AyeoZ,|8鋼ʙ u?>ȫg|Uo]ckt;ԳO%}At3gPhG^ڕ7Jw3r9J~n!WC^| S{Fk̠cToD[ ~ɟH>)CIgK~M1ӦƾG;Q-#zCxg7b)ӹ{Y~J 'wxi鞒''_jBo} wE[߿~tp WO_|/lM~u^p4i {dN=_q[>9![!z'L  9 '\DF?L{ (;@a܅|gɯ}DZt ߐ>W}+A'/x^K9Zycs~eW8sɶsٝr:~O0w\_޷`]y=X?=>,sς 'p~+t 0?rv\|5:L^Ypg=;鹎7\!'1CM~A{3VsM_y^}};u'w *'1I_K/ 9έ#|/ȹ/au_Y$OH?3{1~D&b G=z~_.]f6d pҙ=г |~#>Gg1i 2n=E7~QݹJ{..ps[!]yX#Z4Jk;oG>}} Ƒ>Xo ?J)žVrMwS9=psʽ$pБx|pz8CoPOr]-z:}~pvN\QAOOS8 *sn5?yeٻ!ȧK~/#9u|҇tν/~b9R~k#?{ZSs!:=7.t+܂Z{#EIz?AV|?y?ynWs~֐6322ǐXAqtoW?3470_Sf34$|^bgoPgʗS.=KK+G~,լ/$Bv?|urݻ˒e_?z|sޒN3M%s541rB_Q+tʍ] Hͨ<7GA7-mf3}9ɭ4{7WRpE7ֲ;9*9\eߌ|uU37? n_f>9ܟAy>yQh{G~W܋ owy!ny_fgo3 ao稹dxpp_fNowupy$IUn&V')tӓN'/FاeCKƟ / -x|^|uכCOcKv_dJ_U5W<&Y:s',;ֹ>7]BoOOY! {܅/:px}V+ ]W8,#Og|Vxh{ßH:[E:lA{,EhNh_5iM  .iCgNsg%/Ee Q>惺c#Ko|;yqsAoMOL~sߜ/,@v$e(X~r@muM~Os\MyUJYWYwrk =: sT/=*7p¾=qMcg>y:\yl~/}o9TyCz[)C=LL4V7z4uI7׼rF_$WOгd{wZ;{0{yה=x>Ar8W O,\'^%CW/c>k79[jo]{3FzTtQC<P/ G*=zx tbEw O߮}qpxړċ>ϑ5卮Pn,3[R cSuk:w?߃^ oh;㢿?{`Q9Vwu x?% 5 := C.g tOi|yC_skM|#x~L{/NrsKsNVJ˓^^~?T˧ } ɉMynBs 5~Cs?vMz3~trKo˥k|^U[s/Wy/j=żݗ Js:߂Ӿr?=o'N\Uḽh_?}Ӡ/!w?fjG=8Do9I{L~~mُX[jK ƫV"k s82/N&~Rz1е37tsM<F>&t )>1x)2pCC0~6i(1eܼ=:K ѫܓ +gN߼З]σ&tmٽ½Нӫ7?NWv7p;vLH ϊ΅ފxӊ?cI5o+?RGOg>w]=L`x`> }5?ĻsVV,=ae+ _vc{oW@o>d!S #ӛ*18ʗ_ vѝDN6u |~S?Վ~n6{Ct'|Kz~JrڍoJ.'|L3QC!mVc#csINP0|m2/=~( r\dG뼢,ѫ_x1W'>?<13U%UvO=)?"Oo#Y?Usc4sW.痥 E~/x,'pz]|ŏ9''^>+z/\-|?r}@7\r%ڋo'G!"oD{vv4.?G=b_C @`4o =9%O-ݤ Qн§R [/:iu[,`ߘ{HB?I.ϫsZO[X=A@B,>8ӏǓ.<ҫqTk|Z !b}!!OshoYF['H󴗀wg*=x ҡ锍ͼyNp'xϺ7l[4Cgy}sUջyz|\PF#_p1i/lܤ<) Z@MdGاo\p!x%W7=D~(t!Cz~~E7";{}kE.X 7ޕ7G(|4<1SbKޚSsx^~;kd5{5gA|>uT՜I |nqo9g}mf'YKԼONM$g:f@hw>9ȿOy#GOT-Oȃ#ԾerHߞ.)be1+L?YV<$ONYb$/)es7wǿ,\g/#*!#ﴞ^D~Z}?ks/"/ vsWl͢ /+|&.Kt~73xp# 3>߇\xQ𷑗.~w5`~^YUkѧW~SC;subfBN{Cpχ^Og /ߘʕ#׆ jyuwJCEh%Oo(r^TƵiWH޾ln^Ϣ)3F:ڳwvB|}_j 9懃|GW" 79gִ۹As=~(N[S{ 8*s"J=-N4O~pOwytz.omslH/aMgޑOw9*w!G3|y?|Ab>x4{lr…S͇GÒ'x-"!BoM#5yMٻ#ҏ{yN̿!ʽQ)C8nuzoZ;^/˔<4s}9E?+f~osуR.'4½ECnsY[-'朂vaTNd蛣x }0c?ti/17,i0GsNm-xY/, ]3<.;>-CѹYTsP}ƯӑϜ}<k\ه1ʵ%|{нW.ќ-b>U?9Wi%^"|SQB>qcsG?s—kL Gy\o Zu^ɑ!#\қSM-}Ɣ%l_y|:mK.duɦpׯp.fuu.1}f!W}Ou$w!}*$3b8_kOќZ%9!ߐޣGS^Lk 7aڟCq\:_&߫^j&s@yߘC;In8]9ȷ'ѺD|T{ߍ9fc@. ;&sTov1e/>')]WB5ZEUl? 2:{Mx }6<}%M>}t~5.QzY }'#3 Vy<;~]:PzW% _xW55/W|48%8o~r>N6?w##żP?K_`s6 c"dy:a~# QzzWKp0_ ||D 7ͽ zCJWAtOghh{qZsϧ >^2͕=,$BMo6­ D K(;|%9oPj>C>Gn9z' 1`|'f`#ZJ+>|d(o5m2?Sxp_sQ4?:I{\t\z _an}CF.wu' 뇗ިW7奜?y/swu-Wf -9O}A\A)WPtƒӷs3o5jY xAטeqw}OC`¼FywkC5vTue sV65qoɝW|phݵ}-%s%: g>!\xaBZ]_ #=A~͋]q>&,l?yC,5{}z[Wk[?Q: !=U #_WUxE͚y7,?*uw]\aWsE-F =un&K+_<^1?5w$x <=ftyKf/ޣWxC&x=yg WBW\3׌O9ACy'^]PV09SBx(Eg^iq+NɸWN裰'\O}y?:Η=C~#iO??FQ@39Gs:'%%t'm8a4rϤߙI+}9뷅r KS:SZe !W~"xA_|LNބ wT ~]%0Hr} hm _zztrv|-\.a͖ʣйGΧ!uqL˥6>RxF>Qgߊ5LK9魳e_#w \; {[#ӃCMn< :SCwPG;iO祵pnrFKnvm%Q AoMNRgg6IjyF(#:PA+48(=:{p5tH5=~K>k|?5DŽ"ZDJ@7_1wÆ<%&.-T f_)/'Exsk>M?>X>5G /κwfC7>\R+Ggsz/^!F/j)\S.4;JW~AW.+8MOd;B>sEqgV?xהS9 N2rI&(_0Gh|~'E?Uzt('":B| _v2Λ9Nzru }_~&{xpIȱF=u7x8>^\:>ޏm"zqzo {ҕ7V l+_}?$(ZChpB]~N:_7#%9}<}/On {#]D鞕|N\c?"Vosgt 7|9e>Sa4}=t |+Kz|AJ^9{vʠϹ*><Ҟ]ş O<ˁDghp53y4:rwяLn=zikͻ7CmR?uu;\؏'6_"e|sW{ ECz.ʗ.{q{W%_<{Bw=]D_ أ݇|ҽIM|n.(|q+ȟuـWX!(t{=BGg{Y3!)x^؏ǒ(Ayetx|,Tz.p$W{VWy$q{s}nUVJ1O.y_U*8yog,B|EOҞNy|@ϗ9j@o|Zد|?7=ݍ z~.r  |l2<[D>=(腂.z!Kt߲e݇1s\Qs [?ini'hpwɒY5u6Sf_޳ģ$W*y~?uϠg'<+D7Me=3.Ëܡ6./{v*z_Ѕש{qMy)huQCr[/g;sg~Ot+Qe*3?Y=|L|޼;?|./gNI2xdA';Qheog&eVka}~ɳbf>'ZOU;NOÌ?' _Ttt/ E) !ҧЧs|_"/~|9@H9r踂}agq?=BO U0/7YxF8}0=o5ͷ_?vxs9C<,_u plrzgU448l+'߇{9vjg '999WAGC l"s|u͂WEj>NiR:WйGGoMwȥ9tr~;_?\ߋ>s5\e?'ܷq[+N)=^h<>Z{~:^SK;^|7|i{'g`_ N~!n=|@F25} y#6G >j_<s Gr{79^&[uV퓕;Is!K zx3r>Cs5FoHּY+]E9zCʹG78gu4W[ўI{h:::S d竒>̃[wʥ^M[ʉ#: mhMŝ 9|y"OctY9CgnJw(OMCwŸJ<<2t+ )>t/.#> A^$|FSU<WT2}{9Gm=^ {:c)A(WX~S!trSuќ90B|vTJ%>U~jrl;~';KO*ڇz+~AU[Nl VWn$wtlфo\狏I}=K~_ #[0OjYĺRS1 +apT>\퐟^/з7}!=56mpOA?#3!w_''IoP^y&>ys3&N!9Tܳձ]~ÊW!O{׎~^g{~ok{xo"5x?βՑt'B_n*W)7{ PM%/wO}@C+W(pz?_Eߗ.~9R9Ρ{d Hԯ~\1A ishooޗǔ;ϻ'4x_!7m_47/TUƛ򛰯jOǶy|tḄVI}Saze~>GWLK~휕ЙŹًW2= O\2 r:+]UjwN zIoߦC__sD~GAgkuyG#={c>it<#z;W例w'ɕD4+yv{6I8dʇEei͏좐ğnbco.5_y?(*Kެ1Z=3/\pWzGJOHexF[D{T𥍏w?kjM_ս3Cjw~S'/~FE7۾D< z`s{_ ς[O۶"b^$?=k1>){~tO(=aur[ESi#1z뇩sYxE#&zt[+pY6Ϗڿy^+}P}*̢*2#' W9Uy;{ ެ>ʽ8 :b0^m<3#t>S}2~U!=a&>zyڽ=ԏ{y3ľ ~9t$|~3o_V[tos;tH;^?g]={ɗSθ B_C|ަ^6N'{fIB39GfS<'8gЋ<7 {.}QW=ėh/D7K_(7旮_#_Sk~ݓ1~~0wYPt'^!.=UydSkn9 pOhi//[ӷt/-{xnKk4ocH.Gn4мYG \sN^+~Wo|{ z ]ɹ^fc3?ʏ:go1^!{F z󄟢KEWb+8?(.I.#{ɏJ=>@C{ pn!9[F~k%\'7.~O|~7 rsyM=kv_8cZ | ^4x~W+(ok]i~?R>r=2f>q&ǖ8x/گ Y;A~RĽ7p9|G =Wz ɭj΢y>?p|&eMtFe?aNv.L4:MG V%>c;n ' =W9~ߐdjk\JGNҞ~Dxv}t`1W% =˥>K>)؟ܒ/Mu⩧>๙y N&\|ѩ0_DKަDt'_>Lzzp/=7 }COQ{/펔_!W|m6򡀷{͞rpt }{LWC} G=U&;jw fX F^9}p{C=z ÚWLtQ{ ,r >YyMso;Muk_} [S9נ*s՚ ׄytq*B5ޏBދҟ-=~DI_x?1&_u3uQַg:dztu9>R_论L<^-᭘/O>P{i;.|=,[E6| oѽD9^lW#q~N>T~zrȇ6_;x  9:h||7N.[|g_5Vr0%nmydxmm,w +mUNTc~Np1O\wtnֹW }tpN>ce>brO~?ro &ۇm|%僣a9=笵uN}ٸU;'z/o yd]:7н#Gaϝ^i?!_)\M?9 5+_/DI]cF= c'yz8D@^^&1O;s 6|~i7Β8ѿĽ.)YM8GS6o~z{jCH^FoU{^鷢 4po8x-6pE/_.|DݫBN93B7kv/q%7n3:ϹS/4/*7tIgfo3ߓ΍_% xq_B DWWlX>U9%sJ}&?'#g4haÓuۧg 's?w>ѧX2JQs_2Wl|tKᣊJ6~pʃG{ŸLWyf&?I}6\}zOt/Nҹ|)XgK[<}*YY8Juww`y.Sq_O=_(|*Uge'[ o}sh5Mߩ9xqz믊7-O\Zp|-/qE''ZÚÓ3~!Rݩߍgtzr4NFsV_|^:G;Z=wg]щڂw$?cu3tOr1֞>c?3nh]|F#-y#\]9^93GK)^<>^}^=hmA_^z|Z/0O<ӸZxisoZ.]ܻNG4ǟܑ35-{jy7Y}uZ4hG?u*^56OйQZB{[QڻH0p hoJs׏kٜ:I[t%䳭$){]=/i~n!Tk:~'s.]%O;__^~΂t,G7-=@>֔g]Hu+{5trNUyज़7m.\[nULxmXcu $E{HѾuw^碣W,7kxck j=ħv>-[{hzyNʷ:'zݻ奌|EB}m!_Tg2b#gF7(; ɊεVYvƚiBu% \(‘[{=>rB3au8a^VQo969Ή;WU^VRTK w]ezjzw<>ڽ-tW.=3[\}cI~O9wt!¹Knl/jUVX~&9_zڝ˺ӭ_趢 fN'[쵂c.`Ion~(?tp=xnh4S0$M^4\uk} KOQ_3~6=3Lo՜vֱz&v~j=gGK*]Os.Oǟok1ѼӤ Zrܾud.)HtZ{=ϫv}ھH7Ғ qh.>Vs}yNni .^s^kNy~ι8gP~FIRPW9 QeW#5U[m^qC_4'Ծ?7ccO|Pk]3-^x)cokSo)r>ڿutftAxǸf~7Gi*o2_k9fm?- a:/abeͽ?xѺ^|n{Xfc ~ u~ͫﻼw4}3$6Dlwey*}>)txV=;*ǧ\SJ̑s=[f$`wяS|ou/vOkyfl}=+A$~o㽐l(l}JOI>7|5)O~3!+oZO9*5>o5t(g|rB:ZǴo~j­=0X/Zۊ^n|%=\ryE>5w˲Ojf;R\y=wGޜsᾼͳWSgo勶+SWr}>ٗJkknW-jOsI7E23/?5&U!v2O^ZEz7@|¥>W\߿>ަ 4{;Oy.4[E9t\%R%|~79\޺=Mܸp˅'7\x_;nԘ=vP^ ggJ?K깩¹Xr2_ˌkb{A}Pz|=<>Unlip_;g4o{ca6ss*Z-o~ZQxGdE9#_hwާGrN.ܳdqwS[Z}~'o;MS:vsܻwΔ#[x۵ s<4_k,ܨsM#̟}/GU>y 2o7oJ99Es{ ܛi>t~'^1H9'_s›]xYqtýu֏(XM>ػѸΞ87G>޿zG~V?NvPN.U"G-!q1\j}_Ç(rَWQ=|GGj/,;W۸{[֓sc=b7Ž~8r7  ?Ygʼn9_;Frg/^8Ek":˟*c"k|{UN{V^b8˳ys.A姊7f<`1T˿=ݸOxHw.qXQ uv OLmlٜνʷ\oz)cxsϟEMyYԷP;sK{x΂G1ukc#|ŧ|_ԓRM#<+so/\-SŚC5g?!N{wij)ve=^t՟i`y"|ܒ{޹P^|>R"Tu-ɥ-R<Ɩ9/SjyQgLpH?^g?mC_)ω{~ю|5'w|Л~tBΗ-'Nyj&Z>G;Wp _ \u4FGx3`^MO+Ǧgӱ227Q\+皝, \&0z~:m?qq=[[tܦSw`1Xo34־1_s6 ?>ip_Ͼ\"΍ʵs%ۚ_[\<&KǹeW;A7N8^YfgjvQ_7{31{`=-ox]tÄ luz<5]ANb; wh$*z/{<|I6:|?gϮz^Ĺ6twsҝzm-:{'N_{)y!^/N9;Vm]+|xk>|箷DK <8>o'_Ɵ̛0Oe^=5WtNsrO 0\t!ovU>\s)=&&Uq3LiGk/V~΋+-Żt쾽=>hš+j$g94.cW[`1R),׆sR/,Y0}Oyha)N:v}QYL{zPٟ&I?goߦy5g|'S.;|Ndn_O9D>:iy${V:'ғgk.J}~~17y+y4!*/'o?yr6p9!I%9yB/l?nh^TN,+|g>_,FjÕORT?zz|/{ҹC8`+_k>?F _2mr"'9rhFMGIR_ȯsm~/3n=]dCӔy)🍼!:Wy~$C^Se5^y`8vuYuEi)[7(g%9%VU;1/FgRP6+ '^gnC}ԜT&z3 DbïNS/7}k7֖h"Ffx9ҽ+K_,AYO kg 9P"~|Ɩz>cpzNxZ{oZ8siG?X:ݟu\[unPE폥%?K~Gy|49•ԧĽ~#s~IGNy k;DGZC9dMkGHy 엜ͩ,mvy7w}>OB3oQ4ʽ ~y/;OE=Urcegh^<3m)Qycg רltsT4CSdbuo!{7 #zk";>%*sx&=8_e#U (_sC)cc>Us룏ޖ!W{0{nwʵ ߁y |=ݽ؇i~bc'{ =e 9yq x{ȋ'}.x(PY̤/=} Xr,L@)w7 ][8:'|3J07Wg,t|{KǗn4|9s@5S{34R>\BCrFN|^໭$w=Ics(o[RpnWtYW^97+6Gy|k3'Ϙg]_1q"9CV^Ϳ_MW|-C. )L |.̧S[vcLÛ&܈}'"IC?oCN<sci S5|ɗ-Nռ8:zr#oY:%HI:E[\P繜Sz&y~x8GoO[s~νx3g0r؏]Nx0-~ƹItrñ/}[脍\)bז|E敿[8ٯu/߯_.vAį$>CΉsʜߡwx|)܃xéװwhcY5fjJTfl)WӾ;:|ݗNiv%_{Կz nsUi?>'8Eܫw I|K{߃uz*yCsOξ߯r~|t ~Ggr?MQ\nYs>>`ߗ#'4y{ ;92 m_= ȭιwuuOj*cq7M;%?[(W㏽Qx]:˘dxBswf:w)85<|?p! ޑHɥgχ%\p4OzWӝtA ͝}z#w_o޵с^ݚDzO W#g'rv-?Hs L_Ϣa} '}{?eqkt+{y/W£U搎\,ABsrNNWNT;˓y%>MėER|&MS͓\=ɺK霑 {7ȻüAZx/ctUUx^@.(){QW|a˯#`_o\DnTGæP^WMiC[͏S\wqiOd%yEvN.=./UƷ&|ws*o©su+;Kw7x+}Gr sdO=D{teЕ$イzGލo_4>$bTsG\g߭93Ar;iyit̽ .KN<~̌00Ol'}:w\~ؘԾӟzBs(Ћ.9U%rBlܱrtv T{RF#93ש_0<3D̡_=~p-wx~tUŪiO%ן7|#׸ui9/|C_N.搃Og'od iꯧx3ο+se)~򯅿ԹS|?众n1.J)ࡋ4~Ҹ[oߢ?#<~yK[ /?=.sšD}[PQ~9zm?Q1:^ >)pƭ {xi'aNB)yѿs# |YCGЗk ~}-~7cq9q_ctNOMWG*7O{#=9~Dt[wi{=ᄝ*ԿSji ~b)[|..AЁ0׎;znj.kexY^QNm~oEu*8}OD^9t}$7z#-c煾O}#ϗ~PDCOi&9SuG~pkZxpyGϖq y })K ?-WԾ -H'J/!ow~I/UG?;87<< yh{ׄ^IӃ_t_yYsuJybN/f} 7-'əo^g,oN)Zw\S(vH'|3=0N`Cw 7>\bod8~2:?goO>qtE|:ГWowY_}m6 J|Y Ty{ N~E_bs=]`ݫO\E|ޝyk?ecOhG{ ~}y\{o(.sw T#j_޸?'`_޹L=D5{@^r:`,o'~Nl7G> s;~⽩iOŽ4~{s |ۺwge̢\өVM\"X [sx?e4~GS.n *xKq0*v_Z|R;Lo J+9_jN9'.q"\l}y-TU,=8IT7>O?/ug7v޽{4:yN9զ>'+\er&CÿTgü~bhtи/;;ϥ4Kփ?d_+:zr?I\@~?rZoh>+|g|;q99 \?|ɏ<`-gK|4nMVqb.\_{/wN2p W=׹>'"zy3+hn! Ce[mhzγǶ+}tTFme:Z0xFZу{/|sy2W_3;+ }~ɼ$}nN[z{[k] aSzV\?ҳ O>oU>AgZo#  O\P8 >'WCdk|䖱כG(D9:_B$ <ϔT rEW3ߗ'n:0椃^Ey;-Qz*?^4;FOM5{9uk4NO$zT&5CX g/9T^^Gҿ4r97cUppqP<՛/9[?qߐ0>W;>'yOY8!_Eϒ%Kg*r;"M׹B^9g^s&pcOߋ|KGA_q)<~>5v/|6"sl4O_]~X?S=>_σOpWyO4o0;ލ_+$xk _# <7֏IͧK~sk/ -}2;EN߇|?y'wޯ_)^n`/?ǘ&@3F]=Ly~tۥ|UzGX7FO{d3]&t~r.#&4"]932{I1U1^ ^O={m=+t/x_*C~7չU{ϵlIw Oyr񁂃5r_A_=ș Up>xr-ʟi?7;~$ykDy 1o _D$@[r:ߑt2]okc u̹y'7<_D|ռzs7sRw;=x{ӯ`%|=iBf%_Vyݿ=xoFz.W}+=?7IʣFO\Pb|%Cya$=!>s4 +<O쯎Ɇ~F3ʿo[CkJO{^~{ȱsrmxQ6آiu><A,4m% #z5%3i \~=9ГJ=`"gyRDy?+5xd9M|g3y8ay +3=5硯w%yyQ?kOk|WF.y'(\2V+=ÿbm\K^$rh˼C^+mx.g·0O\8:ڳۙW{=o$B/Yc=1t[D*< oUgFHnW/[߃敔kR{Wx缾9{O\/ɋ{t-$K˽9|#~wsȳ/#[Ͻ1^8rO ?qC~_}uYxޣ=(w6>>Rcc!׾XF,OxnO /e>xD~"_ N1a狙we(=]|ܠq WO6i?_S} C}8#RWxIp?=W};˭rR=eU~=7#߼qN\IG'[D_^>Uyݜkhϑ9 G|qj,y˹9 x!D==RA /]\/Hj\侕PE*Nޝ<\=ss-<߁>&x+b#+z}?}!zAq_}I}&o^8μ_Go>)|=qv^)̽طirޓV\.'Q^>q79g|<_ExrseOAU,Ns{#A!{"4]chk\ʾdݼ'C w)υ~rVm<  ]ٿwl( D:+~}y𼶖:iA:xu~M_GUEsPoUs;~j1zG }=\}tȡ$iwo'1/> b}w휺OįREs9xlsX]#(tp1_ɟ>w+k5W*99ӫ/_I1uO9?= 99 t|#f?Ϧ4x99n0g7?o 吢#{TZQyn81_g X[y䈑\{};g"hvv;U\Zt78x(z{ۯN\}&9πۺfzFNcOpO^4f=Ur8|s:ʻw ֶz>ޟ'=ns١%ps.*i?1[|Ʌ:ޯl t{&O7bZ=o߇O?ͺ |ھds <&yuhfVâ^^9},M^QǷ.i(6OK= o=\wxm|µg @_\OtҋW9S*^|s8О¾>pqF\kKt߃ϡ9ۏԄ׿I>Iʀ}x!zW_F{E3z&Sf[/r/]ݔ?lG;w=29qGc.$">oГ4f3jz{} _߫Ɵ<,>xצ\{|>*3hN~KygG48cF&=}Dʹ^ϔs,I>Nb0!z[z'?tx]VΜߟk!= ?ԟrԯNp^-+9ҕo>JI)FN·F&Zﭤ Nƥ͓99 ͹~Bb~'vcIT攞\hk;8%8q N?<=Y_]F3'n4^ק6|~f#zoM8(4KKs_糊{ mu词WGAWK?zc徢[K9~]b|975sWnlj0r;WWSzf?"W~Z{693\'yrϽ=O'F^9e`oתs̭K7N z3Os OE^ %mnD+;dD^U;kN%pVws_ou9[OsIwO] <=@dX=}rY /?!<%?2o1/s"ROy9\<} aēSN?Tx3>QU־}tӘGBwb?gu*b8gǟ9;G/k=Aq96~9s3Sw{yx~wg"::r4$y|t5񋜲_P~KKO/~;$}Lv=n^8k"'9{a{ӝ47tC*p xޠ#w ]s~&KӁ?> O|S܃t^e*W=xo3xד_s?9N?@jZ~`` CIDž.ܞs>^z!񉠟gs1}nܺG̛%>| tѕ\qpr߄SE]7P߳\FN{r-sE7 1EyX'ec_'yoy rt~3CxhOЕҳD/k'_ +s,8םϝúz:9Om+"߆;7'ăsNOl2g3䝣g_?O}w6V>WWm#ǓRCC11_O72vpsAz1iܩ||M_F6B-6a׃`v8HO_cyskܫxX|gџi~r799hrorOwH=P}^}Ņ|qЉN*{ͽ?eǍ;8Zo`?^w{y~qc {< Szث8qN3RO.׾i}Ĺrųwޔ?\+i{!${=& e~O|u;>=kh.= \LM2/P6̓Ӥ/ Osf_Wrڊåsf^%I樾Q73cNq.\w~R|37sސI_~Yren$-rO)ݥZ0+˟;͏)*ΧE~.tO*\br<#AeՔÉhC7?~M.:ayjs++oC$xn mܒV]Zzygvdspߢd<_zͷF<ᆿ ?9q5yWP ?xk?͡=rf=koxxCGAf~ݒ} ^7zǽƷ@k͠k9G蹾y|?CS>pst!ܓѻl=`w}o?9 ٯ/ùcN?oue/g8<ɯȾV_Aڌ_0W|o} !h Gm_%cاZ/)O'y;>HfK<3>apx|K聓Ds{+_<1pxkEUW $׷ʝ>4_97_H77b^97{W9Oк?n;f?}S}p8wɏ{Ȟ}_t9#fko ?~|ᴮ +9WESYE=Q4& 9HN{?Zýs&OۄydKG |:SjIx3.|;W~*^In_t"N>¼yS/{^:5Ƨsf`+y/=<~/ =s' ~ }h]*oinP y ji侼76IzazЃ}>$|_|wWy{DRMt+S ^y૝勢oR2".H k|Y>CVO_>r!\D㆓qcZoMt-KsKVTx?"g}tޜ;9̫{|CsR=+s;}=|31_+iw碷MDA~}pD}o#V-W}=9󗬷;]:ssGWG(|rkBe]::Cxr]fmtC0339 z|1]2SѭVS?Qo+/=rL#9{CZEytO#ݮ9cj{IoS^+S')6v޽7to2!=:Z>o}:v?̝eN%m;>G!Lmvc)g?b)κo發lgg%+I}oε 'E;8|<嗔y7'7 1{O#:<r+uXLzrӘsv>Y_ |zk?{E_?8?+]z~pÇ=߸< ~vztS r/z}oΐ,r o)0 Ꞣy)>G_lY҇/:;љg?<>Y>@z7 Gx^ɱVJ~r %~{AQs- B(< Б?Cm0~g3rtO??pr-ŻDOOwC"1._f [ԺI ]ȗ½jg]Cs^Kϛzҽn1C_!xZVӂNXN?_*;9)4i9riuͳzxgt)tuW}*XF:|xv#Z"=={-u_Lo=ܮy}6AWrȏ,N"^| =s,1`5l+pGr3F |33N=B֑iͼu]FWy+{QWCj޲׮iUwoǞ5z1xDGħA.1GI y7 ߎy9_ I?xCe&UpgWޙt./Ok{By`rOB[6pir#"7ܗJ0aG3ϣ7x|S.W@newݍ?8|Kd(pIWItGz=B/0:Бeujѯ>w?ampީ}wxϑqQ۸FhsU˒kOХGڻ vP >)~p~xG|3IvZ!ϝs v5qIgBi#G޽S^O"81ٿJ{M'OO>lߤIɯDx﹙rEao-$`Sƀc;|o?|i—3:i Fң\|8Hm o[ߎ>n`zOotkM21xj]f>_91WV^W$kέ0scxikrn`?H-rcNIN''{} B6AmwI/1?Ao|>Gb^eXWM.nxȼD >soy9kޫAtߺWo$GĖ^'>C|΄4/9-+RKL>跳N>8[,_"ryn_\%O }|9o)ϦwthhkOt^Nby=dR_49ˁ86z]_]gk\"[2>C/zSy}7ƿ~HnW~*7s_܏{dҟu~s;uA}:_OBO3PW.:pX+W{N>S*r3ǀ*fro ώ+0_cϺӢODݺO|m==p1|@D]%pyAm@>A5]3x87DBzC;k?ŗMJd?k$j^:C:}9#VK']Ϙ{ȣߢp ?U[DP>S{OΟ6s ; W<7ɅoKsp/s"zStE_xOx`XJ.42 />0wG?7F;r|A9/5fp}Nyoz|'>T9zڭo7߫U'So?9C1JpZR9Jsֿb>;|zv3c?A|F6䢑P9XaH%i>^zc\s>&';wo'=Jws _ 89񞙿+ru[O?'~xеZDO^:77}{po+ 00?^_9ҷF۬~+9 ;;}0FJqm!?PkSΫ+^q~_rÿypsr:x^@'Um|spEG^=g z k: ?y\K A5_Ԧe6ȏ;ԣ1h8̿R=⻣= W{ ]шOcSnzt,l||.s#۰^AGB{,y7\|ɿt{˽?/Kob*x)ď'73^R9"םOKGsy; Jo*3^9{x·@|'[3h%߀ЛGS택HgB"{=UCO\&tq~/X''6Fu(Es1wCUZgdyq9|ȧ|k㣍^"?xN|̕וCBC&WpoauŭYGz*gI pzi.)ozj?лDu$cj|($V;.[9]@WkZoWL~Tu)ß1mtZ{EO05:C[gxʃs7:I_8RgLUem͢~ҚC"φ_/ ySRϪG9/F80t& WdNS9)/+\+Gr+[k'}f6\zʷbhExOn{IgNOMOo4oU$/$Bx>=t\ Όr8g^D!اC/쾓g|/ލ, )rE'2R5B>dk&<6Q*yT ?79'ć0WFqmpx!wpnyo,tVC~s=Oޖ~~?Foo$5\ zz'9_7<ٳ7GK88x 97ﬥs>t4T>~7[!ߩrtq6 ✭7؟M){ŇyқJXt\֜\,/NGs5 {/qӒǽֽ}.KsMG^'?xW ;wyk3xeϾrC>8D|w7kC~~ѹS{Asis{@$xZAS_~V(9s񽯨!_@>M/5W}u9g}Bv?խg #ZD_|zCsQuć&9X/4;ҷCyO 7 >y_9~bEշGΜ+wBÇe}0cn\ѽ3 ߆sVny0gGj.7E)&Kt'yU5s ޤc@zxN+;:&87 ~^[1W/܊EIz0rr%o oĺ|*ܟi82{lV8?l eNg|Oj$߬:kw'˼p˓ %o)nJ{ YwֹF6Ӕ ܅rkxZrE"]mճ==?u2O1уU*K{3>9[RZs 9΍ X'w5w KuA!4m+'t<7]L>et/~CP%Ax b.)6GH y}_t[D.:Moo&>8G;y)}EߛgOg|KN>Fv(09pDŇB~C%uojzWSsoiF+dN})BJ 厕3 ڕc^ppjq%+D=S3e9i{a wr74̯)3gbxA\e2/%?\8,ysԃxե׿DН{ _^n RgJ=xA oycjM._yʚ[e?4z}"o  ut쏜wAg|7|# g^b4쏃'}x]ЅC_ѾXmi|eBiH$<6'g{ ݭ^;/>&7 <}]uxsz(xGj9pE{8H K^n.^K9o($>_gXt~ΑA}g8 >oxqߙo DGZ`~oՓ]`|I?^[PAٺcpRo?xt. }_K9`~+yu*Y'N|zK~: .Py G*|HMa١Z]|KAi.!3mϓ/rgңZ\{ewe叚N}Wsy bm { 7/R/W<Cy7$}`ϾL>nV0>ߤӄk:Q>+zO3 "{'KpߜN ѣ%r8|{I_ H?r8D|z`Xr/?^9:[%>G9ڣTW<]u6=G#hn+??Ugq7{ΜE/ȿKO:z韛_?[!;X}CۿOh z;l:'slq @䉌>XRrt._\7bmKcY~髅?"}F:"~Z.3Oz(؋7ۺ)ux7'z|z;{>LYr_=K籓tE#z1+ށWCW#c>ˏ-n9>6r/O-NsSC77ߧC̑4W>>J?wK#_B<8Iz1#?<_ss7& '=S/y/{{up0p"EcŃ:zໝ?܈7}/VJ?=Σ@w @_ ݓOL9~9QUA-Ht;oϧjU0󼳄͔R?fuk?DGy!=Xym<9!ǂzg1sCGG{jM"='K^Y+='kB.$=ɡu. Ş'1K!{s%?2[ſ9S-"}}pƞIG}4 mI'4u_rE}y 7s|[] ANELcRT.튧S>81-i|dÓv}vcZW58E y`+=!k//U @~u>Q>ItЅ#2wmJ=+G< 㗡4~}rr9{Vg3?_Gi^>/|Mxp|4S||SwCG^=J瘋+8"81 |/ȹ$7֚3O- ߀?eoYW;p,8lcg:=ߵSE}X)8G>ڕ | }ы=B8Q/~lx//F׳[NP85?)y{g{F^ zۢG@ή=*ǡ=jʧ^*yA>ke/Bo\qtQSz]e}^74yfD |ѷҝz/M|ʹ<}3p䈢+v/ Ilo>W|tۏK^D[y}]cN9=Wڷ^FgNt%Y_8tC7/> $ y.*DIgW|/;>sґoGo3Hщx =]m8q,GqNsw_{>'x)qΡwa>e@_6?h˱4[k3h8&\5r 272^m[_8K}%BN=>8Yߍ}5?\D~]z5~fKs }uU}j\? _G!ھUݛt75seC9?s?k.bu}r_s{.aֽg|}os%_~~tOt8'("ȕ×x{{dyOǟ=t3Lz~i;cqn6OܮKȭ=$pasOUxZ:O9WGߟ,GoU_x7O}Ao}x_ЧSķHCmaH{?(yyo:LbtzwWoLb <8G>ϟ{GJ_2yGy/o9GǛp'x O?.җQ(]}Nc7ep/'StlH#7lzqaޯߣ6F|D@hRۼG3(_z'KKǽyP_ex~xlpYܹ_p_{:iz/*IDGxʍy$K Wyf;|*f&F~-U;mC +{¾1<լWﺈ~wut "gK3}= |(xogfb@?0p#o|i}Σ{bP=>\ҡǯn;j}<w%golݣ|޺'>ft սO+ͶS;ǹgW!?'\w|+7#|Է .ku=67#WePȩWAUų=sHP]h t7Ro/ yio"ެ/ 5~Ba>ݵZί @ OboFnHkT?.?Gǔ|fn(C_(g}dwRn ?sRf"eQ:tXG6ڃz{~3OW;rπR+=s\q8zcz{ӹ Oj~'rH _"y˹>f Hgm\e|sWσ799/ȎRn 8dia)~mH'{"'o]>~5os}?G ?'I0yKsZ 窭f17}1}܏gt^Kp}59g }/R~}#ú'{6t~wy9ϯ} _c5g*i_{D4<3,72^s(:YBO%nD~o9׃{uپoxyߣof7ou>Q/(;)rg)w|G.) M˖xM*`srCb4؝IM砫3O: inw*^{zN{Zy~HU{:>4a7(^J}u>T/ p:~Eϡ yA| }#Fn8;|w{'=_ݶx y~$rϘ Yiu7gW [uǞaAqzуܥupb>?=å×N9|hyJ3ߔ6eߗ~%'|k+ H~=qr>0x x5#xֶsOI$K?CzCrXܛ=Vʵ%ו4zjuʵ 0&'KLJ }=~guW^]&,er;t>8l!ɽEŧ󋟞ghMs'<9dZEKL5/,cwHzbRE\'^kw7~B>&Um(vl._Xv߭VL8<:_L98xscҞɡ(O\U<*|俖 ^~os9 ߆થDžg߲k_OSρK~5Kq@G9 ;.3HdRSE;ZK/+7-UYLjr_9zWy ~CIC|~^9̽Gڳ~sTqC~/g;NqoR tȕNr謫^xRsZKY{#Y')^dpj5WV7ߘpk"g7O8Az]t$̗#ʺr[*K(:mo=挡9vsL8qGﮯ !Z=5yCon _'' ZK^ޯ Ge'}dgFW˘i\cJe\#T^L{sw'i~N3=9_E ?bzZ'%gHrdnVx|tp7ʻ\1{Ќ J_)<9AY8y;9ש982v_u;k{F=#Â_їν=B~ y9K􇡛$9,уr z+/ȽFg^ï4ff#w]a)pf9^W~y?rwn*'B[G~&Ǿ7AguG}^{~C\i1_>GO0ts?lzwF ؛ÿ^ŋ?Fw!(حHg}]:el~3Y~s=ape|cw6aϫ?bI'U"S0CI_ᖽII>?x g)?vC8g]sFҼ/ZZK1!od MùE}/Jß? IY"߃3/;ORsx֑v%y[K;l#k]^"zvOV?$*2#q]=< ]q0dPqׄUC0qwp6rq/^vS$|33/^}k{ B_^iG5nGr;۸[1V|սKc?o"׋ڮz/{?=JA@ \wyeҿpN]`.,~ޘ~'/K~sfmw!'y\#gހ9?^G~ty}&>`7^3/\3}\[K/<ҁ7rx{N G'>G#,(i%!zN=smcU?t!V\1et x9W٧',]?^./ ;1~'\|&t & {loˎsI?oo"+Kk+ .{&}WH|4:irɏCYl"}I~L}?(G~soi~ϣy_j]Y^+?SD7%Vs?1:vM11~ׁ?l#ah sc&}/?<< j=u@zi_j?p)Okt*gWnh >bxprO/{ɡG/o){wg/[Cs&_=XKM\oU9n% q"c~L/ [&ܔ PϹGzqT4CGO ] :)b^~|:ƒp_/=zI}4?59P% ?uOO0"=j=o}/|gv)pa| pϨz(t#" TxdVKxc>x%k;.'=7:=%|ĕS 8~ =t 􀀳u~~4ΥEG_9]6fu)yy[xͬ Rʳ>ӕu>PyCY4J!Xg{\vN<'3-C=&{@:75F{u̗9N:&٦W;Vy|I罢wy~.29 kMEoCo9r|~^B9p7rUO_u*(A||q_V2~`~<Ģ1'{YCs]֬ b'\X?oOJs@gPfk兆?3G ^Ϟ;jτ#\@/s;4߻?.i̍WZiG/=r'Fy0ϱOżZ П\{:Q/sr4{ _ry9>?5jp~u߲%L=oOw/yGyu:>Q94o}\ĵU7RLb8Nއ3K33n:za{7/RP]E GOr~{Ε5? W `r7?5zvٟC"t=/N1HbIm/z (>\r+ȯCF>}4&o"~ ܁א @N{\")\nʸ5^5ԛsV)+?eGᔕ4OC. ?IN YHU1/4}Z +fVJ!vy.cѹ ]:QxsHI~9W9]{^~-煣;"l/^D C/P?8%tR^ubMXޠD_MGu%dt7}:y|o/xۏ9F?ۘÍcʦWzlA#y6ݯXtUn%8~2eͯ<;y^ss^3,Axڑe]bD7tFG&8%}~\:Hε*Ϥ|"t+u_g'^|86Ӝ@͌Y=7?6rr?zO}EA҃1M[`WnyEV.a a:qrztpry+L\幪uzCwXJ<@z~mܥy ;':{/~ =ZgNϷDUƿ~oS>rб綞sަstutЙ1ݸ>9<(17GA@w ^V֖|zwN}Y>Fyھ?sl<+  ,ߋ-VNt{'HF5~;MߘyѝϽX[O{>Y}u=#Ƀ'{lÛOA>/ʓYaywe=׽#ᅬy|p?x,=%97܁{3|erX/wR齁՛^zk cG嗔}<[F9>ͣ3g||P'ЙBY^y\pȽ /y{z0vw0[&HI>BpZ/\,6ҡ.K&͝B$3}T<1:17wظ_/y i>L|| J t/7J/z/xVƊ]\ΓOatjs/>s.p5yϿ?w/c޻ba6x hNf]Ŏ<ד9u_8zƬۋ= =7kNz#r/+_{p| WDy?dwrz'OVCL1z6W>7#%x%pݏ Oo:x{59q3DOu=V[{1 ~sB=s?Lz__9> m/A+SL)9ƇΥsxh1~hyϩ}|{c|bIow[VAϵ0ܹdƃ-h=7͏J.{WiN^tLݥo]簵c!7rJܔKt՝uT+2?J.CWR[F{=ݽ>K|Pr3#_6wվ?;>p/8__2yË\_8xho͇;\!Rc9 } T|tԝ7fH։xߌytɔ~']Tok"_p<4I>C'y{4rໂ'^7}E7WU?rEW&}yi]3?Gd"[y=,wB9; ܰ) G8=h~GOOΨӟ)ᔣ<>PY9MyЧ7eo龥}/|wto_W]WOkߠ'"W5xyisyh+syy2]to/?!H:ˢG4t qdəqENuj >^gr5Q >Dsq1/sNqqYt6atg2GciԃxH8 zL;5_z9LJ{ki?`o\1Ɠ}}Ρ޽ ߟ󶳼v=u787%瓓VzXDsOH/^w?1$rGbs{zONt)]Z$};ʹ-\Ρ=}Z?O<0GgKk#yM.| >y^WyI9w/C7'Cop?7x&:H_k^9vz=G\yϜǛѯ~ EpoxB;A-t-c^Xtҹ77!{|Χ9pd>4]>P78Tf|O#rSxp'D{beR 0gr-5?4ϜDz#zE͋bļsA !_Y{+b ++*_XP @ߍE~r>"|xO>[tNA-/䠠9&{iN<\ХMߡ̡=1KͽӝY71&;f ?2`n^zO"o'w[-2޽w[cʿԓIro}|jڿ[sF'<Nce$_{| ޑo&~ KZՋ{}tezCA^Bt/"ڼy N.{xwk(ޙ|b佄98}Aƣi={xT{f8 6hݞ{_>6WQFڟH=iS9nNrLA䍓yg/yQL|GCFqjasaϗng{>.z۪'i.A 8+|eH>ά9$zQO3&oo~- ~TSGn5:=_6s_k9O?,B{ Ϊ:z'1gw\[XxLcW_\U>xdr/wu\}|\$?>MmQww;(Ǎ|_O7hyM:ɯ})X_r*o'tz xN^u7o>qr[ݛ=;;9M- &ߞ6xG7 =/##ӌE:n7N8c}O7}{ji0[@!8 7 0=䎲!/ >_d_ߤ߳{~]S{$C7$ѫ@ OW0OV?|y_O毌w> ~8]҇{|97l̯==9DΆ}ŁyyxtΝf` }Oݨh>&_r]hwy;R օ猴"E{xyygבzzmwWB=a{W_|z؟<73~)NOǜ模3{~N/)DphgyEaQΣx/"_~zQ—E:P ;ݲ;E}gG\r_gNUn Wk, D.<17_B.w_",}'|ƻ؏BJu ok<,xCy% < !y_dYG;wZ'L%=,\̅̽7C=CFٺ<|pxz? GCyCnG8RϚ><;U'<.}((?}oS.5דgϙt#?k7~l6rpTZ_: tAsy̍+Ox^{FOOt݋oK?G?ԧ߁^L" kmy Qn={"OL <=z&oyq|U49񏡃v>ã %>( 6fr>Vhm37>v4[1޿{Hߔzg3R<:#Fos{$gں ӑBx5Ŭl{ih|Nzn*CKR"1zvK-g~DύUߘ# OOfpTx{k(X\1玾mT駣7 ;ϱ6Z76n H>z v+']yMGϥ?Kgwrݳ琇cL9~I/9I'Wsg=S}_{igs# &7~6f+;p,pʝʥ@Ŀ,HΛlsQq (=CDy|WYaՎK?gx#O7''A}־s,v~{j_ץ{O󐿧F=ϑeI;?X~w+#Xw&5:sL:>]x#x"ߑSovyǧBׂ.0 ro7BO/@>ke٤{`e߲҃?N|W~7s7y&䎑 ^Sw4/q~xMD{JK}7~>;*11ENi#,; XI=gO&{>>*/#'Icqҹ^oud}t<RIٙw !ȗ }VGn{6xy/e7DDŽHN>KyGN~st>dOH'=9a=ƿ168nt_:'\{{?|N|4tW~ī2wozOa]FF{nB;{Dr?֏U]K.bTwRɵe nꚇo&rC|ζ0~~V?z/z!e)?<9gra;Dǽ/-gwx,6S`(ظnyn$@zcϲ(OtT֟5}:I4zߙ1_6pKߥ'+_PnoƧ~Q;zxZG"bՒg {CW;^tAۇ-ڿ'Mg.#__#5HU:W'{*@&=|m|Y k!}<**?Kg+o{uda]۸]^V*~߇~loMt9JI0tzM8ט#u9W=ˑg^WpC>7rG /Eo >9Oh_s ݗ*|U5s&Eυ|Hhs;9-rߒ{0zRi~l5O=gL}ϱ0AΏY7}uɱYKu[)gCd)'rvK.m߷SgA +W5?s{$yߤ ÷^A|(6 _|aoBMnq|[sQȋһ[ٽ[u|g_9U}gu9ǃ7}<,HL,<O 2pߠ(HG=9}ն#7(:9N8Ҙ:ɋjՕ߉~|?.xMw$_}{-Lkω\ʧ}"ָ>đ|!qOsT Zi/=R"N$=SH7^=N=qCgdCc5sy8ӫ_ :oY9áOܜު|CO9SgJo_=<<kK\| t|Eo _9>?f~?c6Άvt#rP3V]9uaަo޵Kwḿa-ݡ=G/o;[H?٤D~[K*2zI}B2\.o~{Ee_{&z&n~ ]Ny&9SW4z٘# \s_F.yg^:R37=~esu3軍7?%_y#Iy5/b<+|_zOcBoy~/x yDzLU0{B/=S\II/ޛVthstE|g&я|$h>Sׁrh'sg[M5IB)z/[wOiOΫCx5zy>xnا+Ճ_4+j0w(}̮=*I7y>觥_ >tUks{TݷWْw<&4g΍ D<ܫsΫY^47ș >eWd? f~}d9IS񿅾ؾWKȳcܹu[@N7>™k)<;>zx{)߭ n~2M_w^USo`7j g5UÃ3s~.psHy_u/g:lYٷGדs+%_.+OseO_^ <¸LXD *h{tߗ?.[Ky#_rSo&Z_q=zN|H>\ڸu*\?QxG{)~]f>(}m ЛE繿3Xz/|Nmpp#p8|+Ļ0'F.9+Kzu޽W}j{lp8)E!Υw:_K+>j>s-=\utqj+i4[{='YL|L{~FszCVNOt?7~lhn/g^*ߤagQN漨\NrTx/RJ"'|kCK{o-PN9|u]/;rܣB{Tcuv&+GcA=tAT߱;(=\ ?s=xO|/G_<[}u;aE?;I޴w|W诙7qwZ7cOq,M^Ni{lV¹ǵK xs:ӟȥ~Wo#YᏲ4}s!ǐ'p1 -'rV}V6oGg>?<_sx>pȥ"gEkڏyY|em-/?{dvr1^UNs}mty}u45CO J~5zN9zioƯsgϋeϱtxZ.Q zJK-sEo9ыK/ xm0./4?hˤm.oݙ4}|dc*&BM^ȲWBWOi遱m6[umqK]윗Gߨ4tGݕt;/+GzgAbc}?2|ί ?#ZNԾF{:Ez5w-|:4c=\XGnIrgJC WΨ)>7&fsHO9 Oy5غ~|Ont;zK6䌅.}#|=|YXҥ>!sk|~7tqIgv|0gsVIHqhcޱ;y?h}*yyC0ܟ {9K/&(m&,7趚ֹ+ky9@Q /Þ|?mZvcG.9QRzJkK~o*<3?/6N@=Ezi`.~`'뼏ypq'Ol)?|[zЏ/9q^7+=Ur[Our6=U-ٻf$/=CsAkjh:tyd ?{w_|~z~ћj.7^ERtO"xwsKu~FO]ßex;Ad#~;w/zG==Ə<גU^_5΀5M;m=aޔDžo/>^>6;=]4ӫ_U||Bzcs^;G{'S"|hqybehWrCsXYSQy܁3>>'PM9gO~CAz=v4b]zo?c_BS7}Ό}ܦ=X:mcg@<ϹEU~@pK'='}"95뜋d~ĝ/Mw+1d3hދ~Js9,s2ʮ~uO:Tcl}jc0w-_A+x'|>|kfG~e>!78@[s&/+tSkjBcG9:Yh|+xs샱4D)~=be #m={.|t̫gկ3a>מ1<8?^{ gFxmUwO I| AG5nŞY"}^{zoנS̑S:]|Nx`8Ë~ļtÏПW["g]Cnг'<)'J'xރ~-_YrpgmƛWO~ZbyLsguDy`t+qnY_{:;pdސt&J~!>tʇD߇ކIG.zat~9?.ssD|PMN3c6ߋAjg7>@p?+_}<9w P*p xI#-pb"_ٸWoҜO>l7F~ 95܇yls?w7z C쟵xy:{ BoPyA{@qwV}4W/Ş|MK3T*e9w%}>Z9O'/,h݊sx;aG{Ō-`Nt߫su摞|99:EOR8&rX}2/B|>uq|wcʏީ}qTt%G}k},T`Z_wyޮyޱzA=+⯠Kp{__9o{vaG{W>گسzM{hپZ렻{k.l\oUi4֖OaGz[_.9|<:8{ϥdžMᛵg :6{ FI⽉ƒkCCVy<|oK~K$s(K cp߃3m|}O/N- Y$8Eϐr+c~zCio~нvM|g~]|t"זќ΁=t~Vtoi403 ?yoD8@A}G!G>p#nɻS_^6v: |fO)i<1oͭ;}gȽ߇?+pE"{ 2=%3y.4Z~? <6O~t1[鬵d_o?-&9<G)7 ZzpeA"_z%/~`8o}Ew2?5~!衳˷ZG> {=6 A}Ծ1Z)W1+P?=_ʯ~ʹΓ{P|dI3'_i'uW~󆹩V}3Z]9͖{F9ϋR>g?._"jWg/{Nt>eSrr!m8v!Zl=}ygh~ GqcЫA ೺=ȦB=,/<$ﴤ(WIyv҅z_oS@xNz魏,W&6vvWN=ksȜ@/7mG\(_֑xo?+N5c3ϡ3GSp>bY݇wGқw'?iϽS7|zO_-ߤ=6ϡ}/>okk^w=(Uy}o~>7g?b> }\{i9 诂DgN!Tht)C(dk^:W(<6xz#{yos;ʍ$!|&_ +9QLifi s,Ac# DDw)(oYѡ/wכ(-}ܗ#+ǵ=Wޢz.@g|=4k۱;—C^<|O^=#SK~ }__I[[߅&߄ >筿ǏF}2c#?auFQY/ĺСz)[z*.\>afecӴG~/s<_v^ ~RI^ㅃw^]C9棺wO+P<3ʧb*\}\lS̗xIzÏ\^R{񣢗'ސjɡ_fl9kf_lޡPr&ei9 NPWn+<3tK_pN/$#|߇ýV{7~NUy|[j=|'ρ'u?st>߬xwtznFwNV ?~{CobsD%O1ZA5u}LJR~ CV[)0odl>7a\qgnt.INQ}' g~Sr{{_ ?ʼnCkO/Q1Ӽ[Oȃ_}7 ud'J.Fo~FH9S2ō\0ysc>ɧqޤ3eʃ!ǝ~Srk,]G˾ ]uf寑s@#=XC㰏=kz; V;wtw[ងot;#ࣜ<ݯ7$웈|L#˿~ҧ |;\~pL}5gOQ+p?7LV9 ȉm&YvG~?(Y: ZgU1~ҚGM7O>p3*[k~}x>7zϐБ݋ί o? );P7fw|y1o2F~-n9xIxxUx%ʻvryQ/=ߊߎxpzgC\xrz)#/-tt7.:fM+S'"_X@Dى+':>Ѓb+>ږCgio?ca~j. =^|*33_X_Pf䯠d~fNz$3i}zٲW2Gbμw& ~ 2g].jgzf1nx6ֻDO?q+u4|Ws.6>L6*)Ώ0իģ.͸: 5VC[7|XO*zfz8ȅ7yfis~p3Es{G/q෷DΙ B=?W9! g?㛀w>N:? %?b}.-ꮮ"{\NǾF#7>ᜇ>0=9&wsGM3 ٿd{=!2$|Jo\:ڍH=.g\ytQ;}c΄C^@h<{gӾ6S ?s"W}Z9Zrl<89BNh|쁎MQivptY{ݣÛ >bNç.C.[k#_[;#(?T|9zi^Ё<8RJ}fN}wAs:z_)L͋eKk_FskiBHytNbARNnP<蠘_d>>|>7x7GGZ隆؇s{k|& |њ+UkC9ལ`2|/'_KEW^Hky7P}Rw>+}$LX=¼M=?y. yt/ }ޫ/=5}ux-qC[oClpܻN4ԞGo:ܬ]r  й \8@s~ߡӷȣtp Kj窢{'}z96M|D! g(OȯuG}6Iy"m7~;hJ*|_x?cO t@/v{!YUbt_'^\zE_ry)2o^Cy%oЯ>GW1/P=Z}KџT<{8s Gs"y8wV^vSgN-~&|Ou]-ʦ^x4|$6ўCuu>W>j+OCt}Ӎ]CC\N՝9^?n+ܻ=CӾG^'+}sw֞GPl=kp^g#O *=tqw6WDkK[q@tPWAͅcүqe{ǽ\7zߚt$I+?ϣmK "öL/ >lA{캔}Brmܲ}xr0ycꁏ\M Fz臊'\8F@^,,%. zO[g]s(C3?_ |")o_>QObOyzG~'o'w?7z83~=s8/Ћp"?8[BΓCWϊ~΁#V̽+?ZM>zS7=G~o:t(*z^MhFN/sCnWoVW"W|;=Q#&>o.$S;"=ӷ-6Nz)E?V'59=_  9sC*\xݿ$+]䛢zm7>ʝ;+{s,/\!}#_<p^vwu"$'{$.z'&̬coϢmQ}pcW9wާ7oMI>>'n;w~uyǜNϸ?? tw>T x QŻG]ZK'^ssApb=%m'43~z>I=391 _r>q/{B_Nod>SO=s+O>|yUa^ʟGc_dkUzY']=^6N{~݇=~WB=J>Ur=ŝ╝TxWS7/'5]<>:O$wRn=o:YW|+Gr9:{? 83s^A_\]-!|d#Ogy$GNt?m- ܐrmI^lsǸ} oW)Z''xg`Oao5ECSkBAFsA!t?~xjZ|XeLy=?^Bޜ~nG\up ⾄s|顗!MOy%!nas|r৬/!=Do1o1=Zwk'buĜ}\KkR8r 7ͮs|xM|9ht4o]dgWp?/< zϦ^cWz쑇9%{úE>4oO^I"-ȟR>8kzp>ω(Zt#À}cN`er`~^ɹ9;wеXQ>Tg$^ٰ~]X!9-t_Zq+t'oBbw4^υ0mNM*8V_B-oyFϥy⅚K#w8zGK qx^o}Fw]rY_S%9\6Yͯ獼X~ w堋O ZPz0vSwW#?dk|>䷀x+VI=O\My~/[ukKos9$tKkEoϚ{Zy? p-n>miO'_Zy~7c콖~Bx-j̩E g 3ᬓ읽m^5⫤?4re=g K >|x'wgןG>4Gy/2Z|H!GkCzw:GpǍ߄=T}c߱ߛ \lyᩡI9[N| w>za#/KzSp 2/΁EJǜL9G^aХ)?>ro6~1v֟]j+ g1G0/zʃo]I_F*}}9c^aapfcc/='?/^r*'9{tChCw?^Fyg>aś^>ʦ=)=\ k$9؇ ~ ?<(0?[ 7=.x(Dїj~y~@SEnQ=Dž^ո/ed)K<%$N~FsP%6L]}Gr/gď{Nn$S_6 k)p۔/<8@A@^!t 7M{X' PPQqW;_?yk?ٽ?g3xЏz@nW?:u_c&?Y}`Hc~7#^Ϊ;e2I&@/+(}ľAr[c C1KXQz6?_*](ٗA2ϲ=I[G c:k -RzkG [Ϋ<|lC9\:I GɊ!F9@3UN%OtVySǀGQ!ye:y's񽯡[?'g=&z,l||3 =EΦ?}V} A (}h0XxD;A<cFoeeK{ӻN6ۙBx@ep]S= a|IQ7C:o  97|{G5m'8VTNI:fK&o~ ^W<& x*zUsp/Z8I+}ڗ;7u~6xtʻ'%k xOxW$}!:)S SK#=oCj<{?Rm*Ogi&G{wͅ9zҟG. 45s R$ՕL~=r!,"TQ:_ 6~?ڹγ _ ?9=<'^CtS<"7ĸDiggmih_x(!;t>Un.C4{9e|-l,xht?낾zU姢'?ο{$on ͿPQz q =؟Kߺwd 'ٿy^:`[C/]=8 sʸ+S}z?g{vPO{*C;;_-wpΝ}˵o>%#se}E-?KVy\ keh1ڇ: EwFMy}Q{.G}׃_<0OoçlǦ>iټ)X}Q;g$G_Z5-<:cVG[z6rjI.MgU2=S)LzM+뺗NgGI3"W5|&W~)|^r'Q3(ĵwW#pvszk\zI9̒y}ca{F> #_ }ngZ3: |3z{?$p-ܲ;_M=+܇}z!O1'<^>ǃ=>,B[c5霭+3?k|WQ3#7&+l&'GK/٭6΂ß;y4s>3D'꿇9q*t"}kk{WB9+<{?)ur )ut?ے?^.}+bG/4zWC%=k*/]yoWȒ(z<~{w}[n1?BwtˑَL}'9/5EU6_|y'p to?oQ!+?2^٫%3Ϫu{EǁW#|f!I4Gq_1y/ U3>Ƚ{fo,ywN5}>ESz^+_~f ${z sʏ~>pro'yţk]<^+|=Z!0X{U0c'_iz"g c[d}$:_D@ti8-]𰜗%;.1;ͷt\EZ4 }A~s6 [缡%ؿ؃;=#>${:s:F>y{jg[Ib. r{n\dx 5g$(ʼF;eD~siF>9+z\TOBAwvnG>|Dy#97(l4׮2S)W u!3es {Kk^\gEd)_}RyK1xfs\r~ 3Dϥ|>I_KO9k)O:w8Kno#)S⹉~l~Gз1y<#0=n9t1 ߽;*km_ CG ׄc'~7oů_s'&.Byc^_UWt ':S|x{n2xɏ(-}^8o M<8Mt2yB||oO%~"_^v/{S{4?SfǹT_{uoهr_~N|˾Z^^h y_( =s&"fc3"O>5h?YW8KT O{>s{|t4zKߓszV;a{^rm({ -9=z1(o)x s:}p: '?'>r1Sǩבs8RK+S!*SQOs~y8C3vwTK )_Ty foh c#:稷ta/xo$6zWo;XsK`ފ^ji@ڸQG ЯRlʏo}5xO1Gѻf(7.ռE(^p~=S]}:y3~|lb~\@>=:g+_ĹCƩc!oDܪTxtoKo͐cL}'Oe}- g4DΕ\7*NJr,] ʩkJ_ ]$|4>Jhe=#fis_tҽ:p.G֖ϑ{:sVk<ǒ  @^彑78S:AsG9%IkUsk!:hhH{}{[\ҏzrу?D}9ݻ+|)gOaCoH?Bx18;r7/|IYvz^ ~͔ =OO1_'E7/|۬ 7 @L5"Ŀ/=O~\G:l|OÑO3^ݜ=܇IЧϓs/}rO?GpO{ \y?e'Cz= ͚oB80b|T с rM?sUx 1Ds/&W_ $!th,ad頻K?}>\j\÷ 1lUo k1/N!wNSO<[S[|#%Y:›uӋ \ۡ+z_HyrѺ-42G(Eϙ&ø:>v_&;z{-򷢧$qugPzm׈:irƩp:>)X'O4pW\g7>8xO<yO&GqΩ)󐹜U JΙEyxi ^*!=Z쉜w1ρK3DOss \A|( 'hmqqOJۜo=`mc#ڸ:ȍ \ϸ0yvC =K}8OvrsB`6ROöYI:} 8vNr˦\΀|;lHOMs!Cܗ͋7|Oy|d99z^6qܚW:诘7zʏiqIwnE/{.9r?xFMq= WG_st`_y搷ϧVIs\jqɕ$Q3q?ϳn詒nV=ToeC*#oЏ.~y}Y!ҙV9|acӛUK#0k9絔t ?|+}LNn&LO$w_}usۿ^ٿZ _ O!~AnY?g̻{c=4< +Nz*g+ox5 ?_>|; 9O3myѷ:a{22g+@ {NWśK=arg ݹൟҟ۟bwBK>跶bI>aQcCu΀~`(%V~~g<:wTwz]UU3+iѿ0l*v"~ |e}avvoZ2cŹy A>wt 9fyٞFnս S|+_5oP7:«o?<6xg9jF9+g&]#onߙ|+sg~8#9֡z蛺h?Wsk={" ^|G|zok Wwv/ɯ/']u =γ ގzUtܓgyGNhdwSqgyO^=\6Q˟G~cQBq}lv)R>L<d+:jtY0co|:򹝻x/ϗz_u bb&ς"I?ss_"+nܐ| ɯ/ߢ }'7dyY^}98TMP~PNn@sK-W$:?_DχXڧݘz` B/M}@CGO@/_~t<(o}gse58m{㷔 k y%u~\<9Hҝr4~?prǞJQ\7~яDoswi$vis^Nzh?.er^:foq|l8Wбs~84c]/pӜ_.6s>(357o:›ϒl}d< ['?=/}WxW2nYtëP~ ҞAѼ WѾrͯ&1YNuq[p:9C׵r]_p5^ N0{MևXuŭzVz7?tWCI_'dU yszn/y2}U{|q9|^iqoM\;>Auoz_A~0:u'C,|Vt]7q_ (,&FҫGg|)+?/&].+sO,#ǒszxї|z̑5}yNo|壳SO=u O8QsuGh#fxarm *(r=?EK}9_E)S o]=4V$Q\no8"O9`t[ׇ /S]~N*h{;3sICS|,}#S[^<;fwndW7V5t..z 1>ϰϑ7pVhwpUkf-|I9n&]x)A'*l||Ğ˾)T 5' t: 3z ;~῏(8[1BT~tzw0Zb_NjUzѝ}g|јsMEGNq)z<ȋ+crՏ7TT¹6xsq|x=FoI9|<ȏ+m!$OMJ PUtS|aeԹj\IvƉIwLzB;@|^%E/9RbI\u.Knͯ/_KKyd/e";zڍVK{w_-/^gë1;މ^x˶?waF@?,yN.Uq[;0?=m俴N )/~q3}|䁣kOy|?.̼ '?2zO_9oOg#c9nh^WZXZ]x?fn=Og;ǾWCEҩGߒZ#Ϙ+/SћZxkuaػߜm+>xr*w9~*>ƃ!غt÷ f̯k} N}` ޼L!_b=u8/xwsT2|:y3suxA7n{;엑{ ])sC&]?:3|W"YD]OCOӣ_-Oϥ9e/O׾R;9h{ai,M"j|68蕚 ÷o&|DGN z懞_K-*5AEYqo~RTΛSs,86G}t9HI~vnlWq+ЃqJf,0Бydݫ'l'⼬+\'٤ψNzϻ79.yW9-u^q]!'1W:trj|>c~Yɍ#yw}~|d~GO|Z|r5&n7ƗO<#\D|^9gOly^G,+x@1'_߆S^ys/Nyo/ȷfG!O=tuN SL [j[+ _T:I|_8/+\F}y}C7l#/c(z{q=3o[I*;w@0q)Oɱ"o$:U~Vr39טsbOAW]V+pQ6>"a0v.G䰛"\g3sOGa2͍k-)O(ofS.9<7ENs읦y {x>Q^): S+*حUgF|k }_|OCGrqb>,4{$?|3<|s}yOyC*pt٘sW)N)j_kO.4g_\N?ov^tvq #^}qd?~Sy|hO2z Iyw c8)ͧ܅)]sn||ܷvo|Vk럙p.Yڵ[?]s q?^<@@!{(H~ MzOυϤwt݆zW%AoW?HoEk'LxnYynGFrI/y|1]KQCWkhjr܉zr?oާ]޵1#Oj] d9+n?_ bo$w~җD~/(wO.%G:?nN?([zy|w7kO|l){y?bS͙VnM|a'ǑZݽqyx6Y+⋯^<ǜw~{^\Wx'Hx G'8a99kGyL_y<יcz:7zWSފ1+g?|~VIs8qvEnY:Bq?ټk?B/G@\f wOs<%H/)/si97؇wjss6<)$ϡտؗj_m_[̡$=~?Oνr£B1xijfK^<ʛ9+䁵ޛW3?8mtBZ7КiD{}ぼOg^$(y#VOgG&Z^nO#z4ɇm|/ea*ݮ})_F(">/yx:~8J܌m8U‰8'۽mԋz]8o*&:d|7RkvSi_eW n[zCCJOշP_kBn| >gx ֩>>wK+9"7%0}B鵍o7osnLSgtûKo{3sSCK} q9*ɩgh 7[>-Ga7"Ϛ&2p _ލystZ#K? T9B~Sa$.xo_y5CA#y[^U[K .^q/xxvo3Gpi#-|_<9_ ?|: M Opߤptx_ ߗG뾴 ^}{/=[͝t"K㶐NRvv8t{>߳goK0gCO4{nu ɯJTg~/rGs~jQ9_ Mrq9Fyzh@{XA#w/"Ix9[З3ƾcs1ygkE[yI7!ÄR1O_9z1 #[ O,\OtwzQG=Oy}sГWdߊ0H̽ O{=Qd_G7Y+7_Oހ#pyi|OOG~>E'2$9 K]1_W~.siGpC)}k/~#C@:|Eyj,k ssqz8O߸quݪ\|0:ϽlF`VS>>ݘ3Kю $zj=o룻di'ƽ̽LLky <937uϢ<%Gerw9_=e928rSFq'9gA  oy9}/O yKKV#}A{GA]Dӊo|>p |ܷƐ ߏ9|$z:+ rN'?JNw|؇4Oթ 'b/FG ~y^\h=#>¥%<K|e7ҳ"B' m~_~n8ͫf^  2O<'"7 ?kVޝM9cY߰kc~f{9V/݀;A I'\r#l;aVQ\Ÿinx}3D)W9:rGȭ=H׭{1w718 Cg#|=X_98[opH' 3caȇ%̸K 9tN9Eu)K^ihSNҸWN\|{ ?1%78Fu~(ѯ1ށlyK~~}M&f~<[?L5|V{ʱgFs C9񽡷KolcܼG W[aw'EGcEb?AKwSYX>Oƭ;xrg3kJጕghдO?AlLC / j^t69=$o}c>Wes_uYGS7<|ه3T?m{79y@V|\<(rފW(]$}c隢>Pry._KP` YkEzo|$zy{k?/vא Ϟ2 S\d© OAo^[]R}rO_;"2G\}/}ّcF'R>g>{  %y<-FzO>sk<%#}{Vn[Q>C;xٞe]"19%/yBCϗ:W{yM/d|{.rȓN}y>7L9~VOi\gw7jQ8\G_sI?0y|պ\ Ia~tI\:'8!0;[ه a^'.z4/)tC'FO~3 o#} |MwdģΓO]hweҽ~Μ_gw9ؼ :fI0p(xc:jϙWk| Ow.0quOi';tw=~pAz+?ze ^Ys*5Xghy7у78g^\~AG٧,o*zg4~8%zI迪ϙн}vfc޼21|sPU=Gs:%ϑAy1u_Dt4>t>SwE:k=lLo<~fxN=яM?ppF /#lػa+בfcѿL::Ao! 4y"/w}~^};H3ߡ&|:Sƥ_+pv9љ_<}g׮,؄x;Wt;X}"~~9gtFy׼xy^>c^qZ<'s~2WCyoy^'O}tw_"ڼ|_-$ sYٖf=A:sj9W{[ӾByt<;}O~{VeËW^?>kC7߳|V^S~sTj}? |rnjٯB?#(bD/"R= p.eMxPyO|KULϐOퟻ2ޣ{Op)r¼ZcEBb0?y|;OPowcYrA <?'\Por=;߀>{r$˫(>s]\D2='/ѧO s-셝G}F'otƋ=}ۍ܁9 ?j=`] ) W#u,=>};G \,r?)_t/NF/~NperɿaS :z;whgہxEMN@g}G&>~p G ?هxV9ܗĵ+wk1o uѓC7x>Lպٻ9,68/-toJA[2xu_.~x3G> y%w5?>< }# sإy$?כi@ĝRifs\[(Ǚ[twS/ s@o C=WHy: ѣ⿏`PگO>F>u,.^<[ozޞhnw_4ͳw~8;z=7W } Yԧ׽Sz*A@W:_JU* 8J3s'|?=yGq3s9$=cr#zXt Xؚz'44CLys{zF'k[_hSS9">c|@=GO|=݋?tvk)W 38|Ok999Gj=s~} q$AW)| ' ~0NIk}\_Gf ZYo {Py1W䥑+W:SVc {>|\yYK~,n x_kppV/I+53灎=.=0gzG/?#3,:sʇy#NJ?Vcu#G~nΡ>nA΄OE |u}3/{< {<+YYY;ͬ_%7wQ{9@Uw7W]77J1ylW{Jx=Oc o߲51WVPyc~ΐ=9Hg[GOQo=/u_}7I=np<%?$ 'tqߩs>E}>8:找_ɏ唣֞J}:{ my=O͗b\"u(+wwv?sm<鵨̰`׫Ljgz! ZG0?M`{:/c =5c{/š)!%Z ~XzxiNz ϳt/&uȣg"g>R@E7[j4Gfko.\hU|N䂢hvyx/9y)ɯ>ߟnާ?< s=[ތ'W^G>P=kxE.|$|3_i] -9<:^ڇMJZ5Eʩ"}`ʛ7P}پrN aw1cw]'ᇑ?^9mCj+rZBu/ʃl~9/]yѫ_ϫ 䁣pί[9W934nyr'Zys8g>_wIm<[r/~io觵/ofi@o@y9sik{}?~TwrSZ"7Gmk8}N¡:h>׉ ң?`$s!/;>ԞXێ"\x:O/g~&щGYM8mkߦΤJ~d彼WZ|#}J9y3`?)"] +)r8$ c*(k1VgD@þJ~><ȥ.鼒|oy'} ܗe+N3&8yUWaZ$ws.נ'C?y.Qu^Ak=Ga9Əy_x~ǿ.}{)7sy>Kч1x|B>]Insvc%Awo|5s:S>tr9O s@|FoK''ϖQᔔ[oGLG1c\`-BsrJW:xT~q»C/[/؞W}]+Db= ~ÜWRSu+ٗ7D%O(}7𢇾~}m_;[Ƶ9{+ǚ\+uOfQ_"*Ρց{cjOqgC÷{4sFL}_At"?HDR6Ss;zrl]|ݺsw`^[=΁}B(cBl4!@ǹXz+ŗgwZ?;D'LN!(+ a&Mу?~*t_L!}~/Po &`BI&zCpl_%leJ9>^"(~"wyJS_;#gRn'1gT8T,%m12ǥ4wU6pc^[x)5zLgɍ g>w gj>f6o@O >oG4>H:**?~zt/ `C_[Z>|cE/'c5WRXA}mÏ?YtGB/\=VM%T[r˗ ˟Vp$R>U{o?_׼s~:r<\;K~9K <6yY?t#}?=!njNΗvzwtj6g5f7f<|< ?y Y'Wpvmm7:CETwML\|r=:iY%r5 Oxj8u=)ϻO$t8/ ] ]5m x=dp^=} [[¼[O5|7zk>#cf#Ky[Kw˾J_# WSHO:ti7Ͼ>>EڗO?XOb~:p l_X.k=AMuL^)wS}GwIg+.짹| Q?xS7J Lݬ]Ty^aۦ%z'>*g߇tїc0?@\6y}I>hAṠ皏<:q8 z>lr\#w<.}I'oӹ~мs~ᒣa%ȇFI^sc>%'ɛ@zs̷U{o"w?m{/6is|vY~@|ψ\UԻ ˜cWwrzl~С{[Ͽz^ć7^So]uwJ7Ӏg|/ K\!ٖS^rz7 M珰O!{/z\#rO@;Ir70NFw}WuES>wYP5i'>vr=_iA֜6_ <*NuΓϞ}|)L[{/x_lEϺqȉi]zN#ۺ F>u}nj_IN}. CzwuoWғf=D:OzJgWX>_[sUsO̅<1ujz]b.l\-{yqhb _g 9⵳IUb~V>y;wī'W?69.)<;pxOD5fDvmP8b>Et:;t-\6Y>G^Jh7v^s(p:9=n<7A$نW=7\\Bs4ۍn4<ߢTKu{zsRxq{E>"8<6ok.~xW>={>-zqWO?yw^<~|EsÐs=5 O,(]>sbqY/sOH/Gֺ7b9 ˈ'AWDO-ڕ5'||'PO)zvJvI1q󝯷Wφ}.9|E9ɺ{^?Ϳ_ZM: dž|^Y||rre|V龋;c>R?98ŷXX9'L/_1¯>V%_sp|VQ~W$__v:V~—g+JC8|Qw;.-8z%k֯gu4sOEys199eg7 cL=%M-Rҙq^qGaO bE)CsecVc!fuաk}G˹Od Ϙob~Lq BW6mp.|]V_B|(GO_\163gdz%;yAO9xo_W~k?y#:g/ _Yu{Άb f^򭢧b~/ފl=t|~G/l/k/iE'9N.|tqO|zY=U> }IsFqk+L| ?9?+;叀bg0<+HܢK| N4|~;tj(%raHŎ^47= Gr;7 ~\;X/u{-—gͺ?Oމ,Eo+[7w{'ӟ~x!|"3Cݮpip^ ?W^N={Rw <{Go>ڣF.vީwrDnOWО|}fnt[ѭ2Ԏ~GП9 ^?F4pw!/5[P>ѿ>K~ޮGںW4ΐO>{\8;}ܟg]Up:7|ݡv>dA$ו<;|4; B?žMo2=LErAoAD9ꕚCJRu~WG%G~ߟƔA~U8S" @liZ]s/8@օVGo%}8x6 K}7ȗv~WmWr#';QUG C^~!ѯ [*?r}{ {@N><yh5'=4rY៌4ѓ^x|Yiy3yL 91suKyrh{~5|~qxp$ݭ8lrQ1],[GվVERՂo鸯{|?XknO8ή{z;"}r=&ս_suxj56n_ȼ-9N̙BMc5G=% ~%L>;|ACh;Op{K53po"n_m‹W2<ޛ|cwG'F}@NQy@KO.XG ??\s=&:.U]Q:||'"_$\yÚwq <-{%~ƙf߽' Wsb/?g}ç{ăf d^z4u{*~I7n ޚvw-#)#:Ͻy>rc.|o{- |z)qo+BO\=>|E,<*ĿO<)n1|<sYLzSWma ߇}@'$rRzɏ'Үjw ̿jcޱ9y;x?5䦡덼S;|(+P8z-L} k/%t;~=|"׾YJ ~E;f[M^Quo{:|q̷7|VmNM{ 1P+硼?O{%@ʏxչ{i_>/v|[9{#xA("Os23yM֘[}5㼏;~zjso>V+SI kg{n9m{kxѷї<ԝVy$D{nAr T,zo#Wd; ޛvx$tXn gCBs.u˸Gg.'ȡޟN|ON>yVS˿.5 s+o YQ9^dm܇K?}_eS ?2M>W硾wwܢŁ}7Vo(*Մ{r֮TP򨊏 7oB9ӧXC>7|C'O<\m@L1w·UswܾWbdhԅtӇ-le~ p~Q—#?~ٵzK1r8|kD9~5>;ޛSy5|x kI]F/"2xȧq.x/N体GDx㶲^`᷅EutŞx܁3W?m" 5N"?wFkfsv'S)xS>Oz·{ ;!W?2 ~tGg=YB8K$PKz"EG]r׼{Dzs.~=s#9a/V~? ߩ_ӄ'FKBه.M#/m_\zS1/Bϋ.SW:OW{h^{ȗ G!9phvU"|{u3L|=Z\CWpzwG"}osom?saok?^0Oޕ} <|ysۓ=f徘 ]i'9_| قϱsT]r~# /[}o,BzՉr kolg%|=|uQq\I lܻpέ;~>3|s_}meBgIP}&gxAysrΏs{_glgNk|i: D@o=yN=2};7ʟ~i.>#+%1򇝧yA"x}uz/-d%})L?ШAgwg-+{&o2y\ 6s~= z1>s}EЩõoo܉_:f7Gԝ짘Os^o|t>Лs5|1.'ꋄ_;[H䶒?Ὓߛ~~\2ae_E[Q =+F?* s?.ij~\|c(~ zS6Uy×d7\1{O^Ç ^TycR%0KRŵ}ogc"^rYF <6x_WQL딯 |QK:7q{,,·ϣ|xIky/˄>nx~FN?D'Ml1\Z/c%O޾ttwy3u^? uL>^~f*7grNBiߔxΛ3ғ6@k=[G]}a!N9/K(CWzwDWn^q~ |헔K^<s.98C6 ̿ /Gq9tե53ǴN}l ==dz# ]QtQgk=Bx/O;~.5s|>WOKc}Abʗx2?^ՙAI}~tG!"yC#W&2҃PH|rg3oys'3f9zDЁE[\{D{L>g (ln^e5GGA~}cེ/ɹ.<\9ryO<ky(|Q_GI>W#7p4Bƙ)W9jOoO&*tH? ~{COb]4~sMQf]q ߯?Htj;ϐUj;{?y\7\45Es/{$V^Xg|Vi:4ѽ \]G>[=p|o"st\<ق 6YżЭ19_ sf{sܽ=1З.!;sN ^_5<9?;$?sA&%??x~-kDqF|V4_ykh΂Wa.Ds @.)=8GgSy 9ȹ>Ζz,jsVr6ּX9!7uI#0{VyM9=mt3sϗ=cůF-93_YY[R^7{02qPʣl4t4:^Z;h#JK'9\coV9|l!~}״o{>I5}Oїp^EuQ4gT97' fϟ1/szEvaђξ߼_ t~/tV7EMm;5N*= zs>՜O>`[\ywd7||8^ =*JoJWY6a9o7$,j'Y.I_O֜o\˼-М;ߜ|`95}<&tO|K-[|Ȁ^4 ~IKu/{[#[2M_L~c)^cj˹¸= 1'Sb|Y{+zeNw_N">l{2 8}i1;ud7Wb~7WT)Q?Gc~y||KA?}or1 =OKНe%> 5Ra,zwBx7t|v~z 7o%]+E{>{5nz5yƝh}SG/7ɼGՋ?_miѹ ?<]S6j$/N%?k|g{k72ܝsLMm<\%zфh̘7K^=lJRM d~D>EUDUT՟Cwڃ/W[˾^?I3pW=I^-~ U'-<\sHrIW]"~'}+s$zq$ YQ=49O7r&}cŷ1eptt/*/]Fvrgk÷>/wڲ'/ëI]d}6{K׸s^mG;zo-^+|>.+d+?:܋kܭumޓrᴝSP̯~g pgf\%=M[ Ƿn<>jut>A~EϚ[nи?z ?%|W 7~@x㽝?R;Rs $sۤw)Op%7ݮ[r}'|?0r|7Ͻ}9|12}. 6.mzOgb889~}Qȣ^9tM;~~Fs;Cړ_(s䝓KO9t/W7ppS3gS㜫|)#3HNXjr7j |u>;} K9H @ ? w>9|VrG禥nZ?ZL,;Ҿ}1O: x^{=s`?=A+PR?xfY)?x} npxWn1> 0?? =KҍC{6=zݛ6 ß:ݘ,3(Wg3*><p™1ٯSGzjwh"=9 TGug|{B[` `}tYF>|2M''}1LјKu>A9ь|j̣x4|֙kt$|_"|5=Md-2q-ޛ⛢9ȈY\<-:tKAG 8GGWdޯp[3#LsZHΪc%Comoe[UmJToz4_׺DZ.˜JxSv RuV6;KrWqޒ]O?E^ֳ>8y19]ۙCɥm4Vܓ!2r%yNg䡷_LQX@*d9x`Mםԯ~/+tq|!xM{zz_σO(D|){o\pD qUfF{O轮(o9nc> _;KlWY6ԟ1sD>JH'|VrV־=793y6LNz)}#G ?[zGȷy~7Cm|uhݳik=ᣄ6\R>Ngs^\Ye=ť n $727R?G2\irrsttZ3<=з'_4{o]M'  υׄyb<EEth\Oܜ)7rt:?/>%!/ow,]6u>wɅ$s`w'7D.M>I_B_aOϦ} y'9գC'9ZP̻~Q{DFk1^S}<_6O i/F#\˧96CC>nm'[x?&}#ȯ\ǹS>g,{s;GD؛#1_7<&H7PI+)zsEtcdVr_C~^O.@Se _raӯJ#>灏>CwSDr^_}ynjǝ##> %p#q^{#{=Ʒunʩ܍ZG}hP"9r5V^Oe!<4g+fu8bQ ApAQ;6~T$z N]=V;}GefoJ Y˛KRk\V͜Y~E"GdY~Qp «GW&rGXBO;yPx:gFJ-ެ5bkڇǥs0|/s—=iKBw 'xNbNleFCW#5Y{?Yll>4W4v nmt`]5;q۝^T\}to]C|ӭ _}\`NCV?a?*z_[-9%pwϝo"+|"} >72~jy߮sClE“HZo3~|VكKߏʮݼz}Oהސσ=!l>vogE^U'Гg;|9ЧSejhv'f`Nmb$_9q{_)$~~x\rCϖ{w LJ@+WAMnpmxY(xҡegGPS> ɩ?ްJ/9 =uqK3~ttoo+z ~^<ú.G" _oH{zз9-|kŹ/Ź`9?V^ǡt^Խ}ў=X{ϣagOzo!z6/ kn._w|;%$/^ƽ9}P?MI'S^gMxlSվovQXr'~ ^QC+ szE.7O>'&],/!= ᯡWަY# .F/fUw|>SvDzǧN{C࿿fO`^;5=Qxz~Zͪ/π>d%)q g&%ɓv"|Tjm?2o?ܫFGL&Q;Gӂw^xW}coby^Ԟ̹#_`}R?7̟u Ň˹\<3yB~L/?-/m"f~ٳ^CZ=hu\'SG?3tȭϹzudMBG:e+Oo!Tn B O"oc}K,%̥snu7(6n<=>OS*GF_O||\}Xf?COyx0f7IKd!Goy9cGo.z:ONgGsvW87r^摚xN{b=TvF'O2$zc?fsBOɿG?],n_'>{C^ONqioh\=s $lxЧ~2zgQ3{LoS?B+-#MʲKt k^K#{/O:e {Gv>=zւ5s>6aG ڹFfɣ_󽵮rq0:9=C^XLy?yYEs|Uoo=\/z㩟|n!|D7ϟ{>J.U>ֹm']?z^O:q1܃|.Ћ` {#:*?H r;s^3睅_,V1g{DM$~=VR/Bǁ ܏codnF.S^E_v{r 7±gGOHzO3.8:23Zg19k⧉|Rb^<1Oa0w!Jo@|sWkmͬ/6|.Leҩ 4'Pu_~q΍+7Fz}`a>wcH?o>17&W >ḷYG-KxckZmw[ys޶_y繣g(A3&х/->9b>|Μu2O) @gZwsK)jvۛG}K"wpj|G^c^Vk*f+}V}xsأ0o3뷭+G93ca~e_?;uKGlsr'*9)}7Iágtxw< O\Чx٫> {Ycܫ MK=o^Q9"wֽͽʯaUm:*0 ܜ*GUCh aݝ:^*|#z8Ks\0]wsᖅ}҇~`EWWd|ydJOh3;I9*p׋zґڟ;e?geoBOlط(%tNDjt.=I~/(?m4W2_G :zW {_I<}s޲.>v&h\9 Ë|Z8ʻ~ϭů73C..Dmӭtg2pڑ <^uWyƳ?g)/>~IF?LֳCWW=yG;k8?<1u? /=| Fu=~{{ WćLOTgV늞b%`- 1x1?G7#y؏:9/E?UC|Kp|s2X!W%NO o=z 5t%ǔ{1 _Z]:\1829rx1KN{dxXw6Qۅ9^*ƞ'i_zC5Zj/ޠCuP8^Cg;=ƹBy[31Ŝ9'Χ]s| ' |Mw/l[;Ѕ.NFi/-b=gE[_ྫpoE~sJ>swrOe|?z_AQR@/loz#s߃ 9)LxBsL<0bEf7D.sѿVN^I򵙏"ozރE{XݷK:rwaߨ,UH`K*L*:壂"7(:w$7?HݕK=zG>_ܫiwqr63"%r9ݣ>ѸSFN|ot.D)Q'X?g|0s۵1ǣG ނ޼Ö}%`CuQt>wyGw%}"<syjn̕y/׼}ЛEeC)=ļ>NS s"zz"w!`πWc@NBj ȏ{E{ }e8t;'>^q|:17^BtO{d~it:f^=i4nB}}ygDnQ[ƞxָu$6}|6c4v{ ݨ՝+=[+ν > gOR{{?_(|lLNs__z'kr~ Cmr';+L/~CpMMF|zrHV.+"'=ϑdB&ѹC~ 9 k ay}КT9.N}.6A:_cӋ9zXN /Xz$roxOKzL|tǼo^rq񽡋o8Qa}w+T?ҽ_hץϷ>*t>7}Ӟx 8#/<9:PpOEry;># r(')#0Ǣ6W8Wu)##/Ӎ_Z|>:҃G90~mF1f%{[3ϻuG跓'\ekgA+炎e܁ꡌgKHRoQȍ.Ωύa>olH?s4{gߊ߄?#}.IOI^r?aCwEY =q3m%%| ?t{%:I򿲥#W'wz}GϜ=5yҳG߅N*z\OQO!}t^+ ]J 7yJz7+ѫ/_3:6$Grj7W7t}z/sT){>-kK\8mv!rK 3ψ{'JT ȳ魥>>^3N<Ʀ3~O\si_s =E!o>7z\ڧ&zqOzϡ? 3z=E/Cc꿶m[GRIE>&O컙W }Q+,,>s:JӞLH1NR.Dx'zoȣV^K xL|8pEʿ/M^ Vޛ֋ʩI}o^YZJ+|r)+xW}[ܖL 0EKga~ ܧTbd@ap7=C8_됫Ay_ c"'zMɋF̩'݋cl!ma?⼃3޽RK7GG {+ACWw~?\=zyes|d}EZשZs{7R;_orp!9g%/;}sNBc'=O~#t/7 FO@[ r꽆_Oݫn:V|υ'rq:F}|;˟R*/sO_+<."g}k|~|ܡrpSU?8<%.OʾG8@W>WbUhyH@ڿ}_,p1nqF DLwϣ|.ߐ?)⿉߉O3pY)N7z)̛}&rozEzqѷܡS_ܧ[VUNA,&}^9H5<_-IǺ}S)_U"7wt.4küغQyHKe7_n3|sŏ=փǞ/7_4@}])yݧ|$C$>zGv߿@z9yW*IϼI[c?i*?܎z%|[Hs6x@m=՚铍XYvȯǷWdOZK^˸ro ̣իp$~9T)&ɑ(sy*BP:CpKD7#׹Z]/ +sHgjS(EFO^"]}ϐϏip(-/_@37_oYg~:Wx޳Ժ;ŗB=Vsi>=[{OcE }1C|(rd]*ǜb2>)g1>wIZUSX>ދlF:O~ {j!Cx{oTqrEN姅O?usAb+r U1|>э+so# s O9%͍H']\O\E~#t_l|n '?zKC\\9÷>矣 Ok(skz_~y|2пk*8xaưH8ygZɁy.m?3~pq^Lh]SΉ~\^?o^5wyH>{~~`Ozsxɓ *5=*O*;W~9[˼o4s[GI?V=|:&k^17rb21b1Md{C@^oQi>dI9ߖ}Sˏw#Wȗ`Υ,;t~?zȟ%71Y.zsHC>f_WREZ鯈 ȁ+po2Wҗ~e<>ԛGI%<|p\nqd in$9_[^IudR^| >ٕѿJ:Cg|y>Q9$90޸Nxceߣ_}௜{ :_F $to7:uЗ3?/WE4=7}zfzG Bs"gurU[-|;CO9'r=_3ʑ>jӟލ^#3f/:3.pUQ5VNs/Z_潃C;xmO:أ̛WBT~3vKi97 tD7眹=fHg]}vtovR:8rRɁ&\}LZRq>F7l}{s7rV)эuS.-/OlxW%rKuD//:Nr.0;wܳ3TCy~w Yo}rJkƪ_s<7dT8&tMx`;k}_ ߈]g!<|$|)c.g`~~>v`ۼh##:0zb'7wz8E:Hx‰Ko)&9'oΕΖ vpNsuS_>X\"oyM/8~*@Ă;gF.{FU@쟜̇ xdgGto}prlH5r*9x޸ܸ; /\^sDyL'D?yK>Nr<)Dŭ^:z/"'GǦA{=ޔޑq_)Y1pC ퟤ< 7Iѿ^tŇ؜[wh㾼'Sg߃%o_~/ܞO4J)߾->.w߫z:J>|g?s_9 Eπ߱ȝ1o-'1:aFKB~{xwm/Re9K.v5}t~cSO8o ~Wr0K͘%JQokY.i߫b:J+&Wds17sM) .UoD3{$=8/K"zw+Ç8.x}7̾_=ƞ;p06fSHQ}॑~u!Ҵ'<\^}==scauߊMɏ _)?98y%Q/߁~Os:8ߵ~{=RNGyj<]&9/,@//{vc.R8>ry3Ǔ3~W{Q/.3zᣲ1eFWPgEԾyB̠|0|2a#wRW"=o~c z/N?s5ЯUro77wT:=> ~CO~!7[A9wd%$}(-ܽ?&Zϑ{ssi8:{oN9z/wFisΛ;r|G!g+ϣp'x\r E.=#:eS-*9=6wު81zȧg9ߘy s3=u}}=7u?-ޏLz“jG ZGϹρ!Aف_AGKcӓ_eq!'8ơ{Eȕ'MxvVV^K{I{g;_>s : Kdڇ;7$'pكy0_ޯk9n|G;W׾EtEgtܯCH YzU:_QV~SQk3yӣC7 qVwT5VrI+''UK:wlyIg1:ā{9߮qgľ)>#Spx76|_կ~O޺yg?&_-DwQ-<ޙ/oelB(o}+Ǖ3n ]=0i.Oc= ywDl˂'N.,f7Լ~~uou[[9A'.=|Qlj3X>Գ/ݹN:o ^=z?_e/ (.z0tN /;7VrOCɏ\odVֵ_2 K! .sqptZ+'E~?=}r}vtwȺ;:ǓK$bv8?‘_{LpZt3#'ǁg-tח_>z3kGtо]8?sZ*[ <縿6D{;>|5_p9+e32'mIݻ?;z졜}tE ֝/|)]o~p?py>>tN\s`Lr`mNR' ϫ5&Q=,Vyw䒰qU ݅W'3,@1|9?eEN|Yg/%tKmx#z|=9<>X_M( |̳oNO>xYr''YWtT6EoM# 6FvG񽅏{J͝Khy"W n⻹G[zx6HYdg:ƽ5}#\Q9kcSZW.ѕ6 Ό*F rv>JgC<^c5g'$dw)gsw么^5YN/6zh+S9ї7OS7i{SE?~>?^R};fo)"~W~|9{YG|~ߨ =/|+w tͽ}埠߇ E]:_gS叻w>ȑEmËu{&+9WKwokx:kd,me kOp"πȓ#|g"QdKKo^Y[??w+G {i<tJx8xa|P]գNY\H[X>? ټOs??wt6. ׭)LW?9opQ1V# GC^^ymϕԏq=ޮ i%o ~甼5܋/OI}ΛI vLYV?ŸbsV]#=9y)cȅsm>q1_s~+޳̣_z]S}, /G?Mz7>9+ꞥ~Hz4r^# CX95#=#oΥ?!Κ3 uٟ=/]s*~̝T> 7Gu_su1S<72k G<,f>w)w=|>rwepss? 4O_ackޏ~7O+7l4o^ "7aDyܡ>;GWrMB?o|;FoiBsw{S>>e*rNcN(ܡ\k?_çx~~rr}n{+6g\z%]bt)%L~/Vc( }?{zSӿN.k?/S!|}Zpur[y>~oF;>ft9EN,z6їV,6>=?3rӬcOF]t0[Nnpt2bljG /8Ǚof3l뿥{  3/B2SwG _xf7tŝ{<s>mB{ݖ؏D#9sAk<\66z536#{(`BrtN>N?|~Z#'x-dr/Ɂ9f pfߥ8ƅρǂ "§?|qo'Neַ=dzO]OZvoV+( ܹ3a0G_rYW^w?oYEOzg|y̶aO9Q[_ _cw?>Wp4Lws?D =[/ռ>o 8E_q~zGcxs\$Cܟj]G}\#)|EߚAc5 -iݹ{| O??r@;F>+¯dxȇ$hvIK+וּu9x'wbdE<' 1uw CK7y7=C7'OoʹN|VzGU?=+ɷ~~?| p5җ~%SSpl= Ə2}דA*E~>t pF?.\^,_?9DsI7^1"ǣwMuO_PP{o }*ׅT= 4_xx|Ri:tw1'E4kx@~µGQs>+o$/G g7cI 綾\bE_EzSk̷ \zqN!>q_{1~;t_;HsD==:Ǧ?>M#2o37'׽E7 dO-ٻ +II+}&O{d *87b^|>w/CBV.O8Szo+r =:EWk/} { rN+y8'§Du=ț/<2s߯{s9}"z@yoP-ŏo<8tёww󘿾M>?:O=bgC7?ԿHns"Ǽ}p pO8̧q{~eBg<i<폝դw/=NCgjp4u=ѿ~@:' K'TfeG6o/^:z9g㟷/QO?7wszJsgFPwy!}??ܠO_>QtQ |9kOnƺ Љ.mϼT?^d›wݸl9u_7I-!}QqA֧%^v9eyZە8$1s#,:ˤ?+;98y}z{׹/*rע~X-.p 7}UOzyŕ4F/G{ʫHoS9_Cxx^|F?軣笨}x1*FT>S}EWK̯?.`܆/xcە/GDo~cK9gV\J%=/{.I'ONNmoO:>zxiGkМĹ[R ta`Υ$9ӣ|gg:IoGLwAw' "U g_Kmd?sr@{У\DK٦3g$a3ɥ"W|˨O_Ѓ|ij~=0g5z޸_%wF/G#opt++gtOї >{ {Sq_w3/BH>}}qgw7GBh6vm_P4O{>]]{o~#"< 7Vڳ:k0y]|͝7F?ϗ?P_G&3yd '9;rF9BCutBB> :J:dr;Cn[4Yл7~W2~̓%ÄjK1?4ӹ1crp_Ov_Y^ =Gz8ow{a=g~uϹ1FڽS|_upȁgNFZĺ\>*e.EN<$+;ѧ霉?[Fj+ׇ|Z~t#P#_ߧoD7s{XL< L|!B=>s.IxIy3kFxL"o^~_|𘭫?s~! gסk7a[Oc 8]ƭӃ^=˞?O5^N^sN|4x~/Gy qvÏ,>CO.<`xߏEx|:2g9dC3^\LigaHz9Q/l~=)??p' vF+#wS^ӽDNy'޷bٷ./^E~,[9w߄gK9N%}p[!w}^䧑Ko,sVV_Bӻo%5y7V^]W~}+ϡy*?IGy Gy97:L>|ŐV7NG\iTc;usIz휥V엒^6u \~U1(^ܣY|w pm%-==跑^U`{֢_^YlV}#wov}O{/gM<[ { <$;ۣ}$N%G}Unnhn/rGGwmn;[G/t3n:ݎ|`"7wRtemظnqݮCW{ݮfݿ -vCЭ|G˗[w~ ލhy%~-܆ .w7O޷D}GzzR[>`|Z>b/DӶg+Cmz'+8[^m䃎Ymḇ[Y}yX+gF+RwS18|mpƖ1z>~,ڭy2/rS_Ҥ뷴nm^k^=kY&D՛oioND@NyJ"Cn煖4TmmWv8/*Wkޜӎxhw]KyiS>T=ێny| OoI\l <&9smiP?v24xY "uk[~V⮯ҔC[y޿l6 mo=xB t#nG!@h-N \f/NyPwE[+/{-oiiU?t|ֶ=I#W mK?6-u's-ή}}/R_2v>q{mhnɧ:C_m\OZGϷuGoQ~h>}mDu@ܯ"zm2K?LC3^IKďE+q%P7ܫ/3AE{V=EkO~rMMԗ>q^.{O~5I_^z\yę6yJbOt&_!oiۏ8=ۼRw$ٰYi-:HWBէTwLV| TG|遷7s?wŸ×u ~tw/jkSˁ/jCkvb-{iT~srnJ=pf4^8aniO+Mw>3q!#3dآSgr-k.ny^St m3n/rٯ|KJ!^9?fڶwf|箉4rq] >ݶ@n7xDNc:ulێ jwZyRwI}["|}ZN';tw郟nmfsVP98sJ[^{+jh?W}FQ9g¾'QxHZGi*C< ^V%۸m x 9q9tfQ+]M <^o˄'#nɉ[=u9o|UOi3ɘoXc34#擐3oڃ^Oq[D ѯ;ok29,mЊvNOi \g<&|+"]n1ζ.q!m]`B-;q]zX=+ҟ}};t-_у,0wKm8~KUjs-sMm@wpw[]As[Kͼ+>Ҷn%|D?=;M8:rW' :eVD.mgC>RQwB~-_zeOϕL޻;'.|Cq;@ƧfFjvWgNت{ [ϼ#Έbڵ-}O8z|GݶϺ<su9p4xp vm3xR/69t;/I>4O>P=E͋끪r_;2z(sQhn&xSSOm_>j|| 1>4r'~sN3[1*;O\@iVeumWW:ip[֔+^`6znr ߿5?/J_z=sK[ILKzd"|Ş]$.m_aQ&y){2k:=)hj̛kM{|~3: =>tʅ0#~fvX̣Szڹ=Crˑߴ3?W'ζ鈟8 z9(6=<G|u^{R+W<4Ye#+󘠗yR/m}_2%B]Ѯno-أ+Cw=̍$Wak1_|<*U>of[z֏蝑јwxmcTOPsrCWD^W>z+]A]3E1ʳOV'^!@CܞgtE}N:ՓoT<ր{xmNO DE|rԧ¯9ҪO'#n&?%b94yQN\ 7+k)Ɵb}m3уV}m[? <3^ii/.vhց^31=~Cz7?@C[in ,:=`.4q]]Femؤk/Q@⎪5v#GZ~m_u~:D^7zwS?hoaKzhD|4hW'n0}2~"x|hQY|@pM=:? _ivˬ Z**jyd#ؗZxawNs<=;*ܕS|g7t.yȕ}_/cmۺ=m^?폞W~yjJ;>f}-ͱM"q-僺~\u/jGtk Hv}4낄Vmy?]yQc|vzHY^ޑ|PуoĿT `ɡ#]omS=A+"W=/0$.Ÿ ̶ջY^w1/q6/pԖ}2/@>ض |Gדg "~m^ '^x 5e|±P|'[ɼݐsB~/*4J8Rw?#vJy_B+ KbE=hgEW]?Q< '8'D!P"/f},Uhc PIG\/5KaBWsZBO"??ҢzyBGb}Ͼc9nۗ#On)E@.CFg?#yS ~盹ĪZ7꿂:[ F*`A_vI-q7| j=Ly9Ry>3Q>89| 3(# /bʡD4-E>%:/(ϸx O[-CyACQ3_q'="㑒7z|ӹǶĸ\g;u E<ItN:Kla>ŧ%*z6]r=)./B 7mӸ҇n| ::wd\-I%g>]O˹T3>ࣷ+owڝ=i0?00߃Wk}߯Co–jːrp4k:]ua'oQ8Zұ @K?C~J~#…v zLO?#7ٶ-`Wc ruǜG$<؎+"VZ8ˈ {q]bcտr*zX#m+m,Mrчڶuw_bgz-;~[GujbMhOzﮫCf AAzJuzCq=mWa_N0~GhYd5hWr^%+-vn _ms$wWB>9#߆憾/q_6=>8dX3 .l{7qjhWqЬϩc}?/? #q/9p^;w-`3iQw<|%OM<^ÔOg|/f\7vޮ@iy ȿp'EV(r=qs#}SpZ `}Q/KZ|SnGcү)Mo>}I˿s?éZIki"|/oyu:=xhBr䬓f}6ѯNDOG>c}iek qOx~Ѯ_~ooH}\GkOi WZ:(YE/6UyOC(_ _dXOr@{P>R(hyp[Kp{ؽ^d_ "&s7CʱBY\qaP:zK1=GioTޛwAW7V mнy@,+G9|;m۳v1?C?? <ﮈL<椕?U+-]2.RK;xM[zO{Fo5':Y>yЪ>d_f\4rsg䕂xrhsW!o}?oou ^wMoj/ʱKTz59G~;IB~Z[rv^|{herQ\m)~!G| w>:c\̛$/d/yabU< N+mJ7k:tUpۦ!Y(%I})k9ġn37(ߋsXOzk'Y57'ڔVWd?yӮz z*g]{y@@g8yq'وM@or"vz)CG\ffnu7/GyǼq1.u@~< ;9yЬ#|_)꺌W+c= ymwxwm}vF? =7û~V><c߰\W%|} W:zM0"Ǯ*'d (zFץ:ǿ|/;[ saF{8ϖsC.NЌWLy7_O׻&bE䔃?;ٕşWcVO^᳞+uMn9?kvj [چ(ryn0:ŔS^΋|wO9 Gyzr;\ggztw9Mq>;?sg"?gwf~t}B~J+s؇u7z7T>zyK#:ӣ>g=8UO vP:ļ~b1|:A?᳿| |Q^<@Fz)9OZiWS->\ 6\pyaȾ3G'͹Ms^iYO<޹(N/#MK|(ߒ.=udI#\Cb]R?cGN9hWL,| cZxK8t|LTOxV{z  x}I>-s1I{ʹno߬9yB'g]W%!G5૞5*yG%UϕS>b֟LV+s߾\z/u}tCOcW~,4~w%WW%=u# |r_= {}Sx|#o~~Ows%q׻D>Z|g_(|_<߀U?%{}z;n3=h<à)G;E|s1/s`F>߄vz>ym' qhEqVRzWd=Hk>Zٯ Z{RoP.4;=%tKK|tbO"D;M.|pXrQUs#L!3Jp[ cGor3m;mw_ׅ9qQE+\GO^\@\HAA~W)r<?ss zY/x{;90cg F}huwyνM!v סۓ pns Fy&~>߹򛓿CzrIC>}{x"\m(/~SzKW=tx44g_Qⱹ/'i.~P)~rΡs!|*EARnsz϶n:i|8g╱ϳCʕFq5P/vzmz#D|>h) )A䲈KwtoӪ?~| 6)?ˉ^7ןLs=r%9(xzkМ߼gw^F9ͼ7wz9Y;?ɨM}ZxLɃR"t?vļ!>Jȇ"z[벡sMSo>ʼ\ghukmҗQ'rh|.q{Asˡ<ʝy.^;69Ƀf%vkʝ>rOvHr䔇9ց8Xt)/yFۇHg O8l;h|]wsUO>C|ñX|7ն1Rbw /ѹ"uKT~Mq("_c< Z WhţdǗ=-"ZHZɕ8ceV~eׇ&nտ|ɠS?OP rG;}}'0c_0EhrgJ)6WߑSd:;5$7ǩ ={1ğ6*w齇c(r7+r<~s nByX7gAl ; NCN"G^*r-yyNК;E8K>+H3%? =䪯ߖȵڃg}Jq7+3A+" ')Nio.ʡw*=?N:3ɷGF_3?!w5~fby_y`KK~_M+>S>8/4GЃ>鉵slG竉b>PߎK̻p5xph8<{̽F"zmgvkaǯE>.tO.,o~}cgލc yz;zZ^C^, OLmy>Q~935.;"bq=轓+^9>yB?)|8?uXW|vE{B*N? ȉNܾg&S8ɍ'63\G`B^s?64H|쟓Gy̗f|}kW=w^lS=tR/1#>4H=}z\}-\so3B;Td<Ǯ`Wϟ}O6~owa!p8Du_w;[s[\i̶֩ϖDc?O ڋI-O进U~Vf&@'FH>@ݯ1CsODnu;U|vI}1ʁӭzMެGsS.9ƍL6Wo3U';ZNB˰ %$v!OT9U!>qgFU?:~%*QDZV#n^pk~Z;d=9 eH/1\Ut]9..x=?u".}/gRhڱ :D}ya_e)Ͼ^DXk)_w}jdיvݺpc3H9hn@'wk1N@}Km3r;;7TCq~(yI/E9^g!#WT}oinQhהc=_S?l懺G |缴[sOuL<}6aIoufȡϏ}|XyGWry|S*m2uӱNdyN'˘/=\s#ё7@&jO?pI@ \޾6qZ;ĺhSΕ^ V{gGbm-u944EN Qoy m[X~Nie>k3+Z>YJ'rT"N8V.ܿMܭc'O~ľyC/\o;~To(z\W_J+(^ ?˓i?'9)ɫ`Z_*W$\ 29>q_Ф9|C_ɏ@ 7q 4z_W?5qm2jO9+Oo3>N"חAuއb8v!xu;/=>yNE%u|[*d7Ofs޸7-!"~ Ξ݉#]9ֿÇsGέ11vmʳٕO}wYz8_-ox@ȗ';? d{_dbg|CWn%D'uE$~ |}…X?\wzK&+rr_wm7ux1Z__ޓ"sP>4rhC~q .)O:[C^v؛CK\}d' |E[z wzh>K{|@yuy;Wؗk:C+wDF}u|z۳/9Ǹ4r ⑘ߩ@[sszȕVOs*;9Oy[6\p R\?уz/7cѿ;ms9~$uB H>y4B3NOyK_m͝wE@#9?`Wħm^|ݝD\$>IvNyIδe:?Dy!Ҿ6=n&A|-؋Wx>l}ِ_gȡg nS<-9tg{/2_$u]=\zq4zwH'XorF@H|Xt0zy@RN}Q8`vRu?g8ŭ{넜sIscݓ_ܝUa&+9d^}T^#ިbO۪?Ǻs#"WZ(_'ԫu珅< d}wF>F(̏}PG~>|ֱW=T>%-q~"_NOΣ׾}#Wڲg$3\A;B/g[#ϻ$4x}jٍ>;pˣVD!ao5.m-Op4uz6|X7;ۦ#~&C>)n_8G_r?EC: =FMD_fOX?Wd ]s<$S}IrC2٦^+IocL%&@O6XD_QO;~~zaȷ mu`w_PZ>z;/y7GQ7s4$ֱCzUK&auG9Zd]<OȇEξ,?fjj;O։~81 |B{mޏ^WX skWUu)Z##\0#z~j{uBWǽ_ W=wOs~ZfqL MgsK[5?Li{}ǂN{~@CkV}իUޟG_z~>׎vs`OFWz={tz~qBnT>Ñ*5Ԯ=AUUU~VJ꙾?77p3t];ޛַp㌳s|UT;McA+uGz_P}7N~Ak\4%k?jwKtv9UfhAO8J+{VUv~'w_u:}9Tف#;^E7 cW˽g>]rp流UqCupQ|(t3ȍU?럇Ӯ}ܵjܯS;˵Wg ծ\y$굾Gr=A7?[WO۩WkCUvH5VH\{?8oלz7]eq\~wj4c~wqG\ew]i+?AXP7oǍ}οtjv~oUC =6HA ߵʿO*ϵO}Yu>~wg۠sSV~seiW==Vq^ΞqC=g'8=g:NwdJSz?/NK53u\}Ư Ϫq5ΪZ_F;wG]~W\^U|5^nt->}g_\\,u@:.W`h9>486U6znqzc/E덶{W* ߭?k56辫#Aڏ;]j}pq*Jk815{븨ϡx*~Rxg'W^**6.Ԏ jrvXzڏOz_ z澋\r{q~>F[gK5n~5+;Wڡkz{NGtiׯy.gv0]<{ڏ:{*NWu8a,wr?U+{ZUy}ocѫ-7VNeWz?]A}k4};U[{R^R7s;ڍscA?UNC1Qy.숡z|7hّwtC׍;:;}Rq\\c#xPU;c;{s7Xε~[UMw~]]Q#5~}=2|gVhgB)gqҍ{Wq͍sEwtg]CY\3qw.9]<ʍ}PkU;lڵסAw}ߪ'W{M5>xHh/TWUk,]|Q^sv))~.^q݇4USj'~P /-}'Ѝξ_h=p7q]_NsoPQ:{cҍc+zb*ྻ?8}귾NqKއWZGRŁ?0ϧ{qX"wpK'W\<Խױ89wU~}<|=z_4}P팱8AeoV?885UணUOkgǹp&8Pٵ#KV=.mtvPǽ_W_\\@[͢?~:<#$8A}^7/X[.]}Mur~?]S:=z0z},~VqO]?]mjW=k'U<տ=Tk$^{,Z\?㾯sL^c>_uՎwrGgWNzsƆvэoΎ/jv\ȝZ\Sg{~ΏK99wj7j{r=]&rnzc zu\why z꙾qЮjh~~>pqBkzn$_wνg׎=-]W_U|8?ɵ7ᾏ\\KޗT)n\M^?<-zUxYst;BہWSF*>+K{9L\k N=n6{M"wqʏvg>>q_*pō'&Ϫׯs\mz..ןUӝ}ZCyTvCyv{e/V~\}o}R.s\q8*~?Eύ.r~zp{n}OΞqH[k諽6'\^F_^ܸ1X]WƂn~'U|\z]Vzo(#hn拇?W?yίnw=zn}?VvvڏUq}.;.xd5SŕP١l7 ?\;WzBwvT_ܸ}}z~o$_m]\ontGX<|鰪ߊU۪<xO8;=c=7]ٓ=,7nW ]<ڍAgs*ǽ_ lwd`,beg)Vq CP:Wo\7ڟj9h7]3nwq3}>gVq*_ V~`UU~2g~OBGv{Fod=Uŵ/?zou㸋 W{珻ϥ~ܗCjQ_{}\?Wk~\zOCۧϮ>Gr?Uٍ?= +SgWDHe^ߍc)S;.Yq./W cAXʹs~\;wqtvwvhgR]U̵gϵ+g"UKeWrgqk34^ƕ꾜32~뗪vc5%zG*j'_:{jgڟ<Wq^*~PU\B_.Eko]WP_ÍS0Ծpq*NQō7VtYMcόw?Msoyv[OzcoD^(M3pN=qNnz㖷-9{z[n=SEmy<#iןSNϋmDŽo;팠7O9Ը۷v9Mgvݲ{ߩozoSRgy7gO~KO#;O#;O?yRw$|G)w$zG'=.uGw$rGI_ɗ椗Bp-[u77-)gן~6}-t9GGally/data/nasa.rda0000644000176200001440000111576413663637143014007 0ustar liggesusers m]߅v̌fhwszzzf{z35/ '"0 DAIIYdPPH@!$D ( (r % FI{^߮u{=Ҫ}^^{g׳=}׿n_k~~_o՝?~ow_ޮ oY oY,,,,| !!!!!!!- 1!!!!!!!- )!!!!!!!- ^BBBBBBB[~(!!!!!!!- ?IHHHHHHHxoNHHHHHHHxEBBBBBBB[~KBBBBBBB[>ߚ%$$$$$$$eOHHHHHHHx&$$$$$$$e/HHHHHHHHx‡޲޲𑄄,޲c oY ޲ oY޲p𖅏&$$$$$$$ec oYxBBBBBBB[>O&$$$$$$$eS oY,|:!!!!!!!- ?$$$$$$$$e޲ل,tBBBBBBB[~&!!!!!!!- ?𖅫,|.!!!!!!!- oY|BBBBBBB[/&$$$$$$$eK oYrBBBBBBB[𖅯&$$$$$$$ek oY,|=!!!!!!!- ?ߞo$$$$$$$$eag oYx oYfBBBBBBB[~!!!!!!!!- o%$$$$$$$e oYW_8'?=qO~BOID]NH;JY̐!'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'ğO?'S拟]c?ל-^}O*__?77O?鿾-[>_{sw~૷?{?_?lS?3- >g_w/S;׾G_9[߂?o˿GD9 'y?'D9O<~4t?oo~|{77}s߼W /?7T:Vn+gOw`0 `0 ! `0 `0?o`0 `0 dgw `0 `0|> `0 `0w`0 `0 o0'`0 `0 \`0 `0 O`0 `0 o0~`0 `0 `_`0 `0 M0 `0 ` ?`0 `0 o0]f `0 `0|v`0 `0 7g `0 `0|7`0 `0 7j `0 `0|O݇`0 `0 O`0 `0 \>`0 `0 o0~g`0 `0 7n`0 `0 o0z `0 `0|?`0 `0 o0_`0 `0 `oS`0 `0 7O`0 `0 o0OC0 `0 ` `0 `0 `>`0 `0 x`0 `0 o.˿n`0 `0 o0~_`0 `0 ``0 `0 o0. `0 `0_`0 `0 7 `0 `0|o=`0 `0 7& `0 `0?w`0 `0 o0S! `0 `0`0 `0 o0_~z `0 `0|?8`0 `0 \ƿr `0 `0|/7`0 `0 7?`0 `0 ``0 `0 o0> `0 `0_`0 `0 wC0 `0 ` c`0 `0 w `0 `0|n`0 `0 o0. `0 `0}0 `0 ` ? `0 `0 _ `0 `0 C0 `0 ` /o`0 `0 o0~`0 `0 o0& `0 `0v`0 `0 7! `0 `0`0 `0 `> `0 `0?`0 `0 7r `0 `0|O~c `0 `0|`0 `0 o/`0 `0 o,v?kv?_7şs%+?GHu7s?7Ïv=wwV|y-=~+u9_kh6 a,_; Wa ]$=ϝ篮.)/o_-[7x2/}:/,/_/x+~wKܼG sŏ_{^"i[Տz^·!{K}C ~繀ԫw罺o[Bg PH8 /ns\Qߘ ?ZÒ'ϋ(?}J'^j_*tG8#?/t +S_>,BrV<KugSoNz[▝>xGiTv}7#W\x?(g_[xhWėzzWh\gŗ7 7K?HT&{|BV;|ƻ;n?t>/Z!ѵپ*%v}#STW._X^7;w뽃= = ƺ)?~G1H>ٞ'?l)|HtY^~Wc|. w>GNEĻӾXW?V)[ODž~]siҍu?zno~E^9%+]Py'OGu\|W# m?Bg?qW&]q܈WOyx{϶w}O8b[K_~/WUѯJWk] ݧS_4>粲mX?R.w6qEtD=dǎ:pzn{k}sRqGދ}^Ԩ-q[ny|,tca/P?i7'5m}ZY*+=mo+KkɰhMuP}> xb]N>yz8J_e=i]g]xw:\?NsSG}?ySHyW}&ў/_ g&;:!YQjBr'~!uy/u=Y!qYo25Mh&=묓\RZt@nW}?N'8}Xǧ^]Kyu^Z9| t7g'7_߱#wqzYwڳDo+?Bgϣ^H?mno`{zQVm_S=~V|m;]@h=D [ov6Ӷ 7u =<|9 qy{/U|ѯ)H?zi|:/={fY{DH'|tyu 3?3m[!::SH{sI.t~w~4>}|Jי [=wW?M~/te5V,,|Xq}?oujB\@Ƿ_hxv~ϭYwsFW\>og+6w 6ǬsŸ*,O~{sn_L>W];GE|yw^._<7sIoiN]O%?< z޴Nu zкox}5OtDd]zM}&;,g`;{~>h2`g֑*zȖC:Osvv~X5?K׬}OqcWP<6w+ԾˍouW$x+N|?iK/}WmNLvøʱ'y ρJG?=!׭L[^ǷpZuO>oZIί~Nt|C"ͺyum/h;4'gYhe W%baYW:^yvDGu:Dϫ/*ݤ9 ͒u~|+>z|Dx^8ٟ!ksyM؝ ES [ݒ/CG{c/O"~/9w3YyNyRtA؟tz~>պ׫_<"Z lS^k:Y/Ozڧsn8~+p~twkX1CY?Y7ѠGwy#O ,aԛMzΤn.S~~Su5߼)^pM_5=NTyW{<ֿ/sR{\!j~NnS6KJks[3bO/[Ne[+]/`yީm?unrlq5 9yu74+ϝbyt2;ָ8Jw:an/5~9mta\;vg=~L=. X~(R슯8<Oz~B\uޕnGq]v}qrExx^O|g k.P){>O_ ujji^u3:} yWC]hK5?soZ\&%O<60w~'{zֽvR^znG/|x6>ո,C<*C~=c~>^zQu[kkWk^/Vq*sa^[Oܷ=[6%|6x/d?p/ cM:cW{~l_aпDQyyׅx Im{+[|BI'u~_WǠLyu0gڿU}7o%}W䷿ۀ.}I<)nO:إs'>//Ǐ<4sUyGrkYG?rGxV2:>H ϱÈ y{B~ݕ~(>”@Sgi]|)?{I g%^&H~uE`]r_xX_5RH#<igAzQ!Wx^Bnuut||)ֿbӼ1?$ / pxxus>-7:\{v~]J=tuAt߉D})_wⱽM hp|m}t=U^z~1=/yRh? ^^8rz~4IKֳ(t}~?m\oLCX'q=!>ar@>]o<?o6xm{ӺX~ Nj=M鱿_=/ݣPGjm;.'%4wx'~!~Cy-<:=wG"/ v4V}r)~q]͏,3z@E{M{@[Cnu.'Ҿ*,Q<`7y^7HO3_M uo=zWO/lNq~m]nOl;U?'.={PKk`ܯrbu!iO)ֳ̻ -Լ~z۪}=/v<>3su{}Sy~Bݏ:)ߴ]ڟyܡ1.S=~y;~.o.}>.Gn(fyi]uy<8}۱Oay|X|63aImoڮs y-^佥=`+C5/OaoB8Kswe䏿 _8n? P=Mwƛkߺ /s>n]{;=#2y}Tב-ӵ'Q.uf%\Z{t =O>4Ow'qJw1OxYn|K7g5QפSgyXau׏y'}~σ=~|Aq94ui3/ ?~׋~?/^phZOc_nOyGG;nt;Y"a?׳=#Ei|=T8vQtg/"k'o3n=Vv.)/^wkSN=~-nv _S7_aE3*#2̓^m+quX7,89ǥgf{~5ImOz;XWN3> v2KJ1tic+?0jڦq:xڭ?~о~L u~vce/y\H~blW:2>uW^v5c??m7N~׺ju}7ګqV!;ۺ=+:T|a?{\ci<|* ?J?+;Fr?ax}vQR엀ρeӝ'ux/g>jq=x_rWn~jFv#]>¯?+w}qp/n_9>-J!2%ɏG~p=tYʹSK.~y_?'cOGal?B.wI^u'_ˋw"x>ut9x8))7Əro3|fNI[+>&=ㆾK?GG +ϻ%C[Gxtgq}๵mz9Wϖ*#7+X'pݤKNJT?h~uqۅ|5㺽\ocVۭ=γk;>[./8h?aϋWv)ú>F-}^XB[3z|Q-?ֿ[Ƴ'UԾϯ:}܏4G+;-*cr~zq0m/_k?o?eGu>vy^ߞOT_YgjO{] JdޫkwOoO|1ϬOz=ۇ~~_?~_/,L;>;c?P?8#]4SIyzgrCO.=p9"^ݙSt>H>Þp>]Ws~;(u{}ڿ*ɩ[R)_^lN١_J$'a!ۥzZOzAtL<{\d_loo_wWNJ[ժ`{^oe{o2?p{7W~frCw}GŸ5t:=O{x}f1+?e[Oz;_v[o/Vn[|Wv:Im]H7Y8_kxB'xy^?|;BtLg _1? ,cD/>8G|<ۅSCP}WYJ?,G~ákzN5^ϓBVu7yp;<;W浕/b? G=By?{<zbk W(q~xſT_6o4oNurGryi1o#o+ο[[ Og*G\q/['p:ui}k}gsynr z}gM/|WQoZw5w_d+xw3t?ѭoy^~wxiwfcζn wnnr8~q!?kQ^~|Ķj眿gރ{|B8+~(m,/Nz։}oy~q={ohG>M}v_ OuD? Jϝ Z?ZWۯb~j0z/ Yxp3co?__u}OuGwa~#Ia~/ _qOG"uzݳ I/kyu·B Ny< y|~X[ߧ?|U|v+u'~ayxa/_5N>G{=@Dy/;ϓ`)~e]Ď\s2QYգoz\eO{ۃM9_\<χ{ZN ~~_]?}1=qHy{=gMQݙtj?~? xz:(X? Mc 0_=[cǷ:;%ѫ|{觴@1tl?4W^9u3|m~(򺗝6xٳmvXUuOJ{v~ҷmSxr8mN>z~K⤿]'Ğ _tرwUM~9=Oɟ^&Ǟ,ܹyv%ĎAO zo#ltx_fyn]w}t=~VwK͐o߼ ~e% ^xw;,/t63סP('v;-Ƿn-X=O?(w]vr~Ͽ8 0~:4keQߵ3nW?]PNc+#^܇rL{v!%,m?OQ_LR_C}Oʫq)>,y)]*SAgCRo\xxPW3u>#/g~ԓ9]۟zDz_eOcU?(<<i멉=r[i^z=xo_)~}z݄/-ӾpuN|ֵ_{ֹ~xij^.^[[{ i`]dwx4ZC~QH{NX0_|9;oB5ɿq{@W=@A<7| 3-EʣIaɻKG1a\_Ǐ.,Ď_祑O߮Wzayߞ~:S{ywuڿxh]\] :XOR0=S xo=*o_<a[I9}ydTj~{ڧJ9+Q}=>^h}Cm9Wo;?F@ʉךxoKy?sYuz]%o-{v z?:[ay!gnK\s?ghﮄ]E~;Tǽn [Oڧs}yi_ȯet/hߺKVe){/ۮֻxڿ,p=ODp_[!x;y>~%x'|yZ?,BxN@7tY?Y%<]fVɅy%SߧBGyH:6!ߡy8]OLjcw]Hn=vҩu_zdOu ϻir]vK=;_nNc vAq:(_cVҕ__uۃb#ޛ>{5_}}X[x\ަ^8=y͓o~ =2>Vu?o,ƿ߯m_wkk1}xǎ~w'bV ;"+:09s}h[_nk ~u>OBw;~?{F=7k=]~%<Ͼ3/B{f y>,œr{|ߕN{iwu1*^_"qI/qz_9x<|lڧjΏi($<3/0D~^*O#J|z$gm瓿ۻv .z,s:dOq5Lyz~̅oC~ 9I并[?+?r_r]ԯNsOߕ yuIAu ~oδo%xNSE\ha&i^7n<d?:_~L~NӺxUY?:HnL7iq{|fBJg}x??xH_ߓ+MWg"潦!{B$_o~ ]7 ^,>Qn1^yؿ^*G $?X_JTh? t:[o?=~!㝮{z:{]wWXSo?ݞOۏ{8]olK]B}O!9Nqv>'#N../Nk5-q' _1;($Nr?6nWC}oM.l?$_/U<13)ݺ}-=VجI׳{&̫{̓3ŏ6CyyYpC=:)YW 7{W֭_׋7¿6 ~k|Y@+Oewi>մ>7?na?ϦF~8?IsƎ>p]y:gga߯(AoL'~{Wx~_0>Dm[zݺ޾(y<œ~#?+]/[N;^2~@W<7]nh?pF|)_])u=?j|8~_%zayKz#s?nׁ~ɿqtP7Mv\[~ϻ)/i?qWُC[g=~lw iimRn=_4i%(gO}#ر5Nׁ|o*p>Oz㖗wQ8G:{] ҥS:>o3=S;1?pxu4`ǿ:;u뭯X/e W,yb|zF_R^\oo߫rso\u\>Ov78 _;tH[v׳$sz&N*lHoXޫǽ~. Xҽ^u\yo N^:`Go7p]e 9~u{4XwKni*SH;K~ִKَ = yni7?m']q9qy|I#_~BMHf/u'X3z5{ylˎX)xDz|}I!K7vS~'m;ڿTz^J(SHZz^Qh|<?7/Dn)_v]&= :S?> Ns}Um2~@~r}AxIG^O|i=e@9w{O׾pWxNu~'_A/m/Ѷ7ڵ1kx׾'cc+;d}8metl ogn?: Ϳv[ W[u:N=zi=_vɺ~p>^_S[J}NՐNگN7\G9y׻7jIR]ZܽQ/ո?|inؗˎG8tlRm_}ls'|Mvoxҝ.ОQOnM|lx<= oǭ}Ͽ,q-uA'}E}y}@O,.ֵ|ۭ<Gއu}gf;L߽[_n;~lyu|Ǜ2=[\ѣk~o6үr:ڿ;Kow\ߺ[v~:w'hg/s?#q{~Z8,ϳ4ú^<ǃ4)=THûy]؟|}UhU]'4.5Of:W_OWxރ~7(t;ino|ʑח1ʥS?i^iH7Ò'yn_s9u֩̓>zZga\wS[7-~MOwPF?VN~eA /Du^Ljm._9{=n~[۪~ӏ=2%>6G8EᇴDlcgV(ɅP_Gn[tcO=Blw+~}W*g:<&~ws!t/3w>=ƝhNì;] Ys5^溫Bm?|mہhOqi^}gZ_h?J!宿&\+DZgur>,tf>:oUwt_A'vq^>  {]6PIqv6ZS~uqs@xsy.߼Y(mկr.3GO)O~a~;ᅐ*~>Br9WOu7u`zԾYG?yn*sc@[Di~ {"kO~Nm&?)]ߕ}7{7e7[u㺧n :ك4XۨOnX_|tA f; ۗ+*ozh}rSqon?Ou)]mķ{Nu9~ݡ4?e\^ϰyQ2[ߏ>J:m9ZfGxN%|Eǭ ߫õ3i}^3~o<8ta;H|<珒:ǬZ:,E_8u.s[u^G@>ѿ"wW< /8~{@u<^Dž<tP=_=?`)^/|rx_7nWHh)W׹b_,}yvj,^:ݮ~bC^Kqp/8;>w^?J@+}簼/5~w7UD=M=KzNQq*>ʓaV?畵к(=l꟰꼗-7\ / yhx=cϫx[z&v {=~4<_zry_{<?~֟Boa<rX^:{UDZW~(ty:OsX"<+a'uOqx1/ {__w:p/;<cb'ma1վǛykm=q5/~N;:i>Eu+݃ZM.Ru;}+!Y|]=uv9 m/\(}=x^<ua~{BOuUS/yaNz=+xRoo}ٺO{9W/{d.Yayz紞.e^o)t]M8S?fv wxmG }ָu:E{y{Zurf{묹'ۨ>L<ߔ!V.܋/DV#_{~Kz~wcez<રBtJ .Ki谼g7ׁ=zf=?u_W@: =BW BʙȬEyn5x)Wo7Zo? χt [㹴O:w:NqO^._vwj_+B4>O\\wi;o' >@֟{*v_ Vq]Zy.:H܏[ϋ?|goB'~(\vu]xv#]~.𴟋uϭLv1͛WwQzѼ ={wKכnX^9]?=sۉ'h0+~~i]xޤA=wwzG޺k_D*_޸ߝ.|;Ŵ U)'mwmr1wJG7ΛEFtnnGu+cx`ao}wg*?Syg]'T:yQ(>0}/)3jz]/_./(/eHl=v!W;σ,^$W[-y`\N}V?qe2x~G=A}e%^w:Za3yAE~A/χ->~g`owo[{u}XoUڇ? _wyuM~ei?<_Y|O7Iy><SK_5=xG!O[PhV^vw'XQQN:C; BMv`7ax%}r"iBc=ϝ;;GvaB+3/xڿW\HÎu?Hv=mz^#<(g?~{ޟ[l+Ճys?x<=Ӷތ^υ?(~6w]H[Wem/ ߃=cœ^A<حq]Kuuƺ{_m<wL9ϺEk/ :3akɺuis`~ɿ.:^tڟꟃkD}.ߞ97[t=r|H{F}nؙK=߯k9gxGp+޷xQH0}t;/>{RH㯪cuyDWowۥzN}>jwfq\wn_*SÿЎ{=ϗ< |~Y'>vtg_x~'~O7{<㥶?i<^Z@w#Nes? ǹn8~oZ/PzHǐ+{R˶z\uBuhxݿ~!wK\ՇImq[n'~0?.S:.PE:wUܱܹ=_nz8Nus8]R.m{ڴ>/z=w?fLEKL㗄Gğ~0jm]G<<߿95 _¸ pͫ^[]tRWaׄ?gp:n+=vxw:nx/s(wztXge³0^d]L7{<[CwvA]D~<2] ~?S^uׅW-Dh}?~'{]{>M]<u\.%_"(D!_x?Qq]Sk<u+z qtcG:>naCd=?>8ϧTߓ}^aÇu]G}B۾{\~fS8lw|I> &{݀?8%WȦ2VSm:Q8m/?k|[_n+wrr{Ǯ#hsa+Np#W<^uֵ1_qTb!n4mg39[*Sf oNRHukX^xm;~Je4ᵱNu4ҫt|YO" qϫt9au=uBD_ xQhe5~7 om{E~Z}#O~þu@/x];]o1go:Fq?D(+o?ؾc|.?{{?zZ8`=}jo/S:=~6sDx=̮Wkz~vs|+Z_?XBt _"_^?\L{ቮk%oϭuok`~%z`;]ӵ[gaO3U:~AۙڧDg9n'n_ؼ}UKN{zx'#vuN׾u(~ujwKߴ;B?uk ?t~c;jt8&t̤]w)=ڠzp\r7NOwAX'_\)W83]{gvo»+^~w?xܤ XGn!^3H<ٗ~˴O.>Ͽ"޲fگuʗ ?WYC?_oDyOOߟr;o=vijx0+[~Xc>_ze^l]DSmkf_1]W?GnzV׋9W<}mcѮW.-eXOW]O싻w=b?,{ɮ/vϙ]ԋ=__H?6:jk:~Tǻ_xl/ysxG[~P=y*~>OT'/ݒo+wύMЁF/ϳ|NrL_گ }>OU_"ϵ݋|;٭;: P'!~/?uy~~.峎<{^<߾CǃIu^^x) vb'7t;Uമi]2O]tmcmH:˖0_z5OBny^g?*?D8[3?;(ov_o 7u+݈rYŗKN~o"JX>K?_ _S_{~2W7S_؞w?ē_ }_lc[z9,d"a7O:KKu\%\ɖˮz߯p?micu|<_QzKmw%<a1m>x\^.y+x5eg8i^^e|{0xUxb[/_=>qȭO=.z`uvΊCk.t',^ogs놮w9=ob ]m.tkl=_BCfttz}ݒ宿U[s߮{^k.Y*폥RΦ^/<𴎇EzoJ:Su(ߟ<_[x)'=siո;]qnE:j˵IW!:Z i+}Jx#|*}mG =v { 'sy)?,Co9}V{~lk{8=yzv܏?)1-:on?:?ۺ]ܠ{;\~45Ǵ^ǴNAоT=>po}9o8lc}h=Z'aeOoHa_,o_G~'UvϗxyޕeϻO/hpg:gttxu󼌇{tH gҙ} yW]ۦ3 8/c{] Un_T۫7mmS({ݶwݶ-c{~W{•N'.臅NX;؋i<ZN*ĿHÿ?uy<S<޵5Cu{|.ďGwC!N ?ռBϡS^'wn׵OӴ_TT!zw{=jXy}MSWSz'|uD7>n?F,l/z[|w,ܛOOC;]AʅuZW.@^W<+#<}Hy= ?{g1/ʅ Mqu,-񴾵}/=bk~TN(smۺYntFغ4~o+?]o0Vگ^cKܼ\sW%\oOe{>g?[ '|iOiGL9#?Om?/ƔwˏG>Ds<:anGW|P]^=/jbϞ[:n}֭;x>arC ^*^ >3{eN[v=IX/v?bվ=ʏ8pXywkO}{~z{z[Õĭoگq|gҋwzs׷_g{B?|RP7XW|_=o}˿qLm7{X8V(yOd;u]_h]־E?2ΟN&M Y.<Ϳ߽=:kԯm;uϿ$S~~ua/թJ]y\=.Qq^;׫=y_mvE{?0<6о`G7kۦNgů.\#j~V:!vAWoykBߣwuDBu_k{NWݶ滮IAYͯ:,?N]K `W?+l|K8q O>g_8}!?:c:.ŷק&^o</[;zE;E'VرO`t >,oů!?y*s_vDO/ ωPOW\p /n,xKKݿtvL;<''%ڟlzӅ}y^?>vݫ+]g?+=qVtuigz^W~^W:>cxyhm'B)gwfb=wy|J73u?̛Xq?)yE N^G?G4mӟd .7oXv:E[mʏ-_kfu;ee!cӎF=n]^(׌=bG=߱ok|,l/:~X{;{oC8#W|w4nϰ)/GgiDKm]~h_W?6rlDLQ?}^8,^=S|W.u?a^Z2/=הwx{}oڿ.|_wF'v;TnJA:~mzV릅}C/O'Ek@z ɟ>,«Wx=%x_^=O=]hݛ)^3?oYuu߮H =O?V?k}}|w{^ƅxugϟqP|'Q.X8~2ẋOs޴0P>O|US~s:]Gz~.]]|6Zwk]iִ.+|+DZ9'ڶ{yoZ绸]uuo|;_]5S<{J,ty#Wϫn:j}|5zx{[ݯ^^q:yw_׸Sm+<s -}үFgsz}B>^rqB?*Gݿ}ʹ;=ۦO›ױϾ6֛W_PQ^/-?uݟ|wt?ǩd^|A;}q/s}X8|moK@>q_1{y;i?}seI޿(C<υgçu> ,^g{J:l|&})?Ǻ»}'~`>Sqn#=o9 ɏp3:>7ׯ3rSy|덜Opހ<ތRtM姰Z?$]nGut==nҹCpQ~u=?~ulh~-zQ>z_o0Nnyϫq!v8+{==.zdا?MmmO>I_|:s_m_\QRnפ~WnWZ,l}Ox7߿hon+#;qwo3wG :oXx<U 7xu,l}@ۏvEzN! 'Nz IAl{[7d^^7YC<^?VHh_q዇ym+0·taV|;~Z_񸝸,syDt?3_xYgߙ=i-?se}Rh}7 *dF_?d/Ocm{į \wk>+=m~*w<j5|6F{A(x+=_[\JW懮{>:=W%xߕ^_5mtLS {~M&(7W%t.ߕ{<Ⱦu9Ou_=u:.a.{k;{ ~ǿ`l;]?0R_bz}b#J/ 7_M p%{Jq]t17?wUOc%apt)?Ǘt~[jS;}ޅtML:~'[xP|Oz]~{g A:&?#xw(WA_j]~"uԯ$mnXgrn6kS{kyܥp>һ><\7XZ/}D{9u~\^uNv:3A[۪߀^qԁҁW `_gK[}ap})gczě{,B_n{XǼWezط4WwXh}MNǏeuVu787/Sa= xݒwquS.s_ $WgBK1wl iWxNW:%aɗYbTp;l <~o^'{t꤇vS;,xח8c=o?黦sR_ ?}~x^ב|][}mw/[՗>ʗ_|tiKcJО[C<q9CE?zˤx]c\h,?v8Π.~~n*S>» O18MO-??xNij''n]%>]oy|:y?}?Xg"OMi]'χ~XOQ}xzyʴwo9δ.MzGuN0# bXO])=Z' ?U]oƭzz3gCxqqiC;J|tnuwOEء[qvc7'=W_:j>g!:P9^[JKGyN~yח&㥼wyK'fO9pF>kWŐӛ.ڶ棬us>gzm5z©=4~MXc^/:_c=U{[ߣyׯ;R^gu_ǟߕϣyW~W·Z7r e6Rju϶P/tsoc9G7 M~^m}'f;;+I4zB9Xu9wK^+uă*>>Îz?/x_纛ed-Zm=jǑAʥs=?Wx(_-&ϳ Y/zx䧿b{h%Lܯ8. yz<'<߉|9^g{N7WA>LR#} p^fύ}寎׸uGPQQ/f֡^WV<ğs~;yzU(ws`kK޷I﮿ O۟rF=f)ց'Ӈu˗uzɗ}8ϋpeu㾤RHqWs|N:? =v~ktq~ǩ~ /(㇟l+non8d};]mzuw+4⩶я(Wkw'y#^W~ח[CϾ" ޠh쿀~lqwA~~YZm'"*@s+ް=I9Ǿh?BxN__D;zyu?^?xwG֍?@. Ïy߭߫}|똤׋/2={e?kߙt79߾_<|]\z3kL 9n<=pr:b罼>,ю]z| }SO~﵏I9ig4Mӏ*DW?ϝ!_hǸ/He<iw=W>w} _y{~J}^)??^>.sںmGOzWO?ߺr!}hr_oޥ~9x < iB_ϪlD;~)}ܟK!=&WK9P)SdiEJ~_1>}'[=vݚb%eǷ,[>s X{]N㙔st zasFgh]R|j_7o)/yp;ꏞ}:>vڟ_8=^4c5G]y8pS~gtvd_~@u<~[Ww)d_^r{.>,K~o=rZ/31¿|X"3m]<4ߛ2+N9~(^OM<~|#|Tnx>[yl_a)η?C|Bmu9U~o[_zn~=~o삭Z=3 <_=ҹ^7Ͽߒ3m~Cfs&^=>zla/u`K\OzcӏîM:vKzm_@yjo\||m㞅_{OmzދyOӴgs{s;ʙ^V(.1guP>H9~?zu<MGy{ߟ ^m/߽rݿ!Oqڷ}C}fćo?'^w%_OtsݱdiW`~,+ϻ_=~})_;OuY~\.G:;ُot8]w{8([!ݤ:w4}ϝrC/Ϟ~D?,h$_7}WǟGʴm޾׹yW'$AKHWs;}m/;{ݿ(clHbax< l-:oZOQx/[/q+V-RH Q#_wuu3{꾴oiP_wv9BGn~g_(PS/%=ߴn~L<$^q1(o{;^ i}J郧%YS~# o^:Z<}N~Wr{QHr)csxz yޞ'4M*~牸t{.9Ӽit>b]W8ps~O'ܿۿ Kxݺ~C>,k_/~}4I gt:]4^?{w~ϗY鱔w?OuUyYwG뺬3 ~a,:OEI#4v=T=!m׭cϧ>0#mLϻ5jr>M惌X+oG'$?^+žyXޗ|p`b ~5n?(?Zt|hin@p:Y?r<3ܟ=xm.*ׅ9\[:ēoXlp|xzV/ iz\f7y$Qy1~ul?y.m:l/nU}K?^->oFϺ1>ʞqxrty~ J&i&P$lcuB/sd떷K\ ݏ˝Οy(+}^^Ⱥ3 z)@{G޻Nτk?o_~{~\텇wկWXƯ. i/?ۿ߻%/r?}o_Oڼqtnҋ;;_T=CzVovz yly~wG;n>:꾏7>cMzlzim}pw~M>ܟAwK\/~~mwTl7/M8##qҙ7e' yZϙ3^h mL:K>L-{]dZ{]a.󼮕8ϱ};=,7yjc׏:|)?Fl+[=/j?>ԟ=w9zRc߳)uҙloܾ;kE /C o._MaB( 2|Y<'[uyn!M_~<u7d${^7>{IG~V__W&,w}ҡ.tma`'u<,q-{=wmx#ϻp(t0mxèNc5N?pIwIǾ(wqB]voi/g?zn۟^?w4{ZG獽p} =DG(HmOwo=b:[M~;c[a||6wcGjyKS)ޣ/ܟ~7pZIʫ?Wu#oznmXh?ڷC=^uϋzvёt}p>rvW4SoxOw8х8,tf; =σbx/m݉i||%Ǯ0py<{4d ^퀏}ܟrP^㱓>d޷r>l_N:~M{ߗݭ~8ܣIuhlX|O}ze:Lr ovf>GZC:/u>/8^z|oVLx~^E9#[+y:N9$kr[ƃN޺q_f?~??ta嵿Thݒ);|P'k~&߮. yt9eVz0Od{݃oi?yC~ Vn7Ni]iLo7i[։'vB ~Y:xY8ӓS?jwZRqt=+7cn<7gҷ/ďzvH/ĕe<%]l7VvC=>@}[nhTC~m^ndohSm%ןowl'X€k>tO?u\߻bw8q+:~Om+۞(4hIS'hk=~sx?z{{xgl? tt)Wċ.u/ٷ>OuX"U}[Wx+#DE߲~G B Eu)#pN5dü G~z9vߙ?߆8IH}E{n=,COz?_o?Vx^YomSZ.u t7Uip}Oz hJ$>GՏpAylkG==6y>FK\}Z.ٟ|q}sXZc=ޫBɧ>߇NB|;z;~ngjg)Md]eu~^eZoz3 X=밼/I|zgʏiٮo{֯n}ZW⾵o?5!?HEn,j|m?A/#}_^m7j{^T/߹s"}'Ӽ}z(7Ws U!vzbZw~ĞqYh?H$]<'XQig.Qq_>Nǹ~F:4nk;^Y ~4ȸ>,c'6a>Iw<ˁy~ӿgi\O:a{O~.z>u%~~o)WֿQOPmdĶuz=. 6/ƹlO~q=}I߅StD#|m\rpUqYڋ%U{6O{w+KlImϽ-7^y}ڋa򟎯6A=~߇P=K)]^ĮW"==aɻ 7ی{z7̗z>B ua<Ò~2woQ3rD#qsW9߈.*aU^CyU)>:<.惡p_]غ|~_OqnzQ^N'Q.}y^7n.?V_Bt+/??,"}KsXK H= ϔ[GcVh=~^WȺI{ny_9ζt;|]s1 }s>_|U?oy?|}7n`χ5LO?b3)܇|#]ZP{/a*q(~k{x[sb?)sAu#,q?gpCxB?$~Cľ{@a=|hn<oז8~_^ZSRH/ _BoXFKrhoy/^76'|G_;.;mcyyV|G`~5>۸3w~z~7쟳E!!SYDg\}5ݽ?ퟞV<^Ǔj=׵YH~Wig_ݒ諭(,l˫}ן\7hK: SvK^^5w5[pyr<~/ Wg}pW GoAuCrw{t묇Yߵ^w][?\>u"yko=~<[z0ZֵߖzFb?:n0c=t}yߊ?N?;k3z?w?;Gm{GHzzTOzERx{l{Nl_u,A}yqݿOͥ?@χuօw1^4vZrF;J߭RNWao&n3H{S|hw'=˵n|~Z3~ovA߭./ GK%~)d~:ϸN|U2_ C: ;}+`_`onHb{88,{T\X~볊JN/wAq~2oQKm?gskߺ2o yk(x'?ec_xGދo}Osl]{V=~=^KǃA_σt{߹r{Yo\Áo./kzzR?sۍ?O^:u??Wyl?d{>9O]Rw_+r~8ˏ9/]>wL1'BۃtX{]_ݯ:yyt;tiUGy} G=O:=.n?)=p^9_p8[װu!ϓE|Q?3+lQy %n?w{!owq;!K~X"<0yS5:|{.od7z(u]a/կ_'侔[.WDuK}r R/@!:o?T#oK%WqU}s&m/,2=`?:!2z:wrwa{~U=2=٭cWا_QVlt~ywyKlݏwN;J=v^'yVQOt~~3/cE/;׵ygx~|a=~=?l<rYYB|&?/3|vy-↑K~>ez}㾵MO/ gx[w^x^VjxO<2{|c'N /_ǎn<8?o_Qy=K}Dz_\iߙ c{I븿CڏƼV^NyGu(Ƕ\-?~_sݕқt/+ v?z[`N~N'Ov0Ly\7 _퓿K΀KڷNIzNay('P<}qLuOogϿ^ׁ~|u_s+:~UH~ۯ \r=E8%}MlV[ϓR>:}78ݯt|~~TzV/L^w:뎵ݣtQ/ڧƾ-jD'u:/:In8W :O}{3F,qҡ@c_y5sذz᪞xwIj=Bsfh]<_pz%^q8xg=^Zy.[wS뿹_7 r ?V[ <'=׭}ە(>=r4zV\>=?`_ dzt?sֻ/vB-)[*,B}BhBIIN&=" (([IGϑy Umѭ ?/>}Xk{Kqmտ4]y.}i۱JIOMq>9wU}bW$zWgXOo"Y羯 iHRf~>Wpt4o۞?Nu8?=oW:]<|oOy,I=qwI׫r"YױS&)},ٷDxȞH>_`YÔ3>n=>O~o}ȼ?{^T3ixp=e^%Y7'<`ޟ̃61ǟY xN<|9%g/oR]W<%Mp/hG$h'<1Ư }L9qy=wNa$:8q]o)+]9ࡏZo.K|78"3~|1_wϸWg|ucwahp7gxOG?zNq n~f?'O=y+mϖi{1485i|&^yWsoXy>/pLcG `^.iɮ%?%}?c)<1!+Opz8~1$6Oybū/~s$x뾭刺{/odh;mg0"Nq<}2ni\q^C3/ߠ?lw/V= Ҹ[S}pZ7{z`{<+G$= ߪ"u<fjGZo팟yyp7yxo.N\dIZӲ]l ;Ǫoy_ҵ"EƻDz}Pwץ>敹?ӌo;.;{zI8|nKEL񌬇>x:z䃓HIW={2^O߳d=,&Ʊk}ȇ^}H9pzB}y:/4.qmKE~r>[ƹ붖71'3~lO4?^=Y>m|=v+8M .JOd8d[`0O>'w?]vYcJ{p]r|y{{X۱uعYL|3&zO-WA+)/vs#{^xO~~wd1g|:xEQyƽXW:P1^Wws9c }J}.cq i7\~o<||٤WW}ܒ_'|uq>upsHz>#̿2ߙW{|NC#/{"9bݠ|ZHN<+I;_%}ngu\7ճ|&ґœ%8ɞGøzomTHzDE;q5FH /^L%`vDwxco(c}|ԋ|/lc/O9ϙ>3ߩ78(x(kM̻0ĸ ڟw㿌{R۱c))뾖XNگ x8|x}[[xIo7\H9%I_#mt_ DHczBDcQ/%mޅL[݉f2m<k<oYgX3?i}5Ǫk[7Lx{G:v:vߕ|_s~'I^Hi_8.n8y砷5O?~wym0O&y'iՇ*{dݷS\vOdrҒ')?ƍOro3~͋*N}<ߛ4o<>OyLʃ߹$}Wfq_pJ{X}}URS.p>yA/㉊{HYܮƑўT[/9ij 2ݩx r?kMyu廴[z{9*ًGa>;% Ox[d7z1>ПU"oo'/_o=fjcw\1^aqgG?ǯa|OiCROeNq|9ydޔ?Xb\74.L?)S.|~ڃuܸq aݱ]C{2n|ǚ}'M;N)|]?5m܈{#T 2?<k}{k9/ؾ(}ScMH<ܟ5/Ư:J/h={)>ǭmpO s m7/bǭy@S"-i BuǷ)V]>uZ;eI{_K?qߧu:ϽM<)%8K۽>''OagqOOWz Qos.xAScO*̫]Ӥ/)/i$Ыa78<ybc=_>W<}qʷdqNxӿGO&xfy>>`uxG0GI׫}}>K?:{|9c]zm;!ɸ0Y[7o1C|=KBoﻶG{ny ';ƱR1>{,>x7;5I=2˟RO?3^oү~KҌ$M)seqKuqmo`S54qTswhOڛ׸J}'C4m{˒).?0 |C7^8_w3"3. ^/=uw?/IHIӿ;5~ucw%!UoY9KƯ|纶˃It=7kS xHbsN|H;%L؝VizWiŁURq6.$9vD#*O2| u~܎?)'}Uϡwԓvnq0I<"?'N+>o7~e`xJUz0G.zy<}Kf;gOqO}"cX "kd=e\aB/ǺݍzF0Ṙ{f;1m/ax^a683q4<EJzG[dqHw4RqI[Qt\ks|WyWhРsG~dK|$9qȯI2_wIst|Ii$ӞyOEzy4k>cE2^C?$V=?T(x]d_*_#qU}UORwgH?%{/ẊAƼ_ iD A:YaW:ϻ>GƻQ+X?E2.o{G鎟H?tNE'+ >jc|)%M'wHa?g<02_ww ](i$wݯ6~ß0f[|Uzo!}PƟk?E5_ڸz7Ӽ/y7k޴Wq{6}n8diޡg(|S{6\׏WRe q9̏1NF?Q?#?aQ[Gy4՛qϸAG~?tܫ-s<4zx~ys[{oFo>tܠŽ&mƥwܞ|>'h6#37yxtK%Uo0;m=;{}M7}畎E3:_N.>*x9x>ruN8嘧Z} iҜ՟=riI|\DKd a =Kwy~8/82];=ћ7* 7F:.I}G)#eWMzv }Dy߃~yh/'~Ϻ?/4_uu ']Yv,y6s(vb.w9㿊D}sH3Ɓ|/Uw};}?mG{~Jގ[2sOH߸{?Oi_}R?|ks/TO~gq0Ҵ#~w;ԗzl^ό0Nxu|$Hy?8 k=ءc~8=+菞gyg&+D~'笎otB犤]â^;|8&qxOׯ׮Fm{R)..}i_{H|s /S ?a<.rcAo@9MH=t>9v=>^{Uo# A_|Pcٗǀysy2A$З\#XHGڎy#;!vq*In&I?A'}4o>%M?ԯx} V<2wx@ѐ6cS0VXӖO0O7.~y#O8d\PN댯ǪwzHE}L9!ZFU]7ߍ; ݮio߳ާ8H#W9ԓd]7Kl7U_7~ü5o ="sgkd^m}xOY"czcOOi |O 7s[yf6GG-޺ִ"A}ykloy0)EnG*=Yr:?ѯvʣ?=GƩ~ﳶv6ϾMIYu.}G ;cQlgxs$7m_>ƃ֫}XOmn%X-?%3qb/CYW;ƭN=k~3zR-fޣ Ulk};rOMk^ 1R;D%̋x4_ȼs{Y&O1s§c型P۝}AdӸjytnϡwwJOq? 7JOv["iK:)+zo}Ͽ=?zaOq}]_'aU'y8)ǁߙoḨ _f<8>ϻ!'=g><}f^5v2c|cNq%۸Heڗqs٤ .8<]c۱Jvχ7S.|xߣ~~Ho{v'xOTqnyyss)軖Ņ"k.'>Ag|_]\^Wx'^6xyE%?R9ێk5?8uܭ[ȧcs2㨶oWKc)XG =7~d_:s5nm}~*N@|l{&+}q];ۇ1󪻟~}3NX˕qL;@wKkK>43|k;W[=iڑ_~s|I6x'޴3qS4Ol>*yyJ︄;#9{}Ѽ%`_w H/d9΄uq;'OX|8|x#M;]#"~w<}[寕{_0:¸5NσҶw]so>~-S;p[)S:؉|5yyIS}=ۋdG?p,鬛5~3]/?o~bUXeHۍ_1g<㐬=׭+^~p~u:ϱR~wEO+}wۚTqZm}v~6<^_Nv|66̓j\Aʙd~_-A1X' W0_IqC?]#低 U/EJdy2oΘ߀x޿Qx;5`;D^o^LJ3Nx8}IW!X;{&_U?n{3Y7iƟ9cxz ӒO;RO{g7?pH3mLy7ty]qRU{}S|;?ǣB/%+)k%qz}>*oяk%Ne}{{QÔ+Γ|Kbgۏ4C1oCoHX%841YhG>aq-'XގkD{ pM_o}īz;F/ md]"}ߒפ|vzwzSuԎH90?zL?2|j{NJ4N#y3ߖD_4^BzN<+bP0^7}{<><_:\}8u|n`ޡ^vЮ03딖,|ǝ>k$wKyy~H~ I{^6'iԛc.]/>Y?}w_Ǎ%ڝ=OXWM:^T/ڽM'q{3?Ӄ)üLoxǪi'Uo洃\X%k?3"̓bvוȎUR匛O$F2zձ_w["{<.㮬]G{[qzg>?qI/?b;&8_r|-?$19qE ON}OrE|v>>WGR鳵,Ϙt>5]RC;oxr wDܧ} dߌ!_b-T>}cx;HƟ,9ޟs}釉E뎫1_rcļDn0^/ҋq<_V?zoh?l{d?#Y{!|7|Lg=~ʺ`{HkۏI~'C_y߬S}c1noǚo =}H#qN?v gq_A;^2c{8zv~+Izk~;c';9k:c|z#U7=k\8eO'ǣ>'9MK3Y_xKNKi=ɿ'GG9qDڵ=|WY<+`_k;q^=ڎۻ;ϋ4} {$ o=ۃ_О>s֗|y?B㟨߶JgBJ~(I/O~<<^ޏ}w8mJ[H.+cݶyf}4\g<1^w}"'<˶h5J9)sO_}}gT9[1iAO8BmE- _%G27A=wm摖4|w7_y5i>9KseK:yg=q<-ׯaƵ5Z$ۍgM>b?uBO>Ooؙ'N$f5\UžzxX.{lR/!w,~qq@no^8M>oc}?oyf>Zs'+'<*Ni>y+uŹ"O!pzbn;<~O/l[y)>'b`j[M8߫杘emxLƹ}{ËW`9±J_)zSdd|ޏ~}kߎ5]dV}J%hGSO~q/ipko/J~'yU}V")WXI}@oJ7XcbҍIne{9tI>x-|Iyo>0.ăCҟ=R|?|I{>evW }q~t\>;vø3;Oo<<3ʼ¯*>G-3W:W|謁S\\Qk;tϓ6Ns(%zk=&~(/x m}?uqƯiW7}|Q~%v~$N-=OSyN|qxG,Ogg> 9)w>w>ZiFYvD:~qi)}wŗ$ȸ[|+6qD}v>?xKGW >߿[y2oYp~??K^]hOst$~by1y~:vrϥv?>}յ#w˟/Oz[O:)o}O~>o)0N)iZIHH։8G2bCG_;ѯ *o)[G:øP^7)wY7i\o_'z3=?F-y=2h8ߞQ.y{=3ϛy>u?Ol~[q9㒌;.vϥt}Xhoډv0nyOs8қYnϳ'+븥Sy{]?e=ƛ-#IOGc ?zc*SF|<;Pfެk7<3Ǜe61cJƹ$~)rc^wZg"Y/,kv|nwׇM<JO?ݸAO[ŅoZMCu8\0;Uo:U5z[q/6OzL_W4_lO$m~p?_$Kl9K,oXzHZ=x"xSY'_lێ齅*7Layg+&ߴ˰K^#o\(ztR<ǯz_.zO+;sspyѫ'?_.}ыyip=-?YxomEzD9駤Oq@d<&3Y{ސr:+qhܼ,+'߮'u?=rbq;?8ufW;ڷ2!;cίo?=D5{|Nh;>wf/dCo}.=&;~o6+i;~53ߣ!@~gy.%i/}:q۱'sԞ;}/_{[x?@曙W7e`s5q9׎o3q~V}6.ySo`:| ~/ ,^'zy'C>P@x-|q2fO Bk=u*h'sڕu}4OGO9wǏ|ڣϱJ;yD量~i'&9=ۄ;x&?d,Iߌ>i?;Nj)iq$o?Dڳro^/5w9(*vi"c}n?wO>5ADo"{~sK0*=,z_ζ];_oD֎N)i4*?+i7d]g^xq'z<;58zھko_UyS|_%x N=#7koxڟ~v76ŋ^!~~aJ㬒cA??;}<\\Ers\6)ƃ|/*P^pos58ϝ?ONf>>v{zNL~׸mգ8$}~7?؞|=U{U{c~_x[,/]z4*b_R|{[eyBgwsyEߝƱHSf2'#(8ͣy|Y5&i/ɼg^>֫w_0%=}ko>3{tvqg~d{=n|Vn@2nЫ>ũ>$;ƃqK$pm$/?r_an#5.(ć~2JU2>qZyugPړz`GA?U΁~[}uʿ{iOi' {7)tuқ4w7Zw_0o^Drެy{$q$/={_5z-0t/w"ٸ¤$ICWazwoa{{D-E;~|~qgԞ{9c3NrQu\Y/;ʿ?zscߟ3pHw|ؑɺ͸jR<"zOi/z5qkO Mw;VY~Pʳ'|~^+?,ָ͸A<}-m8 Ljƭ:#OW9%SJ_烼}p璦}̋-6ߦvz}#U}{3J쇜c A9љ?0%OQ`BR}#H}z>~_cz3z@އ_4NG{,Orgz;>C)޴???Xƃ 4v:;iWizyqmM畞x][v~WUW<yF/c.>5I.=>w =mzzRR/%s6bEӖn_]W^ES|#=O{ϤGV<}Mh׿{ߊgmk9^?޴?y/4i| }U>EG1ab<\"?'TQ}-g^ ho/$v俣p2uܥ/uXc{m=]sjw Wx/AO'HÔ6Q=ywoϫ\_>Jr`~jD?7dqH;Ayg{T)o*85ag]u2/|H=oZO?} a\s_"i]-x\ڌ&i i~x/>U^N6x-iKa<~|dyqX,zB}޴auzSƇ:XNuXW?dG4G>g='G} <=:9kKICU>_Q9)EzoQҴqSgn1oj:a?wK0nzu<ŃI_G7<ǤOCYMO >2lޱJ_=R%kOƳk;^vuDO~/y·;>tܖqrG]|/akJU)>^.|&v$>Y:Gm]U:M|υyرصiפﭷǷqiw>~'s~:q7<w>ױh84/vfac/͟q<yÎ=\_ ӿ ^2 Wo{@i&IO~6(5Y:|Sc;t-ozmx]'3Foxm| _s=FƷ;t\qPϟU߼Wz|;6_;~Uwc[?{}y?Q)'0<~k8e~8w@ \3z ϓigVz|}僣hK?kG K;=>}OI%3hm>GRLW_}nP_1n8'=?z=ag=w383$I>Ƶ&m?^oߟ>m#^??q77;D]'|_y'[ˌ|E{'zQXOҷ8Veƻ}ށdr{Criq|/ O=^?>H{y */L& G|T#>}Ji Ǭ_ߞ}0*?'錫¸y\Ǖx}[K9Yxt3~ &ީ=Ƴ.Oz3[m;{3DsϯCg۟g}BI&%)oKtKnkx/vģ1_aԣ=U~(v|ǾdO|&g|B;Υy'M~G1N{}`\=+3e=#Ja3]h/3^E=/?ďP=˲j.>ɺGG_}8NCr֗ۏ244vXc-sa8wߐ}h?r{w/$:z_?,ç3I#\#}o؟~g{~oyS/ y8nI~ձo/x1;_cƣ|~M9Nu}Kd<!ζw1Ǹ)^x]:rN|+5̫EO *o)N}JGcNG>>Cϧ{W"stw^G2&|GA-&ݸ{})L(8%|Fg8gGeS?'u[slǗ>qc w=ry޸_?7]Wyyy(zpI;~{9V?<7o(o)帷SY?=Wqsߟ՛zENK$~wF]y=˫%}V}|]gi:5nx\̇$za^*rN~sc}94Ey&]ʶJ{wS6B4,'*G>]:d}'SoO~U 4YEz{nAA_KGo7Wy%8 =VG(}Wܸ#iEɷm?^gx?>Ot>i ķ^0>7ݛ|0/q/>/(_>ƱY(w2M5WNmSz/0Hin~ƫQ|)ۮwszHp9~#z99u8w}/S|IyyT{[!|㽋ʧw8{{8_ƛ=xȉ|N~g N;R߄:L<WJOapO~;yg\qawG2|m*{?\ǃMt߼_si߸Pqc-o*H xD>k[XKY!y&a;q+/܄N;[ h/]X| w<qjޚxwO=Ϳ8p[AG*1*oY'86<>㸼}Gwy\ާ>Gy8fYi*~L']|"l8WIO*m||$xSqPO8s'[\X%]Kz؏s|5+&I{`/s^O<z3|>q:^,?oy~*׊$^ ׼,#幯/yϒi(QOWUzv-^ri%\}8"y^ }=ˑ_o#Uo$`/X^;' i?{d?s?$ nc{a7."vTvF Gy_%\q_2?̼t.W{,|W=}q}]U/ۇ'Gw9w~~>^%*<7R.q`ޕH>2sCyeއ7Ʃ\=>OLq.wKڸq g܆r%}9>vۯ'-?a}=_c|?|wOǘKe\X& ziw֣p{w|ρuz0|k>UƗ{zi/בH{2?v W^cMKxh}{Wǩؿcbٟ<z{9"HWqKǪiexi|<%ߡOg'xt=~/~'~M/x}w;[|7>_mk=ڿ | |~*w˻)]K}/Y_+Ϻj>󍣈<9ſ-߁O򾔋|}nxd]dq}ԧ}տe=!o)qg~N;N!y3~ xHHߒ1K}=<7^|ii߿I'@?P~qYKd|}}&}S?t-?0%>g$~:W۝t~^zൌSYoy*g_C2H>A(>ŭurK'M$/-Si_s]G#m^KHF_{Ozr_!-}OC^"YO>ϽJNӽDH;nmJx:wڅ?V~{lw/[9ͼ7(L;yc~yơ&^2XtKIJ8f|5vy8n8ËH;S1Cc^S53wIw>WlK.y˫W;g3)]/Q?iS_c5vE>}r&q8.wY(/l<"O~~vynP~b$>_Y"y̏Uv?{UӼdڔc:$k ϩcIw/r[)(\< e|a|4~B>m8}~ ߧsöKWގ*.ɪˡWekgƇNy]I/޶˳ȶʵ]׾~"?Lm1 J{n|{ܾ{9qmk9J?KƓk7ޏ~IN=1^ԛq{==O'Byw{ cO>sDw_6 r 5x5i3J{>2GCH{1_i~O~bW?so^:Uwཎw7\U3/{loCf.}mz_q7~Og⏙G=c^_~`(DZS/O9ˉo;?3_ޮwһ0V9ݻd|zMto{<~ONdYZt?ZDFֱ>g񯒌' {E>uq |{||&m(mӯïHz>vZO{ѷe{Ρ4}i wyϭq7ط};K|dt=9>Hǁ|.gz^>{z__1>o+='\/R97^3}~2کNa\O;7g+eqHh˶;~>u}oԃu{"O"Ui<}dN8_ǃ}4 "ߨī1?rؑw_%v]8?Gh}yyWXsU.koG>G`*Gؽy{OsXʻIfҏگj: /lS9x涭~I%ٶ9Q+=}\u;]]U/$ձ>'o/>ΥX7sZǿt罴[㎤gl<}/CH1y8JsypQz'ƿO>L}RpQ>8%>s:wf=G;<&'~E?z9y<|ϸ w{;3#[os_8 t{=9O!qZ7; |߼*s٤)u8,ǽPzz!~)n?~Gg>gysw@ntT/zZk"}^c}D̗(.tz_N|=_d{7y.̓oI|Gz;}_헴rE/x>*!Ͻy!ƭ@;\oZҌ!Ѓ}HMzz>Sx/GޓO?1., }||j_U{{uɾxK98󉇥W!ivƎbmx;'u|o?8b=281m,zE$s#Y3zZ?"{{Q$ CoUX<3i@p6~)_mk~4su/ z<ŁWϢR]yx|IOo/S$ ^M=_Q='L>Ez0ݾHM~HH㳝Z.J>( y̼3*ȇ Bf?7_z> ZOo{P%x㶼}}Ny}cS|5{>y4WI~Ǘ<[7/B"};^˒`s[#UZƻ'=E瑌·qbx~o;{7"}jqGod8E`}Cy]lsi1n\;Wz%}9~c^"D/E ƹ"+^}]?.7ŋp|c9zb敀/ѮϠڿ+L|MuJ=}NqVi^)z7-D1>{^+G8~&}͗AJO3OƇsz_yw6Nm=G*.{$4uQ$Ϋw)8Czl]$ qk^CпhwQ/oO?ϋߋCo/S xߵ>Z?⾊wI1^'0x_r=_nI/`ډoļ]{ɱ~;H_([|ND¾f4~3n-u>ִ#0k4nh;z^"gh;ycC uӸPc&O{ϫ"2?&i}UPiiϟ>Ivu2Hԃy}4ϱJ㞝OyOg88EzgytՑ[b_~E?|Óz'D=Iدgu[^GdzgxyyQ}H"r-؏ƍgq`HcQKdsO!˛'H3ݒ6?hv] ގIz1^QdJ9m'&K>%S?Ƒq}$MqHocW߃q)xŻMoLLy2ԓ"98щgq-W5#^y~̗a2n狧!"zWqyqtMLyx?i}xo1/F2'~4|sdYv;H'n^~xkIr\ؑꓴy>{Oz܀?y>ߎG1.TAdC&}& C9sYAۓֱ49_jFZ<I.i~)ms"oV=#G$`~C}_A?p+=SwTA5^`\`0_Ð=+?-2O;ߓ߸;|w<6#-墳'iЋy=9;G{#7mZqF޴דߚr{ҽ/q_(8qq6nyI{e>uP;/qƼ+Rz׃J#{#E_>ow'qIck:|U߅z`7=22_U-̷+8ⱖJ_<|tǙ0'Z?UHƇn<.AOy:G<*il_>s0n,vs@/c'P~Ҭc/a)c?+uz2Oy>\-ٓ2;۱ԟz M;O$:+9xUԋu0.GyDž>w|u_M][< }"`ݾߥ}cWs[#OҞG'ֻ~և=Wsd`h[Xӓ\NiSoz}&}͇4.dqarSHI͜?F~Wy2}z~ii\~qr:IzOv1o|۪[c|3k.㮗mw<#ƅzJ' i?)l]1qN;'羏$q!Ǿ~~dwWy'VYN~Mtωo9~"'nN;޼s X%NcOS޿(.O/a|/EEAYϑ;ߤiׯw?*/6i#zSG}4>{>[ex/H;UU}}Ik?|HωG:Wya%?\S_1Y]ovI;ˠ~Ds!R3sm_1~߷y>awK ?$<$zGSǪ_}Uc}??omx(Q?6ߛD_]`ZE$/S=_ri%uvJOC]'q^qﴟơi_tn>ޓf}q{Yg{>e^ໍ'zQk~ͫ,/ii(ߏ[< }tzڟs\i-y~=_;yi f2;/*ڣeur}S){0O?oq ;4#{INx}!ho|:ۿTO)?XR?vCN8>e|yuI :92HeqyGogUggߙWYwQ~xӉwxo^̃dvE#I{]5 R>cn\/EN|_:ޔ=76na>|G3om\v^/~_\+xĉqzga^qs *g~yp=VOqI3{9z8419?Q>z8^k==u;5my}Կz<'}rZwߵ},.}_ =ޚO5 oq=- 79lowU&;xy|ƃ̣O=|a|il$nyUNq+,r*8pGw۸^:kuּy96(Oe ܏ߡ7JʽK>897(hI!ڮU7ɇOkk<Ws?7i  nD{_3w?zǢWIc>[8>,2;ŏnE&]>VKYy`VƗK/?Ҹwl#}kGEzc=+8;<ϝa_9>q>>(1DDsn 8cƑ>|㚮(sWVHA{HOƯy5=?)n13яyE/ڌܒ6&igfxf^K;_#*i}gt{z^̣`~gn\8jyViqh8/$8bn뿣_)nx_=ERDy{ӯ+N9ۉw||'W?4~8~8zzf>8yN~'?K@ۢ7 ;v3RxD 2ʣR>'1߲ͣx"WSK>8H$7c&vcKwGoPkM9ڟz!['?/'AG[|qx=L)^>vIW2N&=:W>q]o~6|'ϑ'~mxjvO)[mwos|[Ҷ{}}d{ͼ08OFiHC7{njzzLq#gEٯ׸IcM'sQ9vuyty^gרr0~wsd|3~/~gԯo9);zﴯX,d|z}{|_G&Ty^>ƇݮW=ߚw\[?ů#}y5[_+ھ}f\q\HFO~nGN@!97JGz_0nwz4yOK?|;$}zn^ۻ|Cr[s\3oJ?z2O{}~k޻e0>EbG.4?yW'*8=^Սۡ)oq{kO +<&MpwCo~w|yƟ?Ƈ}OU=w=[~/F?[߹mz(zݏU_/uHK㐒f_|7~g~^EҮ-%|i>ێ2ߙi1$ѻߢ77_z#]o;2^7f~cS]ڥH~K<#yN:<)AzȎ/?4oq_G2~Knǚ..>ԛ^#~bd׺z{dm%.8u7F[CFo/O}#dNiޝQI?'o6D?2^Ei$ƿ8"N&ʷNfw|$8۪i',v%C$x=x1zЎӹz3m'i~3ȯ iO)_-8˙o`\"i+zE5^iڗvg:2o%󸼩)}D;ڕqd dN1<ä%הg^~ϑCP˞nbqP]$xHړ I{ƞao>o6N;0N$TyU\#}v<4_|ț>'ˍ;^7&]G._Eoc[o~=x}o: =^|=O~WizҮ^Fybo`dcZi?Ɓ,ƥk3m׸ F}̇x=K/{4N#im\__ wcsTiqFgy=sܒsWkybiG=w\&iD/!]'budU?Y%~__V;kgw#@[[_T>gܤ8;ű$ϩ\IGGd2&?ywm3.&qg>?7\'%Xs#3r|w}Oc9(e;2N|gm''aKge"Do}q>=*o\;H8'1OҌCo8qΟ̛@?com.gMv]!n>%}i}da=6?|Aodmcע>#c\( K3rY_$?O߬;?~W7iqK::S"v7^˼<-=A)1zG"'5xκh?vu{]2NWe_d]Jߞ&]>Pؗy 2nq`8wp8<;q9'#|g&f>7(#)ΐgIr;VWHt_ӱJ~ygdxVI'zr,/sY?R>AӺ8B),o뾾{U g~ǸK650i>nIW>ĸa<4![Ƴ[۪Wq}WC3{W>ybN1gD!zx6˺ɇٸt ZO5s'_K2nZ D/(zz9̫[݄o>S*g}܁z=iwG4_Ը!GHg=b{^#=9oz^JxSm.Iǎmq8Svny A]Iێ{sJ9Vۯ$>YC'>y~'i6H)NqmEӺ}4 {t@7ߙD{_#!.IO8жCUtϾͼS~t_ʄp*=NMuҸC";痿s$|} }'Ai'&dx+r~gi[q|=쫾_7σ)'/1l>om0ڼK$m 1~^ ?J7={s煍?xIrYaӏ|ӹ\dM~/i<{:~=Na,L{}-7iq- D=?'^ ⾦}iGV)<;cXCCUץ~m|zt>L(b4{y߸> ӼJx}P?{θ8q559rڕ)gǼx/J؟5 %Z/Cng"їx;.Bctz$x$x/ϙ_㎵ӹy[_P]?{zR>Lş.ʡ[$o8zqU)g|qSۥџv/6 i?Xdޛ3k<ռ>ʷ_jzO=]UWI}|?sۅ|u?%q`=Y=U"G*Fou3n}8|B{_zO,8b=D2?];~e> g5_h^owӋK;yxǪw׍}cӘ͹^H ^zB:Ҽ2'.^ [}OG}:V}{_~~&\-8z~S9݇#oez~r=\Ϗ[ }/}ڏzۛxLz>%}jW{8[uGo|]8?(9>8楣#zMx;ҵ6>gqnyi8)g~,z8>y>AK=JۤNwϖOTqD3pS}keM>e|=ӿo4}_+{~>0> xGGG6uo7>~qwv*2:?U96羭mǪ5|Qyݱůﯕoq~q{}wZ|>9lqiݼwCKg}I9+?V=mo,9we%L|q0=+{HD9?!ЮmrP2cZo44n5]$~Vy`ȼz縯Ǥi?";p{E_z;|j?E_@?}"_ޟέl0lW=}r!'kX7@ho uUx_Q|*iϒ|\4uc1<'by n=kK~zE?g: i^y3_p+A:^,i*ƭrZ񶤽o^}N}3a5^/r赯><~r Eymym?G/~yoWOIEx3s[㰝ڋƇ|gPθ׏ܼ]퉗I4c\0o;+αݞ;,|zIݤ52Oss|S=>OsCq蝴 ׸HcaFO?b]`ݎU_Ϝp|}%K:a;u8qtyA3yd'^~o,mv<ӮǕq6q}'t;YK#t$;\|]Ei:i^ W:HͼuԓyuğI>)'=&m;_0~h|{}ӽ{(wVY~CoL9=9{5^}7~#i~LqI9鿺|K;P^ ]Tޏy7iڿ _x|v'ϫx{g?Ǫ'܇}#^ru<W} y䇔caaA_3a{fI%öʏ?sp8%l|)YޟClRθ;鏤x6&E}x:wuXԓww/?{}GwJӱ>=wܝqCƛ<=I1K~Gaa}7i'O}Tzޓw_^1 /xt'5M89~|A8BڟBm'z ?v3x4~5MA{e$~붾~igxw3GqS,=咦2/'yi+G_laG>F0νې>_vqc+m[ff|yj>ݮ?W(;d1Φ{wuN8 ߍܷUy=51vd$|~d}?O6}H猃RE~O:K>Ey׻oAǪNXNo,NzFϼ9߼۱JaGu?>?OI^U񧖶>R># ?4|9 U%GDboy8a~㋚'HƵϩm2>ѳz'8% v{3mwQ;x?HpEnzϭ71??t㧬g>?U߿O=q'5{v鏼>{_e!2/iWq"'G)"z<%]SW9=Y_:^>Hp<z?ֿ\dqIWcپGTHyOƱ; HIx27*W%iph5]w5|Joм=㝶Y{~x༤35{GXƯ2niS>=:b\v ٯ>y£X3~Sm{ݒv{6z_O:vn4>`i7#4i1W+Q/sݩܴ>gW9N<^l$Doۅ8OQ>ƫzx\X/ޮlIG}ץԊ;cci\׾G`c=%A;e;Hpu{aoI`4@;89~m;o$Tcͧ_*''CZO/1NP=\zWx"!_O$m<zG y^I?|w>Oyy&^)3/}G?%?Y;נqۏ_%]^^ҍz};>_y߀t>kB+>º?-SהO9H3^i/&ɹ|p펿COՋ{Wf?c:N`[-$I"RFyc~o.hLJuNJw=q->{0,YW"{ϞT~}.\Q)^I)r= џnyEz__9;~}tW|Yo7oKqRѻ!>:b{炎7=I~Kd**'v,8?K^8_b^#io[edиҤYwߏBk8=f=qCP_;l Iq$±I}<-ⴿج?~_6:Ց)8*V}ýnI?YoٸHXG5yƕ6Ǜ9dE8b73B? l?7gć̏u;"i]HZXq.4?EW}zޑ=*_9k$s}7<ر˞4)ļ~r8H CWz(!sxC3/*S4/xϿo ~۟wU軭xm0 tܑ}/qpKz(W/?=/=G:_$)zd{ԣu޳'xe_GoXN+P}')oL>>i<ƫEy^UEccߧcx ^_+o}o*྾`x}?ByuyE61ύ3o}noy3/R>G?)Uy[I } z<ҿ<|{zOy׿Oy!k3N'NyJmg||>ǚ_ތW=g\<:/N>۪ s+~F71?~C ng~^>z=%e;~K|}8a';4%N~pKy-OPkvmp^n_ǹ{[߇}T|HG+]?k}=w;ٿ-Ά^ws7$e\9vyiS>ͱ~$s<zcg|?=h=7d^Iiл~j$ =L<Ѯc绌kyV;󰼅{Ntmaq_k>6<#mj_oj^'!/&q'3c| I״ۣ"U>o't|{ēcIySo|^]$8~0֓q8e37_y5F6 `o*^ʺzN;/G'=E<959Tc>w~?%wA7?k}zIP3d=}>q^QZO^G3կN9'o}l'#ɼr*vcoCHp1%%=GLzG&t}}Y/>?pk$%ź|%y[%z${7:c]?~?X~"y{W|<_#{_>SJ{kguI>_\ץjIE*u[%MGo[=z=s{+|\;WHb"{nO; ߒ(߷=vtv~qA?C{._~v7~s7}GσUM}"'WI}{G$U?kпGaaMECšK-W|8Y 9ۼϮ'g\_V9w0_NSȉi\sdy?w4V\)NEH;|wF~"4xQ 0/K-A5[.|5.semoq)#'txm-s~/=a0.|M_3o}r']j==Пq*|ยsH/9˧J>uؼ~_;_ɷ3|4/)]s\/657xz>״3wvK~GAo>ho3=D2>~^!s܄Ҏ9>۞_*IPy_1qbոޓU>/ҼC$*˻}V9\yG>;>_>QnzoEo8ډyX?=4_L}G2 ۋ79S|Ӷ}GSƁ=_i+=sq!4D* zDbO?H=iu?)~98k;ѧ8Cҵ%'hc<˼{ty)W z_D}H΃ͧ1q >~7xꏿ47!͟j'ϑr߄|Qį?w:r:}mЯ'n/)zN3>ϕ}%=qƹLJq-?7tj_윞kQ>ͺɺB=.ƍq(/9qWܬtG)I_<`My}~]K<@:ƿWt:sߣu<H}=7Nx }3>eaz:UgbWWpS92 =|qUʛ¸1~b>㤺w /ǧ}9ǣ'+Ii'>?u9yףz"%#_#i#}iH^Ͻo+#ﶧW'կqyy[)XG~qR)iaF𢡊~GXx=-.wv58zxN ܕ~z'r>o2}ybo>@pf_Y+ͅ?I/ځY>q_ey;ƍZOd9&wUƵeeCY9v:z]؇=iƯ-;##Rc^OP9f\HY;g/+xÄ3^0۷{y8X>8K|~[<ӼVZ<Ɓ&]9wkhjgyW=}/Cr##iw}>Icx\=уqM_xkB;t|$rƙxa+GJH(4>{/kƕ9E;lH桹W=c}n\ ?i$͸ō9wuFy/?ww$Ec>*5 }}ߙY_#}̋9~<߷UI{3nNlOx^{C)wd$ٷ˛)gOqqէxCq'ˢ$~=<ǥy;x75^%w_{N#{wOy+8b\ïg8cK=T=#/κNc1>׭ޤ}e)v~YG'm|̸MSގ>EُD:.t}>I=ן^ /8?;Ɓ.zNZ{F_kd?wke/g/푏CeC?!xσoCsfQ>ʛIyޟ/,rߎw+:nCSry ]/]'A9O8~Ƹ{/ }^)P?oɷ?1f%{~4^kϻFb՞Lt^ǽۡ~mhUg=B?4c>x3>l 9VNG-/f[kwI%9ތ4z:gx#81z_Ӡ?G ?3"~}DӺ!p=z.KkҌ#S|d{vPkxξO5nB\%/%M.X6Rñy̧*x*ONҼx78Py7.*YgX%'z1\7ەwׄ}<~;opz;մO.΀,}N~.I~~G?;va0=#FOo*KS-i Wi>==ύS>v0ue.xD.d|qXozw.1pOS鶻yKCWs}h!?4_/(2c||:E#$^igݸEO|՞츏>Dz]n->Ez,2i9 d?32>="9)'|ƸAIIk~G{#4_i_"UӞ7cM18&};V;Pig/kͼ ǩ'42Kzsz~@OfG:ִ|};>'g|o7o|XOƱ(䳮~zp_%:/劣̏7˾\dL=Ϣ_YPǝq\^19=u݄\5걭xNP|羖kcg%ÉSq$ÉGTO;COa.EyGWM$8y` }9Hs=<0d|gr2_)|3߿D:W|ƾD>wG?GD}߽~ {m=7L}^b(Ky$Ovg~޾;w\5oGO3s{Sʁv{}ӏkz=3D99 S;yK9Wq^oo{&;vq8Ʒ7'8ք.>9{ɺbքS/q'g:x)xBo({l_1n^-}l$u}<.Ci#<wwzo1?[X5|<}}エc~;ݷ{q[~f'7Oj_'3d5}O'|H)_"gvU.W|7I֝zxؙ߼7̸kyC}THϧy y0n|d{xSs;ǗyϦܶJgp#Lviߎt>8Sx~ BץHp'ǟ!zы;gl۾3ԸNgIʷ'wKXox>匏bAMًr;_}vCUvtz$M5'q߿E1mg=m?{1*.֋eKsrG4 Y~_[-ﳟk}'IYzޝ8~MoC'Hmp潙f/OoO3OOoTqi\ IG[^/RO~.7q?PF߃c~GG4ޑKQ5KNq!D{qxڛvF?|M| 'XhgCle3_#{.ẓ2~mωc~>Fҿ艽e{߬_|.巵V~@i^>/1>a|x_-]KJ2}cG9yC0?%/׸#qq08/>ViGӑ}Okͧ>oi=T/OƯFOw9+!kJqO$ɿHo~~a/^P?QmG1~CTϢg!s^#zw%/#?}<'RR'7o|>g#?7|W'kx?z)anW)|Ce<>=ǼoG·oH"gXI$:sZowu̟Au1q߲tҎ/̿|@?n潎A]ƭOTͿ4nky"}ltk9Vy:mϭwO秌4~qKAw7_z;N|Qu8O> 1NnyZ? *=.wk|}N4>cKc?LM%W9$J<ɼB8\;o"P>i? qI_uuZS۟~1: Ӽ~z5ibZ9ω#=gu(?jO|_eȞ< }^qo#ևz*_˓~,_9;u87x-no5n+~p_DGL};;(b7{~4OOM=|I~ʿw擘oܸ5ȶo@_IgO\~S?pZ).xY}o͌I%_Nas›w瓤JG޴ ;v7]L1Ow]{ʳ/|Kdo>K?iO{̇xgG_άSy;UOxv%Er~6/v Nxq|>gU_SG3qp{[uz^)GHǩ7>KqVIy󈬏m+W?ȯIO5YO_WG2ռ5.s76O<Hݏi\M|\~;ٌX3uI$󢌯6g[ǹ{aTO<7߄WN8]"=;??HyПs@w^}S-_&v0uix]'4^{>kI+IA?g[K?k'i_uv}~;i2)3wOJP?e3ʟ96߄4|\uI%.zsXe$ܴˣoGg7lF/ztlH(>`>~FoH3{ SO$NxiG@o}O|(~O9yw|m=7Ϡq/9Oʃ\qT*dP4g\su ;. >Rz5a4kw2HƯw'"4NX3.x sO/@w'H})}}?_8~фoeLwAlwwOw|nzWyPzzIq튾c|1?>ᅟxx5Ҍ+aKKSѳz_}?ih}^eޒ?FFk-?ŭ>/?1H:tכu;^_wǏg\k_o_P||_}/g[obo4QAd>(g.bbDyx7Sϑ!i֡i~ii|LJM#_?=~hk$OMǧՎJggFKy}kÿ8w(i|!voyTsQ[q9H뽍S>4^h f 3_8a~FMxv\z~;V+WKƼ =n屩\<|85O|{_Ov~y>]n?Gڕ~rϙ=^Z7b<8^c}F:Azݥ#0븗*d* 7I5<8oLHH=~{b|<3WT @Sị8Ծ,ƅ=M{ޓy~?:칔/wi?4/v4xL9qi8 ϧwwe?3It?9i3bH}KqbۑyQ)8y zٟ Yo=uDOOPO4;">"w+o'Ƴ5$i%-~lۨG'%͸af0qP/v]xd]sc]`w0z3U qR˻_2lށe8)yL~qO}ףԣ3)O?zG>>ǚ.W;f>ž7Cz?z{7sB ~!o2F?wSpۣ7}A ~HGX}'*w|q s?~U?xG=&øYMq%syN}0Yг~ykˣqG9SiKgB,.ߩw)k2[ʱ`o*7Hs3ŕ"oc|]/OD=,^O}Dzwy-'E§/W~vzyE_Jٯ7O9v?KbM>D/o|$'M{q_$; G'cʃS+|Gơ|_]>ҷ&}g_ߙ<MM۝k1_|G!>7/ӧZXwA_$xC}Ky7~}yyo]_yRuFﱴƭ=Prũqg]gfW|Oַ^ZI74}޺x2y#7Kk<~>=őuwU')NU(M9vﶦx:n=x\P`ܣu?b;q\&<I_$ dyH-͞[`\^x4q=H~a~EI\pQ)?@g^=׺]??HmI=qwd^|b/J{KϿos9q>Q>޿Iz]hSzy_+.o[}IUV0g~K$vGHWgvٶg{{m F/m?=DK9}w|>h;soٓ~z{տtH9?Ak$vTc}n<6;[;slgn8{]y>Ow6Ճzz='OaүU.鮓yz9ƹ;}~rHIxS~.m'ϭFXNqYK7*H纭}~r*g~0DZLv`To>iq'>Wyl=%q\~H.zDmz7Nv,<~}i*4k甾E=i猻m[%auq MY_8|!Ms=wly|s9gڞ8gi\ O}<?}t|Z/"=ɟpb>4m0N~%]zI_UW9ϱg|9YW}n)X|{=4z_|Cn#i77㸕0їvƍm[%ۼb?/쑾|<9qnSJ3=7SlwOt>}|x/f{Մ6/sڅv31YW}j}jHq}q|4{]˿kNno.ƅw]V_gy}C>_~·;<#.z[x{Ҟv;e>?9m<*}~&II>ƵGM}g9^w>=vڷ6-<d0ÍEz;exJyL|Ozz7|z\mKi#_o5/m?a^\;5=I::o#;x^0oGa8G>>]#9^)oMx[s2̻ioN<siW 05or?3[R/6nqµUG1ԄgZ/yʼ+?۪7v?73F< \p: z?<6]^Mq?۷N۞u؟X=/tqD?8~l:4?xb|۱uyx c'^}gv7m^A}~L;NdD?x]:{n^9M?y6HYy F=7} HE>U%vΥC+NzNǕjg?_|y%ҼҷB~`jC>A;xc~ye>>DZd9]^D_'yHo;yܟӼt=\?GyLMqQ/ro5?χ<+|9؞`ߜi\v>C]$m^xw*ә "ߊ~͇D-|dNOz{|Nh>,ya}IN~gi?hjKJ2O3'IIo˿w.;3N^__rސ#wܢϱz=޷A{俬Oa>έoKS9r]Hy~6/!rG}qǎ󩞖ivKov5~}ON{zJ7볭Pa2.*OJos=N\N^>d=9_$OBwqo;~m,e+/Ki}}:GO}^_{E?dw~S={ShwƋk>=?P)x^Ǒm>v׼$to37|//p2["p$ꇾQ~ jĶJ}81ߛ7;i#U?ER?K+J{0|oq#K7.w i~DDƹy~q#E﬇}w{x]ȩ]<=.ƫ>'O3]ga}-d_pO<$4^CyOcEuF;z%ͼWnn?;Ņ\ɼp<-z@=7w1OT!;$>x»Omog7>a~6^:z2|p] l[Gֿi?ՠgL8%'WƵ9^՛L.d|gqOi~L9?"Q迯OvXy4RZ}g3u|PA}q̸q|)p[uVQ3/~4ѸH#e]c\};y?s~>|)N3j|/c½~}_:zNvg,7~$<;cO:>e?O!۾y߱1ycƇowؙ⌦o`|OHDxڦzgvQyl?9\o&ؿNZ3_8ǿ]]ǽ!}??Hs)u/zz }ބ&8TqZm=IHKXe}v|>4<~6^jIsƭlj9ENDg]Ϸb7IA/F}z2eyl/yߢmZk3ĥǰo؟x ?4sڟcUD^#;>K/'I9+Dd!'/N7^dގWvwl}8P ~7ԫx˱DI;|zU綶]~G9̟:*sXqq'w}[wyt;!}k^N +*oEf\_vU?%^WQv;E=֟z??+O\%;}ƛۇhWk#MqV|j?CO<.@,7j~q.9j^xoͼ%WK?dߗ&^'6|׸{$!Eq1㋤U+zҮޗm7cߤoQu~}'?-<7>q vIc/c'w$z[?DNaS#/Ly5O$ƁH>?(c#/o7.]>HƷq9ՎS4oG\(Oq_w}> G88TS>6o =i紷_4q߬Oi|uvW~eީq|۳vD?pMև9ϱr|q֓S5ȸq0Q^6.} >4v}ۮmRٿfp{NVޮ6km?޿7aREo]v'Y_Ҏ?W*z0X~|OKߘw]?Sg_|U.k.iSz%_Ѯ֟9xC>e=0IG7?V99ex/ɎmluÒz>zm~58y[{k8Ŧ6S{Lx2~|cAuv`E>vxCȑW3'Nn?yrOo]>u6nG5zsH+Ho|c?ԗyV<&R9 ͧ6NB;I1-|`K`=.iMпyoqHe}Nb;Bț-o]%G#yy=x>ϙOBU[ppԫq_yNyk8 ۏ]Q?8[[s Ю;Cٸc?ތ?=#|!|K#;|c}yv=V}mzL8PˠD/NH&@&;"^O~5~}׼?|6ʸ:rW~aS\7{?}ۛ(_Xv?{[ޙ-=qmkx1ʰg2oe;w3j3e^6|µɟt?㞎xq,9.9L~ӹu {).N-K?k{㙇1wIϑXϩc{>z=IH'wcrLuٝ6EQ:EREQ"-eYVeYng^Zn~`c0<p>A~|Q0BB!b"Vfg[Ċ#V[qwp<';vO+~yuMGJ^Tq=m?h)~hR|̥}{}^N6(Zwj 6]d7e1G3칮s{o+>:{c?o1 fJy;|ngs'ol9ۯ[z>7_~}AuYהY͠_ _?=z)_ۃTסloOS8ݞrm]wTpTY8/tP8H 9˓8gzd'sۧoe{={uoŷ_X j^g>q^PU}AF=Ky)Ol|+약 ;X?~Ǒz;qkx]q_O#S8w|=xʋA:cKd{:_q{TqZ}}+_t*ϸg㰿Ex'.=/m$hy?!wK;osq-?䯌~ j? \k'WwA'+yA_zܩ!}1@(ݭ1)x]Mws1_m_FUu\SO|>#BeLyDZ^Y3~_><5{CzB +ΝaOGq'*.x!vqA|v)q=pO؏Wzp0xV8L'ǹq\w9oG?yn7ZO0v\iรn_9~=Oma;fjl>g~65w<^˸bEέ:A'=~z}SGd=uCT8_LIj=?Vq>j*~q뜛U8Z}*Ϩ\bu۹Qw.>t 1tG,/EU͗{>9`­Y] yw|3Ӡ؇?`=a1mDי/ɳ''Ewqr uםqQM_0(8vθr߱zۿżMmPj=yq._0(ߗ߯z<.(J_!7A]4xv@qH_'A|}gq ]R~㌗noeˡKyvܛc_h#/qjk<z8]/ҦƟop^EwDžW=9u?Z]vob|G?|b3jq*Pu Ǚ{VTzqw+m9+~F>rĤv׉:Ww~D/.!#o;/h7 _|I2|*|w /_o?&!+q{>ֹ>~~w~W1O:WY_w#ƫ}~F{;UA:p Ν G^/[l_c|ۘoo8qx:cy'''z렻Q K7*{;㮃2dkaFl}q/To{qkn.1~PS!oa^_X½m9f Wzd;N)^=Ul/}?]~iEz\~[m.yom <_8[#uix;_Vg|/c]oI λHhGCWĕDɳگW{O me}5A^WINw?!|O u}Kq%Az,wӋ_ɑK+K?F_Mx?  a0?/D r 7x _ex;eMpmP=~}^§~ۚZm jn2^|n?lc96]Ў'~/rlφ#b;Lyyml]xymw\??s5og]_?7ynx[^=lGR.YWPب\k{Fza;Ϸ=m只߻ͿI3S/L[>U~5ۭ?om[=<* [π:/r8 ޷}{x^ÿAgyWkmf-\Ey[sPZA{o_TqgVUq^xǕvljnbv}_Q~ qeW 8EvjŽEW#i}[Q~9n*/U@^WQK0ǑUNycJ?_[>+|Q_~8ڃ3Q7;z}ym=OkA.up?l:7^`#O)ә~׆v¯U;AǁV~pJy%/)npCMV;v+$(7^C. _YB+;Gփ0l9>c?(|U~&o=*k9G_ů]Ǚоkk Z-ic3=?gYwqe;wݯPƝi?/m; y9+<|8Πί~K/GY}gw; ̓)ꚂxyR_:w[Q{Y-~^S/wf9nvn[wFqr>ؾ`⸎ރOQs/]o :a]d1~h/osg#~^tϸԇ|Tt[gygyq$?b#͏}wiWgb;}^=6lڈZn<ޏ#a.µӎ#j >09ݞ{\?oC_eވzc1oot? k[;jŹtܯ?Wq}Qvz3/k׉[ZU*; >sbec<0޽/qe=p83Y??gmsoY?֏Lqy*|vB &;\1y0)wL+.dn%fW\ j{LJƶqjFW\U^Qeks^c=Cγ;08.C{TR_=rP>83ߌ7~?xP G+hwqjG~ruM{FP۽';H ~.-g5ο{؋zɿ?q݊}9>,//!_ |Ϯߘo|]J;f=OTssuMخ5@\x?!Goh=0m`<9sz~vAuWCMv7]qz^vƃƅcVPK`x~kq}*njwỺvxvy][=#.<xlϰ}wz?x?q|1:>Kc)޳Wj3Oω1Owmo>Po;Nq_~leWUi_gy<չl {B;Ty^6qd/oUޫOl`;;NuI^Ou/ÊX>Bm'b5e]>f=vR]8_r??{;Ṡ| f1ţlܩ]hAG|:v 584- G4[y'U8i\OG>ho4ߎ'?qaVwnʮR=N,hj|*\=y>v8ۻxMCTuyOKೲ> ^Ǒ]c} z_2 3 WWqs82؎l9ַ?ziGώv0Έž:mqb(/88g<8Rm? m|θm5a=4lpޞI 9.q^[ܞSPIn;/j|k ݞH1]\sw |F/|w?^p%|N0(z04=;jG{89ʞVX?MnU?ss7xXsh8)?j;v㰮D9Uܠw/ x|ǓTJݮGa `;y}+A"Ewhr?xI@|^x;g~oxJO<2헩TruS9}/Rj7o/j{Τ_c\x,mzyϸ /ͷΩosS$vῊCq]{8}oGYWl>^oG?zg]O[wZrw{>|lGPqr񡞣x)a|~S;rG.:y3k5i?QeO1^h#a~'hwю>c;Q}c.~>,9N~N= ;uZ1*;uO#/-:ʿ~Jz_/㡏tvO|nDj}ú _|i{,W;/ǝ;v9vtσJV]XOɡ_uw\uu6>!;N)rR^Vd?;zρy 7?,߿5N^'nCNy೴_?E<ɳ5Ԡqo4Bi|sħuӸ*:^vƍ5'!|/c;TlN8ۮ?v8;/ڎ>O??W=.FG|^8kGy(:gQ۫`X|~HoU~Uv@olwRU^ۮڷ :]qTz@/,Gunv3;&8EstuUz}D=Mnݶ3>8w| :붛Vv9ƕ㑹m>Umi~bRU~W\x_8Rm^7A߹~)/~>:I=g;쥥^>Rě7bD|F>/W]śDg7~yauu/Dk 3}?U]l߶b~~=1νI_CߥoOGǺ<z^ϯ^dGa|1α?o;{&/4m2ϭ[sA/G~Bq^}ٛ>|z᷊cۈڿ ecs<"^ٮGyWW~ G3ݍxs&GUy@^o9謹3tRQ'Kqj^:LUc=v5gRţ8ٮfd j?I__eB}i|uzq^b=t|T߱>8`q6SXmPE;o}=8Qh~ߕ_[ſW/>iܶ uW#TBUvs(|罯MgOQvVqA;=orew=n'?)v{:ߏ ۳{Y mݿ^]S&8B!>P3ӿ7/^E7oW񜿓IZ/8: J>z/7(㣻~xz'يz}qH;i=Q]׾^\vR[O_[/n.w=?e:̸{c_uS;.M0=n,q'w+ )]ޏ[sx 8*=7PrxkqwuROS*q[d؟R˃ӶjƗoƽl<u:rv/W{~Ǭ8߿}lݷLvQ7#d2 |}{\Gz<8==9';[Mq߇lu@iPyTs@]jQTz`埧c$gm^xK|2u^59q )g}ܪ^Qg;~sVZ'm/ל`9Vc&P!7*{f%RX׭w~})>4;޵뷭xOkx}8qBqN;~'MDbd_{u'Y>y>xD+{33pkdzi_r+{+}?=bM軞g3ŧ|*n.Bvۣyy/4+NxD1n??ǫWlT㎬zt"jg{:pߴ*|*dzX׍OBXO:Py&2_CV~N{hA~1@y[]~c\V< nRk7?x}ەZ?=_]ȱ_z>'Dm2quߧ핝~pgd~Gc!/OjsN;u^ǥ 8?+lվKyp֣l߶v.˟j!ǙxSrpOh%. >e-}ySqhC{{@oBOy>[npv ۏs̃sg߼V#|㓐S/%>ˍ)qB?yAE5?xQ؞#{iKs=pv.w)M+{y}~K#/`>3~ǑG;Wrȡs5]y2~1_W3vAO#^:Dq12/yi1 Z?8?EiSCo\m9 ?eĹKzi1^ﯩ]^ka >|2>4Nx3-c7eTya?(8oS}UOunRXGlw3‡_zzV_9[?u;sqWEmaq(zP/k3g#^|>q8R%zqlwoDӂ~σh/ jO~e݁^߽y=>w[mݕ~:uo ~=~qfg'p9qA3GnD+}w>ү= '_T;_E"ю\˲~CC [ȫpjGq_rbҷ_ON#U;_*˗*ΛsKXgj߽cb\! Au=}P*|zvu{(쇟\׫qo4~[zBi#|{ݨufrvm^=ܷfe#a|GV ==>1󋜗o;*+ozUvy]G3|7ۥu2.P~lWs']GUHOq͎p}_?u+si<A?^cn MrmAǘoˍb|n<_U~#zT+A'MqIyejyP݇+A~jWUVNS}s+u{W/ǩ9yw޿ڷ}S#jqTq|߸xؔ})8#5ў9. j}Cqq_My|nQliܰ~j8S;ʸ\_]V~s쇱?q)nUFԸn^K~7ڧy:Ws^޹e|cߥ{|lPO̧A0C"zU=@CWg-O99 nwh#=Z Tq>O̿{[os-1.?"~qUN8rc{XqO۩n߁{7.rzPg?{ o]g[A+{*=}vX^Uq8?x=7y/z}q;- 3=*h[r;:xFY.o>G/9?Wqq} j<|=7ǹR9=9hCR^W&|Fx 0Ny\?is/ٟ8]h8^X38:Opk^S_~OuwL?PKyϟ|--/+z ~^Nʭ1Ռ \8_㱝jʫcuv%/?̸xq->wn{=]y^8~Eq/vY/|VCW~}#iW_Z{PEԾ^ƹ/}qH7J{E|9nM]v6AӶ9߁#8~+Wu}xGj^ÿc+O}qcGR!v*}۠=&(zܬ9~?]/!|qz`U8-_Ob=v3Nz~~9WlwNg ö~W8x>}}}?s뵌WSO_שvj-SG/` +3ۛl/=~fi;$|;j78Nv+sIv9}#^}]^Xo*i/;nzB/[7'Ly$mg{KTc_[?f}mr\}iUv]w]M9+|w{MyM5qtW6gKT)L q 8.O*xse|hg}=<X':vm?x+yާ|?g=5_x#hU>wM߯g^ރ(\x]J{Wk~ʧ'xyxGb?+/i&{$}~DDž\Ǽ/bwaouzkoԬ_G~Dz5ßhegpNK?{[q6;١Ge{z~r}~R{Yw>)O|Wg?ǁV?cp<1x-IN\s]pگ#?|a|-~vӎ=w1z,ϭw:;x>> ~]?_rh$hm?ץxwd~+q&q\D6>< |t|E/l|mE=WxNJoq!e=^Eon3}⼲UAAm7/V^Yzcq/\gC.2|çR iw7lڌO# j;]׋G7^oKdr-o^m}Sqs]o66g|؎^~z1(OӎӨ‌w۠U~e/rVSaݷb@qn{ו) Ν_^]S sDSzpuǝu39>wԸx!{{9|wg>W~3_8qu{\Ὴ[T{|3ߌ[i_ *WÝ8#!8~dzO=پx.=lc\x aͯܜ6m~voE=v8޳AǑו?~2Qy;hTWyYO>z_l$ ͧm?w\:n׺< Y>V=ӾQZqXP8z:olw<-CDn[wp^"dH9Ю>CG|moq;R/k;T%׽@5znM{.v)G%৲wsN+/z ix+@1k;wPVet\"olgT~(n=վ ܯ3]]󣌣ǡվo0ř}vxpqaq>!o~^{|V~QU+vC?q0.w)X9q? ^xo|w<ޗy_̯b~|ruMO_O㽟xsQn'*ޫEϣs?m˱vicNxo#^Xgϫ5-)va;_`'~Vv&_Ǒo〪x;YO_{gю۠=^ῊG|p!qn{ǁj|)soTzO[:gC9?zΓvV|?u98Hm7sQ?oA'O)SǛjٟj#tٞYl7Otv i;u$Tٍ+; ׫}l_qy]e'sg;KKx#ǷX~mDmgo u^.8\W_oECg| R Pq\N}PaNX #1W;(~0%vu1λs}ݏ]5{=&(ɸUd_}m,@9ƑH7KAoCٿI;ҿSd˧vn=jI25y4Ko}bx omyR򓗿waPǝVkX[zSݮc?s#zIWq~)U|TP+Xot5cH ;nv?{ո4bz;zoUOVTkjN+_8>L?p<.ٙl|~vT?+9^zU~۷*8)㌞_sʽ$O{֋q>VetgK?sgg}c6R8_ Oዠ}W;x]=8R<x}>ֹ} ~̞O|79=q<yb:|wj^)^ߑ;OHW?xVfD_a=]=~*{y9A-_lzq킎gq`ՏA~"=>z~AmuuxduNzWWq[lO188y3?wj\'<58{r^Re?ޙGJ=&\nU!wZ~|ODmu~xկY:n8/ yk3.lJ{q'Qn8s{>nu6>{>L;Zxj}E|rׅW(8>*6~3v|;!/x~+2W:wBt6hd;W?ܩߏc;ij_zx*'(Z{V);^QZ\xWqN 绠KqWq^ Gx|; 糠YPw_2Ny9w<=ׇq[,{:yF#8?|\_i/sn.dG.Rݷ}K?~o JV0tȼ48 zcCg[QUe*vdU<ǻ_8>N_|wОCE{5UOUP:~v)g:~;Ǖ?gz({ Z'Uzy_Eϥ~L,'*+6a]>}a;V~> %ǻ>rq3[jY~1~} ;jijuϱ—C)̳/fc;v2sgkz}x* Oϣ~^a Gt0>Ϲq\_k}z? !o{\GPZ.)ya%Q_'39=,wyOydn|g]\8爧6ϼ󠓿>:.*d8/I<#?o*O:y4^;N1E;G!lOƿ@{}En9yoEŹ>g?>z;]+bs7A-<=DMЯ Jgq^"oihqﻸy1Ҳʿ0~^T6v6ݷyFs=~W-e6|lV㩨̯nǑr<ӍytwVU^KW>wd<8/۟*?%&>\*{Y9^Un6w8=^vʞq86蟕=u}FZiqlo߷/ێm;eg 5Ψ쐶u?al/ |6R3oWm7YqOew=8vfŗMZP:f?*׭Uć텶&ow>]]S*y۳+~Sz[G_8kς՜pj|Nо~:ߠ\c9{~q8`ar=^Kt*vY/lEKX{cj; 7AmW[/]|k[ݔsܘB+gj1 Hvlym?㍪v/wl1w^ٮx!8 ~ߠmظn]h$C^կ9^`rygc;Q= `P.h86>[mϫw'zGu E쇱ߥzrgLcb|Z^sq;-]w|ۻ^s*aynvxj.wA+HgҾ~uG8؞nsރ3oI> *=,W&ķ0o~r8%yhUwnFG}N>^]Ўú=xgYz>oς/o㬏SVojW#ģuﷂ(\\s;a*z9>A*婞~: }%_[=_S;NdzJ=HY'x3.*<@lscWtcl=ʱIAmϥ=/Ɲqdza~_[Q< ]j&==ͷe?|z=9zpվ"j_*DC\GQAS@uy/}yעTv.{:vPx_nG;ԸO'W>XNg\8Nc[z~8b܁?~y5w:8뵕=Yo<ʾqøq|K/dǕq Yhw{[ƷO[<ߙ~Cnr㪊8nV]9.yn?2GSޔyq{v|<6RvqlǔF>mZP8XuK*+=RL{O\8I[z:+D{\^=|qq.88Ҏ=qW:7qpǕGyzz^m~5/XZ񶦎:>;/>O,m?޳5>݉8XmPVq87j:?7ۭ$emjDŽ&Jy_cjw3OytUߣDZ~}NjY1;5WR;g{MyU<)'1_Mqd>˟Vm[=_+(DF| Zɻ|?*?uqYW} ^qx}\S6? YZ/Xv%K,wjB*Ύq j<GתJCo3~6>~Izw>-,*b4][/~wh8o Z?>v*ޘa~)6(Zo:q>x=Øz1njAGg{0*~sʟWśWگܶ>5ŽsʻJnԠ)NY9(.KW#^U>|Hym,N/3_㼊zį~J==. .g?+TnoCOhO;-*>Mqj\5E;S8p^PY_w(9Z{TqWٞ 4}Wu-g}j:ZxpDǓ?e:~@A'|jLϻcsQ`=V:^G5MƟ18UJl~W|&ǯ.jǫq~mǩI Woe}qE*OSx9_ۛvr?;.[Us\/|J?}~XtzL}i8Rsl*ƛoSrqxAzއ]ghe#_^]i4;Ĺ _8 '%hmK`GbqWά~'G~>Smv Q;C=x*<@^b;qyU3Mq*uw0*To^v@;ި۷#.|;W3]A+=~nOw[YϫKq߹\1謁J=z|rA49HlMwTIxdO;|JЮ*.>yU;o~o2,xٿqփ<0?o(Vvn :]"q'kt^_Ƌ]+/TuT{XnTGS/ E^ۮq>ŏ~ʞb}FYJ}8Rŝ8_zZ7}ڸ_Wvv;/XO|7ߌ7򼮸΍Mg?MrCkzU}v}PrO ;Tz~?{?x8*~Rũya< O<9f;NE8/#pY?^(ޮ=ZEc[YN8ρ^yW/|Dx67<6ᮍ|{yP17zMA[qe#/#?S]ƿ7hgnq^GH/Sl?|nՏ^>Hv}*(CqJվv2zg~~x|N!W.x:nq"P~IA;`nyWq6^hkjwSlOpD;yEO|}^8n{܊_x&*+TU˞-Ǹ1| <+;Uy^v{8u\񒾸}Ua8GB׫Ѽ{s*uO%Z8s]CO] A;k8Ox ĝu۠8qaxK>md;~3Kw:nǮ?9|3h㋜1UDZby59^ɉ?*NysctN9otq.5eZ`QW 񜌳gv5{q_ }^`ױpcmz}5^xTܦhe߅ j/vƵDžS~5h"NesAgė1tw|'n[(r|j1w)Ϻixxkz~=~wMG7=_9wܝl?;qcnb\8^-owm=s1~y}jO?uDžֻs;99;/Qߡ?<rr<⏱u>8v*QUXC|Wٟ^JqYo˥xuMYlG8oymXgOR\g).s|S5?8&X۹ڎlvPsOjsƃ!QVqB燱Oh(gp{qquO2ާq|K+x8|hVֿn8}kƅ?sϩzey 'lR~M'V^?hW>EQ#jTvVg>8CUauT'c{|T}mqAnG%O?Sŕy*XTyY}7״C|ۿ~=Z.|^C㼯޽)8ig/>!xy"|:&(-w<;}/j{Ta\wןGy( m{#P{tn܉ZQbN_kZP]o<>] jYc9NqkNy۝lݲ: Kո|xZU/O: cǑ^MP%gǍQAv^n?vߛ8qqNc>Rǿ\j<]>}<e0O_ag!5Gf;q|X>iTvh7.(r;#\P 9㸤zwsճ?;{qf['cyl7ğDy8v]/W/t?!d<#w'޷u.ۿdGAlߏ>x _K;{B|#)x8CN+_|Any_=_]Sk=^!A޶^\?%Nfs>-oxCnŇ?m#{_Šx=QNqQA{ ~t%Bދoj}]?qNmسk7 &m*> ]?޿-^WY}q⒪ \.k~q|$8x?*ơEtrOd}|9ov?w+wr,he<|_:~?J=} [mq&?b=rviWeamr~ ڟ~>7_eJ{kyF2΍w&ρ#h볟_OGx8i#~NwGcAdo2m'&} xKqp=~P :;8_^7f/̇2.HipK5a7y`q# |:nWSrDDSJeK?S&oy1Cv(gEVz02mx_P^~%Ze0_7"g*~绞]28 ۹hGsm[?k߶q{?ip܆l|ŹNlW7q̂kېo}.ΫsgyP+ 6qn{%DخA?c;T~{뜛OuqP]+3Ӹp RN}PcC'Az}+/ss|F;%aUm(iw3a[=P/͍+}ھwv8r7(_R֛_M4?}o;yŶkx^Ϧrǩ?.9j킼x7̧wun ~/cb;~/ߌ .k=JwxM?w}걟ڗu>z`.Iu@Ǎ8vks??:?>l(Թ^*{|Oz?%}}V\7:ru6(8g{qOבznQh3nxs+Uş>}|gƧ탌5ٯAuvǑr9$Nq]/۠_VꎋXr5&\+{@o8ϫ}zscGg~>;}0Y8[rV3qn?=hفDuvm<O9>>ye'SKC^V>z|y\g}vk'*~b{/׷AG߅Sw؟O6snúl΃q|`;b?_v)yh_¯z^~9>xylEoLmL/l/OWwx.w7|G]q/!?=n_Y߱8jr'x>ۧr-7ÎR=86u^Nq*(?P~LOxD^զ8H;j# W9y75}8/WהpN|l\Ky]_:Slʭ?=Cۮ8GZ~G>%#Gmw\bz=G{<]P/)1O.F}ơP[/quOUS|}z?4Wn8wWuz5|b~y]}{Mu;8u!!׭_rָ=\:hǎwpe-ƇЮҏ]^MPWz?E!=߉y;kr-ga#.%J90_.GGq]~1麽'޳\GQs~GYe\9EzfqAiߝ󩼎۟maz g2]?i=w[?`;di]*?Mu\s΃2~mn{Ok?F_C><s=>AO kۛ*|7:?붇Wߙ?.U.Sq'N;rWzO8.~wЯ?}s+DZ3MPGkOS'x>{~\ׂ=EtqlWsUyaS //>CʓE~n;f? jO_:};_VI+qAe_wo8*cî;I5'kQnGq;ǭG~-(3Mq\w ;''j^6:<5Ovt~k~纝'΍[쿳]ȿdp>!v*LJv;z|̎twsw,O8Su+vxU~]ZEg],mw3N~]*>d} >l>3'8i{ܕ\4^U ؏q06n`?p*+7k{󸚮;^*vU'l1HWgo>s=;?W~%s^|WۛිϞU;\D[Qm#hOPm;)8j_* y=^A9o=N5 ,WzOl-)J]=lN+΅&ނ:n#+QAr08^jߩP] j[sE{'㉿KzE}rw<wGl2^yη-}1v*k{"wq88Oj t9_2.=#΃v?ϊ_Xq\7`gؿ+trE#7'{N}M8?])1->yWuVYPg6wƫ+[Nw9vOvLuys4.U}b'|Uv-˹*nWrQߎ)8ҽ޷ߦxqƞ}yJo۫?d?~aǑOY7×㷼P}7w_ߪ}lr~ ~lW}zOead0OT:mDZu%;<^'zqc;Z[*#:nx/8~j7Ѡo>Z}rsS=\P)݊c#uGJyS[^s~iGjBqQe{U]ϲ  :no> JҳCvux:Ey5ЮxL9yy*n ߿ ?⹾ˋnG;Ǒ:dese|žARVqU~=0L&nz:=WהG/ X/H{7qoAqΟWX3c㽞z^7_ĸow1^AU.㯫x:_߶>Mum=9ԞUmǡ>xwK8C:zL>$αymV;%z\q|Ox/'W#~MT=o~nuys1.qsw}T^ٮhm*9Tozνyw?r|Dž} ~WkǑ*}Ν8s[xg< ;a;?U\åٞ|" Gܪ<=z?ֿ;Ϋݪya}}z?)\@=ۗzm3ߕ=ʷrc7[Q#5vU"SY/D?\zWtlx]u?Wv8{x >OÿcCGևY'ҹ7x4a\9vƷ󽜿ZWi u<o8/;?rǭ^=؞޾=ny 7??vAwyGh# ur98\?N\/ǵY}~95[{sqmA=nd{ t<=^~mu.#֏u^׮兊ƗNtڧ\GxN''u;q^ճv~#:ο'5֧Wxcq|ZWS*όqbBPNտrǫўȟ\?:ŁzE+9mFӎuK:X>@E\]xr|.#^ׇ~vc9n'-_?{WO~ׯF~ j =goSmx'fFgW#{_:WǺ4ay]=t}=(K<3~ze}).Di'|oIݫy_U2p?۟oОve.[ J{OOqWO a=<|n<k?^j8x~uM븃W_ 3>wi).lyR]>Ⱥi/h\I+zUnJO`ʿ켑*vn'i#?ٿSi=ޟ6>qgTe2e9PuU\=waqS~K+;neK?~:_Ez]z-S_ a}dߜyؿkC\^i{H[OqǑw긿?zPA<بzϳu ?wz_y_G+=g?XoY7/:|J7鿿}xb>7\][?-_E(̓KXDnі:N*yA]hۥ+{qW5vl\Xs2sD#pkat<X/߫Q_vnoЮǸww89lǮo0C>:SsگJ;_9}ԏA[?w^?w|w^XqUb-oxg6:I:|}rzA8}O贏PP3W MmzF{8~GK\5n>W Qo|y$\A;+o qnA_l_\Oό )U'ȣ& ΡU>zwv&wW,?U6/X_X|Q>s]qd{BなjyО Ʒ qώ#]z%ώ}_Vߖn/rz1m{`USz ǭoun"Saw˕j1;۞5`z5xtڮCmߨ\}xn^o3?s5b$z}Pc[?.ΝBW'&8&A;C6;>COyw=vz[[qW<[.0> zqs{N̗Oߠ]ρn_P#\]S=/D݈V׭Gq :IwNϓslů`_wE'^r=΢;fqaqf;_կ8›8wCBݍY6A'HE!:x~̟qgljz'z)=?rxZ A'ٞH[ϵȸwl>}a׎no>H¿/z/i`qn}z)|o?_lcזK#z[N`emO?lwZuWD+Ώ߃hmWQ|yxu=8ҍ~ߴ[ߵOc{W23᫲_Yoʾz<˅#{qG={`8U=zW;b]{rq㛐G:qx~|K>sw]k~|Ź`<1~⽋}]֗]P vws[MoKӸCGLP;ZycEï;]Aςjϴ~Sq~%u?u)(6@3}xx̞ypd}ۙ#jxƓϦ_5uZ 'MoToܩuU;&\xljl"/zz1d[v~o788j{=]<=wtS\[Q{nz+v9> wي/:NTъ]mP5morrrqBKm7ϖ7V%Ɠㅼ/[َѶtwgyntg'):~>/V8Rxwxj;jrOE\+?vDP#jl;\審-Y?w-q.݌:yxCu9K^gxb۾A]9zw=:qX#/J?wP Ǒ{j}x !齠Ɓ;~7yw_ޟ>O|#=?+~xu_=d: K? ɼ7*wvCq9r3_TǃT~ʊە~xD~R!{ާôsrzq~qs*V?ߌ*l~cܿ+{)Swa=vO.q[Q'yZ\z>[al߰]v/M/Ps=_Yw6<Ǖqd߫7lVv!U{x((y/=g ю?K"ķ6>Rۣ͏㜬/W5t܃_x?t?r?w6/Q[7v}xv.;My:h_%vMy[켛ʟ27\=;/w~sC瑟soi7bn}rz8߃vY'skqv\q˧6~SCyG>Ou:c{u~IO۰>R5G~C>G#??_Goi}߳ y8w[x%vQ39~*l4;*}-j<}mvG=Oouߗql{e<ڏR_(TEqMyeυ2o,=m_2ul_}xy5_wA年Kr{j eD9.٧'|p˓qW8Rޖ\U~.}i reA/r^=ʶ/d{ מpuMy8|}p=*t_鸰gߧq^߫us?sW[Uk<1륶e7AN\9|/z㯪\|·9gN?uC 3z5tjyqUn쟻7:IixߊD߇Ƕ_:`e{cU# 8n?c;o%/XNkoW@xs]}jg?/sW;zU?C6:΃9ڇ|qg'Otۯn6+`\cGq^D{-7Vq˶>soW mot'?yc9g6|ٟzḃro}Y/sc^u|r(*~loENoe;mWmqln?3~6c"xr~Ĺ>vFWѴSqg.qxo#/muvЯ=ls Kg=7dx\s=y#Z|46yj;]~rK3lg_vHێr|j˷[VyC9S_?UYyaƏ_UO3Ǜ{wU!i.>Q[M'~ Ex4nu_MjTqGrɮN/FvcWﶋ>]urCȧ.衉VqP3Ì{{yG/_)鄣cAm߹_/mչ 猫O /M].o勵+>_aݲƖ|8'':xn#<*^#{r'2^=~*;~3XɅo|"g+;(is<~;wuǑgEu>ze^Wb;5ø^_ [9㮍}hW}Kzxv5- yR-~[C.[aX9OvH5c>xZю듾8bTv Юw|+}zq_ڹ*Zg(~]?9cD@.Qn=lE1q'Υה?6(ryEoA'}OnƉn7 ~a}hG߈}_Wq=_I||;v-)Nqm;8|`;ʳ]>\^#Ĺ筟ݶ?9ܾygz_EYOloU9 lǗ^ւۏ<5uqA?;'/gXPx/jǏ0';&zdF}'ϴ˸ A'Q-h7Z_\{;*gNjNڷU,xosO]W+>gb!7+rOxWS?ͼv<^_*%q·#voܵ uSU[v8ǃoěik"O?Ņz3nx-S6~8ѸcՋ]8g=:NuQ{\g?v/,X?z﷑QE玏}3~N}|;S_O_Y#gǽ80zcW~l;P~Ǒ=<@}!+5Xo|>Nn rnEmr l'~?ۡ/%TzJor*8K1s^y]rH;lu}':_<_g*;姿}< |o <,ʇb^\9;7n>(qWǏyYo6UvIgSQ m/}Is<μveg8uދ퍶7D/kZ.;9.mu\7x?nW󝞳=xۥ ŠЎd^OU# _;ǭy=1NR7.kP{xy !~9_o~5L<87e˺/7{Pw8Ҏ|kZI_?|;Nu?/yҿ}686}Z. P{m>v8~Ʊ1>⼏;Q; _ujZ۵j;)1~ldUqBSocyh0Oc;k%w_Wyuw _U[*z>WԳ <:~JNoy s|KqFUZ>3uvYmOr/}}z_n :᪠O;z|oMr*ҟCXз>O鯂~qϯr)muތz w >q8UcOU?e;>=8'^WA_w<)돶cau~=xݓo >';@*ΰ͋/Lml$Wq&?}Olw|}Liq<둾ߥ8"Xw~LJ?W 㑩)΢=#0?lv<\{6RCⷈ{W#S~7^t'm*,:l\I[gqX7VEqmlrjlTQDZS\ C|U~[YgvPg+UDe_s|ۑ~mmo`^[g8.mocky^ng9- mov. j~}#|2N:qxʎC=`p]s)E.2zÇhu\JyqbK'a=yQnV۾kyf;])Z.W\~\zL7Ovx۟8~%p弯sM&q,Ӷ8/u}˥k)g^9a+kcws˱x4-lugvvBvƁ=/8X:ow||~v5_4QCx.o=因Ƶ g8mw9rq#y_k,(1}ήvU8p~RНsq{q]3ζz.@@y_~ޯ8XOr׹mP5ůYF{zutǵƇ7ƹױU+z|~wǑ?EqjeC{#Z΋QG¯;j|oʷ=f?(bN1ތ~zݮzsO޸*vM+.#ߎpДgASIU ǕB'긞H+80Ǒ߂v_o/ WюJ?NqĢַOE;Cy"qN9ߞ_F;i?ZHV:^KGh?xvu=[eJNey]0+3^G9' eF'O^SkoZ*8IhO'η&\W.?2eh_Eu-w&}\v>s]z*uwoVWL/UB^ݞ˱ d{ǽmtNq-7ѻQ^>Nѣ8;IwxяGMތI\{{\8]vGzNV:'=̛Q6N~D+xtokAk<ך !\n%O/{G!;ˆ7OJgr_Y1ǿk3ߖ_ {M(W+Uymaq/˼f%o+L⹼^TcL]܇u|+a #6hVu7} T ۴ʬp9} 7l#2#g9,{U ~Vc8cnڰ+,d1 :Ԁunxi3k/~0΃NYM8vmN4'm9'6Q$pYq.vE>8?x;9XIϿ}?͏Ǘq<8D2ifly,W.{yN +YRɗa1÷:ghG&ľQ[Tf.7Ε٠i}vm65ɠЧ/l)풷5~<-[jǭU+;{*6雾j;δk9~*\:CnǹSgcqd9h{ O宾R6[fYQnyJNxAwy.z\{u#vq]j78)Fw̏q.'ESA}']Ey~<:j]έ{Yo3Y鈫rVu.c`]j+Vw\gYNeUU\+*7sx7W3+N eFզmi3|^s*i oVzr?`$0ͶNnH'aǀ1 |]7whyɞs=k6rnlH{MNǧA95k]*{.>>:]DV3>^kƐw2r]~e[ֹJf#S]ej]BOkJ\A[WkGaO^SߴY_*wqx}쬭cQjc8N}QWaN,mMϰ_-ҥke6[m\olk uzcZ2>ϵ,#޼ޯb \ WlKs1S.QY{merOeF[ ?t*Ͷ՜xĽ5y~y(i;vy8N88N8A>h7d6><)S=8x6킟]*(q2M|}vr˔<U2AAF֝K gy}\8or]+}wrWGS+\QxKߣMtxtd]oC2ulu['=W-C _Tn!0j(sr{gx*q^gqodpiܴ9tY& q;|ʣlMYwzO6S|~1-HlxFlr;wOӵ.M4cmqqX}P~6Yr;y}F$5ᠼl39vySSg2~9GqNϽnen|fypmޞS-WX<ϩ)y+U3IO9](ˡ,Ʉ7,uö(Neg~>WX`U._Gw[L ΪJq7ڢ|ץKG5=VVt֕^չ͗[s+*^j{m-lxVk(QyU9n.\u-{8ܾ{ypڝ6L=V+w_msz֘$a΍ooU㡺z3<|YwuG?sEW1㽇m.ouk `'mg+Lk`貟r=U{Sj}{?b{rp^u f6gUϓw>lc3<;w۸Te5󽕬 4u:nZnڨpe|n{՚ۿco_ZVjݦu2FYXσEz]wuZg_TpvWzfU|xT^KWY7+,pWc1Թoz[/waLzk3.˻*wWx}ܗnK+׌w{_GU=a>w!UYz_=a߫ږyzT1ju seve1r2f62Y\ >Htզzfc%Vrc;m=*Zr^o#~.{[)ns鰿$(x,߼eUskj]SwU>>/„.pۺsXu{ 6nkok|Ku|t 幯(/&pYjd-76=Zu{mi`0̻9A[綮=yFY1Y0'&z'R]+q<\<ƈ7{m,gȝ|(:_C\wm{ͳoS\9;C2toԞ^L}JYߩe~}_\{of~_Zg~״v/]=?qYSkma9܊K9<2ƲXV[>˪Inڈ 2X gd M kZ>,@Y;yM~k4e(=]߶؟M<?]GYtfcmsϮq/GlDy]χ8n7p4 m=ϋuo<1ߌoi.&oS?yH׉u|UK]GCC׳źUiL3y}ZZVظeއEVy&M*eXaܧ+L]Q{>沿i> vx\75+;_mbls<sŽy\p'B<˷|XzYxX+,u, P6mc5 m3} V>Q0vcdܑ 1:`T89m>kn^>i79﷛=N|nrlg1݀owkx ?,gK+,dteNk*{a;ck\m3+;Jw}_ v˲ObZ%>^iծV=VY2s\<֡W8}~Vk[ymLx|o*-{}? o鶙 ⌝b}!UK= 󹒵|itG7Wڔךs۲?&ine;lx簷wȼ=Hed{5NϜp£tG:pm7ZۈMlc 5&N(6~͞\p/B6mg9~ 9Eyi2Hb>1gOu.qeCpJw";'/K x-p'~ucw5ZuZCn5Qcj=_16Ya G:I.ߪ?ߩ k,m+xo;+=ܰh)Zw>`cݵQ3gcffClB9½CS Ovw]xl=m89ڹ~7ay֢U՚7km"ڿߩlױXUUw2.1>Vm?*LrQ֫bܞN2f2f[:wa5^ (t5/B6+,Jɘ20ae dXً;*cӑm"ۈ687F0Hd,cd<ٶO=m6SԝyON`pˮ`m%"EC#N{foCǗfO8aߥ?q$m#ޏN݉C:(~e÷=+\^R[k#V4~4.p7~vE9Z_ +W2tu&@+8[Ցyǘ]϶'7_xx|%cW|r}ͼZgfmvj=[aѼf6RJ/dp)ϬeZſ,\o 룏\^7*a.>}㶿kCu^run^Ke:2/3_m*:ԕ zQ>_ǁ2owrﵺ_}Y!rޔr-x9W&d{|%W8fV*_[r]y]gy5!:9.[ِX\ΞN#fvAyy}KdFzmm]lٶm=m#n3% a~6er{xL|K^m^Dy xlӹߺǪ,0duj[^-C^rp,9;e%ZZ~>yZ᳼Flc8j[oڼƫ*d^i#/ۜSy^33獥V#>+V)scU?Mo֯:&9S1?z/ "3gYa-?\[kz^; l5Nc5xٴ?\X7ڸgi3xxtlMIq 6氝fhx'67·=nm̯=A.nd0[˭ԼdGg=|4^Yu;/oy׆-'֜^4߶Yޞ%^_szz1P6}ukJǮ֚lciW/jDߴz X#_ųOr/Dz 9l} l=Yd\r #mn%s KUszLuqq?Ȧ%:çu~()DOkxoJ&5~>c8 G#g>o7{*MN[mq>$?h78Qm^ ڷw۸W6?mk;ͩtBfG:ԃr‘6\ /k|2;Vg5ا㟯o>:|X-c %;[ lYW _: }jk@^[r[TYj XZ#*\kE=ӽ\n5_1o=uzU~r[O>:^WVe0 x \gY[ʗJ,Ocʴe1x Ηu|ő1pה[m6l?sX$TvPminlAC㰜;y{.yV~]<7ȕqe}XK}bQ~^m,~Xef:jY봟r9m^*=k8_|uoJ|2_)3?B>UU?|_\޷*:r+WDy= >\rW_=`+pk}0۬onЦ)\[0͓6>ls}?X6y3Pǣ629[Fݴٶ9p XI|E]nQȃ.,2?,[kdUW^gWyUJ'[zU;SʕnozVkfUm y>ZUTx5껱ɧZ71mX_(ITu(H+M|yZk^OezM}fsNq{qjk>3nk<汎>hstVnzs}O[c˹F{ v;a.c'9.7vfڍ6x;oqm O۵6a 6?jt]tĩyݯ/ڍToov8!o~Rޗl7/RO ?\y?˜m^3^͹bQv{߇maF^3snU,}{?Wc_{\Wrw0y~q|0q.3\JI-1qX')1Nr:nqA%3o':9c 8x\a: ;+lz}/{'m#d r沈yrOG+Lm͘}6"XoۈYNe˻k#ZRh7iwM, v9}cuv9ɘC8+_/ڍwqpmRH5zk7sy~t[<߲Hr+ҞqlmS[Qڲm?߈с5ߞ)kf\z&`ZJV=+`3X>(2x Wud52^:Tmn7:۽6Iudv_^y:Ѭʷn}]kDƭֻ;#uʊ̧w9Xl 66M6f^z:n|j.^xkA3~x8ЛTW./vmV-x*1(q֢\|:M}d{n7vx̘.7-ZnX&Up dYxӳ1j]v~ wڼJ:3}iV8$}kP%ϻۋެͺf^՚Z+^ﷵgXr. Qy:Lj|\xXμf=q+(1ųM\Sy<ym+`;?VYԽRy \C=GtT68`Q9yƃ+qr{V}xtkƕOhyCuqzLmSYn>xu;1[a6p֛OƜo#i+i(;cیr=|ޱ\kG֓Yy6L(lX^WA.#0Lwc^nu|:^kj,R^+zos~:?uE-F\W߈gNu)U'j̽3/%WGFM]z< 7>p/.oz|]):Z⥒yCI ~xGѓ \ϽD_ֹ[*6yoE2y59,`;^pȩΌ8=Ns:"6Hژ&{+q&:6 8 l=8>f/S͞_gJ:7V V9]qu|(s|^VzpgmkuQW8j=ɘ ߵzMe_QAV`ۼgZM1I#j0+QZG۪+ǥs9>VXܲ+E=+\gZ7{~>2v۴q͘+X 󸍱u7gZqeqmIzn|6ڜr;N[ 'nQyO༇&> <_Ao9a6s8v'z_`qh7{%6|*_͞)_v~io羄Ǎc|V;,7aTeD.VX>2rV][beNZ^g}e޷m^?VNH y5E%=p)N[ZYo9d^oqƸMoy>s9Oavo9toꘙ7}ӛm=56{s:n/t O? `9a+8(6{qw* iH>xȼek7p(_s:NX6m?|}:0͉ wƷʶ>rNNnڈh7QS%wt{18Gc]J'A/_l~2ek-pٔ㖞 e,sX:lϫ5`O/3\)ek 9Ǘ9.~ܢV:3z(Rٹs_!<<|d:/P:VǶ>}G<2x]zY! 7mvylO)k081O>X{)X=yƘS=krj 忝ʢSYp^_o:^o}i{q\c˕\2U~9q갾{֫Wgʼ}wYa]ec=. 갽cUH3vզrN۲*LvX޴^ϋ{x#c|-˰\1rDfZ_rڼg Xa#2ju~t?mXnvcx'TꝷR~mh{~u<=Sl{cU΢]xُ[sۘw-#˸TfN])]c"0S.3j.Vȿm16˃G^ϽGy>\X'c ܃3W\Ä2m8] mch ʑaSWeӣuʢs^׍?usU/c^^w CWoT๶(4:ns/W=S;elfYe]Myo $cA!G\>ϭڙ},mAGFOy}f, Ʃ^sV=XIGGmĹy-kolc&-?5;cmr*\1hR+\ʦLxfLoܝcϓVCΔ;|>=7'c^5ϑ.Wdep1UVXɼVe\*;zr\7 01׾U_[W-Z^7rZ]Ͻu+Ppms+lgVI[@`}kQZaX˕z?fWss} nq+لlX̘8[m=--xifvroia-eyXG/ʭ.k&2w^/mec/A^x6cʌ+DoΓ͌cVx&>c 7Co>p-u~z9&ad<6rPj-szUwUfUwZ[7Wxcux\ɕ҃+`e޿[^owfD9~XZaJ0xc-_37x91j #+˫<VeUur=I$Zkݧ& ynO< :gtU{s~Ʀn2 ;sUR+UV9+_k\cYK@:rXKxk K!׿i76.Dz(پ?SMTy Q˶UcT.!~\;C~F ݢoeicS>\z>_Xq/wmU[Ӫ6]l.n#+Ly4dA&Z:s1: l3q(3cMX~6M+ec::'oQ6 kǣ6b-Vlڈvm>gd,\H{sum# GS\B'sv5Ga%WuD='Vd^ 28?9ǖߧúnm-aTy߶ ruVzJZ۸&պಒk KUe}B5\8{g^{^|rW:ᘒx>/ީ[:+ƛ)ܣR_ތ穘t3G+M#nob&H1Pm{~Ӳu,T?kKO Nc|*Vd9~[qs|P||[k|T5q۴>~O6C}y?>1Uu:6]ٕ0Ƒ|ԍ&' cֿ n}~|u,fY^ǻ{Wv\O}Ldu|[nbh7n8+frSBl0߰me.,n7yWTLR5_u/'v؆&[5-6Svk|/oKS>/;禸8b#j?sA)1wyHq\2E )oqZ>5~zZ(-9Լ  l}a8~~m[ cO׍mM׭zggͣ8cXmkO,P08q;NUG?kp7+c|:ExjO쒞{ײ1Mo#<ޭ@X7-9|[~S c߯n7?S}@~/xq|յwoL[} ?حCcjMtu}mM!Wm߯ ݴMesF2Z7N$QnnO:NuRRckOO|}%zd}զn;?+aܭ$N(S[Φ<+O1~NL,uX 5.ϵ91d>ej4afy8N!hyp3{8?8wn'>]&A1}n\[RDUq> @\ y=ҭ5>ݭ|Ok?M4Սkz_tc>>D7cꛄOvqS^u9b [x*:㏻G=۝~ϻXo>׭~Xeޝ^ƿ>=δ&M擱ۍW >fggopNm^mgbs5zjY.?lrj=:1Yc']^]7&b YsLk㜒MbC2ET,Z_ϧAtgm!r1S>nV'w>؍Fm;}|[}7hz[' 9b?V1gʵTGܓ^jϊ88s;[?lMh`c?NS{!Ɔy'\5ڮM٫Ws͕lĺm[mVºm;W~MǛ-h?O3y9X~QZUqq/D{J9TlW,ڭ7~_5 \iׁ }NS&pYs ACϊ{j:~xPv9b?ΓL/u5=Nu.xߍcKݴ`7?u0k_J>ҭ+=mwߡgj>٭gwmVݪ47꣏'es"\9!n叻UBn?_T2cy15H\f\V1&뚡4w:kʦޅU?bbNxʮ[Y}OtGOG׿G:<żHצtzy>^t wǸGq}~˩ pyxaԱ$ޮkǡ!4GkS [<~pB,nxObw]出9:yHmxÇyzh6:? T n巺o逾ZYC/uS'Vun{xw#1rPx>ٍwջ;[8Ox8q[ݪi*z[:#~w*u >nt5Z꣞S1ډ ;23ޚZ_ބb:.Ns3^1}=3<~L)ހ^59uX/;~LL4MZԺ~j.A{tz~J/ NviNZ{M<&C/ݚkV9U:jX?xGCxC9}[<{5y(cSsA&p:fއ&P#p)xe9_}ǧޚn%$bmcyp^㤏:. ̵;k{<)pV,TӁ=yxS&9ç1N=M޳~YLqJ3\)9ckc?C=?K̃?4N IFSʺ}j,9X֧:ƲOMҝ:w<Oy6%;k$;Ѳt-)]㚚9P$:R,g7[Eۦ<܍4ॎrTS9+szqqؔxXnǻuӍNqD?uJQ)v>*Oֹ:s|[:g@ q0أ8} ͣZo.Mmʔ](mUSh{֤ǔ u/<$sjk{l؜eSܐkuMҝZfu%;3Uﯮu-ܮqXgs=zeMY*):ح|c yGxe _:n=s8 =S>jkԶ~k_W_ᵋ=5Y%bqV,P_סϤak>ؾ/͵YԔuLt~׾s*oWljser㰝cX~Siێ[}1{u=3smxO>ޫv o׭ꔌ#7-)o1{\|vh.Y|8N#W5W[sg¶wMsu,W|b;*S뽴ߨmas|[jS9>*,9>R<@5V1n::+R.r}YÚ?דsVya*Nhomgj}/VmçYuPwn6kX_/khm :e_mwMNͱ~i*v3캱\_/Lv-&ޝ}WcRDM,=xKֳޝrp6/SMŚSz_Oy*Vf6X/Ŏz϶}Sqȃ]Ώ Z r)lAϳo}81Iq%?RC*gS>ڍ#z?>ҍ6b):u\i_V9~}='^S=$1sSwl~ٍ_4EI{va~): PۭVM}/M{MD/~W)~UQg~!wߏJkJ&kL:ryQnLb9:V.z[˙cw;T!gپu:GK?Rtӱ״}OtwnW(V~շ>]֝ˇq@ R/qOVh75Nbxjtw[owwv:"~,_,9Ce{VZO]G+{2~aNud3Ym Oq~1Q$)c53S~kb ǚ}7_xm)o%?3h1B@jn)_@<(Psò)LF_MSH |e~ĺbpMgb>rSTlc*v?uS-1-M,}ݺ}N|־7;>.k|ux 7|Lw.|I}x]̽_&摪m\s7\)\zォXg8}Ц%}Ϻ8-t˧w>ݭx)twV9~tkvhdTlB՝kb=>f0d.O;.z0(F!Vkwux2Su▪XW>Ug/vstwu޳i~X~,:c˩Xhʇڷ{~Oَd88+xA1Xmu% &'i!>ЭuCgRu/pH7>ԥ8x1~:]ݶ>>T|Tv|#^}~znp<ޭ_un\^ab?qԧ'p=խ7{[6gy涏vZǃԺ*GO5{f>~ݭ$yL7|3qȄ_'Ԍ~M{3ѓr u1nsI:~1q\/ԴN,S5nω,wVAW}sUG{5ǼT''c ϩ\ɽ:kg 9bԱO bN'ω c<ح<)>c+ؤ~}8Nc>x\1P.>>!p\ETR38ֿd7}i>ZpqK1 F3|[}?VEADTOۍ OwJ?r|Ov2pձPn;VQ9_v1g㏻շ?x!RLU/ͩs=ϭ|dS}NYت͎c_>3L:vEOoM~ѝg\%unƾ8CޚQ2[Դ'^R?XΎ~)Zhgxl*tlXEz~)X]9ƨᔝu5?D(:yoyo7L?3}@+ܫc=xA:yq:{Z=n OwzǺoW}ǻ/Gq'TnL>x`حu~}c~6ޟwqY[?,bTm;[VXӈې9AȳBu\Sn=6}[_h7}[e/@{Z{w5OX7ncG|>ñNLᏸ/uy>8+@e? Sk8j?]R[1ZNPG97P7 + ~u&#ڐ!\XYFX7_05qߍqlbgz,Ϳz{Syi=L6>SԽ)[[Sv^cnN<t1Ç>|ީ~X]b8MMܣ>ZR||T棎kKG'u2xu,nr5Ow:t<:Sx2y{Ss^?xN;s6)ٜzo/N.SqЃz V0kxOgΊ.G)Ʃc{W~v\xp);6iwb `O/npzJjsST=>zpe3zfj>ֱ-ﳎ$I㫩5kV3u}k = $?{}”M?A?VL3qqnἸ=MT0k8K.g%}Vp5u:W؃32 ᘊEh˺ⶤ?5pAϙGW5Z:opfx`uno[Vȣ15ǧdSŞ? \diާ|FC]Ss=aߔߴzpgc*fど8> {Nu.h`[O1;^07p=hJh=Z x>=0>ͩz=حL_94S|[m{ S>ܧb6=,r_7 |'8i&nUs[@VSn\#^puQ nvݪx: yjguͿ_ts tYO^pwvM5_ܨ ]nC']8K*]n5lְy= ^o깩.S뒩OuQSklmcrǯ1+s8zww{*֪bT}_roLSA*]lJg7tzvSkW5 }Fe+ugӪ|yʆ!|YJ#; _Սߝ1s-oyXn:1B=oUߪױ/ ߚ n}?o~M4N]?}ˀon?'c_kSM2w\X녯MŔSpY8om.NY㚌8r*+x7DM/ljmM/|PZx^K[G[?Rh`ۧ>ҭԱCz9>{tu}N^W8ŸJ&u񾉱Dž>bZYg/txnS&~qݜ+Jb{w7㓩xk__rD3z1D 뾂M?_d8w}r ~>Y) 5޵cdYweʿ:rV|UǤɟ[VSc uc!Ԋ'H17_m7>GNT|qf>a.t{T6!{^b$Mۋ| Jm[mc?%>gߍ}:~>ҭ}[{O#cNs5uw~: ~׏v:taV@H7zg?t-W¿{w;rpKIx)u'O\^R>|~vO>9_@7qS;w1DίUczmd _?Nun;+|-׷[~t{Ks;x~s!\ i_uqm3؁f}qp9O}T/,4om9z|k:[;NjnǷN- ܛyP?)2;x:zn1;_hR>ޭCqܭ%t﫧1a/c|Ϻ"ILzXt[+ޖ2NǓ;cnvXy/7z;^ېov+ܶv˘o;OeԱD\l"k _N7ys|~{?H9zمq]y~_E_:/G/kK/ˇKV9MhO7szn?vrO{1#>ZT; il8̗~:΋\߂VJSֱؒnmџ>6N%Kn&msS%dzu@_I|_ ;{/fұIz7pu|aA9^dOu1ʒ|#*Y*'%}LeVp㏺Uӯؖ%Ywم9Mɶo}# ϝq4_]nпoҭּ=^ح6=]t+M$Rqʖw#Smp,V{)^m)n߯%w㰄{~8 c?N%ֻyOSj~$)^T5b>Xjrd}EKsO݉`tn7|%݊{Vk%wtŸtȼθaQt^FخMqx.H;mo*L0Zu|k2v?zn7Z wxn.bvSs99_4a_-u㔹seNǍsK9Qv>_`.ߓx_~ح:T}LK7<~w u㧵E>ޭK7u'׶v9p=,걮k9;ny)߲ʖϷWb7Nk_oW>פ1oU_ZO]sT)/kj^9:}x>^|[&?i|2^-y?*TmkݩYu ym } 7NX}mnb]en}m[>6ǽ^U׌,ލyy.Sjow=*wJhaܼBF(V,9GF!yYuQ9zX}Ny:u+2k!mStOV=ҭ6JUҏ*\?,f׹'U XMs]+r^!mPoCvy7]'Fǐ>yt?/wg?/Nbۖx~NunŅγwT <}~-sP6vNW6G=SM؏^~>[Lwצ5=>^otn?W{|f}_Ge;QyQ>_ދQ~X$ʩ\:~dU÷ lԾ|c_?{mGo wMlQS4B.ƚo^[[75@ʠco5n!⟥^,%iV…oj7oL5kW5g`[x]?_䰋۬kݺBmGoa^\x`Oж{T-R:΃o,Cms 9vlg.>~.}w Ĕ?Ouˮ}꺸>R8<[nQW|Z9q/t~jok8Aw!?u?f_~~>[?Ot޶?.S]%l_b*WRMn:7*Ƶ sy], o{w:gf|4w]~qmp_u7.O6y_mS=GݬωzgzjA>Gɓ/wNGЕMI32\Tф^Swcn/ڿz[8Ƕ:~[T}Rt2ut>7x7WL _VuQnR*Y.ﯕow: [:ok]>tJϋmqvyo^lPˀf%a/Yݧc N5KJhuOoC~sݲ?p.uϒQaVKgqOu_]y79n_ֿ{|T?STw/0< pڧ_~Z{֧m3ݲyXFMK樧b}u[PkPI6s=}x:'6vN=3痌ߛ]B'z<}NS^Ag>X/8Vn9ZKg|W?p*Sۀw֯)yxߗTi|r/Fn 9W%G|Mn}w5ijZ3:zEϽ_K}>:!%~}/|12.K|nU݂{ں:]?%:7K]e \mzG<]ԟ~O>ru6?-s~19ϗzd~a/~?6LuwߍgK煪ohٶRv==Tmu3ϔߨnׯwm6m?Qyb|S6n~jc˦_V[GǺe\N< _"3u,!gd;'2&㴺w]%zمpꚕ>|:_Ěg)Oݴ]??wrSeFLo%.Y>_[ɍq#wo}0@G?.yen9257}[TooXlthݲMǶֻx)߆VV؟!K?œn~|x1ձx|9ȼǻ-%lF>٭^F;u[bꠏЭPʶ|ߜgYl %.|[8o~S?_lu}óbm?vWӾ̒Nؑqn6'{QWʛuG- ~cw׶2uyImsnt:_nsp9<gom߿k6yɦUcn]ֿ k*Eglac"73w꣎^*?RdMտ*n}mIt6YG>ȼ׹\fn[t[?SytE?}:@w]`Ժvݹ%F_?rZb5#z_x?K?ݵta}4gձ?%X}ld zoX:~ӈ="f?F3{-54}؏zm1Rޏiښ~RWۦ~JPj_Dn=nK2]g۹r{STcnU;S~%un;K8sK׺f"~'?__K=ZӼ\\VuMuP?KCO|%Qy[&Q߽ӭb+jེU _\Ϳziug0c _#mn^2}[oSgɇ"xߪXfv>/6O. }_}|>Ɠ;;7y׺:z=n~:?SEӶ;*_,xܫ~'NY{q ߻g @/H?㫿p^^u~K fl+>=[2qRKpޭj˗%_j-яMuc7z[6AƏVǒ?uxR%uJ_/QckFny1bO~cTzt:?gȽFr]*hxk5;=KӿT+H% %w^p?Ww pF>K~ClT]y]amu հ߮Gx?Vos|mqֵKJwU|,?ߙ~doۯ{Sک/|[z.Oö<\VﮝUs|!տnOvum1nYZ~K՘-aTjݶz~[K|Lz7u]?:ԏ'3?|yna׼@?w=%hݸmCS4-O.UABdi y|7i\چ*yw.XG{Tu.hotg۶mM?|r:[YjѯT&'ɺ3cZ3E?9Z.Х%Y)K?㵄=]MxwzOu9%uzܻs/տ!-~;s|v\Jf)م~MEK}|z~&z}^WOt[6]!–rގ|OU_ov5y_5K|fΦ疰8罭nsK~z_ImYx<S4'X/~C}) ߭'Kkmnq/rԿ_j&6=^_M>Li|R]Y܋^G?.n]Zߛmb>qVw:_?WЙ59SLjKw^|7DB5UKut7=ǻ}>Z/%_!Nc6ꟙKOwzϓB_?֯?)yԭŒMm;~omsLO?;+Mj_6kEg͓m]n|pؑ[~'bݲmxG۴c b:mxw]/$~?|!l}oz%ϖe.4wQ}@귽†}{wj %nٵJ^m;k ++خm\J^m'ٺ|޿u:>mo[R}cP/y 6k*E?wtYwDS˷~}ۘY?ܯcG\voo8ؤxgp U^.})'sK׺oCv~[ـ䮨kו~ą[Q~~yq2n9GfVvq|~k l{_TV{;Ξ ~U絑ȥ׺Mچnovy[֖Xb?zw г?_K_>Yjy~,>]տ].7C2|7s1ۓxm{ϼcԒun^n.#?K~XQsKE.?1i7q[׃n#υ166lu]Z':Sשl|q6ἎvmN "%~=V]Nً3لJYV~xFgRr!Dg)|?܍?i&3퉾wy.nw>Z%b{_JgmW˞XA'wyo>MNjt{v̥zkzG.?KryK|_[o=kw Z73 O|Ouf)Z<ڭgʏM}ץ߿&5ǵJf~y]].MiY@~ | W־ZTM?pOu_Kz;[Ϋ_Y/},m|:÷,jD~ ZKQyEos,Uؽ%mտn [sK^o߶Wg7ne9zkM0jwOSMڦRvkjO(yӭCxR?\{gWCq]sO<ж1~ye%u>UsoPSK_pSm6lݬ`j=*Zo-e]Ӓ~v|[v}R'WA  ?.R/hl?|[]qڤ{}QZ7uϻz|_wO]n}]׿D"}mN^\"H[ڀ2wı>|Y"ۭ[s^1㰄!]wX粧=^8eN~z>o"O"Yʿ?Ry`^R(§*_7N?wu_\Z_[RdP|޻ku__)uQ}m;yw[M/lﻮ/¿յ}lG fsmS߅=kۼU~,9y?d'_ֽmݼ]7?x'O_Z/ߏ΂ه!ӏ__|lԿEm׋zs ~/XA3R8MOn7%[}[}ޣub.vg`1ׁKα]'Ou]c+uq_GI鯷e/O]D'C{-:'ŞR_Ϸ'n]?xi_w1NI%V%Ѝཱུ%o3_VKuϓC8}R~n-Ybx2^sd~Y"~k^Ѷk]REG~oZu9u΃@NT/׷+\﷔]^v1:^+R%^o->_f?zɺݭ~b}sƵ~uw#ޭ޳/Tͺ]=ۭjo/86nX{g=c/>:m|(9umok?UO ].y86񝊷mڷQ炖_]{=?] 7Oq.'t[Rx_Vu얫ci~+oC|K'~[7qCXkw5QTYۭͶ:b}z-=g\Z6KM`Z69goyCB?o1O}σ?Fw%t#Ӧto76z}BtZ&uN`^.Kw%G-n=Ϲz=:ߩpgˣ.uݲ߻/{_ ?On60?=~6[݈vFP?{/f[m=C=`ns~-kB-R?5Mj7sq?KQWnޅr(Ͷ oWݍ|\Vn^^w},_sJSG#l6NjE-6?h7-8Zor^wZNv"tͶ64Im;fmTR1G5Z_ޭ+ԲY˖j,C"-\A@ttuTs}ꯑ3MEOR.y,kng =}SGz |ou>v=]K.]ԶֆbVvrur)ټ1%AotoZOiK{ޫdkV=.qZ2dCCq[&5<ٝ]_m[}2CK^udstsfRA%~^&uZ/8/]WyV~ދv16ݭuuTKفz_` `t'޷b.~43V׮eI/wrӭޥO!zJq>Ӎ:![`g? =?_%S| {_lmэ:s?K?u6qԱ.iCU1뺶_%j=df;g/KкtB.R>~MKO=~a)yWz$~j%Z2n?sǣݪ-c_ޡ|?%EFZum<϶O/e*9-&]}%׀I>(׸p~\KӐXO똧m2=7ow³9!s{F.ޡZJDY']?/A?߀7oRyG:gvڇ:R?̉j$iydbL8_*P%}k?D}|؜工vUh:>}D2O-~ߍ=zb of D3X2ӏQ\%=>XybmC_?Y߮6~lNw/Ln}mM;)9my~C5E۾5^>vxc?6<&o2U%k[w76}~M븤Mi/kxp>ݺ,'ݛλttX_men7Ϸz>~s F>۾:v~6ʨεV][jQj;xh1qo̡5y^zU̗j |?|s}Zwc3xoK {[}'?\sY:?8^z-ئ!vzr[x7봶͜)}m:m]~ua_cw_%ާߥ'D^p-\KkzWu~q uWO zm 9N3usspw3z?;T0n?p.aKz?5.bm?s^6~tw`:L}zLKG=-k%6Q ߥq]%ӨGY:q}Gr!?ZHX2 /~-C@61ϼӛlyxq_OkC??dIS65CO#?|}ōS@߲пp9v_b,u={K ;KKY@3iw ǵ@ڽG\]=Azmg&NdK05x|8bIY7Dw_~~?/}r޽~d-Y#Fm5럿ۭ|yqO~пZ?v{R|7\ԹͶPSOO]5Mkjv゚zDƋA[Ndڏ3h~wzr|~gZOY">+;Vk:䴮ug]geuRCg[=Xg/W]KQ/mW )yl#]ԫǻ]ίݩ]xmc 閫?gMגvS<8/UKD[|"^k\ʱZ'{-~W}of1}[>ڶ|*I7K?p|[/W]WC?w1'Y?otK&Rn=CޗkKҹJ_v_ϼ?~msԹbK|Oݬs pϋwY:3.Y?[ЋVg~Z%}[6Yot{ĵ7r؃jn񚾡UpY}%nuI :9,G XFMY Iwoy]zUʍ"g{^ڽ/Źt^O]fmtZߩX"Ԋ:6%}q:~%~]??KXSOm%ۭ~k[zK?7Xt/q]{ 6iϜe.XTMcD|^؇h`[;UGDMת-}\&wHߚ\"OU欟6]gMSot+yzuRTg)?~n5-%_\LOk%h~k)sFnC/t5!K?ol)]w롗KȯW}R瘶:o'Z߷ҾsNy]f^?qY_ۀ9}Bҿ^n AG,U{_? %럡MW]\yt}[/f<!!Su عy|/Ͽ]jv^^fvzwK??o[]p9֩\m}H'_" sW,#;^ǶuIK5}K>`tWZTS_׺,u*wX.&O~pg.]wi@ T8HKuk!ڧ-;z->:^Y~2F>v^Ҽϕ]8hQ ֵ??K7]]zoeunߔ}=cD,lyĩ2Yz?Kka?d.n hn?=VAfkX>z7X~RxT?m1\/ tƼ^xU{n;#_j{w?zw[{768dLNnVjQMc'C=F>ReǿQŏaRi?(_?,%wU[k%ﺤ<^̶65 +ܵ=8/~??Χh8/:nű?}7,5ocKy Wqqu6t&\΃ޣ[jO'K?OĹ?C_Vhq16^ ?~տ~_=kٿ5%%Cת?둖?/ۋ/D:GuR.ocsL\7Ǯ/#su|O}/:ޗ}^;o4Ob>}[J'_/myG}8 tcW[ο3Ͼc)Y ^2\?P?Կd /m?V䰗 q{n- [%KZU~⳶j-ӳ7ϒ5/W/])ֵrM7~]:u=G"~UGr5ww}`ygck5큮QcmGȡ,Y6n=!%'!W}T{f:߱Kߗ^?߽=z?<?ߗsk}o=?w=um 1%e:X?_)a/1 [&\΋w?mc: :p[QsbMkzGM71>K?]%7Xk?#k⟳~w*_?>ޭEnoG`ֶt)x?,]]#fSת:ODMml؜6xⓥb)r~T6}[CoTpVn~ljx w\[n%]>Y5=.&ױ^|Mح1zøRo^yo =ڭi~; ޺ݧc+E_xxy+>?]}.?Zs*#EHρ tuN7(@cS:{W$@|}#qu o9p}^*}PƍW$ ya:yqz^.?VD2?%H0?wCL|1nDGFWE|p=ɿŇnoDoi^z[[lS9_'Ͻ*%:m8=JLn͂^r ?+7zn }lj~U|2L~*[o~Opgku7jޫ7 'M|xCt)sַϗŷ矡qAgjye{kI JOsw | $>S?GjBi} ,9]I:G7{냯_Y|\O/!|wg<7 q=x+Bܹos={7`i7ZOql)7Gju?|=$pItџn9O0L ?xS2L{yI0^п6<=v 9 8w7^yz S|8l[FoCS>||L[OǹֱC oTr?3A/M?S%vu{GCz;n-zr;s;OhfyJty#o'tޢr%O 79sb?'0iog ۺmnzsShhؔdR2<Wv?^'}:'96n.u nI넹x~f\n7DWq}kx.<(xi^IׁI{ 2W ~&_8syaZ._5sN][9^ǂh=q8qO㐧Iy+_9)Ǟ/x#=:$.T E?xZ'O}q_˕멎VZҼ8i)31Г%}Q<ߡ5{ &iv]MG%cU7;'$t}}˥0[wuܖM$WǩM7' '=X AxqZ_V,E^wuEA<,iPT;=Nno ,'|FX{\/tT X3ՠ맂|+7NWB/_~rOy4>s!|K|a>pP< 6< o'љ>Ny%W.-:[2ǫ>uE5ץo%]4Z~vIwtY/3ۮr_⧬OIg.I ~ZWp'bi?4a^nj{ב ]<1B~o@u) ܡ}73֟Bhtx>흧)~/tr<Ct)g .?9W`+87?[@Y0<9זm<\փdO<.II'%{a[Պ6@uΒ<qO<^?gS}tuQxxq ~Б`߹?Oj R1=3|_0;1] aw+x: |zR@,/`Cg9'9Z?u1OJT0|p=xpѪ1ˢ|3t%~]v.<::i6']5]گ1λ1#=_ܐu0'}J1x%ּ.ˢ;?|4Ogx/Fa8+_|8??\OJ ]ڗ.|⒔<|Ju0*r>9|O7zΛݙyMǑߖnunSY<\.zגlC~ z=ul%G;_~MWZW%}x;n%/m O0L $@|A7zGk}}WC~R09oK}]Ϸ)`.?ϯIOә@4'GZy=>uuKtYmi?ona;‡^m%>h %&ayZFN/|<屓޴cx*r~gqi}+:!oU⮡~׆>wzߊo9v}H}:Ӿ@ ØD-go؟BF~-(>8zv~?^'$~S=ׁ/͈. ޷O3ȁ<^>c6;,ϥۢ93_iݕIuir.-щo0-oKy8Go['-8S~;qY=J(/ ŞI)ߖKdiې!c;X{!b7#~̧5 .> C~BNN\+y;gK~@緰+ܷ}72}Aۥ]yx0rTPq3覽g)Cs|B(_R.}|lt~:%GtzPCQZO8 w-u< ߐoȇߪ OW7Η'iOin{lZv:.?7_!9C{g1ݶ~ַˢyA>wΣpy 둮ot\=z7_i:Si{ϥ|)+19%=_Ys)W^Nyֺ q׍@0s#m W 8CD_k@ͭ<\72:.q%zI܂5MO\)r#تJy= ;qoK@ҟ)~=[_̇M:pޭ5.7_IG>v\|q伐a/7D|X_rg?|]t{pљ;^:t73GA7q= 7R{y>~3ԯ9{߉®CŹNi/g7F7D0㺔P37}N$/7}Bם~ӝ ybO5#q?rxﴟc~?hߊ[}m#?idX)Ϝ/K1o}INvX~^iп/~wu~Fz8/]o[`:PI;b?p'7$O|\߰}jǡ5Χ0M[-iF1m[? پܧqH3ivc\_8q8ax8@[>D3kiY\g+yq=]蓯_:^w 9ZN_,7@؏,0o:oOS}Mw3FC>Зy./'s躖j}i\zosrヤ뒓7>^;_+_f%N˒ hͷT/<|ohԸ':SF#gZvi %]k${y {&~N~qn^=Hiz-[Zx!OUHLyWW/?}e_lM_GmSz|w,h >A] t8 lw:=wIyὡ3t>?O7K{^wz@q'.HNCm؛zyo%Iu5ӺΡ{}9r=[T:۾iٻ$o  _xH'n}<,p߇~俿.c_v[ $1hoB?g+5 #:iR/{RZtC9~3gOg8W "<=Oz}q5؟\ "{g?Qi^9(8 O?&< "~2~0kɿE|h>%|_ )y,s'얳zov>Л+Htqu-ה<=n;/m=M0WZdy)Gp>Ir)s]~&gx{?/+#7J{1>mZ8/abyru|y|@7~-x<qRDפTs~߭u{)}In ;7AAwAW3q>X<k⭧~~Yz~[_.1'`wQt\΁/i; nã+BG2nO%ݩ" Ǎi= ?_kqy@|/q:W'_軬?cƣ7LzטѢ;ѿ)}sڧto\81aNqxo@W`W4)ц0u;oN][>&тs3)3CΓr|k;#z~uN(?ۦAҺ _,םqii]:ƗIN'y9|SxJo/ӗvU 0w+߷>n`x@`{s!%`9g^^i3_@Z/wK<,aɏ9qJqS Zߝp=O $n7勾,S%y>N%HzRu(<+I:n@p9eH>s/xxHdO1NQ@?[9H<ϡ {ukbR8遡~r n?7p4ˢh~<ػOAI>']xI4UZt;5W?s r ;t7A3џ`˯ۿ@O.KI_UsCczSg>bZ3?U!v)UKoHr>[_=ssM,rN{jidIoZߛ^*,o C<~ /GN/aNww4e'LrmT r"w1<1?-|"ӪkhNB?|G0ΖW+>"E1^N(_;?:ZC'Z &:^8;HyQ^~$S!Vp䯊p@_d/I`x/ZON{x(dghׯyYۊ[Luv|%xE׳>>V\;:$ !<> >ᣵr?|P(Hp~lӹGi^^-Ϝ_3k{t޵W[늴;=tN0q8^ZM{8rM'zM[n*3;%ߤ-8ߣ/8G>K͝-'@?(YD17Gơe|oMXLMsGץe?lR^%di⢔KoٽW+|VS-93@o籘̯jƇϱ8(ׁx>z~+Dt:8N<}я|o0 >xLpYS0 z+#?-;/zW!3[ ,:R΁Ļ/;ʯA }GX'軨q>lry<7lCO|5Hp]3 9p'`'9BY\l}n|q?ɟ_z)Gy ùu "c =~ןs)/yp^yyW|_ Dw׽~ov!7ޞܲim<[vq/߲[yV$Һ:У˒Oz,s9?vy\~'<Do<ʉΗ=N߉ps/}7'mq~[+uq+ %FS\<7jg=N4t.O.y'Uq*zH'L7|,'yR|ܿq8[xXrJ}ȝ}\{]}g ?=ގަxu~{:oE;yEr8i|/s>]w&寜 ~w_3nϦ(C =O>z\\/~??v0!ݗGWEUwg_|XNֺ7C-1:9ODk_dhCϠڏ7ky|C/`y 풼Ӽh@t"w(<ʛ@[v ={۳|nK/Rk;>eϏ+7?:i7t!}Ixf9O)z2~[ ?n[ߛ'ѳgtno}w>hzĉI~p7D{ ͯթ~%n 9<^&ckx%~O'ZH퓝I ~WHۛOutiZqyLvr[Ϙbn}{iUMw)nO+ӷ./~'=k܊SKqЈnuy~?)דzrx ^]_ 9-̧7lǀ?7M#:^?Tg>h21^ t8+4zs>ѿ{+u$~@tX7ϕqhNv)ه4[8ժ'a<\oýnJ9ihϸҏ[)cSK?KKAvGj8wԏ>Á7u6{#`O0K~.8{ -|CϥyЏ"n-øZ-;?Σҫ4?̿>i_g ,3iyn{qNa{U?y_ t=/_>%K/Jo}8>9:i=Aڹ'ۥ}Oz o꾡#qtK{`OпKK7 ; ,T:_h:Gey@)vmOyI^)^ןH@{(|1a+ןI_>@wb[!:, u('h݆ow4)e yN|6M yiA)kC5OX?=6Ioyx,GL|Ng/oqm;>FA/)R";>ܐlol7_#^-3^|!TwEgs܈׽qXs;1ȟt@g0?/SȻ(}ʴIyTd;Z>я׫\ o1mjH[) =[z?p>r3_~ysnI:xܬ֫oHItx9OY_w.ۥ@Ɖ|UߪKy^AҾB_2N^W^xR𤸛Bkߟ:\9o֫%~_CYi=a: ]ǔHߜWaz^yo#}5/ OƑ)e}uw~O=otӏ3&{$eݛz0a)oȩ1c gWCnOσ4^ab] S|w >%1VgqMF\4Ϡޗ8.GW?=LD{K-CkZ_,/E =~Z{y#hoC;<7~^OwNu_mN.c7IS͇!c9 'rn˔G ;W^Wy7^g5#}@_A_k!}@ߞS4_7_IyKE.{r?-Pyfg'כ~5;</)~T/ت{7~M BV#};xCZ|'M)c9@tBb.wׯvtY-=^zA{=|,g}m;r$|֗{vGkOGO;C轸cvw1i[y ?w'v;׹_ t{x=_!푳YMGnIZ|Loc<[)? 0~St&Mבw$z^]KI߭71=-7i9|} Ο=Wsr?\`G=OuqU牾II?u>w_# G,/Ys77;tSHL0{^)xPq 7{^>Bm-{s7LwZWR'nǁ4JuG{Ov(ɋ+#WCWy}|tzL|:q$=:>8'XDgJt:zO| xsOQFoKm7C?/(|>Z> C{rGZ}:6/qt-XzZy֧, eOk xfo| ˺h>ϾykɻEoc^zh[Pu=85_ćOK>d}?QāW]6,̃oJj8:~Fv>@9۝V>MWu~,yX.i%KIyily8O4Doʟ}/DGί rh\{ӛ?mL|y>Y_|Jo~Zc~ͷ#sqk˭o0Lj]}dW7q{yJO%#~##K_~/^8Q|/8^. )߂/ Q}Kނǫ93^?Z?~ Lqb(?S?X>$nγscm<\7 ;~2 ߳}C|D:d'έ3wLwn;rY]?EOI=ӹ~$7-L}~ ϵ@oIdkފYNKɾq'9; 齶džGzijXxq?Kui}Simq57 ։D!yoV%ɉw(9ҹ;.Ja{' |Ȧ0F'?Ag-/;NQ:)N$ί'=>{wlyϕw,vS}hbגo4^;u|} yw.-azs{)>ږ>Gvx\SѧAG^2]~#I~ z~$>R}"]qt9tng>n{sCsn)v|XSon~>ǾK Lׁ{A?tqz ttt4\G@;1N)Ӫq$%EoS>n/r,yI3OZ/z<'~hc|q? V}[3+ Dž$m';03ڷ/)^~P~Q?+wZ; |a_:_ݜf)# :~~$>UqLu,|ׁ/wD{4@ >v*/?-BOق7"Q4ƞ8i1OP߶W]HĦH;;?w$G1mҲi'_я!?|0>i7Sur.XY_ou ]Ou&nyHr<~+Z0k}wSh]rvzCxܛ?(?U;}MsZ_+']'s]Ȼ{ƍ'1;G{<|}Nx@_+ofc|z;.J0>]I풼Locӊg?x%N5ݗKdo_@nr+_˹'GC~<(It:=w#C{Hӎs)|PkyrO~9^&G{E/Tׄ<%^^)2Ϻn;wry.6`oy\'@~NI@lLk}A_7公EOc1ٱi<Ӽ}w gaon2_DNm_O ݯJH⍥gcWז;w!tY?,ߴh}v< x8_y~$2sȴސzX_W?^z!W+p$Ki?@ C7:=A){Чr}r9|Bb^$:7K{COC Iy4?@z==.gxxqp7=~ Z[DOr~E2m_+9˟Λ?_wtZGnַ[OZOyT?l\}HzߊK7n9ڿIl7SY tiO(?#k@ǕrnH8zJ/_<>Cz_uH7<:z\<_]Z:޴\:&yWZE>%:wͭol六ΓC?ָa'uK'_*g;}ۖ|(V4|ٮahu[r6?ǡ|&OYNI~krӼ?L8E+jwHt:>Ir2]sEϗV 9B~Ώ[_ Cܓ OSCI;ե/qāױ_͇SE xֿTkqqT2ͽv#f=p_t~%ϗָ@CzҟHy2Ο#t>7߲xS^.΃$[y%VJ0񂾋%q5~$y3'amR=V멮}Nj)~v~vcvO u<˗E׾Ck[>uLCYwuxgWlxKC>0]3Ύ[]t[ge;f}}F ǤW-ki})tSXZy*m}j}qHxP蒞1nq?}$/5,3Bюy]M`o@7=KyUڍya?go Ys-;2kҸ^Ŗvv).7~M+Orp?VX;H+WGpi:sϏ'>RZCeG'b'GV$9N(/|;ϖP [WxQ ?=dm?E)Iyyio&2Nί%߳?t~EʗءrI&~\iq]:ty<ʣxs3iпo~{ۧtt[Hy{$޵ z5 nioޖ^q}\ϻ4dćb>%˹Wvָ8χD幔?8g=^_/j<OeOJ/<9uACw^0w>&Z1.:mW[S=IүTo7|W-S>|>͟= K7GzT>%+_AoZ/s%՟3?%>j`>{q~LU/tXrC:Ue$/`x:w5э>C~ro՛7izg$#=^%}xu=apV~'C#[?b݉y`:|'3X?ojR:/K#~yЭ<[+۵dZ#OÑ+ rw?A=uG!.qvh{;_O}u;@uq|z%_Ͻy~QDlG<_뭡clM_o_h]}rDo8*.hӁO[xRLLw1@3i9+ҟV~<@e=ZǾ|ү{G81ʯͼeU02FuK^z|nބ ͧmόgBg[1g̿>Z9W۩z}b%:}:¿3߰ z Mz9^bnx.Mw{ &?f;qNMur/D;ρZ߾nyy8@5o\GW<rizsWtAڽZ+5?MAAE^srG}9UnZq}Hs iǸ$̄ _0SF}^ѿ N7??՟8o c+ZsMapyHvjҾo{0Z@q /ౠw'#?pnTG`|=>cУo\yH_ !s7_.m+~Maڟ7@i>:>=. o΃7I@]|qC^.۪kIy۟dϼN|&a>nuWkHrw7y?'u-Zt<1"O4R'$/r/u~'88s{nKnOA8ϵ1ϥ"ߟ >{ܐ7xyA˹Y d x$!||8ɉDߦy+t>W _Oo2>?wt;mͳV1/o9bO[ -;g:tG,;3Je9ӑ7LqR'xZwgʛ>Fل>zG8ܭ?s$I |G!sC:~.]s믬.y\N_ICl''Ivw]6c]t>UgA34Fug'vvy픟2Χcɕۓ߁ni_.^y8 8=rݩu1|P!<qx9$zo͏넑7$a_*tR y9fz4o 6._([4 Gc d/op"Du^oIyg{,t~7x>/˭yk=E) oaul@~$oINX}~Umm1.w9Hu^ޖ~DwHvjQ̵?I@w}?{~nAOH%?D?'x~]'WZy"5 aD_ʧ]xY,Wڻ;ND_i7.cV~v?=cm_i?/=4>G@3#ݿ.sgq|?~$D7CבO9iř/?췁)u{vc,TG5t{Rs XtC}}^A^9/pDPә|u\,yNqH|Y<Ώx@?zlzr?MS缵6?3s(o }^&[gAx?Di9sc?PW+ }_'V|Wy_ZD|Ly.,S—W|^NW9^"y'Ins,uS>5{ӝ0YQ5n'I.g8?q4^j]JyM#.R|`Z$i=|O!M>]=ҿG&ّٞ 6A30Rr=]I?ГE^"[^~>\g,zEѓ{o9z]2w }wx-wsyɎX/X>֯4}R?!>KѲ{K'/Kx]Ǘ#Gz|#[zٔw2r}ȓ\dr'a~Asny?.'o+|-wLz}v7:y!Fw,ә(79Oqr|=r8nkW->| g몹y~ܞCPzM+8IXS㖃zz覞gyD㚞R}Lw _3Nަ|וὨwkS/Vj.lMMч{o k}ozlZe't= |uq+_C}wَzGuyb[yu P2>ϐn{~x\t!Sk.u9y=[ŸYCH_yoɓv|ֿ.yK3o^ Y~r]֛~~yX__#?<бW tA/p0=V~tX UvH@aGC]?ܐ Τ){ugOR*qAWuQ+KϝѺ*8w>:R]uio>~r[w19[|nuA\zS~mBX7ɿ5.v(=n)e~8Ot{ޚ8:ϻn7Ekߚ-;űs~ >LYG^Wu\+qO#7/>ܞi?Ɲ~/哝\#?oB^__sSZ>I#fmуk.o4^ $:}?cP6^aZV>;֣7Y^yx~lY/wOkg#~>ZxO^֭; _}g?]pK7@o>wkqa'zy47l๤sN)+_=IRݛ1^Bzs]z{ܠ3d>Ҹ._ֹ>)Ks5Bm]M= ԟ}y)v댾3Ư7]-H{rw '{]סCr`3z>ŒHtZ/[o_u ׵h|Fy+Q[8F:_Lu:/%Γq~ y=9y8}>.wqƋysQixx~F_:xvzTd>?:q{Z9<9Ks sCQW9dZy}/+Y]`~r1_ %})R\dh,z\C)s'm8'@R!~x@37DGZ{땐^/.1>^_'=|NӛX;h<;vqw~hGҞ=!_u{B;)Qi}wy= 7׸X:[ׁ'U9m: ?uK+K!^'qHy3!j==o><~Ey,]ad;ʣH=Ҟl'Sh+ H{ˁ腮dL֯DӒ3s]zkq܀-zSY_Mv[IO9C/=r^uqRiLO_mrV^4O|88ۉy4|N&?b/KT7ۓh|V7?tyݞF^4M)o[y dҋ$<ntjoӺ}G=?=_OU/ =N\? [\׼Ky4O[}K+Nz]^8\F| 4_ :x*nܓ=.i1*\ѯw^-Nt~l8k*.~Bߟy_* uoʣ9Ôb|y׀{-=7eG \oV?m}=>ЍǡE?8i)=ǹzˎ{K͏^E.C>B^ }~{+R~%S#cums-~,֧Dkǖ>kR\e~ΛvOz)Б(ZSyC=tcGIOOK>^h=^ZOZ?y]W/<WO_GF9O7Lyߖ'yz֭||iA'剸o9ٞz??W^z'HϜb/kܠX<-TaO@/uTw/sj?= \_,?է^SI B֩ |;o77cT<2ʇ~W |;=WA?ӸmϗɟOyG1< +~Z+z=[?*v!2Gj{GvqjwnOÞ >a;*7?Iu@ 9ץW@K~-$+饽N+j77.4o|]յW?W Z//4|x47-r|Nֳ]MM׷|Ə{>lJyjgzG 䟓z%hAO@nu5[y{,|kO~|'q}z]x9љvzCwMbʹog}|]z|5r0no?׌H|8qs#RNύp'KyMm:?cra\/;iz'?c=JY4nxv=n5[y>G?[/l[<-y]q_@[Oy{prq@_5.ˁoE+~Ppo ffB/GW_*|HYoZ`N٦oq ]/tNGyEA/0/(p7]X|V3@} Ei[i2ʟN o5ߠVZu/|o^_)70ГEq3]/l M!/6M~YYNƟ!wW,fڏx^8hՊ;[<32Nwϟ$os?~}D8A/lӟuZ$ZvP }I:yn>DgDϵ{nݪ?q;o^,<ahʗJGn$gLZJvS>ǎGk}Ok_ߓm׿'9IIOl7$_O|כϖ>ѹ@UH>D,ۇ׸y?C^H:Tx"|䯎ca?0x[О?s'Ge\=#s~z^eeև)7-z/Lv԰e~vvSj ֓̓2Һ3_lZGrUE0/>oO禼?9݉Zt/܊l7 '~ i/Q|?-]Zg_ x?'YMMW9q鷼mo-o3'>6Ӻ~% 7VH;r|=\ \ݩq?=gܺ7Aڵ0%M_ˮ_58Oɯqާ|PN|$.Ȥs Ӻ?= v|7uM~/)ζ1tyB5tb?h' O }}ptsi_OxWS}q=Qxߙ׹So|a"S ?9i?qZy)=zO H;L'|$R|t$=m}r-x#v7Շ$=Zy/ t@Iʗ? ^-6+ڿ8NK%K!\0:1Wk?q"xmTfdioc[n%WSi1q",??}h?eݟIUѿ!槥οN()o'[nʗJZAڿ/?ԸB/<|v0zuC"'K;Cq)Fߐ2xC ?Η8o =nI?~ys->[s(y!I<j?W֓t_yEr[zbh=K5I+/'g|ݦתBޗ5~ƛoW$xɾ{~:mk9~@XxA{ ]s+ {Cnz9cW#ԟ9uߣ&הzaO)޵^Z?<{=PϐCM?loh-$~7*LI+yD^^?ڷl~ơ(t9;ʷj:|:_ͺ.x|$?Љ~<G+^ %8wmZڮ?-y[OK27Lo^ܚo'oϥ')Y)K?nε?2[AҸ'yTKᡝrga~ySW<ۧןT?$>}/g >gN|V\ÑO }/q<W z\=ަr~W ,7M/0|{iN<ۥuLh=C#Պ'y*cz7>OܘuOsQ)/@t6m|%G/7':]J7{{w_8CП#͹ćVWݷ;y)lC+qyHCo tẊ˗z׉Awo/y>oZϐTy}gDg]yp?گُ#dvƟl LZo8x>$? K3iOEC?KK_̗h%OV'ǹc=?w7>O8L3e%w_uA_]g7Sy {"z~ ~t׏ U/E~'0+W:y~=zLrn'l}3]#ϛO_}/u^Oz>EPtߒ~XPOpn~OL',z:yhg= -DlՐr~=4 z\'#>O0_!OYnGj_N0xaO1~U/3@ g}txP[/}oW ؗ#q/0}L}=!.uxuz+ʻQ/}!8z=`+q>]];bKiǸOomAƃࣿޤ}D+3"_XM?0_E\_9<חsۛt'yx<>L%}Izq8йHn'3z=.s}@}~|:OCr5n^Bw[myσ/Յ@!Ot:&_:}p~rHap@uL/p~%ϙOzlO@'n4.f^a/%>>"ɣݦ.c9@qHuSt u2<7O+ߖDgi6]EFn;O1 Hsyx:_O@O 0I|;*G 偠O'恰)nتzf4WM`ˎ>v^?'=NM_k M!4IW4Zup:|$;x,/- y=o>y>$>lw͏u-⮔JcAS=)nvs_|S~)>w<?ߴO:3;yLu>̼ÞX3=^ t{]qH 93oyc9oM˱@7DY,tЋC=M_H*|]o=p!LX2|tܑ<-DtTOCO?աX^K&i~gTyj9_ssJѭ+?<=w4G7?7[uFh=uocuqC䎞KusV{~CW{cgIqy ׎/)p% y)뵴NrߔϛMf\[u3s}O۟VݞLJ0~'}I׉/9yھ^n@7 gjH5\d2O˺s؞Sx]z.i{ ׯK߁7J7v;w ~xAO;s#\Oƛ ^wA|c{c=z.]rHۍdE=}w# >sJxO7Tg:L Ƈq#=2>W)[19ɟύ=?̯ySq_~k;9|? ng~?м548|).Kt\*9'^}yKn.3LiZ&@K.JrE7OGuSև[N{(uyZ/_־Xߎ? |]Ek|_ps7}4o<G'2'74ů-n}2}II-y`ƘVv|XƟ@ӗ[oɛi/㺧k׭_I|nZy#\;xHqSicƁFz^'0wAP)Jy3iO)u"|CEOhs}ۃ~כ@k;~ޔ}"ћG 5#yq ?γޛޞ)oӝ7 s?E}% EySk.i^}t~Rn:?7W-q;lG{缰e?=N^G=VYojڧ~:b@K4έuy3.N;~:;-~+Г{>}O$9^*tICʝrvq}{YpC:{>tm/r݌N@ۇ$uSE: dH{Ki(<R ?Ee-t~N>Rr@2~;~v6:^/~zoo+⌴JyU?7p(Kbn~7?a޿P3xxxhx"q )U]8^b쉞4OLt'=K^pOϭ/ Zni>뾟Cdx7_Qz`KiQ}n<0tܯ{In>OTrR`g@BߣOx]Z_Ρ<D|8Φi/sӉAn)?:6^׵8'rkQ5yv[OSފFΩG`k^q'T9kCqi+c9qw G\O뤼ピwItZ%INszk+͙u&Q^>ԝ |h|7ߣNr Cnu:_< 0^{ z?䙠ة_*_(\䳾(X.^*|HB<['S/ e;=nuё?eL:c?@oz?wkh;0g{>_k(1nD{k:/қJ;xmZo| )OÏORA^Q=.^u@?K zn], {[>1>I}G%;e}<?3'1]I~R=P/η~za =nǾޒ1t̕ߦJӝ?{/׭/IOX˭Ҏ-=MrKD'$j~x|U+5?$?'븦iz0 Sx\>pB}ߧtRÔou8J^g$OI?q>/$~[A_xHo#SW\G[wG+lѝ/[ 'f=oŵ)G,WәAnos=A >ڎX֗d7EZAy`:'?#~₴D;Sέ/dZ8ȼfx~8[$~tmo w~yK9?%-_5^;5H <Ϟ' Q?c@~ߏKkyU`oIu]#9a}W||Ç=띿Jvz>;轨T?k1]i_rљ]Hs5>9}JϙnskMBD{vݽG稿_vpt8{j1yY͖KX#Sc}׵z9[q.'}q;~ˣjx!%7N*1 ?oZ9+!ݴ}r!?ہ#z;W_U\q9A9ނU9~W/](k_g/Al'..yc͇t^vI|ˣc>ڗ$?W㤞^g8NoN}+ԓv]^Ϫuqog{޾/߹Bԁx։zӃ]Ճ[?_DO7;QNn{ v:o;/~nX?+~3دHiͶ=r؞G|}}QK\U/~|{6/`v=vQ?>i7ߊx?5IotwRdyد컿d6#_l=WvsZݟj~\dezփz|>\g:ƶ;3v  t波\Uv<'z^ߕ_(:nh?9'g~yzMRdk'}zu^v奞 롊 ;ڍ߅8ѵ!EdSnmީ_4{__O8mx[ƭz|%bq\<='9įmklŝx0N'W]{r?;r?j5O-⡡OԮں=_%8qפzsqvwvi?/햞l/ ^XN?WiHK5w*Prym9_*Oe~T!XCnu!/a8# ϸ#;vM1vKw=mu>$Vߣ~~c7nǦWNW')]/_]Ἀ~vQs,;'}%OGۮ\~31}\p\B=ާ}q[qݿFydy[׿|OG;7>[=]<;`=8=O\߸rw'ƋI\wq;֋'>GruLPhP^}ߵ~śO׾ov^3_<C~^٭zyپ#zw8hkᗗŔ_Osu'E㮝BKjs|DyGUGqT?zOǺwrpֿlgvͻ9q0?tY7}z y-Sպ~v!Hm_zsg=*8q`҃,5lg9t:u1ǵٸ[OΟ]ygQ~.ή*F{gPwDGs::?V98*);8vEwC%g?/6,W5q;;w)ڷ.J}Sgѱag{:u.vq+~N}f;uEKetv=?V)n_߸p{?+r+|*nTqE;/@}IrX<Ώٟ~ַw\\Og<iE?'ޅԇ)͍Z/iW{NP*}H5o]y7t;;Ї]N7O/*MU5qz ӿ_nxqE3ǭYOaweQ>~sUsnr7΍}Uj6%`[rk'ՋϠO+]gv:wE{D]8|㬋3('7>7K~;;Ng',zc<\dz\8rzx^T4>և>|է}wQ҇2ǻ4K./ŝt]pxCg܅gý_Ǭy{δs8{Ne'E`\FOlo.d}Xx *g*/ssTvߨ?g?58vs+qWU+"󶧰Y8>;® vT37!lV~y_E׾p/tvzӦ^.qmʻƱu+и{ZP~]~Sω͎C} 9\W78A=%!Y*1ݲ(qe3+/!p?B M:8Ajg]O?ny+_ 퐎{pg}<ߡ< yx}xԝ,۽SSQ;[q;Aܐ񇯷uv*NW>iNS(?p4rJY/|>{A3~bwv%{Qgot]vs!:v ~ {ߐV:?Yَw,a7׃TygWA8Uݵ'W_KꡲǬkY~C ??G}KWwWa]_vVw> uQ]}WY.^?G\ߣn|=|(w,W}/w֭C: c<]d[ם})vqc9u?˩K=rN\ఽG4<6wzqP{ѨG/^|׳q]q ߂1W3rQyhOTprϻ86E v?t\sa\\Mfonjo\8Me}?:~:ƍHg\p^.{0Y6W'[q ԏUy.ם'M/Oqv_c_Wnͻa;xtq)?#s,V8lωUšt]~9K]bþQ."Q繟ːvi?i~is o|t_:_ \;e;y{\z2^-_q{U?5[wW0a7WL@Зa*8=(jiw=|Zgsy!'_WXW>W>_\n׭<..RGu/qX1uv<;+'ys8?r]s5wW:?wqk/Gs>Ww*^cNd۴Sb<~]}5tuvi|:VqoփXSN8+~ܴߨλIq^Tk{:~rln|dJiݪq59grsXqqu`kܸ0*O? p9r;7T~VwaGm. =O͸s~a?!vAU!ߕ-rڇ>~a5Ct~+=9П9t}T+B<#M}n]0>QY.7_:wgsx3yEө:_|f~r~e[y+gUO.7pWsjO X#?XP*>D]ɮv~µCO]݊No>xqC:\g,lh:|pqw{/dwivIoB:C{_{;s|Gw?ry?zr*]w}W 7]>qgί>}ԓqu黗.WD /pg'.z <u^};C2^YƟQ.)8na=~q c?qr~*NcS]O{Wqwվ0ro/_>3պƩO:ڭrTn?x{f/u߻yOX'guO]~~_~_4=oNc%tzQ]_grS:od{hҝr>]m?l}}G}voáv{ھ/_=:giA{^>{~+NE6wC>W՛Q}< ]OG?]Py)׸?H?CyҎE{:Grq!կx눟Uks~z}= s~vzg:r џxsUvخƍ>!/ҬݲCW}Zw:H}ԕv﫳[̖W煴Nw{WՋknֵZjQ{ʬU=Pn/za_ƃGhvu4cܰf3mCNkaFfvL?~on\g)lxIa{Tp?;׼٧v}eʧ{=b/=_dy4w~!;|;@;z*ngcGБ/S2?kqL_yrVٸύ?N7>_{k3uW?y0k:f{9lRwߵ׌p]k~kj_~3צT}3;w~sN71PEx5/>8?wUn>nhڭ뜟]=*7ב?yTXf7x?fW{.WhWϠtiy`>(W;_7~QQv=4}W?V}\n_ !ith:ߝ'?!ރǰ]ثvL:븵}U\h~X:/װ߫~ K?ЭʿnO?Ώdu/u?h']?_?xoUGTQvr=vy+2n/0,'?nS/D[n] k]py=uO?V=_kq ~W Ue/oWh”ǕKd:nG.E3:f{vؾ0?7.t`~ 'x'{YOu/:V?en?n'r~D?읥?U^sYԝ֕]~?~w\yW|QἊ/=k UH7t]ҍE7}Eg\n?#]ߑn|5~G*qwܔۭ+zu:㠕Xq?>$uu~cS_=>g5{yU{H׏9^`ggy+8UV~xHZ[8V~۾\(;ι .~@=\AH/~x깾.>o]4r!}Q߅4\_p_tFmWoynN.nr R=3x]8/~ ymNQ;qV/t sA e9ɻ=.C}WB:~A{~g(uaݻQ~qQ=c鿻|Zg}SsOߕ?|_hU.jռǾGrr:izۯw~<eArspW?*9?We9ϨEcu[~z[ 3Wsj=ޖyVCBǺcg}|\;}λx3O8K\O\̱GuW/p {9|Xoy.NOxGv?d=~B?psl񴝗uwv:;?a?dq;8t1T$@w`,߇(׬=?t[nH=1sS*Ip?)sqck__v5ϱ/w;66?1cM?&u] O:ˮ_=E_wv6}7ۤxqv^qO}>=տvyOVI?"(嘍 ^OSM_1Ǒ_%g/YWw_S},쾟x#sKrW=b/7U>5%W~ E*tz]ޟc?n=<[?Nw}o]WqVs`t==jtw~qzt.~=wd|F9yG~}X: QyPq ?o\9:}5|SlܤGnn?Wys嚍sv.}ua{λx9NH9^wo?r^ڗС/9pnU'gy]<^_btl9\{-θ/[ M79ﺰG.Vcn8\zkⳲϹuۇK*{f֣ޯn?+_ٽWtv|mA+;,g^cN7>/ٞfU}x_p}*y;TV@N p qک{Y/..˸-[W^wʌ/k߁ ];|lv}3Jm6^ͳQ6U<ۻO3.8|8Ug.u(׽zsD3fx=]79?#:'~߻8O5qx{1n#S,ב!|Qq_FE+s~k{{nQ7Mc_.>lu>az9[}v{A7~tt_]G<&UlƼ6+;OkbVnͣf+,|ψU{^gu zxӁ5{/`/$e׭'I}5 ?'f|_!:c;ϳJwc#}wh6|/]wcBu?z9e_8OQ}0cD;i?{^lP{?;?b~@:i{EӋw+~ ו=i/J?ik_N=.?vq*޻MGw٫y8Yrx6yy#ٷ ]{tL~l>w𝳦ܼ߭OuIO+{5=s3.sj?1`&`g @G<n};q俛ɸ/=_]}2>m1Oվ]yyǪ?+`y/bPŅyow9Nr~n}WqgXo;ӭT5ŧ]?s~no>wry;.41wrS y8s&qa܍]*4l/7Г׎w|by{y_\yn^:u벊/Mwo;v϶7<㼝yAOntg^G+y{rd0nwGW8y]2n->^gh7uu:~Kخy5<> u֞:Tf=nw7GاN׹oKSW Xo{ǐէ g{Ky Vy\^Tu;_gǙ \Ө?ivQqC׏<ԑ?q^G:]}YVv}w;~W=..OKwlo.9w *~Kd#Y?=\hl',_5e}=U<*^g봿Ub\IW<˕~N*^BҞַ~v8}gtq,W,].NEsf}^{./ ^@gQ87g;ήvy3?qqB}U|D=h \w̵KUf.=_OOuqNgiitdCg*#޿ړUjh N^U8'8]uٯ^@gGf9c\[F0ʡ1)nWt[?.++v[]=b;ցnjG_\.>Ccinvl|My?k`o_sS8ra^;?uC^r>X.m]sߺ~<7q=>q_c(͕Q/OrݭwRW;7bփzR߭'E:;\\fWWgϊDZݭO uWprݠ.ӝN~e{E2Q[۱r?}7(ӗ>U<9^6B}+~\\/=n_As#r~U\E>fzK;hlUi/Oj")k3aXi|SJqq UO /*'ۍt'̖dyq^񓯛݊c{[yΚgb9X>TvF7/zqqW?,'{zUq+]yXWܸ#nwq`yPo~\|7lʟtVy^t?پi׊NGX9#Ks}K*uAuq.ʩ>3}.ux2:r>~j..Tq׍vS-_59%Tqn}ٮvUln_{i}؎#3 fNnz%Ykfv?\e'4}N_~]޲ߝOY"}O?_W^u!=EѸtwn_Ut?yM`y!%zgxOo2~%G溋C;_U9?T8D'뮟Ǡz`}ϟGm!f?c`yX/<<Uwٮ]g?~֋O~˿]9I_=ަ~ʍ,5sWqõs|t~S2q=r /v~"t_}&I.~(;Y:c֗k;yo.dh|S9ԏ^a>2].tjo.Nu }Ers8Q=;&tq y="@r;$&e9'[|v+u U|R{OG{hOnYGq@8 ꯯P>ѹߵAGzݿWh'O{/y\=/m3nqJ'nt,]si}<'^N{Q;V?$D5ͳx/r=9?U\>E]{ήWٸα9dk+~5 /9ֻsO-y_!V<}[k?R'.>}ٿy8~/=urOG?yɭgz5^ͧi/d_>},s<9sScW齺92O?b|\9z/j:yS̳3.Q.}]>Dyx^3p8Ywg$$zᲰ:@}8vu{l=W;]~8;n}ѭKG(zk|Ǎwq:}z]17Ty%w_8Cʡ|/q.-u.=Z;\97=hӧ:u]M;y{v62w81vGOʮs}Gگ_+(߿ٮWywW*_v~~^Op&U9X3ګz_8Q/I__5E7/ayhߕG>Y伈Y79X"ٶzMu8'׍#gFwևy+/q^׹oEM??<|ܼWNe ucֱun<,Ojjבv_{oc]CVKQ..T98\)IwP|OvSճqnYů}';9㆜ήjEwu~gcnNYN?ˇ>;w?Ng3C_x}6*_屯ے\qcWdU)=Ǽm=Wv fd^ǁJ}BSP*:,Y}^lw=n~vT#\Wfq{ul?%}fi+Kpިw`73]o݇qr~rqNG}Wy]q{oyucߡi7}&_cG'a}ủ8GG/W0nE1?;!Ew^wo:} ˯E)EF^gv_yzstd*Տ}:a7/pf٥{wGL7:lT@ .E;i]Hdfixqc~H`?_hdy#s_|{Ա|~٣xOwqK^[y;^"?m,*w(7s< U'WlSV ~ 8k(ݙ'^>y~6/.?=j t^|b{K>"ϲU9[y{JBe|T>k{w켿>uZUֿgcQ[wWn@oZj2[+fŮc^tWoc*~cWx#~ӷqk8yݞg|rsu{CCe?5#<.rFpOjCk~wPtǡn < r\|+'C;e[o4Y_Žshsߗ$&Ax1J*Nc97>+GUD;;iQ?:nwW'~fxSr™MՏϺU3E\|M:gXktqͯ`'yierd9'[_la5S/{|Z*KWI|.ee{Q{c{r+;\v>oٟy).xۮ},GtQ9n5+_C)jwPndE~Jyi|r{'N|qSN?߮]<3đtKr6.2VۗlG͎g?*/\<:}bU.WNqwv*q'W깻w`7j83_v#s]uߕ^Wu>k\wSIF9 8+ϳqzuNr6.9;ނa?pUuֳvpI3\VE͸..̿R~W~ǭgy}M'Q;[ /֛kתEӑ羥xؾ;d羽oGϳ_lYpXW]<,'jܧ+;;No~~ D7/fEy+gTB?p[/P58#|F}gE{d"igvCu)D?ͺ[}VcMnv}^/Z_?% daUM{y^cƩYG%)';]`e3CIcq2i,fN^X/ϫ;;9p|8UUnwgc?&{ٯrVwat7ơV,c;ݼUUVvzN~SgM\g?C}./8ܭc7Cj=g|ڭN~=V|ݸ_]ݾwOΞ*N֣=W]<ʱ_o`.N;~+_6{hyWl箾I箻s]y\[ 5κԬ:V\KzƵsqSiԃWi?װ oyٻ︰~i_?~@?nf?Ƌm?תF`;w[7.uepn_yM'RW./\%+j~(^4~u?'=8[ < j7'r֏{Dm~Tۓm]gQL>fi7g~w;8iޯO.{5/A?G=r?fɲ+:zy=>~v;}]9n!ՋGuƷGv6j^rOn府ujTWO@Uq%7q@Z=cI;O]~Ojgwl'KSW*xnOnvc_~*<\rv/AOCUPUJ`9]OU=*!{8W^1SǩWny*ʩrV8OÝ>o˵_uqTG+Gֿڽ{د:A?;GW |aKS~XUA;uNLZ^ztvo+}$|/۩vtdzHWoVqiyA9`κyH\Q>,ӝ}KyοΎmװ"ga{-I+΋ԎU{~pR S3}{_o/q1W\~;'[0>5nz=']Qu=w@.QrV}WC:Bx뽽}[Y23uѶ_ <.SQXvK<熺sjf%pe;4aiλxB_,w?\w2isj?*#DÍ{nX\+4xz gwg}?Cy?u{G|7|ԃtUPPOeýߵ'_Acs]ϳ¼b:ZWCcz{n=d'MW^쮳ԗxux3+ݵ?Qz_#1.\ﯘtu;_O_^z-{v]݈to?ܶk'i݅w؎nj{n\'_Gn^E;7j'NK_Vy>o]Wy#]ud}O>>=}k>.or<(!d|_wl<u~%f&/S'9꣚2/W,?71y#ۡؔw1P]3s^쾙QN6O} 1qZױ3u8ry+rxg8eKSN9|U~ V< ^>~=qj~iq؍Uգ+E}ꩊ['^TvVv>ک9lh_;]=NGa\\/e%]| e? ,w?U~A*R;{e(?_s|;V3=U_7rnq$]]c_|:yҭxLt]?\ n^wxQ.i9h<6Go7@!g੹Ջ+CHWݱާ(8l\N_ݪg\iҗHwK;W^\XNwzm]<Oyx+{^YDz?[E]R|U|Q!T.:_~ʻ[]ww3`P}ns,k~վw3|_vH߃v^|n-7=zviHt׹ߢZ/QcT>})W{VoO_!֯qɽW{zQ|D\ya0T}5rE,N-ոcߣn OW命};5u*2k{Vn<ܗ+?1^*Սy1C]y9q̪ HfU;/zm=ZWo2Σr vh}|Sq++}42~5~"uf)3^k+8qϸqq|'vyՋva9h7:S.9*>/p'Gna!]Nnn^}z^^}v;tfgGs?~ܹ)QXN>K71Z_a;/74U=p?ʡR9]|wagc4i}:y<gWp;qk.]yUG8c>_]`9ňZ72C;][}tFb/qW*/)^Uv|t&گvY#U{m?\">ͿV㥛/Udw:s^|3E8n|sxΑ^=7ޅݼ>kgSů؟?.zs^+ݏL}`98k'"yv8i+:gu.z?/NAŁ:ׇ\7N7_pG7t tu$yV}<>{ԏvվ `}g<[nw7J;HG<}׿xA[qݧy㩱OOp,W8wto׭i׹G>>i׾8+ƏwT/vǯ}W8lvp=.M9=+(RA9/8 .|Unl:]z?}k<`c}s\ݼhv>V̼٥y=;#{n>X٫y"^/xz9&n=.+7KI7wAkvTWoyrQe;!.ުDO}YPcOvG*{Hnl/c{w&Wݷ3y?KЕqDowcQǾ츄n]+?=xwzGW\?w8n%us5yGFRgG؟qi:{OڱK~Y=٥y9t^tc}gXTwEgM;>.^#Ό";]_}OٿѬNU};ܿd9y?u::~U\sXitU?';UsU㈻R_p5ۍ8]oǜWeGH]`[NvWI?{q+ϬqܭcR}>>uESPR} ʍ~Kg^.Q?6kn>~nש|^޿?~RGݍ.дowoU.Ǹ 1_"OX?+rlvZI?H_|9hv8#7{_ ]Uk*#OȐ~d7BXWN;U?^rƧ{fr㍋9Id/z>ۺ򸸔_UKPX<7Fg3SUq1?m{o!XcG#]Ǘi^7q;?qƎNcg~ᄋ6vxP/!#_gף<'w_y;{9WAz۟Uϸc;wvItck/.Tiٯ+v o_eazG׭L}]=d? wZ:4vrtwWw o;apy1׵G;uZ4Uo~^_?m1"~aM}0nR_yŷK~z)Uw,<oƜ<_6yo9P޿ˬWk_'xT.g]?~wu*nN_}c|Gw_wss*^E}p;ܸE:{<]y嘶'~7Dnѕ۽_}w櫪E{nޏٶXw&ܿ#Sq;^Vѕo߸u8Վy>י𼾣vq]Gc]TOVyٍ}@wfyR~+]?rqb`m~d{]*ysӞGg~ ?ecz\e\yЕ]O|ޝqT}T9ntήGMnr*;=='5ྯ}Y#tcXo^Վ__vs c?ļr:&{=j;w];Lb'zwW6ﱿqpv|W˼1wtu{'o~ڡrjW&+æ{_׹U^CWXQ?odocuk]pfs:x~_EnN|~.g(|{ϛ?b=Uto.wWrT-_=T/cQG\9X/;3G'q|qOՏ]7?nezKgՎc5.ey;ogK5PWI/vgӫ'߸ͻܼ{̳~_++Ϋ]w>٧v>ϟVjَǸ}G: ͝3k߾vyL>N}Cȼuup[_޻o~~R;>?nu;Gx:߯y_K997#ݸe}w8r"_ͷ8OEKn=uy{^uv}N'9.΋Nph֧'۸ҏq}//{;N˹߻uAߙY?\:N>Kݯحϝ,u}'0h<+\??{GOd?7XWU8#;zގ qԕzAW /"+>[^G,]Jg΋4zοDyd똬[p_svzs_9\<+\V9{<*;ƽ^n<uXz=軌S;e7EgשNGO穗_Y{pWxyOUoԃv}ݺ_]j.^~Oǜ1Zt~Zh]tѾu}'ay_ ur߄8ÍNa?uƿS\y :|tq]< wP>)V7ҝ'G =.T>+Qyx`?iiXlhСǃT."~tW/ۉ}UTn`At~SyoڸOy+rr~ov4qi};nn`yy|>{k7ﺩW8v*oj*?o>sA7:=g8w.r^ʻ9ew?Է+rr2?̧yݺO:8Ir13kuO^g;}]so*$sS]gqlj=z۱iʥ!kP?yU;T>xcqvsRPzYTGP4>h|?\U#hn7xW}x?6wk ?}'O]n^0;Op'}kw9-7Q7_qkxg|G<Ọ0NH2MW,ea'g{6Mf:~cgڵ'aZ,/g6 {~cGgx~<˵g`*3˾OПXN׿nӍ|9^T}7o7~r6.>:P3Uqs^fԩ.nqcy֡wzGұC_rD9ΎsCs^ܯXW_٥;IG߃_pr=z?:F~ F}Ga<]E"ukvuys&{_l׫{n'>}H?h5}ʡ8fHc}폚- ~8ѬnֳڟW5oݯcqVoJ(l#\_SŬ#//gʳUzxox(inm ~ؿxl~g[U~)Twd[z^y쟨~%.VKwg?Q_qUq&WU?@ip}nk/.Y_W9*~|Y_߿qooߣN~5QX k6>SSh.8!EݼVnjk:?n>%{^w=Gg'B_UIrv=]4xQǴopO{t59ݶ;jOxv[9vK!ݮ8?}o}ߕtW?~ Շx;8+ l2GԱt:!;wsG9qrjny U?7v99γߗVt/\pthC]VCy 򁴏NFSW Qkۺz1;杯qP}<ݯr\e9ՏhQ{伷l{|%>ߩƛf>,=GX_j/q] Qk\^׍,i;fs:_:yCtazA]Gg/*Nr ;taKoxw*^<~0zݗxwI}cn{8u[n#/Gnu/u~}C{tDarUvW?~?'.zlcӷ}~vUݕ*o{8G ۉ:{㼘}]nuD7Ըzh69uN=w6?;jil9\|w=H=W+Nv0wopq*#2_M~r;iUiw9jx}X̃v]$;YxS]v|Uw4A㦟({>wtqy<*'?w;~zUx]Zi8_fUŭ{U=^~ۯ=Nkg=#;nNލ3rT}qe-!ϟ|qVvc|8a7gyȸ S4<4N0?By`5Nv}P_~վ/(ꩊ;1u h>8cKU?nc8+A~}Gץl3|wry_Om?V#X~[xy*vG{Omvվ;՟cX~ y ڟtW{׼yzT~tt~\7qμugwwwO25PO>/}y]ji{]ZC?U9H^g-fցwn&E?t~>Gy#۾8ĎߪݺԔ7rlW}_DGf9~}OkG;Sƃ`o_`޺%Ǚi?S٭~Ad@j[ogߩs^R?JGKθIG}'^/\;w:^]%증-}91Kyz1NλQ:{5{WφqY;֓K2tcS9T!qCQƫU5WS; gκ*{\Mǜ?˯|׫[x.{ra<ߵWwv:Xp<7~*wA{}Votj_ W^e?כSgIϹDS\9s]}cq8^>MSX?xeSN; ~8_Ϛ>ϱ*4{uh ZcO}~r߫~u^;4rvXqL'җE?[YGwi~Wu/U;~cv|^>õkSƛ}?:~rMum?qqN~ެg7vqu8]Tֱ\s}c#Vg?v8a5qZ~t₴ͻHݯu{\vvN7}j V{/9>T~ ?G7[7=]8/͇s6v~wLIj~X]=p {~OAspޕbUA:x\ʍߥ<￟~-{CCriӵWS}(N5?u羿"/Yw՟c&~ў|w=u~g/~?YRݼGqU{xiwSNGLwWY8oBoWgTt.ݽ;F﮻7]$r̶*NB{y׭=_lKw;<*^`9SثnOt\}G? xהW߯nwqҨqGjwc<4PΪ_tz>7nҏx5Ο{furcy}|lzp_~w߸Ӹv|ިF>h%N7Gq G{q,}t%<~\Wq+,ƣtQ!wp|.BH?]2byHp}%Wnh}޸nd\ǸOrĉy-u?|Oq'OgXN{wp\sޫu߽n<=?7>@CS߱~O{LsykN4A[T7/?uُ?0//5_*_]Z7Se/՜K7λ8߬WI u /$PN5. ul7}_r2ogr著1+;4ζ׭_v=l簿ݷ\9Xn3Ot~2~g6~Ul:__HSf㈴S{]'9U?杴Ϲ8øjռua|۷tzrb>psŤ[_;;zr'Sw:te}}=#u'j~3N{94d߈Aӛ߻Ӟz:9_|?8=P\Z~ ܷg /~5Ͼ`zG]72|q+7θO8J9jRp}u~cr<}~"G{_S\VzR3|GUvH(Ἃ=,ܻ> Y>g]纏e?$opl3(Q]17SKf֏gIOoP1kwU|8.^>~]fv׬;=]o/?q+پx#w}vrݺywݷo|UՍϊU\73fdy |x4E$LtgO~5/i^ܯ$:?ekt/y:s3'eGu\߹;yQ~]Z\>:w{*ߝ&z?ݾ|+w~Ǧ8;KVoz//:l},?ڕk7Y+qu{N=II]g}YOkw]|?rHoe~gϾy{}Λuӝ=Yg\ ;w}I;g܏ o79u7% ډSPGUei/WelϬ]<.^~{_v~_WnouѮ=1vz6Kg'o7Wq<_w]&癜Oow8ُ[K?w/NWWvUzõη^༛^ ҭ >Vy[ r}}y+GՏCd^Z.{O#.ᬽ+Dl<'v*'k9v,O{~'藴k~5XfAWH_9Ϙnb?Vbo܍eϾ]~Y7o[ qǎwcg%7_\_Skڇxmқ]W ]Ǡݮ{*XT!7>:&kcحUe|}?wTB]9޹pY?=xU]Wkw?'xˬ_xN\`yg0Una;+܍O{(7V/c#fώ;"'n~ivOdWB;ϰSRO>tn=9;w`sfz_|U܄@:=yz?h]6}lOWv#e/kۗK+Qv9qvVAOUy/ ?_Zsrsqe\|MȼgRձ'uM9>;UܸO{>΋G:l1y['M+Ɓ;=+i/޿:zg+7{62[n7.>ׯIWWqNwKgv걢ݭO|ѿ]w?,n]}-}v;NceWuS7cjvb'_[}7]m=Wm=*A]v7yMz8g6>7G~sze^\B+RoPOvQ_vJo!5t׵^պ/x$]QMbzTq;[U|w:~༕KpgyhY?Ӎ'zpgx^wI#߯WqGO0:t8*}q}o߸N~*{];{+]nڥcG77O":ivcy+^5{.q~qy,~01Uete91T>+"۷ s ;= e:EY?*Mzc~jߜc?WqK_ w]=Ӿpٯ׍;՟WWj{6~qzrT~gv?s˵Οr h g\?38 ~R]v|UdgSwag21=Yo߉7B]Ǖ;sQG>Gg/˯]n~&|Ɵ+=;uΝq\~E\|Kz^vr/zqT=~Qjc<׻?8w{2o:mߕx~t/~*%\ 6城byux3nە~rc7tTǬ߳ߧ}Un]}?f;mgJ'W=+^N|+¾rv8n_U眝F+Տ MM}~g}}~_\"yq~־/'[yc=T7^\q }f7ag|oZ/$~_vb|v'Ji?w?O(??G~t)z侔F)8M^=./lWC9wyūN7w/ϣH?^emVōi?ߑ֏<<4rֹ{5wE{{EY;_kϵ{o+lk'^]v8u=Vһ> 7miUn_*uy}C/џp?*qwZhF6xۗ\c9lUŭu`.t_߉~΄Ύ;yw<.>O;d'ͯS;ݴVEx-eާוh!ߧ~9d9]?r>~\>ry/B==I>Cۉ8KsލǰSԺ^.YSyv7|c'jWGwgءV.Ņl}򡜽͓0&]PQEK;v~q}WawmFΫw׬רm>o~Wԯ37kpuv٭ym9?=}g':<o!hHq~']=n^W}+v=c7n=.?o<.~ 97!i}u֮nƓ~O;W : aC9]yY.7fB_W+/.8G(q#חn1۾?=+|avw('{SOW!u])߁)~о|?h~寇^^}{YjYX/6y|χ|Ǜ| ߫῏?h] ] ] ] ] E}66666666YV,A je5ȲdY YV,A je5ȲdYmd9rh#F,GY6md9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9r#,7r#,7r A,Yd?r A,YdFYldyFYldy ˃A,Y <dy0` ˃A,Y <dy0`#Í,7˒|\%.Kq .Wq+.Wq]F[J>nI>..Wq]F[J>HqmdI>n#KqYȒ|F6$%,%7ʕ|ܒ| WqKq r%'K>n+%\I\u\%wq,uY$eI>˒|\%.Kq]㒏J>nI>n+%A㒏ȕ|\+kr%$7ȕ|\+%㒏dI>˒|\%.Kq],uYAq]\ur%'K>n+%A\I6r%'K>n+%,uYȒ|F6$%,mdI>^?|\;H>˕|ܒ| WqKqur%$7ʕ|ܒ|F\I\u[K>.A,uY$eI>˒|\%%\%7ʕ|ܒ| WqM\u\%7ʕ|ܒ| WqM㒏K>.,uY$eI>˒|\%~|\+%\%7ʕ|ܒ| WqM$Wq\%7ʕ|ܒ|\q],mdI>n#KqYȒ|F6$wO>$J>nI>n+%\u[J>nI>n#WqM$Wq]\-%| Kq],uY$eI>˒|ܒ|\q\I㒏J>nI>n+&Wq]\I㒏J>nI>n+&Wq%|\q],uY$eI>˒|\?H>˕|ܒ|\q\I㒏J>nI>n+&Wq+\I㒏J>nI>..Kq]6$%,mdI>n#KqY'r%$7ȕ|ܒ|\q]\-Ǎr%$+&Wq+.Wq]㒏K>n%.Kq],uY$eI>nI>.A$WqǍr%$7ȕ|\+.Wq]$WqǍr%$7ȕ|\+㒏K>..Kq],uY$eI>$J>nI>.A$WqǍr%$7ȕ|\+8ɕ|F$WqǍr%$|\%.KqYȒ|F6$%,瓏ku[J>nI>..Wq]F[ȕ|\+8ɕ|\+.WqKq%7Ȓ|\%.Kq],uY$$| Wq+F[J>ɕ|\+.Wq+F[J>ɕ|\q%|\%.Kq],uY$r%$| Wq+F[J>ɕ|J>n#Wq+F[K>˒|\%,mdI>n#KqYȒ|Fǵ\- r%$|\+.WqKq\-mJ>ɕ|J>˕|\+%㒏dI>˒|\%.Kq],uY[K>n+8ɕ|\q\- r%J>˕|\+8ɕ|\q\- r%J>.㒏K>˒|\%.Kq],uYu[K>n+8ɕ|\q\- r%J>Nr%+8ɕ|\q\-%eI>˒|F6$%,mdI>n#KqAq][K>˕|\+%Q6r%J>Nr%J>˕|ܒ|\q $eI>˒|\%.Kq],-%7ȕ|J>.Qkr%J>˕|J>.Qkr%|\q%eI>˒|\%.Kq],\-%7ȕ|J>.Qkr%'ȕ|J>.Q㒏$eI>n#KqYȒ|F6$%{|q .WqKq\-%J>˕|ܒ|(WqKqkr%'r%J>nI>.Y$eI>˒|\%.Kq]㒏J>Nr%|(WqKq\5r%J>Nr%|(WqKq\5K>.㒏$eI>˒|\%.Kq]Aq]㒏J>Nr%|(WqKq\5\mJ>Nr%|(WqKquY$%,mdI>n#KqYȒ|ܽ~>v|\+%A㒏r%J>nI>n+%\5\ur%$|\q,uY$eI>˒|\%.KqKq r%'K>n+%A\ur%'K>n+%A\%|\quY$eI>˒|\%.Kq .WqKq r%'K>n+%A\I6r%'K>n+%,uYȒ|F6$%,mdI>^?|\;H>˕|ܒ| WqKqur%$7ʕ|ܒ|F\I\u[K>.A,uY$eI>˒|\%%\%7ʕ|ܒ| WqM\u\%7ʕ|ܒ| WqM㒏K>.,uY$eI>˒|\%~|\+%\%7ʕ|ܒ| WqM$Wq\%7ʕ|ܒ|\q],mdI>n#KqYȒ|F6$wO>$J>nI>n+%\u[J>nI>n#WqM$Wq]\-%| Kq],uY$eI>˒|ܒ|\q\I㒏J>nI>n+&Wq]\I㒏J>nI>n+&Wq%|\q],uY$eI>˒|\?H>˕|ܒ|\q\I㒏J>nI>n+&Wq+\I㒏J>nI>..Kq]6$%,mdI>n#KqY'r%$7ȕ|ܒ|\q]\-Ǎr%$+&Wq+.Wq]㒏K>n%.Kq],uY$eI>nI>.A$WqǍr%$7ȕ|\+.Wq]$WqǍr%$7ȕ|\+㒏K>..Kq],uY$eI>$J>nI>.A$WqǍr%$7ȕ|\+8ɕ|F$WqǍr%$|\%.KqYȒ|F6$%,瓏ku[J>nI>..Wq]F[ȕ|\+8ɕ|\+.WqKq%7Ȓ|\%.Kq],uY$$| Wq+F[J>ɕ|\+.Wq+F[J>ɕ|\q%|\%.Kq],uY$r%$| Wq+F[J>ɕ|J>n#Wq+F[K>˒|\%,mdI>n#KqYȒ|Fǵ\- r%$|\+.WqKq\-mJ>ɕ|J>˕|\+%㒏dI>˒|\%.Kq],uY[K>n+8ɕ|\q\- r%J>˕|\+8ɕ|\q\- r%J>.㒏K>˒|\%.Kq],uYu[K>n+8ɕ|\q\- r%J>Nr%+8ɕ|\q\-%eI>˒|F6$%,mdI>n#KqAq][K>˕|\+%Q6r%J>Nr%J>˕|ܒ|\q $eI>˒|\%.Kq],-%7ȕ|J>.Qkr%J>˕|J>.Qkr%|\q%eI>˒|\%.Kq],\-%7ȕ|J>.Qkr%'ȕ|J>.Q㒏$eI>n#KqYȒ|F6$%{|q .WqKq\-%J>˕|ܒ|(WqKqkr%'r%J>nI>.Y$eI>˒|\%.Kq]㒏J>Nr%|(WqKq\5r%J>Nr%|(WqKq\5K>.㒏$eI>˒|\%.Kq]Aq]㒏J>Nr%|(WqKq\5\mJ>Nr%|(WqKquY$%,mdI>n#KqYȒ|ܽ~>v|\+%A㒏r%J>nI>n+%\5\ur%$|\q,uY$eI>˒|\%.KqKq r%'K>n+%A\ur%'K>n+%A\%|\quY$eI>˒|\%.Kq .WqKq r%'K>n+%A\I6r%'K>n+%,uYȒ|F6$%,mdI>^?|\;H>˕|ܒ| WqKqur%$7ʕ|ܒ|F\I\u[K>.A,uY$eI>˒|\%%\%7ʕ|ܒ| WqM\u\%7ʕ|ܒ| WqM㒏K>.,uY$eI>˒|\%~|\+%\%7ʕ|ܒ| WqM$Wq\%7ʕ|ܒ|\q],mdI>n#KqYȒ|F6$wO>$J>nI>n+%\u[J>nI>n#WqM$Wq]\-%| Kq],uY$eI>˒|ܒ|\q\I㒏J>nI>n+&Wq]\I㒏J>nI>n+&Wq%|\q],uY$eI>˒|\?H>˕|ܒ|\q\I㒏J>nI>n+&Wq+\I㒏J>nI>..Kq]6$%,mdI>n#KqY'r%$7ȕ|ܒ|\q]\-Ǎr%$+&Wq+.Wq]㒏K>n%.Kq],uY$eI>nI>.A$WqǍr%$7ȕ|\+.Wq]$WqǍr%$7ȕ|\+㒏K>..Kq],uY$eI>$J>nI>.A$WqǍr%$7ȕ|\+8ɕ|F$WqǍr%$|\%.KqYȒ|F6$%,瓏ku[J>nI>..Wq]F[ȕ|\+8ɕ|\+.WqKq%7Ȓ|\%.Kq],uY$$| Wq+F[J>ɕ|\+.Wq+F[J>ɕ|\q%|\%.Kq],uY$r%$| Wq+F[J>ɕ|J>n#Wq+F[K>˒|\%,mdI>n#KqYȒ|Fǵ\- r%$|\+.WqKq\-mJ>ɕ|J>˕|\+%㒏dI>˒|\%.Kq],uY[K>n+8ɕ|\q\- r%J>˕|\+8ɕ|\q\- r%J>.㒏K>˒|\%.Kq],uYu[K>n+8ɕ|\q\- r%J>Nr%+8ɕ|\q\-%eI>˒|F6$%,mdI>n#KqAq][K>˕|\+%Q6r%J>Nr%J>˕|ܒ|\q $eI>˒|\%.Kq],-%7ȕ|J>.Qkr%J>˕|J>.Qkr%|\q%eI>˒|\%.Kq],\-%7ȕ|J>.Qkr%'ȕ|J>.Q㒏$eI>n#KqYȒ|F6$%{|q .WqKq\-%J>˕|ܒ|(WqKqkr%'r%J>nqe%////aaaa݋0 0 0 0 6aaaajKaaaax?aaaam, 0 0 0 0.0 0 0 0 QaaaaxaaaamKaaaax< 0 0 0 0_aaaaaaaamO0 0 0 0 " 0 0 0 0Qaaaaxyaaaam70 0 0 0 ٷ0 0 0 0 0 0 0 0 V0 0 0 0 o3ÿ0 0 0 0 o3< 0 0 0 0_aaaam: 0 0 0 0aaaaf0 0 0 0 6aaaaaaaafo/0 0 0 0 ݇aaaamaaaam0 0 0 0 0 0 0 0 [aaaa?0 0 0 0 V* 0 0 0 0? 0 0 0 0 o5? 0 0 0 0 o3Kaaaaxaaaa0 0 0 0 o30 0 0 0 V& 0 0 0 0?qaaaax0 0 0 0 Aaaaax_aaaa^aaaaxaaaaaaaaj?0 0 0 0 OaaaaYaaaax0 0 0 0 VaaaaoaaaaQaaaaxyu' 0 0 0 0( 0 0 0 0aaaaj~aaaaj" 0 0 0 0iaaaax 0 0 0 0 o3? 0 0 0 0 o5 0 0 0 0 o3? 0 0 0 0 o5Iaaaax0 0 0 0 o5aaaa-/eAaaaax;% 0 0 0 0|3 0 0 0 0vaaaaxy7 0 0 0 00 0 0 0 V 0 0 0 00 0 0 0 V0 0 0 0 [ 0 0 0 0 o5aaaajnaaaaxaaaaf~o 0 0 0 0 o5 0 0 0 0 o5aaaaj 0 0 0 0 o53 0 0 0 0<0 0 0 0 o5+ 0 0 0 00 0 0 0 Vw0 0 0 0 [aaaaaaaaf_aaaa[0 0 0 0 [70 0 0 0 [oaaaaw0 0 0 0 [ 0 0 0 0 o50 0 0 0 [ 0 0 0 0 o5; 0 0 0 00 0 0 0 V;aaaaaaaaaaaam0 0 0 0 V0 0 0 0 Vaaaa0 0 0 0 V70 0 0 0 [̓0 0 0 0 V0 0 0 0 [0 0 0 0 o5' 0 0 0 0Naaaaxaaaamuaaaax% 0 0 0 0|3 0 0 0 0vaaaaxy7 0 0 0 00 0 0 0 V 0 0 0 00 0 0 0 V0 0 0 0 [ 0 0 0 0 o5aaaajnaaaaxaaaaf~o 0 0 0 0 o5 0 0 0 0 o5aaaaj 0 0 0 0 o53 0 0 0 0<0 0 0 0 o5+ 0 0 0 00 0 0 0 Vw0 0 0 0 [aaaaaaaafZZW0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0sYW~ɿO>|i'˳?|{oCo?97I;sO:?'|oE?g_[go_/W%o^s/ſT [,{G~y[͏KLGGally/data/pigs.rda0000644000176200001440000000243113663637143014010 0ustar liggesusersBZh91AY&SYtdwj4#L  Rh M#hɣIyLd4i& C#A=@Ph=@C5  4  @4 a@4$ASODzh4zl2zh1@z P6 di <iC/37;?CGKOSW[_cgkosw{ǥH zIY(Y=, ;DͦaO.or7g.aϟ V7='~Y_ս!ȺG;PJJ~ BZ|`ɲ#o4d} }fH,| ,lxH a(q/ f?R rmW{RCw\ =").tPE$6a:,Y $m=eSQ, >oK:R]vٜίcCo:+.6t$\W4&dHg-41%jt8Xң~"/Q ޠe2g$G M0 .%$Qa)?kjac5 @+RyV(asO :Ay@PdLqUKa'A+i)#Ka2~Am\sJ3Njvsj[9EyRx 9;ZLQ3RX ,L&ڈa6n۩79{HK!^Y͂$zhY\!O)+xv;] -5I]Ә/HUPS/yJz t|K!S&1n9,b3̎ *TG>D8xo?]BB? lGGally/data/flea.rda0000644000176200001440000000150513663637143013756 0ustar liggesusersŖMOSQ- |% cK "q,P r-Pr|* ~EK,Z&qctn;=2ZygΜ{o{zޠ1g55[O5ēc;:FT֤;&Z6:+ҩX"rM ^.qLBe-wboػc<K< <O*JM+6ȃp̂90f ]ͱ7X_j?gVx׾X[T%}]d_(m,{]Ipxcenq5?G}NkV/yC9Ig\Y=I3cA=SXvv(h0m*9m$±{T|'mVU;U0YYCC`kTo ^vD-S~S>+,-Q[43#7s#gR=pį 27M}5"Z`|9' }.=(#mK1y@:Ѽ2`A3!fۃj5E~ Np G>'8q '8 8Q bNpHNphNpxNp\ /q˜ ;'85ถS8q'8rc:'8nG '8fpc'8J91e rNpMX EX %X e.Np9Q '88rNpG5'8j8Q '8p# Np4r8Np͜X y_ ! |Np pNp9aG'8 9191c918q5'8 iก9q#'8J813919Q X epr JNpx8G'8VrNp4sc!AM̿,$ e~~i Kp9Va$+&۠=e-d\UMcsHh*p@͙k)hp4yVQ`\V!1ٚd9Ҕd$}=Nk ULۢYM e~**8s`g g,4_("ٓ-(PI0(`*nрJJ!,?+_Eoؒ3"9ǻ]@K@?w@Nwc? <s|Y[yRRڑТ"'PbQO>i3lHX/y|I# ۦ΂٥!X35QPb#Ty@xc~ |ЛyjT;{ȯa9 (HIȒ}%%JA$Z_x9𶮢u: 73V➆(E!- ]$:x{}%Bo7KoPuRPRH+Tx8,+Kխ&4 Z9 a!85WJ|Y j!P)8mP{- &J>q}p{F"yHCPVDmՂԚPe^[Mms GD7x'ZeI ! DMJ8w-żS`" $ ÕX1݃叜 Br P gHaEnQKxvM t(| óH^jJ'! 6rK@(!8eY-gE6K-#(J{C{_M`vZ.0aw" Prq( aFh"6Н❅a*k +AC#I{SG;v`gˢtmWN +8;(X=@PZFC侱gӈ MJl'"nH@G)X**MbJ P  c"q}q_#ԯ5y˵sw4t; 6/\xr9CF?EXfZ?yF ARs(^x]IHܢ< dH5j|eDY^;"wS䜕V MxV}T.#V7m;H5FrXPu $0SDVMqtN$:iTKv6[.d~=DAHAjE%-="*/|gfމ<>3ܠɉbJ竏G?|_xGw[A_]9  5GF_݃{n2a7*ā "3G;rEQdArnxNBBLm )<ڒ=+ $ !2 % u^D>zvHե@u@2Sf97FDuhTUd{jÉ;.80lXUgZ"uCW?<8o f"͸.ijcGQPl& -9,?W?>{q)BHٱw@NB$1 ͵z]#_'4ȎyF OqZb[mkcFZ1L:9) g]?D9ӇSP4ߥҹl͸ peEs(Y|}_&b0p)Z-P('بϡTiR/fueޙkT!(4F˴bqqѬ7_j 6so ,IdHM )x#N}:1#/` R5H&^D#Hv? ,o=U[s!z%V73x'9*}PUlN<~"7"NomWJHhܻjRKTw{ۊy'\%hPΉ~& E[?᣺ѷLNfPϽUAc: _9AQKxH?֩ 7.Ly{*}A#΅LA2guLCrt%>=xtO` O|kDgBM΅G$ʫMh^:g~33PP̌V)FdD{rUar#;,xA$)0:cW@ÎҪM Cb:}rL~9xG|?ݯ.c+21J@{@\ά!jXIPRͦ/:];VAò[P.Q|@ ^15}.j790㭧w ahurji˯,ʦ[5qw-(^H(P('PBzy**(g-'IyB_/v A5Z#)FVH J+DV5{&<F1w?hu@zso.դp{C{ gw .`-sHpqL_hW:|WQ18HJp.[0ډ(%sltS[ ^IX2dl$c s*X*,B-^boj$w}R%X?yE+?|&(dAur f뷨\w˯yLkwPpD2He Q(_Ccgֵ,uDo:wrH!Ak ڰod QOɂDWfY;^+r %r188#vuz `h筛j6 ф2pvϐ-a;r"ٹ֑NEc O(9DpfxgP;vj4K1@jBH(RKRKvмYg=H;$vI /YKS =@#IG n=zv]&B^k7O9Hx,p i:NZ+K*b%lRd}t(5eB)-(g (tcIsի٤`Wu+`6Q;yg\ fcv/PB2ރw% %5k&howL{$N{F+n7OsvL&̫:^51P:O=2+aOSwa%~ڽj[sB04zLƧOlRQLGPT{Ev_d?'p2X>jOړsd2>]| %uT{=9;\d7QT~zbj'JRmj/pTW;%sPm'vQm7)UQmF)/^~mE_G?a}#b1c_gDx:0<Qj>"rTJwJ,M R{.6a"M̸NstI:a--IbC)!!;-L'yLPr s+VsۦR6 6iDl"=[ gc#E*>hwȄ [NKytf!C34XrUZVCNLwmG ) ^A(eg4 ZN啱qRZ]duFjNa*7Ԋ]kN-UJZ#S6Dt Q`lv߅8rqᡥ>I]7F,cj{0WH`'K-i Euy 9_~f3%,[=qt΂|7DT6]G:F\Z1&E#=|'5uN)YJ/vSge0%,lmK+wŲXjQ虆]9)KΑX׬M]lix[6,_H^[SibZ 5wKaR5R.ikSlk2K ,'8SvݷG9"_Y>ӫ.lɘD 5uwu]A5H)ķWro1iuv/[QX/T6B*/X!^$C[K.[b,E승3])C%ۨa.Mj7JfR]qI9pnDHxauػFpBzp猖TYkPZwmz*^5r*ۚiYɭcAhO2خ|WCxl#6)S " AmcˢzYE]ik*#v&NWm,* m'&!b#1q,ԤOdug8I!ZnJgٞ3kh)'&3ܦ]6%%v0HVܼ:Y-AC -wl˛ڷ^yA|MHmD^e[kE*&AyHI-0bZBK<=aŝ#<C,iz. +SʍuÛyM4Q"dZ0 \SLɚ((UڶI-b{;0LPq )Lj[[[vmd[;:ư:Mea63˞$6H/dغўyyNL䴫aGlY3[ILcY]*ZȫQdvl[V۱8HaJ=PJ++QNrm^^5HE2 %qKl 8f=5 PuCl'%F  Hg= J[zj' v4'*MU ѥI~1:\]=bܢ  ( Z:MYUe(/ŷSҚ0fZE;w .a& -5Fԃ 0(HhM܂1 0X2wb>B4$<#)}n_t|txg@:&C r jP9m4S19ꀖ(Pq!.~kLy5ۼ^FЏ04,j454LCDMWvXE\` *"h.|ZW%ZGVdC&dG%Ez1(^['4\.ؿ%G9y]S]6t֢jqz2-Dav7ݐ|C !< [x3hPP5)AAL*B J2dL16l DE30لI 3 dh(!!3) e00 R: upՓ'Q.urhoNJ]jG;/r؎Wfy ǖ-Sێdw95yP{Ny)L&lGtg[h_r6AI[FVѱc!&ت -PmlkERV4emG^*HȠw'x]:JjL03f% v^%9:6͔~!MW ܤXe),\EvE)t+ݝ03ACuژ!3D 7}1lL ilDZ$`2 G^ދ&9U1 DPν0xMBL?.YKJhZihUn+BB$ }aKbP0ie4ĜtAVo>|x>Wɩ>UamIwqEp·K&YU*KO)l|%fīwkEV +Ga5>j=#^6N~rgQml&qMnPE_ceѾ2kT5UTL.ndb)ic~;(и"'y DHM<q=&zqיNqJF/i1aDduF ncwl^+$z=G#xu9b\j+BWh*ʲ[]tso/|j\U[W>ƞަ)^wQvx^ˊB0RYyn]BrLtXcN[jAUmLpKW{.P_Av_dS /uV!!*8[ ¤2ЂTq ܺ䔂OHĊ"#"D"(ԚADlEV*%6 Ed(QDj"0A`mS#FKfXy[jոCJK1((,M79\q@jѫ/m"QLZe!&PmW .]ky#mzr-i4")f 0XFs^E6 N_Taڅ]|kIvJV92 \VU b[De+Ƃ,F gצn  {$6P30b,hzmF  8IʇjKh0q `kh#ZIƫYVh،HE OmքϽwaykp<",T E I0`05U r@x!<3l; ȀF1u{w$S P)GGally/man/0000755000176200001440000000000014063647151012213 5ustar liggesusersGGally/man/ggally_facetdensitystrip.Rd0000644000176200001440000000133013665760216017607 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_facetdensitystrip} \alias{ggally_facetdensitystrip} \title{Density or tiles plot with facets} \usage{ ggally_facetdensitystrip(data, mapping, ..., den_strip = FALSE) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{other arguments being sent to either geom_histogram or stat_density} \item{den_strip}{boolean to decide whether or not to plot a density strip(TRUE) or a facet density(FALSE) plot.} } \description{ Make tile plot or density plot as compact as possible. } \examples{ example(ggally_facetdensity) example(ggally_denstrip) } \author{ Barret Schloerke } \keyword{hplot} GGally/man/broomify.Rd0000644000176200001440000000167313764714663014351 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnostic.R \name{broomify} \alias{broomify} \title{Broomify a model} \usage{ broomify(model, lmStars = TRUE) } \arguments{ \item{model}{model to be sent to \code{\link[broom:reexports]{broom::augment()}}, \code{\link[broom:reexports]{broom::glance()}}, and \code{\link[broom:reexports]{broom::tidy()}}} \item{lmStars}{boolean that determines if stars are added to labels} } \value{ broom::augmented data frame with the broom::glance data.frame and broom::tidy data.frame as 'broom_glance' and 'broom_tidy' attributes respectively. \code{var_x} and \code{var_y} variables are also added as attributes } \description{ broom::augment a model and add broom::glance and broom::tidy output as attributes. X and Y variables are also added. } \examples{ data(mtcars) model <- stats::lm(mpg ~ wt + qsec + am, data = mtcars) broomified_model <- broomify(model) str(broomified_model) } GGally/man/ggally_densityDiag.Rd0000644000176200001440000000156213666472400016312 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_densityDiag} \alias{ggally_densityDiag} \title{Univariate density plot} \usage{ ggally_densityDiag(data, mapping, ..., rescale = FALSE) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used.} \item{...}{other arguments sent to stat_density} \item{rescale}{boolean to decide whether or not to rescale the count output} } \description{ Displays a density plot for the diagonal of a \code{\link{ggpairs}} plot matrix. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_densityDiag(tips, mapping = ggplot2::aes(x = total_bill))) p_(ggally_densityDiag(tips, mapping = ggplot2::aes(x = total_bill, color = day))) } \author{ Barret Schloerke } \keyword{hplot} GGally/man/ggally_barDiag.Rd0000644000176200001440000000153613666472400015400 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_barDiag} \alias{ggally_barDiag} \title{Bar plot} \usage{ ggally_barDiag(data, mapping, ..., rescale = FALSE) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{other arguments are sent to geom_bar} \item{rescale}{boolean to decide whether or not to rescale the count output. Only applies to numeric data} } \description{ Displays a bar plot for the diagonal of a \code{\link{ggpairs}} plot matrix. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_barDiag(tips, mapping = ggplot2::aes(x = day))) p_(ggally_barDiag(tips, mapping = ggplot2::aes(x = tip), binwidth = 0.25)) } \author{ Barret Schloerke } \keyword{hplot} GGally/man/ggally_dot.Rd0000644000176200001440000000216713666472400014636 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_dot} \alias{ggally_dot} \alias{ggally_dot_no_facet} \title{Grouped dot plot} \usage{ ggally_dot(data, mapping, ...) ggally_dot_no_facet(data, mapping, ...) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{other arguments being supplied to geom_jitter} } \description{ Add jittering with the box plot. \code{ggally_dot_no_facet} will be a single panel plot, while \code{ggally_dot} will be a faceted plot } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_dot(tips, mapping = ggplot2::aes(x = total_bill, y = sex))) p_(ggally_dot(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "sex"))) p_(ggally_dot( tips, mapping = ggplot2::aes_string(y = "total_bill", x = "sex", color = "sex") )) p_(ggally_dot( tips, mapping = ggplot2::aes_string(y = "total_bill", x = "sex", color = "sex", shape = "sex") ) + ggplot2::scale_shape(solid=FALSE)) } \author{ Barret Schloerke } \keyword{hplot} GGally/man/ggally_smooth.Rd0000644000176200001440000000254313666472400015357 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_smooth} \alias{ggally_smooth} \alias{ggally_smooth_loess} \alias{ggally_smooth_lm} \title{Scatter plot with a smoothed line} \usage{ ggally_smooth( data, mapping, ..., method = "lm", formula = y ~ x, se = TRUE, shrink = TRUE ) ggally_smooth_loess(data, mapping, ...) ggally_smooth_lm(data, mapping, ...) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{method, se}{parameters supplied to \code{\link[ggplot2]{geom_smooth}}} \item{formula, ...}{other arguments to add to geom_smooth} \item{shrink}{boolean to determine if y range is reduced to range of points or points and error ribbon} } \description{ Add a smoothed condition mean with a given scatter plot. } \details{ Y limits are reduced to match original Y range with the goal of keeping the Y axis the same across plots. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_smooth(tips, mapping = ggplot2::aes(x = total_bill, y = tip))) p_(ggally_smooth(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip"))) p_(ggally_smooth(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip", color = "sex"))) } \author{ Barret Schloerke } \keyword{hplot} GGally/man/australia_PISA2012.Rd0000644000176200001440000000474714063462402015616 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/data-australia-pisa-2012.R \docType{data} \name{australia_PISA2012} \alias{australia_PISA2012} \title{Programme for International Student Assessment (PISA) 2012 Data for Australia} \format{ A data frame with 8247 rows and 32 variables } \source{ \url{https://www.oecd.org/pisa/pisaproducts/database-cbapisa2012.htm} } \usage{ data(australia_PISA2012) } \description{ About PISA } \details{ The Programme for International Student Assessment (PISA) is a triennial international survey which aims to evaluate education systems worldwide by testing the skills and knowledge of 15-year-old students. To date, students representing more than 70 economies have participated in the assessment. While 65 economies took part in the 2012 study, this data set only contains information from the country of Australia. \itemize{ \item gender : Factor w/ 2 levels "female","male": 1 1 2 2 2 1 1 1 2 1 ... \item age : Factor w/ 4 levels "4","5","6","7": 2 2 2 4 3 1 2 2 2 2 ... \item homework : num 5 5 9 3 2 3 4 3 5 1 ... \item desk : num 1 0 1 1 1 1 1 1 1 1 ... \item room : num 1 1 1 1 1 1 1 1 1 1 ... \item study : num 1 1 1 1 1 1 1 1 1 1 ... \item computer : num 1 1 1 1 1 1 1 1 1 1 ... \item software : num 1 1 1 1 1 1 1 1 1 1 ... \item internet : num 1 1 1 1 1 1 1 1 1 1 ... \item literature : num 0 0 1 0 1 1 1 1 1 0 ... \item poetry : num 0 0 1 0 1 1 0 1 1 1 ... \item art : num 1 0 1 0 1 1 0 1 1 1 ... \item textbook : num 1 1 1 1 1 0 1 1 1 1 ... \item dictionary : num 1 1 1 1 1 1 1 1 1 1 ... \item dishwasher : num 1 1 1 1 0 1 1 1 1 1 ... \item PV1MATH : num 562 565 602 520 613 ... \item PV2MATH : num 569 557 594 507 567 ... \item PV3MATH : num 555 553 552 501 585 ... \item PV4MATH : num 579 538 526 521 596 ... \item PV5MATH : num 548 573 619 547 603 ... \item PV1READ : num 582 617 650 554 605 ... \item PV2READ : num 571 572 608 560 557 ... \item PV3READ : num 602 560 594 517 627 ... \item PV4READ : num 572 564 575 564 597 ... \item PV5READ : num 585 565 620 572 598 ... \item PV1SCIE : num 583 627 668 574 639 ... \item PV2SCIE : num 579 600 665 612 635 ... \item PV3SCIE : num 593 574 620 571 666 ... \item PV4SCIE : num 567 582 592 598 700 ... \item PV5SCIE : num 587 625 656 662 670 ... \item SENWGT_STU : num 0.133 0.133 0.141 0.141 0.141 ... \item possessions: num 10 8 12 9 11 11 10 12 12 11 ... } } \keyword{datasets} GGally/man/is_date.Rd0000644000176200001440000000041513663637143014117 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/find-combo.R \name{is_date} \alias{is_date} \title{Check if object is a date} \usage{ is_date(x) } \arguments{ \item{x}{vector} } \description{ Check if object is a date } \keyword{internal} GGally/man/ggally_trends.Rd0000644000176200001440000000355513764714663015362 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stat_weighted_mean.R \name{ggally_trends} \alias{ggally_trends} \title{Trends line plot} \usage{ ggally_trends(data, mapping, ..., include_zero = FALSE) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{other arguments passed to \code{\link[ggplot2:geom_path]{ggplot2::geom_line()}}} \item{include_zero}{Should 0 be included on the y-axis?} } \description{ Plot trends using line plots. For continuous y variables, plot the evolution of the mean. For binary y variables, plot the evolution of the proportion. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") tips_f <- tips tips_f$day <- factor(tips$day, c("Thur", "Fri", "Sat", "Sun")) # Numeric variable p_(ggally_trends(tips_f, mapping = aes(x = day, y = total_bill))) p_(ggally_trends(tips_f, mapping = aes(x = day, y = total_bill, colour = time))) # Binary variable p_(ggally_trends(tips_f, mapping = aes(x = day, y = smoker))) p_(ggally_trends(tips_f, mapping = aes(x = day, y = smoker, colour = sex))) # Discrete variable with 3 or more categories p_(ggally_trends(tips_f, mapping = aes(x = smoker, y = day))) p_(ggally_trends(tips_f, mapping = aes(x = smoker, y = day, color = sex))) # Include zero on Y axis p_(ggally_trends(tips_f, mapping = aes(x = day, y = total_bill), include_zero = TRUE)) p_(ggally_trends(tips_f, mapping = aes(x = day, y = smoker), include_zero = TRUE)) # Change line size p_(ggally_trends(tips_f, mapping = aes(x = day, y = smoker, colour = sex), size = 3)) # Define weights with the appropriate aesthetic d <- as.data.frame(Titanic) p_(ggally_trends( d, mapping = aes(x = Class, y = Survived, weight = Freq, color = Sex), include_zero = TRUE )) } \author{ Joseph Larmarange } \keyword{hplot} GGally/man/psychademic.Rd0000644000176200001440000000172213761572054015000 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/data-psychademic.R \docType{data} \name{psychademic} \alias{psychademic} \title{UCLA canonical correlation analysis data} \format{ A data frame with 600 rows and 8 variables } \usage{ data(psychademic) } \description{ This data contains 600 observations on eight variables } \details{ \itemize{ \item locus_of_control - psychological \item self_concept - psychological \item motivation - psychological. Converted to four character groups \item read - academic \item write - academic \item math - academic \item science - academic \item female - academic. Dropped from original source \item sex - academic. Added as a character version of female column } } \references{ R Data Analysis Examples | Canonical Correlation Analysis. UCLA: Institute for Digital Research and Education. from http://www.stats.idre.ucla.edu/r/dae/canonical-correlation-analysis (accessed May 22, 2017). } \keyword{datasets} GGally/man/GGally-package.Rd0000644000176200001440000000303613777103031015247 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/GGally-package.R \docType{package} \name{GGally-package} \alias{GGally} \alias{GGally-package} \title{GGally: Extension to 'ggplot2'} \description{ The R package 'ggplot2' is a plotting system based on the grammar of graphics. 'GGally' extends 'ggplot2' by adding several functions to reduce the complexity of combining geometric objects with transformed data. Some of these functions include a pairwise plot matrix, a two group pairwise plot matrix, a parallel coordinates plot, a survival plot, and several functions to plot networks. } \seealso{ Useful links: \itemize{ \item \url{https://ggobi.github.io/ggally/} \item \url{https://github.com/ggobi/ggally} \item Report bugs at \url{https://github.com/ggobi/ggally/issues} } } \author{ \strong{Maintainer}: Barret Schloerke \email{schloerke@gmail.com} Authors: \itemize{ \item Di Cook \email{dicook@monash.edu} [thesis advisor] \item Joseph Larmarange \email{joseph@larmarange.net} \item Francois Briatte \email{f.briatte@gmail.com} \item Moritz Marbach \email{mmarbach@mail.uni-mannheim.de} \item Edwin Thoen \email{edwinthoen@gmail.com} \item Amos Elberg \email{amos.elberg@gmail.com} \item Jason Crowley \email{crowley.jason.s@gmail.com} } Other contributors: \itemize{ \item Ott Toomet \email{otoomet@gmail.com} [contributor] \item Heike Hofmann \email{hofmann@iastate.edu} [thesis advisor] \item Hadley Wickham \email{h.wickham@gmail.com} [thesis advisor] } } \keyword{internal} GGally/man/gglegend.Rd0000644000176200001440000000316313666472400014262 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggmatrix_legend.R \name{gglegend} \alias{gglegend} \title{Plot only legend of plot function} \usage{ gglegend(fn) } \arguments{ \item{fn}{this value is passed directly to an empty \code{\link{wrap}} call. Please see \code{?\link{wrap}} for more details.} } \value{ a function that when called with arguments will produce the legend of the plotting function supplied. } \description{ Plot only legend of plot function } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive # display regular plot p_(ggally_points(iris, ggplot2::aes(Sepal.Length, Sepal.Width, color = Species))) # Make a function that will only print the legend points_legend <- gglegend(ggally_points) p_(points_legend(iris, ggplot2::aes(Sepal.Length, Sepal.Width, color = Species))) # produce the sample legend plot, but supply a string that 'wrap' understands same_points_legend <- gglegend("points") identical( attr(attr(points_legend, "fn"), "original_fn"), attr(attr(same_points_legend, "fn"), "original_fn") ) # Complicated examples custom_legend <- wrap(gglegend("points"), size = 6) p_(custom_legend(iris, ggplot2::aes(Sepal.Length, Sepal.Width, color = Species))) # Use within ggpairs pm <- ggpairs( iris, 1:2, mapping = ggplot2::aes(color = Species), upper = list(continuous = gglegend("points")) ) p_(pm) # Place a legend in a specific location pm <- ggpairs(iris, 1:2, mapping = ggplot2::aes(color = Species)) # Make the legend pm[1,2] <- points_legend(iris, ggplot2::aes(Sepal.Width, Sepal.Length, color = Species)) p_(pm) } GGally/man/lowertriangle.Rd0000644000176200001440000000153213665760216015366 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggscatmat.R \name{lowertriangle} \alias{lowertriangle} \title{lowertriangle - rearrange dataset as the preparation of \code{\link{ggscatmat}} function} \usage{ lowertriangle(data, columns = 1:ncol(data), color = NULL) } \arguments{ \item{data}{a data matrix. Should contain numerical (continuous) data.} \item{columns}{an option to choose the column to be used in the raw dataset. Defaults to \code{1:ncol(data)}} \item{color}{an option to choose a factor variable to be grouped with. Defaults to \code{(NULL)}} } \description{ function for making the melted dataset used to plot the lowertriangle scatterplots. } \examples{ data(flea) head(lowertriangle(flea, columns= 2:4)) head(lowertriangle(flea)) head(lowertriangle(flea, color="species")) } \author{ Mengjia Ni, Di Cook } GGally/man/stat_weighted_mean.Rd0000644000176200001440000001203013761572054016334 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stat_weighted_mean.R \docType{data} \name{stat_weighted_mean} \alias{stat_weighted_mean} \alias{StatWeightedMean} \title{Compute weighted y mean} \usage{ stat_weighted_mean( mapping = NULL, data = NULL, geom = "point", position = "identity", ..., na.rm = FALSE, orientation = NA, show.legend = NA, inherit.aes = TRUE ) } \arguments{ \item{mapping}{Set of aesthetic mappings created by \code{\link[ggplot2:aes]{aes()}} or \code{\link[ggplot2:aes_]{aes_()}}. If specified and \code{inherit.aes = TRUE} (the default), it is combined with the default mapping at the top level of the plot. You must supply \code{mapping} if there is no plot mapping.} \item{data}{The data to be displayed in this layer. There are three options: If \code{NULL}, the default, the data is inherited from the plot data as specified in the call to \code{\link[ggplot2:ggplot]{ggplot()}}. A \code{data.frame}, or other object, will override the plot data. All objects will be fortified to produce a data frame. See \code{\link[ggplot2:fortify]{fortify()}} for which variables will be created. A \code{function} will be called with a single argument, the plot data. The return value must be a \code{data.frame}, and will be used as the layer data. A \code{function} can be created from a \code{formula} (e.g. \code{~ head(.x, 10)}).} \item{geom}{Use to override the default connection between \code{geom_histogram()}/\code{geom_freqpoly()} and \code{stat_bin()}.} \item{position}{Position adjustment, either as a string, or the result of a call to a position adjustment function.} \item{...}{Other arguments passed on to \code{\link[ggplot2:layer]{layer()}}. These are often aesthetics, used to set an aesthetic to a fixed value, like \code{colour = "red"} or \code{size = 3}. They may also be parameters to the paired geom/stat.} \item{na.rm}{If \code{FALSE}, the default, missing values are removed with a warning. If \code{TRUE}, missing values are silently removed.} \item{orientation}{The orientation of the layer. The default (\code{NA}) automatically determines the orientation from the aesthetic mapping. In the rare event that this fails it can be given explicitly by setting \code{orientation} to either \code{"x"} or \code{"y"}. See the \emph{Orientation} section for more detail.} \item{show.legend}{logical. Should this layer be included in the legends? \code{NA}, the default, includes if any aesthetics are mapped. \code{FALSE} never includes, and \code{TRUE} always includes. It can also be a named logical vector to finely select the aesthetics to display.} \item{inherit.aes}{If \code{FALSE}, overrides the default aesthetics, rather than combining with them. This is most useful for helper functions that define both data and aesthetics and shouldn't inherit behaviour from the default plot specification, e.g. \code{\link[ggplot2:borders]{borders()}}.} } \description{ This statistic will compute the mean of \strong{y} aesthetic for each unique value of \strong{x}, taking into account \strong{weight} aesthetic if provided. } \section{Computed variables}{ \describe{ \item{y}{weighted y (numerator / denominator)} \item{numerator}{numerator} \item{denominator}{denominator} } } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggplot(tips) + aes(x = day, y = total_bill) + geom_point()) p_(ggplot(tips) + aes(x = day, y = total_bill) + stat_weighted_mean()) p_(ggplot(tips) + aes(x = day, y = total_bill, group = 1) + stat_weighted_mean(geom = "line")) p_(ggplot(tips) + aes(x = day, y = total_bill, colour = sex, group = sex) + stat_weighted_mean(geom = "line")) p_(ggplot(tips) + aes(x = day, y = total_bill, fill = sex) + stat_weighted_mean(geom = "bar", position = "dodge")) # computing a proportion on the fly p_(ggplot(tips) + aes(x = day, y = as.integer(smoker == "Yes"), fill = sex) + stat_weighted_mean(geom = "bar", position = "dodge") + scale_y_continuous(labels = scales::percent)) # taking into account some weights d <- as.data.frame(Titanic) p_(ggplot(d) + aes(x = Class, y = as.integer(Survived == "Yes"), weight = Freq, fill = Sex) + geom_bar(stat = "weighted_mean", position = "dodge") + scale_y_continuous(labels = scales::percent) + labs(y = "Survived")) \dontrun{ cuse <- read.table("https://data.princeton.edu/wws509/datasets/cuse.dat", header = TRUE) cuse$n <- cuse$notUsing + cuse$using cuse$prop <- cuse$using / cuse$n ggplot(cuse) + aes(x = education, y = prop, weight = n) + stat_weighted_mean() ggplot(cuse) + aes(x = age, y = prop, weight = n, color = education) + stat_weighted_mean() ggplot(cuse) + aes(x = education, y = prop, weight = n) + stat_weighted_mean(geom = "bar") # add percentages above each bar ggplot(cuse) + aes(x = age, y = prop, weight = n, fill = education) + stat_weighted_mean(geom = "bar") + geom_text(aes(label = scales::percent(after_stat(y))), stat = "weighted_mean", vjust = 0) + facet_grid(~ education) } } \keyword{datasets} GGally/man/ggally_colbar.Rd0000644000176200001440000000477713761572054015325 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stat_prop.R \name{ggally_colbar} \alias{ggally_colbar} \alias{ggally_rowbar} \title{Column and row bar plots} \usage{ ggally_colbar( data, mapping, label_format = scales::label_percent(accuracy = 0.1), ..., remove_background = FALSE, remove_percentage_axis = FALSE, reverse_fill_levels = FALSE, geom_bar_args = NULL ) ggally_rowbar( data, mapping, label_format = scales::label_percent(accuracy = 0.1), ..., remove_background = FALSE, remove_percentage_axis = FALSE, reverse_fill_levels = TRUE, geom_bar_args = NULL ) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{label_format}{formatter function for displaying proportions, not taken into account if a label aesthetic is provided in \code{mapping}} \item{...}{other arguments passed to \code{\link[ggplot2]{geom_text}(...)}} \item{remove_background}{should the \code{panel.background} be removed?} \item{remove_percentage_axis}{should percentage axis be removed? Removes the y-axis for \code{ggally_colbar()} and x-axis for \code{ggally_rowbar()}} \item{reverse_fill_levels}{should the levels of the fill variable be reversed?} \item{geom_bar_args}{other arguments passed to \code{\link[ggplot2]{geom_bar}(...)}} } \description{ Plot column or row percentage using bar plots. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_colbar(tips, mapping = aes(x = smoker, y = sex))) p_(ggally_rowbar(tips, mapping = aes(x = smoker, y = sex))) # change labels' size p_(ggally_colbar(tips, mapping = aes(x = smoker, y = sex), size = 8)) # change labels' colour and use bold p_(ggally_colbar(tips, mapping = aes(x = smoker, y = sex), colour = "white", fontface = "bold")) # display number of observations instead of proportions p_(ggally_colbar(tips, mapping = aes(x = smoker, y = sex, label = after_stat(count)))) # custom bar width p_(ggally_colbar(tips, mapping = aes(x = smoker, y = sex), geom_bar_args = list(width = .5))) # change format of labels p_(ggally_colbar(tips, mapping = aes(x = smoker, y = sex), label_format = scales::label_percent(accuracy = .01, decimal.mark = ","))) p_(ggduo( data = as.data.frame(Titanic), mapping = aes(weight = Freq), columnsX = "Survived", columnsY = c("Sex", "Class", "Age"), types = list(discrete = "rowbar"), legend = 1 )) } \author{ Joseph Larmarange } \keyword{hplot} GGally/man/twitter_spambots.Rd0000644000176200001440000000154613761572054016125 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/data-twitter_spambots.R \docType{data} \name{twitter_spambots} \alias{twitter_spambots} \title{Twitter spambots} \format{ An object of class \code{network} with 120 edges and 94 vertices. } \usage{ data(twitter_spambots) } \description{ A network of spambots found on Twitter as part of a data mining project. } \details{ Each node of the network is identified by the Twitter screen name of the account and further carries five vertex attributes: \itemize{ \item location user's location, as provided by the user \item lat latitude, based on the user's location \item lon longitude, based on the user's location \item followers number of Twitter accounts that follow this account \item friends number of Twitter accounts followed by the account } } \author{ Amos Elberg } \keyword{datasets} GGally/man/vig_ggally.Rd0000644000176200001440000000130613667446546014644 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vig_ggally.R \name{vig_ggally} \alias{vig_ggally} \title{View GGally vignettes} \usage{ vig_ggally(name) } \arguments{ \item{name}{Vignette name to open. If no name is provided, the vignette index will be opened} } \description{ This function will open the directly to the vignette requested. If no \code{name} is provided, the index of all \pkg{GGally} vignettes will be opened. } \details{ This method allows for vignettes to be hosted remotely, reducing \pkg{GGally}'s package size, and installation time. } \examples{ \donttest{ # View `ggnostic` vignette vig_ggally("ggnostic") # View all vignettes by GGally vig_ggally() } } GGally/man/ggally_nostic_resid.Rd0000644000176200001440000000342413764714663016543 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnostic.R \name{ggally_nostic_resid} \alias{ggally_nostic_resid} \title{\code{\link{ggnostic}} residuals} \usage{ ggally_nostic_resid( data, mapping, ..., linePosition = 0, lineColor = brew_colors("grey"), lineSize = 0.5, lineAlpha = 1, lineType = 1, lineConfColor = brew_colors("grey"), lineConfSize = lineSize, lineConfAlpha = lineAlpha, lineConfType = 2, pVal = c(0.025, 0.975), sigma = attr(data, "broom_glance")$sigma, se = TRUE, method = "auto", formula = y ~ x ) } \arguments{ \item{data, mapping, ...}{parameters supplied to \code{\link{ggally_nostic_line}}} \item{linePosition, lineColor, lineSize, lineAlpha, lineType}{parameters supplied to \code{\link[ggplot2:geom_path]{ggplot2::geom_line()}}} \item{lineConfColor, lineConfSize, lineConfAlpha, lineConfType}{parameters supplied to the confidence interval lines} \item{pVal}{percentiles of a N(0, sigma) distribution to be drawn} \item{sigma}{sigma value for the \code{pVal} percentiles} \item{se}{boolean to determine if the confidence intervals should be displayed} \item{method, formula}{parameters supplied to \code{\link[ggplot2:geom_smooth]{ggplot2::geom_smooth()}}. Defaults to \code{"auto"} and \code{"y ~ x"}} } \value{ \pkg{ggplot2} plot object } \description{ If non-null \code{pVal} and \code{sigma} values are given, confidence interval lines will be added to the plot at the specified \code{pVal} percentiles of a N(0, sigma) distribution. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive dt <- broomify(stats::lm(mpg ~ wt + qsec + am, data = mtcars)) p_(ggally_nostic_resid(dt, ggplot2::aes(wt, .resid))) } \seealso{ \code{stats::\link[stats]{residuals}} } GGally/man/uppertriangle.Rd0000644000176200001440000000164413665760216015375 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggscatmat.R \name{uppertriangle} \alias{uppertriangle} \title{Rearrange dataset as the preparation of \code{\link{ggscatmat}} function} \usage{ uppertriangle( data, columns = 1:ncol(data), color = NULL, corMethod = "pearson" ) } \arguments{ \item{data}{a data matrix. Should contain numerical (continuous) data.} \item{columns}{an option to choose the column to be used in the raw dataset. Defaults to \code{1:ncol(data)}} \item{color}{an option to choose a factor variable to be grouped with. Defaults to \code{(NULL)}} \item{corMethod}{method argument supplied to \code{\link[stats]{cor}}} } \description{ Function for making the dataset used to plot the uppertriangle plots. } \examples{ data(flea) head(uppertriangle(flea, columns=2:4)) head(uppertriangle(flea)) head(uppertriangle(flea, color="species")) } \author{ Mengjia Ni, Di Cook } GGally/man/ggnetworkmap.Rd0000644000176200001440000001660114063460265015212 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnetworkmap.R \name{ggnetworkmap} \alias{ggnetworkmap} \title{Network plot map overlay} \usage{ ggnetworkmap( gg, net, size = 3, alpha = 0.75, weight, node.group, node.color = NULL, node.alpha = NULL, ring.group, segment.alpha = NULL, segment.color = "grey", great.circles = FALSE, segment.size = 0.25, arrow.size = 0, label.nodes = FALSE, label.size = size/2, ... ) } \arguments{ \item{gg}{an object of class \code{ggplot}.} \item{net}{an object of class \code{\link[network]{network}}, or any object that can be coerced to this class, such as an adjacency or incidence matrix, or an edge list: see \link[network]{edgeset.constructors} and \link[network]{network} for details. If the object is of class \link[igraph:aaa-igraph-package]{igraph} and the \link[intergraph:intergraph-package]{intergraph} package is installed, it will be used to convert the object: see \code{\link[intergraph]{asNetwork}} for details.} \item{size}{size of the network nodes. Defaults to 3. If the nodes are weighted, their area is proportionally scaled up to the size set by \code{size}.} \item{alpha}{a level of transparency for nodes, vertices and arrows. Defaults to 0.75.} \item{weight}{if present, the unquoted name of a vertex attribute in \code{data}. Otherwise nodes are unweighted.} \item{node.group}{\code{NULL}, the default, or the unquoted name of a vertex attribute that will be used to determine the color of each node.} \item{node.color}{If \code{node.group} is null, a character string specifying a color.} \item{node.alpha}{transparency of the nodes. Inherits from \code{alpha}.} \item{ring.group}{if not \code{NULL}, the default, the unquoted name of a vertex attribute that will be used to determine the color of each node border.} \item{segment.alpha}{transparency of the vertex links. Inherits from \code{alpha}} \item{segment.color}{color of the vertex links. Defaults to \code{"grey"}.} \item{great.circles}{whether to draw edges as great circles using the \code{geosphere} package. Defaults to \code{FALSE}} \item{segment.size}{size of the vertex links, as a vector of values or as a single value. Defaults to 0.25.} \item{arrow.size}{size of the vertex arrows for directed network plotting, in centimeters. Defaults to 0.} \item{label.nodes}{label nodes with their vertex names attribute. If set to \code{TRUE}, all nodes are labelled. Also accepts a vector of character strings to match with vertex names.} \item{label.size}{size of the labels. Defaults to \code{size / 2}.} \item{...}{other arguments supplied to geom_text for the node labels. Arguments pertaining to the title or other items can be achieved through \pkg{ggplot2} methods.} } \description{ Plots a network with \pkg{ggplot2} suitable for overlay on a \pkg{ggmap} plot or \pkg{ggplot2} } \details{ This is a descendant of the original \code{ggnet} function. \code{ggnet} added the innovation of plotting the network geographically. However, \code{ggnet} needed to be the first object in the ggplot chain. \code{ggnetworkmap} does not. If passed a \code{ggplot} object as its first argument, such as output from \code{ggmap}, \code{ggnetworkmap} will plot on top of that chart, looking for vertex attributes \code{lon} and \code{lat} as coordinates. Otherwise, \code{ggnetworkmap} will generate coordinates using the Fruchterman-Reingold algorithm. This is a function for plotting graphs generated by \code{network} or \code{igraph} in a more flexible and elegant manner than permitted by ggnet. The function does not need to be the first plot in the ggplot chain, so the graph can be plotted on top of a map or other chart. Segments can be straight lines, or plotted as great circles. Note that the great circles feature can produce odd results with arrows and with vertices beyond the plot edges; this is a \pkg{ggplot2} limitation and cannot yet be fixed. Nodes can have two color schemes, which are then plotted as the center and ring around the node. The color schemes are selected by adding scale_fill_ or scale_color_ just like any other \pkg{ggplot2} plot. If there are no rings, scale_color sets the color of the nodes. If there are rings, scale_color sets the color of the rings, and scale_fill sets the color of the centers. Note that additional arguments in the ... are passed to geom_text for plotting labels. } \examples{ # small function to display plots only if it's interactive p_ <- GGally::print_if_interactive invisible(lapply(c("ggplot2", "maps", "network", "sna"), base::library, character.only = TRUE)) ## Example showing great circles on a simple map of the USA ## http://flowingdata.com/2011/05/11/how-to-map-connections-with-great-circles/ \donttest{ airports <- read.csv("http://datasets.flowingdata.com/tuts/maparcs/airports.csv", header = TRUE) rownames(airports) <- airports$iata # select some random flights set.seed(123) flights <- data.frame( origin = sample(airports[200:400, ]$iata, 200, replace = TRUE), destination = sample(airports[200:400, ]$iata, 200, replace = TRUE) ) # convert to network flights <- network(flights, directed = TRUE) # add geographic coordinates flights \%v\% "lat" <- airports[ network.vertex.names(flights), "lat" ] flights \%v\% "lon" <- airports[ network.vertex.names(flights), "long" ] # drop isolated airports delete.vertices(flights, which(degree(flights) < 2)) # compute degree centrality flights \%v\% "degree" <- degree(flights, gmode = "digraph") # add random groups flights \%v\% "mygroup" <- sample(letters[1:4], network.size(flights), replace = TRUE) # create a map of the USA usa <- ggplot(map_data("usa"), aes(x = long, y = lat)) + geom_polygon(aes(group = group), color = "grey65", fill = "#f9f9f9", size = 0.2) # overlay network data to map p <- ggnetworkmap( usa, flights, size = 4, great.circles = TRUE, node.group = mygroup, segment.color = "steelblue", ring.group = degree, weight = degree ) p_(p) ## Exploring a community of spambots found on Twitter ## Data by Amos Elberg: see ?twitter_spambots for details data(twitter_spambots) # create a world map world <- fortify(map("world", plot = FALSE, fill = TRUE)) world <- ggplot(world, aes(x = long, y = lat)) + geom_polygon(aes(group = group), color = "grey65", fill = "#f9f9f9", size = 0.2) # view global structure p <- ggnetworkmap(world, twitter_spambots) p_(p) # domestic distribution p <- ggnetworkmap(net = twitter_spambots) p_(p) # topology p <- ggnetworkmap(net = twitter_spambots, arrow.size = 0.5) p_(p) # compute indegree and outdegree centrality twitter_spambots \%v\% "indegree" <- degree(twitter_spambots, cmode = "indegree") twitter_spambots \%v\% "outdegree" <- degree(twitter_spambots, cmode = "outdegree") p <- ggnetworkmap( net = twitter_spambots, arrow.size = 0.5, node.group = indegree, ring.group = outdegree, size = 4 ) + scale_fill_continuous("Indegree", high = "red", low = "yellow") + labs(color = "Outdegree") p_(p) # show some vertex attributes associated with each account p <- ggnetworkmap( net = twitter_spambots, arrow.size = 0.5, node.group = followers, ring.group = friends, size = 4, weight = indegree, label.nodes = TRUE, vjust = -1.5 ) + scale_fill_continuous("Followers", high = "red", low = "yellow") + labs(color = "Friends") + scale_color_continuous(low = "lightgreen", high = "darkgreen") p_(p) } } \author{ Amos Elberg. Original by Moritz Marbach, Francois Briatte } GGally/man/ggbivariate.Rd0000644000176200001440000000425413666472400014774 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggbivariate.R \name{ggbivariate} \alias{ggbivariate} \title{Display an outcome using several potential explanatory variables} \usage{ ggbivariate( data, outcome, explanatory = NULL, mapping = NULL, types = NULL, ..., rowbar_args = NULL ) } \arguments{ \item{data}{dataset to be used, can have both categorical and numerical variables} \item{outcome}{name or position of the outcome variable (one variable only)} \item{explanatory}{names or positions of the explanatory variables (if \code{NULL}, will take all variables other than \code{outcome})} \item{mapping}{additional aesthetic to be used, for example to indicate weights (see examples)} \item{types}{custom types of plots to use, see \code{\link{ggduo}}} \item{...}{additional arguments passed to \code{\link{ggduo}} (see examples)} \item{rowbar_args}{additional arguments passed to \code{\link{ggally_rowbar}} (see examples)} } \description{ \code{ggbivariate} is a variant of \code{\link{ggduo}} for plotting an outcome variable with several potential explanatory variables. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggbivariate(tips, "smoker", c("day", "time", "sex", "tip"))) # Personalize plot title and legend title p_(ggbivariate( tips, "smoker", c("day", "time", "sex", "tip"), title = "Custom title" ) + labs(fill = "Smoker ?")) # Customize fill colour scale p_(ggbivariate(tips, "smoker", c("day", "time", "sex", "tip")) + scale_fill_brewer(type = "qual")) # Customize labels p_(ggbivariate( tips, "smoker", c("day", "time", "sex", "tip"), rowbar_args = list( colour = "white", size = 4, fontface = "bold", label_format = scales::label_percent(accurary = 1) ) )) # Choose the sub-plot from which get legend p_(ggbivariate(tips, "smoker")) p_(ggbivariate(tips, "smoker", legend = 3)) # Use mapping to indicate weights d <- as.data.frame(Titanic) p_(ggbivariate(d, "Survived", mapping = aes(weight = Freq))) # outcome can be numerical p_(ggbivariate(tips, outcome = "tip", title = "tip")) } \author{ Joseph Larmarange } GGally/man/flea.Rd0000644000176200001440000000174713761572054013425 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/data-flea.R \docType{data} \name{flea} \alias{flea} \title{Historical data used for classification examples.} \format{ A data frame with 74 rows and 7 variables } \usage{ data(flea) } \description{ This data contains physical measurements on three species of flea beetles. } \details{ \itemize{ \item species Ch. concinna, Ch. heptapotamica, Ch. heikertingeri \item tars1 width of the first joint of the first tarsus in microns \item tars2 width of the second joint of the first tarsus in microns \item head the maximal width of the head between the external edges of the eyes in 0.01 mm \item aede1 the maximal width of the aedeagus in the fore-part in microns \item aede2 the front angle of the aedeagus (1 unit = 7.5 degrees) \item aede3 the aedeagus width from the side in microns } } \references{ Lubischew, A. A. (1962), On the Use of Discriminant Functions in Taxonomy, Biometrics 18:455-477. } \keyword{datasets} GGally/man/column_is_character.Rd0000644000176200001440000000105313761572054016510 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggparcoord.R \name{column_is_character} \alias{column_is_character} \alias{column_is_factor} \title{Get vector of variable types from data frame} \usage{ column_is_character(df) column_is_factor(df) } \arguments{ \item{df}{data frame to extract variable types from} } \value{ character vector with variable types, with names corresponding to the variable names from df } \description{ Get vector of variable types from data frame } \author{ Jason Crowley } \keyword{internal} GGally/man/happy.Rd0000644000176200001440000000334613761572054013634 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/data-happy.R \docType{data} \name{happy} \alias{happy} \title{Data related to happiness from the General Social Survey, 1972-2006.} \format{ A data frame with 51020 rows and 10 variables } \usage{ data(happy) } \description{ This data extract is taken from Hadley Wickham's \code{productplots} package. The original description follows, with minor edits. } \details{ The data is a small sample of variables related to happiness from the General Social Survey (GSS). The GSS is a yearly cross-sectional survey of Americans, run from 1972. We combine data for 25 years to yield 51,020 observations, and of the over 5,000 variables, we select nine related to happiness: \itemize{ \item age. age in years: 18--89. \item degree. highest education: lt high school, high school, junior college, bachelor, graduate. \item finrela. relative financial status: far above, above average, average, below average, far below. \item happy. happiness: very happy, pretty happy, not too happy. \item health. health: excellent, good, fair, poor. \item marital. marital status: married, never married, divorced, widowed, separated. \item sex. sex: female, male. \item wtsall. probability weight. 0.43--6.43. } } \references{ Smith, Tom W., Peter V. Marsden, Michael Hout, Jibum Kim. \emph{General Social Surveys, 1972-2006}. [machine-readable data file]. Principal Investigator, Tom W. Smith; Co-Principal Investigators, Peter V. Marsden and Michael Hout, NORC ed. Chicago: National Opinion Research Center, producer, 2005; Storrs, CT: The Roper Center for Public Opinion Research, University of Connecticut, distributor. 1 data file (57,061 logical records) and 1 codebook (3,422 pp). } \keyword{datasets} GGally/man/print_if_interactive.Rd0000644000176200001440000000053013665760216016714 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{print_if_interactive} \alias{print_if_interactive} \title{Print if not CRAN} \usage{ print_if_interactive(p) } \arguments{ \item{p}{plot to be displayed} } \description{ Small function to print a plot if the R session is interactive or in a CI build } GGally/man/ggally_cross.Rd0000644000176200001440000000433613764714663015212 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stat_cross.R \name{ggally_cross} \alias{ggally_cross} \title{Plots the number of observations} \usage{ ggally_cross(data, mapping, ..., scale_max_size = 20, geom_text_args = NULL) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{other arguments passed to \code{\link[ggplot2:geom_point]{ggplot2::geom_point()}}} \item{scale_max_size}{\code{max_size} argument supplied to \code{\link[ggplot2:scale_size]{ggplot2::scale_size_area()}}} \item{geom_text_args}{other arguments passed to \code{\link[ggplot2:geom_text]{ggplot2::geom_text()}}} } \description{ Plot the number of observations by using square points with proportional areas. Could be filled according to chi-squared statistics computed by \code{\link[=stat_cross]{stat_cross()}}. Labels could also be added (see examples). } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_cross(tips, mapping = aes(x = smoker, y = sex))) p_(ggally_cross(tips, mapping = aes(x = day, y = time))) # Custom max size p_(ggally_cross(tips, mapping = aes(x = smoker, y = sex)) + scale_size_area(max_size = 40)) # Custom fill p_(ggally_cross(tips, mapping = aes(x = smoker, y = sex), fill = "red")) # Custom shape p_(ggally_cross(tips, mapping = aes(x = smoker, y = sex), shape = 21)) # Fill squares according to standardized residuals d <- as.data.frame(Titanic) p_(ggally_cross( d, mapping = aes(x = Class, y = Survived, weight = Freq, fill = after_stat(std.resid)) ) + scale_fill_steps2(breaks = c(-3, -2, 2, 3), show.limits = TRUE)) # Add labels p_(ggally_cross( tips, mapping = aes( x = smoker, y = sex, colour = smoker, label = scales::percent(after_stat(prop)) ) )) # Customize labels' appearance and same size for all squares p_(ggally_cross( tips, mapping = aes( x = smoker, y = sex, size = NULL, # do not map size to a variable label = scales::percent(after_stat(prop)) ), size = 40, # fix value for points size fill = "darkblue", geom_text_args = list(colour = "white", fontface = "bold", size = 6) )) } \author{ Joseph Larmarange } \keyword{hplot} GGally/man/find_plot_type.Rd0000644000176200001440000000107613665760216015532 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/find-combo.R \name{find_plot_type} \alias{find_plot_type} \title{Find plot types} \usage{ find_plot_type(col1Name, col2Name, type1, type2, isAllNa, allowDiag) } \arguments{ \item{col1Name}{x column name} \item{col2Name}{y column name} \item{type1}{x column type} \item{type2}{y column type} \item{isAllNa}{is.na(data)} \item{allowDiag}{allow for diag values to be returned} } \description{ Retrieves the type of plot for the specific columns } \author{ Barret Schloerke } \keyword{internal} GGally/man/ggparcoord.Rd0000644000176200001440000001725613761572054014647 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggparcoord.R \name{ggparcoord} \alias{ggparcoord} \title{Parallel coordinate plot} \usage{ ggparcoord( data, columns = 1:ncol(data), groupColumn = NULL, scale = "std", scaleSummary = "mean", centerObsID = 1, missing = "exclude", order = columns, showPoints = FALSE, splineFactor = FALSE, alphaLines = 1, boxplot = FALSE, shadeBox = NULL, mapping = NULL, title = "" ) } \arguments{ \item{data}{the dataset to plot} \item{columns}{a vector of variables (either names or indices) to be axes in the plot} \item{groupColumn}{a single variable to group (color) by} \item{scale}{method used to scale the variables (see Details)} \item{scaleSummary}{if scale=="center", summary statistic to univariately center each variable by} \item{centerObsID}{if scale=="centerObs", row number of case plot should univariately be centered on} \item{missing}{method used to handle missing values (see Details)} \item{order}{method used to order the axes (see Details)} \item{showPoints}{logical operator indicating whether points should be plotted or not} \item{splineFactor}{logical or numeric operator indicating whether spline interpolation should be used. Numeric values will multiplied by the number of columns, \code{TRUE} will default to cubic interpolation, \code{\link[base]{AsIs}} to set the knot count directly and \code{0}, \code{FALSE}, or non-numeric values will not use spline interpolation.} \item{alphaLines}{value of alpha scaler for the lines of the parcoord plot or a column name of the data} \item{boxplot}{logical operator indicating whether or not boxplots should underlay the distribution of each variable} \item{shadeBox}{color of underlying box which extends from the min to the max for each variable (no box is plotted if \code{shadeBox == NULL})} \item{mapping}{aes string to pass to ggplot object} \item{title}{character string denoting the title of the plot} } \value{ ggplot object that if called, will print } \description{ A function for plotting static parallel coordinate plots, utilizing the \code{ggplot2} graphics package. } \details{ \code{scale} is a character string that denotes how to scale the variables in the parallel coordinate plot. Options: \itemize{ \item{\code{std}}{: univariately, subtract mean and divide by standard deviation} \item{\code{robust}}{: univariately, subtract median and divide by median absolute deviation} \item{\code{uniminmax}}{: univariately, scale so the minimum of the variable is zero, and the maximum is one} \item{\code{globalminmax}}{: no scaling is done; the range of the graphs is defined by the global minimum and the global maximum} \item{\code{center}}{: use \code{uniminmax} to standardize vertical height, then center each variable at a value specified by the \code{scaleSummary} param} \item{\code{centerObs}}{: use \code{uniminmax} to standardize vertical height, then center each variable at the value of the observation specified by the \code{centerObsID} param} } \code{missing} is a character string that denotes how to handle missing missing values. Options: \itemize{ \item{\code{exclude}}{: remove all cases with missing values} \item{\code{mean}}{: set missing values to the mean of the variable} \item{\code{median}}{: set missing values to the median of the variable} \item{\code{min10}}{: set missing values to 10\% below the minimum of the variable} \item{\code{random}}{: set missing values to value of randomly chosen observation on that variable} } \code{order} is either a vector of indices or a character string that denotes how to order the axes (variables) of the parallel coordinate plot. Options: \itemize{ \item{\code{(default)}}{: order by the vector denoted by \code{columns}} \item{\code{(given vector)}}{: order by the vector specified} \item{\code{anyClass}}{: order variables by their separation between any one class and the rest (as opposed to their overall variation between classes). This is accomplished by calculating the F-statistic for each class vs. the rest, for each axis variable. The axis variables are then ordered (decreasing) by their maximum of k F-statistics, where k is the number of classes.} \item{\code{allClass}}{: order variables by their overall F statistic (decreasing) from an ANOVA with \code{groupColumn} as the explanatory variable (note: it is required to specify a \code{groupColumn} with this ordering method). Basically, this method orders the variables by their variation between classes (most to least).} \item{\code{skewness}}{: order variables by their sample skewness (most skewed to least skewed)} \item{\code{Outlying}}{: order by the scagnostic measure, Outlying, as calculated by the package \code{scagnostics}. Other scagnostic measures available to order by are \code{Skewed}, \code{Clumpy}, \code{Sparse}, \code{Striated}, \code{Convex}, \code{Skinny}, \code{Stringy}, and \code{Monotonic}. Note: To use these methods of ordering, you must have the \code{scagnostics} package loaded.} } } \examples{ # small function to display plots only if it's interactive p_ <- GGally::print_if_interactive # use sample of the diamonds data for illustrative purposes data(diamonds, package="ggplot2") diamonds.samp <- diamonds[sample(1:dim(diamonds)[1], 100), ] # basic parallel coordinate plot, using default settings p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10)) p_(p) # this time, color by diamond cut p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2) p_(p) # underlay univariate boxplots, add title, use uniminmax scaling p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, scale = "uniminmax", boxplot = TRUE, title = "Parallel Coord. Plot of Diamonds Data") p_(p) # utilize ggplot2 aes to switch to thicker lines p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, title ="Parallel Coord. Plot of Diamonds Data", mapping = ggplot2::aes(size = 1)) + ggplot2::scale_size_identity() p_(p) # basic parallel coord plot of the msleep data, using 'random' imputation and # coloring by diet (can also use variable names in the columns and groupColumn # arguments) data(msleep, package="ggplot2") p <- ggparcoord(data = msleep, columns = 6:11, groupColumn = "vore", missing = "random", scale = "uniminmax") p_(p) # center each variable by its median, using the default missing value handler, # 'exclude' p <- ggparcoord(data = msleep, columns = 6:11, groupColumn = "vore", scale = "center", scaleSummary = "median") p_(p) # with the iris data, order the axes by overall class (Species) separation using # the anyClass option p <- ggparcoord(data = iris, columns = 1:4, groupColumn = 5, order = "anyClass") p_(p) # add points to the plot, add a title, and use an alpha scalar to make the lines # transparent p <- ggparcoord(data = iris, columns = 1:4, groupColumn = 5, order = "anyClass", showPoints = TRUE, title = "Parallel Coordinate Plot for the Iris Data", alphaLines = 0.3) p_(p) # color according to a column iris2 <- iris iris2$alphaLevel <- c("setosa" = 0.2, "versicolor" = 0.3, "virginica" = 0)[iris2$Species] p <- ggparcoord(data = iris2, columns = 1:4, groupColumn = 5, order = "anyClass", showPoints = TRUE, title = "Parallel Coordinate Plot for the Iris Data", alphaLines = "alphaLevel") p_(p) ## Use splines on values, rather than lines (all produce the same result) columns <- c(1, 5:10) p <- ggparcoord(diamonds.samp, columns, groupColumn = 2, splineFactor = TRUE) p_(p) p <- ggparcoord(diamonds.samp, columns, groupColumn = 2, splineFactor = 3) p_(p) splineFactor <- length(columns) * 3 p <- ggparcoord(diamonds.samp, columns, groupColumn = 2, splineFactor = I(splineFactor)) p_(p) } \author{ Jason Crowley, Barret Schloerke, Di Cook, Heike Hofmann, Hadley Wickham } GGally/man/fn_switch.Rd0000644000176200001440000000234113665760216014473 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnostic.R \name{fn_switch} \alias{fn_switch} \title{Function switch} \usage{ fn_switch(types, mapping_val = "y") } \arguments{ \item{types}{list of functions that follow the \code{\link{ggmatrix}} function standard: \code{function(data, mapping, ...){ #make ggplot2 object }}. One key should be a 'default' key for a default switch case.} \item{mapping_val}{mapping value to switch on. Defaults to the 'y' variable of the aesthetics list.} } \description{ Function that allows you to call different functions based upon an aesthetic variable value. } \examples{ ggnostic_continuous_fn <- fn_switch(list( default = ggally_points, .fitted = ggally_points, .se.fit = ggally_nostic_se_fit, .resid = ggally_nostic_resid, .hat = ggally_nostic_hat, .sigma = ggally_nostic_sigma, .cooksd = ggally_nostic_cooksd, .std.resid = ggally_nostic_std_resid )) ggnostic_combo_fn <- fn_switch(list( default = ggally_box_no_facet, fitted = ggally_box_no_facet, .se.fit = ggally_nostic_se_fit, .resid = ggally_nostic_resid, .hat = ggally_nostic_hat, .sigma = ggally_nostic_sigma, .cooksd = ggally_nostic_cooksd, .std.resid = ggally_nostic_std_resid )) } GGally/man/ggally_text.Rd0000644000176200001440000000200713666472400015025 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_text} \alias{ggally_text} \title{Text plot} \usage{ ggally_text( label, mapping = ggplot2::aes(color = "black"), xP = 0.5, yP = 0.5, xrange = c(0, 1), yrange = c(0, 1), ... ) } \arguments{ \item{label}{text that you want to appear} \item{mapping}{aesthetics that don't relate to position (such as color)} \item{xP}{horizontal position percentage} \item{yP}{vertical position percentage} \item{xrange}{range of the data around it. Only nice to have if plotting in a matrix} \item{yrange}{range of the data around it. Only nice to have if plotting in a matrix} \item{...}{other arguments for geom_text} } \description{ Plot text for a plot. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive p_(ggally_text("Example 1")) p_(ggally_text("Example\nTwo", mapping = ggplot2::aes(size = 15), color = I("red"))) } \author{ Barret Schloerke } \keyword{hplot} GGally/man/getPlot.Rd0000644000176200001440000000142313666472400014121 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggpairs_getput.R \name{getPlot} \alias{getPlot} \alias{[.ggmatrix} \title{Subset a \code{\link{ggmatrix}} object} \usage{ getPlot(pm, i, j) \method{[}{ggmatrix}(pm, i, j, ...) } \arguments{ \item{pm}{\code{\link{ggmatrix}} object to select from} \item{i}{row from the top} \item{j}{column from the left} \item{...}{ignored} } \description{ Retrieves the ggplot object at the desired location. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") plotMatrix2 <- ggpairs(tips[, 3:2], upper = list(combo = "denstrip")) p_(plotMatrix2[1, 2]) } \seealso{ \code{\link{putPlot}} } \author{ Barret Schloerke } \keyword{hplot} GGally/man/add_ref_lines.Rd0000644000176200001440000000107413764714663015274 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gglyph.R \name{add_ref_lines} \alias{add_ref_lines} \title{Add reference lines for each cell of the glyphmap.} \usage{ add_ref_lines(data, color = "white", size = 1.5, ...) } \arguments{ \item{data}{A glyphmap structure.} \item{color}{Set the color to draw in, default is "white"} \item{size}{Set the line size, default is 1.5} \item{...}{other arguments passed onto \code{\link[ggplot2:geom_path]{ggplot2::geom_line()}}} } \description{ Add reference lines for each cell of the glyphmap. } GGally/man/ggpairs.Rd0000644000176200001440000002317513761572054014151 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggpairs.R \name{ggpairs} \alias{ggpairs} \title{ggplot2 generalized pairs plot} \usage{ ggpairs( data, mapping = NULL, columns = 1:ncol(data), title = NULL, upper = list(continuous = "cor", combo = "box_no_facet", discrete = "count", na = "na"), lower = list(continuous = "points", combo = "facethist", discrete = "facetbar", na = "na"), diag = list(continuous = "densityDiag", discrete = "barDiag", na = "naDiag"), params = NULL, ..., xlab = NULL, ylab = NULL, axisLabels = c("show", "internal", "none"), columnLabels = colnames(data[columns]), labeller = "label_value", switch = NULL, showStrips = NULL, legend = NULL, cardinality_threshold = 15, progress = NULL, proportions = NULL, legends = stop("deprecated") ) } \arguments{ \item{data}{data set using. Can have both numerical and categorical data.} \item{mapping}{aesthetic mapping (besides \code{x} and \code{y}). See \code{\link[ggplot2]{aes}()}. If \code{mapping} is numeric, \code{columns} will be set to the \code{mapping} value and \code{mapping} will be set to \code{NULL}.} \item{columns}{which columns are used to make plots. Defaults to all columns.} \item{title, xlab, ylab}{title, x label, and y label for the graph} \item{upper}{see Details} \item{lower}{see Details} \item{diag}{see Details} \item{params}{deprecated. Please see \code{\link{wrap_fn_with_param_arg}}} \item{...}{deprecated. Please use \code{mapping}} \item{axisLabels}{either "show" to display axisLabels, "internal" for labels in the diagonal plots, or "none" for no axis labels} \item{columnLabels}{label names to be displayed. Defaults to names of columns being used.} \item{labeller}{labeller for facets. See \code{\link[ggplot2]{labellers}}. Common values are \code{"label_value"} (default) and \code{"label_parsed"}.} \item{switch}{switch parameter for facet_grid. See \code{ggplot2::\link[ggplot2]{facet_grid}}. By default, the labels are displayed on the top and right of the plot. If \code{"x"}, the top labels will be displayed to the bottom. If \code{"y"}, the right-hand side labels will be displayed to the left. Can also be set to \code{"both"}} \item{showStrips}{boolean to determine if each plot's strips should be displayed. \code{NULL} will default to the top and right side plots only. \code{TRUE} or \code{FALSE} will turn all strips on or off respectively.} \item{legend}{May be the two objects described below or the default \code{NULL} value. The legend position can be moved by using ggplot2's theme element \code{pm + theme(legend.position = "bottom")} \describe{\item{a numeric vector of length 2}{provides the location of the plot to use the legend for the plot matrix's legend. Such as \code{legend = c(3,5)} which will use the legend from the plot in the third row and fifth column}\item{a single numeric value}{provides the location of a plot according to the display order. Such as \code{legend = 3} in a plot matrix with 2 rows and 5 columns displayed by column will return the plot in position \code{c(1,2)}}\item{a object from \code{\link{grab_legend}()}}{a predetermined plot legend that will be displayed directly}}} \item{cardinality_threshold}{maximum number of levels allowed in a character / factor column. Set this value to NULL to not check factor columns. Defaults to 15} \item{progress}{\code{NULL} (default) for a progress bar in interactive sessions with more than 15 plots, \code{TRUE} for a progress bar, \code{FALSE} for no progress bar, or a function that accepts at least a plot matrix and returns a new \code{progress::\link[progress]{progress_bar}}. See \code{\link{ggmatrix_progress}}.} \item{proportions}{Value to change how much area is given for each plot. Either \code{NULL} (default), numeric value matching respective length, \code{grid::\link[grid]{unit}} object with matching respective length or \code{"auto"} for automatic relative proportions based on the number of levels for categorical variables.} \item{legends}{deprecated} } \value{ \code{\link{ggmatrix}} object that if called, will print } \description{ Make a matrix of plots with a given data set } \details{ \code{upper} and \code{lower} are lists that may contain the variables 'continuous', 'combo', 'discrete', and 'na'. Each element of the list may be a function or a string. If a string is supplied, it must be a character string representing the tail end of a \code{ggally_NAME} function. The list of current valid \code{ggally_NAME} functions is visible in a dedicated vignette. \describe{ \item{continuous}{This option is used for continuous X and Y data.} \item{combo}{This option is used for either continuous X and categorical Y data or categorical X and continuous Y data.} \item{discrete}{This option is used for categorical X and Y data.} \item{na}{This option is used when all X data is \code{NA}, all Y data is \code{NA}, or either all X or Y data is \code{NA}.} } \code{diag} is a list that may only contain the variables 'continuous', 'discrete', and 'na'. Each element of the diag list is a string implementing the following options: \describe{ \item{continuous}{exactly one of ('densityDiag', 'barDiag', 'blankDiag'). This option is used for continuous X data.} \item{discrete}{exactly one of ('barDiag', 'blankDiag'). This option is used for categorical X and Y data.} \item{na}{exactly one of ('naDiag', 'blankDiag'). This option is used when all X data is \code{NA}.} } If 'blank' is ever chosen as an option, then ggpairs will produce an empty plot. If a function is supplied as an option to \code{upper}, \code{lower}, or \code{diag}, it should implement the function api of \code{function(data, mapping, ...){#make ggplot2 plot}}. If a specific function needs its parameters set, \code{\link{wrap}(fn, param1 = val1, param2 = val2)} the function with its parameters. } \examples{ # small function to display plots only if it's interactive p_ <- GGally::print_if_interactive ## Quick example, with and without colour data(flea) ggpairs(flea, columns = 2:4) pm <- ggpairs(flea, columns = 2:4, ggplot2::aes(colour=species)) p_(pm) # Note: colour should be categorical, else you will need to reset # the upper triangle to use points instead of trying to compute corr data(tips, package = "reshape") pm <- ggpairs(tips[, 1:3]) p_(pm) pm <- ggpairs(tips, 1:3, columnLabels = c("Total Bill", "Tip", "Sex")) p_(pm) pm <- ggpairs(tips, upper = "blank") p_(pm) ## Plot Types # Change default plot behavior pm <- ggpairs( tips[, c(1, 3, 4, 2)], upper = list(continuous = "density", combo = "box_no_facet"), lower = list(continuous = "points", combo = "dot_no_facet") ) p_(pm) # Supply Raw Functions (may be user defined functions!) pm <- ggpairs( tips[, c(1, 3, 4, 2)], upper = list(continuous = ggally_density, combo = ggally_box_no_facet), lower = list(continuous = ggally_points, combo = ggally_dot_no_facet) ) p_(pm) # Use sample of the diamonds data data(diamonds, package="ggplot2") diamonds.samp <- diamonds[sample(1:dim(diamonds)[1], 1000), ] # Different aesthetics for different plot sections and plot types pm <- ggpairs( diamonds.samp[, 1:5], mapping = ggplot2::aes(color = cut), upper = list(continuous = wrap("density", alpha = 0.5), combo = "box_no_facet"), lower = list(continuous = wrap("points", alpha = 0.3), combo = wrap("dot_no_facet", alpha = 0.4)), title = "Diamonds" ) p_(pm) ## Axis Label Variations # Only Variable Labels on the diagonal (no axis labels) pm <- ggpairs(tips[, 1:3], axisLabels="internal") p_(pm) # Only Variable Labels on the outside (no axis labels) pm <- ggpairs(tips[, 1:3], axisLabels="none") p_(pm) ## Facet Label Variations # Default: df_x <- rnorm(100) df_y <- df_x + rnorm(100, 0, 0.1) df <- data.frame(x = df_x, y = df_y, c = sqrt(df_x^2 + df_y^2)) pm <- ggpairs( df, columnLabels = c("alpha[foo]", "alpha[bar]", "sqrt(alpha[foo]^2 + alpha[bar]^2)") ) p_(pm) # Parsed labels: pm <- ggpairs( df, columnLabels = c("alpha[foo]", "alpha[bar]", "sqrt(alpha[foo]^2 + alpha[bar]^2)"), labeller = "label_parsed" ) p_(pm) ## Plot Insertion Example custom_car <- ggpairs(mtcars[, c("mpg", "wt", "cyl")], upper = "blank", title = "Custom Example") # ggplot example taken from example(geom_text) plot <- ggplot2::ggplot(mtcars, ggplot2::aes(x=wt, y=mpg, label=rownames(mtcars))) plot <- plot + ggplot2::geom_text(ggplot2::aes(colour=factor(cyl)), size = 3) + ggplot2::scale_colour_discrete(l=40) custom_car[1, 2] <- plot personal_plot <- ggally_text( "ggpairs allows you\nto put in your\nown plot.\nLike that one.\n <---" ) custom_car[1, 3] <- personal_plot p_(custom_car) ## Remove binwidth warning from ggplot2 # displays warning about picking a better binwidth pm <- ggpairs(tips, 2:3) p_(pm) # no warning displayed pm <- ggpairs(tips, 2:3, lower = list(combo = wrap("facethist", binwidth = 0.5))) p_(pm) # no warning displayed with user supplied function pm <- ggpairs(tips, 2:3, lower = list(combo = wrap(ggally_facethist, binwidth = 0.5))) p_(pm) ## Remove panel grid lines from correlation plots pm <- ggpairs( flea, columns = 2:4, upper = list(continuous = wrap(ggally_cor, displayGrid = FALSE)) ) p_(pm) ## Custom with/height of subplots pm <- ggpairs(tips, columns = c(2, 3, 5)) p_(pm) pm <- ggpairs(tips, columns = c(2, 3, 5), proportions = "auto") p_(pm) pm <- ggpairs(tips, columns = c(2, 3, 5), proportions = c(1, 3, 2)) p_(pm) } \references{ John W Emerson, Walton A Green, Barret Schloerke, Jason Crowley, Dianne Cook, Heike Hofmann, Hadley Wickham. The Generalized Pairs Plot. Journal of Computational and Graphical Statistics, vol. 22, no. 1, pp. 79-91, 2012. } \seealso{ wrap v1_ggmatrix_theme } \author{ Barret Schloerke, Jason Crowley, Di Cook, Heike Hofmann, Hadley Wickham } \keyword{hplot} GGally/man/ggmatrix_gtable.Rd0000644000176200001440000000154013665760216015647 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggmatrix_gtable.R \name{ggmatrix_gtable} \alias{ggmatrix_gtable} \title{\code{\link{ggmatrix}} \pkg{gtable} object} \usage{ ggmatrix_gtable( pm, ..., progress = NULL, progress_format = formals(ggmatrix_progress)$format ) } \arguments{ \item{pm}{\code{\link{ggmatrix}} object to be plotted} \item{...}{ignored} \item{progress, progress_format}{Please use the 'progress' parameter in your \code{\link{ggmatrix}}-like function. See \code{\link{ggmatrix_progress}} for a few examples. These parameters will soon be deprecated.} } \description{ Specialized method to print the \code{\link{ggmatrix}} object. } \examples{ data(tips, package = "reshape") pm <- ggpairs(tips, c(1,3,2), mapping = ggplot2::aes_string(color = "sex")) ggmatrix_gtable(pm) } \author{ Barret Schloerke } GGally/man/ggally_nostic_se_fit.Rd0000644000176200001440000000265413764714663016712 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnostic.R \name{ggally_nostic_se_fit} \alias{ggally_nostic_se_fit} \title{\code{\link{ggnostic}} fitted value's standard error} \usage{ ggally_nostic_se_fit( data, mapping, ..., lineColor = brew_colors("grey"), linePosition = NULL ) } \arguments{ \item{data, mapping, ..., lineColor}{parameters supplied to \code{\link{ggally_nostic_line}}} \item{linePosition}{base comparison for a perfect fit} } \value{ \pkg{ggplot2} plot object } \description{ A function to display \code{stats::\link[stats]{predict}}'s standard errors } \details{ As stated in \code{stats::\link[stats]{predict}} documentation: If the logical 'se.fit' is 'TRUE', standard errors of the predictions are calculated. If the numeric argument 'scale' is set (with optional ''df'), it is used as the residual standard deviation in the computation of the standard errors, otherwise this is extracted from the model fit. Since the se.fit is \code{TRUE} and scale is unset by default, the standard errors are extracted from the model fit. A base line of 0 is added to give reference to a perfect fit. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive dt <- broomify(stats::lm(mpg ~ wt + qsec + am, data = mtcars)) p_(ggally_nostic_se_fit(dt, ggplot2::aes(wt, .se.fit))) } \seealso{ \code{\link[stats:lm.influence]{stats::influence()}} } GGally/man/ggfacet.Rd0000644000176200001440000000436713665760216014121 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggfacet.R \name{ggfacet} \alias{ggfacet} \title{Single \pkg{ggplot2} plot matrix with \code{\link[ggplot2]{facet_grid}}} \usage{ ggfacet( data, mapping = NULL, columnsX = 1:ncol(data), columnsY = 1:ncol(data), fn = ggally_points, ..., columnLabelsX = names(data[columnsX]), columnLabelsY = names(data[columnsY]), xlab = NULL, ylab = NULL, title = NULL, scales = "free" ) } \arguments{ \item{data}{data.frame that contains all columns to be displayed. This data will be melted before being passed into the function \code{fn}} \item{mapping}{aesthetic mapping (besides \code{x} and \code{y}). See \code{\link[ggplot2]{aes}()}} \item{columnsX}{columns to be displayed in the plot matrix} \item{columnsY}{rows to be displayed in the plot matrix} \item{fn}{function to be executed. Similar to \code{\link{ggpairs}} and \code{\link{ggduo}}, the function may either be a string identifier or a real function that \code{\link{wrap}} understands.} \item{...}{extra arguments passed directly to \code{fn}} \item{columnLabelsX, columnLabelsY}{column and row labels to display in the plot matrix} \item{xlab, ylab, title}{plot matrix labels} \item{scales}{parameter supplied to \code{ggplot2::\link[ggplot2]{facet_grid}}. Default behavior is \code{"free"}} } \description{ Single \pkg{ggplot2} plot matrix with \code{\link[ggplot2]{facet_grid}} } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive if (requireNamespace("chemometrics", quietly = TRUE)) { data(NIR, package = "chemometrics") NIR_sub <- data.frame(NIR$yGlcEtOH, NIR$xNIR[,1:3]) str(NIR_sub) x_cols <- c("X1115.0", "X1120.0", "X1125.0") y_cols <- c("Glucose", "Ethanol") # using ggduo directly p <- ggduo(NIR_sub, x_cols, y_cols, types = list(continuous = "points")) p_(p) # using ggfacet p <- ggfacet(NIR_sub, x_cols, y_cols) p_(p) # add a smoother p <- ggfacet(NIR_sub, x_cols, y_cols, fn = 'smooth_loess') p_(p) # same output p <- ggfacet(NIR_sub, x_cols, y_cols, fn = ggally_smooth_loess) p_(p) # Change scales to be the same in for every row and for every column p <- ggfacet(NIR_sub, x_cols, y_cols, scales = "fixed") p_(p) } } GGally/man/eval_data_col.Rd0000644000176200001440000000101213663637143015256 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{eval_data_col} \alias{eval_data_col} \title{Evaluate data column} \usage{ eval_data_col(data, aes_col) } \arguments{ \item{data}{data set to evaluate the data with} \item{aes_col}{Single value from an \code{ggplot2::\link[ggplot2]{aes}(...)} object} } \value{ Aes mapping with the x and y values switched } \description{ Evaluate data column } \examples{ mapping <- ggplot2::aes(Petal.Length) eval_data_col(iris, mapping$x) } GGally/man/ggally_denstrip.Rd0000644000176200001440000000162513666472400015676 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_denstrip} \alias{ggally_denstrip} \title{Tile plot with facets} \usage{ ggally_denstrip(data, mapping, ...) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{other arguments being sent to stat_bin} } \description{ Displays a Tile Plot as densely as possible. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_denstrip(tips, mapping = ggplot2::aes(x = total_bill, y = sex))) p_(ggally_denstrip(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "sex"))) p_(ggally_denstrip( tips, mapping = ggplot2::aes_string(x = "sex", y = "tip", binwidth = "0.2") ) + ggplot2::scale_fill_gradient(low = "grey80", high = "black")) } \author{ Barret Schloerke } \keyword{hplot} GGally/man/ggmatrix_progress.Rd0000644000176200001440000000170013665760216016253 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggmatrix_progress.R \name{ggmatrix_progress} \alias{ggmatrix_progress} \title{\code{\link{ggmatrix}} default progress bar} \usage{ ggmatrix_progress( format = " plot: [:plot_i,:plot_j] [:bar]:percent est::eta ", clear = TRUE, show_after = 0, ... ) } \arguments{ \item{format, clear, show_after, ...}{parameters supplied directly to \code{progress::\link[progress]{progress_bar}$new()}} } \value{ function that accepts a plot matrix as the first argument and \code{...} for future expansion. Internally, the plot matrix is used to determine the total number of plots for the progress bar. } \description{ \code{\link{ggmatrix}} default progress bar } \examples{ p_ <- GGally::print_if_interactive pm <- ggpairs(iris, 1:2, progress = ggmatrix_progress()) p_(pm) # does not clear after finishing pm <- ggpairs(iris, 1:2, progress = ggmatrix_progress(clear = FALSE)) p_(pm) } GGally/man/is_blank_plot.Rd0000644000176200001440000000072013663637143015326 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggmatrix_print.R \name{is_blank_plot} \alias{is_blank_plot} \title{Is Blank Plot? Find out if the plot equals a blank plot} \usage{ is_blank_plot(p) } \description{ Is Blank Plot? Find out if the plot equals a blank plot } \examples{ GGally:::is_blank_plot(ggally_blank()) GGally:::is_blank_plot(ggally_points(mtcars, ggplot2::aes_string(x = "disp", y = "hp"))) } \keyword{internal} GGally/man/require_namespaces.Rd0000644000176200001440000000053413663637143016364 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{require_namespaces} \alias{require_namespaces} \title{Loads package namespaces} \usage{ require_namespaces(pkgs) } \arguments{ \item{pkgs}{vector of character values} } \description{ Loads package namespaces or yells at user... loudly } \keyword{internal} GGally/man/scatmat.Rd0000644000176200001440000000200713666472400014136 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggscatmat.R \name{scatmat} \alias{scatmat} \title{Plots the lowertriangle and density plots of the scatter plot matrix.} \usage{ scatmat(data, columns = 1:ncol(data), color = NULL, alpha = 1) } \arguments{ \item{data}{a data matrix. Should contain numerical (continuous) data.} \item{columns}{an option to choose the column to be used in the raw dataset. Defaults to \code{1:ncol(data)}} \item{color}{an option to group the dataset by the factor variable and color them by different colors. Defaults to \code{NULL}} \item{alpha}{an option to set the transparency in scatterplots for large data. Defaults to \code{1}.} } \description{ Function for making scatterplots in the lower triangle and diagonal density plots. } \examples{ # small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(flea) p_(scatmat(flea, columns=2:4)) p_(scatmat(flea, columns= 2:4, color="species")) } \author{ Mengjia Ni, Di Cook } GGally/man/rescale01.Rd0000644000176200001440000000067313663637143014274 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gglyph.R \name{rescale01} \alias{rescale01} \alias{range01} \alias{max1} \alias{mean0} \alias{min0} \alias{rescale11} \title{Rescaling functions} \usage{ range01(x) max1(x) mean0(x) min0(x) rescale01(x, xlim = NULL) rescale11(x, xlim = NULL) } \arguments{ \item{x}{numeric vector} \item{xlim}{value used in \code{range}} } \description{ Rescaling functions } GGally/man/ggally_cor_v1_5.Rd0000644000176200001440000000341513666472400015462 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/deprecated.R \name{ggally_cor_v1_5} \alias{ggally_cor_v1_5} \title{Correlation value plot} \usage{ ggally_cor_v1_5( data, mapping, alignPercent = 0.6, method = "pearson", use = "complete.obs", corAlignPercent = NULL, corMethod = NULL, corUse = NULL, displayGrid = TRUE, ... ) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{alignPercent}{right align position of numbers. Default is 60 percent across the horizontal} \item{method}{\code{method} supplied to cor function} \item{use}{\code{use} supplied to cor function} \item{corAlignPercent}{deprecated. Use parameter \code{alignPercent}} \item{corMethod}{deprecated. Use parameter \code{method}} \item{corUse}{deprecated. Use parameter \code{use}} \item{displayGrid}{if TRUE, display aligned panel gridlines} \item{...}{other arguments being supplied to geom_text} } \description{ (Deprecated. See \code{\link{ggally_cor}}.) } \details{ Estimate correlation from the given data. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_cor_v1_5(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip"))) # display with no grid p_(ggally_cor_v1_5( tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip"), displayGrid = FALSE )) # change text attributes p_(ggally_cor_v1_5( tips, mapping = ggplot2::aes(x = total_bill, y = tip), size = 15, colour = I("red") )) # split by a variable p_(ggally_cor_v1_5( tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip", color = "sex"), size = 5 )) } \seealso{ \code{\link{ggally_cor}} } \author{ Barret Schloerke } \keyword{hplot} GGally/man/ggtable.Rd0000644000176200001440000000356013761572054014116 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggtable.R \name{ggtable} \alias{ggtable} \title{Cross-tabulated tables of discrete variables} \usage{ ggtable( data, columnsX = 1:ncol(data), columnsY = 1:ncol(data), cells = c("observed", "prop", "row.prop", "col.prop", "expected", "resid", "std.resid"), fill = c("none", "std.resid", "resid"), mapping = NULL, ... ) } \arguments{ \item{data}{dataset to be used, can have both categorical and numerical variables} \item{columnsX, columnsY}{names or positions of which columns are used to make plots. Defaults to all columns.} \item{cells}{Which statistic should be displayed in table cells?} \item{fill}{Which statistic should be used for filling table cells?} \item{mapping}{additional aesthetic to be used, for example to indicate weights (see examples)} \item{...}{additional arguments passed to \code{\link{ggduo}} (see examples)} } \description{ \code{ggtable} is a variant of \code{\link{ggduo}} for quick cross-tabulated tables of discrete variables. } \examples{ # small function to display plots only if it's interactive p_ <- GGally::print_if_interactive if (require(reshape)) { data(tips, package = "reshape") p_(ggtable(tips, "smoker", c("day", "time", "sex"))) # displaying row proportions p_(ggtable(tips, "smoker", c("day", "time", "sex"), cells = "row.prop")) # filling cells with standardized residuals p_(ggtable(tips, "smoker", c("day", "time", "sex"), fill = "std.resid", legend = 1)) # if continuous variables are provided, just displaying some summary statistics p_(ggtable(tips, c("smoker", "total_bill"), c("day", "time", "sex", "tip"))) } # specifying weights d <- as.data.frame(Titanic) p_(ggtable( d, "Survived", c("Class", "Sex", "Age"), mapping = aes(weight = Freq), cells = "row.prop", fill = "std.resid" )) } \author{ Joseph Larmarange } GGally/man/ggally_facetbar.Rd0000644000176200001440000000137713666472400015621 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_facetbar} \alias{ggally_facetbar} \title{Faceted bar plot} \usage{ ggally_facetbar(data, mapping, ...) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{other arguments are sent to geom_bar} } \description{ X variables are plotted using \code{geom_bar} and are faceted by the Y variable. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_facetbar(tips, ggplot2::aes(x = sex, y = smoker, fill = time))) p_(ggally_facetbar(tips, ggplot2::aes(x = smoker, y = sex, fill = time))) } \author{ Barret Schloerke } \keyword{hplot} GGally/man/putPlot.Rd0000644000176200001440000000260013666472400014150 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggpairs_getput.R \name{putPlot} \alias{putPlot} \alias{[<-.ggmatrix} \title{Insert a plot into a \code{\link{ggmatrix}} object} \usage{ putPlot(pm, value, i, j) \method{[}{ggmatrix}(pm, i, j, ...) <- value } \arguments{ \item{pm}{ggally object to be altered} \item{value}{ggplot object to be placed} \item{i}{row from the top} \item{j}{column from the left} \item{...}{ignored} } \description{ Function to place your own plot in the layout. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive custom_car <- ggpairs(mtcars[, c("mpg", "wt", "cyl")], upper = "blank", title = "Custom Example") # ggplot example taken from example(geom_text) plot <- ggplot2::ggplot(mtcars, ggplot2::aes(x=wt, y=mpg, label=rownames(mtcars))) plot <- plot + ggplot2::geom_text(ggplot2::aes(colour=factor(cyl)), size = 3) + ggplot2::scale_colour_discrete(l=40) custom_car[1, 2] <- plot personal_plot <- ggally_text( "ggpairs allows you\nto put in your\nown plot.\nLike that one.\n <---" ) custom_car[1, 3] <- personal_plot # custom_car # remove plots after creating a plot matrix custom_car[2,1] <- NULL custom_car[3,1] <- "blank" # the same as storing null custom_car[3,2] <- NULL p_(custom_car) } \seealso{ \code{\link{getPlot}} } \author{ Barret Schloerke } \keyword{hplot} GGally/man/is_horizontal.Rd0000644000176200001440000000140013663637143015366 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{is_horizontal} \alias{is_horizontal} \alias{is_character_column} \title{Check if plot is horizontal} \usage{ is_horizontal(data, mapping, val = "y") is_character_column(data, mapping, val = "y") } \arguments{ \item{data}{data used in ggplot2 plot} \item{mapping}{ggplot2 \code{aes()} mapping} \item{val}{key to retrieve from \code{mapping}} } \value{ Boolean determining if the data is a character-like data } \description{ Check if plot is horizontal } \examples{ is_horizontal(iris, ggplot2::aes(Sepal.Length, Species)) # TRUE is_horizontal(iris, ggplot2::aes(Sepal.Length, Species), "x") # FALSE is_horizontal(iris, ggplot2::aes(Sepal.Length, Sepal.Width)) # FALSE } GGally/man/skewness.Rd0000644000176200001440000000055113665760216014352 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggparcoord.R \name{skewness} \alias{skewness} \title{Sample skewness} \usage{ skewness(x) } \arguments{ \item{x}{numeric vector} } \value{ sample skewness of \code{x} } \description{ Calculate the sample skewness of a vector while ignoring missing values. } \author{ Jason Crowley } GGally/man/ggts.Rd0000644000176200001440000000131013666472400013442 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnostic.R \name{ggts} \alias{ggts} \title{Multiple time series} \usage{ ggts(..., columnLabelsX = NULL, xlab = "time") } \arguments{ \item{...}{supplied directly to \code{\link{ggduo}}} \item{columnLabelsX}{remove top strips for the X axis by default} \item{xlab}{defaults to "time"} } \value{ \code{\link{ggmatrix}} object } \description{ GGally implementation of ts.plot. Wraps around the ggduo function and removes the column strips } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive p_(ggts(pigs, "time", c("gilts", "profit", "s_per_herdsz", "production", "herdsz"))) } GGally/man/glyphs.Rd0000644000176200001440000000350113666472400014010 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gglyph.R \name{glyphs} \alias{glyphs} \title{Create \code{\link{glyphplot}} data} \usage{ glyphs( data, x_major, x_minor, y_major, y_minor, polar = FALSE, height = ggplot2::rel(0.95), width = ggplot2::rel(0.95), y_scale = identity, x_scale = identity ) } \arguments{ \item{data}{A data frame containing variables named in \code{x_major}, \code{x_minor}, \code{y_major} and \code{y_minor}.} \item{x_major, x_minor, y_major, y_minor}{The name of the variable (as a string) for the major and minor x and y axes. Together, each unique} \item{polar}{A logical of length 1, specifying whether the glyphs should be drawn in polar coordinates. Defaults to \code{FALSE}.} \item{height, width}{The height and width of each glyph. Defaults to 95\% of the \code{\link[ggplot2]{resolution}} of the data. Specify the width absolutely by supplying a numeric vector of length 1, or relative to the} \item{y_scale, x_scale}{The scaling function to be applied to each set of minor values within a grid cell. Defaults to \code{\link{identity}} so that no scaling is performed.} } \description{ Create the data needed to generate a glyph plot. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(nasa) nasaLate <- nasa[ nasa$date >= as.POSIXct("1998-01-01") & nasa$lat >= 20 & nasa$lat <= 40 & nasa$long >= -80 & nasa$long <= -60 , ] temp.gly <- glyphs(nasaLate, "long", "day", "lat", "surftemp", height=2.5) p_(ggplot2::ggplot(temp.gly, ggplot2::aes(gx, gy, group = gid)) + add_ref_lines(temp.gly, color = "grey90") + add_ref_boxes(temp.gly, color = "grey90") + ggplot2::geom_path() + ggplot2::theme_bw() + ggplot2::labs(x = "", y = "")) } \author{ Di Cook, Heike Hofmann, Hadley Wickham } GGally/man/ggally_autopoint.Rd0000644000176200001440000000266113666472400016071 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_autopoint} \alias{ggally_autopoint} \alias{ggally_autopointDiag} \title{Scatterplot for continuous and categorical variables} \usage{ ggally_autopoint(data, mapping, ...) ggally_autopointDiag(data, mapping, ...) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{other arguments passed to \code{\link[ggforce]{geom_autopoint}(...)}} } \description{ Make scatterplots compatible with both continuous and categorical variables using \code{\link[ggforce]{geom_autopoint}} from package \pkg{ggforce}. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_autopoint(tips, mapping = aes(x = tip, y = total_bill))) p_(ggally_autopoint(tips, mapping = aes(x = tip, y = sex))) p_(ggally_autopoint(tips, mapping = aes(x = smoker, y = sex))) p_(ggally_autopoint(tips, mapping = aes(x = smoker, y = sex, color = day))) p_(ggally_autopoint(tips, mapping = aes(x = smoker, y = sex), size = 8)) p_(ggally_autopoint(tips, mapping = aes(x = smoker, y = sex), alpha = .9)) p_(ggpairs( tips, mapping = aes(colour = sex), upper = list(discrete = "autopoint", combo = "autopoint", continuous = "autopoint"), diag = list(discrete = "autopointDiag", continuous = "autopointDiag") )) } \author{ Joseph Larmarange } \keyword{hplot} GGally/man/ggally_na.Rd0000644000176200001440000000116713665760216014451 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_na} \alias{ggally_na} \alias{ggally_naDiag} \title{NA plot} \usage{ ggally_na(data = NULL, mapping = NULL, size = 10, color = "grey20", ...) ggally_naDiag(...) } \arguments{ \item{data}{ignored} \item{mapping}{ignored} \item{size}{size of the geom_text 'NA'} \item{color}{color of the geom_text 'NA'} \item{...}{other arguments sent to geom_text} } \description{ Draws a large \code{NA} in the middle of the plotting area. This plot is useful when all X or Y data is \code{NA} } \author{ Barret Schloerke } \keyword{hplot} GGally/man/ggally_points.Rd0000644000176200001440000000153013666472400015355 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_points} \alias{ggally_points} \title{Scatter plot} \usage{ ggally_points(data, mapping, ...) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{other arguments are sent to geom_point} } \description{ Make a scatter plot with a given data set. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(mtcars) p_(ggally_points(mtcars, mapping = ggplot2::aes(x = disp, y = hp))) p_(ggally_points(mtcars, mapping = ggplot2::aes_string(x = "disp", y = "hp"))) p_(ggally_points( mtcars, mapping = ggplot2::aes_string( x = "disp", y = "hp", color = "as.factor(cyl)", size = "gear" ) )) } \author{ Barret Schloerke } \keyword{hplot} GGally/man/ggcoef_model.Rd0000644000176200001440000003042314063456663015124 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggcoef_model.R \name{ggcoef_model} \alias{ggcoef_model} \alias{ggcoef_compare} \alias{ggcoef_multinom} \alias{ggcoef_plot} \title{Plot model coefficients} \usage{ ggcoef_model( model, tidy_fun = broom::tidy, conf.int = TRUE, conf.level = 0.95, exponentiate = FALSE, variable_labels = NULL, term_labels = NULL, interaction_sep = " * ", categorical_terms_pattern = "{level}", add_reference_rows = TRUE, no_reference_row = NULL, intercept = FALSE, include = dplyr::everything(), significance = 1 - conf.level, significance_labels = NULL, show_p_values = TRUE, signif_stars = TRUE, return_data = FALSE, ... ) ggcoef_compare( models, type = c("dodged", "faceted"), tidy_fun = broom::tidy, conf.int = TRUE, conf.level = 0.95, exponentiate = FALSE, variable_labels = NULL, term_labels = NULL, interaction_sep = " * ", categorical_terms_pattern = "{level}", add_reference_rows = TRUE, no_reference_row = NULL, intercept = FALSE, include = dplyr::everything(), significance = 1 - conf.level, significance_labels = NULL, return_data = FALSE, ... ) ggcoef_multinom( model, type = c("dodged", "faceted"), y.level_label = NULL, tidy_fun = broom::tidy, conf.int = TRUE, conf.level = 0.95, exponentiate = FALSE, variable_labels = NULL, term_labels = NULL, interaction_sep = " * ", categorical_terms_pattern = "{level}", add_reference_rows = TRUE, no_reference_row = NULL, intercept = FALSE, include = dplyr::everything(), significance = 1 - conf.level, significance_labels = NULL, show_p_values = TRUE, signif_stars = TRUE, return_data = FALSE, ... ) ggcoef_plot( data, x = "estimate", y = "label", exponentiate = FALSE, point_size = 2, point_stroke = 2, point_fill = "white", colour = NULL, colour_guide = TRUE, colour_lab = "", colour_labels = ggplot2::waiver(), shape = "significance", shape_values = c(16, 21), shape_guide = TRUE, shape_lab = "", errorbar = TRUE, errorbar_height = 0.1, errorbar_coloured = FALSE, stripped_rows = TRUE, strips_odd = "#11111111", strips_even = "#00000000", vline = TRUE, vline_colour = "grey50", dodged = FALSE, dodged_width = 0.8, facet_row = "var_label", facet_col = NULL, facet_labeller = "label_value" ) } \arguments{ \item{model}{a regression model object} \item{tidy_fun}{option to specify a custom tidier function} \item{conf.int}{should confidence intervals be computed? (see \code{\link[broom:reexports]{broom::tidy()}})} \item{conf.level}{the confidence level to use for the confidence interval if \code{conf.int = TRUE}; must be strictly greater than 0 and less than 1; defaults to 0.95, which corresponds to a 95 percent confidence interval} \item{exponentiate}{if \code{TRUE} a logarithmic scale will be used for x-axis} \item{variable_labels}{a named list or a named vector of custom variable labels} \item{term_labels}{a named list or a named vector of custom term labels} \item{interaction_sep}{separator for interaction terms} \item{categorical_terms_pattern}{a \link[glue:glue]{glue pattern} for labels of categorical terms with treatment or sum contrasts (see \code{\link[broom.helpers:model_list_terms_levels]{model_list_terms_levels()}})} \item{add_reference_rows}{should reference rows be added?} \item{no_reference_row}{variables (accepts \link[dplyr:select]{tidyselect} notation) for those no reference row should be added, when \code{add_reference_rows = TRUE}} \item{intercept}{should the intercept(s) be included?} \item{include}{variables to include. Accepts \link[dplyr:select]{tidyselect} syntax. Use \code{-} to remove a variable. Default is \code{everything()}. See also \code{\link[broom.helpers:select_helpers]{all_continuous()}}, \code{\link[broom.helpers:select_helpers]{all_categorical()}}, \code{\link[broom.helpers:select_helpers]{all_dichotomous()}} and \code{\link[broom.helpers:select_helpers]{all_interaction()}}} \item{significance}{level (between 0 and 1) below which a coefficient is consider to be significantly different from 0 (or 1 if \code{exponentiate = TRUE}), \code{NULL} for not highlighting such coefficients} \item{significance_labels}{optional vector with custom labels for significance variable} \item{show_p_values}{if \code{TRUE}, add p-value to labels} \item{signif_stars}{if \code{TRUE}, add significant stars to labels} \item{return_data}{if \code{TRUE}, will return the data.frame used for plotting instead of the plot} \item{...}{parameters passed to \code{\link[=ggcoef_plot]{ggcoef_plot()}}} \item{models}{named list of models} \item{type}{a dodged plot or a faceted plot?} \item{y.level_label}{an optional named vector for labeling \code{y.level} (see examples)} \item{data}{a data frame containing data to be plotted, typically the output of \code{\link[=ggcoef_model]{ggcoef_model()}}, \code{\link[=ggcoef_compare]{ggcoef_compare()}} or \code{\link[=ggcoef_multinom]{ggcoef_multinom()}} with the option \code{return_data = TRUE}} \item{x, y}{variables mapped to x and y axis} \item{point_size}{size of the points} \item{point_stroke}{thickness of the points} \item{point_fill}{fill colour for the points} \item{colour}{optional variable name to be mapped to colour aesthetic} \item{colour_guide}{should colour guide be displayed in the legend?} \item{colour_lab}{label of the colour aesthetic in the legend} \item{colour_labels}{labels argument passed to \code{\link[ggplot2:scale_colour_discrete]{ggplot2::scale_colour_discrete()}} and \code{\link[ggplot2:discrete_scale]{ggplot2::discrete_scale()}}} \item{shape}{optional variable name to be mapped to the shape aesthetic} \item{shape_values}{values of the different shapes to use in \code{\link[ggplot2:scale_manual]{ggplot2::scale_shape_manual()}}} \item{shape_guide}{should shape guide be displayed in the legend?} \item{shape_lab}{label of the shape aesthetic in the legend} \item{errorbar}{should error bars be plotted?} \item{errorbar_height}{height of error bars} \item{errorbar_coloured}{should error bars be colored as the points?} \item{stripped_rows}{should stripped rows be displayed in the background?} \item{strips_odd}{color of the odd rows} \item{strips_even}{color of the even rows} \item{vline}{should a vertical line be drawn at 0 (or 1 if \code{exponentiate = TRUE})?} \item{vline_colour}{colour of vertical line} \item{dodged}{should points be dodged (according to the colour aesthetic)?} \item{dodged_width}{width value for \code{\link[ggplot2:position_dodge]{ggplot2::position_dodge()}}} \item{facet_row}{variable name to be used for row facets} \item{facet_col}{optional variable name to be used for column facets} \item{facet_labeller}{labeller function to be used for labeling facets; if labels are too long, you can use \code{\link[ggplot2:labellers]{ggplot2::label_wrap_gen()}} (see examples), more information in the documentation of \code{\link[ggplot2:facet_grid]{ggplot2::facet_grid()}}} } \description{ Plot model coefficients } \details{ \code{ggcoef_model()}, \code{ggcoef_multinom()} and \code{ggcoef_compare()} use \code{\link[broom.helpers:tidy_plus_plus]{broom.helpers::tidy_plus_plus()}} to obtain a \code{tibble} of the model coefficients, apply additional data transformation and then pass the produced \code{tibble} to \code{ggcoef_plot()} to generate the plot. For more control, you can use the argument \code{return_data = TRUE} to get the produced \code{tibble}, apply any transformation of your own and then pass your customized \code{tibble} to \code{ggcoef_plot()}. } \section{Functions}{ \itemize{ \item \code{ggcoef_model}: Redesign of \code{\link[=ggcoef]{ggcoef()}} based on \code{\link[broom.helpers:tidy_plus_plus]{broom.helpers::tidy_plus_plus()}}. \item \code{ggcoef_compare}: Designed for displaying several models on the same plot. \item \code{ggcoef_multinom}: A variation of \code{\link[=ggcoef_model]{ggcoef_model()}} adapted to multinomial logistic regressions performed with \code{\link[nnet:multinom]{nnet::multinom()}}. \item \code{ggcoef_plot}: SOME DESCRIPTION HERE }} \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive if (require(broom.helpers)) { data(tips, package = "reshape") mod_simple <- lm(tip ~ day + time + total_bill, data = tips) p_(ggcoef_model(mod_simple)) # custom variable labels # you can use the labelled package to define variable labels before computing model if (require(labelled)) { tips_labelled <- tips \%>\% labelled::set_variable_labels( day = "Day of the week", time = "Lunch or Dinner", total_bill = "Bill's total" ) mod_labelled <- lm(tip ~ day + time + total_bill, data = tips_labelled) p_(ggcoef_model(mod_labelled)) } # you can provide custom variable labels with 'variable_labels' p_(ggcoef_model( mod_simple, variable_labels = c( day = "Week day", time = "Time (lunch or dinner ?)", total_bill = "Total of the bill" ) )) # if labels are too long, you can use 'facet_labeller' to wrap them p_(ggcoef_model( mod_simple, variable_labels = c( day = "Week day", time = "Time (lunch or dinner ?)", total_bill = "Total of the bill" ), facet_labeller = label_wrap_gen(10) )) # do not display variable facets but add colour guide p_(ggcoef_model(mod_simple, facet_row = NULL, colour_guide = TRUE)) # a logistic regression example d_titanic <- as.data.frame(Titanic) d_titanic$Survived <- factor(d_titanic$Survived, c("No", "Yes")) mod_titanic <- glm( Survived ~ Sex * Age + Class, weights = Freq, data = d_titanic, family = binomial ) # use 'exponentiate = TRUE' to get the Odds Ratio p_(ggcoef_model(mod_titanic, exponentiate = TRUE)) # display intercepts p_(ggcoef_model(mod_titanic, exponentiate = TRUE, intercept = TRUE)) # customize terms labels p_( ggcoef_model( mod_titanic, exponentiate = TRUE, show_p_values = FALSE, signif_stars = FALSE, add_reference_rows = FALSE, categorical_terms_pattern = "{level} (ref: {reference_level})", interaction_sep = " x " ) + scale_y_discrete(labels = scales::label_wrap(15)) ) # display only a subset of terms p_(ggcoef_model(mod_titanic, exponentiate = TRUE, include = c("Age", "Class"))) # do not change points' shape based on significance p_(ggcoef_model(mod_titanic, exponentiate = TRUE, significance = NULL)) # a black and white version p_(ggcoef_model( mod_titanic, exponentiate = TRUE, colour = NULL, stripped_rows = FALSE )) # show dichotomous terms on one row p_(ggcoef_model( mod_titanic, exponentiate = TRUE, no_reference_row = broom.helpers::all_dichotomous(), categorical_terms_pattern = "{ifelse(dichotomous, paste0(level, ' / ', reference_level), level)}", show_p_values = FALSE )) # works also with with polynomial terms mod_poly <- lm( tip ~ poly(total_bill, 3) + day, data = tips, ) p_(ggcoef_model(mod_poly)) # or with different type of contrasts # for sum contrasts, the value of the reference term is computed if (require(emmeans)) { mod2 <- lm( tip ~ day + time + sex, data = tips, contrasts = list(time = contr.sum, day = contr.treatment(4, base = 3)) ) p_(ggcoef_model(mod2)) } } if (require(broom.helpers)) { # Use ggcoef_compare() for comparing several models on the same plot mod1 <- lm(Fertility ~ ., data = swiss) mod2 <- step(mod1, trace = 0) mod3 <- lm(Fertility ~ Agriculture + Education * Catholic, data = swiss) models <- list("Full model" = mod1, "Simplified model" = mod2, "With interaction" = mod3) p_(ggcoef_compare(models)) p_(ggcoef_compare(models, type = "faceted")) # you can reverse the vertical position of the point by using a negative value # for dodged_width (but it will produce some warnings) \dontrun{ p_(ggcoef_compare(models, dodged_width = -.9)) } } # specific function for nnet::multinom models if (require(broom.helpers) && require(nnet)) { data(happy) mod <- multinom(happy ~ age + degree + sex, data = happy) p_(ggcoef_multinom(mod, exponentiate = TRUE)) p_(ggcoef_multinom(mod, type = "faceted")) p_(ggcoef_multinom( mod, type = "faceted", y.level_label = c( "pretty happy" = "pretty happy\n(ref: very happy)", "very happy" = "very happy" ) )) } } GGally/man/ggally_crosstable.Rd0000644000176200001440000000374713761572054016220 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stat_cross.R \name{ggally_crosstable} \alias{ggally_crosstable} \title{Display a cross-tabulated table} \usage{ ggally_crosstable( data, mapping, cells = c("observed", "prop", "row.prop", "col.prop", "expected", "resid", "std.resid"), fill = c("none", "std.resid", "resid"), ..., geom_tile_args = list(colour = "grey50") ) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{cells}{Which statistic should be displayed in table cells?} \item{fill}{Which statistic should be used for filling table cells?} \item{...}{other arguments passed to \code{\link[ggplot2]{geom_text}(...)}} \item{geom_tile_args}{other arguments passed to \code{\link[ggplot2]{geom_tile}(...)}} } \description{ \code{ggally_crosstable} is a variation of \code{\link{ggally_table}} with few modifications: (i) table cells are drawn; (ii) x and y axis are not expanded (and therefore are not aligned with other \code{ggally_*} plots); (iii) content and fill of cells can be easily controlled with dedicated arguments. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") # differences with ggally_table() p_(ggally_table(tips, mapping = aes(x = day, y = time))) p_(ggally_crosstable(tips, mapping = aes(x = day, y = time))) # display column proportions p_(ggally_crosstable(tips, mapping = aes(x = day, y = sex), cells = "col.prop")) # display row proportions p_(ggally_crosstable(tips, mapping = aes(x = day, y = sex), cells = "row.prop")) # change size of text p_(ggally_crosstable(tips, mapping = aes(x = day, y = sex), size = 8)) # fill cells with standardized residuals p_(ggally_crosstable(tips, mapping = aes(x = day, y = sex), fill = "std.resid")) # change scale for fill p_(ggally_crosstable(tips, mapping = aes(x = day, y = sex), fill = "std.resid") + scale_fill_steps2(breaks = c(-2, 0, 2), show.limits = TRUE)) } GGally/man/ggally_statistic.Rd0000644000176200001440000000413713665760216016062 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_statistic} \alias{ggally_statistic} \title{Generalized text display} \usage{ ggally_statistic( data, mapping, text_fn, title, na.rm = NA, display_grid = FALSE, justify_labels = "right", justify_text = "left", sep = ": ", family = "mono", title_args = list(), group_args = list(), align_percent = 0.5, title_hjust = 0.5, group_hjust = 0.5 ) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{text_fn}{function that takes in \code{x} and \code{y} and returns a text string} \item{title}{title text to be displayed} \item{na.rm}{logical value which determines if \code{NA} values are removed. If \code{TRUE}, no warning message will be displayed.} \item{display_grid}{if \code{TRUE}, display aligned panel grid lines. If \code{FALSE} (default), display a thin panel border.} \item{justify_labels}{\code{justify} argument supplied when \code{\link[base]{format}}ting the labels} \item{justify_text}{\code{justify} argument supplied when \code{\link[base]{format}}ting the returned \code{text_fn(x, y)} values} \item{sep}{separation value to be placed between the labels and text} \item{family}{font family used when displaying all text. This value will be set in \code{title_args} or \code{group_args} if no \code{family} value exists. By using \code{"mono"}, groups will align with each other.} \item{title_args}{arguments being supplied to the title's \code{\link[ggplot2]{geom_text}()}} \item{group_args}{arguments being supplied to the split-by-color group's \code{\link[ggplot2]{geom_text}()}} \item{align_percent}{relative align position of the text. When \code{title_hjust = 0.5} and \code{group_hjust = 0.5}, this should not be needed to be set.} \item{title_hjust, group_hjust}{\code{hjust} sent to \code{\link[ggplot2]{geom_text}()} for the title and group values respectively. Any \code{hjust} value supplied in \code{title_args} or \code{group_args} will take precedence.} } \description{ Generalized text display } \seealso{ \code{\link{ggally_cor}} } GGally/man/ggcoef.Rd0000644000176200001440000000522613764714663013753 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggcoef.R \name{ggcoef} \alias{ggcoef} \title{Model coefficients with \pkg{broom} and \pkg{ggplot2}} \usage{ ggcoef( x, mapping = aes_string(y = "term", x = "estimate"), conf.int = TRUE, conf.level = 0.95, exponentiate = FALSE, exclude_intercept = FALSE, vline = TRUE, vline_intercept = "auto", vline_color = "gray50", vline_linetype = "dotted", vline_size = 1, errorbar_color = "gray25", errorbar_height = 0, errorbar_linetype = "solid", errorbar_size = 0.5, sort = c("none", "ascending", "descending"), ... ) } \arguments{ \item{x}{a model object to be tidied with \code{\link[broom:reexports]{broom::tidy()}} or a data frame (see Details)} \item{mapping}{default aesthetic mapping} \item{conf.int}{display confidence intervals as error bars?} \item{conf.level}{level of confidence intervals (passed to \code{\link[broom:reexports]{broom::tidy()}} if \code{x} is not a data frame)} \item{exponentiate}{if \code{TRUE}, x-axis will be logarithmic (also passed to \code{\link[broom:reexports]{broom::tidy()}} if \code{x} is not a data frame)} \item{exclude_intercept}{should the intercept be excluded from the plot?} \item{vline}{print a vertical line?} \item{vline_intercept}{\code{xintercept} for the vertical line. \code{"auto"} for \code{x = 0} (or \code{x = 1} if {exponentiate} is \code{TRUE})} \item{vline_color}{color of the vertical line} \item{vline_linetype}{line type of the vertical line} \item{vline_size}{size of the vertical line} \item{errorbar_color}{color of the error bars} \item{errorbar_height}{height of the error bars} \item{errorbar_linetype}{line type of the error bars} \item{errorbar_size}{size of the error bars} \item{sort}{\code{"none"} (default) do not sort, \code{"ascending"} sort by increasing coefficient value, or \code{"descending"} sort by decreasing coefficient value} \item{...}{additional arguments sent to \code{\link[ggplot2:geom_point]{ggplot2::geom_point()}}} } \description{ Plot the coefficients of a model with \pkg{broom} and \pkg{ggplot2}. For an updated and improved version, see \code{\link[=ggcoef_model]{ggcoef_model()}}. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive library(broom) reg <- lm(Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width, data = iris) p_(ggcoef(reg)) \donttest{d <- as.data.frame(Titanic) reg2 <- glm(Survived ~ Sex + Age + Class, family = binomial, data = d, weights = d$Freq) ggcoef(reg2, exponentiate = TRUE) ggcoef( reg2, exponentiate = TRUE, exclude_intercept = TRUE, errorbar_height = .2, color = "blue", sort = "ascending" )} } GGally/man/mapping_swap_x_y.Rd0000644000176200001440000000073213663637143016055 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{mapping_swap_x_y} \alias{mapping_swap_x_y} \title{Swap x and y mapping} \usage{ mapping_swap_x_y(mapping) } \arguments{ \item{mapping}{output of \code{ggplot2::\link[ggplot2]{aes}(...)}} } \value{ Aes mapping with the x and y values switched } \description{ Swap x and y mapping } \examples{ mapping <- ggplot2::aes(Petal.Length, Sepal.Width) mapping mapping_swap_x_y(mapping) } GGally/man/mapping_string.Rd0000644000176200001440000000062313663637143015531 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{mapping_string} \alias{mapping_string} \title{Aes name} \usage{ mapping_string(aes_col) } \arguments{ \item{aes_col}{Single value from \code{ggplot2::\link[ggplot2]{aes}(...)}} } \value{ character string } \description{ Aes name } \examples{ mapping <- ggplot2::aes(Petal.Length) mapping_string(mapping$x) } GGally/man/plotting_data_type.Rd0000644000176200001440000000045013663637143016400 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/find-combo.R \name{plotting_data_type} \alias{plotting_data_type} \title{Get plotting data type} \usage{ plotting_data_type(x) } \arguments{ \item{x}{vector} } \description{ Get plotting data type } \keyword{internal} GGally/man/ggnostic.Rd0000644000176200001440000001307213764714663014334 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnostic.R \name{ggnostic} \alias{ggnostic} \title{Plot matrix of statistical model diagnostics} \usage{ ggnostic( model, ..., columnsX = attr(data, "var_x"), columnsY = c(".resid", ".sigma", ".hat", ".cooksd"), columnLabelsX = attr(data, "var_x_label"), columnLabelsY = gsub("\\\\.", " ", gsub("^\\\\.", "", columnsY)), xlab = "explanatory variables", ylab = "diagnostics", title = paste(deparse(model$call, width.cutoff = 500L), collapse = "\\n"), continuous = list(default = ggally_points, .fitted = ggally_points, .se.fit = ggally_nostic_se_fit, .resid = ggally_nostic_resid, .hat = ggally_nostic_hat, .sigma = ggally_nostic_sigma, .cooksd = ggally_nostic_cooksd, .std.resid = ggally_nostic_std_resid), combo = list(default = ggally_box_no_facet, fitted = ggally_box_no_facet, .se.fit = ggally_nostic_se_fit, .resid = ggally_nostic_resid, .hat = ggally_nostic_hat, .sigma = ggally_nostic_sigma, .cooksd = ggally_nostic_cooksd, .std.resid = ggally_nostic_std_resid), discrete = list(default = ggally_ratio, .fitted = ggally_ratio, .se.fit = ggally_ratio, .resid = ggally_ratio, .hat = ggally_ratio, .sigma = ggally_ratio, .cooksd = ggally_ratio, .std.resid = ggally_ratio), progress = NULL, data = broomify(model) ) } \arguments{ \item{model}{statistical model object such as output from \code{stats::\link[stats]{lm}} or \code{stats::\link[stats]{glm}}} \item{...}{arguments passed directly to \code{\link{ggduo}}} \item{columnsX}{columns to be displayed in the plot matrix. Defaults to the predictor columns of the \code{model}} \item{columnsY}{rows to be displayed in the plot matrix. Defaults to residuals, leave one out sigma value, diagonal of the hat matrix, and Cook's Distance. The possible values are the response variables in the model and the added columns provided by \code{\link[broom:reexports]{broom::augment()}}. See details for more information.} \item{columnLabelsX, columnLabelsY}{column and row labels to display in the plot matrix} \item{xlab, ylab, title}{plot matrix labels passed directly to \code{\link{ggmatrix}}} \item{continuous, combo, discrete}{list of functions for each y variable. See details for more information.} \item{progress}{\code{NULL} (default) for a progress bar in interactive sessions with more than 15 plots, \code{TRUE} for a progress bar, \code{FALSE} for no progress bar, or a function that accepts at least a plot matrix and returns a new \code{progress::\link[progress]{progress_bar}}. See \code{\link{ggmatrix_progress}}.} \item{data}{data defaults to a 'broomify'ed model object. This object will contain information about the X variables, Y variables, and multiple broom outputs. See \code{\link{broomify}(model)} for more information} } \description{ Plot matrix of statistical model diagnostics } \section{\code{columnsY}}{ \code{\link[broom:reexports]{broom::augment()}} collects data from the supplied model and returns a data.frame with the following columns (taken directly from broom documentation). These columns are the only allowed values in the \code{columnsY} parameter to \code{\link{ggnostic}}. \describe{ \item{.resid}{Residuals} \item{.hat}{Diagonal of the hat matrix} \item{.sigma}{Estimate of residual standard deviation when corresponding observation is dropped from model} \item{.cooksd}{Cooks distance, \code{\link[stats:influence.measures]{stats::cooks.distance()}}} \item{.fitted}{Fitted values of model} \item{.se.fit}{Standard errors of fitted values} \item{.std.resid}{Standardized residuals} \item{response variable name}{The response variable in the model may be added. Such as \code{"mpg"} in the model \code{lm(mpg ~ ., data = mtcars)}} } } \section{\code{continuous}, \code{combo}, \code{discrete} types}{ Similar to \code{\link{ggduo}} and \code{\link{ggpairs}}, functions may be supplied to display the different column types. However, since the Y rows are fixed, each row has it's own corresponding function in each of the plot types: continuous, combo, and discrete. Each plot type list can have keys that correspond to the \code{\link[broom:reexports]{broom::augment()}} output: \code{".fitted"}, \code{".resid"}, \code{".std.resid"}, \code{".sigma"}, \code{".se.fit"}, \code{".hat"}, \code{".cooksd"}. An extra key, \code{"default"}, is used to plot the response variables of the model if they are included. Having a function for each diagnostic allows for very fine control over the diagnostics plot matrix. The functions for each type list are wrapped into a switch function that calls the function corresponding to the y variable being plotted. These switch functions are then passed directly to the \code{types} parameter in \code{\link{ggduo}}. } \examples{ # small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(mtcars) # use mtcars dataset and alter the 'am' column to display actual name values mtc <- mtcars mtc$am <- c("0" = "automatic", "1" = "manual")[as.character(mtc$am)] # step the complete model down to a smaller model mod <- stats::step(stats::lm(mpg ~ ., data = mtc), trace = FALSE) # display using defaults pm <- ggnostic(mod) p_(pm) # color by am value pm <- ggnostic(mod, mapping = ggplot2::aes(color = am)) p_(pm) # turn resid smooth error ribbon off pm <- ggnostic(mod, continuous = list(.resid = wrap("nostic_resid", se = FALSE))) p_(pm) ## plot residuals vs fitted in a ggpairs plot matrix dt <- broomify(mod) pm <- ggpairs( dt, c(".fitted", ".resid"), columnLabels = c("fitted", "residuals"), lower = list(continuous = ggally_nostic_resid) ) p_(pm) } GGally/man/ggally_nostic_sigma.Rd0000644000176200001440000000261013764714663016531 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnostic.R \name{ggally_nostic_sigma} \alias{ggally_nostic_sigma} \title{\code{\link{ggnostic}} leave one out model sigma} \usage{ ggally_nostic_sigma( data, mapping, ..., lineColor = brew_colors("grey"), linePosition = attr(data, "broom_glance")$sigma ) } \arguments{ \item{data, mapping, ..., lineColor}{parameters supplied to \code{\link{ggally_nostic_line}}} \item{linePosition}{line that is drawn in the background of the plot. Defaults to the overall model's sigma value.} } \value{ \pkg{ggplot2} plot object } \description{ A function to display \code{\link[stats:lm.influence]{stats::influence()}}'s sigma value. } \details{ As stated in \code{\link[stats:lm.influence]{stats::influence()}} documentation: sigma: a vector whose i-th element contains the estimate of the residual standard deviation obtained when the i-th case is dropped from the regression. (The approximations needed for GLMs can result in this being 'NaN'.) A line is added to display the overall model's sigma value. This gives a baseline for comparison } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive dt <- broomify(stats::lm(mpg ~ wt + qsec + am, data = mtcars)) p_(ggally_nostic_sigma(dt, ggplot2::aes(wt, .sigma))) } \seealso{ \code{\link[stats:lm.influence]{stats::influence()}} } GGally/man/nasa.Rd0000644000176200001440000000164513761572054013435 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/data-nasa.R \docType{data} \name{nasa} \alias{nasa} \title{Data from the Data Expo JSM 2006.} \format{ A data frame with 41472 rows and 17 variables } \usage{ data(nasa) } \description{ This data was provided by NASA for the competition. } \details{ The data shows 6 years of monthly measurements of a 24x24 spatial grid from Central America: \itemize{ \item time integer specifying temporal order of measurements \item x, y, lat, long spatial location of measurements. \item cloudhigh, cloudlow, cloudmid, ozone, pressure, surftemp, temperature are the various satellite measurements. \item date, day, month, year specifying the time of measurements. \item id unique ide for each spatial position. } } \references{ Murrell, P. (2010) The 2006 Data Expo of the American Statistical Association. Computational Statistics, 25:551-554. } \keyword{datasets} GGally/man/model_terms.Rd0000644000176200001440000000154713663637143015030 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnostic.R \name{model_response_variables} \alias{model_response_variables} \alias{model_beta_variables} \alias{model_beta_label} \title{Model term names} \usage{ model_response_variables(model, data = broom::augment(model)) model_beta_variables(model, data = broom::augment(model)) model_beta_label(model, data = broom::augment(model), lmStars = TRUE) } \arguments{ \item{model}{model in question} \item{data}{equivalent to \code{broom::augment(model)}} \item{lmStars}{boolean that determines if stars are added to labels} } \value{ character vector of names } \description{ Retrieve either the response variable names, the beta variable names, or beta variable names. If the model is an object of class 'lm', by default, the beta variable names will include anova significance stars. } GGally/man/singleClassOrder.Rd0000644000176200001440000000165513761572054015757 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggparcoord.R \name{singleClassOrder} \alias{singleClassOrder} \title{Order axis variables} \usage{ singleClassOrder(classVar, axisVars, specClass = NULL) } \arguments{ \item{classVar}{class variable (vector from original dataset)} \item{axisVars}{variables to be plotted as axes (data frame)} \item{specClass}{character string matching to level of \code{classVar}; instead of looking for separation between any class and the rest, will only look for separation between this class and the rest} } \value{ character vector of names of axisVars ordered such that the first variable has the most separation between one of the classes and the rest, and the last variable has the least (as measured by F-statistics from an ANOVA) } \description{ Order axis variables by separation between one class and the rest (most separation to least). } \author{ Jason Crowley } GGally/man/ggally_cor.Rd0000644000176200001440000000567513761572054014644 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_cor} \alias{ggally_cor} \title{Correlation value plot} \usage{ ggally_cor( data, mapping, ..., stars = TRUE, method = "pearson", use = "complete.obs", display_grid = FALSE, digits = 3, title_args = list(...), group_args = list(...), justify_labels = "right", align_percent = 0.5, title = "Corr", alignPercent = warning("deprecated. Use `align_percent`"), displayGrid = warning("deprecated. Use `display_grid`") ) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{other arguments being supplied to \code{\link[ggplot2]{geom_text}()} for the title and groups} \item{stars}{logical value which determines if the significance stars should be displayed. Given the \code{\link[stats]{cor.test}} p-values, display \describe{ \item{\code{"***"}}{if the p-value is \verb{< 0.001}} \item{\code{"**"}}{if the p-value is \verb{< 0.01}} \item{\code{"*"}}{if the p-value is \verb{< 0.05}} \item{\code{"."}}{if the p-value is \verb{< 0.10}} \item{\code{""}}{otherwise} }} \item{method}{\code{method} supplied to cor function} \item{use}{\code{use} supplied to \code{\link[stats]{cor}} function} \item{display_grid}{if \code{TRUE}, display aligned panel grid lines. If \code{FALSE} (default), display a thin panel border.} \item{digits}{number of digits to be displayed after the decimal point. See \code{\link[base]{formatC}} for how numbers are calculated.} \item{title_args}{arguments being supplied to the title's \code{\link[ggplot2]{geom_text}()}} \item{group_args}{arguments being supplied to the split-by-color group's \code{\link[ggplot2]{geom_text}()}} \item{justify_labels}{\code{justify} argument supplied when \code{\link[base]{format}}ting the labels} \item{align_percent}{relative align position of the text. When \code{justify_labels = 0.5}, this should not be needed to be set.} \item{title}{title text to be displayed} \item{alignPercent, displayGrid}{deprecated. Please use their snake-case counterparts.} } \description{ Estimate correlation from the given data. If a color variable is supplied, the correlation will also be calculated per group. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_cor(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip"))) # display with grid p_(ggally_cor( tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip"), display_grid = TRUE )) # change text attributes p_(ggally_cor( tips, mapping = ggplot2::aes(x = total_bill, y = tip), size = 15, colour = I("red"), title = "Correlation" )) # split by a variable p_(ggally_cor( tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip", color = "sex"), size = 5 )) } \seealso{ \code{\link{ggally_statistic}}, \code{\link{ggally_cor_v1_5}} } \author{ Barret Schloerke } \keyword{hplot} GGally/man/ggmatrix.Rd0000644000176200001440000001121513761572054014327 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggmatrix.R \name{ggmatrix} \alias{ggmatrix} \title{\pkg{ggplot2} plot matrix} \usage{ ggmatrix( plots, nrow, ncol, xAxisLabels = NULL, yAxisLabels = NULL, title = NULL, xlab = NULL, ylab = NULL, byrow = TRUE, showStrips = NULL, showAxisPlotLabels = TRUE, showXAxisPlotLabels = TRUE, showYAxisPlotLabels = TRUE, labeller = NULL, switch = NULL, xProportions = NULL, yProportions = NULL, progress = NULL, data = NULL, gg = NULL, legend = NULL ) } \arguments{ \item{plots}{list of plots to be put into matrix} \item{nrow, ncol}{number of rows and columns} \item{xAxisLabels, yAxisLabels}{strip titles for the x and y axis respectively. Set to \code{NULL} to not be displayed} \item{title, xlab, ylab}{title, x label, and y label for the graph. Set to \code{NULL} to not be displayed} \item{byrow}{boolean that determines whether the plots should be ordered by row or by column} \item{showStrips}{boolean to determine if each plot's strips should be displayed. \code{NULL} will default to the top and right side plots only. \code{TRUE} or \code{FALSE} will turn all strips on or off respectively.} \item{showAxisPlotLabels, showXAxisPlotLabels, showYAxisPlotLabels}{booleans that determine if the plots axis labels are printed on the X (bottom) or Y (left) part of the plot matrix. If \code{showAxisPlotLabels} is set, both \code{showXAxisPlotLabels} and \code{showYAxisPlotLabels} will be set to the given value.} \item{labeller}{labeller for facets. See \code{\link[ggplot2]{labellers}}. Common values are \code{"label_value"} (default) and \code{"label_parsed"}.} \item{switch}{switch parameter for facet_grid. See \code{ggplot2::\link[ggplot2]{facet_grid}}. By default, the labels are displayed on the top and right of the plot. If \code{"x"}, the top labels will be displayed to the bottom. If \code{"y"}, the right-hand side labels will be displayed to the left. Can also be set to \code{"both"}} \item{xProportions, yProportions}{Value to change how much area is given for each plot. Either \code{NULL} (default), numeric value matching respective length, or \code{grid::\link[grid]{unit}} object with matching respective length} \item{progress}{\code{NULL} (default) for a progress bar in interactive sessions with more than 15 plots, \code{TRUE} for a progress bar, \code{FALSE} for no progress bar, or a function that accepts at least a plot matrix and returns a new \code{progress::\link[progress]{progress_bar}}. See \code{\link{ggmatrix_progress}}.} \item{data}{data set using. This is the data to be used in place of 'ggally_data' if the plot is a string to be evaluated at print time} \item{gg}{\pkg{ggplot2} theme objects to be applied to every plot} \item{legend}{May be the two objects described below or the default \code{NULL} value. The legend position can be moved by using ggplot2's theme element \code{pm + theme(legend.position = "bottom")} \describe{\item{a numeric vector of length 2}{provides the location of the plot to use the legend for the plot matrix's legend. Such as \code{legend = c(3,5)} which will use the legend from the plot in the third row and fifth column}\item{a single numeric value}{provides the location of a plot according to the display order. Such as \code{legend = 3} in a plot matrix with 2 rows and 5 columns displayed by column will return the plot in position \code{c(1,2)}}\item{a object from \code{\link{grab_legend}()}}{a predetermined plot legend that will be displayed directly}}} } \description{ Make a generic matrix of \pkg{ggplot2} plots. } \section{Memory usage}{ Now that the \code{\link{print.ggmatrix}} method uses a large \pkg{gtable} object, rather than print each plot independently, memory usage may be of concern. From small tests, memory usage flutters around \code{object.size(data) * 0.3 * length(plots)}. So, for a 80Mb random noise dataset with 100 plots, about 2.4 Gb of memory needed to print. For the 3.46 Mb diamonds dataset with 100 plots, about 100 Mb of memory was needed to print. The benefits of using the \pkg{ggplot2} format greatly outweigh the price of about 20\% increase in memory usage from the prior ad-hoc print method. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive plotList <- list() for (i in 1:6) { plotList[[i]] <- ggally_text(paste("Plot #", i, sep = "")) } pm <- ggmatrix( plotList, 2, 3, c("A", "B", "C"), c("D", "E"), byrow = TRUE ) p_(pm) pm <- ggmatrix( plotList, 2, 3, xAxisLabels = c("A", "B", "C"), yAxisLabels = NULL, byrow = FALSE, showXAxisPlotLabels = FALSE ) p_(pm) } \author{ Barret Schloerke } \keyword{hplot} GGally/man/pigs.Rd0000644000176200001440000000222713761572054013452 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/data-pigs.R \docType{data} \name{pigs} \alias{pigs} \title{United Kingdom Pig Production} \format{ A data frame with 48 rows and 8 variables } \usage{ data(pigs) } \description{ This data contains about the United Kingdom Pig Production from the book 'Data' by Andrews and Herzberg. The original data can be on Statlib: http://lib.stat.cmu.edu/datasets/Andrews/T62.1 } \details{ The time variable has been added from a combination of year and quarter \itemize{ \item time year + (quarter - 1) / 4 \item year year of production \item quarter quarter of the year of production \item gilts number of sows giving birth for the first time \item profit ratio of price to an index of feed price \item s_per_herdsz ratio of the number of breeding pigs slaughtered to the total breeding herd size \item production number of pigs slaughtered that were reared for meat \item herdsz breeding herd size } } \references{ Andrews, David F., and Agnes M. Herzberg. Data: a collection of problems from many fields for the student and research worker. Springer Science & Business Media, 2012. } \keyword{datasets} GGally/man/ggnet2.Rd0000644000176200001440000003270213777103031013667 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnet2.R \name{ggnet2} \alias{ggnet2} \title{Network plot} \usage{ ggnet2( net, mode = "fruchtermanreingold", layout.par = NULL, layout.exp = 0, alpha = 1, color = "grey75", shape = 19, size = 9, max_size = 9, na.rm = NA, palette = NULL, alpha.palette = NULL, alpha.legend = NA, color.palette = palette, color.legend = NA, shape.palette = NULL, shape.legend = NA, size.palette = NULL, size.legend = NA, size.zero = FALSE, size.cut = FALSE, size.min = NA, size.max = NA, label = FALSE, label.alpha = 1, label.color = "black", label.size = max_size/2, label.trim = FALSE, node.alpha = alpha, node.color = color, node.label = label, node.shape = shape, node.size = size, edge.alpha = 1, edge.color = "grey50", edge.lty = "solid", edge.size = 0.25, edge.label = NULL, edge.label.alpha = 1, edge.label.color = label.color, edge.label.fill = "white", edge.label.size = max_size/2, arrow.size = 0, arrow.gap = 0, arrow.type = "closed", legend.size = 9, legend.position = "right", ... ) } \arguments{ \item{net}{an object of class \code{\link[network]{network}}, or any object that can be coerced to this class, such as an adjacency or incidence matrix, or an edge list: see \link[network]{edgeset.constructors} and \link[network]{network} for details. If the object is of class \link[igraph:aaa-igraph-package]{igraph} and the \link[intergraph:intergraph-package]{intergraph} package is installed, it will be used to convert the object: see \code{\link[intergraph]{asNetwork}} for details.} \item{mode}{a placement method from those provided in the \code{\link[sna]{sna}} package: see \link[sna:gplot.layout]{gplot.layout} for details. Also accepts the names of two numeric vertex attributes of \code{net}, or a matrix of numeric coordinates, in which case the first two columns of the matrix are used. Defaults to the Fruchterman-Reingold force-directed algorithm.} \item{layout.par}{options to be passed to the placement method, as listed in \link[sna]{gplot.layout}. Defaults to \code{NULL}.} \item{layout.exp}{a multiplier to expand the horizontal axis if node labels get clipped: see \link[scales]{expand_range} for details. Defaults to \code{0} (no expansion).} \item{alpha}{the level of transparency of the edges and nodes, which might be a single value, a vertex attribute, or a vector of values. Also accepts \code{"mode"} on bipartite networks (see 'Details'). Defaults to \code{1} (no transparency).} \item{color}{the color of the nodes, which might be a single value, a vertex attribute, or a vector of values. Also accepts \code{"mode"} on bipartite networks (see 'Details'). Defaults to \code{grey75}.} \item{shape}{the shape of the nodes, which might be a single value, a vertex attribute, or a vector of values. Also accepts \code{"mode"} on bipartite networks (see 'Details'). Defaults to \code{19} (solid circle).} \item{size}{the size of the nodes, in points, which might be a single value, a vertex attribute, or a vector of values. Also accepts \code{"indegree"}, \code{"outdegree"}, \code{"degree"} or \code{"freeman"} to size the nodes by their unweighted degree centrality (\code{"degree"} and \code{"freeman"} are equivalent): see \code{\link[sna]{degree}} for details. All node sizes must be strictly positive. Also accepts \code{"mode"} on bipartite networks (see 'Details'). Defaults to \code{9}.} \item{max_size}{the \emph{maximum} size of the node when \code{size} produces nodes of different sizes, in points. Defaults to \code{9}.} \item{na.rm}{whether to subset the network to nodes that are \emph{not} missing a given vertex attribute. If set to any vertex attribute of \code{net}, the nodes for which this attribute is \code{NA} will be removed. Defaults to \code{NA} (does nothing).} \item{palette}{the palette to color the nodes, when \code{color} is not a color value or a vector of color values. Accepts named vectors of color values, or if \link[RColorBrewer:ColorBrewer]{RColorBrewer} is installed, any ColorBrewer palette name: see \code{\link[RColorBrewer:ColorBrewer]{RColorBrewer::brewer.pal()}} and \url{https://colorbrewer2.org/} for details. Defaults to \code{NULL}, which will create an array of grayscale color values if \code{color} is not a color value or a vector of color values.} \item{alpha.palette}{the palette to control the transparency levels of the nodes set by \code{alpha} when the levels are not numeric values. Defaults to \code{NULL}, which will create an array of alpha transparency values if \code{alpha} is not a numeric value or a vector of numeric values.} \item{alpha.legend}{the name to assign to the legend created by \code{alpha} when its levels are not numeric values. Defaults to \code{NA} (no name).} \item{color.palette}{see \code{palette}} \item{color.legend}{the name to assign to the legend created by \code{palette}. Defaults to \code{NA} (no name).} \item{shape.palette}{the palette to control the shapes of the nodes set by \code{shape} when the shapes are not numeric values. Defaults to \code{NULL}, which will create an array of shape values if \code{shape} is not a numeric value or a vector of numeric values.} \item{shape.legend}{the name to assign to the legend created by \code{shape} when its levels are not numeric values. Defaults to \code{NA} (no name).} \item{size.palette}{the palette to control the sizes of the nodes set by \code{size} when the sizes are not numeric values.} \item{size.legend}{the name to assign to the legend created by \code{size}. Defaults to \code{NA} (no name).} \item{size.zero}{whether to accept zero-sized nodes based on the value(s) of \code{size}. Defaults to \code{FALSE}, which ensures that zero-sized nodes are still shown in the plot and its size legend.} \item{size.cut}{whether to cut the size of the nodes into a certain number of quantiles. Accepts \code{TRUE}, which tries to cut the sizes into quartiles, or any positive numeric value, which tries to cut the sizes into that many quantiles. If the size of the nodes do not contain the specified number of distinct quantiles, the largest possible number is used. See \code{\link[stats]{quantile}} and \code{\link[base]{cut}} for details. Defaults to \code{FALSE} (does nothing).} \item{size.min}{whether to subset the network to nodes with a minimum size, based on the values of \code{size}. Defaults to \code{NA} (preserves all nodes).} \item{size.max}{whether to subset the network to nodes with a maximum size, based on the values of \code{size}. Defaults to \code{NA} (preserves all nodes).} \item{label}{whether to label the nodes. If set to \code{TRUE}, nodes are labeled with their vertex names. If set to a vector that contains as many elements as there are nodes in \code{net}, nodes are labeled with these. If set to any other vector of values, the nodes are labeled only when their vertex name matches one of these values. Defaults to \code{FALSE} (no labels).} \item{label.alpha}{the level of transparency of the node labels, as a numeric value, a vector of numeric values, or as a vertex attribute containing numeric values. Defaults to \code{1} (no transparency).} \item{label.color}{the color of the node labels, as a color value, a vector of color values, or as a vertex attribute containing color values. Defaults to \code{"black"}.} \item{label.size}{the size of the node labels, in points, as a numeric value, a vector of numeric values, or as a vertex attribute containing numeric values. Defaults to \code{max_size / 2} (half the maximum node size), which defaults to \code{4.5}.} \item{label.trim}{whether to apply some trimming to the node labels. Accepts any function that can process a character vector, or a strictly positive numeric value, in which case the labels are trimmed to a fixed-length substring of that length: see \code{\link[base]{substr}} for details. Defaults to \code{FALSE} (does nothing).} \item{node.alpha}{see \code{alpha}} \item{node.color}{see \code{color}} \item{node.label}{see \code{label}} \item{node.shape}{see \code{shape}} \item{node.size}{see \code{size}} \item{edge.alpha}{the level of transparency of the edges. Defaults to the value of \code{alpha}, which defaults to \code{1}.} \item{edge.color}{the color of the edges, as a color value, a vector of color values, or as an edge attribute containing color values. Defaults to \code{"grey50"}.} \item{edge.lty}{the linetype of the edges, as a linetype value, a vector of linetype values, or as an edge attribute containing linetype values. Defaults to \code{"solid"}.} \item{edge.size}{the size of the edges, in points, as a numeric value, a vector of numeric values, or as an edge attribute containing numeric values. All edge sizes must be strictly positive. Defaults to \code{0.25}.} \item{edge.label}{the labels to plot at the middle of the edges, as a single value, a vector of values, or as an edge attribute. Defaults to \code{NULL} (no edge labels).} \item{edge.label.alpha}{the level of transparency of the edge labels, as a numeric value, a vector of numeric values, or as an edge attribute containing numeric values. Defaults to \code{1} (no transparency).} \item{edge.label.color}{the color of the edge labels, as a color value, a vector of color values, or as an edge attribute containing color values. Defaults to \code{label.color}, which defaults to \code{"black"}.} \item{edge.label.fill}{the background color of the edge labels. Defaults to \code{"white"}.} \item{edge.label.size}{the size of the edge labels, in points, as a numeric value, a vector of numeric values, or as an edge attribute containing numeric values. All edge label sizes must be strictly positive. Defaults to \code{max_size / 2} (half the maximum node size), which defaults to \code{4.5}.} \item{arrow.size}{the size of the arrows for directed network edges, in points. See \code{\link[grid]{arrow}} for details. Defaults to \code{0} (no arrows).} \item{arrow.gap}{a setting aimed at improving the display of edge arrows by plotting slightly shorter edges. Accepts any value between \code{0} and \code{1}, where a value of \code{0.05} will generally achieve good results when the size of the nodes is reasonably small. Defaults to \code{0} (no shortening).} \item{arrow.type}{the type of the arrows for directed network edges. See \code{\link[grid]{arrow}} for details. Defaults to \code{"closed"}.} \item{legend.size}{the size of the legend symbols and text, in points. Defaults to \code{9}.} \item{legend.position}{the location of the plot legend(s). Accepts all \code{legend.position} values supported by \code{\link[ggplot2]{theme}}. Defaults to \code{"right"}.} \item{...}{other arguments passed to the \code{geom_text} object that sets the node labels: see \code{\link[ggplot2]{geom_text}} for details.} } \description{ Function for plotting network objects using \pkg{ggplot2}, with additional control over graphical parameters that are not supported by the \code{\link{ggnet}} function. Please visit \url{https://github.com/briatte/ggnet} for the latest version of ggnet2, and \url{https://briatte.github.io/ggnet/} for a vignette that contains many examples and explanations. } \details{ The degree centrality measures that can be produced through the \code{size} argument will take the directedness of the network into account, but will be unweighted. To compute weighted network measures, see the \code{tnet} package by Tore Opsahl (\code{help("tnet", package = "tnet")}). The nodes of bipartite networks can be mapped to their mode by passing the \code{"mode"} argument to any of \code{alpha}, \code{color}, \code{shape} and \code{size}, in which case the nodes of the primary mode will be mapped as \code{"actor"}, and the nodes of the secondary mode will be mapped as \code{"event"}. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive library(network) # random adjacency matrix x <- 10 ndyads <- x * (x - 1) density <- x / ndyads m <- matrix(0, nrow = x, ncol = x) dimnames(m) <- list(letters[ 1:x ], letters[ 1:x ]) m[ row(m) != col(m) ] <- runif(ndyads) < density m # random undirected network n <- network::network(m, directed = FALSE) n p_(ggnet2(n, label = TRUE)) p_(ggnet2(n, label = TRUE, shape = 15)) p_(ggnet2(n, label = TRUE, shape = 15, color = "black", label.color = "white")) # add vertex attribute x = network.vertex.names(n) x = ifelse(x \%in\% c("a", "e", "i"), "vowel", "consonant") n \%v\% "phono" = x p_(ggnet2(n, color = "phono")) p_(ggnet2(n, color = "phono", palette = c("vowel" = "gold", "consonant" = "grey"))) p_(ggnet2(n, shape = "phono", color = "phono")) if (require(RColorBrewer)) { # random groups n \%v\% "group" <- sample(LETTERS[1:3], 10, replace = TRUE) p_(ggnet2(n, color = "group", palette = "Set2")) } # random weights n \%e\% "weight" <- sample(1:3, network.edgecount(n), replace = TRUE) p_(ggnet2(n, edge.size = "weight", edge.label = "weight")) # edge arrows on a directed network p_(ggnet2(network(m, directed = TRUE), arrow.gap = 0.05, arrow.size = 10)) # Padgett's Florentine wedding data data(flo, package = "network") flo p_(ggnet2(flo, label = TRUE)) p_(ggnet2(flo, label = TRUE, label.trim = 4, vjust = -1, size = 3, color = 1)) p_(ggnet2(flo, label = TRUE, size = 12, color = "white")) } \seealso{ \code{\link{ggnet}} in this package, \code{\link[sna]{gplot}} in the \code{\link[sna]{sna}} package, and \code{\link[network]{plot.network}} in the \code{\link[network]{network}} package } \author{ Moritz Marbach and Francois Briatte, with help from Heike Hofmann, Pedro Jordano and Ming-Yu Liu } GGally/man/ggally_nostic_line.Rd0000644000176200001440000000263113764714663016363 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnostic.R \name{ggally_nostic_line} \alias{ggally_nostic_line} \title{\code{\link{ggnostic}} background line with geom} \usage{ ggally_nostic_line( data, mapping, ..., linePosition = NULL, lineColor = "red", lineSize = 0.5, lineAlpha = 1, lineType = 1, continuous_geom = ggplot2::geom_point, combo_geom = ggplot2::geom_boxplot, mapColorToFill = TRUE ) } \arguments{ \item{data, mapping}{supplied directly to \code{\link[ggplot2:ggplot]{ggplot2::ggplot()}}} \item{...}{parameters supplied to \code{continuous_geom} or \code{combo_geom}} \item{linePosition, lineColor, lineSize, lineAlpha, lineType}{parameters supplied to \code{\link[ggplot2:geom_path]{ggplot2::geom_line()}}} \item{continuous_geom}{\pkg{ggplot2} geom that is executed after the line is (possibly) added and if the x data is continuous} \item{combo_geom}{\pkg{ggplot2} geom that is executed after the line is (possibly) added and if the x data is discrete} \item{mapColorToFill}{boolean to determine if combo plots should cut the color mapping to the fill mapping} } \value{ \pkg{ggplot2} plot object } \description{ If a non-null \code{linePosition} value is given, a line will be drawn before the given \code{continuous_geom} or \code{combo_geom} is added to the plot. } \details{ Functions with a color in their name have different default color behavior. } GGally/man/add_and_overwrite_aes.Rd0000644000176200001440000000134113665760216017016 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggpairs.R \name{add_and_overwrite_aes} \alias{add_and_overwrite_aes} \title{Add new aes} \usage{ add_and_overwrite_aes(current, new) } \value{ aes_ output } \description{ Add new aesthetics to a previous aes. } \examples{ data(diamonds, package="ggplot2") diamonds.samp <- diamonds[sample(1:dim(diamonds)[1], 1000), ] pm <- ggpairs(diamonds.samp, columns = 5:7, mapping = ggplot2::aes(color = color), upper = list(continuous = "cor", mapping = ggplot2::aes_string(color = "clarity")), lower = list(continuous = "cor", mapping = ggplot2::aes_string(color = "cut")), title = "Diamonds Sample" ) str(pm) } \author{ Barret Schloerke } \keyword{internal} GGally/man/gg-add.Rd0000644000176200001440000000625113764714663013643 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggpairs_add.R \name{+.gg} \alias{+.gg} \alias{add_to_ggmatrix} \title{Modify a \code{\link{ggmatrix}} object by adding an \pkg{ggplot2} object to all plots} \usage{ \method{+}{gg}(e1, e2) add_to_ggmatrix(e1, e2, location = NULL, rows = NULL, cols = NULL) } \arguments{ \item{e1}{An object of class \code{\link{ggnostic}} or \code{ggplot}} \item{e2}{A component to add to \code{e1}} \item{location}{\describe{ \item{\code{"all"}, \code{TRUE}}{All row and col combinations} \item{\code{"none"}}{No row and column combinations} \item{\code{"upper"}}{Locations where the column value is higher than the row value} \item{\code{"lower"}}{Locations where the row value is higher than the column value} \item{\code{"diag"}}{Locations where the column value is equal to the row value} \item{\code{matrix} or \code{data.frame}}{ \code{matrix} values will be converted into \code{data.frame}s. \itemize{ \item A \code{data.frame} with the exact column names \code{c("row", "col")} \item A \code{data.frame} with the number of rows and columns matching the plot matrix object provided. Each cell will be tested for a "truthy" value to determine if the location should be kept. } } }} \item{rows}{numeric vector of the rows to be used. Will be used with \code{cols} if \code{location} is \code{NULL}} \item{cols}{numeric vector of the cols to be used. Will be used with \code{rows} if \code{location} is \code{NULL}} } \description{ This operator allows you to add \pkg{ggplot2} objects to a \code{\link{ggmatrix}} object. } \details{ If the first object is an object of class \code{\link{ggmatrix}}, you can add the following types of objects, and it will return a modified \pkg{ggplot2} object. \itemize{ \item \code{theme}: update plot theme \item \code{scale}: replace current scale \item \code{coord}: override current coordinate system } The \code{+} operator completely replaces elements with elements from e2. \code{add_to_ggmatrix} gives you more control to modify only some subplots. This function may be replaced and/or removed in the future. \Sexpr[results=rd, stage=render]{lifecycle::badge("experimental")} } \examples{ # small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") pm <- ggpairs(tips[, 2:4], ggplot2::aes(color = sex)) ## change to black and white theme pm + ggplot2::theme_bw() ## change to linedraw theme p_(pm + ggplot2::theme_linedraw()) ## change to custom theme p_(pm + ggplot2::theme(panel.background = ggplot2::element_rect(fill = "lightblue"))) ## add a list of information extra <- list(ggplot2::theme_bw(), ggplot2::labs(caption = "My caption!")) p_(pm + extra) ## modify scale p_(pm + scale_fill_brewer(type = "qual")) ## only first row p_(add_to_ggmatrix(pm, scale_fill_brewer(type = "qual"), rows = 1:2)) ## only second col p_(add_to_ggmatrix(pm, scale_fill_brewer(type = "qual"), cols = 2:3)) ## only to upper triangle of plot matrix p_(add_to_ggmatrix( pm, scale_fill_brewer(type = "qual"), location = "upper" )) } \seealso{ \link[ggplot2:gg-add]{ggplot2::+.gg} and \code{\link[ggplot2:theme]{ggplot2::theme()}} \code{\link{ggmatrix_location}} } GGally/man/ggally_nostic_std_resid.Rd0000644000176200001440000000174613764714663017422 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnostic.R \name{ggally_nostic_std_resid} \alias{ggally_nostic_std_resid} \title{\code{\link{ggnostic}} standardized residuals} \usage{ ggally_nostic_std_resid(data, mapping, ..., sigma = 1) } \arguments{ \item{data, mapping, ...}{parameters supplied to \code{\link{ggally_nostic_resid}}} \item{sigma}{sigma value for the \code{pVal} percentiles. Set to 1 for standardized residuals} } \value{ \pkg{ggplot2} plot object } \description{ If non-null \code{pVal} and \code{sigma} values are given, confidence interval lines will be added to the plot at the specified \code{pVal} locations of a N(0, 1) distribution. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive dt <- broomify(stats::lm(mpg ~ wt + qsec + am, data = mtcars)) p_(ggally_nostic_std_resid(dt, ggplot2::aes(wt, .std.resid))) } \seealso{ \code{\link[stats:influence.measures]{stats::rstandard()}} } GGally/man/mapping_color_to_fill.Rd0000644000176200001440000000053313665760216017051 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggpairs.R \name{mapping_color_to_fill} \alias{mapping_color_to_fill} \title{Aesthetic mapping color fill} \usage{ mapping_color_to_fill(current) } \arguments{ \item{current}{the current aesthetics} } \description{ Replace the fill with the color and make color NULL. } GGally/man/ggally_count.Rd0000644000176200001440000000273413666472400015200 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \docType{data} \name{ggally_count} \alias{ggally_count} \alias{stat_ggally_count} \alias{StatGGallyCount} \alias{ggally_countDiag} \title{Display counts of observations} \usage{ ggally_count(data, mapping, ...) ggally_countDiag(data, mapping, ...) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{other arguments passed to \code{\link[ggplot2]{geom_tile}(...)}} } \description{ Plot the number of observations by using rectangles with proportional areas. } \details{ You can adjust the size of rectangles with the \code{x.width} argument. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_count(tips, mapping = ggplot2::aes(x = smoker, y = sex))) p_(ggally_count(tips, mapping = ggplot2::aes(x = smoker, y = sex, fill = day))) p_(ggally_count( as.data.frame(Titanic), mapping = ggplot2::aes(x = Class, y = Survived, weight = Freq) )) p_(ggally_count( as.data.frame(Titanic), mapping = ggplot2::aes(x = Class, y = Survived, weight = Freq), x.width = 0.5 )) # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive p_(ggally_countDiag(tips, mapping = ggplot2::aes(x = smoker))) p_(ggally_countDiag(tips, mapping = ggplot2::aes(x = smoker, fill = sex))) } \author{ Joseph Larmarange } \keyword{datasets} \keyword{hplot} GGally/man/ggally_facethist.Rd0000644000176200001440000000137213666472400016017 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_facethist} \alias{ggally_facethist} \title{Faceted histogram} \usage{ ggally_facethist(data, mapping, ...) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{parameters sent to stat_bin()} } \description{ Display subsetted histograms of the data in different panels. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_facethist(tips, mapping = ggplot2::aes(x = tip, y = sex))) p_(ggally_facethist(tips, mapping = ggplot2::aes_string(x = "tip", y = "sex"), binwidth = 0.1)) } \author{ Barret Schloerke } \keyword{hplot} GGally/man/geom_stripped_rows.Rd0000644000176200001440000000757113764714663016441 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/geom_stripped_rows.R \name{geom_stripped_rows} \alias{geom_stripped_rows} \alias{geom_stripped_cols} \title{Alternating Background Colour} \usage{ geom_stripped_rows( mapping = NULL, data = NULL, stat = "identity", position = "identity", ..., show.legend = NA, inherit.aes = TRUE, xfrom = -Inf, xto = Inf, width = 1, nudge_y = 0 ) geom_stripped_cols( mapping = NULL, data = NULL, stat = "identity", position = "identity", ..., show.legend = NA, inherit.aes = TRUE, yfrom = -Inf, yto = Inf, width = 1, nudge_x = 0 ) } \arguments{ \item{mapping}{Set of aesthetic mappings created by \code{\link[ggplot2:aes]{aes()}} or \code{\link[ggplot2:aes_]{aes_()}}. If specified and \code{inherit.aes = TRUE} (the default), it is combined with the default mapping at the top level of the plot. You must supply \code{mapping} if there is no plot mapping.} \item{data}{The data to be displayed in this layer. There are three options: If \code{NULL}, the default, the data is inherited from the plot data as specified in the call to \code{\link[ggplot2:ggplot]{ggplot()}}. A \code{data.frame}, or other object, will override the plot data. All objects will be fortified to produce a data frame. See \code{\link[ggplot2:fortify]{fortify()}} for which variables will be created. A \code{function} will be called with a single argument, the plot data. The return value must be a \code{data.frame}, and will be used as the layer data. A \code{function} can be created from a \code{formula} (e.g. \code{~ head(.x, 10)}).} \item{stat}{The statistical transformation to use on the data for this layer, as a string.} \item{position}{Position adjustment, either as a string, or the result of a call to a position adjustment function.} \item{...}{Other arguments passed on to \code{\link[ggplot2:layer]{layer()}}. These are often aesthetics, used to set an aesthetic to a fixed value, like \code{colour = "red"} or \code{size = 3}. They may also be parameters to the paired geom/stat.} \item{show.legend}{logical. Should this layer be included in the legends? \code{NA}, the default, includes if any aesthetics are mapped. \code{FALSE} never includes, and \code{TRUE} always includes. It can also be a named logical vector to finely select the aesthetics to display.} \item{inherit.aes}{If \code{FALSE}, overrides the default aesthetics, rather than combining with them. This is most useful for helper functions that define both data and aesthetics and shouldn't inherit behaviour from the default plot specification, e.g. \code{\link[ggplot2:borders]{borders()}}.} \item{xfrom, xto}{limitation of the strips along the x-axis} \item{width}{width of the strips} \item{yfrom, yto}{limitation of the strips along the y-axis} \item{nudge_x, nudge_y}{horizontal or vertical adjustment to nudge strips by} } \description{ Add alternating background color along the y-axis. The geom takes default aesthetics \code{odd} and \code{even} that receive color codes. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p <- ggplot(tips) + aes(x = time, y = day) + geom_count() + theme_light() p_(p) p_(p + geom_stripped_rows()) p_(p + geom_stripped_cols()) p_(p + geom_stripped_rows() + geom_stripped_cols()) p <- ggplot(tips) + aes(x = total_bill, y = day) + geom_count() + theme_light() p p_(p + geom_stripped_rows()) p_(p + geom_stripped_rows() + scale_y_discrete(expand = expansion(0, 0.5))) p_(p + geom_stripped_rows(xfrom = 10, xto = 35)) p_(p + geom_stripped_rows(odd = "blue", even = "yellow")) p_(p + geom_stripped_rows(odd = "blue", even = "yellow", alpha = .1)) p_(p + geom_stripped_rows(odd = "#00FF0022", even = "#FF000022")) p_(p + geom_stripped_cols()) p_(p + geom_stripped_cols(width = 10)) p_(p + geom_stripped_cols(width = 10, nudge_x = 5)) } GGally/man/pipe.Rd0000644000176200001440000000041113777103031013426 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/GGally-package.R \name{\%>\%} \alias{\%>\%} \title{Pipe operator} \usage{ lhs \%>\% rhs } \description{ See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. } \keyword{internal} GGally/man/glyphplot.Rd0000644000176200001440000000211313665760216014526 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gglyph.R \name{glyphplot} \alias{glyphplot} \alias{is.glyphplot} \alias{[.glyphplot} \alias{print.glyphplot} \title{Glyph plot class} \usage{ glyphplot(data, width, height, polar, x_major, y_major) is.glyphplot(x) \method{[}{glyphplot}(x, ...) \method{print}{glyphplot}(x, ...) } \arguments{ \item{data}{A data frame containing variables named in \code{x_major}, \code{x_minor}, \code{y_major} and \code{y_minor}.} \item{height, width}{The height and width of each glyph. Defaults to 95\% of the \code{\link[ggplot2]{resolution}} of the data. Specify the width absolutely by supplying a numeric vector of length 1, or relative to the} \item{polar}{A logical of length 1, specifying whether the glyphs should be drawn in polar coordinates. Defaults to \code{FALSE}.} \item{x_major, y_major}{The name of the variable (as a string) for the major x and y axes. Together, the} \item{x}{glyphplot to be printed} \item{...}{ignored} } \description{ Glyph plot class } \author{ Di Cook, Heike Hofmann, Hadley Wickham } GGally/man/str.ggmatrix.Rd0000644000176200001440000000116413665760216015142 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggpairs_internal_plots.R \name{str.ggmatrix} \alias{str.ggmatrix} \title{\code{\link{ggmatrix}} structure} \usage{ \method{str}{ggmatrix}(object, ..., raw = FALSE) } \arguments{ \item{object}{\code{\link{ggmatrix}} object to be viewed} \item{...}{passed on to the default \code{str} method} \item{raw}{boolean to determine if the plots should be converted to text or kept as original objects} } \description{ View the condensed version of the \code{\link{ggmatrix}} object. The attribute "class" is ALWAYS altered to "_class" to avoid recursion. } GGally/man/ggduo.Rd0000644000176200001440000002265413761572054013623 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggpairs.R \name{ggduo} \alias{ggduo} \title{\pkg{ggplot2} generalized pairs plot for two columns sets of data} \usage{ ggduo( data, mapping = NULL, columnsX = 1:ncol(data), columnsY = 1:ncol(data), title = NULL, types = list(continuous = "smooth_loess", comboVertical = "box_no_facet", comboHorizontal = "facethist", discrete = "count"), axisLabels = c("show", "none"), columnLabelsX = colnames(data[columnsX]), columnLabelsY = colnames(data[columnsY]), labeller = "label_value", switch = NULL, xlab = NULL, ylab = NULL, showStrips = NULL, legend = NULL, cardinality_threshold = 15, progress = NULL, xProportions = NULL, yProportions = NULL, legends = stop("deprecated") ) } \arguments{ \item{data}{data set using. Can have both numerical and categorical data.} \item{mapping}{aesthetic mapping (besides \code{x} and \code{y}). See \code{\link[ggplot2]{aes}()}. If \code{mapping} is numeric, \code{columns} will be set to the \code{mapping} value and \code{mapping} will be set to \code{NULL}.} \item{columnsX, columnsY}{which columns are used to make plots. Defaults to all columns.} \item{title, xlab, ylab}{title, x label, and y label for the graph} \item{types}{see Details} \item{axisLabels}{either "show" to display axisLabels or "none" for no axis labels} \item{columnLabelsX, columnLabelsY}{label names to be displayed. Defaults to names of columns being used.} \item{labeller}{labeller for facets. See \code{\link[ggplot2]{labellers}}. Common values are \code{"label_value"} (default) and \code{"label_parsed"}.} \item{switch}{switch parameter for facet_grid. See \code{ggplot2::\link[ggplot2]{facet_grid}}. By default, the labels are displayed on the top and right of the plot. If \code{"x"}, the top labels will be displayed to the bottom. If \code{"y"}, the right-hand side labels will be displayed to the left. Can also be set to \code{"both"}} \item{showStrips}{boolean to determine if each plot's strips should be displayed. \code{NULL} will default to the top and right side plots only. \code{TRUE} or \code{FALSE} will turn all strips on or off respectively.} \item{legend}{May be the two objects described below or the default \code{NULL} value. The legend position can be moved by using ggplot2's theme element \code{pm + theme(legend.position = "bottom")} \describe{\item{a numeric vector of length 2}{provides the location of the plot to use the legend for the plot matrix's legend. Such as \code{legend = c(3,5)} which will use the legend from the plot in the third row and fifth column}\item{a single numeric value}{provides the location of a plot according to the display order. Such as \code{legend = 3} in a plot matrix with 2 rows and 5 columns displayed by column will return the plot in position \code{c(1,2)}}\item{a object from \code{\link{grab_legend}()}}{a predetermined plot legend that will be displayed directly}}} \item{cardinality_threshold}{maximum number of levels allowed in a character / factor column. Set this value to NULL to not check factor columns. Defaults to 15} \item{progress}{\code{NULL} (default) for a progress bar in interactive sessions with more than 15 plots, \code{TRUE} for a progress bar, \code{FALSE} for no progress bar, or a function that accepts at least a plot matrix and returns a new \code{progress::\link[progress]{progress_bar}}. See \code{\link{ggmatrix_progress}}.} \item{xProportions, yProportions}{Value to change how much area is given for each plot. Either \code{NULL} (default), numeric value matching respective length, \code{grid::\link[grid]{unit}} object with matching respective length or \code{"auto"} for automatic relative proportions based on the number of levels for categorical variables.} \item{legends}{deprecated} } \description{ Make a matrix of plots with a given data set with two different column sets } \details{ \code{types} is a list that may contain the variables 'continuous', 'combo', 'discrete', and 'na'. Each element of the list may be a function or a string. If a string is supplied, If a string is supplied, it must be a character string representing the tail end of a \code{ggally_NAME} function. The list of current valid \code{ggally_NAME} functions is visible in a dedicated vignette. \describe{ \item{continuous}{This option is used for continuous X and Y data.} \item{comboHorizontal}{This option is used for either continuous X and categorical Y data or categorical X and continuous Y data.} \item{comboVertical}{This option is used for either continuous X and categorical Y data or categorical X and continuous Y data.} \item{discrete}{This option is used for categorical X and Y data.} \item{na}{This option is used when all X data is \code{NA}, all Y data is \code{NA}, or either all X or Y data is \code{NA}.} } If 'blank' is ever chosen as an option, then ggduo will produce an empty plot. If a function is supplied as an option, it should implement the function api of \code{function(data, mapping, ...){#make ggplot2 plot}}. If a specific function needs its parameters set, \code{\link{wrap}(fn, param1 = val1, param2 = val2)} the function with its parameters. } \examples{ # small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(baseball, package = "plyr") # Keep players from 1990-1995 with at least one at bat # Add how many singles a player hit # (must do in two steps as X1b is used in calculations) dt <- transform( subset(baseball, year >= 1990 & year <= 1995 & ab > 0), X1b = h - X2b - X3b - hr ) # Add # the player's batting average, # the player's slugging percentage, # and the player's on base percentage # Make factor a year, as each season is discrete dt <- transform( dt, batting_avg = h / ab, slug = (X1b + 2*X2b + 3*X3b + 4*hr) / ab, on_base = (h + bb + hbp) / (ab + bb + hbp), year = as.factor(year) ) pm <- ggduo( dt, c("year", "g", "ab", "lg"), c("batting_avg", "slug", "on_base"), mapping = ggplot2::aes(color = lg) ) # Prints, but # there is severe over plotting in the continuous plots # the labels could be better # want to add more hitting information p_(pm) # address overplotting issues and add a title pm <- ggduo( dt, c("year", "g", "ab", "lg"), c("batting_avg", "slug", "on_base"), columnLabelsX = c("year", "player game count", "player at bat count", "league"), columnLabelsY = c("batting avg", "slug \%", "on base \%"), title = "Baseball Hitting Stats from 1990-1995", mapping = ggplot2::aes(color = lg), types = list( # change the shape and add some transparency to the points continuous = wrap("smooth_loess", alpha = 0.50, shape = "+") ), showStrips = FALSE ) p_(pm) # Use "auto" to adapt width of the sub-plots pm <- ggduo( dt, c("year", "g", "ab", "lg"), c("batting_avg", "slug", "on_base"), mapping = ggplot2::aes(color = lg), xProportions = "auto" ) p_(pm) # Custom widths & heights of the sub-plots pm <- ggduo( dt, c("year", "g", "ab", "lg"), c("batting_avg", "slug", "on_base"), mapping = ggplot2::aes(color = lg), xProportions = c(6, 4, 3, 2), yProportions = c(1, 2, 1) ) p_(pm) # Example derived from: ## R Data Analysis Examples | Canonical Correlation Analysis. UCLA: Institute for Digital ## Research and Education. ## from http://www.stats.idre.ucla.edu/r/dae/canonical-correlation-analysis ## (accessed May 22, 2017). # "Example 1. A researcher has collected data on three psychological variables, four # academic variables (standardized test scores) and gender for 600 college freshman. # She is interested in how the set of psychological variables relates to the academic # variables and gender. In particular, the researcher is interested in how many # dimensions (canonical variables) are necessary to understand the association between # the two sets of variables." data(psychademic) summary(psychademic) (psych_variables <- attr(psychademic, "psychology")) (academic_variables <- attr(psychademic, "academic")) ## Within correlation p_(ggpairs(psychademic, columns = psych_variables)) p_(ggpairs(psychademic, columns = academic_variables)) ## Between correlation loess_with_cor <- function(data, mapping, ..., method = "pearson") { x <- eval_data_col(data, mapping$x) y <- eval_data_col(data, mapping$y) cor <- cor(x, y, method = method) ggally_smooth_loess(data, mapping, ...) + ggplot2::geom_label( data = data.frame( x = min(x, na.rm = TRUE), y = max(y, na.rm = TRUE), lab = round(cor, digits = 3) ), mapping = ggplot2::aes(x = x, y = y, label = lab), hjust = 0, vjust = 1, size = 5, fontface = "bold", inherit.aes = FALSE # do not inherit anything from the ... ) } pm <- ggduo( psychademic, rev(psych_variables), academic_variables, types = list(continuous = loess_with_cor), showStrips = FALSE ) suppressWarnings(p_(pm)) # ignore warnings from loess # add color according to sex pm <- ggduo( psychademic, mapping = ggplot2::aes(color = sex), rev(psych_variables), academic_variables, types = list(continuous = loess_with_cor), showStrips = FALSE, legend = c(5,2) ) suppressWarnings(p_(pm)) # add color according to sex pm <- ggduo( psychademic, mapping = ggplot2::aes(color = motivation), rev(psych_variables), academic_variables, types = list(continuous = loess_with_cor), showStrips = FALSE, legend = c(5,2) ) + ggplot2::theme(legend.position = "bottom") suppressWarnings(p_(pm)) } GGally/man/ggally_ratio.Rd0000644000176200001440000000232513666472400015162 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_ratio} \alias{ggally_ratio} \title{Mosaic plot} \usage{ ggally_ratio( data, mapping = do.call(ggplot2::aes_string, as.list(colnames(data)[1:2])), ..., floor = 0, ceiling = NULL ) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used. Only x and y will used and both are required} \item{...}{passed to \code{\link[ggplot2]{geom_tile}(...)}} \item{floor}{don't display cells smaller than this value} \item{ceiling}{max value to scale frequencies. If any frequency is larger than the ceiling, the fill color is displayed darker than other rectangles} } \description{ Plots the mosaic plot by using fluctuation. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_ratio(tips, ggplot2::aes(sex, day))) p_(ggally_ratio(tips, ggplot2::aes(sex, day)) + ggplot2::coord_equal()) # only plot tiles greater or equal to 20 and scale to a max of 50 p_(ggally_ratio( tips, ggplot2::aes(sex, day), floor = 20, ceiling = 50 ) + ggplot2::theme(aspect.ratio = 4/2)) } \author{ Barret Schloerke } \keyword{hplot} GGally/man/stat_cross.Rd0000644000176200001440000001115213761572054014671 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stat_cross.R \docType{data} \name{stat_cross} \alias{stat_cross} \alias{StatCross} \title{Compute cross-tabulation statistics} \usage{ stat_cross( mapping = NULL, data = NULL, geom = "point", position = "identity", ..., na.rm = TRUE, show.legend = NA, inherit.aes = TRUE, keep.zero.cells = FALSE ) } \arguments{ \item{mapping}{Set of aesthetic mappings created by \code{\link[ggplot2:aes]{aes()}} or \code{\link[ggplot2:aes_]{aes_()}}. If specified and \code{inherit.aes = TRUE} (the default), it is combined with the default mapping at the top level of the plot. You must supply \code{mapping} if there is no plot mapping.} \item{data}{The data to be displayed in this layer. There are three options: If \code{NULL}, the default, the data is inherited from the plot data as specified in the call to \code{\link[ggplot2:ggplot]{ggplot()}}. A \code{data.frame}, or other object, will override the plot data. All objects will be fortified to produce a data frame. See \code{\link[ggplot2:fortify]{fortify()}} for which variables will be created. A \code{function} will be called with a single argument, the plot data. The return value must be a \code{data.frame}, and will be used as the layer data. A \code{function} can be created from a \code{formula} (e.g. \code{~ head(.x, 10)}).} \item{geom}{Override the default connection between \code{\link[ggplot2]{geom_point}} and \code{stat_prop}.} \item{position}{Position adjustment, either as a string, or the result of a call to a position adjustment function.} \item{...}{Other arguments passed on to \code{\link[ggplot2:layer]{layer()}}. These are often aesthetics, used to set an aesthetic to a fixed value, like \code{colour = "red"} or \code{size = 3}. They may also be parameters to the paired geom/stat.} \item{na.rm}{If \code{TRUE}, the default, missing values are removed with a warning. If \code{TRUE}, missing values are silently removed.} \item{show.legend}{logical. Should this layer be included in the legends? \code{NA}, the default, includes if any aesthetics are mapped. \code{FALSE} never includes, and \code{TRUE} always includes. It can also be a named logical vector to finely select the aesthetics to display.} \item{inherit.aes}{If \code{FALSE}, overrides the default aesthetics, rather than combining with them. This is most useful for helper functions that define both data and aesthetics and shouldn't inherit behaviour from the default plot specification, e.g. \code{\link[ggplot2:borders]{borders()}}.} \item{keep.zero.cells}{If \code{TRUE}, cells with no observations are kept.} } \description{ Computes statistics of a 2-dimensional matrix using \code{\link[broom]{augment.htest}} from \pkg{broom}. } \section{Aesthetics}{ \code{stat_prop} requires the \strong{x} and the \strong{y} aesthetics. } \section{Computed variables}{ \describe{ \item{observed}{number of observations in x,y} \item{prop}{proportion of total} \item{row.prop}{row proportion} \item{col.prop}{column proportion} \item{expected}{expected count under the null hypothesis} \item{resid}{Pearson's residual} \item{std.resid}{standardized residual} } } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive d <- as.data.frame(Titanic) # plot number of observations p_(ggplot(d) + aes(x = Class, y = Survived, weight = Freq, size = after_stat(observed)) + stat_cross() + scale_size_area(max_size = 20)) # custom shape and fill colour based on chi-squared residuals p_(ggplot(d) + aes( x = Class, y = Survived, weight = Freq, size = after_stat(observed), fill = after_stat(std.resid) ) + stat_cross(shape = 22) + scale_fill_steps2(breaks = c(-3, -2, 2, 3), show.limits = TRUE) + scale_size_area(max_size = 20)) # plotting the number of observations as a table p_(ggplot(d) + aes( x = Class, y = Survived, weight = Freq, label = after_stat(observed) ) + geom_text(stat = "cross")) # Row proportions with standardized residuals p_(ggplot(d) + aes( x = Class, y = Survived, weight = Freq, label = scales::percent(after_stat(row.prop)), size = NULL, fill = after_stat(std.resid) ) + stat_cross(shape = 22, size = 30) + geom_text(stat = "cross") + scale_fill_steps2(breaks = c(-3, -2, 2, 3), show.limits = TRUE) + facet_grid(Sex ~ .) + labs(fill = "Standardized residuals") + theme_minimal()) # can work with continuous or character variables data(tips, package = "reshape") p_(ggplot(tips) + aes(x = tip, y = as.character(day), size = after_stat(observed)) + stat_cross(alpha = .1, color = "blue") + scale_size_area(max_size = 12)) } \keyword{datasets} GGally/man/ggsurv.Rd0000644000176200001440000000735414063456663014036 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggsurv.R \name{ggsurv} \alias{ggsurv} \title{Survival curves} \usage{ ggsurv( s, CI = "def", plot.cens = TRUE, surv.col = "gg.def", cens.col = "gg.def", lty.est = 1, lty.ci = 2, size.est = 0.5, size.ci = size.est, cens.size = 2, cens.shape = 3, back.white = FALSE, xlab = "Time", ylab = "Survival", main = "", order.legend = TRUE ) } \arguments{ \item{s}{an object of class \code{survfit}} \item{CI}{should a confidence interval be plotted? Defaults to \code{TRUE} for single stratum objects and \code{FALSE} for multiple strata objects.} \item{plot.cens}{mark the censored observations?} \item{surv.col}{colour of the survival estimate. Defaults to black for one stratum, and to the default \pkg{ggplot2} colours for multiple strata. Length of vector with colour names should be either 1 or equal to the number of strata.} \item{cens.col}{colour of the points that mark censored observations.} \item{lty.est}{linetype of the survival curve(s). Vector length should be either 1 or equal to the number of strata.} \item{lty.ci}{linetype of the bounds that mark the 95\% CI.} \item{size.est}{line width of the survival curve} \item{size.ci}{line width of the 95\% CI} \item{cens.size}{point size of the censoring points} \item{cens.shape}{shape of the points that mark censored observations.} \item{back.white}{if TRUE the background will not be the default grey of \code{ggplot2} but will be white with borders around the plot.} \item{xlab}{the label of the x-axis.} \item{ylab}{the label of the y-axis.} \item{main}{the plot label.} \item{order.legend}{boolean to determine if the legend display should be ordered by final survival time} } \value{ An object of class \code{ggplot} } \description{ This function produces Kaplan-Meier plots using \pkg{ggplot2}. As a first argument it needs a \code{survfit} object, created by the \code{survival} package. Default settings differ for single stratum and multiple strata objects. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive if (require(survival) && require(scales)) { data(lung, package = "survival") sf.lung <- survival::survfit(Surv(time, status) ~ 1, data = lung) p_(ggsurv(sf.lung)) # Multiple strata examples sf.sex <- survival::survfit(Surv(time, status) ~ sex, data = lung) pl.sex <- ggsurv(sf.sex) p_(pl.sex) # Adjusting the legend of the ggsurv fit p_(pl.sex + ggplot2::guides(linetype = "none") + ggplot2::scale_colour_discrete( name = 'Sex', breaks = c(1,2), labels = c('Male', 'Female') )) # Multiple factors lung2 <- plyr::mutate(lung, older = as.factor(age > 60)) sf.sex2 <- survival::survfit(Surv(time, status) ~ sex + older, data = lung2) pl.sex2 <- ggsurv(sf.sex2) p_(pl.sex2) # Change legend title p_(pl.sex2 + labs(color = "New Title", linetype = "New Title")) # We can still adjust the plot after fitting data(kidney, package = "survival") sf.kid <- survival::survfit(Surv(time, status) ~ disease, data = kidney) pl.kid <- ggsurv(sf.kid, plot.cens = FALSE) p_(pl.kid) # Zoom in to first 80 days p_(pl.kid + ggplot2::coord_cartesian(xlim = c(0, 80), ylim = c(0.45, 1))) # Add the diseases names to the plot and remove legend p_(pl.kid + ggplot2::annotate( "text", label = c("PKD", "Other", "GN", "AN"), x = c(90, 125, 5, 60), y = c(0.8, 0.65, 0.55, 0.30), size = 5, colour = scales::hue_pal( h = c(0, 360) + 15, c = 100, l = 65, h.start = 0, direction = 1 )(4) ) + ggplot2::guides(color = "none", linetype = "none")) } } \author{ Edwin Thoen } GGally/man/grab_legend.Rd0000644000176200001440000000213313761572054014735 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggmatrix_legend.R \name{grab_legend} \alias{grab_legend} \alias{print.legend_guide_box} \title{Grab the legend and print it as a plot} \usage{ grab_legend(p) \method{print}{legend_guide_box}(x, ..., plotNew = FALSE) } \arguments{ \item{p}{ggplot2 plot object} \item{x}{legend object that has been grabbed from a ggplot2 object} \item{...}{ignored} \item{plotNew}{boolean to determine if the \code{grid.newpage()} command and a new blank rectangle should be printed} } \description{ Grab the legend and print it as a plot } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive library(ggplot2) histPlot <- qplot( x = Sepal.Length, data = iris, fill = Species, geom = "histogram", binwidth = 1/4 ) (right <- histPlot) (bottom <- histPlot + theme(legend.position = "bottom")) (top <- histPlot + theme(legend.position = "top")) (left <- histPlot + theme(legend.position = "left")) p_(grab_legend(right)) p_(grab_legend(bottom)) p_(grab_legend(top)) p_(grab_legend(left)) } GGally/man/remove_color_unless_equal.Rd0000644000176200001440000000131213664365623017761 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{remove_color_unless_equal} \alias{remove_color_unless_equal} \title{Remove colour mapping unless found in select mapping keys} \usage{ remove_color_unless_equal(mapping, to = c("x", "y")) } \arguments{ \item{mapping}{output of \code{ggplot2::\link[ggplot2]{aes}(...)}} \item{to}{set of mapping keys to check} } \value{ Aes mapping with colour mapping kept only if found in selected mapping keys. } \description{ Remove colour mapping unless found in select mapping keys } \examples{ mapping <- aes(x = sex, y = age, colour = sex) mapping <- aes(x = sex, y = age, colour = region) remove_color_unless_equal(mapping) } GGally/man/ggally_dot_and_box.Rd0000644000176200001440000000166213666472400016327 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_dot_and_box} \alias{ggally_dot_and_box} \title{Box and dot plot} \usage{ ggally_dot_and_box(data, mapping, ..., boxPlot = TRUE) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{parameters passed to either geom_jitter or geom_boxplot} \item{boxPlot}{boolean to decide to plot either box plots (TRUE) or dot plots (FALSE)} } \description{ Place box plots or dot plots on the graph } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_dot_and_box( tips, mapping = ggplot2::aes(x = total_bill, y = sex, color = sex), boxPlot = TRUE )) p_(ggally_dot_and_box( tips, mapping = ggplot2::aes(x = total_bill, y = sex, color = sex), boxPlot = FALSE )) } \author{ Barret Schloerke } \keyword{internal} GGally/man/ggally_nostic_hat.Rd0000644000176200001440000000365013764714663016212 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnostic.R \name{ggally_nostic_hat} \alias{ggally_nostic_hat} \title{\code{\link{ggnostic}} leverage points} \usage{ ggally_nostic_hat( data, mapping, ..., linePosition = 2 * sum(eval_data_col(data, mapping$y))/nrow(data), lineColor = brew_colors("grey"), lineSize = 0.5, lineAlpha = 1, lineType = 2, avgLinePosition = sum(eval_data_col(data, mapping$y))/nrow(data), avgLineColor = brew_colors("grey"), avgLineSize = lineSize, avgLineAlpha = lineAlpha, avgLineType = 1 ) } \arguments{ \item{data, mapping, ...}{supplied directly to \code{\link{ggally_nostic_line}}} \item{linePosition, lineColor, lineSize, lineAlpha, lineType}{parameters supplied to \code{\link[ggplot2:geom_path]{ggplot2::geom_line()}} for the cutoff line} \item{avgLinePosition, avgLineColor, avgLineSize, avgLineAlpha, avgLineType}{parameters supplied to \code{\link[ggplot2:geom_path]{ggplot2::geom_line()}} for the average line} } \value{ \pkg{ggplot2} plot object } \description{ A function to display stats::influence's hat information against a given explanatory variable. } \details{ As stated in \code{\link[stats:lm.influence]{stats::influence()}} documentation: hat: a vector containing the diagonal of the 'hat' matrix. The diagonal elements of the 'hat' matrix describe the influence each response value has on the fitted value for that same observation. A suggested "cutoff" line is added to the plot at a height of 2 * p / n and an expected line at a height of p / n. If either \code{linePosition} or \code{avgLinePosition} is \code{NULL}, the respective line will not be drawn. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive dt <- broomify(stats::lm(mpg ~ wt + qsec + am, data = mtcars)) p_(ggally_nostic_hat(dt, ggplot2::aes(wt, .hat))) } \seealso{ \code{\link[stats:lm.influence]{stats::influence()}} } GGally/man/get_x_axis_labels.Rd0000644000176200001440000000055513663637143016170 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{get_x_axis_labels} \alias{get_x_axis_labels} \title{Get x axis labels} \usage{ get_x_axis_labels(p, xRange) } \arguments{ \item{p}{plot object} \item{xRange}{range of x values} } \description{ Retrieves x axis labels from the plot object directly. } \keyword{internal} GGally/man/ggally_box.Rd0000644000176200001440000000205313666472400014632 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_box} \alias{ggally_box} \alias{ggally_box_no_facet} \title{Box plot} \usage{ ggally_box(data, mapping, ...) ggally_box_no_facet(data, mapping, ...) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{other arguments being supplied to geom_boxplot} } \description{ Make a box plot with a given data set. \code{ggally_box_no_facet} will be a single panel plot, while \code{ggally_box} will be a faceted plot } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_box(tips, mapping = ggplot2::aes(x = total_bill, y = sex))) p_(ggally_box(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "sex"))) p_(ggally_box( tips, mapping = ggplot2::aes_string(y = "total_bill", x = "sex", color = "sex"), outlier.colour = "red", outlier.shape = 13, outlier.size = 8 )) } \author{ Barret Schloerke } \keyword{hplot} GGally/man/brew_colors.Rd0000644000176200001440000000046413663637143015033 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnostic.R \name{brew_colors} \alias{brew_colors} \title{RColorBrewer Set1 colors} \usage{ brew_colors(col) } \arguments{ \item{col}{standard color name used to retrieve hex color value} } \description{ RColorBrewer Set1 colors } GGally/man/wrap.Rd0000644000176200001440000000661713663637143013472 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggpairs_internal_plots.R \name{wrap_fn_with_param_arg} \alias{wrap_fn_with_param_arg} \alias{wrapp} \alias{wrap} \alias{wrap_fn_with_params} \title{Wrap a function with different parameter values} \usage{ wrap_fn_with_param_arg( funcVal, params = NULL, funcArgName = deparse(substitute(funcVal)) ) wrapp(funcVal, params = NULL, funcArgName = deparse(substitute(funcVal))) wrap(funcVal, ..., funcArgName = deparse(substitute(funcVal))) wrap_fn_with_params(funcVal, ..., funcArgName = deparse(substitute(funcVal))) } \arguments{ \item{funcVal}{function that the \code{params} will be applied to. The function should follow the api of \code{function(data, mapping, ...)\{\}}. \code{funcVal} is allowed to be a string of one of the \code{ggally_NAME} functions, such as \code{"points"} for \code{ggally_points} or \code{"facetdensity"} for \code{ggally_facetdensity}.} \item{params}{named vector or list of parameters to be applied to the \code{funcVal}} \item{funcArgName}{name of function to be displayed} \item{...}{named parameters to be supplied to \code{wrap_fn_with_param_arg}} } \value{ a \code{function(data, mapping, ...)\{\}} that will wrap the original function with the parameters applied as arguments } \description{ Wraps a function with the supplied parameters to force different default behavior. This is useful for functions that are supplied to ggpairs. It allows you to change the behavior of one function, rather than creating multiple functions with different parameter settings. } \details{ \code{wrap} is identical to \code{wrap_fn_with_params}. These function take the new parameters as arguments. \code{wrapp} is identical to \code{wrap_fn_with_param_arg}. These functions take the new parameters as a single list. The \code{params} and \code{fn} attributes are there for debugging purposes. If either attribute is altered, the function must be re-wrapped to have the changes take effect. } \examples{ # small function to display plots only if it's interactive p_ <- GGally::print_if_interactive # example function that prints 'val' fn <- function(data, mapping, val = 2) { print(val) } fn(data = NULL, mapping = NULL) # 2 # wrap function to change default value 'val' to 5 instead of 2 wrapped_fn1 <- wrap(fn, val = 5) wrapped_fn1(data = NULL, mapping = NULL) # 5 # you may still supply regular values wrapped_fn1(data = NULL, mapping = NULL, val = 3) # 3 # wrap function to change 'val' to 5 using the arg list wrapped_fn2 <- wrap_fn_with_param_arg(fn, params = list(val = 5)) wrapped_fn2(data = NULL, mapping = NULL) # 5 # change parameter settings in ggpairs for a particular function ## Goal output: regularPlot <- ggally_points( iris, ggplot2::aes(Sepal.Length, Sepal.Width), size = 5, color = "red" ) p_(regularPlot) # Wrap ggally_points to have parameter values size = 5 and color = 'red' w_ggally_points <- wrap(ggally_points, size = 5, color = "red") wrappedPlot <- w_ggally_points( iris, ggplot2::aes(Sepal.Length, Sepal.Width) ) p_(wrappedPlot) # Double check the aes parameters are the same for the geom_point layer identical(regularPlot$layers[[1]]$aes_params, wrappedPlot$layers[[1]]$aes_params) # Use a wrapped function in ggpairs pm <- ggpairs(iris, 1:3, lower = list(continuous = wrap(ggally_points, size = 5, color = "red"))) p_(pm) pm <- ggpairs(iris, 1:3, lower = list(continuous = w_ggally_points)) p_(pm) } GGally/man/v1_ggmatrix_theme.Rd0000644000176200001440000000115213666472400016114 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/deprecated.R \name{v1_ggmatrix_theme} \alias{v1_ggmatrix_theme} \title{Modify a \code{\link{ggmatrix}} object by adding an \pkg{ggplot2} object to all} \usage{ v1_ggmatrix_theme() } \description{ Modify a \code{\link{ggmatrix}} object by adding an \pkg{ggplot2} object to all } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive p_(ggpairs(iris, 1:2) + v1_ggmatrix_theme()) # move the column names to the left and bottom p_(ggpairs(iris, 1:2, switch = "both") + v1_ggmatrix_theme()) } GGally/man/signif_stars.Rd0000644000176200001440000000157513663637143015212 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stars.R \name{signif_stars} \alias{signif_stars} \title{Significance Stars} \usage{ signif_stars(x, three = 0.001, two = 0.01, one = 0.05, point = 0.1) } \arguments{ \item{x}{numeric values that will be compared to the \code{point}, \code{one}, \code{two}, and \code{three} values} \item{three}{threshold below which to display three stars} \item{two}{threshold below which to display two stars} \item{one}{threshold below which to display one star} \item{point}{threshold below which to display one point (\code{NULL} to deactivate)} } \value{ character vector containing the appropriate number of stars for each \code{x} value } \description{ Calculate significance stars } \examples{ x <- c(0.5, 0.1, 0.05, 0.01, 0.001) signif_stars(x) signif_stars(x, one = .15, point = NULL) } \author{ Joseph Larmarange } GGally/man/ggally_facetdensity.Rd0000644000176200001440000000147013666472400016526 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_facetdensity} \alias{ggally_facetdensity} \title{Faceted density plot} \usage{ ggally_facetdensity(data, mapping, ...) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{other arguments being sent to stat_density} } \description{ Make density plots by displaying subsets of the data in different panels. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_facetdensity(tips, mapping = ggplot2::aes(x = total_bill, y = sex))) p_(ggally_facetdensity( tips, mapping = ggplot2::aes_string(y = "total_bill", x = "sex", color = "sex") )) } \author{ Barret Schloerke } \keyword{hplot} GGally/man/ggally_density.Rd0000644000176200001440000000223713666472400015525 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_density} \alias{ggally_density} \title{Bivariate density plot} \usage{ ggally_density(data, mapping, ...) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{...}{parameters sent to either stat_density2d or geom_density2d} } \description{ Make a 2D density plot from a given data. } \details{ The aesthetic "fill" determines whether or not \code{stat_density2d} (filled) or \code{geom_density2d} (lines) is used. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_density(tips, mapping = ggplot2::aes(x = total_bill, y = tip))) p_(ggally_density(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip"))) p_(ggally_density( tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip", fill = "..level..") )) p_(ggally_density( tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip", fill = "..level..") ) + ggplot2::scale_fill_gradient(breaks = c(0.05, 0.1, 0.15, 0.2))) } \author{ Barret Schloerke } \keyword{hplot} GGally/man/ggnet.Rd0000644000176200001440000002206113777103031013602 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnet.R \name{ggnet} \alias{ggnet} \title{Network plot} \usage{ ggnet( net, mode = "fruchtermanreingold", layout.par = NULL, layout.exp = 0, size = 9, alpha = 1, weight = "none", weight.legend = NA, weight.method = weight, weight.min = NA, weight.max = NA, weight.cut = FALSE, group = NULL, group.legend = NA, node.group = group, node.color = NULL, node.alpha = alpha, segment.alpha = alpha, segment.color = "grey50", segment.label = NULL, segment.size = 0.25, arrow.size = 0, arrow.gap = 0, arrow.type = "closed", label = FALSE, label.nodes = label, label.size = size/2, label.trim = FALSE, legend.size = 9, legend.position = "right", names = c("", ""), quantize.weights = FALSE, subset.threshold = 0, top8.nodes = FALSE, trim.labels = FALSE, ... ) } \arguments{ \item{net}{an object of class \code{\link[network]{network}}, or any object that can be coerced to this class, such as an adjacency or incidence matrix, or an edge list: see \link[network]{edgeset.constructors} and \link[network]{network} for details. If the object is of class \link[igraph:aaa-igraph-package]{igraph} and the \link[intergraph:intergraph-package]{intergraph} package is installed, it will be used to convert the object: see \code{\link[intergraph]{asNetwork}} for details.} \item{mode}{a placement method from those provided in the \code{\link[sna]{sna}} package: see \link[sna:gplot.layout]{gplot.layout} for details. Also accepts the names of two numeric vertex attributes of \code{net}, or a matrix of numeric coordinates, in which case the first two columns of the matrix are used. Defaults to the Fruchterman-Reingold force-directed algorithm.} \item{layout.par}{options to be passed to the placement method, as listed in \link[sna]{gplot.layout}. Defaults to \code{NULL}.} \item{layout.exp}{a multiplier to expand the horizontal axis if node labels get clipped: see \link[scales]{expand_range} for details. Defaults to \code{0} (no expansion).} \item{size}{size of the network nodes. If the nodes are weighted, their area is proportionally scaled up to the size set by \code{size}. Defaults to \code{9}.} \item{alpha}{a level of transparency for nodes, vertices and arrows. Defaults to \code{1}.} \item{weight}{the weighting method for the nodes, which might be a vertex attribute or a vector of size values. Also accepts \code{"indegree"}, \code{"outdegree"}, \code{"degree"} or \code{"freeman"} to size the nodes by their unweighted degree centrality (\code{"degree"} and \code{"freeman"} are equivalent): see \code{\link[sna]{degree}} for details. All node weights must be positive. Defaults to \code{"none"} (no weighting).} \item{weight.legend}{the name to assign to the legend created by \code{weight}. Defaults to \code{NA} (no name).} \item{weight.method}{see \code{weight}} \item{weight.min}{whether to subset the network to nodes with a minimum size, based on the values of \code{weight}. Defaults to \code{NA} (preserves all nodes).} \item{weight.max}{whether to subset the network to nodes with a maximum size, based on the values of \code{weight}. Defaults to \code{NA} (preserves all nodes).} \item{weight.cut}{whether to cut the size of the nodes into a certain number of quantiles. Accepts \code{TRUE}, which tries to cut the sizes into quartiles, or any positive numeric value, which tries to cut the sizes into that many quantiles. If the size of the nodes do not contain the specified number of distinct quantiles, the largest possible number is used. See \code{\link[stats]{quantile}} and \code{\link[base]{cut}} for details. Defaults to \code{FALSE} (does nothing).} \item{group}{the groups of the nodes, either as a vector of values or as a vertex attribute. If set to \code{mode} on a bipartite network, the nodes will be grouped as \code{"actor"} if they belong to the primary mode and \code{"event"} if they belong to the secondary mode.} \item{group.legend}{the name to assign to the legend created by \code{group}.} \item{node.group}{see \code{group}} \item{node.color}{a vector of character strings to color the nodes with, holding as many colors as there are levels in \code{node.group}. Defaults to \code{NULL}, which will assign grayscale colors to each group.} \item{node.alpha}{transparency of the nodes. Inherits from \code{alpha}.} \item{segment.alpha}{the level of transparency of the edges. Defaults to \code{alpha}, which defaults to \code{1}.} \item{segment.color}{the color of the edges, as a color value, a vector of color values, or as an edge attribute containing color values. Defaults to \code{"grey50"}.} \item{segment.label}{the labels to plot at the middle of the edges, as a single value, a vector of values, or as an edge attribute. Defaults to \code{NULL} (no edge labels).} \item{segment.size}{the size of the edges, in points, as a single numeric value, a vector of values, or as an edge attribute. Defaults to \code{0.25}.} \item{arrow.size}{the size of the arrows for directed network edges, in points. See \code{\link[grid]{arrow}} for details. Defaults to \code{0} (no arrows).} \item{arrow.gap}{a setting aimed at improving the display of edge arrows by plotting slightly shorter edges. Accepts any value between \code{0} and \code{1}, where a value of \code{0.05} will generally achieve good results when the size of the nodes is reasonably small. Defaults to \code{0} (no shortening).} \item{arrow.type}{the type of the arrows for directed network edges. See \code{\link[grid]{arrow}} for details. Defaults to \code{"closed"}.} \item{label}{whether to label the nodes. If set to \code{TRUE}, nodes are labeled with their vertex names. If set to a vector that contains as many elements as there are nodes in \code{net}, nodes are labeled with these. If set to any other vector of values, the nodes are labeled only when their vertex name matches one of these values. Defaults to \code{FALSE} (no labels).} \item{label.nodes}{see \code{label}} \item{label.size}{the size of the node labels, in points, as a numeric value, a vector of numeric values, or as a vertex attribute containing numeric values. Defaults to \code{size / 2} (half the maximum node size), which defaults to \code{6}.} \item{label.trim}{whether to apply some trimming to the node labels. Accepts any function that can process a character vector, or a strictly positive numeric value, in which case the labels are trimmed to a fixed-length substring of that length: see \code{\link[base]{substr}} for details. Defaults to \code{FALSE} (does nothing).} \item{legend.size}{the size of the legend symbols and text, in points. Defaults to \code{9}.} \item{legend.position}{the location of the plot legend(s). Accepts all \code{legend.position} values supported by \code{\link[ggplot2]{theme}}. Defaults to \code{"right"}.} \item{names}{deprecated: see \code{group.legend} and \code{size.legend}} \item{quantize.weights}{deprecated: see \code{weight.cut}} \item{subset.threshold}{deprecated: see \code{weight.min}} \item{top8.nodes}{deprecated: this functionality was experimental and has been removed entirely from \code{ggnet}} \item{trim.labels}{deprecated: see \code{label.trim}} \item{...}{other arguments passed to the \code{geom_text} object that sets the node labels: see \code{\link[ggplot2]{geom_text}} for details.} } \description{ Function for plotting network objects using \pkg{ggplot2}, now replaced by the \code{\link{ggnet2}} function, which provides additional control over plotting parameters. Please visit \url{https://github.com/briatte/ggnet} for the latest version of ggnet2, and \url{https://briatte.github.io/ggnet/} for a vignette that contains many examples and explanations. } \details{ The degree centrality measures that can be produced through the \code{weight} argument will take the directedness of the network into account, but will be unweighted. To compute weighted network measures, see the \code{tnet} package by Tore Opsahl (\code{help("tnet", package = "tnet")}). } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive library(network) # random adjacency matrix x <- 10 ndyads <- x * (x - 1) density <- x / ndyads m <- matrix(0, nrow = x, ncol = x) dimnames(m) <- list(letters[ 1:x ], letters[ 1:x ]) m[ row(m) != col(m) ] <- runif(ndyads) < density m # random undirected network n <- network::network(m, directed = FALSE) n ggnet(n, label = TRUE, alpha = 1, color = "white", segment.color = "black") # random groups g <- sample(letters[ 1:3 ], 10, replace = TRUE) g # color palette p <- c("a" = "steelblue", "b" = "forestgreen", "c" = "tomato") p_(ggnet(n, node.group = g, node.color = p, label = TRUE, color = "white")) # edge arrows on a directed network p_(ggnet(network(m, directed = TRUE), arrow.gap = 0.05, arrow.size = 10)) } \seealso{ \code{\link{ggnet2}} in this package, \code{\link[sna]{gplot}} in the \code{\link[sna]{sna}} package, and \code{\link[network]{plot.network}} in the \code{\link[network]{network}} package } \author{ Moritz Marbach and Francois Briatte, with help from Heike Hofmann, Pedro Jordano and Ming-Yu Liu } GGally/man/stat_prop.Rd0000644000176200001440000001074213764714663014533 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stat_prop.R \docType{data} \name{stat_prop} \alias{stat_prop} \alias{StatProp} \title{Compute proportions according to custom denominator} \usage{ stat_prop( mapping = NULL, data = NULL, geom = "bar", position = "fill", ..., width = NULL, na.rm = FALSE, orientation = NA, show.legend = NA, inherit.aes = TRUE ) } \arguments{ \item{mapping}{Set of aesthetic mappings created by \code{\link[ggplot2:aes]{aes()}} or \code{\link[ggplot2:aes_]{aes_()}}. If specified and \code{inherit.aes = TRUE} (the default), it is combined with the default mapping at the top level of the plot. You must supply \code{mapping} if there is no plot mapping.} \item{data}{The data to be displayed in this layer. There are three options: If \code{NULL}, the default, the data is inherited from the plot data as specified in the call to \code{\link[ggplot2:ggplot]{ggplot()}}. A \code{data.frame}, or other object, will override the plot data. All objects will be fortified to produce a data frame. See \code{\link[ggplot2:fortify]{fortify()}} for which variables will be created. A \code{function} will be called with a single argument, the plot data. The return value must be a \code{data.frame}, and will be used as the layer data. A \code{function} can be created from a \code{formula} (e.g. \code{~ head(.x, 10)}).} \item{geom}{Override the default connection between \code{\link[ggplot2]{geom_bar}} and \code{stat_prop}.} \item{position}{Position adjustment, either as a string, or the result of a call to a position adjustment function.} \item{...}{Other arguments passed on to \code{\link[ggplot2:layer]{layer()}}. These are often aesthetics, used to set an aesthetic to a fixed value, like \code{colour = "red"} or \code{size = 3}. They may also be parameters to the paired geom/stat.} \item{width}{Bar width. By default, set to 90\% of the resolution of the data.} \item{na.rm}{If \code{FALSE}, the default, missing values are removed with a warning. If \code{TRUE}, missing values are silently removed.} \item{orientation}{The orientation of the layer. The default (\code{NA}) automatically determines the orientation from the aesthetic mapping. In the rare event that this fails it can be given explicitly by setting \code{orientation} to either \code{"x"} or \code{"y"}. See the \emph{Orientation} section for more detail.} \item{show.legend}{logical. Should this layer be included in the legends? \code{NA}, the default, includes if any aesthetics are mapped. \code{FALSE} never includes, and \code{TRUE} always includes. It can also be a named logical vector to finely select the aesthetics to display.} \item{inherit.aes}{If \code{FALSE}, overrides the default aesthetics, rather than combining with them. This is most useful for helper functions that define both data and aesthetics and shouldn't inherit behaviour from the default plot specification, e.g. \code{\link[ggplot2:borders]{borders()}}.} } \description{ \code{stat_prop} is a variation of \code{\link[ggplot2:geom_bar]{ggplot2::stat_count()}} allowing to compute custom proportions according to the \strong{by} aesthetic defining the denominator (i.e. all proportions for a same value of \strong{by} will sum to 1). The \code{by} aesthetic should be a factor. } \section{Aesthetics}{ \code{stat_prop()} understands the following aesthetics (required aesthetics are in bold): \itemize{ \item \strong{x \emph{or} y} \item \strong{by} (this aesthetic should be a \strong{factor}) \item group \item weight } } \section{Computed variables}{ \describe{ \item{count}{number of points in bin} \item{prop}{computed proportion} } } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive d <- as.data.frame(Titanic) p <- ggplot(d) + aes(x = Class, fill = Survived, weight = Freq, by = Class) + geom_bar(position = "fill") + geom_text(stat = "prop", position = position_fill(.5)) p_(p) p_(p + facet_grid(~ Sex)) p_(ggplot(d) + aes(x = Class, fill = Survived, weight = Freq) + geom_bar(position = "dodge") + geom_text( aes(by = Survived), stat = "prop", position = position_dodge(0.9), vjust = "bottom" )) p_(ggplot(d) + aes(x = Class, fill = Survived, weight = Freq, by = 1) + geom_bar() + geom_text( aes(label = scales::percent(after_stat(prop), accuracy = 1)), stat = "prop", position = position_stack(.5) )) } \seealso{ \code{\link[ggplot2:geom_bar]{ggplot2::stat_count()}} } \author{ Joseph Larmarange } \keyword{datasets} GGally/man/ggally_blank.Rd0000644000176200001440000000075413764714663015150 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_blank} \alias{ggally_blank} \alias{ggally_blankDiag} \title{Blank plot} \usage{ ggally_blank(...) ggally_blankDiag(...) } \arguments{ \item{...}{other arguments ignored} } \description{ Draws nothing. } \details{ Makes a "blank" ggplot object that will only draw white space } \seealso{ \code{\link[ggplot2:element]{ggplot2::element_blank()}} } \author{ Barret Schloerke } \keyword{hplot} GGally/man/add_ref_boxes.Rd0000644000176200001440000000135113764714663015300 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gglyph.R \name{add_ref_boxes} \alias{add_ref_boxes} \title{Add reference boxes around each cell of the glyphmap.} \usage{ add_ref_boxes( data, var_fill = NULL, color = "white", size = 0.5, fill = NA, ... ) } \arguments{ \item{data}{A glyphmap structure.} \item{var_fill}{Variable name to use to set the fill color} \item{color}{Set the color to draw in, default is "white"} \item{size}{Set the line size, default is 0.5} \item{fill}{fill value used if \code{var_fill} is \code{NULL}} \item{...}{other arguments passed onto \code{\link[ggplot2:geom_tile]{ggplot2::geom_rect()}}} } \description{ Add reference boxes around each cell of the glyphmap. } GGally/man/scag_order.Rd0000644000176200001440000000117413761572054014620 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggparcoord.R \name{scag_order} \alias{scag_order} \title{Find order of variables} \usage{ scag_order(scag, vars, measure) } \arguments{ \item{scag}{\code{scagnostics} object} \item{vars}{character vector of the variables to be ordered} \item{measure}{scagnostics measure to order according to} } \value{ character vector of variable ordered according to the given scagnostic measure } \description{ Find order of variables based on a specified scagnostic measure by maximizing the index values of that measure along the path. } \author{ Barret Schloerke } GGally/man/ggally_table.Rd0000644000176200001440000000441513761572054015137 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stat_cross.R \name{ggally_table} \alias{ggally_table} \alias{ggally_tableDiag} \title{Display a table of the number of observations} \usage{ ggally_table( data, mapping, keep.zero.cells = FALSE, ..., geom_tile_args = NULL ) ggally_tableDiag( data, mapping, keep.zero.cells = FALSE, ..., geom_tile_args = NULL ) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{keep.zero.cells}{If \code{TRUE}, display cells with no observation.} \item{...}{other arguments passed to \code{\link[ggplot2]{geom_text}(...)}} \item{geom_tile_args}{other arguments passed to \code{\link[ggplot2]{geom_tile}(...)}} } \description{ Plot the number of observations as a table. Other statistics computed by \code{\link{stat_cross}} could be used (see examples). } \note{ The \strong{colour} aesthetic is taken into account only if equal to \strong{x} or \strong{y}. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_table(tips, mapping = aes(x = smoker, y = sex))) p_(ggally_table(tips, mapping = aes(x = day, y = time))) p_(ggally_table(tips, mapping = aes(x = smoker, y = sex, colour = smoker))) # colour is kept only if equal to x or y p_(ggally_table(tips, mapping = aes(x = smoker, y = sex, colour = day))) # diagonal version p_(ggally_tableDiag(tips, mapping = aes(x = smoker))) # custom label size and color p_(ggally_table(tips, mapping = aes(x = smoker, y = sex), size = 16, color = "red")) # display column proportions p_(ggally_table( tips, mapping = aes(x = day, y = sex, label = scales::percent(after_stat(col.prop))) )) # draw table cells p_(ggally_table( tips, mapping = aes(x = smoker, y = sex), geom_tile_args = list(colour = "black", fill = "white") )) # Use standardized residuals to fill table cells p_(ggally_table( as.data.frame(Titanic), mapping = aes( x = Class, y = Survived, weight = Freq, fill = after_stat(std.resid), label = scales::percent(after_stat(col.prop), accuracy = .1) ), geom_tile_args = list(colour = "black") ) + scale_fill_steps2(breaks = c(-3, -2, 2, 3), show.limits = TRUE)) } \author{ Joseph Larmarange } \keyword{hplot} GGally/man/ggally_diagAxis.Rd0000644000176200001440000000236113666472400015575 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_diagAxis} \alias{ggally_diagAxis} \title{Internal axis labels for ggpairs} \usage{ ggally_diagAxis( data, mapping, label = mapping$x, labelSize = 5, labelXPercent = 0.5, labelYPercent = 0.55, labelHJust = 0.5, labelVJust = 0.5, gridLabelSize = 4, ... ) } \arguments{ \item{data}{dataset being plotted} \item{mapping}{aesthetics being used (x is the variable the plot will be made for)} \item{label}{title to be displayed in the middle. Defaults to \code{mapping$x}} \item{labelSize}{size of variable label} \item{labelXPercent}{percent of horizontal range} \item{labelYPercent}{percent of vertical range} \item{labelHJust}{hjust supplied to label} \item{labelVJust}{vjust supplied to label} \item{gridLabelSize}{size of grid labels} \item{...}{other arguments for geom_text} } \description{ This function is used when \code{axisLabels == "internal"}. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(tips, package = "reshape") p_(ggally_diagAxis(tips, ggplot2::aes(x=tip))) p_(ggally_diagAxis(tips, ggplot2::aes(x=sex))) } \author{ Jason Crowley and Barret Schloerke } GGally/man/ggally_summarise_by.Rd0000644000176200001440000000476113666472637016565 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gg-plots.R \name{ggally_summarise_by} \alias{ggally_summarise_by} \alias{weighted_median_iqr} \alias{weighted_mean_sd} \title{Summarize a continuous variable by each value of a discrete variable} \usage{ ggally_summarise_by( data, mapping, text_fn = weighted_median_iqr, text_fn_vertical = NULL, ... ) weighted_median_iqr(x, weights = NULL) weighted_mean_sd(x, weights = NULL) } \arguments{ \item{data}{data set using} \item{mapping}{aesthetics being used} \item{text_fn}{function that takes an x and weights and returns a text string} \item{text_fn_vertical}{function that takes an x and weights and returns a text string, used when \code{x} is discrete and \code{y} is continuous. If not provided, will use \code{text_fn}, replacing spaces by carriage returns.} \item{...}{other arguments passed to \code{\link[ggplot2]{geom_text}(...)}} \item{x}{a numeric vector} \item{weights}{an optional numeric vectors of weights. If \code{NULL}, equal weights of 1 will be taken into account.} } \description{ Display summary statistics of a continuous variable for each value of a discrete variable. } \details{ \code{weighted_median_iqr} computes weighted median and interquartile range. \code{weighted_mean_sd} computes weighted mean and standard deviation. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive if (require(Hmisc)) { data(tips, package = "reshape") p_(ggally_summarise_by(tips, mapping = aes(x = total_bill, y = day))) p_(ggally_summarise_by(tips, mapping = aes(x = day, y = total_bill))) # colour is kept only if equal to the discrete variable p_(ggally_summarise_by(tips, mapping = aes(x = total_bill, y = day, color = day))) p_(ggally_summarise_by(tips, mapping = aes(x = total_bill, y = day, color = sex))) p_(ggally_summarise_by(tips, mapping = aes(x = day, y = total_bill, color = day))) # custom text size p_(ggally_summarise_by(tips, mapping = aes(x = total_bill, y = day), size = 6)) # change statistic to display p_(ggally_summarise_by(tips, mapping = aes(x = total_bill, y = day), text_fn = weighted_mean_sd)) # custom stat function weighted_sum <- function(x, weights = NULL) { if (is.null(weights)) weights <- 1 paste0("Total : ", round(sum(x * weights, na.rm = TRUE), digits = 1)) } p_(ggally_summarise_by(tips, mapping = aes(x = total_bill, y = day), text_fn = weighted_sum)) } } \author{ Joseph Larmarange } \keyword{hplot} GGally/man/ggscatmat.Rd0000644000176200001440000000240213666472400014453 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggscatmat.R \name{ggscatmat} \alias{ggscatmat} \title{Traditional scatterplot matrix for purely quantitative variables} \usage{ ggscatmat( data, columns = 1:ncol(data), color = NULL, alpha = 1, corMethod = "pearson" ) } \arguments{ \item{data}{a data matrix. Should contain numerical (continuous) data.} \item{columns}{an option to choose the column to be used in the raw dataset. Defaults to \code{1:ncol(data)}.} \item{color}{an option to group the dataset by the factor variable and color them by different colors. Defaults to \code{NULL}, i.e. no coloring. If supplied, it will be converted to a factor.} \item{alpha}{an option to set the transparency in scatterplots for large data. Defaults to \code{1}.} \item{corMethod}{method argument supplied to \code{\link[stats]{cor}}} } \description{ This function makes a scatterplot matrix for quantitative variables with density plots on the diagonal and correlation printed in the upper triangle. } \examples{ # small function to display plots only if it's interactive p_ <- GGally::print_if_interactive data(flea) p_(ggscatmat(flea, columns = 2:4)) p_(ggscatmat(flea, columns = 2:4, color = "species")) } \author{ Mengjia Ni, Di Cook } GGally/man/print.ggmatrix.Rd0000644000176200001440000000141413665760216015464 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggmatrix_print.R \name{print.ggmatrix} \alias{print.ggmatrix} \title{Print \code{\link{ggmatrix}} object} \usage{ \method{print}{ggmatrix}(x, newpage = is.null(vp), vp = NULL, ...) } \arguments{ \item{x}{plot to display} \item{newpage}{draw new (empty) page first?} \item{vp}{viewport to draw plot in} \item{...}{arguments passed onto \code{\link{ggmatrix_gtable}}} } \description{ Print method taken from \code{ggplot2:::print.ggplot} and altered for a \code{\link{ggmatrix}} object } \examples{ data(tips, package = "reshape") pMat <- ggpairs(tips, c(1,3,2), mapping = ggplot2::aes_string(color = "sex")) pMat # calls print(pMat), which calls print.ggmatrix(pMat) } \author{ Barret Schloerke } GGally/man/plot_types.Rd0000644000176200001440000000060613665760216014713 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/find-combo.R \name{plot_types} \alias{plot_types} \title{Plot Types} \usage{ plot_types(data, columnsX, columnsY, allowDiag = TRUE) } \arguments{ \item{data}{data set to be used} } \description{ Retrieves the type of plot that should be used for all combinations } \author{ Barret Schloerke } \keyword{internal} GGally/man/ggmatrix_location.Rd0000644000176200001440000000462013761572054016221 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggpairs_add.R \name{ggmatrix_location} \alias{ggmatrix_location} \title{\code{\link{ggmatrix}} plot locations} \usage{ ggmatrix_location(pm, location = NULL, rows = NULL, cols = NULL) } \arguments{ \item{pm}{\code{\link{ggmatrix}} plot object} \item{location}{\describe{ \item{\code{"all"}, \code{TRUE}}{All row and col combinations} \item{\code{"none"}}{No row and column combinations} \item{\code{"upper"}}{Locations where the column value is higher than the row value} \item{\code{"lower"}}{Locations where the row value is higher than the column value} \item{\code{"diag"}}{Locations where the column value is equal to the row value} \item{\code{matrix} or \code{data.frame}}{ \code{matrix} values will be converted into \code{data.frame}s. \itemize{ \item A \code{data.frame} with the exact column names \code{c("row", "col")} \item A \code{data.frame} with the number of rows and columns matching the plot matrix object provided. Each cell will be tested for a "truthy" value to determine if the location should be kept. } } }} \item{rows}{numeric vector of the rows to be used. Will be used with \code{cols} if \code{location} is \code{NULL}} \item{cols}{numeric vector of the cols to be used. Will be used with \code{rows} if \code{location} is \code{NULL}} } \value{ Data frame with columns \code{c("row", "col")} containing locations for the plot matrix } \description{ \lifecycle{experimental} } \details{ Convert many types of location values to a consistent \code{data.frame} of \code{row} and \code{col} values. } \examples{ pm <- ggpairs(reshape::tips, 1:3) # All locations ggmatrix_location(pm, location = "all") ggmatrix_location(pm, location = TRUE) # No locations ggmatrix_location(pm, location = "none") # "upper" triangle locations ggmatrix_location(pm, location = "upper") # "lower" triangle locations ggmatrix_location(pm, location = "lower") # "diag" locations ggmatrix_location(pm, location = "diag") # specific rows ggmatrix_location(pm, rows = 2) # specific columns ggmatrix_location(pm, cols = 2) # row and column combinations ggmatrix_location(pm, rows = c(1,2), cols = c(1,3)) # matrix locations mat <- matrix(TRUE, ncol = 3, nrow = 3) mat[1,1] <- FALSE locs <- ggmatrix_location(pm, location = mat) ## does not contain the 1,1 cell locs # Use the output of a prior ggmatrix_location ggmatrix_location(pm, location = locs) } GGally/man/ggcorr.Rd0000644000176200001440000001363413777103031013767 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggcorr.R \name{ggcorr} \alias{ggcorr} \title{Correlation matrix} \usage{ ggcorr( data, method = c("pairwise", "pearson"), cor_matrix = NULL, nbreaks = NULL, digits = 2, name = "", low = "#3B9AB2", mid = "#EEEEEE", high = "#F21A00", midpoint = 0, palette = NULL, geom = "tile", min_size = 2, max_size = 6, label = FALSE, label_alpha = FALSE, label_color = "black", label_round = 1, label_size = 4, limits = c(-1, 1), drop = is.null(limits) || identical(limits, FALSE), layout.exp = 0, legend.position = "right", legend.size = 9, ... ) } \arguments{ \item{data}{a data frame or matrix containing numeric (continuous) data. If any of the columns contain non-numeric data, they will be dropped with a warning.} \item{method}{a vector of two character strings. The first value gives the method for computing covariances in the presence of missing values, and must be (an abbreviation of) one of \code{"everything"}, \code{"all.obs"}, \code{"complete.obs"}, \code{"na.or.complete"} or \code{"pairwise.complete.obs"}. The second value gives the type of correlation coefficient to compute, and must be one of \code{"pearson"}, \code{"kendall"} or \code{"spearman"}. See \code{\link[stats]{cor}} for details. Defaults to \code{c("pairwise", "pearson")}.} \item{cor_matrix}{the named correlation matrix to use for calculations. Defaults to the correlation matrix of \code{data} when \code{data} is supplied.} \item{nbreaks}{the number of breaks to apply to the correlation coefficients, which results in a categorical color scale. See 'Note'. Defaults to \code{NULL} (no breaks, continuous scaling).} \item{digits}{the number of digits to show in the breaks of the correlation coefficients: see \code{\link[base]{cut}} for details. Defaults to \code{2}.} \item{name}{a character string for the legend that shows the colors of the correlation coefficients. Defaults to \code{""} (no legend name).} \item{low}{the lower color of the gradient for continuous scaling of the correlation coefficients. Defaults to \code{"#3B9AB2"} (blue).} \item{mid}{the midpoint color of the gradient for continuous scaling of the correlation coefficients. Defaults to \code{"#EEEEEE"} (very light grey).} \item{high}{the upper color of the gradient for continuous scaling of the correlation coefficients. Defaults to \code{"#F21A00"} (red).} \item{midpoint}{the midpoint value for continuous scaling of the correlation coefficients. Defaults to \code{0}.} \item{palette}{if \code{nbreaks} is used, a ColorBrewer palette to use instead of the colors specified by \code{low}, \code{mid} and \code{high}. Defaults to \code{NULL}.} \item{geom}{the geom object to use. Accepts either \code{"tile"}, \code{"circle"}, \code{"text"} or \code{"blank"}.} \item{min_size}{when \code{geom} has been set to \code{"circle"}, the minimum size of the circles. Defaults to \code{2}.} \item{max_size}{when \code{geom} has been set to \code{"circle"}, the maximum size of the circles. Defaults to \code{6}.} \item{label}{whether to add correlation coefficients to the plot. Defaults to \code{FALSE}.} \item{label_alpha}{whether to make the correlation coefficients increasingly transparent as they come close to 0. Also accepts any numeric value between \code{0} and \code{1}, in which case the level of transparency is set to that fixed value. Defaults to \code{FALSE} (no transparency).} \item{label_color}{the color of the correlation coefficients. Defaults to \code{"grey75"}.} \item{label_round}{the decimal rounding of the correlation coefficients. Defaults to \code{1}.} \item{label_size}{the size of the correlation coefficients. Defaults to \code{4}.} \item{limits}{bounding of color scaling for correlations, set \code{limits = NULL} or \code{FALSE} to remove} \item{drop}{if using \code{nbreaks}, whether to drop unused breaks from the color scale. Defaults to \code{FALSE} (recommended).} \item{layout.exp}{a multiplier to expand the horizontal axis to the left if variable names get clipped. Defaults to \code{0} (no expansion).} \item{legend.position}{where to put the legend of the correlation coefficients: see \code{\link[ggplot2]{theme}} for details. Defaults to \code{"bottom"}.} \item{legend.size}{the size of the legend title and labels, in points: see \code{\link[ggplot2]{theme}} for details. Defaults to \code{9}.} \item{...}{other arguments supplied to \code{\link[ggplot2]{geom_text}} for the diagonal labels.} } \description{ Function for making a correlation matrix plot, using \pkg{ggplot2}. The function is directly inspired by Tian Zheng and Yu-Sung Su's \code{corrplot} function in the 'arm' package. Please visit \url{https://github.com/briatte/ggcorr} for the latest version of \code{ggcorr}, and see the vignette at \url{https://briatte.github.io/ggcorr/} for many examples of how to use it. } \note{ Recommended values for the \code{nbreaks} argument are \code{3} to \code{11}, as values above 11 are visually difficult to separate and are not supported by diverging ColorBrewer palettes. } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive # Basketball statistics provided by Nathan Yau at Flowing Data. dt <- read.csv("http://datasets.flowingdata.com/ppg2008.csv") # Default output. p_(ggcorr(dt[, -1])) # Labeled output, with coefficient transparency. p_(ggcorr(dt[, -1], label = TRUE, label_alpha = TRUE)) # Custom options. p_(ggcorr( dt[, -1], name = expression(rho), geom = "circle", max_size = 10, min_size = 2, size = 3, hjust = 0.75, nbreaks = 6, angle = -45, palette = "PuOr" # colorblind safe, photocopy-able )) # Supply your own correlation matrix p_(ggcorr( data = NULL, cor_matrix = cor(dt[, -1], use = "pairwise") )) } \seealso{ \code{\link[stats]{cor}} and \code{corrplot} in the \code{arm} package. } \author{ Francois Briatte, with contributions from Amos B. Elberg and Barret Schloerke } GGally/man/ggally_nostic_cooksd.Rd0000644000176200001440000000252413764714663016717 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ggnostic.R \name{ggally_nostic_cooksd} \alias{ggally_nostic_cooksd} \title{\code{\link{ggnostic}} Cook's distance} \usage{ ggally_nostic_cooksd( data, mapping, ..., linePosition = pf(0.5, length(attr(data, "var_x")), nrow(data) - length(attr(data, "var_x"))), lineColor = brew_colors("grey"), lineType = 2 ) } \arguments{ \item{data, mapping, ..., lineColor, lineType}{parameters supplied to \code{\link{ggally_nostic_line}}} \item{linePosition}{4 / n is the general cutoff point for Cook's Distance} } \value{ \pkg{ggplot2} plot object } \description{ A function to display \code{\link[stats:influence.measures]{stats::cooks.distance()}}. } \details{ A line is added at F_{p, n - p}(0.5) to display the general cutoff point for Cook's Distance. Reference: Michael H. Kutner, Christopher J. Nachtsheim, John Neter, and William Li. Applied linear statistical models. The McGraw-Hill / Irwin series operations and decision sciences. McGraw-Hill Irwin, 2005, p. 403 } \examples{ # Small function to display plots only if it's interactive p_ <- GGally::print_if_interactive dt <- broomify(stats::lm(mpg ~ wt + qsec + am, data = mtcars)) p_(ggally_nostic_cooksd(dt, ggplot2::aes(wt, .cooksd))) } \seealso{ \code{\link[stats:influence.measures]{stats::cooks.distance()}} } GGally/DESCRIPTION0000644000176200001440000000522714064014052013141 0ustar liggesusersPackage: GGally Version: 2.1.2 License: GPL (>= 2.0) Title: Extension to 'ggplot2' Type: Package LazyLoad: yes LazyData: true URL: https://ggobi.github.io/ggally/, https://github.com/ggobi/ggally BugReports: https://github.com/ggobi/ggally/issues Authors@R: c( person("Barret", "Schloerke", role = c("aut", "cre"), email = "schloerke@gmail.com"), person("Di", "Cook", role = c("aut", "ths"), email = "dicook@monash.edu"), person("Joseph", "Larmarange", role = "aut", email = "joseph@larmarange.net"), person("Francois", "Briatte", role = "aut", email = "f.briatte@gmail.com"), person("Moritz", "Marbach", role = "aut", email = "mmarbach@mail.uni-mannheim.de"), person("Edwin", "Thoen", role = "aut", email = "edwinthoen@gmail.com"), person("Amos", "Elberg", role = "aut", email = "amos.elberg@gmail.com"), person("Ott", "Toomet", role = "ctb", email = "otoomet@gmail.com"), person("Jason", "Crowley", role = "aut", email = "crowley.jason.s@gmail.com"), person("Heike", "Hofmann", role = "ths", email = "hofmann@iastate.edu"), person("Hadley", "Wickham", role = "ths", email = "h.wickham@gmail.com") ) Description: The R package 'ggplot2' is a plotting system based on the grammar of graphics. 'GGally' extends 'ggplot2' by adding several functions to reduce the complexity of combining geometric objects with transformed data. Some of these functions include a pairwise plot matrix, a two group pairwise plot matrix, a parallel coordinates plot, a survival plot, and several functions to plot networks. Depends: R (>= 3.1), ggplot2 (>= 3.3.4) Imports: dplyr (>= 1.0.0), forcats, grDevices, grid, gtable (>= 0.2.0), lifecycle, plyr (>= 1.8.3), progress, RColorBrewer, reshape (>= 0.8.5), rlang, scales (>= 1.1.0), tidyr, utils Suggests: broom (>= 0.7.0), broom.helpers (>= 1.1.0), chemometrics, geosphere (>= 1.5-1), ggforce, Hmisc, igraph (>= 1.0.1), intergraph (>= 2.0-2), labelled, maps (>= 3.1.0), mapproj, nnet, network (>= 1.17.1), scagnostics, sna (>= 2.3-2), survival, rmarkdown, roxygen2, testthat, crosstalk, knitr, spelling, emmeans RoxygenNote: 7.1.1 SystemRequirements: openssl Encoding: UTF-8 Language: en-US RdMacros: lifecycle NeedsCompilation: no Packaged: 2021-06-20 14:19:23 UTC; barret Author: Barret Schloerke [aut, cre], Di Cook [aut, ths], Joseph Larmarange [aut], Francois Briatte [aut], Moritz Marbach [aut], Edwin Thoen [aut], Amos Elberg [aut], Ott Toomet [ctb], Jason Crowley [aut], Heike Hofmann [ths], Hadley Wickham [ths] Maintainer: Barret Schloerke Repository: CRAN Date/Publication: 2021-06-21 04:40:10 UTC GGally/build/0000755000176200001440000000000014063647151012537 5ustar liggesusersGGally/build/GGally.pdf0000644000176200001440000130656714063647151014433 0ustar liggesusers%PDF-1.5 % 2 0 obj << /Type /ObjStm /N 100 /First 808 /Length 1061 /Filter /FlateDecode >> stream xڕOo8sl@BEJ {+=Glv0eɀR3~$V4)d492yiOEڐx*Hx*$][M7+f("d K PED D `IVrY2MUd=,9v yC%(PRUT4U_9ahy't)'ҖJI!VL;HkTP5iVjU1FpxtGƴfЦ6aRIr;,ܷu8d>QO /pp* ye:F5rjT$ʡ o]'jm҂iٿ SN:f!}oqHڰKx^_3:+v/g˄2>4,Eg-Sw3b /al} oCb)  ?$Hfu63󈘱s|!{eIC>fBMtd f!pއyYMJƯT\|% ~ 'kx]7 !hs}m:|lw"U "em@nlbʻ04\³~pynp֙_SEzL5s%:/&g. v| Mi"?J.n[ Oû?x endstream endobj 203 0 obj << /Type /ObjStm /N 100 /First 853 /Length 1201 /Filter /FlateDecode >> stream xڍVM6+tܹÒ$U䘋<Ƶrl;y-6`1su^?Kƙ)Ų fXiɘfBn΄X0)%}VR*&3|$lN`LWG``*X&,V2Iz2cu,3ginVĥ%x2ôgf}͙k5c&sfql!>4Y\aE #jۜ9:(\2gJ927LpUqf,tR8P UC& 8dBWҴf:#2+$.2DJk`ւ0`(iTK!&X)/ aAA$$, m63b+4J؜0`sD^ 'J<:n G^hp[i/ +P,*Lrʔ/@4 'F8RF PTPF+iaH4^ࡱU< & ^h4RiW QԆ ļ4ӧRqdxAcdg!S__w`//ߚóxb߾D){bQMa,z燮9G}\.nʎ;UhH{iPvE/ќR4ͼMؒW@y ݏo'[+CyD=n}ҍjۭC6shIy<*89 I;Rțy8',!]n:L 8qFoq:ܩoCTqU؆/4>UOGLe:crqCVM^:tQB\ު HD CoB}L#l'C$} Z]Oy6E鋨<ʄCFޟI^FUy9 U^k#OJzf(:CqCUo{B$8o*L 2elW¡}S;Nb~#jG(I:OW]p1 xlױNd182>}U6>>^zn/?b'fKyS 2~_֏l&*7gɴܶ5wcOwa%:Im{8_bo5{3l݅ ??` Aecwzy!džTvl.J<턎? M %SfS|#< endstream endobj 410 0 obj << /Length 1021 /Filter /FlateDecode >> stream xڕWˎ6WhWizHI @hD"lY3({}N@x6!;q #O.Hdt>\|}|$ywI@X<FZ}A@Y/ġeb Vޙ#iakbe -}FﭟW6cБ1_hd>=C*:2z6neu^IL &hy">%8ԋ,mv[t͋ zY#<άx bɛyWZuQs@S^r-sKXDC$ORfeՁSsR6s sed_CS]10IU2[YqtlGK >_IpÄh.JH8 FCOṿ>}d#sV5ML,DE?yIųhwfr~̝˕R>DPR2fjsHa8Jsn~##oi e=PzRu>"nĻSy˯ ͽW?U'DT"2`8A/iUI9>t#MtŴINWӵ1hܛVk^eiwVyyn*'݉&wDBr*A3,mN#Y. _,BC0BNh^[=ONsFJ'enZ2{DEAaxݵC۰3J endstream endobj 445 0 obj << /Length 1008 /Filter /FlateDecode >> stream xmo4Se#< bںWm,4KnFe!ʄ4]slv2;'?&.(h̖1?p#Ψ3KS>åO ٛ\&$FA (T-kYj~cMpΉ/1ax44?6Y%6MG_0}|'#?MПG9o[hK )}:hZb.B]Ŧ~!CЋ@M|WRw%xUI5vBd5Ġ]PX&.w)ޙQtUkQRe,n dI;_jYk}+tk-UgU 3/ΗrOuykӰ]4ȂQ65[+ӛ ^w~w#w~SZa.PP̯w+6߯0iv7jO Fu]۶8EP)9G87K/i(z!iYz74J==K·J% nQȧM|i3Vc1jp0|@>qB;+x +e`\cl/?8~a1|b9zq|cx6}Jx4M*LEB`l`8`ym ;PֺE.O?]O1 aI*0=or`SYI}ŭ)vll?oϑT|Pz6 7G,u?!&$IPJ _;5pKVKKLCK{m$~EIVB"/DqG&w:#Ù R l7G꾽$>g 1HHMEYأ`S<v_;mX0~5no sS*:;c4o:Pj%>t04ẘUO_.O( endstream endobj 524 0 obj << /Length 1021 /Filter /FlateDecode >> stream xMo8`vݕXͭc06O3jn@{2d$?/6!^߻wIb4N$޶>l@z1<_r`(*1#ZV}3tM.nE,!wxM ę4m}]w(k?<&&Fȭ_%F$=e~|p?9%8{6xAK~b. <$5_׀>ޏȆWqY_^7)Hۍ6%9mmCs!Z^ꇾJ@V;@Jň-SKJw,^b?bG(x /aS!حk~6_x; /k1Ua:啕et(ȵAEЕy\B0b^MԵȀm2q`zt $ JV =7J<*F e{n\Gʒ22iڗlVf=*٪Fk?(n2 m9J^6Aֻ(jRR|T{>v<⹳ v"C+Z^\l)^KY UIjŎr,@p" }oqق)iBA}Dhե VԢp Ϝ"GDa܏}EaqGЌ֓9˽T ﰽzMpV:tҞ(f1VE=ڲT7Ζ ! ٭{l;Aa5|,K a;_}QN endstream endobj 599 0 obj << /Length 1495 /Filter /FlateDecode >> stream xZKs6WpCI=miID9,B[P@*J}OPvj^ ~XЋg28u #f9`d3j,2/_<~fBr\۔K0,Vp f#XPcVZf[fhR{.t$nb q\.h~b~>3-s݃?"kPS sw\0 F9dͮ799V"r}iON:ڇAjʣ?ĞKHsN@>6"I2S_c F&2Z&dw)ԗ/?Dl.2Sf:l%b 0 ` Q)JRT3$6*9ԐpS(Lrlr.54叱Ȳ_** ?q ,L 8xmo Ƽ=.s+ 3a~A8=j[j(2){}\x$ڴFXN]}RV$7*/ KB& qn\doѿqG7cQ0\gB)/*[@+H|MCyFg:\ٞvsOۃ"wy;Bh|} 6X U2{<|2ribX b07ߎtݮ5;[Z,Ek;`^ ZCS D3$:&mwB@XyC!Wy%Qj#֭mrZ7tE1 aQjRlj,UIl99YSiV:J^+'u.7QVY ҭaR?v ==$" BėADр= bmo3ATUc" D1Yt{g!jInV|I1cs#ܮAJ Ke`]U[cm,ɣz(Cjx~UV*>~bȐ >|.iӇ3a$S$j]Ek!bw|,Vgp6.noG[:@o[5 jkUU)`L* yMA%HUnu6X'aBEzlw5f HָOOAAa5n'١+m(nҊ`~ p[ɸJS:{NW *Lj=+i8VV׵bZqǪU}~p(Hݼ'҃z&nqҴRɞe_ز;:ǴapFZL "JqZӤ!q0 π0]4a?6 U_`p䶇pkG[MZjZnѹ3JIz xxDG^A҄/5| endstream endobj 404 0 obj << /Type /ObjStm /N 100 /First 911 /Length 2473 /Filter /FlateDecode >> stream xڽZn}߯Ge֗@0 Pb  I|dB&HnQ3Î-A]L׽jfK"XJJX+[RT cTGIR5SZ;HOrNZY xPY5DB<[bʊ%1 sMXN% fɉkfQ௒Q]`hP!x?׬*f^SiIS=I7<~ ե}'jɲCɨ$Mފw35lxjJْ5ZK|sM7TX}sOEZN2$7R!UqÊ&lRVR?VSC`>?70^6qXZ[vgw CZs= puҺL3^2epn8H{ 38Yk$ዔ9v8Wޕ@:Q6h5(!Rn`CH$uO@K>?T\{0l;c}:&Kgi,`X|28P1؈ wIrѣz~ߧ9=M'7goa˛˛k\__𧿝t۫_ cDlHKg;HzP_^^AC>pk;g;Hwot"a,F kxRKþg^@?~aO_|sO7W.77?x\\˷o x}D}#W9 4JK)/?_o`nsx.}{z_ =yb 4CG*j}d>Mְ &Ae3mc$5=,B^1'ˠ^Q!'7 =Wk yd!I5IRG^ Ek[$fT˃<T<ȒW >Yg4h`6`dNzQPϘ۽c#q;'/~nu ҎvDWc21ۿǤWcҫ1՘jLz5&^ y-䵐B^ y-䵐B^ y-C^y=C^y=׏ RĪZ%k5QȣG!B^S,.B^L$65,kC~o(/?b@AePy~h8 hEǑi FF2ܗƮ#CRf׹bd ?ksŀ!IǮswkS`u+ Il1*̮s y:֫:W l1WG[> [/ #늦idj Cb~BzaYTG_/L\1 gQڤ.N1t~F,uTu~Ds:W L yir,@ L1?KGw1 :2 9uhTu~ҢsPcdJ olTr Nl*:26?=(W׹btUm` ;?R*wӄl*"eѨr`MV[A0MM\8EF)unY[r sC JV{X ㋲r.4-&ned£]n4IbzpfvSV'dun(Y^ڨv_\tU (پ&GqE_'U oZ9KF'yCɢ~*D(F2>J;R{bY6ྴQO"7׺e+2!4aȆES׺f"r,tS\ -'@т7!BH_DC P` B̵h[ZkҖN?%j-~ڕ}+/k!\{2⧹Vo h Z-V Ղh Z-V Ղh Z-V Ղh Z-V Ղh Z-V Ղh Z-V l3ag¹wJv- P CVq+oJ]8럠3*۳z$_P3rʲCYYOPrפ̑}' endstream endobj 632 0 obj << /Length 1748 /Filter /FlateDecode >> stream xڽXK6 pCɚ94NC6-ѶJI$(Yo 4gtW?:,$ǼnfQJlǛ%fw~ˏw?vz1Ф\%rC9[!^`૥(~RxXnMkNvgbˈMVԇ効tQr<5*&qjj{u;ҹ,9,ë5die!--`UY!=qq՜8T=#aB'dƁ 6e:j^W[󵩴Ytni,-#MKR26H >2FBBKE@ZHڝ<]%'L[bXX0b,!кqfx7qpR&0h1-qE_PyNa@@8t0%A(R9@?^@)U]qR/ߢW- `&vK&~o CuWorqD@_bcUVN0FQы]nRA82eJu#MȊ8Q.V^Eݳ; eӹ 1Q]]s8@\5#vT_q3J!!+S%he)D88"3LU<(^l4 M]lU`:~tFvaX +QT,R|D E,TEfUVRˉ`ontl?s YF G']EEa.q2QaGS|[ ¨~Vdxz?ILZOnL5n9Ҹ^iTLf3<C}*CG<7`V<>3Է*㪇aMV=ESH^5R'q)6 mg8a'9|6f"I[;0^of׫ ij y\KffWfC@ȪU}ot@Zqܩri%v-_Epېm!?P#xHVmc/Ggspi%?~ii.F \d ˢiz$nZFI%mε K xZ%9SZ'6sxg]+C* ߣ7[//ϕSoO5o bE0tDQuҴ'k f+QHp4w5!9]"h5q@b:t~@Z灯\ .ݓ`ky,oR bxw N}W۩U %vJ1A\596 @}]+ endstream endobj 644 0 obj << /Length 814 /Filter /FlateDecode >> stream xVKO@Wz%ûzjP dubɱSہr:.$T7<>>LG'8`>GQ)szzzx Ap h8Mojy)%>BOя[zBa8HkROQ@>@G_l1 @8t ~ ο B9vބ04=E&Z&\}zמժqa6S"̬EڮAxK}3Blp?&eW<.AB0DQ8(qpe#1⢉gbmo\qXrRJ<F qp7Ybߛ٠tRU툾xJs#俅#,stPǃrjxdC=2\(<.ҥ95mJU-`Oj`){qقX;o K2^][u.~ag&v6|KM;:V{oNyy`E # ny70]52aFw?VlF#l+悭yf2ynuz6{'T Pq@8\Gm5JF^=+Z٪2znm*H3ٲZrt4BUUMQ(_wȠӧ Ff]tA8`e )t@XV6n͏jz'.^KoKNtkpP 6* endstream endobj 657 0 obj << /Length 1195 /Filter /FlateDecode >> stream xXKo6W{BJ"%"M Hw{. Ƣm">Dc!k3#&(ZD(uttvgQT4ta,QAQB4MV(էtm2M ]w[F(8e:gAE~࣊fjXRUFkrGl=H: QBsH(!(&Ҥ*qJs1FB1Y$#4Ke":f ;-fU\v;@b?90`L !S[bsvFYqlWP'( h.)qo!} Yp dʄ\Ɔ!dc{"I^ȟ-NW]c躆?O wG e枵Eᵹb6f2 )iJzݮ2%m1/a' .mԅ\P 0ʶ/z( Ofya]'zc}RmpkjK1fPLkFZIDχY1*W3Fޟ\n USEͽM0ʵ" M۳G: a{d gʖD662>:O4d GPb/(~igZl f+|&; /f2b&]퍈`̂v̀dL -EcÁHL0ءkַ׬ r4~3$hdj)!_nvdl>H:۟b s% c&WQ_b]M2C&P8)qѯiJn&Ww5,#~f6ϼ n =Ü>~p"~%kI򄝿OvcX{X@gGf`Z! Mn[~LK!pr\`~W0\ČPR{a7u \C`3ޯ̓WL:=bk`cfRЮ"=dѫ.^gt` G7˸w=cN6fZ{\wj)}9«/18g÷>3iHq[D^2I3xm0C0H!@*HI^ #?裑U^aTwB{EA_E/dƣ?|+ endstream endobj 670 0 obj << /Length 1075 /Filter /FlateDecode >> stream xڽWێ6}WbwJ !M<&mq$R"֭g̙ nj> $0D=AAN nG_o=XH%BkY("_W7dDŽ2 &`Bd=,|&wv GhhB8_9N1q+)Nv 0< 8( hK"}Y! `C)hGSh!`ė"׿^B@{"@"b" I B2p&#~<0KB_\!E=?ͿA )=2ӱcl R֨ >A4Ŀs8xw;_6zSA$C:}JmRUTpʵvFpUnR_' UӧE HCCxl1>ۚȔytpf0cjXiRpuvq#pQGDMTo\NbWmw`gKJMI򩎨im&ܗRVLʌESsѢ&w/fRi~g_ =RǏW\h'MC? է=ZrH mW,$J3u=-> stream xڵV[o6~ÀHĒDIaIb- Fe $/#Eʖ|kڥԹ|< fb. A3J(XF E0 X}{Slwq]#DaB6ݫeFd/-;v I̡UψvՑ;V^t]3mVy[ p{X=Tkw˚9?AK˲Ȫ]` 8ozuq&Qfo!SJT>AVqi5w>=hBA&$(I<0|:7A@:5)lňx&Բsw, ;ԕeFupjcaO*PoXI)mmh_Z.2 bԨ wx`fV ^Ac`WyڛҞkGeI|rd8RV/g |ɌT[g٧6xOqJ!"7(GmY {D:fjnŜ4-B F4!I;=(Q:&0KyLq[^6fl tMs<BWepj u }Nu_-s&Τԑٻzf\SLWn _o CϤq@c?ѓ\ٍ:pd~L=Nec1󑾫ڲvTJt 1I:)M)2;@`ffM#_(ciq^:wB ?/͎m6:FL4Q,U5/S/9}4ON \R/1IOV';Io"lZ՜}ZIՉ*gq3n%w]ubͤ> 嬧ݡuM+2E!F X+y:ZŪ֝o~!H gr+%dZ ļ;]uBs}9N_fJ>;OʝiN4L} G=̒Dv(%@>E` endstream endobj 703 0 obj << /Length 1265 /Filter /FlateDecode >> stream xW[o6~@̒Xڥ0 ֹOIa%m"w(Rv]2]lW@.Ggl/f|>nՈ&|u2~h.BɬS3mB|,+(~tR m7:U\2fS)K&W!?ZDZN+轩,J Wm&F #0W\ڠ/Ķ+Z.U,t˫"`z,FGc:1`+5gF<cwiGĐ8WPcb08c^D՞Þu%?ӳ,++va7vNRcK}GY6JJ&dשQ>ֵZAB)RL O2^ i E o>IFxf眵}5{qZMpBL.z 4cݨU`5"2*6տVAjS39Y4kӓ/&h endstream endobj 611 0 obj << /Type /ObjStm /N 100 /First 880 /Length 1928 /Filter /FlateDecode >> stream xڽZao8_-Gq [n;H{:4 l}ĉ8^ԕL? ș73R .kqKp"r$Wp]MˎP \,&YN# r ~(bSİSŨf #.'qNN4%kDc ')b#RFpq6$r ť(u"?1I,.e屺$ 6Ia;a)TM*KN Qh"LqIZl`c"XSjs3F4HI`I,0V!`Dl+G"Xa/$(2cE& [X'"c*%`]D@ĜvQ,BމtBզ3 qbԤHQRloJ/3e{\F`fU)a89K2 QwdwmWj^ NmkJ!UM@l5jToX=DużpҼrEM7W4?˓]@PͿ__Qeg{U<9;Qg/kپm]==Q|^Y$j.fE>Y$.i<xÂO!=1\(e@X,AXP.yʞ] ZYHb$kW_!E,rZy22- TwF`3Xlx'T,r8h}^qb N-24,]GP ܶNK~j>ah]GP cBOy듢OE*qEfD^w`5wWQQMAcX \(F3:a`DGK7{v| Y76 As%Yì577ׯ\vskڤne]]njFOΦ?_\RV}YW]|a^O09S0Fɺc9aLZsPX+ĤaEч0 +DT5@;x\]<8 {$SWk6QQʦ4:Vǀl:7_v6Wdbj=:DU00+2*a`BK|,܋{Jxg~09\7>ONH< q.ɧ%ףȼG`l ̣z>2;fCY3l%4 LX$"`< 'j`CW>ED{ݼ,Ýw鏚`AgTbu2 )9Rþ=AO=䚒=>ޔ;R6 }]Qd*|zEyyxOмY Ѽi{}}A|ٷىojwlOfݪ9vӏS!4Pk̪,M3LY)e6=N>.`zuzsfl %LJR:=.f{.#!%Feԝ-JUXPaPتc0@p݊MOܖ: q=I> stream xV[o6~ $Ë}HfPX< ma(- EOPdad0$߹<'! r(!!B2GߜBUMۉ|HΡu,D'G1(Ր]0bz\Fq:7J qSM]|F &'* SgQ@TIҧ:0椳Pa. >D fJv҇Ůǁ8aUg.%hF.3W)Z<䬲!,2-FрIܢ$ɀ?EmQGÑLAI 0`i]FdKG!=k. FԺur ) 6-l8㬌YtU >6:OONaweV57e.jU+~'۲+d0>v#-UMYoDS*j>N/F3y>k;T)ܡfV\ :ΐCZ j'H H/$or|9A$'yx F7bl{3i |:nW, q<`>{*co~8FM/}`; WYǛl^a`Ջ%8G{0]OKY6Doya$tLQE ԊIL2vˀz uFӱC{ sJevK=zPj^M|v;!njμT4+ua&z?wT&t ;ݷo]u(HlL~SF-NtɁg-ON^z74?y endstream endobj 730 0 obj << /Length 2166 /Filter /FlateDecode >> stream xYݏ6_al*bF+HhOmam%Ac E6n{b?!ןmg_zF OfY,Y,쾘|^ʐg,Ok/Uߕm+ey{lZ)-xDpv_1&,ϸ=~' Mڕ)U[,賬Ͳ]<=6Gfg w4ݦ˦VI`c,R#g, ǏU?t/:}$pN!7s`LB>rc MȵU_Z5v< usK 5g )h `ykQUȌòY֤O\ ~o%gsv#p$3v ḧ́Y4QlӉNc5><Ͻf\fX&93]W9`́C"sV@S ͕A \ 6M_ F:"0ڮ)kIƌN$ %inգߜo1_ GDgDѕbUIE5UEѕ/vR@H}#hP1Ч!da>xa&N4`1uc輻jSOl5FضcQ̓p p3D,3W c?SoeFĄ&qꂈ AI3GY\ 뮑4 K-x\T)A,c?Tb0e m=NtN`x2>!`Wf[,(hElJ՗;Q+Tvp1Xd!>H:rOic(kBW/l'SmTi}6;<(~2qʣГI8LK Z!ziSI9l&vq#/GvM|, ML }C]')HY)A cOPԜ/@3Ig#} L9씋'|,h5A#VBҺ׫fW?>CE0A^~z;!XUK S> e MtѠ~%}&iaI,h#@Բ:'+XsF[͵wC!=$&/XS_ZS=}[ &^A*Bާ}^,;ܛ2^Q+3EyN)9^D!4яP_^M_iN.rsXg XaꁦaT@% n,^q45UYUnon6 ZwpQԯlVK#!0Qbrvitf`9YT5a()\!VAޙCgYgYUn|*@ endstream endobj 743 0 obj << /Length 981 /Filter /FlateDecode >> stream xڵVn6}WCclk>du kkDR*q%Eʑp(993ռ@RL+RA`ߚDq擿mA,}SQzw>w0=JL)}@70N=^$ˊX\\Š'4gJ[Veҵ%d=-L&\oځ zDٔx7S0iJldY$Mmad^|pe2(˖A K܎OW61S3 .QIc)M.n0 _ggX#W4w֓,ϳP,9B:[xbu<[Su4>p` N֐8Qs 3JmjQ[`|~Ac#Z ߈l|mJwEy2:gG.gн}Sk|~PTպR4}뺓y?4c%2g:\^xGݱ l}y3y½޴+]jUsn%jW&_;.To6 ћ Р{^6t_07nj*׻Vf=v3zqk^5y%UqC!^6ɗ*˟! uy-KޑCDlDb#ee6Yim,:? > endstream endobj 758 0 obj << /Length 999 /Filter /FlateDecode >> stream xWK6WCmbH4IXdSLJ"+қ%[r@`k87# p=m9{.A4Lr 0B0S&Qqŧ͋wq4iXZ$sR׏+W\p*si3\T/Ol GE>[LF}u%4 C0a=G BX$JN\-0A-ܤb[v+ޱntk*ɸyxN\\*TqcT8'PYvL(ۑ鐃 0-?9]@syt;%oQL{֟ڹٚaFh-y5٭dm?)0IJydG@ǺHw%RT(IsXm-8R^Nh 0J! *e;?dY]I^O/ QE 4o:~!պ0VRU_8}}UJ+0Ҟ}2Mxcy[&TU]eMun @rZng4\Kђ8>59ÏPuD'$Ym߽PpI;ґvЏ,iD/*JSߣ@0CFnmi[]qäɣ[fl&Qy64[d!)?;=y:c#ν KHǦUnMMxRtmWjDѓ֔IbBѷ]x冫jؘ867ܗPB#0w>W^Y]#Ó@l힍X~km㏄`g3f}$ؑ9,Na )]+dZO6ǡp&n턦t,]3BL?:i:^ԬWei'ms3ϰ[Ca&|ʆ⛦ V endstream endobj 769 0 obj << /Length 966 /Filter /FlateDecode >> stream xڽWo6~_At%%EEb-2YT%IEJl/lRLP}\]%8>G5qqG |EQ<ݭDI|mUFP`Ӡr9>%>72$F1 FA>_8I`(<b4u'1(d^FYQ<8Hh|MZU~?IGLBUS⩱A&Auvڍ ҥ(^,1so>HR8HJ~>p0?cۊVؕ؅S)尉0jو\%됼)➼XEBVBbPU*s1v& i×KЧb0^? Jn+˶y1roDwġN㻑]Yx έ#tB6PuJ]Qwdv65v9))k.֔9JBr~ҹ7k&SI(̃vkiXc(17oS+޵ծP,-'Y( 7 %A)ZqۯshZ_$]G.]&ZSVlN1lŞ-4]ʴkݓɅY3ۖtuSk6&pI"dpA/ଫ8?:bsu31Hҿyv6]⢪ANӇq\ dUUzb%{ӓ= FTM&8<۷]gOڊ|ēo~As+U>LD1F@glz ?grO` ^evxS-4?_)k |SUpCѾfGƎW~U^zo Ţd endstream endobj 785 0 obj << /Length 1022 /Filter /FlateDecode >> stream xVMo6WPsI}+h `=qOۅA۴T)[rݢ@6IifPOwH38RĔD)/>A2c3MŲzF }Y8|hR {SgRF̸~S)7|t"kYi3-BJ3[7cZl -T|% %dEmRj~UǾ%;4|Xme%|+U59JRHPFH^lc/|?9amlvWX&Pg /?A:O # (:y2!XFI2'JDa8t,=[Zx61#,I6ʋ8$BEUfK2p@ `GOlW(ͣ5)ސv`!SC_b^ȷd3lYj^ k b_/hD g"4BB ,ŠlYJ9ȄU⹸PFv`k̮!]]Bԙ]tU6Bmy-~K(ԋV?{͟ou\,k%p H %28[еvdhkP978$U%f=I endstream endobj 801 0 obj << /Length 1019 /Filter /FlateDecode >> stream xWێ6}WQb[HlŢh7ާ$02- EEM[P(r83gfxB>e>y{CcP"%)F4Y`0_OQUM (xdje~&(0ˍRgVdz3擿' { $Ir3YfR|wt0H#R 24# ;&y"_n$(bԔ&|/VBvکŜ) [<4!2o\nZ6= ]83lB=A)9%ICm*yӱj, Cg.I!IcoA[EcR%tⱑðH/d{Qamk b%Ն0%E?.;/ЇƚgeU| f W|#⑕_+%4޼aT_o{՛|X"Yq1ΪSU珇;c6Aaii?^"jQTm7>KINѕ!^ '0NĮ̸ּK&QVw;^xΎ1Vۦtÿ~zYVW}ol!ya:*ꦓ~dN)^^|]|`>V'S}#_cA iNc22 i4JaLLI `TiUlwp/ajB"c{c8P)\9r+8k>tCsnY  .zrl pyv%-`Zz<ڕoz=Qz=u1ĝP5bi~_tvwחv ݷᱚ)!$g:εY eՑg~d噪٣y۫2^(1 > stream xYMo7W\(r>"6H[$nJF)e }#?䆩ErggfHESX,5UoDDD5IWOggqnIRS{F jNBU zH(*!a C'L! VIUb0 ` O皨I#^!vH͜U Hա >bt0P4Q_aO\0P{ *c@ 0*mp(xסbelWAMǝÛRJH 4X15|G"`,VKêP1sAYHw1C@Msݯqbn!@ѣECҡX,$€J8BU ~QCzZ`Ig00-w^.!a zH$Y( YEl4f$ qD}o ~ nq܀-ִK &!BX@_w=|8?NG%%=K_~-99ÓiuvrrA,dET Pn4YCXDKf=7ṣblWO֫mz0͟_{ "gL:`=not~|MGi4MSa;L\mOvϖel/ytTRffm &߬Vkh<0)o\Y^mw?߮7,7M rpV6~H7;O>O/ ճ7X1-HQ[ Nuk"`PYy{A4L6CA^G\2)\%e*F+r=9g _}J6, #8鳉o~8Yo'&r.U[.h  hRj˛f{Tϕ̗\B1Vr7ՐŧfZ3PZ+6O49@ jʫC9p(`- T3s+z' ߠ/+] 'Yj!0W(iz邬v̱{萰ga5JD(ZUɄ6pNVC>m"]tW\s?1mEF_>vG>l`CcX:wtc`iLHt_5}7c;f)2eV‘(6 yl&Vor~ז.ζvZ Ta9SsK" e=Mzl6+l7k2a` 61];vJZ7Hk~GҪNqDk8C;z:2"Q9J{+{Kza X 5)E =+Vs4 8W7do4%{[C!Cݏ#6}d*⫓ݡ]$pcC`C҃`X!p9{ˆVFhW!0vćApE zqtacABt<S[7wMz|޷3űlu wLFǮ8~CxHTּZli4]\h<ǡ_LKL= fU|1pIn>O% endstream endobj 813 0 obj << /Length 1163 /Filter /FlateDecode >> stream xWI6WΡ2`s/搦3-%J&F%EyAΏUZ0a=+iEֺ\~Yqun}di}((n 9=MY.BVq*g>D0[ +/mysV"9Vn 4P,V؏PG 8&q9fmjV35$Mq\; )CgiBIq"n7?$ 8x&􄪅ˋ r Hr1  > \sO竢Da籣{+K<$s,iZY=ɱr)~XWB8*qUmo]K/DŅ9fz PcUPA]/t=:`ĉHx'^XŏHJS!|"~0.ʒ&x"(M\q//F//!PKpX ýdm%avX׶FRN/q!5ڞй^`u:6AIIWtDI,_KW=|εSגtr݌&NoqiS׿}> stream xڽXߏ6~bu/eCj(wjՇ(MX/떅\asj ƞf3xի[(hۭU`<ۮpl͜$7iYՍ0l)WxuC \/PD•KC5H!sI^ cE-`BG^* %ŌpU/\7M^~]l/'5&Ȋ͑W)/ |65ϖ&?$U!LYuNJ[?~Lj2 _z&ئ |{h7zгm݊'>Tѐ.6ըdJ80 Kk-2l3d $(&Tk9k+#4J(bc^"QZ9@]ʰSs0F؀hjAx<_79s)ZjAGC/W^J+KQ19sp7YCѪRxPpO2^6HA` 1шuvA ̓.Vz{,Z&U܁y4׮5Um5t"/[.yu)rM^C_0iR\hlzo>Va 0'-Oۡ! {u7>_jlX"P\xzN3!k= Ya1,ZK"AAs@}1ufx9,"#o!|9yDV߷J䃨=p/:! ƺq~˜U? )R)b~-jRPt'OcZ^Oz2ީ/E-u0f9:ls!mqYYU=?a/Ǥo@<;L!W7} /;Ů&ENȽ([bt 9d1`AsȄ+uJU)"юc??d'j5=y'~An0`EADmNpvHG ԷAݑ6SMFap\Z/c|'e x$<#ґდc8F!G7-󮈷 .u3B7b Q{PM̘z2o)8vs*sm )y> stream xڽVIo8W1P3HIT0=tIs-3 14__RlK3  ^Nί L F  Ik,@WQJaGZNKCuzLӤ3;yV=ThzcU@m g`ĈXYfm춙>ҞkTUQ^m^w*Xm ][eRF:']n6NaL$L,qZ?\,L/.Z&AZ -+ Uyȳy.K{LkJL5-0RK@W=ynb\\pѤ뾯XJzȪ{In!\SKgi!OW5m;f[~qB[СĽ7geO_Cx7Zhopʢ=w]0(A)ZtIκt9Y9w=x-?ٓ@}1|ڈ O r9gb6x?(a`m&߾#BH~Mbڴ:<:O> stream xڽVMo8Wa-bH}+=b(. Ze$tPlKVNR83o޼ ;ۋw˛0p2~a$1FQ;yZn4ek'wMY$O lu @y~-m^=A$FYw(jϥ.6o;VG}֓G"xY%/LR(u`M-}fv%^@=[]iM$։b碖m6Zuhy~M'셉k9 .skSmV٬wUR2W(V,~) QH^Шo _&(1_F*$m*(̈́It`/C4F 9Z~ cL? JѝhkMIo{v[h}N XiD*y!t>&m*\Nt:@`vK郲8l&{(/B(1IŷP} VX]z;whz2{jEEc`uh`]/8~ȏ~Gw<;\l?(e(k|g)|8CHPB|zFlø}]CSZz[W}]8l ?&O~ X"A}[&Aք`۱,=jZ ^ `pDPFlj)jm)ȷ6C3e"ІM: j?-á~ wSǎB_]B*@]h~tUbrb}=f߸ژ]e]O0úI$>09nf$6ZuzQ p XճtxqE$7FuYDC?7[:5=.Kh N${_,O.? endstream endobj 862 0 obj << /Length 977 /Filter /FlateDecode >> stream xWM6W61$Hm]`CuE\HBJ_ߡHy%vc9$ 8Λyh#.f,g8XD ~8"8ZfݜXa(?M/9/պjw0#)A0Iк{OPv% (1JPf 6 RD8 Y2p/<yYyq7$( 0# V= 4'rkyNQ?q)@qH\T媑Ř`8 5G7ze?^$Ϋ֩9ٿ>~-ͦ.>2O8a1{dOA&E}LY4J>fҢv~ g|{huJYH ?XѸ*Aat֝wO@nK<ԠN]^ʍwych5|0 yF(t4(ܷ)3WgNj #gg+Y1JA]=2ld|^(/*=x9^^d6Ѫ.B%qg_E@FdrKuXX;KÑ!sp0K !˩TyUnJ[26#s%>z]8WQ&o*:=>+kߵO1zt?%,"G1] endstream endobj 879 0 obj << /Length 1258 /Filter /FlateDecode >> stream xW[OF~ϯCώw>% Enpxo{fi8 Lo;v=_yXS,gA1'5vL`[_ً1~0 ĝ^%+v}E HC4 e1ia?2-f?Ҳ. C.h4ca2L0֔b<-i]/lQߘZE 3E˶67qsu՛=63u%wh Zo;ϱ"OˢY&*sL F[qs^p͔qHoBɔQTf}xpEᣝRM@ m<Ā'ܕ,"&d? ?5Il/7[;$t_[}@V"'ly+,D}1> e0KWת^%ҵFD몑U;0/+bvE6tCΙƇE"X~PEﺄ2nG%Ӝ}R?jjS|Vq¡8D$عGu@!/څ p%0t(mzNlB􌓓|@sr*\5ɝ`p05;=.!ec v(ʮ"/;[O #O ڼdc(U##N;VeWZIBpFÆ!l-krH({}л29WX!zz248DxoR D+p#O;xp!5|ƒV Ʉ9˓ek8ЧQ;_wTo><[ƉݿWtx!udlS$M6>Co%k4LV䳚a i{gj6ѓ~t(Qyk9B~toV\ endstream endobj 895 0 obj << /Length 1264 /Filter /FlateDecode >> stream xW[o6~C͐=0tR`(lKhVʒ*ʱ_Ëd2ņ A@R]XfOK/BDoO q$P&/B6%*Kd2 lҖ/v%xn. 9֛O Rlxe֊u猵■Od& _ H9?&)jyXRPTo,C^ O"!LA(XEwo4oB7mLCא!QTMdTm˲50f & C/oْ%OH+TU96|P;7) W pˤX/-?+EQjW+aF4Bo&bPm%lʺS48V tf[K-2P-fʙQb[e_|b؁DvJ.I(v[/o쉕*F; >YI_ZY$V̩%O82T/TOó;#VĊDǾX37t"}A5y1`^dDYdx_m(}xwFݨwyl?T͈9oS^? Vx ?Oٯ,R endstream endobj 903 0 obj << /Length 1069 /Filter /FlateDecode >> stream xWK6WCmR;hYC) DU]w(R69 g&Wmxb{c0( ޢ>MrMiUmk$W - +HM&q5D%7/*&[x),5SD{bPώo@QAkXK^{2xaIaZ;W,4`Ǜ}7hW( d 0&WF nKL{0شjc0@Qwg)Fk$0o B/h"_Si~v/. ]BܟĻVV;go=WTBeֿcΠ endstream endobj 915 0 obj << /Length 972 /Filter /FlateDecode >> stream xVn8+w11Z3 f)(f]HBD4?"i[:E``"s删,"4[ JYxf2w,}qNgzjI\/촻~^_G Vz^z>J`qt40ez7\/.VedU3m]EZ3¾ TVxw[c/N?IYw]J4<d A>Bf빱sjWWZ㳳saQhlg\$$J!Oy./7-u8}%wOxu-˼ZO쌋V?Jb~+Beњ ̟o.e7~YH+#ƅxؘ$ ̊nm 1$o^~O0 -v} nt13U )ǰ".Go#}T] t=rvۭ|B@/@ <̒MVH:y!Tfb&ZÞွQ=/Sp$ڴu65aM妹~ȯn3PwNЯ*yaf¡" jr_Qᄆ.]sٴþcշܖRoaA>j[{]OJgkogc;]__wuἙP /\dΈ%Ӽ4oG8S CS2z+Zk W~'ݭB_ں7+Gs'BhvUU^)d"Mԟ e5 ^yWTR V>uW5:#M'8Bi\]wE%{yzm&o[wns}e " p;&_? .dG endstream endobj 810 0 obj << /Type /ObjStm /N 100 /First 879 /Length 1815 /Filter /FlateDecode >> stream xYKo7W\(r$'0䁴Z Hrh8J`ԱI)~cْN{"w[r^ZS R(P2NGgvz\t>.f_Ew;oGa?7FBd%Rwy=tǟ,2 DN\)"Qzq"4:8ڗ b/wfsA7BĞB5z ZE9,$-^6sŋVNcNsM&jX.L?0!:KO jcyq5qmP]`A-]9 \>Z#:[:=G.kOz Snw mnSeۧ]}J۠:']hYPQO/TK(jk. GM 'gV-h$J?T=se!Ƭr,w{Af]{o]uSuO]џP8(~\~`^l;9旡5&^hЮ*f] KZ!.P,mt?Yt N VU0s)=(UcO)qWN͇-vay+_XxY#9aXy/h endstream endobj 926 0 obj << /Length 1014 /Filter /FlateDecode >> stream xڵW[o6~ .%XZ VE<`CWMDiIޯE(4I\s1A"ff>z.P$L|Bp'(MQkiRS:aRk^wQI 0$#%(8cvO Z{Wy It;xwCBϐ$PJRLau<(y!γg>t"$BJ&Z6l[eDZ)$ I-߸Z5JϢe^ ͂ '젬.i3\ȴ<J)iairO >!|?>C(Y2QB&;Vע, c 7EVfIq i,gi#M . w IΜY@qy}fUXX.fwr՗+@ur ~EWk+eb)ʡu-jU3EuĚyÚ j++|/I{%W'’#N6owPv+Wv^׶Aw/eU)GqkkmӾ> stream xW]o6}!IC6=lWl h,i;r,N`s={2{G0FA{IQ$}:0> A"`GQ4dEq?ͪVM`٥nfV?_d;G_`xl1!(<=܈ϼ"k!?_8"&A+Bih]4#jVv bjdepv(˂-9Dŏ27úږgOZ%Qj-Y]+#G`\ ފ|a%A nn K9aFkQξy.YfGq9:UͩmEXudBwފRdYhC" [ܚ"BAM000Qgt3M(4P^0+ofp0TLe +suݍH4 +`2h@M媮 ; ݷҭ 2Dv7f;]nWӖ+m}-& ffF ]|T5k&+[~j*uׅroa6\YӸr3[o>ŏio7lYncq`k;ӿoLu5Y+MM=m-6/H3~s1m1qa[([d 8w .cDqlWWJ? odRHV>q^4\.X_8rhQo!{\??+StaÒoo'* #[dJo (HS^HTUz fAi!mH/i7>rѰZծfNh=s;0CrwF{OcD.ga9V$.8BR<(@S6Zߩ׵hfV3P}%xZV9qWw1\M[cC~Yɋ(=B|/*|H:ޗ@l>ekn928|v.YOFxp{pF6UBAҾA=ՐI:dŊm+*ʒ(%+e' endstream endobj 949 0 obj << /Length 949 /Filter /FlateDecode >> stream xVKo@W P֛eY^QsH$RKWjF  }@ؕz!Y~0`+u= NkE(oM斃1ro>FK̺%Xs#sq*nʥ;(:F"i2246cwȹѓx(_5sz>::ƉqU!+x(sC&!rH\xM ĵBpHuwÀ!E,bDYZ[2+D̼Lf F"6!nc` +pU` 2%"ģjOc^_R8=-+8?Mu79(X3&̦HaGppaVat駏CzIUBYY3Id,)㲖+ }eŪ]HZ@ Mdhí؆n]Uh[(a&YQmg+s c:HOI Q/wϴyiWxw~kIcl7݈.'R崲@QZq> 6!ȍB2-"[Mf J{HO{9kiNa |&kxgZPET64XtI{2[IU39?j7l] ¶Q @0 endstream endobj 960 0 obj << /Length 1020 /Filter /FlateDecode >> stream xW]o6}ĒV>hO]!2- DMRdq0AR$Ϲ5v ;ٛ?pRF^䬶AF9y77?Y~MQ,l$xAs&7|+[0.vD/Fq;y=;T8 Aq#qd ( C`2P|'\- 80<&d|^&,]/ċ=IQh\U?(f-KXZlw'KKwN*g ]OR},B~-ذou+wTfؿ]zb:Hs mX%7Kz!cM;)d;l'/bbiۂF3B<9NٲנNe츴|G3/tg/l@@$@^d>d8[&l$X9(=~LE&"&4ܦ]*Qr;+b =Gw3zp]?к8?F 42Aw&﯐~ Zz`Me>BBV#Y/(HŠd:v+`u344^տUi}uv?+1  R2I2KYdzKou3і+ByR5/ eT 2I-f%eUGkZi]̭ߠ<M0 do m3̍[ 39xtvN)fS~CQLZWJgu3ɣ͕'Ku w_ʝmM/o_( ^+9Ld:lqEwU`vhmqrC8-iy(>{ݰ0ݿk},F%tNQգI`5Sb2O:-Dm9[{hnᑃlb̖@'ˍ endstream endobj 973 0 obj << /Length 805 /Filter /FlateDecode >> stream xUn0+P nڌ [cӲIVECr$'Ά=ə73oHRDl::%8 YKD \( 8CeogKfƻ~;.Q حdDi`?w>̚u924uD?,̙kx>*_PWq!p *WكaPBXfdoJ5.:)I2MU|\LGF Q!h^nn Z&`cOkZ A!Pqrt=~0Vl ` hǠ:IR#IL;q\zQJI-l)= [ '6 "p\ϕCl]~Dw癮r͝V0ҎJُlJ-=VSs%Kk%CT]ćگ!BƅL cܩ5u[Mѽml3w<)1Ϻ#6C;&8]w~Tds';e{ @~x%A{n4mgWP ai/zixi;OV~.7弽ڙɗ]IA"c)IY?[3E;PC t~bT3kշՕɤ-g0AB{-edv]QɉjWRG޲ju7Hk>rƘ? vnZ=&C8珌}F3#3:#6MnOhV#ܟ(ÿ͙n# endstream endobj 988 0 obj << /Length 1062 /Filter /FlateDecode >> stream xڽW]o6}  ~T`i(`k]`CVMBeIٯ/d9@ӇH{0dw[&p0_!GQ+pQ~NJB'dϼ(evrJ!Sa,/{ -1t@>~?:21\P %:dxs$}xw>? yuQud<<xLD8 c`.J171p ylO7oMqed!E07f1mlժj=ZT4vyY٢)d͝0f PjԢNu@NiuQwo3ii)*|UWFV]\>+9WtR`OG9ƈCȢ *{eEoXT u&%'C0*K+vUmnrB)Uڨz[ve~̺v]Ӌ]7m.~-joԙ_P,_ v\7: TToߍ/tV i8Qlj1Ats^\ k0_:]0C ˿sinE'^=vu]RK|M >4,nwyD5t*lY k3zܥhR^:)yV*skӌ}vاȾŀ} endstream endobj 1008 0 obj << /Length 1202 /Filter /FlateDecode >> stream xڵVKs6Wp΄j#C5ubiic=8 LBBPRC{wP&mi \ vAԡ2DSo,ԙM) }Y&΍YvXjdf~:ed6ZQdD+X7CQy..3w}1TdGF|XXo f-g@ ԨN0:C{^[Sn%u:E~Ϳ$E:#}Pq-+/ހlkY(3wӾds9wƸ~BYnĩl +3 <)kvb63s7S<(2D}64r^UH!}<~)k!VixXL0޿>(>+r !c00 ~[=(uOZwP {|>!{Ӟ@kA>0 ? fL tTA endstream endobj 923 0 obj << /Type /ObjStm /N 100 /First 885 /Length 1457 /Filter /FlateDecode >> stream xY]o[7 }c+%A~ ۀ >lK^4Û%qVɔIGԇ,`9)`ʁM@%pB-Hh JÈBaYPڜb19T6h[hɛZ%4sL]%M5)Iϔ}$w'{ ;F T)J(,ZfL5hV}r01)Fr"a12%)1@o&J8GXȒV l)ApfQ T&jhOF6ݠ!S &NyOa ]IfV"aAˆ ؁/0ZkS*>)scf091Y TCIU}fJ \=3gec(SmfR*xj0gc#\)pQa %Nr:%4(3H8RRמ@p>_HP9 KT&d 8fphA~7\~qqz~W\5&ʱ^p(z\^X.c l Krp3IzA (~=[a~}ϫpm헿pq>^buz1* |,!c/'G/aBG3(݄bvto&fc$*& FֱWaY{k<9ri5r~Q%>03jY&L>ߦ"MvYk{ym[;D7ױ7LRVNňv,}`cA mr^0#hI^o>B*mS켚F;ױׅV$.Z$fQ,iDS`P'X jxg4Jg=;3h`gi[X?;ױ7ǀm5X[Z`]b$YVj^{lƉ(^QL3e~ERA+;+f;qGuPҨMRjܩA2u㗦v)CRum{9X,x8>LK.q{OFlxsjl|4^.>FƆxrK߹|7W46b 0| Γ|urcOČ$r&lPl ᦘU|u:xt?8{]-N<-0GZ+A::=.NeFd1_piujEIca`ԟz Y^Rtuwi[,0tb E;BjQ>um>mYNj|aå%dh(S-_WcEZm`v[WnuFuno vH(hr4QAn~9VO endstream endobj 1022 0 obj << /Length 1518 /Filter /FlateDecode >> stream xXKs6WpCւK6δ鴵K@"DH츇. MTj;ba!̾]\ $xl=F)2€_p&υ\|Ja*i*nim̚Q,4t<42rY/.f4Ck JTK^UUVnD9j0 q'jUg:AIVM?-5#WaL$g}W|$6MfevfD?yYz0̫o*l4\Zuy 1'A<˖V.ԧϾ!0vFeilm]p}Smu} 1wN2DމV1(s7jM[LpaDRIQx>@2ތfʊm~Y-$Қ22_H!uGŝjW`>5i;x7%X #Lerۣϯ:l%.cԅքsk$e;2Nʲmdhʼnn6uGYRa/ZmvMUߙU9~ܩec4qy۟T.F hF8$B*BȽ4 "#Ӝ tQIUl7(R]v:KwNdZQ 7Hʵ RãdKݒCd]pZKBs q4}l$%UWYw)1# 'ui)S+8b @ߓ^LXH 3 w0̄.L> stream xWK6W9@%EQ=ihE8l,2 YTf!)Y&X`MI73 ;uvusS'Fqv' 0bssIQ<ޗiEz_Fd/on?cH(z64("Bxi䯋ꐬ7 c'C^&eC;n+h`DSYd\';*dz_+)vTw IF[Q%-N}ƌX'Ha( ##^eOweLBHzM^7`r%|IőǼ骪<33Q-S+"uf̤0FGz'B{P|S@ C!|/Nuc,ze{(v^*aNzA#(O {(YSeh,m$ξ==ںنx*W# :2Z9. a;G8gX&J֬3 s?vF}M ~ 7 pr\dӈ]]7{lPSڅOJxB4ӓ>@e<9L4ijA5gNʂ'zCjOfPG x*$:ØFv&GZD:ȔEQSS$9bp@.'ث\ e_~ܡE7}G?kRny_‹~Vďrþs,%8o endstream endobj 1052 0 obj << /Length 1279 /Filter /FlateDecode >> stream xWKo6WiИChi7 ݸ{]D;*ZN/ˢV:ȁ&3?~z[z7_Wkx HBz $(2㜄ջ , A҂J?f~]՝uן BmA{5XHZ4h1^=W^B ݷ7ن]И7Xᘥ,@DT.|[1!Z#cYgwm]m)_?}ݰW+3D}6irW OzB q1R'λX]>#(G(Q# p^ɫFLrx %[_ kq횦yffV5?eA(#>be0aHAJ,rו!nQmL-^.\R]E@B!qw ZыhUA1=7?ҺRqf%+;ӽ({}  [ަ2۸~ZfъCOm~c8)WZ#:NPOQuEqg#uD_sv8&= YSt+$}/uWg#d3ERɅ!U:)^RI +y.EVb2 9{JWֵoZ89"-ߨv膑B2ԓT Z "l•=j%yZs]o~ML*aʩ$VVN橘 b)2ɐ&0N0-/PE˳ʄO&'V6;0軓DiۡLt[hLWMH3Qvg]vQ%-A4<6 Dxӓh}ֻV &yk"7Z ľX0͍f˦f=v#I,"ۡ wnp^}ƢT:]t> stream xڕWKs6Wp҃[鴱܋$(WHȎ/][o\7^n&yu bUˑpiDh}\{xu{0`I&5+4UӚy5Y :9 A/,F$s %#@ʗfCk`PuIj6_Qp?X*\w%XֱUV;5Q& oEz z'-<ا(-/NAl y]aaIYS* P nD(zlFRvadE;AU۫RI_\#P;ܤ cV3U1 ;AONtp Be"Be2 F>/@4ʜ.#?XJBR6< 7-5B;sd4×' V\XLn8I|#ԍTZ6 [u ( Y\_DG,+H NR,>c'CP*gri_ r| ԙG+-;KG\|vb4~ľ_Ole(@Q> stream xڵWK6Wiњ!Mhm-ZzE;CR&Ca|hpޤNPfv918ۃ(%8a@rg:onzKQ,$̋Gd_UdtJȻD8n Qp,r,"A4(J5 ԌľoSщGj?C)&7D/J~_uZ.5þ] %yأ*kHۺ.wY!Dj/5zHb:ev,eթOeqa ew+:3˕VFJ4GCn|#9sfs0h\"j,)Zi&Ra|% bavXS^ͽ%+$Lu~E:E1̛F&9#t+c*:z ,MI9}Lo_6߯8];ubOo>n/^燘D!3d {P_@$b endstream endobj 1099 0 obj << /Length 1062 /Filter /FlateDecode >> stream xVK6WC-$2Cf(Cb740-,Ew9glj卋0HI3of!vr; %:C0F> ((=g9ysV.p?E1U ~"Efg,‹;u 燊z}Y m(c{.+4>7O1S%<DjX*Vh4e3PҬ޾{2P>>yEW"tYz0aFZ]y.+'3yq[ƕI@G1c7ӼgD=QBM!Er>tŻ!\j%EkKX+{Y!I{BPÞni&è807eyazt4OvSxOiXpVU)3d4cN,m?pO-6iDa7Y@z1b0Pmw`HqtavEO2r єQ?Ƞ yYljb T?96@գETcZoW0ȾC)ƍFKiASl7c3 @,tlb(cVVjr-U/F^> Z/XD:KY+dKPSD͈.ӭF~:cܔrɸQ/ONY>͈".&naGd`!?%Z94j,X+ )qh('q&ЖƖ6x*eTW*NqwRJ m~"mFT%ҸҴv\^1p`2;4wc.X/o"]Ҩ'b0%9=t:qNQkQoYN\ 79ͱO!un*w\<c\v[2pC+d? 伮t槒PJ+=vuwafmghʚ. ׿1B E0־;ҎB+*jf< @0!HS\W$]su[,6%*Sٮ s2b endstream endobj 1114 0 obj << /Length 1190 /Filter /FlateDecode >> stream xڵWKs6W`C LcO:h` 8+]sj.{4m#Ԥ}t31{-u5"O0/4 ODU]AU[s'"ϭd*.{eVۊRud'C.8Ѓ:>]e{,!3*2Ʉy5;WKRKmQIշy{}Aӊۦ &6 et,lu.5qQbu>s3 5+lZLҠe endstream endobj 1016 0 obj << /Type /ObjStm /N 100 /First 979 /Length 1857 /Filter /FlateDecode >> stream xZMo7W\(A2i @Ԩ" $o(ckgUKU.,5$̛7\C.&TYM?]kUBvBX DC*dmХd )[ה]d5ƊGB֡@2T1FRPKX@abI $.&UHպrpII-KJ 30:K`e0; moXd֖*0#iQ:P GrTmŶTsBl85` CqB uPQnRr,mb=ZMɇh6Lfnr~8,~<vG U|cw Og9L_닩͗&Ngdo(15XժQôO*=za2xiֻ4VoٙԴ=|Gz)p1zt:Y-=:^Lf6*fL{Pk1Ȩ7E",CNqW-y5hPZ=Rgb:=U99m{E.-Q} 3P;d6U^Al|5n5\2rHotBhyEi jh>[yB6a? r #ξ\;ݙo#-%H09/i؜O&>ڏKJRJ[vz #.0BzhܛrFcRh)^F*{)#RQ]i@tTDպHJvہ8o#5%s*|{P B ֵ{Vt{9%Fl  li UMU[YQmtǦ|Z OW7P_G*̵@m smU@XQHޏ6hye{x&j4oP9@;zvYAGl#tX jb:`[t'DNiBe8Dt\OlV]EU;oEv 􉯠*\1mC-[U}ښr>eAޏe6kpa") ԎU]oU.=%~{cw.w tYOE6TbBp}jx-{jl𶄗 )%^ M\> stream xڵWK6W6f~,ClȭYAK\XITI9^;)[ EX 79~nx!Qȋ Bq6G_WEW,w=m?}8݆U* s]pa r$PV-~sPDi~t \0 Tg{㱣Y4\Gb'i4F il2ҶT7^謚znF \!4?G#OUE8 Tf5-EBպ77A7QwPxrYQRp}+jt؋؄ѐF"gI :I䚘61tBG}s!HBtBTiX]L0{h{qVo,#ձ$}Z[>Mڗ+Nu3p|ܕJܩY=;\Y6U͵bA,.ౢu+_cFa +wWN[G:5(ȂuƘظP  '*!G[I}잪l0joz24DL"MSzX? Q~ W(=jl#Ržd!4*zTM=-s{%sl{s*WޯT٣ H]P AcFG4zXh8;bz66 ?'7 7u=m +ha_o4`[߰ 젗`I_b Xl>ku߸*X=Cicpk#1X3r*kBT͟t;"D_zw'R5%[wƢ#&3~ࡂv:.R9MIL켥.͢ʇnl5 ؋EWb! p9@8ߩz pp.of^ݱ،plY#MGlHGBPy ]{CYz gh(;[h5JdI[ޒrgeyc=[z1kA-x[N[) @V/(4_e/e endstream endobj 1143 0 obj << /Length 1524 /Filter /FlateDecode >> stream xڽX[o6~2X(i@R,)V ںCWMdɓ&ޯE(CXyw,fB({G0F!^1B-VŻWw,IA H#?+}e]vRav1{F{䨄Fc-/_w,D#Š{ia8oܗك([kUSdM:do*zǨj|:ٶ(m]W} %Q&~s_ AN4KQsFL[{^޲)GMJs@->7 Y s2CV :er00G$BaXvLF@CZ탐ۍ>g[m|}T(qn/ a**=˂FIO&.⣯q(^p!ʢFt2e.nG9$ipITp~CW9hY0{Eeˬ!os*)z/W?nFD'mQ˥bJOo "!x|V5Fu#7G<`1uiMJo]ZwUqSoT$h3UXw.N3 /e0Xӂg`eU5!˗$×BC8Jz-`䪕XtNԼD50XmjlJIpB= (/Odo1{ W iGW+:ݮ,q#z0xMOI FYʇ_)JD=wQH+KCq)j Ŝ'Bi8PJ/kX#F~#EMW$s9a~2{E"VJr "n:1jfHQecmtlijK#v)}C$m/Ɉ!:p. &ihHԱpZ肇CK Nqi0(\ c8pI]-x4(-S- VBouH87yr(m 2u\'&-FMHoFm>۰R"Fx|UuPi74UR՝MXi kO_ste endstream endobj 1151 0 obj << /Length 1395 /Filter /FlateDecode >> stream xڕWK6W(1ÇHS4 ٜJVQw($s7^,&)rgbaCe(i=1ba%1F}} yU=p:b}GN$㱐-l\㖥LEQe_A-蚡+ `)Rm EYDi>8IQ%7F$d=\Fќv8l# aD}# fBF1>!y_ǀD8Q3j{B1zK&&#ȡmRzVm y}ż;HF1VZ(ɦ{5aTV6Ɓ(D dd,gg4F$3H}]%gS."Nmk'Qoo(Ly0+'²{6 e,ACjF֭`ay}??ySe=4G* *v{~!(e,E/VnUoqċ3J/j ˦~l+lsLg'=Kٗtp阮sXG1^L3c'c$ڕ Ij@n[Y{nSdNm8}my؋bwE]{Qtp=ZS(z[ 3̄ ^y@,"Ż7ui j|5^Fu+YdilN W%q'"Dq5s!_A(7.fC+ݑ˚gfNM{ ?t\p򹷤݌/ c?[GoGoY 8yf[He^ǽOGjj/-ѩ | /bTе0CI4U^W ЙQإr@ uA5 Dzjil*:Ʒ[‘U"zћ0m@ޛʁ҉tɥ=[^'gki H!g706o2-zkQQmݮ_1~ZUp&>}4KTD;B4 Qʛ#> i4P.2cNe^/cNXxzT 2ES7C;,ϋ󲒷˛S;X zŬ 8&:(߶\Ɛѥb(JBw"T pL2:b|^\w ?Y?Uc3f*3o{|jhMw*y?O{ endstream endobj 1165 0 obj << /Length 1090 /Filter /FlateDecode >> stream xWYo6~҇&EPΠ"ǢzlBeZ!kE)o![td) 93!9N`zbvvE|'AIb#NbYǾaٕOF$@";J&OcZ˂I6c:"9 \>Ѳ)4gWqbP}l c_d`||[ ]U_]MA7)Nn]fMDey;{-b!AXUZ p E MZziA?t C.JJ@'p>e"+Z/.1G6x#Nh@s?e6H4Pl*J}YҖ .7{K4- )d vuGtɋhn A$XsqLfuQ1e&vZwlY: Bt~u^!\s_ζzw*{ե1̞L M~:B5' F6{Z ghEdzB(I&Xˏ7ٱ ZƚޱVt]Y;JKFT,?_޵9B@۾-HK +FL]C&U:Khe2=IEǰ4t>עJ3ۺ1pebF--~ww]yކ SO{kD,@IM].fgŽ->}󝬜LBIb-ADL* EUz:$ Qz=X P?zb6T^{3P咵d}r.!1T.=(Ih/vDm?dCxؐ$ю$ *U$OF+ 2BrVi 6~ihwO@.|s/coY^d$ endstream endobj 1174 0 obj << /Length 1128 /Filter /FlateDecode >> stream xWo6~_adr=t[кO`-E"e ȶA@J"ᄏ#o~= "?c4 AaN<|e8J2qxpMbM"[-K e떬V|_XEnkHIqc"C L2AEOT2qb uOQ$.ZԆ M~璖'bG).&^ʜ^?HX@"0ތ'!ƣ;8t/VZk̓&T'euV4ըL At;rIMKZ*1= Zl\?NoKlh`C3/>rIJyk#$-60TnHI5ڎC[ɲMe>VR ~oq BP\φ}or_Ί~foKKR[ kEE p3%)#/(Zz+l}]`/b]RdI-Xɾ%EzNm{ۆg?{meKva/ f;+%ZeZcwIKWkʹh> stream xVKs6W`Ci$fb{KZ9% LAT,/J8Lo= w+ltu< 0[H8 l>OH2:{uK(I!X124PMq<ٵZgMWa2DALT[6jJ%q3%gs*W4i^6Cb+_NDZjԷl~/]T],97-P_ }Q{ tngf߬l}>hf66>9cPlF"Pfx2@0b G识Qz⪊\IZ:K5 H/twBm]rnX9x^ʻ;?H5u͓EN@ kFy>`U/SQծY[MBT8d*_diH|yg.lqTcJD endstream endobj 1192 0 obj << /Length 1171 /Filter /FlateDecode >> stream xW[o6~rah$XP [> ]a02-D~ɢc9wn;ع^L^^3G؏0FA;IQb|p|(VjL?._^\=T)2by;΂8;Y9;+ؼv`k:JtB<AW-Fq9 NP 48 uG,Qȉ[Q]V);2G(oa1 d-e :3/E9D=5qv rl¢$ؠ,H{9" eUǻF廩"-hB͵QKbbZo-)uol=poYEVR3<4#}H!pjk.2?L׏QӃy(;u@YID%k!̈UYѭ \?\nޕj'<@ޫ6+3MI+7ԄJg283*f^RkE9[&`,3%~Ku1(t `Zhc~׹G@'$7ZRGN?|88F8Ys˥pP}>< b0t/]'0p`5 f_Cmg5JynTp!5G=yC𖈒RzZ\bu ]\)7f/Qa3h*}`e_Aւ/XK*}9nv endstream endobj 1205 0 obj << /Length 1344 /Filter /FlateDecode >> stream xWYo8~1Kꤊ݇=EQS0Vʒ*9wxɔ$Ne8x gGu/fΓ4*Qyt(I1ʒ8/')=zy'@i d̑qvrA0v/:<՗IZۋ-~p][ ZD Qxufvtu^"W;0nNhgv~75 8wn^9ȝhWͶ|&.>~~c.,H蛋8"#\y$$Zmf_⨂Mx%%nM @GMihl"&K(OI )܍QI7?3cUnVTS=.hLu#ll|Da Fdy¬$%EDr5^(f  XS^{fySzP|Uv[;h#?x,Gyj:6)|=@?7R? |PWv7!AX Ocu &\&,<(i(BC(Mϒ t/rct2eb킅p.vp6đ߿?xP\|y:; K6 YBUd@wBAD'v!ENYyCM\YR/ǰe򅞮|\b:8Ysv;c2\wX`NZ-X(q *Vt-ظRxfWNW@oXѶߵnwms7&gzdAR+p~YKGK'T`qYb 1c_x4y#m[_g'1<^(qkp(ǥ?0=>{7(8UAht.A%VO[mzouh+a||ROVr~|ڈzȫTƱjqz:ʩ /i$<8 SDR: 8ø jr^/} =~)C3~!q\"_Y.cxwIP %Sm {}wy=6l`mWq<_ZVD,?nocʦI\WG8S kRcwG ҋ.Z%4s]ZIeY[m__r y1PI%zJlMa7b={bO\no|[o+vJl!m}JR;w`9,jܢ5s FA/&g endstream endobj 1121 0 obj << /Type /ObjStm /N 100 /First 961 /Length 1696 /Filter /FlateDecode >> stream xYKoG#\z!IDCBPJUmx Ů]ի\C 9g !k - D D͉)` @PA(dPB 54q UlO)pOم)srA3aE:xz e`A֔Z +Yg,D20 Y,gdP<ͺHRJ50tP,UK9pW%d%Yy|SZZKk %8!nضI@4K` .H)H.! CHNE.5PbxRH|SE""͕:bί=J8qzA.i=L^P73NA +gUݡҕTs3]bYZk[v荃%^'Y]͂QסP]lhp@WH|+=a"3n٭ rUPz$PzL@B/lg'YHga"pѫكRGٶ6RbEOC 瘐Мs0 MfSp SAb?{ g.A!8{hko "&b'OUſxq~1? ՉWllq2.,NxO8HXЦU2 .d]d;p;`6~z՟plhX],O?Xob#R,b=}? /y8_ ӣxqVu+md;"u/h5&$^ХFMQ4s'E%OE3l MDD41kw).-f|{9#9v+bCAҢ@6$&O|7,?6rp441@4y0!5JЛ2;׈. A;q$s?訞j,MgEsY*Q4=U&z[%DH i[lJ tV0伄鳫#]%2zG}z)8ڕm :e_ѿ]7r1'iHY&k&-w|. eT4NdZe* 'bZ,e*S@'Rf7lv.7+յL7.NBJ_h(Ebկw':ɕ|m NDa_}`:m/FGQtucR^>c?w "WøЍø%)?;> stream xڵV[o6~-+] t-21IT(ʎwx-ʲ X\>ccIErccAd-"}ZfYon,:p\{( 8Zݽ[1`@`"c׷BPg8ۛy8iSҿ0O't:tF={+Itx6EnO0vab{h[7(L=!P\;qc%;T֑')-h5)FܭW–<_bq,^S_An%,_c&8ԃ{xjQq5Zu\4f1_#B,o pò<׿펰paˆȌ X"+i^m(+7+n޼QW-C`7$ٵ <RhV/v= ް.t9ֻq}K: GI0c@T'2j&fV\=Qcg#{P)^*~YCvdUT+²*{:"^ <6EZ-1:s[跽 pk(}' =\?v?-'8V`' \Я&[)gup1 \ˏ;ZDþkAl$vff'Rߨ{·zRO{f(HC/C"iN<y]a6 f%dCET+nRFjNhexӘ J.ԸD(煪l^4ԣ7 AkY@'/P#Evzξ7qeځJ= κr5ݶza YP1քώ-[a Ta?WIpyQ!\;h/$DLA" hp]}{##ϧB]5g!0(W]5{6pC\`iKiIeVwasrxkZARc+zO? TDݑh3ĐRiUTu^HSr:rLy1/gC|(8vlJTxPFL endstream endobj 1229 0 obj << /Length 1602 /Filter /FlateDecode >> stream xڵXݏ6 " X^TY>(W`fC;48vf;\d89_?)2EDRI:L/77I`XOJ0wdNO}:gPG>fI F j?a sHɜ,/IlS:}My\qH}Bnnkk#wE}dJ~wwTEiCJ>P^E.@ȫD7J*% w9Bpk)މ@qPӽ*gn[וe3!2r"0bz\) H?l|:X+ZB+aTT?ȁ&%ӷ (-h $̋菈`Oymfo RZ 7QsURBFb2}BHKL?^)7#gF\Tebt=Fb8j ="_O@"]XR<1j^1ʳ@5Z^ufj4L O3zXVa mU<O?ilk}-` AxhX#E'\a|Qvpd;8 o[̋8S^|,9?3F,ӫ*JVlf`"VfK.{ ie@y n˱Jz'[8.TzZYĔA&®j#^+J8&o =@kM?f? Z%K^P`^M\TW&TGN)6 I;CV6JUiL6.I:5Y$/"O4>t!k6lžc_ށSYAOM_mD?5J|+ѫpB+[3C^i{[~)a[%|W>I}EU6FBKӯ% pF@yiur{Od>ph֌% endstream endobj 1235 0 obj << /Length 641 /Filter /FlateDecode >> stream xV]o0}WXyJ|Mk'Uմc Ě,g>-U=!2{=o֗:9\8A# \ %HyO\o! P>B,48֏'vǃaVY׷z(>֤4Jpi|{CFx(&Zhx'G)v|4cǴ!Vm8D> pp^;e !h4+o,U$0 !$[gCꪥ9,eIQfI u}0>cQEMu}柆e%noDV(uuztﻖ#14֝,t{* *yR/[MQ\V,Z1u'iycfa|n Y0Z&]6ifg}Ns4M$ϸ50jaŽPxYNMҦRk[uWywPOfl.2"]A3&Qwb4=-VQy BruRE+޿#[q6FUC%phȶ1]`,iS}@Qƕ~!>dއA\R endstream endobj 1243 0 obj << /Length 710 /Filter /FlateDecode >> stream xڥV]o0}﯈ Y>U M<@rc'pqI).nϹ7; />N>&wxiz g7O?#ooItcn98\0E ÍI`|-!miǛe8 S Z#/a'i#[ CkGg0o^K!6E$e>"<=t>N+ W)[ǯNI֌#C'rsAGL$֤H_ޘ'l:BH![5lN?)&JIxF$\ش "HF5^.:&W%E['KHe&엩鏗zʸyR`5d/X+$G2$Q#F~z:*fb=Bk0UL(P2+>$]NrmR ]lBoJ9ep5L4r\XX4+Nah'f}\Ef>Z,v;t%::iS+Lͩ^MOxMCI)q KBRTY#Y'*/>Uh~Mɣ3*0?7o('dplFՃbU%歓]9倾%dzև/ $ endstream endobj 1260 0 obj << /Length 1975 /Filter /FlateDecode >> stream xY[~?OҴb`UQDVyyڍZ0D4F\沿>U.ĆhqWٝwor,g;8. q^豈$dS1pןCb@9`(T===ʢSߙG<ܳ d/hݏ;swUeLĚ?gb"!#:F'* #W<VM戒}Wٖ,<}9e]]gS$^tMv]haUDv=EwEjo㦪L)U?`/TZuJ|8z`rpQ ,x> Y0x|%M~. TIpuި?I&zP[n _~7x$9ΛSI-xNYbﵐܐ7* `2.(S]H&ڐI{ ?w?[F'Y9C\3 tuJB<Muzv;t7$eo s ^I˳AI}:Ut:YCWVl~ yi+̏pjZYMBN;Q%kvuyJe+(IoW !k,}>*tkc@U2v9\]M4YUxn7n[N.mz μ|.?p9Rͷ"N='vK"hvB7pj]v߹:8O;Xv< 'FE\Y^++ݙ l ` S-˼41:] vej-]-"Jwmյv)72[އ.^xSa#V3gA> stream xYmo8_P"mr Oۃ@K#TRJ7|lr,VBy̐ۅ׻}"AIHf=Q?\DJe>=IE~v̘6|DK="hԢT5R|+#Qtk7OEJݬH->'n_U57D p?)r>OG`Y6h}] HM!* =ߐ`d֥Q{k};V<%8ŁVm[dY=̏qX3y꒽vj{p-_|0'9sMj þ 78px/5SJYEUBD^;R4Ve䩃 I$o'//~ 41"Id} #,5 {tHjڨ=e,k7,=p%}fe/̙~^l Q=\5U )IXղLj (~k -~ rk_N\B.f0jk͋ Q4Ĕ>b̀%ݬAT@d%A ?mf# e/ʍL؟.^k}J^U*|>~KrUPUʟym q Ye^]E,+uR㐠:]4&Lzqhu}h|Ŏ{V /ԭ5n;;}I0M.Jcd"^g扸]J\[_AwòLȼ'ڛ|phN_y~гA?nWB0&GJ-TԀ;ө`Cx3HІezzi6lU}p6X࿣Vnwn `Z .=cEܴU}?4qM(Ak8\cn@? ֹ ߙBnRTz^~X/?: QݬHV":EၢznJ4]]rtNH7 R;fCj?اQ]ud>NVgG 8'[Y|P Q%}V'c C hX~?Q&}xt|+ؖMQc2nlgbW_2iKZ COTMu*ƮHDQ!nR!{}gSAmSK "f=K{CkiԗYVzx|u@Ea΢d:EftUv#C0 v0y!7~"OF-G0Ь=˾|r'] kY2 NBFUS0+^x]*nU6,yJh!l7!aO(B4f$(cgBHC]<@ _]dj5Aq#PHoV, ^`qQayMf\<[]n>5J?K5"{=pL` *Nd9pp".b%z;&YkZ5ΫBGZB5t ݫ (":$pKKg8G+vUv<20;I}RD$ ;w?ﻇII{|? W" endstream endobj 1297 0 obj << /Length 1104 /Filter /FlateDecode >> stream xX[o6~ϯk7[d]`k`:q1 %DrA"H2sx.߹Ȏ[u}f}v~/؎C/֙:C+ ;Xz:x[ )įZr~{<ъ l@9 NۗP /wqj=֊3~Jzz8Ϋ`,YSn"<1"Paj%%V(S+6Q8F-IZj% Rչ.u8%qHr<^\T'(Kb.ȵcW@k8 vC )5pşԊGCUReY`Pˌz*8kE*r8v(MÅ.| 8 d0k hboMH!w"іa6=XIdɦVlajx`Рo`Fߒj*syҹ0|NS ֊$C;tſ/Y/.jm("L=܁Gc ~MzBrNɡr} Bbވzr0N-'#3YIbv+:/ x7?MʤӲsi#?~%w(G&b;C='= F6H ywRJhf'NS3byf?ہT'\c3oݴeh?6GRr-'*MA hWUǤza<wx-/* !yv Σy[ſ\{@&9,Es SQfƇeUMO3igLpވ*nh潗Jps3?w4.0|QV(\w(P~ & 1PjNݮAmNkgTSŧmC7Ĩs?0B}y/fLgzv g1ՙI>7r)"?cPFCذ"\H7t&LP~lPI ~F sqB{GvP Z(=琍jѯi<p :8`J~{\Ͼk endstream endobj 1303 0 obj << /Length 988 /Filter /FlateDecode >> stream xڵW[o6~ϯf2ևnHZiZlT/~HtDYݥ{0Hsw.9^ #'u8UQ$Yo?WoG#7!lrˬ"Ē:oY8l0Hd!V-ogs{S `WTsHsp& Q3sy8oʳdqP|԰pWӗ@˟yA໩ޜ IS[ x|w9ƾuKl:-gLshؖ&bpfT C^"9(]ʘ K6st.6?y'uʖWNFu9Glljy0'> Qݨ!0g9ʷjudl쮲$:g#H:]+HhA{97su.M>lDM~q9KZk ʷh f%R.84GE`2r$h5`0nD W@Ow_YTi~rww4_Z["R&RRf# Gp/it򕬜&-#0F<;;u}@sĮךR[`loN7yD(]}amj,ӑLc \.VIhq(mv<s~g槁$ϴ`vGW-`+pŌK]DI~iKSHIzN@aJ5Y8\%tE]Zx:d TyԽZli䲶piW)6n|HF66.9aUAPou.I$R s >|+@G#;q^hs  h<3Xxىf֛տ9P endstream endobj 1309 0 obj << /Length 1445 /Filter /FlateDecode >> stream xڕWmo6_!@ko-z [(bk [[DDeI$ưEJ1ms?<{'Y9v|4$$3_:C' =P3dJ˺~]S0bձbyFP'W.bvI+MM>{Wo"uYnК9(]b%AE ׄ/rS1-IȲ5OE~k?2yЫ\ Kٻk\7~ro׼"r7 k$kA4]AlҢf)dz'myUe[?*kS&Eu梑uj+lc}Z/&߈Mpf؃vҳ!ZPr쀝"՝?Cy}~.y6y]ek:tժ&^U@ڷFU6)r*!V\iL "峼Bbc2[Ad*/=v,ZOA佭u طmٷ 0ia4b붸|J֡&i.JtX93\ksdndŴx E!E\B#se_rRw&+QW֬'ޓileCYW5*]+D#?Ve );zl8.|8K ☦-x-xnK-}{E0j.wVzpSm=#M8t>+v'Q|mB,G};fdpN CǺq|  ;7߇nFb `Dc8wA?ĸ(vMxnf@W2Y~aHBǯ~zl%o7i-Δ]'`b(4ФՠI#SL/*J˚s;T3$x룮#s:(Hχn~p^h2h*tJx HhU~2Oi0jrPZi Yi)xҺ7mtӾѶCaBO_#Q2|x YqDXyɊ_UXh[Yc4PԕT `ȏoz!k)FO. )!RVf `0[Ԁώ]n4B~2aPQ`l`v$N"OIPV0MZArùe`X-6P& H!QQ@NG2Hj)TضW#/5ܪu]NI0ydVU"DGzŁ)uI|@NmcXs=vأGGPSy9|eb0>O>xԃd &lG܇y̙ۼs~Rx]ae endstream endobj 1319 0 obj << /Length 1963 /Filter /FlateDecode >> stream xڽY_Oa8kDRTwmfZɢ!"3$%Z{0I _nڭ}UA Aa1a\2}%J %SXݮ4mtbnذ4#Y64E674wc#]"/IR>jFQ G׻&EG>/e^?E2P^%/Oș. ='\kUu@wϟ^"^^4iknkY~]5&4ye^j}}i -SliNu[>'TRNn'{Z>SOԷVj*r4iB5`}4W,RBZQN? rOIhw:V g%9BrM@ Rt>`<.&;zhAJ\:Ⱦܸ4M/u7 oK7A @7's##Nn6YExpKI3߫%90LƃnҘf3J xҭ"3wu]VyR0٣ލ_@0d%fM1 JYʫsT 40-{ʲWh]eqkӶ4d}Rn8wuno Mv0R# _NilJV$|w~z]g ' `)'; ƭg&rmӑ< 749Ja3ńV߃kS0H|N,>suM̶[b9 UOh,_#iucHv%bNGG)Ikt0,k"ege:z*V"²KՃl\~UjNRa=Zl̜gg8BEBkuhuID#G%4P Y}lvJLmd$&4NGf v˂$?",?+#$'@|.VdQZ0+H N@1ğg J|-FC^ߎMsKNm\TWć$j WnǤnqot:*ʓf\qR "}xR>z_x]R“*YD9iox^-v.ڽY`X;1R1E]CWlOӥ#-&W>uw!5;Hq^DMb(Fmi3-~u18`Qp8.~~5!|4ѓO\H@ \D) Qt}Izws:z̮9i fq# l&؂M8$+T?oFf#/~É|] &y0u0)[pǂ0N8Ħ9#ts^a~3w endstream endobj 1329 0 obj << /Length 2143 /Filter /FlateDecode >> stream xڽYKo802u1×^Ci`t0ږ%A_U$#vwc(*Ռ>{wu+,!IÌQJ gQHI .1_Ҫi.VjIbe .yGyu;,D,p{E!Ibmdr>j_76{n[M.uڴnZfd,KraB_Lhǽ?OHB9M:-I.g3)1PM*GI􂖅*NJ$ud} h`!RO6yy|Zr\:s}(8,xAzp{=Ԁy!o%9-$MKpM-1aG0$A}-Td-6 ]=BQD(9"PG(EѠG^/v ޮQH;#o]Vu"AMFDh ^ȄIA)£W' hY9#*kf=v*\qf;*͙Bީ.V~5RT[gZ%iQ{BoV 1 #єq+-z}ߪ~0" L~z7Nò9PHl7٦W>t##ȚablFXҗTXri$!KnDe>=u O$R\or,Rm~Zgd;|F]t@'"Ou+ zxjs˺QY0Jn oO/=(VX/ء}7Ob ##o4RIţy3_[i%]-*ߏŲ2|o,W{)Ɂ-9 I`i$.0)O6cfy\XWQLiLSIPa\XNp'!4ɏ !V j.Y<_A7F^y7{˷N|.ժ* ۭ rHbƜϕnwNa|&Jw I 6¡hJ%TzR l3FÉ>mB|dܗbi8*^K}}q}"{ڃ5$X`io8r\)kWΈyUr[t 1ՅuH][)+3p՘·// ;t`륛> stream xZMo9W\$d1@f`$Y`wd# # ;Vd[%0flLRFYgQ,%y@&/=Q) <0>plU Y) RlP"95thA,>˛Gx- ejheGF9B({ D.o$b*"|P;Kdъm}bsdX7zĒq0.Y%(c mûYl2YWF\)T6&E1CfM,%lb'2X$Gp vM*oD80a&agaٓȱ`AdE2<" L1bѦO=41@YB=\]M![-'/g?hovkqG۬ͬUz{ON!vO`r'{4+^:oV]ן߫SG6~ƃ>~z.$lV}r>PVY_?6KHꔿ`B$ wY*^UWmf~]KK[~!p5XgDAmM9bػя[Qؑ7h4lru=y+j޴NDzi9=c&Ȳcw;Li"tHш[ڙ;֞m^Hdu#tا!di0 `zHϑռio>ݶ-!"A`+ʬ%X'd"J3$'D`ĊIӺQLKȸmƳ>.6w}ѧG#kI짥3M,#K;.6^dtLI`8{*$J"om<=oO0ϑeFp!}|5RF0]v 5LL:ɡה 蒶auF3ji(-R7 )u4Y֟7YI]{| :CC@>^@5/bU,z[$~ku %3ᢞ@FyŖ,!k(JXj c," B >ƳeyC*b=ZO(?Y@%xczJ~l"QW)th0lH=Z1;]N4]N郢$#x M>a =2qǂcAܱ Xw,; _qǯWI\(WU"ȑנKAv\w#b8ۺ|P;TbYEKzuQ4_].nt0[AA Bܐ eE6$^7ף47FJi՚o>-QwNP"vY< vE= `Xhh>j,ʜ(%Au|%?^zr[gMoc [{DzAvPR}ho8YTNicʁ'{ j$R[T;rDIJχè 0+z}C;> w!ujPujPjx?nz9 E-'S{Ĵڛz~$!6U xo `R  v?n}^$@4?r s 8Ԟ[i endstream endobj 1339 0 obj << /Length 1036 /Filter /FlateDecode >> stream xڥV[s6~`ؗx&@qgvN'l&xd,Xb%W7bdm<>}E^Eo[zp'xq ͽYH{ʣǛ ҏTպ 6x /A̔cndQtuWAkvĵâ6a^ o!C<* Q8y3ʢ*)cjOk?_k Y1cM[31V?m8Džo;.~v4FjA: v0߬n([5XsAښ Z 4r"mYhǬvρ1@=b B(ܚn'ii HUOXm!f;̑,7x_L>MbK/m/d+^<=GZ~`^x; zi3 #'ӧɰc2<0}VI8/cm*ېaCN(M^pX5AlgW#dRS*ۿқYm(3 f!mr$nSTJfqqXm;Kut茴Bsk1{o=ŌݱB|IvYqȀS3S1-kQ(;2= ZCEaf#VhsB\hTy([H+XCwŤ*xb|\qιXWS#\%%vgo)rx}ڮ \B~IrK$[;2EF=umkVne51SҎHJ0c1/wSڰkCq:ӇިE/r3}=ks1ϰ醷[ohƒ.Sb^6_oPÛe9a)jyi٨ahaz I$S endstream endobj 1351 0 obj << /Length 2646 /Filter /FlateDecode >> stream xڝَ}H#a $vz٠Al[YY2$ySŢ$J-Yd,v`?>|9O L"X~8 8dWE|쏟x-c/"A:VuWeZ X~O=XkYx~GD; 3vio XLf{}bcI+B m3; խsD8bi":p@̴85ycKV\- lvO\.-58aWyx nw5Y:/mvo<[ymٷ1M5 1fL aXI 8ּΩ] }5ơ ?,jĖ!(q!=< '{~;OsHXkNj D8q"gUq: H E2VpB1n+ݛcI%'f'зPK] 3 /%}9CS :!PW@D#n)3uu "T9;ϳ_m:scch ƙS6=#;JRtqz㚯мe>>Q]enzƲ1M<7_59 нkݘۼ9;;ZݕUi&,aGV qcǪ@c7RD;7wR=Xi^N]~Z ۰dj7zjL(oa)<TUI7MzC}Iom-1;{7v7@*ϯ[PB >vb=_ZpzHY,?Obx9o Fփ3KekE$DqW(JŝLcraCᄏ 1Hf|PW\nU}8 " !4 q|_t&)SKAiɬc:H[4ZY ՙLY^x}Ԋ̅tb Ls F<]khl20L 5UT XDO8{1QjAkf[D9 * cI$橝Gh[b8 y1fSwgɔcW k[\ co:wyKCFp- aԲN:[jog͈(Nn*ġjۻ:1 0rQI'I'0b2$x]Ej9l^~< CWճg<_‰Y )S]ҵu73ADjB@ NaD4G9#MQ K˿.Ne8h<#-;Ų Yo΂G༱|z_(= hςtUPQEx} Pg5LQ! MVe|a8 S,!s{v0YUa e0$iU&~Yۅ]H[rו% .{nkwN\_QeX!dPE#>OLYT0Jkh:hPݗȦ>H0]~=Y{ SzZ(N;Tޫ3UXG;wià,\a9TkUϖ ԎJ ͖DɮU:A sNlR$00'`aB>8eU$ W 8vCbc!D +Vb[dOE!yQH5}[O=^ YL2%?Td=QV(/o2jE^g@5Kܑ#cA('*eb(-jyq9!aX>(P(vLn-tc8Zw ;AۼTO2uFy!WBA)n-֣s;ݎ54a E;Y d{H~>.%;ed`T[#N3/Y]Fbq5loߝGw*K\5/k\x§qdZs (F4ly У4^q)?T6whmc@;b5&,s|bЉ"|M(SiJu}OzOe^bOjyx5\X_U&XsPX#tgKqOm5Wѓ%Cfm q9\#Ew`3.rf*.p Hҩޘ7OKk s)L<\' endstream endobj 1364 0 obj << /Length 1819 /Filter /FlateDecode >> stream xڽX۸vj3 )I(Âi[, 鶸f8C=le\?q3yphg9t;ϟ%" pvI΢n7Cw^N/E 6=sX8H_Y![͈xl+ݐ^# X~lʚ !YݫhH,@~a{uY5̊cyl@J|$%`"p曬IkjEf/ԵX ߨt6(JJ)s})y 3&kx=i,PփѼi؁*n'܀cUޠA8<7nd$2ݫZiـ)|.k]պ hcHA GMȌ"u}Q*?f"H]^.VM.rk%;[Џ0PfRC!tTCp A54  Rݺ+tjdvGLtv&#}{oVŲjM>6V-2NRƏlj+H꿔u_, SV V 0/G-[7 wXr"~&1F܀㏍=؋tA)'@k\׭KX ^CQAC 3>uPqdh وϠGPzT6vC1 w qIFSTs,ܴw:ߓ_0, 2~ږ|2;k \GZNV3;'| ))']tOh\8rM"q^O^t=vƛ#j˦sSc#j^6I &8Wu9z,@e+cAL̕%izhTŻz=;bٗǜCe8\0Ue,o;u'pgON{UU,@>in2uTv;e+ܾF:a?1M M'qS.Qz4kyRA3wU.9'YU]_Vըv AKs3^s rIt S""SoHxIcMt{MM,uTŇ8@o0|_F/>w~U<tp6S4pk*.0@Elc˯hpL.>֕ tك]J:~UՊ ~7A{mO8I&$ Z5z K6RN5 WUX_(8X]M\\@3$vВg1תi-zm>fC}&U(j 5d)ɈL8C&дGx'׼ t)١H2!C=6mzjfL019LxQ+6+4W`EeohpP,Φܹ5@%0zn{qpA F AJodN.+D}inS:2L?\[ xX}nb|^wDhs:Uoe=m85D@GۼL}uLqvSI֘f4rą tG]J1{%SdSeqgp~$q=>`#Z_5F_^[ r<ů]9Z\ã.ط ~M ո⸻u3&At\na -nr_To'-rgl6 >=_s endstream endobj 1371 0 obj << /Length 1156 /Filter /FlateDecode >> stream xWo6~_!xؐ`,!ME l؆,H*%ERd >a/:J:?w;^o[h歷^x20G:LӤdW? ǓITiZ"Pe0Z<4̜h2C]z=ߘQAVĄ[R4BR( Fᬔ$1 rRJጥPi0#!Fr6-9HhpzF( Ѣ^خ‘:.D/Ф ,b, Ő|#Hа*D^ى\Kt۶8jsOl^}ڀ@=vFʜ>T|n&,]쥐aVR٫66nV%fwo ?"wre+ުy ļr -g6H2c:]! quZzj4Hzp픱8N9Q/9PQG4޷{m`gnzw^3&'ԁv4)2h^v8ん=J6ǻckZ_n:]$,& 쓋 9 {v8Ms.yor]?Ȗ6}7 uAje櫣ݖBRlLeg/^`B<&V?k/r^V__>C^ږ QҔUoONG-@pCnQwkYq Q1:sDL2\yO¥DfVS) U q)`wIWkgg759J 3p Ee+Ai!A9;ͫr1M'I"a˝PNs4E4q;ۺc> Ã-f,d9ǬsR݋yMnE9qŽNF{Yuj%~Y$޽爊&U I -뮈]zsUt]&M0CXO;V3٩5`к;yVE endstream endobj 1375 0 obj << /Length 820 /Filter /FlateDecode >> stream xWo0~_u> stream xڽXn6}Wy!%6 ,x -[ECMY-X<˙3aybrsG/A X{cPE 0E=M7)_ 5{^|  bʲPLps^ jyQVJU;q.6LQ]LeU9O0eTU\ѶysQ&Fu#7eVnVZm1!+H=c [gZ£谗{_b߁dxaC{}>JbaP}:1sawrZj6C<-jw~ԫ P5[~<1D6MNT E!-wÄ3'!WMV,:tcCf5 "v$ GQv'($99z(ME%,{uy%!cV$_?b5/]akhUۢ;䶄ڸ6+{ԐO<߽UV*9!t‰{ Bv hz> stream xڵW[o6~!%Q`ðnݖÀ,0TVc;)Er]Ws'l$6ֳ7k6|;clb`,1\#j&2n玳zym[N˥rAM˒$[:OXI&@PjŖ+iJxUHpj6-an%uKudM,JjR^eA-\M"4֣ѻCLtXFAkO ܹgRҀ rmbȧbۥkpuU M<<S\wMZWAxL.˜: lLt͛S=;"_r-jO:y;N4f>p%~{I}A)\Z^z؆em"PɵrJaR:k’^J삢FL|a7<-5M˻f<Ρ Z[o9U 0\CX !, Mٌ/*VT^TP}J188?Ͻ D $~K5AYwswރ29;ծe&պA^WZMsAM^| y>܄-O<#Z Cň@ͅl dA][ܰ|MeQg=p±aQz5'M{=rȆ/> stream xWKo6Wa`&@-⠇l`0ի8K(^{ك!f̓#pn?>r7 YoЉBs֙p%Zx\zy ЍbLH@@ة/H);\6a嵇 <,#7N2E۾J;ZW_WgFYSW)ꎩe]znS>l^?(q2c4vZu8!it>I(pQOKμVM7t\tŔŖ}7A<6M0A[>*<j|uG\ʻg Q[lI֖4!);ooVgܩ FIj *2 h7hX񞑡yuϴOik))ǒm"\yִ)qӌ|e㨧VOSSxT}ݳI{ON۬)yLq[j`\C<r8D%z5X>ҏcGn2P'BzVLBH#WuwQ]|^/YI 8p]4HGd%$v^$t|(F/qσ=c@ } t@>4BI> stream xڽY_8O$R6f{]͜4ZvzO;;HN" ӝoU.y8c*̲Y0},Y’dz, &xIg_X/#qA)d*pɲ}3R >-K)9[rTRo/_8c,(Wz>ǧ>iLͮz iG#/+\G7~~E.]:TuWD޼PWYmԛMQf4}&3%]dH/zjrbcyvܛmz10|b )ܡQ/BU}y.\jKO:ۊ+㖎6/҄u.ØEN(U1_^r\W .O̓8/i x"ʱx:Ltr3GⴙqLn+'Cs}prp4ns"sSR|5mg4o}/IeD9gQ{"oj^ɛCfyAUS3/";h׮>nuȂ *R, T?~(MUU&-N[mLk}^ZԞ ! Wn76~DO`7EmDNoiR~</7Ҹ^`r'te#~#ʹ֯'zʋlW!rzdZ;Cj:v-5Ƒy{dUz H0;7EPIF Yw㗯)T8JwK;Se`*${ߢcnڂi3u䛈vᵚЁHw2λA0.D]:8.pfGRJ:8Yi)kH '= VUV{[mMk*+PmxҺQ0FT0Blhta4R$dR*1-)v7rUejWcLy#^EPăBEŋ(i;Τ܎PK]18Z]޶ ~k6`v1gw(x6 \3@7,&:ܥT=:?rԩw wnn1tQCis)V2=R/F ـXkf,MkdABu Bg>M'|rj+Y"H옗j;JHAv \K6-:@UO1QWaf,±` ['/>sˎ-0HbA_G)cřA>UHCSnX׈y/bAZנB!ϙ_#NֻheiIT)vFJ q5;y{ˈ|;Z#.~w]r^kHon{=k)W׌$kqxWjE2O%'WiMyIX cAw{{%ye sPs4J]L~c$N[qq!4+(NǮT|]Mh^\VV7oFԵ1赎&\^6ِKhy,3-¦=c,D¹?_ga0/ (۩dP8dOh nPh>zdToڍ>E)k f9ܹLx048i2vR؂Fs"ȳkCDāY@-Nx{aۻE:DZK'ԥ\Я푏`>}.Iݛ7#` -jղ#+"/;:'}?= /(>= endstream endobj 1424 0 obj << /Length 1806 /Filter /FlateDecode >> stream xXYoF~ϯZkimHcE$<Ѣ3;K)'m {o63>;ū3ϝ,d0^Yprv}t6\5Uz?|3`a +/%ߎd\|!Lot< Geu Rl\pąto:ij:Y:ҥ^/r.}玖e5"kYCR0<>\\LGJz^{٩l|ع&V i-<&xžOwe6iY"N c^"[uZlh첲}ߩi - V|&V]C9u6-i7hY6Mc1~?kE}4-z m rMcM|aBՁkcEM7;ҰE1:I"ع'[:RO/ ;t4vIЧzgqw۔]eĘth`5 uUhb5YZmZهUҰP-Ouy"bcඏ8LBscqaLz"*%p#Ije> stream xڵVn8}WEԥ}آi( Ffdm%Kd$P86}u.<3g8删7٫w\QY=TR&9HDCBV.Ͱ) H}X"IQ NIdme°IMDϠ^qp6?"n[mF׹Vm!?ǏGo<5ad`q9zA?Jm(πT5Y|RPW$RN.뮃1N]@c(c.zE3VD@l拁o~ԟPu%RMS2C~rҮ%^i,6vQoL4A};<ʼn7f&3%  RgT8)F)`T$>Q 6.j.ԂR-7,תn뇢,`{)RV65 MhF Z+Gl]J`P.O/ ( Mck%;i^Ⴉr̾[^@ײQ/e|XivF̚^ fJ6M={ 僽~-U{B6mr#եɌ]Z;cvu`a7rk8 ;|jg,{tY^~ 15MBLB~Z+ǛF|% v~[Ou>uyd- =l f endstream endobj 1336 0 obj << /Type /ObjStm /N 100 /First 972 /Length 1976 /Filter /FlateDecode >> stream xZKo9W{z.0 vAI41ɐmLWZ#vܲۚ9$fɏf2321s ' 2ၛCRN $` JДZiX-(߸4RV|SXo 6a8}eˁrcnfϵoF*)4G^0fm8IET *>VGS߭;UqsݏUS`|5'e= GPHLrr(@tѱWPԱcw|q3 `1rIARQfi@pviFAPS3<%%P&N堔4 @14b?i0RrwNՒYp.xa%`jq&bL]LH[A5%XuS_>BI|0BYV_Z+~&PT_VQ(V/0bgPjGXs#`w& ns$Tm&P @@)wFwZ#V_Ujڊzu2!‡+\M?FE gysqo~z^k CL{j<@]olp$K67yvqN^ٻ.x1?[̾ǶOfoW&->Ͽ[} 4J~5J_'ֺJAqXx/8Hnpn88Y|WqSʆrEO7a%d t l @l͇דwź#Kgsi~{TcK;=S̙m0qne狫@'#7Ep(HԚ9R1\?,.. DwO:I*"H~\\77l}iB(G#t9vNU:b9! P@L0&/(E1}zu^\]uL$!Rd<=sG4V1' R=Q?>fy~JM}!Zat@jr7=;Z4$Jst(G;/];3>@ỹ`ыqܨ2z+gO'H8֟~L{?zQғY1<ۋP:mK4y کP~!n⸩Śb%Q+M!a/w,X ⪨GeZd{36FKdz[%W2|ZVԈ8(O7 QBeZ9xּ]HGggl΀-2T`=U= {3J^;Uy$_nd$7#`s+4['5~voCPo4m,o!.+V#joc3WX&o[Jab>nW_; lɍ2Q<9D=i!"!Ҥq`ʶm+7Y.oevx&EA_q>_^펁!YNʰ%-2ܴ%=ےiHm<$;ލ Ɏy dRkU [(n bSv匂1޿H0nG@Rr[kYkzD$ ep_$`o$"͍B%DRmg-ah!qyw /lVj$w"TGr+̊@NcƿHw?M: endstream endobj 1450 0 obj << /Length 1365 /Filter /FlateDecode >> stream xڽXIo6Wb=d"fBi[,^:̋Q-=1b<#QoWMTY1O'~csc!$ڮ!P+7ՄjyS;WwQSnzE^%zu4l-k۵1hL+L?b:M)Qč]/1(ɲ AQ:R8R9#j f5,Mi۪bB$B/Ef4WVsg h!R(R#:m7jZ۲abV{ce?E%Y}ݾKlk>:]E{/{w,ڦtMˊ76αm;fh&3mPm M 0**mQ T5"nz4 ZVU*+(D$l^ku\jKC|¥&?<4b"uP~oII\3` eQ{3KOORcnIfC_HyK9]!oοaqt9#ؒC /F`gOPȷ򄏋>3 Q.ܰЮ1uv\7[PÛ(4`ÚK\ @6ǹP WLÙ a8iDΖE5#}- ]G9Qq!Qm֏e/\Y@unZ+"Q6ȠA#3hZ8P}(r ;)tJ/LLR+Tu\u[+EAyZ~D GxŴ}F-BkikL:SF0"} :C5\,H,DYA84>*4 >PP`@AP[بCǠ)P0F')  $͆s:K70٤sidJ~7CDҶ:R7CpOoW-r(0`;gp( haIף|v>@C1Ԑ518unQڽ[fܶ!1ЀJ  n6ư{f8NIPHܸ}J6e&.À a}J]$ji%uR&-K\3 )d&1'Mr3L7HҽwZ %?j޳%rlw1EoUI;EA־0c@>@]y=\l> stream xڵVK6WI2`qB{HE^xsq C) YRn}"镴r99oFC tup3m3D Q,8C#9[y|y %Zt:']?:5mՕ1nG!=u4.6&ޯ]AhҤ˫r\Jp$"Zgw| dv7˴*^=hܤW>vInpl>*&9 .\ XI㧬 iUvIn,uR 9Eq5b(h8yV,V}Wg5KUs%pFcµnVD/a} :!8< ӠzF8FTO.TC1){orIpڹ,KsV6^8v,ْ;, x&kO8՚fG0|Mo%(;M/4 8éd6ym}%HP@HY@(P0A0c&c%d} Sn|?I^Ie1 UPw8Z9K3g XX}^mHoײIeilȶcٙb}gZȤtkz:$Y'd.xv< >43d~5^h F`/) 5>B ecI%04k.ry_ǼiWJ=[ıd\mpS)Ӯ!̀"p^GI~aF/SݼP &SxXG=3˼}Ӣ.?Lߓg I endstream endobj 1478 0 obj << /Length 1001 /Filter /FlateDecode >> stream xڝVێ6}W~&)V) ,q<WR+ E"6,^ 眙@ mmC,&1= 1Hb]e1 g-0I'JB Y!-!0 o(' [hş.&vĎ$(ۋȟԱ!0K3G.]IK%BHBĎ ifC {Y 5V扵;t1]\؁"3(M+Tm?, vMHp*ԛ>ma+%Ǒ|cN"c8bB$jAi`dEti vN?' 5-jֺ* Sd~ps(B.=!wuP},!T+₩MtMj$TU8{ԈgOG^Ptʼ0up؋;K[@ÆLg{jv)m<Mހ._>x J<Tkf{1yg_WY+[esײ!Lf ݳf$ Gl<wDW3, kzaCnXHQ3[V6y@;I.ԩOtB[ۥbj?*:h|k8&*uȿ}Ighmϵڷ祥՗%y? endstream endobj 1494 0 obj << /Length 3038 /Filter /FlateDecode >> stream x˒_j@n7iJA$FM2Iݍ@B^SF6p7}zcW,VOϫ( Y³U,MS_O|'3Ȥ, 3a6V&4.xse( 6j[~ܾE1*XV3&*ۇ8Kn*Gl` +V=b'd ,bDIY n*́( ^OJKZN \gavp(Hp.XB 䧩 m,U[pHAmYW0hd'Ǿ6Ǥ^S_HA l^}q+5k?cZbd[]BfPEbЩ(f`IXxqyٍcM/%"Dn+x`vT`IEp^ȍ֤9DziTbLA#/uPOA+A zaEf!q :)|\,c39|nbIg1K%-VeHlXj9$SI,mKe@.vzH`; '%7:ñ6%|OQ\ f(7M%FIbQ`ghp lb3-H=:c[E/D(άW ԏ4)ihw}GշYѮ5FSl[Pg+Vz]b{5ͮfumyVt"L'B'h=t#M\Ŗh6ͩf۹*s=-Dz6WO'H6 d4:%4`ث[Omi[TMi9rp5N` CnE´]?`kH( ~+-y Kŗ ,E] gpqGR9exT߷rZhr?|aǪ((;$?Qq)o_[t5 cZm:"% iҖXg>KjB{gc|UMddq/x#JE4J:gzʙ~ y yP; Pv:E\ZˮisyV`) 6@Z2w!-$Xc.fhElW8."8dXz?X)ܻeh ߀s5|]\D'Y0|k}景/Lwl!>ޘ[țvG(kHXtbpgseO¸5!9^Qڅcd٬p"H;K;#d endstream endobj 1520 0 obj << /Length 2499 /Filter /FlateDecode >> stream xڽˎ>_aIsf$]$lm+#bz9 Hr9n}}$79lLF&MBKy(7V<|^FQY&+q˻vbyNfD0::cۑ oz4ay&܅Iow"š}%]pjHߪ9i`A]Ѭ%pRO"k">E?E纛x' 5BѮɰi-ԺYa*X˯K,emd=玗|I%QųcYsB,^Xյtn4fߣnu-NFBk"0]WҨF u]ʃ+@jpеjF#'d񛌐T%$p|:rpx F֌sʊ`-_[-&sUƱ"_zV(KM4p4QĒ&$^4z_ݠ)˜e7NNBe]` .|H9|A #K@~4Ӂ`Cǧ_{1W[\FUDc5 z53 D|-V5Vbz>D,Xt-J;L^g@4W{Fr0|nʱShN;1wqhnB<9ma7fxt~-KHTD:5gbVt-z^+BO+=0095'Ð l-PJh81xڙ{HH?|y$XLڜeos g<3].#oIadb%|=9sN%P٣/,/ׁ6ߝ~YHmER]fu%m$ep,Su[ ,hsA>Gxs^ zRƼӨULT'79Dv1lJ(| rh>Ek:smHķܩ3HrY,db! uxTp BSkI"eD]GxF1sYu5Vf]W ݦk %j^wJjYFXJ+q)20 Ebl=!=FݵDvSeÎT65n^I45᷹ ,όXŊY`S/c&*pW(r 83q+e`؍Q6%bup2?ksi 9~%J,ֳ*[?ck$#@lYWQ tk4'3"ͅKќ.[SPj+7nDM:H'$$}eH}QG5qx-t+.&(9bܾZ- zPwbERU?5c4,J2EW:M)r-3kٌ۩)C[eH[ t@z]Q? E6= Ez" v :3(ˮvu^%rULsJE\_ ?N9?%] 5 +dk3*[Fқ(3UDA{R Kb>)ETko P;{rU/OO#AUMu{[SH.lXM0 "PE]o[#Ӓ%F{һt b5gS^v͖{'IN '톓96UIgO%{ yPƘyLIgIxw%|D(]k+2|m+1Sr7GaBWmљ@oJ4t-M#/hdQWNʹ;4mѿ﻾T7 @Yۖs\|*Cu>w(FjO endstream endobj 1531 0 obj << /Length 1409 /Filter /FlateDecode >> stream xڭWn6}WC¦I[4 P,&-2wu$ү"EeBZ3J-l~׊P۾uq}+1ۺKE_(p> UV@(8_vQESSN_==*-W\Fo&`?S6k:bXIyLAad2}EZW/42J!x^S~Xy/nseqBjK&2~6[ȷm FtR&2ػ}-)yс0`RvF=*_V2U q#"MM׿ALYhՠ2W}|1+b5Jz  QlXi.EJ`j~VtxV4}>pV^qbv>BRϭgU23ByAs422M+26;6|l\[?h@`4`"Jc?/^er|4ΗQt?|:V9+2ɿa}riV1H}?xzaiΊMm298i]IfyN 1GP3cvT+*LyLȱ@;s3DMX}8监ŶYӦ5cEol~y頻(ʄ!r[:>9A$Dk8CMoIc9@RjF # TC³?3"gV /inD<ͬ_G -‡֎,qИf(O:*I6vwQń3QLn+&y%sj>op@iyBԦrѨ}# @x(|/SH+a<)txNIDqYui$[<ViM6hMUAE !=Vl.SRiUU@n;nGfw }MեbFόц)pO $Cwqr>kݵm\)D|-_ojN^y!1Va>rH(&;mF Q [ժ3 pﭪ:#qC1I i\%'+=5̒T}>}q9I .6Y endstream endobj 1549 0 obj << /Length 889 /Filter /FlateDecode >> stream xڝV[o0~﯈x R1ŹLC'Ӧ{MLpk7NBw)|߅4#gt<AFNAYΣ[%.!@U ryHؙjûjyLgB>T%<i0fp4.XU[IQ]&j[m6oGBK 굅{ iVtP)HRkX㭨ʌ $:\`BT)ƅI]Y໛﷧ٖ__rLi,~6OAa=3p2tj?isk>IPY'%_I膢$ *iBwC))) ]:>]0Mh>@w2zr|%edrwi㺮^{R8.f4Z8XՐv9M#?ieoIM@g3A%-St횔2$~z Y8i_MXGȭI}όfĩF}#A'^⵪_< BDp-oi\8!we{矈THnpOxft<:M04UQg3Rf%)R~S-3o԰cMrц !iv̆p1emƫna0EdߵK%a.U)ktR"r(P*Rf@ȘE7>'dDsg%'bxNAE1c$eD\jʛ9qnpo9 G6 QgJ\tD·> stream xZK=K/iov >,6';04ݜnj#;bzFA0,V}ۛ?yAe&1L*` gZ&?lOxAL*f 9]aC2^b'?[7Y=#I<-\KeqWuypݾb;[=7zua~S? غ-MtcCDPL3%L$Ka1ǵJ̪u}Ӷ;u>uB2bZ 2y\'*]m]eߕmvo6ص4 o{B_>? 5ņF~G>s%luMթ3_ fLMML8۰]g8\?$p{H#nE8ä1i;8S6uy:T~]  SA NmQJwO vu݇f߹W돮vuƭE6 }y S QE1onDT'GǼm=]ׂI#e~Rf/!wذ,Z]T^u~^yS/ |`YDO%Cs0/eBw_YFWNq, ~c^mC?r?Kȿ-[ce0sunxt[0lZ [OzHM^Ll;; SqEYl_UPž-e&f˩XoU,oI]hlV fhmwh 7"Cw| si┎N)ȈɅ=%qrL.;-tV=9|pem ^ &do1x { t˴gT{s ><@[|lzo7?Z!Y2c{=c:jE&mZprus'jao7{Ҩ=S%{Ճ l)ԤHdt<+LIkO2LGL'<2Lp5 n2 ƽX@z4MotuZDGtC]6 |fXDGtGtA)f]6K$Ny.\=CG 9IJ^tN.ŴVgr.8r.8f.0 eQv?_.-Q SZZ[Em7E) Mj6n{Ȅ;# CbPJ}Ǻ ٛ8\! p6)Lp3d'<3q:U ,cDB!{aB|~3] R߀Uܰ2t3X!lc^0j[/y @eх&KPBNutBѧ^"3yO~1+SA\u-)݃nwI$HϐS_KS333 t }!C pC>!uc4cϧ>gƞ=|,tTo3ĒWc{ŢʦA!fJ˚qCv-!S! !!;yddBBM -Awسk6'@YfTQ2+n f겫5ۅ  J׬SKٽY6\̍BC}hA1T7a|0UEOgnd!*y|" :(?gū!&`0dBb2y**0Bkfkb)h&z jNA*ڳdK]YwWcSHwwϥ흩<vx5z jOg#׮n4ϒi>,it^asNh07w`z拑~g$CJbwNpWƇ DF%3T]YNf9xf~,du0O*W0_p_yaX.a5]j1|5:X_߫H-KT/ž~8' 7*q g֓Ԅ5lN;߽!RV7w080 ;38~J-kDDf̍ow%o/Ԥ e],|0\L ;zsdHI3d_9s(]9VbbrϾΊbW #!yK觾@K3IR{Oi$ L!# bo {)kfCJ D4b2҃HSuB"goQ%3V%ѿ3j Uk v#eff<=|II6(~F V 1?#iTSS{ ?+\]v^2zUfZSC<}'^Tˊio:a _tmD%r\⊏CA8sr|u endstream endobj 1447 0 obj << /Type /ObjStm /N 100 /First 978 /Length 2323 /Filter /FlateDecode >> stream xZo7~_K`H[ݡpr]<Ȏu$FPlVWv swgɏÙo~R("6gE 2bx { zcI0`8d] XNTSͅ+D,%aTcPD46_UQw&<+0W|$b=&JTtIPge f"&=a1Saa;\%#.;]CxaPjz8u +2 8. sD`C gAb-{^TfC&}ɹ[ZW` få aLᠳu )"6U]8aB0JtS9uxΣvBH=aÃ's Gx_]0q̮..|ҾD[J*ls)g9<4koL%,E nb0^`7Ǧ7{7ܛRlze~h_-Nw{~80lU(bg{7Y`ȻlIWX toNzŇ颮w~~V g̖괟/#V"3="d{8Tz>Sђ#(brqCBfQ6 J(`eB?fWbEd (g!֦׺ PYV(dģI6 6)g^L_c b5mq؊s 쫊j&d;|>Բd1Mǧ@zdxD/(}GKdvR鑷)jqPV3F > stream x]}EO0Je}ۢEwӧ^Q8gbcss(L(y0%Q$/>}MUTǧIRT,Wߣ5N& ,@ʠe9|~*6qFf8G[גrFd0KYc8&][o/k>=[lpP,Gs7rnP-zns.BEﺲJ;*Xd`^QPz+0Pʧ5|s=^K "/YbWiX8VL aOkٵ<T١hYTvi9$A{Z~mѻٶ?xcB.(^$:^X9-i\=%\˽<8MUeӟ; b h*;UH!@?TumCJаj=z[Kh, ILNcԶ;wCO"@͝x)q)q Kucǭdj'h}/k*M9 KCW4 윳ì_uиNI^" |)sRp5,X+R]4N ^Ʃi [pia<:[hB^U⥫sb'(sNci)%)RDπ$׬}4=`0 qQx'1ё[ f 9ͅ:^_͸,ֿx> stream xڥYKoϯ0|R(1@H&bvdE,ݭZjHx},R>g_}E7o~{So$$&1IvsW;~'IΉ`)c.vOlpaWnYf:q.j+2AdAo,I#КNu;`rq*ڵŠM;S᧯_uà>k?W4v5n4śio_#mXM6ۄ_>\n\ZjpHq?㬅j# ??q7Z3Q N9I)][tPWyKά2i6]U:'0ÏQ q}өpVs>*ono;wO6k਀yǝ WxmnzǮm TՀSbg$ w{7Gٺ)xCq/k#/\xK֠x "նiQӘ0F?H bؙ{B+!~`C>C"2S7-yv_e2j#Â6uelylߓcboӖͩ&3=@.b$BxR'dLBiKT׵ɜ&PLCf_c‡[FYuٺ_@. n,Ob 'nqG]ܙ ^{1})tťHy-fP;d#]XN }P8 zNY9+a@Dmg*' "'7(+{Vs?,1(G%2Xd!?fes^uB n(6$pL4\6|Ճc)a\wݩ3PrUY-c:eo[>HoE-t F, !W~Cշ#BBper<}SxE&IInӫeraD=U-]-t/1\8~%2g֘9tD]j s'$rR2/lJE MI&7x`lӥ-k Q%wZWIѮ,xܵz;E_dnS"W1@kI9@b/ c+vm6Nn e7l4gvC.gnzcV0"E= F_˜C7D|^A4e+x2⌺{>yL20aM&|j$-Cr}(pe#PtG{VkY:$"D>A ʉŘ=>oC(^֖QaS?gX-'05lyg>9:#{.̲-`w1b'^̗AICӎ`4GHU/#;hgy.tEמPpNk=r%VTX ꋉ6eC=YP\V |Y4tREў 0BW㐓J$ѹy&HuiPn{E{_|6xКY?K}9Spz*&-Q{[w$@v)k ͳ&SSN3\S Fq+L@<"b{Ȝ endstream endobj 1592 0 obj << /Length 1007 /Filter /FlateDecode >> stream xڽW]o0}߯AӤ@$1M^ff]AwƁOvsϽNtԛ#gr| 9YՋ$!^^/?.'M| 9\`} 2?,OQavAL(,C\ Fz?eTMWkqLũc\)ܩyJy#^鑫qt Os1" pT͎ G p6HZa(rt8UF_xv&f97xuOMO-l ز-ebȉ$   ?T(l(`,@Wjq/U3&[Hm0lӝA{Xg{ܤ{Ӷn썦" ! 6+A^r)E\aq.F?VؚUY3[T$ EZUUֺ1챠A֫rSx{~9RB~#IU+e"sJ.bqsd,Weƒ{mzsj20[A#]ѐ3x樫e%fW(-P<]~ua.~mZDIA $3h%51h_QIt]@(iB"]׌`qJWt׵%rz^chXm^,.M]煭4[QB]Hs>_.W,nY;_֖ ^ S߾E='wQB` ۩Pg '(kqԧt۞D:&ݣ+mT2u_[7۝hgK`n#)s3> stream xڭXKs6Wprgb$;C:M:dGSDAT@xIro>o_nV/^8Q)nvG)(a4F|똍(YBNSchZvwWVvr^ݬlqD4F ]}-\*Ϣ;Czń⨊>MWckGF<&QSĒx~`Z<(ψ7}tuM>VMgw4Ľh-D)谅p2ޯ-Tyʦ #E$A.A5DeH{pWv6Y ]2%b㞮=TҲ5wbGWl.= t* 2O5YRpˢA5A.,V1sqbldjrt71<Ϲsu/9x]sQ"(MId?tJ4[9.J{$wk6wޟ8N̕~W#TE%>:+h.rRg*T!b^/BN,۵.SM_* :E{ rȅCfEvv-*ѶV@^J9ʲD'" 6a< `ҍ,RoiCsg<k;uvW~#A#U!e;ljiOeɤPsnBօSIi=ZE+ݽSw-4]UO@%P򌏣OVjPNQa;:Qo17cOј& DY=nZ9rȉ*JKJhzqu'ۯ.R >U%mAg6n 9&`gM?1{h=~ V0$I_,}x}0Ҿ9daAqY _ -Є:dt:*ӶM~N߉SeLKP$6~KP.T=Usl! H>Øغo:, 5o4!ary+ÈMH]Sn YDՌ/+vSqgg<;|ŭ;7^aۨ^3A7\Q`IK] A᳓^ h=]:p%GgiU೿8% v-u폲(wo1O<6ܲ ń,8 [= =PUjna\ԣi:ǐRɲ X/mbq?J> stream xڝn6}".6un.z:S[DȒ!I~ύ(;I_Lꐇ< WÛoެ߇Ue6WR^WI(WwկiE~ڛ/Tp(!ny7] i1jg{[^TM;9$TyJG@~gyyG / S-W0 oX6\?4x޶zF:t]0@>='"@^.(4E`LEkCmOM2ؕVAΈ#=m\o#lւCL d ˢ5<on"UxPXºOrU}@}!ҾFuE03ޞh>/u0v>AW K5,( H K,7`)"E0L6$6q(n 'n xtxφقpˇ-!ɢy%U{ۖ=RO-N=15 ϭ̃)^㷄. V<}biAᘾoˇ%ɱ'?^l/y{#GϿqczt#wVOi7e*r>y*+[H~[u"w"pqw5`z{4MxB}.'$_sي{mjS/<5zFAq4(/©{rIM]b{ 1;U- RAER2;f%P)՜s͠+*g| LE1y`JmpqL|GvAͩp,Ķ]S^ ҩ1T32I,nW"xL?\maꞿɪ&嶬M5[W:LTlΕUB2DͱKLQь)nPue1s)%-g*\h*Hi='.?ShveqG+ <82v*31h9*ñdH=hoJy"01Nvr9?O϶A2/. R7=:)!` 멃@CP:(`@B:PѢ9lf?+SȆEC/S9#?=(^#>O%&x\cьƾ9j0#[0124Kg?M nNA!FV@L@0ŜUt. gMzV?a>8b.bA:;Aᢶ-\-)L \6N`c io֛JRp%ND|Y4?]f k` mH/xq:FchEC0H(@)y[&r`%p-|z%Qh+Z.^bT렃K}&g'HF2PeHv5&N;K_Y? Zxy0b8~3W:Qٮ3AӢtRA<8DgFˆĪ`@Rx_(,}W'Bc`gdhmG:vp vO%S Ǧ ׁ9,o|le[mSs6Ev׮3ٸ[mdÖN\-s"dž0c;@=Bl) ԍfz,Tö/2Nv4pjG#1[5Иp ڈDQQؔʵ8.Yj bx3p0ݳdێ%̝yvr\e9ʩB`J󚣃8`񵄂!7`A( c+)v@ jQUu ny(+9@U~B5`hPN@AVW.ypW*=9bxcni/vEфΫkĈ|:}3|NܝsOT*NdQD;p6`8Y(7vэ6P$ z .zza:t]05T6 @׆JѳLK%T:_ r̾]`YlvAS}0hΛR-/a?\KH0Chv@J$3-˞pz.6 ucQv<*iSW,72JU&k3?|Cvzaz~̒$ ٺxC\ ebC_+?|ߗc5ؠ+wsaQázY?3X\C} R]mѲt*Z> lxXt|ElgJ%~Hفӡ`-h> stream xW]O0}WDaHI~MʤiO<1 ֜8rJR)1^&{=1=}=<;8$ 7[x! z!A2x̾^ă$ h$pԚ> stream xVێ6}Wn+.efy)-iEDʢ"Rko>CRMK <-3 ;irr3Gp:0F(( 3g:B/ }(g 2Ō81`C_a]vg7&rWHɚvͥv^ݿ2)oe򆱩ykGs7lyy*tg{aSۛp&wZmMq=ܘiHԈ6 !Qk+򆷵/*#iԦ)|.Tv(ՁӘ.C$d(U+YT-owSK=M,Zo7_?&^)R|l|E5͚JV.<)dig4V4Ix[ɱ%fqF$3`U:n2қ1*a%R"e.iPujdч"Yxʃ (άtBsH0de8C'o%jKQ͚g)$H-ӂ^{̖rH  A&#ISԲ%(ݺ#@ts?~뙻GB{As6p>3Z\4 Bh=TOğV)I]Liʰ,?5Ln1o㟄;lc}s9ts}K~.ڵV#qs{}\nJl_7MA%SyIBPZ6B/uj} QTnPJe5T=N};J(J+mi<n2uH =N"TgijvJp@29n֯F=R(|큺T4g,zUwh8Ac1QvA`Y_ endstream endobj 1642 0 obj << /Length 2127 /Filter /FlateDecode >> stream xڵYM60z ،omd $Hm^dr3Z9$g3o?xw6b?^XƋ$X|iƫ~yy IX2Jpٕ;/o , i,e^m XeNǕVFAt30jey8 s8YELx,I]`sU: )1-zQtl'=< #m]Sp)z wģ >Z=St:-b ^|]1G 3"/wthNé81Oҗ8L&z7ʏ_A QG \ ~n{'~pagQIB|oeţeA"M4]TWxA꡻i `ՓDO/VQFԻJ<3˜)mUL)g5#cD[P$Rzz r:KT 80Ryu  ȪNtJv )I ATo$۷W!w7FQ>,7ԩuO̙%tDu4)f*\uZ/ϐ+^>ޅH|f4@TZi#4L"޸8AhO 7i7Z%h%,w3$SzOYۛgs V/spش~m!,.K$}Z}]  I4N~b hJEXn/kP֮iU*5h)B rR/6SW6Of`˜xcwd s{vҲ{U8B^ڔ ̬6 ۃFT{.2r{̷ z0!xiQϬ2 {Oiۦb +<mJ=_!|}hT,6JW^޼(~QY4ÌzMg%Ʀz)VWF\==nT!'x:|wT,ӛjPQep%|f꨸@~ؚQD+/&ܛٻG[ endstream endobj 1650 0 obj << /Length 1852 /Filter /FlateDecode >> stream xڥX_6O1PQlٖEE.rH-ef~(x6;,fEIEDRqn>|}o.6&. }6㱑E}f(Y/Ƶ߼s;˼ђT]QW `82jVprdNۼ=7^XDUI>{$.,-LX4YpXɋz}ẈI88H#OLp5Tٺn;-U}[7?0<#Ak%ug>-Q˫@Ni^cEDKX5#&A1ml /sMkLNT6 x MHɐ|Xiz&G"I<(r~`RRpZ"(!X+]WcN^,yݪ`kUIl0꿒yNbH~WY_08tG}'h˘8a&N,^ʦPxt ꧢ#1bL H*<(+a1؜*4s}nA>-Ϩ4st` Mիw֡ GiW>S89|%bU焈ɘ WMw([ԤlA )@3[0}US^:BAs1/ (iPhp,?j%㜶]˦ˍ["{ӶhL]wŷJS%WJ! eh\ IO c1}pm)o.3zZS `/*· endstream endobj 1660 0 obj << /Length 1620 /Filter /FlateDecode >> stream xڽX[8~?"S"\1Jm]U[EN# =' `iUsxE?yzxOE"|D ^<WI!(IQhbyH߰XNꍟѶkW.H^()O}M @)dZ}/קĆNSٌ%LKR6o ~EQ1xM%=ɪțVى˳87rZD|8mխn˦#ͮ-SĹIJ q2VtY``LRge !J Je(%3"PvKGc2%۲Ruek4J9kyewO!mmBP&%SWCb5;5"!)vˏ)KK|`[^uI-x ^¡dE?Q_7X_GkQěV~6UVdR(=f7_YqK9owy~lj湭/398lkրcόD梜A0&nZQ;:[YpH_u௲CYVn1bf8Biꧨ=L0 Ӏ:oY'lu@W\lE{ԣt+'.9rC&U)M"ڮͶ=6߭ :)?myyfN?A>9;[T8%.q\LB[]$r }%ZZ|, \2z!ط"0F1(: G8mn!w{ Ojs^I ¼!6JĈt%s|oJlto8BBP'_94]c3GS5 ߏIϬWhx\am{X,#df2!6}L$b0C ~l02JT}‹Q!YͪݙQ#هp#oY^4whSx3(t~Q ܿ Κߑ%I_U!6rQJmX9a -%'3:P 9t%ёb)Ibl8A5a>xe.~^o`1&:Aaa)2QJ"Tr"2k?e^c;+騶bNcl1Gr?뻙LTj,ZVV)@00D BߊIZD4[F E`ǹhҐZHsO#Q'3sU6I©Pz(&<$1$cvt~7+!Xm5؄t!\6qS *_;~FHoo)( 4Z?USp[Ќۗ- JyprZ 9١0 yvl aDLZJG1Aq7IW@`}ECXCam9-rhdlBufi&7mIWZ0!/ )(yGO.J[hkV/{iGΊF5В0- (~{zFL endstream endobj 1673 0 obj << /Length 2944 /Filter /FlateDecode >> stream xڽZ[۸~ϯcF$EJ ЇI-v-ti[,AsxHZR4I- v.υrrJϷ^KU -' t”W/v)}NiYZn\^g_=<_-28i)2n}؍liV"ljʚz)RznloCY[z-fV8VMZEG=J}s6^6ew̽0$HsQ- Va]YU5OT8 5™Pj9+N<_{(πp$7Diݾ'ZWn0#bSWZ-DbϿ!+xԴs qu{م2e| ķ h 6k[D\^mnmwq_9@`I0[; ~2.2Ȥ$[whogOhV:nݖ+ 3Wz9ST D=,ۙd2M-m%@zT^!ӉMW%bm|Ck6랞w7p" >5lz^\'J[D%dFY%iщ|j9<|jKJ ͮOۖkzk;C{ge]0.@2cKÎJ2$_Tڐ)6JH+xRCSg'c&b*~Z-zb$+-N!C有OEt#䭳F3="ɻ}Ir z,@nm"%VYO}m><ۗV,O#1Xj5i7p J!vGzٶEb2!p]zm22Oj9>P{v=妆V\#}KλM-suzJ ի u03J)L +݉Mڧn2+v$2=O>LKT*dٸ&MM;?05眼RJ<][}H@)Lk FODrx6hVlΤ+5}\p@rsNfZE\4ZU E2} \R_qЙNR)u2¿IεΉ 0eo x,C_D0 > stream xZ[o:~ϯcB- {Ev⨮Q 7t&۱M=(gDWFY섈 V>)o,h^) UQ1Ia|{a K2išDPX$RLcRXY+f,YrN*6b< ʅ\FVf70K"A,3fU&YyL@֕{TGaB1|9(HJR[Ίa٠Q n(a+b䀌" ^1˪P`/(J!¼!,E0- KdL7(OXH17 O+ށ!E؄gh$9${p$;2EVcHX^,&2,1-&h4P N) xMYCl; Od@eb$GPߔ.qAR 5 PU̙8 (TOE7@Ųv[& e zdz?oD8-gH6jy 97;,^~.wտOgry\UtM߰VQak ʬ.WavN@J1XSTovUUo}6Q +5~)./YWݤ{!9/ۯT;8V >;L"sj|(#J!V[8RkHvp60NY(ߨTlތ̚a߷Hռ#9 53%ïbyuYzh{*zOyU4*`E:bP8(mLodS9X=ḋs!ی-FY]^RX.K(ANrMI#Y01+k8`[yEmfM$"F_{/R0M{O溇G"[j'I3~3,9 GA0.0Q0 [a,1HG 1m Rn/#.VͷX#A: %$-<D `P 6ZTȁi{yRl`"8$s})g?];]7A!=<<<8Dŗ͵b~ibb Bԓ}t:i&JdB#mv6C`ҶQum{q|\_M/~2xB|4Ij+JFj4G8sQ똟V@f&]Bē/0LYD];nN4J[D3ۼh#x^#UV=`J4;`ƒAaBS% x0CP$óC٢]ɈHl+P@\ rͭǓ3\.&L*ah|[ 8AG#_ߞ ~MNC,!);CJJ:iuZ3Q1fTQ\ֳQ)Xa ko4p~IJ?%"IJ('VִOayǾw &nj?f@k|?ܗ!C㘾yI8CA E,nxu\G刜e$N9*瓶ns˒$\ϫƺ3#Mry "6ϛ1}e9#&1V9oi3>Փ/vi^fh]ld3k>D{z@a8Qs WxXD>+z1o|S(1TerC endstream endobj 1679 0 obj << /Length 1221 /Filter /FlateDecode >> stream xV[OF~Wه؉-E===&g'`5pGA8:Ӊ?̗u 0O_kί>^AG2}'ʬV%aU\4#2#uExC{O+9RGVa/؉ǡ%'bt }7ыi"?v-ܱ^9Ma8׻b󜸭mO4/wۛn=vR;%UE1$VZ"(?֜ؓBJqW?SQ,sR摤H{>#UԯV[Hh}Pցp%2H*"p :iB|x Я'ZT[.nqSqSB`BQJc;ةnFLA :hEPZoERuv~F]'e?j^dV֡y ( !hx -Hf6k߂-fF[[!>֧5޵4W2ZJ$Qt)1tWǒ;lqddxg3B[E֧ ko/|*$ $9zGH1:k|ۈ45߈Efh)KgH}K[ Җ}߰bNfՖ&>42/DtKܓjIeg8ǯ@YpRCr;_/D B)TsZ9+8- wCvjڊz7ߔkɳ %O4\>5\}d䁉<[qk>ah _ey{s8 e@ ɊF4SZLn[wby(bI*@TȾc=wС7d78{1je(7CtFhGՖ"0}όw|1Z@X*2# ty-.gLNnQ8ٙ=fH15YN< t endstream endobj 1688 0 obj << /Length 1141 /Filter /FlateDecode >> stream xWK6Yi%KF}h٢EыDJJR__>қ(P'IpRSxp6M΋(L҉0q2򖹷+({^~=4ÇХfQM$A2Mp0zp9G9%9{3Å/L|! A̜7BJ1O[Vyʾ9vUA>+sm pO# O?@2wZ9IF1qD8?MrUyۑx/,Xw(AU!Rֈ\02RXF"Cc]Up(] ƵIG *3`EuI+H%}KNL6;Aw8Lg!̂%$Iipm0+0g\6({" JePwh_QJu>]hpʥ46]UYa2la_\4=Ů86cGBA P .LU˕ YZ!ko'5g(wL0|{z]<8>`d};P|l iHbBUup9!/0LzᎂRu KI9hll?*R.oD>{7Rrʯe?]f1No s(@ ߡK?x;Jʁ:'pJz/ܳiqB-zE]yVPe108G) 1MqBBSt.ᔤenMXF!WP,ҮI,oteq)#u(s׼7')fjcV* qP-G6LۙM 4kYɹNXr:M*=`I:[(g(n,4QJV 0%ʞurn+p y> stream xVn8}WK Etzs"^>uhXITIjKb)J6iQ>s3C'wz|8 I$;.C'  l3IW_o6>:B0tQ )c<;m rbE }MZ#?XkiKd8?`^:7MIgV\{MʀͿvr85Qv )vlr+=N,nih},Fp%B{oۯ^Sݵ0oEh=b֩ds\_~vs/Y>C̸5)mf5V"ά)㜔PK&#:#KKO}ʶ(xxNNkI떵>ULy=h` gFݷ~ꉼhui{VL}([M5Z@Ŏu KJpu#z"|Wŷ`؍AdjQYrQ;Z9繑kR\.> Uh=x\d*NG=n%-,?F'|Pl2E. z$ ޥoײͯDc5.C$|V"k"Mj CUc4 v8-]XVڜ:C9kD,A$'Il9ƆG˶0S*|,ۉ7/̢+*UZ4R|;gv'4T2o._4"cWC@o aG[ᦙy'XR9̓f2wxʧBu?(f endstream endobj 1700 0 obj << /Length 2006 /Filter /FlateDecode >> stream xڭYo6_a1+ЇvmE;ӺblmdHߑw>,vRA4y$~x̛W,x4Dcl|zUUuv/Dx>8ΐB<퟾ ny]_|ߪmW|+',)VosG,Mݨ=Q-h+5{A-yIlNAe]\ڪՍ\ֹ\o^x'RnݝYR PC$35>'7#7y.ԡC @Uɸ?ZJ'3U}!wxPG(ZZhG1et^=_Aȩ%qm޴j(̵TNV±1{>Rxepuy?y.{N+H jBOT"1]iW&m_ gi"UyycgF=gbӽ*(' Cnڞ7e7U>O2_T\xݩZ e}Q]F=ۺ}'VͮKu%M9_,zE:zTSt꥾zɪ\uh!4pK5D+"GC Gں$Kf"?b&sn?Ҝ( d">-iL߲U/i\c"L@Р/Ǝ%o c` !~l ^2i8,ǣ좪\8מJV6m%ƀYm8XժCSkT4 \+5 /, 0jpq}DDO+hYM֓ v K1%BL/0Ŕ ASgԛt- 7>_K@װp sJVY7u 3`]nFcݎL_3Q8fg^Z靾̆W%XC68;۝MCڣ,p lQ0\kqPL^Xdg3SB?Ko iE)9ׄ"p\=pWcrxwv#)WmTHFQtt4\E(ܜЏo8ՎͧN9ˁ* ܚ!IXz,bO)peK5@_yu-3Y[*T/B<˳i&eI^|-`= ҀpPQ+`tr'=cU]`p7/qW#.uQ-eqTak%&eP% x0Wr Skҭk437Ă=11 Fg@gq]6{N?W uBLilpDw'y\D^z[B@Qz^b )q>1 >mX3%0#а8 c14FS\ׂ!=tNH g> stream xXKsܸWL9T447U-*뭵9.DBF|I_qڪ$'` g/h{y$}/E^rqG?8 e$^\_oe_t]_"3Oߣcayy.AS̋˶^du(Vjkܮx)4uvW#=͊MUZk:a#)߾"ֱ2?P|/?F@Tsj* PJKU /eDڲ*=F,'=Y`ӡjwh'yx'?hXq B/cʆuj6z_#ߢY] zQM.җJ}d(2"-, H#Z0'ˑӈ!EWVB/-kx"˿Sֺw0HR/'`>~ܲX zvVKg!b*vNX6#fMWL R wJD)oZE%qfle@#4dn\TRN䘈LQ2vx6AADR,1,$# s1ٯ,֕E{-bWoLHz e\! JY`xQ?bv(0*+sbCqayS%3#[0Ä2C+s &Zbof׸> -AWQ56,CwU!O~tP%Af*c#?7sp#:1Ĉ9_;+䗕yEi'nn=3wF8,B:0PP~ON`VP{@y9|0f&y<:#QoQđ'Dpv(9d1٘>6]Y$5VIDf"|Y BјRĒ6,}kpcHߨV=2AeM1ZBoV[8sSې8(ٖ̑`LyȟPѕLȈ`qB^ 7ȮW~0SZ$e Q底ō,3+k9;CDjYxy~TS晣QV#M*QՔW툿*\B̎~V{a26Y]dSe LpIg^>q|Ux =κVdatj>M} `}$}qZV8dQ|S>Kl gg0*'_ӂ݊V^g9Rkjl8ѓԔ0X0A|*ֿ Ro ̨jl{GDJtz j:YcVu]㲻7eeijurF޳@.=2ʡ}>*āǡoO&(,~r" uh7N{RzFG|jDS[Wiht Q1`ߣxjڅhv;&Зħ> stream xXKs6WpH4DzK8Nn\!a B_ŋ/Qvx:9,.>,`agmfkVrV@mXlwc7'I"\gn~?tF:mB2VO/@vq٥YsZ{svI^zC\VrryEV Q ̗m|ŒFY )IWTU*%Rc\l6扄[t<DzCWc Y^ T8lJ )=hV65iK@ 77Zc f+?+aIR嬳M4h 18a%&`@xX2T 3NȹR<6[ץ),Urղ š{}sWTTBe 0p"M'z-CMN3gќ3KLpJ SBk@Fb&ALq~p(-8j1Dۍ> stream xڭYOF}Ea}Há!RDhgI¿yƋm \}:[:?֋3918(% 0Y7yb)":v[-cC4xWܪscbĞ"lR"T #T6{7I^|JŽsFr+{F>~Yy+B@sSYI+\mssAJ'23G-ںxOLɾҬX@hz%@*3(]}S5-Zٻ[1ƀ7Ujv:Ur|PZ5` Dm}y=#}'14m ucAd" Y{dx5= mUBn&,I%]YiyYfc֢SA̍ ?Mx#1cǒzhi,07o۲̴ZB j1/:. |Q0DC|n,ikLD(Α+3E!B!BmP׆C6R;D2+:WDu3 51rjXM4aԿ<Ş.bK.FUo ζjU}k0P-  ɛ种=iҏL0mMh <[¢TiSd܂.TmCD}QxMC<)we! ^busݴybO.3`6$q[䙽2dƎ_m}H>xċ.u ٫IqN =t6kʬq U֛)>&1&SV3)b4!87c _'CA]D`rq^`]b,(Iv/ߨFrx ȜφV7OcP^_hƇ]?B%p>c7)d~$̏b¨OTTTVcAF0r˪HDٷ$UPZU{xlkh3a38| F|PW0WJyU7n\uYۗ\Ԋ%g #V޸M7U@x }d1WlOBk83L@@=`)#h3&mm8?XuaܠwpV˛vgv@RC6ŋ hb!ڎ`P#ʆ9K8øjs0 I5)fW( 7ݒ='BY endstream endobj 1730 0 obj << /Length 1253 /Filter /FlateDecode >> stream xڽWMo6W 5#R"&{uAh]Yr%9;CR(l=I3ooQ,oW$bbYP#~-xgU)ܻ~tQc0` Gȕg_߱SK$|dZKwz~+ ͻhl-)%Iew$ FFOȵld+l_&'-"bݦ{O/K?Vv6Ieَ^ɝv+h@ާ4~O& )E!¯q&,Q3=R?KS49%$\nzNq؉k-5#u!&#)[Ib>}soF?eڶďiok#;kIWċM?~OևIc]W=Z  YurNZjL&s_]'2ؠkC Uzbc ;:|EùmUQۮIN+>Mlk)90ȨĘbÉp VⓞF%w4܎`.m~UF=`'*+cbSglR VRǧ/hN"'U(rv`,'_gV±_0pU5TЭaɟ8D|P[+ @EH^P2.uj`@ ,ܻ4t@Ll1t'Z Q@|e_I&QC(O4UUvY??Mn?[Wtﶩ9i8~_hf_JtGk~`5VV9T:\J @|8(-ןYb:QCj=BL v $Շ*E0 /?Oz(Ϣ{ l{JiWlR*TN*%p@9_f 8&J?ȲԐ @9@ opDaCk6&Tsݘ[&LaдDZ3xբٰL,fcX fU=.Gy:oOx! OCpBurY]81]K3ԉ* JUx? 33Aqb7Vg/h1 'i7MH.bd\Br|,S8uwCm %oi&mܮ1{O endstream endobj 1738 0 obj << /Length 1114 /Filter /FlateDecode >> stream xڭW[6~_&s ViTE`(d+6ICO HF̜gO0ӡoMY$j*{._j"G@Y;5[w7b=Z# .S=&"~E6D'aXl0Ň@M 8:|攫zd[|ʆ"6{s#?s#5s1C܅CmYqZBsVʦWx_^oYK7*rlt™0& UF tQhgяL t,qIWjPEY`{w&=X8uIʶ^1vYטv5<2}Px0>'bydnhb`@Ik-Ł(uCt/A ~AEf>Sςk>Hs'-vKk˘n kzE"Q򓫩 u]l 97(M6/{t!{EO̵跷ΨF*i" H [o?'*eAN iZ=ߞ8޴xUuHp,E?VpWkg!X`Q5՝Fф+la>:nyK" endstream endobj 1745 0 obj << /Length 1257 /Filter /FlateDecode >> stream xڵWn6}WއhPw)hQ  t7]DDt[ECTDI ?gggCvf\̎OPsr~tJWggI׃%[]nz MwTvX#ShE{|M::ub]H~-[KgvFZs^״7]| r?9Փ.ލnSX;@ik{uCN iKzΩ?;mT endstream endobj 1756 0 obj << /Length 1119 /Filter /FlateDecode >> stream xڕV]o6} @͐%KmM" tA[ŽTRr#Eˑ+ˋ@]]0lR> :D0=IDžywuM8eY Wמ"0+ombY-WB }3!5xuo-UCBe¿~L񤦢YUAlۿQ4&̘/+;q&uo }l?+E\{W=Rk:T lS!9aZ1F)?E88RMJfRGĭVJA;>U)s*;F+*f-ccUQQizt~4uTch>C*ٞ}gdn~X9^LhEa*i$Zr|?ܵfztiޠw<#3,RAwd<19e1H2!-kɾ\ 58eiϝ;: (A}> DꥡGquB@]:6EV2czF$n߯yZ[K1əYǐomhA/L%/+^Fi )̆WonRx>Y\'):ZHRE+_PYԦ 6NYԅ5UX`q4X Qntz^L#:V&߃"6flђ{MS!l[PFs: ?oEVPϦq S L# C-hRps'\}Ȼpq@kѷ.`*wB|CD(phDяeԢ.ݙi촐n޹}zeNXj6C2JVR*gJ512Tԗ{zr;=.C>)vt A~{ڨ׾4Xq.fH4LxA<{3 za'$b*&sdb)).~]J=y/Z)M*y1nd\k1oն%{&u 'q=;}Q5! endstream endobj 1767 0 obj << /Length 1180 /Filter /FlateDecode >> stream xWKo6WX˗<qCDjdɕM;|Hb{(|h7͐"(G}$8XF,B`."Geno۲˯?qqdec)]J 5fq9{FA$3(nn ϯ zK7HPc]~>-6B@ E1h` ؓ%2=f`! Ru2, "S5$t|mۮQYh`֗e0',5D:Zm1.IC !T.拐Ⱥ׺ם֥jFUo{,`XRQ^O9w=7[יWuㄣDxx`̏n(R'4%N M/ZH`BOD ȶMQо1S仍EXCғ1j84 . ΅3ҪQmDuթ*m@5+uT,ΜXTy Y2$7TGPpa#GE~`*uʦX CςhBlJ'IHA™ C;^ʢ~\Z\VY^J 2 c}ev%ԮjJ?t~'Q/0Ύrnr8A2,`%H4Bzb+[++QlkNroèVмM:ݷ9 vfnw,*u;q@M% Y4<ѥ sΗvH;_}tl4(HUi^aAV\:ݼnMZ2Nٮ]9vQCECC|udE:ݚ7O({ehtljepj(JJcǔc&o٣qJ jf#Y:yXx8;9Or*G}ȍϊԎl쓽'M*-Hp/_ N=j;:;A~{ ES^gS耟C`;ј i{xU>_tqoWUU*B@|~7E*> stream xY]o[7 }+"%A~ [ >l+^,C9u@KQ!%Q5jqQ Qa9 ⒒ I(&FJpEńj #䪨@Yh %SY ͂ Qj_1\mBAXJ!PN);fiЛirBQEfnW!#M"GD$٦(8c[cQH T@2sD6%/s0C8$6 l)LI1@^&`+dDlRblRJ$X8ʥ}∱R&q9 1܋%) K\l5% jN%6h(daZbkd#-|iBhց@T eӛf(H`dʥEܢ(x[Ħ,8=7mbR*$m NUj@x[$AjXSP`a͟ fk!6YlI`h[jz-B[^@@$dԽx7vduo]n&]@ʇu]l||p@-߂S GU׽qwӷS׽rߜ6^.f7Mg[0+DA$2%{x^N'7sbM Li_˻U+^A"vqzC%b'S(>OEG-?;DEY>~zc=@Ew6^c;{u꺷 w{Ɠ*oKj<_6+UD yl6[c:agLVme?8XH_F$к#ON ޒ4,-2F!-l[D5־h0;{X;lxuosUs[vqOrj Oj7,#Zӟ1J εQkB~z#գ&A#N2F0kOQ>p퉖6#ROtbdD髛sn /Nvz'izH=ღ8i*9W4G O+bb/1WpGd?Td?VV QIҎa+:DO[3dSM>;+z&(o?Gޝ}3~q3.IYe juO;o?~w;]ld-^= Z y@kA i endstream endobj 1784 0 obj << /Length 1453 /Filter /FlateDecode >> stream xڭW{6 >Ep{%YЭvC .vN/#Eٱ]'M!$Q|,٫WOlgгr339g|3׶fhvnrkn˓;ځLE)Ib[!Aw_h% G"Q$Y}uf03̀yAL%dG$jq6hډrv{2;לky*ʅr>F7/'yL9I42Na4'(z1HY-DN (~cfE |wu|ltKC& \&=z̆Ufs@@rwba Q&a@[DEHU]ny P K4峢vѬM"l u,blٸwQAYo4iHH(Hs +ͫ"^MC [:xU E>-*1#̈>:Hf`+7>U}XrB!a̩6D([ 噄$$ꪁS21> o =`V4Zɬc**O̳s't-Ivyk*Ih3/t>n ,ܝ@ S8iCa{s񠼈SEJD jek}POr]ŵrZ# n<"TkQK+C ,u^˔T$Z5Gi ,6lnK=CKbMoj| :kGYo6#hHJU4L^et<l ~ss*p@KUwgY)Bj׊=nlqD^oNնW]ҰvI/$9S y9f'O/r~+v4w/bW`t@<5;4="W:#YhhmqE> stream xڭV[o6~҇X͐ua>h( ,Z&οhiR<;92Jz75=H!Ac/!|W5u'f!ABp  8Wշ"9DDoV_B/?yIRm<HD~Ы槪#p@y Dtʰ%a߳ndu08cUg ؂ŏ0K|#ؠYI}KY#؉$ Mb0zj oC}6txﵰaaHK50,GPmnys#Fc T넠Č*15H&m2%YjX^RdcX ff}wtrI.(_r7ŢA#+1BXbMbӳf|,hӞۯmԌo#4kw2⣨9ȻRZ7ET|O(kTsJb3ɋmdYSGN)'U& endstream endobj 1802 0 obj << /Length 1602 /Filter /FlateDecode >> stream xڝWKs6WVjbRIN$T],(iu"X/,XXųWITQyX(IY%qhte٫49M"J9ng(z ,.d?(YĪ$/,h6VUN RΣcA^fq΢vKYhiO -gC0J,[i^A֞hhZ֧9-PDŊnJ"Ѧ:s}$ * "QkX&Za [ /:IP+Ih4EX ih]mOE}t*E{Ijc'y]O ߠҲ.\n n isE!t: OGRmKZHK+ 8r e ܎!Vv#$^ս$1+[z 6فσyD.Z SݝD MİLx(.O;U[# ~} Z:t)a3~4ҪѺPgVCeZADM'28F QrD yM'߫XOo_|7&[pL>\eOm)UcKh(M{=9A%ܵ1Fp YYmY -pUq9N~*ŠAwzT%> stream xWo6_A/20g=[2 vև(Ó$_Hʦdn)wgV_&oG)N#QB0"G}˺n?k 4q8%:4!斫 )Atw8 SO Z;[i% (1#@ɯñ9VPD8HgG4֜22O}/_qSԝYix}( 0DgݛǂE '<0m.+ǸNh|b;+ϴT'IU޶b%Gcsg#d!: J|=~.^y0=>P.l-uŶg`ixm)}ϸH;82Ktm+&W+[fW7z} MlwNcm6 MJ/U6<" Mm ؿ3ZY7zR" Eg_!1^?@0՚\1Pٺ[ifopޙ=Mb5˫F*? S }K?- zVqAzq%O,XX짧u^7:[T H;V|zwkC%F%,0?ysD(| endstream endobj 1827 0 obj << /Length 727 /Filter /FlateDecode >> stream xڽVN@}WPYZ.REQ /8.S6$*RgΜ9^BDЉys|K&x(! 0E93eXpfiׄwm'mIf$]!>"ȣyec$fٳ׭u:i s7djqvyz7Bf9[MI/RnsD{ ɺyQH Bu;a-ѱFtO=m):b;m?`\ Soɲy:1qcL KcrVs}-xkF%Wuk"ue}U[?CtoyX{y8+^~X9oP^kUݔ:ƧpX*g}GUO><`% endstream endobj 1843 0 obj << /Length 968 /Filter /FlateDecode >> stream xڽˎ6-2bH4 RuCD ZIwȡlK54]0Cr3F2ȇ{%1' 5Q H0YUɫl9D_>̿/>|zn0A,TD3f].f73(#| i$)g_3'OqD4iI8t ,su@T)"l-AQ oWKѶwKڼyl*G40z7Vb졼/Q;5خi1;@9ikٶUߝ4,S&2 j@8C۾z$ȳ)yhFȀ^}c'JRd@\> stream xڽVKs6WjƂ훛[+''$LIB!ί$Q\;Mzb\AkDЯ_u Kb(!8&!CdOw(< 0EV;#5!NIe!zr>C) ZRKUvN9-$(% &ynjxGv'z7pͧ3`N8P鍰^VY{K0O>CwD$ uFhVKՌ\ݻ2f1LJ haDr(jjK/[ķs<\xN8ptŵdJ|ƄOST/7r1gW6>Ϯe9\@}Up ] )ZAiv@xkQ͌ RDZZ332b3ُK/{R\ lUwVAK4 DS%U1 SL)K!3BАx a&5244vNrN[g kD8 d`EƐ\Y|t=$3?ua i|fzz@8x-}s7v)b?tG9AZ_˜^@P\6ϗז .0@Ctdߖze`qyՔ5v?Duinm*6@V\W~<61/Ǖ\^Z=90 pqQX(v Բf7EZ䚖Ҿ1[US endstream endobj 1779 0 obj << /Type /ObjStm /N 100 /First 964 /Length 1445 /Filter /FlateDecode >> stream xXKo7W^+eb2Ǔ@n^4 lN$Z{Uͳ"D JU**E>H`qٓ'*Q(:7->^.V(LLj }1B ^'ǀ'?O*@jyz 'az8LoV7|6=bu+M˛{/O-?SϺ?],PzNUё͟~lyu>һI o&p^kV J,=}o_x{\.~tW= +DFiQD IT.M"=PS_2@r+HMtH7ECGB C3AJbuGovlv> stream xڵWmo6_!fc1KR/ C6vؚSZ\VREҖq =w;c/~b,%k` FO̻6RT-Rξ^z$B$<`{ H;9]Ms-o(`OA:{s Ŋ4Vvv3IY1e&K+3cY33ui7͐RTxǙ=}dG|`_( Bab/8N}17L_!j@Qzwn,mdӎj{p+?ݝr|uxlD.FT !ە Z~J9yZU#u څE Ų9UՊLxږ}.%2وr Wj[VSX%dzv CXWzjmpQRr|}^6%&,PY>k0S^Y)u#9}(Ul)L&Lu^qe>"?M[@j))fҺ(sPbqKevmY OLQOPƢmvpx4~3#B~`)@aqkab<a oJ 6>+ىSL(b}B!ȶ1E8^ZN.b/`tA +?u:( DJ(y#)Jb ={nr]C>澀% )Z'11|J#הT^z}ljhW'V=Mǀ"uKOOOzO3}cwlemb{1Z C^|X$0*?+wQ*{rsߌz'i2؂ŀ ?S&y Ã$LPaܭFs-~3PZitoheo_?|_B]~9y-uҦ:G\q0ZI?]p:L`w#CAut}u&..+2kyٴw۸~V:7#T&)O'VJ4]ڻq[WW}> KǡԝPV Pf%tOzut: endstream endobj 1888 0 obj << /Length 982 /Filter /FlateDecode >> stream xڵVMo8W(5M[yh)-l%Kmym Qy3of nj2 B,c*Fa (*hua~K(~Q^[+f'_ܒdE1+,#oVEg,qj9 fbtfޓsqUSh \|T\N?%9%NTH9 dbRE ̉&<`F*&>_#CWk !IO.z7NvdꢚVKǠϢ}Ov]gѯp$K-כ#7JUrv7*VF^qHG#03JA[(NR\JݹYbgh72芕=2e.r%F0<>SKfM\됚ZW5#b3mk#=Q~l+fWC3_ `VqF"XHQ WPѕ(em'o,Xp>QǑ|n=,(۪>U \*{Vlz+Yލ+g[t9~#U&{a4Y4=DoZi[]lLMw#n:6}0̢ȚmHsH2nfuv>n8D+s`KKY<'l3g0wk0*{9/bo Iׇ`"B endstream endobj 1902 0 obj << /Length 1213 /Filter /FlateDecode >> stream xڕWn6}WCm4֊+nh70vPRt;G$ˎ H̜9slk2FoGo e:oezm>I\Nn긭NA !DYh2δhqg݀(4p 1eܸ~X#PkQÎmF!L K,TRɼ& 6j>7}߮)"y=ɵQ~b ~n|Պ=g(~`CZV")dg;؝ 1#!nR+8R>6ۧuQSБ&cL9=3|GIl~:F"=<˚Pa('>v̈h3UeRH|ʅbNRmp i Ewܮ)'2CM"bFZ&d\MӼoAGikbJz(f,~&t`:pM(oX{:EzFd=lwYB3VM[h?Ѯ*hp&#^ʼ0OsI9[<@rLxt#Wn)@ 5z|pP~B0LMs&U[S Cr.y.57Wn>.NM))6QO3oA5Q<2Y*r);Vw+@8/UKݯ.`p&-CJ B2Y-f`M)tSǩM*6+[.F^b=lU}MQ04<#fϵL$g+@=nz1dS2*@0['TkK**%Avͱg_\hayoVC{"f4e-(}@76Q܊#Kԋ6 .ݹ^ g{n㣌)gKb =<:4бEþ`-&<|9G+  `@%ߢ ߉?fϸw:oLzu> stream xڽUn0+dbhcR4 :!ȴ"D[$+KM{FCP4>O n#\4#J涋<`34;R6\\|˔{6GQ)0H$Aɠ%n3]x2L{fpyJ5MM) A n|[{vZyŮrKg9 $/uȪʧ:H\9bXYT܀1%[!f.}EEݟ.Ju6q֭Ldx.ǜnAקwjjp/P2.qilH?Sn;s:^Ლ`kHyNגBw@F?C ?-swn rߎV#8pT2[T{qʿbQWVP3y(U}̣H;=#\`H5`#+R{̢.@*p@KISV2|*[Z~34g%^<h.B˒UAM%g|E"[gl]m|cR%}s\?XbAKÀ8nQÔ  Ó> stream xڵWKo6Q*D=Рu (ZxӋ\VR#[=bC}ػ{_3 ;_V֫0pRF4r;`0r#Pgu>şu6A8MAt@Za}^qrQ y[ߛ[j8>IP(=W/ #&e]y>e e+몹e.\;VVAk]Bv-骟׫WֱCE1EN֬bg Ja:8!!(pcvVy3( 1`Gh L,Q ŭ2V +5NǃwyaB w'SldDŸ>P$|@[[*d`ȴ_mphW~gٗxk\mn"<6?U[g\2Qd;/p)cʡ䃊> rBF?v|(.VHh(9 qzH/a96Kl{Z$ĝ{4^?QNI"&]/K/V%#ʒ]yYJl0i(VZ΁>1 &!(k!Ŷ/<1kNF`n+N" i3Ex)Up!K\mXnRު%f%gA'P Z&}t\׏E}B# 8Y]Z xȻ&zmQfISfȃ:l?@#)i6퐦$~v8$:3o*U{$/Ds X;SbQJΖ\Lcj^}ȻAfχ%\~Ioz_r[ϫ m*uPh?kJ1:hn_&%_UIբ[NU!%P0e߬ZF勺UXe P[TBƝ k,ZcĆ'9z Eݢ^//5QŮtHzI{jD7m;h`=o桍u` $0ˬ<1T){%xsMK+Γ]HK\k q|I׀AĢlrM.[':Ys  endstream endobj 1944 0 obj << /Length 1004 /Filter /FlateDecode >> stream xڽWK6W6`3$E!c`X'$X02mDUcw(Zɖ ؋EopLt=s9y(q\#Jx€`chBٗoG$FN7Ej#=!?p,43Lwz'5Hl`n4,_قtnS|Կ֑EAe&Q N-(l2sevNUaߴUZn_L.Uu-a_mbPGVd`<:ZBJ9ߤ9a ~l;+>WWec@4WB C:1DNGC|"t.[}vyQ`WC ͼ a^'bahp8RLKDH[1?EGg7Ex1[_:ҲIǔv[D2[W]*ȇWyܘ;iv٦PW{nK'vTPfG]^efB]pd sX*M,7f̟l]y[jhV_[VRj=@gN H2q|WGxx d7^74_i|j cq- T/C%1pbʽ# ׈[{2-48KgS"=knxh غ7գ~Z6Ȩ!G'5mnfih8X10iWNic--MΏ5%3Vf2kԟDʵBH},+VwA7PԺrX/ X'# a`'}d> stream xڽVKo8W(kDC6mE^Sl%uݜH|35@ߋً8Ҕ`!)R|D٬+~]1!%¯7Q<f*Ɖ>4C7ٿ3$9AQ>E`6oڢ9؛512 |1R2PG 90}gNC)Jv_qaMMiNME&0Z+l#Qp8i1M I߲Ǭ=n6N ^Yډܸ͎ ig_P֯NU֎gB: ]2xHiF-#3f1<ѶXXW8OuZ [^\[z>}.(XjH$mvp{ă,U&ڃ  ӷ'/dȲ+e*Z2csÌd͞ gͽ3~[RrAIy0p#c55SG}(_HBvefm5Ŧ+ c Heڕטq'g80O2R$qP6BroCoЪf3W)TT'C8+En|F[-DW `$O}YUV6 3kZR _"g+T%0?(6DÜ05AFTE&uB"LjZ]49+6V,7L_"a  jbk;'{cJms476kQ&kۦ8$@຺-hl3O{vZz3R,&r5wwT,$YwqWvrӚ.#Ίee·ծ+74{[e1՟EIFH>}jS^$t4j !N?fU'EFL$+5ߨ-y)rݨlZե截OyS..oȍz+3_.cHě4#c>G9U"G endstream endobj 1870 0 obj << /Type /ObjStm /N 100 /First 966 /Length 1452 /Filter /FlateDecode >> stream xXn[7+l7$gF<@ Ni,D5Ra@=3vb9R* [sy<8or=p_ +-d_o 9PF ŰB)COj -t4A,\*)tO$w(dVt@r3ٝB.9)9̦gՔ-{Wnm6_ÿMq`lJlg5l@ PJb7I9|e t%T* DanB@A2?hn~^.ZC T5\ְ8Ĥ2Tq Y@r*j&&GH&!( 1՝'2e89*r`840RL%A4XuG֪; %g&2wjTLl$]A)ҝA+fr f MǑ ͔@,*n&FܝevGWIZ`6= Ljd> Ӌ@rHH-.^<<A0sL0pX0ڡXg;d;kF]鶟;R ozz|l az0LVც3Njlz *KMGUoޞɝO[VY+(;nuͺW՝uwC?j=`kCa4"QQ4by!ZG(hRDuF3J3NVΚ3$1͹uO}= >ZB//`8]"~fkQĐ < "|ڐrqG:RSnoDC~+BrE#? ܖ[ַ4f> stream xڥYK62搔HI &dw3= MJd+9o*[Nf=y xշwWDqLKm9b$3n MMFdp]vyqmkpxTL(X~!, C e?y_˂%g:ASp,uRm,KSgKxn e2YV3ٙxd,Șȡb&z 9bVLiz6zqY(:yލ }75kۯ `ED팸3ƇI$29 ѤllLl "9|c ى 8lߝm"j<8,3u^.O&ĀRx D5'ѩӋ˫og$7$ضfF;1\#"O;<Q. fݹ%aWs7 qZL%bUZ1gt\8eBF~_ 1o|Ub:1d/!Aj"Yy =U>?';[Q"~M[(f-VmL OڻU>p1ݩ1LD/_y/W_􍈢= gNAbANR6yMA<$sgcĐajW)Q:_ ynLGM=׫$zzBM:&V%G Fɀ^"ʬǺ}e{cAHG[2=G,]'"k qζcFҋ/O7R Vu u"PXwW 2BShҧC H''A1-i'0uQW״MgYΕvHsϋqI *bv( R8Ӗz{IB"dX0Xd(!٤!HVhv5-' i j#*X|#I>:.fnh| .,?Ѕ͜DѯuYR+C%Gψ ?wr4 d*e(0"w!pܗmrS>soiZ hX 6N1[xP׻P,uZRѶ`GK}2\ژ$l_n=l zȘrj}MPɴk*d;嚝>]߲(#THOig E.O{pu$fN "vF 7zǧGs0{DQe2y(ÝI =!8= /Q|XUMY'>',Ř%> stream xW[o6~ϯXPRl[ahݧm0hȢ*R_?ޤbsC:k:W?ίn@3_9> b'!@a 7qyK1.,,I JtM~ xa*3/BH?ҀϤ 'U,GI 4pQ,G%;Qr',$rZ]=0dߐ%DGnoG jŮ.zLWUZ̐{ha%;BI$,3Ia>JphzkJyH/R"y+Y](?pg02ek̓=65YS0ɚi_<aeO\(S%k*|?rEZWc2+uDq,AHHs,w^E17oV/b G>|ã6 cjee.D2O-%(ZꧥǠƉ]^K/R*T4eҗwTkŒ]k`|G@a6w\oq'O==.M,crr騲ו4<,dxl`iųl8DV0L%[S7Ə˞b~δm)zMOHlZT\.>OyׯMZ)qzjDe_Ġ&d.bLU$F&RY^hJ#kxT7mms\ڌ pqgVc-ߕSF+KR]H5'{ql'Jqե{OwãʹD/St _?}dm/NSG9U)v3[q %l=ֶwSK7ߗϞw?Q endstream endobj 2003 0 obj << /Length 1919 /Filter /FlateDecode >> stream xڝXKoFWދ9x73{$ZdKE2|S[ЁzWE?ݭ~a4QpDQGHm{J%E>kI;ںٍ~(Ieg}QۮMǑ׮K7xc߮[@1n{RW٫LC[(V?X?탼rT5sWUn+A:?ޞ8#H{cVEtuPEuUYWjWnI\ ꋦsoTEx0}5*}Ƨ"á);w3\sf*m0w*}Ց4i@\{bJ#qtU_Hg&xQQ/2Ks]9*(rRg0>M+hF EZG! d$X& =X%W n/ꪳ*"^Eu[}mɮs]PFd[|bX,)v|-{f q'<|B- =+-"ke_7PF٪N$h_L(%I26 [;PBz֝ʲFMP#>3l₇kI!A4R)c˂!:,{\+erZw/2<׿R+' qOyfNCdsRI^9$ɱ1x6~gAEp^7 KN1 {qqJX*^v#'KnYen;W>ō~rMlhLe"35k#hi{'4& &ǏoFc˼gګ9ig8\-&,^ӮREں-tիg~Y$ӛ>*_TG=z6YXǫg3< '0{1^$$/`d xiRu :}`q[; C 8ML/F+D"i؁FyFktV {a^@)09L(4Y|gQ'ҙV:24Smk({7b򘯋ޞbwQnWT5WM2ecOF[QjY k]Z"/3#};u=؃ta|\.'~(]`8% 7URN ˓am;2^wKu(}U0$nTI|Q˱@e"⩅:ku?K3[?,tW;.}(mㆹ(NN3 2GAY:K%! 7;Gj |?%$ "di(kK@Ygd;؟VQ+&yxdܝ 8T#3_` endstream endobj 2015 0 obj << /Length 2127 /Filter /FlateDecode >> stream xڥX6HqƌޏM Rmu?%l6dQ7á:^! i/ {͇wHXb[8n6xb\?`^/VvqKY m {Kr}J4U}E'< 2+GU/Kb4A0P"%w#mfC9# yWÒ QFr)/h=cH4H@,tbkߠ hıʴxF0]=e鉫v`߹$ 1+ WdH7BjnJyb[<۵Nt XRZ} >laNt#|4ׯ>hqV7uB+s!̱Ve?ۙ #E]@J5a#\3GC+Z;d+If1n7f2U\U&Iz+S[`=&cIdzH5CGևY}G3@Mdi^"enP.n8gI$"q0"DUAv+zqŏaE L>A5̄ >ZAqǟsz80t^U;jzgSbA`~jtރOMkq081:2-) 2i,ñ{!IV5d4ԯJ'{<t +o{]q-`ԘEP"CkwwSF58v'\bR{i.銮E3f{-p\7x=0ny&UQre~VWqQysx^%4=x4)CkD{g]3Kuʞg7x%}CŗU`Zɦ|bBst6 lot&5l]X׍9nK)jpQ\p_5K0Y^&- R q4%Yx>H}3V!:YV<ꘪ9!r(@vkxfw]ĩ=FgB◲xec-Hr B R{`TnQ~8l,a/϶/FECYf^z`l@~ L}K|]m !im쉧lZ=?}sMAl ]K6&O+vxqZ (AbiJ^HэN Qn!gZ#=~ 0:|o#(&? WA3_7sq endstream endobj 2029 0 obj << /Length 1363 /Filter /FlateDecode >> stream xWK6W{ј!) m[Xh9I`m%Ewח%xK(q8<ϼ%! ##^b[d)xu[D}PZQ䛭r'xLz}K}/!S>(7htlD[x\U9Oߚ:/+&$HZxJ2knyqt L@BԨ n܈ƘR`vg~y*ra>ދ%$lepEDF>+x (7G!7II6%Yx6YmċٻZxtC!XW\9{6 N ޷'JjweJd 4..XY'&/(w_m ѾyS&R4Ky]Ax:1B/M#\V!D.H,&2ꥻ`A~{Zt1BP »3S:dz,dbv!߳x$ŻjW菱iUnq8M{څvWcSX -PLNT1[i_aF)/W~XZ'la/rc楬uվOT!+2 r9QDPB<=g3!&u!ot}jF8+"26yMǎx]x ç#(g\HGN= qvk^)sypA95clϠ?\;o#*FٳV{^nEK52_|tcבsO FxX,͌fgb?&qe{$X~Z@ZyK5Ϫ^s3;~?c* A4 Eho$<}o"Ϳ`L;9TN9`7,]!4J\j! h*/ЈNsiv c5-Vyg#|}֬x^4R=j("E 﫿f摓RBl}kiZ{ժuqpDQʺh 4hY,&4x(kщzF"2]< k1n'okM^vnt֫fSA-vY;I'7YkGDZ HC$P MbP[j˧-Z)w|tݢU׍9#u&MӼ5vF՚ˣ>}p1(&=YYd| 1.?Wn endstream endobj 2039 0 obj << /Length 2329 /Filter /FlateDecode >> stream xY[o~_a1#Q(-(`[dE@۴]YR%yP(. ABs΅L-Ň7zxs>9S.( Y L|Y#z?>bUuχܿ8OskD*Rd,$Pg׌Em$m@Z#V`rPS%UDJ׭V%f03EDqs}Qa0YgBˌHӮUYCC5 tE+Agrβak2f b BJ4Zmtź X7ր%)DBϘʊ?x6aC.(  zsEu]ǖ57Cɤtn!1ǜ[x.X˩4;uҗ]mkۡ~ 2$FauѷѼ!퐈08cxFU_Nau޿w3: C9): p(>4ѷ-vZ} " CXGfPfYcLEI6K`J+J剾&|ASWEygVS$ryi`K-NJ[{ 08i6I{;bW,DQaz8 ʨu'H1Jbr9KVr1 >i=BdGϭn @@zhEgO4*JY{Qd]1Nr ܔź ʴ)8x->A3c@Y"<2Kұ~npQ8_¡p 4KBJ28fOX"~O[äWjjUSp6`Μ02O;Mu\;TG<~س7(PƁZǍ _4K; Аs.Sđs2\l8/ ʴ\&ЅmD}pW3:+C5NX#' 9ʽsT%rةB+K|(E|kj>ְEϐPu? zBv:]šI}auSt 6'S EʯYKwَ!xu"hQtYmh\LTXf g_fN@hpcPQ;MQn}3pJyD*jgG{kٷ gﰍ:YVNx_|\e9J1OM ñL%/(޵DJ:{B˫~=/U endstream endobj 2048 0 obj << /Length 1142 /Filter /FlateDecode >> stream xW]o6}=HLSw>t[Rq7YaEe}#%rֽH{%/tJ:/g?f˳ t2(vV[LJa$1QU\>B޻իY q8@z̋" V]3h7ZI8TKA4JA#x-")w3.zSյiM;6ɵ,ڻcr3y.|(b}ERb#Q֕F@j\ai<P&9d~jڵ^]5'5.ele_zR^6 -G]]ۉ)_lm)[qI͔Kv1U#HIZwSpM7vqM76245ސ WIKXN aD9aΆxͧ,̌HX`exGūJ&}BS58ؾړayG0g|<&OGd-)4VHUx`t$;)N0iXYF-kN;.V"\7VR,Zl-Rt˫+Lp"Ra)Q*^DRA7ߌUnQгH?RtZwqg9O=2+Me<)YN'k:Kw6MWt+1.l޵F#{{_JC߂P]_(@-ȲhbZaՉޓ{Ts?C9:^z()uyFk {e^R7>{ģbj:meүfg$:BMB;rPY\;'}9ERs>P4mu%/CYC!6] HV:}˙sPHwk0E @hPuW8 ~| 6 &jS? @f_YGq}JI aËSx(rx|dJ Ց4\wq8Rt{l<݀IӑYešr+=O,rWXH iqů^za=> stream xWM6W!sIG:@Ki7(hÕ8#jMvQ5|yf(cavuaKQ{cDYF6|u._QԻV7&h8MuABVS֣:g/ l6lB0pd޼ cfo;3*HX<# 񘌑5&y Š(jMQŸEZ")LJ971ow]ul&?as!" S-3l=4ӏfLJfq;,MUSDX윱9UkƝ4R!2صke\`9/NA$pph۸Ψ&{ͫ8Uj! ugKnc6VF %fVGkJ RLMH0.i˽}} J0|HK (bp_vJ.et!NǷeQ6N}emW 9r^ի% KLW_-E@8e/IX 9X/6% qХVg>ʣ*!KaY54RuV-,iߞYmm;dk{r-> t[65ZLOpvE7Ӝ/ltOk;蔯R8#WΉ  tth4Ld^H(14eWnPf_G"Nz/(E4p'2ͥ 4h~ގ9\#м`euו(,@HɧoD}Lewe ?u`a2uzvIð2kڪ_5 B쟔rE]"y C= _U ^vo}Yvv3hE07+5{v iK{N<-:FxZYTV9j׏L~ߔu^M&{OYAo@>oK/~i[[]#"_,/L endstream endobj 1970 0 obj << /Type /ObjStm /N 100 /First 967 /Length 1850 /Filter /FlateDecode >> stream xZn[G+zl]~F<f"3C{ITW/( uVWWtb9@j-4gƠT \4 n9[P!`]k POU(z J?TBRssY8XX+dʁI!0))2W1lq ߲  T xM- WqnfOJUo@sknfAsQFİL`Pߖ xA* A[m΄K$ ž`KA1H gZK;m ]7LCr߀1Ɣ:oقeX * Z K3fh]`w)%X^&qd>ԐP ͦ]emŖkr s!=}y%.i(W@(_dS/ڋanAxb|lRɒ|#:_2 =zD$cM_ +$:+Av?TIZkCVa={6N'adJQ|f.x5/s|K6ď()1E#bzOP$84UFe,u,XqDGY_;/'['l}):7ezb a'arqnz_[?arsմ+͟/.WW7 ؿN 9Q^a5xX.WPzzrܬr  B2מ6qzӟyljbMLc)_C8Gs\3d+*C߄aU&~2]{noӘ#-z5RA T$# =FTY\oNhH1 h*y!Efꉊw,Q咙ݻĞ1޶ Ӣ7P=jzsƠ8uO)EEsbq,V>}X/_Ή8f$8ٰ<OnL \E!"Nk~Jpt[彷Zw[&`lsBh0>ǣŢ.CkI(XX+׽㶲ZP+灶OLG3ʞqhC5;U>/OvV$oE?-*Z6%u`$Fs^2 b@lӋr.%l s ʨ|"12HP;p*B*$D oDPBuo2o=֣`TV'o< U2v߈ZM+#Ɂ_;Yi+ g--@mL_碯3ь!3ь,`Z&0ȾD0[.KG`8藣th6Q.=jdh) 84"26Z< t :! p9ְl+ ؤ%fKkpnG GCZ3cgZnZÜ||mod,Z`/֑hF=/?=-{7'靷Y-6yZqM :ۻϾ[2kn _3zI!.n\SwYW-R*iǡ=N>g}P'3{#zQ8el>8a +VB 2I 9%=w%ٯ (h2_ێk4eR(B!HBO#9n^`ލ(E12ƢJ?ܠ 4 endstream endobj 2070 0 obj << /Length 1042 /Filter /FlateDecode >> stream xڵVKHWH~"awaN,Bv[rұ'Xz~U*A/ffOnyRF,B `D(9C5z?,X|X|r#Q) i/cQ첾-~b'ʌXOWgуpSf?˗=h (1%BogN:vGE1 1 r4!f8MKvA9(.(Œdy:͡i<&3})ks!75p4"@˳`>|cΘS\VuRH : 3.of~ZKВ8Nmn]la}. >^)X{ikQDAæ׽*4 ]b2q~=]DGCBjyT ^Kb/,J/$d~ci +sJ W Uw^="މ~+מUJ,'V/VU߉,`K1^,9v+\}V^!ζ̳ʼ*}Y)fI0^K͒Ƙ/I@Tsi&MUg=ཽ3Ne}f̋8@*a&}+{T6~hGns Mߖety˂w|;phq|P[<>$Vs LԅPC8)-c5]b#U^ N(wnqgki`k}9,*w.>ksxV_R~>IٮDw9ç/!"JRTbht=!12[蛠 ZraOefd*O+bU׈ձ!NcX9al痽;.7+\<49ZԌ{tDn޹_U''z6 N~LrtUՙSi0q_\V)f)N<ǺVjEtS\Z j5f1Mh+> stream xڵVKs6WpCi =8:qCQa0+$G}wP"q%v+^7~_tP2ބ4_n ~rǜıp_wLcm`nt2/O%]gzw.܍T`tBS!>r,8{\fw@ (.@]+{br13@S/aD^#񲟺d#1\exf` ;[ lo匚 t[4ƛwh?AtѠ+Eq(V V?E л%M$xJRÑc &Mcgf2]}b\'3tZZr榟^ x@p] 8U6췭d]5a8w8-dk)1y]C0-J?W:}%n<> stream xڽXI6ϯ0Ce`I0EEK[̘ʒaI3}\P=Ӧ"-[p/śW7iHQ[CD#,D1%v{ [uD'Gi(@Hz8&6EQ7IWe DV=jz߈\:΍7ɔN+0 SGGFiYW}o|*G3{{vB{\JH%B}lڢZaO\y>CBQGM:׆Yk"Յ&$hwgpq :nԛt`P!Qܰ,V-ph! ;iv\]ݕ[3견$)0F0w@%uх0>Ap+Zq!*_PFţ')@y0dZgG,[*O+Ai 9bt S'H])U7s# o<ǐ‡8ha$;qDX_~q &Eab8FdM+!DpKД(7/2q'ۭ} "$}BdM#psIaEbnCp}^j )~@޵^cY4̨V XП~{!! ?h ĉՔʘ|lPGP!,Qf۱WMP°[~(Jz8!&ɂ Wy<# SUiM؊Tߔ $TS±PT5W%VS5HG&HKS,hNa ā)(Y=M9b4Ro#\Z=5S)a [S5@+}XO[{ "]LT[)x4TnqID&U^!駪\Vy+:tCHpUIKfe[S6f蹕^[S~7/qnw}QkN(ΦV]OveP̔KdP]s:ȲgҎk7_8K JȐmMY;g"&hդUNZ@xV6Pbx@aqws~IlNl.H-ߙS }eV+Huej}uk_uU"ckt*q!>@6ġQG˾NZ`훨9ʌoިt6'd"` ^$icj %9kj4V .hH0qa*1}`&u5V+WTͣfӃ_B t +Q4k,2 84}ޟM9DW"YP=om[=6ái˦ @d0x `Kt៞jΐ;\;>]O晦-Tk}yWB:|#@FO$j}71 endstream endobj 2104 0 obj << /Length 754 /Filter /FlateDecode >> stream xڝV[o0~ϯ@m-RF˴TM]k#0_? 8mׇ|Vajvy+uȏ\AdőVq:TmV2̽c1\}f)/z~A3W]yy+a(hĉۀ }O:ۡ{nUkI%(z IXyFჄ. O4YNdCz@T(U"ܞe{$u0=]QK'@Q"_nni+W8GC&O!noՕ;´:Qz(*El̾ "(F 7Da`uWfR?Hϕ7tnP( (`Y>g5m9c4xl7]L+"X@̪\B$ D5,[Tt{-'wa8=KJs֠L֡SQg;h#ƭO䳢^o x ؜dq*JGij+{\w[N╺B% ZN㾬k1&Ж'f$sjgVq*6`9i q}x/Sr=|F2hzWՔ᫂dmjW=dR cH׎ԧ:d'hP)T'h^zdEջtORH j{~ݝ endstream endobj 2231 0 obj << /Length 1685 /Filter /FlateDecode >> stream x[[8~_Ǯv 0}^ZuV+A%L: @`jUFps|α./7xC.\.U_"?|х|۷ցՏl>=aK^ Y! C%O|x 埃`>p]rPSWlQnaf;ZRm-n{;Vs-9yn0_gs `Pk@XtVCRZ`Cy++G۾H6Gb]nWS؀PI.̫.#P0DZKE](`{x I$R(X ]#g`@-gȧDJQUWrr"#DG&!,\߄p|&!Zؕe^I'Ú[UOr.-qWD G "g;M!8smS.?(t< Z׍b6eNWN(&w{qZspe4@BDV%rch^A\!(=@tCIa#,aunXƹG#(c##p=4}H*IׇZl Ud; *eRY'[OdFLq\ߕTmcђ$Ajri 5C^xT"xMVRa!tCxךj##=#"dS`bɮ-1!'WR_lt|3~!ʏ ml!QH<4ÀLc% :W2ѱl YM*Y粦> XHq8M4W̜+>NZsf7uZϷ:G*4]эhb!ڞ7L5( J#+أ$dhy'ji%Ano,҈jL9 |vNlVOj8t*v&pfW*lPtȆ(A8QM+Y3Mkctg,/*s/^<O^L Ry<82b$[kq 2\Շ+( Wz_ՈI&EC7 , 8thx\ yrAOyyUʣ3ͮiJd,蜑ov]gȆ^8v/f(ui'/*ﶨ_F=wz(BA94ņ ,EZI t]R(^Ct(ja}4ccz֞KpqʳP6l_ۆߩ;2.Ǎf`rٰ-ŸulD;Z\*urb[>ܠ -I/_]a:Hĥ^blŢ:G=tSճ/< ?1V6|s7\, JӮ|A~]{ 2&ݎOZ}4gApiw>Ǽ\q9&C뤛Pi?j9$Z^O]҈eʑ>pжsr7jP{ endstream endobj 2064 0 obj << /Type /ObjStm /N 100 /First 987 /Length 2404 /Filter /FlateDecode >> stream xZ[\ ~_E#$0Z HȃlnGp}}2v,`/gEHpTZֹhRפ [ Ɗz>%Q&SWjMLCb4f+XCҀ lF = )!58L F1<=F K&qLĠ+!aP_fN 1²k98$WLf]c<B|R*& CJ~c㈇aGgQ\x{Q"FHpzG"Ew4\yA"@ ,. I1>$,%8 ԋungg1?>륷+i"zUm,=-}gb)KG :ʠX6YK̜ P%wXqXE1z!Ć;cg#L,-Ɛ8@f֗k+!Qc4D74RE,cN efCX6pG,K"tqh3 GXYjjd¼zEyUdW׿=qſЖYgjӰ9ۛI9<{^=*( )LJ~Wjx7Z~Y9|r07?<9lDW/o{qH$뻿\n)B*R-{~9zss gG GX[g_?wwkᏇ?>FCXsko\#U"L;>]pmj>|)$5[w"iEZ *V skʋۻj;3V )KG*|+u|q/YBky'Uc YR b+`wʙKhov6u\mC,#S }zWc߻6f;J}Z`7xM6}EoG_lŦ{`Db;S.mC;M]gE{6:FR.mU&_DA:o~.sF~@F~HFKƬ '3_͙?jίB='Z  )h BO!))9999999999%%%%%%%%%%5555555555--^$$%%%%%rO={"Dލw={"D<y$H#G"D<y&L3g"D<y&'''''''''(NAR,Haș9ș9ș9ș9ș9ș9ș9ș|x^=(Q)jK^'a;ؑ# }j#W9۬6lmh@K6:š0g 6} {:":HHJ0IX7Q~na+K߲b^ x#6eE LFBQ~ܰڞV 4GAZND[FNTDTdlˆ#P1<aEc{9'TO޲B<Sp4ވuDOĊ |=ǺPAb['_9@Ȃh lѕy(xB.㑌8ڞU-ړ%&X5"2ںm|mqHL~TڨKqf]"CmÆ=*RPDĭy¢Gh୾Ŗ"Mǥ"ĸV˶%ug[9 U"lKx}Ks6/X8X"l@~2"x}rx ȆgHD{v\C=(o0Yʔ\gl.lHF ^mp,$ <$AA?l)•֋N퓕[:a&_ᙔ}+٠KmD/>yi.Ogg7Re)a{XԆmj3O=hd #.P\2c"2Wd/u KԣME"h6?EC&[41/a{0נ*lFUH S!_ jm;2vuOt"`x:q:qnu; 6.[_=+\縃6ZbB- [9 퍘ش!u7Iah{;!.z:{>JnH-3Ov'3[Q3W6mA_|} endstream endobj 2409 0 obj << /Length 2260 /Filter /FlateDecode >> stream x\K61E&skhEQ@RZ["[nKY-"mM~7Ñv~GWHa=`Vyb۬>_~Ĥe,R_H_~Dͥo"^gm)OI50 8>C*-6*?QGDHO؈'-DVn (\ w (>[@E-gܰ!w1尘sd$5_,E%v)#T=381n [r=4EYg(Z)[KQ D!(oYDfrٿt,I5Z؀B}]:BCւml9YvH` ,/jJZOɋO`@9kG~b@Ѫ_U*ږHJ' |tX' fU*_W92CWS];6=^GRқ^r>iSx~- ]Br:M̊l^[xEuLpt؄,ʿ7hт!Z0?HZj|t _:zHrv8!<'M(Lv9ñ`oڨNz^ZV k`͑ b9`^[yyZJFw]jQ|iwRA $1㒻d׺p4Kn͢$x͈>6 E.޶ *3NO|Rmy5h,yֺ lF )rH9<ռ ظt)fS"y ZqUgOUCwAF/x/MdC^۰հ­M\`m"`vs⾊@ê!|+ B"2zːoZ{SGni1J8Y7YEFhr7km4٦vӎ :Lϳ8Wb`p@URX\Ԟ{-jU{1V74&ϱvl .8h! 4BsM&O¤Vi'1~AfW 1u43ss/6ANt7?%I4<q:нjtWӰkU&;ZOHQvD}[g3ijµu桟t?C@1^ uqr (=`8܅|لu Fx҈ohcZuQވJP!{<b܀s|LsOa ܷfI~T=˽'!}9ނSuE;C4PӪtD1N ,v~Hhod}YLY)kb¬saIcCS`+ÿc xC?HUqzܷ._>#i9PNd]k䏊纚Y54Y<T~óZ(ƿ(Sƨd3F}n&OctFH?I0 endstream endobj 2233 0 obj << /Type /ObjStm /N 100 /First 1014 /Length 2676 /Filter /FlateDecode >> stream xڽ[M_cr᲻ l J$`D#!ˀS75kk]RjHA f>Ak9W5bj>П0jjQ "?`TrNxO`R^I1|G}^ZƜV09mcPh#X*nO.L}^`8^`=kߖ`(6Di!45L}5,8ސuk 95<đG%9b }C)#`+Kw+.Ҥ&LBUSP-G{btk:P;h`4BKnq{nqEhY}5`+Q$VH +ڀ/a#0Qs݊}J-SzPлj#% ) C;c⦆+dȾWUX$g\Z>g[7Yς{1٘řv~np\rKexbW}>4 of#`c]"VS*XeX\չYw@0Rd}+@Q^x8ϏONO?~髧?<~|һ_O;}V[-f؈6b<"< /^ӷ7O2ksDט;vL$F{$$ CXd  da9^-da-v%G,DHX }1{6CIJ+8ԕ DNr k#f/5$4z-X"ZJG4+C+HtJ3 ѓ1NAɢg3by֫aj+ ,1C(D)kg"Ak倰)vŪ>7r)SLX}D+Nb+I2#^"oca6XI>yġ8%I)ID!uUXB:>r>9S`<Ψ DatXۅzjey9<GR(}}!C [saca F]6lAzij4]{5;9%Q@"wrK*$6iqXQ@mE)v! @w69$ _C :tNg;1%\BI%d>< qM%vO Rtby!bnKtb%A,,Iܠo=bK*]$ce}s1 Q٭̹i\W4vnClB{̥~7x'o3ߩ!ȁ8"WyBS['[%$!Y$Hu; 7(vCxު/M8}Ĺ֐{ ~w,) z/WO>M<>tRnH.5ɼǧ>WOM7p>~W׏g<_='5,{G*Jyܧ_|3iҶA9(pq6ȉ@90\\\\\\\\\\\\\\\\\\\\܈܈܈܈܈܈܈܈܈܈܉܉܉܉܉܉܉܉܉܉<<<<<<<<<<6dpʁq9(T" Bd!Y,D" Jd%YDV"SVFYeeJd#وlD4jШA4jШA4jШA4jШA4jШA4jШA4jШA4jШA4jШA4jШA4jШA4jШA4jШA4jШA4jШA4jШA`35L M֤yA 7{7T īFk]׾IC2f zK,<;B]D%$VFK%@iÓ{=-qbg,KC?S]b#A8ⰲ_@as@S֑yI,|Z"~K 1zα/я֯˕O)~n \Z(,q&1oVΡ*[KyC$I*`:" BFBD]zpm뫪VX;*m4@Q\>6v5B6 瞣4/5KCJY& j݁aU endstream endobj 2411 0 obj << /Type /ObjStm /N 100 /First 1014 /Length 2302 /Filter /FlateDecode >> stream xڽZˊ%WhioTB m ^yfxG3 ah/>7ի&tWt'qb^Za($Qs\P1➛>=Zo{Pm/Xuɚ AXB6 u\Q0:N1lۇ->4r틂+8(?xy )7P)CX~qty|9((ʚ0*AEE]hI{> -:ZJVIJz Jыj.\b5V\!+.6N8G%ԬXyqkvJ"Y҈Jt!.=r*+yPl7Y>}^z0!-(2\Q ,\ XJtiHk [}^`Ԕc3pOП[e42)x|j-GD䴦Hz%@@۶zL. h3‰7@$>6@c?6dD: nļ m{Na B^4N>A<8"R* l/>_>?_~}_~ oz?_?k45h n}ߗ_~R^P~T~[dZ+I6jt!{ց80pDkqЖ'3' z[1ڨ~!0pB#H@޸A A8A05D+_PY`*X! lWkvc&d a8y׆ZJzW2Ճչz6R\qhZ5 >l^DLUDCl F ;l֕PJ>N A`k5' ѐ b,>zY N & Ğ?DYtbFU^a D8!.yX>Qգ#}NSf FI٢2V@{@j^͆qV3`ՌAD{ #>.r69Oh9,\0GM (fY40; {@fY졏Gbp&]@\D`bIĽ PIEN<@I b}`w1;e/YX1!8@0|b^O`*A̡d*JaB4;K}9"xd,g3Ty&G#)h$J>1?("%4ڞr6v("K;te Y2=ap0QibnjB=G!o0ufD њ3r*̣7蜝Qg"2QbVj`Yi k|״A$KR!uti %M{f֕AҞMLf\bHZ2+ Ht$ B6J펆m= CᆎDVED3^XL8xSFQ@3ܲ?Aݦ"jd/d5Ɠ#Q 4$y"_JϡU>J[,#Mx?@Th%G SEW UГ=--ql(}{=Mͱu O`&\+58-Ds9h\CX0,"o+3%$Ma[XN#MaO!|`63ҷChZ444ZaOyM# je+3; gϤo V"OX{ֺm} qkkAL$&I{XW{KC5j+oטZ˚!]c2/Ց-B7<ܟ"6K+tR|eՁac}ԗWl}Yy`K 7l¯A,%tެ 0+ } ;WU 奥 a'k+S੼*Ȼ<\RVa(.sL8AXX' sv^i뫪+DO\'o{Es ӌUK0v /e>ɾ3*7\j^1> \pO"?\bb°=cF5dF崾?@ظD> stream x\[6~_aċ(jm- lv!6G[%y&__ʖ=LQ҈ȃ:|{#&+ :<v;o{~<ח?׏g7NCG[x|, H\C~h?O7_o뼓oB\a&'dR!|sgًon]yEaѹ̹%N|Ya0t`Wp%cOoI'2OjR;MC^9dTUSQdէ'QTE0o3;4vVUڊ`}B$/6o =u6[c@{m8j|!8usL%/P2Xbz%[,?5d+nfǷ"6} nbO* gYeʉhΓVa{VvBig9| l:WH6fP?by cz m!rwNi^ݞ[>Acؾ/{x'>UX${p}uA2yv}ə KH:.Zz31Bh+^ھSY,m߸ pIad<$ bMV9OV䠒, 0(L%r/+]gd~yYck3莛@"Rjg!7SnB9J'U 6E;p;'ûwaY} 1@OG u-n4\c0M^dl&cO" wB^ jdņɿ%'^Bq&JFJNlk=,^ Sz\u(vSy A5k>{j0$t'cǀ92ʻM})6M7Jzs Fٞ-']j0J0+o|2vY˃΢j}F/kU^Qݖ珻ɇib&MS*"Ϯ 3-qCeQi-0$yZג /SրډD,ꤻ+nWP@˥*>"vZ]iST9O֙郌n4h;>Y7X'[w#2}˴08/4ocZKQ4V&ȝZZ[+U{}"*E~ֽvʼ4&x  G~[c@@qWϗ˂h,Sz~3fx^Z8JM(xHYiwA ur>txr])W)f?%X,fS#.^j$_5 =ҟ endstream endobj 2412 0 obj << /Type /ObjStm /N 100 /First 1022 /Length 2683 /Filter /FlateDecode >> stream x[M1pXŪ" l J$`DAcցU u}pՖJ=QeFbVR%' 54%wC..t$j\]D]}ƉF> X)tGi\0GK~wAc?K#9:I\T!TSq]0|ZnQ/xLFHD68UQ4[IQq0̟ U2c4xII:g\R`'qV2NSM6eM&^KJg /i#Md/j+RO]?w혃Kcb]{ q`A4 S 4Fs \ 8xN)׏:`P'hiCTDI0[_jQ@wIu{)]MAtlT0)E0ق /ٸ\`6+3A &n)f.*-%G?S]]T\݊ĔX<c\`6Q=/㧻˫O|o 2Dy{/OMUN0^0~3þMϞ˫tty˻V&pCsFvs:gw0${8Dڲ"yKlPMj= z@8tL$|#!Q(7dqf;.CI7VSnZ t|t p@*8_w9ԙ} Ȅ1>\lhdD׉$5PyQaX5P,3$HoAbCl7˕ꗇLoaC/ꗧ\4{粹DSe9[k&AוMNpzf#^$fz&5l2^1vZ;X),arDt ᑇfH^Q6XȣVv(qyHB z.KFsB5l'd(BjnJے \LYu ۲)% Ϟ}/ȃIz2x@-L*H+y` ġ=V.X. *[=HS *z܆ 5^?)0tMڳ+b\*U^289' ;tM)EmX֊Zm۱;Ab8@5a`<챋Mӝs`OShN J~H[62^o?/>-/BoX8I^W/0 -/qy{O__?Kp|>}j`Az}o?_Ӣ)O)/UwUu쏘9UtZˡ*H6C% Cdddddd5k @\r 5%%%%%%%%%%5555555555----------[ @nr -[ @r={ @y#G @<yX( !hB 111111111111119ʈxjh# QŦ& ^T&6䛡>_QB;JٓNrțeC' 7AC{{aiI4kVz@ F|4S#C"' iwRx&V(a.qćv:ݫV6)9+ּ]ìN!&V;|#1v|?f8td{;;s3ecF1/urRиc}".~-ot3#>]EνK$/F)]UIcStrRXЍêCm~8 SL"9ЮIX+L[,}Hb10~advR5  *r hAn[Oݛۜrm"n,%HRS[{&DC}~MXkC}& w,jsSh˘AbI̼v01z1TQk13>0r:haf+ Ml% jʙ\:(l+;fͽ!#11o)w1ĺ_mk;D87]Hg%,zPaSU\7Aa&gX5QuObE1$z n%ggݵc&vWl,-"O,`4~˷ =1'b2WT5U6v-uWr+rkm6&>l-l6K+xLFamIl.?e` endstream endobj 2547 0 obj << /Type /ObjStm /N 100 /First 1014 /Length 2159 /Filter /FlateDecode >> stream xZˎ\W2ٰY/>Av, KĂ330)Ǭt,ؽ0 M8UERd$VX5BRJ4hTBuUшs:1wՌ)%WL %-ZJ\FˈĽ,x0DE scm=4نWK뤺R*xHc!HT&xe.;AX1s/ܣ`o׿~rgJ9H*SHI3c ]Ot}K?L_}u zK=a!ӠS2&ZRx.VenA%aih8qil_S @lq!z/e"S@t9= sP>~v`:Sun JI u2`0@)G앚g'aÂA2D5E\:j?B=eLAҺЉ #jqf,"9lL^+Sq )kQE2"b-/PKUL aZ'N TBRfAԅ PH0N aaH񼁧+Y襎P>SDkUr/k@v ՍN9j|fJaKjCO;M |A )E]9zlA 4 DmE鷁 I(`.7Juw20q#tD0T\៨r=( FUERϣdr}trDҐejDS(=Qd" ezv@a!( =k:;&CZYg,0=~Q@$ԐWH / } k%C0W }帣znFW{ ĺ?v؟?k-pzxK%Lb UdqM_ ~> stream xڜT%ہ9Op4 .s+or̿fUջwWu7%* P ڃm]?Z3-\m1VJJ5+[@tr/4v[č]a S7o?{'^1jiDwtt 1p2*y4VN. c; ??-쌭lLhLzق=+=_?>]M͘fL&oe 9 3@UF`?`gt+i0g`:h@cW_CLL 3-Xb 0o W*5O0GVX `/}f [`qRbC,*jb0rq8Xx93տHM pghQwLh@{X8XL?CT=Y&wt1w#]:w¤V7"zz'`tGQ~p9U]vV[ 8=5$?%RPe\D@%̪\LTTd:B 5k+&j O]{ 6aI/ü٠J~PLp`ft~C?<0;Wm\*\5WQxUFXmM(r9ۆٰMpcQU!F{vt"Q`n3~=I4)@Iնxõ!ǬjE1ٔY4 Q gj@kšwgiE\~J M qI^畈 1rnb)J+՚SmO'Ȋ}4`NǠ,7HOs<WSg2AŔMnl̦D#_h0q! CygXm_P};Gk@{tL|Pwe\D24QfŚHAlϑ*zS+bVХխ.RMOexQ$AձMz8 tXꞮֵ^:S(;||5QezY WV9WIalC7jD 4VGKR6-Q7!m8kh\]n]ͱcɉ˝Ni`>`B+o bՊ=/(`.F<(_=ibZcu[>l̶/" a6Li2;Gx/,Yp2>Nv tԯn{6>E:Qn@%o@ -o3l\2nkV;=5!-$uF8DgλW!_d$۩?hf*0֝?3hKQD>ilj icl%Ԙ0.[0) :Q~+}V )Z]*VXi=JA[P=5e0,~)sjK4?aK΢8)! 偀K5R=$wIMNvwJr#_*>86'_C[?.h6b^S~]CN R0kK\Ɍg0eƭֲN{J["kFa(E RqoX?< aevt@0g#)pXho)r7/MsV4" _LT쒳{eXtvϮgͰF<fN~¹P,DS<-#jɲC Uu%+C@,Vt 񇙝6l CuNe$KWD)ylis/R^/i՚S9Qj/sk09B>GA[L4:؄@mz,gż>334baZàtIQ}aD*υ>QiA'U\6_65ip,pjjkx8Cpҵ6?g4-F+ە۶z7VPgKY%]=~λNs8qEQP]VCuZb]wbeR$A\x߻@d9;㏱jbKl!/ 9`sy2avo.IKC]VouxY &Mc]O ~kdE~2ʙ.};7f_ˁ.6F^͛Tgm|tfEqT-]KL ?5 e s" o$|fXuECRv5 n.c럵 I*9Hz+QaX!h72JSPM%`TdWŵ7֊z@Pm ڰ8EPo4[Ŀ)ν੐[kIqnRfS^vE?A>ԈS}N@I;5sxR!U)6 =j5DXqIeDv =#ҋ杝u}z,N$#xqzeA+aCpB)eڧ  5.7E?cU[a4FEmpp)ϻtVuZo#o"Z qݩqTrw:.65t̥lbjΪ/\,\:{;?r\DK?lXFUng*șe랛oJvd)^>1A*,E!mpr iM OF6i$uffzjnõbj:GvTbq<M_p5,9>Xv`zO~W% rcX̞ "Jγ:}c[Lkf|" {˔mZ?쓐T*9ָඖqpMgқri}cK8q>D-.NC}Ds|?i2MhM~"2#/hӹ^5 r{ -n[puw cx7JtXA,oX"b6pio=ύߍo)١ Ϡ Ib}@folĸOJйrsYo ъVnNa+,^̳hI<=dIy>(mZ'H3<5!Ő+]>ʔU~Y j"9Ն#{z1  =jh᳣ۨ@xdidЎ aK5K@EMg4&3Jںx!χh 5ec%rl"ѝWsl-EO0hMWؾPg2O|r`;T7tAE}tiO| ZD)9k_t炷d^Gg w,%pC_gVEbv izJHba'K₿%{TC.o&C@(EG3̣N+kq1Ÿ6ulȽPyTouKw/J%VLtP!]l|yGc#ȡK;,i@Q C}Ι)T~ ;A,5SgLVwn!- |3Aikl`PX9ҩ2~rkZۅx5L񼞷OHSms}\/6 /szQtXO0KdaGawO6<){ !9M}Ez;C8]а)|jXf^FdaJO 8wSM8<0\p৴vJc#F sXfNЂ0ɨ<dqSz4)8D[>Nm׆٢irv7{}^kA;`A͹LVGi,;&E)Z7|e~ "?]VG?IF9u.?elaIYhY:Yw`ʡ0ZP|zT19H"WC 0 s(5k61P|WE+Sa6:~T"} Q b1iR^Y&'"OEE[C.GҦYaBZd?R¦|Їu=MR+kA}_#N`dNv# iu툍xڥ|+3lj04F(R`;ާ#8g7VsZ6{z|03R=Wh8H B]od&r_Js]/fHV[},G t&>$|=7,/Ͻlț{ VqzY^}ȔzH`*xũW.ӎ~Qj&,Y`}E/d-N9>zݍC5W  Bǖs9;UR9[C׽Q*'(N?DѺ=:!d,oof:씫#e(MC1}k Pv*l[8y}ONTHy/`&RK>S\̜/{F".s.Nb9ܫkhxV<3j> TaƐX+[vW9i'IHU;-l(ŽIS97Ӿ&Oyl}^i?~_;{eyUv "?SFNXїL,dfv_Huhd]Ex( +mZ:"ù4 ׍@d8Pw8+?#x\}oz3sN܈o#s>F;@Myնdm|񢠔f/0#kf91QZlPmawo1Zt&x5w?TYhYw;OQ}١F?%{ÍyƄlK0%? ";Na 1Gв@EuMjOĩm,Y_\3d _ȌTPŎYLI3i/3-WmciKjQ~/W<:$ĭ׋{]<r? HBS~_G6 ύ7XW@b\ /G]q$4覗T-?:Hz!bhτ愭Q"JN3Æ qI[1FW\G2~>q rJ~4D3~ >5ܩ6RTwr$[6.fЯ+ߺQD6Îpq|ysd{GxY!e2_(Pŝ%L utcUٙ!I),yBY;ƘQpm=-DV%fYѡlsܱ{BA-|hgu WL-YG 5oЈUakDgOJq9b$hay8skq*74T%'y%i{%Y1!+)ad̟#xA9&[WVtO^fԊ&kTB޽wfwpl!v'OK 3c;ƂtLRUTaidj yW$a1q^>p +X*\bO[yx0hKq1V My4wȢa,BPn}OjM$C3E7 Ƕ4?Erׅal7Hy淉%{0{3 ٰ7Ҷ{E{kSX&e |C=p6Zپ%u]$baj/npi$kD`wV  i3WJXH6>N|lhzryыDz:+MEUfe vsyUZ 9؈+myn<;H!7yPX!o5똣4T֬RlgĸCƷZ&Uq&XS#D6ACQ xP4L~Q]#^ҡL/tkmS\F7SbXkV Uڿ]駐`4|f{/Nًo =,[ti+|х#\ˉً RD '#\LN3?{Z$heEy611?Ҫ+}Xsv55l޼ {-~*?^3腵T"S5fm⚽O5iXf؊۞|$f ,kTSȉ3+Ā}[}k 233"dXﵾe<ƾ55xTs"lڋɅM!R}/}Je R>Su\Rh@t)u\Noܮ󏼠@Ÿ{K..g]^rOK0!v/jr\ ]ZX~;<^%ъhA&Y Q\F\ul\.:jt=~KY}: 2\`lf;P1Z0r+bt+伎It=k&В/ԩ%+%Aɥ>O ۥqnRA:z٩Hӎggq̰Ŀ"ҢxbQYM~LVr*,qӳߟ6gu %g&zܵ 2:Ŧb^0-BsAYMܓr;sA}Ԋu_G]~0i(shoqy 6 80H%h;Z]JՖ5wUi* kop|yNnOeFV!0ۥn'|LߘC,aS٫hΕo 7>I"O6OT ')0-RYRYIHo؆9P -\PKѦv2g_.W#/3~{mf/ hU5%$=6s%ƺ\q\,'τh㘳0pѨ;lR~D|[RK$ӟ8Dzj:j5:V9jW YDI~Ψ*gF8lzBLEXIk ^xq9a~Eiӹݝ1SJLҘߝ|wLQ`& jL YcYU~̯=^jTpU8NW"A` pzy,'~H˶**~Mg̤U =F/ v}w+k=\`Z%6MC*{>%0OZi?yREϷ~yA jb,ŕYF1 *-S9JԞ B߷6"\34C eO̞ ^eҿ[œtz1gȮՇ2R1L E/ӫS!PWb9RT}:~XLb:vmwyp44m,ݣEKXs"3L/.>Spdk]jD]] vcTq[~}eUա _6ݰ_urz15v7n=$ʇ-a$$xku y :)/?w_\^3.OLxShWp;"vbŏ52[qzKpf]f Ŋ|Aҩn/}|OnDz]j'12-KgddhfožB\N09VH TeԥdH?_6& ,/ux>K|Kֵ|זH͎­mТ{ZQ*h2qקD7]!xvP} 95͕ջ&JTz-b;fYFx\C2[Z/6ָ&q&9a\r#[W* a:;P'B2,#b"3mURGC4Y@31X~n>\Ɗʀ&vY:XR} x u_ړ}d,b .m'FGqi/ti|#^}f_,1dD% hCw?.LyiF^xTb=l4*SLj*:z|0jlذm֙VBr@'DGw8u^mt@vLYS$J#'`K;~g+ q5iwݾʳI O &([Ȗ#,nQng%ƾ91۠YXvB#A9g\__!3Ml{4 b`vmK ~TjaT+ }7ݽ qh~agxy𳀕G>sM/R8offLƐxX[ʏgG. 9ko3AdGMt|& in׶=+W7RBu/Jß+zWU OXt}EHd^d"_b 0TEW:$_?^>(qaW2aop+;tA}n"Wx}5I:.]=׵%mP'|Ya>ә1 <:xfa*@+2^;]_yڹ܈u뇲m[4;3sƼ\~rywb:RHʮ(9֋qb77w@~ ɓ>OQT#%]:BKs1%Ym۝jDOY~ډow!VIi; h%_RmI={FG% w㯉w澅ސSKYTzlJJ\kQ.(K%:P}{?._.g*K|FFn_w~sЧ%m07=&x-Ԩ3p~3v.bB1[#21^$oHS,4VuQ+#+=q+{W9)o;:'2皽1ǝ_B>Y!~ JLh$HP4H x']+h= ߋIcݱ./Nڹ|ޙoyyPCGa| sM.I3<{[#Lek@6CP77{ÈWE '=kN)@OI9LH聮%e/P}Kc9d+\8O'_b›]|1>[!4F4tCvqL*ןYn挂|*'r?Dzɧފ]bY3 3y #Nzx1%ڼ}lw _WoLkf4P-%`+|H#-BAkA P 'O, \}HGPUJ\Y}V`#|`i) sy+w IWnf[ALI~ݚB]K )eY9#֥j~#eL9ka2e Z_L6M+٭.}JQkER*ʨ1vt1Ϩyx@5cjFJ].veQ jUZ8LhXm E}:T buV(S1?[t]j R \[Z2Z(2H7uL4| %1p$zj,D1iË5joW`Wcun9UUjZ`̦ٔ|i ؕǭ ^-s1lX,3&\yD.T28~MMk 9H~N*ܦModnvNF 4lK¬M{!zJV!lfޔh.qă Gץc#CWpy-NB)CbjjT|qyo6]" >"XTImVM!Um%Jް /Y3(,M.5 'Q 7pyU$a RdqJ6m"$<=<_(t&* ]t|> TՍÐػDsIЌAMmi)D$|-8 y[ڗ'̮+5QGigEEy,$ D3tW%WՁN~-7hd8( b֯%#&bi SblY 0$/t^y0$ESW鈘14d h2}hB(0Ig1WS”{ܵ{Ƒʢ.Q6ijSe+Z!^1T~-M7;O&Lںze>$;]e,7ט*IZU`B_ϣ?b(sA\]rB^%\·R>f FLC62bW#%HEtoBЏ?0.E3*y \3H8ˊ>%5"'  Ĩk)c]+cmT4ؾ :MnL>=]>#19mZAXy،.mV 2YinЫ4vD㙍PNA˘<%ŋĘF~OjdS3b }dAtAGH-[Zy-jq/ 'P, mc:Ʊ+a Z{P7k50'x7B!>Pjm^xV4hCsH\]Ծq w%i> Sshv)5)A*ѹp `IK=nz Emv&2RHu*P\&ZQ^OY8~Tv|T@}$!=sTHS5 U$H~ ZS5 VtVm.Oy !yg֝LX"龯%x GhQ/*I⍵dvׯ}9i 3994a|]tGGmwzbm2%F F^&2h}r"|kOg,|C~ί  UI }aO3CqzKz-'%.Uazz #R^Z^sGFtF8c+洗;L/T2@R*U8rqg𕘀ߡF!X+y?2SZ`3}3,TbAFѭs{y3ܜ uAܞnOE`KfqiOW";B&AϞ3o9̆''itݰ]*qc_? Fݝ!z({ ̗K_a%@I]Z9rhYVHA LZsw}RNtcW\aon!H\*9AjeΞpBJXC t}yj:ÜM+།lBRsxV`Y[ҮM8ݨ<:+l\ Ӌ@=/HaP/^ 4NNrvJ{Fn$V/DuiYaҝ\0qH9҅65qȑs.FZYmLn[*bB%\`Y7c6\>H_Sm_C5gsK0b}%`p*\Џ ͸4f~Z^O]5kW`̣JkN Ù;m] R){4ѺvTt56_:'{oɌ^=q!1 9\_/lBzt<þ2LjǣNnt;7i EDi~_Ǩ`!_/+ke0*-w_A~H?Q#&3>?O'8v3;B\kn|Ьy2Mǁm75~r߾U3FG m\,Wf왦[9bVH(E˟oV(FHޡNVZ'z@$Ȉm#ț3g!sU )9GC)/<.\ =-КY=1{7aYBTA}<{ҹQ ⵖ߰ӼwOP}V׊&8YGq_ T,NxZ-S[])|}q FwS+V'Dn-ktt :VA8 ųV27Ccvfad:(4 6u$GuZx$v;L# IT18%RTA& &KPa.82+1@<,׉*a=hEw5GIeFj4"hL1 $:~ ao~_b hwLL94!Xo&)d݅<yIo+-;4 ^}^0!wQiy`'V#f@LS0YboT!i.4^tBcXvl]^zkNҼzl͖֋*SUe9[ \bU- `_H=$~56X^͈H_@7,KvA6e/$;Kml`)%avs;iGYLIЁH@$|֖E.tUa, @tW}A6bf1tA[2HrܯE570?΄7!.~&=hؾ%V\f3^J,ږ1IO`J8 FyBNʍR2G7)v+%,=[O1s|3#v'ƀk. \(˭!(\S 0"+j$$|(ǎjYgޣ13^ + bƸ2&`MzQΗkr':UqF>f2-fK}(."O\fFRja2^L"g7ݖW`P(י%9F XڭS.^? LhF3@ !R|6F# ;jш.u'@+Ql`t("AE_38'{OCCWm=ƭ^2&DK<:Ωmi GF*a/"2"Q!BpF}+E $*k<Ѹoi>Vj>E@_ޤqxv~| FNJUZoaTz#2YXD;' jS-(!?חtusy"r;;B˯)d`uX3,S{UKlRm3*iMU-PZh1cz;G'4ءgЌ+W,1uH)u""ʥxiXMd/Z۷0OZHeko\ K tBo!bW6הmKFwB z*?ZᱸGNPǮ}T>6p'ya?A4ۨ0ow[v96o l+cj-xB ɛ}Yg 7)=Uopa",m]gS\԰p!n·`JuZ&/"B׉uc]%zlz񽣂9'V7 %hEfM7^db1nt1ėErr*%eciWőں6PGhFwTZX Eq)oqe4`,9%=.7pU]9sy0Ak#V+M3|N6"B cvyux,í(kLz1OEV&D:Hd-&0"AzHK{+ٮv7O&_L>>vvros W|p[/ W@4 dgQH¼?Ihf;m@LIe誩f'/4L_D$iSfef((.o Od3bM@ՙMųDx-غˀ^ɮV(Cm~dE42:ӵ!yŽtI fW>3pǃx-q KNGk!GB'+ѾhjMF7C`F [/ˁW7iX0#T1?8ZqRhOOi:@n1g8PJq1Ql E-{FVmg?FRCcSiHJ6]߉e.BU R[>-:7E14sؐK H{tcA{OB)Yc έ)Eh͎u<6b5y=MδE90m!q:og\*8X٨)1w{Wf2wK6/T9Nj^E OD}C<^3qXi L#=֡woEyNDFL"8 `z͛xʸ2ozEY/{\b^iLbNBjUJVi-#u}Ʉh$Hp &6^_/gpYT3ϗLڕ%'zg9eDR\wѩ4 vX{"ՈA#K',,qzPFt4wMwwj}aE, W冎B~iED5Uxrd_'mԈ&ҫخ9 Pfq0Zhֽ$bis5ICq'Ƭ8`=d&%mnnuiN~C f~:Kl$6"Nƥ^CEe< wj_u`0ҦZ˵SL9wV.vIZߤWI{`)FexJ]3l'6$M!YTSA~.ǞŸ;F$`}YAK&T=.`#ҍ,G[}9W`f%?ށiq)㧉#-$~?X3)mIKXxR`Ԟf"zMDAgQ30UTنŶa([B IYahzx״TKbKu%m̝uF!V , 9e7UݏD4\̵DwEHiA-o/lpJ 3TW%!nJ /TNt,LЫ.goAET+칣Fd3b`0JZ3́Q9WiLQi4W.ϓWP2R7l_^i'ʾ}qyxgGGn 00Wq ~%f{}2uR Go+m@2!PP,'ŠV.p;IPHW^OH u@fd@zc<^90M5XA <Nו[v[M4zC918ltmLk@Y9Ipb;߄I#[L`^wz 6D1w?[xmf*P=k͜Ӽ1fO_fCǪRBx'y:Rj`FF\YE&yTͅNrVf'QؐhU2ʲn_fW؍9tƇpwzh~?:V"4;=u"r=mvJ՛XK'z 9p 83ܠVpInA0.D}@֕"qM!K;S}T;9 vrLo]v@>Xm`^OocנBcӔ :G;B6D\]'4ŔA7$/b)뻧?S#|-W%-Fù{賬ao\jRY9-{ V?3`CFGZS{ZA^z2q0Y8W(4CAɴMWQ+u+MH5eOjJi$Zn6ST B-{)M-JaV:ZO/ttE^iqd ƤWΦה1˗%/T2& +ʇtKF(>tQ; A`jpRe,l tNja}K$DCyEꆗD"lydrڗzSUr)slN탚nSz[+6rm*}CJ'~5g-)њ9x!'wDq 4v}]-"_ RĄ?qg@Ll,#wsV"+msTʷm +q sW_^(qwO2 +UD T_ %65xnPDUԚJT?vfVrЉ[P:4ljmQ bmkHlEmV}ZUKd![-Wzw7RY1 l>fH*ˍΝjb #F\3D ΂~4]uF/)Q7 WrT cը!e˹7MgYU =,_@{# e(SN۩.uXg`!Ilx~_A tafƇžF A/]N(?6@\@Mkw9zJ-/!kAvz5grLW 8H.KNZNE]Qn˓ oKx3?D̵c[ tu,2¶pNI^Bx6L/O7|.?Sz LVМl"wmX13v=[j^mm}s|΍lu tQ1Ye#P_2UdЕ"w-;PC?}kg>VMמAӦ,^#//~+VYL껣k=#c"!J} # Xppqmȼ4/ˏ>LVJv? l6ɊhCV#r{(ag俰9-JdȧUǸߏ #=oNF<sî+z;lc\G0u5w׽{m\YąAŴ TSvw 4$&@Ve' Ce4d5}E6C^_/>BxtQz۔k ;<ѳgl漅HP ^U?H47Pņ.8Q潄l4&'6m/$N¹k[ߢ-T Sa?̺ 1Ei-x)碿C^n5aݮ˾;֪暳 (#n{ǜ2mϔτRig-U  |BR1QbP|a9Uk{^iwڢnpP-8\­BA͞3@i!Rb5'OE71W!sOV,ǼTRF @N1^/v3:/Fǫvc|xڞ/X2aayyЀOBk"w8q?T\@ѡwZfe%ZQf!r'LjmK ϠEJ<]l-i xe`A爓ko H(J{__Z[Y\e^niXR_|~z&*Hɛ&1;96t o/>Z5n0J#+dh HM)M=f  bz S3Y G]X;7Mӷ`s3]BBf1s3ruq#P#GJg$AɛRuäoä>V'yJ hɯ={MX  +YQx\&ޭ8xWa!T=tCjkv=вYs;v#R`ůA.wVp$v$Ej%KM E6Mp8y$XC'E+&;qEҦ IpD}`%"*S'! {,ty\5J9$B7^A9T*rHKq^,^Z86@eq.qcT4|rsc₢Ue:BoM.:k߹vETC>\2CP&e!`Y$.ݽ@@T2XuvoĠyL[Z= 4p*Q2)"_4"V5_g!z 6aqLdPNw?zUnFOIBS7܈phُy)&*cHS]\:WOC{O/BIH+Z& 5[sM+#CQҸ~7E` < k<3mAe)rgDEYB*m9vW-ܝnG.fWV%񒆚1yVeaao&ǺUjClƐȸDI*#PAve-s7dM~A`B%hG?X+x&SJ&y5$$̴iB,2VcI\Rmcxe;j9"h.Ç;B>9fZ˃]B$]{V2OiT9?Ppeچ 11%A( &>>{_';6( L SY\\ZL2yQW$/ M0-'Y=jG$EbDZw8<7Tua$hC!/#3Λ*1Ӗswhh3u(ߏXz?cStD@#oJ%MGZ=[^BLܓNrŦ6`atat 8G76Dܣ2X1icdgY(;&1d(LbҴaFnROFiɳc0~`Tn7]QE_( oc`n(%_Uz(yk#es(}t5G/6\qU+B)3+‡Y.fsNf>fjv|bBu wӰB$ bʺrkRV[ɞDž+J#΢ - w]P2+@s=ʮ$!}3 qab=OJ(F| faL1EJh+F׍tJ~?^ ,ޖWwo4Jŭ07 VDDY̜{)ᬐY%;|wm!iME+g橱7#ftho`ڴx|Js&q5_q9EAR= -pU( Pr@KhtF=1S "#=͞# ՁΖ#u `,CCǞKmZ= [#t \? Z$'mJ߱ͦ$퀤#'D[@#{ʴxPӋRN036zĒjASTE=m~ﭘ|lg$^?$To{oݢ xUB NW.bzL9%~藭b|h&?yݬ3$9RlL 0t@xn>[ uB-d5J#ܖ;\ \J-W;R;lM#lC>wudLEFSħ&rMiN}r=PgAy_Pm4 [7h*r_'{D^a+ o2r "Uqo(d0l+Be$vZ+W{*Mu ?t8v 7lp-JlG U1mݼ ]%"(Td8ߴme<*W!Q5Sȫb,B=EJYԹ`pI7(Bܒ JRZgB!uZwgI^~W:*{t&kB/1&ͥ+^FOw&T?aRo͡>2j߆%lt`n-K+ԧ?^^^&ldCi&dN[qQ6OFpP@6t[Va !xDd 23ACk<|r£^*+?`z#/EҁQĝ4/n$b~cD s MOyaİ>M7t 0A[hrgD!6gRД8B"jh\%/-gJ7v!t"9DʑLJ\Ph 8UUm!8?|ZAJ)AR)iA`X XnN;זUC,aB9O!B%+mSdȓ@3"q)nZUC> ?ršF8Hspe(h6ywL;2)80bK7g2Xٔ%>[^[2/&`7oQ 嵚LczAesOuF|0Yp endstream endobj 2563 0 obj << /Length1 1386 /Length2 6039 /Length3 0 /Length 6990 /Filter /FlateDecode >> stream xڍxTSۺ5Ҥ#H7 & wAjHB$t^7J](]zQA^x}kk͹d30T`($F$[ PT!4c$f04 T,PD@4HBRQU C!ahN% III(<0 8ܰ!`0H#K {{{ B('y^7 0a^0(=hB$g8/1 C!H(0Ðul$W?ѿ#p #H/ Fa^`8n PW2 cBh8׌¿`Y UA4ɯT0v}+{GBt6Ex´T`&ۜ`PJ\\ =| ¿ ~;31`pG 0@tsEps#Ik9ƞ`0( 7 iݷ43(@PJ@ 1@?X-# W}e?#^?s顰̅xMtk;_YWwGo?_v#| `UjPs_ՅAn€jPB:a-+Vp /e77 3@0( |XA\w4]0YW AAMDL`|~ ,Da!쌁GɯؙhW98rLV{[0 B2?Ȅ8UbP欁gՈ" zX]tQeg: MqDmLПg'Dl* XG.d44Zxzl.˞#wN+-n"7Z^w D8N$Ytfom%7k2SiCu&'NwiW`O4(4zgGl)ð {x1)QMmX㸅ȣc7RՙݵwۍF=UsRպ\RfAd'dPYcBA{hۊQK,Uw ^4mu gxš? D?|p{jn+Aݥң"ę7Ej:"v"7[Q$[>S 7;<Qdnef&NJ[DVҡ5r=gUw8(BJ3{9Πsuwo!!|_mTEQkWM%i݈{1:O;̴LVAOE;747LE?!һ$}MaR4͕zWd'~ 3C?~ՖSv[&-Nn䃼@jie5{左[F׽Ts UIȧFr):]JZY4%P!M?WșhϏ$ءaSzGQ4cQ˚]WV?X[t8 4"Se =y<#0lZp\7.E{:pU"U^hzzIǶHaITX>oxYPb'yq)F~Oi7&lT?ˮge(l~90qV9]\|>\*Zdxv]W}[?+gM)e Pjo}q}G.Aj`{ƴ5=G3WC*IDzZ3+W- u˳m7fHqw0LgJ+hR7RI[<]6C3WILggdgltyͱJR%5j0[0r'm>8i(s>{meǏlp|in|;ԙvgn]I0S? !0j)n-R}E:/!#G㨛U9:o۴?5f>b?^\sNMܥb=!ڌ8wnc\6΂'2,Uϼr`}Ʀk^%]q[9NJ [x;N&"- 5z.6B<{5B޾K~'\}BЄeG4lz}]g$-!JXo*T2.?`gl`)V !d~oѣnW?wݑH ]@ O7}oz]y)1X R|[727r4UE]zaEi-U'U7yYhc-b0kx'8tx.Dѳkx%{@! f njuɁby蕋Iv|Ho J8 3$%ͽl˾&wIbpa[rfR cG(]S6!bs~P^Ξ}<ѐ&A$㰓[v²s&>'+Su oR!Oωm") gK[A!ţըC~moC| [P輱:Rǯ.n"cd67wK6Ù_'Sp|,F|a.2))9 \++ĺ| ,"bBnUhME3ƢQ/~;XT悔 MqwQ,;[П!%7QM9J0XHtvdK.8JpS\dYiہQļ J)N|[!=͚QbY%F~=Q?cґF՛^gl᦭*Ҫd_-Ei;·'Mc]L]ecgz z 6R kSHXܕj^TQ J̐e4>c V/cbje`rbqؙaΌ O`kn_EkV2BDKW i7Y͎rK%ȑ/ɷkhԵW{|Czn,)v_-vwı{ e yѼ5OR d;, ]kA\8]vn>&אY8Ca"r7q֚啢s;<5 Ll@.Or%Ռǣ==+䂓6sS/n2~ }URڈV0fo0pj22fm˨@.g^pdt,Pb쎆DY0g+*mռ?sngS~)nFXN`fLe鳨N}t2m `^uyu'cS]0 `%O)Ĕ J(RK0)a䫌  "MO-5Y@+횃-aF $O8fh1*N>niȩ.38Ep:Z=g\P_kn+:Xh߄oqʑxXv:#-"]SY 4{r#}1E(BuY0ՊcyOB4/rky8H»rCo 27n'EPf^X|;8Ԃ&Q`YKFY4@F3nfyXܤE)b /c=u1r5|!*x]m:1LJukgsC:!a\ ݅xVfO^z3z:G/NT+t kNQg7ʯ62OWNm7w|PlU((?=$F_d2R^_EU\UE"||wp_*IA؅ӊ)AĨq\ݱD?jTI?"+!r S ;/B،1ПKfv#{POlduk"'r OP5KֺAyY9XbiD*NQz)hrM3Sv{COEW=U#sSc/$.gK!Aj Cb%\cV 1B&m.T 2@"fUR_B>kqQy'E w؋,%t=/齗AA]ޣߑRFɓfab<Șp[Ci$q6qnyQ 7(%CYFXfr9bR3ȓPW@яPHVrJU͋7p,lk_*Oh}'yIk|N-LKR}şua sjR8Ė8w_noUmNf S`{*js,W|ƩI)i"flvX=5S]j}1w,oPN5b* ]*"KzKM%)։u.MCI.LDb#P3pAk˪kSE]u.z_|>M`qX>u"9=zڳaz s}%p^5`,hoN~Jxd~;B jwgTFCVclSd,iRоTsIXa-s*:EG-t>ğJX"[ss=d_SK hǧ'y~{j2K` ÍexlTI&yʞZԁ~᪸ nUmV}BWQ9MD`Ͼqn /ο`i$TעKr3ݬk-=mxA] Hb`#b\ ^y)Dgw06|bNmP`f&2E%{ E{S0d3)Fy!Pש݆mO/O&h@*-.>͍$lmKPYg5PCk-Ǧ *\Z&_&FLX?o-X=8~8 .+"=`Yδߜ7W@Ce+37q㼮Tw;?Fz0| /|;ܘ:o) Ds =K-a鴨\gWE > stream xuSyQa"AXHx\dDg"B+1+|&WY#]AĆ#t rt&TA>Z4s:¢gBvP#X4L,SB ]3i̜!>@͝[q?,fδ6Ptw'alPXp+c62@gH4Lx`Ѹp;џb B;E`B !@5|SGa5 V ku^(o>H0fn_T06x)"o1WB;Blľ  îWALd3Ep?5wO-47˝dq\xӽsiiWsYw! 10uL 2)5,fμ87 `px.1"`P @7C0sN0aB0 Q̯4xf.=eςAp+P/AIg'ϐc0nYXm,Zn+t^fD6r)m`9o9L{c" j湥i0=gCT~Ф5EkcϝWFWO;T&#񺓛Qz|%1͏(u#%[҅S.x^Ѡ[ꨂJvU}E*&6޼d(۴dzt̬]ӣ뫻5S^ّX}Dkm60dx0t~zli^Kɚv󶞆{k'֩#%ILf=?x$6wjVurhu(237k<]iu4Mтָ'" ^&?S^PZo#fn=q-ޞ'IS 6Ɖg'v5+:+E-%F#/7삯O$1w_H\W8PAݓҨ@BT9>2hZJ?U7[qf*L&\꺪#oXl-Aih\Fѹw)}ʭDءx5{b 2+: M%w:~uxe[ؤ=j*/ާ z:V]q[e"Y)sa@&YDtd[~Lwp[:eMY1uX|ƹڪ~9qluL,a$+o[{$mr>[4|x~p7>Qi\XZT< 0\8e@<2}llDUޭ\Q=D-)p#1ve9k|U\3)J)}AؾގWuЉ<گ4kli3[}!FW7=81&A[%E R9etI犓%?Hd)g֍{}:drވ>~s@ҞhReQ? {#nq69WxKKԇn7r겜p=*VmI.xu$ #c|?M>ՙe:Y`{Yt2C eͺiۍ{6i8U捞5 K֭^]%+ ڍ#VE\~E"Pk~%lLs+ęyoj UVHF`iͶ8QO 6kKZ$M sSC] ąhv~B1Ja:`:>LcKRa-4&w([nR(UK}5*a㧬'R4>o R:`4V̷(2語rnxjo \s͓T҅ اPPhy`#qRãvEjA fR[SiNuC%eNy՝թsG9޷h{cdE>!Gm,)hi|-M7Q21dՈDZêhEm 쩒\h endstream endobj 2567 0 obj << /Length1 1626 /Length2 14088 /Length3 0 /Length 14926 /Filter /FlateDecode >> stream xڭweTܒ-ܡq;ii Xpww4$ݹsg7ϼk)ٵv)IDL Ff^< a@sōA@^ 4Xxxx(bV g::Y xbeaupڃ> ljj@ d [bJ:2R)E ll Pv52[]fV%08M>Ҁ_M@g;++=c _>rtv});\LA 4U p04s0u}0^  `fhkQo.Vb@pZ;]\>`>οm=v;?9X\,5MA-{s ?f_; h( '+'k_.}whIW[[Ecxd @CjYz=Z fDCfF\$͔@scۏymװ7:Z?t{f[Z%?\@{!@%{8~pVW '7}doJ 򯳂1+@ofϿN#ao`ڨ>6? M]?t;~",:YejqrzY KՋ |6y*^j&yZ<_weimR>{ ֨ڸ JO.7t95UT M9]GVIq4.Szm U{+]XxJ>coǤI #z7'7.G0WG]1x 5ZL>*mƭ;d(Q*5?2Hˠ4D7BG9je*k҄[tc1@}e`H*aCE$c/qs+s|?jZ]wE-ж;A+dzIs>l2#|yD;jӄge=zpgju3}o=E֢a]})qpxFMU X`x,!@T8Fc h$ 3؃ӷeEU8wvD<؃%UG zV`WZDO;*A8"(q]oPr#ʊ8ݷal+LyPiו~H=3)O'<*e%fpof;>/_g=w/i&X)cheI#'#w,(. ׾k\peyLM76;+fÜK olIh@.*Ғ6ͼk1r.mJ9+V*gv*o U#Ot'X:Yŧ%tXIz'av `#]TӋeQd`t3ɯ j ,s@,] y~ZjB$XK/E}e}K4%ow_Atwo2ENA'amy,rVdY돨ZQ6~8b vUl1m=tv,}*< ~sVWW i~w{y!&t3Q@(rYflSoll@WQ5ּI&? =1:g\>V`{LUA?R3kգ8{_)^1h-2>X7Wg-cX lꦯ9j|Ͽ<*?4׿P\4-g*څ~Ja K_P]w38Us`5ŸS/(^#ܞSSStiHG`.^@uSEdr2tm2 ,6;/U5K􉙋h+'Մ#[~Åh}-AD4)(q֎in3S8J[=t瘝j:FeDs)c]$ph Zσ實l\7 raSL9A Y>=DL6l&Xkq<=,/_zySYDŽc#Zq ;xȁڨP~=?ϕwnlQ΀|DΔP1lGNGfIVJLn7}d^«4.s]* Ա d%WRd/`[}{ eCĹfX$ sAl39jz5oA]uY2 M{Nu q(YJ)4aIQS!+ʂ}XQ!ܵOle c$(%OXL98d+ cYmGyc=c\MM،X+G׻T3n9,VV#Y9Ly`p=㟬T}C 6` cs 35#P= R fQTYiiC@%m3IKRN?.zOr̩0JĦ孤e:9 h!)lzg4&eDɓOpՙ6{c ^Y/̻ xLy A6fq7{ulAQIx]J+* xH)16Po 3^jR:ϷS d]R-HF}QSWx^0@OXrVk'Br#莻eCk#kKTc!=`u O]_|ri _[2oGaYcD6K7Nc(bKܢ }p蠯=V [ttp2rESYVHTF ދ$^:}jy"P3.U'[g-BI%Fra:* ]*Q2m%F`tuB@X-\77)9xj9JFߥ@˱[XL߄o޶dpe߲wcXa\!&Y𞙆WeN`4*0:>j Y@/GoFuQ)ZԃjrC]#EWL,{TNᄍ:O.y,K7^RcE_O3 fvl=퉶S4SULr*Vɪڛٺ3L;A.'N)럫lpGaJ,|sJ'YNJ9Tߑ1vݷSԵ-J$}Y ICUInE^}3 p h)0|5TaK LQU͋(30 Q+ Fj1Kn4Iŷj _\)㭥KxF98l>~ca  fd-: 8iz_cƸ9~ܩ!jsM{*{WI-]R7g2hT]85^%ڣ߸7~ߒXe&Qvmy]fk&#)T‰Z7Qx ' l}* ݛw7aTRbex`fԾ|s'VB``O#^Np3ie(֬RT%e{kV:alLJZS~NF [m̲!h.xqq[Y<،*1֧pb홉1EE;Γ[ZDN^ᥧzOf>Q1nJ0LʽGĮ/U0+X%XփA9 h8J(Fͩ: %1lfʃ'NT$Sݛ`2_~UkUh 6/g[7*Y#=@X8*#[5|dq(_J gd̬{5 ¥/kĚffePvx7޶]!Y$\CIGW DɘT԰o^/>4bhjӪu}ZDl~[mɽuIvgSf __\Y~"z 8LPg#_ 2sZ+d*./,'ƒb9 )\RjSoO !ásV 932t޾卩r7;L0eSVD3 \ ?`6vpF!$n][Jd{چ3P\?xu^.BYE,PwOk"%RZЍcQ`3D#+ʳ`.n0tL%+)ޢ5qW3x.uXX]J"-WEy4653}>R#?#_&xo[w"# L k4#T-3Hf)PL2[k_euwL/wܳ?TFó:U)|Ȳ\$=RH9k51n`AʼnO7>21M\&*}NeQT fҿq[\@'yV78Q3TvWO'6—bJyfaó B*5MavJXԂW5\I2T})_`g*Kq"W i{+c4وRŠgDf.GF&C"տBp-naWȫ,G6(VZ|z>4'rš[jpƖWHaYɐIjjCSQܦ{r24}b m'TCiP>}kJ$f)F;^nixr.8n;z49]|lHoV_FK<Kv'2 DU<-R ׺ 3.!d({1,Ž&蠑wj=+"PMRH -@ϽmsD˒: ]tWѴzO$VS8ZRs bUA˲Yspڝ8 \a /N.T\NP*qZ;'#4 VTt/\ niLS L˽D!Нz$$d0)Kc˓6{e{ѹ}ODI/]!_KI LOa@}<7M3ܯ?$ũS ׇ&^ R8.pt>cxcØAj`A;9- QSַWY \r!A1z-.}64G6ŷda/M*aPͫ`gRLtX%k&[~FԍTDPAf~cNШ1WN9=ugkW/Av~PROY{"aC2@ ɐ) W+@S!gNa(;!6–'=Ul1?[a`]&Uy" NOqU8/qƥ9M bP|ɅO 4Ycj\mveT*.FλGnԻeV@E' nY{gO769Uֻ mLS=Щ ˦,d0S# g8Cv:Wsv4V@zHtn;]jvKt+jlu(aYn5exx~糳02ȗk}/i ,:t;PbdjGpieNY)KwߨlucÁXS 87n6h? :.?Mcc|t%R_۾QtXxH^I7}>Z˛o\uLWX<&֮g9xNfPd|y-<ԗ"/' !"C<60Wcq 09.#?YEn GgF5mD@)w2&MD4 )يUJk),Bi.C*ÅފDl?+""A7--Fat_n"!&8rr | @E+l)1j}ˣ0c8|3}g >No193-J۴T f#`,.L=0fJHt]^et)=K O]ȵvM9x]!$Әd&ͥx$uF F~ʭvivVf~*m= "\n:UqLZv~qCݒ08 #;]to'99?.MxCQp pUP`\p2qݔ-r1w'y5,QYEb_DR6m6qǧh7ߡK? #t Ջx9tֈ4TPgy2b>mz`ݽgz\ݴ]E g 5_g-ZjVFuy5pPTh 7 1jke G@ąV_SgF.+W >I"<ӊ{!_g\*>Oeii b>=qJ] p?opM49/m!unlgKX|h?ᔹ2 $1R\2ti9EYҸf̡') CZUR: ]KKopw@uY[b^tLu` cP$))?9[8wg?uDOOddsȸRgD$Z:V\ouxh([ve{?:ZZBW?HHO;ZxOCl<,kDE$KyfZH:pErS6} ]}k~j3VdXC7vWƳ |IJ28h3Kzjxʪ^XyҚ&4&%Yj2x)}9[9UX FY BvM^O 5ǹ HlDRzCgݏ7ħ5"4׀R!g~J >+fvTo0NQSG>c^u:J8Ķw;[. *ہho ZVȻW[oC藅O,Gڍi[;t<洌>2XcllL9˕<͑܅ۨ=ck1S:yO [[*tM ;u쏿DФw0?@ߘQEGoQ[smմK?*ԋ7=^򲊄8 %F`h&HJy9.u(=5dM֬DF9-e 24źcxg !R gKJ wn"i1:酗n^ш|ފ[ZXWuz>P=nW_ -dBov4ZrZY-aOR|=ݖlRD ਄9:pECMgac+N gGXzE3 ~o;_:KWO#ۖXɄ4h3.+n}Щ^=)Ԝ*TSě+W{^f$!>JYUø}5rAz5F`s'ފN(S1*t;c3B/d沙P9LRe'T-hk% rμJeg!B!VS! KS9t[狭3s }SsjI )\^i.z\RKz z\wcn励Gږ>N⮃2u3B8b|3i*hmop})`O)%:/05aR x ܒvphKW}_Ue{LoR0=aƉ̎*&y! UN5N&-zGC<6E;;"uvt(|SX+OP@s1m_;kr7I(D[U|!XR=m6|\o.0YTϬnT5*ouu8h#+487K{9]26m6E0 톙煠ᒈhoNzxmjnc+T~VmDO OØRS 7mP{H F!!$V24оB`%ՋiqM",_T6>_V*&0̳FxQ#A^-?~1榱A#-<'qG{a 86&qC.4H$Ǝmܸ/2O)W"_G{g8912CQOEYOHzˇevjHp#yfL9B}])_3E"7wԖ)g)H,} rJx_n"k`#;xn;NA/Ȍve{57o@>nʱf#@L]o蚟 Ra%ȃƚP)``dR#}04ƈ'W2(qh endstream endobj 2569 0 obj << /Length1 1642 /Length2 3593 /Length3 0 /Length 4407 /Filter /FlateDecode >> stream xڭTy<}׏Pe$[\ʚm-ʾf& Scf J7 )-kB(BB;|z;s\G\\ sqX`vs ܌p GB`*&  DKD !O&]\I\DTTo˾ DF.X@ bpx7KRǁ \A ck=#@X ڄ HKEg<Ho(AR#hj nh" %QX$_;(OQ=ܨG$4Phj &0szpH0* %!X"@H@&1257 O@ADc]@ . T*'/#x wW h8K@9$jn4"?/zXg iGy b,j@$##*6TТyFpC>"-oQGs$J.V{;&y%>/Rrky ?IL;t6r0S3m2k">=S|W,EO8*YKNO k|Pfvh“cJ{Ϧ"HdGr1r~Si7l:kk{%yU:5+6m6r$Git#Ԟ2cŭco;TzL LQ 4)8}ȍَ¯ YSn[K]qʧo򂗮w\żc\8XV\`W NԞ%Wl: +maBae$WV[9,u?,qw} }?_x1]6Hq/9TVMrるax2֗U3G䭼2Q1m"FB٬"5Rd:;G|AH{C5 o&qAE;LJxϑL3;7y#̂o8+Hm[nbdLP*{fӌՇ`p/jJ>QZWch+X>6>TnE:w 4zβ!-qƐ/Ǩ&덚kRn=W,fia[H&| qoƏ2|i1y<"ӄ߱C|;VQ,?އt!#`<=K-dXO•m ;=3/J?@7}g˄)UL,AP֧BK;SgbOC9Uk൲GX&c2?M&eć7J~ cԃG?p(:Y޸dYi=D!:eRƷ9Z6A}sk-ͅB/gY` jpBVr"@YXnt`3bv-)qgfڑ&VфgD΍Hy7 TN8|{yz/G7YΗ#vC_ jLT|be13} ܹ,.3PXq8ӑrn֚\)uF *VJ3e""I)aJo>+P̣CxwWI.v5CGUbfӂޙ߳lp[İ"46Bz9 ywܰ;VG8 sEEߓ27N!_pHkC&T|^@ḻQے[Ȇ6$LhNd` 7y" dTV:kRKկ+Oo/"w&URzJd1ך"hO7.f 6YV&Mxnm4?Zɐ+ J]|њsQc} \IZg{Ėd{*39ݶJO=myDdP +Ȟt5 yUS}pM೧ct-gz va-M7ݯe^xر+r!ԚhݍlE@aDVJUP!,!ᲞEZ歵%z ΰl[4Wjqw5)y5Dr;:8M߽}dx*M DR$!(L"OaCbdR IŘ */fɲh=qGeaQ]>Vx'Eh 9:\g#joH z)ZUG|lVF$Mo`S5q4xwlF03)Њ9enyʳiJFm/U'/(fz8)Y3`9Dnj?6Khxv2"H/%M7⃟2[/ܿ:u-妑nq%FAj;jgﺤQ]bO,m&WuEFQ|r7(reW#)εοc?k9 endstream endobj 2571 0 obj << /Length1 1630 /Length2 19616 /Length3 0 /Length 20468 /Filter /FlateDecode >> stream xڬct&;[+fŶm''+m۶*mbv*w>}zOxks,rbEB&vF@q;[Ly #'e;y;.@3_=9#V Dff9@@AMKK\Fadaf :T@5 %%/WHmE#k c1 H 0sX[ٚXӚ_,!'!hl7 n D:X89X8 m`aklbOv*__0E;'g'cG {g߬N;ӿ&v./_VgC ['3\F@-UV@p:Xg:'_v5X8;Mᘘ4v]501[ob6WDP-`4cs@2 B -bm-ohw}c-94ֆC kS߱ٚJ 'q w9W5:Z[r312?$5_3iȨHko.G7rv&SFX𝙓[3&忀S3tvp7#ӿt ?lhkw_uvtí-[ge8aLc ٨Z\_cUi^B4tnq(Ms4ևiMٛ .!/D٢= dqu(Ψ~7WE0sBOZNl8! "rh|td6778͕éj)U%Iϲ S.S ٗQuX(|,sq"?фȬ^}ײ*e?wU*WqSԲD:i1':bvh:#'rz,k=(:=NgBj #|c"x,Ci.ߔYcyqR0+-G қcQjdYpP$lRZ{t".̞ 4ɔx4hI\h_||[4nju[!ɾCxaܧpP+H@ u;\*=`h}&] Ԏa R,huO}zZXrf~}kR|~^5A7GK_W (֯ YWxb#1TP4infir ubI :=qk`lVJKTTZ@9C]eSc6]TͬSx!&qL0 JĂy 6aO72WFff=7).b g2T$axʂW ؓWOGg~[v JS@W"Rm? ~ɶ<ٔt%wip2$AW%lFU+mN{`:|{ġ?W4]qJ*o^("~  ߹h"~S%LDʫljbj_*b,jk=K \7ObUE}]mBpyR)WÏVw8 (~4'J"P0Z1v鸗)F#n0b.sae$`O]+i%@}g4q4z5}JZ&@:WovYk`۟75zxbXE?4f4ӞY. pUX\lsWeZ>h@S^YuntLkTH$q,g+yPj}g&m@fﮐ?+w+b(ə JÃJ dv'<~PE]X;}#2Q8j`)n%ELv|84<5LC5 /|Ik4r'6|7N'U O2O-lp\f$n.cl/HL$zj6 ȴT5X=ꪶL0d+(T"0Bwڜ9RsKlAujN[#RoCw3i4fQGmn Y<-^Bb=nۚY71u٫2%8DaiB CGu6Ky27C)|Y俉3-wp0Y4r0 ͭ + tЧgQviV_hl w\Lw_L:"ܸ7x٥AKV V͎"5"X%LX{!> zGҀOD(n\ \ SvmhfCut9b矙d>i}kuLp5e:Iibʻ圙<~K+7;kYr1gF҈Gcwk"[VbJFUI`G됡o/@%zěˎau 'yP%RU==ۅ"G|+/<\bsrm(z !B7ߑ|&bA4c˜l7PI@1K=Hd]a.=$e5?#N H@g37/kD<ݰI%±}';o?`n~/祉P~:X5"gF{- !LU(MbSwO.'D3;^}a;ý}a5:zW((wCO}o-*Xm/wt %߅e@:r 9g-j cÑa/`vx0Hv>؃_Jn_Ѿĵle b.Yjekl7dҽ iVǧ2iykq7 EUT9':hy =WVOwtF 3Lm4+/C:~"'Ft˙j/hB&"p‰IqǥTaÜnDЦ#3Vd.^+<5џK֘2xIL7ÔrL"Yii[~:EgL3:B%@6H)}qaXd6ܬlG19Lju; )5q@X@,.\*+Eא~wLgA,~ kCJ-s$ L"&gz7+z0MeD.Wx(`faEh1d6f&x60љ 9m S)g\ 3 5+(Tƞf7#?ZM~ٹ5)w\qGa~P"t|emM7:{2 ;W&O-njDF@Qޓ|<]`#ҨVocX媑 $+vum\Hy쀋yiHG }x FOYkB@Wwwu/Λ`\olL]U!9WYX̪ -űxoȠjZw 5h?,]bR^,ڄwTgk5+V\0iCBBhjLLO]?UV_IL=<}g]ewF8Y me9^ Ru+'ς>26F9&&._fW3+9;W\(mu&V*/!5|!cMCcps|a{S$XPeozu?kO~W?yr< k%"є+k9J iƣxefI&Գ"`T-I#~( n"z3< @}٩[YPoibYMwՕM7) fG?f&Tv{:6A[#Fq_r,MOp ,Xc+@~>nX(fzovـ6F$hhFXOV֋V د0yC@[G 24h <,Έ첝_%דN2+GCL!y|ʣQ< ~_W.@["[@b_t!FyƧ)cMۦuፅ zNl!BCjQczkdrdtq^; p޹SM,*B}f ߳Ppb(خ2f=ʰcqZ3e ,MeWmCfiĂn=%U_gn-{)1eSd6Y5YshQZG&xaoBuvN. ۧhjS_CĒH#' ňco탿^P,<Є삊iX~,Q BrnN6vWֲmzl)*nKzC}I~ VHesҵԀ1veu)ҥFO6tj:m~^N gKfO5]|L`;{SuVB⏅ęnћGFxBm_TGc1 * 6|p 6|$/H6-]ʌmq`}pI޸ζBb*h%FH3jG w5!׃5E.o <`A'̚ , Ѣkb~q\3,^gGyeK Vش|wRqoFUM(͛[f,H"Rk9DfsT7UG8rW[zF3gVnSaݯ@ᑹ;5³]B\IbW!LE0Vc?ϠCA5Э)c*2ӸGa@6̟BPyU\AsyZN:~nGFWuyOKf M[lKyD$"TFr9M4AH_sXa;~f=QZ.!bl+&*Yk 'l0B Ch%%>TsN};[v5 쯆&G*|97τg~70O8?l[ x{}&Lat U\1Q9|9dYTdĦ70؏Dۮ5%48򓂶L"/|̼qS{O3WL6fM`xo6l[3r4I6]qx xkPDm P8g~vȧ5RZ30;,_@/5u9EV b兘aG| .7'u:mG'Xu B+qJw~nbgv+ǝ afw $iL=q,2K $ T窟 aVb)JɃ_*PYÔ BTU]퇁<}I³w +s+-KG iFI[ l@C %m&Oi JtUaH|Zj% (Ji 9i҂aX}PٴKhĝ !rƀTi)xc,||YWsl!ץ?C1"Me+Y_:n{rIIȠڪ zETx1P/MY+[jƳFʶ$كc?`+i1C67iL"ᚚCp:&4ݩWW%<1D4Qiۍuq#-'7WO^ }EFw NN+K87:^{g yJf0~alӶf,~D0,2U,XS n ,A*)5cGq8֬%cX-ubELďNmB;^w֏ {{OߗW:t@CɇD<⢒F0:ϊ* AM^ZJ|Bi#m@5NVtīj-c`W˒EĮa+)%o/T6ٕ{nvqqp}oYIoDnGYٝ?-̣ {~n2a첱^O6>FO'a1Z) E$})O$H6ve+یOLvS קP`ɍȲ4Yc2Fy Q|ťiW}t״|7'v"[/U^jjTo[T;9WY4jcK.Ōs76 8䏾P!͓ Y7#OK* gvx!t\nT~#!Y]Y܉חt>7@fc ⦳L<}$Q,tbpry~LD C.ޝB8)uݓq/TTݮ.0Y"s1YVHm:~ CO†}28:}N#ԪU^Z ٺu͍W˙zTg 7f1W™7/zw%=(ÇTlw'~r@OIDBȡ2et,0RzrQs v`UNjӖ[G穕a)CqDXaL}r̶4)nǗIFeV].[ˉf*>ާzb7Ky jI}ނXW_[ ѳ!'x6I@;^A#SIayx#N(1sHM!۷oZt]U ޛ6sY?\(ɪЦn ͵h;Olķ4I` ]M8N`^X.hkޖ 8QrnY%4M3rz hUy?xou^sX+ka-+].uX f%F,f~w"6YTW]ٍhqK8 s+1ׁQ@QЄyq* Z$픛֪:pq6^yx:[(urNXjl`Aq-F w@iT qQo%6p6pw3c4F@w/,Vp sEdc[^V?(/M65bL&kPp~Bҙ-2|U ЄP[EXb./.xf:cJl{vl2N y!C.ݴ2GApY;#g_Q w՜,&,pԷ0722-,,ñ,mP`Vn1ȧ9 kC(~*B6P:]z.~z񭄈uh)fwKװ*$\5}sv$:5D`rr׺?e%TrbDև}Ҏ6gqj&P|dtplXxz#BAL^,~q[{&ܻ;rwT׍Y2FP%& їQ&xR_zVӈgp Bf b/X#߼a+^eq:Mx vҬC]eЬa摧HՖyg=ه+.m`i)X˙'nzċNUؠ[K,ݳ[^&kS6" kLr m8C,Vn^%:csm_L/}S0aYӡݗIUu:5c= m#_LZ{_@ hPwk8Ի.&p$mktlk5w,dQ$e~{1*x&kHt]9:[!v7)I03]@H1K-C[>m_-\¡ƎYsc.9+ӣ@Vh1A&-pz`C+eI\ .Q!Q6;ߍ>4"K 1sU!LF_YbU: L#%\\]g/Ȏ`J 5H["/ڃbhHGoA~gů;[šP'VK)6F:=AP~텑!仸M^\kq߿͂=kf$iDI Lާp\ Rc' ?:n,<7?0dzzB%j;KX.'1VB92변b@.2ۑ0-EqKƨŤ9!;r:nܴͬ_[*"~-#D1oԆwWHwn=] jEB5749x${7-$-N2`Gt Vdb2ᅪ^NO555k9QKը*7՘wQSi!G.󐘶/$h$F~`cRl^Rj 3q@_ їo7ZD\UfEq{G/ ХMh=vy Id\y17sJ TCOES=!z'8]l\nj`}~ %?'`| 7G |nǺPяjWmtrwer0tԗXD [-ha8°ŒX^3s[ɠDe~̀[IU0jr/`ל a?%:Aݧ6W:߳τ`eԭ&4hHb@4׬ B*5 _$M'x&3Hzwr%\RNmnoÚB`]~c)X:v"O &y[ +C6c?(1gfNS@5KݿUXeonl1ƟbV ;ٚoBKX̣єB9HaMESOxSFH*Mum P'p}_SZh(0F;jm :84CF۾ Y:{~󍃾cZ鵝Jͥz_n^<#t3L6亱IRRڮVq?ӧZp@gՕK6~఑%)DB!;Dٸ7_?k|o,?H?)Ca&: g̑ blyVp5PB Uܫ{x3mB-Ԋ,.xahK'=W yY!6/ U4N(ucj-(е"Q)1D]>8C3Ӈ@AnlqAgې5.r$raݺmH5#p5=t%°2cjKSHᄈ4>[iM^K@rz!a6a }T3]C=|W nk ,Rjk; W {SžmdBYN5ח))4Ew|gR u&aI?.eOCd` XE)w_s-J%-7,+g?%mZm곷AŴ&h\$O_o/Lu;N(Ý-|xoJIAϰl#>^s-wGe d"߃$5;;ZЖw"T)ɻQh,e{h`uTз‰)/hm3Z/z+mW='Ms9 xkU~V˴Iyq! x VdM&4(b}ӆ-h(S$m-eAk5 D7s9 2W׶FWg=#>I^9*eѿд>]iq  rc3kAlʣS*{oUEPutJjl|&JFH '@_U9[]ZTX ^{d\5;cw9-jO|rԶX!>L]njNS_;N]J\'hǹí6C!"FyG ,L@KcS $;}pIN4gGLU% 1NKSQ{g!3R_gY$_ĺ;:>Pqt%2Sca$vhZ~GC\?L(sÖH\~3J =FȠ9잓mnNtDqAq(lk;ڎ5L>KmHW{w@sfY^h.%/`Y&ur$-!K$^;]|,x qEmz"pc3V~v9}g໽O *;]1hYW:R>‹CJU3&B&YB]bS' ۊ69~.}ϕb@R0(e6|)G&)Vc6'JqlZܮظemIU*NGyn*Zqd| >i4G>b4M[C[ V盋9'P)2`5)y *xmTD"gKdUM?QA :RH{y/r #AN W(؊NSL͙שxX+ACv)mgVv?umnX!x_cL>sLt!x݁eMg۬<Rq9B]A:԰P*vh[z`$zH2*kgkHƟiXSo~Og)Da\S!ҏ TX9[XxRP{"ve0%m/]NG[<9](B f.򿜭;U[ȸ6di1>`SDYèDߩ6|61+P֢[ZӼʻn(^d6zT2ВOlY0+ʩ!Ao* `W ruo5'WŴA_qZ@.e_ vFcYM*N6~`}1!>ܐRr GxJR;i'Cɱgc$nX#mID@qe-k\K42ؼ{pRύr4"x'?^C:\JW aOV1JIexwS@I5}78Qhyek=yoA]&P$@x/ M赿-&!; }oi~>bxDG.5/4Ē֠uk^,p@{?`P8n4dJN1R(o~eoknk]Ԍ/Wf)~ y`JLm`w1AJRS)žFֹlwң>]t7St"*ĎD =eDzt"vZlj "g<ɴ+% ԗǰDb)/QXkM: _ǘ9a"&d!nQ.jԾ-׫f?>3烳#%pLp*sx5kENݬh(7%L>)odLWҕ_b"r: E~LKmT|܌Ÿau_[lVraj1 dvg5/ýBZ{u Xc]LܟX~Id\O@jBBn@%}ٛ|2e/)j=r׮OSu2sD\'zS:M /a̝V&+7hz/k鳠7 ݰsHQg=+t"g,(=?|]F[s: ޅlcZ9*S;F .>f3XrdPf?+-4&`\_A X*ω=~dwJ x0"cQVd?T6Nɬ[\ DquE"p5&|R׬{o=quwQ^|ϭGA5eG֫9?cn1Q)5?`xKǵ소x]ss˹ λj0چ>]Dsy|sǝ;!v=y3iw`߰*緺21tEr EHsq^l0XL>[˷ύ49"z{Y PՔ:\>\Ri"oVftIr}dQ1xMZ73&])N\Ɲ)Ch7}bIm#ĸm{]pibo6 \̫c 0j 39nK@RF9GaEpw.`˃cdIk\G>R2CS˹wUc$)H/\ECɑJ]Wش[aAuo6/ӒL~V'gtSJ+„`mL |y̿\g:3~ dko|<~6;Sq@Xί|&{6w:tW{IU/K*2Lr]:7I|eT"sP7.vy2rZ,CQTqGd.;{׀)%*!3m^]Ur( o *<%ɢCEoFBM}a(O|^Jox( ,NM֢ī|զ}Q uMT6ܱ ӨSƬ[3w8cPGq'bSZ+GQѲkXQ>7irC Md7ׯ=(oޝ;<|ցuc}BdnK\+muK׸f3Bx#PFKn k=a|M&k+!Z@機Wq%·$= m6$.[פ?i;W~O*v\s PIJn00FsH-QtWNF'#tkO)\`T``ғa%<&}>Xƀ0ȟ gyG%ol-hЩ/8vH~QxN\ ~FCUEO,R *o8?˻Jje&JkOMe{ $D$DZ,[PP5cE6'[fO8 ULt%ҨW8‰-|"\X~pt]͜ ):v{擼=Q[z˃ v`>oCN>s V濛jp5 ~=gdɆ3%3}GaQA|%65>8vD3>Ud?,~̓-$ m)@mj*$8_Em&R̹`#<%+7O0!tjT[s0D]1(t4kB+` N++tg%aݬʔKyGEK֘s [}`;ú4%Sv{?4,LQ p/KuW+Z;'HŠ}l/H t"IqU-GYL3A/2m}G+rBFr[@\?ծE}ο;s"r>d Io,Q^\EE{7w8^ar͝Rl<5-~{)*b{3C $ %~Sф" 2J1z&x&ݲ= {x9 D?ZVPDm+ʋq8jX {dNar&e{tf{\㣛mڭkcK9 SMzxV 4hG%ǾOΡE2*kd-L7?a䄈τyi5raey?u֡1F"C0D1$NmBϲRp PgNUF4d I+S帏辊;g'Sl.CD6[ar&̏[NS^Ԋ:'}/UsNof0^exln`6I=UfPt[l -JGh}pv,S JYIgZ @pd|:?0OW-97jx{ L =Lƫ1;Esݮܮ^nJՕ5}CV y*k:+ |B㖿W%oo% r{u9a¸1?UNb+fTЁsDYc[1A,r+J*TrÊؠP3p2*6r3$aw!4a~ly,; YӷLEd*2'u|tq*#/ouy1u?L2uܮL~xeo~HwÂt0wX%g) endstream endobj 2573 0 obj << /Length1 1644 /Length2 12354 /Length3 0 /Length 13210 /Filter /FlateDecode >> stream xڭyeTܲ%\ q.h6wNpHyͺ3g>O]Lb@{3 5svRUSdRY:^\)lo'i t@@$`CH;CVP=Y^м~q;؂젯ώ jXm@ Or2:e- 1:ـ`s`a0%089n 7s_؂^NKP{7W? 9@_oؾbdNP's x*)$oE?q]vQ6}1"1o>`׿+ -bv01s ;I@@U0 `ajڷZv@lzXY Ӵ+׿ ߫xXt%ˤa:llZy̢p5Z;֗\);Jb!eì= [!szSi])FeLv|{@6%]9˽e'#Lm= vc_G(y);-8'q^3Eh0%ej7Kz!AA{GVi*,8횤l#6o b+ 2Ji c 6NncE*w_O57:ՅQkaTK;abJz_! iл e*pRf0ΠfMY+[u7NGӦl?o$:)s3F *hz#| Гq3ؾ@ʎA$D- V7!w[`hlp2+7/d1\f3_ stLy~a:/30Q(t SU#'KaMGQ -W6u:|Z}: }XvoT}~EU%ރ;oUlɷ@?u<ښƏCA1m#V-2…}fT;vZ"N2e(d4T"Tmdaw;͟dنc#XP@oMp E.M8gpd>b&U w(ʨtV1x~AңMK.vבsN>K0%.KrOrǟ֣,ecNså-rR Wlf<ܧx7` .ٕNvO ڂZuf"r(-MkL W [=:rF"[s! YFe lܐCk1[ :X\vjw">dj=ĜSL&;ŕZdk#UH-|uc|tY,mԓT s<H7 kG8~ƫ7 5cʔn)8DA97MQ ;}/_)f;k?IfP6*Ho~;^֛@A3MBdOa1Ay7nROlVlr:E%)0-/+=ȵDb-7 u,/k(0dV5{ sc[F&۾Ѫ]!e5䫦YZ1SIevRJ[CXTQ,n]#\cTVWo"-E= LBIgZsS4dx-Y˚OU,W ,iȪ)-?8#@T%7QmNb_&.2^k?WK14µ_k|4o#~Z5o:5\5g!P *fL$Ic~V~EfQ;7}6sbW~ ٦m;F'e4[pxc(r^Ǔ^p+Z1 QLRR0XjUDs<:{s2D{Ľ#~njg`x Yzo(/[P؂)lZ.HPdUBWY睮f :!抶""R,h65l@)5Dw$7X{cuv^#hf8ܪc[o?r|TۤL೧KR͛*3rQ8I:N؎VUWV` 06g -KÆr ZzOtD+*}T'oi=d/P$Sh2OJ.Ka>O#c< ;>PVE&Hg٘ L|pC|òԃ)]7Xiݔڨm|74X E: Ft;#'f w,xk/6ox[vkYQ ' / O0 #oXq04nĴ<>JwԪnLDju~LPHQ9If( z<lth.l+CDI|P#I0/7/ 1^Ngy߁:{v#AL15$l30k,n-j4"G{5VǶXZѰ_s e\v s{|AN4'+qUZͩO˫_m|:k1fVSEř/o7MtQg v2h LoY.ž=#$-KEmV"um_"bvto &wGqwfiC,#~OPzE{7؍nԋy@>\ibrW|'m7>a/LTN dl{hm6]ˣ.uK3+"//ygdW]S:ߡ@c P4inNXNBl w\z\qESBҤ7}m)zAIDUe %Q0s:wg-I|'>FtM 7u_ǽFf#= uhҹVq& e|TkO5RU֬MXgu7/I'u_Xذo>Nxɪy+:L]Ork/WfWC.1S8YmTUٶ>辙(-)Ӈr D OͲq 2Lӏ3_B†G S8RMFݞc dn]?٘o#mQNϨ#>G$ [-c,&h*/r*ŵcupQ 5(%)*Cr`iH,vm}<TAIƤ/TfˆuTI`<)Q=_C/zXҺ=4g奃C&YRR[D6ɖ7!ڸ$t5:?#ʿ*lQ' ˑ'n9ysqrk U5.&eūg38( kOi S-͕RCš,:GM$"f}v``u&͕ko:ZhGi._av-4 VK+>UdDX͛*E2+3L:= m4Q &QˡdzKO4=BFL 1BEmidT҈iVY[(' b}h&0QҋD1vCM?$("pGG^"ikO.ۇL+fl~MLF?s1xk=1V45IowVDe !T/4 p n0K4#dpw/)u! ִ &<(0Qe&=RA褗3 צ3R=5:n]}\=u.Azծ2-"q|{Dzݖq # CzeTI!^I (y2;?_"an70X7J>J\VvU:nCRM2B]j*5J0\IDE>ԍ.Oj jstp$b(d nФpBh-LE8}jޯU4ܝQQ6T rزICaU>B8S)Fch*cT;|U3YBζ̂hRPRʒ%$ӊQ%B=W7Ӂz :Ѷh~Q]& G{s'WE.x3jmy %ϳ17Lⳟ99!ta&p|ζW[L3]:pOmu@Ƈ}U٢آ/, Qm7oGٻ-=x5i[u5}TEve< dԲυ:S˓D]GQ`WCCGR`e^[ϤV`קz"96m; qٖ[}D'OTA1MĎEŎ,h''C}5>)&~a&;mN e-]ĢPh!e/NYS]rK``zhB6Y2Tw뢉~v`S퓕+BWxĻ+ >4P%Nieh5˦ l~+I _Ymp`ݭ&ژ&b;Yh hPVCNMMĆI%s.Mޝ^95|d\JαQtzytbjVdU`ϹnUGwWCP.*~ʍWU&l9qۧrQ_)IsHd$<_4n~_\Q´0z=*&:U6;R Ypུ?i2.)j2C8uۡz]pt!V<8vUNz.=$pͧ 7=+ߤFH8PAmH$@QXoW~_^oBVR|EXrlksdr5q9kY/!*vdPK B,~ z%+bftWU(1x"u K!ٚ2YPD^k\/y&XJO ;F%xS$"ybZ{˲.gp r,OC_JP3Tyo|Zs2GIגg(Pm鴳(4V@/r `s[I, ?$tZpΚS̞_z:'FV,7m{rsur9\VbJRe/4њN),OcƼxiGsIs Z'dԱ텇0 ~OxBbѤI>ޤu Zk7ڝlXfAT"&h"/;~w)]>I>oϵr >kVRIbcPw:QhƬ5C}sw6Q;NGE[t[qj G@Iڟ0r8G̨0vb~d̶$W9/LFb00 2;_tp?燐FޭUo.ɠ?5|OL98=>_jG6 r|CDqշRcȉtLTYO`  )OH‚|^ݓ ,!ݡN4(Ŭ0`e<^wr 'ÐGGzfUF[dt<o)ϯ?L-Ԝ%NK 6gb]R`/N/*nɗQ7XMti\%^ά&VdYzf24qCjg@ {ɄΘ\2a|J;Ioٱ-)-Derr"/u+[s9FoE=q<rCẙ[ #ۻ՝F'īrB_y ' [Fl wX-ؓ 8*w]{m0B>Dqj:͜sx82jTvad7(^}*/~ni.cHtirVt;<-MdS)KZ'*fJhHzЩCɭkb+F>HbNU9!︀::YJH]u1i|;%\ H7߬\X)V4-#Wd\KycDҜ4g b{Ha]OM뤜{~'l:I_X/V8`"׈hb%&7a^.h,:A067Ercu-` QebEo Vc._M`XAq +41]򘓿ECgW՞G;c d ^dB`S(XBmTҺ^?I8\i^=0Yɧ\I,#R1,-DccaO's]Q|x![7++gJ +Y!"I^X/yO-F"KԈ;[*vUٖѴRor S}|lPMFAs; 1yw[#ӣQr9<. s c8&_/P;د+᧚^0E| 1Wr\H(]4?,E!f5B-  HP n9~ĨGƹM 3S_ڎ ;Qk'ؘf}AVPPe:z)WqΪ,3v^j}| ZP,)#A!شD aT_[8Si4Gsoe1/KU95n ,Ny Ҿ4EM[ 5'_\EQg@ Dd+rG0wtAج  ]zӍB.f_˸Fv"oK_|\^3L`E!zʻy"I:HX|ʗ$Jg3K& h@B*:Xl.ןkB$$ |vy yqb&f " ĝs ڨ'Ҿ 㴎2}?֤!Nl|~ wpc wVKt'4^HIL'䓜@#B&u]B.Ӓ-]67%{}ۍs<@z9 Bwq Ox/g}tX7.Vj# % X}1ܬ81M$oeAb}prS&iF}q Uh~:>d'8@4T0 W-v57wY-,ujN-M4HˆfxPUJjP?|}dUObKqў*p‘/|HP X(qεtˆXL2]Ԛ($E݊1\oi"2:uH0e%vQvJWC\iAt1/B;-O=3Z۟_Cgk]è=?a>OdNWrD'߭3ǨetmKn̈́ & \~,Ջ6y@ї/0'?!?:34Y7FaJD0Ղk4O oT_W Gnkcj丷VrRpJ!RO-RiYVRU  py W{ q63Tˡ+3G'C` Pg^jL̾A, )F`~4uu&o`-K^w2ѿB5v%b"ήoGzR而X ջ1`,9}wu|S8&"q:3ֻo번^29Vgg8]?spp VaB)TH>[(SYbO\?yc|ZUʤB |z}y͈R ȾXTZW]Pڼ~!jQuK~rM 8r8_lLr褔TZerz=[t!|f1qQh~pGX%2R ô;VCn {Zf`KG:WTU `tIz"wc;y蛬MaV_u4C+sdAрPͫF< Qd;JPfN,L4ktp 6ABPBfnzՌXgL1z`5%Ӳ \ItŬzo_߅0T]xT/r*|=Ҿ q>Fo LCHM<d/Ѳ{w+Ck/VBH߶11H Hz1:}91l&9u'yQlv{;Yiùa ˊӘL\5YK-򜈿kKp*vLo`pc/p5cgCgvz錝ZPy{ݻ?BMGnmރtΒ|"KRhx J^S[vN2T@om)rSw-_3ܢ/r8(-7u_D:F33&+/`ҧ%ؿu(Ѫ;ޑuLdwы2;Y/ޛy(Bu/>xS;ũ" BD9;CdodR$s~Jy)E endstream endobj 2575 0 obj << /Length1 1647 /Length2 15417 /Length3 0 /Length 16261 /Filter /FlateDecode >> stream xڭct%l&N:رmNvl6;m'vԱm'_?;gά9?^ꪪĊ*B@q{;zf&n=,2Uc#'qXۉ@S(`#;x:Y[Ԕ5ii ?Ζv?܀6@;ׁ*@ 0D%Tj o6&YK3`f`bogjOk Fg0 ttv` 0w2s{.K;W k7WAN=lb]M,\*N #r;[f=MM\i__3O.c odN*?+8͍Lmirs;'_5X8mY4qy303n_DP-` 4cw@2 "UJ-jc#odwgY66FN/_5pW)*DoT`fdeW3:X_0U Kk`73=_0ʊiHN?2iٛ?Tozf.=wsbaߤ\,=:L LL̀ϓ37gT\L4:9U_oq#=&p&!_ݾ»#zg|ڑۋ{ͿPu 97Z41y][C3בƚ^< 3Vv xh#HŚy.qkaf#FN(r쏇X25R0CT Xb'FpU8~,Y#7: ÊjB敐zBx$\bZ^TZi}`]oF}WH g񩕹PHO75jˤ\^ؘX%nXܽlE;Hqe#6vJjl)\nEXd"ɂ(^FT?N+&4Kܲ?7l:^?c$)^ GPcD>p5$i;(S qx9;@"NuAiŻ)AE؎;0xyʹ#r-ROT( _͢5~ҌJ$V{6̔˭pFK SƜV7Չz5cɑ%.r0D?vpqd$%FW:,{e0<(b]K7'n^:H<,@0hQ7 &~w FASj 99aE8'e@pJD8ČK}PLE J .;EYۊN0F(8"(6V<}Tp0V&,CW Nb9MmJm͓ MjIH"* 3C'IKg5C&ckWKifBpv`rvސPF82lnKmGͭ@ںd9LE:fhI:+}Pi 9LFdB&:=#dCێJhje5݃AǗYx xΊxylXņ;x_+jnڝۃ,>_%Wzrqhvg̼z.*潝xBܺ0dɕ=:j޻-YEf"w 3S% .C*=ۦOmڅky/S9da[^GZXEq#lڈN-ګ,mp[OS5%~5[\Q}eO{UqҒHuc}:5;n W\4 }T* ~z]Yؼˠ?&}ɮ5%vyEGW9 '.XyO"RTmUeek>/#l2,[;A..*Uڴ~$rf[V5#qM7~࠮P.b˯t@?Ŏ 63n+J_WEfl2+a&)hyK*]AԿ೩@]2D ! #y;HҵᶴOu{'= OxxwB=0Ng7aV_j_W=B5GϽ C Fq/uXY6qWΠʍ: [w9zKZ? bwz:z9m_gDB!""wۨh/lC8A9@W%e-pxDP# i/Lx-x9 zj Q bɭ)ƉfDkYJFXq> nz^w=7-g'&EZDo@(M,1Q|TiL?$/ ,DԚԝg㓥{\*Or{BY^nkJE'd+g͚7O1nv2}cИ͙!~~x_=r3^}ѹk9 sp1ekʨ>5 L[K ^ WWV.de>d叁zIםfjj5״f&cNH- h3͓r`W{1t6*iԱX,Z*}~Hr C'81ZȖԘ *˟Ч#H5pP-;7GLI[b!iH*?R}P2dCWpiF}a办6f[f5\IA*bݽ+tV\5b5}k%[ FRgȊjԢC(  VJ'9w4=ՙ݃ ?)Qp0xgSŏD&Gw.B@\ls0vIzW(rK.\M Di.cEwJNCzP|d֠gV 5t|L&F sF:|/3xuoaj/ﻊ'D MpQ}FhRtش7}DAnsNqUXHfIn[{=ۡ9h,㺦tw*cTS, Lf͙Mq3Rk0kAvu~)dYxzIֳlz|7C>Uw1逬h"}R23'G$LMx2qwf]МK%ޅ5Gò:SQ|-8xs>ʐT(y9L4Gu v#wP*HȾ{fj >iNoC)Q{*zNWrڱpTV6xӄqxڕw 89b ie\{YY唂+4~|/ S¦nuЙ9NRkG0UX<6V۫1zSgӐ#K[(HYE ņP$R[mt]:vZ9:pƱ%{^ObA\ ']_k%`qH ƠGG&8T7rL34viH ̰Y*.c&U";$M3c&b)K3VHeL>~0g@ӒϨ?h-ko$#q,  בWĺyd݊'8FEoI:-5+{<3/ē.ŐúO^0kE7zX=>jeǃ*ۀF!3,?wHLuƠΚ#,"K teRnC_BDȮ\if"f]0ey%cR77N= ]Xݓ)=OѝóIu'`sЊ?$u(R\ʾĄЈYr2 >,2w2ߛBwL_ mױa;^@ʟ_DYh}ON7S+uzf6%!7"DETu=R 붅HX,4ATڐťg)&) vBo`Iոr0x?]ЃKDϲѼ9қYmP3!_JjKL?y#u)wD^46Hj|B2WwFvVPa:*'XuqGNR`x ŗk+Qlyq9Ra9W:FgQSP{_<2O;3:?e%~V""A{:@?W|lcF^1d>8w$6݊?09LCuHSGQ9k {Mޭ7Yrr5@h;TJ^Po\R׈ZɦI%֘s1#y ^EΙ{ q *xF׊_?ZYU| 1o&oǫW_6`~TvL jn݈j0O^hgxn7BlLiik&vۋs\+U Mgm!őr5yl!;Cח#cDJ@YR, CwK՝d0>F!]`W0 ͭN*? K5//)x{9RR>"'rʱD";1!ǡTƭ:q$4,1IE',rm&YE~M!#!q3죝1nit DqC1աu`W9ѥ؅:n}8rsilYKkvHa(\+yWr>79cv"W.]uN7P ag(mW8jt(' =eYA;nϔI_<6]^Ȝ"vyS"Df|X 5ܙ5tݮrt51/B (&RҒ!O)p>q+/TW/fގ,Dj ײC  WДZ3[Zxmc˱DL(f{ZS ,{d {RX&j8xBܹ3kҗC?%~z[&y0Ooεjs vz&Y! ;DuSYIh8w& "5nuwHL N``q\.]m/bҼ`0S1_Ĵ"xbFvբR.v~ nbS۾X־[Pܮ0hӟ4V۴5nB&f\Lu9s{:DPf8&gb+akn9L kuCV+_\M8}>d#3BxQ)$[;%#U/" KL b L}D_̄G(a̝mt^զQ+-ܱG#]5wYI-W[2.[$eZ.CvkJ1!{6,Mf7:ur9Oޣ}- 8$cnDaf\3pH BͰirSy Il^j!+"iY$z5Лs+He:rab}cHcyx[dOQ_}@r+gAP ԗ./^R vC5Z<=[1|֟QQ5 $<5?sSW=tOpXG.-8 _ׄZtDmrwpqwH Dn?&8+oar\p#H,r5}Qę}0&|zzyڧĉb8 TKzzWM@@!/WS8P! D},"YԕNkMM+BLT[PO{x>I1Jrb*QM,[5芡UvVqF^TGt멳+Nħ1jυ{F Ib :Kv8\P PabWlHzt`Z۸MTycѽg$h6Bppq*|KB`.( [N 3Mϼ=\m1-h[@ ST)^lM%]̷ۺ$rR[¥ĺOw ) 3 HW(lU0 i{'Cm5 ѳ7G;zTJg1`X72`izb]5?)K$3 5XWX8=%o~YJkoyO^uőNm̻CW9\NhDets^]aȻ\ -e`vlPQR9NIoLg׾$Azֆ>ηhtڃG!A;A"V*|YϽOI'x_ e[mwRiHж)Nt0Q?TyJ)1cnWZ໾j'\$3|A<ηtqu A%r( P G)?F8DԸl9oCe cqpKk(yK//o\ڈdʍC0=Tt˛IYW5~uQU=ƽ֩]؃3iG4&F* ,6H}eGd =',5N'FSBNR þRܶRUbo6fVx "4ao82$|f@eZ,8*3BDQ*ŸvӪaW:44_gmOk>(^dIW}r[Uy\&̉Zal;]it`XJ}ݩN7zq]YMRDdhG>W }2e9*̏l 7a (m2V^RyWxqHrEJ8SCF.ݕ C /940!.̥քꙥ01 B$5gnbq~bHi\@w !PZ"$nO|l$rl"*BB^NɠĦSKpeެS.S5TґF&bnx#ϯĸVf#}=s6$)~U38h V >O5F5A2-+ !BDG)߃ %[8KǯDDede O4YHЅ:ŕ@GxTf2/f#ky8uSMD!i+g@@1tsq8[$񫄹fH WQ@3$R-Sl7Vʸ3I:lZ ))_;a{`<xT;%9u9`) b]򦊺-ضO?kۂobSbsa!`44aZ1!0p!l%yngғi4N~J"] ?HuDŽv&~-դ*QEU=9ܾ: vqO}i0L[Ps=#7 "K}(`^)IJڌ2}CF{F(E}򔴭'ٗVܒ9Q 9 /Z'94NӋg8o4!\nχǃ lF{= )ɿ?u\ƹ'_|F] +FfƼ`sm/No3[MJbeMX-ҟe?|[(&xM#Yb` S2[vg<MK/[L&(:%B%!Z pWٔV`3~́+ HJe1Llz3_갇F˾xY>RO.\u~U'Wxg=TTz3?9W!V[,~zm1OH!Lxyz_ݱQ$r'fMwD3Hdk\:ԋyI3[gE,oV#޷}ךBv-:` Кr#Լ)`3S=$k/輻 ]%CaU…dz{sd W&9e%F}zj9^uxA,קMRu!lŠ|wG3Gw>nsh)%q@toIW!ѰR*hq0"s|O+dRio/(Q3z3f1TRl٧g|aa8J9bAl,YF||sHdA9>-s!%w_X/VQ%`/(V_(ЌboUOpz;^l=eo04*{_Gs+8TcP/B6F)D/ۻP*o?Ppu&Dr3."ax/Z`;JՁ\NxO67k9F\۶XQ%}vZ(e8݅*pj/ʰJ8W'JΕ_5x ~jJ$f$M6V<-G"F%E!Miggq4Mrh=ҶIv8R)ߴYGI6YȫO?OSAyP4gI (%p*eE4aDŇE# ,El]l__"D."}QrɷxM#^ch2vHkQ/UW3_#h.U( ]$ѭ֬kpű~ J2`gٿ:p; I<52/+Fڷl 4+ڶu>,&Iw#-C!.`ޝ϶nc'TpybR=\~}hĥۣs!4sbʶq$ fQ~WBd՜k ֙6ЯgKF 0<:}҅^^{%&5d8|+S|Z" S?B@?qg-oR%lCһEw_w[P]¥\"urѪ5!#ΠϷ;/>=ԢUb=#TZ-z*2/qʂMlg}oDgB;1v BMh_n!+v!i:pLʙP c*aݳ<7Z}:mhzUۈkG}/]ж|Ofh5boeS`k{̇1)P>Qv2. iM%bsVY'k;K*?8o{0ϭDM& -MmwݣfI +# _<2!q}\SX\sw ܾdJ}z7}f)1w\o qFb5Iޕ!d8Cɉ/JFH" #cS_7s/|W̷)z |`6 OF֊_ϲo?òʈIͥ3Q}ҩsu&o#PRm3Pטٍ(xTӽ Υ%;r{αC szizE)"(IlP! fV:R"Xj(\Ih?%, PR&;s֪ [u#BNp( -m-hG}4,Z*֛ؕGϙ 黁rQ.- h'euH {6ݏAB)]5ҋ:Z:s\pѪ-4-ݞGjag O|Bd$ՖŅqW|Կ2E/+~ïUcSG# hIkA^7n:@iZ}p(=fy"8͢GH)q%>Zc>EҚ{WNv<pTm JIx q?ҡ imǪ\`#^ew/d=,[]O^7"ŨXMA!+MNe:uQmfTdVvf"z:>)SAY#4Iӌa24z1@t/,%2b!Ou$fx/7lZՓ`[]=##iϚ:3)1|ZCjXb`n/EwzƦ!"cS=P$ $$ƻeUϩlNP,b#P S)X+|2@{y& R$z*XKKJ-tG)ZJq]z.M Ho]25-v ߭I, .}vY4.t䩤}۲'o,tD@G74}9j+ʏ*)|?/Om/s)7[4cY%Fe|k7cن=x)i eDhz*Aۗ}QSJ \դ T4wli$[ h#qgOm|@ҫ3,Qr n<<.r6^xjP,78"[-ncyc3~I14kI61)*/ZG6S<zL0_Mnr,kY#=O^v'~3f,c^W/!z[c'.nbHQg12.FU{yWwd>syu9A*XJ (s^S*~3PG39Q^%7E5Otrǡ˯'c #)9m^mywU]^|R u|VH1bA p T)>^@$OΒN) IĤs)J̄U1;ڧAFDEl  Xs*f,CimyUj~=oϮQ-'ȝ肏%Ens]Hn:zcԜo/YofZ-6z|2Xu!Cޔhׇ }pu=m'\$^|/3RpRK#Ͷ({jf"\)QAv3.]ؽ8 iZH%83dkbB@F' ZwE4!Pv1 x@ĽvuFb .AJRoU㎽rtɘDkp#E4C|O0 ~yl^֖|CWzWZݧ2M:WǰBQb"+3UE06{=eB"᭽ȵ*%Zh -XT]lP%fS9?qwzN۵5@rFj:RL8V4Ddj9 ABd7@!VǴ;;0覒I­9O11o,[RG4;+& ?MIk@bAI/ԇ)-7ʢ0ۉ-C{cN'Gx@}_6=9|D=N>[PW j@V*:֯Tgzp$89Μq)Q;'?(͜89vUׄVPL'5&f+=vbF)|)&}Di'> wOVV|a(Vؗsx5z‚,_0ԠA^yĴib͉fvx 3L,W0L׈Sfs#t.w%_|[K٭5ԝNƏ!^z F kq9dyYIJ @@,10TVF 6yhEUOK| 2>ak\Q Ф~Q/FmNIde&5}'b [ݬubv_k'p]1 t 6:^]r״e,A JaQ#$kCpxfH-3<^_Kf7zUhޒU(ё= dCwA?.jO_2aƕE4_wC*}oqTZB:2Zp$ } `Ysz/L@%庘  {#/3Gӵ~0emne@YȘf+\p& JҮYbֺؐ9(S~l@Ij)$ʈ?FbDo"z&NEF_4XltɀQ7)oiXTMh s8Ex\uEGu٧~1V3:Q{1 ^qOE0;n1GLJwC^A m[uCciZ:0 gxR60D;*"Y$@3UrS;]!fXJ6jI.~HVF;"2ZAB9u8fFP%=ku#y| 8^b:#^Ɖ l[\<=x_jmΗAۭ^G` ELޘD ƊI(lZ'm~%^0<5rgnCooʯvf}F > stream xZo[* X,89NdKc{fM=r8.[v.ZƈnR2Ff>UD$*1ω }F<;;p$Ny1|8H :x\'1W7kbc@#Zo$| (q8XuhƄ><3>'=8  Uˑ`P0JjXH2 PH-Օa[8zmRVn=A"O .`9jA %hi[6R21@ 8!>!dXdKGªvv`9hL85gԫ}@/dЃ?Յ*?Iz-'E:oyt Ic2a  c*ppζ8Li,i'[?en>~p} |4>RaC|Ԇa%$\ #\6I|۾/i Ĉ->aϰV7}I_#w􌾣ﴠdHzPUj6#Ta dSΪ=S=*+f|V QyDjl:6q`@tqAt8hI,D]!i]~+ ihs5+K1s: `:+J{95A6`*:NN:yF' O%O3N.|ϽZv>WNgjE0y^>?W"q/8F\f<c~`|tߜ+bx#~pNq 1{Յ&FmQ9Y TXkڨBi,.fy~wr,-QKg&o2*sQnL6b6"͔VwًPw%tvY:͗P*Km*Dy>.ZNc hQpv*3W[oe(σ{st >?p{W^ѹ[w:',<}`arTf<׻UΞmdfhj%b7rk6BKt*UT1ܲ@Q];VV.Ebp6pB؁ꪋ5 UTߘo}g YD;MM5xn^as*DV!%9Wޅ,PܨC<,wc D w;yUBfR.a49F)}/A:eYs:k3<ɢhc611SA9.nj-X.bT (-ĉ;RT3mkqoyDETQ2&^m7qqQ7&B𖐺%;o8k KAP-7{y` endstream endobj 2579 0 obj << /Type /ObjStm /N 100 /First 906 /Length 1815 /Filter /FlateDecode >> stream xڥ͊d  :tmwƋa1=~{Ȍ( Zt(O')&WKR$tA Z4`zIj4`kvI9 ǠtjgGa S%UPג:hPlp+31(S%-%+2__%Kcw 2.,3ȚYڪ/gŃgųQR"e*,B¯ s5L\)V'&*5e}TąTfd , ,&`, eɅd0X[ &ŭf5bWyC{x6* Y`2 2+: . TfZj?/vk[cşP1LurS5\D |,J6v rnƽa0&,1.!T\<6Nf[9$[T΍\WG/kp/P>@p]` A%{kSC>X>xLXH_ƈձ}_ϧß~ۇ_vOO"]}ǿ*J_vz4m>H̯_AǙ@e~n<٥}k2-wo*6ui557MJWo7LKy}WJO^b3籱gIݏ_~zkr>B-1u>z_ߟo+bB썝gUX5bMr,&/>_i{ލXuĺ1+h@tbZ @Ty1j4@U5P  P=E@1(@dc(r @d @ <PQQHxD@2@$ yv.PFl0&=0b1ax& 5a& cLjˆMƄ0Ԅ0 3a #6agPFl0& τ&؄aL CM Ø0<0b1ax& 5a& cLjˆMƄ0Ԅ0 3a #6agPFl0& τ&؄aL CM Ø0<0b1ax& 5a& cLjˆMƄ0Ԅ0 3a #6agPFl0& τ&cob OCfr|zX F,ybYR(6 O+v|bB+F XU1b/}S=Л+XW=a<]@3)h@4bhj P@Ty1bT@Q%P ( P<E@6 +@dc0 1 x$ <$ <$ $b@R 1Wbb6|viԁc6+k^y k endstream endobj 2584 0 obj << /Type /ObjStm /N 100 /First 986 /Length 4875 /Filter /FlateDecode >> stream xڕ\M65-d = R%83Tvuϯ߈ (⒃of{]/ g md!E(/dՅe/-о}jC9Q]xdt(/D),ڒZ .@ZڐƛB 42 /&/h”`߄ai* Jj0BЀ8*k -E_:P]_h~=[Q | V.p_RQy/ E|t3|%Px[R0ꭢ/Y_?,!7yW%P! Bl"%H*>!,"`4e!1=Ch$I5,YH>5%=6!LAkylrDRК$ B{jXH fy/ <IHihj-5H^a1t Yg7ƨB+)HI !+-@kH`5A^ Z.@v'QBkS q^c>i)B_H*`>`7 Jւt蛅ւvXBk᧟>M .O~L_> /8 R!gnH_3sCѐ^5$2cHM5C3CĐ !kW inHf MdU-'fȶflɶDY%pml3mV֜l![OdU5'dzlDZ%[qul5VVl![MdU%'[eȖrlVD\%[rel9-Vl![LdU'[fblεp}DV[=,W:֗Ҿ? tçϧx \$GP.8vou@m>?@??ͱm[wXj@g.b_Z>4c_/W7׾O]\[oұU&)8|;m9qYwiW]^Y0߳Uᱫ?k -ǖXeCsv†bԷNr.U֗wNDJ"@E/U_ۮyWZQkO}sˤeZ#W9ȁժy]7ijuv/uv`AɺNuayNp_qϮME,K!,YST1YXvu7_18 QRxq_?=i1r]ŧԨa}9SK=gǏĵyNwߐ;#99 yQ/W%~i`WﻺLe ׻}}{쌐"ח*4/С l߬_i>a~^ U]{-nѕ\uo D4[OsykWH}NTRFQr^DPj|DcwdϾkߞ'iIŒ+Chc˔^mۿÄr>׻1ʬ܋-#Y0,c"lCE[N ser;_iⴞIrypg!WcQFu4̪A nU[2"h ۣmlQCZ,#;(a/S\tB7k Z&LlЀU P0KT-/wiZ,ͳmf8 $5$ޗBX.ggAC~U;[ӥaY<7Ark 6TZ8ptdlP49be @bT[)$A˥BZHjx>צbppn-ZbgTeHuN ~jJP:tɅ^eK?.;T5S4:[: -HhijYьF7 mtJ]K5 kb㶀 ތɥ+1Tm.<_D}G#[1nE_D7]7-+@~l=˰nsx6ʭrtgQ5,g kk:U{ +-4y=M@[KǛ6ɻ@X4,T,,I!9b «slG}Wv ET \) `z6diEZK[K4N+.Iq=*;k5>`+\}sjԆ˪ ?Ru4GyNwqawזȗwB:La-Tx Iz-EPa,vRusqԷ:A:9LaUrAоr³zCR}jģv]M%;Qu{? AVN*Q7>ҙ^bA:VgfQtW &f.sՀ ψ/Q_ c>m~+J&njp\ȧ>q]sy>NPPݜВR}p:Hc'w"uk޻5ͮ~ZS˔giƁu&MjE9vxA@]qzd5T9zx2,\Wȝ!%uӽiM &Ig\//*_qS `^ӯv&I/]Kwq7za1btRD#x7owƇx,JȃdOҨŃmKcC+vL]iŖpT~;doL¨Zjv0nf%8JYw~%&zr+aU^Vlz{gm=ՁԊBt'idчy42.XGn\py,[ʬ:zM %>ܜ/@,lpwY-byx}HAw<aJK[Jߧ '_i_&+wy{gN%5 2qOKzxBACEwF60n_HWtxgxƔ/M.ܞl ޾JZ1 z>,adt: z Ue=_|X᲎C sW:XiztM-nTfZ̎#Jyn9k7y;+FZ%_aƺ=.qbuO`^ԴU1c;:>AJ-)[3wW/ v=F*jwӾe{wY˒1%X1CKDbfm]5`!O XZ8{N4 f^ֵT'MEP~&\OCg= Q/O %WeCu}tdsyy"6]N-( r-negyݠ'}z>},e\ReǞxxQ 6b-*FH/͞]4C;#ՋzflՑj6)V4e)px;Iϰnw|Z.jiPO"a5t=;#לQќvGlqxu)Uƭ cAlm+ς:5|ӛJQ" |"( ȁ%5om42h jYۭAPOhJ%E)fGIK$ԋy)l"~S\('iG"_"!U݄0I%J$LZtbjI\ޘ&^֖  (+̘,'ģqh3Р$a 6AP£D汩[^ _6r@hkI`{HV2`'SVfN$Z'$~8bL) x'l$GpzL²B;IR8n|$N-C!Q8*J#;YD-_Hh$'9499Ѽk O0=%>- Cz4'L#= HO;pIx7%3Njy` <i 3VҜ"j22t7V΢æ'tDRbӯ*yLaW5<l|f%y,4Hb1 ZL.Nh-`Ct25@.^uHR2:z@sL>氳%G9(&GG,'y򳻛54=9$$ /c3#mE,xL+MS_/xʤDbv,AB3{ڷpv}iKT@XQDi=Gd[$YloaiNH f9`*Ix\\jh;N4gYagjzf?nfbb?kYSLY#ss3=3e>R~6Ï m+ρj[9#xH_H1k rsȴ!b.E ?3)T?( endstream endobj 2665 0 obj << /Type /ObjStm /N 100 /First 1041 /Length 4095 /Filter /FlateDecode >> stream xڍ\ +MU!SnIֲlEֺuU!z=$!&&T{/7rx`Mq(qUQĝq`t, @ wNV3 F$ $ri4vK-mDa讐P5vlC2!JT 0dS@K{q "_҈qhn vaW)PzRl$ICQұl fA 0؈ b%V{", CwF`$Y 0{!tC)rP=&Il40^H앃 ̻{T@*rI {R]JZn(L>T()Xp#Vd.Њ3=}(q 0')9\`>̯ٙW%< $r@bi6V4hE+ǽ%-? $Uv`iA+oZшY ۇ!%-B ex4EwhE#&Њqf)Pq f´Qr^ +BsؓH Lr@` QaN9(E3;Kٽ)i5R 0x4:|(EÁXm(Ed:HIiL3 0ir hB)tP6n#٫V#O L”R+{N?j@`:U sX vSHE#9T0ڽ)i5R0w(L]a,Ws*KN?j@b: λ$m4VtЊN~VtI^)N>c C!HB,:vXl۫V#iShLWAg 荓@4NnS(ݫV#i bǼN1v6Xtb tM!$},jBc:d(4ABc:3(Ģ̍xe4ݫV#43UhLt9(ĢpQEOKjZTHL  'eЊNB+:!,,ۺW#SYPN@0Ba^ m 8NΡ~{ҽi5R040Gp4 GɒxH sgP(Q9X`N 8xCnsrݫV# +&_W(AsD˒P!m rFԴP|W]0BarV 8xKVW#5F*$&_YWHL38XAgf 8xX AݫV#sVUhAZU1|]!jB,lޫV#oǴ|9جyE<$ӏИ+400b=yXlثV#c) 9D$v!I+4Gi5RCb3$P8 9/ujFǁ:8UHuq^ki:8]Ky.گ{5Rj@+ @br:5CS$b#|9T=ZF=ډqk8Z;1tNqR8$ȖV#ۉqpN.9\cym+=Ib 0cV9-sx/W#[Zl!0k&A_9h"2JRA,٫^li%,|LÉ[] Q mq2Ȃ] d:1x'!h%oDg<5bb4`KF,#rz~=m`"| Ov#R{p#O Qh)>;ZÂ9ƠD|4f^^{#`P|ÉBC`A| eǏQNϘN\ᢸc8>|̇^ Hf$0Jd ]nr|~OF_ [ _?=ߞ?>M?w?yW?ܱǗi6jjj^~>}{wﯿ]qR+QV^s}ua?߿yχOO WWɢLu1Ӻn͆>;CX)rY)m2S}퇟>}O쳬$J$_fcU>3ʥ\ʥ.늦^][ׇwW+r+YʊGNsJꛏ>];YEV.\>|1oze㕘t% h]?6ұ|BW&2ѕ.|u߱<|ӖBn+Bn ϶߲<|~"n+" }7,_妯 /a'?||يVܶг0?fj%a+ [I؂W8^_oK\|%+ _I--'WT.> endobj 2766 0 obj << /Type /ObjStm /N 17 /First 158 /Length 552 /Filter /FlateDecode >> stream xڍn0z ]. _AHTÀcrۗ:KEnCof؂ƱsX1iyYXSԧ");rEK,B.k4f۸y+{q$:*rZ2./~pH}޸,"@DHEVf_a^8^+OmuuuXC !BQ%~P "D#~Y"', ,B B B R![f3, ``t"Kt’b```tᇗGCf#G ]gC佴RxHUY m1%!!!hEB """("(Zt߽:̹[~8(+++p9oն} l߮U`'sTdf?v@7;&/߹'7X7ސ^zuwGoOڡۛi3UaM[~>t3˕}W?t8/х_u endstream endobj 2784 0 obj << /Type /XRef /Index [0 2785] /Size 2785 /W [1 3 1] /Root 2782 0 R /Info 2783 0 R /ID [<0895C8299C6C037AF97B733766B22DE6> <0895C8299C6C037AF97B733766B22DE6>] /Length 6592 /Filter /FlateDecode >> stream x%i$Oz#9j羧z?46NWBf%$#Xij-{eikUHȖ]D[ {Kl`aͽ ,car2/loW<_DYjRT-Ϭ5%PqbglbK\kbK3'v[l|X TlBX$Z,qz\lb2bQU\r-bWJbq!vVljX#vE,IX+@l:X/F, ^%6 6[+nbbwj`J̽Q {>}P s+b;ŃpHl[pDlţpL5OV<'ĎR< ΋T< g}xΉ={x.{x.bøgZW^'9NJ7ش#pGCŻ0:d}]=/Pb|PlX>V z*}LD<)vG1ngb& }o)#e{X[!;bAXYj։پ&u>8v0.vL"f1=Z`1qIQ+߬N@ ,umチ>X"vO윢>5ٓ;xhō=V=㿁>5٭b5b;kPtd|?Sh3}0jG1#`21mv)j)Ձ>#_쀢>'vJL;ECܧ%ŏӹMQ ?&J,~2b3bSqF?9gzX\n3b";Csbbۮ$vD,nSiakb7sXeV1%B,Ѣ6[ۨ5戅ZX4Xt2a }XZE}TXXJc<m>5K担5+FCQc[#VĢͬT>.`VQc_-IP1V|; fFdsx }1X`1, &`9 $u6Fa+dh".wz)n1\ܟ2<6/MNxY5OSz2;aLn{apa8GpNi8g p.eW\pnmwa}x<'sxთ-Ֆ=)>ssPkK׹ v0Oſo.,dW~ͧCp}* !: VZ~cl ֦߉f6CЭ<#Ov؞jb ^A8Uu(vKQA\I8Z<\p T;Ũ \pn-y܅iS?T;=7{ <2~/!| Tz'^}>~\>pQZ 1M?2yvGzȭ&qЩ#gZ Hcqn$N; G !T?%.sIݰ.^|+*Vv>ou)#9=FU ~0U(Ŀ i?jvioyqg5 m@TQo!G|Џp>6?^Gs?sþTsuS)~;Qq s`̇bXKa轧F߿ O~<|n{sZX jQ$ YS틿Eڼ "Y$y7H SWQMvLrPSnDŽF`혷 R\13SDnMOׁmѾjrTeؾ TmS}~lӲl0n'ARfi9o'h;ƧDnͤF-~H!}NMŷvH!iRuԥitC|H!ApᨅC {H!}WTRuŨ4i6ܘjBG!1Ժ}ꋁ&Q )=CJ!R?먔C9gjC ?q1I'cU!SD|0Y&>1C?PtTg9? ;C{{?1h% `flcqDq18 4pΦ_p .R|wnmw!f@Oiկ {1b"5{V*jLiGq=0f=+7@^)ĹѷG}{)8=r#^oeJGRG GƔ?(c{IzDg@Уt=J(ݣtҽ02ȩ?CB 4@<ԍPcq8lc[8w =Iҟd.9F7C AtK |w%[KŴa(H)MwۛR=n1MvJ2Wz c̞yKX;q>s-!7b8l}2^qbxƷ1#T0ůnbc)(΁rO,3'y2a^J8wXeqc8iJX+RӨ`4`tO`2'qZSɿ1'-v;a?lJ+){aCp՗q8 SpΤ5qEn4U:܀pFzނ0 )<)_bKjAgsuF߸EVJtbU:1? t묇0o|Zx8C::$"w֤Z8~X哛!r"wC;:`[JzG;8v.ĬlFC4ѹYA: a7u%`[t.+0Չ9>J)}w4npXs-*Jw/~K q1(kQ3#uH҉k A8:+ZC?9o/f `d?f _qX`1Û\ `do2VTGԲbhX!\kR}8d, Mn>R3֧zffzqҊ.3y|p3,LZ<~*9 h; 7,\W\0 d̕ICgbj&"+x =G^gS|SRgo~<CZ◖5˷eE·fE~ie4-ũ>*qF.KZ|/-hF+V cpu?F+ KLn1CkC/!H/X37%-+{-έݩ_o -iP*^UZG O/Z\:rw7ZgS?1MiZb_0M_Zb-`اWrz O7ZӊU[/Rk4h~ihE吊-*t7⒊3T#3tw|?T +W#38XGQ3TԯVQq0+E"wE*V}jK?3b7y]~%]A rK*k\:8Ċ,+}D"mEJWPѷo+ZG}Z@{[ѷ=TyEՊU+Vό-4IzOG}/A=Xf{i5g:!SFgl`6ybXK 5ߧ- F)з%]V8ZXal6oe')X2>8P^v18'$"zX+?B~b]P;#Fn`%cSn]uӍ VK] vͻ`ɪKJ OwQFF$P=)*֭u0&h#AaPK.UGYw[ ܥo#_|%m]v)=~KI?Kiy]Jw)ݥt7({>M|9j K.ͻv;t ߥt760iluӽ!vw)}6\b,A_?A7|CX3q\ ag +`<b|;BXa , Xae;8NZXal 4v `7쁽p<q"0p 8 4p2\p 6܁0 ><x 9nBX܍%1 %6%-݃e`$rIȥu⒴|oI+[ҷo \dr%B3K"k%KJ.)]nM_$|9yI%K4/C@%K4/i^ҼyI%K4/i^ҼyI%K-[uwq{) |I%K—/ _$|I%K—/ _$|I%K—bb4%MkS"4jJnܦ)mf0惙AVs,sdM=Msԛ<[~M;/L`Ӫ`S| [7߃&%gYA 4/h^мyA 4/h^мyA 4/h^мyA 4/h^мyA 4/h^мyA 4/h^мyA"4c܃iji/" ,P@ ,P@ ,P@ ,P@ ,P@ ,P@ ,P@ ,P@ ,P@ ,P@ ,P@ ,P@0, l[;j@RLɭ2U&Vs+Xn,wv$X+`eLIIJlVY:`eLƲZZ+]6eeLɲ2YV&dY-T+b Kز#$]_T+jeR.Lɷ2V&ʪ+teLҕ߲ү̎LҕU1BsW&d^+yeULѼ G#Bo?f ?\'.S^OZ>O7Oߨf?J&4i|gǧiG|Nu&>Kt?>7dKҬ?[m#9M<ͺh݈ؐWN$ڍt۰۵apÞFAa}cl_!Bcl[v7vxeۈW0ln,v!ar !^p9 &Wg "AÖFqL7.CTbp#^Mas ^%և47Fby{ / 4lroī3 ύ; &Mށ 4lGFz9v}[Ͻq{%@nt͒,l>7"`ZN9@9s >'|N9s >'|N9s >'|N9s >'|N9s >'|N9s >'|N9s >'|N9s >'|N9s >'|N9s H?        q?Q[ endstream endobj startxref 357042 %%EOF GGally/tests/0000755000176200001440000000000013666472400012603 5ustar liggesusersGGally/tests/spelling.R0000644000176200001440000000046113666472400014544 0ustar liggesusers# `devtools::spell_check()` # only check spelling if on CI and spelling is available if (requireNamespace('spelling', quietly = TRUE)) { if (isTRUE(as.logical(Sys.getenv("CI")))) { spelling::spell_check_test(vignettes = TRUE, error = FALSE, skip_on_cran = TRUE) } } GGally/tests/testthat/0000755000176200001440000000000014064014052014427 5ustar liggesusersGGally/tests/testthat/test-ggmatrix_add.R0000644000176200001440000000265713666055642020214 0ustar liggesusers context("ggmatrix_add") data(tips, package = "reshape") test_that("add", { pm <- ggpairs(tips) expect_true(is.null(pm$title)) expect_true(is.null(pm$xlab)) expect_true(is.null(pm$ylab)) pm1 <- pm + labs(title = "my title", x = "x label", y = "y label") expect_equivalent(pm1$title, "my title") expect_equivalent(pm1$xlab, "x label") expect_equivalent(pm1$ylab, "y label") expect_true(is.null(pm$gg)) # first add pm2 <- pm + ggplot2::theme_bw() expect_true(! is.null(pm2$gg)) # second to nth add pm3 <- pm + ggplot2::theme_bw() expect_true(! is.null(pm3$gg)) # bad add expect_error(pm + 3, "'ggmatrix' does not know how to add") # adding scale pm4 <- pm + ggplot2::scale_fill_brewer() expect_false(identical(pm$plots[[1]], pm4$plots[[1]])) expect_false(identical(pm$plots[[2]], pm4$plots[[2]])) # change only some subplots pm5 <- add_to_ggmatrix(pm, ggplot2::coord_equal(), cols = 1) expect_false(identical(pm$plots[[1]], pm5$plots[[1]])) expect_true(identical(pm$plots[[2]], pm5$plots[[2]])) }) test_that("add_list", { pm <- ggpairs(tips, 1:2) pm1 <- pm + list( ggplot2::labs(x = "x title"), ggplot2::labs(title = "list title") ) expect_equal(pm1$xlab, "x title") expect_equal(pm1$title, "list title") }) test_that("v1_ggmatrix_theme", { pm <- ggpairs(tips, 1:2) pm1 <- pm + v1_ggmatrix_theme() expect_true(is.null(pm$gg)) expect_true(!is.null(pm1$gg)) }) GGally/tests/testthat/test-ggcoef_model.R0000644000176200001440000001061414063456663020164 0ustar liggesuserscontext("ggcoef_model") test_that("example of ggcoef_model", { skip_if_not_installed("broom.helpers") expect_print <- function(x) { expect_error(print(x), NA) } skip_if_not_installed("reshape") data(tips, package = "reshape") mod_simple <- lm(tip ~ day + time + total_bill, data = tips) expect_print(ggcoef_model(mod_simple)) expect_warning( print( ggcoef_model(mod_simple, shape_guide = FALSE, colour_guide = FALSE) ), NA ) # custom variable labels # you can use to define variable labels before computing model if (require(labelled)) { tips_labelled <- tips %>% labelled::set_variable_labels( day = "Day of the week", time = "Lunch or Dinner", total_bill = "Bill's total" ) mod_labelled <- lm(tip ~ day + time + total_bill, data = tips_labelled) expect_print(ggcoef_model(mod_labelled)) } expect_print(ggcoef_model( mod_simple, variable_labels = c( day = "Week day", time = "Time (lunch or dinner ?)", total_bill = "Total of the bill" ) )) # if labels are too long, you can use 'facet_labeller' to wrap them expect_print(ggcoef_model( mod_simple, variable_labels = c( day = "Week day", time = "Time (lunch or dinner ?)", total_bill = "Total of the bill" ), facet_labeller = label_wrap_gen(10) )) # do not display variable facets but add colour guide expect_print(ggcoef_model(mod_simple, facet_row = NULL, colour_guide = TRUE)) # a logistic regression example d_titanic <- as.data.frame(Titanic) d_titanic$Survived <- factor(d_titanic$Survived, c("No", "Yes")) mod_titanic <- glm( Survived ~ Sex * Age + Class, weights = Freq, data = d_titanic, family = binomial ) # use 'exponentiate = TRUE' to get the Odds Ratio expect_print(ggcoef_model(mod_titanic, exponentiate = TRUE)) # display intercepts expect_print(ggcoef_model(mod_titanic, exponentiate = TRUE, intercept = TRUE)) # display only a subset of terms expect_print(ggcoef_model(mod_titanic, exponentiate = TRUE, include = c("Age", "Class"))) # do not change points' shape based on significance expect_print(ggcoef_model(mod_titanic, exponentiate = TRUE, significance = NULL)) # a black and white version expect_print(ggcoef_model( mod_titanic, exponentiate = TRUE, colour = NULL, stripped_rows = FALSE )) # show dichotomous terms on one row expect_print(ggcoef_model( mod_titanic, exponentiate = TRUE, no_reference_row = broom.helpers::all_dichotomous(), categorical_terms_pattern = "{ifelse(dichotomous, paste0(level, ' / ', reference_level), level)}", show_p_values = FALSE )) # works also with with polynomial terms mod_poly <- lm( tip ~ poly(total_bill, 3) + day, data = tips, ) expect_print(ggcoef_model(mod_poly)) # or with different type of contrasts # for sum contrasts, the value of the reference term is computed emmeans_is_installed <- (system.file(package = "emmeans") != "") if (emmeans_is_installed) { mod2 <- lm( tip ~ day + time + sex, data = tips, contrasts = list(time = contr.sum, day = contr.treatment(4, base = 3)) ) expect_print(ggcoef_model(mod2)) } # Use ggcoef_compare() for comparing several models on the same plot mod1 <- lm(Fertility ~ ., data = swiss) mod2 <- step(mod1, trace = 0) mod3 <- lm(Fertility ~ Agriculture + Education * Catholic, data = swiss) models <- list("Full model" = mod1, "Simplified model" = mod2, "With interaction" = mod3) expect_print(ggcoef_compare(models)) expect_print(ggcoef_compare(models, type = "faceted")) # specific function for nnet::multinom models skip_if_not_installed("nnet") library(nnet) data(happy) mod <- multinom(happy ~ age + degree + sex, data = happy) expect_print(ggcoef_multinom(mod, exponentiate = TRUE)) expect_print(ggcoef_multinom(mod, type = "faceted")) expect_print(ggcoef_multinom( mod, type = "faceted", y.level_label = c( "pretty happy" = "pretty happy\n(ref: very happy)" ) )) }) test_that("ggcoef_model works with tieders not returning p-values", { skip_if_not_installed("broom.helpers") skip_if_not_installed("scagnostics") mod <- lm(Sepal.Width ~ Species, iris) my_tidier <- function(x, ...) { x %>% broom::tidy(...) %>% dplyr::select(-.data$p.value) } expect_error( mod %>% ggcoef_model(tidy_fun = my_tidier), NA ) }) GGally/tests/testthat/test-wrap.R0000644000176200001440000000214013666055642016516 0ustar liggesusers context("wrap") test_that("errors", { fn <- ggally_points # named params expect_error(wrap(fn, NA), "all parameters") expect_error(wrap(fn, y = TRUE, 5), "all parameters") # named params to wrapp expect_error(wrapp(fn, list(5)), "'params' must") expect_error(wrapp(fn, table(1:10, 1:10)), "'params' must") expect_error(wrapp(fn, list(A = 4, 5)), "'params' must") # if the character fn doesn't exist expect_error(wrap("does not exist", A = 5), "Function provided") expect_error(wrapp("does not exist", list(A = 5)), "Function provided") }) test_that("wrap", { (regularPlot <- ggally_points( iris, ggplot2::aes(Sepal.Length, Sepal.Width), size = 5, color = "red" )) # Wrap ggally_points to have parameter values size = 5 and color = 'red' w_ggally_points <- wrap(ggally_points, size = 5, color = "red") (wrappedPlot <- w_ggally_points( iris, ggplot2::aes(Sepal.Length, Sepal.Width) )) # Double check the aes parameters are the same for the geom_point layer expect_true(identical(regularPlot$layers[[1]]$aes_params, wrappedPlot$layers[[1]]$aes_params)) }) GGally/tests/testthat/test-ggmatrix_getput.R0000644000176200001440000000240613663637143020763 0ustar liggesusers context("ggmatrix_getput") data(tips, package = "reshape") test_that("stops", { pm <- ggpairs(tips) p <- ggally_blankDiag() expect_error(pm["total_bill", 1], "'i' may only be a single") expect_error(pm[1, "total_bill"], "'j' may only be a single") expect_error(pm["total_bill", 1] <- p, "'i' may only be a single") expect_error(pm[1, "total_bill"] <- p, "'j' may only be a single") pm <- ggduo(tips, 1:3, 1:4) expect_error(pm[0, 1], "'i' may only be in the range") expect_error(pm[1, 0], "'j' may only be in the range") expect_error(pm[5, 1], "'i' may only be in the range") expect_error(pm[1, 4], "'j' may only be in the range") for (i in 1:4) { for (j in 1:3) { expect_silent({ p <- pm[i, j] }) } } }) test_that("get", { a <- ggpairs( tips, 1:4, axisLabels = "show" ) p <- a[2, 1] expect_equal(p$labels$x, "total_bill") expect_equal(p$labels$y, "tip") # test odd input and retrieve it a[2, 1] <- 1:4 expect_error({ a[2, 1] }, "unknown plot object type") # nolint }) test_that("put", { a <- ggpairs( tips, 1:4, axisLabels = "show" ) txt <- "My Custom Plot" a[2, 1] <- ggally_text(txt) p <- a[2, 1] expect_equal(get("aes_params", envir = p$layers[[1]])$label, txt) }) GGally/tests/testthat/test-stat_weighted_mean.R0000644000176200001440000000503613761572054021404 0ustar liggesuserscontext("stat_weighted_mean") test_that("example", { expect_print <- function(x) { expect_silent(print(x)) } skip_if_not_installed("reshape") data(tips, package = "reshape") expect_print(ggplot(tips) + aes(x = day, y = total_bill) + geom_point()) expect_print(ggplot(tips) + aes(x = day, y = total_bill) + stat_weighted_mean()) expect_print(ggplot(tips) + aes(x = day, y = total_bill, group = 1) + stat_weighted_mean(geom = "line")) expect_print(ggplot(tips) + aes(x = day, y = total_bill, colour = sex, group = sex) + stat_weighted_mean(geom = "line")) expect_print(ggplot(tips) + aes(x = day, y = total_bill, fill = sex) + stat_weighted_mean(geom = "bar", position = "dodge")) # computing a proportion on the fly expect_print(ggplot(tips) + aes(x = day, y = as.integer(smoker == "Yes"), fill = sex) + stat_weighted_mean(geom = "bar", position = "dodge") + scale_y_continuous(labels = scales::percent)) # taking into account some weights d <- as.data.frame(Titanic) expect_print(ggplot(d) + aes(x = Class, y = as.integer(Survived == "Yes"), weight = Freq, fill = Sex) + geom_bar(stat = "weighted_mean", position = "dodge") + scale_y_continuous(labels = scales::percent) + labs(y = "Survived")) tips_f <- tips tips_f$day <- factor(tips$day, c("Thur", "Fri", "Sat", "Sun")) # Numeric variable expect_print(ggally_trends(tips_f, mapping = aes(x = day, y = total_bill))) expect_print(ggally_trends(tips_f, mapping = aes(x = day, y = total_bill, colour = time))) # Binary variable expect_print(ggally_trends(tips_f, mapping = aes(x = day, y = smoker))) expect_print(ggally_trends(tips_f, mapping = aes(x = day, y = smoker, colour = sex))) # Discrete variable with 3 or more categories expect_print(ggally_trends(tips_f, mapping = aes(x = smoker, y = day))) expect_print(ggally_trends(tips_f, mapping = aes(x = smoker, y = day, color = sex))) # Include zero on Y axis expect_print(ggally_trends(tips_f, mapping = aes(x = day, y = total_bill), include_zero = TRUE)) expect_print(ggally_trends(tips_f, mapping = aes(x = day, y = smoker), include_zero = TRUE)) # Change line size expect_print(ggally_trends(tips_f, mapping = aes(x = day, y = smoker, colour = sex), size = 3)) # Define weights with the appropriate aesthetic d <- as.data.frame(Titanic) expect_print(ggally_trends( d, mapping = aes(x = Class, y = Survived, weight = Freq, color = Sex), include_zero = TRUE )) }) GGally/tests/testthat/test-ggnet.R0000644000176200001440000001603414063460161016644 0ustar liggesusers context("ggnet") if ("package:igraph" %in% search()) { detach("package:igraph") } rq <- function(...) { suppressMessages(require(..., quietly = TRUE)) } rq(network) # network objects rq(sna) # placement and centrality rq(ggplot2) # grammar of graphics rq(grid) # arrows rq(scales) # sizing rq(intergraph) # test igraph conversion test_that("examples", { ### --- start: documented examples set.seed(54321) # random adjacency matrix x <- 10 ndyads <- x * (x - 1) density <- x / ndyads m <- matrix(0, nrow = x, ncol = x) dimnames(m) <- list(letters[ 1:x ], letters[ 1:x ]) m[ row(m) != col(m) ] <- runif(ndyads) < density m # random undirected network n <- network::network(m, directed = FALSE) n ggnet(n, label = TRUE, alpha = 1, color = "white", segment.color = "black") # random groups g <- sample(letters[ 1:3 ], 10, replace = TRUE) # color palette p <- c("a" = "steelblue", "b" = "forestgreen", "c" = "tomato") p <- ggnet(n, node.group = g, node.color = p, label = TRUE, color = "white") expect_equal(length(p$layers), 3) expect_true(!is.null(p$mapping$colour)) ### --- end: documented examples ### --- test deprecations # test mode = "geo" xy <- gplot.layout.circle(n) # nolint n %v% "lon" <- xy[, 1] n %v% "lat" <- xy[, 2] expect_warning(ggnet(n, mode = "geo"), "deprecated") # test names = c(x, y) expect_warning(ggnet(n, names = c("a", "b")), "deprecated") # test quantize.weights expect_warning(ggnet(n, quantize.weights = TRUE)) # test subset.threshold expect_warning(ggnet(n, subset.threshold = 2)) # test top8.nodes expect_warning(ggnet(n, top8.nodes = TRUE)) # test trim.labels expect_warning(ggnet(n, trim.labels = TRUE)) # # test subset.threshold by removing all nodes # expect_warning( # expect_error( # ggnet(n, subset.threshold = 11), # "NA/NaN/Inf" # ), # "NaNs produced" # ) # # p <- ggnet(n, mode = "geo") # expect_equal(p$data$X1, xy[, 1]) # expect_equal(p$data$X2, xy[, 2]) # test user-submitted weights ggnet(n, weight = sample(1:2, 10, replace = TRUE)) # test segment.label x <- sample(letters, network.edgecount(n)) p <- ggnet(n, segment.label = x) expect_true(mapping_string(p$layers[[2]]$mapping$x) == "midX") expect_true(mapping_string(p$layers[[2]]$mapping$y) == "midY") # test weight.cut n %v% "weights" <- 1:10 ggnet(n, weight.method = "weights", weight.cut = TRUE) ### --- test errors in set_node expect_error(ggnet(n, group = NA), "incorrect") expect_error(ggnet(n, group = 1:3), "incorrect") expect_error(ggnet(n, label = TRUE, label.size = -10:-1), "incorrect") expect_error(ggnet(n, size = "phono"), "incorrect") ggnet(n, group = "weights") ### --- test errors in set_edges expect_error(ggnet(n, segment.label = NA), "incorrect") expect_error(ggnet(n, segment.label = 1:3), "incorrect") expect_error(ggnet(n, segment.label = -11:-1), "incorrect") # unnecessary # expect_error(ggnet(n, size = "phono"), "incorrect") n %e% "weights" <- sample(1:2, network.edgecount(n), replace = TRUE) ggnet(n, segment.label = "weights") ggnet(n, segment.label = "a") ### --- test mode = c(x, y) ggnet(n, mode = matrix(1, ncol = 2, nrow = 10)) ggnet(n, mode = c("lon", "lat")) expect_error(ggnet(n, mode = c("xx", "yy")), "not found") n %v% "abc" <- "abc" expect_error(ggnet(n, mode = c("abc", "abc")), "not numeric") expect_error(ggnet(n, mode = matrix(1, ncol = 2, nrow = 9)), "coordinates length") ### --- test arrow.size expect_error(ggnet(n, arrow.size = -1), "incorrect arrow.size") expect_warning(ggnet(n, arrow.size = 1), "arrow.size ignored") ### --- test arrow.gap suppressWarnings(expect_error( ggnet(n, arrow.size = 12, arrow.gap = -1), "incorrect arrow.gap" )) suppressWarnings(expect_warning( ggnet(n, arrow.size = 12, arrow.gap = 0.1), "arrow.gap ignored" # network is undirected; arrow.gap ignored )) suppressWarnings(expect_warning( ggnet(n, arrow.size = 12, arrow.gap = 0.1), "arrow.size ignored" # network is undirected; arrow.size ignored )) m <- network::network(m, directed = TRUE) ggnet(m, arrow.size = 12, arrow.gap = 0.05) ### --- test degree centrality ggnet(n, weight = "degree") ### --- test weight.min, weight.max and weight.cut # test weight.min expect_error(ggnet(n, weight = "degree", weight.min = -1), "incorrect weight.min") expect_message(ggnet(n, weight = "degree", weight.min = 1), "weight.min removed") expect_warning(ggnet(n, weight = "degree", weight.min = 99), "removed all nodes") # test weight.max expect_error(ggnet(n, weight = "degree", weight.max = -1), "incorrect weight.max") expect_message(ggnet(n, weight = "degree", weight.max = 99), "weight.max removed") expect_warning(ggnet(n, weight = 1:10, weight.max = 0.5), "removed all nodes") expect_error(ggnet(n, weight = "abc"), "incorrect weight.method") # test weight.cut expect_error(ggnet(n, weight.cut = NA), "incorrect weight.cut") expect_error(ggnet(n, weight.cut = "a"), "incorrect weight.cut") expect_warning(ggnet(n, weight.cut = 3), "weight.cut ignored") ggnet(n, weight = "degree", weight.cut = 3) ### --- test node.group and node.color expect_warning(ggnet(n, group = 1:10, node.color = "blue"), "unequal length") ### --- test node labels and label sizes ggnet(n, label = letters[ 1:10 ], color = "white") ggnet(n, label = "abc", color = "white", label.size = 4, size = 12) expect_error(ggnet(n, label = letters[ 1:10 ], label.size = "abc"), "incorrect label.size") ### --- test node placement expect_error(ggnet(n, mode = "xyz"), "unsupported") expect_error(ggnet(n, mode = letters[1:3]), "incorrect mode") ### --- test label.trim expect_error(ggnet(n, label = TRUE, label.trim = "xyz"), "incorrect label.trim") ggnet(n, label = TRUE, color = "white", label.trim = 1) ggnet(n, label = TRUE, color = "white", label.trim = toupper) ### --- test layout.exp expect_error(ggnet(n, layout.exp = "xyz")) ggnet(n, layout.exp = 0.1) ### --- test bipartite functionality # weighted adjacency matrix bip <- data.frame( event1 = c(1, 2, 1), event2 = c(0, 0, 3), event3 = c(1, 1, 0), row.names = letters[1:3] ) # weighted bipartite network bip <- network( bip, matrix.type = "bipartite", ignore.eval = FALSE # names.eval = "weights" ) # test bipartite mode ggnet(bip, group = "mode") ### --- test network coercion expect_warning(ggnet(network(matrix(1, nrow = 2, ncol = 2), loops = TRUE)), "self-loops") expect_error(ggnet(1:2), "network object") expect_error(ggnet(network(data.frame(1:2, 3:4), hyper = TRUE)), "hyper") expect_error(ggnet(network(data.frame(1:2, 3:4), multiple = TRUE)), "multiplex graphs") ### --- test igraph functionality if (requireNamespace("igraph", quietly = TRUE)) { library(igraph) # test igraph conversion p <- ggnet(asIgraph(n)) expect_null(p$guides$colour) expect_equal(length(p$layers), 2) # test igraph degree ggnet(n, weight = "degree") expect_true(TRUE) } }) GGally/tests/testthat/data/0000755000176200001440000000000013663637143015360 5ustar liggesusersGGally/tests/testthat/data/airports.csv0000644000176200001440000010545013663637143017745 0ustar liggesusers"iata","airport","city","state","country","lat","long" "00M","Thigpen ","Bay Springs","MS","USA",31.95376472,-89.23450472 "00R","Livingston Municipal","Livingston","TX","USA",30.68586111,-95.01792778 "00V","Meadow Lake","Colorado Springs","CO","USA",38.94574889,-104.5698933 "01G","Perry-Warsaw","Perry","NY","USA",42.74134667,-78.05208056 "01J","Hilliard Airpark","Hilliard","FL","USA",30.6880125,-81.90594389 "01M","Tishomingo County","Belmont","MS","USA",34.49166667,-88.20111111 "02A","Gragg-Wade ","Clanton","AL","USA",32.85048667,-86.61145333 "02C","Capitol","Brookfield","WI","USA",43.08751,-88.17786917 "02G","Columbiana County","East Liverpool","OH","USA",40.67331278,-80.64140639 "03D","Memphis Memorial","Memphis","MO","USA",40.44725889,-92.22696056 "04M","Calhoun County","Pittsboro","MS","USA",33.93011222,-89.34285194 "04Y","Hawley Municipal","Hawley","MN","USA",46.88384889,-96.35089861 "05C","Griffith-Merrillville ","Griffith","IN","USA",41.51961917,-87.40109333 "05F","Gatesville - City/County","Gatesville","TX","USA",31.42127556,-97.79696778 "05U","Eureka","Eureka","NV","USA",39.60416667,-116.0050597 "06A","Moton Municipal","Tuskegee","AL","USA",32.46047167,-85.68003611 "06C","Schaumburg","Chicago/Schaumburg","IL","USA",41.98934083,-88.10124278 "06D","Rolla Municipal","Rolla","ND","USA",48.88434111,-99.62087694 "06M","Eupora Municipal","Eupora","MS","USA",33.53456583,-89.31256917 "06N","Randall ","Middletown","NY","USA",41.43156583,-74.39191722 "06U","Jackpot/Hayden ","Jackpot","NV","USA",41.97602222,-114.6580911 "07C","Dekalb County","Auburn","IN","USA",41.30716667,-85.06433333 "07F","Gladewater Municipal","Gladewater","TX","USA",32.52883861,-94.97174556 "07G","Fitch H Beach","Charlotte","MI","USA",42.57450861,-84.81143139 "07K","Central City Municipal","Central City","NE","USA",41.11668056,-98.05033639 "08A","Wetumpka Municipal","Wetumpka","AL","USA",32.52943944,-86.32822139 "08D","Stanley Municipal","Stanley","ND","USA",48.30079861,-102.4063514 "08K","Harvard State","Harvard","NE","USA",40.65138528,-98.07978667 "08M","Carthage-Leake County","Carthage","MS","USA",32.76124611,-89.53007139 "09A","Butler-Choctaw County","Butler","AL","USA",32.11931306,-88.1274625 "09J","Jekyll Island","Jekyll Island","GA","USA",31.07447222,-81.42777778 "09K","Sargent Municipal","Sargent","NE","USA",41.63695083,-99.34038139 "09M","Charleston Municipal","Charleston","MS","USA",33.99150222,-90.078145 "09W","South Capitol Street","Washington","DC","USA",38.86872333,-77.00747583 "0A3","Smithville Municipal","Smithville","TN","USA",35.98531194,-85.80931806 "0A8","Bibb County","Centreville","AL","USA",32.93679056,-87.08888306 "0A9","Elizabethton Municipal","Elizabethton","TN","USA",36.37094306,-82.17374111 "0AK","Pilot Station","Pilot Station","AK","USA",61.93396417,-162.8929358 "0B1","Col. Dyke ","Bethel","ME","USA",44.42506444,-70.80784778 "0B4","Hartington Municipal","Hartington","NE","USA",42.60355556,-97.25263889 "0B5","Turners Falls","Montague","MA","USA",42.59136361,-72.52275472 "0B7","Warren-Sugar Bush","Warren","VT","USA",44.11672722,-72.82705806 "0B8","Elizabeth ","Fishers Island","NY","USA",41.25130806,-72.03161139 "0C0","Dacy","Chicago/Harvard","IL","USA",42.40418556,-88.63343222 "0C4","Pender Municipal","Pender","NE","USA",42.11388722,-96.72892556 "0D1","South Haven Municipal","South Haven","MI","USA",42.35083333,-86.25613889 "0D8","Gettysburg Municipal","Gettysburg","SD","USA",44.98730556,-99.9535 "0E0","Moriarty","Moriarty","NM","USA",34.98560639,-106.0094661 "0E8","Crownpoint","Crownpoint","NM","USA",35.71765889,-108.2015961 "0F2","Bowie Municipal","Bowie","TX","USA",33.60166667,-97.77556 "0F4","Loup City Municipal","Loup City","NE","USA",41.29028694,-98.99064278 "0F7","Fountainhead Lodge Airpark","Eufaula","OK","USA",35.38898833,-95.60165111 "0F8","William R Pogue Municipal","Sand Springs","OK","USA",36.17528,-96.15181028 "0F9","Tishomingo Airpark","Tishomingo","OK","USA",34.19592833,-96.67555694 "0G0","North Buffalo Suburban","Lockport","NY","USA",43.10318389,-78.70334583 "0G3","Tecumseh Municipal","Tecumseh","NE","USA",40.39944417,-96.17139694 "0G6","Williams County","Bryan","OH","USA",41.46736111,-84.50655556 "0G7","Finger Lakes Regional","Seneca Falls","NY","USA",42.88062278,-76.78162028 "0H1","Trego Wakeeney ","Wakeeney","KS","USA",39.0044525,-99.89289917 "0I8","Cynthiana-Harrison County","Cynthiana","KY","USA",38.36674167,-84.28410056 "0J0","Abbeville Municipal","Abbeville","AL","USA",31.60016778,-85.23882222 "0J4","Florala Municipal","Florala","AL","USA",31.04247361,-86.31156111 "0J6","Headland Municipal","Headland","AL","USA",31.364895,-85.30965556 "0K7","Humboldt Municipal","Humboldt","IA","USA",42.7360825,-94.24524167 "0L5","Goldfield","Goldfield","NV","USA",37.71798833,-117.2384119 "0L7","Jean","Jean","NV","USA",35.76827222,-115.3296378 "0L9","Echo Bay","Overton","NV","USA",36.31108972,-114.4638672 "0M0","Dumas Municipal","Dumas","AR","USA",33.8845475,-91.53429111 "0M1","Scott ","Parsons","TN","USA",35.63778,-88.127995 "0M4","Benton County","Camden","TN","USA",36.01122694,-88.12328833 "0M5","Humphreys County","Waverly","TN","USA",36.11659972,-87.73815889 "0M6","Panola County","Batesville","MS","USA",34.36677444,-89.90008917 "0M8","Byerley","Lake Providence","LA","USA",32.82587917,-91.187665 "0O3","Calaveras Co-Maury Rasmussen ","San Andreas","CA","USA",38.14611639,-120.6481733 "0O4","Corning Municipal","Corning","CA","USA",39.94376806,-122.1713781 "0O5","University","Davis","CA","USA",38.53146222,-121.7864906 "0Q5","Shelter Cove","Shelter Cove","CA","USA",40.02764333,-124.0733639 "0Q6","Shingletown","Shingletown","CA","USA",40.52210111,-121.8177683 "0R0","Columbia-Marion County","Columbia","MS","USA",31.29700806,-89.81282944 "0R1","Atmore Municipal","Atmore","AL","USA",31.01621528,-87.44675972 "0R3","Abbeville Chris Crusta Memorial","Abbeville","LA","USA",29.97576083,-92.08415167 "0R4","Concordia Parish","Vidalia","LA","USA",31.56683278,-91.50011889 "0R5","David G Joyce","Winnfield","LA","USA",31.96366222,-92.66026056 "0R7","Red River","Coushatta","LA","USA",31.99071694,-93.30739306 "0S7","Dorothy Scott","Oroville","WA","USA",48.958965,-119.4119622 "0S9","Jefferson County International","Port Townsend","WA","USA",48.04981361,-122.8012792 "0V2","Harriet Alexander ","Salida","CO","USA",38.53916389,-106.0458483 "0V3","Pioneer Village ","Minden","NE","USA",40.5149125,-98.94565083 "0V4","Brookneal/Campbell County","Brookneal","VA","USA",37.14172222,-79.01638889 "0V6","Mission Sioux","Mission","SD","USA",43.30694778,-100.6281936 "0V7","Kayenta","Kayenta","AZ","USA",36.70972139,-110.2367978 "10C","Galt","Chicago/Greenwood/Wonderlake","IL","USA",42.40266472,-88.37588917 "10D","Winsted Municipal","Winsted","MN","USA",44.94996278,-94.0669175 "10G","Holmes County","Millersburg","OH","USA",40.53716667,-81.95436111 "10N","Wallkill","Wallkill","NY","USA",41.62787111,-74.13375583 "10U","Owyhee","Owyhee","NV","USA",41.95323306,-116.1876014 "11A","Clayton Municipal","Clayton","AL","USA",31.88329917,-85.48491361 "11D","Clarion Cty","Clarion","PA","USA",41.22581222,-79.44098972 "11IS","Schaumburg Heliport","Chicago/Schaumburg","IL","USA",42.04808278,-88.05257194 "11J","Early County","Blakely","GA","USA",31.39698611,-84.89525694 "11R","Brenham Municipal","Brenham","TX","USA",30.219,-96.37427778 "12C","Rochelle Municipal","Rochelle","IL","USA",41.89300139,-89.07829 "12D","Tower Municipal","Tower","MN","USA",47.81833333,-92.29166667 "12J","Brewton Municipal","Brewton","AL","USA",31.05126306,-87.06796833 "12K","Superior Municipal","Superior","NE","USA",40.04636111,-98.06011111 "12Y","Le Sueur Municipal","Le Sueur","MN","USA",44.43746472,-93.91274083 "13C","Lakeview","Lakeview","MI","USA",43.45213722,-85.26480333 "13K","Eureka Municipal","Eureka","KS","USA",37.8515825,-96.29169806 "13N","Trinca","Andover","NJ","USA",40.96676444,-74.78016556 "14J","Carl Folsom","Elba","AL","USA",31.40988861,-86.08883583 "14M","Hollandale Municipal","Hollandale","MS","USA",33.18262167,-90.83065444 "14Y","Todd Field ","Long Prairie","MN","USA",45.89857556,-94.87391 "15F","Haskell Municipal","Haskell","TX","USA",33.19155556,-99.71793056 "15J","Cook County","Adel","GA","USA",31.13780556,-83.45308333 "15M","Luka ","Luka","MS","USA",34.7723125,-88.16587444 "15Z","McCarthy 2","McCarthy","AK","USA",61.43706083,-142.9037372 "16A","Nunapitchuk","Nunapitchuk","AK","USA",60.90582833,-162.4391158 "16G","Seneca County","Tiffin","OH","USA",41.09405556,-83.2125 "16J","Dawson Municipal","Dawson","GA","USA",31.74328472,-84.419285 "16S","Myrtle Creek Municipal","Myrtle Creek","OR","USA",42.99845056,-123.3095092 "17G","Port Bucyrus-Crawford County","Bucyrus","OH","USA",40.78141667,-82.97469444 "17J","Donalsonville Municipal","Donalsonville","GA","USA",31.00694444,-84.87761111 "17K","Boise City","Boise City","OK","USA",36.77430028,-102.5104364 "17M","Magee Municipal","Magee","MS","USA",31.86127139,-89.80285361 "17N","Cross Keys","Cross Keys","NJ","USA",39.70547583,-75.03300306 "17Z","Manokotak","Manokotak","AK","USA",58.98896583,-159.0499739 "18A","Franklin County","Canon","GA","USA",34.34010472,-83.13348333 "18I","McCreary County ","Pine Knot","KY","USA",36.69591306,-84.39160389 "19A","Jackson County","Jefferson","GA","USA",34.17402472,-83.56066528 "19M","C A Moore","Lexington","MS","USA",33.12546111,-90.02555694 "19N","Camden","Berlin","NJ","USA",39.77842056,-74.94780389 "19P","Port Protection SPB","Port Protection","AK","USA",56.32880417,-133.6100844 "1A3","Martin Campbell ","Copperhill","TN","USA",35.01619111,-84.34631083 "1A5","Macon County","Franklin","NC","USA",35.222595,-83.41904389 "1A6","Middlesboro-Bell County","Middlesboro","KY","USA",36.6106375,-83.73741611 "1A7","Jackson County","Gainesboro","TN","USA",36.39728139,-85.64164278 "1A9","Autauga County","Prattville","AL","USA",32.438775,-86.51044778 "1B0","Dexter Regional","Dexter","ME","USA",45.00839444,-69.23976722 "1B1","Columbia Cty","Hudson","NY","USA",42.29130028,-73.71031944 "1B3","Fair Haven","Fair Haven","VT","USA",43.61534389,-73.27455556 "1B9","Mansfield Municipal","Mansfield","MA","USA",42.00013306,-71.19677139 "1C5","Clow","Chicago/Plainfield","IL","USA",41.69597444,-88.12923056 "1D1","Milbank Municipal","Milbank","SD","USA",45.23053806,-96.56596556 "1D2","Canton -Plymouth - Mettetal","Plymouth","MI","USA",42.35003667,-83.45826833 "1D3","Platte Municipal","Platte","SD","USA",43.40332833,-98.82952972 "1D6","Hector Municipal","Hector","MN","USA",44.73107278,-94.71471333 "1D7","Webster Municipal","Webster","SD","USA",45.29329111,-97.51369889 "1D8","Redfield Municipal","Redfield","SD","USA",44.86247611,-98.52953972 "1F0","Downtown Ardmore","Ardmore","OK","USA",34.14698917,-97.12265194 "1F1","Lake Murray State Park","Overbrook","OK","USA",34.07509694,-97.10667917 "1F4","Madill Municipal","Madill","OK","USA",34.14040194,-96.81203222 "1F9","Bridgeport Municipal","Bridgeport","TX","USA",33.17533333,-97.82838889 "1G0","Wood County","Bowling Green","OH","USA",41.391,-83.63013889 "1G3","Kent State University","Kent","OH","USA",41.15186167,-81.41658306 "1G4","Grand Canyon West","Peach Springs","AZ","USA",35.99221,-113.8166164 "1G5","Freedom ","Medina","OH","USA",41.13144444,-81.76491667 "1G6","Michael ","Cicero","NY","USA",43.18166667,-76.12777778 "1H0","Creve Coeur","St Louis","MO","USA",38.72752,-90.50830417 "1H2","Effingham County Memorial","Effingham","IL","USA",39.07045083,-88.53351972 "1H3","Linn State Tech. College","Linn","MO","USA",38.47149444,-91.81531667 "1H8","Casey Municipal","Casey","IL","USA",39.30250917,-88.00406194 "1I5","Freehold","Freehold","NY","USA",42.36425,-74.06596806 "1I9","Delphi Municipal","Delphi","IN","USA",40.54281417,-86.68167194 "1J0","Tri-County","Bonifay","FL","USA",30.84577778,-85.60138889 "1K2","Lindsay Municipal","Lindsay","OK","USA",34.85007333,-97.58642028 "1K4","David J. Perry","Goldsby","OK","USA",35.1550675,-97.47039389 "1K5","Waynoka Municipal","Waynoka","OK","USA",36.56670028,-98.85231333 "1K9","Satanta Municipal","Satanta","KS","USA",37.45419111,-100.9921119 "1L0","St. John the Baptist Parish","Reserve","LA","USA",30.08720833,-90.58266528 "1L1","Lincoln Co","Panaca","NV","USA",37.78746444,-114.4216567 "1L7","Escalante Municipal","Escalante","UT","USA",37.74532639,-111.5701653 "1L9","Parowan","Parowan","UT","USA",37.85969694,-112.816055 "1M1","North Little Rock Municipal","No Lit Rock","AR","USA",34.83398056,-92.25792778 "1M2","Belzoni Municipal","Belzoni","MS","USA",33.14518056,-90.51528472 "1M4","Posey ","Haleyville","AL","USA",34.28034806,-87.60044139 "1M5","Portland Municipal","Portland","TN","USA",36.59287528,-86.47691028 "1M7","Fulton","Fulton","KY","USA",36.52589417,-88.91561611 "1MO","Mountain Grove Memorial","Mountain Grove","MO","USA",37.12071889,-92.311245 "1N2","Spadaro ","East Moriches","NY","USA",40.82787639,-72.74871083 "1N4","Woodbine Muni ","Woodbine","NJ","USA",39.21915,-74.794765 "1N7","Blairstown","Blairstown","NJ","USA",40.97114556,-74.99747556 "1N9","Allentown Queen City Muni","Allentown","PA","USA",40.57027778,-75.48830556 "1ND3","Hamry ","Kindred","ND","USA",46.6485775,-97.00564306 "1O1","Grandfield Municipal","Grandfield","OK","USA",34.23758944,-98.74200917 "1O2","Lampson ","Lakeport","CA","USA",38.99017472,-122.8997175 "1O3","Lodi","Lodi","CA","USA",38.20241667,-121.2684167 "1O4","Thomas Municipal","Thomas","OK","USA",35.73338222,-98.73063833 "1O6","Dunsmuir Municipal-Mott","Dunsmuir","CA","USA",41.26320889,-122.2719528 "1R1","Jena","Jena","LA","USA",31.671005,-92.15846722 "1R7","Brookhaven-Lincoln County","Brookhaven","MS","USA",31.6058475,-90.40931583 "1R8","Bay Minette Municipal","Bay Minette","AL","USA",30.87046278,-87.81738167 "1S0","Pierce County ","Puyallup","WA","USA",47.10391667,-122.2871944 "1S3","Tillitt ","Forsyth","MT","USA",46.27110639,-106.6239206 "1S5","Sunnyside Municipal","Sunnyside","WA","USA",46.32763139,-119.9705964 "1S6","Priest River Muni","Priest River","ID","USA",48.19018611,-116.9093644 "1U7","Bear Lake County","Paris","ID","USA",42.24714972,-111.33826 "1V0","Navajo State Park ","Navajo Dam","NM","USA",36.80833833,-107.6514444 "1V2","Grant County ","Hyannis","NE","USA",42.00942944,-101.7693439 "1V5","Boulder Muni","Boulder","CO","USA",40.03942972,-105.2258217 "1V6","Fremont County","Canon City","CO","USA",38.42838111,-105.1054994 "1V9","Blake ","Delta","CO","USA",38.78539722,-108.0636611 "20A","Robbins ","Oneonta","AL","USA",33.97231972,-86.37942722 "20M","Macon Municipal","Macon","MS","USA",33.13345889,-88.53559806 "20N","Kingston-Ulster","Kingston","NY","USA",41.9852525,-73.96409722 "20U","Beach","Beach","ND","USA",46.92362444,-103.9785389 "20V","McElroy Airfield","Kremmling","CO","USA",40.05367972,-106.3689467 "21D","Lake Elmo","St Paul","MN","USA",44.99748861,-92.85568111 "21F","Jacksboro Municipal","Jacksboro","TX","USA",33.228725,-98.14671083 "22B","Mountain Meadow Airstrip","Burlington","CT","USA",41.77287528,-73.01121667 "22I","Vinton County","McArthur","OH","USA",39.328125,-82.44182167 "22M","Pontotoc County","Pontotoc","MS","USA",34.27593833,-89.03839694 "22N","Carbon Cty-Jake Arner Memorial","Lehighton","PA","USA",40.80950889,-75.76149639 "23J","Herlong","Jacksonville","FL","USA",30.27778889,-81.80594722 "23M","Clarke County","Quitman","MS","USA",32.08487111,-88.73893389 "23N","Bayport Aerodrome","Bayport","NY","USA",40.75843139,-73.05372083 "23R","Devine Municipal","Devine","TX","USA",29.1384075,-98.94189028 "24A","Jackson County","Sylva","NC","USA",35.3168625,-83.20936806 "24J","Suwannee County","Live Oak","FL","USA",30.30105583,-83.02318778 "24N","Jicarilla Apache Nation","Dulce","NM","USA",36.828535,-106.8841914 "25J","Cuthbert-Randolph","Cuthbert","GA","USA",31.70016583,-84.82492194 "25M","Ripley ","Ripley","MS","USA",34.72226778,-89.01504944 "25R","International","Edinburg","TX","USA",26.44201083,-98.12945306 "26A","Ashland/Lineville","Ashland/Lineville","AL","USA",33.28761417,-85.80412861 "26N","Ocean City Muni cipal","Ocean City","NJ","USA",39.26347222,-74.60747222 "26R","Jackson County","Edna/Ganado","TX","USA",29.00101,-96.58194667 "26U","McDermitt State","McDermitt","OR","USA",42.00211083,-117.7231972 "27A","Elbert County-Patz ","Elberton","GA","USA",34.09519722,-82.81586417 "27D","Myers ","Canby","MN","USA",44.72801889,-96.26309972 "27J","Newberry Municipal","Newberry","SC","USA",34.30927778,-81.63972222 "27K","Georgetown-Scott County","Georgetown","KY","USA",38.23442528,-84.43468667 "28J","Kay Larkin","Palatka","FL","USA",29.65863889,-81.68855556 "29D","Grove City","Grove City","PA","USA",41.14597611,-80.16592194 "29G","Portage County","Ravenna","OH","USA",41.210195,-81.25163083 "29S","Gardiner","Gardiner","MT","USA",45.04993556,-110.7466008 "2A0","Mark Anton","Dayton","TN","USA",35.48624611,-84.93109722 "2A1","Jamestown Municipal","Jamestown","TN","USA",36.34970833,-84.94664472 "2A3","Larsen Bay","Larsen Bay","AK","USA",57.53510667,-153.9784169 "2A9","Kotlik","Kotlik","AK","USA",63.03116111,-163.5299278 "2AK","Lime Village","Lime Village","AK","USA",61.35848528,-155.4403508 "2B3","Parlin ","Newport","NH","USA",43.38812944,-72.18925417 "2B7","Pittsfield Municipal","Pittsfield","ME","USA",44.76852778,-69.37441667 "2B9","Post Mills","Post Mills","VT","USA",43.884235,-72.25370333 "2D1","Barber","Alliance","OH","USA",40.97089139,-81.09981889 "2D5","Oakes Municipal","Oakes","ND","USA",46.17301972,-98.07987556 "2F5","Lamesa Municipal","Lamesa","TX","USA",32.75627778,-101.9194722 "2F6","Skiatook Municipal","Skiatook","OK","USA",36.357035,-96.01138556 "2F7","Commerce Municipal","Commerce","TX","USA",33.29288889,-95.89641806 "2F8","Morehouse Memorial","Bastrop","LA","USA",32.75607944,-91.88057194 "2G2","Jefferson County Airpark","Steubenville","OH","USA",40.35944306,-80.70007806 "2G3","Connellsville","Connellsville","PA","USA",39.95893667,-79.65713306 "2G4","Garrett County","Oakland","MD","USA",39.58027778,-79.33941667 "2G9","Somerset County","Somerset","PA","USA",40.03911111,-79.01455556 "2H0","Shelby County","Shelbyville","IL","USA",39.41042861,-88.8454325 "2H2","Aurora Memorial Municipal","Aurora","MO","USA",36.96230778,-93.69531111 "2I0","Madisonville Municipal","Madisonville","KY","USA",37.35502778,-87.39963889 "2I5","Chanute","Rantoul","IL","USA",40.29355556,-88.14236111 "2IS","Airglades","Clewiston","FL","USA",26.74200972,-81.04978917 "2J2","Liberty County","Hinesville","GA","USA",31.78461111,-81.64116667 "2J3","Louisville Municipal","Louisville","GA","USA",32.98654083,-82.38568139 "2J5","Millen","Millen","GA","USA",32.89376972,-81.96511583 "2J9","Quincy Municipal","Quincy","FL","USA",30.59786111,-84.55741667 "2K3","Stanton County Municipal","Johnson","KS","USA",37.58271111,-101.73281 "2K4","Scott ","Mangum","OK","USA",34.89172583,-99.52675667 "2K5","Telida","Telida","AK","USA",63.39387278,-153.2689733 "2M0","Princeton-Caldwell County","Princeton","KY","USA",37.11560444,-87.85556944 "2M2","Lawrenceburg Municipal","Lawrenceburg","TN","USA",35.2343025,-87.25793222 "2M3","Sallisaw Municipal","Sallisaw","OK","USA",35.43816667,-94.80277778 "2M4","G. V. Montgomery","Forest","MS","USA",32.35347778,-89.48867944 "2M8","Charles W. Baker","Millington","TN","USA",35.27897583,-89.93147611 "2O1","Gansner ","Quincy","CA","USA",39.94378056,-120.9468983 "2O3","Angwin-Parrett ","Angwin","CA","USA",38.57851778,-122.4352572 "2O6","Chowchilla","Chowchilla","CA","USA",37.11244417,-120.2468406 "2O7","Independence","Independence","CA","USA",36.81382111,-118.2050956 "2O8","Hinton Municipal","Hinton","OK","USA",35.50592472,-98.34236111 "2P2","Washington Island","Washington Island","WI","USA",45.38620833,-86.92448056 "2Q3","Yolo Co-Davis/Woodland/Winters","Davis/Woodland/Winters","CA","USA",38.5790725,-121.8566322 "2R0","Waynesboro Municipal","Waynesboro","MS","USA",31.64599472,-88.63475667 "2R4","Peter Prince ","Milton","FL","USA",30.63762083,-86.99365278 "2R5","St Elmo","St Elmo","AL","USA",30.50190833,-88.27511667 "2R9","Karnes County","Kenedy","TX","USA",28.8250075,-97.86558333 "2S1","Vashon Municipal","Vashon","WA","USA",47.45815333,-122.4773506 "2S6","Sportsman Airpark","Newberg","OR","USA",45.29567333,-122.9553783 "2S7","Chiloquin State","Chiloquin","OR","USA",42.58319167,-121.8761261 "2S8","Wilbur","Wilbur","WA","USA",47.75320639,-118.7438936 "2T1","Muleshoe Municipal","Muleshoe","TX","USA",34.18513639,-102.6410981 "2V1","Stevens ","Pagosa Springs","CO","USA",37.277505,-107.0558742 "2V2","Vance Brand","Longmont","CO","USA",40.16367139,-105.1630369 "2V5","Wray Municipal","Wray","CO","USA",40.10032333,-102.24096 "2V6","Yuma Municipal","Yuma","CO","USA",40.10415306,-102.7129869 "2W5","Maryland","Indian Head","MD","USA",38.60053667,-77.07296917 "2W6","Captain Walter Francis Duke Regional ","Leonardtown","MD","USA",38.31536111,-76.55011111 "2Y3","Yakutat SPB","Yakutat","AK","USA",59.5624775,-139.7410994 "2Y4","Rockwell City Municipal","Rockwell City","IA","USA",42.38748056,-94.61803333 "31F","Gaines County","Seminole","TX","USA",32.67535389,-102.652685 "32M","Norfolk","Norfolk","MA","USA",42.12787528,-71.37033556 "32S","Stevensville","Stevensville","MT","USA",46.52511111,-114.0528056 "33J","Geneva Municipal","Geneva","AL","USA",31.05527778,-85.88033333 "33M","Water Valley ","Water Valley","MS","USA",34.16677639,-89.68619722 "33N","Delaware Airpark","Dover","DE","USA",39.21837556,-75.59642667 "33S","Pru ","Ritzville","WA","USA",47.12487194,-118.3927539 "34A","Laurens County","Laurens","SC","USA",34.50705556,-81.94719444 "35A","Union County, Troy Shelton ","Union","SC","USA",34.68680111,-81.64121167 "35D","Padgham ","Allegan","MI","USA",42.53098278,-85.82513556 "35S","Wasco State","Wasco","OR","USA",45.58944444,-120.6741667 "36K","Lakin","Lakin","KS","USA",37.96946389,-101.2554472 "36S","Happy Camp","Happy Camp","CA","USA",41.79067944,-123.3889444 "36U","Heber City Municipal/Russ McDonald ","Heber","UT","USA",40.48180556,-111.4288056 "37T","Calico Rock-Izard County","Calico Rock","AR","USA",36.16565278,-92.14523611 "37W","Harnett County","Erwin","NC","USA",35.37880028,-78.73362917 "38A","Shaktoolik","Shaktoolik","AK","USA",64.36263194,-161.2025369 "38S","Deer Lodge-City-County","Deer Lodge","MT","USA",46.38881583,-112.7669842 "38U","Wayne Wonderland","Loa","UT","USA",38.36247972,-111.5960164 "39N","Princeton","Princeton","NJ","USA",40.39834833,-74.65760361 "3A0","Grove Hill Municipal","Grove Hill","AL","USA",31.68932389,-87.7613875 "3A1","Folsom ","Cullman","AL","USA",34.26870833,-86.85833611 "3A2","New Tazewell Municipal","Tazewell","TN","USA",36.41008417,-83.55546167 "3A3","Anson County","Wadesboro","NC","USA",35.02397611,-80.08127333 "3AU","Augusta Municipal","Augusta","KS","USA",37.67162778,-97.07787222 "3B0","Southbridge Municipal","Southbridge","MA","USA",42.10092806,-72.03840833 "3B1","Greenville Municipal","Greenville","ME","USA",45.46302778,-69.55161111 "3B2","Marshfield","Marshfield","MA","USA",42.09824111,-70.67212083 "3B9","Chester","Chester","CT","USA",41.38390472,-72.50589444 "3BS","Jack Barstow","Midland","MI","USA",43.66291528,-84.261325 "3CK","Lake In The Hills","Lake In The Hills","IL","USA",42.20680306,-88.32304028 "3CM","James Clements Municipal","Bay City","MI","USA",43.54691667,-83.89550222 "3CU","Cable Union","Cable","WI","USA",46.19424889,-91.24640972 "3D2","Ephraim/Gibraltar","Ephraim","WI","USA",45.13535778,-87.18586556 "3D4","Frankfort Dow Memorial","Frankfort","MI","USA",44.62506389,-86.20061944 "3F3","De Soto Parish","Mansfield","LA","USA",32.07345972,-93.76551889 "3F4","Vivian","Vivian","LA","USA",32.86133333,-94.01015361 "3F7","Jones Memorial","Bristow","OK","USA",35.80685278,-96.42185556 "3FM","Fremont Municipal","Fremont","MI","USA",43.43890528,-85.99478 "3FU","Faulkton Municipal","Faulkton","SD","USA",45.03191861,-99.11566417 "3G3","Wadsworth Municipal","Wadsworth","OH","USA",41.00158222,-81.75513111 "3G4","Ashland County","Ashland","OH","USA",40.90297222,-82.25563889 "3G7","Williamson/Sodus","Williamson","NY","USA",43.23472222,-77.12097222 "3GM","Grand Haven Memorial Airpark","Grand Haven","MI","USA",43.03404639,-86.1981625 "3I2","Mason County","Point Pleasant","WV","USA",38.91463889,-82.09858333 "3I7","Phillipsburg","Phillipsburg","OH","USA",39.91344194,-84.40030889 "3J1","Ridgeland","Ridgeland","SC","USA",32.49268694,-80.99233028 "3J7","Greene County Airpark","Greensboro","GA","USA",33.59766667,-83.139 "3JC","Freeman ","Junction City","KS","USA",39.04327556,-96.84328694 "3K3","Syracuse-Hamilton County Municipal","Syracuse","KS","USA",37.99167972,-101.7462822 "3K6","St Louis-Metro East","Troy/Marine/St. Louis","IL","USA",38.73290861,-89.80656722 "3K7","Mark Hoard Memorial","Leoti","KS","USA",38.45696333,-101.3532161 "3LC","Logan County","Lincoln","IL","USA",40.15847222,-89.33497222 "3LF","Litchfield Municipal","Litchfield","IL","USA",39.16635306,-89.67489694 "3M7","Lafayette Municipal","Lafayette","TN","USA",36.518375,-86.05828083 "3M8","North Pickens ","Reform","AL","USA",33.38900611,-88.00557806 "3M9","Warren Municipal","Warren","AR","USA",33.56044333,-92.08538861 "3MY","Mt. Hawley Auxiliary","Peoria","IL","USA",40.79525917,-89.6134025 "3N6","Old Bridge","Old Bridge","NJ","USA",40.32988667,-74.34678694 "3N8","Mahnomen County ","Mahnomen","MN","USA",47.25996056,-95.92809778 "3ND0","Northwood Municipal","Northwood","ND","USA",47.72423333,-97.59042222 "3O1","Gustine","Gustine","CA","USA",37.26271722,-120.9632586 "3O3","Municipal","Purcell","OK","USA",34.97979444,-97.38586167 "3O4","Sayre Municipal","Sayre","OK","USA",35.16755222,-99.65787361 "3O5","Walters Municipal","Walters","OK","USA",34.37258444,-98.40588583 "3O7","Hollister Municipal","Hollister","CA","USA",36.89334528,-121.4102706 "3O9","Grand Lake Regional","Afton","OK","USA",36.5775775,-94.86190028 "3R0","Beeville Municipal","Beeville","TX","USA",28.36455528,-97.79208194 "3R1","Bay City Municipal","Bay City","TX","USA",28.973255,-95.86345528 "3R2","Le Gros Memorial","Crowley","LA","USA",30.16173611,-92.48396111 "3R4","Hart","Many","LA","USA",31.54489667,-93.48645306 "3R7","Jennings","Jennings","LA","USA",30.24269333,-92.67344778 "3S4","Illinois Valley","Illinois Valley (Cave Junction)","OR","USA",42.10372417,-123.6822911 "3S8","Grants Pass","Grants Pass","OR","USA",42.51011722,-123.3879894 "3S9","Condon State-Pauling ","Condon","OR","USA",45.24651889,-120.1664233 "3SG","Harry W Browne","Saginaw - H.Browne","MI","USA",43.43341028,-83.86245833 "3SQ","St Charles","St Charles","MO","USA",38.84866139,-90.50011833 "3T3","Boyceville Municipal ","Boyceville","WI","USA",45.042185,-92.0293475 "3T5","Fayette Regional Air Center","La Grange","TX","USA",29.90930556,-96.9505 "3TR","Jerry Tyler Memorial","Niles","MI","USA",41.83590806,-86.22517611 "3U3","Bowman ","Anaconda","MT","USA",46.15313278,-112.86784 "3U7","Benchmark","Benchmark","MT","USA",47.48133194,-112.8697678 "3U8","Big Sandy","Big Sandy","MT","USA",48.16247972,-110.1132631 "3V4","Fort Morgan Municipal","Fort Morgan","CO","USA",40.33423194,-103.8039508 "3WO","Shawano Municipal","Shawano","WI","USA",44.78777778,-88.56152444 "3Y2","George L Scott Municipal","West Union","IA","USA",42.98508917,-91.79060417 "3Y3","Winterset Madison County","Winterset","IA","USA",41.36276778,-94.02106194 "3Z9","Haines SPB","Haines","AK","USA",59.23495111,-135.4407181 "40J","Perry-Foley","Perry","FL","USA",30.06927778,-83.58058333 "40N","Chester Cty-G O Carlson","Coatesville","PA","USA",39.97897222,-75.86547222 "40U","Manila","Manila","UT","USA",40.98607,-109.6784811 "41U","Manti-Ephraim","Manti","UT","USA",39.32912833,-111.6146397 "42A","Melbourne Municipal","Melbourne","AR","USA",36.07079222,-91.82914667 "42C","White Cloud","White Cloud","MI","USA",43.55974139,-85.77421944 "42J","Keystone Airpark","Keystone Heights","FL","USA",29.84475,-82.04752778 "42S","Poplar","Poplar","MT","USA",48.11595861,-105.1821928 "43A","Montgomery County","Star","NC","USA",35.38819528,-79.79281667 "44B","Dover/Foxcroft","Dover-Foxcroft","ME","USA",45.18338806,-69.2328225 "44N","Sky Acres","Millbrook","NY","USA",41.70742861,-73.73802889 "45J","Rockingham-Hamlet","Rockingham","NC","USA",34.89107083,-79.75905806 "45OH","North Bass Island","North Bass Island","OH","USA",41.71932528,-82.82196917 "45R","Kountz - Hawthorne ","Kountze/Silsbee","TX","USA",30.33633806,-94.25754361 "46A","Blairsville","Blairsville","GA","USA",34.85508722,-83.996855 "46D","Carrington Municipal","Carrington","ND","USA",47.45111111,-99.15111111 "46N","Sky Park","Red Hook","NY","USA",41.98458333,-73.83596556 "47A","Cherokee County","Canton","GA","USA",34.31058333,-84.42391667 "47J","Cheraw Municipal","Cheraw","SC","USA",34.71258333,-79.95794444 "47N","Central Jersey Regional","Manville","NJ","USA",40.52438417,-74.59839194 "47V","Curtis Municipal","Curtis","NE","USA",40.63750778,-100.4712539 "48A","Cochran","Cochran","GA","USA",32.39936111,-83.27591667 "48D","Clare Municipal","Clare","MI","USA",43.83111111,-84.74133333 "48I","Braxton County","Sutton","WV","USA",38.68704444,-80.65176083 "48K","Ness City Municipal","Ness City","KS","USA",38.47110278,-99.90806667 "48S","Harlem","Harlem","MT","USA",48.56666472,-108.7729339 "48V","Tri-County","Erie","CO","USA",40.010225,-105.047975 "49A","Gilmer County","Ellijay","GA","USA",34.62786417,-84.52492889 "49T","Downtown Heliport","Dallas","TX","USA",32.77333333,-96.80027778 "49X","Chemehuevi Valley","Chemehuevi Valley","CA","USA",34.52751083,-114.4310697 "49Y","Fillmore County","Preston","MN","USA",43.67676,-92.17973444 "4A2","Atmautluak","Atmautluak","AK","USA",60.86674556,-162.2731389 "4A4","Cornelius-Moore ","Cedartown","GA","USA",34.01869444,-85.14647222 "4A5","Marshall-Searcy County","Marshall","AR","USA",35.89893667,-92.65588611 "4A6","Scottsboro Municipal","Scottsboro","AL","USA",34.68897278,-86.0058125 "4A7","Clayton County","Hampton","GA","USA",33.38911111,-84.33236111 "4A9","Isbell ","Fort Payne","AL","USA",34.4728925,-85.72221722 "4B0","South Albany","South Bethlehem","NY","USA",42.56072611,-73.83395639 "4B1","Duanesburg","Duanesburg","NY","USA",42.75840889,-74.13290472 "4B6","Ticonderoga Muni","Ticonderoga","NY","USA",43.87700278,-73.41317639 "4B7","Schroon Lake","Schroon Lake","NY","USA",43.86256083,-73.74262972 "4B8","Robertson ","Plainville","CT","USA",41.69037667,-72.8648225 "4B9","Simsbury Tri-Town","Simsbury","CT","USA",41.91676389,-72.77731778 "4C8","Albia Municipal","Albia","IA","USA",40.99445361,-92.76297194 "4D0","Abrams Municipal","Grandledge","MI","USA",42.77420167,-84.73309806 "4D9","Alma Municipal","Alma","NE","USA",40.11389972,-99.34565306 "4F2","Panola County-Sharpe ","Carthage","TX","USA",32.17608333,-94.29880556 "4F4","Gilmer-Upshur County","Gilmer","TX","USA",32.699,-94.94886111 "4G1","Greenville Muni","Greenville","PA","USA",41.44683167,-80.39126167 "4G2","Hamburg Inc.","Hamburg","NY","USA",42.7008925,-78.91475694 "4G5","Monroe County","Woodsfield","OH","USA",39.77904472,-81.10277222 "4G6","Hornell Muni","Hornell","NY","USA",42.38214444,-77.6821125 "4G7","Fairmont Muni","Fairmont","WV","USA",39.44816667,-80.16702778 "4I0","Mingo County","Williamson","WV","USA",37.68760139,-82.26097306 "4I3","Knox County","Mount Vernon","OH","USA",40.32872222,-82.52377778 "4I7","Putnam County","Greencastle","IN","USA",39.63359556,-86.8138325 "4I9","Morrow County","Mt. Gilead","OH","USA",40.52452778,-82.85005556 "4J1","Brantley County","Nahunta","GA","USA",31.21272417,-81.90539083 "4J2","Berrien County","Nashville","GA","USA",31.21255556,-83.22627778 "4J5","Quitman-Brooks County","Quitman","GA","USA",30.80575139,-83.58654889 "4J6","St Marys","St Marys","GA","USA",30.75468028,-81.55731917 "4K0","Pedro Bay","Pedro Bay","AK","USA",59.78960972,-154.1238331 "4K5","Ouzinkie","Ouzinkie","AK","USA",57.92287611,-152.5005111 "4K6","Bloomfield Municipal","Bloomfield","IA","USA",40.73210556,-92.42826889 "4KA","Tununak","Tununak","AK","USA",60.57559667,-165.2731272 "4M1","Carroll County","Berryville","AR","USA",36.38340333,-93.61685667 "4M3","Carlisle Municipal","Carlisle","AR","USA",34.80823,-91.71205083 "4M4","Clinton Municipal","Clinton","AR","USA",35.59785528,-92.45182472 "4M7","Russellville-Logan County","Russellville","KY","USA",36.79991667,-86.81016667 "4M8","Clarendon Municipal","Clarendon","AR","USA",34.64870694,-91.39457111 "4M9","Corning Municipal","Corning","AR","USA",36.40423139,-90.64792639 "4N1","Greenwood Lake","West Milford","NJ","USA",41.12854806,-74.34584611 "4O3","Blackwell-Tonkawa Municipal","Blackwell-Tonkawa","OK","USA",36.74511583,-97.34959972 "4O4","McCurtain County Regional","Idabel","OK","USA",33.909325,-94.85835278 "4O5","Cherokee Municipal","Cherokee","OK","USA",36.78336306,-98.35035083 "4PH","Polacca","Polacca","AZ","USA",35.79167222,-110.4234653 "4R1","I H Bass Jr Memorial","Lumberton","MS","USA",31.01546028,-89.48256556 "4R3","Jackson Municipal","Jackson","AL","USA",31.47210861,-87.89472083 "4R4","Fairhope Municipal","Fairhope","AL","USA",30.4621125,-87.87801972 "4R5","Madeline Island","La Pointe","WI","USA",46.78865556,-90.75866944 "4R7","Eunice","Eunice","LA","USA",30.46628389,-92.42379917 "4R9","Dauphin Island","Dauphin Island","AL","USA",30.26048083,-88.12749972 "4S1","Gold Beach Muni","Gold Beach","OR","USA",42.41344444,-124.4242742 "4S2","Hood River","Hood River","OR","USA",45.67261833,-121.5364625 "4S3","Joseph State","Joseph","OR","USA",45.35709583,-117.2532244 "4S9","Portland-Mulino","Mulino (Portland)","OR","USA",45.21632417,-122.5900839 "4SD","Reno/Stead","Reno","NV","USA",39.66738111,-119.8754169 "4T6","Mid-Way","Midlothian-Waxahachie","TX","USA",32.45609722,-96.91240972 "4U3","Liberty County","Chester","MT","USA",48.51072222,-110.9908639 "4U6","Circle Town County","Circle","MT","USA",47.41861972,-105.5619431 "4V0","Rangely","Rangely","CO","USA",40.09469917,-108.7612172 "4V1","Johnson ","Walsenburg","CO","USA",37.69640056,-104.7838747 "4V9","Antelope County","Neligh","NE","USA",42.11222889,-98.0386775 "4W1","Elizabethtown Municipal","Elizabethtown","NC","USA",34.60183722,-78.57973306 "4Z4","Holy Cross","Holy Cross","AK","USA",62.18829583,-159.7749503 "4Z7","Hyder SPB","Hyder","AK","USA",55.90331972,-130.0067031 "50I","Kentland Municipal","Kentland","IN","USA",40.75873222,-87.42821917 "50J","Berkeley County","Moncks Corner","SC","USA",33.18605556,-80.03563889 "50K","Pawnee City Municipal","Pawnee City","NE","USA",40.11611111,-96.19445278 "50R","Lockhart Municipal","Lockhart","TX","USA",29.85033333,-97.67241667 "51D","Edgeley Municipal ","Edgeley","ND","USA",46.34858333,-98.73555556 "51Z","Minto (New)","Minto","AK","USA",65.14370889,-149.3699647 "52A","Madison Municipal","Madison","GA","USA",33.61212528,-83.46044333 "52E","Timberon ","Timberon","NM","USA",32.63388889,-105.6863889 "52J","Lee County","Bishopville","SC","USA",34.24459889,-80.23729333 "53A","Dr. C.P. Savage, Sr.","Montezuma","GA","USA",32.302,-84.00747222 "53K","Osage City Municipal","Osage City","KS","USA",38.63334222,-95.80859806 "54J","Defuniak Springs","Defuniak Springs","FL","USA",30.7313,-86.15160833 "55D","Grayling Army Airfield","Grayling","MI","USA",44.68032028,-84.72886278 "55J","Fernandina Beach Municipal","Fernandina Beach","FL","USA",30.61170083,-81.462345 "55S","Packwood","Packwood","WA","USA",46.60400083,-121.6778664 "56D","Wyandot County","Upper Sandusky","OH","USA",40.88336139,-83.3145325 "56M","Warsaw Municipal","Warsaw","MO","USA",38.34688889,-93.345425 "56S","Seaside Municipal","Seaside","OR","USA",46.01649694,-123.9054167 "57B","Islesboro","Islesboro","ME","USA",44.30285556,-68.91058722 "57C","East Troy Municipal","East Troy","WI","USA",42.79711111,-88.3725 "59B","Newton ","Jackman","ME","USA",45.63199111,-70.24728944 "5A4","Okolona Mun.-Richard M. Stovall ","Okolona","MS","USA",34.01580528,-88.72618944 GGally/tests/testthat/test-ggtable.R0000644000176200001440000000214014021243556017137 0ustar liggesuserscontext("ggtable") suppressMessages(require(broom)) test_that("example", { expect_print <- function(x) { expect_silent(print(x)) } skip_if_not_installed("reshape") reg <- lm(Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width, data = iris) expect_print(ggcoef(reg)) data(tips, package = "reshape") expect_print(ggtable(tips, "smoker", c("day", "time", "sex"))) # displaying row proportions expect_print(ggtable(tips, "smoker", c("day", "time", "sex"), cells = "row.prop")) # filling cells with residuals expect_print(ggtable(tips, "smoker", c("day", "time", "sex"), fill = "std.resid", legend = 1)) expect_print(ggtable(tips, "smoker", c("day", "time", "sex"), fill = "resid", legend = 1)) # if continuous variables are provided, just displaying some summary statistics expect_print(ggtable(tips, c("smoker", "total_bill"), c("day", "time", "sex", "tip"))) # specifying weights d <- as.data.frame(Titanic) expect_print(ggtable( d, "Survived", c("Class", "Sex", "Age"), mapping = aes(weight = Freq), cells = "row.prop", fill = "std.resid" )) }) GGally/tests/testthat/test-ggmatrix.R0000644000176200001440000001043013664221313017354 0ustar liggesusers context("ggmatrix") data(tips, package = "reshape") expect_print <- function(x) { testthat::expect_silent(print(x)) } test_that("stops", { expect_error(ggmatrix(plots = matrix(), nrow = 2, ncol = 3), "'plots' must be a list()") expect_error(ggmatrix(plots = list(), nrow = "2", ncol = 3), "'nrow' must be a numeric value") expect_error(ggmatrix(plots = list(), nrow = 2, ncol = "3"), "'ncol' must be a numeric value") expect_error( ggmatrix(plots = list(), nrow = c(2, 3), ncol = 3), "'nrow' must be a single numeric value" ) expect_error( ggmatrix(plots = list(), nrow = 2, ncol = c(2, 3)), "'ncol' must be a single numeric value" ) }) test_that("expression labels", { chars <- c("col1", "col2") exprs <- c("alpha[0]", "gamma[x + y ^ z]") expect_print(ggpairs(tips, 1:2, columnLabels = exprs, labeller = "label_parsed")) expect_error(print(ggpairs(tips, 1:2, columnLabels = expression(alpha, beta))), "xAxisLabels") }) test_that("byrow", { plotList <- list() for (i in 1:6) { p <- ggally_text(paste("Plot #", i, sep = "")) p$ggally_check_val <- i plotList[[i]] <- p } a <- ggmatrix( plotList, 2, 3, c("A", "B", "C"), c("D", "E"), byrow = TRUE ) k <- 1 for (i in 1:2) { for (j in 1:3) { expect_equal(a[i, j]$ggally_check_val, k) k <- k + 1 } } a <- ggmatrix( plotList, 2, 3, c("A", "B", "C"), c("D", "E"), byrow = FALSE ) k <- 1 for (j in 1:3) { for (i in 1:2) { expect_equal(a[i, j]$ggally_check_val, k) k <- k + 1 } } a }) test_that("missing plot", { plotList <- list() for (i in c(1, 3, 5)) { p <- ggally_text(paste("Plot #", i, sep = "")) p$ggally_check_val <- i plotList[[i]] <- p } a <- ggmatrix( plotList, 2, 3, c("A", "B", "C"), c("D", "E"), byrow = TRUE ) # reaches code where there are more cells than plots print(a) expect_equal(a[1, 1]$ggally_check_val, 1) expect_equal(a[1, 3]$ggally_check_val, 3) expect_equal(a[2, 2]$ggally_check_val, 5) }) test_that("str.ggmatrix", { pm <- ggpairs(tips, 1:3, upper = "blank") pm[1, 1] <- pm[1, 1] txt <- capture.output({ str(pm) }) expect_true(any(str_detect(txt, "Custom str.ggmatrix output:"))) txt <- capture.output({ str(pm, raw = TRUE) }) expect_false(any(str_detect(txt, "Custom str.ggmatrix output:"))) }) test_that("blank", { pm <- ggpairs(tips, 1:2) pm[1, 2] <- "blank" expect_print(pm) pm[2, 1] <- NULL expect_print(pm) expect_equal(length(pm$plots), 4) expect_error({ pm[2, 2] <- "not blank" }, "character values \\(besides 'blank'\\)") # nolint }) test_that("proportions", { pm <- ggpairs(iris, 1:2, mapping = ggplot2::aes(color = Species)) pm[2, 2] <- pm[2, 2] + ggplot2::coord_flip() pm2 <- ggmatrix( data = iris, pm$plots, ncol = 2, nrow = 2, xProportions = c(2, 1), yProportions = c(1, 2), title = "big plot, small marginals" ) expect_print(pm2) # turn on progress for a quick plot # TODO - turn test back on when it uses message properly # testthat::expect_message(print(pm2, progress = TRUE)) }) test_that("ggmatrix_gtable progress", { pm <- ggpairs(iris, 1:2) expect_silent({ pg <- ggmatrix_gtable(pm) }) expect_warning({ ggmatrix_gtable(pm, progress = TRUE) }) expect_warning({ ggmatrix_gtable(pm, progress_format = "asdfasdf :plot_i") }) }) # # printShowStrips <- c(TRUE, FALSE) # if (i <= length(printShowStrips)) { # printShowStrip <- printShowStrips[i] # } else { # printShowStrip <- NULL # } # test_that("ggmatrix proportions", { expect_error({ ggmatrix_proportions("not auto", reshape::tips, 1:ncol(reshape::tips)) }, "need to be non-NA") expect_error({ ggmatrix_proportions(NA, reshape::tips, 1:ncol(reshape::tips)) }, "need to be non-NA") expect_error({ ggmatrix_proportions(c(1, NA, 1, 1, 1, 1, 1), reshape::tips, 1:ncol(reshape::tips)) }, "need to be non-NA") expect_equal( ggmatrix_proportions("auto", reshape::tips, 1:ncol(reshape::tips)), c(2.5, 2.5, 2, 2, 4, 2, 2.5) ) expect_equal( ggmatrix_proportions(1, reshape::tips, 1:ncol(reshape::tips)), c(1, 1, 1, 1, 1, 1, 1) ) expect_equal( ggmatrix_proportions(NULL, reshape::tips, 1:ncol(reshape::tips)), NULL ) }) GGally/tests/testthat/test-gg-plots.R0000644000176200001440000001620314063456663017306 0ustar liggesusers context("gg-plots") # This file takes too long testthat::skip_on_cran() data(tips, package = "reshape") data(nasa) nas <- subset(nasa, x <= 2 & y == 1) expect_print <- function(x) { testthat::expect_silent(print(x)) } test_that("denstrip", { expect_message( suppressWarnings(print(ggally_denstrip(tips, mapping = aes_string("sex", "tip")))), "`stat_bin()` using `bins = 30`", fixed = TRUE ) expect_message( suppressWarnings(print(ggally_denstrip(tips, mapping = aes_string("tip", "sex")))), "`stat_bin()` using `bins = 30`", fixed = TRUE ) }) test_that("density", { p <- ggally_density( tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip", fill = "..level..") ) + ggplot2::scale_fill_gradient(breaks = c(0.05, 0.1, 0.15, 0.2)) expect_equal(p$labels$fill, "level") }) test_that("cor", { ti <- tips class(ti) <- c("NOTFOUND", "data.frame") p <- ggally_cor(ti, ggplot2::aes(x = total_bill, y = tip, color = day), use = "complete.obs") expect_equal(mapping_string(get("mapping", envir = p$layers[[2]])$colour), "labelp") p <- ggally_cor( ti, ggplot2::aes(x = total_bill, y = tip, color = I("blue")), use = "complete.obs" ) expect_equal(mapping_string(get("mapping", envir = p$layers[[1]])$colour), "I(\"blue\")") expect_err <- function(..., msg = NULL) { expect_error( ggally_cor( ti, ggplot2::aes(x = total_bill, y = tip), ... ), msg ) } expect_print(ggally_cor(ti, ggplot2::aes(x = total_bill, y = tip, color = I("green")))) ti3 <- ti2 <- ti ti2[2, "total_bill"] <- NA ti3[2, "total_bill"] <- NA ti3[3, "tip"] <- NA ti3[4, "total_bill"] <- NA ti3[4, "tip"] <- NA expect_warn <- function(data, msg) { expect_warning( ggally_cor(data, ggplot2::aes(x = total_bill, y = tip)), msg ) } expect_warn(ti2, "Removing 1 row that") expect_warn(ti3, "Removed 3 rows containing") expect_error( ggally_cor( ti, ggplot2::aes(x = total_bill, y = tip, color = size) ), "must be categorical" ) expect_silent( ggally_cor( ti, ggplot2::aes(x = total_bill, y = tip, color = as.factor(size)) ) ) }) test_that("diagAxis", { p <- ggally_diagAxis(iris, ggplot2::aes(x = Petal.Width)) pDat1 <- get("data", envir = p$layers[[2]]) attr(pDat1, "out.attrs") <- NULL testDt1 <- data.frame( xPos = c(0.076, 0.076, 0.076, 0.076, 0.076, 0.076, 0.500, 1.000, 1.500, 2.000, 2.500), yPos = c(0.500, 1.000, 1.500, 2.000, 2.500, 0.076, 0.076, 0.076, 0.076, 0.076, 0.076), lab = as.character(c(0.5, 1, 1.5, 2, 2.5, 0, 0.5, 1, 1.5, 2, 2.5)), hjust = c(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 0.5, 0.5), vjust = c(0.5, 0.5, 0.5, 0.5, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), stringsAsFactors = FALSE ) rownames(testDt1) <- 2:12 expect_equal(pDat1, testDt1) p <- ggally_diagAxis(iris, ggplot2::aes(x = Species)) pDat2 <- get("data", envir = p$layers[[2]]) attr(pDat2, "out.attrs") <- NULL testDt2 <- data.frame( x = c(0.125, 0.500, 0.875), y = c(0.875, 0.500, 0.125), lab = c("setosa", "versicolor", "virginica") ) expect_equal(pDat2, testDt2) expect_error({ ggally_diagAxis(iris, mapping = ggplot2::aes(y = Sepal.Length)) }, "mapping\\$x is null.") # nolint }) test_that("dates", { class(nas) <- c("NOTFOUND", "data.frame") p <- ggally_cor(nas, ggplot2::aes(x = date, y = ozone)) expect_equal(get("aes_params", envir = p$layers[[1]])$label, "Corr:\n0.278***") p <- ggally_barDiag(nas, ggplot2::aes(x = date)) expect_equal(mapping_string(p$mapping$x), "date") expect_equal(as.character(p$labels$y), "count") }) test_that("cor stars are aligned", { p <- ggally_cor(iris, ggplot2::aes(x = Sepal.Length, y = Petal.Width, color = as.factor(Species))) expect_equal(get("aes_params", envir = p$layers[[1]])$label, "Corr: 0.818***") #expect_equal(get("aes_params", envir = p$layers[[1]])$family, "mono") labels <- eval_data_col(p$layers[[2]]$data, p$layers[[2]]$mapping$label) expect_equal(as.character(labels), c(" setosa: 0.278. ", "versicolor: 0.546***", " virginica: 0.281* ")) }) test_that("ggally_statistic handles factors", { simple_chisq <- function(x, y){ scales::number(chisq.test(x,y)$p.value, accuracy=.001) } expect_silent({ p <- ggally_statistic(reshape::tips, aes(x=sex, y=day), text_fn = simple_chisq, title = "Chi^2") }) }) test_that("rescale", { p <- ggally_densityDiag(tips, mapping = ggplot2::aes(x = day), rescale = FALSE) expect_true(p$labels$y == "density") expect_print(p) p <- ggally_densityDiag(tips, mapping = ggplot2::aes(x = day), rescale = TRUE) expect_true(! identical(p$labels$y, "density")) expect_print(p) p <- ggally_barDiag(tips, mapping = ggplot2::aes(x = tip), binwidth = 0.25, rescale = FALSE) expect_true(p$labels$y == "count") expect_print(p) p <- ggally_barDiag(tips, mapping = ggplot2::aes(x = tip), binwidth = 0.25, rescale = TRUE) expect_true(! identical(p$labels$y, "count")) expect_print(p) }) test_that("shrink", { p <- ggally_smooth_loess(iris, mapping = ggplot2::aes(Sepal.Width, Petal.Length)) expect_true(!is.null(p$coordinates$limits$y)) expect_print(p) p <- ggally_smooth_loess(iris, mapping = ggplot2::aes(Sepal.Width, Petal.Length), shrink = FALSE) expect_true(is.null(p$coordinates$limits$y)) expect_print(p) }) test_that("smooth_se", { p <- ggally_smooth_loess(iris, mapping = ggplot2::aes(Sepal.Width, Petal.Length), se = TRUE) expect_equal(p$layers[[2]]$stat_params$se, TRUE) expect_print(p) p <- ggally_smooth_loess(iris, mapping = ggplot2::aes(Sepal.Width, Petal.Length), se = FALSE) expect_equal(p$layers[[2]]$stat_params$se, FALSE) expect_print(p) }) test_that("ggally_count", { p <- ggally_count( as.data.frame(Titanic), ggplot2::aes(x = Class, y = Survived, weight = Freq) ) expect_print(p) p <- ggally_count( as.data.frame(Titanic), ggplot2::aes(x = Class, y = Survived, weight = Freq), fill = "red" ) expect_print(p) p <- ggally_count( as.data.frame(Titanic), ggplot2::aes(x = Class, y = Survived, weight = Freq, fill = Sex) ) expect_print(p) p <- ggally_count( as.data.frame(Titanic), ggplot2::aes(x = Class, y = Survived, weight = Freq, fill = Class) ) expect_print(p) p <- ggally_count( as.data.frame(Titanic), ggplot2::aes(x = Survived, y = interaction(Sex, Age), weight = Freq, fill = Class) ) expect_print(p) # check that y character vectors are rendering p <- ggally_count( as.data.frame(Titanic), ggplot2::aes(x = Class, y = toupper(Survived), weight = Freq, fill = Class) ) expect_print(p) # check countDiag p <- ggally_countDiag( as.data.frame(Titanic), ggplot2::aes(x = Survived, weight = Freq, fill = Class) ) expect_print(p) # change size of tiles p <- ggally_count( as.data.frame(Titanic), ggplot2::aes(x = Class, y = Survived, weight = Freq, fill = Class), x.width = .5 ) expect_print(p) # no warnings expected if na.rm = TRUE p <- ggally_count( as.data.frame(Titanic), ggplot2::aes(x = interaction(Class, Age), y = Survived, weight = Freq, fill = Class), na.rm = TRUE ) expect_print(p) }) GGally/tests/testthat/test-zzz_ggpairs.R0000644000176200001440000005332013777103031020111 0ustar liggesuserscontext("ggpairs") # This file takes too long testthat::skip_on_cran() data(tips, package = "reshape") expect_print <- function(p) { testthat::expect_silent(print(p)) } facethistBindwidth1 <- list(combo = wrap("facethist", binwidth = 1)) facethistBindwidth1Duo <- list( comboHorizontal = wrap("facethist", binwidth = 1), comboVertical = wrap("facethist", binwidth = 1) ) test_that("structure", { expect_null <- function(x) { expect_true(is.null(x)) } expect_obj <- function(x) { expect_is(x$data, "data.frame") expect_is(x$plots, "list") expect_equivalent(length(x$plots), ncol(tips) ^ 2) expect_null(x$title) expect_null(x$xlab) expect_null(x$ylab) expect_is(x$xAxisLabels, "character") expect_is(x$yAxisLabels, "character") expect_is(x$showXAxisPlotLabels, "logical") expect_is(x$showYAxisPlotLabels, "logical") expect_null(x$legend) expect_is(x$byrow, "logical") expect_null(x$gg) expect_true("gg" %in% names(x)) } expect_obj(ggduo(tips)) expect_obj(ggpairs(tips)) }) test_that("columns", { expect_obj <- function(pm, columnsX, columnsY) { expect_equivalent(length(pm$plots), length(columnsX) * length(columnsY)) expect_equivalent(pm$xAxisLabels, columnsX) expect_equivalent(pm$yAxisLabels, columnsY) expect_equivalent(pm$ncol, length(columnsX)) expect_equivalent(pm$nrow, length(columnsY)) } columnsUsed <- c("total_bill", "tip", "sex") pm <- ggpairs(tips, columns = columnsUsed) expect_obj(pm, columnsUsed, columnsUsed) columnsX <- c("total_bill", "tip", "sex") columnsY <- c("smoker", "day", "time", "size") pm <- ggduo(tips, columnsX, columnsY) expect_obj(pm, columnsX, columnsY) }) test_that("column labels", { expect_obj <- function(pm, columnLabelsX, columnLabelsY) { expect_equivalent(pm$xAxisLabels, columnLabelsX) expect_equivalent(pm$yAxisLabels, columnLabelsY) } columnTitles <- c("A", "B", "C") pm <- ggpairs(tips, 1:3, columnLabels = columnTitles) expect_obj(pm, columnTitles, columnTitles) columnTitles <- c("Total Bill %", "Tip 123456", "Sex ( /a asdf)") pm <- ggpairs(tips, 1:3, columnLabels = columnTitles) expect_obj(pm, columnTitles, columnTitles) columnLabelsX <- c("Total Bill %", "Tip 123456", "Sex ( /a asdf)") columnLabelsY <- c("Smoker !#@", "Day 678", "1", "NULL") pm <- ggduo(tips, 1:3, 4:7, columnLabelsX = columnLabelsX, columnLabelsY = columnLabelsY) expect_obj(pm, columnLabelsX, columnLabelsY) }) test_that("character", { expect_obj <- function(pm) { expect_true(is.factor(pm$data$sex)) expect_true(is.factor(pm$data$smoker)) } tips2 <- tips tips2$sex <- as.character(tips2$sex) tips2$smoker <- as.character(tips2$smoker) expect_obj(ggpairs(tips2)) expect_obj(ggduo(tips2)) }) test_that("upper/lower/diag = blank", { columnsUsed <- 1:3 au <- ggpairs(tips, columnsUsed, upper = "blank") ad <- ggpairs(tips, columnsUsed, diag = "blank") al <- ggpairs(tips, columnsUsed, lower = "blank") for (i in 1:3) { for (j in 1:3) { if (i < j) { expect_true( is_blank_plot(au[i, j])) expect_false( is_blank_plot(ad[i, j])) expect_false( is_blank_plot(al[i, j])) } if (i > j) { expect_false( is_blank_plot(au[i, j])) expect_false( is_blank_plot(ad[i, j])) expect_true( is_blank_plot(al[i, j])) } if (i == j) { expect_false( is_blank_plot(au[i, j])) expect_true( is_blank_plot(ad[i, j])) expect_false( is_blank_plot(al[i, j])) } } } a <- ggpairs(tips, columnsUsed) a[1, 1] <- ggplot2::qplot(total_bill, data = tips) expect_false(is_blank_plot(a[1, 1])) }) test_that("stops", { expect_warning({ pm <- ggpairs(tips, axisLabels = "not_a_chosen", lower = facethistBindwidth1) }, "'axisLabels' not in ") # nolint expect_warning({ pm <- ggduo(tips, axisLabels = "not_a_chosen", types = facethistBindwidth1Duo) }, "'axisLabels' not in ") # nolint expect_warning({ pm <- ggpairs(tips, color = "sex") }, "Extra arguments: ") # nolint expect_warning({ pm <- ggduo(tips, 2:3, 2:3, types = list(combo = "facetdensity")) }, "Setting:\n\ttypes") # nolint expect_error({ ggpairs(tips, columns = c("tip", "day", "not in tips")) }, "Columns in 'columns' not found in data") # nolint expect_error({ ggduo(tips, columnsX = c("tip", "day", "not in tips"), columnsY = "smoker") }, "Columns in 'columnsX' not found in data") # nolint expect_error({ ggduo(tips, columnsX = c("tip", "day", "smoker"), columnsY = "not in tips") }, "Columns in 'columnsY' not found in data") # nolint expect_warning({ pm <- ggpairs(tips, legends = TRUE) }, "'legends' will be deprecated") # nolint expect_error({ ggpairs(tips, params = c(size = 2)) }, "'params' is a deprecated") # nolint expect_error( { ggpairs(tips, columns = 1:10) }, "Make sure your numeric 'columns' values are less than or equal to") # nolint expect_error( { ggduo(tips, columnsX = 1:10) }, "Make sure your numeric 'columnsX' values are less than or equal to") # nolint expect_error( { ggduo(tips, columnsY = 1:10) }, "Make sure your numeric 'columnsY' values are less than or equal to") # nolint expect_error({ ggpairs(tips, columns = -5:5) }, "Make sure your numeric 'columns' values are positive") # nolint expect_error({ ggduo(tips, columnsX = -5:5) }, "Make sure your numeric 'columnsX' values are positive") # nolint expect_error({ ggduo(tips, columnsY = -5:5) }, "Make sure your numeric 'columnsY' values are positive") # nolint expect_error({ ggpairs(tips, columns = (2:10) / 2) }, "Make sure your numeric 'columns' values are integers") # nolint expect_error({ ggduo(tips, columnsX = (2:10) / 2) }, "Make sure your numeric 'columnsX' values are integers") # nolint expect_error({ ggduo(tips, columnsY = (2:10) / 2) }, "Make sure your numeric 'columnsY' values are integers") # nolint expect_error({ ggpairs(tips, columns = 1:3, columnLabels = c("A", "B", "C", "Extra")) }, "The length of the 'columnLabels' does not match the length of the 'columns'") # nolint expect_error({ ggduo(tips, columnsX = 1:3, columnLabelsX = c("A", "B", "C", "Extra")) }, "The length of the 'columnLabelsX' does not match the length of the 'columnsX'") # nolint expect_error({ ggduo(tips, columnsY = 1:3, columnLabelsY = c("A", "B", "C", "Extra")) }, "The length of the 'columnLabelsY' does not match the length of the 'columnsY'") # nolint expect_error({ ggpairs(tips, upper = c("not_a_list")) }, "'upper' is not a list") # nolint expect_error({ ggpairs(tips, diag = c("not_a_list")) }, "'diag' is not a list") # nolint expect_error({ ggpairs(tips, lower = c("not_a_list")) }, "'lower' is not a list") # nolint expect_error({ ggduo(tips, types = c("not_a_list")) }, "'types' is not a list") # nolint # # couldn't get correct error message # # variables: 'colour' have non standard format: 'total_bill + tip'. # expect_error({ # ggpairs(tips, mapping = ggplot2::aes(color = total_bill + tip)) # }, "variables\\: \"colour\" have non standard format") # nolint # expect_error({ # ggduo(tips, mapping = ggplot2::aes(color = total_bill + tip)) # }, "variables\\: \"colour\" have non standard format") # nolint errorString <- "'aes_string' is a deprecated element" expect_error({ ggpairs(tips, upper = list(aes_string = ggplot2::aes(color = day))) }, errorString) # nolint expect_error({ ggpairs(tips, lower = list(aes_string = ggplot2::aes(color = day))) }, errorString) # nolint expect_error({ ggpairs(tips, diag = list(aes_string = ggplot2::aes(color = day))) }, errorString) # nolint expect_error({ ggduo(tips, types = list(aes_string = ggplot2::aes(color = day))) }, errorString) # nolint expect_diag_warn <- function(key, value) { warnString <- str_c("Changing diag\\$", key, " from '", value, "' to '", value, "Diag'") diagObj <- list() diagObj[[key]] <- value expect_warning({ pm <- ggpairs(tips, diag = diagObj) }, warnString ) } # diag # continuous # densityDiag # barDiag # blankDiag # discrete # barDiag # blankDiag expect_diag_warn("continuous", "density") expect_diag_warn("continuous", "bar") expect_diag_warn("continuous", "blank") expect_diag_warn("discrete", "bar") expect_diag_warn("discrete", "blank") }) test_that("cardinality", { expect_silent(stop_if_high_cardinality(tips, 1:ncol(tips), NULL)) expect_silent(stop_if_high_cardinality(tips, 1:ncol(tips), FALSE)) expect_error( stop_if_high_cardinality(tips, 1:ncol(tips), "not numeric"), "'cardinality_threshold' should" ) expect_error( stop_if_high_cardinality(tips, 1:ncol(tips), 2), "Column 'day' has more levels" ) }) test_that("blank types", { columnsUsed <- 1:3 pmUpper <- ggpairs(tips, columnsUsed, upper = "blank", lower = facethistBindwidth1) pmDiag <- ggpairs(tips, columnsUsed, diag = "blank", lower = facethistBindwidth1) pmLower <- ggpairs(tips, columnsUsed, lower = "blank") for (i in columnsUsed) { for (j in columnsUsed) { if (i < j) { # upper expect_true(is_blank_plot(pmUpper[i, j])) expect_false(is_blank_plot(pmDiag[i, j])) expect_false(is_blank_plot(pmLower[i, j])) } else if ( i > j) { # lower expect_false(is_blank_plot(pmUpper[i, j])) expect_false(is_blank_plot(pmDiag[i, j])) expect_true(is_blank_plot(pmLower[i, j])) } else { # diag expect_false(is_blank_plot(pmUpper[i, j])) expect_true(is_blank_plot(pmDiag[i, j])) expect_false(is_blank_plot(pmLower[i, j])) } } } columnsUsedX <- 1:3 columnsUsedY <- 4:5 pmDuo <- ggduo(tips, columnsUsedX, columnsUsedY, types = "blank") for (i in seq_along(columnsUsedX)) { for (j in seq_along(columnsUsedY)) { expect_true(is_blank_plot(pmDuo[j, i])) } } }) test_that("axisLabels", { expect_obj <- function(pm, axisLabel) { expect_true(is.null(pm$showStrips)) if (axisLabel == "show") { expect_true(pm$showXAxisPlotLabels) expect_true(pm$showYAxisPlotLabels) expect_false(is.null(pm$xAxisLabels)) expect_false(is.null(pm$yAxisLabels)) } else if (axisLabel == "internal") { for (i in 1:(pm$ncol)) { p <- pm[i, i] expect_true(inherits(p$layers[[1]]$geom, "GeomText")) expect_true(inherits(p$layers[[2]]$geom, "GeomText")) expect_equal(length(p$layers), 2) } expect_false(pm$showXAxisPlotLabels) expect_false(pm$showYAxisPlotLabels) expect_true(is.null(pm$xAxisLabels)) expect_true(is.null(pm$yAxisLabels)) } else if (axisLabel == "none") { expect_false(pm$showXAxisPlotLabels) expect_false(pm$showYAxisPlotLabels) expect_false(is.null(pm$xAxisLabels)) expect_false(is.null(pm$yAxisLabels)) } expect_print(pm) } fn <- function(axisLabels) { pm <- ggpairs( iris, c(3, 4, 5, 1), upper = "blank", lower = facethistBindwidth1, axisLabels = axisLabels, title = str_c("axisLabels = ", axisLabels), progress = FALSE ) pm } for (axisLabels in c("show", "internal", "none")) { expect_obj(fn(axisLabels), axisLabels) } plots <- ggpairs(iris, 1:3)$plots for (val in c(TRUE, FALSE)) { pm <- ggmatrix( plots, 3, 3, showAxisPlotLabels = val ) expect_equal(pm$showXAxisPlotLabels, val) expect_equal(pm$showYAxisPlotLabels, val) } fn <- function(axisLabels) { a <- ggduo( iris, c(4, 5), c(5, 1), types = facethistBindwidth1Duo, axisLabels = axisLabels, title = str_c("axisLabels = ", axisLabels) ) a } for (axisLabels in c("show", "none")) { expect_obj(fn(axisLabels), axisLabels) } }) test_that("strips and axis", { # axis should line up with left side strips pm <- ggpairs( tips, c(3, 1, 4), showStrips = TRUE, title = "Axis should line up even if strips are present", lower = list(combo = wrap("facethist", binwidth = 1)) ) expect_print(pm) # default behavior. tested in other places # expect_silent({ # pm <- ggpairs(tips, c(3, 1, 4), showStrips = FALSE) # print(pm) # }) }) test_that("dates", { startDt <- as.POSIXct("2000-01-01", tz = "UTC") endDt <- as.POSIXct("2000-04-01", tz = "UTC") dts <- seq(startDt, endDt, 86400) # 86400 = as.numeric(ddays(1)) x <- data.frame( date = dts, x1 = rnorm(length(dts)), x2 = rnorm(length(dts)), cat = sample(c("a", "b", "c"), length(dts), replace = TRUE) ) class(x) <- c("NOT_data.frame", "data.frame") a <- ggpairs( x, c(2, 1, 4, 3), mapping = ggplot2::aes(color = cat), lower = "blank", diag = list(continuous = "densityDiag"), upper = list(continuous = "cor") ) p <- a[1, 2] expect_true(inherits(p$layers[[1]]$geom, "GeomText")) expect_true(inherits(p$layers[[2]]$geom, "GeomText")) expect_equal(length(p$layers), 2) a <- ggpairs( x, c(2, 1, 4, 3), mapping = ggplot2::aes(color = cat), lower = "blank", diag = list(continuous = "barDiag"), upper = list(continuous = "cor") ) p <- a[1, 1] expect_true(inherits(p$layers[[1]]$geom, "GeomBar")) expect_equal(length(p$layers), 1) }) test_that("mapping", { pm <- ggpairs(tips, mapping = 1:3) expect_equal(pm$xAxisLabels, names(tips)[1:3]) pm <- ggpairs(tips, columns = 1:3) expect_equal(pm$xAxisLabels, names(tips)[1:3]) expect_error({ ggpairs(tips, columns = 1:3, mapping = 1:3) }, "'mapping' should not be numeric") # nolint }) test_that("user functions", { p0 <- ggally_points(tips, ggplot2::aes(x = total_bill, y = tip)) pm1 <- ggpairs(tips, 1:2, lower = list(continuous = "points")) p1 <- pm1[2, 1] pm2 <- ggpairs(tips, 1:2, lower = list(continuous = ggally_points)) p2 <- pm2[2, 1] expect_equal_plots <- function(x, y) { expect_equal(length(x$layers), 1) expect_equal(length(y$layers), 1) expect_true( "GeomPoint" %in% class(x$layers[[1]]$geom) ) expect_true( "GeomPoint" %in% class(y$layers[[1]]$geom) ) expect_equal(x$labels, list(x = "total_bill", y = "tip")) expect_equal(x$labels, y$labels) } expect_equal_plots(p0, p1) expect_equal_plots(p0, p2) }) test_that("NA data", { expect_is_na_plot <- function(p) { expect_true(identical(as.character(p$data$label), "NA")) expect_true(inherits(p$layers[[1]]$geom, "GeomText")) expect_equivalent(length(p$layers), 1) } expect_not_na_plot <- function(p) { expect_false(identical(as.character(p$data$label), "NA")) } expect_is_blank <- function(p) { expect_true(is_blank_plot(p)) } dd <- data.frame(x = c(1:5, rep(NA, 5)), y = c(rep(NA, 5), 2:6), z = 1:10, w = NA) pm <- ggpairs(dd) test_pm <- function(pm, na_mat) { for (i in 1:4) { for (j in 1:4) { if (na_mat[i, j]) { expect_is_na_plot(pm[i, j]) } else { if (j == 3 & i < 3) { expect_warning({ p <- pm[i, j] }, "Removed 5 rows" ) } else { p <- pm[i, j] } expect_not_na_plot(p) } } } } na_mat <- matrix(FALSE, ncol = 4, nrow = 4) na_mat[1, 2] <- TRUE na_mat[2, 1] <- TRUE na_mat[1:4, 4] <- TRUE na_mat[4, 1:4] <- TRUE test_pm(pm, na_mat) }) test_that("strip-top and strip-right", { data(tips, package = "reshape") double_strips <- function(data, mapping, ...) { dt <- count(data, c(mapping_string(mapping$x), mapping_string(mapping$y))) ggplot2::qplot( xmin = 0.25, xmax = 0.75, ymin = 1, ymax = freq, data = dt, geom = "rect" ) + ggplot2::facet_grid(paste0(mapping_string(mapping$y), " ~ ", mapping_string(mapping$x))) + ggplot2::scale_x_continuous(breaks = 0.5, labels = NULL) } pm <- ggpairs( tips, 3:6, lower = "blank", diag = "blank", upper = list(discrete = double_strips), progress = FALSE ) expect_print(pm) pm <- ggpairs( tips, 3:6, lower = "blank", diag = "blank", upper = list(discrete = double_strips), showStrips = TRUE, progress = FALSE ) expect_print(pm) }) test_that("subtypes", { testthat::skip_on_cran() testthat::skip_if_not_installed("Hmisc") # list of the different plot types to check # continuous # points # smooth # smooth_loess # density # cor # blank # combo # box # dot plot # facethist # facetdensity # denstrip # blank # discrete # ratio # facetbar # blank gn <- function(x) { fnName <- attr(x, "name") ifnull(fnName, x) } ggpairs_fn1 <- function(title, types, diag, ...) { ggpairs( tips, 1:4, axisLabels = "show", title = paste( "upper = c(cont = ", gn(types$continuous), ", combo = ", gn(types$combo), ", discrete = ", gn(types$discrete), "); diag = c(cont = ", gn(diag$continuous), ", discrete = ", gn(diag$discrete), ")", sep = ""), upper = types, lower = types, diag = diag, progress = FALSE, ... ) + ggplot2::theme(plot.title = ggplot2::element_text(size = 9)) } ggpairs_fn2 <- function(...) { ggpairs_fn1(..., mapping = ggplot2::aes(color = day), legend = c(1, 3)) } ggduo_fn1 <- function(title, types, diag, ...) { types$comboHorizontal <- types$combo types$comboVertical <- types$combo types$combo <- NULL ggduo( tips, 1:3, 1:4, axisLabels = "show", title = paste( "types = c(cont = ", gn(types$continuous), ", combo = ", gn(types$comboHorizontal), ", discrete = ", gn(types$discrete), ")", sep = ""), types = types, progress = FALSE, ... ) + ggplot2::theme(plot.title = ggplot2::element_text(size = 9)) } ggduo_fn2 <- function(...) { ggduo_fn1(..., mapping = ggplot2::aes(color = day), legend = 3) + theme(legend.position = "bottom") } # re ordered the subs so that density can have no binwidth param conSubs <- list( "autopoint", "density", "points", "smooth", "smooth_lm", "smooth_loess", "cor", "blank") comSubs <- list( "autopoint", "box", "dot", "box_no_facet", "dot_no_facet", wrap("facethist", binwidth = 1), "facetdensity", "facetdensitystrip", "summarise_by", wrap("denstrip", binwidth = 1), "blank" ) disSubs <- list( "autopoint", "colbar", "count", "cross", "crosstable", "facetbar", "ratio", "rowbar", "table", "trends", "blank") conDiagSubs <- c("autopointDiag", "densityDiag", wrap("barDiag", binwidth = 1), "blankDiag") disDiagSubs <- c("autopointDiag", "barDiag", "countDiag", "tableDiag", "blankDiag") # for (fn in list(ggpairs_fn1, ggpairs_fn2, ggduo_fn1, ggduo_fn2)) { for (fn_num in 1:4) { fn <- list(ggpairs_fn1, ggpairs_fn2, ggduo_fn1, ggduo_fn2)[[fn_num]] for (i in 1:6) { conSub <- if (i <= length(conSubs)) conSubs[[i]] else "blank" comSub <- if (i <= length(comSubs)) comSubs[[i]] else "blank" disSub <- if (i <= length(disSubs)) disSubs[[i]] else "blank" diagConSub <- if (i <= length(conDiagSubs)) conDiagSubs[[i]] else "blankDiag" diagDisSub <- if (i <= length(disDiagSubs)) disDiagSubs[[i]] else "blankDiag" # print(list( # fn_num = fn_num, # types = list( # continuous = conSub, # combo = comSub, # discrete = disSub # ), # diag = list( # continuous = diagConSub, # discrete = diagDisSub # ) # )) # expect_silent({ pm <- fn( types = list( continuous = conSub, combo = comSub, discrete = disSub ), diag = list( continuous = diagConSub, discrete = diagDisSub ) ) }) if (grepl("/Users/barret/", getwd(), fixed = TRUE)) { # only if on personal machine, do viz test expect_print(pm) } } } expect_error({ ggpairs(tips, 1:2, lower = "blank", diag = "blank", upper = list(continuous = "BAD_TYPE")) }) }) # pm <- ggpairs(tips, upper = "blank") # # pm # # Custom Example # pm <- ggpairs( # tips[, c(1, 3, 4, 2)], # upper = list(continuous = "density", combo = "box"), # lower = list(continuous = "points", combo = "dot") # ) # # pm # # Use sample of the diamonds data # data(diamonds, package="ggplot2") # diamonds.samp <- diamonds[sample(1:dim(diamonds)[1], 200), ] # # Custom Example # pm <- ggpairs( # diamonds.samp[, 1:5], # upper = list(continuous = "density", combo = "box"), # lower = list(continuous = "points", combo = "dot"), # color = "cut", # alpha = 0.4, # title = "Diamonds" # ) # # pm # # Will plot four "Incorrect Plots" # bad_plots <- ggpairs( # tips[, 1:3], # upper = list(continuous = "wrongType1", combo = "wrongType2"), # lower = list(continuous = "IDK1", combo = "IDK2", discrete = "mosaic"), # ) # # bad_plots # # Only Variable Labels on the diagonal (no axis labels) # pm <- ggpairs(tips[, 1:3], axisLabels="internal") # # pm # # Only Variable Labels on the outside (no axis labels) # pm <- ggpairs(tips[, 1:3], axisLabels="none") # # pm # # Custom Examples # custom_car <- ggpairs(mtcars[, c("mpg", "wt", "cyl")], upper = "blank", title = "Custom Example") # #' # ggplot example taken from example(geom_text) # #' plot <- ggplot2::ggplot(mtcars, ggplot2::aes(x=wt, y=mpg, label=rownames(mtcars))) # #' plot <- plot + # #' ggplot2::geom_text(ggplot2::aes(colour=factor(cyl)), size = 3) + # #' ggplot2::scale_colour_discrete(l=40) # #' custom_car <- putPlot(custom_car, plot, 1, 2) # #' personal_plot <- ggally_text( # #' "ggpairs allows you\nto put in your\nown plot.\nLike that one.\n <---" # #' ) # #' custom_car <- putPlot(custom_car, personal_plot, 1, 3) # #' # custom_car GGally/tests/testthat/test-ggfacet.R0000644000176200001440000000204313663637143017146 0ustar liggesuserscontext("ggfacet") expect_print <- function(p) { testthat::expect_silent(print(p)) } if (requireNamespace("chemometrics", quietly = TRUE)) { data(NIR, package = "chemometrics") NIR_sub <- data.frame(NIR$yGlcEtOH, NIR$xNIR[, 1:3]) test_that("warnings", { expect_warning( ggfacet(iris, columnsX = 1:5, columnsY = 1), "1 factor variables are being removed from X columns" ) expect_warning( ggfacet(iris, columnsX = 1, columnsY = 1:5), "1 factor variables are being removed from Y columns" ) }) test_that("generally works", { # factor variables expect_print( ggfacet( NIR_sub, columnsY = 1:2, columnsX = 3:5, fn = ggally_smooth_loess ) ) }) test_that("generally works", { # factor variables expect_print( ggfacet( NIR_sub, columnsY = 1:2, columnsX = 3:5, fn = ggally_smooth_loess ) ) expect_print( ggts(pigs, "time", c("gilts", "profit", "s_per_herdsz", "production", "herdsz")) ) }) } GGally/tests/testthat/test-gglyph.R0000644000176200001440000000710013663637143017037 0ustar liggesusers context("gglyph") data(nasa) nasaLate <- nasa[ nasa$date >= as.POSIXct("1998-01-01") & nasa$lat >= 20 & nasa$lat <= 40 & nasa$long >= -80 & nasa$long <= -60 , ] do_glyph <- function(...) { glyphs( nasaLate, # no lint "long", "day", "lat", "surftemp", height = 2.37, width = 2.38, ... ) } do_gg <- function(dt) { ggplot2::ggplot(dt, ggplot2::aes(gx, gy, group = gid)) + add_ref_lines(dt, color = "red", size = 0.5) + add_ref_boxes(dt, color = "blue") + ggplot2::geom_path() + ggplot2::theme_bw() + ggplot2::labs(x = "", y = "") + ggplot2::xlim(-80, -60) + ggplot2::ylim(20, 40) } test_that("examples", { dt <- do_glyph() expect_true(all(c("gx", "gy", "gid") %in% names(dt))) expect_true(all(names(nasaLate) %in% names(dt))) p <- do_gg(dt) expect_equal(length(p$layers), 3) expect_equal(as.character(get("aes_params", envir = p$layers[[1]])$colour), "red") expect_equal(as.character(get("aes_params", envir = p$layers[[2]])$colour), "blue") }) test_that("message", { expect_message(glyphs(nasaLate, "long", "day", "lat", "surftemp", height = 1), "Using width 2.38") expect_message(glyphs(nasaLate, "long", "day", "lat", "surftemp", width = 1), "Using height 2.37") }) test_that("scales", { dt <- do_glyph(x_scale = log) dt$dayLog <- dt$day dt$day <- NULL dtm <- merge(dt, nasaLate) expect_true(all(dtm$dayLog == log(dtm$day))) dt <- do_glyph(y_scale = log) dt$surftempLog <- dt$surftemp dt$surftemp <- NULL dtm <- merge(dt, nasaLate) expect_true(all(dtm$surftempLog == log(dtm$surftemp))) for (scale_fn in c(range01, max1, mean0, min0, rescale01, rescale11)) { dt <- do_glyph(y_scale = scale_fn) dt$surftempScaled <- dt$surftemp dt$surftemp <- NULL dtm <- merge(dt, nasaLate) expect_true(all(dtm$surftempScaled != dtm$surftemp)) } for (scale_fn in c(rescale01, rescale11)) { scale_fn2 <- function(x) { scale_fn(x, xlim = c(1 / 4, 3 / 4)) } dt <- do_glyph(y_scale = scale_fn2) dt$surftempScaled <- dt$surftemp dt$surftemp <- NULL dtm <- merge(dt, nasaLate) expect_true(all(dtm$surftempScaled != dtm$surftemp)) } }) test_that("polar", { dt <- do_glyph(polar = TRUE) expect_equal(attr(dt, "polar"), TRUE) # idk how to test that polar happened p <- do_gg(dt) expect_equal(length(p$layers), 3) }) test_that("fill", { dt <- do_glyph() # idk how to test that polar happened do_gg_fill <- function(...){ ggplot2::ggplot(dt, ggplot2::aes(gx, gy, group = gid)) + add_ref_lines(dt, color = "red", size = 0.5) + add_ref_boxes(dt, color = "blue", ...) + ggplot2::geom_path() + ggplot2::theme_bw() + ggplot2::labs(x = "", y = "") + ggplot2::xlim(-80, -60) + ggplot2::ylim(20, 40) } p <- do_gg_fill(fill = "green") expect_equal(mapping_string(get("aes_params", envir = p$layers[[2]])$fill), "\"green\"") p <- do_gg_fill(var_fill = "gid") expect_equal(mapping_string(get("mapping", envir = p$layers[[2]])$fill), "fill") }) test_that("print", { dt <- do_glyph() txt <- capture.output(print(dt)) expect_equal(txt[length(txt) - 2], "Cartesian glyphplot: ") expect_equal(txt[length(txt) - 1], " Size: [2.38, 2.37]") expect_equal(txt[length(txt) - 0], " Major axes: long, lat" ) dt <- do_glyph(polar = TRUE) txt <- capture.output(print(dt)) expect_equal(txt[length(txt) - 2], "Polar glyphplot: ") expect_equal(txt[length(txt) - 1], " Size: [2.38, 2.37]") expect_equal(txt[length(txt) - 0], " Major axes: long, lat" ) txt <- capture.output(print(rel(0.95))) expect_equal(txt, "[1] 0.95 *") }) GGally/tests/testthat/test-ggcoef.R0000644000176200001440000000112413663637143016777 0ustar liggesusers context("ggcoef") suppressMessages(require(broom)) test_that("example", { expect_print <- function(x) { expect_silent(print(x)) } reg <- lm(Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width, data = iris) expect_print(ggcoef(reg)) skip_if_not_installed("MASS") d <- as.data.frame(Titanic) reg2 <- glm(Survived ~ Sex + Age + Class, family = binomial, data = d, weights = d$Freq) expect_print(ggcoef(reg2, exponentiate = TRUE)) expect_print(ggcoef( reg2, exponentiate = TRUE, exclude_intercept = TRUE, errorbar_height = .2, color = "blue" )) }) GGally/tests/testthat/test-ggsave.R0000644000176200001440000000044713663637143017030 0ustar liggesusers context("ggsave") test_that("ggsave", { pm <- ggpairs(iris, 1:2) test_file <- "test.pdf" on.exit({ unlink(test_file) }) expect_true(!file.exists(test_file)) expect_silent({ ggsave(test_file, pm, width = 7, height = 7) }) expect_true(file.exists(test_file)) }) GGally/tests/testthat/test-stat_cross.R0000644000176200001440000000770613761572054017743 0ustar liggesuserscontext("stat_cross") test_that("example", { expect_print <- function(x) { expect_silent(print(x)) } d <- as.data.frame(Titanic) # plot number of observations expect_print(ggplot(d) + aes(x = Class, y = Survived, weight = Freq, size = after_stat(observed)) + stat_cross() + scale_size_area(max_size = 20)) # custom shape and fill colour based on chi-squared residuals expect_print(ggplot(d) + aes( x = Class, y = Survived, weight = Freq, size = after_stat(observed), fill = after_stat(std.resid) ) + stat_cross(shape = 22) + scale_fill_steps2(breaks = c(-3, -2, 2, 3), show.limits = TRUE) + scale_size_area(max_size = 20)) # plotting the number of observations as a table expect_print(ggplot(d) + aes( x = Class, y = Survived, weight = Freq, label = after_stat(observed) ) + geom_text(stat = "cross")) # Row proportions with standardized residuals expect_print(ggplot(d) + aes( x = Class, y = Survived, weight = Freq, label = scales::percent(after_stat(row.prop)), size = NULL, fill = after_stat(std.resid) ) + stat_cross(shape = 22, size = 30) + geom_text(stat = "cross") + scale_fill_steps2(breaks = c(-3, -2, 2, 3), show.limits = TRUE) + facet_grid(Sex ~ .) + labs(fill = "Standardized residuals") + theme_minimal()) # ggally_cross skip_if_not_installed("reshape") data(tips, package = "reshape") expect_print(ggally_cross(tips, mapping = aes(x = smoker, y = sex))) expect_print(ggally_cross(tips, mapping = aes(x = day, y = time))) # Custom fill expect_print(ggally_cross(tips, mapping = aes(x = smoker, y = sex), fill = "red")) # Custom shape expect_print(ggally_cross(tips, mapping = aes(x = smoker, y = sex), shape = 21)) # Fill squares according to standardized residuals d <- as.data.frame(Titanic) expect_print(ggally_cross( d, mapping = aes(x = Class, y = Survived, weight = Freq, fill = after_stat(std.resid)) ) + scale_fill_steps2(breaks = c(-3, -2, 2, 3), show.limits = TRUE)) # Add labels expect_print(ggally_cross( tips, mapping = aes( x = smoker, y = sex, colour = smoker, label = scales::percent(after_stat(prop)) ) )) # Customize labels' appearance and same size for all squares expect_print(ggally_cross( tips, mapping = aes( x = smoker, y = sex, size = NULL, # do not map size to a variable label = scales::percent(after_stat(prop)) ), size = 40, # fix value for points size fill = "darkblue", geom_text_args = list(colour = "white", fontface = "bold", size = 6) )) expect_print(ggally_table(tips, mapping = aes(x = smoker, y = sex))) expect_print(ggally_table(tips, mapping = aes(x = day, y = time))) expect_print(ggally_table(tips, mapping = aes(x = smoker, y = sex, colour = smoker))) # colour is kept only if equal to x or y expect_print(ggally_table(tips, mapping = aes(x = smoker, y = sex, colour = day))) # diagonal version expect_print(ggally_tableDiag(tips, mapping = aes(x = smoker))) # custom label size and color expect_print(ggally_table(tips, mapping = aes(x = smoker, y = sex), size = 16, color = "red")) # display column proportions expect_print(ggally_table( tips, mapping = aes(x = day, y = sex, label = scales::percent(after_stat(col.prop))) )) # draw table cells expect_print(ggally_table( tips, mapping = aes(x = smoker, y = sex), geom_tile_args = list(colour = "black", fill = "white") )) # Use standardized residuals to fill table cells expect_print(ggally_table( as.data.frame(Titanic), mapping = aes( x = Class, y = Survived, weight = Freq, fill = after_stat(std.resid), label = scales::percent(after_stat(col.prop), accuracy = .1) ), geom_tile_args = list(colour = "black") ) + scale_fill_steps2(breaks = c(-3, -2, 2, 3), show.limits = TRUE)) }) GGally/tests/testthat/test-ggparcoord.R0000644000176200001440000002306613663637143017705 0ustar liggesusers context("ggparcoord") set.seed(123) data(diamonds, package = "ggplot2") diamonds.samp <- diamonds[sample(1:dim(diamonds)[1], 100), ] iris2 <- iris iris2$alphaLevel <- c("setosa" = 0.2, "versicolor" = 0.3, "virginica" = 0)[iris2$Species] test_that("stops", { # basic parallel coordinate plot, using default settings # ggparcoord(data = diamonds.samp, columns = c(1, 5:10)) # this time, color by diamond cut expect_error( ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = NULL, order = "anyClass"), "can't use the 'order' methods " ) expect_error( ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = NULL, order = "allClass"), "can't use the 'order' methods " ) expect_error( ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = c(1, 2)), "invalid value for 'groupColumn'" ) expect_error( ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 1i), "invalid value for 'groupColumn'" ) expect_error( ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, scale = "notValid"), "invalid value for 'scale'" ) expect_error( ggparcoord( data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, centerObsID = nrow(diamonds.samp) + 10 ), "invalid value for 'centerObsID'" ) expect_error( ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, missing = "notValid"), "invalid value for 'missing'" ) expect_error( ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, order = "notValid"), "invalid value for 'order'" ) expect_error( ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, order = 1i), "invalid value for 'order'" ) expect_error( ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, showPoints = 1), "invalid value for 'showPoints'" ) expect_error( ggparcoord( data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, alphaLines = "notAColumn" ), "'alphaLines' column is missing in data" ) tmpDt <- diamonds.samp tmpDt$price[1] <- NA range(tmpDt$price) expect_error( ggparcoord( data = tmpDt, columns = c(1, 5:10), groupColumn = 2, alphaLines = "price" ), "missing data in 'alphaLines' column" ) expect_error( ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, alphaLines = "price"), "invalid value for 'alphaLines' column; max range " ) expect_error( ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, alphaLines = -0.1), "invalid value for 'alphaLines'; must be a scalar value" ) expect_error( ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, alphaLines = 1.1), "invalid value for 'alphaLines'; must be a scalar value" ) expect_error( ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, boxplot = 1), "invalid value for 'boxplot'" ) expect_error( ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, shadeBox = c(1, 2)), "invalid value for 'shadeBox'; must be a single color" ) expect_error( ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, shadeBox = "notacolor"), "invalid value for 'shadeBox'; must be a valid R color" ) expect_error( ggparcoord(diamonds.samp, columns = c(1, 5:10), groupColumn = 2, splineFactor = NULL), "invalid value for 'splineFactor'" ) }) test_that("alphaLines", { p <- ggparcoord( data = iris2, columns = 1:4, groupColumn = 5, order = "anyClass", showPoints = TRUE, title = "Parallel Coordinate Plot for the Iris Data", alphaLines = "alphaLevel" ) expect_equal(length(p$layers), 2) expect_equivalent(mapping_string(get("mapping", envir = p$layers[[1]])$alpha), "alphaLevel") }) test_that("splineFactor", { ## Use splines on values, rather than lines (all produce the same result) columns <- c(1, 5:10) p1 <- ggparcoord(diamonds.samp, columns, groupColumn = 2, splineFactor = TRUE) p2 <- ggparcoord(diamonds.samp, columns, groupColumn = 2, splineFactor = 3) splineFactor <- length(columns) * 3 p3 <- ggparcoord(diamonds.samp, columns, groupColumn = 2, splineFactor = I(splineFactor)) pList <- list(p1, p2, p3) for (p in pList) { expect_equivalent(mapping_string(get("mapping", envir = p$layers[[1]])$x), "spline.x") expect_equivalent(mapping_string(get("mapping", envir = p$layers[[1]])$y), "spline.y") tmp <- unique(as.numeric(get("data", envir = p$layers[[1]])$ggally_splineFactor)) expect_true( (tmp == 3) || (tmp == 21) ) } p <- ggparcoord( data = iris2, columns = 1:4, groupColumn = 5, splineFactor = 3, alphaLines = "alphaLevel" ) expect_equal(mapping_string(get("mapping", p$layers[[1]])$alpha), "alphaLevel") p <- ggparcoord( data = iris2, columns = 1:4, groupColumn = 5, splineFactor = 3, showPoints = TRUE ) expect_equal(length(p$layers), 2) expect_equal(mapping_string(get("mapping", p$layers[[1]])$x), "spline.x") expect_equal(mapping_string(get("mapping", p$layers[[2]])$y), "value") }) test_that("groupColumn", { ds2 <- diamonds.samp ds2$color <- mapping_string(ds2$color) # column 3 has a character # column 4 has a factor p <- ggparcoord(data = ds2, columns = c(1, 3:10), groupColumn = 2) expect_true("color" %in% levels(p$data$variable)) expect_true("clarity" %in% levels(p$data$variable)) expect_true(is.numeric(p$data$value)) expect_equal(mapping_string(p$mapping$colour), colnames(ds2)[2]) p <- ggparcoord( data = ds2, columns = c( "carat", "color", "clarity", "depth", "table", "price", "x", "y", "z" ), order = c(1, 3:10), groupColumn = "cut" ) expect_true("color" %in% levels(p$data$variable)) expect_true("clarity" %in% levels(p$data$variable)) expect_true(is.numeric(p$data$value)) expect_equal(levels(p$data$cut), levels(ds2$cut)) # group column is a regular column ## factor p <- ggparcoord(data = ds2, columns = c(1, 3:10), groupColumn = 4) expect_true("clarity" %in% levels(p$data$variable)) ## character p <- ggparcoord(data = ds2, columns = c(1, 3:10), groupColumn = 3) expect_true("color" %in% levels(p$data$variable)) ## numeric p <- ggparcoord(data = ds2, columns = c(1, 3:10), groupColumn = 1) expect_true("carat" %in% levels(p$data$variable)) }) test_that("scale", { for (scale in c("std", "robust", "uniminmax", "globalminmax", "center", "centerObs")) { p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, scale = scale) } expect_true(TRUE) }) test_that("missing", { ds2 <- diamonds.samp ds2[3, 1] <- NA for (missing in c("exclude", "mean", "median", "min10", "random")) { p <- ggparcoord(data = ds2, columns = c(1, 5:10), groupColumn = 2, missing = missing) } expect_true(TRUE) }) test_that("order", { if (requireNamespace("scagnostics", quietly = TRUE)) { for (ordering in c("Outlying", "Skewed", "Clumpy", "Sparse", "Striated", "Convex", "Skinny", "Stringy", "Monotonic")) { p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, order = ordering) expect_true(all(levels(p$data) != c("carat", "depth", "table", "price", "x", "y", "z"))) } } for (ordering in c("skewness", "allClass", "anyClass")) { p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, order = ordering) expect_true(all(levels(p$data) != c("carat", "depth", "table", "price", "x", "y", "z"))) } }) test_that("basic", { # no color supplied p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10)) expect_true(is.null(p$mapping$colour)) # color supplied p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2) expect_false(is.null(p$mapping$colour)) # title supplied ttl <- "Parallel Coord. Plot of Diamonds Data" p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10), title = ttl) expect_equal(p$labels$title, ttl) col <- "blue" p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10), shadeBox = col) expect_equal(length(p$layers), 2) expect_equal(get("aes_params", envir = p$layers[[1]])$colour, col) p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10), mapping = ggplot2::aes(size = 1)) expect_equal(length(p$layers), 1) expect_equal(p$mapping$size, 1) }) test_that("size", { p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10), mapping = ggplot2::aes(size = gear)) expect_equal(mapping_string(p$mapping$size), "gear") p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10)) + ggplot2::aes(size = gear) expect_equal(mapping_string(p$mapping$size), "gear") }) test_that("columns containing only a single value do not cause an scaling error", { df <- data.frame(obs = 1:5, var1 = sample(10, 5), var2 = rep(3, 5)) # no scaling expect_silent(ggparcoord(data = df, columns = 1:3, scale = "globalminmax")) # requires scaling, must not throw an errror due to scaling the single values (to NaN) expect_silent(ggparcoord(data = df, columns = 1:3, scale = "uniminmax")) df2 <- data.frame(df, var3 = factor(c("a", "b", "c", "a", "c"))) # requires scaling, must not throw an errror due to scaling the single values (to NaN) expect_silent(ggparcoord(data = df2, columns = 1:4, scale = "uniminmax")) df3 <- data.frame(df2, var4 = factor(c("d", "d", "d", "d", "d"))) expect_silent(ggparcoord(data = df3, columns = 1:4, scale = "uniminmax")) expect_silent(ggparcoord(data = df3, columns = 1:4, scale = "robust")) expect_silent(ggparcoord(data = df3, columns = 1:4, scale = "std")) }) GGally/tests/testthat/test-gglegend.R0000644000176200001440000000453313663637143017330 0ustar liggesusers context("gglegend") expect_print <- function(p, ...) { testthat::expect_silent(print(p)) } test_that("examples", { library(ggplot2) histPlot <- ggplot(diamonds, aes(price, fill = cut)) + geom_histogram(binwidth = 500) (right <- histPlot) (bottom <- histPlot + theme(legend.position = "bottom")) (top <- histPlot + theme(legend.position = "top")) (left <- histPlot + theme(legend.position = "left")) expect_legend <- function(p) { plotLegend <- grab_legend(p) expect_true(inherits(plotLegend, "gtable")) expect_true(inherits(plotLegend, "gTree")) expect_true(inherits(plotLegend, "grob")) expect_print(plotLegend) } expect_legend(right) expect_legend(bottom) expect_legend(top) expect_legend(left) }) test_that("legend", { # display regular plot expect_print( ggally_points(iris, ggplot2::aes(Sepal.Length, Sepal.Width, color = Species)) ) # Make a function that will only print the legend points_legend <- gglegend(ggally_points) expect_print(points_legend( iris, ggplot2::aes(Sepal.Length, Sepal.Width, color = Species) )) # produce the sample legend plot, but supply a string that 'wrap' understands same_points_legend <- gglegend("points") expect_identical( attr(attr(points_legend, "fn"), "original_fn"), attr(attr(same_points_legend, "fn"), "original_fn") ) # Complicated examples custom_legend <- wrap(gglegend("points"), size = 6) p <- custom_legend( iris, ggplot2::aes(Sepal.Length, Sepal.Width, color = Species) ) expect_print(p) expect_true(inherits(p, "gtable")) expect_true(inherits(p, "gTree")) expect_true(inherits(p, "grob")) # Use within ggpairs expect_silent({ pm <- ggpairs( iris, 1:2, mapping = ggplot2::aes(color = Species), upper = list(continuous = gglegend("points")) ) print(pm) }) # Use within ggpairs expect_silent({ pm <- ggpairs( iris, 1:2, mapping = ggplot2::aes(color = Species) ) pm[1, 2] <- points_legend(iris, ggplot2::aes(Sepal.Width, Sepal.Length, color = Species)) print(pm) }) }) test_that("plotNew", { points_legend <- gglegend(ggally_points) expect_print(points_legend( iris, ggplot2::aes(Sepal.Length, Sepal.Width, color = Species) )) expect_print(points_legend( iris, ggplot2::aes(Sepal.Length, Sepal.Width, color = Species) ), plotNew = TRUE) }) GGally/tests/testthat/test-utils.R0000644000176200001440000000145513777103031016702 0ustar liggesusers # context("utils") # test_that("require_namespaces", { # skip("This test is too error prone") # if ("Hmisc" %in% loadedNamespaces()) unloadNamespace("Hmisc") # #NB: survival is required by Hmisc, so Hmisc must be unloaded before # if ("multcomp" %in% loadedNamespaces()) unloadNamespace("multcomp") # #NB: survival is required by multcomp, so multcomp must be unloaded before # if ("survival" %in% loadedNamespaces()) unloadNamespace("survival") # expect_false("package:survival" %in% search()) # suppressMessages(require_namespaces(c("survival"))) # expect_false("package:survival" %in% search()) # expect_false(is.null(getNamespace("survival"))) # expect_error( # suppressWarnings(suppressMessages( # require_namespaces("DOES_NOT_EXIST_qweqweqweqwe") # )) # ) # }) GGally/tests/testthat/test-crosstalk.R0000644000176200001440000000210213663637143017547 0ustar liggesusers context("crosstalk") test_that("crosstalk works with ggduo and ggpairs", { skip_if_not_installed("crosstalk") sd <- try(crosstalk::SharedData$new(iris[1:4]), silent = TRUE) if (inherits(sd, "try-error")) { skip("crosstalk data can not be initialized") } expect_silent({ pm <- ggpairs(sd) }) expect_error({ pm <- ggpairs(sd, 3:5) }, "Make sure your numeric" ) expect_error({ pm <- ggpairs(sd, c("Petal.Length", "Petal.Width", crosstalk_key())) }, "Columns in 'columns' not" ) expect_silent({ pm <- ggduo(sd) }) expect_error({ pm <- ggduo(sd, c(1:2, 5), 3:5) }, "Make sure your numeric 'columnsX'" ) expect_error({ pm <- ggduo( sd, c("Sepal.Length", "Sepal.Width", crosstalk_key()), c("Petal.Length", "Petal.Width") ) }, "Columns in 'columnsX' not" ) expect_error({ pm <- ggduo( sd, c("Sepal.Length", "Sepal.Width"), c("Petal.Length", "Petal.Width", crosstalk_key()) ) }, "Columns in 'columnsY' not" ) }) GGally/tests/testthat/test-ggnostic.R0000644000176200001440000000473513663637143017375 0ustar liggesuserscontext("ggnostic") expect_print <- function(p) { testthat::expect_silent({ print(p) }) } test_that("fn_switch", { fn1 <- function(data, mapping, ...) { return(1) } fn2 <- function(data, mapping, ...) { return(2) } fn3 <- function(data, mapping, ...) { return(3) } fn5 <- function(data, mapping, ...) { return(5) } fn <- fn_switch(list(A = fn1, B = fn2, C = fn3), "value") dummy_dt <- data.frame(A = rnorm(100), B = rnorm(100), C = rnorm(100)) chars <- c("A", "B", "C") for (i in 1:3) { mapping <- ggplot2::aes_string(value = chars[i]) expect_equal(fn(dummy_dt, mapping), i) } fn <- fn_switch(list(A = fn1, default = fn5), "value") expect_equal(fn(dummy_dt, ggplot2::aes_string(value = "A")), 1) expect_equal(fn(dummy_dt, ggplot2::aes_string(value = "B")), 5) expect_equal(fn(dummy_dt, ggplot2::aes_string(value = "C")), 5) fn <- fn_switch(list(A = fn1), "value") expect_equal(fn(dummy_dt, ggplot2::aes_string(value = "A")), 1) expect_error(fn(dummy_dt, ggplot2::aes_string(value = "B")), "function could not be found") }) test_that("model_beta_label", { mod <- lm(mpg ~ wt + qsec + am, mtcars) expect_equal(model_beta_label(mod), c("wt***", "qsec***", "am*")) expect_equal(model_beta_label(mod, lmStars = FALSE), c("wt", "qsec", "am")) }) test_that("ggnostic mtcars", { mtc <- mtcars; mtc$am <- c("0" = "automatic", "1" = "manual")[as.character(mtc$am)]; mod <- lm(mpg ~ wt + qsec + am, data = mtc); continuous_type <- list( .resid = wrap(ggally_nostic_resid, method = "loess"), .std.resid = wrap(ggally_nostic_std_resid, method = "loess") ) pm <- ggnostic( mod, mapping = ggplot2::aes(), columnsY = c("mpg", ".fitted", ".se.fit", ".resid", ".std.resid", ".sigma", ".hat", ".cooksd"), continuous = continuous_type, progress = FALSE ) expect_print(pm) pm <- ggnostic( mod, mapping = ggplot2::aes(color = am), legend = c(1, 3), continuous = continuous_type, progress = FALSE ) expect_print(pm) }) test_that("error checking", { get_cols <- function(cols) { match_nostic_columns( cols, c("mpg", broom_columns()), "columnsY" ) } expect_equivalent( get_cols(c(".resid", ".sig", ".hat", ".c")), c(".resid", ".sigma", ".hat", ".cooksd") ) expect_error( get_cols(c( "not_there", ".fitted", ".se.fit", ".resid", ".std.resid", ".sigma", ".hat", ".cooksd" )), "Could not match 'columnsY'" ) }) GGally/tests/testthat/test-deprecated.R0000644000176200001440000000026513663637143017652 0ustar liggesusers context("deprecated") data(tips, package = "reshape") test_that("ggally-cor", { expect_silent( p <- ggally_cor_v1_5(tips, ggplot2::aes_string("total_bill", "tip")) ) }) GGally/tests/testthat/test-ggsurv.R0000644000176200001440000001162413663637143017070 0ustar liggesusers context("ggsurv") suppressMessages(require(survival)) suppressMessages(require(scales)) data(lung, package = "survival") data(kidney, package = "survival") sf.lung <- survival::survfit(Surv(time, status) ~ 1, data = lung) sf.kid <- survival::survfit(Surv(time, status) ~ disease, data = kidney) expect_print <- function(x) { testthat::expect_silent(print(x)) } test_that("single", { a <- ggsurv(sf.lung) expect_equivalent(mapping_string(a$mapping$x), "time") expect_equivalent(mapping_string(a$mapping$y), "surv") expect_true(is.null(a$labels$group)) expect_true(is.null(a$labels$colour)) expect_true(is.null(a$labels$linetype)) }) test_that("multiple", { a <- ggsurv(sf.kid) expect_equivalent(mapping_string(a$mapping$x), "time") expect_equivalent(mapping_string(a$mapping$y), "surv") expect_true(!is.null(a$labels$group)) expect_true(!is.null(a$labels$colour)) expect_true(!is.null(a$labels$linetype)) }) test_that("adjust plot", { a <- ggsurv(sf.kid, plot.cens = FALSE) expect_equivalent(length(a$layers), 1) a <- ggsurv(sf.kid, plot.cens = TRUE) expect_equivalent(length(a$layers), 2) }) test_that("stops", { noCensor <- subset(lung, status == 1) lungNoCensor <- survival::survfit(Surv(time, status) ~ 1, data = noCensor) # check that the surv.col and lty.est are of the correct length expect_error(ggsurv(lungNoCensor, surv.col = c("black", "red"))) expect_error(ggsurv(lungNoCensor, lty.est = 1:2)) # must have censor to plot expect_error(ggsurv(lungNoCensor, plot.cens = TRUE)) noCensor <- subset(kidney, status == 1) kidneyNoCensor <- survival::survfit(Surv(time, status) ~ disease, data = noCensor) # check that the surv.col and lty.est are of the correct length. should be 4 expect_error(ggsurv(kidneyNoCensor, surv.col = c("black", "red", "blue"))) expect_error(ggsurv(kidneyNoCensor, lty.est = 1:3)) # must have censor to plot expect_error(ggsurv(kidneyNoCensor, plot.cens = TRUE)) # must have censor to plot expect_silent( ggsurv(sf.kid, CI = TRUE, surv.col = c("black", "red", "blue", "green")) ) expect_silent( ggsurv(sf.kid, CI = TRUE, lty.est = 1:4) ) ggsurv(sf.kid, CI = TRUE, surv.col = "red") }) test_that("back.white", { sf.lung <- survival::survfit(Surv(time, status) ~ 1, data = lung) sf.kid <- survival::survfit(Surv(time, status) ~ disease, data = kidney) a <- ggsurv(sf.lung, back.white = FALSE) expect_true(length(a$theme) == 0) a <- ggsurv(sf.lung, back.white = TRUE) expect_true(length(a$theme) != 0) a <- ggsurv(sf.kid, back.white = FALSE) expect_true(length(a$theme) == 0) a <- ggsurv(sf.kid, back.white = TRUE) expect_true(length(a$theme) != 0) }) test_that("surv.col", { ggsurv(sf.lung, surv.col = "red") ggsurv(sf.kid, surv.col = "red") ggsurv(sf.kid, surv.col = c("black", "red", "blue", "green")) ggsurv(sf.kid, lty.est = 1) ggsurv(sf.kid, lty.est = 1:4) expect_true("idk how to test it happened" != "fail") }) test_that("CI", { a <- ggsurv(sf.lung, CI = FALSE) b <- ggsurv(sf.lung, CI = TRUE) expect_equivalent(length(b$layers) - length(a$layers), 2) a <- ggsurv(sf.kid, CI = FALSE) b <- ggsurv(sf.kid, CI = TRUE) expect_equivalent(length(b$layers) - length(a$layers), 2) }) test_that("multiple colors", { expect_print(ggsurv(sf.kid, plot.cens = TRUE)) expect_warning({ ggsurv(sf.kid, plot.cens = TRUE, cens.col = c("red", "blue")) }, "Color scales for censored points") # nolint expect_silent({ print( ggsurv(sf.kid, plot.cens = TRUE, cens.col = "blue") ) }) cusotm_color <- c("green", "blue", "purple", "orange") expect_silent({ print( ggsurv(sf.kid, plot.cens = TRUE, cens.col = cusotm_color) ) }) expect_warning({ ggsurv( sf.kid, plot.cens = TRUE, cens.col = cusotm_color, cens.shape = c(1, 2) ) }, "The length of the censored shapes") # nolint expect_silent({ print( ggsurv( sf.kid, plot.cens = TRUE, cens.col = cusotm_color, cens.shape = c(1, 2, 3, 4) ) ) }) }) test_that("cens.size", { a <- ggsurv(sf.lung) b <- ggsurv(sf.lung, cens.size = 5) expect_true(a$layers[[4]]$aes_params$size == 2) expect_true(b$layers[[4]]$aes_params$size != 2) a <- ggsurv(sf.kid) b <- ggsurv(sf.lung, cens.size = 5) expect_true(a$layers[[2]]$aes_params$size == 2) expect_true(b$layers[[2]]$aes_params$size != 2) }) # 881 R/ggsurv.r 231 231 0 # 883 R/ggsurv.r 242 242 0 # 884 R/ggsurv.r 247 249 0 # 885 R/ggsurv.r 248 248 0 # 886 R/ggsurv.r 251 255 0 # 887 R/ggsurv.r 252 252 0 # 888 R/ggsurv.r 254 254 0 # 889 R/ggsurv.r 256 258 0 # 890 R/ggsurv.r 263 263 0 # 891 R/ggsurv.r 274 274 0 GGally/tests/testthat/test-ggbivariate.R0000644000176200001440000000232513761572054020033 0ustar liggesuserscontext("ggbivariate") test_that("example", { expect_print <- function(x) { expect_silent(print(x)) } skip_if_not_installed("reshape") data(tips, package = "reshape") expect_print(ggbivariate(tips, "smoker", c("day", "time", "sex", "tip"))) # Personalize plot title and legend title expect_print(ggbivariate( tips, "smoker", c("day", "time", "sex", "tip"), title = "Custom title" ) + labs(fill = "Smoker ?")) # Customize fill colour scale expect_print(ggbivariate(tips, "smoker", c("day", "time", "sex", "tip")) + scale_fill_brewer(type = "qual")) # Customize labels expect_print(ggbivariate( tips, "smoker", c("day", "time", "sex", "tip"), rowbar_args = list( colour = "white", size = 4, fontface = "bold", label_format = scales::label_percent(accurary = 1) ) )) # Choose the sub-plot from which get legend expect_print(ggbivariate(tips, "smoker")) expect_print(ggbivariate(tips, "smoker", legend = 3)) # Use mapping to indicate weights d <- as.data.frame(Titanic) expect_print(ggbivariate(d, "Survived", mapping = aes(weight = Freq))) # outcome can be numerical expect_print(ggbivariate(tips, outcome = "tip", title = "tip")) }) GGally/tests/testthat/test-vig_ggally.R0000644000176200001440000000051513666472400017670 0ustar liggesuserstest_that("all vignetts are accounted for", { testthat::skip_on_cran() # make sure vig dir exists vig_dir <- file.path("..", "..", "vignettes") testthat::skip_if_not(dir.exists(vig_dir)) vigs <- dir(vig_dir, pattern = "\\.Rmd$") vigs <- sub(".Rmd", "", vigs, fixed = TRUE) expect_equal(vignettes_for_ggally, vigs) }) GGally/tests/testthat/test-ggnetworkmap.R0000644000176200001440000001706114063460077020254 0ustar liggesusers context("ggnetworkmap") if ("package:igraph" %in% search()) { detach("package:igraph") } rq <- function(...) { require(..., quietly = TRUE) } skip_if(!rq(network)) skip_if(!rq(sna)) skip_if(!rq(maps)) skip_if(!rq(ggplot2)) skip_if(!rq(intergraph)) # test igraph conversion skip_if_not_installed("geosphere") # first 500 rows of http://datasets.flowingdata.com/tuts/maparcs/airports.csv # avoids downloading the dataset to test the package airports <- read.csv(test_path("data/airports.csv"), header = TRUE) rownames(airports) <- airports$iata # select some random flights set.seed(123) flights <- data.frame( origin = sample(airports[200:400, ]$iata, 200, replace = TRUE), destination = sample(airports[200:400, ]$iata, 200, replace = TRUE) ) # convert to network flights <- network(flights, directed = TRUE) # add geographic coordinates flights %v% "lat" <- airports[ network.vertex.names(flights), "lat" ] # nolint flights %v% "lon" <- airports[ network.vertex.names(flights), "long" ] # nolint # drop isolated airports delete.vertices(flights, which(degree(flights) < 2)) # compute degree centrality flights %v% "degree" <- degree(flights, gmode = "digraph") # add random groups flights %v% "mygroup" <- sample(letters[1:4], network.size(flights), replace = TRUE) # create a map of the USA usa <- ggplot(map_data("usa"), aes(x = long, y = lat)) + geom_polygon(aes(group = group), color = "grey65", fill = "#f9f9f9", size = 0.2) test_that("basic drawing", { # no map p <- ggnetworkmap(net = flights, size = 2) expect_true(is.null(nrow(p$data))) # overlay network data to map p <- ggnetworkmap(usa, flights, size = 2) expect_false(is.null(nrow(p$data))) }) test_that("great circles", { p <- ggnetworkmap(usa, flights, size = 2, great.circles = TRUE) expect_equal(length(p$layers), 3) expect_equal(get("aes_params", envir = p$layers[[3]])$colour, "black") }) test_that("node groups", { p <- ggnetworkmap(usa, flights, size = 2, great.circles = TRUE, node.group = degree) expect_equal(length(p$layers), 3) expect_true(is.null(get("aes_params", envir = p$layers[[3]])$colour)) expect_equal(mapping_string(get("mapping", envir = p$layers[[3]])$colour), ".ngroup") p <- ggnetworkmap(usa, flights, size = 2, great.circles = TRUE, node.color = "red") expect_equal(mapping_string(get("aes_params", envir = p$layers[[3]])$colour), "\"red\"") }) test_that("ring groups", { p <- ggnetworkmap(usa, flights, size = 2, great.circles = TRUE, node.group = degree, ring.group = mygroup) expect_equal(length(p$layers), 3) expect_true(is.null(get("aes_params", envir = p$layers[[3]])$colour)) expect_equal(mapping_string(get("mapping", envir = p$layers[[3]])$colour), ".rgroup") expect_equal(mapping_string(get("mapping", envir = p$layers[[3]])$fill), ".ngroup") }) test_that("segment color", { p <- ggnetworkmap(usa, flights, size = 2, great.circles = TRUE, node.group = degree, ring.group = mygroup, segment.color = "cornflowerblue" ) expect_equal(length(p$layers), 3) expect_true(is.null(get("aes_params", envir = p$layers[[3]])$colour)) expect_equal(mapping_string(get("mapping", envir = p$layers[[3]])$colour), ".rgroup") expect_equal(mapping_string(get("mapping", envir = p$layers[[3]])$fill), ".ngroup") expect_equal( mapping_string(get("aes_params", envir = p$layers[[2]])$colour), "\"cornflowerblue\"" ) }) test_that("weight", { p <- ggnetworkmap(usa, flights, size = 2, great.circles = TRUE, node.group = degree, ring.group = mygroup, segment.color = "cornflowerblue", weight = degree ) expect_equal(length(p$layers), 3) expect_true(is.null(get("aes_params", envir = p$layers[[3]])$colour)) expect_equal(mapping_string(get("mapping", envir = p$layers[[3]])$colour), ".rgroup") expect_equal(mapping_string(get("mapping", envir = p$layers[[3]])$fill), ".ngroup") expect_equal( mapping_string(get("aes_params", envir = p$layers[[2]])$colour), "\"cornflowerblue\"" ) expect_equal(mapping_string(get("mapping", envir = p$layers[[3]])$size), ".weight") }) test_that("labels", { p <- ggnetworkmap(usa, flights, size = 2, great.circles = TRUE, node.group = degree, ring.group = mygroup, segment.color = "cornflowerblue", weight = degree, label.nodes = TRUE) expect_equal(length(p$layers), 4) expect_true(is.null(get("aes_params", envir = p$layers[[3]])$colour)) expect_equal(mapping_string(get("mapping", envir = p$layers[[3]])$colour), ".rgroup") expect_equal(mapping_string(get("mapping", envir = p$layers[[3]])$fill), ".ngroup") expect_equal( mapping_string(get("aes_params", envir = p$layers[[2]])$colour), "\"cornflowerblue\"" ) expect_equal(mapping_string(get("mapping", envir = p$layers[[3]])$size), ".weight") expect_equal(mapping_string(get("mapping", envir = p$layers[[4]])$label), ".label") expect_true(is.null(get("aes_params", envir = p$layers[[2]])$arrow)) }) test_that("arrows", { p <- ggnetworkmap(usa, flights, size = 2, great.circles = TRUE, node.group = degree, ring.group = mygroup, segment.color = "cornflowerblue", weight = degree, label.nodes = TRUE, arrow.size = 0.2) expect_equal(length(p$layers), 4) expect_true(is.null(get("aes_params", envir = p$layers[[3]])$colour)) expect_equal(mapping_string(get("mapping", envir = p$layers[[3]])$colour), ".rgroup") expect_equal(mapping_string(get("mapping", envir = p$layers[[3]])$fill), ".ngroup") expect_equal( mapping_string(get("aes_params", envir = p$layers[[2]])$colour), "\"cornflowerblue\"" ) expect_equal(mapping_string(get("mapping", envir = p$layers[[3]])$size), ".weight") expect_equal(mapping_string(get("mapping", envir = p$layers[[4]])$label), ".label") # look at geom_params for arrow info expect_true(is.list(get("geom_params", envir = p$layers[[2]])$arrow)) }) test_that("labels", { expect_error(ggnetworkmap(usa, flights, label.nodes = c("A", "B"))) testLabels <- paste("L", 1:network.size(flights), sep = "") # does logical check p <- ggnetworkmap(usa, flights, label.nodes = testLabels) ## PROBLEM HERE: why would vertex.names be equal to testLabels? ## expect_equal(get("data", p$layers[[4]])$.label, testLabels) # does vertex.names check p <- ggnetworkmap(usa, flights, label.nodes = TRUE) expect_true(!is.null(get("data", p$layers[[4]])$.label)) # does id check flights2 <- flights flights2 %v% "id" <- testLabels p <- ggnetworkmap(usa, flights2, label.nodes = TRUE) expect_true(!is.null(get("data", p$layers[[4]])$.label)) }) ### --- test arrow.size test_that("arrow.size", { expect_error(ggnetworkmap(net = flights, arrow.size = -1), "incorrect arrow.size") expect_warning(ggnetworkmap(net = network(as.matrix(flights), directed = FALSE), arrow.size = 1), "arrow.size ignored") }) ### --- test network coercion test_that("network coercion", { expect_warning( ggnetworkmap(net = network(matrix(1, nrow = 2, ncol = 2), loops = TRUE)), "self-loops" ) expect_error(ggnetworkmap(net = 1:2), "network object") expect_error(ggnetworkmap(net = network(data.frame(1:2, 3:4), hyper = TRUE)), "hyper") expect_error( ggnetworkmap(net = network(data.frame(1:2, 3:4), multiple = TRUE)), "multiplex graphs" ) }) ### --- test igraph functionality test_that("igraph conversion", { if (requireNamespace("igraph", quietly = TRUE)) { library(igraph) n <- asIgraph(flights) p <- ggnetworkmap(net = n) expect_equal(length(p$layers), 2) } }) expect_true(TRUE) GGally/tests/testthat/test-ggcorr.R0000644000176200001440000000476413663637143017045 0ustar liggesusers context("ggcorr") # nba <- read.csv("http://datasets.flowingdata.com/ppg2008.csv") data(flea) test_that("limits", { print(ggcorr(flea[, -1])) print(ggcorr(flea[, -1], limits = TRUE)) print(ggcorr(flea[, -1], limits = FALSE)) print(ggcorr(flea[, -1], limits = NULL)) print(ggcorr(flea[, -1], limits = c(-5, 5))) print(ggcorr(flea[, -1], limits = c(-0.5, 0.5))) expect_true(TRUE) }) test_that("examples", { # Default output. p <- ggcorr(flea[, -1]) expect_equal(length(p$layers), 2) # Labelled output, with coefficient transparency. p <- ggcorr(flea[, -1], label = TRUE, label_alpha = TRUE, name = "") expect_equal(length(p$layers), 3) # Custom options. p <- ggcorr( flea[, -1], geom = "circle", max_size = 6, size = 3, hjust = 0.75, nbreaks = 6, angle = -45, palette = "PuOr" # colorblind safe, photocopy-able ) expect_equal(length(p$layers), 3) p <- ggcorr(flea[, -1], label = TRUE, name = "") expect_equal(length(p$layers), 3) # test other combinations of geoms + color scales ggcorr(flea[, -1], nbreaks = 4, palette = "PuOr") ggcorr(flea[, -1], nbreaks = 4, geom = "circle") ggcorr(flea[, -1], geom = "text") ggcorr(flea[, -1], geom = "text", limits = FALSE) ggcorr(flea[, -1], nbreaks = 4, geom = "text") ggcorr(flea[, -1], nbreaks = 4, palette = "PuOr", geom = "text") ggcorr(flea[, -1], label = TRUE, label_alpha = 0.5) }) test_that("non-numeric data", { expect_warning(ggcorr(flea), "not numeric") }) test_that("null midpoint", { expect_message(ggcorr(flea[, -1], midpoint = NULL), "Color gradient") }) test_that("further options", { ggcorr(flea[, -1], geom = "circle") ggcorr(flea[, -1], geom = "circle", limits = FALSE) ggcorr(flea[, -1], geom = "tile", nbreaks = 3) ggcorr(flea[, -1], geom = "tile", limits = FALSE) expect_error(ggcorr(flea[, -1], layout.exp = "a"), "incorrect layout.exp") expect_silent({ ggcorr(flea[, -1], layout.exp = 1) }) }) test_that("data.matrix", { p <- ggcorr(data.matrix(flea[, -1])) expect_equal(length(p$layers), 2) }) test_that("cor_matrix", { p <- ggcorr(data = NULL, cor_matrix = cor(flea[, -1], use = "pairwise")) expect_equal(length(p$layers), 2) }) test_that("other geoms", { expect_error(ggcorr(flea[, -1], geom = "hexbin"), "incorrect geom") expect_silent({ ggcorr(flea[, -1], geom = "blank") }) }) test_that("backwards compatibility", { expect_silent({ ggcorr(flea[, -1], method = "everything") }) }) GGally/tests/testthat/test-ggscatmat.R0000644000176200001440000000165413663637143017527 0ustar liggesusers context("ggscatmat") data(flea) test_that("example", { flea2 <- flea flea2$species2 <- as.character(flea2$species) expect_warning(p <- ggscatmat(flea2, c(1:3)), "Factor variables are omitted in plot") expect_warning(p <- ggscatmat(flea2, c(2:3, 8)), "Factor variables are omitted in plot") expect_true(is.null(p$labels$colour)) # print(p) p <- ggscatmat(flea, columns = 2:4, color = "species") expect_true(!is.null(p$labels$colour)) # print(p) }) test_that("corMethod", { expect_silent({ p <- ggscatmat(flea, columns = 2:3, corMethod = "pearson") p <- ggscatmat(flea, columns = 2:3, corMethod = "rsquare") }) }) test_that("stops", { expect_error(ggscatmat(flea, columns = c(1, 2)), "Not enough numeric variables to") expect_error(ggscatmat(flea, columns = c(1, 1, 1)), "All of your variables are factors") expect_error(scatmat(flea, columns = c(1, 1, 1)), "All of your variables are factors") }) GGally/tests/testthat/test-ggmatrix_location.R0000644000176200001440000001007613666055642021266 0ustar liggesusers expect_loc_grid <- function(loc, to_loc) { testthat::expect_equal( colnames(loc), colnames(to_loc) ) testthat::expect_equal( nrow(loc), nrow(to_loc) ) loc <- loc[order(loc$row, loc$col), ] to_loc <- to_loc[order(to_loc$row, to_loc$col), ] testthat::expect_equal( loc$row, to_loc$row ) testthat::expect_equal( loc$col, to_loc$col ) } expect_rows_cols <- function(loc, rows, cols) { to_loc <- expand.grid(row = rows, col = cols) expect_loc_grid(loc, to_loc) } test_that("rows work", { pm <- ggpairs(reshape::tips) expect_rows_cols( ggmatrix_location(pm, rows = c(3,5)), rows = c(3,5), cols = 1:7 ) expect_rows_cols( ggmatrix_location(pm, rows = 1), rows = 1, cols = 1:7 ) expect_error( ggmatrix_location(pm, rows = TRUE), "numeric" ) expect_error( ggmatrix_location(pm, rows = "1"), "numeric" ) }) test_that("cols work", { pm <- ggpairs(reshape::tips) expect_rows_cols( ggmatrix_location(pm, cols = c(3,5)), rows = 1:7, cols = c(3,5) ) expect_rows_cols( ggmatrix_location(pm, cols = 1), rows = 1:7, cols = 1 ) expect_error( ggmatrix_location(pm, cols = TRUE), "numeric" ) expect_error( ggmatrix_location(pm, cols = "1"), "numeric" ) }) test_that("location logical", { pm <- ggpairs(reshape::tips) expect_loc_grid( ggmatrix_location(pm, location = TRUE), expand.grid(row = 1:7, col = 1:7) ) expect_warning( ggmatrix_location(pm, location = FALSE) ) }) test_that("location character", { pm <- ggpairs(reshape::tips) to_loc <- expand.grid(row = 1:7, col = 1:7) expect_loc_grid( ggmatrix_location(pm, location = "all"), to_loc ) expect_loc_grid( ggmatrix_location(pm, location = "none"), subset(to_loc, FALSE) ) expect_loc_grid( ggmatrix_location(pm, location = "upper"), subset(to_loc, col > row) ) expect_loc_grid( ggmatrix_location(pm, location = "lower"), subset(to_loc, col < row) ) expect_loc_grid( ggmatrix_location(pm, location = "diag"), subset(to_loc, col == row) ) expect_error( ggmatrix_location(pm, location = "unknown") ) }) test_that("location matrix", { pm <- ggpairs(reshape::tips) to_loc <- subset(expand.grid(row = 1:7, col = 1:7), row %in% c(3,5) | col %in% c(3,5)) mat <- matrix(FALSE, nrow = 7, ncol = 7, byrow = TRUE) mat[,c(3,5)] <- TRUE mat[c(3,5), ] <- TRUE expect_loc_grid( ggmatrix_location(pm, location = mat), to_loc ) expect_loc_grid( ggmatrix_location(pm, location = as.data.frame(mat)), to_loc ) mat2 <- mat mat2[TRUE] <- FALSE expect_loc_grid( ggmatrix_location(pm, location = mat2), subset(to_loc, FALSE) ) expect_error( ggmatrix_location(pm, location = mat[, 1:6]) ) expect_error( ggmatrix_location(pm, location = mat[1:6, ]) ) expect_error( ggmatrix_location(pm, location = cbind(mat, 1)) ) expect_error( ggmatrix_location(pm, location = rbind(mat, 1)) ) }) test_that("location matrix", { pm <- ggpairs(reshape::tips) to_loc <- expand.grid(row = 1:7, col = 1:7) expect_loc_grid( ggmatrix_location(pm), expand.grid(row = 1:7, col = 1:7) ) expect_error( ggmatrix_location(pm, location = expand.grid(row = 1:7, col = 2:8)) ) expect_error( ggmatrix_location(pm, location = expand.grid(row = 2:8, col = 1:7)) ) expect_error( ggmatrix_location(pm, location = expand.grid(row = 1:7, col = 0:6)) ) expect_error( ggmatrix_location(pm, location = expand.grid(row = 0:6, col = 1:7)) ) expect_error( ggmatrix_location(pm, location = expand.grid(row = 1:7, col = c(1:6, NA))) ) expect_error( ggmatrix_location(pm, location = expand.grid(row = c(1:6, NA), col = 1:7)) ) }) test_that("location recursion", { pm <- ggpairs(reshape::tips) to_loc <- expand.grid(row = 1:7, col = 1:7) expect_loc_grid( ggmatrix_location(pm), expand.grid(row = 1:7, col = 1:7) ) expect_loc_grid( ggmatrix_location(pm, location = ggmatrix_location(pm)), expand.grid(row = 1:7, col = 1:7) ) }) GGally/tests/testthat/test-ggnet2.R0000644000176200001440000002076414063460176016741 0ustar liggesusers context("ggnet2") if ("package:igraph" %in% search()) { detach("package:igraph") } rq <- function(...) { require(..., quietly = TRUE) } rq(network) # network objects rq(sna) # placement and centrality rq(ggplot2) # grammar of graphics rq(grid) # arrows rq(scales) # sizing rq(intergraph) # test igraph conversion rq(RColorBrewer) # test ColorBrewer palettes test_that("examples", { ### --- start: documented examples set.seed(54321) # random adjacency matrix x <- 10 ndyads <- x * (x - 1) density <- x / ndyads m <- matrix(0, nrow = x, ncol = x) dimnames(m) <- list(letters[ 1:x ], letters[ 1:x ]) m[ row(m) != col(m) ] <- runif(ndyads) < density m # random undirected network n <- network::network(m, directed = FALSE) n ggnet2(n, label = TRUE) # ggnet2(n, label = TRUE, shape = 15) # ggnet2(n, label = TRUE, shape = 15, color = "black", label.color = "white") # add vertex attribute x <- network.vertex.names(n) # nolint x <- ifelse(x %in% c("a", "e", "i"), "vowel", "consonant") n %v% "phono" <- x ggnet2(n, color = "phono") ggnet2(n, color = "phono", palette = c("vowel" = "gold", "consonant" = "grey")) ggnet2(n, shape = "phono", color = "phono") # random groups n %v% "group" <- sample(LETTERS[1:3], 10, replace = TRUE) ggnet2(n, color = "group", palette = "Set2") # random weights n %e% "weight" <- sample(1:3, network.edgecount(n), replace = TRUE) ggnet2(n, edge.size = "weight", edge.label = "weight") # Padgett's Florentine wedding data data(flo, package = "network") flo ggnet2(flo, label = TRUE) ggnet2(flo, label = TRUE, label.trim = 4, vjust = -1, size = 3, color = 1) # ggnet2(flo, label = TRUE, size = 12, color = "white") ### --- end: documented examples # test node assignment errors expect_error(ggnet2(n, color = NA)) expect_error(ggnet2(n, color = -1)) expect_error(ggnet2(n, color = rep("red", network.size(n) - 1))) # test node assignment ggnet2(n, color = rep("red", network.size(n))) # test node assignment errors expect_error(ggnet2(n, edge.color = NA)) expect_error(ggnet2(n, edge.color = -1)) expect_error(ggnet2(n, edge.color = rep("red", network.edgecount(n) - 1))) # test edge assignment ggnet2(n, edge.color = rep("red", network.edgecount(n))) # ggnet2(n, edge.color = "weight") # test mode = c("x", "y") ggnet2(n, mode = matrix(1, ncol = 2, nrow = 10)) n %v% "x" <- sample(1:10) n %v% "y" <- sample(1:10) ggnet2(n, mode = c("x", "y")) expect_error(ggnet2(n, mode = c("xx", "yy")), "not found") expect_error(ggnet2(n, mode = c("phono", "phono")), "not numeric") expect_error(ggnet2(n, mode = matrix(1, ncol = 2, nrow = 9)), "coordinates length") # test arrow.size expect_error(ggnet2(n, arrow.size = -1), "incorrect arrow.size") expect_warning(ggnet2(n, arrow.size = 1), "arrow.size ignored") # test arrow.gap suppressWarnings(expect_error( ggnet(n, arrow.size = 12, arrow.gap = -1), "incorrect arrow.gap" )) suppressWarnings(expect_warning( ggnet(n, arrow.size = 12, arrow.gap = 0.1), "arrow.gap ignored" # network is undirected; arrow.gap ignored )) suppressWarnings(expect_warning( ggnet(n, arrow.size = 12, arrow.gap = 0.1), "arrow.size ignored" # network is undirected; arrow.size ignored )) m <- network::network(m, directed = TRUE) ggnet2(m, arrow.size = 12, arrow.gap = 0.05) # test max_size expect_error(ggnet2(n, max_size = NA), "incorrect max_size") # test na.rm expect_error(ggnet2(n, na.rm = 1:2), "incorrect na.rm") expect_error(ggnet2(n, na.rm = "xyz"), "not found") n %v% "missing" <- ifelse(n %v% "phono" == "vowel", NA, n %v% "phono") expect_message(ggnet2(n, na.rm = "missing"), "removed") n %v% "missing" <- NA expect_warning(ggnet2(n, na.rm = "missing"), "removed all nodes") # test size = "degree" ggnet2(n, size = "degree") # test size.min expect_error(ggnet2(n, size = "degree", size.min = -1), "incorrect size.min") expect_message(ggnet2(n, size = "degree", size.min = 1), "size.min removed") expect_warning(ggnet2(n, size = "abc", size.min = 1), "not numeric") expect_warning(ggnet2(n, size = 4, size.min = 5), "removed all nodes") # test size.max expect_error(ggnet2(n, size = "degree", size.max = -1), "incorrect size.max") expect_message(ggnet2(n, size = "degree", size.max = 99), "size.max removed") expect_warning(ggnet2(n, size = "abc", size.max = 1), "not numeric") expect_warning(ggnet2(n, size = 4, size.max = 3), "removed all nodes") # test size.cut ggnet2(n, size = 1:10, size.cut = 3) ggnet2(n, size = 1:10, size.cut = TRUE) expect_error(ggnet2(n, size = 1:10, size.cut = NA), "incorrect size.cut") expect_error(ggnet2(n, size = 1:10, size.cut = "xyz"), "incorrect size.cut") expect_warning(ggnet2(n, size = "abc", size.cut = 3), "not numeric") expect_warning(ggnet2(n, size = 1, size.cut = 3), "ignored") # test alpha.palette ggnet2(n, alpha = "phono", alpha.palette = c("vowel" = 1, "consonant" = 0.5)) ggnet2(n, alpha = factor(1:10)) expect_error( ggnet2(n, alpha = "phono", alpha.palette = c("vowel" = 1)), "no alpha.palette value" ) # test color.palette # ggnet2(n, color = "phono", color.palette = c("vowel" = 1, "consonant" = 2)) ggnet2(n, color = factor(1:10)) ggnet2(n, color = "phono", palette = "Set1") # only 2 groups, palette has min. 3 expect_error(ggnet2(n, color = factor(1:10), palette = "Set1"), "too many node groups") expect_error( ggnet2(n, color = "phono", color.palette = c("vowel" = 1)), "no color.palette value" ) # test shape.palette ggnet2(n, shape = "phono", shape.palette = c("vowel" = 15, "consonant" = 19)) expect_warning(ggnet2(n, shape = factor(1:10)), "discrete values") expect_error( ggnet2(n, shape = "phono", shape.palette = c("vowel" = 1)), "no shape.palette value" ) # test size.palette ggnet2(n, size = "phono", size.palette = c("vowel" = 1, "consonant" = 2)) ggnet2(n, size = factor(1:10)) expect_error(ggnet2(n, size = "phono", size.palette = c("vowel" = 1)), "no size.palette value") # test node.label ggnet2(n, label = sample(letters, 10)) ggnet2(n, label = "phono") # test label.alpha expect_error(ggnet2(n, label = TRUE, label.alpha = "xyz"), "incorrect label.alpha") # test label.color expect_error(ggnet2(n, label = TRUE, label.color = "xyz"), "incorrect label.color") # test label.size expect_error(ggnet2(n, label = TRUE, label.size = "xyz"), "incorrect label.size") # test label.trim expect_error(ggnet2(n, label = TRUE, label.trim = "xyz"), "incorrect label.trim") ggnet2(n, label = TRUE, label.trim = toupper) # test mode expect_error(ggnet2(n, mode = "xyz"), "unsupported") expect_error(ggnet2(n, mode = letters[1:3]), "incorrect mode") # test edge.node shared colors ggnet2(n, color = "phono", edge.color = c("color", "grey")) # test edge.color expect_error(ggnet2(n, edge.color = "xyz"), "incorrect edge.color") # test edge.label.alpha expect_error( ggnet2(n, edge.label = "xyz", edge.label.alpha = "xyz"), "incorrect edge.label.alpha" ) # test edge.label.color expect_error( ggnet2(n, edge.label = "xyz", edge.label.color = "xyz"), "incorrect edge.label.color" ) # test edge.label.size expect_error(ggnet2(n, edge.label = "xyz", edge.label.size = "xyz"), "incorrect edge.label.size") # test edge.size expect_error(ggnet2(n, edge.size = "xyz"), "incorrect edge.size") # test layout.exp expect_error(ggnet2(n, layout.exp = "xyz")) ggnet2(n, layout.exp = 0.1) ### --- test bipartite functionality # weighted adjacency matrix bip <- data.frame( event1 = c(1, 2, 1), event2 = c(0, 0, 3), event3 = c(1, 1, 0), row.names = letters[1:3] ) # weighted bipartite network bip <- network( bip, matrix.type = "bipartite", ignore.eval = FALSE, # names.eval = "weights" ) # test bipartite mode ggnet2(bip, color = "mode") ### --- test network coercion expect_warning(ggnet2(network(matrix(1, nrow = 2, ncol = 2), loops = TRUE)), "self-loops") expect_error(ggnet2(1:2), "network object") expect_error(ggnet2(network(data.frame(1:2, 3:4), hyper = TRUE)), "hyper") expect_error(ggnet2(network(data.frame(1:2, 3:4), multiple = TRUE)), "multiplex graphs") ### --- test igraph functionality if (requireNamespace("igraph", quietly = TRUE)) { library(igraph) # test igraph conversion p <- ggnet2(asIgraph(n), color = "group") expect_null(p$guides$colour) # test igraph degree ggnet2(n, size = "degree") expect_true(TRUE) } }) GGally/tests/testthat/test-stat_prop.R0000644000176200001440000000454413764714663017576 0ustar liggesuserscontext("stat_prop") test_that("example", { expect_print <- function(x) { expect_silent(print(x)) } d <- as.data.frame(Titanic) p <- ggplot(d) + aes(x = Class, fill = Survived, weight = Freq, by = Class) + geom_bar(position = "fill") + geom_text(stat = "prop", position = position_fill(.5)) expect_print(p) expect_print(p + facet_grid(~Sex)) expect_print(ggplot(d) + aes(x = Class, fill = Survived, weight = Freq) + geom_bar(position = "dodge") + geom_text( aes(by = Survived), stat = "prop", position = position_dodge(0.9), vjust = "bottom" )) expect_print(ggplot(d) + aes(x = Class, fill = Survived, weight = Freq, by = 1) + geom_bar() + geom_text( aes(label = scales::percent(after_stat(prop), accuracy = 1)), stat = "prop", position = position_stack(.5) )) skip_if_not_installed("reshape") data(tips, package = "reshape") expect_print(ggally_colbar(tips, mapping = aes(x = smoker, y = sex))) expect_print(ggally_rowbar(tips, mapping = aes(x = smoker, y = sex))) # change labels' size expect_print(ggally_colbar(tips, mapping = aes(x = smoker, y = sex), size = 8)) # change labels' colour and use bold expect_print(ggally_colbar(tips, mapping = aes(x = smoker, y = sex), colour = "white", fontface = "bold")) # display number of observations instead of proportions expect_print(ggally_colbar(tips, mapping = aes(x = smoker, y = sex, label = after_stat(count)))) # custom bar width expect_print(ggally_colbar(tips, mapping = aes(x = smoker, y = sex), geom_bar_args = list(width = .5))) # change format of labels expect_print(ggally_colbar(tips, mapping = aes(x = smoker, y = sex), label_format = scales::label_percent(accuracy = .01, decimal.mark = ","))) expect_print(ggduo( data = as.data.frame(Titanic), mapping = aes(weight = Freq), columnsX = "Survived", columnsY = c("Sex", "Class", "Age"), types = list(discrete = "rowbar"), legend = 1 )) }) test_that("stat_prop() works with an y aesthetic", { expect_print <- function(x) { expect_silent(print(x)) } d <- as.data.frame(Titanic) p <- ggplot(d) + aes(y = Class, fill = Survived, weight = Freq, by = Class) + geom_bar(position = "fill") + geom_text(stat = "prop", position = position_fill(.5)) expect_print(p) }) GGally/tests/testthat.R0000644000176200001440000000016513666472400014570 0ustar liggesusers if (requireNamespace('testthat', quietly = TRUE)) { library(testthat) library(GGally) test_check("GGally") } GGally/R/0000755000176200001440000000000014063647151011641 5ustar liggesusersGGally/R/data-twitter_spambots.R0000644000176200001440000000141613663637143016314 0ustar liggesusers#' Twitter spambots #' #' A network of spambots found on Twitter as part of a data mining project. #' #' Each node of the network is identified by the Twitter screen name of the #' account and further carries five vertex attributes: #' #' @details \itemize{ #' \item location user's location, as provided by the user #' \item lat latitude, based on the user's location #' \item lon longitude, based on the user's location #' \item followers number of Twitter accounts that follow this account #' \item friends number of Twitter accounts followed by the account #' } #' #' @docType data #' @author Amos Elberg #' @keywords datasets #' @name twitter_spambots #' @usage data(twitter_spambots) #' @format An object of class \code{network} with 120 edges and 94 vertices. NULL GGally/R/ggnet2.R0000644000176200001440000010534114063456663013164 0ustar liggesusersif (getRversion() >= "2.15.1") { utils::globalVariables(c("X1", "X2", "Y1", "Y2", "midX", "midY")) } #' Network plot #' #' Function for plotting network objects using \pkg{ggplot2}, with additional control #' over graphical parameters that are not supported by the \code{\link{ggnet}} #' function. Please visit \url{https://github.com/briatte/ggnet} for the latest #' version of ggnet2, and \url{https://briatte.github.io/ggnet/} for a vignette #' that contains many examples and explanations. #' #' @export #' @param net an object of class \code{\link[network]{network}}, or any object #' that can be coerced to this class, such as an adjacency or incidence matrix, #' or an edge list: see \link[network]{edgeset.constructors} and #' \link[network]{network} for details. If the object is of class #' [igraph][igraph::igraph-package] and the #' [intergraph][intergraph::intergraph-package] package is installed, #' it will be used to convert the object: see #' \code{\link[intergraph]{asNetwork}} for details. #' @param mode a placement method from those provided in the #' \code{\link[sna]{sna}} package: see \link[sna:gplot.layout]{gplot.layout} for #' details. Also accepts the names of two numeric vertex attributes of #' \code{net}, or a matrix of numeric coordinates, in which case the first two #' columns of the matrix are used. #' Defaults to the Fruchterman-Reingold force-directed algorithm. #' @param layout.par options to be passed to the placement method, as listed in #' \link[sna]{gplot.layout}. #' Defaults to \code{NULL}. #' @param layout.exp a multiplier to expand the horizontal axis if node labels #' get clipped: see \link[scales]{expand_range} for details. #' Defaults to \code{0} (no expansion). #' @param alpha the level of transparency of the edges and nodes, which might be #' a single value, a vertex attribute, or a vector of values. #' Also accepts \code{"mode"} on bipartite networks (see 'Details'). #' Defaults to \code{1} (no transparency). #' @param color the color of the nodes, which might be a single value, a vertex #' attribute, or a vector of values. #' Also accepts \code{"mode"} on bipartite networks (see 'Details'). #' Defaults to \code{grey75}. #' @param shape the shape of the nodes, which might be a single value, a vertex #' attribute, or a vector of values. #' Also accepts \code{"mode"} on bipartite networks (see 'Details'). #' Defaults to \code{19} (solid circle). #' @param size the size of the nodes, in points, which might be a single value, #' a vertex attribute, or a vector of values. Also accepts \code{"indegree"}, #' \code{"outdegree"}, \code{"degree"} or \code{"freeman"} to size the nodes by #' their unweighted degree centrality (\code{"degree"} and \code{"freeman"} are #' equivalent): see \code{\link[sna]{degree}} for details. All node sizes must #' be strictly positive. #' Also accepts \code{"mode"} on bipartite networks (see 'Details'). #' Defaults to \code{9}. #' @param max_size the \emph{maximum} size of the node when \code{size} produces #' nodes of different sizes, in points. #' Defaults to \code{9}. #' @param na.rm whether to subset the network to nodes that are \emph{not} #' missing a given vertex attribute. If set to any vertex attribute of #' \code{net}, the nodes for which this attribute is \code{NA} will be removed. #' Defaults to \code{NA} (does nothing). #' @param palette the palette to color the nodes, when \code{color} is not a #' color value or a vector of color values. Accepts named vectors of color #' values, or if [RColorBrewer][RColorBrewer::RColorBrewer] is installed, any #' ColorBrewer palette name: see [RColorBrewer::brewer.pal()] and #' \url{https://colorbrewer2.org/} for details. #' Defaults to \code{NULL}, which will create an array of grayscale color values #' if \code{color} is not a color value or a vector of color values. #' @param alpha.palette the palette to control the transparency levels of the #' nodes set by \code{alpha} when the levels are not numeric values. #' Defaults to \code{NULL}, which will create an array of alpha transparency #' values if \code{alpha} is not a numeric value or a vector of numeric values. #' @param alpha.legend the name to assign to the legend created by #' \code{alpha} when its levels are not numeric values. #' Defaults to \code{NA} (no name). #' @param color.palette see \code{palette} #' @param color.legend the name to assign to the legend created by #' \code{palette}. #' Defaults to \code{NA} (no name). #' @param shape.palette the palette to control the shapes of the nodes set by #' \code{shape} when the shapes are not numeric values. #' Defaults to \code{NULL}, which will create an array of shape values if #' \code{shape} is not a numeric value or a vector of numeric values. #' @param shape.legend the name to assign to the legend created by #' \code{shape} when its levels are not numeric values. #' Defaults to \code{NA} (no name). #' @param size.palette the palette to control the sizes of the nodes set by #' \code{size} when the sizes are not numeric values. #' @param size.legend the name to assign to the legend created by #' \code{size}. #' Defaults to \code{NA} (no name). #' @param size.zero whether to accept zero-sized nodes based on the value(s) of #' \code{size}. #' Defaults to \code{FALSE}, which ensures that zero-sized nodes are still #' shown in the plot and its size legend. #' @param size.cut whether to cut the size of the nodes into a certain number of #' quantiles. Accepts \code{TRUE}, which tries to cut the sizes into quartiles, #' or any positive numeric value, which tries to cut the sizes into that many #' quantiles. If the size of the nodes do not contain the specified number of #' distinct quantiles, the largest possible number is used. #' See \code{\link[stats]{quantile}} and \code{\link[base]{cut}} for details. #' Defaults to \code{FALSE} (does nothing). #' @param size.min whether to subset the network to nodes with a minimum size, #' based on the values of \code{size}. #' Defaults to \code{NA} (preserves all nodes). #' @param size.max whether to subset the network to nodes with a maximum size, #' based on the values of \code{size}. #' Defaults to \code{NA} (preserves all nodes). #' @param label whether to label the nodes. If set to \code{TRUE}, nodes are #' labeled with their vertex names. If set to a vector that contains as many #' elements as there are nodes in \code{net}, nodes are labeled with these. If #' set to any other vector of values, the nodes are labeled only when their #' vertex name matches one of these values. #' Defaults to \code{FALSE} (no labels). #' @param label.alpha the level of transparency of the node labels, as a #' numeric value, a vector of numeric values, or as a vertex attribute #' containing numeric values. #' Defaults to \code{1} (no transparency). #' @param label.color the color of the node labels, as a color value, a vector #' of color values, or as a vertex attribute containing color values. #' Defaults to \code{"black"}. #' @param label.size the size of the node labels, in points, as a numeric value, #' a vector of numeric values, or as a vertex attribute containing numeric #' values. #' Defaults to \code{max_size / 2} (half the maximum node size), which defaults #' to \code{4.5}. #' @param label.trim whether to apply some trimming to the node labels. Accepts #' any function that can process a character vector, or a strictly positive #' numeric value, in which case the labels are trimmed to a fixed-length #' substring of that length: see \code{\link[base]{substr}} for details. #' Defaults to \code{FALSE} (does nothing). #' @param node.alpha see \code{alpha} #' @param node.color see \code{color} #' @param node.label see \code{label} #' @param node.shape see \code{shape} #' @param node.size see \code{size} #' @param edge.alpha the level of transparency of the edges. #' Defaults to the value of \code{alpha}, which defaults to \code{1}. #' @param edge.color the color of the edges, as a color value, a vector of color #' values, or as an edge attribute containing color values. #' Defaults to \code{"grey50"}. #' @param edge.lty the linetype of the edges, as a linetype value, a vector of #' linetype values, or as an edge attribute containing linetype values. #' Defaults to \code{"solid"}. #' @param edge.size the size of the edges, in points, as a numeric value, a #' vector of numeric values, or as an edge attribute containing numeric values. #' All edge sizes must be strictly positive. #' Defaults to \code{0.25}. #' @param edge.label the labels to plot at the middle of the edges, as a single #' value, a vector of values, or as an edge attribute. #' Defaults to \code{NULL} (no edge labels). #' @param edge.label.alpha the level of transparency of the edge labels, as a #' numeric value, a vector of numeric values, or as an edge attribute #' containing numeric values. #' Defaults to \code{1} (no transparency). #' @param edge.label.color the color of the edge labels, as a color value, a #' vector of color values, or as an edge attribute containing color values. #' Defaults to \code{label.color}, which defaults to \code{"black"}. #' @param edge.label.fill the background color of the edge labels. #' Defaults to \code{"white"}. #' @param edge.label.size the size of the edge labels, in points, as a numeric #' value, a vector of numeric values, or as an edge attribute containing numeric #' values. All edge label sizes must be strictly positive. #' Defaults to \code{max_size / 2} (half the maximum node size), which defaults #' to \code{4.5}. #' @param arrow.size the size of the arrows for directed network edges, in #' points. See \code{\link[grid]{arrow}} for details. #' Defaults to \code{0} (no arrows). #' @param arrow.gap a setting aimed at improving the display of edge arrows by #' plotting slightly shorter edges. Accepts any value between \code{0} and #' \code{1}, where a value of \code{0.05} will generally achieve good results #' when the size of the nodes is reasonably small. #' Defaults to \code{0} (no shortening). #' @param arrow.type the type of the arrows for directed network edges. See #' \code{\link[grid]{arrow}} for details. #' Defaults to \code{"closed"}. #' @param legend.size the size of the legend symbols and text, in points. #' Defaults to \code{9}. #' @param legend.position the location of the plot legend(s). Accepts all #' \code{legend.position} values supported by \code{\link[ggplot2]{theme}}. #' Defaults to \code{"right"}. #' @param ... other arguments passed to the \code{geom_text} object that sets #' the node labels: see \code{\link[ggplot2]{geom_text}} for details. #' @seealso \code{\link{ggnet}} in this package, #' \code{\link[sna]{gplot}} in the \code{\link[sna]{sna}} package, and #' \code{\link[network]{plot.network}} in the \code{\link[network]{network}} #' package #' @author Moritz Marbach and Francois Briatte, with help from Heike Hofmann, #' Pedro Jordano and Ming-Yu Liu #' @details The degree centrality measures that can be produced through the #' \code{size} argument will take the directedness of the network into account, #' but will be unweighted. To compute weighted network measures, see the #' \code{tnet} package by Tore Opsahl (\code{help("tnet", package = "tnet")}). #' #' The nodes of bipartite networks can be mapped to their mode by passing the #' \code{"mode"} argument to any of \code{alpha}, \code{color}, \code{shape} and #' \code{size}, in which case the nodes of the primary mode will be mapped as #' \code{"actor"}, and the nodes of the secondary mode will be mapped as #' \code{"event"}. #' @importFrom utils installed.packages #' @importFrom grDevices gray.colors #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' library(network) #' #' # random adjacency matrix #' x <- 10 #' ndyads <- x * (x - 1) #' density <- x / ndyads #' m <- matrix(0, nrow = x, ncol = x) #' dimnames(m) <- list(letters[ 1:x ], letters[ 1:x ]) #' m[ row(m) != col(m) ] <- runif(ndyads) < density #' m #' #' # random undirected network #' n <- network::network(m, directed = FALSE) #' n #' #' p_(ggnet2(n, label = TRUE)) #' p_(ggnet2(n, label = TRUE, shape = 15)) #' p_(ggnet2(n, label = TRUE, shape = 15, color = "black", label.color = "white")) #' #' # add vertex attribute #' x = network.vertex.names(n) #' x = ifelse(x %in% c("a", "e", "i"), "vowel", "consonant") #' n %v% "phono" = x #' #' p_(ggnet2(n, color = "phono")) #' p_(ggnet2(n, color = "phono", palette = c("vowel" = "gold", "consonant" = "grey"))) #' p_(ggnet2(n, shape = "phono", color = "phono")) #' #' if (require(RColorBrewer)) { #' #' # random groups #' n %v% "group" <- sample(LETTERS[1:3], 10, replace = TRUE) #' #' p_(ggnet2(n, color = "group", palette = "Set2")) #' #' } #' #' # random weights #' n %e% "weight" <- sample(1:3, network.edgecount(n), replace = TRUE) #' p_(ggnet2(n, edge.size = "weight", edge.label = "weight")) #' #' # edge arrows on a directed network #' p_(ggnet2(network(m, directed = TRUE), arrow.gap = 0.05, arrow.size = 10)) #' #' # Padgett's Florentine wedding data #' data(flo, package = "network") #' flo #' #' p_(ggnet2(flo, label = TRUE)) #' p_(ggnet2(flo, label = TRUE, label.trim = 4, vjust = -1, size = 3, color = 1)) #' p_(ggnet2(flo, label = TRUE, size = 12, color = "white")) ggnet2 <- function( net, mode = "fruchtermanreingold", layout.par = NULL, layout.exp = 0, alpha = 1, color = "grey75", shape = 19, size = 9, max_size = 9, na.rm = NA, palette = NULL, alpha.palette = NULL, alpha.legend = NA, color.palette = palette, color.legend = NA, shape.palette = NULL, shape.legend = NA, size.palette = NULL, size.legend = NA, size.zero = FALSE, size.cut = FALSE, size.min = NA, size.max = NA, label = FALSE, label.alpha = 1, label.color = "black", label.size = max_size / 2, label.trim = FALSE, node.alpha = alpha, node.color = color, node.label = label, node.shape = shape, node.size = size, edge.alpha = 1, edge.color = "grey50", edge.lty = "solid", edge.size = .25, edge.label = NULL, edge.label.alpha = 1, edge.label.color = label.color, edge.label.fill = "white", edge.label.size = max_size / 2, arrow.size = 0, arrow.gap = 0, arrow.type = "closed", legend.size = 9, legend.position = "right", ... ){ # -- packages ---------------------------------------------------------------- require_namespaces(c("network", "sna", "scales")) # -- conversion to network class --------------------------------------------- if (inherits(net, "igraph") && "intergraph" %in% rownames(installed.packages())) { net = intergraph::asNetwork(net) } else if (inherits(net, "igraph")) { stop("install the 'intergraph' package to use igraph objects with ggnet2") } if (!network::is.network(net)) { net = try(network::network(net), silent = TRUE) } if (!network::is.network(net)) { stop("could not coerce net to a network object") } # -- network functions ------------------------------------------------------- get_v = get("%v%", envir = getNamespace("network")) get_e = get("%e%", envir = getNamespace("network")) set_mode = function(x, mode = network::get.network.attribute(x, "bipartite")) { c(rep("actor", mode), rep("event", n_nodes - mode)) } set_node = function(x, value, mode = TRUE) { if (is.null(x) || any(is.na(x)) || any(is.infinite(x)) || any(is.nan(x))) { stop(paste("incorrect", value, "value")) } else if (is.numeric(x) && any(x < 0)) { stop(paste("incorrect", value, "value")) } else if (length(x) == n_nodes) { x } else if (length(x) > 1) { stop(paste("incorrect", value, "length")) } else if (any(x %in% v_attr)) { get_v(net, x) } else if (mode && identical(x, "mode") && is_bip) { set_mode(net) } else { x } } set_edge = function(x, value) { if (is.null(x) || any(is.na(x)) || any(is.infinite(x)) || any(is.nan(x))) { stop(paste("incorrect", value, "value")) } else if (is.numeric(x) && any(x < 0)) { stop(paste("incorrect", value, "value")) } else if (length(x) == n_edges) { x } else if (length(x) > 1) { stop(paste("incorrect", value, "length")) } else if (any(x %in% e_attr)) { get_e(net, x) } else { x } } set_attr = function(x) { if (length(x) == n_nodes) { x } else if (length(x) > 1) { stop(paste("incorrect coordinates length")) } else if (!x %in% v_attr) { stop(paste("vertex attribute", x, "was not found")) } else if (!is.numeric(get_v(net, x))) { stop(paste("vertex attribute", x, "is not numeric")) } else { get_v(net, x) } } set_name = function(x, y) { z = length(x) == 1 && x %in% v_attr z = ifelse(is.na(y), z, y) z = ifelse(isTRUE(z), x, z) ifelse(is.logical(z), "", z) } set_size = function(x) { y = x + (0 %in% x) * !size.zero y = scales::rescale_max(y) y = scales::abs_area(max_size)(y) if (is.null(names(x))) names(y) = x else names(y) = names(x) y } is_one = function(x) length(unique(x)) == 1 is_col = function(x) all(is.numeric(x)) | all(network::is.color(x)) # -- network structure ------------------------------------------------------- n_nodes = network::network.size(net) n_edges = network::network.edgecount(net) v_attr = network::list.vertex.attributes(net) e_attr = network::list.edge.attributes(net) is_bip = network::is.bipartite(net) is_dir = ifelse(network::is.directed(net), "digraph", "graph") if (!is.numeric(arrow.size) || arrow.size < 0) { stop("incorrect arrow.size value") } else if (arrow.size > 0 & is_dir == "graph") { warning("network is undirected; arrow.size ignored") arrow.size = 0 } if (!is.numeric(arrow.gap) || arrow.gap < 0 || arrow.gap > 1) { stop("incorrect arrow.gap value") } else if (arrow.gap > 0 & is_dir == "graph") { warning("network is undirected; arrow.gap ignored") arrow.gap = 0 } if (network::is.hyper(net)) { stop("ggnet2 cannot plot hyper graphs") } if (network::is.multiplex(net)) { stop("ggnet2 cannot plot multiplex graphs") } if (network::has.loops(net)) { warning("ggnet2 does not know how to handle self-loops") } # -- check max_size ---------------------------------------------------------- x = max_size if (!is.numeric(x) || is.infinite(x) || is.nan(x) || x < 0) { stop("incorrect max_size value") } # -- initialize dataset ------------------------------------------------------ data = data.frame(label = get_v(net, "vertex.names"), stringsAsFactors = FALSE) data$alpha = set_node(node.alpha , "node.alpha") data$color = set_node(node.color , "node.color") data$shape = set_node(node.shape , "node.shape") data$size = set_node(node.size , "node.size") # -- node removal ------------------------------------------------------------ if (length(na.rm) > 1) { stop("incorrect na.rm value") } else if (!is.na(na.rm)) { if (!na.rm %in% v_attr) { stop(paste("vertex attribute", na.rm, "was not found")) } x = which(is.na(get_v(net, na.rm))) message(paste("na.rm removed", length(x), "nodes out of", nrow(data))) if (length(x) > 0) { data = data[ -x, ] network::delete.vertices(net, x) if (!nrow(data)) { warning("na.rm removed all nodes; nothing left to plot") return(invisible(NULL)) } } } # -- weight methods ---------------------------------------------------------- x = size if (length(x) == 1 && x %in% c("indegree", "outdegree", "degree", "freeman")) { # prevent namespace conflict with igraph if ("package:igraph" %in% search()) { y = ifelse(is_dir == "digraph", "directed", "undirected") z = c("indegree" = "in", "outdegree" = "out", "degree" = "all", "freeman" = "all")[ x ] data$size = igraph::degree(igraph::graph.adjacency(as.matrix(net), mode = y), mode = z) } else { data$size = sna::degree(net, gmode = is_dir, cmode = ifelse(x == "degree", "freeman", x)) } size.legend = ifelse(is.na(size.legend), x, size.legend) } # -- weight thresholds ------------------------------------------------------- x = ifelse(is.na(size.min), 0, size.min) if (length(x) > 1 || !is.numeric(x) || is.infinite(x) || is.nan(x) || x < 0) { stop("incorrect size.min value") } else if (x > 0 && !is.numeric(data$size)) { warning("node.size is not numeric; size.min ignored") } else if (x > 0) { x = which(data$size < x) message(paste("size.min removed", length(x), "nodes out of", nrow(data))) if (length(x) > 0) { data = data[ -x, ] network::delete.vertices(net, x) if (!nrow(data)) { warning("size.min removed all nodes; nothing left to plot") return(invisible(NULL)) } } } x = ifelse(is.na(size.max), 0, size.max) if (length(x) > 1 || !is.numeric(x) || is.infinite(x) || is.nan(x) || x < 0) { stop("incorrect size.max value") } else if (x > 0 && !is.numeric(data$size)) { warning("node.size is not numeric; size.max ignored") } else if (x > 0) { x = which(data$size > x) message(paste("size.max removed", length(x), "nodes out of", nrow(data))) if (length(x) > 0) { data = data[ -x, ] network::delete.vertices(net, x) if (!nrow(data)) { warning("size.max removed all nodes; nothing left to plot") return(invisible(NULL)) } } } # -- weight quantiles -------------------------------------------------------- x = size.cut if (length(x) > 1 || is.null(x) || is.na(x) || is.infinite(x) || is.nan(x)) { stop("incorrect size.cut value") } else if (isTRUE(x)) { x = 4 } else if (is.logical(x) && !x) { x = 0 } else if (!is.numeric(x)) { stop("incorrect size.cut value") } if (x >= 1 && !is.numeric(data$size)) { warning("node.size is not numeric; size.cut ignored") } else if (x >= 1) { x = unique(quantile(data$size, probs = seq(0, 1, by = 1 / as.integer(x)))) if (length(x) > 1) { data$size = cut(data$size, unique(x), include.lowest = TRUE) } else { warning("node.size is invariant; size.cut ignored") } } # -- alpha palette ----------------------------------------------------------- if (!is.null(alpha.palette)) { x = alpha.palette } else if (is.factor(data$alpha)) { x = levels(data$alpha) } else { x = unique(data$alpha) } if (!is.null(names(x))) { y = unique(na.omit(data$alpha[ !data$alpha %in% names(x) ])) if (length(y) > 0) { stop(paste("no alpha.palette value for", paste0(y, collapse = ", "))) } } else if (is.factor(data$alpha) || !is.numeric(x)) { data$alpha = factor(data$alpha) x = scales::rescale_max(1:length(levels(data$alpha))) names(x) = levels(data$alpha) } alpha.palette = x # -- color palette ----------------------------------------------------------- if (!is.null(color.palette)) { x = color.palette } else if (is.factor(data$color)) { x = levels(data$color) } else { x = unique(data$color) } if (length(x) == 1 && "RColorBrewer" %in% rownames(installed.packages()) && x %in% rownames(RColorBrewer::brewer.pal.info)) { data$color = factor(data$color) n_groups = length(levels(data$color)) n_colors = RColorBrewer::brewer.pal.info[ x, "maxcolors" ] if (n_groups > n_colors) { stop(paste0("too many node groups (", n_groups, ") for ", "ColorBrewer palette ", x, " (max: ", n_colors, ")")) } else if (n_groups < 3) { n_groups = 3 } x = RColorBrewer::brewer.pal(n_groups, x)[ 1:length(levels(data$color)) ] names(x) = levels(data$color) } if (!is.null(names(x))) { y = unique(na.omit(data$color[ !data$color %in% names(x) ])) if (length(y) > 0) { stop(paste("no color.palette value for", paste0(y, collapse = ", "))) } } else if (is.factor(data$color) || !is_col(x)) { data$color = factor(data$color) x = gray.colors(length(x)) names(x) = levels(data$color) } color.palette = x # -- shape palette ----------------------------------------------------------- if (!is.null(shape.palette)) { x = shape.palette } else if (is.factor(data$shape)) { x = levels(data$shape) } else { x = unique(data$shape) } if (!is.null(names(x))) { y = unique(na.omit(data$shape[ !data$shape %in% names(x) ])) if (length(y) > 0) { stop(paste("no shape.palette value for", paste0(y, collapse = ", "))) } } else if (is.factor(data$shape) || !is.numeric(x)) { data$shape = factor(data$shape) x = scales::shape_pal()(length(levels(data$shape))) names(x) = levels(data$shape) } shape.palette = x # -- size palette ------------------------------------------------------------ if (!is.null(size.palette)) { x = size.palette } else if (is.factor(data$size)) { x = levels(data$size) } else { x = unique(data$size) } if (!is.null(names(x))) { y = unique(na.omit(data$size[ !data$size %in% names(x) ])) if (length(y) > 0) { stop(paste("no size.palette value for", paste0(y, collapse = ", "))) } } else if (is.factor(data$size) || !is.numeric(x)) { data$size = factor(data$size) x = 1:length(levels(data$size)) names(x) = levels(data$size) } size.palette = x # -- node labels ------------------------------------------------------------- l = node.label if (isTRUE(l)) { l = data$label } else if (length(l) > 1 & length(l) == n_nodes) { data$label = l } else if (length(l) == 1 && l %in% v_attr) { l = get_v(net, l) } else { l = ifelse(data$label %in% l, data$label, "") } # -- node placement ---------------------------------------------------------- if (is.character(mode) && length(mode) == 1) { mode = paste0("gplot.layout.", mode) if (!exists(mode, where = getNamespace("sna"))) { stop(paste("unsupported placement method:", mode)) } else { mode <- get(mode, getNamespace("sna")) } # sna placement algorithm xy = network::as.matrix.network.adjacency(net) xy = do.call(mode, list(xy, layout.par)) xy = data.frame(x = xy[, 1], y = xy[, 2]) } else if (is.character(mode) && length(mode) == 2) { # fixed coordinates from vertex attributes xy = data.frame(x = set_attr(mode[1]), y = set_attr(mode[2])) } else if (is.numeric(mode) && is.matrix(mode)) { # fixed coordinates from matrix xy = data.frame(x = set_attr(mode[, 1]), y = set_attr(mode[, 2])) } else { stop("incorrect mode value") } xy$x = scale(xy$x, min(xy$x), diff(range(xy$x)))[,1] xy$y = scale(xy$y, min(xy$y), diff(range(xy$y)))[,1] data = cbind(data, xy) # -- edge colors ------------------------------------------------------------- edges = network::as.matrix.network.edgelist(net) if (edge.color[1] == "color" && length(edge.color) == 2) { # edge colors from node source and target edge.color = ifelse(data$color[ edges[, 1]] == data$color[ edges[, 2]], as.character(data$color[ edges[, 1]]), edge.color[2]) if (!is.null(names(color.palette))) { x = which(edge.color %in% names(color.palette)) edge.color[x] = color.palette[ edge.color[x] ] } edge.color[ is.na(edge.color) ] = edge.color[2] } edge.color = set_edge(edge.color, "edge.color") if (!is_col(edge.color)) { stop("incorrect edge.color value") } # -- edge list --------------------------------------------------------------- edges = data.frame(xy[ edges[, 1], ], xy[ edges[, 2], ]) names(edges) = c("X1", "Y1", "X2", "Y2") # -- edge labels, colors and sizes ------------------------------------------- if (!is.null(edge.label)) { edges$midX = (edges$X1 + edges$X2) / 2 edges$midY = (edges$Y1 + edges$Y2) / 2 edges$label = set_edge(edge.label, "edge.label") edge.label.alpha = set_edge(edge.label.alpha, "edge.label.alpha") if (!is.numeric(edge.label.alpha)) { stop("incorrect edge.label.alpha value") } edge.label.color = set_edge(edge.label.color, "edge.label.color") if (!is_col(edge.label.color)) { stop("incorrect edge.label.color value") } edge.label.size = set_edge(edge.label.size, "edge.label.size") if (!is.numeric(edge.label.size)) { stop("incorrect edge.label.size value") } } # -- edge linetype ----------------------------------------------------------- edge.lty = set_edge(edge.lty, "edge.lty") # -- edge size --------------------------------------------------------------- edge.size = set_edge(edge.size, "edge.size") if (!is.numeric(edge.size) || any(edge.size <= 0)) { stop("incorrect edge.size value") } # -- plot edges -------------------------------------------------------------- p = ggplot(data, aes(x = x, y = y)) if (nrow(edges) > 0) { if (arrow.gap > 0) { x.dir = with(edges, (X2 - X1)) # do not use absolute value y.dir = with(edges, (Y2 - Y1)) arrow.gap = with(edges, arrow.gap / sqrt(x.dir ^ 2 + y.dir ^ 2)) edges = transform(edges, X1 = X1 + arrow.gap * x.dir, Y1 = Y1 + arrow.gap * y.dir, X2 = X1 + (1 - arrow.gap) * x.dir, Y2 = Y1 + (1 - arrow.gap) * y.dir) } p = p + geom_segment( data = edges, aes(x = X1, y = Y1, xend = X2, yend = Y2), size = edge.size, color = edge.color, alpha = edge.alpha, lty = edge.lty, arrow = arrow( type = arrow.type, length = unit(arrow.size, "pt") ) ) } if (nrow(edges) > 0 && !is.null(edge.label)) { p = p + geom_point( data = edges, aes(x = midX, y = midY), alpha = edge.alpha, color = edge.label.fill, size = edge.label.size * 1.5 ) + geom_text( data = edges, aes(x = midX, y = midY, label = label), alpha = edge.label.alpha, color = edge.label.color, size = edge.label.size ) } # -- plot nodes -------------------------------------------------------------- x = list() if (is.numeric(data$alpha) && is_one(data$alpha)) { x = c(x, alpha = unique(data$alpha)) } if (!is.factor(data$color) && is_one(data$color)) { x = c(x, colour = unique(data$color)) # must be English spelling } if (is.numeric(data$shape) && is_one(data$shape)) { x = c(x, shape = unique(data$shape)) } if (is.numeric(data$size) && is_one(data$size)) { x = c(x, size = unique(data$size)) } else { x = c(x, size = max_size) } p = p + geom_point(aes(alpha = factor(alpha), color = factor(color), shape = factor(shape), size = factor(size))) # -- legend: alpha ----------------------------------------------------------- if (is.numeric(data$alpha)) { v_alpha = unique(data$alpha) names(v_alpha) = unique(data$alpha) p = p + scale_alpha_manual("", values = v_alpha) + guides(alpha = "none") } else { p = p + scale_alpha_manual(set_name(node.alpha, alpha.legend), values = alpha.palette, breaks = names(alpha.palette), guide = guide_legend(override.aes = x)) } # -- legend: color ----------------------------------------------------------- if (!is.null(names(color.palette))) { p = p + scale_color_manual(set_name(node.color, color.legend), values = color.palette, breaks = names(color.palette), guide = guide_legend(override.aes = x)) } else { v_color = unique(data$color) names(v_color) = unique(data$color) p = p + scale_color_manual("", values = v_color) + guides(color = "none") } # -- legend: shape ----------------------------------------------------------- if (is.numeric(data$shape)) { v_shape = unique(data$shape) names(v_shape) = unique(data$shape) p = p + scale_shape_manual("", values = v_shape) + guides(shape = "none") } else { p = p + scale_shape_manual(set_name(node.shape, shape.legend), values = shape.palette, breaks = names(shape.palette), guide = guide_legend(override.aes = x)) } # -- legend: size ------------------------------------------------------------ x = x[ names(x) != "size" ] if (is.numeric(data$size)) { v_size = set_size(unique(data$size)) if (length(v_size) == 1) { v_size = as.numeric(names(v_size)) p = p + scale_size_manual("", values = v_size) + guides(size = "none") } else { p = p + scale_size_manual(set_name(node.size, size.legend), values = v_size, guide = guide_legend(override.aes = x)) } } else { p = p + scale_size_manual(set_name(node.size, size.legend), values = set_size(size.palette), guide = guide_legend(override.aes = x)) } # -- plot node labels -------------------------------------------------------- if (!is_one(l) || unique(l) != "") { label.alpha = set_node(label.alpha, "label.alpha", mode = FALSE) if (!is.numeric(label.alpha)) { stop("incorrect label.alpha value") } label.color = set_node(label.color, "label.color", mode = FALSE) if (!is_col(label.color)) { stop("incorrect label.color value") } label.size = set_node(label.size, "label.size", mode = FALSE) if (!is.numeric(label.size)) { stop("incorrect label.size value") } x = label.trim if (length(x) > 1 || (!is.logical(x) & !is.numeric(x) & !is.function(x))) { stop("incorrect label.trim value") } else if (is.numeric(x) && x > 0) { l = substr(l, 1, x) } else if (is.function(x)) { l = x(l) } p = p + geom_text( label = l, alpha = label.alpha, color = label.color, size = label.size, ... ) } # -- horizontal scale expansion ---------------------------------------------- x = range(data$x) if (!is.numeric(layout.exp) || layout.exp < 0) { stop("incorrect layout.exp value") } else if (layout.exp > 0) { x = scales::expand_range(x, layout.exp / 2) } # -- finalize ---------------------------------------------------------------- p = p + scale_x_continuous(breaks = NULL, limits = x) + scale_y_continuous(breaks = NULL) + theme( panel.background = element_blank(), panel.grid = element_blank(), axis.title = element_blank(), legend.key = element_blank(), legend.position = legend.position, legend.text = element_text(size = legend.size), legend.title = element_text(size = legend.size) ) return(p) } GGally/R/ggsave.R0000644000176200001440000000017013666343177013247 0ustar liggesusers #' @export # @examples # ggsave("test.pdf", ggpairs(iris, 1:2)) grid.draw.ggmatrix <- function(x, ...) { print(x) } GGally/R/data-australia-pisa-2012.R0000644000176200001440000000502314063462301016164 0ustar liggesusers#' Programme for International Student Assessment (PISA) 2012 Data for Australia #' #' About PISA #' #' The Programme for International Student Assessment (PISA) is a triennial international survey which aims to evaluate education systems worldwide by testing the skills and knowledge of 15-year-old students. To date, students representing more than 70 economies have participated in the assessment. #' #' While 65 economies took part in the 2012 study, this data set only contains information from the country of Australia. #' #' @details \itemize{ #' \item gender : Factor w/ 2 levels "female","male": 1 1 2 2 2 1 1 1 2 1 ... #' \item age : Factor w/ 4 levels "4","5","6","7": 2 2 2 4 3 1 2 2 2 2 ... #' \item homework : num 5 5 9 3 2 3 4 3 5 1 ... #' \item desk : num 1 0 1 1 1 1 1 1 1 1 ... #' \item room : num 1 1 1 1 1 1 1 1 1 1 ... #' \item study : num 1 1 1 1 1 1 1 1 1 1 ... #' \item computer : num 1 1 1 1 1 1 1 1 1 1 ... #' \item software : num 1 1 1 1 1 1 1 1 1 1 ... #' \item internet : num 1 1 1 1 1 1 1 1 1 1 ... #' \item literature : num 0 0 1 0 1 1 1 1 1 0 ... #' \item poetry : num 0 0 1 0 1 1 0 1 1 1 ... #' \item art : num 1 0 1 0 1 1 0 1 1 1 ... #' \item textbook : num 1 1 1 1 1 0 1 1 1 1 ... #' \item dictionary : num 1 1 1 1 1 1 1 1 1 1 ... #' \item dishwasher : num 1 1 1 1 0 1 1 1 1 1 ... #' \item PV1MATH : num 562 565 602 520 613 ... #' \item PV2MATH : num 569 557 594 507 567 ... #' \item PV3MATH : num 555 553 552 501 585 ... #' \item PV4MATH : num 579 538 526 521 596 ... #' \item PV5MATH : num 548 573 619 547 603 ... #' \item PV1READ : num 582 617 650 554 605 ... #' \item PV2READ : num 571 572 608 560 557 ... #' \item PV3READ : num 602 560 594 517 627 ... #' \item PV4READ : num 572 564 575 564 597 ... #' \item PV5READ : num 585 565 620 572 598 ... #' \item PV1SCIE : num 583 627 668 574 639 ... #' \item PV2SCIE : num 579 600 665 612 635 ... #' \item PV3SCIE : num 593 574 620 571 666 ... #' \item PV4SCIE : num 567 582 592 598 700 ... #' \item PV5SCIE : num 587 625 656 662 670 ... #' \item SENWGT_STU : num 0.133 0.133 0.141 0.141 0.141 ... #' \item possessions: num 10 8 12 9 11 11 10 12 12 11 ... #' } #' #' @docType data #' @keywords datasets #' @name australia_PISA2012 #' @usage data(australia_PISA2012) #' @format A data frame with 8247 rows and 32 variables #' @source \url{https://www.oecd.org/pisa/pisaproducts/database-cbapisa2012.htm} NULL GGally/R/gglyph.R0000644000176200001440000002174313764714663013277 0ustar liggesusers#' Create \code{\link{glyphplot}} data #' #' Create the data needed to generate a glyph plot. #' #' @param data A data frame containing variables named in \code{x_major}, #' \code{x_minor}, \code{y_major} and \code{y_minor}. #' @param x_major,x_minor,y_major,y_minor The name of the variable (as a #' string) for the major and minor x and y axes. Together, each unique # combination of \code{x_major} and \code{y_major} specifies a grid cell. #' @param polar A logical of length 1, specifying whether the glyphs should #' be drawn in polar coordinates. Defaults to \code{FALSE}. #' @param height,width The height and width of each glyph. Defaults to 95% of #' the \code{\link[ggplot2]{resolution}} of the data. Specify the width #' absolutely by supplying a numeric vector of length 1, or relative to the # resolution of the data by using \code{\link[ggplot2]{rel}}. #' @param y_scale,x_scale The scaling function to be applied to each set of #' minor values within a grid cell. Defaults to \code{\link{identity}} so #' that no scaling is performed. #' @export #' @author Di Cook, Heike Hofmann, Hadley Wickham #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(nasa) #' nasaLate <- nasa[ #' nasa$date >= as.POSIXct("1998-01-01") & #' nasa$lat >= 20 & #' nasa$lat <= 40 & #' nasa$long >= -80 & #' nasa$long <= -60 #' , ] #' temp.gly <- glyphs(nasaLate, "long", "day", "lat", "surftemp", height=2.5) #' p_(ggplot2::ggplot(temp.gly, ggplot2::aes(gx, gy, group = gid)) + #' add_ref_lines(temp.gly, color = "grey90") + #' add_ref_boxes(temp.gly, color = "grey90") + #' ggplot2::geom_path() + #' ggplot2::theme_bw() + #' ggplot2::labs(x = "", y = "")) glyphs <- function( data, x_major, x_minor, y_major, y_minor, polar = FALSE, height = ggplot2::rel(0.95), width = ggplot2::rel(0.95), y_scale = identity, x_scale = identity ) { data$gid <- interaction(data[[x_major]], data[[y_major]], drop = TRUE) if (is.rel(width)) { width <- resolution(data[[x_major]], zero = FALSE) * unclass(width) message("Using width ", format(width, digits = 3)) } if (is.rel(height)) { height <- resolution(data[[y_major]], zero = FALSE) * unclass(height) message("Using height ", format(height, digits = 3)) } if (!identical(x_scale, identity) || !identical(y_scale, identity)) { data <- ddply(data, "gid", function(df) { df[[x_minor]] <- x_scale(df[[x_minor]]) df[[y_minor]] <- y_scale(df[[y_minor]]) df }) } if (polar) { theta <- 2 * pi * rescale01(data[[x_minor]]) r <- rescale01(data[[y_minor]]) data$gx <- data[[x_major]] + width / 2 * r * sin(theta) data$gy <- data[[y_major]] + height / 2 * r * cos(theta) data <- data[order(data[[x_major]], data[[x_minor]]), ] } else { data$gx <- data[[x_major]] + rescale11(data[[x_minor]]) * width / 2 data$gy <- data[[y_major]] + rescale11(data[[y_minor]]) * height / 2 } structure(data, width = width, height = height, polar = polar, x_major = x_major, y_major = y_major, class = c("glyphplot", "data.frame")) } # Create reference lines for a glyph plot ref_lines <- function(data) { stopifnot(is.glyphplot(data)) glyph <- attributes(data) cells <- unique(data[c(glyph$x_major, glyph$y_major, "gid")]) if (glyph$polar) { ref_line <- function(df) { theta <- seq(0, 2 * pi, length.out = 30) data.frame( gid = df$gid, gx = df[[glyph$x_major]] + glyph$width / 4 * sin(theta), gy = df[[glyph$y_major]] + glyph$height / 4 * cos(theta) ) } } else { ref_line <- function(df) { data.frame( gid = df$gid, gx = df[[glyph$x_major]] + c(-1, 1) * glyph$width / 2, gy = df[[glyph$y_major]] ) } } ddply(cells, "gid", ref_line) } # Create reference boxes for a glyph plot ref_boxes <- function(data, fill = NULL) { stopifnot(is.glyphplot(data)) glyph <- attributes(data) cells <- data.frame(unique(data[c(glyph$x_major, glyph$y_major, "gid", fill)])) df <- data.frame(xmin = cells[[glyph$x_major]] - glyph$width / 2, xmax = cells[[glyph$x_major]] + glyph$width / 2, ymin = cells[[glyph$y_major]] - glyph$height / 2, ymax = cells[[glyph$y_major]] + glyph$height / 2) if (!is.null(fill)){ df$fill <- cells[[fill]] } df } # Glyph plot class ----------------------------------------------------------- #' Glyph plot class #' #' @param data A data frame containing variables named in \code{x_major}, #' \code{x_minor}, \code{y_major} and \code{y_minor}. #' @param height,width The height and width of each glyph. Defaults to 95% of #' the \code{\link[ggplot2]{resolution}} of the data. Specify the width #' absolutely by supplying a numeric vector of length 1, or relative to the # resolution of the data by using \code{\link[ggplot2]{rel}}. #' @param polar A logical of length 1, specifying whether the glyphs should #' be drawn in polar coordinates. Defaults to \code{FALSE}. #' @param x_major,y_major The name of the variable (as a #' string) for the major x and y axes. Together, the # combination of \code{x_major} and \code{y_major} specifies a grid cell. #' @export #' @author Di Cook, Heike Hofmann, Hadley Wickham glyphplot <- function(data, width, height, polar, x_major, y_major) { structure(data, width = width, height = height, polar = polar, x_major = x_major, y_major = y_major, class = c("glyphplot", "data.frame")) } #' @export #' @rdname glyphplot is.glyphplot <- function(x) { inherits(x, "glyphplot") } #' @export #' @rdname glyphplot "[.glyphplot" <- function(x, ...) { glyphplot(NextMethod(), width = attr(x, "width"), height = attr(x, "height"), x_major = attr(x, "x_major"), y_major = attr(x, "y_major"), polar = attr(x, "polar")) } #' @param x glyphplot to be printed #' @param ... ignored #' @export #' @rdname glyphplot #' @method print glyphplot print.glyphplot <- function(x, ...) { NextMethod() if (attr(x, "polar")) { cat("Polar ") } else { cat("Cartesian ") } width <- format(attr(x, "width"), digits = 3) height <- format(attr(x, "height"), digits = 3) cat("glyphplot: \n") cat(" Size: [", width, ", ", height, "]\n", sep = "") cat(" Major axes: ", attr(x, "x_major"), ", ", attr(x, "y_major"), "\n", sep = "") # cat("\n") } # Relative dimensions -------------------------------------------------------- # Relative dimensions # # @param x numeric value between 0 and 1 # rel <- function(x) { # structure(x, class = "rel") # } # @export # rel <- ggplot2::rel # @rdname rel # @param ... ignored # print.rel <- function(x, ...) { # print(noquote(paste(x, " *", sep = ""))) # } ## works even though it is not exported # @export # ggplot2::print.rel # @rdname rel # is.rel <- function(x) { # inherits(x, "rel") # } ## only used internally. and ggplot2 has this exported # @export # ggplot2:::is.rel is.rel <- ggplot2:::is.rel # Rescaling functions -------------------------------------------------------- #' Rescaling functions #' #' @param x numeric vector #' @param xlim value used in \code{range} #' @name rescale01 #' @export #' @rdname rescale01 range01 <- function(x) { rng <- range(x, na.rm = TRUE) (x - rng[1]) / (rng[2] - rng[1]) } #' @export #' @rdname rescale01 max1 <- function(x) { x / max(x, na.rm = TRUE) } #' @export #' @rdname rescale01 mean0 <- function(x) { x - mean(x, na.rm = TRUE) } #' @export #' @rdname rescale01 min0 <- function(x) { x - min(x, na.rm = TRUE) } #' @export #' @rdname rescale01 rescale01 <- function(x, xlim=NULL) { if (is.null(xlim)) { rng <- range(x, na.rm = TRUE) } else { rng <- xlim } (x - rng[1]) / (rng[2] - rng[1]) } #' @export #' @rdname rescale01 rescale11 <- function(x, xlim=NULL) { 2 * rescale01(x, xlim) - 1 } #' Add reference lines for each cell of the glyphmap. #' #' @param data A glyphmap structure. #' @param color Set the color to draw in, default is "white" #' @param size Set the line size, default is 1.5 #' @param ... other arguments passed onto [ggplot2::geom_line()] #' @export add_ref_lines <- function(data, color = "white", size = 1.5, ...){ rl <- ref_lines(data) geom_path(data = rl, color = color, size = size, ...) } #' Add reference boxes around each cell of the glyphmap. #' #' @param data A glyphmap structure. #' @param var_fill Variable name to use to set the fill color #' @param color Set the color to draw in, default is "white" #' @param size Set the line size, default is 0.5 #' @param fill fill value used if \code{var_fill} is \code{NULL} #' @param ... other arguments passed onto [ggplot2::geom_rect()] #' @export add_ref_boxes <- function(data, var_fill = NULL, color = "white", size = 0.5, fill = NA, ...){ rb <- ref_boxes(data, var_fill) if (!is.null(var_fill)){ geom_rect(aes_all(names(rb)), data = rb, color = color, size = size, inherit.aes = FALSE, ...) } else{ geom_rect(aes_all(names(rb)), data = rb, color = color, size = size, inherit.aes = FALSE, fill = fill, ...) } } GGally/R/ggmatrix.R0000644000176200001440000001051713666472400013613 0ustar liggesusers #' \pkg{ggplot2} plot matrix #' #' Make a generic matrix of \pkg{ggplot2} plots. #' #' @section Memory usage: #' Now that the \code{\link{print.ggmatrix}} method uses a large \pkg{gtable} object, rather than print each plot independently, memory usage may be of concern. From small tests, memory usage flutters around \code{object.size(data) * 0.3 * length(plots)}. So, for a 80Mb random noise dataset with 100 plots, about 2.4 Gb of memory needed to print. For the 3.46 Mb diamonds dataset with 100 plots, about 100 Mb of memory was needed to print. The benefits of using the \pkg{ggplot2} format greatly outweigh the price of about 20% increase in memory usage from the prior ad-hoc print method. #' #' @param plots list of plots to be put into matrix #' @param nrow,ncol number of rows and columns #' @param xAxisLabels,yAxisLabels strip titles for the x and y axis respectively. Set to \code{NULL} to not be displayed #' @param title,xlab,ylab title, x label, and y label for the graph. Set to \code{NULL} to not be displayed #' @param byrow boolean that determines whether the plots should be ordered by row or by column #' @param showStrips boolean to determine if each plot's strips should be displayed. \code{NULL} will default to the top and right side plots only. \code{TRUE} or \code{FALSE} will turn all strips on or off respectively. #' @param showAxisPlotLabels,showXAxisPlotLabels,showYAxisPlotLabels booleans that determine if the plots axis labels are printed on the X (bottom) or Y (left) part of the plot matrix. If \code{showAxisPlotLabels} is set, both \code{showXAxisPlotLabels} and \code{showYAxisPlotLabels} will be set to the given value. #' @template ggmatrix-labeller-param #' @template ggmatrix-switch-param #' @param xProportions,yProportions Value to change how much area is given for each plot. Either \code{NULL} (default), numeric value matching respective length, or \code{grid::\link[grid]{unit}} object with matching respective length #' @template ggmatrix-progress #' @param data data set using. This is the data to be used in place of 'ggally_data' if the plot is a string to be evaluated at print time #' @param gg \pkg{ggplot2} theme objects to be applied to every plot #' @template ggmatrix-legend-param #' @keywords hplot #' @author Barret Schloerke #' @importFrom rlang %||% #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' plotList <- list() #' for (i in 1:6) { #' plotList[[i]] <- ggally_text(paste("Plot #", i, sep = "")) #' } #' pm <- ggmatrix( #' plotList, #' 2, 3, #' c("A", "B", "C"), #' c("D", "E"), #' byrow = TRUE #' ) #' p_(pm) #' #' pm <- ggmatrix( #' plotList, #' 2, 3, #' xAxisLabels = c("A", "B", "C"), #' yAxisLabels = NULL, #' byrow = FALSE, #' showXAxisPlotLabels = FALSE #' ) #' p_(pm) ggmatrix <- function( plots, nrow, ncol, xAxisLabels = NULL, yAxisLabels = NULL, title = NULL, xlab = NULL, ylab = NULL, byrow = TRUE, showStrips = NULL, showAxisPlotLabels = TRUE, showXAxisPlotLabels = TRUE, showYAxisPlotLabels = TRUE, labeller = NULL, switch = NULL, xProportions = NULL, yProportions = NULL, progress = NULL, data = NULL, gg = NULL, legend = NULL ) { if (!is.list(plots)) { stop("'plots' must be a list()") } check_nrow_ncol(nrow, "nrow") check_nrow_ncol(ncol, "ncol") if (!missing(showAxisPlotLabels)) { showXAxisPlotLabels <- showAxisPlotLabels showYAxisPlotLabels <- showAxisPlotLabels } progress <- as_ggmatrix_progress(progress, nrow * ncol) plotMatrix <- list( data = data, plots = plots, title = title, xlab = xlab, ylab = ylab, showStrips = showStrips, xAxisLabels = xAxisLabels, yAxisLabels = yAxisLabels, showXAxisPlotLabels = showXAxisPlotLabels, showYAxisPlotLabels = showYAxisPlotLabels, labeller = labeller, switch = switch, xProportions = xProportions, yProportions = yProportions, progress = progress, legend = legend, gg = gg, nrow = nrow, ncol = ncol, byrow = byrow ) attributes(plotMatrix)$class <- c("gg", "ggmatrix") plotMatrix } check_nrow_ncol <- function(x, title) { if (!is.numeric(x)) { stop(paste("'", title, "' must be a numeric value", sep = "")) } if (length(x) != 1) { stop(paste("'", title, "' must be a single numeric value", sep = "")) } } GGally/R/ggcoef_model.R0000644000176200001440000005412614063456663014414 0ustar liggesusers#' Plot model coefficients #' #' @describeIn ggcoef_model Redesign of [GGally::ggcoef()] based on [broom.helpers::tidy_plus_plus()]. #' @inheritParams broom.helpers::tidy_plus_plus #' @param model a regression model object #' @param conf.level the confidence level to use for the confidence #' interval if `conf.int = TRUE`; must be strictly greater than 0 #' and less than 1; defaults to 0.95, which corresponds to a 95 #' percent confidence interval #' @param show_p_values if `TRUE`, add p-value to labels #' @param signif_stars if `TRUE`, add significant stars to labels #' @param significance level (between 0 and 1) below which a #' coefficient is consider to be significantly different from 0 #' (or 1 if `exponentiate = TRUE`), `NULL` for not highlighting #' such coefficients #' @param significance_labels optional vector with custom labels #' for significance variable #' @param return_data if `TRUE`, will return the data.frame used #' for plotting instead of the plot #' @param ... parameters passed to [ggcoef_plot()] #' @details #' `ggcoef_model()`, `ggcoef_multinom()` and `ggcoef_compare()` use #' [broom.helpers::tidy_plus_plus()] to obtain a `tibble` of the model #' coefficients, apply additional data transformation and then pass the #' produced `tibble` to `ggcoef_plot()` to generate the plot. #' #' For more control, you can use the argument `return_data = TRUE` to #' get the produced `tibble`, apply any transformation of your own and #' then pass your customized `tibble` to `ggcoef_plot()`. #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' if (require(broom.helpers)) { #' data(tips, package = "reshape") #' mod_simple <- lm(tip ~ day + time + total_bill, data = tips) #' p_(ggcoef_model(mod_simple)) #' #' # custom variable labels #' # you can use the labelled package to define variable labels before computing model #' if (require(labelled)) { #' tips_labelled <- tips %>% #' labelled::set_variable_labels( #' day = "Day of the week", #' time = "Lunch or Dinner", #' total_bill = "Bill's total" #' ) #' mod_labelled <- lm(tip ~ day + time + total_bill, data = tips_labelled) #' p_(ggcoef_model(mod_labelled)) #' } #' # you can provide custom variable labels with 'variable_labels' #' p_(ggcoef_model( #' mod_simple, #' variable_labels = c( #' day = "Week day", #' time = "Time (lunch or dinner ?)", #' total_bill = "Total of the bill" #' ) #' )) #' # if labels are too long, you can use 'facet_labeller' to wrap them #' p_(ggcoef_model( #' mod_simple, #' variable_labels = c( #' day = "Week day", #' time = "Time (lunch or dinner ?)", #' total_bill = "Total of the bill" #' ), #' facet_labeller = label_wrap_gen(10) #' )) #' #' # do not display variable facets but add colour guide #' p_(ggcoef_model(mod_simple, facet_row = NULL, colour_guide = TRUE)) #' #' # a logistic regression example #' d_titanic <- as.data.frame(Titanic) #' d_titanic$Survived <- factor(d_titanic$Survived, c("No", "Yes")) #' mod_titanic <- glm( #' Survived ~ Sex * Age + Class, #' weights = Freq, #' data = d_titanic, #' family = binomial #' ) #' #' # use 'exponentiate = TRUE' to get the Odds Ratio #' p_(ggcoef_model(mod_titanic, exponentiate = TRUE)) #' #' # display intercepts #' p_(ggcoef_model(mod_titanic, exponentiate = TRUE, intercept = TRUE)) #' #' # customize terms labels #' p_( #' ggcoef_model( #' mod_titanic, #' exponentiate = TRUE, #' show_p_values = FALSE, #' signif_stars = FALSE, #' add_reference_rows = FALSE, #' categorical_terms_pattern = "{level} (ref: {reference_level})", #' interaction_sep = " x " #' ) + #' scale_y_discrete(labels = scales::label_wrap(15)) #' ) #' #' # display only a subset of terms #' p_(ggcoef_model(mod_titanic, exponentiate = TRUE, include = c("Age", "Class"))) #' #' # do not change points' shape based on significance #' p_(ggcoef_model(mod_titanic, exponentiate = TRUE, significance = NULL)) #' #' # a black and white version #' p_(ggcoef_model( #' mod_titanic, exponentiate = TRUE, #' colour = NULL, stripped_rows = FALSE #' )) #' #' # show dichotomous terms on one row #' p_(ggcoef_model( #' mod_titanic, #' exponentiate = TRUE, #' no_reference_row = broom.helpers::all_dichotomous(), #' categorical_terms_pattern = #' "{ifelse(dichotomous, paste0(level, ' / ', reference_level), level)}", #' show_p_values = FALSE #' )) #' #' # works also with with polynomial terms #' mod_poly <- lm( #' tip ~ poly(total_bill, 3) + day, #' data = tips, #' ) #' p_(ggcoef_model(mod_poly)) #' #' # or with different type of contrasts #' # for sum contrasts, the value of the reference term is computed #' if (require(emmeans)) { #' mod2 <- lm( #' tip ~ day + time + sex, #' data = tips, #' contrasts = list(time = contr.sum, day = contr.treatment(4, base = 3)) #' ) #' p_(ggcoef_model(mod2)) #' } #' } ggcoef_model <- function ( model, tidy_fun = broom::tidy, conf.int = TRUE, conf.level = .95, exponentiate = FALSE, variable_labels = NULL, term_labels = NULL, interaction_sep = " * ", categorical_terms_pattern = "{level}", add_reference_rows = TRUE, no_reference_row = NULL, intercept = FALSE, include = dplyr::everything(), significance = 1 - conf.level, significance_labels = NULL, show_p_values = TRUE, signif_stars = TRUE, return_data = FALSE, ... ){ data <- ggcoef_data( model = model, tidy_fun = tidy_fun, conf.int = conf.int, conf.level = conf.level, exponentiate = exponentiate, variable_labels = variable_labels, term_labels = term_labels, interaction_sep = interaction_sep, categorical_terms_pattern = categorical_terms_pattern, add_reference_rows = add_reference_rows, no_reference_row = no_reference_row, intercept = intercept, include = include, significance = significance, significance_labels = significance_labels ) if (show_p_values & signif_stars) data$add_to_label <- paste0(data$p_value_label, data$signif_stars) if (show_p_values & !signif_stars) data$add_to_label <- data$p_value_label if (!show_p_values & signif_stars) data$add_to_label <- data$signif_stars if (show_p_values | signif_stars) { data$label <- forcats::fct_inorder( factor( paste0( data$label, ifelse(data$add_to_label == "", "", paste0(" (", data$add_to_label, ")")) ) ) ) data$label_light <- forcats::fct_inorder( factor( paste0( data$label_light, ifelse(data$add_to_label == "", "", paste0(" (", data$add_to_label, ")")) ) ) ) } if (return_data) return(data) args <- list(...) args$data <- data args$exponentiate <- exponentiate if (!"y" %in% names(args) && !"facet_row" %in% names(args)) args$y <- "label_light" if (!"colour" %in% names(args) & !all(is.na(data$var_label))) { args$colour <- "var_label" if (!"colour_guide" %in% names(args)) { args$colour_guide <- FALSE } } do.call(ggcoef_plot, args) } #' @describeIn ggcoef_model Designed for displaying several models on the same plot. #' @export #' @param models named list of models #' @param type a dodged plot or a faceted plot? #' @examples #' #' if (require(broom.helpers)) { #' # Use ggcoef_compare() for comparing several models on the same plot #' mod1 <- lm(Fertility ~ ., data = swiss) #' mod2 <- step(mod1, trace = 0) #' mod3 <- lm(Fertility ~ Agriculture + Education * Catholic, data = swiss) #' models <- list("Full model" = mod1, "Simplified model" = mod2, "With interaction" = mod3) #' #' p_(ggcoef_compare(models)) #' p_(ggcoef_compare(models, type = "faceted")) #' #' # you can reverse the vertical position of the point by using a negative value #' # for dodged_width (but it will produce some warnings) #' \dontrun{ #' p_(ggcoef_compare(models, dodged_width = -.9)) #' } #' } ggcoef_compare <- function ( models, type = c("dodged", "faceted"), tidy_fun = broom::tidy, conf.int = TRUE, conf.level = .95, exponentiate = FALSE, variable_labels = NULL, term_labels = NULL, interaction_sep = " * ", categorical_terms_pattern = "{level}", add_reference_rows = TRUE, no_reference_row = NULL, intercept = FALSE, include = dplyr::everything(), significance = 1 - conf.level, significance_labels = NULL, return_data = FALSE, ... ){ data <- lapply( X = models, FUN = ggcoef_data, tidy_fun = tidy_fun, conf.int = conf.int, conf.level = conf.level, exponentiate = exponentiate, variable_labels = variable_labels, term_labels = term_labels, interaction_sep = interaction_sep, categorical_terms_pattern = categorical_terms_pattern, add_reference_rows = add_reference_rows, no_reference_row = no_reference_row , intercept = intercept, significance = significance, significance_labels = significance_labels ) data <- dplyr::bind_rows(data, .id = "model") coefficients_label <- attr(data, "coefficients_label") data$model <- forcats::fct_inorder(data$model) # include should be applied after lapply data <- data %>% broom.helpers::tidy_select_variables( include = include, model = models[[1]] # we just need to pass a model to allow the function to work ) %>% broom.helpers::tidy_detach_model() # Add NA values for unobserved combinations # (i.e. for a term present in one model but not in another) data <- data %>% tidyr::complete( .data$model, tidyr::nesting( !!sym("variable"), !!sym("var_label"), !!sym("var_class"), !!sym("var_type"), !!sym("contrasts"), !!sym("reference_row"), !!sym("label"), !!sym("label_light") ) ) attr(data, "coefficients_label") <- coefficients_label if (return_data) return(data) type <- match.arg(type) args <- list(...) args$data <- data args$exponentiate <- exponentiate if (!"y" %in% names(args) && !"facet_row" %in% names(args)) args$y <- "label_light" if (type == "dodged") { if (!"dodged " %in% names(args)) { args$dodged <- TRUE } if (!"colour" %in% names(args)) { args$colour <- "model" } if (!"errorbar_coloured" %in% names(args)) { args$errorbar_coloured <- TRUE } } else { if (!"facet_col" %in% names(args)) { args$facet_col <- "model" } if (!"colour" %in% names(args) & !all(is.na(data$var_label))) { args$colour <- "var_label" if (!"colour_guide" %in% names(args)) { args$colour_guide <- FALSE } } } do.call(ggcoef_plot, args) } #' @describeIn ggcoef_model A variation of [ggcoef_model()] adapted to multinomial logistic regressions performed with [nnet::multinom()]. #' @param y.level_label an optional named vector for labeling `y.level` (see examples) #' @export #' @examples #' #' # specific function for nnet::multinom models #' if (require(broom.helpers) && require(nnet)) { #' data(happy) #' mod <- multinom(happy ~ age + degree + sex, data = happy) #' p_(ggcoef_multinom(mod, exponentiate = TRUE)) #' p_(ggcoef_multinom(mod, type = "faceted")) #' p_(ggcoef_multinom( #' mod, type = "faceted", #' y.level_label = c( #' "pretty happy" = "pretty happy\n(ref: very happy)", #' "very happy" = "very happy" #' ) #' )) #' } ggcoef_multinom <- function ( model, type = c("dodged", "faceted"), y.level_label = NULL, tidy_fun = broom::tidy, conf.int = TRUE, conf.level = .95, exponentiate = FALSE, variable_labels = NULL, term_labels = NULL, interaction_sep = " * ", categorical_terms_pattern = "{level}", add_reference_rows = TRUE, no_reference_row = NULL, intercept = FALSE, include = dplyr::everything(), significance = 1 - conf.level, significance_labels = NULL, show_p_values = TRUE, signif_stars = TRUE, return_data = FALSE, ... ){ data <- ggcoef_data( model, tidy_fun = tidy_fun, conf.int = conf.int, conf.level = conf.level, exponentiate = exponentiate, variable_labels = variable_labels, term_labels = term_labels, interaction_sep = interaction_sep, categorical_terms_pattern = categorical_terms_pattern, add_reference_rows = add_reference_rows, no_reference_row = no_reference_row, intercept = intercept, include = include, significance = significance, significance_labels = significance_labels ) if (!is.null(y.level_label)) data$y.level <- factor( data$y.level, levels = names(y.level_label), labels = y.level_label ) else data$y.level <- forcats::fct_inorder(factor(data$y.level)) if (return_data) return(data) type <- match.arg(type) args <- list(...) args$data <- data args$exponentiate <- exponentiate if (!"y" %in% names(args) && !"facet_row" %in% names(args)) args$y <- "label_light" if (type == "dodged") { if (!"dodged " %in% names(args)) { args$dodged <- TRUE } if (!"colour" %in% names(args)) { args$colour <- "y.level" } if (!"errorbar_coloured" %in% names(args)) { args$errorbar_coloured <- TRUE } } else { if (!"facet_col" %in% names(args)) { args$facet_col <- "y.level" } if (!"colour" %in% names(args) & !all(is.na(data$var_label))) { args$colour <- "var_label" if (!"colour_guide" %in% names(args)) { args$colour_guide <- FALSE } } } do.call(ggcoef_plot, args) } # not exporting ggcoef_data ggcoef_data <- function ( model, tidy_fun = broom::tidy, conf.int = TRUE, conf.level = .95, exponentiate = FALSE, variable_labels = NULL, term_labels = NULL, interaction_sep = " * ", categorical_terms_pattern = "{level}", add_reference_rows = TRUE, no_reference_row = NULL, intercept = FALSE, include = dplyr::everything(), significance = conf.level, significance_labels = NULL ){ if (!requireNamespace("broom.helpers", quietly = TRUE)) stop("Package broom.helpers is required.") if (length(significance) == 0) significance <- NULL data <- broom.helpers::tidy_plus_plus( model = model, tidy_fun = tidy_fun, conf.int = conf.int, conf.level = conf.level, exponentiate = exponentiate, variable_labels = variable_labels, term_labels = term_labels, interaction_sep = interaction_sep, categorical_terms_pattern = categorical_terms_pattern, add_reference_rows = add_reference_rows, no_reference_row = no_reference_row, add_estimate_to_reference_rows = TRUE, add_header_rows = FALSE, intercept = intercept, include = include, keep_model = FALSE ) if (!"p.value" %in% names(data)) { data$p.value <- NA_real_ significance <- NULL } if(!is.null(significance)) { if (is.null(significance_labels)) significance_labels <- paste(c("p \u2264", "p >"), significance) data$significance <- factor( !is.na(data$p.value) & data$p.value <= significance, levels = c(TRUE, FALSE), labels = significance_labels ) } data$signif_stars <- signif_stars(data$p.value, point = NULL) data$p_value_label <- ifelse(is.na(data$p.value), "", scales::pvalue(data$p.value, add_p = TRUE)) # keep only rows with estimate data <- data[!is.na(data$estimate), ] data$var_label <- forcats::fct_inorder(data$var_label) data$label <- forcats::fct_inorder(data$label) data$label_light <- dplyr::if_else( as.character(data$label) == as.character(data$var_label) & ((!grepl("^nmatrix", data$var_class)) | is.na(data$var_class)), "", as.character(data$label) ) %>% forcats::fct_inorder() data } #' @describeIn ggcoef_model SOME DESCRIPTION HERE #' @param data a data frame containing data to be plotted, #' typically the output of [ggcoef_model()], [ggcoef_compare()] #' or [ggcoef_multinom()] with the option `return_data = TRUE` #' @param x,y variables mapped to x and y axis #' @param exponentiate if `TRUE` a logarithmic scale will #' be used for x-axis #' @param point_size size of the points #' @param point_stroke thickness of the points #' @param point_fill fill colour for the points #' @param colour optional variable name to be mapped to #' colour aesthetic #' @param colour_guide should colour guide be displayed #' in the legend? #' @param colour_lab label of the colour aesthetic in the legend #' @param colour_labels labels argument passed to #' [ggplot2::scale_colour_discrete()] and #' [ggplot2::discrete_scale()] #' @param shape optional variable name to be mapped to the #' shape aesthetic #' @param shape_values values of the different shapes to use in #' [ggplot2::scale_shape_manual()] #' @param shape_guide should shape guide be displayed in the legend? #' @param shape_lab label of the shape aesthetic in the legend #' @param errorbar should error bars be plotted? #' @param errorbar_height height of error bars #' @param errorbar_coloured should error bars be colored as the points? #' @param stripped_rows should stripped rows be displayed in the background? #' @param strips_odd color of the odd rows #' @param strips_even color of the even rows #' @param vline should a vertical line be drawn at 0 (or 1 if `exponentiate = TRUE`)? #' @param vline_colour colour of vertical line #' @param dodged should points be dodged (according to the colour aesthetic)? #' @param dodged_width width value for [ggplot2::position_dodge()] #' @param facet_row variable name to be used for row facets #' @param facet_col optional variable name to be used for column facets #' @param facet_labeller labeller function to be used for labeling facets; #' if labels are too long, you can use [ggplot2::label_wrap_gen()] (see examples), #' more information in the documentation of [ggplot2::facet_grid()] #' @export ggcoef_plot <- function ( data, x = "estimate", y = "label", exponentiate = FALSE, point_size = 2, point_stroke = 2, point_fill = "white", colour = NULL, colour_guide = TRUE, colour_lab = "", colour_labels = ggplot2::waiver(), shape = "significance", shape_values = c(16, 21), shape_guide = TRUE, shape_lab = "", errorbar = TRUE, errorbar_height = .1, errorbar_coloured = FALSE, stripped_rows = TRUE, strips_odd = "#11111111", strips_even = "#00000000", vline = TRUE, vline_colour = "grey50", dodged = FALSE, dodged_width = .8, facet_row = "var_label", facet_col = NULL, facet_labeller = "label_value" ){ data[[y]] <- forcats::fct_rev(forcats::fct_inorder(data[[y]])) if (!is.null(facet_row)) data[[facet_row]] <- forcats::fct_inorder(data[[facet_row]]) if (stripped_rows) { if (!"term" %in% names(data)) { data$term <- data[[y]] } data <- data %>% dplyr::mutate(.fill = dplyr::if_else( as.integer(forcats::fct_inorder(.data$term)) %% 2L == 1, strips_even, strips_odd )) } # mapping mapping <- aes_string(x = x, y = y) errorbar <- errorbar & all(c("conf.low", "conf.high") %in% names(data)) if(errorbar) { mapping$xmin <- aes_string(xmin = "conf.low")$xmin mapping$xmax <- aes_string(xmax = "conf.high")$xmax } if(!is.null(shape) && shape %in% names(data)) { mapping$shape <- aes_string(shape = shape)$shape } if(!is.null(colour) && colour %in% names(data)) { mapping$colour <- aes_string(colour = colour)$colour mapping$group <- aes_string(group = colour)$group } # position if (dodged) position <- position_dodge(dodged_width) else position <- position_identity() # plot p <- ggplot(data = data, mapping = mapping) if (stripped_rows) p <- p + geom_stripped_rows( mapping = aes_string( odd = ".fill", even = ".fill", colour = NULL, linetype = NULL ) ) if (vline) p <- p + geom_vline(xintercept = ifelse(exponentiate, 1, 0), colour = vline_colour) if(errorbar) { if (!is.null(colour) & errorbar_coloured) { p <- p + geom_errorbarh( na.rm = TRUE, height = errorbar_height, position = position ) } else { p <- p + geom_errorbarh( mapping = aes(colour = NULL), na.rm = TRUE, height = errorbar_height, colour = "black", position = position ) } } if (!is.null(facet_col) & is.character(facet_col)) facet_col <- vars(!!sym(facet_col)) if (!is.null(facet_row) & is.character(facet_row)) facet_row <- vars(!!sym(facet_row)) p <- p + geom_point( size = point_size, stroke = point_stroke, fill = point_fill, position = position, na.rm = TRUE ) + facet_grid( rows = facet_row, cols = facet_col, labeller = facet_labeller, scales = "free_y", space = "free_y", switch = "y" ) + ylab("") + scale_y_discrete(expand = expansion(mult = 0, add = .5)) + theme_light() + theme( legend.position = "bottom", legend.box = "vertical", strip.placement = "outside", strip.text.y.left = element_text(face = "bold", angle = 0, colour = "black", hjust = 0, vjust = 1), strip.text.x = element_text(face = "bold", colour = "black"), strip.background = element_blank(), panel.grid.minor = element_blank(), panel.grid.major.y = element_blank(), panel.grid.major.x = element_line(linetype = "dashed"), axis.title.x = element_text(face = "bold"), axis.ticks.y = element_blank() ) if(!is.null(colour) && colour %in% names(data)) { if (colour_guide) { colour_guide <- guide_legend() } else { colour_guide <- "none" } p <- p + scale_colour_discrete(guide = colour_guide, labels = colour_labels) + labs(colour = colour_lab) } if(!is.null(shape) && shape %in% names(data)) { if (shape_guide) { shape_guide <- guide_legend() } else { shape_guide <- "none" } p <- p + scale_shape_manual( values = shape_values, drop = FALSE, guide = shape_guide, na.translate = FALSE ) + labs(shape = shape_lab) } if (exponentiate) p <- p + scale_x_log10() if (!is.null(attr(data, "coefficients_label"))) p <- p + xlab(attr(data, "coefficients_label")) p } GGally/R/utils.R0000644000176200001440000000211313665760216013126 0ustar liggesusers #' Print if not CRAN #' #' Small function to print a plot if the R session is interactive or in a CI build #' #' @param p plot to be displayed #' @export print_if_interactive <- function(p) { if (interactive() || nzchar(Sys.getenv("CAN_PRINT"))) { print(p) } } #' Loads package namespaces #' #' Loads package namespaces or yells at user... loudly #' #' @param pkgs vector of character values #' @keywords internal require_namespaces <- function(pkgs) { for (pkg in pkgs) { if (! requireNamespace(pkg, quietly = TRUE)) { stop(str_c("please install the package '", pkg, "'. install.packages('", pkg, "') ")) } } } str_c <- function (..., sep = "", collapse = NULL) { paste(..., sep = sep, collapse = collapse) } str_detect <- function(string, pattern, ...) { grepl(pattern, string, ...) } # str_replace <- function(string, pattern, replacement) { # sub(pattern, replacement, string) # } ifnull <- function(a, b) { if (!is.null(a)) { a } else { b } } hf <- function(field) { eval(parse(text = read.dcf(".helper_functions", fields = field))) } GGally/R/stat_cross.R0000644000176200001440000003362214021227576014155 0ustar liggesusers#' Compute cross-tabulation statistics #' #' Computes statistics of a 2-dimensional matrix using \code{\link[broom]{augment.htest}} #' from \pkg{broom}. #' #' @inheritParams ggplot2::stat_identity #' @param geom Override the default connection between \code{\link[ggplot2]{geom_point}} #' and \code{stat_prop}. #' @param na.rm If \code{TRUE}, the default, missing values are removed with a warning. #' If `TRUE`, missing values are silently removed. #' @param keep.zero.cells If \code{TRUE}, cells with no observations are kept. #' @section Aesthetics: #' \code{stat_prop} requires the \strong{x} and the \strong{y} aesthetics. #' @section Computed variables: #' \describe{ #' \item{observed}{number of observations in x,y} #' \item{prop}{proportion of total} #' \item{row.prop}{row proportion} #' \item{col.prop}{column proportion} #' \item{expected}{expected count under the null hypothesis} #' \item{resid}{Pearson's residual} #' \item{std.resid}{standardized residual} #' } #' #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' d <- as.data.frame(Titanic) #' #' # plot number of observations #' p_(ggplot(d) + #' aes(x = Class, y = Survived, weight = Freq, size = after_stat(observed)) + #' stat_cross() + #' scale_size_area(max_size = 20)) #' #' # custom shape and fill colour based on chi-squared residuals #' p_(ggplot(d) + #' aes( #' x = Class, y = Survived, weight = Freq, #' size = after_stat(observed), fill = after_stat(std.resid) #' ) + #' stat_cross(shape = 22) + #' scale_fill_steps2(breaks = c(-3, -2, 2, 3), show.limits = TRUE) + #' scale_size_area(max_size = 20)) #' #' # plotting the number of observations as a table #' p_(ggplot(d) + #' aes( #' x = Class, y = Survived, weight = Freq, label = after_stat(observed) #' ) + #' geom_text(stat = "cross")) #' #' # Row proportions with standardized residuals #' p_(ggplot(d) + #' aes( #' x = Class, y = Survived, weight = Freq, #' label = scales::percent(after_stat(row.prop)), #' size = NULL, fill = after_stat(std.resid) #' ) + #' stat_cross(shape = 22, size = 30) + #' geom_text(stat = "cross") + #' scale_fill_steps2(breaks = c(-3, -2, 2, 3), show.limits = TRUE) + #' facet_grid(Sex ~ .) + #' labs(fill = "Standardized residuals") + #' theme_minimal()) #' #' # can work with continuous or character variables #' data(tips, package = "reshape") #' p_(ggplot(tips) + #' aes(x = tip, y = as.character(day), size = after_stat(observed)) + #' stat_cross(alpha = .1, color = "blue") + #' scale_size_area(max_size = 12)) stat_cross <- function(mapping = NULL, data = NULL, geom = "point", position = "identity", ..., na.rm = TRUE, show.legend = NA, inherit.aes = TRUE, keep.zero.cells = FALSE) { params <- list( na.rm = na.rm, keep.zero.cells = keep.zero.cells, ... ) layer( data = data, mapping = mapping, stat = StatCross, geom = geom, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = params ) } #' @rdname stat_cross #' @format NULL #' @usage NULL #' @export StatCross <- ggproto("StatCross", Stat, required_aes = c("x", "y"), default_aes = aes( weight = 1 ), setup_params = function(data, params) { params }, extra_params = c("na.rm"), compute_panel = function(self, data, scales, keep.zero.cells = FALSE) { if (is.null(data$weight)) data$weight <- rep(1, nrow(data)) # compute cross statistics panel <- broom::augment(chisq.test(xtabs(weight ~ y + x, data = data))) panel_names <- names(panel) for (to_name in c( "observed", "prop", "row.prop", "col.prop", "expected", "resid", "std.resid" )) { from_name <- paste0(".", to_name) panel_names[which(panel_names == from_name)] <- to_name } names(panel) <- panel_names # to handle the fact that ggplot2 could transform factors into integers # before computation of the statistic if(is.numeric(data$x)) panel$x <- as.numeric(panel$x) if(is.numeric(data$y)) panel$y <- as.numeric(panel$y) # keeping first value of other aesthetics in data panel <- merge( panel, dplyr::select(data, -.data$PANEL), by = c("x", "y"), all.x = TRUE ) panel <- panel %>% dplyr::distinct(.data$x, .data$y, .keep_all = TRUE) if (!keep.zero.cells) { panel <- panel[panel$observed != 0,] } panel } ) #' Plots the number of observations #' #' Plot the number of observations by using square points #' with proportional areas. Could be filled according to chi-squared #' statistics computed by [stat_cross()]. Labels could also #' be added (see examples). #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... other arguments passed to [ggplot2::geom_point()] #' @param scale_max_size `max_size` argument supplied to [ggplot2::scale_size_area()] #' @param geom_text_args other arguments passed to [ggplot2::geom_text()] #' @author Joseph Larmarange #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_cross(tips, mapping = aes(x = smoker, y = sex))) #' p_(ggally_cross(tips, mapping = aes(x = day, y = time))) #' #' # Custom max size #' p_(ggally_cross(tips, mapping = aes(x = smoker, y = sex)) + #' scale_size_area(max_size = 40)) #' #' # Custom fill #' p_(ggally_cross(tips, mapping = aes(x = smoker, y = sex), fill = "red")) #' #' # Custom shape #' p_(ggally_cross(tips, mapping = aes(x = smoker, y = sex), shape = 21)) #' #' # Fill squares according to standardized residuals #' d <- as.data.frame(Titanic) #' p_(ggally_cross( #' d, #' mapping = aes(x = Class, y = Survived, weight = Freq, fill = after_stat(std.resid)) #' ) + #' scale_fill_steps2(breaks = c(-3, -2, 2, 3), show.limits = TRUE)) #' #' # Add labels #' p_(ggally_cross( #' tips, #' mapping = aes( #' x = smoker, y = sex, colour = smoker, #' label = scales::percent(after_stat(prop)) #' ) #' )) #' #' # Customize labels' appearance and same size for all squares #' p_(ggally_cross( #' tips, #' mapping = aes( #' x = smoker, y = sex, #' size = NULL, # do not map size to a variable #' label = scales::percent(after_stat(prop)) #' ), #' size = 40, # fix value for points size #' fill = "darkblue", #' geom_text_args = list(colour = "white", fontface = "bold", size = 6) #' )) ggally_cross <- function(data, mapping, ..., scale_max_size = 20, geom_text_args = NULL){ mapping <- remove_color_unless_equal(mapping, to = c("x", "y")) mapping <- mapping_color_to_fill(mapping) args <- list(...) # default values for geom_point if (!"size" %in% names(mapping)) { mapping$size <- aes_string(size = "after_stat(observed)")$size } if (is.null(mapping$shape) & is.null(args$shape)) { args$shape <- 22 } if (is.null(mapping$fill) & is.null(args$fill)) { args$fill <- GeomRect$default_aes$fill } args$keep.zero.cells <- FALSE p <- ggplot(data = data, mapping) + do.call(stat_cross, args) + scale_size_area(max_size = scale_max_size) # default values for geom_text geom_text_args$stat <- "cross" geom_text_args$keep.zero.cells <- FALSE if (is.null(geom_text_args$mapping)) geom_text_args$mapping <- aes(colour = NULL, size = NULL) if (is.null(geom_text_args$show.legend)) geom_text_args$show.legend <- FALSE if (!is.null(mapping$label)) { p <- p + do.call(geom_text, geom_text_args) } p } #' Display a table of the number of observations #' #' Plot the number of observations as a table. Other statistics computed #' by \code{\link{stat_cross}} could be used (see examples). #' #' @param data data set using #' @param mapping aesthetics being used #' @param keep.zero.cells If \code{TRUE}, display cells with no observation. #' @param ... other arguments passed to \code{\link[ggplot2]{geom_text}(...)} #' @param geom_tile_args other arguments passed to \code{\link[ggplot2]{geom_tile}(...)} #' @note The \strong{colour} aesthetic is taken into account only if equal to #' \strong{x} or \strong{y}. #' @author Joseph Larmarange #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_table(tips, mapping = aes(x = smoker, y = sex))) #' p_(ggally_table(tips, mapping = aes(x = day, y = time))) #' p_(ggally_table(tips, mapping = aes(x = smoker, y = sex, colour = smoker))) #' #' # colour is kept only if equal to x or y #' p_(ggally_table(tips, mapping = aes(x = smoker, y = sex, colour = day))) #' #' # diagonal version #' p_(ggally_tableDiag(tips, mapping = aes(x = smoker))) #' #' # custom label size and color #' p_(ggally_table(tips, mapping = aes(x = smoker, y = sex), size = 16, color = "red")) #' #' # display column proportions #' p_(ggally_table( #' tips, #' mapping = aes(x = day, y = sex, label = scales::percent(after_stat(col.prop))) #' )) #' #' # draw table cells #' p_(ggally_table( #' tips, #' mapping = aes(x = smoker, y = sex), #' geom_tile_args = list(colour = "black", fill = "white") #' )) #' #' # Use standardized residuals to fill table cells #' p_(ggally_table( #' as.data.frame(Titanic), #' mapping = aes( #' x = Class, y = Survived, weight = Freq, #' fill = after_stat(std.resid), #' label = scales::percent(after_stat(col.prop), accuracy = .1) #' ), #' geom_tile_args = list(colour = "black") #' ) + #' scale_fill_steps2(breaks = c(-3, -2, 2, 3), show.limits = TRUE)) ggally_table <- function(data, mapping, keep.zero.cells = FALSE, ..., geom_tile_args = NULL){ mapping <- remove_color_unless_equal(mapping, to = c("x", "y")) # default values geom_text if (!"label" %in% names(mapping)) { mapping$label <- aes_string(label = "after_stat(observed)")$label } geom_text_args <- list(...) geom_text_args$stat <- "cross" geom_text_args$keep.zero.cells <- keep.zero.cells # default values geom_tile geom_tile_args$stat <- "cross" geom_tile_args$keep.zero.cells <- keep.zero.cells geom_tile_args$mapping <- aes(colour = NULL)$colour if (is.null(geom_tile_args$colour)) geom_tile_args$colour <- "transparent" if (is.null(mapping$fill) & is.null(geom_tile_args$fill)) geom_tile_args$fill <- "transparent" ggplot(data = data, mapping) + do.call(geom_tile, geom_tile_args) + do.call(geom_text, geom_text_args) } #' @export #' @rdname ggally_table ggally_tableDiag <- function(data, mapping, keep.zero.cells = FALSE, ..., geom_tile_args = NULL) { mapping$y <- mapping$x ggally_table( data = data, mapping = mapping, keep.zero.cells = keep.zero.cells, ..., geom_tile_args = geom_tile_args ) } #' Display a cross-tabulated table #' #' \code{ggally_crosstable} is a variation of \code{\link{ggally_table}} with few modifications: (i) table cells are drawn; (ii) x and y axis are not expanded (and therefore are not aligned with other \code{ggally_*} plots); (iii) content and fill of cells can be easily controlled with dedicated arguments. #' @param data data set using #' @param mapping aesthetics being used #' @param cells Which statistic should be displayed in table cells? #' @param fill Which statistic should be used for filling table cells? #' @param ... other arguments passed to \code{\link[ggplot2]{geom_text}(...)} #' @param geom_tile_args other arguments passed to \code{\link[ggplot2]{geom_tile}(...)} #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' #' # differences with ggally_table() #' p_(ggally_table(tips, mapping = aes(x = day, y = time))) #' p_(ggally_crosstable(tips, mapping = aes(x = day, y = time))) #' #' # display column proportions #' p_(ggally_crosstable(tips, mapping = aes(x = day, y = sex), cells = "col.prop")) #' #' # display row proportions #' p_(ggally_crosstable(tips, mapping = aes(x = day, y = sex), cells = "row.prop")) #' #' # change size of text #' p_(ggally_crosstable(tips, mapping = aes(x = day, y = sex), size = 8)) #' #' # fill cells with standardized residuals #' p_(ggally_crosstable(tips, mapping = aes(x = day, y = sex), fill = "std.resid")) #' #' # change scale for fill #' p_(ggally_crosstable(tips, mapping = aes(x = day, y = sex), fill = "std.resid") + #' scale_fill_steps2(breaks = c(-2, 0, 2), show.limits = TRUE)) ggally_crosstable <- function( data, mapping, cells = c("observed", "prop", "row.prop", "col.prop", "expected", "resid", "std.resid"), fill = c("none", "std.resid", "resid"), ..., geom_tile_args = list(colour = "grey50") ){ fill <- match.arg(fill) if (fill == "std.resid") mapping$fill <- aes_string(fill = "after_stat(std.resid)")$fill if (fill == "resid") mapping$fill <- aes_string(fill = "after_stat(resid)")$fill if (fill == "none") geom_tile_args$fill <- "white" cells <- match.arg(cells) if (!"label" %in% names(mapping) && cells %in% c("observed", "expected")) mapping$label <- aes_string(label = paste0("scales::number(after_stat(", cells, "), accuracy = 1)"))$label if (!"label" %in% names(mapping) && cells %in% c("prop", "row.prop", "col.prop")) mapping$label <- aes_string(label = paste0("scales::percent(after_stat(", cells, "), accuracy = .1)"))$label if (!"label" %in% names(mapping) && cells %in% c("resid", "std.resid")) mapping$label <- aes_string(label = paste0("scales::number(after_stat(", cells, "), accuracy = .1)"))$label p <- ggally_table(data = data, mapping = mapping, keep.zero.cells = TRUE, geom_tile_args = geom_tile_args, ...) + scale_x_discrete(expand = expansion(0, 0)) + scale_y_discrete(expand = expansion(0, 0)) + theme(axis.ticks = element_blank()) if (fill == "std.resid") p <- p + scale_fill_steps2(breaks = c(-Inf, -3, -2, 2, 3, Inf)) p } GGally/R/deprecated.R0000644000176200001440000002023513666472400014067 0ustar liggesusers#' Modify a \code{\link{ggmatrix}} object by adding an \pkg{ggplot2} object to all #' # \lifecycle{deprecated} #' #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' p_(ggpairs(iris, 1:2) + v1_ggmatrix_theme()) #' # move the column names to the left and bottom #' p_(ggpairs(iris, 1:2, switch = "both") + v1_ggmatrix_theme()) v1_ggmatrix_theme <- function() { theme( strip.background = element_rect(fill = "white"), strip.placement = "outside" ) } #' Correlation value plot #' # \lifecycle{deprecated} #' #' (Deprecated. See \code{\link{ggally_cor}}.) #' #' Estimate correlation from the given data. #' #' @param data data set using #' @param mapping aesthetics being used #' @param alignPercent right align position of numbers. Default is 60 percent across the horizontal #' @param method \code{method} supplied to cor function #' @param use \code{use} supplied to cor function #' @param corAlignPercent deprecated. Use parameter \code{alignPercent} #' @param corMethod deprecated. Use parameter \code{method} #' @param corUse deprecated. Use parameter \code{use} #' @param displayGrid if TRUE, display aligned panel gridlines #' @param ... other arguments being supplied to geom_text #' @author Barret Schloerke #' @importFrom stats complete.cases cor #' @seealso \code{\link{ggally_cor}} #' @export #' @keywords hplot #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_cor_v1_5(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip"))) #' #' # display with no grid #' p_(ggally_cor_v1_5( #' tips, #' mapping = ggplot2::aes_string(x = "total_bill", y = "tip"), #' displayGrid = FALSE #' )) #' #' # change text attributes #' p_(ggally_cor_v1_5( #' tips, #' mapping = ggplot2::aes(x = total_bill, y = tip), #' size = 15, #' colour = I("red") #' )) #' #' # split by a variable #' p_(ggally_cor_v1_5( #' tips, #' mapping = ggplot2::aes_string(x = "total_bill", y = "tip", color = "sex"), #' size = 5 #' )) ggally_cor_v1_5 <- function( data, mapping, alignPercent = 0.6, method = "pearson", use = "complete.obs", corAlignPercent = NULL, corMethod = NULL, corUse = NULL, displayGrid = TRUE, ... ){ if (! is.null(corAlignPercent)) { stop("'corAlignPercent' is deprecated. Please use argument 'alignPercent'") } if (! is.null(corMethod)) { stop("'corMethod' is deprecated. Please use argument 'method'") } if (! is.null(corUse)) { stop("'corUse' is deprecated. Please use argument 'use'") } useOptions <- c( "all.obs", "complete.obs", "pairwise.complete.obs", "everything", "na.or.complete" ) use <- pmatch(use, useOptions) if (is.na(use)) { warning("correlation 'use' not found. Using default value of 'all.obs'") use <- useOptions[1] } else { use <- useOptions[use] } cor_fn <- function(x, y) { # also do ddply below if fn is altered cor(x, y, method = method, use = use) } # xVar <- data[[as.character(mapping$x)]] # yVar <- data[[as.character(mapping$y)]] # x_bad_rows <- is.na(xVar) # y_bad_rows <- is.na(yVar) # bad_rows <- x_bad_rows | y_bad_rows # if (any(bad_rows)) { # total <- sum(bad_rows) # if (total > 1) { # warning("Removed ", total, " rows containing missing values") # } else if (total == 1) { # warning("Removing 1 row that contained a missing value") # } # # xVar <- xVar[!bad_rows] # yVar <- yVar[!bad_rows] # } # mapping$x <- mapping$y <- NULL xData <- eval_data_col(data, mapping$x) yData <- eval_data_col(data, mapping$y) if (is_date(xData)) { xData <- as.numeric(xData) } if (is_date(yData)) { yData <- as.numeric(yData) } colorData <- eval_data_col(data, mapping$colour) if (is.numeric(colorData)) { stop("ggally_cor: mapping color column must be categorical, not numeric") } if (use %in% c("complete.obs", "pairwise.complete.obs", "na.or.complete")) { if (!is.null(colorData) && (length(colorData) == length(xData))) { rows <- complete.cases(xData, yData, colorData) } else { rows <- complete.cases(xData, yData) } if (any(!rows)) { total <- sum(!rows) if (total > 1) { warning("Removed ", total, " rows containing missing values") } else if (total == 1) { warning("Removing 1 row that contained a missing value") } } if (!is.null(colorData) && (length(colorData) == length(xData))) { colorData <- colorData[rows] } xData <- xData[rows] yData <- yData[rows] } xVal <- xData yVal <- yData # if the mapping has to deal with the data, remove it if (packageVersion("ggplot2") > "2.2.1") { for (mappingName in names(mapping)) { itemData <- eval_data_col(data, mapping[[mappingName]]) if (!inherits(itemData, "AsIs")) { mapping[[mappingName]] <- NULL } } } else { if (length(names(mapping)) > 0){ for (i in length(names(mapping)):1){ # find the last value of the aes, such as cyl of as.factor(cyl) tmp_map_val <- deparse(mapping[names(mapping)[i]][[1]]) if (tmp_map_val[length(tmp_map_val)] %in% colnames(data)) mapping[[names(mapping)[i]]] <- NULL if (length(names(mapping)) < 1){ mapping <- NULL break; } } } } if ( !is.null(colorData) && !inherits(colorData, "AsIs") ) { cord <- ddply( data.frame(x = xData, y = yData, color = colorData), "color", function(dt) { cor_fn(dt$x, dt$y) } ) colnames(cord)[2] <- "correlation" cord$correlation <- signif(as.numeric(cord$correlation), 3) # put in correct order lev <- levels(as.factor(colorData)) ord <- rep(-1, nrow(cord)) for (i in 1:nrow(cord)) { for (j in seq_along(lev)){ if (identical(as.character(cord$color[i]), as.character(lev[j]))) { ord[i] <- j } } } # print(order(ord[ord >= 0])) # print(lev) cord <- cord[order(ord[ord >= 0]), ] cord$label <- str_c(cord$color, ": ", cord$correlation) # calculate variable ranges so the gridlines line up xmin <- min(xVal, na.rm = TRUE) xmax <- max(xVal, na.rm = TRUE) xrange <- c(xmin - 0.01 * (xmax - xmin), xmax + 0.01 * (xmax - xmin)) ymin <- min(yVal, na.rm = TRUE) ymax <- max(yVal, na.rm = TRUE) yrange <- c(ymin - 0.01 * (ymax - ymin), ymax + 0.01 * (ymax - ymin)) # print(cord) p <- ggally_text( label = str_c("Corr: ", signif(cor_fn(xVal, yVal), 3)), mapping = mapping, xP = 0.5, yP = 0.9, xrange = xrange, yrange = yrange, ... ) xPos <- rep(alignPercent, nrow(cord)) * diff(xrange) + min(xrange, na.rm = TRUE) yPos <- seq( from = 0.9, to = 0.2, length.out = nrow(cord) + 1) yPos <- yPos * diff(yrange) + min(yrange, na.rm = TRUE) yPos <- yPos[-1] # print(range(yVal)) # print(yPos) cordf <- data.frame(xPos = xPos, yPos = yPos, labelp = cord$label) cordf$labelp <- factor(cordf$labelp, levels = cordf$labelp) # print(cordf) # print(str(cordf)) p <- p + geom_text( data = cordf, aes( x = xPos, y = yPos, label = labelp, color = labelp ), hjust = 1, ... ) } else { # calculate variable ranges so the gridlines line up xmin <- min(xVal, na.rm = TRUE) xmax <- max(xVal, na.rm = TRUE) xrange <- c(xmin - 0.01 * (xmax - xmin), xmax + 0.01 * (xmax - xmin)) ymin <- min(yVal, na.rm = TRUE) ymax <- max(yVal, na.rm = TRUE) yrange <- c(ymin - 0.01 * (ymax - ymin), ymax + 0.01 * (ymax - ymin)) p <- ggally_text( label = paste( "Corr:\n", signif( cor_fn(xVal, yVal), 3 ), sep = "", collapse = "" ), mapping, xP = 0.5, yP = 0.5, xrange = xrange, yrange = yrange, ... ) } if (!isTRUE(displayGrid)) { p <- p + theme( panel.grid.major = element_blank(), panel.grid.minor = element_blank() ) } p + theme(legend.position = "none") } GGally/R/GGally-package.R0000644000176200001440000000123713777103031014532 0ustar liggesusers#' @keywords internal "_PACKAGE" # The following block is used by usethis to automatically manage # roxygen namespace tags. Modify with care! ## usethis namespace: start #' @importFrom lifecycle deprecate_soft ## usethis namespace: end NULL # \lifecycle{experimental} # \lifecycle{maturing} # \lifecycle{stable} # \lifecycle{superseded} # \lifecycle{questioning} # \lifecycle{soft-deprecated} # \lifecycle{deprecated} # \lifecycle{defunct} # \lifecycle{archived} #' Pipe operator #' #' See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. #' #' @name %>% #' @rdname pipe #' @keywords internal #' @export #' @importFrom dplyr %>% #' @usage lhs \%>\% rhs NULL GGally/R/ggfacet.R0000644000176200001440000001051113665760216013367 0ustar liggesusers#' Single \pkg{ggplot2} plot matrix with \code{\link[ggplot2]{facet_grid}} #' #' #' @param data data.frame that contains all columns to be displayed. This data will be melted before being passed into the function \code{fn} #' @param mapping aesthetic mapping (besides \code{x} and \code{y}). See \code{\link[ggplot2]{aes}()} #' @param fn function to be executed. Similar to \code{\link{ggpairs}} and \code{\link{ggduo}}, the function may either be a string identifier or a real function that \code{\link{wrap}} understands. #' @param ... extra arguments passed directly to \code{fn} #' @param columnsX columns to be displayed in the plot matrix #' @param columnsY rows to be displayed in the plot matrix #' @param columnLabelsX,columnLabelsY column and row labels to display in the plot matrix #' @param xlab,ylab,title plot matrix labels #' @param scales parameter supplied to \code{ggplot2::\link[ggplot2]{facet_grid}}. Default behavior is \code{"free"} #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' if (requireNamespace("chemometrics", quietly = TRUE)) { #' data(NIR, package = "chemometrics") #' NIR_sub <- data.frame(NIR$yGlcEtOH, NIR$xNIR[,1:3]) #' str(NIR_sub) #' x_cols <- c("X1115.0", "X1120.0", "X1125.0") #' y_cols <- c("Glucose", "Ethanol") #' #' # using ggduo directly #' p <- ggduo(NIR_sub, x_cols, y_cols, types = list(continuous = "points")) #' p_(p) #' #' # using ggfacet #' p <- ggfacet(NIR_sub, x_cols, y_cols) #' p_(p) #' #' # add a smoother #' p <- ggfacet(NIR_sub, x_cols, y_cols, fn = 'smooth_loess') #' p_(p) #' # same output #' p <- ggfacet(NIR_sub, x_cols, y_cols, fn = ggally_smooth_loess) #' p_(p) #' #' # Change scales to be the same in for every row and for every column #' p <- ggfacet(NIR_sub, x_cols, y_cols, scales = "fixed") #' p_(p) #' } ggfacet <- function( data, mapping = NULL, columnsX = 1:ncol(data), columnsY = 1:ncol(data), fn = ggally_points, ..., columnLabelsX = names(data[columnsX]), columnLabelsY = names(data[columnsY]), xlab = NULL, ylab = NULL, title = NULL, scales = "free" ) { data <- fix_data(data) fn <- wrap(fn) # fix args if ( !missing(mapping) & !is.list(mapping) & !missing(columnsX) & missing(columnsY) ) { columnsY <- columnsX columnsX <- mapping mapping <- NULL } stop_if_bad_mapping(mapping) columnsX <- fix_column_values(data, columnsX, columnLabelsX, "columnsX", "columnLabelsX") columnsY <- fix_column_values(data, columnsY, columnLabelsY, "columnsY", "columnLabelsY") # could theoretically work like # mtc <- mtcars # mtc$am <- as.factor(mtc$am) # mtc$cyl <- as.factor(mtc$cyl) # ggfacet( # mtc, # columnsY = c(1,3,4,5), columnsX = c("am", "cyl"), # fn = function(data, mapping){ggplot(data, mapping) + geom_boxplot()} # ) is_factor_x <- sapply(data[columnsX], is.factor) if (sum(is_factor_x) != 0) { warning(paste(sum(is_factor_x), " factor variables are being removed from X columns", sep = "")) columnsX <- columnsX[!is_factor_x] columnLabelsX <- columnLabelsX[!is_factor_x] } is_factor_y <- sapply(data[columnsY], is.factor) if (sum(is_factor_y) != 0) { warning(paste(sum(is_factor_y), " factor variables are being removed from Y columns", sep = "")) columnsY <- columnsY[!is_factor_y] columnLabelsY <- columnLabelsY[!is_factor_y] } tall_data <- ddply( expand.grid(.x_col = columnsX, .y_col = columnsY), c(".x_col", ".y_col"), function(row) { x_var <- row$.x_col[1] y_var <- row$.y_col[1] ret <- data ret[[".x_val"]] <- data[[x_var]] ret[[".y_val"]] <- data[[y_var]] ret } ) if (is.null(mapping)) { mapping <- aes() } mapping[c("x", "y")] <- aes_string(x = ".x_val", y = ".y_val") names(columnLabelsX) <- as.character(columnsX) names(columnLabelsY) <- as.character(columnsY) labeller <- function(vals) { val_names <- names(vals) if (".x_col" %in% val_names) { vals[[".x_col"]] <- columnLabelsX[as.character(vals[[".x_col"]])] } if (".y_col" %in% val_names) { vals[[".y_col"]] <- columnLabelsY[as.character(vals[[".y_col"]])] } vals } p <- fn(tall_data, mapping, ...) + facet_grid(.y_col ~ .x_col, labeller = labeller, scales = scales) + labs(title = title, x = xlab, y = ylab) p } GGally/R/data-pigs.R0000644000176200001440000000214613663637143013645 0ustar liggesusers#' United Kingdom Pig Production #' #' This data contains about the United Kingdom Pig Production from the book 'Data' by Andrews and Herzberg. The original data can be on Statlib: http://lib.stat.cmu.edu/datasets/Andrews/T62.1 #' #' The time variable has been added from a combination of year and quarter #' #' @details \itemize{ #' \item time year + (quarter - 1) / 4 #' \item year year of production #' \item quarter quarter of the year of production #' \item gilts number of sows giving birth for the first time #' \item profit ratio of price to an index of feed price #' \item s_per_herdsz ratio of the number of breeding pigs slaughtered to the total breeding herd size #' \item production number of pigs slaughtered that were reared for meat #' \item herdsz breeding herd size #' } #' #' @docType data #' @keywords datasets #' @name pigs #' @usage data(pigs) #' @format A data frame with 48 rows and 8 variables #' @references #' Andrews, David F., and Agnes M. Herzberg. Data: a collection of problems from many fields for the student and research worker. Springer Science & Business Media, 2012. NULL GGally/R/ggsurv.R0000644000176200001440000002672414063456663013322 0ustar liggesusersif(getRversion() >= "2.15.1") { utils::globalVariables(c("cens", "surv", "up", "low")) } #' Survival curves #' #' This function produces Kaplan-Meier plots using \pkg{ggplot2}. #' As a first argument it needs a \code{survfit} object, created by the #' \code{survival} package. Default settings differ for single stratum and #' multiple strata objects. #' #' @export #' @param s an object of class \code{survfit} #' @param CI should a confidence interval be plotted? Defaults to \code{TRUE} #' for single stratum objects and \code{FALSE} for multiple strata objects. #' @param plot.cens mark the censored observations? #' @param surv.col colour of the survival estimate. Defaults to black for #' one stratum, and to the default \pkg{ggplot2} colours for multiple #' strata. Length of vector with colour names should be either 1 or equal #' to the number of strata. #' @param cens.col colour of the points that mark censored observations. #' @param lty.est linetype of the survival curve(s). Vector length should be #' either 1 or equal to the number of strata. #' @param lty.ci linetype of the bounds that mark the 95% CI. #' @param size.est line width of the survival curve #' @param size.ci line width of the 95% CI #' @param cens.size point size of the censoring points #' @param cens.shape shape of the points that mark censored observations. #' @param back.white if TRUE the background will not be the default #' grey of \code{ggplot2} but will be white with borders around the plot. #' @param xlab the label of the x-axis. #' @param ylab the label of the y-axis. #' @param main the plot label. #' @param order.legend boolean to determine if the legend display should be ordered by final survival time #' @return An object of class \code{ggplot} #' @author Edwin Thoen #' @importFrom stats time #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' if (require(survival) && require(scales)) { #' data(lung, package = "survival") #' sf.lung <- survival::survfit(Surv(time, status) ~ 1, data = lung) #' p_(ggsurv(sf.lung)) #' #' # Multiple strata examples #' sf.sex <- survival::survfit(Surv(time, status) ~ sex, data = lung) #' pl.sex <- ggsurv(sf.sex) #' p_(pl.sex) #' #' # Adjusting the legend of the ggsurv fit #' p_(pl.sex + #' ggplot2::guides(linetype = "none") + #' ggplot2::scale_colour_discrete( #' name = 'Sex', #' breaks = c(1,2), #' labels = c('Male', 'Female') #' )) #' #' # Multiple factors #' lung2 <- plyr::mutate(lung, older = as.factor(age > 60)) #' sf.sex2 <- survival::survfit(Surv(time, status) ~ sex + older, data = lung2) #' pl.sex2 <- ggsurv(sf.sex2) #' p_(pl.sex2) #' #' # Change legend title #' p_(pl.sex2 + labs(color = "New Title", linetype = "New Title")) #' #' # We can still adjust the plot after fitting #' data(kidney, package = "survival") #' sf.kid <- survival::survfit(Surv(time, status) ~ disease, data = kidney) #' pl.kid <- ggsurv(sf.kid, plot.cens = FALSE) #' p_(pl.kid) #' #' # Zoom in to first 80 days #' p_(pl.kid + ggplot2::coord_cartesian(xlim = c(0, 80), ylim = c(0.45, 1))) #' #' # Add the diseases names to the plot and remove legend #' p_(pl.kid + #' ggplot2::annotate( #' "text", #' label = c("PKD", "Other", "GN", "AN"), #' x = c(90, 125, 5, 60), #' y = c(0.8, 0.65, 0.55, 0.30), #' size = 5, #' colour = scales::hue_pal( #' h = c(0, 360) + 15, #' c = 100, #' l = 65, #' h.start = 0, #' direction = 1 #' )(4) #' ) + #' ggplot2::guides(color = "none", linetype = "none")) #' } ggsurv <- function( s, CI = 'def', plot.cens = TRUE, surv.col = 'gg.def', cens.col = 'gg.def', lty.est = 1, lty.ci = 2, size.est = 0.5, size.ci = size.est, cens.size = 2, cens.shape = 3, back.white = FALSE, xlab = 'Time', ylab = 'Survival', main = '', order.legend = TRUE ){ require_namespaces(c("survival", "scales")) strata <- ifelse(is.null(s$strata) == TRUE, 1, length(s$strata)) stopifnot(length(surv.col) == 1 | length(surv.col) == strata) stopifnot(length(lty.est) == 1 | length(lty.est) == strata) if(strata == 1) { fn <- ggsurv_s } else { fn <- ggsurv_m } pl <- fn( s, CI , plot.cens, surv.col, cens.col, lty.est, lty.ci, size.est, size.ci, cens.size, cens.shape, back.white, xlab, ylab, main, strata, order.legend ) pl } # survival function for single survival ggsurv_s <- function( s, CI = 'def', plot.cens = TRUE, surv.col = 'gg.def', cens.col = 'gg.def', lty.est = 1, lty.ci = 2, size.est = 0.5, size.ci = size.est, cens.size = 2, cens.shape = 3, back.white = FALSE, xlab = 'Time', ylab = 'Survival', main = '', strata = 1, order.legend = TRUE ){ dat <- data.frame( time = c(0, s$time), surv = c(1, s$surv), up = c(1, s$upper), low = c(1, s$lower), cens = c(0, s$n.censor) ) dat.cens <- subset(dat, cens != 0) col <- ifelse(surv.col == 'gg.def', 'black', surv.col) pl <- ggplot(dat, aes(x = time, y = surv)) + geom_step(col = col, lty = lty.est, size = size.est) + xlab(xlab) + ylab(ylab) + ggtitle(main) if(identical(CI, TRUE) | identical(CI, 'def')) { pl <- pl + geom_step(aes(y = up), color = col, lty = lty.ci, size = size.ci) + geom_step(aes(y = low), color = col, lty = lty.ci, size = size.ci) } if (identical(plot.cens, TRUE) ) { if (nrow(dat.cens) == 0){ stop('There are no censored observations') } col <- ifelse(cens.col == 'gg.def', 'red', cens.col) pl <- pl + geom_point( data = dat.cens, mapping = aes(y = surv), shape = cens.shape, col = col, size = cens.size ) } if(back.white == TRUE) { pl <- pl + theme_bw() } pl } # survival function for multiple survivals ggsurv_m <- function( s, CI = 'def', plot.cens = TRUE, surv.col = 'gg.def', cens.col = 'gg.def', lty.est = 1, lty.ci = 2, size.est = 0.5, size.ci = size.est, cens.size = 2, cens.shape = 3, back.white = FALSE, xlab = 'Time', ylab = 'Survival', main = '', strata = length(s$strata), order.legend = TRUE ) { n <- s$strata has_many <- all(grepl(",", names(s$strata))) if (has_many) { gr.name <- "combination" ugroups <- names(s$strata) } else { # singular strataEqualNames <- strsplit(names(s$strata), "=") gr.name <- strataEqualNames[[1]][[1]] ugroups <- vapply(strataEqualNames, `[[`, character(1), 2) } getlast <- function(x) { res <- NULL maxTime <- max(x$time) for (mo in names(x$strata)) { sur <- x[mo]$surv n <- length(sur) # grab the last survival value surValue <- sur[n] if (isTRUE(all.equal(surValue, 0))) { # if they die, order by percent complete of max observation. # tie value of 0 if the last person dies at the last time surTime <- x[mo]$time[n] surValue <- (surTime / maxTime) - 1 } res <- append(res, surValue) } return(res) } if (isTRUE(order.legend)) { group_order <- order(getlast(s), decreasing = TRUE) lastv <- ugroups[group_order] if (length(surv.col) == length(n)) { surv.col <- surv.col[group_order] } if (length(cens.col) == length(n)) { cens.col <- cens.col[group_order] } } else { lastv <- ugroups } groups <- factor(ugroups, levels = lastv) gr.df <- vector('list', strata) n.ind <- cumsum(c(0, n)) for (i in 1:strata) { indI <- (n.ind[i]+1):n.ind[i+1] gr.df[[i]] <- data.frame( time = c(0, s$time[ indI ]), surv = c(1, s$surv[ indI ]), up = c(1, s$upper[ indI ]), low = c(1, s$lower[ indI ]), cens = c(0, s$n.censor[ indI ]), group = rep(groups[i], n[i] + 1) ) } dat <- do.call(rbind, gr.df) pl <- ggplot(dat, aes(x = time, y = surv, group = group)) + geom_step(aes(col = group, lty = group), size = size.est) + xlab(xlab) + ylab(ylab) + ggtitle(main) pl <- if(surv.col[1] != 'gg.def'){ scaleValues <- if (length(surv.col) == 1) { rep(surv.col, strata) } else{ surv.col } pl + scale_colour_manual(values = scaleValues) } else { pl + scale_colour_discrete() } lineScaleValues <- if (length(lty.est) == 1) { rep(lty.est, strata) } else { lty.est } pl <- pl + scale_linetype_manual(values = lineScaleValues) if(identical(CI,TRUE)) { stepLty <- if ((length(surv.col) > 1 | surv.col == 'gg.def')[1]) { lty.ci } else { surv.col } pl <- pl + geom_step(aes(y = up, lty = group, col = group), lty = stepLty, size = size.ci) + geom_step(aes(y = low,lty = group, col = group), lty = stepLty, size = size.ci) } if (identical(plot.cens, TRUE) ){ dat.cens <- subset(dat, cens != 0) dat.cens <- subset(dat.cens, group != "PKD") if (nrow(dat.cens) == 0) { stop('There are no censored observations') } if (length(cens.col) == 1) { if (identical(cens.col, "gg.def")) { # match the colors of the lines pl <- pl + geom_point( data = dat.cens, mapping = aes(y = surv, col = group), shape = cens.shape, size = cens.size, show.legend = FALSE ) } else { # supply the raw color value pl <- pl + geom_point( data = dat.cens, mapping = aes(y = surv), shape = cens.shape, color = cens.col, size = cens.size ) } } else if (length(cens.col) > 0) { # if(!(identical(cens.col,surv.col) || is.null(cens.col))) { # warning ("Color scales for survival curves and censored points don't match.\nOnly one color scale can be used. Defaulting to surv.col") # } if (! identical(cens.col, "gg.def")) { if (length(cens.col) != strata) { warning("Color scales for censored points don't match the number of groups. Defaulting to ggplot2 default color scale") cens.col <- "gg.def" } } if (identical(cens.col, "gg.def")) { # match the group color value pl <- pl + geom_point( data = dat.cens, mapping = aes(y = surv, col = group), shape = cens.shape, show.legend = FALSE, size = cens.size ) } else { # custom colors and maybe custom shape uniqueGroupVals = levels(dat.cens$group) if (length(cens.shape) == 1) { cens.shape = rep(cens.shape, strata) } if (length(cens.shape) != strata) { warning("The length of the censored shapes does not match the number of groups (or 1). Defaulting shape = 3 (+)") cens.shape = rep(3, strata) } for (i in seq_along(uniqueGroupVals)) { groupVal = uniqueGroupVals[i] dtGroup <- subset(dat.cens, group == groupVal) if (nrow(dtGroup) == 0) { next } pl <- pl + geom_point( data = dtGroup, mapping = aes(y=surv), color = I(cens.col[i]), shape = cens.shape[i], show.legend = FALSE, size = cens.size ) } } } } if(identical(back.white, TRUE)) { pl <- pl + theme_bw() } pl <- pl + labs( color = gr.name, linetype = gr.name ) pl } GGally/R/ggmatrix_gtable_helpers.R0000644000176200001440000000773513663637143016667 0ustar liggesusers plot_gtable <- function(p) { ggplot_gtable(ggplot_build(p)) } # axis_size_left(p) # axis_size_bottom(p) # axis_size_left(g) # axis_size_bottom(g) axis_list <- (function(){ axis_label_size_wrapper <- function(fn, filter_val, select_val, unitTo, valueOnly) { function(pg) { pg_axis <- gtable::gtable_filter(pg, filter_val) items <- pg_axis[[select_val]] if (!inherits(items, "unit.list")) { ret <- fn(items, unitTo = unitTo, valueOnly = valueOnly) } else { ret <- vapply(items, fn, numeric(1), unitTo = unitTo, valueOnly = valueOnly) } max(ret) } } axis_size_left <- axis_label_size_wrapper( grid::convertWidth, "axis-l", "widths", unitTo = "cm", valueOnly = TRUE ) axis_size_bottom <- axis_label_size_wrapper( grid::convertHeight, "axis-b", "heights", unitTo = "cm", valueOnly = TRUE ) list(axis_size_left, axis_size_bottom) })() axis_size_left <- axis_list[[1]] axis_size_bottom <- axis_list[[2]] # add_correct_label <- function(pmg, pm, plot_panel <- function( pg, row_pos, col_pos, matrix_show_strips, matrix_ncol, plot_show_axis_labels ) { # ask about strips layout_names <- c("panel") strip_right_name <- "strip-r|strip-l" strip_top_name <- "strip-t|strip-b" legend_name <- "guide-box" all_layout_names <- c(layout_names, strip_right_name, strip_top_name, legend_name) if (is.null(matrix_show_strips)) { # make sure it's on the outer right and top edge if (col_pos == (matrix_ncol)) { layout_names <- c(layout_names, strip_right_name) } if (row_pos == 1) { layout_names <- c(layout_names, strip_top_name) } } else if (matrix_show_strips) { layout_names <- c(layout_names, strip_right_name, strip_top_name) } # if they have a custom plot, make sure it shows up if (! is.null(plot_show_axis_labels)) { # pShowStrips <- ! identical(p$axisLabels, FALSE) # copied from old code. want to replace it to something like above if (plot_show_axis_labels %in% c("internal", "none")) { layout_names <- all_layout_names } } # get correct panel (and strips) layout_rows <- str_detect(pg$layout$name, paste(layout_names, collapse = "|")) layout_info <- pg$layout[layout_rows, ] top_bottom <- layout_info[, c("t", "b")] left_right <- layout_info[, c("l", "r")] plot_panel <- pg[ min(top_bottom):max(top_bottom), min(left_right):max(left_right) ] plot_panel } add_left_axis <- function(pmg, pg, show_strips, grob_pos) { layout <- pg$layout layout_name <- layout$name # axis layout info al <- layout[str_detect(layout_name, "axis-l"), ] if (show_strips) { alx <- layout[str_detect(layout_name, "axis-l|strip-t|strip-b"), ] } else { alx <- al } # get only the axis left objects (and maybe strip top spacer) axis_panel <- pg[min(alx$b):max(alx$t), min(al$l)] # force to align left axis_panel <- gtable::gtable_add_cols(axis_panel, grid::unit(1, "null"), 0) pmg$grobs[[grob_pos]] <- axis_panel pmg } add_bottom_axis <- function(pmg, pg, show_strips, grob_pos) { layout <- pg$layout layout_name <- layout$name # axis layout info al <- layout[str_detect(layout_name, "axis-b"), ] if (show_strips) { alx <- layout[str_detect(layout_name, "axis-b|strip-r|strip-l"), ] } else { alx <- al } # get only the axis left objects (and maybe strip top spacer) axis_panel <- pg[min(al$t), min(alx$l):max(alx$r)] # force to align top axis_panel <- gtable::gtable_add_rows(axis_panel, grid::unit(1, "null"), 1) pmg$grobs[[grob_pos]] <- axis_panel pmg } set_max_axis_size <- function(pmg, axis_sizes, layout_name, layout_cols, pmg_key) { m_axis_size <- max(axis_sizes, na.rm = TRUE) grob_pos_vals <- which(str_detect(pmg$layout$name, layout_name)) val_pos <- pmg$layout[grob_pos_vals, layout_cols] val_pos <- unique(unlist(val_pos)) # if (length(val_pos) > 1) { # stop(stop_msg) # } pmg[[pmg_key]][[val_pos]] <- unit(m_axis_size, "cm") pmg } GGally/R/stars.R0000644000176200001440000000202313663637143013122 0ustar liggesusers#' Significance Stars #' #' Calculate significance stars #' #' @param x numeric values that will be compared to the \code{point}, \code{one}, \code{two}, and \code{three} values #' @param three threshold below which to display three stars #' @param two threshold below which to display two stars #' @param one threshold below which to display one star #' @param point threshold below which to display one point (\code{NULL} to deactivate) #' @return character vector containing the appropriate number of stars for each \code{x} value #' @author Joseph Larmarange #' @export #' @examples #' x <- c(0.5, 0.1, 0.05, 0.01, 0.001) #' signif_stars(x) #' signif_stars(x, one = .15, point = NULL) signif_stars <- function(x, three = 0.001, two = 0.01, one = 0.05, point = 0.1) { res <- rep_len("", length.out = length(x)) if (!is.null(point)) { res[x <= point] <- "." } if (!is.null(one)) { res[x <= one] <- "*" } if (!is.null(two)) { res[x <= two] <- "**" } if (!is.null(three)) { res[x <= three] <- "***" } res } GGally/R/data-nasa.R0000644000176200001440000000156213663637143013626 0ustar liggesusers#' Data from the Data Expo JSM 2006. #' #' This data was provided by NASA for the competition. #' #' The data shows 6 years of monthly measurements of a 24x24 spatial grid #' from Central America: #' #' @details \itemize{ #' \item time integer specifying temporal order of measurements #' \item x, y, lat, long spatial location of measurements. #' \item cloudhigh, cloudlow, cloudmid, ozone, pressure, surftemp, temperature #' are the various satellite measurements. #' \item date, day, month, year specifying the time of measurements. #' \item id unique ide for each spatial position. #' } #' #' @docType data #' @keywords datasets #' @name nasa #' @usage data(nasa) #' @format A data frame with 41472 rows and 17 variables #' @references #' Murrell, P. (2010) The 2006 Data Expo of the American Statistical Association. #' Computational Statistics, 25:551-554. NULL GGally/R/ggpairs_add.R0000644000176200001440000002466113764714663014253 0ustar liggesusers #' Modify a \code{\link{ggmatrix}} object by adding an \pkg{ggplot2} object to all plots #' #' This operator allows you to add \pkg{ggplot2} objects to a \code{\link{ggmatrix}} object. #' #' If the first object is an object of class \code{\link{ggmatrix}}, you can add #' the following types of objects, and it will return a modified \pkg{ggplot2} #' object. #' #' \itemize{ ###### \item \code{data.frame}: replace current data.frame ###### (must use \code{%+%}) ###### \item \code{uneval}: replace current aesthetics ###### \item \code{layer}: add new layer #' \item \code{theme}: update plot theme #' \item \code{scale}: replace current scale #' \item \code{coord}: override current coordinate system ###### \item \code{facet}: override current coordinate faceting #' } #' #' The \code{+} operator completely replaces elements #' with elements from e2. #' #' @param e1 An object of class \code{\link{ggnostic}} or \code{ggplot} #' @param e2 A component to add to \code{e1} #' #' @export #' @seealso [ggplot2::+.gg] and [ggplot2::theme()] #' @method + gg #' @rdname gg-add #' @examples #' # small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' data(tips, package = "reshape") #' #' pm <- ggpairs(tips[, 2:4], ggplot2::aes(color = sex)) #' ## change to black and white theme #' pm + ggplot2::theme_bw() #' ## change to linedraw theme #' p_(pm + ggplot2::theme_linedraw()) #' ## change to custom theme #' p_(pm + ggplot2::theme(panel.background = ggplot2::element_rect(fill = "lightblue"))) #' ## add a list of information #' extra <- list(ggplot2::theme_bw(), ggplot2::labs(caption = "My caption!")) #' p_(pm + extra) "+.gg" <- function(e1, e2) { if (!is.ggmatrix(e1)) { return(e1 %+% e2) } if (is.null(e1$gg)) { e1$gg <- list() } if (inherits(e2, "labels")) { add_labels_to_ggmatrix(e1, e2) } else if (is.theme(e2)) { add_theme_to_ggmatrix(e1, e2) } else if (is.list(e2)) { add_list_to_ggmatrix(e1, e2) } else if (is.ggproto(e2)) { add_to_ggmatrix(e1, e2) } else { stop( "'ggmatrix' does not know how to add objects that do not have class 'theme', 'labels' or 'ggproto'.", " Received object with class: '", paste(class(e2), collapse = ", "), "'" ) } } add_gg_info <- function(p, gg) { if (!is.null(gg)) { if (!is.null(gg$theme)) { p <- p + gg$theme } if (!is.null(gg$labs)) { p <- p + gg$labs } } p } add_labels_to_ggmatrix <- function(e1, e2) { label_names <- names(e2) if ("x" %in% label_names) { e1$xlab <- e2$x } if ("y" %in% label_names) { e1$ylab <- e2$y } if ("title" %in% label_names) { e1$title <- e2$title } non_ggmatrix_labels <- label_names[!label_names %in% c("x", "y", "title")] if (length(non_ggmatrix_labels) > 0) { if (is.null(e1$gg$labs)) { e1$gg$labs <- structure(list(), class = "labels") } e1$gg$labs[non_ggmatrix_labels] <- e2[non_ggmatrix_labels] } e1 } add_theme_to_ggmatrix <- function(e1, e2) { # Get the name of what was passed in as e2, and pass along so that it # can be displayed in error messages # e2name <- deparse(substitute(e2)) if (is.null(e1$gg$theme)) { e1$gg$theme <- e2 } else { # calls ggplot2 add method and stores the result in gg e1$gg$theme <- e1$gg$theme %+% e2 } e1 } #' @export #' @rdname gg-add #' @inheritParams ggmatrix_location #' @details #' \code{add_to_ggmatrix} gives you more control to modify #' only some subplots. This function may be replaced and/or removed in the future. \Sexpr[results=rd, stage=render]{lifecycle::badge("experimental")} #' @seealso \code{\link{ggmatrix_location}} #' @examples #' ## modify scale #' p_(pm + scale_fill_brewer(type = "qual")) #' ## only first row #' p_(add_to_ggmatrix(pm, scale_fill_brewer(type = "qual"), rows = 1:2)) #' ## only second col #' p_(add_to_ggmatrix(pm, scale_fill_brewer(type = "qual"), cols = 2:3)) #' ## only to upper triangle of plot matrix #' p_(add_to_ggmatrix( #' pm, #' scale_fill_brewer(type = "qual"), #' location = "upper" #' )) add_to_ggmatrix <- function( e1, e2, location = NULL, rows = NULL, cols = NULL ) { if (!is.ggmatrix(e1)) stop("e1 should be a ggmatrix.") if (!is.ggproto(e2)) stop("e2 should be a ggproto object.") pm <- e1 gg <- e2 loc <- ggmatrix_location(pm, location = location, rows = rows, cols = cols) row_vals <- loc$row col_vals <- loc$col for (i in seq_along(row_vals)) { row <- row_vals[i] col <- col_vals[i] # wrap in try to not let one plot fail, but also print the error try({ pm[row, col] <- pm[row, col] + gg }) } pm } #' \code{\link{ggmatrix}} plot locations #' #' \lifecycle{experimental} #' #' Convert many types of location values to a consistent \code{data.frame} of \code{row} and \code{col} values. #' #' @param pm \code{\link{ggmatrix}} plot object #' @param location \describe{ #' \item{\code{"all"}, \code{TRUE}}{All row and col combinations} #' \item{\code{"none"}}{No row and column combinations} #' \item{\code{"upper"}}{Locations where the column value is higher than the row value} #' \item{\code{"lower"}}{Locations where the row value is higher than the column value} #' \item{\code{"diag"}}{Locations where the column value is equal to the row value} #' \item{\code{matrix} or \code{data.frame}}{ #' \code{matrix} values will be converted into \code{data.frame}s. #' \itemize{ #' \item A \code{data.frame} with the exact column names \code{c("row", "col")} #' \item A \code{data.frame} with the number of rows and columns matching the plot matrix object provided. Each cell will be tested for a "truthy" value to determine if the location should be kept. #' } #' } #' } #' @param rows numeric vector of the rows to be used. Will be used with \code{cols} if \code{location} is \code{NULL} #' @param cols numeric vector of the cols to be used. Will be used with \code{rows} if \code{location} is \code{NULL} #' @return Data frame with columns \code{c("row", "col")} containing locations for the plot matrix #' @export #' @examples #' pm <- ggpairs(reshape::tips, 1:3) #' #' # All locations #' ggmatrix_location(pm, location = "all") #' ggmatrix_location(pm, location = TRUE) #' #' # No locations #' ggmatrix_location(pm, location = "none") #' #' # "upper" triangle locations #' ggmatrix_location(pm, location = "upper") #' #' # "lower" triangle locations #' ggmatrix_location(pm, location = "lower") #' #' # "diag" locations #' ggmatrix_location(pm, location = "diag") #' #' # specific rows #' ggmatrix_location(pm, rows = 2) #' #' # specific columns #' ggmatrix_location(pm, cols = 2) #' #' # row and column combinations #' ggmatrix_location(pm, rows = c(1,2), cols = c(1,3)) #' #' # matrix locations #' mat <- matrix(TRUE, ncol = 3, nrow = 3) #' mat[1,1] <- FALSE #' locs <- ggmatrix_location(pm, location = mat) #' ## does not contain the 1,1 cell #' locs #' #' # Use the output of a prior ggmatrix_location #' ggmatrix_location(pm, location = locs) ggmatrix_location <- function( pm, location = NULL, rows = NULL, cols = NULL ) { if (!is.ggmatrix(pm)) stop("pm should be a ggmatrix.") if (!is.null(location)) { if ( is.logical(location) && !( is.matrix(location) || is.data.frame(location) ) ) { if (length(location) != 1) { stop("`location` logical value must be of length 1") } location <- if (isTRUE(location)) { "all" } else { warning("Not `TRUE` logical `location` value. Setting to `'none'`") "none" } } if (is.character(location)) { location <- match.arg(location, c("all", "upper", "lower", "diag", "none"), several.ok = FALSE) locs <- expand.grid(row = seq_len(pm$nrow), col = seq_len(pm$ncol)) location <- switch( location, "all" = locs, "none" = subset(locs, FALSE), "diag" = subset(locs, row == col), "upper" = subset(locs, col > row), "lower" = subset(locs, col < row), stop(location, " not implemented") ) } else { if (is.matrix(location)) { location <- as.data.frame(location) } if (is.data.frame(location)) { if (!identical(c("row", "col"), colnames(location))) { # using data.frame of locations as truthy vals if (ncol(location) != pm$ncol) { stop("location provided does not have the same size of columns") } if (nrow(location) != pm$nrow) { stop("location provided does not have the same size of rows") } # turn wide matrix into a tall data.frame of row/col combos tmp_locs <- data.frame(row = numeric(0), col = numeric(0)) for (i in seq_len(nrow(location))) { for (j in seq_len(ncol(location))) { val <- location[i,j] if (val) { tmp_locs[nrow(tmp_locs) + 1, ] <- list(row = i, col = j) } } } location <- tmp_locs } # end (location is data.frame) } # end (location not character) } # end (location not null) } else { # location is null if (is.null(rows)) { rows <- seq_len(pm$nrow) } if (!is.numeric(rows)) { stop("rows must be numeric") } if (is.null(cols)) { cols <- seq_len(pm$ncol) } if (!is.numeric(cols)) { stop("cols must be numeric") } location <- expand.grid(row = rows, col = cols) } # location will be a 2d data.frame with colnames of `'row'` and `'col'` locs <- as.data.frame(location) if (ncol(locs) < 2) { utils::str(locs) stop("not enough columns to inspect for a location") } if (!all(c("row", "col") %in% colnames(locs))) { stop("invalid location row / col object") } row <- locs$row if (any(row > pm$nrow) || any(row <= 0) || any(is.na(row))) { stop( "`row` must be non-NA / positive numeric values `<= pm$nrow`", "\n", "pm$nrow: ", dput_val(pm$nrow), "\n", "row: ", dput_val(row) ) } col <- locs$col if (any(col > pm$ncol) || any(col <= 0) || any(is.na(col))) { stop( "`col` must be non-NA / positive numeric values `<= pm$ncol`", "\n", "pm$ncol: ", dput_val(pm$ncol), "\n", "col: ", dput_val(col) ) } # typical case return( locs[, c("row", "col")] ) } add_list_to_ggmatrix <- function(e1, e2) { for (item in e2) { e1 <- e1 + item } e1 } is.ggmatrix <- function(x) { inherits(x, "ggmatrix") } GGally/R/ggpairs.R0000644000176200001440000011072013666472400013422 0ustar liggesusers# list of the different plot types to check # continuous # points # smooth # smooth_loess # density # cor # blank # combo # box # box_no_facet # dot # dot_no_facet # facethist # facetdensity # denstrip # blank # discrete # ratio # count # facetbar # blank # diag # continuous # densityDiag # barDiag # blankDiag # discrete # barDiag # blankDiag crosstalk_key <- function() { ".crossTalkKey" } fortify_SharedData <- function(model, data, ...) { key <- model$key() set <- model$groupName() data <- model$origData() # need a consistent name so we know how to access it in ggplotly() # MUST be added last. can NOT be done first data[[crosstalk_key()]] <- key structure(data, set = set) } fix_data <- function(data) { if (inherits(data, "SharedData")) { data <- fortify_SharedData(data) } data <- fortify(data) data <- as.data.frame(data) for (i in 1:dim(data)[2] ) { if (is.character(data[[i]])) { data[[i]] <- as.factor(data[[i]]) } } data } fix_data_slim <- function(data, isSharedData) { if (isSharedData) { data[[crosstalk_key()]] <- NULL } data } fix_column_values <- function( data, columns, columnLabels, columnsName, columnLabelsName, isSharedData = FALSE ) { colnamesData <- colnames(data) if (is.character(columns)) { colNumValues <- lapply(columns, function(colName){ which(colnamesData == colName) }) isFound <- as.logical(unlist(lapply(colNumValues, length))) if (any(!isFound)) { stop( "Columns in '", columnsName, "' not found in data: c(", str_c(str_c("'", columns[!isFound], "'"), collapse = ", "), "). Choices: c('", paste(colnamesData, collapse = "', '"), "')" ) } columns <- unlist(colNumValues) } if (any(columns > ncol(data))) { stop( "Make sure your numeric '", columnsName, "'", " values are less than or equal to ", ncol(data), ".\n", "\t", columnsName, " = c(", str_c(columns, collapse = ", "), ")" ) } if (any(columns < 1)) { stop( "Make sure your numeric '", columnsName, "' values are positive.", "\n", "\t", columnsName, " = c(", paste(columns, collapse = ", "), ")" ) } if (any( (columns %% 1) != 0)) { stop( "Make sure your numeric '", columnsName, "' values are integers.", "\n", "\t", columnsName, " = c(", paste(columns, collapse = ", "), ")" ) } if (!is.null(columnLabels)) { if (length(columnLabels) != length(columns)) { stop( "The length of the '", columnLabelsName, "'", " does not match the length of the '", columnsName, "' being used.", " Labels: c('", paste(columnLabels, collapse = ", "), "')\n", " Columns: c('", paste(columns, collapse = ", "), "')" ) } } columns } warn_deprecated <- function(is_supplied, title) { if (is_supplied) { warning(paste( "'", title, "' will be deprecated in future versions. Please remove it from your code", sep = "" )) } } stop_if_bad_mapping <- function(mapping) { if (is.numeric(mapping)) { stop( "'mapping' should not be numeric", " unless 'columns' is missing from function call." ) } } warn_if_args_exist <- function(args) { if (length(args) > 0) { argNames <- names(args) warning(str_c( "Extra arguments: ", str_c(shQuote(argNames), collapse = ", "), " are being ignored.", " If these are meant to be aesthetics, submit them using the", " 'mapping' variable within ggpairs with ggplot2::aes or ggplot2::aes_string." )) } } fix_axis_label_choice <- function(axisLabels, axisLabelChoices) { if (length(axisLabels) > 1) { axisLabels <- axisLabels[1] } axisLabelChoice <- pmatch(axisLabels, axisLabelChoices) if (is.na(axisLabelChoice)) { warning(str_c( "'axisLabels' not in c(", str_c(str_c("'", axisLabelChoices, "'"), collapse = ", "), "). Reverting to '", axisLabelChoices[1], "'" )) axisLabelChoice <- 1 } axisLabels <- axisLabelChoices[axisLabelChoice] } stop_if_high_cardinality <- function(data, columns, threshold) { if (is.null(threshold)) { return() } if (identical(threshold, FALSE)) { return() } if (!is.numeric(threshold)) { stop("'cardinality_threshold' should be a numeric or NULL") } for (col in names(data[columns])) { data_col <- data[[col]] if (!is.numeric(data_col)) { level_length <- length(levels(data_col)) if (level_length > threshold) { stop( "Column '", col, "' has more levels (", level_length, ")", " than the threshold (", threshold, ") allowed.\n", "Please remove the column or increase the 'cardinality_threshold' parameter. Increasing the cardinality_threshold may produce long processing times" # nolint ) } } } } #' \pkg{ggplot2} generalized pairs plot for two columns sets of data #' #' Make a matrix of plots with a given data set with two different column sets #' #' @details #' \code{types} is a list that may contain the variables #' 'continuous', 'combo', 'discrete', and 'na'. Each element of the list may be a function or a string. If a string is supplied, If a string is supplied, it must be a character string representing the tail end of a \code{ggally_NAME} function. The list of current valid \code{ggally_NAME} functions is visible in a dedicated vignette. #'\describe{ #' \item{continuous}{This option is used for continuous X and Y data.} #' \item{comboHorizontal}{This option is used for either continuous X and categorical Y data or categorical X and continuous Y data.} #' \item{comboVertical}{This option is used for either continuous X and categorical Y data or categorical X and continuous Y data.} #' \item{discrete}{This option is used for categorical X and Y data.} #' \item{na}{This option is used when all X data is \code{NA}, all Y data is \code{NA}, or either all X or Y data is \code{NA}.} #'} #' #' If 'blank' is ever chosen as an option, then ggduo will produce an empty plot. #' #' If a function is supplied as an option, it should implement the function api of \code{function(data, mapping, ...){#make ggplot2 plot}}. If a specific function needs its parameters set, \code{\link{wrap}(fn, param1 = val1, param2 = val2)} the function with its parameters. #' #' @export #' @param data data set using. Can have both numerical and categorical data. #' @param mapping aesthetic mapping (besides \code{x} and \code{y}). See \code{\link[ggplot2]{aes}()}. If \code{mapping} is numeric, \code{columns} will be set to the \code{mapping} value and \code{mapping} will be set to \code{NULL}. #' @param columnsX,columnsY which columns are used to make plots. Defaults to all columns. #' @param title,xlab,ylab title, x label, and y label for the graph #' @param types see Details #' @param axisLabels either "show" to display axisLabels or "none" for no axis labels #' @param columnLabelsX,columnLabelsY label names to be displayed. Defaults to names of columns being used. #' @template ggmatrix-labeller-param #' @template ggmatrix-switch-param #' @param showStrips boolean to determine if each plot's strips should be displayed. \code{NULL} will default to the top and right side plots only. \code{TRUE} or \code{FALSE} will turn all strips on or off respectively. #' @template ggmatrix-legend-param #' @param cardinality_threshold maximum number of levels allowed in a character / factor column. Set this value to NULL to not check factor columns. Defaults to 15 #' @template ggmatrix-progress #' @param legends deprecated #' @param xProportions,yProportions Value to change how much area is given for each plot. Either \code{NULL} (default), numeric value matching respective length, \code{grid::\link[grid]{unit}} object with matching respective length or \code{"auto"} for automatic relative proportions based on the number of levels for categorical variables. #' @export #' @examples #' # small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(baseball, package = "plyr") #' #' # Keep players from 1990-1995 with at least one at bat #' # Add how many singles a player hit #' # (must do in two steps as X1b is used in calculations) #' dt <- transform( #' subset(baseball, year >= 1990 & year <= 1995 & ab > 0), #' X1b = h - X2b - X3b - hr #' ) #' # Add #' # the player's batting average, #' # the player's slugging percentage, #' # and the player's on base percentage #' # Make factor a year, as each season is discrete #' dt <- transform( #' dt, #' batting_avg = h / ab, #' slug = (X1b + 2*X2b + 3*X3b + 4*hr) / ab, #' on_base = (h + bb + hbp) / (ab + bb + hbp), #' year = as.factor(year) #' ) #' #' #' pm <- ggduo( #' dt, #' c("year", "g", "ab", "lg"), #' c("batting_avg", "slug", "on_base"), #' mapping = ggplot2::aes(color = lg) #' ) #' # Prints, but #' # there is severe over plotting in the continuous plots #' # the labels could be better #' # want to add more hitting information #' p_(pm) #' #' # address overplotting issues and add a title #' pm <- ggduo( #' dt, #' c("year", "g", "ab", "lg"), #' c("batting_avg", "slug", "on_base"), #' columnLabelsX = c("year", "player game count", "player at bat count", "league"), #' columnLabelsY = c("batting avg", "slug %", "on base %"), #' title = "Baseball Hitting Stats from 1990-1995", #' mapping = ggplot2::aes(color = lg), #' types = list( #' # change the shape and add some transparency to the points #' continuous = wrap("smooth_loess", alpha = 0.50, shape = "+") #' ), #' showStrips = FALSE #' ) #' #' p_(pm) #' #' # Use "auto" to adapt width of the sub-plots #' pm <- ggduo( #' dt, #' c("year", "g", "ab", "lg"), #' c("batting_avg", "slug", "on_base"), #' mapping = ggplot2::aes(color = lg), #' xProportions = "auto" #' ) #' #' p_(pm) #' #' # Custom widths & heights of the sub-plots #' pm <- ggduo( #' dt, #' c("year", "g", "ab", "lg"), #' c("batting_avg", "slug", "on_base"), #' mapping = ggplot2::aes(color = lg), #' xProportions = c(6, 4, 3, 2), #' yProportions = c(1, 2, 1) #' ) #' #' p_(pm) #' #' # Example derived from: #' ## R Data Analysis Examples | Canonical Correlation Analysis. UCLA: Institute for Digital #' ## Research and Education. #' ## from http://www.stats.idre.ucla.edu/r/dae/canonical-correlation-analysis #' ## (accessed May 22, 2017). #' # "Example 1. A researcher has collected data on three psychological variables, four #' # academic variables (standardized test scores) and gender for 600 college freshman. #' # She is interested in how the set of psychological variables relates to the academic #' # variables and gender. In particular, the researcher is interested in how many #' # dimensions (canonical variables) are necessary to understand the association between #' # the two sets of variables." #' data(psychademic) #' summary(psychademic) #' #' (psych_variables <- attr(psychademic, "psychology")) #' (academic_variables <- attr(psychademic, "academic")) #' #' ## Within correlation #' p_(ggpairs(psychademic, columns = psych_variables)) #' p_(ggpairs(psychademic, columns = academic_variables)) #' #' ## Between correlation #' loess_with_cor <- function(data, mapping, ..., method = "pearson") { #' x <- eval_data_col(data, mapping$x) #' y <- eval_data_col(data, mapping$y) #' cor <- cor(x, y, method = method) #' ggally_smooth_loess(data, mapping, ...) + #' ggplot2::geom_label( #' data = data.frame( #' x = min(x, na.rm = TRUE), #' y = max(y, na.rm = TRUE), #' lab = round(cor, digits = 3) #' ), #' mapping = ggplot2::aes(x = x, y = y, label = lab), #' hjust = 0, vjust = 1, #' size = 5, fontface = "bold", #' inherit.aes = FALSE # do not inherit anything from the ... #' ) #' } #' pm <- ggduo( #' psychademic, #' rev(psych_variables), academic_variables, #' types = list(continuous = loess_with_cor), #' showStrips = FALSE #' ) #' suppressWarnings(p_(pm)) # ignore warnings from loess #' #' # add color according to sex #' pm <- ggduo( #' psychademic, #' mapping = ggplot2::aes(color = sex), #' rev(psych_variables), academic_variables, #' types = list(continuous = loess_with_cor), #' showStrips = FALSE, #' legend = c(5,2) #' ) #' suppressWarnings(p_(pm)) #' #' #' # add color according to sex #' pm <- ggduo( #' psychademic, #' mapping = ggplot2::aes(color = motivation), #' rev(psych_variables), academic_variables, #' types = list(continuous = loess_with_cor), #' showStrips = FALSE, #' legend = c(5,2) #' ) + #' ggplot2::theme(legend.position = "bottom") #' suppressWarnings(p_(pm)) # # # # pm <- ggduo( # dt, # c("year", "g", "ab", "lg", "lg"), # c("batting_avg", "slug", "on_base", "hit_type"), # columnLabelsX = c("year", "player game count", "player at bat count", "league", ""), # columnLabelsY = c("batting avg", "slug %", "on base %", "hit type"), # title = "Baseball Hitting Stats from 1990-1995 (player strike in 1994)", # mapping = aes(color = year), # types = list( # continuous = wrap("smooth_loess", alpha = 0.50, shape = "+"), # comboHorizontal = wrap(display_hit_type_combo, binwidth = 15), # discrete = wrap(display_hit_type_discrete, color = "black", size = 0.15) # ), # showStrips = FALSE # ); # # # make the 5th column blank, except for the legend # pm[1,5] <- NULL # pm[2,5] <- grab_legend(pm[2,1]) # pm[3,5] <- NULL # pm[4,5] <- NULL # # pm # # ggduo( # australia_PISA2012, # c("gender", "age", "homework", "possessions"), # c("PV1MATH", "PV2MATH", "PV3MATH", "PV4MATH", "PV5MATH"), # types = list( # continuous = "points", # combo = "box", # discrete = "ratio" # ) # ) # # ggduo( # australia_PISA2012, # c("gender", "age", "homework", "possessions"), # c("PV1MATH", "PV2MATH", "PV3MATH", "PV4MATH", "PV5MATH"), # mapping = ggplot2::aes(color = gender), # types = list( # continuous = wrap("smooth", alpha = 0.25, method = "loess"), # combo = "box", # discrete = "ratio" # ) # ) # # ggduo(australia_PISA2012, c("gender", "age", "homework", "possessions"), c("PV1MATH", "PV1READ", "PV1SCIE"), types = list(continuous = "points", combo = "box", discrete = "ratio")) # ggduo(australia_PISA2012, c("gender", "age", "homework", "possessions"), c("PV1MATH", "PV1READ", "PV1SCIE"), types = list(continuous = wrap("smooth", alpha = 0.25, method = "loess"), combo = "box", discrete = "ratio"), mapping = ggplot2::aes(color = gender)) ggduo <- function( data, mapping = NULL, columnsX = 1:ncol(data), columnsY = 1:ncol(data), title = NULL, types = list( continuous = "smooth_loess", comboVertical = "box_no_facet", comboHorizontal = "facethist", discrete = "count" ), axisLabels = c("show", "none"), columnLabelsX = colnames(data[columnsX]), columnLabelsY = colnames(data[columnsY]), labeller = "label_value", switch = NULL, xlab = NULL, ylab = NULL, showStrips = NULL, legend = NULL, cardinality_threshold = 15, progress = NULL, xProportions = NULL, yProportions = NULL, legends = stop("deprecated") ) { warn_deprecated(!missing(legends), "legends") isSharedData <- inherits(data, "SharedData") data_ <- fix_data(data) data <- fix_data_slim(data_, isSharedData) # fix args if ( !missing(mapping) & !is.list(mapping) & !missing(columnsX) & missing(columnsY) ) { columnsY <- columnsX columnsX <- mapping mapping <- NULL } stop_if_bad_mapping(mapping) columnsX <- fix_column_values(data, columnsX, columnLabelsX, "columnsX", "columnLabelsX") columnsY <- fix_column_values(data, columnsY, columnLabelsY, "columnsY", "columnLabelsY") stop_if_high_cardinality(data, columnsX, cardinality_threshold) stop_if_high_cardinality(data, columnsY, cardinality_threshold) xProportions <- ggmatrix_proportions(xProportions, data, columnsX) yProportions <- ggmatrix_proportions(yProportions, data, columnsY) types <- check_and_set_ggpairs_defaults( "types", types, continuous = "smooth_loess", discrete = "count", na = "na", isDuo = TRUE ) if (!is.null(types[["combo"]])) { warning(str_c( "\nSetting:\n", "\ttypes$comboHorizontal <- types$combo\n", "\ttypes$comboVertical <- types$combo" )) types$comboHorizontal <- types$combo types$comboVertical <- types$combo types$combo <- NULL } if (is.null(types[["comboVertical"]])) { types$comboVertical <- "box_no_facet" } if (is.null(types[["comboHorizontal"]])) { types$comboHorizontal <- "facethist" } axisLabels <- fix_axis_label_choice(axisLabels, c("show", "none")) # get plot type information dataTypes <- plot_types(data, columnsX, columnsY, allowDiag = FALSE) ggduoPlots <- lapply(seq_len(nrow(dataTypes)), function(i) { plotType <- dataTypes[i, "plotType"] # posX <- dataTypes[i, "posX"] # posY <- dataTypes[i, "posY"] xColName <- dataTypes[i, "xVar"] yColName <- dataTypes[i, "yVar"] sectionAes <- add_and_overwrite_aes( add_and_overwrite_aes( aes_(x = as.name(xColName), y = as.name(yColName)), mapping ), types$mapping ) if (plotType == "combo") { if (dataTypes[i, "isVertical"]) { plotTypesList <- list(combo = types$comboVertical) } else { plotTypesList <- list(combo = types$comboHorizontal) } } else { plotTypesList <- types } args <- list(types = plotTypesList, sectionAes = sectionAes) plot_fn <- ggmatrix_plot_list(plotType) plotObj <- do.call(plot_fn, args) return(plotObj) }) plotMatrix <- ggmatrix( plots = ggduoPlots, byrow = TRUE, nrow = length(columnsY), ncol = length(columnsX), xAxisLabels = columnLabelsX, yAxisLabels = columnLabelsY, labeller = labeller, switch = switch, showStrips = showStrips, showXAxisPlotLabels = identical(axisLabels, "show"), showYAxisPlotLabels = identical(axisLabels, "show"), title = title, xlab = xlab, ylab = ylab, data = data_, gg = NULL, progress = progress, legend = legend, xProportions = xProportions, yProportions = yProportions ) plotMatrix } ### Example removed due to not using facet labels anymore # #Sequence to show how to change label size # make_small_strip <- function(plot_matrix, from_top, from_left, new_size = 7){ # up <- from_left > from_top # p <- getPlot(plot_matrix, from_top, from_left) # if(up) # p <- p + opts(strip.text.x = element_text(size = new_size)) # else # p <- p + opts(strip.text.y = element_text(angle = -90, size = new_size)) # # putPlot(plot_matrix, p, from_top, from_left) # } # small_label_diamond <- make_small_strip(diamondMatrix, 2, 1) # small_label_diamond <- make_small_strip(small_label_diamond, 1, 2) # small_label_diamond <- make_small_strip(small_label_diamond, 2, 2) # #small_label_diamond # now with much smaller strip text #' ggplot2 generalized pairs plot #' #' Make a matrix of plots with a given data set #' #' @details #' \code{upper} and \code{lower} are lists that may contain the variables #' 'continuous', 'combo', 'discrete', and 'na'. Each element of the list may be a function or a string. If a string is supplied, it must be a character string representing the tail end of a \code{ggally_NAME} function. The list of current valid \code{ggally_NAME} functions is visible in a dedicated vignette. #'\describe{ #' \item{continuous}{This option is used for continuous X and Y data.} #' \item{combo}{This option is used for either continuous X and categorical Y data or categorical X and continuous Y data.} #' \item{discrete}{This option is used for categorical X and Y data.} #' \item{na}{This option is used when all X data is \code{NA}, all Y data is \code{NA}, or either all X or Y data is \code{NA}.} #'} #' #' \code{diag} is a list that may only contain the variables 'continuous', 'discrete', and 'na'. Each element of the diag list is a string implementing the following options: #'\describe{ #' \item{continuous}{exactly one of ('densityDiag', 'barDiag', 'blankDiag'). This option is used for continuous X data.} #' \item{discrete}{exactly one of ('barDiag', 'blankDiag'). This option is used for categorical X and Y data.} #' \item{na}{exactly one of ('naDiag', 'blankDiag'). This option is used when all X data is \code{NA}.} #'} #' #' If 'blank' is ever chosen as an option, then ggpairs will produce an empty plot. #' #' If a function is supplied as an option to \code{upper}, \code{lower}, or \code{diag}, it should implement the function api of \code{function(data, mapping, ...){#make ggplot2 plot}}. If a specific function needs its parameters set, \code{\link{wrap}(fn, param1 = val1, param2 = val2)} the function with its parameters. #' #' @export #' @seealso wrap v1_ggmatrix_theme #' @param data data set using. Can have both numerical and categorical data. #' @param mapping aesthetic mapping (besides \code{x} and \code{y}). See \code{\link[ggplot2]{aes}()}. If \code{mapping} is numeric, \code{columns} will be set to the \code{mapping} value and \code{mapping} will be set to \code{NULL}. #' @param columns which columns are used to make plots. Defaults to all columns. #' @param title,xlab,ylab title, x label, and y label for the graph #' @param upper see Details #' @param lower see Details #' @param diag see Details #' @param params deprecated. Please see \code{\link{wrap_fn_with_param_arg}} #' @param ... deprecated. Please use \code{mapping} #' @param axisLabels either "show" to display axisLabels, "internal" for labels in the diagonal plots, or "none" for no axis labels #' @param columnLabels label names to be displayed. Defaults to names of columns being used. #' @param proportions Value to change how much area is given for each plot. Either \code{NULL} (default), numeric value matching respective length, \code{grid::\link[grid]{unit}} object with matching respective length or \code{"auto"} for automatic relative proportions based on the number of levels for categorical variables. #' @template ggmatrix-labeller-param #' @template ggmatrix-switch-param #' @param showStrips boolean to determine if each plot's strips should be displayed. \code{NULL} will default to the top and right side plots only. \code{TRUE} or \code{FALSE} will turn all strips on or off respectively. #' @template ggmatrix-legend-param #' @param cardinality_threshold maximum number of levels allowed in a character / factor column. Set this value to NULL to not check factor columns. Defaults to 15 #' @template ggmatrix-progress #' @param legends deprecated #' @keywords hplot #' @import ggplot2 #' @references John W Emerson, Walton A Green, Barret Schloerke, Jason Crowley, Dianne Cook, Heike Hofmann, Hadley Wickham. The Generalized Pairs Plot. Journal of Computational and Graphical Statistics, vol. 22, no. 1, pp. 79-91, 2012. #' @author Barret Schloerke, Jason Crowley, Di Cook, Heike Hofmann, Hadley Wickham #' @return \code{\link{ggmatrix}} object that if called, will print #' @examples #' # small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' #' ## Quick example, with and without colour #' data(flea) #' ggpairs(flea, columns = 2:4) #' pm <- ggpairs(flea, columns = 2:4, ggplot2::aes(colour=species)) #' p_(pm) #' # Note: colour should be categorical, else you will need to reset #' # the upper triangle to use points instead of trying to compute corr #' #' data(tips, package = "reshape") #' pm <- ggpairs(tips[, 1:3]) #' p_(pm) #' pm <- ggpairs(tips, 1:3, columnLabels = c("Total Bill", "Tip", "Sex")) #' p_(pm) #' pm <- ggpairs(tips, upper = "blank") #' p_(pm) #' #' ## Plot Types #' # Change default plot behavior #' pm <- ggpairs( #' tips[, c(1, 3, 4, 2)], #' upper = list(continuous = "density", combo = "box_no_facet"), #' lower = list(continuous = "points", combo = "dot_no_facet") #' ) #' p_(pm) #' # Supply Raw Functions (may be user defined functions!) #' pm <- ggpairs( #' tips[, c(1, 3, 4, 2)], #' upper = list(continuous = ggally_density, combo = ggally_box_no_facet), #' lower = list(continuous = ggally_points, combo = ggally_dot_no_facet) #' ) #' p_(pm) #' #' # Use sample of the diamonds data #' data(diamonds, package="ggplot2") #' diamonds.samp <- diamonds[sample(1:dim(diamonds)[1], 1000), ] #' #' # Different aesthetics for different plot sections and plot types #' pm <- ggpairs( #' diamonds.samp[, 1:5], #' mapping = ggplot2::aes(color = cut), #' upper = list(continuous = wrap("density", alpha = 0.5), combo = "box_no_facet"), #' lower = list(continuous = wrap("points", alpha = 0.3), combo = wrap("dot_no_facet", alpha = 0.4)), #' title = "Diamonds" #' ) #' p_(pm) #' #' ## Axis Label Variations #' # Only Variable Labels on the diagonal (no axis labels) #' pm <- ggpairs(tips[, 1:3], axisLabels="internal") #' p_(pm) #' # Only Variable Labels on the outside (no axis labels) #' pm <- ggpairs(tips[, 1:3], axisLabels="none") #' p_(pm) #' #' ## Facet Label Variations #' # Default: #' df_x <- rnorm(100) #' df_y <- df_x + rnorm(100, 0, 0.1) #' df <- data.frame(x = df_x, y = df_y, c = sqrt(df_x^2 + df_y^2)) #' pm <- ggpairs( #' df, #' columnLabels = c("alpha[foo]", "alpha[bar]", "sqrt(alpha[foo]^2 + alpha[bar]^2)") #' ) #' p_(pm) #' # Parsed labels: #' pm <- ggpairs( #' df, #' columnLabels = c("alpha[foo]", "alpha[bar]", "sqrt(alpha[foo]^2 + alpha[bar]^2)"), #' labeller = "label_parsed" #' ) #' p_(pm) #' #' ## Plot Insertion Example #' custom_car <- ggpairs(mtcars[, c("mpg", "wt", "cyl")], upper = "blank", title = "Custom Example") #' # ggplot example taken from example(geom_text) #' plot <- ggplot2::ggplot(mtcars, ggplot2::aes(x=wt, y=mpg, label=rownames(mtcars))) #' plot <- plot + #' ggplot2::geom_text(ggplot2::aes(colour=factor(cyl)), size = 3) + #' ggplot2::scale_colour_discrete(l=40) #' custom_car[1, 2] <- plot #' personal_plot <- ggally_text( #' "ggpairs allows you\nto put in your\nown plot.\nLike that one.\n <---" #' ) #' custom_car[1, 3] <- personal_plot #' p_(custom_car) #' #' ## Remove binwidth warning from ggplot2 #' # displays warning about picking a better binwidth #' pm <- ggpairs(tips, 2:3) #' p_(pm) #' # no warning displayed #' pm <- ggpairs(tips, 2:3, lower = list(combo = wrap("facethist", binwidth = 0.5))) #' p_(pm) #' # no warning displayed with user supplied function #' pm <- ggpairs(tips, 2:3, lower = list(combo = wrap(ggally_facethist, binwidth = 0.5))) #' p_(pm) #' #' ## Remove panel grid lines from correlation plots #' pm <- ggpairs( #' flea, columns = 2:4, #' upper = list(continuous = wrap(ggally_cor, displayGrid = FALSE)) #' ) #' p_(pm) #' #' ## Custom with/height of subplots #' pm <- ggpairs(tips, columns = c(2, 3, 5)) #' p_(pm) #' #' pm <- ggpairs(tips, columns = c(2, 3, 5), proportions = "auto") #' p_(pm) #' #' pm <- ggpairs(tips, columns = c(2, 3, 5), proportions = c(1, 3, 2)) #' p_(pm) #' ggpairs <- function( data, mapping = NULL, columns = 1:ncol(data), title = NULL, upper = list(continuous = "cor", combo = "box_no_facet", discrete = "count", na = "na"), lower = list(continuous = "points", combo = "facethist", discrete = "facetbar", na = "na"), diag = list(continuous = "densityDiag", discrete = "barDiag", na = "naDiag"), params = NULL, ..., xlab = NULL, ylab = NULL, axisLabels = c("show", "internal", "none"), columnLabels = colnames(data[columns]), labeller = "label_value", switch = NULL, showStrips = NULL, legend = NULL, cardinality_threshold = 15, progress = NULL, proportions = NULL, legends = stop("deprecated") ){ warn_deprecated(!missing(legends), "legends") warn_if_args_exist(list(...)) stop_if_params_exist(params) isSharedData <- inherits(data, "SharedData") data_ <- fix_data(data) data <- fix_data_slim(data_, isSharedData) if ( !missing(mapping) & !is.list(mapping) & missing(columns) ) { columns <- mapping mapping <- NULL } stop_if_bad_mapping(mapping) columns <- fix_column_values(data, columns, columnLabels, "columns", "columnLabels") stop_if_high_cardinality(data, columns, cardinality_threshold) upper <- check_and_set_ggpairs_defaults( "upper", upper, continuous = "cor", combo = "box_no_facet", discrete = "count", na = "na" ) lower <- check_and_set_ggpairs_defaults( "lower", lower, continuous = "points", combo = "facethist", discrete = "facetbar", na = "na" ) diag <- check_and_set_ggpairs_defaults( "diag", diag, continuous = "densityDiag", discrete = "barDiag", na = "naDiag", isDiag = TRUE ) axisLabels <- fix_axis_label_choice(axisLabels, c("show", "internal", "none")) proportions <- ggmatrix_proportions(proportions, data, columns) # get plot type information dataTypes <- plot_types(data, columns, columns, allowDiag = TRUE) # make internal labels on the diag axis if (identical(axisLabels, "internal")) { dataTypes$plotType[dataTypes$posX == dataTypes$posY] <- "label" } ggpairsPlots <- lapply(seq_len(nrow(dataTypes)), function(i) { plotType <- dataTypes[i, "plotType"] posX <- dataTypes[i, "posX"] posY <- dataTypes[i, "posY"] xColName <- dataTypes[i, "xVar"] yColName <- dataTypes[i, "yVar"] if (posX > posY) { types <- upper } else if (posX < posY) { types <- lower } else { types <- diag } sectionAes <- add_and_overwrite_aes( add_and_overwrite_aes( aes_(x = as.name(xColName), y = as.name(yColName)), mapping ), types$mapping ) args <- list(types = types, sectionAes = sectionAes) if (plotType == "label") { args$label <- columnLabels[posX] } plot_fn <- ggmatrix_plot_list(plotType) p <- do.call(plot_fn, args) return(p) }) plotMatrix <- ggmatrix( plots = ggpairsPlots, byrow = TRUE, nrow = length(columns), ncol = length(columns), xAxisLabels = (if (axisLabels == "internal") NULL else columnLabels), yAxisLabels = (if (axisLabels == "internal") NULL else columnLabels), labeller = labeller, switch = switch, showStrips = showStrips, showXAxisPlotLabels = identical(axisLabels, "show"), showYAxisPlotLabels = identical(axisLabels, "show"), title = title, xlab = xlab, ylab = ylab, data = data_, gg = NULL, progress = progress, legend = legend, xProportions = proportions, yProportions = proportions ) plotMatrix } #' Add new aes #' #' Add new aesthetics to a previous aes. #' #' @keywords internal #' @author Barret Schloerke #' @return aes_ output #' @import ggplot2 #' @rdname add_and_overwrite_aes #' @examples #' data(diamonds, package="ggplot2") #' diamonds.samp <- diamonds[sample(1:dim(diamonds)[1], 1000), ] #' pm <- ggpairs(diamonds.samp, columns = 5:7, #' mapping = ggplot2::aes(color = color), #' upper = list(continuous = "cor", mapping = ggplot2::aes_string(color = "clarity")), #' lower = list(continuous = "cor", mapping = ggplot2::aes_string(color = "cut")), #' title = "Diamonds Sample" #' ) #' str(pm) #' add_and_overwrite_aes <- function(current, new) { if (length(new) >= 1) { for (i in 1:length(new)) { current[names(new)[i]] <- new[i] } } for (curName in names(current)) { if (is.null(current[[curName]])) { current[[curName]] <- NULL } } current } #' Aesthetic mapping color fill #' #' Replace the fill with the color and make color NULL. #' #' @param current the current aesthetics #' @export mapping_color_to_fill <- function(current) { if (is.null(current)) { return(aes()) } currentNames <- names(current) color <- c("color", "colour") if (any(color %in% currentNames) && "fill" %in% currentNames) { # do nothing } else if (any(color %in% currentNames)) { # fill <- current[["fill" %in% currentNames]] # col <- current[[color %in% currentNames]] # current <- add_and_overwrite_aes(current, aes_string(fill = col, color = NA)) current$fill <- current$colour current$colour <- NULL } # if(!is.null(mapping$colour) && !is.null(mapping$fill)) { # # do nothing # } else if(!is.null(mapping$colour)) { # } current } set_to_blank_list_if_blank <- function( val, combo = TRUE, blank = "blank", isDuo = FALSE ) { isBlank <- is.null(val) if (!isBlank) { isBlank <- (!is.list(val) && (val == blank || val == "blank")) } if (isBlank) { val <- list() val$continuous <- blank if (combo) { val$combo <- blank } if (isDuo) { val$comboVertical <- blank val$comboHorizontal <- blank } val$discrete <- blank val$na <- blank } val } check_and_set_ggpairs_defaults <- function( name, obj, continuous = NULL, combo = NULL, discrete = NULL, na = NULL, isDiag = FALSE, isDuo = FALSE ) { blankVal <- ifelse(isDiag, "blankDiag", "blank") obj <- set_to_blank_list_if_blank( obj, combo = ! isDiag & ! isDuo, blank = blankVal, isDuo = isDuo ) if (!is.list(obj)) { stop("'", name, "' is not a list") } stop_if_params_exist(obj$params) if (is.null(obj$continuous) && (!is.null(continuous))) { obj$continuous <- continuous } if (is.null(obj$combo) && (!is.null(combo))) { obj$combo <- combo } if (is.null(obj$discrete) && (!is.null(discrete))) { obj$discrete <- discrete } if (is.null(obj$na) && (!is.null(na))) { obj$na <- na } if (! is.null(obj$aes_string)) { stop( "'aes_string' is a deprecated element for the section ", name, ".\n", "Please use 'mapping' instead. " ) } if (isDiag) { for (key in c("continuous", "discrete", "na")) { val <- obj[[key]] if (is.character(val)) { if (! str_detect(val, "Diag$")) { newVal <- paste(val, "Diag", sep = "") warning(paste( "Changing diag$", key, " from '", val, "' to '", newVal, "'", sep = "" )) obj[[key]] <- newVal } } } } obj } get_subtype_name <- function(.subType) { fn <- wrapp(.subType) ret <- attr(fn, "name") if (ret == ".subType") { ret <- "custom_function" } ret } stop_if_params_exist <- function(params) { if (! is.null(params)) { stop( "'params' is a deprecated argument. ", "Please 'wrap' the function to supply arguments. ", "help(\"wrap\", package = \"GGally\")" ) } } ggmatrix_proportions <- function(proportions, data, columns) { if (is.null(proportions)) { return(proportions) } # relative proportions if "auto" # wrap in isTRUE for safe guarding if (isTRUE(length(proportions) == 1 && proportions == "auto")) { proportions <- c() for (v in columns) { if (is.numeric(data[[v]])) proportions <- c(proportions, NA) else proportions <- c(proportions, length(levels(as.factor(data[[v]])))) } # for numeric variables, the average proportions[is.na(proportions)] <- mean(proportions, na.rm = TRUE) proportions[is.na(proportions)] <- 1 # in case all are numeric } else { is_valid_type <- vapply(proportions, function(x) { (!is.na(x)) && ( grid::is.unit(x) || is.numeric(x) ) }, logical(1)) if (!all(is_valid_type)) { stop("proportions need to be non-NA numeric values or 'auto'. proportions: ", dput_val(proportions)) } } if (length(proportions) == 1 && length(columns) > 1) { proportions <- replicate(length(columns), proportions) } proportions } dput_val <- function(x) { f <- file() on.exit({ close(f) }) dput(x, f) ret <- paste0(readLines(f), collapse = "\n") ret } #diamondMatrix <- ggpairs( # diamonds, # columns = 8:10, # upper = list(points = "scatterplot", aes_string = aes_string(color = "cut")), # lower = list(points = "scatterplot", aes_string = aes_string(color = "cut")), # diag = "blank", ## color = "color", # title = "Diamonds" #) #if(TRUE) #{ # #d <- diamonds[runif(floor(nrow(diamonds)/10), 0, nrow(diamonds)), ] # #diamondMatrix <- ggpairs( # d, # columns = 8:10, # upper = list(continuous = "points", aes_string = aes_string(color = "clarity")), # lower = list(continuous = "points", aes_string = aes_string(color = "cut")), # diag = "blank", ## color = "color", # title = "Diamonds" #) # # #m <- mtcars ##m$vs <- as.factor(m$vs) ##m$cyl <- as.factor(m$cyl) ##m$qsec <- as.factor(m$qsec) #carsMatrix <- ggpairs( # mtcars, # columns = c(1, 3, 4), # upper = list(continuous = "points", aes_string = aes_string(shape = "cyl", size = 5)), # lower = list(continuous = "points", aes_string = aes_string(size = "cyl")), # diag = "blank", # color = "cyl", # title = "mtcars", #) # # # carsMatrix <- ggpairs( # mtcars, # columns = c(1, 3, 4), # upper = list(aes_string = aes_string(shape = "as.factor(cyl)", size = 5)), # lower = list(aes_string = aes_string(size = "as.factor(cyl)")), # diag = "blank", # color = "cyl", # title = "Custom Cars", # ) # # #} GGally/R/ggparcoord.R0000644000176200001440000006141213761572054014122 0ustar liggesusersif (getRversion() >= "2.15.1") { utils::globalVariables(c("variable", "value", "ggally_splineFactor")) } #' Parallel coordinate plot #' #' A function for plotting static parallel coordinate plots, utilizing #' the \code{ggplot2} graphics package. #' #' \code{scale} is a character string that denotes how to scale the variables #' in the parallel coordinate plot. Options: #' \itemize{ #' \item{\code{std}}{: univariately, subtract mean and divide by standard deviation} #' \item{\code{robust}}{: univariately, subtract median and divide by median absolute deviation} #' \item{\code{uniminmax}}{: univariately, scale so the minimum of the variable is zero, and the maximum is one} #' \item{\code{globalminmax}}{: no scaling is done; the range of the graphs is defined #' by the global minimum and the global maximum} #' \item{\code{center}}{: use \code{uniminmax} to standardize vertical height, then #' center each variable at a value specified by the \code{scaleSummary} param} #' \item{\code{centerObs}}{: use \code{uniminmax} to standardize vertical height, then #' center each variable at the value of the observation specified by the \code{centerObsID} param} #' } #' #' \code{missing} is a character string that denotes how to handle missing #' missing values. Options: #' \itemize{ #' \item{\code{exclude}}{: remove all cases with missing values} #' \item{\code{mean}}{: set missing values to the mean of the variable} #' \item{\code{median}}{: set missing values to the median of the variable} #' \item{\code{min10}}{: set missing values to 10% below the minimum of the variable} #' \item{\code{random}}{: set missing values to value of randomly chosen observation on that variable} #' } #' #' \code{order} is either a vector of indices or a character string that denotes how to #' order the axes (variables) of the parallel coordinate plot. Options: #' \itemize{ #' \item{\code{(default)}}{: order by the vector denoted by \code{columns}} #' \item{\code{(given vector)}}{: order by the vector specified} #' \item{\code{anyClass}}{: order variables by their separation between any one class and #' the rest (as opposed to their overall variation between classes). This is accomplished #' by calculating the F-statistic for each class vs. the rest, for each axis variable. #' The axis variables are then ordered (decreasing) by their maximum of k F-statistics, #' where k is the number of classes.} #' \item{\code{allClass}}{: order variables by their overall F statistic (decreasing) from #' an ANOVA with \code{groupColumn} as the explanatory variable (note: it is required #' to specify a \code{groupColumn} with this ordering method). Basically, this method #' orders the variables by their variation between classes (most to least).} #' \item{\code{skewness}}{: order variables by their sample skewness (most skewed to #' least skewed)} #' \item{\code{Outlying}}{: order by the scagnostic measure, Outlying, as calculated #' by the package \code{scagnostics}. Other scagnostic measures available to order #' by are \code{Skewed}, \code{Clumpy}, \code{Sparse}, \code{Striated}, \code{Convex}, \code{Skinny}, \code{Stringy}, and #' \code{Monotonic}. Note: To use these methods of ordering, you must have the \code{scagnostics} #' package loaded.} #' } #' #' @param data the dataset to plot #' @param columns a vector of variables (either names or indices) to be axes in the plot #' @param groupColumn a single variable to group (color) by #' @param scale method used to scale the variables (see Details) #' @param scaleSummary if scale=="center", summary statistic to univariately #' center each variable by #' @param centerObsID if scale=="centerObs", row number of case plot should #' univariately be centered on #' @param missing method used to handle missing values (see Details) #' @param order method used to order the axes (see Details) #' @param showPoints logical operator indicating whether points should be #' plotted or not #' @param splineFactor logical or numeric operator indicating whether spline interpolation should be used. Numeric values will multiplied by the number of columns, \code{TRUE} will default to cubic interpolation, \code{\link[base]{AsIs}} to set the knot count directly and \code{0}, \code{FALSE}, or non-numeric values will not use spline interpolation. #' @param alphaLines value of alpha scaler for the lines of the parcoord plot or a column name of the data #' @param boxplot logical operator indicating whether or not boxplots should #' underlay the distribution of each variable #' @param shadeBox color of underlying box which extends from the min to the #' max for each variable (no box is plotted if \code{shadeBox == NULL}) #' @param mapping aes string to pass to ggplot object #' @param title character string denoting the title of the plot #' @author Jason Crowley, Barret Schloerke, Di Cook, Heike Hofmann, Hadley Wickham #' @return ggplot object that if called, will print #' @import plyr #' @importFrom reshape melt melt.data.frame #' @importFrom stats complete.cases sd median mad lm spline #' @export #' @examples #' # small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' # use sample of the diamonds data for illustrative purposes #' data(diamonds, package="ggplot2") #' diamonds.samp <- diamonds[sample(1:dim(diamonds)[1], 100), ] #' #' # basic parallel coordinate plot, using default settings #' p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10)) #' p_(p) #' #' # this time, color by diamond cut #' p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2) #' p_(p) #' #' # underlay univariate boxplots, add title, use uniminmax scaling #' p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, #' scale = "uniminmax", boxplot = TRUE, title = "Parallel Coord. Plot of Diamonds Data") #' p_(p) #' #' # utilize ggplot2 aes to switch to thicker lines #' p <- ggparcoord(data = diamonds.samp, columns = c(1, 5:10), groupColumn = 2, #' title ="Parallel Coord. Plot of Diamonds Data", mapping = ggplot2::aes(size = 1)) + #' ggplot2::scale_size_identity() #' p_(p) #' #' # basic parallel coord plot of the msleep data, using 'random' imputation and #' # coloring by diet (can also use variable names in the columns and groupColumn #' # arguments) #' data(msleep, package="ggplot2") #' p <- ggparcoord(data = msleep, columns = 6:11, groupColumn = "vore", missing = #' "random", scale = "uniminmax") #' p_(p) #' #' # center each variable by its median, using the default missing value handler, #' # 'exclude' #' p <- ggparcoord(data = msleep, columns = 6:11, groupColumn = "vore", scale = #' "center", scaleSummary = "median") #' p_(p) #' #' # with the iris data, order the axes by overall class (Species) separation using #' # the anyClass option #' p <- ggparcoord(data = iris, columns = 1:4, groupColumn = 5, order = "anyClass") #' p_(p) #' #' # add points to the plot, add a title, and use an alpha scalar to make the lines #' # transparent #' p <- ggparcoord(data = iris, columns = 1:4, groupColumn = 5, order = "anyClass", #' showPoints = TRUE, title = "Parallel Coordinate Plot for the Iris Data", #' alphaLines = 0.3) #' p_(p) #' #' # color according to a column #' iris2 <- iris #' iris2$alphaLevel <- c("setosa" = 0.2, "versicolor" = 0.3, "virginica" = 0)[iris2$Species] #' p <- ggparcoord(data = iris2, columns = 1:4, groupColumn = 5, order = "anyClass", #' showPoints = TRUE, title = "Parallel Coordinate Plot for the Iris Data", #' alphaLines = "alphaLevel") #' p_(p) #' #' ## Use splines on values, rather than lines (all produce the same result) #' columns <- c(1, 5:10) #' p <- ggparcoord(diamonds.samp, columns, groupColumn = 2, splineFactor = TRUE) #' p_(p) #' p <- ggparcoord(diamonds.samp, columns, groupColumn = 2, splineFactor = 3) #' p_(p) #' splineFactor <- length(columns) * 3 #' p <- ggparcoord(diamonds.samp, columns, groupColumn = 2, splineFactor = I(splineFactor)) #' p_(p) ggparcoord <- function( data, columns = 1:ncol(data), groupColumn = NULL, scale = "std", scaleSummary = "mean", centerObsID = 1, missing = "exclude", order = columns, showPoints = FALSE, splineFactor = FALSE, alphaLines = 1, boxplot = FALSE, shadeBox = NULL, mapping = NULL, title = "" ) { if (! identical(class(data), "data.frame")) { data <- as.data.frame(data) } saveData <- data ### Error Checking ### if (is.null(groupColumn)) { if (any(tolower(order) %in% c("anyclass", "allclass"))) { stop("can't use the 'order' methods anyClass or allClass without specifying groupColumn") } } else if ( !( (length(groupColumn) == 1) && (is.numeric(groupColumn) || is.character(groupColumn))) ) { stop("invalid value for 'groupColumn'; must be a single numeric or character index") } if (!(tolower(scale) %in% c( "std", "robust", "uniminmax", "globalminmax", "center", "centerobs" ))) { stop(str_c( "invalid value for 'scale'; must be one of ", "'std', 'robust', 'uniminmax', 'globalminmax', 'center', or 'centerObs'" )) } if (!(centerObsID %in% 1:dim(data)[1])) { stop("invalid value for 'centerObsID'; must be a single numeric row index") } if (!(tolower(missing) %in% c("exclude", "mean", "median", "min10", "random"))) { stop( "invalid value for 'missing'; must be one of 'exclude', 'mean', 'median', 'min10', 'random'" ) } if (!( is.numeric(order) || ( is.character(order) && (order %in% c( "skewness", "allClass", "anyClass", "Outlying", "Skewed", "Clumpy", "Sparse", "Striated", "Convex", "Skinny", "Stringy", "Monotonic" )) )) ) { stop(str_c( "invalid value for 'order'; must either be a vector of column indices or one of ", "'skewness', 'allClass', 'anyClass', 'Outlying', 'Skewed', 'Clumpy', 'Sparse', 'Striated', ", "'Convex', 'Skinny', 'Stringy', 'Monotonic'" )) } if (!(is.logical(showPoints))) { stop("invalid value for 'showPoints'; must be a logical operator") } alphaLinesIsCharacter <- is.character(alphaLines) if (alphaLinesIsCharacter) { if (!(alphaLines %in% names(data))) { stop("'alphaLines' column is missing in data") } alphaVar <- data[[alphaLines]] alphaRange <- range(alphaVar) if (any(is.na(alphaRange))) { stop("missing data in 'alphaLines' column") } if (alphaRange[1] < 0 || alphaRange[2] > 1) { stop("invalid value for 'alphaLines' column; max range must be from 0 to 1") } } else if ((alphaLines < 0) || (alphaLines > 1)) { # nolint stop("invalid value for 'alphaLines'; must be a scalar value between 0 and 1") } if (!(is.logical(boxplot))) { stop("invalid value for 'boxplot'; must be a logical operator") } if (!is.null(shadeBox) && length(shadeBox) != 1) { stop("invalid value for 'shadeBox'; must be a single color") } else { valid_color <- tryCatch(is.matrix(grDevices::col2rgb(shadeBox)), error = function(e) FALSE) if (!valid_color) { stop("invalid value for 'shadeBox'; must be a valid R color") } } if (is.logical(splineFactor)) { if (splineFactor) { splineFactor <- 3 } else { splineFactor <- 0 } } else if (! is.numeric(splineFactor)) { stop("invalid value for 'splineFactor'; must be a logical or numeric value") } ### Setup ### if (is.numeric(groupColumn)) { groupColumn <- names(data)[groupColumn] } if (!is.null(groupColumn)) { groupVar <- data[[groupColumn]] } if (is.character(columns)) { columns_ <- c() for (colPos in seq_along(columns)) { columns_[colPos] <- which(colnames(data) == columns[colPos]) } columns <- columns_ } # data <- data[columns] # Change character vars to factors char.vars <- column_is_character(data) if (length(char.vars) >= 1) { for (char.var in char.vars) { data[[char.var]] <- factor(data[[char.var]]) } } # Change factors to numeric fact.vars <- column_is_factor(data) fact.vars <- setdiff(fact.vars, groupColumn) if (length(fact.vars) >= 1) { for (fact.var in fact.vars) { data[[fact.var]] <- as.numeric(data[[fact.var]]) } } # Save this form of the data for order calculations (don't want imputed # missing values affecting order, but do want any factor/character vars # being plotted as numeric) saveData2 <- data if (!is.null(groupColumn)) { saveData2[[groupColumn]] <- as.numeric(saveData2[[groupColumn]]) } p <- c(ncol(data) + 1, ncol(data) + 2) data$.ID <- as.factor(1:nrow(data)) data$anyMissing <- apply(is.na(data[, columns]), 1, any) columnsPlusTwo <- c(columns, p) inner_rescaler_default <- function (x, type = "sd", ...) { # copied directly from reshape because of import difficulties :-( # rescaler.default switch(type, rank = rank(x, ...), var = , # nolint sd = (x - mean(x, na.rm = TRUE)) / sd(x, na.rm = TRUE), robust = (x - median(x, na.rm = TRUE)) / mad(x, na.rm = TRUE), I = x, range = (x - min(x, na.rm = TRUE)) / diff(range(x, na.rm = TRUE)) ) } inner_rescaler <- function(x, type = "sd", ...) { # copied directly from reshape because of import difficulties :-( # rescaler.data.frame continuous <- sapply(x, is.numeric) if (any(continuous)) { if (type %in% c("sd", "robust", "range")) { # indicating columns containing only one single value singleVal <- sapply(x, function(col){ if (length(unique(col)) == 1) { TRUE } else { FALSE } }) ind <- continuous & !singleVal x[ind] <- lapply(x[ind], inner_rescaler_default, type = type, ...) x[singleVal] <- 1 } else { x[continuous] <- lapply(x[continuous], inner_rescaler_default, type = type, ...) } } x } ### Scaling ### if (tolower(scale) %in% c("std", "robust", "uniminmax", "center")) { rescalerType <- c( "std" = "sd", "robust" = "robust", "uniminmax" = "range", "center" = "range" )[tolower(scale)] data[columnsPlusTwo] <- inner_rescaler(data[columnsPlusTwo], type = rescalerType) if (tolower(scale) == "center") { data[columns] <- apply(data[columns], 2, function(x) { x <- x - eval( parse(text = paste( scaleSummary, "(x, na.rm=TRUE)", sep = "" )) ) }) } } ### Imputation ### if (tolower(missing) == "exclude") { dataCompleteCases <- complete.cases(data[columnsPlusTwo]) if (!is.null(groupColumn)) { groupVar <- groupVar[dataCompleteCases] } if (alphaLinesIsCharacter) { alphaVar <- alphaVar[dataCompleteCases] } data <- data[dataCompleteCases, ] } else if (tolower(missing) %in% c("mean", "median", "min10", "random")) { missingFns <- list( mean = function(x) { mean(x, na.rm = TRUE) }, median = function(x) { median(x, na.rm = TRUE) }, min10 = function(x){ 0.9 * min(x, na.rm = TRUE) }, random = function(x) { num <- sum(is.na(x)) idx <- sample(which(!is.na(x)), num, replace = TRUE) x[idx] } ) missing_fn <- missingFns[[tolower(missing)]] data[columns] <- apply(data[columns], 2, function(x) { if (any(is.na(x))){ x[is.na(x)] <- missing_fn(x) } return(x) }) } ### Scaling (round 2) ### # Centering by observation needs to be done after handling missing values # in case the observation to be centered on has missing values if (tolower(scale) == "centerobs") { data[columnsPlusTwo] <- inner_rescaler(data[columnsPlusTwo], type = "range") data[columns] <- apply(data[columns], 2, function(x){ x <- x - x[centerObsID] }) } # meltIDVars <- c(".ID", "anyMissing") meltIDVars <- colnames(data)[-columns] if (!is.null(groupColumn)) { # data <- cbind(data, groupVar) # names(data)[dim(data)[2]] <- groupCol meltIDVars <- union(groupColumn, meltIDVars) } if (alphaLinesIsCharacter) { data <- cbind(data, alphaVar) names(data)[dim(data)[2]] <- alphaLines meltIDVars <- union(meltIDVars, alphaLines) } # if(is.list(mapping)) { # mappingNames <- names(mapping) # } data.m <- melt(data, id.vars = meltIDVars, measure.vars = columns) ### Ordering ### if (length(order) > 1 & is.numeric(order)) { data.m$variable <- factor(data.m$variable, levels = names(saveData)[order]) } else if (order %in% c("Outlying", "Skewed", "Clumpy", "Sparse", "Striated", "Convex", "Skinny", "Stringy", "Monotonic")) { require_namespaces("scagnostics") scag <- scagnostics::scagnostics(saveData2) data.m$variable <- factor(data.m$variable, levels = scag_order(scag, names(saveData2), order)) } else if (tolower(order) == "skewness") { abs.skew <- abs(apply(saveData2, 2, skewness)) data.m$variable <- factor( data.m$variable, levels = names(abs.skew)[order(abs.skew, decreasing = TRUE)] ) } else if (tolower(order) == "allclass") { f.stats <- rep(NA, length(columns)) names(f.stats) <- names(saveData2[columns]) for (i in 1:length(columns)) { f.stats[i] <- summary(lm(saveData2[, i] ~ groupVar))$fstatistic[1] } data.m$variable <- factor( data.m$variable, levels = names(f.stats)[order(f.stats, decreasing = TRUE)] ) } else if (tolower(order) == "anyclass") { axis.order <- singleClassOrder(groupVar, saveData2) data.m$variable <- factor(data.m$variable, levels = axis.order) } if (!is.null(groupColumn)) { mapping2 <- aes_string( x = "variable", y = "value", group = ".ID", colour = groupColumn ) } else { mapping2 <- aes_string( x = "variable", y = "value", group = ".ID" ) } mapping2 <- add_and_overwrite_aes(mapping2, mapping) # mapping2 <- add_and_overwrite_aes(aes_string(size = I(0.5)), mapping2) p <- ggplot(data = data.m, mapping = mapping2) if (!is.null(shadeBox)) { # Fix so that if missing = "min10", the box only goes down to the true min d.sum <- ddply(data.m, c("variable"), summarize, min = min(value), max = max(value)) p <- p + geom_linerange( data = d.sum, size = I(10), col = shadeBox, inherit.aes = FALSE, mapping = aes_string( x = "variable", ymin = "min", ymax = "max", group = "variable" ) ) } if (boxplot) { p <- p + geom_boxplot(mapping = aes_string(group = "variable"), alpha = 0.8) } if (!is.null(mapping2$size)) { lineSize <- mapping2$size } else { lineSize <- 0.5 } if (splineFactor > 0) { data.m$ggally_splineFactor <- splineFactor if (class(splineFactor) == "AsIs") { data.m <- ddply( data.m, ".ID", transform, spline = spline(variable, value, n = ggally_splineFactor[1]) ) } else { data.m <- ddply( data.m, ".ID", transform, spline = spline(variable, value, n = length(variable) * ggally_splineFactor[1]) ) } linexvar <- "spline.x" lineyvar <- "spline.y" if (alphaLinesIsCharacter) { p <- p + geom_line( aes_string(x = linexvar, y = lineyvar, alpha = alphaLines), size = lineSize, data = data.m ) + scale_alpha(range = alphaRange) } else { p <- p + geom_line( aes_string(x = linexvar, y = lineyvar), alpha = alphaLines, size = lineSize, data = data.m ) } if (showPoints) { p <- p + geom_point(aes(x = as.numeric(variable), y = value)) } xAxisLabels <- levels(data.m$variable) # while continuous data, this makes it present like it's discrete p <- p + scale_x_continuous( breaks = seq_along(xAxisLabels), labels = xAxisLabels, minor_breaks = FALSE ) } else { if (alphaLinesIsCharacter) { p <- p + geom_line(aes_string(alpha = alphaLines), size = lineSize, data = data.m) + scale_alpha(range = alphaRange) } else { # p <- p + geom_line(alpha = alphaLines, size = lineSize) p <- p + geom_line(alpha = alphaLines) } if (showPoints) { p <- p + geom_point() } } if (title != "") { p <- p + labs(title = title) } p } #' Get vector of variable types from data frame #' #' @keywords internal #' @param df data frame to extract variable types from #' @author Jason Crowley #' @return character vector with variable types, with names corresponding to #' the variable names from df column_is_character <- function(df) { x <- unlist(lapply(unclass(df), is.character)) names(x)[x] } #' @rdname column_is_character column_is_factor <- function(df) { x <- unlist(lapply(unclass(df), is.factor)) names(x)[x] } #' Find order of variables #' #' Find order of variables based on a specified scagnostic measure #' by maximizing the index values of that measure along the path. #' #' @param scag \code{scagnostics} object #' @param vars character vector of the variables to be ordered #' @param measure scagnostics measure to order according to #' @author Barret Schloerke #' @return character vector of variable ordered according to the given #' scagnostic measure scag_order <- function(scag, vars, measure) { scag <- sort(scag[measure, ], decreasing = TRUE) scagNames <- names(scag) # retrieve all names. assume name doesn't contain a space nameLocs <- regexec("^([^ ]+) \\* ([^ ]+)$", scagNames) colNames <- lapply(seq_along(nameLocs), function(i) { nameLoc <- nameLocs[[i]] scagName <- scagNames[[i]] # retrieve the column name from "FIRSTNAME * SECONDNAME" substr(rep(scagName, 2), nameLoc[-1], nameLoc[-1] + attr(nameLoc, "match.length")[-1] - 1) }) ret <- c() colNamesLength <- length(colNames) colNameValues <- unlist(colNames) for (i in seq_along(colNames)) { cols <- colNames[[i]] colsUsed <- cols %in% ret # if none of the columns have been added... if (colsUsed[1] == FALSE && colsUsed[2] == FALSE) { # find out which column comes next in the set, append that one first if (i < colNamesLength) { remainingColumns <- colNameValues[(2 * (i + 1)):(2 * colNamesLength)] col1Pos <- which.min(cols[1] == remainingColumns) col2Pos <- which.min(cols[2] == remainingColumns) if (col2Pos < col1Pos) { cols <- rev(cols) } ret <- append(ret, cols) } else { # nothing left in set, append both ret <- append(ret, cols) } # if only the first hasn't been added... } else if (colsUsed[1] == FALSE) { ret <- append(ret, cols[1]) # if only the second hasn't been added... } else if (colsUsed[2] == FALSE) { ret <- append(ret, cols[2]) } } if (length(ret) != length(vars)) { stop(str_c( "Could not compute a correct ordering: ", length(vars) - length(ret), " values are missing. ", "Missing: ", paste0(vars[! (vars %in% ret)], collapse = ", ") )) } return(ret) } #' Order axis variables #' #' Order axis variables by separation between one class and the rest #' (most separation to least). #' #' @param classVar class variable (vector from original dataset) #' @param axisVars variables to be plotted as axes (data frame) #' @param specClass character string matching to level of \code{classVar}; instead #' of looking for separation between any class and the rest, will only look for #' separation between this class and the rest #' @author Jason Crowley #' @importFrom stats lm #' @return character vector of names of axisVars ordered such that the first #' variable has the most separation between one of the classes and the rest, and #' the last variable has the least (as measured by F-statistics from an ANOVA) singleClassOrder <- function(classVar, axisVars, specClass=NULL) { if (!is.null(specClass)) { # for when user is interested in ordering by variation between one class and # the rest...will add this later } else { var.names <- colnames(axisVars) class.names <- levels(classVar) f.stats <- matrix(NA, nrow = length(class.names), ncol = length(var.names), dimnames = list(class.names, var.names)) for (i in 1:length(class.names)) { f.stats[i, ] <- apply(axisVars, 2, function(x) { return(summary(lm(x ~ as.factor(classVar == class.names[i])))$fstatistic[1]) }) } var.maxF <- apply(f.stats, 2, max) return(names(var.maxF)[order(var.maxF, decreasing = TRUE)]) } } #' Sample skewness #' #' Calculate the sample skewness of a vector #' while ignoring missing values. #' #' @param x numeric vector #' @author Jason Crowley #' @return sample skewness of \code{x} skewness <- function(x) { x <- x[!is.na(x)] xbar <- mean(x) n <- length(x) skewness <- (1 / n) * sum( (x - xbar) ^ 3) / ( (1 / n) * sum( (x - xbar) ^ 2)) ^ (3 / 2) return(skewness) } GGally/R/ggcoef.R0000644000176200001440000001020713764714663013230 0ustar liggesusers#' Model coefficients with \pkg{broom} and \pkg{ggplot2} #' #' Plot the coefficients of a model with \pkg{broom} and \pkg{ggplot2}. #' For an updated and improved version, see [ggcoef_model()]. #' #' @param x a model object to be tidied with [broom::tidy()] or a data frame (see Details) #' @param mapping default aesthetic mapping #' @param conf.int display confidence intervals as error bars? #' @param conf.level level of confidence intervals (passed to [broom::tidy()] #' if \code{x} is not a data frame) #' @param exponentiate if \code{TRUE}, x-axis will be logarithmic (also passed to [broom::tidy()] #' if \code{x} is not a data frame) #' @param exclude_intercept should the intercept be excluded from the plot? #' @param vline print a vertical line? #' @param vline_intercept \code{xintercept} for the vertical line. #' \code{"auto"} for \code{x = 0} (or \code{x = 1} if {exponentiate} is \code{TRUE}) #' @param vline_color color of the vertical line #' @param vline_linetype line type of the vertical line #' @param vline_size size of the vertical line #' @param errorbar_color color of the error bars #' @param errorbar_height height of the error bars #' @param errorbar_linetype line type of the error bars #' @param errorbar_size size of the error bars #' @param sort \code{"none"} (default) do not sort, \code{"ascending"} sort by increasing coefficient value, or \code{"descending"} sort by decreasing coefficient value #' @param ... additional arguments sent to [ggplot2::geom_point()] #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' library(broom) #' reg <- lm(Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width, data = iris) #' p_(ggcoef(reg)) #' \donttest{d <- as.data.frame(Titanic) #' reg2 <- glm(Survived ~ Sex + Age + Class, family = binomial, data = d, weights = d$Freq) #' ggcoef(reg2, exponentiate = TRUE) #' ggcoef( #' reg2, exponentiate = TRUE, exclude_intercept = TRUE, #' errorbar_height = .2, color = "blue", sort = "ascending" #' )} #' @export ggcoef <- function( x, mapping = aes_string(y = "term", x = "estimate"), conf.int = TRUE, conf.level = 0.95, exponentiate = FALSE, exclude_intercept = FALSE, vline = TRUE, vline_intercept = "auto", vline_color = "gray50", vline_linetype = "dotted", vline_size = 1, errorbar_color = "gray25", errorbar_height = 0, errorbar_linetype = "solid", errorbar_size = .5, sort = c("none", "ascending", "descending"), ... ) { if (!is.data.frame(x)) { require_namespaces("broom") x <- broom::tidy( x, conf.int = conf.int, conf.level = conf.level, exponentiate = exponentiate ) } if (!("term" %in% names(x))) { stop("x doesn't contain a column names 'term'.") } if (!("estimate" %in% names(x))) { stop("x doesn't contain a column names 'estimate'.") } if (exclude_intercept) { x <- x[x$term != "(Intercept)", ] } sort <- match.arg(sort) if (sort != "none") { x$term <- as.factor(x$term) if (sort == "ascending") { new_order <- order(x$estimate, decreasing = FALSE) } else { new_order <- order(x$estimate, decreasing = TRUE) } x$term <- as.character(x$term) x$term <- factor(x$term, levels = x$term[new_order]) } p <- ggplot(x, mapping = mapping) if (vline) { if (exponentiate) { if (vline_intercept == "auto") { vline_intercept <- 1 } p <- p + geom_vline( xintercept = vline_intercept, color = vline_color, linetype = vline_linetype, size = vline_size ) + scale_x_log10() } else { if (vline_intercept == "auto") { vline_intercept <- 0 } p <- p + geom_vline( xintercept = vline_intercept, color = vline_color, linetype = vline_linetype, size = vline_size ) } } if (conf.int & "conf.low" %in% names(x) & "conf.high" %in% names(x)) p <- p + geom_errorbarh( aes_string(xmin = "conf.low", xmax = "conf.high"), color = errorbar_color, height = errorbar_height, linetype = errorbar_linetype, size = errorbar_size ) p + geom_point(...) } GGally/R/ggscatmat.R0000644000176200001440000003066213761572054013750 0ustar liggesusersif (getRversion() >= "2.15.1") { utils::globalVariables(c("xvalue", "yvalue")) } #' lowertriangle - rearrange dataset as the preparation of \code{\link{ggscatmat}} function #' #' function for making the melted dataset used to plot the lowertriangle scatterplots. #' #' @export #' @param data a data matrix. Should contain numerical (continuous) data. #' @param columns an option to choose the column to be used in the raw dataset. Defaults to \code{1:ncol(data)} #' @param color an option to choose a factor variable to be grouped with. Defaults to \code{(NULL)} #' @author Mengjia Ni, Di Cook #' @examples #' data(flea) #' head(lowertriangle(flea, columns= 2:4)) #' head(lowertriangle(flea)) #' head(lowertriangle(flea, color="species")) lowertriangle <- function(data, columns=1:ncol(data), color=NULL) { # why do we need to ocheck this again? # data <- upgrade_scatmat_data(data) data.choose <- data[columns] dn <- data.choose[sapply(data.choose, is.numeric)] factor <- data[sapply(data, is.factor)] p <- ncol(dn) q <- nrow(dn) newdata <- as.data.frame(matrix(NA, nrow = q*p*p, ncol = 6+ncol(factor)), stringsAsFactors = FALSE) newdata[5:6] <- as.data.frame(matrix("", nrow = q*p*p, ncol = 2), stringsAsFactors = FALSE) r <-1 for (i in 1:p) { for (j in 1:p) { newdata[r:(r+q-1), 1:6] <- cbind(dn[[i]], dn[[j]], i, j, colnames(dn)[i], colnames(dn)[j]) r <- r+q } } if (ncol(newdata) > 6){newdata[7:ncol(newdata)] <- factor} colnames(newdata) <- c("xvalue", "yvalue", "xslot", "yslot", "xlab", "ylab", colnames(factor)) rp <- data.frame(newdata) rp[[2]][rp[[3]] >= rp[[4]]] <- "NA" rp[[1]][rp[[3]] > rp[[4]]] <- "NA" rp$xvalue <- suppressWarnings(as.numeric(as.character(rp$xvalue))) rp$yvalue <- suppressWarnings(as.numeric(as.character(rp$yvalue))) rp$xslot <- suppressWarnings(as.numeric(as.character(rp$xslot))) rp$yslot <- suppressWarnings(as.numeric(as.character(rp$yslot))) rp$xlab <- factor(rp$xlab, levels = unique(rp$xlab)) rp$ylab <- factor(rp$ylab, levels = unique(rp$ylab)) if (is.null(color)){ rp.new <- rp[1:6] } else { colorcolumn <- rp[[which(colnames(rp) == color)]] rp.new <- cbind(rp[1:6], colorcolumn) } return(rp.new) } #' Rearrange dataset as the preparation of \code{\link{ggscatmat}} function #' #' Function for making the dataset used to plot the uppertriangle plots. #' #' @export #' @param data a data matrix. Should contain numerical (continuous) data. #' @param columns an option to choose the column to be used in the raw dataset. Defaults to \code{1:ncol(data)} #' @param color an option to choose a factor variable to be grouped with. Defaults to \code{(NULL)} #' @param corMethod method argument supplied to \code{\link[stats]{cor}} #' @author Mengjia Ni, Di Cook #' @importFrom stats cor #' @examples #' data(flea) #' head(uppertriangle(flea, columns=2:4)) #' head(uppertriangle(flea)) #' head(uppertriangle(flea, color="species")) uppertriangle <- function(data, columns=1:ncol(data), color=NULL, corMethod = "pearson") { # data <- upgrade_scatmat_data(data) data.choose <- data[columns] # why do we need to ocheck this again? dn <- data.choose[sapply(data.choose, is.numeric)] factor <- data[sapply(data, is.factor)] p <- ncol(dn) newdata <- NULL for (i in 1:p) { for (j in 1:p) { newdata <- rbind(newdata, cbind(dn[, i], dn[, j], i, j, colnames(dn)[i], colnames(dn)[j], min(dn[, i]) + 0.5 * (max(dn[, i]) - min(dn[, i])), min(dn[, j]) + 0.5 * (max(dn[, j]) - min(dn[, j])), factor) ) } } colnames(newdata) <- c( "xvalue", "yvalue", "xslot", "yslot", "xlab", "ylab", "xcenter", "ycenter", colnames(factor) ) rp <- data.frame(newdata, stringsAsFactors = TRUE) rp[[2]][rp[[3]] <= rp[[4]]] <- "NA" rp[[1]][rp[[3]] < rp[[4]]] <- "NA" rp$xvalue <- suppressWarnings(as.numeric(as.character(rp$xvalue))) rp$yvalue <- suppressWarnings(as.numeric(as.character(rp$yvalue))) if (is.null(color)){ rp.new <- rp[1:8] }else{ colorcolumn <- rp[[which(colnames(rp) == color)]] rp.new <- cbind(rp[1:8], colorcolumn) } a <- rp.new b <- subset(a, (a$yvalue != "NA") & (a$xvalue != "NA")) b$xlab <- factor(b$xlab, levels=unique(b$xlab)) b$ylab <- factor(b$ylab, levels=unique(b$ylab)) if (is.null(color)){ data.cor <- ddply( b, .(xlab, ylab), function(subsetDt) { xlab <- subsetDt$xlab ylab <- subsetDt$ylab xvalue <- subsetDt$xvalue yvalue <- subsetDt$yvalue if (identical(corMethod, "rsquare")) { r <- cor( xvalue, yvalue, use = "pairwise.complete.obs", method = "pearson" ) r <- r ^ 2 } else { r <- cor( xvalue, yvalue, use = "pairwise.complete.obs", method = corMethod ) } r <- paste(round(r, digits = 2)) data.frame( xlab = unique(xlab), ylab = unique(ylab), r = r, xvalue = min(xvalue) + 0.5 * (max(xvalue) - min(xvalue)), yvalue = min(yvalue) + 0.5 * (max(yvalue) - min(yvalue)) ) } ) return(data.cor) }else{ c <- b data.cor1 <- ddply( c, .(ylab, xlab, colorcolumn), function(subsetDt) { xlab <- subsetDt$xlab ylab <- subsetDt$ylab colorcolumn <- subsetDt$colorcolumn xvalue <- subsetDt$xvalue yvalue <- subsetDt$yvalue if (identical(corMethod, "rsquare")) { r <- cor( xvalue, yvalue, use = "pairwise.complete.obs", method = "pearson" ) r <- r ^ 2 } else { r <- cor( xvalue, yvalue, use = "pairwise.complete.obs", method = corMethod ) } r <- paste(round(r, digits = 2)) data.frame( ylab = unique(ylab), xlab = unique(xlab), colorcolumn = unique(colorcolumn), r = r ) } ) n <- nrow(data.frame(unique(b$colorcolumn))) position <- ddply(b, .(ylab, xlab), summarise, xvalue = min(xvalue) + 0.5 * (max(xvalue) - min(xvalue)), ymin = min(yvalue), ymax = max(yvalue), range = max(yvalue) - min(yvalue)) df <- data.frame() for (i in 1:nrow(position)) { for (j in 1:n){ row <- position[i, ] df <- rbind(df, cbind(row[, 3], (row[, 4] + row[, 6] * j / (n + 1)))) } } data.cor <- cbind(data.cor1, df) colnames(data.cor) <- c("ylab", "xlab", "colorcolumn", "r", "xvalue", "yvalue") return(data.cor) } } #' Plots the lowertriangle and density plots of the scatter plot matrix. #' #' Function for making scatterplots in the lower triangle and diagonal density plots. #' #' @export #' @param data a data matrix. Should contain numerical (continuous) data. #' @param columns an option to choose the column to be used in the raw dataset. Defaults to \code{1:ncol(data)} #' @param color an option to group the dataset by the factor variable and color them by different colors. Defaults to \code{NULL} #' @param alpha an option to set the transparency in scatterplots for large data. Defaults to \code{1}. #' @author Mengjia Ni, Di Cook #' @examples #' # small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(flea) #' #' p_(scatmat(flea, columns=2:4)) #' p_(scatmat(flea, columns= 2:4, color="species")) scatmat <- function(data, columns=1:ncol(data), color=NULL, alpha=1) { # data <- upgrade_scatmat_data(data) data.choose <- data[columns] dn <- data.choose[sapply(data.choose, is.numeric)] if (ncol(dn) == 0) { stop("All of your variables are factors. Need numeric variables to make scatterplot matrix.") } else { ltdata.new <- lowertriangle(data, columns = columns, color = color) ## set up the plot r <- ggplot(ltdata.new, mapping = aes_string(x = "xvalue", y = "yvalue")) + theme(axis.title.x = element_blank(), axis.title.y = element_blank()) + facet_grid(ylab ~ xlab, scales = "free") + theme(aspect.ratio = 1) if (is.null(color)) { ## b/w version densities <- do.call("rbind", lapply(1:ncol(dn), function(i) { data.frame(xlab = names(dn)[i], ylab = names(dn)[i], x = dn[, i], stringsAsFactors = TRUE) })) for (m in 1:ncol(dn)) { j <- subset(densities, xlab == names(dn)[m]) r <- r + stat_density( aes( x = x, y = ..scaled.. * diff(range(x)) + min(x) # nolint ), data = j, position = "identity", geom = "line", color = "black") } ## add b/w points r <- r + geom_point(alpha = alpha, na.rm = TRUE) return(r) } else { ## do the colored version densities <- do.call("rbind", lapply(1:ncol(dn), function(i) { data.frame(xlab = names(dn)[i], ylab = names(dn)[i], x = dn[, i], colorcolumn = data[, which(colnames(data) == color)], stringsAsFactors = TRUE) })) for (m in 1:ncol(dn)) { j <- subset(densities, xlab == names(dn)[m]) r <- r + # r is the facet grid plot stat_density( aes_string( x = "x", y = "..scaled.. * diff(range(x)) + min(x)", colour = "colorcolumn" ), data = j, position = "identity", geom = "line" ) } ## add color points r <- r + geom_point( data = ltdata.new, aes_string(colour = "colorcolumn"), alpha = alpha, na.rm = TRUE ) return(r) } } } #'Traditional scatterplot matrix for purely quantitative variables #' #' This function makes a scatterplot matrix for quantitative variables with density plots on the diagonal #' and correlation printed in the upper triangle. #' #' @export #' @param data a data matrix. Should contain numerical (continuous) data. #' @param columns an option to choose the column to be used in the raw dataset. Defaults to \code{1:ncol(data)}. #' @param color an option to group the dataset by the factor variable and color them by different colors. #' Defaults to \code{NULL}, i.e. no coloring. If supplied, it will be converted to a factor. #' @param alpha an option to set the transparency in scatterplots for large data. Defaults to \code{1}. #' @param corMethod method argument supplied to \code{\link[stats]{cor}} #' @author Mengjia Ni, Di Cook #' @examples #' # small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(flea) #' #' p_(ggscatmat(flea, columns = 2:4)) #' p_(ggscatmat(flea, columns = 2:4, color = "species")) ggscatmat <- function(data, columns = 1:ncol(data), color = NULL, alpha = 1, corMethod = "pearson"){ ## if 'color' is not a factor, mold it into one if (!is.null(color)) { if (is.null(data[[color]])) { stop(paste0("Non-existent column <", color, "> requested")) } data[[color]] <- as.factor(data[[color]]) } ## do we really need this next line? data <- upgrade_scatmat_data(data) data.choose <- data[columns] dn <- data.choose[sapply(data.choose, is.numeric)] if (ncol(dn) == 0) { stop("All of your variables are factors. Need numeric variables to make scatterplot matrix.") } if (ncol(dn) < 2){ stop ("Not enough numeric variables to make a scatter plot matrix") } a <- uppertriangle(data, columns = columns, color = color, corMethod = corMethod) if (is.null(color)){ plot <- scatmat(data, columns = columns, alpha = alpha) + geom_text(data = a, aes_string(label = "r"), colour = "black") } else { plot <- scatmat(data, columns = columns, color = color, alpha = alpha) + geom_text(data = a, aes_string(label = "r", color = "colorcolumn")) + labs(color = color) } is.factor.or.character <- function(x) {is.factor(x)|is.character(x)} factor <- data.choose[sapply(data.choose, is.factor.or.character)] if (ncol(factor) == 0){ return(plot) } else { warning("Factor variables are omitted in plot") return(plot) } } upgrade_scatmat_data <- function(data) { data <- as.data.frame(data) dataIsCharacter <- sapply(data, is.factor) if (any(dataIsCharacter)) { dataCharacterColumns <- names(dataIsCharacter[dataIsCharacter]) for (dataCol in dataCharacterColumns) { data[[dataCol]] <- as.factor(data[[dataCol]]) } } data } GGally/R/ggmatrix_make_plot.R0000644000176200001440000000271313663637143015651 0ustar liggesusers make_label_plot <- function(types, sectionAes, label) { sectionAes$y <- NULL p <- make_ggmatrix_plot_obj( wrapp( "diagAxis", params = c("label" = label), funcArgName = "ggally_diagAxis" ), mapping = sectionAes ) return(p) } ggmatrix_plot_list <- (function(){ make_diag_plot_wrapper <- function(sub_type_val) { plot_fn <- make_plot_wrapper(sub_type_val) function(types, sectionAes) { sectionAes$y <- NULL plot_fn(types, sectionAes) } } make_plot_wrapper <- function(sub_type_val) { function(types, sectionAes) { sub_type <- types[[sub_type_val]] sub_type_name <- get_subtype_name(sub_type) p <- make_ggmatrix_plot_obj( wrapp(sub_type, funcArgName = sub_type_name), mapping = sectionAes ) return(p) } } na_fn <- make_plot_wrapper("na") na_diag_fn <- make_plot_wrapper("na") continuous_fn <- make_plot_wrapper("continuous") combo_fn <- make_plot_wrapper("combo") discrete_fn <- make_plot_wrapper("discrete") continuous_diag_fn <- make_diag_plot_wrapper("continuous") discrete_diag_fn <- make_diag_plot_wrapper("discrete") function(type) { switch(type, "na" = na_fn, "na-diag" = na_diag_fn, "continuous" = continuous_fn, "combo" = combo_fn, "discrete" = discrete_fn, "continuous-diag" = continuous_diag_fn, "discrete-diag" = discrete_diag_fn, "label" = make_label_plot ) } })() GGally/R/ggbivariate.R0000644000176200001440000000705413761572054014261 0ustar liggesusers#' Display an outcome using several potential explanatory variables #' #' \code{ggbivariate} is a variant of \code{\link{ggduo}} for plotting #' an outcome variable with several potential explanatory variables. #' #' @param data dataset to be used, can have both categorical and #' numerical variables #' @param outcome name or position of the outcome variable (one variable only) #' @param explanatory names or positions of the explanatory variables (if \code{NULL}, #' will take all variables other than \code{outcome}) #' @param mapping additional aesthetic to be used, for example to indicate #' weights (see examples) #' @param types custom types of plots to use, see \code{\link{ggduo}} #' @param ... additional arguments passed to \code{\link{ggduo}} (see examples) #' @param rowbar_args additional arguments passed to \code{\link{ggally_rowbar}} #' (see examples) #' @author Joseph Larmarange #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggbivariate(tips, "smoker", c("day", "time", "sex", "tip"))) #' #' # Personalize plot title and legend title #' p_(ggbivariate( #' tips, "smoker", c("day", "time", "sex", "tip"), #' title = "Custom title" #' ) + #' labs(fill = "Smoker ?")) #' #' # Customize fill colour scale #' p_(ggbivariate(tips, "smoker", c("day", "time", "sex", "tip")) + #' scale_fill_brewer(type = "qual")) #' #' # Customize labels #' p_(ggbivariate( #' tips, "smoker", c("day", "time", "sex", "tip"), #' rowbar_args = list( #' colour = "white", #' size = 4, #' fontface = "bold", #' label_format = scales::label_percent(accurary = 1) #' ) #' )) #' #' # Choose the sub-plot from which get legend #' p_(ggbivariate(tips, "smoker")) #' p_(ggbivariate(tips, "smoker", legend = 3)) #' #' # Use mapping to indicate weights #' d <- as.data.frame(Titanic) #' p_(ggbivariate(d, "Survived", mapping = aes(weight = Freq))) #' #' # outcome can be numerical #' p_(ggbivariate(tips, outcome = "tip", title = "tip")) ggbivariate <- function( data, outcome, explanatory = NULL, mapping = NULL, types = NULL, ..., rowbar_args = NULL ) { if (length(outcome) != 1) stop("You should provide only one `outcome`.") if (is.numeric(outcome)) outcome <- names(data)[outcome] if (is.null(explanatory)) explanatory <- names(data)[!names(data) %in% c(outcome, mapping_string(mapping$weight))] if (!is.numeric(data[[outcome]])) { # mapping outcome to colour mapping$colour <- aes_string(colour = outcome)$colour } # default behavior if (is.null(rowbar_args$remove_percentage_axis)) rowbar_args$remove_percentage_axis <- TRUE if (is.null(rowbar_args$remove_background)) rowbar_args$remove_background <- TRUE if (is.null(types$discrete)) types$discrete = wrapp(ggally_rowbar, rowbar_args) if (is.null(types$comboVertical)) types$comboVertical <- "box_no_facet" if (is.null(types$continuous)) types$continuous <- "smooth_lm" if (is.null(types$comboHorizontal)) types$comboHorizontal <- "box_no_facet" ggduo_args <- list(...) ggduo_args$data <- data ggduo_args$mapping <- mapping ggduo_args$types <- types ggduo_args$columnsX <- outcome ggduo_args$columnsY <- explanatory if (!"yProportions" %in% names(ggduo_args)) ggduo_args$yProportions <- "auto" if (!is.numeric(data[[outcome]]) & !"legend" %in% names(list(...))) ggduo_args$legend <- 1 p <- do.call(ggduo, ggduo_args) + theme( legend.position = "top", strip.text.x = element_blank() ) p } GGally/R/ggtable.R0000644000176200001440000000471113761572054013377 0ustar liggesusers#' Cross-tabulated tables of discrete variables #' #' \code{ggtable} is a variant of \code{\link{ggduo}} for quick #' cross-tabulated tables of discrete variables. #' #' @param data dataset to be used, can have both categorical and #' numerical variables #' @param columnsX,columnsY names or positions of which columns are used to make plots. Defaults to all columns. #' @param cells Which statistic should be displayed in table cells? #' @param fill Which statistic should be used for filling table cells? #' @param mapping additional aesthetic to be used, for example to indicate #' weights (see examples) #' @param ... additional arguments passed to \code{\link{ggduo}} (see examples) #' @author Joseph Larmarange #' @export #' @examples #' # small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' if (require(reshape)) { #' data(tips, package = "reshape") #' p_(ggtable(tips, "smoker", c("day", "time", "sex"))) #' #' # displaying row proportions #' p_(ggtable(tips, "smoker", c("day", "time", "sex"), cells = "row.prop")) #' #' # filling cells with standardized residuals #' p_(ggtable(tips, "smoker", c("day", "time", "sex"), fill = "std.resid", legend = 1)) #' #' # if continuous variables are provided, just displaying some summary statistics #' p_(ggtable(tips, c("smoker", "total_bill"), c("day", "time", "sex", "tip"))) #' } #' #' # specifying weights #' d <- as.data.frame(Titanic) #' p_(ggtable( #' d, #' "Survived", #' c("Class", "Sex", "Age"), #' mapping = aes(weight = Freq), #' cells = "row.prop", #' fill = "std.resid" #' )) ggtable <- function( data, columnsX = 1:ncol(data), columnsY = 1:ncol(data), cells = c("observed", "prop", "row.prop", "col.prop", "expected", "resid", "std.resid"), fill = c("none", "std.resid", "resid"), mapping = NULL, ... ) { fill <- match.arg(fill) cells <- match.arg(cells) types <- list( discrete = wrapp(ggally_crosstable, list(cells = cells, fill = fill)), continuous = "cor", comboVertical = "summarise_by", comboHorizontal = "summarise_by" ) ggduo_args <- list(...) ggduo_args$data <- data ggduo_args$mapping <- mapping ggduo_args$types <- types ggduo_args$columnsX <- columnsX ggduo_args$columnsY <- columnsY if (!"xProportions" %in% names(ggduo_args)) ggduo_args$xProportions <- "auto" if (!"yProportions" %in% names(ggduo_args)) ggduo_args$yProportions <- "auto" p <- do.call(ggduo, ggduo_args) p } GGally/R/ggnetworkmap.R0000644000176200001440000003652614063460042014475 0ustar liggesusersif(getRversion() >= "2.15.1") { utils::globalVariables(c( "lon", "lat", "group", "id", "lon1", "lat1", "lon2", "lat2", ".label" )) } #' Network plot map overlay #' #' Plots a network with \pkg{ggplot2} suitable for overlay on a \pkg{ggmap} plot or \pkg{ggplot2} #' #' This is a descendant of the original \code{ggnet} function. \code{ggnet} added the innovation of plotting the network geographically. #' However, \code{ggnet} needed to be the first object in the ggplot chain. \code{ggnetworkmap} does not. If passed a \code{ggplot} object as its first argument, #' such as output from \code{ggmap}, \code{ggnetworkmap} will plot on top of that chart, looking for vertex attributes \code{lon} and \code{lat} as coordinates. #' Otherwise, \code{ggnetworkmap} will generate coordinates using the Fruchterman-Reingold algorithm. #' #' @export #' @param gg an object of class \code{ggplot}. #' @param net an object of class \code{\link[network]{network}}, or any object #' that can be coerced to this class, such as an adjacency or incidence matrix, #' or an edge list: see \link[network]{edgeset.constructors} and #' \link[network]{network} for details. If the object is of class #' [igraph][igraph::igraph-package] and the #' [intergraph][intergraph::intergraph-package] package is installed, #' it will be used to convert the object: see #' \code{\link[intergraph]{asNetwork}} for details. #' @param size size of the network nodes. Defaults to 3. If the nodes are weighted, their area is proportionally scaled up to the size set by \code{size}. #' @param alpha a level of transparency for nodes, vertices and arrows. Defaults to 0.75. #' @param weight if present, the unquoted name of a vertex attribute in \code{data}. Otherwise nodes are unweighted. #' @param node.group \code{NULL}, the default, or the unquoted name of a vertex attribute that will be used to determine the color of each node. #' @param ring.group if not \code{NULL}, the default, the unquoted name of a vertex attribute that will be used to determine the color of each node border. #' @param node.color If \code{node.group} is null, a character string specifying a color. #' @param node.alpha transparency of the nodes. Inherits from \code{alpha}. #' @param segment.alpha transparency of the vertex links. Inherits from \code{alpha} #' @param segment.color color of the vertex links. Defaults to \code{"grey"}. #' @param segment.size size of the vertex links, as a vector of values or as a single value. Defaults to 0.25. #' @param great.circles whether to draw edges as great circles using the \code{geosphere} package. Defaults to \code{FALSE} #' @param arrow.size size of the vertex arrows for directed network plotting, in centimeters. Defaults to 0. #' @param label.nodes label nodes with their vertex names attribute. If set to \code{TRUE}, all nodes are labelled. Also accepts a vector of character strings to match with vertex names. #' @param label.size size of the labels. Defaults to \code{size / 2}. #' @param ... other arguments supplied to geom_text for the node labels. Arguments pertaining to the title or other items can be achieved through \pkg{ggplot2} methods. #' @author Amos Elberg. Original by Moritz Marbach, Francois Briatte #' @details This is a function for plotting graphs generated by \code{network} or \code{igraph} in a more flexible and elegant manner than permitted by ggnet. The function does not need to be the first plot in the ggplot chain, so the graph can be plotted on top of a map or other chart. Segments can be straight lines, or plotted as great circles. Note that the great circles feature can produce odd results with arrows and with vertices beyond the plot edges; this is a \pkg{ggplot2} limitation and cannot yet be fixed. Nodes can have two color schemes, which are then plotted as the center and ring around the node. The color schemes are selected by adding scale_fill_ or scale_color_ just like any other \pkg{ggplot2} plot. If there are no rings, scale_color sets the color of the nodes. If there are rings, scale_color sets the color of the rings, and scale_fill sets the color of the centers. Note that additional arguments in the ... are passed to geom_text for plotting labels. #' @importFrom utils installed.packages #' @examples #' # small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' invisible(lapply(c("ggplot2", "maps", "network", "sna"), base::library, character.only = TRUE)) #' #' ## Example showing great circles on a simple map of the USA #' ## http://flowingdata.com/2011/05/11/how-to-map-connections-with-great-circles/ #' \donttest{ #' airports <- read.csv("http://datasets.flowingdata.com/tuts/maparcs/airports.csv", header = TRUE) #' rownames(airports) <- airports$iata #' #' # select some random flights #' set.seed(123) #' flights <- data.frame( #' origin = sample(airports[200:400, ]$iata, 200, replace = TRUE), #' destination = sample(airports[200:400, ]$iata, 200, replace = TRUE) #' ) #' #' # convert to network #' flights <- network(flights, directed = TRUE) #' #' # add geographic coordinates #' flights %v% "lat" <- airports[ network.vertex.names(flights), "lat" ] #' flights %v% "lon" <- airports[ network.vertex.names(flights), "long" ] #' #' # drop isolated airports #' delete.vertices(flights, which(degree(flights) < 2)) #' #' # compute degree centrality #' flights %v% "degree" <- degree(flights, gmode = "digraph") #' #' # add random groups #' flights %v% "mygroup" <- sample(letters[1:4], network.size(flights), replace = TRUE) #' #' # create a map of the USA #' usa <- ggplot(map_data("usa"), aes(x = long, y = lat)) + #' geom_polygon(aes(group = group), color = "grey65", #' fill = "#f9f9f9", size = 0.2) #' #' # overlay network data to map #' p <- ggnetworkmap( #' usa, flights, size = 4, great.circles = TRUE, #' node.group = mygroup, segment.color = "steelblue", #' ring.group = degree, weight = degree #' ) #' p_(p) #' #' ## Exploring a community of spambots found on Twitter #' ## Data by Amos Elberg: see ?twitter_spambots for details #' #' data(twitter_spambots) #' #' # create a world map #' world <- fortify(map("world", plot = FALSE, fill = TRUE)) #' world <- ggplot(world, aes(x = long, y = lat)) + #' geom_polygon(aes(group = group), color = "grey65", #' fill = "#f9f9f9", size = 0.2) #' #' # view global structure #' p <- ggnetworkmap(world, twitter_spambots) #' p_(p) #' #' # domestic distribution #' p <- ggnetworkmap(net = twitter_spambots) #' p_(p) #' #' # topology #' p <- ggnetworkmap(net = twitter_spambots, arrow.size = 0.5) #' p_(p) #' #' # compute indegree and outdegree centrality #' twitter_spambots %v% "indegree" <- degree(twitter_spambots, cmode = "indegree") #' twitter_spambots %v% "outdegree" <- degree(twitter_spambots, cmode = "outdegree") #' #' p <- ggnetworkmap( #' net = twitter_spambots, #' arrow.size = 0.5, #' node.group = indegree, #' ring.group = outdegree, size = 4 #' ) + #' scale_fill_continuous("Indegree", high = "red", low = "yellow") + #' labs(color = "Outdegree") #' p_(p) #' #' # show some vertex attributes associated with each account #' p <- ggnetworkmap( #' net = twitter_spambots, #' arrow.size = 0.5, #' node.group = followers, #' ring.group = friends, #' size = 4, #' weight = indegree, #' label.nodes = TRUE, vjust = -1.5 #' ) + #' scale_fill_continuous("Followers", high = "red", low = "yellow") + #' labs(color = "Friends") + #' scale_color_continuous(low = "lightgreen", high = "darkgreen") #' p_(p) #'} #' ggnetworkmap <- function ( gg, net, size = 3, alpha = 0.75, weight, node.group, node.color = NULL, node.alpha = NULL, ring.group, segment.alpha = NULL, segment.color = "grey", great.circles = FALSE, segment.size = 0.25, arrow.size = 0, label.nodes = FALSE, label.size = size/2, ...) { require_namespaces(c("network", "sna")) # sna # node placement if there is no ggplot object in function call # -- conversion to network class --------------------------------------------- if (class(net) == "igraph" && "intergraph" %in% rownames(installed.packages())) { net = intergraph::asNetwork(net) } else if (class(net) == "igraph") { stop("install the 'intergraph' package to use igraph objects with ggnet") } if (!network::is.network(net)) { net = try(network::network(net), silent = TRUE) } if (!network::is.network(net)) { stop("could not coerce net to a network object") } # -- network functions ------------------------------------------------------- get_v = utils::getFromNamespace("%v%", ns = "network") # -- network structure ------------------------------------------------------- vattr = network::list.vertex.attributes(net) is_dir = ifelse(network::is.directed(net), "digraph", "graph") if (!is.numeric(arrow.size) || arrow.size < 0) { stop("incorrect arrow.size value") } else if (arrow.size > 0 & is_dir == "graph") { warning("network is undirected; arrow.size ignored") arrow.size = 0 } if (network::is.hyper(net)) { stop("ggnetworkmap cannot plot hyper graphs") } if (network::is.multiplex(net)) { stop("ggnetworkmap cannot plot multiplex graphs") } if (network::has.loops(net)) { warning("ggnetworkmap does not know how to handle self-loops") } # -- ... ------------------------------------------------------- # get arguments labels = label.nodes # alpha default inherit <- function(x) ifelse(is.null(x), alpha, x) # get sociomatrix m <- network::as.matrix.network.adjacency(net) if (missing(gg)) { # mapproj doesn't need to be loaded, but # it needs to exist for ggplot2::coord_map() to work properly if (! ("mapproj" %in% installed.packages())) { require_namespaces("mapproj") } gg <- ggplot() + coord_map() plotcord <- sna::gplot.layout.fruchtermanreingold(net, list(m,layout.par = NULL)) plotcord <- data.frame(plotcord) colnames(plotcord) = c("lon", "lat") } else { plotcord = data.frame( lon = as.numeric(get_v(net, "lon")), lat = as.numeric(get_v(net, "lat")) ) } # Correct vertex labels if (! is.logical(labels)) { stopifnot(length(labels) == nrow(plotcord)) plotcord$.label <- labels } else if ("id" %in% vattr) { plotcord$.label <- as.character(get_v(net, "id")) } else if ("vertex.names" %in% vattr) { plotcord$.label <- network::network.vertex.names(net) } point_aes <- list( x = substitute(lon), y = substitute(lat) ) point_args <- list( alpha = substitute(inherit(node.alpha)) ) # get node groups if(!missing(node.group)) { plotcord$.ngroup <- get_v(net, as.character(substitute(node.group))) if (missing(ring.group)) { point_aes$color = substitute(.ngroup) } else { point_aes$fill = substitute(.ngroup) } } else if (! missing(node.color)) { point_args$color <- substitute(node.color) } else { point_args$color <- substitute( "black") } # rings if(!missing(ring.group)) { plotcord$.rgroup <- get_v(net, as.character(substitute(ring.group))) point_aes$color <- substitute(.rgroup) point_args$pch <- substitute(21) } # # # Plot edges # # # get edgelist edges <- network::as.matrix.network.edgelist(net) edges <- data.frame( lat1 = plotcord[ edges[, 1], "lat"], lon1 = plotcord[ edges[, 1], "lon"], lat2 = plotcord[ edges[, 2], "lat"], lon2 = plotcord[ edges[,2], "lon"]) edges <- subset(na.omit(edges), (! (lat1 == lat2 & lon2 == lon2))) edge_args <- list(size = substitute(segment.size), alpha = substitute(inherit(segment.alpha)), color = substitute(segment.color) ) edge_aes <- list() # -- edge arrows ------------------------------------------------------------- if (!missing(arrow.size) & arrow.size > 0) { edge_args$arrow <- substitute(arrow( type = "closed", length = unit(arrow.size, "cm") )) } # -- great circles ----------------------------------------------------------- if (great.circles) { # geosphere # great circles require_namespaces("geosphere") pts <- 25 # number of intermediate points for drawing great circles i <- 0 # used to keep track of groups when getting intermediate points for great circles edges <- ddply( .data = edges, .variables = c("lat1","lat2","lon1","lon2"), .parallel = FALSE, .fun = function(x) { p1Mat <- x[,c("lon1", "lat1")] colnames(p1Mat) <- NULL p2Mat <- x[,c("lon2", "lat2")] colnames(p2Mat) <- NULL inter <- geosphere::gcIntermediate( p1 = p1Mat, p2 = p2Mat, n = pts, addStartEnd = TRUE, breakAtDateLine = TRUE ) if (!is.list(inter)) { i <<- i + 1 inter <- data.frame(inter) inter$group <- i return(inter) } else { if (is.matrix(inter[[1]])) { i <<- i + 1 ret <- data.frame(inter[[1]]) ret$group <- i i <<- i + 1 ret2 <- data.frame(inter[[2]]) ret2$group <- i return(rbind(ret, ret2)) } else { ret <- data.frame(lon = numeric(0), lat = numeric(0), group = numeric(0)) for (j in 1: length(inter)) { i <<- i + 1 ret1 <- data.frame(inter[[j]][[1]]) ret1$group <- i i <<- i + 1 ret2 <- data.frame(inter[[j]][[2]]) ret2$group <- i ret <- rbind(ret, ret1, ret2) } return(ret) } } } ) edge_aes$x = substitute(lon) edge_aes$y = substitute(lat) edge_aes$group = substitute(group) edge_args$data = substitute(edges) edge_args$mapping <- do.call(aes, edge_aes) gg <- gg + do.call(geom_path, edge_args) } else { edge_aes$x = substitute(lon1) edge_aes$y = substitute(lat1) edge_aes$xend = substitute(lon2) edge_aes$yend = substitute(lat2) edge_args$data <- substitute(edges) edge_args$mapping = do.call(aes, edge_aes) gg <- gg + do.call(geom_segment, edge_args) } # # # Done drawing edges, time to draws nodes # # # custom weights: vertex attribute # null weighting sizer <- NULL if(missing(weight)) { point_args$size <- substitute(size) } else { # Setup weight-sizing plotcord$.weight = get_v(net, as.character(substitute(weight))) # proportional scaling if (is.factor(plotcord$.weight)) { sizer <- scale_size_discrete(name = substitute(weight), range = c(size/nlevels(plotcord$weight), size)) } else { sizer <- scale_size_area(name = substitute(weight), max_size = size) } point_aes$size <- substitute(.weight) } # Add points to plot point_args$data <- substitute(plotcord) point_args$mapping <- do.call(aes, point_aes) gg = gg + do.call(geom_point, point_args) if (!is.null(sizer)) { gg = gg + sizer } # -- node labels ------------------------------------------------------------- if (isTRUE(labels)) { gg <- gg + geom_text(data = plotcord, aes(x = lon, y = lat, label = .label), size = label.size, ...) } gg = gg + scale_x_continuous(breaks = NULL) + scale_y_continuous(breaks = NULL) + labs(color = "", fill = "", size = "", y = NULL, x = NULL) + theme(panel.background = element_blank(), legend.key = element_blank()) return(gg) } GGally/R/geom_stripped_rows.R0000644000176200001440000001306213764714663015713 0ustar liggesusers#' Alternating Background Colour #' #' Add alternating background color along the y-axis. The geom takes default #' aesthetics \code{odd} and \code{even} that receive color codes. #' #' @inheritParams ggplot2::layer #' @inheritParams ggplot2::geom_rect #' @param xfrom,xto limitation of the strips along the x-axis #' @param width width of the strips #' @param nudge_x,nudge_y horizontal or vertical adjustment to nudge strips by #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p <- ggplot(tips) + #' aes(x = time, y = day) + #' geom_count() + #' theme_light() #' #' p_(p) #' p_(p + geom_stripped_rows()) #' p_(p + geom_stripped_cols()) #' p_(p + geom_stripped_rows() + geom_stripped_cols()) #' #' p <- ggplot(tips) + #' aes(x = total_bill, y = day) + #' geom_count() + #' theme_light() #' p #' p_(p + geom_stripped_rows()) #' p_(p + geom_stripped_rows() + scale_y_discrete(expand = expansion(0, 0.5))) #' p_(p + geom_stripped_rows(xfrom = 10, xto = 35)) #' p_(p + geom_stripped_rows(odd = "blue", even = "yellow")) #' p_(p + geom_stripped_rows(odd = "blue", even = "yellow", alpha = .1)) #' p_(p + geom_stripped_rows(odd = "#00FF0022", even = "#FF000022")) #' #' p_(p + geom_stripped_cols()) #' p_(p + geom_stripped_cols(width = 10)) #' p_(p + geom_stripped_cols(width = 10, nudge_x = 5)) geom_stripped_rows <- function(mapping = NULL, data = NULL, stat = "identity", position = "identity", ..., show.legend = NA, inherit.aes = TRUE, xfrom = -Inf, xto = Inf, width = 1, nudge_y = 0) { ggplot2::layer( data = data, mapping = mapping, stat = stat, geom = GeomStrippedRows, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list(xfrom = xfrom, xto = xto, width = width, nudge_y = nudge_y, ...) ) } GeomStrippedRows <- ggplot2::ggproto("GeomStrippedRows", ggplot2::Geom, required_aes = c("y"), default_aes = ggplot2::aes( odd = "#11111111", even = "#00000000", alpha = NA, colour = NA, linetype = "solid", size = .5 ), # draw_key = ggplot2::draw_key_blank, draw_key = ggplot2::draw_key_rect, draw_panel = function(data, panel_params, coord, xfrom, xto, width = 1, nudge_y = 0) { ggplot2::GeomRect$draw_panel( data %>% dplyr::mutate( y = plyr::round_any(.data$y, width), ymin = .data$y - width / 2 + nudge_y, ymax = .data$y + width / 2 + nudge_y, xmin = xfrom, xmax = xto ) %>% dplyr::select( .data$xmin, .data$xmax, .data$ymin, .data$ymax, .data$odd, .data$even, .data$alpha, .data$colour, .data$linetype, .data$size ) %>% dplyr::distinct(.data$ymin, .keep_all = TRUE) %>% dplyr::arrange(.data$ymin) %>% dplyr::mutate( .n = dplyr::row_number(), fill = dplyr::if_else( .data$.n %% 2L == 1L, true = .data$odd, false = .data$even ) ) %>% dplyr::select(-.data$.n, -.data$odd, -.data$even), panel_params, coord ) } ) #' @rdname geom_stripped_rows #' @param yfrom,yto limitation of the strips along the y-axis #' @export geom_stripped_cols <- function(mapping = NULL, data = NULL, stat = "identity", position = "identity", ..., show.legend = NA, inherit.aes = TRUE, yfrom = -Inf, yto = Inf, width = 1, nudge_x = 0) { ggplot2::layer( data = data, mapping = mapping, stat = stat, geom = GeomStrippedCols, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list(yfrom = yfrom, yto = yto, width = width, nudge_x = nudge_x, ...) ) } GeomStrippedCols <- ggplot2::ggproto("GeomStrippedCols", ggplot2::Geom, required_aes = c("y"), default_aes = ggplot2::aes( odd = "#11111111", even = "#00000000", alpha = NA, colour = NA, linetype = "solid", size = .5 ), # draw_key = ggplot2::draw_key_blank, draw_key = ggplot2::draw_key_rect, draw_panel = function(data, panel_params, coord, yfrom, yto, width = 1, nudge_x = 0) { ggplot2::GeomRect$draw_panel( data %>% dplyr::mutate( x = plyr::round_any(.data$x, width), xmin = .data$x - width / 2 + nudge_x, xmax = .data$x + width / 2 + nudge_x, ymin = yfrom, ymax = yto ) %>% dplyr::select( .data$xmin, .data$xmax, .data$ymin, .data$ymax, .data$odd, .data$even, .data$alpha, .data$colour, .data$linetype, .data$size ) %>% dplyr::distinct(.data$xmin, .keep_all = TRUE) %>% dplyr::arrange(.data$xmin) %>% dplyr::mutate( .n = dplyr::row_number(), fill = dplyr::if_else( .data$.n %% 2L == 1L, true = .data$odd, false = .data$even ) ) %>% dplyr::select(-.data$.n, -.data$odd, -.data$even), panel_params, coord ) } ) GGally/R/ggcorr.R0000644000176200001440000003435314063456663013265 0ustar liggesusersif (getRversion() >= "2.15.1") { utils::globalVariables(c("x", "y", "coefficient", "breaks", "label")) } #' Correlation matrix #' #' Function for making a correlation matrix plot, using \pkg{ggplot2}. #' The function is directly inspired by Tian Zheng and Yu-Sung Su's #' \code{corrplot} function in the 'arm' package. #' Please visit \url{https://github.com/briatte/ggcorr} for the latest version #' of \code{ggcorr}, and see the vignette at #' \url{https://briatte.github.io/ggcorr/} for many examples of how to use it. #' #' @export #' @param data a data frame or matrix containing numeric (continuous) data. If #' any of the columns contain non-numeric data, they will be dropped with a #' warning. #' @param method a vector of two character strings. The first value gives the #' method for computing covariances in the presence of missing values, and must #' be (an abbreviation of) one of \code{"everything"}, \code{"all.obs"}, #' \code{"complete.obs"}, \code{"na.or.complete"} or #' \code{"pairwise.complete.obs"}. The second value gives the type of #' correlation coefficient to compute, and must be one of \code{"pearson"}, #' \code{"kendall"} or \code{"spearman"}. #' See \code{\link[stats]{cor}} for details. #' Defaults to \code{c("pairwise", "pearson")}. #' @param cor_matrix the named correlation matrix to use for calculations. #' Defaults to the correlation matrix of \code{data} when \code{data} is #' supplied. #' @param palette if \code{nbreaks} is used, a ColorBrewer palette to use #' instead of the colors specified by \code{low}, \code{mid} and \code{high}. #' Defaults to \code{NULL}. #' @param name a character string for the legend that shows the colors of the #' correlation coefficients. #' Defaults to \code{""} (no legend name). #' @param geom the geom object to use. Accepts either \code{"tile"}, #' \code{"circle"}, \code{"text"} or \code{"blank"}. #' @param min_size when \code{geom} has been set to \code{"circle"}, the minimum #' size of the circles. #' Defaults to \code{2}. #' @param max_size when \code{geom} has been set to \code{"circle"}, the maximum #' size of the circles. #' Defaults to \code{6}. #' @param label whether to add correlation coefficients to the plot. #' Defaults to \code{FALSE}. #' @param label_alpha whether to make the correlation coefficients increasingly #' transparent as they come close to 0. Also accepts any numeric value between #' \code{0} and \code{1}, in which case the level of transparency is set to that #' fixed value. #' Defaults to \code{FALSE} (no transparency). #' @param label_color the color of the correlation coefficients. #' Defaults to \code{"grey75"}. #' @param label_round the decimal rounding of the correlation coefficients. #' Defaults to \code{1}. #' @param label_size the size of the correlation coefficients. #' Defaults to \code{4}. #' @param nbreaks the number of breaks to apply to the correlation coefficients, #' which results in a categorical color scale. See 'Note'. #' Defaults to \code{NULL} (no breaks, continuous scaling). #' @param digits the number of digits to show in the breaks of the correlation #' coefficients: see \code{\link[base]{cut}} for details. #' Defaults to \code{2}. #' @param low the lower color of the gradient for continuous scaling of the #' correlation coefficients. #' Defaults to \code{"#3B9AB2"} (blue). #' @param mid the midpoint color of the gradient for continuous scaling of the #' correlation coefficients. #' Defaults to \code{"#EEEEEE"} (very light grey). #' @param high the upper color of the gradient for continuous scaling of the #' correlation coefficients. #' Defaults to \code{"#F21A00"} (red). #' @param midpoint the midpoint value for continuous scaling of the #' correlation coefficients. #' Defaults to \code{0}. #' @param limits bounding of color scaling for correlations, set \code{limits = NULL} or \code{FALSE} to remove #' @param drop if using \code{nbreaks}, whether to drop unused breaks from the #' color scale. #' Defaults to \code{FALSE} (recommended). #' @param layout.exp a multiplier to expand the horizontal axis to the left if #' variable names get clipped. #' Defaults to \code{0} (no expansion). #' @param legend.position where to put the legend of the correlation #' coefficients: see \code{\link[ggplot2]{theme}} for details. #' Defaults to \code{"bottom"}. #' @param legend.size the size of the legend title and labels, in points: see #' \code{\link[ggplot2]{theme}} for details. #' Defaults to \code{9}. #' @param ... other arguments supplied to \code{\link[ggplot2]{geom_text}} for #' the diagonal labels. #' @note Recommended values for the \code{nbreaks} argument are \code{3} to #' \code{11}, as values above 11 are visually difficult to separate and are not #' supported by diverging ColorBrewer palettes. #' #' @seealso \code{\link[stats]{cor}} and \code{corrplot} in the #' \code{arm} package. #' @author Francois Briatte, with contributions from Amos B. Elberg and #' Barret Schloerke #' @importFrom reshape melt melt.data.frame melt.default #' @importFrom stats cor #' @importFrom grDevices colorRampPalette #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' # Basketball statistics provided by Nathan Yau at Flowing Data. #' dt <- read.csv("http://datasets.flowingdata.com/ppg2008.csv") #' #' # Default output. #' p_(ggcorr(dt[, -1])) #' #' # Labeled output, with coefficient transparency. #' p_(ggcorr(dt[, -1], #' label = TRUE, #' label_alpha = TRUE)) #' #' # Custom options. #' p_(ggcorr( #' dt[, -1], #' name = expression(rho), #' geom = "circle", #' max_size = 10, #' min_size = 2, #' size = 3, #' hjust = 0.75, #' nbreaks = 6, #' angle = -45, #' palette = "PuOr" # colorblind safe, photocopy-able #' )) #' #' # Supply your own correlation matrix #' p_(ggcorr( #' data = NULL, #' cor_matrix = cor(dt[, -1], use = "pairwise") #' )) ggcorr <- function( data, method = c("pairwise", "pearson"), cor_matrix = NULL, nbreaks = NULL, digits = 2, name = "", low = "#3B9AB2", mid = "#EEEEEE", high = "#F21A00", midpoint = 0, palette = NULL, geom = "tile", min_size = 2, max_size = 6, label = FALSE, label_alpha = FALSE, label_color = "black", label_round = 1, label_size = 4, limits = c(-1, 1), drop = is.null(limits) || identical(limits, FALSE), layout.exp = 0, legend.position = "right", legend.size = 9, ...) { if (is.numeric(limits)) { if (length(limits) != 2) { stop("'limits' must be of length 2 if numeric") } } if (is.logical(limits)) { if (limits) { limits <- c(-1, 1) } else { limits <- NULL } } # -- check geom argument ----------------------------------------------------- if (length(geom) > 1 || !geom %in% c("blank", "circle", "text", "tile")) { stop("incorrect geom value") } # -- correlation method ------------------------------------------------------ if (length(method) == 1) { method = c(method, "pearson") # for backwards compatibility } # -- check data columns ------------------------------------------------------ if (!is.null(data)) { if (!is.data.frame(data)) { data = as.data.frame(data) } x = which(!sapply(data, is.numeric)) if (length(x) > 0) { warning(paste("data in column(s)", paste0(paste0("'", names(data)[x], "'"), collapse = ", "), "are not numeric and were ignored")) data = data[, -x ] } } # -- correlation matrix ------------------------------------------------------ if (is.null(cor_matrix)) { cor_matrix = cor(data, use = method[1], method = method[2]) } m = cor_matrix colnames(m) = rownames(m) = gsub(" ", "_", colnames(m)) # protect spaces # -- correlation data.frame -------------------------------------------------- m = data.frame(m * lower.tri(m)) rownames(m) = names(m) m$.ggally_ggcorr_row_names = rownames(m) m = reshape::melt(m, id.vars = ".ggally_ggcorr_row_names") names(m) = c("x", "y", "coefficient") m$coefficient[ m$coefficient == 0 ] = NA # -- correlation quantiles --------------------------------------------------- if (!is.null(nbreaks)) { x = seq(-1, 1, length.out = nbreaks + 1) if (!nbreaks %% 2) { x = sort(c(x, 0)) } m$breaks = cut(m$coefficient, breaks = unique(x), include.lowest = TRUE, dig.lab = digits) } # -- gradient midpoint ------------------------------------------------------- if (is.null(midpoint)) { midpoint = median(m$coefficient, na.rm = TRUE) message(paste("Color gradient midpoint set at median correlation to", round(midpoint, 2))) } # -- plot structure ---------------------------------------------------------- m$label = round(m$coefficient, label_round) p = ggplot(na.omit(m), aes(x, y)) if (geom == "tile") { if (is.null(nbreaks)) { # -- tiles, continuous --------------------------------------------------- p = p + geom_tile(aes(fill = coefficient), color = "white") } else { # -- tiles, ordinal ------------------------------------------------------ p = p + geom_tile(aes(fill = breaks), color = "white") } # -- tiles, color scale ---------------------------------------------------- if (is.null(nbreaks) && !is.null(limits)) { p = p + scale_fill_gradient2(name, low = low, mid = mid, high = high, midpoint = midpoint, limits = limits) } else if (is.null(nbreaks)) { p = p + scale_fill_gradient2(name, low = low, mid = mid, high = high, midpoint = midpoint) } else if (is.null(palette)) { x = colorRampPalette(c(low, mid, high))(length(levels(m$breaks))) p = p + scale_fill_manual(name, values = x, drop = drop) } else { p = p + scale_fill_brewer(name, palette = palette, drop = drop) } } else if (geom == "circle") { p = p + geom_point(aes(size = abs(coefficient) * 1.25), color = "grey50") # border if (is.null(nbreaks)) { # -- circles, continuous ------------------------------------------------- p = p + geom_point(aes(size = abs(coefficient), color = coefficient)) } else { # -- circles, ordinal ---------------------------------------------------- p = p + geom_point(aes(size = abs(coefficient), color = breaks)) } p = p + scale_size_continuous(range = c(min_size, max_size)) + guides(size = "none") r = list(size = (min_size + max_size) / 2) # -- circles, color scale -------------------------------------------------- if (is.null(nbreaks) && !is.null(limits)) { p = p + scale_color_gradient2(name, low = low, mid = mid, high = high, midpoint = midpoint, limits = limits) } else if (is.null(nbreaks)) { p = p + scale_color_gradient2(name, low = low, mid = mid, high = high, midpoint = midpoint) } else if (is.null(palette)) { x = colorRampPalette(c(low, mid, high))(length(levels(m$breaks))) p = p + scale_color_manual(name, values = x, drop = drop) + guides(color = guide_legend(override.aes = r)) } else { p = p + scale_color_brewer(name, palette = palette, drop = drop) + guides(color = guide_legend(override.aes = r)) } } else if (geom == "text") { if (is.null(nbreaks)) { # -- text, continuous ---------------------------------------------------- p = p + geom_text(aes(label = label, color = coefficient), size = label_size) } else { # -- text, ordinal ------------------------------------------------------- p = p + geom_text(aes(label = label, color = breaks), size = label_size) } # -- text, color scale ---------------------------------------------------- if (is.null(nbreaks) && !is.null(limits)) { p = p + scale_color_gradient2(name, low = low, mid = mid, high = high, midpoint = midpoint, limits = limits) } else if (is.null(nbreaks)) { p = p + scale_color_gradient2(name, low = low, mid = mid, high = high, midpoint = midpoint) } else if (is.null(palette)) { x = colorRampPalette(c(low, mid, high))(length(levels(m$breaks))) p = p + scale_color_manual(name, values = x, drop = drop) } else { p = p + scale_color_brewer(name, palette = palette, drop = drop) } } # -- coefficient labels ------------------------------------------------------ if (label) { if (isTRUE(label_alpha)) { p = p + geom_text(aes(x, y, label = label, alpha = abs(coefficient)), color = label_color, size = label_size, show.legend = FALSE) } else if (label_alpha > 0) { p = p + geom_text( aes(x, y, label = label), show.legend = FALSE, alpha = label_alpha, color = label_color, size = label_size ) } else { p = p + geom_text(aes(x, y, label = label), color = label_color, size = label_size) } } # -- horizontal scale expansion ---------------------------------------------- textData <- m[ m$x == m$y & is.na(m$coefficient), ] xLimits <- levels(textData$y) textData$diagLabel <- textData$x if (!is.numeric(layout.exp) || layout.exp < 0) { stop("incorrect layout.exp value") } else if (layout.exp > 0) { layout.exp <- as.integer(layout.exp) # copy to fill in spacer info textData <- rbind(textData[1:layout.exp, ], textData) spacer <- paste(".ggally_ggcorr_spacer_value", 1:layout.exp, sep = "") textData$x[1:layout.exp] <- spacer textData$diagLabel[1:layout.exp] <- NA xLimits <- c(spacer, levels(m$y)) } p = p + geom_text(data = textData, aes_string(label = "diagLabel"), ..., na.rm = TRUE) + scale_x_discrete(breaks = NULL, limits = xLimits) + scale_y_discrete(breaks = NULL, limits = levels(m$y)) + labs(x = NULL, y = NULL) + coord_equal() + theme( panel.background = element_blank(), legend.key = element_blank(), legend.position = legend.position, legend.title = element_text(size = legend.size), legend.text = element_text(size = legend.size) ) return(p) } GGally/R/find-combo.R0000644000176200001440000000611413665760216014010 0ustar liggesusers#' Plot Types #' #' Retrieves the type of plot that should be used for all combinations #' #' @param data data set to be used #' @author Barret Schloerke #' @keywords internal plot_types <- function(data, columnsX, columnsY, allowDiag = TRUE) { plotTypesX <- lapply(data[columnsX], plotting_data_type) plotTypesY <- lapply(data[columnsY], plotting_data_type) columnNamesX <- names(data)[columnsX] columnNamesY <- names(data)[columnsY] isNaData <- as.data.frame(is.na(data)) lenX <- length(plotTypesX) lenY <- length(plotTypesY) n <- lenX * lenY plotType <- character(n) xVar <- character(n) yVar <- character(n) posX <- integer(n) posY <- integer(n) #horizontal then vertical for (yI in seq_len(lenY)) { yColName <- columnNamesY[yI] for (xI in seq_len(lenX)) { xColName <- columnNamesX[xI] yVarVal <- ifelse(xColName == yColName && allowDiag, NA, yColName) pos <- (yI - 1) * lenX + xI plotType[pos] <- find_plot_type( xColName, yColName, plotTypesX[xI], plotTypesY[yI], isAllNa = all(isNaData[[xColName]] | isNaData[[yColName]]), allowDiag = allowDiag ) xVar[pos] <- xColName yVar[pos] <- yVarVal posX[pos] <- xI posY[pos] <- yI } } dataInfo <- data.frame( plotType = plotType, xVar = xVar, yVar = yVar, posX = posX, posY = posY, isVertical = NA, stringsAsFactors = FALSE ) isCombo <- dataInfo$plotType == "combo" if (any(isCombo)) { dataInfo$isVertical[isCombo] <- unlist(plotTypesX[xVar[isCombo]]) == "discrete" } dataInfo } #' Find plot types #' #' Retrieves the type of plot for the specific columns #' #' @param col1Name x column name #' @param col2Name y column name #' @param type1 x column type #' @param type2 y column type #' @param isAllNa is.na(data) #' @param allowDiag allow for diag values to be returned #' @author Barret Schloerke #' @keywords internal find_plot_type <- function(col1Name, col2Name, type1, type2, isAllNa, allowDiag) { # diag calculations if (col1Name == col2Name && allowDiag) { if (type1 == "na") { return("na-diag") } else if (type1 == "continuous") { return("continuous-diag") } else { return("discrete-diag") } } if (type1 == "na" | type2 == "na") { return("na") } #cat(names(data)[col2Name],": ", type2,"\t",names(data)[col1Name],": ",type1,"\n") isCats <- c(type1, type2) %in% "discrete" if (any(isCats)) { if (all(isCats)) { return("discrete") } return("combo") } # check if any combo of the two columns is all na if (isAllNa) { return("na") } return("continuous") } #' Check if object is a date #' #' @keywords internal #' @param x vector is_date <- function(x) { inherits(x, c("POSIXt", "POSIXct", "POSIXlt", "Date")) } #' Get plotting data type #' #' @keywords internal #' @param x vector plotting_data_type <- function(x) { if (all(is.na(x))) { return("na") } if (is_date(x)) { "continuous" } else if (any(is.factor(x), is.character(x), is.logical(x))) { "discrete" } else { "continuous" } } GGally/R/ggmatrix_gtable.R0000644000176200001440000002114513665760216015134 0ustar liggesusers #' \code{\link{ggmatrix}} \pkg{gtable} object #' #' Specialized method to print the \code{\link{ggmatrix}} object. #' #' @param pm \code{\link{ggmatrix}} object to be plotted #' @param ... ignored #' @param progress,progress_format Please use the 'progress' parameter in your \code{\link{ggmatrix}}-like function. See \code{\link{ggmatrix_progress}} for a few examples. These parameters will soon be deprecated. #' @author Barret Schloerke #' @importFrom grid gpar grid.layout grid.newpage grid.text grid.rect popViewport pushViewport viewport grid.draw #' @export #' @examples #' data(tips, package = "reshape") #' pm <- ggpairs(tips, c(1,3,2), mapping = ggplot2::aes_string(color = "sex")) #' ggmatrix_gtable(pm) ggmatrix_gtable <- function( pm, ..., progress = NULL, progress_format = formals(ggmatrix_progress)$format ) { # pm is for "plot matrix" # init progress bar handle if (missing(progress) && missing(progress_format)) { # only look at plot matrix for progress bar hasProgressBar <- !isFALSE(pm$progress) progress_fn <- pm$progress } else { warning("Please use the 'progress' parameter in your ggmatrix-like function call. See ?ggmatrix_progress for a few examples. ggmatrix_gtable 'progress' and 'progress_format' will soon be deprecated.", immediate = TRUE) # has progress variable defined # overrides pm$progress if (missing(progress_format)) { progress_fn <- as_ggmatrix_progress(progress) } else { progress_fn <- as_ggmatrix_progress( progress, pm$ncol * pm$nrow, format = progress_format ) } hasProgressBar <- !isFALSE(progress_fn) ggmatrix_progress } if (hasProgressBar) { pb <- progress_fn(pm) # pb$tick(tokens = list(plot_i = 1, plot_j = 1)) } # make a fake facet grid to fill in with proper plot panels get_labels <- function(labels, length_out, name) { if (is.expression(labels)) { stop("'", name, "' can only be a character vector or NULL.", " Character values can be parsed using the 'labeller' parameter.") } ifnull(labels, as.character(seq_len(length_out))) } fake_data <- expand.grid( Var1 = get_labels(pm$xAxisLabels, pm$ncol, "xAxisLabels"), Var2 = get_labels(pm$yAxisLabels, pm$nrow, "yAxisLabels") ) fake_data$x <- 1 fake_data$y <- 1 # make the smallest plot possible so the guts may be replaced pm_fake <- ggplot(fake_data, mapping = aes_("x", "y")) + geom_point() + # make the 'fake' strips for x and y titles facet_grid(Var2 ~ Var1, labeller = ifnull(pm$labeller, "label_value"), switch = pm$switch) + # remove both x and y titles labs(x = pm$xlab, y = pm$ylab) # add all custom ggplot2 things pm_fake <- add_gg_info(pm_fake, pm$gg) # add the title or remove the location completely if (is.null(pm$title)) { pm_fake <- pm_fake + theme(plot.title = element_blank()) } else { pm_fake <- pm_fake + labs(title = pm$title) } # if there are no labels, then there should be no strips if (is.null(pm$xAxisLabels)) { pm_fake <- pm_fake + theme(strip.text.x = element_blank()) } if (is.null(pm$yAxisLabels)) { pm_fake <- pm_fake + theme(strip.text.y = element_blank()) } # if there is a legend, make a fake legend that will be replaced later if (!is.null(pm$legend)) { pm_fake <- pm_fake + geom_point(mapping = aes_(color = "Var1")) } # make a gtable of the plot matrix (to be filled in) pmg <- plot_gtable(pm_fake) ############### ## Everything beyond this point is only to fill in the correct information. ## No grobs should be appended or removed. It should be done with themes or geoms above ############### # help with grob positions pmg$layout$grob_pos <- seq_along(pmg$grobs) pmg_layout <- pmg$layout pmg_layout_name <- pmg_layout$name pmg_layout_grob_pos <- pmg_layout$grob_pos # zero out rest of the plotting area (just in case it is not replaced) zero_pos_vals <- pmg_layout_grob_pos[ str_detect( pmg_layout_name, paste(c("panel", "axis-l", "axis-b", "guide-box"), collapse = "|") ) ] for (zero_pos in zero_pos_vals) { pmg$grobs[[zero_pos]] <- ggplot2::zeroGrob() } pmg # insert legend if (!is.null(pm$legend)) { legend <- pm$legend if (is.numeric(legend)) { if (length(legend) == 1) { legend <- get_pos_rev(pm, legend) } else if (length(legend) > 2) { stop("'legend' must be a single or double numberic value. Or 'legend' must be an object produced from 'grab_legend()'") # nolint } legend_obj <- grab_legend(pm[legend[1], legend[2]]) } else if (inherits(legend, "legend_guide_box")) { legend_obj <- legend } legend_layout <- (pmg_layout[pmg_layout_name == "guide-box", ])[1, ] class(legend_obj) <- setdiff(class(legend_obj), "legend_guide_box") pmg$grobs[[legend_layout$grob_pos]] <- legend_obj legend_position <- ifnull(pm_fake$theme$legend.position, "right") if (legend_position %in% c("right", "left")) { pmg$widths[[legend_layout$l]] <- legend_obj$widths[1] } else if (legend_position %in% c("top", "bottom")) { pmg$heights[[legend_layout$t]] <- legend_obj$heights[1] } else { stop(paste("ggmatrix does not know how display a legend when legend.position with value: '", legend_position, "'. Valid values: c('right', 'left', 'bottom', 'top')", sep = "")) # nolint } } # Get all 'panel' grob_pos in the pmg panel_layout <- pmg_layout[str_detect(pmg_layout_name, "panel"), ] panel_locations_order <- order(panel_layout$t, panel_layout$l, decreasing = FALSE) panel_locations <- panel_layout[panel_locations_order, "grob_pos"] # init the axis sizes left_axis_sizes <- numeric(pm$nrow + 1) bottom_axis_sizes <- numeric(pm$ncol + 1) axis_l_grob_pos <- pmg_layout_grob_pos[str_detect(pmg_layout_name, "axis-l")] axis_b_grob_pos <- pmg_layout_grob_pos[str_detect(pmg_layout_name, "axis-b")] # change the plot size ratios x_proportions <- pm$xProportions if (!is.null(x_proportions)) { panel_width_pos <- sort(unique(panel_layout$l)) if (!inherits(x_proportions, "unit")) { x_proportions <- grid::unit(x_proportions, "null") } pmg$widths[panel_width_pos] <- x_proportions } y_proportions <- pm$yProportions if (!is.null(y_proportions)) { panel_height_pos <- sort(unique(panel_layout$t)) if (!inherits(y_proportions, "unit")) { y_proportions <- grid::unit(y_proportions, "null") } pmg$heights[panel_height_pos] <- y_proportions } # build and insert all plots and axis labels plot_number <- 0 for (i in seq_len(pm$nrow)) { for (j in seq_len(pm$ncol)) { plot_number <- plot_number + 1 grob_pos_panel <- panel_locations[plot_number] # update the progress bar is possible if (hasProgressBar) { pb$tick(tokens = list(plot_i = i, plot_j = j)) } # retrieve plot p <- pm[i, j] # ignore all blank plots. all blank plots do not draw anything else if (is_blank_plot(p)) { next } # if it's not a ggplot2 obj, insert it and pray it works if (!is.ggplot(p)) { pmg$grobs[[grob_pos_panel]] <- p next } # get the plot's gtable to slice and dice pg <- plot_gtable(p) # if the left axis should be added if (j == 1 && pm$showYAxisPlotLabels) { left_axis_sizes[i] <- axis_size_left(pg) pmg <- add_left_axis( pmg, pg, show_strips = ( (i == 1) && is.null(pm$showStrips) ) || isTRUE(pm$showStrips), grob_pos = axis_l_grob_pos[i] ) } # if the bottom axis should be added if (i == pm$nrow && pm$showXAxisPlotLabels) { bottom_axis_sizes[j] <- axis_size_bottom(pg) pmg <- add_bottom_axis( pmg, pg, show_strips = ( (j == pm$ncol) && is.null(pm$showStrips) ) || isTRUE(pm$showStrips), grob_pos = axis_b_grob_pos[j] ) } # grab plot panel and insert pmg$grobs[[grob_pos_panel]] <- plot_panel( pg = pg, row_pos = i, col_pos = j, matrix_show_strips = pm$showStrips, matrix_ncol = pm$ncol, plot_show_axis_labels = p$showLabels ) } } # make sure the axes have enough room pmg <- set_max_axis_size( pmg, axis_sizes = left_axis_sizes, layout_name = "axis-l", layout_cols = c("l", "r"), pmg_key = "widths" #stop_msg = "left axis width issue!! Fix!" ) pmg <- set_max_axis_size( pmg, axis_sizes = bottom_axis_sizes, layout_name = "axis-b", layout_cols = c("t", "b"), pmg_key = "heights" #stop_msg = "bottom axis height issue!! Fix!" ) pmg } GGally/R/ggpairs_getput.R0000644000176200001440000001055613666472400015020 0ustar liggesusers#' Insert a plot into a \code{\link{ggmatrix}} object #' #' Function to place your own plot in the layout. #' #' @param pm ggally object to be altered #' @param value ggplot object to be placed #' @param i row from the top #' @param j column from the left #' @keywords hplot #' @author Barret Schloerke #' @seealso \code{\link{getPlot}} #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' custom_car <- ggpairs(mtcars[, c("mpg", "wt", "cyl")], upper = "blank", title = "Custom Example") #' # ggplot example taken from example(geom_text) #' plot <- ggplot2::ggplot(mtcars, ggplot2::aes(x=wt, y=mpg, label=rownames(mtcars))) #' plot <- plot + #' ggplot2::geom_text(ggplot2::aes(colour=factor(cyl)), size = 3) + #' ggplot2::scale_colour_discrete(l=40) #' custom_car[1, 2] <- plot #' personal_plot <- ggally_text( #' "ggpairs allows you\nto put in your\nown plot.\nLike that one.\n <---" #' ) #' custom_car[1, 3] <- personal_plot #' # custom_car #' #' # remove plots after creating a plot matrix #' custom_car[2,1] <- NULL #' custom_car[3,1] <- "blank" # the same as storing null #' custom_car[3,2] <- NULL #' p_(custom_car) putPlot <- function(pm, value, i, j){ pos <- get_pos(pm, i, j) if (is.null(value)) { pm$plots[[pos]] <- make_ggmatrix_plot_obj(wrap("blank", funcArgName = "ggally_blank")) } else if (mode(value) == "character") { if (value == "blank") { pm$plots[[pos]] <- make_ggmatrix_plot_obj(wrap("blank", funcArgName = "ggally_blank")) } else { stop("character values (besides 'blank') are not allowed to be stored as plot values.") } } else { pm$plots[[pos]] <- value } pm } #' Subset a \code{\link{ggmatrix}} object #' #' Retrieves the ggplot object at the desired location. #' #' @param pm \code{\link{ggmatrix}} object to select from #' @param i row from the top #' @param j column from the left #' @keywords hplot #' @author Barret Schloerke #' @importFrom utils capture.output #' @seealso \code{\link{putPlot}} #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' plotMatrix2 <- ggpairs(tips[, 3:2], upper = list(combo = "denstrip")) #' p_(plotMatrix2[1, 2]) getPlot <- function(pm, i, j){ if (FALSE) { cat("i: ", i, " j: ", j, "\n") } pos <- get_pos(pm, i, j) if (pos > length(pm$plots)) { plotObj <- NULL } else { plotObj <- pm$plots[[pos]] } if (is.null(plotObj)) { p <- ggally_blank() } else { if (ggplot2::is.ggplot(plotObj)) { p <- plotObj } else if (inherits(plotObj, "ggmatrix_plot_obj")) { fn <- plotObj$fn p <- fn(pm$data, plotObj$mapping) } else if (inherits(plotObj, "legend_guide_box")) { p <- plotObj } else { firstNote <- str_c("Position: i = ", i, ", j = ", j, "\nstr(plotObj):\n", sep = "") strObj <- capture.output({ str(plotObj) }) stop(str_c("unknown plot object type.\n", firstNote, strObj)) } p <- add_gg_info(p, pm$gg) } p } get_pos <- function(pm, i, j) { if (isTRUE(pm$byrow)) { pos <- j + (pm$ncol * (i - 1)) } else { pos <- i + (pm$nrow * (j - 1)) } pos } get_pos_rev <- function(pm, pos) { if (isTRUE(pm$byrow)) { i <- ceiling(pos / pm$ncol) j <- (pos - 1) %% pm$ncol + 1 } else { i <- (pos - 1) %% pm$nrow + 1 j <- ceiling(pos / pm$nrow) } c(i, j) } check_i_j <- function(pm, i, j) { if ( (length(i) > 1) || (mode(i) != "numeric")) { stop("'i' may only be a single numeric value") } if ( (length(j) > 1) || (mode(j) != "numeric")) { stop("'j' may only be a single numeric value") } if (i > pm$nrow || i < 1) { stop("'i' may only be in the range from 1:", pm$nrow) } if (j > pm$ncol || j < 1) { stop("'j' may only be in the range from 1:", pm$ncol) } invisible() } #' @rdname getPlot #' @usage \method{[}{ggmatrix}(pm, i, j, ...) #' @param ... ignored #' @export `[.ggmatrix` <- function(pm, i, j, ...) { # print(list(x = i, y = j)) check_i_j(pm, i, j) getPlot(pm, i, j) } #' @rdname putPlot #' @usage \method{[}{ggmatrix}(pm, i, j, ...) <- value #' @param ... ignored #' @export `[<-.ggmatrix` <- function(pm, i, j, ..., value) { # x = matrix # i = first subset # j = second subset # y = value check_i_j(pm, i, j) putPlot(pm, value, i, j) } GGally/R/ggmatrix_progress.R0000644000176200001440000000301413665760216015535 0ustar liggesusers#' \code{\link{ggmatrix}} default progress bar #' #' @param format,clear,show_after,... parameters supplied directly to \code{progress::\link[progress]{progress_bar}$new()} #' @return function that accepts a plot matrix as the first argument and \code{...} for future expansion. Internally, the plot matrix is used to determine the total number of plots for the progress bar. #' @export #' @examples #' p_ <- GGally::print_if_interactive #' #' pm <- ggpairs(iris, 1:2, progress = ggmatrix_progress()) #' p_(pm) #' #' # does not clear after finishing #' pm <- ggpairs(iris, 1:2, progress = ggmatrix_progress(clear = FALSE)) #' p_(pm) ggmatrix_progress <- function( format = " plot: [:plot_i,:plot_j] [:bar]:percent est::eta ", clear = TRUE, show_after = 0, ... ) { ret <- function(pm, ...) { progress::progress_bar$new( format = format, clear = clear, show_after = show_after, total = pm$ncol * pm$nrow, ... ) } ret } as_ggmatrix_progress <- function(x, total, ...) { if (isFALSE(x)) { return(FALSE) } if (isTRUE(x)) { return(ggmatrix_progress(...)) } if (is.null(x)) { shouldDisplay <- interactive() && total > 15 if (!shouldDisplay) { return(FALSE) } else { return(ggmatrix_progress(...)) } } if (is.function(x)) { return(x) } stop( "as_ggmatrix_progress only knows how to handle TRUE, FALSE, NULL, or a function.", " If a function, it must return a new progress_bar" ) } isFALSE <- function(x) { identical(FALSE, x) } GGally/R/ggnet.R0000644000176200001440000005724013777103031013073 0ustar liggesusersif (getRversion() >= "2.15.1") { utils::globalVariables(c("X1", "X2", "Y1", "Y2", "midX", "midY")) } #' Network plot #' #' Function for plotting network objects using \pkg{ggplot2}, now replaced by the #' \code{\link{ggnet2}} function, which provides additional control over #' plotting parameters. Please visit \url{https://github.com/briatte/ggnet} for #' the latest version of ggnet2, and \url{https://briatte.github.io/ggnet/} for a #' vignette that contains many examples and explanations. #' #' @export #' @param net an object of class \code{\link[network]{network}}, or any object #' that can be coerced to this class, such as an adjacency or incidence matrix, #' or an edge list: see \link[network]{edgeset.constructors} and #' \link[network]{network} for details. If the object is of class #' [igraph][igraph::igraph-package] and the #' [intergraph][intergraph::intergraph-package] package is installed, #' it will be used to convert the object: see #' \code{\link[intergraph]{asNetwork}} for details. #' @param mode a placement method from those provided in the #' \code{\link[sna]{sna}} package: see \link[sna:gplot.layout]{gplot.layout} for #' details. Also accepts the names of two numeric vertex attributes of #' \code{net}, or a matrix of numeric coordinates, in which case the first two #' columns of the matrix are used. #' Defaults to the Fruchterman-Reingold force-directed algorithm. #' @param layout.par options to be passed to the placement method, as listed in #' \link[sna]{gplot.layout}. #' Defaults to \code{NULL}. #' @param layout.exp a multiplier to expand the horizontal axis if node labels #' get clipped: see \link[scales]{expand_range} for details. #' Defaults to \code{0} (no expansion). #' @param size size of the network nodes. If the nodes are weighted, their area is proportionally scaled up to the size set by \code{size}. #' Defaults to \code{9}. #' @param alpha a level of transparency for nodes, vertices and arrows. #' Defaults to \code{1}. #' @param weight the weighting method for the nodes, which might be a vertex #' attribute or a vector of size values. Also accepts \code{"indegree"}, #' \code{"outdegree"}, \code{"degree"} or \code{"freeman"} to size the nodes by #' their unweighted degree centrality (\code{"degree"} and \code{"freeman"} are #' equivalent): see \code{\link[sna]{degree}} for details. All node weights must #' be positive. #' Defaults to \code{"none"} (no weighting). #' @param weight.method see \code{weight} #' @param weight.legend the name to assign to the legend created by #' \code{weight}. #' Defaults to \code{NA} (no name). #' @param weight.min whether to subset the network to nodes with a minimum size, #' based on the values of \code{weight}. #' Defaults to \code{NA} (preserves all nodes). #' @param weight.max whether to subset the network to nodes with a maximum size, #' based on the values of \code{weight}. #' Defaults to \code{NA} (preserves all nodes). #' @param weight.cut whether to cut the size of the nodes into a certain number #' of quantiles. Accepts \code{TRUE}, which tries to cut the sizes into #' quartiles, or any positive numeric value, which tries to cut the sizes into #' that many quantiles. If the size of the nodes do not contain the specified #' number of distinct quantiles, the largest possible number is used. #' See \code{\link[stats]{quantile}} and \code{\link[base]{cut}} for details. #' Defaults to \code{FALSE} (does nothing). #' @param group the groups of the nodes, either as a vector of values or as a #' vertex attribute. If set to \code{mode} on a bipartite network, the nodes #' will be grouped as \code{"actor"} if they belong to the primary mode and #' \code{"event"} if they belong to the secondary mode. #' @param group.legend the name to assign to the legend created by #' \code{group}. #' @param node.group see \code{group} #' @param node.color a vector of character strings to color the nodes with, #' holding as many colors as there are levels in \code{node.group}. #' Defaults to \code{NULL}, which will assign grayscale colors to each group. #' @param node.alpha transparency of the nodes. Inherits from \code{alpha}. #' @param segment.alpha the level of transparency of the edges. #' Defaults to \code{alpha}, which defaults to \code{1}. #' @param segment.color the color of the edges, as a color value, a vector of #' color values, or as an edge attribute containing color values. #' Defaults to \code{"grey50"}. #' @param segment.size the size of the edges, in points, as a single numeric #' value, a vector of values, or as an edge attribute. #' Defaults to \code{0.25}. #' @param segment.label the labels to plot at the middle of the edges, as a #' single value, a vector of values, or as an edge attribute. #' Defaults to \code{NULL} (no edge labels). #' @param arrow.size the size of the arrows for directed network edges, in #' points. See \code{\link[grid]{arrow}} for details. #' Defaults to \code{0} (no arrows). #' @param arrow.gap a setting aimed at improving the display of edge arrows by #' plotting slightly shorter edges. Accepts any value between \code{0} and #' \code{1}, where a value of \code{0.05} will generally achieve good results #' when the size of the nodes is reasonably small. #' Defaults to \code{0} (no shortening). #' @param arrow.type the type of the arrows for directed network edges. See #' \code{\link[grid]{arrow}} for details. #' Defaults to \code{"closed"}. #' @param label whether to label the nodes. If set to \code{TRUE}, nodes are #' labeled with their vertex names. If set to a vector that contains as many #' elements as there are nodes in \code{net}, nodes are labeled with these. If #' set to any other vector of values, the nodes are labeled only when their #' vertex name matches one of these values. #' Defaults to \code{FALSE} (no labels). #' @param label.nodes see \code{label} #' @param label.size the size of the node labels, in points, as a numeric value, #' a vector of numeric values, or as a vertex attribute containing numeric #' values. #' Defaults to \code{size / 2} (half the maximum node size), which defaults to #' \code{6}. #' @param label.trim whether to apply some trimming to the node labels. Accepts #' any function that can process a character vector, or a strictly positive #' numeric value, in which case the labels are trimmed to a fixed-length #' substring of that length: see \code{\link[base]{substr}} for details. #' Defaults to \code{FALSE} (does nothing). #' @param legend.size the size of the legend symbols and text, in points. #' Defaults to \code{9}. #' @param legend.position the location of the plot legend(s). Accepts all #' \code{legend.position} values supported by \code{\link[ggplot2]{theme}}. #' Defaults to \code{"right"}. #' @param names deprecated: see \code{group.legend} and \code{size.legend} #' @param quantize.weights deprecated: see \code{weight.cut} #' @param subset.threshold deprecated: see \code{weight.min} #' @param top8.nodes deprecated: this functionality was experimental and has #' been removed entirely from \code{ggnet} #' @param trim.labels deprecated: see \code{label.trim} #' @param ... other arguments passed to the \code{geom_text} object that sets #' the node labels: see \code{\link[ggplot2]{geom_text}} for details. #' @seealso \code{\link{ggnet2}} in this package, #' \code{\link[sna]{gplot}} in the \code{\link[sna]{sna}} package, and #' \code{\link[network]{plot.network}} in the \code{\link[network]{network}} #' package #' @author Moritz Marbach and Francois Briatte, with help from Heike Hofmann, #' Pedro Jordano and Ming-Yu Liu #' @details The degree centrality measures that can be produced through the #' \code{weight} argument will take the directedness of the network into account, #' but will be unweighted. To compute weighted network measures, see the #' \code{tnet} package by Tore Opsahl (\code{help("tnet", package = "tnet")}). #' @importFrom stats quantile na.omit #' @importFrom utils head installed.packages #' @importFrom grDevices gray.colors #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' library(network) #' #' # random adjacency matrix #' x <- 10 #' ndyads <- x * (x - 1) #' density <- x / ndyads #' m <- matrix(0, nrow = x, ncol = x) #' dimnames(m) <- list(letters[ 1:x ], letters[ 1:x ]) #' m[ row(m) != col(m) ] <- runif(ndyads) < density #' m #' #' # random undirected network #' n <- network::network(m, directed = FALSE) #' n #' #' ggnet(n, label = TRUE, alpha = 1, color = "white", segment.color = "black") #' #' # random groups #' g <- sample(letters[ 1:3 ], 10, replace = TRUE) #' g #' #' # color palette #' p <- c("a" = "steelblue", "b" = "forestgreen", "c" = "tomato") #' #' p_(ggnet(n, node.group = g, node.color = p, label = TRUE, color = "white")) #' #' # edge arrows on a directed network #' p_(ggnet(network(m, directed = TRUE), arrow.gap = 0.05, arrow.size = 10)) ggnet <- function( net, mode = "fruchtermanreingold", layout.par = NULL, layout.exp = 0, size = 9, alpha = 1, weight = "none", weight.legend = NA, weight.method = weight, weight.min = NA, weight.max = NA, weight.cut = FALSE, group = NULL, group.legend = NA, node.group = group, node.color = NULL, node.alpha = alpha, segment.alpha = alpha, segment.color = "grey50", segment.label = NULL, segment.size = 0.25, arrow.size = 0, arrow.gap = 0, arrow.type = "closed", label = FALSE, label.nodes = label, label.size = size / 2, label.trim = FALSE, legend.size = 9, legend.position = "right", # -- deprecated arguments ---------------------------------------------------- names = c("", ""), quantize.weights = FALSE, subset.threshold = 0, top8.nodes = FALSE, trim.labels = FALSE, ... ){ # -- packages ---------------------------------------------------------------- require_namespaces(c("network", "sna", "scales")) # -- deprecations ------------------------------------------------------------ if (length(mode) == 1 && mode == "geo") { warning("mode = 'geo' is deprecated; please use mode = c('lon', 'lat') instead") mode = c("lon", "lat") } if (!identical(names, c("", ""))) { warning("names is deprecated; please use group.legend and size.legend instead") group.legend = names[1] size.legend = names[2] } if (isTRUE(quantize.weights)) { warning("quantize.weights is deprecated; please use weight.cut instead") weight.cut = TRUE } if (subset.threshold > 0) { warning("subset.threshold is deprecated; please use weight.min instead") weight.min = subset.threshold } if (isTRUE(top8.nodes)) { warning("top8.nodes is deprecated") } if (isTRUE(trim.labels)) { warning("trim.labels is deprecated; please use label.trim instead") label.trim = function(x) gsub("^@|^http://(www\\.)?|/$", "", x) } # -- conversion to network class --------------------------------------------- if (inherits(net, "igraph") && "intergraph" %in% rownames(installed.packages())) { net = intergraph::asNetwork(net) } else if (inherits(net, "igraph")) { stop("install the 'intergraph' package to use igraph objects with ggnet") } if (!network::is.network(net)) { net = try(network::network(net), silent = TRUE) } if (!network::is.network(net)) { stop("could not coerce net to a network object") } # -- network functions ------------------------------------------------------- get_v = utils::getFromNamespace("%v%", ns = "network") get_e = utils::getFromNamespace("%e%", ns = "network") set_mode = function(x, mode = network::get.network.attribute(x, "bipartite")) { c(rep("actor", mode), rep("event", n_nodes - mode)) } set_node = function(x, value, mode = TRUE) { if (is.null(x) || any(is.na(x)) || any(is.infinite(x)) || any(is.nan(x))) { stop(paste("incorrect", value, "value")) } else if (is.numeric(x) && any(x < 0)) { stop(paste("incorrect", value, "value")) } else if (length(x) == n_nodes) { x } else if (length(x) > 1) { stop(paste("incorrect", value, "length")) } else if (any(x %in% v_attr)) { get_v(net, x) } else if (mode && identical(x, "mode") && is_bip) { set_mode(net) } else { x } } set_edge = function(x, value) { if (is.null(x) || any(is.na(x)) || any(is.infinite(x)) || any(is.nan(x))) { stop(paste("incorrect", value, "value")) } else if (is.numeric(x) && any(x < 0)) { stop(paste("incorrect", value, "value")) } else if (length(x) == n_edges) { x } else if (length(x) > 1) { stop(paste("incorrect", value, "length")) } else if (any(x %in% e_attr)) { get_e(net, x) } else { x } } set_attr = function(x) { if (length(x) == n_nodes) { x } else if (length(x) > 1) { stop(paste("incorrect coordinates length")) } else if (!x %in% v_attr) { stop(paste("vertex attribute", x, "was not found")) } else if (!is.numeric(get_v(net, x))) { stop(paste("vertex attribute", x, "is not numeric")) } else { get_v(net, x) } } set_name = function(x, y) ifelse(length(x) == 1, x, ifelse(is.na(y), "", y)) is_one = function(x) length(unique(x)) == 1 is_col = function(x) all(is.numeric(x)) | all(network::is.color(x)) # -- network structure ------------------------------------------------------- n_nodes = network::network.size(net) n_edges = network::network.edgecount(net) v_attr = network::list.vertex.attributes(net) e_attr = network::list.edge.attributes(net) is_bip = network::is.bipartite(net) is_dir = ifelse(network::is.directed(net), "digraph", "graph") if (!is.numeric(arrow.size) || arrow.size < 0) { stop("incorrect arrow.size value") } else if (arrow.size > 0 & is_dir == "graph") { warning("network is undirected; arrow.size ignored") arrow.size = 0 } if (!is.numeric(arrow.gap) || arrow.gap < 0 || arrow.gap > 1) { stop("incorrect arrow.gap value") } else if (arrow.gap > 0 & is_dir == "graph") { warning("network is undirected; arrow.gap ignored") arrow.gap = 0 } if (network::is.hyper(net)) { stop("ggnet cannot plot hyper graphs") } if (network::is.multiplex(net)) { stop("ggnet cannot plot multiplex graphs") } if (network::has.loops(net)) { warning("ggnet does not know how to handle self-loops") } # -- check size -------------------------------------------------------------- x = size if (!is.numeric(x) || is.infinite(x) || is.nan(x) || x < 0 || length(x) > 1) { stop("incorrect size value") } # -- initialize dataset ------------------------------------------------------ data = data.frame(label = get_v(net, "vertex.names"), stringsAsFactors = FALSE) # -- weight methods ---------------------------------------------------------- x = weight.method if (length(x) == 1 && x %in% c("indegree", "outdegree", "degree", "freeman")) { # prevent namespace conflict with igraph if ("package:igraph" %in% search()) { y = ifelse(is_dir == "digraph", "directed", "undirected") z = c("indegree" = "in", "outdegree" = "out", "degree" = "all", "freeman" = "all")[ x ] data$weight = igraph::degree(igraph::graph.adjacency(as.matrix(net), mode = y), mode = z) } else { data$weight = sna::degree(net, gmode = is_dir, cmode = ifelse(x == "degree", "freeman", x)) } } else if (length(x) > 1 && length(x) == n_nodes) { data$weight = x } else if (length(x) == 1 && x %in% v_attr) { data$weight = get_v(net, x) } if (!is.null(data$weight) && !is.numeric(data$weight)) { stop("incorrect weight.method value") } # -- weight thresholds ------------------------------------------------------- x = ifelse(is.na(weight.min), 0, weight.min) if (length(x) > 1 || !is.numeric(x) || is.infinite(x) || is.nan(x) || x < 0) { stop("incorrect weight.min value") } else if (x > 0) { x = which(data$weight < x) message(paste("weight.min removed", length(x), "nodes out of", nrow(data))) if (length(x) > 0) { data = data[ -x, ] network::delete.vertices(net, x) if (!nrow(data)) { warning("weight.min removed all nodes; nothing left to plot") return(invisible(NULL)) } } } x = ifelse(is.na(weight.max), 0, weight.max) if (length(x) > 1 || !is.numeric(x) || is.infinite(x) || is.nan(x) || x < 0) { stop("incorrect weight.max value") } else if (x > 0) { x = which(data$weight > x) message(paste("weight.max removed", length(x), "nodes out of", nrow(data))) if (length(x) > 0) { data = data[ -x, ] network::delete.vertices(net, x) if (!nrow(data)) { warning("weight.max removed all nodes; nothing left to plot") return(invisible(NULL)) } } } # -- weight quantiles -------------------------------------------------------- x = weight.cut if (length(x) > 1 || is.null(x) || is.na(x) || is.infinite(x) || is.nan(x)) { stop("incorrect weight.cut value") } else if (isTRUE(x)) { x = 4 } else if (is.logical(x) && !x) { x = 0 } else if (!is.numeric(x)) { stop("incorrect weight.cut value") } if (x >= 1) { x = unique(quantile(data$weight, probs = seq(0, 1, by = 1 / as.integer(x)))) if (length(x) > 1) { data$weight = cut(data$weight, unique(x), include.lowest = TRUE) } else { warning("node weight is invariant; weight.cut ignored") } } # -- node sizing ------------------------------------------------------------- if (is.factor(data$weight)) { sizer = scale_size_area( set_name(weight.method, weight.legend), max_size = size, breaks = sort(unique(as.integer(data$weight))), labels = levels(data$weight)[ sort(unique(as.integer(data$weight))) ] ) data$weight = as.integer(data$weight) } else { sizer = scale_size_area( set_name(weight.method, weight.legend), max_size = size ) } # -- node grouping ----------------------------------------------------------- if (!is.null(node.group)) { data$group = factor(set_node(node.group, "node.group")) x = length(unique(na.omit(data$group))) if (length(node.color) != x) { if (!is.null(node.color)) { warning("node groups and colors are of unequal length; using grayscale colors") } node.color = gray.colors(x) names(node.color) = unique(na.omit(data$group)) } } # -- node labels ------------------------------------------------------------- l = label.nodes if (isTRUE(l)) { l = data$label } else if (length(l) > 1 & length(l) == n_nodes) { data$label = l } else if (length(l) == 1 && l %in% v_attr) { l = get_v(net, l) } else { l = ifelse(data$label %in% l, data$label, "") } # -- node placement ---------------------------------------------------------- if (is.character(mode) && length(mode) == 1) { mode = paste0("gplot.layout.", mode) snaNamespace = asNamespace("sna") if (!exists(mode, envir = snaNamespace)) { stop(paste("unsupported placement method:", mode)) } mode = get(mode, envir = snaNamespace) # sna placement algorithm xy = network::as.matrix.network.adjacency(net) xy = do.call(mode, list(xy, layout.par)) xy = data.frame(x = xy[, 1], y = xy[, 2]) } else if (is.character(mode) && length(mode) == 2) { # fixed coordinates from vertex attributes xy = data.frame(x = set_attr(mode[1]), y = set_attr(mode[2])) } else if (is.numeric(mode) && is.matrix(mode)) { # fixed coordinates from matrix xy = data.frame(x = set_attr(mode[, 1]), y = set_attr(mode[, 2])) } else { stop("incorrect mode value") } xy$x = scale(xy$x, min(xy$x), diff(range(xy$x)))[,1] xy$y = scale(xy$y, min(xy$y), diff(range(xy$y)))[,1] data = cbind(data, xy) # -- edge list --------------------------------------------------------------- edges = network::as.matrix.network.edgelist(net) edges = data.frame(xy[ edges[, 1], ], xy[ edges[, 2], ]) names(edges) = c("X1", "Y1", "X2", "Y2") # -- edge labels ------------------------------------------------------------- if (!is.null(segment.label)) { edges$midX = (edges$X1 + edges$X2) / 2 edges$midY = (edges$Y1 + edges$Y2) / 2 edges$label = set_edge(segment.label, "segment.label") } # -- plot edges -------------------------------------------------------------- p = ggplot(data, aes(x = x, y = y)) if (nrow(edges) > 0) { if (arrow.gap > 0) { x.length = with(edges, abs(X2 - X1)) y.length = with(edges, abs(Y2 - Y1)) arrow.gap = with(edges, arrow.gap / sqrt(x.length ^ 2 + y.length ^ 2)) edges = transform(edges, X1 = X1 + arrow.gap * x.length, Y1 = Y1 + arrow.gap * y.length, X2 = X1 + (1 - arrow.gap) * x.length, Y2 = Y1 + (1 - arrow.gap) * y.length) } p = p + geom_segment( data = edges, aes(x = X1, y = Y1, xend = X2, yend = Y2), alpha = segment.alpha, size = segment.size, color = segment.color, arrow = arrow( type = arrow.type, length = unit(arrow.size, "pt") ) ) } if (nrow(edges) > 0 && !is.null(segment.label)) { p = p + geom_point( data = edges, aes(x = midX, y = midY), color = "white", size = size ) + geom_text( data = edges, aes(x = midX, y = midY, label = label), alpha = segment.alpha, color = segment.color, size = size / 2 ) } # -- plot nodes -------------------------------------------------------------- if (length(weight.method) == 1 && weight.method == "none") { p = p + geom_point( alpha = node.alpha, size = size ) } else { p = p + geom_point( aes(size = weight), alpha = node.alpha ) + sizer } # -- plot node colors -------------------------------------------------------- if (!is.null(node.group)) { p = p + aes(color = group) + scale_color_manual( set_name(node.group, group.legend), values = node.color, guide = guide_legend(override.aes = list(size = legend.size)) ) } # -- plot node labels -------------------------------------------------------- if (!is_one(l) || unique(l) != "") { label.size = set_node(label.size, "label.size", mode = FALSE) if (!is.numeric(label.size)) { stop("incorrect label.size value") } x = label.trim if (length(x) > 1 || (!is.logical(x) & !is.numeric(x) & !is.function(x))) { stop("incorrect label.trim value") } else if (is.numeric(x) && x > 0) { l = substr(l, 1, x) } else if (is.function(x)) { l = x(l) } p = p + geom_text( label = l, size = label.size, show.legend = FALSE, # required by ggplot2 >= 1.0.1.9003 ... ) } # -- horizontal scale expansion ---------------------------------------------- x = range(data$x) if (!is.numeric(layout.exp) || layout.exp < 0) { stop("incorrect layout.exp value") } else if (layout.exp > 0) { x = scales::expand_range(x, layout.exp / 2) } # -- finalize ---------------------------------------------------------------- p = p + scale_x_continuous(breaks = NULL, limits = x) + scale_y_continuous(breaks = NULL) + theme( panel.background = element_blank(), panel.grid = element_blank(), axis.title = element_blank(), legend.key = element_blank(), legend.position = legend.position, legend.text = element_text(size = legend.size), legend.title = element_text(size = legend.size) ) return(p) } GGally/R/ggmatrix_legend.R0000644000176200001440000000664213666472400015135 0ustar liggesusers#' Grab the legend and print it as a plot #' #' @param p ggplot2 plot object #' @param x legend object that has been grabbed from a ggplot2 object #' @param ... ignored #' @param plotNew boolean to determine if the `grid.newpage()` command and a new blank rectangle should be printed #' @import ggplot2 #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' library(ggplot2) #' histPlot <- qplot( #' x = Sepal.Length, #' data = iris, #' fill = Species, #' geom = "histogram", #' binwidth = 1/4 #' ) #' (right <- histPlot) #' (bottom <- histPlot + theme(legend.position = "bottom")) #' (top <- histPlot + theme(legend.position = "top")) #' (left <- histPlot + theme(legend.position = "left")) #' #' p_(grab_legend(right)) #' p_(grab_legend(bottom)) #' p_(grab_legend(top)) #' p_(grab_legend(left)) grab_legend <- function(p) { builtP <- ggplot_build(p) pTable <- ggplot_gtable(builtP) ret <- get_legend_from_gtable(pTable) return(ret) } get_legend_from_gtable <- function(pTable) { ret <- ggplot2::zeroGrob() if (inherits(pTable, "gtable")) { if ("guide-box" %in% pTable$layout$name) { ret <- gtable_filter(pTable, "guide-box") } } class(ret) <- c("legend_guide_box", class(ret)) ret } #' @importFrom grid grid.newpage grid.draw gpar #' @importFrom gtable gtable_filter #' @rdname grab_legend #' @export print.legend_guide_box <- function(x, ..., plotNew = FALSE) { if (identical(plotNew, TRUE)) { grid.newpage() } grid::grid.rect(gp = grid::gpar(fill = "white", col = "white")) grid.draw(x) } #' Plot only legend of plot function #' #' @param fn this value is passed directly to an empty \code{\link{wrap}} call. Please see \code{?\link{wrap}} for more details. #' @return a function that when called with arguments will produce the legend of the plotting function supplied. #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' # display regular plot #' p_(ggally_points(iris, ggplot2::aes(Sepal.Length, Sepal.Width, color = Species))) #' #' # Make a function that will only print the legend #' points_legend <- gglegend(ggally_points) #' p_(points_legend(iris, ggplot2::aes(Sepal.Length, Sepal.Width, color = Species))) #' #' # produce the sample legend plot, but supply a string that 'wrap' understands #' same_points_legend <- gglegend("points") #' identical( #' attr(attr(points_legend, "fn"), "original_fn"), #' attr(attr(same_points_legend, "fn"), "original_fn") #' ) #' #' # Complicated examples #' custom_legend <- wrap(gglegend("points"), size = 6) #' p_(custom_legend(iris, ggplot2::aes(Sepal.Length, Sepal.Width, color = Species))) #' #' # Use within ggpairs #' pm <- ggpairs( #' iris, 1:2, #' mapping = ggplot2::aes(color = Species), #' upper = list(continuous = gglegend("points")) #' ) #' p_(pm) #' #' # Place a legend in a specific location #' pm <- ggpairs(iris, 1:2, mapping = ggplot2::aes(color = Species)) #' # Make the legend #' pm[1,2] <- points_legend(iris, ggplot2::aes(Sepal.Width, Sepal.Length, color = Species)) #' p_(pm) gglegend <- function(fn) { # allows users to supply a character just like in ggpairs fn <- wrapp(fn, list()) fn <- attr(fn, "fn") ret <- function(...) { p <- fn(...) grab_legend(p) } # attach function so people can see what it is attr(ret, "fn") <- fn attr(ret, "name") <- "gglegend" ret } GGally/R/data-psychademic.R0000644000176200001440000000162313663637143015173 0ustar liggesusers#' UCLA canonical correlation analysis data #' #' This data contains 600 observations on eight variables #' #' @details \itemize{ #' \item locus_of_control - psychological #' \item self_concept - psychological #' \item motivation - psychological. Converted to four character groups #' \item read - academic #' \item write - academic #' \item math - academic #' \item science - academic #' \item female - academic. Dropped from original source #' \item sex - academic. Added as a character version of female column #' } #' #' @docType data #' @keywords datasets #' @name psychademic #' @usage data(psychademic) #' @format A data frame with 600 rows and 8 variables #' @references #' R Data Analysis Examples | Canonical Correlation Analysis. UCLA: Institute for Digital Research and Education. from http://www.stats.idre.ucla.edu/r/dae/canonical-correlation-analysis (accessed May 22, 2017). NULL GGally/R/ggpairs_internal_plots.R0000644000176200001440000002074013666472400016541 0ustar liggesusers #' Wrap a function with different parameter values #' #' Wraps a function with the supplied parameters to force different default behavior. This is useful for functions that are supplied to ggpairs. It allows you to change the behavior of one function, rather than creating multiple functions with different parameter settings. #' #' \code{wrap} is identical to \code{wrap_fn_with_params}. These function take the new parameters as arguments. #' #' \code{wrapp} is identical to \code{wrap_fn_with_param_arg}. These functions take the new parameters as a single list. #' #' The \code{params} and \code{fn} attributes are there for debugging purposes. If either attribute is altered, the function must be re-wrapped to have the changes take effect. #' #' @param funcVal function that the \code{params} will be applied to. The function should follow the api of \code{function(data, mapping, ...)\{\}}. \code{funcVal} is allowed to be a string of one of the \code{ggally_NAME} functions, such as \code{"points"} for \code{ggally_points} or \code{"facetdensity"} for \code{ggally_facetdensity}. #' @param ... named parameters to be supplied to \code{wrap_fn_with_param_arg} #' @param params named vector or list of parameters to be applied to the \code{funcVal} #' @param funcArgName name of function to be displayed #' @return a \code{function(data, mapping, ...)\{\}} that will wrap the original function with the parameters applied as arguments #' @export #' @rdname wrap #' @examples #' # small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' # example function that prints 'val' #' fn <- function(data, mapping, val = 2) { #' print(val) #' } #' fn(data = NULL, mapping = NULL) # 2 #' #' # wrap function to change default value 'val' to 5 instead of 2 #' wrapped_fn1 <- wrap(fn, val = 5) #' wrapped_fn1(data = NULL, mapping = NULL) # 5 #' # you may still supply regular values #' wrapped_fn1(data = NULL, mapping = NULL, val = 3) # 3 #' #' # wrap function to change 'val' to 5 using the arg list #' wrapped_fn2 <- wrap_fn_with_param_arg(fn, params = list(val = 5)) #' wrapped_fn2(data = NULL, mapping = NULL) # 5 #' #' # change parameter settings in ggpairs for a particular function #' ## Goal output: #' regularPlot <- ggally_points( #' iris, #' ggplot2::aes(Sepal.Length, Sepal.Width), #' size = 5, color = "red" #' ) #' p_(regularPlot) #' #' # Wrap ggally_points to have parameter values size = 5 and color = 'red' #' w_ggally_points <- wrap(ggally_points, size = 5, color = "red") #' wrappedPlot <- w_ggally_points( #' iris, #' ggplot2::aes(Sepal.Length, Sepal.Width) #' ) #' p_(wrappedPlot) #' #' # Double check the aes parameters are the same for the geom_point layer #' identical(regularPlot$layers[[1]]$aes_params, wrappedPlot$layers[[1]]$aes_params) #' #' # Use a wrapped function in ggpairs #' pm <- ggpairs(iris, 1:3, lower = list(continuous = wrap(ggally_points, size = 5, color = "red"))) #' p_(pm) #' pm <- ggpairs(iris, 1:3, lower = list(continuous = w_ggally_points)) #' p_(pm) wrap_fn_with_param_arg <- function( funcVal, params = NULL, funcArgName = deparse(substitute(funcVal)) ) { if (missing(funcArgName)) { fnName <- attr(funcVal, "name") if (!is.null(fnName)) { funcArgName <- fnName } } if (!is.null(params)) { if (is.vector(params)) { params <- as.list(params) } if (length(params) > 0) { if (!is.list(params)) { stop("'params' must be a named list, named vector, or NULL") } if (is.null(names(params))) { stop("'params' must be a named list, named vector, or NULL") } if (any(nchar(names(params)) == 0)) { stop("'params' must be a named list, named vector, or NULL") } } } if (mode(funcVal) == "character") { if (missing(funcArgName)) { funcArgName <- str_c("ggally_", funcVal) } tryCatch({ funcVal <- get( str_c("ggally_", funcVal), mode = "function" ) }, error = function(e) { stop(str_c( "Error retrieving `GGally` function.\n", "Please provide a string such as `'points'` for `ggally_points()`\n", "For a list of all predefined functions, check out `vig_ggally(\"ggally_plots\")`\n", "A custom function may be supplied directly: `wrap(my_fn, param = val)`\n", "Function provided: ", funcVal )) } ) } allParams <- ifnull(attr(funcVal, "params"), list()) allParams[names(params)] <- params original_fn <- funcVal ret_fn <- function(data, mapping, ...) { allParams$data <- data allParams$mapping <- mapping argsList <- list(...) allParams[names(argsList)] <- argsList do.call(original_fn, allParams) } class(ret_fn) <- "ggmatrix_fn_with_params" attr(ret_fn, "name") <- as.character(funcArgName) attr(ret_fn, "params") <- allParams attr(ret_fn, "fn") <- original_fn ret_fn } #' @export #' @rdname wrap wrapp <- wrap_fn_with_param_arg #' @export #' @rdname wrap wrap <- function(funcVal, ..., funcArgName = deparse(substitute(funcVal))) { if (missing(funcArgName)) { fnName <- attr(funcVal, "name") if (!is.null(fnName)) { funcArgName <- fnName } else if (is.character(funcVal)) { funcArgName <- str_c("ggally_", funcVal) } } params <- list(...) if (length(params) > 0) { if (is.null(names(params))) { stop("all parameters must be named arguments") } if (any(nchar(names(params)) == 0)) { stop("all parameters must be named arguments") } } wrap_fn_with_param_arg(funcVal, params = params, funcArgName = funcArgName) } #' @export #' @rdname wrap wrap_fn_with_params <- wrap as.character.ggmatrix_fn_with_params <- function(x, ...) { params <- attr(x, "params") fnName <- attr(x, "name") if (length(params) == 0) { txt <- str_c("wrap: '", fnName, "'") } else { txt <- str_c("wrap: '", attr(x, "name"), "'; params: ", mapping_as_string(params)) } txt } make_ggmatrix_plot_obj <- function(fn, mapping = ggplot2::aes(), dataPos = 1, gg = NULL) { # nonCallVals <- which(lapply(mapping, mode) == "call") # if (length(nonCallVals) > 0) { # nonCallNames <- names(mapping)[nonCallVals] # browser() # stop( # paste( # "variables: ", # paste(shQuote(nonCallNames, type = "cmd"), sep = ", "), # " have non standard format: ", # paste(shQuote(unlist(mapping[nonCallVals]), type = "cmd"), collapse = ", "), # ". Please rename the columns or make a new column.", # sep = "" # ) # ) # } ret <- list( fn = fn, mapping = mapping, dataPos = dataPos, gg = gg ) class(ret) <- "ggmatrix_plot_obj" ret } blank_plot_string <- function() { "PM; (blank)" } mapping_as_string <- function(mapping) { str_c("c(", str_c(names(mapping), as.character(mapping), sep = " = ", collapse = ", "), ")") } as.character.ggmatrix_plot_obj <- function(x, ...) { hasGg <- (!is.null(x$gg)) mappingTxt <- mapping_as_string(x$mapping) fnTxt <- ifelse(inherits(x$fn, "ggmatrix_fn_with_params"), as.character(x$fn), "custom_function") if (inherits(x$fn, "ggmatrix_fn_with_params")) { if (attr(x$fn, "name") %in% c("ggally_blank", "ggally_blankDiag")) { return(blank_plot_string()) } } str_c( "PM", "; aes: ", mappingTxt, "; fn: {", fnTxt, "}", # "; dataPos: ", x$dataPos, "; gg: ", as.character(hasGg) ) } #' \code{\link{ggmatrix}} structure #' #' View the condensed version of the \code{\link{ggmatrix}} object. The attribute "class" is ALWAYS altered to "_class" to avoid recursion. #' #' @param object \code{\link{ggmatrix}} object to be viewed #' @param ... passed on to the default \code{str} method #' @param raw boolean to determine if the plots should be converted to text or kept as original objects #' @method str ggmatrix #' @importFrom utils str #' @export str.ggmatrix <- function(object, ..., raw = FALSE) { objName <- deparse(substitute(object)) obj <- object if (identical(raw, FALSE)) { cat(str_c( "\nCustom str.ggmatrix output: \nTo view original object use ", "'str(", objName, ", raw = TRUE)'\n\n" )) obj$plots <- lapply(obj$plots, function(plotObj) { if (ggplot2::is.ggplot(plotObj)) { str_c("PM; ggplot2 object; mapping: ", mapping_as_string(plotObj$mapping)) } else if (inherits(plotObj, "ggmatrix_plot_obj")) { as.character(plotObj) } else { plotObj } }) } attr(obj, "_class") <- attr(obj, "class") class(obj) <- NULL str(obj, ...) } GGally/R/stat_prop.R0000644000176200001440000002112513764714663014012 0ustar liggesusers#' Compute proportions according to custom denominator #' #' \code{stat_prop} is a variation of [ggplot2::stat_count()] allowing to compute custom #' proportions according to the \strong{by} aesthetic defining the denominator #' (i.e. all proportions for a same value of \strong{by} will sum to 1). #' The \code{by} aesthetic should be a factor. #' #' @inheritParams ggplot2::stat_count #' @param geom Override the default connection between \code{\link[ggplot2]{geom_bar}} #' and \code{stat_prop}. #' @section Aesthetics: #' `stat_prop()` understands the following aesthetics (required aesthetics are in bold): #' #' - **x *or* y** #' - **by** (this aesthetic should be a **factor**) #' - group #' - weight #' @section Computed variables: #' \describe{ #' \item{count}{number of points in bin} #' \item{prop}{computed proportion} #' } #' @seealso [ggplot2::stat_count()] #' #' @import ggplot2 #' @author Joseph Larmarange #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' d <- as.data.frame(Titanic) #' #' p <- ggplot(d) + #' aes(x = Class, fill = Survived, weight = Freq, by = Class) + #' geom_bar(position = "fill") + #' geom_text(stat = "prop", position = position_fill(.5)) #' p_(p) #' p_(p + facet_grid(~ Sex)) #' #' p_(ggplot(d) + #' aes(x = Class, fill = Survived, weight = Freq) + #' geom_bar(position = "dodge") + #' geom_text( #' aes(by = Survived), stat = "prop", #' position = position_dodge(0.9), vjust = "bottom" #' )) #' #' p_(ggplot(d) + #' aes(x = Class, fill = Survived, weight = Freq, by = 1) + #' geom_bar() + #' geom_text( #' aes(label = scales::percent(after_stat(prop), accuracy = 1)), #' stat = "prop", #' position = position_stack(.5) #' )) stat_prop <- function( mapping = NULL, data = NULL, geom = "bar", position = "fill", ..., width = NULL, na.rm = FALSE, orientation = NA, show.legend = NA, inherit.aes = TRUE ) { params <- list( na.rm = na.rm, orientation = orientation, width = width, ... ) if (!is.null(params$y)) { stop("stat_prop() must not be used with a y aesthetic.", call. = FALSE) } layer( data = data, mapping = mapping, stat = StatProp, geom = geom, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = params ) } #' @rdname stat_prop #' @format NULL #' @usage NULL #' @export StatProp <- ggproto("StatProp", Stat, required_aes = c("x|y", "by"), default_aes = aes( x = after_stat(count), y = after_stat(count), weight = 1, label = scales::percent(after_stat(prop), accuracy = .1) ), setup_params = function(data, params) { params$flipped_aes <- has_flipped_aes(data, params, main_is_orthogonal = FALSE) has_x <- !(is.null(data$x) && is.null(params$x)) has_y <- !(is.null(data$y) && is.null(params$y)) if (!has_x && !has_y) { stop("stat_prop() requires an x or y aesthetic.", call. = FALSE) } if (has_x && has_y) { stop("stat_prop() can only have an x or y aesthetic.", call. = FALSE) } # there is an unresolved bug when by is a character vector. To be explored. if (is.character(data$by)) { stop("The by aesthetic should be a factor instead of a character.", call. = FALSE) } params }, extra_params = c("na.rm"), compute_panel = function(self, data, scales, width = NULL, flipped_aes = FALSE) { data <- flip_data(data, flipped_aes) data$weight <- data$weight %||% rep(1, nrow(data)) width <- width %||% (ggplot2::resolution(data$x) * 0.9) # sum weights for each combination of by and aesthetics # the use of . allows to consider all aesthetics defined in data panel <- aggregate(weight ~ ., data = data, sum, na.rm = TRUE) names(panel)[which(names(panel) == "weight")] <- "count" panel$count[is.na(panel$count)] <- 0 # compute proportions by by sum_abs <- function(x) {sum(abs(x))} panel$prop <- panel$count / ave(panel$count, panel$by, FUN = sum_abs) panel$width <- width panel$flipped_aes <- flipped_aes flip_data(panel, flipped_aes) } ) #' Column and row bar plots #' #' Plot column or row percentage using bar plots. #' #' @param data data set using #' @param mapping aesthetics being used #' @param label_format formatter function for displaying proportions, not taken into account if a label aesthetic is provided in \code{mapping} #' @param ... other arguments passed to \code{\link[ggplot2]{geom_text}(...)} #' @param remove_percentage_axis should percentage axis be removed? Removes the y-axis for \code{ggally_colbar()} and x-axis for \code{ggally_rowbar()} #' @param remove_background should the \code{panel.background} be removed? #' @param reverse_fill_levels should the levels of the fill variable be reversed? #' @param geom_bar_args other arguments passed to \code{\link[ggplot2]{geom_bar}(...)} #' @author Joseph Larmarange #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_colbar(tips, mapping = aes(x = smoker, y = sex))) #' p_(ggally_rowbar(tips, mapping = aes(x = smoker, y = sex))) #' #' # change labels' size #' p_(ggally_colbar(tips, mapping = aes(x = smoker, y = sex), size = 8)) #' #' # change labels' colour and use bold #' p_(ggally_colbar(tips, mapping = aes(x = smoker, y = sex), #' colour = "white", fontface = "bold")) #' #' # display number of observations instead of proportions #' p_(ggally_colbar(tips, mapping = aes(x = smoker, y = sex, label = after_stat(count)))) #' #' # custom bar width #' p_(ggally_colbar(tips, mapping = aes(x = smoker, y = sex), geom_bar_args = list(width = .5))) #' #' # change format of labels #' p_(ggally_colbar(tips, mapping = aes(x = smoker, y = sex), #' label_format = scales::label_percent(accuracy = .01, decimal.mark = ","))) #' #' p_(ggduo( #' data = as.data.frame(Titanic), #' mapping = aes(weight = Freq), #' columnsX = "Survived", #' columnsY = c("Sex", "Class", "Age"), #' types = list(discrete = "rowbar"), #' legend = 1 #' )) ggally_colbar <- function( data, mapping, label_format = scales::label_percent(accuracy = .1), ..., remove_background = FALSE, remove_percentage_axis = FALSE, reverse_fill_levels = FALSE, geom_bar_args = NULL ) { if (is.null(mapping$x)) stop("'x' aesthetic is required.") if (is.null(mapping$y)) stop("'y' aesthetic is required.") # y should be mapped to fill and x to by mapping$fill <- mapping$y mapping$y <- NULL mapping$by <- mapping$x # colour should not be mapped in aes if (!is.null(mapping$colour)) mapping$colour <- NULL # label mapping if (!is.null(mapping$label)) { mapping_text <- aes() mapping_text$label <- mapping$label } else { mapping_text <- aes_string(label = "label_format(after_stat(prop))") } # position for geom_bar geom_bar_args$position <- position_fill(reverse = reverse_fill_levels) p <- ggplot(data, mapping) + do.call(geom_bar, geom_bar_args) + geom_text( mapping = mapping_text, stat = "prop", position = position_fill(.5, reverse = reverse_fill_levels), ... ) + scale_y_continuous( labels = scales::percent, expand = expansion(ifelse(remove_background, 0, .05), 0) ) + scale_x_discrete(expand = expansion(0, ifelse(remove_background, 0, .6))) + ylab("") + guides(fill = guide_legend(reverse = reverse_fill_levels)) if (isTRUE(remove_background)) { p <- p + theme( panel.background = element_blank() ) } if (isTRUE(remove_percentage_axis)) { p <- p + theme( panel.grid = element_blank(), axis.text.y = element_blank(), axis.ticks.y = element_blank() ) } p } #' @rdname ggally_colbar #' @export ggally_rowbar <- function( data, mapping, label_format = scales::label_percent(accuracy = .1), ..., remove_background = FALSE, remove_percentage_axis = FALSE, reverse_fill_levels = TRUE, geom_bar_args = NULL ) { mapping <- mapping_swap_x_y(mapping) p <- ggally_colbar( data = data, mapping = mapping, label_format = label_format, ..., remove_background = remove_background, remove_percentage_axis = FALSE, reverse_fill_levels = reverse_fill_levels, geom_bar_args = geom_bar_args ) + coord_flip() + guides(fill = guide_legend(reverse = !reverse_fill_levels)) if (isTRUE(remove_percentage_axis)) { p <- p + theme( panel.grid = element_blank(), axis.text.x = element_blank(), axis.ticks.x = element_blank() ) } p } GGally/R/vig_ggally.R0000644000176200001440000000242413777103031014105 0ustar liggesusers#' View GGally vignettes #' #' This function will open the directly to the vignette requested. If no \code{name} is provided, the index of all \pkg{GGally} vignettes will be opened. #' #' This method allows for vignettes to be hosted remotely, reducing \pkg{GGally}'s package size, and installation time. #' #' @param name Vignette name to open. If no name is provided, the vignette index will be opened #' @export #' @examples #' \donttest{ #' # View `ggnostic` vignette #' vig_ggally("ggnostic") #' #' # View all vignettes by GGally #' vig_ggally() #' } vig_ggally <- function(name) { vig_url <- if (missing(name) || is.null(name)) { "https://ggobi.github.io/ggally/articles/" } else { tryCatch({ paste0( "https://ggobi.github.io/ggally/articles/", match.arg(name, vignettes_for_ggally), ".html" ) }, error = function(e) { message("Unknown vignette: ", name, ". Opening Vignette index page") "https://ggobi.github.io/ggally/articles/" }) } browseURL(vig_url) } vignettes_for_ggally <- c( "ggally_plots", "ggally_stats", "ggbivariate", "ggcoef_model", "ggcoef", "ggduo", "ggmatrix", "ggnetworkmap", "ggnostic", "ggpairs", "ggscatmat", "ggsurv", "ggtable", "glyph" ) GGally/R/data-happy.R0000644000176200001440000000334013761572054014017 0ustar liggesusers#' Data related to happiness from the General Social Survey, 1972-2006. #' #' This data extract is taken from Hadley Wickham's \code{productplots} package. #' The original description follows, with minor edits. #' #' The data is a small sample of variables related to #' happiness from the General Social Survey (GSS). The GSS #' is a yearly cross-sectional survey of Americans, run from #' 1972. We combine data for 25 years to yield 51,020 #' observations, and of the over 5,000 variables, we select #' nine related to happiness: #' #' @details \itemize{ #' \item age. age in years: 18--89. #' \item degree. highest education: lt high school, high school, junior college, bachelor, graduate. #' \item finrela. relative financial status: far above, above average, average, below average, far below. #' \item happy. happiness: very happy, pretty happy, not too happy. #' \item health. health: excellent, good, fair, poor. #' \item marital. marital status: married, never married, divorced, widowed, separated. #' \item sex. sex: female, male. #' \item wtsall. probability weight. 0.43--6.43. #' } #' #' @docType data #' @keywords datasets #' @name happy #' @usage data(happy) #' @format A data frame with 51020 rows and 10 variables #' @references #' Smith, Tom W., Peter V. Marsden, Michael Hout, Jibum Kim. \emph{General Social Surveys, 1972-2006}. #' \[machine-readable data file\]. Principal Investigator, Tom W. Smith; Co-Principal Investigators, #' Peter V. Marsden and Michael Hout, NORC ed. #' Chicago: National Opinion Research Center, producer, 2005; #' Storrs, CT: The Roper Center for Public Opinion Research, University of Connecticut, distributor. #' 1 data file (57,061 logical records) and 1 codebook (3,422 pp). NULL GGally/R/data-flea.R0000644000176200001440000000165713663637143013620 0ustar liggesusers#' Historical data used for classification examples. #' #' This data contains physical measurements on three species of flea beetles. #' #' @details \itemize{ #' \item species Ch. concinna, Ch. heptapotamica, Ch. heikertingeri #' \item tars1 width of the first joint of the first tarsus in microns #' \item tars2 width of the second joint of the first tarsus in microns #' \item head the maximal width of the head between the external edges of the eyes in 0.01 mm #' \item aede1 the maximal width of the aedeagus in the fore-part in microns #' \item aede2 the front angle of the aedeagus (1 unit = 7.5 degrees) #' \item aede3 the aedeagus width from the side in microns #' } #' #' @docType data #' @keywords datasets #' @name flea #' @usage data(flea) #' @format A data frame with 74 rows and 7 variables #' @references #' Lubischew, A. A. (1962), On the Use of Discriminant Functions in #' Taxonomy, Biometrics 18:455-477. NULL GGally/R/gg-plots.R0000644000176200001440000015452614063456663013543 0ustar liggesusers# add global variable if (getRversion() >= "2.15.1") { utils::globalVariables(unique(c( "labelp", # cor plot c("..density..", "..scaled..", "x"), # facetdensitystrip plot c("..scaled..", "x"), #density diagonal plot c("x", "y", "lab"), # internal axis plot c("x", "y", "result", "freq"), # fluctuation plot c("weight") # ggally_summarise_by ))) } # retrieve the evaulated data column given the aes (which could possibly do operations) #' Evaluate data column #' @param data data set to evaluate the data with #' @param aes_col Single value from an \code{ggplot2::\link[ggplot2]{aes}(...)} object #' @return Aes mapping with the x and y values switched #' @export #' @examples #' mapping <- ggplot2::aes(Petal.Length) #' eval_data_col(iris, mapping$x) eval_data_col <- function(data, aes_col) { rlang::eval_tidy(aes_col, data) } #' Aes name #' @param aes_col Single value from \code{ggplot2::\link[ggplot2]{aes}(...)} #' @return character string #' @export #' @examples #' mapping <- ggplot2::aes(Petal.Length) #' mapping_string(mapping$x) mapping_string <- function(aes_col) { gsub("^~", "", deparse(aes_col, 500L)) } # is categories on the left? #' Check if plot is horizontal #' #' @param data data used in ggplot2 plot #' @param mapping ggplot2 \code{aes()} mapping #' @param val key to retrieve from \code{mapping} #' @return Boolean determining if the data is a character-like data #' @export #' @rdname is_horizontal #' @examples #' is_horizontal(iris, ggplot2::aes(Sepal.Length, Species)) # TRUE #' is_horizontal(iris, ggplot2::aes(Sepal.Length, Species), "x") # FALSE #' is_horizontal(iris, ggplot2::aes(Sepal.Length, Sepal.Width)) # FALSE is_horizontal <- function(data, mapping, val = "y") { yData <- eval_data_col(data, mapping[[val]]) is.factor(yData) || is.character(yData) || is.logical(yData) } #' @export #' @rdname is_horizontal is_character_column <- is_horizontal #' Swap x and y mapping #' @param mapping output of \code{ggplot2::\link[ggplot2]{aes}(...)} #' @return Aes mapping with the x and y values switched #' @export #' @examples #' mapping <- ggplot2::aes(Petal.Length, Sepal.Width) #' mapping #' mapping_swap_x_y(mapping) mapping_swap_x_y <- function(mapping) { tmp <- mapping$x mapping$x <- mapping$y mapping$y <- tmp mapping } #' Remove colour mapping unless found in select mapping keys #' @param mapping output of \code{ggplot2::\link[ggplot2]{aes}(...)} #' @param to set of mapping keys to check #' @return Aes mapping with colour mapping kept only if found in selected mapping keys. #' @export #' @examples #' mapping <- aes(x = sex, y = age, colour = sex) # remove_color_unless_equal(mapping, to = c("x", "y")) # remove_color_unless_equal(mapping, to = c("y")) #' #' mapping <- aes(x = sex, y = age, colour = region) #' remove_color_unless_equal(mapping) remove_color_unless_equal <- function(mapping, to = c("x", "y")) { if (!is.null(mapping$colour)) { color_str <- mapping_string(mapping$colour) for (to_val in to) { to_str <- mapping_string(mapping[[to_val]]) if (color_str == to_str) { # found! return return(mapping) } } # not found. Remove color value mapping <- mapping[names(mapping) != "colour"] } mapping } #' Scatter plot #' #' Make a scatter plot with a given data set. #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... other arguments are sent to geom_point #' @author Barret Schloerke #' @export #' @keywords hplot #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(mtcars) #' p_(ggally_points(mtcars, mapping = ggplot2::aes(x = disp, y = hp))) #' p_(ggally_points(mtcars, mapping = ggplot2::aes_string(x = "disp", y = "hp"))) #' p_(ggally_points( #' mtcars, #' mapping = ggplot2::aes_string( #' x = "disp", #' y = "hp", #' color = "as.factor(cyl)", #' size = "gear" #' ) #' )) ggally_points <- function(data, mapping, ...){ p <- ggplot(data = data, mapping = mapping) + geom_point(...) p } #' Scatter plot with a smoothed line #' #' Add a smoothed condition mean with a given scatter plot. #' #' Y limits are reduced to match original Y range with the goal of keeping the Y axis the same across plots. #' #' @param data data set using #' @param mapping aesthetics being used #' @param formula,... other arguments to add to geom_smooth #' @param method,se parameters supplied to \code{\link[ggplot2]{geom_smooth}} #' @param shrink boolean to determine if y range is reduced to range of points or points and error ribbon #' @author Barret Schloerke #' @export #' @keywords hplot #' @rdname ggally_smooth #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_smooth(tips, mapping = ggplot2::aes(x = total_bill, y = tip))) #' p_(ggally_smooth(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip"))) #' p_(ggally_smooth(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip", color = "sex"))) ggally_smooth <- function(data, mapping, ..., method = "lm", formula = y ~ x, se = TRUE, shrink = TRUE) { p <- ggplot(data = data, mapping) p <- p + geom_point(...) if (! is.null(mapping$color) || ! is.null(mapping$colour)) { p <- p + geom_smooth(method = method, se = se, formula = formula) } else { p <- p + geom_smooth(method = method, se = se, formula = formula, colour = I("black")) } if (isTRUE(shrink)) { p <- p + coord_cartesian( ylim = range(eval_data_col(data, mapping$y), na.rm = TRUE) ) } p } #' @export #' @rdname ggally_smooth ggally_smooth_loess <- function(data, mapping, ...) { ggally_smooth(data = data, mapping = mapping, ..., method = "loess") } #' @export #' @rdname ggally_smooth ggally_smooth_lm <- function(data, mapping, ...) { ggally_smooth(data = data, mapping = mapping, ..., method = "lm") } #' Bivariate density plot #' #' Make a 2D density plot from a given data. #' #' The aesthetic "fill" determines whether or not \code{stat_density2d} (filled) or \code{geom_density2d} (lines) is used. #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... parameters sent to either stat_density2d or geom_density2d #' @author Barret Schloerke #' @export #' @keywords hplot #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_density(tips, mapping = ggplot2::aes(x = total_bill, y = tip))) #' p_(ggally_density(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip"))) #' p_(ggally_density( #' tips, #' mapping = ggplot2::aes_string(x = "total_bill", y = "tip", fill = "..level..") #' )) #' p_(ggally_density( #' tips, #' mapping = ggplot2::aes_string(x = "total_bill", y = "tip", fill = "..level..") #' ) + ggplot2::scale_fill_gradient(breaks = c(0.05, 0.1, 0.15, 0.2))) ggally_density <- function(data, mapping, ...){ rangeX <- range(eval_data_col(data, mapping$x), na.rm = TRUE) rangeY <- range(eval_data_col(data, mapping$y), na.rm = TRUE) p <- ggplot(data = data) + geom_point( data = data.frame(rangeX = rangeX, rangeY = rangeY), mapping = aes(x = rangeX, y = rangeY), alpha = 0 ) if (!is.null(mapping$fill)) { p <- p + stat_density2d(mapping = mapping, geom = "polygon", ...) } else { p <- p + geom_density2d(mapping = mapping, ...) } p } #' Correlation value plot #' #' Estimate correlation from the given data. If a color variable is supplied, the correlation will also be calculated per group. #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... other arguments being supplied to \code{\link[ggplot2]{geom_text}()} for the title and groups #' @param stars logical value which determines if the significance stars should be displayed. Given the \code{\link[stats]{cor.test}} p-values, display \describe{ #' \item{\code{"***"}}{if the p-value is \verb{< 0.001}} #' \item{\code{"**"}}{if the p-value is \verb{< 0.01}} #' \item{\code{"*"}}{if the p-value is \verb{< 0.05}} #' \item{\code{"."}}{if the p-value is \verb{< 0.10}} #' \item{\code{""}}{otherwise} #' } #' @param method \code{method} supplied to cor function #' @param use \code{use} supplied to \code{\link[stats]{cor}} function #' @param display_grid if \code{TRUE}, display aligned panel grid lines. If \code{FALSE} (default), display a thin panel border. #' @param digits number of digits to be displayed after the decimal point. See \code{\link[base]{formatC}} for how numbers are calculated. #' @param title_args arguments being supplied to the title's \code{\link[ggplot2]{geom_text}()} #' @param group_args arguments being supplied to the split-by-color group's \code{\link[ggplot2]{geom_text}()} #' @param justify_labels \code{justify} argument supplied when \code{\link[base]{format}}ting the labels #' @param align_percent relative align position of the text. When \code{justify_labels = 0.5}, this should not be needed to be set. #' @param alignPercent,displayGrid deprecated. Please use their snake-case counterparts. #' @param title title text to be displayed #' @author Barret Schloerke #' @importFrom stats complete.cases cor #' @seealso \code{\link{ggally_statistic}}, \code{\link{ggally_cor_v1_5}} #' @export #' @keywords hplot #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_cor(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "tip"))) #' # display with grid #' p_(ggally_cor( #' tips, #' mapping = ggplot2::aes_string(x = "total_bill", y = "tip"), #' display_grid = TRUE #' )) #' # change text attributes #' p_(ggally_cor( #' tips, #' mapping = ggplot2::aes(x = total_bill, y = tip), #' size = 15, #' colour = I("red"), #' title = "Correlation" #' )) #' # split by a variable #' p_(ggally_cor( #' tips, #' mapping = ggplot2::aes_string(x = "total_bill", y = "tip", color = "sex"), #' size = 5 #' )) ggally_cor <- function( data, mapping, ..., stars = TRUE, method = "pearson", use = "complete.obs", display_grid = FALSE, digits = 3, title_args = list(...), group_args = list(...), justify_labels = "right", align_percent = 0.5, title = "Corr", alignPercent = warning("deprecated. Use `align_percent`"), displayGrid = warning("deprecated. Use `display_grid`") ) { if (!missing(alignPercent)) { warning("`alignPercent` is deprecated. Please use `align_percent` if alignment still needs to be adjusted") align_percent <- alignPercent } if (!missing(displayGrid)) { warning("`displayGrid` is deprecated. Please use `display_grid`") display_grid <- displayGrid } na.rm <- if (missing(use)) { # display warnings NA } else { (use %in% c("complete.obs", "pairwise.complete.obs", "na.or.complete")) } ggally_statistic( data = data, mapping = mapping, na.rm = na.rm, align_percent = align_percent, display_grid = display_grid, title_args = title_args, group_args = group_args, justify_labels = justify_labels, justify_text = "left", sep = if ("colour" %in% names(mapping)) ": " else ":\n", title = title, text_fn = function(x, y) { if (is_date(x)) { x <- as.numeric(x) } if (is_date(y)) { y <- as.numeric(y) } corObj <- stats::cor.test(x, y, method = method, use = use) # make sure all values have X-many decimal places cor_est <- as.numeric(corObj$estimate) cor_txt <- formatC(cor_est, digits = digits, format = "f") # if stars should be added if (isTRUE(stars)) { cor_txt <- str_c( cor_txt, signif_stars(corObj$p.value) ) } cor_txt } ) } #' Generalized text display #' #' @param data data set using #' @param mapping aesthetics being used #' @param title title text to be displayed #' @param text_fn function that takes in \code{x} and \code{y} and returns a text string #' @param na.rm logical value which determines if \code{NA} values are removed. If \code{TRUE}, no warning message will be displayed. #' @param display_grid if \code{TRUE}, display aligned panel grid lines. If \code{FALSE} (default), display a thin panel border. #' @param justify_labels \code{justify} argument supplied when \code{\link[base]{format}}ting the labels #' @param justify_text \code{justify} argument supplied when \code{\link[base]{format}}ting the returned \code{text_fn(x, y)} values #' @param sep separation value to be placed between the labels and text #' @param family font family used when displaying all text. This value will be set in \code{title_args} or \code{group_args} if no \code{family} value exists. By using \code{"mono"}, groups will align with each other. #' @param title_args arguments being supplied to the title's \code{\link[ggplot2]{geom_text}()} #' @param group_args arguments being supplied to the split-by-color group's \code{\link[ggplot2]{geom_text}()} #' @param align_percent relative align position of the text. When \code{title_hjust = 0.5} and \code{group_hjust = 0.5}, this should not be needed to be set. #' @param title_hjust,group_hjust \code{hjust} sent to \code{\link[ggplot2]{geom_text}()} for the title and group values respectively. Any \code{hjust} value supplied in \code{title_args} or \code{group_args} will take precedence. #' @seealso \code{\link{ggally_cor}} #' @export ggally_statistic <- function( data, mapping, text_fn, title, na.rm = NA, display_grid = FALSE, justify_labels = "right", justify_text = "left", sep = ": ", family = "mono", title_args = list(), group_args = list(), align_percent = 0.5, title_hjust = 0.5, group_hjust = 0.5 ) { set_if_not_there <- function(obj, key, value) { obj <- as.list(obj) #if (! "family" %in% rlang::names2(obj)) { # obj$family <- family #} obj } #title_args <- set_if_not_there(title_args, "family", family) #group_args <- set_if_not_there(group_args, "family", family) title_args <- set_if_not_there(title_args, "hjust", title_hjust) group_args <- set_if_not_there(group_args, "hjust", group_hjust) xData <- eval_data_col(data, mapping$x) yData <- eval_data_col(data, mapping$y) colorData <- eval_data_col(data, mapping$colour) if (is.numeric(colorData)) { stop("`mapping` color column must be categorical, not numeric") } display_na_rm <- is.na(na.rm) if (display_na_rm) { na.rm <- TRUE } if (isTRUE(na.rm)) { if (!is.null(colorData) && (length(colorData) == length(xData))) { rows <- complete.cases(xData, yData, colorData) } else { rows <- complete.cases(xData, yData) } if (any(!rows)) { if (!is.null(colorData) && (length(colorData) == length(xData))) { colorData <- colorData[rows] } xData <- xData[rows] yData <- yData[rows] if (isTRUE(display_na_rm)) { total <- sum(!rows) if (total > 1) { warning("Removed ", total, " rows containing missing values") } else if (total == 1) { warning("Removing 1 row that contained a missing value") } } } } xVal <- xData yVal <- yData # if the mapping has to deal with the data, remove it ### IDK what this does. inherited from old code. for (mappingName in names(mapping)) { itemData <- eval_data_col(data, mapping[[mappingName]]) if (!inherits(itemData, "AsIs")) { mapping[[mappingName]] <- NULL } } ### END IDK # calculate variable ranges so the gridlines line up xValNum <- as.numeric(xVal) yValNum <- as.numeric(yVal) xmin <- min(xValNum, na.rm = TRUE) xmax <- max(xValNum, na.rm = TRUE) xrange <- c(xmin - 0.01 * (xmax - xmin), xmax + 0.01 * (xmax - xmin)) ymin <- min(yValNum, na.rm = TRUE) ymax <- max(yValNum, na.rm = TRUE) yrange <- c(ymin - 0.01 * (ymax - ymin), ymax + 0.01 * (ymax - ymin)) # if there is a color grouping... if ( !is.null(colorData) && !inherits(colorData, "AsIs") ) { cord <- ddply( data.frame(x = xData, y = yData, color = colorData), "color", function(dt) { text_fn(dt$x, dt$y) } ) colnames(cord)[2] <- "text" # put in correct order lev <- levels(as.factor(colorData)) ord <- rep(-1, nrow(cord)) for (i in 1:nrow(cord)) { for (j in seq_along(lev)){ if (identical(as.character(cord$color[i]), as.character(lev[j]))) { ord[i] <- j } } } cord <- cord[order(ord[ord >= 0]), ] # make labels align together cord$label <- str_c( format(cord$color, justify = justify_labels), sep, format(cord$text, justify = justify_text) ) # title ggally_text_args <- append( list( label = str_c(title, sep, text_fn(xVal, yVal)), mapping = mapping, xP = 0.5, yP = 0.9, xrange = xrange, yrange = yrange ), title_args ) p <- do.call(ggally_text, ggally_text_args) xPos <- rep(align_percent, nrow(cord)) * diff(xrange) + min(xrange, na.rm = TRUE) yPos <- seq( from = 0.9, to = 0.2, length.out = nrow(cord) + 1) yPos <- yPos * diff(yrange) + min(yrange, na.rm = TRUE) yPos <- yPos[-1] cordf <- data.frame(xPos = xPos, yPos = yPos, labelp = cord$label) cordf$labelp <- factor(cordf$labelp, levels = cordf$labelp) # group text values geom_text_args <- append( list( data = cordf, aes( x = xPos, y = yPos, label = labelp, color = labelp ) ), group_args ) p <- p + do.call(geom_text, geom_text_args) } else { ggally_text_args <- append( list( label = paste0(title, sep, text_fn(xVal, yVal), collapse = ""), mapping, xP = 0.5, yP = 0.5, xrange = xrange, yrange = yrange ), title_args ) p <- do.call(ggally_text, ggally_text_args) } if (!isTRUE(display_grid)) { p <- p + theme( panel.grid.major = element_blank(), panel.grid.minor = element_blank(), panel.border = element_rect( linetype = "solid", color = theme_get()$panel.background$fill, fill = "transparent" ) ) } p + theme(legend.position = "none") } #' Box plot #' #' Make a box plot with a given data set. \code{ggally_box_no_facet} will be a single panel plot, while \code{ggally_box} will be a faceted plot #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... other arguments being supplied to geom_boxplot #' @author Barret Schloerke #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_box(tips, mapping = ggplot2::aes(x = total_bill, y = sex))) #' p_(ggally_box(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "sex"))) #' p_(ggally_box( #' tips, #' mapping = ggplot2::aes_string(y = "total_bill", x = "sex", color = "sex"), #' outlier.colour = "red", #' outlier.shape = 13, #' outlier.size = 8 #' )) ggally_box <- function(data, mapping, ...){ mapping <- mapping_color_to_fill(mapping) ggally_dot_and_box(data, mapping, ..., boxPlot = TRUE) } #' @export #' @rdname ggally_box ggally_box_no_facet <- function(data, mapping, ...) { mapping <- mapping_color_to_fill(mapping) ggally_dot_and_box_no_facet(data, mapping, ..., boxPlot = TRUE) } #' Grouped dot plot #' #' Add jittering with the box plot. \code{ggally_dot_no_facet} will be a single panel plot, while \code{ggally_dot} will be a faceted plot #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... other arguments being supplied to geom_jitter #' @author Barret Schloerke #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_dot(tips, mapping = ggplot2::aes(x = total_bill, y = sex))) #' p_(ggally_dot(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "sex"))) #' p_(ggally_dot( #' tips, #' mapping = ggplot2::aes_string(y = "total_bill", x = "sex", color = "sex") #' )) #' p_(ggally_dot( #' tips, #' mapping = ggplot2::aes_string(y = "total_bill", x = "sex", color = "sex", shape = "sex") #' ) + ggplot2::scale_shape(solid=FALSE)) ggally_dot <- function(data, mapping, ...){ ggally_dot_and_box(data, mapping, ..., boxPlot = FALSE) } #' @export #' @rdname ggally_dot ggally_dot_no_facet <- function(data, mapping, ...) { ggally_dot_and_box_no_facet(data, mapping, ..., boxPlot = FALSE) } #' Box and dot plot #' #' Place box plots or dot plots on the graph #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... parameters passed to either geom_jitter or geom_boxplot #' @param boxPlot boolean to decide to plot either box plots (TRUE) or dot plots (FALSE) #' @author Barret Schloerke #' @keywords internal #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_dot_and_box( #' tips, #' mapping = ggplot2::aes(x = total_bill, y = sex, color = sex), #' boxPlot = TRUE #' )) #' p_(ggally_dot_and_box( #' tips, #' mapping = ggplot2::aes(x = total_bill, y = sex, color = sex), #' boxPlot = FALSE #' )) ggally_dot_and_box <- function(data, mapping, ..., boxPlot = TRUE){ horizontal <- is_horizontal(data, mapping) if (horizontal) { mapping <- mapping_swap_x_y(mapping) } xVal <- mapping_string(mapping$x) p <- ggplot(data = data) if (boxPlot) { p <- p + geom_boxplot(mapping, ...) } else { p <- p + geom_jitter(mapping, ...) } if (!horizontal) { p <- p + facet_grid(paste(". ~ ", xVal, sep = ""), scales = "free_x") + theme(panel.spacing = unit(0.1, "lines")) } else { p <- p + coord_flip() + facet_grid(paste(xVal, " ~ .", sep = ""), scales = "free_y") + theme(panel.spacing = unit(0.1, "lines")) } p } ggally_dot_and_box_no_facet <- function(data, mapping, ..., boxPlot = TRUE) { horizontal <- is_horizontal(data, mapping) if (horizontal) { mapping <- mapping_swap_x_y(mapping) } p <- ggplot(data = data) if (boxPlot) { p <- p + geom_boxplot(mapping, ...) } else { p <- p + geom_jitter(mapping, ...) } if (horizontal) { p <- p + scale_x_discrete( limits = rev(levels(eval_data_col(data, mapping$x))) ) + coord_flip() } p } #' Faceted histogram #' #' Display subsetted histograms of the data in different panels. #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... parameters sent to stat_bin() #' @author Barret Schloerke #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_facethist(tips, mapping = ggplot2::aes(x = tip, y = sex))) #' p_(ggally_facethist(tips, mapping = ggplot2::aes_string(x = "tip", y = "sex"), binwidth = 0.1)) ggally_facethist <- function(data, mapping, ...){ mapping <- mapping_color_to_fill(mapping) horizontal <- is_horizontal(data, mapping) if (!horizontal) { mapping <- mapping_swap_x_y(mapping) } xVal <- mapping_string(mapping$x) yVal <- mapping_string(mapping$y) mapping$y <- NULL p <- ggplot(data = data, mapping) p <- p + stat_bin(...) if (horizontal) { p <- p + facet_grid(paste(yVal, " ~ .", sep = "")) + theme(panel.spacing = unit(0.1, "lines")) } else { p <- p + facet_grid(paste(". ~", yVal, sep = "")) + theme(panel.spacing = unit(0.1, "lines")) + coord_flip() } p <- p + labs(x = xVal, y = yVal) p } #' Faceted density plot #' #' Make density plots by displaying subsets of the data in different panels. #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... other arguments being sent to stat_density #' @author Barret Schloerke #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_facetdensity(tips, mapping = ggplot2::aes(x = total_bill, y = sex))) #' p_(ggally_facetdensity( #' tips, #' mapping = ggplot2::aes_string(y = "total_bill", x = "sex", color = "sex") #' )) ggally_facetdensity <- function(data, mapping, ...){ ggally_facetdensitystrip(data, mapping, ..., den_strip = FALSE) } #' Tile plot with facets #' #' Displays a Tile Plot as densely as possible. #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... other arguments being sent to stat_bin #' @author Barret Schloerke #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_denstrip(tips, mapping = ggplot2::aes(x = total_bill, y = sex))) #' p_(ggally_denstrip(tips, mapping = ggplot2::aes_string(x = "total_bill", y = "sex"))) #' p_(ggally_denstrip( #' tips, #' mapping = ggplot2::aes_string(x = "sex", y = "tip", binwidth = "0.2") #' ) + ggplot2::scale_fill_gradient(low = "grey80", high = "black")) ggally_denstrip <- function(data, mapping, ...){ mapping <- mapping_color_to_fill(mapping) ggally_facetdensitystrip(data, mapping, ..., den_strip = TRUE) } #' Density or tiles plot with facets #' #' Make tile plot or density plot as compact as possible. #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... other arguments being sent to either geom_histogram or stat_density #' @param den_strip boolean to decide whether or not to plot a density strip(TRUE) or a facet density(FALSE) plot. #' @author Barret Schloerke #' @keywords hplot #' @export #' @examples #' example(ggally_facetdensity) #' example(ggally_denstrip) ggally_facetdensitystrip <- function(data, mapping, ..., den_strip = FALSE){ horizontal <- is_horizontal(data, mapping) if (!horizontal) { mapping <- mapping_swap_x_y(mapping) } xVal <- mapping_string(mapping$x) yVal <- mapping_string(mapping$y) mappingY <- mapping$y # nolint mapping$y <- NULL # will be faceted p <- ggplot(data = data, mapping) + labs(x = xVal, y = yVal) if (identical(den_strip, TRUE)) { p <- p + geom_histogram( mapping = aes(fill = ..density..), # nolint position = "fill", ... ) + scale_y_continuous( breaks = c(0.5), labels = "1" ) } else { p <- p + stat_density( aes( y = ..scaled.. * diff(range(x, na.rm = TRUE)) + min(x, na.rm = TRUE) # nolint ), position = "identity", geom = "line", ... ) } if (horizontal) { p <- p + facet_grid(paste(yVal, " ~ .", sep = "")) if (identical(den_strip, TRUE)) { p <- p + theme(axis.text.y = element_blank()) } } else { p <- p + coord_flip() p <- p + facet_grid(paste(". ~ ", yVal, sep = "")) if (identical(den_strip, TRUE)) { p <- p + theme(axis.text.x = element_blank()) } } p } #' Univariate density plot #' #' Displays a density plot for the diagonal of a \code{\link{ggpairs}} plot matrix. #' #' @param data data set using #' @param mapping aesthetics being used. #' @param ... other arguments sent to stat_density #' @param rescale boolean to decide whether or not to rescale the count output #' @author Barret Schloerke #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_densityDiag(tips, mapping = ggplot2::aes(x = total_bill))) #' p_(ggally_densityDiag(tips, mapping = ggplot2::aes(x = total_bill, color = day))) ggally_densityDiag <- function(data, mapping, ..., rescale = FALSE){ mapping <- mapping_color_to_fill(mapping) p <- ggplot(data, mapping) + scale_y_continuous() if (identical(rescale, TRUE)) { p <- p + stat_density( aes( y = ..scaled.. * diff(range(x, na.rm = TRUE)) + min(x, na.rm = TRUE) # nolint ), position = "identity", geom = "line", ... ) } else { p <- p + geom_density(...) } p } #' Bar plot #' #' Displays a bar plot for the diagonal of a \code{\link{ggpairs}} plot matrix. #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... other arguments are sent to geom_bar #' @param rescale boolean to decide whether or not to rescale the count output. Only applies to numeric data #' @author Barret Schloerke #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_barDiag(tips, mapping = ggplot2::aes(x = day))) #' p_(ggally_barDiag(tips, mapping = ggplot2::aes(x = tip), binwidth = 0.25)) ggally_barDiag <- function(data, mapping, ..., rescale = FALSE){ mapping <- mapping_color_to_fill(mapping) mapping$y <- NULL x_data <- eval_data_col(data, mapping$x) numer <- ("continuous" == plotting_data_type(x_data)) p <- ggplot(data = data, mapping) if (is_date(x_data)) { p <- p + geom_histogram(...) #TODO make y axis lines match date positions # buildInfo <- ggplot_build(p + geom_bar(...)) # histBarPerc <- buildInfo$data[[1]]$ncount } else if (numer) { if (identical(rescale, TRUE)) { p <- p + geom_histogram( aes( y = ..density.. / max(..density..) * diff(range(x, na.rm = TRUE)) + min(x, na.rm = TRUE) # nolint ), ... ) + coord_cartesian(ylim = range(eval_data_col(data, mapping$x), na.rm = TRUE)) } else { p <- p + geom_histogram(...) } } else { p <- p + geom_bar(...) } p } #' Text plot #' #' Plot text for a plot. #' #' @param label text that you want to appear #' @param mapping aesthetics that don't relate to position (such as color) #' @param xP horizontal position percentage #' @param yP vertical position percentage #' @param xrange range of the data around it. Only nice to have if plotting in a matrix #' @param yrange range of the data around it. Only nice to have if plotting in a matrix #' @param ... other arguments for geom_text #' @author Barret Schloerke #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' p_(ggally_text("Example 1")) #' p_(ggally_text("Example\nTwo", mapping = ggplot2::aes(size = 15), color = I("red"))) ggally_text <- function( label, mapping = ggplot2::aes(color = "black"), xP = 0.5, yP = 0.5, xrange = c(0, 1), yrange = c(0, 1), ... ){ theme <- theme_get() p <- ggplot() + xlim(xrange) + ylim(yrange) + theme( panel.grid.minor = element_blank(), panel.grid.major = element_line( colour = ifnull(theme$panel.background$fill, NA) ), panel.background = element_rect( fill = ifnull(theme$panel.grid.major$colour, NA) ) ) + labs(x = NULL, y = NULL) new_mapping <- aes_string( x = xP * diff(xrange) + min(xrange, na.rm = TRUE), y = yP * diff(yrange) + min(yrange, na.rm = TRUE) ) if (is.null(mapping)) { mapping <- new_mapping } else { mapping <- add_and_overwrite_aes(mapping, new_mapping) } # dont mess with color if it's already there if (!is.null(mapping$colour)) { p <- p + geom_text( label = label, mapping = mapping, ...) + guides(colour = "none") } else if ("colour" %in% names(aes(...))) { p <- p + geom_text( label = label, mapping = mapping, ...) } else { bg <- ifnull(theme$panel.background$fill, "grey92") fg <- ifnull(theme$axis.text$colour, "gray30") colour <- scales::colour_ramp(c(bg, fg))(0.75) p <- p + geom_text( label = label, mapping = mapping, colour = colour, ...) } p <- p + theme(legend.position = "none") p } #' Get x axis labels #' #' Retrieves x axis labels from the plot object directly. #' #' @importFrom gtable gtable_filter #' @param p plot object #' @param xRange range of x values #' @keywords internal get_x_axis_labels <- function(p, xRange) { pGrob <- ggplotGrob(p) axisTable <- gtable_filter(pGrob, "axis-b")$grobs[[1]]$children$axis # have to do a function as filter doesn't work get_raw_grob_by_name <- function(g, name) { for (item in g$grobs) { if (str_detect(item$name, name) ) { return(item$children[[1]]) } } NULL } name <- if (packageVersion("ggplot2") >= 3.3) { "title" } else { "axis.text.x" } xAxisGrob <- get_raw_grob_by_name(axisTable, name) axisBreaks <- as.numeric(xAxisGrob$label) axisLabs <- rbind( expand.grid(xPos = axisBreaks[1], yPos = axisBreaks), expand.grid(xPos = axisBreaks, yPos = axisBreaks[1]) )[-1, ] axisLabs <- as.data.frame(axisLabs) axisLabs$lab <- as.character(apply(axisLabs, 1, max)) axisLabs$hjust <- 0.5 axisLabs$vjust <- 0.5 minPos <- xRange[1] maxPos <- xRange[2] for (i in seq_len(nrow(axisLabs))) { xPos <- axisLabs[i, "xPos"] yPos <- axisLabs[i, "yPos"] if (yPos < minPos) { axisLabs[i, "yPos"] <- minPos axisLabs[i, "vjust"] <- 0 } else if (yPos > maxPos) { axisLabs[i, "yPos"] <- maxPos axisLabs[i, "vjust"] <- 1 } if (xPos < minPos) { axisLabs[i, "xPos"] <- minPos axisLabs[i, "hjust"] <- 0 } else if (xPos > maxPos) { axisLabs[i, "xPos"] <- maxPos axisLabs[i, "hjust"] <- 1 } } axisLabs } #' Internal axis labels for ggpairs #' #' This function is used when \code{axisLabels == "internal"}. #' #' @param data dataset being plotted #' @param mapping aesthetics being used (x is the variable the plot will be made for) #' @param label title to be displayed in the middle. Defaults to \code{mapping$x} #' @param labelSize size of variable label #' @param labelXPercent percent of horizontal range #' @param labelYPercent percent of vertical range #' @param labelHJust hjust supplied to label #' @param labelVJust vjust supplied to label #' @param gridLabelSize size of grid labels #' @param ... other arguments for geom_text #' @author Jason Crowley and Barret Schloerke #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_diagAxis(tips, ggplot2::aes(x=tip))) #' p_(ggally_diagAxis(tips, ggplot2::aes(x=sex))) ggally_diagAxis <- function( data, mapping, label = mapping$x, labelSize = 5, labelXPercent = 0.5, labelYPercent = 0.55, labelHJust = 0.5, labelVJust = 0.5, gridLabelSize = 4, ... ) { if (is.null(mapping$x)) { stop("mapping$x is null. There must be a column value in this location.") } mapping$y <- NULL numer <- ! is_horizontal(data, mapping, "x") if (! is.character(label)) { label <- mapping_string(mapping$x) } xData <- eval_data_col(data, mapping$x) if (numer) { xmin <- min(xData, na.rm = TRUE) xmax <- max(xData, na.rm = TRUE) # add a lil fluff... it looks better xrange <- c(xmin - .01 * (xmax - xmin), xmax + .01 * (xmax - xmin)) # xrange <- c(xmin, xmax) p <- ggally_text( label = label, mapping = aes(col = "grey50"), xrange = xrange, yrange = xrange, size = labelSize, xP = labelXPercent, yP = labelYPercent, hjust = labelHJust, vjust = labelVJust ) axisBreaks <- get_x_axis_labels(p, xrange) # print(axisBreaks) p <- p + geom_text( data = axisBreaks, mapping = aes_string( x = "xPos", y = "yPos", label = "lab", hjust = "hjust", vjust = "vjust" ), col = "grey50", size = gridLabelSize ) } else { breakLabels <- levels(as.factor(xData)) numLvls <- length(breakLabels) p <- ggally_text( label = label, mapping = aes(col = "grey50"), xrange = c(0, 1), yrange = c(0, 1), size = labelSize, yP = labelYPercent, xP = labelXPercent, hjust = labelHJust, vjust = labelVJust ) #axisBreaks <- (1+2*0:(numLvls-1))/(2*numLvls) axisBreaks <- 0:(numLvls - 1) * (0.125 + (1 - 0.125 * (numLvls - 1)) / numLvls) + (1 - 0.125 * (numLvls - 1)) / (2 * numLvls) axisLabs <- data.frame( x = axisBreaks[1:numLvls], y = axisBreaks[numLvls:1], lab = breakLabels ) p <- p + geom_text( data = axisLabs, mapping = aes( x = x, y = y, label = lab ), col = "grey50", size = gridLabelSize ) # hack to remove warning message... cuz it doesn't listen to suppress messages p$scales$scales[[1]]$breaks <- axisBreaks p$scales$scales[[2]]$breaks <- axisBreaks # pLabs <- pLabs + # scale_x_continuous(breaks=axisBreaks,limits=c(0,1)) + # scale_y_continuous(breaks=axisBreaks,limits=c(0,1)) } p } #' Faceted bar plot #' #' X variables are plotted using \code{geom_bar} and are faceted by the Y variable. #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... other arguments are sent to geom_bar #' @author Barret Schloerke #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_facetbar(tips, ggplot2::aes(x = sex, y = smoker, fill = time))) #' p_(ggally_facetbar(tips, ggplot2::aes(x = smoker, y = sex, fill = time))) ggally_facetbar <- function(data, mapping, ...){ mapping <- mapping_color_to_fill(mapping) # numer <- is.null(attributes(data[,as.character(mapping$x)])$class) # xVal <- mapping$x yVal <- mapping_string(mapping$y) mapping$y <- NULL p <- ggplot(data, mapping) + geom_bar(...) + facet_grid(paste(yVal, " ~ .", sep = "")) p } #' Mosaic plot #' #' Plots the mosaic plot by using fluctuation. #' #' @param data data set using #' @param mapping aesthetics being used. Only x and y will used and both are required #' @param ... passed to \code{\link[ggplot2]{geom_tile}(...)} #' @param floor don't display cells smaller than this value #' @param ceiling max value to scale frequencies. If any frequency is larger than the ceiling, the fill color is displayed darker than other rectangles #' @author Barret Schloerke #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_ratio(tips, ggplot2::aes(sex, day))) #' p_(ggally_ratio(tips, ggplot2::aes(sex, day)) + ggplot2::coord_equal()) #' # only plot tiles greater or equal to 20 and scale to a max of 50 #' p_(ggally_ratio( #' tips, ggplot2::aes(sex, day), #' floor = 20, ceiling = 50 #' ) + ggplot2::theme(aspect.ratio = 4/2)) ggally_ratio <- function( data, mapping = do.call(ggplot2::aes_string, as.list(colnames(data)[1:2])), ..., floor = 0, ceiling = NULL ) { # capture the original names xName <- mapping_string(mapping$x) yName <- mapping_string(mapping$y) countData <- plyr::count(data, vars = c(xName, yName)) # overwrite names so name clashes don't happen colnames(countData)[1:2] <- c("x", "y") xNames <- levels(countData[["x"]]) yNames <- levels(countData[["y"]]) countData <- subset(countData, freq >= floor) if (is.null(ceiling)) { ceiling <- max(countData$freq) } countData[["freqSize"]] <- sqrt(pmin(countData[["freq"]], ceiling) / ceiling) countData[["col"]] <- ifelse(countData[["freq"]] > ceiling, "grey30", "grey50") countData[["xPos"]] <- as.numeric(countData[["x"]]) + (1 / 2) * countData[["freqSize"]] countData[["yPos"]] <- as.numeric(countData[["y"]]) + (1 / 2) * countData[["freqSize"]] p <- ggplot( data = countData, mapping = aes_string( x = "xPos", y = "yPos", height = "freqSize", width = "freqSize", fill = "col" ) ) + geom_tile(...) + scale_fill_identity() + scale_x_continuous( name = xName, limits = c(0.9999, length(xNames) + 1), breaks = 1:(length(xNames) + 1), labels = c(xNames, ""), minor_breaks = FALSE ) + scale_y_continuous( name = yName, limits = c(0.9999, length(yNames) + 1), breaks = 1:(length(yNames) + 1), labels = c(yNames, ""), minor_breaks = FALSE ) + theme( axis.text.x = element_text( hjust = 0, vjust = 1, colour = "grey50" ), axis.text.y = element_text( hjust = 0, vjust = 0, angle = 90, colour = "grey50" ) ) p } #' Display counts of observations #' #' Plot the number of observations by using rectangles #' with proportional areas. #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... other arguments passed to \code{\link[ggplot2]{geom_tile}(...)} #' @details #' You can adjust the size of rectangles with the \code{x.width} argument. #' @author Joseph Larmarange #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_count(tips, mapping = ggplot2::aes(x = smoker, y = sex))) #' p_(ggally_count(tips, mapping = ggplot2::aes(x = smoker, y = sex, fill = day))) #' #' p_(ggally_count( #' as.data.frame(Titanic), #' mapping = ggplot2::aes(x = Class, y = Survived, weight = Freq) #' )) #' p_(ggally_count( #' as.data.frame(Titanic), #' mapping = ggplot2::aes(x = Class, y = Survived, weight = Freq), #' x.width = 0.5 #' )) ggally_count <- function(data, mapping, ...) { mapping <- mapping_color_to_fill(mapping) if (is.null(mapping$x)) stop("'x' aesthetic is required.") if (is.null(mapping$y)) stop("'y' aesthetic is required.") # for stat_ggally_count(), y should be mapped to base_y # and always be a factor count_col <- ".ggally_y" data[[count_col]] <- as.factor(eval_data_col(data, mapping$y)) ylabel <- mapping_string(mapping$y) mapping$base_y <- aes_string(base_y = count_col)$base_y mapping$y <- NULL # default values args <- list(...) if (!"fill" %in% names(args) & is.null(mapping$fill)) { args$fill <- GeomRect$default_aes$fill } ggplot(data, mapping) + do.call(stat_ggally_count, args) + scale_y_continuous( breaks = 1:length(levels(data[[count_col]])), labels = levels(data[[count_col]]) ) + theme(panel.grid.minor = element_blank()) + ylab(ylabel) } #' @export #' @rdname ggally_count #' @format NULL #' @usage NULL #' @export # na.rm = TRUE to remove warnings if NA (cf. stat_count) # x.width to control size of tiles stat_ggally_count <- function(mapping = NULL, data = NULL, geom = "tile", position = "identity", ..., x.width = .9, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) { params <- list( x.width = x.width, na.rm = na.rm, ... ) if (!is.null(params$y)) { stop("stat_ggally_count() must not be used with a y aesthetic, but with a base_y aesthetic instead.", call. = FALSE) } layer( data = data, mapping = mapping, stat = StatGGallyCount, geom = geom, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = params ) } #' @rdname ggally_count #' @format NULL #' @usage NULL #' @export StatGGallyCount <- ggproto("StatGGallyCount", Stat, required_aes = c("x", "base_y"), default_aes = aes( weight = 1, width = after_stat(width), height = after_stat(height), y = after_stat(y) ), setup_params = function(data, params) { params }, extra_params = c("na.rm"), compute_panel = function(self, data, scales, x.width = NULL) { if (is.null(data$weight)) data$weight <- rep(1, nrow(data)) if(is.null(x.width)) x.width <- .9 # sum weights for each combination of aesthetics # the use of . allows to consider all aesthetics defined in data panel <- stats::aggregate(weight ~ ., data = data, sum, na.rm = TRUE) names(panel)[which(names(panel) == "weight")] <- "n" # compute proportions by x and y f <- function(n) {sum(abs(n), na.rm = TRUE)} panel$n_xy <- stats::ave(panel$n, panel$x, panel$base_y, FUN = f) panel$prop <- panel$n / panel$n_xy panel$width <- sqrt(panel$n_xy) / max(sqrt(panel$n_xy)) * x.width panel$height <- panel$width * panel$prop panel$cum_height <- stats::ave(panel$height, panel$x, panel$base_y, FUN = cumsum) panel$y <- as.numeric(panel$base_y) + panel$cum_height - panel$height / 2 - panel$width / 2 panel } ) #' @rdname ggally_count #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' p_(ggally_countDiag(tips, mapping = ggplot2::aes(x = smoker))) #' p_(ggally_countDiag(tips, mapping = ggplot2::aes(x = smoker, fill = sex))) ggally_countDiag <- function(data, mapping, ...) { mapping$y <- mapping$x ggally_count(data = data, mapping = mapping, ...) } #' Blank plot #' #' Draws nothing. #' #' Makes a "blank" ggplot object that will only draw white space #' #' @author Barret Schloerke #' @param ... other arguments ignored #' @seealso [ggplot2::element_blank()] #' @export #' @keywords hplot ggally_blank <- function(...){ aes(...) # ignored a <- data.frame(X = 1:2, Y = 1:2) p <- ggplot(data = a, aes_string(x = "X", y = "Y")) + geom_point( colour = "transparent") + theme( axis.line = element_blank(), axis.text.x = element_blank(), axis.text.y = element_blank(), axis.ticks = element_blank(), axis.title.x = element_blank(), axis.title.y = element_blank(), legend.background = element_blank(), legend.key = element_blank(), legend.text = element_blank(), legend.title = element_blank(), panel.background = element_blank(), panel.border = element_blank(), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), plot.background = element_blank(), plot.title = element_blank(), strip.background = element_blank(), strip.text.x = element_blank(), strip.text.y = element_blank() ) class(p) <- c(class(p), "ggmatrix_blank") p } #' @rdname ggally_blank #' @export ggally_blankDiag <- function(...) { ggally_blank(...) } #' NA plot #' #' Draws a large \code{NA} in the middle of the plotting area. This plot is useful when all X or Y data is \code{NA} #' #' @author Barret Schloerke #' @param data ignored #' @param mapping ignored #' @param size size of the geom_text 'NA' #' @param color color of the geom_text 'NA' #' @param ... other arguments sent to geom_text #' @export #' @keywords hplot ggally_na <- function(data = NULL, mapping = NULL, size = 10, color = "grey20", ...) { a <- data.frame(x = 1, y = 1, label = "NA") p <- ggplot(data = a, aes_string(x = "x", y = "y", label = "label")) + geom_text(color = color, size = size, ...) + theme( axis.line = element_blank(), axis.text.x = element_blank(), axis.text.y = element_blank(), axis.ticks = element_blank(), axis.title.x = element_blank(), axis.title.y = element_blank(), legend.background = element_blank(), legend.key = element_blank(), legend.text = element_blank(), legend.title = element_blank(), panel.background = element_blank(), panel.border = element_blank(), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), plot.background = element_blank(), plot.title = element_blank(), strip.background = element_blank(), strip.text.x = element_blank(), strip.text.y = element_blank() ) p } #' @rdname ggally_na #' @export ggally_naDiag <- function(...) { ggally_na(...) } #' Scatterplot for continuous and categorical variables #' #' Make scatterplots compatible with both continuous and categorical variables #' using \code{\link[ggforce]{geom_autopoint}} from package \pkg{ggforce}. #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... other arguments passed to \code{\link[ggforce]{geom_autopoint}(...)} #' @author Joseph Larmarange #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' p_(ggally_autopoint(tips, mapping = aes(x = tip, y = total_bill))) #' p_(ggally_autopoint(tips, mapping = aes(x = tip, y = sex))) #' p_(ggally_autopoint(tips, mapping = aes(x = smoker, y = sex))) #' p_(ggally_autopoint(tips, mapping = aes(x = smoker, y = sex, color = day))) #' p_(ggally_autopoint(tips, mapping = aes(x = smoker, y = sex), size = 8)) #' p_(ggally_autopoint(tips, mapping = aes(x = smoker, y = sex), alpha = .9)) #' #' p_(ggpairs( #' tips, #' mapping = aes(colour = sex), #' upper = list(discrete = "autopoint", combo = "autopoint", continuous = "autopoint"), #' diag = list(discrete = "autopointDiag", continuous = "autopointDiag") #' )) ggally_autopoint <- function(data, mapping, ...) { require_namespaces("ggforce") args <- list(...) if (!"alpha" %in% names(args) & is.null(mapping$alpha)) args$alpha <- .5 # mapping needs to be sent directly to geom_autopoint args$mapping <- mapping ggplot(data, mapping) + do.call(ggforce::geom_autopoint, args) } #' @rdname ggally_autopoint #' @export ggally_autopointDiag <- function(data, mapping, ...) { mapping$y <- mapping$x ggally_autopoint(data = data, mapping = mapping, ...) } #' Summarize a continuous variable by each value of a discrete variable #' #' Display summary statistics of a continuous variable for each value of a discrete variable. #' #' @param data data set using #' @param mapping aesthetics being used #' @param text_fn function that takes an x and weights and returns a text string #' @param text_fn_vertical function that takes an x and weights and returns a text string, used when \code{x} is discrete and \code{y} is continuous. If not provided, will use \code{text_fn}, replacing spaces by carriage returns. #' @param ... other arguments passed to \code{\link[ggplot2]{geom_text}(...)} #' @author Joseph Larmarange #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' if (require(Hmisc)) { #' data(tips, package = "reshape") #' p_(ggally_summarise_by(tips, mapping = aes(x = total_bill, y = day))) #' p_(ggally_summarise_by(tips, mapping = aes(x = day, y = total_bill))) #' #' # colour is kept only if equal to the discrete variable #' p_(ggally_summarise_by(tips, mapping = aes(x = total_bill, y = day, color = day))) #' p_(ggally_summarise_by(tips, mapping = aes(x = total_bill, y = day, color = sex))) #' p_(ggally_summarise_by(tips, mapping = aes(x = day, y = total_bill, color = day))) #' #' # custom text size #' p_(ggally_summarise_by(tips, mapping = aes(x = total_bill, y = day), size = 6)) #' #' # change statistic to display #' p_(ggally_summarise_by(tips, mapping = aes(x = total_bill, y = day), text_fn = weighted_mean_sd)) #' #' # custom stat function #' weighted_sum <- function(x, weights = NULL) { #' if (is.null(weights)) weights <- 1 #' paste0("Total : ", round(sum(x * weights, na.rm = TRUE), digits = 1)) #' } #' p_(ggally_summarise_by(tips, mapping = aes(x = total_bill, y = day), text_fn = weighted_sum)) #' } ggally_summarise_by <- function( data, mapping, text_fn = weighted_median_iqr, text_fn_vertical = NULL, ...) { if (is.null(mapping$x)) stop("'x' aesthetic is required.") if (is.null(mapping$y)) stop("'y' aesthetic is required.") horizontal <- is_horizontal(data, mapping) if(horizontal) { res <- ddply( data.frame( x = eval_data_col(data, mapping$x), y = eval_data_col(data, mapping$y), weight = eval_data_col(data, mapping$weight) %||% 1, stringsAsFactors = FALSE ), c('y'), plyr::here(summarize), label = text_fn(x, weight) ) # keep colour if matching the discrete variable if (mapping_string(mapping$colour) == mapping_string(mapping$y)) col <- "y" else col <- NULL ggplot(res) + aes_string(y = "y", x = "1", label = "label", colour = col) + geom_text(...) + xlab("") + ylab(mapping_string(mapping$y)) + #theme_minimal() + theme( panel.grid.major = element_blank(), panel.grid.minor = element_blank(), axis.ticks.x = element_blank(), axis.text.x = element_blank(), legend.position = "none", panel.background = element_blank(), panel.border = element_rect( linetype = "solid", color = theme_get()$panel.background$fill, fill = "transparent" ) ) } else { if (is.null(text_fn_vertical)) { text_fn_vertical <- function(x, weights) { gsub(" ", "\n", text_fn(x, weights)) } } ggally_summarise_by(data, mapping_swap_x_y(mapping), text_fn_vertical, ...) + coord_flip() + theme( axis.ticks.y = element_blank(), axis.text.y = element_blank(), axis.text.x = theme_get()$axis.text, axis.ticks.x = theme_get()$axis.ticks ) + theme(axis.text.x = element_text(size = 9)) } } #' @rdname ggally_summarise_by #' @param x a numeric vector #' @param weights an optional numeric vectors of weights. If \code{NULL}, equal weights of 1 will be taken into account. #' @details #' \code{weighted_median_iqr} computes weighted median and interquartile range. #' @export weighted_median_iqr <- function(x, weights = NULL) { require_namespaces("Hmisc") s <- round(Hmisc::wtd.quantile(x, weights = weights, probs = c(.25, .5, .75), na.rm = TRUE), digits = 1) paste0("Median: ", s[2], " [", s[1], "-", s[3], "]") } #' @rdname ggally_summarise_by #' @details #' \code{weighted_mean_sd} computes weighted mean and standard deviation. #' @export weighted_mean_sd <- function(x, weights = NULL) { require_namespaces("Hmisc") m <- round(Hmisc::wtd.mean(x, weights = weights, na.rm = TRUE), digits = 1) sd <- round(sqrt(Hmisc::wtd.var(x, weights = weights, na.rm = TRUE)), digits = 1) paste0("Mean: ", m, " (", sd, ")") } GGally/R/ggmatrix_print.R0000644000176200001440000000323113665760216015026 0ustar liggesusers ggplot2_set_last_plot <- utils::getFromNamespace("set_last_plot", "ggplot2") #' Print \code{\link{ggmatrix}} object #' #' Print method taken from \code{ggplot2:::print.ggplot} and altered for a \code{\link{ggmatrix}} object #' #' @param x plot to display #' @param newpage draw new (empty) page first? #' @param vp viewport to draw plot in #' @param ... arguments passed onto \code{\link{ggmatrix_gtable}} #' @method print ggmatrix #' @author Barret Schloerke #' @import utils #' @importFrom grid grid.newpage grid.draw seekViewport pushViewport upViewport #' @export #' @examples #' data(tips, package = "reshape") #' pMat <- ggpairs(tips, c(1,3,2), mapping = ggplot2::aes_string(color = "sex")) #' pMat # calls print(pMat), which calls print.ggmatrix(pMat) print.ggmatrix <- function (x, newpage = is.null(vp), vp = NULL, ...) { if (newpage) { grid.newpage() } grDevices::recordGraphics(requireNamespace("GGally", quietly = TRUE), list(), getNamespace("GGally")) gtable <- ggmatrix_gtable(x, ...) # must be done after gtable, as gtable calls many ggplot2::print.ggplot methods ggplot2_set_last_plot(x) if (is.null(vp)) { grid.draw(gtable) } else { if (is.character(vp)) { seekViewport(vp) } else { pushViewport(vp) } grid.draw(gtable) upViewport() } invisible(data) } #' Is Blank Plot? #' Find out if the plot equals a blank plot #' #' @keywords internal #' @examples #' GGally:::is_blank_plot(ggally_blank()) #' GGally:::is_blank_plot(ggally_points(mtcars, ggplot2::aes_string(x = "disp", y = "hp"))) #' is_blank_plot <- function(p){ is.null(p) || identical(p, "blank") || inherits(p, "ggmatrix_blank") } GGally/R/ggnostic.R0000644000176200001440000006673213764714663013631 0ustar liggesusers# cooksd # on all predicted values # important to prediction # sigma # how much of a problem it is to the model ## .hat: Diagonal of the hat matrix ## .sigma: Estimate of residual standard deviation when corresponding observation is dropped from model ## .fitted: Fitted values of model ## .cooksd: Cooks distance, 'cooks.distance' ## .se.fit: Standard errors of fitted values ## .resid: Residuals ## .std.resid: Standardized residuals (Some unusual "lm" objects, such as "rlm" from MASS, may omit '.cooksd' and '.std.resid'. "gam" from mgcv omits '.sigma') #' Broomify a model #' #' broom::augment a model and add broom::glance and broom::tidy output as attributes. X and Y variables are also added. #' #' @param model model to be sent to [broom::augment()], [broom::glance()], and [broom::tidy()] #' @param lmStars boolean that determines if stars are added to labels #' @return broom::augmented data frame with the broom::glance data.frame and broom::tidy data.frame as 'broom_glance' and 'broom_tidy' attributes respectively. \code{var_x} and \code{var_y} variables are also added as attributes #' @export #' @examples #' data(mtcars) #' model <- stats::lm(mpg ~ wt + qsec + am, data = mtcars) #' broomified_model <- broomify(model) #' str(broomified_model) broomify <- function(model, lmStars = TRUE) { if (inherits(model, "broomify")) { return(model) } require_namespaces("broom") broom_glance_info <- broom::glance(model) broom_tidy_coef <- broom::tidy(model) broom_augment_rows <- broom::augment(model, se_fit = TRUE) attr(broom_augment_rows, "broom_glance") <- broom_glance_info attr(broom_augment_rows, "broom_tidy") <- broom_tidy_coef attr(broom_augment_rows, "var_x") <- model_beta_variables(data = broom_augment_rows) attr(broom_augment_rows, "var_y") <- model_response_variables(data = broom_augment_rows) attr(broom_augment_rows, "var_x_label") <- model_beta_label( model, data = broom_augment_rows, lmStars ) class(broom_augment_rows) <- c(class(broom_augment_rows), "broomify") return(broom_augment_rows) } model_variables <- function(model, data = broom::augment(model)) { augment_names <- names(data) augment_names <- augment_names[!grepl("^\\.", augment_names)] } #' Model term names #' #' Retrieve either the response variable names, the beta variable names, or beta variable names. If the model is an object of class 'lm', by default, the beta variable names will include anova significance stars. #' #' @param model model in question #' @param data equivalent to \code{broom::augment(model)} #' @param lmStars boolean that determines if stars are added to labels #' @return character vector of names #' @rdname model_terms #' @export #' @importFrom stats terms model_response_variables <- function(model, data = broom::augment(model)) { model_variables(model = model, data = data)[1] } #' @rdname model_terms #' @export model_beta_variables <- function(model, data = broom::augment(model)) { model_variables(model = model, data = data)[-1] } #' @importFrom stats symnum beta_stars <- function(p_val) { unclass(symnum( p_val, corr = FALSE, na = FALSE, cutpoints = c(0, 0.001, 0.01, 0.05, 0.1, 1), symbols = c("***", "**", "*", ".", " ") )) } #' @export #' @rdname model_terms #' @importFrom stats anova model_beta_label <- function(model, data = broom::augment(model), lmStars = TRUE) { beta_vars <- model_beta_variables(model, data = data) if ( (! identical(class(model), "lm")) || (!isTRUE(lmStars))) { return(beta_vars) } # for lm models only tidy_anova <- broom::tidy(anova(model)) tidy_anova <- tidy_anova[tidy_anova$term %in% beta_vars, ] p_vals <- tidy_anova$p.value names(p_vals) <- tidy_anova$term p_vals <- p_vals[beta_vars] x_labs <- paste(beta_vars, beta_stars(p_vals), sep = "") gsub("\\s+$", "", x_labs) } broom_columns <- function() { c(".fitted", ".se.fit", ".resid", ".hat", ".sigma", ".cooksd", ".std.resid") } #' RColorBrewer Set1 colors #' #' @param col standard color name used to retrieve hex color value #' @import RColorBrewer #' @export brew_colors <- function(col) { brew_cols <- RColorBrewer::brewer.pal(n = 9, "Set1") names(brew_cols) <- c( "red", "blue", "green", "purple", "orange", "yellow", "brown", "pink", "grey" ) brew_cols <- as.list(brew_cols) ret <- brew_cols[[col]] if (is.null(ret)) { stop(paste("color '", col, "' not found in: c(", paste(names(brew_cols), collapse = ", "), ")", sep = "")) } ret } #' \code{\link{ggnostic}} background line with geom #' #' If a non-null \code{linePosition} value is given, a line will be drawn before the given \code{continuous_geom} or \code{combo_geom} is added to the plot. #' #' Functions with a color in their name have different default color behavior. #' #' @param data,mapping supplied directly to [ggplot2::ggplot()] #' @param ... parameters supplied to \code{continuous_geom} or \code{combo_geom} #' @param linePosition,lineColor,lineSize,lineAlpha,lineType parameters supplied to #' [ggplot2::geom_line()] #' @param continuous_geom \pkg{ggplot2} geom that is executed after the line is (possibly) #' added and if the x data is continuous #' @param combo_geom \pkg{ggplot2} geom that is executed after the line is (possibly) added and #' if the x data is discrete #' @param mapColorToFill boolean to determine if combo plots should cut the color mapping to the fill mapping #' @return \pkg{ggplot2} plot object #' @rdname ggally_nostic_line ggally_nostic_line <- function( data, mapping, ..., linePosition = NULL, lineColor = "red", lineSize = 0.5, lineAlpha = 1, lineType = 1, continuous_geom = ggplot2::geom_point, combo_geom = ggplot2::geom_boxplot, mapColorToFill = TRUE ) { x_is_character <- is_character_column(data, mapping, "x") if (x_is_character & isTRUE(mapColorToFill)) { mapping <- mapping_color_to_fill(mapping) } p <- ggplot(data = data, mapping = mapping) if (!is.null(linePosition)) { p <- p + geom_hline( yintercept = linePosition, color = lineColor, size = lineSize, alpha = lineAlpha, linetype = lineType ) } if (x_is_character) { p <- p + combo_geom(...) } else { p <- p + continuous_geom(...) } p } #' \code{\link{ggnostic}} residuals #' #' If non-null \code{pVal} and \code{sigma} values are given, confidence interval lines will be added to the plot at the specified \code{pVal} percentiles of a N(0, sigma) distribution. #' #' @param data,mapping,... parameters supplied to \code{\link{ggally_nostic_line}} #' @param linePosition,lineColor,lineSize,lineAlpha,lineType parameters supplied to #' [ggplot2::geom_line()] #' @param lineConfColor,lineConfSize,lineConfAlpha,lineConfType parameters supplied to the #' confidence interval lines #' @param pVal percentiles of a N(0, sigma) distribution to be drawn #' @param sigma sigma value for the \code{pVal} percentiles #' @param se boolean to determine if the confidence intervals should be displayed #' @param method,formula parameters supplied to [ggplot2::geom_smooth()]. #' Defaults to \code{"auto"} and \code{"y ~ x"} #' @return \pkg{ggplot2} plot object #' @seealso \code{stats::\link[stats]{residuals}} #' @export #' @importFrom stats qnorm #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' dt <- broomify(stats::lm(mpg ~ wt + qsec + am, data = mtcars)) #' p_(ggally_nostic_resid(dt, ggplot2::aes(wt, .resid))) ggally_nostic_resid <- function( data, mapping, ..., linePosition = 0, lineColor = brew_colors("grey"), lineSize = 0.5, lineAlpha = 1, lineType = 1, lineConfColor = brew_colors("grey"), lineConfSize = lineSize, lineConfAlpha = lineAlpha, lineConfType = 2, pVal = c(0.025, 0.975), sigma = attr(data, "broom_glance")$sigma, se = TRUE, method = "auto", formula = y ~ x ) { if (!is.null(linePosition) & !is.null(pVal) & !is.null(sigma)) { scaled_sigmas <- qnorm(pVal, lower.tail = TRUE, sd = sigma) linePosition <- c(linePosition, linePosition + scaled_sigmas) lineColor <- c(lineColor, lineConfColor, lineConfColor) lineType <- c(lineType, lineConfType, lineConfType) lineSize <- c(lineSize, lineConfSize, lineConfSize) lineAlpha <- c(lineAlpha, lineConfAlpha, lineConfAlpha) } p <- ggally_nostic_line( data, mapping, ..., linePosition = linePosition, lineColor = lineColor, lineType = lineType, lineSize = lineSize, lineAlpha = lineAlpha ) if (! is_character_column(data, mapping, "x")) { p <- p + geom_smooth(se = se, method = method, formula = formula) } p + coord_cartesian( ylim = range( c(linePosition, eval_data_col(data, mapping$y)), na.rm = TRUE ) ) } #' \code{\link{ggnostic}} standardized residuals #' #' If non-null \code{pVal} and \code{sigma} values are given, confidence interval lines will be added to the plot at the specified \code{pVal} locations of a N(0, 1) distribution. #' #' @param data,mapping,... parameters supplied to \code{\link{ggally_nostic_resid}} #' @param sigma sigma value for the \code{pVal} percentiles. Set to 1 for standardized residuals #' @seealso [stats::rstandard()] #' @return \pkg{ggplot2} plot object #' @rdname ggally_nostic_std_resid #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' dt <- broomify(stats::lm(mpg ~ wt + qsec + am, data = mtcars)) #' p_(ggally_nostic_std_resid(dt, ggplot2::aes(wt, .std.resid))) ggally_nostic_std_resid <- function( data, mapping, ..., sigma = 1 ) { ggally_nostic_resid( data, mapping, ..., sigma = sigma ) } #' \code{\link{ggnostic}} fitted value's standard error #' #' A function to display \code{stats::\link[stats]{predict}}'s standard errors #' #' @details #' As stated in \code{stats::\link[stats]{predict}} documentation: #' #' If the logical 'se.fit' is 'TRUE', standard errors of the predictions are calculated. If the numeric argument 'scale' is set (with optional ''df'), it is used as the residual standard deviation in the computation of the standard errors, otherwise this is extracted from the model fit. #' #' Since the se.fit is \code{TRUE} and scale is unset by default, the standard errors are extracted from the model fit. #' #' A base line of 0 is added to give reference to a perfect fit. #' #' @param data,mapping,...,lineColor parameters supplied to \code{\link{ggally_nostic_line}} #' @param linePosition base comparison for a perfect fit #' @seealso [stats::influence()] #' @return \pkg{ggplot2} plot object #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' dt <- broomify(stats::lm(mpg ~ wt + qsec + am, data = mtcars)) #' p_(ggally_nostic_se_fit(dt, ggplot2::aes(wt, .se.fit))) ggally_nostic_se_fit <- function( data, mapping, ..., lineColor = brew_colors("grey"), linePosition = NULL ) { ggally_nostic_line( data, mapping, ..., lineColor = lineColor, linePosition = linePosition ) } #' \code{\link{ggnostic}} leave one out model sigma #' #' A function to display [stats::influence()]'s sigma value. #' #' @details #' As stated in [stats::influence()] documentation: #' #' sigma: a vector whose i-th element contains the estimate of the residual standard deviation obtained when the i-th case is dropped from the regression. (The approximations needed for GLMs can result in this being 'NaN'.) #' #' A line is added to display the overall model's sigma value. This gives a baseline for comparison #' #' @param data,mapping,...,lineColor parameters supplied to \code{\link{ggally_nostic_line}} #' @param linePosition line that is drawn in the background of the plot. Defaults to the overall model's sigma value. #' @seealso [stats::influence()] #' @return \pkg{ggplot2} plot object #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' dt <- broomify(stats::lm(mpg ~ wt + qsec + am, data = mtcars)) #' p_(ggally_nostic_sigma(dt, ggplot2::aes(wt, .sigma))) ggally_nostic_sigma <- function( data, mapping, ..., lineColor = brew_colors("grey"), linePosition = attr(data, "broom_glance")$sigma ) { ggally_nostic_line( data, mapping, ..., lineColor = lineColor, linePosition = linePosition ) } #' \code{\link{ggnostic}} Cook's distance #' #' A function to display [stats::cooks.distance()]. #' #' @details #' A line is added at F_{p, n - p}(0.5) to display the general cutoff point for Cook's Distance. #' #' Reference: Michael H. Kutner, Christopher J. Nachtsheim, John Neter, and William Li. Applied linear statistical models. The McGraw-Hill / Irwin series operations and decision sciences. McGraw-Hill Irwin, 2005, p. 403 #' #' @param data,mapping,...,lineColor,lineType parameters supplied to \code{\link{ggally_nostic_line}} #' @param linePosition 4 / n is the general cutoff point for Cook's Distance #' @seealso [stats::cooks.distance()] #' @return \pkg{ggplot2} plot object #' @rdname ggally_nostic_cooksd #' @export #' @importFrom stats pf #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' dt <- broomify(stats::lm(mpg ~ wt + qsec + am, data = mtcars)) #' p_(ggally_nostic_cooksd(dt, ggplot2::aes(wt, .cooksd))) ggally_nostic_cooksd <- function( data, mapping, ..., linePosition = pf(0.5, length(attr(data, "var_x")), nrow(data) - length(attr(data, "var_x"))), lineColor = brew_colors("grey"), lineType = 2 ) { ggally_nostic_line( data, mapping, ..., linePosition = linePosition, lineColor = lineColor, lineType = lineType ) } #' \code{\link{ggnostic}} leverage points #' #' A function to display stats::influence's hat information against a given explanatory variable. #' #' @details #' As stated in [stats::influence()] documentation: #' #' hat: a vector containing the diagonal of the 'hat' matrix. #' #' The diagonal elements of the 'hat' matrix describe the influence each response value has on the fitted value for that same observation. #' #' A suggested "cutoff" line is added to the plot at a height of 2 * p / n and an expected line at a height of p / n. #' If either \code{linePosition} or \code{avgLinePosition} is \code{NULL}, the respective line will not be drawn. #' #' @param data,mapping,... supplied directly to \code{\link{ggally_nostic_line}} #' @param linePosition,lineColor,lineSize,lineAlpha,lineType parameters supplied to #' [ggplot2::geom_line()] for the cutoff line #' @param avgLinePosition,avgLineColor,avgLineSize,avgLineAlpha,avgLineType parameters supplied #' to [ggplot2::geom_line()] for the average line #' @seealso [stats::influence()] #' @return \pkg{ggplot2} plot object #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' dt <- broomify(stats::lm(mpg ~ wt + qsec + am, data = mtcars)) #' p_(ggally_nostic_hat(dt, ggplot2::aes(wt, .hat))) ggally_nostic_hat <- function( data, mapping, ..., linePosition = 2 * sum(eval_data_col(data, mapping$y)) / nrow(data), lineColor = brew_colors("grey"), lineSize = 0.5, lineAlpha = 1, lineType = 2, avgLinePosition = sum(eval_data_col(data, mapping$y)) / nrow(data), avgLineColor = brew_colors("grey"), avgLineSize = lineSize, avgLineAlpha = lineAlpha, avgLineType = 1 ) { if (is.null(linePosition)) { lineColor <- lineSize <- lineAlpha <- lineType <- NULL } if (is.null(avgLinePosition)) { avgLineColor <- avgLineSize <- avgLineAlpha <- avgLineType <- NULL } ggally_nostic_line( data, mapping, ..., linePosition = c(linePosition, avgLinePosition), lineColor = c(lineColor, avgLineColor), lineSize = c(lineSize, avgLineSize), lineType = c(lineType, avgLineType), lineAlpha = c(lineAlpha, avgLineAlpha) ) } #' Function switch #' #' Function that allows you to call different functions based upon an aesthetic variable value. #' #' @param types list of functions that follow the \code{\link{ggmatrix}} function standard: \code{function(data, mapping, ...){ #make ggplot2 object }}. One key should be a 'default' key for a default switch case. #' @param mapping_val mapping value to switch on. Defaults to the 'y' variable of the aesthetics list. #' @export #' @examples #' ggnostic_continuous_fn <- fn_switch(list( #' default = ggally_points, #' .fitted = ggally_points, #' .se.fit = ggally_nostic_se_fit, #' .resid = ggally_nostic_resid, #' .hat = ggally_nostic_hat, #' .sigma = ggally_nostic_sigma, #' .cooksd = ggally_nostic_cooksd, #' .std.resid = ggally_nostic_std_resid #' )) #' #' ggnostic_combo_fn <- fn_switch(list( #' default = ggally_box_no_facet, #' fitted = ggally_box_no_facet, #' .se.fit = ggally_nostic_se_fit, #' .resid = ggally_nostic_resid, #' .hat = ggally_nostic_hat, #' .sigma = ggally_nostic_sigma, #' .cooksd = ggally_nostic_cooksd, #' .std.resid = ggally_nostic_std_resid #' )) fn_switch <- function( types, mapping_val = "y" ) { function(data, mapping, ...) { var <- mapping_string(mapping[[mapping_val]]) fn <- ifnull(types[[var]], types[["default"]]) if (is.null(fn)) { stop(str_c( "function could not be found for ", mapping_val, " or 'default'. ", "Please include one of these two keys as a function." )) } fn(data = data, mapping = mapping, ...) } } check_and_set_nostic_types <- function( types, default, .fitted, .resid, .std.resid, # nolint .sigma, .se.fit, # nolint .hat, .cooksd ) { types_names <- names(types) set_type_value <- function(name, value) { if (is.null(types[[name]])) { # value is not set if (! (name %in% types_names)) { # set suggested fn types[[name]] <<- value } else { # does not plot displayed types[[name]] <<- ggally_blank } } } set_type_value("default", default) set_type_value(".fitted", .fitted) set_type_value(".resid", .resid) set_type_value(".std.resid", .std.resid) # nolint set_type_value(".sigma", .sigma) set_type_value(".se.fit", .se.fit) # nolint set_type_value(".hat", .hat) set_type_value(".cooksd", .cooksd) types } #' Plot matrix of statistical model diagnostics #' #' #' @section `columnsY`: #' [broom::augment()] collects data from the supplied model and returns a data.frame with the following columns (taken directly from broom documentation). These columns are the only allowed values in the \code{columnsY} parameter to \code{\link{ggnostic}}. #' #' \describe{ #' \item{.resid}{Residuals} #' \item{.hat}{Diagonal of the hat matrix} #' \item{.sigma}{Estimate of residual standard deviation when #' corresponding observation is dropped from model} #' \item{.cooksd}{Cooks distance, [stats::cooks.distance()]} #' \item{.fitted}{Fitted values of model} #' \item{.se.fit}{Standard errors of fitted values} #' \item{.std.resid}{Standardized residuals} #' \item{response variable name}{The response variable in the model may be added. Such as \code{"mpg"} in the model \code{lm(mpg ~ ., data = mtcars)}} #' } #' #' @section `continuous`, `combo`, `discrete` types: #' Similar to \code{\link{ggduo}} and \code{\link{ggpairs}}, functions may be supplied to display the different column types. However, since the Y rows are fixed, each row has it's own corresponding function in each of the plot types: continuous, combo, and discrete. Each plot type list can have keys that correspond to the [broom::augment()] output: \code{".fitted"}, \code{".resid"}, \code{".std.resid"}, \code{".sigma"}, \code{".se.fit"}, \code{".hat"}, \code{".cooksd"}. An extra key, \code{"default"}, is used to plot the response variables of the model if they are included. Having a function for each diagnostic allows for very fine control over the diagnostics plot matrix. The functions for each type list are wrapped into a switch function that calls the function corresponding to the y variable being plotted. These switch functions are then passed directly to the \code{types} parameter in \code{\link{ggduo}}. #' #' @param model statistical model object such as output from \code{stats::\link[stats]{lm}} or \code{stats::\link[stats]{glm}} #' @param ... arguments passed directly to \code{\link{ggduo}} #' @param columnsX columns to be displayed in the plot matrix. Defaults to the predictor columns of the \code{model} #' @param columnsY rows to be displayed in the plot matrix. Defaults to residuals, leave one out sigma value, diagonal of the hat matrix, and Cook's Distance. The possible values are the response variables in the model and the added columns provided by [broom::augment()]. See details for more information. #' @param columnLabelsX,columnLabelsY column and row labels to display in the plot matrix #' @param xlab,ylab,title plot matrix labels passed directly to \code{\link{ggmatrix}} #' @param continuous,combo,discrete list of functions for each y variable. See details for more information. #' @template ggmatrix-progress #' @param data data defaults to a 'broomify'ed model object. This object will contain information about the X variables, Y variables, and multiple broom outputs. See \code{\link{broomify}(model)} for more information #' @export #' @examples #' # small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' data(mtcars) #' #' # use mtcars dataset and alter the 'am' column to display actual name values #' mtc <- mtcars #' mtc$am <- c("0" = "automatic", "1" = "manual")[as.character(mtc$am)] #' #' # step the complete model down to a smaller model #' mod <- stats::step(stats::lm(mpg ~ ., data = mtc), trace = FALSE) #' #' # display using defaults #' pm <- ggnostic(mod) #' p_(pm) #' #' # color by am value #' pm <- ggnostic(mod, mapping = ggplot2::aes(color = am)) #' p_(pm) #' #' # turn resid smooth error ribbon off #' pm <- ggnostic(mod, continuous = list(.resid = wrap("nostic_resid", se = FALSE))) #' p_(pm) #' #' #' ## plot residuals vs fitted in a ggpairs plot matrix #' dt <- broomify(mod) #' pm <- ggpairs( #' dt, c(".fitted", ".resid"), #' columnLabels = c("fitted", "residuals"), #' lower = list(continuous = ggally_nostic_resid) #' ) #' p_(pm) ggnostic <- function( model, ..., columnsX = attr(data, "var_x"), # columnsY = c(".fitted", ".se.fit", ".resid", ".std.resid", ".sigma", ".hat", ".cooksd"), columnsY = c(".resid", ".sigma", ".hat", ".cooksd"), columnLabelsX = attr(data, "var_x_label"), columnLabelsY = gsub("\\.", " ", gsub("^\\.", "", columnsY)), xlab = "explanatory variables", ylab = "diagnostics", title = paste(deparse(model$call, width.cutoff = 500L), collapse = "\n"), continuous = list( default = ggally_points, .fitted = ggally_points, .se.fit = ggally_nostic_se_fit, .resid = ggally_nostic_resid, .hat = ggally_nostic_hat, .sigma = ggally_nostic_sigma, .cooksd = ggally_nostic_cooksd, .std.resid = ggally_nostic_std_resid ), combo = list( default = ggally_box_no_facet, fitted = ggally_box_no_facet, .se.fit = ggally_nostic_se_fit, .resid = ggally_nostic_resid, .hat = ggally_nostic_hat, .sigma = ggally_nostic_sigma, .cooksd = ggally_nostic_cooksd, .std.resid = ggally_nostic_std_resid ), discrete = list( default = ggally_ratio, .fitted = ggally_ratio, .se.fit = ggally_ratio, .resid = ggally_ratio, .hat = ggally_ratio, .sigma = ggally_ratio, .cooksd = ggally_ratio, .std.resid = ggally_ratio ), progress = NULL, data = broomify(model) ) { continuous_types <- check_and_set_nostic_types( continuous, default = ggally_nostic_line, .fitted = ggally_nostic_line, .se.fit = ggally_nostic_se_fit, .resid = ggally_nostic_resid, .hat = ggally_nostic_hat, .sigma = ggally_nostic_sigma, .cooksd = ggally_nostic_cooksd, .std.resid = ggally_nostic_std_resid ) combo_types <- check_and_set_nostic_types( combo, default = ggally_nostic_line, .fitted = ggally_nostic_line, .se.fit = ggally_nostic_se_fit, .resid = ggally_nostic_resid, .hat = ggally_nostic_hat, .sigma = ggally_nostic_sigma, .cooksd = ggally_nostic_cooksd, .std.resid = ggally_nostic_std_resid ) discrete_types <- check_and_set_nostic_types( discrete, default = ggally_ratio, .fitted = ggally_ratio, .se.fit = ggally_ratio, .resid = ggally_ratio, .hat = ggally_ratio, .sigma = ggally_ratio, .cooksd = ggally_ratio, .std.resid = ggally_ratio ) continuous_fn <- fn_switch(continuous_types, "y") combo_fn <- fn_switch(combo_types, "y") discrete_fn <- fn_switch(discrete_types, "y") columnsX <- match_nostic_columns(columnsX, attr(data, "var_x"), "columnsX") columnsY <- match_nostic_columns( columnsY, c(attr(data, "var_y"), broom_columns()), "columnsY" ) ggduo( data = data, columnsX = columnsX, columnsY = columnsY, columnLabelsX = columnLabelsX, columnLabelsY = columnLabelsY, types = list( continuous = continuous_fn, comboVertical = combo_fn, comboHorizontal = combo_fn, discrete = discrete_fn ), ..., progress = progress, title = title, xlab = xlab, ylab = ylab ) } # https://github.com/ggobi/ggobi/blob/master/data/pigs.xml #' Multiple time series #' #' GGally implementation of ts.plot. Wraps around the ggduo function and removes the column strips #' @param ... supplied directly to \code{\link{ggduo}} #' @param columnLabelsX remove top strips for the X axis by default #' @param xlab defaults to "time" #' @return \code{\link{ggmatrix}} object #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' p_(ggts(pigs, "time", c("gilts", "profit", "s_per_herdsz", "production", "herdsz"))) ggts <- function( ..., columnLabelsX = NULL, xlab = "time" ) { pm <- ggduo( ..., # remove the "time" strip columnLabelsX = columnLabelsX, xlab = xlab ) pm } # if (!is.null(group)) { # column_type <- unlist(lapply( # data[setdiff(names(data), broom_columns())], # plotting_data_type # )) # is_discrete <- column_type[column_type == "discrete"] # group_names <- names(is_discrete) # } else { # group_names <- deparse(mapping$group) # } # # line_mapping <- mapping # line_mapping[c("x", "y", "xend", "yend")] <- # aes_string(x = "xmin", y = "ymin", xend = "xmax", yend = "ymax") # # color_group <- c(group_names) # # if (!is.null(mapping$colour)) { # # set the colors to the mapping colors # color_group[length(color_group) + 1] <- deparse(mapping$colour) # } else { # # set the default color to the line color # line_mapping$colour <- I(lineColor) # } # # color_group <- unique(color_group) # # print(line_mapping) # print(color_group) # hline_data <- ddply( # data, color_group, # function(subsetDt) { # ret <- data.frame( # ymax = mean(subsetDt[[deparse(mapping$y)]], na.rm = TRUE), # ymin = mean(subsetDt[[deparse(mapping$y)]], na.rm = TRUE), # xmin = min(subsetDt[[deparse(mapping$x)]], na.rm = TRUE), # xmax = max(subsetDt[[deparse(mapping$x)]], na.rm = TRUE) # ) # # transfer the unique columns that need to be there # for (col in color_group) { # ret[[col]] <- unique(subsetDt[[col]]) # } # ret # } # ) match_nostic_columns <- function(columns, choices, name) { column_matches <- pmatch(columns, choices, nomatch = NA, duplicates.ok = TRUE) if (any(is.na(column_matches))) { stop(paste( "Could not match '", name, "': c(", paste("'", columns[is.na(column_matches)], "'", collapse = ", ", sep = ""), ") to choices: c(", paste("'", choices, "'", collapse = ", ", sep = ""), ")", sep = "" )) } columns <- choices[column_matches] columns } GGally/R/stat_weighted_mean.R0000644000176200001440000001756013764714663015642 0ustar liggesusers#' Compute weighted y mean #' #' This statistic will compute the mean of \strong{y} aesthetic for #' each unique value of \strong{x}, taking into account \strong{weight} #' aesthetic if provided. #' #' @section Computed variables: #' \describe{ #' \item{y}{weighted y (numerator / denominator)} #' \item{numerator}{numerator} #' \item{denominator}{denominator} #' } #' #' @inheritParams ggplot2::stat_bin #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' #' p_(ggplot(tips) + #' aes(x = day, y = total_bill) + #' geom_point()) #' #' p_(ggplot(tips) + #' aes(x = day, y = total_bill) + #' stat_weighted_mean()) #' #' p_(ggplot(tips) + #' aes(x = day, y = total_bill, group = 1) + #' stat_weighted_mean(geom = "line")) #' #' p_(ggplot(tips) + #' aes(x = day, y = total_bill, colour = sex, group = sex) + #' stat_weighted_mean(geom = "line")) #' #' p_(ggplot(tips) + #' aes(x = day, y = total_bill, fill = sex) + #' stat_weighted_mean(geom = "bar", position = "dodge")) #' #' # computing a proportion on the fly #' p_(ggplot(tips) + #' aes(x = day, y = as.integer(smoker == "Yes"), fill = sex) + #' stat_weighted_mean(geom = "bar", position = "dodge") + #' scale_y_continuous(labels = scales::percent)) #' #' # taking into account some weights #' d <- as.data.frame(Titanic) #' p_(ggplot(d) + #' aes(x = Class, y = as.integer(Survived == "Yes"), weight = Freq, fill = Sex) + #' geom_bar(stat = "weighted_mean", position = "dodge") + #' scale_y_continuous(labels = scales::percent) + #' labs(y = "Survived")) #' #' #' \dontrun{ #' cuse <- read.table("https://data.princeton.edu/wws509/datasets/cuse.dat", header = TRUE) #' cuse$n <- cuse$notUsing + cuse$using #' cuse$prop <- cuse$using / cuse$n #' #' ggplot(cuse) + #' aes(x = education, y = prop, weight = n) + #' stat_weighted_mean() #' #' ggplot(cuse) + #' aes(x = age, y = prop, weight = n, color = education) + #' stat_weighted_mean() #' #' ggplot(cuse) + #' aes(x = education, y = prop, weight = n) + #' stat_weighted_mean(geom = "bar") #' #' # add percentages above each bar #' ggplot(cuse) + #' aes(x = age, y = prop, weight = n, fill = education) + #' stat_weighted_mean(geom = "bar") + #' geom_text(aes(label = scales::percent(after_stat(y))), stat = "weighted_mean", vjust = 0) + #' facet_grid(~ education) #' } stat_weighted_mean <- function( mapping = NULL, data = NULL, geom = "point", position = "identity", ..., na.rm = FALSE, orientation = NA, show.legend = NA, inherit.aes = TRUE ) { layer( data = data, mapping = mapping, stat = StatWeightedMean, geom = geom, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list( na.rm = na.rm, orientation = orientation, ... ) ) } #' @rdname stat_weighted_mean #' @format NULL #' @usage NULL #' @export StatWeightedMean <- ggproto( "StatSummary", Stat, required_aes = c("x", "y"), extra_params = c("na.rm", "orientation"), setup_params = function(data, params) { params$flipped_aes <- has_flipped_aes(data, params) params }, compute_panel = function(data, scales, na.rm = FALSE, flipped_aes = FALSE) { data <- ggplot2::flip_data(data, flipped_aes) if (is.null(data$weight)) data$weight <- rep(1, nrow(data)) summarised <- aggregate( cbind(numerator = y * weight, denominator = weight) ~ ., data, FUN = sum, na.rm = TRUE ) summarised$y <- summarised$numerator / summarised$denominator summarised$flipped_aes <- flipped_aes ggplot2::flip_data(summarised, flipped_aes) } ) #' Trends line plot #' #' Plot trends using line plots. #' For continuous y variables, plot the evolution of the mean. #' For binary y variables, plot the evolution of the proportion. #' #' @param data data set using #' @param mapping aesthetics being used #' @param ... other arguments passed to [ggplot2::geom_line()] #' @param include_zero Should 0 be included on the y-axis? #' @author Joseph Larmarange #' @keywords hplot #' @export #' @examples #' # Small function to display plots only if it's interactive #' p_ <- GGally::print_if_interactive #' #' data(tips, package = "reshape") #' tips_f <- tips #' tips_f$day <- factor(tips$day, c("Thur", "Fri", "Sat", "Sun")) #' #' # Numeric variable #' p_(ggally_trends(tips_f, mapping = aes(x = day, y = total_bill))) #' p_(ggally_trends(tips_f, mapping = aes(x = day, y = total_bill, colour = time))) #' #' # Binary variable #' p_(ggally_trends(tips_f, mapping = aes(x = day, y = smoker))) #' p_(ggally_trends(tips_f, mapping = aes(x = day, y = smoker, colour = sex))) #' #' # Discrete variable with 3 or more categories #' p_(ggally_trends(tips_f, mapping = aes(x = smoker, y = day))) #' p_(ggally_trends(tips_f, mapping = aes(x = smoker, y = day, color = sex))) #' #' # Include zero on Y axis #' p_(ggally_trends(tips_f, mapping = aes(x = day, y = total_bill), include_zero = TRUE)) #' p_(ggally_trends(tips_f, mapping = aes(x = day, y = smoker), include_zero = TRUE)) #' #' # Change line size #' p_(ggally_trends(tips_f, mapping = aes(x = day, y = smoker, colour = sex), size = 3)) #' #' # Define weights with the appropriate aesthetic #' d <- as.data.frame(Titanic) #' p_(ggally_trends( #' d, #' mapping = aes(x = Class, y = Survived, weight = Freq, color = Sex), #' include_zero = TRUE #' )) ggally_trends <- function( data, mapping, ..., include_zero = FALSE ) { if (is.null(mapping$x)) stop("'x' aesthetic is required.") if (is.null(mapping$y)) stop("'y' aesthetic is required.") # computing group g <- list() if (!is.null(mapping$alpha)) g <- append(g, list(eval_data_col(data, mapping$alpha))) if (!is.null(mapping$colour)) g <- append(g, list(eval_data_col(data, mapping$colour))) if (!is.null(mapping$linetype)) g <- append(g, list(eval_data_col(data, mapping$linetype))) if (!is.null(mapping$size)) g <- append(g, list(eval_data_col(data, mapping$size))) if (length(g) == 0) { mapping$group <- aes(group = 1)$group } else { data$.group <- interaction(g) mapping$group <- aes_string(group = ".group")$group } # considering the different situations regarding y y <- eval_data_col(data, mapping$y) if (is.factor(y) || is.character(y) || is.logical(y)) { y <- as.factor(y) if (length(levels(y)) == 2) { # Binary variable data[[".ggally_y"]] <- as.integer(y == levels(y)[2]) mapping$y <- aes_string(y = ".ggally_y")$y p <- ggplot(data, mapping) + stat_weighted_mean(geom = "line", ...) + scale_y_continuous(labels = scales::percent) + ylab("") } else { # 3 or more categories yname <- mapping_string(mapping$y) # we need to duplicate date for each level of y # and to map y to linetype d <- data.frame() for (l in levels(y)) { tmp <- data tmp[[".ggally_y"]] <- as.integer(y == l) tmp$y <- l d <- plyr::rbind.fill(d, tmp) } mapping$linetype <- aes_string(y = "y")$y mapping$y <- aes_string(y = ".ggally_y")$y # recomputing groups g <- list() if (!is.null(mapping$alpha)) g <- append(g, list(eval_data_col(d, mapping$alpha))) if (!is.null(mapping$colour)) g <- append(g, list(eval_data_col(d, mapping$colour))) if (!is.null(mapping$linetype)) g <- append(g, list(eval_data_col(d, mapping$linetype))) if (!is.null(mapping$size)) g <- append(g, list(eval_data_col(d, mapping$size))) d$.group <- interaction(g) mapping$group <- aes_string(group = ".group")$group p <- ggplot(d, mapping) + stat_weighted_mean(geom = "line", ...) + scale_y_continuous(labels = scales::percent) + ylab("") + labs(linetype = yname) } } else { # Numeric variable p <- ggplot(data, mapping) + stat_weighted_mean(geom = "line", ...) } if (include_zero) p <- p + expand_limits(y = 0) p } GGally/NEWS.md0000644000176200001440000003632314063456723012550 0ustar liggesusers# GGally 2.1.2 ### Bug fixes * Replace `ggplot2` usage of `*_guide = FALSE` with `*_guide = "none"` (@larmarange, #418) * Require `network >= 1.17.1` (#418) # GGally 2.1.1 ### Bug fixes * Ignore `colour` aesthetic if all values are `NA`. (@larmarange, #404) * Avoid all duplicates within `stat_cross()`. (@larmarange, #402) * Avoid an error when tidiers do not return p-values. (@larmarange, #400) * Suggest `emmeans` to allow `ggcoef()` example to execute. (#407) # GGally 2.1.0 ### Breaking changes * Following version 7.0.0 of `broom`, computed residuals in `stat_cross()` are now named `"resid"` and `"std.resid"`. `cells` and `fill` arguments of `ggally_crosstable()` and `ggtable()` have been updated accordingly (@larmarange, #391) ### Other changes * `ggcoef()` redesign based on `broom.helpers` with four new functions: `ggcoef_model()`, `ggcoef_compare()`, `ggcoef_multinom()` and `ggcoef_plot()` (more informations in the dedicated vignette, @larmarange, #392) * New geometries: `geom_stripped_rows()` and `geom_stripped_cols()` (#392, @larmarange) * New option `reverse_fill_labels` for `ggally_colbar()` and `ggally_rowbar()` (@larmarange, #374) * `stat_prop()` now accepts a **x** or a **y** aesthetic (#395, @larmarange) * Temporarily not listening to `ggally_statistic(family)` to avoid monospaced font issues. See #373 for more details. (#387) # GGally 2.0.0 ### New Vignettes * [`vig_ggally("ggally_plots")`](https://ggobi.github.io/ggally/articles/ggally_plots.html) - ggally_*(): List of available high-level plots * [`vig_ggally("ggally_stats")`](https://ggobi.github.io/ggally/articles/ggally_stats.html) - stat_*(): Additional statistics for ggplot2 * [`vig_ggally("ggbivariate")`](https://ggobi.github.io/ggally/articles/ggbivariate.html) - ggbivariate(): Plot an outcome with several potential explanatory variables * [`vig_ggally("ggtable")`](https://ggobi.github.io/ggally/articles/ggtable.html) - ggtable(): Cross-tabulated tables * To view all vignettes for `GGally`, call `GGally::vig_ggally()` ### New functions `ggbivariate()` (@larmarange, #324) * Display an outcome using several potential explanatory variables * [`vig_ggally("ggbivariate")`](https://ggobi.github.io/ggally/articles/ggbivariate.html) `ggtable()` (@larmarange, #351) * Cross-tabulated tables of discrete variables * [`vig_ggally("ggtable")`](https://ggobi.github.io/ggally/articles/ggtable.html) `add_to_ggmatrix()` (#362) * Add ggplot2 objects to `ggmatrix` objects at selected locations * Locations can be rows, columns, matrices, or other shorthand values. `ggally_autopoint()`, `ggally_autopointDiag()` (@larmarange, #325) * Make scatterplots compatible with both continuous and categorical variables using `ggforce::geom_autopoint()`. `ggally_colbar()`, `ggally_rowbar()` (@larmarange, #324) * Plot column or row percentage using bar plots. `ggally_count()`, `ggally_countDiag()` (@larmarange, #321) * Plot the number of observations by using rectangles with proportional areas. `ggally_cross()` (@larmarange, #326) * Plot the number of observations by using square points with proportional areas. `ggally_crosstable()` (@larmarange, #351) * Display a cross-tabulated table. `ggally_statistic()` (#327) * A generalized version of `ggally_cor()` * Use this method to create functions similar to `ggally_cor()` that return any text value given and `x` and `y` vector of data `ggally_summarise_by()` (@larmarange, #325) * Display summary statistics of a continuous variable for each value of a discrete variable. `ggally_table()` (@larmarange, #326) * Plot the number of observations as a table. `ggally_trends()` (@larmarange, #333) * Plot trends using line plots. `signif_stars()` (@larmarange, #327) * Return the appropriate number of significance stars as a character vector for the provided numeric input values. ### New `ggplot2` plot statistics: `stat_cross()` (@larmarange, #326) * Computes statistics of a 2-dimensional matrix using `broom::augment.htest`. `stat_prop()` (@larmarange, #324) * Compute proportions according to custom denominator. `stat_weighted_mean()` (@larmarange, #333) * Compute the mean of y aesthetic for each unique value of x, taking into account weight aesthetic if provided. ### Major updates `ggally_cor()` (#327) * New implementation using `ggally_statistic()` * Will now hide the grid by default and add a border (`displayGrid = FALSE`) * Added the ability to display significance stars (`stars = TRUE`) * Alignment has been fixed so both short and long names should be displayed within view. `alignPercent` now corresponds to the center of the text. * Added the ability to separate the arguments sent to the title and the groups (`title_args` and `group_args`) * Digits now represents the total number of digits after the decimal place. * To use the old version, change your `ggally_cor()` function calls to `ggally_cor_v1_5()`. * Previously deprecated parameters have been removed Website * Updated to use `pkgdown` (#335) ### Features and bug fixes: `ggpairs()` (#331) * New `proportion` argument to control relative size of sub-plots * option `proportion = "auto"` for automatic guess based on the number of levels for discrete variables `ggduo()` (#331) * New `xProportion` and `yProportion` arguments to control relative size of sub-plots * Set option `xProportion = "auto"` and `yProportion = "auto"` for automatic guess based on the number of levels for discrete variables `ggscatmat()` * `lowertriangle()` now preallocates it's memory usage for a 2-5x speed improvement. (@vlepori, #328) * Fixed `facet`'ing error where the factor order was not preserved. This error caused the facets to be alphabetically sorted, cause plots to appear in unexpected locations. (#355) # GGally 1.5.0 * Updated to work with ggplot2 v3.3.0 (#308) `ggnet` and `ggnet2` * Fixed some logic bugs from newer R versions `ggally_box` and `ggally_dot` * Label now appears axis and is displayed in a plot matrix. (#253) `ggsurv` * Provide sensible legend values when multiple factors are present. (#310) `ggally_cor` * Added `displayGrid` argument to turn of the background grid. (#312) GGally 1.3.3 ---------------- `ggpairs` and `ggduo` * Become ggplot2 v2.2.2 compliant (#266) * When retrieving functions with wrap, `ggally_*` functions do not require the GGally namespace (#269) * Exported `eval_data_col`, `mapping_string`, and `mapping_swap_x_y` (5d157f6) * Exported `is_horizontal` and `is_character_column` (#270) * Logical values are now treated as discrete (#272) `ggmatrix` * `progress` parameter added to ggmatrix (and appropriate parent functions). Allows for `TRUE`, `FALSE`, `NULL`, and `function(pm){...}` (#271) `ggnostic` * Cooks distance cutoff is now at F_{p, n - p}(0.5) (#274) `ggnet2` * Replaced loading packages with loading namespaces(#262) `ggally_smooth` * Added `shrink` and `se` parameters to `ggally_smooth` (#247) `ggcoef` * Added `sort` parameter to sort by beta values (#273) `ggparcoord` * Fixed bug where x axis breaks and labels did not appear when `splineFactor = TRUE` (#279) GGally 1.3.2 ----------------- `ggpairs` and `ggduo` * Removed warning where pure numeric names gave a warning (#238, @lepennec) * Fixed ordering issue with horizontal boxplots (#239) `ggparcoord` * Fixed missing `x` aes requirement when shadebox is provided (#237, @treysp) Package * Made igraph a non required dependency for tests (#240) GGally 1.3.1 ----------------- Added new dataset `psychademic` * See `?psychademic` for more details * (And updated the broken UCLA links) Added original ggmatrix theme * added function to set theme to have clear strip background and rearrange the strip positions * added parameter `switch` to ggmatrix (and friends) to allow for strip repositioning. See `?ggplot::facet_grid` for more documentation on `switch` (#223, #224) `ggsurv` error reporting * removed a one error check that is covered in other places (#222) `+.gg` * allow to add a list of items to a ggmatrix (#228) `ggmatrix.print` * fix strip issues with ggplot2 name update GGally 1.3.0 ----------------- `ggmatrix.print` - massive update! * Now prints with a ggplot2 facet'ed structure * Column titles are now placed in the strip of a plot matrix * If there are 16 plots or more, a progress bar is displayed automatically (if interactive). Please look at the documentation for `ggmatrix_gtable` more details. `ggmatrix` legend * A legend may be added with the `legend` parameter in `ggduo`, `ggpairs`, and `ggmatrix` * May specify a (length two) numeric plot coordinate * May specify a (length one) numeric plot position * May specify a legend object retrieved from `grab_legend` `ggnostic` - New function! * Produces a `ggmatrix` of diagnostic plots from a model object * Uses broom to retrieve model information * Each column of the plot matrix is a predictor variable. The rows can display the response variables, fitted points, residuals, standardized residuals, leave one out model sigma values, diagonals of the hat matrix, and cook's distance for each point. `ggfacet` - New function! * Produces single ggplot2 object * interface is very similar to `ggduo` and `ggpairs` `fn_switch` - New function! * Provide many functions in a list but only call one function at run time according to a mapping value * Useful for `ggnostic` for different behavior depending on the y variable * Allows for a 'default' value for the default switch case `ggmatrix` - allow custom labellers for facet labels * Added labeller parameter which is supplied to `ggplot2::facet_grid()` * Allows for labels with plotmath expressions `ggmatrix` and `ggplot2::last_plot()` * If a `ggmatrix` object is printed, `ggplot2::last_plot()` will return the plot matrix `ggmatrix` and ggplot2 labels * `ggplot2::labs` `+`'ed to a ggmatrix object * `ggplot2::xlab` and `ggplot2::ylab` may be `+`'ed to a ggmatrix object * `ggplot2::ggtitle` `+`'ed to a ggmatrix object * (anything that returns a class of "labels" may be added to a ggmatrix object) `ggmatrix` and `ggplot2::ggsave()` * `ggsave` now works with `ggmatrix` objects `ggpairs` and `ggduo` check for cardinality (#197) * Before creating a ggmatrix object, a check is made for character/factor columns * If there are more than 15 (default) unique combinations, an error is thrown. * Setting `cardinality_threshold` parameter to a higher value can fix the problem (knowing single cell plots may take more time to produce) * Setting `cardinality_threshold` parameter to `NULL` can stop the check `ggmatrix` plot proportions * `ggmatrix` can set the plot proportions with the parameters `xProportions` and `yProportions` * These will change the relative size of the plot panels produced. `ggally_cor` colour aesthetic * color must be a non-numeric value `ggsurv` * added boolean to allow for legend to not be sorted * fixed bug where censored points with custom color didn't match properly (#185) Vignettes * vignettes are now displayed using `packagedocs`. More info at http://hafen.github.io/packagedocs/ `ggally_box_no_facet` and `ggally_dot_no_facet` * New methods added as defaults to pair with new ggmatrix print method GGally 1.2.0 ----------------- install requirements * relaxed install requirements on grid (5d06dfc, d57469a, 933bb14, 73b314d) ggduo - New! * plot two grouped data in a plot matrix (#173) * helpful for plotting two sets of columns, multivariate analysis, and canonical correlation analysis * be sure to check out the examples! ggally_smooth_loess - New! * uses the loess method with drawing a line (1552f96) ggally_smooth_lm - New! * uses the lm method with drawing a line (1552f96) * alias of ggally_smooth ggmatrix.print * fixed bug strips where causing spacing issue when printing axis labels (174630d) ggnetworkmap * fixed bug where checking for the package 'intergraph' couldn't be reached ggsurv * changed default of plotting multiple censored data color to match the survival line package testing * added many more tests! GGally 1.1.0 ----------------- ggcoef - New! * plot model coefficients with broom and ggplot2 PR#162 * Plotting model coefficients (http://www.r-statistics.com/2010/07/visualization-of-regression-coefficients-in-r/) gglegend - New! * pull out the legend of a plot which can also be used in ggpairs PR#155, PR#169 ggally_densityDiag * fixed bug where '...' was not respected (d0fe633) ggally_smooth * added 'method' parameter (411213c) ggally_ratio * Does not call ggfluctuation2 anymore. PR#165 ggcorr * fixed issue with unnamed correlation matrix used as input PR#146 * fixed issue undesired shifting when layout.exp was > 0 PR#171 ggfluctuation2 * is being deprecated. Please use ggally_ratio instead PR#165 ggnetworkmap * fixed issue with overlaying network on a world map PR#157 ggparcoord * Fixed odd bug where a list was trying to be forced as a double PR#162 ggpairs * Fixed improperly rotated axes with ggally_ratio PR#165 ggscatmat * added 'corMethod' parameter for use in upper triangle PR#145 ggsurv * size.est and size.ci parameters added PR#153 * ordering changed to reflect survival time PR#147 * added a vignette PR#154 wrap * documentation updated PR#152 * changes default behavior only. If an argument is supplied, the argument will take precedence github chat * https://gitter.im/ggobi/ggally is the place to visit for general questions. travis-ci * cache packages for faster checking * install covr and lintr from github for testing purposes GGally 1.0.1 ----------------- ggparcoord * fix handling of factor group variable PR#131 ggscatmat * force all char columns to factors PR#134 print.ggmatrix * add boolean for grid.newpage ggmatrix print method PR#126 GGally 1.0.0 ----------------- ggplot2 * GGally has been upgraded to run on the latest ggplot2 v1.1.0. PR#109 New functions * ggmatrix. Make a generic matrix of ggplot2 plots * ggnetworkmap. Plot a network with ggplot2 suitable for overlay on a ggmap::map ggplot, or other ggplot * ggnet2. Function for plotting network objects using ggplot2, with additional control over graphical parameters that are not supported by the ggnet function Vignettes * glyph - new! * ggmatrix - new! * ggnetworkmap - new! * ggpairs - new! * ggscatmat - new! ggmatrix * allows for bracket notation when getting or setting plots. PR#61 * full control over axis labels and axis text. PR#107, PR#111 ggpairs * is now wrapper to ggmatrix * takes in 'wrapped' functions. This better handles the case of many different parameters being supplied to different plot types. PR#90 * dates are better handled in ggpairs. Still room for improvement for default behavior, but they do not cause errors. PR#58, PR#59 * displays a 'NA' plot when all or a combination of the data is NA. PR#119 ggcorr * legend title expressions may be used. PR#55 * handles objects that may be coerced into a data.frame PR#70 gglyph * changed geom_line to geom_path in gglyph. Fixes ordering issue. PR#51 ggparcoord * remaining columns are passed through so aesthetics may be added later. PR#54 * fixed parcoord ordering issues with odd names. PR#106 * fixed scaling when unique length equals 1. PR#122 ggsurv * color censored marks the same color as the line. PR#74 * allow for different censored color marks. PR#113 ggally_density * add fake data points to extend the limits of the stat_density2d. PR#114 ggally_na * new plot type! Data * removed cityServiceFirms * added twitter_spambots GGally/MD50000644000176200001440000002545614064014052011751 0ustar liggesuserscba9ad83e6f3bf689065d904d2b6114a *DESCRIPTION 4963b9d74b4095a6d731c7548564a454 *NAMESPACE c9ae2f952e97a2d31960705b07638cc8 *NEWS.md 2846a43d13944e14ee6ff2893e618728 *R/GGally-package.R fe023d1b299a98d2b59777015babcbe5 *R/data-australia-pisa-2012.R 040a3753cbc5934d39c690be2df943ae *R/data-flea.R 6855f0bd7808ec56f495a80016d319a2 *R/data-happy.R 1a857c1deb41349c1238f3fa93e38fa7 *R/data-nasa.R 53bf3cbe28642ebaa5499f14cc23ae52 *R/data-pigs.R 8e06efe84025094992bd469e708538c9 *R/data-psychademic.R f0212d247c0c0145a7f37da9e39820f3 *R/data-twitter_spambots.R 1d7baab448755be8be58068be2b0d074 *R/deprecated.R 0fec33cf2e05ebe18efd124e923f5e0b *R/find-combo.R 48fef882643ea9f5a28063c75ce98f9b *R/geom_stripped_rows.R 0f9137a0b64a847b44e3c7bf09bd7854 *R/gg-plots.R a73a9c08904cc55f6d652b315b36b71c *R/ggbivariate.R eb0ce199736f358496e947caeb0f5d12 *R/ggcoef.R d5630435678242420ec73209984f8a0a *R/ggcoef_model.R 0215c53ec0d889e69bdf1d849adcc499 *R/ggcorr.R f4d826b085f092c69874ca6abdf626c5 *R/ggfacet.R 00d00cf8fe9b5bc61939d2f3a1a70a00 *R/gglyph.R 92d1af435bdae78b2540599d91bfda09 *R/ggmatrix.R 6835fe809275860fa0dc56546649d068 *R/ggmatrix_gtable.R 8e0abbe4c3688786f30ddfefb5cb8c1d *R/ggmatrix_gtable_helpers.R abcef28b8d3c4e3435eeed1c52f532cb *R/ggmatrix_legend.R 57bef64d096aa258081a717ce8716bf7 *R/ggmatrix_make_plot.R 75c2845f5b93fae7bff984cafe2f873d *R/ggmatrix_print.R 75ae7b19938f0b6d692bee89bd95c2c5 *R/ggmatrix_progress.R b72d9436c092a50d3f81a6a90a2ac903 *R/ggnet.R 9e9f0a5984934ae462d0b9d9146513c7 *R/ggnet2.R d623272533d66a5c87af99446208fef9 *R/ggnetworkmap.R 93c436ba77b1dcbc1469b77c6d00b2fc *R/ggnostic.R 1d388344a3dfdc93e16f4d78c5b59d02 *R/ggpairs.R bd5a73244441616d1712df3ee7431236 *R/ggpairs_add.R e621b66230e4f308d24af123e58fe09a *R/ggpairs_getput.R 62f9d3efd656a50e834e3ee80e767388 *R/ggpairs_internal_plots.R b505b9c446b9f1e0983f2b2b8e2a5436 *R/ggparcoord.R f6d995d40e93b1961dbb3a5119bcbf89 *R/ggsave.R d2475c76973190240bc8455dc35e94f5 *R/ggscatmat.R cc4b223e98b5125cbdf344e54f5d3bbf *R/ggsurv.R 2d27929c417a7bc5823a60d9f3a876b8 *R/ggtable.R e9479272ab8382566fd217d0615e0e3b *R/stars.R 7c8aecd9d0b64d7d7717095c9fcc9738 *R/stat_cross.R acef0f3426286f7345831783224e47db *R/stat_prop.R 3b48e22f023f648df0268f8d7043aea5 *R/stat_weighted_mean.R 6ab71685d6d9a36e5288d990dd2c30bc *R/utils.R 1d7f11e8ecdacfb9ecd138402c06a209 *R/vig_ggally.R b92b8f2e94acf02f7112e11dea9ab346 *README.md 6feac25e1fe2bcd252052389cb03f85c *build/GGally.pdf 65c02cbc1793d76534f54142a9074573 *data/australia_PISA2012.rda 6fd42916be1426cb25e43fdec13fab51 *data/flea.rda 3eb6b88fcd8a095e3c5fa15ad242edf4 *data/happy.rda 7a121e6c90535716c3276dde47de36c7 *data/nasa.rda 24f257996f1963af78909cbf777b0dd3 *data/pigs.rda 0fa100acd2b9fc9893090e2cfcd6e9b4 *data/psychademic.rda 367c722d81b37120a120efaa1875dda2 *data/twitter_spambots.rda e3213402cd27e12c28738891a7d911f7 *inst/WORDLIST cd40e8ec4b40bbecc4c57add09a1ed80 *man/GGally-package.Rd e1c20100fbe929ea2a7aa8d83533a2b0 *man/add_and_overwrite_aes.Rd 18261a1145ae8a1492a634a834517f13 *man/add_ref_boxes.Rd b24b20ab6c78fc645f04bc463018267d *man/add_ref_lines.Rd 953f2bba0b7bca339c674a60fd24c9c6 *man/australia_PISA2012.Rd 9658083025d94f544042320c128a01ef *man/brew_colors.Rd 8ed351ce64c1134278edc46607ca4e3a *man/broomify.Rd a2984c095b5b91c509908af15e5f586f *man/column_is_character.Rd bc11181f44873ea5925ca2d80a0b712a *man/eval_data_col.Rd 81923b2be588fcc5dfd9aa3b6e328c7f *man/find_plot_type.Rd 39b138b2488f2f74dae4d700b070aa6e *man/flea.Rd ef2193f3b1b556972e922eca679a700c *man/fn_switch.Rd a08716f31e8966cf4efb5b61c504ad35 *man/geom_stripped_rows.Rd 67c7cf1f9d5a41084515bc7a0a9d403a *man/getPlot.Rd 17c6fc6d258e79fd6a485dba92464f57 *man/get_x_axis_labels.Rd 8217aa58f57a8e50fed631451b6549aa *man/gg-add.Rd 515045a1092effbdc36cde87c060d985 *man/ggally_autopoint.Rd 2a124f2171db9f08011096698abd95f1 *man/ggally_barDiag.Rd c78f81dade5b9053fd880bb59b697348 *man/ggally_blank.Rd 70be421a04b6daa1b005a0c619b33a8f *man/ggally_box.Rd effda7fe5cf123cdc1dff10c99d8333b *man/ggally_colbar.Rd d8cba5c64da2fb5f989288b349366789 *man/ggally_cor.Rd b5a24f76a154031bb21b61d06936852e *man/ggally_cor_v1_5.Rd e17de8606d6ac07ab042d04323a3a2ed *man/ggally_count.Rd ada17102031d0031296cd6d6b6b6c519 *man/ggally_cross.Rd 47cf08c23a5a297ea3f23fbf5c507c41 *man/ggally_crosstable.Rd b18f19344657ac3820af9e718af68253 *man/ggally_density.Rd 698fb48d36abae446b1e34f7af61b781 *man/ggally_densityDiag.Rd cab9ae948093368b0fd18b10391cb58a *man/ggally_denstrip.Rd 74bb5982fd0cc2be1d7d31f9d604fee2 *man/ggally_diagAxis.Rd 4efd2afb212832ad8b1e9bc69cb7ff41 *man/ggally_dot.Rd 8c714e3f68eec4f34a3efa38762fb411 *man/ggally_dot_and_box.Rd b2739ae9f00d2e645f29581040bcd061 *man/ggally_facetbar.Rd 7d84e8560103c2ca5e86067030c0cf45 *man/ggally_facetdensity.Rd 09964f4d2694a0a8f459b43cc1fa92cf *man/ggally_facetdensitystrip.Rd 8693fe178be8069e475912b22f173d44 *man/ggally_facethist.Rd d063db9255b26f92564ec7862c7767db *man/ggally_na.Rd 37a5478f251b558516e5f4612b86a6b8 *man/ggally_nostic_cooksd.Rd a3217c303371cca0228af9de872eac50 *man/ggally_nostic_hat.Rd 6d4797ef22b49ea47034e8e41686cfb8 *man/ggally_nostic_line.Rd 8ecd42b643b3e89001e5befd33f10242 *man/ggally_nostic_resid.Rd 836d0d6d142a990614dc203be31ab0e0 *man/ggally_nostic_se_fit.Rd a7a1ea2178a34f5c3ca5ad782a722d93 *man/ggally_nostic_sigma.Rd ef0d0fd2d28065a03b669eb8fdec4809 *man/ggally_nostic_std_resid.Rd c57bc5917cfe34b9b7975f999ebf7378 *man/ggally_points.Rd e56663ea191ea27627cb8fffd5e4efe0 *man/ggally_ratio.Rd 5b1a94d154aa212087936afc91e6322a *man/ggally_smooth.Rd 9bb9170cbc581fe0b12cfc58cf72a9ed *man/ggally_statistic.Rd 82f18d1b496c6d4939c4af2e71b19f90 *man/ggally_summarise_by.Rd b12ac5f6f11e871b832664b5cf3a1ace *man/ggally_table.Rd 026a9a50a95ecd1fb15d2732197c0022 *man/ggally_text.Rd e147547772b393621078c0b64845fac3 *man/ggally_trends.Rd cf9d5481ef2449adf2c33b194ee5843b *man/ggbivariate.Rd 36369def645851f5d89d46be0ad82198 *man/ggcoef.Rd 42a5becd15a25064768e7a5e7286523c *man/ggcoef_model.Rd 3bc389701f3bade75d550a6bc3b7badb *man/ggcorr.Rd 4d0e9b5a1569ab603330f0e64f48af9b *man/ggduo.Rd a1e171d466c36aaf6022f152f04c0a20 *man/ggfacet.Rd 553170f69b1fc2b66ed220042baa9f89 *man/gglegend.Rd 6fd6ee868cdab39c54d62d21bb83bf8c *man/ggmatrix.Rd 894398c3e25d5684dc6a336c522a71d6 *man/ggmatrix_gtable.Rd 777d2472a8d59e9a911f6816e0210ed6 *man/ggmatrix_location.Rd 6dc24f284da4b73b17526a3c7f2aedbf *man/ggmatrix_progress.Rd 41da2b274005a31396c291f3701f7ee0 *man/ggnet.Rd 6f4047705f106fc0a1219192be9bf0fb *man/ggnet2.Rd 2636e3673c38d7437b40b56baad07246 *man/ggnetworkmap.Rd db0eabeeb3caef47deaf744193da9385 *man/ggnostic.Rd fcb364c09b87ee5b877680acadf4f9ee *man/ggpairs.Rd 0126681881250bd4b8f34481a26123b9 *man/ggparcoord.Rd 917862b9dcde65670eab1265751a252d *man/ggscatmat.Rd aba2f30eeb155b9c2ad662cdb9f4c7ef *man/ggsurv.Rd 42416be46c21207ef3221bfb1d3c0dfc *man/ggtable.Rd 0500aec2073247bb42d3ca1fbb3f9841 *man/ggts.Rd a696e6d7bb227bb33d3ac0b634385bb2 *man/glyphplot.Rd 3ee70b07857892e5239131a268220acd *man/glyphs.Rd 6b9c6d4bae59a6160a408f52ea4a9aad *man/grab_legend.Rd 28893fe20f76351104c13ebaeba18f6a *man/happy.Rd 51a1e77f4ab853957443164be10cceac *man/is_blank_plot.Rd be1f8fe5db0d0c9d51974196a85061d0 *man/is_date.Rd 55d306069f4f123f3ce8fd9c2fc9601b *man/is_horizontal.Rd a194cce6fc7ebc72c39e2504b2d1811c *man/lowertriangle.Rd 14e2dce5846392e116237df589dfeb32 *man/mapping_color_to_fill.Rd 0d432bdcf9299be35c5b363dbbc64f05 *man/mapping_string.Rd d02972d593e54c3cb51de2e49cb30053 *man/mapping_swap_x_y.Rd d8af662435affcb043a73ddb94bc2e83 *man/model_terms.Rd 783045db095103c24a5026a9cd516b57 *man/nasa.Rd 8080159689196b1bbb3cedafde981e5b *man/pigs.Rd 4d35f28ae8b0828e310ff489bf07fda2 *man/pipe.Rd e16c96a58d59002cea25781fc649fcaa *man/plot_types.Rd 6cbaa828dbbfc062f797d7cc9abd87fc *man/plotting_data_type.Rd f1a0bf138f0a610fc31a0488a8c5ee2c *man/print.ggmatrix.Rd 8aeae61731697ad035b83bf24bc9dbb5 *man/print_if_interactive.Rd 0135c93fdec59d4d375b6edf0c1cf134 *man/psychademic.Rd 33796682214a524f7384e689185bdf88 *man/putPlot.Rd e436ad91b858415de99301ff6c2ec38a *man/remove_color_unless_equal.Rd d35494061c24badf1b5d68461961f454 *man/require_namespaces.Rd ffac1d44e1c0606b9e884a27fa29c0b4 *man/rescale01.Rd 31363eeaa15c717367f1245a95db12ec *man/scag_order.Rd 6777aba6556a54334a49e0ab0ebd2610 *man/scatmat.Rd 044d6b9a3a4aae12b95982a012588fd8 *man/signif_stars.Rd 67474385131500b211522f1ea55719cd *man/singleClassOrder.Rd 25c66ede127b5706227c780f3078dcd2 *man/skewness.Rd d32e8ad8249f875b1db949b055b58146 *man/stat_cross.Rd 1a94812068817e987d0be9fbccfe1211 *man/stat_prop.Rd 17d38664cde657ffa66215e606ebee73 *man/stat_weighted_mean.Rd fa652c1bb1100d688d8a3a1d2b70a76e *man/str.ggmatrix.Rd 5e328e96e1363f38468d558d5fa94b89 *man/twitter_spambots.Rd 2657c3b0941d52ab3bbb0698e882b5b3 *man/uppertriangle.Rd 6651c6e074ba57977d211d419cbbfe95 *man/v1_ggmatrix_theme.Rd 778ad32efebd7eee73cf5b60e6254dbf *man/vig_ggally.Rd f4f6cbf8d28334f9b6289f4249d4fa9c *man/wrap.Rd cd7066afad955fd8df6dad2775db4280 *tests/spelling.R cb9dc46d86c7be0d678d7f89f3e541dc *tests/testthat.R 2c7748f1ee4d74df72fcf9096088a947 *tests/testthat/data/airports.csv 5f9090fb42d8870062b0923c3d27bf78 *tests/testthat/test-crosstalk.R 38e7ada6f1de83eb996db1e324cf9b0f *tests/testthat/test-deprecated.R 66cf703e55b77049aca18276dea9a27e *tests/testthat/test-gg-plots.R 8806ef1a3c7e9e542822bc45a207d72c *tests/testthat/test-ggbivariate.R e5c3eac509e9838ea83977ae418cdcb7 *tests/testthat/test-ggcoef.R 9397981646529c3d77439b1ea3c2c639 *tests/testthat/test-ggcoef_model.R 3d88848b89a0203051f8e133b2c4c7d5 *tests/testthat/test-ggcorr.R 18e03a5c87b84e383f62a01533941bd6 *tests/testthat/test-ggfacet.R 46248cc7d714d0074acf4a02c3618b39 *tests/testthat/test-gglegend.R 98399724907f8d4e67bb7ffc08e553d2 *tests/testthat/test-gglyph.R e1d61eaecc274c314e71ef2ea4336fe0 *tests/testthat/test-ggmatrix.R 228d8d272ad8295aac6e5f92a7ffe95a *tests/testthat/test-ggmatrix_add.R dc5de6cdeb8d7fee273934fa3b76f07c *tests/testthat/test-ggmatrix_getput.R 941d446713be01b47b185c9f4defa910 *tests/testthat/test-ggmatrix_location.R 7551583d305f619e39108918b648d1af *tests/testthat/test-ggnet.R 47d755b53c0cca75e463232ce57c7f4c *tests/testthat/test-ggnet2.R c5222a87f73fa5ef0c847d02abc4eda1 *tests/testthat/test-ggnetworkmap.R 8f2bb655440c1d56b0baf2e6843e4399 *tests/testthat/test-ggnostic.R 5b66d1c3b84932a1921dce9470784d27 *tests/testthat/test-ggparcoord.R a57f9ae203c741346a13a74238edcf6f *tests/testthat/test-ggsave.R 7a191fa22ba8f8c0acaadb6d59235058 *tests/testthat/test-ggscatmat.R bf26664513110ce6d809dddc82dcf0fe *tests/testthat/test-ggsurv.R df18394a59c2a75e8e4f99cb898fbff2 *tests/testthat/test-ggtable.R d4e6588d599e183189ee21da4636160f *tests/testthat/test-stat_cross.R ac32a0d643fbf59fe6d00ef486f2503a *tests/testthat/test-stat_prop.R 4af92d3f1fcf429fb9e70717a8436ad1 *tests/testthat/test-stat_weighted_mean.R 29afb30bd458187706dcb0f87775faf4 *tests/testthat/test-utils.R 9d005157950a1f22273004e8758f737b *tests/testthat/test-vig_ggally.R 9d279504ea7127eb3712ef49b583cdcd *tests/testthat/test-wrap.R 9bf5dacf41c3ff49cae47d3698b8b2f5 *tests/testthat/test-zzz_ggpairs.R GGally/inst/0000755000176200001440000000000014021431207012400 5ustar liggesusersGGally/inst/WORDLIST0000644000176200001440000000245714021431207013602 0ustar liggesusersaede aedeagus aes Aes anova api axisLabels axisVars barDiag bb behaviour Biometrics blankDiag Broomify broomify'ed cardinality centerObs ci cityServiceFirms cloudhigh cloudlow cloudmid cmu codebook codecov ColorBrewer colour colours columnsY concinna corMethod covr dae densityDiag df dfc diag directedness DOI edu Environmetrics exponentiate facet'ed fe finrela formatter Fruchterman Gb geocoded geoms ggally ggbivariate ggcoef ggcorr ggduo ggfluctuation gglegend gglyph ggmap ggmatrix ggnet ggnetworkmap ggnostic ggpairs ggparcoord ggplot ggscatmat ggsurv ggtable github glyphmap glyphplot glyphs gplot grayscale grey gridlines GSS heikertingeri Heptapot heptapotamica herdsz Herzberg hjust Hout http ide idre igraph ing intergraph io Jibum jitter Jordano JSM Kaplan Kutner larmarange labelled labeller labellers linetype lintr Liu lm loess lon lowertriangle lt Lubischew Marsden McGraw Mengjia Murrell na Nachtsheim naDiag NaN Neter newpage NORC num Opsahl param parcoord plotmath preallocates Programme PV quartiles RColorBrewer rdocumentation Reingold rescale Rescaling resid RStudio scagnostic scagnostics scaler SCIE se SENWGT shadebox shadeBox spambots Springer Statlib stdres Storrs stratums Su's Summarise surftemp th Tian travis truthy ucla univariately uppertriangle vjust Wickham's wtsall www Yau Yu Zheng tidiers