From 78ea27b73150858962d2be1b053207f08e01603f Mon Sep 17 00:00:00 2001 From: SIKAI ZHANG <34108862+MatthewSZhang@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:05:54 +0800 Subject: [PATCH] CI doctest after make doc --- .github/workflows/ci.yml | 3 +- doc/auto_examples/auto_examples_jupyter.zip | Bin 0 -> 22527 bytes doc/auto_examples/auto_examples_python.zip | Bin 0 -> 16670 bytes .../images/sphx_glr_plot_affinity_001.png | Bin 0 -> 15451 bytes .../images/sphx_glr_plot_redundancy_001.png | Bin 0 -> 24229 bytes .../images/sphx_glr_plot_speed_001.png | Bin 0 -> 35255 bytes .../thumb/sphx_glr_plot_affinity_thumb.png | Bin 0 -> 13090 bytes .../thumb/sphx_glr_plot_redundancy_thumb.png | Bin 0 -> 15544 bytes .../thumb/sphx_glr_plot_speed_thumb.png | Bin 0 -> 22366 bytes doc/auto_examples/index.rst | 100 + doc/auto_examples/plot_affinity.codeobj.json | 1001 +++++++++ doc/auto_examples/plot_affinity.ipynb | 97 + doc/auto_examples/plot_affinity.py | 105 + doc/auto_examples/plot_affinity.py.md5 | 1 + doc/auto_examples/plot_affinity.rst | 218 ++ doc/auto_examples/plot_affinity.zip | Bin 0 -> 7782 bytes .../plot_redundancy.codeobj.json | 1925 +++++++++++++++++ doc/auto_examples/plot_redundancy.ipynb | 133 ++ doc/auto_examples/plot_redundancy.py | 213 ++ doc/auto_examples/plot_redundancy.py.md5 | 1 + doc/auto_examples/plot_redundancy.rst | 343 +++ doc/auto_examples/plot_redundancy.zip | Bin 0 -> 16231 bytes doc/auto_examples/plot_speed.codeobj.json | 558 +++++ doc/auto_examples/plot_speed.ipynb | 133 ++ doc/auto_examples/plot_speed.py | 199 ++ doc/auto_examples/plot_speed.py.md5 | 1 + doc/auto_examples/plot_speed.rst | 345 +++ doc/auto_examples/plot_speed.zip | Bin 0 -> 15206 bytes doc/auto_examples/sg_execution_times.rst | 43 + doc/sg_execution_times.rst | 43 + 30 files changed, 5461 insertions(+), 1 deletion(-) create mode 100644 doc/auto_examples/auto_examples_jupyter.zip create mode 100644 doc/auto_examples/auto_examples_python.zip create mode 100644 doc/auto_examples/images/sphx_glr_plot_affinity_001.png create mode 100644 doc/auto_examples/images/sphx_glr_plot_redundancy_001.png create mode 100644 doc/auto_examples/images/sphx_glr_plot_speed_001.png create mode 100644 doc/auto_examples/images/thumb/sphx_glr_plot_affinity_thumb.png create mode 100644 doc/auto_examples/images/thumb/sphx_glr_plot_redundancy_thumb.png create mode 100644 doc/auto_examples/images/thumb/sphx_glr_plot_speed_thumb.png create mode 100644 doc/auto_examples/index.rst create mode 100644 doc/auto_examples/plot_affinity.codeobj.json create mode 100644 doc/auto_examples/plot_affinity.ipynb create mode 100644 doc/auto_examples/plot_affinity.py create mode 100644 doc/auto_examples/plot_affinity.py.md5 create mode 100644 doc/auto_examples/plot_affinity.rst create mode 100644 doc/auto_examples/plot_affinity.zip create mode 100644 doc/auto_examples/plot_redundancy.codeobj.json create mode 100644 doc/auto_examples/plot_redundancy.ipynb create mode 100644 doc/auto_examples/plot_redundancy.py create mode 100644 doc/auto_examples/plot_redundancy.py.md5 create mode 100644 doc/auto_examples/plot_redundancy.rst create mode 100644 doc/auto_examples/plot_redundancy.zip create mode 100644 doc/auto_examples/plot_speed.codeobj.json create mode 100644 doc/auto_examples/plot_speed.ipynb create mode 100644 doc/auto_examples/plot_speed.py create mode 100644 doc/auto_examples/plot_speed.py.md5 create mode 100644 doc/auto_examples/plot_speed.rst create mode 100644 doc/auto_examples/plot_speed.zip create mode 100644 doc/auto_examples/sg_execution_times.rst create mode 100644 doc/sg_execution_times.rst diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91d7910..e0da8a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,7 @@ jobs: - name: Test with doctest shell: bash run: | + pixi run doc CMD=doctest pixi run doc - name: Test coverage shell: bash @@ -69,7 +70,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Build wheels - uses: pypa/cibuildwheel@v2.21.0 + uses: pypa/cibuildwheel@v2.21.3 env: CIBW_BUILD: cp3*-* CIBW_SKIP: pp* *i686* *musllinux* *-macosx_universal2 *-manylinux_ppc64le *-manylinux_s390x diff --git a/doc/auto_examples/auto_examples_jupyter.zip b/doc/auto_examples/auto_examples_jupyter.zip new file mode 100644 index 0000000000000000000000000000000000000000..7708a44a96f440fe4748dff59094100b39c25e61 GIT binary patch literal 22527 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 L@c+l(^3(qT)Jyev literal 0 HcmV?d00001 diff --git a/doc/auto_examples/auto_examples_python.zip b/doc/auto_examples/auto_examples_python.zip new file mode 100644 index 0000000000000000000000000000000000000000..965e21f674f1f62c99807d3f7db7466460ee3e85 GIT binary patch literal 16670 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 Dr-&zF literal 0 HcmV?d00001 diff --git a/doc/auto_examples/images/sphx_glr_plot_affinity_001.png b/doc/auto_examples/images/sphx_glr_plot_affinity_001.png new file mode 100644 index 0000000000000000000000000000000000000000..0019cd947fbfdaf98586c1ad643582722d84572c GIT binary patch literal 15451 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 NkDfk~bm;7r{|7*7Op*Wq literal 0 HcmV?d00001 diff --git a/doc/auto_examples/images/sphx_glr_plot_redundancy_001.png b/doc/auto_examples/images/sphx_glr_plot_redundancy_001.png new file mode 100644 index 0000000000000000000000000000000000000000..8c5904c8b63dd02e82b139898fb4fe677efd3c46 GIT binary patch literal 24229 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 z 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 G{Qm&z5?|&3 literal 0 HcmV?d00001 diff --git a/doc/auto_examples/images/sphx_glr_plot_speed_001.png b/doc/auto_examples/images/sphx_glr_plot_speed_001.png new file mode 100644 index 0000000000000000000000000000000000000000..ca28f467919c9dd22d7478d5349a1ed623540bfd GIT binary patch literal 35255 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

uvley`~ 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]+gm>3cq@V`vBO|+r^kih($%JE2qBqL( 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 NJso}RY)zX>{|$X)A!Psn literal 0 HcmV?d00001 diff --git a/doc/auto_examples/images/thumb/sphx_glr_plot_speed_thumb.png b/doc/auto_examples/images/thumb/sphx_glr_plot_speed_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..3f72aae87743f84e1f521a8aaa9492df8f1ad260 GIT binary patch literal 22366 zcmcG0WmHvd)GeZdAV^3nr3gq1C>_!uf^>s4(%m5?C=!a4lme2{(j_Gg($d}C4R`JL z`^LCG@2~sAp`LU0IeR~`)?9PV`MiH6Cy9+ohKYiLf-Nm2rig-aEdu_K+(Ls_?6tqS zp`fTek`{Ze?3%DK;o_*gF!Xz7sx$5AhSV);VSE`X-@kk*^fftezv(_sIUW(mC3yVc zfo@ce-_3$+52S2l@K*XfM5IM-QQzqEV88J6N$8*_eia-cJ<_9DEW*xVdWh9FvSf8O zNdG|u?5bBsF?Mq*l|ViaBIqEqQ~CeUh3t`M*;$g zKYudy_4P@~%P)1tadal~5r;I}%Nw+{w?BCJ@Otrxr+kh!T(2&U;Kd>r`V>F= z0vD8&^T$j~M%&Z%v9h%M0s@8VO#2&SF&W9;Uk7v4`mn3^{(>jhBo?E9G4w=ImtpN3~&kKX>jdYn22th6vt^4o8Xa>zE7ctcTo}& z6MM}|=sz@6I4qSruHe_!*8Vc4;|D+1*8@qNeuUTk4tH z+?4B2e`)97&}dL-yZo0L^Y-mGlhr(MG0ObXlEd+~J}N4zt+Vr=`k8P&<3zcZdp6a+ z%?A3zc{(s+FFDSVH^zc=^U>k*#JhBI(@RT| zdy8Gyq~zrOSU!u4kALCqEx0jS@{o@&ey$~4sm|LAMmn&~=)tpRMDp_Tv$M0AN*QTwkMBCzoo^f{ zaqlHk^>=2Q69m1!!i#q)DCC@-pUwRVO_h$Mm5pWdeG$VlGdtU0n(XyQP0uSZBZIDo ze-BGsT>J(a+V0s_T|bQ2nt`L8-L>Bn6XY;#un2os9M{Ln!r^HPO}fZxk7sX}+Dyr1 z$R%=n9<#xiA+x*E_XuWJqt=s0PEJlXf%{Wh8jZHLb}XL@OS0D?>GS8$ONU3RTv*-Q z+^R26*Gh*sMvCcBc6N3?l5)Iz`xXV(rp(di1gVfWe@6`K(wI%%54cq^Ny+|i&##Y9 zOi0VgEv^pcFzHs4u(Gng!y*-_aX;AJ+k42%`xVw?Uy<<(A{K2aZSDIfCnvCXYA+78 z_T2><-(f#|@W2nIa%N$nx!i6}z0&b@rgd2CEOe@{oUD#3Hti{TU5`3GKJL2HOsr9ANs3LzNkvB&6nc+K z5h74elE9NVS+3AQ^O6*LOx2vhp~SiQ`5SLXEYo2dbS4SJdz~HjX2?^9-RBX3xmoGY zz-b91hbh9nd$-YL$}6b2n4?GNia$p+cea^W?^n&Akz&&|I%dU zO$)@GEVRc3vk+ZF}ZAw(dzt(v3lg9GE=Z?E0w zu@8_-h!g^K6WXMOT1Oi4{8MBcP|Z0An+lT4c*xFkCbsaaSGcgwsCamgk3fv*Hok9Kd=@U0M_w@9%Fa$gZO4P6+f`e~)dU-)e zKR!8u_w8De^D~cOQR7!RKf0>(|JB{y{ZqZrZ+6xI*;T5lMB##`^r|_8xvDSB%^8S7 zZYIh}YHE@ltPN+tuQ6#91;EIq%O`hNyX|Gdf`-+)7;jViJhk^9S|Bl#y0EP5$BhF> zh%y9M$#bd%&Ci@2Hax@?wzs#b;C8m^t|(A)RXrNdtYC@29$18xSo#DLCABxWs)`SC zE(+{v7+VM!6)Y>d9$i8$_RO{8cJDVmZ5_8n@i)?2^ypLzl&4=^4wr3ji zEqvdU49n+n#>o!kYQ+#Ts@g)}rj<)*Ei&#v1{p@FNMLh!p~G>dZ=teBIJoWshS(RW z=g+UjbJ@@e3Q~kLi@?%Qp(8{NcM#P7WhFGZ!xO zr@1-sp=1~vjDM;1M0*^E=`C#RALZpFI5;@&hwE((fw$v$9BAR&AOVM2PgM36o60YC z#kas_f!T*u{#KIGYd_silPP*l6CK9dzaAx%?%0A(H)qg&Vh#)KdUJC#|NdW5&x1j= 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 + +.. thumbnail-parent-div-open + +.. raw:: html + +

+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/images/thumb/sphx_glr_plot_speed_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_plot_speed.py` + +.. raw:: html + +
Computational speed comparison
+
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/images/thumb/sphx_glr_plot_redundancy_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_plot_redundancy.py` + +.. raw:: html + +
Feature selection performance on redundant features
+
+ + +.. thumbnail-parent-div-close + +.. raw:: html + + + + +.. toctree:: + :hidden: + + /auto_examples/plot_affinity + /auto_examples/plot_speed + /auto_examples/plot_redundancy + + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-gallery + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download all examples in Python source code: auto_examples_python.zip ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download all examples in Jupyter notebooks: auto_examples_jupyter.zip ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/auto_examples/plot_affinity.codeobj.json b/doc/auto_examples/plot_affinity.codeobj.json new file mode 100644 index 0000000..cde0fa0 --- /dev/null +++ b/doc/auto_examples/plot_affinity.codeobj.json @@ -0,0 +1,1001 @@ +{ + "FastCan": [ + { + "is_class": true, + "is_explicit": false, + "module": "fastcan._fastcan", + "module_short": "fastcan", + "name": "FastCan" + }, + { + "is_class": true, + "is_explicit": false, + "module": "fastcan", + "module_short": "fastcan", + "name": "FastCan" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection._base", + "module_short": "sklearn.feature_selection", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "TransformerMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "TransformerMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._estimator_html_repr", + "module_short": "sklearn.utils._estimator_html_repr", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._metadata_requests", + "module_short": "sklearn.utils._metadata_requests", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._set_output", + "module_short": "sklearn.utils._set_output", + "name": "_SetOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_SetOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_SetOutputMixin" + }, + { + "is_class": false, + "is_explicit": false, + "module": "fastcan", + "module_short": "fastcan", + "name": "FastCan" + }, + { + "is_class": false, + "is_explicit": true, + "module": "builtins", + "module_short": "builtins", + "name": "FastCan" + }, + { + "is_class": false, + "is_explicit": true, + "module": "builtins", + "module_short": "builtins", + "name": "FastCan" + } + ], + "OrthogonalMatchingPursuit": [ + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.linear_model._omp", + "module_short": "sklearn.linear_model", + "name": "OrthogonalMatchingPursuit" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.linear_model", + "module_short": "sklearn.linear_model", + "name": "OrthogonalMatchingPursuit" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "OrthogonalMatchingPursuit" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "MultiOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "MultiOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "RegressorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "RegressorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.linear_model._base", + "module_short": "sklearn.linear_model._base", + "name": "LinearModel" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.linear_model", + "module_short": "sklearn.linear_model", + "name": "LinearModel" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "LinearModel" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._estimator_html_repr", + "module_short": "sklearn.utils._estimator_html_repr", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._metadata_requests", + "module_short": "sklearn.utils._metadata_requests", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_MetadataRequester" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.linear_model", + "module_short": "sklearn.linear_model", + "name": "OrthogonalMatchingPursuit" + } + ], + "X": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ], + "X.shape": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "tuple" + } + ], + "X_affine": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ], + "_": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ], + "axs": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ], + "bin_lims": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ], + "counts_fastcan": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ], + "counts_ols": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ], + "counts_omp": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ], + "fastcan_selector": [ + { + "is_class": false, + "is_explicit": false, + "module": "fastcan._fastcan", + "module_short": "fastcan", + "name": "FastCan" + }, + { + "is_class": false, + "is_explicit": false, + "module": "fastcan", + "module_short": "fastcan", + "name": "FastCan" + } + ], + "fastcan_selector.fit": [ + { + "is_class": false, + "is_explicit": false, + "module": "fastcan._fastcan", + "module_short": "fastcan", + "name": "FastCan.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "fastcan", + "module_short": "fastcan", + "name": "FastCan.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.feature_selection._base", + "module_short": "sklearn.feature_selection._base", + "name": "SelectorMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SelectorMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "SelectorMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "BaseEstimator.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "BaseEstimator.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "TransformerMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "TransformerMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils._estimator_html_repr", + "module_short": "sklearn.utils._estimator_html_repr", + "name": "_HTMLDocumentationLinkMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_HTMLDocumentationLinkMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_HTMLDocumentationLinkMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils._metadata_requests", + "module_short": "sklearn.utils._metadata_requests", + "name": "_MetadataRequester.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_MetadataRequester.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_MetadataRequester.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils._set_output", + "module_short": "sklearn.utils._set_output", + "name": "_SetOutputMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_SetOutputMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_SetOutputMixin.fit" + } + ], + "fig": [ + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.figure", + "module_short": "matplotlib.figure", + "name": "Figure" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib", + "module_short": "matplotlib", + "name": "Figure" + } + ], + "i": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "int" + } + ], + "ids_fastcan": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ], + "ids_fastcan.tolist": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "builtin_function_or_method" + } + ], + "ids_fastcan_all": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "list" + } + ], + "ids_ols": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ], + "ids_ols.tolist": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "builtin_function_or_method" + } + ], + "ids_ols_all": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "list" + } + ], + "ids_omp": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ], + "ids_omp.tolist": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "builtin_function_or_method" + } + ], + "ids_omp_all": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "list" + } + ], + "load_diabetes": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "function" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.datasets", + "module_short": "sklearn.datasets", + "name": "load_diabetes" + } + ], + "n_features": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "int" + } + ], + "n_selected": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "int" + } + ], + "np.arange": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "builtin_function_or_method" + }, + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "arange" + } + ], + "np.diag": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "_ArrayFunctionDispatcher" + }, + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "diag" + } + ], + "np.histogram": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "_ArrayFunctionDispatcher" + }, + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "histogram" + } + ], + "np.random.default_rng": [ + { + "is_class": false, + "is_explicit": false, + "module": "_cython_3_0_11", + "module_short": "_cython_3_0_11", + "name": "cython_function_or_method" + }, + { + "is_class": false, + "is_explicit": false, + "module": "numpy.random", + "module_short": "numpy.random", + "name": "default_rng" + } + ], + "np.sort": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "_ArrayFunctionDispatcher" + }, + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "sort" + } + ], + "ols": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "function" + }, + { + "is_class": false, + "is_explicit": false, + "module": "fastcan", + "module_short": "fastcan", + "name": "ols" + } + ], + "omp_selector": [ + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.linear_model._omp", + "module_short": "sklearn.linear_model", + "name": "OrthogonalMatchingPursuit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.linear_model", + "module_short": "sklearn.linear_model", + "name": "OrthogonalMatchingPursuit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "OrthogonalMatchingPursuit" + } + ], + "omp_selector.fit": [ + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.linear_model._omp", + "module_short": "sklearn.linear_model", + "name": "OrthogonalMatchingPursuit.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.linear_model", + "module_short": "sklearn.linear_model", + "name": "OrthogonalMatchingPursuit.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "OrthogonalMatchingPursuit.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "MultiOutputMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "MultiOutputMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "RegressorMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "RegressorMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.linear_model._base", + "module_short": "sklearn.linear_model._base", + "name": "LinearModel.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.linear_model", + "module_short": "sklearn.linear_model", + "name": "LinearModel.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "LinearModel.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "BaseEstimator.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "BaseEstimator.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils._estimator_html_repr", + "module_short": "sklearn.utils._estimator_html_repr", + "name": "_HTMLDocumentationLinkMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_HTMLDocumentationLinkMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_HTMLDocumentationLinkMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils._metadata_requests", + "module_short": "sklearn.utils._metadata_requests", + "name": "_MetadataRequester.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_MetadataRequester.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_MetadataRequester.fit" + } + ], + "plt.show": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "function" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.pyplot", + "module_short": "matplotlib.pyplot", + "name": "show" + } + ], + "plt.subplots": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "function" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.pyplot", + "module_short": "matplotlib.pyplot", + "name": "subplots" + } + ], + "plt.tight_layout": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "function" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.pyplot", + "module_short": "matplotlib.pyplot", + "name": "tight_layout" + } + ], + "rng": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy.random._generator", + "module_short": "numpy.random", + "name": "Generator" + }, + { + "is_class": false, + "is_explicit": false, + "module": "numpy.random", + "module_short": "numpy.random", + "name": "Generator" + }, + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "Generator" + } + ], + "rng.random": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy.random._generator", + "module_short": "numpy.random", + "name": "Generator.random" + }, + { + "is_class": false, + "is_explicit": false, + "module": "numpy.random", + "module_short": "numpy.random", + "name": "Generator.random" + }, + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "Generator.random" + } + ], + "y": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ] +} \ No newline at end of file diff --git a/doc/auto_examples/plot_affinity.ipynb b/doc/auto_examples/plot_affinity.ipynb new file mode 100644 index 0000000..0ce1e13 --- /dev/null +++ b/doc/auto_examples/plot_affinity.ipynb @@ -0,0 +1,97 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Affine Invariance\n\n.. currentmodule:: fastcan\n\nIn this examples, we will compare the robustness of the three feature\nselection methods on affine transformed features.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Sikai Zhang\n# SPDX-License-Identifier: MIT" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Initialize test\nThe three feature selection methods, i.e., OMP, OLS, and :class:`FastCan`,\nwill select three features from the 10 features of `diabetes` dataset. It can be\nseen, the three methods select the same features.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import numpy as np\nfrom sklearn.datasets import load_diabetes\nfrom sklearn.linear_model import OrthogonalMatchingPursuit\n\nfrom fastcan import FastCan, ols\n\nX, y = load_diabetes(return_X_y=True)\n\nn_selected = 3\nomp_selector = OrthogonalMatchingPursuit(n_nonzero_coefs=n_selected)\nfastcan_selector = FastCan(n_features_to_select=n_selected, verbose=0)\n(ids_omp,) = omp_selector.fit(X, y).coef_.nonzero()\nids_ols, _ = ols(X, y, n_selected)\nids_fastcan = fastcan_selector.fit(X, y).indices_\n\nprint(\"Indices of features selected by:\")\nprint(\"OMP: \", np.sort(ids_omp))\nprint(\"OLS: \", np.sort(ids_ols))\nprint(\"FastCan: \", np.sort(ids_fastcan))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Affine transformation\nIn this test, the 10 features of ``diabetes`` dataset will be randomly polluted\nby the affine transformation. The three feature selection methods will select\nthree features from the polluted features. The more stable the result, the better.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "n_features = X.shape[1]\nrng = np.random.default_rng()\n\nids_omp_all = []\nids_ols_all = []\nids_fastcan_all = []\nfor i in range(10):\n X_affine = X @ np.diag(rng.random(n_features)) + rng.random(n_features)\n\n (ids_omp,) = omp_selector.fit(X_affine, y).coef_.nonzero()\n ids_ols, _ = ols(X_affine, y, n_selected)\n ids_fastcan = fastcan_selector.fit(X_affine, y).indices_\n ids_omp_all += ids_omp.tolist()\n ids_ols_all += ids_ols.tolist()\n ids_fastcan_all += ids_fastcan.tolist()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Plot results\nIt can be seen, only :class:`FastCan` has robust results when the feature\nis polluted by the affine transformation.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n\nbin_lims = np.arange(n_features+1)\ncounts_omp, _ = np.histogram(ids_omp_all, bins=bin_lims)\ncounts_ols, _ = np.histogram(ids_ols_all, bins=bin_lims)\ncounts_fastcan, _ = np.histogram(ids_fastcan_all, bins=bin_lims)\n\nfig, axs = plt.subplots(1, 3, figsize=(8, 3))\n\naxs[0].bar(bin_lims[:-1], counts_omp)\naxs[0].set_xticks(bin_lims[:-1])\naxs[0].set_ylim((0, 11))\naxs[0].set_title(\"OMP\")\naxs[0].set_xlabel(\"Feature Index\")\naxs[0].set_ylabel(\"Count of Selected Times\")\n\n\naxs[1].bar(bin_lims[:-1], counts_ols)\naxs[1].set_xticks(bin_lims[:-1])\naxs[1].set_ylim((0, 11))\naxs[1].set_title(\"OLS\")\naxs[1].set_xlabel(\"Feature Index\")\n\naxs[2].bar(bin_lims[:-1], counts_fastcan)\naxs[2].set_xticks(bin_lims[:-1])\naxs[2].set_ylim((0, 11))\naxs[2].set_title(\"FastCan\")\naxs[2].set_xlabel(\"Feature Index\")\n\nplt.tight_layout()\nplt.show()" + ] + } + ], + "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.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/doc/auto_examples/plot_affinity.py b/doc/auto_examples/plot_affinity.py new file mode 100644 index 0000000..694b563 --- /dev/null +++ b/doc/auto_examples/plot_affinity.py @@ -0,0 +1,105 @@ +""" +================= +Affine Invariance +================= + +.. currentmodule:: fastcan + +In this examples, we will compare the robustness of the three feature +selection methods on affine transformed features. +""" + +# Authors: Sikai Zhang +# SPDX-License-Identifier: MIT + +# %% +# Initialize test +# --------------- +# The three feature selection methods, i.e., OMP, OLS, and :class:`FastCan`, +# will select three features from the 10 features of `diabetes` dataset. It can be +# seen, the three methods select the same features. + +import numpy as np +from sklearn.datasets import load_diabetes +from sklearn.linear_model import OrthogonalMatchingPursuit + +from fastcan import FastCan, ols + +X, y = load_diabetes(return_X_y=True) + +n_selected = 3 +omp_selector = OrthogonalMatchingPursuit(n_nonzero_coefs=n_selected) +fastcan_selector = FastCan(n_features_to_select=n_selected, verbose=0) +(ids_omp,) = omp_selector.fit(X, y).coef_.nonzero() +ids_ols, _ = ols(X, y, n_selected) +ids_fastcan = fastcan_selector.fit(X, y).indices_ + +print("Indices of features selected by:") +print("OMP: ", np.sort(ids_omp)) +print("OLS: ", np.sort(ids_ols)) +print("FastCan: ", np.sort(ids_fastcan)) + + + +# %% +# Affine transformation +# --------------------- +# In this test, the 10 features of ``diabetes`` dataset will be randomly polluted +# by the affine transformation. The three feature selection methods will select +# three features from the polluted features. The more stable the result, the better. + + + +n_features = X.shape[1] +rng = np.random.default_rng() + +ids_omp_all = [] +ids_ols_all = [] +ids_fastcan_all = [] +for i in range(10): + X_affine = X @ np.diag(rng.random(n_features)) + rng.random(n_features) + + (ids_omp,) = omp_selector.fit(X_affine, y).coef_.nonzero() + ids_ols, _ = ols(X_affine, y, n_selected) + ids_fastcan = fastcan_selector.fit(X_affine, y).indices_ + ids_omp_all += ids_omp.tolist() + ids_ols_all += ids_ols.tolist() + ids_fastcan_all += ids_fastcan.tolist() + +# %% +# Plot results +# ------------ +# It can be seen, only :class:`FastCan` has robust results when the feature +# is polluted by the affine transformation. + +import matplotlib.pyplot as plt + +bin_lims = np.arange(n_features+1) +counts_omp, _ = np.histogram(ids_omp_all, bins=bin_lims) +counts_ols, _ = np.histogram(ids_ols_all, bins=bin_lims) +counts_fastcan, _ = np.histogram(ids_fastcan_all, bins=bin_lims) + +fig, axs = plt.subplots(1, 3, figsize=(8, 3)) + +axs[0].bar(bin_lims[:-1], counts_omp) +axs[0].set_xticks(bin_lims[:-1]) +axs[0].set_ylim((0, 11)) +axs[0].set_title("OMP") +axs[0].set_xlabel("Feature Index") +axs[0].set_ylabel("Count of Selected Times") + + +axs[1].bar(bin_lims[:-1], counts_ols) +axs[1].set_xticks(bin_lims[:-1]) +axs[1].set_ylim((0, 11)) +axs[1].set_title("OLS") +axs[1].set_xlabel("Feature Index") + +axs[2].bar(bin_lims[:-1], counts_fastcan) +axs[2].set_xticks(bin_lims[:-1]) +axs[2].set_ylim((0, 11)) +axs[2].set_title("FastCan") +axs[2].set_xlabel("Feature Index") + +plt.tight_layout() +plt.show() diff --git a/doc/auto_examples/plot_affinity.py.md5 b/doc/auto_examples/plot_affinity.py.md5 new file mode 100644 index 0000000..53118a3 --- /dev/null +++ b/doc/auto_examples/plot_affinity.py.md5 @@ -0,0 +1 @@ +0a2896c53754a83692e621f654ec4b45 \ No newline at end of file diff --git a/doc/auto_examples/plot_affinity.rst b/doc/auto_examples/plot_affinity.rst new file mode 100644 index 0000000..dd3654f --- /dev/null +++ b/doc/auto_examples/plot_affinity.rst @@ -0,0 +1,218 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/plot_affinity.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code. + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_plot_affinity.py: + + +================= +Affine Invariance +================= + +.. currentmodule:: fastcan + +In this examples, we will compare the robustness of the three feature +selection methods on affine transformed features. + +.. GENERATED FROM PYTHON SOURCE LINES 11-15 + +.. code-block:: Python + + + # Authors: Sikai Zhang + # SPDX-License-Identifier: MIT + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 16-21 + +Initialize test +--------------- +The three feature selection methods, i.e., OMP, OLS, and :class:`FastCan`, +will select three features from the 10 features of `diabetes` dataset. It can be +seen, the three methods select the same features. + +.. GENERATED FROM PYTHON SOURCE LINES 21-44 + +.. code-block:: Python + + + import numpy as np + from sklearn.datasets import load_diabetes + from sklearn.linear_model import OrthogonalMatchingPursuit + + from fastcan import FastCan, ols + + X, y = load_diabetes(return_X_y=True) + + n_selected = 3 + omp_selector = OrthogonalMatchingPursuit(n_nonzero_coefs=n_selected) + fastcan_selector = FastCan(n_features_to_select=n_selected, verbose=0) + (ids_omp,) = omp_selector.fit(X, y).coef_.nonzero() + ids_ols, _ = ols(X, y, n_selected) + ids_fastcan = fastcan_selector.fit(X, y).indices_ + + print("Indices of features selected by:") + print("OMP: ", np.sort(ids_omp)) + print("OLS: ", np.sort(ids_ols)) + print("FastCan: ", np.sort(ids_fastcan)) + + + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Indices of features selected by: + OMP: [2 3 8] + OLS: [2 3 8] + FastCan: [2 3 8] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 45-50 + +Affine transformation +--------------------- +In this test, the 10 features of ``diabetes`` dataset will be randomly polluted +by the affine transformation. The three feature selection methods will select +three features from the polluted features. The more stable the result, the better. + +.. GENERATED FROM PYTHON SOURCE LINES 50-69 + +.. code-block:: Python + + + + + n_features = X.shape[1] + rng = np.random.default_rng() + + ids_omp_all = [] + ids_ols_all = [] + ids_fastcan_all = [] + for i in range(10): + X_affine = X @ np.diag(rng.random(n_features)) + rng.random(n_features) + + (ids_omp,) = omp_selector.fit(X_affine, y).coef_.nonzero() + ids_ols, _ = ols(X_affine, y, n_selected) + ids_fastcan = fastcan_selector.fit(X_affine, y).indices_ + ids_omp_all += ids_omp.tolist() + ids_ols_all += ids_ols.tolist() + ids_fastcan_all += ids_fastcan.tolist() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 70-74 + +Plot results +------------ +It can be seen, only :class:`FastCan` has robust results when the feature +is polluted by the affine transformation. + +.. GENERATED FROM PYTHON SOURCE LINES 74-106 + +.. code-block:: Python + + + import matplotlib.pyplot as plt + + bin_lims = np.arange(n_features+1) + counts_omp, _ = np.histogram(ids_omp_all, bins=bin_lims) + counts_ols, _ = np.histogram(ids_ols_all, bins=bin_lims) + counts_fastcan, _ = np.histogram(ids_fastcan_all, bins=bin_lims) + + fig, axs = plt.subplots(1, 3, figsize=(8, 3)) + + axs[0].bar(bin_lims[:-1], counts_omp) + axs[0].set_xticks(bin_lims[:-1]) + axs[0].set_ylim((0, 11)) + axs[0].set_title("OMP") + axs[0].set_xlabel("Feature Index") + axs[0].set_ylabel("Count of Selected Times") + + + axs[1].bar(bin_lims[:-1], counts_ols) + axs[1].set_xticks(bin_lims[:-1]) + axs[1].set_ylim((0, 11)) + axs[1].set_title("OLS") + axs[1].set_xlabel("Feature Index") + + axs[2].bar(bin_lims[:-1], counts_fastcan) + axs[2].set_xticks(bin_lims[:-1]) + axs[2].set_ylim((0, 11)) + axs[2].set_title("FastCan") + axs[2].set_xlabel("Feature Index") + + plt.tight_layout() + plt.show() + + + +.. image-sg:: /auto_examples/images/sphx_glr_plot_affinity_001.png + :alt: OMP, OLS, FastCan + :srcset: /auto_examples/images/sphx_glr_plot_affinity_001.png + :class: sphx-glr-single-img + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 0.734 seconds) + + +.. _sphx_glr_download_auto_examples_plot_affinity.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: plot_affinity.ipynb ` + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: plot_affinity.py ` + + .. container:: sphx-glr-download sphx-glr-download-zip + + :download:`Download zipped: plot_affinity.zip ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/auto_examples/plot_affinity.zip b/doc/auto_examples/plot_affinity.zip new file mode 100644 index 0000000000000000000000000000000000000000..1779a3d3647638812714c7ef9b17fa78acd94842 GIT binary patch literal 7782 zcmdT}&2A&d5msy`hsni3d^OORg#i*vjYZ2q3<3mfI9NbgEtY`xAX_8MHpwR0P*2Zb zy2lc27=drO#s@!vkA8YO1R1>zTZK_q`8} z@blj5zkK!afByBizyJQ|2>-tiW4Xw5LKh1zxn4*4It;^Ly!i>fKma3W@*7%kDram9 zCWs<3D~p0joo4e=u+fMtsM0ekgWyaOz2b_nJDTQ#sR6lVlKwt7`~YN`SSRj&zMx~_-qcE^95(ch`c&`gUdep2!5P_XE+u7TiAms z4I{_x4~)D4sJ%_aegr%RL;q0Ca6-Qw=cj^UsCyY0K$x0?E1D) zMHWSt8ovxrDkITVZ|9s&8L0P`%&De|>4=a?o(7cVD#&BZ{V#qrq_j zPxGwMM3!m3CR7ob2WEG5BN#1Y8 zY{lhrQWmP@IzC|N%UWMb`uh!~U8 z00PYoWCaZD)SxF5naOWiktMT?E!4P{(+`|pO+ijKSmxqXB|39}#@Ye-h85FHvGGYi z=<&HqK%qe&G-)apEx7KXefyEuOFBt0#YZzH9{HKfHbv3i1*ysXJ4gjLpGOl z2z!+TL0)jFd*PWKz+m!LxEKcSP1mEa?~xF|BND>Cd88mhye0h_aDLGR2vq}|!#W_R zIRN3~vd>kXD!VDBKTNh$(0zk(UuZB@ZNzV;N}VfJve=ZHLIHxD%u=x?c_u`u*&HNJ z*Cuf`%ZyPZdPwFs6B^{T>O>=@{g##gt?)F1;;1y83RfeUDupu*QeLwHk`-gUdTe8I z8L1V`+12nmDC82N1)h~PEt<0hh4l%HLrB?}g1n$miO1yX+6P&Cz&)6H5b7-9giCbT zlJ$ls{ZT;Rb7_wf2C^>6=eR9Ygk=vlbNe@vq~9mc$@ZDR2=kG(BS+j;TT=yf>84A) zqMbD!eP8C(>-$laKz=bMF?#X&*zTO>kSCI{cCy@1jfE&ON7Doj zZ&H|mP%zfa3HVwa0IvLJ5(0bkz#%KB?e^UDlF03f;c;CZ2OtZM?kYa_CPZ~`q*P;X zA@~%!3j7NXkz7E1pK_T9o+=xc)P_(Uzt4yLV3w6q+X88-9Rw0|4?0^GH1%PDo*WPm zrp8`kz0jA*?n3(rb}e+Swl%i1xbbW{0)ho!!kK=DW`UWJDyL|*>J10vbU2hb6D32YPa+`Rqou!7acVoZ9Q149IW@8#wv6 zQgba>FNDGkRlG5NC!lE%z3_!?ub_6$*B*aIYm$ z{LQ|A+HZ*ibS-fc-jeeRr>2*hAXTvBPf>8RBw`kSjKWU z3BXx@IzNgA#L%rD$OB>-IvcOeZ)eTnHxht zL;FB;#pVECP@$Ncs+|hYIWM(&MAwzDZJypvUeN#h*vkRtJ|+yFb?8wnpNut0@mNoU=!NA=u%O?A-R~}0ItCcSFcI?}T z)$ZQVMjXQZ(HRe7{}pT}DQzKIth6;_X=#i%0OrW=-T}nzOMp$3y9S8E9rpkR@9hFk zzUNj0ngy$yr|#RXHCl)*cD1epz^?O)N@Fk9vh8hlS>eFScWdb$x6{-U_}!X1#1>86 zjDG?z?9D)>trxvd+wT1UG+TJXWG)CcN+b|X!zaTgmpAM)ykUi8Lgj3R{N6FraL!c@ z_YR4K_dhTmPS)^ZMv~K-0Sma}E$PzC=4Q5@^Q-Lmsa3|Y+|VyfxLjn-J+Zw=c~NBc zrBGuExU&}tml>!@wHHjm3ls%R9PpBty8$J@)sW(U?1Nd zZ^*AU?u<`onS`x1h{tzgtUy0)qVn}Jg%^k_5pZK*5YB*xCu{w~(`fif^e5*pFvj>S zlv8|zN%5B9Q&hbgNK!5{lzI|;KY96^cRqM`_bXj|)r+5>f54x3329Gs`+|3K_2!Kd juEEdGKl}q;5w};H`=!at-~J8(@4&ym!tZ~6h~NGTg>crV literal 0 HcmV?d00001 diff --git a/doc/auto_examples/plot_redundancy.codeobj.json b/doc/auto_examples/plot_redundancy.codeobj.json new file mode 100644 index 0000000..6b2e60e --- /dev/null +++ b/doc/auto_examples/plot_redundancy.codeobj.json @@ -0,0 +1,1925 @@ +{ + "DEP_INFO_IDS": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "list" + } + ], + "FastCan": [ + { + "is_class": true, + "is_explicit": false, + "module": "fastcan._fastcan", + "module_short": "fastcan", + "name": "FastCan" + }, + { + "is_class": true, + "is_explicit": false, + "module": "fastcan", + "module_short": "fastcan", + "name": "FastCan" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection._base", + "module_short": "sklearn.feature_selection", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "TransformerMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "TransformerMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._estimator_html_repr", + "module_short": "sklearn.utils._estimator_html_repr", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._metadata_requests", + "module_short": "sklearn.utils._metadata_requests", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._set_output", + "module_short": "sklearn.utils._set_output", + "name": "_SetOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_SetOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_SetOutputMixin" + }, + { + "is_class": false, + "is_explicit": false, + "module": "fastcan", + "module_short": "fastcan", + "name": "FastCan" + }, + { + "is_class": false, + "is_explicit": true, + "module": "builtins", + "module_short": "builtins", + "name": "FastCan" + }, + { + "is_class": false, + "is_explicit": true, + "module": "builtins", + "module_short": "builtins", + "name": "FastCan" + }, + { + "is_class": false, + "is_explicit": true, + "module": "builtins", + "module_short": "builtins", + "name": "FastCan" + } + ], + "INDEP_INFO_IDS": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "list" + } + ], + "LinearSVR": [ + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.svm._classes", + "module_short": "sklearn.svm", + "name": "LinearSVR" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.svm", + "module_short": "sklearn.svm", + "name": "LinearSVR" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "LinearSVR" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "RegressorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "RegressorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.linear_model._base", + "module_short": "sklearn.linear_model._base", + "name": "LinearModel" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.linear_model", + "module_short": "sklearn.linear_model", + "name": "LinearModel" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "LinearModel" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._estimator_html_repr", + "module_short": "sklearn.utils._estimator_html_repr", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._metadata_requests", + "module_short": "sklearn.utils._metadata_requests", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_MetadataRequester" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.svm", + "module_short": "sklearn.svm", + "name": "LinearSVR" + } + ], + "N_FEATURES": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "int" + } + ], + "N_REPEATED": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "int" + } + ], + "N_SAMPLES": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "int" + } + ], + "N_SELECTED": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "int" + } + ], + "N_SELECTORS": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "int" + } + ], + "REDUNDANT_IDS": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "list" + } + ], + "RFE": [ + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection._rfe", + "module_short": "sklearn.feature_selection", + "name": "RFE" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "RFE" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "RFE" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._metadata_requests", + "module_short": "sklearn.utils._metadata_requests", + "name": "_RoutingNotSupportedMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_RoutingNotSupportedMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_RoutingNotSupportedMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection._base", + "module_short": "sklearn.feature_selection", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "MetaEstimatorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "MetaEstimatorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "TransformerMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "TransformerMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._estimator_html_repr", + "module_short": "sklearn.utils._estimator_html_repr", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._metadata_requests", + "module_short": "sklearn.utils._metadata_requests", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._set_output", + "module_short": "sklearn.utils._set_output", + "name": "_SetOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_SetOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_SetOutputMixin" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "RFE" + } + ], + "RandomForestRegressor": [ + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.ensemble._forest", + "module_short": "sklearn.ensemble", + "name": "RandomForestRegressor" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.ensemble", + "module_short": "sklearn.ensemble", + "name": "RandomForestRegressor" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "RandomForestRegressor" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.ensemble._forest", + "module_short": "sklearn.ensemble._forest", + "name": "ForestRegressor" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.ensemble", + "module_short": "sklearn.ensemble", + "name": "ForestRegressor" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "ForestRegressor" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "RegressorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "RegressorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.ensemble._forest", + "module_short": "sklearn.ensemble._forest", + "name": "BaseForest" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.ensemble", + "module_short": "sklearn.ensemble", + "name": "BaseForest" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "BaseForest" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "MultiOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "MultiOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.ensemble._base", + "module_short": "sklearn.ensemble", + "name": "BaseEnsemble" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.ensemble", + "module_short": "sklearn.ensemble", + "name": "BaseEnsemble" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "BaseEnsemble" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "MetaEstimatorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "MetaEstimatorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._estimator_html_repr", + "module_short": "sklearn.utils._estimator_html_repr", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._metadata_requests", + "module_short": "sklearn.utils._metadata_requests", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_MetadataRequester" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.ensemble", + "module_short": "sklearn.ensemble", + "name": "RandomForestRegressor" + } + ], + "SelectFromModel": [ + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection._from_model", + "module_short": "sklearn.feature_selection", + "name": "SelectFromModel" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SelectFromModel" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "SelectFromModel" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "MetaEstimatorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "MetaEstimatorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection._base", + "module_short": "sklearn.feature_selection", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "TransformerMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "TransformerMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._estimator_html_repr", + "module_short": "sklearn.utils._estimator_html_repr", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._metadata_requests", + "module_short": "sklearn.utils._metadata_requests", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._set_output", + "module_short": "sklearn.utils._set_output", + "name": "_SetOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_SetOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_SetOutputMixin" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SelectFromModel" + } + ], + "SelectKBest": [ + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection._univariate_selection", + "module_short": "sklearn.feature_selection", + "name": "SelectKBest" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SelectKBest" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "SelectKBest" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection._univariate_selection", + "module_short": "sklearn.feature_selection._univariate_selection", + "name": "_BaseFilter" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "_BaseFilter" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_BaseFilter" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection._base", + "module_short": "sklearn.feature_selection", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "TransformerMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "TransformerMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._estimator_html_repr", + "module_short": "sklearn.utils._estimator_html_repr", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._metadata_requests", + "module_short": "sklearn.utils._metadata_requests", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._set_output", + "module_short": "sklearn.utils._set_output", + "name": "_SetOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_SetOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_SetOutputMixin" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SelectKBest" + } + ], + "SequentialFeatureSelector": [ + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection._sequential", + "module_short": "sklearn.feature_selection", + "name": "SequentialFeatureSelector" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SequentialFeatureSelector" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "SequentialFeatureSelector" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._metadata_requests", + "module_short": "sklearn.utils._metadata_requests", + "name": "_RoutingNotSupportedMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_RoutingNotSupportedMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_RoutingNotSupportedMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection._base", + "module_short": "sklearn.feature_selection", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "SelectorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "MetaEstimatorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "MetaEstimatorMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "BaseEstimator" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "TransformerMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "TransformerMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._estimator_html_repr", + "module_short": "sklearn.utils._estimator_html_repr", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_HTMLDocumentationLinkMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._metadata_requests", + "module_short": "sklearn.utils._metadata_requests", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_MetadataRequester" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils._set_output", + "module_short": "sklearn.utils._set_output", + "name": "_SetOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_SetOutputMixin" + }, + { + "is_class": true, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_SetOutputMixin" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SequentialFeatureSelector" + } + ], + "X": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ], + "ax": [ + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.axes._axes", + "module_short": "matplotlib.axes", + "name": "Axes" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.axes", + "module_short": "matplotlib.axes", + "name": "Axes" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib", + "module_short": "matplotlib", + "name": "Axes" + } + ], + "ax.bar": [ + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.axes._axes", + "module_short": "matplotlib.axes", + "name": "Axes.bar" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.axes", + "module_short": "matplotlib.axes", + "name": "Axes.bar" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib", + "module_short": "matplotlib", + "name": "Axes.bar" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.axes._base", + "module_short": "matplotlib.axes._base", + "name": "_AxesBase.bar" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.axes", + "module_short": "matplotlib.axes", + "name": "_AxesBase.bar" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib", + "module_short": "matplotlib", + "name": "_AxesBase.bar" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.artist", + "module_short": "matplotlib.artist", + "name": "Artist.bar" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib", + "module_short": "matplotlib", + "name": "Artist.bar" + } + ], + "ax.bar_label": [ + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.axes._axes", + "module_short": "matplotlib.axes", + "name": "Axes.bar_label" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.axes", + "module_short": "matplotlib.axes", + "name": "Axes.bar_label" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib", + "module_short": "matplotlib", + "name": "Axes.bar_label" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.axes._base", + "module_short": "matplotlib.axes._base", + "name": "_AxesBase.bar_label" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.axes", + "module_short": "matplotlib.axes", + "name": "_AxesBase.bar_label" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib", + "module_short": "matplotlib", + "name": "_AxesBase.bar_label" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.artist", + "module_short": "matplotlib.artist", + "name": "Artist.bar_label" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib", + "module_short": "matplotlib", + "name": "Artist.bar_label" + } + ], + "f_regression": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "function" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "f_regression" + } + ], + "fig": [ + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.figure", + "module_short": "matplotlib.figure", + "name": "Figure" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib", + "module_short": "matplotlib", + "name": "Figure" + } + ], + "get_n_missed": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "function" + } + ], + "i": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "int" + } + ], + "j": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "int" + } + ], + "lsvr": [ + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.svm._classes", + "module_short": "sklearn.svm", + "name": "LinearSVR" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.svm", + "module_short": "sklearn.svm", + "name": "LinearSVR" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "LinearSVR" + } + ], + "make_redundant": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "function" + } + ], + "mutual_info_regression": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "function" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "mutual_info_regression" + } + ], + "n_missed": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ], + "n_missed.sum": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "builtin_function_or_method" + } + ], + "np.dot": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "_ArrayFunctionDispatcher" + }, + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "dot" + } + ], + "np.inf": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "float" + }, + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "inf" + } + ], + "np.random.default_rng": [ + { + "is_class": false, + "is_explicit": false, + "module": "_cython_3_0_11", + "module_short": "_cython_3_0_11", + "name": "cython_function_or_method" + }, + { + "is_class": false, + "is_explicit": false, + "module": "numpy.random", + "module_short": "numpy.random", + "name": "default_rng" + } + ], + "np.setdiff1d": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "_ArrayFunctionDispatcher" + }, + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "setdiff1d" + } + ], + "np.zeros": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "builtin_function_or_method" + }, + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "zeros" + } + ], + "plt.show": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "function" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.pyplot", + "module_short": "matplotlib.pyplot", + "name": "show" + } + ], + "plt.subplots": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "function" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.pyplot", + "module_short": "matplotlib.pyplot", + "name": "subplots" + } + ], + "plt.title": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "function" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.pyplot", + "module_short": "matplotlib.pyplot", + "name": "title" + } + ], + "plt.xlabel": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "function" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.pyplot", + "module_short": "matplotlib.pyplot", + "name": "xlabel" + } + ], + "plt.ylabel": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "function" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.pyplot", + "module_short": "matplotlib.pyplot", + "name": "ylabel" + } + ], + "rects": [ + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib.container", + "module_short": "matplotlib.container", + "name": "BarContainer" + }, + { + "is_class": false, + "is_explicit": false, + "module": "matplotlib", + "module_short": "matplotlib", + "name": "BarContainer" + } + ], + "result_ids": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ], + "rfr": [ + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.ensemble._forest", + "module_short": "sklearn.ensemble", + "name": "RandomForestRegressor" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.ensemble", + "module_short": "sklearn.ensemble", + "name": "RandomForestRegressor" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "RandomForestRegressor" + } + ], + "selector": [ + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.feature_selection._sequential", + "module_short": "sklearn.feature_selection", + "name": "SequentialFeatureSelector" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SequentialFeatureSelector" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "SequentialFeatureSelector" + } + ], + "selector.fit": [ + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.feature_selection._sequential", + "module_short": "sklearn.feature_selection", + "name": "SequentialFeatureSelector.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SequentialFeatureSelector.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "SequentialFeatureSelector.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils._metadata_requests", + "module_short": "sklearn.utils._metadata_requests", + "name": "_RoutingNotSupportedMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_RoutingNotSupportedMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_RoutingNotSupportedMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.feature_selection._base", + "module_short": "sklearn.feature_selection._base", + "name": "SelectorMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.feature_selection", + "module_short": "sklearn.feature_selection", + "name": "SelectorMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "SelectorMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "MetaEstimatorMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "MetaEstimatorMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "BaseEstimator.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "BaseEstimator.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.base", + "module_short": "sklearn.base", + "name": "TransformerMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "TransformerMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils._estimator_html_repr", + "module_short": "sklearn.utils._estimator_html_repr", + "name": "_HTMLDocumentationLinkMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_HTMLDocumentationLinkMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_HTMLDocumentationLinkMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils._metadata_requests", + "module_short": "sklearn.utils._metadata_requests", + "name": "_MetadataRequester.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_MetadataRequester.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_MetadataRequester.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils._set_output", + "module_short": "sklearn.utils._set_output", + "name": "_SetOutputMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn.utils", + "module_short": "sklearn.utils", + "name": "_SetOutputMixin.fit" + }, + { + "is_class": false, + "is_explicit": false, + "module": "sklearn", + "module_short": "sklearn", + "name": "_SetOutputMixin.fit" + } + ], + "selector_dict": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "dict" + } + ], + "selector_dict.keys": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "builtin_function_or_method" + } + ], + "selector_dict.values": [ + { + "is_class": false, + "is_explicit": false, + "module": "builtins", + "module_short": "builtins", + "name": "builtin_function_or_method" + } + ], + "y": [ + { + "is_class": false, + "is_explicit": false, + "module": "numpy", + "module_short": "numpy", + "name": "ndarray" + } + ] +} \ No newline at end of file diff --git a/doc/auto_examples/plot_redundancy.ipynb b/doc/auto_examples/plot_redundancy.ipynb new file mode 100644 index 0000000..79519cf --- /dev/null +++ b/doc/auto_examples/plot_redundancy.ipynb @@ -0,0 +1,133 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Feature selection performance on redundant features\n\n.. currentmodule:: fastcan\n\nIn this examples, we will compare the performance of feature selectors on the\ndatasets, which contain redundant features.\nHere four types of features should be distinguished:\n\n* Unuseful features: the features do not contribute to the target\n* Dependent informative features: the features contribute to the target and form\n the redundant features\n* Redundant features: the features are constructed by linear transformation of\n dependent informative features\n* Independent informative features: the features contribute to the target but\n does not contribute to the redundant features.\n\n

Note

If we do not distinguish dependent and independent informative features and use\n informative features to form both the target and the redundant features. The task\n will be much easier.

\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Sikai Zhang\n# SPDX-License-Identifier: MIT" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Define dataset generator\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import numpy as np\n\n\ndef make_redundant(\n n_samples,\n n_features,\n dep_info_ids,\n indep_info_ids,\n redundant_ids,\n random_seed,\n):\n \"\"\"Make a dataset with linearly redundant features.\n\n Parameters\n ----------\n n_samples : int\n The number of samples.\n\n n_features : int\n The number of features.\n\n dep_info_ids : list[int]\n The indices of dependent informative features.\n\n indep_info_ids : list[int]\n The indices of independent informative features.\n\n redundant_ids : list[int]\n The indices of redundant features.\n\n random_seed : int\n Random seed.\n\n Returns\n -------\n X : array-like of shape (n_samples, n_features)\n Feature matrix.\n\n y : array-like of shape (n_samples,)\n Target vector.\n \"\"\"\n rng = np.random.default_rng(random_seed)\n info_ids = dep_info_ids + indep_info_ids\n n_dep_info = len(dep_info_ids)\n n_info = len(info_ids)\n n_redundant = len(redundant_ids)\n informative_coef = rng.random(n_info)\n redundant_coef = rng.random((n_dep_info, n_redundant))\n\n X = rng.random((n_samples, n_features))\n y = np.dot(X[:, info_ids], informative_coef)\n\n X[:, redundant_ids] = X[:, dep_info_ids]@redundant_coef\n return X, y" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Define score function\nThis function is used to compute the number of correct features missed by selectors.\n\n* For independent informative features, selectors should select all of them\n* For dependent informative features, selectors only need to select any\n ``n_dep_info``-combination of the set ``dep_info_ids`` + ``redundant_ids``. That\n means if the indices of dependent informative features are $[0, 1]$ and the\n indices of the redundant features are $[5]$, then the correctly selected\n indices can be any of $[0, 1]$, $[0, 5]$, and $[1, 5]$.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def get_n_missed(\n dep_info_ids,\n indep_info_ids,\n redundant_ids,\n selected_ids\n):\n \"\"\"Get the number of features miss selected.\"\"\"\n n_redundant = len(redundant_ids)\n n_missed_indep = len(np.setdiff1d(indep_info_ids, selected_ids))\n n_missed_dep = len(\n np.setdiff1d(dep_info_ids+redundant_ids, selected_ids)\n )-n_redundant\n if n_missed_dep < 0:\n n_missed_dep = 0\n return n_missed_indep+n_missed_dep" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prepare selectors\nWe compare :class:`FastCan` with eight selectors of :mod:`sklearn`, which\ninclude the Select From a Model (SFM) algorithm, the Recursive Feature Elimination\n(RFE) algorithm, the Sequential Feature Selection (SFS) algorithm, and Select K Best\n(SKB) algorithm.\nThe list of the selectors are given below:\n\n* fastcan: :class:`FastCan` selector\n* skb_reg: is the SKB algorithm ranking features with ANOVA (analysis of variance)\n F-statistic and p-values\n* skb_mir: is the SKB algorithm ranking features mutual information for regression\n* sfm_lsvr: the SFM algorithm with a linear support vector regressor\n* sfm_rfr: the SFM algorithm with a random forest regressor\n* rfe_lsvr: is the RFE algorithm with a linear support vector regressor\n* rfe_rfr: is the RFE algorithm with a random forest regressor\n* sfs_lsvr: is the forward SFS algorithm with a linear support vector regressor\n* sfs_rfr: is the forward SFS algorithm with a random forest regressor\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.ensemble import RandomForestRegressor\nfrom sklearn.feature_selection import (\n RFE,\n SelectFromModel,\n SelectKBest,\n SequentialFeatureSelector,\n f_regression,\n mutual_info_regression,\n)\nfrom sklearn.svm import LinearSVR\n\nfrom fastcan import FastCan\n\nlsvr = LinearSVR(C = 1, dual=\"auto\", max_iter=100000, random_state=0)\nrfr = RandomForestRegressor(n_estimators = 10, random_state=0)\n\n\nN_SAMPLES = 1000\nN_FEATURES = 10\nDEP_INFO_IDS = [2, 4, 7, 9]\nINDEP_INFO_IDS = [0, 1, 6]\nREDUNDANT_IDS = [5, 8]\nN_SELECTED = len(DEP_INFO_IDS+INDEP_INFO_IDS)\nN_REPEATED = 10\n\nselector_dict = {\n \"fastcan\": FastCan(N_SELECTED, verbose=0),\n \"skb_reg\": SelectKBest(f_regression, k=N_SELECTED),\n \"skb_mir\": SelectKBest(mutual_info_regression, k=N_SELECTED),\n \"sfm_lsvr\": SelectFromModel(lsvr, max_features=N_SELECTED, threshold=-np.inf),\n \"sfm_rfr\": SelectFromModel(rfr, max_features=N_SELECTED, threshold=-np.inf),\n \"rfe_lsvr\": RFE(lsvr, n_features_to_select=N_SELECTED, step=1),\n \"rfe_rfr\": RFE(rfr, n_features_to_select=N_SELECTED, step=1),\n \"sfs_lsvr\": SequentialFeatureSelector(lsvr, n_features_to_select=N_SELECTED, cv=2),\n \"sfs_rfr\": SequentialFeatureSelector(rfr, n_features_to_select=N_SELECTED, cv=2),\n}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Run test\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "N_SELECTORS = len(selector_dict)\nn_missed = np.zeros((N_REPEATED, N_SELECTORS), dtype=int)\n\nfor i in range(N_REPEATED):\n X, y = make_redundant(\n n_samples=N_SAMPLES,\n n_features=N_FEATURES,\n dep_info_ids=DEP_INFO_IDS,\n indep_info_ids=INDEP_INFO_IDS,\n redundant_ids=REDUNDANT_IDS,\n random_seed=i,\n )\n for j, selector in enumerate(selector_dict.values()):\n result_ids = selector.fit(X, y).get_support(indices=True)\n n_missed[i, j] = get_n_missed(\n dep_info_ids=DEP_INFO_IDS,\n indep_info_ids=INDEP_INFO_IDS,\n redundant_ids=REDUNDANT_IDS,\n selected_ids=result_ids,\n )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Plot results\n:class:`FastCan` correctly selects all informative features with zero missed\nfeatures.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n\nfig, ax = plt.subplots(figsize = (8, 5))\nrects = ax.bar(selector_dict.keys(), n_missed.sum(0), width = 0.5)\nax.bar_label(rects, n_missed.sum(0), padding=3)\nplt.xlabel(\"Selector\")\nplt.ylabel(\"No. of missed features\")\nplt.title(\"Performance of selectors on datasets with linearly redundant features\")\nplt.show()" + ] + } + ], + "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.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/doc/auto_examples/plot_redundancy.py b/doc/auto_examples/plot_redundancy.py new file mode 100644 index 0000000..4d2db4a --- /dev/null +++ b/doc/auto_examples/plot_redundancy.py @@ -0,0 +1,213 @@ +""" +=================================================== +Feature selection performance on redundant features +=================================================== + +.. currentmodule:: fastcan + +In this examples, we will compare the performance of feature selectors on the +datasets, which contain redundant features. +Here four types of features should be distinguished: + +* Unuseful features: the features do not contribute to the target +* Dependent informative features: the features contribute to the target and form + the redundant features +* Redundant features: the features are constructed by linear transformation of + dependent informative features +* Independent informative features: the features contribute to the target but + does not contribute to the redundant features. + +.. note:: + If we do not distinguish dependent and independent informative features and use + informative features to form both the target and the redundant features. The task + will be much easier. +""" + +# Authors: Sikai Zhang +# SPDX-License-Identifier: MIT + +# %% +# Define dataset generator +# ------------------------ + +import numpy as np + + +def make_redundant( + n_samples, + n_features, + dep_info_ids, + indep_info_ids, + redundant_ids, + random_seed, +): + """Make a dataset with linearly redundant features. + + Parameters + ---------- + n_samples : int + The number of samples. + + n_features : int + The number of features. + + dep_info_ids : list[int] + The indices of dependent informative features. + + indep_info_ids : list[int] + The indices of independent informative features. + + redundant_ids : list[int] + The indices of redundant features. + + random_seed : int + Random seed. + + Returns + ------- + X : array-like of shape (n_samples, n_features) + Feature matrix. + + y : array-like of shape (n_samples,) + Target vector. + """ + rng = np.random.default_rng(random_seed) + info_ids = dep_info_ids + indep_info_ids + n_dep_info = len(dep_info_ids) + n_info = len(info_ids) + n_redundant = len(redundant_ids) + informative_coef = rng.random(n_info) + redundant_coef = rng.random((n_dep_info, n_redundant)) + + X = rng.random((n_samples, n_features)) + y = np.dot(X[:, info_ids], informative_coef) + + X[:, redundant_ids] = X[:, dep_info_ids]@redundant_coef + return X, y + +# %% +# Define score function +# --------------------- +# This function is used to compute the number of correct features missed by selectors. +# +# * For independent informative features, selectors should select all of them +# * For dependent informative features, selectors only need to select any +# ``n_dep_info``-combination of the set ``dep_info_ids`` + ``redundant_ids``. That +# means if the indices of dependent informative features are :math:`[0, 1]` and the +# indices of the redundant features are :math:`[5]`, then the correctly selected +# indices can be any of :math:`[0, 1]`, :math:`[0, 5]`, and :math:`[1, 5]`. + +def get_n_missed( + dep_info_ids, + indep_info_ids, + redundant_ids, + selected_ids +): + """Get the number of features miss selected.""" + n_redundant = len(redundant_ids) + n_missed_indep = len(np.setdiff1d(indep_info_ids, selected_ids)) + n_missed_dep = len( + np.setdiff1d(dep_info_ids+redundant_ids, selected_ids) + )-n_redundant + if n_missed_dep < 0: + n_missed_dep = 0 + return n_missed_indep+n_missed_dep + +# %% +# Prepare selectors +# ----------------- +# We compare :class:`FastCan` with eight selectors of :mod:`sklearn`, which +# include the Select From a Model (SFM) algorithm, the Recursive Feature Elimination +# (RFE) algorithm, the Sequential Feature Selection (SFS) algorithm, and Select K Best +# (SKB) algorithm. +# The list of the selectors are given below: +# +# * fastcan: :class:`FastCan` selector +# * skb_reg: is the SKB algorithm ranking features with ANOVA (analysis of variance) +# F-statistic and p-values +# * skb_mir: is the SKB algorithm ranking features mutual information for regression +# * sfm_lsvr: the SFM algorithm with a linear support vector regressor +# * sfm_rfr: the SFM algorithm with a random forest regressor +# * rfe_lsvr: is the RFE algorithm with a linear support vector regressor +# * rfe_rfr: is the RFE algorithm with a random forest regressor +# * sfs_lsvr: is the forward SFS algorithm with a linear support vector regressor +# * sfs_rfr: is the forward SFS algorithm with a random forest regressor + + +from sklearn.ensemble import RandomForestRegressor +from sklearn.feature_selection import ( + RFE, + SelectFromModel, + SelectKBest, + SequentialFeatureSelector, + f_regression, + mutual_info_regression, +) +from sklearn.svm import LinearSVR + +from fastcan import FastCan + +lsvr = LinearSVR(C = 1, dual="auto", max_iter=100000, random_state=0) +rfr = RandomForestRegressor(n_estimators = 10, random_state=0) + + +N_SAMPLES = 1000 +N_FEATURES = 10 +DEP_INFO_IDS = [2, 4, 7, 9] +INDEP_INFO_IDS = [0, 1, 6] +REDUNDANT_IDS = [5, 8] +N_SELECTED = len(DEP_INFO_IDS+INDEP_INFO_IDS) +N_REPEATED = 10 + +selector_dict = { + "fastcan": FastCan(N_SELECTED, verbose=0), + "skb_reg": SelectKBest(f_regression, k=N_SELECTED), + "skb_mir": SelectKBest(mutual_info_regression, k=N_SELECTED), + "sfm_lsvr": SelectFromModel(lsvr, max_features=N_SELECTED, threshold=-np.inf), + "sfm_rfr": SelectFromModel(rfr, max_features=N_SELECTED, threshold=-np.inf), + "rfe_lsvr": RFE(lsvr, n_features_to_select=N_SELECTED, step=1), + "rfe_rfr": RFE(rfr, n_features_to_select=N_SELECTED, step=1), + "sfs_lsvr": SequentialFeatureSelector(lsvr, n_features_to_select=N_SELECTED, cv=2), + "sfs_rfr": SequentialFeatureSelector(rfr, n_features_to_select=N_SELECTED, cv=2), +} + +# %% +# Run test +# -------- + +N_SELECTORS = len(selector_dict) +n_missed = np.zeros((N_REPEATED, N_SELECTORS), dtype=int) + +for i in range(N_REPEATED): + X, y = make_redundant( + n_samples=N_SAMPLES, + n_features=N_FEATURES, + dep_info_ids=DEP_INFO_IDS, + indep_info_ids=INDEP_INFO_IDS, + redundant_ids=REDUNDANT_IDS, + random_seed=i, + ) + for j, selector in enumerate(selector_dict.values()): + result_ids = selector.fit(X, y).get_support(indices=True) + n_missed[i, j] = get_n_missed( + dep_info_ids=DEP_INFO_IDS, + indep_info_ids=INDEP_INFO_IDS, + redundant_ids=REDUNDANT_IDS, + selected_ids=result_ids, + ) + +# %% +# Plot results +# ------------ +# :class:`FastCan` correctly selects all informative features with zero missed +# features. + +import matplotlib.pyplot as plt + +fig, ax = plt.subplots(figsize = (8, 5)) +rects = ax.bar(selector_dict.keys(), n_missed.sum(0), width = 0.5) +ax.bar_label(rects, n_missed.sum(0), padding=3) +plt.xlabel("Selector") +plt.ylabel("No. of missed features") +plt.title("Performance of selectors on datasets with linearly redundant features") +plt.show() diff --git a/doc/auto_examples/plot_redundancy.py.md5 b/doc/auto_examples/plot_redundancy.py.md5 new file mode 100644 index 0000000..121982e --- /dev/null +++ b/doc/auto_examples/plot_redundancy.py.md5 @@ -0,0 +1 @@ +a56289335c27ea32ed49611b004509cf \ No newline at end of file diff --git a/doc/auto_examples/plot_redundancy.rst b/doc/auto_examples/plot_redundancy.rst new file mode 100644 index 0000000..fcc6707 --- /dev/null +++ b/doc/auto_examples/plot_redundancy.rst @@ -0,0 +1,343 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/plot_redundancy.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code. + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_plot_redundancy.py: + + +=================================================== +Feature selection performance on redundant features +=================================================== + +.. currentmodule:: fastcan + +In this examples, we will compare the performance of feature selectors on the +datasets, which contain redundant features. +Here four types of features should be distinguished: + +* Unuseful features: the features do not contribute to the target +* Dependent informative features: the features contribute to the target and form + the redundant features +* Redundant features: the features are constructed by linear transformation of + dependent informative features +* Independent informative features: the features contribute to the target but + does not contribute to the redundant features. + +.. note:: + If we do not distinguish dependent and independent informative features and use + informative features to form both the target and the redundant features. The task + will be much easier. + +.. GENERATED FROM PYTHON SOURCE LINES 25-29 + +.. code-block:: Python + + + # Authors: Sikai Zhang + # SPDX-License-Identifier: MIT + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 30-32 + +Define dataset generator +------------------------ + +.. GENERATED FROM PYTHON SOURCE LINES 32-88 + +.. code-block:: Python + + + import numpy as np + + + def make_redundant( + n_samples, + n_features, + dep_info_ids, + indep_info_ids, + redundant_ids, + random_seed, + ): + """Make a dataset with linearly redundant features. + + Parameters + ---------- + n_samples : int + The number of samples. + + n_features : int + The number of features. + + dep_info_ids : list[int] + The indices of dependent informative features. + + indep_info_ids : list[int] + The indices of independent informative features. + + redundant_ids : list[int] + The indices of redundant features. + + random_seed : int + Random seed. + + Returns + ------- + X : array-like of shape (n_samples, n_features) + Feature matrix. + + y : array-like of shape (n_samples,) + Target vector. + """ + rng = np.random.default_rng(random_seed) + info_ids = dep_info_ids + indep_info_ids + n_dep_info = len(dep_info_ids) + n_info = len(info_ids) + n_redundant = len(redundant_ids) + informative_coef = rng.random(n_info) + redundant_coef = rng.random((n_dep_info, n_redundant)) + + X = rng.random((n_samples, n_features)) + y = np.dot(X[:, info_ids], informative_coef) + + X[:, redundant_ids] = X[:, dep_info_ids]@redundant_coef + return X, y + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 89-99 + +Define score function +--------------------- +This function is used to compute the number of correct features missed by selectors. + +* For independent informative features, selectors should select all of them +* For dependent informative features, selectors only need to select any + ``n_dep_info``-combination of the set ``dep_info_ids`` + ``redundant_ids``. That + means if the indices of dependent informative features are :math:`[0, 1]` and the + indices of the redundant features are :math:`[5]`, then the correctly selected + indices can be any of :math:`[0, 1]`, :math:`[0, 5]`, and :math:`[1, 5]`. + +.. GENERATED FROM PYTHON SOURCE LINES 99-116 + +.. code-block:: Python + + + def get_n_missed( + dep_info_ids, + indep_info_ids, + redundant_ids, + selected_ids + ): + """Get the number of features miss selected.""" + n_redundant = len(redundant_ids) + n_missed_indep = len(np.setdiff1d(indep_info_ids, selected_ids)) + n_missed_dep = len( + np.setdiff1d(dep_info_ids+redundant_ids, selected_ids) + )-n_redundant + if n_missed_dep < 0: + n_missed_dep = 0 + return n_missed_indep+n_missed_dep + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 117-135 + +Prepare selectors +----------------- +We compare :class:`FastCan` with eight selectors of :mod:`sklearn`, which +include the Select From a Model (SFM) algorithm, the Recursive Feature Elimination +(RFE) algorithm, the Sequential Feature Selection (SFS) algorithm, and Select K Best +(SKB) algorithm. +The list of the selectors are given below: + +* fastcan: :class:`FastCan` selector +* skb_reg: is the SKB algorithm ranking features with ANOVA (analysis of variance) + F-statistic and p-values +* skb_mir: is the SKB algorithm ranking features mutual information for regression +* sfm_lsvr: the SFM algorithm with a linear support vector regressor +* sfm_rfr: the SFM algorithm with a random forest regressor +* rfe_lsvr: is the RFE algorithm with a linear support vector regressor +* rfe_rfr: is the RFE algorithm with a random forest regressor +* sfs_lsvr: is the forward SFS algorithm with a linear support vector regressor +* sfs_rfr: is the forward SFS algorithm with a random forest regressor + +.. GENERATED FROM PYTHON SOURCE LINES 135-174 + +.. code-block:: Python + + + + from sklearn.ensemble import RandomForestRegressor + from sklearn.feature_selection import ( + RFE, + SelectFromModel, + SelectKBest, + SequentialFeatureSelector, + f_regression, + mutual_info_regression, + ) + from sklearn.svm import LinearSVR + + from fastcan import FastCan + + lsvr = LinearSVR(C = 1, dual="auto", max_iter=100000, random_state=0) + rfr = RandomForestRegressor(n_estimators = 10, random_state=0) + + + N_SAMPLES = 1000 + N_FEATURES = 10 + DEP_INFO_IDS = [2, 4, 7, 9] + INDEP_INFO_IDS = [0, 1, 6] + REDUNDANT_IDS = [5, 8] + N_SELECTED = len(DEP_INFO_IDS+INDEP_INFO_IDS) + N_REPEATED = 10 + + selector_dict = { + "fastcan": FastCan(N_SELECTED, verbose=0), + "skb_reg": SelectKBest(f_regression, k=N_SELECTED), + "skb_mir": SelectKBest(mutual_info_regression, k=N_SELECTED), + "sfm_lsvr": SelectFromModel(lsvr, max_features=N_SELECTED, threshold=-np.inf), + "sfm_rfr": SelectFromModel(rfr, max_features=N_SELECTED, threshold=-np.inf), + "rfe_lsvr": RFE(lsvr, n_features_to_select=N_SELECTED, step=1), + "rfe_rfr": RFE(rfr, n_features_to_select=N_SELECTED, step=1), + "sfs_lsvr": SequentialFeatureSelector(lsvr, n_features_to_select=N_SELECTED, cv=2), + "sfs_rfr": SequentialFeatureSelector(rfr, n_features_to_select=N_SELECTED, cv=2), + } + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 175-177 + +Run test +-------- + +.. GENERATED FROM PYTHON SOURCE LINES 177-199 + +.. code-block:: Python + + + N_SELECTORS = len(selector_dict) + n_missed = np.zeros((N_REPEATED, N_SELECTORS), dtype=int) + + for i in range(N_REPEATED): + X, y = make_redundant( + n_samples=N_SAMPLES, + n_features=N_FEATURES, + dep_info_ids=DEP_INFO_IDS, + indep_info_ids=INDEP_INFO_IDS, + redundant_ids=REDUNDANT_IDS, + random_seed=i, + ) + for j, selector in enumerate(selector_dict.values()): + result_ids = selector.fit(X, y).get_support(indices=True) + n_missed[i, j] = get_n_missed( + dep_info_ids=DEP_INFO_IDS, + indep_info_ids=INDEP_INFO_IDS, + redundant_ids=REDUNDANT_IDS, + selected_ids=result_ids, + ) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 200-204 + +Plot results +------------ +:class:`FastCan` correctly selects all informative features with zero missed +features. + +.. GENERATED FROM PYTHON SOURCE LINES 204-214 + +.. code-block:: Python + + + import matplotlib.pyplot as plt + + fig, ax = plt.subplots(figsize = (8, 5)) + rects = ax.bar(selector_dict.keys(), n_missed.sum(0), width = 0.5) + ax.bar_label(rects, n_missed.sum(0), padding=3) + plt.xlabel("Selector") + plt.ylabel("No. of missed features") + plt.title("Performance of selectors on datasets with linearly redundant features") + plt.show() + + + +.. image-sg:: /auto_examples/images/sphx_glr_plot_redundancy_001.png + :alt: Performance of selectors on datasets with linearly redundant features + :srcset: /auto_examples/images/sphx_glr_plot_redundancy_001.png + :class: sphx-glr-single-img + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 19.255 seconds) + + +.. _sphx_glr_download_auto_examples_plot_redundancy.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: plot_redundancy.ipynb ` + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: plot_redundancy.py ` + + .. container:: sphx-glr-download sphx-glr-download-zip + + :download:`Download zipped: plot_redundancy.zip ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/auto_examples/plot_redundancy.zip b/doc/auto_examples/plot_redundancy.zip new file mode 100644 index 0000000000000000000000000000000000000000..36461628e05d2686f381359924913693ccec3022 GIT binary patch literal 16231 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 nyYf{JeMpLazW?s`znhnr&wln^{Nk6?`_ +# Zhang, S., Wang, T., Worden, K., Sun L., & Cross, E. J. +# Mechanical Systems and Signal Processing, 223:111895 (2025). + +from timeit import timeit + +import matplotlib.pyplot as plt + +from fastcan import FastCan + +n_features_to_select = 10 + +t_h = timeit( + f"s = FastCan({n_features_to_select}, verbose=0).fit(X, y)", + number=10, + globals=globals(), +) +t_eta = timeit( + f"s = FastCan({n_features_to_select}, eta=True, verbose=0).fit(X, y)", + number=10, + globals=globals() +) +t_base = timeit( + f"indices, _ = baseline(X, y, {n_features_to_select})", + number=10, + globals=globals() +) +print(f"Elapsed time using h correlation algorithm: {t_h:.5f} seconds") +print(f"Elapsed time using eta cosine algorithm: {t_eta:.5f} seconds") +print(f"Elapsed time using baseline algorithm: {t_base:.5f} seconds") + +# %% +# Results of selection +# -------------------- +# It can be found that the selected indices of the three methods are exactly the same. + +r_h = FastCan(n_features_to_select, verbose=0).fit(X, y).indices_ +r_eta = FastCan(n_features_to_select, eta=True, verbose=0).fit(X, y).indices_ +r_base, _ = baseline(X, y, n_features_to_select) + +print("The indices of the seleted features:", end="\n") +print(f"h-correlation: {r_h}") +print(f"eta-cosine: {r_eta}") +print(f"Baseline: {r_base}") + +# %% +# Comparison between h-correlation and eta-cosine methods +# ------------------------------------------------------- +# Let's increase the number of features to 100. Then let the h-correlation method +# and eta-cosine method to 1 to 30 features and compare their speed performance. +# There are some interesting findings: +# +# #. h-correlation method is faster when the number of the selected feature is small. +# +# #. The slope for eta-cosine method is much lower than h-correlation method. +# +# But why is it? Briefly speaking, at the preprocessing stage, the eta-cosine method +# requires computing the singular value decomposition (SVD) for +# ``np.column_stack((X, y))``, which dominates its elapsed time. However, in the +# following iterated selection process, the eta-cosine method will be much faster than +# the h-correlation method. + +X = rng.random((3000, 100)) +y = rng.random((3000, 20)) + +n_features_max = 30 + +time_h = np.zeros(n_features_max, dtype=float) +time_eta = np.zeros(n_features_max, dtype=float) +for i in range(n_features_max): + time_h[i] = timeit( + f"s = FastCan({i+1}, verbose=0).fit(X, y)", + number=10, + globals=globals(), + ) + time_eta[i] = timeit( + f"s = FastCan({i+1}, eta=True, verbose=0).fit(X, y)", + number=10, + globals=globals() + ) + +feature_num = np.arange(n_features_max, dtype=int)+1 +plt.plot(feature_num, time_h, label = "h-correlation") +plt.plot(feature_num, time_eta, label = r'$\eta$-cosine') +plt.title("Elapsed Time Comparison") +plt.xlabel("Number of Selected Features") +plt.ylabel("Elapsed Time (s)") +plt.legend(loc="lower right") +plt.show() diff --git a/doc/auto_examples/plot_speed.py.md5 b/doc/auto_examples/plot_speed.py.md5 new file mode 100644 index 0000000..09fd2b9 --- /dev/null +++ b/doc/auto_examples/plot_speed.py.md5 @@ -0,0 +1 @@ +d3837352ac9674afa7f3c68c11aaf977 \ No newline at end of file diff --git a/doc/auto_examples/plot_speed.rst b/doc/auto_examples/plot_speed.rst new file mode 100644 index 0000000..dc3154f --- /dev/null +++ b/doc/auto_examples/plot_speed.rst @@ -0,0 +1,345 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/plot_speed.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code. + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_plot_speed.py: + + +============================== +Computational speed comparison +============================== + +.. currentmodule:: fastcan + +In this examples, we will compare the computational speed of three different feature +selection methods: h-correlation based :class:`FastCan`, eta-cosine based +:class:`FastCan`, and baseline model based on +``sklearn.cross_decomposition.CCA``. + +.. GENERATED FROM PYTHON SOURCE LINES 14-18 + +.. code-block:: Python + + + # Authors: Sikai Zhang + # SPDX-License-Identifier: MIT + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 19-25 + +Prepare data +------------ + +We will generate a random matrix with 3000 samples and 20 variables as feature +matrix :math:`X` and a random matrix with 3000 samples and 20 variables as target +matrix :math:`y`. + +.. GENERATED FROM PYTHON SOURCE LINES 25-32 + +.. code-block:: Python + + + import numpy as np + + rng = np.random.default_rng(12345) + X = rng.random((3000, 20)) + y = rng.random((3000, 5)) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 33-41 + +Define baseline method +---------------------- +The baseline method can be realised by ``CCA`` in ``scikit-learn``. +The baseline method will, in greedy manner, select the feature which maximizes the +canonical correlation between ``np.column_stack((X_selected, X[:, j]))`` and ``y``, +where ``X_selected`` is the selected features in past iterations. As the number of +canonical correlation coefficients may be more than one, the feature ranking +criterion used here is the sum squared of all canonical correlation coefficients. + +.. GENERATED FROM PYTHON SOURCE LINES 41-88 + +.. code-block:: Python + + + from fastcan import ssc + + + def baseline(X, y, t): + """Baseline method using CCA from sklearn. + + Parameters + ---------- + X : array-like of shape (n_samples, n_features) + Feature matrix. + + y : array-like of shape (n_samples, n_outputs) + Target matrix. + + t : int + The parameter is the absolute number of features to select. + + Returns + ------- + indices_ : ndarray of shape (t,), dtype=int + The indices of the selected features. The order of the indices + is corresponding to the feature selection process. + + scores_: ndarray of shape (t,), dtype=float + The sum of the squared correlation of selected features. The order of + the scores is corresponding to the feature selection process. + """ + n_samples, n_features = X.shape + mask = np.zeros(n_features, dtype=bool) + r2 = np.zeros(n_features, dtype=float) + indices = np.zeros(t, dtype=int) + scores = np.zeros(t, dtype=float) + X_selected = np.zeros((n_samples, 0), dtype=float) + for i in range(t): + for j in range(n_features): + if not mask[j]: + X_candidate = np.column_stack((X_selected, X[:, j])) + r2[j] = ssc(X_candidate, y) + d = np.argmax(r2) + indices[i] = d + scores[i] = r2[d] + mask[d] = True + r2[d] = 0 + X_selected = np.column_stack((X_selected, X[:, d])) + return indices, scores + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 89-114 + +Elapsed time comparison +----------------------- +Let the three methods select 10 informative features from the feature matrix +:math:`X`. It can be found that the two FastCan methods are much faster than +the baseline method. + +.. dropdown:: Complexity + + The overall computational complexities of the three methods are + + #. Baseline: :math:`O[t^3 N n]` + #. h-correlation: :math:`O[t N n m]` + #. eta-cosine: :math:`O[t n^2 m]` + + * :math:`N` : number of data points + * :math:`n` : feature dimension + * :math:`m` : target dimension + * :math:`t` : number of features to be selected + + .. rubric:: References + + * `"Canonical-correlation-based fast feature selection for structural + health monitoring" `_ + Zhang, S., Wang, T., Worden, K., Sun L., & Cross, E. J. + Mechanical Systems and Signal Processing, 223:111895 (2025). + +.. GENERATED FROM PYTHON SOURCE LINES 114-142 + +.. code-block:: Python + + + from timeit import timeit + + import matplotlib.pyplot as plt + + from fastcan import FastCan + + n_features_to_select = 10 + + t_h = timeit( + f"s = FastCan({n_features_to_select}, verbose=0).fit(X, y)", + number=10, + globals=globals(), + ) + t_eta = timeit( + f"s = FastCan({n_features_to_select}, eta=True, verbose=0).fit(X, y)", + number=10, + globals=globals() + ) + t_base = timeit( + f"indices, _ = baseline(X, y, {n_features_to_select})", + number=10, + globals=globals() + ) + print(f"Elapsed time using h correlation algorithm: {t_h:.5f} seconds") + print(f"Elapsed time using eta cosine algorithm: {t_eta:.5f} seconds") + print(f"Elapsed time using baseline algorithm: {t_base:.5f} seconds") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Elapsed time using h correlation algorithm: 0.03627 seconds + Elapsed time using eta cosine algorithm: 0.05120 seconds + Elapsed time using baseline algorithm: 9.39280 seconds + + + + +.. GENERATED FROM PYTHON SOURCE LINES 143-146 + +Results of selection +-------------------- +It can be found that the selected indices of the three methods are exactly the same. + +.. GENERATED FROM PYTHON SOURCE LINES 146-156 + +.. code-block:: Python + + + r_h = FastCan(n_features_to_select, verbose=0).fit(X, y).indices_ + r_eta = FastCan(n_features_to_select, eta=True, verbose=0).fit(X, y).indices_ + r_base, _ = baseline(X, y, n_features_to_select) + + print("The indices of the seleted features:", end="\n") + print(f"h-correlation: {r_h}") + print(f"eta-cosine: {r_eta}") + print(f"Baseline: {r_base}") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + The indices of the seleted features: + h-correlation: [ 9 5 16 3 17 1 8 0 12 18] + eta-cosine: [ 9 5 16 3 17 1 8 0 12 18] + Baseline: [ 9 5 16 3 17 1 8 0 12 18] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 157-172 + +Comparison between h-correlation and eta-cosine methods +------------------------------------------------------- +Let's increase the number of features to 100. Then let the h-correlation method +and eta-cosine method to 1 to 30 features and compare their speed performance. +There are some interesting findings: + +#. h-correlation method is faster when the number of the selected feature is small. + +#. The slope for eta-cosine method is much lower than h-correlation method. + +But why is it? Briefly speaking, at the preprocessing stage, the eta-cosine method +requires computing the singular value decomposition (SVD) for +``np.column_stack((X, y))``, which dominates its elapsed time. However, in the +following iterated selection process, the eta-cosine method will be much faster than +the h-correlation method. + +.. GENERATED FROM PYTHON SOURCE LINES 172-200 + +.. code-block:: Python + + + X = rng.random((3000, 100)) + y = rng.random((3000, 20)) + + n_features_max = 30 + + time_h = np.zeros(n_features_max, dtype=float) + time_eta = np.zeros(n_features_max, dtype=float) + for i in range(n_features_max): + time_h[i] = timeit( + f"s = FastCan({i+1}, verbose=0).fit(X, y)", + number=10, + globals=globals(), + ) + time_eta[i] = timeit( + f"s = FastCan({i+1}, eta=True, verbose=0).fit(X, y)", + number=10, + globals=globals() + ) + + feature_num = np.arange(n_features_max, dtype=int)+1 + plt.plot(feature_num, time_h, label = "h-correlation") + plt.plot(feature_num, time_eta, label = r'$\eta$-cosine') + plt.title("Elapsed Time Comparison") + plt.xlabel("Number of Selected Features") + plt.ylabel("Elapsed Time (s)") + plt.legend(loc="lower right") + plt.show() + + + +.. image-sg:: /auto_examples/images/sphx_glr_plot_speed_001.png + :alt: Elapsed Time Comparison + :srcset: /auto_examples/images/sphx_glr_plot_speed_001.png + :class: sphx-glr-single-img + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 36.506 seconds) + + +.. _sphx_glr_download_auto_examples_plot_speed.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: plot_speed.ipynb ` + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: plot_speed.py ` + + .. container:: sphx-glr-download sphx-glr-download-zip + + :download:`Download zipped: plot_speed.zip ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/auto_examples/plot_speed.zip b/doc/auto_examples/plot_speed.zip new file mode 100644 index 0000000000000000000000000000000000000000..e2b1bc1b943983b50acfc8edeb8f8cbf88425766 GIT binary patch literal 15206 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 + + + + + + + + + .. list-table:: + :header-rows: 1 + :class: table table-striped sg-datatable + + * - Example + - Time + - Mem (MB) + * - :ref:`sphx_glr_auto_examples_plot_affinity.py` (``plot_affinity.py``) + - 00:00.734 + - 0.0 + * - :ref:`sphx_glr_auto_examples_plot_redundancy.py` (``plot_redundancy.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr_auto_examples_plot_speed.py` (``plot_speed.py``) + - 00:00.000 + - 0.0 diff --git a/doc/sg_execution_times.rst b/doc/sg_execution_times.rst new file mode 100644 index 0000000..243af45 --- /dev/null +++ b/doc/sg_execution_times.rst @@ -0,0 +1,43 @@ + +:orphan: + +.. _sphx_glr_sg_execution_times: + + +Computation times +================= +**00:00.734** total execution time for 3 files **from all galleries**: + +.. container:: + + .. raw:: html + + + + + + + + .. list-table:: + :header-rows: 1 + :class: table table-striped sg-datatable + + * - Example + - Time + - Mem (MB) + * - :ref:`sphx_glr_auto_examples_plot_affinity.py` (``../examples/plot_affinity.py``) + - 00:00.734 + - 0.0 + * - :ref:`sphx_glr_auto_examples_plot_redundancy.py` (``../examples/plot_redundancy.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr_auto_examples_plot_speed.py` (``../examples/plot_speed.py``) + - 00:00.000 + - 0.0