From 29829f8026b7613fe068d3587b6ef6493cf43720 Mon Sep 17 00:00:00 2001 From: Ole Schulz-Trieglaff Date: Wed, 16 Dec 2015 20:02:33 +0000 Subject: [PATCH 1/3] Initial commit, prototype tree implementation and intro slides --- 07-Decision-Trees/IntroDecisionTrees.odp | Bin 0 -> 36347 bytes 07-Decision-Trees/dtree_simple.py | 131 +++++++++++++++++++++++ 07-Decision-Trees/transform.data.pl | 44 ++++++++ 3 files changed, 175 insertions(+) create mode 100644 07-Decision-Trees/IntroDecisionTrees.odp create mode 100644 07-Decision-Trees/dtree_simple.py create mode 100644 07-Decision-Trees/transform.data.pl diff --git a/07-Decision-Trees/IntroDecisionTrees.odp b/07-Decision-Trees/IntroDecisionTrees.odp new file mode 100644 index 0000000000000000000000000000000000000000..d5134f2350f616a06bbcaf57d8580835282a0e9d GIT binary patch literal 36347 zcmdqIRa9L;v?Y3QcXv%7xCIOD?(PH#4#C~so#5{7!QI{6odkEMckb=mJ^GE;AMfM- zyfe<;XP+%qwM$mbHRmb?X$VM65C|3o>MscuXZ>7J!UzI^{%yc1khQtBiKE+36T_cB zt;~%K9nEcR7@cj58Eg$5%pDkPf121B+Zs7po7gxq{IoZ50Dk}D9)LK{0~|X z=--A5B&uTOWc}U7(A>&_(eb|y8GhQBhAGI2Bf;at1GgYaN{A?dKw!Wjs2dg>*c->t zFoQs}gOVbGDy|u4x-c4;>NuI!Dy6e3{UC7oyyY4f(b@-e%lYNDL3Hg34dn{6NZxx) zZMG&{g!@hDn*L}580VGi?9{ez$TheLuiuR~pU^Wh-EKz5c^@)gHq-GMM~ruIarfcx zUaCM4eJ_v{pn!@V*ltowCUB-<{74XD9rXXlQ^puzGM^KKa=Y~ddVHf0LlS9{lsMCN zreVM&=WeA6rAk$)a$h zp6|D0p!D{FdGtY?`qub&y<{x(^H=FS$Jil@FBjeDtmNsipsswS((3F@%>qx>rFFd~ zi_;zL5|y9w%1D;pD@!GwC33%@kp$>rgdeL^j%9qd>NjX}G)`(i^9gkuHfIfW0)h#H50gZ5T8uwjgC_#2%>!HH8tC2Re_z2r10uZ z&BW9HnoGLriR4b&%Fyev#i zWFIgj_m1u8Bs0a`fXBWg8mDjHw*FzeZ)G|Bx#nog{Z3{F( z_m~mCS8cTZwqWi$s#a3%6%}N4;Nmjsii!$;P@tK4yL?F8bL@k;JLlem$pZDB9noce z_NjNXqb~@-sB=Z#g*J8m-JB*QCZ(g2AZl_cZkVa0K4eNQLUF5XJ)+RlCezrDr2SfR z86%Z{=*l>`B_E}gKV4F1@omzVx!9IeVi+iGs^!6-uLS&gU3{`3WxL$4pDyWy383(A z?&=zbDXmFOPmUqu6B_OJA5}|YV$p7sx>b4EqhDK{m+SiO@-2L0ggYPOTcLWUP%Swd z!x@QwMRrB2;YVwKKWT;f%ir-ZB-NeFuPtJ-KBZi zyFZM6)%J059AmZEZAdfmhjF%c*_}PZ>1!>%CE9FqK0Zpo@mi;}nD+JN9hy8@uNZ9U z^Y|>6>Bg~>I>?|tL=3^rUL8R0{MPPzbYePV@=7I}nRQD;-|99L*5`h)dU=-C;WM7f zJ22VrAtU$FV0-c2xcXqL(g^uP71IR;>*8HG|hF(z=ty z4b!D)QnP~9obQCU!1QI1M_Xpq_H(p$qusB1Q$IflzQ)G-udvY8M9^$MRXPqc>5;}? zo|+{U43>XAVcAIyxREHX~K4QIkUrHpc zQP;E0!c?ibyKUFDqEQz$)*m0&2YHAg?>mWdoAv92 z9ojrJ(ohPfHyO1X+&3ZkO6-mqa{2SfV zc~I5c3$^a`Z56&!x#;iDXKnMe7=d~6=WE<9S834{rq_3)!j{&xX4b#8Ij?n{vF4nkYI`Tpxd_c5t&HAa-D@vKTBj3qpLZ)~T`5 z1T&r6<(jMBlD2R7JyN$}wtYzAy5zVSZy-q*F< z^26rk$Bb&3O6gLGo67v&{RZ?4Bqs4@i{WX%Ag?%M3ZIK$SZsT(ve;6jh3m~>OKk5B z81cD$_8ObneD~Ud;Zn3L+VQ1JG6#S|4O)$4$SftB9K2RtX3R=U7nM`4afE+HI=`Nx26d=@Np;jxD^L4ld-!oThQyjrc0yvn_? z%w(xrug&Sfmeg0O+F@AP;5>uTaO18_j83op`tb^Zw7VTjx(zvdwbEKSgWx{ysCB|4 zQiv~=bX_z`w+Nb@%jZPzDPFZN0A5Gt#(J&&{T^#>uZpkB{bGnzUOOX6I$NcFqvTtC zk$ZD%*KA=p5`TGs-fz6GZdz#iM^d8K6V}&2yvzVP&$>nJ0>6 zX4Lw>Unif0N6+}}S_D&t4kDm+yQ+@K=QYihEC>l}n9Z+m7a;~?@aA=kIg+at-n;dx ztB>eu`ArLGWYar$wn%tA;fvscM6_7QcD&(-pderD+6(4W5L+$PtIg4TugJwQP%-y& zn4R(1%)iI&zPokAie>-oIEZbL9Mo`WTotQSgb!D=mKZC;oP54}^&wA(>wf9QclrbK z(pqS7CzYgBJ=b~!hpA;QHAhP!jg>Eg?_r8%I!iFi>6O+qjOVBo9fR!jsLjBVan0>@ z@98SyVP)3H-8fjJF*1v?IIx>`Vzk~B8k)4{Qley;ajg(x!P~zE0bD_4`S!!HPyZMRV zKYfcc_0sd28d2aGds5@8*S!@+lQHo162Uzx*%t=Z7`xSBGcYZvv>BQu$Y}Z&d-y(m z#UemML#sC(y#3oLZ(>yLb4M4Yd>rM8#trc{77*j2%H(^rbkY7;pn~^aqGp-jgn~lr zxc@5oDkC|A{e|4Txw29&o7_5HY+y;|*Z39=BwB0#H~K%SZxkbD#nYd7rC-i&3(E$; z!!fT(_{%=7A~;S@iHJ9w^Xwd_^Jeh*d_SLLXH?Lm6Tb&5ZGPE=09$qZgO8UXj*F@= zJ{%RNC21ztSPQqwZO!?;k;+vdxIm^;YZ1Rut-A^!4jkHPl(a-@h8PM-R7|`O6 zZ|&sXMMrtsPU9Q4w}-k^G@N>FdH3cUIq>oD`51&z%XArRIWHy4)tzruRx<`T-$eJ) zRFxZED-X)aR3fwc6|PkTRYtD@xMLmg9qz{SU3{FtSlASNnQZ%kS*l2w7PFUuFLipB zjW2rNW##%;Zys^fAyP_a+|;nyifw^jiC3S+x2lt_bpCo@+pJ$M42u@i(7Fuw2oNaW zN7yFIj`H0KgDr&A=_X1zNWOO3a9%vnGo+qNY=VRFHLdUT4Xvp#7D+FZZCqb)J0{oL zxjqktp_}~mJ;*p$G!~iRF-eoj;@%irv)pQhg6PtrS5dJq#LT$8mXaQUZVw^VXh0vU zVbYrTkQPQuHfqK?MB}z9Wn)jq!2E87iHrO5IOZmUDJQt=YKgJg-KPLe&g=eoD)0ze z-}Jp`vkl|o^c=xwtn{YQQp@A*TJ#qo@x0ag;70Ns0z|aX%Bzz`m!WQc6$P}Tbp-q$ z^gR;FPXD=Glv&raziFkU8JtNWZ5&)xd6j7%_AgT~Q?pi9U-8oEB5S6b?x5i( z@;3Z}db1d`uQ`Km>W=<8d@QP}a5KqoHuR@UEe8t4XUQaWxeAGmE`?Z~o^qX!EOwX{ zRc~xHc#EF$ydM*KR%K=q>B=W@LIh3gS07=XyR9#%e>9KVB^_JzZ-t}KEk@^S1~?+A zcScKN!Sl8U=gWtvId#3;Ur%omUAHXvCb+q})2hG(P{QE(`fO&BUlLxcFMBp!(E8hJH4rW#F6g;Y`*#65 z?#LZ@P0B42TP&-9=`dVhnp1ky&76wJ1NRo`}{X^5f!Nf=lwEe2)C(|-C8|@4a zmeeEK67JxoUyvb@?s~y3ZY`xQ@6Xb0tphP_QoAx+UiPfAK%Rrp`2}B~6AJujdgR3Av+MT^ zRxZhR^e0lZTFq)04i;(y0Pvd<%TeK(`&u=^=tXMXbhez-<#vEXi&MSgE?_j9$?cS{ z7Kp(RIeq8gp0I;d*luaOqy*cuwzAv?{dAOx#X4v_tLy{%m5}oII5%Y~PqMm0@zK3* zGnu$KxY(&+K!#L&lmYK}GHv|*qlv?IVCr~_jxr~wW7~u*OA`<48k}fOEj4HHm*B8sUA!w0zIURE;v@Q z8Fy!AG5l3}xeXB!+Ql}!1Pc;M)KQ0rTJP@r6FB?XG_NYq*og7$Oq5Lho5>d|*N@MI zDW0`)wED+QmFawEUrTQ97lIRiy$_UCYjCFT?H%a^!3QsEx8mTa$K>RI98hv<5#ZtM znXRaL{!z zT*}DzVI}fmWB%G7)jP7#A=`D;2Yqj^>|nF=wFvow&rqRw8Fxyku*`b7$?}ql9{T>| zx5r#wO>fYK&XWz9ekVB18Zh3LhChQ(kALq8)l%QP_>pw@kZ{z0#v@qBsM{qGeO7+4 z7?pP4-!n4MGkLgsH^w$lt$3qMW9^P;In(md7bv3cf+FsrorxU3Ay%kjhO}P8obY@{ zkW!)U>b8HFkC;D#of)>XvJtq?K*4|TD9Fv%iCUa|$>F2XVsunZ#p8XtAHUWkXT+lF zJzGJ`D51U3G`^SfGelQhZbtLGv#0BnKPx3Rr^&(5&YlhFR5a;`s{OH%!874S{;7%o zOqKJD%bD8?f^o)qyjL}m|2ZU7$|)wh{N{2;7?*cEt|;m{D6z%$&YzQhwPp|WD!%5t z=|?rGk5!*pZ-uZx-zs}SAj>D|em~pSr(C%&C8Np77K|y|Ny~9T`4_QeV?%0E-xB@v zEhj;-q(gE{#`WvSjk=!RCVr#;578n99pTmp{LYt>mUp2!nO>(ouI=MPR)?O4xx+$U zN%yj*S_eBjCD^${F+L?l#Hg^duivG@Cy!5dke52Wj}$TZ8B5o_9@#dwQl;4ynii$p zjvg-xrcM-?vLKP@g zvrN!mZ%5-lxZ#s`fORzuvb@+k>#jy%wr{8s=}{gx zI6B^s9b=(=cbC($ow{%-YAUCrK+^nG7W=b1wrHtb)nfg#9FtcElVl{iIZEMX7M{2*z!c(Xo84G?UN!;mOeCtGiMKs`Kf0bX>AqrWaM%_^f3sK9u_g0WMEUyAuX zs>Uh|14rbC4$KwDBI7$r?8aCwD~IGd1uv!S$kx}OyZ*x~@iEPY#a7KFw24;uE~uSP z8Wg`cN^TF*AFz7une(@Q7c4EY5GU1ot65qWA}4R1+((>zGVLTI`Rn#WDub-}_9=AW zheh~)@q%FT_-`>X^O-dj&hl@Tm7h;o+|tLc&o8%<1U?w4AJLMR%5k1{oC+Q7z6Mmp z<-?MqX;ou^o9=+1oL5c_aNqZa6(M79TTbqOE~TG4BWi7N3F?1`f)3822+w^!~Ul|391pS~M z*Y{UQ?ZO`{^e}HW!?*>vD9wIvgPMVP`YfN-?tb^@e4~xu=baGh#Id!%zdzgO-EAzH z-s#VLE*qX4=9aZZV>N1*^Nq{jn_w%zA0{CO3Q78n)8%|US2zTp&FtsP{aL-~)MB|h zqwQw?{6^8KfYiPO}1N`rOL&z zv8dm1YHYVUrGGt64Tg4R)HnZ8uhz@t^J?{cy55z0-DtGLS=d>u)V{j9S}0S^M?nbP z0H<=(w}7sn%&2sg7{9+@KNE(e@Wpd{xma(3aJX2kT)RFPt9?5u&faWuL1|L0{VwWy z5JkWtCL%(}?@cJC++?L$Q|NdyOBtJESn#fKN?pkxrzJ+pEkrf+P)v26Lctfek3pYNTPW(rr^KQ~6@mJ(k7q)9K~$KDQo2 z!vB7pAA?P&App5eH!G1wMx~(_JVQJ~vUg#c?bYObw))j_vB7*U_!Pw_0`m(w5oRy( z`=03iJ$0>8p;QBgheRKTv$qF+(%-( zAekpI^h%zZi0^ohJ2C`?0QY*A&&NmtmFZ*#m;0^RMB3L{n7RVpZ|~{3Sy@@AqDF&} zf*+{F#P6potivvo7cQdP%|d7bEdluaBl;>?tTH2U14|pm`vc8 zs25jr#qzW&B_LtH>4%$}d|@UWD^4!1iF6Lz)dur?@ff{2V>p?`nVGnK_-Jezl@b9z z@B;GVgn46bNlD2XnmS6{PZg<6CybsS99y2O4u|7$6yi$T(l2)0EFlOeu&~|lTi!27 zv#{R`I}3WIzvWg7NST_NqKh(JB~ZzCdOp=T98D@0$pA0#0rWXNJw47q_dF5-I=3Fg#KdMEQTSiOv1o&>W;7JTI*L8wWep&=MUWtlq6qZM)ha_!NP2Cy zbGgE}{m)U5kTQ8a?n@N&fuU|7&v}4!F9_+I%=vR0gc$7(?MTYN@X8U4)l00*p>)tE!225&|bz z7@v_tkc`e&>Zwfep5mg-^Dvjm$6YSBdpsV_2Z$44AAI%(krIRa1rSvr-27u7;a)l& zPte_&GURl{Ra7t$-ER&jY98sen-FokeROe|d1v#Sk*YhYS<-EUz}a{tDnP={L$ z6dhPGfwvRCuXT8Ew11{En9Aa1LPMYE?CfOvMu>!n80bDY1K5x~s#`d^7&a6c?A8^F z!_<(Q0T7NUy?!TQJVYV|9)(`RW*=$b^MTBYBGY(7(}Ooh)JXml9pM~aJ@DVI0! zhl%=^`D9YCpy9EdogFHFhx;A+8L=r5k1Og*UZ~|#m0+H))%-`d-V7G0P+<=q4UV%s zg$gXcT7`z6Opa%_3^jwu7mUv`z7!!QeQyC%jh0J?kPk8WW?v?FDYPZNH7F9kB>sI=|uJz_CD%!HU^a(DR{E?F?t8s0vNJ(M!zVPFE4DI}Fc!bFS+6Ggt8j5{KOl?wo(7Z* z2l)HvxZ$%~H9MXtX&oy3w$YV?)fOFM3?Uo-0LKNZ!-C(zOLY3X$o1`8-wI$6@VtbK zje%Nvt?qhXZF9Nc^LkFCR_xu{$|jz6+q#>0GJqVm5!e+M7Z={&A4z~gH~YI-Nvl>t zt>4MR4K|!xYcv#Iez55xYXbDY(}3RV<=X1%>XGY5uE1DMfv~RJ++2HtP|q$P89oV9 zEVClXxOXf>HZ)qfV-{p%2%tnLYU0NOzZ8rgzsOQJ9&@FO9&=K~W#$X7KB zzeOpFsGf_ff{N`l4o{$?-KMjZ-JfCph`*LXGEk+eLVp3;Byd_xbH% zOOd%lPrlx~0h)l!X{R@+FoK9>dYn>oK*U_3YUCQ0#Z(iCf#JPYsw^WsLmo8a6`vGA z%Pm?hFE7;6))rALvBVcji_`B3nERjHyi7?+UZED^I-lt+2r}i07c6vjbVSl4U z-oCrL`)8@zwyvGlVknwOHl6)|Qoex{CL=yJHujr~jRwtWwNw&~t*b#ee234+d*T;m zprc{o<8zzO79bOGZ&yU%F!uJ2CetI}bOP4?Xezre$J*Muqobp*x3_{N9NVC8ZmCL_ z;Ff^J7`6is>dVW+MHhhifYCk8qEw5~JtG`~926LcNv*JbwKwGK?EENM-6rG@fFVL& zcgNe~nJu4>x4sY*@o2&px0`=evzJCmMHN6(q5;erTW3*EwxmZGzLB`j5F{vnyF4Bk zUQK{`;qC1W$YLG9o;OERbM?B3*Q z1T)ZHaKa&-n3#P3tOs8I+=y4NYJR=|3TOoK#rG^+o>dE~c5-S8r3v{1 z-k}Q^$Up-f&$vDsq_9+=y5%wY{|+9R)$*J@&Vwu_uc^E%LnyS50$5Fi z1+rXkq9Qdul&*+rCYJ+rYIk-1REy^miz?zsIOdmLIQbQy*Q+4_$`oZAz;aW|xxyf( z$v_UTV9au5fbt&v_d>jVaRSd~391dS2g0%Xrhw+&@lLo2M%td{rTfiQT+Y=TrGpz@3~&}Y17J|%QG%}kh>q(YqIVt83592~)`X-B-(iY8K;~1X z^#1x}_&hE8_I$S&SasMNgiuhy5-Y_m!s>jg@e}SdLO_wYX+Mre2CoNpJhYqt4+$B{ zkGm!PQk!i!3GEJdrws7V+f)r05}e+zkA>2|X7`LrK%c~pg3}yWQewtNAY=cS-`+7f zUPmzLyLJK)zt1;E>=;VJxUMD_K!Ie43L04yg6^?kx{#-E+}?mw1h`OW`I7FwH2`*) zMZ>KHFP6*fB{Oq@#EKQpxLp1w!bKH|I!tVqmC9<`D;7s}A6nqQ*6DDBylZtDSO^eg zgT`*9YSE|d+!~%LovR zpUD_{p<{6D@F7IR#K=gXv7Dt4oi1;pI)c#@Jppj2g&IR)IRzGAj#hye;So}*(=`^F z%1W?--pcWm>HTXp@Gu@1SE<*54~{4pltQ&D=iWn`aq``mA`^x4B4k<4Q zHZ3ZhOeZ)m%^nCT0!oqd2AdU<cgF|52Niuw9W@a{)Q7)Df{qEXR zQbPTDi$hul4hx;~6`1VWSxoVuKP~2JhekyerU~H8v;d&`7(N!l9=o`tmV^fOP=e9o zkcA#~mkt8~R+rG+v1 zO2NpRjl-bvV1;#ZG-n%uQAzyqQ*h~9}4`d1wSegS4IDRZ;ZHforBHH=0Jh$_hP8i9=G)3YNq64)YdTP*5;K zRKFuv$IjZ{5euJ?p5B;L*SN0TCwf=099sN792H8f)1U{OoW_dC3-cb4gpbhNp!)JS zV?Q7A=gzJaOa}6lD3?Q=v zH)e7?ueN&ANALS{f*1KXaqIgmZiOW1b8{#Q*#-bUIVq{$+~MJ20IEieVOKQK-pG|V zK64Qb!~UO0taL65FVfmVC3#ZU;_69q2j1bj}v+8wK*d}y1qU#%krexxvt0UF!lfs z`;Oyd1O8)_KNQ01$_hZ=n6sq;oRe1n=a8K=7f3N*6i|V6UVi?aJMSs+=+ST-1wbHK zt~Jpk?Xy07!~jW<`TeNt?d?6L=&|MZ9*D)l1><9)relOf-_ka8!pz>G|CbGEpMuvR z4y0WL%wzF?J8}5m%uD|NBKfEfuxXf4s?P*p{r<)GN{Y&fR0$dQ|7XxJEG+DQL?ZvY z@?RfB;K0Ge(b3$-)Pcd(+G;~<%VC!t)%!v}?&>zzi&VgSqX*K)HxR)=M+lmg{Ws|w)@6nx{Z})FJ@qDGq@*C zX;01-=(}BbnteD%)#Ewkp5zidcdLr}E4hdA%*?e|Rq6fCP`LbzE@_g0L1L`jB8%u- zUHtX8IEPW)LV~*3lf#%MH5Oa>0(H1t{*5x`jYY?dnabYpzqx*((%jH+q)D-6UeokN zQNH|}3hMHiwHq`$qlVNPEe)$j%}QrQ-8Uazt>;9O0~RA%hG)Mfx6#fu+TPM^tii|) z?Yq&ky(SL!CaRFe19DS2k24fY4l{kK^!jJjTzJnt3aSw!BcrjI#0ebunL&??2Dh!L zC!xX>-)Im^Pq${5%9ub`fezQ%s9T4(?#7TC#y#o}8mx9b50}khq>pcR^W;b$d3H}# z$pr7n$DVN(ADVA}Udd^_2dD&t>{0Z3)(-rs-x_wgR(JYYA_h>A+KE^T(O0oDn(PqA z9A2qQIATYsQ`$HVx@9Uf)+Gwd~q z!=79uxDq9@y!jgRY~0#`vAqgwjbn>g_c@#}Eh}8hOoV z;DtDhmB@1`G6^+B{K0yc1CAu+KmmS?kpH+j8QcnB4Q)e8=1DWINx7W#QnL&xbDeL$Q9;tQ#o` zZ<5738zL9lJ0^I=Hjd!h%d}&NWqRGf zePpJ!QTal|byVR8<|6U#1fm6az9)LarZ%q1ffn1o%NbNSDv^C(a@x(YJmIuF_E7OC z&4<-VfM%A#s(vzg`)D*Cwtc&b?_t5lgLXmsTL;mWt4 zueK&kFn~|+Hq=q|3Z+ObwciXDUHd9!I-=NZ&Dr^m@b^(`k{g;OxjpzWnP9-VCTA=T z9myST^jTiy!>@Qa0MU){V8>a1d-`v|LM)$v|LCTw7B8%5#_z3Jy+4p(+I*6z93pYM z7~a2VI;O+yf{KTB{3eH{x&l{y^)dX*Wb7YH`%8(+lbQX^YN_0h(-(`)*JasEa{XD7 zmlkOq&LnI@bu=8@lQWno$duEZD`pQw-cD!LAfJ>kpEXpFJevKM#F-1TU_AE-p4Qxi z>67~s?a8@dx0|A|@ZTXyNZOKTLtv%upIzWAg0&hE97bhr$MAQOJ;aZWkn;HDeEak9 z$DiNF&z$+t9PV*93Y zzHp1be`cNGg(8@<3!K5})_f=`PY(SdBkYb}OiXeg`+X4L@-19|umLb54O9qvqwxov z1_8YI18+yIZ0^32JZXI6T+JU0qls(3bSFOH8Y%49$X+hS2oCN;byjrhU3GgPn&7!u z%(=^VT8Ah(_gX4&KVFkK>jG1dhJm$~3+em< z1p-Y`f7O@ z#ovCGjHet4+ECmE>#^6T8)^CXn>9s#~Un!{?D$y&Ss^%W&Of7~^aUay_=?eU6YkizY1SM<*fPuaTZRF*6= zseY%YBo2cP?yZ|=GcjAStjS+y(zPaQY5gwm%xUVqy}c1YPlyKy5C@7qq?%p4r;({_ z{sbz)BbzWA`Pu-DY3YJL3| zk7yEpzF>yARH0t=B`;3vG88gnpoEUjq5FpbRW%tlpgKQVIC5_%aq3LKN!okYJ;jEEg;K_l0)c40w|1G~a}aUWaaKyq?5zM5;&)W zec9`(B+QLSP0wN(J>Tl;Vr)UwPqcdjx~=iqYGc^QUg@a0t{3d5^ci1NWi)y63}iZN zWy44XM>8A+cGaJVg7*qxBaKa9=$u`oq0=STjobi>6fb|UQUfRo#CZd#i=E~M&8vq1 zW`6>tk8d{xdC@elzf<#jEk}*K9c@!edcO_<6urf1fht#JJ_^~tE{U#um5=agh{a{b z?8*_9v2V61RS7NOBwcLxm2W!;j>NCOb#`mY1kT3w7C^d$uV{^-ZCVPv^= z_x`?dmQ1}Hit3{+t2oc^{n2Ak5u`W9b`yqBA8QN5`GD7DI9{cyjg`&~5r^&O_+856 z)WyDatmXskC}(B)e-@%DC42=t=jf}|G!#`9IdOToe@^LzfEn-S8?Y_W!?abfYgW9L zFV~p0IjS?44Q2a!q-#{-0fOWBWp)00M80dkZI2#iWAafoho%U&0%=|!((}CoMd$n(XN7x|xq8dY6nr|GOBB`kjsgDwAkci2UBK zm-h5`e|iCZgyUKO1TWy}3DDo`2cloX3k)s(lFCS{k90i#`0x_q>J9~`DjIL;Z)>h+ zhfqrT@U~}zokmVYKFTCj;S&)uN2KNXVJM)^p91iGD#M`B<|5@)dqjU0xxaseC05)K zLun)A1D3@&A%j)8n_c0a+gWSP7lJ9}_Fh!R!UyV;$X*0~`JoM{1_7GAj%6_+rCB<_ z{^23TP+#1itreHCIz3))F-A`(&^$02m(W!sOCd11^RT+57oi%F5h*`R&$w|HBaY?#8YGE6yIe$V?Rz@*i%~nqNK#R4 z?ZKXKs+{g=ZHh3oAu6AJDD?xx@D9@xXM6m2ZIY&7eS4=bq_G1R1D4>p`Hw>{!FpK> zHqO#M-i%4y*kY<~2K!7%Y@HKcY#m)EW>)j}9e@_z&o0{Q4>Pgj&(EPh`3eL$yw3f` z$Evf=r-DR7VUb$V@hOV_MX#;qp6z`oP+)D<$Q424ElfSz6&+lZs2W*I&ZW_s`#gn;9|$fK}6 z*xm>?EMCOzF{mOtdc8i*q+(|m(zvVAOKwi^&hY)&vjv3Z7r$<-tHcatnYb{>Z^kN4 zBjQW|BTM>(f@l$(C_>IO;osa`&TRYywoC@|p zdq*B9qSjoN0Y?iVY&=yQrL5e!lY*l4C|vvm7o}vKz7)kp0t>$+##>FgEN$aOVcvCB zby%Z4{SJ8oF7#OAVu{h>ra_qaj}j&I-CeD!;_MY3F@j>Ue^mgnff5)UJy4MUkpI1Q zv`VFuv{*Ru+vy}4_}i`zCg=BClc+&JpCFiMZZ1=|TPs&n;y7r{&InGl-X?(GvB6?6 zP(X!d_P{`oPrzG{jrl{$_L1L>X3h9lFm1fqfM*zG`P}cpFg=$omtiSjn=R1_f`b&j z+{CW&t6DWgyijh!kN)`1m*hxpp&NdyXiC4T3&}kQZm1`4F%|TYR=;kOU#|pCbXA%X zSWYd(kj1&i1f$8p&9ZFa7ub#rf$YS8u)*h$avw>cqEo7pJc)?boEYXl001+E5vvjv zPePhXJ=oKr3Z~!`vFD*idR^r+y#0=S8@fpD{J28_9E66hhC)-4ljpJL zfrW*JmPDBekZU3R5eP3`7=1!z7ymx+Wo#yOvhY3N%~E5nx0jPf?(QL1Bn1sgLZrOs zJO5lOJ>uBY%~2b6l%iKytsxrz+%K6oC72$3oJLRGO=b+m=>JF&5wa1^!V6pstgkmu z#>tKpN@1YNS@|PDU9iN7(&WWY>^0`!eQ={SE7pqT+!%2QO!OTAie+rBu|?pb+;Koda5qL37KtHYv5vu|6Xg+|5A7hl<^xp zWit-kc#r5Q9k^J{6jl=aFBgQ~81gCR#;1$^{=fx@8Jv>TYZ5eXpdRuXw{_`cw&Fif z84Ko4F8Tld{i2fczpfczYuwF+o-iQQ`l0G!8obUub+N$7K)%g7^F{G%jC)q)0e4RsOrCCN?N2=pX1t z7!j(Ar9_aNm5#st2fc#|e*HWB?^_p3XZ%T1BA$*8_-mXy_>^(z0tEALnf}D z-E!}6*~lk*Z~5FF$E`No?P)ko zM3|uu!ff~|4gtn(0SImw-@?Ga_;b2aUpQO@m$u&Fp}x`LAPz`D(nQ~!E0UeX*dQ9w z)ldeF?d_E+0hw+U`{Qz_Z=}`fZ}6g}SzR=}Mh)o`0K$z`fhk;Oba*Uz3=n0|VPAl5 zX$)}4jl<<6naFJnx+PkAynzQ`08CRx%(=RIB8KQNQwk*&vn zl1}XGEM`U|_CEUR2#A82v6n+JB!gpC%Qb>T{NC$`KX@jA5TbZ)n4O%f*Cz*HX5pcj z1KvdLg%1!k@%r{y1pWvpWIB;ONovD!FB zKywUz&qGk{0jTK)N2|!Pi_DonC|#2~JRTO)B9cY3-`BtBu z8qMG~33xX1fHXN_x$1s0TbN}g1Dike%N)fhweyNUJZ%n-Up7u1#qi2|8&D;R9^<^S z#X?DSa?FIh3BYr~k&NHtgd*5MaM_YZ-qlQv-!X;8AkG-dpM!H5+0Scy`g7?A`oqNF)uFP#AuabH{8=zRr z{eckTPCDUmll8dbWG~e@VGWJFz}(%KtxXizKRHa4jR$I^ z6jZHQKgbnYsHpwh=B#3J#DBf~xO-GDRwp^;X&Y9dl)=9T71YK#+M^+*^!e8pqi72_=B#sg!WHU5Y z$YfX0>!#Y7{Gwy$vH6R6o4^q@`eU~j-f`y%`R376u0M7OMF@SH*1%E-i@?dg845Aa z@tSsW2mlz*64I~0Ew(i{Yx-e z2-I)@mcCPE$AH6*<|&v2?xgQ;;i*`;v+rX-)WEosYKxi$W>sie2?N+$F_ZaG82BKD zJvxNsOyV?`u|dlsu~GaHpZ6>0a`b<^X)cz`I&9#zy1^yNVGG1E}oV2{>^?o`P08^T$~97g!oMW>nJ#s}+`un{;wUVGTI47Px`~ zr!>~ouT>RwvEQfP-(FO?KkcT~Q=vhV8AJw`+Q;Z=9Z4Y#OIZ&6nsjdh?X zN&5ardtV(;)z&%!*mF_N+My7AY@&EPcNE4Ozc?br zIxd;F;N$3}27m21BfT=cw`ZRHXmt1k0VbFy2p>0vmwid0CH=wbs3aTfd{8bnnWCIT zG_He+srt?LfE2qI*qCPur!06IFDnGHUEy%T5CYIuSuJb{6-cp;oyL%51l0;wR;Nl( z?2s^Mn1KZFy-q8Zas7bWMmaK};k z>26_wB9wI2PFPbf6HoH@fLn#?BR&f zio@vk>uB{LbgzbUaLE)td%ZaAbWIaHHe>2Xfw9 z%hegEXa1x!zO=k7n*!$!Q64f(T^0_M%{)%d;Rp$c3^Bu?G@zo?3Xq-)3Bjen ztnbx-OnND`rK@?)()ab$TkZa2>ax3$su=@mLBIb-=u-6k_k_;n%g&>W1Xc%6S*!ty zlM2rfj6(@(E3wW1KE9Xa8LiNLpLcwwRy99RgtN@qNbE}d76)1zE!a}#pK&8bC z*Ey@Fj7Eu-Y1BPOF5MF14j4#x*%UX7cWe}7%Yn!ThA!8AL7;G6P`C*wI|ft>{MQ5D z@q&1HK__2T-bySpdfzg0n$D&I`35AZKvB2?&6$ zuLadF!~l2oU~r!as4oV{6FhGMng@dyVt5zo>lgZX7v@bC!1W8@g#|G9FoyRK38Px%tfHc#s;a7{rlzj0uA!l!si~=@rS(PhVf(z`)?$yLZ6*Nx35kh`Nl8h`$;l}xDXFQcX=!Qc>FF668JU@xfJahxc6Lrq4&ca|mk0PE zNV(9Mf26pfC_V#}L z`n9jG5Ae$v7#J8F92^=N8Xg`V85tQJ1-$>p#>dBjJrt9ZlT%Yu)6>)6zI~gSnVFrP zotvBc{{8#>{QSbg!jB(678e(nmX?;6mx1j&tE;PkGbr%FVPj)sb8{2e-Lt*Dy|c5k zySux$x3|B)e{cZ!l^z0HLXMA*PfkvNojRwdr)Otp=jZ1a7Z;b8mseL;*VorKH#fJp zw|94U_xJaJ7v|&RBhVyrstketL2&iUxSW4?Zio+e4)nYpPD0poC=e7U;kYC8|3CeQ z2zofobS^Cs;+yDDQEnYh7-9Td1Y28S(1l^p$J6ZwuP108g*Up3bRC_sgh3NRIXpKQ z&w3@}^}*iJzTZR7);rtLuob$#N26@V!GfU8>~Ki>+n~3vy^D0&*{V8}$o#H;lEe6i z;Ly1a?)xK2=bp%IZ!${ir26Z12KLrIN6AqL+zH&fzDp1ZiuYAN_$Ea;^@%J|l#tv9 zUe!2cmBy~0M8E?MJKyW(sEbhVsXbqGrcoLE=0GRYeE4tSFwbD}abf6mH8Kbu4<4T2fS=4lA(`Gb@B*0n(6?Y_XSg8K$_rmzc$_BT zCn>j8O>eML9>7+9xRb^F#ZRi$WtHqmtAzE}9^pT)Z!sSvf!#SltHU6m0>%8Cm>mUIfE`mTx=I^tPK@w#ryyK9$u)uC?61EPM-fAR5zp74AFcB%Il71*$ zfHO-4;SqD7UQfto@Hpb9ydb5!pD$tTwwD1fQ$=th9T9L?n33Zh+GQn>`T60&>BFah z_BRnQaI3bOuv=oIzU*9sB@$P`rjOpsABv9+A;R=A*?2&e9p~m_Vb=`dm^=E=Po6i% zcCvDetQiC*!y&-;t$vh@X*T8rnw|U=` zg~f6smNv~k+X;M6!W};+8mHVD!m&Bf|Vkv??;Lbv<9lWh(hl;fOf1F!K6R|z+DW& zc@_Oje4rEba3Akv0*^w>IYp)rfIRvStHxixf`LV!>X#coM}-hP^qYd@F8$Q3nHF+C z3?+9~8k0qViY}ZlfshFj2C+x3z{<^10}rV9C8k(^(W8E5<^~Nw?;3M1#z6{tJs6`4 z!SUzL3Dz9w1PYcv!pBPkP<%%SWQ7DFEjRHmX4bGcGk+l!n(+ogkl{HY`Jj&a6uK-# zJ$(fciZW;Bzu3g$^aQ?(1VI0m7)MRZ7m^0-eP<6s0s7AbKph$gPgQKpz47{BUBp=o zsUGT&CQrHs<0o_A5y_J<;&U35Q>X>#pF;t;FpBX4{S;Edo}%4DY%@TN6WDUkAY!G- zDT&FUmw{Bs9JCvG@Jro;+c$ZzNI)}5k;LL8mTt74WC27uu8(ZizDoEFfU9XKe=J@3;AKXm)AYsC6`ADr@CcFv#}v+_=QEDp$reIR6+Pv z5e5s;f4Tx6xxRfGFrGb|1s+|54?1e7-18>Dm&h3&llr^?CKa+7I9baJ-PxuZPFR3c zNU7`9aTzsC{*%|Qh6}nn3LdYxy1~iAc_?;;;}Tx3@FoxPmEfgVY~vNK0>4F|jCs&$ zp?AYHoD}1}yV^g-2p(I}VeE5IIJ>^;R@^w!*j310_ORrG^M7w3=~$ao#ET$Qr&5E1aPBxXrC=D75DuwSvrdihBZFj*o3+{ir%k7mZQ8q@TFx0YbnM=#RH1#6j?hd{BD3q&WILk?76-$c&UuO z8>V@0ml5YS87QpTkl8q#ESOa%;4@xUSDqbF|14TV>}>PGPxQIM>#nhWnDVp7AClPg z46La=F{#50WMmxoU-~J>%1;j{x{uFPPSAkPNGlO<4?3${q`R`eZyF`9bUDL45DfFy zqnQ1n-`)~AM?GTUxk1?Yjj6&T$oJdU3vIO&-(;*ndMKrVCstN6_!;eA3%r6)p|E^F zc?J`{ZVp6iEZpNxqAV*laBfsYoD)ZqEEvKKlWYu&w_rF-&@ut6K0VRGrgaaruKWj0 zRO&^aRXV7xTEE^-VnnQpI1%U`g*<*~5Oj#M1j62kb;y@F;RvCX1tJ5U8W9fPX#DPO zxgCivZ?m7*iK`>DG1NE74T?Z;P++?Q2$6jLGw-;KjsIfrS2vQB!A>Wly-2GZ!^!N- zn_48Ta1MKKPukm{RUzulM=&h6A!C6fLCDYo9Xtq1{ELrdYZIy$-a>n2gceGNDAb`O zH(Si9zqZPg(u4wA-wJ5I*}x97+t2IA%%1QythQ$bcFu{ShyO+`L)~TJmbCF;Mopn~ z3VMq&N!IqWI}gcYg6KtIe{0K#gK#nt2o@cckBN?_fnu@u63obil&^gcXG2cKq6xi7 z30tn*q&UC%0|i@W3I2)2duS^@9NcjLc+%tGO0*~%i2`)%I?JWpr5{0!;i!&iMBy}zRw8Y&K!8Rk(pO5T z)9Ai5_Vn)Xlq|MH!$3F3M=&|sSr=i@F%&WahM)=0^QV*1A_@3Za6p+C5fW2mJ5WO@ zbWmbofPy@(Phfv03}OKRBM6Pbujy`$iH6UEvUn|twofkCa;~<55Uxfe6rV=No1N@# zUX@~bV1zb;J3n*x8Y6EA@s}L-K2&;e5SNGAef=K)>PAO>MhtDkX6MzR+Pa5Cq8p^c z0i%R_c`x}_Bobz$YnL}_37tq9<{@2uu)tWe+BcseoGP^3nA+G@wZ#502 z9=%z*k&Y(3rz8mGq2GIZ-qj*$@IE&uZlRX{2s;89Ag%6*Ft6{l5hmxw8S-e1H&tfvIY`V~qF59u0yfRpP7 z3;9Cd&nC^HfW5jH#pseH9=N-Y`EqsxmtAl=R4F&hj)5wf$#+pfH4Q_(bJ@cw?mj*6 z#p!OJTFdczQYG)lABDK-$IF!So}e6YE;Lt^05X^|<^bD20Ntp-@Zz2ubh* zfV{=*70jjo42s?DO7}}=lSCI{K|m=|a)POUT@Z|lRRP#J6M6n`AQ~&^ogZn~Z;BOY z%o?pdN{?qUHc*P*k*}7^nX417ei)s90iaJ zU)QT?e|=UWidJQW_{Yo!rE69Fhz=X} z{LZunTF6GsHs{V>N^57Vwy&l*FCX+2pm?9RK4uC91^<+QZX2f^9VUFP2#pT-4hs0r z&*u@Aw-?1N?z_-Q=S!v*Jw&8bVD+Y;u65Sgi5pl^{-JvYi^Moxtz19(uk|5Bo{jWHod!CF?F9Dz;%>A!WNs z?6E85lt>nf3kPsc{@J;~v)OoEX`UPDa}-F7HCMfnm?^5i66KtUSFCz+mM#?<)Q z;@PuK*LHFb{UkgXTsRK4Rd0O?qzxqyPzr^eCAVIqCuPX=?d8!{052PbW;$uiJi@J6 zXo-EG!Gn$}DcqnfaAh$wD!h1j6oxY%v?|L!6&_G&HxdYR#rAMp#;4_Yjv3>shz~I3cU&6*o_xCqyy)Q)0UR^{hsL;$WwiHo0@L$fIUtnZ6o~6Vbt7ux z)4^8Gj@#VnfA9(yc>`6VkT+`VxdCYfRU8GKU8vLhHwvyOF+;a0;$R9S|G=CJl6hm# z4rfl79R?v+PYXam91sEVzy`PpkTFF2im0JzaD2!`94he*BtLu!h^H+t%SbThmu%M2 z^(^nA&sU>;A$^Cp_!VS|nh*7BrYiP8imNYgX9nc`?-{nZQkdiqB?Jp{k{OIZ9kt*B zP#27zoYctMy&!&|*Tz8;+RKnsH4B~LcxjS|LS4J@!i8)ISu82>YycxdEarHB!?Y(q z9pdh}A8VOEBS=pkA@TnIqALxWZM@yX*I}mcs~XzVe}W#T()uyV&+-z!@08yMMA`7I zv+o2!i9w8|%pZ;uAYM$ZYFk2d`UM}Dj8ATnoY(N;qrDd(Nq|tK1Nj@uV+ey3=Ue>s z)DYl9_IGo9EE=X!Z>*aD_4QgfA7BL83}h2j*IJcbwiWigrg6`}z( z7Rx~@umTxLAy&XI( z&kN7_N3s~7mCVWi1fpqq4A1r_koq*o6^PyRf2o-7=OZxv38Z1oJLgXzuI?J}bbkVA zWVF=(6G-SC3Ig??K$;w!$^QhB>fVV!{znjzn$OV712G%f3w4>Yo>`nH6vCitlqmff znzy)mZ0>O+BGYIV0G}vOaw~|+9Ja9wZ@lzfmGTG>AnHkkY!qcfCtXZa!PTayo)u!C zA_hEvNg-z*SKuZMF;5i`O((_DPRr#FV}5C2jM0K1POdNwhG2-jNUAFV=>W3+&qUVS ztD_UBvnPgb(JF$msjFuNLIIT-5Tuc zJ0N`yUPNH&fL@Jb-VPrMYaqbq+9be#B;CgPV&nunIDO{KE-O5tIsB+xc3j-qBn*mvD|5PkF5GAz9?_kZ*kzT$6b!Z$~j=<#~0f3~w%Qevc_|R>F;>enH3Oux( zenCPK5(Hr7gm^sWk(3>Pp4+n zF(Z5hZ|`zs$3HDeUy=Jap57*C&juU8jkAr*%?LlaDKsAZM2-olquSdJz+l)H)0e&WZp9}a$;{_&QBWXE-)I7 z!nTKECT${ZMIozWZ@I=J6=L>pAAcz34~HEqp|s@bfj!lYS8%+91sFr%fGUWAifsIh zHh2%#e4+`p6L$F0n zq7@&uX!Td`qt8-zwv&lDtD#vTJrHLc+{QqE&(|HPLgC48HTo(OC_v`z_LV)bGCI&H z*JaUf>0e?Brwf$VztpvF4OVsp9J_TVMC!*4L)Tt`ru`z^%UIw3vR1rpB|J=R1yW2O zs7OakvU=qEj?fWkrKlKp9WrN!SP8m%KMe@m1mhuqaEJBY>OQ}}L#nKL%n0PN9BBO) zBMT_mdJbT|8_-RuFd$@HV5%?7!gCEV8F+pICPM_$0S`F~sp$u2cyVZ%5$~d29DJ`s z@_dj)4fMPL6rumLL9QPVR1BE45(nKaAkY9@PEd3> z-*4pn^^-Ejjs}oToRP{h(dlgHZCC2#E|n_?F+OnR(_-Jc)jmz1w@}veI3aV_jKn?O z`61Ce<}*mSx6MZM^{zKr`>7k0PnW{J5G6+sGiUi$0=tYx){VlicKqbwlrl9(yS5Ey zFz}O|6Pr@vM~4#FRz;OrHLcP;HH|&|Zl>2ISsOrlm_T+Dx4 zEyM11uhR4fyQJ%VX)W)x_=#AY#GIa$&v2j5M&(g&$BB(2@&c6|Y2wj`JyMzQI3NF+ zYuwS5Ig~BilhK`|G!n!@Gbu$}gSzg36)W`)IPNb`SQf9LM3;yvRXqiIvTMeuAD!7< zVdhTfY-bSPZh~`IXP;kEC*TjvtH__4_24b@%2Hw)W4L~ZD-{yTuGOh36?BmVV-I%* z=<{8y=T8E;fA1{&yUn}6<}#3>t&Jn(H-dm_Kh$~auwIYjbzIp&7ca|)`YN_r=zi-ooXApzn>ul(SIzYi)uijduoCtr?jCZ7|ZyxS8^P2l>XtDY5F2m92= z9}QvKqcOzw@}4`YKkikp#QO<{L$Bzn9y};{T)ftH-S%qQ*6n2P542kjN400d*P6qa z);E+BCvrKCd821P!gXom9Yk={w~-=Kd1oJ+q&1b2cq?VH)9X*MKdLM4uSquQYSq47 ztY;gnNc>oP$xO8BwktfDULUB4EBpyBJG?&7R@G<(E{C*8Xgdmc zb#1teVnu-$#h3YpB>pY^f%>!;!P+=axm&Y4QwCTaEG(2VyPXf*i^Yq&U7 zramdk(I}HcFG_S!}#-6rX`tdsQ#i zNAvf=5n-;=ZrH;0oo41;Z_}X%ZRAKozmA7o_s25fD5=A6+LFQM;8b?eP1{c&bg?yH z>-3Y)(LdQkA+|GJku9e&<;lL7$(!=(&alBRw7Yy3 zoL$#X=WfICKdry)*~NwFw`r~ib)O~@?&w_FJ>>hl;s$vjT1d!nm9Af~$mqu3v2Us% zIjr!^WZVuvNg*p4GIjMRjeGSN;ih-K^d8X}UiZBO)(IxM_-nsnZ$sHIn=2n}OoN@VdJ8V#}Y0JJ5JSL%*{Q z&stP|=ujn`z=^TSjcq)_V4qKfb$LVejT(_OSu?HAI6TB0HP7>dYhQQSG_4V5W07fe z-WhU}F%$BushB&}_V8`DMow|%y~-o!_eEU8zuI$eT{`VEeI3jJoH9l8Th29PL{)Z#K^S2odOdMbt!hMJ zgn|gDM3Gn=V|Mu%aozLVZ1uI!q^jmp8fG)%?!nPRb^18C3y%|uSBI{+#uAYZ_D53u zaIPXxQf%^cPLKiOids(M<`^R?jeA6=4uMlI1?hyz^1l3cD&F zrWAweX=t z;Y4pENkos+VaY7g23h@K%4-X)()mwH+**>$YR%8HDRPg;kLvAhK3neT*-oC|qQIId zl<4O3rC4EqGzs^{O| zX0qMwVyMGS?_0_{O1VH|ysat5kJZGLkd5cZn(B;myD`i%UWMP2IKJ`**Y17z6sL71 zM{sV5kts5Yie^_?XN&jKw%s5Rqz84}tkwG*BN1d2CZh9-+=hut^ZGu6pwge0c+>w& zWf6(p38|0&Yx`7gLk9{?q^>D3Gds=z?FIKZMAYbz@r_rMoV_7j<14S$ z9Gv+*vWl~_h*5J_u`Z^js3^}lALCW(ug`9vxok#LIsRa;`lQv)Y;HX{TBk|EY2PZ0m;*G%}fxVv;LAsn@0W z%jusVuFrw&_;HUe$-?u!S~!0=2{Eg;j&x^!MP`%uDhpe-m+KYhax~Gqjo+VLPIwjy>`?cV!iaBlg0^UV@dnE7~tj(Lp7`vM{9^P=|)UIyEo*YxJ+p^U7 zvVOD@XB=ngvfpy>m>4I2Gt$b%0*F0jLn@Rm&QCRnK%A`>G>$rY;`+&0DDQPA8;)uJ zH%~-MS{r0OIo5WWI&6`x^tTpTF+MlisopYQRoIp2QN|_hw=3GM?~X|3uJ(4^T(*NR zr%AY-+MI*hzJQO5$xZ`e8zKe+o*}>gSgSA57E38}?55}8jmFr+C^Vd>_sMeLJ7wP4 zP0~2yR{U=5`y|?4k53f#hnFl)8VO>tGHVx$iZbQ1Zsu?1TnL(G%SMo8f;IVN=%VW- z@R(&MN#^5MjVv{^7f2#3x;d)uO!MbEKGgGYsJRwNyABO33TSQ7CQZ#`!Cx^7){i;Q zDZ8fTs1P5N6>^`?9P*47-oNP%3J&QY2&QcQf?RK4J^B@&cfnudH94%icG{JJc3Lp^ z?VQd$3_`BXaS&OjIS!9Jp$Bfzh;>m-wPr71-bJ4)H_o5zkMXqNCYduma;g4c^&XRK zq>L!V`5MLqRW%aH<_EUt|MoR~i%Y(~cJPqa3w$Uf6c{6#lim)#)WdvE66T|gE}VY7 zB3@GYoKT|7Ej_o9g4d`}YFua_c?FsNr|+yn7CHmi^*pmYk(=gP{76Vk4j%usEJ{ zxb>j0soy2oTTrzKoecz&9Ai5_MdtI5#&&j|lOq6D=k_&)>Z<6*vSg*Mn)jZ&Ym=pT zAREpWt6Uk+au{U2MsAVFJ8|LqfZ8bdVvCfOy`5YDjV9fHjW~lyWu@_9KASIVV>y3S zf6wzUGN|%^Q}aA5&n!pR)VA0judykyQtjJri_k?@Dm>TGMgb1@HWKx|IPHy|ZE}GV z6u}CyXNZ?+!&cgaHmdjA1j&dc#klaNA<;y-Xv`pK=OgYTBWT!*|?ut_S@Y7Lx_8YFZpN!p8=u zSJ3Bp)PS6-%Z6T(-dOrTcb0!gXY0y3x%^109d-~IgM%XMS$N21>1G{%GHJ&t9uoW8 ztp0K^O2BRRPFfY5XG+D4myY~&>yww=%Ka~yHX`0o6dCsaB(($%r+z53urEZl{*+@x zh@3m#B{Okj9Bj6xPpkWx!aXpI?epyRVXFLnsOfp$UR-O1+5D92p`W%EC6q@Gyq~>6 z{=q5=X3~rpD&pT!ZH~c%^^Uf{6vHQhAGg94nhxjj)g&JB!KF6fam8=){cIwwLTLZ# zRDcq22&HFj?&E{aP%M55U2%iNqd7_EFaN{aZ31q(xs-N%q@yS29a)l$>_^JZE8SJ+ z)$s7mr4Zf&ozoy3)uA`7EcsWDL6StMvB(-XBiK`OF8=0Gj?&3EOjj0foXeJ=+g-!w zXQz9a=U#kc6otvbrheyWgHph!FT^1f&#+Bf8zJS(&dtx+?aL{t$DT(sMn62y#Ko~< zbD5^^PUt`>90_aYNGTkQk@GVYv?$}Fn~x^1ZsO;y9&WW)jowbTElUfG?4)K(M< zUPJ1gMYPmKD6E=Qm08nnm~9+~cctFe^3rwU)mHDy!NpKt_3mwsj_oXIhP8FG4d_L+ zfUOdYodsI(;!lSmW-D8dt+var4~k&1)hp_Z`|FJjG>22uXV1JVdHW88k0CN*EZgh49;-?)1$lkFdTn5b+p{^qZr%6>1et!3q9}R!iQ(njo132tOv;CABujc z7fj*)#BrQJ8xTe0Z|!D7{Q%L|LMe!Qc|#ALz>V|40ZTx5jtqv^CaW4uC(g; z%fQT51Jl-7*?fEJmM~7->-~&9uJW6GF-zMt9dJoR?WHAQ(JENj{zeF5ozb%Nf{al? zpssiSm30PeHUv31x>_0k3aoUfGZMGZh|#u9uQIQ-Ed5iS%6w-1)1s9%gKAbJHHGwvdO;5c7va2WJuGL)5n7~V{si6trKY}!QSh=hW(YDB{#>!Ct^5aW$y?R$IT+vd?VX3JjQwKnsp2Fzl4R%^&= zSa(%rj>GXCuru^!38PJpc7;~#VTpVR#4sBq`asx76PXKzK^I}+V<0UZ(x}3k zE+B0aKT%oaWO(SNrQTs>3$iIUjd)SUP&lQnDl26JOydgQd2Gi^DGfZj-lyyVJ8=)g zCrxr{s);ypY%a@B6P&2%8|ebK-9+ z=Z(g-M4G=wDZhR_$NsFxBW9qCzsU}~_@b6@nz4(r2ywWM)UqqYXyNUG@GNZtZEBl1 z*JL1cW&B60Kjx zPjyA$?_SeS4q8VDg&4a9_G7j}bE^=WxvvIa{b-@>rFzWdxMF+U!F)T?Nb>O1gM%sr z6}v!u3Jq=DgJLB8IuAblQM2qm*h5wUW~l*RU*WM*+keZ^Hq-}}?nCuw&KQtw`LH6L2l z%%RQ#3x=M}AHEMKS@VcBU2B!-(Q~Gw8}j)10^2c!4cQZC-5NyZRANM_gsji@Z}Q&^ z@=Fv25{ca z!aAlW3R?-|rZ=5-Kp)*|`L zDyv2fsi|802?|Tg(M8g}1t0M!*=mUN4Dzp3y6DS=<%~VCszDb|LAlZMV&PY(u;e~D zGok*NJBG{I5{$prh^2UCO=YpRB;K3t&3O5$6&FpAY?mvS;gF|fvGFD=zO?<3`%T}o z=XgCG{aF6p#F9ScX>$Y(*(_FXPHsfA{P|}IhLp{Yd8hqn4i2w(hSv&xPF@pV7vx%u z7xucPn=vkK+u@9$qpMb!zp)%5oN~>SRuGudL{+sGE=;QtnR^H-wiTf*`$8)QHpY`Y z+Usy+13g_FW;7aAP;)eVof^xKL>E?Esjar%soDeEudeUX8f$S4vhwM^cb&E0fsJ{B z^Ug^OpMFe}vnDZk@-@YscD{EgZk0gGc97L^ju*DNPUiUlzL}pGoMGv)7n_Xm{t4u>R)~PTE<-^d`tT! zlM5&1m~xF-T$|1r{ku||%s>(iQ9@%%AOd$x!z|aKyV_ukgC@u~p0qb1Fr4ml^a<{V zU1dty`rdo&UMWGVfVyd%d5MHxBhzQ9>@)eOl3y+)L!$*YS;E98DEcQ<#A)Z=2v)MN z;x&zwBnd_vlDEi8C-tgYp(A4+_g5K^Uc|CvL@)FiGH2n93E{^+)QZ+m35H`OO4`QJ z$(ZZcfrMo}-F#H|a~Y)R_mqhlMFoi!n&RXp8@a;+V3klsxIrp-7S`g_ux~;bCEn1L z2ELJI=y|hO23JXLa0}Pa32!ICJWP!ufLHN40aGAx*N8yG3Q;Ma9IibIwsrgzxh}tz zD0VYYDnrIlkMjPBU7SZQHYMqHP@jsY76!%CTy`9y1}0r)Xy(YCp{d8bTF|Jh&A>w5 zfZT{o7r{c!Gakp(*X`1E`>+E8dt!z`tW>w`iz`26xRnWOA9=psk_BBN7-v5oQ{A$9 z;yz^CCE`xr&>GqOU_0p$i6wiUH->*>CjK^BTKQJf=G^ajI8jKveGd1glU^TUa4;?o%uIgrL=!e`61V)OY!!AsK%y!PL?a6JF-O= zRSZj_zFaL#R$}rlrsQ<|^}5XWz~qsw+7LcUx!`UyA>yy$mA#=21s{v-Q`t2>S-Dp? zR|!)sE23A!1$*#aEHF zuv93f#sCvzR)ccP9BmOfHL)yklp&O@2_tpmvMI!d0@39f^Xzlu&5=>KPLiI|d<7+= z1mRnQ$xxBy8yoE7vGl0eiLdbYnA>m@9rJ`!8Q<>_`;i`4H?4n8PU^1_#GO2R_>d$l zhv~tR{J{YOFyy z3_We%l@SbV4AT|uy7=lETYOBC7>$g7Jln4SUatP`L4m)h8`b%t@|=%eFz!e*W?j@g z`^K?vUOc}yG4nV(75%mnoDnz7)A}^PWHt@mV!CKzSg-79;aw%yxv)({kh9Rp)y<1tDt8qSzOW1qP*z&+RQrZ*%@xI z@E{xveIYxn5-)0jo$#_Y`A5-%225#s;sIl5`fuCPH1-is%ZtCQoz%xTGa|srFix#} zzfTa$)=;S>>WQ#HfBu%#MV(SjEA>J1p(I3ek=(@DKV<^CS~TV}_lY*jcvje49Z^rY z4Z3B#(gfZ+mql`JPri_mW|4!VLvrq8`0DQqB32Wq@6LF9G#cL~(Ox-wRV8ki@lPZ< zr{z{Vk@Weaj#Xkh2V2Sn{ra!iNU&(EH~RIzaXVTJJk=f1y!n=;ggTq_LyK2LXf}zh z^UHPagk4%9zs-vg!Z)=+Q{|-cwYthXMj`1a_VE-p96fRs*tx&XL5&0GJOpdBvytQI zJiFvJN|-1FYovG)?i$AGMkw~tQ|qK^6w$|WJpw=-#x@2M_bWi(N>ZT_hcxvE;CbB^ zdvcp`t8HcG`AVTYz`GjdMR4HUH%ABmghwB$%U==rbZmYZRw`$@rt}SFach`K*&{j` zR!6yLYGX_F=bf2^GV1eDDS>E&OeX2(iRW0ZckpYRw6qsz zC7T{oLYAM3(h*g-v#o+kSR4gd6HDHF{!ZWj+SNq0n~!HZ^24~C-+>Is(ql%Ctg8CX z*+BbQQ?l4sz1>5cbJf;*N`?S3?Wmg~DQj&}2hk+s`o;lY>V0`o-cTSkx?yMK2Cjai zQK$HvoL;@$GFyZ%W06jhx!%WD*P_g_mvpwV9hp|9(%_?a)MBXExAYz7cOCs}{3G~P>q=KHJR9sK zjPeiHb>8+?etD90viYpKr!HJ>@0hjT*&m_dR^o@xxY-}cW3Wa_EI;2>2I4O_u3T$O z%ePH_ro2?rO>Ltq{ANo>CWOP)+F=!6MSGsh-e`UyN>kc>^z0b_ONL8W*2Q+y})X zU&*qHW^1-OTKCuvwJMAWtSvrUaSEwqo4AR4*%9X)M|Cu;$;antGrBJNZHiz0reGoN z!!$MS_w8ZM1_AV-`;TSl;UxptT6x7?(C)WJ*-=ZJdo}nlggjcy##f#0vNz2pQ`o2)>Gx*Xuf(O9?lVtTCiClO zepdBRt`(!Ef!WZTBD%#>-*pUA7FZQbzk-)}X2_l>IW&x#luI}@NE7^kZ8&N3Eo3=y$_>v+A<0J6tlFDa|&&j!YxfxKWa5T5Iao}Jz zFf=r_G6rsK-?JJzy?_7j7ns1dU%{YYVK%lVCV2(WQzYc z$d0zQR{u4Ne}`paWozJQ{J#|pBriJy8)K{gWf>u|{Jo5S_wpJC;7m?JMTGg)8%b7c z0~>P_V~1a_u|KN}*mSX>x1Qm|9*4$PaZ~0bZT^6H0L`Yt%IH$EB0jdgQSd4K$Y8NI zG(5L{we-PiELp14AI7XZVOSBfmk#twwbLjuzhRL(BX{#18=iO#X2b~Zvy;1mrhu34 zwM@eBO*me`rVLNYej`Vg$OM_!j@@VuIfmN67MQUkR(Q~?qu)NSpD(QmPKK=%?KvN`VgLLEV5#m7@>>?DPZ zRRH~?T&U+=ht@JN)(uRnztyUleeFH^31j}u&6+*tUF_FSq4BjpvEt2ZUOoHy;;6mw zxMY2#G+(!9VSClF_SSaKhC2TH_b93EqH`LO=$Qv)Mhse9HT$eq$V3`J` z8l%LiLx<<|v^ zw*qB2NY{b_+_iF9wB&NV`RDNq0e+;AxiAer?3S;_neDdEge=GH01MU8f zc?bk1_+N$H{VVCeMh*L&w7bARk^YAmVt*z5*MJOvAT9k@(!XLd03ZJlp5ZsrzlrjX zo+E#*Mr9xx0#J#+JcRy=^~V)Wh*!z)Ny_X`J|=%f{G$$lEO`E&Hl6;s{P|Zr2;6Uj zJVZnMo(=;3b721meeoO7-x|a3L_i>je)v6!1^y?|KWmFWcZT0FK_G~}_&s_49i=~L zkN+v1&woSbkJ{v)=|J?!@9FSw==_Iv`DbE(?z0R*|0$;5wax#OivHhF`Ca?`GnGFJ zI_Yny{81bIGo3&4H2ODme%DU_qVikk`Q!Kq(NDi8*x-NARthq3@DMO8;7) { + if (m/pclass/) { + print; + next; + } + my $line = $_; + my @f = split(/,/,$line); + #print $f[4],"\n"; + $mean_age+=$f[4]; + ++$num; +} +close(IN); + +$mean_age/=$num; +print "mean_age=",$mean_age,"\n"; + +open(IN,$infile) or die; +while() { + if (m/pclass/) { + print; + next; + } + my $line = $_; + my @f=split(/,/,$line); + if ($f[4]>$mean_age) { + $f[4]="\"Old\""; + } else { + $f[4]="\"Young\""; + } + print join(",",@f); +} +close(IN); + From 1c0c09ce387d51d4fe0bb5943c4f086d2bb98ee4 Mon Sep 17 00:00:00 2001 From: Ole Schulz-Trieglaff Date: Wed, 16 Dec 2015 21:05:49 +0000 Subject: [PATCH 2/3] Some code clean-up and added elegant way to compute entropy --- 07-Decision-Trees/dtree_simple.py | 68 ++++++++++++++++--------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/07-Decision-Trees/dtree_simple.py b/07-Decision-Trees/dtree_simple.py index 08a1ddb..338390d 100644 --- a/07-Decision-Trees/dtree_simple.py +++ b/07-Decision-Trees/dtree_simple.py @@ -1,38 +1,25 @@ #import sys import csv -from math import log -from collections import defaultdict import random -# http://nbviewer.ipython.org/gist/kevindavenport/c4b377f9c0626c9dd856 -class decisionnode: - def __init__(self,col=-1,results=None,children=[]): - self.col=col # column index of criteria being tested - self.results=results # dict of results for a branch, None for everything except endpoints - self.children = children +from math import log +from collections import defaultdict +from operator import itemgetter +from itertools import groupby -# This returns a dictionary with the target values keys -# with counts as values. Needed to calculate the entropy below -def uniquecounts(data): - results = defaultdict(lambda: 0) - for passengers in data: - # this assumes that your target (the variable which you want to predict)i - # is at position zero in each row. - survived = passengers[len(passengers)-1] - results[survived]+=1 - return results +continuous_features = ["fare", "age", "sibsp", "parch"] -# Entropy - our criterion used to create nodes in the decision tree -# https://en.wikipedia.org/wiki/Entropy_%28information_theory%29 +# Nice but probably inefficient way to compute the entropy def entropy(data): - cnt=uniquecounts(data) - # Now calculate the entropy - entropy=0.0 - for r in cnt.keys(): - # current probability of class - p=float(cnt[r])/len(data) - entropy=entropy-p*log(p,2) + survived_idx = len(data[0])-1 + # need to sort the data, otherwise group by won't work + data.sort(key=itemgetter(survived_idx)) + entropy = 0.0 + num = float(len(data)) + for outcome,iterList in groupby(data, itemgetter(survived_idx)): + p = sum(1 for _ in iterList)/num + entropy -= p*log(p,2) return entropy def splitData(data,column,value): @@ -42,6 +29,21 @@ def splitData(data,column,value): res.append(dt) return res +def splitDataContinuous(data,column): + up = [] + lo = [] + mean = 0.0 + for dt in data: + mean += data[column] + mean /= float(len(data)) + for dt in data: + if dt[column] > value: + up.append(dt) + else: + lo.append(dt) + return (up,lo) + + def informationGain(data,column): values = set() values_freq = defaultdict(int) @@ -58,13 +60,14 @@ def informationGain(data,column): return (gain,values) -def buildTree(data,header,excluded_attributes,offset=''): +def buildTree(data,header,excluded_attributes,offset=""): num_cols = len(data[0])-1 best_gain = 0.0 best_attribute = 0 best_values = set() if len(excluded_attributes)==num_cols: + print offset,"No attributes left, done" return print offset,"entropy=",entropy(data) @@ -80,20 +83,21 @@ def buildTree(data,header,excluded_attributes,offset=''): best_values = vals - #print "best gain is",best_gain,"for",header[best_attribute] + print offset,"best gain is",best_gain,"for",header[best_attribute] #print "best_values=",best_values if best_gain == 0.0: #print "Done" #result = [row[len(row)-1] for row in data] #print "Result =",result + print offset,"Gain is zero. Done?" return excluded_attributes.append(header[best_attribute]) - offset += ' ' + offset += " " for bv in best_values: bv_subset = splitData(data,best_attribute,bv) - print offset,"Split at",bv,"with",round(len(bv_subset)/float(len(data)),4) - buildTree(bv_subset,header,excluded_attributes) + #print offset,"Split at",bv,"with",round(len(bv_subset)/float(len(data)),4) + buildTree(bv_subset,header,excluded_attributes,offset) def main(): From c3fc98f0593d92e6f98794884e27494e6dc6a8ee Mon Sep 17 00:00:00 2001 From: Ole Schulz-Trieglaff Date: Wed, 20 Jan 2016 21:08:54 +0000 Subject: [PATCH 3/3] Initial check-in for k-nearest neighbour --- 08-K-nearest-neighbours/knn.py | 79 ++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 08-K-nearest-neighbours/knn.py diff --git a/08-K-nearest-neighbours/knn.py b/08-K-nearest-neighbours/knn.py new file mode 100644 index 0000000..89e544b --- /dev/null +++ b/08-K-nearest-neighbours/knn.py @@ -0,0 +1,79 @@ +#!/usr/bin/python + +# Cambridge Programmer Study Group +# +# Ole Schulz-Trieglaff + +#from collections import defaultdict +import math +import random +#import operator +#import itertools + +def most_common(lst): + return max(set(lst), key=lst.count) + +def distance(p1,p2): + return math.sqrt(sum([(a-b)*(a-b) for a,b in zip(p1,p2)])) + +def find_k_neighbours(trainData,trainLabels,point,k): + # assign every point to its closest centroid + distances = [ (distance(point, tpoint),index) for index, tpoint in enumerate(trainData)] + neighbours = sorted(distances, key=lambda x: x[0])[:k] + neighbours_idx = [n[1] for n in neighbours] + pred_labels = [trainLabels[idx] for idx in neighbours_idx] + return most_common(pred_labels) + +def main(): + + # set this to keep reproducible results + #random.seed(42) + + datafile = "iris.data" + data = [] + labels = [] + + with open(datafile,'r') as input: + for line in input: + fields = line.split(',') + label = fields[-1] + data.append(map(float,fields[:-1])) + labels.append(label) + + print "Have",len(data)," examples" + + num_folds = 10 + for k in [1,2,3,5,7,9,11]: + # number of test data points, we keep them separate from the training data + accs = [] + for f in range(0,num_folds): + num_test = 40 + testData = [] + testLabel = [] + + trainData = [] + trainLabel = [] + # generate num_test indizes + test_idx = set(random.sample(range(len(data)),num_test)) + for idx,item in enumerate(data): + if idx in test_idx: + testData.append(item) + testLabel.append(labels[idx]) + else: + trainData.append(item) + trainLabel.append(labels[idx]) + + pred_labels = [] + for idx,item in enumerate(testData): + l = find_k_neighbours(trainData,trainLabel,testData[idx],k) + pred_labels.append(l) + + res = [p for p, t in zip(pred_labels, testLabel) if p == t] + acc= len(res)/float(len(testLabel)) + accs.append(acc) + print "at k=",k," mean accuracy=",sum(accs)/float(len(accs)) + + + +if __name__ == "__main__": + main()