From 1a4cecf4cd703a9099493cde00baac84bab0a41c Mon Sep 17 00:00:00 2001 From: Reinier Koops Date: Thu, 14 Mar 2024 11:11:49 +0100 Subject: [PATCH] remove functionality which is done better in other libraries (Skorecard, Feature Engine, Scikit-Learn, Scipy, Statsmodels) --- docs/api/imputation_selector.md | 6 - docs/api/metric_volatility.md | 12 - docs/api/stat_tests.md | 18 - docs/img/autodist.png | Bin 13503 -> 0 bytes docs/img/imputation_comparison.png | Bin 26384 -> 0 bytes docs/img/metric_volatility_bootstrapped.png | Bin 20485 -> 0 bytes docs/img/metric_volatility_split_seed.png | Bin 20872 -> 0 bytes docs/img/metric_volatility_train_test.png | Bin 32176 -> 0 bytes docs/tutorials/nb_binning.ipynb | 642 --------------- .../nb_distribution_statistics.ipynb | 513 ------------ docs/tutorials/nb_imputation_comparison.ipynb | 324 -------- probatus/binning/__init__.py | 23 - probatus/binning/binning.py | 470 ----------- probatus/interpret/shap_dependence.py | 28 +- probatus/metric_volatility/__init__.py | 38 - probatus/metric_volatility/metric.py | 125 --- probatus/metric_volatility/utils.py | 70 -- probatus/metric_volatility/volatility.py | 759 ------------------ probatus/missing_values/__init__.py | 23 - probatus/missing_values/imputation.py | 403 ---------- probatus/stat_tests/__init__.py | 29 - probatus/stat_tests/ad.py | 68 -- .../stat_tests/distribution_statistics.py | 424 ---------- probatus/stat_tests/es.py | 78 -- probatus/stat_tests/ks.py | 63 -- probatus/stat_tests/psi.py | 140 ---- probatus/stat_tests/sw.py | 97 --- probatus/stat_tests/utils.py | 54 -- tests/binning/__init__.py | 0 tests/binning/test_binning.py | 336 -------- tests/docs/test_docstring.py | 14 - tests/interpret/test_shap_dependence.py | 10 +- tests/metric_volatility/__init__.py | 0 .../test_metric_volatility.py | 422 ---------- tests/missing_values/test_imputation.py | 107 --- tests/stat_tests/__init__.py | 0 .../test_distribution_statistics.py | 244 ------ tests/stat_tests/test_stat_tests.py | 100 --- tests/stat_tests/test_utils.py | 34 - 39 files changed, 13 insertions(+), 5661 deletions(-) delete mode 100644 docs/api/imputation_selector.md delete mode 100644 docs/api/metric_volatility.md delete mode 100644 docs/api/stat_tests.md delete mode 100644 docs/img/autodist.png delete mode 100644 docs/img/imputation_comparison.png delete mode 100644 docs/img/metric_volatility_bootstrapped.png delete mode 100644 docs/img/metric_volatility_split_seed.png delete mode 100644 docs/img/metric_volatility_train_test.png delete mode 100644 docs/tutorials/nb_binning.ipynb delete mode 100644 docs/tutorials/nb_distribution_statistics.ipynb delete mode 100644 docs/tutorials/nb_imputation_comparison.ipynb delete mode 100644 probatus/binning/__init__.py delete mode 100644 probatus/binning/binning.py delete mode 100644 probatus/metric_volatility/__init__.py delete mode 100644 probatus/metric_volatility/metric.py delete mode 100644 probatus/metric_volatility/utils.py delete mode 100644 probatus/metric_volatility/volatility.py delete mode 100644 probatus/missing_values/__init__.py delete mode 100644 probatus/missing_values/imputation.py delete mode 100644 probatus/stat_tests/__init__.py delete mode 100644 probatus/stat_tests/ad.py delete mode 100644 probatus/stat_tests/distribution_statistics.py delete mode 100644 probatus/stat_tests/es.py delete mode 100644 probatus/stat_tests/ks.py delete mode 100644 probatus/stat_tests/psi.py delete mode 100644 probatus/stat_tests/sw.py delete mode 100644 probatus/stat_tests/utils.py delete mode 100644 tests/binning/__init__.py delete mode 100644 tests/binning/test_binning.py delete mode 100644 tests/metric_volatility/__init__.py delete mode 100644 tests/metric_volatility/test_metric_volatility.py delete mode 100644 tests/missing_values/test_imputation.py delete mode 100644 tests/stat_tests/__init__.py delete mode 100644 tests/stat_tests/test_distribution_statistics.py delete mode 100644 tests/stat_tests/test_stat_tests.py delete mode 100644 tests/stat_tests/test_utils.py diff --git a/docs/api/imputation_selector.md b/docs/api/imputation_selector.md deleted file mode 100644 index d4fc675f..00000000 --- a/docs/api/imputation_selector.md +++ /dev/null @@ -1,6 +0,0 @@ -# Imputation Selector - -This module allows us to select imputation strategies. - - -::: probatus.missing_values.imputation diff --git a/docs/api/metric_volatility.md b/docs/api/metric_volatility.md deleted file mode 100644 index 2da631ec..00000000 --- a/docs/api/metric_volatility.md +++ /dev/null @@ -1,12 +0,0 @@ -# Metric Volatility - -The aim of this module is the analysis of how well a model performs on a given dataset, and how stable the performance is. - -The following features are implemented: - -- [TrainTestVolatility][probatus.metric_volatility.volatility.TrainTestVolatility]: Estimation of the volatility of metrics. The estimation is done by splitting the data into train and test multiple times and training and scoring a model based on these metrics. -- [SplitSeedVolatility][probatus.metric_volatility.volatility.SplitSeedVolatility]: Estimates the volatility of metrics based on splitting the data into train and test sets multiple times randomly, each time with a different seed. -- [BootstrappedVolatility][probatus.metric_volatility.volatility.BootstrappedVolatility]: Estimates the volatility of metrics based on splitting the data into train and test with static seed, and bootstrapping the train and test set. - - -::: probatus.metric_volatility.volatility \ No newline at end of file diff --git a/docs/api/stat_tests.md b/docs/api/stat_tests.md deleted file mode 100644 index 55f5ba26..00000000 --- a/docs/api/stat_tests.md +++ /dev/null @@ -1,18 +0,0 @@ -# Statistical Tests - -This module allows us to apply different statistical tests. - -::: probatus.stat_tests.distribution_statistics - -## Available tests -- [Anderson-Darling (ad)][probatus.stat_tests.ad.ad] -- [Epps-Singleton (es)][probatus.stat_tests.es.es] -- [Kolmogorov-Smirnov (ks)][probatus.stat_tests.ks.ks] -- [Population Stability Index (psi)][probatus.stat_tests.psi.psi] -- [Shapiro-Wilk (sw)][probatus.stat_tests.sw.sw] - -::: probatus.stat_tests.ad -::: probatus.stat_tests.es -::: probatus.stat_tests.ks -::: probatus.stat_tests.psi -::: probatus.stat_tests.sw diff --git a/docs/img/autodist.png b/docs/img/autodist.png deleted file mode 100644 index b3fc3896cc8e03a3fd745c114431f02d07d04f2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13503 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 F`hPtmALsx8 diff --git a/docs/img/imputation_comparison.png b/docs/img/imputation_comparison.png deleted file mode 100644 index 6050aa6e8fc9dcaec621e392cddafaa40845f8b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26384 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 RyORM3JYD@<);T3K0RXG3RoMUl diff --git a/docs/img/metric_volatility_split_seed.png b/docs/img/metric_volatility_split_seed.png deleted file mode 100644 index 75443b23b63eae9dafa0ba0458b346800ce01478..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20872 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 diff --git a/docs/img/metric_volatility_train_test.png b/docs/img/metric_volatility_train_test.png deleted file mode 100644 index d780cb9a550f906231e9177f92ae90eeb3ec9033..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32176 zcmcG$by!qw*EUW{!;sQl(k0#9B`G=5(j6kr(A^=eq983LJs{mkheIRMNXNIw`+lDH z{r$ebKaXP$X7=7!t!wSI)^)CP8KbSKgo81#l9Tar z8u+2;ASb7-A}2?y?cr+c;ADeH9LhB=w8CH`N@*@I8 z1bIOy#3^($UcQJdo;$?p9V6q37o*^eIp`3X7xT}Te9|hf6)>WG#<#avX^vhewiVC*#m%vW|Dv z#H5AF7*WuD@CgZ(Ip;uzc1WSdD8#s-@dQ~t)8rQnt7&QC=c&=WeSGv3Mn?X|j0|1d ze|&sgeRzCav%$t5X(A=X4Maw0Og6XHsGunV1Z!Ypq++X~fdGga0UZG`$^iid_=X64 z$bk<60&*$@0S)*|0DR<&kp6QQiLD6vKi?5~;19~YkW*0s{=TsCu(5IRw0HHQu$U4D zx|(&+Gx9RhP#3jwb>;$FyS}pF@^f~BH$f2h69v9H+jxO#{hXa#JVpH^=>I+;3VerO z=BB6p`-qpL1ig`lHm#hihYjs>E&(ncdPz)LT3T@rYgZ9J_!9NfGdTwQ44 z{eoY)dV5LG)58b)@4tWMY2)YcKO?z#{<|z-f!y#r+`L>o-2d$xXetiBDyr?^XX9if z@8ArG2N*;0`EvpBzt8_)cm8L@e_HB$+IYyhIs*;8B>zXg|2F>LH~;?){|;&VKST2K z{C|f0Uw8g(Db5XF`Tvr{KVtrS6_B$era1S1_e>Jg;nkx&0)jMxioA@TAL8$KXd!w( z{(R?dXEl?TwoxELp)mM8Fa5i{Keg9|gE#mMfm(@5JaevX4Dp);GaVuYJ^9+O=vdll zT6AUZZ^MuGV;-~p!r#Q|#rVbQ&&>R8x7&S(Th`hy+l~rM%u6#;usmd?(a{mM8~p)fj~(AJeS;r9$miaJgLm{PZRicc?IwP)rSd+yxwV} zJ!AHN+f3+zh?tRL;=YnUf1U257!BQUSZ=Pp=i=tZyQ|U&$;ZI!DrRQgu~c^>pxRft;hMW;$r zW#x!&&0`kv_@$jbiGw30=teSX8&C@86c;AF77y6>9*+bapCN8Em+Dl`2G(pj=Mwo++gdu zI|HGEzvxDP5?<4akIB`|esu-*M>JpuX5tZ?<9xJkmGIo$+5@jW99?^l@6N`f-W;{h z9U2ld9HhB5Oj_egob^jR@T%tvPcEK4-mg7I8d|*ln%c~WmF72Ylkm~KkJvCmr&N(C zVD#7Xo1GpkZtS(-$F~`K97Z*?%fXNLu&Qr|d{WP0FKpxkk_l0)bp|m;_E`9=zQ?=G zgQLiF9Dt=7Iez)tL;dDAe$YjIVdU32^QG*^7?uM2OuQphf3|bX<|d~FzO%<~Yxi7E zjSIH6&tO6M4PLtwbf&&5-Xt_buNNB}6q8LC{EniVF?Qy9p`o3{a#2sxkz*5gD4ZK~ zj9lCQ$W|D9;fN<>U;io6PI=h3b==&JCci9v=s7<9HLv3E8_nSf<Aul=>zm)qi&A|Hp4*F5;ruM*NQ*aQ=B)o|PKA;Er{fy*D2AhM3DZ7qG_^t1&no;xqe^HsvX%N)h>U^C#; zNXV`$Iee-8PM#^)F6SD3#bRzq;36Zd*ptlVjxRK}b>2pcrnha>z+5%v9{Zy5RZVxfO= z@r9NI@x$GYlnF(opEmcWH$j%1MJ~GI4xKYni_Ffs{15($5?eV<#ADQnUjCAjF2Tqpq$%jkyb?Z!2UEJ^S2t3x%ceL)r|BQCb_dOMFoMdW%j4f^eyO}M-PL`k%uA+$oo ztW`i6bi1^ctFU#ok%X5dc~h?MbvIeQs{qdNzddOBl3Hqg*t~(uAY7*A>hkTN5sSP1 zT-Wak7UisrHbCkl>+jV5XBIZdQi#14bo<5F^$9K|sn3E<)-li>n)QlhI8M*o%N0AA zgLs}xtAp^_2*1YU{l3LxT>&1>Vg0ydy?9BoJD$Y3_SM?sgB?zdR-q*9d|FGW#}=TU8r&}lc94NX9T^1p`h`%hdxXdc&#^c^@4y1tR%zU4-e-~N{Bn$#(pO+CL zc)sGhM%E&Evs)_HpGbuaZLMxQRrTA>v8#M%c~W+G2*`c*37+UKt)o`MQ!mWDSAE4e zQ;ie6Gj<$RyHty4b(Yufo%21%L4vE0_6U?{c;tyBlYO-cQz^{cYASgu%Ib+KqLvgX>NeAF=};QYJBd96EH&MaXMvN zejWMLye8{8i5cYqVFk#5dNWM>7~6mHZA2fhK%{1*HGaPe%R8crCD%?jV7H9(Ak9V7 z$xoNpU8c%ZW^>9mlqGAtijxjwA?Xti$tB3R!d*3T7JPbIWIwZbjEdi!@8T!boeH&* zckWsr-)wOQI|u`MVB$kV**O0y?zkI`CgN;NY`iACM&kB&eTHa_@6oEk6DB66j1!V| zJp2qK*5~Ayv9Is{%r!yyT$YkTU`X0ev)l5HOl#L7>E`TWbq4%RcHMNvL-DGiPMUKYz zw9^K$lBN?0H_Mp^kWjK}ql1RBPXf|63{0GfT{`bC+@Lr@M&@)cSif{W-X95iLFkoE zSUf=j=>(Vw)Fi@^_vh0e+z5Exes_`3D+N6OZ{5ov?c;O%%?Rlapdy^((Kg~eO8FeNxQt;{Zd81CZ1DnomF@=@x3aFLLzBQ7D`@t#B4YA<|%=Unbv|4Vr>NR71U#FyUD2@+9$mmJ#QU3Ae@U@Bi=p+E982^b?9CS}qw zjEW0!C9f=@n5Kk(axuQDaw(%zv59(kE$_}B)If1b@UDHFV&;ZvJmjg0_?tHp#SBvO zn-9+gF!$P8+dm~Ua?=}c5EHzh$sp)|OIa47IHpD&*^8B<7+b32?x(P&&K>_msTyhL zYk-+CaK2i$|QuhSxR*XnWV*1PB6Cn~;iwV9?B&xp^B5>h}RfndHaOvKcl z@3PWrY-Q_|BR)k~B&2|c#!CKcBE$|Z>KUR*bk%X=N_A} zt2~=`f*iEkCThn@Om^)jnQO6O6F49$4m2j6rLr_R$!rs)9gI}|fb(5d5l-#%sH?pq zCqNHpyXjmirG%{&SfoYm^NiAKUQM{mDpFpbGjIuk2Pp_+9d3>6>0D0kOi(6i+#<45 z1RuIE;;@@v8J7@7a^+DPH%(%BmH0t$FN$4~hR4g0nIpoTiY>Z8WR#x$v%{f0$#Y}x z#3-nDd8cTpPmKmN<=;!ICJkXAf}M7Yabes|0{*l{42`SV$?97LK zW^J{tH0!V?A>H*-m@xQr6+TE%5-(zHwMIGX$?$N#7g+_9`6i%xkqS^N1!UE%^Nq27 z=M)?=c;~Q53@8V_qH8})_Iw>DC>)K=95(BAmzC5+s*znqH#wJhrJe%@>9fL zWQ>Ky)W7(-eX5UUB+REM+h+W_uX$Ku1XKr*#>`4HKQk8Wagpe7f%40{?II0&t-7m? zy_R6D;Ux@r|Ca+{8)OeH!2@(rD3Fkew_ZIphqABM6EAj80!+nOaM#h|_NG5l*zS^Y z)D#4XQ$K2uLO#{}=1CvX=E$|Zgn%alUkksNH-93Lz z)b~PK483K_&~pq+0gv@=yu{ew36a9$)MN4Qrm*O+KbQxg)C2|KoRio$B%_h@B&0U` z9Xd>8-%0XKLBj=jxiqZ$dsXqmc}^Nq`nPCt)3GEf=icZ}79JTk%HP$W+)W9uCnSxt{6Vu0CdKmoUE9A1B9)Il@1myMg{9EAm6*DL zxTdA zrhl)YP2*qz#Ji(58;f}B>r5(m1s93ttTCdvMh41w?8qm)Bg4aKSN6!lRkg1`Q6EF~ z2^^o0Ae~AtH!wlsO-U~I=XxWlt)l2L*}Uqegh2yT(ga8S`cVc+mM{%61^)Deo|4?e4@+ZIppXkxRg1HVg)0R~J!6 zr=6qWQrdg)2BR)SKR)feiu@y+@022VeH`AL6EYk}-A^j!@GXDYbD{mjgV~u}h;ZM2 zfYolhrO1>{B7TKl>csN1jb?llizeksDoudP$OcS7x;ldg1jLC?HYO#aBvJ}K%3EUrBKJT>3G6z{EO<{ z(*XoeiV6H=H)h{}cRn%VoQsU^DO}#!{!iPEvo|0%;mxGF?-Rg#oA2Y9B93>dL{Y?{ zsZdJptU@38HVZ}Kgh@u zJ}9_4K62o@E_^CDei6O>v-oLDznE(kG?~nFl4*Bjn7ecN9Ithlm)Xp>j3(>d)ugMX z=fm~R5VK%PVP{rOFREfZJ5+|DXa#pf3AntZ_<_$iUh+`iAbqEItk|ln^facKkvxS*M zf_1Zd(o@=&2z4)wYy21s%3{9$ldLyfAUdh-CthTzf12WOXdhCatmr^2%_fXo$rL28 zDQ~6~b^C zgVi_Ae;R|`8$QB%VU0O9J#CDx7lrUA^J33AFF>Ka)ciUw&Zjqy5)0LPe2H8hSt3O0 z7vBw1@Qm88=Lex#)b6^z4xYVgZ$C;97I-;un`fQG5r;G449u|9s zOD#U(`-x}VZbnDpFu7V2#)p{Es7kGzl}L6IdzFlrBOx^a!@VBhKS*ta;L27PKTRIw zb+42ArqmDO#(gUv(BSVy>d%B*iQl_%K(i95zBx+eFWc!+D4f#w2dyqmatldQ99hho z$^1NLc%Z(SA$S0-K%7AwOv@8-CSDwzoY61=D|jND_SBj_INgS_-tT4Pv5EY9kg5uw zEhQqk0A?hF+K_-%G@U7-HJQTaJK~E70N=1CLOjR4K_mDsa!4z@OV@&l5)tuQc9On} zn)i1_m}gyBO#aqXC1olh9FDRkL^R;wMPt&`+oipS;RV;wwK{z;Q+9uvt`ON1|J0ri zk<<7L3o#nwgX6EXG$O|H6I)}w*Ie9g8wl)8vOg%~M zO(*{ej=ILI6qm8xrYs-X?^KV~3H$5?G9prYCm(g}V#kG3k(b&MR#(pB9+PCsE0Z>; z0LR^CXppKb73I}<7t||~qcPu6T~L4xb+?g;hSiE6UdQQ@APwSomGsJDGF#Vsu;;L| z#$l3$l<|D%cZvD8Ifz&k@ArasbfW&Vr{;d(Ew)D4k#K7gq{u{sS)$C!`i2fRLpS+8 z4Vzzx`ub&kzH9sPCtKo|B8g36j|V%;YA-H{QdW{$`#+Fk5+m*T9(!mz1Lgm&Uaf^TV0@;(g4dd zRzbyA`7gM>q4VseYgEr$vUTj~`@GQqElc|by-03eJRxEJiZkmiiqT~6hc`|?v)GLB zbq0f4SR*-TrH`UAC|JqN3w~4Qyna6(Cai$Zbfz<(lBH7rTY!xP1(OPqS-&1x@Y`hF zn+j7Od}-Hkk#Fer!!Dl*j}`RlOt@kt;?OM>ir3}zHtUiQmfTBc7A6ff(FIUYvIj6^ zbd*?p-w(!`RsP(={u)!hSrwSBO9JYO?ZH3E0F4?Ldsn@c4U}Pw9b3OUaip*4kBZ3- zhkTd0!H%41pix>5E`Ay8@vMoRPwLHLkj5vsBH)5SzZ33}cDo1l zlMsgHGX(5}2*p$w1!D7V-8+|kJHP70Mr5%|qD;`OjhInpekorH6#BIs@a|<4i3WRz zYGE{EKuERG*`)L@PH)n8Kjr9Ryfbgc`t`1r>6bh#e^?*UNNVQn3h{n{cyN|XX{*)^ zoZ)}HJRU1s0Qa2JR2u4;20zRyOy` zkOQ-)mKxXgp%g#cc~~kW%nbKhsa`wd;nLz7!{$9fbr%emWtPY_(ovR0ki+%Tt2j=lo&w3=nQ^JAoC)RRxfS0=*Za>aaUcDRcPzLAg(XIe z8!9zVEH$qCy3C9FatC8Dy{~f}t1y@(RC~xBtH2SFuze_=lz4i2x)H^>pvG+}XAt18 zU;r+m)qJt&2sLr-Aa1Q*l5qO^!{A~MvtWaUgJ6l>!M%B-rT^fduzn3n6C}=sw==FX z>>VDJbbE4~uae~^?3LzA{aZ0NSYB@mMSb;*5HX!50m%__dudTLU47;^d-zf{4L3vB zq&Rk>j{uc`y>|2c#nNb9kIP_S;(c)xb*yB?>UZ;G=fz4gX3c#2b{feo0IK|El0V}0 zQRxp5l+3GT=bcyjX(kO>R0d*F>&hKrppnscI81+7Q1b^+k+0+ZX9U4V?UznJdLR@o zq>W21-+p{w>PFee%X@}z7lljJ!}aa=tM*#E!ncCIfx8#auCyetdx>8Q5&sIMx>q}ioYpB4b8c{rrhpfWQMYnuGjt@U#CO#)BY&eh2V6aap}{-5y{ zJQMUgKK9>!E=hQ}LY!zF9*JvNM>V@_E+M5StNlEq5S&e!FlhCIV6w{3q0sxnxR{Zi zIoZW)5ZDE$KY2G}%(0dB5fPUFrus`N!0Ok`C)f!KbyMTG=dXwPTB|Sjr6%(Z{s&*@ zs?_tF_w;0LcE$^762(uIr`gk(H882fd>R@@Xa5+*I9xVRRf@ei_?q>V(^YIB-ZZc` zW3Xn#kb(0^y|QUFAd~Bxo@?D8GYOhUvEi;f9IehwQth6)qbo^ewmdCZL)R;%dChbc z#>B4P&HTiiET#B8`w+Bl>XavVFP6uBY7RF0_#i~-c^6Y`w&Pu5N8Cfa@T9M? z4uL&hY`x3BdZPNiWo$tXA(ihe>OFY5!QYz&iI-Q%qEl{nu|xm2=KZ6(5?V2h(PuTK ztyvS-uVYx>8gzx|knsRSVR!Ls#SdeTC*8#fd^76bLb|LFi*eMiyu!YQrfEqocp*N} zvF*l&rVwX&(LU^!A|gLAvWvhaagNtC_^A0V-YL1!L_Zq2UxK5jOzUMbjdfId8}P!- zQX7jX*o9rh1P?sjn@D_jtk;|QV1b`;&if*oUIrnu-p~KjpE`nibvaXmmn4~48Bu-1 zstMT*&U19XJ1!+>f3j%Y%|Pe@%YzR<6 zyAmQ1?N+0_b$L_~iRywir@<;eLnYeJwy@jC;hyApvA za9zZo#oKOL_E~ZkGz}Qx?I-OHv5t?fq^<0^UKkIuB_Xe5EX&wU_B`DjwEaGldHzca~p3z-+<6WyECqti&xjO;|!QPCzMGISZNa5)Oc005ERX z@$94$jq@fdzc2|s5&acir&v&0lYG=3xf(I)Rn5y18ndU*kQ(mG&1TG^1Y#G}{*dZc zJ6O!<;EED7I4Yw;MT}n7mQg;|ZzSaN>xG~BL{k@yN&!Hi;(C(kFII*_BRN8eABzKe&W^Kn6m}tKjdO$IzQr=ck z!IWgJE`YWpBgR#jAnZk3ekoef-7&L}?z%pRKybwLRKGdjvkK_ixF|cpQu!&F*g|jg zYHyQ=JMy-HVI9nsw~w`GUIN8~&X)Bp6Rv;2!ACn=vyZx>voH#){aho%_>NYT!rQ&B zdDnWbdPN%M7FQS!DxU)Qvh$hu9M=WvX~$ zN_RIB>rnAd$8sD+=zgMW(`c@KHGFp-)koYnU6$Lbu(U0F-!QEsFndTE+pUWzQ}nRv z-cJS73cg9h&j^0JUCZ#!yihayI9h*`-171f$a?{SK(FtjTbp^Q)C9vWNi^!2PIeN6 z`q{H*7$5UJVl~R&d-ka^qGPA|s)!w;C*j@}@EP{X)KNjz1$6GADVxjd8+-)@Ngsd7^Mm$SgFyMIYqvY)41F-VX)iJk0j@*B*5 z8867aH_GD9JqbBDXk1(&c>M$x<%oN?t{g6WM}6WkK7Dd7LEYH#9nVtk%?&x=Q@O{PTm(cv;&8m9IdKs}Ki6=At0r*E0se0pD)ar`br` zzI;)#kUJjR!t&*`>$&z;x-QjRtZ1-C!*;>UDE{PzFIc6u+(y%gS-)LE+qV3Trh z>t|2SFH~<;oZbYD-}~h;zjpm5?zz$>QMGZ>3%%m+jOz5e@+*j$p01^-m0Ep}kgUQO zhMly~EK+vHB%Qlm#Wg8x4};$oYfRID!H8Jc@I+I;?)}L}4mPP*#TK%(oGAA3L3${a zw3v4vz*Fdi?}Tg*Z(Wn1)%Q6qsG%Nd8eah{lYMIeC^%7M1t;YLuUE zM1qKYjuJ?)Z+w&ty;ga)2LZ7npv>4}79}&jTBYr}g{Tx)mYl`x`!kppVD!8ZB}{Dj z@N+EXbH@;-PVt0sM=!Z6^OxFuhvT2!jn2Y(3p`_e-=B|Q+R`&tkO5(?h>6~MMJ6)U{y-ylVbfl z-+MNhC{xjWJNy+W$FOv?HoLNZRB&lN;1tOJEO7kRYWbiP_8wnqRk`A+ zhIgRI4~LJ<%e)m5T?ryKAgFQI8!wR1psUv*9`2eK3bV0v(u<_QZS1ghbdn6~4Z=$% z7c6|$-XvBn-9`!|zI0LOQ|mm|hv`Ogjbw~c15{zQ8}<$>ifQKR|8hQzR|h2!2?Mu4+y;poHNe) z+)Sy74D9!60<_@Sz`c$h@K~ch_D+O6O6jCx~-ow=nQTP^`>&Mpv!x+S4KA$ zTQpi`=)ceWB$${hzo6A*emOtS@q2ee3tfrQ)6{RbqZmBv%MW$1duKJ9UN?|iFR*CH zSi(}QPo&|0#;iD2TVg3wysF!@a1<_q!XrjVlPEei&uMpelfy%4hFhMtbKW|9Sc5(o7A|El&l&=Hp{%$y|{%=2ctJGg0BHkOG<&3o?U09p021+QvC!M zsu;wG*Q1u7C5k3rz%Nx4kZ6di=f#6-1@C$las*T;~HY1YXxeH%=rK7g26U0;#Xutv=Myo~1`U_F6%BYs4aQy!0OJyW)~w zlk3v=#$YMn(BdTKTS+r>^epWokh_-RPCsYmt=rHAJw1rvW;mmhkt{+>u|zX(oUuKe z)i*S=F>yW%m02V^U|cnb4&Ru0q4o9R0ysED!JttlMt5h}{jSj%?+`fhe{T*=uwPnZ zUJ^_iqoe=3eHG{%$CWqWFpk2)4~7G1le-wF`FIQ`F!~OYi55@0eeaT9UWIyzAk*ts zz6)w6Ndy+MQ~XeSU6u3o#+aI~BuO#Pg371|?FtjI&bG%exI3@feykMiSKF<17jaqD z7Ijz+TBeH7@NZhwiSPidr24PKix{_LH581_QH~JDnsPXuAQOUj%cwedOwbF_L@-&M z4ytikr)8C#fx2!lOKV;kksYY5b*ad|fSbx0A*4^$c&c)`0nYL5({gerwO@oh^=s;m z^Rx`k{=86!sk8Q$jWU`NlNTJ={V9=*@Oti<_Z zxv%h9{u$K2vxSw?|F^qh&!=&xgRuyANv>@a$}j{Sw;k&?8v&o(>;|})Pz$Eley8Y z`$}ZBTmiO6V_i3YEPW!oUX@O=FvMqGD{{x>xqIXx%$o%12R$eyRdN3utN1{)Eu-Sk z%LBOqjl8O7#x}HgGu|B3OD>&q**rnOJN{5S`7}K*QX{~XNN}aB2?ut}WHEraNl7qv zG6BU~2n1EeYUE29T!n7L~Kt*P+kE%6v5PfQ)pSB&JQa0+V#XU zUO|FaZ4xcMq#)LGqY_PYN_ z*mOsNhy;rN8lXV7HPBI-cvN%7WgBWwl9J&v3EI|~d|l-FG1w~No891zB9c*Tr{x0@ z#ml{=rfO%eft}@RKAzq#s*`(wNmk2rf46rj>rGNc}w2$lazIFf2Zt9>yi9dAd5-7&Qm{$nr4i-{;e9RtW@HPytxo)5%Y778j{8-lJOGU8 zRqeX(GpOaBDCGx9iwBYpLhFX2cBpCW(Bq>1++Ow5Z3q^U&+rLXu!Kz!gJE@_g%T^+HZ*G78l3fi zZ2UOnzl;D=K*HB&(^Lr>a$U{-a$7M85%Dv6ID}NF=t7u6o(fdx1==&Fb@8~Zo4`I& zRO7T>Wowd$@^%UDDKn8?Yi3@0tt&H8-GNx5ICwtMGY122mo*a5g?drButy>Sw;Bf; zW#wnEzS;+iqPFxb4s(Lh#{sj8T=_SZ9Ith2Q%nwZzlbo^SbA^~x)8D)&r-gW{2MfU zRg#u}e=+~eT7sc#%F>{tMhfp+lkH+gRyB5Epae_Z{7jF4t{OO3D50aN-aKkRH8q&< zW$khelN4dVO0NE}6Wd=_Ev*f}vDJmBQlH@+@kTo*c{5!5JEebyI4!cyQ6P^Q+|ZqZmLMhixOKw$%7i*N5y6+&50A z1~PlJQ=?TCrS%yV5<(PJ<4Ug-^kPf%Gt~YnQMg=)h?&jguQqiNh)rp?F-5_sUViMy zLx6-lLnYS-=ox%oe?q!ax88#vQqO%us#NtFQH|;qrYD47apWo(5UaLya+jpO8ObR5 zXO-3np|B0x2*dvI0cmv|B76}C68wz%i$khh1!zOgsx%+JEQuWlJzJmgArALkW0epe z;;jgw2#4Kc`bbU&KV9lNAc&tzC|A7-9~6VgpT8tC)+M*&=%9H&Q{75WTcT@-LM$l- zvcx#aSE1v$kz6MsTP(uCA=L;1PTPQ#(L-PX3*TJTMQ*NyA0>to~{D6%ec* zRuVPl-=BYF8xvx1Kf`jPY68rh4d1Bcbzepyp)cl^C4c`G{A6a0T+(MTYdh3vm&g5~o}GPBxgH!Sv*T@C{Qc}r4KckL7|J}9D;Q7F_QO?d%r6gQ9F?uvl z2u}`zjp10`-iV+11QEhVhZ0MQII8n(y~l7iQd*Ls=2agi0|UTP&P+)JAaBmVKR*m% z3wDJ#Yk_A8#)gUsXZruSdQS;HveLi zCKvETkqn#^4g*-)cvvJH=$tPb%F|b)qH4yZUj=5DPlX4;9bp!nRc}+P%nIyRuAnpd ztf}Xy_RE*#aHOB-?ajHit}cP?SUwp*F~X zvGSqhm)+eT1>>~`{J{j)-Iq>$A@9|eAZM*FF46{2f8oUe;1%z= zuL-$EYygXrd(p__@^A$TY#wp;!uTq>({K5sm_Zi`m*(OGhHpr(NwwLZ0x~KKM>tp$ zcZxb?uH z39ZE5h<82m`K zYkd4OXF-!q+kYue%lSVAD$-CTT6Pj*4rN?+kUR0T}kKLLSp>&v(unekvm5h!B;D--e~OU0VrwoeBe9{&;{yA74XmsnIx zuT8=mC&W;qiwPZwj$8k9RhTkQq~cBo_$AY=FUx-&jIL}KB#0ClIQagFmo{LYp7kt9 zZ~Iq5z$+sS#3K&0sGpqv5qt>9J{quM0=>HW1^@UoX>Pyp`wx^99T?DD3~}vFPKAR4GjX~05$!CdM398wJ_WjD#nxQ*!yR+0Slf%MoK0+ z9~jBuhw_h$TXh;%8i9A<3F495=W6orsfQF)FhgZ!iX6Pdf&h01gEN#FloGLrGdZ!o zy!^@+DLZZtoU5w1$_xH#DmG&D&@l`Og;%Bkq~M4eH<;`%e@YlMSi!Pjllp48Fn}t9xkUzBQ{XNg#nbtU;xbMpcJ`}zu`WO4T-|F&3zt- z0D6E#KG_o-a_erm^KA9nBWU&BXRb?SOFm^mi3I`cRRt;lS(xdwg7Af6yT>PlNSCHBv;Ud;K~3;f;Qo0fJP1js&M!!I4O$KdF9e1sf7cGwux6pw7FNP|5JWBw37r3b_gAg~k+~Nsj!tX%lelXO$qiqz3 z2;1F1kPD+6EmLFdnCO0lYsdBX04WerI;PbqHqPMR#;;`&O2Q5;gD6@f=xkV6u`$5z zDSsnRc(alo>n5Pg3IgDdWX5##K>#gab#Q$~0zRXz*XR>1U_MFqw?G16>~A@eNN5qI z_lyqs%h%adeVgaKUhwyWA(=pD0USjYpWW=Axr)E3@Xrww6Uimg#)ZNfwO^u=0|?Kw z$Wa3zP~V;l5l<(g{vf9?vJ`)yK@ zB9h8?d2#@6&dBU|r!|&o!YJ&`yFjGVNLK{dq^U+#%T=sQ zV&B1axhEHH*GIvRj+;-A7~%2U1A{h$PI32i%|a!{Fs1}Ka~1gemm&Z0$Qt)ZSp8*- zk~rn^hFkFR*_wV|^(|nnh)9qc_EJ*`7%)AzM|kUy_AIL~X~Z;Uw0A{`BPeNQcnsv~ViSn-L_Y|SategR=Q*lLIs8~a)C-!o&%LUW+Sp##wuPQVuTf2PI;KnAffxrR;okpbLHm zB6U+Mr>pcAnku;$VqLVjP@kueo_-2_(T2D5w zReTXPjpVW@FNZs%%Q9ub;p4j=+dOC6-tdGCzOrigD6bjWtZ~#Z zt%kE-@^;n{9d9H_Rq@T8+rmjY`&&Fdzqgkj?r$j^z`N1rILtq_r0xZ3Mv|*Pn*$Z1 zu!`yqzBEqN;S6kR3?*@R6bKX%8|m2(Cu4E2g3=Am{rf8E2R-C(X?QH9=K-tDk-v{D zSmYH&En^5e0(cpecgCi{%@*(A?}$2zE#A4RBf$Y#d1*2TF--Jk4okM^?4+Nuvl(PaFnf39~*=&Ao+dITqrg6v>MV zs~e!o7VP;V+oWezorB$nfFLXP4G151F_2H{aIKKHEd0zdxT+p*>*AynyW>Wl0p)QE1achDKp^!7VRT%pu^4&l?U9_uBqWMa z86RuoPb2{nL`eQI2=ZG%u};02O-{C!-3NdIqNw+oK)6co^XbOrO0@-09v2}El`c{& zkriC13IIxHYDJimQKFGIhCc;KkWEch7;-O_VZ7?GUI6OfmPQ)xY-Kq&BQQLT^p9zL z&zt9&De9oYzUybr7WX7TJRyZ2l3yeSlqYIYRLMN$ks+zTd`I*T!pDbVV23goD1Ql5 z0rS=;7O9htf$1suFDpEf$v7CWVMX!6HQ#H zzTVC-7133*7;SERXROb++W`pbIvjxt-{7IviX&~$VLIRV6ZBr5{q=(~mkQt*%3c3j zI_YesbukfH+2-x?A@Cm{w8e6IMsJO}kCnDVD*4KAut4Gs1W=wuWwgBh`vcctEI^wK zg}1Y^7Y}o^WX*)rxg$h>8e6hGj+^LW}U#;^I?|fyOWJcm`fBqhNx5? zKSR+LF{L%izpU};{v!TW%JI44y$!a8LGrKG^(o;xdO?Ru5hL$zULdZy6uyEyQGL<` zgHS+H7?ffGcFFZCJTYHYqwBtk6x#tJ5dMj=xKONmW`{am?x0^y7l79Y!O!B2>pN%T zQbPCL5Z7IjjxvstRFz(0!&dZ({%7nKWYlC5BGO!ZTcSkw%HPqvZlrUuz`Z2Y)b5u@FY*b3Qr(pyH5^zHKBzyTXJA&2*z z3F;d(UlwD=v=t%!1wTZaXM7OiNLu%4U&fRbxg}5@!Dh6nl%=_ba!`m2uO#3Al;=jC@S6E-6_(YA|fT- z-4co@9ny^=ymRpXdtSeBz4zQXb7p4mz1G_6;u`4jxhnF1sV35SsgaM;`(cPFYe9G3 z-g``t8h>x5RPDQ_t}S@pQ2b< zIQzn*016l)RRPm}sbNMnZ1kp$utYVREC;kWk{@)*jT8w>;IZ5%gJr1j5opwOltT9U z5K3ZCNNct<+|5%L=J!HIVX0CQet#)9JWskMm zezKPtb<)i|nQlPn)EE8Mi3<#N81`Ib1$`Iv@P4Ys+&+>r&L}(q3Eem3Bvu8ccqlwk zLbF1mki%;cb{+xHx@I!r(Vzcz=WGaEkLWgT8m)!NaW!UBe5me7@=FZkr!ON0@_jOoUBbI<&J^BM1R>?o;E za(&*i0PWXpL5MFEs)*t1~R5wyC$8 z1K~d3$5F^LELf%TKHt?5iCg;Ye%RUn1^^--B%z}VKuMekID}`9f3*9uhx`9N|3wTS z4bg8F@^Zz7S7+)l0zm{;T*3>f=f&8=mWy>(4J+h5GXL0fZQSQGjZm9=Qkf4}RAya4 zNJ8OA_n8@Znqi=72+gO;EuU}~x$-&zjo8V3rQY{LE`m)idV=_IX>YGE=g78_2?%X z+0B_zeZ&b>qP5@QKhM^D92fn~V#cctx1j->g%3=L7>4A%)Y!7kV&Ix{58i@0R(Lxv zcsHRw2rY1r4sJ-lp@2grw?lxeK}C|2p6wGX-R}veB%++v#oi*M?Zt+yCqRMtup7WX z_Bty7N+xM>+kZis9~K|t#BzTYLrESF3&GN0BK?D8J5c19{OQzhi5%*{8DvGk9nv?| zr7D>)BJ*t!|C>@c_T#0z!?aqtundPui-6DdM!HbU=w-Lt4_|k3G;>A8`-i4Zi16ig z{$XH-us0}vzA5y9qL}~`vZWg#SaIW?u>l71bf~mT8+KG7sUs8!qi=ffyaE1aRAkRG z2wcg%8*^4Q&^XyzAu--q-^40mj&rV`dQVgOJrA$vx^H`6P&~sp*f-fc{*ie8G zFhlbgumQmOdh7_^hgJ)L@v(PS3^{qq4diHk%n$Ne!t<{czeMO$Qd)Z7J;aD ze`Ht(mymt-76*+WZ3QZBIau|@MO}9-|5Rv)_A+JJ<6RB&ts@RFECJ9d zw6dmUc(Zj007n-h*qdV?n9+M?eg9kt-t7gAB)Bd$hYp0ttxiCIbKs2}7(>vYeeOis z^;wAR8Dy`ptm0>_plO8!uNaDpE@?xTdcuO2n5sgPIpHXUL4(xvY0hvY0@+3=E~Mz{ zCdtyXlPpL+6mxZnPsV5T&CXnPRyB>e(JiPtyMzjX1Snm2%E$+iS5@1XXtUM9f%AU_ zIHyLJm^Vl?jn+yvoZEo1D1+`f<0Ob8Ndn;+UK&(qezmO*&;2&2)`nS#2eEC^#6ET) z9P>-3qq?(@HH%5ZSy8pw>bBoDZjToAW3akMv#Xr|wN4n^e$Is$V6H@NAP!dB=Wr91pNx>I@44e@Z5oC$N1B1%Uvet;=+k|f)7^X zFsP%6xLURgijrjcDxA(dAP1eJozmQ}9;`q5%r$m`s<-D5Fc~SSdLb-N%T#gi;7NN@LNPk@qDnk;%0>wm%g&9$VST1X4adxk%SvvB+eavf4krK|M}KQBG~r! z+082P3aFABd=2$cAsY*lM6-7kb5r*40 zO1_-q$O;)b8i&eWkVWluK2GVKFIA&PAJ)r!=Kt4~9C@`ceD52cyQ4wH%&TLbd-Cls z{gKu;TNNe(jI?A+1y{MyY$gCE+sTTs;6*^44&7jsuSd|Y(M|!o_r_xE%pPP%7Uas- z5wOtpKTJ(5y@AuK5$vK+jmtuTSgS6d7MMEaM?DmWGqx3RQ0i`JE6k-#BPtz0(d&vj zuWFve_|$0N$q9&l-`Hd!{0H@!vGoDk1ZcP+&ypLF``Q(Z5)LB&U|5ox*l}+WC1-kZ z%YQcynkB*Mwo6BZMthLPVPM{igw)01H~MkZDzKM_AjCfcZLOb$1uH%{zi6sZA)HmK zWZ~lPYnix#_djH44sj!PU83vc&DyRldWIjzCXZj(wF$hB9a)bDngq;f`9V8A4sHapq|C>v z`ql}g5aC=VEr?8Q-vKiv)SSj^ePO4xN7?7C8##eS?x*yu6{aotKIEDmm+hj|V@hLO zOCfKmYX0cQU{tnO?F-+u$JtLs?p0o)WzSig!A2e{kXbWZB`3nS-m3GQ6W@mpJS_Bz8|^xdb-`3!KH3ny6vn#2zUu8Ki^m zSXBydBP@lXMVo1uF>LA+IOxG=s$^*D{GKaxA!x>V0ytZM2k}LY>W@yos=fcXCjjC= zese1+sSNki2c@_!fQ}+CNo4PVz7-yNWGI1EqZ+L5_LMbg6Q04YjDua=G$NUpd8E7C ztRpu|?<_t2{WjF6@83KPWFU;U2JM)c{gnQpo}qub=dS~AJD-~P5e z+y`pw7l6w6qN%wKzGfW{kXgnB*_HM7k>+96clhbl%}E^*GiDq8ON=9x3yne|JU_;b z$l48>c?4cgFQk=ep4b^!CMi^YzMq;_pweLEj-u_6IXeHYvaez1XvNcxVe^Uhu}v#a zY|LWaEDMg!#cXW>v6j*UX-{ygG`T=?HMBKd9&6uP)xS*Ljs0Vm$t8)W^@^^bJMF8> zmkn?r>sS@i4Nt5lYJ)GCr2<3zYLQ+_Td1dV9fk8GeY4yLPx(LkbQfmp>9ngqRQhk` z!Biwz020p$qv>Dab3ZqlKq$rNTD7kAzfXZgju-HHBFM)7IF9>DsGJ@s%tUohSP}34 zgl2~XzWVnN@6RVS7qFtwXF`T$CLan!zXyCG)}RGj7RrBmz%#QXQI}8mM2xYK^Vgk< zOTmAB$p6mc2@=+CzyttfL=Mt8e3*@Tx*1`o^6nGQsA*HH`PLVLQ*7z-g)$v&M)|>Y zAPfNmcZXCJCW(r9a)`A>DGJX}xT3KdBL9_qu)tMmsbst8(yCM~ur%(6h!hT%P#0k) zB1rA-myEg?w1m_Q*h~NYeUBXh4NDVUa_9fvp$OAf*`28BjU}@ZYWInID#w_stWbB) z^n^%B(|4Zv6Tw?Lk-|}g7CI)@65^%?y+*(@{WotXq=ART9O##^zu7F`YVFk@f6doR z7rK6T9y7#Lki^SEnE1bk5cmaG-_($(J-gBJJd3o+u(eg-!3ToJ1axen8mdW}|E`9H z1GE&n))sfADshY(A~lBR$y-Pa$Nx<}0Y5@r8G-Z8mhj=HeeD~Pnur!05iWAZ`#OG5 z4@~OGCrh#nWAzvV^z<(;HXreCrl+ONv)Mlg)>y=aCInQ_bZqi^>*pye5#>HqzWO}B zjn%$lMWaRk@vE;Yq6|g2UmPJ)fqr!TtEtJK){~eRrXjmlTZ)caGn^o*) z`H^TxCl_5Dh+=6Nii-(H_-Ppc?a0eAJUkpzCGdJl_-kW<#!n6?K{BIH&?;DMqm&%* z7CZJn{u7G%26MWH$T|j{y|j(Et&iSWmWjfV>z$5;3Pulyp{Z_Rha5IL-N(Uc!kv+d z{46;JR}*4H2|$iG01-%N8mCd%=2%uZkR^x%nPo`F?fLr^0G?riL79h%Cx|aq4 z=@B~M2~;5DG)f`Y66cvbBUE&~Ta>4z&3F-xL5c_5q<%U8qq%TNU{-Z+d$~QCzmgz} z#an!`5>EYAvaxm@Uw<(%^r63H{@{8dLTZ40r_HoP-Gd-=LIgY70KYt7&4wI}Hvhi; z_lpH!JlSLTvYOBh)O1In#Hc1>_S3D>$0g!4wDcN7pg<7$Sk{yT$-D497Nox64N ztPfX#0@r#XhjI=GZYHY?_&+uP@5|?cv{TX$i;XRI6o8xByH)8q7YcTWXRy?G1e(Z2 zevac>e<-9AbDcMtA4Cm7a_SCrwE|Ke>u%Q+bsh}NYdMjQ_YmUW7ui{o^_mRo#F}9R z6^#N3@IKwa6qOOKYLw`j!!(@Y?e12jw+F1g~pY_l&CuBEG*31b7)S)yhDV#^22pxMpU zG2mz2V9$KdQ(Z$=Vfim<}YFzgugF08~Z=zHbQD!8>nmw-O=w za-pwq3A&#MxRyMIul!cP;qJXpi0V%g(0jijz7zF?KuvmH5S1H|FAT0FfPRYcbNaTzJanEB zK^IBD4Dmq% zhmoj5OP-R2T5H*a);>PnM$h{+=1EYiQU6kU{;90(+*|I*IU%~xd!EG-QWM2=>SHHL zX%<+MpdtSYfKQ%0>G8?k;T%&xpVPC(If@x>W^MnZ)8c-$1h`ts!G2s5N_%AEkB?e=2IZ_Bbr7QbVmB4_y zs4UMN%rLl}_TMvxQqJtCTFck^BEK(mEE%%{bgl#oWUxt2AmaOfD~bV%t`Ul^^D%$Z zxuPTFDL3I5`rjZE8tCAe{g*Jq;K3SGDy8aV7BEi6GC#S`oS;Rc3Y&GDGs<eg1zd z8H_j@93a?Eifb$o(fRzy6kcX7}F~C__T< z$!Mq&b7p`1s%OwP{+G{9YkI^JPPT}kf2jdj8)}HASv>qPw@z;Ta<T4Y0HAu?RBgy@?0#=5n51 z_y6CMPhkSjqo4E&vhVAYu4WrtdE_ajm3R;qM%W=&lrwENgP8%!q2Ky-1p+D=5|Swj zmF#?9R!hbV>J3F(fLe5#TZ@ezOkL{%+-u1JCn0$MB;FBqqBFA zl6TjI5>n%h$~_%DLqVnI-BO5h_}a=SN#?Jako*KmzFfEzN6Hl1se}miA5;$Y^3#ke z3)J)M>2c0|*o9r{H<@L*iNC5c8873co|I*M;7cp-pVBNh%*yQyYoe>e8`iaqE3-s6 z)~8_Mkc$<4S%P5^e+H_7KtmdwdHQYaGsS9?SuPFwdJPwR8xh(`b==zDZM$kEyIy;# zrpybwlTp7?4%86*L0nq7yFI+?2Jw*SH84Q6ZB@CL$_*$Y2aiTO5>9k&zwrkagvAT5 zgx~`w?ABVttfhs6xiAyWG|W$pl22HKkz*5st5q)8A?cpzI$AVt;^ zJ6_5AYNe46X32INmdD&5PfZON05VA9E$V(iA@tR@@Cc+)QDp*W-FmlVVhHCF3P?yF z9ag$Q#`1Bp&Pq8eJt#@KOIXPOn0j0RKilqB&(m&5HwM%iF}>PGq8r7uD%tqDHTv)7 zKofUwlGfJo{)^B`4-!VgcB5JjpGW@XV_HhIikjSm(Mzsx=aU6C_r7xoWST4kSlALf zMYMO6!B>}{Auf%m;l*69wWOJu)biP;mwci?T({4KJi+3nMv=~?RhjchI(K6sR)Gxz zOC1mmGFJf>f8B%3|43V1c9OgGkyN%~E9)yJ6-5ZH7d%8K6JdERseM-cQ?Odw% zF}}m<#gYUs?|MPiTcK)q*LN(2k86~bm0xOL^C!9J(%Y3?4_o@$x8%uRx!eF>106zS z*E=XjX4jQFRjPFGoZ?e23Fzq>A=;QPkdJS#rWEuH$5w4BhW zIJiJQwsg~j7G(E}s{kH5*Fx7edKN)qsBmph*{AL;7~x4C+IrkiKL6_E`KwcnXa|0q z@p@wA!{xS9Q|nzu?k0dPg>W`5)e$w1J;iCCI9943jQ!eP;37}LzuCs~yfeV?CwU*w z%Hj*9s4W;{@CF?<#X92mm0phH4K)$SR!FE`Wh)$O>H)UcbJ1e9>&$J_{H)z+naZd( zy4Nn_K)yf*IYK{Kti;mLSL=K|3k;4+v)w9)wz`LP3^rGkj7;~_WSbinU&YMs_G}J@62*{C_D53Zp}T1ewUwr2e20ltnEJUy18B{h~`2| zH@Wympy(@emSO#F>Dyt*RwWm^>CbDkA=!AIb->8{Ah8RNkouWm5&w)WX4A~IG+qLhyVC{;q-FLlqge; zwUJlT9T3)6$eG+24Ta?PsFID$sQiU!|Tnn#fpzLs2Ib>%fJW zN&Eiiw^q++TaP-OjcD#%N7EL2Pg}g9C4r>o>k8U0K530Lb@}u_!<}8*M`RQNIdg<* zko=A`+B*tXEmx(T(}{EGAvcIBQ;*&plc`MOJbBXKa+)+M`Kr|OV@aAM^;fmc_mc;L zvp&)sZLMYTuQ|#NPs-g35a0rM9ezrYKg*#$N6b_i(RVgkG=9}7Pvm9&vZmSkRnWXL ziOPbw_r++|lT|_u=`Tm6Y&AJ^h_Tr3i%X!@nb%lVbg)#QQL|G(AM5e$2sK0;ZPLwdV zE`L3GCO9PT$wM6*r=)Yv^Xw!k!bT-yh9IGH>BH7Sl^gha}}neTZI;KTM#fvqKpLmB(8ufD{HF0~x%*E_^deO)4f3OiEi8YZskcDk?6828m z&s+TLWsW1YLc7PfAjp+oo4P*T#^<#I5Y-yYgW5UG;;2-YxUWl)V968hA6$BS& zjY6i=20q4{fL;xSDs8tjd25wHNEa|P-}vT-0}%9t(Z`GNE&2V~-(fO&dB<^g11w9- zJpu)j$xKYi9y%w+iEL&#Z0CoELF%ELo>j04UU))E^&UWF>S--}B4X+bII~R*vZEQV!1Bq3@vE^`entNyOeFub$J?rA2^uw^&eC&}ykX)VmaJq9>ArmsRhkCIZ zhh~?yq^4?HLQ%K(&U}DPjthfyKQyr(j`NBzE$~@rjT`D(k_4vt)isnr=Om=`cH_(W{^ZlyeCc51P^b(4 zt6<%#utliYVe6?q3pJ(uvhAHpkxdY%>m{J8Ok>67-E)lHs!BO3r26^Cg)Ibq<=Th7 ziEO>I@cg_Q#cq#8&(1v|ereqEK)fqt*2iTO9`sz@y>yK^0p5GvFT4Uy76EYb)Ex*w znA4C>Ad4`gwvF#f%>$@U8%{GH$}xT;W%MKH1HUa|dn!Uh1%431q#B`)B9oZo^SY@+ z7n0fgwhOdsqs9hbVh_~sfaF-|#aF`s4sh!)5Sg>;3J3Myj(wV)R>tc>tPf-nEu;L_SNM;) z{=g=2$pBE%)(WUgdv<=I&xrn5!>Ovs8;aM@1M^;~|FA{B_cQ>D(Oj-Sy!a!x>j^iB5Z0-^t9!Wibtap(fZdF=Po(`cd07{cc4q@ z*TcZdb94MR0n&u0ak&4Q8yjzBahhsMAnn`WqemGYpSf7h7{SOlqe8;!8z_a9WwhFV z+)qID*%6Wctv~h&7?5OO6iqmLeFlvhXb?nX7Q=^ZVS1yYYC%W59<7vcb?jrU?6#^P zR3*j5pRBHw)p|q`V<6q#2H-CPszLGzkH`St3H7DLG-3)Gx;@sA))@NBotc4@?aLnu zwG)j+)9~K~vk}-XI^Yd-P=z?ULDiiw& z-}=|%QmScPPidYXF;0CiP`Y}tDcN`5CXX1;eyUKXeKqK-q!jsu`3yf>^lRW2N;EE! z$-&2pXo8O$fK5b8k=c9WAxdb}p`-ybP&AT2hf6t*WO-H9xYCv`T-sPvcjpd)S zFSgNFk!x==y{2HMmFcm=V~|EH%1uRq6tX5C)vXX%JDZr!Hcx=Vty5!wpu)(&bz?I# zRat5_xnN@baG-hgoe8RpWosjn(h+7g37)xaS%rx(e!brUagWqvoFqF!aB7^59ss zEV|4-s+Bg)J;Q9LIG|a<;HSy$P%j&p^<6KqU+9R)!8^7xvQDqe&n;JLEhDMR|K3uF zb<7uFIW6I7pk?~{6FtSezN+!OdV2!tSA6!2Z61v~7OgN^&#BpPS>0WX>tm(5^+?fm zO1$+G)XZ6pU5b@Stz`qpzdsQ_OPpxxysbwUr5{wyqU|u%O=0&`K=A-a+tQYm#EV&$ zUmhPSj)5Y5sA9bO$!*1f*4=hM{#rt|=;OAv?~2#zLMiB0JBP%ptG)`^#Z=zS8tu(a z^77ioq@QI<9pEGn$`Wwkz0O+YP#=io+sI05PHHQnahN6?3-eunvtlx#;G5ZA!^)&J z7GOysNsWDQO5wzJ)}27yi*%98*&LbflDJ4Ncrs1XG~rCZTNJ|U?l#?LAG4?JceC1= z$@gT`Mr}h_ zInSxb!cA@iZ-&ReVXZU+>#wsJPOLMWp5%JFFi#Un4~k~dWaiyuBit#qS=O&Dk9LWA zk+xjpyl>p_!X>0SHnU_^=M~mtZ#1zE5;9w5iNv{64uRm_+O*b|k6_f8uqxwvy1&d~ z8|&n;r^Y~;>~X6n)fyjAYbujaY#={wc9LvJGA`DS-{I+%k~OzQu<9H^K7FyI0jI)M zP#S<5Bh!n}xxhQIjoZm(Hs0)@?Pt26?ZZaQ?P_{tYL*cACqu_<2nSa$%vdW^xXk4^ zDvfhd!?e}~WZjlw6rICLD6u&M+sr@pC!TG2;Gg*PvTWA(3SZ$?Eqpx}4nF(;6 zQH`y45%~(ik5vh;#s67!dyXtL*c%-{3-6$SJWP6B_G|B5WN5gTN*hb$=-I5VN}K6K zxw2zu%B#g_T0aS+PUT%lK=eSmS-Yg^zv5uWP-0 zk2$owVm+g3KFcg0LrDpviSvLHDr#CBX$D%zsJ18>CVt`e@tV4mN}46|!;lzW^UVOM z**Zr4;~-)*0*o$nd{%Z68V+p-7>(s&s-EcwUR6u0420&y)4Ok<-6<?IL1AH$l+3KL=*~@M#Yn)7ph?Ht6^{V$TvXWp=Ay@cevg-LGY-OZ`|Cr&{l24&#+K zSF5=Zp2Y^!N3m9g%NmOO7Zjv4VN+v9$F19*$;$85JZ02}%{&a%CiZSk#0G41p1o9L zo**z8l#c&G>dVuzV=%Me*)}@yQgkBz=qh5_s~rmq>#^^&RkHd{TmR%RdVkqb$B5m} z(Y_;$={MOD{bCZ+W&ABwSY1dr06lR#=nh3+BQ3TW_P8DN5aMfAo1o#EY}R5zCmr2U z*Yg;QAd<*?lq=y&W?X!%i+m~RcO%TS!j^Rrd^hcLqkcpXA9yE95zHBho!cuMCUrF zT$kaaOS>t;I#p^k^FKRK`NcB!^aP*LXP#S=a1lH$>?@;tl$n}5`q`t5?)_u@OfO1# zD#TxJY2lRX5|!d}Ps&iDWIx2#mf<6Ipr>yIjXq`?+%t$6CBtvS=Bs=ljKRYIc;Tt941@Il{7hY0pftg$Ck%9^{7oAPS?8@pc&q@G86 zm9nu7Ez=)2-iHro=t;(SPITBjDZBk6Q;V;eP(j#R8P! zvMCFPoxLf$%VQFZm?|D^5`nWD_cSZR$2a9QN4S55-Xa~w#7+8}#k`&oIWMBPsps^N zHQ3f07J7Q;es?J(-AqCi^R#Xa-!*S}HoigjFG<_XvYinL|1b)Qqh^E2?6(pOLhPhq z1d;H(2lX#6DQ>sgYg48(*E%YZwR7SGhbS;gJ)VAW$#u^4KTAwayMX+uyzlXLen|~;&*J3X3_<8u&S==9MCSX8vV0N24a*HE(?Nu9_$?@!e`#_an4u0A)y2$z~ZH&<4IG5u@7IL0pP8lzVhsez2) zy3H9ouMBcX|5{%dO^E`RsR^=~uPxC&>IG)nq^4 zc<%e8rZTQD{bH2-ID2iN^IRv5^Jg_4-=Cb1V$*X=x{49ZZ`c{CIEFEND~$)u1=UQz z4Rpi>=+LB{7~z@+F=BO*UUiGlUub`b>Sd!5{nfsYQpJbMJRU{hEyKuZ#HCLd;a^2n z7xFDisB-=&NJrDnv;hITR3Gcz+0w@l>Vcok6c&A0%OB1%Fmw%9FzV0-)PyTd97w59 zQ4Gdpv8?C@t_xGSKfhHW^J(y^$*=qJR+6pJ;5)(Xmr6a0V76GvLYYF<{C%WO9{q+9P8%NHdP$dx}-Fpy(x309A6_D+o8rS@Bmcf0*b4f zR^Jzj3voSb7dx9V6~{nn0gP`rHgz21{6%#B#LbS_as<`y30N&gTKlx8@+V)efMqHw zQW;VDcH*L*V3a*h*?Ctd5>jE#O6}fH1JQ8_jQt+nzK@iR$i~%dIR1z>kI$hBJ?Ygs znWXKuiQ(mYBJ26A-A82D!>N~Stq3-&F2ZW=ukZ21#-(eZCn4l;ar^Jp&$Rbk-DoCxYD38SOBv-9#x7H2e~2Kus@332jvlROTuQcVS{?{QTYsFt1=#5 z`j&zx!Sy~e%Pi5HdmCemJ#dQ)+?~W>=SvB3u?_aaY2uVgHWSdY#e(;GHQO~f1ln-J z$9Imd5X31j8Q&lyiO(PBoyMTfYf&YBjte~_!Enj+TPtx;Xp-|g;HR<|+obpGR#6Z> zLZL?_zCwPBMf70Ul*@9`F)@x?iX1)-yV;j*4U{Wu?JlosY@8qVC*m4%>snBp%rD za4LJDF$xMwq28wHzhw^RTbL}2=sxL9&*YMNz^h9__*i_q!h7lxSnyC%qJKX8`h2Xc zR8QU`xLtG&*Q=(`yikmig8tJ9ZI6KXdux}bnEKx(3$f%6`xP+@5j!HIxnmGV;#&`R zr3Qn-N$5_9kV_X5jl~{`v%mcQ8i&Q`5P7R@oUtFJ@08kL!Ewr=(gbyk^;k5bYgL?| zJ_KxqcqtOW!MOf})zTgDp4TM@10B6S3>+QseBD~4;_t>!V`z4Fe^3(Z}NuMb`9IQ?1ON{vqOcVH+F6Qv;u+)Z@Nd$M>&25k(8?#QMFnWC?hO2R(#^$s9fI zZvQp|y(~_%Nqxh^`Q~Z`=sPS5D$k0%fh6O2pUo%#)FLkO)%C{OBSoxhuULAlfdJmd@n7_m zBun{g@DmhQ3js}Whx(U|B(`S(A(qnpSwJ{ou~dxZaZTH z#l&n63A3;jyRVx@&_Cg*@y6;8qMs*?6Ey73#aA<(j?WdirbQeVcM?&K?vVBHON{&> zmOYrxGjn#6yVdu;284?ldMWN1-u!_nt(GBq;_PdY?bpH~k_z*M6Vnd+R8)>w%rJJp z6TGx!)9#iyV=aj&|LHi4L<5Nd%E*Uf%D1cYobgf$+GztG@166GmK=vk8QmnK_B(K~ zlV>q{oK|-~WX#|VsQGLqXm?SD#fdHT#y>qJ>J^01wLeuiIDi4@NoaZe43 z4NGKnQojs{#qpYk@!Fp-b5r8G|>OmnHgF;hD5I+>TXLYA9FoEEK8FUYtA z@uO0J;Wjw^{i5KohF<2xB(289B%~<#p{)A2rP&xLk&tiAk|^m&@- zK8QmBpIj<0Y#5oXS)K!m{orl2x`;2zsy=P9xr6_`e+iRDUDoPB>Jr_5pVDVX5Y(PC zbWZ*JUp9aYWdngvUgJjpFNvrk1fzXf-8^jazZ~NE1K16w+Y5K(FZ-!RaNwVkysBJ{ I%=4iC2b^-kApigX diff --git a/docs/tutorials/nb_binning.ipynb b/docs/tutorials/nb_binning.ipynb deleted file mode 100644 index 41f4acb7..00000000 --- a/docs/tutorials/nb_binning.ipynb +++ /dev/null @@ -1,642 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Binning\n", - "\n", - "[![open in colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ing-bank/probatus/blob/master/docs/tutorials/nb_binning.ipynb)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "%%capture\n", - "!pip install probatus" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "%config Completer.use_jedi = False\n", - "%load_ext autoreload\n", - "%autoreload 2\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import pandas as pd\n", - "\n", - "pd.set_option(\"display.max_columns\", 100)\n", - "pd.set_option(\"display.max_row\", 500)\n", - "pd.set_option(\"display.max_colwidth\", 200)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This notebook explains how the various implemented binning strategies of `probatus` work. \n", - "First, we import all binning strategies:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "from probatus.binning import AgglomerativeBucketer, QuantileBucketer, SimpleBucketer, TreeBucketer" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's create some data on which we want to apply the binning strategies. We choose a logistic function because it clearly supports the explanation on how binning strategies work. Moreover, the typical reliability curve for a trained random forest model has this shape and binning strategies could be used for probability calibration (see also the website of Scikit-learn on [probability calibration](https://scikit-learn.org/stable/modules/calibration.html))." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "

" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "def log_function(x):\n", - " return 1 / (1 + np.exp(-x))\n", - "\n", - "\n", - "x = [log_function(x) for x in np.arange(-10, 10, 0.01)]\n", - "\n", - "plt.plot(x);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Simple binning" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `SimpleBucketer` object creates binning of the values of `x` into equally sized bins. The attributes `counts`, the number of elements per bin, and `boundaries`, the actual boundaries that resulted from the binning strategy, are assigned to the object instance. In this example we choose to get 4 bins:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "counts [891 109 110 890]\n", - "boundaries [4.53978687e-05 2.50022585e-01 4.99999772e-01 7.49976959e-01\n", - " 9.99954146e-01]\n" - ] - } - ], - "source": [ - "mySimpleBucketer = SimpleBucketer(bin_count=4)\n", - "mySimpleBucketer.fit(x)\n", - "print(\"counts\", mySimpleBucketer.counts_)\n", - "print(\"boundaries\", mySimpleBucketer.boundaries_)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "df = pd.DataFrame({\"x\": x})\n", - "df[\"label\"] = pd.cut(x, bins=mySimpleBucketer.boundaries_, include_lowest=True)\n", - "\n", - "fig, ax = plt.subplots()\n", - "for label in df.label.unique():\n", - " df[df.label == label].plot(ax=ax, y=\"x\", legend=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As can be seen, the number of elements in the tails of the data is larger than in the middle:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "df.groupby(\"label\")[\"x\"].count().plot(kind=\"bar\")\n", - "plt.title(\"Histogram\");" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Quantile binning " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `QuantileBucketer` object creates bins that all contain an equal amount of samples" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "counts [500 500 500 500]\n", - "boundaries [4.53978687e-05 6.67631251e-03 4.98750010e-01 9.93257042e-01\n", - " 9.99954146e-01]\n" - ] - } - ], - "source": [ - "myQuantileBucketer = QuantileBucketer(bin_count=4)\n", - "myQuantileBucketer.fit(x)\n", - "print(\"counts\", myQuantileBucketer.counts_)\n", - "print(\"boundaries\", myQuantileBucketer.boundaries_)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "df = pd.DataFrame({\"x\": x})\n", - "df[\"label\"] = pd.cut(x, bins=myQuantileBucketer.boundaries_, include_lowest=True)\n", - "\n", - "fig, ax = plt.subplots()\n", - "for label in df.label.unique():\n", - " df[df.label == label].plot(ax=ax, y=\"x\", legend=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As can be seen, the number of elements is the same in all bins:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "df.groupby(\"label\")[\"x\"].count().plot(kind=\"bar\")\n", - "plt.title(\"Histogram\");" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Binning by agglomerative clustering" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `AgglomerativeBucketer` class applies the Scikit-Learn `AgglomerativeClustering` algorithm to the data and uses the clusters to determine the bins.\n", - "We use different data to show the value of this algoritm; we create the following distribution:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "x_agglomerative = np.append(np.random.normal(0, 1, size=1000), np.random.normal(6, 0.2, size=50))\n", - "plt.hist(x_agglomerative, bins=20)\n", - "plt.title(\"Sample data\");" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When we apply the `AgglomerativeBucketer` algorithm with 2 bins, we see that the algorithm nicely creates a split in between the two centers" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "counts [1000 50]\n", - "boundaries [-2.71525699097944, 4.582406874429196, 6.454492599188006]\n" - ] - } - ], - "source": [ - "myAgglomerativeBucketer = AgglomerativeBucketer(bin_count=2)\n", - "myAgglomerativeBucketer.fit(x_agglomerative)\n", - "print(\"counts\", myAgglomerativeBucketer.counts_)\n", - "print(\"boundaries\", myAgglomerativeBucketer.boundaries_)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "df = pd.DataFrame({\"x\": x_agglomerative})\n", - "df[\"label\"] = pd.cut(x_agglomerative, bins=myAgglomerativeBucketer.boundaries_, include_lowest=True)\n", - "\n", - "fig, ax = plt.subplots()\n", - "for label in df.label.unique():\n", - " df[df.label == label].hist(ax=ax)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that the `SimpleBucketer` strategy would just have created a split in the middle of the maximum and the minimum (at about 1.75). The `QuantileBucketer` strategy had created two bins with equal amount of elements in it, resulting in a split at around 0." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "counts_agglomerative_simple, boundaries_agglomerative_simple = SimpleBucketer.simple_bins(x_agglomerative, 2)\n", - "\n", - "df = pd.DataFrame({\"x\": x_agglomerative})\n", - "df[\"label\"] = pd.cut(x_agglomerative, bins=boundaries_agglomerative_simple, include_lowest=True)\n", - "\n", - "fig, ax = plt.subplots()\n", - "for label in df.label.unique():\n", - " df[df.label == label].hist(ax=ax)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "counts_agglomerative_quantile, boundaries_agglomerative_quantile = QuantileBucketer.quantile_bins(x_agglomerative, 2)\n", - "\n", - "df = pd.DataFrame({\"x\": x_agglomerative})\n", - "df[\"label\"] = pd.cut(x_agglomerative, bins=boundaries_agglomerative_quantile, include_lowest=True)\n", - "\n", - "fig, ax = plt.subplots()\n", - "for label in df.label.unique():\n", - " df[df.label == label].hist(ax=ax)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Binning with Decision Trees\n", - "\n", - "Binning with decision trees leverages the information of a binary feature or the binary target in order to create buckets that have a significantly different proportion of the binary feature/target.
\n", - "\n", - "It works by fitting a tree on 1 feature only.
\n", - "It leverages the properties of the split finder algorithm in the decision tree. The splits are done to maximize the gini/entropy.
\n", - "The leaves approximate the optimal bins.\n", - "\n", - "The example below shows a distribution defined by a step function" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "def make_step_function(x):\n", - " if x < 4:\n", - " return 0.001\n", - " elif x < 6:\n", - " return 0.3\n", - " elif x < 8:\n", - " return 0.5\n", - " elif x < 9:\n", - " return 0.95\n", - " else:\n", - " return 0.9999\n", - "\n", - "\n", - "x = np.arange(0, 10, 0.001)\n", - "probs = [make_step_function(x_) for x_ in x]\n", - "\n", - "y = np.array([1 if np.random.rand() < prob else 0 for prob in probs])\n", - "\n", - "fig, ax = plt.subplots()\n", - "ax2 = ax.twinx()\n", - "\n", - "ax.hist(x[y == 0], alpha=0.15)\n", - "ax.hist(x[y == 1], alpha=0.15)\n", - "ax2.plot(x, probs, color=\"black\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The light blue histogram indicates the distribution of class 0 (`y=0`), while the light orange histogram indicates the distribution of class 1 (`y=1`).
\n", - "The black line indicates the probability function that isused to assign class 0 or 1. In this toy example, it's a step function." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 5/5 [00:00<00:00, 17985.87it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "counts by TreeBucketer: [4000 1998 2001 936 1065]\n", - "counts by QuantileBucketer: [625 625 625 625 625 625 625 625 625 625 625 625 625 625 625 625]\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "# Try a tree bucketer\n", - "tb = TreeBucketer(\n", - " inf_edges=True,\n", - " max_depth=4,\n", - " criterion=\"entropy\",\n", - " min_samples_leaf=400, # Minimum number of entries in the bins\n", - " min_impurity_decrease=0.001,\n", - ").fit(x, y)\n", - "\n", - "counts_tree, boundaries_tree = tb.counts_, tb.boundaries_\n", - "\n", - "df_tree = pd.DataFrame({\"x\": x, \"y\": y, \"probs\": probs})\n", - "\n", - "df_tree[\"label\"] = pd.cut(x, bins=boundaries_tree, include_lowest=True)\n", - "\n", - "# Try a quantile bucketer\n", - "myQuantileBucketer = QuantileBucketer(bin_count=16)\n", - "myQuantileBucketer.fit(x)\n", - "q_boundaries = myQuantileBucketer.boundaries_\n", - "q_counts = myQuantileBucketer.counts_\n", - "\n", - "df_q = pd.DataFrame({\"x\": x, \"y\": y, \"probs\": probs})\n", - "df_q[\"label\"] = pd.cut(x, bins=q_boundaries, include_lowest=True)\n", - "\n", - "\n", - "fig, ax = plt.subplots(1, 2, figsize=(12, 5))\n", - "\n", - "for label in df_tree.label.unique():\n", - " df_tree[df_tree.label == label].plot(ax=ax[0], x=\"x\", y=\"probs\", legend=False)\n", - " ax[0].scatter(df_tree[df_tree.label == label][\"x\"].mean(), df_tree[df_tree.label == label][\"y\"].mean())\n", - " ax[0].set_title(\"Tree bucketer\")\n", - "\n", - "for label in df_q.label.unique():\n", - " df_q[df_q.label == label].plot(ax=ax[1], x=\"x\", y=\"probs\", legend=False)\n", - " ax[1].scatter(df_q[df_q.label == label][\"x\"].mean(), df_q[df_q.label == label][\"y\"].mean())\n", - " ax[1].set_title(\"Quantile bucketer\")\n", - "\n", - "print(f\"counts by TreeBucketer: {counts_tree}\")\n", - "print(f\"counts by QuantileBucketer: {q_counts}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Comparing the `TreeBucketer` and the `QuantileBucketer` (the dots compare the average distribution of class 1 in the bin):
\n", - "Each buckets obtained by the `TreeBucketer` follow the probability distribution (i.e. the entries in the bucket have the same probability of being class 1).
\n", - "On the contrary, the `QuantileBucketer` splits the values below 4 in 6 buckets, which all have the same probability of being class 1.
\n", - "Note also that the tree is grown with the maximum depth of 4, which potentially lets it grow up to 16 buckets ($2^4$).
\n", - "\n", - "The learned tree is visualized below, whreere the splitting according to the step function is visualized clearly.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "from sklearn.tree import plot_tree\n", - "\n", - "fig, ax = plt.subplots(figsize=(12, 5))\n", - "tre_out = plot_tree(tb.tree, ax=ax)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.3-final" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/nb_distribution_statistics.ipynb b/docs/tutorials/nb_distribution_statistics.ipynb deleted file mode 100644 index 9c1e87a2..00000000 --- a/docs/tutorials/nb_distribution_statistics.ipynb +++ /dev/null @@ -1,513 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Univariate Distribution Similarity\n", - "\n", - "[![open in colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ing-bank/probatus/blob/master/docs/tutorials/nb_distribution_statistics.ipynb)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There are many situations when you want to perform univariate distribution comparison of a given feature, e.g. stability of the feature over different months.\n", - "\n", - "In order to do that, you can use statistical tests. In this tutorial we present how to easily do this using the `DistributionStatistics` class, and with the statistical tests directly.\n", - "\n", - "Available tests:\n", - "- `'ES'`: Epps-Singleton\n", - "- `'KS'`: Kolmogorov-Smirnov\n", - "- `'PSI'`: Population Stability Index\n", - "- `'SW'`: Shapiro-Wilk\n", - "- `'AD'`: Anderson-Darling\n", - "\n", - "Details on the available tests can be found [here](https://ing-bank.github.io/probatus/api/stat_tests.html#available-tests).\n", - "\n", - "You can perform all these tests using a convenient wrapper class called `DistributionStatistics`.\n", - "\n", - "In this tutorial we will focus on how to perform two useful tests: Population Stability Index (widely applied in banking industry) and Kolmogorov-Smirnov." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "%%capture\n", - "!pip install probatus" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import pandas as pd\n", - "\n", - "from probatus.binning import QuantileBucketer\n", - "from probatus.stat_tests import DistributionStatistics, ks, psi" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's define some test distributions and visualize them. For these examples, we will use a normal distribution and a shifted version of the same distribution." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "counts = 1000\n", - "np.random.seed(0)\n", - "d1 = pd.Series(np.random.normal(size=counts), name=\"feature_1\")\n", - "d2 = pd.Series(np.random.normal(loc=0.5, size=counts), name=\"feature_1\")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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PAS8DuzFCSBn8xc4y04PAD3AIzh7K2czhPNnD3DI91lERBaIGp2LiIiIiIgsE8rgiYiIiIiILBMK8ERERCQoY8y9vgbq03/du9BzExGRmbREU0REREREZJlQBk9ERERERGSZSFroCUSqqKjIVldXL/Q0REREREREFsTevXs7rbXFga4tuQCvurqaPXv2LPQ0REREREREFoQxpj7YNS3RFBERERERWSYU4ImIiIiIiCwTCvBERERERESWiSW3B09ERERERBa/sbExmpqaGB4eXuipLFlpaWlUVlaSnJwc9jMK8EREREREJOaamprIzs6muroaY8xCT2fJsdbS1dVFU1MTNTU1YT+nJZoiIiIiIhJzw8PDFBYWKriLkjGGwsLCiDOgCvBERERERCQuFNzNTTTfPwV4IiIiIiIiMVBXV8cDDzwQ8XN33HEHjzzySEzmoABPREREREQkBqIN8GJJAZ6IiIiIiCxLP/nJTzjvvPPYvn07H/7wh6mvr2ft2rV0dnbi9Xq55JJLeOKJJ6irq2PDhg3cfvvtbN26lZtvvhm32w3A3r17ueyyyzjnnHO4+uqraWlpAeDYsWNceeWVbNu2jbPPPpvjx49z1113sXv3brZv3843vvENPB4Pf/d3f8e5557L1q1b+c53vgM4BVQ+/vGPs2nTJq677jra29tj9jWriqaIiIiIiMTV3/9qPwea+2M65qaKHP6/6zcHvX7w4EEeeughnnnmGZKTk/nYxz7Grl27uPPOO/nIRz7C+eefz6ZNm7jqqquoq6vj8OHD3H///Vx00UW8//3v59vf/jZ//dd/zSc+8QkeffRRiouLeeihh/j85z/P97//fW677TbuuusubrrpJoaHh/F6vXzta1/j3/7t3/j1r38NwH333Udubi4vvfQSIyMjXHTRRVx11VW88sorHD58mDfeeIO2tjY2bdrE+9///ph8XxTgiYiILCVjQ9DwPKw4B9JyFno2IiKL1h/+8Af27t3LueeeC8DQ0BAlJSV8+ctf5uGHH+bee+/l1Vdfnbi/qqqKiy66CID3vOc93H333VxzzTXs27ePt771rQB4PB7Ky8txuVycPHmSm266CXD61QXyxBNP8Prrr0/sr+vr6+Po0aM8/fTT3HrrrSQmJlJRUcFb3vKWmH3dCvBERESWksc/D3vuh9It8OFdkJC40DMSEZlV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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "from probatus.utils.plots import plot_distributions_of_feature\n", - "\n", - "feature_distributions = [d1, d2]\n", - "sample_names = [\"expected\", \"actual\"]\n", - "plot_distributions_of_feature(feature_distributions, sample_names=sample_names, plot_perc_outliers_removed=0.01)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Binning - QuantileBucketer\n", - "\n", - "To visualize the data, we will bin the data using a quantile bucketer, available in the `probatus.binning` module.\n", - "\n", - "Binning is used by all the `stats_tests` in order to group observations." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Bincounts for d1 and d2:\n", - "[100 100 100 100 100 100 100 100 100 100]\n", - "[ 25 62 50 68 76 90 84 169 149 227]\n" - ] - } - ], - "source": [ - "bins = 10\n", - "myBucketer = QuantileBucketer(bins)\n", - "d1_bincounts = myBucketer.fit_compute(d1)\n", - "d2_bincounts = myBucketer.compute(d2)\n", - "\n", - "print(\"Bincounts for d1 and d2:\")\n", - "print(d1_bincounts)\n", - "print(d2_bincounts)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's plot the distribution for which we will calculate the statistics." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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+gwa1LcfyQ033JDx48dn2LBhGTt2bE499dTuO2q99NJL2WWXXbL33nun1tp9zqc//emMGjUqv/nNb5Ikxx57bMaPH5/vfve72XbbbTNs2LDsueeeeeCBBxa71ssvv5zzzz8/b37zm7Puuutmm222yZVXXvmamr7zne9kt912y/DhwzNy5Mh84AMfyJw5czJt2rRccMEFmTNnTkopKaXk2GOP7T7v3//937PXXntlvfXWy8iRI/OJT3wizz333GJj33777dlpp50ybNiw7LrrrvnZz37WV28lAAAArDUEQqvJDTfckJNPPjmTJk3K9ddfnx122CHHH398r8Z65JFHVmh/oeuuuy6HHHJIdtttt9x4442ZOnVqLrnkkpx+emuDrKFDh+aqq67KrFmz8o//+I9JkltvvTUXXXRRLrrooowdO7Z7rDlz5uSzn/1szjzzzFx99dV55plnst9+++XFF1/s7vPpT386X/ziF3PCCSfkpptuyuTJk3P88cfn+9//fnefr3/96znkkEOy9dZb57rrrsvXvva1bLPNNpk/f34+/vGP56Mf/WjGjh2bWbNmZdasWTnzzDOTJHfccUcmTJiQsWPHZubMmbnwwgtz880357jjjuse+4knnsj++++fjTbaKDNnzsyf//mf58gjj8zzzz/fq/cZAAAA1laWjK0m5557biZOnJjp06cnSfbbb7/Mnz+/V8vCSikZPHjwMmcT1Vrz+c9/PkcffXQuvvji7vZ11103J598ck4//fSMHDkyO+ywQ6ZNm5Yzzjgje+65Z4477rgcdthh+chHPrLYeE899VRuuOGGvOtd70qS7Lrrrtl6660zY8aMnHjiiZk9e3amT5+er33taznmmGOSJO973/syb968nH322Zk0aVJefvnlnHbaaZk8eXKuueaa7rEPOuig7uNNNtkk6667bnbffffFrn/aaaflXe96V6699trutnHjxmXChAm577778ra3vS0XXnhhhg0blptuuinrrbdekmT99dfPn/3Zn63sWwwAAABrNTOEVoOFCxfm3nvvzcEHH7xY+yGHHNKr8bbYYossWLAgRx999FL7PPzww3n00Ufz4Q9/OAsWLOj+ee9735sXX3wx9913X3ffU089NTvttFP23HPPvPjii4sFSIuMGTOmOwxaVMOuu+6aO++8M0ny4x//OIMGDcrkyZMXu96ECRPyq1/9KgsXLsxDDz2UJ554YrFZPSvi+eefz6xZs17zu+y5554ZOnRo7rnnniTJnXfemX333bc7DEp6/x4DAADA2swModVg/vz5WbBgQcaMGbNY+6uf96WnnnoqSfKBD3xgia8/9thj3ceDBw/OYYcdlp///Of5yEc+kpEjR76m/5JqHTNmTPc+SE899VQWLlyYDTfccInXmzdvXn77298mac0CWhlPP/10Fi5cmJNOOiknnXTSUn+X3/zmN9lxxx0Xe2348OHZYIMNVup6AAAAsLYTCK0Go0ePzpAhQ9LV1bVY+6uf96WNNtooSXLJJZdkl112ec3rW221Vffxo48+mrPPPju77LJL/vmf/zkf//jHs8MOOyy31q6urvzJn/xJ9/WGDBmSO+64Y4lL2caMGdO9AfSSNtNelte//vUppWTatGlLDLje+MY3JknGjh37mjpfeOGF/P73v1+p6wEAAMDaTiC0GgwePDg777xzbrjhhpx44ond7ddff32/XfOtb31rxo0bl0ceeSSf+MQnltqv1prjjz8+b3rTmzJr1qy8733vyzHHHJNf/OIXGTp0aHe/rq6u/OxnP+teNvboo4/ml7/8Zffyr/e+971ZuHBhnnnmmey7777LrOnKK6/MgQceuMQ+66yzzmIbVSetfYB23333PPTQQznrrLOW+ru84x3vyBVXXJHnn3++e9lYf77HAAAAMFAJhFaTM844I4ccckg++clPZvLkyfnpT3+aH/zgB6/p98ADD+SBBx7oDkXuvvvubLDBBhk9enT22muvJK07fm299da54oorlrqP0KBBg3LBBRfkqKOOyrPPPpv9998/66yzTn7961/nu9/9bmbOnJn11lsvF110Uf7t3/4td911V9Zdd93MmDEjO+20U84999xMmzate7xRo0blqKOOyjnnnJPhw4fnrLPOypgxY7pvC//Wt741J554Yg4//PCceuqpGT9+fF588cXcf//9efjhh3PZZZdl0KBB+fKXv5wjjzwyRx55ZI444oiUUvKTn/wkRxxxRMaPH59tt902Tz75ZGbMmJG3ve1tGTVqVLbccst8+ctfzoQJEzJo0KAceuihed3rXpdHH300N910U84999xss802mTJlSi666KJMmjQpn/3sZ/PEE0/kvPPOy/Dhw/v2wwQAAIABTiC0mkyePDlf/epXc/755+fKK6/M3nvvncsvvzz77bffYv2uu+66nH322d3PF90Cfq+99sptt92WpDWrZ+HChXn55ZeXec2PfOQjGTFiRP7mb/4mV1xxRQYPHpw3velNmTRpUtZZZ53Mnj07f/VXf5WpU6d2772z9dZb50tf+lKmTJmSgw46KG9/+9uTtDaRPuOMM3Laaadlzpw5GT9+fK655poMGzZssVq32WabXHrppTnrrLMyYsSIbL/99vnYxz7W3eejH/1ohg0blnPPPTeHHnpo9+yf0aNHJ0k+/OEP59Zbb82pp56a+fPn55hjjsmMGTOy55575vbbb8/UqVNz1FFHZeHChdliiy0yceLEbLzxxkladx27+eabc8opp+RDH/pQtttuu3zjG994zWbeAAAA0HSl1trpGjJ+/Ph69913L/X1Bx98MNttt91qrIiejj322Nx3331Z1mc00PkzBgAADHi3ntfpCtYO+5ze6Qr6TCnlnlrr+CW95rbzAAAAAA0jEAIAAABoGHsIsVwzZszodAkAAABAHzJDCAAAAKBhBEIAAAAADSMQAgAAAGgYgRAAAABAwwiEAAAAABpGIAQAAADQMAKhDrrvvvtSSsltt92WJHn22WczderU7Lbbbtlwww0zduzYTJ48OQ8//HDHauzq6sq0adPyyCOP9Ns1pk2bllGjRvXb+AAAAMDiBEJrkEcffTSXXnpp9ttvv8ycOTP/8i//knnz5uVP//RP89hjj3Wkpq6urpx99tn9GggBAAAAq9eQThewSm49rzPX3ef0fhl2q622yn//939n+PDh3W3vfve7s/nmm+eKK67I1KlT++W6AAAAQLOYIbQaXXzxxdlss82y/vrr58ADD8y8efMWe3399ddfLAxKko022ihbbLFFurq6Vvp6s2bNykEHHZQ3vvGNWX/99bPzzjvnm9/85mv6zZkzJ0cccURGjRqV9dZbLzvuuGOuvvrqPPLII9lhhx2SJPvss09KKSmlJElmzJiRUkp+//vfLzbWlltumb/8y7/sfn7TTTdl3333zZgxYzJixIjsvvvuueWWW1b6dwEAAAD6znIDoVLKZqWUW0spD5ZS7i+lfKbdvlEp5UellP9qP76h3V5KKV8ppcwupfxHKeXt/f1LDAQ33HBDTj755EyaNCnXX399dthhhxx//PHLPW/+/PmZPXt2tt9++8XaSymZNm3aMs+dM2dO9thjj1x22WX53ve+lw996EM57rjjcs0113T36erqyjvf+c7cdddd+bu/+7t873vfy8c+9rE89thj2WSTTboDpIsuuiizZs3KrFmzVur3/p//+Z8ceOCB+frXv55vf/vbede73pX9998/d9xxx0qNAwAAAPSdFVkytiDJ52qtvyylvC7JPaWUHyU5NsmPa63nl1JOS3Jakr9Ksn+St7R//jTJ9PZjo5177rmZOHFipk+fniTZb7/9Mn/+/Fx22WXLPO9zn/tcNthggxx++OGLtQ8ePDiDBi07z+t5Tq0173nPezJ37txceumlOeKII5Ik//AP/5Bnnnkm99xzTzbZZJMkyYQJE7rP23HHHZMk22+/fXbfffcV/G1f8alPfar7+OWXX84+++yT+++/P5dffnn22GOPlR4PAAAAWHXLnSFUa51Xa/1l+/i5JA8mGZfk4CRXtrtdmeSD7eODk1xVW36e5PWllE36vPIBZOHChbn33ntz8MEHL9Z+yCGHLPO86dOn5xvf+EYuu+yyjBw5crHXFixYkLPOOmuZ5z/99NM55ZRTssUWW2To0KEZOnRoLrnkksXuWvaTn/wkEydO7A6D+trcuXNzzDHHZNy4cRkyZEiGDh2aW265paN3TgMAAICmW6lNpUspWybZJckvkmxca52XtEKjUsqYdrdxSXreEmtuu23xDXMaZP78+VmwYEHGjBmzWPurn/d044035tOf/nS+9KUvZfLkyb267rHHHpuf//znOfPMM7P99ttnxIgRmT59em644YbuPr/97W/zjne8o1fjL8/LL7+cgw46KM8991y+8IUv5M1vfnPWX3/9nHXWWb3aEwkAAADoGyscCJVSNkjy7SRTaq3PLtpceEldl9BWlzDeCUlOSJLNN998RcsYkEaPHp0hQ4a8JgRZWijys5/9LIcffnhOPPHEfP7zn+/VNV988cXcdNNN+ad/+qeceOKJ3e0vv/zyYv1Gjhz5ms2tV8SwYcOSJH/84x8Xa3/66ae7j2fPnp177703//qv/5qJEyd2t7/wwgsrfT0AAACg76zQXcZKKUPTCoO+WWu9vt385KKlYO3HRenG3CSb9Th90yRPvHrMWusltdbxtdbxo0eP7m39A8LgwYOz8847LzYzJ0muv/761/S9//77M2nSpEycODFf+cpXen3NP/zhD1m4cGHWXXfd7rbnnnsuN95442L9JkyYkB/+8Id58sknlzjOOuusk6QVMPW06aabJkkefPDB7rZf/OIXefbZZ7ufLwp+etYwZ84cG0oDAABAhy13hlBpTQW6PMmDtda/7/HSjUmOSXJ++/GGHu2fKqV8K63NpJ9ZtLSsyc4444wccsgh+eQnP5nJkyfnpz/9aX7wgx8s1qerqysTJ07MBhtskFNOOSV33nln92sjRoxY7E5jQ4YMyVlnnbXUfYQ23HDDvOMd78gXvvCFjBgxIoMGDcr555+fDTfccLHQ5i/+4i9y1VVX5d3vfnf++q//OptttlkefPDB/O///m9OPfXUbL755hk+fHiuvPLKbLjhhhk6dGjGjx+f3XbbLePGjcspp5ySc845J7/73e/y5S9/OSNGjOgee9ttt82mm26az33ucznnnHPy3HPPZerUqRk3blxfva0AAABAL6zIDKE9khyV5L2llF+1fz6QVhC0bynlv5Ls236eJDcn+XWS2UkuTXJS35c98EyePDlf/epX873vfS8f/OAHc++99+byyy9frM8DDzyQuXPn5rHHHss+++yTd77znd0/J520+Nu4cOHC1yz/erWrr746W221VY4++uh85jOfyYc+9KEcffTRi/UZPXp07rjjjuyyyy6ZMmVKJk2alEsuuaR7Gd+wYcNy6aWX5p577slee+3Vvd/QOuusk+985zsZNGhQDj300FxwwQWZPn163vCGN3SPve666+b666/PkCFDcuihh+bMM8/M6aefnr322qvX7yMAAACw6kqtr9neZ7UbP358vfvuu5f6+oMPPpjttttuNVZE0/gzBgAADHi3ntfpCtYO+5ze6Qr6TCnlnlrr+CW9tkJ7CAEAAACw9hAIAQAAADSMQAgAAACgYQRCAAAAAA0jEAIAAABomAETCK0Jd0Nj7eTPFgAAAE0zIAKhoUOH5oUXXuh0GaylXnjhhQwdOrTTZQAAAMBqMyACoTFjxuTxxx/P888/bzYHfabWmueffz6PP/54xowZ0+lyAAAAYLUZ0ukCVsSIESOSJE888UReeumlDlfD2mTo0KHZeOONu/+MAQAAQBMMiEAoaYVC/qcdAAAAYNUNiCVjAAAAAPQdgRAAAABAwwiEAAAAABpGIAQAAADQMAIhAAAAgIYRCAEAAAA0jEAIAAAAoGEEQgAAAAANIxAC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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "plt.figure(figsize=(20, 5))\n", - "plt.bar(range(0, len(d1_bincounts)), d1_bincounts, label=\"d1: expected\")\n", - "plt.bar(range(0, len(d2_bincounts)), d2_bincounts, label=\"d2: actual\", alpha=0.5)\n", - "plt.title(\"PSI (bucketed)\", fontsize=16, fontweight=\"bold\")\n", - "plt.legend(fontsize=15)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "By visualizing the bins, we can already notice that the distributions are different.\n", - "\n", - "Let's use the statistical test to prove that." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## PSI - Population Stability Index\n", - "The population stability index ([Karakoulas, 2004](https://cms.rmau.org/uploadedFiles/Credit_Risk/Library/RMA_Journal/Other_Topics_(1998_to_present)/Empirical%20Validation%20of%20Retail%20Credit-Scoring%20Models.pdf)) has long been used to evaluate distribution similarity in the banking industry, while developing credit decision models.\n", - "\n", - "In `probatus` we have implemented the PSI according to [Yurdakul 2018](https://scholarworks.wmich.edu/cgi/viewcontent.cgi?article=4249&context=dissertations), which derives a p-value, based on the hard to interpret PSI statistic. Using the p-value is a more reliable choice, because the banking industry-standard PSI critical values of 0.1 and 0.25 are unreliable heuristics as there is a strong dependency on sample sizes and number of bins. Aside from these heuristics, the PSI value is not easily interpretable in the context of common statistical frameworks (like a p-value or confidence levels)." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "PSI = 0.33942407655561885\n", - "\n", - "PSI: Critical values defined according to de facto industry standard:\n", - "PSI > 0.25: Significant distribution change; investigate.\n", - "\n", - "PSI: Critical values defined according to Yurdakul (2018):\n", - "99.9% confident distributions have changed.\n" - ] - } - ], - "source": [ - "psi_value, p_value = psi(d1_bincounts, d2_bincounts, verbose=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Based on the above test, the distribution between the two samples significantly differ.\n", - "Not only is the PSI statistic above the commonly used critical value, but also the p-value shows a very high confidence." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## PSI with DistributionStatistics " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Using the `DistributionStatistics` class one can apply the above test without the need to manually perform the binning. We initialize a `DistributionStatistics` instance with the desired test, binning_strategy (or choose `\"default\"` to choose the test's most appropriate binning strategy) and the number of bins. Then we start the test with the unbinned values as input." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "PSI = 0.33942407655561885\n", - "\n", - "PSI: Critical values defined according to de facto industry standard:\n", - "PSI > 0.25: Significant distribution change; investigate.\n", - "\n", - "PSI: Critical values defined according to Yurdakul (2018):\n", - "99.9% confident distributions have changed.\n" - ] - } - ], - "source": [ - "distribution_test = DistributionStatistics(\"psi\", binning_strategy=\"default\", bin_count=10)\n", - "psi_value, p_value = distribution_test.compute(d1, d2, verbose=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## KS: Kolmogorov-Smirnov with DistributionStatistics\n", - "The Kolmogorov-Smirnov test compares two distributions by calculating the maximum difference of the two samples' distribution functions, as illustrated by the black arrow in the following figure. The KS test is available in `probatus.stat_tests.ks`.\n", - "\n", - "\"Example\n", - "\n", - "The main advantage of this method is its sensitivity to differences in both location and shape of the empirical cumulative distribution functions of the two samples.\n", - "\n", - "The main disadvantages are that: it works for continuous distributions (unless modified, e.g. see ([Jeng 2006](https://bmcmedresmethodol.biomedcentral.com/track/pdf/10.1186/1471-2288-6-45))); in large samples, small and unimportant differences can be statistically significant ([Taplin & Hunt 2019](https://www.mdpi.com/2227-9091/7/2/53/pdf)); and finally in small samples, large and important differences can be statistically insignificant ([Taplin & Hunt 2019](https://www.mdpi.com/2227-9091/7/2/53/pdf))." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As before, using the test requires you to perform the binning beforehand" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "KS: pvalue = 2.104700973377179e-27\n", - "\n", - "KS: Null hypothesis rejected with 99% confidence. Distributions very different.\n" - ] - } - ], - "source": [ - "k_value, p_value = ks(d1, d2, verbose=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Again, we can also choose to combine the binning and the statistical test using the `DistributionStatistics` class." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "KS: pvalue = 2.104700973377179e-27\n", - "\n", - "KS: Null hypothesis rejected with 99% confidence. Distributions very different.\n" - ] - } - ], - "source": [ - "distribution_test = DistributionStatistics(\"ks\", binning_strategy=None)\n", - "ks_value, p_value = distribution_test.compute(d1, d2, verbose=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## AutoDist" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "from probatus.stat_tests import AutoDist" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Multiple statistics can automatically be calculated using `AutoDist`. To show this, let's create two new dataframes with two features each." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "size, n_features = 100, 2\n", - "\n", - "df1 = pd.DataFrame(np.random.normal(size=(size, n_features)), columns=[f\"feat_{x}\" for x in range(n_features)])\n", - "df2 = pd.DataFrame(np.random.normal(size=(size, n_features)), columns=[f\"feat_{x}\" for x in range(n_features)])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can now specify the statistical tests we want to perform and the binning strategies to perform. We can also set both of these variables to `'all'` or binning strategies to `'default'` to use the default binning strategy for every chosen statistical test." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "statistical_tests = [\"KS\", \"PSI\"]\n", - "binning_strategies = \"default\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's compute the statistics and their p_values:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 2/2 [00:00<00:00, 141.92it/s]\n", - "100%|██████████| 2/2 [00:00<00:00, 139.13it/s]\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
columnp_value_KS_no_bucketing_0p_value_PSI_quantilebucketer_10statistic_KS_no_bucketing_0statistic_PSI_quantilebucketer_10
0feat_00.8154150.4432440.090.192113
1feat_10.2819420.0109220.140.374575
\n", - "
" - ], - "text/plain": [ - " column p_value_KS_no_bucketing_0 p_value_PSI_quantilebucketer_10 \\\n", - "0 feat_0 0.815415 0.443244 \n", - "1 feat_1 0.281942 0.010922 \n", - "\n", - " statistic_KS_no_bucketing_0 statistic_PSI_quantilebucketer_10 \n", - "0 0.09 0.192113 \n", - "1 0.14 0.374575 " - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "myAutoDist = AutoDist(statistical_tests=statistical_tests, binning_strategies=binning_strategies, bin_count=10)\n", - "myAutoDist.compute(df1, df2)" - ] - } - ], - "metadata": { - "environment": { - "name": "common-cpu.m48", - "type": "gcloud", - "uri": "gcr.io/deeplearning-platform-release/base-cpu:m48" - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.8" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/nb_imputation_comparison.ipynb b/docs/tutorials/nb_imputation_comparison.ipynb deleted file mode 100644 index c6cf2cd2..00000000 --- a/docs/tutorials/nb_imputation_comparison.ipynb +++ /dev/null @@ -1,324 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Imputation Comparison\n", - "\n", - "[![open in colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ing-bank/probatus/blob/master/docs/tutorials/nb_imputation_comparison.ipynb)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This notebook explains how the `ImputationSelector` class works in `probatus`. With `ImputationSelector` you can compare multiple imputation strategies\n", - "and choose a strategy which works the best for a given model and a dataset.\n", - "Currently `ImputationSelector` supports any [scikit-learn](https://scikit-learn.org/stable/) compatible imputation strategy. For categorical variables the missing values are replaced by a `missing` token and `OneHotEncoder` is applied. The user-supplied imputation strategies are applied to numerical columns only. \n", - "Support for user-supplied imputation strategies for categorical columns can be added in the future releases.\n", - "\n", - "Let us look at an example and start by importing all the required classes and methods.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "###Install the packages\n", - "# %%capture\n", - "#!pip install probatus\n", - "#!pip install lightgbm" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Using `tqdm.autonotebook.tqdm` in notebook mode. Use `tqdm.tqdm` instead to force console mode (e.g. in jupyter console)\n" - ] - } - ], - "source": [ - "%matplotlib inline\n", - "%load_ext autoreload\n", - "%autoreload 2\n", - "import pandas as pd\n", - "\n", - "pd.set_option(\"display.max_columns\", 100)\n", - "pd.set_option(\"display.max_row\", 500)\n", - "pd.set_option(\"display.max_colwidth\", 200)\n", - "import lightgbm as lgb\n", - "from sklearn.datasets import make_classification\n", - "from sklearn.experimental import enable_iterative_imputer\n", - "\n", - "from sklearn.impute import IterativeImputer, KNNImputer, SimpleImputer\n", - "from sklearn.linear_model import LogisticRegression\n", - "\n", - "from probatus.missing_values.imputation import ImputationSelector\n", - "from probatus.utils.missing_helpers import generate_MCAR" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's create a classification dataset to apply the various imputation strategies.\n", - "\n", - "We'll use the `probatus.utils.missing_helpers.generate_MCAR` function to randomly add missing values to the dataset." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Shape of X,y : (2000, 20),(2000,)\n" - ] - } - ], - "source": [ - "n_features = 20\n", - "X, y = make_classification(n_samples=2000, n_features=n_features, random_state=123, class_sep=0.3)\n", - "X = pd.DataFrame(X, columns=[\"f_\" + str(i) for i in range(0, n_features)])\n", - "print(f\"Shape of X,y : {X.shape},{y.shape}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
0
f_00.2080
f_10.1960
f_20.1990
f_30.2095
f_40.2150
\n", - "
" - ], - "text/plain": [ - " 0\n", - "f_0 0.2080\n", - "f_1 0.1960\n", - "f_2 0.1990\n", - "f_3 0.2095\n", - "f_4 0.2150" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "X_missing = generate_MCAR(X, missing=0.2)\n", - "missing_stats = pd.DataFrame(X_missing.isnull().mean())\n", - "\n", - "missing_stats.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The data has approximately 20% missing values in each feature." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Imputation Strategies" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a dictionary with all the strategies to compare. Also, create a classifier to use for evaluating various strategies.\n", - "If the model supports handling of missing features by default then the model performance on an unimputed dataset is calculated. You can indicate that the model supports handling missing values by setting the parameter `model_na_support=True`.\n", - "The model performance against the unimputed dataset can be found in `No Imputation` results." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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sHtw7ddHSQh37u7u7Nmzx2Zfam2xExEREUmQqkfFWiwW69qv69ev599//6VMmTJkypQJNze3JI8pUqQIn3/+Oblz507OqoqIiIi8cKk62CWEug8//JCvv/4ad3d34uPj6dOnD40aNSIoKCjJ4/LmzQvYBkMRERGR1C5Vppr7x3ts3ryZLVu28MMPP3D48GFatWrFzJkzmT59OufPn3/keRTqRERExJGkyha7hOlIZs2axa5duyhevDjlypUD7q0g4ebmxvz58zGZTLRr1+6hLXciIiIijiRVBrsEa9as4fvvv6dcuXJER0djNpsBGDJkCAALFy4kKiqKfv36ERgYaM+qioiIiLxwqeZZZFLT7c2fP59u3boRGRnJN998w/Xr1637hgwZQuXKlTl37hwBAQHJWVURERERu0gVLXb3D3K4c+cOcXFx1ta58ePHExUVxcSJE3F1deX999+3Tt43fvx4rf0qIiIiaUaKb7G7P9SNGjWKBg0aUKhQIcaOHcuuXbsA+OqrryhXrhzjxo1jwYIFNi13CnUiIiKSVqTYJcUenIpkwIABfPnllwwYMACTycSECRN47bXX+OCDDwgNDQWgTZs2LFy4kPDwcGrXrm2vqicr6zIjfX3wdVd4FRGxu8HX/7uMyBNwiCXF7g91P/zwAwsXLmT58uV07dqVUqVKceLECXbs2MGECRPYsmULAF9++SV9+vShZs2a9qq2iIjIf7p48SI1a9bE29ub/Pnzs379+iTLFSxYELPZbH05OzvTpUuXROXat2+PyWTin3/+sdk+evRosmbNio+PD0WLFuXGjRsv5Hok5UhxfewaNGjASy+9xOTJk4F7gybSp0/PBx98QKlSpVixYgVNmzZl1qxZvPTSS9SpUwdXV1du3LhBWFgY/fv3ByA+Pl7LhImISIrUqVMngoKCuHjxIj/99BMNGjQgMjIy0WC/AwcOWP8dExNDUFAQ9erVsymza9cuDh48mOg9pkyZwurVq/ntt9/ImjUr+/bte+iqTOI4UtSj2JiYGLZs2ULZsmVtfvmuXLlCfHw8rq6u1KlThxo1atCnTx8AChUqxMWLF+nQoQODBw+2U83tR49iRURSmP94FBsdHU1AQADHjh0jODgYgAoVKtC8eXNatmz50OMiIiLo1asXJ06csPYbNwyD8uXLM3HiREqUKMHp06cJDg4mPj6e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GKYhUf9d6BEU7OR5eZJHscmSlv755x88PT2tS5y89NJL9OvXD4vFQr169fj++++pX78+c+bMYdq0aWzatImlS5eSLVs21q5di4uLC/Hx8RpAISIiySa6n8+jCww4lzwVEXkCL7zFbtGiRbRp04bMmTPTtm1bXnrpJetw7tjYWD788EMmT57MggULePfdd60DKm7duoWXlxdwb14etdglTS12IiJ2osETkkxSTItdbGwsGzZsIC4ujkuXLrFkyRJOnDjB8OHDyZcvHx07duTtt9/Gx8eH999/Hz8/P6pWrQpgDXWGYSjUiYiIiDyGF5qY3Nzc+OSTT3B1deX48eMULFiQiIgIlixZwurVq2nXrh137twhT548ODk5Ua1aNX7//XeKFy9uPYeWCRMRERF5PC+8Keyll17io48+Yvjw4axbt44sWbLQqVMn2rVrx19//cX58+eZPXs2MTExXL58mVdfffVFV0lERETEISXbqNhz584xfPhwduzYQZ06dejfv791n2EYmEwm6/+qT93jUx87ERE7UR87SSYpcoLiTJkyMWDAAEqVKsWyZcsYNWqUdV/CpMQmkwmLxaJQJyIiIvIUknXliaCgIJtwN3DgQACbIJewGoWjenCdXBEREZHnJdlTVFBQEP379yd37tz8+++/aSrgGIZhDa47duywc21ERETE0dileSwoKIgJEyYwbdo0a986R2exWKwjfD/66CNKly7NmTNn7FwrERERcSR268wWEBAAYJ2Q2NElXOOhQ4e4desWP//8M1myZLFzrURERMSR2D1RpYVQlyAiIoKwsDA2bdpEzpw5n6qlMiYmhqioKJuXiIiICNixxS4tcnZ2pmDBgvz8889cvnyZ4ODgJ26xHDFiBEOGDEm0vdCdr3AyvJ5ndUVE5FH6rrR3DezqxMia9q6CJCHtNJcls4TRr/d755136NOnD6+++ipNmzblwIEDODk5JVn2Yfr168f169etr9OnTz/PaouIiDx3Fy9epGbNmnh7e5M/f37Wr1+fZLkTJ04QFhaGv78/WbJk4bPPPrPZv337dl5//XXMZjNZs2Zl0aJF1n03btygTZs2BAQE4O/vT7NmzV7oNaVUarF7Ae5vhVu8eDEXL17k+vXrNG7cmAoVKjBq1CiGDx9OmzZt+Oqrr3jllVceu+XO3d0dd3f3F30JIiIiz02nTp0I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MmbMyPTp07lz5w4///wzGTNmtDmHs7OzHa8i+WjwhIiIiGNLtYMnwsPDmTBhAgULFuSTTz4he/bsxMbGUrZsWU6cOMHSpUspXLgwf/zxB127duXatWscPnyYHj162JwnrYQ6ERERkfuliBY7wzCIj4+nZMmStG3blo4dOwKwdetWxo4dy7///suHH35I7dq1rcds27aNv//+m99++42JEyfi6upqr+rblVrsREREHFuqa7EzmUzExcVx7do1PD09rdtLly7Nhx9+SMaMGfnf//7HokWLrPtef/11GjduzNSpU3F1deXu3bv2qLqIiIhIipEigl0CHx8fXnrpJQAsFgtwL8B9+OGHpE+fns8//5wffvghyWPTaoudiIiISAK7Brv169fz2WefAfeC2c2bN/Hx8QHuPZ5NeEr8+uuv07t3bzJkyED//v355Zdf7FZnERERkZTKbtOdxMTE8P3337Nt2za8vb1p3bo1MTEx1keqDw6AeP311+nZsyfLli2jbNmy9qiyiIiISIpm18ETZ8+eZfTo0Wzfvp0yZcqwfPlyGjdujI+PDxaLBXd3dwBu377NhQsXaNOmDS+//DKQtqY0eRQNnhAREXFsT/Jdb/dRsefOnWPYsGFs2rSJgwcPkjt3bnx8fLh58yYWiwWTyYSHhwf+/v5s2LBBy4M9QMFORETEsT3Jd73dU1KmTJkYOHAgzs7O+Pv7U6FCBWu/u9jYWFxcXHBycsIwDEwmk1rqRERERB4iRYyKDQoKom/fvhQtWpS1a9cyYsQIANzc3IiPjwfuTYliGIZCnYiIiMhDpIhgB/da7gYMGMDrr7/OihUrGDhwIGA7jYnJZLJX9URERERSvBQT7OBey13//v3JnTs3//77LylgUQwRERGRVMPufeweFBQUxIQJE/D397c+flVLnYiIiMh/S3HBDiAgIAC4t/qEk1OKalQUERERSbFSdGpSqBMRERF5fEpOIiIiIg5CwU5ERETEQSjYiYiIiDgIBTsRERERB6FgJyIiIuIgFOxEREREHISCnYiIiIiDULATERERcRAKdiIiIiIOQsFORERExEEo2ImIiIg4CAU7EREREQehYCciIiLiIFzsXQF5NoZhABAVFWXnmoiIiMiLkPAdn/Cd/ygKdqnc5cuXAciaNaudayIiIiIv0o0bN/Dz83tkGQW7VC4gIACAU6dO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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "strategies = {\n", - " \"KNN Imputer\": KNNImputer(n_neighbors=3),\n", - " \"Median Imputer\": SimpleImputer(strategy=\"median\", add_indicator=True),\n", - " \"Iterative Imputer\": IterativeImputer(add_indicator=True, n_nearest_features=5, sample_posterior=True),\n", - "}\n", - "\n", - "clf = lgb.LGBMClassifier(n_estimators=2)\n", - "cmp = ImputationSelector(clf=clf, strategies=strategies, cv=5, random_state=45, model_na_support=True)\n", - "cmp.fit_compute(X_missing, y)\n", - "result_plot = cmp.plot()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "However if the model does not support missing values by default (e.g. `LogisticRegression`), results for only the imputation strategies are calculated. \n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "clf = LogisticRegression()\n", - "cmp = ImputationSelector(clf=clf, strategies=strategies, cv=5)\n", - "cmp.fit_compute(X_missing, y)\n", - "result_plot = cmp.plot()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also pass a sklearn pipeline instead of a classifier." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from sklearn.pipeline import Pipeline\n", - "from sklearn.preprocessing import StandardScaler\n", - "\n", - "steps = [(\"scaler\", StandardScaler()), (\"LR\", LogisticRegression())]\n", - "clf = Pipeline(steps)\n", - "cmp = ImputationSelector(clf=clf, strategies=strategies, cv=5, model_na_support=False)\n", - "cmp.fit_compute(X_missing, y)\n", - "result_plot = cmp.plot()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "jp-MarkdownHeadingCollapsed": true - }, - "source": [ - "## Scikit Learn Compatible Imputers. \n", - "\n", - "You can also use any other scikit-learn compatible imputer as an imputing strategy.\n", - "e.g. [feature engine](https://feature-engine.readthedocs.io/en/latest/index.html) library provides a host of other imputing stratgies as well. You can pass them for comparision as well." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.10" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/probatus/binning/__init__.py b/probatus/binning/__init__.py deleted file mode 100644 index bade5d31..00000000 --- a/probatus/binning/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2020 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -from .binning import SimpleBucketer, AgglomerativeBucketer, QuantileBucketer, TreeBucketer, Bucketer - -__all__ = ["SimpleBucketer", "AgglomerativeBucketer", "QuantileBucketer", "TreeBucketer", "Bucketer"] diff --git a/probatus/binning/binning.py b/probatus/binning/binning.py deleted file mode 100644 index 155fe86b..00000000 --- a/probatus/binning/binning.py +++ /dev/null @@ -1,470 +0,0 @@ -# Copyright (c) 2020 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -import warnings -from abc import abstractmethod - -import numpy as np -import pandas as pd -from sklearn.cluster import AgglomerativeClustering -from sklearn.tree import DecisionTreeClassifier, _tree -from sklearn.utils.validation import check_is_fitted - -from probatus.utils import ApproximationWarning, BaseFitComputeClass, assure_numpy_array - - -class Bucketer(BaseFitComputeClass): - """ - Bucket (bin) some data. - """ - - def __repr__(self): - """ - String representation. - """ - repr_ = f"{self.__class__.__name__}\n\tbincount: {self.bin_count}" - if hasattr(self, "boundaries_"): - repr_ += f"\nResults:\n\tcounts: {self.counts_}\n\tboundaries: {self.boundaries_}" - return repr_ - - @abstractmethod - def fit(self): - """ - Fit Bucketer. - """ - pass - - @property - def boundaries(self): - """ - The boundaries of the bins. - """ - msg = "The 'boundaries' attribute is deprecated, use 'boundaries_' instead." - msg += "The underscore suffix signals this is a fitted attribute." - warnings.warn( - msg, - DeprecationWarning, - ) - check_is_fitted(self) - return self.boundaries_ - - @property - def counts(self): - """ - Counts. - """ - msg = "The 'counts' attribute is deprecated, use 'counts_' instead." - msg += "The underscore suffix signals this is a fitted attribute." - warnings.warn(msg, DeprecationWarning) - check_is_fitted(self) - return self.counts_ - - def compute(self, X, y=None): - """ - Applies fitted bucketing algorithm on input data and counts number of samples per bin. - - Args: - X: (np.array) data to be bucketed - y: (np.array) ignored, for sklearn compatibility - - Returns: counts of the elements in X using the bucketing that was obtained by fitting the Bucketer instance - - """ - check_is_fitted(self) - - return self._compute_counts_per_bin(X, self.boundaries_) - - @staticmethod - def _compute_counts_per_bin(X, boundaries): - """ - Computes the counts per bin. - - Args: - X (np.array): data to be bucketed - boundaries (np.array): boundaries of the bins. - - Returns (np.array): Counts per bin. - """ - # np.digitize returns the indices of the bins to which each value in input array belongs - # the smallest value of the `boundaries` attribute equals the lowest value in the set the instance was - # fitted on, to prevent the smallest value of x_new to be in his own bucket, we ignore the first boundary - # value - bins = len(boundaries) - 1 - digitize_result = np.digitize(X, boundaries[1:], right=True) - result = pd.DataFrame({"bucket": digitize_result}).groupby("bucket")["bucket"].count() - # reindex the dataframe such that also empty buckets are included in the result - return result.reindex(np.arange(bins), fill_value=0).to_numpy() - - def fit_compute(self, X, y=None): - """ - Apply bucketing to new data and return number of samples per bin. - - Args: - X: (np.array) data to be bucketed - y: (np.array) One dimensional array, used if the target is needed for the bucketing. By default is set to - None - - Returns: counts of the elements in x_new using the bucketing that was obtained by fitting the Bucketer instance - - """ - self.fit(X, y) - return self.compute(X, y) - - @staticmethod - def _enforce_inf_boundaries(boundaries): - """ - This function ensures that the boundaries of the buckets are infinite. - - Arguments - boundaries: (list) List of bin boundaries. - - Returns: - (list): Boundaries with infinite edges - """ - boundaries[0] = -np.inf - boundaries[-1] = np.inf - return boundaries - - -class SimpleBucketer(Bucketer): - """ - Create equally spaced bins using numpy.histogram function. - - Example: - ```python - from probatus.binning import SimpleBucketer - - x = [1, 2, 1] - bins = 3 - myBucketer = SimpleBucketer(bin_count=bins) - myBucketer.fit(x) - ``` - - myBucketer.counts gives the number of elements per bucket - myBucketer.boundaries gives the boundaries of the buckets - """ - - def __init__(self, bin_count): - """ - Init. - """ - self.bin_count = bin_count - - @staticmethod - def simple_bins(x, bin_count, inf_edges=True): - """ - Simple bins. - """ - _, boundaries = np.histogram(x, bins=bin_count) - if inf_edges: - boundaries = Bucketer._enforce_inf_boundaries(boundaries) - - counts = Bucketer._compute_counts_per_bin(x, boundaries) - return counts, boundaries - - def fit(self, x, y=None): - """ - Fit bucketing on x. - - Args: - x: (np.array) Input array on which the boundaries of bins are fitted - y: (np.array) ignored. For sklearn-compatibility - - Returns: fitted bucketer object - """ - self.counts_, self.boundaries_ = self.simple_bins(x, self.bin_count) - return self - - -class AgglomerativeBucketer(Bucketer): - """ - Create binning by applying the Scikit-learn implementation of Agglomerative Clustering. - - Usage: - ```python - from probatus.binning import AgglomerativeBucketer - - x = [1, 2, 1] - bins = 3 - myBucketer = AgglomerativeBucketer(bin_count=bins) - myBucketer.fit(x) - ``` - - myBucketer.counts gives the number of elements per bucket - myBucketer.boundaries gives the boundaries of the buckets - """ - - def __init__(self, bin_count): - """ - Init. - """ - self.bin_count = bin_count - - @staticmethod - def agglomerative_clustering_binning(x, bin_count, inf_edges=True): - """ - Cluster. - """ - clustering = AgglomerativeClustering(n_clusters=bin_count).fit(np.asarray(x).reshape(-1, 1)) - df = pd.DataFrame({"x": x, "label": clustering.labels_}).sort_values(by="x") - cluster_minimum_values = df.groupby("label")["x"].min().sort_values().tolist() - cluster_maximum_values = df.groupby("label")["x"].max().sort_values().tolist() - # take the mean of the upper boundary of a cluster and the lower boundary of the next cluster - boundaries = [ - np.mean([cluster_minimum_values[i + 1], cluster_maximum_values[i]]) - for i in range(len(cluster_minimum_values) - 1) - ] - # add the lower boundary of the lowest cluster and the upper boundary of the highest cluster - boundaries = [cluster_minimum_values[0]] + boundaries + [cluster_maximum_values[-1]] - if inf_edges: - boundaries = Bucketer._enforce_inf_boundaries(boundaries) - counts = Bucketer._compute_counts_per_bin(x, boundaries) - return counts, boundaries - - def fit(self, x, y=None): - """ - Fit bucketing on x. - - Args: - x: (np.array) Input array on which the boundaries of bins are fitted - y: (np.array) ignored. For sklearn-compatibility - - Returns: fitted bucketer object - """ - self.counts_, self.boundaries_ = self.agglomerative_clustering_binning(x, self.bin_count) - return self - - -class QuantileBucketer(Bucketer): - """ - Create bins with equal number of elements. - - Usage: - ```python - from probatus.binning import QuantileBucketer - - x = [1, 2, 1] - bins = 3 - myBucketer = QuantileBucketer(bin_count=bins) - myBucketer.fit(x) - ``` - - myBucketer.counts gives the number of elements per bucket - myBucketer.boundaries gives the boundaries of the buckets - """ - - def __init__(self, bin_count): - """ - Init. - """ - self.bin_count = bin_count - - @staticmethod - def quantile_bins(x, bin_count, inf_edges=True): - """ - Bins. - """ - try: - out, boundaries = pd.qcut(x, q=bin_count, retbins=True, duplicates="raise") - except ValueError: - # If there are too many duplicate values (assume a lot of filled missing) - # this crashes - the exception drops them. - # This means that it will return approximate quantile bins - out, boundaries = pd.qcut(x, q=bin_count, retbins=True, duplicates="drop") - warnings.warn( - ApproximationWarning( - f"Unable to calculate quantile bins for this feature, because possibly " - f"there is too many duplicate values.Approximated quantiles, as a result," - f"the multiple boundaries have the same value. The number of bins has " - f"been lowered to {boundaries-1}. This can cause issue if you want to " - f"calculate the statistical test based on this binning. We suggest to " - f"retry with max number of bins of {boundaries-1} or apply different " - f"type of binning e.g. simple. If you run this functionality in AutoDist for multiple features, " - f"then you can decrease the bins only for that feature in a separate AutoDist run." - ) - ) - df = pd.DataFrame({"x": x}) - df["label"] = out - if inf_edges: - boundaries = Bucketer._enforce_inf_boundaries(boundaries) - counts = Bucketer._compute_counts_per_bin(x, boundaries) - return counts, boundaries - - def fit(self, x, y=None): - """ - Fit bucketing on x. - - Args: - x: (np.array) Input array on which the boundaries of bins are fitted - y: (np.array) ignored. For sklearn-compatibility - - Returns: fitted bucketer object - """ - self.counts_, self.boundaries_ = self.quantile_bins(x, self.bin_count) - return self - - -class TreeBucketer(Bucketer): - """ - Class for bucketing using Decision Trees. - - It returns the optimal buckets found by a one-dimensional Decision Tree relative to a binary target. - - Useful if the buckets be defined such that there is a substantial difference between the buckets in - the distribution of the target. - - Usage: - ```python - from probatus.binning import TreeBucketer - - x = [1, 2, 2, 5 ,3] - y = [0, 0 ,1 ,1 ,1] - myBucketer = TreeBucketer(inf_edges=True,max_depth=2,min_impurity_decrease=0.001) - myBucketer.fit(x,y) - ``` - - myBucketer.counts gives the number of elements per bucket - myBucketer.boundaries gives the boundaries of the buckets - - Args: - inf_edges (boolean): Flag to keep the lower and upper boundary as infinite (if set to True). - If false, the edges will be set to the minimum and maximum value of the fitted - - tree (sklearn.tree.DecisionTreeClassifier): decision tree object defined by the user. By default is None, and - it will be constructed using the provided **kwargs - - **tree_kwargs: kwargs related to the decision tree. - For and extensive list of parameters, please check the sklearn Decision Tree Classifier documentation - https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html - - The most relevant parameters useful for the bucketing, are listed below: - - - - criterion : {"gini", "entropy"}, default="gini" - The function to measure the quality of a split. Supported criteria are - "gini" for the Gini impurity and "entropy" for the information gain. - - - - max_depth : int, default=None - Defines the maximum theoretical number of bins (2^max_depth) - - The maximum depth of the tree. If None, then nodes are expanded until - all leaves are pure or until all leaves contain less than - min_samples_split samples. - - - - - min_samples_leaf : int or float, default=1 - Defines the minimum number of entries in each bucket. - - The minimum number of samples required to be at a leaf node. - A split point at any depth will only be considered if it leaves at - least ``min_samples_leaf`` training samples in each of the left and - right branches. This may have the effect of smoothing the model, - especially in regression. - - - If int, then consider `min_samples_leaf` as the minimum number. - - If float, then `min_samples_leaf` is a fraction and - `ceil(min_samples_leaf * n_samples)` are the minimum - number of samples for each node. - - .. versionchanged:: 0.18 - Added float values for fractions. - - - - min_impurity_decrease : float, default=0.0 - Controls the way the TreeBucketer splits. - When the criterion is set to 'entropy', the best results tend to - be achieved in the range [0.0001 - 0.01] - - A node will be split if this split induces a decrease of the impurity - greater than or equal to this value. - - The weighted impurity decrease equation is the following:: - - N_t / N * (impurity - N_t_R / N_t * right_impurity - - N_t_L / N_t * left_impurity) - - where ``N`` is the total number of samples, ``N_t`` is the number of - samples at the current node, ``N_t_L`` is the number of samples in the - left child, and ``N_t_R`` is the number of samples in the right child. - - ``N``, ``N_t``, ``N_t_R`` and ``N_t_L`` all refer to the weighted sum, - if ``sample_weight`` is passed. - - .. versionadded:: 0.19 - - """ - - def __init__(self, inf_edges=False, tree=None, **tree_kwargs): - """ - Init. - """ - self.bin_count = -1 - self.inf_edges = inf_edges - if tree is None: - self.tree = DecisionTreeClassifier(**tree_kwargs) - else: - self.tree = tree - - @staticmethod - def tree_bins(x, y, inf_edges, tree): - """ - Tree. - """ - X_in = assure_numpy_array(x).reshape(-1, 1) - y_in = assure_numpy_array(y).reshape(-1, 1) - tree.fit(X_in, y_in) - - if tree.min_samples_leaf >= X_in.shape[0]: - error_msg = ( - "Cannot Fit decision tree. min_samples_leaf must be < than the length of x.m" - + f"Currently min_samples_leaf {tree.min_samples_leaf} " - + f"and the length of X is {X_in.shape[0]}" - ) - raise ValueError(error_msg) - - leaves = tree.apply(X_in) - index, counts = np.unique(leaves, return_counts=True) - - bin_count = len(index) - - boundaries = np.unique(tree.tree_.threshold[tree.tree_.feature != _tree.TREE_UNDEFINED]) - boundaries = [np.min(X_in)] + boundaries.tolist() + [np.max(X_in)] - - if inf_edges: - boundaries[0] = -np.inf - boundaries[-1] = np.inf - - return counts.tolist(), boundaries, bin_count, tree - - def fit(self, X, y): - """ - Fit bucketing on x. - - Args: - x: (np.array) Input array on which the boundaries of bins are fitted - y: (np.array) optional, One dimensional array with the target. - - Returns: fitted bucketer object - """ - self.counts_, self.boundaries_, self.bin_count, self.tree = self.tree_bins(X, y, self.inf_edges, self.tree) - return self diff --git a/probatus/interpret/shap_dependence.py b/probatus/interpret/shap_dependence.py index d930004b..e809f89f 100644 --- a/probatus/interpret/shap_dependence.py +++ b/probatus/interpret/shap_dependence.py @@ -21,8 +21,8 @@ import matplotlib.pyplot as plt import numpy as np import pandas as pd +from sklearn.preprocessing import KBinsDiscretizer -from probatus.binning import AgglomerativeBucketer, QuantileBucketer, SimpleBucketer from probatus.utils import BaseFitComputePlotClass, preprocess_data, preprocess_labels, shap_to_df @@ -46,7 +46,7 @@ class DependencePlotter(BaseFitComputePlotClass): bdp = DependencePlotter(clf) shap_values = bdp.fit_compute(X, y) - bdp.plot(feature=2, type_binning='simple') + bdp.plot(feature=2) ``` @@ -171,7 +171,6 @@ def plot( feature, figsize=(15, 10), bins=10, - type_binning="simple", show=True, min_q=0, max_q=1, @@ -190,9 +189,6 @@ def plot( bins (int or list[float]): Number of bins or boundaries of bins (supplied in list) for target-rate plot. - type_binning ({'simple', 'agglomerative', 'quantile'}): - Type of binning to be used in target-rate plot (see :mod:`binning` for more information). - show (bool, optional): If True, the plots are showed to the user, otherwise they are not shown. Not showing plot can be useful, when you want to edit the returned axis, before showing it. @@ -215,8 +211,6 @@ def plot( raise ValueError("min_q must be smaller than max_q") if feature not in self.X.columns: raise ValueError("Feature not recognized") - if type_binning not in ["simple", "agglomerative", "quantile"]: - raise ValueError("Select one of the following binning methods: 'simple', 'agglomerative', 'quantile'") if (alpha < 0) or (alpha > 1): raise ValueError("alpha must be a float value between 0 and 1") @@ -227,7 +221,7 @@ def plot( ax2 = plt.subplot2grid((3, 1), (2, 0)) self._dependence_plot(feature=feature, ax=ax1) - self._target_rate_plot(feature=feature, bins=bins, type_binning=type_binning, ax=ax2) + self._target_rate_plot(feature=feature, bins=bins, ax=ax2) ax2.set_xlim(ax1.get_xlim()) @@ -268,7 +262,7 @@ def _dependence_plot(self, feature, ax=None): return ax - def _target_rate_plot(self, feature, bins=10, type_binning="simple", ax=None): + def _target_rate_plot(self, feature, bins=10, ax=None): """ Plots the distributions of the specific features, as well as the target rate as function of the feature. @@ -279,9 +273,6 @@ def _target_rate_plot(self, feature, bins=10, type_binning="simple", ax=None): bins (int or list[float]), optional: Number of bins or boundaries of desired bins in list. - type_binning ({'simple', 'agglomerative', 'quantile'}, optional): - Type of binning strategy used to create bins. - ax (matplotlib.pyplot.axes, optional): Optional axis on which to draw plot. @@ -294,12 +285,11 @@ def _target_rate_plot(self, feature, bins=10, type_binning="simple", ax=None): # Create bins if not explicitly supplied if isinstance(bins, int): - if type_binning == "simple": - counts, bins = SimpleBucketer.simple_bins(x, bins) - elif type_binning == "agglomerative": - counts, bins = AgglomerativeBucketer.agglomerative_clustering_binning(x, bins) - elif type_binning == "quantile": - counts, bins = QuantileBucketer.quantile_bins(x, bins) + simple_binner = KBinsDiscretizer(n_bins=bins, encode="ordinal", strategy="uniform").fit( + np.array(x).reshape(-1, 1) + ) + bins = simple_binner.bin_edges_[0] + bins[0], bins[-1] = -np.inf, np.inf # Determine bin for datapoints bins[-1] = bins[-1] + 1 diff --git a/probatus/metric_volatility/__init__.py b/probatus/metric_volatility/__init__.py deleted file mode 100644 index a034a7ec..00000000 --- a/probatus/metric_volatility/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2020 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -from .metric import get_metric -from .volatility import ( - BaseVolatilityEstimator, - TrainTestVolatility, - BootstrappedVolatility, - SplitSeedVolatility, -) -from .utils import sample_data, check_sampling_input - -__all__ = [ - "get_metric", - "BaseVolatilityEstimator", - "TrainTestVolatility", - "BootstrappedVolatility", - "SplitSeedVolatility", - "sample_data", - "check_sampling_input", -] diff --git a/probatus/metric_volatility/metric.py b/probatus/metric_volatility/metric.py deleted file mode 100644 index e246bed7..00000000 --- a/probatus/metric_volatility/metric.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) 2020 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -import numpy as np -import pandas as pd -from sklearn.model_selection import train_test_split - -from probatus.metric_volatility.utils import sample_data -from probatus.utils import assure_numpy_array - - -def get_metric( - X, - y, - clf, - test_size, - split_seed, - scorers, - train_sampling_type=None, - test_sampling_type=None, - train_sampling_fraction=1, - test_sampling_fraction=1, -): - """ - Draws random train/test sample from the data using random seed and calculates metric of interest. - - Args: - X (np.array or pd.DataFrame): - Dataset with features. - - y (np.array or pd.Series): - Target of the prediction. - - clf (model object): - Binary classification model or pipeline. - - test_size (float): - Fraction of data used for testing the model. - - split_seed (int): - Randomized seed used for splitting data. - - scorers (list of Scorers): - List of Scorer objects used to score the trained model. - - train_sampling_type (str, optional): - String indicating what type of sampling should be applied on train set: - - - `None`: indicates that no additional sampling is done after splitting data, - - `'bootstrap'`: indicates that sampling with replacement will be performed on train data, - - `'subsample'`: indicates that sampling without repetition will be performed on train data. - - test_sampling_type (str, optional): - string indicating what type of sampling should be applied on test set: - - - `None`: indicates that no additional sampling is done after splitting data - - `'bootstrap'`: indicates that sampling with replacement will be performed on test data - - `'subsample'`: indicates that sampling without repetition will be performed on test data - - train_sampling_fraction (float, optional): - Fraction of train data sampled, if sample_train_type is not None. Default value is 1. - - test_sampling_fraction (float, optional): - Fraction of test data sampled, if sample_test_type is not None. Default value is 1. - - Returns: - (pd.Dataframe): - Dataframe with results for a given model trained. Rows indicate the metric measured and columns their - results. - """ - - if not (isinstance(X, np.ndarray) or isinstance(X, pd.DataFrame)): - X = assure_numpy_array(X) - if not (isinstance(X, np.ndarray) or isinstance(X, pd.Series)): - y = assure_numpy_array(y) - - X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=split_seed, stratify=y) - - # Sample data based on the input arguments - X_train, y_train = sample_data( - X=X_train, - y=y_train, - sampling_type=train_sampling_type, - sampling_fraction=train_sampling_fraction, - dataset_name="train", - ) - X_test, y_test = sample_data( - X=X_test, - y=y_test, - sampling_type=test_sampling_type, - sampling_fraction=test_sampling_fraction, - dataset_name="test", - ) - - clf = clf.fit(X_train, y_train) - - results_columns = ["metric_name", "train_score", "test_score", "delta_score"] - results = [] - - for scorer in scorers: - score_train = scorer.score(clf, X_train, y_train) - score_test = scorer.score(clf, X_test, y_test) - score_delta = score_train - score_test - - results.append( - [scorer.metric_name, score_train, score_test, score_delta], - ) - return pd.DataFrame(results, columns=results_columns) diff --git a/probatus/metric_volatility/utils.py b/probatus/metric_volatility/utils.py deleted file mode 100644 index dbdeb5e8..00000000 --- a/probatus/metric_volatility/utils.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2020 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -import numpy as np -import pandas as pd - - -def sample_data(X, y, sampling_type, sampling_fraction, dataset_name="dataset"): - """ - Sample data. - """ - check_sampling_input(sampling_type, sampling_fraction, dataset_name) - - if sampling_type is None: - return X, y - - number_of_samples = np.ceil(sampling_fraction * X.shape[0]).astype(int) - array_index = list(range(X.shape[0])) - - if sampling_type == "bootstrap": - rows_indexes = np.random.choice(array_index, number_of_samples, replace=True) - else: - if sampling_fraction == 1 or number_of_samples == X.shape[0]: - return X, y - else: - rows_indexes = np.random.choice(array_index, number_of_samples, replace=True) - - # Get output correctly based on the type - if isinstance(X, pd.DataFrame): - output_X = X.iloc[rows_indexes] - else: - output_X = X[rows_indexes] - if isinstance(y, pd.DataFrame): - output_y = y.iloc[rows_indexes] - else: - output_y = y[rows_indexes] - - return output_X, output_y - - -def check_sampling_input(sampling_type, fraction, dataset_name): - """ - Check. - """ - if sampling_type is not None: - if sampling_type == "bootstrap": - if fraction <= 0: - raise (ValueError(f"For bootstrapping {dataset_name} fraction needs to be above 0")) - elif sampling_type == "subsample": - if fraction <= 0 or fraction >= 1: - raise (ValueError(f"For bootstrapping {dataset_name} fraction needs to be be above 0 and below 1")) - else: - raise (ValueError("This sampling method is not implemented")) diff --git a/probatus/metric_volatility/volatility.py b/probatus/metric_volatility/volatility.py deleted file mode 100644 index 3d004dcc..00000000 --- a/probatus/metric_volatility/volatility.py +++ /dev/null @@ -1,759 +0,0 @@ -# Copyright (c) 2020 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -import warnings - -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -from joblib import Parallel, delayed -from tqdm.auto import tqdm - -from probatus.metric_volatility.metric import get_metric -from probatus.metric_volatility.utils import check_sampling_input -from probatus.stat_tests import DistributionStatistics -from probatus.utils import ( - BaseFitComputePlotClass, - assure_list_of_strings, - assure_list_values_allowed, - get_scorers, - preprocess_data, - preprocess_labels, -) - - -class BaseVolatilityEstimator(BaseFitComputePlotClass): - """ - Base object for estimating volatility estimation. - - This class is a base class, therefore cannot be used on its - own. Implements common API that can be used by all subclasses. - """ - - def __init__( - self, - clf, - scoring="roc_auc", - test_prc=0.25, - n_jobs=1, - stats_tests_to_apply=None, - verbose=0, - random_state=None, - ): - """ - Initializes the class. - - Args: - clf (model object): - Binary classification model or pipeline. - - scoring (string, list of strings, probatus.utils.Scorer or list of probatus.utils.Scorers, optional): - Metrics for which the score is calculated. It can be either a name or list of names metric names and - needs to be aligned with predefined classification scorers names in sklearn - ([link](https://scikit-learn.org/stable/modules/model_evaluation.html)). - Another option is using probatus.utils.Scorer to define a custom metric. - - test_prc (float, optional): - Percentage of input data used as test. By default 0.25. - - n_jobs (int, optional): - Number of parallel executions. If -1 use all available cores. By default 1. - - stats_tests_to_apply (str or list of str, optional): - Test or list of tests to apply. Available tests: - - - `'ES'`: Epps-Singleton - - `'KS'`: Kolmogorov-Smirnov - - `'PSI'`: Population Stability Index - - `'SW'`: Shapiro-Wilk - - `'AD'`: Anderson-Darling - - Details on the available tests can be found [here](/probatus/api/stat_tests.html#available-tests). - - verbose (int, optional): - Controls verbosity of the output: - - - 0 - neither prints nor warnings are shown - - 1 - 50 - only most important warnings and indication of progress in fitting the object. - - 51 - 100 - shows other warnings and prints - - above 100 - presents all prints and all warnings (including SHAP warnings). - - random_state (int, optional): - Random state set at each round of feature elimination. If it is None, the results will not be - reproducible and in random search at each iteration a different hyperparameters might be tested. For - reproducible results set it to integer. - """ - self.clf = clf - self.n_jobs = n_jobs - self.random_state = random_state - self.test_prc = test_prc - self.iterations_results = None - self.report = None - self.verbose = verbose - self.allowed_stats_tests = list(DistributionStatistics.statistical_test_dict.keys()) - - # TODO set reasonable default value for the parameter, to choose the statistical test for the user for different - # ways to compute volatility - if stats_tests_to_apply is not None: - self.stats_tests_to_apply = assure_list_of_strings(stats_tests_to_apply, "stats_tests_to_apply") - assure_list_values_allowed( - variable=self.stats_tests_to_apply, - variable_name="stats_tests_to_apply", - allowed_values=self.allowed_stats_tests, - ) - else: - self.stats_tests_to_apply = [] - - self.stats_tests_objects = [] - if len(self.stats_tests_to_apply) > 0: - if self.verbose > 0: - warnings.warn( - "Computing statistics for distributions is an experimental feature. While using it, keep " - "in mind that the samples of metrics might be correlated." - ) - for test_name in self.stats_tests_to_apply: - self.stats_tests_objects.append(DistributionStatistics(statistical_test=test_name)) - - self.scorers = get_scorers(scoring) - - def fit(self, *args, **kwargs): - """ - Base fit functionality that should be executed before each fit. - - Returns: - (BaseVolatilityEstimator): - Fitted object. - """ - # Set seed for results reproducibility - if self.random_state is not None: - np.random.seed(self.random_state) - - # Initialize the report and results - self.iterations_results = None - self.report = None - self.fitted = True - return self - - def compute(self, metrics=None): - """ - Reports the statistics. - - Args: - metrics (str or list of strings, optional): - Name or list of names of metrics to be plotted. If not all metrics are presented. - - Returns: - (pandas.Dataframe): - Report that contains the evaluation mean and std on train and test sets for each metric. - """ - self._check_if_fitted() - if self.report is None: - raise ( - ValueError( - "Report is None, thus it has not been computed by fit method. Please extend the " - "BaseVolatilityEstimator class, overwrite fit method, and within fit run compute_report()" - ) - ) - - if metrics is None: - return self.report - else: - if not isinstance(metrics, list): - metrics = [metrics] - return self.report.loc[metrics] - - def plot( - self, - metrics=None, - bins=10, - show=True, - height_per_subplot=5, - width_per_subplot=5, - ): - """ - Plots distribution of the metric. - - Args: - metrics (str or list of strings, optional): - Name or list of names of metrics to be plotted. If not all metrics are presented. - - bins (int, optional): - Number of bins into which histogram is built. - - show (bool, optional): - If True, the plots are showed to the user, otherwise they are not shown. Not showing plot can be useful, - when you want to edit the returned axis, before showing it. - - height_per_subplot (int, optional): - Height of each subplot. Default is 5. - - width_per_subplot (int, optional): - Width of each subplot. Default is 5. - - Returns - (list(matplotlib.axes)): - Axes that include the plot. - """ - - target_report = self.compute(metrics=metrics) - - if target_report.shape[0] >= 1: - fig, axs = plt.subplots( - target_report.shape[0], - 2, - figsize=( - width_per_subplot * 2, - height_per_subplot * target_report.shape[0], - ), - ) - - # Enable traversing the axs - axs = axs.flatten() - axis_index = 0 - - for metric, row in target_report.iterrows(): - train, test, delta = self._get_samples_to_plot(metric_name=metric) - - axs[axis_index].hist(train, alpha=0.5, label=f"Train {metric}", bins=bins) - axs[axis_index].hist(test, alpha=0.5, label=f"Test {metric}", bins=bins) - axs[axis_index].set_title(f"Distributions {metric}") - axs[axis_index].legend(loc="upper right") - - axs[axis_index + 1].hist(delta, alpha=0.5, label=f"Delta {metric}", bins=bins) - axs[axis_index + 1].set_title(f"Distributions delta {metric}") - axs[axis_index + 1].legend(loc="upper right") - - axis_index += 2 - - for ax in axs.flat: - ax.set(xlabel=f"{metric} score", ylabel="Results count") - - if show: - plt.show() - else: - plt.close() - - return axs - - def _get_samples_to_plot(self, metric_name): - """ - Selects samples to be plotted. - - Args: - metric_name (str): - Name of metric for which the data should be selected. - """ - current_metric_results = self.iterations_results[self.iterations_results["metric_name"] == metric_name] - train = current_metric_results["train_score"] - test = current_metric_results["test_score"] - delta = current_metric_results["delta_score"] - - return train, test, delta - - def _create_report(self): - """ - Create a report. - - Based on the results for each metric for different sampling, mean and std of distributions of all metrics and - store them as report. - """ - unique_metrics = self.iterations_results["metric_name"].unique() - - # Get columns which will be filled - stats_tests_columns = [] - for stats_tests_object in self.stats_tests_objects: - stats_tests_columns.append(f"{stats_tests_object.statistical_test_name} statistic") - stats_tests_columns.append(f"{stats_tests_object.statistical_test_name} p-value") - stats_columns = [ - "train_mean", - "train_std", - "test_mean", - "test_std", - "delta_mean", - "delta_std", - ] - report_columns = stats_columns + stats_tests_columns - - report = [] - - for metric in unique_metrics: - metric_iterations_results = self.iterations_results[self.iterations_results["metric_name"] == metric] - metrics = self._compute_mean_std_from_runs(metric_iterations_results) - stats_tests_values = self._compute_stats_tests_values(metric_iterations_results) - metric_row = metrics + stats_tests_values - report.append(metric_row) - - self.report = pd.DataFrame(report, columns=report_columns, index=unique_metrics) - - def _compute_mean_std_from_runs(self, metric_iterations_results): - """ - Compute mean and std of results. - - Args: - metric_iterations_results (pandas.DataFrame): - Scores for a single metric for each iteration. - - Returns: - (list): - List containing mean and std of train, test and deltas. - """ - train_mean_score = np.mean(metric_iterations_results["train_score"]) - test_mean_score = np.mean(metric_iterations_results["test_score"]) - delta_mean_score = np.mean(metric_iterations_results["delta_score"]) - train_std_score = np.std(metric_iterations_results["train_score"]) - test_std_score = np.std(metric_iterations_results["test_score"]) - delta_std_score = np.std(metric_iterations_results["delta_score"]) - return [ - train_mean_score, - train_std_score, - test_mean_score, - test_std_score, - delta_mean_score, - delta_std_score, - ] - - def _compute_stats_tests_values(self, metric_iterations_results): - """ - Compute statistics and p-values of specified tests. - - Args: - metric_iterations_results (pandas.DataFrame): - Scores for a single metric for each iteration. - - Returns: - (list): - List containing statistics and p-values of distributions. - """ - statistics = [] - for stats_test in self.stats_tests_objects: - stats, p_value = stats_test.compute( - metric_iterations_results["test_score"], - metric_iterations_results["train_score"], - ) - statistics += [stats, p_value] - return statistics - - def fit_compute(self, *args, **kwargs): - """ - Fit compute. - - Runs trains and evaluates a number of models on train and test sets extracted using different random seeds. - Reports the statistics of the selected metric. - - Takes as arguments the same parameters as fit() method. - - Returns: - (pandas.Dataframe): - Report that contains the evaluation mean and std on train and test sets for each metric. - """ - self.fit(*args, **kwargs) - return self.compute() - - -class TrainTestVolatility(BaseVolatilityEstimator): - """ - Estimation of volatility of metrics. - - The estimation is done by splitting the data into train and test multiple times - and training and scoring a model based on these metrics. The class allows for choosing whether at each iteration - the train test split should be the same or different, whether and how the train and test sets should be sampled. - - Examples: - - ```python - from sklearn.datasets import make_classification - from sklearn.ensemble import RandomForestClassifier - from probatus.metric_volatility import TrainTestVolatility - X, y = make_classification(n_features=4) - clf = RandomForestClassifier() - volatility = TrainTestVolatility(clf, iterations=10 , test_prc = 0.5) - volatility_report = volatility.fit_compute(X, y) - volatility.plot() - ``` - - - """ - - def __init__( - self, - clf, - iterations=1000, - scoring="roc_auc", - sample_train_test_split_seed=True, - train_sampling_type=None, - test_sampling_type=None, - train_sampling_fraction=1, - test_sampling_fraction=1, - test_prc=0.25, - n_jobs=1, - stats_tests_to_apply=None, - verbose=0, - random_state=None, - ): - """ - Initializes the class. - - Args: - clf (model object): - Binary classification model or pipeline. - - iterations (int, optional): - Number of iterations in seed bootstrapping. By default 1000. - - scoring (string, list of strings, probatus.utils.Scorer or list of probatus.utils.Scorers, optional): - Metrics for which the score is calculated. It can be either a name or list of names metric names and - needs to be aligned with predefined classification scorers names in sklearn - ([link](https://scikit-learn.org/stable/modules/model_evaluation.html)). - Another option is using probatus.utils.Scorer to define a custom metric. - - sample_train_test_split_seed (bool, optional): - Flag indicating whether each train test split should be done - randomly or measurement should be done for single split. Default is True, which indicates that each. - iteration is performed on a random train test split. If the value is False, the random_seed for the - split is set to train_test_split_seed. - - train_sampling_type (str, optional): - String indicating what type of sampling should be applied on train set: - - - `None` indicates that no additional sampling is done after splitting data, - - `'bootstrap'` indicates that sampling with replacement will be performed on train data, - - `'subsample'` indicates that sampling without repetition will be performed on train data. - - test_sampling_type (str, optional): - String indicating what type of sampling should be applied on test set: - - - `None` indicates that no additional sampling is done after splitting data, - - `'bootstrap'` indicates that sampling with replacement will be performed on test data, - - `'subsample'` indicates that sampling without repetition will be performed on test data. - - train_sampling_fraction (float, optional): - Fraction of train data sampled, if sample_train_type is not None. - Default value is 1. - - test_sampling_fraction (float, optional): - Fraction of test data sampled, if sample_test_type is not None. Default value is 1. - - test_prc (float, optional): - Percentage of input data used as test. By default 0.25. - - n_jobs (int, optional): - Number of parallel executions. If -1 use all available cores. By default 1. - - stats_tests_to_apply (str or list of str, optional): - List of tests to apply, default is None. Available options: - - - `'ES'`: Epps-Singleton - - `'KS'`: Kolmogorov-Smirnov - - `'PSI'`: Population Stability Index - - `'SW'`: Shapiro-Wilk - - `'AD'`: Anderson-Darling - - Details on the available tests can be found [here](/probatus/api/stat_tests.html#available-tests). - - verbose (int, optional): - Controls verbosity of the output: - - - 0 - neither prints nor warnings are shown - - 1 - 50 - only most important warnings - - 51 - 100 - shows other warnings and prints - - above 100 - presents all prints and all warnings (including SHAP warnings). - - random_state (int, optional): - Random state set at each round of feature elimination. If it is None, the results will not be - reproducible and in random search at each iteration a different hyperparameters might be tested. For - reproducible results set it to integer. - """ - super().__init__( - clf=clf, - scoring=scoring, - test_prc=test_prc, - n_jobs=n_jobs, - stats_tests_to_apply=stats_tests_to_apply, - verbose=verbose, - random_state=random_state, - ) - self.iterations = iterations - self.train_sampling_type = train_sampling_type - self.test_sampling_type = test_sampling_type - self.sample_train_test_split_seed = sample_train_test_split_seed - self.train_sampling_fraction = train_sampling_fraction - self.test_sampling_fraction = test_sampling_fraction - - check_sampling_input(train_sampling_type, train_sampling_fraction, "train") - check_sampling_input(test_sampling_type, test_sampling_fraction, "test") - - def fit(self, X, y, column_names=None): - """ - Fit. - - Bootstraps a number of random seeds, then splits the data based on the sampled seeds and estimates performance - of the model based on the split data. - - Args: - X (pandas.DataFrame or numpy.ndarray): - Array with samples and features. - - y (pandas.Series or numpy.ndarray): - Array with targets. - - column_names (list of str, optional): - List of feature names of the provided samples. If provided it will be used to overwrite the existing - feature names. If not provided the existing feature names are used or default feature names are - generated. - - Returns: - (TrainTestVolatility): - Fitted object. - """ - super().fit() - - self.X, self.column_names = preprocess_data(X, X_name="X", column_names=column_names, verbose=self.verbose) - self.y = preprocess_labels(y, y_name="y", index=self.X.index, verbose=self.verbose) - - if self.sample_train_test_split_seed: - random_seeds = np.random.random_integers(0, 999999, self.iterations) - else: - random_seeds = (np.ones(self.iterations)).astype(int) - if self.random_state: - random_seeds = random_seeds * self.random_state - - if self.verbose > 0: - random_seeds = tqdm(random_seeds) - - results_per_iteration = Parallel(n_jobs=self.n_jobs)( - delayed(get_metric)( - X=self.X, - y=self.y, - clf=self.clf, - test_size=self.test_prc, - split_seed=split_seed, - scorers=self.scorers, - train_sampling_type=self.train_sampling_type, - test_sampling_type=self.test_sampling_type, - train_sampling_fraction=self.train_sampling_fraction, - test_sampling_fraction=self.test_sampling_fraction, - ) - for split_seed in random_seeds - ) - - self.iterations_results = pd.concat(results_per_iteration, ignore_index=True) - - self._create_report() - return self - - -class SplitSeedVolatility(TrainTestVolatility): - """ - Estimation of volatility of metrics depending on the seed used to split the data. - - At every iteration it splits the - data into train and test set using a different stratified split and volatility of the metrics is calculated. - - Examples: - ```python - from sklearn.datasets import make_classification - from sklearn.ensemble import RandomForestClassifier - from probatus.metric_volatility import SplitSeedVolatility - X, y = make_classification(n_features=4) - clf = RandomForestClassifier() - volatility = SplitSeedVolatility(clf, iterations=10 , test_prc = 0.5) - volatility_report = volatility.fit_compute(X, y) - volatility.plot() - ``` - - - """ - - def __init__( - self, - clf, - iterations=1000, - scoring="roc_auc", - test_prc=0.25, - n_jobs=1, - stats_tests_to_apply=None, - verbose=0, - random_state=None, - ): - """ - Initializes the class. - - Args: - clf (model object): - Binary classification model or pipeline. - - iterations (int, optional): - Number of iterations in seed bootstrapping. By default 1000. - - scoring (string, list of strings, probatus.utils.Scorer or list of probatus.utils.Scorers, optional): - Metrics for which the score is calculated. It can be either a name or list of names metric names and - needs to be aligned with predefined classification scorers names in sklearn - ([link](https://scikit-learn.org/stable/modules/model_evaluation.html)). - Another option is using probatus.utils.Scorer to define a custom metric. - - test_prc (float, optional): - Percentage of input data used as test. By default 0.25. - - n_jobs (int, optional): - Number of parallel executions. If -1 use all available cores. By default 1. - - stats_tests_to_apply (None, string or list of str, optional): - List of tests to apply, default is None. Available options: - - - `'ES'`: Epps-Singleton - - `'KS'`: Kolmogorov-Smirnov - - `'PSI'`: Population Stability Index - - `'SW'`: Shapiro-Wilk - - `'AD'`: Anderson-Darling - - Details on the available tests can be found [here](/probatus/api/stat_tests.html#available-tests). - - verbose (int, optional): - Controls verbosity of the output: - - - 0 - neither prints nor warnings are shown - - 1 - 50 - only most important warnings - - 51 - 100 - shows other warnings and prints - - above 100 - presents all prints and all warnings (including SHAP warnings). - - random_state (int, optional): - Random state set at each round of feature elimination. If it is None, the results will not be - reproducible and in random search at each iteration a different hyperparameters might be tested. For - reproducible results set it to integer. - """ - super().__init__( - clf=clf, - sample_train_test_split_seed=True, - train_sampling_type=None, - test_sampling_type=None, - train_sampling_fraction=1, - test_sampling_fraction=1, - iterations=iterations, - scoring=scoring, - test_prc=test_prc, - n_jobs=n_jobs, - stats_tests_to_apply=stats_tests_to_apply, - verbose=verbose, - random_state=random_state, - ) - - -class BootstrappedVolatility(TrainTestVolatility): - """ - Estimation of volatility of metrics by bootstrapping both train and test set. - - By default at every iteration the - train test split is the same. The test shows volatility of metric with regards to sampling different rows from - static train and test sets. - - Examples: - ```python - from sklearn.datasets import make_classification - from sklearn.ensemble import RandomForestClassifier - from probatus.metric_volatility import BootstrappedVolatility - X, y = make_classification(n_features=4) - clf = RandomForestClassifier() - volatility = BootstrappedVolatility(clf, iterations=10 , test_prc = 0.5) - volatility_report = volatility.fit_compute(X, y) - volatility.plot() - ``` - - """ - - def __init__( - self, - clf, - iterations=1000, - scoring="roc_auc", - train_sampling_fraction=1, - test_sampling_fraction=1, - test_prc=0.25, - n_jobs=1, - stats_tests_to_apply=None, - verbose=0, - random_state=None, - ): - """ - Initializes the class. - - Args: - clf (model object): - Binary classification model or pipeline. - - iterations (int, optional): - Number of iterations in seed bootstrapping. By default 1000. - - scoring (string, list of strings, probatus.utils.Scorer or list of probatus.utils.Scorers, optional): - Metrics for which the score is calculated. It can be either a name or list of names metric names and - needs to be aligned with predefined classification scorers names in sklearn - ([link](https://scikit-learn.org/stable/modules/model_evaluation.html)). - Another option is using probatus.utils.Scorer to define a custom metric. - - train_sampling_fraction (float, optional): - Fraction of train data sampled, if sample_train_type is not None. Default value is 1. - - test_sampling_fraction (float, optional): - Fraction of test data sampled, if sample_test_type is not None. Default value is 1. - - test_prc (float, optional): - Percentage of input data used as test. By default 0.25. - - n_jobs (int, optional): - Number of parallel executions. If -1 use all available cores. By default 1. - - stats_tests_to_apply (str or list of str, optional): - List of tests to apply, default is None. Available options: - - - `'ES'`: Epps-Singleton - - `'KS'`: Kolmogorov-Smirnov - - `'PSI'`: Population Stability Index - - `'SW'`: Shapiro-Wilk - - `'AD'`: Anderson-Darling - - Details on the available tests can be found [here](/probatus/api/stat_tests.html#available-tests). - - verbose (int, optional): - Controls verbosity of the output: - - - 0 - neither prints nor warnings are shown - - 1 - 50 - only most important warnings - - 51 - 100 - shows other warnings and prints - - above 100 - presents all prints and all warnings (including SHAP warnings). - - random_state (int, optional): - Random state set at each round of feature elimination. If it is None, the results will not be - reproducible and in random search at each iteration a different hyperparameters might be tested. For - reproducible results set it to integer. - """ - super().__init__( - clf=clf, - sample_train_test_split_seed=False, - train_sampling_type="bootstrap", - test_sampling_type="bootstrap", - iterations=iterations, - scoring=scoring, - train_sampling_fraction=train_sampling_fraction, - test_sampling_fraction=test_sampling_fraction, - test_prc=test_prc, - n_jobs=n_jobs, - stats_tests_to_apply=stats_tests_to_apply, - verbose=verbose, - random_state=random_state, - ) diff --git a/probatus/missing_values/__init__.py b/probatus/missing_values/__init__.py deleted file mode 100644 index 04a73b50..00000000 --- a/probatus/missing_values/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2020 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -from .imputation import ImputationSelector - -__all__ = ["ImputationSelector"] diff --git a/probatus/missing_values/imputation.py b/probatus/missing_values/imputation.py deleted file mode 100644 index 80e070eb..00000000 --- a/probatus/missing_values/imputation.py +++ /dev/null @@ -1,403 +0,0 @@ -# Copyright (c) 2021 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -from sklearn.compose import ColumnTransformer -from sklearn.impute import SimpleImputer -from sklearn.model_selection import cross_validate -from sklearn.pipeline import Pipeline -from sklearn.preprocessing import OneHotEncoder - -from probatus.utils import BaseFitComputePlotClass, get_single_scorer, preprocess_data, preprocess_labels - - -class ImputationSelector(BaseFitComputePlotClass): - """ - Comparison of various imputation strategies that can be used for imputing missing values. - - The aim of this class is to present the model performance based on imputation - strategies and a chosen model. - For models like XGBoost & LighGBM which have capabilities to handle missing values by default - the model performance with no imputation will be shown as well. - The missing values categorical features are imputed with the value `missing` and an missing indicator is - added. - - Example: - ```python - - #Import the class - import pandas as pd - import numpy as np - import matplotlib.pyplot as plt - from probatus.missing_values.imputation import ImputationSelector - from probatus.utils.missing_helpers import generate_MCAR - from sklearn.linear_model import LogisticRegression - from sklearn.experimental import enable_iterative_imputer - from sklearn.impute import KNNImputer,SimpleImputer,IterativeImputer - from sklearn.datasets import make_classification - - # Create data with missing values. - n_features = 10 - X,y = make_classification(n_samples=1000,n_features=n_features,random_state=123,class_sep=0.3) - X = pd.DataFrame(X, columns=["f_"+str(i) for i in range(0,n_features)]) - X_missing = generate_MCAR(X,missing=0.2) - - # Create the strategies. - strategies = { - 'Simple Median Imputer' : SimpleImputer(strategy='median',add_indicator=True), - 'Simple Mean Imputer' : SimpleImputer(strategy='mean',add_indicator=True), - 'Iterative Imputer' : IterativeImputer(add_indicator=True,n_nearest_features=5, - sample_posterior=True), - 'KNN' : KNNImputer(n_neighbors=3)} - #Create a classifier. - clf = LogisticRegression() - #Create the comparison of the imputation strategies. - cmp = ImputationSelector( - clf=clf, - strategies=strategies, - cv=5, - model_na_support=False) - - cmp.fit_compute(X_missing,y) - #Plot the results. - performance_plot=cmp.plot() - - ``` - - - - """ - - def __init__( - self, - clf, - strategies, - scoring="roc_auc", - cv=5, - model_na_support=False, - n_jobs=-1, - verbose=0, - random_state=None, - ): - """ - Initialise the class. - - Args: - clf (binary classifier,sklearn.Pipeline): - A binary classification model, that will used to evaluate various imputation strategies. - - strategies (dictionary of sklearn.impute objects or any other scikit learn compatible imputer.): - Dictionary containing the sklearn.impute objects. - e.g. - strategies = {'KNN' : KNNImputer(n_neighbors=3), - 'Simple Median Imputer' : SimpleImputer(strategy='median',add_indicator=True), - 'Iterative Imputer' : IterativeImputer(add_indicator=True,n_nearest_features=5, - sample_posterior=True)} - This allows you to have fine grained control over the imputation method. - - scoring (string, list of strings, probatus.utils.Scorer or list of probatus.utils.Scorers, optional): - Metrics for which the score is calculated. It can be either a name or list of names metric names and - needs to be aligned with predefined - [classification scorers names in sklearn](https://scikit-learn.org/stable/modules/model_evaluation.html). - Another option is using probatus.utils.Scorer to define a custom metric. - - model_na_support (boolean): default False - If the classifier supports missing values by default e.g. LightGBM,XGBoost etc. - If True an default comparison `No Imputation` result will be added indicating the model performance - without any explicit imputation. - If False only the provided strategies will be used. - - n_jobs (int, optional): - Number of cores to run in parallel while fitting across folds. None means 1 unless in a - `joblib.parallel_backend` context. -1 means using all processors. - - verbose (int, optional): - Controls verbosity of the output: - - - 0 - nether prints nor warnings are shown - - 1 - 50 - only most important warnings regarding data properties are shown (excluding SHAP warnings) - - 51 - 100 - shows most important warnings, prints of the feature removal process - - above 100 - presents all prints and all warnings (including SHAP warnings). - - random_state (int, optional): - Random state set at each round of feature elimination. If it is None, the results will not be - reproducible and in random search at each iteration a different hyperparameters might be tested. For - reproducible results set it to integer. - """ # noqa - self.clf = clf - self.model_na_support = model_na_support - self.cv = cv - self.scorer = get_single_scorer(scoring) - self.strategies = strategies - self.verbose = verbose - self.n_jobs = n_jobs - self.random_state = random_state - self.fitted = False - self.report_df = pd.DataFrame([]) - - def __repr__(self): - """ - String representation. - """ - return f"Imputation comparison for {self.clf.__class__.__name__}" - - def fit(self, X, y, column_names=None): - """ - Calculates the cross validated results for various imputation strategies. - - Args: - X (pd.DataFrame): - input variables. - - y (pd.Series): - target variable. - - column_names (None, or list of str, optional): - List of feature names for the dataset. - If None, then column names from the X dataframe are used. - """ - if self.random_state is not None: - np.random.seed(self.random_state) - - # Place holder for results. - results = [] - - self.X, self.column_names = preprocess_data(X, column_names=column_names, verbose=self.verbose) - self.y = preprocess_labels(y, index=self.X.index, verbose=self.verbose) - - # Identify categorical features. - categorical_columns = X.select_dtypes(include=["category", "object"]).columns - # Identify the numeric columns.Numeric columns are all columns expect the categorical columns - numeric_columns = X.select_dtypes("number").columns - - for strategy in self.strategies: - numeric_transformer = Pipeline(steps=[("imputer", self.strategies[strategy])]) - - categorical_transformer = Pipeline( - steps=[ - ( - "imp_cat", - SimpleImputer( - strategy="constant", - fill_value="missing", - add_indicator=True, - ), - ), - ("ohe_cat", OneHotEncoder(handle_unknown="ignore")), - ] - ) - - preprocessor = ColumnTransformer( - transformers=[ - ("num", numeric_transformer, numeric_columns), - ("cat", categorical_transformer, categorical_columns), - ], - remainder="passthrough", - ) - - model_pipeline = Pipeline(steps=[("preprocessor", preprocessor), ("classifier", self.clf)]) - - temp_results = self._calculate_results(X, y, clf=model_pipeline, strategy=strategy) - - results.append(temp_results) - - # If model supports missing values by default, then calculate the scores - # on raw data without any imputation. - if self.model_na_support: - categorical_transformer = Pipeline( - steps=[ - ("ohe_cat", OneHotEncoder(handle_unknown="ignore")), - ] - ) - - preprocessor = ColumnTransformer( - transformers=[("cat", categorical_transformer, categorical_columns)], - remainder="passthrough", - ) - - model_pipeline = Pipeline(steps=[("preprocessor", preprocessor), ("classifier", self.clf)]) - - temp_results = self._calculate_results(X, y, clf=model_pipeline, strategy="No Imputation") - results.append(temp_results) - - self.report_df = pd.DataFrame(results) - # Set the index of the dataframe to the imputation methods. - self.report_df = self.report_df.set_index(self.report_df.strategy) - self.report_df.drop(columns=["strategy"], inplace=True) - self.report_df.sort_values(by="mean_test_score", inplace=True) - self.fitted = True - return self - - def _calculate_results(self, X, y, clf, strategy): - """ - Method to calculate the results for a particular imputation strategy. - - Args: - X (pd.DataFrame): - input variables. - - y (pd.Series): - target variable. - - clf (binary classifier,sklearn.Pipeline): - A binary classification model, that will used to evaluate various imputation strategies. - - strategy(string): - Name of the strategy used for imputation. - - Returns: - - temp_df(dict) : Dictionary containing the results of the evaluation. - """ - - imputation_cv_results = cross_validate( - clf, - X, - y, - scoring=self.scorer.scorer, - cv=self.cv, - n_jobs=self.n_jobs, - return_train_score=True, - ) - # Calculate the mean of the results. - imp_agg_results = {k: np.mean(v) for k, v in imputation_cv_results.items()} - imp_agg_results = {"mean_" + str(key): val for key, val in imp_agg_results.items()} - imp_agg_results["test_score_std"] = np.std(imputation_cv_results["test_score"]) - imp_agg_results["train_score_std"] = np.std(imputation_cv_results["train_score"]) - # Round off all calculations to 3 decimal places - imp_agg_results = {k: np.round(v, 3) for k, v in imp_agg_results.items()} - imp_agg_results["strategy"] = strategy - - return imp_agg_results - - def compute(self): - """ - Checks if fit() method has been run. - - and computes the DataFrame with results of imputation for each - strategy. - - Returns: - (pd.DataFrame): - DataFrame with results of imputation for each strategy. - """ - self._check_if_fitted() - return self.report_df - - def fit_compute(self, X, y, column_names=None): - """ - Calculates the cross validated results for various imputation strategies. - - Args: - X (pd.DataFrame): - input variables. - - y (pd.Series): - target variable. - - column_names (None, or list of str, optional): - List of feature names for the dataset. - If None, then column names from the X dataframe are used. - - Returns: - (pd.DataFrame): - DataFrame with results of imputation for each strategy. - - """ - self.fit(X, y, column_names=column_names) - return self.compute() - - def plot(self, show=True, **figure_kwargs): - """ - Generates plot of the performance of various imputation strategies. - - Args: - show (bool, optional): - If True, the plots are showed to the user, otherwise they are not shown. Not showing plot can be useful, - when you want to edit the returned axis, before showing it. - - **figure_kwargs: - Keyword arguments that are passed to the plt.figure, at its initialization. - - Returns: - (plt.axis): - Axis containing the performance plot. - """ - fig, ax = plt.subplots(**figure_kwargs) - - report_df = self.compute() - imp_methods = list(report_df.index) - test_performance = list(report_df["mean_test_score"]) - test_std_error = list(report_df["test_score_std"]) - train_performance = list(report_df["mean_train_score"]) - train_std_error = list(report_df["train_score_std"]) - - y = np.arange(len(imp_methods)) # the label locations - width = 0.35 # the width of the bars - - def _autolabel(rects): - """ - Label the bars of the plot. - """ - for rect in rects: - width = rect.get_width() - ax.annotate( - f"{width}", - xy=((width + 0.05 * width), rect.get_y() + rect.get_height() / 2), - xytext=(4, 0), # 4 points horizontal offset - textcoords="offset points", - ha="center", - va="bottom", - fontsize="small", - ) - - train_rect = ax.barh( - y - width / 2, - train_performance, - width, - xerr=train_std_error, - align="center", - label="CV-Train", - ) - test_rect = ax.barh( - y + width / 2, - test_performance, - width, - xerr=test_std_error, - align="center", - label="CV-Test", - ) - _autolabel(train_rect) - _autolabel(test_rect) - - ax.set_xlabel(f'{self.scorer.metric_name.replace("_"," ").upper()} Score') - ax.set_title("Imputation Techniques Comparison") - ax.set_yticks(y) - ax.set_yticklabels(imp_methods, rotation=45) - plt.margins(0.2) - plt.legend(loc="best", ncol=2) - fig.tight_layout() - - if show: - plt.show() - else: - plt.close() - return ax diff --git a/probatus/stat_tests/__init__.py b/probatus/stat_tests/__init__.py deleted file mode 100644 index 6a44af74..00000000 --- a/probatus/stat_tests/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2020 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -from .ad import ad -from .es import es -from .sw import sw -from .ks import ks -from .psi import psi -from .distribution_statistics import DistributionStatistics -from .distribution_statistics import AutoDist - -__all__ = ["ks", "psi", "ad", "es", "sw", "DistributionStatistics", "AutoDist"] diff --git a/probatus/stat_tests/ad.py b/probatus/stat_tests/ad.py deleted file mode 100644 index aaa05a82..00000000 --- a/probatus/stat_tests/ad.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) 2020 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -from probatus.stat_tests.utils import verbose_p_vals -from probatus.utils import NotInstalledError - -from ..utils import assure_numpy_array - -try: - from scipy import stats -except ModuleNotFoundError: - stats = NotInstalledError("scipy", "extras") - - -@verbose_p_vals -def ad(d1, d2, verbose=False): - """ - Calculates the Anderson-Darling test statistic on 2 distributions. - - Can be used on continuous or discrete distributions. - - Any binning/bucketing of the distributions/samples should be done before passing them to this function. - - Advantages: - - - Unlike the KS, the AD (like the ES) can be used on both continuous & discrete distributions. - - Works well even when the sample has fewer than 25 observations. - - More powerful than KS, especially for differences in the tails of distributions. - - References: - - - [Wikipedia article about the Anderson-Darling test](https://en.wikipedia.org/wiki/Anderson%E2%80%93Darling_test) - - [SciPy documentation](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.anderson_ksamp.html) - - Args: - d1 (np.array or pandas.Series): First sample. - - d2 (np.array or pandas.Series): Second sample. - - verbose (bool): If True, useful interpretation info is printed to stdout. - - Returns: - float: Anderson-Darling test statistic. - float: p-value of rejecting the null hypothesis (that the two distributions are identical). - """ - d1 = assure_numpy_array(d1) - d2 = assure_numpy_array(d2) - - ad, critical_values, pvalue = stats.anderson_ksamp([d1, d2]) - - return ad, pvalue diff --git a/probatus/stat_tests/distribution_statistics.py b/probatus/stat_tests/distribution_statistics.py deleted file mode 100644 index 6de31d48..00000000 --- a/probatus/stat_tests/distribution_statistics.py +++ /dev/null @@ -1,424 +0,0 @@ -# Copyright (c) 2020 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -import itertools -import warnings - -import numpy as np -import pandas as pd -from tqdm import tqdm - -from probatus.binning import AgglomerativeBucketer, QuantileBucketer, SimpleBucketer -from probatus.stat_tests import ad, es, ks, psi, sw -from probatus.utils.arrayfuncs import check_numeric_dtypes - - -class DistributionStatistics: - """ - Wrapper that applies a statistical test to compare two distributions. - - Details on the available tests can be found [here](/probatus/api/stat_tests.html#available-tests). - - For some tests, default data binning strategies are also provided. - - Example: - ```python - import numpy as np - import pandas as pd - from probatus.stat_tests import DistributionStatistics - - d1 = np.histogram(np.random.normal(size=1000), 10)[0] - d2 = np.histogram(np.random.normal(size=1000), 10)[0] - myTest = DistributionStatistics('KS', bin_count=10) - test_statistic, p_value = myTest.compute(d1, d2, verbose=True) - ``` - """ - - binning_strategy_dict = { - "simplebucketer": SimpleBucketer, - "agglomerativebucketer": AgglomerativeBucketer, - "quantilebucketer": QuantileBucketer, - None: None, - } - statistical_test_dict = { - "ES": { - "func": es, - "name": "Epps-Singleton", - "default_binning": None, - }, - "KS": { - "func": ks, - "name": "Kolmogorov-Smirnov", - "default_binning": None, - }, - "AD": { - "func": ad, - "name": "Anderson-Darling TS", - "default_binning": None, - }, - "SW": { - "func": sw, - "name": "Shapiro-Wilk based difference", - "default_binning": None, - }, - "PSI": { - "func": psi, - "name": "Population Stability Index", - "default_binning": "quantilebucketer", - }, - } - - def __init__(self, statistical_test, binning_strategy="default", bin_count=10): - """ - Initializes the class. - - Args: - statistical_test (str): Statistical test to apply. Available tests: - - - `'ES'`: Epps-Singleton - - `'KS'`: Kolmogorov-Smirnov - - `'PSI'`: Population Stability Index - - `'SW'`: Shapiro-Wilk - - `'AD'`: Anderson-Darling - - Details on the available tests can be found [here](/probatus/api/stat_tests.html#available-tests) - - binning_strategy (string, optional): - Binning strategy to apply, binning strategies implemented: - - - `'simplebucketer'`: equally spaced bins, - - `'agglomerativebucketer'`: binning by applying the Scikit-learn implementation of Agglomerative - Clustering, - - `'quantilebucketer'`: bins with equal number of elements, - - `'default'`: applies a default binning for a given stats_test. For all tests apart from PSI, no - binning (None) is used. For PSI by default quantilebucketer is used, - - `None`: no binning is applied. The test is computed based on original distribution. - - bin_count (int, optional): In case binning_strategy is not None, specify the number of bins to be used by - the binning strategy. By default 10 bins are used. - """ - self.statistical_test = statistical_test.upper() - self.binning_strategy = binning_strategy - self.bin_count = bin_count - self.fitted = False - - # Initialize the statistical test - if self.statistical_test not in self.statistical_test_dict: - raise NotImplementedError(f"The statistical test should be one of {self.statistical_test_dict.keys()}") - else: - self.statistical_test_name = self.statistical_test_dict[self.statistical_test]["name"] - self._statistical_test_function = self.statistical_test_dict[self.statistical_test]["func"] - - # Initialize the binning strategy - if self.binning_strategy: - self.binning_strategy = self.binning_strategy.lower() - if self.binning_strategy == "default": - self.binning_strategy = self.statistical_test_dict[self.statistical_test]["default_binning"] - if self.binning_strategy not in self.binning_strategy_dict: - raise NotImplementedError( - f"The binning strategy should be one of {list(self.binning_strategy_dict.keys())}" - ) - else: - binner = self.binning_strategy_dict[self.binning_strategy] - if binner is not None: - self.binner = binner(bin_count=self.bin_count) - - def __repr__(self): - """ - String representation. - """ - repr_ = f"DistributionStatistics object\n\tstatistical_test: {self.statistical_test}" - if self.binning_strategy: - repr_ += f"\n\tbinning_strategy: {self.binning_strategy}\n\tbin_count: {self.bin_count}" - else: - repr_ += "\n\tNo binning applied" - if self.fitted: - repr_ += f"\nResults\n\tvalue {self.statistical_test}-statistic: {self.statistic}" - if hasattr(self, "p_value"): - repr_ += f"\n\tp-value: {self.p_value}" - return repr_ - - def compute(self, d1, d2, verbose=False): - """ - Apply the statistical test and compute statistic value and p-value. - - Args: - d1 (np.array or pandas.DataFrame): - distribution 1. - - d2 (np.array or pandas.DataFrame): - distribution 2. - - verbose (bool, optional): - Flag indicating whether prints should be shown. - - Returns: - float: Statistic value - float: p_value. For PSI test, only the statistic value is returned - """ - check_numeric_dtypes(d1) - check_numeric_dtypes(d2) - - # Bin the data - if self.binning_strategy: - self.binner.fit(d1) - d1_preprocessed = self.binner.compute(d1) - d2_preprocessed = self.binner.compute(d2) - else: - d1_preprocessed, d2_preprocessed = d1, d2 - - # Perform the statistical test - res = self._statistical_test_function(d1_preprocessed, d2_preprocessed, verbose=verbose) - self.fitted = True - - # Check form of results and return - if type(res) == tuple: - self.statistic, self.p_value = res - return self.statistic, self.p_value - else: - self.statistic = res - return self.statistic - - -class AutoDist: - """Apply stat tests and binning strategies. - - Class to automatically apply all implemented statistical distribution tests and binning strategies - to (a selection of) features in two dataframes. - - Details on the available tests can be found [here](/probatus/api/stat_tests.html#available-tests). - - Example: - ```python - import numpy as np - import pandas as pd - from probatus.stat_tests import AutoDist - - df1 = pd.DataFrame(np.random.normal(size=(1000, 2)), columns=['feat_0', 'feat_1']) - df2 = pd.DataFrame(np.random.normal(size=(1000, 2)), columns=['feat_0', 'feat_1']) - myAutoDist = AutoDist(statistical_tests=["KS", "PSI"], binning_strategies='simplebucketer', bin_count=10) - myAutoDist.compute(df1, df2, column_names=df1.columns) - ``` - - - """ - - def __init__(self, statistical_tests="all", binning_strategies="default", bin_count=10): - """ - Initializes the class. - - Args: - statistical_tests (str or list of str, optional): Test or list of tests to apply. - Set to `'all'` to apply all the available test. Available tests: - - - `'ES'`: Epps-Singleton - - `'KS'`: Kolmogorov-Smirnov - - `'PSI'`: Population Stability Index - - `'SW'`: Shapiro-Wilk - - `'AD'`: Anderson-Darling - - Details on the available tests can be found [here](/probatus/api/stat_tests.html#available-tests). - - binning_strategies (str, optional): Binning strategies to apply for each test, either list of tests names, - 'all' or 'default'. Binning strategies that can be chosen: - - - `'SimpleBucketer'`: equally spaced bins, - - `'AgglomerativeBucketer'`: binning by applying the Scikit-learn implementation of Agglomerative - Clustering, - - `'QuantileBucketer'`: bins with equal number of elements, - - `None`: no binning is applied. Note that not all statistical tests will be performed since some of - them require binning strategies. - - `'default'`: applies a default binning for a given stats_test. For all tests apart from PSI, no - binning (None) is used. For PSI by default quantilebucketer is used. - - `'all'`: each binning strategy is used for each statistical test - - bin_count (integer, None or list of integers, optional): - bin_count value(s) to be used, note that None can only be used when no bucketing strategy is applied. - """ - self.fitted = False - - # Initialize statistical tests to be performed - if statistical_tests == "all": - self.statistical_tests = list(DistributionStatistics.statistical_test_dict.keys()) - elif isinstance(statistical_tests, str): - self.statistical_tests = [statistical_tests] - else: - self.statistical_tests = statistical_tests - - # Initialize binning strategies to be used - if binning_strategies == "all": - self.binning_strategies = list(DistributionStatistics.binning_strategy_dict.keys()) - elif isinstance(binning_strategies, str): - self.binning_strategies = [binning_strategies] - elif binning_strategies is None: - self.binning_strategies = [None] - else: - self.binning_strategies = binning_strategies - if not isinstance(bin_count, list): - self.bin_count = [bin_count] - else: - self.bin_count = bin_count - - def __repr__(self): - """ - String representation. - """ - repr_ = "AutoDist object" - if not self.fitted: - repr_ += "\n\tAutoDist not fitted" - if self.fitted: - repr_ += "\n\tAutoDist fitted" - repr_ += f"\n\tstatistical_tests: {self.statistical_tests}" - repr_ += f"\n\tbinning_strategies: {self.binning_strategies}" - repr_ += f"\n\tbin_count: {self.bin_count}" - return repr_ - - def compute( - self, - df1, - df2, - column_names=None, - return_failed_tests=True, - suppress_warnings=False, - ): - """ - Fit the AutoDist object to data; i.e. apply the statistical tests and binning strategies. - - Args: - - df1 (pandas.DataFrame): - DataFrame 1 for distribution comparison with DataFrame 2. - - df2 (pandas.DataFrame): - DataFrame 2 for distribution comparison with DataFrame 1. - - column_names (list of str, optional): - list of columns in df1 and df2 that should be compared. If None, all column names will be compared. - - return_failed_tests (bool, optional): - remove tests in result that did not succeed. - - suppress_warnings (bool, optional): - whether to suppress warnings during the fit process. - - Returns: - pandas.DataFrame: DataFrame with results of the performed statistical tests and binning strategies. - - """ - if column_names is None: - column_names = df1.columns.to_list() - if len(set(column_names) - set(df2.columns)): - raise Exception("column_names was set to None but columns in provided dataframes are different") - # Check if all columns in column_names are in df1 and df2 - elif len(set(column_names) - set(df1.columns)) or len(set(column_names) - set(df2.columns)): - raise Exception("Not all columns in `column_names` are in the provided dataframes") - - # Calculate statistics and p-values for all combinations - result_all = [] - for col in column_names: - # Issue a warning if missing values are present in one of the two columns. These observations are removed - # in the calculations. - if np.sum(df1[col].isna()) + np.sum(df2[col].isna()): - warnings.warn(f"Missing values in column {col} have been removed") - - # Remove the missing values. - feature_df1 = df1[col].dropna() - feature_df2 = df2[col].dropna() - - for stat_test, bin_strat, bins in tqdm( - list( - itertools.product( - self.statistical_tests, - self.binning_strategies, - self.bin_count, - ) - ) - ): - if self.binning_strategies == ["default"]: - bin_strat = DistributionStatistics.statistical_test_dict[stat_test]["default_binning"] - - dist = DistributionStatistics( - statistical_test=stat_test, - binning_strategy=bin_strat, - bin_count=bins, - ) - try: - if suppress_warnings: - warnings.filterwarnings("ignore") - _ = dist.compute(feature_df1, feature_df2) - if suppress_warnings: - warnings.filterwarnings("default") - statistic = dist.statistic - p_value = dist.p_value - except Exception: - statistic, p_value = "an error occurred", None - pass - - # Append result to results list - result_ = { - "column": col, - "statistical_test": stat_test, - "binning_strategy": bin_strat, - "bin_count": bins, - "statistic": statistic, - "p_value": p_value, - } - - result_all.append(result_) - - result_all = pd.DataFrame(result_all) - - if not return_failed_tests: - result_all = result_all[result_all["statistic"] != "an error occurred"] - self.fitted = True - self._result = result_all[ - [ - "column", - "statistical_test", - "binning_strategy", - "bin_count", - "statistic", - "p_value", - ] - ] - self._result["bin_count"] = self._result["bin_count"].astype(int) - self._result.loc[self._result["binning_strategy"].isnull(), "bin_count"] = 0 - self._result.loc[self._result["binning_strategy"].isnull(), "binning_strategy"] = "no_bucketing" - - # Remove duplicates that appear if multiple bin numbers are passed, and binning strategy None - - self._result = self._result.drop_duplicates( - subset=["column", "statistical_test", "binning_strategy", "bin_count"], - keep="first", - ) - - # create pivot table as final output - self.result = pd.pivot_table( - self._result, - values=["statistic", "p_value"], - index="column", - columns=["statistical_test", "binning_strategy", "bin_count"], - aggfunc="sum", - ) - - # flatten multi-index - self.result.columns = ["_".join([str(x) for x in line]) for line in self.result.columns.values] - self.result.reset_index(inplace=True) - return self.result diff --git a/probatus/stat_tests/es.py b/probatus/stat_tests/es.py deleted file mode 100644 index a3a433b4..00000000 --- a/probatus/stat_tests/es.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) 2020 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -from probatus.utils import NotInstalledError - -try: - from scipy import stats -except ModuleNotFoundError: - stats = NotInstalledError("scipy", "extras") - -from probatus.stat_tests.utils import verbose_p_vals - -from ..utils import assure_numpy_array - - -@verbose_p_vals -def es(d1, d2, verbose=False): - """ - Calculates the Epps-Singleton test statistic on 2 distributions. - - Can be used on continuous or discrete distributions. - Any binning/bucketing of the distributions/samples should be done before passing them to this - function. - - Whereas KS relies on the empirical distribution function, ES is based on the empirical characteristic function - (Epps & Singleton 1986, Goerg & Kaiser 2009). - - Advantages: - - - Unlike the KS, the ES can be used on both continuous & discrete distributions. - - - ES has higher power (vs KS) in many examples. - - Disadvantages: - - - Not recommended for fewer than 25 observations. Instead, use the Anderson-Darling TS. (However, ES can still be - used for small samples. A correction factor is applied so that the asymptotic TS distribution more closely follows - the chi-squared distribution, such that p-values can be computed.) - - - References: - - - [SciPy documentation](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.epps_singleton_2samp.html) - - Args: - d1 (np.array or pandas.Series): First sample. - - d2 (np.array or pandas.Series): Second sample. - - verbose (bool): If True, useful interpretation info is printed to stdout. - - Returns: - float: Epps-Singleton test statistic - float: p-value of rejecting the null hypothesis (that the two distributions are identical) - """ - d1 = assure_numpy_array(d1) - d2 = assure_numpy_array(d2) - - es, pvalue = stats.epps_singleton_2samp(d1, d2) - - return es, pvalue diff --git a/probatus/stat_tests/ks.py b/probatus/stat_tests/ks.py deleted file mode 100644 index 175951ab..00000000 --- a/probatus/stat_tests/ks.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) 2020 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -from probatus.utils import NotInstalledError - -try: - from scipy import stats -except ModuleNotFoundError: - stats = NotInstalledError("scipy", "extras") - -from probatus.stat_tests.utils import verbose_p_vals - -from ..utils import assure_numpy_array - - -@verbose_p_vals -def ks(d1, d2, verbose=False): - """ - Calculates the Kolmogorov-Smirnov test statistic on 2 samples. - - Any binning/bucketing of the distributions/samples should be done before passing them to this function. - - References: - - - [Wikipedia article about Kolmogorov-Smirnov test](https://en.wikipedia.org/wiki/Kolmogorov%E2%80%93Smirnov_test) - - [SciPy documentation](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.ks_2samp.html) - - Args: - d1 (np.ndarray or pandas.Series): First sample. - - d2 (np.ndarray or pandas.Series): Second sample. - - verbose (bool): If True, useful interpretation info is printed to stdout. - - Returns: - float: Kolmogorov-Smirnov test statistic. - float: p-value of rejecting the null hypothesis (that the two distributions are identical). - """ - # Perform data checks - d1 = assure_numpy_array(d1) - d2 = assure_numpy_array(d2) - - # Perform statistical tests - ks, pvalue = stats.ks_2samp(d1, d2) - - return ks, pvalue diff --git a/probatus/stat_tests/psi.py b/probatus/stat_tests/psi.py deleted file mode 100644 index 41f55e2b..00000000 --- a/probatus/stat_tests/psi.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (c) 2020 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -import warnings - -import numpy as np - -from probatus.utils import NotInstalledError - -try: - from scipy import stats -except ModuleNotFoundError: - stats = NotInstalledError("scipy", "extras") - -from ..utils import assure_numpy_array - - -def psi(d1, d2, verbose=False): - """ - Calculates the Population Stability Index. - - A simple statistical test that quantifies the similarity of two distributions. - Commonly used in the banking / risk modeling industry. - Only works on categorical data or bucketed numerical data. - Distributions must be binned/bucketed before passing them to this function. - Bin boundaries should be the same for both distributions. - Distributions must have the same number of buckets. - Note that the PSI varies with number of buckets chosen (typically 10-20 bins are used). - Quantile bucketing is typically recommended. - - References: - - - [Statistical Properties of Population Stability Index](https://scholarworks.wmich.edu/cgi/viewcontent.cgi?article=4249&context=dissertations) - - - Args: - d1 (np.ndarray or pandas.Series): First distribution ("expected"). - - d2 (np.ndarray or pandas.Series): Second distribution ("actual"). - - verbose (bool): If True, useful interpretation info is printed to stdout. - - - Returns: - float: Measure of the similarity between d1 & d2. (range 0-inf, with 0 indicating identical - distributions and > 0.25 indicating significantly different distributions) - float: p-value for rejecting null hypothesis (that the two distributions are identical) - """ # noqa - # Perform data checks - d1 = assure_numpy_array(d1) - d2 = assure_numpy_array(d2) - - if len(d1) < 10: - warnings.warn("PSI is not well-behaved when using less than 10 bins.") - if len(d1) > 20: - warnings.warn("PSI is not well-behaved when using more than 20 bins.") - if len(d1) != len(d2): - raise ValueError("Distributions do not have the same number of bins.") - - # Number of bins/buckets - b = len(d1) - - # Calculate the number of samples in each distribution - n = d1.sum() - m = d2.sum() - - # Calculate the ratio of samples in each bin - expected_ratio = d1 / n - actual_ratio = d2 / m - - # Necessary to avoid divide by zero and ln(0). Should have minor impact on PSI value. - has_empty_bucket = False - for i in range(b): - if expected_ratio[i] == 0: - expected_ratio[i] = 0.0001 - has_empty_bucket = True - - if actual_ratio[i] == 0: - actual_ratio[i] = 0.0001 - has_empty_bucket = True - - if has_empty_bucket: - warnings.warn( - "PSI: Some of the buckets have zero counts. In theory this situation would mean PSI=Inf due to " - "division by 0. However, we artificially modified the count of samples in these bins to a small " - "number. This may cause that the PSI value for this feature is over-estimated (larger). " - "Decreasing the number of buckets may also help avoid buckets with zero counts." - ) - - # Calculate the PSI value - psi_value = np.sum((actual_ratio - expected_ratio) * np.log(actual_ratio / expected_ratio)) - - # Print the evaluation of statistical hypotheses - if verbose: - print("\nPSI =", psi_value) - - print("\nPSI: Critical values defined according to de facto industry standard:") - if psi_value <= 0.1: - print("PSI <= 0.10: No significant distribution change.") - elif 0.1 < psi_value <= 0.25: - print("PSI <= 0.25: Small distribution change; may require investigation.") - elif psi_value > 0.25: - print("PSI > 0.25: Significant distribution change; investigate.") - - # Calculate the critical values and - alpha = [0.95, 0.99, 0.999] - z_alpha = stats.norm.ppf(alpha) - psi_critvals = ((1 / n) + (1 / m)) * (b - 1) + z_alpha * ((1 / n) + (1 / m)) * np.sqrt(2 * (b - 1)) - print("\nPSI: Critical values defined according to Yurdakul (2018):") - if psi_value > psi_critvals[2]: - print("99.9% confident distributions have changed.") - elif psi_value > psi_critvals[1]: - print("99% confident distributions have changed.") - elif psi_value > psi_critvals[0]: - print("95% confident distributions have changed.") - elif psi_value < psi_critvals[0]: - print("No significant distribution change.") - - # Calculate p-value - z = (psi_value / ((1 / n) + (1 / m)) - (b - 1)) / np.sqrt(2 * (b - 1)) - p_value = 1 - stats.norm.cdf(z) - - return psi_value, p_value diff --git a/probatus/stat_tests/sw.py b/probatus/stat_tests/sw.py deleted file mode 100644 index bd27d44c..00000000 --- a/probatus/stat_tests/sw.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) 2020 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -import random - -import pandas as pd - -from probatus.utils import NotInstalledError - -from ..utils import assure_numpy_array - -try: - from scipy import stats -except ModuleNotFoundError: - stats = NotInstalledError("scipy", "extras") - - -def sw(d1, d2, verbose=False): - """ - Calculates the Shapiro-Wilk test statistic on 2 distributions. - - This examines whether deviation from normality of two distributions are significantly different. - - References: - - - [Wikipedia article about the Shapiro-Wilk test](https://en.wikipedia.org/wiki/Shapiro%E2%80%93Wilk_test) - - [SciPy documentation](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.shapiro.html) - - - Args: - d1 (np.ndarray or pandas.Series): First sample. - - d2 (np.ndarray or pandas.Series): Second sample. - - verbose (bool): If True, useful interpretation info is printed to stdout. - - Returns: - float: Shapiro-Wilk test statistic - float: p-value of rejecting the null hypothesis (that the two distributions are identical) - """ - d1 = assure_numpy_array(d1) - d2 = assure_numpy_array(d2) - - if len(d1) > 5000: - d1 = pd.Series(random.choices(d1, k=5000)) - if len(d2) > 5000: - d2 = pd.Series(random.choices(d2, k=5000)) - - delta = stats.shapiro(d1)[0] - stats.shapiro(d2)[0] - - d1 = pd.Series(d1) - d2 = pd.Series(d2) - - MOT = pd.concat([d1, d2]) - n1 = d1.shape[0] - n2 = d2.shape[0] - - def ran_delta(n1, n2): - take_ran = lambda n: random.sample(range(MOT.shape[0]), n) - ran_1 = MOT.iloc[take_ran(n1),] - ran_2 = MOT.iloc[take_ran(n2),] - delta_ran = stats.shapiro(ran_1)[0] - stats.shapiro(ran_2)[0] - return delta_ran - - collect = [ran_delta(n1, n2) for a in range(100)] - collect = pd.Series(list(collect)) - delta_p_value = 1 - stats.percentileofscore(collect, delta) / 100 - - quants = [0.025, 0.975] - sig_vals = list(collect.quantile(quants)) - - if verbose: - if delta < sig_vals[0] or delta > sig_vals[1]: - print("\nShapiro_Difference | Null hypothesis : REJECTED.") - print("\nDelta is outside 95% CI -> Distributions very different.") - else: - print("\nShapiro_Difference | Null hypothesis : NOT REJECTED.") - print("\nDelta is inside 95% CI -> Distributions are not different.") - - return delta, delta_p_value diff --git a/probatus/stat_tests/utils.py b/probatus/stat_tests/utils.py deleted file mode 100644 index bf5b307b..00000000 --- a/probatus/stat_tests/utils.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (c) 2020 ING Bank N.V. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -import functools - - -def verbose_p_vals(func): - """ - Decorator to enable verbose printing of p-values. - """ - - @functools.wraps(func) - def wrapper_verbose_p_vals(*args, **kwargs): - test_name = func.__name__.upper() - - stat, pvalue = func(*args, **kwargs) - - if "verbose" in kwargs and kwargs["verbose"] is True: - print(f"\n{test_name}: pvalue =", pvalue) - if pvalue < 0.01: - print( - "\n{}: Null hypothesis rejected with 99% confidence. Distributions very different.".format( - test_name - ) - ) - elif pvalue < 0.05: - print(f"\n{test_name}: Null hypothesis rejected with 95% confidence. Distributions different.") - else: - print( - "\n{}: Null hypothesis cannot be rejected. Distributions not statistically different.".format( - test_name - ) - ) - - return stat, pvalue - - return wrapper_verbose_p_vals diff --git a/tests/binning/__init__.py b/tests/binning/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/binning/test_binning.py b/tests/binning/test_binning.py deleted file mode 100644 index a9ec56f3..00000000 --- a/tests/binning/test_binning.py +++ /dev/null @@ -1,336 +0,0 @@ -import numpy as np -import pytest -from sklearn.exceptions import NotFittedError - -from probatus.binning import AgglomerativeBucketer, Bucketer, QuantileBucketer, SimpleBucketer, TreeBucketer - - -@pytest.mark.filterwarnings("ignore:") -def test_deprecations(): - """ - Test. - """ - x = [1, 2, 1] - bins = 3 - myBucketer = SimpleBucketer(bin_count=bins) - myBucketer.fit(x) - with pytest.deprecated_call(): - myBucketer.counts - - with pytest.deprecated_call(): - myBucketer.boundaries - - -def test_simple_bins(): - """ - Test. - """ - x = [1, 2, 1] - bins = 3 - myBucketer = SimpleBucketer(bin_count=bins) - with pytest.raises(NotFittedError): - myBucketer.compute([1, 2]) - - myBucketer.fit(x) - assert len(myBucketer.counts_) == bins - assert np.array_equal(myBucketer.counts_, np.array([2, 0, 1])) - assert len(myBucketer.boundaries_) == bins + 1 - np.testing.assert_array_almost_equal(myBucketer.boundaries_, np.array([-np.inf, 1.33333333, 1.66666667, np.inf])) - # test static method - counts, boundaries = SimpleBucketer(bin_count=bins).simple_bins(x, bins) - assert np.array_equal(myBucketer.counts_, counts) - np.testing.assert_array_almost_equal(myBucketer.boundaries_, boundaries) - assert repr(myBucketer).startswith("SimpleBucketer") - - -def test_quantile_bins(): - """ - Test. - """ - bins = 4 - random_state = np.random.RandomState(0) - x = random_state.normal(0, 1, size=1000) - myBucketer = QuantileBucketer(bin_count=bins) - with pytest.raises(NotFittedError): - myBucketer.compute([1, 2]) - myBucketer.fit(x) - assert len(myBucketer.counts_) == bins - assert np.array_equal(myBucketer.counts_, np.array([250, 250, 250, 250])) - assert len(myBucketer.boundaries_) == bins + 1 - np.testing.assert_array_almost_equal( - myBucketer.boundaries_, np.array([-np.inf, -0.7, -0.1, 0.6, np.inf]), decimal=1 - ) - # test static method - counts, boundaries = QuantileBucketer(bin_count=bins).quantile_bins(x, bins) - assert np.array_equal(myBucketer.counts_, counts) - np.testing.assert_array_almost_equal(myBucketer.boundaries_, boundaries) - # test inf edges - counts, boundaries = QuantileBucketer(bin_count=bins).quantile_bins(x, bins, inf_edges=True) - assert boundaries[0] == -np.inf - assert boundaries[-1] == np.inf - assert repr(myBucketer).startswith("QuantileBucketer") - - -def test_agglomerative_clustering_new(): - """ - Test. - """ - - x = [0.5, 1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4.5] - bins = 4 - myBucketer = AgglomerativeBucketer(bin_count=bins) - with pytest.raises(NotFittedError): - myBucketer.compute([1, 2]) - myBucketer.fit(x) - assert len(myBucketer.counts_) == bins - print(myBucketer.counts_) - assert np.array_equal(myBucketer.counts_, np.array([4, 2, 5, 3])) - assert len(myBucketer.boundaries_) == bins + 1 - np.testing.assert_array_almost_equal(myBucketer.boundaries_, np.array([-np.inf, 1.5, 2.5, 3.5, np.inf]), decimal=2) - # test static method - counts, boundaries = AgglomerativeBucketer(bin_count=bins).agglomerative_clustering_binning(x, bins) - assert np.array_equal(myBucketer.counts_, counts) - np.testing.assert_array_almost_equal(myBucketer.boundaries_, boundaries) - assert repr(myBucketer).startswith("AgglomerativeBucketer") - - -def test_compute(): - """ - Test. - """ - x = np.arange(10) - bins = 5 - myBucketer = QuantileBucketer(bins) - x_new = x - with pytest.raises(NotFittedError): - assert myBucketer.compute(x_new) - myBucketer.fit(x) - assert len(myBucketer.compute(x_new)) == bins - np.testing.assert_array_equal(myBucketer.counts_, myBucketer.compute(x_new)) - np.testing.assert_array_equal(myBucketer.counts_, myBucketer.fit_compute(x_new)) - x_new = x + 100 - np.testing.assert_array_equal(np.array([0, 0, 0, 0, 10]), myBucketer.compute(x_new)) - x_new = x - 100 - np.testing.assert_array_equal(np.array([10, 0, 0, 0, 0]), myBucketer.compute(x_new)) - x_new = [1, 1, 1, 4, 4, 7] - np.testing.assert_array_equal(np.array([3, 0, 2, 1, 0]), myBucketer.compute(x_new)) - - -def test_quantile_with_unique_values(): - """ - Test. - """ - np.random.seed(42) - dist_0_1 = np.random.uniform(size=20) - dist_peak_at_0 = np.zeros(shape=20) - - skewed_dist = np.hstack((dist_0_1, dist_peak_at_0)) - actual_out = QuantileBucketer(10).quantile_bins(skewed_dist, 10) - - expected_out = ( - np.array([20, 4, 4, 4, 4, 4]), - np.array([0.0, 0.01894458, 0.23632033, 0.42214475, 0.60977678, 0.67440958, 0.99940487]), - ) - - assert (actual_out[0] == expected_out[0]).all() - - -def test_tree_bucketer(): - """ - Test. - """ - x = np.array( - [ - 0.0, - 0.2, - 0.4, - 0.6, - 0.8, - 1.0, - 1.2, - 1.4, - 1.6, - 1.8, - 2.0, - 2.2, - 2.4, - 2.6, - 2.8, - 3.0, - 3.2, - 3.4, - 3.6, - 3.8, - 4.0, - 4.2, - 4.4, - 4.6, - 4.8, - 5.0, - 5.2, - 5.4, - 5.6, - 5.8, - 6.0, - 6.2, - 6.4, - 6.6, - 6.8, - 7.0, - 7.2, - 7.4, - 7.6, - 7.8, - 8.0, - 8.2, - 8.4, - 8.6, - 8.8, - 9.0, - 9.2, - 9.4, - 9.6, - 9.8, - ] - ) - - y = np.array( - [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 0, - 0, - 0, - 1, - 0, - 0, - 1, - 0, - 1, - 1, - 0, - 0, - 0, - 1, - 1, - 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - ] - ) - - myTreeBucketer = TreeBucketer(inf_edges=False, max_depth=3, min_samples_leaf=10, random_state=42) - - with pytest.raises(NotFittedError): - myTreeBucketer.compute([1, 2]) - - myTreeBucketer.fit(x, y) - - assert all(myTreeBucketer.counts_ == np.array([21, 15, 14])) - assert myTreeBucketer.bin_count == 3 - assert all(myTreeBucketer.boundaries_ - np.array([0.0, 4.1, 7.1, 9.8]) < 0.01) - - # If infinite edges is False, it must get the edges of the x array - assert myTreeBucketer.boundaries_[0] == 0 - assert myTreeBucketer.boundaries_[-1] == 9.8 - - myTreeBucketer = TreeBucketer(inf_edges=True, max_depth=3, min_samples_leaf=10, random_state=42) - - myTreeBucketer.fit(x, y) - # check that the infinite edges is True, then edges must be infinite - assert myTreeBucketer.boundaries_[0] == -np.inf - assert myTreeBucketer.boundaries_[-1] == +np.inf - - -def test_tree_bucketer_dependence(): - """ - Test. - """ - x = np.arange(0, 10, 0.01) - y = [1 if z < 0.5 else 0 for z in np.random.uniform(size=x.shape[0])] - - # Test number of leaves is always within the expected ranges - myTreeBucketer = TreeBucketer(inf_edges=False, max_depth=3, min_samples_leaf=10, random_state=42).fit(x, y) - assert myTreeBucketer.bin_count <= np.power(2, myTreeBucketer.tree.max_depth) - - # Test number of leaves is always within the expected ranges - myTreeBucketer = TreeBucketer(inf_edges=False, max_depth=6, min_samples_leaf=1, random_state=42).fit(x, y) - assert myTreeBucketer.bin_count <= np.power(2, myTreeBucketer.tree.max_depth) - - # Test that the counts per bin never drop below min_samples_leaf - myTreeBucketer = TreeBucketer(inf_edges=False, max_depth=6, min_samples_leaf=100, random_state=42).fit(x, y) - assert all([x >= myTreeBucketer.tree.min_samples_leaf for x in myTreeBucketer.counts_]) - - myTreeBucketer = TreeBucketer(inf_edges=False, max_depth=6, min_samples_leaf=200, random_state=42).fit(x, y) - assert all([x >= myTreeBucketer.tree.min_samples_leaf for x in myTreeBucketer.counts_]) - - # Test that if the leaf is set to the number of entries,it raises an Error - myTreeBucketer = TreeBucketer(inf_edges=False, max_depth=6, min_samples_leaf=x.shape[0], random_state=42) - - with pytest.raises(ValueError): - assert myTreeBucketer.fit(x, y) - - # Test that if the leaf is set to the number of entries-1, it returns only one bin - myTreeBucketer = TreeBucketer(inf_edges=False, max_depth=6, min_samples_leaf=x.shape[0] - 1, random_state=42).fit( - x, y - ) - assert myTreeBucketer.bin_count == 1 - assert all([x >= myTreeBucketer.tree.min_samples_leaf for x in myTreeBucketer.counts_]) - - -def test_tree_binning(): - """ - Test binning with a decisiontree. - """ - x = [1, 2, 2, 5, 3] - y = [0, 0, 1, 1, 1] - myBucketer = TreeBucketer(inf_edges=True, max_depth=2, min_impurity_decrease=0.001) - myBucketer.fit(x, y) - assert myBucketer.boundaries_ == [-np.inf, 1.5, 2.5, np.inf] - assert myBucketer.bin_count == 3 - assert myBucketer.counts_ == [1, 2, 2] - - myBucketer = TreeBucketer(max_depth=2, min_impurity_decrease=0.001) - myBucketer.fit(x, y) - assert myBucketer.boundaries_ == [1, 1.5, 2.5, 5] - assert myBucketer.bin_count == 3 - assert myBucketer.counts_ == [1, 2, 2] - - -def test_compute_counts_per_bin(): - """ - Test for checking if counts per bin are correctly computed. - """ - x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - boundaries = [0, 1, 5, 6, 10, 11] # down boundary < current value <= up boundary - np.testing.assert_array_almost_equal(Bucketer._compute_counts_per_bin(x, boundaries), np.array([1, 4, 1, 4, 0])) diff --git a/tests/docs/test_docstring.py b/tests/docs/test_docstring.py index c53e840d..0ff744e2 100644 --- a/tests/docs/test_docstring.py +++ b/tests/docs/test_docstring.py @@ -7,13 +7,9 @@ import matplotlib.pyplot as plt import pytest -import probatus.binning import probatus.feature_elimination import probatus.interpret -import probatus.metric_volatility -import probatus.missing_values import probatus.sample_similarity -import probatus.stat_tests import probatus.utils # Turn off interactive mode in plots @@ -21,22 +17,12 @@ matplotlib.use("Agg") CLASSES_TO_TEST = [ - probatus.binning.SimpleBucketer, - probatus.binning.AgglomerativeBucketer, - probatus.binning.QuantileBucketer, - probatus.binning.TreeBucketer, probatus.feature_elimination.ShapRFECV, probatus.interpret.DependencePlotter, probatus.interpret.ShapModelInterpreter, - probatus.metric_volatility.TrainTestVolatility, - probatus.metric_volatility.BootstrappedVolatility, - probatus.metric_volatility.SplitSeedVolatility, probatus.sample_similarity.SHAPImportanceResemblance, probatus.sample_similarity.PermutationImportanceResemblance, - probatus.stat_tests.DistributionStatistics, - probatus.stat_tests.AutoDist, probatus.utils.Scorer, - probatus.missing_values.ImputationSelector, ] CLASSES_TO_TEST_LGBM = [ diff --git a/tests/interpret/test_shap_dependence.py b/tests/interpret/test_shap_dependence.py index 9a37468a..5e0e6fa3 100644 --- a/tests/interpret/test_shap_dependence.py +++ b/tests/interpret/test_shap_dependence.py @@ -121,8 +121,7 @@ def test_fit_complex(complex_data_split, complex_fitted_lightgbm): assert plotter.fitted is True # Check if plotting does not cause errors - for binning in ["simple", "agglomerative", "quantile"]: - _ = plotter.plot(feature="f2_missing", type_binning=binning, show=False) + _ = plotter.plot(feature="f2_missing", show=False) def test_get_X_y_shap_with_q_cut_normal(X_y, clf): @@ -182,8 +181,7 @@ def test_plot_normal(X_y, clf): Test. """ plotter = DependencePlotter(clf).fit(X_y[0], X_y[1]) - for binning in ["simple", "agglomerative", "quantile"]: - _ = plotter.plot(feature=0, type_binning=binning) + _ = plotter.plot(feature=0) def test_plot_class_names(X_y, clf): @@ -202,8 +200,8 @@ def test_plot_input(X_y, clf): plotter = DependencePlotter(clf).fit(X_y[0], X_y[1]) with pytest.raises(ValueError): plotter.plot(feature="not a feature") - with pytest.raises(ValueError): - plotter.plot(feature=0, type_binning=5) + with pytest.raises(TypeError): + plotter.plot(feature=0, bins=5.0) with pytest.raises(ValueError): plotter.plot(feature=0, min_q=1, max_q=0) diff --git a/tests/metric_volatility/__init__.py b/tests/metric_volatility/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/metric_volatility/test_metric_volatility.py b/tests/metric_volatility/test_metric_volatility.py deleted file mode 100644 index 2b832be6..00000000 --- a/tests/metric_volatility/test_metric_volatility.py +++ /dev/null @@ -1,422 +0,0 @@ -import os -from unittest.mock import patch - -import matplotlib -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -import pytest -from sklearn.tree import DecisionTreeClassifier - -from probatus.metric_volatility import ( - BaseVolatilityEstimator, - BootstrappedVolatility, - SplitSeedVolatility, - TrainTestVolatility, - check_sampling_input, - get_metric, - sample_data, -) -from probatus.stat_tests.distribution_statistics import DistributionStatistics -from probatus.utils import NotFittedError, Scorer - -# Turn off interactive mode in plots -plt.ioff() -matplotlib.use("Agg") - - -@pytest.fixture(scope="function") -def X_array(): - """ - Fixture. - """ - return np.array([[2, 1], [3, 2], [4, 3], [1, 2], [1, 1]]) - - -@pytest.fixture(scope="function") -def y_list(): - """ - Fixture. - """ - return [1, 0, 0, 1, 1] - - -@pytest.fixture(scope="function") -def y_array(y_list): - """ - Fixture. - """ - return np.array(y_list) - - -@pytest.fixture(scope="function") -def X_df(X_array): - """ - Fixture. - """ - return pd.DataFrame(X_array, columns=["c1", "c2"]) - - -@pytest.fixture(scope="function") -def y_series(y_list): - """ - Fixture. - """ - return pd.Series(y_list) - - -@pytest.fixture(scope="function") -def iteration_results(): - """ - Fixture. - """ - iterations_cols = ["metric_name", "train_score", "test_score", "delta_score"] - return pd.DataFrame( - [ - ["roc_auc", 0.8, 0.7, 0.1], - ["roc_auc", 0.7, 0.6, 0.1], - ["roc_auc", 0.9, 0.8, 0.1], - ["accuracy", 1, 0.9, 0.1], - ["accuracy", 0.8, 0.7, 0.1], - ["accuracy", 0.9, 0.8, 0.1], - ], - columns=iterations_cols, - ) - - -@pytest.fixture(scope="function") -def report(): - """ - Fixture. - """ - report_cols = ["train_mean", "train_std", "test_mean", "test_std", "delta_mean", "delta_std"] - report_index = ["roc_auc", "accuracy"] - return pd.DataFrame( - [[0.8, 0.08164, 0.7, 0.08164, 0.1, 0], [0.9, 0.08164, 0.8, 0.08164, 0.1, 0]], - columns=report_cols, - index=report_index, - ).astype(float) - - -@pytest.fixture(scope="function") -def iterations_train(): - """ - Fixture. - """ - return pd.Series([0.8, 0.7, 0.9], name="train_score") - - -@pytest.fixture(scope="function") -def iterations_test(): - """ - Fixture. - """ - return pd.Series([0.7, 0.6, 0.8], name="test_score") - - -@pytest.fixture(scope="function") -def iterations_delta(): - """ - Fixture. - """ - return pd.Series([0.1, 0.1, 0.1], name="delta_score") - - -def test_inits(mock_model): - """ - Test. - """ - vol1 = SplitSeedVolatility( - mock_model, - scoring=["accuracy", "roc_auc"], - test_prc=0.3, - n_jobs=2, - stats_tests_to_apply=["ES", "KS"], - random_state=1, - iterations=20, - ) - - assert id(vol1.clf) == id(mock_model) - assert vol1.test_prc == 0.3 - assert vol1.n_jobs == 2 - assert vol1.stats_tests_to_apply == ["ES", "KS"] - assert vol1.random_state == 1 - assert vol1.iterations == 20 - assert len(vol1.stats_tests_objects) == 2 - assert len(vol1.scorers) == 2 - assert vol1.sample_train_test_split_seed is True - - vol2 = BootstrappedVolatility(mock_model, scoring="roc_auc", stats_tests_to_apply="KS", test_sampling_fraction=0.8) - - assert id(vol2.clf) == id(mock_model) - assert vol2.stats_tests_to_apply == ["KS"] - assert len(vol2.stats_tests_objects) == 1 - assert len(vol2.scorers) == 1 - assert vol2.sample_train_test_split_seed is False - assert vol2.test_sampling_fraction == 0.8 - assert vol2.fitted is False - assert vol2.iterations_results is None - assert vol2.report is None - - -def test_base_fit(mock_model, X_df, y_series): - """ - Test. - """ - vol = BaseVolatilityEstimator(mock_model, random_state=1) - - with patch("numpy.random.seed") as mock_seed: - vol.fit(X_df, y_series) - mock_seed.assert_called_with(1) - - assert vol.iterations_results is None - assert vol.report is None - assert vol.fitted is True - - -def test_compute(report, mock_model): - """ - Test. - """ - vol = BaseVolatilityEstimator(mock_model) - - with pytest.raises(NotFittedError): - vol.compute() - - vol.fit() - with pytest.raises(ValueError): - vol.compute() - - vol.report = report - - pd.testing.assert_frame_equal(vol.compute(), report) - pd.testing.assert_frame_equal(vol.compute(metrics=["roc_auc"]), report.loc[["roc_auc"]]) - pd.testing.assert_frame_equal(vol.compute(metrics="roc_auc"), report.loc[["roc_auc"]]) - - -def test_plot(report, mock_model, iterations_train, iterations_test, iterations_delta): - """ - Test. - """ - with patch.object(BaseVolatilityEstimator, "compute", return_value=report.loc[["roc_auc"]]) as mock_compute: - with patch.object( - BaseVolatilityEstimator, - "_get_samples_to_plot", - return_value=(iterations_train, iterations_test, iterations_delta), - ) as mock_get_samples: - vol = BaseVolatilityEstimator(mock_model) - vol.fitted = True - - vol.plot(metrics="roc_auc") - mock_compute.assert_called_with(metrics="roc_auc") - mock_get_samples.assert_called_with(metric_name="roc_auc") - - -def test_get_samples_to_plot(mock_model, iteration_results, iterations_train, iterations_test, iterations_delta): - """ - Test. - """ - vol = BaseVolatilityEstimator(mock_model) - vol.fitted = True - vol.iterations_results = iteration_results - - train, test, delta = vol._get_samples_to_plot(metric_name="roc_auc") - pd.testing.assert_series_equal(train, iterations_train) - pd.testing.assert_series_equal(test, iterations_test) - pd.testing.assert_series_equal(delta, iterations_delta) - - -def test_create_report(mock_model, iteration_results, report): - """ - Test. - """ - vol = BaseVolatilityEstimator(mock_model) - vol.fitted = True - vol.iterations_results = iteration_results - - vol._create_report() - pd.testing.assert_frame_equal(vol.report, report, atol=1e-3) - - -def test_compute_mean_std_from_runs(mock_model, iteration_results): - """ - Test. - """ - vol = BaseVolatilityEstimator(mock_model) - results = vol._compute_mean_std_from_runs(iteration_results[iteration_results["metric_name"] == "roc_auc"]) - expected_results = [0.8, 0.08164, 0.7, 0.08164, 0.1, 0] - for idx, item in enumerate(results): - assert pytest.approx(item, 0.01) == expected_results[idx] - - -def test_compute_stats_tests_values(mock_model, iteration_results): - """ - Test. - """ - vol = BaseVolatilityEstimator(mock_model, stats_tests_to_apply=["KS"]) - - with patch.object(DistributionStatistics, "compute", return_value=(0.1, 0.05)): - stats = vol._compute_stats_tests_values(iteration_results) - - assert stats[0] == 0.1 - assert stats[1] == 0.05 - - -def test_fit_compute(mock_model, report, X_df, y_series): - """ - Test. - """ - vol = BaseVolatilityEstimator(mock_model) - - with patch.object(BaseVolatilityEstimator, "fit") as mock_fit: - with patch.object(BaseVolatilityEstimator, "compute", return_value=report) as mock_compute: - result = vol.fit_compute(X_df, y_series) - - mock_fit.assert_called_with(X_df, y_series) - mock_compute.assert_called_with() - - pd.testing.assert_frame_equal(result, report) - - -def test_fit_train_test_sample_seed(mock_model, X_df, y_series, iteration_results): - """ - Test. - """ - vol = TrainTestVolatility(mock_model, scoring="roc_auc", iterations=3, sample_train_test_split_seed=True) - - with patch.object(BaseVolatilityEstimator, "fit") as mock_base_fit: - with patch.object(TrainTestVolatility, "_create_report") as mock_create_report: - with patch( - "probatus.metric_volatility.volatility.get_metric", - side_effect=[iteration_results.iloc[[0]], iteration_results.iloc[[1]], iteration_results.iloc[[2]]], - ): - vol.fit(X_df, y_series) - - mock_base_fit.assert_called_once() - mock_create_report.assert_called_once() - - pd.testing.assert_frame_equal(vol.iterations_results, iteration_results.iloc[[0, 1, 2]]) - - -def test_get_metric(mock_model, X_df, y_series): - """ - Test. - """ - split_seed = 1 - test_prc = 0.6 - with patch( - "probatus.metric_volatility.metric.train_test_split", - return_value=(X_df.iloc[[0, 1, 2]], X_df.iloc[[3, 4]], y_series.iloc[[0, 1, 2]], y_series.iloc[[3, 4]]), - ) as mock_split: - with patch( - "probatus.metric_volatility.metric.sample_data", - side_effect=[(X_df.iloc[[0, 1, 1]], y_series.iloc[[0, 1, 1]]), (X_df.iloc[[3, 3]], y_series.iloc[[3, 3]])], - ) as mock_sample: - with patch.object(Scorer, "score", side_effect=[0.8, 0.7]): - output = get_metric( - X_df, - y_series, - mock_model, - test_size=test_prc, - split_seed=split_seed, - scorers=[Scorer("roc_auc")], - train_sampling_type="bootstrap", - test_sampling_type="bootstrap", - train_sampling_fraction=1, - test_sampling_fraction=1, - ) - mock_split.assert_called_once() - mock_sample.assert_called() - mock_model.fit.assert_called() - - expected_output = pd.DataFrame( - [["roc_auc", 0.8, 0.7, 0.1]], columns=["metric_name", "train_score", "test_score", "delta_score"] - ) - pd.testing.assert_frame_equal(expected_output, output) - - -def test_sample_data_no_sampling(X_df, y_series): - """ - Test. - """ - with patch("probatus.metric_volatility.utils.check_sampling_input") as mock_sampling_input: - X_out, y_out = sample_data(X_df, y_series, sampling_type=None, sampling_fraction=1) - mock_sampling_input.assert_called_once() - pd.testing.assert_frame_equal(X_out, X_df) - pd.testing.assert_series_equal(y_out, y_series) - - -def test_sample_data_bootstrap(X_df, y_series): - """ - Test. - """ - with patch("probatus.metric_volatility.utils.check_sampling_input") as mock_sampling_input: - X_out, y_out = sample_data(X_df, y_series, sampling_type="bootstrap", sampling_fraction=0.8) - mock_sampling_input.assert_called_once() - assert X_out.shape == (4, 2) - assert y_out.shape == (4,) - - -def test_sample_data_sample(X_df, y_series): - """ - Test. - """ - with patch("probatus.metric_volatility.utils.check_sampling_input") as mock_sampling_input: - X_out, y_out = sample_data(X_df, y_series, sampling_type="subsample", sampling_fraction=1) - mock_sampling_input.assert_called_once() - pd.testing.assert_frame_equal(X_out, X_df) - pd.testing.assert_series_equal(y_out, y_series) - - -def test_check_sampling_input(X_array, y_array): - """ - Test. - """ - with pytest.raises(ValueError): - check_sampling_input("bootstrap", 0, "dataset") - with pytest.raises(ValueError): - check_sampling_input("subsample", 0, "dataset") - with pytest.raises(ValueError): - check_sampling_input("subsample", 1, "dataset") - with pytest.raises(ValueError): - check_sampling_input("subsample", 10, "dataset") - with pytest.raises(ValueError): - check_sampling_input("wrong_name", 0.5, "dataset") - - -def test_fit_compute_full_process(X_df, y_series): - """ - Test. - """ - clf = DecisionTreeClassifier() - vol = TrainTestVolatility( - clf, scoring=["roc_auc", "recall"], iterations=3, sample_train_test_split_seed=False, random_state=42 - ) - - report = vol.fit_compute(X_df, y_series) - assert report.shape == (2, 6) - - # Check if plot runs - vol.plot(show=False) - - -@pytest.mark.skipif(os.environ.get("SKIP_LIGHTGBM") == "true", reason="LightGBM tests disabled") -def test_fit_compute_complex(complex_data, complex_lightgbm): - """ - Test. - """ - X, y = complex_data - vol = TrainTestVolatility( - complex_lightgbm, - scoring="roc_auc", - iterations=3, - sample_train_test_split_seed=True, - verbose=150, - random_state=42, - ) - - report = vol.fit_compute(X, y) - assert report.shape == (1, 6) - - # Check if plot runs - vol.plot(show=False) diff --git a/tests/missing_values/test_imputation.py b/tests/missing_values/test_imputation.py deleted file mode 100644 index a0300e8a..00000000 --- a/tests/missing_values/test_imputation.py +++ /dev/null @@ -1,107 +0,0 @@ -# Code to test the imputation strategies. -import os - -import numpy as np -import pandas as pd -import pytest -from sklearn.ensemble import RandomForestClassifier -from sklearn.experimental import enable_iterative_imputer # noqa -from sklearn.impute import IterativeImputer, KNNImputer, SimpleImputer -from sklearn.linear_model import LogisticRegression - -from probatus.missing_values.imputation import ImputationSelector - - -@pytest.fixture(scope="function") -def X(): - """ - Fixture. - """ - return pd.DataFrame( - { - "col_1": [1, np.nan, 1, 1, np.nan, 1, 1, 0, 1, 1], - "col_2": [0, 0, 0, np.nan, 0, 0, 0, 1, 0, 0], - "col_3": [1, 0, np.nan, 0, 1, np.nan, 1, 0, 1, 1], - "col_4": ["A", "B", "A", np.nan, "B", np.nan, "C", "A", "B", "C"], - }, - index=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - ) - - -@pytest.fixture(scope="function") -def y(): - """ - Fixture. - """ - return pd.Series([1, 0, 1, 0, 1, 0, 1, 0, 0, 0], index=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) - - -@pytest.fixture(scope="function") -def strategies(): - """ - Test strategies. - """ - return { - "Simple Median Imputer": SimpleImputer(strategy="median", add_indicator=True), - "Simple Mean Imputer": SimpleImputer(strategy="mean", add_indicator=True), - "Iterative Imputer": IterativeImputer(add_indicator=True, n_nearest_features=5, sample_posterior=True), - "KNN": KNNImputer(n_neighbors=3), - } - - -def test_imputation_linear(X, y, strategies, capsys): - """ - Test imputation linear. - """ - # Initialize the classifier - clf = LogisticRegression() - cmp = ImputationSelector(clf=clf, strategies=strategies, cv=3, model_na_support=False) - report = cmp.fit_compute(X, y) - _ = cmp.plot(show=False) - - assert cmp.fitted - cmp._check_if_fitted() - assert report.shape[0] == 4 - - # Check if there is any prints - out, _ = capsys.readouterr() - assert len(out) == 0 - - -def test_imputation_bagging(X, y, strategies, capsys): - """ - Test bagging. - """ - # Initialize the classifier - clf = RandomForestClassifier() - cmp = ImputationSelector(clf=clf, strategies=strategies, cv=3, model_na_support=False) - report = cmp.fit_compute(X, y) - _ = cmp.plot(show=False) - - assert cmp.fitted - cmp._check_if_fitted() - assert report.shape[0] == 4 - - # Check if there is any prints - out, _ = capsys.readouterr() - assert len(out) == 0 - - -@pytest.mark.skipif(os.environ.get("SKIP_LIGHTGBM") == "true", reason="LightGBM tests disabled") -def test_imputation_boosting(X, y, strategies, complex_lightgbm, capsys): - """ - Test boosting. - """ - # Initialize the classifier - clf = complex_lightgbm - cmp = ImputationSelector(clf=clf, strategies=strategies, cv=3, model_na_support=True) - report = cmp.fit_compute(X, y) - _ = cmp.plot(show=False) - - assert cmp.fitted - cmp._check_if_fitted() - assert report.shape[0] == 5 - - # Check if there is any prints - out, _ = capsys.readouterr() - assert len(out) == 0 diff --git a/tests/stat_tests/__init__.py b/tests/stat_tests/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/stat_tests/test_distribution_statistics.py b/tests/stat_tests/test_distribution_statistics.py deleted file mode 100644 index 86d69bb9..00000000 --- a/tests/stat_tests/test_distribution_statistics.py +++ /dev/null @@ -1,244 +0,0 @@ -import numbers - -import numpy as np -import pandas as pd -import pytest -from sklearn.datasets import make_classification -from sklearn.model_selection import train_test_split - -from probatus.stat_tests import AutoDist, DistributionStatistics, ks, psi - - -def test_distribution_statistics_base(): - """ - Test. - """ - with pytest.raises(NotImplementedError): - assert DistributionStatistics("doesnotexist", "SimpleBucketer", bin_count=10) - with pytest.raises(NotImplementedError): - assert DistributionStatistics("psi", "doesnotexist", bin_count=10) - myTest = DistributionStatistics("psi", "SimpleBucketer", bin_count=10) - assert repr(myTest).startswith("DistributionStatistics") - - -def test_distribution_statistics_psi(): - """ - Test. - """ - d1 = np.histogram(np.random.normal(size=1000), 10)[0] - d2 = np.histogram(np.random.weibull(1, size=1000) - 1, 10)[0] - myTest = DistributionStatistics("psi", "SimpleBucketer", bin_count=10) - assert not myTest.fitted - psi_test, p_value_test = myTest.compute(d1, d2) - assert myTest.fitted - assert isinstance(psi_test, numbers.Number) - - -def test_distribution_statistics_tuple_output(): - """ - Test. - """ - d1 = np.histogram(np.random.normal(size=1000), 10)[0] - d2 = np.histogram(np.random.weibull(1, size=1000) - 1, 10)[0] - myTest = DistributionStatistics("ks", "SimpleBucketer", bin_count=10) - assert not myTest.fitted - res = myTest.compute(d1, d2) - assert myTest.fitted - assert isinstance(res, tuple) - - -def test_distribution_statistics_ks_no_binning(): - """ - Test. - """ - d1 = np.histogram(np.random.normal(size=1000), 10)[0] - d2 = np.histogram(np.random.weibull(1, size=1000) - 1, 10)[0] - myTest = DistributionStatistics("ks", binning_strategy=None) - assert not myTest.fitted - res = myTest.compute(d1, d2) - assert myTest.fitted - assert isinstance(res, tuple) - - -def test_distribution_statistics_attributes_psi(): - """ - Test. - """ - a = np.random.normal(size=1000) - b = np.random.normal(size=1000) - d1 = np.histogram(a, 10)[0] - d2 = np.histogram(b, 10)[0] - myTest = DistributionStatistics("psi", binning_strategy=None) - _ = myTest.compute(d1, d2, verbose=False) - psi_value_test, p_value_test = psi(d1, d2, verbose=False) - assert myTest.statistic == psi_value_test - - -def test_distribution_statistics_attributes_ks(): - """ - Test. - """ - d1 = np.histogram(np.random.normal(size=1000), 10)[0] - d2 = np.histogram(np.random.normal(size=1000), 10)[0] - myTest = DistributionStatistics("ks", binning_strategy=None) - _ = myTest.compute(d1, d2, verbose=False) - ks_value, p_value = ks(d1, d2) - assert myTest.statistic == ks_value - - -def test_distribution_statistics_autodist_base(): - """ - Test. - """ - nr_features = 2 - size = 1000 - np.random.seed(0) - df1 = pd.DataFrame(np.random.normal(size=(size, nr_features)), columns=[f"feat_{x}" for x in range(nr_features)]) - df2 = pd.DataFrame(np.random.normal(size=(size, nr_features)), columns=[f"feat_{x}" for x in range(nr_features)]) - features = df1.columns - myAutoDist = AutoDist(statistical_tests="all", binning_strategies="all", bin_count=[10, 20]) - assert repr(myAutoDist).startswith("AutoDist") - assert not myAutoDist.fitted - res = myAutoDist.compute(df1, df2, column_names=features) - assert myAutoDist.fitted - pd.testing.assert_frame_equal(res, myAutoDist.result) - assert isinstance(res, pd.DataFrame) - assert res["column"].values.tolist() == features.to_list() - - dist = DistributionStatistics(statistical_test="ks", binning_strategy="simplebucketer", bin_count=10) - dist.compute(df1["feat_0"], df2["feat_0"]) - assert dist.p_value == res.loc[res["column"] == "feat_0", "p_value_KS_simplebucketer_10"][0] - assert dist.statistic == res.loc[res["column"] == "feat_0", "statistic_KS_simplebucketer_10"][0] - - dist = DistributionStatistics(statistical_test="ks", binning_strategy=None, bin_count=10) - dist.compute(df1["feat_0"], df2["feat_0"]) - assert dist.p_value == res.loc[res["column"] == "feat_0", "p_value_KS_no_bucketing_0"][0] - assert dist.statistic == res.loc[res["column"] == "feat_0", "statistic_KS_no_bucketing_0"][0] - - -def test_distribution_statistics_autodist_column_names_error(): - """ - Test. - """ - df1 = pd.DataFrame({"feat_0": [1, 2, 3, 4, 5], "feat_1": [5, 6, 7, 8, 9]}) - df2 = df1 - features = df1.columns.values.tolist() + ["missing_feature"] - myAutoDist = AutoDist() - with pytest.raises(Exception): - assert myAutoDist.compute(df1, df2, column_names=features) - - df1 = pd.DataFrame({"feat_0": [1, 2, 3, 4, 5], "feat_1": [5, 6, 7, 8, 9]}) - df2 = df1.copy() - df1["feat_2"] = 0 - features = df2.columns.values.tolist() + ["missing_feature"] - myAutoDist = AutoDist() - with pytest.raises(Exception): - assert myAutoDist.compute(df1, df2, column_names=features) - - -@pytest.mark.skip(reason="Currently fails on ubuntu, to be investigated further.") -def test_distribution_statistics_autodist_return_failed_tests(): - """ - Test. - """ - df1 = pd.DataFrame({"feat_0": [1, 2, 3, 4, 5], "feat_1": [5, 6, 7, 8, 9]}) - df2 = df1 - features = df1.columns.values.tolist() - myAutoDist = AutoDist(binning_strategies="all") - res = myAutoDist.compute(df1, df2, column_names=features, return_failed_tests=True) - assert res.isin(["an error occurred"]).any().any() - res = myAutoDist.compute(df1, df2, column_names=features, return_failed_tests=False) - assert not res.isin(["an error occurred"]).any().any() - - -def test_distribution_statistics_autodist_default(): - """ - Test. - """ - df1 = pd.DataFrame({"feat_0": [1, 2, 3, 4, 5], "feat_1": [5, 6, 7, 8, 9]}) - df2 = df1 - features = df1.columns.values.tolist() - myAutoDist = AutoDist(binning_strategies="default", bin_count=10) - res = myAutoDist.compute(df1, df2, column_names=features) - for stat_test, stat_info in DistributionStatistics.statistical_test_dict.items(): - if stat_info["default_binning"]: - assert f"p_value_{stat_test}_{stat_info['default_binning']}_10" in res.columns - else: - assert f"p_value_{stat_test}_no_bucketing_0" in res.columns - - assert "p_value_agglomerativebucketer_10" not in res.columns - assert res.shape == (len(df1.columns), 1 + 2 * len(DistributionStatistics.statistical_test_dict)) - - -def test_distribution_statistics_autodist_init(): - """ - Test. - """ - myAutoDist = AutoDist(statistical_tests="all", binning_strategies="all") - assert isinstance(myAutoDist.statistical_tests, list) - myAutoDist = AutoDist(statistical_tests="ks", binning_strategies="all") - assert myAutoDist.statistical_tests == ["ks"] - myAutoDist = AutoDist(statistical_tests=["ks", "psi"], binning_strategies="all") - assert myAutoDist.statistical_tests == ["ks", "psi"] - - myAutoDist = AutoDist(statistical_tests="all", binning_strategies="all") - assert isinstance(myAutoDist.binning_strategies, list) - myAutoDist = AutoDist(statistical_tests="all", binning_strategies="quantilebucketer") - assert myAutoDist.binning_strategies == ["quantilebucketer"] - myAutoDist = AutoDist(statistical_tests="all", binning_strategies=["quantilebucketer", "simplebucketer"]) - assert myAutoDist.binning_strategies == ["quantilebucketer", "simplebucketer"] - - -def test_missing_values_in_autodist(): - """Test missing values have no impact in AutoDist functionality.""" - # Create dummy dataframe - X, y = make_classification(50, 5, random_state=0) - X = pd.DataFrame(X) - # Split train and test - X_train, X_test, _, _ = train_test_split(X, y, test_size=0.2, random_state=1) - # Define an add-on with only missing values - X_na = pd.DataFrame(np.tile(np.nan, (X.shape[1], X.shape[1]))) - - # Compute the statistics with the missing values - with_missings = AutoDist( - statistical_tests=["PSI", "KS"], binning_strategies="SimpleBucketer", bin_count=10 - ).compute(pd.concat([X_train, X_na]), pd.concat([X_test, X_na])) - - # Compute the statistics withpout the missing values - no_missing = AutoDist(statistical_tests=["PSI", "KS"], binning_strategies="SimpleBucketer", bin_count=10).compute( - X_train, X_test - ) - - # Test the two set of results are identical - pd.testing.assert_frame_equal(with_missings, no_missing) - - -def test_warnings_are_issued_for_missing(): - """Test if warnings are issued when missing values are present in the input of autodist.""" - # Generate an input dataframe without missing values - X = pd.DataFrame({"A": [number for number in range(0, 50)]}) - X = X.assign(B=X["A"], C=X["A"], D=X["A"], E=X["A"]) - - # Add some missing values to the dataframe. - X_na = X.copy() - X_na.iloc[X.sample(5, random_state=1).index, 1:3] = np.nan - - # Test missing value removal on the first data input. - with pytest.warns(None) as record_first: - _ = AutoDist(statistical_tests=["PSI"], binning_strategies="SimpleBucketer", bin_count=10).compute(X_na, X) - assert len(record_first) == 2 - - # Test missing values removal on the second data input - with pytest.warns(None) as record_second: - _ = AutoDist(statistical_tests=["PSI"], binning_strategies="SimpleBucketer", bin_count=10).compute(X, X_na) - assert len(record_second) == 2 - - # Test the missing values removal on the first and second data input - with pytest.warns(None) as record_both: - _ = AutoDist(statistical_tests=["PSI"], binning_strategies="SimpleBucketer", bin_count=10).compute(X_na, X_na) - assert len(record_both) == 2 - - # Test case where there are no missing values - with pytest.warns(None) as record_both: - _ = AutoDist(statistical_tests=["PSI"], binning_strategies="SimpleBucketer", bin_count=10).compute(X, X) - assert len(record_both) == 0 diff --git a/tests/stat_tests/test_stat_tests.py b/tests/stat_tests/test_stat_tests.py deleted file mode 100644 index a62f1a1c..00000000 --- a/tests/stat_tests/test_stat_tests.py +++ /dev/null @@ -1,100 +0,0 @@ -import numpy as np -import pandas as pd - -from probatus.binning import binning -from probatus.stat_tests import ad, es, ks, psi, sw - - -def test_psi_returns_zero(): - """ - Test. - """ - x = np.random.normal(size=1000) - myBucketer = binning.QuantileBucketer(bin_count=10) - myBucketer.fit(x) - d1 = myBucketer.counts_ - d2 = d1 - psi_test, p_value_test = psi(d1, d2, verbose=False) - assert psi_test == 0.0 - - -def test_psi_returns_large(): - """ - Test. - """ - d1 = np.histogram(np.random.normal(size=1000), 10)[0] - d2 = np.histogram(np.random.weibull(1, size=1000) - 1, 10)[0] - psi_test, p_value_test = psi(d1, d2, verbose=False) - assert psi_test > 1.0 - - -def test_ks_returns_one(): - """ - Test. - """ - d1 = np.random.normal(size=1000) - d2 = d1 - assert ks(d1, d2)[1] == 1.0 - - -def test_ks_accepts_pd_series(): - """ - Test. - """ - d1 = pd.Series(np.random.normal(size=1000)) - d2 = d1 - assert ks(d1, d2)[1] == 1.0 - - -def test_ks_returns_small(): - """ - Test. - """ - d1 = np.random.normal(size=1000) - d2 = np.random.weibull(1, size=1000) - 1 - assert ks(d1, d2)[1] < 0.001 - - -def test_es_returns_one(): - """ - Test. - """ - d1 = np.random.normal(size=1000) - d2 = d1 - assert es(d1, d2)[1] == 1.0 - - -def test_es_returns_small(): - """ - Test. - """ - d1 = np.random.normal(size=1000) - d2 = np.random.weibull(1, size=1000) - 1 - assert es(d1, d2)[1] < 0.001 - - -def test_ad_returns_big(): - """ - Test. - """ - d1 = np.random.normal(size=1000) - d2 = d1 - assert ad(d1, d2)[1] >= 0.25 - - -def test_ad_returns_small(): - """ - Test. - """ - d1 = np.random.normal(size=1000) - d2 = np.random.weibull(1, size=1000) - 1 - assert ad(d1, d2)[1] <= 0.001 - - -def test_sw_returns_zero(): - """ - Test. - """ - d1 = np.random.normal(size=1000) - d2 = d1 - assert sw(d1, d2)[0] == 0 diff --git a/tests/stat_tests/test_utils.py b/tests/stat_tests/test_utils.py deleted file mode 100644 index 58e8d1b8..00000000 --- a/tests/stat_tests/test_utils.py +++ /dev/null @@ -1,34 +0,0 @@ -import numpy as np - -from probatus.stat_tests import es, ks - - -def test_verbosity_true_(capsys): - """ - Test. - """ - d1 = np.random.normal(size=1000) - d2 = d1 - ks(d1, d2, verbose=True) - captured = capsys.readouterr() - assert ( - captured.out - == "\nKS: pvalue = 1.0\n\nKS: Null hypothesis cannot be rejected. Distributions not statistically different.\n" - ) - es(d1, d2, verbose=True) - captured = capsys.readouterr() - assert ( - captured.out - == "\nES: pvalue = 1.0\n\nES: Null hypothesis cannot be rejected. Distributions not statistically different.\n" - ) - - -def test_verbosity_false(capsys): - """ - Test. - """ - d1 = np.random.normal(size=1000) - d2 = d1 - ks(d1, d2, verbose=False) - captured = capsys.readouterr() - assert captured.out == ""