diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 265dd504..aa022732 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -12,7 +12,7 @@ Changelog **New feature:** -- Added a new function, :func:`tabmat.from_polars`, to convert a :class:`polars.DataFrame` into a :class:`tabmat.SplitMatrix`. +- Added a new function, :func:`tabmat.from_df`, to convert any dataframe supported by narwhals into a :class:`tabmat.SplitMatrix`. **Other changes:** diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml index 7636b11b..b8dfd178 100644 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -38,6 +38,7 @@ requirements: - {{ pin_compatible('numpy') }} - formulaic>=0.6 - scipy + - narwhals test: requires: diff --git a/pixi.lock b/pixi.lock index 00dc65f4..a08d4b2e 100644 --- a/pixi.lock +++ b/pixi.lock @@ -89,6 +89,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-devel-2024.1.0-ha770c72_693.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-include-2024.1.0-ha957f24_693.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.1.0-py312h1103770_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.2-h488ebb8_0.conda @@ -197,6 +198,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-2.1.5-py312h024a12e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.9.2-py312h32d6e5a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.1.0-py312hb544834_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.2-h9f1df11_0.conda @@ -295,6 +297,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-include-2024.1.0-h66d3029_694.conda - conda: https://conda.anaconda.org/conda-forge/win-64/msys2-conda-epoch-20160418-1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.1.0-py312h49bc9c5_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.2-h3d672ee_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.3.1-h2466b09_3.conda @@ -463,6 +466,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-1.11.2-py312h66e93f0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.1.0-py312h1103770_0.conda @@ -633,6 +637,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-1.11.2-py312h024a12e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.1.0-py312hb544834_0.conda @@ -776,6 +781,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/msys2-conda-epoch-20160418-1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-1.11.2-py312h4389bb4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.1.0-py312h49bc9c5_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.3.1-h2466b09_3.conda @@ -975,6 +981,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_0.conda @@ -1208,6 +1215,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_0.conda @@ -1426,6 +1434,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2024.1.0-h66d3029_694.conda - conda: https://conda.anaconda.org/conda-forge/win-64/msys2-conda-epoch-20160418-1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_0.conda @@ -1769,6 +1778,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.5-py312h66e93f0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-1.11.2-py312h66e93f0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.1.0-py312h1103770_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.1-hb9d3cd8_3.conda @@ -1910,6 +1920,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-2.1.5-py312h024a12e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-1.11.2-py312h024a12e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.1.0-py312hb544834_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.3.1-h8359307_3.conda @@ -2025,6 +2036,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/msys2-conda-epoch-20160418-1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-1.11.2-py312h4389bb4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.1.0-py312h49bc9c5_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.3.1-h2466b09_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.0.2-h784c2ca_0.conda @@ -2181,6 +2193,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.5-py39h8cd3c5a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-1.11.2-py39h8cd3c5a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.4.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.24.0-py39h223a676_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.1-hb9d3cd8_3.conda @@ -2320,6 +2333,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-2.1.5-py39h06df861_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-1.11.2-py39h06df861_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.4.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.24.0-py39hefdcf20_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.3.1-h8359307_3.conda @@ -2433,6 +2447,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/msys2-conda-epoch-20160418-1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-1.11.2-py39ha55e580_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.4.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-1.24.0-py39hbccbffa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.3.1-h2466b09_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.0.2-h784c2ca_0.conda @@ -2588,6 +2603,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.5-py310ha75aee5_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-1.11.2-py310ha75aee5_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.1.0-py310hf9f9071_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.1-hb9d3cd8_3.conda @@ -2728,6 +2744,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-2.1.5-py310h493c2e1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-1.11.2-py310h493c2e1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.1.0-py310h52bbd9b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.3.1-h8359307_3.conda @@ -2842,6 +2859,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/msys2-conda-epoch-20160418-1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-1.11.2-py310ha8f682b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.1.0-py310h1ec8c79_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.3.1-h2466b09_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.0.2-h784c2ca_0.conda @@ -2999,6 +3017,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.5-py311h9ecbd09_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-1.11.2-py311h9ecbd09_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.1.0-py311hed25524_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.1-hb9d3cd8_3.conda @@ -3140,6 +3159,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-2.1.5-py311h460d6c5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-1.11.2-py311h460d6c5_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.1.0-py311h4268184_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.3.1-h8359307_3.conda @@ -3255,6 +3275,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/msys2-conda-epoch-20160418-1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-1.11.2-py311he736701_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.1.0-py311h35ffc71_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.3.1-h2466b09_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.0.2-h784c2ca_0.conda @@ -3412,6 +3433,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.5-py312h66e93f0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-1.11.2-py312h66e93f0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.1.0-py312h1103770_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.1-hb9d3cd8_3.conda @@ -3553,6 +3575,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-2.1.5-py312h024a12e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-1.11.2-py312h024a12e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.1.0-py312hb544834_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.3.1-h8359307_3.conda @@ -3668,6 +3691,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/msys2-conda-epoch-20160418-1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-1.11.2-py312h4389bb4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.1.0-py312h49bc9c5_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.3.1-h2466b09_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.0.2-h784c2ca_0.conda @@ -3824,6 +3848,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.5-py39h8cd3c5a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-1.11.2-py39h8cd3c5a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.0.2-py39h9cb892a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.1-hb9d3cd8_3.conda @@ -3964,6 +3989,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-2.1.5-py39h06df861_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-1.11.2-py39h06df861_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.0.2-py39hd1e06cf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.3.1-h8359307_3.conda @@ -4078,6 +4104,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/msys2-conda-epoch-20160418-1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-1.11.2-py39ha55e580_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.0.2-py39h60232e0_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.3.1-h2466b09_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.0.2-h784c2ca_0.conda @@ -12200,6 +12227,35 @@ packages: license_family: MIT size: 10492 timestamp: 1675543414256 +- kind: conda + name: narwhals + version: 1.4.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.4.1-pyhd8ed1ab_0.conda + sha256: d4a51e0dcfb065bbd0b2a14cac1f5c997a6a26051fb961ac51f2bf356c24ca1b + md5: b93a767929335d6d0e1a2dc3dfaf7b65 + depends: + - python >=3.8 + license: MIT + license_family: MIT + size: 87516 + timestamp: 1723657930455 +- kind: conda + name: narwhals + version: 1.6.4 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.6.4-pyhd8ed1ab_0.conda + sha256: 6f37aa5f4e5171e790fab92b57cb963a1ce761590f77b8863a456da99419128f + md5: 2e93f28b2462c7e88e1946839486f64e + depends: + - python >=3.8 + license: MIT + size: 97200 + timestamp: 1725923577816 - kind: conda name: nbclient version: 0.10.0 diff --git a/pixi.toml b/pixi.toml index ebb25708..994e29da 100644 --- a/pixi.toml +++ b/pixi.toml @@ -101,6 +101,7 @@ formulaic = ">=0.6.4" numpy = ">=1.24.0" pandas = ">=1.4.4" scipy = ">=1.7.3" +narwhals = ">=1.4.1" [feature.dev.dependencies] ipython = "*" @@ -154,6 +155,7 @@ numpy = "1.24.0" pandas = "1.4.4" scipy = "1.7.3" formulaic = "0.6.4" +narwhals = "1.4.1" [environments] default = ["dev", "test"] diff --git a/setup.py b/setup.py index 10a0a807..64bd195b 100644 --- a/setup.py +++ b/setup.py @@ -157,7 +157,7 @@ ], package_dir={"": "src"}, packages=find_packages(where="src"), - install_requires=["formulaic>=0.6", "numpy", "scipy"], + install_requires=["formulaic>=0.6", "narwhals", "numpy", "scipy"], python_requires=">=3.9", ext_modules=cythonize( ext_modules, diff --git a/src/tabmat/__init__.py b/src/tabmat/__init__.py index b88fa82f..c95f2181 100644 --- a/src/tabmat/__init__.py +++ b/src/tabmat/__init__.py @@ -1,7 +1,7 @@ import importlib.metadata from .categorical_matrix import CategoricalMatrix -from .constructor import from_csc, from_formula, from_pandas, from_polars +from .constructor import from_csc, from_df, from_formula, from_pandas from .dense_matrix import DenseMatrix from .matrix_base import MatrixBase from .sparse_matrix import SparseMatrix @@ -23,7 +23,7 @@ "from_csc", "from_formula", "from_pandas", - "from_polars", + "from_df", "as_tabmat", "hstack", ] diff --git a/src/tabmat/categorical_matrix.py b/src/tabmat/categorical_matrix.py index 26cf0050..08d48250 100644 --- a/src/tabmat/categorical_matrix.py +++ b/src/tabmat/categorical_matrix.py @@ -167,6 +167,7 @@ def matvec(mat, vec): import warnings from typing import Optional, Union +import narwhals.stable.v1 as nw import numpy as np from scipy import sparse as sps @@ -195,8 +196,13 @@ def matvec(mat, vec): if importlib.util.find_spec("pandas"): import pandas as pd +else: + pd = None # type: ignore + if importlib.util.find_spec("polars"): import polars as pl +else: + pl = None # type: ignore def _is_indexer_full_length(full_length: int, indexer: Union[slice, np.ndarray]): @@ -210,35 +216,68 @@ def _is_indexer_full_length(full_length: int, indexer: Union[slice, np.ndarray]) return len(range(*indexer.indices(full_length))) == full_length -def _is_pandas(x) -> bool: - if importlib.util.find_spec("pandas"): - return isinstance(x, (pd.Categorical, pd.CategoricalDtype)) - return False - - -def _is_polars(x) -> bool: - if importlib.util.find_spec("polars"): - return isinstance(x, (pl.Series, pl.Categorical, pl.Enum)) - return False +def _factorize(x: np.ndarray) -> tuple[np.ndarray, np.ndarray]: + "A dumber version of pandas.factorize for when pandas is not available." + na_mask = (x == None) | (x != x) # noqa: E711 # The second part is for NaNs + categories, indices_nona = np.unique(x[~na_mask], return_inverse=True) + indices = np.full(x.shape, -1, dtype=np.int32) + indices[~na_mask] = indices_nona + return indices, categories -def _extract_codes_and_categories(cat_vec): - if _is_pandas(cat_vec): - categories = cat_vec.categories.to_numpy() +def _extract_codes_and_categories_pandas(cat_vec) -> tuple[np.ndarray, np.ndarray]: + if isinstance(cat_vec, pd.Categorical): + categories = cat_vec.categories indices = cat_vec.codes - elif _is_pandas(cat_vec.dtype): - categories = cat_vec.cat.categories.to_numpy() + elif isinstance(cat_vec.dtype, pd.CategoricalDtype): + categories = cat_vec.cat.categories indices = cat_vec.cat.codes.to_numpy() - elif _is_polars(cat_vec): - if not _is_polars(cat_vec.dtype): - cat_vec = cat_vec.cast(pl.Categorical) - categories = cat_vec.cat.to_local().cat.get_categories().to_numpy() - indices = cat_vec.cat.to_local().to_physical().fill_null(-1).to_numpy() else: indices, categories = pd.factorize(cat_vec, sort=True) + return indices, categories.to_numpy() + + +def _extract_codes_and_categories_polars(cat_vec) -> tuple[np.ndarray, np.ndarray]: + if not isinstance(cat_vec.dtype, (pl.Categorical, pl.Enum)): + cat_vec = cat_vec.cast(pl.Categorical) + categories = cat_vec.cat.to_local().cat.get_categories().to_numpy() + indices = cat_vec.cat.to_local().to_physical().fill_null(-1).to_numpy() + return indices, categories + + +def _extract_codes_and_categories_numpy(cat_vec) -> tuple[np.ndarray, np.ndarray]: + if pd: + indices, categories = pd.factorize(cat_vec, sort=True) + else: + indices, categories = _factorize(cat_vec) return indices, categories +def _extract_codes_and_categories(cat_vec) -> tuple[np.ndarray, np.ndarray]: + """ + Extract codes and categories from a series or vector. + + The input can be any series supported by narwhals, or an object that + can be converted to a numpy array. Pandas and polars inputs are special + cased for performance considerations. + """ + cat_vec_native = nw.to_native(cat_vec, strict=False) + + if pd and isinstance(cat_vec_native, (pd.Series, pd.Categorical)): + return _extract_codes_and_categories_pandas(cat_vec_native) + elif pl and isinstance(cat_vec_native, pl.Series): + return _extract_codes_and_categories_polars(cat_vec_native) + else: + if isinstance( + cat_vec_narwhals := nw.from_native(cat_vec, series_only=True, strict=False), + nw.Series, + ): + cat_vec = cat_vec_narwhals.cast(nw.String).to_numpy() + else: + cat_vec = np.asarray(cat_vec) + return _extract_codes_and_categories_numpy(cat_vec) + + def _row_col_indexing( arr: Union[np.ndarray, sps.spmatrix], rows: Optional[np.ndarray], @@ -315,7 +354,6 @@ def __init__( if not hasattr(cat_vec, "dtype"): cat_vec = np.asarray(cat_vec) # avoid errors in pd.factorize - self._input_dtype = cat_vec.dtype self._missing_method = cat_missing_method self._missing_category = cat_missing_name @@ -388,9 +426,9 @@ def cat(self): "This property will be removed in the next major release.", category=DeprecationWarning, ) - try: + if pd: return pd.Categorical.from_codes(self.indices, categories=self.categories) - except NameError: + else: raise ModuleNotFoundError( "The `cat` property is provided for backward compatibility and " "requires pandas to be installed." diff --git a/src/tabmat/constructor.py b/src/tabmat/constructor.py index 2c33130b..f4209f29 100644 --- a/src/tabmat/constructor.py +++ b/src/tabmat/constructor.py @@ -3,6 +3,7 @@ from collections.abc import Mapping from typing import Any, Optional, Union +import narwhals.stable.v1 as nw import numpy as np from formulaic import Formula, ModelSpec from formulaic.materializers.types import NAAction @@ -18,77 +19,15 @@ from .sparse_matrix import SparseMatrix from .split_matrix import SplitMatrix -try: - import polars as pl -except ImportError: - pl = None # type: ignore try: import pandas as pd except ImportError: pd = None # type: ignore -def _is_boolean(series, engine: str): - if engine == "pandas": - return pd.api.types.is_bool_dtype(series) - elif engine == "polars": - return series.dtype.is_(pl.Boolean) - else: - raise ValueError(f"Unknown engine: {engine}") - - -def _is_numeric(series, engine: str): - if engine == "pandas": - return pd.api.types.is_numeric_dtype(series) - elif engine == "polars": - return series.dtype.is_numeric() - else: - raise ValueError(f"Unknown engine: {engine}") - - -def _iter_columns(df, engine: str): - if engine == "pandas": - return df.items() - elif engine == "polars": - return ((col.name, col) for col in df.iter_columns()) - else: - raise ValueError(f"Unknown engine: {engine}") - - -def _object_as_cat(series, engine: str): - if engine == "pandas": - if series.dtype == object: - return series.astype("category") - return series - elif engine == "polars": - if series.dtype == pl.String: - return series.cast(pl.Categorical) - return series - else: - raise ValueError(f"Unknown engine: {engine}") - - -def _is_categorical(series, engine: str): - if engine == "pandas": - return isinstance(series.dtype, pd.CategoricalDtype) - elif engine == "polars": - return isinstance(series.dtype, (pl.Categorical, pl.Enum)) - else: - raise ValueError(f"Unknown engine: {engine}") - - -def _select_cols(df, idx, engine): - if engine == "pandas": - return df.iloc[:, idx] - elif engine == "polars": - return df.select(pl.nth(idx)) - else: - raise ValueError(f"Unknown engine: {engine}") - - -def _from_dataframe( +@nw.narwhalify(eager_only=True) +def from_df( df, - engine: str, dtype: np.dtype = np.float64, sparse_threshold: float = 0.1, cat_threshold: int = 4, @@ -100,11 +39,45 @@ def _from_dataframe( cat_missing_name: str = "(MISSING)", ) -> MatrixBase: """ - See docstring of from_pandas or from_polars for details. + Transform a DataFrame into an efficient SplitMatrix. - engine should be either 'pandas' or 'polars'. - """ + Parameters + ---------- + df : DataFrame + This can be any dataframes supported by narwhals (pandas, polars, etc.). + dtype : np.dtype, default np.float64 + dtype of all sub-matrices of the resulting SplitMatrix. + sparse_threshold : float, default 0.1 + Density threshold below which numerical columns will be stored in a sparse + format. + cat_threshold : int, default 4 + Number of levels of a categorical column under which the column will be stored + as sparse one-hot-encoded columns instead of CategoricalMatrix + object_as_cat : bool, default False + If True, DataFrame columns stored as python objects will be treated as + categorical columns. + cat_position : str {'end'|'expand'}, default 'expand' + Position of the categorical variable in the index. If "last", all the + categoricals (including the ones that did not satisfy cat_threshold) + will be placed at the end of the index list. If "expand", all the variables + will remain in the same order. + drop_first : bool, default False + If true, categoricals variables will have their first category dropped. + This allows multiple categorical variables to be included in an + unregularized model. If False, all categories are included. + cat_missing_method: str {'fail'|'zero'|'convert'}, default 'fail' + How to handle missing values in categorical columns: + - if 'fail', raise an error if there are missing values. + - if 'zero', missing values will represent all-zero indicator columns. + - if 'convert', missing values will be converted to the '(MISSING)' category. + cat_missing_name: str, default '(MISSING)' + Name of the category to which missing values will be converted if + ``cat_missing_method='convert'``. + Returns + ------- + SplitMatrix + """ matrices: list[Union[DenseMatrix, SparseMatrix, CategoricalMatrix]] = [] indices: list[np.ndarray] = [] is_cat: list[bool] = [] @@ -117,10 +90,21 @@ def _from_dataframe( mxcolidx = 0 - for dfcolidx, (colname, coldata) in enumerate(_iter_columns(df, engine)): + for dfcolidx, colname in enumerate(df.columns): + coldata = df[:, dfcolidx] if object_as_cat: - coldata = _object_as_cat(coldata, engine) - if _is_categorical(coldata, engine): + if isinstance(coldata.dtype, (nw.String, nw.Object)): + coldata = coldata.cast(nw.Categorical) + + # deal with Pandas sparse dtype (not supported by narwhals) + if pd is not None: + if isinstance(nw.to_native(coldata).dtype, pd.SparseDtype): + sparse_dfidx.append(dfcolidx) + sparse_tmidx.append(mxcolidx) + mxcolidx += 1 + continue + + if isinstance(coldata.dtype, (nw.Categorical, nw.Enum)): cat = CategoricalMatrix( coldata, drop_first=drop_first, @@ -163,7 +147,7 @@ def _from_dataframe( mxcolidx += cat.shape[1] elif cat_position == "end": indices.append(np.arange(cat.shape[1])) - elif _is_boolean(coldata, engine): + elif isinstance(coldata.dtype, nw.Boolean): if (coldata != False).mean() <= sparse_threshold: # noqa E712 sparse_dfidx.append(dfcolidx) sparse_tmidx.append(mxcolidx) @@ -172,7 +156,7 @@ def _from_dataframe( dense_dfidx.append(dfcolidx) dense_tmidx.append(mxcolidx) mxcolidx += 1 - elif _is_numeric(coldata, engine): + elif coldata.dtype.is_numeric(): if (coldata != 0).mean() <= sparse_threshold: sparse_dfidx.append(dfcolidx) sparse_tmidx.append(mxcolidx) @@ -181,7 +165,6 @@ def _from_dataframe( dense_dfidx.append(dfcolidx) dense_tmidx.append(mxcolidx) mxcolidx += 1 - else: ignored_cols.append(colname) @@ -192,7 +175,7 @@ def _from_dataframe( if dense_dfidx: matrices.append( DenseMatrix( - _select_cols(df, dense_dfidx, engine).to_numpy().astype(dtype), + df[:, dense_dfidx].to_numpy().astype(dtype), column_names=np.asarray(df.columns)[dense_dfidx], term_names=np.asarray(df.columns)[dense_dfidx], ) @@ -202,7 +185,7 @@ def _from_dataframe( if sparse_dfidx: matrices.append( SparseMatrix( - sps.coo_matrix(_select_cols(df, sparse_dfidx, engine), dtype=dtype), + sps.coo_matrix(df[:, sparse_dfidx], dtype=dtype), dtype=dtype, column_names=np.asarray(df.columns)[sparse_dfidx], term_names=np.asarray(df.columns)[sparse_dfidx], @@ -235,6 +218,8 @@ def from_pandas( cat_missing_name: str = "(MISSING)", ) -> MatrixBase: """ + Deprecated. Please use `from_df` instead. + Transform a pandas.DataFrame into an efficient SplitMatrix. Parameters @@ -274,77 +259,8 @@ def from_pandas( ------- SplitMatrix """ - return _from_dataframe( - df, - engine="pandas", - dtype=dtype, - sparse_threshold=sparse_threshold, - cat_threshold=cat_threshold, - object_as_cat=object_as_cat, - cat_position=cat_position, - drop_first=drop_first, - categorical_format=categorical_format, - cat_missing_method=cat_missing_method, - cat_missing_name=cat_missing_name, - ) - - -def from_polars( - df, - dtype: np.dtype = np.float64, - sparse_threshold: float = 0.1, - cat_threshold: int = 4, - object_as_cat: bool = False, - cat_position: str = "expand", - drop_first: bool = False, - categorical_format: str = "{name}[{category}]", - cat_missing_method: str = "fail", - cat_missing_name: str = "(MISSING)", -) -> MatrixBase: - """ - Transform a polars.DataFrame into an efficient SplitMatrix. - - Parameters - ---------- - df : pl.DataFrame - Polars DataFrame to convert. - dtype : np.dtype, default np.float64 - dtype of all sub-matrices of the resulting SplitMatrix. - sparse_threshold : float, default 0.1 - Density threshold below which numerical columns will be stored in a sparse - format. - cat_threshold : int, default 4 - Number of levels of a categorical column under which the column will be stored - as sparse one-hot-encoded columns instead of CategoricalMatrix - object_as_cat : bool, default False - If True, DataFrame columns stored as ``pl.String`` will be treated as - categorical columns. Note that this is different from pandas, where all object - columns are converted to categorical columns. - cat_position : str {'end'|'expand'}, default 'expand' - Position of the categorical variable in the index. If "last", all the - categoricals (including the ones that did not satisfy cat_threshold) - will be placed at the end of the index list. If "expand", all the variables - will remain in the same order. - drop_first : bool, default False - If true, categoricals variables will have their first category dropped. - This allows multiple categorical variables to be included in an - unregularized model. If False, all categories are included. - cat_missing_method: str {'fail'|'zero'|'convert'}, default 'fail' - How to handle missing values in categorical columns: - - if 'fail', raise an error if there are missing values. - - if 'zero', missing values will represent all-zero indicator columns. - - if 'convert', missing values will be converted to the '(MISSING)' category. - cat_missing_name: str, default '(MISSING)' - Name of the category to which missing values will be converted if - ``cat_missing_method='convert'``. - - Returns - ------- - SplitMatrix - """ - return _from_dataframe( + return from_df( df, - engine="polars", dtype=dtype, sparse_threshold=sparse_threshold, cat_threshold=cat_threshold, diff --git a/tests/test_categorical_matrix.py b/tests/test_categorical_matrix.py index 1c102a61..556d298d 100644 --- a/tests/test_categorical_matrix.py +++ b/tests/test_categorical_matrix.py @@ -1,8 +1,11 @@ import re +import narwhals.stable.v1 as nw import numpy as np import pandas as pd import polars as pl +import polars.testing +import pyarrow import pytest from tabmat.categorical_matrix import CategoricalMatrix, _extract_codes_and_categories @@ -205,6 +208,31 @@ def test_categorical_indexing(drop_first, missing, cat_missing_method): np.testing.assert_allclose(mat[:, [0, 1]].toarray(), expected) +@pytest.mark.parametrize( + "input_type", ["pandas.Categorical", "pandas", "polars", "pyarrow", "list"] +) +@pytest.mark.parametrize("narwhals_input", [True, False]) +def test_extract_codes_and_categories(input_type, narwhals_input): + cat_vec = pd.Series(["a", "b", "c", pd.NA, "b", "a", "d"], dtype="category") + if input_type == "pandas.Categorical": + cat_vec = pd.Categorical(cat_vec) + elif input_type == "polars": + cat_vec = pl.Series(cat_vec) + elif input_type == "pyarrow": + cat_vec = pyarrow.chunked_array([cat_vec]) + elif input_type == "list": + cat_vec = cat_vec.astype("object") + + if narwhals_input: + if input_type in ["list", "pandas.Categorical"]: + pytest.skip("Narwhals doesn't support list or pandas.Categorical inputs") + cat_vec = nw.from_native(cat_vec, series_only=True) + + indices, categories = _extract_codes_and_categories(cat_vec) + np.testing.assert_array_equal(indices, np.array([0, 1, 2, -1, 1, 0, 3])) + np.testing.assert_array_equal(categories, np.array(["a", "b", "c", "d"])) + + def test_polars_non_contiguous_codes(): str_series = ["labrador", "boxer", "beagle"] with pl.StringCache(): diff --git a/tests/test_constructor.py b/tests/test_constructor.py index ea5c6c4c..24171822 100644 --- a/tests/test_constructor.py +++ b/tests/test_constructor.py @@ -44,7 +44,7 @@ def construct_data(backend): def test_pandas_to_matrix(): df = construct_data("pandas") - mat = tm.from_pandas( + mat = tm.from_df( df, dtype=np.float64, sparse_threshold=0.3, cat_threshold=4, object_as_cat=True ) @@ -71,7 +71,7 @@ def test_pandas_to_matrix(): def test_polars_to_matrix(categorical_dtype): df = construct_data("polars").with_columns(cl=pl.col("cl").cast(categorical_dtype)) - mat = tm.from_polars(df, dtype=np.float64, sparse_threshold=0.3, cat_threshold=4) + mat = tm.from_df(df, dtype=np.float64, sparse_threshold=0.3, cat_threshold=4) assert mat.shape == (N_ROWS, N_ROWS + 5) assert len(mat.matrices) == 3 @@ -100,11 +100,11 @@ def test_from_pandas_missing(cat_missing_method): with pytest.raises( ValueError, match="Categorical data can't have missing values" ): - tm.from_pandas(df, cat_missing_method=cat_missing_method) + tm.from_df(df, cat_missing_method=cat_missing_method) elif cat_missing_method == "zero": - assert tm.from_pandas(df, cat_missing_method=cat_missing_method).shape == (6, 2) + assert tm.from_df(df, cat_missing_method=cat_missing_method).shape == (6, 2) elif cat_missing_method == "convert": - assert tm.from_pandas(df, cat_missing_method=cat_missing_method).shape == (6, 3) + assert tm.from_df(df, cat_missing_method=cat_missing_method).shape == (6, 3) @pytest.mark.parametrize("cat_missing_method", ["fail", "zero", "convert"]) @@ -117,11 +117,11 @@ def test_from_polars_missing(cat_missing_method): with pytest.raises( ValueError, match="Categorical data can't have missing values" ): - tm.from_polars(df, cat_missing_method=cat_missing_method) + tm.from_df(df, cat_missing_method=cat_missing_method) elif cat_missing_method == "zero": - assert tm.from_polars(df, cat_missing_method=cat_missing_method).shape == (6, 2) + assert tm.from_df(df, cat_missing_method=cat_missing_method).shape == (6, 2) elif cat_missing_method == "convert": - assert tm.from_polars(df, cat_missing_method=cat_missing_method).shape == (6, 3) + assert tm.from_df(df, cat_missing_method=cat_missing_method).shape == (6, 3) @pytest.mark.parametrize("prefix_sep", ["_", ": "]) @@ -130,7 +130,7 @@ def test_names_pandas(prefix_sep, drop_first): df = construct_data("pandas") categorical_format = "{name}" + prefix_sep + "{category}" - mat_end = tm.from_pandas( + mat_end = tm.from_df( df, dtype=np.float64, sparse_threshold=0.3, @@ -144,7 +144,7 @@ def test_names_pandas(prefix_sep, drop_first): expanded_df = pd.get_dummies(df, prefix_sep=prefix_sep, drop_first=drop_first) assert mat_end.column_names == expanded_df.columns.tolist() - mat_expand = tm.from_pandas( + mat_expand = tm.from_df( df, dtype=np.float64, sparse_threshold=0.3, @@ -165,7 +165,7 @@ def test_names_polars(prefix_sep, drop_first): df = construct_data("polars") categorical_format = "{name}" + prefix_sep + "{category}" - mat_end = tm.from_polars( + mat_end = tm.from_df( df, dtype=np.float64, sparse_threshold=0.3, @@ -180,7 +180,7 @@ def test_names_polars(prefix_sep, drop_first): ) assert mat_end.column_names == list(expanded_df.columns) - mat_expand = tm.from_polars( + mat_expand = tm.from_df( df, dtype=np.float64, sparse_threshold=0.3, diff --git a/tests/test_formula.py b/tests/test_formula.py index 480ab7e5..8ea19d76 100644 --- a/tests/test_formula.py +++ b/tests/test_formula.py @@ -443,7 +443,7 @@ def test_term_names_against_expectation(df, formula, expected_names): ids=["brackets", "double_underscore", "custom"], ) def test_all_names_against_from_pandas(df, categorical_format): - mat_from_pandas = tm.from_pandas( + mat_from_pandas = tm.from_df( df, drop_first=False, object_as_cat=True, categorical_format=categorical_format ) mat_from_formula = tm.from_formula( @@ -653,7 +653,7 @@ def test_cat_missing_handling(cat_missing_method, cat_missing_name): } ) - mat_from_pandas = tm.from_pandas( + mat_from_pandas = tm.from_df( df, cat_threshold=0, cat_missing_method=cat_missing_method, diff --git a/tests/test_real_matrix.py b/tests/test_real_matrix.py index 919a0bbc..9ce280e5 100644 --- a/tests/test_real_matrix.py +++ b/tests/test_real_matrix.py @@ -8,7 +8,7 @@ @pytest.fixture() def X(): df = pd.read_pickle("tests/real_matrix.pkl") - X_split = tm.from_pandas(df, np.float64) + X_split = tm.from_df(df, np.float64) wts = np.ones(df.shape[0]) / df.shape[0] X_std = X_split.standardize(wts, True, True)[0] return X_std